diff --git a/.cirrus.yml b/.cirrus.yml index 27efc48619..90645fede6 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -22,7 +22,9 @@ macos_task: install_script: - brew install pkg-config python gnu-sed glib pixman make sdl2 script: - - ./configure --python=/usr/local/bin/python3 || { cat config.log; exit 1; } + - mkdir build + - cd build + - ../configure --python=/usr/local/bin/python3 || { cat config.log; exit 1; } - gmake -j$(sysctl -n hw.ncpu) - gmake check -j$(sysctl -n hw.ncpu) @@ -33,6 +35,8 @@ macos_xcode_task: install_script: - brew install pkg-config gnu-sed glib pixman make sdl2 script: - - ./configure --cc=clang || { cat config.log; exit 1; } + - mkdir build + - cd build + - ../configure --cc=clang || { cat config.log; exit 1; } - gmake -j$(sysctl -n hw.ncpu) - gmake check -j$(sysctl -n hw.ncpu) diff --git a/.github/lockdown.yml b/.github/lockdown.yml new file mode 100644 index 0000000000..9acc393f1c --- /dev/null +++ b/.github/lockdown.yml @@ -0,0 +1,34 @@ +# Configuration for Repo Lockdown - https://github.com/dessant/repo-lockdown + +# Close issues and pull requests +close: true + +# Lock issues and pull requests +lock: true + +issues: + comment: | + Thank you for your interest in the QEMU project. + + This repository is a read-only mirror of the project's master + repostories hosted on https://git.qemu.org/git/qemu.git. + The project does not process issues filed on GitHub. + + The project issues are tracked on Launchpad: + https://bugs.launchpad.net/qemu + + QEMU welcomes bug report contributions. You can file new ones on: + https://bugs.launchpad.net/qemu/+filebug + +pulls: + comment: | + Thank you for your interest in the QEMU project. + + This repository is a read-only mirror of the project's master + repostories hosted on https://git.qemu.org/git/qemu.git. + The project does not process merge requests filed on GitHub. + + QEMU welcomes contributions of code (either fixing bugs or adding new + functionality). However, we get a lot of patches, and so we have some + guidelines about contributing on the project website: + https://www.qemu.org/contribute/ diff --git a/.gitignore b/.gitignore index 7de868d1ea..0c5af83aa7 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ /config-target.* /config.status /config-temp +/tools/virtiofsd/50-qemu-virtiofsd.json /elf2dmp /trace-events-all /trace/generated-events.h @@ -37,6 +38,7 @@ /qapi/qapi-emit-events.[ch] /qapi/qapi-events-*.[ch] /qapi/qapi-events.[ch] +/qapi/qapi-init-commands.[ch] /qapi/qapi-introspect.[ch] /qapi/qapi-types-*.[ch] /qapi/qapi-types.[ch] @@ -44,9 +46,6 @@ !/qapi/qapi-visit-core.c /qapi/qapi-visit.[ch] /qapi/qapi-doc.texi -/qemu-doc.html -/qemu-doc.info -/qemu-doc.txt /qemu-edid /qemu-img /qemu-nbd diff --git a/.gitlab-ci-edk2.yml b/.gitlab-ci-edk2.yml new file mode 100644 index 0000000000..088ba4b43a --- /dev/null +++ b/.gitlab-ci-edk2.yml @@ -0,0 +1,49 @@ +docker-edk2: + stage: build + rules: # Only run this job when the Dockerfile is modified + - changes: + - .gitlab-ci-edk2.yml + - .gitlab-ci.d/edk2/Dockerfile + when: always + image: docker:19.03.1 + services: + - docker:19.03.1-dind + variables: + GIT_DEPTH: 3 + IMAGE_TAG: $CI_REGISTRY_IMAGE:edk2-cross-build + # We don't use TLS + DOCKER_HOST: tcp://docker:2375 + DOCKER_TLS_CERTDIR: "" + before_script: + - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY + script: + - docker pull $IMAGE_TAG || true + - docker build --cache-from $IMAGE_TAG --tag $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA + --tag $IMAGE_TAG .gitlab-ci.d/edk2 + - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA + - docker push $IMAGE_TAG + +build-edk2: + rules: # Only run this job when ... + - changes: # ... roms/edk2/ is modified (submodule updated) + - roms/edk2/* + when: always + - if: '$CI_COMMIT_REF_NAME =~ /^edk2/' # or the branch/tag starts with 'edk2' + when: always + - if: '$CI_COMMIT_MESSAGE =~ /edk2/i' # or last commit description contains 'EDK2' + when: always + artifacts: + paths: # 'artifacts.zip' will contains the following files: + - pc-bios/edk2*bz2 + - pc-bios/edk2-licenses.txt + - edk2-stdout.log + - edk2-stderr.log + image: $CI_REGISTRY_IMAGE:edk2-cross-build + variables: + GIT_DEPTH: 3 + script: # Clone the required submodules and build EDK2 + - git submodule update --init roms/edk2 + - git -C roms/edk2 submodule update --init + - export JOBS=$(($(getconf _NPROCESSORS_ONLN) + 1)) + - echo "=== Using ${JOBS} simultaneous jobs ===" + - make -j${JOBS} -C roms efi 2>&1 1>edk2-stdout.log | tee -a edk2-stderr.log >&2 diff --git a/.gitlab-ci-opensbi.yml b/.gitlab-ci-opensbi.yml new file mode 100644 index 0000000000..dd051c0124 --- /dev/null +++ b/.gitlab-ci-opensbi.yml @@ -0,0 +1,63 @@ +docker-opensbi: + stage: build + rules: # Only run this job when the Dockerfile is modified + - changes: + - .gitlab-ci-opensbi.yml + - .gitlab-ci.d/opensbi/Dockerfile + when: always + image: docker:19.03.1 + services: + - docker:19.03.1-dind + variables: + GIT_DEPTH: 3 + IMAGE_TAG: $CI_REGISTRY_IMAGE:opensbi-cross-build + # We don't use TLS + DOCKER_HOST: tcp://docker:2375 + DOCKER_TLS_CERTDIR: "" + before_script: + - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY + script: + - docker pull $IMAGE_TAG || true + - docker build --cache-from $IMAGE_TAG --tag $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA + --tag $IMAGE_TAG .gitlab-ci.d/opensbi + - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA + - docker push $IMAGE_TAG + +build-opensbi: + rules: # Only run this job when ... + - changes: # ... roms/opensbi/ is modified (submodule updated) + - roms/opensbi/* + when: always + - if: '$CI_COMMIT_REF_NAME =~ /^opensbi/' # or the branch/tag starts with 'opensbi' + when: always + - if: '$CI_COMMIT_MESSAGE =~ /opensbi/i' # or last commit description contains 'OpenSBI' + when: always + artifacts: + paths: # 'artifacts.zip' will contains the following files: + - pc-bios/opensbi-riscv32-sifive_u-fw_jump.bin + - pc-bios/opensbi-riscv32-virt-fw_jump.bin + - pc-bios/opensbi-riscv64-sifive_u-fw_jump.bin + - pc-bios/opensbi-riscv64-virt-fw_jump.bin + - opensbi32-virt-stdout.log + - opensbi32-virt-stderr.log + - opensbi64-virt-stdout.log + - opensbi64-virt-stderr.log + - opensbi32-sifive_u-stdout.log + - opensbi32-sifive_u-stderr.log + - opensbi64-sifive_u-stdout.log + - opensbi64-sifive_u-stderr.log + image: $CI_REGISTRY_IMAGE:opensbi-cross-build + variables: + GIT_DEPTH: 3 + script: # Clone the required submodules and build OpenSBI + - git submodule update --init roms/opensbi + - export JOBS=$(($(getconf _NPROCESSORS_ONLN) + 1)) + - echo "=== Using ${JOBS} simultaneous jobs ===" + - make -j${JOBS} -C roms/opensbi clean + - make -j${JOBS} -C roms opensbi32-virt 2>&1 1>opensbi32-virt-stdout.log | tee -a opensbi32-virt-stderr.log >&2 + - make -j${JOBS} -C roms/opensbi clean + - make -j${JOBS} -C roms opensbi64-virt 2>&1 1>opensbi64-virt-stdout.log | tee -a opensbi64-virt-stderr.log >&2 + - make -j${JOBS} -C roms/opensbi clean + - make -j${JOBS} -C roms opensbi32-sifive_u 2>&1 1>opensbi32-sifive_u-stdout.log | tee -a opensbi32-sifive_u-stderr.log >&2 + - make -j${JOBS} -C roms/opensbi clean + - make -j${JOBS} -C roms opensbi64-sifive_u 2>&1 1>opensbi64-sifive_u-stdout.log | tee -a opensbi64-sifive_u-stderr.log >&2 diff --git a/.gitlab-ci.d/edk2/Dockerfile b/.gitlab-ci.d/edk2/Dockerfile new file mode 100644 index 0000000000..b4584d1cf6 --- /dev/null +++ b/.gitlab-ci.d/edk2/Dockerfile @@ -0,0 +1,27 @@ +# +# Docker image to cross-compile EDK2 firmware binaries +# +FROM ubuntu:16.04 + +MAINTAINER Philippe Mathieu-Daudé + +# Install packages required to build EDK2 +RUN apt update \ + && \ + \ + DEBIAN_FRONTEND=noninteractive \ + apt install --assume-yes --no-install-recommends \ + build-essential \ + ca-certificates \ + dos2unix \ + gcc-aarch64-linux-gnu \ + gcc-arm-linux-gnueabi \ + git \ + iasl \ + make \ + nasm \ + python \ + uuid-dev \ + && \ + \ + rm -rf /var/lib/apt/lists/* diff --git a/.gitlab-ci.d/opensbi/Dockerfile b/.gitlab-ci.d/opensbi/Dockerfile new file mode 100644 index 0000000000..4ba8a4de86 --- /dev/null +++ b/.gitlab-ci.d/opensbi/Dockerfile @@ -0,0 +1,33 @@ +# +# Docker image to cross-compile OpenSBI firmware binaries +# +FROM ubuntu:18.04 + +MAINTAINER Bin Meng + +# Install packages required to build OpenSBI +RUN apt update \ + && \ + \ + DEBIAN_FRONTEND=noninteractive \ + apt install --assume-yes --no-install-recommends \ + build-essential \ + ca-certificates \ + git \ + make \ + wget \ + && \ + \ + rm -rf /var/lib/apt/lists/* + +# Manually install the kernel.org "Crosstool" based toolchains for gcc-8.3 +RUN wget -O - \ + https://mirrors.edge.kernel.org/pub/tools/crosstool/files/bin/x86_64/8.3.0/x86_64-gcc-8.3.0-nolibc-riscv32-linux.tar.xz \ + | tar -C /opt -xJ +RUN wget -O - \ + https://mirrors.edge.kernel.org/pub/tools/crosstool/files/bin/x86_64/8.3.0/x86_64-gcc-8.3.0-nolibc-riscv64-linux.tar.xz \ + | tar -C /opt -xJ + +# Export the toolchains to the system path +ENV PATH="/opt/gcc-8.3.0-nolibc/riscv32-linux/bin:${PATH}" +ENV PATH="/opt/gcc-8.3.0-nolibc/riscv64-linux/bin:${PATH}" diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index be57c6a454..b889fb96b6 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,3 +1,7 @@ +include: + - local: '/.gitlab-ci-edk2.yml' + - local: '/.gitlab-ci-opensbi.yml' + before_script: - apt-get update -qq - apt-get install -y -qq flex bison libglib2.0-dev libpixman-1-dev genisoimage @@ -6,7 +10,9 @@ build-system1: script: - apt-get install -y -qq libgtk-3-dev libvte-dev nettle-dev libcacard-dev libusb-dev libvde-dev libspice-protocol-dev libgl1-mesa-dev libvdeplug-dev - - ./configure --enable-werror --target-list="aarch64-softmmu alpha-softmmu + - mkdir build + - cd build + - ../configure --enable-werror --target-list="aarch64-softmmu alpha-softmmu cris-softmmu hppa-softmmu lm32-softmmu moxie-softmmu microblazeel-softmmu mips64el-softmmu m68k-softmmu ppc-softmmu riscv64-softmmu sparc-softmmu" - make -j2 @@ -16,7 +22,10 @@ build-system2: script: - apt-get install -y -qq libsdl2-dev libgcrypt-dev libbrlapi-dev libaio-dev libfdt-dev liblzo2-dev librdmacm-dev libibverbs-dev libibumad-dev - - ./configure --enable-werror --target-list="tricore-softmmu unicore32-softmmu + libzstd-dev + - mkdir build + - cd build + - ../configure --enable-werror --target-list="tricore-softmmu unicore32-softmmu microblaze-softmmu mips-softmmu riscv32-softmmu s390x-softmmu sh4-softmmu sparc64-softmmu x86_64-softmmu xtensa-softmmu nios2-softmmu or1k-softmmu" - make -j2 @@ -24,7 +33,9 @@ build-system2: build-disabled: script: - - ./configure --enable-werror --disable-rdma --disable-slirp --disable-curl + - mkdir build + - cd build + - ../configure --enable-werror --disable-rdma --disable-slirp --disable-curl --disable-capstone --disable-live-block-migration --disable-glusterfs --disable-replication --disable-coroutine-pool --disable-smartcard --disable-guest-agent --disable-curses --disable-libxml2 --disable-tpm @@ -36,32 +47,38 @@ build-disabled: build-tcg-disabled: script: - - apt-get install -y -qq clang libgtk-3-dev libbluetooth-dev libusb-dev - - ./configure --cc=clang --enable-werror --disable-tcg --audio-drv-list="" + - apt-get install -y -qq clang libgtk-3-dev libusb-dev + - mkdir build + - cd build + - ../configure --cc=clang --enable-werror --disable-tcg --audio-drv-list="" - make -j2 - make check-unit - make check-qapi-schema - cd tests/qemu-iotests/ - ./check -raw 001 002 003 004 005 008 009 010 011 012 021 025 032 033 048 - 052 063 077 086 101 104 106 113 147 148 150 151 152 157 159 160 - 163 170 171 183 184 192 194 197 205 208 215 221 222 226 227 236 - - ./check -qcow2 028 040 051 056 057 058 065 067 068 082 085 091 095 096 102 - 122 124 127 129 132 139 142 144 145 147 151 152 155 157 165 194 - 196 197 200 202 203 205 208 209 215 216 218 222 227 234 246 247 - 248 250 254 255 256 + 052 063 077 086 101 104 106 113 148 150 151 152 157 159 160 163 + 170 171 183 184 192 194 197 208 215 221 222 226 227 236 253 277 + - ./check -qcow2 028 051 056 057 058 065 067 068 082 085 091 095 096 102 122 + 124 132 139 142 144 145 151 152 155 157 165 194 196 197 200 202 + 208 209 215 216 218 222 227 234 246 247 248 250 254 255 257 258 + 260 261 262 263 264 270 272 273 277 279 build-user: script: - - ./configure --enable-werror --disable-system --disable-guest-agent + - mkdir build + - cd build + - ../configure --enable-werror --disable-system --disable-guest-agent --disable-capstone --disable-slirp --disable-fdt - make -j2 - make run-tcg-tests-i386-linux-user run-tcg-tests-x86_64-linux-user build-clang: script: - - apt-get install -y -qq clang libsdl2-dev libattr1-dev libcap-dev + - apt-get install -y -qq clang libsdl2-dev libattr1-dev libcap-ng-dev xfslibs-dev libiscsi-dev libnfs-dev libseccomp-dev gnutls-dev librbd-dev - - ./configure --cc=clang --cxx=clang++ --enable-werror + - mkdir build + - cd build + - ../configure --cc=clang --cxx=clang++ --enable-werror --target-list="alpha-softmmu arm-softmmu m68k-softmmu mips64-softmmu ppc-softmmu s390x-softmmu x86_64-softmmu arm-linux-user" - make -j2 @@ -70,14 +87,18 @@ build-clang: build-tci: script: - TARGETS="aarch64 alpha arm hppa m68k microblaze moxie ppc64 s390x x86_64" - - ./configure --enable-tcg-interpreter + - mkdir build + - cd build + - ../configure --enable-tcg-interpreter --target-list="$(for tg in $TARGETS; do echo -n ${tg}'-softmmu '; done)" - make -j2 - - make tests/boot-serial-test tests/cdrom-test tests/pxe-test + - make run-tcg-tests-x86_64-softmmu + - make tests/qtest/boot-serial-test tests/qtest/cdrom-test tests/qtest/pxe-test - for tg in $TARGETS ; do export QTEST_QEMU_BINARY="${tg}-softmmu/qemu-system-${tg}" ; - ./tests/boot-serial-test || exit 1 ; - ./tests/cdrom-test || exit 1 ; + ./tests/qtest/boot-serial-test || exit 1 ; + ./tests/qtest/cdrom-test || exit 1 ; done - - QTEST_QEMU_BINARY="x86_64-softmmu/qemu-system-x86_64" ./tests/pxe-test - - QTEST_QEMU_BINARY="s390x-softmmu/qemu-system-s390x" ./tests/pxe-test -m slow + - QTEST_QEMU_BINARY="x86_64-softmmu/qemu-system-x86_64" ./tests/qtest/pxe-test + - QTEST_QEMU_BINARY="s390x-softmmu/qemu-system-s390x" + ./tests/qtest/pxe-test -m slow diff --git a/.gitmodules b/.gitmodules index 19792c9a11..9c0501a4d4 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,9 +10,6 @@ [submodule "roms/openbios"] path = roms/openbios url = https://git.qemu.org/git/openbios.git -[submodule "roms/openhackware"] - path = roms/openhackware - url = https://git.qemu.org/git/openhackware.git [submodule "roms/qemu-palcode"] path = roms/qemu-palcode url = https://git.qemu.org/git/qemu-palcode.git diff --git a/.mailmap b/.mailmap index 3816e4effe..6412067bde 100644 --- a/.mailmap +++ b/.mailmap @@ -39,11 +39,13 @@ Julia Suvorova Julia Suvorova via Qemu-devel Justin Terry (VM) via Qemu-devel # Next, replace old addresses by a more recent one. -Aleksandar Markovic -Aleksandar Markovic +Aleksandar Markovic +Aleksandar Markovic +Aleksandar Markovic Aleksandar Rikalo Anthony Liguori Anthony Liguori James Hogan +Leif Lindholm Paul Burton Paul Burton Paul Burton @@ -151,7 +153,8 @@ Xiaoqiang Zhao Xinhua Cao Xiong Zhang Yin Yin -yuchenlin +Yu-Chen Lin +Yu-Chen Lin YunQiang Su YunQiang Su Yuri Pudgorodskiy 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 f74a3de3ff..2cce7b5689 100644 --- a/.shippable.yml +++ b/.shippable.yml @@ -35,5 +35,7 @@ build: options: "-e HOME=/root" ci: - unset CC - - ./configure ${QEMU_CONFIGURE_OPTS} --target-list=${TARGET_LIST} + - mkdir build + - cd build + - ../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 445b0646c1..2fd63eceaa 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,7 @@ # The current Travis default is a VM based 16.04 Xenial on GCE # Additional builds with specific requirements for a full VM need to # be added as additional matrix: entries later on +os: linux dist: xenial language: c compiler: @@ -26,7 +27,6 @@ addons: - libaio-dev - libattr1-dev - libbrlapi-dev - - libcap-dev - libcap-ng-dev - libgcc-4.8-dev - libgnutls28-dev @@ -49,16 +49,12 @@ addons: - libusb-1.0-0-dev - libvdeplug-dev - libvte-2.91-dev + - libzstd-dev - sparse - uuid-dev - gcovr - homebrew: - packages: - - ccache - - glib - - pixman - - gnu-sed - update: true + # Tests dependencies + - genisoimage # The channel name "irc.oftc.net#qemu" is encrypted against qemu/qemu @@ -74,94 +70,126 @@ notifications: env: global: - - SRC_DIR="." - - BUILD_DIR="." + - 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" - CCACHE_SLOPPINESS="include_file_ctime,include_file_mtime" - CCACHE_MAXSIZE=1G + - G_MESSAGES_DEBUG=error git: # we want to do this ourselves submodules: false - -before_script: - - if [ "$TRAVIS_OS_NAME" == "osx" ] ; then export PATH="/usr/local/opt/ccache/libexec:$PATH" ; fi +# 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: - 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: - - make -j3 && travis_retry ${TEST_CMD} + - 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: +jobs: 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" @@ -169,118 +197,147 @@ 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: - - CONFIG="--disable-user --target-list=${MAIN_SOFTMMU_TARGETS}" - - 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 before_script: - - ./configure ${CONFIG} --extra-cflags="-fsanitize=undefined -Werror" || { cat config.log && exit 1; } + - mkdir -p ${BUILD_DIR} && cd ${BUILD_DIR} + - ${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="" - # We manually include builds which we disable "make check" for - - env: - - CONFIG="--enable-debug --enable-tcg-interpreter" - - TEST_CMD="" + # Check the TCG interpreter (TCI) + - 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: + - BASE_CONFIG="--disable-docs --enable-tools" - CONFIG="--target-list=i386-softmmu,ppc-softmmu,ppc64-softmmu,m68k-softmmu,x86_64-softmmu" os: osx osx_image: xcode10.3 compiler: clang + addons: + homebrew: + packages: + - ccache + - glib + - pixman + - gnu-sed + - python + update: true + before_script: + - brew link --overwrite python + - export PATH="/usr/local/opt/ccache/libexec:$PATH" + - 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 - python: - - "3.4" + python: 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 - python: - - "3.6" + python: 3.6 # 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" + dist: bionic + env: + - CONFIG="--enable-tools --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_failure: - - cat tests/results/latest/job.log + - CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-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 + - du -chs $HOME/avocado/data/cache addons: apt: packages: - python3-pil - python3-pip - - python3.5-venv + - python3-numpy + - python3-opencv + - python3-venv + - rpm2cpio - tesseract-ocr - tesseract-ocr-eng # Using newer GCC with sanitizers - - addons: + - name: "GCC9 with sanitizers (softmmu)" + addons: apt: update: true sources: @@ -323,56 +380,212 @@ matrix: - CONFIG="--cc=gcc-9 --cxx=g++-9 --disable-pie --disable-linux-user" - TEST_CMD="" before_script: - - ./configure ${CONFIG} --extra-cflags="-g3 -O0 -Wno-error=stringop-truncation -fsanitize=thread -fuse-ld=gold" || { cat config.log && exit 1; } + - mkdir -p ${BUILD_DIR} && cd ${BUILD_DIR} + - ${SRC_DIR}/configure ${CONFIG} --extra-cflags="-g3 -O0 -Wno-error=stringop-truncation -fsanitize=thread" --extra-ldflags="-fuse-ld=gold" || { cat config.log && exit 1; } # 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 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 build-tcg" + - TEST_CMD="make check-tcg" - CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-debug-tcg" + - name: "[aarch64] GCC check-tcg" + arch: arm64 + dist: xenial + addons: + apt_packages: + - libaio-dev + - libattr1-dev + - libbrlapi-dev + - libcap-ng-dev + - libgcrypt20-dev + - libgnutls28-dev + - libgtk-3-dev + - libiscsi-dev + - liblttng-ust-dev + - libncurses5-dev + - libnfs-dev + - libnss3-dev + - libpixman-1-dev + - libpng-dev + - librados-dev + - libsdl2-dev + - libseccomp-dev + - liburcu-dev + - 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}" + + - name: "[ppc64] GCC check-tcg" + arch: ppc64le + dist: xenial + addons: + apt_packages: + - libaio-dev + - libattr1-dev + - libbrlapi-dev + - libcap-ng-dev + - libgcrypt20-dev + - libgnutls28-dev + - libgtk-3-dev + - libiscsi-dev + - liblttng-ust-dev + - libncurses5-dev + - libnfs-dev + - libnss3-dev + - libpixman-1-dev + - libpng-dev + - librados-dev + - libsdl2-dev + - libseccomp-dev + - liburcu-dev + - 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" + + - name: "[s390x] GCC check-tcg" + arch: s390x + dist: bionic + addons: + apt_packages: + - libaio-dev + - libattr1-dev + - libbrlapi-dev + - libcap-ng-dev + - libgcrypt20-dev + - libgnutls28-dev + - libgtk-3-dev + - libiscsi-dev + - liblttng-ust-dev + - libncurses5-dev + - libnfs-dev + - libnss3-dev + - libpixman-1-dev + - libpng-dev + - librados-dev + - libsdl2-dev + - libseccomp-dev + - liburcu-dev + - 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" + script: + - ( cd ${SRC_DIR} ; git submodule update --init roms/SLOF ) + - BUILD_RC=0 && make -j${JOBS} || BUILD_RC=$? + - | + if [ "$BUILD_RC" -eq 0 ] ; then + mv pc-bios/s390-ccw/*.img pc-bios/ ; + ${TEST_CMD} ; + else + $(exit $BUILD_RC); + fi + + - name: "[s390x] GCC check (KVM)" + arch: s390x + dist: bionic + addons: + apt_packages: + - libaio-dev + - libattr1-dev + - libbrlapi-dev + - libcap-ng-dev + - libgcrypt20-dev + - libgnutls28-dev + - libgtk-3-dev + - libiscsi-dev + - liblttng-ust-dev + - libncurses5-dev + - libnfs-dev + - libnss3-dev + - libpixman-1-dev + - libpng-dev + - librados-dev + - libsdl2-dev + - libseccomp-dev + - liburcu-dev + - libusb-1.0-0-dev + - libvdeplug-dev + - libvte-2.91-dev + # Tests dependencies + - genisoimage + env: + - TEST_CMD="make check-unit" + - CONFIG="--disable-containers --disable-tcg --enable-kvm --disable-tools" + script: + - ( cd ${SRC_DIR} ; git submodule update --init roms/SLOF ) + - BUILD_RC=0 && make -j${JOBS} || BUILD_RC=$? + - | + if [ "$BUILD_RC" -eq 0 ] ; then + mv pc-bios/s390-ccw/*.img pc-bios/ ; + ${TEST_CMD} ; + else + $(exit $BUILD_RC); + fi # 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 - tar -xf ${SRC_DIR}/qemu-${QEMU_VERSION}.tar.bz2 && cd qemu-${QEMU_VERSION} - - ./configure ${BASE_CONFIG} ${CONFIG} || { cat config.log && exit 1; } + - mkdir -p release-build && cd release-build + - ../configure ${BASE_CONFIG} ${CONFIG} || { cat config.log && exit 1; } - make install diff --git a/Kconfig.host b/Kconfig.host index bb6e116e2a..55136e037d 100644 --- a/Kconfig.host +++ b/Kconfig.host @@ -25,6 +25,11 @@ config TPM config VHOST_USER bool + select VHOST + +config VHOST_KERNEL + bool + select VHOST config XEN bool diff --git a/MAINTAINERS b/MAINTAINERS index 5e5e3e52d6..8cbc1fac2b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -101,7 +101,8 @@ F: include/hw/watchdog/wdt_diag288.h F: pc-bios/s390-ccw/ F: pc-bios/s390-ccw.img F: target/s390x/ -F: docs/vfio-ap.txt +F: docs/system/target-s390x.rst +F: docs/system/s390x/ F: tests/migration/s390x/ K: ^Subject:.*(?i)s390x? T: git https://github.com/cohuck/qemu.git s390-next @@ -155,6 +156,7 @@ F: include/hw/cpu/a*mpcore.h F: disas/arm.c F: disas/arm-a64.cc F: disas/libvixl/ +F: docs/system/target-arm.rst ARM SMMU M: Eric Auger @@ -178,6 +180,8 @@ S: Maintained F: target/hppa/ F: hw/hppa/ F: disas/hppa.c +F: hw/net/*i82596* +F: include/hw/net/lasi_82596.h LM32 TCG CPUs M: Michael Walle @@ -206,13 +210,14 @@ F: hw/microblaze/ F: disas/microblaze.c MIPS TCG CPUs -M: Aurelien Jarno -M: Aleksandar Markovic +M: Aleksandar Markovic +R: Aurelien Jarno R: Aleksandar Rikalo S: Maintained F: target/mips/ F: default-configs/*mips* F: disas/*mips* +F: docs/system/cpu-models-mips.rst.inc F: hw/intc/mips_gic.c F: hw/mips/ F: hw/misc/mips_* @@ -221,6 +226,8 @@ F: include/hw/intc/mips_gic.h F: include/hw/mips/ F: include/hw/misc/mips_* F: include/hw/timer/mips_gictimer.h +F: tests/acceptance/linux_ssh_mips_malta.py +F: tests/acceptance/machine_mips_malta.py F: tests/tcg/mips/ K: ^Subject:.*(?i)mips @@ -271,6 +278,11 @@ F: include/hw/riscv/ F: linux-user/host/riscv32/ F: linux-user/host/riscv64/ +RENESAS RX CPUs +M: Yoshinori Sato +S: Maintained +F: target/rx/ + S390 TCG CPUs M: Richard Henderson M: David Hildenbrand @@ -316,7 +328,7 @@ F: tests/tcg/i386/ F: tests/tcg/x86_64/ F: hw/i386/ F: disas/i386.c -F: docs/qemu-cpu-models.texi +F: docs/system/cpu-models-x86.rst.inc T: git https://github.com/ehabkost/qemu.git x86-next Xtensa TCG CPUs @@ -362,9 +374,8 @@ S: Maintained F: target/arm/kvm.c MIPS KVM CPUs -M: James Hogan -R: Aleksandar Rikalo -S: Maintained +M: Aleksandar Markovic +S: Odd Fixes F: target/mips/kvm.c PPC KVM CPUs @@ -401,6 +412,21 @@ S: Supported F: target/i386/kvm.c F: scripts/kvm/vmxcap +X86 HVF CPUs +M: Roman Bolshakov +S: Maintained +F: accel/stubs/hvf-stub.c +F: target/i386/hvf/ +F: include/sysemu/hvf.h + +WHPX CPUs +M: Sunil Muthuswamy +S: Supported +F: target/i386/whpx-all.c +F: target/i386/whp-dispatch.h +F: accel/stubs/whpx-stub.c +F: include/sysemu/whpx.h + Guest CPU Cores (Xen) --------------------- X86 Xen CPUs @@ -414,15 +440,28 @@ F: hw/9pfs/xen-9p* F: hw/char/xen_console.c F: hw/display/xenfb.c F: hw/net/xen_nic.c +F: hw/usb/xen-usb.c F: hw/block/xen* F: hw/block/dataplane/xen* F: hw/xen/ F: hw/xenpv/ F: hw/i386/xen/ +F: hw/pci-host/xen_igd_pt.c F: include/hw/block/dataplane/xen* F: include/hw/xen/ F: include/sysemu/xen-mapcache.h +Guest CPU Cores (HAXM) +--------------------- +X86 HAXM CPUs +M: Wenchao Wang +M: Colin Xu +L: haxm-team@intel.com +W: https://github.com/intel/haxm/issues +S: Maintained +F: include/sysemu/hax.h +F: target/i386/hax-* + Hosts ----- LINUX @@ -479,6 +518,15 @@ F: hw/*/allwinner* F: include/hw/*/allwinner* F: hw/arm/cubieboard.c +Allwinner-h3 +M: Niek Linnenbank +L: qemu-arm@nongnu.org +S: Maintained +F: hw/*/allwinner-h3* +F: include/hw/*/allwinner-h3* +F: hw/arm/orangepi.c +F: docs/system/orangepi.rst + ARM PrimeCell and CMSDK devices M: Peter Maydell L: qemu-arm@nongnu.org @@ -531,7 +579,7 @@ F: include/hw/misc/arm11scu.h F: include/hw/timer/a9gtimer.h F: include/hw/timer/arm_mptimer.h F: include/hw/timer/armv7m_systick.h -F: tests/test-arm-mptimer.c +F: tests/qtest/test-arm-mptimer.c Exynos M: Igor Mitsyanko @@ -558,6 +606,14 @@ F: include/hw/arm/digic.h F: hw/*/digic* F: include/hw/*/digic* +Goldfish RTC +M: Anup Patel +M: Alistair Francis +L: qemu-riscv@nongnu.org +S: Maintained +F: hw/rtc/goldfish_rtc.c +F: include/hw/rtc/goldfish_rtc.h + Gumstix M: Peter Maydell R: Philippe Mathieu-Daudé @@ -594,6 +650,8 @@ S: Maintained F: hw/arm/integratorcp.c F: hw/misc/arm_integrator_debug.c F: include/hw/misc/arm_integrator_debug.h +F: tests/acceptance/machine_arm_integratorcp.py +F: docs/system/arm/integratorcp.rst MCIMX6UL EVK / i.MX6ul M: Peter Maydell @@ -652,6 +710,7 @@ M: Peter Maydell L: qemu-arm@nongnu.org S: Odd Fixes F: hw/arm/musicpal.c +F: docs/system/arm/musicpal.rst nSeries M: Andrzej Zaborowski @@ -667,6 +726,8 @@ F: hw/rtc/twl92230.c F: include/hw/display/blizzard.h F: include/hw/input/tsc2xxx.h F: include/hw/misc/cbus.h +F: tests/acceptance/machine_arm_n8x0.py +F: docs/system/arm/nseries.rst Palm M: Andrzej Zaborowski @@ -676,6 +737,7 @@ S: Odd Fixes F: hw/arm/palm.c F: hw/input/tsc210x.c F: include/hw/input/tsc2xxx.h +F: docs/system/arm/palm.rst Raspberry Pi M: Peter Maydell @@ -697,6 +759,7 @@ F: hw/arm/realview* F: hw/cpu/realview_mpcore.c F: hw/intc/realview_gic.c F: include/hw/intc/realview_gic.h +F: docs/system/arm/realview.rst PXA2XX M: Andrzej Zaborowski @@ -716,6 +779,7 @@ F: hw/misc/max111x.c F: include/hw/arm/pxa.h F: include/hw/arm/sharpsl.h F: include/hw/display/tc6393xb.h +F: docs/system/arm/xscale.rst SABRELITE / i.MX6 M: Peter Maydell @@ -726,6 +790,8 @@ F: hw/arm/sabrelite.c F: hw/arm/fsl-imx6.c F: hw/misc/imx6_*.c F: hw/ssi/imx_spi.c +F: hw/usb/imx-usb-phy.c +F: include/hw/usb/imx-usb-phy.h F: include/hw/arm/fsl-imx6.h F: include/hw/misc/imx6_*.h F: include/hw/ssi/imx_spi.h @@ -733,7 +799,7 @@ F: include/hw/ssi/imx_spi.h SBSA-REF M: Radoslaw Biernacki M: Peter Maydell -R: Leif Lindholm +R: Leif Lindholm L: qemu-arm@nongnu.org S: Maintained F: hw/arm/sbsa-ref.c @@ -751,6 +817,7 @@ L: qemu-arm@nongnu.org S: Maintained F: hw/*/stellaris* F: include/hw/input/gamepad.h +F: docs/system/arm/stellaris.rst Versatile Express M: Peter Maydell @@ -764,6 +831,7 @@ L: qemu-arm@nongnu.org S: Maintained F: hw/*/versatile* F: hw/misc/arm_sysctl.c +F: docs/system/arm/versatile.rst Virt M: Peter Maydell @@ -805,6 +873,7 @@ F: hw/arm/virt-acpi-build.c STM32F205 M: Alistair Francis M: Peter Maydell +L: qemu-arm@nongnu.org S: Maintained F: hw/arm/stm32f205_soc.c F: hw/misc/stm32f2xx_syscfg.c @@ -814,15 +883,33 @@ F: hw/adc/* F: hw/ssi/stm32f2xx_spi.c F: include/hw/*/stm32*.h +STM32F405 +M: Alistair Francis +M: Peter Maydell +L: qemu-arm@nongnu.org +S: Maintained +F: hw/arm/stm32f405_soc.c +F: hw/misc/stm32f4xx_syscfg.c +F: hw/misc/stm32f4xx_exti.c + Netduino 2 M: Alistair Francis M: Peter Maydell +L: qemu-arm@nongnu.org S: Maintained F: hw/arm/netduino2.c +Netduino Plus 2 +M: Alistair Francis +M: Peter Maydell +L: qemu-arm@nongnu.org +S: Maintained +F: hw/arm/netduinoplus2.c + SmartFusion2 M: Subbaraya Sundeep M: Peter Maydell +L: qemu-arm@nongnu.org S: Maintained F: hw/arm/msf2-soc.c F: hw/misc/msf2-sysreg.c @@ -836,6 +923,7 @@ F: include/hw/ssi/mss-spi.h Emcraft M2S-FG484 M: Subbaraya Sundeep M: Peter Maydell +L: qemu-arm@nongnu.org S: Maintained F: hw/arm/msf2-som.c @@ -862,7 +950,7 @@ F: hw/*/nrf51*.c F: hw/*/microbit*.c F: include/hw/*/nrf51*.h F: include/hw/*/microbit*.h -F: tests/microbit-test.c +F: tests/qtest/microbit-test.c CRIS Machines ------------- @@ -874,10 +962,11 @@ F: hw/*/etraxfs_*.c HP-PARISC Machines ------------------ -Dino +HP B160L M: Richard Henderson R: Helge Deller S: Odd Fixes +F: default-configs/hppa-softmmu.mak F: hw/hppa/ F: pc-bios/hppa-firmware.img @@ -955,29 +1044,35 @@ F: hw/display/jazz_led.c F: hw/dma/rc4030.c Malta -M: Aurelien Jarno -R: Aleksandar Rikalo +M: Aleksandar Markovic +M: Philippe Mathieu-Daudé +R: Aurelien Jarno S: Maintained +F: hw/isa/piix4.c +F: hw/acpi/piix4.c F: hw/mips/mips_malta.c F: hw/mips/gt64xxx_pci.c +F: include/hw/southbridge/piix.h F: tests/acceptance/linux_ssh_mips_malta.py +F: tests/acceptance/machine_mips_malta.py Mipssim -M: Aleksandar Markovic +M: Aleksandar Markovic R: Aleksandar Rikalo S: Odd Fixes F: hw/mips/mips_mipssim.c F: hw/net/mipsnet.c R4000 -M: Aurelien Jarno +M: Aleksandar Markovic +R: Aurelien Jarno R: Aleksandar Rikalo -S: Maintained +S: Obsolete F: hw/mips/mips_r4k.c Fulong 2E -M: Aleksandar Markovic -R: Aleksandar Rikalo +M: Philippe Mathieu-Daudé +M: Aleksandar Markovic S: Odd Fixes F: hw/mips/mips_fulong2e.c F: hw/isa/vt82c686.c @@ -1081,7 +1176,6 @@ F: hw/dma/i82374.c F: hw/rtc/m48t59-isa.c F: include/hw/isa/pc87312.h F: include/hw/rtc/m48t59.h -F: pc-bios/ppc_rom.bin F: tests/acceptance/ppc_prep_40p.py sPAPR @@ -1095,10 +1189,10 @@ F: include/hw/*/xics* F: pc-bios/slof.bin F: docs/specs/ppc-spapr-hcalls.txt F: docs/specs/ppc-spapr-hotplug.txt -F: tests/spapr* -F: tests/libqos/*spapr* -F: tests/rtas* -F: tests/libqos/rtas* +F: tests/qtest/spapr* +F: tests/qtest/libqos/*spapr* +F: tests/qtest/rtas* +F: tests/qtest/libqos/rtas* PowerNV (Non-Virtualized) M: Cédric Le Goater @@ -1110,7 +1204,7 @@ F: hw/intc/pnv* F: hw/intc/xics_pnv.c F: include/hw/ppc/pnv* F: pc-bios/skiboot.lid -F: tests/pnv* +F: tests/qtest/pnv* virtex_ml507 M: Edgar E. Iglesias @@ -1215,7 +1309,7 @@ S: Supported F: hw/s390x/ipl.* F: pc-bios/s390-ccw/ F: pc-bios/s390-ccw.img -F: docs/devel/s390-dasd-ipl.txt +F: docs/devel/s390-dasd-ipl.rst T: git https://github.com/borntraeger/qemu.git s390-next L: qemu-s390x@nongnu.org @@ -1258,7 +1352,7 @@ F: hw/misc/sga.c F: hw/isa/apm.c F: include/hw/isa/apm.h F: tests/test-x86-cpuid.c -F: tests/test-x86-cpuid-compat.c +F: tests/qtest/test-x86-cpuid-compat.c PC Chipset M: Michael S. Tsirkin @@ -1354,10 +1448,10 @@ F: hw/ide/ F: hw/block/block.c F: hw/block/cdrom.c F: hw/block/hd-geometry.c -F: tests/ide-test.c -F: tests/ahci-test.c -F: tests/cdrom-test.c -F: tests/libqos/ahci* +F: tests/qtest/ide-test.c +F: tests/qtest/ahci-test.c +F: tests/qtest/cdrom-test.c +F: tests/qtest/libqos/ahci* T: git https://github.com/jnsnow/qemu.git ide IPMI @@ -1366,7 +1460,7 @@ S: Maintained F: include/hw/ipmi/* F: hw/ipmi/* F: hw/smbios/smbios_type_38.c -F: tests/ipmi* +F: tests/qtest/ipmi* T: git https://github.com/cminyard/qemu.git master-ipmi-rebase Floppy @@ -1375,11 +1469,12 @@ L: qemu-block@nongnu.org S: Supported F: hw/block/fdc.c F: include/hw/block/fdc.h -F: tests/fdc-test.c +F: tests/qtest/fdc-test.c T: git https://github.com/jnsnow/qemu.git ide OMAP M: Peter Maydell +L: qemu-arm@nongnu.org S: Maintained F: hw/*/omap* F: include/hw/arm/omap.h @@ -1413,8 +1508,8 @@ F: hw/acpi/* F: hw/smbios/* F: hw/i386/acpi-build.[hc] F: hw/arm/virt-acpi-build.c -F: tests/bios-tables-test.c -F: tests/acpi-utils.[hc] +F: tests/qtest/bios-tables-test.c +F: tests/qtest/acpi-utils.[hc] F: tests/data/acpi/ ppc4xx @@ -1437,7 +1532,7 @@ M: Jason Wang S: Odd Fixes F: hw/net/ F: include/hw/net/ -F: tests/virtio-net-test.c +F: tests/qtest/virtio-net-test.c F: docs/virtio-net-failover.rst T: git https://github.com/jasowang/qemu.git net @@ -1454,7 +1549,7 @@ R: Fam Zheng S: Supported F: include/hw/scsi/* F: hw/scsi/* -F: tests/virtio-scsi-test.c +F: tests/qtest/virtio-scsi-test.c T: git https://github.com/bonzini/qemu.git scsi-next SSI @@ -1464,7 +1559,7 @@ F: hw/ssi/* F: hw/block/m25p80.c F: include/hw/ssi/ssi.h X: hw/ssi/xilinx_* -F: tests/m25p80-test.c +F: tests/qtest/m25p80-test.c Xilinx SPI M: Alistair Francis @@ -1477,13 +1572,14 @@ S: Odd Fixes F: include/hw/sd/sd* F: hw/sd/core.c F: hw/sd/sd* -F: tests/sd* +F: hw/sd/ssi-sd.c +F: tests/qtest/sd* USB M: Gerd Hoffmann S: Maintained F: hw/usb/* -F: tests/usb-*-test.c +F: tests/qtest/usb-*-test.c F: docs/usb2.txt F: docs/usb-storage.txt F: include/hw/usb.h @@ -1524,7 +1620,7 @@ F: hw/s390x/ap-bridge.c F: include/hw/s390x/ap-device.h F: include/hw/s390x/ap-bridge.h F: hw/vfio/ap.c -F: docs/vfio-ap.txt +F: docs/system/s390x/vfio-ap.rst L: qemu-s390x@nongnu.org vhost @@ -1545,15 +1641,25 @@ F: hw/virtio/Makefile.objs F: hw/virtio/trace-events F: net/vhost-user.c F: include/hw/virtio/ -F: tests/virtio-balloon-test.c + +virtio-balloon +M: Michael S. Tsirkin +M: David Hildenbrand +S: Maintained +F: hw/virtio/virtio-balloon*.c +F: include/hw/virtio/virtio-balloon.h +F: balloon.c +F: include/sysemu/balloon.h virtio-9p M: Greg Kurz +R: Christian Schoenebeck S: Odd Fixes F: hw/9pfs/ X: hw/9pfs/xen-9p* F: fsdev/ -F: tests/virtio-9p-test.c +F: docs/interop/virtfs-proxy-helper.rst +F: tests/qtest/virtio-9p-test.c T: git https://github.com/gkurz/qemu.git 9p-next virtio-blk @@ -1562,7 +1668,7 @@ L: qemu-block@nongnu.org S: Supported F: hw/block/virtio-blk.c F: hw/block/dataplane/* -F: tests/virtio-blk-test.c +F: tests/qtest/virtio-blk-test.c T: git https://github.com/stefanha/qemu.git block virtio-ccw @@ -1575,6 +1681,15 @@ T: git https://github.com/cohuck/qemu.git s390-next T: git https://github.com/borntraeger/qemu.git s390-next L: qemu-s390x@nongnu.org +virtiofs +M: Dr. David Alan Gilbert +M: Stefan Hajnoczi +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 S: Maintained @@ -1583,6 +1698,12 @@ F: hw/input/virtio-input*.c F: include/hw/virtio/virtio-input.h F: contrib/vhost-user-input/* +virtio-iommu +M: Eric Auger +S: Maintained +F: hw/virtio/virtio-iommu*.c +F: include/hw/virtio/virtio-iommu.h + virtio-serial M: Laurent Vivier R: Amit Shah @@ -1590,8 +1711,7 @@ S: Supported F: hw/char/virtio-serial-bus.c F: hw/char/virtio-console.c F: include/hw/virtio/virtio-serial.h -F: tests/virtio-console-test.c -F: tests/virtio-serial-test.c +F: tests/qtest/virtio-serial-test.c virtio-rng M: Laurent Vivier @@ -1601,7 +1721,7 @@ F: hw/virtio/virtio-rng.c F: include/hw/virtio/virtio-rng.h F: include/sysemu/rng*.h F: backends/rng*.c -F: tests/virtio-rng-test.c +F: tests/qtest/virtio-rng-test.c virtio-crypto M: Gonglei @@ -1615,7 +1735,7 @@ M: Keith Busch L: qemu-block@nongnu.org S: Supported F: hw/block/nvme* -F: tests/nvme-test.c +F: tests/qtest/nvme-test.c megasas M: Hannes Reinecke @@ -1623,7 +1743,7 @@ L: qemu-block@nongnu.org S: Supported F: hw/scsi/megasas.c F: hw/scsi/mfi.h -F: tests/megasas-test.c +F: tests/qtest/megasas-test.c Network packet abstractions M: Dmitry Fleytman @@ -1638,7 +1758,7 @@ M: Dmitry Fleytman S: Maintained F: hw/net/vmxnet* F: hw/scsi/vmw_pvscsi* -F: tests/vmxnet3-test.c +F: tests/qtest/vmxnet3-test.c Rocker M: Jiri Pirko @@ -1686,7 +1806,7 @@ F: docs/generic-loader.txt Intel Hexadecimal Object File Loader M: Su Hang S: Maintained -F: tests/hexloader-test.c +F: tests/qtest/hexloader-test.c F: tests/data/hex-loader/test.hex CHRP NVRAM @@ -1694,7 +1814,7 @@ M: Thomas Huth S: Maintained F: hw/nvram/chrp_nvram.c F: include/hw/nvram/chrp_nvram.h -F: tests/prom-env-test.c +F: tests/qtest/prom-env-test.c VM Generation ID M: Ben Warren @@ -1702,7 +1822,7 @@ S: Maintained F: hw/acpi/vmgenid.c F: include/hw/acpi/vmgenid.h F: docs/specs/vmgenid.txt -F: tests/vmgenid-test.c +F: tests/qtest/vmgenid-test.c F: stubs/vmgenid.c Unimplemented device @@ -1733,6 +1853,18 @@ F: hw/display/virtio-gpu* F: hw/display/virtio-vga.* F: include/hw/virtio/virtio-gpu.h +vhost-user-blk +M: Raphael Norwitz +S: Maintained +F: contrib/vhost-user-blk/ +F: contrib/vhost-user-scsi/ +F: hw/block/vhost-user-blk.c +F: hw/scsi/vhost-user-scsi.c +F: hw/virtio/vhost-user-blk-pci.c +F: hw/virtio/vhost-user-scsi-pci.c +F: include/hw/virtio/vhost-user-blk.h +F: include/hw/virtio/vhost-user-scsi.h + vhost-user-gpu M: Marc-André Lureau M: Gerd Hoffmann @@ -1771,8 +1903,8 @@ F: hw/nvram/fw_cfg.c F: stubs/fw_cfg.c F: include/hw/nvram/fw_cfg.h F: include/standard-headers/linux/qemu_fw_cfg.h -F: tests/libqos/fw_cfg.c -F: tests/fw_cfg-test.c +F: tests/qtest/libqos/fw_cfg.c +F: tests/qtest/fw_cfg-test.c T: git https://github.com/philmd/qemu.git fw_cfg-next XIVE @@ -1792,9 +1924,9 @@ S: Maintained F: audio/ F: hw/audio/ F: include/hw/audio/ -F: tests/ac97-test.c -F: tests/es1370-test.c -F: tests/intel-hda-test.c +F: tests/qtest/ac97-test.c +F: tests/qtest/es1370-test.c +F: tests/qtest/intel-hda-test.c Block layer core M: Kevin Wolf @@ -1806,6 +1938,7 @@ F: block/ F: hw/block/ F: include/block/ F: qemu-img* +F: docs/interop/qemu-img.rst F: qemu-io* F: tests/qemu-iotests/ F: util/qemu-progress.c @@ -1820,6 +1953,8 @@ L: qemu-block@nongnu.org S: Supported F: util/async.c F: util/aio-*.c +F: util/aio-*.h +F: util/fdmon-*.c F: block/io.c F: migration/block* F: include/block/aio.h @@ -1855,6 +1990,7 @@ Block QAPI, monitor, command line M: Markus Armbruster S: Supported F: blockdev.c +F: blockdev-hmp-cmds.c F: block/qapi.c F: qapi/block*.json F: qapi/transaction.json @@ -1865,12 +2001,12 @@ M: John Snow R: Vladimir Sementsov-Ogievskiy L: qemu-block@nongnu.org S: Supported -F: util/hbitmap.c -F: block/dirty-bitmap.c F: include/qemu/hbitmap.h F: include/block/dirty-bitmap.h -F: qcow2-bitmap.c +F: block/dirty-bitmap.c +F: block/qcow2-bitmap.c F: migration/block-dirty-bitmap.c +F: util/hbitmap.c F: tests/test-hbitmap.c F: docs/interop/bitmaps.rst T: git https://github.com/jnsnow/qemu.git bitmaps @@ -1902,6 +2038,11 @@ M: Markus Armbruster S: Supported F: scripts/coverity-model.c +Coverity Scan integration +M: Peter Maydell +S: Maintained +F: scripts/coverity-scan/ + Device Tree M: Alistair Francis R: David Gibson @@ -1930,6 +2071,11 @@ F: include/qemu/error-report.h F: qapi/error.json F: util/error.c F: util/qemu-error.c +F: scripts/coccinelle/err-bad-newline.cocci +F: scripts/coccinelle/error-use-after-free.cocci +F: scripts/coccinelle/error_propagate_null.cocci +F: scripts/coccinelle/remove_local_err.cocci +F: scripts/coccinelle/use-error_fatal.cocci GDB stub M: Alex Bennée @@ -1946,9 +2092,11 @@ F: ioport.c F: include/exec/memop.h F: include/exec/memory.h F: include/exec/ram_addr.h +F: include/exec/ramblock.h F: memory.c F: include/exec/memory-internal.h F: exec.c +F: scripts/coccinelle/memory-region-housekeeping.cocci SPICE M: Gerd Hoffmann @@ -1982,7 +2130,8 @@ F: include/qemu/main-loop.h F: include/sysemu/runstate.h F: util/main-loop.c F: util/qemu-timer.c -F: vl.c +F: softmmu/vl.c +F: softmmu/main.c F: qapi/run-state.json Human Monitor (HMP) @@ -1995,7 +2144,7 @@ F: monitor/hmp* F: hmp.h F: hmp-commands*.hx F: include/monitor/hmp-target.h -F: tests/test-hmp.c +F: tests/qtest/test-hmp.c F: include/qemu/qemu-print.h F: util/qemu-print.c @@ -2038,6 +2187,11 @@ F: python/qemu/*py F: scripts/*.py F: tests/*.py +Benchmark util +M: Vladimir Sementsov-Ogievskiy +S: Maintained +F: scripts/simplebench/ + QAPI M: Markus Armbruster M: Michael Roth @@ -2121,8 +2275,8 @@ F: qapi/error.json F: docs/devel/*qmp-* F: docs/interop/*qmp-* F: scripts/qmp/ -F: tests/qmp-test.c -F: tests/qmp-cmd-test.c +F: tests/qtest/qmp-test.c +F: tests/qtest/qmp-cmd-test.c T: git https://repo.or.cz/qemu/armbru.git qapi-next qtest @@ -2132,9 +2286,15 @@ R: Paolo Bonzini S: Maintained F: qtest.c F: accel/qtest.c -F: tests/libqtest* -F: tests/libqos/ -F: tests/*-test.c +F: tests/qtest/ + +Device Fuzzing +M: Alexander Bulekov +R: Paolo Bonzini +R: Bandan Das +R: Stefan Hajnoczi +S: Maintained +F: tests/qtest/fuzz/ Register API M: Alistair Francis @@ -2161,10 +2321,11 @@ M: Stefan Hajnoczi S: Maintained F: trace/ F: trace-events -F: qemu-option-trace.texi +F: docs/qemu-option-trace.rst.inc F: scripts/tracetool.py F: scripts/tracetool/ F: scripts/qemu-trace-stap* +F: docs/interop/qemu-trace-stap.rst F: docs/devel/tracing.txt T: git https://github.com/stefanha/qemu.git tracing @@ -2178,7 +2339,7 @@ F: include/hw/acpi/tpm.h F: include/sysemu/tpm* F: qapi/tpm.json F: backends/tpm.c -F: tests/*tpm* +F: tests/qtest/*tpm* T: git https://github.com/stefanberger/qemu-tpm.git tpm-next Checkpatch @@ -2189,14 +2350,26 @@ Migration M: Juan Quintela M: Dr. David Alan Gilbert S: Maintained +F: hw/core/vmstate-if.c +F: include/hw/vmstate-if.h F: include/migration/ F: migration/ F: scripts/vmstate-static-checker.py F: tests/vmstate-static-checker-data/ -F: tests/migration-test.c +F: tests/qtest/migration-test.c F: docs/devel/migration.rst F: qapi/migration.json +D-Bus +M: Marc-André Lureau +S: Maintained +F: backends/dbus-vmstate.c +F: tests/dbus-vmstate* +F: util/dbus.c +F: include/qemu/dbus.h +F: docs/interop/dbus.rst +F: docs/interop/dbus-vmstate.rst + Seccomp M: Eduardo Otubo S: Supported @@ -2339,6 +2512,8 @@ F: roms/edk2 F: roms/edk2-* F: tests/data/uefi-boot-images/ F: tests/uefi-test-tools/ +F: .gitlab-ci-edk2.yml +F: .gitlab-ci.d/edk2/ Usermode Emulation ------------------ @@ -2360,6 +2535,9 @@ S: Maintained F: linux-user/ F: default-configs/*-linux-user.mak F: scripts/qemu-binfmt-conf.sh +F: scripts/update-syscalltbl.sh +F: scripts/update-mips-syscall-args.sh +F: scripts/gensyscalls.sh Tiny Code Generator (TCG) ------------------------- @@ -2367,6 +2545,7 @@ Common TCG code M: Richard Henderson S: Maintained F: tcg/ +F: include/tcg/ TCG Plugins M: Alex Bennée @@ -2376,8 +2555,7 @@ F: plugins/ F: tests/plugin AArch64 TCG target -M: Claudio Fontana -M: Claudio Fontana +M: Richard Henderson S: Maintained L: qemu-arm@nongnu.org F: tcg/aarch64/ @@ -2398,7 +2576,8 @@ F: tcg/i386/ F: disas/i386.c MIPS TCG target -M: Aurelien Jarno +M: Aleksandar Markovic +R: Aurelien Jarno R: Aleksandar Rikalo S: Maintained F: tcg/mips/ @@ -2488,6 +2667,7 @@ F: include/block/nbd* F: qemu-nbd.* F: blockdev-nbd.c F: docs/interop/nbd.txt +F: docs/interop/qemu-nbd.rst T: git https://repo.or.cz/qemu/ericb.git nbd NFS @@ -2591,6 +2771,15 @@ F: block/file-posix.c F: block/file-win32.c F: block/win32-aio.c +Linux io_uring +M: Aarushi Mehta +M: Julia Suvorova +M: Stefan Hajnoczi +L: qemu-block@nongnu.org +S: Maintained +F: block/io_uring.c +F: stubs/io_uring.c + qcow2 M: Kevin Wolf M: Max Reitz @@ -2640,7 +2829,7 @@ F: tests/test-replication.c F: docs/block-replication.txt PVRDMA -M: Yuval Shaia +M: Yuval Shaia M: Marcel Apfelbaum S: Maintained F: hw/rdma/* @@ -2662,6 +2851,7 @@ M: Alex Bennée M: Fam Zheng R: Philippe Mathieu-Daudé S: Maintained +F: .github/lockdown.yml F: .travis.yml F: scripts/travis/ F: .shippable.yml @@ -2706,7 +2896,7 @@ F: contrib/gitdm/* Incompatible changes R: libvir-list@redhat.com -F: qemu-deprecated.texi +F: docs/system/deprecated.rst Build System ------------ @@ -2715,6 +2905,10 @@ M: Daniel P. Berrange S: Odd Fixes F: scripts/git-submodule.sh +UI translations +M: Aleksandar Markovic +F: po/*.po + Sphinx documentation configuration and build machinery M: Peter Maydell S: Maintained diff --git a/Makefile b/Makefile index cddc24bc66..b7171e042d 100644 --- a/Makefile +++ b/Makefile @@ -18,9 +18,6 @@ UNCHECKED_GOALS := %clean TAGS cscope ctags dist \ help check-help print-% \ docker docker-% vm-help vm-test vm-build-% -print-%: - @echo '$*=$($*)' - # All following code might depend on configuration variables ifneq ($(wildcard config-host.mak),) # Put the all: rule here so that config-host.mak can contain dependencies. @@ -120,6 +117,7 @@ GENERATED_QAPI_FILES += qapi/qapi-builtin-visit.h qapi/qapi-builtin-visit.c GENERATED_QAPI_FILES += qapi/qapi-visit.h qapi/qapi-visit.c GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-visit-%.h) GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-visit-%.c) +GENERATED_QAPI_FILES += qapi/qapi-init-commands.h qapi/qapi-init-commands.c GENERATED_QAPI_FILES += qapi/qapi-commands.h qapi/qapi-commands.c GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-commands-%.h) GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-commands-%.c) @@ -130,7 +128,28 @@ GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-events-%.c) GENERATED_QAPI_FILES += qapi/qapi-introspect.c qapi/qapi-introspect.h GENERATED_QAPI_FILES += qapi/qapi-doc.texi +# The following list considers only the storage daemon main module. All other +# modules are currently shared with the main schema, so we don't actually +# generate additional files. + +GENERATED_STORAGE_DAEMON_QAPI_FILES = storage-daemon/qapi/qapi-commands.h +GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-commands.c +GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-emit-events.h +GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-emit-events.c +GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-events.h +GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-events.c +GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-init-commands.h +GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-init-commands.c +GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-introspect.h +GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-introspect.c +GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-types.h +GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-types.c +GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-visit.h +GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-visit.c +GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-doc.texi + generated-files-y += $(GENERATED_QAPI_FILES) +generated-files-y += $(GENERATED_STORAGE_DAEMON_QAPI_FILES) generated-files-y += trace/generated-tcg-tracers.h @@ -324,13 +343,14 @@ HELPERS-y = HELPERS-$(call land,$(CONFIG_SOFTMMU),$(CONFIG_LINUX)) = qemu-bridge-helper$(EXESUF) -ifdef CONFIG_LINUX -ifdef CONFIG_VIRGL -ifdef CONFIG_GBM +ifeq ($(CONFIG_LINUX)$(CONFIG_VIRGL)$(CONFIG_GBM)$(CONFIG_TOOLS),yyyy) HELPERS-y += vhost-user-gpu$(EXESUF) vhost-user-json-y += contrib/vhost-user-gpu/50-qemu-gpu.json endif -endif + +ifeq ($(CONFIG_LINUX)$(CONFIG_SECCOMP)$(CONFIG_LIBCAP_NG),yyy) +HELPERS-y += virtiofsd$(EXESUF) +vhost-user-json-y += tools/virtiofsd/50-qemu-virtiofsd.json endif # Sphinx does not allow building manuals into the same directory as @@ -345,16 +365,23 @@ MANUAL_BUILDDIR := docs endif ifdef BUILD_DOCS -DOCS=qemu-doc.html qemu-doc.txt qemu.1 qemu-img.1 qemu-nbd.8 $(MANUAL_BUILDDIR)/interop/qemu-ga.8 +DOCS+=$(MANUAL_BUILDDIR)/system/qemu.1 +DOCS+=$(MANUAL_BUILDDIR)/tools/qemu-img.1 +DOCS+=$(MANUAL_BUILDDIR)/tools/qemu-nbd.8 +DOCS+=$(MANUAL_BUILDDIR)/interop/qemu-ga.8 +ifeq ($(CONFIG_LINUX)$(CONFIG_SECCOMP)$(CONFIG_LIBCAP_NG),yyy) +DOCS+=$(MANUAL_BUILDDIR)/tools/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 -DOCS+=docs/qemu-block-drivers.7 -DOCS+=docs/qemu-cpu-models.7 +DOCS+=$(MANUAL_BUILDDIR)/system/qemu-cpu-models.7 +DOCS+=$(MANUAL_BUILDDIR)/index.html ifdef CONFIG_VIRTFS -DOCS+=fsdev/virtfs-proxy-helper.1 +DOCS+=$(MANUAL_BUILDDIR)/tools/virtfs-proxy-helper.1 endif ifdef CONFIG_TRACE_SYSTEMTAP -DOCS+=scripts/qemu-trace-stap.1 +DOCS+=$(MANUAL_BUILDDIR)/tools/qemu-trace-stap.1 endif else DOCS= @@ -389,15 +416,16 @@ MINIKCONF_ARGS = \ CONFIG_OPENGL=$(CONFIG_OPENGL) \ CONFIG_X11=$(CONFIG_X11) \ CONFIG_VHOST_USER=$(CONFIG_VHOST_USER) \ + CONFIG_VHOST_KERNEL=$(CONFIG_VHOST_KERNEL) \ CONFIG_VIRTFS=$(CONFIG_VIRTFS) \ CONFIG_LINUX=$(CONFIG_LINUX) \ CONFIG_PVRDMA=$(CONFIG_PVRDMA) -MINIKCONF_INPUTS = $(SRC_PATH)/Kconfig.host $(SRC_PATH)/hw/Kconfig \ - $(wildcard $(SRC_PATH)/hw/*/Kconfig) +MINIKCONF_INPUTS = $(SRC_PATH)/Kconfig.host $(SRC_PATH)/hw/Kconfig +MINIKCONF_DEPS = $(MINIKCONF_INPUTS) $(wildcard $(SRC_PATH)/hw/*/Kconfig) MINIKCONF = $(PYTHON) $(SRC_PATH)/scripts/minikconf.py \ -$(SUBDIR_DEVICES_MAK): %/config-devices.mak: default-configs/%.mak $(MINIKCONF_INPUTS) $(BUILD_DIR)/config-host.mak +$(SUBDIR_DEVICES_MAK): %/config-devices.mak: default-configs/%.mak $(MINIKCONF_DEPS) $(BUILD_DIR)/config-host.mak $(call quiet-command, $(MINIKCONF) $(MINIKCONF_ARGS) > $@.tmp, "GEN", "$@.tmp") $(call quiet-command, if test -f $@; then \ if cmp -s $@.old $@; then \ @@ -433,6 +461,7 @@ dummy := $(call unnest-vars,, \ elf2dmp-obj-y \ ivshmem-client-obj-y \ ivshmem-server-obj-y \ + virtiofsd-obj-y \ rdmacm-mux-obj-y \ libvhost-user-obj-y \ vhost-user-scsi-obj-y \ @@ -442,16 +471,13 @@ dummy := $(call unnest-vars,, \ qga-vss-dll-obj-y \ block-obj-y \ block-obj-m \ + storage-daemon-obj-y \ + storage-daemon-obj-m \ crypto-obj-y \ - crypto-user-obj-y \ qom-obj-y \ io-obj-y \ common-obj-y \ common-obj-m \ - ui-obj-y \ - ui-obj-m \ - audio-obj-y \ - audio-obj-m \ trace-obj-y) include $(SRC_PATH)/tests/Makefile.include @@ -474,11 +500,12 @@ config-host.h-timestamp: config-host.mak qemu-options.def: $(SRC_PATH)/qemu-options.hx $(SRC_PATH)/scripts/hxtool $(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -h < $< > $@,"GEN","$@") -TARGET_DIRS_RULES := $(foreach t, all clean install, $(addsuffix /$(t), $(TARGET_DIRS))) +TARGET_DIRS_RULES := $(foreach t, all fuzz clean install, $(addsuffix /$(t), $(TARGET_DIRS))) SOFTMMU_ALL_RULES=$(filter %-softmmu/all, $(TARGET_DIRS_RULES)) $(SOFTMMU_ALL_RULES): $(authz-obj-y) $(SOFTMMU_ALL_RULES): $(block-obj-y) +$(SOFTMMU_ALL_RULES): $(storage-daemon-obj-y) $(SOFTMMU_ALL_RULES): $(chardev-obj-y) $(SOFTMMU_ALL_RULES): $(crypto-obj-y) $(SOFTMMU_ALL_RULES): $(io-obj-y) @@ -487,6 +514,15 @@ ifdef DECOMPRESS_EDK2_BLOBS $(SOFTMMU_ALL_RULES): $(edk2-decompressed) endif +SOFTMMU_FUZZ_RULES=$(filter %-softmmu/fuzz, $(TARGET_DIRS_RULES)) +$(SOFTMMU_FUZZ_RULES): $(authz-obj-y) +$(SOFTMMU_FUZZ_RULES): $(block-obj-y) +$(SOFTMMU_FUZZ_RULES): $(chardev-obj-y) +$(SOFTMMU_FUZZ_RULES): $(crypto-obj-y) +$(SOFTMMU_FUZZ_RULES): $(io-obj-y) +$(SOFTMMU_FUZZ_RULES): config-all-devices.mak +$(SOFTMMU_FUZZ_RULES): $(edk2-decompressed) + .PHONY: $(TARGET_DIRS_RULES) # The $(TARGET_DIRS_RULES) are of the form SUBDIR/GOAL, so that # $(dir $@) yields the sub-directory, and $(notdir $@) yields the sub-goal @@ -499,7 +535,7 @@ DTC_CPPFLAGS=-I$(BUILD_DIR)/dtc -I$(SRC_PATH)/dtc -I$(SRC_PATH)/dtc/libfdt .PHONY: dtc/all dtc/all: .git-submodule-status dtc/libfdt dtc/tests - $(call quiet-command,$(MAKE) $(DTC_MAKE_ARGS) CPPFLAGS="$(DTC_CPPFLAGS)" CFLAGS="$(DTC_CFLAGS)" LDFLAGS="$(LDFLAGS)" ARFLAGS="$(ARFLAGS)" CC="$(CC)" AR="$(AR)" LD="$(LD)" $(SUBDIR_MAKEFLAGS) libfdt/libfdt.a,) + $(call quiet-command,$(MAKE) $(DTC_MAKE_ARGS) CPPFLAGS="$(DTC_CPPFLAGS)" CFLAGS="$(DTC_CFLAGS)" LDFLAGS="$(QEMU_LDFLAGS)" ARFLAGS="$(ARFLAGS)" CC="$(CC)" AR="$(AR)" LD="$(LD)" $(SUBDIR_MAKEFLAGS) libfdt/libfdt.a,) dtc/%: .git-submodule-status @mkdir -p $@ @@ -526,7 +562,7 @@ slirp/all: .git-submodule-status BUILD_DIR="$(BUILD_DIR)/slirp" \ PKG_CONFIG="$(PKG_CONFIG)" \ CC="$(CC)" AR="$(AR)" LD="$(LD)" RANLIB="$(RANLIB)" \ - CFLAGS="$(QEMU_CFLAGS) $(CFLAGS)" LDFLAGS="$(LDFLAGS)") + CFLAGS="$(QEMU_CFLAGS) $(CFLAGS)" LDFLAGS="$(QEMU_LDFLAGS)") # Compatibility gunk to keep make working across the rename of targets # for recursion, to be removed some time after 4.1. @@ -535,6 +571,9 @@ subdir-capstone: capstone/all subdir-slirp: slirp/all $(filter %/all, $(TARGET_DIRS_RULES)): libqemuutil.a $(common-obj-y) \ + $(qom-obj-y) + +$(filter %/fuzz, $(TARGET_DIRS_RULES)): libqemuutil.a $(common-obj-y) \ $(qom-obj-y) $(crypto-user-obj-$(CONFIG_USER_ONLY)) ROM_DIRS = $(addprefix pc-bios/, $(ROMS)) @@ -546,6 +585,7 @@ $(ROM_DIRS_RULES): .PHONY: recurse-all recurse-clean recurse-install recurse-all: $(addsuffix /all, $(TARGET_DIRS) $(ROM_DIRS)) +recurse-fuzz: $(addsuffix /fuzz, $(TARGET_DIRS) $(ROM_DIRS)) recurse-clean: $(addsuffix /clean, $(TARGET_DIRS) $(ROM_DIRS)) recurse-install: $(addsuffix /install, $(TARGET_DIRS)) $(addsuffix /install, $(TARGET_DIRS)): all @@ -570,6 +610,7 @@ qemu-img.o: qemu-img-cmds.h qemu-img$(EXESUF): qemu-img.o $(authz-obj-y) $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS) qemu-nbd$(EXESUF): qemu-nbd.o $(authz-obj-y) $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS) qemu-io$(EXESUF): qemu-io.o $(authz-obj-y) $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS) +qemu-storage-daemon$(EXESUF): qemu-storage-daemon.o $(authz-obj-y) $(block-obj-y) $(crypto-obj-y) $(chardev-obj-y) $(io-obj-y) $(qom-obj-y) $(storage-daemon-obj-y) $(COMMON_LDADDS) qemu-bridge-helper$(EXESUF): qemu-bridge-helper.o $(COMMON_LDADDS) @@ -578,7 +619,6 @@ qemu-keymap$(EXESUF): qemu-keymap.o ui/input-keymap.o $(COMMON_LDADDS) qemu-edid$(EXESUF): qemu-edid.o hw/display/edid-generate.o $(COMMON_LDADDS) fsdev/virtfs-proxy-helper$(EXESUF): fsdev/virtfs-proxy-helper.o fsdev/9p-marshal.o fsdev/9p-iov-marshal.o $(COMMON_LDADDS) -fsdev/virtfs-proxy-helper$(EXESUF): LIBS += -lcap scsi/qemu-pr-helper$(EXESUF): scsi/qemu-pr-helper.o scsi/utils.o $(authz-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS) ifdef CONFIG_MPATH @@ -613,6 +653,7 @@ $(SRC_PATH)/scripts/qapi-gen.py qga/qapi-generated/qga-qapi-types.c qga/qapi-generated/qga-qapi-types.h \ qga/qapi-generated/qga-qapi-visit.c qga/qapi-generated/qga-qapi-visit.h \ qga/qapi-generated/qga-qapi-commands.h qga/qapi-generated/qga-qapi-commands.c \ +qga/qapi-generated/qga-qapi-init-commands.h qga/qapi-generated/qga-qapi-init-commands.c \ qga/qapi-generated/qga-qapi-doc.texi: \ qga/qapi-generated/qapi-gen-timestamp ; qga/qapi-generated/qapi-gen-timestamp: $(SRC_PATH)/qga/qapi-schema.json $(qapi-py) @@ -631,7 +672,18 @@ qapi-gen-timestamp: $(qapi-modules) $(qapi-py) "GEN","$(@:%-timestamp=%)") @>$@ -QGALIB_GEN=$(addprefix qga/qapi-generated/, qga-qapi-types.h qga-qapi-visit.h qga-qapi-commands.h) +qapi-modules-storage-daemon = \ + $(SRC_PATH)/storage-daemon/qapi/qapi-schema.json \ + $(QAPI_MODULES_STORAGE_DAEMON:%=$(SRC_PATH)/qapi/%.json) + +$(GENERATED_STORAGE_DAEMON_QAPI_FILES): storage-daemon/qapi/qapi-gen-timestamp ; +storage-daemon/qapi/qapi-gen-timestamp: $(qapi-modules-storage-daemon) $(qapi-py) + $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-gen.py \ + -o "storage-daemon/qapi" $<, \ + "GEN","$(@:%-timestamp=%)") + @>$@ + +QGALIB_GEN=$(addprefix qga/qapi-generated/, qga-qapi-types.h qga-qapi-visit.h qga-qapi-commands.h qga-qapi-init-commands.h) $(qga-obj-y): $(QGALIB_GEN) qemu-ga$(EXESUF): $(qga-obj-y) $(COMMON_LDADDS) @@ -677,6 +729,12 @@ rdmacm-mux$(EXESUF): LIBS += "-libumad" rdmacm-mux$(EXESUF): $(rdmacm-mux-obj-y) $(COMMON_LDADDS) $(call LINK, $^) +# relies on Linux-specific syscalls +ifeq ($(CONFIG_LINUX)$(CONFIG_SECCOMP)$(CONFIG_LIBCAP_NG),yyy) +virtiofsd$(EXESUF): $(virtiofsd-obj-y) libvhost-user.a $(COMMON_LDADDS) + $(call LINK, $^) +endif + vhost-user-gpu$(EXESUF): $(vhost-user-gpu-obj-y) $(libvhost-user-obj-y) libqemuutil.a libqemustub.a $(call LINK, $^) @@ -723,6 +781,7 @@ clean: recurse-clean rm -f trace/generated-tracers-dtrace.h* rm -f $(foreach f,$(generated-files-y),$(f) $(f)-timestamp) rm -f qapi-gen-timestamp + rm -f storage-daemon/qapi/qapi-gen-timestamp rm -rf qga/qapi-generated rm -f config-all-devices.mak @@ -739,16 +798,12 @@ rm -f $(MANUAL_BUILDDIR)/$1/objects.inv $(MANUAL_BUILDDIR)/$1/searchindex.js $(M endef distclean: clean - rm -f config-host.mak config-host.h* config-host.ld $(DOCS) qemu-options.texi qemu-img-cmds.texi qemu-monitor.texi qemu-monitor-info.texi + rm -f config-host.mak config-host.h* $(DOCS) rm -f tests/tcg/config-*.mak rm -f config-all-devices.mak config-all-disas.mak config.status rm -f $(SUBDIR_DEVICES_MAK) rm -f po/*.mo tests/qemu-iotests/common.env rm -f roms/seabios/config.mak roms/vgabios/config.mak - rm -f qemu-doc.info qemu-doc.aux qemu-doc.cp qemu-doc.cps - rm -f qemu-doc.fn qemu-doc.fns qemu-doc.info qemu-doc.ky qemu-doc.kys - rm -f qemu-doc.log qemu-doc.pdf qemu-doc.pg qemu-doc.toc qemu-doc.tp - rm -f qemu-doc.vr qemu-doc.txt rm -f qemu-plugins-ld.symbols qemu-plugins-ld64.symbols rm -f config.log rm -f linux-headers/asm @@ -758,12 +813,13 @@ distclean: clean rm -f docs/interop/qemu-qmp-ref.txt docs/interop/qemu-ga-ref.txt rm -f docs/interop/qemu-qmp-ref.pdf docs/interop/qemu-ga-ref.pdf rm -f docs/interop/qemu-qmp-ref.html docs/interop/qemu-ga-ref.html - rm -f docs/qemu-block-drivers.7 - rm -f docs/qemu-cpu-models.7 rm -rf .doctrees $(call clean-manual,devel) $(call clean-manual,interop) $(call clean-manual,specs) + $(call clean-manual,system) + $(call clean-manual,tools) + $(call clean-manual,user) for d in $(TARGET_DIRS); do \ rm -rf $$d || exit 1 ; \ done @@ -779,7 +835,7 @@ ifdef INSTALL_BLOBS BLOBS=bios.bin bios-256k.bin bios-microvm.bin sgabios.bin vgabios.bin vgabios-cirrus.bin \ vgabios-stdvga.bin vgabios-vmware.bin vgabios-qxl.bin vgabios-virtio.bin \ vgabios-ramfb.bin vgabios-bochs-display.bin vgabios-ati.bin \ -ppc_rom.bin openbios-sparc32 openbios-sparc64 openbios-ppc QEMU,tcx.bin QEMU,cgthree.bin \ +openbios-sparc32 openbios-sparc64 openbios-ppc QEMU,tcx.bin QEMU,cgthree.bin \ pxe-e1000.rom pxe-eepro100.rom pxe-ne2k_pci.rom \ pxe-pcnet.rom pxe-rtl8139.rom pxe-virtio.rom \ efi-e1000.rom efi-eepro100.rom efi-ne2k_pci.rom \ @@ -795,7 +851,7 @@ u-boot.e500 u-boot-sam460-20100605.bin \ qemu_vga.ndrv \ edk2-licenses.txt \ hppa-firmware.img \ -opensbi-riscv32-virt-fw_jump.bin \ +opensbi-riscv32-sifive_u-fw_jump.bin opensbi-riscv32-virt-fw_jump.bin \ opensbi-riscv64-sifive_u-fw_jump.bin opensbi-riscv64-virt-fw_jump.bin @@ -820,27 +876,29 @@ endef install-sphinxdocs: sphinxdocs $(call install-manual,interop) $(call install-manual,specs) + $(call install-manual,system) + $(call install-manual,tools) + $(call install-manual,user) install-doc: $(DOCS) install-sphinxdocs $(INSTALL_DIR) "$(DESTDIR)$(qemu_docdir)" - $(INSTALL_DATA) qemu-doc.html "$(DESTDIR)$(qemu_docdir)" - $(INSTALL_DATA) qemu-doc.txt "$(DESTDIR)$(qemu_docdir)" + $(INSTALL_DATA) $(MANUAL_BUILDDIR)/index.html "$(DESTDIR)$(qemu_docdir)" $(INSTALL_DATA) docs/interop/qemu-qmp-ref.html "$(DESTDIR)$(qemu_docdir)" $(INSTALL_DATA) docs/interop/qemu-qmp-ref.txt "$(DESTDIR)$(qemu_docdir)" ifdef CONFIG_POSIX $(INSTALL_DIR) "$(DESTDIR)$(mandir)/man1" - $(INSTALL_DATA) qemu.1 "$(DESTDIR)$(mandir)/man1" + $(INSTALL_DATA) $(MANUAL_BUILDDIR)/system/qemu.1 "$(DESTDIR)$(mandir)/man1" $(INSTALL_DIR) "$(DESTDIR)$(mandir)/man7" $(INSTALL_DATA) docs/interop/qemu-qmp-ref.7 "$(DESTDIR)$(mandir)/man7" - $(INSTALL_DATA) docs/qemu-block-drivers.7 "$(DESTDIR)$(mandir)/man7" - $(INSTALL_DATA) docs/qemu-cpu-models.7 "$(DESTDIR)$(mandir)/man7" + $(INSTALL_DATA) $(MANUAL_BUILDDIR)/system/qemu-block-drivers.7 "$(DESTDIR)$(mandir)/man7" + $(INSTALL_DATA) $(MANUAL_BUILDDIR)/system/qemu-cpu-models.7 "$(DESTDIR)$(mandir)/man7" ifeq ($(CONFIG_TOOLS),y) - $(INSTALL_DATA) qemu-img.1 "$(DESTDIR)$(mandir)/man1" + $(INSTALL_DATA) $(MANUAL_BUILDDIR)/tools/qemu-img.1 "$(DESTDIR)$(mandir)/man1" $(INSTALL_DIR) "$(DESTDIR)$(mandir)/man8" - $(INSTALL_DATA) qemu-nbd.8 "$(DESTDIR)$(mandir)/man8" + $(INSTALL_DATA) $(MANUAL_BUILDDIR)/tools/qemu-nbd.8 "$(DESTDIR)$(mandir)/man8" endif ifdef CONFIG_TRACE_SYSTEMTAP - $(INSTALL_DATA) scripts/qemu-trace-stap.1 "$(DESTDIR)$(mandir)/man1" + $(INSTALL_DATA) $(MANUAL_BUILDDIR)/tools/qemu-trace-stap.1 "$(DESTDIR)$(mandir)/man1" endif ifneq (,$(findstring qemu-ga,$(TOOLS))) $(INSTALL_DATA) $(MANUAL_BUILDDIR)/interop/qemu-ga.8 "$(DESTDIR)$(mandir)/man8" @@ -851,7 +909,10 @@ endif endif ifdef CONFIG_VIRTFS $(INSTALL_DIR) "$(DESTDIR)$(mandir)/man1" - $(INSTALL_DATA) fsdev/virtfs-proxy-helper.1 "$(DESTDIR)$(mandir)/man1" + $(INSTALL_DATA) $(MANUAL_BUILDDIR)/tools/virtfs-proxy-helper.1 "$(DESTDIR)$(mandir)/man1" +endif +ifeq ($(CONFIG_LINUX)$(CONFIG_SECCOMP)$(CONFIG_LIBCAP_NG),yyy) + $(INSTALL_DATA) $(MANUAL_BUILDDIR)/tools/virtiofsd.1 "$(DESTDIR)$(mandir)/man1" endif install-datadir: @@ -1006,16 +1067,32 @@ docs/version.texi: $(SRC_PATH)/VERSION config-host.mak # and handles "don't rebuild things unless necessary" itself. # The '.doctrees' files are cached information to speed this up. .PHONY: sphinxdocs -sphinxdocs: $(MANUAL_BUILDDIR)/devel/index.html $(MANUAL_BUILDDIR)/interop/index.html $(MANUAL_BUILDDIR)/specs/index.html +sphinxdocs: $(MANUAL_BUILDDIR)/devel/index.html \ + $(MANUAL_BUILDDIR)/interop/index.html \ + $(MANUAL_BUILDDIR)/specs/index.html \ + $(MANUAL_BUILDDIR)/system/index.html \ + $(MANUAL_BUILDDIR)/tools/index.html \ + $(MANUAL_BUILDDIR)/user/index.html # Canned command to build a single manual # Arguments: $1 = manual name, $2 = Sphinx builder ('html' or 'man') # Note the use of different doctree for each (manual, builder) tuple; # this works around Sphinx not handling parallel invocation on # a single doctree: https://github.com/sphinx-doc/sphinx/issues/2946 -build-manual = $(call quiet-command,CONFDIR="$(qemu_confdir)" sphinx-build $(if $(V),,-q) -W -n -b $2 -D version=$(VERSION) -D release="$(FULL_VERSION)" -d .doctrees/$1-$2 $(SRC_PATH)/docs/$1 $(MANUAL_BUILDDIR)/$1 ,"SPHINX","$(MANUAL_BUILDDIR)/$1") +build-manual = $(call quiet-command,CONFDIR="$(qemu_confdir)" $(SPHINX_BUILD) $(if $(V),,-q) $(SPHINX_WERROR) -b $2 -D version=$(VERSION) -D release="$(FULL_VERSION)" -d .doctrees/$1-$2 $(SRC_PATH)/docs/$1 $(MANUAL_BUILDDIR)/$1 ,"SPHINX","$(MANUAL_BUILDDIR)/$1") # We assume all RST files in the manual's directory are used in it -manual-deps = $(wildcard $(SRC_PATH)/docs/$1/*.rst) $(SRC_PATH)/docs/$1/conf.py $(SRC_PATH)/docs/conf.py +manual-deps = $(wildcard $(SRC_PATH)/docs/$1/*.rst $(SRC_PATH)/docs/$1/*/*.rst) \ + $(SRC_PATH)/docs/defs.rst.inc \ + $(SRC_PATH)/docs/$1/conf.py $(SRC_PATH)/docs/conf.py \ + $(SRC_PATH)/docs/sphinx/*.py +# Macro to write out the rule and dependencies for building manpages +# Usage: $(call define-manpage-rule,manualname,manpage1 manpage2...[,extradeps]) +# 'extradeps' is optional, and specifies extra files (eg .hx files) that +# the manual page depends on. +define define-manpage-rule +$(call atomic,$(foreach manpage,$2,$(MANUAL_BUILDDIR)/$1/$(manpage)),$(call manual-deps,$1) $3) + $(call build-manual,$1,man) +endef $(MANUAL_BUILDDIR)/devel/index.html: $(call manual-deps,devel) $(call build-manual,devel,html) @@ -1026,20 +1103,28 @@ $(MANUAL_BUILDDIR)/interop/index.html: $(call manual-deps,interop) $(MANUAL_BUILDDIR)/specs/index.html: $(call manual-deps,specs) $(call build-manual,specs,html) -$(MANUAL_BUILDDIR)/interop/qemu-ga.8: $(call manual-deps,interop) - $(call build-manual,interop,man) +$(MANUAL_BUILDDIR)/system/index.html: $(call manual-deps,system) $(SRC_PATH)/hmp-commands.hx $(SRC_PATH)/hmp-commands-info.hx $(SRC_PATH)/qemu-options.hx + $(call build-manual,system,html) -qemu-options.texi: $(SRC_PATH)/qemu-options.hx $(SRC_PATH)/scripts/hxtool - $(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@,"GEN","$@") +$(MANUAL_BUILDDIR)/tools/index.html: $(call manual-deps,tools) $(SRC_PATH)/qemu-img-cmds.hx $(SRC_PATH)/docs/qemu-option-trace.rst.inc + $(call build-manual,tools,html) -qemu-monitor.texi: $(SRC_PATH)/hmp-commands.hx $(SRC_PATH)/scripts/hxtool - $(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@,"GEN","$@") +$(MANUAL_BUILDDIR)/user/index.html: $(call manual-deps,user) + $(call build-manual,user,html) -qemu-monitor-info.texi: $(SRC_PATH)/hmp-commands-info.hx $(SRC_PATH)/scripts/hxtool - $(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@,"GEN","$@") +$(call define-manpage-rule,interop,qemu-ga.8) -qemu-img-cmds.texi: $(SRC_PATH)/qemu-img-cmds.hx $(SRC_PATH)/scripts/hxtool - $(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@,"GEN","$@") +$(call define-manpage-rule,system,qemu.1 qemu-block-drivers.7 qemu-cpu-models.7) + +$(call define-manpage-rule,tools,\ + qemu-img.1 qemu-nbd.8 qemu-trace-stap.1\ + virtiofsd.1 virtfs-proxy-helper.1,\ + $(SRC_PATH)/qemu-img-cmds.hx $(SRC_PATH)/docs/qemu-option-trace.rst.inc) + +$(MANUAL_BUILDDIR)/index.html: $(SRC_PATH)/docs/index.html.in qemu-version.h + @mkdir -p "$(MANUAL_BUILDDIR)" + $(call quiet-command, sed "s|@@VERSION@@|${VERSION}|g" $< >$@, \ + "GEN","$@") docs/interop/qemu-qmp-qapi.texi: qapi/qapi-doc.texi @cp -p $< $@ @@ -1047,26 +1132,10 @@ docs/interop/qemu-qmp-qapi.texi: qapi/qapi-doc.texi docs/interop/qemu-ga-qapi.texi: qga/qapi-generated/qga-qapi-doc.texi @cp -p $< $@ -qemu.1: qemu-doc.texi qemu-options.texi qemu-monitor.texi qemu-monitor-info.texi -qemu.1: qemu-option-trace.texi -qemu-img.1: qemu-img.texi qemu-option-trace.texi qemu-img-cmds.texi -fsdev/virtfs-proxy-helper.1: fsdev/virtfs-proxy-helper.texi -qemu-nbd.8: qemu-nbd.texi qemu-option-trace.texi -docs/qemu-block-drivers.7: docs/qemu-block-drivers.texi -docs/qemu-cpu-models.7: docs/qemu-cpu-models.texi -scripts/qemu-trace-stap.1: scripts/qemu-trace-stap.texi - -html: qemu-doc.html docs/interop/qemu-qmp-ref.html docs/interop/qemu-ga-ref.html sphinxdocs -info: qemu-doc.info docs/interop/qemu-qmp-ref.info docs/interop/qemu-ga-ref.info -pdf: qemu-doc.pdf docs/interop/qemu-qmp-ref.pdf docs/interop/qemu-ga-ref.pdf -txt: qemu-doc.txt docs/interop/qemu-qmp-ref.txt docs/interop/qemu-ga-ref.txt - -qemu-doc.html qemu-doc.info qemu-doc.pdf qemu-doc.txt: \ - qemu-img.texi qemu-nbd.texi qemu-options.texi \ - qemu-tech.texi qemu-option-trace.texi \ - qemu-deprecated.texi qemu-monitor.texi qemu-img-cmds.texi \ - qemu-monitor-info.texi docs/qemu-block-drivers.texi \ - docs/qemu-cpu-models.texi docs/security.texi +html: docs/interop/qemu-qmp-ref.html docs/interop/qemu-ga-ref.html sphinxdocs +info: docs/interop/qemu-qmp-ref.info docs/interop/qemu-ga-ref.info +pdf: docs/interop/qemu-qmp-ref.pdf docs/interop/qemu-ga-ref.pdf +txt: docs/interop/qemu-qmp-ref.txt docs/interop/qemu-ga-ref.txt docs/interop/qemu-ga-ref.dvi docs/interop/qemu-ga-ref.html \ docs/interop/qemu-ga-ref.info docs/interop/qemu-ga-ref.pdf \ @@ -1169,50 +1238,57 @@ endif include $(SRC_PATH)/tests/docker/Makefile.include include $(SRC_PATH)/tests/vm/Makefile.include +print-help-run = printf " %-30s - %s\\n" "$1" "$2" +print-help = $(quiet-@)$(call print-help-run,$1,$2) + .PHONY: help help: @echo 'Generic targets:' - @echo ' all - Build all' + $(call print-help,all,Build all) ifdef CONFIG_MODULES - @echo ' modules - Build all modules' + $(call print-help,modules,Build all modules) endif - @echo ' dir/file.o - Build specified target only' - @echo ' install - Install QEMU, documentation and tools' - @echo ' ctags/TAGS - Generate tags file for editors' - @echo ' cscope - Generate cscope index' + $(call print-help,dir/file.o,Build specified target only) + $(call print-help,install,Install QEMU, documentation and tools) + $(call print-help,ctags/TAGS,Generate tags file for editors) + $(call print-help,cscope,Generate cscope index) @echo '' @$(if $(TARGET_DIRS), \ echo 'Architecture specific targets:'; \ $(foreach t, $(TARGET_DIRS), \ - printf " %-30s - Build for %s\\n" $(t)/all $(t);) \ + $(call print-help-run,$(t)/all,Build for $(t));) \ + echo '') + @$(if $(TOOLS), \ + echo 'Tools targets:'; \ + $(foreach t, $(TOOLS), \ + $(call print-help-run,$(t),Build $(shell basename $(t)) tool);) \ echo '') @echo 'Cleaning targets:' - @echo ' clean - Remove most generated files but keep the config' + $(call print-help,clean,Remove most generated files but keep the config) ifdef CONFIG_GCOV - @echo ' clean-coverage - Remove coverage files' + $(call print-help,clean-coverage,Remove coverage files) endif - @echo ' distclean - Remove all generated files' - @echo ' dist - Build a distributable tarball' + $(call print-help,distclean,Remove all generated files) + $(call print-help,dist,Build a distributable tarball) @echo '' @echo 'Test targets:' - @echo ' check - Run all tests (check-help for details)' - @echo ' docker - Help about targets running tests inside containers' - @echo ' vm-help - Help about targets running tests inside VM' + $(call print-help,check,Run all tests (check-help for details)) + $(call print-help,docker,Help about targets running tests inside containers) + $(call print-help,vm-help,Help about targets running tests inside VM) @echo '' @echo 'Documentation targets:' - @echo ' html info pdf txt' - @echo ' - Build documentation in specified format' + $(call print-help,html info pdf txt,Build documentation in specified format) ifdef CONFIG_GCOV - @echo ' coverage-report - Create code coverage report' + $(call print-help,coverage-report,Create code coverage report) endif @echo '' ifdef CONFIG_WIN32 @echo 'Windows targets:' - @echo ' installer - Build NSIS-based installer for QEMU' + $(call print-help,installer,Build NSIS-based installer for QEMU) ifdef QEMU_GA_MSI_ENABLED - @echo ' msi - Build MSI-based installer for qemu-ga' + $(call print-help,msi,Build MSI-based installer for qemu-ga) endif @echo '' endif - @echo ' $(MAKE) [targets] (quiet build, default)' - @echo ' $(MAKE) V=1 [targets] (verbose build)' + $(call print-help,$(MAKE) [targets],(quiet build, default)) + $(call print-help,$(MAKE) V=1 [targets],(verbose build)) diff --git a/Makefile.objs b/Makefile.objs index 11ba1a36bd..a7c967633a 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -1,18 +1,18 @@ ####################################################################### # Common libraries for tools and emulators -stub-obj-y = stubs/ util/ crypto/ -util-obj-y = util/ qobject/ qapi/ +stub-obj-y = stubs/ +util-obj-y = crypto/ util/ qobject/ qapi/ +qom-obj-y = qom/ + +####################################################################### +# code used by both qemu system emulation and qemu-img + +ifeq ($(call lor,$(CONFIG_SOFTMMU),$(CONFIG_TOOLS)),y) chardev-obj-y = chardev/ -####################################################################### -# authz-obj-y is code used by both qemu system emulation and qemu-img - authz-obj-y = authz/ -####################################################################### -# block-obj-y is code used by both qemu system emulation and qemu-img - block-obj-y = nbd/ block-obj-y += block.o blockjob.o job.o block-obj-y += block/ scsi/ @@ -21,22 +21,21 @@ block-obj-$(CONFIG_REPLICATION) += replication.o block-obj-m = block/ -####################################################################### -# crypto-obj-y is code used by both qemu system emulation and qemu-img - crypto-obj-y = crypto/ -crypto-user-obj-y = crypto/ - -####################################################################### -# qom-obj-y is code used by both qemu system emulation and qemu-img - -qom-obj-y = qom/ - -####################################################################### -# io-obj-y is code used by both qemu system emulation and qemu-img io-obj-y = io/ +endif # CONFIG_SOFTMMU or CONFIG_TOOLS + +####################################################################### +# storage-daemon-obj-y is code used by qemu-storage-daemon (these objects are +# used for system emulation, too, but specified separately there) + +storage-daemon-obj-y = block/ monitor/ qapi/ qom/ storage-daemon/ +storage-daemon-obj-y += blockdev.o blockdev-nbd.o iothread.o job-qmp.o +storage-daemon-obj-$(CONFIG_WIN32) += os-win32.o +storage-daemon-obj-$(CONFIG_POSIX) += os-posix.o + ###################################################################### # Target independent part of system emulation. The long term path is to # suppress *all* target specific code in case of system emulation, i.e. a @@ -49,12 +48,13 @@ common-obj-y += dump/ common-obj-y += job-qmp.o common-obj-y += monitor/ common-obj-y += net/ -common-obj-y += qdev-monitor.o device-hotplug.o +common-obj-y += qdev-monitor.o common-obj-$(CONFIG_WIN32) += os-win32.o common-obj-$(CONFIG_POSIX) += os-posix.o common-obj-$(CONFIG_LINUX) += fsdev/ +common-obj-y += accel/ common-obj-y += migration/ common-obj-y += audio/ @@ -65,12 +65,8 @@ common-obj-y += replay/ common-obj-y += ui/ common-obj-m += ui/ -common-obj-y += bt-host.o bt-vhci.o -bt-host.o-cflags := $(BLUEZ_CFLAGS) common-obj-y += dma-helpers.o -common-obj-y += vl.o -vl.o-cflags := $(GPROF_CFLAGS) $(SDL_CFLAGS) common-obj-$(CONFIG_TPM) += tpm.o common-obj-y += backends/ @@ -82,11 +78,9 @@ qemu-seccomp.o-libs := $(SECCOMP_LIBS) common-obj-$(CONFIG_FDT) += device_tree.o -###################################################################### -# qapi - common-obj-y += qapi/ -endif + +endif # CONFIG_SOFTMMU ####################################################################### # Target-independent parts used in system and user emulation @@ -125,11 +119,13 @@ vhost-user-blk-obj-y = contrib/vhost-user-blk/ rdmacm-mux-obj-y = contrib/rdmacm-mux/ vhost-user-input-obj-y = contrib/vhost-user-input/ vhost-user-gpu-obj-y = contrib/vhost-user-gpu/ +virtiofsd-obj-y = tools/virtiofsd/ ###################################################################### trace-events-subdirs = trace-events-subdirs += accel/kvm trace-events-subdirs += accel/tcg +trace-events-subdirs += backends trace-events-subdirs += crypto trace-events-subdirs += monitor ifeq ($(CONFIG_USER_ONLY),y) @@ -143,8 +139,8 @@ trace-events-subdirs += nbd trace-events-subdirs += scsi endif ifeq ($(CONFIG_SOFTMMU),y) -trace-events-subdirs += chardev trace-events-subdirs += audio +trace-events-subdirs += chardev trace-events-subdirs += hw/9pfs trace-events-subdirs += hw/acpi trace-events-subdirs += hw/alpha @@ -179,6 +175,7 @@ trace-events-subdirs += hw/scsi trace-events-subdirs += hw/sd trace-events-subdirs += hw/sparc trace-events-subdirs += hw/sparc64 +trace-events-subdirs += hw/ssi trace-events-subdirs += hw/timer trace-events-subdirs += hw/tpm trace-events-subdirs += hw/usb @@ -192,6 +189,7 @@ trace-events-subdirs += migration trace-events-subdirs += net trace-events-subdirs += ui endif +trace-events-subdirs += hw/core trace-events-subdirs += hw/display trace-events-subdirs += qapi trace-events-subdirs += qom @@ -204,7 +202,6 @@ trace-events-subdirs += target/riscv trace-events-subdirs += target/s390x trace-events-subdirs += target/sparc trace-events-subdirs += util -trace-events-subdirs += hw/core trace-events-files = $(SRC_PATH)/trace-events $(trace-events-subdirs:%=$(SRC_PATH)/%/trace-events) diff --git a/Makefile.target b/Makefile.target index 24d79d26eb..8ed1eba95b 100644 --- a/Makefile.target +++ b/Makefile.target @@ -12,7 +12,7 @@ endif $(call set-vpath, $(SRC_PATH):$(BUILD_DIR)) ifdef CONFIG_LINUX -QEMU_CFLAGS += -I../linux-headers +QEMU_CFLAGS += -isystem ../linux-headers endif QEMU_CFLAGS += -iquote .. -iquote $(SRC_PATH)/target/$(TARGET_BASE_ARCH) -DNEED_CPU_H @@ -128,7 +128,8 @@ ifdef CONFIG_LINUX_USER QEMU_CFLAGS+=-I$(SRC_PATH)/linux-user/$(TARGET_ABI_DIR) \ -I$(SRC_PATH)/linux-user/host/$(ARCH) \ - -I$(SRC_PATH)/linux-user + -I$(SRC_PATH)/linux-user \ + -Ilinux-user/$(TARGET_ABI_DIR) obj-y += linux-user/ obj-y += gdbstub.o thunk.o @@ -160,6 +161,7 @@ obj-y += qapi/ obj-y += memory.o obj-y += memory_mapping.o obj-y += migration/ram.o +obj-y += softmmu/ LIBS := $(libs_softmmu) $(LIBS) # Hardware support @@ -184,7 +186,6 @@ dummy := $(call unnest-vars,.., \ block-obj-m \ chardev-obj-y \ crypto-obj-y \ - crypto-user-obj-y \ qom-obj-y \ io-obj-y \ common-obj-y \ @@ -193,7 +194,6 @@ all-obj-y += $(common-obj-y) all-obj-y += $(qom-obj-y) all-obj-$(CONFIG_SOFTMMU) += $(authz-obj-y) all-obj-$(CONFIG_SOFTMMU) += $(block-obj-y) $(chardev-obj-y) -all-obj-$(CONFIG_USER_ONLY) += $(crypto-user-obj-y) all-obj-$(CONFIG_SOFTMMU) += $(crypto-obj-y) all-obj-$(CONFIG_SOFTMMU) += $(io-obj-y) @@ -204,7 +204,7 @@ endif COMMON_LDADDS = ../libqemuutil.a # build either PROG or PROGW -$(QEMU_PROG_BUILD): $(all-obj-y) $(COMMON_LDADDS) +$(QEMU_PROG_BUILD): $(all-obj-y) $(COMMON_LDADDS) $(softmmu-main-y) $(call LINK, $(filter-out %.mak, $^)) ifdef CONFIG_DARWIN $(call quiet-command,Rez -append $(SRC_PATH)/pc-bios/qemu.rsrc -o $@,"REZ","$(TARGET_DIR)$@") @@ -229,6 +229,22 @@ ifdef CONFIG_TRACE_SYSTEMTAP rm -f *.stp endif +ifdef CONFIG_FUZZ +include $(SRC_PATH)/tests/qtest/fuzz/Makefile.include +include $(SRC_PATH)/tests/qtest/Makefile.include + +fuzz: fuzz-vars +fuzz-vars: QEMU_CFLAGS := $(FUZZ_CFLAGS) $(QEMU_CFLAGS) +fuzz-vars: QEMU_LDFLAGS := $(FUZZ_LDFLAGS) $(QEMU_LDFLAGS) +fuzz-vars: $(QEMU_PROG_FUZZ) +dummy := $(call unnest-vars,, fuzz-obj-y) + + +$(QEMU_PROG_FUZZ): config-devices.mak $(all-obj-y) $(COMMON_LDADDS) $(fuzz-obj-y) + $(call LINK, $(filter-out %.mak, $^)) + +endif + install: all ifneq ($(PROGS),) $(call install-prog,$(PROGS),$(DESTDIR)$(bindir)) diff --git a/VERSION b/VERSION index 6aba2b245a..0062ac9718 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -4.2.0 +5.0.0 diff --git a/accel/Makefile.objs b/accel/Makefile.objs index 8b498d39d8..17e5ac6061 100644 --- a/accel/Makefile.objs +++ b/accel/Makefile.objs @@ -1,4 +1,4 @@ -obj-$(CONFIG_SOFTMMU) += accel.o +common-obj-$(CONFIG_SOFTMMU) += accel.o obj-$(call land,$(CONFIG_SOFTMMU),$(CONFIG_POSIX)) += qtest.o obj-$(CONFIG_KVM) += kvm/ obj-$(CONFIG_TCG) += tcg/ diff --git a/accel/accel.c b/accel/accel.c index 5fa31717b4..cb555e3b06 100644 --- a/accel/accel.c +++ b/accel/accel.c @@ -28,13 +28,7 @@ #include "hw/boards.h" #include "sysemu/arch_init.h" #include "sysemu/sysemu.h" -#include "sysemu/kvm.h" -#include "sysemu/qtest.h" -#include "hw/xen/xen.h" #include "qom/object.h" -#include "qemu/error-report.h" -#include "qemu/option.h" -#include "qapi/error.h" static const TypeInfo accel_type = { .name = TYPE_ACCEL, @@ -44,7 +38,7 @@ static const TypeInfo accel_type = { }; /* Lookup AccelClass from opt_name. Returns NULL if not found */ -static AccelClass *accel_find(const char *opt_name) +AccelClass *accel_find(const char *opt_name) { char *class_name = g_strdup_printf(ACCEL_CLASS_NAME("%s"), opt_name); AccelClass *ac = ACCEL_CLASS(object_class_by_name(class_name)); @@ -52,11 +46,9 @@ static AccelClass *accel_find(const char *opt_name) return ac; } -static int accel_init_machine(AccelClass *acc, MachineState *ms) +int accel_init_machine(AccelState *accel, MachineState *ms) { - ObjectClass *oc = OBJECT_CLASS(acc); - const char *cname = object_class_get_name(oc); - AccelState *accel = ACCEL(object_new(cname)); + AccelClass *acc = ACCEL_GET_CLASS(accel); int ret; ms->accelerator = accel; *(acc->allowed) = true; @@ -71,63 +63,9 @@ static int accel_init_machine(AccelClass *acc, MachineState *ms) return ret; } -void configure_accelerator(MachineState *ms, const char *progname) +AccelState *current_accel(void) { - const char *accel; - char **accel_list, **tmp; - int ret; - bool accel_initialised = false; - bool init_failed = false; - AccelClass *acc = NULL; - - accel = qemu_opt_get(qemu_get_machine_opts(), "accel"); - if (accel == NULL) { - /* Select the default accelerator */ - int pnlen = strlen(progname); - if (pnlen >= 3 && g_str_equal(&progname[pnlen - 3], "kvm")) { - /* If the program name ends with "kvm", we prefer KVM */ - accel = "kvm:tcg"; - } else { -#if defined(CONFIG_TCG) - accel = "tcg"; -#elif defined(CONFIG_KVM) - accel = "kvm"; -#else - error_report("No accelerator selected and" - " no default accelerator available"); - exit(1); -#endif - } - } - - accel_list = g_strsplit(accel, ":", 0); - - for (tmp = accel_list; !accel_initialised && tmp && *tmp; tmp++) { - acc = accel_find(*tmp); - if (!acc) { - continue; - } - ret = accel_init_machine(acc, ms); - if (ret < 0) { - init_failed = true; - error_report("failed to initialize %s: %s", - acc->name, strerror(-ret)); - } else { - accel_initialised = true; - } - } - g_strfreev(accel_list); - - if (!accel_initialised) { - if (!init_failed) { - error_report("-machine accel=%s: No accelerator found", accel); - } - exit(1); - } - - if (init_failed) { - error_report("Back to %s accelerator", acc->name); - } + return current_machine->accelerator; } void accel_setup_post(MachineState *ms) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index ca00daa2f5..439a4efe52 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -41,6 +41,9 @@ #include "hw/irq.h" #include "sysemu/sev.h" #include "sysemu/balloon.h" +#include "qapi/visitor.h" +#include "qapi/qapi-types-common.h" +#include "qapi/qapi-visit-common.h" #include "hw/boards.h" @@ -92,6 +95,10 @@ struct KVMState int max_nested_state_len; int many_ioeventfds; int intx_set_mask; + int kvm_shadow_mem; + bool kernel_irqchip_allowed; + bool kernel_irqchip_required; + OnOffAuto kernel_irqchip_split; bool sync_mmu; bool manual_dirty_log_protect; /* The man page (and posix) say ioctl numbers are signed int, but @@ -157,7 +164,7 @@ static NotifierList kvm_irqchip_change_notifiers = int kvm_get_max_memslots(void) { - KVMState *s = KVM_STATE(current_machine->accelerator); + KVMState *s = KVM_STATE(current_accel()); return s->nr_slots; } @@ -301,13 +308,23 @@ static int kvm_set_user_memory_region(KVMMemoryListener *kml, KVMSlot *slot, boo /* Set the slot size to 0 before setting the slot to the desired * value. This is needed based on KVM commit 75d61fbc. */ mem.memory_size = 0; - kvm_vm_ioctl(s, KVM_SET_USER_MEMORY_REGION, &mem); + ret = kvm_vm_ioctl(s, KVM_SET_USER_MEMORY_REGION, &mem); + if (ret < 0) { + goto err; + } } mem.memory_size = slot->memory_size; ret = kvm_vm_ioctl(s, KVM_SET_USER_MEMORY_REGION, &mem); slot->old_flags = mem.flags; +err: trace_kvm_set_user_memory(mem.slot, mem.flags, mem.guest_phys_addr, mem.memory_size, mem.userspace_addr, ret); + if (ret < 0) { + error_report("%s: KVM_SET_USER_MEMORY_REGION failed, slot=%d," + " start=0x%" PRIx64 ", size=0x%" PRIx64 ": %s", + __func__, mem.slot, slot->start_addr, + (uint64_t)mem.memory_size, strerror(errno)); + } return ret; } @@ -518,6 +535,27 @@ static int kvm_get_dirty_pages_log_range(MemoryRegionSection *section, #define ALIGN(x, y) (((x)+(y)-1) & ~((y)-1)) +/* Allocate the dirty bitmap for a slot */ +static void kvm_memslot_init_dirty_bitmap(KVMSlot *mem) +{ + /* + * XXX bad kernel interface alert + * For dirty bitmap, kernel allocates array of size aligned to + * bits-per-long. But for case when the kernel is 64bits and + * the userspace is 32bits, userspace can't align to the same + * bits-per-long, since sizeof(long) is different between kernel + * and user space. This way, userspace will provide buffer which + * may be 4 bytes less than the kernel will use, resulting in + * userspace memory corruption (which is not detectable by valgrind + * too, in most cases). + * So for now, let's align to 64 instead of HOST_LONG_BITS here, in + * a hope that sizeof(long) won't become >8 any time soon. + */ + hwaddr bitmap_size = ALIGN(((mem->memory_size) >> TARGET_PAGE_BITS), + /*HOST_LONG_BITS*/ 64) / 8; + mem->dirty_bmap = g_malloc0(bitmap_size); +} + /** * kvm_physical_sync_dirty_bitmap - Sync dirty bitmap from kernel space * @@ -550,23 +588,9 @@ static int kvm_physical_sync_dirty_bitmap(KVMMemoryListener *kml, goto out; } - /* XXX bad kernel interface alert - * For dirty bitmap, kernel allocates array of size aligned to - * bits-per-long. But for case when the kernel is 64bits and - * the userspace is 32bits, userspace can't align to the same - * bits-per-long, since sizeof(long) is different between kernel - * and user space. This way, userspace will provide buffer which - * may be 4 bytes less than the kernel will use, resulting in - * userspace memory corruption (which is not detectable by valgrind - * too, in most cases). - * So for now, let's align to 64 instead of HOST_LONG_BITS here, in - * a hope that sizeof(long) won't become >8 any time soon. - */ if (!mem->dirty_bmap) { - hwaddr bitmap_size = ALIGN(((mem->memory_size) >> TARGET_PAGE_BITS), - /*HOST_LONG_BITS*/ 64) / 8; /* Allocate on the first log_sync, once and for all */ - mem->dirty_bmap = g_malloc0(bitmap_size); + kvm_memslot_init_dirty_bitmap(mem); } d.dirty_bitmap = mem->dirty_bmap; @@ -1067,6 +1091,13 @@ static void kvm_set_phys_mem(KVMMemoryListener *kml, mem->ram = ram; mem->flags = kvm_mem_flags(mr); + if (mem->flags & KVM_MEM_LOG_DIRTY_PAGES) { + /* + * Reallocate the bmap; it means it doesn't disappear in + * middle of a migrate. + */ + kvm_memslot_init_dirty_bitmap(mem); + } err = kvm_set_user_memory_region(kml, mem, true); if (err) { fprintf(stderr, "%s: error registering slot: %s\n", __func__, @@ -1758,10 +1789,11 @@ void kvm_irqchip_set_qemuirq_gsi(KVMState *s, qemu_irq irq, int gsi) g_hash_table_insert(s->gsimap, irq, GINT_TO_POINTER(gsi)); } -static void kvm_irqchip_create(MachineState *machine, KVMState *s) +static void kvm_irqchip_create(KVMState *s) { int ret; + assert(s->kernel_irqchip_split != ON_OFF_AUTO_AUTO); if (kvm_check_extension(s, KVM_CAP_IRQCHIP)) { ; } else if (kvm_check_extension(s, KVM_CAP_S390_IRQCHIP)) { @@ -1776,9 +1808,9 @@ static void kvm_irqchip_create(MachineState *machine, KVMState *s) /* First probe and see if there's a arch-specific hook to create the * in-kernel irqchip for us */ - ret = kvm_arch_irqchip_create(machine, s); + ret = kvm_arch_irqchip_create(s); if (ret == 0) { - if (machine_kernel_irqchip_split(machine)) { + if (s->kernel_irqchip_split == ON_OFF_AUTO_ON) { perror("Split IRQ chip mode not supported."); exit(1); } else { @@ -1826,7 +1858,7 @@ static int kvm_max_vcpu_id(KVMState *s) bool kvm_vcpu_id_is_valid(int vcpu_id) { - KVMState *s = KVM_STATE(current_machine->accelerator); + KVMState *s = KVM_STATE(current_accel()); return vcpu_id >= 0 && vcpu_id < kvm_max_vcpu_id(s); } @@ -2049,8 +2081,12 @@ static int kvm_init(MachineState *ms) goto err; } - if (machine_kernel_irqchip_allowed(ms)) { - kvm_irqchip_create(ms, s); + if (s->kernel_irqchip_split == ON_OFF_AUTO_AUTO) { + s->kernel_irqchip_split = mc->default_kernel_irqchip_split ? ON_OFF_AUTO_ON : ON_OFF_AUTO_OFF; + } + + if (s->kernel_irqchip_allowed) { + kvm_irqchip_create(s); } if (kvm_eventfds_allowed) { @@ -2152,9 +2188,9 @@ void kvm_flush_coalesced_mmio_buffer(void) ent = &ring->coalesced_mmio[ring->first]; if (ent->pio == 1) { - address_space_rw(&address_space_io, ent->phys_addr, - MEMTXATTRS_UNSPECIFIED, ent->data, - ent->len, true); + address_space_write(&address_space_io, ent->phys_addr, + MEMTXATTRS_UNSPECIFIED, ent->data, + ent->len); } else { cpu_physical_memory_write(ent->phys_addr, ent->data, ent->len); } @@ -2940,6 +2976,95 @@ static bool kvm_accel_has_memory(MachineState *ms, AddressSpace *as, return false; } +static void kvm_get_kvm_shadow_mem(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + KVMState *s = KVM_STATE(obj); + int64_t value = s->kvm_shadow_mem; + + visit_type_int(v, name, &value, errp); +} + +static void kvm_set_kvm_shadow_mem(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + KVMState *s = KVM_STATE(obj); + Error *error = NULL; + int64_t value; + + visit_type_int(v, name, &value, &error); + if (error) { + error_propagate(errp, error); + return; + } + + s->kvm_shadow_mem = value; +} + +static void kvm_set_kernel_irqchip(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + Error *err = NULL; + KVMState *s = KVM_STATE(obj); + OnOffSplit mode; + + visit_type_OnOffSplit(v, name, &mode, &err); + if (err) { + error_propagate(errp, err); + return; + } else { + switch (mode) { + case ON_OFF_SPLIT_ON: + s->kernel_irqchip_allowed = true; + s->kernel_irqchip_required = true; + s->kernel_irqchip_split = ON_OFF_AUTO_OFF; + break; + case ON_OFF_SPLIT_OFF: + s->kernel_irqchip_allowed = false; + s->kernel_irqchip_required = false; + s->kernel_irqchip_split = ON_OFF_AUTO_OFF; + break; + case ON_OFF_SPLIT_SPLIT: + s->kernel_irqchip_allowed = true; + s->kernel_irqchip_required = true; + s->kernel_irqchip_split = ON_OFF_AUTO_ON; + break; + default: + /* The value was checked in visit_type_OnOffSplit() above. If + * we get here, then something is wrong in QEMU. + */ + abort(); + } + } +} + +bool kvm_kernel_irqchip_allowed(void) +{ + return kvm_state->kernel_irqchip_allowed; +} + +bool kvm_kernel_irqchip_required(void) +{ + return kvm_state->kernel_irqchip_required; +} + +bool kvm_kernel_irqchip_split(void) +{ + return kvm_state->kernel_irqchip_split == ON_OFF_AUTO_ON; +} + +static void kvm_accel_instance_init(Object *obj) +{ + KVMState *s = KVM_STATE(obj); + + s->kvm_shadow_mem = -1; + s->kernel_irqchip_allowed = true; + s->kernel_irqchip_split = ON_OFF_AUTO_AUTO; +} + static void kvm_accel_class_init(ObjectClass *oc, void *data) { AccelClass *ac = ACCEL_CLASS(oc); @@ -2947,11 +3072,24 @@ static void kvm_accel_class_init(ObjectClass *oc, void *data) ac->init_machine = kvm_init; ac->has_memory = kvm_accel_has_memory; ac->allowed = &kvm_allowed; + + object_class_property_add(oc, "kernel-irqchip", "on|off|split", + NULL, kvm_set_kernel_irqchip, + NULL, NULL, &error_abort); + object_class_property_set_description(oc, "kernel-irqchip", + "Configure KVM in-kernel irqchip", &error_abort); + + object_class_property_add(oc, "kvm-shadow-mem", "int", + kvm_get_kvm_shadow_mem, kvm_set_kvm_shadow_mem, + NULL, NULL, &error_abort); + object_class_property_set_description(oc, "kvm-shadow-mem", + "KVM shadow MMU size", &error_abort); } static const TypeInfo kvm_accel_type = { .name = TYPE_KVM_ACCEL, .parent = TYPE_ACCEL, + .instance_init = kvm_accel_instance_init, .class_init = kvm_accel_class_init, .instance_size = sizeof(KVMState), }; diff --git a/accel/tcg/atomic_template.h b/accel/tcg/atomic_template.h index 837676231f..26969487d6 100644 --- a/accel/tcg/atomic_template.h +++ b/accel/tcg/atomic_template.h @@ -64,13 +64,10 @@ the ATOMIC_NAME macro, and redefined below. */ #if DATA_SIZE == 1 # define END -# define MEND _be /* either le or be would be fine */ #elif defined(HOST_WORDS_BIGENDIAN) # define END _be -# define MEND _be #else # define END _le -# define MEND _le #endif ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr, @@ -79,8 +76,8 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr, ATOMIC_MMU_DECLS; DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; DATA_TYPE ret; - uint16_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, false, - ATOMIC_MMU_IDX); + uint16_t info = trace_mem_build_info(SHIFT, false, 0, false, + ATOMIC_MMU_IDX); atomic_trace_rmw_pre(env, addr, info); #if DATA_SIZE == 16 @@ -99,8 +96,8 @@ ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr EXTRA_ARGS) { ATOMIC_MMU_DECLS; DATA_TYPE val, *haddr = ATOMIC_MMU_LOOKUP; - uint16_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, false, - ATOMIC_MMU_IDX); + uint16_t info = trace_mem_build_info(SHIFT, false, 0, false, + ATOMIC_MMU_IDX); atomic_trace_ld_pre(env, addr, info); val = atomic16_read(haddr); @@ -114,8 +111,8 @@ void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr, { ATOMIC_MMU_DECLS; DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; - uint16_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, true, - ATOMIC_MMU_IDX); + uint16_t info = trace_mem_build_info(SHIFT, false, 0, true, + ATOMIC_MMU_IDX); atomic_trace_st_pre(env, addr, info); atomic16_set(haddr, val); @@ -130,8 +127,8 @@ ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr, ATOMIC_MMU_DECLS; DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; DATA_TYPE ret; - uint16_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, false, - ATOMIC_MMU_IDX); + uint16_t info = trace_mem_build_info(SHIFT, false, 0, false, + ATOMIC_MMU_IDX); atomic_trace_rmw_pre(env, addr, info); ret = atomic_xchg__nocheck(haddr, val); @@ -147,10 +144,8 @@ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \ ATOMIC_MMU_DECLS; \ DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; \ DATA_TYPE ret; \ - uint16_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, \ - false, \ - ATOMIC_MMU_IDX); \ - \ + uint16_t info = trace_mem_build_info(SHIFT, false, 0, false, \ + ATOMIC_MMU_IDX); \ atomic_trace_rmw_pre(env, addr, info); \ ret = atomic_##X(haddr, val); \ ATOMIC_MMU_CLEANUP; \ @@ -183,10 +178,8 @@ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \ ATOMIC_MMU_DECLS; \ XDATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; \ XDATA_TYPE cmp, old, new, val = xval; \ - uint16_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, \ - false, \ - ATOMIC_MMU_IDX); \ - \ + uint16_t info = trace_mem_build_info(SHIFT, false, 0, false, \ + ATOMIC_MMU_IDX); \ atomic_trace_rmw_pre(env, addr, info); \ smp_mb(); \ cmp = atomic_read__nocheck(haddr); \ @@ -213,7 +206,6 @@ GEN_ATOMIC_HELPER_FN(umax_fetch, MAX, DATA_TYPE, new) #endif /* DATA SIZE >= 16 */ #undef END -#undef MEND #if DATA_SIZE > 1 @@ -221,10 +213,8 @@ GEN_ATOMIC_HELPER_FN(umax_fetch, MAX, DATA_TYPE, new) within the ATOMIC_NAME macro. */ #ifdef HOST_WORDS_BIGENDIAN # define END _le -# define MEND _le #else # define END _be -# define MEND _be #endif ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr, @@ -233,9 +223,8 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr, ATOMIC_MMU_DECLS; DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; DATA_TYPE ret; - uint16_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, - false, - ATOMIC_MMU_IDX); + uint16_t info = trace_mem_build_info(SHIFT, false, MO_BSWAP, false, + ATOMIC_MMU_IDX); atomic_trace_rmw_pre(env, addr, info); #if DATA_SIZE == 16 @@ -254,9 +243,8 @@ ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr EXTRA_ARGS) { ATOMIC_MMU_DECLS; DATA_TYPE val, *haddr = ATOMIC_MMU_LOOKUP; - uint16_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, - false, - ATOMIC_MMU_IDX); + uint16_t info = trace_mem_build_info(SHIFT, false, MO_BSWAP, false, + ATOMIC_MMU_IDX); atomic_trace_ld_pre(env, addr, info); val = atomic16_read(haddr); @@ -270,9 +258,8 @@ void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr, { ATOMIC_MMU_DECLS; DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; - uint16_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, - true, - ATOMIC_MMU_IDX); + uint16_t info = trace_mem_build_info(SHIFT, false, MO_BSWAP, true, + ATOMIC_MMU_IDX); val = BSWAP(val); atomic_trace_st_pre(env, addr, info); @@ -289,9 +276,8 @@ ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr, ATOMIC_MMU_DECLS; DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; ABI_TYPE ret; - uint16_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, - false, - ATOMIC_MMU_IDX); + uint16_t info = trace_mem_build_info(SHIFT, false, MO_BSWAP, false, + ATOMIC_MMU_IDX); atomic_trace_rmw_pre(env, addr, info); ret = atomic_xchg__nocheck(haddr, BSWAP(val)); @@ -307,10 +293,8 @@ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \ ATOMIC_MMU_DECLS; \ DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; \ DATA_TYPE ret; \ - uint16_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, \ - false, \ - ATOMIC_MMU_IDX); \ - \ + uint16_t info = trace_mem_build_info(SHIFT, false, MO_BSWAP, \ + false, ATOMIC_MMU_IDX); \ atomic_trace_rmw_pre(env, addr, info); \ ret = atomic_##X(haddr, BSWAP(val)); \ ATOMIC_MMU_CLEANUP; \ @@ -341,10 +325,8 @@ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \ ATOMIC_MMU_DECLS; \ XDATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; \ XDATA_TYPE ldo, ldn, old, new, val = xval; \ - uint16_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, \ - false, \ - ATOMIC_MMU_IDX); \ - \ + uint16_t info = trace_mem_build_info(SHIFT, false, MO_BSWAP, \ + false, ATOMIC_MMU_IDX); \ atomic_trace_rmw_pre(env, addr, info); \ smp_mb(); \ ldn = atomic_read__nocheck(haddr); \ @@ -378,7 +360,6 @@ GEN_ATOMIC_HELPER_FN(add_fetch, ADD, DATA_TYPE, new) #endif /* DATA_SIZE >= 16 */ #undef END -#undef MEND #endif /* DATA_SIZE > 1 */ #undef BSWAP diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index c01f59c743..d95c4848a4 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -23,7 +23,7 @@ #include "trace.h" #include "disas/disas.h" #include "exec/exec-all.h" -#include "tcg.h" +#include "tcg/tcg.h" #include "qemu/atomic.h" #include "sysemu/qtest.h" #include "qemu/timer.h" @@ -156,7 +156,7 @@ static inline tcg_target_ulong cpu_tb_exec(CPUState *cpu, TranslationBlock *itb) #if defined(DEBUG_DISAS) if (qemu_loglevel_mask(CPU_LOG_TB_CPU) && qemu_log_in_addr_range(itb->pc)) { - qemu_log_lock(); + FILE *logfile = qemu_log_lock(); int flags = 0; if (qemu_loglevel_mask(CPU_LOG_TB_FPU)) { flags |= CPU_DUMP_FPU; @@ -165,7 +165,7 @@ static inline tcg_target_ulong cpu_tb_exec(CPUState *cpu, TranslationBlock *itb) flags |= CPU_DUMP_CCOP; #endif log_cpu_state(cpu, flags); - qemu_log_unlock(); + qemu_log_unlock(logfile); } #endif /* DEBUG_DISAS */ @@ -240,6 +240,8 @@ void cpu_exec_step_atomic(CPUState *cpu) uint32_t cf_mask = cflags & CF_HASH_MASK; if (sigsetjmp(cpu->jmp_env, 0) == 0) { + start_exclusive(); + tb = tb_lookup__cpu_state(cpu, &pc, &cs_base, &flags, cf_mask); if (tb == NULL) { mmap_lock(); @@ -247,8 +249,6 @@ void cpu_exec_step_atomic(CPUState *cpu) mmap_unlock(); } - start_exclusive(); - /* Since we got here, we know that parallel_cpus must be true. */ parallel_cpus = false; cc->cpu_exec_enter(cpu); @@ -271,14 +271,15 @@ void cpu_exec_step_atomic(CPUState *cpu) qemu_plugin_disable_mem_helpers(cpu); } - if (cpu_in_exclusive_context(cpu)) { - /* We might longjump out of either the codegen or the - * execution, so must make sure we only end the exclusive - * region if we started it. - */ - parallel_cpus = true; - end_exclusive(); - } + + /* + * As we start the exclusive region before codegen we must still + * be in the region if we longjump out of either the codegen or + * the execution. + */ + g_assert(cpu_in_exclusive_context(cpu)); + parallel_cpus = true; + end_exclusive(); } struct tb_desc { diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index 98221948d6..e3b5750c3b 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -34,6 +34,8 @@ #include "qemu/atomic.h" #include "qemu/atomic128.h" #include "translate-all.h" +#include "trace-root.h" +#include "trace/mem.h" #ifdef CONFIG_PLUGIN #include "qemu/plugin-memory.h" #endif @@ -78,9 +80,14 @@ QEMU_BUILD_BUG_ON(sizeof(target_ulong) > sizeof(run_on_cpu_data)); QEMU_BUILD_BUG_ON(NB_MMU_MODES > 16); #define ALL_MMUIDX_BITS ((1 << NB_MMU_MODES) - 1) -static inline size_t sizeof_tlb(CPUArchState *env, uintptr_t mmu_idx) +static inline size_t tlb_n_entries(CPUTLBDescFast *fast) { - return env_tlb(env)->f[mmu_idx].mask + (1 << CPU_TLB_ENTRY_BITS); + return (fast->mask >> CPU_TLB_ENTRY_BITS) + 1; +} + +static inline size_t sizeof_tlb(CPUTLBDescFast *fast) +{ + return fast->mask + (1 << CPU_TLB_ENTRY_BITS); } static void tlb_window_reset(CPUTLBDesc *desc, int64_t ns, @@ -90,26 +97,10 @@ static void tlb_window_reset(CPUTLBDesc *desc, int64_t ns, desc->window_max_entries = max_entries; } -static void tlb_dyn_init(CPUArchState *env) -{ - int i; - - for (i = 0; i < NB_MMU_MODES; i++) { - CPUTLBDesc *desc = &env_tlb(env)->d[i]; - size_t n_entries = 1 << CPU_TLB_DYN_DEFAULT_BITS; - - tlb_window_reset(desc, get_clock_realtime(), 0); - desc->n_used_entries = 0; - env_tlb(env)->f[i].mask = (n_entries - 1) << CPU_TLB_ENTRY_BITS; - env_tlb(env)->f[i].table = g_new(CPUTLBEntry, n_entries); - env_tlb(env)->d[i].iotlb = g_new(CPUIOTLBEntry, n_entries); - } -} - /** * tlb_mmu_resize_locked() - perform TLB resize bookkeeping; resize if necessary - * @env: CPU that owns the TLB - * @mmu_idx: MMU index of the TLB + * @desc: The CPUTLBDesc portion of the TLB + * @fast: The CPUTLBDescFast portion of the same TLB * * Called with tlb_lock_held. * @@ -146,13 +137,12 @@ static void tlb_dyn_init(CPUArchState *env) * high), since otherwise we are likely to have a significant amount of * conflict misses. */ -static void tlb_mmu_resize_locked(CPUArchState *env, int mmu_idx) +static void tlb_mmu_resize_locked(CPUTLBDesc *desc, CPUTLBDescFast *fast, + int64_t now) { - CPUTLBDesc *desc = &env_tlb(env)->d[mmu_idx]; - size_t old_size = tlb_n_entries(env, mmu_idx); + size_t old_size = tlb_n_entries(fast); size_t rate; size_t new_size = old_size; - int64_t now = get_clock_realtime(); int64_t window_len_ms = 100; int64_t window_len_ns = window_len_ms * 1000 * 1000; bool window_expired = now > desc->window_begin_ns + window_len_ns; @@ -191,14 +181,15 @@ static void tlb_mmu_resize_locked(CPUArchState *env, int mmu_idx) return; } - g_free(env_tlb(env)->f[mmu_idx].table); - g_free(env_tlb(env)->d[mmu_idx].iotlb); + g_free(fast->table); + g_free(desc->iotlb); tlb_window_reset(desc, now, 0); /* desc->n_used_entries is cleared by the caller */ - env_tlb(env)->f[mmu_idx].mask = (new_size - 1) << CPU_TLB_ENTRY_BITS; - env_tlb(env)->f[mmu_idx].table = g_try_new(CPUTLBEntry, new_size); - env_tlb(env)->d[mmu_idx].iotlb = g_try_new(CPUIOTLBEntry, new_size); + fast->mask = (new_size - 1) << CPU_TLB_ENTRY_BITS; + fast->table = g_try_new(CPUTLBEntry, new_size); + desc->iotlb = g_try_new(CPUIOTLBEntry, new_size); + /* * If the allocations fail, try smaller sizes. We just freed some * memory, so going back to half of new_size has a good chance of working. @@ -206,27 +197,51 @@ static void tlb_mmu_resize_locked(CPUArchState *env, int mmu_idx) * allocations to fail though, so we progressively reduce the allocation * size, aborting if we cannot even allocate the smallest TLB we support. */ - while (env_tlb(env)->f[mmu_idx].table == NULL || - env_tlb(env)->d[mmu_idx].iotlb == NULL) { + while (fast->table == NULL || desc->iotlb == NULL) { if (new_size == (1 << CPU_TLB_DYN_MIN_BITS)) { error_report("%s: %s", __func__, strerror(errno)); abort(); } new_size = MAX(new_size >> 1, 1 << CPU_TLB_DYN_MIN_BITS); - env_tlb(env)->f[mmu_idx].mask = (new_size - 1) << CPU_TLB_ENTRY_BITS; + fast->mask = (new_size - 1) << CPU_TLB_ENTRY_BITS; - g_free(env_tlb(env)->f[mmu_idx].table); - g_free(env_tlb(env)->d[mmu_idx].iotlb); - env_tlb(env)->f[mmu_idx].table = g_try_new(CPUTLBEntry, new_size); - env_tlb(env)->d[mmu_idx].iotlb = g_try_new(CPUIOTLBEntry, new_size); + g_free(fast->table); + g_free(desc->iotlb); + fast->table = g_try_new(CPUTLBEntry, new_size); + desc->iotlb = g_try_new(CPUIOTLBEntry, new_size); } } -static inline void tlb_table_flush_by_mmuidx(CPUArchState *env, int mmu_idx) +static void tlb_mmu_flush_locked(CPUTLBDesc *desc, CPUTLBDescFast *fast) { - tlb_mmu_resize_locked(env, mmu_idx); - memset(env_tlb(env)->f[mmu_idx].table, -1, sizeof_tlb(env, mmu_idx)); - env_tlb(env)->d[mmu_idx].n_used_entries = 0; + desc->n_used_entries = 0; + desc->large_page_addr = -1; + desc->large_page_mask = -1; + desc->vindex = 0; + memset(fast->table, -1, sizeof_tlb(fast)); + memset(desc->vtable, -1, sizeof(desc->vtable)); +} + +static void tlb_flush_one_mmuidx_locked(CPUArchState *env, int mmu_idx, + int64_t now) +{ + CPUTLBDesc *desc = &env_tlb(env)->d[mmu_idx]; + CPUTLBDescFast *fast = &env_tlb(env)->f[mmu_idx]; + + tlb_mmu_resize_locked(desc, fast, now); + tlb_mmu_flush_locked(desc, fast); +} + +static void tlb_mmu_init(CPUTLBDesc *desc, CPUTLBDescFast *fast, int64_t now) +{ + size_t n_entries = 1 << CPU_TLB_DYN_DEFAULT_BITS; + + tlb_window_reset(desc, now, 0); + desc->n_used_entries = 0; + fast->mask = (n_entries - 1) << CPU_TLB_ENTRY_BITS; + fast->table = g_new(CPUTLBEntry, n_entries); + desc->iotlb = g_new(CPUIOTLBEntry, n_entries); + tlb_mmu_flush_locked(desc, fast); } static inline void tlb_n_used_entries_inc(CPUArchState *env, uintptr_t mmu_idx) @@ -242,13 +257,17 @@ static inline void tlb_n_used_entries_dec(CPUArchState *env, uintptr_t mmu_idx) void tlb_init(CPUState *cpu) { CPUArchState *env = cpu->env_ptr; + int64_t now = get_clock_realtime(); + int i; qemu_spin_init(&env_tlb(env)->c.lock); - /* Ensure that cpu_reset performs a full flush. */ - env_tlb(env)->c.dirty = ALL_MMUIDX_BITS; + /* All tlbs are initialized flushed. */ + env_tlb(env)->c.dirty = 0; - tlb_dyn_init(env); + for (i = 0; i < NB_MMU_MODES; i++) { + tlb_mmu_init(&env_tlb(env)->d[i], &env_tlb(env)->f[i], now); + } } /* flush_all_helper: run fn across all cpus @@ -287,21 +306,12 @@ void tlb_flush_counts(size_t *pfull, size_t *ppart, size_t *pelide) *pelide = elide; } -static void tlb_flush_one_mmuidx_locked(CPUArchState *env, int mmu_idx) -{ - tlb_table_flush_by_mmuidx(env, mmu_idx); - env_tlb(env)->d[mmu_idx].large_page_addr = -1; - env_tlb(env)->d[mmu_idx].large_page_mask = -1; - env_tlb(env)->d[mmu_idx].vindex = 0; - memset(env_tlb(env)->d[mmu_idx].vtable, -1, - sizeof(env_tlb(env)->d[0].vtable)); -} - static void tlb_flush_by_mmuidx_async_work(CPUState *cpu, run_on_cpu_data data) { CPUArchState *env = cpu->env_ptr; uint16_t asked = data.host_int; uint16_t all_dirty, work, to_clean; + int64_t now = get_clock_realtime(); assert_cpu_is_self(cpu); @@ -316,7 +326,7 @@ static void tlb_flush_by_mmuidx_async_work(CPUState *cpu, run_on_cpu_data data) for (work = to_clean; work != 0; work &= work - 1) { int mmu_idx = ctz32(work); - tlb_flush_one_mmuidx_locked(env, mmu_idx); + tlb_flush_one_mmuidx_locked(env, mmu_idx, now); } qemu_spin_unlock(&env_tlb(env)->c.lock); @@ -438,7 +448,7 @@ static void tlb_flush_page_locked(CPUArchState *env, int midx, tlb_debug("forcing full flush midx %d (" TARGET_FMT_lx "/" TARGET_FMT_lx ")\n", midx, lp_addr, lp_mask); - tlb_flush_one_mmuidx_locked(env, midx); + tlb_flush_one_mmuidx_locked(env, midx, get_clock_realtime()); } else { if (tlb_flush_entry_locked(tlb_entry(env, midx, page), page)) { tlb_n_used_entries_dec(env, midx); @@ -447,28 +457,29 @@ static void tlb_flush_page_locked(CPUArchState *env, int midx, } } -/* As we are going to hijack the bottom bits of the page address for a - * mmuidx bit mask we need to fail to build if we can't do that +/** + * tlb_flush_page_by_mmuidx_async_0: + * @cpu: cpu on which to flush + * @addr: page of virtual address to flush + * @idxmap: set of mmu_idx to flush + * + * Helper for tlb_flush_page_by_mmuidx and friends, flush one page + * at @addr from the tlbs indicated by @idxmap from @cpu. */ -QEMU_BUILD_BUG_ON(NB_MMU_MODES > TARGET_PAGE_BITS_MIN); - -static void tlb_flush_page_by_mmuidx_async_work(CPUState *cpu, - run_on_cpu_data data) +static void tlb_flush_page_by_mmuidx_async_0(CPUState *cpu, + target_ulong addr, + uint16_t idxmap) { CPUArchState *env = cpu->env_ptr; - target_ulong addr_and_mmuidx = (target_ulong) data.target_ptr; - target_ulong addr = addr_and_mmuidx & TARGET_PAGE_MASK; - unsigned long mmu_idx_bitmap = addr_and_mmuidx & ALL_MMUIDX_BITS; int mmu_idx; assert_cpu_is_self(cpu); - tlb_debug("page addr:" TARGET_FMT_lx " mmu_map:0x%lx\n", - addr, mmu_idx_bitmap); + tlb_debug("page addr:" TARGET_FMT_lx " mmu_map:0x%x\n", addr, idxmap); qemu_spin_lock(&env_tlb(env)->c.lock); for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) { - if (test_bit(mmu_idx, &mmu_idx_bitmap)) { + if ((idxmap >> mmu_idx) & 1) { tlb_flush_page_locked(env, mmu_idx, addr); } } @@ -477,22 +488,75 @@ static void tlb_flush_page_by_mmuidx_async_work(CPUState *cpu, tb_flush_jmp_cache(cpu, addr); } +/** + * tlb_flush_page_by_mmuidx_async_1: + * @cpu: cpu on which to flush + * @data: encoded addr + idxmap + * + * Helper for tlb_flush_page_by_mmuidx and friends, called through + * async_run_on_cpu. The idxmap parameter is encoded in the page + * offset of the target_ptr field. This limits the set of mmu_idx + * that can be passed via this method. + */ +static void tlb_flush_page_by_mmuidx_async_1(CPUState *cpu, + run_on_cpu_data data) +{ + target_ulong addr_and_idxmap = (target_ulong) data.target_ptr; + target_ulong addr = addr_and_idxmap & TARGET_PAGE_MASK; + uint16_t idxmap = addr_and_idxmap & ~TARGET_PAGE_MASK; + + tlb_flush_page_by_mmuidx_async_0(cpu, addr, idxmap); +} + +typedef struct { + target_ulong addr; + uint16_t idxmap; +} TLBFlushPageByMMUIdxData; + +/** + * tlb_flush_page_by_mmuidx_async_2: + * @cpu: cpu on which to flush + * @data: allocated addr + idxmap + * + * Helper for tlb_flush_page_by_mmuidx and friends, called through + * async_run_on_cpu. The addr+idxmap parameters are stored in a + * TLBFlushPageByMMUIdxData structure that has been allocated + * specifically for this helper. Free the structure when done. + */ +static void tlb_flush_page_by_mmuidx_async_2(CPUState *cpu, + run_on_cpu_data data) +{ + TLBFlushPageByMMUIdxData *d = data.host_ptr; + + tlb_flush_page_by_mmuidx_async_0(cpu, d->addr, d->idxmap); + g_free(d); +} + void tlb_flush_page_by_mmuidx(CPUState *cpu, target_ulong addr, uint16_t idxmap) { - target_ulong addr_and_mmu_idx; - tlb_debug("addr: "TARGET_FMT_lx" mmu_idx:%" PRIx16 "\n", addr, idxmap); /* This should already be page aligned */ - addr_and_mmu_idx = addr & TARGET_PAGE_MASK; - addr_and_mmu_idx |= idxmap; + addr &= TARGET_PAGE_MASK; - if (!qemu_cpu_is_self(cpu)) { - async_run_on_cpu(cpu, tlb_flush_page_by_mmuidx_async_work, - RUN_ON_CPU_TARGET_PTR(addr_and_mmu_idx)); + if (qemu_cpu_is_self(cpu)) { + tlb_flush_page_by_mmuidx_async_0(cpu, addr, idxmap); + } else if (idxmap < TARGET_PAGE_SIZE) { + /* + * Most targets have only a few mmu_idx. In the case where + * we can stuff idxmap into the low TARGET_PAGE_BITS, avoid + * allocating memory for this operation. + */ + async_run_on_cpu(cpu, tlb_flush_page_by_mmuidx_async_1, + RUN_ON_CPU_TARGET_PTR(addr | idxmap)); } else { - tlb_flush_page_by_mmuidx_async_work( - cpu, RUN_ON_CPU_TARGET_PTR(addr_and_mmu_idx)); + TLBFlushPageByMMUIdxData *d = g_new(TLBFlushPageByMMUIdxData, 1); + + /* Otherwise allocate a structure, freed by the worker. */ + d->addr = addr; + d->idxmap = idxmap; + async_run_on_cpu(cpu, tlb_flush_page_by_mmuidx_async_2, + RUN_ON_CPU_HOST_PTR(d)); } } @@ -504,17 +568,36 @@ void tlb_flush_page(CPUState *cpu, target_ulong addr) void tlb_flush_page_by_mmuidx_all_cpus(CPUState *src_cpu, target_ulong addr, uint16_t idxmap) { - const run_on_cpu_func fn = tlb_flush_page_by_mmuidx_async_work; - target_ulong addr_and_mmu_idx; - tlb_debug("addr: "TARGET_FMT_lx" mmu_idx:%"PRIx16"\n", addr, idxmap); /* This should already be page aligned */ - addr_and_mmu_idx = addr & TARGET_PAGE_MASK; - addr_and_mmu_idx |= idxmap; + addr &= TARGET_PAGE_MASK; - flush_all_helper(src_cpu, fn, RUN_ON_CPU_TARGET_PTR(addr_and_mmu_idx)); - fn(src_cpu, RUN_ON_CPU_TARGET_PTR(addr_and_mmu_idx)); + /* + * Allocate memory to hold addr+idxmap only when needed. + * See tlb_flush_page_by_mmuidx for details. + */ + if (idxmap < TARGET_PAGE_SIZE) { + flush_all_helper(src_cpu, tlb_flush_page_by_mmuidx_async_1, + RUN_ON_CPU_TARGET_PTR(addr | idxmap)); + } else { + CPUState *dst_cpu; + + /* Allocate a separate data block for each destination cpu. */ + CPU_FOREACH(dst_cpu) { + if (dst_cpu != src_cpu) { + TLBFlushPageByMMUIdxData *d + = g_new(TLBFlushPageByMMUIdxData, 1); + + d->addr = addr; + d->idxmap = idxmap; + async_run_on_cpu(dst_cpu, tlb_flush_page_by_mmuidx_async_2, + RUN_ON_CPU_HOST_PTR(d)); + } + } + } + + tlb_flush_page_by_mmuidx_async_0(src_cpu, addr, idxmap); } void tlb_flush_page_all_cpus(CPUState *src, target_ulong addr) @@ -526,17 +609,41 @@ void tlb_flush_page_by_mmuidx_all_cpus_synced(CPUState *src_cpu, target_ulong addr, uint16_t idxmap) { - const run_on_cpu_func fn = tlb_flush_page_by_mmuidx_async_work; - target_ulong addr_and_mmu_idx; - tlb_debug("addr: "TARGET_FMT_lx" mmu_idx:%"PRIx16"\n", addr, idxmap); /* This should already be page aligned */ - addr_and_mmu_idx = addr & TARGET_PAGE_MASK; - addr_and_mmu_idx |= idxmap; + addr &= TARGET_PAGE_MASK; - flush_all_helper(src_cpu, fn, RUN_ON_CPU_TARGET_PTR(addr_and_mmu_idx)); - async_safe_run_on_cpu(src_cpu, fn, RUN_ON_CPU_TARGET_PTR(addr_and_mmu_idx)); + /* + * Allocate memory to hold addr+idxmap only when needed. + * See tlb_flush_page_by_mmuidx for details. + */ + if (idxmap < TARGET_PAGE_SIZE) { + flush_all_helper(src_cpu, tlb_flush_page_by_mmuidx_async_1, + RUN_ON_CPU_TARGET_PTR(addr | idxmap)); + async_safe_run_on_cpu(src_cpu, tlb_flush_page_by_mmuidx_async_1, + RUN_ON_CPU_TARGET_PTR(addr | idxmap)); + } else { + CPUState *dst_cpu; + TLBFlushPageByMMUIdxData *d; + + /* Allocate a separate data block for each destination cpu. */ + CPU_FOREACH(dst_cpu) { + if (dst_cpu != src_cpu) { + d = g_new(TLBFlushPageByMMUIdxData, 1); + d->addr = addr; + d->idxmap = idxmap; + async_run_on_cpu(dst_cpu, tlb_flush_page_by_mmuidx_async_2, + RUN_ON_CPU_HOST_PTR(d)); + } + } + + d = g_new(TLBFlushPageByMMUIdxData, 1); + d->addr = addr; + d->idxmap = idxmap; + async_safe_run_on_cpu(src_cpu, tlb_flush_page_by_mmuidx_async_2, + RUN_ON_CPU_HOST_PTR(d)); + } } void tlb_flush_page_all_cpus_synced(CPUState *src, target_ulong addr) @@ -620,7 +727,7 @@ void tlb_reset_dirty(CPUState *cpu, ram_addr_t start1, ram_addr_t length) qemu_spin_lock(&env_tlb(env)->c.lock); for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) { unsigned int i; - unsigned int n = tlb_n_entries(env, mmu_idx); + unsigned int n = tlb_n_entries(&env_tlb(env)->f[mmu_idx]); for (i = 0; i < n; i++) { tlb_reset_dirty_range_locked(&env_tlb(env)->f[mmu_idx].table[i], @@ -1625,6 +1732,137 @@ tcg_target_ulong helper_be_ldsl_mmu(CPUArchState *env, target_ulong addr, return (int32_t)helper_be_ldul_mmu(env, addr, oi, retaddr); } +/* + * Load helpers for cpu_ldst.h. + */ + +static inline uint64_t cpu_load_helper(CPUArchState *env, abi_ptr addr, + int mmu_idx, uintptr_t retaddr, + MemOp op, FullLoadHelper *full_load) +{ + uint16_t meminfo; + TCGMemOpIdx oi; + uint64_t ret; + + meminfo = trace_mem_get_info(op, mmu_idx, false); + trace_guest_mem_before_exec(env_cpu(env), addr, meminfo); + + op &= ~MO_SIGN; + oi = make_memop_idx(op, mmu_idx); + ret = full_load(env, addr, oi, retaddr); + + qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, meminfo); + + return ret; +} + +uint32_t cpu_ldub_mmuidx_ra(CPUArchState *env, abi_ptr addr, + int mmu_idx, uintptr_t ra) +{ + return cpu_load_helper(env, addr, mmu_idx, ra, MO_UB, full_ldub_mmu); +} + +int cpu_ldsb_mmuidx_ra(CPUArchState *env, abi_ptr addr, + int mmu_idx, uintptr_t ra) +{ + return (int8_t)cpu_load_helper(env, addr, mmu_idx, ra, MO_SB, + full_ldub_mmu); +} + +uint32_t cpu_lduw_mmuidx_ra(CPUArchState *env, abi_ptr addr, + int mmu_idx, uintptr_t ra) +{ + return cpu_load_helper(env, addr, mmu_idx, ra, MO_TEUW, + MO_TE == MO_LE + ? full_le_lduw_mmu : full_be_lduw_mmu); +} + +int cpu_ldsw_mmuidx_ra(CPUArchState *env, abi_ptr addr, + int mmu_idx, uintptr_t ra) +{ + return (int16_t)cpu_load_helper(env, addr, mmu_idx, ra, MO_TESW, + MO_TE == MO_LE + ? full_le_lduw_mmu : full_be_lduw_mmu); +} + +uint32_t cpu_ldl_mmuidx_ra(CPUArchState *env, abi_ptr addr, + int mmu_idx, uintptr_t ra) +{ + return cpu_load_helper(env, addr, mmu_idx, ra, MO_TEUL, + MO_TE == MO_LE + ? full_le_ldul_mmu : full_be_ldul_mmu); +} + +uint64_t cpu_ldq_mmuidx_ra(CPUArchState *env, abi_ptr addr, + int mmu_idx, uintptr_t ra) +{ + return cpu_load_helper(env, addr, mmu_idx, ra, MO_TEQ, + MO_TE == MO_LE + ? helper_le_ldq_mmu : helper_be_ldq_mmu); +} + +uint32_t cpu_ldub_data_ra(CPUArchState *env, target_ulong ptr, + uintptr_t retaddr) +{ + return cpu_ldub_mmuidx_ra(env, ptr, cpu_mmu_index(env, false), retaddr); +} + +int cpu_ldsb_data_ra(CPUArchState *env, target_ulong ptr, uintptr_t retaddr) +{ + return cpu_ldsb_mmuidx_ra(env, ptr, cpu_mmu_index(env, false), retaddr); +} + +uint32_t cpu_lduw_data_ra(CPUArchState *env, target_ulong ptr, + uintptr_t retaddr) +{ + return cpu_lduw_mmuidx_ra(env, ptr, cpu_mmu_index(env, false), retaddr); +} + +int cpu_ldsw_data_ra(CPUArchState *env, target_ulong ptr, uintptr_t retaddr) +{ + return cpu_ldsw_mmuidx_ra(env, ptr, cpu_mmu_index(env, false), retaddr); +} + +uint32_t cpu_ldl_data_ra(CPUArchState *env, target_ulong ptr, uintptr_t retaddr) +{ + return cpu_ldl_mmuidx_ra(env, ptr, cpu_mmu_index(env, false), retaddr); +} + +uint64_t cpu_ldq_data_ra(CPUArchState *env, target_ulong ptr, uintptr_t retaddr) +{ + return cpu_ldq_mmuidx_ra(env, ptr, cpu_mmu_index(env, false), retaddr); +} + +uint32_t cpu_ldub_data(CPUArchState *env, target_ulong ptr) +{ + return cpu_ldub_data_ra(env, ptr, 0); +} + +int cpu_ldsb_data(CPUArchState *env, target_ulong ptr) +{ + return cpu_ldsb_data_ra(env, ptr, 0); +} + +uint32_t cpu_lduw_data(CPUArchState *env, target_ulong ptr) +{ + return cpu_lduw_data_ra(env, ptr, 0); +} + +int cpu_ldsw_data(CPUArchState *env, target_ulong ptr) +{ + return cpu_ldsw_data_ra(env, ptr, 0); +} + +uint32_t cpu_ldl_data(CPUArchState *env, target_ulong ptr) +{ + return cpu_ldl_data_ra(env, ptr, 0); +} + +uint64_t cpu_ldq_data(CPUArchState *env, target_ulong ptr) +{ + return cpu_ldq_data_ra(env, ptr, 0); +} + /* * Store Helpers */ @@ -1854,6 +2092,94 @@ void helper_be_stq_mmu(CPUArchState *env, target_ulong addr, uint64_t val, store_helper(env, addr, val, oi, retaddr, MO_BEQ); } +/* + * Store Helpers for cpu_ldst.h + */ + +static inline void QEMU_ALWAYS_INLINE +cpu_store_helper(CPUArchState *env, target_ulong addr, uint64_t val, + int mmu_idx, uintptr_t retaddr, MemOp op) +{ + TCGMemOpIdx oi; + uint16_t meminfo; + + meminfo = trace_mem_get_info(op, mmu_idx, true); + trace_guest_mem_before_exec(env_cpu(env), addr, meminfo); + + oi = make_memop_idx(op, mmu_idx); + store_helper(env, addr, val, oi, retaddr, op); + + qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, meminfo); +} + +void cpu_stb_mmuidx_ra(CPUArchState *env, target_ulong addr, uint32_t val, + int mmu_idx, uintptr_t retaddr) +{ + cpu_store_helper(env, addr, val, mmu_idx, retaddr, MO_UB); +} + +void cpu_stw_mmuidx_ra(CPUArchState *env, target_ulong addr, uint32_t val, + int mmu_idx, uintptr_t retaddr) +{ + cpu_store_helper(env, addr, val, mmu_idx, retaddr, MO_TEUW); +} + +void cpu_stl_mmuidx_ra(CPUArchState *env, target_ulong addr, uint32_t val, + int mmu_idx, uintptr_t retaddr) +{ + cpu_store_helper(env, addr, val, mmu_idx, retaddr, MO_TEUL); +} + +void cpu_stq_mmuidx_ra(CPUArchState *env, target_ulong addr, uint64_t val, + int mmu_idx, uintptr_t retaddr) +{ + cpu_store_helper(env, addr, val, mmu_idx, retaddr, MO_TEQ); +} + +void cpu_stb_data_ra(CPUArchState *env, target_ulong ptr, + uint32_t val, uintptr_t retaddr) +{ + cpu_stb_mmuidx_ra(env, ptr, val, cpu_mmu_index(env, false), retaddr); +} + +void cpu_stw_data_ra(CPUArchState *env, target_ulong ptr, + uint32_t val, uintptr_t retaddr) +{ + cpu_stw_mmuidx_ra(env, ptr, val, cpu_mmu_index(env, false), retaddr); +} + +void cpu_stl_data_ra(CPUArchState *env, target_ulong ptr, + uint32_t val, uintptr_t retaddr) +{ + cpu_stl_mmuidx_ra(env, ptr, val, cpu_mmu_index(env, false), retaddr); +} + +void cpu_stq_data_ra(CPUArchState *env, target_ulong ptr, + uint64_t val, uintptr_t retaddr) +{ + cpu_stq_mmuidx_ra(env, ptr, val, cpu_mmu_index(env, false), retaddr); +} + +void cpu_stb_data(CPUArchState *env, target_ulong ptr, uint32_t val) +{ + cpu_stb_data_ra(env, ptr, val, 0); +} + +void cpu_stw_data(CPUArchState *env, target_ulong ptr, uint32_t val) +{ + cpu_stw_data_ra(env, ptr, val, 0); +} + +void cpu_stl_data(CPUArchState *env, target_ulong ptr, uint32_t val) +{ + cpu_stl_data_ra(env, ptr, val, 0); +} + +void cpu_stq_data(CPUArchState *env, target_ulong ptr, uint64_t val) +{ + cpu_stq_data_ra(env, ptr, val, 0); +} + /* First set of helpers allows passing in of OI and RETADDR. This makes them callable from other helpers. */ @@ -1912,98 +2238,50 @@ void helper_be_stq_mmu(CPUArchState *env, target_ulong addr, uint64_t val, /* Code access functions. */ -static uint64_t full_ldub_cmmu(CPUArchState *env, target_ulong addr, +static uint64_t full_ldub_code(CPUArchState *env, target_ulong addr, TCGMemOpIdx oi, uintptr_t retaddr) { - return load_helper(env, addr, oi, retaddr, MO_8, true, full_ldub_cmmu); + return load_helper(env, addr, oi, retaddr, MO_8, true, full_ldub_code); } -uint8_t helper_ret_ldub_cmmu(CPUArchState *env, target_ulong addr, - TCGMemOpIdx oi, uintptr_t retaddr) +uint32_t cpu_ldub_code(CPUArchState *env, abi_ptr addr) { - return full_ldub_cmmu(env, addr, oi, retaddr); + TCGMemOpIdx oi = make_memop_idx(MO_UB, cpu_mmu_index(env, true)); + return full_ldub_code(env, addr, oi, 0); } -int8_t helper_ret_ldsb_cmmu(CPUArchState *env, target_ulong addr, - TCGMemOpIdx oi, uintptr_t retaddr) +static uint64_t full_lduw_code(CPUArchState *env, target_ulong addr, + TCGMemOpIdx oi, uintptr_t retaddr) { - return (int8_t) full_ldub_cmmu(env, addr, oi, retaddr); + return load_helper(env, addr, oi, retaddr, MO_TEUW, true, full_lduw_code); } -static uint64_t full_le_lduw_cmmu(CPUArchState *env, target_ulong addr, - TCGMemOpIdx oi, uintptr_t retaddr) +uint32_t cpu_lduw_code(CPUArchState *env, abi_ptr addr) { - return load_helper(env, addr, oi, retaddr, MO_LEUW, true, - full_le_lduw_cmmu); + TCGMemOpIdx oi = make_memop_idx(MO_TEUW, cpu_mmu_index(env, true)); + return full_lduw_code(env, addr, oi, 0); } -uint16_t helper_le_lduw_cmmu(CPUArchState *env, target_ulong addr, - TCGMemOpIdx oi, uintptr_t retaddr) +static uint64_t full_ldl_code(CPUArchState *env, target_ulong addr, + TCGMemOpIdx oi, uintptr_t retaddr) { - return full_le_lduw_cmmu(env, addr, oi, retaddr); + return load_helper(env, addr, oi, retaddr, MO_TEUL, true, full_ldl_code); } -int16_t helper_le_ldsw_cmmu(CPUArchState *env, target_ulong addr, - TCGMemOpIdx oi, uintptr_t retaddr) +uint32_t cpu_ldl_code(CPUArchState *env, abi_ptr addr) { - return (int16_t) full_le_lduw_cmmu(env, addr, oi, retaddr); + TCGMemOpIdx oi = make_memop_idx(MO_TEUL, cpu_mmu_index(env, true)); + return full_ldl_code(env, addr, oi, 0); } -static uint64_t full_be_lduw_cmmu(CPUArchState *env, target_ulong addr, - TCGMemOpIdx oi, uintptr_t retaddr) +static uint64_t full_ldq_code(CPUArchState *env, target_ulong addr, + TCGMemOpIdx oi, uintptr_t retaddr) { - return load_helper(env, addr, oi, retaddr, MO_BEUW, true, - full_be_lduw_cmmu); + return load_helper(env, addr, oi, retaddr, MO_TEQ, true, full_ldq_code); } -uint16_t helper_be_lduw_cmmu(CPUArchState *env, target_ulong addr, - TCGMemOpIdx oi, uintptr_t retaddr) +uint64_t cpu_ldq_code(CPUArchState *env, abi_ptr addr) { - return full_be_lduw_cmmu(env, addr, oi, retaddr); -} - -int16_t helper_be_ldsw_cmmu(CPUArchState *env, target_ulong addr, - TCGMemOpIdx oi, uintptr_t retaddr) -{ - return (int16_t) full_be_lduw_cmmu(env, addr, oi, retaddr); -} - -static uint64_t full_le_ldul_cmmu(CPUArchState *env, target_ulong addr, - TCGMemOpIdx oi, uintptr_t retaddr) -{ - return load_helper(env, addr, oi, retaddr, MO_LEUL, true, - full_le_ldul_cmmu); -} - -uint32_t helper_le_ldl_cmmu(CPUArchState *env, target_ulong addr, - TCGMemOpIdx oi, uintptr_t retaddr) -{ - return full_le_ldul_cmmu(env, addr, oi, retaddr); -} - -static uint64_t full_be_ldul_cmmu(CPUArchState *env, target_ulong addr, - TCGMemOpIdx oi, uintptr_t retaddr) -{ - return load_helper(env, addr, oi, retaddr, MO_BEUL, true, - full_be_ldul_cmmu); -} - -uint32_t helper_be_ldl_cmmu(CPUArchState *env, target_ulong addr, - TCGMemOpIdx oi, uintptr_t retaddr) -{ - return full_be_ldul_cmmu(env, addr, oi, retaddr); -} - -uint64_t helper_le_ldq_cmmu(CPUArchState *env, target_ulong addr, - TCGMemOpIdx oi, uintptr_t retaddr) -{ - return load_helper(env, addr, oi, retaddr, MO_LEQ, true, - helper_le_ldq_cmmu); -} - -uint64_t helper_be_ldq_cmmu(CPUArchState *env, target_ulong addr, - TCGMemOpIdx oi, uintptr_t retaddr) -{ - return load_helper(env, addr, oi, retaddr, MO_BEQ, true, - helper_be_ldq_cmmu); + TCGMemOpIdx oi = make_memop_idx(MO_TEQ, cpu_mmu_index(env, true)); + return full_ldq_code(env, addr, oi, 0); } diff --git a/accel/tcg/tcg-all.c b/accel/tcg/tcg-all.c index c59d5b0024..acfdcfdf59 100644 --- a/accel/tcg/tcg-all.c +++ b/accel/tcg/tcg-all.c @@ -30,8 +30,23 @@ #include "cpu.h" #include "sysemu/cpus.h" #include "qemu/main-loop.h" +#include "tcg/tcg.h" +#include "qapi/error.h" +#include "qemu/error-report.h" +#include "hw/boards.h" +#include "qapi/qapi-builtin-visit.h" -unsigned long tcg_tb_size; +typedef struct TCGState { + AccelState parent_obj; + + bool mttcg_enabled; + unsigned long tb_size; +} TCGState; + +#define TYPE_TCG_ACCEL ACCEL_CLASS_NAME("tcg") + +#define TCG_STATE(obj) \ + OBJECT_CHECK(TCGState, (obj), TYPE_TCG_ACCEL) /* mask must never be zero, except for A20 change call */ static void tcg_handle_interrupt(CPUState *cpu, int mask) @@ -58,27 +73,153 @@ static void tcg_handle_interrupt(CPUState *cpu, int mask) } } +/* + * We default to false if we know other options have been enabled + * which are currently incompatible with MTTCG. Otherwise when each + * guest (target) has been updated to support: + * - atomic instructions + * - memory ordering primitives (barriers) + * they can set the appropriate CONFIG flags in ${target}-softmmu.mak + * + * Once a guest architecture has been converted to the new primitives + * there are two remaining limitations to check. + * + * - The guest can't be oversized (e.g. 64 bit guest on 32 bit host) + * - The host must have a stronger memory order than the guest + * + * It may be possible in future to support strong guests on weak hosts + * but that will require tagging all load/stores in a guest with their + * implicit memory order requirements which would likely slow things + * down a lot. + */ + +static bool check_tcg_memory_orders_compatible(void) +{ +#if defined(TCG_GUEST_DEFAULT_MO) && defined(TCG_TARGET_DEFAULT_MO) + return (TCG_GUEST_DEFAULT_MO & ~TCG_TARGET_DEFAULT_MO) == 0; +#else + return false; +#endif +} + +static bool default_mttcg_enabled(void) +{ + if (use_icount || TCG_OVERSIZED_GUEST) { + return false; + } else { +#ifdef TARGET_SUPPORTS_MTTCG + return check_tcg_memory_orders_compatible(); +#else + return false; +#endif + } +} + +static void tcg_accel_instance_init(Object *obj) +{ + TCGState *s = TCG_STATE(obj); + + s->mttcg_enabled = default_mttcg_enabled(); +} + static int tcg_init(MachineState *ms) { - tcg_exec_init(tcg_tb_size * 1024 * 1024); + TCGState *s = TCG_STATE(current_accel()); + + tcg_exec_init(s->tb_size * 1024 * 1024); cpu_interrupt_handler = tcg_handle_interrupt; + mttcg_enabled = s->mttcg_enabled; return 0; } +static char *tcg_get_thread(Object *obj, Error **errp) +{ + TCGState *s = TCG_STATE(obj); + + return g_strdup(s->mttcg_enabled ? "multi" : "single"); +} + +static void tcg_set_thread(Object *obj, const char *value, Error **errp) +{ + TCGState *s = TCG_STATE(obj); + + if (strcmp(value, "multi") == 0) { + if (TCG_OVERSIZED_GUEST) { + error_setg(errp, "No MTTCG when guest word size > hosts"); + } else if (use_icount) { + error_setg(errp, "No MTTCG when icount is enabled"); + } else { +#ifndef TARGET_SUPPORTS_MTTCG + warn_report("Guest not yet converted to MTTCG - " + "you may get unexpected results"); +#endif + if (!check_tcg_memory_orders_compatible()) { + warn_report("Guest expects a stronger memory ordering " + "than the host provides"); + error_printf("This may cause strange/hard to debug errors\n"); + } + s->mttcg_enabled = true; + } + } else if (strcmp(value, "single") == 0) { + s->mttcg_enabled = false; + } else { + error_setg(errp, "Invalid 'thread' setting %s", value); + } +} + +static void tcg_get_tb_size(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + TCGState *s = TCG_STATE(obj); + uint32_t value = s->tb_size; + + visit_type_uint32(v, name, &value, errp); +} + +static void tcg_set_tb_size(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + TCGState *s = TCG_STATE(obj); + Error *error = NULL; + uint32_t value; + + visit_type_uint32(v, name, &value, &error); + if (error) { + error_propagate(errp, error); + return; + } + + s->tb_size = value; +} + static void tcg_accel_class_init(ObjectClass *oc, void *data) { AccelClass *ac = ACCEL_CLASS(oc); ac->name = "tcg"; ac->init_machine = tcg_init; ac->allowed = &tcg_allowed; -} -#define TYPE_TCG_ACCEL ACCEL_CLASS_NAME("tcg") + object_class_property_add_str(oc, "thread", + tcg_get_thread, + tcg_set_thread, + NULL); + + object_class_property_add(oc, "tb-size", "int", + tcg_get_tb_size, tcg_set_tb_size, + NULL, NULL, &error_abort); + object_class_property_set_description(oc, "tb-size", + "TCG translation block cache size", &error_abort); + +} static const TypeInfo tcg_accel_type = { .name = TYPE_TCG_ACCEL, .parent = TYPE_ACCEL, + .instance_init = tcg_accel_instance_init, .class_init = tcg_accel_class_init, + .instance_size = sizeof(TCGState), }; static void register_accel_types(void) diff --git a/accel/tcg/tcg-runtime-gvec.c b/accel/tcg/tcg-runtime-gvec.c index 51cb29ca79..ca449702e6 100644 --- a/accel/tcg/tcg-runtime-gvec.c +++ b/accel/tcg/tcg-runtime-gvec.c @@ -21,51 +21,9 @@ #include "qemu/host-utils.h" #include "cpu.h" #include "exec/helper-proto.h" -#include "tcg-gvec-desc.h" +#include "tcg/tcg-gvec-desc.h" -/* Virtually all hosts support 16-byte vectors. Those that don't can emulate - * them via GCC's generic vector extension. This turns out to be simpler and - * more reliable than getting the compiler to autovectorize. - * - * In tcg-op-gvec.c, we asserted that both the size and alignment of the data - * are multiples of 16. - * - * When the compiler does not support all of the operations we require, the - * loops are written so that we can always fall back on the base types. - */ -#ifdef CONFIG_VECTOR16 -typedef uint8_t vec8 __attribute__((vector_size(16))); -typedef uint16_t vec16 __attribute__((vector_size(16))); -typedef uint32_t vec32 __attribute__((vector_size(16))); -typedef uint64_t vec64 __attribute__((vector_size(16))); - -typedef int8_t svec8 __attribute__((vector_size(16))); -typedef int16_t svec16 __attribute__((vector_size(16))); -typedef int32_t svec32 __attribute__((vector_size(16))); -typedef int64_t svec64 __attribute__((vector_size(16))); - -#define DUP16(X) { X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X } -#define DUP8(X) { X, X, X, X, X, X, X, X } -#define DUP4(X) { X, X, X, X } -#define DUP2(X) { X, X } -#else -typedef uint8_t vec8; -typedef uint16_t vec16; -typedef uint32_t vec32; -typedef uint64_t vec64; - -typedef int8_t svec8; -typedef int16_t svec16; -typedef int32_t svec32; -typedef int64_t svec64; - -#define DUP16(X) X -#define DUP8(X) X -#define DUP4(X) X -#define DUP2(X) X -#endif /* CONFIG_VECTOR16 */ - static inline void clear_high(void *d, intptr_t oprsz, uint32_t desc) { intptr_t maxsz = simd_maxsz(desc); @@ -83,8 +41,8 @@ void HELPER(gvec_add8)(void *d, void *a, void *b, uint32_t desc) intptr_t oprsz = simd_oprsz(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec8)) { - *(vec8 *)(d + i) = *(vec8 *)(a + i) + *(vec8 *)(b + i); + for (i = 0; i < oprsz; i += sizeof(uint8_t)) { + *(uint8_t *)(d + i) = *(uint8_t *)(a + i) + *(uint8_t *)(b + i); } clear_high(d, oprsz, desc); } @@ -94,8 +52,8 @@ void HELPER(gvec_add16)(void *d, void *a, void *b, uint32_t desc) intptr_t oprsz = simd_oprsz(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec16)) { - *(vec16 *)(d + i) = *(vec16 *)(a + i) + *(vec16 *)(b + i); + for (i = 0; i < oprsz; i += sizeof(uint16_t)) { + *(uint16_t *)(d + i) = *(uint16_t *)(a + i) + *(uint16_t *)(b + i); } clear_high(d, oprsz, desc); } @@ -105,8 +63,8 @@ void HELPER(gvec_add32)(void *d, void *a, void *b, uint32_t desc) intptr_t oprsz = simd_oprsz(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec32)) { - *(vec32 *)(d + i) = *(vec32 *)(a + i) + *(vec32 *)(b + i); + for (i = 0; i < oprsz; i += sizeof(uint32_t)) { + *(uint32_t *)(d + i) = *(uint32_t *)(a + i) + *(uint32_t *)(b + i); } clear_high(d, oprsz, desc); } @@ -116,8 +74,8 @@ void HELPER(gvec_add64)(void *d, void *a, void *b, uint32_t desc) intptr_t oprsz = simd_oprsz(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec64)) { - *(vec64 *)(d + i) = *(vec64 *)(a + i) + *(vec64 *)(b + i); + for (i = 0; i < oprsz; i += sizeof(uint64_t)) { + *(uint64_t *)(d + i) = *(uint64_t *)(a + i) + *(uint64_t *)(b + i); } clear_high(d, oprsz, desc); } @@ -125,11 +83,10 @@ void HELPER(gvec_add64)(void *d, void *a, void *b, uint32_t desc) void HELPER(gvec_adds8)(void *d, void *a, uint64_t b, uint32_t desc) { intptr_t oprsz = simd_oprsz(desc); - vec8 vecb = (vec8)DUP16(b); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec8)) { - *(vec8 *)(d + i) = *(vec8 *)(a + i) + vecb; + for (i = 0; i < oprsz; i += sizeof(uint8_t)) { + *(uint8_t *)(d + i) = *(uint8_t *)(a + i) + (uint8_t)b; } clear_high(d, oprsz, desc); } @@ -137,11 +94,10 @@ void HELPER(gvec_adds8)(void *d, void *a, uint64_t b, uint32_t desc) void HELPER(gvec_adds16)(void *d, void *a, uint64_t b, uint32_t desc) { intptr_t oprsz = simd_oprsz(desc); - vec16 vecb = (vec16)DUP8(b); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec16)) { - *(vec16 *)(d + i) = *(vec16 *)(a + i) + vecb; + for (i = 0; i < oprsz; i += sizeof(uint16_t)) { + *(uint16_t *)(d + i) = *(uint16_t *)(a + i) + (uint16_t)b; } clear_high(d, oprsz, desc); } @@ -149,11 +105,10 @@ void HELPER(gvec_adds16)(void *d, void *a, uint64_t b, uint32_t desc) void HELPER(gvec_adds32)(void *d, void *a, uint64_t b, uint32_t desc) { intptr_t oprsz = simd_oprsz(desc); - vec32 vecb = (vec32)DUP4(b); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec32)) { - *(vec32 *)(d + i) = *(vec32 *)(a + i) + vecb; + for (i = 0; i < oprsz; i += sizeof(uint32_t)) { + *(uint32_t *)(d + i) = *(uint32_t *)(a + i) + (uint32_t)b; } clear_high(d, oprsz, desc); } @@ -161,11 +116,10 @@ void HELPER(gvec_adds32)(void *d, void *a, uint64_t b, uint32_t desc) void HELPER(gvec_adds64)(void *d, void *a, uint64_t b, uint32_t desc) { intptr_t oprsz = simd_oprsz(desc); - vec64 vecb = (vec64)DUP2(b); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec64)) { - *(vec64 *)(d + i) = *(vec64 *)(a + i) + vecb; + for (i = 0; i < oprsz; i += sizeof(uint64_t)) { + *(uint64_t *)(d + i) = *(uint64_t *)(a + i) + b; } clear_high(d, oprsz, desc); } @@ -175,8 +129,8 @@ void HELPER(gvec_sub8)(void *d, void *a, void *b, uint32_t desc) intptr_t oprsz = simd_oprsz(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec8)) { - *(vec8 *)(d + i) = *(vec8 *)(a + i) - *(vec8 *)(b + i); + for (i = 0; i < oprsz; i += sizeof(uint8_t)) { + *(uint8_t *)(d + i) = *(uint8_t *)(a + i) - *(uint8_t *)(b + i); } clear_high(d, oprsz, desc); } @@ -186,8 +140,8 @@ void HELPER(gvec_sub16)(void *d, void *a, void *b, uint32_t desc) intptr_t oprsz = simd_oprsz(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec16)) { - *(vec16 *)(d + i) = *(vec16 *)(a + i) - *(vec16 *)(b + i); + for (i = 0; i < oprsz; i += sizeof(uint16_t)) { + *(uint16_t *)(d + i) = *(uint16_t *)(a + i) - *(uint16_t *)(b + i); } clear_high(d, oprsz, desc); } @@ -197,8 +151,8 @@ void HELPER(gvec_sub32)(void *d, void *a, void *b, uint32_t desc) intptr_t oprsz = simd_oprsz(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec32)) { - *(vec32 *)(d + i) = *(vec32 *)(a + i) - *(vec32 *)(b + i); + for (i = 0; i < oprsz; i += sizeof(uint32_t)) { + *(uint32_t *)(d + i) = *(uint32_t *)(a + i) - *(uint32_t *)(b + i); } clear_high(d, oprsz, desc); } @@ -208,8 +162,8 @@ void HELPER(gvec_sub64)(void *d, void *a, void *b, uint32_t desc) intptr_t oprsz = simd_oprsz(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec64)) { - *(vec64 *)(d + i) = *(vec64 *)(a + i) - *(vec64 *)(b + i); + for (i = 0; i < oprsz; i += sizeof(uint64_t)) { + *(uint64_t *)(d + i) = *(uint64_t *)(a + i) - *(uint64_t *)(b + i); } clear_high(d, oprsz, desc); } @@ -217,11 +171,10 @@ void HELPER(gvec_sub64)(void *d, void *a, void *b, uint32_t desc) void HELPER(gvec_subs8)(void *d, void *a, uint64_t b, uint32_t desc) { intptr_t oprsz = simd_oprsz(desc); - vec8 vecb = (vec8)DUP16(b); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec8)) { - *(vec8 *)(d + i) = *(vec8 *)(a + i) - vecb; + for (i = 0; i < oprsz; i += sizeof(uint8_t)) { + *(uint8_t *)(d + i) = *(uint8_t *)(a + i) - (uint8_t)b; } clear_high(d, oprsz, desc); } @@ -229,11 +182,10 @@ void HELPER(gvec_subs8)(void *d, void *a, uint64_t b, uint32_t desc) void HELPER(gvec_subs16)(void *d, void *a, uint64_t b, uint32_t desc) { intptr_t oprsz = simd_oprsz(desc); - vec16 vecb = (vec16)DUP8(b); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec16)) { - *(vec16 *)(d + i) = *(vec16 *)(a + i) - vecb; + for (i = 0; i < oprsz; i += sizeof(uint16_t)) { + *(uint16_t *)(d + i) = *(uint16_t *)(a + i) - (uint16_t)b; } clear_high(d, oprsz, desc); } @@ -241,11 +193,10 @@ void HELPER(gvec_subs16)(void *d, void *a, uint64_t b, uint32_t desc) void HELPER(gvec_subs32)(void *d, void *a, uint64_t b, uint32_t desc) { intptr_t oprsz = simd_oprsz(desc); - vec32 vecb = (vec32)DUP4(b); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec32)) { - *(vec32 *)(d + i) = *(vec32 *)(a + i) - vecb; + for (i = 0; i < oprsz; i += sizeof(uint32_t)) { + *(uint32_t *)(d + i) = *(uint32_t *)(a + i) - (uint32_t)b; } clear_high(d, oprsz, desc); } @@ -253,11 +204,10 @@ void HELPER(gvec_subs32)(void *d, void *a, uint64_t b, uint32_t desc) void HELPER(gvec_subs64)(void *d, void *a, uint64_t b, uint32_t desc) { intptr_t oprsz = simd_oprsz(desc); - vec64 vecb = (vec64)DUP2(b); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec64)) { - *(vec64 *)(d + i) = *(vec64 *)(a + i) - vecb; + for (i = 0; i < oprsz; i += sizeof(uint64_t)) { + *(uint64_t *)(d + i) = *(uint64_t *)(a + i) - b; } clear_high(d, oprsz, desc); } @@ -267,8 +217,8 @@ void HELPER(gvec_mul8)(void *d, void *a, void *b, uint32_t desc) intptr_t oprsz = simd_oprsz(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec8)) { - *(vec8 *)(d + i) = *(vec8 *)(a + i) * *(vec8 *)(b + i); + for (i = 0; i < oprsz; i += sizeof(uint8_t)) { + *(uint8_t *)(d + i) = *(uint8_t *)(a + i) * *(uint8_t *)(b + i); } clear_high(d, oprsz, desc); } @@ -278,8 +228,8 @@ void HELPER(gvec_mul16)(void *d, void *a, void *b, uint32_t desc) intptr_t oprsz = simd_oprsz(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec16)) { - *(vec16 *)(d + i) = *(vec16 *)(a + i) * *(vec16 *)(b + i); + for (i = 0; i < oprsz; i += sizeof(uint16_t)) { + *(uint16_t *)(d + i) = *(uint16_t *)(a + i) * *(uint16_t *)(b + i); } clear_high(d, oprsz, desc); } @@ -289,8 +239,8 @@ void HELPER(gvec_mul32)(void *d, void *a, void *b, uint32_t desc) intptr_t oprsz = simd_oprsz(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec32)) { - *(vec32 *)(d + i) = *(vec32 *)(a + i) * *(vec32 *)(b + i); + for (i = 0; i < oprsz; i += sizeof(uint32_t)) { + *(uint32_t *)(d + i) = *(uint32_t *)(a + i) * *(uint32_t *)(b + i); } clear_high(d, oprsz, desc); } @@ -300,8 +250,8 @@ void HELPER(gvec_mul64)(void *d, void *a, void *b, uint32_t desc) intptr_t oprsz = simd_oprsz(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec64)) { - *(vec64 *)(d + i) = *(vec64 *)(a + i) * *(vec64 *)(b + i); + for (i = 0; i < oprsz; i += sizeof(uint64_t)) { + *(uint64_t *)(d + i) = *(uint64_t *)(a + i) * *(uint64_t *)(b + i); } clear_high(d, oprsz, desc); } @@ -309,11 +259,10 @@ void HELPER(gvec_mul64)(void *d, void *a, void *b, uint32_t desc) void HELPER(gvec_muls8)(void *d, void *a, uint64_t b, uint32_t desc) { intptr_t oprsz = simd_oprsz(desc); - vec8 vecb = (vec8)DUP16(b); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec8)) { - *(vec8 *)(d + i) = *(vec8 *)(a + i) * vecb; + for (i = 0; i < oprsz; i += sizeof(uint8_t)) { + *(uint8_t *)(d + i) = *(uint8_t *)(a + i) * (uint8_t)b; } clear_high(d, oprsz, desc); } @@ -321,11 +270,10 @@ void HELPER(gvec_muls8)(void *d, void *a, uint64_t b, uint32_t desc) void HELPER(gvec_muls16)(void *d, void *a, uint64_t b, uint32_t desc) { intptr_t oprsz = simd_oprsz(desc); - vec16 vecb = (vec16)DUP8(b); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec16)) { - *(vec16 *)(d + i) = *(vec16 *)(a + i) * vecb; + for (i = 0; i < oprsz; i += sizeof(uint16_t)) { + *(uint16_t *)(d + i) = *(uint16_t *)(a + i) * (uint16_t)b; } clear_high(d, oprsz, desc); } @@ -333,11 +281,10 @@ void HELPER(gvec_muls16)(void *d, void *a, uint64_t b, uint32_t desc) void HELPER(gvec_muls32)(void *d, void *a, uint64_t b, uint32_t desc) { intptr_t oprsz = simd_oprsz(desc); - vec32 vecb = (vec32)DUP4(b); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec32)) { - *(vec32 *)(d + i) = *(vec32 *)(a + i) * vecb; + for (i = 0; i < oprsz; i += sizeof(uint32_t)) { + *(uint32_t *)(d + i) = *(uint32_t *)(a + i) * (uint32_t)b; } clear_high(d, oprsz, desc); } @@ -345,11 +292,10 @@ void HELPER(gvec_muls32)(void *d, void *a, uint64_t b, uint32_t desc) void HELPER(gvec_muls64)(void *d, void *a, uint64_t b, uint32_t desc) { intptr_t oprsz = simd_oprsz(desc); - vec64 vecb = (vec64)DUP2(b); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec64)) { - *(vec64 *)(d + i) = *(vec64 *)(a + i) * vecb; + for (i = 0; i < oprsz; i += sizeof(uint64_t)) { + *(uint64_t *)(d + i) = *(uint64_t *)(a + i) * b; } clear_high(d, oprsz, desc); } @@ -359,8 +305,8 @@ void HELPER(gvec_neg8)(void *d, void *a, uint32_t desc) intptr_t oprsz = simd_oprsz(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec8)) { - *(vec8 *)(d + i) = -*(vec8 *)(a + i); + for (i = 0; i < oprsz; i += sizeof(uint8_t)) { + *(uint8_t *)(d + i) = -*(uint8_t *)(a + i); } clear_high(d, oprsz, desc); } @@ -370,8 +316,8 @@ void HELPER(gvec_neg16)(void *d, void *a, uint32_t desc) intptr_t oprsz = simd_oprsz(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec16)) { - *(vec16 *)(d + i) = -*(vec16 *)(a + i); + for (i = 0; i < oprsz; i += sizeof(uint16_t)) { + *(uint16_t *)(d + i) = -*(uint16_t *)(a + i); } clear_high(d, oprsz, desc); } @@ -381,8 +327,8 @@ void HELPER(gvec_neg32)(void *d, void *a, uint32_t desc) intptr_t oprsz = simd_oprsz(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec32)) { - *(vec32 *)(d + i) = -*(vec32 *)(a + i); + for (i = 0; i < oprsz; i += sizeof(uint32_t)) { + *(uint32_t *)(d + i) = -*(uint32_t *)(a + i); } clear_high(d, oprsz, desc); } @@ -392,8 +338,8 @@ void HELPER(gvec_neg64)(void *d, void *a, uint32_t desc) intptr_t oprsz = simd_oprsz(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec64)) { - *(vec64 *)(d + i) = -*(vec64 *)(a + i); + for (i = 0; i < oprsz; i += sizeof(uint64_t)) { + *(uint64_t *)(d + i) = -*(uint64_t *)(a + i); } clear_high(d, oprsz, desc); } @@ -499,8 +445,8 @@ void HELPER(gvec_not)(void *d, void *a, uint32_t desc) intptr_t oprsz = simd_oprsz(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec64)) { - *(vec64 *)(d + i) = ~*(vec64 *)(a + i); + for (i = 0; i < oprsz; i += sizeof(uint64_t)) { + *(uint64_t *)(d + i) = ~*(uint64_t *)(a + i); } clear_high(d, oprsz, desc); } @@ -510,8 +456,8 @@ void HELPER(gvec_and)(void *d, void *a, void *b, uint32_t desc) intptr_t oprsz = simd_oprsz(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec64)) { - *(vec64 *)(d + i) = *(vec64 *)(a + i) & *(vec64 *)(b + i); + for (i = 0; i < oprsz; i += sizeof(uint64_t)) { + *(uint64_t *)(d + i) = *(uint64_t *)(a + i) & *(uint64_t *)(b + i); } clear_high(d, oprsz, desc); } @@ -521,8 +467,8 @@ void HELPER(gvec_or)(void *d, void *a, void *b, uint32_t desc) intptr_t oprsz = simd_oprsz(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec64)) { - *(vec64 *)(d + i) = *(vec64 *)(a + i) | *(vec64 *)(b + i); + for (i = 0; i < oprsz; i += sizeof(uint64_t)) { + *(uint64_t *)(d + i) = *(uint64_t *)(a + i) | *(uint64_t *)(b + i); } clear_high(d, oprsz, desc); } @@ -532,8 +478,8 @@ void HELPER(gvec_xor)(void *d, void *a, void *b, uint32_t desc) intptr_t oprsz = simd_oprsz(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec64)) { - *(vec64 *)(d + i) = *(vec64 *)(a + i) ^ *(vec64 *)(b + i); + for (i = 0; i < oprsz; i += sizeof(uint64_t)) { + *(uint64_t *)(d + i) = *(uint64_t *)(a + i) ^ *(uint64_t *)(b + i); } clear_high(d, oprsz, desc); } @@ -543,8 +489,8 @@ void HELPER(gvec_andc)(void *d, void *a, void *b, uint32_t desc) intptr_t oprsz = simd_oprsz(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec64)) { - *(vec64 *)(d + i) = *(vec64 *)(a + i) &~ *(vec64 *)(b + i); + for (i = 0; i < oprsz; i += sizeof(uint64_t)) { + *(uint64_t *)(d + i) = *(uint64_t *)(a + i) &~ *(uint64_t *)(b + i); } clear_high(d, oprsz, desc); } @@ -554,8 +500,8 @@ void HELPER(gvec_orc)(void *d, void *a, void *b, uint32_t desc) intptr_t oprsz = simd_oprsz(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec64)) { - *(vec64 *)(d + i) = *(vec64 *)(a + i) |~ *(vec64 *)(b + i); + for (i = 0; i < oprsz; i += sizeof(uint64_t)) { + *(uint64_t *)(d + i) = *(uint64_t *)(a + i) |~ *(uint64_t *)(b + i); } clear_high(d, oprsz, desc); } @@ -565,8 +511,8 @@ void HELPER(gvec_nand)(void *d, void *a, void *b, uint32_t desc) intptr_t oprsz = simd_oprsz(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec64)) { - *(vec64 *)(d + i) = ~(*(vec64 *)(a + i) & *(vec64 *)(b + i)); + for (i = 0; i < oprsz; i += sizeof(uint64_t)) { + *(uint64_t *)(d + i) = ~(*(uint64_t *)(a + i) & *(uint64_t *)(b + i)); } clear_high(d, oprsz, desc); } @@ -576,8 +522,8 @@ void HELPER(gvec_nor)(void *d, void *a, void *b, uint32_t desc) intptr_t oprsz = simd_oprsz(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec64)) { - *(vec64 *)(d + i) = ~(*(vec64 *)(a + i) | *(vec64 *)(b + i)); + for (i = 0; i < oprsz; i += sizeof(uint64_t)) { + *(uint64_t *)(d + i) = ~(*(uint64_t *)(a + i) | *(uint64_t *)(b + i)); } clear_high(d, oprsz, desc); } @@ -587,8 +533,8 @@ void HELPER(gvec_eqv)(void *d, void *a, void *b, uint32_t desc) intptr_t oprsz = simd_oprsz(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec64)) { - *(vec64 *)(d + i) = ~(*(vec64 *)(a + i) ^ *(vec64 *)(b + i)); + for (i = 0; i < oprsz; i += sizeof(uint64_t)) { + *(uint64_t *)(d + i) = ~(*(uint64_t *)(a + i) ^ *(uint64_t *)(b + i)); } clear_high(d, oprsz, desc); } @@ -596,11 +542,10 @@ void HELPER(gvec_eqv)(void *d, void *a, void *b, uint32_t desc) void HELPER(gvec_ands)(void *d, void *a, uint64_t b, uint32_t desc) { intptr_t oprsz = simd_oprsz(desc); - vec64 vecb = (vec64)DUP2(b); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec64)) { - *(vec64 *)(d + i) = *(vec64 *)(a + i) & vecb; + for (i = 0; i < oprsz; i += sizeof(uint64_t)) { + *(uint64_t *)(d + i) = *(uint64_t *)(a + i) & b; } clear_high(d, oprsz, desc); } @@ -608,11 +553,10 @@ void HELPER(gvec_ands)(void *d, void *a, uint64_t b, uint32_t desc) void HELPER(gvec_xors)(void *d, void *a, uint64_t b, uint32_t desc) { intptr_t oprsz = simd_oprsz(desc); - vec64 vecb = (vec64)DUP2(b); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec64)) { - *(vec64 *)(d + i) = *(vec64 *)(a + i) ^ vecb; + for (i = 0; i < oprsz; i += sizeof(uint64_t)) { + *(uint64_t *)(d + i) = *(uint64_t *)(a + i) ^ b; } clear_high(d, oprsz, desc); } @@ -620,11 +564,10 @@ void HELPER(gvec_xors)(void *d, void *a, uint64_t b, uint32_t desc) void HELPER(gvec_ors)(void *d, void *a, uint64_t b, uint32_t desc) { intptr_t oprsz = simd_oprsz(desc); - vec64 vecb = (vec64)DUP2(b); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec64)) { - *(vec64 *)(d + i) = *(vec64 *)(a + i) | vecb; + for (i = 0; i < oprsz; i += sizeof(uint64_t)) { + *(uint64_t *)(d + i) = *(uint64_t *)(a + i) | b; } clear_high(d, oprsz, desc); } @@ -635,8 +578,8 @@ void HELPER(gvec_shl8i)(void *d, void *a, uint32_t desc) int shift = simd_data(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec8)) { - *(vec8 *)(d + i) = *(vec8 *)(a + i) << shift; + for (i = 0; i < oprsz; i += sizeof(uint8_t)) { + *(uint8_t *)(d + i) = *(uint8_t *)(a + i) << shift; } clear_high(d, oprsz, desc); } @@ -647,8 +590,8 @@ void HELPER(gvec_shl16i)(void *d, void *a, uint32_t desc) int shift = simd_data(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec16)) { - *(vec16 *)(d + i) = *(vec16 *)(a + i) << shift; + for (i = 0; i < oprsz; i += sizeof(uint16_t)) { + *(uint16_t *)(d + i) = *(uint16_t *)(a + i) << shift; } clear_high(d, oprsz, desc); } @@ -659,8 +602,8 @@ void HELPER(gvec_shl32i)(void *d, void *a, uint32_t desc) int shift = simd_data(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec32)) { - *(vec32 *)(d + i) = *(vec32 *)(a + i) << shift; + for (i = 0; i < oprsz; i += sizeof(uint32_t)) { + *(uint32_t *)(d + i) = *(uint32_t *)(a + i) << shift; } clear_high(d, oprsz, desc); } @@ -671,8 +614,8 @@ void HELPER(gvec_shl64i)(void *d, void *a, uint32_t desc) int shift = simd_data(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec64)) { - *(vec64 *)(d + i) = *(vec64 *)(a + i) << shift; + for (i = 0; i < oprsz; i += sizeof(uint64_t)) { + *(uint64_t *)(d + i) = *(uint64_t *)(a + i) << shift; } clear_high(d, oprsz, desc); } @@ -683,8 +626,8 @@ void HELPER(gvec_shr8i)(void *d, void *a, uint32_t desc) int shift = simd_data(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec8)) { - *(vec8 *)(d + i) = *(vec8 *)(a + i) >> shift; + for (i = 0; i < oprsz; i += sizeof(uint8_t)) { + *(uint8_t *)(d + i) = *(uint8_t *)(a + i) >> shift; } clear_high(d, oprsz, desc); } @@ -695,8 +638,8 @@ void HELPER(gvec_shr16i)(void *d, void *a, uint32_t desc) int shift = simd_data(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec16)) { - *(vec16 *)(d + i) = *(vec16 *)(a + i) >> shift; + for (i = 0; i < oprsz; i += sizeof(uint16_t)) { + *(uint16_t *)(d + i) = *(uint16_t *)(a + i) >> shift; } clear_high(d, oprsz, desc); } @@ -707,8 +650,8 @@ void HELPER(gvec_shr32i)(void *d, void *a, uint32_t desc) int shift = simd_data(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec32)) { - *(vec32 *)(d + i) = *(vec32 *)(a + i) >> shift; + for (i = 0; i < oprsz; i += sizeof(uint32_t)) { + *(uint32_t *)(d + i) = *(uint32_t *)(a + i) >> shift; } clear_high(d, oprsz, desc); } @@ -719,8 +662,8 @@ void HELPER(gvec_shr64i)(void *d, void *a, uint32_t desc) int shift = simd_data(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec64)) { - *(vec64 *)(d + i) = *(vec64 *)(a + i) >> shift; + for (i = 0; i < oprsz; i += sizeof(uint64_t)) { + *(uint64_t *)(d + i) = *(uint64_t *)(a + i) >> shift; } clear_high(d, oprsz, desc); } @@ -731,8 +674,8 @@ void HELPER(gvec_sar8i)(void *d, void *a, uint32_t desc) int shift = simd_data(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec8)) { - *(svec8 *)(d + i) = *(svec8 *)(a + i) >> shift; + for (i = 0; i < oprsz; i += sizeof(uint8_t)) { + *(int8_t *)(d + i) = *(int8_t *)(a + i) >> shift; } clear_high(d, oprsz, desc); } @@ -743,8 +686,8 @@ void HELPER(gvec_sar16i)(void *d, void *a, uint32_t desc) int shift = simd_data(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec16)) { - *(svec16 *)(d + i) = *(svec16 *)(a + i) >> shift; + for (i = 0; i < oprsz; i += sizeof(uint16_t)) { + *(int16_t *)(d + i) = *(int16_t *)(a + i) >> shift; } clear_high(d, oprsz, desc); } @@ -755,8 +698,8 @@ void HELPER(gvec_sar32i)(void *d, void *a, uint32_t desc) int shift = simd_data(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec32)) { - *(svec32 *)(d + i) = *(svec32 *)(a + i) >> shift; + for (i = 0; i < oprsz; i += sizeof(uint32_t)) { + *(int32_t *)(d + i) = *(int32_t *)(a + i) >> shift; } clear_high(d, oprsz, desc); } @@ -767,8 +710,8 @@ void HELPER(gvec_sar64i)(void *d, void *a, uint32_t desc) int shift = simd_data(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec64)) { - *(svec64 *)(d + i) = *(svec64 *)(a + i) >> shift; + for (i = 0; i < oprsz; i += sizeof(uint64_t)) { + *(int64_t *)(d + i) = *(int64_t *)(a + i) >> shift; } clear_high(d, oprsz, desc); } @@ -917,39 +860,30 @@ void HELPER(gvec_sar64v)(void *d, void *a, void *b, uint32_t desc) clear_high(d, oprsz, desc); } -/* If vectors are enabled, the compiler fills in -1 for true. - Otherwise, we must take care of this by hand. */ -#ifdef CONFIG_VECTOR16 -# define DO_CMP0(X) X -#else -# define DO_CMP0(X) -(X) -#endif - #define DO_CMP1(NAME, TYPE, OP) \ void HELPER(NAME)(void *d, void *a, void *b, uint32_t desc) \ { \ intptr_t oprsz = simd_oprsz(desc); \ intptr_t i; \ for (i = 0; i < oprsz; i += sizeof(TYPE)) { \ - *(TYPE *)(d + i) = DO_CMP0(*(TYPE *)(a + i) OP *(TYPE *)(b + i)); \ + *(TYPE *)(d + i) = -(*(TYPE *)(a + i) OP *(TYPE *)(b + i)); \ } \ clear_high(d, oprsz, desc); \ } #define DO_CMP2(SZ) \ - DO_CMP1(gvec_eq##SZ, vec##SZ, ==) \ - DO_CMP1(gvec_ne##SZ, vec##SZ, !=) \ - DO_CMP1(gvec_lt##SZ, svec##SZ, <) \ - DO_CMP1(gvec_le##SZ, svec##SZ, <=) \ - DO_CMP1(gvec_ltu##SZ, vec##SZ, <) \ - DO_CMP1(gvec_leu##SZ, vec##SZ, <=) + DO_CMP1(gvec_eq##SZ, uint##SZ##_t, ==) \ + DO_CMP1(gvec_ne##SZ, uint##SZ##_t, !=) \ + DO_CMP1(gvec_lt##SZ, int##SZ##_t, <) \ + DO_CMP1(gvec_le##SZ, int##SZ##_t, <=) \ + DO_CMP1(gvec_ltu##SZ, uint##SZ##_t, <) \ + DO_CMP1(gvec_leu##SZ, uint##SZ##_t, <=) DO_CMP2(8) DO_CMP2(16) DO_CMP2(32) DO_CMP2(64) -#undef DO_CMP0 #undef DO_CMP1 #undef DO_CMP2 @@ -1450,11 +1384,11 @@ void HELPER(gvec_bitsel)(void *d, void *a, void *b, void *c, uint32_t desc) intptr_t oprsz = simd_oprsz(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec64)) { - vec64 aa = *(vec64 *)(a + i); - vec64 bb = *(vec64 *)(b + i); - vec64 cc = *(vec64 *)(c + i); - *(vec64 *)(d + i) = (bb & aa) | (cc & ~aa); + for (i = 0; i < oprsz; i += sizeof(uint64_t)) { + uint64_t aa = *(uint64_t *)(a + i); + uint64_t bb = *(uint64_t *)(b + i); + uint64_t cc = *(uint64_t *)(c + i); + *(uint64_t *)(d + i) = (bb & aa) | (cc & ~aa); } clear_high(d, oprsz, desc); } diff --git a/accel/tcg/tcg-runtime.c b/accel/tcg/tcg-runtime.c index 8a1e408e31..446465a09a 100644 --- a/accel/tcg/tcg-runtime.c +++ b/accel/tcg/tcg-runtime.c @@ -30,6 +30,7 @@ #include "exec/tb-lookup.h" #include "disas/disas.h" #include "exec/log.h" +#include "tcg/tcg.h" /* 32-bit helpers */ diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index 9f48da9472..9924e66d1f 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -18,6 +18,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qemu-common.h" #define NO_CPU_IO_DEFS @@ -25,7 +26,7 @@ #include "trace.h" #include "disas/disas.h" #include "exec/exec-all.h" -#include "tcg.h" +#include "tcg/tcg.h" #if defined(CONFIG_USER_ONLY) #include "qemu.h" #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) @@ -891,43 +892,61 @@ static void page_lock_pair(PageDesc **ret_p1, tb_page_addr_t phys1, } } -#if defined(CONFIG_USER_ONLY) -/* Currently it is not recommended to allocate big chunks of data in - user mode. It will change when a dedicated libc will be used. */ -/* ??? 64-bit hosts ought to have no problem mmaping data outside the - region in which the guest needs to run. Revisit this. */ -#define USE_STATIC_CODE_GEN_BUFFER -#endif - /* Minimum size of the code gen buffer. This number is randomly chosen, but not so small that we can't have a fair number of TB's live. */ -#define MIN_CODE_GEN_BUFFER_SIZE (1024u * 1024) +#define MIN_CODE_GEN_BUFFER_SIZE (1 * MiB) /* Maximum size of the code gen buffer we'd like to use. Unless otherwise indicated, this is constrained by the range of direct branches on the host cpu, as used by the TCG implementation of goto_tb. */ #if defined(__x86_64__) -# define MAX_CODE_GEN_BUFFER_SIZE (2ul * 1024 * 1024 * 1024) +# define MAX_CODE_GEN_BUFFER_SIZE (2 * GiB) #elif defined(__sparc__) -# define MAX_CODE_GEN_BUFFER_SIZE (2ul * 1024 * 1024 * 1024) +# define MAX_CODE_GEN_BUFFER_SIZE (2 * GiB) #elif defined(__powerpc64__) -# define MAX_CODE_GEN_BUFFER_SIZE (2ul * 1024 * 1024 * 1024) +# define MAX_CODE_GEN_BUFFER_SIZE (2 * GiB) #elif defined(__powerpc__) -# define MAX_CODE_GEN_BUFFER_SIZE (32u * 1024 * 1024) +# define MAX_CODE_GEN_BUFFER_SIZE (32 * MiB) #elif defined(__aarch64__) -# define MAX_CODE_GEN_BUFFER_SIZE (2ul * 1024 * 1024 * 1024) +# define MAX_CODE_GEN_BUFFER_SIZE (2 * GiB) #elif defined(__s390x__) /* We have a +- 4GB range on the branches; leave some slop. */ -# define MAX_CODE_GEN_BUFFER_SIZE (3ul * 1024 * 1024 * 1024) +# define MAX_CODE_GEN_BUFFER_SIZE (3 * GiB) #elif defined(__mips__) /* We have a 256MB branch region, but leave room to make sure the main executable is also within that region. */ -# define MAX_CODE_GEN_BUFFER_SIZE (128ul * 1024 * 1024) +# define MAX_CODE_GEN_BUFFER_SIZE (128 * MiB) #else # define MAX_CODE_GEN_BUFFER_SIZE ((size_t)-1) #endif -#define DEFAULT_CODE_GEN_BUFFER_SIZE_1 (32u * 1024 * 1024) +#if TCG_TARGET_REG_BITS == 32 +#define DEFAULT_CODE_GEN_BUFFER_SIZE_1 (32 * MiB) +#ifdef CONFIG_USER_ONLY +/* + * For user mode on smaller 32 bit systems we may run into trouble + * allocating big chunks of data in the right place. On these systems + * we utilise a static code generation buffer directly in the binary. + */ +#define USE_STATIC_CODE_GEN_BUFFER +#endif +#else /* TCG_TARGET_REG_BITS == 64 */ +#ifdef CONFIG_USER_ONLY +/* + * As user-mode emulation typically means running multiple instances + * of the translator don't go too nuts with our default code gen + * buffer lest we make things too hard for the OS. + */ +#define DEFAULT_CODE_GEN_BUFFER_SIZE_1 (128 * MiB) +#else +/* + * We expect most system emulation to run one or two guests per host. + * Users running large scale system emulation may want to tweak their + * runtime setup via the tb-size control on the command line. + */ +#define DEFAULT_CODE_GEN_BUFFER_SIZE_1 (1 * GiB) +#endif +#endif #define DEFAULT_CODE_GEN_BUFFER_SIZE \ (DEFAULT_CODE_GEN_BUFFER_SIZE_1 < MAX_CODE_GEN_BUFFER_SIZE \ @@ -937,15 +956,7 @@ static inline size_t size_code_gen_buffer(size_t tb_size) { /* Size the buffer. */ if (tb_size == 0) { -#ifdef USE_STATIC_CODE_GEN_BUFFER tb_size = DEFAULT_CODE_GEN_BUFFER_SIZE; -#else - /* ??? Needs adjustments. */ - /* ??? If we relax the requirement that CONFIG_USER_ONLY use the - static buffer, we could size this on RESERVED_VA, on the text - segment size of the executable, or continue to use the default. */ - tb_size = (unsigned long)(ram_size / 4); -#endif } if (tb_size < MIN_CODE_GEN_BUFFER_SIZE) { tb_size = MIN_CODE_GEN_BUFFER_SIZE; @@ -1032,47 +1043,20 @@ static inline void *alloc_code_gen_buffer(void) { int prot = PROT_WRITE | PROT_READ | PROT_EXEC; int flags = MAP_PRIVATE | MAP_ANONYMOUS; - uintptr_t start = 0; size_t size = tcg_ctx->code_gen_buffer_size; void *buf; - /* Constrain the position of the buffer based on the host cpu. - Note that these addresses are chosen in concert with the - addresses assigned in the relevant linker script file. */ -# if defined(__PIE__) || defined(__PIC__) - /* Don't bother setting a preferred location if we're building - a position-independent executable. We're more likely to get - an address near the main executable if we let the kernel - choose the address. */ -# elif defined(__x86_64__) && defined(MAP_32BIT) - /* Force the memory down into low memory with the executable. - Leave the choice of exact location with the kernel. */ - flags |= MAP_32BIT; - /* Cannot expect to map more than 800MB in low memory. */ - if (size > 800u * 1024 * 1024) { - tcg_ctx->code_gen_buffer_size = size = 800u * 1024 * 1024; - } -# elif defined(__sparc__) - start = 0x40000000ul; -# elif defined(__s390x__) - start = 0x90000000ul; -# elif defined(__mips__) -# if _MIPS_SIM == _ABI64 - start = 0x128000000ul; -# else - start = 0x08000000ul; -# endif -# endif - - buf = mmap((void *)start, size, prot, flags, -1, 0); + buf = mmap(NULL, size, prot, flags, -1, 0); if (buf == MAP_FAILED) { return NULL; } #ifdef __mips__ if (cross_256mb(buf, size)) { - /* Try again, with the original still mapped, to avoid re-acquiring - that 256mb crossing. This time don't specify an address. */ + /* + * Try again, with the original still mapped, to avoid re-acquiring + * the same 256mb crossing. + */ size_t size2; void *buf2 = mmap(NULL, size, prot, flags, -1, 0); switch ((int)(buf2 != MAP_FAILED)) { @@ -1804,7 +1788,7 @@ TranslationBlock *tb_gen_code(CPUState *cpu, #ifdef DEBUG_DISAS if (qemu_loglevel_mask(CPU_LOG_TB_OUT_ASM) && qemu_log_in_addr_range(tb->pc)) { - qemu_log_lock(); + FILE *logfile = qemu_log_lock(); qemu_log("OUT: [size=%d]\n", gen_code_size); if (tcg_ctx->data_gen_ptr) { size_t code_size = tcg_ctx->data_gen_ptr - tb->tc.ptr; @@ -1829,7 +1813,7 @@ TranslationBlock *tb_gen_code(CPUState *cpu, } qemu_log("\n"); qemu_log_flush(); - qemu_log_unlock(); + qemu_log_unlock(logfile); } #endif diff --git a/accel/tcg/translator.c b/accel/tcg/translator.c index f977682be7..603d17ff83 100644 --- a/accel/tcg/translator.c +++ b/accel/tcg/translator.c @@ -138,11 +138,11 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db, #ifdef DEBUG_DISAS if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM) && qemu_log_in_addr_range(db->pc_first)) { - qemu_log_lock(); + FILE *logfile = qemu_log_lock(); qemu_log("----------------\n"); ops->disas_log(db, cpu); qemu_log("\n"); - qemu_log_unlock(); + qemu_log_unlock(logfile); } #endif } diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index b09f7a1577..4be78eb9b3 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -20,12 +20,14 @@ #include "cpu.h" #include "disas/disas.h" #include "exec/exec-all.h" -#include "tcg.h" +#include "tcg/tcg.h" #include "qemu/bitops.h" #include "exec/cpu_ldst.h" #include "translate-all.h" #include "exec/helper-proto.h" #include "qemu/atomic128.h" +#include "trace-root.h" +#include "trace/mem.h" #undef EAX #undef ECX @@ -734,6 +736,240 @@ int cpu_signal_handler(int host_signum, void *pinfo, /* The softmmu versions of these helpers are in cputlb.c. */ +uint32_t cpu_ldub_data(CPUArchState *env, abi_ptr ptr) +{ + uint32_t ret; + uint16_t meminfo = trace_mem_get_info(MO_UB, MMU_USER_IDX, false); + + trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo); + ret = ldub_p(g2h(ptr)); + qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo); + return ret; +} + +int cpu_ldsb_data(CPUArchState *env, abi_ptr ptr) +{ + int ret; + uint16_t meminfo = trace_mem_get_info(MO_SB, MMU_USER_IDX, false); + + trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo); + ret = ldsb_p(g2h(ptr)); + qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo); + return ret; +} + +uint32_t cpu_lduw_data(CPUArchState *env, abi_ptr ptr) +{ + uint32_t ret; + uint16_t meminfo = trace_mem_get_info(MO_TEUW, MMU_USER_IDX, false); + + trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo); + ret = lduw_p(g2h(ptr)); + qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo); + return ret; +} + +int cpu_ldsw_data(CPUArchState *env, abi_ptr ptr) +{ + int ret; + uint16_t meminfo = trace_mem_get_info(MO_TESW, MMU_USER_IDX, false); + + trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo); + ret = ldsw_p(g2h(ptr)); + qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo); + return ret; +} + +uint32_t cpu_ldl_data(CPUArchState *env, abi_ptr ptr) +{ + uint32_t ret; + uint16_t meminfo = trace_mem_get_info(MO_TEUL, MMU_USER_IDX, false); + + trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo); + ret = ldl_p(g2h(ptr)); + qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo); + return ret; +} + +uint64_t cpu_ldq_data(CPUArchState *env, abi_ptr ptr) +{ + uint64_t ret; + uint16_t meminfo = trace_mem_get_info(MO_TEQ, MMU_USER_IDX, false); + + trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo); + ret = ldq_p(g2h(ptr)); + qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo); + return ret; +} + +uint32_t cpu_ldub_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t retaddr) +{ + uint32_t ret; + + set_helper_retaddr(retaddr); + ret = cpu_ldub_data(env, ptr); + clear_helper_retaddr(); + return ret; +} + +int cpu_ldsb_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t retaddr) +{ + int ret; + + set_helper_retaddr(retaddr); + ret = cpu_ldsb_data(env, ptr); + clear_helper_retaddr(); + return ret; +} + +uint32_t cpu_lduw_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t retaddr) +{ + uint32_t ret; + + set_helper_retaddr(retaddr); + ret = cpu_lduw_data(env, ptr); + clear_helper_retaddr(); + return ret; +} + +int cpu_ldsw_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t retaddr) +{ + int ret; + + set_helper_retaddr(retaddr); + ret = cpu_ldsw_data(env, ptr); + clear_helper_retaddr(); + return ret; +} + +uint32_t cpu_ldl_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t retaddr) +{ + uint32_t ret; + + set_helper_retaddr(retaddr); + ret = cpu_ldl_data(env, ptr); + clear_helper_retaddr(); + return ret; +} + +uint64_t cpu_ldq_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t retaddr) +{ + uint64_t ret; + + set_helper_retaddr(retaddr); + ret = cpu_ldq_data(env, ptr); + clear_helper_retaddr(); + return ret; +} + +void cpu_stb_data(CPUArchState *env, abi_ptr ptr, uint32_t val) +{ + uint16_t meminfo = trace_mem_get_info(MO_UB, MMU_USER_IDX, true); + + trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo); + stb_p(g2h(ptr), val); + qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo); +} + +void cpu_stw_data(CPUArchState *env, abi_ptr ptr, uint32_t val) +{ + uint16_t meminfo = trace_mem_get_info(MO_TEUW, MMU_USER_IDX, true); + + trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo); + stw_p(g2h(ptr), val); + qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo); +} + +void cpu_stl_data(CPUArchState *env, abi_ptr ptr, uint32_t val) +{ + uint16_t meminfo = trace_mem_get_info(MO_TEUL, MMU_USER_IDX, true); + + trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo); + stl_p(g2h(ptr), val); + qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo); +} + +void cpu_stq_data(CPUArchState *env, abi_ptr ptr, uint64_t val) +{ + uint16_t meminfo = trace_mem_get_info(MO_TEQ, MMU_USER_IDX, true); + + trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo); + stq_p(g2h(ptr), val); + qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo); +} + +void cpu_stb_data_ra(CPUArchState *env, abi_ptr ptr, + uint32_t val, uintptr_t retaddr) +{ + set_helper_retaddr(retaddr); + cpu_stb_data(env, ptr, val); + clear_helper_retaddr(); +} + +void cpu_stw_data_ra(CPUArchState *env, abi_ptr ptr, + uint32_t val, uintptr_t retaddr) +{ + set_helper_retaddr(retaddr); + cpu_stw_data(env, ptr, val); + clear_helper_retaddr(); +} + +void cpu_stl_data_ra(CPUArchState *env, abi_ptr ptr, + uint32_t val, uintptr_t retaddr) +{ + set_helper_retaddr(retaddr); + cpu_stl_data(env, ptr, val); + clear_helper_retaddr(); +} + +void cpu_stq_data_ra(CPUArchState *env, abi_ptr ptr, + uint64_t val, uintptr_t retaddr) +{ + set_helper_retaddr(retaddr); + cpu_stq_data(env, ptr, val); + clear_helper_retaddr(); +} + +uint32_t cpu_ldub_code(CPUArchState *env, abi_ptr ptr) +{ + uint32_t ret; + + set_helper_retaddr(1); + ret = ldub_p(g2h(ptr)); + clear_helper_retaddr(); + return ret; +} + +uint32_t cpu_lduw_code(CPUArchState *env, abi_ptr ptr) +{ + uint32_t ret; + + set_helper_retaddr(1); + ret = lduw_p(g2h(ptr)); + clear_helper_retaddr(); + return ret; +} + +uint32_t cpu_ldl_code(CPUArchState *env, abi_ptr ptr) +{ + uint32_t ret; + + set_helper_retaddr(1); + ret = ldl_p(g2h(ptr)); + clear_helper_retaddr(); + return ret; +} + +uint64_t cpu_ldq_code(CPUArchState *env, abi_ptr ptr) +{ + uint64_t ret; + + set_helper_retaddr(1); + ret = ldq_p(g2h(ptr)); + clear_helper_retaddr(); + return ret; +} + /* Do not allow unaligned operations to proceed. Return the host address. */ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr, int size, uintptr_t retaddr) diff --git a/arch_init.c b/arch_init.c index 705d0b94ad..d9eb0ec1dd 100644 --- a/arch_init.c +++ b/arch_init.c @@ -77,6 +77,8 @@ int graphic_depth = 32; #define QEMU_ARCH QEMU_ARCH_PPC #elif defined(TARGET_RISCV) #define QEMU_ARCH QEMU_ARCH_RISCV +#elif defined(TARGET_RX) +#define QEMU_ARCH QEMU_ARCH_RX #elif defined(TARGET_S390X) #define QEMU_ARCH QEMU_ARCH_S390X #elif defined(TARGET_SH4) diff --git a/audio/alsaaudio.c b/audio/alsaaudio.c index f37ce1ce85..a8e62542f9 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; @@ -802,7 +819,7 @@ static size_t alsa_read(HWVoiceIn *hw, void *buf, size_t len) switch (nread) { case 0: trace_alsa_read_zero(len); - return pos;; + return pos; case -EPIPE: if (alsa_recover(alsa->handle)) { @@ -818,7 +835,7 @@ static size_t alsa_read(HWVoiceIn *hw, void *buf, size_t len) default: alsa_logerr(nread, "Failed to read %zu frames to %p\n", len, dst); - return pos;; + return pos; } } @@ -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 56fae55047..7a9e680355 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,32 +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; @@ -330,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; @@ -343,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 { @@ -769,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 @@ -878,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) @@ -1075,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); @@ -1096,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; } @@ -1402,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; } @@ -1412,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)) { @@ -1427,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); @@ -1439,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; @@ -1475,21 +1486,19 @@ 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); + void *src = hw->pcm_ops->get_buffer_in(hw, &size); - memcpy(dst, buf, copy_size); - hw->pcm_ops->put_buffer_in(hw, buf, copy_size); - return copy_size; + memcpy(buf, src, size); + hw->pcm_ops->put_buffer_in(hw, src, size); + + return size; } - static int audio_driver_init(AudioState *s, struct audio_driver *drv, bool msg, Audiodev *dev) { @@ -1738,7 +1747,7 @@ static AudioState *audio_init(Audiodev *dev, const char *name) if (dev->timer_period <= 0) { s->period_ticks = 1; } else { - s->period_ticks = dev->timer_period * SCALE_US; + s->period_ticks = dev->timer_period * (int64_t)SCALE_US; } e = qemu_add_vm_change_state_handler (audio_vm_change_state_handler, s); @@ -1836,11 +1845,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); @@ -2079,6 +2092,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..4cdf19ab67 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; @@ -277,7 +279,7 @@ static int dsound_get_status_out (LPDIRECTSOUNDBUFFER dsb, DWORD *statusp, return -1; } - if (*statusp & DSERR_BUFFERLOST) { + if (*statusp & DSBSTATUS_BUFFERLOST) { dsound_restore_out(dsb, s); return -1; } @@ -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,20 +521,31 @@ 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); + req_size = MIN(*size, MIN(req_size, hw->size_emul - hw->pos_emul)); + + if (req_size == 0) { + *size = 0; + return NULL; + } err = dsound_lock_in(dscb, &hw->info, hw->pos_emul, req_size, &ret, NULL, &act_size, NULL, false, ds->s); diff --git a/audio/mixeng.c b/audio/mixeng.c index 2f5ba71381..739a500449 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 CONV_NATURAL_FLOAT(x) (x) +#define CLIP_NATURAL_FLOAT(x) (x) +#else +static const float float_scale = UINT_MAX / 2.f; +#define CONV_NATURAL_FLOAT(x) ((x) * float_scale) + +#ifdef RECIPROCAL +static const float float_scale_reciprocal = 2.f / UINT_MAX; +#define CLIP_NATURAL_FLOAT(x) ((x) * float_scale_reciprocal) +#else +#define CLIP_NATURAL_FLOAT(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 = CONV_NATURAL_FLOAT(*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 = CONV_NATURAL_FLOAT(*in++); + dst->r = CONV_NATURAL_FLOAT(*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++ = CLIP_NATURAL_FLOAT(src->l + 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++ = CLIP_NATURAL_FLOAT(src->l); + *out++ = CLIP_NATURAL_FLOAT(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/mixeng_template.h b/audio/mixeng_template.h index 77cc89b9e8..bc8509e423 100644 --- a/audio/mixeng_template.h +++ b/audio/mixeng_template.h @@ -41,32 +41,31 @@ static inline mixeng_real glue (conv_, ET) (IN_T v) #ifdef RECIPROCAL #ifdef SIGNED - return nv * (1.f / (mixeng_real) (IN_MAX - IN_MIN)); + return nv * (2.f / ((mixeng_real)IN_MAX - IN_MIN)); #else - return (nv - HALF) * (1.f / (mixeng_real) IN_MAX); + return (nv - HALF) * (2.f / (mixeng_real)IN_MAX); #endif #else /* !RECIPROCAL */ #ifdef SIGNED - return nv / (mixeng_real) ((mixeng_real) IN_MAX - IN_MIN); + return nv / (((mixeng_real)IN_MAX - IN_MIN) / 2.f); #else - return (nv - HALF) / (mixeng_real) IN_MAX; + return (nv - HALF) / ((mixeng_real)IN_MAX / 2.f); #endif #endif } static inline IN_T glue (clip_, ET) (mixeng_real v) { - if (v >= 0.5) { + if (v >= 1.f) { return IN_MAX; - } - else if (v < -0.5) { + } else if (v < -1.f) { return IN_MIN; } #ifdef SIGNED - return ENDIAN_CONVERT ((IN_T) (v * ((mixeng_real) IN_MAX - IN_MIN))); + return ENDIAN_CONVERT((IN_T)(v * (((mixeng_real)IN_MAX - IN_MIN) / 2.f))); #else - return ENDIAN_CONVERT ((IN_T) ((v * IN_MAX) + HALF)); + return ENDIAN_CONVERT((IN_T)((v * ((mixeng_real)IN_MAX / 2.f)) + HALF)); #endif } @@ -84,10 +83,9 @@ static inline int64_t glue (conv_, ET) (IN_T v) static inline IN_T glue (clip_, ET) (int64_t v) { - if (v >= 0x7f000000) { + if (v >= 0x7fffffffLL) { return IN_MAX; - } - else if (v < -2147483648LL) { + } else if (v < -2147483648LL) { return IN_MIN; } 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 55a91f8980..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); @@ -156,34 +154,48 @@ static size_t qpa_read(HWVoiceIn *hw, void *data, size_t length) { PAVoiceIn *p = (PAVoiceIn *) hw; PAConnection *c = p->g->conn; - size_t l; - int r; + size_t total = 0; pa_threaded_mainloop_lock(c->mainloop); CHECK_DEAD_GOTO(c, p->stream, unlock_and_fail, "pa_threaded_mainloop_lock failed\n"); - - if (!p->read_length) { - r = pa_stream_peek(p->stream, &p->read_data, &p->read_length); - CHECK_SUCCESS_GOTO(c, r == 0, unlock_and_fail, - "pa_stream_peek failed\n"); + if (pa_stream_get_state(p->stream) != PA_STREAM_READY) { + /* wait for stream to become ready */ + goto unlock; } - l = MIN(p->read_length, length); - memcpy(data, p->read_data, l); + while (total < length) { + size_t l; + int r; - p->read_data += l; - p->read_length -= l; + if (!p->read_length) { + r = pa_stream_peek(p->stream, &p->read_data, &p->read_length); + CHECK_SUCCESS_GOTO(c, r == 0, unlock_and_fail, + "pa_stream_peek failed\n"); + if (!p->read_length) { + /* buffer is empty */ + break; + } + } - if (!p->read_length) { - r = pa_stream_drop(p->stream); - CHECK_SUCCESS_GOTO(c, r == 0, unlock_and_fail, - "pa_stream_drop failed\n"); + l = MIN(p->read_length, length - total); + memcpy((char *)data + total, p->read_data, l); + + p->read_data += l; + p->read_length -= l; + total += l; + + if (!p->read_length) { + r = pa_stream_drop(p->stream); + CHECK_SUCCESS_GOTO(c, r == 0, unlock_and_fail, + "pa_stream_drop failed\n"); + } } +unlock: pa_threaded_mainloop_unlock(c->mainloop); - return l; + return total; unlock_and_fail: pa_threaded_mainloop_unlock(c->mainloop); @@ -265,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; @@ -290,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; @@ -474,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); @@ -522,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); @@ -536,7 +557,6 @@ static void qpa_simple_disconnect(PAConnection *c, pa_stream *stream) { int err; - pa_threaded_mainloop_lock(c->mainloop); /* * wait until actually connects. workaround pa bug #247 * https://gitlab.freedesktop.org/pulseaudio/pulseaudio/issues/247 @@ -550,7 +570,6 @@ static void qpa_simple_disconnect(PAConnection *c, pa_stream *stream) dolog("Failed to disconnect! err=%d\n", err); } pa_stream_unref(stream); - pa_threaded_mainloop_unlock(c->mainloop); } static void qpa_fini_out (HWVoiceOut *hw) @@ -558,8 +577,12 @@ static void qpa_fini_out (HWVoiceOut *hw) PAVoiceOut *pa = (PAVoiceOut *) hw; if (pa->stream) { - qpa_simple_disconnect(pa->g->conn, pa->stream); + PAConnection *c = pa->g->conn; + + pa_threaded_mainloop_lock(c->mainloop); + qpa_simple_disconnect(c, pa->stream); pa->stream = NULL; + pa_threaded_mainloop_unlock(c->mainloop); } } @@ -568,8 +591,20 @@ static void qpa_fini_in (HWVoiceIn *hw) PAVoiceIn *pa = (PAVoiceIn *) hw; if (pa->stream) { - qpa_simple_disconnect(pa->g->conn, pa->stream); + PAConnection *c = pa->g->conn; + + pa_threaded_mainloop_lock(c->mainloop); + if (pa->read_length) { + int r = pa_stream_drop(pa->stream); + if (r) { + qpa_logerr(pa_context_errno(c->context), + "pa_stream_drop failed\n"); + } + pa->read_length = 0; + } + qpa_simple_disconnect(c, pa->stream); pa->stream = NULL; + pa_threaded_mainloop_unlock(c->mainloop); } } 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 } diff --git a/backends/Makefile.objs b/backends/Makefile.objs index f0691116e8..28a847cd57 100644 --- a/backends/Makefile.objs +++ b/backends/Makefile.objs @@ -17,3 +17,7 @@ endif common-obj-$(call land,$(CONFIG_VHOST_USER),$(CONFIG_VIRTIO)) += vhost-user.o common-obj-$(CONFIG_LINUX) += hostmem-memfd.o + +common-obj-$(CONFIG_GIO) += dbus-vmstate.o +dbus-vmstate.o-cflags = $(GIO_CFLAGS) +dbus-vmstate.o-libs = $(GIO_LIBS) diff --git a/backends/cryptodev-vhost-user.c b/backends/cryptodev-vhost-user.c index b344283940..6edada8e9e 100644 --- a/backends/cryptodev-vhost-user.c +++ b/backends/cryptodev-vhost-user.c @@ -152,7 +152,7 @@ cryptodev_vhost_claim_chardev(CryptoDevBackendVhostUser *s, return chr; } -static void cryptodev_vhost_user_event(void *opaque, int event) +static void cryptodev_vhost_user_event(void *opaque, QEMUChrEvent event) { CryptoDevBackendVhostUser *s = opaque; CryptoDevBackend *b = CRYPTODEV_BACKEND(s); @@ -171,6 +171,11 @@ static void cryptodev_vhost_user_event(void *opaque, int event) b->ready = false; cryptodev_vhost_user_stop(queues, s); break; + case CHR_EVENT_BREAK: + case CHR_EVENT_MUX_IN: + case CHR_EVENT_MUX_OUT: + /* Ignore */ + break; } } diff --git a/backends/cryptodev.c b/backends/cryptodev.c index 3c071eab95..5a9735684e 100644 --- a/backends/cryptodev.c +++ b/backends/cryptodev.c @@ -176,19 +176,10 @@ cryptodev_backend_complete(UserCreatable *uc, Error **errp) { CryptoDevBackend *backend = CRYPTODEV_BACKEND(uc); CryptoDevBackendClass *bc = CRYPTODEV_BACKEND_GET_CLASS(uc); - Error *local_err = NULL; if (bc->init) { - bc->init(backend, &local_err); - if (local_err) { - goto out; - } + bc->init(backend, errp); } - - return; - -out: - error_propagate(errp, local_err); } void cryptodev_backend_set_used(CryptoDevBackend *backend, bool used) diff --git a/backends/dbus-vmstate.c b/backends/dbus-vmstate.c new file mode 100644 index 0000000000..cc594a722e --- /dev/null +++ b/backends/dbus-vmstate.c @@ -0,0 +1,511 @@ +/* + * QEMU dbus-vmstate + * + * Copyright (C) 2019 Red Hat Inc + * + * Authors: + * Marc-André Lureau + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "qemu/dbus.h" +#include "qemu/error-report.h" +#include "qapi/error.h" +#include "qom/object_interfaces.h" +#include "qapi/qmp/qerror.h" +#include "migration/vmstate.h" +#include "trace.h" + +typedef struct DBusVMState DBusVMState; +typedef struct DBusVMStateClass DBusVMStateClass; + +#define TYPE_DBUS_VMSTATE "dbus-vmstate" +#define DBUS_VMSTATE(obj) \ + OBJECT_CHECK(DBusVMState, (obj), TYPE_DBUS_VMSTATE) +#define DBUS_VMSTATE_GET_CLASS(obj) \ + OBJECT_GET_CLASS(DBusVMStateClass, (obj), TYPE_DBUS_VMSTATE) +#define DBUS_VMSTATE_CLASS(klass) \ + OBJECT_CLASS_CHECK(DBusVMStateClass, (klass), TYPE_DBUS_VMSTATE) + +struct DBusVMStateClass { + ObjectClass parent_class; +}; + +struct DBusVMState { + Object parent; + + GDBusConnection *bus; + char *dbus_addr; + char *id_list; + + uint32_t data_size; + uint8_t *data; +}; + +static const GDBusPropertyInfo vmstate_property_info[] = { + { -1, (char *) "Id", (char *) "s", + G_DBUS_PROPERTY_INFO_FLAGS_READABLE, NULL }, +}; + +static const GDBusPropertyInfo * const vmstate_property_info_pointers[] = { + &vmstate_property_info[0], + NULL +}; + +static const GDBusInterfaceInfo vmstate1_interface_info = { + -1, + (char *) "org.qemu.VMState1", + (GDBusMethodInfo **) NULL, + (GDBusSignalInfo **) NULL, + (GDBusPropertyInfo **) &vmstate_property_info_pointers, + NULL, +}; + +#define DBUS_VMSTATE_SIZE_LIMIT (1 * MiB) + +static GHashTable * +get_id_list_set(DBusVMState *self) +{ + g_auto(GStrv) ids = NULL; + g_autoptr(GHashTable) set = NULL; + int i; + + if (!self->id_list) { + return NULL; + } + + ids = g_strsplit(self->id_list, ",", -1); + set = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); + for (i = 0; ids[i]; i++) { + g_hash_table_add(set, ids[i]); + ids[i] = NULL; + } + + return g_steal_pointer(&set); +} + +static GHashTable * +dbus_get_proxies(DBusVMState *self, GError **err) +{ + g_autoptr(GHashTable) proxies = NULL; + g_autoptr(GHashTable) ids = NULL; + g_auto(GStrv) names = NULL; + Error *error = NULL; + size_t i; + + ids = get_id_list_set(self); + proxies = g_hash_table_new_full(g_str_hash, g_str_equal, + g_free, g_object_unref); + + names = qemu_dbus_get_queued_owners(self->bus, "org.qemu.VMState1", &error); + if (!names) { + g_set_error(err, G_IO_ERROR, G_IO_ERROR_FAILED, "%s", + error_get_pretty(error)); + error_free(error); + return NULL; + } + + for (i = 0; names[i]; i++) { + g_autoptr(GDBusProxy) proxy = NULL; + g_autoptr(GVariant) result = NULL; + g_autofree char *id = NULL; + size_t size; + + proxy = g_dbus_proxy_new_sync(self->bus, G_DBUS_PROXY_FLAGS_NONE, + (GDBusInterfaceInfo *) &vmstate1_interface_info, + names[i], + "/org/qemu/VMState1", + "org.qemu.VMState1", + NULL, err); + if (!proxy) { + return NULL; + } + + result = g_dbus_proxy_get_cached_property(proxy, "Id"); + if (!result) { + g_set_error_literal(err, G_IO_ERROR, G_IO_ERROR_FAILED, + "VMState Id property is missing."); + return NULL; + } + + id = g_variant_dup_string(result, &size); + if (ids && !g_hash_table_remove(ids, id)) { + g_clear_pointer(&id, g_free); + g_clear_object(&proxy); + continue; + } + if (size == 0 || size >= 256) { + g_set_error(err, G_IO_ERROR, G_IO_ERROR_FAILED, + "VMState Id '%s' is invalid.", id); + return NULL; + } + + if (!g_hash_table_insert(proxies, id, proxy)) { + g_set_error(err, G_IO_ERROR, G_IO_ERROR_FAILED, + "Duplicated VMState Id '%s'", id); + return NULL; + } + id = NULL; + proxy = NULL; + + g_clear_pointer(&result, g_variant_unref); + } + + if (ids) { + g_autofree char **left = NULL; + + left = (char **)g_hash_table_get_keys_as_array(ids, NULL); + if (*left) { + g_autofree char *leftids = g_strjoinv(",", left); + g_set_error(err, G_IO_ERROR, G_IO_ERROR_FAILED, + "Required VMState Id are missing: %s", leftids); + return NULL; + } + } + + return g_steal_pointer(&proxies); +} + +static int +dbus_load_state_proxy(GDBusProxy *proxy, const uint8_t *data, size_t size) +{ + g_autoptr(GError) err = NULL; + g_autoptr(GVariant) result = NULL; + g_autoptr(GVariant) value = NULL; + + value = g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE, + data, size, sizeof(char)); + result = g_dbus_proxy_call_sync(proxy, "Load", + g_variant_new("(@ay)", + g_steal_pointer(&value)), + G_DBUS_CALL_FLAGS_NO_AUTO_START, + -1, NULL, &err); + if (!result) { + error_report("%s: Failed to Load: %s", __func__, err->message); + return -1; + } + + return 0; +} + +static int dbus_vmstate_post_load(void *opaque, int version_id) +{ + DBusVMState *self = DBUS_VMSTATE(opaque); + g_autoptr(GInputStream) m = NULL; + g_autoptr(GDataInputStream) s = NULL; + g_autoptr(GError) err = NULL; + g_autoptr(GHashTable) proxies = NULL; + uint32_t nelem; + + trace_dbus_vmstate_post_load(version_id); + + proxies = dbus_get_proxies(self, &err); + if (!proxies) { + error_report("%s: Failed to get proxies: %s", __func__, err->message); + return -1; + } + + m = g_memory_input_stream_new_from_data(self->data, self->data_size, NULL); + s = g_data_input_stream_new(m); + g_data_input_stream_set_byte_order(s, G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN); + + nelem = g_data_input_stream_read_uint32(s, NULL, &err); + if (err) { + goto error; + } + + while (nelem > 0) { + GDBusProxy *proxy = NULL; + uint32_t len; + gsize bytes_read, avail; + char id[256]; + + len = g_data_input_stream_read_uint32(s, NULL, &err); + if (err) { + goto error; + } + if (len >= 256) { + error_report("%s: Invalid DBus vmstate proxy name %u", + __func__, len); + return -1; + } + if (!g_input_stream_read_all(G_INPUT_STREAM(s), id, len, + &bytes_read, NULL, &err)) { + goto error; + } + g_return_val_if_fail(bytes_read == len, -1); + id[len] = 0; + + trace_dbus_vmstate_loading(id); + + proxy = g_hash_table_lookup(proxies, id); + if (!proxy) { + error_report("%s: Failed to find proxy Id '%s'", __func__, id); + return -1; + } + + len = g_data_input_stream_read_uint32(s, NULL, &err); + avail = g_buffered_input_stream_get_available( + G_BUFFERED_INPUT_STREAM(s)); + + if (len > DBUS_VMSTATE_SIZE_LIMIT || len > avail) { + error_report("%s: Invalid vmstate size: %u", __func__, len); + return -1; + } + + if (dbus_load_state_proxy(proxy, + g_buffered_input_stream_peek_buffer(G_BUFFERED_INPUT_STREAM(s), + NULL), + len) < 0) { + error_report("%s: Failed to restore Id '%s'", __func__, id); + return -1; + } + + if (!g_seekable_seek(G_SEEKABLE(s), len, G_SEEK_CUR, NULL, &err)) { + goto error; + } + + nelem -= 1; + } + + return 0; + +error: + error_report("%s: Failed to read from stream: %s", __func__, err->message); + return -1; +} + +static void +dbus_save_state_proxy(gpointer key, + gpointer value, + gpointer user_data) +{ + GDataOutputStream *s = user_data; + const char *id = key; + GDBusProxy *proxy = value; + g_autoptr(GVariant) result = NULL; + g_autoptr(GVariant) child = NULL; + g_autoptr(GError) err = NULL; + const uint8_t *data; + gsize size; + + trace_dbus_vmstate_saving(id); + + result = g_dbus_proxy_call_sync(proxy, "Save", + NULL, G_DBUS_CALL_FLAGS_NO_AUTO_START, + -1, NULL, &err); + if (!result) { + error_report("%s: Failed to Save: %s", __func__, err->message); + return; + } + + child = g_variant_get_child_value(result, 0); + data = g_variant_get_fixed_array(child, &size, sizeof(char)); + if (!data) { + error_report("%s: Failed to Save: not a byte array", __func__); + return; + } + if (size > DBUS_VMSTATE_SIZE_LIMIT) { + error_report("%s: Too large vmstate data to save: %zu", + __func__, (size_t)size); + return; + } + + if (!g_data_output_stream_put_uint32(s, strlen(id), NULL, &err) || + !g_data_output_stream_put_string(s, id, NULL, &err) || + !g_data_output_stream_put_uint32(s, size, NULL, &err) || + !g_output_stream_write_all(G_OUTPUT_STREAM(s), + data, size, NULL, NULL, &err)) { + error_report("%s: Failed to write to stream: %s", + __func__, err->message); + } +} + +static int dbus_vmstate_pre_save(void *opaque) +{ + DBusVMState *self = DBUS_VMSTATE(opaque); + g_autoptr(GOutputStream) m = NULL; + g_autoptr(GDataOutputStream) s = NULL; + g_autoptr(GHashTable) proxies = NULL; + g_autoptr(GError) err = NULL; + + trace_dbus_vmstate_pre_save(); + + proxies = dbus_get_proxies(self, &err); + if (!proxies) { + error_report("%s: Failed to get proxies: %s", __func__, err->message); + return -1; + } + + m = g_memory_output_stream_new_resizable(); + s = g_data_output_stream_new(m); + g_data_output_stream_set_byte_order(s, G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN); + + if (!g_data_output_stream_put_uint32(s, g_hash_table_size(proxies), + NULL, &err)) { + error_report("%s: Failed to write to stream: %s", + __func__, err->message); + return -1; + } + + g_hash_table_foreach(proxies, dbus_save_state_proxy, s); + + if (g_memory_output_stream_get_size(G_MEMORY_OUTPUT_STREAM(m)) + > UINT32_MAX) { + error_report("%s: DBus vmstate buffer is too large", __func__); + return -1; + } + + if (!g_output_stream_close(G_OUTPUT_STREAM(m), NULL, &err)) { + error_report("%s: Failed to close stream: %s", __func__, err->message); + return -1; + } + + g_free(self->data); + self->data_size = + g_memory_output_stream_get_size(G_MEMORY_OUTPUT_STREAM(m)); + self->data = + g_memory_output_stream_steal_data(G_MEMORY_OUTPUT_STREAM(m)); + + return 0; +} + +static const VMStateDescription dbus_vmstate = { + .name = TYPE_DBUS_VMSTATE, + .version_id = 0, + .pre_save = dbus_vmstate_pre_save, + .post_load = dbus_vmstate_post_load, + .fields = (VMStateField[]) { + VMSTATE_UINT32(data_size, DBusVMState), + VMSTATE_VBUFFER_ALLOC_UINT32(data, DBusVMState, 0, 0, data_size), + VMSTATE_END_OF_LIST() + } +}; + +static void +dbus_vmstate_complete(UserCreatable *uc, Error **errp) +{ + DBusVMState *self = DBUS_VMSTATE(uc); + g_autoptr(GError) err = NULL; + + if (!object_resolve_path_type("", TYPE_DBUS_VMSTATE, NULL)) { + error_setg(errp, "There is already an instance of %s", + TYPE_DBUS_VMSTATE); + return; + } + + if (!self->dbus_addr) { + error_setg(errp, QERR_MISSING_PARAMETER, "addr"); + return; + } + + self->bus = g_dbus_connection_new_for_address_sync(self->dbus_addr, + G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT | + G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION, + NULL, NULL, &err); + if (err) { + error_setg(errp, "failed to connect to DBus: '%s'", err->message); + return; + } + + if (vmstate_register(VMSTATE_IF(self), VMSTATE_INSTANCE_ID_ANY, + &dbus_vmstate, self) < 0) { + error_setg(errp, "Failed to register vmstate"); + } +} + +static void +dbus_vmstate_finalize(Object *o) +{ + DBusVMState *self = DBUS_VMSTATE(o); + + vmstate_unregister(VMSTATE_IF(self), &dbus_vmstate, self); + + g_clear_object(&self->bus); + g_free(self->dbus_addr); + g_free(self->id_list); + g_free(self->data); +} + +static char * +get_dbus_addr(Object *o, Error **errp) +{ + DBusVMState *self = DBUS_VMSTATE(o); + + return g_strdup(self->dbus_addr); +} + +static void +set_dbus_addr(Object *o, const char *str, Error **errp) +{ + DBusVMState *self = DBUS_VMSTATE(o); + + g_free(self->dbus_addr); + self->dbus_addr = g_strdup(str); +} + +static char * +get_id_list(Object *o, Error **errp) +{ + DBusVMState *self = DBUS_VMSTATE(o); + + return g_strdup(self->id_list); +} + +static void +set_id_list(Object *o, const char *str, Error **errp) +{ + DBusVMState *self = DBUS_VMSTATE(o); + + g_free(self->id_list); + self->id_list = g_strdup(str); +} + +static char * +dbus_vmstate_get_id(VMStateIf *vmif) +{ + return g_strdup(TYPE_DBUS_VMSTATE); +} + +static void +dbus_vmstate_class_init(ObjectClass *oc, void *data) +{ + UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); + VMStateIfClass *vc = VMSTATE_IF_CLASS(oc); + + ucc->complete = dbus_vmstate_complete; + vc->get_id = dbus_vmstate_get_id; + + object_class_property_add_str(oc, "addr", + get_dbus_addr, set_dbus_addr, + &error_abort); + object_class_property_add_str(oc, "id-list", + get_id_list, set_id_list, + &error_abort); +} + +static const TypeInfo dbus_vmstate_info = { + .name = TYPE_DBUS_VMSTATE, + .parent = TYPE_OBJECT, + .instance_size = sizeof(DBusVMState), + .instance_finalize = dbus_vmstate_finalize, + .class_size = sizeof(DBusVMStateClass), + .class_init = dbus_vmstate_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_USER_CREATABLE }, + { TYPE_VMSTATE_IF }, + { } + } +}; + +static void +register_types(void) +{ + type_register_static(&dbus_vmstate_info); +} + +type_init(register_types); diff --git a/backends/hostmem-file.c b/backends/hostmem-file.c index be64020746..c8c355f5aa 100644 --- a/backends/hostmem-file.c +++ b/backends/hostmem-file.c @@ -18,13 +18,6 @@ #include "sysemu/sysemu.h" #include "qom/object_interfaces.h" -/* hostmem-file.c */ -/** - * @TYPE_MEMORY_BACKEND_FILE: - * name of backend that uses mmap on a file descriptor - */ -#define TYPE_MEMORY_BACKEND_FILE "memory-backend-file" - #define MEMORY_BACKEND_FILE(obj) \ OBJECT_CHECK(HostMemoryBackendFile, (obj), TYPE_MEMORY_BACKEND_FILE) @@ -58,7 +51,6 @@ file_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) return; } - backend->force_prealloc = mem_prealloc; name = host_memory_backend_get_name(backend); memory_region_init_ram_from_file(&backend->mr, OBJECT(backend), name, diff --git a/backends/hostmem-memfd.c b/backends/hostmem-memfd.c index 26070b425e..74ba9879c4 100644 --- a/backends/hostmem-memfd.c +++ b/backends/hostmem-memfd.c @@ -45,7 +45,6 @@ memfd_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) return; } - backend->force_prealloc = mem_prealloc; fd = qemu_memfd_create(TYPE_MEMORY_BACKEND_MEMFD, backend->size, m->hugetlb, m->hugetlbsize, m->seal ? F_SEAL_GROW | F_SEAL_SHRINK | F_SEAL_SEAL : 0, diff --git a/backends/hostmem-ram.c b/backends/hostmem-ram.c index 6aab8d3a73..5cc53e76c9 100644 --- a/backends/hostmem-ram.c +++ b/backends/hostmem-ram.c @@ -16,8 +16,6 @@ #include "qemu/module.h" #include "qom/object_interfaces.h" -#define TYPE_MEMORY_BACKEND_RAM "memory-backend-ram" - static void ram_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) { diff --git a/backends/hostmem.c b/backends/hostmem.c index e773bdfa6e..327f9eebc3 100644 --- a/backends/hostmem.c +++ b/backends/hostmem.c @@ -215,7 +215,7 @@ static bool host_memory_backend_get_prealloc(Object *obj, Error **errp) { HostMemoryBackend *backend = MEMORY_BACKEND(obj); - return backend->prealloc || backend->force_prealloc; + return backend->prealloc; } static void host_memory_backend_set_prealloc(Object *obj, bool value, @@ -223,15 +223,6 @@ static void host_memory_backend_set_prealloc(Object *obj, bool value, { Error *local_err = NULL; HostMemoryBackend *backend = MEMORY_BACKEND(obj); - MachineState *ms = MACHINE(qdev_get_machine()); - - if (backend->force_prealloc) { - if (value) { - error_setg(errp, - "remove -mem-prealloc to use the prealloc property"); - return; - } - } if (!host_memory_backend_mr_inited(backend)) { backend->prealloc = value; @@ -243,7 +234,7 @@ static void host_memory_backend_set_prealloc(Object *obj, bool value, void *ptr = memory_region_get_ram_ptr(&backend->mr); uint64_t sz = memory_region_size(&backend->mr); - os_mem_prealloc(fd, ptr, sz, ms->smp.cpus, &local_err); + os_mem_prealloc(fd, ptr, sz, backend->prealloc_threads, &local_err); if (local_err) { error_propagate(errp, local_err); return; @@ -252,14 +243,44 @@ static void host_memory_backend_set_prealloc(Object *obj, bool value, } } +static void host_memory_backend_get_prealloc_threads(Object *obj, Visitor *v, + const char *name, void *opaque, Error **errp) +{ + HostMemoryBackend *backend = MEMORY_BACKEND(obj); + visit_type_uint32(v, name, &backend->prealloc_threads, errp); +} + +static void host_memory_backend_set_prealloc_threads(Object *obj, Visitor *v, + const char *name, void *opaque, Error **errp) +{ + HostMemoryBackend *backend = MEMORY_BACKEND(obj); + Error *local_err = NULL; + uint32_t value; + + visit_type_uint32(v, name, &value, &local_err); + if (local_err) { + goto out; + } + if (value <= 0) { + error_setg(&local_err, + "property '%s' of %s doesn't take value '%d'", + name, object_get_typename(obj), value); + goto out; + } + backend->prealloc_threads = value; +out: + error_propagate(errp, local_err); +} + static void host_memory_backend_init(Object *obj) { HostMemoryBackend *backend = MEMORY_BACKEND(obj); MachineState *machine = MACHINE(qdev_get_machine()); + /* TODO: convert access to globals to compat properties */ backend->merge = machine_mem_merge(machine); backend->dump = machine_dump_guest_core(machine); - backend->prealloc = mem_prealloc; + backend->prealloc_threads = 1; } static void host_memory_backend_post_init(Object *obj) @@ -313,7 +334,6 @@ host_memory_backend_memory_complete(UserCreatable *uc, Error **errp) { HostMemoryBackend *backend = MEMORY_BACKEND(uc); HostMemoryBackendClass *bc = MEMORY_BACKEND_GET_CLASS(uc); - MachineState *ms = MACHINE(qdev_get_machine()); Error *local_err = NULL; void *ptr; uint64_t sz; @@ -378,7 +398,7 @@ host_memory_backend_memory_complete(UserCreatable *uc, Error **errp) */ if (backend->prealloc) { os_mem_prealloc(memory_region_get_fd(&backend->mr), ptr, sz, - ms->smp.cpus, &local_err); + backend->prealloc_threads, &local_err); if (local_err) { goto out; } @@ -456,6 +476,12 @@ host_memory_backend_class_init(ObjectClass *oc, void *data) host_memory_backend_set_prealloc, &error_abort); object_class_property_set_description(oc, "prealloc", "Preallocate memory", &error_abort); + object_class_property_add(oc, "prealloc-threads", "int", + host_memory_backend_get_prealloc_threads, + host_memory_backend_set_prealloc_threads, + NULL, NULL, &error_abort); + object_class_property_set_description(oc, "prealloc-threads", + "Number of CPU threads to use for prealloc", &error_abort); object_class_property_add(oc, "size", "int", host_memory_backend_get_size, host_memory_backend_set_size, diff --git a/backends/trace-events b/backends/trace-events new file mode 100644 index 0000000000..59058f7630 --- /dev/null +++ b/backends/trace-events @@ -0,0 +1,7 @@ +# See docs/devel/tracing.txt for syntax documentation. + +# dbus-vmstate.c +dbus_vmstate_pre_save(void) +dbus_vmstate_post_load(int version_id) "version_id: %d" +dbus_vmstate_loading(const char *id) "id: %s" +dbus_vmstate_saving(const char *id) "id: %s" diff --git a/block.c b/block.c index 473eb6eeaa..2e3905c99e 100644 --- a/block.c +++ b/block.c @@ -363,6 +363,7 @@ char *bdrv_get_full_backing_filename(BlockDriverState *bs, Error **errp) void bdrv_register(BlockDriver *bdrv) { + assert(bdrv->format_name); QLIST_INSERT_HEAD(&bdrv_drivers, bdrv, list); } @@ -482,7 +483,8 @@ static void coroutine_fn bdrv_create_co_entry(void *opaque) CreateCo *cco = opaque; assert(cco->drv); - ret = cco->drv->bdrv_co_create_opts(cco->filename, cco->opts, &local_err); + ret = cco->drv->bdrv_co_create_opts(cco->drv, + cco->filename, cco->opts, &local_err); error_propagate(&cco->err, local_err); cco->ret = ret; } @@ -532,19 +534,168 @@ out: return ret; } +/** + * Helper function for bdrv_create_file_fallback(): Resize @blk to at + * least the given @minimum_size. + * + * On success, return @blk's actual length. + * Otherwise, return -errno. + */ +static int64_t create_file_fallback_truncate(BlockBackend *blk, + int64_t minimum_size, Error **errp) +{ + Error *local_err = NULL; + int64_t size; + int ret; + + ret = blk_truncate(blk, minimum_size, false, PREALLOC_MODE_OFF, &local_err); + if (ret < 0 && ret != -ENOTSUP) { + error_propagate(errp, local_err); + return ret; + } + + size = blk_getlength(blk); + if (size < 0) { + error_free(local_err); + error_setg_errno(errp, -size, + "Failed to inquire the new image file's length"); + return size; + } + + if (size < minimum_size) { + /* Need to grow the image, but we failed to do that */ + error_propagate(errp, local_err); + return -ENOTSUP; + } + + error_free(local_err); + local_err = NULL; + + return size; +} + +/** + * Helper function for bdrv_create_file_fallback(): Zero the first + * sector to remove any potentially pre-existing image header. + */ +static int create_file_fallback_zero_first_sector(BlockBackend *blk, + int64_t current_size, + Error **errp) +{ + int64_t bytes_to_clear; + int ret; + + bytes_to_clear = MIN(current_size, BDRV_SECTOR_SIZE); + if (bytes_to_clear) { + ret = blk_pwrite_zeroes(blk, 0, bytes_to_clear, BDRV_REQ_MAY_UNMAP); + if (ret < 0) { + error_setg_errno(errp, -ret, + "Failed to clear the new image's first sector"); + return ret; + } + } + + return 0; +} + +/** + * Simple implementation of bdrv_co_create_opts for protocol drivers + * which only support creation via opening a file + * (usually existing raw storage device) + */ +int coroutine_fn bdrv_co_create_opts_simple(BlockDriver *drv, + const char *filename, + QemuOpts *opts, + Error **errp) +{ + BlockBackend *blk; + QDict *options; + int64_t size = 0; + char *buf = NULL; + PreallocMode prealloc; + Error *local_err = NULL; + int ret; + + size = qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0); + buf = qemu_opt_get_del(opts, BLOCK_OPT_PREALLOC); + prealloc = qapi_enum_parse(&PreallocMode_lookup, buf, + PREALLOC_MODE_OFF, &local_err); + g_free(buf); + if (local_err) { + error_propagate(errp, local_err); + return -EINVAL; + } + + if (prealloc != PREALLOC_MODE_OFF) { + error_setg(errp, "Unsupported preallocation mode '%s'", + PreallocMode_str(prealloc)); + return -ENOTSUP; + } + + options = qdict_new(); + qdict_put_str(options, "driver", drv->format_name); + + blk = blk_new_open(filename, NULL, options, + BDRV_O_RDWR | BDRV_O_RESIZE, errp); + if (!blk) { + error_prepend(errp, "Protocol driver '%s' does not support image " + "creation, and opening the image failed: ", + drv->format_name); + return -EINVAL; + } + + size = create_file_fallback_truncate(blk, size, errp); + if (size < 0) { + ret = size; + goto out; + } + + ret = create_file_fallback_zero_first_sector(blk, size, errp); + if (ret < 0) { + goto out; + } + + ret = 0; +out: + blk_unref(blk); + return ret; +} + int bdrv_create_file(const char *filename, QemuOpts *opts, Error **errp) { BlockDriver *drv; - Error *local_err = NULL; - int ret; drv = bdrv_find_protocol(filename, true, errp); if (drv == NULL) { return -ENOENT; } - ret = bdrv_create(drv, filename, opts, &local_err); - error_propagate(errp, local_err); + return bdrv_create(drv, filename, opts, errp); +} + +int coroutine_fn bdrv_co_delete_file(BlockDriverState *bs, Error **errp) +{ + Error *local_err = NULL; + int ret; + + assert(bs != NULL); + + if (!bs->drv) { + error_setg(errp, "Block node '%s' is not opened", bs->filename); + return -ENOMEDIUM; + } + + if (!bs->drv->bdrv_co_delete_file) { + error_setg(errp, "Driver '%s' does not support image deletion", + bs->drv->format_name); + return -ENOTSUP; + } + + ret = bs->drv->bdrv_co_delete_file(bs, &local_err); + if (ret < 0) { + error_propagate(errp, local_err); + } + return ret; } @@ -845,6 +996,28 @@ static BlockdevDetectZeroesOptions bdrv_parse_detect_zeroes(QemuOpts *opts, return detect_zeroes; } +/** + * Set open flags for aio engine + * + * Return 0 on success, -1 if the engine specified is invalid + */ +int bdrv_parse_aio(const char *mode, int *flags) +{ + if (!strcmp(mode, "threads")) { + /* do nothing, default */ + } else if (!strcmp(mode, "native")) { + *flags |= BDRV_O_NATIVE_AIO; +#ifdef CONFIG_LINUX_IO_URING + } else if (!strcmp(mode, "io_uring")) { + *flags |= BDRV_O_IO_URING; +#endif + } else { + return -1; + } + + return 0; +} + /** * Set open flags for a given discard mode * @@ -1422,6 +1595,24 @@ QemuOptsList bdrv_runtime_opts = { }, }; +QemuOptsList bdrv_create_opts_simple = { + .name = "simple-create-opts", + .head = QTAILQ_HEAD_INITIALIZER(bdrv_create_opts_simple.head), + .desc = { + { + .name = BLOCK_OPT_SIZE, + .type = QEMU_OPT_SIZE, + .help = "Virtual disk size" + }, + { + .name = BLOCK_OPT_PREALLOC, + .type = QEMU_OPT_STRING, + .help = "Preallocation mode (allowed values: off)" + }, + { /* end of list */ } + } +}; + /* * Common part for opening disk images and files * @@ -1712,8 +1903,6 @@ static int bdrv_child_check_perm(BdrvChild *c, BlockReopenQueue *q, bool *tighten_restrictions, Error **errp); static void bdrv_child_abort_perm_update(BdrvChild *c); static void bdrv_child_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared); -static void bdrv_get_cumulative_perm(BlockDriverState *bs, uint64_t *perm, - uint64_t *shared_perm); typedef struct BlockReopenQueueEntry { bool prepared; @@ -1937,8 +2126,8 @@ static void bdrv_set_perm(BlockDriverState *bs, uint64_t cumulative_perms, } } -static void bdrv_get_cumulative_perm(BlockDriverState *bs, uint64_t *perm, - uint64_t *shared_perm) +void bdrv_get_cumulative_perm(BlockDriverState *bs, uint64_t *perm, + uint64_t *shared_perm) { BdrvChild *c; uint64_t cumulative_perms = 0; @@ -1976,18 +2165,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); } /* @@ -2227,6 +2417,24 @@ void bdrv_format_default_perms(BlockDriverState *bs, BdrvChild *c, *nshared = shared; } +uint64_t bdrv_qapi_perm_to_blk_perm(BlockPermission qapi_perm) +{ + static const uint64_t permissions[] = { + [BLOCK_PERMISSION_CONSISTENT_READ] = BLK_PERM_CONSISTENT_READ, + [BLOCK_PERMISSION_WRITE] = BLK_PERM_WRITE, + [BLOCK_PERMISSION_WRITE_UNCHANGED] = BLK_PERM_WRITE_UNCHANGED, + [BLOCK_PERMISSION_RESIZE] = BLK_PERM_RESIZE, + [BLOCK_PERMISSION_GRAPH_MOD] = BLK_PERM_GRAPH_MOD, + }; + + QEMU_BUILD_BUG_ON(ARRAY_SIZE(permissions) != BLOCK_PERMISSION__MAX); + QEMU_BUILD_BUG_ON(1UL << ARRAY_SIZE(permissions) != BLK_PERM_ALL + 1); + + assert(qapi_perm < BLOCK_PERMISSION__MAX); + + return permissions[qapi_perm]; +} + static void bdrv_replace_child_noperm(BdrvChild *child, BlockDriverState *new_bs) { @@ -2394,13 +2602,13 @@ BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs, if (bdrv_get_aio_context(child_bs) != ctx) { ret = bdrv_try_set_aio_context(child_bs, ctx, &local_err); if (ret < 0 && child_role->can_set_aio_ctx) { - GSList *ignore = g_slist_prepend(NULL, child);; + GSList *ignore = g_slist_prepend(NULL, child); ctx = bdrv_get_aio_context(child_bs); if (child_role->can_set_aio_ctx(child, ctx, &ignore, NULL)) { error_free(local_err); ret = 0; g_slist_free(ignore); - ignore = g_slist_prepend(NULL, child);; + ignore = g_slist_prepend(NULL, child); child_role->set_aio_ctx(child, ctx, &ignore); } g_slist_free(ignore); @@ -2409,6 +2617,7 @@ BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs, error_propagate(errp, local_err); g_free(child); bdrv_abort_perm_update(child_bs); + bdrv_unref(child_bs); return NULL; } } @@ -2458,10 +2667,7 @@ BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs, static void bdrv_detach_child(BdrvChild *child) { - if (child->next.le_prev) { - QLIST_REMOVE(child, next); - child->next.le_prev = NULL; - } + QLIST_SAFE_REMOVE(child, next); bdrv_replace_child(child, NULL); @@ -2559,10 +2765,10 @@ void bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd, if (bs->backing) { bdrv_unref_child(bs, bs->backing); + bs->backing = NULL; } if (!backing_hd) { - bs->backing = NULL; goto out; } @@ -3519,6 +3725,15 @@ cleanup_perm: } } } + + if (ret == 0) { + QTAILQ_FOREACH_REVERSE(bs_entry, bs_queue, entry) { + BlockDriverState *bs = bs_entry->state.bs; + + if (bs->drv->bdrv_reopen_commit_post) + bs->drv->bdrv_reopen_commit_post(&bs_entry->state); + } + } cleanup: QTAILQ_FOREACH_SAFE(bs_entry, bs_queue, entry, next) { if (ret) { @@ -3602,6 +3817,29 @@ static void bdrv_reopen_perm(BlockReopenQueue *q, BlockDriverState *bs, *shared = cumulative_shared_perms; } +static bool bdrv_reopen_can_attach(BlockDriverState *parent, + BdrvChild *child, + BlockDriverState *new_child, + Error **errp) +{ + AioContext *parent_ctx = bdrv_get_aio_context(parent); + AioContext *child_ctx = bdrv_get_aio_context(new_child); + GSList *ignore; + bool ret; + + ignore = g_slist_prepend(NULL, child); + ret = bdrv_can_set_aio_context(new_child, parent_ctx, &ignore, NULL); + g_slist_free(ignore); + if (ret) { + return ret; + } + + ignore = g_slist_prepend(NULL, child); + ret = bdrv_can_set_aio_context(parent, child_ctx, &ignore, errp); + g_slist_free(ignore); + return ret; +} + /* * Take a BDRVReopenState and check if the value of 'backing' in the * reopen_state->options QDict is valid or not. @@ -3653,14 +3891,11 @@ static int bdrv_reopen_parse_backing(BDRVReopenState *reopen_state, } /* - * TODO: before removing the x- prefix from x-blockdev-reopen we - * should move the new backing file into the right AioContext - * instead of returning an error. + * Check AioContext compatibility so that the bdrv_set_backing_hd() call in + * bdrv_reopen_commit() won't fail. */ if (new_backing_bs) { - if (bdrv_get_aio_context(new_backing_bs) != bdrv_get_aio_context(bs)) { - error_setg(errp, "Cannot use a new backing file " - "with a different AioContext"); + if (!bdrv_reopen_can_attach(bs, bs->backing, new_backing_bs, errp)) { return -EINVAL; } } @@ -4162,6 +4397,7 @@ void bdrv_replace_node(BlockDriverState *from, BlockDriverState *to, bdrv_ref(from); assert(qemu_get_current_aio_context() == qemu_get_aio_context()); + assert(bdrv_get_aio_context(from) == bdrv_get_aio_context(to)); bdrv_drained_begin(from); /* Put all parents into @list and calculate their cumulative permissions */ @@ -4766,14 +5002,15 @@ BlockDriverState *bdrv_find_node(const char *node_name) } /* Put this QMP function here so it can access the static graph_bdrv_states. */ -BlockDeviceInfoList *bdrv_named_nodes_list(Error **errp) +BlockDeviceInfoList *bdrv_named_nodes_list(bool flat, + Error **errp) { BlockDeviceInfoList *list, *entry; BlockDriverState *bs; list = NULL; QTAILQ_FOREACH(bs, &graph_bdrv_states, node_list) { - BlockDeviceInfo *info = bdrv_block_device_info(NULL, bs, errp); + BlockDeviceInfo *info = bdrv_block_device_info(NULL, bs, flat, errp); if (!info) { qapi_free_BlockDeviceInfoList(list); return NULL; @@ -4854,36 +5091,23 @@ static void xdbg_graph_add_node(XDbgBlockGraphConstructor *gr, void *node, static void xdbg_graph_add_edge(XDbgBlockGraphConstructor *gr, void *parent, const BdrvChild *child) { - typedef struct { - unsigned int flag; - BlockPermission num; - } PermissionMap; - - static const PermissionMap permissions[] = { - { BLK_PERM_CONSISTENT_READ, BLOCK_PERMISSION_CONSISTENT_READ }, - { BLK_PERM_WRITE, BLOCK_PERMISSION_WRITE }, - { BLK_PERM_WRITE_UNCHANGED, BLOCK_PERMISSION_WRITE_UNCHANGED }, - { BLK_PERM_RESIZE, BLOCK_PERMISSION_RESIZE }, - { BLK_PERM_GRAPH_MOD, BLOCK_PERMISSION_GRAPH_MOD }, - { 0, 0 } - }; - const PermissionMap *p; + BlockPermission qapi_perm; XDbgBlockGraphEdge *edge; - QEMU_BUILD_BUG_ON(1UL << (ARRAY_SIZE(permissions) - 1) != BLK_PERM_ALL + 1); - edge = g_new0(XDbgBlockGraphEdge, 1); edge->parent = xdbg_graph_node_num(gr, parent); edge->child = xdbg_graph_node_num(gr, child->bs); edge->name = g_strdup(child->name); - for (p = permissions; p->flag; p++) { - if (p->flag & child->perm) { - QAPI_LIST_ADD(edge->perm, p->num); + for (qapi_perm = 0; qapi_perm < BLOCK_PERMISSION__MAX; qapi_perm++) { + uint64_t flag = bdrv_qapi_perm_to_blk_perm(qapi_perm); + + if (flag & child->perm) { + QAPI_LIST_ADD(edge->perm, qapi_perm); } - if (p->flag & child->shared_perm) { - QAPI_LIST_ADD(edge->shared_perm, p->num); + if (flag & child->shared_perm) { + QAPI_LIST_ADD(edge->shared_perm, qapi_perm); } } @@ -5335,10 +5559,6 @@ static void coroutine_fn bdrv_co_invalidate_cache(BlockDriverState *bs, return; } - if (!(bs->open_flags & BDRV_O_INACTIVE)) { - return; - } - QLIST_FOREACH(child, &bs->children, next) { bdrv_co_invalidate_cache(child->bs, &local_err); if (local_err) { @@ -5360,34 +5580,36 @@ static void coroutine_fn bdrv_co_invalidate_cache(BlockDriverState *bs, * just keep the extended permissions for the next time that an activation * of the image is tried. */ - bs->open_flags &= ~BDRV_O_INACTIVE; - bdrv_get_cumulative_perm(bs, &perm, &shared_perm); - ret = bdrv_check_perm(bs, NULL, perm, shared_perm, NULL, NULL, &local_err); - if (ret < 0) { - bs->open_flags |= BDRV_O_INACTIVE; - error_propagate(errp, local_err); - return; - } - bdrv_set_perm(bs, perm, shared_perm); - - if (bs->drv->bdrv_co_invalidate_cache) { - bs->drv->bdrv_co_invalidate_cache(bs, &local_err); - if (local_err) { + if (bs->open_flags & BDRV_O_INACTIVE) { + bs->open_flags &= ~BDRV_O_INACTIVE; + bdrv_get_cumulative_perm(bs, &perm, &shared_perm); + ret = bdrv_check_perm(bs, NULL, perm, shared_perm, NULL, NULL, &local_err); + if (ret < 0) { bs->open_flags |= BDRV_O_INACTIVE; error_propagate(errp, local_err); return; } - } + bdrv_set_perm(bs, perm, shared_perm); - FOR_EACH_DIRTY_BITMAP(bs, bm) { - bdrv_dirty_bitmap_skip_store(bm, false); - } + if (bs->drv->bdrv_co_invalidate_cache) { + bs->drv->bdrv_co_invalidate_cache(bs, &local_err); + if (local_err) { + bs->open_flags |= BDRV_O_INACTIVE; + error_propagate(errp, local_err); + return; + } + } - ret = refresh_total_sectors(bs, bs->total_sectors); - if (ret < 0) { - bs->open_flags |= BDRV_O_INACTIVE; - error_setg_errno(errp, -ret, "Could not refresh total sector count"); - return; + FOR_EACH_DIRTY_BITMAP(bs, bm) { + bdrv_dirty_bitmap_skip_store(bm, false); + } + + ret = refresh_total_sectors(bs, bs->total_sectors); + if (ret < 0) { + bs->open_flags |= BDRV_O_INACTIVE; + error_setg_errno(errp, -ret, "Could not refresh total sector count"); + return; + } } QLIST_FOREACH(parent, &bs->parents, next_parent) { @@ -5751,12 +5973,11 @@ void bdrv_img_create(const char *filename, const char *fmt, return; } + /* Create parameter list */ create_opts = qemu_opts_append(create_opts, drv->create_opts); create_opts = qemu_opts_append(create_opts, proto_drv->create_opts); - /* Create parameter list with default values */ opts = qemu_opts_create(create_opts, NULL, 0, &error_abort); - qemu_opt_set_number(opts, BLOCK_OPT_SIZE, img_size, &error_abort); /* Parse -o options */ if (options) { @@ -5766,6 +5987,13 @@ void bdrv_img_create(const char *filename, const char *fmt, } } + if (!qemu_opt_get(opts, BLOCK_OPT_SIZE)) { + qemu_opt_set_number(opts, BLOCK_OPT_SIZE, img_size, &error_abort); + } else if (img_size != UINT64_C(-1)) { + error_setg(errp, "The image size must be specified only once"); + goto out; + } + if (base_filename) { qemu_opt_set(opts, BLOCK_OPT_BACKING_FILE, base_filename, &local_err); if (local_err) { @@ -6169,65 +6397,55 @@ int bdrv_amend_options(BlockDriverState *bs, QemuOpts *opts, return bs->drv->bdrv_amend_options(bs, opts, status_cb, cb_opaque, errp); } -/* This function will be called by the bdrv_recurse_is_first_non_filter method - * of block filter and by bdrv_is_first_non_filter. - * It is used to test if the given bs is the candidate or recurse more in the - * node graph. +/* + * This function checks whether the given @to_replace is allowed to be + * replaced by a node that always shows the same data as @bs. This is + * used for example to verify whether the mirror job can replace + * @to_replace by the target mirrored from @bs. + * To be replaceable, @bs and @to_replace may either be guaranteed to + * always show the same data (because they are only connected through + * filters), or some driver may allow replacing one of its children + * because it can guarantee that this child's data is not visible at + * all (for example, for dissenting quorum children that have no other + * parents). */ -bool bdrv_recurse_is_first_non_filter(BlockDriverState *bs, - BlockDriverState *candidate) +bool bdrv_recurse_can_replace(BlockDriverState *bs, + BlockDriverState *to_replace) { - /* return false if basic checks fails */ if (!bs || !bs->drv) { return false; } - /* the code reached a non block filter driver -> check if the bs is - * the same as the candidate. It's the recursion termination condition. - */ - if (!bs->drv->is_filter) { - return bs == candidate; - } - /* Down this path the driver is a block filter driver */ - - /* If the block filter recursion method is defined use it to recurse down - * the node graph. - */ - if (bs->drv->bdrv_recurse_is_first_non_filter) { - return bs->drv->bdrv_recurse_is_first_non_filter(bs, candidate); + if (bs == to_replace) { + return true; } - /* the driver is a block filter but don't allow to recurse -> return false - */ + /* See what the driver can do */ + if (bs->drv->bdrv_recurse_can_replace) { + return bs->drv->bdrv_recurse_can_replace(bs, to_replace); + } + + /* For filters without an own implementation, we can recurse on our own */ + if (bs->drv->is_filter) { + BdrvChild *child = bs->file ?: bs->backing; + return bdrv_recurse_can_replace(child->bs, to_replace); + } + + /* Safe default */ return false; } -/* This function checks if the candidate is the first non filter bs down it's - * bs chain. Since we don't have pointers to parents it explore all bs chains - * from the top. Some filters can choose not to pass down the recursion. +/* + * Check whether the given @node_name can be replaced by a node that + * has the same data as @parent_bs. If so, return @node_name's BDS; + * NULL otherwise. + * + * @node_name must be a (recursive) *child of @parent_bs (or this + * function will return NULL). + * + * The result (whether the node can be replaced or not) is only valid + * for as long as no graph or permission changes occur. */ -bool bdrv_is_first_non_filter(BlockDriverState *candidate) -{ - BlockDriverState *bs; - BdrvNextIterator it; - - /* walk down the bs forest recursively */ - for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { - bool perm; - - /* try to recurse in this top level bs */ - perm = bdrv_recurse_is_first_non_filter(bs, candidate); - - /* candidate is the first non filter */ - if (perm) { - bdrv_next_cleanup(&it); - return true; - } - } - - return false; -} - BlockDriverState *check_to_replace_node(BlockDriverState *parent_bs, const char *node_name, Error **errp) { @@ -6252,8 +6470,11 @@ BlockDriverState *check_to_replace_node(BlockDriverState *parent_bs, * Another benefit is that this tests exclude backing files which are * blocked by the backing blockers. */ - if (!bdrv_recurse_is_first_non_filter(parent_bs, to_replace_bs)) { - error_setg(errp, "Only top most non filter can be replaced"); + if (!bdrv_recurse_can_replace(parent_bs, to_replace_bs)) { + error_setg(errp, "Cannot replace '%s' by a node mirrored from '%s', " + "because it cannot be guaranteed that doing so would not " + "lead to an abrupt change of visible data", + node_name, parent_bs->node_name); to_replace_bs = NULL; goto out; } @@ -6410,6 +6631,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/Makefile.objs b/block/Makefile.objs index e394fe0b6c..3635b6b4c1 100644 --- a/block/Makefile.objs +++ b/block/Makefile.objs @@ -18,6 +18,7 @@ block-obj-y += block-backend.o snapshot.o qapi.o block-obj-$(CONFIG_WIN32) += file-win32.o win32-aio.o block-obj-$(CONFIG_POSIX) += file-posix.o block-obj-$(CONFIG_LINUX_AIO) += linux-aio.o +block-obj-$(CONFIG_LINUX_IO_URING) += io_uring.o block-obj-y += null.o mirror.o commit.o io.o create.o block-obj-y += throttle-groups.o block-obj-$(CONFIG_LINUX) += nvme.o @@ -43,8 +44,12 @@ block-obj-y += crypto.o block-obj-y += aio_task.o block-obj-y += backup-top.o +block-obj-y += filter-compress.o +common-obj-y += monitor/ -common-obj-y += stream.o +block-obj-y += stream.o + +common-obj-y += qapi-sysemu.o nfs.o-libs := $(LIBNFS_LIBS) iscsi.o-cflags := $(LIBISCSI_CFLAGS) @@ -65,5 +70,7 @@ block-obj-$(if $(CONFIG_LZFSE),m,n) += dmg-lzfse.o dmg-lzfse.o-libs := $(LZFSE_LIBS) qcow.o-libs := -lz linux-aio.o-libs := -laio +io_uring.o-cflags := $(LINUX_IO_URING_CFLAGS) +io_uring.o-libs := $(LINUX_IO_URING_LIBS) parallels.o-cflags := $(LIBXML2_CFLAGS) parallels.o-libs := $(LIBXML2_LIBS) diff --git a/block/backup-top.c b/block/backup-top.c index 7cdb1f8eba..3b50c06e2c 100644 --- a/block/backup-top.c +++ b/block/backup-top.c @@ -38,6 +38,7 @@ typedef struct BDRVBackupTopState { BlockCopyState *bcs; BdrvChild *target; bool active; + int64_t cluster_size; } BDRVBackupTopState; static coroutine_fn int backup_top_co_preadv( @@ -48,11 +49,17 @@ static coroutine_fn int backup_top_co_preadv( } static coroutine_fn int backup_top_cbw(BlockDriverState *bs, uint64_t offset, - uint64_t bytes) + uint64_t bytes, BdrvRequestFlags flags) { BDRVBackupTopState *s = bs->opaque; - uint64_t end = QEMU_ALIGN_UP(offset + bytes, s->bcs->cluster_size); - uint64_t off = QEMU_ALIGN_DOWN(offset, s->bcs->cluster_size); + uint64_t off, end; + + if (flags & BDRV_REQ_WRITE_UNCHANGED) { + return 0; + } + + off = QEMU_ALIGN_DOWN(offset, s->cluster_size); + end = QEMU_ALIGN_UP(offset + bytes, s->cluster_size); return block_copy(s->bcs, off, end - off, NULL); } @@ -60,7 +67,7 @@ static coroutine_fn int backup_top_cbw(BlockDriverState *bs, uint64_t offset, static int coroutine_fn backup_top_co_pdiscard(BlockDriverState *bs, int64_t offset, int bytes) { - int ret = backup_top_cbw(bs, offset, bytes); + int ret = backup_top_cbw(bs, offset, bytes, 0); if (ret < 0) { return ret; } @@ -71,7 +78,7 @@ static int coroutine_fn backup_top_co_pdiscard(BlockDriverState *bs, static int coroutine_fn backup_top_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int bytes, BdrvRequestFlags flags) { - int ret = backup_top_cbw(bs, offset, bytes); + int ret = backup_top_cbw(bs, offset, bytes, flags); if (ret < 0) { return ret; } @@ -84,11 +91,9 @@ static coroutine_fn int backup_top_co_pwritev(BlockDriverState *bs, uint64_t bytes, QEMUIOVector *qiov, int flags) { - if (!(flags & BDRV_REQ_WRITE_UNCHANGED)) { - int ret = backup_top_cbw(bs, offset, bytes); - if (ret < 0) { - return ret; - } + int ret = backup_top_cbw(bs, offset, bytes, flags); + if (ret < 0) { + return ret; } return bdrv_co_pwritev(bs->backing, offset, bytes, qiov, flags); @@ -190,13 +195,19 @@ 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; } + state = top->opaque; top->total_sectors = source->total_sectors; - top->opaque = state = g_new0(BDRVBackupTopState, 1); + top->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED | + (BDRV_REQ_FUA & source->supported_write_flags); + top->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED | + ((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK) & + source->supported_zero_flags); bdrv_ref(target); state->target = bdrv_attach_child(top, target, "target", &child_file, errp); @@ -212,8 +223,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 +236,15 @@ 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->cluster_size = cluster_size; 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 +252,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; @@ -255,14 +269,11 @@ append_failed: void bdrv_backup_top_drop(BlockDriverState *bs) { BDRVBackupTopState *s = bs->opaque; - AioContext *aio_context = bdrv_get_aio_context(bs); - - block_copy_state_free(s->bcs); - - aio_context_acquire(aio_context); bdrv_drained_begin(bs); + block_copy_state_free(s->bcs); + s->active = false; bdrv_child_refresh_perms(bs, bs->backing, &error_abort); bdrv_replace_node(bs, backing_bs(bs), &error_abort); @@ -271,6 +282,4 @@ void bdrv_backup_top_drop(BlockDriverState *bs) bdrv_drained_end(bs); bdrv_unref(bs); - - aio_context_release(aio_context); } diff --git a/block/backup.c b/block/backup.c index cf62b1a38c..a7a7dcaf4c 100644 --- a/block/backup.c +++ b/block/backup.c @@ -57,15 +57,6 @@ static void backup_progress_bytes_callback(int64_t bytes, void *opaque) BackupBlockJob *s = opaque; s->bytes_read += bytes; - job_progress_update(&s->common.job, bytes); -} - -static void backup_progress_reset_callback(void *opaque) -{ - BackupBlockJob *s = opaque; - uint64_t estimate = bdrv_get_dirty_count(s->bcs->copy_bitmap); - - job_progress_set_remaining(&s->common.job, estimate); } static int coroutine_fn backup_do_cow(BackupBlockJob *job, @@ -111,7 +102,7 @@ static void backup_cleanup_sync_bitmap(BackupBlockJob *job, int ret) if (ret < 0 && job->bitmap_mode == BITMAP_SYNC_MODE_ALWAYS) { /* If we failed and synced, merge in the bits we didn't copy: */ - bdrv_dirty_bitmap_merge_internal(bm, job->bcs->copy_bitmap, + bdrv_dirty_bitmap_merge_internal(bm, block_copy_dirty_bitmap(job->bcs), NULL, true); } } @@ -135,7 +126,6 @@ static void backup_abort(Job *job) static void backup_clean(Job *job) { BackupBlockJob *s = container_of(job, BackupBlockJob, common.job); - bdrv_backup_top_drop(s->backup_top); } @@ -151,7 +141,8 @@ void backup_do_checkpoint(BlockJob *job, Error **errp) return; } - bdrv_set_dirty_bitmap(backup_job->bcs->copy_bitmap, 0, backup_job->len); + bdrv_set_dirty_bitmap(block_copy_dirty_bitmap(backup_job->bcs), 0, + backup_job->len); } static BlockErrorAction backup_error_action(BackupBlockJob *job, @@ -196,7 +187,7 @@ static int coroutine_fn backup_loop(BackupBlockJob *job) BdrvDirtyBitmapIter *bdbi; int ret = 0; - bdbi = bdrv_dirty_iter_new(job->bcs->copy_bitmap); + bdbi = bdrv_dirty_iter_new(block_copy_dirty_bitmap(job->bcs)); while ((offset = bdrv_dirty_iter_next(bdbi)) != -1) { do { if (yield_and_check(job)) { @@ -216,14 +207,14 @@ static int coroutine_fn backup_loop(BackupBlockJob *job) return ret; } -static void backup_init_copy_bitmap(BackupBlockJob *job) +static void backup_init_bcs_bitmap(BackupBlockJob *job) { bool ret; uint64_t estimate; + BdrvDirtyBitmap *bcs_bitmap = block_copy_dirty_bitmap(job->bcs); if (job->sync_mode == MIRROR_SYNC_MODE_BITMAP) { - ret = bdrv_dirty_bitmap_merge_internal(job->bcs->copy_bitmap, - job->sync_bitmap, + ret = bdrv_dirty_bitmap_merge_internal(bcs_bitmap, job->sync_bitmap, NULL, true); assert(ret); } else { @@ -232,12 +223,12 @@ static void backup_init_copy_bitmap(BackupBlockJob *job) * We can't hog the coroutine to initialize this thoroughly. * Set a flag and resume work when we are able to yield safely. */ - job->bcs->skip_unallocated = true; + block_copy_set_skip_unallocated(job->bcs, true); } - bdrv_set_dirty_bitmap(job->bcs->copy_bitmap, 0, job->len); + bdrv_set_dirty_bitmap(bcs_bitmap, 0, job->len); } - estimate = bdrv_get_dirty_count(job->bcs->copy_bitmap); + estimate = bdrv_get_dirty_count(bcs_bitmap); job_progress_set_remaining(&job->common.job, estimate); } @@ -246,7 +237,7 @@ static int coroutine_fn backup_run(Job *job, Error **errp) BackupBlockJob *s = container_of(job, BackupBlockJob, common.job); int ret = 0; - backup_init_copy_bitmap(s); + backup_init_bcs_bitmap(s); if (s->sync_mode == MIRROR_SYNC_MODE_TOP) { int64_t offset = 0; @@ -265,12 +256,12 @@ static int coroutine_fn backup_run(Job *job, Error **errp) offset += count; } - s->bcs->skip_unallocated = false; + block_copy_set_skip_unallocated(s->bcs, false); } if (s->sync_mode == MIRROR_SYNC_MODE_NONE) { /* - * All bits are set in copy_bitmap to allow any cluster to be copied. + * All bits are set in bcs bitmap to allow any cluster to be copied. * This does not actually require them to be copied. */ while (!job_is_cancelled(job)) { @@ -461,8 +452,8 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, job->cluster_size = cluster_size; job->len = len; - block_copy_set_callbacks(bcs, backup_progress_bytes_callback, - backup_progress_reset_callback, job); + block_copy_set_progress_callback(bcs, backup_progress_bytes_callback, job); + block_copy_set_progress_meter(bcs, &job->common.job.progress); /* Required permissions are already taken by backup-top target */ block_job_add_bdrv(&job->common, "target", target, 0, BLK_PERM_ALL, diff --git a/block/blkdebug.c b/block/blkdebug.c index 5ae96c52b0..af44aa973f 100644 --- a/block/blkdebug.c +++ b/block/blkdebug.c @@ -28,10 +28,14 @@ #include "qemu/cutils.h" #include "qemu/config-file.h" #include "block/block_int.h" +#include "block/qdict.h" #include "qemu/module.h" #include "qemu/option.h" +#include "qapi/qapi-visit-block-core.h" #include "qapi/qmp/qdict.h" +#include "qapi/qmp/qlist.h" #include "qapi/qmp/qstring.h" +#include "qapi/qobject-input-visitor.h" #include "sysemu/qtest.h" typedef struct BDRVBlkdebugState { @@ -44,6 +48,9 @@ typedef struct BDRVBlkdebugState { uint64_t opt_discard; uint64_t max_discard; + uint64_t take_child_perms; + uint64_t unshare_child_perms; + /* For blkdebug_refresh_filename() */ char *config_file; @@ -344,6 +351,69 @@ static void blkdebug_parse_filename(const char *filename, QDict *options, qdict_put_str(options, "x-image", filename); } +static int blkdebug_parse_perm_list(uint64_t *dest, QDict *options, + const char *prefix, Error **errp) +{ + int ret = 0; + QDict *subqdict = NULL; + QObject *crumpled_subqdict = NULL; + Visitor *v = NULL; + BlockPermissionList *perm_list = NULL, *element; + Error *local_err = NULL; + + *dest = 0; + + qdict_extract_subqdict(options, &subqdict, prefix); + if (!qdict_size(subqdict)) { + goto out; + } + + crumpled_subqdict = qdict_crumple(subqdict, errp); + if (!crumpled_subqdict) { + ret = -EINVAL; + goto out; + } + + v = qobject_input_visitor_new(crumpled_subqdict); + visit_type_BlockPermissionList(v, NULL, &perm_list, &local_err); + if (local_err) { + error_propagate(errp, local_err); + ret = -EINVAL; + goto out; + } + + for (element = perm_list; element; element = element->next) { + *dest |= bdrv_qapi_perm_to_blk_perm(element->value); + } + +out: + qapi_free_BlockPermissionList(perm_list); + visit_free(v); + qobject_unref(subqdict); + qobject_unref(crumpled_subqdict); + return ret; +} + +static int blkdebug_parse_perms(BDRVBlkdebugState *s, QDict *options, + Error **errp) +{ + int ret; + + ret = blkdebug_parse_perm_list(&s->take_child_perms, options, + "take-child-perms.", errp); + if (ret < 0) { + return ret; + } + + ret = blkdebug_parse_perm_list(&s->unshare_child_perms, options, + "unshare-child-perms.", errp); + if (ret < 0) { + return ret; + } + + return 0; +} + static QemuOptsList runtime_opts = { .name = "blkdebug", .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head), @@ -419,6 +489,12 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags, /* Set initial state */ s->state = 1; + /* Parse permissions modifiers before opening the image file */ + ret = blkdebug_parse_perms(s, options, errp); + if (ret < 0) { + goto out; + } + /* Open the image file */ bs->file = bdrv_open_child(qemu_opt_get(opts, "x-image"), options, "image", bs, &child_file, false, &local_err); @@ -916,6 +992,21 @@ static int blkdebug_reopen_prepare(BDRVReopenState *reopen_state, return 0; } +static void blkdebug_child_perm(BlockDriverState *bs, BdrvChild *c, + const BdrvChildRole *role, + BlockReopenQueue *reopen_queue, + uint64_t perm, uint64_t shared, + uint64_t *nperm, uint64_t *nshared) +{ + BDRVBlkdebugState *s = bs->opaque; + + bdrv_filter_default_perms(bs, c, role, reopen_queue, perm, shared, + nperm, nshared); + + *nperm |= s->take_child_perms; + *nshared &= ~s->unshare_child_perms; +} + static const char *const blkdebug_strong_runtime_opts[] = { "config", "inject-error.", @@ -940,7 +1031,7 @@ static BlockDriver bdrv_blkdebug = { .bdrv_file_open = blkdebug_open, .bdrv_close = blkdebug_close, .bdrv_reopen_prepare = blkdebug_reopen_prepare, - .bdrv_child_perm = bdrv_filter_default_perms, + .bdrv_child_perm = blkdebug_child_perm, .bdrv_getlength = blkdebug_getlength, .bdrv_refresh_filename = blkdebug_refresh_filename, diff --git a/block/blkverify.c b/block/blkverify.c index 304b0a1368..ba6b1853ae 100644 --- a/block/blkverify.c +++ b/block/blkverify.c @@ -268,18 +268,18 @@ static int blkverify_co_flush(BlockDriverState *bs) return bdrv_co_flush(s->test_file->bs); } -static bool blkverify_recurse_is_first_non_filter(BlockDriverState *bs, - BlockDriverState *candidate) +static bool blkverify_recurse_can_replace(BlockDriverState *bs, + BlockDriverState *to_replace) { BDRVBlkverifyState *s = bs->opaque; - bool perm = bdrv_recurse_is_first_non_filter(bs->file->bs, candidate); - - if (perm) { - return true; - } - - return bdrv_recurse_is_first_non_filter(s->test_file->bs, candidate); + /* + * blkverify quits the whole qemu process if there is a mismatch + * between bs->file->bs and s->test_file->bs. Therefore, we know + * know that both must match bs and we can recurse down to either. + */ + return bdrv_recurse_can_replace(bs->file->bs, to_replace) || + bdrv_recurse_can_replace(s->test_file->bs, to_replace); } static void blkverify_refresh_filename(BlockDriverState *bs) @@ -327,7 +327,7 @@ static BlockDriver bdrv_blkverify = { .bdrv_co_flush = blkverify_co_flush, .is_filter = true, - .bdrv_recurse_is_first_non_filter = blkverify_recurse_is_first_non_filter, + .bdrv_recurse_can_replace = blkverify_recurse_can_replace, }; static void bdrv_blkverify_init(void) diff --git a/block/block-backend.c b/block/block-backend.c index 8b8f2a80a0..38ae413826 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -1140,16 +1140,22 @@ static int blk_check_byte_request(BlockBackend *blk, int64_t offset, return 0; } +/* To be called between exactly one pair of blk_inc/dec_in_flight() */ static void coroutine_fn blk_wait_while_drained(BlockBackend *blk) { + assert(blk->in_flight > 0); + if (blk->quiesce_counter && !blk->disable_request_queuing) { + blk_dec_in_flight(blk); qemu_co_queue_wait(&blk->queued_requests, NULL); + blk_inc_in_flight(blk); } } -int coroutine_fn blk_co_preadv(BlockBackend *blk, int64_t offset, - unsigned int bytes, QEMUIOVector *qiov, - BdrvRequestFlags flags) +/* To be called between exactly one pair of blk_inc/dec_in_flight() */ +static int coroutine_fn +blk_do_preadv(BlockBackend *blk, int64_t offset, unsigned int bytes, + QEMUIOVector *qiov, BdrvRequestFlags flags) { int ret; BlockDriverState *bs; @@ -1178,10 +1184,24 @@ int coroutine_fn blk_co_preadv(BlockBackend *blk, int64_t offset, return ret; } -int coroutine_fn blk_co_pwritev_part(BlockBackend *blk, int64_t offset, - unsigned int bytes, - QEMUIOVector *qiov, size_t qiov_offset, - BdrvRequestFlags flags) +int coroutine_fn blk_co_preadv(BlockBackend *blk, int64_t offset, + unsigned int bytes, QEMUIOVector *qiov, + BdrvRequestFlags flags) +{ + int ret; + + blk_inc_in_flight(blk); + ret = blk_do_preadv(blk, offset, bytes, qiov, flags); + blk_dec_in_flight(blk); + + return ret; +} + +/* To be called between exactly one pair of blk_inc/dec_in_flight() */ +static int coroutine_fn +blk_do_pwritev_part(BlockBackend *blk, int64_t offset, unsigned int bytes, + QEMUIOVector *qiov, size_t qiov_offset, + BdrvRequestFlags flags) { int ret; BlockDriverState *bs; @@ -1214,6 +1234,20 @@ int coroutine_fn blk_co_pwritev_part(BlockBackend *blk, int64_t offset, return ret; } +int coroutine_fn blk_co_pwritev_part(BlockBackend *blk, int64_t offset, + unsigned int bytes, + QEMUIOVector *qiov, size_t qiov_offset, + BdrvRequestFlags flags) +{ + int ret; + + blk_inc_in_flight(blk); + ret = blk_do_pwritev_part(blk, offset, bytes, qiov, qiov_offset, flags); + blk_dec_in_flight(blk); + + return ret; +} + int coroutine_fn blk_co_pwritev(BlockBackend *blk, int64_t offset, unsigned int bytes, QEMUIOVector *qiov, BdrvRequestFlags flags) @@ -1234,7 +1268,7 @@ static void blk_read_entry(void *opaque) BlkRwCo *rwco = opaque; QEMUIOVector *qiov = rwco->iobuf; - rwco->ret = blk_co_preadv(rwco->blk, rwco->offset, qiov->size, + rwco->ret = blk_do_preadv(rwco->blk, rwco->offset, qiov->size, qiov, rwco->flags); aio_wait_kick(); } @@ -1244,8 +1278,8 @@ static void blk_write_entry(void *opaque) BlkRwCo *rwco = opaque; QEMUIOVector *qiov = rwco->iobuf; - rwco->ret = blk_co_pwritev(rwco->blk, rwco->offset, qiov->size, - qiov, rwco->flags); + rwco->ret = blk_do_pwritev_part(rwco->blk, rwco->offset, qiov->size, + qiov, 0, rwco->flags); aio_wait_kick(); } @@ -1262,6 +1296,7 @@ static int blk_prw(BlockBackend *blk, int64_t offset, uint8_t *buf, .ret = NOT_DONE, }; + blk_inc_in_flight(blk); if (qemu_in_coroutine()) { /* Fast-path if already in coroutine context */ co_entry(&rwco); @@ -1270,6 +1305,7 @@ static int blk_prw(BlockBackend *blk, int64_t offset, uint8_t *buf, bdrv_coroutine_enter(blk_bs(blk), co); BDRV_POLL_WHILE(blk_bs(blk), rwco.ret == NOT_DONE); } + blk_dec_in_flight(blk); return rwco.ret; } @@ -1387,14 +1423,8 @@ static void blk_aio_read_entry(void *opaque) BlkRwCo *rwco = &acb->rwco; QEMUIOVector *qiov = rwco->iobuf; - if (rwco->blk->quiesce_counter) { - blk_dec_in_flight(rwco->blk); - blk_wait_while_drained(rwco->blk); - blk_inc_in_flight(rwco->blk); - } - assert(qiov->size == acb->bytes); - rwco->ret = blk_co_preadv(rwco->blk, rwco->offset, acb->bytes, + rwco->ret = blk_do_preadv(rwco->blk, rwco->offset, acb->bytes, qiov, rwco->flags); blk_aio_complete(acb); } @@ -1405,15 +1435,9 @@ static void blk_aio_write_entry(void *opaque) BlkRwCo *rwco = &acb->rwco; QEMUIOVector *qiov = rwco->iobuf; - if (rwco->blk->quiesce_counter) { - blk_dec_in_flight(rwco->blk); - blk_wait_while_drained(rwco->blk); - blk_inc_in_flight(rwco->blk); - } - assert(!qiov || qiov->size == acb->bytes); - rwco->ret = blk_co_pwritev(rwco->blk, rwco->offset, acb->bytes, - qiov, rwco->flags); + rwco->ret = blk_do_pwritev_part(rwco->blk, rwco->offset, acb->bytes, + qiov, 0, rwco->flags); blk_aio_complete(acb); } @@ -1488,38 +1512,6 @@ BlockAIOCB *blk_aio_pwritev(BlockBackend *blk, int64_t offset, blk_aio_write_entry, flags, cb, opaque); } -static void blk_aio_flush_entry(void *opaque) -{ - BlkAioEmAIOCB *acb = opaque; - BlkRwCo *rwco = &acb->rwco; - - rwco->ret = blk_co_flush(rwco->blk); - blk_aio_complete(acb); -} - -BlockAIOCB *blk_aio_flush(BlockBackend *blk, - BlockCompletionFunc *cb, void *opaque) -{ - return blk_aio_prwv(blk, 0, 0, NULL, blk_aio_flush_entry, 0, cb, opaque); -} - -static void blk_aio_pdiscard_entry(void *opaque) -{ - BlkAioEmAIOCB *acb = opaque; - BlkRwCo *rwco = &acb->rwco; - - rwco->ret = blk_co_pdiscard(rwco->blk, rwco->offset, acb->bytes); - blk_aio_complete(acb); -} - -BlockAIOCB *blk_aio_pdiscard(BlockBackend *blk, - int64_t offset, int bytes, - BlockCompletionFunc *cb, void *opaque) -{ - return blk_aio_prwv(blk, offset, bytes, NULL, blk_aio_pdiscard_entry, 0, - cb, opaque); -} - void blk_aio_cancel(BlockAIOCB *acb) { bdrv_aio_cancel(acb); @@ -1530,7 +1522,9 @@ void blk_aio_cancel_async(BlockAIOCB *acb) bdrv_aio_cancel_async(acb); } -int blk_co_ioctl(BlockBackend *blk, unsigned long int req, void *buf) +/* To be called between exactly one pair of blk_inc/dec_in_flight() */ +static int coroutine_fn +blk_do_ioctl(BlockBackend *blk, unsigned long int req, void *buf) { blk_wait_while_drained(blk); @@ -1546,8 +1540,7 @@ static void blk_ioctl_entry(void *opaque) BlkRwCo *rwco = opaque; QEMUIOVector *qiov = rwco->iobuf; - rwco->ret = blk_co_ioctl(rwco->blk, rwco->offset, - qiov->iov[0].iov_base); + rwco->ret = blk_do_ioctl(rwco->blk, rwco->offset, qiov->iov[0].iov_base); aio_wait_kick(); } @@ -1561,7 +1554,7 @@ static void blk_aio_ioctl_entry(void *opaque) BlkAioEmAIOCB *acb = opaque; BlkRwCo *rwco = &acb->rwco; - rwco->ret = blk_co_ioctl(rwco->blk, rwco->offset, rwco->iobuf); + rwco->ret = blk_do_ioctl(rwco->blk, rwco->offset, rwco->iobuf); blk_aio_complete(acb); } @@ -1572,7 +1565,9 @@ BlockAIOCB *blk_aio_ioctl(BlockBackend *blk, unsigned long int req, void *buf, return blk_aio_prwv(blk, req, 0, buf, blk_aio_ioctl_entry, 0, cb, opaque); } -int blk_co_pdiscard(BlockBackend *blk, int64_t offset, int bytes) +/* To be called between exactly one pair of blk_inc/dec_in_flight() */ +static int coroutine_fn +blk_do_pdiscard(BlockBackend *blk, int64_t offset, int bytes) { int ret; @@ -1586,7 +1581,50 @@ int blk_co_pdiscard(BlockBackend *blk, int64_t offset, int bytes) return bdrv_co_pdiscard(blk->root, offset, bytes); } -int blk_co_flush(BlockBackend *blk) +static void blk_aio_pdiscard_entry(void *opaque) +{ + BlkAioEmAIOCB *acb = opaque; + BlkRwCo *rwco = &acb->rwco; + + rwco->ret = blk_do_pdiscard(rwco->blk, rwco->offset, acb->bytes); + blk_aio_complete(acb); +} + +BlockAIOCB *blk_aio_pdiscard(BlockBackend *blk, + int64_t offset, int bytes, + BlockCompletionFunc *cb, void *opaque) +{ + return blk_aio_prwv(blk, offset, bytes, NULL, blk_aio_pdiscard_entry, 0, + cb, opaque); +} + +int coroutine_fn blk_co_pdiscard(BlockBackend *blk, int64_t offset, int bytes) +{ + int ret; + + blk_inc_in_flight(blk); + ret = blk_do_pdiscard(blk, offset, bytes); + blk_dec_in_flight(blk); + + return ret; +} + +static void blk_pdiscard_entry(void *opaque) +{ + BlkRwCo *rwco = opaque; + QEMUIOVector *qiov = rwco->iobuf; + + rwco->ret = blk_do_pdiscard(rwco->blk, rwco->offset, qiov->size); + aio_wait_kick(); +} + +int blk_pdiscard(BlockBackend *blk, int64_t offset, int bytes) +{ + return blk_prw(blk, offset, NULL, bytes, blk_pdiscard_entry, 0); +} + +/* To be called between exactly one pair of blk_inc/dec_in_flight() */ +static int coroutine_fn blk_do_flush(BlockBackend *blk) { blk_wait_while_drained(blk); @@ -1597,10 +1635,36 @@ int blk_co_flush(BlockBackend *blk) return bdrv_co_flush(blk_bs(blk)); } +static void blk_aio_flush_entry(void *opaque) +{ + BlkAioEmAIOCB *acb = opaque; + BlkRwCo *rwco = &acb->rwco; + + rwco->ret = blk_do_flush(rwco->blk); + blk_aio_complete(acb); +} + +BlockAIOCB *blk_aio_flush(BlockBackend *blk, + BlockCompletionFunc *cb, void *opaque) +{ + return blk_aio_prwv(blk, 0, 0, NULL, blk_aio_flush_entry, 0, cb, opaque); +} + +int coroutine_fn blk_co_flush(BlockBackend *blk) +{ + int ret; + + blk_inc_in_flight(blk); + ret = blk_do_flush(blk); + blk_dec_in_flight(blk); + + return ret; +} + static void blk_flush_entry(void *opaque) { BlkRwCo *rwco = opaque; - rwco->ret = blk_co_flush(rwco->blk); + rwco->ret = blk_do_flush(rwco->blk); aio_wait_kick(); } @@ -2083,20 +2147,6 @@ int blk_truncate(BlockBackend *blk, int64_t offset, bool exact, return bdrv_truncate(blk->root, offset, exact, prealloc, errp); } -static void blk_pdiscard_entry(void *opaque) -{ - BlkRwCo *rwco = opaque; - QEMUIOVector *qiov = rwco->iobuf; - - rwco->ret = blk_co_pdiscard(rwco->blk, rwco->offset, qiov->size); - aio_wait_kick(); -} - -int blk_pdiscard(BlockBackend *blk, int64_t offset, int bytes) -{ - return blk_prw(blk, offset, NULL, bytes, blk_pdiscard_entry, 0); -} - int blk_save_vmstate(BlockBackend *blk, const uint8_t *buf, int64_t pos, int size) { diff --git a/block/block-copy.c b/block/block-copy.c index 79798a1567..05227e18bf 100644 --- a/block/block-copy.c +++ b/block/block-copy.c @@ -24,37 +24,136 @@ #define BLOCK_COPY_MAX_BUFFER (1 * MiB) #define BLOCK_COPY_MAX_MEM (128 * MiB) -static void coroutine_fn block_copy_wait_inflight_reqs(BlockCopyState *s, - int64_t start, - int64_t end) +typedef struct BlockCopyInFlightReq { + int64_t offset; + int64_t bytes; + QLIST_ENTRY(BlockCopyInFlightReq) list; + CoQueue wait_queue; /* coroutines blocked on this request */ +} BlockCopyInFlightReq; + +typedef struct BlockCopyState { + /* + * BdrvChild objects are not owned or managed by block-copy. They are + * provided by block-copy user and user is responsible for appropriate + * permissions on these children. + */ + BdrvChild *source; + BdrvChild *target; + BdrvDirtyBitmap *copy_bitmap; + int64_t in_flight_bytes; + int64_t cluster_size; + bool use_copy_range; + int64_t copy_size; + uint64_t len; + QLIST_HEAD(, BlockCopyInFlightReq) inflight_reqs; + + BdrvRequestFlags write_flags; + + /* + * skip_unallocated: + * + * Used by sync=top jobs, which first scan the source node for unallocated + * areas and clear them in the copy_bitmap. During this process, the bitmap + * is thus not fully initialized: It may still have bits set for areas that + * are unallocated and should actually not be copied. + * + * This is indicated by skip_unallocated. + * + * In this case, block_copy() will query the source’s allocation status, + * skip unallocated regions, clear them in the copy_bitmap, and invoke + * block_copy_reset_unallocated() every time it does. + */ + bool skip_unallocated; + + ProgressMeter *progress; + /* progress_bytes_callback: called when some copying progress is done. */ + ProgressBytesCallbackFunc progress_bytes_callback; + void *progress_opaque; + + SharedResource *mem; +} BlockCopyState; + +static BlockCopyInFlightReq *find_conflicting_inflight_req(BlockCopyState *s, + int64_t offset, + int64_t bytes) { BlockCopyInFlightReq *req; - bool waited; - do { - waited = false; - QLIST_FOREACH(req, &s->inflight_reqs, list) { - if (end > req->start_byte && start < req->end_byte) { - qemu_co_queue_wait(&req->wait_queue, NULL); - waited = true; - break; - } + QLIST_FOREACH(req, &s->inflight_reqs, list) { + if (offset + bytes > req->offset && offset < req->offset + req->bytes) { + return req; } - } while (waited); + } + + return NULL; } +/* + * If there are no intersecting requests return false. Otherwise, wait for the + * first found intersecting request to finish and return true. + */ +static bool coroutine_fn block_copy_wait_one(BlockCopyState *s, int64_t offset, + int64_t bytes) +{ + BlockCopyInFlightReq *req = find_conflicting_inflight_req(s, offset, bytes); + + if (!req) { + return false; + } + + qemu_co_queue_wait(&req->wait_queue, NULL); + + return true; +} + +/* Called only on full-dirty region */ static void block_copy_inflight_req_begin(BlockCopyState *s, BlockCopyInFlightReq *req, - int64_t start, int64_t end) + int64_t offset, int64_t bytes) { - req->start_byte = start; - req->end_byte = end; + assert(!find_conflicting_inflight_req(s, offset, bytes)); + + bdrv_reset_dirty_bitmap(s->copy_bitmap, offset, bytes); + s->in_flight_bytes += bytes; + + req->offset = offset; + req->bytes = bytes; qemu_co_queue_init(&req->wait_queue); QLIST_INSERT_HEAD(&s->inflight_reqs, req, list); } -static void coroutine_fn block_copy_inflight_req_end(BlockCopyInFlightReq *req) +/* + * block_copy_inflight_req_shrink + * + * Drop the tail of the request to be handled later. Set dirty bits back and + * wake up all requests waiting for us (may be some of them are not intersecting + * with shrunk request) + */ +static void coroutine_fn block_copy_inflight_req_shrink(BlockCopyState *s, + BlockCopyInFlightReq *req, int64_t new_bytes) { + if (new_bytes == req->bytes) { + return; + } + + assert(new_bytes > 0 && new_bytes < req->bytes); + + s->in_flight_bytes -= req->bytes - new_bytes; + bdrv_set_dirty_bitmap(s->copy_bitmap, + req->offset + new_bytes, req->bytes - new_bytes); + + req->bytes = new_bytes; + qemu_co_queue_restart_all(&req->wait_queue); +} + +static void coroutine_fn block_copy_inflight_req_end(BlockCopyState *s, + BlockCopyInFlightReq *req, + int ret) +{ + s->in_flight_bytes -= req->bytes; + if (ret < 0) { + bdrv_set_dirty_bitmap(s->copy_bitmap, req->offset, req->bytes); + } QLIST_REMOVE(req, list); qemu_co_queue_restart_all(&req->wait_queue); } @@ -70,16 +169,19 @@ void block_copy_state_free(BlockCopyState *s) g_free(s); } +static uint32_t block_copy_max_transfer(BdrvChild *source, BdrvChild *target) +{ + return MIN_NON_ZERO(INT_MAX, + MIN_NON_ZERO(source->bs->bl.max_transfer, + target->bs->bl.max_transfer)); +} + BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target, int64_t cluster_size, BdrvRequestFlags write_flags, Error **errp) { BlockCopyState *s; BdrvDirtyBitmap *copy_bitmap; - uint32_t max_transfer = - MIN_NON_ZERO(INT_MAX, - MIN_NON_ZERO(source->bs->bl.max_transfer, - target->bs->bl.max_transfer)); copy_bitmap = bdrv_create_dirty_bitmap(source->bs, cluster_size, NULL, errp); @@ -99,7 +201,7 @@ BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target, .mem = shres_create(BLOCK_COPY_MAX_MEM), }; - if (max_transfer < cluster_size) { + if (block_copy_max_transfer(source, target) < cluster_size) { /* * copy_range does not respect max_transfer. We don't want to bother * with requests smaller than block-copy cluster size, so fallback to @@ -114,12 +216,11 @@ BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target, s->copy_size = cluster_size; } else { /* - * copy_range does not respect max_transfer (it's a TODO), so we factor - * that in here. + * We enable copy-range, but keep small copy_size, until first + * successful copy_range (look at block_copy_do_copy). */ s->use_copy_range = true; - s->copy_size = MIN(MAX(cluster_size, BLOCK_COPY_MAX_COPY_RANGE), - QEMU_ALIGN_DOWN(max_transfer, cluster_size)); + s->copy_size = MAX(s->cluster_size, BLOCK_COPY_MAX_BUFFER); } QLIST_INIT(&s->inflight_reqs); @@ -127,48 +228,83 @@ BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target, return s; } -void block_copy_set_callbacks( +void block_copy_set_progress_callback( BlockCopyState *s, ProgressBytesCallbackFunc progress_bytes_callback, - ProgressResetCallbackFunc progress_reset_callback, void *progress_opaque) { s->progress_bytes_callback = progress_bytes_callback; - s->progress_reset_callback = progress_reset_callback; s->progress_opaque = progress_opaque; } +void block_copy_set_progress_meter(BlockCopyState *s, ProgressMeter *pm) +{ + s->progress = pm; +} + /* * block_copy_do_copy * - * Do copy of cluser-aligned chunk. @end is allowed to exceed s->len only to - * cover last cluster when s->len is not aligned to clusters. + * Do copy of cluster-aligned chunk. Requested region is allowed to exceed + * s->len only to cover last cluster when s->len is not aligned to clusters. * * No sync here: nor bitmap neighter intersecting requests handling, only copy. * * Returns 0 on success. */ static int coroutine_fn block_copy_do_copy(BlockCopyState *s, - int64_t start, int64_t end, - bool *error_is_read) + int64_t offset, int64_t bytes, + bool zeroes, bool *error_is_read) { int ret; - int nbytes = MIN(end, s->len) - start; + int64_t nbytes = MIN(offset + bytes, s->len) - offset; void *bounce_buffer = NULL; - assert(QEMU_IS_ALIGNED(start, s->cluster_size)); - assert(QEMU_IS_ALIGNED(end, s->cluster_size)); - assert(end < s->len || end == QEMU_ALIGN_UP(s->len, s->cluster_size)); + assert(offset >= 0 && bytes > 0 && INT64_MAX - offset >= bytes); + assert(QEMU_IS_ALIGNED(offset, s->cluster_size)); + assert(QEMU_IS_ALIGNED(bytes, s->cluster_size)); + assert(offset < s->len); + assert(offset + bytes <= s->len || + offset + bytes == QEMU_ALIGN_UP(s->len, s->cluster_size)); + assert(nbytes < INT_MAX); + + if (zeroes) { + ret = bdrv_co_pwrite_zeroes(s->target, offset, nbytes, s->write_flags & + ~BDRV_REQ_WRITE_COMPRESSED); + if (ret < 0) { + trace_block_copy_write_zeroes_fail(s, offset, ret); + if (error_is_read) { + *error_is_read = false; + } + } + return ret; + } if (s->use_copy_range) { - ret = bdrv_co_copy_range(s->source, start, s->target, start, nbytes, + ret = bdrv_co_copy_range(s->source, offset, s->target, offset, nbytes, 0, s->write_flags); if (ret < 0) { - trace_block_copy_copy_range_fail(s, start, ret); + trace_block_copy_copy_range_fail(s, offset, ret); s->use_copy_range = false; s->copy_size = MAX(s->cluster_size, BLOCK_COPY_MAX_BUFFER); /* Fallback to read+write with allocated buffer */ } else { + if (s->use_copy_range) { + /* + * Successful copy-range. Now increase copy_size. copy_range + * does not respect max_transfer (it's a TODO), so we factor + * that in here. + * + * Note: we double-check s->use_copy_range for the case when + * parallel block-copy request unsets it during previous + * bdrv_co_copy_range call. + */ + s->copy_size = + MIN(MAX(s->cluster_size, BLOCK_COPY_MAX_COPY_RANGE), + QEMU_ALIGN_DOWN(block_copy_max_transfer(s->source, + s->target), + s->cluster_size)); + } goto out; } } @@ -176,24 +312,27 @@ static int coroutine_fn block_copy_do_copy(BlockCopyState *s, /* * In case of failed copy_range request above, we may proceed with buffered * request larger than BLOCK_COPY_MAX_BUFFER. Still, further requests will - * be properly limited, so don't care too much. + * be properly limited, so don't care too much. Moreover the most likely + * case (copy_range is unsupported for the configuration, so the very first + * copy_range request fails) is handled by setting large copy_size only + * after first successful copy_range. */ bounce_buffer = qemu_blockalign(s->source->bs, nbytes); - ret = bdrv_co_pread(s->source, start, nbytes, bounce_buffer, 0); + ret = bdrv_co_pread(s->source, offset, nbytes, bounce_buffer, 0); if (ret < 0) { - trace_block_copy_read_fail(s, start, ret); + trace_block_copy_read_fail(s, offset, ret); if (error_is_read) { *error_is_read = true; } goto out; } - ret = bdrv_co_pwrite(s->target, start, nbytes, bounce_buffer, + ret = bdrv_co_pwrite(s->target, offset, nbytes, bounce_buffer, s->write_flags); if (ret < 0) { - trace_block_copy_write_fail(s, start, ret); + trace_block_copy_write_fail(s, offset, ret); if (error_is_read) { *error_is_read = false; } @@ -206,6 +345,38 @@ out: return ret; } +static int block_copy_block_status(BlockCopyState *s, int64_t offset, + int64_t bytes, int64_t *pnum) +{ + int64_t num; + BlockDriverState *base; + int ret; + + if (s->skip_unallocated && s->source->bs->backing) { + base = s->source->bs->backing->bs; + } else { + base = NULL; + } + + ret = bdrv_block_status_above(s->source->bs, base, offset, bytes, &num, + NULL, NULL); + if (ret < 0 || num < s->cluster_size) { + /* + * On error or if failed to obtain large enough chunk just fallback to + * copy one cluster. + */ + num = s->cluster_size; + ret = BDRV_BLOCK_ALLOCATED | BDRV_BLOCK_DATA; + } else if (offset + num == s->len) { + num = QEMU_ALIGN_UP(num, s->cluster_size); + } else { + num = QEMU_ALIGN_DOWN(num, s->cluster_size); + } + + *pnum = num; + return ret; +} + /* * Check if the cluster starting at offset is allocated or not. * return via pnum the number of contiguous clusters sharing this allocation. @@ -269,21 +440,28 @@ int64_t block_copy_reset_unallocated(BlockCopyState *s, if (!ret) { bdrv_reset_dirty_bitmap(s->copy_bitmap, offset, bytes); - s->progress_reset_callback(s->progress_opaque); + progress_set_remaining(s->progress, + bdrv_get_dirty_count(s->copy_bitmap) + + s->in_flight_bytes); } *count = bytes; return ret; } -int coroutine_fn block_copy(BlockCopyState *s, - int64_t start, uint64_t bytes, - bool *error_is_read) +/* + * block_copy_dirty_clusters + * + * Copy dirty clusters in @offset/@bytes range. + * Returns 1 if dirty clusters found and successfully copied, 0 if no dirty + * clusters found and -errno on failure. + */ +static int coroutine_fn block_copy_dirty_clusters(BlockCopyState *s, + int64_t offset, int64_t bytes, + bool *error_is_read) { int ret = 0; - int64_t end = bytes + start; /* bytes */ - int64_t status_bytes; - BlockCopyInFlightReq req; + bool found_dirty = false; /* * block_copy() user is responsible for keeping source and target in same @@ -292,60 +470,109 @@ int coroutine_fn block_copy(BlockCopyState *s, assert(bdrv_get_aio_context(s->source->bs) == bdrv_get_aio_context(s->target->bs)); - assert(QEMU_IS_ALIGNED(start, s->cluster_size)); - assert(QEMU_IS_ALIGNED(end, s->cluster_size)); + assert(QEMU_IS_ALIGNED(offset, s->cluster_size)); + assert(QEMU_IS_ALIGNED(bytes, s->cluster_size)); - block_copy_wait_inflight_reqs(s, start, bytes); - block_copy_inflight_req_begin(s, &req, start, end); + while (bytes) { + BlockCopyInFlightReq req; + int64_t next_zero, cur_bytes, status_bytes; - while (start < end) { - int64_t next_zero, chunk_end; - - if (!bdrv_dirty_bitmap_get(s->copy_bitmap, start)) { - trace_block_copy_skip(s, start); - start += s->cluster_size; + if (!bdrv_dirty_bitmap_get(s->copy_bitmap, offset)) { + trace_block_copy_skip(s, offset); + offset += s->cluster_size; + bytes -= s->cluster_size; continue; /* already copied */ } - chunk_end = MIN(end, start + s->copy_size); + found_dirty = true; - next_zero = bdrv_dirty_bitmap_next_zero(s->copy_bitmap, start, - chunk_end - start); + cur_bytes = MIN(bytes, s->copy_size); + + next_zero = bdrv_dirty_bitmap_next_zero(s->copy_bitmap, offset, + cur_bytes); if (next_zero >= 0) { - assert(next_zero > start); /* start is dirty */ - assert(next_zero < chunk_end); /* no need to do MIN() */ - chunk_end = next_zero; + assert(next_zero > offset); /* offset is dirty */ + assert(next_zero < offset + cur_bytes); /* no need to do MIN() */ + cur_bytes = next_zero - offset; + } + block_copy_inflight_req_begin(s, &req, offset, cur_bytes); + + ret = block_copy_block_status(s, offset, cur_bytes, &status_bytes); + assert(ret >= 0); /* never fail */ + cur_bytes = MIN(cur_bytes, status_bytes); + block_copy_inflight_req_shrink(s, &req, cur_bytes); + if (s->skip_unallocated && !(ret & BDRV_BLOCK_ALLOCATED)) { + block_copy_inflight_req_end(s, &req, 0); + progress_set_remaining(s->progress, + bdrv_get_dirty_count(s->copy_bitmap) + + s->in_flight_bytes); + trace_block_copy_skip_range(s, offset, status_bytes); + offset += status_bytes; + bytes -= status_bytes; + continue; } - if (s->skip_unallocated) { - ret = block_copy_reset_unallocated(s, start, &status_bytes); - if (ret == 0) { - trace_block_copy_skip_range(s, start, status_bytes); - start += status_bytes; - continue; - } - /* Clamp to known allocated region */ - chunk_end = MIN(chunk_end, start + status_bytes); - } + trace_block_copy_process(s, offset); - trace_block_copy_process(s, start); - - bdrv_reset_dirty_bitmap(s->copy_bitmap, start, chunk_end - start); - - co_get_from_shres(s->mem, chunk_end - start); - ret = block_copy_do_copy(s, start, chunk_end, error_is_read); - co_put_to_shres(s->mem, chunk_end - start); + co_get_from_shres(s->mem, cur_bytes); + ret = block_copy_do_copy(s, offset, cur_bytes, ret & BDRV_BLOCK_ZERO, + error_is_read); + co_put_to_shres(s->mem, cur_bytes); + block_copy_inflight_req_end(s, &req, ret); if (ret < 0) { - bdrv_set_dirty_bitmap(s->copy_bitmap, start, chunk_end - start); - break; + return ret; } - s->progress_bytes_callback(chunk_end - start, s->progress_opaque); - start = chunk_end; - ret = 0; + progress_work_done(s->progress, cur_bytes); + s->progress_bytes_callback(cur_bytes, s->progress_opaque); + offset += cur_bytes; + bytes -= cur_bytes; } - block_copy_inflight_req_end(&req); + return found_dirty; +} + +/* + * block_copy + * + * Copy requested region, accordingly to dirty bitmap. + * Collaborate with parallel block_copy requests: if they succeed it will help + * us. If they fail, we will retry not-copied regions. So, if we return error, + * it means that some I/O operation failed in context of _this_ block_copy call, + * not some parallel operation. + */ +int coroutine_fn block_copy(BlockCopyState *s, int64_t offset, int64_t bytes, + bool *error_is_read) +{ + int ret; + + do { + ret = block_copy_dirty_clusters(s, offset, bytes, error_is_read); + + if (ret == 0) { + ret = block_copy_wait_one(s, offset, bytes); + } + + /* + * We retry in two cases: + * 1. Some progress done + * Something was copied, which means that there were yield points + * and some new dirty bits may have appeared (due to failed parallel + * block-copy requests). + * 2. We have waited for some intersecting block-copy request + * It may have failed and produced new dirty bits. + */ + } while (ret > 0); return ret; } + +BdrvDirtyBitmap *block_copy_dirty_bitmap(BlockCopyState *s) +{ + return s->copy_bitmap; +} + +void block_copy_set_skip_unallocated(BlockCopyState *s, bool skip) +{ + s->skip_unallocated = skip; +} diff --git a/block/commit.c b/block/commit.c index 23c90b3b91..8e672799af 100644 --- a/block/commit.c +++ b/block/commit.c @@ -43,27 +43,6 @@ typedef struct CommitBlockJob { char *backing_file_str; } CommitBlockJob; -static int coroutine_fn commit_populate(BlockBackend *bs, BlockBackend *base, - int64_t offset, uint64_t bytes, - void *buf) -{ - int ret = 0; - - assert(bytes < SIZE_MAX); - - ret = blk_co_pread(bs, offset, bytes, buf, 0); - if (ret < 0) { - return ret; - } - - ret = blk_co_pwrite(base, offset, bytes, buf, 0); - if (ret < 0) { - return ret; - } - - return 0; -} - static int commit_prepare(Job *job) { CommitBlockJob *s = container_of(job, CommitBlockJob, common.job); @@ -140,7 +119,6 @@ static int coroutine_fn commit_run(Job *job, Error **errp) int ret = 0; int64_t n = 0; /* bytes */ void *buf = NULL; - int bytes_written = 0; int64_t len, base_len; ret = len = blk_getlength(s->top); @@ -165,6 +143,7 @@ static int coroutine_fn commit_run(Job *job, Error **errp) for (offset = 0; offset < len; offset += n) { bool copy; + bool error_in_source = true; /* Note that even when no rate limit is applied we need to yield * with no pending I/O here so that bdrv_drain_all() returns. @@ -179,12 +158,20 @@ static int coroutine_fn commit_run(Job *job, Error **errp) copy = (ret == 1); trace_commit_one_iteration(s, offset, n, ret); if (copy) { - ret = commit_populate(s->top, s->base, offset, n, buf); - bytes_written += n; + assert(n < SIZE_MAX); + + ret = blk_co_pread(s->top, offset, n, buf, 0); + if (ret >= 0) { + ret = blk_co_pwrite(s->base, offset, n, buf, 0); + if (ret < 0) { + error_in_source = false; + } + } } if (ret < 0) { BlockErrorAction action = - block_job_error_action(&s->common, false, s->on_error, -ret); + block_job_error_action(&s->common, s->on_error, + error_in_source, -ret); if (action == BLOCK_ERROR_ACTION_REPORT) { goto out; } else { diff --git a/block/copy-on-read.c b/block/copy-on-read.c index e95223d3cb..242d3ff055 100644 --- a/block/copy-on-read.c +++ b/block/copy-on-read.c @@ -118,13 +118,6 @@ static void cor_lock_medium(BlockDriverState *bs, bool locked) } -static bool cor_recurse_is_first_non_filter(BlockDriverState *bs, - BlockDriverState *candidate) -{ - return bdrv_recurse_is_first_non_filter(bs->file->bs, candidate); -} - - static BlockDriver bdrv_copy_on_read = { .format_name = "copy-on-read", @@ -143,8 +136,6 @@ static BlockDriver bdrv_copy_on_read = { .bdrv_co_block_status = bdrv_co_block_status_from_file, - .bdrv_recurse_is_first_non_filter = cor_recurse_is_first_non_filter, - .has_variable_length = true, .is_filter = true, }; diff --git a/block/crypto.c b/block/crypto.c index 24823835c1..d577f89659 100644 --- a/block/crypto.c +++ b/block/crypto.c @@ -30,6 +30,7 @@ #include "qapi/error.h" #include "qemu/module.h" #include "qemu/option.h" +#include "qemu/cutils.h" #include "crypto.h" typedef struct BlockCrypto BlockCrypto; @@ -484,6 +485,67 @@ static int64_t block_crypto_getlength(BlockDriverState *bs) } +static BlockMeasureInfo *block_crypto_measure(QemuOpts *opts, + BlockDriverState *in_bs, + Error **errp) +{ + g_autoptr(QCryptoBlockCreateOptions) create_opts = NULL; + Error *local_err = NULL; + BlockMeasureInfo *info; + uint64_t size; + size_t luks_payload_size; + QDict *cryptoopts; + + /* + * Preallocation mode doesn't affect size requirements but we must consume + * the option. + */ + g_free(qemu_opt_get_del(opts, BLOCK_OPT_PREALLOC)); + + size = qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0); + + if (in_bs) { + int64_t ssize = bdrv_getlength(in_bs); + + if (ssize < 0) { + error_setg_errno(&local_err, -ssize, + "Unable to get image virtual_size"); + goto err; + } + + size = ssize; + } + + cryptoopts = qemu_opts_to_qdict_filtered(opts, NULL, + &block_crypto_create_opts_luks, true); + qdict_put_str(cryptoopts, "format", "luks"); + create_opts = block_crypto_create_opts_init(cryptoopts, &local_err); + qobject_unref(cryptoopts); + if (!create_opts) { + goto err; + } + + if (!qcrypto_block_calculate_payload_offset(create_opts, NULL, + &luks_payload_size, + &local_err)) { + goto err; + } + + /* + * Unallocated blocks are still encrypted so allocation status makes no + * difference to the file size. + */ + info = g_new(BlockMeasureInfo, 1); + info->fully_allocated = luks_payload_size + size; + info->required = luks_payload_size + size; + return info; + +err: + error_propagate(errp, local_err); + return NULL; +} + + static int block_crypto_probe_luks(const uint8_t *buf, int buf_size, const char *filename) { @@ -539,7 +601,8 @@ fail: return ret; } -static int coroutine_fn block_crypto_co_create_opts_luks(const char *filename, +static int coroutine_fn block_crypto_co_create_opts_luks(BlockDriver *drv, + const char *filename, QemuOpts *opts, Error **errp) { @@ -596,6 +659,23 @@ static int coroutine_fn block_crypto_co_create_opts_luks(const char *filename, ret = 0; fail: + /* + * If an error occurred, delete 'filename'. Even if the file existed + * beforehand, it has been truncated and corrupted in the process. + */ + if (ret && bs) { + Error *local_delete_err = NULL; + int r_del = bdrv_co_delete_file(bs, &local_delete_err); + /* + * ENOTSUP will happen if the block driver doesn't support + * the 'bdrv_co_delete_file' interface. This is a predictable + * scenario and shouldn't be reported back to the user. + */ + if ((r_del < 0) && (r_del != -ENOTSUP)) { + error_report_err(local_delete_err); + } + } + bdrv_unref(bs); qapi_free_QCryptoBlockCreateOptions(create_opts); qobject_unref(cryptoopts); @@ -670,6 +750,7 @@ static BlockDriver bdrv_crypto_luks = { .bdrv_co_preadv = block_crypto_co_preadv, .bdrv_co_pwritev = block_crypto_co_pwritev, .bdrv_getlength = block_crypto_getlength, + .bdrv_measure = block_crypto_measure, .bdrv_get_info = block_crypto_get_info_luks, .bdrv_get_specific_info = block_crypto_get_specific_info_luks, diff --git a/block/curl.c b/block/curl.c index f86299378e..6e325901dc 100644 --- a/block/curl.c +++ b/block/curl.c @@ -214,11 +214,35 @@ static size_t curl_header_cb(void *ptr, size_t size, size_t nmemb, void *opaque) { BDRVCURLState *s = opaque; size_t realsize = size * nmemb; - const char *accept_line = "Accept-Ranges: bytes"; + const char *header = (char *)ptr; + const char *end = header + realsize; + const char *accept_ranges = "accept-ranges:"; + const char *bytes = "bytes"; - if (realsize >= strlen(accept_line) - && strncmp((char *)ptr, accept_line, strlen(accept_line)) == 0) { - s->accept_range = true; + if (realsize >= strlen(accept_ranges) + && g_ascii_strncasecmp(header, accept_ranges, + strlen(accept_ranges)) == 0) { + + char *p = strchr(header, ':') + 1; + + /* Skip whitespace between the header name and value. */ + while (p < end && *p && g_ascii_isspace(*p)) { + p++; + } + + if (end - p >= strlen(bytes) + && strncmp(p, bytes, strlen(bytes)) == 0) { + + /* Check that there is nothing but whitespace after the value. */ + p += strlen(bytes); + while (p < end && *p && g_ascii_isspace(*p)) { + p++; + } + + if (p == end || !*p) { + s->accept_range = true; + } + } } return realsize; diff --git a/block/dirty-bitmap.c b/block/dirty-bitmap.c index 7039e82520..063793e316 100644 --- a/block/dirty-bitmap.c +++ b/block/dirty-bitmap.c @@ -860,16 +860,24 @@ char *bdrv_dirty_bitmap_sha256(const BdrvDirtyBitmap *bitmap, Error **errp) return hbitmap_sha256(bitmap->bitmap, errp); } -int64_t bdrv_dirty_bitmap_next_zero(BdrvDirtyBitmap *bitmap, uint64_t offset, - uint64_t bytes) +int64_t bdrv_dirty_bitmap_next_dirty(BdrvDirtyBitmap *bitmap, int64_t offset, + int64_t bytes) +{ + return hbitmap_next_dirty(bitmap->bitmap, offset, bytes); +} + +int64_t bdrv_dirty_bitmap_next_zero(BdrvDirtyBitmap *bitmap, int64_t offset, + int64_t bytes) { return hbitmap_next_zero(bitmap->bitmap, offset, bytes); } bool bdrv_dirty_bitmap_next_dirty_area(BdrvDirtyBitmap *bitmap, - uint64_t *offset, uint64_t *bytes) + int64_t start, int64_t end, int64_t max_dirty_count, + int64_t *dirty_start, int64_t *dirty_count) { - return hbitmap_next_dirty_area(bitmap->bitmap, offset, bytes); + return hbitmap_next_dirty_area(bitmap->bitmap, start, end, max_dirty_count, + dirty_start, dirty_count); } /** diff --git a/block/file-posix.c b/block/file-posix.c index 1b805bd938..7e19bbff5f 100644 --- a/block/file-posix.c +++ b/block/file-posix.c @@ -156,6 +156,7 @@ typedef struct BDRVRawState { bool has_write_zeroes:1; bool discard_zeroes:1; bool use_linux_aio:1; + bool use_linux_io_uring:1; bool page_cache_inconsistent:1; bool has_fallocate; bool needs_alignment; @@ -444,7 +445,7 @@ static QemuOptsList raw_runtime_opts = { { .name = "aio", .type = QEMU_OPT_STRING, - .help = "host AIO implementation (threads, native)", + .help = "host AIO implementation (threads, native, io_uring)", }, { .name = "locking", @@ -503,9 +504,16 @@ static int raw_open_common(BlockDriverState *bs, QDict *options, goto fail; } - aio_default = (bdrv_flags & BDRV_O_NATIVE_AIO) - ? BLOCKDEV_AIO_OPTIONS_NATIVE - : BLOCKDEV_AIO_OPTIONS_THREADS; + if (bdrv_flags & BDRV_O_NATIVE_AIO) { + aio_default = BLOCKDEV_AIO_OPTIONS_NATIVE; +#ifdef CONFIG_LINUX_IO_URING + } else if (bdrv_flags & BDRV_O_IO_URING) { + aio_default = BLOCKDEV_AIO_OPTIONS_IO_URING; +#endif + } else { + aio_default = BLOCKDEV_AIO_OPTIONS_THREADS; + } + aio = qapi_enum_parse(&BlockdevAioOptions_lookup, qemu_opt_get(opts, "aio"), aio_default, &local_err); @@ -514,7 +522,11 @@ static int raw_open_common(BlockDriverState *bs, QDict *options, ret = -EINVAL; goto fail; } + s->use_linux_aio = (aio == BLOCKDEV_AIO_OPTIONS_NATIVE); +#ifdef CONFIG_LINUX_IO_URING + s->use_linux_io_uring = (aio == BLOCKDEV_AIO_OPTIONS_IO_URING); +#endif locking = qapi_enum_parse(&OnOffAuto_lookup, qemu_opt_get(opts, "locking"), @@ -600,6 +612,22 @@ static int raw_open_common(BlockDriverState *bs, QDict *options, } #endif /* !defined(CONFIG_LINUX_AIO) */ +#ifdef CONFIG_LINUX_IO_URING + if (s->use_linux_io_uring) { + if (!aio_setup_linux_io_uring(bdrv_get_aio_context(bs), errp)) { + error_prepend(errp, "Unable to use io_uring: "); + goto fail; + } + } +#else + if (s->use_linux_io_uring) { + error_setg(errp, "aio=io_uring was specified, but is not supported " + "in this build."); + ret = -EINVAL; + goto fail; + } +#endif /* !defined(CONFIG_LINUX_IO_URING) */ + s->has_discard = true; s->has_write_zeroes = true; if ((bs->open_flags & BDRV_O_NOCACHE) != 0) { @@ -860,7 +888,6 @@ static int raw_handle_perm_lock(BlockDriverState *bs, "Is another process using the image [%s]?\n", bs->filename); } - op = RAW_PL_ABORT; /* fall through to unlock bytes. */ case RAW_PL_ABORT: raw_apply_lock_bytes(s, s->fd, s->perm, ~s->shared_perm, @@ -1877,21 +1904,25 @@ static int coroutine_fn raw_co_prw(BlockDriverState *bs, uint64_t offset, return -EIO; /* - * Check if the underlying device requires requests to be aligned, - * and if the request we are trying to submit is aligned or not. - * If this is the case tell the low-level driver that it needs - * to copy the buffer. + * When using O_DIRECT, the request must be aligned to be able to use + * either libaio or io_uring interface. If not fail back to regular thread + * pool read/write code which emulates this for us if we + * set QEMU_AIO_MISALIGNED. */ - if (s->needs_alignment) { - if (!bdrv_qiov_is_aligned(bs, qiov)) { - type |= QEMU_AIO_MISALIGNED; -#ifdef CONFIG_LINUX_AIO - } else if (s->use_linux_aio) { - LinuxAioState *aio = aio_get_linux_aio(bdrv_get_aio_context(bs)); - assert(qiov->size == bytes); - return laio_co_submit(bs, aio, s->fd, offset, qiov, type); + if (s->needs_alignment && !bdrv_qiov_is_aligned(bs, qiov)) { + type |= QEMU_AIO_MISALIGNED; +#ifdef CONFIG_LINUX_IO_URING + } else if (s->use_linux_io_uring) { + LuringState *aio = aio_get_linux_io_uring(bdrv_get_aio_context(bs)); + assert(qiov->size == bytes); + return luring_co_submit(bs, aio, s->fd, offset, qiov, type); +#endif +#ifdef CONFIG_LINUX_AIO + } else if (s->use_linux_aio) { + LinuxAioState *aio = aio_get_linux_aio(bdrv_get_aio_context(bs)); + assert(qiov->size == bytes); + return laio_co_submit(bs, aio, s->fd, offset, qiov, type); #endif - } } acb = (RawPosixAIOData) { @@ -1927,24 +1958,36 @@ static int coroutine_fn raw_co_pwritev(BlockDriverState *bs, uint64_t offset, static void raw_aio_plug(BlockDriverState *bs) { + BDRVRawState __attribute__((unused)) *s = bs->opaque; #ifdef CONFIG_LINUX_AIO - BDRVRawState *s = bs->opaque; if (s->use_linux_aio) { LinuxAioState *aio = aio_get_linux_aio(bdrv_get_aio_context(bs)); laio_io_plug(bs, aio); } #endif +#ifdef CONFIG_LINUX_IO_URING + if (s->use_linux_io_uring) { + LuringState *aio = aio_get_linux_io_uring(bdrv_get_aio_context(bs)); + luring_io_plug(bs, aio); + } +#endif } static void raw_aio_unplug(BlockDriverState *bs) { + BDRVRawState __attribute__((unused)) *s = bs->opaque; #ifdef CONFIG_LINUX_AIO - BDRVRawState *s = bs->opaque; if (s->use_linux_aio) { LinuxAioState *aio = aio_get_linux_aio(bdrv_get_aio_context(bs)); laio_io_unplug(bs, aio); } #endif +#ifdef CONFIG_LINUX_IO_URING + if (s->use_linux_io_uring) { + LuringState *aio = aio_get_linux_io_uring(bdrv_get_aio_context(bs)); + luring_io_unplug(bs, aio); + } +#endif } static int raw_co_flush_to_disk(BlockDriverState *bs) @@ -1964,14 +2007,20 @@ static int raw_co_flush_to_disk(BlockDriverState *bs) .aio_type = QEMU_AIO_FLUSH, }; +#ifdef CONFIG_LINUX_IO_URING + if (s->use_linux_io_uring) { + LuringState *aio = aio_get_linux_io_uring(bdrv_get_aio_context(bs)); + return luring_co_submit(bs, aio, s->fd, 0, NULL, QEMU_AIO_FLUSH); + } +#endif return raw_thread_pool_submit(bs, handle_aiocb_flush, &acb); } static void raw_aio_attach_aio_context(BlockDriverState *bs, AioContext *new_context) { + BDRVRawState __attribute__((unused)) *s = bs->opaque; #ifdef CONFIG_LINUX_AIO - BDRVRawState *s = bs->opaque; if (s->use_linux_aio) { Error *local_err = NULL; if (!aio_setup_linux_aio(new_context, &local_err)) { @@ -1981,6 +2030,16 @@ static void raw_aio_attach_aio_context(BlockDriverState *bs, } } #endif +#ifdef CONFIG_LINUX_IO_URING + if (s->use_linux_io_uring) { + Error *local_err; + if (!aio_setup_linux_io_uring(new_context, &local_err)) { + error_reportf_err(local_err, "Unable to use linux io_uring, " + "falling back to thread pool: "); + s->use_linux_io_uring = false; + } + } +#endif } static void raw_close(BlockDriverState *bs) @@ -2346,7 +2405,9 @@ out: return result; } -static int coroutine_fn raw_co_create_opts(const char *filename, QemuOpts *opts, +static int coroutine_fn raw_co_create_opts(BlockDriver *drv, + const char *filename, + QemuOpts *opts, Error **errp) { BlockdevCreateOptions options; @@ -2386,6 +2447,28 @@ static int coroutine_fn raw_co_create_opts(const char *filename, QemuOpts *opts, return raw_co_create(&options, errp); } +static int coroutine_fn raw_co_delete_file(BlockDriverState *bs, + Error **errp) +{ + struct stat st; + int ret; + + if (!(stat(bs->filename, &st) == 0) || !S_ISREG(st.st_mode)) { + error_setg_errno(errp, ENOENT, "%s is not a regular file", + bs->filename); + return -ENOENT; + } + + ret = unlink(bs->filename); + if (ret < 0) { + ret = -errno; + error_setg_errno(errp, -ret, "Error when deleting file %s", + bs->filename); + } + + return ret; +} + /* * Find allocation range in @bs around offset @start. * May change underlying file descriptor's file offset. @@ -2753,7 +2836,6 @@ raw_do_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int bytes, req->overlap_bytes = req->bytes; bdrv_mark_request_serialising(req, bs->bl.request_alignment); - bdrv_wait_serialising_requests(req); } #endif @@ -3017,6 +3099,7 @@ BlockDriver bdrv_file = { .bdrv_co_block_status = raw_co_block_status, .bdrv_co_invalidate_cache = raw_co_invalidate_cache, .bdrv_co_pwrite_zeroes = raw_co_pwrite_zeroes, + .bdrv_co_delete_file = raw_co_delete_file, .bdrv_co_preadv = raw_co_preadv, .bdrv_co_pwritev = raw_co_pwritev, @@ -3418,67 +3501,6 @@ static coroutine_fn int hdev_co_pwrite_zeroes(BlockDriverState *bs, return raw_do_pwrite_zeroes(bs, offset, bytes, flags, true); } -static int coroutine_fn hdev_co_create_opts(const char *filename, QemuOpts *opts, - Error **errp) -{ - int fd; - int ret = 0; - struct stat stat_buf; - int64_t total_size = 0; - bool has_prefix; - - /* This function is used by both protocol block drivers and therefore either - * of these prefixes may be given. - * The return value has to be stored somewhere, otherwise this is an error - * due to -Werror=unused-value. */ - has_prefix = - strstart(filename, "host_device:", &filename) || - strstart(filename, "host_cdrom:" , &filename); - - (void)has_prefix; - - ret = raw_normalize_devicepath(&filename, errp); - if (ret < 0) { - return ret; - } - - /* Read out options */ - total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), - BDRV_SECTOR_SIZE); - - fd = qemu_open(filename, O_WRONLY | O_BINARY); - if (fd < 0) { - ret = -errno; - error_setg_errno(errp, -ret, "Could not open device"); - return ret; - } - - if (fstat(fd, &stat_buf) < 0) { - ret = -errno; - error_setg_errno(errp, -ret, "Could not stat device"); - } else if (!S_ISBLK(stat_buf.st_mode) && !S_ISCHR(stat_buf.st_mode)) { - error_setg(errp, - "The given file is neither a block nor a character device"); - ret = -ENODEV; - } else if (lseek(fd, 0, SEEK_END) < total_size) { - error_setg(errp, "Device is too small"); - ret = -ENOSPC; - } - - if (!ret && total_size) { - uint8_t buf[BDRV_SECTOR_SIZE] = { 0 }; - int64_t zero_size = MIN(BDRV_SECTOR_SIZE, total_size); - if (lseek(fd, 0, SEEK_SET) == -1) { - ret = -errno; - } else { - ret = qemu_write_full(fd, buf, zero_size); - ret = ret == zero_size ? 0 : -errno; - } - } - qemu_close(fd); - return ret; -} - static BlockDriver bdrv_host_device = { .format_name = "host_device", .protocol_name = "host_device", @@ -3491,8 +3513,8 @@ static BlockDriver bdrv_host_device = { .bdrv_reopen_prepare = raw_reopen_prepare, .bdrv_reopen_commit = raw_reopen_commit, .bdrv_reopen_abort = raw_reopen_abort, - .bdrv_co_create_opts = hdev_co_create_opts, - .create_opts = &raw_create_opts, + .bdrv_co_create_opts = bdrv_co_create_opts_simple, + .create_opts = &bdrv_create_opts_simple, .mutable_opts = mutable_opts, .bdrv_co_invalidate_cache = raw_co_invalidate_cache, .bdrv_co_pwrite_zeroes = hdev_co_pwrite_zeroes, @@ -3619,12 +3641,11 @@ static BlockDriver bdrv_host_cdrom = { .bdrv_reopen_prepare = raw_reopen_prepare, .bdrv_reopen_commit = raw_reopen_commit, .bdrv_reopen_abort = raw_reopen_abort, - .bdrv_co_create_opts = hdev_co_create_opts, - .create_opts = &raw_create_opts, + .bdrv_co_create_opts = bdrv_co_create_opts_simple, + .create_opts = &bdrv_create_opts_simple, .mutable_opts = mutable_opts, .bdrv_co_invalidate_cache = raw_co_invalidate_cache, - .bdrv_co_preadv = raw_co_preadv, .bdrv_co_pwritev = raw_co_pwritev, .bdrv_co_flush_to_disk = raw_co_flush_to_disk, @@ -3753,8 +3774,8 @@ static BlockDriver bdrv_host_cdrom = { .bdrv_reopen_prepare = raw_reopen_prepare, .bdrv_reopen_commit = raw_reopen_commit, .bdrv_reopen_abort = raw_reopen_abort, - .bdrv_co_create_opts = hdev_co_create_opts, - .create_opts = &raw_create_opts, + .bdrv_co_create_opts = bdrv_co_create_opts_simple, + .create_opts = &bdrv_create_opts_simple, .mutable_opts = mutable_opts, .bdrv_co_preadv = raw_co_preadv, diff --git a/block/file-win32.c b/block/file-win32.c index 77e8ff7b68..15859839a1 100644 --- a/block/file-win32.c +++ b/block/file-win32.c @@ -588,7 +588,9 @@ static int raw_co_create(BlockdevCreateOptions *options, Error **errp) return 0; } -static int coroutine_fn raw_co_create_opts(const char *filename, QemuOpts *opts, +static int coroutine_fn raw_co_create_opts(BlockDriver *drv, + const char *filename, + QemuOpts *opts, Error **errp) { BlockdevCreateOptions options; diff --git a/block/filter-compress.c b/block/filter-compress.c new file mode 100644 index 0000000000..82c315b298 --- /dev/null +++ b/block/filter-compress.c @@ -0,0 +1,159 @@ +/* + * Compress filter block driver + * + * Copyright (c) 2019 Virtuozzo International GmbH + * + * Author: + * Andrey Shinkevich + * (based on block/copy-on-read.c by Max Reitz) + * + * 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 or + * (at your option) any later version of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#include "qemu/osdep.h" +#include "block/block_int.h" +#include "qemu/module.h" +#include "qapi/error.h" + + +static int compress_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) +{ + bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, false, + errp); + if (!bs->file) { + return -EINVAL; + } + + if (!bs->file->bs->drv || !block_driver_can_compress(bs->file->bs->drv)) { + error_setg(errp, + "Compression is not supported for underlying format: %s", + bdrv_get_format_name(bs->file->bs) ?: "(no format)"); + + return -ENOTSUP; + } + + bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED | + (BDRV_REQ_FUA & bs->file->bs->supported_write_flags); + + bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED | + ((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK) & + bs->file->bs->supported_zero_flags); + + return 0; +} + + +static int64_t compress_getlength(BlockDriverState *bs) +{ + return bdrv_getlength(bs->file->bs); +} + + +static int coroutine_fn compress_co_preadv_part(BlockDriverState *bs, + uint64_t offset, uint64_t bytes, + QEMUIOVector *qiov, + size_t qiov_offset, + int flags) +{ + return bdrv_co_preadv_part(bs->file, offset, bytes, qiov, qiov_offset, + flags); +} + + +static int coroutine_fn compress_co_pwritev_part(BlockDriverState *bs, + uint64_t offset, + uint64_t bytes, + QEMUIOVector *qiov, + size_t qiov_offset, int flags) +{ + return bdrv_co_pwritev_part(bs->file, offset, bytes, qiov, qiov_offset, + flags | BDRV_REQ_WRITE_COMPRESSED); +} + + +static int coroutine_fn compress_co_pwrite_zeroes(BlockDriverState *bs, + int64_t offset, int bytes, + BdrvRequestFlags flags) +{ + return bdrv_co_pwrite_zeroes(bs->file, offset, bytes, flags); +} + + +static int coroutine_fn compress_co_pdiscard(BlockDriverState *bs, + int64_t offset, int bytes) +{ + return bdrv_co_pdiscard(bs->file, offset, bytes); +} + + +static void compress_refresh_limits(BlockDriverState *bs, Error **errp) +{ + BlockDriverInfo bdi; + int ret; + + if (!bs->file) { + return; + } + + ret = bdrv_get_info(bs->file->bs, &bdi); + if (ret < 0 || bdi.cluster_size == 0) { + return; + } + + bs->bl.request_alignment = bdi.cluster_size; +} + + +static void compress_eject(BlockDriverState *bs, bool eject_flag) +{ + bdrv_eject(bs->file->bs, eject_flag); +} + + +static void compress_lock_medium(BlockDriverState *bs, bool locked) +{ + bdrv_lock_medium(bs->file->bs, locked); +} + + +static BlockDriver bdrv_compress = { + .format_name = "compress", + + .bdrv_open = compress_open, + .bdrv_child_perm = bdrv_filter_default_perms, + + .bdrv_getlength = compress_getlength, + + .bdrv_co_preadv_part = compress_co_preadv_part, + .bdrv_co_pwritev_part = compress_co_pwritev_part, + .bdrv_co_pwrite_zeroes = compress_co_pwrite_zeroes, + .bdrv_co_pdiscard = compress_co_pdiscard, + .bdrv_refresh_limits = compress_refresh_limits, + + .bdrv_eject = compress_eject, + .bdrv_lock_medium = compress_lock_medium, + + .bdrv_co_block_status = bdrv_co_block_status_from_file, + + .has_variable_length = true, + .is_filter = true, +}; + +static void bdrv_compress_init(void) +{ + bdrv_register(&bdrv_compress); +} + +block_init(bdrv_compress_init); diff --git a/block/gluster.c b/block/gluster.c index 4fa4a77a47..0aa1f2cda4 100644 --- a/block/gluster.c +++ b/block/gluster.c @@ -1130,7 +1130,8 @@ out: return ret; } -static int coroutine_fn qemu_gluster_co_create_opts(const char *filename, +static int coroutine_fn qemu_gluster_co_create_opts(BlockDriver *drv, + const char *filename, QemuOpts *opts, Error **errp) { diff --git a/block/io.c b/block/io.c index f75777f5ea..aba67f66b9 100644 --- a/block/io.c +++ b/block/io.c @@ -715,12 +715,69 @@ static void tracked_request_begin(BdrvTrackedRequest *req, qemu_co_mutex_unlock(&bs->reqs_lock); } -void bdrv_mark_request_serialising(BdrvTrackedRequest *req, uint64_t align) +static bool tracked_request_overlaps(BdrvTrackedRequest *req, + int64_t offset, uint64_t bytes) { + /* aaaa bbbb */ + if (offset >= req->overlap_offset + req->overlap_bytes) { + return false; + } + /* bbbb aaaa */ + if (req->overlap_offset >= offset + bytes) { + return false; + } + return true; +} + +static bool coroutine_fn +bdrv_wait_serialising_requests_locked(BlockDriverState *bs, + BdrvTrackedRequest *self) +{ + BdrvTrackedRequest *req; + bool retry; + bool waited = false; + + do { + retry = false; + QLIST_FOREACH(req, &bs->tracked_requests, list) { + if (req == self || (!req->serialising && !self->serialising)) { + continue; + } + if (tracked_request_overlaps(req, self->overlap_offset, + self->overlap_bytes)) + { + /* Hitting this means there was a reentrant request, for + * example, a block driver issuing nested requests. This must + * never happen since it means deadlock. + */ + assert(qemu_coroutine_self() != req->co); + + /* If the request is already (indirectly) waiting for us, or + * will wait for us as soon as it wakes up, then just go on + * (instead of producing a deadlock in the former case). */ + if (!req->waiting_for) { + self->waiting_for = req; + qemu_co_queue_wait(&req->wait_queue, &bs->reqs_lock); + self->waiting_for = NULL; + retry = true; + waited = true; + break; + } + } + } + } while (retry); + return waited; +} + +bool bdrv_mark_request_serialising(BdrvTrackedRequest *req, uint64_t align) +{ + BlockDriverState *bs = req->bs; int64_t overlap_offset = req->offset & ~(align - 1); uint64_t overlap_bytes = ROUND_UP(req->offset + req->bytes, align) - overlap_offset; + bool waited; + qemu_co_mutex_lock(&bs->reqs_lock); if (!req->serialising) { atomic_inc(&req->bs->serialising_in_flight); req->serialising = true; @@ -728,18 +785,9 @@ void bdrv_mark_request_serialising(BdrvTrackedRequest *req, uint64_t align) req->overlap_offset = MIN(req->overlap_offset, overlap_offset); req->overlap_bytes = MAX(req->overlap_bytes, overlap_bytes); -} - -static bool is_request_serialising_and_aligned(BdrvTrackedRequest *req) -{ - /* - * If the request is serialising, overlap_offset and overlap_bytes are set, - * so we can check if the request is aligned. Otherwise, don't care and - * return false. - */ - - return req->serialising && (req->offset == req->overlap_offset) && - (req->bytes == req->overlap_bytes); + waited = bdrv_wait_serialising_requests_locked(bs, req); + qemu_co_mutex_unlock(&bs->reqs_lock); + return waited; } /** @@ -793,20 +841,6 @@ static int bdrv_get_cluster_size(BlockDriverState *bs) } } -static bool tracked_request_overlaps(BdrvTrackedRequest *req, - int64_t offset, uint64_t bytes) -{ - /* aaaa bbbb */ - if (offset >= req->overlap_offset + req->overlap_bytes) { - return false; - } - /* bbbb aaaa */ - if (req->overlap_offset >= offset + bytes) { - return false; - } - return true; -} - void bdrv_inc_in_flight(BlockDriverState *bs) { atomic_inc(&bs->in_flight); @@ -823,48 +857,18 @@ void bdrv_dec_in_flight(BlockDriverState *bs) bdrv_wakeup(bs); } -bool coroutine_fn bdrv_wait_serialising_requests(BdrvTrackedRequest *self) +static bool coroutine_fn bdrv_wait_serialising_requests(BdrvTrackedRequest *self) { BlockDriverState *bs = self->bs; - BdrvTrackedRequest *req; - bool retry; bool waited = false; if (!atomic_read(&bs->serialising_in_flight)) { return false; } - do { - retry = false; - qemu_co_mutex_lock(&bs->reqs_lock); - QLIST_FOREACH(req, &bs->tracked_requests, list) { - if (req == self || (!req->serialising && !self->serialising)) { - continue; - } - if (tracked_request_overlaps(req, self->overlap_offset, - self->overlap_bytes)) - { - /* Hitting this means there was a reentrant request, for - * example, a block driver issuing nested requests. This must - * never happen since it means deadlock. - */ - assert(qemu_coroutine_self() != req->co); - - /* If the request is already (indirectly) waiting for us, or - * will wait for us as soon as it wakes up, then just go on - * (instead of producing a deadlock in the former case). */ - if (!req->waiting_for) { - self->waiting_for = req; - qemu_co_queue_wait(&req->wait_queue, &bs->reqs_lock); - self->waiting_for = NULL; - retry = true; - waited = true; - break; - } - } - } - qemu_co_mutex_unlock(&bs->reqs_lock); - } while (retry); + qemu_co_mutex_lock(&bs->reqs_lock); + waited = bdrv_wait_serialising_requests_locked(bs, self); + qemu_co_mutex_unlock(&bs->reqs_lock); return waited; } @@ -1395,7 +1399,7 @@ static int coroutine_fn bdrv_co_do_copy_on_readv(BdrvChild *child, if (!(flags & BDRV_REQ_PREFETCH)) { qemu_iovec_from_buf(qiov, qiov_offset + progress, bounce_buffer + skip_bytes, - pnum - skip_bytes); + MIN(pnum - skip_bytes, bytes - progress)); } } else if (!(flags & BDRV_REQ_PREFETCH)) { /* Read directly into the destination */ @@ -1445,8 +1449,7 @@ static int coroutine_fn bdrv_aligned_preadv(BdrvChild *child, * potential fallback support, if we ever implement any read flags * to pass through to drivers. For now, there aren't any * passthrough flags. */ - assert(!(flags & ~(BDRV_REQ_NO_SERIALISING | BDRV_REQ_COPY_ON_READ | - BDRV_REQ_PREFETCH))); + assert(!(flags & ~(BDRV_REQ_COPY_ON_READ | BDRV_REQ_PREFETCH))); /* Handle Copy on Read and associated serialisation */ if (flags & BDRV_REQ_COPY_ON_READ) { @@ -1456,12 +1459,7 @@ static int coroutine_fn bdrv_aligned_preadv(BdrvChild *child, * it ensures that the CoR read and write operations are atomic and * guest writes cannot interleave between them. */ bdrv_mark_request_serialising(req, bdrv_get_cluster_size(bs)); - } - - /* BDRV_REQ_SERIALISING is only for write operation */ - assert(!(flags & BDRV_REQ_SERIALISING)); - - if (!(flags & BDRV_REQ_NO_SERIALISING)) { + } else { bdrv_wait_serialising_requests(req); } @@ -1567,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); @@ -1708,10 +1708,22 @@ 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 */ - if (atomic_read(&bs->copy_on_read) && !(flags & BDRV_REQ_NO_SERIALISING)) { + if (atomic_read(&bs->copy_on_read)) { flags |= BDRV_REQ_COPY_ON_READ; } @@ -1852,20 +1864,24 @@ bdrv_co_write_req_prepare(BdrvChild *child, int64_t offset, uint64_t bytes, return -EPERM; } - /* BDRV_REQ_NO_SERIALISING is only for read operation */ - assert(!(flags & BDRV_REQ_NO_SERIALISING)); assert(!(bs->open_flags & BDRV_O_INACTIVE)); assert((bs->open_flags & BDRV_O_NO_IO) == 0); assert(!(flags & ~BDRV_REQ_MASK)); if (flags & BDRV_REQ_SERIALISING) { - bdrv_mark_request_serialising(req, bdrv_get_cluster_size(bs)); + waited = bdrv_mark_request_serialising(req, bdrv_get_cluster_size(bs)); + /* + * For a misaligned request we should have already waited earlier, + * because we come after bdrv_padding_rmw_read which must be called + * with the request already marked as serialising. + */ + assert(!waited || + (req->offset == req->overlap_offset && + req->bytes == req->overlap_bytes)); + } else { + bdrv_wait_serialising_requests(req); } - waited = bdrv_wait_serialising_requests(req); - - assert(!waited || !req->serialising || - is_request_serialising_and_aligned(req)); assert(req->overlap_offset <= offset); assert(offset + bytes <= req->overlap_offset + req->overlap_bytes); assert(end_sector <= bs->total_sectors || child->perm & BLK_PERM_RESIZE); @@ -2027,7 +2043,6 @@ static int coroutine_fn bdrv_co_do_zero_pwritev(BdrvChild *child, padding = bdrv_init_padding(bs, offset, bytes, &pad); if (padding) { bdrv_mark_request_serialising(req, align); - bdrv_wait_serialising_requests(req); bdrv_padding_rmw_read(child, req, &pad, true); @@ -2115,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. @@ -2130,7 +2157,6 @@ int coroutine_fn bdrv_co_pwritev_part(BdrvChild *child, if (bdrv_pad_request(bs, &qiov, &qiov_offset, &offset, &bytes, &pad)) { bdrv_mark_request_serialising(&req, align); - bdrv_wait_serialising_requests(&req); bdrv_padding_rmw_read(child, &req, &pad, false); } @@ -3222,9 +3248,7 @@ static int coroutine_fn bdrv_co_copy_range_internal( /* BDRV_REQ_SERIALISING is only for write operation */ assert(!(read_flags & BDRV_REQ_SERIALISING)); - if (!(read_flags & BDRV_REQ_NO_SERIALISING)) { - bdrv_wait_serialising_requests(&req); - } + bdrv_wait_serialising_requests(&req); ret = src->bs->drv->bdrv_co_copy_range_from(src->bs, src, src_offset, diff --git a/block/io_uring.c b/block/io_uring.c new file mode 100644 index 0000000000..a3142ca989 --- /dev/null +++ b/block/io_uring.c @@ -0,0 +1,433 @@ +/* + * Linux io_uring support. + * + * Copyright (C) 2009 IBM, Corp. + * Copyright (C) 2009 Red Hat, Inc. + * Copyright (C) 2019 Aarushi Mehta + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ +#include "qemu/osdep.h" +#include +#include "qemu-common.h" +#include "block/aio.h" +#include "qemu/queue.h" +#include "block/block.h" +#include "block/raw-aio.h" +#include "qemu/coroutine.h" +#include "qapi/error.h" +#include "trace.h" + +/* io_uring ring size */ +#define MAX_ENTRIES 128 + +typedef struct LuringAIOCB { + Coroutine *co; + struct io_uring_sqe sqeq; + ssize_t ret; + QEMUIOVector *qiov; + bool is_read; + QSIMPLEQ_ENTRY(LuringAIOCB) next; + + /* + * Buffered reads may require resubmission, see + * luring_resubmit_short_read(). + */ + int total_read; + QEMUIOVector resubmit_qiov; +} LuringAIOCB; + +typedef struct LuringQueue { + int plugged; + unsigned int in_queue; + unsigned int in_flight; + bool blocked; + QSIMPLEQ_HEAD(, LuringAIOCB) submit_queue; +} LuringQueue; + +typedef struct LuringState { + AioContext *aio_context; + + struct io_uring ring; + + /* io queue for submit at batch. Protected by AioContext lock. */ + LuringQueue io_q; + + /* I/O completion processing. Only runs in I/O thread. */ + QEMUBH *completion_bh; +} LuringState; + +/** + * luring_resubmit: + * + * Resubmit a request by appending it to submit_queue. The caller must ensure + * that ioq_submit() is called later so that submit_queue requests are started. + */ +static void luring_resubmit(LuringState *s, LuringAIOCB *luringcb) +{ + QSIMPLEQ_INSERT_TAIL(&s->io_q.submit_queue, luringcb, next); + s->io_q.in_queue++; +} + +/** + * luring_resubmit_short_read: + * + * Before Linux commit 9d93a3f5a0c ("io_uring: punt short reads to async + * context") a buffered I/O request with the start of the file range in the + * page cache could result in a short read. Applications need to resubmit the + * remaining read request. + * + * This is a slow path but recent kernels never take it. + */ +static void luring_resubmit_short_read(LuringState *s, LuringAIOCB *luringcb, + int nread) +{ + QEMUIOVector *resubmit_qiov; + size_t remaining; + + trace_luring_resubmit_short_read(s, luringcb, nread); + + /* Update read position */ + luringcb->total_read = nread; + remaining = luringcb->qiov->size - luringcb->total_read; + + /* Shorten qiov */ + resubmit_qiov = &luringcb->resubmit_qiov; + if (resubmit_qiov->iov == NULL) { + qemu_iovec_init(resubmit_qiov, luringcb->qiov->niov); + } else { + qemu_iovec_reset(resubmit_qiov); + } + qemu_iovec_concat(resubmit_qiov, luringcb->qiov, luringcb->total_read, + remaining); + + /* Update sqe */ + luringcb->sqeq.off = nread; + luringcb->sqeq.addr = (__u64)(uintptr_t)luringcb->resubmit_qiov.iov; + luringcb->sqeq.len = luringcb->resubmit_qiov.niov; + + luring_resubmit(s, luringcb); +} + +/** + * luring_process_completions: + * @s: AIO state + * + * Fetches completed I/O requests, consumes cqes and invokes their callbacks + * The function is somewhat tricky because it supports nested event loops, for + * example when a request callback invokes aio_poll(). + * + * Function schedules BH completion so it can be called again in a nested + * event loop. When there are no events left to complete the BH is being + * canceled. + * + */ +static void luring_process_completions(LuringState *s) +{ + struct io_uring_cqe *cqes; + int total_bytes; + /* + * Request completion callbacks can run the nested event loop. + * Schedule ourselves so the nested event loop will "see" remaining + * completed requests and process them. Without this, completion + * callbacks that wait for other requests using a nested event loop + * would hang forever. + * + * This workaround is needed because io_uring uses poll_wait, which + * is woken up when new events are added to the uring, thus polling on + * the same uring fd will block unless more events are received. + * + * Other leaf block drivers (drivers that access the data themselves) + * are networking based, so they poll sockets for data and run the + * correct coroutine. + */ + qemu_bh_schedule(s->completion_bh); + + while (io_uring_peek_cqe(&s->ring, &cqes) == 0) { + LuringAIOCB *luringcb; + int ret; + + if (!cqes) { + break; + } + + luringcb = io_uring_cqe_get_data(cqes); + ret = cqes->res; + io_uring_cqe_seen(&s->ring, cqes); + cqes = NULL; + + /* Change counters one-by-one because we can be nested. */ + s->io_q.in_flight--; + trace_luring_process_completion(s, luringcb, ret); + + /* total_read is non-zero only for resubmitted read requests */ + total_bytes = ret + luringcb->total_read; + + if (ret < 0) { + if (ret == -EINTR) { + luring_resubmit(s, luringcb); + continue; + } + } else if (!luringcb->qiov) { + goto end; + } else if (total_bytes == luringcb->qiov->size) { + ret = 0; + /* Only read/write */ + } else { + /* Short Read/Write */ + if (luringcb->is_read) { + if (ret > 0) { + luring_resubmit_short_read(s, luringcb, ret); + continue; + } else { + /* Pad with zeroes */ + qemu_iovec_memset(luringcb->qiov, total_bytes, 0, + luringcb->qiov->size - total_bytes); + ret = 0; + } + } else { + ret = -ENOSPC; + } + } +end: + luringcb->ret = ret; + qemu_iovec_destroy(&luringcb->resubmit_qiov); + + /* + * If the coroutine is already entered it must be in ioq_submit() + * and will notice luringcb->ret has been filled in when it + * eventually runs later. Coroutines cannot be entered recursively + * so avoid doing that! + */ + if (!qemu_coroutine_entered(luringcb->co)) { + aio_co_wake(luringcb->co); + } + } + qemu_bh_cancel(s->completion_bh); +} + +static int ioq_submit(LuringState *s) +{ + int ret = 0; + LuringAIOCB *luringcb, *luringcb_next; + + while (s->io_q.in_queue > 0) { + /* + * Try to fetch sqes from the ring for requests waiting in + * the overflow queue + */ + QSIMPLEQ_FOREACH_SAFE(luringcb, &s->io_q.submit_queue, next, + luringcb_next) { + struct io_uring_sqe *sqes = io_uring_get_sqe(&s->ring); + if (!sqes) { + break; + } + /* Prep sqe for submission */ + *sqes = luringcb->sqeq; + QSIMPLEQ_REMOVE_HEAD(&s->io_q.submit_queue, next); + } + ret = io_uring_submit(&s->ring); + trace_luring_io_uring_submit(s, ret); + /* Prevent infinite loop if submission is refused */ + if (ret <= 0) { + if (ret == -EAGAIN) { + continue; + } + break; + } + s->io_q.in_flight += ret; + s->io_q.in_queue -= ret; + } + s->io_q.blocked = (s->io_q.in_queue > 0); + + if (s->io_q.in_flight) { + /* + * We can try to complete something just right away if there are + * still requests in-flight. + */ + luring_process_completions(s); + } + return ret; +} + +static void luring_process_completions_and_submit(LuringState *s) +{ + aio_context_acquire(s->aio_context); + luring_process_completions(s); + + if (!s->io_q.plugged && s->io_q.in_queue > 0) { + ioq_submit(s); + } + aio_context_release(s->aio_context); +} + +static void qemu_luring_completion_bh(void *opaque) +{ + LuringState *s = opaque; + luring_process_completions_and_submit(s); +} + +static void qemu_luring_completion_cb(void *opaque) +{ + LuringState *s = opaque; + luring_process_completions_and_submit(s); +} + +static bool qemu_luring_poll_cb(void *opaque) +{ + LuringState *s = opaque; + struct io_uring_cqe *cqes; + + if (io_uring_peek_cqe(&s->ring, &cqes) == 0) { + if (cqes) { + luring_process_completions_and_submit(s); + return true; + } + } + + return false; +} + +static void ioq_init(LuringQueue *io_q) +{ + QSIMPLEQ_INIT(&io_q->submit_queue); + io_q->plugged = 0; + io_q->in_queue = 0; + io_q->in_flight = 0; + io_q->blocked = false; +} + +void luring_io_plug(BlockDriverState *bs, LuringState *s) +{ + trace_luring_io_plug(s); + s->io_q.plugged++; +} + +void luring_io_unplug(BlockDriverState *bs, LuringState *s) +{ + assert(s->io_q.plugged); + trace_luring_io_unplug(s, s->io_q.blocked, s->io_q.plugged, + s->io_q.in_queue, s->io_q.in_flight); + if (--s->io_q.plugged == 0 && + !s->io_q.blocked && s->io_q.in_queue > 0) { + ioq_submit(s); + } +} + +/** + * luring_do_submit: + * @fd: file descriptor for I/O + * @luringcb: AIO control block + * @s: AIO state + * @offset: offset for request + * @type: type of request + * + * Fetches sqes from ring, adds to pending queue and preps them + * + */ +static int luring_do_submit(int fd, LuringAIOCB *luringcb, LuringState *s, + uint64_t offset, int type) +{ + int ret; + struct io_uring_sqe *sqes = &luringcb->sqeq; + + switch (type) { + case QEMU_AIO_WRITE: + io_uring_prep_writev(sqes, fd, luringcb->qiov->iov, + luringcb->qiov->niov, offset); + break; + case QEMU_AIO_READ: + io_uring_prep_readv(sqes, fd, luringcb->qiov->iov, + luringcb->qiov->niov, offset); + break; + case QEMU_AIO_FLUSH: + io_uring_prep_fsync(sqes, fd, IORING_FSYNC_DATASYNC); + break; + default: + fprintf(stderr, "%s: invalid AIO request type, aborting 0x%x.\n", + __func__, type); + abort(); + } + io_uring_sqe_set_data(sqes, luringcb); + + QSIMPLEQ_INSERT_TAIL(&s->io_q.submit_queue, luringcb, next); + s->io_q.in_queue++; + trace_luring_do_submit(s, s->io_q.blocked, s->io_q.plugged, + s->io_q.in_queue, s->io_q.in_flight); + if (!s->io_q.blocked && + (!s->io_q.plugged || + s->io_q.in_flight + s->io_q.in_queue >= MAX_ENTRIES)) { + ret = ioq_submit(s); + trace_luring_do_submit_done(s, ret); + return ret; + } + return 0; +} + +int coroutine_fn luring_co_submit(BlockDriverState *bs, LuringState *s, int fd, + uint64_t offset, QEMUIOVector *qiov, int type) +{ + int ret; + LuringAIOCB luringcb = { + .co = qemu_coroutine_self(), + .ret = -EINPROGRESS, + .qiov = qiov, + .is_read = (type == QEMU_AIO_READ), + }; + trace_luring_co_submit(bs, s, &luringcb, fd, offset, qiov ? qiov->size : 0, + type); + ret = luring_do_submit(fd, &luringcb, s, offset, type); + + if (ret < 0) { + return ret; + } + + if (luringcb.ret == -EINPROGRESS) { + qemu_coroutine_yield(); + } + return luringcb.ret; +} + +void luring_detach_aio_context(LuringState *s, AioContext *old_context) +{ + aio_set_fd_handler(old_context, s->ring.ring_fd, false, NULL, NULL, NULL, + s); + qemu_bh_delete(s->completion_bh); + s->aio_context = NULL; +} + +void luring_attach_aio_context(LuringState *s, AioContext *new_context) +{ + s->aio_context = new_context; + s->completion_bh = aio_bh_new(new_context, qemu_luring_completion_bh, s); + aio_set_fd_handler(s->aio_context, s->ring.ring_fd, false, + qemu_luring_completion_cb, NULL, qemu_luring_poll_cb, s); +} + +LuringState *luring_init(Error **errp) +{ + int rc; + LuringState *s = g_new0(LuringState, 1); + struct io_uring *ring = &s->ring; + + trace_luring_init_state(s, sizeof(*s)); + + rc = io_uring_queue_init(MAX_ENTRIES, ring, 0); + if (rc < 0) { + error_setg_errno(errp, errno, "failed to init linux io_uring ring"); + g_free(s); + return NULL; + } + + ioq_init(&s->io_q); + return s; + +} + +void luring_cleanup(LuringState *s) +{ + io_uring_queue_exit(&s->ring); + g_free(s); + trace_luring_cleanup_state(s); +} diff --git a/block/iscsi.c b/block/iscsi.c index 2aea7e3f13..0b4b7210df 100644 --- a/block/iscsi.c +++ b/block/iscsi.c @@ -701,7 +701,7 @@ static int coroutine_fn iscsi_co_block_status(BlockDriverState *bs, struct scsi_get_lba_status *lbas = NULL; struct scsi_lba_status_descriptor *lbasd = NULL; struct IscsiTask iTask; - uint64_t lba; + uint64_t lba, max_bytes; int ret; iscsi_co_init_iscsitask(iscsilun, &iTask); @@ -721,6 +721,7 @@ static int coroutine_fn iscsi_co_block_status(BlockDriverState *bs, } lba = offset / iscsilun->block_size; + max_bytes = (iscsilun->num_blocks - lba) * iscsilun->block_size; qemu_mutex_lock(&iscsilun->mutex); retry: @@ -752,7 +753,7 @@ retry: } lbas = scsi_datain_unmarshall(iTask.task); - if (lbas == NULL) { + if (lbas == NULL || lbas->num_descriptors == 0) { ret = -EIO; goto out_unlock; } @@ -764,7 +765,7 @@ retry: goto out_unlock; } - *pnum = (int64_t) lbasd->num_blocks * iscsilun->block_size; + *pnum = MIN((int64_t) lbasd->num_blocks * iscsilun->block_size, max_bytes); if (lbasd->provisioning == SCSI_PROVISIONING_TYPE_DEALLOCATED || lbasd->provisioning == SCSI_PROVISIONING_TYPE_ANCHORED) { @@ -990,8 +991,7 @@ iscsi_aio_ioctl_cb(struct iscsi_context *iscsi, int status, acb->ioh->driver_status |= SG_ERR_DRIVER_SENSE; acb->ioh->sb_len_wr = acb->task->datain.size - 2; - ss = (acb->ioh->mx_sb_len >= acb->ioh->sb_len_wr) ? - acb->ioh->mx_sb_len : acb->ioh->sb_len_wr; + ss = MIN(acb->ioh->mx_sb_len, acb->ioh->sb_len_wr); memcpy(acb->ioh->sbp, &acb->task->datain.data[2], ss); } @@ -2001,7 +2001,7 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, iscsilun->cluster_size = iscsilun->bl.opt_unmap_gran * iscsilun->block_size; if (iscsilun->lbprz) { - ret = iscsi_allocmap_init(iscsilun, bs->open_flags); + ret = iscsi_allocmap_init(iscsilun, flags); } } @@ -2163,58 +2163,6 @@ static int coroutine_fn iscsi_co_truncate(BlockDriverState *bs, int64_t offset, return 0; } -static int coroutine_fn iscsi_co_create_opts(const char *filename, QemuOpts *opts, - Error **errp) -{ - int ret = 0; - int64_t total_size = 0; - BlockDriverState *bs; - IscsiLun *iscsilun = NULL; - QDict *bs_options; - Error *local_err = NULL; - - bs = bdrv_new(); - - /* Read out options */ - total_size = DIV_ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), - BDRV_SECTOR_SIZE); - bs->opaque = g_new0(struct IscsiLun, 1); - iscsilun = bs->opaque; - - bs_options = qdict_new(); - iscsi_parse_filename(filename, bs_options, &local_err); - if (local_err) { - error_propagate(errp, local_err); - ret = -EINVAL; - } else { - ret = iscsi_open(bs, bs_options, 0, NULL); - } - qobject_unref(bs_options); - - if (ret != 0) { - goto out; - } - iscsi_detach_aio_context(bs); - if (iscsilun->type != TYPE_DISK) { - ret = -ENODEV; - goto out; - } - if (bs->total_sectors < total_size) { - ret = -ENOSPC; - goto out; - } - - ret = 0; -out: - if (iscsilun->iscsi != NULL) { - iscsi_destroy_context(iscsilun->iscsi); - } - g_free(bs->opaque); - bs->opaque = NULL; - bdrv_unref(bs); - return ret; -} - static int iscsi_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) { IscsiLun *iscsilun = bs->opaque; @@ -2450,18 +2398,6 @@ out_unlock: return r; } -static QemuOptsList iscsi_create_opts = { - .name = "iscsi-create-opts", - .head = QTAILQ_HEAD_INITIALIZER(iscsi_create_opts.head), - .desc = { - { - .name = BLOCK_OPT_SIZE, - .type = QEMU_OPT_SIZE, - .help = "Virtual disk size" - }, - { /* end of list */ } - } -}; static const char *const iscsi_strong_runtime_opts[] = { "transport", @@ -2485,8 +2421,8 @@ static BlockDriver bdrv_iscsi = { .bdrv_parse_filename = iscsi_parse_filename, .bdrv_file_open = iscsi_open, .bdrv_close = iscsi_close, - .bdrv_co_create_opts = iscsi_co_create_opts, - .create_opts = &iscsi_create_opts, + .bdrv_co_create_opts = bdrv_co_create_opts_simple, + .create_opts = &bdrv_create_opts_simple, .bdrv_reopen_prepare = iscsi_reopen_prepare, .bdrv_reopen_commit = iscsi_reopen_commit, .bdrv_co_invalidate_cache = iscsi_co_invalidate_cache, @@ -2524,8 +2460,8 @@ static BlockDriver bdrv_iser = { .bdrv_parse_filename = iscsi_parse_filename, .bdrv_file_open = iscsi_open, .bdrv_close = iscsi_close, - .bdrv_co_create_opts = iscsi_co_create_opts, - .create_opts = &iscsi_create_opts, + .bdrv_co_create_opts = bdrv_co_create_opts_simple, + .create_opts = &bdrv_create_opts_simple, .bdrv_reopen_prepare = iscsi_reopen_prepare, .bdrv_reopen_commit = iscsi_reopen_commit, .bdrv_co_invalidate_cache = iscsi_co_invalidate_cache, diff --git a/block/linux-aio.c b/block/linux-aio.c index c7eca9a256..3c0527c2bf 100644 --- a/block/linux-aio.c +++ b/block/linux-aio.c @@ -26,7 +26,7 @@ * than this we will get EAGAIN from io_submit which is communicated to * the guest as an I/O error. */ -#define MAX_EVENTS 128 +#define MAX_EVENTS 1024 struct qemu_laiocb { Coroutine *co; @@ -121,7 +121,7 @@ struct aio_ring { unsigned incompat_features; unsigned header_length; /* size of aio_ring */ - struct io_event io_events[0]; + struct io_event io_events[]; }; /** diff --git a/block/mirror.c b/block/mirror.c index f0f2d9dff1..c26fd9260d 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -102,7 +102,9 @@ struct MirrorOp { bool is_pseudo_op; bool is_active_write; + bool is_in_flight; CoQueue waiting_requests; + Coroutine *co; QTAILQ_ENTRY(MirrorOp) next; }; @@ -292,7 +294,9 @@ mirror_wait_for_any_operation(MirrorBlockJob *s, bool active) * caller of this function. Since there is only one pseudo op * at any given time, we will always find some real operation * to wait on. */ - if (!op->is_pseudo_op && op->is_active_write == active) { + if (!op->is_pseudo_op && op->is_in_flight && + op->is_active_write == active) + { qemu_co_queue_wait(&op->waiting_requests, NULL); return; } @@ -366,6 +370,7 @@ static void coroutine_fn mirror_co_read(void *opaque) /* Copy the dirty cluster. */ s->in_flight++; s->bytes_in_flight += op->bytes; + op->is_in_flight = true; trace_mirror_one_iteration(s, op->offset, op->bytes); ret = bdrv_co_preadv(s->mirror_top_bs->backing, op->offset, op->bytes, @@ -381,6 +386,7 @@ static void coroutine_fn mirror_co_zero(void *opaque) op->s->in_flight++; op->s->bytes_in_flight += op->bytes; *op->bytes_handled = op->bytes; + op->is_in_flight = true; ret = blk_co_pwrite_zeroes(op->s->target, op->offset, op->bytes, op->s->unmap ? BDRV_REQ_MAY_UNMAP : 0); @@ -395,6 +401,7 @@ static void coroutine_fn mirror_co_discard(void *opaque) op->s->in_flight++; op->s->bytes_in_flight += op->bytes; *op->bytes_handled = op->bytes; + op->is_in_flight = true; ret = blk_co_pdiscard(op->s->target, op->offset, op->bytes); mirror_write_complete(op, ret); @@ -429,6 +436,7 @@ static unsigned mirror_perform(MirrorBlockJob *s, int64_t offset, default: abort(); } + op->co = co; QTAILQ_INSERT_TAIL(&s->ops_in_flight, op, next); qemu_coroutine_enter(co); @@ -673,6 +681,7 @@ static int mirror_exit_common(Job *job) bdrv_set_backing_hd(target_bs, backing, &local_err); if (local_err) { error_report_err(local_err); + local_err = NULL; ret = -EPERM; } } @@ -695,7 +704,19 @@ static int mirror_exit_common(Job *job) * drain potential other users of the BDS before changing the graph. */ assert(s->in_drain); bdrv_drained_begin(target_bs); - bdrv_replace_node(to_replace, target_bs, &local_err); + /* + * Cannot use check_to_replace_node() here, because that would + * check for an op blocker on @to_replace, and we have our own + * there. + */ + if (bdrv_recurse_can_replace(src, to_replace)) { + bdrv_replace_node(to_replace, target_bs, &local_err); + } else { + error_setg(&local_err, "Can no longer replace '%s' by '%s', " + "because it can no longer be guaranteed that doing so " + "would not lead to an abrupt change of visible data", + to_replace->node_name, target_bs->node_name); + } bdrv_drained_end(target_bs); if (local_err) { error_report_err(local_err); @@ -1304,6 +1325,7 @@ static MirrorOp *coroutine_fn active_write_prepare(MirrorBlockJob *s, .offset = offset, .bytes = bytes, .is_active_write = true, + .is_in_flight = true, }; qemu_co_queue_init(&op->waiting_requests); QTAILQ_INSERT_TAIL(&s->ops_in_flight, op, next); diff --git a/block/monitor/Makefile.objs b/block/monitor/Makefile.objs new file mode 100644 index 0000000000..0a74f9a8b5 --- /dev/null +++ b/block/monitor/Makefile.objs @@ -0,0 +1 @@ +common-obj-y += block-hmp-cmds.o diff --git a/block/monitor/block-hmp-cmds.c b/block/monitor/block-hmp-cmds.c new file mode 100644 index 0000000000..4c8c375172 --- /dev/null +++ b/block/monitor/block-hmp-cmds.c @@ -0,0 +1,1013 @@ +/* + * Blockdev HMP commands + * + * Authors: + * Anthony Liguori + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * This work is licensed under the terms of the GNU GPL, version 2. + * See the COPYING file in the top-level directory. + * Contributions after 2012-01-13 are licensed under the terms of the + * GNU GPL, version 2 or (at your option) any later version. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include "hw/boards.h" +#include "sysemu/block-backend.h" +#include "sysemu/blockdev.h" +#include "qapi/qapi-commands-block.h" +#include "qapi/qmp/qdict.h" +#include "qapi/error.h" +#include "qapi/qmp/qerror.h" +#include "qemu/config-file.h" +#include "qemu/option.h" +#include "qemu/sockets.h" +#include "qemu/cutils.h" +#include "sysemu/sysemu.h" +#include "monitor/monitor.h" +#include "monitor/hmp.h" +#include "block/nbd.h" +#include "block/qapi.h" +#include "block/block_int.h" +#include "block/block-hmp-cmds.h" +#include "qemu-io.h" + +static void hmp_drive_add_node(Monitor *mon, const char *optstr) +{ + QemuOpts *opts; + QDict *qdict; + Error *local_err = NULL; + + opts = qemu_opts_parse_noisily(&qemu_drive_opts, optstr, false); + if (!opts) { + return; + } + + qdict = qemu_opts_to_qdict(opts, NULL); + + if (!qdict_get_try_str(qdict, "node-name")) { + qobject_unref(qdict); + error_report("'node-name' needs to be specified"); + goto out; + } + + BlockDriverState *bs = bds_tree_init(qdict, &local_err); + if (!bs) { + error_report_err(local_err); + goto out; + } + + bdrv_set_monitor_owned(bs); +out: + qemu_opts_del(opts); +} + +void hmp_drive_add(Monitor *mon, const QDict *qdict) +{ + Error *err = NULL; + DriveInfo *dinfo; + QemuOpts *opts; + MachineClass *mc; + const char *optstr = qdict_get_str(qdict, "opts"); + bool node = qdict_get_try_bool(qdict, "node", false); + + if (node) { + hmp_drive_add_node(mon, optstr); + return; + } + + opts = drive_def(optstr); + if (!opts) + return; + + mc = MACHINE_GET_CLASS(current_machine); + dinfo = drive_new(opts, mc->block_default_type, &err); + if (err) { + error_report_err(err); + qemu_opts_del(opts); + goto err; + } + + if (!dinfo) { + return; + } + + switch (dinfo->type) { + case IF_NONE: + monitor_printf(mon, "OK\n"); + break; + default: + monitor_printf(mon, "Can't hot-add drive to type %d\n", dinfo->type); + goto err; + } + return; + +err: + if (dinfo) { + BlockBackend *blk = blk_by_legacy_dinfo(dinfo); + monitor_remove_blk(blk); + blk_unref(blk); + } +} + +void hmp_drive_del(Monitor *mon, const QDict *qdict) +{ + const char *id = qdict_get_str(qdict, "id"); + BlockBackend *blk; + BlockDriverState *bs; + AioContext *aio_context; + Error *local_err = NULL; + + bs = bdrv_find_node(id); + if (bs) { + qmp_blockdev_del(id, &local_err); + if (local_err) { + error_report_err(local_err); + } + return; + } + + blk = blk_by_name(id); + if (!blk) { + error_report("Device '%s' not found", id); + return; + } + + if (!blk_legacy_dinfo(blk)) { + error_report("Deleting device added with blockdev-add" + " is not supported"); + return; + } + + aio_context = blk_get_aio_context(blk); + aio_context_acquire(aio_context); + + bs = blk_bs(blk); + if (bs) { + if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_DRIVE_DEL, &local_err)) { + error_report_err(local_err); + aio_context_release(aio_context); + return; + } + + blk_remove_bs(blk); + } + + /* Make the BlockBackend and the attached BlockDriverState anonymous */ + monitor_remove_blk(blk); + + /* + * If this BlockBackend has a device attached to it, its refcount will be + * decremented when the device is removed; otherwise we have to do so here. + */ + if (blk_get_attached_dev(blk)) { + /* Further I/O must not pause the guest */ + blk_set_on_error(blk, BLOCKDEV_ON_ERROR_REPORT, + BLOCKDEV_ON_ERROR_REPORT); + } else { + blk_unref(blk); + } + + aio_context_release(aio_context); +} + +void hmp_commit(Monitor *mon, const QDict *qdict) +{ + const char *device = qdict_get_str(qdict, "device"); + BlockBackend *blk; + int ret; + + if (!strcmp(device, "all")) { + ret = blk_commit_all(); + } else { + BlockDriverState *bs; + AioContext *aio_context; + + blk = blk_by_name(device); + if (!blk) { + error_report("Device '%s' not found", device); + return; + } + if (!blk_is_available(blk)) { + error_report("Device '%s' has no medium", device); + return; + } + + bs = blk_bs(blk); + aio_context = bdrv_get_aio_context(bs); + aio_context_acquire(aio_context); + + ret = bdrv_commit(bs); + + aio_context_release(aio_context); + } + if (ret < 0) { + error_report("'commit' error for '%s': %s", device, strerror(-ret)); + } +} + +void hmp_drive_mirror(Monitor *mon, const QDict *qdict) +{ + const char *filename = qdict_get_str(qdict, "target"); + const char *format = qdict_get_try_str(qdict, "format"); + bool reuse = qdict_get_try_bool(qdict, "reuse", false); + bool full = qdict_get_try_bool(qdict, "full", false); + Error *err = NULL; + DriveMirror mirror = { + .device = (char *)qdict_get_str(qdict, "device"), + .target = (char *)filename, + .has_format = !!format, + .format = (char *)format, + .sync = full ? MIRROR_SYNC_MODE_FULL : MIRROR_SYNC_MODE_TOP, + .has_mode = true, + .mode = reuse ? NEW_IMAGE_MODE_EXISTING : NEW_IMAGE_MODE_ABSOLUTE_PATHS, + .unmap = true, + }; + + if (!filename) { + error_setg(&err, QERR_MISSING_PARAMETER, "target"); + hmp_handle_error(mon, err); + return; + } + qmp_drive_mirror(&mirror, &err); + hmp_handle_error(mon, err); +} + +void hmp_drive_backup(Monitor *mon, const QDict *qdict) +{ + const char *device = qdict_get_str(qdict, "device"); + const char *filename = qdict_get_str(qdict, "target"); + const char *format = qdict_get_try_str(qdict, "format"); + bool reuse = qdict_get_try_bool(qdict, "reuse", false); + bool full = qdict_get_try_bool(qdict, "full", false); + bool compress = qdict_get_try_bool(qdict, "compress", false); + Error *err = NULL; + DriveBackup backup = { + .device = (char *)device, + .target = (char *)filename, + .has_format = !!format, + .format = (char *)format, + .sync = full ? MIRROR_SYNC_MODE_FULL : MIRROR_SYNC_MODE_TOP, + .has_mode = true, + .mode = reuse ? NEW_IMAGE_MODE_EXISTING : NEW_IMAGE_MODE_ABSOLUTE_PATHS, + .has_compress = !!compress, + .compress = compress, + }; + + if (!filename) { + error_setg(&err, QERR_MISSING_PARAMETER, "target"); + hmp_handle_error(mon, err); + return; + } + + qmp_drive_backup(&backup, &err); + hmp_handle_error(mon, err); +} + +void hmp_block_job_set_speed(Monitor *mon, const QDict *qdict) +{ + Error *error = NULL; + const char *device = qdict_get_str(qdict, "device"); + int64_t value = qdict_get_int(qdict, "speed"); + + qmp_block_job_set_speed(device, value, &error); + + hmp_handle_error(mon, error); +} + +void hmp_block_job_cancel(Monitor *mon, const QDict *qdict) +{ + Error *error = NULL; + const char *device = qdict_get_str(qdict, "device"); + bool force = qdict_get_try_bool(qdict, "force", false); + + qmp_block_job_cancel(device, true, force, &error); + + hmp_handle_error(mon, error); +} + +void hmp_block_job_pause(Monitor *mon, const QDict *qdict) +{ + Error *error = NULL; + const char *device = qdict_get_str(qdict, "device"); + + qmp_block_job_pause(device, &error); + + hmp_handle_error(mon, error); +} + +void hmp_block_job_resume(Monitor *mon, const QDict *qdict) +{ + Error *error = NULL; + const char *device = qdict_get_str(qdict, "device"); + + qmp_block_job_resume(device, &error); + + hmp_handle_error(mon, error); +} + +void hmp_block_job_complete(Monitor *mon, const QDict *qdict) +{ + Error *error = NULL; + const char *device = qdict_get_str(qdict, "device"); + + qmp_block_job_complete(device, &error); + + hmp_handle_error(mon, error); +} + +void hmp_snapshot_blkdev(Monitor *mon, const QDict *qdict) +{ + const char *device = qdict_get_str(qdict, "device"); + const char *filename = qdict_get_try_str(qdict, "snapshot-file"); + const char *format = qdict_get_try_str(qdict, "format"); + bool reuse = qdict_get_try_bool(qdict, "reuse", false); + enum NewImageMode mode; + Error *err = NULL; + + if (!filename) { + /* + * In the future, if 'snapshot-file' is not specified, the snapshot + * will be taken internally. Today it's actually required. + */ + error_setg(&err, QERR_MISSING_PARAMETER, "snapshot-file"); + hmp_handle_error(mon, err); + return; + } + + mode = reuse ? NEW_IMAGE_MODE_EXISTING : NEW_IMAGE_MODE_ABSOLUTE_PATHS; + qmp_blockdev_snapshot_sync(true, device, false, NULL, + filename, false, NULL, + !!format, format, + true, mode, &err); + hmp_handle_error(mon, err); +} + +void hmp_snapshot_blkdev_internal(Monitor *mon, const QDict *qdict) +{ + const char *device = qdict_get_str(qdict, "device"); + const char *name = qdict_get_str(qdict, "name"); + Error *err = NULL; + + qmp_blockdev_snapshot_internal_sync(device, name, &err); + hmp_handle_error(mon, err); +} + +void hmp_snapshot_delete_blkdev_internal(Monitor *mon, const QDict *qdict) +{ + const char *device = qdict_get_str(qdict, "device"); + const char *name = qdict_get_str(qdict, "name"); + const char *id = qdict_get_try_str(qdict, "id"); + Error *err = NULL; + + qmp_blockdev_snapshot_delete_internal_sync(device, !!id, id, + true, name, &err); + hmp_handle_error(mon, err); +} + +void hmp_nbd_server_start(Monitor *mon, const QDict *qdict) +{ + const char *uri = qdict_get_str(qdict, "uri"); + bool writable = qdict_get_try_bool(qdict, "writable", false); + bool all = qdict_get_try_bool(qdict, "all", false); + Error *local_err = NULL; + BlockInfoList *block_list, *info; + SocketAddress *addr; + BlockExportNbd export; + + if (writable && !all) { + error_setg(&local_err, "-w only valid together with -a"); + goto exit; + } + + /* First check if the address is valid and start the server. */ + addr = socket_parse(uri, &local_err); + if (local_err != NULL) { + goto exit; + } + + nbd_server_start(addr, NULL, NULL, &local_err); + qapi_free_SocketAddress(addr); + if (local_err != NULL) { + goto exit; + } + + if (!all) { + return; + } + + /* Then try adding all block devices. If one fails, close all and + * exit. + */ + block_list = qmp_query_block(NULL); + + for (info = block_list; info; info = info->next) { + if (!info->value->has_inserted) { + continue; + } + + export = (BlockExportNbd) { + .device = info->value->device, + .has_writable = true, + .writable = writable, + }; + + qmp_nbd_server_add(&export, &local_err); + + if (local_err != NULL) { + qmp_nbd_server_stop(NULL); + break; + } + } + + qapi_free_BlockInfoList(block_list); + +exit: + hmp_handle_error(mon, local_err); +} + +void hmp_nbd_server_add(Monitor *mon, const QDict *qdict) +{ + const char *device = qdict_get_str(qdict, "device"); + const char *name = qdict_get_try_str(qdict, "name"); + bool writable = qdict_get_try_bool(qdict, "writable", false); + Error *local_err = NULL; + + BlockExportNbd export = { + .device = (char *) device, + .has_name = !!name, + .name = (char *) name, + .has_writable = true, + .writable = writable, + }; + + qmp_nbd_server_add(&export, &local_err); + hmp_handle_error(mon, local_err); +} + +void hmp_nbd_server_remove(Monitor *mon, const QDict *qdict) +{ + const char *name = qdict_get_str(qdict, "name"); + bool force = qdict_get_try_bool(qdict, "force", false); + Error *err = NULL; + + /* Rely on NBD_SERVER_REMOVE_MODE_SAFE being the default */ + qmp_nbd_server_remove(name, force, NBD_SERVER_REMOVE_MODE_HARD, &err); + hmp_handle_error(mon, err); +} + +void hmp_nbd_server_stop(Monitor *mon, const QDict *qdict) +{ + Error *err = NULL; + + qmp_nbd_server_stop(&err); + hmp_handle_error(mon, err); +} + +void hmp_block_resize(Monitor *mon, const QDict *qdict) +{ + const char *device = qdict_get_str(qdict, "device"); + int64_t size = qdict_get_int(qdict, "size"); + Error *err = NULL; + + qmp_block_resize(true, device, false, NULL, size, &err); + hmp_handle_error(mon, err); +} + +void hmp_block_stream(Monitor *mon, const QDict *qdict) +{ + Error *error = NULL; + const char *device = qdict_get_str(qdict, "device"); + const char *base = qdict_get_try_str(qdict, "base"); + int64_t speed = qdict_get_try_int(qdict, "speed", 0); + + qmp_block_stream(true, device, device, base != NULL, base, false, NULL, + false, NULL, qdict_haskey(qdict, "speed"), speed, true, + BLOCKDEV_ON_ERROR_REPORT, false, false, false, false, + &error); + + hmp_handle_error(mon, error); +} + +void hmp_block_passwd(Monitor *mon, const QDict *qdict) +{ + const char *device = qdict_get_str(qdict, "device"); + const char *password = qdict_get_str(qdict, "password"); + Error *err = NULL; + + qmp_block_passwd(true, device, false, NULL, password, &err); + hmp_handle_error(mon, err); +} + +void hmp_block_set_io_throttle(Monitor *mon, const QDict *qdict) +{ + Error *err = NULL; + char *device = (char *) qdict_get_str(qdict, "device"); + BlockIOThrottle throttle = { + .bps = qdict_get_int(qdict, "bps"), + .bps_rd = qdict_get_int(qdict, "bps_rd"), + .bps_wr = qdict_get_int(qdict, "bps_wr"), + .iops = qdict_get_int(qdict, "iops"), + .iops_rd = qdict_get_int(qdict, "iops_rd"), + .iops_wr = qdict_get_int(qdict, "iops_wr"), + }; + + /* + * qmp_block_set_io_throttle has separate parameters for the + * (deprecated) block device name and the qdev ID but the HMP + * version has only one, so we must decide which one to pass. + */ + if (blk_by_name(device)) { + throttle.has_device = true; + throttle.device = device; + } else { + throttle.has_id = true; + throttle.id = device; + } + + qmp_block_set_io_throttle(&throttle, &err); + hmp_handle_error(mon, err); +} + +void hmp_eject(Monitor *mon, const QDict *qdict) +{ + bool force = qdict_get_try_bool(qdict, "force", false); + const char *device = qdict_get_str(qdict, "device"); + Error *err = NULL; + + qmp_eject(true, device, false, NULL, true, force, &err); + hmp_handle_error(mon, err); +} + +void hmp_qemu_io(Monitor *mon, const QDict *qdict) +{ + BlockBackend *blk; + BlockBackend *local_blk = NULL; + bool qdev = qdict_get_try_bool(qdict, "qdev", false); + const char *device = qdict_get_str(qdict, "device"); + const char *command = qdict_get_str(qdict, "command"); + Error *err = NULL; + int ret; + + if (qdev) { + blk = blk_by_qdev_id(device, &err); + if (!blk) { + goto fail; + } + } else { + blk = blk_by_name(device); + if (!blk) { + BlockDriverState *bs = bdrv_lookup_bs(NULL, device, &err); + if (bs) { + blk = local_blk = blk_new(bdrv_get_aio_context(bs), + 0, BLK_PERM_ALL); + ret = blk_insert_bs(blk, bs, &err); + if (ret < 0) { + goto fail; + } + } else { + goto fail; + } + } + } + + /* + * Notably absent: Proper permission management. This is sad, but it seems + * almost impossible to achieve without changing the semantics and thereby + * limiting the use cases of the qemu-io HMP command. + * + * In an ideal world we would unconditionally create a new BlockBackend for + * qemuio_command(), but we have commands like 'reopen' and want them to + * take effect on the exact BlockBackend whose name the user passed instead + * of just on a temporary copy of it. + * + * Another problem is that deleting the temporary BlockBackend involves + * draining all requests on it first, but some qemu-iotests cases want to + * issue multiple aio_read/write requests and expect them to complete in + * the background while the monitor has already returned. + * + * This is also what prevents us from saving the original permissions and + * restoring them later: We can't revoke permissions until all requests + * have completed, and we don't know when that is nor can we really let + * anything else run before we have revoken them to avoid race conditions. + * + * What happens now is that command() in qemu-io-cmds.c can extend the + * permissions if necessary for the qemu-io command. And they simply stay + * extended, possibly resulting in a read-only guest device keeping write + * permissions. Ugly, but it appears to be the lesser evil. + */ + qemuio_command(blk, command); + +fail: + blk_unref(local_blk); + hmp_handle_error(mon, err); +} + +static void print_block_info(Monitor *mon, BlockInfo *info, + BlockDeviceInfo *inserted, bool verbose) +{ + ImageInfo *image_info; + + assert(!info || !info->has_inserted || info->inserted == inserted); + + if (info && *info->device) { + monitor_printf(mon, "%s", info->device); + if (inserted && inserted->has_node_name) { + monitor_printf(mon, " (%s)", inserted->node_name); + } + } else { + assert(info || inserted); + monitor_printf(mon, "%s", + inserted && inserted->has_node_name ? inserted->node_name + : info && info->has_qdev ? info->qdev + : ""); + } + + if (inserted) { + monitor_printf(mon, ": %s (%s%s%s)\n", + inserted->file, + inserted->drv, + inserted->ro ? ", read-only" : "", + inserted->encrypted ? ", encrypted" : ""); + } else { + monitor_printf(mon, ": [not inserted]\n"); + } + + if (info) { + if (info->has_qdev) { + monitor_printf(mon, " Attached to: %s\n", info->qdev); + } + if (info->has_io_status && info->io_status != BLOCK_DEVICE_IO_STATUS_OK) { + monitor_printf(mon, " I/O status: %s\n", + BlockDeviceIoStatus_str(info->io_status)); + } + + if (info->removable) { + monitor_printf(mon, " Removable device: %slocked, tray %s\n", + info->locked ? "" : "not ", + info->tray_open ? "open" : "closed"); + } + } + + + if (!inserted) { + return; + } + + monitor_printf(mon, " Cache mode: %s%s%s\n", + inserted->cache->writeback ? "writeback" : "writethrough", + inserted->cache->direct ? ", direct" : "", + inserted->cache->no_flush ? ", ignore flushes" : ""); + + if (inserted->has_backing_file) { + monitor_printf(mon, + " Backing file: %s " + "(chain depth: %" PRId64 ")\n", + inserted->backing_file, + inserted->backing_file_depth); + } + + if (inserted->detect_zeroes != BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF) { + monitor_printf(mon, " Detect zeroes: %s\n", + BlockdevDetectZeroesOptions_str(inserted->detect_zeroes)); + } + + if (inserted->bps || inserted->bps_rd || inserted->bps_wr || + inserted->iops || inserted->iops_rd || inserted->iops_wr) + { + monitor_printf(mon, " I/O throttling: bps=%" PRId64 + " bps_rd=%" PRId64 " bps_wr=%" PRId64 + " bps_max=%" PRId64 + " bps_rd_max=%" PRId64 + " bps_wr_max=%" PRId64 + " iops=%" PRId64 " iops_rd=%" PRId64 + " iops_wr=%" PRId64 + " iops_max=%" PRId64 + " iops_rd_max=%" PRId64 + " iops_wr_max=%" PRId64 + " iops_size=%" PRId64 + " group=%s\n", + inserted->bps, + inserted->bps_rd, + inserted->bps_wr, + inserted->bps_max, + inserted->bps_rd_max, + inserted->bps_wr_max, + inserted->iops, + inserted->iops_rd, + inserted->iops_wr, + inserted->iops_max, + inserted->iops_rd_max, + inserted->iops_wr_max, + inserted->iops_size, + inserted->group); + } + + if (verbose) { + monitor_printf(mon, "\nImages:\n"); + image_info = inserted->image; + while (1) { + bdrv_image_info_dump(image_info); + if (image_info->has_backing_image) { + image_info = image_info->backing_image; + } else { + break; + } + } + } +} + +void hmp_info_block(Monitor *mon, const QDict *qdict) +{ + BlockInfoList *block_list, *info; + BlockDeviceInfoList *blockdev_list, *blockdev; + const char *device = qdict_get_try_str(qdict, "device"); + bool verbose = qdict_get_try_bool(qdict, "verbose", false); + bool nodes = qdict_get_try_bool(qdict, "nodes", false); + bool printed = false; + + /* Print BlockBackend information */ + if (!nodes) { + block_list = qmp_query_block(NULL); + } else { + block_list = NULL; + } + + for (info = block_list; info; info = info->next) { + if (device && strcmp(device, info->value->device)) { + continue; + } + + if (info != block_list) { + monitor_printf(mon, "\n"); + } + + print_block_info(mon, info->value, info->value->has_inserted + ? info->value->inserted : NULL, + verbose); + printed = true; + } + + qapi_free_BlockInfoList(block_list); + + if ((!device && !nodes) || printed) { + return; + } + + /* Print node information */ + blockdev_list = qmp_query_named_block_nodes(false, false, NULL); + for (blockdev = blockdev_list; blockdev; blockdev = blockdev->next) { + assert(blockdev->value->has_node_name); + if (device && strcmp(device, blockdev->value->node_name)) { + continue; + } + + if (blockdev != blockdev_list) { + monitor_printf(mon, "\n"); + } + + print_block_info(mon, NULL, blockdev->value, verbose); + } + qapi_free_BlockDeviceInfoList(blockdev_list); +} + +void hmp_info_blockstats(Monitor *mon, const QDict *qdict) +{ + BlockStatsList *stats_list, *stats; + + stats_list = qmp_query_blockstats(false, false, NULL); + + for (stats = stats_list; stats; stats = stats->next) { + if (!stats->value->has_device) { + continue; + } + + monitor_printf(mon, "%s:", stats->value->device); + monitor_printf(mon, " rd_bytes=%" PRId64 + " wr_bytes=%" PRId64 + " rd_operations=%" PRId64 + " wr_operations=%" PRId64 + " flush_operations=%" PRId64 + " wr_total_time_ns=%" PRId64 + " rd_total_time_ns=%" PRId64 + " flush_total_time_ns=%" PRId64 + " rd_merged=%" PRId64 + " wr_merged=%" PRId64 + " idle_time_ns=%" PRId64 + "\n", + stats->value->stats->rd_bytes, + stats->value->stats->wr_bytes, + stats->value->stats->rd_operations, + stats->value->stats->wr_operations, + stats->value->stats->flush_operations, + stats->value->stats->wr_total_time_ns, + stats->value->stats->rd_total_time_ns, + stats->value->stats->flush_total_time_ns, + stats->value->stats->rd_merged, + stats->value->stats->wr_merged, + stats->value->stats->idle_time_ns); + } + + qapi_free_BlockStatsList(stats_list); +} + +void hmp_info_block_jobs(Monitor *mon, const QDict *qdict) +{ + BlockJobInfoList *list; + + list = qmp_query_block_jobs(&error_abort); + + if (!list) { + monitor_printf(mon, "No active jobs\n"); + return; + } + + while (list) { + if (strcmp(list->value->type, "stream") == 0) { + monitor_printf(mon, "Streaming device %s: Completed %" PRId64 + " of %" PRId64 " bytes, speed limit %" PRId64 + " bytes/s\n", + list->value->device, + list->value->offset, + list->value->len, + list->value->speed); + } else { + monitor_printf(mon, "Type %s, device %s: Completed %" PRId64 + " of %" PRId64 " bytes, speed limit %" PRId64 + " bytes/s\n", + list->value->type, + list->value->device, + list->value->offset, + list->value->len, + list->value->speed); + } + list = list->next; + } + + qapi_free_BlockJobInfoList(list); +} + +void hmp_info_snapshots(Monitor *mon, const QDict *qdict) +{ + BlockDriverState *bs, *bs1; + BdrvNextIterator it1; + QEMUSnapshotInfo *sn_tab, *sn; + bool no_snapshot = true; + int nb_sns, i; + int total; + int *global_snapshots; + AioContext *aio_context; + + typedef struct SnapshotEntry { + QEMUSnapshotInfo sn; + QTAILQ_ENTRY(SnapshotEntry) next; + } SnapshotEntry; + + typedef struct ImageEntry { + const char *imagename; + QTAILQ_ENTRY(ImageEntry) next; + QTAILQ_HEAD(, SnapshotEntry) snapshots; + } ImageEntry; + + QTAILQ_HEAD(, ImageEntry) image_list = + QTAILQ_HEAD_INITIALIZER(image_list); + + ImageEntry *image_entry, *next_ie; + SnapshotEntry *snapshot_entry; + + bs = bdrv_all_find_vmstate_bs(); + if (!bs) { + monitor_printf(mon, "No available block device supports snapshots\n"); + return; + } + aio_context = bdrv_get_aio_context(bs); + + aio_context_acquire(aio_context); + nb_sns = bdrv_snapshot_list(bs, &sn_tab); + aio_context_release(aio_context); + + if (nb_sns < 0) { + monitor_printf(mon, "bdrv_snapshot_list: error %d\n", nb_sns); + return; + } + + for (bs1 = bdrv_first(&it1); bs1; bs1 = bdrv_next(&it1)) { + int bs1_nb_sns = 0; + ImageEntry *ie; + SnapshotEntry *se; + AioContext *ctx = bdrv_get_aio_context(bs1); + + aio_context_acquire(ctx); + if (bdrv_can_snapshot(bs1)) { + sn = NULL; + bs1_nb_sns = bdrv_snapshot_list(bs1, &sn); + if (bs1_nb_sns > 0) { + no_snapshot = false; + ie = g_new0(ImageEntry, 1); + ie->imagename = bdrv_get_device_name(bs1); + QTAILQ_INIT(&ie->snapshots); + QTAILQ_INSERT_TAIL(&image_list, ie, next); + for (i = 0; i < bs1_nb_sns; i++) { + se = g_new0(SnapshotEntry, 1); + se->sn = sn[i]; + QTAILQ_INSERT_TAIL(&ie->snapshots, se, next); + } + } + g_free(sn); + } + aio_context_release(ctx); + } + + if (no_snapshot) { + monitor_printf(mon, "There is no snapshot available.\n"); + return; + } + + global_snapshots = g_new0(int, nb_sns); + total = 0; + for (i = 0; i < nb_sns; i++) { + SnapshotEntry *next_sn; + if (bdrv_all_find_snapshot(sn_tab[i].name, &bs1) == 0) { + global_snapshots[total] = i; + total++; + QTAILQ_FOREACH(image_entry, &image_list, next) { + QTAILQ_FOREACH_SAFE(snapshot_entry, &image_entry->snapshots, + next, next_sn) { + if (!strcmp(sn_tab[i].name, snapshot_entry->sn.name)) { + QTAILQ_REMOVE(&image_entry->snapshots, snapshot_entry, + next); + g_free(snapshot_entry); + } + } + } + } + } + monitor_printf(mon, "List of snapshots present on all disks:\n"); + + if (total > 0) { + bdrv_snapshot_dump(NULL); + monitor_printf(mon, "\n"); + for (i = 0; i < total; i++) { + sn = &sn_tab[global_snapshots[i]]; + /* + * The ID is not guaranteed to be the same on all images, so + * overwrite it. + */ + pstrcpy(sn->id_str, sizeof(sn->id_str), "--"); + bdrv_snapshot_dump(sn); + monitor_printf(mon, "\n"); + } + } else { + monitor_printf(mon, "None\n"); + } + + QTAILQ_FOREACH(image_entry, &image_list, next) { + if (QTAILQ_EMPTY(&image_entry->snapshots)) { + continue; + } + monitor_printf(mon, + "\nList of partial (non-loadable) snapshots on '%s':\n", + image_entry->imagename); + bdrv_snapshot_dump(NULL); + monitor_printf(mon, "\n"); + QTAILQ_FOREACH(snapshot_entry, &image_entry->snapshots, next) { + bdrv_snapshot_dump(&snapshot_entry->sn); + monitor_printf(mon, "\n"); + } + } + + QTAILQ_FOREACH_SAFE(image_entry, &image_list, next, next_ie) { + SnapshotEntry *next_sn; + QTAILQ_FOREACH_SAFE(snapshot_entry, &image_entry->snapshots, next, + next_sn) { + g_free(snapshot_entry); + } + g_free(image_entry); + } + g_free(sn_tab); + g_free(global_snapshots); +} diff --git a/block/nbd.c b/block/nbd.c index 5f18f78a94..2160859f64 100644 --- a/block/nbd.c +++ b/block/nbd.c @@ -70,6 +70,7 @@ typedef struct BDRVNBDState { CoMutex send_mutex; CoQueue free_sema; Coroutine *connection_co; + Coroutine *teardown_co; QemuCoSleepState *connection_co_sleep_ns_state; bool drained; bool wait_drained_end; @@ -94,6 +95,19 @@ typedef struct BDRVNBDState { static int nbd_client_connect(BlockDriverState *bs, Error **errp); +static void nbd_clear_bdrvstate(BDRVNBDState *s) +{ + object_unref(OBJECT(s->tlscreds)); + qapi_free_SocketAddress(s->saddr); + s->saddr = NULL; + g_free(s->export); + s->export = NULL; + g_free(s->tlscredsid); + s->tlscredsid = NULL; + g_free(s->x_dirty_bitmap); + s->x_dirty_bitmap = NULL; +} + static void nbd_channel_error(BDRVNBDState *s, int ret) { if (ret == -EIO) { @@ -203,7 +217,15 @@ static void nbd_teardown_connection(BlockDriverState *bs) qemu_co_sleep_wake(s->connection_co_sleep_ns_state); } } - BDRV_POLL_WHILE(bs, s->connection_co); + if (qemu_in_coroutine()) { + s->teardown_co = qemu_coroutine_self(); + /* connection_co resumes us when it terminates */ + qemu_coroutine_yield(); + s->teardown_co = NULL; + } else { + BDRV_POLL_WHILE(bs, s->connection_co); + } + assert(!s->connection_co); } static bool nbd_client_connecting(BDRVNBDState *s) @@ -395,6 +417,9 @@ static coroutine_fn void nbd_connection_entry(void *opaque) s->ioc = NULL; } + if (s->teardown_co) { + aio_co_wake(s->teardown_co); + } aio_wait_kick(); } @@ -866,6 +891,7 @@ typedef struct NBDReplyChunkIter { static void nbd_iter_channel_error(NBDReplyChunkIter *iter, int ret, Error **local_err) { + assert(local_err && *local_err); assert(ret < 0); if (!iter->ret) { @@ -1515,8 +1541,10 @@ static int nbd_parse_uri(const char *filename, QDict *options) goto out; } - p = uri->path ? uri->path : "/"; - p += strspn(p, "/"); + p = uri->path ? uri->path : ""; + if (p[0] == '/') { + p++; + } if (p[0]) { qdict_put_str(options, "export", p); } @@ -1864,11 +1892,7 @@ static int nbd_process_options(BlockDriverState *bs, QDict *options, error: if (ret < 0) { - object_unref(OBJECT(s->tlscreds)); - qapi_free_SocketAddress(s->saddr); - g_free(s->export); - g_free(s->tlscredsid); - g_free(s->x_dirty_bitmap); + nbd_clear_bdrvstate(s); } qemu_opts_del(opts); return ret; @@ -1891,6 +1915,7 @@ static int nbd_open(BlockDriverState *bs, QDict *options, int flags, ret = nbd_client_connect(bs, errp); if (ret < 0) { + nbd_clear_bdrvstate(s); return ret; } /* successfully connected */ @@ -1947,12 +1972,7 @@ static void nbd_close(BlockDriverState *bs) BDRVNBDState *s = bs->opaque; nbd_client_close(bs); - - object_unref(OBJECT(s->tlscreds)); - qapi_free_SocketAddress(s->saddr); - g_free(s->export); - g_free(s->tlscredsid); - g_free(s->x_dirty_bitmap); + nbd_clear_bdrvstate(s); } static int64_t nbd_getlength(BlockDriverState *bs) @@ -2018,6 +2038,8 @@ static BlockDriver bdrv_nbd = { .protocol_name = "nbd", .instance_size = sizeof(BDRVNBDState), .bdrv_parse_filename = nbd_parse_filename, + .bdrv_co_create_opts = bdrv_co_create_opts_simple, + .create_opts = &bdrv_create_opts_simple, .bdrv_file_open = nbd_open, .bdrv_reopen_prepare = nbd_client_reopen_prepare, .bdrv_co_preadv = nbd_client_co_preadv, @@ -2043,6 +2065,8 @@ static BlockDriver bdrv_nbd_tcp = { .protocol_name = "nbd+tcp", .instance_size = sizeof(BDRVNBDState), .bdrv_parse_filename = nbd_parse_filename, + .bdrv_co_create_opts = bdrv_co_create_opts_simple, + .create_opts = &bdrv_create_opts_simple, .bdrv_file_open = nbd_open, .bdrv_reopen_prepare = nbd_client_reopen_prepare, .bdrv_co_preadv = nbd_client_co_preadv, @@ -2068,6 +2092,8 @@ static BlockDriver bdrv_nbd_unix = { .protocol_name = "nbd+unix", .instance_size = sizeof(BDRVNBDState), .bdrv_parse_filename = nbd_parse_filename, + .bdrv_co_create_opts = bdrv_co_create_opts_simple, + .create_opts = &bdrv_create_opts_simple, .bdrv_file_open = nbd_open, .bdrv_reopen_prepare = nbd_client_reopen_prepare, .bdrv_co_preadv = nbd_client_co_preadv, diff --git a/block/nfs.c b/block/nfs.c index 9a6311e270..cc2413d5ab 100644 --- a/block/nfs.c +++ b/block/nfs.c @@ -662,7 +662,9 @@ out: return ret; } -static int coroutine_fn nfs_file_co_create_opts(const char *url, QemuOpts *opts, +static int coroutine_fn nfs_file_co_create_opts(BlockDriver *drv, + const char *url, + QemuOpts *opts, Error **errp) { BlockdevCreateOptions *create_options; diff --git a/block/nvme.c b/block/nvme.c index d41c4bda6e..7b7c0cc5d6 100644 --- a/block/nvme.c +++ b/block/nvme.c @@ -1333,6 +1333,9 @@ static BlockDriver bdrv_nvme = { .protocol_name = "nvme", .instance_size = sizeof(BDRVNVMeState), + .bdrv_co_create_opts = bdrv_co_create_opts_simple, + .create_opts = &bdrv_create_opts_simple, + .bdrv_parse_filename = nvme_parse_filename, .bdrv_file_open = nvme_file_open, .bdrv_close = nvme_close, diff --git a/block/parallels.c b/block/parallels.c index 7a01997659..6d4ed77f16 100644 --- a/block/parallels.c +++ b/block/parallels.c @@ -609,7 +609,8 @@ exit: goto out; } -static int coroutine_fn parallels_co_create_opts(const char *filename, +static int coroutine_fn parallels_co_create_opts(BlockDriver *drv, + const char *filename, QemuOpts *opts, Error **errp) { diff --git a/block/qapi-sysemu.c b/block/qapi-sysemu.c new file mode 100644 index 0000000000..8498402ad4 --- /dev/null +++ b/block/qapi-sysemu.c @@ -0,0 +1,590 @@ +/* + * QMP command handlers specific to the system emulators + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * later. See the COPYING file in the top-level directory. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" + +#include "qapi/error.h" +#include "qapi/qapi-commands-block.h" +#include "qapi/qmp/qdict.h" +#include "sysemu/block-backend.h" +#include "sysemu/blockdev.h" + +static BlockBackend *qmp_get_blk(const char *blk_name, const char *qdev_id, + Error **errp) +{ + BlockBackend *blk; + + if (!blk_name == !qdev_id) { + error_setg(errp, "Need exactly one of 'device' and 'id'"); + return NULL; + } + + if (qdev_id) { + blk = blk_by_qdev_id(qdev_id, errp); + } else { + blk = blk_by_name(blk_name); + if (blk == NULL) { + error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, + "Device '%s' not found", blk_name); + } + } + + return blk; +} + +/* + * Attempt to open the tray of @device. + * If @force, ignore its tray lock. + * Else, if the tray is locked, don't open it, but ask the guest to open it. + * On error, store an error through @errp and return -errno. + * If @device does not exist, return -ENODEV. + * If it has no removable media, return -ENOTSUP. + * If it has no tray, return -ENOSYS. + * If the guest was asked to open the tray, return -EINPROGRESS. + * Else, return 0. + */ +static int do_open_tray(const char *blk_name, const char *qdev_id, + bool force, Error **errp) +{ + BlockBackend *blk; + const char *device = qdev_id ?: blk_name; + bool locked; + + blk = qmp_get_blk(blk_name, qdev_id, errp); + if (!blk) { + return -ENODEV; + } + + if (!blk_dev_has_removable_media(blk)) { + error_setg(errp, "Device '%s' is not removable", device); + return -ENOTSUP; + } + + if (!blk_dev_has_tray(blk)) { + error_setg(errp, "Device '%s' does not have a tray", device); + return -ENOSYS; + } + + if (blk_dev_is_tray_open(blk)) { + return 0; + } + + locked = blk_dev_is_medium_locked(blk); + if (locked) { + blk_dev_eject_request(blk, force); + } + + if (!locked || force) { + blk_dev_change_media_cb(blk, false, &error_abort); + } + + if (locked && !force) { + error_setg(errp, "Device '%s' is locked and force was not specified, " + "wait for tray to open and try again", device); + return -EINPROGRESS; + } + + return 0; +} + +void qmp_blockdev_open_tray(bool has_device, const char *device, + bool has_id, const char *id, + bool has_force, bool force, + Error **errp) +{ + Error *local_err = NULL; + int rc; + + if (!has_force) { + force = false; + } + rc = do_open_tray(has_device ? device : NULL, + has_id ? id : NULL, + force, &local_err); + if (rc && rc != -ENOSYS && rc != -EINPROGRESS) { + error_propagate(errp, local_err); + return; + } + error_free(local_err); +} + +void qmp_blockdev_close_tray(bool has_device, const char *device, + bool has_id, const char *id, + Error **errp) +{ + BlockBackend *blk; + Error *local_err = NULL; + + device = has_device ? device : NULL; + id = has_id ? id : NULL; + + blk = qmp_get_blk(device, id, errp); + if (!blk) { + return; + } + + if (!blk_dev_has_removable_media(blk)) { + error_setg(errp, "Device '%s' is not removable", device ?: id); + return; + } + + if (!blk_dev_has_tray(blk)) { + /* Ignore this command on tray-less devices */ + return; + } + + if (!blk_dev_is_tray_open(blk)) { + return; + } + + blk_dev_change_media_cb(blk, true, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } +} + +static void blockdev_remove_medium(bool has_device, const char *device, + bool has_id, const char *id, Error **errp) +{ + BlockBackend *blk; + BlockDriverState *bs; + AioContext *aio_context; + bool has_attached_device; + + device = has_device ? device : NULL; + id = has_id ? id : NULL; + + blk = qmp_get_blk(device, id, errp); + if (!blk) { + return; + } + + /* For BBs without a device, we can exchange the BDS tree at will */ + has_attached_device = blk_get_attached_dev(blk); + + if (has_attached_device && !blk_dev_has_removable_media(blk)) { + error_setg(errp, "Device '%s' is not removable", device ?: id); + return; + } + + if (has_attached_device && blk_dev_has_tray(blk) && + !blk_dev_is_tray_open(blk)) + { + error_setg(errp, "Tray of device '%s' is not open", device ?: id); + return; + } + + bs = blk_bs(blk); + if (!bs) { + return; + } + + aio_context = bdrv_get_aio_context(bs); + aio_context_acquire(aio_context); + + if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_EJECT, errp)) { + goto out; + } + + blk_remove_bs(blk); + + if (!blk_dev_has_tray(blk)) { + /* For tray-less devices, blockdev-open-tray is a no-op (or may not be + * called at all); therefore, the medium needs to be ejected here. + * Do it after blk_remove_bs() so blk_is_inserted(blk) returns the @load + * value passed here (i.e. false). */ + blk_dev_change_media_cb(blk, false, &error_abort); + } + +out: + aio_context_release(aio_context); +} + +void qmp_blockdev_remove_medium(const char *id, Error **errp) +{ + blockdev_remove_medium(false, NULL, true, id, errp); +} + +static void qmp_blockdev_insert_anon_medium(BlockBackend *blk, + BlockDriverState *bs, Error **errp) +{ + Error *local_err = NULL; + bool has_device; + int ret; + + /* For BBs without a device, we can exchange the BDS tree at will */ + has_device = blk_get_attached_dev(blk); + + if (has_device && !blk_dev_has_removable_media(blk)) { + error_setg(errp, "Device is not removable"); + return; + } + + if (has_device && blk_dev_has_tray(blk) && !blk_dev_is_tray_open(blk)) { + error_setg(errp, "Tray of the device is not open"); + return; + } + + if (blk_bs(blk)) { + error_setg(errp, "There already is a medium in the device"); + return; + } + + ret = blk_insert_bs(blk, bs, errp); + if (ret < 0) { + return; + } + + if (!blk_dev_has_tray(blk)) { + /* For tray-less devices, blockdev-close-tray is a no-op (or may not be + * called at all); therefore, the medium needs to be pushed into the + * slot here. + * Do it after blk_insert_bs() so blk_is_inserted(blk) returns the @load + * value passed here (i.e. true). */ + blk_dev_change_media_cb(blk, true, &local_err); + if (local_err) { + error_propagate(errp, local_err); + blk_remove_bs(blk); + return; + } + } +} + +static void blockdev_insert_medium(bool has_device, const char *device, + bool has_id, const char *id, + const char *node_name, Error **errp) +{ + BlockBackend *blk; + BlockDriverState *bs; + + blk = qmp_get_blk(has_device ? device : NULL, + has_id ? id : NULL, + errp); + if (!blk) { + return; + } + + bs = bdrv_find_node(node_name); + if (!bs) { + error_setg(errp, "Node '%s' not found", node_name); + return; + } + + if (bdrv_has_blk(bs)) { + error_setg(errp, "Node '%s' is already in use", node_name); + return; + } + + qmp_blockdev_insert_anon_medium(blk, bs, errp); +} + +void qmp_blockdev_insert_medium(const char *id, const char *node_name, + Error **errp) +{ + blockdev_insert_medium(false, NULL, true, id, node_name, errp); +} + +void qmp_blockdev_change_medium(bool has_device, const char *device, + bool has_id, const char *id, + const char *filename, + bool has_format, const char *format, + bool has_read_only, + BlockdevChangeReadOnlyMode read_only, + Error **errp) +{ + BlockBackend *blk; + BlockDriverState *medium_bs = NULL; + int bdrv_flags; + bool detect_zeroes; + int rc; + QDict *options = NULL; + Error *err = NULL; + + blk = qmp_get_blk(has_device ? device : NULL, + has_id ? id : NULL, + errp); + if (!blk) { + goto fail; + } + + if (blk_bs(blk)) { + blk_update_root_state(blk); + } + + bdrv_flags = blk_get_open_flags_from_root_state(blk); + bdrv_flags &= ~(BDRV_O_TEMPORARY | BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING | + BDRV_O_PROTOCOL | BDRV_O_AUTO_RDONLY); + + if (!has_read_only) { + read_only = BLOCKDEV_CHANGE_READ_ONLY_MODE_RETAIN; + } + + switch (read_only) { + case BLOCKDEV_CHANGE_READ_ONLY_MODE_RETAIN: + break; + + case BLOCKDEV_CHANGE_READ_ONLY_MODE_READ_ONLY: + bdrv_flags &= ~BDRV_O_RDWR; + break; + + case BLOCKDEV_CHANGE_READ_ONLY_MODE_READ_WRITE: + bdrv_flags |= BDRV_O_RDWR; + break; + + default: + abort(); + } + + options = qdict_new(); + detect_zeroes = blk_get_detect_zeroes_from_root_state(blk); + qdict_put_str(options, "detect-zeroes", detect_zeroes ? "on" : "off"); + + if (has_format) { + qdict_put_str(options, "driver", format); + } + + medium_bs = bdrv_open(filename, NULL, options, bdrv_flags, errp); + if (!medium_bs) { + goto fail; + } + + rc = do_open_tray(has_device ? device : NULL, + has_id ? id : NULL, + false, &err); + if (rc && rc != -ENOSYS) { + error_propagate(errp, err); + goto fail; + } + error_free(err); + err = NULL; + + blockdev_remove_medium(has_device, device, has_id, id, &err); + if (err) { + error_propagate(errp, err); + goto fail; + } + + qmp_blockdev_insert_anon_medium(blk, medium_bs, &err); + if (err) { + error_propagate(errp, err); + goto fail; + } + + qmp_blockdev_close_tray(has_device, device, has_id, id, errp); + +fail: + /* If the medium has been inserted, the device has its own reference, so + * ours must be relinquished; and if it has not been inserted successfully, + * the reference must be relinquished anyway */ + bdrv_unref(medium_bs); +} + +void qmp_eject(bool has_device, const char *device, + bool has_id, const char *id, + bool has_force, bool force, Error **errp) +{ + Error *local_err = NULL; + int rc; + + if (!has_force) { + force = false; + } + + rc = do_open_tray(has_device ? device : NULL, + has_id ? id : NULL, + force, &local_err); + if (rc && rc != -ENOSYS) { + error_propagate(errp, local_err); + return; + } + error_free(local_err); + + blockdev_remove_medium(has_device, device, has_id, id, errp); +} + +/* throttling disk I/O limits */ +void qmp_block_set_io_throttle(BlockIOThrottle *arg, Error **errp) +{ + ThrottleConfig cfg; + BlockDriverState *bs; + BlockBackend *blk; + AioContext *aio_context; + + blk = qmp_get_blk(arg->has_device ? arg->device : NULL, + arg->has_id ? arg->id : NULL, + errp); + if (!blk) { + return; + } + + aio_context = blk_get_aio_context(blk); + aio_context_acquire(aio_context); + + bs = blk_bs(blk); + if (!bs) { + error_setg(errp, "Device has no medium"); + goto out; + } + + throttle_config_init(&cfg); + cfg.buckets[THROTTLE_BPS_TOTAL].avg = arg->bps; + cfg.buckets[THROTTLE_BPS_READ].avg = arg->bps_rd; + cfg.buckets[THROTTLE_BPS_WRITE].avg = arg->bps_wr; + + cfg.buckets[THROTTLE_OPS_TOTAL].avg = arg->iops; + cfg.buckets[THROTTLE_OPS_READ].avg = arg->iops_rd; + cfg.buckets[THROTTLE_OPS_WRITE].avg = arg->iops_wr; + + if (arg->has_bps_max) { + cfg.buckets[THROTTLE_BPS_TOTAL].max = arg->bps_max; + } + if (arg->has_bps_rd_max) { + cfg.buckets[THROTTLE_BPS_READ].max = arg->bps_rd_max; + } + if (arg->has_bps_wr_max) { + cfg.buckets[THROTTLE_BPS_WRITE].max = arg->bps_wr_max; + } + if (arg->has_iops_max) { + cfg.buckets[THROTTLE_OPS_TOTAL].max = arg->iops_max; + } + if (arg->has_iops_rd_max) { + cfg.buckets[THROTTLE_OPS_READ].max = arg->iops_rd_max; + } + if (arg->has_iops_wr_max) { + cfg.buckets[THROTTLE_OPS_WRITE].max = arg->iops_wr_max; + } + + if (arg->has_bps_max_length) { + cfg.buckets[THROTTLE_BPS_TOTAL].burst_length = arg->bps_max_length; + } + if (arg->has_bps_rd_max_length) { + cfg.buckets[THROTTLE_BPS_READ].burst_length = arg->bps_rd_max_length; + } + if (arg->has_bps_wr_max_length) { + cfg.buckets[THROTTLE_BPS_WRITE].burst_length = arg->bps_wr_max_length; + } + if (arg->has_iops_max_length) { + cfg.buckets[THROTTLE_OPS_TOTAL].burst_length = arg->iops_max_length; + } + if (arg->has_iops_rd_max_length) { + cfg.buckets[THROTTLE_OPS_READ].burst_length = arg->iops_rd_max_length; + } + if (arg->has_iops_wr_max_length) { + cfg.buckets[THROTTLE_OPS_WRITE].burst_length = arg->iops_wr_max_length; + } + + if (arg->has_iops_size) { + cfg.op_size = arg->iops_size; + } + + if (!throttle_is_valid(&cfg, errp)) { + goto out; + } + + if (throttle_enabled(&cfg)) { + /* Enable I/O limits if they're not enabled yet, otherwise + * just update the throttling group. */ + if (!blk_get_public(blk)->throttle_group_member.throttle_state) { + blk_io_limits_enable(blk, + arg->has_group ? arg->group : + arg->has_device ? arg->device : + arg->id); + } else if (arg->has_group) { + blk_io_limits_update_group(blk, arg->group); + } + /* Set the new throttling configuration */ + blk_set_io_limits(blk, &cfg); + } else if (blk_get_public(blk)->throttle_group_member.throttle_state) { + /* If all throttling settings are set to 0, disable I/O limits */ + blk_io_limits_disable(blk); + } + +out: + aio_context_release(aio_context); +} + +void qmp_block_latency_histogram_set( + const char *id, + bool has_boundaries, uint64List *boundaries, + bool has_boundaries_read, uint64List *boundaries_read, + bool has_boundaries_write, uint64List *boundaries_write, + bool has_boundaries_flush, uint64List *boundaries_flush, + Error **errp) +{ + BlockBackend *blk = qmp_get_blk(NULL, id, errp); + BlockAcctStats *stats; + int ret; + + if (!blk) { + return; + } + + stats = blk_get_stats(blk); + + if (!has_boundaries && !has_boundaries_read && !has_boundaries_write && + !has_boundaries_flush) + { + block_latency_histograms_clear(stats); + return; + } + + if (has_boundaries || has_boundaries_read) { + ret = block_latency_histogram_set( + stats, BLOCK_ACCT_READ, + has_boundaries_read ? boundaries_read : boundaries); + if (ret) { + error_setg(errp, "Device '%s' set read boundaries fail", id); + return; + } + } + + if (has_boundaries || has_boundaries_write) { + ret = block_latency_histogram_set( + stats, BLOCK_ACCT_WRITE, + has_boundaries_write ? boundaries_write : boundaries); + if (ret) { + error_setg(errp, "Device '%s' set write boundaries fail", id); + return; + } + } + + if (has_boundaries || has_boundaries_flush) { + ret = block_latency_histogram_set( + stats, BLOCK_ACCT_FLUSH, + has_boundaries_flush ? boundaries_flush : boundaries); + if (ret) { + error_setg(errp, "Device '%s' set flush boundaries fail", id); + return; + } + } +} diff --git a/block/qapi.c b/block/qapi.c index 9a5d0c9b27..afd9f3b4a7 100644 --- a/block/qapi.c +++ b/block/qapi.c @@ -42,7 +42,9 @@ #include "qemu/cutils.h" BlockDeviceInfo *bdrv_block_device_info(BlockBackend *blk, - BlockDriverState *bs, Error **errp) + BlockDriverState *bs, + bool flat, + Error **errp) { ImageInfo **p_image_info; BlockDriverState *bs0; @@ -156,6 +158,11 @@ BlockDeviceInfo *bdrv_block_device_info(BlockBackend *blk, return NULL; } + /* stop gathering data for flat output */ + if (flat) { + break; + } + if (bs0->drv && bs0->backing) { info->backing_file_depth++; bs0 = bs0->backing->bs; @@ -389,7 +396,7 @@ static void bdrv_query_info(BlockBackend *blk, BlockInfo **p_info, if (bs && bs->drv) { info->has_inserted = true; - info->inserted = bdrv_block_device_info(blk, bs, errp); + info->inserted = bdrv_block_device_info(blk, bs, false, errp); if (info->inserted == NULL) { goto err; } @@ -657,7 +664,7 @@ void bdrv_snapshot_dump(QEMUSnapshotInfo *sn) char *sizing = NULL; if (!sn) { - qemu_printf("%-10s%-20s%7s%20s%15s", + qemu_printf("%-10s%-20s%11s%20s%15s", "ID", "TAG", "VM SIZE", "DATE", "VM CLOCK"); } else { ti = sn->date_sec; @@ -672,7 +679,7 @@ void bdrv_snapshot_dump(QEMUSnapshotInfo *sn) (int)(secs % 60), (int)((sn->vm_clock_nsec / 1000000) % 1000)); sizing = size_to_str(sn->vm_state_size); - qemu_printf("%-10s%-20s%7s%20s%15s", + qemu_printf("%-10s%-20s%11s%20s%15s", sn->id_str, sn->name, sizing, date_buf, diff --git a/block/qcow.c b/block/qcow.c index fce8989868..8973e4e565 100644 --- a/block/qcow.c +++ b/block/qcow.c @@ -934,7 +934,8 @@ exit: return ret; } -static int coroutine_fn qcow_co_create_opts(const char *filename, +static int coroutine_fn qcow_co_create_opts(BlockDriver *drv, + const char *filename, QemuOpts *opts, Error **errp) { BlockdevCreateOptions *create_options = NULL; diff --git a/block/qcow2-bitmap.c b/block/qcow2-bitmap.c index c6c8ebbe89..cb06954b4a 100644 --- a/block/qcow2-bitmap.c +++ b/block/qcow2-bitmap.c @@ -647,7 +647,6 @@ static Qcow2BitmapList *bitmap_list_load(BlockDriverState *bs, uint64_t offset, return bm_list; broken_dir: - ret = -EINVAL; error_setg(errp, "Broken bitmap directory"); fail: @@ -1289,7 +1288,6 @@ static uint64_t *store_bitmap_data(BlockDriverState *bs, uint64_t bm_size = bdrv_dirty_bitmap_size(bitmap); const char *bm_name = bdrv_dirty_bitmap_name(bitmap); uint8_t *buf = NULL; - BdrvDirtyBitmapIter *dbi; uint64_t *tb; uint64_t tb_size = size_to_clusters(s, @@ -1308,12 +1306,14 @@ static uint64_t *store_bitmap_data(BlockDriverState *bs, return NULL; } - dbi = bdrv_dirty_iter_new(bitmap); buf = g_malloc(s->cluster_size); limit = bytes_covered_by_bitmap_cluster(s, bitmap); assert(DIV_ROUND_UP(bm_size, limit) == tb_size); - while ((offset = bdrv_dirty_iter_next(dbi)) >= 0) { + offset = 0; + while ((offset = bdrv_dirty_bitmap_next_dirty(bitmap, offset, INT64_MAX)) + >= 0) + { uint64_t cluster = offset / limit; uint64_t end, write_size; int64_t off; @@ -1356,23 +1356,17 @@ static uint64_t *store_bitmap_data(BlockDriverState *bs, goto fail; } - if (end >= bm_size) { - break; - } - - bdrv_set_dirty_iter(dbi, end); + offset = end; } *bitmap_table_size = tb_size; g_free(buf); - bdrv_dirty_iter_free(dbi); return tb; fail: clear_bitmap_table(bs, tb, tb_size); g_free(buf); - bdrv_dirty_iter_free(dbi); g_free(tb); return NULL; @@ -1703,8 +1697,14 @@ bool coroutine_fn qcow2_co_can_store_new_dirty_bitmap(BlockDriverState *bs, Error **errp) { BDRVQcow2State *s = bs->opaque; - bool found; - Qcow2BitmapList *bm_list; + BdrvDirtyBitmap *bitmap; + uint64_t bitmap_directory_size = 0; + uint32_t nb_bitmaps = 0; + + if (bdrv_find_dirty_bitmap(bs, name)) { + error_setg(errp, "Bitmap already exists: %s", name); + return false; + } if (s->qcow_version < 3) { /* Without autoclear_features, we would always have to assume @@ -1720,38 +1720,27 @@ bool coroutine_fn qcow2_co_can_store_new_dirty_bitmap(BlockDriverState *bs, goto fail; } - if (s->nb_bitmaps == 0) { - return true; + FOR_EACH_DIRTY_BITMAP(bs, bitmap) { + if (bdrv_dirty_bitmap_get_persistence(bitmap)) { + nb_bitmaps++; + bitmap_directory_size += + calc_dir_entry_size(strlen(bdrv_dirty_bitmap_name(bitmap)), 0); + } } + nb_bitmaps++; + bitmap_directory_size += calc_dir_entry_size(strlen(name), 0); - if (s->nb_bitmaps >= QCOW2_MAX_BITMAPS) { + if (nb_bitmaps > QCOW2_MAX_BITMAPS) { error_setg(errp, "Maximum number of persistent bitmaps is already reached"); goto fail; } - if (s->bitmap_directory_size + calc_dir_entry_size(strlen(name), 0) > - QCOW2_MAX_BITMAP_DIRECTORY_SIZE) - { + if (bitmap_directory_size > QCOW2_MAX_BITMAP_DIRECTORY_SIZE) { error_setg(errp, "Not enough space in the bitmap directory"); goto fail; } - qemu_co_mutex_lock(&s->lock); - bm_list = bitmap_list_load(bs, s->bitmap_directory_offset, - s->bitmap_directory_size, errp); - qemu_co_mutex_unlock(&s->lock); - if (bm_list == NULL) { - goto fail; - } - - found = find_bitmap_by_name(bm_list, name); - bitmap_list_free(bm_list); - if (found) { - error_setg(errp, "Bitmap with the same name is already stored"); - goto fail; - } - return true; fail: diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c index 8982b7b762..17f1363279 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); } @@ -1015,8 +1026,11 @@ err: void qcow2_alloc_cluster_abort(BlockDriverState *bs, QCowL2Meta *m) { BDRVQcow2State *s = bs->opaque; - qcow2_free_clusters(bs, m->alloc_offset, m->nb_clusters << s->cluster_bits, - QCOW2_DISCARD_NEVER); + if (!has_data_file(bs) && !m->keep_old_clusters) { + qcow2_free_clusters(bs, m->alloc_offset, + m->nb_clusters << s->cluster_bits, + QCOW2_DISCARD_NEVER); + } } /* @@ -1913,6 +1927,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..7ef1c0e42a 100644 --- a/block/qcow2-refcount.c +++ b/block/qcow2-refcount.c @@ -889,6 +889,7 @@ static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs, offset); if (table != NULL) { qcow2_cache_put(s->refcount_block_cache, &refcount_block); + old_table_index = -1; qcow2_cache_discard(s->refcount_block_cache, table); } @@ -1262,7 +1263,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-threads.c b/block/qcow2-threads.c index 8f5a0d1ebe..a68126f291 100644 --- a/block/qcow2-threads.c +++ b/block/qcow2-threads.c @@ -128,12 +128,12 @@ static ssize_t qcow2_compress(void *dest, size_t dest_size, * @src - source buffer, @src_size bytes * * Returns: 0 on success - * -1 on fail + * -EIO on fail */ static ssize_t qcow2_decompress(void *dest, size_t dest_size, const void *src, size_t src_size) { - int ret = 0; + int ret; z_stream strm; memset(&strm, 0, sizeof(strm)); @@ -144,17 +144,19 @@ static ssize_t qcow2_decompress(void *dest, size_t dest_size, ret = inflateInit2(&strm, -12); if (ret != Z_OK) { - return -1; + return -EIO; } ret = inflate(&strm, Z_FINISH); - if ((ret != Z_STREAM_END && ret != Z_BUF_ERROR) || strm.avail_out != 0) { + if ((ret == Z_STREAM_END || ret == Z_BUF_ERROR) && strm.avail_out == 0) { /* * We approve Z_BUF_ERROR because we need @dest buffer to be filled, but * @src buffer may be processed partly (because in qcow2 we know size of * compressed data with precision of one sector) */ - ret = -1; + ret = 0; + } else { + ret = -EIO; } inflateEnd(&strm); @@ -246,12 +248,15 @@ qcow2_co_encdec(BlockDriverState *bs, uint64_t host_offset, .len = len, .func = func, }; + uint64_t sector_size; - assert(QEMU_IS_ALIGNED(guest_offset, BDRV_SECTOR_SIZE)); - assert(QEMU_IS_ALIGNED(host_offset, BDRV_SECTOR_SIZE)); - assert(QEMU_IS_ALIGNED(len, BDRV_SECTOR_SIZE)); assert(s->crypto); + sector_size = qcrypto_block_get_sector_size(s->crypto); + assert(QEMU_IS_ALIGNED(guest_offset, sector_size)); + assert(QEMU_IS_ALIGNED(host_offset, sector_size)); + assert(QEMU_IS_ALIGNED(len, sector_size)); + return len == 0 ? 0 : qcow2_co_process(bs, qcow2_encdec_pool_func, &arg); } @@ -270,7 +275,8 @@ qcow2_co_encdec(BlockDriverState *bs, uint64_t host_offset, * will be written to the underlying storage device at * @host_offset * - * @len - length of the buffer (must be a BDRV_SECTOR_SIZE multiple) + * @len - length of the buffer (must be a multiple of the encryption + * sector size) * * Depending on the encryption method, @host_offset and/or @guest_offset * may be used for generating the initialization vector for diff --git a/block/qcow2.c b/block/qcow2.c index 7c18721741..b524b0c53f 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -135,13 +135,16 @@ static ssize_t qcow2_crypto_hdr_init_func(QCryptoBlock *block, size_t headerlen, s->crypto_header.length = headerlen; s->crypto_header.offset = ret; - /* Zero fill remaining space in cluster so it has predictable - * content in case of future spec changes */ + /* + * Zero fill all space in cluster so it has predictable + * content, as we may not initialize some regions of the + * header (eg only 1 out of 8 key slots will be initialized) + */ clusterlen = size_to_clusters(s, headerlen) * s->cluster_size; assert(qcow2_pre_write_overlap_check(bs, 0, ret, clusterlen, false) == 0); ret = bdrv_pwrite_zeroes(bs->file, - ret + headerlen, - clusterlen - headerlen, 0); + ret, + clusterlen, 0); if (ret < 0) { error_setg_errno(errp, -ret, "Could not zero fill encryption header"); return -1; @@ -174,7 +177,7 @@ static ssize_t qcow2_crypto_hdr_write_func(QCryptoBlock *block, size_t offset, } -/* +/* * read qcow2 extension and fill bs * start reading from start_offset * finish reading upon magic of value 0 or when end_offset reached @@ -367,7 +370,7 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset, return -EINVAL; } - if (bitmaps_ext.bitmap_directory_offset & (s->cluster_size - 1)) { + if (offset_into_cluster(s, bitmaps_ext.bitmap_directory_offset)) { error_setg(errp, "bitmaps_ext: " "invalid bitmap directory offset"); return -EINVAL; @@ -453,16 +456,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 +472,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 +1494,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; @@ -1705,14 +1707,14 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options, if (!(bdrv_get_flags(bs) & BDRV_O_INACTIVE)) { /* It's case 1, 2 or 3.2. Or 3.1 which is BUG in management layer. */ bool header_updated = qcow2_load_dirty_bitmaps(bs, &local_err); + if (local_err != NULL) { + error_propagate(errp, local_err); + ret = -EINVAL; + goto fail; + } update_header = update_header && !header_updated; } - if (local_err != NULL) { - error_propagate(errp, local_err); - ret = -EINVAL; - goto fail; - } if (update_header) { ret = qcow2_update_header(bs); @@ -1722,7 +1724,8 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options, } } - bs->supported_zero_flags = header.version >= 3 ? BDRV_REQ_MAY_UNMAP : 0; + bs->supported_zero_flags = header.version >= 3 ? + BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK : 0; /* Repair image if dirty */ if (!(flags & (BDRV_O_CHECK | BDRV_O_INACTIVE)) && !bs->read_only && @@ -1755,6 +1758,7 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options, g_free(s->image_data_file); if (has_data_file(bs)) { bdrv_unref_child(bs, s->data_file); + s->data_file = NULL; } g_free(s->unknown_header_fields); cleanup_unknown_header_ext(bs); @@ -1881,6 +1885,11 @@ fail: static void qcow2_reopen_commit(BDRVReopenState *state) { qcow2_update_options_commit(state->bs, state->opaque); + g_free(state->opaque); +} + +static void qcow2_reopen_commit_post(BDRVReopenState *state) +{ if (state->flags & BDRV_O_RDWR) { Error *local_err = NULL; @@ -1895,7 +1904,6 @@ static void qcow2_reopen_commit(BDRVReopenState *state) bdrv_get_node_name(state->bs)); } } - g_free(state->opaque); } static void qcow2_reopen_abort(BDRVReopenState *state) @@ -1958,9 +1966,8 @@ static int coroutine_fn qcow2_co_block_status(BlockDriverState *bs, { BDRVQcow2State *s = bs->opaque; uint64_t cluster_offset; - int index_in_cluster, ret; unsigned int bytes; - int status = 0; + int ret, status = 0; qemu_co_mutex_lock(&s->lock); @@ -1981,8 +1988,7 @@ static int coroutine_fn qcow2_co_block_status(BlockDriverState *bs, if ((ret == QCOW2_CLUSTER_NORMAL || ret == QCOW2_CLUSTER_ZERO_ALLOC) && !s->crypto) { - index_in_cluster = offset & (s->cluster_size - 1); - *map = cluster_offset | index_in_cluster; + *map = cluster_offset | offset_into_cluster(s, offset); *file = s->data_file->bs; status |= BDRV_BLOCK_OFFSET_VALID; } @@ -2070,8 +2076,6 @@ qcow2_co_preadv_encrypted(BlockDriverState *bs, goto fail; } - assert(QEMU_IS_ALIGNED(offset, BDRV_SECTOR_SIZE)); - assert(QEMU_IS_ALIGNED(bytes, BDRV_SECTOR_SIZE)); if (qcow2_co_decrypt(bs, file_cluster_offset + offset_into_cluster(s, offset), offset, buf, bytes) < 0) @@ -2169,10 +2173,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); @@ -2508,7 +2509,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, @@ -2610,6 +2611,7 @@ static void qcow2_close(BlockDriverState *bs) qcrypto_block_free(s->crypto); s->crypto = NULL; + qapi_free_QCryptoBlockOpenOptions(s->crypto_opts); g_free(s->unknown_header_fields); cleanup_unknown_header_ext(bs); @@ -2620,6 +2622,7 @@ static void qcow2_close(BlockDriverState *bs) if (has_data_file(bs)) { bdrv_unref_child(bs, s->data_file); + s->data_file = NULL; } qcow2_refcount_close(bs); @@ -2820,9 +2823,16 @@ int qcow2_update_header(BlockDriverState *bs) buflen -= ret; } - /* Feature table */ - if (s->qcow_version >= 3) { - Qcow2Feature features[] = { + /* + * Feature table. A mere 8 feature names occupies 392 bytes, and + * when coupled with the v3 minimum header of 104 bytes plus the + * 8-byte end-of-extension marker, that would leave only 8 bytes + * for a backing file name in an image with 512-byte clusters. + * Thus, we choose to omit this header for cluster sizes 4k and + * smaller. + */ + if (s->qcow_version >= 3 && s->cluster_size > 4096) { + static const Qcow2Feature features[] = { { .type = QCOW2_FEAT_TYPE_INCOMPATIBLE, .bit = QCOW2_INCOMPAT_DIRTY_BITNR, @@ -2843,6 +2853,16 @@ int qcow2_update_header(BlockDriverState *bs) .bit = QCOW2_COMPAT_LAZY_REFCOUNTS_BITNR, .name = "lazy refcounts", }, + { + .type = QCOW2_FEAT_TYPE_AUTOCLEAR, + .bit = QCOW2_AUTOCLEAR_BITMAPS_BITNR, + .name = "bitmaps", + }, + { + .type = QCOW2_FEAT_TYPE_AUTOCLEAR, + .bit = QCOW2_AUTOCLEAR_DATA_FILE_RAW_BITNR, + .name = "raw external data", + }, }; ret = header_ext_add(buf, QCOW2_EXT_MAGIC_FEATURE_TABLE, @@ -3252,7 +3272,7 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp) * inconsistency later. * * We do need a refcount table because growing the refcount table means - * allocating two new refcount blocks - the seconds of which would be at + * allocating two new refcount blocks - the second of which would be at * 2 GB for 64k clusters, and we don't want to have a 2 GB initial file * size for any qcow2 image. */ @@ -3277,7 +3297,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; } @@ -3496,7 +3517,7 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp) goto out; } - /* Want a backing file? There you go.*/ + /* Want a backing file? There you go. */ if (qcow2_opts->has_backing_file) { const char *backing_format = NULL; @@ -3554,7 +3575,9 @@ out: return ret; } -static int coroutine_fn qcow2_co_create_opts(const char *filename, QemuOpts *opts, +static int coroutine_fn qcow2_co_create_opts(BlockDriver *drv, + const char *filename, + QemuOpts *opts, Error **errp) { BlockdevCreateOptions *create_options = NULL; @@ -3761,6 +3784,12 @@ static coroutine_fn int qcow2_co_pdiscard(BlockDriverState *bs, int ret; BDRVQcow2State *s = bs->opaque; + /* If the image does not support QCOW_OFLAG_ZERO then discarding + * clusters could expose stale data from the backing file. */ + if (s->qcow_version < 3 && bs->backing) { + return -ENOTSUP; + } + if (!QEMU_IS_ALIGNED(offset | bytes, s->cluster_size)) { assert(bytes < s->cluster_size); /* Ignore partial clusters, except for the special case of the @@ -3833,10 +3862,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: @@ -3898,7 +3923,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); @@ -3955,8 +3980,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; } @@ -4222,10 +4248,8 @@ fail: return ret; } -/* XXX: put compressed sectors first, then all the cluster aligned - tables to avoid losing bytes in alignment */ static coroutine_fn int -qcow2_co_pwritev_compressed_part(BlockDriverState *bs, +qcow2_co_pwritev_compressed_task(BlockDriverState *bs, uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, size_t qiov_offset) { @@ -4235,32 +4259,11 @@ qcow2_co_pwritev_compressed_part(BlockDriverState *bs, uint8_t *buf, *out_buf; uint64_t cluster_offset; - if (has_data_file(bs)) { - return -ENOTSUP; - } - - if (bytes == 0) { - /* align end of file to a sector boundary to ease reading with - sector based I/Os */ - int64_t len = bdrv_getlength(bs->file->bs); - if (len < 0) { - return len; - } - return bdrv_co_truncate(bs->file, len, false, PREALLOC_MODE_OFF, NULL); - } - - if (offset_into_cluster(s, offset)) { - return -EINVAL; - } + assert(bytes == s->cluster_size || (bytes < s->cluster_size && + (offset + bytes == bs->total_sectors << BDRV_SECTOR_BITS))); buf = qemu_blockalign(bs, s->cluster_size); - if (bytes != s->cluster_size) { - if (bytes > s->cluster_size || - offset + bytes != bs->total_sectors << BDRV_SECTOR_BITS) - { - qemu_vfree(buf); - return -EINVAL; - } + if (bytes < s->cluster_size) { /* Zero-pad last write if image size is not cluster aligned */ memset(buf + bytes, 0, s->cluster_size - bytes); } @@ -4309,6 +4312,82 @@ fail: return ret; } +static coroutine_fn int qcow2_co_pwritev_compressed_task_entry(AioTask *task) +{ + Qcow2AioTask *t = container_of(task, Qcow2AioTask, task); + + assert(!t->cluster_type && !t->l2meta); + + return qcow2_co_pwritev_compressed_task(t->bs, t->offset, t->bytes, t->qiov, + t->qiov_offset); +} + +/* + * XXX: put compressed sectors first, then all the cluster aligned + * tables to avoid losing bytes in alignment + */ +static coroutine_fn int +qcow2_co_pwritev_compressed_part(BlockDriverState *bs, + uint64_t offset, uint64_t bytes, + QEMUIOVector *qiov, size_t qiov_offset) +{ + BDRVQcow2State *s = bs->opaque; + AioTaskPool *aio = NULL; + int ret = 0; + + if (has_data_file(bs)) { + return -ENOTSUP; + } + + if (bytes == 0) { + /* + * align end of file to a sector boundary to ease reading with + * sector based I/Os + */ + int64_t len = bdrv_getlength(bs->file->bs); + if (len < 0) { + return len; + } + return bdrv_co_truncate(bs->file, len, false, PREALLOC_MODE_OFF, NULL); + } + + if (offset_into_cluster(s, offset)) { + return -EINVAL; + } + + if (offset_into_cluster(s, bytes) && + (offset + bytes) != (bs->total_sectors << BDRV_SECTOR_BITS)) { + return -EINVAL; + } + + while (bytes && aio_task_pool_status(aio) == 0) { + uint64_t chunk_size = MIN(bytes, s->cluster_size); + + if (!aio && chunk_size != bytes) { + aio = aio_task_pool_new(QCOW2_MAX_WORKERS); + } + + ret = qcow2_add_task(bs, aio, qcow2_co_pwritev_compressed_task_entry, + 0, 0, offset, chunk_size, qiov, qiov_offset, NULL); + if (ret < 0) { + break; + } + qiov_offset += chunk_size; + offset += chunk_size; + bytes -= chunk_size; + } + + if (aio) { + aio_task_pool_wait_all(aio); + if (ret == 0) { + ret = aio_task_pool_status(aio); + } + g_free(aio); + } + + return ret; +} + static int coroutine_fn qcow2_co_preadv_compressed(BlockDriverState *bs, uint64_t file_cluster_offset, @@ -4562,60 +4641,6 @@ static coroutine_fn int qcow2_co_flush_to_os(BlockDriverState *bs) return ret; } -static ssize_t qcow2_measure_crypto_hdr_init_func(QCryptoBlock *block, - size_t headerlen, void *opaque, Error **errp) -{ - size_t *headerlenp = opaque; - - /* Stash away the payload size */ - *headerlenp = headerlen; - return 0; -} - -static ssize_t qcow2_measure_crypto_hdr_write_func(QCryptoBlock *block, - size_t offset, const uint8_t *buf, size_t buflen, - void *opaque, Error **errp) -{ - /* Discard the bytes, we're not actually writing to an image */ - return buflen; -} - -/* Determine the number of bytes for the LUKS payload */ -static bool qcow2_measure_luks_headerlen(QemuOpts *opts, size_t *len, - Error **errp) -{ - QDict *opts_qdict; - QDict *cryptoopts_qdict; - QCryptoBlockCreateOptions *cryptoopts; - QCryptoBlock *crypto; - - /* Extract "encrypt." options into a qdict */ - opts_qdict = qemu_opts_to_qdict(opts, NULL); - qdict_extract_subqdict(opts_qdict, &cryptoopts_qdict, "encrypt."); - qobject_unref(opts_qdict); - - /* Build QCryptoBlockCreateOptions object from qdict */ - qdict_put_str(cryptoopts_qdict, "format", "luks"); - cryptoopts = block_crypto_create_opts_init(cryptoopts_qdict, errp); - qobject_unref(cryptoopts_qdict); - if (!cryptoopts) { - return false; - } - - /* Fake LUKS creation in order to determine the payload size */ - crypto = qcrypto_block_create(cryptoopts, "encrypt.", - qcow2_measure_crypto_hdr_init_func, - qcow2_measure_crypto_hdr_write_func, - len, errp); - qapi_free_QCryptoBlockCreateOptions(cryptoopts); - if (!crypto) { - return false; - } - - qcrypto_block_free(crypto); - return true; -} - static BlockMeasureInfo *qcow2_measure(QemuOpts *opts, BlockDriverState *in_bs, Error **errp) { @@ -4666,9 +4691,27 @@ static BlockMeasureInfo *qcow2_measure(QemuOpts *opts, BlockDriverState *in_bs, g_free(optstr); if (has_luks) { + g_autoptr(QCryptoBlockCreateOptions) create_opts = NULL; + QDict *opts_qdict; + QDict *cryptoopts; size_t headerlen; - if (!qcow2_measure_luks_headerlen(opts, &headerlen, &local_err)) { + opts_qdict = qemu_opts_to_qdict(opts, NULL); + qdict_extract_subqdict(opts_qdict, &cryptoopts, "encrypt."); + qobject_unref(opts_qdict); + + qdict_put_str(cryptoopts, "format", "luks"); + + create_opts = block_crypto_create_opts_init(cryptoopts, errp); + qobject_unref(cryptoopts); + if (!create_opts) { + goto err; + } + + if (!qcrypto_block_calculate_payload_offset(create_opts, + "encrypt.", + &headerlen, + &local_err)) { goto err; } @@ -4800,6 +4843,7 @@ static ImageInfoSpecific *qcow2_get_specific_info(BlockDriverState *bs, if (local_err) { error_propagate(errp, local_err); qapi_free_ImageInfoSpecific(spec_info); + qapi_free_QCryptoBlockInfo(encrypt_info); return NULL; } *spec_info->u.qcow2.data = (ImageInfoSpecificQCow2){ @@ -5492,6 +5536,7 @@ BlockDriver bdrv_qcow2 = { .bdrv_close = qcow2_close, .bdrv_reopen_prepare = qcow2_reopen_prepare, .bdrv_reopen_commit = qcow2_reopen_commit, + .bdrv_reopen_commit_post = qcow2_reopen_commit_post, .bdrv_reopen_abort = qcow2_reopen_abort, .bdrv_join_options = qcow2_join_options, .bdrv_child_perm = bdrv_format_default_perms, diff --git a/block/qcow2.h b/block/qcow2.h index 0942126232..f4de0a27d5 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -301,9 +301,6 @@ typedef struct BDRVQcow2State { QEMUTimer *cache_clean_timer; unsigned cache_clean_interval; - uint8_t *cluster_cache; - uint8_t *cluster_data; - uint64_t cluster_cache_offset; QLIST_HEAD(, QCowL2Meta) cluster_allocs; uint64_t *refcount_table; diff --git a/block/qed.c b/block/qed.c index d8c4e5fb1e..1af9b3cb1d 100644 --- a/block/qed.c +++ b/block/qed.c @@ -720,7 +720,8 @@ out: return ret; } -static int coroutine_fn bdrv_qed_co_create_opts(const char *filename, +static int coroutine_fn bdrv_qed_co_create_opts(BlockDriver *drv, + const char *filename, QemuOpts *opts, Error **errp) { diff --git a/block/quorum.c b/block/quorum.c index df68adcfaa..6d7a56bd93 100644 --- a/block/quorum.c +++ b/block/quorum.c @@ -796,17 +796,53 @@ static coroutine_fn int quorum_co_flush(BlockDriverState *bs) return result; } -static bool quorum_recurse_is_first_non_filter(BlockDriverState *bs, - BlockDriverState *candidate) +static bool quorum_recurse_can_replace(BlockDriverState *bs, + BlockDriverState *to_replace) { BDRVQuorumState *s = bs->opaque; int i; for (i = 0; i < s->num_children; i++) { - bool perm = bdrv_recurse_is_first_non_filter(s->children[i]->bs, - candidate); - if (perm) { - return true; + /* + * We have no idea whether our children show the same data as + * this node (@bs). It is actually highly likely that + * @to_replace does not, because replacing a broken child is + * one of the main use cases here. + * + * We do know that the new BDS will match @bs, so replacing + * any of our children by it will be safe. It cannot change + * the data this quorum node presents to its parents. + * + * However, replacing @to_replace by @bs in any of our + * children's chains may change visible data somewhere in + * there. We therefore cannot recurse down those chains with + * bdrv_recurse_can_replace(). + * (More formally, bdrv_recurse_can_replace() requires that + * @to_replace will be replaced by something matching the @bs + * passed to it. We cannot guarantee that.) + * + * Thus, we can only check whether any of our immediate + * children matches @to_replace. + * + * (In the future, we might add a function to recurse down a + * chain that checks that nothing there cares about a change + * in data from the respective child in question. For + * example, most filters do not care when their child's data + * suddenly changes, as long as their parents do not care.) + */ + if (s->children[i]->bs == to_replace) { + /* + * We now have to ensure that there is no other parent + * that cares about replacing this child by a node with + * potentially different data. + * We do so by checking whether there are any other parents + * at all, which is stricter than necessary, but also very + * simple. (We may decide to implement something more + * complex and permissive when there is an actual need for + * it.) + */ + return QLIST_FIRST(&to_replace->parents) == s->children[i] && + QLIST_NEXT(s->children[i], next_parent) == NULL; } } @@ -1114,6 +1150,23 @@ static char *quorum_dirname(BlockDriverState *bs, Error **errp) return NULL; } +static void quorum_child_perm(BlockDriverState *bs, BdrvChild *c, + const BdrvChildRole *role, + BlockReopenQueue *reopen_queue, + uint64_t perm, uint64_t shared, + uint64_t *nperm, uint64_t *nshared) +{ + *nperm = perm & DEFAULT_PERM_PASSTHROUGH; + + /* + * We cannot share RESIZE or WRITE, as this would make the + * children differ from each other. + */ + *nshared = (shared & (BLK_PERM_CONSISTENT_READ | + BLK_PERM_WRITE_UNCHANGED)) + | DEFAULT_PERM_UNCHANGED; +} + static const char *const quorum_strong_runtime_opts[] = { QUORUM_OPT_VOTE_THRESHOLD, QUORUM_OPT_BLKVERIFY, @@ -1143,10 +1196,9 @@ static BlockDriver bdrv_quorum = { .bdrv_add_child = quorum_add_child, .bdrv_del_child = quorum_del_child, - .bdrv_child_perm = bdrv_filter_default_perms, + .bdrv_child_perm = quorum_child_perm, - .is_filter = true, - .bdrv_recurse_is_first_non_filter = quorum_recurse_is_first_non_filter, + .bdrv_recurse_can_replace = quorum_recurse_can_replace, .strong_runtime_opts = quorum_strong_runtime_opts, }; diff --git a/block/raw-format.c b/block/raw-format.c index 3a76ec7dd2..93b25e1b6b 100644 --- a/block/raw-format.c +++ b/block/raw-format.c @@ -419,7 +419,9 @@ static int raw_has_zero_init_truncate(BlockDriverState *bs) return bdrv_has_zero_init_truncate(bs->file->bs); } -static int coroutine_fn raw_co_create_opts(const char *filename, QemuOpts *opts, +static int coroutine_fn raw_co_create_opts(BlockDriver *drv, + const char *filename, + QemuOpts *opts, Error **errp) { return bdrv_create_file(filename, opts, errp); diff --git a/block/rbd.c b/block/rbd.c index 027cbcc695..e637639a07 100644 --- a/block/rbd.c +++ b/block/rbd.c @@ -104,6 +104,7 @@ typedef struct BDRVRBDState { rbd_image_t image; char *image_name; char *snap; + char *namespace; uint64_t image_size; } BDRVRBDState; @@ -152,7 +153,7 @@ static void qemu_rbd_parse_filename(const char *filename, QDict *options, const char *start; char *p, *buf; QList *keypairs = NULL; - char *found_str; + char *found_str, *image_name; if (!strstart(filename, "rbd:", &start)) { error_setg(errp, "File name must start with 'rbd:'"); @@ -171,18 +172,24 @@ static void qemu_rbd_parse_filename(const char *filename, QDict *options, qdict_put_str(options, "pool", found_str); if (strchr(p, '@')) { - found_str = qemu_rbd_next_tok(p, '@', &p); - qemu_rbd_unescape(found_str); - qdict_put_str(options, "image", found_str); + image_name = qemu_rbd_next_tok(p, '@', &p); found_str = qemu_rbd_next_tok(p, ':', &p); qemu_rbd_unescape(found_str); qdict_put_str(options, "snapshot", found_str); } else { - found_str = qemu_rbd_next_tok(p, ':', &p); - qemu_rbd_unescape(found_str); - qdict_put_str(options, "image", found_str); + image_name = qemu_rbd_next_tok(p, ':', &p); } + /* Check for namespace in the image_name */ + if (strchr(image_name, '/')) { + found_str = qemu_rbd_next_tok(image_name, '/', &image_name); + qemu_rbd_unescape(found_str); + qdict_put_str(options, "namespace", found_str); + } else { + qdict_put_str(options, "namespace", ""); + } + qemu_rbd_unescape(image_name); + qdict_put_str(options, "image", image_name); if (!p) { goto done; } @@ -343,6 +350,11 @@ static QemuOptsList runtime_opts = { .type = QEMU_OPT_STRING, .help = "Rados pool name", }, + { + .name = "namespace", + .type = QEMU_OPT_STRING, + .help = "Rados namespace name in the pool", + }, { .name = "image", .type = QEMU_OPT_STRING, @@ -425,7 +437,8 @@ static int qemu_rbd_co_create(BlockdevCreateOptions *options, Error **errp) return qemu_rbd_do_create(options, NULL, NULL, errp); } -static int coroutine_fn qemu_rbd_co_create_opts(const char *filename, +static int coroutine_fn qemu_rbd_co_create_opts(BlockDriver *drv, + const char *filename, QemuOpts *opts, Error **errp) { @@ -467,13 +480,14 @@ static int coroutine_fn qemu_rbd_co_create_opts(const char *filename, * schema, but when they come from -drive, they're all QString. */ loc = rbd_opts->location; - loc->pool = g_strdup(qdict_get_try_str(options, "pool")); - loc->conf = g_strdup(qdict_get_try_str(options, "conf")); - loc->has_conf = !!loc->conf; - loc->user = g_strdup(qdict_get_try_str(options, "user")); - loc->has_user = !!loc->user; - loc->image = g_strdup(qdict_get_try_str(options, "image")); - keypairs = qdict_get_try_str(options, "=keyvalue-pairs"); + loc->pool = g_strdup(qdict_get_try_str(options, "pool")); + loc->conf = g_strdup(qdict_get_try_str(options, "conf")); + loc->has_conf = !!loc->conf; + loc->user = g_strdup(qdict_get_try_str(options, "user")); + loc->has_user = !!loc->user; + loc->q_namespace = g_strdup(qdict_get_try_str(options, "namespace")); + loc->image = g_strdup(qdict_get_try_str(options, "image")); + keypairs = qdict_get_try_str(options, "=keyvalue-pairs"); ret = qemu_rbd_do_create(create_options, keypairs, password_secret, errp); if (ret < 0) { @@ -648,6 +662,11 @@ static int qemu_rbd_connect(rados_t *cluster, rados_ioctx_t *io_ctx, error_setg_errno(errp, -r, "error opening pool %s", opts->pool); goto failed_shutdown; } + /* + * Set the namespace after opening the io context on the pool, + * if nspace == NULL or if nspace == "", it is just as we did nothing + */ + rados_ioctx_set_namespace(*io_ctx, opts->q_namespace); return 0; diff --git a/block/replication.c b/block/replication.c index 99532ce521..da013c2041 100644 --- a/block/replication.c +++ b/block/replication.c @@ -144,12 +144,15 @@ fail: static void replication_close(BlockDriverState *bs) { BDRVReplicationState *s = bs->opaque; + Job *commit_job; if (s->stage == BLOCK_REPLICATION_RUNNING) { replication_stop(s->rs, false, NULL); } if (s->stage == BLOCK_REPLICATION_FAILOVER) { - job_cancel_sync(&s->commit_job->job); + commit_job = &s->commit_job->job; + assert(commit_job->aio_context == qemu_get_current_aio_context()); + job_cancel_sync(commit_job); } if (s->mode == REPLICATION_MODE_SECONDARY) { @@ -306,12 +309,6 @@ out: return ret; } -static bool replication_recurse_is_first_non_filter(BlockDriverState *bs, - BlockDriverState *candidate) -{ - return bdrv_recurse_is_first_non_filter(bs->file->bs, candidate); -} - static void secondary_do_checkpoint(BDRVReplicationState *s, Error **errp) { Error *local_err = NULL; @@ -456,6 +453,17 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode, aio_context_acquire(aio_context); s = bs->opaque; + if (s->stage == BLOCK_REPLICATION_DONE || + s->stage == BLOCK_REPLICATION_FAILOVER) { + /* + * This case happens when a secondary is promoted to primary. + * Ignore the request because the secondary side of replication + * doesn't have to do anything anymore. + */ + aio_context_release(aio_context); + return; + } + if (s->stage != BLOCK_REPLICATION_NONE) { error_setg(errp, "Block replication is running or done"); aio_context_release(aio_context); @@ -580,6 +588,17 @@ static void replication_do_checkpoint(ReplicationState *rs, Error **errp) aio_context_acquire(aio_context); s = bs->opaque; + if (s->stage == BLOCK_REPLICATION_DONE || + s->stage == BLOCK_REPLICATION_FAILOVER) { + /* + * This case happens when a secondary was promoted to primary. + * Ignore the request because the secondary side of replication + * doesn't have to do anything anymore. + */ + aio_context_release(aio_context); + return; + } + if (s->mode == REPLICATION_MODE_SECONDARY) { secondary_do_checkpoint(s, errp); } @@ -596,7 +615,7 @@ static void replication_get_error(ReplicationState *rs, Error **errp) aio_context_acquire(aio_context); s = bs->opaque; - if (s->stage != BLOCK_REPLICATION_RUNNING) { + if (s->stage == BLOCK_REPLICATION_NONE) { error_setg(errp, "Block replication is not running"); aio_context_release(aio_context); return; @@ -638,6 +657,17 @@ static void replication_stop(ReplicationState *rs, bool failover, Error **errp) aio_context_acquire(aio_context); s = bs->opaque; + if (s->stage == BLOCK_REPLICATION_DONE || + s->stage == BLOCK_REPLICATION_FAILOVER) { + /* + * This case happens when a secondary was promoted to primary. + * Ignore the request because the secondary side of replication + * doesn't have to do anything anymore. + */ + aio_context_release(aio_context); + return; + } + if (s->stage != BLOCK_REPLICATION_RUNNING) { error_setg(errp, "Block replication is not running"); aio_context_release(aio_context); @@ -699,7 +729,6 @@ static BlockDriver bdrv_replication = { .bdrv_co_writev = replication_co_writev, .is_filter = true, - .bdrv_recurse_is_first_non_filter = replication_recurse_is_first_non_filter, .has_variable_length = true, .strong_runtime_opts = replication_strong_runtime_opts, diff --git a/block/sheepdog.c b/block/sheepdog.c index cfa84338a2..59f7ebb171 100644 --- a/block/sheepdog.c +++ b/block/sheepdog.c @@ -2157,7 +2157,9 @@ out: return ret; } -static int coroutine_fn sd_co_create_opts(const char *filename, QemuOpts *opts, +static int coroutine_fn sd_co_create_opts(BlockDriver *drv, + const char *filename, + QemuOpts *opts, Error **errp) { BlockdevCreateOptions *create_options = NULL; @@ -3269,6 +3271,7 @@ static BlockDriver bdrv_sheepdog_tcp = { .bdrv_co_create = sd_co_create, .bdrv_co_create_opts = sd_co_create_opts, .bdrv_has_zero_init = bdrv_has_zero_init_1, + .bdrv_has_zero_init_truncate = bdrv_has_zero_init_1, .bdrv_getlength = sd_getlength, .bdrv_get_allocated_file_size = sd_get_allocated_file_size, .bdrv_co_truncate = sd_co_truncate, @@ -3307,6 +3310,7 @@ static BlockDriver bdrv_sheepdog_unix = { .bdrv_co_create = sd_co_create, .bdrv_co_create_opts = sd_co_create_opts, .bdrv_has_zero_init = bdrv_has_zero_init_1, + .bdrv_has_zero_init_truncate = bdrv_has_zero_init_1, .bdrv_getlength = sd_getlength, .bdrv_get_allocated_file_size = sd_get_allocated_file_size, .bdrv_co_truncate = sd_co_truncate, diff --git a/block/snapshot.c b/block/snapshot.c index 8081616ae9..bd9fb01817 100644 --- a/block/snapshot.c +++ b/block/snapshot.c @@ -426,7 +426,7 @@ fail: } int bdrv_all_delete_snapshot(const char *name, BlockDriverState **first_bad_bs, - Error **err) + Error **errp) { int ret = 0; BlockDriverState *bs; @@ -441,7 +441,7 @@ int bdrv_all_delete_snapshot(const char *name, BlockDriverState **first_bad_bs, bdrv_snapshot_find(bs, snapshot, name) >= 0) { ret = bdrv_snapshot_delete(bs, snapshot->id_str, - snapshot->name, err); + snapshot->name, errp); } aio_context_release(ctx); if (ret < 0) { diff --git a/block/ssh.c b/block/ssh.c index b4375cf7d2..84e92821c0 100644 --- a/block/ssh.c +++ b/block/ssh.c @@ -963,7 +963,9 @@ fail: return ret; } -static int coroutine_fn ssh_co_create_opts(const char *filename, QemuOpts *opts, +static int coroutine_fn ssh_co_create_opts(BlockDriver *drv, + const char *filename, + QemuOpts *opts, Error **errp) { BlockdevCreateOptions *create_options; diff --git a/block/stream.c b/block/stream.c index 5562ccbf57..aa2e7af98e 100644 --- a/block/stream.c +++ b/block/stream.c @@ -114,7 +114,6 @@ static int coroutine_fn stream_run(Job *job, Error **errp) int64_t offset = 0; uint64_t delay_ns = 0; int error = 0; - int ret = 0; int64_t n = 0; /* bytes */ if (bs == s->bottom) { @@ -139,6 +138,7 @@ static int coroutine_fn stream_run(Job *job, Error **errp) for ( ; offset < len; offset += n) { bool copy; + int ret; /* Note that even when no rate limit is applied we need to yield * with no pending I/O here so that bdrv_drain_all() returns. @@ -183,7 +183,6 @@ static int coroutine_fn stream_run(Job *job, Error **errp) break; } } - ret = 0; /* Publish progress */ job_progress_update(&s->common.job, n); diff --git a/block/throttle-groups.c b/block/throttle-groups.c index 77014c741b..37695b0cd7 100644 --- a/block/throttle-groups.c +++ b/block/throttle-groups.c @@ -893,8 +893,7 @@ static void throttle_group_set_limits(Object *obj, Visitor *v, { ThrottleGroup *tg = THROTTLE_GROUP(obj); ThrottleConfig cfg; - ThrottleLimits arg = { 0 }; - ThrottleLimits *argp = &arg; + ThrottleLimits *argp; Error *local_err = NULL; visit_type_ThrottleLimits(v, name, &argp, &local_err); @@ -912,6 +911,7 @@ static void throttle_group_set_limits(Object *obj, Visitor *v, unlock: qemu_mutex_unlock(&tg->lock); ret: + qapi_free_ThrottleLimits(argp); error_propagate(errp, local_err); return; } diff --git a/block/throttle.c b/block/throttle.c index 0349f42257..71f4bb0ad1 100644 --- a/block/throttle.c +++ b/block/throttle.c @@ -207,12 +207,6 @@ static void throttle_reopen_abort(BDRVReopenState *reopen_state) reopen_state->opaque = NULL; } -static bool throttle_recurse_is_first_non_filter(BlockDriverState *bs, - BlockDriverState *candidate) -{ - return bdrv_recurse_is_first_non_filter(bs->file->bs, candidate); -} - static void coroutine_fn throttle_co_drain_begin(BlockDriverState *bs) { ThrottleGroupMember *tgm = bs->opaque; @@ -252,8 +246,6 @@ static BlockDriver bdrv_throttle = { .bdrv_co_pwrite_zeroes = throttle_co_pwrite_zeroes, .bdrv_co_pdiscard = throttle_co_pdiscard, - .bdrv_recurse_is_first_non_filter = throttle_recurse_is_first_non_filter, - .bdrv_attach_aio_context = throttle_attach_aio_context, .bdrv_detach_aio_context = throttle_detach_aio_context, diff --git a/block/trace-events b/block/trace-events index 6ba86decca..29dff8881c 100644 --- a/block/trace-events +++ b/block/trace-events @@ -48,6 +48,7 @@ block_copy_process(void *bcs, int64_t start) "bcs %p start %"PRId64 block_copy_copy_range_fail(void *bcs, int64_t start, int ret) "bcs %p start %"PRId64" ret %d" block_copy_read_fail(void *bcs, int64_t start, int ret) "bcs %p start %"PRId64" ret %d" block_copy_write_fail(void *bcs, int64_t start, int ret) "bcs %p start %"PRId64" ret %d" +block_copy_write_zeroes_fail(void *bcs, int64_t start, int ret) "bcs %p start %"PRId64" ret %d" # ../blockdev.c qmp_block_job_cancel(void *job) "job %p" @@ -63,6 +64,18 @@ qmp_block_stream(void *bs) "bs %p" file_paio_submit(void *acb, void *opaque, int64_t offset, int count, int type) "acb %p opaque %p offset %"PRId64" count %d type %d" file_copy_file_range(void *bs, int src, int64_t src_off, int dst, int64_t dst_off, int64_t bytes, int flags, int64_t ret) "bs %p src_fd %d offset %"PRIu64" dst_fd %d offset %"PRIu64" bytes %"PRIu64" flags %d ret %"PRId64 +#io_uring.c +luring_init_state(void *s, size_t size) "s %p size %zu" +luring_cleanup_state(void *s) "%p freed" +luring_io_plug(void *s) "LuringState %p plug" +luring_io_unplug(void *s, int blocked, int plugged, int queued, int inflight) "LuringState %p blocked %d plugged %d queued %d inflight %d" +luring_do_submit(void *s, int blocked, int plugged, int queued, int inflight) "LuringState %p blocked %d plugged %d queued %d inflight %d" +luring_do_submit_done(void *s, int ret) "LuringState %p submitted to kernel %d" +luring_co_submit(void *bs, void *s, void *luringcb, int fd, uint64_t offset, size_t nbytes, int type) "bs %p s %p luringcb %p fd %d offset %" PRId64 " nbytes %zd type %d" +luring_process_completion(void *s, void *aiocb, int ret) "LuringState %p luringcb %p ret %d" +luring_io_uring_submit(void *s, int ret) "LuringState %p ret %d" +luring_resubmit_short_read(void *s, void *luringcb, int nread) "LuringState %p luringcb %p nread %d" + # qcow2.c qcow2_add_task(void *co, void *bs, void *pool, const char *action, int cluster_type, uint64_t file_cluster_offset, uint64_t offset, uint64_t bytes, void *qiov, size_t qiov_offset) "co %p bs %p pool %p: %s: cluster_type %d file_cluster_offset %" PRIu64 " offset %" PRIu64 " bytes %" PRIu64 " qiov %p qiov_offset %zu" qcow2_writev_start_req(void *co, int64_t offset, int bytes) "co %p offset 0x%" PRIx64 " bytes %d" diff --git a/block/vdi.c b/block/vdi.c index 0142da7233..e1a11f2aa0 100644 --- a/block/vdi.c +++ b/block/vdi.c @@ -896,7 +896,9 @@ static int coroutine_fn vdi_co_create(BlockdevCreateOptions *create_options, return vdi_co_do_create(create_options, DEFAULT_CLUSTER_SIZE, errp); } -static int coroutine_fn vdi_co_create_opts(const char *filename, QemuOpts *opts, +static int coroutine_fn vdi_co_create_opts(BlockDriver *drv, + const char *filename, + QemuOpts *opts, Error **errp) { QDict *qdict = NULL; diff --git a/block/vhdx.c b/block/vhdx.c index f02d2611be..33e57cd656 100644 --- a/block/vhdx.c +++ b/block/vhdx.c @@ -2046,7 +2046,8 @@ delete_and_exit: return ret; } -static int coroutine_fn vhdx_co_create_opts(const char *filename, +static int coroutine_fn vhdx_co_create_opts(BlockDriver *drv, + const char *filename, QemuOpts *opts, Error **errp) { diff --git a/block/vmdk.c b/block/vmdk.c index 20e909d997..218d9c9800 100644 --- a/block/vmdk.c +++ b/block/vmdk.c @@ -187,7 +187,7 @@ typedef struct VmdkMetaData { typedef struct VmdkGrainMarker { uint64_t lba; uint32_t size; - uint8_t data[0]; + uint8_t data[]; } QEMU_PACKED VmdkGrainMarker; enum { @@ -2588,7 +2588,9 @@ exit: return blk; } -static int coroutine_fn vmdk_co_create_opts(const char *filename, QemuOpts *opts, +static int coroutine_fn vmdk_co_create_opts(BlockDriver *drv, + const char *filename, + QemuOpts *opts, Error **errp) { Error *local_err = NULL; diff --git a/block/vpc.c b/block/vpc.c index a65550298e..d8141b52da 100644 --- a/block/vpc.c +++ b/block/vpc.c @@ -835,7 +835,7 @@ static int create_dynamic_disk(BlockBackend *blk, uint8_t *buf, /* Write the footer (twice: at the beginning and at the end) */ block_size = 0x200000; - num_bat_entries = (total_sectors + block_size / 512) / (block_size / 512); + num_bat_entries = DIV_ROUND_UP(total_sectors, block_size / 512); ret = blk_pwrite(blk, offset, buf, HEADER_SIZE, 0); if (ret < 0) { @@ -1089,8 +1089,10 @@ out: return ret; } -static int coroutine_fn vpc_co_create_opts(const char *filename, - QemuOpts *opts, Error **errp) +static int coroutine_fn vpc_co_create_opts(BlockDriver *drv, + const char *filename, + QemuOpts *opts, + Error **errp) { BlockdevCreateOptions *create_options = NULL; QDict *qdict; diff --git a/block/vvfat.c b/block/vvfat.c index 019b8f1341..ab800c4887 100644 --- a/block/vvfat.c +++ b/block/vvfat.c @@ -3124,17 +3124,10 @@ write_target_commit(BlockDriverState *bs, uint64_t offset, uint64_t bytes, return ret; } -static void write_target_close(BlockDriverState *bs) { - BDRVVVFATState* s = *((BDRVVVFATState**) bs->opaque); - bdrv_unref_child(s->bs, s->qcow); - g_free(s->qcow_filename); -} - static BlockDriver vvfat_write_target = { .format_name = "vvfat_write_target", .instance_size = sizeof(void*), .bdrv_co_pwritev = write_target_commit, - .bdrv_close = write_target_close, }; static void vvfat_qcow_options(int *child_flags, QDict *child_options, diff --git a/blockdev-nbd.c b/blockdev-nbd.c index 8c20baa4a4..1a95d89f00 100644 --- a/blockdev-nbd.c +++ b/blockdev-nbd.c @@ -132,6 +132,11 @@ void nbd_server_start(SocketAddress *addr, const char *tls_creds, nbd_server = NULL; } +void nbd_server_start_options(NbdServerOptions *arg, Error **errp) +{ + nbd_server_start(arg->addr, arg->tls_creds, arg->tls_authz, errp); +} + void qmp_nbd_server_start(SocketAddressLegacy *addr, bool has_tls_creds, const char *tls_creds, bool has_tls_authz, const char *tls_authz, @@ -143,9 +148,7 @@ void qmp_nbd_server_start(SocketAddressLegacy *addr, qapi_free_SocketAddress(addr_flat); } -void qmp_nbd_server_add(const char *device, bool has_name, const char *name, - bool has_writable, bool writable, - bool has_bitmap, const char *bitmap, Error **errp) +void qmp_nbd_server_add(BlockExportNbd *arg, Error **errp) { BlockDriverState *bs = NULL; BlockBackend *on_eject_blk; @@ -158,23 +161,28 @@ void qmp_nbd_server_add(const char *device, bool has_name, const char *name, return; } - if (!has_name) { - name = device; + if (!arg->has_name) { + arg->name = arg->device; } - if (strlen(name) > NBD_MAX_STRING_SIZE) { - error_setg(errp, "export name '%s' too long", name); + if (strlen(arg->name) > NBD_MAX_STRING_SIZE) { + error_setg(errp, "export name '%s' too long", arg->name); return; } - if (nbd_export_find(name)) { - error_setg(errp, "NBD server already has export named '%s'", name); + if (arg->description && strlen(arg->description) > NBD_MAX_STRING_SIZE) { + error_setg(errp, "description '%s' too long", arg->description); return; } - on_eject_blk = blk_by_name(device); + if (nbd_export_find(arg->name)) { + error_setg(errp, "NBD server already has export named '%s'", arg->name); + return; + } - bs = bdrv_lookup_bs(device, device, errp); + on_eject_blk = blk_by_name(arg->device); + + bs = bdrv_lookup_bs(arg->device, arg->device, errp); if (!bs) { return; } @@ -188,14 +196,15 @@ void qmp_nbd_server_add(const char *device, bool has_name, const char *name, goto out; } - if (!has_writable) { - writable = false; + if (!arg->has_writable) { + arg->writable = false; } if (bdrv_is_read_only(bs)) { - writable = false; + arg->writable = false; } - exp = nbd_export_new(bs, 0, len, name, NULL, bitmap, !writable, !writable, + exp = nbd_export_new(bs, 0, len, arg->name, arg->description, arg->bitmap, + !arg->writable, !arg->writable, NULL, false, on_eject_blk, errp); if (!exp) { goto out; diff --git a/blockdev.c b/blockdev.c index 48245c0560..0457213bac 100644 --- a/blockdev.c +++ b/blockdev.c @@ -64,16 +64,13 @@ #include "qemu/main-loop.h" #include "qemu/throttle-options.h" -static QTAILQ_HEAD(, BlockDriverState) monitor_bdrv_states = +QTAILQ_HEAD(, BlockDriverState) monitor_bdrv_states = QTAILQ_HEAD_INITIALIZER(monitor_bdrv_states); -static int do_open_tray(const char *blk_name, const char *qdev_id, - bool force, Error **errp); -static void blockdev_remove_medium(bool has_device, const char *device, - bool has_id, const char *id, Error **errp); -static void blockdev_insert_medium(bool has_device, const char *device, - bool has_id, const char *id, - const char *node_name, Error **errp); +void bdrv_set_monitor_owned(BlockDriverState *bs) +{ + QTAILQ_INSERT_TAIL(&monitor_bdrv_states, bs, monitor_list); +} static const char *const if_name[IF_COUNT] = { [IF_NONE] = "none", @@ -385,13 +382,9 @@ static void extract_common_blockdev_options(QemuOpts *opts, int *bdrv_flags, } if ((aio = qemu_opt_get(opts, "aio")) != NULL) { - if (!strcmp(aio, "native")) { - *bdrv_flags |= BDRV_O_NATIVE_AIO; - } else if (!strcmp(aio, "threads")) { - /* this is the default */ - } else { - error_setg(errp, "invalid aio option"); - return; + if (bdrv_parse_aio(aio, bdrv_flags) < 0) { + error_setg(errp, "invalid aio option"); + return; } } } @@ -652,7 +645,7 @@ err_no_opts: } /* Takes the ownership of bs_opts */ -static BlockDriverState *bds_tree_init(QDict *bs_opts, Error **errp) +BlockDriverState *bds_tree_init(QDict *bs_opts, Error **errp) { int bdrv_flags = 0; @@ -1060,64 +1053,6 @@ static BlockDriverState *qmp_get_root_bs(const char *name, Error **errp) return bs; } -static BlockBackend *qmp_get_blk(const char *blk_name, const char *qdev_id, - Error **errp) -{ - BlockBackend *blk; - - if (!blk_name == !qdev_id) { - error_setg(errp, "Need exactly one of 'device' and 'id'"); - return NULL; - } - - if (qdev_id) { - blk = blk_by_qdev_id(qdev_id, errp); - } else { - blk = blk_by_name(blk_name); - if (blk == NULL) { - error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, - "Device '%s' not found", blk_name); - } - } - - return blk; -} - -void hmp_commit(Monitor *mon, const QDict *qdict) -{ - const char *device = qdict_get_str(qdict, "device"); - BlockBackend *blk; - int ret; - - if (!strcmp(device, "all")) { - ret = blk_commit_all(); - } else { - BlockDriverState *bs; - AioContext *aio_context; - - blk = blk_by_name(device); - if (!blk) { - error_report("Device '%s' not found", device); - return; - } - if (!blk_is_available(blk)) { - error_report("Device '%s' has no medium", device); - return; - } - - bs = blk_bs(blk); - aio_context = bdrv_get_aio_context(bs); - aio_context_acquire(aio_context); - - ret = bdrv_commit(bs); - - aio_context_release(aio_context); - } - if (ret < 0) { - error_report("'commit' error for '%s': %s", device, strerror(-ret)); - } -} - static void blockdev_do_action(TransactionAction *action, Error **errp) { TransactionActionList list; @@ -1544,7 +1479,7 @@ static void external_snapshot_prepare(BlkActionState *common, DO_UPCAST(ExternalSnapshotState, common, common); TransactionAction *action = common->action; AioContext *aio_context; - int ret; + uint64_t perm, shared; /* 'blockdev-snapshot' and 'blockdev-snapshot-sync' have similar * purpose but a different set of parameters */ @@ -1604,11 +1539,6 @@ static void external_snapshot_prepare(BlkActionState *common, } } - if (!bdrv_is_first_non_filter(state->old_bs)) { - error_setg(errp, QERR_FEATURE_DISABLED, "snapshot"); - goto out; - } - if (action->type == TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC) { BlockdevSnapshotSync *s = action->u.blockdev_snapshot_sync.data; const char *format = s->has_format ? s->format : "qcow2"; @@ -1664,16 +1594,17 @@ static void external_snapshot_prepare(BlkActionState *common, goto out; } - if (bdrv_has_blk(state->new_bs)) { + /* + * Allow attaching a backing file to an overlay that's already in use only + * if the parents don't assume that they are already seeing a valid image. + * (Specifically, allow it as a mirror target, which is write-only access.) + */ + bdrv_get_cumulative_perm(state->new_bs, &perm, &shared); + if (perm & BLK_PERM_CONSISTENT_READ) { error_setg(errp, "The overlay is already in use"); goto out; } - if (bdrv_op_is_blocked(state->new_bs, BLOCK_OP_TYPE_EXTERNAL_SNAPSHOT, - errp)) { - goto out; - } - if (state->new_bs->backing != NULL) { error_setg(errp, "The overlay already has a backing image"); goto out; @@ -1684,11 +1615,6 @@ static void external_snapshot_prepare(BlkActionState *common, goto out; } - ret = bdrv_try_set_aio_context(state->new_bs, aio_context, errp); - if (ret < 0) { - goto out; - } - /* This removes our old bs and adds the new bs. This is an operation that * can fail, so we need to do it in .prepare; undoing it for abort is * always possible. */ @@ -1730,6 +1656,8 @@ static void external_snapshot_abort(BlkActionState *common) if (state->new_bs) { if (state->overlay_appended) { AioContext *aio_context; + AioContext *tmp_context; + int ret; aio_context = bdrv_get_aio_context(state->old_bs); aio_context_acquire(aio_context); @@ -1737,6 +1665,25 @@ static void external_snapshot_abort(BlkActionState *common) bdrv_ref(state->old_bs); /* we can't let bdrv_set_backind_hd() close state->old_bs; we need it */ bdrv_set_backing_hd(state->new_bs, NULL, &error_abort); + + /* + * The call to bdrv_set_backing_hd() above returns state->old_bs to + * the main AioContext. As we're still going to be using it, return + * it to the AioContext it was before. + */ + tmp_context = bdrv_get_aio_context(state->old_bs); + if (aio_context != tmp_context) { + aio_context_release(aio_context); + aio_context_acquire(tmp_context); + + ret = bdrv_try_set_aio_context(state->old_bs, + aio_context, NULL); + assert(ret == 0); + + aio_context_release(tmp_context); + aio_context_acquire(aio_context); + } + bdrv_replace_node(state->new_bs, state->old_bs, &error_abort); bdrv_unref(state->old_bs); /* bdrv_replace_node() ref'ed old_bs */ @@ -1770,39 +1717,145 @@ typedef struct DriveBackupState { BlockJob *job; } DriveBackupState; -static BlockJob *do_drive_backup(DriveBackup *backup, JobTxn *txn, - Error **errp); +static BlockJob *do_backup_common(BackupCommon *backup, + BlockDriverState *bs, + BlockDriverState *target_bs, + AioContext *aio_context, + JobTxn *txn, Error **errp); static void drive_backup_prepare(BlkActionState *common, Error **errp) { DriveBackupState *state = DO_UPCAST(DriveBackupState, common, common); - BlockDriverState *bs; DriveBackup *backup; + BlockDriverState *bs; + BlockDriverState *target_bs; + BlockDriverState *source = NULL; AioContext *aio_context; + AioContext *old_context; + QDict *options; Error *local_err = NULL; + int flags; + int64_t size; + bool set_backing_hd = false; + int ret; assert(common->action->type == TRANSACTION_ACTION_KIND_DRIVE_BACKUP); backup = common->action->u.drive_backup.data; + if (!backup->has_mode) { + backup->mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS; + } + bs = bdrv_lookup_bs(backup->device, backup->device, errp); if (!bs) { return; } + if (!bs->drv) { + error_setg(errp, "Device has no medium"); + return; + } + aio_context = bdrv_get_aio_context(bs); aio_context_acquire(aio_context); /* Paired with .clean() */ bdrv_drained_begin(bs); - state->bs = bs; + if (!backup->has_format) { + backup->format = backup->mode == NEW_IMAGE_MODE_EXISTING ? + NULL : (char *) bs->drv->format_name; + } + + /* Early check to avoid creating target */ + if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_BACKUP_SOURCE, errp)) { + goto out; + } + + flags = bs->open_flags | BDRV_O_RDWR; + + /* + * See if we have a backing HD we can use to create our new image + * on top of. + */ + if (backup->sync == MIRROR_SYNC_MODE_TOP) { + source = backing_bs(bs); + if (!source) { + backup->sync = MIRROR_SYNC_MODE_FULL; + } + } + if (backup->sync == MIRROR_SYNC_MODE_NONE) { + source = bs; + flags |= BDRV_O_NO_BACKING; + set_backing_hd = true; + } + + size = bdrv_getlength(bs); + if (size < 0) { + error_setg_errno(errp, -size, "bdrv_getlength failed"); + goto out; + } + + if (backup->mode != NEW_IMAGE_MODE_EXISTING) { + assert(backup->format); + if (source) { + bdrv_refresh_filename(source); + bdrv_img_create(backup->target, backup->format, source->filename, + source->drv->format_name, NULL, + size, flags, false, &local_err); + } else { + bdrv_img_create(backup->target, backup->format, NULL, NULL, NULL, + size, flags, false, &local_err); + } + } - state->job = do_drive_backup(backup, common->block_job_txn, &local_err); if (local_err) { error_propagate(errp, local_err); goto out; } + options = qdict_new(); + qdict_put_str(options, "discard", "unmap"); + qdict_put_str(options, "detect-zeroes", "unmap"); + if (backup->format) { + qdict_put_str(options, "driver", backup->format); + } + + target_bs = bdrv_open(backup->target, NULL, options, flags, errp); + if (!target_bs) { + goto out; + } + + /* Honor bdrv_try_set_aio_context() context acquisition requirements. */ + old_context = bdrv_get_aio_context(target_bs); + aio_context_release(aio_context); + aio_context_acquire(old_context); + + ret = bdrv_try_set_aio_context(target_bs, aio_context, errp); + if (ret < 0) { + bdrv_unref(target_bs); + aio_context_release(old_context); + return; + } + + aio_context_release(old_context); + aio_context_acquire(aio_context); + + if (set_backing_hd) { + bdrv_set_backing_hd(target_bs, source, &local_err); + if (local_err) { + goto unref; + } + } + + state->bs = bs; + + state->job = do_backup_common(qapi_DriveBackup_base(backup), + bs, target_bs, aio_context, + common->block_job_txn, errp); + +unref: + bdrv_unref(target_bs); out: aio_context_release(aio_context); } @@ -1860,16 +1913,15 @@ typedef struct BlockdevBackupState { BlockJob *job; } BlockdevBackupState; -static BlockJob *do_blockdev_backup(BlockdevBackup *backup, JobTxn *txn, - Error **errp); - static void blockdev_backup_prepare(BlkActionState *common, Error **errp) { BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common); BlockdevBackup *backup; - BlockDriverState *bs, *target; + BlockDriverState *bs; + BlockDriverState *target_bs; AioContext *aio_context; - Error *local_err = NULL; + AioContext *old_context; + int ret; assert(common->action->type == TRANSACTION_ACTION_KIND_BLOCKDEV_BACKUP); backup = common->action->u.blockdev_backup.data; @@ -1879,25 +1931,33 @@ static void blockdev_backup_prepare(BlkActionState *common, Error **errp) return; } - target = bdrv_lookup_bs(backup->target, backup->target, errp); - if (!target) { + target_bs = bdrv_lookup_bs(backup->target, backup->target, errp); + if (!target_bs) { return; } + /* Honor bdrv_try_set_aio_context() context acquisition requirements. */ aio_context = bdrv_get_aio_context(bs); + old_context = bdrv_get_aio_context(target_bs); + aio_context_acquire(old_context); + + ret = bdrv_try_set_aio_context(target_bs, aio_context, errp); + if (ret < 0) { + aio_context_release(old_context); + return; + } + + aio_context_release(old_context); aio_context_acquire(aio_context); state->bs = bs; /* Paired with .clean() */ bdrv_drained_begin(state->bs); - state->job = do_blockdev_backup(backup, common->block_job_txn, &local_err); - if (local_err) { - error_propagate(errp, local_err); - goto out; - } + state->job = do_backup_common(qapi_BlockdevBackup_base(backup), + bs, target_bs, aio_context, + common->block_job_txn, errp); -out: aio_context_release(aio_context); } @@ -2382,29 +2442,6 @@ exit: job_txn_unref(block_job_txn); } -void qmp_eject(bool has_device, const char *device, - bool has_id, const char *id, - bool has_force, bool force, Error **errp) -{ - Error *local_err = NULL; - int rc; - - if (!has_force) { - force = false; - } - - rc = do_open_tray(has_device ? device : NULL, - has_id ? id : NULL, - force, &local_err); - if (rc && rc != -ENOSYS) { - error_propagate(errp, local_err); - return; - } - error_free(local_err); - - blockdev_remove_medium(has_device, device, has_id, id, errp); -} - void qmp_block_passwd(bool has_device, const char *device, bool has_node_name, const char *node_name, const char *password, Error **errp) @@ -2413,455 +2450,6 @@ void qmp_block_passwd(bool has_device, const char *device, "Setting block passwords directly is no longer supported"); } -/* - * Attempt to open the tray of @device. - * If @force, ignore its tray lock. - * Else, if the tray is locked, don't open it, but ask the guest to open it. - * On error, store an error through @errp and return -errno. - * If @device does not exist, return -ENODEV. - * If it has no removable media, return -ENOTSUP. - * If it has no tray, return -ENOSYS. - * If the guest was asked to open the tray, return -EINPROGRESS. - * Else, return 0. - */ -static int do_open_tray(const char *blk_name, const char *qdev_id, - bool force, Error **errp) -{ - BlockBackend *blk; - const char *device = qdev_id ?: blk_name; - bool locked; - - blk = qmp_get_blk(blk_name, qdev_id, errp); - if (!blk) { - return -ENODEV; - } - - if (!blk_dev_has_removable_media(blk)) { - error_setg(errp, "Device '%s' is not removable", device); - return -ENOTSUP; - } - - if (!blk_dev_has_tray(blk)) { - error_setg(errp, "Device '%s' does not have a tray", device); - return -ENOSYS; - } - - if (blk_dev_is_tray_open(blk)) { - return 0; - } - - locked = blk_dev_is_medium_locked(blk); - if (locked) { - blk_dev_eject_request(blk, force); - } - - if (!locked || force) { - blk_dev_change_media_cb(blk, false, &error_abort); - } - - if (locked && !force) { - error_setg(errp, "Device '%s' is locked and force was not specified, " - "wait for tray to open and try again", device); - return -EINPROGRESS; - } - - return 0; -} - -void qmp_blockdev_open_tray(bool has_device, const char *device, - bool has_id, const char *id, - bool has_force, bool force, - Error **errp) -{ - Error *local_err = NULL; - int rc; - - if (!has_force) { - force = false; - } - rc = do_open_tray(has_device ? device : NULL, - has_id ? id : NULL, - force, &local_err); - if (rc && rc != -ENOSYS && rc != -EINPROGRESS) { - error_propagate(errp, local_err); - return; - } - error_free(local_err); -} - -void qmp_blockdev_close_tray(bool has_device, const char *device, - bool has_id, const char *id, - Error **errp) -{ - BlockBackend *blk; - Error *local_err = NULL; - - device = has_device ? device : NULL; - id = has_id ? id : NULL; - - blk = qmp_get_blk(device, id, errp); - if (!blk) { - return; - } - - if (!blk_dev_has_removable_media(blk)) { - error_setg(errp, "Device '%s' is not removable", device ?: id); - return; - } - - if (!blk_dev_has_tray(blk)) { - /* Ignore this command on tray-less devices */ - return; - } - - if (!blk_dev_is_tray_open(blk)) { - return; - } - - blk_dev_change_media_cb(blk, true, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } -} - -static void blockdev_remove_medium(bool has_device, const char *device, - bool has_id, const char *id, Error **errp) -{ - BlockBackend *blk; - BlockDriverState *bs; - AioContext *aio_context; - bool has_attached_device; - - device = has_device ? device : NULL; - id = has_id ? id : NULL; - - blk = qmp_get_blk(device, id, errp); - if (!blk) { - return; - } - - /* For BBs without a device, we can exchange the BDS tree at will */ - has_attached_device = blk_get_attached_dev(blk); - - if (has_attached_device && !blk_dev_has_removable_media(blk)) { - error_setg(errp, "Device '%s' is not removable", device ?: id); - return; - } - - if (has_attached_device && blk_dev_has_tray(blk) && - !blk_dev_is_tray_open(blk)) - { - error_setg(errp, "Tray of device '%s' is not open", device ?: id); - return; - } - - bs = blk_bs(blk); - if (!bs) { - return; - } - - aio_context = bdrv_get_aio_context(bs); - aio_context_acquire(aio_context); - - if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_EJECT, errp)) { - goto out; - } - - blk_remove_bs(blk); - - if (!blk_dev_has_tray(blk)) { - /* For tray-less devices, blockdev-open-tray is a no-op (or may not be - * called at all); therefore, the medium needs to be ejected here. - * Do it after blk_remove_bs() so blk_is_inserted(blk) returns the @load - * value passed here (i.e. false). */ - blk_dev_change_media_cb(blk, false, &error_abort); - } - -out: - aio_context_release(aio_context); -} - -void qmp_blockdev_remove_medium(const char *id, Error **errp) -{ - blockdev_remove_medium(false, NULL, true, id, errp); -} - -static void qmp_blockdev_insert_anon_medium(BlockBackend *blk, - BlockDriverState *bs, Error **errp) -{ - Error *local_err = NULL; - bool has_device; - int ret; - - /* For BBs without a device, we can exchange the BDS tree at will */ - has_device = blk_get_attached_dev(blk); - - if (has_device && !blk_dev_has_removable_media(blk)) { - error_setg(errp, "Device is not removable"); - return; - } - - if (has_device && blk_dev_has_tray(blk) && !blk_dev_is_tray_open(blk)) { - error_setg(errp, "Tray of the device is not open"); - return; - } - - if (blk_bs(blk)) { - error_setg(errp, "There already is a medium in the device"); - return; - } - - ret = blk_insert_bs(blk, bs, errp); - if (ret < 0) { - return; - } - - if (!blk_dev_has_tray(blk)) { - /* For tray-less devices, blockdev-close-tray is a no-op (or may not be - * called at all); therefore, the medium needs to be pushed into the - * slot here. - * Do it after blk_insert_bs() so blk_is_inserted(blk) returns the @load - * value passed here (i.e. true). */ - blk_dev_change_media_cb(blk, true, &local_err); - if (local_err) { - error_propagate(errp, local_err); - blk_remove_bs(blk); - return; - } - } -} - -static void blockdev_insert_medium(bool has_device, const char *device, - bool has_id, const char *id, - const char *node_name, Error **errp) -{ - BlockBackend *blk; - BlockDriverState *bs; - - blk = qmp_get_blk(has_device ? device : NULL, - has_id ? id : NULL, - errp); - if (!blk) { - return; - } - - bs = bdrv_find_node(node_name); - if (!bs) { - error_setg(errp, "Node '%s' not found", node_name); - return; - } - - if (bdrv_has_blk(bs)) { - error_setg(errp, "Node '%s' is already in use", node_name); - return; - } - - qmp_blockdev_insert_anon_medium(blk, bs, errp); -} - -void qmp_blockdev_insert_medium(const char *id, const char *node_name, - Error **errp) -{ - blockdev_insert_medium(false, NULL, true, id, node_name, errp); -} - -void qmp_blockdev_change_medium(bool has_device, const char *device, - bool has_id, const char *id, - const char *filename, - bool has_format, const char *format, - bool has_read_only, - BlockdevChangeReadOnlyMode read_only, - Error **errp) -{ - BlockBackend *blk; - BlockDriverState *medium_bs = NULL; - int bdrv_flags; - bool detect_zeroes; - int rc; - QDict *options = NULL; - Error *err = NULL; - - blk = qmp_get_blk(has_device ? device : NULL, - has_id ? id : NULL, - errp); - if (!blk) { - goto fail; - } - - if (blk_bs(blk)) { - blk_update_root_state(blk); - } - - bdrv_flags = blk_get_open_flags_from_root_state(blk); - bdrv_flags &= ~(BDRV_O_TEMPORARY | BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING | - BDRV_O_PROTOCOL | BDRV_O_AUTO_RDONLY); - - if (!has_read_only) { - read_only = BLOCKDEV_CHANGE_READ_ONLY_MODE_RETAIN; - } - - switch (read_only) { - case BLOCKDEV_CHANGE_READ_ONLY_MODE_RETAIN: - break; - - case BLOCKDEV_CHANGE_READ_ONLY_MODE_READ_ONLY: - bdrv_flags &= ~BDRV_O_RDWR; - break; - - case BLOCKDEV_CHANGE_READ_ONLY_MODE_READ_WRITE: - bdrv_flags |= BDRV_O_RDWR; - break; - - default: - abort(); - } - - options = qdict_new(); - detect_zeroes = blk_get_detect_zeroes_from_root_state(blk); - qdict_put_str(options, "detect-zeroes", detect_zeroes ? "on" : "off"); - - if (has_format) { - qdict_put_str(options, "driver", format); - } - - medium_bs = bdrv_open(filename, NULL, options, bdrv_flags, errp); - if (!medium_bs) { - goto fail; - } - - rc = do_open_tray(has_device ? device : NULL, - has_id ? id : NULL, - false, &err); - if (rc && rc != -ENOSYS) { - error_propagate(errp, err); - goto fail; - } - error_free(err); - err = NULL; - - blockdev_remove_medium(has_device, device, has_id, id, &err); - if (err) { - error_propagate(errp, err); - goto fail; - } - - qmp_blockdev_insert_anon_medium(blk, medium_bs, &err); - if (err) { - error_propagate(errp, err); - goto fail; - } - - qmp_blockdev_close_tray(has_device, device, has_id, id, errp); - -fail: - /* If the medium has been inserted, the device has its own reference, so - * ours must be relinquished; and if it has not been inserted successfully, - * the reference must be relinquished anyway */ - bdrv_unref(medium_bs); -} - -/* throttling disk I/O limits */ -void qmp_block_set_io_throttle(BlockIOThrottle *arg, Error **errp) -{ - ThrottleConfig cfg; - BlockDriverState *bs; - BlockBackend *blk; - AioContext *aio_context; - - blk = qmp_get_blk(arg->has_device ? arg->device : NULL, - arg->has_id ? arg->id : NULL, - errp); - if (!blk) { - return; - } - - aio_context = blk_get_aio_context(blk); - aio_context_acquire(aio_context); - - bs = blk_bs(blk); - if (!bs) { - error_setg(errp, "Device has no medium"); - goto out; - } - - throttle_config_init(&cfg); - cfg.buckets[THROTTLE_BPS_TOTAL].avg = arg->bps; - cfg.buckets[THROTTLE_BPS_READ].avg = arg->bps_rd; - cfg.buckets[THROTTLE_BPS_WRITE].avg = arg->bps_wr; - - cfg.buckets[THROTTLE_OPS_TOTAL].avg = arg->iops; - cfg.buckets[THROTTLE_OPS_READ].avg = arg->iops_rd; - cfg.buckets[THROTTLE_OPS_WRITE].avg = arg->iops_wr; - - if (arg->has_bps_max) { - cfg.buckets[THROTTLE_BPS_TOTAL].max = arg->bps_max; - } - if (arg->has_bps_rd_max) { - cfg.buckets[THROTTLE_BPS_READ].max = arg->bps_rd_max; - } - if (arg->has_bps_wr_max) { - cfg.buckets[THROTTLE_BPS_WRITE].max = arg->bps_wr_max; - } - if (arg->has_iops_max) { - cfg.buckets[THROTTLE_OPS_TOTAL].max = arg->iops_max; - } - if (arg->has_iops_rd_max) { - cfg.buckets[THROTTLE_OPS_READ].max = arg->iops_rd_max; - } - if (arg->has_iops_wr_max) { - cfg.buckets[THROTTLE_OPS_WRITE].max = arg->iops_wr_max; - } - - if (arg->has_bps_max_length) { - cfg.buckets[THROTTLE_BPS_TOTAL].burst_length = arg->bps_max_length; - } - if (arg->has_bps_rd_max_length) { - cfg.buckets[THROTTLE_BPS_READ].burst_length = arg->bps_rd_max_length; - } - if (arg->has_bps_wr_max_length) { - cfg.buckets[THROTTLE_BPS_WRITE].burst_length = arg->bps_wr_max_length; - } - if (arg->has_iops_max_length) { - cfg.buckets[THROTTLE_OPS_TOTAL].burst_length = arg->iops_max_length; - } - if (arg->has_iops_rd_max_length) { - cfg.buckets[THROTTLE_OPS_READ].burst_length = arg->iops_rd_max_length; - } - if (arg->has_iops_wr_max_length) { - cfg.buckets[THROTTLE_OPS_WRITE].burst_length = arg->iops_wr_max_length; - } - - if (arg->has_iops_size) { - cfg.op_size = arg->iops_size; - } - - if (!throttle_is_valid(&cfg, errp)) { - goto out; - } - - if (throttle_enabled(&cfg)) { - /* Enable I/O limits if they're not enabled yet, otherwise - * just update the throttling group. */ - if (!blk_get_public(blk)->throttle_group_member.throttle_state) { - blk_io_limits_enable(blk, - arg->has_group ? arg->group : - arg->has_device ? arg->device : - arg->id); - } else if (arg->has_group) { - blk_io_limits_update_group(blk, arg->group); - } - /* Set the new throttling configuration */ - blk_set_io_limits(blk, &cfg); - } else if (blk_get_public(blk)->throttle_group_member.throttle_state) { - /* If all throttling settings are set to 0, disable I/O limits */ - blk_io_limits_disable(blk); - } - -out: - aio_context_release(aio_context); -} - void qmp_block_dirty_bitmap_add(const char *node, const char *name, bool has_granularity, uint32_t granularity, bool has_persistent, bool persistent, @@ -2870,6 +2458,7 @@ void qmp_block_dirty_bitmap_add(const char *node, const char *name, { BlockDriverState *bs; BdrvDirtyBitmap *bitmap; + AioContext *aio_context; if (!name || name[0] == '\0') { error_setg(errp, "Bitmap name cannot be empty"); @@ -2881,11 +2470,14 @@ void qmp_block_dirty_bitmap_add(const char *node, const char *name, return; } + aio_context = bdrv_get_aio_context(bs); + aio_context_acquire(aio_context); + if (has_granularity) { if (granularity < 512 || !is_power_of_2(granularity)) { error_setg(errp, "Granularity must be power of 2 " "and at least 512"); - return; + goto out; } } else { /* Default to cluster size, if available: */ @@ -2903,12 +2495,12 @@ void qmp_block_dirty_bitmap_add(const char *node, const char *name, if (persistent && !bdrv_can_store_new_dirty_bitmap(bs, name, granularity, errp)) { - return; + goto out; } bitmap = bdrv_create_dirty_bitmap(bs, granularity, name, errp); if (bitmap == NULL) { - return; + goto out; } if (disabled) { @@ -2916,6 +2508,9 @@ void qmp_block_dirty_bitmap_add(const char *node, const char *name, } bdrv_dirty_bitmap_set_persistence(bitmap, persistent); + +out: + aio_context_release(aio_context); } static BdrvDirtyBitmap *do_block_dirty_bitmap_remove( @@ -2924,21 +2519,27 @@ static BdrvDirtyBitmap *do_block_dirty_bitmap_remove( { BlockDriverState *bs; BdrvDirtyBitmap *bitmap; + AioContext *aio_context; bitmap = block_dirty_bitmap_lookup(node, name, &bs, errp); if (!bitmap || !bs) { return NULL; } + aio_context = bdrv_get_aio_context(bs); + aio_context_acquire(aio_context); + if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_BUSY | BDRV_BITMAP_RO, errp)) { + aio_context_release(aio_context); return NULL; } if (bdrv_dirty_bitmap_get_persistence(bitmap) && bdrv_remove_persistent_dirty_bitmap(bs, name, errp) < 0) { - return NULL; + aio_context_release(aio_context); + return NULL; } if (release) { @@ -2949,6 +2550,7 @@ static BdrvDirtyBitmap *do_block_dirty_bitmap_remove( *bitmap_bs = bs; } + aio_context_release(aio_context); return release ? NULL : bitmap; } @@ -3110,66 +2712,6 @@ BlockDirtyBitmapSha256 *qmp_x_debug_block_dirty_bitmap_sha256(const char *node, return ret; } -void hmp_drive_del(Monitor *mon, const QDict *qdict) -{ - const char *id = qdict_get_str(qdict, "id"); - BlockBackend *blk; - BlockDriverState *bs; - AioContext *aio_context; - Error *local_err = NULL; - - bs = bdrv_find_node(id); - if (bs) { - qmp_blockdev_del(id, &local_err); - if (local_err) { - error_report_err(local_err); - } - return; - } - - blk = blk_by_name(id); - if (!blk) { - error_report("Device '%s' not found", id); - return; - } - - if (!blk_legacy_dinfo(blk)) { - error_report("Deleting device added with blockdev-add" - " is not supported"); - return; - } - - aio_context = blk_get_aio_context(blk); - aio_context_acquire(aio_context); - - bs = blk_bs(blk); - if (bs) { - if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_DRIVE_DEL, &local_err)) { - error_report_err(local_err); - aio_context_release(aio_context); - return; - } - - blk_remove_bs(blk); - } - - /* Make the BlockBackend and the attached BlockDriverState anonymous */ - monitor_remove_blk(blk); - - /* If this BlockBackend has a device attached to it, its refcount will be - * decremented when the device is removed; otherwise we have to do so here. - */ - if (blk_get_attached_dev(blk)) { - /* Further I/O must not pause the guest */ - blk_set_on_error(blk, BLOCKDEV_ON_ERROR_REPORT, - BLOCKDEV_ON_ERROR_REPORT); - } else { - blk_unref(blk); - } - - aio_context_release(aio_context); -} - void qmp_block_resize(bool has_device, const char *device, bool has_node_name, const char *node_name, int64_t size, Error **errp) @@ -3191,11 +2733,6 @@ void qmp_block_resize(bool has_device, const char *device, aio_context = bdrv_get_aio_context(bs); aio_context_acquire(aio_context); - if (!bdrv_is_first_non_filter(bs)) { - error_setg(errp, QERR_FEATURE_DISABLED, "resize"); - goto out; - } - if (size < 0) { error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "size", "a >0 size"); goto out; @@ -3326,6 +2863,7 @@ void qmp_block_commit(bool has_job_id, const char *job_id, const char *device, bool has_top, const char *top, bool has_backing_file, const char *backing_file, bool has_speed, int64_t speed, + bool has_on_error, BlockdevOnError on_error, bool has_filter_node_name, const char *filter_node_name, bool has_auto_finalize, bool auto_finalize, bool has_auto_dismiss, bool auto_dismiss, @@ -3336,15 +2874,14 @@ void qmp_block_commit(bool has_job_id, const char *job_id, const char *device, BlockDriverState *base_bs, *top_bs; AioContext *aio_context; Error *local_err = NULL; - /* This will be part of the QMP command, if/when the - * BlockdevOnError change for blkmirror makes it in - */ - BlockdevOnError on_error = BLOCKDEV_ON_ERROR_REPORT; int job_flags = JOB_DEFAULT; if (!has_speed) { speed = 0; } + if (!has_on_error) { + on_error = BLOCKDEV_ON_ERROR_REPORT; + } if (!has_filter_node_name) { filter_node_name = NULL; } @@ -3488,7 +3025,6 @@ static BlockJob *do_backup_common(BackupCommon *backup, BlockJob *job = NULL; BdrvDirtyBitmap *bmap = NULL; int job_flags = JOB_DEFAULT; - int ret; if (!backup->has_speed) { backup->speed = 0; @@ -3512,11 +3048,6 @@ static BlockJob *do_backup_common(BackupCommon *backup, backup->compress = false; } - ret = bdrv_try_set_aio_context(target_bs, aio_context, errp); - if (ret < 0) { - return NULL; - } - if ((backup->sync == MIRROR_SYNC_MODE_BITMAP) || (backup->sync == MIRROR_SYNC_MODE_INCREMENTAL)) { /* done before desugaring 'incremental' to print the right message */ @@ -3596,129 +3127,22 @@ static BlockJob *do_backup_common(BackupCommon *backup, return job; } -static BlockJob *do_drive_backup(DriveBackup *backup, JobTxn *txn, - Error **errp) +void qmp_drive_backup(DriveBackup *backup, Error **errp) { - BlockDriverState *bs; - BlockDriverState *target_bs; - BlockDriverState *source = NULL; - BlockJob *job = NULL; - AioContext *aio_context; - QDict *options; - Error *local_err = NULL; - int flags; - int64_t size; - bool set_backing_hd = false; - - if (!backup->has_mode) { - backup->mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS; - } - - bs = bdrv_lookup_bs(backup->device, backup->device, errp); - if (!bs) { - return NULL; - } - - if (!bs->drv) { - error_setg(errp, "Device has no medium"); - return NULL; - } - - aio_context = bdrv_get_aio_context(bs); - aio_context_acquire(aio_context); - - if (!backup->has_format) { - backup->format = backup->mode == NEW_IMAGE_MODE_EXISTING ? - NULL : (char*) bs->drv->format_name; - } - - /* Early check to avoid creating target */ - if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_BACKUP_SOURCE, errp)) { - goto out; - } - - flags = bs->open_flags | BDRV_O_RDWR; - - /* See if we have a backing HD we can use to create our new image - * on top of. */ - if (backup->sync == MIRROR_SYNC_MODE_TOP) { - source = backing_bs(bs); - if (!source) { - backup->sync = MIRROR_SYNC_MODE_FULL; - } - } - if (backup->sync == MIRROR_SYNC_MODE_NONE) { - source = bs; - flags |= BDRV_O_NO_BACKING; - set_backing_hd = true; - } - - size = bdrv_getlength(bs); - if (size < 0) { - error_setg_errno(errp, -size, "bdrv_getlength failed"); - goto out; - } - - if (backup->mode != NEW_IMAGE_MODE_EXISTING) { - assert(backup->format); - if (source) { - bdrv_refresh_filename(source); - bdrv_img_create(backup->target, backup->format, source->filename, - source->drv->format_name, NULL, - size, flags, false, &local_err); - } else { - bdrv_img_create(backup->target, backup->format, NULL, NULL, NULL, - size, flags, false, &local_err); - } - } - - if (local_err) { - error_propagate(errp, local_err); - goto out; - } - - options = qdict_new(); - qdict_put_str(options, "discard", "unmap"); - qdict_put_str(options, "detect-zeroes", "unmap"); - if (backup->format) { - qdict_put_str(options, "driver", backup->format); - } - - target_bs = bdrv_open(backup->target, NULL, options, flags, errp); - if (!target_bs) { - goto out; - } - - if (set_backing_hd) { - bdrv_set_backing_hd(target_bs, source, &local_err); - if (local_err) { - goto unref; - } - } - - job = do_backup_common(qapi_DriveBackup_base(backup), - bs, target_bs, aio_context, txn, errp); - -unref: - bdrv_unref(target_bs); -out: - aio_context_release(aio_context); - return job; + TransactionAction action = { + .type = TRANSACTION_ACTION_KIND_DRIVE_BACKUP, + .u.drive_backup.data = backup, + }; + blockdev_do_action(&action, errp); } -void qmp_drive_backup(DriveBackup *arg, Error **errp) +BlockDeviceInfoList *qmp_query_named_block_nodes(bool has_flat, + bool flat, + Error **errp) { + bool return_flat = has_flat && flat; - BlockJob *job; - job = do_drive_backup(arg, NULL, errp); - if (job) { - job_start(&job->job); - } -} - -BlockDeviceInfoList *qmp_query_named_block_nodes(Error **errp) -{ - return bdrv_named_nodes_list(errp); + return bdrv_named_nodes_list(return_flat, errp); } XDbgBlockGraph *qmp_x_debug_query_block_graph(Error **errp) @@ -3726,41 +3150,13 @@ XDbgBlockGraph *qmp_x_debug_query_block_graph(Error **errp) return bdrv_get_xdbg_block_graph(errp); } -BlockJob *do_blockdev_backup(BlockdevBackup *backup, JobTxn *txn, - Error **errp) +void qmp_blockdev_backup(BlockdevBackup *backup, Error **errp) { - BlockDriverState *bs; - BlockDriverState *target_bs; - AioContext *aio_context; - BlockJob *job; - - bs = bdrv_lookup_bs(backup->device, backup->device, errp); - if (!bs) { - return NULL; - } - - target_bs = bdrv_lookup_bs(backup->target, backup->target, errp); - if (!target_bs) { - return NULL; - } - - aio_context = bdrv_get_aio_context(bs); - aio_context_acquire(aio_context); - - job = do_backup_common(qapi_BlockdevBackup_base(backup), - bs, target_bs, aio_context, txn, errp); - - aio_context_release(aio_context); - return job; -} - -void qmp_blockdev_backup(BlockdevBackup *arg, Error **errp) -{ - BlockJob *job; - job = do_blockdev_backup(arg, NULL, errp); - if (job) { - job_start(&job->job); - } + TransactionAction action = { + .type = TRANSACTION_ACTION_KIND_BLOCKDEV_BACKUP, + .u.blockdev_backup.data = backup, + }; + blockdev_do_action(&action, errp); } /* Parameter check and block job starting for drive mirroring. @@ -3890,6 +3286,7 @@ void qmp_drive_mirror(DriveMirror *arg, Error **errp) BlockDriverState *bs; BlockDriverState *source, *target_bs; AioContext *aio_context; + AioContext *old_context; BlockMirrorBackingMode backing_mode; Error *local_err = NULL; QDict *options = NULL; @@ -4002,12 +3399,22 @@ void qmp_drive_mirror(DriveMirror *arg, Error **errp) (arg->mode == NEW_IMAGE_MODE_EXISTING || !bdrv_has_zero_init(target_bs))); + + /* Honor bdrv_try_set_aio_context() context acquisition requirements. */ + old_context = bdrv_get_aio_context(target_bs); + aio_context_release(aio_context); + aio_context_acquire(old_context); + ret = bdrv_try_set_aio_context(target_bs, aio_context, errp); if (ret < 0) { bdrv_unref(target_bs); - goto out; + aio_context_release(old_context); + return; } + aio_context_release(old_context); + aio_context_acquire(aio_context); + blockdev_mirror_common(arg->has_job_id ? arg->job_id : NULL, bs, target_bs, arg->has_replaces, arg->replaces, arg->sync, backing_mode, zero_target, @@ -4049,6 +3456,7 @@ void qmp_blockdev_mirror(bool has_job_id, const char *job_id, BlockDriverState *bs; BlockDriverState *target_bs; AioContext *aio_context; + AioContext *old_context; BlockMirrorBackingMode backing_mode = MIRROR_LEAVE_BACKING_CHAIN; Error *local_err = NULL; bool zero_target; @@ -4066,10 +3474,16 @@ void qmp_blockdev_mirror(bool has_job_id, const char *job_id, zero_target = (sync == MIRROR_SYNC_MODE_FULL); + /* Honor bdrv_try_set_aio_context() context acquisition requirements. */ + old_context = bdrv_get_aio_context(target_bs); aio_context = bdrv_get_aio_context(bs); - aio_context_acquire(aio_context); + aio_context_acquire(old_context); ret = bdrv_try_set_aio_context(target_bs, aio_context, errp); + + aio_context_release(old_context); + aio_context_acquire(aio_context); + if (ret < 0) { goto out; } @@ -4207,7 +3621,16 @@ void qmp_block_job_finalize(const char *id, Error **errp) } trace_qmp_block_job_finalize(job); + job_ref(&job->job); job_finalize(&job->job, errp); + + /* + * Job's context might have changed via job_finalize (and job_txn_apply + * automatically acquires the new one), so make sure we release the correct + * one. + */ + aio_context = blk_get_aio_context(job->blk); + job_unref(&job->job); aio_context_release(aio_context); } @@ -4305,37 +3728,6 @@ out: aio_context_release(aio_context); } -void hmp_drive_add_node(Monitor *mon, const char *optstr) -{ - QemuOpts *opts; - QDict *qdict; - Error *local_err = NULL; - - opts = qemu_opts_parse_noisily(&qemu_drive_opts, optstr, false); - if (!opts) { - return; - } - - qdict = qemu_opts_to_qdict(opts, NULL); - - if (!qdict_get_try_str(qdict, "node-name")) { - qobject_unref(qdict); - error_report("'node-name' needs to be specified"); - goto out; - } - - BlockDriverState *bs = bds_tree_init(qdict, &local_err); - if (!bs) { - error_report_err(local_err); - goto out; - } - - QTAILQ_INSERT_TAIL(&monitor_bdrv_states, bs, monitor_list); - -out: - qemu_opts_del(opts); -} - void qmp_blockdev_add(BlockdevOptions *options, Error **errp) { BlockDriverState *bs; @@ -4365,7 +3757,7 @@ void qmp_blockdev_add(BlockdevOptions *options, Error **errp) goto fail; } - QTAILQ_INSERT_TAIL(&monitor_bdrv_states, bs, monitor_list); + bdrv_set_monitor_owned(bs); fail: visit_free(v); @@ -4583,62 +3975,6 @@ void qmp_x_blockdev_set_iothread(const char *node_name, StrOrNull *iothread, aio_context_release(old_context); } -void qmp_block_latency_histogram_set( - const char *id, - bool has_boundaries, uint64List *boundaries, - bool has_boundaries_read, uint64List *boundaries_read, - bool has_boundaries_write, uint64List *boundaries_write, - bool has_boundaries_flush, uint64List *boundaries_flush, - Error **errp) -{ - BlockBackend *blk = qmp_get_blk(NULL, id, errp); - BlockAcctStats *stats; - int ret; - - if (!blk) { - return; - } - - stats = blk_get_stats(blk); - - if (!has_boundaries && !has_boundaries_read && !has_boundaries_write && - !has_boundaries_flush) - { - block_latency_histograms_clear(stats); - return; - } - - if (has_boundaries || has_boundaries_read) { - ret = block_latency_histogram_set( - stats, BLOCK_ACCT_READ, - has_boundaries_read ? boundaries_read : boundaries); - if (ret) { - error_setg(errp, "Device '%s' set read boundaries fail", id); - return; - } - } - - if (has_boundaries || has_boundaries_write) { - ret = block_latency_histogram_set( - stats, BLOCK_ACCT_WRITE, - has_boundaries_write ? boundaries_write : boundaries); - if (ret) { - error_setg(errp, "Device '%s' set write boundaries fail", id); - return; - } - } - - if (has_boundaries || has_boundaries_flush) { - ret = block_latency_histogram_set( - stats, BLOCK_ACCT_FLUSH, - has_boundaries_flush ? boundaries_flush : boundaries); - if (ret) { - error_setg(errp, "Device '%s' set flush boundaries fail", id); - return; - } - } -} - QemuOptsList qemu_common_drive_opts = { .name = "drive", .head = QTAILQ_HEAD_INITIALIZER(qemu_common_drive_opts.head), @@ -4650,7 +3986,7 @@ QemuOptsList qemu_common_drive_opts = { },{ .name = "aio", .type = QEMU_OPT_STRING, - .help = "host AIO implementation (threads, native)", + .help = "host AIO implementation (threads, native, io_uring)", },{ .name = BDRV_OPT_CACHE_WB, .type = QEMU_OPT_BOOL, diff --git a/blockjob.c b/blockjob.c index c6e20e2fcd..fc850312c1 100644 --- a/blockjob.c +++ b/blockjob.c @@ -261,7 +261,8 @@ void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp) return; } if (speed < 0) { - error_setg(errp, QERR_INVALID_PARAMETER, "speed"); + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "speed", + "a non-negative value"); return; } @@ -298,8 +299,8 @@ BlockJobInfo *block_job_query(BlockJob *job, Error **errp) info->device = g_strdup(job->job.id); info->busy = atomic_read(&job->job.busy); info->paused = job->job.pause_count > 0; - info->offset = job->job.progress_current; - info->len = job->job.progress_total; + info->offset = job->job.progress.current; + info->len = job->job.progress.total; info->speed = job->speed; info->io_status = job->iostatus; info->ready = job_is_ready(&job->job), @@ -329,8 +330,8 @@ static void block_job_event_cancelled(Notifier *n, void *opaque) qapi_event_send_block_job_cancelled(job_type(&job->job), job->job.id, - job->job.progress_total, - job->job.progress_current, + job->job.progress.total, + job->job.progress.current, job->speed); } @@ -349,8 +350,8 @@ static void block_job_event_completed(Notifier *n, void *opaque) qapi_event_send_block_job_completed(job_type(&job->job), job->job.id, - job->job.progress_total, - job->job.progress_current, + job->job.progress.total, + job->job.progress.current, job->speed, !!msg, msg); @@ -378,8 +379,8 @@ static void block_job_event_ready(Notifier *n, void *opaque) qapi_event_send_block_job_ready(job_type(&job->job), job->job.id, - job->job.progress_total, - job->job.progress_current, + job->job.progress.total, + job->job.progress.current, job->speed); } diff --git a/bsd-user/main.c b/bsd-user/main.c index 470a8bf79e..770c2b267a 100644 --- a/bsd-user/main.c +++ b/bsd-user/main.c @@ -33,7 +33,7 @@ #include "qemu/module.h" #include "cpu.h" #include "exec/exec-all.h" -#include "tcg.h" +#include "tcg/tcg.h" #include "qemu/timer.h" #include "qemu/envlist.h" #include "exec/log.h" @@ -963,7 +963,7 @@ int main(int argc, char **argv) if (qemu_loglevel_mask(CPU_LOG_PAGE)) { qemu_log("guest_base 0x%lx\n", guest_base); - log_page_dump(); + log_page_dump("binary load"); qemu_log("start_brk 0x" TARGET_ABI_FMT_lx "\n", info->start_brk); qemu_log("end_code 0x" TARGET_ABI_FMT_lx "\n", info->end_code); diff --git a/bsd-user/qemu.h b/bsd-user/qemu.h index 09e8aed9c7..f8bb1e5459 100644 --- a/bsd-user/qemu.h +++ b/bsd-user/qemu.h @@ -95,7 +95,7 @@ typedef struct TaskState { struct sigqueue *first_free; /* first free siginfo queue entry */ int signal_pending; /* non zero if a signal may be pending */ - uint8_t stack[0]; + uint8_t stack[]; } __attribute__((aligned(16))) TaskState; void init_task_state(TaskState *ts); diff --git a/bsd-user/syscall.c b/bsd-user/syscall.c index 0d45b654bb..d38ec7a162 100644 --- a/bsd-user/syscall.c +++ b/bsd-user/syscall.c @@ -330,7 +330,7 @@ abi_long do_freebsd_syscall(void *cpu_env, int num, abi_long arg1, switch(num) { case TARGET_FREEBSD_NR_exit: -#ifdef TARGET_GPROF +#ifdef CONFIG_GPROF _mcleanup(); #endif gdb_exit(cpu_env, arg1); @@ -432,7 +432,7 @@ abi_long do_netbsd_syscall(void *cpu_env, int num, abi_long arg1, switch(num) { case TARGET_NETBSD_NR_exit: -#ifdef TARGET_GPROF +#ifdef CONFIG_GPROF _mcleanup(); #endif gdb_exit(cpu_env, arg1); @@ -511,7 +511,7 @@ abi_long do_openbsd_syscall(void *cpu_env, int num, abi_long arg1, switch(num) { case TARGET_OPENBSD_NR_exit: -#ifdef TARGET_GPROF +#ifdef CONFIG_GPROF _mcleanup(); #endif gdb_exit(cpu_env, arg1); diff --git a/bt-host.c b/bt-host.c deleted file mode 100644 index 2f8f631c25..0000000000 --- a/bt-host.c +++ /dev/null @@ -1,198 +0,0 @@ -/* - * Wrap a host Bluetooth HCI socket in a struct HCIInfo. - * - * Copyright (C) 2008 Andrzej Zaborowski - * - * 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 or - * (at your option) version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#include "qemu/osdep.h" -#include "qemu-common.h" -#include "sysemu/bt.h" -#include "qemu/main-loop.h" - -#ifndef _WIN32 -# include -# include -# ifdef CONFIG_BLUEZ -# include -# include -# include -# else -# include "hw/bt.h" -# define HCI_MAX_FRAME_SIZE 1028 -# endif - -struct bt_host_hci_s { - struct HCIInfo hci; - int fd; - - uint8_t hdr[HCI_MAX_FRAME_SIZE]; - int len; -}; - -static void bt_host_send(struct HCIInfo *hci, - int type, const uint8_t *data, int len) -{ - struct bt_host_hci_s *s = (struct bt_host_hci_s *) hci; - uint8_t pkt = type; - struct iovec iv[2]; - - iv[0].iov_base = (void *)&pkt; - iv[0].iov_len = 1; - iv[1].iov_base = (void *) data; - iv[1].iov_len = len; - - while (writev(s->fd, iv, 2) < 0) { - if (errno != EAGAIN && errno != EINTR) { - fprintf(stderr, "qemu: error %i writing bluetooth packet.\n", - errno); - return; - } - } -} - -static void bt_host_cmd(struct HCIInfo *hci, const uint8_t *data, int len) -{ - bt_host_send(hci, HCI_COMMAND_PKT, data, len); -} - -static void bt_host_acl(struct HCIInfo *hci, const uint8_t *data, int len) -{ - bt_host_send(hci, HCI_ACLDATA_PKT, data, len); -} - -static void bt_host_sco(struct HCIInfo *hci, const uint8_t *data, int len) -{ - bt_host_send(hci, HCI_SCODATA_PKT, data, len); -} - -static void bt_host_read(void *opaque) -{ - struct bt_host_hci_s *s = (struct bt_host_hci_s *) opaque; - uint8_t *pkt; - int pktlen; - - /* Seems that we can't read only the header first and then the amount - * of data indicated in the header because Linux will discard everything - * that's not been read in one go. */ - s->len = read(s->fd, s->hdr, sizeof(s->hdr)); - - if (s->len < 0) { - fprintf(stderr, "qemu: error %i reading HCI frame\n", errno); - return; - } - - pkt = s->hdr; - while (s->len --) - switch (*pkt ++) { - case HCI_EVENT_PKT: - if (s->len < 2) - goto bad_pkt; - - pktlen = MIN(pkt[1] + 2, s->len); - s->hci.evt_recv(s->hci.opaque, pkt, pktlen); - s->len -= pktlen; - pkt += pktlen; - - /* TODO: if this is an Inquiry Result event, it's also - * interpreted by Linux kernel before we received it, possibly - * we should clean the kernel Inquiry cache through - * ioctl(s->fd, HCI_INQUIRY, ...). */ - break; - - case HCI_ACLDATA_PKT: - if (s->len < 4) - goto bad_pkt; - - pktlen = MIN(((pkt[3] << 8) | pkt[2]) + 4, s->len); - s->hci.acl_recv(s->hci.opaque, pkt, pktlen); - s->len -= pktlen; - pkt += pktlen; - break; - - case HCI_SCODATA_PKT: - if (s->len < 3) - goto bad_pkt; - - pktlen = MIN(pkt[2] + 3, s->len); - s->len -= pktlen; - pkt += pktlen; - break; - - default: - bad_pkt: - fprintf(stderr, "qemu: bad HCI packet type %02x\n", pkt[-1]); - } -} - -static int bt_host_bdaddr_set(struct HCIInfo *hci, const uint8_t *bd_addr) -{ - return -ENOTSUP; -} - -struct HCIInfo *bt_host_hci(const char *id) -{ - struct bt_host_hci_s *s; - int fd = -1; -# ifdef CONFIG_BLUEZ - int dev_id = hci_devid(id); - struct hci_filter flt; - - if (dev_id < 0) { - fprintf(stderr, "qemu: `%s' not available\n", id); - return 0; - } - - fd = hci_open_dev(dev_id); - - /* XXX: can we ensure nobody else has the device opened? */ -# endif - - if (fd < 0) { - fprintf(stderr, "qemu: Can't open `%s': %s (%i)\n", - id, strerror(errno), errno); - return NULL; - } - -# ifdef CONFIG_BLUEZ - hci_filter_clear(&flt); - hci_filter_all_ptypes(&flt); - hci_filter_all_events(&flt); - - if (qemu_setsockopt(fd, SOL_HCI, HCI_FILTER, &flt, sizeof(flt)) < 0) { - fprintf(stderr, "qemu: Can't set HCI filter on socket (%i)\n", errno); - return 0; - } -# endif - - s = g_malloc0(sizeof(struct bt_host_hci_s)); - s->fd = fd; - s->hci.cmd_send = bt_host_cmd; - s->hci.sco_send = bt_host_sco; - s->hci.acl_send = bt_host_acl; - s->hci.bdaddr_set = bt_host_bdaddr_set; - - qemu_set_fd_handler(s->fd, bt_host_read, NULL, s); - - return &s->hci; -} -#else -struct HCIInfo *bt_host_hci(const char *id) -{ - fprintf(stderr, "qemu: bluetooth passthrough not supported (yet)\n"); - - return 0; -} -#endif diff --git a/bt-vhci.c b/bt-vhci.c deleted file mode 100644 index 886e146743..0000000000 --- a/bt-vhci.c +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Support for host VHCIs inside qemu scatternets. - * - * Copyright (C) 2008 Andrzej Zaborowski - * - * 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 or - * (at your option) version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#include "qemu/osdep.h" -#include "sysemu/bt.h" -#include "hw/bt.h" -#include "qemu/main-loop.h" - -#define VHCI_DEV "/dev/vhci" -#define VHCI_UDEV "/dev/hci_vhci" - -struct bt_vhci_s { - int fd; - struct HCIInfo *info; - - uint8_t hdr[4096]; - int len; -}; - -static void vhci_read(void *opaque) -{ - struct bt_vhci_s *s = (struct bt_vhci_s *) opaque; - uint8_t *pkt; - int pktlen; - - /* Seems that we can't read only the header first and then the amount - * of data indicated in the header because Linux will discard everything - * that's not been read in one go. */ - s->len = read(s->fd, s->hdr, sizeof(s->hdr)); - - if (s->len < 0) { - fprintf(stderr, "qemu: error %i reading the PDU\n", errno); - return; - } - - pkt = s->hdr; - while (s->len --) - switch (*pkt ++) { - case HCI_COMMAND_PKT: - if (s->len < 3) - goto bad_pkt; - - pktlen = MIN(pkt[2] + 3, s->len); - s->info->cmd_send(s->info, pkt, pktlen); - s->len -= pktlen; - pkt += pktlen; - break; - - case HCI_ACLDATA_PKT: - if (s->len < 4) - goto bad_pkt; - - pktlen = MIN(((pkt[3] << 8) | pkt[2]) + 4, s->len); - s->info->acl_send(s->info, pkt, pktlen); - s->len -= pktlen; - pkt += pktlen; - break; - - case HCI_SCODATA_PKT: - if (s->len < 3) - goto bad_pkt; - - pktlen = MIN(pkt[2] + 3, s->len); - s->info->sco_send(s->info, pkt, pktlen); - s->len -= pktlen; - pkt += pktlen; - break; - - default: - bad_pkt: - fprintf(stderr, "qemu: bad HCI packet type %02x\n", pkt[-1]); - } -} - -static void vhci_host_send(void *opaque, - int type, const uint8_t *data, int len) -{ - struct bt_vhci_s *s = (struct bt_vhci_s *) opaque; -#if 0 - uint8_t pkt = type; - struct iovec iv[2]; - - iv[0].iov_base = &pkt; - iv[0].iov_len = 1; - iv[1].iov_base = (void *) data; - iv[1].iov_len = len; - - while (writev(s->fd, iv, 2) < 0) - if (errno != EAGAIN && errno != EINTR) { - fprintf(stderr, "qemu: error %i writing bluetooth packet.\n", - errno); - return; - } -#else - /* Apparently VHCI wants us to write everything in one chunk :-( */ - static uint8_t buf[4096]; - - buf[0] = type; - memcpy(buf + 1, data, len); - - while (write(s->fd, buf, len + 1) < 0) - if (errno != EAGAIN && errno != EINTR) { - fprintf(stderr, "qemu: error %i writing bluetooth packet.\n", - errno); - return; - } -#endif -} - -static void vhci_out_hci_packet_event(void *opaque, - const uint8_t *data, int len) -{ - vhci_host_send(opaque, HCI_EVENT_PKT, data, len); -} - -static void vhci_out_hci_packet_acl(void *opaque, - const uint8_t *data, int len) -{ - vhci_host_send(opaque, HCI_ACLDATA_PKT, data, len); -} - -void bt_vhci_init(struct HCIInfo *info) -{ - struct bt_vhci_s *s; - int err[2]; - int fd; - - fd = open(VHCI_DEV, O_RDWR); - err[0] = errno; - if (fd < 0) { - fd = open(VHCI_UDEV, O_RDWR); - err[1] = errno; - } - - if (fd < 0) { - fprintf(stderr, "qemu: Can't open `%s': %s (%i)\n", - VHCI_DEV, strerror(err[0]), err[0]); - fprintf(stderr, "qemu: Can't open `%s': %s (%i)\n", - VHCI_UDEV, strerror(err[1]), err[1]); - exit(-1); - } - - s = g_malloc0(sizeof(struct bt_vhci_s)); - s->fd = fd; - s->info = info ?: qemu_next_hci(); - s->info->opaque = s; - s->info->evt_recv = vhci_out_hci_packet_event; - s->info->acl_recv = vhci_out_hci_packet_acl; - - qemu_set_fd_handler(s->fd, vhci_read, NULL, s); -} diff --git a/chardev/char-mux.c b/chardev/char-mux.c index 200c62a0d0..46c44af67c 100644 --- a/chardev/char-mux.c +++ b/chardev/char-mux.c @@ -117,7 +117,7 @@ static void mux_print_help(Chardev *chr) } } -static void mux_chr_send_event(MuxChardev *d, int mux_nr, int event) +static void mux_chr_send_event(MuxChardev *d, int mux_nr, QEMUChrEvent event) { CharBackend *be = d->backends[mux_nr]; @@ -126,7 +126,7 @@ static void mux_chr_send_event(MuxChardev *d, int mux_nr, int event) } } -static void mux_chr_be_event(Chardev *chr, int event) +static void mux_chr_be_event(Chardev *chr, QEMUChrEvent event) { MuxChardev *d = MUX_CHARDEV(chr); @@ -232,7 +232,7 @@ static void mux_chr_read(void *opaque, const uint8_t *buf, int size) } } -void mux_chr_send_all_event(Chardev *chr, int event) +void mux_chr_send_all_event(Chardev *chr, QEMUChrEvent event) { MuxChardev *d = MUX_CHARDEV(chr); int i; @@ -247,7 +247,7 @@ void mux_chr_send_all_event(Chardev *chr, int event) } } -static void mux_chr_event(void *opaque, int event) +static void mux_chr_event(void *opaque, QEMUChrEvent event) { mux_chr_send_all_event(CHARDEV(opaque), event); } diff --git a/chardev/char-pipe.c b/chardev/char-pipe.c index 94d714ffcd..fd12c9e63b 100644 --- a/chardev/char-pipe.c +++ b/chardev/char-pipe.c @@ -70,7 +70,7 @@ static int win_chr_pipe_init(Chardev *chr, const char *filename, MAXCONNECT, NSENDBUF, NRECVBUF, NTIMEOUT, NULL); g_free(openname); if (s->file == INVALID_HANDLE_VALUE) { - error_setg(errp, "Failed CreateNamedPipe (%lu)", GetLastError()); + error_setg_win32(errp, GetLastError(), "Failed CreateNamedPipe"); s->file = NULL; goto fail; } diff --git a/chardev/char-win.c b/chardev/char-win.c index 34825f683d..d4fb44c4dc 100644 --- a/chardev/char-win.c +++ b/chardev/char-win.c @@ -96,7 +96,7 @@ int win_chr_serial_init(Chardev *chr, const char *filename, Error **errp) s->file = CreateFile(filename, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0); if (s->file == INVALID_HANDLE_VALUE) { - error_setg(errp, "Failed CreateFile (%lu)", GetLastError()); + error_setg_win32(errp, GetLastError(), "Failed CreateFile"); s->file = NULL; goto fail; } diff --git a/chardev/char.c b/chardev/char.c index 7b6b2cb123..e77564060d 100644 --- a/chardev/char.c +++ b/chardev/char.c @@ -37,6 +37,7 @@ #include "qemu/help_option.h" #include "qemu/module.h" #include "qemu/option.h" +#include "qemu/id.h" #include "chardev/char-mux.h" @@ -48,7 +49,7 @@ static Object *get_chardevs_root(void) return container_get(object_get_root(), "/chardevs"); } -static void chr_be_event(Chardev *s, int event) +static void chr_be_event(Chardev *s, QEMUChrEvent event) { CharBackend *be = s->be; @@ -59,7 +60,7 @@ static void chr_be_event(Chardev *s, int event) be->chr_event(be->opaque, event); } -void qemu_chr_be_event(Chardev *s, int event) +void qemu_chr_be_event(Chardev *s, QEMUChrEvent event) { /* Keep track if the char device is open */ switch (event) { @@ -69,6 +70,11 @@ void qemu_chr_be_event(Chardev *s, int event) case CHR_EVENT_CLOSED: s->be_open = 0; break; + case CHR_EVENT_BREAK: + case CHR_EVENT_MUX_IN: + case CHR_EVENT_MUX_OUT: + /* Ignore */ + break; } CHARDEV_GET_CLASS(s)->chr_be_event(s, event); @@ -731,7 +737,13 @@ Chardev *qemu_chr_new_noreplay(const char *label, const char *filename, if (qemu_opt_get_bool(opts, "mux", 0)) { assert(permit_mux_mon); - monitor_init_hmp(chr, true); + monitor_init_hmp(chr, true, &err); + if (err) { + error_report_err(err); + object_unparent(OBJECT(chr)); + chr = NULL; + goto out; + } } out: @@ -944,10 +956,10 @@ void qemu_chr_set_feature(Chardev *chr, return set_bit(feature, chr->features); } -Chardev *qemu_chardev_new(const char *id, const char *typename, - ChardevBackend *backend, - GMainContext *gcontext, - Error **errp) +static Chardev *chardev_new(const char *id, const char *typename, + ChardevBackend *backend, + GMainContext *gcontext, + Error **errp) { Object *obj; Chardev *chr = NULL; @@ -991,6 +1003,21 @@ end: return chr; } +Chardev *qemu_chardev_new(const char *id, const char *typename, + ChardevBackend *backend, + GMainContext *gcontext, + Error **errp) +{ + g_autofree char *genid = NULL; + + if (!id) { + genid = id_generate(ID_CHR); + id = genid; + } + + return chardev_new(id, typename, backend, gcontext, errp); +} + ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend, Error **errp) { @@ -1003,8 +1030,8 @@ ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend, return NULL; } - chr = qemu_chardev_new(id, object_class_get_name(OBJECT_CLASS(cc)), - backend, NULL, errp); + chr = chardev_new(id, object_class_get_name(OBJECT_CLASS(cc)), + backend, NULL, errp); if (!chr) { return NULL; } @@ -1061,8 +1088,8 @@ ChardevReturn *qmp_chardev_change(const char *id, ChardevBackend *backend, return NULL; } - chr_new = qemu_chardev_new(NULL, object_class_get_name(OBJECT_CLASS(cc)), - backend, chr->gcontext, errp); + chr_new = chardev_new(NULL, object_class_get_name(OBJECT_CLASS(cc)), + backend, chr->gcontext, errp); if (!chr_new) { return NULL; } diff --git a/chardev/spice.c b/chardev/spice.c index 241e2b7770..bf7ea1e294 100644 --- a/chardev/spice.c +++ b/chardev/spice.c @@ -216,9 +216,7 @@ static void char_spice_finalize(Object *obj) vmc_unregister_interface(s); - if (s->next.le_prev) { - QLIST_REMOVE(s, next); - } + QLIST_SAFE_REMOVE(s, next); g_free((char *)s->sin.subtype); g_free((char *)s->sin.portname); diff --git a/configure b/configure index 87149c826d..97ddde16a5 100755 --- a/configure +++ b/configure @@ -101,7 +101,7 @@ update_cxxflags() { # Set QEMU_CXXFLAGS from QEMU_CFLAGS by filtering out those # options which some versions of GCC's C++ compiler complain about # because they only make sense for C programs. - QEMU_CXXFLAGS="$QEMU_CXXFLAGS -D__STDC_LIMIT_MACROS" + QEMU_CXXFLAGS="$QEMU_CXXFLAGS -D__STDC_LIMIT_MACROS -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS" for arg in $QEMU_CFLAGS; do case $arg in @@ -126,7 +126,7 @@ compile_object() { compile_prog() { local_cflags="$1" local_ldflags="$2" - do_cc $QEMU_CFLAGS $local_cflags -o $TMPE $TMPC $LDFLAGS $local_ldflags + do_cc $QEMU_CFLAGS $local_cflags -o $TMPE $TMPC $QEMU_LDFLAGS $local_ldflags } # symbolically link $1 to $2. Portable version of "ln -sf". @@ -302,6 +302,8 @@ audio_win_int="" libs_qga="" debug_info="yes" stack_protector="" +use_containers="yes" +gdb_bin=$(command -v "gdb") if test -e "$source_path/.git" then @@ -349,7 +351,6 @@ unset target_list_exclude # Distributions want to ensure that several features are compiled in, and it # is impossible without a --enable-foo that exits if a feature is not found. -bluez="" brlapi="" curl="" curses="" @@ -371,6 +372,7 @@ xen="" xen_ctrl_version="" xen_pci_passthrough="" linux_aio="" +linux_io_uring="" cap_ng="" attr="" libattr="" @@ -404,6 +406,7 @@ EXESUF="" DSOSUF=".so" LDFLAGS_SHARED="-shared" modules="no" +module_upgrades="no" prefix="/usr/local" mandir="\${prefix}/share/man" datadir="\${prefix}/share" @@ -448,13 +451,14 @@ lzo="" snappy="" bzip2="" lzfse="" +zstd="" guest_agent="" guest_agent_with_vss="no" guest_agent_ntddscsi="no" guest_agent_msi="" vss_win32_sdk="" win_sdk="no" -want_tools="yes" +want_tools="" libiscsi="" libnfs="" coroutine="" @@ -504,6 +508,7 @@ debug_mutex="no" libpmem="" default_devices="yes" plugins="no" +fuzzing="no" supported_cpu="no" supported_os="no" @@ -523,10 +528,11 @@ for opt do --cpu=*) cpu="$optarg" ;; --extra-cflags=*) QEMU_CFLAGS="$QEMU_CFLAGS $optarg" + QEMU_LDFLAGS="$QEMU_LDFLAGS $optarg" ;; --extra-cxxflags=*) QEMU_CXXFLAGS="$QEMU_CXXFLAGS $optarg" ;; - --extra-ldflags=*) LDFLAGS="$LDFLAGS $optarg" + --extra-ldflags=*) QEMU_LDFLAGS="$QEMU_LDFLAGS $optarg" EXTRA_LDFLAGS="$optarg" ;; --enable-debug-info) debug_info="yes" @@ -596,9 +602,9 @@ QEMU_CFLAGS="-Wall -Wundef -Wwrite-strings -Wmissing-prototypes $QEMU_CFLAGS" QEMU_CFLAGS="-Wstrict-prototypes -Wredundant-decls $QEMU_CFLAGS" QEMU_CFLAGS="-D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE $QEMU_CFLAGS" QEMU_INCLUDES="-iquote . -iquote \$(SRC_PATH) -iquote \$(SRC_PATH)/accel/tcg -iquote \$(SRC_PATH)/include" +QEMU_INCLUDES="$QEMU_INCLUDES -iquote \$(SRC_PATH)/disas/libvixl" if test "$debug_info" = "yes"; then CFLAGS="-g $CFLAGS" - LDFLAGS="-g $LDFLAGS" fi # running configure in the source tree? @@ -633,6 +639,15 @@ int main(void) { return 0; } EOF } +write_c_fuzzer_skeleton() { + cat > $TMPC < +#include +int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size); +int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { return 0; } +EOF +} + if check_define __linux__ ; then targetos="Linux" elif check_define _WIN32 ; then @@ -792,6 +807,7 @@ MINGW32*) audio_drv_list="" fi supported_os="yes" + pie="no" ;; GNU/kFreeBSD) bsd="yes" @@ -844,12 +860,12 @@ Darwin) LDFLAGS_SHARED="-bundle -undefined dynamic_lookup" if [ "$cpu" = "x86_64" ] ; then QEMU_CFLAGS="-arch x86_64 $QEMU_CFLAGS" - LDFLAGS="-arch x86_64 $LDFLAGS" + QEMU_LDFLAGS="-arch x86_64 $QEMU_LDFLAGS" fi cocoa="yes" audio_drv_list="coreaudio try-sdl" audio_possible_drivers="coreaudio sdl" - LDFLAGS="-framework CoreFoundation -framework IOKit $LDFLAGS" + QEMU_LDFLAGS="-framework CoreFoundation -framework IOKit $QEMU_LDFLAGS" libs_softmmu="-F/System/Library/Frameworks -framework Cocoa -framework IOKit $libs_softmmu" # Disable attempts to use ObjectiveC features in os/object.h since they # won't work when we're compiling with gcc as a C compiler. @@ -886,7 +902,7 @@ Linux) linux="yes" linux_user="yes" kvm="yes" - QEMU_INCLUDES="-I\$(SRC_PATH)/linux-headers -I$PWD/linux-headers $QEMU_INCLUDES" + QEMU_INCLUDES="-isystem \$(SRC_PATH)/linux-headers -isystem $PWD/linux-headers $QEMU_INCLUDES" supported_os="yes" libudev="yes" ;; @@ -902,16 +918,38 @@ fi : ${install=${INSTALL-install}} # We prefer python 3.x. A bare 'python' is traditionally # python 2.x, but some distros have it as python 3.x, so -# we check that before python2 +# we check that too python= -for binary in "${PYTHON-python3}" python python2 +for binary in "${PYTHON-python3}" python do if has "$binary" then - python="$binary" + python=$(command -v "$binary") break fi done + +sphinx_build= +for binary in sphinx-build-3 sphinx-build +do + if has "$binary" + then + sphinx_build=$(command -v "$binary") + break + fi +done + +# Check for ancillary tools used in testing +genisoimage= +for binary in genisoimage +do + if has $binary + then + genisoimage=$(command -v "$binary") + break + fi +done + : ${smbd=${SMBD-/usr/sbin/smbd}} # Default objcc to clang if available, otherwise use CC @@ -926,7 +964,7 @@ if test "$mingw32" = "yes" ; then DSOSUF=".dll" # MinGW needs -mthreads for TLS and macro _MT. QEMU_CFLAGS="-mthreads $QEMU_CFLAGS" - LIBS="-lwinmm -lws2_32 -liphlpapi -lopengl32 -lgdi32 $LIBS" + LIBS="-lwinmm -lws2_32 -lopengl32 -lgdi32 $LIBS" write_c_skeleton; if compile_prog "" "-liberty" ; then LIBS="-liberty $LIBS" @@ -973,6 +1011,8 @@ for opt do ;; --python=*) python="$optarg" ;; + --sphinx-build=*) sphinx_build="$optarg" + ;; --gcov=*) gcov_tool="$optarg" ;; --smbd=*) smbd="$optarg" @@ -995,6 +1035,10 @@ for opt do --disable-modules) modules="no" ;; + --disable-module-upgrades) module_upgrades="no" + ;; + --enable-module-upgrades) module_upgrades="yes" + ;; --cpu=*) ;; --target-list=*) target_list="$optarg" @@ -1024,7 +1068,6 @@ for opt do ;; --static) static="yes" - LDFLAGS="-static $LDFLAGS" QEMU_PKG_CONFIG_FLAGS="--static $QEMU_PKG_CONFIG_FLAGS" ;; --mandir=*) mandir="$optarg" @@ -1155,10 +1198,6 @@ for opt do ;; --enable-brlapi) brlapi="yes" ;; - --disable-bluez) bluez="no" - ;; - --enable-bluez) bluez="yes" - ;; --disable-kvm) kvm="no" ;; --enable-kvm) kvm="yes" @@ -1260,6 +1299,10 @@ for opt do ;; --enable-linux-aio) linux_aio="yes" ;; + --disable-linux-io-uring) linux_io_uring="no" + ;; + --enable-linux-io-uring) linux_io_uring="yes" + ;; --disable-attr) attr="no" ;; --enable-attr) attr="yes" @@ -1350,6 +1393,10 @@ for opt do ;; --disable-lzfse) lzfse="no" ;; + --disable-zstd) zstd="no" + ;; + --enable-zstd) zstd="yes" + ;; --enable-guest-agent) guest_agent="yes" ;; --disable-guest-agent) guest_agent="no" @@ -1384,6 +1431,11 @@ for opt do ;; --enable-avx2) avx2_opt="yes" ;; + --disable-avx512f) avx512f_opt="no" + ;; + --enable-avx512f) avx512f_opt="yes" + ;; + --enable-glusterfs) glusterfs="yes" ;; --disable-virtio-blk-data-plane|--enable-virtio-blk-data-plane) @@ -1543,6 +1595,16 @@ for opt do ;; --disable-plugins) plugins="no" ;; + --enable-containers) use_containers="yes" + ;; + --disable-containers) use_containers="no" + ;; + --enable-fuzzing) fuzzing=yes + ;; + --disable-fuzzing) fuzzing=no + ;; + --gdb=*) gdb_bin="$optarg" + ;; *) echo "ERROR: unknown option $opt" echo "Try '$0 --help' for more information" @@ -1554,42 +1616,42 @@ done case "$cpu" in ppc) CPU_CFLAGS="-m32" - LDFLAGS="-m32 $LDFLAGS" + QEMU_LDFLAGS="-m32 $QEMU_LDFLAGS" ;; ppc64) CPU_CFLAGS="-m64" - LDFLAGS="-m64 $LDFLAGS" + QEMU_LDFLAGS="-m64 $QEMU_LDFLAGS" ;; sparc) CPU_CFLAGS="-m32 -mv8plus -mcpu=ultrasparc" - LDFLAGS="-m32 -mv8plus $LDFLAGS" + QEMU_LDFLAGS="-m32 -mv8plus $QEMU_LDFLAGS" ;; sparc64) CPU_CFLAGS="-m64 -mcpu=ultrasparc" - LDFLAGS="-m64 $LDFLAGS" + QEMU_LDFLAGS="-m64 $QEMU_LDFLAGS" ;; s390) CPU_CFLAGS="-m31" - LDFLAGS="-m31 $LDFLAGS" + QEMU_LDFLAGS="-m31 $QEMU_LDFLAGS" ;; s390x) CPU_CFLAGS="-m64" - LDFLAGS="-m64 $LDFLAGS" + QEMU_LDFLAGS="-m64 $QEMU_LDFLAGS" ;; i386) CPU_CFLAGS="-m32" - LDFLAGS="-m32 $LDFLAGS" + QEMU_LDFLAGS="-m32 $QEMU_LDFLAGS" ;; x86_64) # ??? Only extremely old AMD cpus do not have cmpxchg16b. # If we truly care, we should simply detect this case at # runtime and generate the fallback to serial emulation. CPU_CFLAGS="-m64 -mcx16" - LDFLAGS="-m64 $LDFLAGS" + QEMU_LDFLAGS="-m64 $QEMU_LDFLAGS" ;; x32) CPU_CFLAGS="-mx32" - LDFLAGS="-mx32 $LDFLAGS" + QEMU_LDFLAGS="-mx32 $QEMU_LDFLAGS" ;; # No special flags required for other host CPUs esac @@ -1675,6 +1737,7 @@ Advanced options (experts only): --make=MAKE use specified make [$make] --install=INSTALL use specified install [$install] --python=PYTHON use specified python [$python] + --sphinx-build=SPHINX use specified sphinx-build [$sphinx_build] --smbd=SMBD use specified smbd [$smbd] --with-git=GIT use specified git [$git] --static enable static build [$static] @@ -1726,6 +1789,8 @@ Advanced options (experts only): track the maximum stack usage of stacks created by qemu_alloc_stack --enable-plugins enable plugins via shared library loading + --disable-containers don't use containers for cross-building + --gdb=GDB-path gdb to use for gdbstub tests [$gdb_bin] Optional features, enabled with --enable-FEATURE and disabled with --disable-FEATURE, default is enabled if available: @@ -1739,6 +1804,7 @@ disabled with --disable-FEATURE, default is enabled if available: guest-agent-msi build guest agent Windows MSI installation package pie Position Independent Executables modules modules support (non-Windows) + module-upgrades try to load modules from alternate paths for upgrades debug-tcg TCG debugging (default is disabled) debug-info debugging information sparse sparse checker @@ -1766,7 +1832,6 @@ disabled with --disable-FEATURE, default is enabled if available: curl curl connectivity membarrier membarrier system call (for Linux 4.14+ or Windows) fdt fdt device tree - bluez bluez stack connectivity kvm KVM acceleration support hax HAX acceleration support hvf Hypervisor.framework acceleration support @@ -1776,6 +1841,7 @@ disabled with --disable-FEATURE, default is enabled if available: vde support for vde network netmap support for netmap network linux-aio Linux AIO support + linux-io-uring Linux io_uring support cap-ng libcap-ng support attr attr and xattr support vhost-net vhost-net kernel acceleration support @@ -1798,6 +1864,8 @@ disabled with --disable-FEATURE, default is enabled if available: (for reading bzip2-compressed dmg images) lzfse support of lzfse compression library (for reading lzfse-compressed dmg images) + zstd support for zstd compression library + (for migration compression) seccomp seccomp support coroutine-pool coroutine freelist (better performance) glusterfs GlusterFS backend @@ -1808,6 +1876,7 @@ disabled with --disable-FEATURE, default is enabled if available: tcmalloc tcmalloc support jemalloc jemalloc support avx2 AVX2 optimization support + avx512f AVX512F optimization support replication replication support opengl opengl support virglrenderer virgl rendering support @@ -1838,6 +1907,20 @@ fi # Remove old dependency files to make sure that they get properly regenerated rm -f */config-devices.mak.d +# Remove syscall_nr.h to be sure they will be regenerated in the build +# directory, not in the source directory +for arch in alpha hppa m68k xtensa sh4 microblaze arm ppc s390x sparc sparc64 \ + i386 x86_64 mips mips64 ; do + # remove the file if it has been generated in the source directory + rm -f "${source_path}/linux-user/${arch}/syscall_nr.h" + # remove the dependency files + for target in ${arch}*-linux-user ; do + test -d "${target}" && find "${target}" -type f -name "*.d" \ + -exec grep -q "${source_path}/linux-user/${arch}/syscall_nr.h" {} \; \ + -print | while read file ; do rm "${file}" "${file%.d}.o" ; done + done +done + if test -z "$python" then error_exit "Python not found. Use --python=/path/to/python" @@ -1845,8 +1928,8 @@ fi # Note that if the Python conditional here evaluates True we will exit # with status 1 which is a shell 'false' value. -if ! $python -c 'import sys; sys.exit(sys.version_info < (2,7))'; then - error_exit "Cannot use '$python', Python 2 >= 2.7 or Python 3 is required." \ +if ! $python -c 'import sys; sys.exit(sys.version_info < (3,5))'; then + error_exit "Cannot use '$python', Python >= 3.5 is required." \ "Use --python=/path/to/python to specify a supported Python." fi @@ -1970,6 +2053,7 @@ EOF if do_cc $QEMU_CFLAGS -Werror $flag -c -o $TMPO $TMPC && compile_prog "-Werror $flag" ""; then QEMU_CFLAGS="$QEMU_CFLAGS $flag" + QEMU_LDFLAGS="$QEMU_LDFLAGS $flag" sp_on=1 break fi @@ -1999,16 +2083,16 @@ if test "$modules" = "yes" && test "$mingw32" = "yes" ; then error_exit "Modules are not available for Windows" fi +# module_upgrades is only reasonable if modules are enabled +if test "$modules" = "no" && test "$module_upgrades" = "yes" ; then + error_exit "Can't enable module-upgrades as Modules are not enabled" +fi + # Static linking is not possible with modules or PIE if test "$static" = "yes" ; then if test "$modules" = "yes" ; then error_exit "static and modules are mutually incompatible" fi - if test "$pie" = "yes" ; then - error_exit "static and pie are mutually incompatible" - else - pie="no" - fi fi # Unconditional check for compiler __thread support @@ -2022,51 +2106,52 @@ if ! compile_prog "-Werror" "" ; then "Thread-Local Storage (TLS). Please upgrade to a version that does." fi -if test "$pie" = ""; then - case "$cpu-$targetos" in - i386-Linux|x86_64-Linux|x32-Linux|i386-OpenBSD|x86_64-OpenBSD) - ;; - *) - pie="no" - ;; - esac -fi - -if test "$pie" != "no" ; then - cat > $TMPC << EOF +cat > $TMPC << EOF #ifdef __linux__ # define THREAD __thread #else # define THREAD #endif - static THREAD int tls_var; - int main(void) { return tls_var; } - EOF - # check we support --no-pie first... - if compile_prog "-Werror -fno-pie" "-no-pie"; then - CFLAGS_NOPIE="-fno-pie" - LDFLAGS_NOPIE="-nopie" - fi - if compile_prog "-fPIE -DPIE" "-pie"; then +# Check we support --no-pie first; we will need this for building ROMs. +if compile_prog "-Werror -fno-pie" "-no-pie"; then + CFLAGS_NOPIE="-fno-pie" + LDFLAGS_NOPIE="-no-pie" +fi + +if test "$static" = "yes"; then + if test "$pie" != "no" && compile_prog "-Werror -fPIE -DPIE" "-static-pie"; then QEMU_CFLAGS="-fPIE -DPIE $QEMU_CFLAGS" - LDFLAGS="-pie $LDFLAGS" + QEMU_LDFLAGS="-static-pie $QEMU_LDFLAGS" pie="yes" - if compile_prog "" "-Wl,-z,relro -Wl,-z,now" ; then - LDFLAGS="-Wl,-z,relro -Wl,-z,now $LDFLAGS" - fi + elif test "$pie" = "yes"; then + error_exit "-static-pie not available due to missing toolchain support" else - if test "$pie" = "yes"; then - error_exit "PIE not available due to missing toolchain support" - else - echo "Disabling PIE due to missing toolchain support" - pie="no" - fi + QEMU_LDFLAGS="-static $QEMU_LDFLAGS" + pie="no" fi +elif test "$pie" = "no"; then + QEMU_CFLAGS="$CFLAGS_NOPIE $QEMU_CFLAGS" + QEMU_LDFLAGS="$LDFLAGS_NOPIE $QEMU_LDFLAGS" +elif compile_prog "-Werror -fPIE -DPIE" "-pie"; then + QEMU_CFLAGS="-fPIE -DPIE $QEMU_CFLAGS" + QEMU_LDFLAGS="-pie $QEMU_LDFLAGS" + pie="yes" +elif test "$pie" = "yes"; then + error_exit "PIE not available due to missing toolchain support" +else + echo "Disabling PIE due to missing toolchain support" + pie="no" +fi + +# Detect support for PT_GNU_RELRO + DT_BIND_NOW. +# The combination is known as "full relro", because .got.plt is read-only too. +if compile_prog "" "-Wl,-z,relro -Wl,-z,now" ; then + QEMU_LDFLAGS="-Wl,-z,relro -Wl,-z,now $QEMU_LDFLAGS" fi ########################################## @@ -2195,6 +2280,16 @@ else echo big/little test failed fi +########################################## +# system tools +if test -z "$want_tools"; then + if test "$softmmu" = "no"; then + want_tools=no + else + want_tools=yes + fi +fi + ########################################## # cocoa implies not SDL or GTK # (the cocoa UI code currently assumes it is always the active UI @@ -2411,6 +2506,25 @@ EOF fi fi +########################################## +# zstd check + +if test "$zstd" != "no" ; then + libzstd_minver="1.4.0" + if $pkg_config --atleast-version=$libzstd_minver libzstd ; then + zstd_cflags="$($pkg_config --cflags libzstd)" + zstd_libs="$($pkg_config --libs libzstd)" + LIBS="$zstd_libs $LIBS" + QEMU_CFLAGS="$QEMU_CFLAGS $zstd_cflags" + zstd="yes" + else + if test "$zstd" = "yes" ; then + feature_not_found "libzstd" "Install libzstd devel" + fi + zstd="no" + fi +fi + ########################################## # libseccomp check @@ -3300,7 +3414,9 @@ if test "$vnc" = "yes" && test "$vnc_sasl" != "no" ; then int main(void) { sasl_server_init(NULL, "qemu"); return 0; } EOF # Assuming Cyrus-SASL installed in /usr prefix - vnc_sasl_cflags="" + # QEMU defines struct iovec in "qemu/osdep.h", + # we don't want libsasl to redefine it in . + vnc_sasl_cflags="-DSTRUCT_IOVEC_DEFINED" vnc_sasl_libs="-lsasl2" if compile_prog "$vnc_sasl_cflags" "$vnc_sasl_libs" ; then vnc_sasl=yes @@ -3698,26 +3814,6 @@ EOF fi fi # test "$curl" -########################################## -# bluez support probe -if test "$bluez" != "no" ; then - cat > $TMPC << EOF -#include -int main(void) { return bt_error(0); } -EOF - bluez_cflags=$($pkg_config --cflags bluez 2>/dev/null) - bluez_libs=$($pkg_config --libs bluez 2>/dev/null) - if compile_prog "$bluez_cflags" "$bluez_libs" ; then - bluez=yes - libs_softmmu="$bluez_libs $libs_softmmu" - else - if test "$bluez" = "yes" ; then - feature_not_found "bluez" "Install bluez-libs/libbluetooth devel" - fi - bluez="no" - fi -fi - ########################################## # glib support probe @@ -3753,10 +3849,19 @@ if $pkg_config --atleast-version=$glib_req_ver gio-2.0; then gio=yes gio_cflags=$($pkg_config --cflags gio-2.0) gio_libs=$($pkg_config --libs gio-2.0) + gdbus_codegen=$($pkg_config --variable=gdbus_codegen gio-2.0) + if [ ! -x "$gdbus_codegen" ]; then + gdbus_codegen= + fi else gio=no fi +if $pkg_config --atleast-version=$glib_req_ver gio-unix-2.0; then + gio_cflags="$gio_cflags $($pkg_config --cflags gio-unix-2.0)" + gio_libs="$gio_libs $($pkg_config --libs gio-unix-2.0)" +fi + # Sanity check that the current size_t matches the # size that glib thinks it should be. This catches # problems on multi-arch where people try to build @@ -3793,6 +3898,26 @@ if ! compile_prog "$glib_cflags -Werror" "$glib_libs" ; then fi fi +# Silence clang warnings triggered by glib < 2.57.2 +cat > $TMPC << EOF +#include +typedef struct Foo { + int i; +} Foo; +static void foo_free(Foo *f) +{ + g_free(f); +} +G_DEFINE_AUTOPTR_CLEANUP_FUNC(Foo, foo_free); +int main(void) { return 0; } +EOF +if ! compile_prog "$glib_cflags -Werror" "$glib_libs" ; then + if cc_has_warning_flag "-Wno-unused-function"; then + glib_cflags="$glib_cflags -Wno-unused-function" + CFLAGS="$CFLAGS -Wno-unused-function" + fi +fi + ######################################### # zlib check @@ -3896,22 +4021,6 @@ else mpathpersist=no fi -########################################## -# libcap probe - -if test "$cap" != "no" ; then - cat > $TMPC < -#include -int main(void) { cap_t caps; caps = cap_init(); return caps != NULL; } -EOF - if compile_prog "" "-lcap" ; then - cap=yes - else - cap=no - fi -fi - ########################################## # pthread probe PTHREADLIBS_LIST="-pthread -lpthread -lpthreadGC2" @@ -4063,6 +4172,26 @@ EOF linux_aio=no fi fi +########################################## +# linux-io-uring probe + +if test "$linux_io_uring" != "no" ; then + if $pkg_config liburing; then + linux_io_uring_cflags=$($pkg_config --cflags liburing) + linux_io_uring_libs=$($pkg_config --libs liburing) + linux_io_uring=yes + + # io_uring is used in libqemuutil.a where per-file -libs variables are not + # seen by programs linking the archive. It's not ideal, but just add the + # library dependency globally. + LIBS="$linux_io_uring_libs $LIBS" + else + if test "$linux_io_uring" = "yes" ; then + feature_not_found "linux io_uring" "Install liburing devel" + fi + linux_io_uring=no + fi +fi ########################################## # TPM emulation is only on POSIX @@ -4141,7 +4270,7 @@ fi fdt_required=no for target in $target_list; do case $target in - aarch64*-softmmu|arm*-softmmu|ppc*-softmmu|microblaze*-softmmu|mips64el-softmmu|riscv*-softmmu) + aarch64*-softmmu|arm*-softmmu|ppc*-softmmu|microblaze*-softmmu|mips64el-softmmu|riscv*-softmmu|rx-softmmu) fdt_required=yes ;; esac @@ -4154,6 +4283,13 @@ if test "$fdt_required" = "yes"; then "targets which need it (by specifying a cut down --target-list)." fi fdt=yes +elif test "$fdt" != "yes" ; then + fdt=no +fi + +# fdt is only required when building softmmu targets +if test -z "$fdt" -a "$softmmu" != "yes" ; then + fdt="no" fi if test "$fdt" != "no" ; then @@ -4820,6 +4956,18 @@ if compile_prog "" "" ; then syncfs=yes fi +# check for kcov support (kernel must be 4.4+, compiled with certain options) +kcov=no +if check_include sys/kcov.h ; then + kcov=yes +fi + +# If we're making warnings fatal, apply this to Sphinx runs as well +sphinx_werror="" +if test "$werror" = "yes"; then + sphinx_werror="-W" +fi + # Check we have a new enough version of sphinx-build has_sphinx_build() { # This is a bit awkward but works: create a trivial document and @@ -4828,16 +4976,26 @@ has_sphinx_build() { # sphinx-build doesn't exist at all or if it is too old. mkdir -p "$TMPDIR1/sphinx" touch "$TMPDIR1/sphinx/index.rst" - sphinx-build -c "$source_path/docs" -b html "$TMPDIR1/sphinx" "$TMPDIR1/sphinx/out" >/dev/null 2>&1 + "$sphinx_build" $sphinx_werror -c "$source_path/docs" \ + -b html "$TMPDIR1/sphinx" \ + "$TMPDIR1/sphinx/out" >> config.log 2>&1 } # Check if tools are available to build documentation. if test "$docs" != "no" ; then - if has makeinfo && has pod2man && has_sphinx_build; then + if has_sphinx_build; then + sphinx_ok=yes + else + sphinx_ok=no + fi + if has makeinfo && has pod2man && test "$sphinx_ok" = "yes"; then docs=yes else if test "$docs" = "yes" ; then - feature_not_found "docs" "Install texinfo, Perl/perl-podlators and python-sphinx" + if has $sphinx_build && test "$sphinx_ok" != "yes"; then + echo "Warning: $sphinx_build exists but it is either too old or uses too old a Python version" >&2 + fi + feature_not_found "docs" "Install texinfo, Perl/perl-podlators and a Python 3 version of python-sphinx" fi docs=no fi @@ -5250,6 +5408,19 @@ if compile_prog "" "" ; then strchrnul=yes fi +######################################### +# check if we have st_atim + +st_atim=no +cat > $TMPC << EOF +#include +#include +int main(void) { return offsetof(struct stat, st_atim); } +EOF +if compile_prog "" "" ; then + st_atim=yes +fi + ########################################## # check if trace backend exists @@ -5497,6 +5668,36 @@ EOF fi fi +########################################## +# avx512f optimization requirement check +# +# There is no point enabling this if cpuid.h is not usable, +# since we won't be able to select the new routines. +# by default, it is turned off. +# if user explicitly want to enable it, check environment + +if test "$cpuid_h" = "yes" && test "$avx512f_opt" = "yes"; then + cat > $TMPC << EOF +#pragma GCC push_options +#pragma GCC target("avx512f") +#include +#include +static int bar(void *a) { + __m512i x = *(__m512i *)a; + return _mm512_test_epi64_mask(x, x); +} +int main(int argc, char *argv[]) +{ + return bar(argv[0]); +} +EOF + if ! compile_object "" ; then + avx512f_opt="no" + fi +else + avx512f_opt="no" +fi + ######################################## # check if __[u]int128_t is usable. @@ -5634,58 +5835,6 @@ if test "$plugins" = "yes" && "for this purpose. You can't build with --static." fi -######################################## -# See if 16-byte vector operations are supported. -# Even without a vector unit the compiler may expand these. -# There is a bug in old GCC for PPC that crashes here. -# Unfortunately it's the system compiler for Centos 7. - -cat > $TMPC << EOF -typedef unsigned char U1 __attribute__((vector_size(16))); -typedef unsigned short U2 __attribute__((vector_size(16))); -typedef unsigned int U4 __attribute__((vector_size(16))); -typedef unsigned long long U8 __attribute__((vector_size(16))); -typedef signed char S1 __attribute__((vector_size(16))); -typedef signed short S2 __attribute__((vector_size(16))); -typedef signed int S4 __attribute__((vector_size(16))); -typedef signed long long S8 __attribute__((vector_size(16))); -static U1 a1, b1; -static U2 a2, b2; -static U4 a4, b4; -static U8 a8, b8; -static S1 c1; -static S2 c2; -static S4 c4; -static S8 c8; -static int i; -void helper(void *d, void *a, int shift, int i); -void helper(void *d, void *a, int shift, int i) -{ - *(U1 *)(d + i) = *(U1 *)(a + i) << shift; - *(U2 *)(d + i) = *(U2 *)(a + i) << shift; - *(U4 *)(d + i) = *(U4 *)(a + i) << shift; - *(U8 *)(d + i) = *(U8 *)(a + i) << shift; -} -int main(void) -{ - a1 += b1; a2 += b2; a4 += b4; a8 += b8; - a1 -= b1; a2 -= b2; a4 -= b4; a8 -= b8; - a1 *= b1; a2 *= b2; a4 *= b4; a8 *= b8; - a1 &= b1; a2 &= b2; a4 &= b4; a8 &= b8; - a1 |= b1; a2 |= b2; a4 |= b4; a8 |= b8; - a1 ^= b1; a2 ^= b2; a4 ^= b4; a8 ^= b8; - a1 <<= i; a2 <<= i; a4 <<= i; a8 <<= i; - a1 >>= i; a2 >>= i; a4 >>= i; a8 >>= i; - c1 >>= i; c2 >>= i; c4 >>= i; c8 >>= i; - return 0; -} -EOF - -vector16=no -if compile_prog "" "" ; then - vector16=yes -fi - ######################################## # See if __attribute__((alias)) is supported. # This false for Xcode 9, but has been remedied for Xcode 10. @@ -5859,7 +6008,7 @@ fi ########################################## # check for usable AF_ALG environment -hava_afalg=no +have_afalg=no cat > $TMPC << EOF #include #include @@ -5894,7 +6043,7 @@ EOF hvf='no' else hvf='yes' - LDFLAGS="-framework Hypervisor $LDFLAGS" + QEMU_LDFLAGS="-framework Hypervisor $QEMU_LDFLAGS" fi fi @@ -6051,6 +6200,15 @@ EOF fi fi +########################################## +# checks for fuzzer +if test "$fuzzing" = "yes" ; then + write_c_fuzzer_skeleton + if compile_prog "$CPU_CFLAGS -Werror -fsanitize=address,fuzzer" ""; then + have_fuzzer=yes + fi +fi + ########################################## # check for libpmem @@ -6072,6 +6230,11 @@ fi ########################################## # check for slirp +# slirp is only required when building softmmu targets +if test -z "$slirp" -a "$softmmu" != "yes" ; then + slirp="no" +fi + case "$slirp" in "" | yes) if $pkg_config slirp; then @@ -6102,6 +6265,9 @@ case "$slirp" in mkdir -p slirp slirp_cflags="-I\$(SRC_PATH)/slirp/src -I\$(BUILD_DIR)/slirp/src" slirp_libs="-L\$(BUILD_DIR)/slirp -lslirp" + if test "$mingw32" = "yes" ; then + slirp_libs="$slirp_libs -lws2_32 -liphlpapi" + fi ;; system) @@ -6125,8 +6291,8 @@ esac write_c_skeleton if test "$gcov" = "yes" ; then - CFLAGS="-fprofile-arcs -ftest-coverage -g $CFLAGS" - LDFLAGS="-fprofile-arcs -ftest-coverage $LDFLAGS" + QEMU_CFLAGS="-fprofile-arcs -ftest-coverage -g $QEMU_CFLAGS" + QEMU_LDFLAGS="-fprofile-arcs -ftest-coverage $QEMU_LDFLAGS" elif test "$fortify_source" = "yes" ; then CFLAGS="-O2 -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 $CFLAGS" elif test "$debug" = "no"; then @@ -6134,7 +6300,8 @@ elif test "$debug" = "no"; then fi if test "$have_asan" = "yes"; then - CFLAGS="-fsanitize=address $CFLAGS" + QEMU_CFLAGS="-fsanitize=address $QEMU_CFLAGS" + QEMU_LDFLAGS="-fsanitize=address $QEMU_LDFLAGS" if test "$have_asan_iface_h" = "no" ; then echo "ASAN build enabled, but ASAN header missing." \ "Without code annotation, the report may be inferior." @@ -6144,7 +6311,8 @@ if test "$have_asan" = "yes"; then fi fi if test "$have_ubsan" = "yes"; then - CFLAGS="-fsanitize=undefined $CFLAGS" + QEMU_CFLAGS="-fsanitize=undefined $QEMU_CFLAGS" + QEMU_LDFLAGS="-fsanitize=undefined $QEMU_LDFLAGS" fi ########################################## @@ -6179,7 +6347,7 @@ fi if test "$solaris" = "no" ; then if $ld --version 2>/dev/null | grep "GNU ld" >/dev/null 2>/dev/null ; then - LDFLAGS="-Wl,--warn-common $LDFLAGS" + QEMU_LDFLAGS="-Wl,--warn-common $QEMU_LDFLAGS" fi fi @@ -6194,7 +6362,7 @@ fi if test "$mingw32" = "yes" ; then for flag in --dynamicbase --no-seh --nxcompat; do if ld_has $flag ; then - LDFLAGS="-Wl,$flag $LDFLAGS" + QEMU_LDFLAGS="-Wl,$flag $QEMU_LDFLAGS" fi done fi @@ -6226,7 +6394,7 @@ tools="" if test "$want_tools" = "yes" ; then tools="qemu-img\$(EXESUF) qemu-io\$(EXESUF) qemu-edid\$(EXESUF) $tools" if [ "$linux" = "yes" -o "$bsd" = "yes" -o "$solaris" = "yes" ] ; then - tools="qemu-nbd\$(EXESUF) $tools" + tools="qemu-nbd\$(EXESUF) qemu-storage-daemon\$(EXESUF) $tools" fi if [ "$ivshmem" = "yes" ]; then tools="ivshmem-client\$(EXESUF) ivshmem-server\$(EXESUF) $tools" @@ -6237,12 +6405,12 @@ if test "$want_tools" = "yes" ; then fi if test "$softmmu" = yes ; then if test "$linux" = yes; then - if test "$virtfs" != no && test "$cap" = yes && test "$attr" = yes ; then + if test "$virtfs" != no && test "$cap_ng" = yes && test "$attr" = yes ; then virtfs=yes tools="$tools fsdev/virtfs-proxy-helper\$(EXESUF)" else if test "$virtfs" = yes; then - error_exit "VirtFS requires libcap devel and libattr devel" + error_exit "VirtFS requires libcap-ng devel and libattr devel" fi virtfs=no fi @@ -6367,49 +6535,6 @@ if test "$cpu" = "s390x" ; then fi fi -# Probe for the need for relocating the user-only binary. -if ( [ "$linux_user" = yes ] || [ "$bsd_user" = yes ] ) && [ "$pie" = no ]; then - textseg_addr= - case "$cpu" in - arm | i386 | ppc* | s390* | sparc* | x86_64 | x32) - # ??? Rationale for choosing this address - textseg_addr=0x60000000 - ;; - mips) - # A 256M aligned address, high in the address space, with enough - # room for the code_gen_buffer above it before the stack. - textseg_addr=0x60000000 - ;; - esac - if [ -n "$textseg_addr" ]; then - cat > $TMPC </dev/null 2>&1; then - error_exit \ - "We need to link the QEMU user mode binaries at a" \ - "specific text address. Unfortunately your linker" \ - "doesn't support either the -Ttext-segment option or" \ - "printing the default linker script with --verbose." \ - "If you don't want the user mode binaries, pass the" \ - "--disable-user option to configure." - fi - - $ld --verbose | sed \ - -e '1,/==================================================/d' \ - -e '/==================================================/,$d' \ - -e "s/[.] = [0-9a-fx]* [+] SIZEOF_HEADERS/. = $textseg_addr + SIZEOF_HEADERS/" \ - -e "s/__executable_start = [0-9a-fx]*/__executable_start = $textseg_addr/" > config-host.ld - textseg_ldflags="-Wl,-T../config-host.ld" - fi - fi -fi - # Check that the C++ compiler exists and works with the C compiler. # All the QEMU_CXXFLAGS are based on QEMU_CFLAGS. Keep this at the end to don't miss any other that could be added. if has $cxx; then @@ -6429,7 +6554,7 @@ EOF update_cxxflags - if do_cxx $QEMU_CXXFLAGS -o $TMPE $TMPCXX $TMPO $LDFLAGS; then + if do_cxx $QEMU_CXXFLAGS -o $TMPE $TMPCXX $TMPO $QEMU_LDFLAGS; then # C++ compiler $cxx works ok with C compiler $cc : else @@ -6470,6 +6595,7 @@ else echo "local state directory queried at runtime" echo "Windows SDK $win_sdk" fi +echo "Build directory $(pwd)" echo "Source path $source_path" echo "GIT binary $git" echo "GIT submodules $git_submodules" @@ -6480,16 +6606,20 @@ echo "Objective-C compiler $objcc" echo "ARFLAGS $ARFLAGS" echo "CFLAGS $CFLAGS" echo "QEMU_CFLAGS $QEMU_CFLAGS" -echo "LDFLAGS $LDFLAGS" echo "QEMU_LDFLAGS $QEMU_LDFLAGS" echo "make $make" echo "install $install" echo "python $python ($python_version)" +if test "$docs" != "no"; then + echo "sphinx-build $sphinx_build" +fi +echo "genisoimage $genisoimage" echo "slirp support $slirp $(echo_version $slirp $slirp_version)" if test "$slirp" != "no" ; then echo "smbd $smbd" fi echo "module support $modules" +echo "alt path mod load $module_upgrades" echo "host CPU $cpu" echo "host big endian $bigendian" echo "target list $target_list" @@ -6542,12 +6672,12 @@ if test "$xen" = "yes" ; then echo "xen ctrl version $xen_ctrl_version" fi echo "brlapi support $brlapi" -echo "bluez support $bluez" echo "Documentation $docs" echo "PIE $pie" echo "vde support $vde" echo "netmap support $netmap" echo "Linux AIO support $linux_aio" +echo "Linux io_uring support $linux_io_uring" echo "ATTR/XATTR support $attr" echo "Install blobs $blobs" echo "KVM support $kvm" @@ -6611,11 +6741,13 @@ echo "lzo support $lzo" echo "snappy support $snappy" echo "bzip2 support $bzip2" echo "lzfse support $lzfse" +echo "zstd support $zstd" echo "NUMA host support $numa" echo "libxml2 $libxml2" echo "tcmalloc support $tcmalloc" echo "jemalloc support $jemalloc" echo "avx2 optimization $avx2_opt" +echo "avx512f optimization $avx512f_opt" echo "replication support $replication" echo "VxHS block device $vxhs" echo "bochs support $bochs" @@ -6632,6 +6764,8 @@ echo "libpmem support $libpmem" echo "libudev $libudev" echo "default devices $default_devices" echo "plugin support $plugins" +echo "fuzzing support $fuzzing" +echo "gdb $gdb_bin" if test "$supported_cpu" = "no"; then echo @@ -6659,15 +6793,6 @@ if test "$supported_os" = "no"; then echo "us upstream at qemu-devel@nongnu.org." fi -# Note that if the Python conditional here evaluates True we will exit -# with status 1 which is a shell 'false' value. -if ! $python -c 'import sys; sys.exit(sys.version_info < (3,0))'; then - echo - echo "warning: Python 2 support is deprecated" >&2 - echo "warning: Python 3 will be required for building future versions of QEMU" >&2 - python2="y" -fi - config_host_mak="config-host.mak" echo "# Automatically generated by configure - do not modify" >config-all-disas.mak @@ -6791,8 +6916,11 @@ fi if test "$l2tpv3" = "yes" ; then echo "CONFIG_L2TPV3=y" >> $config_host_mak fi +if test "$gprof" = "yes" ; then + echo "CONFIG_GPROF=y" >> $config_host_mak +fi if test "$cap_ng" = "yes" ; then - echo "CONFIG_LIBCAP=y" >> $config_host_mak + echo "CONFIG_LIBCAP_NG=y" >> $config_host_mak fi echo "CONFIG_AUDIO_DRIVERS=$audio_drv_list" >> $config_host_mak for drv in $audio_drv_list; do @@ -6847,6 +6975,9 @@ if test "$modules" = "yes"; then echo "CONFIG_STAMP=_$( (echo $qemu_version; echo $pkgversion; cat $0) | $shacmd - | cut -f1 -d\ )" >> $config_host_mak echo "CONFIG_MODULES=y" >> $config_host_mak fi +if test "$module_upgrades" = "yes"; then + echo "CONFIG_MODULE_UPGRADES=y" >> $config_host_mak +fi if test "$have_x11" = "yes" && test "$need_x11" = "yes"; then echo "CONFIG_X11=y" >> $config_host_mak echo "X11_CFLAGS=$x11_cflags" >> $config_host_mak @@ -6939,6 +7070,9 @@ fi if test "$syncfs" = "yes" ; then echo "CONFIG_SYNCFS=y" >> $config_host_mak fi +if test "$kcov" = "yes" ; then + echo "CONFIG_KCOV=y" >> $config_host_mak +fi if test "$inotify" = "yes" ; then echo "CONFIG_INOTIFY=y" >> $config_host_mak fi @@ -6951,6 +7085,9 @@ fi if test "$strchrnul" = "yes" ; then echo "HAVE_STRCHRNUL=y" >> $config_host_mak fi +if test "$st_atim" = "yes" ; then + echo "HAVE_STRUCT_STAT_ST_ATIM=y" >> $config_host_mak +fi if test "$byteswap_h" = "yes" ; then echo "CONFIG_BYTESWAP_H=y" >> $config_host_mak fi @@ -6966,10 +7103,6 @@ if test "$brlapi" = "yes" ; then echo "CONFIG_BRLAPI=y" >> $config_host_mak echo "BRLAPI_LIBS=$brlapi_libs" >> $config_host_mak fi -if test "$bluez" = "yes" ; then - echo "CONFIG_BLUEZ=y" >> $config_host_mak - echo "BLUEZ_CFLAGS=$bluez_cflags" >> $config_host_mak -fi if test "$gtk" = "yes" ; then echo "CONFIG_GTK=m" >> $config_host_mak echo "GTK_CFLAGS=$gtk_cflags" >> $config_host_mak @@ -6982,6 +7115,7 @@ if test "$gio" = "yes" ; then echo "CONFIG_GIO=y" >> $config_host_mak echo "GIO_CFLAGS=$gio_cflags" >> $config_host_mak echo "GIO_LIBS=$gio_libs" >> $config_host_mak + echo "GDBUS_CODEGEN=$gdbus_codegen" >> $config_host_mak fi echo "CONFIG_TLS_PRIORITY=\"$tls_priority\"" >> $config_host_mak if test "$gnutls" = "yes" ; then @@ -7040,6 +7174,11 @@ fi if test "$linux_aio" = "yes" ; then echo "CONFIG_LINUX_AIO=y" >> $config_host_mak fi +if test "$linux_io_uring" = "yes" ; then + echo "CONFIG_LINUX_IO_URING=y" >> $config_host_mak + echo "LINUX_IO_URING_CFLAGS=$linux_io_uring_cflags" >> $config_host_mak + echo "LINUX_IO_URING_LIBS=$linux_io_uring_libs" >> $config_host_mak +fi if test "$attr" = "yes" ; then echo "CONFIG_ATTR=y" >> $config_host_mak fi @@ -7164,6 +7303,10 @@ if test "$avx2_opt" = "yes" ; then echo "CONFIG_AVX2_OPT=y" >> $config_host_mak fi +if test "$avx512f_opt" = "yes" ; then + echo "CONFIG_AVX512F_OPT=y" >> $config_host_mak +fi + if test "$lzo" = "yes" ; then echo "CONFIG_LZO=y" >> $config_host_mak fi @@ -7182,6 +7325,10 @@ if test "$lzfse" = "yes" ; then echo "LZFSE_LIBS=-llzfse" >> $config_host_mak fi +if test "$zstd" = "yes" ; then + echo "CONFIG_ZSTD=y" >> $config_host_mak +fi + if test "$libiscsi" = "yes" ; then echo "CONFIG_LIBISCSI=m" >> $config_host_mak echo "LIBISCSI_CFLAGS=$libiscsi_cflags" >> $config_host_mak @@ -7275,10 +7422,6 @@ if test "$atomic64" = "yes" ; then echo "CONFIG_ATOMIC64=y" >> $config_host_mak fi -if test "$vector16" = "yes" ; then - echo "CONFIG_VECTOR16=y" >> $config_host_mak -fi - if test "$attralias" = "yes" ; then echo "CONFIG_ATTRIBUTE_ALIAS=y" >> $config_host_mak fi @@ -7468,6 +7611,16 @@ fi if test "$sheepdog" = "yes" ; then echo "CONFIG_SHEEPDOG=y" >> $config_host_mak fi +if test "$fuzzing" = "yes" ; then + if test "$have_fuzzer" = "yes"; then + FUZZ_LDFLAGS=" -fsanitize=address,fuzzer" + FUZZ_CFLAGS=" -fsanitize=address,fuzzer" + CFLAGS=" -fsanitize=address,fuzzer-no-link" + else + error_exit "Your compiler doesn't support -fsanitize=address,fuzzer" + exit 1 + fi +fi if test "$plugins" = "yes" ; then echo "CONFIG_PLUGIN=y" >> $config_host_mak @@ -7490,6 +7643,10 @@ if test "$plugins" = "yes" ; then fi fi +if test -n "$gdb_bin" ; then + echo "HAVE_GDB_BIN=$gdb_bin" >> $config_host_mak +fi + if test "$tcg_interpreter" = "yes"; then QEMU_INCLUDES="-iquote \$(SRC_PATH)/tcg/tci $QEMU_INCLUDES" elif test "$ARCH" = "sparc64" ; then @@ -7505,7 +7662,6 @@ elif test "$ARCH" = "riscv32" || test "$ARCH" = "riscv64" ; then else QEMU_INCLUDES="-iquote \$(SRC_PATH)/tcg/\$(ARCH) $QEMU_INCLUDES" fi -QEMU_INCLUDES="-iquote \$(SRC_PATH)/tcg $QEMU_INCLUDES" echo "TOOLS=$tools" >> $config_host_mak echo "ROMS=$roms" >> $config_host_mak @@ -7516,7 +7672,9 @@ echo "INSTALL_DATA=$install -c -m 0644" >> $config_host_mak echo "INSTALL_PROG=$install -c -m 0755" >> $config_host_mak echo "INSTALL_LIB=$install -c -m 0644" >> $config_host_mak echo "PYTHON=$python" >> $config_host_mak -echo "PYTHON2=$python2" >> $config_host_mak +echo "SPHINX_BUILD=$sphinx_build" >> $config_host_mak +echo "SPHINX_WERROR=$sphinx_werror" >> $config_host_mak +echo "GENISOIMAGE=$genisoimage" >> $config_host_mak echo "CC=$cc" >> $config_host_mak if $iasl -h > /dev/null 2>&1; then echo "IASL=$iasl" >> $config_host_mak @@ -7547,9 +7705,8 @@ if test "$sparse" = "yes" ; then echo "HOST_CC := REAL_CC=\"\$(HOST_CC)\" cgcc" >> $config_host_mak echo "QEMU_CFLAGS += -Wbitwise -Wno-transparent-union -Wno-old-initializer -Wno-non-pointer-null" >> $config_host_mak fi -echo "LDFLAGS=$LDFLAGS" >> $config_host_mak -echo "LDFLAGS_NOPIE=$LDFLAGS_NOPIE" >> $config_host_mak echo "QEMU_LDFLAGS=$QEMU_LDFLAGS" >> $config_host_mak +echo "LDFLAGS_NOPIE=$LDFLAGS_NOPIE" >> $config_host_mak echo "LD_REL_FLAGS=$LD_REL_FLAGS" >> $config_host_mak echo "LD_I386_EMULATION=$ld_i386_emulation" >> $config_host_mak echo "LIBS+=$LIBS" >> $config_host_mak @@ -7571,6 +7728,11 @@ if test "$libudev" != "no"; then echo "CONFIG_LIBUDEV=y" >> $config_host_mak echo "LIBUDEV_LIBS=$libudev_libs" >> $config_host_mak fi +if test "$fuzzing" != "no"; then + echo "CONFIG_FUZZ=y" >> $config_host_mak + echo "FUZZ_CFLAGS=$FUZZ_CFLAGS" >> $config_host_mak + echo "FUZZ_LDFLAGS=$FUZZ_LDFLAGS" >> $config_host_mak +fi if test "$edk2_blobs" = "yes" ; then echo "DECOMPRESS_EDK2_BLOBS=y" >> $config_host_mak @@ -7660,17 +7822,21 @@ case "$target_name" in i386) mttcg="yes" gdb_xml_files="i386-32bit.xml" + TARGET_SYSTBL_ABI=i386 ;; x86_64) TARGET_BASE_ARCH=i386 + TARGET_SYSTBL_ABI=common,64 mttcg="yes" gdb_xml_files="i386-64bit.xml" ;; alpha) mttcg="yes" + TARGET_SYSTBL_ABI=common ;; arm|armeb) TARGET_ARCH=arm + TARGET_SYSTBL_ABI=common,oabi bflt="yes" mttcg="yes" gdb_xml_files="arm-core.xml arm-vfp.xml arm-vfp3.xml arm-neon.xml" @@ -7686,15 +7852,18 @@ case "$target_name" in ;; hppa) mttcg="yes" + TARGET_SYSTBL_ABI=common,32 ;; lm32) ;; m68k) bflt="yes" gdb_xml_files="cf-core.xml cf-fp.xml m68k-fp.xml" + TARGET_SYSTBL_ABI=common ;; microblaze|microblazeel) TARGET_ARCH=microblaze + TARGET_SYSTBL_ABI=common bflt="yes" echo "TARGET_ABI32=y" >> $config_target_mak ;; @@ -7702,6 +7871,7 @@ case "$target_name" in mttcg="yes" TARGET_ARCH=mips echo "TARGET_ABI_MIPSO32=y" >> $config_target_mak + TARGET_SYSTBL_ABI=o32 ;; mipsn32|mipsn32el) mttcg="yes" @@ -7709,12 +7879,14 @@ case "$target_name" in TARGET_BASE_ARCH=mips echo "TARGET_ABI_MIPSN32=y" >> $config_target_mak echo "TARGET_ABI32=y" >> $config_target_mak + TARGET_SYSTBL_ABI=n32 ;; mips64|mips64el) - mttcg="yes" + mttcg="no" TARGET_ARCH=mips64 TARGET_BASE_ARCH=mips echo "TARGET_ABI_MIPSN64=y" >> $config_target_mak + TARGET_SYSTBL_ABI=n64 ;; moxie) ;; @@ -7726,10 +7898,12 @@ case "$target_name" in ;; ppc) gdb_xml_files="power-core.xml power-fpu.xml power-altivec.xml power-spe.xml" + TARGET_SYSTBL_ABI=common,nospu,32 ;; ppc64) TARGET_BASE_ARCH=ppc TARGET_ABI_DIR=ppc + TARGET_SYSTBL_ABI=common,nospu,64 mttcg=yes gdb_xml_files="power64-core.xml power-fpu.xml power-altivec.xml power-spe.xml power-vsx.xml" ;; @@ -7737,6 +7911,7 @@ case "$target_name" in TARGET_ARCH=ppc64 TARGET_BASE_ARCH=ppc TARGET_ABI_DIR=ppc + TARGET_SYSTBL_ABI=common,nospu,64 mttcg=yes gdb_xml_files="power64-core.xml power-fpu.xml power-altivec.xml power-spe.xml power-vsx.xml" ;; @@ -7744,6 +7919,7 @@ case "$target_name" in TARGET_ARCH=ppc64 TARGET_BASE_ARCH=ppc TARGET_ABI_DIR=ppc + TARGET_SYSTBL_ABI=common,nospu,32 echo "TARGET_ABI32=y" >> $config_target_mak gdb_xml_files="power64-core.xml power-fpu.xml power-altivec.xml power-spe.xml power-vsx.xml" ;; @@ -7751,30 +7927,41 @@ case "$target_name" in TARGET_BASE_ARCH=riscv TARGET_ABI_DIR=riscv mttcg=yes - gdb_xml_files="riscv-32bit-cpu.xml riscv-32bit-fpu.xml riscv-32bit-csr.xml riscv-32bit-virtual.xml" + gdb_xml_files="riscv-32bit-cpu.xml riscv-32bit-fpu.xml riscv-64bit-fpu.xml riscv-32bit-csr.xml riscv-32bit-virtual.xml" ;; riscv64) TARGET_BASE_ARCH=riscv TARGET_ABI_DIR=riscv mttcg=yes - gdb_xml_files="riscv-64bit-cpu.xml riscv-64bit-fpu.xml riscv-64bit-csr.xml riscv-64bit-virtual.xml" + gdb_xml_files="riscv-64bit-cpu.xml riscv-32bit-fpu.xml riscv-64bit-fpu.xml riscv-64bit-csr.xml riscv-64bit-virtual.xml" + ;; + rx) + TARGET_ARCH=rx + bflt="yes" + target_compiler=$cross_cc_rx + gdb_xml_files="rx-core.xml" ;; sh4|sh4eb) TARGET_ARCH=sh4 + TARGET_SYSTBL_ABI=common bflt="yes" ;; sparc) + TARGET_SYSTBL_ABI=common,32 ;; sparc64) TARGET_BASE_ARCH=sparc + TARGET_SYSTBL_ABI=common,64 ;; sparc32plus) TARGET_ARCH=sparc64 TARGET_BASE_ARCH=sparc TARGET_ABI_DIR=sparc + TARGET_SYSTBL_ABI=common,32 echo "TARGET_ABI32=y" >> $config_target_mak ;; s390x) + TARGET_SYSTBL_ABI=common,64 mttcg=yes gdb_xml_files="s390x-core64.xml s390-acr.xml s390-fpr.xml s390-vx.xml s390-cr.xml s390-virt.xml s390-gs.xml" ;; @@ -7786,6 +7973,7 @@ case "$target_name" in ;; xtensa|xtensaeb) TARGET_ARCH=xtensa + TARGET_SYSTBL_ABI=common bflt="yes" mttcg="yes" ;; @@ -7815,6 +8003,9 @@ echo "TARGET_ABI_DIR=$TARGET_ABI_DIR" >> $config_target_mak if [ "$HOST_VARIANT_DIR" != "" ]; then echo "HOST_VARIANT_DIR=$HOST_VARIANT_DIR" >> $config_target_mak fi +if [ "$TARGET_SYSTBL_ABI" != "" ]; then + echo "TARGET_SYSTBL_ABI=$TARGET_SYSTBL_ABI" >> $config_target_mak +fi if supported_xen_target $target; then echo "CONFIG_XEN=y" >> $config_target_mak @@ -7875,7 +8066,7 @@ if test "$target_bsd_user" = "yes" ; then fi -# generate QEMU_CFLAGS/LDFLAGS for targets +# generate QEMU_CFLAGS/QEMU_LDFLAGS for targets cflags="" ldflags="" @@ -7940,6 +8131,9 @@ for i in $ARCH $TARGET_BASE_ARCH ; do riscv*) disas_config "RISCV" ;; + rx) + disas_config "RX" + ;; s390*) disas_config "S390" ;; @@ -7966,7 +8160,6 @@ alpha) esac if test "$gprof" = "yes" ; then - echo "TARGET_GPROF=y" >> $config_target_mak if test "$target_linux_user" = "yes" ; then cflags="-p $cflags" ldflags="-p $ldflags" @@ -7977,10 +8170,6 @@ if test "$gprof" = "yes" ; then fi fi -if test "$target_linux_user" = "yes" || test "$target_bsd_user" = "yes" ; then - ldflags="$ldflags $textseg_ldflags" -fi - # Newer kernels on s390 check for an S390_PGSTE program header and # enable the pgste page table extensions in that case. This makes # the vm.allocate_pgste sysctl unnecessary. We enable this program @@ -7996,7 +8185,7 @@ if test "$TARGET_ARCH" = "s390x" && test "$target_softmmu" = "yes" && \ fi fi -echo "LDFLAGS+=$ldflags" >> $config_target_mak +echo "QEMU_LDFLAGS+=$ldflags" >> $config_target_mak echo "QEMU_CFLAGS+=$cflags" >> $config_target_mak done # for target in $targets @@ -8032,8 +8221,8 @@ fi # so the build tree will be missing the link back to the new file, and # tests might fail. Prefer to keep the relevant files in their own # directory and symlink the directory instead. -DIRS="tests tests/tcg tests/tcg/lm32 tests/libqos tests/qapi-schema tests/qemu-iotests tests/vm" -DIRS="$DIRS tests/fp tests/qgraph" +DIRS="tests tests/tcg tests/tcg/lm32 tests/qapi-schema tests/qtest/libqos" +DIRS="$DIRS tests/qtest tests/qemu-iotests tests/vm tests/fp tests/qgraph" DIRS="$DIRS docs docs/interop fsdev scsi" DIRS="$DIRS pc-bios/optionrom pc-bios/s390-ccw" DIRS="$DIRS roms/seabios roms/vgabios" @@ -8072,7 +8261,7 @@ done (for i in $cross_cc_vars; do export $i done -export target_list source_path +export target_list source_path use_containers $source_path/tests/tcg/configure.sh) # temporary config to build submodules diff --git a/contrib/libvhost-user/libvhost-user-glib.c b/contrib/libvhost-user/libvhost-user-glib.c index 99edd2f3de..53f1ca4cdd 100644 --- a/contrib/libvhost-user/libvhost-user-glib.c +++ b/contrib/libvhost-user/libvhost-user-glib.c @@ -89,9 +89,8 @@ vug_source_new(VugDev *gdev, int fd, GIOCondition cond, src->gfd.events = cond; g_source_add_poll(gsrc, &src->gfd); - id = g_source_attach(gsrc, NULL); + id = g_source_attach(gsrc, g_main_context_get_thread_default()); g_assert(id); - g_source_unref(gsrc); return gsrc; } @@ -131,6 +130,16 @@ static void vug_watch(VuDev *dev, int condition, void *data) } } +void vug_source_destroy(GSource *src) +{ + if (!src) { + return; + } + + g_source_destroy(src); + g_source_unref(src); +} + bool vug_init(VugDev *dev, uint16_t max_queues, int socket, vu_panic_cb panic, const VuDevIface *iface) @@ -144,7 +153,7 @@ vug_init(VugDev *dev, uint16_t max_queues, int socket, } dev->fdmap = g_hash_table_new_full(NULL, NULL, NULL, - (GDestroyNotify) g_source_destroy); + (GDestroyNotify) vug_source_destroy); dev->src = vug_source_new(dev, socket, G_IO_IN, vug_watch, NULL); @@ -157,5 +166,5 @@ vug_deinit(VugDev *dev) g_assert(dev); g_hash_table_unref(dev->fdmap); - g_source_unref(dev->src); + vug_source_destroy(dev->src); } diff --git a/contrib/libvhost-user/libvhost-user-glib.h b/contrib/libvhost-user/libvhost-user-glib.h index 64d539d93a..1a79a4916e 100644 --- a/contrib/libvhost-user/libvhost-user-glib.h +++ b/contrib/libvhost-user/libvhost-user-glib.h @@ -31,5 +31,6 @@ void vug_deinit(VugDev *dev); GSource *vug_source_new(VugDev *dev, int fd, GIOCondition cond, vu_watch_cb vu_cb, gpointer data); +void vug_source_destroy(GSource *src); #endif /* LIBVHOST_USER_GLIB_H */ diff --git a/contrib/libvhost-user/libvhost-user.c b/contrib/libvhost-user/libvhost-user.c index ec27b78ff1..3bca996c62 100644 --- a/contrib/libvhost-user/libvhost-user.c +++ b/contrib/libvhost-user/libvhost-user.c @@ -136,6 +136,7 @@ vu_request_to_string(unsigned int req) REQ(VHOST_USER_GET_INFLIGHT_FD), REQ(VHOST_USER_SET_INFLIGHT_FD), REQ(VHOST_USER_GPU_SET_SOCKET), + REQ(VHOST_USER_VRING_KICK), REQ(VHOST_USER_MAX), }; #undef REQ @@ -163,7 +164,10 @@ vu_panic(VuDev *dev, const char *msg, ...) dev->panic(dev, buf); free(buf); - /* FIXME: find a way to call virtio_error? */ + /* + * FIXME: + * find a way to call virtio_error, or perhaps close the connection? + */ } /* Translate guest physical address to our virtual address. */ @@ -392,26 +396,37 @@ vu_send_reply(VuDev *dev, int conn_fd, VhostUserMsg *vmsg) return vu_message_write(dev, conn_fd, vmsg); } +/* + * Processes a reply on the slave channel. + * Entered with slave_mutex held and releases it before exit. + * Returns true on success. + */ static bool vu_process_message_reply(VuDev *dev, const VhostUserMsg *vmsg) { VhostUserMsg msg_reply; + bool result = false; if ((vmsg->flags & VHOST_USER_NEED_REPLY_MASK) == 0) { - return true; + result = true; + goto out; } if (!vu_message_read(dev, dev->slave_fd, &msg_reply)) { - return false; + goto out; } if (msg_reply.request != vmsg->request) { DPRINT("Received unexpected msg type. Expected %d received %d", vmsg->request, msg_reply.request); - return false; + goto out; } - return msg_reply.payload.u64 == 0; + result = msg_reply.payload.u64 == 0; + +out: + pthread_mutex_unlock(&dev->slave_mutex); + return result; } /* Kick the log_call_fd if required. */ @@ -553,6 +568,21 @@ vu_reset_device_exec(VuDev *dev, VhostUserMsg *vmsg) return false; } +static bool +map_ring(VuDev *dev, VuVirtq *vq) +{ + vq->vring.desc = qva_to_va(dev, vq->vra.desc_user_addr); + vq->vring.used = qva_to_va(dev, vq->vra.used_user_addr); + vq->vring.avail = qva_to_va(dev, vq->vra.avail_user_addr); + + DPRINT("Setting virtq addresses:\n"); + DPRINT(" vring_desc at %p\n", vq->vring.desc); + DPRINT(" vring_used at %p\n", vq->vring.used); + DPRINT(" vring_avail at %p\n", vq->vring.avail); + + return !(vq->vring.desc && vq->vring.used && vq->vring.avail); +} + static bool vu_set_mem_table_exec_postcopy(VuDev *dev, VhostUserMsg *vmsg) { @@ -756,6 +786,14 @@ vu_set_mem_table_exec(VuDev *dev, VhostUserMsg *vmsg) close(vmsg->fds[i]); } + for (i = 0; i < dev->max_queues; i++) { + if (dev->vq[i].vring.desc) { + if (map_ring(dev, &dev->vq[i])) { + vu_panic(dev, "remaping queue %d during setmemtable", i); + } + } + } + return false; } @@ -842,18 +880,12 @@ vu_set_vring_addr_exec(VuDev *dev, VhostUserMsg *vmsg) DPRINT(" avail_user_addr: 0x%016" PRIx64 "\n", vra->avail_user_addr); DPRINT(" log_guest_addr: 0x%016" PRIx64 "\n", vra->log_guest_addr); + vq->vra = *vra; vq->vring.flags = vra->flags; - vq->vring.desc = qva_to_va(dev, vra->desc_user_addr); - vq->vring.used = qva_to_va(dev, vra->used_user_addr); - vq->vring.avail = qva_to_va(dev, vra->avail_user_addr); vq->vring.log_guest_addr = vra->log_guest_addr; - DPRINT("Setting virtq addresses:\n"); - DPRINT(" vring_desc at %p\n", vq->vring.desc); - DPRINT(" vring_used at %p\n", vq->vring.used); - DPRINT(" vring_avail at %p\n", vq->vring.avail); - if (!(vq->vring.desc && vq->vring.used && vq->vring.avail)) { + if (map_ring(dev, vq)) { vu_panic(dev, "Invalid vring_addr message"); return false; } @@ -920,6 +952,7 @@ static bool vu_check_queue_msg_file(VuDev *dev, VhostUserMsg *vmsg) { int index = vmsg->payload.u64 & VHOST_USER_VRING_IDX_MASK; + bool nofd = vmsg->payload.u64 & VHOST_USER_VRING_NOFD_MASK; if (index >= dev->max_queues) { vmsg_close_fds(vmsg); @@ -927,8 +960,12 @@ vu_check_queue_msg_file(VuDev *dev, VhostUserMsg *vmsg) return false; } - if (vmsg->payload.u64 & VHOST_USER_VRING_NOFD_MASK || - vmsg->fd_num != 1) { + if (nofd) { + vmsg_close_fds(vmsg); + return true; + } + + if (vmsg->fd_num != 1) { vmsg_close_fds(vmsg); vu_panic(dev, "Invalid fds in request: %d", vmsg->request); return false; @@ -1025,6 +1062,7 @@ static bool vu_set_vring_kick_exec(VuDev *dev, VhostUserMsg *vmsg) { int index = vmsg->payload.u64 & VHOST_USER_VRING_IDX_MASK; + bool nofd = vmsg->payload.u64 & VHOST_USER_VRING_NOFD_MASK; DPRINT("u64: 0x%016"PRIx64"\n", vmsg->payload.u64); @@ -1038,8 +1076,8 @@ vu_set_vring_kick_exec(VuDev *dev, VhostUserMsg *vmsg) dev->vq[index].kick_fd = -1; } - dev->vq[index].kick_fd = vmsg->fds[0]; - DPRINT("Got kick_fd: %d for vq: %d\n", vmsg->fds[0], index); + dev->vq[index].kick_fd = nofd ? -1 : vmsg->fds[0]; + DPRINT("Got kick_fd: %d for vq: %d\n", dev->vq[index].kick_fd, index); dev->vq[index].started = true; if (dev->iface->queue_set_started) { @@ -1105,10 +1143,13 @@ bool vu_set_queue_host_notifier(VuDev *dev, VuVirtq *vq, int fd, return false; } + pthread_mutex_lock(&dev->slave_mutex); if (!vu_message_write(dev, dev->slave_fd, &vmsg)) { + pthread_mutex_unlock(&dev->slave_mutex); return false; } + /* Also unlocks the slave_mutex */ return vu_process_message_reply(dev, &vmsg); } @@ -1116,6 +1157,7 @@ static bool vu_set_vring_call_exec(VuDev *dev, VhostUserMsg *vmsg) { int index = vmsg->payload.u64 & VHOST_USER_VRING_IDX_MASK; + bool nofd = vmsg->payload.u64 & VHOST_USER_VRING_NOFD_MASK; DPRINT("u64: 0x%016"PRIx64"\n", vmsg->payload.u64); @@ -1128,14 +1170,14 @@ vu_set_vring_call_exec(VuDev *dev, VhostUserMsg *vmsg) dev->vq[index].call_fd = -1; } - dev->vq[index].call_fd = vmsg->fds[0]; + dev->vq[index].call_fd = nofd ? -1 : vmsg->fds[0]; /* in case of I/O hang after reconnecting */ - if (eventfd_write(vmsg->fds[0], 1)) { + if (dev->vq[index].call_fd != -1 && eventfd_write(vmsg->fds[0], 1)) { return -1; } - DPRINT("Got call_fd: %d for vq: %d\n", vmsg->fds[0], index); + DPRINT("Got call_fd: %d for vq: %d\n", dev->vq[index].call_fd, index); return false; } @@ -1144,6 +1186,7 @@ static bool vu_set_vring_err_exec(VuDev *dev, VhostUserMsg *vmsg) { int index = vmsg->payload.u64 & VHOST_USER_VRING_IDX_MASK; + bool nofd = vmsg->payload.u64 & VHOST_USER_VRING_NOFD_MASK; DPRINT("u64: 0x%016"PRIx64"\n", vmsg->payload.u64); @@ -1156,7 +1199,7 @@ vu_set_vring_err_exec(VuDev *dev, VhostUserMsg *vmsg) dev->vq[index].err_fd = -1; } - dev->vq[index].err_fd = vmsg->fds[0]; + dev->vq[index].err_fd = nofd ? -1 : vmsg->fds[0]; return false; } @@ -1164,11 +1207,20 @@ vu_set_vring_err_exec(VuDev *dev, VhostUserMsg *vmsg) static bool vu_get_protocol_features_exec(VuDev *dev, VhostUserMsg *vmsg) { + /* + * Note that we support, but intentionally do not set, + * VHOST_USER_PROTOCOL_F_INBAND_NOTIFICATIONS. This means that + * a device implementation can return it in its callback + * (get_protocol_features) if it wants to use this for + * simulation, but it is otherwise not desirable (if even + * implemented by the master.) + */ uint64_t features = 1ULL << VHOST_USER_PROTOCOL_F_MQ | 1ULL << VHOST_USER_PROTOCOL_F_LOG_SHMFD | 1ULL << VHOST_USER_PROTOCOL_F_SLAVE_REQ | 1ULL << VHOST_USER_PROTOCOL_F_HOST_NOTIFIER | - 1ULL << VHOST_USER_PROTOCOL_F_SLAVE_SEND_FD; + 1ULL << VHOST_USER_PROTOCOL_F_SLAVE_SEND_FD | + 1ULL << VHOST_USER_PROTOCOL_F_REPLY_ACK; if (have_userfault()) { features |= 1ULL << VHOST_USER_PROTOCOL_F_PAGEFAULT; @@ -1195,6 +1247,25 @@ vu_set_protocol_features_exec(VuDev *dev, VhostUserMsg *vmsg) dev->protocol_features = vmsg->payload.u64; + if (vu_has_protocol_feature(dev, + VHOST_USER_PROTOCOL_F_INBAND_NOTIFICATIONS) && + (!vu_has_protocol_feature(dev, VHOST_USER_PROTOCOL_F_SLAVE_REQ) || + !vu_has_protocol_feature(dev, VHOST_USER_PROTOCOL_F_REPLY_ACK))) { + /* + * The use case for using messages for kick/call is simulation, to make + * the kick and call synchronous. To actually get that behaviour, both + * of the other features are required. + * Theoretically, one could use only kick messages, or do them without + * having F_REPLY_ACK, but too many (possibly pending) messages on the + * socket will eventually cause the master to hang, to avoid this in + * scenarios where not desired enforce that the settings are in a way + * that actually enables the simulation case. + */ + vu_panic(dev, + "F_IN_BAND_NOTIFICATIONS requires F_SLAVE_REQ && F_REPLY_ACK"); + return false; + } + if (dev->iface->set_protocol_features) { dev->iface->set_protocol_features(dev, features); } @@ -1455,6 +1526,34 @@ vu_set_inflight_fd(VuDev *dev, VhostUserMsg *vmsg) return false; } +static bool +vu_handle_vring_kick(VuDev *dev, VhostUserMsg *vmsg) +{ + unsigned int index = vmsg->payload.state.index; + + if (index >= dev->max_queues) { + vu_panic(dev, "Invalid queue index: %u", index); + return false; + } + + DPRINT("Got kick message: handler:%p idx:%d\n", + dev->vq[index].handler, index); + + if (!dev->vq[index].started) { + dev->vq[index].started = true; + + if (dev->iface->queue_set_started) { + dev->iface->queue_set_started(dev, index, true); + } + } + + if (dev->vq[index].handler) { + dev->vq[index].handler(dev, index); + } + + return false; +} + static bool vu_process_message(VuDev *dev, VhostUserMsg *vmsg) { @@ -1537,6 +1636,8 @@ vu_process_message(VuDev *dev, VhostUserMsg *vmsg) return vu_get_inflight_fd(dev, vmsg); case VHOST_USER_SET_INFLIGHT_FD: return vu_set_inflight_fd(dev, vmsg); + case VHOST_USER_VRING_KICK: + return vu_handle_vring_kick(dev, vmsg); default: vmsg_close_fds(vmsg); vu_panic(dev, "Unhandled request: %d", vmsg->request); @@ -1550,13 +1651,20 @@ vu_dispatch(VuDev *dev) { VhostUserMsg vmsg = { 0, }; int reply_requested; - bool success = false; + bool need_reply, success = false; if (!vu_message_read(dev, dev->sock, &vmsg)) { goto end; } + need_reply = vmsg.flags & VHOST_USER_NEED_REPLY_MASK; + reply_requested = vu_process_message(dev, &vmsg); + if (!reply_requested && need_reply) { + vmsg_set_reply_u64(&vmsg, 0); + reply_requested = 1; + } + if (!reply_requested) { success = true; goto end; @@ -1628,6 +1736,7 @@ vu_deinit(VuDev *dev) close(dev->slave_fd); dev->slave_fd = -1; } + pthread_mutex_destroy(&dev->slave_mutex); if (dev->sock != -1) { close(dev->sock); @@ -1663,6 +1772,7 @@ vu_init(VuDev *dev, dev->remove_watch = remove_watch; dev->iface = iface; dev->log_call_fd = -1; + pthread_mutex_init(&dev->slave_mutex, NULL); dev->slave_fd = -1; dev->max_queues = max_queues; @@ -1989,8 +2099,7 @@ vring_notify(VuDev *dev, VuVirtq *vq) return !v || vring_need_event(vring_get_used_event(vq), new, old); } -void -vu_queue_notify(VuDev *dev, VuVirtq *vq) +static void _vu_queue_notify(VuDev *dev, VuVirtq *vq, bool sync) { if (unlikely(dev->broken) || unlikely(!vq->vring.avail)) { @@ -2002,11 +2111,48 @@ vu_queue_notify(VuDev *dev, VuVirtq *vq) return; } + if (vq->call_fd < 0 && + vu_has_protocol_feature(dev, + VHOST_USER_PROTOCOL_F_INBAND_NOTIFICATIONS) && + vu_has_protocol_feature(dev, VHOST_USER_PROTOCOL_F_SLAVE_REQ)) { + VhostUserMsg vmsg = { + .request = VHOST_USER_SLAVE_VRING_CALL, + .flags = VHOST_USER_VERSION, + .size = sizeof(vmsg.payload.state), + .payload.state = { + .index = vq - dev->vq, + }, + }; + bool ack = sync && + vu_has_protocol_feature(dev, + VHOST_USER_PROTOCOL_F_REPLY_ACK); + + if (ack) { + vmsg.flags |= VHOST_USER_NEED_REPLY_MASK; + } + + vu_message_write(dev, dev->slave_fd, &vmsg); + if (ack) { + vu_message_read(dev, dev->slave_fd, &vmsg); + } + return; + } + if (eventfd_write(vq->call_fd, 1) < 0) { vu_panic(dev, "Error writing eventfd: %s", strerror(errno)); } } +void vu_queue_notify(VuDev *dev, VuVirtq *vq) +{ + _vu_queue_notify(dev, vq, false); +} + +void vu_queue_notify_sync(VuDev *dev, VuVirtq *vq) +{ + _vu_queue_notify(dev, vq, true); +} + static inline void vring_used_flags_set_bit(VuVirtq *vq, int mask) { diff --git a/contrib/libvhost-user/libvhost-user.h b/contrib/libvhost-user/libvhost-user.h index 46b600799b..f30394fab6 100644 --- a/contrib/libvhost-user/libvhost-user.h +++ b/contrib/libvhost-user/libvhost-user.h @@ -19,6 +19,7 @@ #include #include #include +#include #include "standard-headers/linux/virtio_ring.h" /* Based on qemu/hw/virtio/vhost-user.c */ @@ -53,6 +54,7 @@ enum VhostUserProtocolFeature { VHOST_USER_PROTOCOL_F_SLAVE_SEND_FD = 10, VHOST_USER_PROTOCOL_F_HOST_NOTIFIER = 11, VHOST_USER_PROTOCOL_F_INFLIGHT_SHMFD = 12, + VHOST_USER_PROTOCOL_F_INBAND_NOTIFICATIONS = 14, VHOST_USER_PROTOCOL_F_MAX }; @@ -94,6 +96,7 @@ typedef enum VhostUserRequest { VHOST_USER_GET_INFLIGHT_FD = 31, VHOST_USER_SET_INFLIGHT_FD = 32, VHOST_USER_GPU_SET_SOCKET = 33, + VHOST_USER_VRING_KICK = 35, VHOST_USER_MAX } VhostUserRequest; @@ -102,6 +105,8 @@ typedef enum VhostUserSlaveRequest { VHOST_USER_SLAVE_IOTLB_MSG = 1, VHOST_USER_SLAVE_CONFIG_CHANGE_MSG = 2, VHOST_USER_SLAVE_VRING_HOST_NOTIFIER_MSG = 3, + VHOST_USER_SLAVE_VRING_CALL = 4, + VHOST_USER_SLAVE_VRING_ERR = 5, VHOST_USER_SLAVE_MAX } VhostUserSlaveRequest; @@ -281,7 +286,7 @@ typedef struct VuVirtqInflight { uint16_t used_idx; /* Used to track the state of each descriptor in descriptor table */ - VuDescStateSplit desc[0]; + VuDescStateSplit desc[]; } VuVirtqInflight; typedef struct VuVirtqInflightDesc { @@ -326,6 +331,9 @@ typedef struct VuVirtq { int err_fd; unsigned int enable; bool started; + + /* Guest addresses of our ring */ + struct vhost_vring_addr vra; } VuVirtq; enum VuWatchCondtion { @@ -355,6 +363,8 @@ struct VuDev { VuVirtq *vq; VuDevInflightInfo inflight_info; int log_call_fd; + /* Must be held while using slave_fd */ + pthread_mutex_t slave_mutex; int slave_fd; uint64_t log_size; uint8_t *log_table; @@ -522,6 +532,16 @@ bool vu_queue_empty(VuDev *dev, VuVirtq *vq); */ void vu_queue_notify(VuDev *dev, VuVirtq *vq); +/** + * vu_queue_notify_sync: + * @dev: a VuDev context + * @vq: a VuVirtq queue + * + * Request to notify the queue via callfd (skipped if unnecessary) + * or sync message if possible. + */ +void vu_queue_notify_sync(VuDev *dev, VuVirtq *vq); + /** * vu_queue_pop: * @dev: a VuDev context diff --git a/contrib/rdmacm-mux/main.c b/contrib/rdmacm-mux/main.c index de53048f06..bd82abbad3 100644 --- a/contrib/rdmacm-mux/main.c +++ b/contrib/rdmacm-mux/main.c @@ -490,7 +490,7 @@ static int read_and_process(int fd) static int accept_all(void) { - int fd, rc = 0;; + int fd, rc = 0; pthread_rwlock_wrlock(&server.lock); diff --git a/contrib/vhost-user-blk/vhost-user-blk.c b/contrib/vhost-user-blk/vhost-user-blk.c index ae61034656..6fd91c7e99 100644 --- a/contrib/vhost-user-blk/vhost-user-blk.c +++ b/contrib/vhost-user-blk/vhost-user-blk.c @@ -576,70 +576,90 @@ vub_new(char *blk_file) return vdev_blk; } +static int opt_fdnum = -1; +static char *opt_socket_path; +static char *opt_blk_file; +static gboolean opt_print_caps; +static gboolean opt_read_only; + +static GOptionEntry entries[] = { + { "print-capabilities", 'c', 0, G_OPTION_ARG_NONE, &opt_print_caps, + "Print capabilities", NULL }, + { "fd", 'f', 0, G_OPTION_ARG_INT, &opt_fdnum, + "Use inherited fd socket", "FDNUM" }, + { "socket-path", 's', 0, G_OPTION_ARG_FILENAME, &opt_socket_path, + "Use UNIX socket path", "PATH" }, + {"blk-file", 'b', 0, G_OPTION_ARG_FILENAME, &opt_blk_file, + "block device or file path", "PATH"}, + { "read-only", 'r', 0, G_OPTION_ARG_NONE, &opt_read_only, + "Enable read-only", NULL } +}; + int main(int argc, char **argv) { - int opt; - char *unix_socket = NULL; - char *blk_file = NULL; - bool enable_ro = false; int lsock = -1, csock = -1; VubDev *vdev_blk = NULL; + GError *error = NULL; + GOptionContext *context; - while ((opt = getopt(argc, argv, "b:rs:h")) != -1) { - switch (opt) { - case 'b': - blk_file = g_strdup(optarg); - break; - case 's': - unix_socket = g_strdup(optarg); - break; - case 'r': - enable_ro = true; - break; - case 'h': - default: - printf("Usage: %s [ -b block device or file, -s UNIX domain socket" - " | -r Enable read-only ] | [ -h ]\n", argv[0]); - return 0; + context = g_option_context_new(NULL); + g_option_context_add_main_entries(context, entries, NULL); + if (!g_option_context_parse(context, &argc, &argv, &error)) { + g_printerr("Option parsing failed: %s\n", error->message); + exit(EXIT_FAILURE); + } + if (opt_print_caps) { + g_print("{\n"); + g_print(" \"type\": \"block\",\n"); + g_print(" \"features\": [\n"); + g_print(" \"read-only\",\n"); + g_print(" \"blk-file\"\n"); + g_print(" ]\n"); + g_print("}\n"); + exit(EXIT_SUCCESS); + } + + if (!opt_blk_file) { + g_print("%s\n", g_option_context_get_help(context, true, NULL)); + exit(EXIT_FAILURE); + } + + if (opt_socket_path) { + lsock = unix_sock_new(opt_socket_path); + if (lsock < 0) { + exit(EXIT_FAILURE); } + } else if (opt_fdnum < 0) { + g_print("%s\n", g_option_context_get_help(context, true, NULL)); + exit(EXIT_FAILURE); + } else { + lsock = opt_fdnum; } - if (!unix_socket || !blk_file) { - printf("Usage: %s [ -b block device or file, -s UNIX domain socket" - " | -r Enable read-only ] | [ -h ]\n", argv[0]); - return -1; - } - - lsock = unix_sock_new(unix_socket); - if (lsock < 0) { - goto err; - } - - csock = accept(lsock, (void *)0, (void *)0); + csock = accept(lsock, NULL, NULL); if (csock < 0) { - fprintf(stderr, "Accept error %s\n", strerror(errno)); - goto err; + g_printerr("Accept error %s\n", strerror(errno)); + exit(EXIT_FAILURE); } - vdev_blk = vub_new(blk_file); + vdev_blk = vub_new(opt_blk_file); if (!vdev_blk) { - goto err; + exit(EXIT_FAILURE); } - if (enable_ro) { + if (opt_read_only) { vdev_blk->enable_ro = true; } if (!vug_init(&vdev_blk->parent, VHOST_USER_BLK_MAX_QUEUES, csock, vub_panic_cb, &vub_iface)) { - fprintf(stderr, "Failed to initialized libvhost-user-glib\n"); - goto err; + g_printerr("Failed to initialize libvhost-user-glib\n"); + exit(EXIT_FAILURE); } g_main_loop_run(vdev_blk->loop); - + g_main_loop_unref(vdev_blk->loop); + g_option_context_free(context); vug_deinit(&vdev_blk->parent); - -err: vub_free(vdev_blk); if (csock >= 0) { close(csock); @@ -647,8 +667,8 @@ err: if (lsock >= 0) { close(lsock); } - g_free(unix_socket); - g_free(blk_file); + g_free(opt_socket_path); + g_free(opt_blk_file); return 0; } diff --git a/contrib/vhost-user-gpu/50-qemu-gpu.json.in b/contrib/vhost-user-gpu/50-qemu-gpu.json.in index 658b545864..f5edd097f8 100644 --- a/contrib/vhost-user-gpu/50-qemu-gpu.json.in +++ b/contrib/vhost-user-gpu/50-qemu-gpu.json.in @@ -1,5 +1,5 @@ { "description": "QEMU vhost-user-gpu", "type": "gpu", - "binary": "@libexecdir@/vhost-user-gpu", + "binary": "@libexecdir@/vhost-user-gpu" } diff --git a/contrib/vhost-user-gpu/Makefile.objs b/contrib/vhost-user-gpu/Makefile.objs index 6170c919e4..09296091be 100644 --- a/contrib/vhost-user-gpu/Makefile.objs +++ b/contrib/vhost-user-gpu/Makefile.objs @@ -1,7 +1,7 @@ -vhost-user-gpu-obj-y = main.o virgl.o vugbm.o +vhost-user-gpu-obj-y = vhost-user-gpu.o virgl.o vugbm.o -main.o-cflags := $(PIXMAN_CFLAGS) $(GBM_CFLAGS) -main.o-libs := $(PIXMAN_LIBS) +vhost-user-gpu.o-cflags := $(PIXMAN_CFLAGS) $(GBM_CFLAGS) +vhost-user-gpu.o-libs := $(PIXMAN_LIBS) virgl.o-cflags := $(VIRGL_CFLAGS) $(GBM_CFLAGS) virgl.o-libs := $(VIRGL_LIBS) diff --git a/contrib/vhost-user-gpu/main.c b/contrib/vhost-user-gpu/vhost-user-gpu.c similarity index 99% rename from contrib/vhost-user-gpu/main.c rename to contrib/vhost-user-gpu/vhost-user-gpu.c index b45d2019b4..a019d0a9ac 100644 --- a/contrib/vhost-user-gpu/main.c +++ b/contrib/vhost-user-gpu/vhost-user-gpu.c @@ -848,7 +848,7 @@ vg_handle_ctrl(VuDev *dev, int qidx) QTAILQ_INSERT_TAIL(&vg->fenceq, cmd, next); vg->inflight++; } else { - g_free(cmd); + free(cmd); } } } @@ -939,7 +939,7 @@ vg_handle_cursor(VuDev *dev, int qidx) } vu_queue_push(dev, vq, elem, 0); vu_queue_notify(dev, vq); - g_free(elem); + free(elem); } } diff --git a/contrib/vhost-user-gpu/virgl.c b/contrib/vhost-user-gpu/virgl.c index 43413e29df..b0bc22c3c1 100644 --- a/contrib/vhost-user-gpu/virgl.c +++ b/contrib/vhost-user-gpu/virgl.c @@ -519,7 +519,7 @@ virgl_write_fence(void *opaque, uint32_t fence) g_debug("FENCE %" PRIu64, cmd->cmd_hdr.fence_id); vg_ctrl_response_nodata(g, cmd, VIRTIO_GPU_RESP_OK_NODATA); QTAILQ_REMOVE(&g->fenceq, cmd, next); - g_free(cmd); + free(cmd); g->inflight--; } } diff --git a/contrib/vhost-user-input/main.c b/contrib/vhost-user-input/main.c index ef4b7769f2..6020c6f33a 100644 --- a/contrib/vhost-user-input/main.c +++ b/contrib/vhost-user-input/main.c @@ -187,7 +187,7 @@ vi_queue_set_started(VuDev *dev, int qidx, bool started) } if (!started && vi->evsrc) { - g_source_destroy(vi->evsrc); + vug_source_destroy(vi->evsrc); vi->evsrc = NULL; } } @@ -401,9 +401,7 @@ main(int argc, char *argv[]) vug_deinit(&vi.dev); - if (vi.evsrc) { - g_source_unref(vi.evsrc); - } + vug_source_destroy(vi.evsrc); g_array_free(vi.config, TRUE); g_free(vi.queue); return 0; diff --git a/contrib/vhost-user-scsi/vhost-user-scsi.c b/contrib/vhost-user-scsi/vhost-user-scsi.c index 0fc14d7899..7a1db164c8 100644 --- a/contrib/vhost-user-scsi/vhost-user-scsi.c +++ b/contrib/vhost-user-scsi/vhost-user-scsi.c @@ -115,7 +115,7 @@ static int get_cdb_len(uint8_t *cdb) case 4: return 16; case 5: return 12; } - g_warning("Unable to determine cdb len (0x%02hhX)", cdb[0] >> 5); + g_warning("Unable to determine cdb len (0x%02hhX)", (uint8_t)(cdb[0] >> 5)); return -1; } diff --git a/cpus.c b/cpus.c index 63bda152f5..ef441bdf62 100644 --- a/cpus.c +++ b/cpus.c @@ -53,7 +53,7 @@ #include "qemu/bitmap.h" #include "qemu/seqlock.h" #include "qemu/guest-random.h" -#include "tcg.h" +#include "tcg/tcg.h" #include "hw/nmi.h" #include "sysemu/replay.h" #include "sysemu/runstate.h" @@ -166,78 +166,6 @@ typedef struct TimersState { static TimersState timers_state; bool mttcg_enabled; -/* - * We default to false if we know other options have been enabled - * which are currently incompatible with MTTCG. Otherwise when each - * guest (target) has been updated to support: - * - atomic instructions - * - memory ordering primitives (barriers) - * they can set the appropriate CONFIG flags in ${target}-softmmu.mak - * - * Once a guest architecture has been converted to the new primitives - * there are two remaining limitations to check. - * - * - The guest can't be oversized (e.g. 64 bit guest on 32 bit host) - * - The host must have a stronger memory order than the guest - * - * It may be possible in future to support strong guests on weak hosts - * but that will require tagging all load/stores in a guest with their - * implicit memory order requirements which would likely slow things - * down a lot. - */ - -static bool check_tcg_memory_orders_compatible(void) -{ -#if defined(TCG_GUEST_DEFAULT_MO) && defined(TCG_TARGET_DEFAULT_MO) - return (TCG_GUEST_DEFAULT_MO & ~TCG_TARGET_DEFAULT_MO) == 0; -#else - return false; -#endif -} - -static bool default_mttcg_enabled(void) -{ - if (use_icount || TCG_OVERSIZED_GUEST) { - return false; - } else { -#ifdef TARGET_SUPPORTS_MTTCG - return check_tcg_memory_orders_compatible(); -#else - return false; -#endif - } -} - -void qemu_tcg_configure(QemuOpts *opts, Error **errp) -{ - const char *t = qemu_opt_get(opts, "thread"); - if (t) { - if (strcmp(t, "multi") == 0) { - if (TCG_OVERSIZED_GUEST) { - error_setg(errp, "No MTTCG when guest word size > hosts"); - } else if (use_icount) { - error_setg(errp, "No MTTCG when icount is enabled"); - } else { -#ifndef TARGET_SUPPORTS_MTTCG - warn_report("Guest not yet converted to MTTCG - " - "you may get unexpected results"); -#endif - if (!check_tcg_memory_orders_compatible()) { - warn_report("Guest expects a stronger memory ordering " - "than the host provides"); - error_printf("This may cause strange/hard to debug errors\n"); - } - mttcg_enabled = true; - } - } else if (strcmp(t, "single") == 0) { - mttcg_enabled = false; - } else { - error_setg(errp, "Invalid 'thread' setting %s", t); - } - } else { - mttcg_enabled = default_mttcg_enabled(); - } -} /* The current number of executed instructions is based on what we * originally budgeted minus the current state of the decrementing @@ -1098,9 +1026,9 @@ static int do_vm_stop(RunState state, bool send_stop) int ret = 0; if (runstate_is_running()) { + runstate_set(state); cpu_disable_ticks(); pause_all_vcpus(); - runstate_set(state); vm_state_notify(0, state); if (send_stop) { qapi_event_send_stop(); @@ -1388,6 +1316,10 @@ static int64_t tcg_get_icount_limit(void) */ deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL, QEMU_TIMER_ATTR_ALL); + /* Check realtime timers, because they help with input processing */ + deadline = qemu_soonest_timeout(deadline, + qemu_clock_deadline_ns_all(QEMU_CLOCK_REALTIME, + QEMU_TIMER_ATTR_ALL)); /* Maintain prior (possibly buggy) behaviour where if no deadline * was set (as there is no QEMU_CLOCK_VIRTUAL timer) or it is more than @@ -1907,6 +1839,11 @@ void qemu_mutex_unlock_iothread(void) qemu_mutex_unlock(&qemu_global_mutex); } +void qemu_cond_wait_iothread(QemuCond *cond) +{ + qemu_cond_wait(cond, &qemu_global_mutex); +} + static bool all_vcpus_paused(void) { CPUState *cpu; @@ -1962,6 +1899,10 @@ void resume_all_vcpus(void) { CPUState *cpu; + if (!runstate_is_running()) { + return; + } + qemu_clock_enable(QEMU_CLOCK_VIRTUAL, true); CPU_FOREACH(cpu) { cpu_resume(cpu); diff --git a/crypto/Makefile.objs b/crypto/Makefile.objs index cdb01f9de9..c2a371b0b4 100644 --- a/crypto/Makefile.objs +++ b/crypto/Makefile.objs @@ -19,13 +19,10 @@ crypto-obj-y += tlscredspsk.o crypto-obj-y += tlscredsx509.o crypto-obj-y += tlssession.o crypto-obj-y += secret.o -crypto-rng-obj-$(CONFIG_GCRYPT) += random-gcrypt.o -crypto-rng-obj-$(if $(CONFIG_GCRYPT),n,$(CONFIG_GNUTLS)) += random-gnutls.o -crypto-rng-obj-$(if $(CONFIG_GCRYPT),n,$(if $(CONFIG_GNUTLS),n,y)) += random-platform.o -crypto-obj-y += $(crypto-rng-obj-y) crypto-obj-y += pbkdf.o crypto-obj-$(CONFIG_NETTLE) += pbkdf-nettle.o crypto-obj-$(if $(CONFIG_NETTLE),n,$(CONFIG_GCRYPT)) += pbkdf-gcrypt.o +crypto-obj-$(if $(CONFIG_NETTLE),n,$(if $(CONFIG_GCRYPT),n,y)) += pbkdf-stub.o crypto-obj-y += ivgen.o crypto-obj-y += ivgen-essiv.o crypto-obj-y += ivgen-plain.o @@ -36,7 +33,7 @@ crypto-obj-y += block.o crypto-obj-y += block-qcow.o crypto-obj-y += block-luks.o -# Let the userspace emulators avoid linking stuff they won't use. -crypto-user-obj-y = aes.o $(crypto-rng-obj-y) init.o - -stub-obj-y += pbkdf-stub.o +util-obj-$(CONFIG_GCRYPT) += random-gcrypt.o +util-obj-$(if $(CONFIG_GCRYPT),n,$(CONFIG_GNUTLS)) += random-gnutls.o +util-obj-$(if $(CONFIG_GCRYPT),n,$(if $(CONFIG_GNUTLS),n,y)) += random-platform.o +util-obj-y += aes.o init.o diff --git a/crypto/block.c b/crypto/block.c index 325752871c..6f42b32f1e 100644 --- a/crypto/block.c +++ b/crypto/block.c @@ -115,6 +115,42 @@ QCryptoBlock *qcrypto_block_create(QCryptoBlockCreateOptions *options, } +static ssize_t qcrypto_block_headerlen_hdr_init_func(QCryptoBlock *block, + size_t headerlen, void *opaque, Error **errp) +{ + size_t *headerlenp = opaque; + + /* Stash away the payload size */ + *headerlenp = headerlen; + return 0; +} + + +static ssize_t qcrypto_block_headerlen_hdr_write_func(QCryptoBlock *block, + size_t offset, const uint8_t *buf, size_t buflen, + void *opaque, Error **errp) +{ + /* Discard the bytes, we're not actually writing to an image */ + return buflen; +} + + +bool +qcrypto_block_calculate_payload_offset(QCryptoBlockCreateOptions *create_opts, + const char *optprefix, + size_t *len, + Error **errp) +{ + /* Fake LUKS creation in order to determine the payload size */ + g_autoptr(QCryptoBlock) crypto = + qcrypto_block_create(create_opts, optprefix, + qcrypto_block_headerlen_hdr_init_func, + qcrypto_block_headerlen_hdr_write_func, + len, errp); + return crypto != NULL; +} + + QCryptoBlockInfo *qcrypto_block_get_info(QCryptoBlock *block, Error **errp) { diff --git a/crypto/tlscredsx509.c b/crypto/tlscredsx509.c index 01fc304e5d..53a4368f49 100644 --- a/crypto/tlscredsx509.c +++ b/crypto/tlscredsx509.c @@ -380,7 +380,7 @@ qcrypto_tls_creds_load_cert(QCryptoTLSCredsX509 *creds, gnutls_x509_crt_t cert = NULL; g_autofree char *buf = NULL; gsize buflen; - GError *gerr; + GError *gerr = NULL; int ret = -1; int err; diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak index 1f2e0e7fde..36a0e89daa 100644 --- a/default-configs/arm-softmmu.mak +++ b/default-configs/arm-softmmu.mak @@ -30,6 +30,7 @@ CONFIG_Z2=y CONFIG_COLLIE=y CONFIG_ASPEED_SOC=y CONFIG_NETDUINO2=y +CONFIG_NETDUINOPLUS2=y CONFIG_MPS2=y CONFIG_RASPI=y CONFIG_DIGIC=y @@ -40,3 +41,4 @@ CONFIG_FSL_IMX25=y CONFIG_FSL_IMX7=y CONFIG_FSL_IMX6UL=y CONFIG_SEMIHOSTING=y +CONFIG_ALLWINNER_H3=y diff --git a/default-configs/i386-softmmu.mak b/default-configs/i386-softmmu.mak index efadca2a4b..3afd45a351 100644 --- a/default-configs/i386-softmmu.mak +++ b/default-configs/i386-softmmu.mak @@ -20,7 +20,7 @@ #CONFIG_SGA=n #CONFIG_TEST_DEVICES=n #CONFIG_TPM_CRB=n -#CONFIG_TPM_TIS=n +#CONFIG_TPM_TIS_ISA=n #CONFIG_VTD=n # Boards: diff --git a/default-configs/ppc-softmmu.mak b/default-configs/ppc-softmmu.mak index bf86128a0c..61b78b844d 100644 --- a/default-configs/ppc-softmmu.mak +++ b/default-configs/ppc-softmmu.mak @@ -1,7 +1,6 @@ # Default configuration for ppc-softmmu # For embedded PPCs: -CONFIG_MPC_I2C=y CONFIG_DS1338=y CONFIG_E500=y CONFIG_PPC405=y diff --git a/default-configs/ppc64-softmmu.mak b/default-configs/ppc64-softmmu.mak index cca52665d9..ae0841fa3a 100644 --- a/default-configs/ppc64-softmmu.mak +++ b/default-configs/ppc64-softmmu.mak @@ -8,3 +8,4 @@ CONFIG_POWERNV=y # For pSeries CONFIG_PSERIES=y +CONFIG_NVDIMM=y diff --git a/default-configs/rx-softmmu.mak b/default-configs/rx-softmmu.mak new file mode 100644 index 0000000000..7c4eb2c1a0 --- /dev/null +++ b/default-configs/rx-softmmu.mak @@ -0,0 +1,2 @@ +# Default configuration for rx-softmmu + diff --git a/device-hotplug.c b/device-hotplug.c deleted file mode 100644 index f01d53774b..0000000000 --- a/device-hotplug.c +++ /dev/null @@ -1,91 +0,0 @@ -/* - * QEMU device hotplug helpers - * - * Copyright (c) 2004 Fabrice Bellard - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu/osdep.h" -#include "hw/boards.h" -#include "sysemu/block-backend.h" -#include "sysemu/blockdev.h" -#include "qapi/qmp/qdict.h" -#include "qapi/error.h" -#include "qemu/config-file.h" -#include "qemu/option.h" -#include "sysemu/sysemu.h" -#include "monitor/monitor.h" -#include "block/block_int.h" - -static DriveInfo *add_init_drive(const char *optstr) -{ - Error *err = NULL; - DriveInfo *dinfo; - QemuOpts *opts; - MachineClass *mc; - - opts = drive_def(optstr); - if (!opts) - return NULL; - - mc = MACHINE_GET_CLASS(current_machine); - dinfo = drive_new(opts, mc->block_default_type, &err); - if (err) { - error_report_err(err); - qemu_opts_del(opts); - return NULL; - } - - return dinfo; -} - -void hmp_drive_add(Monitor *mon, const QDict *qdict) -{ - DriveInfo *dinfo = NULL; - const char *opts = qdict_get_str(qdict, "opts"); - bool node = qdict_get_try_bool(qdict, "node", false); - - if (node) { - hmp_drive_add_node(mon, opts); - return; - } - - dinfo = add_init_drive(opts); - if (!dinfo) { - goto err; - } - - switch (dinfo->type) { - case IF_NONE: - monitor_printf(mon, "OK\n"); - break; - default: - monitor_printf(mon, "Can't hot-add drive to type %d\n", dinfo->type); - goto err; - } - return; - -err: - if (dinfo) { - BlockBackend *blk = blk_by_legacy_dinfo(dinfo); - monitor_remove_blk(blk); - blk_unref(blk); - } -} diff --git a/device_tree.c b/device_tree.c index f8b46b3c73..bba6cc2164 100644 --- a/device_tree.c +++ b/device_tree.c @@ -530,7 +530,12 @@ void qemu_fdt_dumpdtb(void *fdt, int size) if (dumpdtb) { /* Dump the dtb to a file and quit */ - exit(g_file_set_contents(dumpdtb, fdt, size, NULL) ? 0 : 1); + if (g_file_set_contents(dumpdtb, fdt, size, NULL)) { + info_report("dtb dumped to %s. Exiting.", dumpdtb); + exit(0); + } + error_report("%s: Failed dumping dtb to %s", __func__, dumpdtb); + exit(1); } } diff --git a/disas/libvixl/Makefile.objs b/disas/libvixl/Makefile.objs index 27183b7c20..99a637f6a0 100644 --- a/disas/libvixl/Makefile.objs +++ b/disas/libvixl/Makefile.objs @@ -1,14 +1,5 @@ -libvixl_OBJS = vixl/utils.o \ +common-obj-$(CONFIG_ARM_A64_DIS) = vixl/utils.o \ vixl/compiler-intrinsics.o \ vixl/a64/instructions-a64.o \ vixl/a64/decoder-a64.o \ vixl/a64/disasm-a64.o - -# The -Wno-sign-compare is needed only for gcc 4.6, which complains about -# some signed-unsigned equality comparisons which later gcc versions do not. -$(addprefix $(obj)/,$(libvixl_OBJS)): QEMU_CXXFLAGS := -I$(SRC_PATH)/disas/libvixl $(QEMU_CXXFLAGS) -Wno-sign-compare -# Ensure that C99 macros are defined regardless of the inclusion order of -# headers in vixl. This is required at least on NetBSD. -$(addprefix $(obj)/,$(libvixl_OBJS)): QEMU_CXXFLAGS += -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS - -common-obj-$(CONFIG_ARM_A64_DIS) += $(libvixl_OBJS) diff --git a/disas/mips.c b/disas/mips.c index dfefe5e589..b9a5204304 100644 --- a/disas/mips.c +++ b/disas/mips.c @@ -1409,6 +1409,16 @@ const struct mips_opcode mips_builtin_opcodes[] = {"dvp", "t", 0x41600024, 0xffe0ffff, TRAP|WR_t, 0, I32R6}, {"evp", "", 0x41600004, 0xffffffff, TRAP, 0, I32R6}, {"evp", "t", 0x41600004, 0xffe0ffff, TRAP|WR_t, 0, I32R6}, +{"ginvi", "v", 0x7c00003d, 0xfc1ffcff, TRAP | INSN_TLB, 0, I32R6}, +{"ginvt", "v", 0x7c0000bd, 0xfc1ffcff, TRAP | INSN_TLB, 0, I32R6}, +{"crc32b", "t,v,t", 0x7c00000f, 0xfc00ff3f, WR_d | RD_s | RD_t, 0, I32R6}, +{"crc32h", "t,v,t", 0x7c00004f, 0xfc00ff3f, WR_d | RD_s | RD_t, 0, I32R6}, +{"crc32w", "t,v,t", 0x7c00008f, 0xfc00ff3f, WR_d | RD_s | RD_t, 0, I32R6}, +{"crc32d", "t,v,t", 0x7c0000cf, 0xfc00ff3f, WR_d | RD_s | RD_t, 0, I64R6}, +{"crc32cb", "t,v,t", 0x7c00010f, 0xfc00ff3f, WR_d | RD_s | RD_t, 0, I32R6}, +{"crc32ch", "t,v,t", 0x7c00014f, 0xfc00ff3f, WR_d | RD_s | RD_t, 0, I32R6}, +{"crc32cw", "t,v,t", 0x7c00018f, 0xfc00ff3f, WR_d | RD_s | RD_t, 0, I32R6}, +{"crc32cd", "t,v,t", 0x7c0001cf, 0xfc00ff3f, WR_d | RD_s | RD_t, 0, I64R6}, /* MSA */ {"sll.b", "+d,+e,+f", 0x7800000d, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA}, diff --git a/dma-helpers.c b/dma-helpers.c index d3871dc61e..e8a26e81e1 100644 --- a/dma-helpers.c +++ b/dma-helpers.c @@ -28,8 +28,8 @@ int dma_memory_set(AddressSpace *as, dma_addr_t addr, uint8_t c, dma_addr_t len) memset(fillbuf, c, FILLBUF_SIZE); while (len > 0) { l = len < FILLBUF_SIZE ? len : FILLBUF_SIZE; - error |= address_space_rw(as, addr, MEMTXATTRS_UNSPECIFIED, - fillbuf, l, true); + error |= address_space_write(as, addr, MEMTXATTRS_UNSPECIFIED, + fillbuf, l); len -= l; addr += l; } diff --git a/docs/COLO-FT.txt b/docs/COLO-FT.txt index ad24680d13..c8e1740935 100644 --- a/docs/COLO-FT.txt +++ b/docs/COLO-FT.txt @@ -145,81 +145,189 @@ The diagram just shows the main qmp command, you can get the detail in test procedure. == Test procedure == -1. Startup qemu -Primary: -# qemu-system-x86_64 -accel kvm -m 2048 -smp 2 -qmp stdio -name primary \ - -device piix3-usb-uhci -vnc :7 \ - -device usb-tablet -netdev tap,id=hn0,vhost=off \ - -device virtio-net-pci,id=net-pci0,netdev=hn0 \ - -drive if=virtio,id=primary-disk0,driver=quorum,read-pattern=fifo,vote-threshold=1,\ - children.0.file.filename=1.raw,\ - children.0.driver=raw -S -Secondary: -# qemu-system-x86_64 -accel kvm -m 2048 -smp 2 -qmp stdio -name secondary \ - -device piix3-usb-uhci -vnc :7 \ - -device usb-tablet -netdev tap,id=hn0,vhost=off \ - -device virtio-net-pci,id=net-pci0,netdev=hn0 \ - -drive if=none,id=secondary-disk0,file.filename=1.raw,driver=raw,node-name=node0 \ - -drive if=virtio,id=active-disk0,driver=replication,mode=secondary,\ - file.driver=qcow2,top-id=active-disk0,\ - file.file.filename=/mnt/ramfs/active_disk.img,\ - file.backing.driver=qcow2,\ - file.backing.file.filename=/mnt/ramfs/hidden_disk.img,\ - file.backing.backing=secondary-disk0 \ - -incoming tcp:0:8888 +Note: Here we are running both instances on the same host for testing, +change the IP Addresses if you want to run it on two hosts. Initally +127.0.0.1 is the Primary Host and 127.0.0.2 is the Secondary Host. -2. On Secondary VM's QEMU monitor, issue command +== Startup qemu == +1. Primary: +Note: Initally, $imagefolder/primary.qcow2 needs to be copied to all hosts. +You don't need to change any IP's here, because 0.0.0.0 listens on any +interface. The chardev's with 127.0.0.1 IP's loopback to the local qemu +instance. + +# imagefolder="/mnt/vms/colo-test-primary" + +# qemu-system-x86_64 -enable-kvm -cpu qemu64,+kvmclock -m 512 -smp 1 -qmp stdio \ + -device piix3-usb-uhci -device usb-tablet -name primary \ + -netdev tap,id=hn0,vhost=off,helper=/usr/lib/qemu/qemu-bridge-helper \ + -device rtl8139,id=e0,netdev=hn0 \ + -chardev socket,id=mirror0,host=0.0.0.0,port=9003,server,nowait \ + -chardev socket,id=compare1,host=0.0.0.0,port=9004,server,wait \ + -chardev socket,id=compare0,host=127.0.0.1,port=9001,server,nowait \ + -chardev socket,id=compare0-0,host=127.0.0.1,port=9001 \ + -chardev socket,id=compare_out,host=127.0.0.1,port=9005,server,nowait \ + -chardev socket,id=compare_out0,host=127.0.0.1,port=9005 \ + -object filter-mirror,id=m0,netdev=hn0,queue=tx,outdev=mirror0 \ + -object filter-redirector,netdev=hn0,id=redire0,queue=rx,indev=compare_out \ + -object filter-redirector,netdev=hn0,id=redire1,queue=rx,outdev=compare0 \ + -object iothread,id=iothread1 \ + -object colo-compare,id=comp0,primary_in=compare0-0,secondary_in=compare1,\ +outdev=compare_out0,iothread=iothread1 \ + -drive if=ide,id=colo-disk0,driver=quorum,read-pattern=fifo,vote-threshold=1,\ +children.0.file.filename=$imagefolder/primary.qcow2,children.0.driver=qcow2 -S + +2. Secondary: +Note: Active and hidden images need to be created only once and the +size should be the same as primary.qcow2. Again, you don't need to change +any IP's here, except for the $primary_ip variable. + +# imagefolder="/mnt/vms/colo-test-secondary" +# primary_ip=127.0.0.1 + +# qemu-img create -f qcow2 $imagefolder/secondary-active.qcow2 10G + +# qemu-img create -f qcow2 $imagefolder/secondary-hidden.qcow2 10G + +# qemu-system-x86_64 -enable-kvm -cpu qemu64,+kvmclock -m 512 -smp 1 -qmp stdio \ + -device piix3-usb-uhci -device usb-tablet -name secondary \ + -netdev tap,id=hn0,vhost=off,helper=/usr/lib/qemu/qemu-bridge-helper \ + -device rtl8139,id=e0,netdev=hn0 \ + -chardev socket,id=red0,host=$primary_ip,port=9003,reconnect=1 \ + -chardev socket,id=red1,host=$primary_ip,port=9004,reconnect=1 \ + -object filter-redirector,id=f1,netdev=hn0,queue=tx,indev=red0 \ + -object filter-redirector,id=f2,netdev=hn0,queue=rx,outdev=red1 \ + -object filter-rewriter,id=rew0,netdev=hn0,queue=all \ + -drive if=none,id=parent0,file.filename=$imagefolder/primary.qcow2,driver=qcow2 \ + -drive if=none,id=childs0,driver=replication,mode=secondary,file.driver=qcow2,\ +top-id=colo-disk0,file.file.filename=$imagefolder/secondary-active.qcow2,\ +file.backing.driver=qcow2,file.backing.file.filename=$imagefolder/secondary-hidden.qcow2,\ +file.backing.backing=parent0 \ + -drive if=ide,id=colo-disk0,driver=quorum,read-pattern=fifo,vote-threshold=1,\ +children.0=childs0 \ + -incoming tcp:0.0.0.0:9998 + + +3. On Secondary VM's QEMU monitor, issue command {'execute':'qmp_capabilities'} -{ 'execute': 'nbd-server-start', - 'arguments': {'addr': {'type': 'inet', 'data': {'host': 'xx.xx.xx.xx', 'port': '8889'} } } -} -{'execute': 'nbd-server-add', 'arguments': {'device': 'secondary-disk0', 'writable': true } } +{'execute': 'nbd-server-start', 'arguments': {'addr': {'type': 'inet', 'data': {'host': '0.0.0.0', 'port': '9999'} } } } +{'execute': 'nbd-server-add', 'arguments': {'device': 'parent0', 'writable': true } } Note: a. The qmp command nbd-server-start and nbd-server-add must be run before running the qmp command migrate on primary QEMU b. Active disk, hidden disk and nbd target's length should be the same. - c. It is better to put active disk and hidden disk in ramdisk. + c. It is better to put active disk and hidden disk in ramdisk. They + will be merged into the parent disk on failover. -3. On Primary VM's QEMU monitor, issue command: +4. On Primary VM's QEMU monitor, issue command: {'execute':'qmp_capabilities'} -{ 'execute': 'human-monitor-command', - 'arguments': {'command-line': 'drive_add -n buddy driver=replication,mode=primary,file.driver=nbd,file.host=xx.xx.xx.xx,file.port=8889,file.export=secondary-disk0,node-name=nbd_client0'}} -{ 'execute':'x-blockdev-change', 'arguments':{'parent': 'primary-disk0', 'node': 'nbd_client0' } } -{ 'execute': 'migrate-set-capabilities', - 'arguments': {'capabilities': [ {'capability': 'x-colo', 'state': true } ] } } -{ 'execute': 'migrate', 'arguments': {'uri': 'tcp:xx.xx.xx.xx:8888' } } +{'execute': 'human-monitor-command', 'arguments': {'command-line': 'drive_add -n buddy driver=replication,mode=primary,file.driver=nbd,file.host=127.0.0.2,file.port=9999,file.export=parent0,node-name=replication0'}} +{'execute': 'x-blockdev-change', 'arguments':{'parent': 'colo-disk0', 'node': 'replication0' } } +{'execute': 'migrate-set-capabilities', 'arguments': {'capabilities': [ {'capability': 'x-colo', 'state': true } ] } } +{'execute': 'migrate', 'arguments': {'uri': 'tcp:127.0.0.2:9998' } } Note: a. There should be only one NBD Client for each primary disk. - b. xx.xx.xx.xx is the secondary physical machine's hostname or IP - c. The qmp command line must be run after running qmp command line in + b. The qmp command line must be run after running qmp command line in secondary qemu. -4. After the above steps, you will see, whenever you make changes to PVM, SVM will be synced. +5. After the above steps, you will see, whenever you make changes to PVM, SVM will be synced. You can issue command '{ "execute": "migrate-set-parameters" , "arguments":{ "x-checkpoint-delay": 2000 } }' -to change the checkpoint period time +to change the idle checkpoint period time -5. Failover test -You can kill Primary VM and run 'x_colo_lost_heartbeat' in Secondary VM's -monitor at the same time, then SVM will failover and client will not detect this -change. +6. Failover test +You can kill one of the VMs and Failover on the surviving VM: -Before issuing '{ "execute": "x-colo-lost-heartbeat" }' command, we have to -issue block related command to stop block replication. -Primary: - Remove the nbd child from the quorum: - { 'execute': 'x-blockdev-change', 'arguments': {'parent': 'colo-disk0', 'child': 'children.1'}} - { 'execute': 'human-monitor-command','arguments': {'command-line': 'drive_del blk-buddy0'}} - Note: there is no qmp command to remove the blockdev now +If you killed the Secondary, then follow "Primary Failover". After that, +if you want to resume the replication, follow "Primary resume replication" -Secondary: - The primary host is down, so we should do the following thing: - { 'execute': 'nbd-server-stop' } +If you killed the Primary, then follow "Secondary Failover". After that, +if you want to resume the replication, follow "Secondary resume replication" + +== Primary Failover == +The Secondary died, resume on the Primary + +{'execute': 'x-blockdev-change', 'arguments':{ 'parent': 'colo-disk0', 'child': 'children.1'} } +{'execute': 'human-monitor-command', 'arguments':{ 'command-line': 'drive_del replication0' } } +{'execute': 'object-del', 'arguments':{ 'id': 'comp0' } } +{'execute': 'object-del', 'arguments':{ 'id': 'iothread1' } } +{'execute': 'object-del', 'arguments':{ 'id': 'm0' } } +{'execute': 'object-del', 'arguments':{ 'id': 'redire0' } } +{'execute': 'object-del', 'arguments':{ 'id': 'redire1' } } +{'execute': 'x-colo-lost-heartbeat' } + +== Secondary Failover == +The Primary died, resume on the Secondary and prepare to become the new Primary + +{'execute': 'nbd-server-stop'} +{'execute': 'x-colo-lost-heartbeat'} + +{'execute': 'object-del', 'arguments':{ 'id': 'f2' } } +{'execute': 'object-del', 'arguments':{ 'id': 'f1' } } +{'execute': 'chardev-remove', 'arguments':{ 'id': 'red1' } } +{'execute': 'chardev-remove', 'arguments':{ 'id': 'red0' } } + +{'execute': 'chardev-add', 'arguments':{ 'id': 'mirror0', 'backend': {'type': 'socket', 'data': {'addr': { 'type': 'inet', 'data': { 'host': '0.0.0.0', 'port': '9003' } }, 'server': true } } } } +{'execute': 'chardev-add', 'arguments':{ 'id': 'compare1', 'backend': {'type': 'socket', 'data': {'addr': { 'type': 'inet', 'data': { 'host': '0.0.0.0', 'port': '9004' } }, 'server': true } } } } +{'execute': 'chardev-add', 'arguments':{ 'id': 'compare0', 'backend': {'type': 'socket', 'data': {'addr': { 'type': 'inet', 'data': { 'host': '127.0.0.1', 'port': '9001' } }, 'server': true } } } } +{'execute': 'chardev-add', 'arguments':{ 'id': 'compare0-0', 'backend': {'type': 'socket', 'data': {'addr': { 'type': 'inet', 'data': { 'host': '127.0.0.1', 'port': '9001' } }, 'server': false } } } } +{'execute': 'chardev-add', 'arguments':{ 'id': 'compare_out', 'backend': {'type': 'socket', 'data': {'addr': { 'type': 'inet', 'data': { 'host': '127.0.0.1', 'port': '9005' } }, 'server': true } } } } +{'execute': 'chardev-add', 'arguments':{ 'id': 'compare_out0', 'backend': {'type': 'socket', 'data': {'addr': { 'type': 'inet', 'data': { 'host': '127.0.0.1', 'port': '9005' } }, 'server': false } } } } + +== Primary resume replication == +Resume replication after new Secondary is up. + +Start the new Secondary (Steps 2 and 3 above), then on the Primary: +{'execute': 'drive-mirror', 'arguments':{ 'device': 'colo-disk0', 'job-id': 'resync', 'target': 'nbd://127.0.0.2:9999/parent0', 'mode': 'existing', 'format': 'raw', 'sync': 'full'} } + +Wait until disk is synced, then: +{'execute': 'stop'} +{'execute': 'block-job-cancel', 'arguments':{ 'device': 'resync'} } + +{'execute': 'human-monitor-command', 'arguments':{ 'command-line': 'drive_add -n buddy driver=replication,mode=primary,file.driver=nbd,file.host=127.0.0.2,file.port=9999,file.export=parent0,node-name=replication0'}} +{'execute': 'x-blockdev-change', 'arguments':{ 'parent': 'colo-disk0', 'node': 'replication0' } } + +{'execute': 'object-add', 'arguments':{ 'qom-type': 'filter-mirror', 'id': 'm0', 'props': { 'netdev': 'hn0', 'queue': 'tx', 'outdev': 'mirror0' } } } +{'execute': 'object-add', 'arguments':{ 'qom-type': 'filter-redirector', 'id': 'redire0', 'props': { 'netdev': 'hn0', 'queue': 'rx', 'indev': 'compare_out' } } } +{'execute': 'object-add', 'arguments':{ 'qom-type': 'filter-redirector', 'id': 'redire1', 'props': { 'netdev': 'hn0', 'queue': 'rx', 'outdev': 'compare0' } } } +{'execute': 'object-add', 'arguments':{ 'qom-type': 'iothread', 'id': 'iothread1' } } +{'execute': 'object-add', 'arguments':{ 'qom-type': 'colo-compare', 'id': 'comp0', 'props': { 'primary_in': 'compare0-0', 'secondary_in': 'compare1', 'outdev': 'compare_out0', 'iothread': 'iothread1' } } } + +{'execute': 'migrate-set-capabilities', 'arguments':{ 'capabilities': [ {'capability': 'x-colo', 'state': true } ] } } +{'execute': 'migrate', 'arguments':{ 'uri': 'tcp:127.0.0.2:9998' } } + +Note: +If this Primary previously was a Secondary, then we need to insert the +filters before the filter-rewriter by using the +"'insert': 'before', 'position': 'id=rew0'" Options. See below. + +== Secondary resume replication == +Become Primary and resume replication after new Secondary is up. Note +that now 127.0.0.1 is the Secondary and 127.0.0.2 is the Primary. + +Start the new Secondary (Steps 2 and 3 above, but with primary_ip=127.0.0.2), +then on the old Secondary: +{'execute': 'drive-mirror', 'arguments':{ 'device': 'colo-disk0', 'job-id': 'resync', 'target': 'nbd://127.0.0.1:9999/parent0', 'mode': 'existing', 'format': 'raw', 'sync': 'full'} } + +Wait until disk is synced, then: +{'execute': 'stop'} +{'execute': 'block-job-cancel', 'arguments':{ 'device': 'resync' } } + +{'execute': 'human-monitor-command', 'arguments':{ 'command-line': 'drive_add -n buddy driver=replication,mode=primary,file.driver=nbd,file.host=127.0.0.1,file.port=9999,file.export=parent0,node-name=replication0'}} +{'execute': 'x-blockdev-change', 'arguments':{ 'parent': 'colo-disk0', 'node': 'replication0' } } + +{'execute': 'object-add', 'arguments':{ 'qom-type': 'filter-mirror', 'id': 'm0', 'props': { 'insert': 'before', 'position': 'id=rew0', 'netdev': 'hn0', 'queue': 'tx', 'outdev': 'mirror0' } } } +{'execute': 'object-add', 'arguments':{ 'qom-type': 'filter-redirector', 'id': 'redire0', 'props': { 'insert': 'before', 'position': 'id=rew0', 'netdev': 'hn0', 'queue': 'rx', 'indev': 'compare_out' } } } +{'execute': 'object-add', 'arguments':{ 'qom-type': 'filter-redirector', 'id': 'redire1', 'props': { 'insert': 'before', 'position': 'id=rew0', 'netdev': 'hn0', 'queue': 'rx', 'outdev': 'compare0' } } } +{'execute': 'object-add', 'arguments':{ 'qom-type': 'iothread', 'id': 'iothread1' } } +{'execute': 'object-add', 'arguments':{ 'qom-type': 'colo-compare', 'id': 'comp0', 'props': { 'primary_in': 'compare0-0', 'secondary_in': 'compare1', 'outdev': 'compare_out0', 'iothread': 'iothread1' } } } + +{'execute': 'migrate-set-capabilities', 'arguments':{ 'capabilities': [ {'capability': 'x-colo', 'state': true } ] } } +{'execute': 'migrate', 'arguments':{ 'uri': 'tcp:127.0.0.1:9998' } } == TODO == -1. Support continuous VM replication. -2. Support shared storage. -3. Develop the heartbeat part. -4. Reduce checkpoint VM’s downtime while doing checkpoint. +1. Support shared storage. +2. Develop the heartbeat part. +3. Reduce checkpoint VM’s downtime while doing checkpoint. diff --git a/docs/block-replication.txt b/docs/block-replication.txt index 6bde6737fb..108e9166a8 100644 --- a/docs/block-replication.txt +++ b/docs/block-replication.txt @@ -65,12 +65,12 @@ blocks that are already in QEMU. ^ || .---------- | || | Secondary 1 Quorum || '---------- - / \ || - / \ || - Primary 2 filter - disk ^ virtio-blk - | ^ - 3 NBD -------> 3 NBD | + / \ || virtio-blk + / \ || ^ + Primary 2 filter | + disk ^ 7 Quorum + | / + 3 NBD -------> 3 NBD / client || server 2 filter || ^ ^ --------. || | | @@ -106,6 +106,10 @@ any state that would otherwise be lost by the speculative write-through of the NBD server into the secondary disk. So before block replication, the primary disk and secondary disk should contain the same data. +7) The secondary also has a quorum node, so after secondary failover it +can become the new primary and continue replication. + + == Failure Handling == There are 7 internal errors when block replication is running: 1. I/O error on primary disk @@ -171,16 +175,18 @@ Primary: leading whitespace. 5. The qmp command line must be run after running qmp command line in secondary qemu. - 6. After failover we need remove children.1 (replication driver). + 6. After primary failover we need remove children.1 (replication driver). Secondary: -drive if=none,driver=raw,file.filename=1.raw,id=colo1 \ - -drive if=xxx,id=topxxx,driver=replication,mode=secondary,top-id=topxxx\ + -drive if=none,id=childs1,driver=replication,mode=secondary,top-id=childs1 file.file.filename=active_disk.qcow2,\ file.driver=qcow2,\ file.backing.file.filename=hidden_disk.qcow2,\ file.backing.driver=qcow2,\ file.backing.backing=colo1 + -drive if=xxx,driver=quorum,read-pattern=fifo,id=top-disk1,\ + vote-threshold=1,children.0=childs1 Then run qmp command in secondary qemu: { 'execute': 'nbd-server-start', @@ -234,6 +240,8 @@ Secondary: The primary host is down, so we should do the following thing: { 'execute': 'nbd-server-stop' } +Promote Secondary to Primary: + see COLO-FT.txt + TODO: -1. Continuous block replication -2. Shared disk +1. Shared disk diff --git a/docs/can.txt b/docs/can.txt index 9fa6ed51c8..11ed8f2d68 100644 --- a/docs/can.txt +++ b/docs/can.txt @@ -13,7 +13,7 @@ controller is implemented. The PCI addon card hardware has been selected as the first CAN interface to implement because such device can be easily connected -to systems with different CPU architectures (x86, PowerPC, ARM, etc.). +to systems with different CPU architectures (x86, PowerPC, Arm, etc.). The project has been initially started in frame of RTEMS GSoC 2013 slot by Jin Yang under our mentoring The initial idea was to provide generic diff --git a/docs/conf.py b/docs/conf.py index b7edb0666b..d6e173ef77 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -28,6 +28,17 @@ import os import sys +import sphinx +from sphinx.errors import ConfigError + +# Make Sphinx fail cleanly if using an old Python, rather than obscurely +# failing because some code in one of our extensions doesn't work there. +# In newer versions of Sphinx this will display nicely; in older versions +# Sphinx will also produce a Python backtrace but at least the information +# gets printed... +if sys.version_info < (3,5): + raise ConfigError( + "QEMU requires a Sphinx that uses Python 3.5 or better\n") # The per-manual conf.py will set qemu_docdir for a single-manual build; # otherwise set it here if this is an entire-manual-set build. @@ -48,13 +59,15 @@ sys.path.insert(0, os.path.join(qemu_docdir, "sphinx")) # If your documentation needs a minimal Sphinx version, state it here. # -# 1.3 is where the 'alabaster' theme was shipped with Sphinx. -needs_sphinx = '1.3' +# Sphinx 1.5 and earlier can't build our docs because they are too +# picky about the syntax of the argument to the option:: directive +# (see Sphinx bugs #646, #3366). +needs_sphinx = '1.6' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. -extensions = ['qmp_lexer'] +extensions = ['kerneldoc', 'qmp_lexer', 'hxtool'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] @@ -70,7 +83,7 @@ master_doc = 'index' # General information about the project. project = u'QEMU' -copyright = u'2019, The QEMU Project Developers' +copyright = u'2020, The QEMU Project Developers' author = u'The QEMU Project Developers' # The version info for the project you're documenting, acts as replacement for @@ -122,6 +135,12 @@ suppress_warnings = ["ref.option"] # style document building; our Makefile always sets the variable. confdir = os.getenv('CONFDIR', "/etc/qemu") rst_epilog = ".. |CONFDIR| replace:: ``" + confdir + "``\n" +# We slurp in the defs.rst.inc and literally include it into rst_epilog, +# because Sphinx's include:: directive doesn't work with absolute paths +# and there isn't any one single relative path that will work for all +# documents and for both via-make and direct sphinx-build invocation. +with open(os.path.join(qemu_docdir, 'defs.rst.inc')) as f: + rst_epilog += f.read() # -- Options for HTML output ---------------------------------------------- @@ -216,3 +235,9 @@ texinfo_documents = [ +# We use paths starting from qemu_docdir here so that you can run +# sphinx-build from anywhere and the kerneldoc extension can still +# find everything. +kerneldoc_bin = os.path.join(qemu_docdir, '../scripts/kernel-doc') +kerneldoc_srctree = os.path.join(qemu_docdir, '..') +hxtool_srctree = os.path.join(qemu_docdir, '..') diff --git a/docs/defs.rst.inc b/docs/defs.rst.inc new file mode 100644 index 0000000000..48d05aaf33 --- /dev/null +++ b/docs/defs.rst.inc @@ -0,0 +1,15 @@ +.. + Generally useful rST substitution definitions. This is included for + all rST files as part of the epilogue by docs/conf.py. conf.py + also defines some dynamically generated substitutions like CONFDIR. + + Note that |qemu_system| and |qemu_system_x86| are intended to be + used inside a parsed-literal block: the definition must not include + extra literal formatting with ``..``: this works in the HTML output + but the manpages will end up misrendered with following normal text + incorrectly in boldface. + +.. |qemu_system| replace:: qemu-system-x86_64 +.. |qemu_system_x86| replace:: qemu_system-x86_64 +.. |I2C| replace:: I\ :sup:`2`\ C +.. |I2S| replace:: I\ :sup:`2`\ S diff --git a/docs/devel/atomics.rst b/docs/devel/atomics.rst new file mode 100644 index 0000000000..445c3b3503 --- /dev/null +++ b/docs/devel/atomics.rst @@ -0,0 +1,507 @@ +========================= +Atomic operations in QEMU +========================= + +CPUs perform independent memory operations effectively in random order. +but this can be a problem for CPU-CPU interaction (including interactions +between QEMU and the guest). Multi-threaded programs use various tools +to instruct the compiler and the CPU to restrict the order to something +that is consistent with the expectations of the programmer. + +The most basic tool is locking. Mutexes, condition variables and +semaphores are used in QEMU, and should be the default approach to +synchronization. Anything else is considerably harder, but it's +also justified more often than one would like; +the most performance-critical parts of QEMU in particular require +a very low level approach to concurrency, involving memory barriers +and atomic operations. The semantics of concurrent memory accesses are governed +by the C11 memory model. + +QEMU provides a header, ``qemu/atomic.h``, which wraps C11 atomics to +provide better portability and a less verbose syntax. ``qemu/atomic.h`` +provides macros that fall in three camps: + +- compiler barriers: ``barrier()``; + +- weak atomic access and manual memory barriers: ``atomic_read()``, + ``atomic_set()``, ``smp_rmb()``, ``smp_wmb()``, ``smp_mb()``, ``smp_mb_acquire()``, + ``smp_mb_release()``, ``smp_read_barrier_depends()``; + +- sequentially consistent atomic access: everything else. + +In general, use of ``qemu/atomic.h`` should be wrapped with more easily +used data structures (e.g. the lock-free singly-linked list operations +``QSLIST_INSERT_HEAD_ATOMIC`` and ``QSLIST_MOVE_ATOMIC``) or synchronization +primitives (such as RCU, ``QemuEvent`` or ``QemuLockCnt``). Bare use of +atomic operations and memory barriers should be limited to inter-thread +checking of flags and documented thoroughly. + + + +Compiler memory barrier +======================= + +``barrier()`` prevents the compiler from moving the memory accesses on +either side of it to the other side. The compiler barrier has no direct +effect on the CPU, which may then reorder things however it wishes. + +``barrier()`` is mostly used within ``qemu/atomic.h`` itself. On some +architectures, CPU guarantees are strong enough that blocking compiler +optimizations already ensures the correct order of execution. In this +case, ``qemu/atomic.h`` will reduce stronger memory barriers to simple +compiler barriers. + +Still, ``barrier()`` can be useful when writing code that can be interrupted +by signal handlers. + + +Sequentially consistent atomic access +===================================== + +Most of the operations in the ``qemu/atomic.h`` header ensure *sequential +consistency*, where "the result of any execution is the same as if the +operations of all the processors were executed in some sequential order, +and the operations of each individual processor appear in this sequence +in the order specified by its program". + +``qemu/atomic.h`` provides the following set of atomic read-modify-write +operations:: + + void atomic_inc(ptr) + void atomic_dec(ptr) + void atomic_add(ptr, val) + void atomic_sub(ptr, val) + void atomic_and(ptr, val) + void atomic_or(ptr, val) + + typeof(*ptr) atomic_fetch_inc(ptr) + typeof(*ptr) atomic_fetch_dec(ptr) + typeof(*ptr) atomic_fetch_add(ptr, val) + typeof(*ptr) atomic_fetch_sub(ptr, val) + typeof(*ptr) atomic_fetch_and(ptr, val) + typeof(*ptr) atomic_fetch_or(ptr, val) + typeof(*ptr) atomic_fetch_xor(ptr, val) + typeof(*ptr) atomic_fetch_inc_nonzero(ptr) + typeof(*ptr) atomic_xchg(ptr, val) + typeof(*ptr) atomic_cmpxchg(ptr, old, new) + +all of which return the old value of ``*ptr``. These operations are +polymorphic; they operate on any type that is as wide as a pointer or +smaller. + +Similar operations return the new value of ``*ptr``:: + + typeof(*ptr) atomic_inc_fetch(ptr) + typeof(*ptr) atomic_dec_fetch(ptr) + typeof(*ptr) atomic_add_fetch(ptr, val) + typeof(*ptr) atomic_sub_fetch(ptr, val) + typeof(*ptr) atomic_and_fetch(ptr, val) + typeof(*ptr) atomic_or_fetch(ptr, val) + typeof(*ptr) atomic_xor_fetch(ptr, val) + +``qemu/atomic.h`` also provides loads and stores that cannot be reordered +with each other:: + + typeof(*ptr) atomic_mb_read(ptr) + void atomic_mb_set(ptr, val) + +However these do not provide sequential consistency and, in particular, +they do not participate in the total ordering enforced by +sequentially-consistent operations. For this reason they are deprecated. +They should instead be replaced with any of the following (ordered from +easiest to hardest): + +- accesses inside a mutex or spinlock + +- lightweight synchronization primitives such as ``QemuEvent`` + +- RCU operations (``atomic_rcu_read``, ``atomic_rcu_set``) when publishing + or accessing a new version of a data structure + +- other atomic accesses: ``atomic_read`` and ``atomic_load_acquire`` for + loads, ``atomic_set`` and ``atomic_store_release`` for stores, ``smp_mb`` + to forbid reordering subsequent loads before a store. + + +Weak atomic access and manual memory barriers +============================================= + +Compared to sequentially consistent atomic access, programming with +weaker consistency models can be considerably more complicated. +The only guarantees that you can rely upon in this case are: + +- atomic accesses will not cause data races (and hence undefined behavior); + ordinary accesses instead cause data races if they are concurrent with + other accesses of which at least one is a write. In order to ensure this, + the compiler will not optimize accesses out of existence, create unsolicited + accesses, or perform other similar optimzations. + +- acquire operations will appear to happen, with respect to the other + components of the system, before all the LOAD or STORE operations + specified afterwards. + +- release operations will appear to happen, with respect to the other + components of the system, after all the LOAD or STORE operations + specified before. + +- release operations will *synchronize with* acquire operations; + see :ref:`acqrel` for a detailed explanation. + +When using this model, variables are accessed with: + +- ``atomic_read()`` and ``atomic_set()``; these prevent the compiler from + optimizing accesses out of existence and creating unsolicited + accesses, but do not otherwise impose any ordering on loads and + stores: both the compiler and the processor are free to reorder + them. + +- ``atomic_load_acquire()``, which guarantees the LOAD to appear to + happen, with respect to the other components of the system, + before all the LOAD or STORE operations specified afterwards. + Operations coming before ``atomic_load_acquire()`` can still be + reordered after it. + +- ``atomic_store_release()``, which guarantees the STORE to appear to + happen, with respect to the other components of the system, + after all the LOAD or STORE operations specified before. + Operations coming after ``atomic_store_release()`` can still be + reordered before it. + +Restrictions to the ordering of accesses can also be specified +using the memory barrier macros: ``smp_rmb()``, ``smp_wmb()``, ``smp_mb()``, +``smp_mb_acquire()``, ``smp_mb_release()``, ``smp_read_barrier_depends()``. + +Memory barriers control the order of references to shared memory. +They come in six kinds: + +- ``smp_rmb()`` guarantees that all the LOAD operations specified before + the barrier will appear to happen before all the LOAD operations + specified after the barrier with respect to the other components of + the system. + + In other words, ``smp_rmb()`` puts a partial ordering on loads, but is not + required to have any effect on stores. + +- ``smp_wmb()`` guarantees that all the STORE operations specified before + the barrier will appear to happen before all the STORE operations + specified after the barrier with respect to the other components of + the system. + + In other words, ``smp_wmb()`` puts a partial ordering on stores, but is not + required to have any effect on loads. + +- ``smp_mb_acquire()`` guarantees that all the LOAD operations specified before + the barrier will appear to happen before all the LOAD or STORE operations + specified after the barrier with respect to the other components of + the system. + +- ``smp_mb_release()`` guarantees that all the STORE operations specified *after* + the barrier will appear to happen after all the LOAD or STORE operations + specified *before* the barrier with respect to the other components of + the system. + +- ``smp_mb()`` guarantees that all the LOAD and STORE operations specified + before the barrier will appear to happen before all the LOAD and + STORE operations specified after the barrier with respect to the other + components of the system. + + ``smp_mb()`` puts a partial ordering on both loads and stores. It is + stronger than both a read and a write memory barrier; it implies both + ``smp_mb_acquire()`` and ``smp_mb_release()``, but it also prevents STOREs + coming before the barrier from overtaking LOADs coming after the + barrier and vice versa. + +- ``smp_read_barrier_depends()`` is a weaker kind of read barrier. On + most processors, whenever two loads are performed such that the + second depends on the result of the first (e.g., the first load + retrieves the address to which the second load will be directed), + the processor will guarantee that the first LOAD will appear to happen + before the second with respect to the other components of the system. + However, this is not always true---for example, it was not true on + Alpha processors. Whenever this kind of access happens to shared + memory (that is not protected by a lock), a read barrier is needed, + and ``smp_read_barrier_depends()`` can be used instead of ``smp_rmb()``. + + Note that the first load really has to have a _data_ dependency and not + a control dependency. If the address for the second load is dependent + on the first load, but the dependency is through a conditional rather + than actually loading the address itself, then it's a _control_ + dependency and a full read barrier or better is required. + + +Memory barriers and ``atomic_load_acquire``/``atomic_store_release`` are +mostly used when a data structure has one thread that is always a writer +and one thread that is always a reader: + + +----------------------------------+----------------------------------+ + | thread 1 | thread 2 | + +==================================+==================================+ + | :: | :: | + | | | + | atomic_store_release(&a, x); | y = atomic_load_acquire(&b); | + | atomic_store_release(&b, y); | x = atomic_load_acquire(&a); | + +----------------------------------+----------------------------------+ + +In this case, correctness is easy to check for using the "pairing" +trick that is explained below. + +Sometimes, a thread is accessing many variables that are otherwise +unrelated to each other (for example because, apart from the current +thread, exactly one other thread will read or write each of these +variables). In this case, it is possible to "hoist" the barriers +outside a loop. For example: + + +------------------------------------------+----------------------------------+ + | before | after | + +==========================================+==================================+ + | :: | :: | + | | | + | n = 0; | n = 0; | + | for (i = 0; i < 10; i++) | for (i = 0; i < 10; i++) | + | n += atomic_load_acquire(&a[i]); | n += atomic_read(&a[i]); | + | | smp_mb_acquire(); | + +------------------------------------------+----------------------------------+ + | :: | :: | + | | | + | | smp_mb_release(); | + | for (i = 0; i < 10; i++) | for (i = 0; i < 10; i++) | + | atomic_store_release(&a[i], false); | atomic_set(&a[i], false); | + +------------------------------------------+----------------------------------+ + +Splitting a loop can also be useful to reduce the number of barriers: + + +------------------------------------------+----------------------------------+ + | before | after | + +==========================================+==================================+ + | :: | :: | + | | | + | n = 0; | smp_mb_release(); | + | for (i = 0; i < 10; i++) { | for (i = 0; i < 10; i++) | + | atomic_store_release(&a[i], false); | atomic_set(&a[i], false); | + | smp_mb(); | smb_mb(); | + | n += atomic_read(&b[i]); | n = 0; | + | } | for (i = 0; i < 10; i++) | + | | n += atomic_read(&b[i]); | + +------------------------------------------+----------------------------------+ + +In this case, a ``smp_mb_release()`` is also replaced with a (possibly cheaper, and clearer +as well) ``smp_wmb()``: + + +------------------------------------------+----------------------------------+ + | before | after | + +==========================================+==================================+ + | :: | :: | + | | | + | | smp_mb_release(); | + | for (i = 0; i < 10; i++) { | for (i = 0; i < 10; i++) | + | atomic_store_release(&a[i], false); | atomic_set(&a[i], false); | + | atomic_store_release(&b[i], false); | smb_wmb(); | + | } | for (i = 0; i < 10; i++) | + | | atomic_set(&b[i], false); | + +------------------------------------------+----------------------------------+ + + +.. _acqrel: + +Acquire/release pairing and the *synchronizes-with* relation +------------------------------------------------------------ + +Atomic operations other than ``atomic_set()`` and ``atomic_read()`` have +either *acquire* or *release* semantics [#rmw]_. This has two effects: + +.. [#rmw] Read-modify-write operations can have both---acquire applies to the + read part, and release to the write. + +- within a thread, they are ordered either before subsequent operations + (for acquire) or after previous operations (for release). + +- if a release operation in one thread *synchronizes with* an acquire operation + in another thread, the ordering constraints propagates from the first to the + second thread. That is, everything before the release operation in the + first thread is guaranteed to *happen before* everything after the + acquire operation in the second thread. + +The concept of acquire and release semantics is not exclusive to atomic +operations; almost all higher-level synchronization primitives also have +acquire or release semantics. For example: + +- ``pthread_mutex_lock`` has acquire semantics, ``pthread_mutex_unlock`` has + release semantics and synchronizes with a ``pthread_mutex_lock`` for the + same mutex. + +- ``pthread_cond_signal`` and ``pthread_cond_broadcast`` have release semantics; + ``pthread_cond_wait`` has both release semantics (synchronizing with + ``pthread_mutex_lock``) and acquire semantics (synchronizing with + ``pthread_mutex_unlock`` and signaling of the condition variable). + +- ``pthread_create`` has release semantics and synchronizes with the start + of the new thread; ``pthread_join`` has acquire semantics and synchronizes + with the exiting of the thread. + +- ``qemu_event_set`` has release semantics, ``qemu_event_wait`` has + acquire semantics. + +For example, in the following example there are no atomic accesses, but still +thread 2 is relying on the *synchronizes-with* relation between ``pthread_exit`` +(release) and ``pthread_join`` (acquire): + + +----------------------+-------------------------------+ + | thread 1 | thread 2 | + +======================+===============================+ + | :: | :: | + | | | + | *a = 1; | | + | pthread_exit(a); | pthread_join(thread1, &a); | + | | x = *a; | + +----------------------+-------------------------------+ + +Synchronization between threads basically descends from this pairing of +a release operation and an acquire operation. Therefore, atomic operations +other than ``atomic_set()`` and ``atomic_read()`` will almost always be +paired with another operation of the opposite kind: an acquire operation +will pair with a release operation and vice versa. This rule of thumb is +extremely useful; in the case of QEMU, however, note that the other +operation may actually be in a driver that runs in the guest! + +``smp_read_barrier_depends()``, ``smp_rmb()``, ``smp_mb_acquire()``, +``atomic_load_acquire()`` and ``atomic_rcu_read()`` all count +as acquire operations. ``smp_wmb()``, ``smp_mb_release()``, +``atomic_store_release()`` and ``atomic_rcu_set()`` all count as release +operations. ``smp_mb()`` counts as both acquire and release, therefore +it can pair with any other atomic operation. Here is an example: + + +----------------------+------------------------------+ + | thread 1 | thread 2 | + +======================+==============================+ + | :: | :: | + | | | + | atomic_set(&a, 1); | | + | smp_wmb(); | | + | atomic_set(&b, 2); | x = atomic_read(&b); | + | | smp_rmb(); | + | | y = atomic_read(&a); | + +----------------------+------------------------------+ + +Note that a load-store pair only counts if the two operations access the +same variable: that is, a store-release on a variable ``x`` *synchronizes +with* a load-acquire on a variable ``x``, while a release barrier +synchronizes with any acquire operation. The following example shows +correct synchronization: + + +--------------------------------+--------------------------------+ + | thread 1 | thread 2 | + +================================+================================+ + | :: | :: | + | | | + | atomic_set(&a, 1); | | + | atomic_store_release(&b, 2); | x = atomic_load_acquire(&b); | + | | y = atomic_read(&a); | + +--------------------------------+--------------------------------+ + +Acquire and release semantics of higher-level primitives can also be +relied upon for the purpose of establishing the *synchronizes with* +relation. + +Note that the "writing" thread is accessing the variables in the +opposite order as the "reading" thread. This is expected: stores +before a release operation will normally match the loads after +the acquire operation, and vice versa. In fact, this happened already +in the ``pthread_exit``/``pthread_join`` example above. + +Finally, this more complex example has more than two accesses and data +dependency barriers. It also does not use atomic accesses whenever there +cannot be a data race: + + +----------------------+------------------------------+ + | thread 1 | thread 2 | + +======================+==============================+ + | :: | :: | + | | | + | b[2] = 1; | | + | smp_wmb(); | | + | x->i = 2; | | + | smp_wmb(); | | + | atomic_set(&a, x); | x = atomic_read(&a); | + | | smp_read_barrier_depends(); | + | | y = x->i; | + | | smp_read_barrier_depends(); | + | | z = b[y]; | + +----------------------+------------------------------+ + +Comparison with Linux kernel primitives +======================================= + +Here is a list of differences between Linux kernel atomic operations +and memory barriers, and the equivalents in QEMU: + +- atomic operations in Linux are always on a 32-bit int type and + use a boxed ``atomic_t`` type; atomic operations in QEMU are polymorphic + and use normal C types. + +- Originally, ``atomic_read`` and ``atomic_set`` in Linux gave no guarantee + at all. Linux 4.1 updated them to implement volatile + semantics via ``ACCESS_ONCE`` (or the more recent ``READ``/``WRITE_ONCE``). + + QEMU's ``atomic_read`` and ``atomic_set`` implement C11 atomic relaxed + semantics if the compiler supports it, and volatile semantics otherwise. + Both semantics prevent the compiler from doing certain transformations; + the difference is that atomic accesses are guaranteed to be atomic, + while volatile accesses aren't. Thus, in the volatile case we just cross + our fingers hoping that the compiler will generate atomic accesses, + since we assume the variables passed are machine-word sized and + properly aligned. + + No barriers are implied by ``atomic_read`` and ``atomic_set`` in either Linux + or QEMU. + +- atomic read-modify-write operations in Linux are of three kinds: + + ===================== ========================================= + ``atomic_OP`` returns void + ``atomic_OP_return`` returns new value of the variable + ``atomic_fetch_OP`` returns the old value of the variable + ``atomic_cmpxchg`` returns the old value of the variable + ===================== ========================================= + + In QEMU, the second kind is named ``atomic_OP_fetch``. + +- different atomic read-modify-write operations in Linux imply + a different set of memory barriers; in QEMU, all of them enforce + sequential consistency. + +- in QEMU, ``atomic_read()`` and ``atomic_set()`` do not participate in + the total ordering enforced by sequentially-consistent operations. + This is because QEMU uses the C11 memory model. The following example + is correct in Linux but not in QEMU: + + +----------------------------------+--------------------------------+ + | Linux (correct) | QEMU (incorrect) | + +==================================+================================+ + | :: | :: | + | | | + | a = atomic_fetch_add(&x, 2); | a = atomic_fetch_add(&x, 2); | + | b = READ_ONCE(&y); | b = atomic_read(&y); | + +----------------------------------+--------------------------------+ + + because the read of ``y`` can be moved (by either the processor or the + compiler) before the write of ``x``. + + Fixing this requires an ``smp_mb()`` memory barrier between the write + of ``x`` and the read of ``y``. In the common case where only one thread + writes ``x``, it is also possible to write it like this: + + +--------------------------------+ + | QEMU (correct) | + +================================+ + | :: | + | | + | a = atomic_read(&x); | + | atomic_set(&x, a + 2); | + | smp_mb(); | + | b = atomic_read(&y); | + +--------------------------------+ + +Sources +======= + +- ``Documentation/memory-barriers.txt`` from the Linux kernel diff --git a/docs/devel/atomics.txt b/docs/devel/atomics.txt deleted file mode 100644 index a4db3a4aaa..0000000000 --- a/docs/devel/atomics.txt +++ /dev/null @@ -1,403 +0,0 @@ -CPUs perform independent memory operations effectively in random order. -but this can be a problem for CPU-CPU interaction (including interactions -between QEMU and the guest). Multi-threaded programs use various tools -to instruct the compiler and the CPU to restrict the order to something -that is consistent with the expectations of the programmer. - -The most basic tool is locking. Mutexes, condition variables and -semaphores are used in QEMU, and should be the default approach to -synchronization. Anything else is considerably harder, but it's -also justified more often than one would like. The two tools that -are provided by qemu/atomic.h are memory barriers and atomic operations. - -Macros defined by qemu/atomic.h fall in three camps: - -- compiler barriers: barrier(); - -- weak atomic access and manual memory barriers: atomic_read(), - atomic_set(), smp_rmb(), smp_wmb(), smp_mb(), smp_mb_acquire(), - smp_mb_release(), smp_read_barrier_depends(); - -- sequentially consistent atomic access: everything else. - - -COMPILER MEMORY BARRIER -======================= - -barrier() prevents the compiler from moving the memory accesses either -side of it to the other side. The compiler barrier has no direct effect -on the CPU, which may then reorder things however it wishes. - -barrier() is mostly used within qemu/atomic.h itself. On some -architectures, CPU guarantees are strong enough that blocking compiler -optimizations already ensures the correct order of execution. In this -case, qemu/atomic.h will reduce stronger memory barriers to simple -compiler barriers. - -Still, barrier() can be useful when writing code that can be interrupted -by signal handlers. - - -SEQUENTIALLY CONSISTENT ATOMIC ACCESS -===================================== - -Most of the operations in the qemu/atomic.h header ensure *sequential -consistency*, where "the result of any execution is the same as if the -operations of all the processors were executed in some sequential order, -and the operations of each individual processor appear in this sequence -in the order specified by its program". - -qemu/atomic.h provides the following set of atomic read-modify-write -operations: - - void atomic_inc(ptr) - void atomic_dec(ptr) - void atomic_add(ptr, val) - void atomic_sub(ptr, val) - void atomic_and(ptr, val) - void atomic_or(ptr, val) - - typeof(*ptr) atomic_fetch_inc(ptr) - typeof(*ptr) atomic_fetch_dec(ptr) - typeof(*ptr) atomic_fetch_add(ptr, val) - typeof(*ptr) atomic_fetch_sub(ptr, val) - typeof(*ptr) atomic_fetch_and(ptr, val) - typeof(*ptr) atomic_fetch_or(ptr, val) - typeof(*ptr) atomic_fetch_xor(ptr, val) - typeof(*ptr) atomic_fetch_inc_nonzero(ptr) - typeof(*ptr) atomic_xchg(ptr, val) - typeof(*ptr) atomic_cmpxchg(ptr, old, new) - -all of which return the old value of *ptr. These operations are -polymorphic; they operate on any type that is as wide as a pointer. - -Similar operations return the new value of *ptr: - - typeof(*ptr) atomic_inc_fetch(ptr) - typeof(*ptr) atomic_dec_fetch(ptr) - typeof(*ptr) atomic_add_fetch(ptr, val) - typeof(*ptr) atomic_sub_fetch(ptr, val) - typeof(*ptr) atomic_and_fetch(ptr, val) - typeof(*ptr) atomic_or_fetch(ptr, val) - typeof(*ptr) atomic_xor_fetch(ptr, val) - -Sequentially consistent loads and stores can be done using: - - atomic_fetch_add(ptr, 0) for loads - atomic_xchg(ptr, val) for stores - -However, they are quite expensive on some platforms, notably POWER and -ARM. Therefore, qemu/atomic.h provides two primitives with slightly -weaker constraints: - - typeof(*ptr) atomic_mb_read(ptr) - void atomic_mb_set(ptr, val) - -The semantics of these primitives map to Java volatile variables, -and are strongly related to memory barriers as used in the Linux -kernel (see below). - -As long as you use atomic_mb_read and atomic_mb_set, accesses cannot -be reordered with each other, and it is also not possible to reorder -"normal" accesses around them. - -However, and this is the important difference between -atomic_mb_read/atomic_mb_set and sequential consistency, it is important -for both threads to access the same volatile variable. It is not the -case that everything visible to thread A when it writes volatile field f -becomes visible to thread B after it reads volatile field g. The store -and load have to "match" (i.e., be performed on the same volatile -field) to achieve the right semantics. - - -These operations operate on any type that is as wide as an int or smaller. - - -WEAK ATOMIC ACCESS AND MANUAL MEMORY BARRIERS -============================================= - -Compared to sequentially consistent atomic access, programming with -weaker consistency models can be considerably more complicated. -In general, if the algorithm you are writing includes both writes -and reads on the same side, it is generally simpler to use sequentially -consistent primitives. - -When using this model, variables are accessed with: - -- atomic_read() and atomic_set(); these prevent the compiler from - optimizing accesses out of existence and creating unsolicited - accesses, but do not otherwise impose any ordering on loads and - stores: both the compiler and the processor are free to reorder - them. - -- atomic_load_acquire(), which guarantees the LOAD to appear to - happen, with respect to the other components of the system, - before all the LOAD or STORE operations specified afterwards. - Operations coming before atomic_load_acquire() can still be - reordered after it. - -- atomic_store_release(), which guarantees the STORE to appear to - happen, with respect to the other components of the system, - after all the LOAD or STORE operations specified afterwards. - Operations coming after atomic_store_release() can still be - reordered after it. - -Restrictions to the ordering of accesses can also be specified -using the memory barrier macros: smp_rmb(), smp_wmb(), smp_mb(), -smp_mb_acquire(), smp_mb_release(), smp_read_barrier_depends(). - -Memory barriers control the order of references to shared memory. -They come in six kinds: - -- smp_rmb() guarantees that all the LOAD operations specified before - the barrier will appear to happen before all the LOAD operations - specified after the barrier with respect to the other components of - the system. - - In other words, smp_rmb() puts a partial ordering on loads, but is not - required to have any effect on stores. - -- smp_wmb() guarantees that all the STORE operations specified before - the barrier will appear to happen before all the STORE operations - specified after the barrier with respect to the other components of - the system. - - In other words, smp_wmb() puts a partial ordering on stores, but is not - required to have any effect on loads. - -- smp_mb_acquire() guarantees that all the LOAD operations specified before - the barrier will appear to happen before all the LOAD or STORE operations - specified after the barrier with respect to the other components of - the system. - -- smp_mb_release() guarantees that all the STORE operations specified *after* - the barrier will appear to happen after all the LOAD or STORE operations - specified *before* the barrier with respect to the other components of - the system. - -- smp_mb() guarantees that all the LOAD and STORE operations specified - before the barrier will appear to happen before all the LOAD and - STORE operations specified after the barrier with respect to the other - components of the system. - - smp_mb() puts a partial ordering on both loads and stores. It is - stronger than both a read and a write memory barrier; it implies both - smp_mb_acquire() and smp_mb_release(), but it also prevents STOREs - coming before the barrier from overtaking LOADs coming after the - barrier and vice versa. - -- smp_read_barrier_depends() is a weaker kind of read barrier. On - most processors, whenever two loads are performed such that the - second depends on the result of the first (e.g., the first load - retrieves the address to which the second load will be directed), - the processor will guarantee that the first LOAD will appear to happen - before the second with respect to the other components of the system. - However, this is not always true---for example, it was not true on - Alpha processors. Whenever this kind of access happens to shared - memory (that is not protected by a lock), a read barrier is needed, - and smp_read_barrier_depends() can be used instead of smp_rmb(). - - Note that the first load really has to have a _data_ dependency and not - a control dependency. If the address for the second load is dependent - on the first load, but the dependency is through a conditional rather - than actually loading the address itself, then it's a _control_ - dependency and a full read barrier or better is required. - - -This is the set of barriers that is required *between* two atomic_read() -and atomic_set() operations to achieve sequential consistency: - - | 2nd operation | - |-----------------------------------------------| - 1st operation | (after last) | atomic_read | atomic_set | - ---------------+----------------+-------------+----------------| - (before first) | | none | smp_mb_release | - ---------------+----------------+-------------+----------------| - atomic_read | smp_mb_acquire | smp_rmb | ** | - ---------------+----------------+-------------+----------------| - atomic_set | none | smp_mb()*** | smp_wmb() | - ---------------+----------------+-------------+----------------| - - * Or smp_read_barrier_depends(). - - ** This requires a load-store barrier. This is achieved by - either smp_mb_acquire() or smp_mb_release(). - - *** This requires a store-load barrier. On most machines, the only - way to achieve this is a full barrier. - - -You can see that the two possible definitions of atomic_mb_read() -and atomic_mb_set() are the following: - - 1) atomic_mb_read(p) = atomic_read(p); smp_mb_acquire() - atomic_mb_set(p, v) = smp_mb_release(); atomic_set(p, v); smp_mb() - - 2) atomic_mb_read(p) = smp_mb() atomic_read(p); smp_mb_acquire() - atomic_mb_set(p, v) = smp_mb_release(); atomic_set(p, v); - -Usually the former is used, because smp_mb() is expensive and a program -normally has more reads than writes. Therefore it makes more sense to -make atomic_mb_set() the more expensive operation. - -There are two common cases in which atomic_mb_read and atomic_mb_set -generate too many memory barriers, and thus it can be useful to manually -place barriers, or use atomic_load_acquire/atomic_store_release instead: - -- when a data structure has one thread that is always a writer - and one thread that is always a reader, manual placement of - memory barriers makes the write side faster. Furthermore, - correctness is easy to check for in this case using the "pairing" - trick that is explained below: - - thread 1 thread 1 - ------------------------- ------------------------ - (other writes) - atomic_mb_set(&a, x) atomic_store_release(&a, x) - atomic_mb_set(&b, y) atomic_store_release(&b, y) - - => - thread 2 thread 2 - ------------------------- ------------------------ - y = atomic_mb_read(&b) y = atomic_load_acquire(&b) - x = atomic_mb_read(&a) x = atomic_load_acquire(&a) - (other reads) - - Note that the barrier between the stores in thread 1, and between - the loads in thread 2, has been optimized here to a write or a - read memory barrier respectively. On some architectures, notably - ARMv7, smp_mb_acquire and smp_mb_release are just as expensive as - smp_mb, but smp_rmb and/or smp_wmb are more efficient. - -- sometimes, a thread is accessing many variables that are otherwise - unrelated to each other (for example because, apart from the current - thread, exactly one other thread will read or write each of these - variables). In this case, it is possible to "hoist" the implicit - barriers provided by atomic_mb_read() and atomic_mb_set() outside - a loop. For example, the above definition atomic_mb_read() gives - the following transformation: - - n = 0; n = 0; - for (i = 0; i < 10; i++) => for (i = 0; i < 10; i++) - n += atomic_mb_read(&a[i]); n += atomic_read(&a[i]); - smp_mb_acquire(); - - Similarly, atomic_mb_set() can be transformed as follows: - - smp_mb_release(); - for (i = 0; i < 10; i++) => for (i = 0; i < 10; i++) - atomic_mb_set(&a[i], false); atomic_set(&a[i], false); - smp_mb(); - - - The other thread can still use atomic_mb_read()/atomic_mb_set(). - -The two tricks can be combined. In this case, splitting a loop in -two lets you hoist the barriers out of the loops _and_ eliminate the -expensive smp_mb(): - - smp_mb_release(); - for (i = 0; i < 10; i++) { => for (i = 0; i < 10; i++) - atomic_mb_set(&a[i], false); atomic_set(&a[i], false); - atomic_mb_set(&b[i], false); smb_wmb(); - } for (i = 0; i < 10; i++) - atomic_set(&a[i], false); - smp_mb(); - - -Memory barrier pairing ----------------------- - -A useful rule of thumb is that memory barriers should always, or almost -always, be paired with another barrier. In the case of QEMU, however, -note that the other barrier may actually be in a driver that runs in -the guest! - -For the purposes of pairing, smp_read_barrier_depends() and smp_rmb() -both count as read barriers. A read barrier shall pair with a write -barrier or a full barrier; a write barrier shall pair with a read -barrier or a full barrier. A full barrier can pair with anything. -For example: - - thread 1 thread 2 - =============== =============== - a = 1; - smp_wmb(); - b = 2; x = b; - smp_rmb(); - y = a; - -Note that the "writing" thread is accessing the variables in the -opposite order as the "reading" thread. This is expected: stores -before the write barrier will normally match the loads after the -read barrier, and vice versa. The same is true for more than 2 -access and for data dependency barriers: - - thread 1 thread 2 - =============== =============== - b[2] = 1; - smp_wmb(); - x->i = 2; - smp_wmb(); - a = x; x = a; - smp_read_barrier_depends(); - y = x->i; - smp_read_barrier_depends(); - z = b[y]; - -smp_wmb() also pairs with atomic_mb_read() and smp_mb_acquire(). -and smp_rmb() also pairs with atomic_mb_set() and smp_mb_release(). - - -COMPARISON WITH LINUX KERNEL MEMORY BARRIERS -============================================ - -Here is a list of differences between Linux kernel atomic operations -and memory barriers, and the equivalents in QEMU: - -- atomic operations in Linux are always on a 32-bit int type and - use a boxed atomic_t type; atomic operations in QEMU are polymorphic - and use normal C types. - -- Originally, atomic_read and atomic_set in Linux gave no guarantee - at all. Linux 4.1 updated them to implement volatile - semantics via ACCESS_ONCE (or the more recent READ/WRITE_ONCE). - - QEMU's atomic_read/set implement, if the compiler supports it, C11 - atomic relaxed semantics, and volatile semantics otherwise. - Both semantics prevent the compiler from doing certain transformations; - the difference is that atomic accesses are guaranteed to be atomic, - while volatile accesses aren't. Thus, in the volatile case we just cross - our fingers hoping that the compiler will generate atomic accesses, - since we assume the variables passed are machine-word sized and - properly aligned. - No barriers are implied by atomic_read/set in either Linux or QEMU. - -- atomic read-modify-write operations in Linux are of three kinds: - - atomic_OP returns void - atomic_OP_return returns new value of the variable - atomic_fetch_OP returns the old value of the variable - atomic_cmpxchg returns the old value of the variable - - In QEMU, the second kind does not exist. Currently Linux has - atomic_fetch_or only. QEMU provides and, or, inc, dec, add, sub. - -- different atomic read-modify-write operations in Linux imply - a different set of memory barriers; in QEMU, all of them enforce - sequential consistency, which means they imply full memory barriers - before and after the operation. - -- Linux does not have an equivalent of atomic_mb_set(). In particular, - note that smp_store_mb() is a little weaker than atomic_mb_set(). - atomic_mb_read() compiles to the same instructions as Linux's - smp_load_acquire(), but this should be treated as an implementation - detail. - -SOURCES -======= - -* Documentation/memory-barriers.txt from the Linux kernel - -* "The JSR-133 Cookbook for Compiler Writers", available at - http://g.oswego.edu/dl/jmm/cookbook.html diff --git a/docs/devel/bitops.rst b/docs/devel/bitops.rst new file mode 100644 index 0000000000..6addaecf8d --- /dev/null +++ b/docs/devel/bitops.rst @@ -0,0 +1,8 @@ +================== +Bitwise operations +================== + +The header ``qemu/bitops.h`` provides utility functions for +performing bitwise operations. + +.. kernel-doc:: include/qemu/bitops.h diff --git a/docs/devel/fuzzing.txt b/docs/devel/fuzzing.txt new file mode 100644 index 0000000000..324d2cd92b --- /dev/null +++ b/docs/devel/fuzzing.txt @@ -0,0 +1,116 @@ += Fuzzing = + +== Introduction == + +This document describes the virtual-device fuzzing infrastructure in QEMU and +how to use it to implement additional fuzzers. + +== Basics == + +Fuzzing operates by passing inputs to an entry point/target function. The +fuzzer tracks the code coverage triggered by the input. Based on these +findings, the fuzzer mutates the input and repeats the fuzzing. + +To fuzz QEMU, we rely on libfuzzer. Unlike other fuzzers such as AFL, libfuzzer +is an _in-process_ fuzzer. For the developer, this means that it is their +responsibility to ensure that state is reset between fuzzing-runs. + +== Building the fuzzers == + +NOTE: If possible, build a 32-bit binary. When forking, the 32-bit fuzzer is +much faster, since the page-map has a smaller size. This is due to the fact that +AddressSanitizer mmaps ~20TB of memory, as part of its detection. This results +in a large page-map, and a much slower fork(). + +To build the fuzzers, install a recent version of clang: +Configure with (substitute the clang binaries with the version you installed): + + CC=clang-8 CXX=clang++-8 /path/to/configure --enable-fuzzing + +Fuzz targets are built similarly to system/softmmu: + + make i386-softmmu/fuzz + +This builds ./i386-softmmu/qemu-fuzz-i386 + +The first option to this command is: --fuzz_taget=FUZZ_NAME +To list all of the available fuzzers run qemu-fuzz-i386 with no arguments. + +eg: + ./i386-softmmu/qemu-fuzz-i386 --fuzz-target=virtio-net-fork-fuzz + +Internally, libfuzzer parses all arguments that do not begin with "--". +Information about these is available by passing -help=1 + +Now the only thing left to do is wait for the fuzzer to trigger potential +crashes. + +== Adding a new fuzzer == +Coverage over virtual devices can be improved by adding additional fuzzers. +Fuzzers are kept in tests/qtest/fuzz/ and should be added to +tests/qtest/fuzz/Makefile.include + +Fuzzers can rely on both qtest and libqos to communicate with virtual devices. + +1. Create a new source file. For example ``tests/qtest/fuzz/foo-device-fuzz.c``. + +2. Write the fuzzing code using the libqtest/libqos API. See existing fuzzers +for reference. + +3. Register the fuzzer in ``tests/fuzz/Makefile.include`` by appending the +corresponding object to fuzz-obj-y + +Fuzzers can be more-or-less thought of as special qtest programs which can +modify the qtest commands and/or qtest command arguments based on inputs +provided by libfuzzer. Libfuzzer passes a byte array and length. Commonly the +fuzzer loops over the byte-array interpreting it as a list of qtest commands, +addresses, or values. + += Implementation Details = + +== The Fuzzer's Lifecycle == + +The fuzzer has two entrypoints that libfuzzer calls. libfuzzer provides it's +own main(), which performs some setup, and calls the entrypoints: + +LLVMFuzzerInitialize: called prior to fuzzing. Used to initialize all of the +necessary state + +LLVMFuzzerTestOneInput: called for each fuzzing run. Processes the input and +resets the state at the end of each run. + +In more detail: + +LLVMFuzzerInitialize parses the arguments to the fuzzer (must start with two +dashes, so they are ignored by libfuzzer main()). Currently, the arguments +select the fuzz target. Then, the qtest client is initialized. If the target +requires qos, qgraph is set up and the QOM/LIBQOS modules are initialized. +Then the QGraph is walked and the QEMU cmd_line is determined and saved. + +After this, the vl.c:qemu__main is called to set up the guest. There are +target-specific hooks that can be called before and after qemu_main, for +additional setup(e.g. PCI setup, or VM snapshotting). + +LLVMFuzzerTestOneInput: Uses qtest/qos functions to act based on the fuzz +input. It is also responsible for manually calling the main loop/main_loop_wait +to ensure that bottom halves are executed and any cleanup required before the +next input. + +Since the same process is reused for many fuzzing runs, QEMU state needs to +be reset at the end of each run. There are currently two implemented +options for resetting state: +1. Reboot the guest between runs. + Pros: Straightforward and fast for simple fuzz targets. + Cons: Depending on the device, does not reset all device state. If the + device requires some initialization prior to being ready for fuzzing + (common for QOS-based targets), this initialization needs to be done after + each reboot. + Example target: i440fx-qtest-reboot-fuzz +2. Run each test case in a separate forked process and copy the coverage + information back to the parent. This is fairly similar to AFL's "deferred" + fork-server mode [3] + Pros: Relatively fast. Devices only need to be initialized once. No need + to do slow reboots or vmloads. + Cons: Not officially supported by libfuzzer. Does not work well for devices + that rely on dedicated threads. + Example target: virtio-net-fork-fuzz diff --git a/docs/devel/index.rst b/docs/devel/index.rst index c86a3cdff2..a9e1200dff 100644 --- a/docs/devel/index.rst +++ b/docs/devel/index.rst @@ -17,9 +17,13 @@ Contents: loads-stores memory migration + atomics stable-process testing decodetree secure-coding-practices tcg tcg-plugins + bitops + reset + s390-dasd-ipl diff --git a/docs/devel/kconfig.rst b/docs/devel/kconfig.rst index b7bca44704..e5df72b342 100644 --- a/docs/devel/kconfig.rst +++ b/docs/devel/kconfig.rst @@ -8,7 +8,7 @@ time different targets can share large amounts of code. For example, a POWER and an x86 board can run the same code to emulate a PCI network card, even though the boards use different PCI host bridges, and they can run the same code to emulate a SCSI disk while using different -SCSI adapters. ARM, s390 and x86 boards can all present a virtio-blk +SCSI adapters. Arm, s390 and x86 boards can all present a virtio-blk disk to their guests, but with three different virtio guest interfaces. Each QEMU target enables a subset of the boards, devices and buses that diff --git a/docs/devel/loads-stores.rst b/docs/devel/loads-stores.rst index c74cd090e6..0d99eb24c1 100644 --- a/docs/devel/loads-stores.rst +++ b/docs/devel/loads-stores.rst @@ -72,31 +72,34 @@ Regexes for git grep - ``\`` - ``\`` -``cpu_{ld,st}_*`` -~~~~~~~~~~~~~~~~~ +``cpu_{ld,st}*_mmuidx_ra`` +~~~~~~~~~~~~~~~~~~~~~~~~~~ -These functions operate on a guest virtual address. Be aware -that these functions may cause a guest CPU exception to be -taken (e.g. for an alignment fault or MMU fault) which will -result in guest CPU state being updated and control longjumping -out of the function call. They should therefore only be used -in code that is implementing emulation of the target CPU. +These functions operate on a guest virtual address plus a context, +known as a "mmu index" or ``mmuidx``, which controls how that virtual +address is translated. The meaning of the indexes are target specific, +but specifying a particular index might be necessary if, for instance, +the helper requires an "always as non-privileged" access rather that +the default access for the current state of the guest CPU. -These functions may throw an exception (longjmp() back out -to the top level TCG loop). This means they must only be used -from helper functions where the translator has saved all -necessary CPU state before generating the helper function call. -It's usually better to use the ``_ra`` variants described below -from helper functions, but these functions are the right choice -for calls made from hooks like the CPU do_interrupt hook or -when you know for certain that the translator had to save all -the CPU state that ``cpu_restore_state()`` would restore anyway. +These functions may cause a guest CPU exception to be taken +(e.g. for an alignment fault or MMU fault) which will result in +guest CPU state being updated and control longjmp'ing out of the +function call. They should therefore only be used in code that is +implementing emulation of the guest CPU. + +The ``retaddr`` parameter is used to control unwinding of the +guest CPU state in case of a guest CPU exception. This is passed +to ``cpu_restore_state()``. Therefore the value should either be 0, +to indicate that the guest CPU state is already synchronized, or +the result of ``GETPC()`` from the top level ``HELPER(foo)`` +function, which is a return address into the generated code. Function names follow the pattern: -load: ``cpu_ld{sign}{size}_{mmusuffix}(env, ptr)`` +load: ``cpu_ld{sign}{size}_mmuidx_ra(env, ptr, mmuidx, retaddr)`` -store: ``cpu_st{size}_{mmusuffix}(env, ptr, val)`` +store: ``cpu_st{size}_mmuidx_ra(env, ptr, val, mmuidx, retaddr)`` ``sign`` - (empty) : for 32 or 64 bit sizes @@ -109,56 +112,151 @@ store: ``cpu_st{size}_{mmusuffix}(env, ptr, val)`` - ``l`` : 32 bits - ``q`` : 64 bits -``mmusuffix`` is one of the generic suffixes ``data`` or ``code``, or -(for softmmu configs) a target-specific MMU mode suffix as defined -in the target's ``cpu.h``. +Regexes for git grep: + - ``\`` + - ``\`` -Regexes for git grep - - ``\`` - - ``\`` +``cpu_{ld,st}*_data_ra`` +~~~~~~~~~~~~~~~~~~~~~~~~ -``cpu_{ld,st}_*_ra`` -~~~~~~~~~~~~~~~~~~~~ - -These functions work like the ``cpu_{ld,st}_*`` functions except -that they also take a ``retaddr`` argument. This extra argument -allows for correct unwinding of any exception that is taken, -and should generally be the result of GETPC() called directly -from the top level HELPER(foo) function (i.e. the return address -in the generated code). +These functions work like the ``cpu_{ld,st}_mmuidx_ra`` functions +except that the ``mmuidx`` parameter is taken from the current mode +of the guest CPU, as determined by ``cpu_mmu_index(env, false)``. These are generally the preferred way to do accesses by guest -virtual address from helper functions; see the documentation -of the non-``_ra`` variants for when those would be better. - -Calling these functions with a ``retaddr`` argument of 0 is -equivalent to calling the non-``_ra`` version of the function. +virtual address from helper functions, unless the access should +be performed with a context other than the default. Function names follow the pattern: -load: ``cpu_ld{sign}{size}_{mmusuffix}_ra(env, ptr, retaddr)`` +load: ``cpu_ld{sign}{size}_data_ra(env, ptr, ra)`` -store: ``cpu_st{sign}{size}_{mmusuffix}_ra(env, ptr, val, retaddr)`` +store: ``cpu_st{size}_data_ra(env, ptr, val, ra)`` + +``sign`` + - (empty) : for 32 or 64 bit sizes + - ``u`` : unsigned + - ``s`` : signed + +``size`` + - ``b`` : 8 bits + - ``w`` : 16 bits + - ``l`` : 32 bits + - ``q`` : 64 bits + +Regexes for git grep: + - ``\`` + - ``\`` + +``cpu_{ld,st}*_data`` +~~~~~~~~~~~~~~~~~~~~~ + +These functions work like the ``cpu_{ld,st}_data_ra`` functions +except that the ``retaddr`` parameter is 0, and thus does not +unwind guest CPU state. + +This means they must only be used from helper functions where the +translator has saved all necessary CPU state. These functions are +the right choice for calls made from hooks like the CPU ``do_interrupt`` +hook or when you know for certain that the translator had to save all +the CPU state anyway. + +Function names follow the pattern: + +load: ``cpu_ld{sign}{size}_data(env, ptr)`` + +store: ``cpu_st{size}_data(env, ptr, val)`` + +``sign`` + - (empty) : for 32 or 64 bit sizes + - ``u`` : unsigned + - ``s`` : signed + +``size`` + - ``b`` : 8 bits + - ``w`` : 16 bits + - ``l`` : 32 bits + - ``q`` : 64 bits Regexes for git grep - - ``\`` - - ``\`` + - ``\`` + - ``\`` -``helper_*_{ld,st}*mmu`` -~~~~~~~~~~~~~~~~~~~~~~~~ +``cpu_ld*_code`` +~~~~~~~~~~~~~~~~ + +These functions perform a read for instruction execution. The ``mmuidx`` +parameter is taken from the current mode of the guest CPU, as determined +by ``cpu_mmu_index(env, true)``. The ``retaddr`` parameter is 0, and +thus does not unwind guest CPU state, because CPU state is always +synchronized while translating instructions. Any guest CPU exception +that is raised will indicate an instruction execution fault rather than +a data read fault. + +In general these functions should not be used directly during translation. +There are wrapper functions that are to be used which also take care of +plugins for tracing. + +Function names follow the pattern: + +load: ``cpu_ld{sign}{size}_code(env, ptr)`` + +``sign`` + - (empty) : for 32 or 64 bit sizes + - ``u`` : unsigned + - ``s`` : signed + +``size`` + - ``b`` : 8 bits + - ``w`` : 16 bits + - ``l`` : 32 bits + - ``q`` : 64 bits + +Regexes for git grep: + - ``\`` + +``translator_ld*`` +~~~~~~~~~~~~~~~~~~ + +These functions are a wrapper for ``cpu_ld*_code`` which also perform +any actions required by any tracing plugins. They are only to be +called during the translator callback ``translate_insn``. + +There is a set of functions ending in ``_swap`` which, if the parameter +is true, returns the value in the endianness that is the reverse of +the guest native endianness, as determined by ``TARGET_WORDS_BIGENDIAN``. + +Function names follow the pattern: + +load: ``translator_ld{sign}{size}(env, ptr)`` + +swap: ``translator_ld{sign}{size}_swap(env, ptr, swap)`` + +``sign`` + - (empty) : for 32 or 64 bit sizes + - ``u`` : unsigned + - ``s`` : signed + +``size`` + - ``b`` : 8 bits + - ``w`` : 16 bits + - ``l`` : 32 bits + - ``q`` : 64 bits + +Regexes for git grep + - ``\`` + +``helper_*_{ld,st}*_mmu`` +~~~~~~~~~~~~~~~~~~~~~~~~~ These functions are intended primarily to be called by the code generated by the TCG backend. They may also be called by target -CPU helper function code. Like the ``cpu_{ld,st}_*_ra`` functions -they perform accesses by guest virtual address; the difference is -that these functions allow you to specify an ``opindex`` parameter -which encodes (among other things) the mmu index to use for the -access. This is necessary if your helper needs to make an access -via a specific mmu index (for instance, an "always as non-privileged" -access) rather than using the default mmu index for the current state -of the guest CPU. +CPU helper function code. Like the ``cpu_{ld,st}_mmuidx_ra`` functions +they perform accesses by guest virtual address, with a given ``mmuidx``. -The ``opindex`` parameter should be created by calling ``make_memop_idx()``. +These functions specify an ``opindex`` parameter which encodes +(among other things) the mmu index to use for the access. This parameter +should be created by calling ``make_memop_idx()``. The ``retaddr`` parameter should be the result of GETPC() called directly from the top level HELPER(foo) function (or 0 if no guest CPU state @@ -166,13 +264,12 @@ unwinding is required). **TODO** The names of these functions are a bit odd for historical reasons because they were originally expected to be called only from -within generated code. We should rename them to bring them -more in line with the other memory access functions. +within generated code. We should rename them to bring them more in +line with the other memory access functions. The explicit endianness +is the only feature they have beyond ``*_mmuidx_ra``. load: ``helper_{endian}_ld{sign}{size}_mmu(env, addr, opindex, retaddr)`` -load (code): ``helper_{endian}_ld{sign}{size}_cmmu(env, addr, opindex, retaddr)`` - store: ``helper_{endian}_st{size}_mmu(env, addr, val, opindex, retaddr)`` ``sign`` @@ -192,7 +289,7 @@ store: ``helper_{endian}_st{size}_mmu(env, addr, val, opindex, retaddr)`` - ``ret`` : target endianness Regexes for git grep - - ``\`` + - ``\`` - ``\`` ``address_space_*`` @@ -205,7 +302,7 @@ way QEMU defines the view of memory that a device or CPU has. or bus fabric.) Each CPU has an AddressSpace. Some kinds of CPU have more than -one AddressSpace (for instance ARM guest CPUs have an AddressSpace +one AddressSpace (for instance Arm guest CPUs have an AddressSpace for the Secure world and one for NonSecure if they implement TrustZone). Devices which can do DMA-type operations should generally have an AddressSpace. There is also a "system address space" which typically diff --git a/docs/devel/memory.rst b/docs/devel/memory.rst index b6a4c37ea5..5dc8a12682 100644 --- a/docs/devel/memory.rst +++ b/docs/devel/memory.rst @@ -361,3 +361,8 @@ callbacks are called: - .impl.unaligned specifies that the *implementation* supports unaligned accesses; if false, unaligned accesses will be emulated by two aligned accesses. + +API Reference +------------- + +.. kernel-doc:: include/exec/memory.h diff --git a/docs/devel/multi-thread-tcg.txt b/docs/devel/multi-thread-tcg.txt index 782bebc28b..3c85ac0eab 100644 --- a/docs/devel/multi-thread-tcg.txt +++ b/docs/devel/multi-thread-tcg.txt @@ -227,7 +227,7 @@ minimise contention. (Current solution) MMIO access automatically serialises hardware emulation by way of the -BQL. Currently ARM targets serialise all ARM_CP_IO register accesses +BQL. Currently Arm targets serialise all ARM_CP_IO register accesses and also defer the reset/startup of vCPUs to the vCPU context by way of async_run_on_cpu(). @@ -268,7 +268,7 @@ ordered backends this could become a NOP. Aside from explicit standalone memory barrier instructions there are also implicit memory ordering semantics which comes with each guest memory access instruction. For example all x86 load/stores come with -fairly strong guarantees of sequential consistency where as ARM has +fairly strong guarantees of sequential consistency whereas Arm has special variants of load/store instructions that imply acquire/release semantics. @@ -317,7 +317,7 @@ x86 cmpxchg instruction. The second type offer a pair of load/store instructions which offer a guarantee that a region of memory has not been touched between the -load and store instructions. An example of this is ARM's ldrex/strex +load and store instructions. An example of this is Arm's ldrex/strex pair where the strex instruction will return a flag indicating a successful store only if no other CPU has accessed the memory region since the ldrex. @@ -339,7 +339,7 @@ CURRENT OPEN QUESTIONS: The TCG provides a number of atomic helpers (tcg_gen_atomic_*) which can be used directly or combined to emulate other instructions like -ARM's ldrex/strex instructions. While they are susceptible to the ABA +Arm's ldrex/strex instructions. While they are susceptible to the ABA problem so far common guests have not implemented patterns where this may be a problem - typically presenting a locking ABI which assumes cmpxchg like semantics. diff --git a/docs/devel/qapi-code-gen.txt b/docs/devel/qapi-code-gen.txt index 45c93a43cc..1967adfa92 100644 --- a/docs/devel/qapi-code-gen.txt +++ b/docs/devel/qapi-code-gen.txt @@ -172,7 +172,8 @@ Syntax: ENUM = { 'enum': STRING, 'data': [ ENUM-VALUE, ... ], '*prefix': STRING, - '*if': COND } + '*if': COND, + '*features': FEATURES } ENUM-VALUE = STRING | { 'name': STRING, '*if': COND } @@ -207,6 +208,9 @@ the job satisfactorily. The optional 'if' member specifies a conditional. See "Configuring the schema" below for more on this. +The optional 'features' member specifies features. See "Features" +below for more on this. + === Type references and array types === @@ -230,7 +234,9 @@ Syntax: '*features': FEATURES } MEMBERS = { MEMBER, ... } MEMBER = STRING : TYPE-REF - | STRING : { 'type': TYPE-REF, '*if': COND } + | STRING : { 'type': TYPE-REF, + '*if': COND, + '*features': FEATURES } Member 'struct' names the struct type. @@ -279,12 +285,14 @@ below for more on this. Syntax: UNION = { 'union': STRING, 'data': BRANCHES, - '*if': COND } + '*if': COND, + '*features': FEATURES } | { 'union': STRING, 'data': BRANCHES, 'base': ( MEMBERS | STRING ), 'discriminator': STRING, - '*if': COND } + '*if': COND, + '*features': FEATURES } BRANCHES = { BRANCH, ... } BRANCH = STRING : TYPE-REF | STRING : { 'type': TYPE-REF, '*if': COND } @@ -391,15 +399,19 @@ is identical on the wire to: The optional 'if' member specifies a conditional. See "Configuring the schema" below for more on this. +The optional 'features' member specifies features. See "Features" +below for more on this. + === Alternate types === Syntax: ALTERNATE = { 'alternate': STRING, 'data': ALTERNATIVES, - '*if': COND } + '*if': COND, + '*features': FEATURES } ALTERNATIVES = { ALTERNATIVE, ... } - ALTERNATIVE = STRING : TYPE-REF + ALTERNATIVE = STRING : STRING | STRING : { 'type': STRING, '*if': COND } Member 'alternate' names the alternate type. @@ -441,6 +453,9 @@ following example objects: The optional 'if' member specifies a conditional. See "Configuring the schema" below for more on this. +The optional 'features' member specifies features. See "Features" +below for more on this. + === Commands === @@ -584,6 +599,9 @@ started with --preconfig. The optional 'if' member specifies a conditional. See "Configuring the schema" below for more on this. +The optional 'features' member specifies features. See "Features" +below for more on this. + === Events === @@ -595,7 +613,8 @@ Syntax: 'data': STRING, 'boxed': true, ) - '*if': COND } + '*if': COND, + '*features': FEATURES } Member 'event' names the event. This is the event name used in the Client JSON Protocol. @@ -628,6 +647,9 @@ complex type. See section "Code generated for events" for examples. The optional 'if' member specifies a conditional. See "Configuring the schema" below for more on this. +The optional 'features' member specifies features. See "Features" +below for more on this. + === Features === @@ -642,13 +664,8 @@ that previously resulted in an error). QMP clients may still need to know whether the extension is available. For this purpose, a list of features can be specified for a command or -struct type. This is exposed to the client as a list of strings, -where each string signals that this build of QEMU shows a certain -behaviour. - -Each member of the 'features' array defines a feature. It can either -be { 'name': STRING, '*if': COND }, or STRING, which is shorthand for -{ 'name': STRING }. +struct type. Each list member can either be { 'name': STRING, '*if': +COND }, or STRING, which is shorthand for { 'name': STRING }. The optional 'if' member specifies a conditional. See "Configuring the schema" below for more on this. @@ -659,6 +676,18 @@ Example: 'data': { 'number': 'int' }, 'features': [ 'allow-negative-numbers' ] } +The feature strings are exposed to clients in introspection, as +explained in section "Client JSON Protocol introspection". + +Intended use is to have each feature string signal that this build of +QEMU shows a certain behaviour. + + +==== Special features ==== + +Feature "deprecated" marks a command, event, or struct member as +deprecated. It is not supported elsewhere so far. + === Naming rules and reserved names === @@ -965,8 +994,9 @@ schema, along with the SchemaInfo type. This text attempts to give an overview how things work. For details you need to consult the QAPI schema. -SchemaInfo objects have common members "name" and "meta-type", and -additional variant members depending on the value of meta-type. +SchemaInfo objects have common members "name", "meta-type", +"features", and additional variant members depending on the value of +meta-type. Each SchemaInfo object describes a wire ABI entity of a certain meta-type: a command, event or one of several kinds of type. @@ -979,6 +1009,9 @@ not. Therefore, the SchemaInfo for types have auto-generated meaningless names. For readability, the examples in this section use meaningful type names instead. +Optional member "features" exposes the entity's feature strings as a +JSON array of strings. + To examine a type, start with a command or event using it, then follow references by name. @@ -988,9 +1021,9 @@ The SchemaInfo for a command has meta-type "command", and variant members "arg-type", "ret-type" and "allow-oob". On the wire, the "arguments" member of a client's "execute" command must conform to the object type named by "arg-type". The "return" member that the server -passes in a success response conforms to the type named by -"ret-type". When "allow-oob" is set, it means the command supports -out-of-band execution. +passes in a success response conforms to the type named by "ret-type". +When "allow-oob" is true, it means the command supports out-of-band +execution. It defaults to false. If the command takes no arguments, "arg-type" names an object type without members. Likewise, if the command returns nothing, "ret-type" @@ -1047,6 +1080,16 @@ Example: the SchemaInfo for MyType from section Struct types { "name": "member2", "type": "int" }, { "name": "member3", "type": "str", "default": null } ] } +"features" exposes the command's feature strings as a JSON array of +strings. + +Example: the SchemaInfo for TestType from section Features: + + { "name": "TestType", "meta-type": "object", + "members": [ + { "name": "number", "type": "int" } ], + "features": ["allow-negative-numbers"] } + "tag" is the name of the common member serving as type tag. "variants" is a JSON array describing the object's variant members. Each element is a JSON object with members "case" (the value of type @@ -1493,6 +1536,10 @@ $(prefix)qapi-commands.c: Command marshal/dispatch functions for each $(prefix)qapi-commands.h: Function prototypes for the QMP commands specified in the schema +$(prefix)qapi-init-commands.h - Command initialization prototype + +$(prefix)qapi-init-commands.c - Command initialization code + Example: $ cat qapi-generated/example-qapi-commands.h @@ -1502,11 +1549,9 @@ Example: #define EXAMPLE_QAPI_COMMANDS_H #include "example-qapi-types.h" - #include "qapi/qmp/dispatch.h" UserDefOne *qmp_my_command(UserDefOneList *arg1, Error **errp); void qmp_marshal_my_command(QDict *args, QObject **ret, Error **errp); - void example_qmp_init_marshal(QmpCommandList *cmds); #endif /* EXAMPLE_QAPI_COMMANDS_H */ $ cat qapi-generated/example-qapi-commands.c @@ -1566,7 +1611,19 @@ Example: visit_end_struct(v, NULL); visit_free(v); } +[Uninteresting stuff omitted...] + $ cat qapi-generated/example-qapi-init-commands.h +[Uninteresting stuff omitted...] + #ifndef EXAMPLE_QAPI_INIT_COMMANDS_H + #define EXAMPLE_QAPI_INIT_COMMANDS_H + #include "qapi/qmp/dispatch.h" + + void example_qmp_init_marshal(QmpCommandList *cmds); + + #endif /* EXAMPLE_QAPI_INIT_COMMANDS_H */ + $ cat qapi-generated/example-qapi-init-commands.c +[Uninteresting stuff omitted...] void example_qmp_init_marshal(QmpCommandList *cmds) { QTAILQ_INIT(cmds); @@ -1574,7 +1631,6 @@ Example: qmp_register_command(cmds, "my-command", qmp_marshal_my_command, QCO_NO_OPTIONS); } - [Uninteresting stuff omitted...] For a modular QAPI schema (see section Include directives), code for diff --git a/docs/devel/rcu.txt b/docs/devel/rcu.txt index d83fed2f79..0ce15ba198 100644 --- a/docs/devel/rcu.txt +++ b/docs/devel/rcu.txt @@ -132,7 +132,7 @@ The core RCU API is small: typeof(*p) atomic_rcu_read(p); - atomic_rcu_read() is similar to atomic_mb_read(), but it makes + atomic_rcu_read() is similar to atomic_load_acquire(), but it makes some assumptions on the code that calls it. This allows a more optimized implementation. @@ -154,7 +154,7 @@ The core RCU API is small: void atomic_rcu_set(p, typeof(*p) v); - atomic_rcu_set() is also similar to atomic_mb_set(), and it also + atomic_rcu_set() is similar to atomic_store_release(), though it also makes assumptions on the code that calls it in order to allow a more optimized implementation. diff --git a/docs/devel/reset.rst b/docs/devel/reset.rst new file mode 100644 index 0000000000..abea1102dc --- /dev/null +++ b/docs/devel/reset.rst @@ -0,0 +1,289 @@ + +======================================= +Reset in QEMU: the Resettable interface +======================================= + +The reset of qemu objects is handled using the resettable interface declared +in ``include/hw/resettable.h``. + +This interface allows objects to be grouped (on a tree basis); so that the +whole group can be reset consistently. Each individual member object does not +have to care about others; in particular, problems of order (which object is +reset first) are addressed. + +As of now DeviceClass and BusClass implement this interface. + + +Triggering reset +---------------- + +This section documents the APIs which "users" of a resettable object should use +to control it. All resettable control functions must be called while holding +the iothread lock. + +You can apply a reset to an object using ``resettable_assert_reset()``. You need +to call ``resettable_release_reset()`` to release the object from reset. To +instantly reset an object, without keeping it in reset state, just call +``resettable_reset()``. These functions take two parameters: a pointer to the +object to reset and a reset type. + +Several types of reset will be supported. For now only cold reset is defined; +others may be added later. The Resettable interface handles reset types with an +enum: + +``RESET_TYPE_COLD`` + Cold reset is supported by every resettable object. In QEMU, it means we reset + to the initial state corresponding to the start of QEMU; this might differ + from what is a real hardware cold reset. It differs from other resets (like + warm or bus resets) which may keep certain parts untouched. + +Calling ``resettable_reset()`` is equivalent to calling +``resettable_assert_reset()`` then ``resettable_release_reset()``. It is +possible to interleave multiple calls to these three functions. There may +be several reset sources/controllers of a given object. The interface handles +everything and the different reset controllers do not need to know anything +about each others. The object will leave reset state only when each other +controllers end their reset operation. This point is handled internally by +maintaining a count of in-progress resets; it is crucial to call +``resettable_release_reset()`` one time and only one time per +``resettable_assert_reset()`` call. + +For now migration of a device or bus in reset is not supported. Care must be +taken not to delay ``resettable_release_reset()`` after its +``resettable_assert_reset()`` counterpart. + +Note that, since resettable is an interface, the API takes a simple Object as +parameter. Still, it is a programming error to call a resettable function on a +non-resettable object and it will trigger a run time assert error. Since most +calls to resettable interface are done through base class functions, such an +error is not likely to happen. + +For Devices and Buses, the following helper functions exist: + +- ``device_cold_reset()`` +- ``bus_cold_reset()`` + +These are simple wrappers around resettable_reset() function; they only cast the +Device or Bus into an Object and pass the cold reset type. When possible +prefer to use these functions instead of ``resettable_reset()``. + +Device and bus functions co-exist because there can be semantic differences +between resetting a bus and resetting the controller bridge which owns it. +For example, consider a SCSI controller. Resetting the controller puts all +its registers back to what reset state was as well as reset everything on the +SCSI bus, whereas resetting just the SCSI bus only resets everything that's on +it but not the controller. + + +Multi-phase mechanism +--------------------- + +This section documents the internals of the resettable interface. + +The resettable interface uses a multi-phase system to relieve objects and +machines from reset ordering problems. To address this, the reset operation +of an object is split into three well defined phases. + +When resetting several objects (for example the whole machine at simulation +startup), all first phases of all objects are executed, then all second phases +and then all third phases. + +The three phases are: + +1. The **enter** phase is executed when the object enters reset. It resets only + local state of the object; it must not do anything that has a side-effect + on other objects, such as raising or lowering a qemu_irq line or reading or + writing guest memory. + +2. The **hold** phase is executed for entry into reset, once every object in the + group which is being reset has had its *enter* phase executed. At this point + devices can do actions that affect other objects. + +3. The **exit** phase is executed when the object leaves the reset state. + Actions affecting other objects are permitted. + +As said in previous section, the interface maintains a count of reset. This +count is used to ensure phases are executed only when required. *enter* and +*hold* phases are executed only when asserting reset for the first time +(if an object is already in reset state when calling +``resettable_assert_reset()`` or ``resettable_reset()``, they are not +executed). +The *exit* phase is executed only when the last reset operation ends. Therefore +the object does not need to care how many of reset controllers it has and how +many of them have started a reset. + + +Handling reset in a resettable object +------------------------------------- + +This section documents the APIs that an implementation of a resettable object +must provide and what functions it has access to. It is intended for people +who want to implement or convert a class which has the resettable interface; +for example when specializing an existing device or bus. + +Methods to implement +.................... + +Three methods should be defined or left empty. Each method corresponds to a +phase of the reset; they are name ``phases.enter()``, ``phases.hold()`` and +``phases.exit()``. They all take the object as parameter. The *enter* method +also take the reset type as second parameter. + +When extending an existing class, these methods may need to be extended too. +The ``resettable_class_set_parent_phases()`` class function may be used to +backup parent class methods. + +Here follows an example to implement reset for a Device which sets an IO while +in reset. + +:: + + static void mydev_reset_enter(Object *obj, ResetType type) + { + MyDevClass *myclass = MYDEV_GET_CLASS(obj); + MyDevState *mydev = MYDEV(obj); + /* call parent class enter phase */ + if (myclass->parent_phases.enter) { + myclass->parent_phases.enter(obj, type); + } + /* initialize local state only */ + mydev->var = 0; + } + + static void mydev_reset_hold(Object *obj) + { + MyDevClass *myclass = MYDEV_GET_CLASS(obj); + MyDevState *mydev = MYDEV(obj); + /* call parent class hold phase */ + if (myclass->parent_phases.hold) { + myclass->parent_phases.hold(obj); + } + /* set an IO */ + qemu_set_irq(mydev->irq, 1); + } + + static void mydev_reset_exit(Object *obj) + { + MyDevClass *myclass = MYDEV_GET_CLASS(obj); + MyDevState *mydev = MYDEV(obj); + /* call parent class exit phase */ + if (myclass->parent_phases.exit) { + myclass->parent_phases.exit(obj); + } + /* clear an IO */ + qemu_set_irq(mydev->irq, 0); + } + + typedef struct MyDevClass { + MyParentClass parent_class; + /* to store eventual parent reset methods */ + ResettablePhases parent_phases; + } MyDevClass; + + static void mydev_class_init(ObjectClass *class, void *data) + { + MyDevClass *myclass = MYDEV_CLASS(class); + ResettableClass *rc = RESETTABLE_CLASS(class); + resettable_class_set_parent_reset_phases(rc, + mydev_reset_enter, + mydev_reset_hold, + mydev_reset_exit, + &myclass->parent_phases); + } + +In the above example, we override all three phases. It is possible to override +only some of them by passing NULL instead of a function pointer to +``resettable_class_set_parent_reset_phases()``. For example, the following will +only override the *enter* phase and leave *hold* and *exit* untouched:: + + resettable_class_set_parent_reset_phases(rc, mydev_reset_enter, + NULL, NULL, + &myclass->parent_phases); + +This is equivalent to providing a trivial implementation of the hold and exit +phases which does nothing but call the parent class's implementation of the +phase. + +Polling the reset state +....................... + +Resettable interface provides the ``resettable_is_in_reset()`` function. +This function returns true if the object parameter is currently under reset. + +An object is under reset from the beginning of the *init* phase to the end of +the *exit* phase. During all three phases, the function will return that the +object is in reset. + +This function may be used if the object behavior has to be adapted +while in reset state. For example if a device has an irq input, +it will probably need to ignore it while in reset; then it can for +example check the reset state at the beginning of the irq callback. + +Note that until migration of the reset state is supported, an object +should not be left in reset. So apart from being currently executing +one of the reset phases, the only cases when this function will return +true is if an external interaction (like changing an io) is made during +*hold* or *exit* phase of another object in the same reset group. + +Helpers ``device_is_in_reset()`` and ``bus_is_in_reset()`` are also provided +for devices and buses and should be preferred. + + +Base class handling of reset +---------------------------- + +This section documents parts of the reset mechanism that you only need to know +about if you are extending it to work with a new base class other than +DeviceClass or BusClass, or maintaining the existing code in those classes. Most +people can ignore it. + +Methods to implement +.................... + +There are two other methods that need to exist in a class implementing the +interface: ``get_state()`` and ``child_foreach()``. + +``get_state()`` is simple. *resettable* is an interface and, as a consequence, +does not have any class state structure. But in order to factorize the code, we +need one. This method must return a pointer to ``ResettableState`` structure. +The structure must be allocated by the base class; preferably it should be +located inside the object instance structure. + +``child_foreach()`` is more complex. It should execute the given callback on +every reset child of the given resettable object. All children must be +resettable too. Additional parameters (a reset type and an opaque pointer) must +be passed to the callback too. + +In ``DeviceClass`` and ``BusClass`` the ``ResettableState`` is located +``DeviceState`` and ``BusState`` structure. ``child_foreach()`` is implemented +to follow the bus hierarchy; for a bus, it calls the function on every child +device; for a device, it calls the function on every bus child. When we reset +the main system bus, we reset the whole machine bus tree. + +Changing a resettable parent +............................ + +One thing which should be taken care of by the base class is handling reset +hierarchy changes. + +The reset hierarchy is supposed to be static and built during machine creation. +But there are actually some exceptions. To cope with this, the resettable API +provides ``resettable_change_parent()``. This function allows to set, update or +remove the parent of a resettable object after machine creation is done. As +parameters, it takes the object being moved, the old parent if any and the new +parent if any. + +This function can be used at any time when not in a reset operation. During +a reset operation it must be used only in *hold* phase. Using it in *enter* or +*exit* phase is an error. +Also it should not be used during machine creation, although it is harmless to +do so: the function is a no-op as long as old and new parent are NULL or not +in reset. + +There is currently 2 cases where this function is used: + +1. *device hotplug*; it means a new device is introduced on a live bus. + +2. *hot bus change*; it means an existing live device is added, moved or + removed in the bus hierarchy. At the moment, it occurs only in the raspi + machines for changing the sdbus used by sd card. diff --git a/docs/devel/s390-dasd-ipl.txt b/docs/devel/s390-dasd-ipl.rst similarity index 51% rename from docs/devel/s390-dasd-ipl.txt rename to docs/devel/s390-dasd-ipl.rst index 9107e048e4..2529eb5f54 100644 --- a/docs/devel/s390-dasd-ipl.txt +++ b/docs/devel/s390-dasd-ipl.rst @@ -1,49 +1,55 @@ -***************************** -***** s390 hardware IPL ***** -***************************** +Booting from real channel-attached devices on s390x +=================================================== + +s390 hardware IPL +----------------- The s390 hardware IPL process consists of the following steps. -1. A READ IPL ccw is constructed in memory location 0x0. - This ccw, by definition, reads the IPL1 record which is located on the disk - at cylinder 0 track 0 record 1. Note that the chain flag is on in this ccw - so when it is complete another ccw will be fetched and executed from memory - location 0x08. +1. A READ IPL ccw is constructed in memory location ``0x0``. + This ccw, by definition, reads the IPL1 record which is located on the disk + at cylinder 0 track 0 record 1. Note that the chain flag is on in this ccw + so when it is complete another ccw will be fetched and executed from memory + location ``0x08``. -2. Execute the Read IPL ccw at 0x00, thereby reading IPL1 data into 0x00. - IPL1 data is 24 bytes in length and consists of the following pieces of - information: [psw][read ccw][tic ccw]. When the machine executes the Read - IPL ccw it read the 24-bytes of IPL1 to be read into memory starting at - location 0x0. Then the ccw program at 0x08 which consists of a read - ccw and a tic ccw is automatically executed because of the chain flag from - the original READ IPL ccw. The read ccw will read the IPL2 data into memory - and the TIC (Transfer In Channel) will transfer control to the channel - program contained in the IPL2 data. The TIC channel command is the - equivalent of a branch/jump/goto instruction for channel programs. - NOTE: The ccws in IPL1 are defined by the architecture to be format 0. +2. Execute the Read IPL ccw at ``0x00``, thereby reading IPL1 data into ``0x00``. + IPL1 data is 24 bytes in length and consists of the following pieces of + information: ``[psw][read ccw][tic ccw]``. When the machine executes the Read + IPL ccw it read the 24-bytes of IPL1 to be read into memory starting at + location ``0x0``. Then the ccw program at ``0x08`` which consists of a read + ccw and a tic ccw is automatically executed because of the chain flag from + the original READ IPL ccw. The read ccw will read the IPL2 data into memory + and the TIC (Transfer In Channel) will transfer control to the channel + program contained in the IPL2 data. The TIC channel command is the + equivalent of a branch/jump/goto instruction for channel programs. + + NOTE: The ccws in IPL1 are defined by the architecture to be format 0. 3. Execute IPL2. - The TIC ccw instruction at the end of the IPL1 channel program will begin - the execution of the IPL2 channel program. IPL2 is stage-2 of the boot - process and will contain a larger channel program than IPL1. The point of - IPL2 is to find and load either the operating system or a small program that - loads the operating system from disk. At the end of this step all or some of - the real operating system is loaded into memory and we are ready to hand - control over to the guest operating system. At this point the guest - operating system is entirely responsible for loading any more data it might - need to function. NOTE: The IPL2 channel program might read data into memory - location 0 thereby overwriting the IPL1 psw and channel program. This is ok - as long as the data placed in location 0 contains a psw whose instruction - address points to the guest operating system code to execute at the end of - the IPL/boot process. - NOTE: The ccws in IPL2 are defined by the architecture to be format 0. + The TIC ccw instruction at the end of the IPL1 channel program will begin + the execution of the IPL2 channel program. IPL2 is stage-2 of the boot + process and will contain a larger channel program than IPL1. The point of + IPL2 is to find and load either the operating system or a small program that + loads the operating system from disk. At the end of this step all or some of + the real operating system is loaded into memory and we are ready to hand + control over to the guest operating system. At this point the guest + operating system is entirely responsible for loading any more data it might + need to function. + + NOTE: The IPL2 channel program might read data into memory + location ``0x0`` thereby overwriting the IPL1 psw and channel program. This is ok + as long as the data placed in location ``0x0`` contains a psw whose instruction + address points to the guest operating system code to execute at the end of + the IPL/boot process. + + NOTE: The ccws in IPL2 are defined by the architecture to be format 0. 4. Start executing the guest operating system. - The psw that was loaded into memory location 0 as part of the ipl process - should contain the needed flags for the operating system we have loaded. The - psw's instruction address will point to the location in memory where we want - to start executing the operating system. This psw is loaded (via LPSW - instruction) causing control to be passed to the operating system code. + The psw that was loaded into memory location ``0x0`` as part of the ipl process + should contain the needed flags for the operating system we have loaded. The + psw's instruction address will point to the location in memory where we want + to start executing the operating system. This psw is loaded (via LPSW + instruction) causing control to be passed to the operating system code. In a non-virtualized environment this process, handled entirely by the hardware, is kicked off by the user initiating a "Load" procedure from the hardware @@ -54,18 +60,17 @@ written immediately after the special "Read IPL" ccw, the IPL1 channel program will be executed immediately (the special read ccw has the chaining bit turned on). The TIC at the end of the IPL1 channel program will cause the IPL2 channel program to be executed automatically. After this sequence completes the "Load" -procedure then loads the psw from 0x0. +procedure then loads the psw from ``0x0``. -********************************************************** -***** How this all pertains to QEMU (and the kernel) ***** -********************************************************** +How this all pertains to QEMU (and the kernel) +---------------------------------------------- In theory we should merely have to do the following to IPL/boot a guest operating system from a DASD device: -1. Place a "Read IPL" ccw into memory location 0x0 with chaining bit on. -2. Execute channel program at 0x0. -3. LPSW 0x0. +1. Place a "Read IPL" ccw into memory location ``0x0`` with chaining bit on. +2. Execute channel program at ``0x0``. +3. LPSW ``0x0``. However, our emulation of the machine's channel program logic within the kernel is missing one key feature that is required for this process to work: @@ -89,32 +94,31 @@ Lastly, in some cases (the zipl bootloader for example) the IPL2 program also transfers control to another channel program segment immediately after reading it from the disk. So we need to be able to handle this case. -************************** -***** What QEMU does ***** -************************** +What QEMU does +-------------- Since we are forced to live with prefetch we cannot use the very simple IPL procedure we defined in the preceding section. So we compensate by doing the following. -1. Place "Read IPL" ccw into memory location 0x0, but turn off chaining bit. -2. Execute "Read IPL" at 0x0. +1. Place "Read IPL" ccw into memory location ``0x0``, but turn off chaining bit. +2. Execute "Read IPL" at ``0x0``. - So now IPL1's psw is at 0x0 and IPL1's channel program is at 0x08. + So now IPL1's psw is at ``0x0`` and IPL1's channel program is at ``0x08``. -4. Write a custom channel program that will seek to the IPL2 record and then +3. Write a custom channel program that will seek to the IPL2 record and then execute the READ and TIC ccws from IPL1. Normally the seek is not required because after reading the IPL1 record the disk is automatically positioned to read the very next record which will be IPL2. But since we are not reading both IPL1 and IPL2 as part of the same channel program we must manually set the position. -5. Grab the target address of the TIC instruction from the IPL1 channel program. +4. Grab the target address of the TIC instruction from the IPL1 channel program. This address is where the IPL2 channel program starts. Now IPL2 is loaded into memory somewhere, and we know the address. -6. Execute the IPL2 channel program at the address obtained in step #5. +5. Execute the IPL2 channel program at the address obtained in step #4. Because this channel program can be dynamic, we must use a special algorithm that detects a READ immediately followed by a TIC and breaks the ccw chain @@ -126,8 +130,9 @@ following. channel program from executing properly. Now the operating system code is loaded somewhere in guest memory and the psw - in memory location 0x0 will point to entry code for the guest operating + in memory location ``0x0`` will point to entry code for the guest operating system. -7. LPSW 0x0. +6. LPSW ``0x0`` + LPSW transfers control to the guest operating system and we're done. diff --git a/docs/devel/stable-process.rst b/docs/devel/stable-process.rst index 98736a9ea4..e541b983fa 100644 --- a/docs/devel/stable-process.rst +++ b/docs/devel/stable-process.rst @@ -18,8 +18,10 @@ What should go into a stable release? ------------------------------------- Generally, the following patches are considered stable material: -- Patches that fix severe issues, like fixes for CVEs -- Patches that fix regressions + +* Patches that fix severe issues, like fixes for CVEs + +* Patches that fix regressions If you think the patch would be important for users of the current release (or for a distribution picking fixes), it is usually a good candidate diff --git a/docs/devel/tcg-plugins.rst b/docs/devel/tcg-plugins.rst index 718eef00f2..a05990906c 100644 --- a/docs/devel/tcg-plugins.rst +++ b/docs/devel/tcg-plugins.rst @@ -51,8 +51,17 @@ about how QEMU's translation works to the plugins. While there are conceptions such as translation time and translation blocks the details are opaque to plugins. The plugin is able to query select details of instructions and system configuration only through the -exported *qemu_plugin* functions. The types used to describe -instructions and events are opaque to the plugins themselves. +exported *qemu_plugin* functions. + +Query Handle Lifetime +--------------------- + +Each callback provides an opaque anonymous information handle which +can usually be further queried to find out information about a +translation, instruction or operation. The handles themselves are only +valid during the lifetime of the callback so it is important that any +information that is needed is extracted during the callback and saved +by the plugin. Usage ===== diff --git a/docs/devel/tcg.rst b/docs/devel/tcg.rst index 4956a30a4e..4ebde44b9d 100644 --- a/docs/devel/tcg.rst +++ b/docs/devel/tcg.rst @@ -83,7 +83,7 @@ memory until the end of the translation block. This is done for internal emulation state that is rarely accessed directly by the program and/or changes very often throughout the execution of a translation block---this includes condition codes on x86, delay slots on SPARC, conditional execution on -ARM, and so on. This state is stored for each target instruction, and +Arm, and so on. This state is stored for each target instruction, and looked up on exceptions. MMU emulation diff --git a/docs/devel/testing.rst b/docs/devel/testing.rst index 8e981e062d..770a987ea4 100644 --- a/docs/devel/testing.rst +++ b/docs/devel/testing.rst @@ -16,8 +16,8 @@ The usual way to run these tests is: make check -which includes QAPI schema tests, unit tests, and QTests. Different sub-types -of "make check" tests will be explained below. +which includes QAPI schema tests, unit tests, QTests and some iotests. +Different sub-types of "make check" tests will be explained below. Before running tests, it is best to build QEMU programs first. Some tests expect the executables to exist and will fail with obscure messages if they @@ -79,8 +79,8 @@ QTest cases can be executed with make check-qtest -The QTest library is implemented by ``tests/libqtest.c`` and the API is defined -in ``tests/libqtest.h``. +The QTest library is implemented by ``tests/qtest/libqtest.c`` and the API is +defined in ``tests/qtest/libqtest.h``. Consider adding a new QTest case when you are introducing a new virtual hardware, or extending one if you are adding functionalities to an existing @@ -94,20 +94,20 @@ libqos instead of directly calling into libqtest. Steps to add a new QTest case are: 1. Create a new source file for the test. (More than one file can be added as - necessary.) For example, ``tests/test-foo-device.c``. + necessary.) For example, ``tests/qtest/foo-test.c``. 2. Write the test code with the glib and libqtest/libqos API. See also existing tests and the library headers for reference. -3. Register the new test in ``tests/Makefile.include``. Add the test executable - name to an appropriate ``check-qtest-*-y`` variable. For example: +3. Register the new test in ``tests/qtest/Makefile.include``. Add the test + executable name to an appropriate ``check-qtest-*-y`` variable. For example: - ``check-qtest-generic-y = tests/test-foo-device$(EXESUF)`` + ``check-qtest-generic-y = tests/qtest/foo-test$(EXESUF)`` 4. Add object dependencies of the executable in the Makefile, including the test source file(s) and other interesting objects. For example: - ``tests/test-foo-device$(EXESUF): tests/test-foo-device.o $(libqos-obj-y)`` + ``tests/qtest/foo-test$(EXESUF): tests/qtest/foo-test.o $(libqos-obj-y)`` Debugging a QTest failure is slightly harder than the unit test because the tests look up QEMU program names in the environment variables, such as @@ -152,8 +152,9 @@ parser (either fixing a bug or extending/modifying the syntax). To do this: check-block ----------- -``make check-block`` is a legacy command to invoke block layer iotests and is -rarely used. See "QEMU iotests" section below for more information. +``make check-block`` runs a subset of the block layer iotests (the tests that +are in the "auto" group in ``tests/qemu-iotests/group``). +See the "QEMU iotests" section below for more information. GCC gcov support ---------------- @@ -418,13 +419,15 @@ access, so they SHOULD NOT be exposed to external interfaces if you are concerned about attackers taking control of the guest and potentially exploiting a QEMU security bug to compromise the host. -QEMU binary ------------ +QEMU binaries +------------- By default, qemu-system-x86_64 is searched in $PATH to run the guest. If there isn't one, or if it is older than 2.10, the test won't work. In this case, provide the QEMU binary in env var: ``QEMU=/path/to/qemu-2.10+``. +Likewise the path to qemu-img can be set in QEMU_IMG environment variable. + Make jobs --------- @@ -746,6 +749,17 @@ name. If one is not given explicitly, it will either be set to ``None``, or, if the test is tagged with one (and only one) ``:avocado: tags=arch:VALUE`` tag, it will be set to ``VALUE``. +machine +~~~~~~~ + +The machine type that will be set to all QEMUMachine instances created +by the test. + +The ``machine`` attribute will be set to the test parameter of the same +name. If one is not given explicitly, it will either be set to +``None``, or, if the test is tagged with one (and only one) +``:avocado: tags=machine:VALUE`` tag, it will be set to ``VALUE``. + qemu_bin ~~~~~~~~ @@ -781,6 +795,13 @@ architecture of a kernel or disk image to boot a VM with. This parameter has a direct relation with the ``arch`` attribute. If not given, it will default to None. +machine +~~~~~~~ + +The machine type that will be set to all QEMUMachine instances created +by the test. + + qemu_bin ~~~~~~~~ diff --git a/docs/devel/tracing.txt b/docs/devel/tracing.txt index 8c0376fefa..cb5f685de9 100644 --- a/docs/devel/tracing.txt +++ b/docs/devel/tracing.txt @@ -342,8 +342,10 @@ edit the "trace-events-all" file). In addition, there might be cases where relatively complex computations must be performed to generate values that are only used as arguments for a trace -function. In these cases you can use the macro 'TRACE_${EVENT_NAME}_ENABLED' to -guard such computations and avoid its compilation when the event is disabled: +function. In these cases you can use 'trace_event_get_state_backends()' to +guard such computations, so they are skipped if the event has been either +compile-time disabled or run-time disabled. If the event is compile-time +disabled, this check will have no performance impact. #include "trace.h" /* needed for trace event prototype */ @@ -356,7 +358,7 @@ guard such computations and avoid its compilation when the event is disabled: align = getpagesize(); } ptr = qemu_memalign(align, size); - if (TRACE_QEMU_VMALLOC_ENABLED) { /* preprocessor macro */ + if (trace_event_get_state_backends(TRACE_QEMU_VMALLOC)) { void *complex; /* some complex computations to produce the 'complex' value */ trace_qemu_vmalloc(size, ptr, complex); @@ -364,10 +366,6 @@ guard such computations and avoid its compilation when the event is disabled: return ptr; } -You can check both if the event has been disabled and is dynamically enabled at -the same time using the 'trace_event_get_state_backends' routine (see header -"trace/control.h" for more information). - === "tcg" === Guest code generated by TCG can be traced by defining an event with the "tcg" diff --git a/docs/index.html.in b/docs/index.html.in new file mode 100644 index 0000000000..e9a160384c --- /dev/null +++ b/docs/index.html.in @@ -0,0 +1,19 @@ + + + + + QEMU @@VERSION@@ Documentation + + +

QEMU @@VERSION@@ Documentation

+ + + diff --git a/docs/index.rst b/docs/index.rst index baa5791c17..763e3d0426 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -10,7 +10,9 @@ Welcome to QEMU's documentation! :maxdepth: 2 :caption: Contents: + system/index + user/index + tools/index interop/index - devel/index specs/index - + devel/index diff --git a/docs/interop/conf.py b/docs/interop/conf.py index e87b8c22be..42ce7e3d36 100644 --- a/docs/interop/conf.py +++ b/docs/interop/conf.py @@ -18,5 +18,5 @@ html_theme_options['description'] = u'System Emulation Management and Interopera # (source start file, name, description, authors, manual section). man_pages = [ ('qemu-ga', 'qemu-ga', u'QEMU Guest Agent', - ['Michael Roth '], 8) + ['Michael Roth '], 8), ] diff --git a/docs/interop/dbus-vmstate.rst b/docs/interop/dbus-vmstate.rst new file mode 100644 index 0000000000..1d719c1c60 --- /dev/null +++ b/docs/interop/dbus-vmstate.rst @@ -0,0 +1,74 @@ +============= +D-Bus VMState +============= + +Introduction +============ + +The QEMU dbus-vmstate object's aim is to migrate helpers' data running +on a QEMU D-Bus bus. (refer to the :doc:`dbus` document for +some recommendations on D-Bus usage) + +Upon migration, QEMU will go through the queue of +``org.qemu.VMState1`` D-Bus name owners and query their ``Id``. It +must be unique among the helpers. + +It will then save arbitrary data of each Id to be transferred in the +migration stream and restored/loaded at the corresponding destination +helper. + +For now, the data amount to be transferred is arbitrarily limited to +1Mb. The state must be saved quickly (a fraction of a second). (D-Bus +imposes a time limit on reply anyway, and migration would fail if data +isn't given quickly enough.) + +dbus-vmstate object can be configured with the expected list of +helpers by setting its ``id-list`` property, with a comma-separated +``Id`` list. + +Interface +========= + +On object path ``/org/qemu/VMState1``, the following +``org.qemu.VMState1`` interface should be implemented: + +.. code:: xml + + + + + + + + + + + +"Id" property +------------- + +A string that identifies the helper uniquely. (maximum 256 bytes +including terminating NUL byte) + +.. note:: + + The helper ID namespace is a separate namespace. In particular, it is not + related to QEMU "id" used in -object/-device objects. + +Load(in u8[] bytes) method +-------------------------- + +The method called on destination with the state to restore. + +The helper may be initially started in a waiting state (with +an --incoming argument for example), and it may resume on success. + +An error may be returned to the caller. + +Save(out u8[] bytes) method +--------------------------- + +The method called on the source to get the current state to be +migrated. The helper should continue to run normally. + +An error may be returned to the caller. diff --git a/docs/interop/dbus.rst b/docs/interop/dbus.rst new file mode 100644 index 0000000000..76a5bde625 --- /dev/null +++ b/docs/interop/dbus.rst @@ -0,0 +1,110 @@ +===== +D-Bus +===== + +Introduction +============ + +QEMU may be running with various helper processes involved: + - vhost-user* processes (gpu, virtfs, input, etc...) + - TPM emulation (or other devices) + - user networking (slirp) + - network services (DHCP/DNS, samba/ftp etc) + - background tasks (compression, streaming etc) + - client UI + - admin & cli + +Having several processes allows stricter security rules, as well as +greater modularity. + +While QEMU itself uses QMP as primary IPC (and Spice/VNC for remote +display), D-Bus is the de facto IPC of choice on Unix systems. The +wire format is machine friendly, good bindings exist for various +languages, and there are various tools available. + +Using a bus, helper processes can discover and communicate with each +other easily, without going through QEMU. The bus topology is also +easier to apprehend and debug than a mesh. However, it is wise to +consider the security aspects of it. + +Security +======== + +A QEMU D-Bus bus should be private to a single VM. Thus, only +cooperative tasks are running on the same bus to serve the VM. + +D-Bus, the protocol and standard, doesn't have mechanisms to enforce +security between peers once the connection is established. Peers may +have additional mechanisms to enforce security rules, based for +example on UNIX credentials. + +The daemon can control which peers can send/recv messages using +various metadata attributes, however, this is alone is not generally +sufficient to make the deployment secure. The semantics of the actual +methods implemented using D-Bus are just as critical. Peers need to +carefully validate any information they received from a peer with a +different trust level. + +dbus-daemon policy +------------------ + +dbus-daemon can enforce various policies based on the UID/GID of the +processes that are connected to it. It is thus a good idea to run +helpers as different UID from QEMU and set appropriate policies. + +Depending on the use case, you may choose different scenarios: + + - Everything the same UID + + - Convenient for developers + - Improved reliability - crash of one part doens't take + out entire VM + - No security benefit over traditional QEMU, unless additional + unless additional controls such as SELinux or AppArmor are + applied + + - Two UIDs, one for QEMU, one for dbus & helpers + + - Moderately improved user based security isolation + + - Many UIDs, one for QEMU one for dbus and one for each helpers + + - Best user based security isolation + - Complex to manager distinct UIDs needed for each VM + +For example, to allow only ``qemu`` user to talk to ``qemu-helper`` +``org.qemu.Helper1`` service, a dbus-daemon policy may contain: + +.. code:: xml + + + + + + + + + + + +dbus-daemon can also perfom SELinux checks based on the security +context of the source and the target. For example, ``virtiofs_t`` +could be allowed to send a message to ``svirt_t``, but ``virtiofs_t`` +wouldn't be allowed to send a message to ``virtiofs_t``. + +See dbus-daemon man page for details. + +Guidelines +========== + +When implementing new D-Bus interfaces, it is recommended to follow +the "D-Bus API Design Guidelines": +https://dbus.freedesktop.org/doc/dbus-api-design.html + +The "org.qemu.*" prefix is reserved for services implemented & +distributed by the QEMU project. + +QEMU Interfaces +=============== + +:doc:`dbus-vmstate` diff --git a/docs/interop/firmware.json b/docs/interop/firmware.json index 8ffb7856d2..240f565397 100644 --- a/docs/interop/firmware.json +++ b/docs/interop/firmware.json @@ -27,8 +27,7 @@ # # @openfirmware: The interface is defined by the (historical) IEEE # 1275-1994 standard. Examples for firmware projects that -# provide this interface are: OpenBIOS, OpenHackWare, -# SLOF. +# provide this interface are: OpenBIOS and SLOF. # # @uboot: Firmware interface defined by the U-Boot project. # diff --git a/docs/interop/index.rst b/docs/interop/index.rst index 3e33fb5933..049387ac6d 100644 --- a/docs/interop/index.rst +++ b/docs/interop/index.rst @@ -13,6 +13,8 @@ Contents: :maxdepth: 2 bitmaps + dbus + dbus-vmstate live-block-operations pr-helper qemu-ga diff --git a/docs/interop/qcow2.txt b/docs/interop/qcow2.txt index af5711e533..640e0eca40 100644 --- a/docs/interop/qcow2.txt +++ b/docs/interop/qcow2.txt @@ -79,9 +79,9 @@ The first cluster of a qcow2 image contains the file header: Offset into the image file at which the snapshot table starts. Must be aligned to a cluster boundary. -If the version is 3 or higher, the header has the following additional fields. -For version 2, the values are assumed to be zero, unless specified otherwise -in the description of a field. +For version 2, the header is exactly 72 bytes in length, and finishes here. +For version 3 or higher, the header length is at least 104 bytes, including +the next fields through header_length. 72 - 79: incompatible_features Bitmask of incompatible features. An implementation must @@ -109,7 +109,12 @@ in the description of a field. An External Data File Name header extension may be present if this bit is set. - Bits 3-63: Reserved (set to 0) + Bit 3: Compression type bit. If this bit is set, + a non-default compression is used for compressed + clusters. The compression_type field must be + present and not zero. + + Bits 4-63: Reserved (set to 0) 80 - 87: compatible_features Bitmask of compatible features. An implementation can @@ -138,7 +143,8 @@ in the description of a field. bit is unset, the bitmaps extension data must be considered inconsistent. - Bit 1: If this bit is set, the external data file can + Bit 1: Raw external data bit + If this bit is set, the external data file can be read as a consistent standalone raw image without looking at the qcow2 metadata. @@ -164,6 +170,57 @@ in the description of a field. 100 - 103: header_length Length of the header structure in bytes. For version 2 images, the length is always assumed to be 72 bytes. + For version 3 it's at least 104 bytes and must be a multiple + of 8. + + +=== Additional fields (version 3 and higher) === + +In general, these fields are optional and may be safely ignored by the software, +as well as filled by zeros (which is equal to field absence), if software needs +to set field B, but does not care about field A which precedes B. More +formally, additional fields have the following compatibility rules: + +1. If the value of the additional field must not be ignored for correct +handling of the file, it will be accompanied by a corresponding incompatible +feature bit. + +2. If there are no unrecognized incompatible feature bits set, an unknown +additional field may be safely ignored other than preserving its value when +rewriting the image header. + +3. An explicit value of 0 will have the same behavior as when the field is not +present*, if not altered by a specific incompatible bit. + +*. A field is considered not present when header_length is less than or equal +to the field's offset. Also, all additional fields are not present for +version 2. + + 104: compression_type + + Defines the compression method used for compressed clusters. + All compressed clusters in an image use the same compression + type. + + If the incompatible bit "Compression type" is set: the field + must be present and non-zero (which means non-zlib + compression type). Otherwise, this field must not be present + or must be zero (which means zlib). + + Available compression type values: + 0: zlib + + +=== Header padding === + +@header_length must be a multiple of 8, which means that if the end of the last +additional field is not aligned, some padding is needed. This padding must be +zeroed, so that if some existing (or future) additional field will fall into +the padding, it will be interpreted accordingly to point [3.] of the previous +paragraph, i.e. in the same manner as when this field is not present. + + +=== Header extensions === Directly after the image header, optional sections called header extensions can be stored. Each extension has a structure like the following: diff --git a/docs/interop/qemu-ga.rst b/docs/interop/qemu-ga.rst index 1313a4ae1c..3063357bb5 100644 --- a/docs/interop/qemu-ga.rst +++ b/docs/interop/qemu-ga.rst @@ -36,13 +36,14 @@ Options .. option:: -m, --method=METHOD Transport method: one of ``unix-listen``, ``virtio-serial``, or - ``isa-serial`` (``virtio-serial`` is the default). + ``isa-serial``, or ``vsock-listen`` (``virtio-serial`` is the default). .. option:: -p, --path=PATH Device/socket path (the default for virtio-serial is ``/dev/virtio-ports/org.qemu.guest_agent.0``, - the default for isa-serial is ``/dev/ttyS0``) + the default for isa-serial is ``/dev/ttyS0``). Socket addresses for + vsock-listen are written as ``:``. .. option:: -l, --logfile=PATH diff --git a/docs/interop/vhost-user.json b/docs/interop/vhost-user.json index da6aaf51c8..ef8ac5941f 100644 --- a/docs/interop/vhost-user.json +++ b/docs/interop/vhost-user.json @@ -31,6 +31,7 @@ # @rproc-serial: virtio remoteproc serial link # @scsi: virtio scsi # @vsock: virtio vsock transport +# @fs: virtio fs (since 4.2) # # Since: 4.0 ## @@ -50,10 +51,42 @@ 'rpmsg', 'rproc-serial', 'scsi', - 'vsock' + 'vsock', + 'fs' ] } +## +# @VHostUserBackendBlockFeature: +# +# List of vhost user "block" features. +# +# @read-only: The --read-only command line option is supported. +# @blk-file: The --blk-file command line option is supported. +# +# Since: 5.0 +## +{ + 'enum': 'VHostUserBackendBlockFeature', + 'data': [ 'read-only', 'blk-file' ] +} + +## +# @VHostUserBackendCapabilitiesBlock: +# +# Capabilities reported by vhost user "block" backends +# +# @features: list of supported features. +# +# Since: 5.0 +## +{ + 'struct': 'VHostUserBackendCapabilitiesBlock', + 'data': { + 'features': [ 'VHostUserBackendBlockFeature' ] + } +} + ## # @VHostUserBackendInputFeature: # diff --git a/docs/interop/vhost-user.rst b/docs/interop/vhost-user.rst index 7827b710aa..3b1b6602c7 100644 --- a/docs/interop/vhost-user.rst +++ b/docs/interop/vhost-user.rst @@ -2,6 +2,7 @@ Vhost-user Protocol =================== :Copyright: 2014 Virtual Open Systems Sarl. +:Copyright: 2019 Intel Corporation :Licence: This work is licensed under the terms of the GNU GPL, version 2 or later. See the COPYING file in the top-level directory. @@ -279,6 +280,9 @@ If *master* is unable to send the full message or receives a wrong reply it will close the connection. An optional reconnection mechanism can be implemented. +If *slave* detects some error such as incompatible features, it may also +close the connection. This should only happen in exceptional circumstances. + Any protocol extensions are gated by protocol feature bits, which allows full backwards compatibility on both master and slave. As older slaves don't support negotiating protocol features, a feature @@ -315,7 +319,8 @@ it until ring is started, or after it has been stopped. Client must start ring upon receiving a kick (that is, detecting that file descriptor is readable) on the descriptor specified by -``VHOST_USER_SET_VRING_KICK``, and stop ring upon receiving +``VHOST_USER_SET_VRING_KICK`` or receiving the in-band message +``VHOST_USER_VRING_KICK`` if negotiated, and stop ring upon receiving ``VHOST_USER_GET_VRING_BASE``. While processing the rings (whether they are enabled or not), client @@ -563,7 +568,7 @@ For split virtqueue, queue region can be implemented as: uint16_t used_idx; /* Used to track the state of each descriptor in descriptor table */ - DescStateSplit desc[0]; + DescStateSplit desc[]; } QueueRegionSplit; To track inflight I/O, the queue region should be processed as follows: @@ -685,7 +690,7 @@ For packed virtqueue, queue region can be implemented as: uint8_t padding[7]; /* Used to track the state of each descriptor fetched from descriptor ring */ - DescStatePacked desc[0]; + DescStatePacked desc[]; } QueueRegionPacked; To track inflight I/O, the queue region should be processed as follows: @@ -767,24 +772,49 @@ When reconnecting: #. Resubmit inflight ``DescStatePacked`` entries in order of their counter value +In-band notifications +--------------------- + +In some limited situations (e.g. for simulation) it is desirable to +have the kick, call and error (if used) signals done via in-band +messages instead of asynchronous eventfd notifications. This can be +done by negotiating the ``VHOST_USER_PROTOCOL_F_INBAND_NOTIFICATIONS`` +protocol feature. + +Note that due to the fact that too many messages on the sockets can +cause the sending application(s) to block, it is not advised to use +this feature unless absolutely necessary. It is also considered an +error to negotiate this feature without also negotiating +``VHOST_USER_PROTOCOL_F_SLAVE_REQ`` and ``VHOST_USER_PROTOCOL_F_REPLY_ACK``, +the former is necessary for getting a message channel from the slave +to the master, while the latter needs to be used with the in-band +notification messages to block until they are processed, both to avoid +blocking later and for proper processing (at least in the simulation +use case.) As it has no other way of signalling this error, the slave +should close the connection as a response to a +``VHOST_USER_SET_PROTOCOL_FEATURES`` message that sets the in-band +notifications feature flag without the other two. + Protocol features ----------------- .. code:: c - #define VHOST_USER_PROTOCOL_F_MQ 0 - #define VHOST_USER_PROTOCOL_F_LOG_SHMFD 1 - #define VHOST_USER_PROTOCOL_F_RARP 2 - #define VHOST_USER_PROTOCOL_F_REPLY_ACK 3 - #define VHOST_USER_PROTOCOL_F_MTU 4 - #define VHOST_USER_PROTOCOL_F_SLAVE_REQ 5 - #define VHOST_USER_PROTOCOL_F_CROSS_ENDIAN 6 - #define VHOST_USER_PROTOCOL_F_CRYPTO_SESSION 7 - #define VHOST_USER_PROTOCOL_F_PAGEFAULT 8 - #define VHOST_USER_PROTOCOL_F_CONFIG 9 - #define VHOST_USER_PROTOCOL_F_SLAVE_SEND_FD 10 - #define VHOST_USER_PROTOCOL_F_HOST_NOTIFIER 11 - #define VHOST_USER_PROTOCOL_F_INFLIGHT_SHMFD 12 + #define VHOST_USER_PROTOCOL_F_MQ 0 + #define VHOST_USER_PROTOCOL_F_LOG_SHMFD 1 + #define VHOST_USER_PROTOCOL_F_RARP 2 + #define VHOST_USER_PROTOCOL_F_REPLY_ACK 3 + #define VHOST_USER_PROTOCOL_F_MTU 4 + #define VHOST_USER_PROTOCOL_F_SLAVE_REQ 5 + #define VHOST_USER_PROTOCOL_F_CROSS_ENDIAN 6 + #define VHOST_USER_PROTOCOL_F_CRYPTO_SESSION 7 + #define VHOST_USER_PROTOCOL_F_PAGEFAULT 8 + #define VHOST_USER_PROTOCOL_F_CONFIG 9 + #define VHOST_USER_PROTOCOL_F_SLAVE_SEND_FD 10 + #define VHOST_USER_PROTOCOL_F_HOST_NOTIFIER 11 + #define VHOST_USER_PROTOCOL_F_INFLIGHT_SHMFD 12 + #define VHOST_USER_PROTOCOL_F_RESET_DEVICE 13 + #define VHOST_USER_PROTOCOL_F_INBAND_NOTIFICATIONS 14 Master message types -------------------- @@ -946,7 +976,12 @@ Master message types Bits (0-7) of the payload contain the vring index. Bit 8 is the invalid FD flag. This flag is set when there is no file descriptor in the ancillary data. This signals that polling should be used - instead of waiting for a kick. + instead of waiting for the kick. Note that if the protocol feature + ``VHOST_USER_PROTOCOL_F_INBAND_NOTIFICATIONS`` has been negotiated + this message isn't necessary as the ring is also started on the + ``VHOST_USER_VRING_KICK`` message, it may however still be used to + set an event file descriptor (which will be preferred over the + message) or to enable polling. ``VHOST_USER_SET_VRING_CALL`` :id: 13 @@ -959,7 +994,12 @@ Master message types Bits (0-7) of the payload contain the vring index. Bit 8 is the invalid FD flag. This flag is set when there is no file descriptor in the ancillary data. This signals that polling will be used - instead of waiting for the call. + instead of waiting for the call. Note that if the protocol features + ``VHOST_USER_PROTOCOL_F_INBAND_NOTIFICATIONS`` and + ``VHOST_USER_PROTOCOL_F_SLAVE_REQ`` have been negotiated this message + isn't necessary as the ``VHOST_USER_SLAVE_VRING_CALL`` message can be + used, it may however still be used to set an event file descriptor + or to enable polling. ``VHOST_USER_SET_VRING_ERR`` :id: 14 @@ -971,7 +1011,12 @@ Master message types Bits (0-7) of the payload contain the vring index. Bit 8 is the invalid FD flag. This flag is set when there is no file descriptor - in the ancillary data. + in the ancillary data. Note that if the protocol features + ``VHOST_USER_PROTOCOL_F_INBAND_NOTIFICATIONS`` and + ``VHOST_USER_PROTOCOL_F_SLAVE_REQ`` have been negotiated this message + isn't necessary as the ``VHOST_USER_SLAVE_VRING_ERR`` message can be + used, it may however still be used to set an event file descriptor + (which will be preferred over the message). ``VHOST_USER_GET_QUEUE_NUM`` :id: 17 @@ -1190,6 +1235,34 @@ Master message types ancillary data. The GPU protocol is used to inform the master of rendering state and updates. See vhost-user-gpu.rst for details. +``VHOST_USER_RESET_DEVICE`` + :id: 34 + :equivalent ioctl: N/A + :master payload: N/A + :slave payload: N/A + + Ask the vhost user backend to disable all rings and reset all + internal device state to the initial state, ready to be + reinitialized. The backend retains ownership of the device + throughout the reset operation. + + Only valid if the ``VHOST_USER_PROTOCOL_F_RESET_DEVICE`` protocol + feature is set by the backend. + +``VHOST_USER_VRING_KICK`` + :id: 35 + :equivalent ioctl: N/A + :slave payload: vring state description + :master payload: N/A + + When the ``VHOST_USER_PROTOCOL_F_INBAND_NOTIFICATIONS`` protocol + feature has been successfully negotiated, this message may be + submitted by the master to indicate that a buffer was added to + the vring instead of signalling it using the vring's kick file + descriptor or having the slave rely on polling. + + The state.num field is currently reserved and must be set to 0. + Slave message types ------------------- @@ -1246,6 +1319,34 @@ Slave message types ``VHOST_USER_PROTOCOL_F_HOST_NOTIFIER`` protocol feature has been successfully negotiated. +``VHOST_USER_SLAVE_VRING_CALL`` + :id: 4 + :equivalent ioctl: N/A + :slave payload: vring state description + :master payload: N/A + + When the ``VHOST_USER_PROTOCOL_F_INBAND_NOTIFICATIONS`` protocol + feature has been successfully negotiated, this message may be + submitted by the slave to indicate that a buffer was used from + the vring instead of signalling this using the vring's call file + descriptor or having the master relying on polling. + + The state.num field is currently reserved and must be set to 0. + +``VHOST_USER_SLAVE_VRING_ERR`` + :id: 5 + :equivalent ioctl: N/A + :slave payload: vring state description + :master payload: N/A + + When the ``VHOST_USER_PROTOCOL_F_INBAND_NOTIFICATIONS`` protocol + feature has been successfully negotiated, this message may be + submitted by the slave to indicate that an error occurred on the + specific vring, instead of signalling the error file descriptor + set by the master via ``VHOST_USER_SET_VRING_ERR``. + + The state.num field is currently reserved and must be set to 0. + .. _reply_ack: VHOST_USER_PROTOCOL_F_REPLY_ACK @@ -1376,3 +1477,20 @@ Command line options: Enable virgl rendering support. (optional) + +vhost-user-blk +-------------- + +Command line options: + +--blk-file=PATH + + Specify block device or file path. + + (optional) + +--read-only + + Enable read-only. + + (optional) diff --git a/docs/qemu-block-drivers.texi b/docs/qemu-block-drivers.texi deleted file mode 100644 index 2c7ea49c32..0000000000 --- a/docs/qemu-block-drivers.texi +++ /dev/null @@ -1,889 +0,0 @@ -@c man begin SYNOPSIS -QEMU block driver reference manual -@c man end - -@set qemu_system qemu-system-x86_64 - -@c man begin DESCRIPTION - -@node disk_images_formats -@subsection Disk image file formats - -QEMU supports many image file formats that can be used with VMs as well as with -any of the tools (like @code{qemu-img}). This includes the preferred formats -raw and qcow2 as well as formats that are supported for compatibility with -older QEMU versions or other hypervisors. - -Depending on the image format, different options can be passed to -@code{qemu-img create} and @code{qemu-img convert} using the @code{-o} option. -This section describes each format and the options that are supported for it. - -@table @option -@item raw - -Raw disk image format. This format has the advantage of -being simple and easily exportable to all other emulators. If your -file system supports @emph{holes} (for example in ext2 or ext3 on -Linux or NTFS on Windows), then only the written sectors will reserve -space. Use @code{qemu-img info} to know the real size used by the -image or @code{ls -ls} on Unix/Linux. - -Supported options: -@table @code -@item preallocation -Preallocation mode (allowed values: @code{off}, @code{falloc}, @code{full}). -@code{falloc} mode preallocates space for image by calling posix_fallocate(). -@code{full} mode preallocates space for image by writing data to underlying -storage. This data may or may not be zero, depending on the storage location. -@end table - -@item qcow2 -QEMU image format, the most versatile format. Use it to have smaller -images (useful if your filesystem does not supports holes, for example -on Windows), zlib based compression and support of multiple VM -snapshots. - -Supported options: -@table @code -@item compat -Determines the qcow2 version to use. @code{compat=0.10} uses the -traditional image format that can be read by any QEMU since 0.10. -@code{compat=1.1} enables image format extensions that only QEMU 1.1 and -newer understand (this is the default). Amongst others, this includes -zero clusters, which allow efficient copy-on-read for sparse images. - -@item backing_file -File name of a base image (see @option{create} subcommand) -@item backing_fmt -Image format of the base image -@item encryption -This option is deprecated and equivalent to @code{encrypt.format=aes} - -@item encrypt.format - -If this is set to @code{luks}, it requests that the qcow2 payload (not -qcow2 header) be encrypted using the LUKS format. The passphrase to -use to unlock the LUKS key slot is given by the @code{encrypt.key-secret} -parameter. LUKS encryption parameters can be tuned with the other -@code{encrypt.*} parameters. - -If this is set to @code{aes}, the image is encrypted with 128-bit AES-CBC. -The encryption key is given by the @code{encrypt.key-secret} parameter. -This encryption format is considered to be flawed by modern cryptography -standards, suffering from a number of design problems: - -@itemize @minus -@item The AES-CBC cipher is used with predictable initialization vectors based -on the sector number. This makes it vulnerable to chosen plaintext attacks -which can reveal the existence of encrypted data. -@item The user passphrase is directly used as the encryption key. A poorly -chosen or short passphrase will compromise the security of the encryption. -@item In the event of the passphrase being compromised there is no way to -change the passphrase to protect data in any qcow images. The files must -be cloned, using a different encryption passphrase in the new file. The -original file must then be securely erased using a program like shred, -though even this is ineffective with many modern storage technologies. -@end itemize - -The use of this is no longer supported in system emulators. Support only -remains in the command line utilities, for the purposes of data liberation -and interoperability with old versions of QEMU. The @code{luks} format -should be used instead. - -@item encrypt.key-secret - -Provides the ID of a @code{secret} object that contains the passphrase -(@code{encrypt.format=luks}) or encryption key (@code{encrypt.format=aes}). - -@item encrypt.cipher-alg - -Name of the cipher algorithm and key length. Currently defaults -to @code{aes-256}. Only used when @code{encrypt.format=luks}. - -@item encrypt.cipher-mode - -Name of the encryption mode to use. Currently defaults to @code{xts}. -Only used when @code{encrypt.format=luks}. - -@item encrypt.ivgen-alg - -Name of the initialization vector generator algorithm. Currently defaults -to @code{plain64}. Only used when @code{encrypt.format=luks}. - -@item encrypt.ivgen-hash-alg - -Name of the hash algorithm to use with the initialization vector generator -(if required). Defaults to @code{sha256}. Only used when @code{encrypt.format=luks}. - -@item encrypt.hash-alg - -Name of the hash algorithm to use for PBKDF algorithm -Defaults to @code{sha256}. Only used when @code{encrypt.format=luks}. - -@item encrypt.iter-time - -Amount of time, in milliseconds, to use for PBKDF algorithm per key slot. -Defaults to @code{2000}. Only used when @code{encrypt.format=luks}. - -@item cluster_size -Changes the qcow2 cluster size (must be between 512 and 2M). Smaller cluster -sizes can improve the image file size whereas larger cluster sizes generally -provide better performance. - -@item preallocation -Preallocation mode (allowed values: @code{off}, @code{metadata}, @code{falloc}, -@code{full}). An image with preallocated metadata is initially larger but can -improve performance when the image needs to grow. @code{falloc} and @code{full} -preallocations are like the same options of @code{raw} format, but sets up -metadata also. - -@item lazy_refcounts -If this option is set to @code{on}, reference count updates are postponed with -the goal of avoiding metadata I/O and improving performance. This is -particularly interesting with @option{cache=writethrough} which doesn't batch -metadata updates. The tradeoff is that after a host crash, the reference count -tables must be rebuilt, i.e. on the next open an (automatic) @code{qemu-img -check -r all} is required, which may take some time. - -This option can only be enabled if @code{compat=1.1} is specified. - -@item nocow -If this option is set to @code{on}, it will turn off COW of the file. It's only -valid on btrfs, no effect on other file systems. - -Btrfs has low performance when hosting a VM image file, even more when the guest -on the VM also using btrfs as file system. Turning off COW is a way to mitigate -this bad performance. Generally there are two ways to turn off COW on btrfs: -a) Disable it by mounting with nodatacow, then all newly created files will be -NOCOW. b) For an empty file, add the NOCOW file attribute. That's what this option -does. - -Note: this option is only valid to new or empty files. If there is an existing -file which is COW and has data blocks already, it couldn't be changed to NOCOW -by setting @code{nocow=on}. One can issue @code{lsattr filename} to check if -the NOCOW flag is set or not (Capital 'C' is NOCOW flag). - -@end table - -@item qed -Old QEMU image format with support for backing files and compact image files -(when your filesystem or transport medium does not support holes). - -When converting QED images to qcow2, you might want to consider using the -@code{lazy_refcounts=on} option to get a more QED-like behaviour. - -Supported options: -@table @code -@item backing_file -File name of a base image (see @option{create} subcommand). -@item backing_fmt -Image file format of backing file (optional). Useful if the format cannot be -autodetected because it has no header, like some vhd/vpc files. -@item cluster_size -Changes the cluster size (must be power-of-2 between 4K and 64K). Smaller -cluster sizes can improve the image file size whereas larger cluster sizes -generally provide better performance. -@item table_size -Changes the number of clusters per L1/L2 table (must be power-of-2 between 1 -and 16). There is normally no need to change this value but this option can be -used for performance benchmarking. -@end table - -@item qcow -Old QEMU image format with support for backing files, compact image files, -encryption and compression. - -Supported options: -@table @code -@item backing_file -File name of a base image (see @option{create} subcommand) -@item encryption -This option is deprecated and equivalent to @code{encrypt.format=aes} - -@item encrypt.format -If this is set to @code{aes}, the image is encrypted with 128-bit AES-CBC. -The encryption key is given by the @code{encrypt.key-secret} parameter. -This encryption format is considered to be flawed by modern cryptography -standards, suffering from a number of design problems enumerated previously -against the @code{qcow2} image format. - -The use of this is no longer supported in system emulators. Support only -remains in the command line utilities, for the purposes of data liberation -and interoperability with old versions of QEMU. - -Users requiring native encryption should use the @code{qcow2} format -instead with @code{encrypt.format=luks}. - -@item encrypt.key-secret - -Provides the ID of a @code{secret} object that contains the encryption -key (@code{encrypt.format=aes}). - -@end table - -@item luks - -LUKS v1 encryption format, compatible with Linux dm-crypt/cryptsetup - -Supported options: -@table @code - -@item key-secret - -Provides the ID of a @code{secret} object that contains the passphrase. - -@item cipher-alg - -Name of the cipher algorithm and key length. Currently defaults -to @code{aes-256}. - -@item cipher-mode - -Name of the encryption mode to use. Currently defaults to @code{xts}. - -@item ivgen-alg - -Name of the initialization vector generator algorithm. Currently defaults -to @code{plain64}. - -@item ivgen-hash-alg - -Name of the hash algorithm to use with the initialization vector generator -(if required). Defaults to @code{sha256}. - -@item hash-alg - -Name of the hash algorithm to use for PBKDF algorithm -Defaults to @code{sha256}. - -@item iter-time - -Amount of time, in milliseconds, to use for PBKDF algorithm per key slot. -Defaults to @code{2000}. - -@end table - -@item vdi -VirtualBox 1.1 compatible image format. -Supported options: -@table @code -@item static -If this option is set to @code{on}, the image is created with metadata -preallocation. -@end table - -@item vmdk -VMware 3 and 4 compatible image format. - -Supported options: -@table @code -@item backing_file -File name of a base image (see @option{create} subcommand). -@item compat6 -Create a VMDK version 6 image (instead of version 4) -@item hwversion -Specify vmdk virtual hardware version. Compat6 flag cannot be enabled -if hwversion is specified. -@item subformat -Specifies which VMDK subformat to use. Valid options are -@code{monolithicSparse} (default), -@code{monolithicFlat}, -@code{twoGbMaxExtentSparse}, -@code{twoGbMaxExtentFlat} and -@code{streamOptimized}. -@end table - -@item vpc -VirtualPC compatible image format (VHD). -Supported options: -@table @code -@item subformat -Specifies which VHD subformat to use. Valid options are -@code{dynamic} (default) and @code{fixed}. -@end table - -@item VHDX -Hyper-V compatible image format (VHDX). -Supported options: -@table @code -@item subformat -Specifies which VHDX subformat to use. Valid options are -@code{dynamic} (default) and @code{fixed}. -@item block_state_zero -Force use of payload blocks of type 'ZERO'. Can be set to @code{on} (default) -or @code{off}. When set to @code{off}, new blocks will be created as -@code{PAYLOAD_BLOCK_NOT_PRESENT}, which means parsers are free to return -arbitrary data for those blocks. Do not set to @code{off} when using -@code{qemu-img convert} with @code{subformat=dynamic}. -@item block_size -Block size; min 1 MB, max 256 MB. 0 means auto-calculate based on image size. -@item log_size -Log size; min 1 MB. -@end table -@end table - -@subsubsection Read-only formats -More disk image file formats are supported in a read-only mode. -@table @option -@item bochs -Bochs images of @code{growing} type. -@item cloop -Linux Compressed Loop image, useful only to reuse directly compressed -CD-ROM images present for example in the Knoppix CD-ROMs. -@item dmg -Apple disk image. -@item parallels -Parallels disk image format. -@end table - - -@node host_drives -@subsection Using host drives - -In addition to disk image files, QEMU can directly access host -devices. We describe here the usage for QEMU version >= 0.8.3. - -@subsubsection Linux - -On Linux, you can directly use the host device filename instead of a -disk image filename provided you have enough privileges to access -it. For example, use @file{/dev/cdrom} to access to the CDROM. - -@table @code -@item CD -You can specify a CDROM device even if no CDROM is loaded. QEMU has -specific code to detect CDROM insertion or removal. CDROM ejection by -the guest OS is supported. Currently only data CDs are supported. -@item Floppy -You can specify a floppy device even if no floppy is loaded. Floppy -removal is currently not detected accurately (if you change floppy -without doing floppy access while the floppy is not loaded, the guest -OS will think that the same floppy is loaded). -Use of the host's floppy device is deprecated, and support for it will -be removed in a future release. -@item Hard disks -Hard disks can be used. Normally you must specify the whole disk -(@file{/dev/hdb} instead of @file{/dev/hdb1}) so that the guest OS can -see it as a partitioned disk. WARNING: unless you know what you do, it -is better to only make READ-ONLY accesses to the hard disk otherwise -you may corrupt your host data (use the @option{-snapshot} command -line option or modify the device permissions accordingly). -@end table - -@subsubsection Windows - -@table @code -@item CD -The preferred syntax is the drive letter (e.g. @file{d:}). The -alternate syntax @file{\\.\d:} is supported. @file{/dev/cdrom} is -supported as an alias to the first CDROM drive. - -Currently there is no specific code to handle removable media, so it -is better to use the @code{change} or @code{eject} monitor commands to -change or eject media. -@item Hard disks -Hard disks can be used with the syntax: @file{\\.\PhysicalDrive@var{N}} -where @var{N} is the drive number (0 is the first hard disk). - -WARNING: unless you know what you do, it is better to only make -READ-ONLY accesses to the hard disk otherwise you may corrupt your -host data (use the @option{-snapshot} command line so that the -modifications are written in a temporary file). -@end table - - -@subsubsection Mac OS X - -@file{/dev/cdrom} is an alias to the first CDROM. - -Currently there is no specific code to handle removable media, so it -is better to use the @code{change} or @code{eject} monitor commands to -change or eject media. - -@node disk_images_fat_images -@subsection Virtual FAT disk images - -QEMU can automatically create a virtual FAT disk image from a -directory tree. In order to use it, just type: - -@example -@value{qemu_system} linux.img -hdb fat:/my_directory -@end example - -Then you access access to all the files in the @file{/my_directory} -directory without having to copy them in a disk image or to export -them via SAMBA or NFS. The default access is @emph{read-only}. - -Floppies can be emulated with the @code{:floppy:} option: - -@example -@value{qemu_system} linux.img -fda fat:floppy:/my_directory -@end example - -A read/write support is available for testing (beta stage) with the -@code{:rw:} option: - -@example -@value{qemu_system} linux.img -fda fat:floppy:rw:/my_directory -@end example - -What you should @emph{never} do: -@itemize -@item use non-ASCII filenames ; -@item use "-snapshot" together with ":rw:" ; -@item expect it to work when loadvm'ing ; -@item write to the FAT directory on the host system while accessing it with the guest system. -@end itemize - -@node disk_images_nbd -@subsection NBD access - -QEMU can access directly to block device exported using the Network Block Device -protocol. - -@example -@value{qemu_system} linux.img -hdb nbd://my_nbd_server.mydomain.org:1024/ -@end example - -If the NBD server is located on the same host, you can use an unix socket instead -of an inet socket: - -@example -@value{qemu_system} linux.img -hdb nbd+unix://?socket=/tmp/my_socket -@end example - -In this case, the block device must be exported using qemu-nbd: - -@example -qemu-nbd --socket=/tmp/my_socket my_disk.qcow2 -@end example - -The use of qemu-nbd allows sharing of a disk between several guests: -@example -qemu-nbd --socket=/tmp/my_socket --share=2 my_disk.qcow2 -@end example - -@noindent -and then you can use it with two guests: -@example -@value{qemu_system} linux1.img -hdb nbd+unix://?socket=/tmp/my_socket -@value{qemu_system} linux2.img -hdb nbd+unix://?socket=/tmp/my_socket -@end example - -If the nbd-server uses named exports (supported since NBD 2.9.18, or with QEMU's -own embedded NBD server), you must specify an export name in the URI: -@example -@value{qemu_system} -cdrom nbd://localhost/debian-500-ppc-netinst -@value{qemu_system} -cdrom nbd://localhost/openSUSE-11.1-ppc-netinst -@end example - -The URI syntax for NBD is supported since QEMU 1.3. An alternative syntax is -also available. Here are some example of the older syntax: -@example -@value{qemu_system} linux.img -hdb nbd:my_nbd_server.mydomain.org:1024 -@value{qemu_system} linux2.img -hdb nbd:unix:/tmp/my_socket -@value{qemu_system} -cdrom nbd:localhost:10809:exportname=debian-500-ppc-netinst -@end example - -@node disk_images_sheepdog -@subsection Sheepdog disk images - -Sheepdog is a distributed storage system for QEMU. It provides highly -available block level storage volumes that can be attached to -QEMU-based virtual machines. - -You can create a Sheepdog disk image with the command: -@example -qemu-img create sheepdog:///@var{image} @var{size} -@end example -where @var{image} is the Sheepdog image name and @var{size} is its -size. - -To import the existing @var{filename} to Sheepdog, you can use a -convert command. -@example -qemu-img convert @var{filename} sheepdog:///@var{image} -@end example - -You can boot from the Sheepdog disk image with the command: -@example -@value{qemu_system} sheepdog:///@var{image} -@end example - -You can also create a snapshot of the Sheepdog image like qcow2. -@example -qemu-img snapshot -c @var{tag} sheepdog:///@var{image} -@end example -where @var{tag} is a tag name of the newly created snapshot. - -To boot from the Sheepdog snapshot, specify the tag name of the -snapshot. -@example -@value{qemu_system} sheepdog:///@var{image}#@var{tag} -@end example - -You can create a cloned image from the existing snapshot. -@example -qemu-img create -b sheepdog:///@var{base}#@var{tag} sheepdog:///@var{image} -@end example -where @var{base} is an image name of the source snapshot and @var{tag} -is its tag name. - -You can use an unix socket instead of an inet socket: - -@example -@value{qemu_system} sheepdog+unix:///@var{image}?socket=@var{path} -@end example - -If the Sheepdog daemon doesn't run on the local host, you need to -specify one of the Sheepdog servers to connect to. -@example -qemu-img create sheepdog://@var{hostname}:@var{port}/@var{image} @var{size} -@value{qemu_system} sheepdog://@var{hostname}:@var{port}/@var{image} -@end example - -@node disk_images_iscsi -@subsection iSCSI LUNs - -iSCSI is a popular protocol used to access SCSI devices across a computer -network. - -There are two different ways iSCSI devices can be used by QEMU. - -The first method is to mount the iSCSI LUN on the host, and make it appear as -any other ordinary SCSI device on the host and then to access this device as a -/dev/sd device from QEMU. How to do this differs between host OSes. - -The second method involves using the iSCSI initiator that is built into -QEMU. This provides a mechanism that works the same way regardless of which -host OS you are running QEMU on. This section will describe this second method -of using iSCSI together with QEMU. - -In QEMU, iSCSI devices are described using special iSCSI URLs - -@example -URL syntax: -iscsi://[[%]@@][:]// -@end example - -Username and password are optional and only used if your target is set up -using CHAP authentication for access control. -Alternatively the username and password can also be set via environment -variables to have these not show up in the process list - -@example -export LIBISCSI_CHAP_USERNAME= -export LIBISCSI_CHAP_PASSWORD= -iscsi://// -@end example - -Various session related parameters can be set via special options, either -in a configuration file provided via '-readconfig' or directly on the -command line. - -If the initiator-name is not specified qemu will use a default name -of 'iqn.2008-11.org.linux-kvm[:'] where is the UUID of the -virtual machine. If the UUID is not specified qemu will use -'iqn.2008-11.org.linux-kvm[:'] where is the name of the -virtual machine. - -@example -Setting a specific initiator name to use when logging in to the target --iscsi initiator-name=iqn.qemu.test:my-initiator -@end example - -@example -Controlling which type of header digest to negotiate with the target --iscsi header-digest=CRC32C|CRC32C-NONE|NONE-CRC32C|NONE -@end example - -These can also be set via a configuration file -@example -[iscsi] - user = "CHAP username" - password = "CHAP password" - initiator-name = "iqn.qemu.test:my-initiator" - # header digest is one of CRC32C|CRC32C-NONE|NONE-CRC32C|NONE - header-digest = "CRC32C" -@end example - - -Setting the target name allows different options for different targets -@example -[iscsi "iqn.target.name"] - user = "CHAP username" - password = "CHAP password" - initiator-name = "iqn.qemu.test:my-initiator" - # header digest is one of CRC32C|CRC32C-NONE|NONE-CRC32C|NONE - header-digest = "CRC32C" -@end example - - -Howto use a configuration file to set iSCSI configuration options: -@example -cat >iscsi.conf < /sys/bus/pci/devices/0000:06:0d.0/driver/unbind -# echo 1102 0002 > /sys/bus/pci/drivers/vfio-pci/new_id - -# @value{qemu_system} -drive file=nvme://@var{host}:@var{bus}:@var{slot}.@var{func}/@var{namespace} -@end example - -Alternative syntax using properties: - -@example -@value{qemu_system} -drive file.driver=nvme,file.device=@var{host}:@var{bus}:@var{slot}.@var{func},file.namespace=@var{namespace} -@end example - -@var{host}:@var{bus}:@var{slot}.@var{func} is the NVMe controller's PCI device -address on the host. - -@var{namespace} is the NVMe namespace number, starting from 1. - -@node disk_image_locking -@subsection Disk image file locking - -By default, QEMU tries to protect image files from unexpected concurrent -access, as long as it's supported by the block protocol driver and host -operating system. If multiple QEMU processes (including QEMU emulators and -utilities) try to open the same image with conflicting accessing modes, all but -the first one will get an error. - -This feature is currently supported by the file protocol on Linux with the Open -File Descriptor (OFD) locking API, and can be configured to fall back to POSIX -locking if the POSIX host doesn't support Linux OFD locking. - -To explicitly enable image locking, specify "locking=on" in the file protocol -driver options. If OFD locking is not possible, a warning will be printed and -the POSIX locking API will be used. In this case there is a risk that the lock -will get silently lost when doing hot plugging and block jobs, due to the -shortcomings of the POSIX locking API. - -QEMU transparently handles lock handover during shared storage migration. For -shared virtual disk images between multiple VMs, the "share-rw" device option -should be used. - -By default, the guest has exclusive write access to its disk image. If the -guest can safely share the disk image with other writers the @code{-device -...,share-rw=on} parameter can be used. This is only safe if the guest is -running software, such as a cluster file system, that coordinates disk accesses -to avoid corruption. - -Note that share-rw=on only declares the guest's ability to share the disk. -Some QEMU features, such as image file formats, require exclusive write access -to the disk image and this is unaffected by the share-rw=on option. - -Alternatively, locking can be fully disabled by "locking=off" block device -option. In the command line, the option is usually in the form of -"file.locking=off" as the protocol driver is normally placed as a "file" child -under a format driver. For example: - -@code{-blockdev driver=qcow2,file.filename=/path/to/image,file.locking=off,file.driver=file} - -To check if image locking is active, check the output of the "lslocks" command -on host and see if there are locks held by the QEMU process on the image file. -More than one byte could be locked by the QEMU instance, each byte of which -reflects a particular permission that is acquired or protected by the running -block driver. - -@c man end - -@ignore - -@setfilename qemu-block-drivers -@settitle QEMU block drivers reference - -@c man begin SEEALSO -The HTML documentation of QEMU for more precise information and Linux -user mode emulator invocation. -@c man end - -@c man begin AUTHOR -Fabrice Bellard and the QEMU Project developers -@c man end - -@end ignore diff --git a/docs/qemu-cpu-models.texi b/docs/qemu-cpu-models.texi deleted file mode 100644 index f88a1def0d..0000000000 --- a/docs/qemu-cpu-models.texi +++ /dev/null @@ -1,677 +0,0 @@ -@c man begin SYNOPSIS -QEMU / KVM CPU model configuration -@c man end - -@set qemu_system_x86 qemu-system-x86_64 - -@c man begin DESCRIPTION - -@menu -* recommendations_cpu_models_x86:: Recommendations for KVM CPU model configuration on x86 hosts -* recommendations_cpu_models_MIPS:: Supported CPU model configurations on MIPS hosts -* cpu_model_syntax_apps:: Syntax for configuring CPU models -@end menu - -QEMU / KVM virtualization supports two ways to configure CPU models - -@table @option - -@item Host passthrough - -This passes the host CPU model features, model, stepping, exactly to the -guest. Note that KVM may filter out some host CPU model features if they -cannot be supported with virtualization. Live migration is unsafe when -this mode is used as libvirt / QEMU cannot guarantee a stable CPU is -exposed to the guest across hosts. This is the recommended CPU to use, -provided live migration is not required. - -@item Named model - -QEMU comes with a number of predefined named CPU models, that typically -refer to specific generations of hardware released by Intel and AMD. -These allow the guest VMs to have a degree of isolation from the host CPU, -allowing greater flexibility in live migrating between hosts with differing -hardware. -@end table - -In both cases, it is possible to optionally add or remove individual CPU -features, to alter what is presented to the guest by default. - -Libvirt supports a third way to configure CPU models known as "Host model". -This uses the QEMU "Named model" feature, automatically picking a CPU model -that is similar the host CPU, and then adding extra features to approximate -the host model as closely as possible. This does not guarantee the CPU family, -stepping, etc will precisely match the host CPU, as they would with "Host -passthrough", but gives much of the benefit of passthrough, while making -live migration safe. - -@node recommendations_cpu_models_x86 -@subsection Recommendations for KVM CPU model configuration on x86 hosts - -The information that follows provides recommendations for configuring -CPU models on x86 hosts. The goals are to maximise performance, while -protecting guest OS against various CPU hardware flaws, and optionally -enabling live migration between hosts with heterogeneous CPU models. - -@menu -* preferred_cpu_models_intel_x86:: Preferred CPU models for Intel x86 hosts -* important_cpu_features_intel_x86:: Important CPU features for Intel x86 hosts -* preferred_cpu_models_amd_x86:: Preferred CPU models for AMD x86 hosts -* important_cpu_features_amd_x86:: Important CPU features for AMD x86 hosts -* default_cpu_models_x86:: Default x86 CPU models -* other_non_recommended_cpu_models_x86:: Other non-recommended x86 CPUs -@end menu - -@node preferred_cpu_models_intel_x86 -@subsubsection Preferred CPU models for Intel x86 hosts - -The following CPU models are preferred for use on Intel hosts. Administrators / -applications are recommended to use the CPU model that matches the generation -of the host CPUs in use. In a deployment with a mixture of host CPU models -between machines, if live migration compatibility is required, use the newest -CPU model that is compatible across all desired hosts. - -@table @option -@item @code{Skylake-Server} -@item @code{Skylake-Server-IBRS} - -Intel Xeon Processor (Skylake, 2016) - - -@item @code{Skylake-Client} -@item @code{Skylake-Client-IBRS} - -Intel Core Processor (Skylake, 2015) - - -@item @code{Broadwell} -@item @code{Broadwell-IBRS} -@item @code{Broadwell-noTSX} -@item @code{Broadwell-noTSX-IBRS} - -Intel Core Processor (Broadwell, 2014) - - -@item @code{Haswell} -@item @code{Haswell-IBRS} -@item @code{Haswell-noTSX} -@item @code{Haswell-noTSX-IBRS} - -Intel Core Processor (Haswell, 2013) - - -@item @code{IvyBridge} -@item @code{IvyBridge-IBRS} - -Intel Xeon E3-12xx v2 (Ivy Bridge, 2012) - - -@item @code{SandyBridge} -@item @code{SandyBridge-IBRS} - -Intel Xeon E312xx (Sandy Bridge, 2011) - - -@item @code{Westmere} -@item @code{Westmere-IBRS} - -Westmere E56xx/L56xx/X56xx (Nehalem-C, 2010) - - -@item @code{Nehalem} -@item @code{Nehalem-IBRS} - -Intel Core i7 9xx (Nehalem Class Core i7, 2008) - - -@item @code{Penryn} - -Intel Core 2 Duo P9xxx (Penryn Class Core 2, 2007) - - -@item @code{Conroe} - -Intel Celeron_4x0 (Conroe/Merom Class Core 2, 2006) - -@end table - -@node important_cpu_features_intel_x86 -@subsubsection Important CPU features for Intel x86 hosts - -The following are important CPU features that should be used on Intel x86 -hosts, when available in the host CPU. Some of them require explicit -configuration to enable, as they are not included by default in some, or all, -of the named CPU models listed above. In general all of these features are -included if using "Host passthrough" or "Host model". - - -@table @option - -@item @code{pcid} - -Recommended to mitigate the cost of the Meltdown (CVE-2017-5754) fix - -Included by default in Haswell, Broadwell & Skylake Intel CPU models. - -Should be explicitly turned on for Westmere, SandyBridge, and IvyBridge -Intel CPU models. Note that some desktop/mobile Westmere CPUs cannot -support this feature. - - -@item @code{spec-ctrl} - -Required to enable the Spectre v2 (CVE-2017-5715) fix. - -Included by default in Intel CPU models with -IBRS suffix. - -Must be explicitly turned on for Intel CPU models without -IBRS suffix. - -Requires the host CPU microcode to support this feature before it -can be used for guest CPUs. - - -@item @code{stibp} - -Required to enable stronger Spectre v2 (CVE-2017-5715) fixes in some -operating systems. - -Must be explicitly turned on for all Intel CPU models. - -Requires the host CPU microcode to support this feature before it -can be used for guest CPUs. - - -@item @code{ssbd} - -Required to enable the CVE-2018-3639 fix - -Not included by default in any Intel CPU model. - -Must be explicitly turned on for all Intel CPU models. - -Requires the host CPU microcode to support this feature before it -can be used for guest CPUs. - - -@item @code{pdpe1gb} - -Recommended to allow guest OS to use 1GB size pages - -Not included by default in any Intel CPU model. - -Should be explicitly turned on for all Intel CPU models. - -Note that not all CPU hardware will support this feature. - -@item @code{md-clear} - -Required to confirm the MDS (CVE-2018-12126, CVE-2018-12127, CVE-2018-12130, -CVE-2019-11091) fixes. - -Not included by default in any Intel CPU model. - -Must be explicitly turned on for all Intel CPU models. - -Requires the host CPU microcode to support this feature before it -can be used for guest CPUs. -@end table - - -@node preferred_cpu_models_amd_x86 -@subsubsection Preferred CPU models for AMD x86 hosts - -The following CPU models are preferred for use on Intel hosts. Administrators / -applications are recommended to use the CPU model that matches the generation -of the host CPUs in use. In a deployment with a mixture of host CPU models -between machines, if live migration compatibility is required, use the newest -CPU model that is compatible across all desired hosts. - -@table @option - -@item @code{EPYC} -@item @code{EPYC-IBPB} - -AMD EPYC Processor (2017) - - -@item @code{Opteron_G5} - -AMD Opteron 63xx class CPU (2012) - - -@item @code{Opteron_G4} - -AMD Opteron 62xx class CPU (2011) - - -@item @code{Opteron_G3} - -AMD Opteron 23xx (Gen 3 Class Opteron, 2009) - - -@item @code{Opteron_G2} - -AMD Opteron 22xx (Gen 2 Class Opteron, 2006) - - -@item @code{Opteron_G1} - -AMD Opteron 240 (Gen 1 Class Opteron, 2004) -@end table - -@node important_cpu_features_amd_x86 -@subsubsection Important CPU features for AMD x86 hosts - -The following are important CPU features that should be used on AMD x86 -hosts, when available in the host CPU. Some of them require explicit -configuration to enable, as they are not included by default in some, or all, -of the named CPU models listed above. In general all of these features are -included if using "Host passthrough" or "Host model". - - -@table @option - -@item @code{ibpb} - -Required to enable the Spectre v2 (CVE-2017-5715) fix. - -Included by default in AMD CPU models with -IBPB suffix. - -Must be explicitly turned on for AMD CPU models without -IBPB suffix. - -Requires the host CPU microcode to support this feature before it -can be used for guest CPUs. - - -@item @code{stibp} - -Required to enable stronger Spectre v2 (CVE-2017-5715) fixes in some -operating systems. - -Must be explicitly turned on for all AMD CPU models. - -Requires the host CPU microcode to support this feature before it -can be used for guest CPUs. - - -@item @code{virt-ssbd} - -Required to enable the CVE-2018-3639 fix - -Not included by default in any AMD CPU model. - -Must be explicitly turned on for all AMD CPU models. - -This should be provided to guests, even if amd-ssbd is also -provided, for maximum guest compatibility. - -Note for some QEMU / libvirt versions, this must be force enabled -when when using "Host model", because this is a virtual feature -that doesn't exist in the physical host CPUs. - - -@item @code{amd-ssbd} - -Required to enable the CVE-2018-3639 fix - -Not included by default in any AMD CPU model. - -Must be explicitly turned on for all AMD CPU models. - -This provides higher performance than virt-ssbd so should be -exposed to guests whenever available in the host. virt-ssbd -should none the less also be exposed for maximum guest -compatibility as some kernels only know about virt-ssbd. - - -@item @code{amd-no-ssb} - -Recommended to indicate the host is not vulnerable CVE-2018-3639 - -Not included by default in any AMD CPU model. - -Future hardware generations of CPU will not be vulnerable to -CVE-2018-3639, and thus the guest should be told not to enable -its mitigations, by exposing amd-no-ssb. This is mutually -exclusive with virt-ssbd and amd-ssbd. - - -@item @code{pdpe1gb} - -Recommended to allow guest OS to use 1GB size pages - -Not included by default in any AMD CPU model. - -Should be explicitly turned on for all AMD CPU models. - -Note that not all CPU hardware will support this feature. -@end table - - -@node default_cpu_models_x86 -@subsubsection Default x86 CPU models - -The default QEMU CPU models are designed such that they can run on all hosts. -If an application does not wish to do perform any host compatibility checks -before launching guests, the default is guaranteed to work. - -The default CPU models will, however, leave the guest OS vulnerable to various -CPU hardware flaws, so their use is strongly discouraged. Applications should -follow the earlier guidance to setup a better CPU configuration, with host -passthrough recommended if live migration is not needed. - -@table @option -@item @code{qemu32} -@item @code{qemu64} - -QEMU Virtual CPU version 2.5+ (32 & 64 bit variants) - -qemu64 is used for x86_64 guests and qemu32 is used for i686 guests, when no --cpu argument is given to QEMU, or no is provided in libvirt XML. -@end table - - -@node other_non_recommended_cpu_models_x86 -@subsubsection Other non-recommended x86 CPUs - -The following CPUs models are compatible with most AMD and Intel x86 hosts, but -their usage is discouraged, as they expose a very limited featureset, which -prevents guests having optimal performance. - -@table @option - -@item @code{kvm32} -@item @code{kvm64} - -Common KVM processor (32 & 64 bit variants) - -Legacy models just for historical compatibility with ancient QEMU versions. - - -@item @code{486} -@item @code{athlon} -@item @code{phenom} -@item @code{coreduo} -@item @code{core2duo} -@item @code{n270} -@item @code{pentium} -@item @code{pentium2} -@item @code{pentium3} - -Various very old x86 CPU models, mostly predating the introduction of -hardware assisted virtualization, that should thus not be required for -running virtual machines. -@end table - -@node recommendations_cpu_models_MIPS -@subsection Supported CPU model configurations on MIPS hosts - -QEMU supports variety of MIPS CPU models: - -@menu -* cpu_models_MIPS32:: Supported CPU models for MIPS32 hosts -* cpu_models_MIPS64:: Supported CPU models for MIPS64 hosts -* cpu_models_nanoMIPS:: Supported CPU models for nanoMIPS hosts -* preferred_cpu_models_MIPS:: Preferred CPU models for MIPS hosts -@end menu - -@node cpu_models_MIPS32 -@subsubsection Supported CPU models for MIPS32 hosts - -The following CPU models are supported for use on MIPS32 hosts. Administrators / -applications are recommended to use the CPU model that matches the generation -of the host CPUs in use. In a deployment with a mixture of host CPU models -between machines, if live migration compatibility is required, use the newest -CPU model that is compatible across all desired hosts. - -@table @option -@item @code{mips32r6-generic} - -MIPS32 Processor (Release 6, 2015) - - -@item @code{P5600} - -MIPS32 Processor (P5600, 2014) - - -@item @code{M14K} -@item @code{M14Kc} - -MIPS32 Processor (M14K, 2009) - - -@item @code{74Kf} - -MIPS32 Processor (74K, 2007) - - -@item @code{34Kf} - -MIPS32 Processor (34K, 2006) - - -@item @code{24Kc} -@item @code{24KEc} -@item @code{24Kf} - -MIPS32 Processor (24K, 2003) - - -@item @code{4Kc} -@item @code{4Km} -@item @code{4KEcR1} -@item @code{4KEmR1} -@item @code{4KEc} -@item @code{4KEm} - -MIPS32 Processor (4K, 1999) -@end table - -@node cpu_models_MIPS64 -@subsubsection Supported CPU models for MIPS64 hosts - -The following CPU models are supported for use on MIPS64 hosts. Administrators / -applications are recommended to use the CPU model that matches the generation -of the host CPUs in use. In a deployment with a mixture of host CPU models -between machines, if live migration compatibility is required, use the newest -CPU model that is compatible across all desired hosts. - -@table @option -@item @code{I6400} - -MIPS64 Processor (Release 6, 2014) - - -@item @code{Loongson-2F} - -MIPS64 Processor (Loongson 2, 2008) - - -@item @code{Loongson-2E} - -MIPS64 Processor (Loongson 2, 2006) - - -@item @code{mips64dspr2} - -MIPS64 Processor (Release 2, 2006) - - -@item @code{MIPS64R2-generic} -@item @code{5KEc} -@item @code{5KEf} - -MIPS64 Processor (Release 2, 2002) - - -@item @code{20Kc} - -MIPS64 Processor (20K, 2000) - - -@item @code{5Kc} -@item @code{5Kf} - -MIPS64 Processor (5K, 1999) - - -@item @code{VR5432} - -MIPS64 Processor (VR, 1998) - - -@item @code{R4000} - -MIPS64 Processor (MIPS III, 1991) -@end table - -@node cpu_models_nanoMIPS -@subsubsection Supported CPU models for nanoMIPS hosts - -The following CPU models are supported for use on nanoMIPS hosts. Administrators / -applications are recommended to use the CPU model that matches the generation -of the host CPUs in use. In a deployment with a mixture of host CPU models -between machines, if live migration compatibility is required, use the newest -CPU model that is compatible across all desired hosts. - -@table @option -@item @code{I7200} - -MIPS I7200 (nanoMIPS, 2018) - -@end table - -@node preferred_cpu_models_MIPS -@subsubsection Preferred CPU models for MIPS hosts - -The following CPU models are preferred for use on different MIPS hosts: - -@table @option -@item @code{MIPS III} -R4000 - -@item @code{MIPS32R2} -34Kf - -@item @code{MIPS64R6} -I6400 - -@item @code{nanoMIPS} -I7200 -@end table - -@node cpu_model_syntax_apps -@subsection Syntax for configuring CPU models - -The example below illustrate the approach to configuring the various -CPU models / features in QEMU and libvirt - -@menu -* cpu_model_syntax_qemu:: QEMU command line -* cpu_model_syntax_libvirt:: Libvirt guest XML -@end menu - -@node cpu_model_syntax_qemu -@subsubsection QEMU command line - -@table @option - -@item Host passthrough - -@example - $ @value{qemu_system_x86} -cpu host -@end example - -With feature customization: - -@example - $ @value{qemu_system_x86} -cpu host,-vmx,... -@end example - -@item Named CPU models - -@example - $ @value{qemu_system_x86} -cpu Westmere -@end example - -With feature customization: - -@example - $ @value{qemu_system_x86} -cpu Westmere,+pcid,... -@end example - -@end table - -@node cpu_model_syntax_libvirt -@subsubsection Libvirt guest XML - -@table @option - -@item Host passthrough - -@example - -@end example - -With feature customization: - -@example - - - ... - -@end example - -@item Host model - -@example - -@end example - -With feature customization: - -@example - - - ... - -@end example - -@item Named model - -@example - - - -@end example - -With feature customization: - -@example - - - - ... - -@end example - -@end table - -@c man end - -@ignore - -@setfilename qemu-cpu-models -@settitle QEMU / KVM CPU model configuration - -@c man begin SEEALSO -The HTML documentation of QEMU for more precise information and Linux -user mode emulator invocation. -@c man end - -@c man begin AUTHOR -Daniel P. Berrange -@c man end - -@end ignore diff --git a/docs/qemu-option-trace.rst.inc b/docs/qemu-option-trace.rst.inc new file mode 100644 index 0000000000..7e09773a9c --- /dev/null +++ b/docs/qemu-option-trace.rst.inc @@ -0,0 +1,26 @@ + +Specify tracing options. + +.. option:: [enable=]PATTERN + + Immediately enable events matching *PATTERN* + (either event name or a globbing pattern). This option is only + available if QEMU has been compiled with the ``simple``, ``log`` + or ``ftrace`` tracing backend. To specify multiple events or patterns, + specify the :option:`-trace` option multiple times. + + Use :option:`-trace help` to print a list of names of trace points. + +.. option:: events=FILE + + Immediately enable events listed in *FILE*. + The file must contain one event name (as listed in the ``trace-events-all`` + file) per line; globbing patterns are accepted too. This option is only + available if QEMU has been compiled with the ``simple``, ``log`` or + ``ftrace`` tracing backend. + +.. option:: file=FILE + + Log output traces to *FILE*. + This option is only available if QEMU has been compiled with + the ``simple`` tracing backend. diff --git a/docs/replay.txt b/docs/replay.txt index ce97c3f72f..70c27edb36 100644 --- a/docs/replay.txt +++ b/docs/replay.txt @@ -19,7 +19,7 @@ Deterministic replay has the following features: the memory, state of the hardware devices, clocks, and screen of the VM. * Writes execution log into the file for later replaying for multiple times on different machines. - * Supports i386, x86_64, and ARM hardware platforms. + * Supports i386, x86_64, and Arm hardware platforms. * Performs deterministic replay of all operations with keyboard and mouse input devices. @@ -67,6 +67,7 @@ Modifications of qemu include: * network filter for recording and replaying the packets * block driver for making block layer deterministic * serial port input record and replay + * recording of random numbers obtained from the external sources Locking and thread synchronisation ---------------------------------- diff --git a/docs/specs/acpi_cpu_hotplug.txt b/docs/specs/acpi_cpu_hotplug.txt index ee219c8358..9bb22d1270 100644 --- a/docs/specs/acpi_cpu_hotplug.txt +++ b/docs/specs/acpi_cpu_hotplug.txt @@ -15,14 +15,14 @@ CPU present bitmap for: PIIX-PM (IO port 0xaf00-0xaf1f, 1-byte access) One bit per CPU. Bit position reflects corresponding CPU APIC ID. Read-only. The first DWORD in bitmap is used in write mode to switch from legacy - to new CPU hotplug interface, write 0 into it to do switch. + to modern CPU hotplug interface, write 0 into it to do switch. --------------------------------------------------------------- QEMU sets corresponding CPU bit on hot-add event and issues SCI with GPE.2 event set. CPU present map is read by ACPI BIOS GPE.2 handler to notify OS about CPU hot-add events. CPU hot-remove isn't supported. ===================================== -ACPI CPU hotplug interface registers: +Modern ACPI CPU hotplug interface registers: ------------------------------------- Register block base address: ICH9-LPC IO port 0x0cd8 @@ -30,9 +30,25 @@ Register block base address: Register block size: ACPI_CPU_HOTPLUG_REG_LEN = 12 +All accesses to registers described below, imply little-endian byte order. + +Reserved resisters behavior: + - write accesses are ignored + - read accesses return all bits set to 0. + +The last stored value in 'CPU selector' must refer to a possible CPU, otherwise + - reads from any register return 0 + - writes to any other register are ignored until valid value is stored into it +On QEMU start, 'CPU selector' is initialized to a valid value, on reset it +keeps the current value. + read access: offset: - [0x0-0x3] reserved + [0x0-0x3] Command data 2: (DWORD access) + if value last stored in 'Command field': + 0: reads as 0x0 + 3: upper 32 bits of architecture specific CPU ID value + other values: reserved [0x4] CPU device status fields: (1 byte access) bits: 0: Device is enabled and may be used by guest @@ -44,15 +60,17 @@ read access: 3-7: reserved and should be ignored by OSPM [0x5-0x7] reserved [0x8] Command data: (DWORD access) - in case of error or unsupported command reads is 0xFFFFFFFF - current 'Command field' value: - 0: returns PXM value corresponding to device + contains 0 unless value last stored in 'Command field' is one of: + 0: contains 'CPU selector' value of a CPU with pending event[s] + 3: lower 32 bits of architecture specific CPU ID value + (in x86 case: APIC ID) write access: offset: [0x0-0x3] CPU selector: (DWORD access) selects active CPU device. All following accesses to other registers will read/store data from/to selected CPU. + Valid values: [0 .. max_cpus) [0x4] CPU device control fields: (1 byte access) bits: 0: reserved, OSPM must clear it before writing to register. @@ -69,26 +87,65 @@ write access: value: 0: selects a CPU device with inserting/removing events and following reads from 'Command data' register return - selected CPU (CPU selector value). If no CPU with events - found, the current CPU selector doesn't change and - corresponding insert/remove event flags are not set. + selected CPU ('CPU selector' value). + If no CPU with events found, the current 'CPU selector' doesn't + change and corresponding insert/remove event flags are not modified. 1: following writes to 'Command data' register set OST event register in QEMU 2: following writes to 'Command data' register set OST status register in QEMU + 3: following reads from 'Command data' and 'Command data 2' return + architecture specific CPU ID value for currently selected CPU. other values: reserved [0x6-0x7] reserved [0x8] Command data: (DWORD access) - current 'Command field' value: - 0: OSPM reads value of CPU selector + if last stored 'Command field' value: 1: stores value into OST event register 2: stores value into OST status register, triggers ACPI_DEVICE_OST QMP event from QEMU to external applications with current values of OST event and status registers. - other values: reserved + other values: reserved -Selecting CPU device beyond possible range has no effect on platform: - - write accesses to CPU hot-plug registers not documented above are - ignored - - read accesses to CPU hot-plug registers not documented above return - all bits set to 0. +Typical usecases: + - (x86) Detecting and enabling modern CPU hotplug interface. + QEMU starts with legacy CPU hotplug interface enabled. Detecting and + switching to modern interface is based on the 2 legacy CPU hotplug features: + 1. Writes into CPU bitmap are ignored. + 2. CPU bitmap always has bit#0 set, corresponding to boot CPU. + + Use following steps to detect and enable modern CPU hotplug interface: + 1. Store 0x0 to the 'CPU selector' register, + attempting to switch to modern mode + 2. Store 0x0 to the 'CPU selector' register, + to ensure valid selector value + 3. Store 0x0 to the 'Command field' register, + 4. Read the 'Command data 2' register. + If read value is 0x0, the modern interface is enabled. + Otherwise legacy or no CPU hotplug interface available + + - Get a cpu with pending event + 1. Store 0x0 to the 'CPU selector' register. + 2. Store 0x0 to the 'Command field' register. + 3. Read the 'CPU device status fields' register. + 4. If both bit#1 and bit#2 are clear in the value read, there is no CPU + with a pending event and selected CPU remains unchanged. + 5. Otherwise, read the 'Command data' register. The value read is the + selector of the CPU with the pending event (which is already + selected). + + - Enumerate CPUs present/non present CPUs + 01. Set the present CPU count to 0. + 02. Set the iterator to 0. + 03. Store 0x0 to the 'CPU selector' register, to ensure that it's in + a valid state and that access to other registers won't be ignored. + 04. Store 0x0 to the 'Command field' register to make 'Command data' + register return 'CPU selector' value of selected CPU + 05. Read the 'CPU device status fields' register. + 06. If bit#0 is set, increment the present CPU count. + 07. Increment the iterator. + 08. Store the iterator to the 'CPU selector' register. + 09. Read the 'Command data' register. + 10. If the value read is not zero, goto 05. + 11. Otherwise store 0x0 to the 'CPU selector' register, to put it + into a valid state and exit. + The iterator at this point equals "max_cpus". diff --git a/docs/specs/fw_cfg.txt b/docs/specs/fw_cfg.txt index 08c00bdf44..8f1ebc66fa 100644 --- a/docs/specs/fw_cfg.txt +++ b/docs/specs/fw_cfg.txt @@ -82,7 +82,7 @@ Selector Register IOport: 0x510 Data Register IOport: 0x511 DMA Address IOport: 0x514 -=== ARM Register Locations === +=== Arm Register Locations === Selector Register address: Base + 8 (2 bytes) Data Register address: Base + 0 (8 bytes) diff --git a/docs/specs/index.rst b/docs/specs/index.rst index 984ba44029..de46a8b5e7 100644 --- a/docs/specs/index.rst +++ b/docs/specs/index.rst @@ -13,3 +13,4 @@ Contents: ppc-xive ppc-spapr-xive acpi_hw_reduced_hotplug + tpm diff --git a/docs/specs/ivshmem-spec.txt b/docs/specs/ivshmem-spec.txt index 042f7eae22..1beb3a01ec 100644 --- a/docs/specs/ivshmem-spec.txt +++ b/docs/specs/ivshmem-spec.txt @@ -38,8 +38,8 @@ There are two basic configurations: Interrupts are message-signaled (MSI-X). vectors=N configures the number of vectors to use. -For more details on ivshmem device properties, see The QEMU Emulator -User Documentation (qemu-doc.*). +For more details on ivshmem device properties, see the QEMU Emulator +user documentation. == The ivshmem PCI device's guest interface == diff --git a/docs/specs/pvpanic.txt b/docs/specs/pvpanic.txt index c7bbacc778..a90fbca72b 100644 --- a/docs/specs/pvpanic.txt +++ b/docs/specs/pvpanic.txt @@ -16,8 +16,13 @@ pvpanic exposes a single I/O port, by default 0x505. On read, the bits recognized by the device are set. Software should ignore bits it doesn't recognize. On write, the bits not recognized by the device are ignored. Software should set only bits both itself and the device recognize. -Currently, only bit 0 is recognized, setting it indicates a guest panic -has happened. + +Bit Definition +-------------- +bit 0: a guest panic has happened and should be processed by the host +bit 1: a guest panic has happened and will be handled by the guest; + the host should record it or report it, but should not affect + the execution of the guest. ACPI Interface -------------- @@ -26,13 +31,12 @@ pvpanic device is defined with ACPI ID "QEMU0001". Custom methods: RDPT: To determine whether guest panic notification is supported. Arguments: None -Return: Returns a byte, bit 0 set to indicate guest panic - notification is supported. Other bits are reserved and - should be ignored. +Return: Returns a byte, with the same semantics as the I/O port + interface. WRPT: To send a guest panic event -Arguments: Arg0 is a byte, with bit 0 set to indicate guest panic has - happened. Other bits are reserved and should be cleared. +Arguments: Arg0 is a byte to be written, with the same semantics as + the I/O interface. Return: None The ACPI device will automatically refer to the right port in case it diff --git a/docs/specs/tpm.rst b/docs/specs/tpm.rst new file mode 100644 index 0000000000..5e61238bc5 --- /dev/null +++ b/docs/specs/tpm.rst @@ -0,0 +1,526 @@ +=============== +QEMU TPM Device +=============== + +Guest-side hardware interface +============================= + +TIS interface +------------- + +The QEMU TPM emulation implements a TPM TIS hardware interface +following the Trusted Computing Group's specification "TCG PC Client +Specific TPM Interface Specification (TIS)", Specification Version +1.3, 21 March 2013. (see the `TIS specification`_, or a later version +of it). + +The TIS interface makes a memory mapped IO region in the area +0xfed40000-0xfed44fff available to the guest operating system. + +QEMU files related to TPM TIS interface: + - ``hw/tpm/tpm_tis_common.c`` + - ``hw/tpm/tpm_tis_isa.c`` + - ``hw/tpm/tpm_tis_sysbus.c`` + - ``hw/tpm/tpm_tis.h`` + +Both an ISA device and a sysbus device are available. The former is +used with pc/q35 machine while the latter can be instantiated in the +Arm virt machine. + +CRB interface +------------- + +QEMU also implements a TPM CRB interface following the Trusted +Computing Group's specification "TCG PC Client Platform TPM Profile +(PTP) Specification", Family "2.0", Level 00 Revision 01.03 v22, May +22, 2017. (see the `CRB specification`_, or a later version of it) + +The CRB interface makes a memory mapped IO region in the area +0xfed40000-0xfed40fff (1 locality) available to the guest +operating system. + +QEMU files related to TPM CRB interface: + - ``hw/tpm/tpm_crb.c`` + +SPAPR interface +--------------- + +pSeries (ppc64) machines offer a tpm-spapr device model. + +QEMU files related to the SPAPR interface: + - ``hw/tpm/tpm_spapr.c`` + +fw_cfg interface +================ + +The bios/firmware may read the ``"etc/tpm/config"`` fw_cfg entry for +configuring the guest appropriately. + +The entry of 6 bytes has the following content, in little-endian: + +.. code-block:: c + + #define TPM_VERSION_UNSPEC 0 + #define TPM_VERSION_1_2 1 + #define TPM_VERSION_2_0 2 + + #define TPM_PPI_VERSION_NONE 0 + #define TPM_PPI_VERSION_1_30 1 + + struct FwCfgTPMConfig { + uint32_t tpmppi_address; /* PPI memory location */ + uint8_t tpm_version; /* TPM version */ + uint8_t tpmppi_version; /* PPI version */ + }; + +ACPI interface +============== + +The TPM device is defined with ACPI ID "PNP0C31". QEMU builds a SSDT +and passes it into the guest through the fw_cfg device. The device +description contains the base address of the TIS interface 0xfed40000 +and the size of the MMIO area (0x5000). In case a TPM2 is used by +QEMU, a TPM2 ACPI table is also provided. The device is described to +be used in polling mode rather than interrupt mode primarily because +no unused IRQ could be found. + +To support measurement logs to be written by the firmware, +e.g. SeaBIOS, a TCPA table is implemented. This table provides a 64kb +buffer where the firmware can write its log into. For TPM 2 only a +more recent version of the TPM2 table provides support for +measurements logs and a TCPA table does not need to be created. + +The TCPA and TPM2 ACPI tables follow the Trusted Computing Group +specification "TCG ACPI Specification" Family "1.2" and "2.0", Level +00 Revision 00.37. (see the `ACPI specification`_, or a later version +of it) + +ACPI PPI Interface +------------------ + +QEMU supports the Physical Presence Interface (PPI) for TPM 1.2 and +TPM 2. This interface requires ACPI and firmware support. (see the +`PPI specification`_) + +PPI enables a system administrator (root) to request a modification to +the TPM upon reboot. The PPI specification defines the operation +requests and the actions the firmware has to take. The system +administrator passes the operation request number to the firmware +through an ACPI interface which writes this number to a memory +location that the firmware knows. Upon reboot, the firmware finds the +number and sends commands to the TPM. The firmware writes the TPM +result code and the operation request number to a memory location that +ACPI can read from and pass the result on to the administrator. + +The PPI specification defines a set of mandatory and optional +operations for the firmware to implement. The ACPI interface also +allows an administrator to list the supported operations. In QEMU the +ACPI code is generated by QEMU, yet the firmware needs to implement +support on a per-operations basis, and different firmwares may support +a different subset. Therefore, QEMU introduces the virtual memory +device for PPI where the firmware can indicate which operations it +supports and ACPI can enable the ones that are supported and disable +all others. This interface lies in main memory and has the following +layout: + + +-------------+--------+--------+-------------------------------------------+ + | Field | Length | Offset | Description | + +=============+========+========+===========================================+ + | ``func`` | 0x100 | 0x000 | Firmware sets values for each supported | + | | | | operation. See defined values below. | + +-------------+--------+--------+-------------------------------------------+ + | ``ppin`` | 0x1 | 0x100 | SMI interrupt to use. Set by firmware. | + | | | | Not supported. | + +-------------+--------+--------+-------------------------------------------+ + | ``ppip`` | 0x4 | 0x101 | ACPI function index to pass to SMM code. | + | | | | Set by ACPI. Not supported. | + +-------------+--------+--------+-------------------------------------------+ + | ``pprp`` | 0x4 | 0x105 | Result of last executed operation. Set by | + | | | | firmware. See function index 5 for values.| + +-------------+--------+--------+-------------------------------------------+ + | ``pprq`` | 0x4 | 0x109 | Operation request number to execute. See | + | | | | 'Physical Presence Interface Operation | + | | | | Summary' tables in specs. Set by ACPI. | + +-------------+--------+--------+-------------------------------------------+ + | ``pprm`` | 0x4 | 0x10d | Operation request optional parameter. | + | | | | Values depend on operation. Set by ACPI. | + +-------------+--------+--------+-------------------------------------------+ + | ``lppr`` | 0x4 | 0x111 | Last executed operation request number. | + | | | | Copied from pprq field by firmware. | + +-------------+--------+--------+-------------------------------------------+ + | ``fret`` | 0x4 | 0x115 | Result code from SMM function. | + | | | | Not supported. | + +-------------+--------+--------+-------------------------------------------+ + | ``res1`` | 0x40 | 0x119 | Reserved for future use | + +-------------+--------+--------+-------------------------------------------+ + |``next_step``| 0x1 | 0x159 | Operation to execute after reboot by | + | | | | firmware. Used by firmware. | + +-------------+--------+--------+-------------------------------------------+ + | ``movv`` | 0x1 | 0x15a | Memory overwrite variable | + +-------------+--------+--------+-------------------------------------------+ + +The following values are supported for the ``func`` field. They +correspond to the values used by ACPI function index 8. + + +----------+-------------------------------------------------------------+ + | Value | Description | + +==========+=============================================================+ + | 0 | Operation is not implemented. | + +----------+-------------------------------------------------------------+ + | 1 | Operation is only accessible through firmware. | + +----------+-------------------------------------------------------------+ + | 2 | Operation is blocked for OS by firmware configuration. | + +----------+-------------------------------------------------------------+ + | 3 | Operation is allowed and physically present user required. | + +----------+-------------------------------------------------------------+ + | 4 | Operation is allowed and physically present user is not | + | | required. | + +----------+-------------------------------------------------------------+ + +The location of the table is given by the fw_cfg ``tpmppi_address`` +field. The PPI memory region size is 0x400 (``TPM_PPI_ADDR_SIZE``) to +leave enough room for future updates. + +QEMU files related to TPM ACPI tables: + - ``hw/i386/acpi-build.c`` + - ``include/hw/acpi/tpm.h`` + +TPM backend devices +=================== + +The TPM implementation is split into two parts, frontend and +backend. The frontend part is the hardware interface, such as the TPM +TIS interface described earlier, and the other part is the TPM backend +interface. The backend interfaces implement the interaction with a TPM +device, which may be a physical or an emulated device. The split +between the front- and backend devices allows a frontend to be +connected with any available backend. This enables the TIS interface +to be used with the passthrough backend or the swtpm backend. + +QEMU files related to TPM backends: + - ``backends/tpm.c`` + - ``include/sysemu/tpm_backend.h`` + - ``include/sysemu/tpm_backend_int.h`` + +The QEMU TPM passthrough device +------------------------------- + +In case QEMU is run on Linux as the host operating system it is +possible to make the hardware TPM device available to a single QEMU +guest. In this case the user must make sure that no other program is +using the device, e.g., /dev/tpm0, before trying to start QEMU with +it. + +The passthrough driver uses the host's TPM device for sending TPM +commands and receiving responses from. Besides that it accesses the +TPM device's sysfs entry for support of command cancellation. Since +none of the state of a hardware TPM can be migrated between hosts, +virtual machine migration is disabled when the TPM passthrough driver +is used. + +Since the host's TPM device will already be initialized by the host's +firmware, certain commands, e.g. ``TPM_Startup()``, sent by the +virtual firmware for device initialization, will fail. In this case +the firmware should not use the TPM. + +Sharing the device with the host is generally not a recommended usage +scenario for a TPM device. The primary reason for this is that two +operating systems can then access the device's single set of +resources, such as platform configuration registers +(PCRs). Applications or kernel security subsystems, such as the Linux +Integrity Measurement Architecture (IMA), are not expecting to share +PCRs. + +QEMU files related to the TPM passthrough device: + - ``hw/tpm/tpm_passthrough.c`` + - ``hw/tpm/tpm_util.c`` + - ``hw/tpm/tpm_util.h`` + + +Command line to start QEMU with the TPM passthrough device using the host's +hardware TPM ``/dev/tpm0``: + +.. code-block:: console + + qemu-system-x86_64 -display sdl -accel kvm \ + -m 1024 -boot d -bios bios-256k.bin -boot menu=on \ + -tpmdev passthrough,id=tpm0,path=/dev/tpm0 \ + -device tpm-tis,tpmdev=tpm0 test.img + + +The following commands should result in similar output inside the VM +with a Linux kernel that either has the TPM TIS driver built-in or +available as a module: + +.. code-block:: console + + # dmesg | grep -i tpm + [ 0.711310] tpm_tis 00:06: 1.2 TPM (device=id 0x1, rev-id 1) + + # dmesg | grep TCPA + [ 0.000000] ACPI: TCPA 0x0000000003FFD191C 000032 (v02 BOCHS \ + BXPCTCPA 0000001 BXPC 00000001) + + # ls -l /dev/tpm* + crw-------. 1 root root 10, 224 Jul 11 10:11 /dev/tpm0 + + # find /sys/devices/ | grep pcrs$ | xargs cat + PCR-00: 35 4E 3B CE 23 9F 38 59 ... + ... + PCR-23: 00 00 00 00 00 00 00 00 ... + +The QEMU TPM emulator device +---------------------------- + +The TPM emulator device uses an external TPM emulator called 'swtpm' +for sending TPM commands to and receiving responses from. The swtpm +program must have been started before trying to access it through the +TPM emulator with QEMU. + +The TPM emulator implements a command channel for transferring TPM +commands and responses as well as a control channel over which control +commands can be sent. (see the `SWTPM protocol`_ specification) + +The control channel serves the purpose of resetting, initializing, and +migrating the TPM state, among other things. + +The swtpm program behaves like a hardware TPM and therefore needs to +be initialized by the firmware running inside the QEMU virtual +machine. One necessary step for initializing the device is to send +the TPM_Startup command to it. SeaBIOS, for example, has been +instrumented to initialize a TPM 1.2 or TPM 2 device using this +command. + +QEMU files related to the TPM emulator device: + - ``hw/tpm/tpm_emulator.c`` + - ``hw/tpm/tpm_util.c`` + - ``hw/tpm/tpm_util.h`` + +The following commands start the swtpm with a UnixIO control channel over +a socket interface. They do not need to be run as root. + +.. code-block:: console + + mkdir /tmp/mytpm1 + swtpm socket --tpmstate dir=/tmp/mytpm1 \ + --ctrl type=unixio,path=/tmp/mytpm1/swtpm-sock \ + --log level=20 + +Command line to start QEMU with the TPM emulator device communicating +with the swtpm (x86): + +.. code-block:: console + + qemu-system-x86_64 -display sdl -accel kvm \ + -m 1024 -boot d -bios bios-256k.bin -boot menu=on \ + -chardev socket,id=chrtpm,path=/tmp/mytpm1/swtpm-sock \ + -tpmdev emulator,id=tpm0,chardev=chrtpm \ + -device tpm-tis,tpmdev=tpm0 test.img + +In case a pSeries machine is emulated, use the following command line: + +.. code-block:: console + + qemu-system-ppc64 -display sdl -machine pseries,accel=kvm \ + -m 1024 -bios slof.bin -boot menu=on \ + -nodefaults -device VGA -device pci-ohci -device usb-kbd \ + -chardev socket,id=chrtpm,path=/tmp/mytpm1/swtpm-sock \ + -tpmdev emulator,id=tpm0,chardev=chrtpm \ + -device tpm-spapr,tpmdev=tpm0 \ + -device spapr-vscsi,id=scsi0,reg=0x00002000 \ + -device virtio-blk-pci,scsi=off,bus=pci.0,addr=0x3,drive=drive-virtio-disk0,id=virtio-disk0 \ + -drive file=test.img,format=raw,if=none,id=drive-virtio-disk0 + +In case an Arm virt machine is emulated, use the following command line: + +.. code-block:: console + + qemu-system-aarch64 -machine virt,gic-version=3,accel=kvm \ + -cpu host -m 4G \ + -nographic -no-acpi \ + -chardev socket,id=chrtpm,path=/tmp/mytpm1/swtpm-sock \ + -tpmdev emulator,id=tpm0,chardev=chrtpm \ + -device tpm-tis-device,tpmdev=tpm0 \ + -device virtio-blk-pci,drive=drv0 \ + -drive format=qcow2,file=hda.qcow2,if=none,id=drv0 \ + -drive if=pflash,format=raw,file=flash0.img,readonly \ + -drive if=pflash,format=raw,file=flash1.img + + On Arm, ACPI boot with TPM is not yet supported. + +In case SeaBIOS is used as firmware, it should show the TPM menu item +after entering the menu with 'ESC'. + +.. code-block:: console + + Select boot device: + 1. DVD/CD [ata1-0: QEMU DVD-ROM ATAPI-4 DVD/CD] + [...] + 5. Legacy option rom + + t. TPM Configuration + +The following commands should result in similar output inside the VM +with a Linux kernel that either has the TPM TIS driver built-in or +available as a module: + +.. code-block:: console + + # dmesg | grep -i tpm + [ 0.711310] tpm_tis 00:06: 1.2 TPM (device=id 0x1, rev-id 1) + + # dmesg | grep TCPA + [ 0.000000] ACPI: TCPA 0x0000000003FFD191C 000032 (v02 BOCHS \ + BXPCTCPA 0000001 BXPC 00000001) + + # ls -l /dev/tpm* + crw-------. 1 root root 10, 224 Jul 11 10:11 /dev/tpm0 + + # find /sys/devices/ | grep pcrs$ | xargs cat + PCR-00: 35 4E 3B CE 23 9F 38 59 ... + ... + PCR-23: 00 00 00 00 00 00 00 00 ... + +Migration with the TPM emulator +=============================== + +The TPM emulator supports the following types of virtual machine +migration: + +- VM save / restore (migration into a file) +- Network migration +- Snapshotting (migration into storage like QoW2 or QED) + +The following command sequences can be used to test VM save / restore. + +In a 1st terminal start an instance of a swtpm using the following command: + +.. code-block:: console + + mkdir /tmp/mytpm1 + swtpm socket --tpmstate dir=/tmp/mytpm1 \ + --ctrl type=unixio,path=/tmp/mytpm1/swtpm-sock \ + --log level=20 --tpm2 + +In a 2nd terminal start the VM: + +.. code-block:: console + + qemu-system-x86_64 -display sdl -accel kvm \ + -m 1024 -boot d -bios bios-256k.bin -boot menu=on \ + -chardev socket,id=chrtpm,path=/tmp/mytpm1/swtpm-sock \ + -tpmdev emulator,id=tpm0,chardev=chrtpm \ + -device tpm-tis,tpmdev=tpm0 \ + -monitor stdio \ + test.img + +Verify that the attached TPM is working as expected using applications +inside the VM. + +To store the state of the VM use the following command in the QEMU +monitor in the 2nd terminal: + +.. code-block:: console + + (qemu) migrate "exec:cat > testvm.bin" + (qemu) quit + +At this point a file called ``testvm.bin`` should exists and the swtpm +and QEMU processes should have ended. + +To test 'VM restore' you have to start the swtpm with the same +parameters as before. If previously a TPM 2 [--tpm2] was saved, --tpm2 +must now be passed again on the command line. + +In the 1st terminal restart the swtpm with the same command line as +before: + +.. code-block:: console + + swtpm socket --tpmstate dir=/tmp/mytpm1 \ + --ctrl type=unixio,path=/tmp/mytpm1/swtpm-sock \ + --log level=20 --tpm2 + +In the 2nd terminal restore the state of the VM using the additional +'-incoming' option. + +.. code-block:: console + + qemu-system-x86_64 -display sdl -accel kvm \ + -m 1024 -boot d -bios bios-256k.bin -boot menu=on \ + -chardev socket,id=chrtpm,path=/tmp/mytpm1/swtpm-sock \ + -tpmdev emulator,id=tpm0,chardev=chrtpm \ + -device tpm-tis,tpmdev=tpm0 \ + -incoming "exec:cat < testvm.bin" \ + test.img + +Troubleshooting migration +------------------------- + +There are several reasons why migration may fail. In case of problems, +please ensure that the command lines adhere to the following rules +and, if possible, that identical versions of QEMU and swtpm are used +at all times. + +VM save and restore: + + - QEMU command line parameters should be identical apart from the + '-incoming' option on VM restore + + - swtpm command line parameters should be identical + +VM migration to 'localhost': + + - QEMU command line parameters should be identical apart from the + '-incoming' option on the destination side + + - swtpm command line parameters should point to two different + directories on the source and destination swtpm (--tpmstate dir=...) + (especially if different versions of libtpms were to be used on the + same machine). + +VM migration across the network: + + - QEMU command line parameters should be identical apart from the + '-incoming' option on the destination side + + - swtpm command line parameters should be identical + +VM Snapshotting: + - QEMU command line parameters should be identical + + - swtpm command line parameters should be identical + + +Besides that, migration failure reasons on the swtpm level may include +the following: + + - the versions of the swtpm on the source and destination sides are + incompatible + + - downgrading of TPM state may not be supported + + - the source and destination libtpms were compiled with different + compile-time options and the destination side refuses to accept the + state + + - different migration keys are used on the source and destination side + and the destination side cannot decrypt the migrated state + (swtpm ... --migration-key ... ) + + +.. _TIS specification: + https://trustedcomputinggroup.org/pc-client-work-group-pc-client-specific-tpm-interface-specification-tis/ + +.. _CRB specification: + https://trustedcomputinggroup.org/resource/pc-client-platform-tpm-profile-ptp-specification/ + + +.. _ACPI specification: + https://trustedcomputinggroup.org/tcg-acpi-specification/ + +.. _PPI specification: + https://trustedcomputinggroup.org/resource/tcg-physical-presence-interface-specification/ + +.. _SWTPM protocol: + https://github.com/stefanberger/swtpm/blob/master/man/man3/swtpm_ioctls.pod diff --git a/docs/specs/tpm.txt b/docs/specs/tpm.txt deleted file mode 100644 index 9c8cca042d..0000000000 --- a/docs/specs/tpm.txt +++ /dev/null @@ -1,427 +0,0 @@ -QEMU TPM Device -=============== - -= Guest-side Hardware Interface = - -The QEMU TPM emulation implements a TPM TIS hardware interface following the -Trusted Computing Group's specification "TCG PC Client Specific TPM Interface -Specification (TIS)", Specification Version 1.3, 21 March 2013. This -specification, or a later version of it, can be accessed from the following -URL: - -https://trustedcomputinggroup.org/pc-client-work-group-pc-client-specific-tpm-interface-specification-tis/ - -The TIS interface makes a memory mapped IO region in the area 0xfed40000 - -0xfed44fff available to the guest operating system. - - -QEMU files related to TPM TIS interface: - - hw/tpm/tpm_tis.c - - hw/tpm/tpm_tis.h - - -QEMU also implements a TPM CRB interface following the Trusted Computing -Group's specification "TCG PC Client Platform TPM Profile (PTP) -Specification", Family "2.0", Level 00 Revision 01.03 v22, May 22, 2017. -This specification, or a later version of it, can be accessed from the -following URL: - -https://trustedcomputinggroup.org/resource/pc-client-platform-tpm-profile-ptp-specification/ - -The CRB interface makes a memory mapped IO region in the area 0xfed40000 - -0xfed40fff (1 locality) available to the guest operating system. - -QEMU files related to TPM CRB interface: - - hw/tpm/tpm_crb.c - -= fw_cfg interface = - -The bios/firmware may read the "etc/tpm/config" fw_cfg entry for -configuring the guest appropriately. - -The entry of 6 bytes has the following content, in little-endian: - - #define TPM_VERSION_UNSPEC 0 - #define TPM_VERSION_1_2 1 - #define TPM_VERSION_2_0 2 - - #define TPM_PPI_VERSION_NONE 0 - #define TPM_PPI_VERSION_1_30 1 - - struct FwCfgTPMConfig { - uint32_t tpmppi_address; /* PPI memory location */ - uint8_t tpm_version; /* TPM version */ - uint8_t tpmppi_version; /* PPI version */ - }; - -= ACPI Interface = - -The TPM device is defined with ACPI ID "PNP0C31". QEMU builds a SSDT and passes -it into the guest through the fw_cfg device. The device description contains -the base address of the TIS interface 0xfed40000 and the size of the MMIO area -(0x5000). In case a TPM2 is used by QEMU, a TPM2 ACPI table is also provided. -The device is described to be used in polling mode rather than interrupt mode -primarily because no unused IRQ could be found. - -To support measurement logs to be written by the firmware, e.g. SeaBIOS, a TCPA -table is implemented. This table provides a 64kb buffer where the firmware can -write its log into. For TPM 2 only a more recent version of the TPM2 table -provides support for measurements logs and a TCPA table does not need to be -created. - -The TCPA and TPM2 ACPI tables follow the Trusted Computing Group specification -"TCG ACPI Specification" Family "1.2" and "2.0", Level 00 Revision 00.37. This -specification, or a later version of it, can be accessed from the following -URL: - -https://trustedcomputinggroup.org/tcg-acpi-specification/ - -== ACPI PPI Interface == - -QEMU supports the Physical Presence Interface (PPI) for TPM 1.2 and TPM 2. This -interface requires ACPI and firmware support. The specification can be found at -the following URL: - -https://trustedcomputinggroup.org/resource/tcg-physical-presence-interface-specification/ - -PPI enables a system administrator (root) to request a modification to the -TPM upon reboot. The PPI specification defines the operation requests and the -actions the firmware has to take. The system administrator passes the operation -request number to the firmware through an ACPI interface which writes this -number to a memory location that the firmware knows. Upon reboot, the firmware -finds the number and sends commands to the TPM. The firmware writes the TPM -result code and the operation request number to a memory location that ACPI can -read from and pass the result on to the administrator. - -The PPI specification defines a set of mandatory and optional operations for -the firmware to implement. The ACPI interface also allows an administrator to -list the supported operations. In QEMU the ACPI code is generated by QEMU, yet -the firmware needs to implement support on a per-operations basis, and -different firmwares may support a different subset. Therefore, QEMU introduces -the virtual memory device for PPI where the firmware can indicate which -operations it supports and ACPI can enable the ones that are supported and -disable all others. This interface lies in main memory and has the following -layout: - - +----------+--------+--------+-------------------------------------------+ - | Field | Length | Offset | Description | - +----------+--------+--------+-------------------------------------------+ - | func | 0x100 | 0x000 | Firmware sets values for each supported | - | | | | operation. See defined values below. | - +----------+--------+--------+-------------------------------------------+ - | ppin | 0x1 | 0x100 | SMI interrupt to use. Set by firmware. | - | | | | Not supported. | - +----------+--------+--------+-------------------------------------------+ - | ppip | 0x4 | 0x101 | ACPI function index to pass to SMM code. | - | | | | Set by ACPI. Not supported. | - +----------+--------+--------+-------------------------------------------+ - | pprp | 0x4 | 0x105 | Result of last executed operation. Set by | - | | | | firmware. See function index 5 for values.| - +----------+--------+--------+-------------------------------------------+ - | pprq | 0x4 | 0x109 | Operation request number to execute. See | - | | | | 'Physical Presence Interface Operation | - | | | | Summary' tables in specs. Set by ACPI. | - +----------+--------+--------+-------------------------------------------+ - | pprm | 0x4 | 0x10d | Operation request optional parameter. | - | | | | Values depend on operation. Set by ACPI. | - +----------+--------+--------+-------------------------------------------+ - | lppr | 0x4 | 0x111 | Last executed operation request number. | - | | | | Copied from pprq field by firmware. | - +----------+--------+--------+-------------------------------------------+ - | fret | 0x4 | 0x115 | Result code from SMM function. | - | | | | Not supported. | - +----------+--------+--------+-------------------------------------------+ - | res1 | 0x40 | 0x119 | Reserved for future use | - +----------+--------+--------+-------------------------------------------+ - | next_step| 0x1 | 0x159 | Operation to execute after reboot by | - | | | | firmware. Used by firmware. | - +----------+--------+--------+-------------------------------------------+ - | movv | 0x1 | 0x15a | Memory overwrite variable | - +----------+--------+--------+-------------------------------------------+ - - The following values are supported for the 'func' field. They correspond - to the values used by ACPI function index 8. - - +----------+-------------------------------------------------------------+ - | value | Description | - +----------+-------------------------------------------------------------+ - | 0 | Operation is not implemented. | - +----------+-------------------------------------------------------------+ - | 1 | Operation is only accessible through firmware. | - +----------+-------------------------------------------------------------+ - | 2 | Operation is blocked for OS by firmware configuration. | - +----------+-------------------------------------------------------------+ - | 3 | Operation is allowed and physically present user required. | - +----------+-------------------------------------------------------------+ - | 4 | Operation is allowed and physically present user is not | - | | required. | - +----------+-------------------------------------------------------------+ - -The location of the table is given by the fw_cfg tpmppi_address field. -The PPI memory region size is 0x400 (TPM_PPI_ADDR_SIZE) to leave -enough room for future updates. - - -QEMU files related to TPM ACPI tables: - - hw/i386/acpi-build.c - - include/hw/acpi/tpm.h - - -= TPM backend devices = - -The TPM implementation is split into two parts, frontend and backend. The -frontend part is the hardware interface, such as the TPM TIS interface -described earlier, and the other part is the TPM backend interface. The backend -interfaces implement the interaction with a TPM device, which may be a physical -or an emulated device. The split between the front- and backend devices allows -a frontend to be connected with any available backend. This enables the TIS -interface to be used with the passthrough backend or the (future) swtpm backend. - - -QEMU files related to TPM backends: - - backends/tpm.c - - include/sysemu/tpm_backend.h - - include/sysemu/tpm_backend_int.h - - -== The QEMU TPM passthrough device == - -In case QEMU is run on Linux as the host operating system it is possible to -make the hardware TPM device available to a single QEMU guest. In this case the -user must make sure that no other program is using the device, e.g., /dev/tpm0, -before trying to start QEMU with it. - -The passthrough driver uses the host's TPM device for sending TPM commands -and receiving responses from. Besides that it accesses the TPM device's sysfs -entry for support of command cancellation. Since none of the state of a -hardware TPM can be migrated between hosts, virtual machine migration is -disabled when the TPM passthrough driver is used. - -Since the host's TPM device will already be initialized by the host's firmware, -certain commands, e.g. TPM_Startup(), sent by the virtual firmware for device -initialization, will fail. In this case the firmware should not use the TPM. - -Sharing the device with the host is generally not a recommended usage scenario -for a TPM device. The primary reason for this is that two operating systems can -then access the device's single set of resources, such as platform configuration -registers (PCRs). Applications or kernel security subsystems, such as the -Linux Integrity Measurement Architecture (IMA), are not expecting to share PCRs. - - -QEMU files related to the TPM passthrough device: - - hw/tpm/tpm_passthrough.c - - hw/tpm/tpm_util.c - - hw/tpm/tpm_util.h - - -Command line to start QEMU with the TPM passthrough device using the host's -hardware TPM /dev/tpm0: - -qemu-system-x86_64 -display sdl -accel kvm \ - -m 1024 -boot d -bios bios-256k.bin -boot menu=on \ - -tpmdev passthrough,id=tpm0,path=/dev/tpm0 \ - -device tpm-tis,tpmdev=tpm0 test.img - -The following commands should result in similar output inside the VM with a -Linux kernel that either has the TPM TIS driver built-in or available as a -module: - -#> dmesg | grep -i tpm -[ 0.711310] tpm_tis 00:06: 1.2 TPM (device=id 0x1, rev-id 1) - -#> dmesg | grep TCPA -[ 0.000000] ACPI: TCPA 0x0000000003FFD191C 000032 (v02 BOCHS \ - BXPCTCPA 0000001 BXPC 00000001) - -#> ls -l /dev/tpm* -crw-------. 1 root root 10, 224 Jul 11 10:11 /dev/tpm0 - -#> find /sys/devices/ | grep pcrs$ | xargs cat -PCR-00: 35 4E 3B CE 23 9F 38 59 ... -... -PCR-23: 00 00 00 00 00 00 00 00 ... - - -== The QEMU TPM emulator device == - -The TPM emulator device uses an external TPM emulator called 'swtpm' for -sending TPM commands to and receiving responses from. The swtpm program -must have been started before trying to access it through the TPM emulator -with QEMU. - -The TPM emulator implements a command channel for transferring TPM commands -and responses as well as a control channel over which control commands can -be sent. The specification for the control channel can be found here: - -https://github.com/stefanberger/swtpm/blob/master/man/man3/swtpm_ioctls.pod - - -The control channel serves the purpose of resetting, initializing, and -migrating the TPM state, among other things. - -The swtpm program behaves like a hardware TPM and therefore needs to be -initialized by the firmware running inside the QEMU virtual machine. -One necessary step for initializing the device is to send the TPM_Startup -command to it. SeaBIOS, for example, has been instrumented to initialize -a TPM 1.2 or TPM 2 device using this command. - - -QEMU files related to the TPM emulator device: - - hw/tpm/tpm_emulator.c - - hw/tpm/tpm_util.c - - hw/tpm/tpm_util.h - - -The following commands start the swtpm with a UnixIO control channel over -a socket interface. They do not need to be run as root. - -mkdir /tmp/mytpm1 -swtpm socket --tpmstate dir=/tmp/mytpm1 \ - --ctrl type=unixio,path=/tmp/mytpm1/swtpm-sock \ - --log level=20 - -Command line to start QEMU with the TPM emulator device communicating with -the swtpm: - -qemu-system-x86_64 -display sdl -accel kvm \ - -m 1024 -boot d -bios bios-256k.bin -boot menu=on \ - -chardev socket,id=chrtpm,path=/tmp/mytpm1/swtpm-sock \ - -tpmdev emulator,id=tpm0,chardev=chrtpm \ - -device tpm-tis,tpmdev=tpm0 test.img - - -In case SeaBIOS is used as firmware, it should show the TPM menu item -after entering the menu with 'ESC'. - -Select boot device: -1. DVD/CD [ata1-0: QEMU DVD-ROM ATAPI-4 DVD/CD] -[...] -5. Legacy option rom - -t. TPM Configuration - - -The following commands should result in similar output inside the VM with a -Linux kernel that either has the TPM TIS driver built-in or available as a -module: - -#> dmesg | grep -i tpm -[ 0.711310] tpm_tis 00:06: 1.2 TPM (device=id 0x1, rev-id 1) - -#> dmesg | grep TCPA -[ 0.000000] ACPI: TCPA 0x0000000003FFD191C 000032 (v02 BOCHS \ - BXPCTCPA 0000001 BXPC 00000001) - -#> ls -l /dev/tpm* -crw-------. 1 root root 10, 224 Jul 11 10:11 /dev/tpm0 - -#> find /sys/devices/ | grep pcrs$ | xargs cat -PCR-00: 35 4E 3B CE 23 9F 38 59 ... -... -PCR-23: 00 00 00 00 00 00 00 00 ... - - -=== Migration with the TPM emulator === - -The TPM emulator supports the following types of virtual machine migration: - -- VM save / restore (migration into a file) -- Network migration -- Snapshotting (migration into storage like QoW2 or QED) - -The following command sequences can be used to test VM save / restore. - - -In a 1st terminal start an instance of a swtpm using the following command: - -mkdir /tmp/mytpm1 -swtpm socket --tpmstate dir=/tmp/mytpm1 \ - --ctrl type=unixio,path=/tmp/mytpm1/swtpm-sock \ - --log level=20 --tpm2 - -In a 2nd terminal start the VM: - -qemu-system-x86_64 -display sdl -accel kvm \ - -m 1024 -boot d -bios bios-256k.bin -boot menu=on \ - -chardev socket,id=chrtpm,path=/tmp/mytpm1/swtpm-sock \ - -tpmdev emulator,id=tpm0,chardev=chrtpm \ - -device tpm-tis,tpmdev=tpm0 \ - -monitor stdio \ - test.img - -Verify that the attached TPM is working as expected using applications inside -the VM. - -To store the state of the VM use the following command in the QEMU monitor in -the 2nd terminal: - -(qemu) migrate "exec:cat > testvm.bin" -(qemu) quit - -At this point a file called 'testvm.bin' should exists and the swtpm and QEMU -processes should have ended. - -To test 'VM restore' you have to start the swtpm with the same parameters -as before. If previously a TPM 2 [--tpm2] was saved, --tpm2 must now be -passed again on the command line. - -In the 1st terminal restart the swtpm with the same command line as before: - -swtpm socket --tpmstate dir=/tmp/mytpm1 \ - --ctrl type=unixio,path=/tmp/mytpm1/swtpm-sock \ - --log level=20 --tpm2 - -In the 2nd terminal restore the state of the VM using the additional -'-incoming' option. - -qemu-system-x86_64 -display sdl -accel kvm \ - -m 1024 -boot d -bios bios-256k.bin -boot menu=on \ - -chardev socket,id=chrtpm,path=/tmp/mytpm1/swtpm-sock \ - -tpmdev emulator,id=tpm0,chardev=chrtpm \ - -device tpm-tis,tpmdev=tpm0 \ - -incoming "exec:cat < testvm.bin" \ - test.img - - -Troubleshooting migration: - -There are several reasons why migration may fail. In case of problems, -please ensure that the command lines adhere to the following rules and, -if possible, that identical versions of QEMU and swtpm are used at all -times. - -VM save and restore: - - QEMU command line parameters should be identical apart from the - '-incoming' option on VM restore - - swtpm command line parameters should be identical - -VM migration to 'localhost': - - QEMU command line parameters should be identical apart from the - '-incoming' option on the destination side - - swtpm command line parameters should point to two different - directories on the source and destination swtpm (--tpmstate dir=...) - (especially if different versions of libtpms were to be used on the - same machine). - -VM migration across the network: - - QEMU command line parameters should be identical apart from the - '-incoming' option on the destination side - - swtpm command line parameters should be identical - -VM Snapshotting: - - QEMU command line parameters should be identical - - swtpm command line parameters should be identical - - -Besides that, migration failure reasons on the swtpm level may include -the following: - - - the versions of the swtpm on the source and destination sides are - incompatible - - downgrading of TPM state may not be supported - - the source and destination libtpms were compiled with different - compile-time options and the destination side refuses to accept the - state - - different migration keys are used on the source and destination side - and the destination side cannot decrypt the migrated state - (swtpm ... --migration-key ... ) diff --git a/docs/sphinx/hxtool.py b/docs/sphinx/hxtool.py new file mode 100644 index 0000000000..fb0649a3d5 --- /dev/null +++ b/docs/sphinx/hxtool.py @@ -0,0 +1,192 @@ +# coding=utf-8 +# +# QEMU hxtool .hx file parsing extension +# +# Copyright (c) 2020 Linaro +# +# This work is licensed under the terms of the GNU GPLv2 or later. +# See the COPYING file in the top-level directory. +"""hxtool is a Sphinx extension that implements the hxtool-doc directive""" + +# The purpose of this extension is to read fragments of rST +# from .hx files, and insert them all into the current document. +# The rST fragments are delimited by SRST/ERST lines. +# The conf.py file must set the hxtool_srctree config value to +# the root of the QEMU source tree. +# Each hxtool-doc:: directive takes one argument which is the +# path of the .hx file to process, relative to the source tree. + +import os +import re +from enum import Enum + +from docutils import nodes +from docutils.statemachine import ViewList +from docutils.parsers.rst import directives, Directive +from sphinx.errors import ExtensionError +from sphinx.util.nodes import nested_parse_with_titles +import sphinx + +# Sphinx up to 1.6 uses AutodocReporter; 1.7 and later +# use switch_source_input. Check borrowed from kerneldoc.py. +Use_SSI = sphinx.__version__[:3] >= '1.7' +if Use_SSI: + from sphinx.util.docutils import switch_source_input +else: + from sphinx.ext.autodoc import AutodocReporter + +__version__ = '1.0' + +# We parse hx files with a state machine which may be in one of two +# states: reading the C code fragment, or inside a rST fragment. +class HxState(Enum): + CTEXT = 1 + RST = 2 + +def serror(file, lnum, errtext): + """Raise an exception giving a user-friendly syntax error message""" + raise ExtensionError('%s line %d: syntax error: %s' % (file, lnum, errtext)) + +def parse_directive(line): + """Return first word of line, if any""" + return re.split('\W', line)[0] + +def parse_defheading(file, lnum, line): + """Handle a DEFHEADING directive""" + # The input should be "DEFHEADING(some string)", though note that + # the 'some string' could be the empty string. If the string is + # empty we ignore the directive -- these are used only to add + # blank lines in the plain-text content of the --help output. + # + # Return the heading text. We strip out any trailing ':' for + # consistency with other headings in the rST documentation. + match = re.match(r'DEFHEADING\((.*?):?\)', line) + if match is None: + serror(file, lnum, "Invalid DEFHEADING line") + return match.group(1) + +def parse_archheading(file, lnum, line): + """Handle an ARCHHEADING directive""" + # The input should be "ARCHHEADING(some string, other arg)", + # though note that the 'some string' could be the empty string. + # As with DEFHEADING, empty string ARCHHEADINGs will be ignored. + # + # Return the heading text. We strip out any trailing ':' for + # consistency with other headings in the rST documentation. + match = re.match(r'ARCHHEADING\((.*?):?,.*\)', line) + if match is None: + serror(file, lnum, "Invalid ARCHHEADING line") + return match.group(1) + +class HxtoolDocDirective(Directive): + """Extract rST fragments from the specified .hx file""" + required_argument = 1 + optional_arguments = 1 + option_spec = { + 'hxfile': directives.unchanged_required + } + has_content = False + + def run(self): + env = self.state.document.settings.env + hxfile = env.config.hxtool_srctree + '/' + self.arguments[0] + + # Tell sphinx of the dependency + env.note_dependency(os.path.abspath(hxfile)) + + state = HxState.CTEXT + # We build up lines of rST in this ViewList, which we will + # later put into a 'section' node. + rstlist = ViewList() + current_node = None + node_list = [] + + with open(hxfile) as f: + lines = (l.rstrip() for l in f) + for lnum, line in enumerate(lines, 1): + directive = parse_directive(line) + + if directive == 'HXCOMM': + pass + elif directive == 'SRST': + if state == HxState.RST: + serror(hxfile, lnum, 'expected ERST, found SRST') + else: + state = HxState.RST + elif directive == 'ERST': + if state == HxState.CTEXT: + serror(hxfile, lnum, 'expected SRST, found ERST') + else: + state = HxState.CTEXT + elif directive == 'DEFHEADING' or directive == 'ARCHHEADING': + if directive == 'DEFHEADING': + heading = parse_defheading(hxfile, lnum, line) + else: + heading = parse_archheading(hxfile, lnum, line) + if heading == "": + continue + # Put the accumulated rST into the previous node, + # and then start a fresh section with this heading. + if len(rstlist) > 0: + if current_node is None: + # We had some rST fragments before the first + # DEFHEADING. We don't have a section to put + # these in, so rather than magicing up a section, + # make it a syntax error. + serror(hxfile, lnum, + 'first DEFHEADING must precede all rST text') + self.do_parse(rstlist, current_node) + rstlist = ViewList() + if current_node is not None: + node_list.append(current_node) + section_id = 'hxtool-%d' % env.new_serialno('hxtool') + current_node = nodes.section(ids=[section_id]) + current_node += nodes.title(heading, heading) + else: + # Not a directive: put in output if we are in rST fragment + if state == HxState.RST: + # Sphinx counts its lines from 0 + rstlist.append(line, hxfile, lnum - 1) + + if current_node is None: + # We don't have multiple sections, so just parse the rst + # fragments into a dummy node so we can return the children. + current_node = nodes.section() + self.do_parse(rstlist, current_node) + return current_node.children + else: + # Put the remaining accumulated rST into the last section, and + # return all the sections. + if len(rstlist) > 0: + self.do_parse(rstlist, current_node) + node_list.append(current_node) + return node_list + + # This is from kerneldoc.py -- it works around an API change in + # Sphinx between 1.6 and 1.7. Unlike kerneldoc.py, we use + # sphinx.util.nodes.nested_parse_with_titles() rather than the + # plain self.state.nested_parse(), and so we can drop the saving + # of title_styles and section_level that kerneldoc.py does, + # because nested_parse_with_titles() does that for us. + def do_parse(self, result, node): + if Use_SSI: + with switch_source_input(self.state, result): + nested_parse_with_titles(self.state, result, node) + else: + save = self.state.memo.reporter + self.state.memo.reporter = AutodocReporter(result, self.state.memo.reporter) + try: + nested_parse_with_titles(self.state, result, node) + finally: + self.state.memo.reporter = save + +def setup(app): + """ Register hxtool-doc directive with Sphinx""" + app.add_config_value('hxtool_srctree', None, 'env') + app.add_directive('hxtool-doc', HxtoolDocDirective) + + return dict( + version = __version__, + parallel_read_safe = True, + parallel_write_safe = True + ) diff --git a/docs/sphinx/kerneldoc.py b/docs/sphinx/kerneldoc.py new file mode 100644 index 0000000000..3e87940206 --- /dev/null +++ b/docs/sphinx/kerneldoc.py @@ -0,0 +1,173 @@ +# coding=utf-8 +# +# Copyright © 2016 Intel Corporation +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice (including the next +# paragraph) shall be included in all copies or substantial portions of the +# Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. +# +# Authors: +# Jani Nikula +# +# Please make sure this works on both python2 and python3. +# + +import codecs +import os +import subprocess +import sys +import re +import glob + +from docutils import nodes, statemachine +from docutils.statemachine import ViewList +from docutils.parsers.rst import directives, Directive + +# +# AutodocReporter is only good up to Sphinx 1.7 +# +import sphinx + +Use_SSI = sphinx.__version__[:3] >= '1.7' +if Use_SSI: + from sphinx.util.docutils import switch_source_input +else: + from sphinx.ext.autodoc import AutodocReporter + +import kernellog + +__version__ = '1.0' + +class KernelDocDirective(Directive): + """Extract kernel-doc comments from the specified file""" + required_argument = 1 + optional_arguments = 4 + option_spec = { + 'doc': directives.unchanged_required, + 'functions': directives.unchanged, + 'export': directives.unchanged, + 'internal': directives.unchanged, + } + has_content = False + + def run(self): + env = self.state.document.settings.env + cmd = [env.config.kerneldoc_bin, '-rst', '-enable-lineno'] + + filename = env.config.kerneldoc_srctree + '/' + self.arguments[0] + export_file_patterns = [] + + # Tell sphinx of the dependency + env.note_dependency(os.path.abspath(filename)) + + tab_width = self.options.get('tab-width', self.state.document.settings.tab_width) + + # FIXME: make this nicer and more robust against errors + if 'export' in self.options: + cmd += ['-export'] + export_file_patterns = str(self.options.get('export')).split() + elif 'internal' in self.options: + cmd += ['-internal'] + export_file_patterns = str(self.options.get('internal')).split() + elif 'doc' in self.options: + cmd += ['-function', str(self.options.get('doc'))] + elif 'functions' in self.options: + functions = self.options.get('functions').split() + if functions: + for f in functions: + cmd += ['-function', f] + else: + cmd += ['-no-doc-sections'] + + for pattern in export_file_patterns: + for f in glob.glob(env.config.kerneldoc_srctree + '/' + pattern): + env.note_dependency(os.path.abspath(f)) + cmd += ['-export-file', f] + + cmd += ['-sphinx-version', sphinx.__version__] + cmd += [filename] + + try: + kernellog.verbose(env.app, + 'calling kernel-doc \'%s\'' % (" ".join(cmd))) + + p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + out, err = p.communicate() + + out, err = codecs.decode(out, 'utf-8'), codecs.decode(err, 'utf-8') + + if p.returncode != 0: + sys.stderr.write(err) + + kernellog.warn(env.app, + 'kernel-doc \'%s\' failed with return code %d' % (" ".join(cmd), p.returncode)) + return [nodes.error(None, nodes.paragraph(text = "kernel-doc missing"))] + elif env.config.kerneldoc_verbosity > 0: + sys.stderr.write(err) + + lines = statemachine.string2lines(out, tab_width, convert_whitespace=True) + result = ViewList() + + lineoffset = 0; + line_regex = re.compile("^#define LINENO ([0-9]+)$") + for line in lines: + match = line_regex.search(line) + if match: + # sphinx counts lines from 0 + lineoffset = int(match.group(1)) - 1 + # we must eat our comments since the upset the markup + else: + result.append(line, filename, lineoffset) + lineoffset += 1 + + node = nodes.section() + self.do_parse(result, node) + + return node.children + + except Exception as e: # pylint: disable=W0703 + kernellog.warn(env.app, 'kernel-doc \'%s\' processing failed with: %s' % + (" ".join(cmd), str(e))) + return [nodes.error(None, nodes.paragraph(text = "kernel-doc missing"))] + + def do_parse(self, result, node): + if Use_SSI: + with switch_source_input(self.state, result): + self.state.nested_parse(result, 0, node, match_titles=1) + else: + save = self.state.memo.title_styles, self.state.memo.section_level, self.state.memo.reporter + self.state.memo.reporter = AutodocReporter(result, self.state.memo.reporter) + self.state.memo.title_styles, self.state.memo.section_level = [], 0 + try: + self.state.nested_parse(result, 0, node, match_titles=1) + finally: + self.state.memo.title_styles, self.state.memo.section_level, self.state.memo.reporter = save + + +def setup(app): + app.add_config_value('kerneldoc_bin', None, 'env') + app.add_config_value('kerneldoc_srctree', None, 'env') + app.add_config_value('kerneldoc_verbosity', 1, 'env') + + app.add_directive('kernel-doc', KernelDocDirective) + + return dict( + version = __version__, + parallel_read_safe = True, + parallel_write_safe = True + ) diff --git a/docs/sphinx/kernellog.py b/docs/sphinx/kernellog.py new file mode 100644 index 0000000000..af924f51a7 --- /dev/null +++ b/docs/sphinx/kernellog.py @@ -0,0 +1,28 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Sphinx has deprecated its older logging interface, but the replacement +# only goes back to 1.6. So here's a wrapper layer to keep around for +# as long as we support 1.4. +# +import sphinx + +if sphinx.__version__[:3] >= '1.6': + UseLogging = True + from sphinx.util import logging + logger = logging.getLogger('kerneldoc') +else: + UseLogging = False + +def warn(app, message): + if UseLogging: + logger.warning(message) + else: + app.warn(message) + +def verbose(app, message): + if UseLogging: + logger.verbose(message) + else: + app.verbose(message) + + diff --git a/docs/arm-cpu-features.rst b/docs/system/arm/cpu-features.rst similarity index 86% rename from docs/arm-cpu-features.rst rename to docs/system/arm/cpu-features.rst index 1b367e22e1..2d5c06cd01 100644 --- a/docs/arm-cpu-features.rst +++ b/docs/system/arm/cpu-features.rst @@ -1,19 +1,13 @@ +Arm CPU Features ================ -ARM CPU Features -================ - -Examples of probing and using ARM CPU features - -Introduction -============ CPU features are optional features that a CPU of supporting type may choose to implement or not. In QEMU, optional CPU features have corresponding boolean CPU proprieties that, when enabled, indicate that the feature is implemented, and, conversely, when disabled, -indicate that it is not implemented. An example of an ARM CPU feature +indicate that it is not implemented. An example of an Arm CPU feature is the Performance Monitoring Unit (PMU). CPU types such as the -Cortex-A15 and the Cortex-A57, which respectively implement ARM +Cortex-A15 and the Cortex-A57, which respectively implement Arm architecture reference manuals ARMv7-A and ARMv8-A, may both optionally implement PMUs. For example, if a user wants to use a Cortex-A15 without a PMU, then the `-cpu` parameter should contain `pmu=off` on the QEMU @@ -31,7 +25,9 @@ supporting the feature or only supporting the feature under certain configurations. For example, the `aarch64` CPU feature, which, when disabled, enables the optional AArch32 CPU feature, is only supported when using the KVM accelerator and when running on a host CPU type that -supports the feature. +supports the feature. While `aarch64` currently only works with KVM, +it could work with TCG. CPU features that are specific to KVM are +prefixed with "kvm-" and are described in "KVM VCPU Features". CPU Feature Probing =================== @@ -41,9 +37,9 @@ CPU type is possible with the `query-cpu-model-expansion` QMP command. Below are some examples where `scripts/qmp/qmp-shell` (see the top comment block in the script for usage) is used to issue the QMP commands. -(1) Determine which CPU features are available for the `max` CPU type - (Note, we started QEMU with qemu-system-aarch64, so `max` is - implementing the ARMv8-A reference manual in this case):: +1. Determine which CPU features are available for the `max` CPU type + (Note, we started QEMU with qemu-system-aarch64, so `max` is + implementing the ARMv8-A reference manual in this case):: (QEMU) query-cpu-model-expansion type=full model={"name":"max"} { "return": { @@ -171,6 +167,39 @@ disabling many SVE vector lengths would be quite verbose, the `sve` CPU properties have special semantics (see "SVE CPU Property Parsing Semantics"). +KVM VCPU Features +================= + +KVM VCPU features are CPU features that are specific to KVM, such as +paravirt features or features that enable CPU virtualization extensions. +The features' CPU properties are only available when KVM is enabled and +are named with the prefix "kvm-". KVM VCPU features may be probed, +enabled, and disabled in the same way as other CPU features. Below is +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* + adjusted). + + When virtual time adjustment is enabled each + time the VM transitions back to running state + the VCPU's virtual counter is updated to ensure + stopped time is not counted. This avoids time + jumps surprising guest OSes and applications, + as long as they use the virtual counter for + timekeeping. However it has the side effect of + the virtual and physical counters diverging. + All timekeeping based on the virtual counter + will appear to lag behind any timekeeping that + does not subtract VM stopped time. The guest + may resynchronize its virtual counter with + other time sources as needed. + + Enable kvm-no-adjvtime to disable virtual time + adjustment, also restoring the legacy (pre-5.0) + behavior. + SVE CPU Properties ================== diff --git a/docs/system/arm/integratorcp.rst b/docs/system/arm/integratorcp.rst new file mode 100644 index 0000000000..e6f050f602 --- /dev/null +++ b/docs/system/arm/integratorcp.rst @@ -0,0 +1,16 @@ +Integrator/CP (``integratorcp``) +================================ + +The Arm Integrator/CP board is emulated with the following devices: + +- ARM926E, ARM1026E, ARM946E, ARM1136 or Cortex-A8 CPU + +- Two PL011 UARTs + +- SMC 91c111 Ethernet adapter + +- PL110 LCD controller + +- PL050 KMI with PS/2 keyboard and mouse. + +- PL181 MultiMedia Card Interface with SD card. diff --git a/docs/system/arm/musicpal.rst b/docs/system/arm/musicpal.rst new file mode 100644 index 0000000000..9de380edf8 --- /dev/null +++ b/docs/system/arm/musicpal.rst @@ -0,0 +1,19 @@ +Freecom MusicPal (``musicpal``) +=============================== + +The Freecom MusicPal internet radio emulation includes the following +elements: + +- Marvell MV88W8618 Arm core. + +- 32 MB RAM, 256 KB SRAM, 8 MB flash. + +- Up to 2 16550 UARTs + +- MV88W8xx8 Ethernet controller + +- MV88W8618 audio controller, WM8750 CODEC and mixer + +- 128x64 display with brightness control + +- 2 buttons, 2 navigation wheels with button function diff --git a/docs/system/arm/nseries.rst b/docs/system/arm/nseries.rst new file mode 100644 index 0000000000..cd9edf5d88 --- /dev/null +++ b/docs/system/arm/nseries.rst @@ -0,0 +1,33 @@ +Nokia N800 and N810 tablets (``n800``, ``n810``) +================================================ + +Nokia N800 and N810 internet tablets (known also as RX-34 and RX-44 / +48) emulation supports the following elements: + +- Texas Instruments OMAP2420 System-on-chip (ARM1136 core) + +- RAM and non-volatile OneNAND Flash memories + +- Display connected to EPSON remote framebuffer chip and OMAP on-chip + display controller and a LS041y3 MIPI DBI-C controller + +- TI TSC2301 (in N800) and TI TSC2005 (in N810) touchscreen + controllers driven through SPI bus + +- National Semiconductor LM8323-controlled qwerty keyboard driven + through |I2C| bus + +- Secure Digital card connected to OMAP MMC/SD host + +- Three OMAP on-chip UARTs and on-chip STI debugging console + +- Mentor Graphics \"Inventra\" dual-role USB controller embedded in a + TI TUSB6010 chip - only USB host mode is supported + +- TI TMP105 temperature sensor driven through |I2C| bus + +- TI TWL92230C power management companion with an RTC on + |I2C| bus + +- Nokia RETU and TAHVO multi-purpose chips with an RTC, connected + through CBUS diff --git a/docs/system/arm/orangepi.rst b/docs/system/arm/orangepi.rst new file mode 100644 index 0000000000..c41adad488 --- /dev/null +++ b/docs/system/arm/orangepi.rst @@ -0,0 +1,253 @@ +Orange Pi PC (``orangepi-pc``) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The Xunlong Orange Pi PC is an Allwinner H3 System on Chip +based embedded computer with mainline support in both U-Boot +and Linux. The board comes with a Quad Core Cortex-A7 @ 1.3GHz, +1GiB RAM, 100Mbit ethernet, USB, SD/MMC, USB, HDMI and +various other I/O. + +Supported devices +""""""""""""""""" + +The Orange Pi PC machine supports the following devices: + + * SMP (Quad Core Cortex-A7) + * Generic Interrupt Controller configuration + * SRAM mappings + * SDRAM controller + * Real Time Clock + * Timer device (re-used from Allwinner A10) + * UART + * SD/MMC storage controller + * EMAC ethernet + * USB 2.0 interfaces + * Clock Control Unit + * System Control module + * Security Identifier device + +Limitations +""""""""""" + +Currently, Orange Pi PC does *not* support the following features: + +- Graphical output via HDMI, GPU and/or the Display Engine +- Audio output +- Hardware Watchdog + +Also see the 'unimplemented' array in the Allwinner H3 SoC module +for a complete list of unimplemented I/O devices: ``./hw/arm/allwinner-h3.c`` + +Boot options +"""""""""""" + +The Orange Pi PC machine can start using the standard -kernel functionality +for loading a Linux kernel or ELF executable. Additionally, the Orange Pi PC +machine can also emulate the BootROM which is present on an actual Allwinner H3 +based SoC, which loads the bootloader from a SD card, specified via the -sd argument +to qemu-system-arm. + +Machine-specific options +"""""""""""""""""""""""" + +The following machine-specific options are supported: + +- allwinner-rtc.base-year=YYYY + + The Allwinner RTC device is automatically created by the Orange Pi PC machine + and uses a default base year value which can be overridden using the 'base-year' property. + The base year is the actual represented year when the RTC year value is zero. + This option can be used in case the target operating system driver uses a different + base year value. The minimum value for the base year is 1900. + +- allwinner-sid.identifier=abcd1122-a000-b000-c000-12345678ffff + + The Security Identifier value can be read by the guest. + For example, U-Boot uses it to determine a unique MAC address. + +The above machine-specific options can be specified in qemu-system-arm +via the '-global' argument, for example: + +.. code-block:: bash + + $ qemu-system-arm -M orangepi-pc -sd mycard.img \ + -global allwinner-rtc.base-year=2000 + +Running mainline Linux +"""""""""""""""""""""" + +Mainline Linux kernels from 4.19 up to latest master are known to work. +To build a Linux mainline kernel that can be booted by the Orange Pi PC machine, +simply configure the kernel using the sunxi_defconfig configuration: + +.. code-block:: bash + + $ ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- make mrproper + $ ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- make sunxi_defconfig + +To be able to use USB storage, you need to manually enable the corresponding +configuration item. Start the kconfig configuration tool: + +.. code-block:: bash + + $ ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- make menuconfig + +Navigate to the following item, enable it and save your configuration: + + Device Drivers > USB support > USB Mass Storage support + +Build the Linux kernel with: + +.. code-block:: bash + + $ ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- make + +To boot the newly build linux kernel in QEMU with the Orange Pi PC machine, use: + +.. code-block:: bash + + $ qemu-system-arm -M orangepi-pc -nic user -nographic \ + -kernel /path/to/linux/arch/arm/boot/zImage \ + -append 'console=ttyS0,115200' \ + -dtb /path/to/linux/arch/arm/boot/dts/sun8i-h3-orangepi-pc.dtb + +Orange Pi PC images +""""""""""""""""""" + +Note that the mainline kernel does not have a root filesystem. You may provide it +with an official Orange Pi PC image from the official website: + + http://www.orangepi.org/downloadresources/ + +Another possibility is to run an Armbian image for Orange Pi PC which +can be downloaded from: + + https://www.armbian.com/orange-pi-pc/ + +Alternatively, you can also choose to build you own image with buildroot +using the orangepi_pc_defconfig. Also see https://buildroot.org for more information. + +You can choose to attach the selected image either as an SD card or as USB mass storage. +For example, to boot using the Orange Pi PC Debian image on SD card, simply add the -sd +argument and provide the proper root= kernel parameter: + +.. code-block:: bash + + $ qemu-system-arm -M orangepi-pc -nic user -nographic \ + -kernel /path/to/linux/arch/arm/boot/zImage \ + -append 'console=ttyS0,115200 root=/dev/mmcblk0p2' \ + -dtb /path/to/linux/arch/arm/boot/dts/sun8i-h3-orangepi-pc.dtb \ + -sd OrangePi_pc_debian_stretch_server_linux5.3.5_v1.0.img + +To attach the image as an USB mass storage device to the machine, +simply append to the command: + +.. code-block:: bash + + -drive if=none,id=stick,file=myimage.img \ + -device usb-storage,bus=usb-bus.0,drive=stick + +Instead of providing a custom Linux kernel via the -kernel command you may also +choose to let the Orange Pi PC machine load the bootloader from SD card, just like +a real board would do using the BootROM. Simply pass the selected image via the -sd +argument and remove the -kernel, -append, -dbt and -initrd arguments: + +.. code-block:: bash + + $ qemu-system-arm -M orangepi-pc -nic user -nographic \ + -sd Armbian_19.11.3_Orangepipc_buster_current_5.3.9.img + +Note that both the official Orange Pi PC images and Armbian images start +a lot of userland programs via systemd. Depending on the host hardware and OS, +they may be slow to emulate, especially due to emulating the 4 cores. +To help reduce the performance slow down due to emulating the 4 cores, you can +give the following kernel parameters via U-Boot (or via -append): + +.. code-block:: bash + + => setenv extraargs 'systemd.default_timeout_start_sec=9000 loglevel=7 nosmp console=ttyS0,115200' + +Running U-Boot +"""""""""""""" + +U-Boot mainline can be build and configured using the orangepi_pc_defconfig +using similar commands as describe above for Linux. Note that it is recommended +for development/testing to select the following configuration setting in U-Boot: + + Device Tree Control > Provider for DTB for DT Control > Embedded DTB + +To start U-Boot using the Orange Pi PC machine, provide the +u-boot binary to the -kernel argument: + +.. code-block:: bash + + $ qemu-system-arm -M orangepi-pc -nic user -nographic \ + -kernel /path/to/uboot/u-boot -sd disk.img + +Use the following U-boot commands to load and boot a Linux kernel from SD card: + +.. code-block:: bash + + => setenv bootargs console=ttyS0,115200 + => ext2load mmc 0 0x42000000 zImage + => ext2load mmc 0 0x43000000 sun8i-h3-orangepi-pc.dtb + => bootz 0x42000000 - 0x43000000 + +Running NetBSD +"""""""""""""" + +The NetBSD operating system also includes support for Allwinner H3 based boards, +including the Orange Pi PC. NetBSD 9.0 is known to work best for the Orange Pi PC +board and provides a fully working system with serial console, networking and storage. +For the Orange Pi PC machine, get the 'evbarm-earmv7hf' based image from: + + https://cdn.netbsd.org/pub/NetBSD/NetBSD-9.0/evbarm-earmv7hf/binary/gzimg/armv7.img.gz + +The image requires manually installing U-Boot in the image. Build U-Boot with +the orangepi_pc_defconfig configuration as described in the previous section. +Next, unzip the NetBSD image and write the U-Boot binary including SPL using: + +.. code-block:: bash + + $ gunzip armv7.img.gz + $ dd if=/path/to/u-boot-sunxi-with-spl.bin of=armv7.img bs=1024 seek=8 conv=notrunc + +Finally, before starting the machine the SD image must be extended such +that the NetBSD kernel will not conclude the NetBSD partition is larger than +the emulated SD card: + +.. code-block:: bash + + $ dd if=/dev/zero bs=1M count=64 >> armv7.img + +Start the machine using the following command: + +.. code-block:: bash + + $ qemu-system-arm -M orangepi-pc -nic user -nographic \ + -sd armv7.img -global allwinner-rtc.base-year=2000 + +At the U-Boot stage, interrupt the automatic boot process by pressing a key +and set the following environment variables before booting: + +.. code-block:: bash + + => setenv bootargs root=ld0a + => setenv kernel netbsd-GENERIC.ub + => setenv fdtfile dtb/sun8i-h3-orangepi-pc.dtb + => setenv bootcmd 'fatload mmc 0:1 ${kernel_addr_r} ${kernel}; fatload mmc 0:1 ${fdt_addr_r} ${fdtfile}; fdt addr ${fdt_addr_r}; bootm ${kernel_addr_r} - ${fdt_addr_r}' + +Optionally you may save the environment variables to SD card with 'saveenv'. +To continue booting simply give the 'boot' command and NetBSD boots. + +Orange Pi PC acceptance tests +""""""""""""""""""""""""""""" + +The Orange Pi PC machine has several acceptance tests included. +To run the whole set of tests, build QEMU from source and simply +provide the following command: + +.. code-block:: bash + + $ AVOCADO_ALLOW_LARGE_STORAGE=yes avocado --show=app,console run \ + -t machine:orangepi-pc tests/acceptance/boot_linux_console.py diff --git a/docs/system/arm/palm.rst b/docs/system/arm/palm.rst new file mode 100644 index 0000000000..47ff9b36d4 --- /dev/null +++ b/docs/system/arm/palm.rst @@ -0,0 +1,23 @@ +Palm Tungsten|E PDA (``cheetah``) +================================= + +The Palm Tungsten|E PDA (codename \"Cheetah\") emulation includes the +following elements: + +- Texas Instruments OMAP310 System-on-chip (ARM925T core) + +- ROM and RAM memories (ROM firmware image can be loaded with + -option-rom) + +- On-chip LCD controller + +- On-chip Real Time Clock + +- TI TSC2102i touchscreen controller / analog-digital converter / + Audio CODEC, connected through MicroWire and |I2S| busses + +- GPIO-connected matrix keypad + +- Secure Digital card connected to OMAP MMC/SD host + +- Three on-chip UARTs diff --git a/docs/system/arm/realview.rst b/docs/system/arm/realview.rst new file mode 100644 index 0000000000..65f5be346b --- /dev/null +++ b/docs/system/arm/realview.rst @@ -0,0 +1,34 @@ +Arm Realview boards (``realview-eb``, ``realview-eb-mpcore``, ``realview-pb-a8``, ``realview-pbx-a9``) +====================================================================================================== + +Several variants of the Arm RealView baseboard are emulated, including +the EB, PB-A8 and PBX-A9. Due to interactions with the bootloader, only +certain Linux kernel configurations work out of the box on these boards. + +Kernels for the PB-A8 board should have CONFIG_REALVIEW_HIGH_PHYS_OFFSET +enabled in the kernel, and expect 512M RAM. Kernels for The PBX-A9 board +should have CONFIG_SPARSEMEM enabled, CONFIG_REALVIEW_HIGH_PHYS_OFFSET +disabled and expect 1024M RAM. + +The following devices are emulated: + +- ARM926E, ARM1136, ARM11MPCore, Cortex-A8 or Cortex-A9 MPCore CPU + +- Arm AMBA Generic/Distributed Interrupt Controller + +- Four PL011 UARTs + +- SMC 91c111 or SMSC LAN9118 Ethernet adapter + +- PL110 LCD controller + +- PL050 KMI with PS/2 keyboard and mouse + +- PCI host bridge + +- PCI OHCI USB controller + +- LSI53C895A PCI SCSI Host Bus Adapter with hard disk and CD-ROM + devices + +- PL181 MultiMedia Card Interface with SD card. diff --git a/docs/system/arm/stellaris.rst b/docs/system/arm/stellaris.rst new file mode 100644 index 0000000000..8af4ad79c7 --- /dev/null +++ b/docs/system/arm/stellaris.rst @@ -0,0 +1,26 @@ +Stellaris boards (``lm3s6965evb``, ``lm3s811evb``) +================================================== + +The Luminary Micro Stellaris LM3S811EVB emulation includes the following +devices: + +- Cortex-M3 CPU core. + +- 64k Flash and 8k SRAM. + +- Timers, UARTs, ADC and |I2C| interface. + +- OSRAM Pictiva 96x16 OLED with SSD0303 controller on + |I2C| bus. + +The Luminary Micro Stellaris LM3S6965EVB emulation includes the +following devices: + +- Cortex-M3 CPU core. + +- 256k Flash and 64k SRAM. + +- Timers, UARTs, ADC, |I2C| and SSI interfaces. + +- OSRAM Pictiva 128x64 OLED with SSD0323 controller connected via + SSI. diff --git a/docs/system/arm/sx1.rst b/docs/system/arm/sx1.rst new file mode 100644 index 0000000000..8bce30d4b2 --- /dev/null +++ b/docs/system/arm/sx1.rst @@ -0,0 +1,18 @@ +Siemens SX1 (``sx1``, ``sx1-v1``) +================================= + +The Siemens SX1 models v1 and v2 (default) basic emulation. The +emulation includes the following elements: + +- Texas Instruments OMAP310 System-on-chip (ARM925T core) + +- ROM and RAM memories (ROM firmware image can be loaded with + -pflash) V1 1 Flash of 16MB and 1 Flash of 8MB V2 1 Flash of 32MB + +- On-chip LCD controller + +- On-chip Real Time Clock + +- Secure Digital card connected to OMAP MMC/SD host + +- Three on-chip UARTs diff --git a/docs/system/arm/versatile.rst b/docs/system/arm/versatile.rst new file mode 100644 index 0000000000..51221c30a4 --- /dev/null +++ b/docs/system/arm/versatile.rst @@ -0,0 +1,29 @@ +Arm Versatile boards (``versatileab``, ``versatilepb``) +======================================================= + +The Arm Versatile baseboard is emulated with the following devices: + +- ARM926E, ARM1136 or Cortex-A8 CPU + +- PL190 Vectored Interrupt Controller + +- Four PL011 UARTs + +- SMC 91c111 Ethernet adapter + +- PL110 LCD controller + +- PL050 KMI with PS/2 keyboard and mouse. + +- PCI host bridge. Note the emulated PCI bridge only provides access + to PCI memory space. It does not provide access to PCI IO space. This + means some devices (eg. ne2k_pci NIC) are not usable, and others (eg. + rtl8139 NIC) are only usable when the guest drivers use the memory + mapped control registers. + +- PCI OHCI USB controller. + +- LSI53C895A PCI SCSI Host Bus Adapter with hard disk and CD-ROM + devices. + +- PL181 MultiMedia Card Interface with SD card. diff --git a/docs/system/arm/xscale.rst b/docs/system/arm/xscale.rst new file mode 100644 index 0000000000..89ec93e904 --- /dev/null +++ b/docs/system/arm/xscale.rst @@ -0,0 +1,29 @@ +Sharp XScale-based PDA models (``akita``, ``borzoi``, ``spitz``, ``terrier``) +============================================================================= + +The XScale-based clamshell PDA models (\"Spitz\", \"Akita\", \"Borzoi\" +and \"Terrier\") emulation includes the following peripherals: + +- Intel PXA270 System-on-chip (ARMv5TE core) + +- NAND Flash memory + +- IBM/Hitachi DSCM microdrive in a PXA PCMCIA slot - not in \"Akita\" + +- On-chip OHCI USB controller + +- On-chip LCD controller + +- On-chip Real Time Clock + +- TI ADS7846 touchscreen controller on SSP bus + +- Maxim MAX1111 analog-digital converter on |I2C| bus + +- GPIO-connected keyboard controller and LEDs + +- Secure Digital card connected to PXA MMC/SD host + +- Three on-chip UARTs + +- WM8750 audio CODEC on |I2C| and |I2S| busses diff --git a/docs/system/build-platforms.rst b/docs/system/build-platforms.rst new file mode 100644 index 0000000000..c2b92a9698 --- /dev/null +++ b/docs/system/build-platforms.rst @@ -0,0 +1,79 @@ +.. _Supported-build-platforms: + +Supported build platforms +========================= + +QEMU aims to support building and executing on multiple host OS +platforms. This appendix outlines which platforms are the major build +targets. These platforms are used as the basis for deciding upon the +minimum required versions of 3rd party software QEMU depends on. The +supported platforms are the targets for automated testing performed by +the project when patches are submitted for review, and tested before and +after merge. + +If a platform is not listed here, it does not imply that QEMU won't +work. If an unlisted platform has comparable software versions to a +listed platform, there is every expectation that it will work. Bug +reports are welcome for problems encountered on unlisted platforms +unless they are clearly older vintage than what is described here. + +Note that when considering software versions shipped in distros as +support targets, QEMU considers only the version number, and assumes the +features in that distro match the upstream release with the same +version. In other words, if a distro backports extra features to the +software in their distro, QEMU upstream code will not add explicit +support for those backports, unless the feature is auto-detectable in a +manner that works for the upstream releases too. + +The Repology site https://repology.org is a useful resource to identify +currently shipped versions of software in various operating systems, +though it does not cover all distros listed below. + +Linux OS +-------- + +For distributions with frequent, short-lifetime releases, the project +will aim to support all versions that are not end of life by their +respective vendors. For the purposes of identifying supported software +versions, the project will look at Fedora, Ubuntu, and openSUSE distros. +Other short- lifetime distros will be assumed to ship similar software +versions. + +For distributions with long-lifetime releases, the project will aim to +support the most recent major version at all times. Support for the +previous major version will be dropped 2 years after the new major +version is released, or when it reaches "end of life". For the purposes +of identifying supported software versions, the project will look at +RHEL, Debian, Ubuntu LTS, and SLES distros. Other long-lifetime distros +will be assumed to ship similar software versions. + +Windows +------- + +The project supports building with current versions of the MinGW +toolchain, hosted on Linux. + +macOS +----- + +The project supports building with the two most recent versions of +macOS, with the current homebrew package set available. + +FreeBSD +------- + +The project aims to support the all the versions which are not end of +life. + +NetBSD +------ + +The project aims to support the most recent major version at all times. +Support for the previous major version will be dropped 2 years after the +new major version is released. + +OpenBSD +------- + +The project aims to support the all the versions which are not end of +life. diff --git a/docs/system/conf.py b/docs/system/conf.py new file mode 100644 index 0000000000..6251849fef --- /dev/null +++ b/docs/system/conf.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +# +# QEMU documentation build configuration file for the 'system' manual. +# +# This includes the top level conf file and then makes any necessary tweaks. +import sys +import os + +qemu_docdir = os.path.abspath("..") +parent_config = os.path.join(qemu_docdir, "conf.py") +exec(compile(open(parent_config, "rb").read(), parent_config, 'exec')) + +# This slightly misuses the 'description', but is the best way to get +# the manual title to appear in the sidebar. +html_theme_options['description'] = u'System Emulation User''s Guide' + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + ('qemu-manpage', 'qemu', u'QEMU User Documentation', + ['Fabrice Bellard'], 1), + ('qemu-block-drivers', 'qemu-block-drivers', + u'QEMU block drivers reference', + ['Fabrice Bellard and the QEMU Project developers'], 7), + ('qemu-cpu-models', 'qemu-cpu-models', + u'QEMU CPU Models', + ['The QEMU Project developers'], 7) +] diff --git a/docs/system/cpu-models-mips.rst.inc b/docs/system/cpu-models-mips.rst.inc new file mode 100644 index 0000000000..499b5b6fed --- /dev/null +++ b/docs/system/cpu-models-mips.rst.inc @@ -0,0 +1,105 @@ +Supported CPU model configurations on MIPS hosts +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +QEMU supports variety of MIPS CPU models: + +Supported CPU models for MIPS32 hosts +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The following CPU models are supported for use on MIPS32 hosts. +Administrators / applications are recommended to use the CPU model that +matches the generation of the host CPUs in use. In a deployment with a +mixture of host CPU models between machines, if live migration +compatibility is required, use the newest CPU model that is compatible +across all desired hosts. + +``mips32r6-generic`` + MIPS32 Processor (Release 6, 2015) + +``P5600`` + MIPS32 Processor (P5600, 2014) + +``M14K``, ``M14Kc`` + MIPS32 Processor (M14K, 2009) + +``74Kf`` + MIPS32 Processor (74K, 2007) + +``34Kf`` + MIPS32 Processor (34K, 2006) + +``24Kc``, ``24KEc``, ``24Kf`` + MIPS32 Processor (24K, 2003) + +``4Kc``, ``4Km``, ``4KEcR1``, ``4KEmR1``, ``4KEc``, ``4KEm`` + MIPS32 Processor (4K, 1999) + + +Supported CPU models for MIPS64 hosts +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The following CPU models are supported for use on MIPS64 hosts. +Administrators / applications are recommended to use the CPU model that +matches the generation of the host CPUs in use. In a deployment with a +mixture of host CPU models between machines, if live migration +compatibility is required, use the newest CPU model that is compatible +across all desired hosts. + +``I6400`` + MIPS64 Processor (Release 6, 2014) + +``Loongson-2F`` + MIPS64 Processor (Loongson 2, 2008) + +``Loongson-2E`` + MIPS64 Processor (Loongson 2, 2006) + +``mips64dspr2`` + MIPS64 Processor (Release 2, 2006) + +``MIPS64R2-generic``, ``5KEc``, ``5KEf`` + MIPS64 Processor (Release 2, 2002) + +``20Kc`` + MIPS64 Processor (20K, 2000 + +``5Kc``, ``5Kf`` + MIPS64 Processor (5K, 1999) + +``VR5432`` + MIPS64 Processor (VR, 1998) + +``R4000`` + MIPS64 Processor (MIPS III, 1991) + + +Supported CPU models for nanoMIPS hosts +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The following CPU models are supported for use on nanoMIPS hosts. +Administrators / applications are recommended to use the CPU model that +matches the generation of the host CPUs in use. In a deployment with a +mixture of host CPU models between machines, if live migration +compatibility is required, use the newest CPU model that is compatible +across all desired hosts. + +``I7200`` + MIPS I7200 (nanoMIPS, 2018) + +Preferred CPU models for MIPS hosts +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The following CPU models are preferred for use on different MIPS hosts: + +``MIPS III`` + R4000 + +``MIPS32R2`` + 34Kf + +``MIPS64R6`` + I6400 + +``nanoMIPS`` + I7200 + diff --git a/docs/system/cpu-models-x86.rst.inc b/docs/system/cpu-models-x86.rst.inc new file mode 100644 index 0000000000..9a2327828e --- /dev/null +++ b/docs/system/cpu-models-x86.rst.inc @@ -0,0 +1,418 @@ +Recommendations for KVM CPU model configuration on x86 hosts +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The information that follows provides recommendations for configuring +CPU models on x86 hosts. The goals are to maximise performance, while +protecting guest OS against various CPU hardware flaws, and optionally +enabling live migration between hosts with heterogeneous CPU models. + + +Two ways to configure CPU models with QEMU / KVM +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +(1) **Host passthrough** + + This passes the host CPU model features, model, stepping, exactly to + the guest. Note that KVM may filter out some host CPU model features + if they cannot be supported with virtualization. Live migration is + unsafe when this mode is used as libvirt / QEMU cannot guarantee a + stable CPU is exposed to the guest across hosts. This is the + recommended CPU to use, provided live migration is not required. + +(2) **Named model** + + QEMU comes with a number of predefined named CPU models, that + typically refer to specific generations of hardware released by + Intel and AMD. These allow the guest VMs to have a degree of + isolation from the host CPU, allowing greater flexibility in live + migrating between hosts with differing hardware. @end table + +In both cases, it is possible to optionally add or remove individual CPU +features, to alter what is presented to the guest by default. + +Libvirt supports a third way to configure CPU models known as "Host +model". This uses the QEMU "Named model" feature, automatically picking +a CPU model that is similar the host CPU, and then adding extra features +to approximate the host model as closely as possible. This does not +guarantee the CPU family, stepping, etc will precisely match the host +CPU, as they would with "Host passthrough", but gives much of the +benefit of passthrough, while making live migration safe. + + +Preferred CPU models for Intel x86 hosts +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The following CPU models are preferred for use on Intel hosts. +Administrators / applications are recommended to use the CPU model that +matches the generation of the host CPUs in use. In a deployment with a +mixture of host CPU models between machines, if live migration +compatibility is required, use the newest CPU model that is compatible +across all desired hosts. + +``Cascadelake-Server``, ``Cascadelake-Server-noTSX`` + Intel Xeon Processor (Cascade Lake, 2019), with "stepping" levels 6 + or 7 only. (The Cascade Lake Xeon processor with *stepping 5 is + vulnerable to MDS variants*.) + +``Skylake-Server``, ``Skylake-Server-IBRS``, ``Skylake-Server-IBRS-noTSX`` + Intel Xeon Processor (Skylake, 2016) + +``Skylake-Client``, ``Skylake-Client-IBRS``, ``Skylake-Client-noTSX-IBRS}`` + Intel Core Processor (Skylake, 2015) + +``Broadwell``, ``Broadwell-IBRS``, ``Broadwell-noTSX``, ``Broadwell-noTSX-IBRS`` + Intel Core Processor (Broadwell, 2014) + +``Haswell``, ``Haswell-IBRS``, ``Haswell-noTSX``, ``Haswell-noTSX-IBRS`` + Intel Core Processor (Haswell, 2013) + +``IvyBridge``, ``IvyBridge-IBR`` + Intel Xeon E3-12xx v2 (Ivy Bridge, 2012) + +``SandyBridge``, ``SandyBridge-IBRS`` + Intel Xeon E312xx (Sandy Bridge, 2011) + +``Westmere``, ``Westmere-IBRS`` + Westmere E56xx/L56xx/X56xx (Nehalem-C, 2010) + +``Nehalem``, ``Nehalem-IBRS`` + Intel Core i7 9xx (Nehalem Class Core i7, 2008) + +``Penryn`` + Intel Core 2 Duo P9xxx (Penryn Class Core 2, 2007) + +``Conroe`` + Intel Celeron_4x0 (Conroe/Merom Class Core 2, 2006) + + +Important CPU features for Intel x86 hosts +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The following are important CPU features that should be used on Intel +x86 hosts, when available in the host CPU. Some of them require explicit +configuration to enable, as they are not included by default in some, or +all, of the named CPU models listed above. In general all of these +features are included if using "Host passthrough" or "Host model". + +``pcid`` + Recommended to mitigate the cost of the Meltdown (CVE-2017-5754) fix. + + Included by default in Haswell, Broadwell & Skylake Intel CPU models. + + Should be explicitly turned on for Westmere, SandyBridge, and + IvyBridge Intel CPU models. Note that some desktop/mobile Westmere + CPUs cannot support this feature. + +``spec-ctrl`` + Required to enable the Spectre v2 (CVE-2017-5715) fix. + + Included by default in Intel CPU models with -IBRS suffix. + + Must be explicitly turned on for Intel CPU models without -IBRS + suffix. + + Requires the host CPU microcode to support this feature before it + can be used for guest CPUs. + +``stibp`` + Required to enable stronger Spectre v2 (CVE-2017-5715) fixes in some + operating systems. + + Must be explicitly turned on for all Intel CPU models. + + Requires the host CPU microcode to support this feature before it can + be used for guest CPUs. + +``ssbd`` + Required to enable the CVE-2018-3639 fix. + + Not included by default in any Intel CPU model. + + Must be explicitly turned on for all Intel CPU models. + + Requires the host CPU microcode to support this feature before it + can be used for guest CPUs. + +``pdpe1gb`` + Recommended to allow guest OS to use 1GB size pages. + + Not included by default in any Intel CPU model. + + Should be explicitly turned on for all Intel CPU models. + + Note that not all CPU hardware will support this feature. + +``md-clear`` + Required to confirm the MDS (CVE-2018-12126, CVE-2018-12127, + CVE-2018-12130, CVE-2019-11091) fixes. + + Not included by default in any Intel CPU model. + + Must be explicitly turned on for all Intel CPU models. + + Requires the host CPU microcode to support this feature before it + can be used for guest CPUs. + +``mds-no`` + Recommended to inform the guest OS that the host is *not* vulnerable + to any of the MDS variants ([MFBDS] CVE-2018-12130, [MLPDS] + CVE-2018-12127, [MSBDS] CVE-2018-12126). + + This is an MSR (Model-Specific Register) feature rather than a CPUID feature, + so it will not appear in the Linux ``/proc/cpuinfo`` in the host or + guest. Instead, the host kernel uses it to populate the MDS + vulnerability file in ``sysfs``. + + So it should only be enabled for VMs if the host reports @code{Not + affected} in the ``/sys/devices/system/cpu/vulnerabilities/mds`` file. + +``taa-no`` + Recommended to inform that the guest that the host is ``not`` + vulnerable to CVE-2019-11135, TSX Asynchronous Abort (TAA). + + This too is an MSR feature, so it does not show up in the Linux + ``/proc/cpuinfo`` in the host or guest. + + It should only be enabled for VMs if the host reports ``Not affected`` + in the ``/sys/devices/system/cpu/vulnerabilities/tsx_async_abort`` + file. + +``tsx-ctrl`` + Recommended to inform the guest that it can disable the Intel TSX + (Transactional Synchronization Extensions) feature; or, if the + processor is vulnerable, use the Intel VERW instruction (a + processor-level instruction that performs checks on memory access) as + a mitigation for the TAA vulnerability. (For details, refer to + Intel's `deep dive into MDS + `_.) + + Expose this to the guest OS if and only if: (a) the host has TSX + enabled; *and* (b) the guest has ``rtm`` CPU flag enabled. + + By disabling TSX, KVM-based guests can avoid paying the price of + mitigating TSX-based attacks. + + Note that ``tsx-ctrl`` too is an MSR feature, so it does not show + up in the Linux ``/proc/cpuinfo`` in the host or guest. + + To validate that Intel TSX is indeed disabled for the guest, there are + two ways: (a) check for the *absence* of ``rtm`` in the guest's + ``/proc/cpuinfo``; or (b) the + ``/sys/devices/system/cpu/vulnerabilities/tsx_async_abort`` file in + the guest should report ``Mitigation: TSX disabled``. + + +Preferred CPU models for AMD x86 hosts +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The following CPU models are preferred for use on Intel hosts. +Administrators / applications are recommended to use the CPU model that +matches the generation of the host CPUs in use. In a deployment with a +mixture of host CPU models between machines, if live migration +compatibility is required, use the newest CPU model that is compatible +across all desired hosts. + +``EPYC``, ``EPYC-IBPB`` + AMD EPYC Processor (2017) + +``Opteron_G5`` + AMD Opteron 63xx class CPU (2012) + +``Opteron_G4`` + AMD Opteron 62xx class CPU (2011) + +``Opteron_G3`` + AMD Opteron 23xx (Gen 3 Class Opteron, 2009) + +``Opteron_G2`` + AMD Opteron 22xx (Gen 2 Class Opteron, 2006) + +``Opteron_G1`` + AMD Opteron 240 (Gen 1 Class Opteron, 2004) + + +Important CPU features for AMD x86 hosts +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The following are important CPU features that should be used on AMD x86 +hosts, when available in the host CPU. Some of them require explicit +configuration to enable, as they are not included by default in some, or +all, of the named CPU models listed above. In general all of these +features are included if using "Host passthrough" or "Host model". + +``ibpb`` + Required to enable the Spectre v2 (CVE-2017-5715) fix. + + Included by default in AMD CPU models with -IBPB suffix. + + Must be explicitly turned on for AMD CPU models without -IBPB suffix. + + Requires the host CPU microcode to support this feature before it + can be used for guest CPUs. + +``stibp`` + Required to enable stronger Spectre v2 (CVE-2017-5715) fixes in some + operating systems. + + Must be explicitly turned on for all AMD CPU models. + + Requires the host CPU microcode to support this feature before it + can be used for guest CPUs. + +``virt-ssbd`` + Required to enable the CVE-2018-3639 fix + + Not included by default in any AMD CPU model. + + Must be explicitly turned on for all AMD CPU models. + + This should be provided to guests, even if amd-ssbd is also provided, + for maximum guest compatibility. + + Note for some QEMU / libvirt versions, this must be force enabled when + when using "Host model", because this is a virtual feature that + doesn't exist in the physical host CPUs. + +``amd-ssbd`` + Required to enable the CVE-2018-3639 fix + + Not included by default in any AMD CPU model. + + Must be explicitly turned on for all AMD CPU models. + + This provides higher performance than ``virt-ssbd`` so should be + exposed to guests whenever available in the host. ``virt-ssbd`` should + none the less also be exposed for maximum guest compatibility as some + kernels only know about ``virt-ssbd``. + +``amd-no-ssb`` + Recommended to indicate the host is not vulnerable CVE-2018-3639 + + Not included by default in any AMD CPU model. + + Future hardware generations of CPU will not be vulnerable to + CVE-2018-3639, and thus the guest should be told not to enable + its mitigations, by exposing amd-no-ssb. This is mutually + exclusive with virt-ssbd and amd-ssbd. + +``pdpe1gb`` + Recommended to allow guest OS to use 1GB size pages + + Not included by default in any AMD CPU model. + + Should be explicitly turned on for all AMD CPU models. + + Note that not all CPU hardware will support this feature. + + +Default x86 CPU models +^^^^^^^^^^^^^^^^^^^^^^ + +The default QEMU CPU models are designed such that they can run on all +hosts. If an application does not wish to do perform any host +compatibility checks before launching guests, the default is guaranteed +to work. + +The default CPU models will, however, leave the guest OS vulnerable to +various CPU hardware flaws, so their use is strongly discouraged. +Applications should follow the earlier guidance to setup a better CPU +configuration, with host passthrough recommended if live migration is +not needed. + +``qemu32``, ``qemu64`` + QEMU Virtual CPU version 2.5+ (32 & 64 bit variants) + +``qemu64`` is used for x86_64 guests and ``qemu32`` is used for i686 +guests, when no ``-cpu`` argument is given to QEMU, or no ```` is +provided in libvirt XML. + +Other non-recommended x86 CPUs +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The following CPUs models are compatible with most AMD and Intel x86 +hosts, but their usage is discouraged, as they expose a very limited +featureset, which prevents guests having optimal performance. + +``kvm32``, ``kvm64`` + Common KVM processor (32 & 64 bit variants). + + Legacy models just for historical compatibility with ancient QEMU + versions. + +``486``, ``athlon``, ``phenom``, ``coreduo``, ``core2duo``, ``n270``, ``pentium``, ``pentium2``, ``pentium3`` + Various very old x86 CPU models, mostly predating the introduction + of hardware assisted virtualization, that should thus not be + required for running virtual machines. + + +Syntax for configuring CPU models +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The examples below illustrate the approach to configuring the various +CPU models / features in QEMU and libvirt. + +QEMU command line +^^^^^^^^^^^^^^^^^ + +Host passthrough: + +.. parsed-literal:: + + |qemu_system| -cpu host + +Host passthrough with feature customization: + +.. parsed-literal:: + + |qemu_system| -cpu host,-vmx,... + +Named CPU models: + +.. parsed-literal:: + + |qemu_system| -cpu Westmere + +Named CPU models with feature customization: + +.. parsed-literal:: + + |qemu_system| -cpu Westmere,+pcid,... + +Libvirt guest XML +^^^^^^^^^^^^^^^^^ + +Host passthrough:: + + + +Host passthrough with feature customization:: + + + + ... + + +Host model:: + + + +Host model with feature customization:: + + + + ... + + +Named model:: + + + + + +Named model with feature customization:: + + + + + ... + diff --git a/docs/system/deprecated.rst b/docs/system/deprecated.rst new file mode 100644 index 0000000000..3142fac386 --- /dev/null +++ b/docs/system/deprecated.rst @@ -0,0 +1,509 @@ +Deprecated features +=================== + +In general features are intended to be supported indefinitely once +introduced into QEMU. In the event that a feature needs to be removed, +it will be listed in this section. The feature will remain functional +for 2 releases prior to actual removal. Deprecated features may also +generate warnings on the console when QEMU starts up, or if activated +via a monitor command, however, this is not a mandatory requirement. + +Prior to the 2.10.0 release there was no official policy on how +long features would be deprecated prior to their removal, nor +any documented list of which features were deprecated. Thus +any features deprecated prior to 2.10.0 will be treated as if +they were first deprecated in the 2.10.0 release. + +What follows is a list of all features currently marked as +deprecated. + +System emulator command line arguments +-------------------------------------- + +``-machine enforce-config-section=on|off`` (since 3.1) +'''''''''''''''''''''''''''''''''''''''''''''''''''''' + +The ``enforce-config-section`` parameter is replaced by the +``-global migration.send-configuration={on|off}`` option. + +``-no-kvm`` (since 1.3.0) +''''''''''''''''''''''''' + +The ``-no-kvm`` argument is now a synonym for setting ``-accel tcg``. + +``-usbdevice`` (since 2.10.0) +''''''''''''''''''''''''''''' + +The ``-usbdevice DEV`` argument is now a synonym for setting +the ``-device usb-DEV`` argument instead. The deprecated syntax +would automatically enable USB support on the machine type. +If using the new syntax, USB support must be explicitly +enabled via the ``-machine usb=on`` argument. + +``-drive file=json:{...{'driver':'file'}}`` (since 3.0) +''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +The 'file' driver for drives is no longer appropriate for character or host +devices and will only accept regular files (S_IFREG). The correct driver +for these file types is 'host_cdrom' or 'host_device' as appropriate. + +``-net ...,name=``\ *name* (since 3.1) +'''''''''''''''''''''''''''''''''''''' + +The ``name`` parameter of the ``-net`` option is a synonym +for the ``id`` parameter, which should now be used instead. + +``-smp`` (invalid topologies) (since 3.1) +''''''''''''''''''''''''''''''''''''''''' + +CPU topology properties should describe whole machine topology including +possible CPUs. + +However, historically it was possible to start QEMU with an incorrect topology +where *n* <= *sockets* * *cores* * *threads* < *maxcpus*, +which could lead to an incorrect topology enumeration by the guest. +Support for invalid topologies will be removed, the user must ensure +topologies described with -smp include all possible cpus, i.e. +*sockets* * *cores* * *threads* = *maxcpus*. + +``-vnc acl`` (since 4.0.0) +'''''''''''''''''''''''''' + +The ``acl`` option to the ``-vnc`` argument has been replaced +by the ``tls-authz`` and ``sasl-authz`` options. + +``QEMU_AUDIO_`` environment variables and ``-audio-help`` (since 4.0) +''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +The ``-audiodev`` argument is now the preferred way to specify audio +backend settings instead of environment variables. To ease migration to +the new format, the ``-audiodev-help`` option can be used to convert +the current values of the environment variables to ``-audiodev`` options. + +Creating sound card devices and vnc without ``audiodev=`` property (since 4.2) +'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +When not using the deprecated legacy audio config, each sound card +should specify an ``audiodev=`` property. Additionally, when using +vnc, you should specify an ``audiodev=`` propery if you plan to +transmit audio through the VNC protocol. + +``-mon ...,control=readline,pretty=on|off`` (since 4.1) +''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +The ``pretty=on|off`` switch has no effect for HMP monitors, but is +silently ignored. Using the switch with HMP monitors will become an +error in the future. + +``-realtime`` (since 4.1) +''''''''''''''''''''''''' + +The ``-realtime mlock=on|off`` argument has been replaced by the +``-overcommit mem-lock=on|off`` argument. + +``-numa node,mem=``\ *size* (since 4.1) +''''''''''''''''''''''''''''''''''''''' + +The parameter ``mem`` of ``-numa node`` is used to assign a part of +guest RAM to a NUMA node. But when using it, it's impossible to manage specified +RAM chunk on the host side (like bind it to a host node, setting bind policy, ...), +so guest end-ups with the fake NUMA configuration with suboptiomal performance. +However since 2014 there is an alternative way to assign RAM to a NUMA node +using parameter ``memdev``, which does the same as ``mem`` and adds +means to actualy manage node RAM on the host side. Use parameter ``memdev`` +with *memory-backend-ram* backend as an replacement for parameter ``mem`` +to achieve the same fake NUMA effect or a properly configured +*memory-backend-file* backend to actually benefit from NUMA configuration. +In future new machine versions will not accept the option but it will still +work with old machine types. User can check QAPI schema to see if the legacy +option is supported by looking at MachineInfo::numa-mem-supported property. + +``-numa`` node (without memory specified) (since 4.1) +''''''''''''''''''''''''''''''''''''''''''''''''''''' + +Splitting RAM by default between NUMA nodes has the same issues as ``mem`` +parameter described above with the difference that the role of the user plays +QEMU using implicit generic or board specific splitting rule. +Use ``memdev`` with *memory-backend-ram* backend or ``mem`` (if +it's supported by used machine type) to define mapping explictly instead. + +``-mem-path`` fallback to RAM (since 4.1) +''''''''''''''''''''''''''''''''''''''''' + +Currently if guest RAM allocation from file pointed by ``mem-path`` +fails, QEMU falls back to allocating from RAM, which might result +in unpredictable behavior since the backing file specified by the user +is ignored. In the future, users will be responsible for making sure +the backing storage specified with ``-mem-path`` can actually provide +the guest RAM configured with ``-m`` and QEMU will fail to start up if +RAM allocation is unsuccessful. + +RISC-V ``-bios`` (since 4.1) +'''''''''''''''''''''''''''' + +QEMU 4.1 introduced support for the -bios option in QEMU for RISC-V for the +RISC-V virt machine and sifive_u machine. + +QEMU 4.1 has no changes to the default behaviour to avoid breakages. This +default will change in a future QEMU release, so please prepare now. All users +of the virt or sifive_u machine must change their command line usage. + +QEMU 4.1 has three options, please migrate to one of these three: + 1. ``-bios none`` - This is the current default behavior if no -bios option + is included. QEMU will not automatically load any firmware. It is up + to the user to load all the images they need. + 2. ``-bios default`` - In a future QEMU release this will become the default + behaviour if no -bios option is specified. This option will load the + default OpenSBI firmware automatically. The firmware is included with + the QEMU release and no user interaction is required. All a user needs + to do is specify the kernel they want to boot with the -kernel option + 3. ``-bios `` - Tells QEMU to load the specified file as the firmwrae. + +``-tb-size`` option (since 5.0) +''''''''''''''''''''''''''''''' + +QEMU 5.0 introduced an alternative syntax to specify the size of the translation +block cache, ``-accel tcg,tb-size=``. The new syntax deprecates the +previously available ``-tb-size`` option. + +``-show-cursor`` option (since 5.0) +''''''''''''''''''''''''''''''''''' + +Use ``-display sdl,show-cursor=on`` or + ``-display gtk,show-cursor=on`` instead. + +QEMU Machine Protocol (QMP) commands +------------------------------------ + +``change`` (since 2.5.0) +'''''''''''''''''''''''' + +Use ``blockdev-change-medium`` or ``change-vnc-password`` instead. + +``blockdev-open-tray``, ``blockdev-close-tray`` argument ``device`` (since 2.8.0) +''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +Use argument ``id`` instead. + +``eject`` argument ``device`` (since 2.8.0) +''''''''''''''''''''''''''''''''''''''''''' + +Use argument ``id`` instead. + +``blockdev-change-medium`` argument ``device`` (since 2.8.0) +'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +Use argument ``id`` instead. + +``block_set_io_throttle`` argument ``device`` (since 2.8.0) +''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +Use argument ``id`` instead. + +``migrate_set_downtime`` and ``migrate_set_speed`` (since 2.8.0) +'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +Use ``migrate-set-parameters`` instead. + +``query-named-block-nodes`` result ``encryption_key_missing`` (since 2.10.0) +'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +Always false. + +``query-block`` result ``inserted.encryption_key_missing`` (since 2.10.0) +''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +Always false. + +``blockdev-add`` empty string argument ``backing`` (since 2.10.0) +''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +Use argument value ``null`` instead. + +``migrate-set-cache-size`` and ``query-migrate-cache-size`` (since 2.11.0) +'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +Use ``migrate-set-parameters`` and ``query-migrate-parameters`` instead. + +``block-commit`` arguments ``base`` and ``top`` (since 3.1.0) +''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +Use arguments ``base-node`` and ``top-node`` instead. + +``object-add`` option ``props`` (since 5.0) +''''''''''''''''''''''''''''''''''''''''''' + +Specify the properties for the object as top-level arguments instead. + +``query-named-block-nodes`` and ``query-block`` result dirty-bitmaps[i].status (since 4.0) +'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +The ``status`` field of the ``BlockDirtyInfo`` structure, returned by +these commands is deprecated. Two new boolean fields, ``recording`` and +``busy`` effectively replace it. + +``query-block`` result field ``dirty-bitmaps`` (Since 4.2) +'''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +The ``dirty-bitmaps`` field of the ``BlockInfo`` structure, returned by +the query-block command is itself now deprecated. The ``dirty-bitmaps`` +field of the ``BlockDeviceInfo`` struct should be used instead, which is the +type of the ``inserted`` field in query-block replies, as well as the +type of array items in query-named-block-nodes. + +Since the ``dirty-bitmaps`` field is optionally present in both the old and +new locations, clients must use introspection to learn where to anticipate +the field if/when it does appear in command output. + +``query-cpus`` (since 2.12.0) +''''''''''''''''''''''''''''' + +The ``query-cpus`` command is replaced by the ``query-cpus-fast`` command. + +``query-cpus-fast`` ``arch`` output member (since 3.0.0) +'''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +The ``arch`` output member of the ``query-cpus-fast`` command is +replaced by the ``target`` output member. + +``cpu-add`` (since 4.0) +''''''''''''''''''''''' + +Use ``device_add`` for hotplugging vCPUs instead of ``cpu-add``. See +documentation of ``query-hotpluggable-cpus`` for additional +details. + +``query-events`` (since 4.0) +'''''''''''''''''''''''''''' + +The ``query-events`` command has been superseded by the more powerful +and accurate ``query-qmp-schema`` command. + +chardev client socket with ``wait`` option (since 4.0) +'''''''''''''''''''''''''''''''''''''''''''''''''''''' + +Character devices creating sockets in client mode should not specify +the 'wait' field, which is only applicable to sockets in server mode + +Human Monitor Protocol (HMP) commands +------------------------------------- + +``cpu-add`` (since 4.0) +''''''''''''''''''''''' + +Use ``device_add`` for hotplugging vCPUs instead of ``cpu-add``. See +documentation of ``query-hotpluggable-cpus`` for additional details. + +``acl_show``, ``acl_reset``, ``acl_policy``, ``acl_add``, ``acl_remove`` (since 4.0.0) +'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +The ``acl_show``, ``acl_reset``, ``acl_policy``, ``acl_add``, and +``acl_remove`` commands are deprecated with no replacement. Authorization +for VNC should be performed using the pluggable QAuthZ objects. + +Guest Emulator ISAs +------------------- + +RISC-V ISA privledge specification version 1.09.1 (since 4.1) +''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +The RISC-V ISA privledge specification version 1.09.1 has been deprecated. +QEMU supports both the newer version 1.10.0 and the ratified version 1.11.0, these +should be used instead of the 1.09.1 version. + +System emulator CPUS +-------------------- + +RISC-V ISA CPUs (since 4.1) +''''''''''''''''''''''''''' + +The RISC-V cpus with the ISA version in the CPU name have been depcreated. The +four CPUs are: ``rv32gcsu-v1.9.1``, ``rv32gcsu-v1.10.0``, ``rv64gcsu-v1.9.1`` and +``rv64gcsu-v1.10.0``. Instead the version can be specified via the CPU ``priv_spec`` +option when using the ``rv32`` or ``rv64`` CPUs. + +RISC-V ISA CPUs (since 4.1) +''''''''''''''''''''''''''' + +The RISC-V no MMU cpus have been depcreated. The two CPUs: ``rv32imacu-nommu`` and +``rv64imacu-nommu`` should no longer be used. Instead the MMU status can be specified +via the CPU ``mmu`` option when using the ``rv32`` or ``rv64`` CPUs. + +``compat`` property of server class POWER CPUs (since 5.0) +'''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +The ``compat`` property used to set backwards compatibility modes for +the processor has been deprecated. The ``max-cpu-compat`` property of +the ``pseries`` machine type should be used instead. + +KVM guest support on 32-bit Arm hosts (since 5.0) +''''''''''''''''''''''''''''''''''''''''''''''''' + +The Linux kernel has dropped support for allowing 32-bit Arm systems +to host KVM guests as of the 5.7 kernel. Accordingly, QEMU is deprecating +its support for this configuration and will remove it in a future version. +Running 32-bit guests on a 64-bit Arm host remains supported. + +System emulator devices +----------------------- + +``ide-drive`` (since 4.2) +''''''''''''''''''''''''' + +The 'ide-drive' device is deprecated. Users should use 'ide-hd' or +'ide-cd' as appropriate to get an IDE hard disk or CD-ROM as needed. + +``scsi-disk`` (since 4.2) +''''''''''''''''''''''''' + +The 'scsi-disk' device is deprecated. Users should use 'scsi-hd' or +'scsi-cd' as appropriate to get a SCSI hard disk or CD-ROM as needed. + +System emulator machines +------------------------ + +mips ``r4k`` platform (since 5.0) +''''''''''''''''''''''''''''''''' + +This machine type is very old and unmaintained. Users should use the ``malta`` +machine type instead. + +``pc-1.0``, ``pc-1.1``, ``pc-1.2`` and ``pc-1.3`` (since 5.0) +''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +These machine types are very old and likely can not be used for live migration +from old QEMU versions anymore. A newer machine type should be used instead. + +``spike_v1.9.1`` and ``spike_v1.10`` (since 4.1) +'''''''''''''''''''''''''''''''''''''''''''''''' + +The version specific Spike machines have been deprecated in favour of the +generic ``spike`` machine. If you need to specify an older version of the RISC-V +spec you can use the ``-cpu rv64gcsu,priv_spec=v1.9.1`` command line argument. + +Device options +-------------- + +Emulated device options +''''''''''''''''''''''' + +``-device virtio-blk,scsi=on|off`` (since 5.0.0) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The virtio-blk SCSI passthrough feature is a legacy VIRTIO feature. VIRTIO 1.0 +and later do not support it because the virtio-scsi device was introduced for +full SCSI support. Use virtio-scsi instead when SCSI passthrough is required. + +Note this also applies to ``-device virtio-blk-pci,scsi=on|off``, which is an +alias. + +Block device options +'''''''''''''''''''' + +``"backing": ""`` (since 2.12.0) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +In order to prevent QEMU from automatically opening an image's backing +chain, use ``"backing": null`` instead. + +``rbd`` keyvalue pair encoded filenames: ``""`` (since 3.1.0) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Options for ``rbd`` should be specified according to its runtime options, +like other block drivers. Legacy parsing of keyvalue pair encoded +filenames is useful to open images with the old format for backing files; +These image files should be updated to use the current format. + +Example of legacy encoding:: + + json:{"file.driver":"rbd", "file.filename":"rbd:rbd/name"} + +The above, converted to the current supported format:: + + json:{"file.driver":"rbd", "file.pool":"rbd", "file.image":"name"} + +Related binaries +---------------- + +``qemu-img convert -n -o`` (since 4.2.0) +'''''''''''''''''''''''''''''''''''''''' + +All options specified in ``-o`` are image creation options, so +they have no effect when used with ``-n`` to skip image creation. +Silently ignored options can be confusing, so this combination of +options will be made an error in future versions. + +Backwards compatibility +----------------------- + +Runnability guarantee of CPU models (since 4.1.0) +''''''''''''''''''''''''''''''''''''''''''''''''' + +Previous versions of QEMU never changed existing CPU models in +ways that introduced additional host software or hardware +requirements to the VM. This allowed management software to +safely change the machine type of an existing VM without +introducing new requirements ("runnability guarantee"). This +prevented CPU models from being updated to include CPU +vulnerability mitigations, leaving guests vulnerable in the +default configuration. + +The CPU model runnability guarantee won't apply anymore to +existing CPU models. Management software that needs runnability +guarantees must resolve the CPU model aliases using te +``alias-of`` field returned by the ``query-cpu-definitions`` QMP +command. + +While those guarantees are kept, the return value of +``query-cpu-definitions`` will have existing CPU model aliases +point to a version that doesn't break runnability guarantees +(specifically, version 1 of those CPU models). In future QEMU +versions, aliases will point to newer CPU model versions +depending on the machine type, so management software must +resolve CPU model aliases before starting a virtual machine. + + +Recently removed features +========================= + +What follows is a record of recently removed, formerly deprecated +features that serves as a record for users who have encountered +trouble after a recent upgrade. + +QEMU Machine Protocol (QMP) commands +------------------------------------ + +``block-dirty-bitmap-add`` "autoload" parameter (since 4.2.0) +''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +The "autoload" parameter has been ignored since 2.12.0. All bitmaps +are automatically loaded from qcow2 images. + +Human Monitor Protocol (HMP) commands +------------------------------------- + +The ``hub_id`` parameter of ``hostfwd_add`` / ``hostfwd_remove`` (removed in 5.0) +''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +The ``[hub_id name]`` parameter tuple of the 'hostfwd_add' and +'hostfwd_remove' HMP commands has been replaced by ``netdev_id``. + +Related binaries +---------------- + +``qemu-nbd --partition`` (removed in 5.0.0) +''''''''''''''''''''''''''''''''''''''''''' + +The ``qemu-nbd --partition $digit`` code (also spelled ``-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 ``--image-opts`` option with a raw blockdev +using the ``offset`` and ``size`` parameters layered on top of +any other existing blockdev. For example, if partition 1 is 100MiB +long starting at 1MiB, the old command:: + + qemu-nbd -t -P 1 -f qcow2 file.qcow2 + +can be rewritten as:: + + 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/docs/system/device-url-syntax.rst.inc b/docs/system/device-url-syntax.rst.inc new file mode 100644 index 0000000000..88d7a372a7 --- /dev/null +++ b/docs/system/device-url-syntax.rst.inc @@ -0,0 +1,228 @@ + +In addition to using normal file images for the emulated storage +devices, QEMU can also use networked resources such as iSCSI devices. +These are specified using a special URL syntax. + +``iSCSI`` + iSCSI support allows QEMU to access iSCSI resources directly and use + as images for the guest storage. Both disk and cdrom images are + supported. + + Syntax for specifying iSCSI LUNs is + "iscsi://[:]//" + + By default qemu will use the iSCSI initiator-name + 'iqn.2008-11.org.linux-kvm[:]' but this can also be set from + the command line or a configuration file. + + Since version Qemu 2.4 it is possible to specify a iSCSI request + timeout to detect stalled requests and force a reestablishment of the + session. The timeout is specified in seconds. The default is 0 which + means no timeout. Libiscsi 1.15.0 or greater is required for this + feature. + + Example (without authentication): + + .. parsed-literal:: + + |qemu_system| -iscsi initiator-name=iqn.2001-04.com.example:my-initiator \ + -cdrom iscsi://192.0.2.1/iqn.2001-04.com.example/2 \ + -drive file=iscsi://192.0.2.1/iqn.2001-04.com.example/1 + + Example (CHAP username/password via URL): + + .. parsed-literal:: + + |qemu_system| -drive file=iscsi://user%password@192.0.2.1/iqn.2001-04.com.example/1 + + Example (CHAP username/password via environment variables): + + .. parsed-literal:: + + LIBISCSI_CHAP_USERNAME="user" \ + LIBISCSI_CHAP_PASSWORD="password" \ + |qemu_system| -drive file=iscsi://192.0.2.1/iqn.2001-04.com.example/1 + +``NBD`` + QEMU supports NBD (Network Block Devices) both using TCP protocol as + well as Unix Domain Sockets. With TCP, the default port is 10809. + + Syntax for specifying a NBD device using TCP, in preferred URI form: + "nbd://[:]/[]" + + Syntax for specifying a NBD device using Unix Domain Sockets; + remember that '?' is a shell glob character and may need quoting: + "nbd+unix:///[]?socket=" + + Older syntax that is also recognized: + "nbd::[:exportname=]" + + Syntax for specifying a NBD device using Unix Domain Sockets + "nbd:unix:[:exportname=]" + + Example for TCP + + .. parsed-literal:: + + |qemu_system| --drive file=nbd:192.0.2.1:30000 + + Example for Unix Domain Sockets + + .. parsed-literal:: + + |qemu_system| --drive file=nbd:unix:/tmp/nbd-socket + +``SSH`` + QEMU supports SSH (Secure Shell) access to remote disks. + + Examples: + + .. parsed-literal:: + + |qemu_system| -drive file=ssh://user@host/path/to/disk.img + |qemu_system| -drive file.driver=ssh,file.user=user,file.host=host,file.port=22,file.path=/path/to/disk.img + + Currently authentication must be done using ssh-agent. Other + authentication methods may be supported in future. + +``Sheepdog`` + Sheepdog is a distributed storage system for QEMU. QEMU supports + using either local sheepdog devices or remote networked devices. + + Syntax for specifying a sheepdog device + + :: + + sheepdog[+tcp|+unix]://[host:port]/vdiname[?socket=path][#snapid|#tag] + + Example + + .. parsed-literal:: + + |qemu_system| --drive file=sheepdog://192.0.2.1:30000/MyVirtualMachine + + See also https://sheepdog.github.io/sheepdog/. + +``GlusterFS`` + GlusterFS is a user space distributed file system. QEMU supports the + use of GlusterFS volumes for hosting VM disk images using TCP, Unix + Domain Sockets and RDMA transport protocols. + + Syntax for specifying a VM disk image on GlusterFS volume is + + .. parsed-literal:: + + URI: + gluster[+type]://[host[:port]]/volume/path[?socket=...][,debug=N][,logfile=...] + + JSON: + 'json:{"driver":"qcow2","file":{"driver":"gluster","volume":"testvol","path":"a.img","debug":N,"logfile":"...", +   "server":[{"type":"tcp","host":"...","port":"..."}, +   {"type":"unix","socket":"..."}]}}' + + Example + + .. parsed-literal:: + + URI: + |qemu_system| --drive file=gluster://192.0.2.1/testvol/a.img, +   file.debug=9,file.logfile=/var/log/qemu-gluster.log + + JSON: + |qemu_system| 'json:{"driver":"qcow2", +   "file":{"driver":"gluster", +   "volume":"testvol","path":"a.img", +   "debug":9,"logfile":"/var/log/qemu-gluster.log", +   "server":[{"type":"tcp","host":"1.2.3.4","port":24007}, +   {"type":"unix","socket":"/var/run/glusterd.socket"}]}}' + |qemu_system| -drive driver=qcow2,file.driver=gluster,file.volume=testvol,file.path=/path/a.img, +   file.debug=9,file.logfile=/var/log/qemu-gluster.log, +   file.server.0.type=tcp,file.server.0.host=1.2.3.4,file.server.0.port=24007, +   file.server.1.type=unix,file.server.1.socket=/var/run/glusterd.socket + + See also http://www.gluster.org. + +``HTTP/HTTPS/FTP/FTPS`` + QEMU supports read-only access to files accessed over http(s) and + ftp(s). + + Syntax using a single filename: + + :: + + ://[[:]@]/ + + where: + + ``protocol`` + 'http', 'https', 'ftp', or 'ftps'. + + ``username`` + Optional username for authentication to the remote server. + + ``password`` + Optional password for authentication to the remote server. + + ``host`` + Address of the remote server. + + ``path`` + Path on the remote server, including any query string. + + The following options are also supported: + + ``url`` + The full URL when passing options to the driver explicitly. + + ``readahead`` + The amount of data to read ahead with each range request to the + remote server. This value may optionally have the suffix 'T', 'G', + 'M', 'K', 'k' or 'b'. If it does not have a suffix, it will be + assumed to be in bytes. The value must be a multiple of 512 bytes. + It defaults to 256k. + + ``sslverify`` + Whether to verify the remote server's certificate when connecting + over SSL. It can have the value 'on' or 'off'. It defaults to + 'on'. + + ``cookie`` + Send this cookie (it can also be a list of cookies separated by + ';') with each outgoing request. Only supported when using + protocols such as HTTP which support cookies, otherwise ignored. + + ``timeout`` + Set the timeout in seconds of the CURL connection. This timeout is + the time that CURL waits for a response from the remote server to + get the size of the image to be downloaded. If not set, the + default timeout of 5 seconds is used. + + Note that when passing options to qemu explicitly, ``driver`` is the + value of . + + Example: boot from a remote Fedora 20 live ISO image + + .. parsed-literal:: + + |qemu_system_x86| --drive media=cdrom,file=https://archives.fedoraproject.org/pub/archive/fedora/linux/releases/20/Live/x86_64/Fedora-Live-Desktop-x86_64-20-1.iso,readonly + + |qemu_system_x86| --drive media=cdrom,file.driver=http,file.url=http://archives.fedoraproject.org/pub/fedora/linux/releases/20/Live/x86_64/Fedora-Live-Desktop-x86_64-20-1.iso,readonly + + Example: boot from a remote Fedora 20 cloud image using a local + overlay for writes, copy-on-read, and a readahead of 64k + + .. parsed-literal:: + + qemu-img create -f qcow2 -o backing_file='json:{"file.driver":"http",, "file.url":"http://archives.fedoraproject.org/pub/archive/fedora/linux/releases/20/Images/x86_64/Fedora-x86_64-20-20131211.1-sda.qcow2",, "file.readahead":"64k"}' /tmp/Fedora-x86_64-20-20131211.1-sda.qcow2 + + |qemu_system_x86| -drive file=/tmp/Fedora-x86_64-20-20131211.1-sda.qcow2,copy-on-read=on + + Example: boot from an image stored on a VMware vSphere server with a + self-signed certificate using a local overlay for writes, a readahead + of 64k and a timeout of 10 seconds. + + .. parsed-literal:: + + qemu-img create -f qcow2 -o backing_file='json:{"file.driver":"https",, "file.url":"https://user:password@vsphere.example.com/folder/test/test-flat.vmdk?dcPath=Datacenter&dsName=datastore1",, "file.sslverify":"off",, "file.readahead":"64k",, "file.timeout":10}' /tmp/test.qcow2 + + |qemu_system_x86| -drive file=/tmp/test.qcow2 diff --git a/docs/system/gdb.rst b/docs/system/gdb.rst new file mode 100644 index 0000000000..a40145fcf8 --- /dev/null +++ b/docs/system/gdb.rst @@ -0,0 +1,89 @@ +.. _gdb_005fusage: + +GDB usage +--------- + +QEMU supports working with gdb via gdb's remote-connection facility +(the "gdbstub"). This allows you to debug guest code in the same +way that you might with a low-level debug facility like JTAG +on real hardware. You can stop and start the virtual machine, +examine state like registers and memory, and set breakpoints and +watchpoints. + +In order to use gdb, launch QEMU with the ``-s`` and ``-S`` options. +The ``-s`` option will make QEMU listen for an incoming connection +from gdb on TCP port 1234, and ``-S`` will make QEMU not start the +guest until you tell it to from gdb. (If you want to specify which +TCP port to use or to use something other than TCP for the gdbstub +connection, use the ``-gdb dev`` option instead of ``-s``.) + +.. parsed-literal:: + + |qemu_system| -s -S -kernel bzImage -hda rootdisk.img -append "root=/dev/hda" + +QEMU will launch but will silently wait for gdb to connect. + +Then launch gdb on the 'vmlinux' executable:: + + > gdb vmlinux + +In gdb, connect to QEMU:: + + (gdb) target remote localhost:1234 + +Then you can use gdb normally. For example, type 'c' to launch the +kernel:: + + (gdb) c + +Here are some useful tips in order to use gdb on system code: + +1. Use ``info reg`` to display all the CPU registers. + +2. Use ``x/10i $eip`` to display the code at the PC position. + +3. Use ``set architecture i8086`` to dump 16 bit code. Then use + ``x/10i $cs*16+$eip`` to dump the code at the PC position. + +Advanced debugging options: + +The default single stepping behavior is step with the IRQs and timer +service routines off. It is set this way because when gdb executes a +single step it expects to advance beyond the current instruction. With +the IRQs and timer service routines on, a single step might jump into +the one of the interrupt or exception vectors instead of executing the +current instruction. This means you may hit the same breakpoint a number +of times before executing the instruction gdb wants to have executed. +Because there are rare circumstances where you want to single step into +an interrupt vector the behavior can be controlled from GDB. There are +three commands you can query and set the single step behavior: + +``maintenance packet qqemu.sstepbits`` + This will display the MASK bits used to control the single stepping + IE: + + :: + + (gdb) maintenance packet qqemu.sstepbits + sending: "qqemu.sstepbits" + received: "ENABLE=1,NOIRQ=2,NOTIMER=4" + +``maintenance packet qqemu.sstep`` + This will display the current value of the mask used when single + stepping IE: + + :: + + (gdb) maintenance packet qqemu.sstep + sending: "qqemu.sstep" + received: "0x7" + +``maintenance packet Qqemu.sstep=HEX_VALUE`` + This will change the single step mask, so if wanted to enable IRQs on + the single step, but not timers, you would use: + + :: + + (gdb) maintenance packet Qqemu.sstep=0x5 + sending: "qemu.sstep=0x5" + received: "OK" diff --git a/docs/system/images.rst b/docs/system/images.rst new file mode 100644 index 0000000000..ff26bf9587 --- /dev/null +++ b/docs/system/images.rst @@ -0,0 +1,85 @@ +.. _disk_005fimages: + +Disk Images +----------- + +QEMU supports many disk image formats, including growable disk images +(their size increase as non empty sectors are written), compressed and +encrypted disk images. + +.. _disk_005fimages_005fquickstart: + +Quick start for disk image creation +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can create a disk image with the command:: + + qemu-img create myimage.img mysize + +where myimage.img is the disk image filename and mysize is its size in +kilobytes. You can add an ``M`` suffix to give the size in megabytes and +a ``G`` suffix for gigabytes. + +See the qemu-img invocation documentation for more information. + +.. _disk_005fimages_005fsnapshot_005fmode: + +Snapshot mode +~~~~~~~~~~~~~ + +If you use the option ``-snapshot``, all disk images are considered as +read only. When sectors in written, they are written in a temporary file +created in ``/tmp``. You can however force the write back to the raw +disk images by using the ``commit`` monitor command (or C-a s in the +serial console). + +.. _vm_005fsnapshots: + +VM snapshots +~~~~~~~~~~~~ + +VM snapshots are snapshots of the complete virtual machine including CPU +state, RAM, device state and the content of all the writable disks. In +order to use VM snapshots, you must have at least one non removable and +writable block device using the ``qcow2`` disk image format. Normally +this device is the first virtual hard drive. + +Use the monitor command ``savevm`` to create a new VM snapshot or +replace an existing one. A human readable name can be assigned to each +snapshot in addition to its numerical ID. + +Use ``loadvm`` to restore a VM snapshot and ``delvm`` to remove a VM +snapshot. ``info snapshots`` lists the available snapshots with their +associated information:: + + (qemu) info snapshots + Snapshot devices: hda + Snapshot list (from hda): + ID TAG VM SIZE DATE VM CLOCK + 1 start 41M 2006-08-06 12:38:02 00:00:14.954 + 2 40M 2006-08-06 12:43:29 00:00:18.633 + 3 msys 40M 2006-08-06 12:44:04 00:00:23.514 + +A VM snapshot is made of a VM state info (its size is shown in +``info snapshots``) and a snapshot of every writable disk image. The VM +state info is stored in the first ``qcow2`` non removable and writable +block device. The disk image snapshots are stored in every disk image. +The size of a snapshot in a disk image is difficult to evaluate and is +not shown by ``info snapshots`` because the associated disk sectors are +shared among all the snapshots to save disk space (otherwise each +snapshot would need a full copy of all the disk images). + +When using the (unrelated) ``-snapshot`` option +(:ref:`disk_005fimages_005fsnapshot_005fmode`), +you can always make VM snapshots, but they are deleted as soon as you +exit QEMU. + +VM snapshots currently have the following known limitations: + +- They cannot cope with removable devices if they are removed or + inserted after a snapshot is done. + +- A few device drivers still have incomplete snapshot support so their + state is not saved or restored properly (in particular USB). + +.. include:: qemu-block-drivers.rst.inc diff --git a/docs/system/index.rst b/docs/system/index.rst new file mode 100644 index 0000000000..c0f685b818 --- /dev/null +++ b/docs/system/index.rst @@ -0,0 +1,35 @@ +.. This is the top level page for the 'system' manual. + + +QEMU System Emulation User's Guide +================================== + +This manual is the overall guide for users using QEMU +for full system emulation (as opposed to user-mode emulation). +This includes working with hypervisors such as KVM, Xen, Hax +or Hypervisor.Framework. + +Contents: + +.. toctree:: + :maxdepth: 3 + + quickstart + invocation + keys + mux-chardev + monitor + images + net + usb + ivshmem + linuxboot + vnc-security + tls + gdb + managed-startup + targets + security + deprecated + build-platforms + license diff --git a/docs/system/invocation.rst b/docs/system/invocation.rst new file mode 100644 index 0000000000..4ba38fc23d --- /dev/null +++ b/docs/system/invocation.rst @@ -0,0 +1,18 @@ +.. _sec_005finvocation: + +Invocation +---------- + +.. parsed-literal:: + + |qemu_system| [options] [disk_image] + +disk_image is a raw hard disk image for IDE hard disk 0. Some targets do +not need a disk image. + +.. hxtool-doc:: qemu-options.hx + +Device URL Syntax +~~~~~~~~~~~~~~~~~ + +.. include:: device-url-syntax.rst.inc diff --git a/docs/system/ivshmem.rst b/docs/system/ivshmem.rst new file mode 100644 index 0000000000..b03a48afa3 --- /dev/null +++ b/docs/system/ivshmem.rst @@ -0,0 +1,64 @@ +.. _pcsys_005fivshmem: + +Inter-VM Shared Memory device +----------------------------- + +On Linux hosts, a shared memory device is available. The basic syntax +is: + +.. parsed-literal:: + + |qemu_system_x86| -device ivshmem-plain,memdev=hostmem + +where hostmem names a host memory backend. For a POSIX shared memory +backend, use something like + +:: + + -object memory-backend-file,size=1M,share,mem-path=/dev/shm/ivshmem,id=hostmem + +If desired, interrupts can be sent between guest VMs accessing the same +shared memory region. Interrupt support requires using a shared memory +server and using a chardev socket to connect to it. The code for the +shared memory server is qemu.git/contrib/ivshmem-server. An example +syntax when using the shared memory server is: + +.. parsed-literal:: + + # First start the ivshmem server once and for all + ivshmem-server -p pidfile -S path -m shm-name -l shm-size -n vectors + + # Then start your qemu instances with matching arguments + |qemu_system_x86| -device ivshmem-doorbell,vectors=vectors,chardev=id + -chardev socket,path=path,id=id + +When using the server, the guest will be assigned a VM ID (>=0) that +allows guests using the same server to communicate via interrupts. +Guests can read their VM ID from a device register (see +ivshmem-spec.txt). + +Migration with ivshmem +~~~~~~~~~~~~~~~~~~~~~~ + +With device property ``master=on``, the guest will copy the shared +memory on migration to the destination host. With ``master=off``, the +guest will not be able to migrate with the device attached. In the +latter case, the device should be detached and then reattached after +migration using the PCI hotplug support. + +At most one of the devices sharing the same memory can be master. The +master must complete migration before you plug back the other devices. + +ivshmem and hugepages +~~~~~~~~~~~~~~~~~~~~~ + +Instead of specifying the using POSIX shm, you may specify a +memory backend that has hugepage support: + +.. parsed-literal:: + + |qemu_system_x86| -object memory-backend-file,size=1G,mem-path=/dev/hugepages/my-shmem-file,share,id=mb1 + -device ivshmem-plain,memdev=mb1 + +ivshmem-server also supports hugepages mount points with the ``-m`` +memory path argument. diff --git a/docs/system/keys.rst b/docs/system/keys.rst new file mode 100644 index 0000000000..e596ae6c4e --- /dev/null +++ b/docs/system/keys.rst @@ -0,0 +1,6 @@ +.. _pcsys_005fkeys: + +Keys in the graphical frontends +------------------------------- + +.. include:: keys.rst.inc diff --git a/docs/system/keys.rst.inc b/docs/system/keys.rst.inc new file mode 100644 index 0000000000..bd9b8e5f6f --- /dev/null +++ b/docs/system/keys.rst.inc @@ -0,0 +1,35 @@ +During the graphical emulation, you can use special key combinations to +change modes. The default key mappings are shown below, but if you use +``-alt-grab`` then the modifier is Ctrl-Alt-Shift (instead of Ctrl-Alt) +and if you use ``-ctrl-grab`` then the modifier is the right Ctrl key +(instead of Ctrl-Alt): + +Ctrl-Alt-f + Toggle full screen + +Ctrl-Alt-+ + Enlarge the screen + +Ctrl-Alt\-- + Shrink the screen + +Ctrl-Alt-u + Restore the screen's un-scaled dimensions + +Ctrl-Alt-n + Switch to virtual console 'n'. Standard console mappings are: + + *1* + Target system display + + *2* + Monitor + + *3* + Serial port + +Ctrl-Alt + Toggle mouse and keyboard grab. + +In the virtual consoles, you can use Ctrl-Up, Ctrl-Down, Ctrl-PageUp and +Ctrl-PageDown to move in the back log. diff --git a/docs/system/license.rst b/docs/system/license.rst new file mode 100644 index 0000000000..cde3d2d25d --- /dev/null +++ b/docs/system/license.rst @@ -0,0 +1,11 @@ +.. _License: + +License +======= + +QEMU is a trademark of Fabrice Bellard. + +QEMU is released under the `GNU General Public +License `__, version 2. Parts +of QEMU have specific licenses, see file +`LICENSE `__. diff --git a/docs/system/linuxboot.rst b/docs/system/linuxboot.rst new file mode 100644 index 0000000000..228650abc5 --- /dev/null +++ b/docs/system/linuxboot.rst @@ -0,0 +1,30 @@ +.. _direct_005flinux_005fboot: + +Direct Linux Boot +----------------- + +This section explains how to launch a Linux kernel inside QEMU without +having to make a full bootable image. It is very useful for fast Linux +kernel testing. + +The syntax is: + +.. parsed-literal:: + + |qemu_system| -kernel bzImage -hda rootdisk.img -append "root=/dev/hda" + +Use ``-kernel`` to provide the Linux kernel image and ``-append`` to +give the kernel command line arguments. The ``-initrd`` option can be +used to provide an INITRD image. + +If you do not need graphical output, you can disable it and redirect the +virtual serial port and the QEMU monitor to the console with the +``-nographic`` option. The typical command line is: + +.. parsed-literal:: + + |qemu_system| -kernel bzImage -hda rootdisk.img \ + -append "root=/dev/hda console=ttyS0" -nographic + +Use Ctrl-a c to switch between the serial console and the monitor (see +:ref:`pcsys_005fkeys`). diff --git a/docs/system/managed-startup.rst b/docs/system/managed-startup.rst new file mode 100644 index 0000000000..9bcf98ea79 --- /dev/null +++ b/docs/system/managed-startup.rst @@ -0,0 +1,35 @@ +Managed start up options +======================== + +In system mode emulation, it's possible to create a VM in a paused +state using the ``-S`` command line option. In this state the machine +is completely initialized according to command line options and ready +to execute VM code but VCPU threads are not executing any code. The VM +state in this paused state depends on the way QEMU was started. It +could be in: + +- initial state (after reset/power on state) +- with direct kernel loading, the initial state could be amended to execute + code loaded by QEMU in the VM's RAM and with incoming migration +- with incoming migration, initial state will be amended with the migrated + machine state after migration completes + +This paused state is typically used by users to query machine state and/or +additionally configure the machine (by hotplugging devices) in runtime before +allowing VM code to run. + +However, at the ``-S`` pause point, it's impossible to configure options +that affect initial VM creation (like: ``-smp``/``-m``/``-numa`` ...) or +cold plug devices. The experimental ``--preconfig`` command line option +allows pausing QEMU before the initial VM creation, in a "preconfig" state, +where additional queries and configuration can be performed via QMP +before moving on to the resulting configuration startup. In the +preconfig state, QEMU only allows a limited set of commands over the +QMP monitor, where the commands do not depend on an initialized +machine, including but not limited to: + +- ``qmp_capabilities`` +- ``query-qmp-schema`` +- ``query-commands`` +- ``query-status`` +- ``x-exit-preconfig`` diff --git a/docs/system/monitor.rst b/docs/system/monitor.rst new file mode 100644 index 0000000000..0bcd5da216 --- /dev/null +++ b/docs/system/monitor.rst @@ -0,0 +1,31 @@ +.. _pcsys_005fmonitor: + +QEMU Monitor +------------ + +The QEMU monitor is used to give complex commands to the QEMU emulator. +You can use it to: + +- Remove or insert removable media images (such as CD-ROM or + floppies). + +- Freeze/unfreeze the Virtual Machine (VM) and save or restore its + state from a disk file. + +- Inspect the VM state without an external debugger. + +Commands +~~~~~~~~ + +The following commands are available: + +.. hxtool-doc:: hmp-commands.hx + +.. hxtool-doc:: hmp-commands-info.hx + +Integer expressions +~~~~~~~~~~~~~~~~~~~ + +The monitor understands integers expressions for every integer argument. +You can use register names to get the value of specifics CPU registers +by prefixing them with *$*. diff --git a/docs/system/mux-chardev.rst b/docs/system/mux-chardev.rst new file mode 100644 index 0000000000..413a6b3446 --- /dev/null +++ b/docs/system/mux-chardev.rst @@ -0,0 +1,6 @@ +.. _mux_005fkeys: + +Keys in the character backend multiplexer +----------------------------------------- + +.. include:: mux-chardev.rst.inc diff --git a/docs/system/mux-chardev.rst.inc b/docs/system/mux-chardev.rst.inc new file mode 100644 index 0000000000..84ea12cbf5 --- /dev/null +++ b/docs/system/mux-chardev.rst.inc @@ -0,0 +1,27 @@ +During emulation, if you are using a character backend multiplexer +(which is the default if you are using ``-nographic``) then several +commands are available via an escape sequence. These key sequences all +start with an escape character, which is Ctrl-a by default, but can be +changed with ``-echr``. The list below assumes you're using the default. + +Ctrl-a h + Print this help + +Ctrl-a x + Exit emulator + +Ctrl-a s + Save disk data back to file (if -snapshot) + +Ctrl-a t + Toggle console timestamps + +Ctrl-a b + Send break (magic sysrq in Linux) + +Ctrl-a c + Rotate between the frontends connected to the multiplexer (usually + this switches between the monitor and the console) + +Ctrl-a Ctrl-a + Send the escape character to the frontend diff --git a/docs/system/net.rst b/docs/system/net.rst new file mode 100644 index 0000000000..4b2640c448 --- /dev/null +++ b/docs/system/net.rst @@ -0,0 +1,100 @@ +.. _pcsys_005fnetwork: + +Network emulation +----------------- + +QEMU can simulate several network cards (e.g. PCI or ISA cards on the PC +target) and can connect them to a network backend on the host or an +emulated hub. The various host network backends can either be used to +connect the NIC of the guest to a real network (e.g. by using a TAP +devices or the non-privileged user mode network stack), or to other +guest instances running in another QEMU process (e.g. by using the +socket host network backend). + +Using TAP network interfaces +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This is the standard way to connect QEMU to a real network. QEMU adds a +virtual network device on your host (called ``tapN``), and you can then +configure it as if it was a real ethernet card. + +Linux host +^^^^^^^^^^ + +As an example, you can download the ``linux-test-xxx.tar.gz`` archive +and copy the script ``qemu-ifup`` in ``/etc`` and configure properly +``sudo`` so that the command ``ifconfig`` contained in ``qemu-ifup`` can +be executed as root. You must verify that your host kernel supports the +TAP network interfaces: the device ``/dev/net/tun`` must be present. + +See :ref:`sec_005finvocation` to have examples of command +lines using the TAP network interfaces. + +Windows host +^^^^^^^^^^^^ + +There is a virtual ethernet driver for Windows 2000/XP systems, called +TAP-Win32. But it is not included in standard QEMU for Windows, so you +will need to get it separately. It is part of OpenVPN package, so +download OpenVPN from : https://openvpn.net/. + +Using the user mode network stack +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +By using the option ``-net user`` (default configuration if no ``-net`` +option is specified), QEMU uses a completely user mode network stack +(you don't need root privilege to use the virtual network). The virtual +network configuration is the following:: + + guest (10.0.2.15) <------> Firewall/DHCP server <-----> Internet + | (10.0.2.2) + | + ----> DNS server (10.0.2.3) + | + ----> SMB server (10.0.2.4) + +The QEMU VM behaves as if it was behind a firewall which blocks all +incoming connections. You can use a DHCP client to automatically +configure the network in the QEMU VM. The DHCP server assign addresses +to the hosts starting from 10.0.2.15. + +In order to check that the user mode network is working, you can ping +the address 10.0.2.2 and verify that you got an address in the range +10.0.2.x from the QEMU virtual DHCP server. + +Note that ICMP traffic in general does not work with user mode +networking. ``ping``, aka. ICMP echo, to the local router (10.0.2.2) +shall work, however. If you're using QEMU on Linux >= 3.0, it can use +unprivileged ICMP ping sockets to allow ``ping`` to the Internet. The +host admin has to set the ping_group_range in order to grant access to +those sockets. To allow ping for GID 100 (usually users group):: + + echo 100 100 > /proc/sys/net/ipv4/ping_group_range + +When using the built-in TFTP server, the router is also the TFTP server. + +When using the ``'-netdev user,hostfwd=...'`` option, TCP or UDP +connections can be redirected from the host to the guest. It allows for +example to redirect X11, telnet or SSH connections. + +Hubs +~~~~ + +QEMU can simulate several hubs. A hub can be thought of as a virtual +connection between several network devices. These devices can be for +example QEMU virtual ethernet cards or virtual Host ethernet devices +(TAP devices). You can connect guest NICs or host network backends to +such a hub using the ``-netdev +hubport`` or ``-nic hubport`` options. The legacy ``-net`` option also +connects the given device to the emulated hub with ID 0 (i.e. the +default hub) unless you specify a netdev with ``-net nic,netdev=xxx`` +here. + +Connecting emulated networks between QEMU instances +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Using the ``-netdev socket`` (or ``-nic socket`` or ``-net socket``) +option, it is possible to create emulated networks that span several +QEMU instances. See the description of the ``-netdev socket`` option in +:ref:`sec_005finvocation` to have a basic +example. diff --git a/docs/system/qemu-block-drivers.rst b/docs/system/qemu-block-drivers.rst new file mode 100644 index 0000000000..bd99d4fa8e --- /dev/null +++ b/docs/system/qemu-block-drivers.rst @@ -0,0 +1,20 @@ +:orphan: + +QEMU block drivers reference +============================ + +Synopsis +-------- + +QEMU block driver reference manual + +Description +----------- + +.. include:: qemu-block-drivers.rst.inc + +See also +-------- + +The HTML documentation of QEMU for more precise information and Linux +user mode emulator invocation. diff --git a/docs/system/qemu-block-drivers.rst.inc b/docs/system/qemu-block-drivers.rst.inc new file mode 100644 index 0000000000..b052a6d14e --- /dev/null +++ b/docs/system/qemu-block-drivers.rst.inc @@ -0,0 +1,954 @@ +Disk image file formats +~~~~~~~~~~~~~~~~~~~~~~~ + +QEMU supports many image file formats that can be used with VMs as well as with +any of the tools (like ``qemu-img``). This includes the preferred formats +raw and qcow2 as well as formats that are supported for compatibility with +older QEMU versions or other hypervisors. + +Depending on the image format, different options can be passed to +``qemu-img create`` and ``qemu-img convert`` using the ``-o`` option. +This section describes each format and the options that are supported for it. + +.. program:: image-formats +.. option:: raw + + Raw disk image format. This format has the advantage of + being simple and easily exportable to all other emulators. If your + file system supports *holes* (for example in ext2 or ext3 on + Linux or NTFS on Windows), then only the written sectors will reserve + space. Use ``qemu-img info`` to know the real size used by the + image or ``ls -ls`` on Unix/Linux. + + Supported options: + + .. program:: raw + .. option:: preallocation + + Preallocation mode (allowed values: ``off``, ``falloc``, + ``full``). ``falloc`` mode preallocates space for image by + calling ``posix_fallocate()``. ``full`` mode preallocates space + for image by writing data to underlying storage. This data may or + may not be zero, depending on the storage location. + +.. program:: image-formats +.. option:: qcow2 + + QEMU image format, the most versatile format. Use it to have smaller + images (useful if your filesystem does not supports holes, for example + on Windows), zlib based compression and support of multiple VM + snapshots. + + Supported options: + + .. program:: qcow2 + .. option:: compat + + Determines the qcow2 version to use. ``compat=0.10`` uses the + traditional image format that can be read by any QEMU since 0.10. + ``compat=1.1`` enables image format extensions that only QEMU 1.1 and + newer understand (this is the default). Amongst others, this includes + zero clusters, which allow efficient copy-on-read for sparse images. + + .. option:: backing_file + + File name of a base image (see ``create`` subcommand) + + .. option:: backing_fmt + + Image format of the base image + + .. option:: encryption + + This option is deprecated and equivalent to ``encrypt.format=aes`` + + .. option:: encrypt.format + + If this is set to ``luks``, it requests that the qcow2 payload (not + qcow2 header) be encrypted using the LUKS format. The passphrase to + use to unlock the LUKS key slot is given by the ``encrypt.key-secret`` + parameter. LUKS encryption parameters can be tuned with the other + ``encrypt.*`` parameters. + + If this is set to ``aes``, the image is encrypted with 128-bit AES-CBC. + The encryption key is given by the ``encrypt.key-secret`` parameter. + This encryption format is considered to be flawed by modern cryptography + standards, suffering from a number of design problems: + + - The AES-CBC cipher is used with predictable initialization vectors based + on the sector number. This makes it vulnerable to chosen plaintext attacks + which can reveal the existence of encrypted data. + - The user passphrase is directly used as the encryption key. A poorly + chosen or short passphrase will compromise the security of the encryption. + - In the event of the passphrase being compromised there is no way to + change the passphrase to protect data in any qcow images. The files must + be cloned, using a different encryption passphrase in the new file. The + original file must then be securely erased using a program like shred, + though even this is ineffective with many modern storage technologies. + + The use of this is no longer supported in system emulators. Support only + remains in the command line utilities, for the purposes of data liberation + and interoperability with old versions of QEMU. The ``luks`` format + should be used instead. + + .. option:: encrypt.key-secret + + Provides the ID of a ``secret`` object that contains the passphrase + (``encrypt.format=luks``) or encryption key (``encrypt.format=aes``). + + .. option:: encrypt.cipher-alg + + Name of the cipher algorithm and key length. Currently defaults + to ``aes-256``. Only used when ``encrypt.format=luks``. + + .. option:: encrypt.cipher-mode + + Name of the encryption mode to use. Currently defaults to ``xts``. + Only used when ``encrypt.format=luks``. + + .. option:: encrypt.ivgen-alg + + Name of the initialization vector generator algorithm. Currently defaults + to ``plain64``. Only used when ``encrypt.format=luks``. + + .. option:: encrypt.ivgen-hash-alg + + Name of the hash algorithm to use with the initialization vector generator + (if required). Defaults to ``sha256``. Only used when ``encrypt.format=luks``. + + .. option:: encrypt.hash-alg + + Name of the hash algorithm to use for PBKDF algorithm + Defaults to ``sha256``. Only used when ``encrypt.format=luks``. + + .. option:: encrypt.iter-time + + Amount of time, in milliseconds, to use for PBKDF algorithm per key slot. + Defaults to ``2000``. Only used when ``encrypt.format=luks``. + + .. option:: cluster_size + + Changes the qcow2 cluster size (must be between 512 and 2M). Smaller cluster + sizes can improve the image file size whereas larger cluster sizes generally + provide better performance. + + .. option:: preallocation + + Preallocation mode (allowed values: ``off``, ``metadata``, ``falloc``, + ``full``). An image with preallocated metadata is initially larger but can + improve performance when the image needs to grow. ``falloc`` and ``full`` + preallocations are like the same options of ``raw`` format, but sets up + metadata also. + + .. option:: lazy_refcounts + + If this option is set to ``on``, reference count updates are postponed with + the goal of avoiding metadata I/O and improving performance. This is + particularly interesting with :option:`cache=writethrough` which doesn't batch + metadata updates. The tradeoff is that after a host crash, the reference count + tables must be rebuilt, i.e. on the next open an (automatic) ``qemu-img + check -r all`` is required, which may take some time. + + This option can only be enabled if ``compat=1.1`` is specified. + + .. option:: nocow + + If this option is set to ``on``, it will turn off COW of the file. It's only + valid on btrfs, no effect on other file systems. + + Btrfs has low performance when hosting a VM image file, even more + when the guest on the VM also using btrfs as file system. Turning off + COW is a way to mitigate this bad performance. Generally there are two + ways to turn off COW on btrfs: + + - Disable it by mounting with nodatacow, then all newly created files + will be NOCOW. + - For an empty file, add the NOCOW file attribute. That's what this + option does. + + Note: this option is only valid to new or empty files. If there is + an existing file which is COW and has data blocks already, it couldn't + be changed to NOCOW by setting ``nocow=on``. One can issue ``lsattr + filename`` to check if the NOCOW flag is set or not (Capital 'C' is + NOCOW flag). + +.. program:: image-formats +.. option:: qed + + Old QEMU image format with support for backing files and compact image files + (when your filesystem or transport medium does not support holes). + + When converting QED images to qcow2, you might want to consider using the + ``lazy_refcounts=on`` option to get a more QED-like behaviour. + + Supported options: + + .. program:: qed + .. option:: backing_file + + File name of a base image (see ``create`` subcommand). + + .. option:: backing_fmt + + Image file format of backing file (optional). Useful if the format cannot be + autodetected because it has no header, like some vhd/vpc files. + + .. option:: cluster_size + + Changes the cluster size (must be power-of-2 between 4K and 64K). Smaller + cluster sizes can improve the image file size whereas larger cluster sizes + generally provide better performance. + + .. option:: table_size + + Changes the number of clusters per L1/L2 table (must be + power-of-2 between 1 and 16). There is normally no need to + change this value but this option can between used for + performance benchmarking. + +.. program:: image-formats +.. option:: qcow + + Old QEMU image format with support for backing files, compact image files, + encryption and compression. + + Supported options: + + .. program:: qcow + .. option:: backing_file + + File name of a base image (see ``create`` subcommand) + + .. option:: encryption + + This option is deprecated and equivalent to ``encrypt.format=aes`` + + .. option:: encrypt.format + + If this is set to ``aes``, the image is encrypted with 128-bit AES-CBC. + The encryption key is given by the ``encrypt.key-secret`` parameter. + This encryption format is considered to be flawed by modern cryptography + standards, suffering from a number of design problems enumerated previously + against the ``qcow2`` image format. + + The use of this is no longer supported in system emulators. Support only + remains in the command line utilities, for the purposes of data liberation + and interoperability with old versions of QEMU. + + Users requiring native encryption should use the ``qcow2`` format + instead with ``encrypt.format=luks``. + + .. option:: encrypt.key-secret + + Provides the ID of a ``secret`` object that contains the encryption + key (``encrypt.format=aes``). + +.. program:: image-formats +.. option:: luks + + LUKS v1 encryption format, compatible with Linux dm-crypt/cryptsetup + + Supported options: + + .. program:: luks + .. option:: key-secret + + Provides the ID of a ``secret`` object that contains the passphrase. + + .. option:: cipher-alg + + Name of the cipher algorithm and key length. Currently defaults + to ``aes-256``. + + .. option:: cipher-mode + + Name of the encryption mode to use. Currently defaults to ``xts``. + + .. option:: ivgen-alg + + Name of the initialization vector generator algorithm. Currently defaults + to ``plain64``. + + .. option:: ivgen-hash-alg + + Name of the hash algorithm to use with the initialization vector generator + (if required). Defaults to ``sha256``. + + .. option:: hash-alg + + Name of the hash algorithm to use for PBKDF algorithm + Defaults to ``sha256``. + + .. option:: iter-time + + Amount of time, in milliseconds, to use for PBKDF algorithm per key slot. + Defaults to ``2000``. + +.. program:: image-formats +.. option:: vdi + + VirtualBox 1.1 compatible image format. + + Supported options: + + .. program:: vdi + .. option:: static + + If this option is set to ``on``, the image is created with metadata + preallocation. + +.. program:: image-formats +.. option:: vmdk + + VMware 3 and 4 compatible image format. + + Supported options: + + .. program: vmdk + .. option:: backing_file + + File name of a base image (see ``create`` subcommand). + + .. option:: compat6 + + Create a VMDK version 6 image (instead of version 4) + + .. option:: hwversion + + Specify vmdk virtual hardware version. Compat6 flag cannot be enabled + if hwversion is specified. + + .. option:: subformat + + Specifies which VMDK subformat to use. Valid options are + ``monolithicSparse`` (default), + ``monolithicFlat``, + ``twoGbMaxExtentSparse``, + ``twoGbMaxExtentFlat`` and + ``streamOptimized``. + +.. program:: image-formats +.. option:: vpc + + VirtualPC compatible image format (VHD). + + Supported options: + + .. program:: vpc + .. option:: subformat + + Specifies which VHD subformat to use. Valid options are + ``dynamic`` (default) and ``fixed``. + +.. program:: image-formats +.. option:: VHDX + + Hyper-V compatible image format (VHDX). + + Supported options: + + .. program:: VHDX + .. option:: subformat + + Specifies which VHDX subformat to use. Valid options are + ``dynamic`` (default) and ``fixed``. + + .. option:: block_state_zero + + Force use of payload blocks of type 'ZERO'. Can be set to ``on`` (default) + or ``off``. When set to ``off``, new blocks will be created as + ``PAYLOAD_BLOCK_NOT_PRESENT``, which means parsers are free to return + arbitrary data for those blocks. Do not set to ``off`` when using + ``qemu-img convert`` with ``subformat=dynamic``. + + .. option:: block_size + + Block size; min 1 MB, max 256 MB. 0 means auto-calculate based on + image size. + + .. option:: log_size + + Log size; min 1 MB. + +Read-only formats +~~~~~~~~~~~~~~~~~ + +More disk image file formats are supported in a read-only mode. + +.. program:: image-formats +.. option:: bochs + + Bochs images of ``growing`` type. + +.. program:: image-formats +.. option:: cloop + + Linux Compressed Loop image, useful only to reuse directly compressed + CD-ROM images present for example in the Knoppix CD-ROMs. + +.. program:: image-formats +.. option:: dmg + + Apple disk image. + +.. program:: image-formats +.. option:: parallels + + Parallels disk image format. + +Using host drives +~~~~~~~~~~~~~~~~~ + +In addition to disk image files, QEMU can directly access host +devices. We describe here the usage for QEMU version >= 0.8.3. + +Linux +^^^^^ + +On Linux, you can directly use the host device filename instead of a +disk image filename provided you have enough privileges to access +it. For example, use ``/dev/cdrom`` to access to the CDROM. + +CD + You can specify a CDROM device even if no CDROM is loaded. QEMU has + specific code to detect CDROM insertion or removal. CDROM ejection by + the guest OS is supported. Currently only data CDs are supported. + +Floppy + You can specify a floppy device even if no floppy is loaded. Floppy + removal is currently not detected accurately (if you change floppy + without doing floppy access while the floppy is not loaded, the guest + OS will think that the same floppy is loaded). + Use of the host's floppy device is deprecated, and support for it will + be removed in a future release. + +Hard disks + Hard disks can be used. Normally you must specify the whole disk + (``/dev/hdb`` instead of ``/dev/hdb1``) so that the guest OS can + see it as a partitioned disk. WARNING: unless you know what you do, it + is better to only make READ-ONLY accesses to the hard disk otherwise + you may corrupt your host data (use the ``-snapshot`` command + line option or modify the device permissions accordingly). + +Windows +^^^^^^^ + +CD + The preferred syntax is the drive letter (e.g. ``d:``). The + alternate syntax ``\\.\d:`` is supported. ``/dev/cdrom`` is + supported as an alias to the first CDROM drive. + + Currently there is no specific code to handle removable media, so it + is better to use the ``change`` or ``eject`` monitor commands to + change or eject media. + +Hard disks + Hard disks can be used with the syntax: ``\\.\PhysicalDriveN`` + where *N* is the drive number (0 is the first hard disk). + + WARNING: unless you know what you do, it is better to only make + READ-ONLY accesses to the hard disk otherwise you may corrupt your + host data (use the ``-snapshot`` command line so that the + modifications are written in a temporary file). + +Mac OS X +^^^^^^^^ + +``/dev/cdrom`` is an alias to the first CDROM. + +Currently there is no specific code to handle removable media, so it +is better to use the ``change`` or ``eject`` monitor commands to +change or eject media. + +Virtual FAT disk images +~~~~~~~~~~~~~~~~~~~~~~~ + +QEMU can automatically create a virtual FAT disk image from a +directory tree. In order to use it, just type: + +.. parsed-literal:: + + |qemu_system| linux.img -hdb fat:/my_directory + +Then you access access to all the files in the ``/my_directory`` +directory without having to copy them in a disk image or to export +them via SAMBA or NFS. The default access is *read-only*. + +Floppies can be emulated with the ``:floppy:`` option: + +.. parsed-literal:: + + |qemu_system| linux.img -fda fat:floppy:/my_directory + +A read/write support is available for testing (beta stage) with the +``:rw:`` option: + +.. parsed-literal:: + + |qemu_system| linux.img -fda fat:floppy:rw:/my_directory + +What you should *never* do: + +- use non-ASCII filenames +- use "-snapshot" together with ":rw:" +- expect it to work when loadvm'ing +- write to the FAT directory on the host system while accessing it with the guest system + +NBD access +~~~~~~~~~~ + +QEMU can access directly to block device exported using the Network Block Device +protocol. + +.. parsed-literal:: + + |qemu_system| linux.img -hdb nbd://my_nbd_server.mydomain.org:1024/ + +If the NBD server is located on the same host, you can use an unix socket instead +of an inet socket: + +.. parsed-literal:: + + |qemu_system| linux.img -hdb nbd+unix://?socket=/tmp/my_socket + +In this case, the block device must be exported using qemu-nbd: + +.. parsed-literal:: + + qemu-nbd --socket=/tmp/my_socket my_disk.qcow2 + +The use of qemu-nbd allows sharing of a disk between several guests: + +.. parsed-literal:: + + qemu-nbd --socket=/tmp/my_socket --share=2 my_disk.qcow2 + +and then you can use it with two guests: + +.. parsed-literal:: + + |qemu_system| linux1.img -hdb nbd+unix://?socket=/tmp/my_socket + |qemu_system| linux2.img -hdb nbd+unix://?socket=/tmp/my_socket + +If the nbd-server uses named exports (supported since NBD 2.9.18, or with QEMU's +own embedded NBD server), you must specify an export name in the URI: + +.. parsed-literal:: + + |qemu_system| -cdrom nbd://localhost/debian-500-ppc-netinst + |qemu_system| -cdrom nbd://localhost/openSUSE-11.1-ppc-netinst + +The URI syntax for NBD is supported since QEMU 1.3. An alternative syntax is +also available. Here are some example of the older syntax: + +.. parsed-literal:: + + |qemu_system| linux.img -hdb nbd:my_nbd_server.mydomain.org:1024 + |qemu_system| linux2.img -hdb nbd:unix:/tmp/my_socket + |qemu_system| -cdrom nbd:localhost:10809:exportname=debian-500-ppc-netinst + + + +Sheepdog disk images +~~~~~~~~~~~~~~~~~~~~ + +Sheepdog is a distributed storage system for QEMU. It provides highly +available block level storage volumes that can be attached to +QEMU-based virtual machines. + +You can create a Sheepdog disk image with the command: + +.. parsed-literal:: + + qemu-img create sheepdog:///IMAGE SIZE + +where *IMAGE* is the Sheepdog image name and *SIZE* is its +size. + +To import the existing *FILENAME* to Sheepdog, you can use a +convert command. + +.. parsed-literal:: + + qemu-img convert FILENAME sheepdog:///IMAGE + +You can boot from the Sheepdog disk image with the command: + +.. parsed-literal:: + + |qemu_system| sheepdog:///IMAGE + +You can also create a snapshot of the Sheepdog image like qcow2. + +.. parsed-literal:: + + qemu-img snapshot -c TAG sheepdog:///IMAGE + +where *TAG* is a tag name of the newly created snapshot. + +To boot from the Sheepdog snapshot, specify the tag name of the +snapshot. + +.. parsed-literal:: + + |qemu_system| sheepdog:///IMAGE#TAG + +You can create a cloned image from the existing snapshot. + +.. parsed-literal:: + + qemu-img create -b sheepdog:///BASE#TAG sheepdog:///IMAGE + +where *BASE* is an image name of the source snapshot and *TAG* +is its tag name. + +You can use an unix socket instead of an inet socket: + +.. parsed-literal:: + + |qemu_system| sheepdog+unix:///IMAGE?socket=PATH + +If the Sheepdog daemon doesn't run on the local host, you need to +specify one of the Sheepdog servers to connect to. + +.. parsed-literal:: + + qemu-img create sheepdog://HOSTNAME:PORT/IMAGE SIZE + |qemu_system| sheepdog://HOSTNAME:PORT/IMAGE + +iSCSI LUNs +~~~~~~~~~~ + +iSCSI is a popular protocol used to access SCSI devices across a computer +network. + +There are two different ways iSCSI devices can be used by QEMU. + +The first method is to mount the iSCSI LUN on the host, and make it appear as +any other ordinary SCSI device on the host and then to access this device as a +/dev/sd device from QEMU. How to do this differs between host OSes. + +The second method involves using the iSCSI initiator that is built into +QEMU. This provides a mechanism that works the same way regardless of which +host OS you are running QEMU on. This section will describe this second method +of using iSCSI together with QEMU. + +In QEMU, iSCSI devices are described using special iSCSI URLs. URL syntax: + +:: + + iscsi://[[%]@][:]// + +Username and password are optional and only used if your target is set up +using CHAP authentication for access control. +Alternatively the username and password can also be set via environment +variables to have these not show up in the process list: + +:: + + export LIBISCSI_CHAP_USERNAME= + export LIBISCSI_CHAP_PASSWORD= + iscsi://// + +Various session related parameters can be set via special options, either +in a configuration file provided via '-readconfig' or directly on the +command line. + +If the initiator-name is not specified qemu will use a default name +of 'iqn.2008-11.org.linux-kvm[:'] where is the UUID of the +virtual machine. If the UUID is not specified qemu will use +'iqn.2008-11.org.linux-kvm[:'] where is the name of the +virtual machine. + +Setting a specific initiator name to use when logging in to the target: + +:: + + -iscsi initiator-name=iqn.qemu.test:my-initiator + +Controlling which type of header digest to negotiate with the target: + +:: + + -iscsi header-digest=CRC32C|CRC32C-NONE|NONE-CRC32C|NONE + +These can also be set via a configuration file: + +:: + + [iscsi] + user = "CHAP username" + password = "CHAP password" + initiator-name = "iqn.qemu.test:my-initiator" + # header digest is one of CRC32C|CRC32C-NONE|NONE-CRC32C|NONE + header-digest = "CRC32C" + +Setting the target name allows different options for different targets: + +:: + + [iscsi "iqn.target.name"] + user = "CHAP username" + password = "CHAP password" + initiator-name = "iqn.qemu.test:my-initiator" + # header digest is one of CRC32C|CRC32C-NONE|NONE-CRC32C|NONE + header-digest = "CRC32C" + +How to use a configuration file to set iSCSI configuration options: + +.. parsed-literal:: + + cat >iscsi.conf < /sys/bus/pci/devices/0000:06:0d.0/driver/unbind + # echo 1102 0002 > /sys/bus/pci/drivers/vfio-pci/new_id + + # |qemu_system| -drive file=nvme://HOST:BUS:SLOT.FUNC/NAMESPACE + +Alternative syntax using properties: + +.. parsed-literal:: + + |qemu_system| -drive file.driver=nvme,file.device=HOST:BUS:SLOT.FUNC,file.namespace=NAMESPACE + +*HOST*:*BUS*:*SLOT*.\ *FUNC* is the NVMe controller's PCI device +address on the host. + +*NAMESPACE* is the NVMe namespace number, starting from 1. + +Disk image file locking +~~~~~~~~~~~~~~~~~~~~~~~ + +By default, QEMU tries to protect image files from unexpected concurrent +access, as long as it's supported by the block protocol driver and host +operating system. If multiple QEMU processes (including QEMU emulators and +utilities) try to open the same image with conflicting accessing modes, all but +the first one will get an error. + +This feature is currently supported by the file protocol on Linux with the Open +File Descriptor (OFD) locking API, and can be configured to fall back to POSIX +locking if the POSIX host doesn't support Linux OFD locking. + +To explicitly enable image locking, specify "locking=on" in the file protocol +driver options. If OFD locking is not possible, a warning will be printed and +the POSIX locking API will be used. In this case there is a risk that the lock +will get silently lost when doing hot plugging and block jobs, due to the +shortcomings of the POSIX locking API. + +QEMU transparently handles lock handover during shared storage migration. For +shared virtual disk images between multiple VMs, the "share-rw" device option +should be used. + +By default, the guest has exclusive write access to its disk image. If the +guest can safely share the disk image with other writers the +``-device ...,share-rw=on`` parameter can be used. This is only safe if +the guest is running software, such as a cluster file system, that +coordinates disk accesses to avoid corruption. + +Note that share-rw=on only declares the guest's ability to share the disk. +Some QEMU features, such as image file formats, require exclusive write access +to the disk image and this is unaffected by the share-rw=on option. + +Alternatively, locking can be fully disabled by "locking=off" block device +option. In the command line, the option is usually in the form of +"file.locking=off" as the protocol driver is normally placed as a "file" child +under a format driver. For example: + +:: + + -blockdev driver=qcow2,file.filename=/path/to/image,file.locking=off,file.driver=file + +To check if image locking is active, check the output of the "lslocks" command +on host and see if there are locks held by the QEMU process on the image file. +More than one byte could be locked by the QEMU instance, each byte of which +reflects a particular permission that is acquired or protected by the running +block driver. diff --git a/docs/system/qemu-cpu-models.rst b/docs/system/qemu-cpu-models.rst new file mode 100644 index 0000000000..53d7538c47 --- /dev/null +++ b/docs/system/qemu-cpu-models.rst @@ -0,0 +1,20 @@ +:orphan: + +QEMU / KVM CPU model configuration +================================== + +Synopsis +'''''''' + +QEMU CPU Modelling Infrastructure manual + +Description +''''''''''' + +.. include:: cpu-models-x86.rst.inc +.. include:: cpu-models-mips.rst.inc + +See also +'''''''' + +The HTML documentation of QEMU for more precise information and Linux user mode emulator invocation. diff --git a/docs/system/qemu-manpage.rst b/docs/system/qemu-manpage.rst new file mode 100644 index 0000000000..e9a25d0680 --- /dev/null +++ b/docs/system/qemu-manpage.rst @@ -0,0 +1,45 @@ +:orphan: + +.. + This file is the skeleton for the qemu.1 manpage. It mostly + should simply include the .rst.inc files corresponding to the + parts of the documentation that go in the manpage as well as the + HTML manual. + +Title +===== + +Synopsis +-------- + +.. parsed-literal:: + + |qemu_system| [options] [disk_image] + +Description +----------- + +.. include:: target-i386-desc.rst.inc + +Options +------- + +disk_image is a raw hard disk image for IDE hard disk 0. Some targets do +not need a disk image. + +.. hxtool-doc:: qemu-options.hx + +.. include:: keys.rst.inc + +.. include:: mux-chardev.rst.inc + +Notes +----- + +.. include:: device-url-syntax.rst.inc + +See also +-------- + +The HTML documentation of QEMU for more precise information and Linux +user mode emulator invocation. diff --git a/docs/system/quickstart.rst b/docs/system/quickstart.rst new file mode 100644 index 0000000000..3a3acab5e7 --- /dev/null +++ b/docs/system/quickstart.rst @@ -0,0 +1,13 @@ +.. _pcsys_005fquickstart: + +Quick Start +----------- + +Download and uncompress a PC hard disk image with Linux installed (e.g. +``linux.img``) and type: + +.. parsed-literal:: + + |qemu_system| linux.img + +Linux should boot and give you a prompt. diff --git a/docs/vfio-ap.txt b/docs/system/s390x/vfio-ap.rst similarity index 55% rename from docs/vfio-ap.txt rename to docs/system/s390x/vfio-ap.rst index b1eb2deeaf..3cd84179a2 100644 --- a/docs/vfio-ap.txt +++ b/docs/system/s390x/vfio-ap.rst @@ -1,17 +1,11 @@ Adjunct Processor (AP) Device ============================= -Contents: -========= -* Introduction -* AP Architectural Overview -* Start Interpretive Execution (SIE) Instruction -* AP Matrix Configuration on Linux Host -* Starting a Linux Guest Configured with an AP Matrix -* Example: Configure AP Matrices for Three Linux Guests +.. contents:: + +Introduction +------------ -Introduction: -============ The IBM Adjunct Processor (AP) Cryptographic Facility is comprised of three AP instructions and from 1 to 256 PCIe cryptographic adapter cards. These AP devices provide cryptographic functions to all CPUs assigned to a @@ -21,8 +15,9 @@ On s390x, AP adapter cards are exposed via the AP bus. This document describes how those cards may be made available to KVM guests using the VFIO mediated device framework. -AP Architectural Overview: -========================= +AP Architectural Overview +------------------------- + In order understand the terminology used in the rest of this document, let's start with some definitions: @@ -75,7 +70,8 @@ start with some definitions: must be one of the control domains. Start Interpretive Execution (SIE) Instruction -============================================== +---------------------------------------------- + A KVM guest is started by executing the Start Interpretive Execution (SIE) instruction. The SIE state description is a control block that contains the state information for a KVM guest and is supplied as input to the SIE @@ -114,246 +110,278 @@ The APQNs can provide secure key functionality - i.e., a private key is stored on the adapter card for each of its domains - so each APQN must be assigned to at most one guest or the linux host. - Example 1: Valid configuration: - ------------------------------ - Guest1: adapters 1,2 domains 5,6 - Guest2: adapter 1,2 domain 7 +Example 1: Valid configuration +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - This is valid because both guests have a unique set of APQNs: Guest1 has - APQNs (1,5), (1,6), (2,5) and (2,6); Guest2 has APQNs (1,7) and (2,7). ++----------+--------+--------+ +| | Guest1 | Guest2 | ++==========+========+========+ +| adapters | 1, 2 | 1, 2 | ++----------+--------+--------+ +| domains | 5, 6 | 7 | ++----------+--------+--------+ - Example 2: Valid configuration: - ------------------------------ - Guest1: adapters 1,2 domains 5,6 - Guest2: adapters 3,4 domains 5,6 +This is valid because both guests have a unique set of APQNs: - This is also valid because both guests have a unique set of APQNs: - Guest1 has APQNs (1,5), (1,6), (2,5), (2,6); - Guest2 has APQNs (3,5), (3,6), (4,5), (4,6) +* Guest1 has APQNs (1,5), (1,6), (2,5) and (2,6); +* Guest2 has APQNs (1,7) and (2,7). - Example 3: Invalid configuration: - -------------------------------- - Guest1: adapters 1,2 domains 5,6 - Guest2: adapter 1 domains 6,7 +Example 2: Valid configuration +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - This is an invalid configuration because both guests have access to - APQN (1,6). ++----------+--------+--------+ +| | Guest1 | Guest2 | ++==========+========+========+ +| adapters | 1, 2 | 3, 4 | ++----------+--------+--------+ +| domains | 5, 6 | 5, 6 | ++----------+--------+--------+ + +This is also valid because both guests have a unique set of APQNs: + +* Guest1 has APQNs (1,5), (1,6), (2,5), (2,6); +* Guest2 has APQNs (3,5), (3,6), (4,5), (4,6) + +Example 3: Invalid configuration +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ++----------+--------+--------+ +| | Guest1 | Guest2 | ++==========+========+========+ +| adapters | 1, 2 | 1 | ++----------+--------+--------+ +| domains | 5, 6 | 6, 7 | ++----------+--------+--------+ + +This is an invalid configuration because both guests have access to +APQN (1,6). + +AP Matrix Configuration on Linux Host +------------------------------------- -AP Matrix Configuration on Linux Host: -===================================== A linux system is a guest of the LPAR in which it is running and has access to the AP resources configured for the LPAR. The LPAR's AP matrix is configured via its Activation Profile which can be edited on the HMC. When the linux system is started, the AP bus will detect the AP devices assigned to the -LPAR and create the following in sysfs: +LPAR and create the following in sysfs:: -/sys/bus/ap -... [devices] -...... xx.yyyy -...... ... -...... cardxx -...... ... + /sys/bus/ap + ... [devices] + ...... xx.yyyy + ...... ... + ...... cardxx + ...... ... Where: - cardxx is AP adapter number xx (in hex) -....xx.yyyy is an APQN with xx specifying the APID and yyyy specifying the - APQI + +``cardxx`` + is AP adapter number xx (in hex) + +``xx.yyyy`` + is an APQN with xx specifying the APID and yyyy specifying the APQI For example, if AP adapters 5 and 6 and domains 4, 71 (0x47), 171 (0xab) and 255 (0xff) are configured for the LPAR, the sysfs representation on the linux -host system would look like this: +host system would look like this:: -/sys/bus/ap -... [devices] -...... 05.0004 -...... 05.0047 -...... 05.00ab -...... 05.00ff -...... 06.0004 -...... 06.0047 -...... 06.00ab -...... 06.00ff -...... card05 -...... card06 + /sys/bus/ap + ... [devices] + ...... 05.0004 + ...... 05.0047 + ...... 05.00ab + ...... 05.00ff + ...... 06.0004 + ...... 06.0047 + ...... 06.00ab + ...... 06.00ff + ...... card05 + ...... card06 A set of default device drivers are also created to control each type of AP -device that can be assigned to the LPAR on which a linux host is running: +device that can be assigned to the LPAR on which a linux host is running:: -/sys/bus/ap -... [drivers] -...... [cex2acard] for Crypto Express 2/3 accelerator cards -...... [cex2aqueue] for AP queues served by Crypto Express 2/3 - accelerator cards -...... [cex4card] for Crypto Express 4/5/6 accelerator and coprocessor - cards -...... [cex4queue] for AP queues served by Crypto Express 4/5/6 - accelerator and coprocessor cards -...... [pcixcccard] for Crypto Express 2/3 coprocessor cards -...... [pcixccqueue] for AP queues served by Crypto Express 2/3 - coprocessor cards + /sys/bus/ap + ... [drivers] + ...... [cex2acard] for Crypto Express 2/3 accelerator cards + ...... [cex2aqueue] for AP queues served by Crypto Express 2/3 + accelerator cards + ...... [cex4card] for Crypto Express 4/5/6 accelerator and coprocessor + cards + ...... [cex4queue] for AP queues served by Crypto Express 4/5/6 + accelerator and coprocessor cards + ...... [pcixcccard] for Crypto Express 2/3 coprocessor cards + ...... [pcixccqueue] for AP queues served by Crypto Express 2/3 + coprocessor cards Binding AP devices to device drivers ------------------------------------- +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + There are two sysfs files that specify bitmasks marking a subset of the APQN range as 'usable by the default AP queue device drivers' or 'not usable by the default device drivers' and thus available for use by the alternate device -driver(s). The sysfs locations of the masks are: +driver(s). The sysfs locations of the masks are:: /sys/bus/ap/apmask /sys/bus/ap/aqmask - The 'apmask' is a 256-bit mask that identifies a set of AP adapter IDs - (APID). Each bit in the mask, from left to right (i.e., from most significant - to least significant bit in big endian order), corresponds to an APID from - 0-255. If a bit is set, the APID is marked as usable only by the default AP - queue device drivers; otherwise, the APID is usable by the vfio_ap - device driver. +The ``apmask`` is a 256-bit mask that identifies a set of AP adapter IDs +(APID). Each bit in the mask, from left to right (i.e., from most significant +to least significant bit in big endian order), corresponds to an APID from +0-255. If a bit is set, the APID is marked as usable only by the default AP +queue device drivers; otherwise, the APID is usable by the vfio_ap +device driver. - The 'aqmask' is a 256-bit mask that identifies a set of AP queue indexes - (APQI). Each bit in the mask, from left to right (i.e., from most significant - to least significant bit in big endian order), corresponds to an APQI from - 0-255. If a bit is set, the APQI is marked as usable only by the default AP - queue device drivers; otherwise, the APQI is usable by the vfio_ap device - driver. +The ``aqmask`` is a 256-bit mask that identifies a set of AP queue indexes +(APQI). Each bit in the mask, from left to right (i.e., from most significant +to least significant bit in big endian order), corresponds to an APQI from +0-255. If a bit is set, the APQI is marked as usable only by the default AP +queue device drivers; otherwise, the APQI is usable by the vfio_ap device +driver. - Take, for example, the following mask: +Take, for example, the following mask:: 0x7dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff - It indicates: +It indicates: 1, 2, 3, 4, 5, and 7-255 belong to the default drivers' pool, and 0 and 6 belong to the vfio_ap device driver's pool. - The APQN of each AP queue device assigned to the linux host is checked by the - AP bus against the set of APQNs derived from the cross product of APIDs - and APQIs marked as usable only by the default AP queue device drivers. If a - match is detected, only the default AP queue device drivers will be probed; - otherwise, the vfio_ap device driver will be probed. +The APQN of each AP queue device assigned to the linux host is checked by the +AP bus against the set of APQNs derived from the cross product of APIDs +and APQIs marked as usable only by the default AP queue device drivers. If a +match is detected, only the default AP queue device drivers will be probed; +otherwise, the vfio_ap device driver will be probed. - By default, the two masks are set to reserve all APQNs for use by the default - AP queue device drivers. There are two ways the default masks can be changed: +By default, the two masks are set to reserve all APQNs for use by the default +AP queue device drivers. There are two ways the default masks can be changed: - 1. The sysfs mask files can be edited by echoing a string into the - respective sysfs mask file in one of two formats: + 1. The sysfs mask files can be edited by echoing a string into the + respective sysfs mask file in one of two formats: - * An absolute hex string starting with 0x - like "0x12345678" - sets - the mask. If the given string is shorter than the mask, it is padded - with 0s on the right; for example, specifying a mask value of 0x41 is - the same as specifying: + * An absolute hex string starting with 0x - like "0x12345678" - sets + the mask. If the given string is shorter than the mask, it is padded + with 0s on the right; for example, specifying a mask value of 0x41 is + the same as specifying:: 0x4100000000000000000000000000000000000000000000000000000000000000 - Keep in mind that the mask reads from left to right (i.e., most - significant to least significant bit in big endian order), so the mask - above identifies device numbers 1 and 7 (01000001). + Keep in mind that the mask reads from left to right (i.e., most + significant to least significant bit in big endian order), so the mask + above identifies device numbers 1 and 7 (``01000001``). - If the string is longer than the mask, the operation is terminated with - an error (EINVAL). + If the string is longer than the mask, the operation is terminated with + an error (EINVAL). - * Individual bits in the mask can be switched on and off by specifying - each bit number to be switched in a comma separated list. Each bit - number string must be prepended with a ('+') or minus ('-') to indicate - the corresponding bit is to be switched on ('+') or off ('-'). Some - valid values are: + * Individual bits in the mask can be switched on and off by specifying + each bit number to be switched in a comma separated list. Each bit + number string must be prepended with a (``+``) or minus (``-``) to indicate + the corresponding bit is to be switched on (``+``) or off (``-``). Some + valid values are:: "+0" switches bit 0 on "-13" switches bit 13 off "+0x41" switches bit 65 on "-0xff" switches bit 255 off - The following example: + The following example:: + +0,-6,+0x47,-0xf0 - Switches bits 0 and 71 (0x47) on - Switches bits 6 and 240 (0xf0) off + Switches bits 0 and 71 (0x47) on + Switches bits 6 and 240 (0xf0) off - Note that the bits not specified in the list remain as they were before - the operation. + Note that the bits not specified in the list remain as they were before + the operation. - 2. The masks can also be changed at boot time via parameters on the kernel - command line like this: + 2. The masks can also be changed at boot time via parameters on the kernel + command line like this:: ap.apmask=0xffff ap.aqmask=0x40 - This would create the following masks: + This would create the following masks: + + apmask:: - apmask: 0xffff000000000000000000000000000000000000000000000000000000000000 - aqmask: + aqmask:: + 0x4000000000000000000000000000000000000000000000000000000000000000 - Resulting in these two pools: + Resulting in these two pools:: default drivers pool: adapter 0-15, domain 1 alternate drivers pool: adapter 16-255, domains 0, 2-255 -Configuring an AP matrix for a linux guest. ------------------------------------------- +Configuring an AP matrix for a linux guest +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + The sysfs interfaces for configuring an AP matrix for a guest are built on the VFIO mediated device framework. To configure an AP matrix for a guest, a -mediated matrix device must first be created for the /sys/devices/vfio_ap/matrix +mediated matrix device must first be created for the ``/sys/devices/vfio_ap/matrix`` device. When the vfio_ap device driver is loaded, it registers with the VFIO mediated device framework. When the driver registers, the sysfs interfaces for -creating mediated matrix devices is created: +creating mediated matrix devices is created:: -/sys/devices -... [vfio_ap] -......[matrix] -......... [mdev_supported_types] -............ [vfio_ap-passthrough] -............... create -............... [devices] + /sys/devices + ... [vfio_ap] + ......[matrix] + ......... [mdev_supported_types] + ............ [vfio_ap-passthrough] + ............... create + ............... [devices] A mediated AP matrix device is created by writing a UUID to the attribute file -named 'create', for example: +named ``create``, for example:: uuidgen > create - or +or + +:: echo $uuid > create When a mediated AP matrix device is created, a sysfs directory named after -the UUID is created in the 'devices' subdirectory: +the UUID is created in the ``devices`` subdirectory:: -/sys/devices -... [vfio_ap] -......[matrix] -......... [mdev_supported_types] -............ [vfio_ap-passthrough] -............... create -............... [devices] -.................. [$uuid] + /sys/devices + ... [vfio_ap] + ......[matrix] + ......... [mdev_supported_types] + ............ [vfio_ap-passthrough] + ............... create + ............... [devices] + .................. [$uuid] There will also be three sets of attribute files created in the mediated matrix device's sysfs directory to configure an AP matrix for the -KVM guest: +KVM guest:: -/sys/devices -... [vfio_ap] -......[matrix] -......... [mdev_supported_types] -............ [vfio_ap-passthrough] -............... create -............... [devices] -.................. [$uuid] -..................... assign_adapter -..................... assign_control_domain -..................... assign_domain -..................... matrix -..................... unassign_adapter -..................... unassign_control_domain -..................... unassign_domain + /sys/devices + ... [vfio_ap] + ......[matrix] + ......... [mdev_supported_types] + ............ [vfio_ap-passthrough] + ............... create + ............... [devices] + .................. [$uuid] + ..................... assign_adapter + ..................... assign_control_domain + ..................... assign_domain + ..................... matrix + ..................... unassign_adapter + ..................... unassign_control_domain + ..................... unassign_domain -assign_adapter +``assign_adapter`` To assign an AP adapter to the mediated matrix device, its APID is written - to the 'assign_adapter' file. This may be done multiple times to assign more + to the ``assign_adapter`` file. This may be done multiple times to assign more than one adapter. The APID may be specified using conventional semantics as a decimal, hexadecimal, or octal number. For example, to assign adapters 4, 5 and 16 to a mediated matrix device in decimal, hexadecimal and octal - respectively: + respectively:: echo 4 > assign_adapter echo 0x5 > assign_adapter @@ -373,22 +401,22 @@ assign_adapter APQNs are bound to the driver, the operation will terminate with an error (EADDRNOTAVAIL). - No APQN that can be derived from the adapter ID and the IDs of the + * No APQN that can be derived from the adapter ID and the IDs of the previously assigned domains can be assigned to another mediated matrix device. If an APQN is assigned to another mediated matrix device, the operation will terminate with an error (EADDRINUSE). -unassign_adapter - To unassign an AP adapter, its APID is written to the 'unassign_adapter' +``unassign_adapter`` + To unassign an AP adapter, its APID is written to the ``unassign_adapter`` file. This may also be done multiple times to unassign more than one adapter. -assign_domain +``assign_domain`` To assign a usage domain, the domain number is written into the - 'assign_domain' file. This may be done multiple times to assign more than one + ``assign_domain`` file. This may be done multiple times to assign more than one usage domain. The domain number is specified using conventional semantics as a decimal, hexadecimal, or octal number. For example, to assign usage domains 4, 8, and 71 to a mediated matrix device in decimal, hexadecimal and octal - respectively: + respectively:: echo 4 > assign_domain echo 0x8 > assign_domain @@ -408,23 +436,23 @@ assign_domain APQNs are bound to the driver, the operation will terminate with an error (EADDRNOTAVAIL). - No APQN that can be derived from the domain ID being assigned and the IDs + * No APQN that can be derived from the domain ID being assigned and the IDs of the previously assigned adapters can be assigned to another mediated matrix device. If an APQN is assigned to another mediated matrix device, the operation will terminate with an error (EADDRINUSE). -unassign_domain +``unassign_domain`` To unassign a usage domain, the domain number is written into the - 'unassign_domain' file. This may be done multiple times to unassign more than + ``unassign_domain`` file. This may be done multiple times to unassign more than one usage domain. -assign_control_domain +``assign_control_domain`` To assign a control domain, the domain number is written into the - 'assign_control_domain' file. This may be done multiple times to + ``assign_control_domain`` file. This may be done multiple times to assign more than one control domain. The domain number may be specified using conventional semantics as a decimal, hexadecimal, or octal number. For example, to assign control domains 4, 8, and 71 to a mediated matrix device - in decimal, hexadecimal and octal respectively: + in decimal, hexadecimal and octal respectively:: echo 4 > assign_domain echo 0x8 > assign_domain @@ -435,33 +463,34 @@ assign_control_domain allowed by the machine model. If a control domain number higher than the maximum is specified, the operation will terminate with an error (ENODEV). -unassign_control_domain +``unassign_control_domain`` To unassign a control domain, the domain number is written into the - 'unassign_domain' file. This may be done multiple times to unassign more than + ``unassign_domain`` file. This may be done multiple times to unassign more than one control domain. Notes: No changes to the AP matrix will be allowed while a guest using the mediated matrix device is running. Attempts to assign an adapter, domain or control domain will be rejected and an error (EBUSY) returned. -Starting a Linux Guest Configured with an AP Matrix: -=================================================== +Starting a Linux Guest Configured with an AP Matrix +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + To provide a mediated matrix device for use by a guest, the following option -must be specified on the QEMU command line: +must be specified on the QEMU command line:: -device vfio_ap,sysfsdev=$path-to-mdev The sysfsdev parameter specifies the path to the mediated matrix device. -There are a number of ways to specify this path: +There are a number of ways to specify this path:: -/sys/devices/vfio_ap/matrix/$uuid -/sys/bus/mdev/devices/$uuid -/sys/bus/mdev/drivers/vfio_mdev/$uuid -/sys/devices/vfio_ap/matrix/mdev_supported_types/vfio_ap-passthrough/devices/$uuid + /sys/devices/vfio_ap/matrix/$uuid + /sys/bus/mdev/devices/$uuid + /sys/bus/mdev/drivers/vfio_mdev/$uuid + /sys/devices/vfio_ap/matrix/mdev_supported_types/vfio_ap-passthrough/devices/$uuid When the linux guest is started, the guest will open the mediated matrix device's file descriptor to get information about the mediated matrix -device. The vfio_ap device driver will update the APM, AQM, and ADM fields in +device. The ``vfio_ap`` device driver will update the APM, AQM, and ADM fields in the guest's CRYCB with the adapter, usage domain and control domains assigned via the mediated matrix device's sysfs attribute files. Programs running on the linux guest will then: @@ -486,20 +515,22 @@ facilities: The AP facilities feature indicates that AP facilities are installed on the guest. This feature will be exposed for use only if the AP facilities are installed on the host system. The feature is s390-specific and is - represented as a parameter of the -cpu option on the QEMU command line: + represented as a parameter of the -cpu option on the QEMU command line:: qemu-system-s390x -cpu $model,ap=on|off - Where: + Where: - $model is the CPU model defined for the guest (defaults to the model of - the host system if not specified). + ``$model`` + is the CPU model defined for the guest (defaults to the model of + the host system if not specified). - ap=on|off indicates whether AP facilities are installed (on) or not - (off). The default for CPU models zEC12 or newer - is ap=on. AP facilities must be installed on the guest if a - vfio-ap device (-device vfio-ap,sysfsdev=$path) is configured - for the guest, or the guest will fail to start. + ``ap=on|off`` + indicates whether AP facilities are installed (on) or not + (off). The default for CPU models zEC12 or newer + is ``ap=on``. AP facilities must be installed on the guest if a + vfio-ap device (``-device vfio-ap,sysfsdev=$path``) is configured + for the guest, or the guest will fail to start. 2. Query Configuration Information (QCI) facility @@ -507,27 +538,29 @@ facilities: configuration of the AP facilities. This facility will be available only if the QCI facility is installed on the host system. The feature is s390-specific and is represented as a parameter of the -cpu option on the - QEMU command line: + QEMU command line:: qemu-system-s390x -cpu $model,apqci=on|off - Where: + Where: - $model is the CPU model defined for the guest + ``$model`` + is the CPU model defined for the guest - apqci=on|off indicates whether the QCI facility is installed (on) or - not (off). The default for CPU models zEC12 or newer - is apqci=on; for older models, QCI will not be installed. + ``apqci=on|off`` + indicates whether the QCI facility is installed (on) or + not (off). The default for CPU models zEC12 or newer + is ``apqci=on``; for older models, QCI will not be installed. - If QCI is installed (apqci=on) but AP facilities are not - (ap=off), an error message will be logged, but the guest - will be allowed to start. It makes no sense to have QCI - installed if the AP facilities are not; this is considered - an invalid configuration. + If QCI is installed (``apqci=on``) but AP facilities are not + (``ap=off``), an error message will be logged, but the guest + will be allowed to start. It makes no sense to have QCI + installed if the AP facilities are not; this is considered + an invalid configuration. - If the QCI facility is not installed, APQNs with an APQI - greater than 15 will not be detected by the AP bus - running on the guest. + If the QCI facility is not installed, APQNs with an APQI + greater than 15 will not be detected by the AP bus + running on the guest. 3. Adjunct Process Facility Test (APFT) facility @@ -535,48 +568,51 @@ facilities: AP facilities available for a given AP queue. This facility will be available only if the APFT facility is installed on the host system. The feature is s390-specific and is represented as a parameter of the -cpu option on the - QEMU command line: + QEMU command line:: qemu-system-s390x -cpu $model,apft=on|off - Where: + Where: - $model is the CPU model defined for the guest (defaults to the model of - the host system if not specified). + ``$model`` + is the CPU model defined for the guest (defaults to the model of + the host system if not specified). - apft=on|off indicates whether the APFT facility is installed (on) or - not (off). The default for CPU models zEC12 and - newer is apft=on for older models, APFT will not be - installed. + ``apft=on|off`` + indicates whether the APFT facility is installed (on) or + not (off). The default for CPU models zEC12 and + newer is ``apft=on`` for older models, APFT will not be + installed. - If APFT is installed (apft=on) but AP facilities are not - (ap=off), an error message will be logged, but the guest - will be allowed to start. It makes no sense to have APFT - installed if the AP facilities are not; this is considered - an invalid configuration. + If APFT is installed (``apft=on``) but AP facilities are not + (``ap=off``), an error message will be logged, but the guest + will be allowed to start. It makes no sense to have APFT + installed if the AP facilities are not; this is considered + an invalid configuration. - It also makes no sense to turn APFT off because the AP bus - running on the guest will not detect CEX4 and newer devices - without it. Since only CEX4 and newer devices are supported - for guest usage, no AP devices can be made accessible to a - guest started without APFT installed. + It also makes no sense to turn APFT off because the AP bus + running on the guest will not detect CEX4 and newer devices + without it. Since only CEX4 and newer devices are supported + for guest usage, no AP devices can be made accessible to a + guest started without APFT installed. + +Hot plug a vfio-ap device into a running guest +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Hot plug a vfio-ap device into a running guest: -============================================== Only one vfio-ap device can be attached to the virtual machine's ap-bus, so a vfio-ap device can be hot plugged if and only if no vfio-ap device is attached to the bus already, whether via the QEMU command line or a prior hot plug action. -To hot plug a vfio-ap device, use the QEMU device_add command: +To hot plug a vfio-ap device, use the QEMU ``device_add`` command:: (qemu) device_add vfio-ap,sysfsdev="$path-to-mdev" - Where the '$path-to-mdev' value specifies the absolute path to a mediated - device to which AP resources to be used by the guest have been assigned. +Where the ``$path-to-mdev`` value specifies the absolute path to a mediated +device to which AP resources to be used by the guest have been assigned. Note that on Linux guests, the AP devices will be created in the -/sys/bus/ap/devices directory when the AP bus subsequently performs its periodic +``/sys/bus/ap/devices`` directory when the AP bus subsequently performs its periodic scan, so there may be a short delay before the AP devices are accessible on the guest. @@ -587,66 +623,69 @@ The command will fail if: * The CPU model features for controlling guest access to AP facilities are not enabled (see 'CPU model features' subsection in the previous section). -Hot unplug a vfio-ap device from a running guest: -================================================ +Hot unplug a vfio-ap device from a running guest +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + A vfio-ap device can be unplugged from a running KVM guest if a vfio-ap device has been attached to the virtual machine's ap-bus via the QEMU command line or a prior hot plug action. -To hot unplug a vfio-ap device, use the QEMU device_del command: +To hot unplug a vfio-ap device, use the QEMU ``device_del`` command:: (qemu) device_del vfio-ap,sysfsdev="$path-to-mdev" - Where $path-to-mdev is the same as the path specified when the vfio-ap - device was attached to the virtual machine's ap-bus. +Where ``$path-to-mdev`` is the same as the path specified when the vfio-ap +device was attached to the virtual machine's ap-bus. -On a Linux guest, the AP devices will be removed from the /sys/bus/ap/devices +On a Linux guest, the AP devices will be removed from the ``/sys/bus/ap/devices`` directory on the guest when the AP bus subsequently performs its periodic scan, so there may be a short delay before the AP devices are no longer accessible by the guest. -The command will fail if the $path-to-mdev specified on the device_del command +The command will fail if the ``$path-to-mdev`` specified on the ``device_del`` command does not match the value specified when the vfio-ap device was attached to the virtual machine's ap-bus. -Example: Configure AP Matrixes for Three Linux Guests: -===================================================== +Example: Configure AP Matrices for Three Linux Guests +----------------------------------------------------- + Let's now provide an example to illustrate how KVM guests may be given access to AP facilities. For this example, we will show how to configure three guests such that executing the lszcrypt command on the guests would look like this: -Guest1 ------- -CARD.DOMAIN TYPE MODE ------------------------------- -05 CEX5C CCA-Coproc -05.0004 CEX5C CCA-Coproc -05.00ab CEX5C CCA-Coproc -06 CEX5A Accelerator -06.0004 CEX5A Accelerator -06.00ab CEX5C CCA-Coproc +Guest1:: -Guest2 ------- -CARD.DOMAIN TYPE MODE ------------------------------- -05 CEX5A Accelerator -05.0047 CEX5A Accelerator -05.00ff CEX5A Accelerator (5,4), (5,171), (6,4), (6,171), + CARD.DOMAIN TYPE MODE + ------------------------------ + 05 CEX5C CCA-Coproc + 05.0004 CEX5C CCA-Coproc + 05.00ab CEX5C CCA-Coproc + 06 CEX5A Accelerator + 06.0004 CEX5A Accelerator + 06.00ab CEX5C CCA-Coproc -Guest3 ------- -CARD.DOMAIN TYPE MODE ------------------------------- -06 CEX5A Accelerator -06.0047 CEX5A Accelerator -06.00ff CEX5A Accelerator +Guest2:: + + CARD.DOMAIN TYPE MODE + ------------------------------ + 05 CEX5A Accelerator + 05.0047 CEX5A Accelerator + 05.00ff CEX5A Accelerator + +Guest3:: + + CARD.DOMAIN TYPE MODE + ------------------------------ + 06 CEX5A Accelerator + 06.0047 CEX5A Accelerator + 06.00ff CEX5A Accelerator These are the steps: 1. Install the vfio_ap module on the linux host. The dependency chain for the vfio_ap module is: + * iommu * s390 * zcrypt @@ -657,6 +696,7 @@ These are the steps: To build the vfio_ap module, the kernel build must be configured with the following Kconfig elements selected: + * IOMMU_SUPPORT * S390 * ZCRYPT @@ -666,21 +706,21 @@ These are the steps: * VFIO_MDEV_DEVICE * KVM - If using make menuconfig select the following to build the vfio_ap module: - -> Device Drivers - -> IOMMU Hardware Support - select S390 AP IOMMU Support - -> VFIO Non-Privileged userspace driver framework - -> Mediated device driver framework - -> VFIO driver for Mediated devices - -> I/O subsystem - -> VFIO support for AP devices + If using make menuconfig select the following to build the vfio_ap module:: + -> Device Drivers + -> IOMMU Hardware Support + select S390 AP IOMMU Support + -> VFIO Non-Privileged userspace driver framework + -> Mediated device driver framework + -> VFIO driver for Mediated devices + -> I/O subsystem + -> VFIO support for AP devices 2. Secure the AP queues to be used by the three guests so that the host can not access them. To secure the AP queues 05.0004, 05.0047, 05.00ab, 05.00ff, 06.0004, 06.0047, 06.00ab, and 06.00ff for use by the vfio_ap device driver, the corresponding APQNs must be removed from the default queue drivers pool - as follows: + as follows:: echo -5,-6 > /sys/bus/ap/apmask @@ -689,19 +729,19 @@ These are the steps: This will result in AP queues 05.0004, 05.0047, 05.00ab, 05.00ff, 06.0004, 06.0047, 06.00ab, and 06.00ff getting bound to the vfio_ap device driver. The sysfs directory for the vfio_ap device driver will now contain symbolic links - to the AP queue devices bound to it: + to the AP queue devices bound to it:: - /sys/bus/ap - ... [drivers] - ...... [vfio_ap] - ......... [05.0004] - ......... [05.0047] - ......... [05.00ab] - ......... [05.00ff] - ......... [06.0004] - ......... [06.0047] - ......... [06.00ab] - ......... [06.00ff] + /sys/bus/ap + ... [drivers] + ...... [vfio_ap] + ......... [05.0004] + ......... [05.0047] + ......... [05.00ab] + ......... [05.00ff] + ......... [06.0004] + ......... [06.0047] + ......... [06.00ab] + ......... [06.00ff] Keep in mind that only type 10 and newer adapters (i.e., CEX4 and later) can be bound to the vfio_ap device driver. The reason for this is to @@ -712,153 +752,153 @@ These are the steps: The administrator, therefore, must take care to secure only AP queues that can be bound to the vfio_ap device driver. The device type for a given AP queue device can be read from the parent card's sysfs directory. For example, - to see the hardware type of the queue 05.0004: + to see the hardware type of the queue 05.0004:: - cat /sys/bus/ap/devices/card05/hwtype + cat /sys/bus/ap/devices/card05/hwtype The hwtype must be 10 or higher (CEX4 or newer) in order to be bound to the vfio_ap device driver. 3. Create the mediated devices needed to configure the AP matrixes for the three guests and to provide an interface to the vfio_ap driver for - use by the guests: + use by the guests:: - /sys/devices/vfio_ap/matrix/ - --- [mdev_supported_types] - ------ [vfio_ap-passthrough] (passthrough mediated matrix device type) - --------- create - --------- [devices] + /sys/devices/vfio_ap/matrix/ + ... [mdev_supported_types] + ...... [vfio_ap-passthrough] (passthrough mediated matrix device type) + ......... create + ......... [devices] - To create the mediated devices for the three guests: + To create the mediated devices for the three guests:: uuidgen > create uuidgen > create uuidgen > create - or + or - echo $uuid1 > create - echo $uuid2 > create - echo $uuid3 > create + :: + + echo $uuid1 > create + echo $uuid2 > create + echo $uuid3 > create This will create three mediated devices in the [devices] subdirectory named after the UUID used to create the mediated device. We'll call them $uuid1, - $uuid2 and $uuid3 and this is the sysfs directory structure after creation: + $uuid2 and $uuid3 and this is the sysfs directory structure after creation:: - /sys/devices/vfio_ap/matrix/ - --- [mdev_supported_types] - ------ [vfio_ap-passthrough] - --------- [devices] - ------------ [$uuid1] - --------------- assign_adapter - --------------- assign_control_domain - --------------- assign_domain - --------------- matrix - --------------- unassign_adapter - --------------- unassign_control_domain - --------------- unassign_domain + /sys/devices/vfio_ap/matrix/ + ... [mdev_supported_types] + ...... [vfio_ap-passthrough] + ......... [devices] + ............ [$uuid1] + ............... assign_adapter + ............... assign_control_domain + ............... assign_domain + ............... matrix + ............... unassign_adapter + ............... unassign_control_domain + ............... unassign_domain - ------------ [$uuid2] - --------------- assign_adapter - --------------- assign_control_domain - --------------- assign_domain - --------------- matrix - --------------- unassign_adapter - ----------------unassign_control_domain - ----------------unassign_domain + ............ [$uuid2] + ............... assign_adapter + ............... assign_control_domain + ............... assign_domain + ............... matrix + ............... unassign_adapter + ............... unassign_control_domain + ............... unassign_domain - ------------ [$uuid3] - --------------- assign_adapter - --------------- assign_control_domain - --------------- assign_domain - --------------- matrix - --------------- unassign_adapter - ----------------unassign_control_domain - ----------------unassign_domain + ............ [$uuid3] + ............... assign_adapter + ............... assign_control_domain + ............... assign_domain + ............... matrix + ............... unassign_adapter + ............... unassign_control_domain + ............... unassign_domain 4. The administrator now needs to configure the matrixes for the mediated devices $uuid1 (for Guest1), $uuid2 (for Guest2) and $uuid3 (for Guest3). - This is how the matrix is configured for Guest1: + This is how the matrix is configured for Guest1:: echo 5 > assign_adapter echo 6 > assign_adapter echo 4 > assign_domain echo 0xab > assign_domain - Control domains can similarly be assigned using the assign_control_domain - sysfs file. + Control domains can similarly be assigned using the assign_control_domain + sysfs file. - If a mistake is made configuring an adapter, domain or control domain, - you can use the unassign_xxx interfaces to unassign the adapter, domain or - control domain. + If a mistake is made configuring an adapter, domain or control domain, + you can use the ``unassign_xxx`` interfaces to unassign the adapter, domain or + control domain. - To display the matrix configuration for Guest1: + To display the matrix configuration for Guest1:: cat matrix - The output will display the APQNs in the format xx.yyyy, where xx is - the adapter number and yyyy is the domain number. The output for Guest1 - will look like this: + The output will display the APQNs in the format ``xx.yyyy``, where xx is + the adapter number and yyyy is the domain number. The output for Guest1 + will look like this:: 05.0004 05.00ab 06.0004 06.00ab - This is how the matrix is configured for Guest2: + This is how the matrix is configured for Guest2:: echo 5 > assign_adapter echo 0x47 > assign_domain echo 0xff > assign_domain - This is how the matrix is configured for Guest3: + This is how the matrix is configured for Guest3:: echo 6 > assign_adapter echo 0x47 > assign_domain echo 0xff > assign_domain -5. Start Guest1: +5. Start Guest1:: - /usr/bin/qemu-system-s390x ... -cpu host,ap=on,apqci=on,apft=on \ - -device vfio-ap,sysfsdev=/sys/devices/vfio_ap/matrix/$uuid1 ... + /usr/bin/qemu-system-s390x ... -cpu host,ap=on,apqci=on,apft=on -device vfio-ap,sysfsdev=/sys/devices/vfio_ap/matrix/$uuid1 ... -7. Start Guest2: +7. Start Guest2:: - /usr/bin/qemu-system-s390x ... -cpu host,ap=on,apqci=on,apft=on \ - -device vfio-ap,sysfsdev=/sys/devices/vfio_ap/matrix/$uuid2 ... + /usr/bin/qemu-system-s390x ... -cpu host,ap=on,apqci=on,apft=on -device vfio-ap,sysfsdev=/sys/devices/vfio_ap/matrix/$uuid2 ... -7. Start Guest3: +7. Start Guest3:: - /usr/bin/qemu-system-s390x ... -cpu host,ap=on,apqci=on,apft=on \ - -device vfio-ap,sysfsdev=/sys/devices/vfio_ap/matrix/$uuid3 ... + /usr/bin/qemu-system-s390x ... -cpu host,ap=on,apqci=on,apft=on -device vfio-ap,sysfsdev=/sys/devices/vfio_ap/matrix/$uuid3 ... When the guest is shut down, the mediated matrix devices may be removed. -Using our example again, to remove the mediated matrix device $uuid1: +Using our example again, to remove the mediated matrix device $uuid1:: /sys/devices/vfio_ap/matrix/ - --- [mdev_supported_types] - ------ [vfio_ap-passthrough] - --------- [devices] - ------------ [$uuid1] - --------------- remove + ... [mdev_supported_types] + ...... [vfio_ap-passthrough] + ......... [devices] + ............ [$uuid1] + ............... remove echo 1 > remove - This will remove all of the mdev matrix device's sysfs structures including - the mdev device itself. To recreate and reconfigure the mdev matrix device, - all of the steps starting with step 3 will have to be performed again. Note - that the remove will fail if a guest using the mdev is still running. +This will remove all of the mdev matrix device's sysfs structures including +the mdev device itself. To recreate and reconfigure the mdev matrix device, +all of the steps starting with step 3 will have to be performed again. Note +that the remove will fail if a guest using the mdev is still running. - It is not necessary to remove an mdev matrix device, but one may want to - remove it if no guest will use it during the remaining lifetime of the linux - host. If the mdev matrix device is removed, one may want to also reconfigure - the pool of adapters and queues reserved for use by the default drivers. +It is not necessary to remove an mdev matrix device, but one may want to +remove it if no guest will use it during the remaining lifetime of the linux +host. If the mdev matrix device is removed, one may want to also reconfigure +the pool of adapters and queues reserved for use by the default drivers. Limitations -=========== +----------- + * The KVM/kernel interfaces do not provide a way to prevent restoring an APQN to the default drivers pool of a queue that is still assigned to a mediated device in use by a guest. It is incumbent upon the administrator to @@ -867,10 +907,10 @@ Limitations device, such as a private key configured specifically for the guest. * Dynamically assigning AP resources to or unassigning AP resources from a - mediated matrix device - see 'Configuring an AP matrix for a linux guest' + mediated matrix device - see `Configuring an AP matrix for a linux guest`_ section above - while a running guest is using it is currently not supported. * Live guest migration is not supported for guests using AP devices. If a guest is using AP devices, the vfio-ap device configured for the guest must be - unplugged before migrating the guest (see 'Hot unplug a vfio-ap device from a - running guest' section above. + unplugged before migrating the guest (see `Hot unplug a vfio-ap device from a + running guest`_ section above.) diff --git a/docs/security.texi b/docs/system/security.rst similarity index 77% rename from docs/security.texi rename to docs/system/security.rst index 0d6b30edfc..f2092c8768 100644 --- a/docs/security.texi +++ b/docs/system/security.rst @@ -1,19 +1,22 @@ -@node Security -@chapter Security +Security +======== -@section Overview +Overview +-------- This chapter explains the security requirements that QEMU is designed to meet and principles for securely deploying QEMU. -@section Security Requirements +Security Requirements +--------------------- QEMU supports many different use cases, some of which have stricter security requirements than others. The community has agreed on the overall security requirements that users may depend on. These requirements define what is considered supported from a security perspective. -@subsection Virtualization Use Case +Virtualization Use Case +''''''''''''''''''''''' The virtualization use case covers cloud and virtual private server (VPS) hosting, as well as traditional data center and desktop virtualization. These @@ -23,18 +26,17 @@ safely on the physical CPU at close-to-native speed. The following entities are untrusted, meaning that they may be buggy or malicious: -@itemize -@item Guest -@item User-facing interfaces (e.g. VNC, SPICE, WebSocket) -@item Network protocols (e.g. NBD, live migration) -@item User-supplied files (e.g. disk images, kernels, device trees) -@item Passthrough devices (e.g. PCI, USB) -@end itemize +- Guest +- User-facing interfaces (e.g. VNC, SPICE, WebSocket) +- Network protocols (e.g. NBD, live migration) +- User-supplied files (e.g. disk images, kernels, device trees) +- Passthrough devices (e.g. PCI, USB) Bugs affecting these entities are evaluated on whether they can cause damage in real-world use cases and treated as security bugs if this is the case. -@subsection Non-virtualization Use Case +Non-virtualization Use Case +''''''''''''''''''''''''''' The non-virtualization use case covers emulation using the Tiny Code Generator (TCG). In principle the TCG and device emulation code used in conjunction with @@ -47,12 +49,14 @@ Bugs affecting the non-virtualization use case are not considered security bugs at this time. Users with non-virtualization use cases must not rely on QEMU to provide guest isolation or any security guarantees. -@section Architecture +Architecture +------------ This section describes the design principles that ensure the security requirements are met. -@subsection Guest Isolation +Guest Isolation +''''''''''''''' Guest isolation is the confinement of guest code to the virtual machine. When guest code gains control of execution on the host this is called escaping the @@ -71,7 +75,8 @@ malicious guest must not gain control of other guests or access their data. Disk image files and network traffic must be protected from other guests unless explicitly shared between them by the user. -@subsection Principle of Least Privilege +Principle of Least Privilege +'''''''''''''''''''''''''''' The principle of least privilege states that each component only has access to the privileges necessary for its function. In the case of QEMU this means that @@ -84,7 +89,7 @@ the guest. Following the principle of least privilege immediately fulfills guest isolation requirements. For example, guest A only has access to its own disk image file -@code{a.img} and not guest B's disk image file @code{b.img}. +``a.img`` and not guest B's disk image file ``b.img``. In reality certain resources are inaccessible to the guest but must be available to QEMU to perform its function. For example, host system calls are @@ -95,7 +100,8 @@ New features must be designed to follow the principle of least privilege. Should this not be possible for technical reasons, the security risk must be clearly documented so users are aware of the trade-off of enabling the feature. -@subsection Isolation mechanisms +Isolation mechanisms +'''''''''''''''''''' Several isolation mechanisms are available to realize this architecture of guest isolation and the principle of least privilege. With the exception of @@ -105,46 +111,46 @@ described briefly for Linux here. The fundamental isolation mechanism is that QEMU processes must run as unprivileged users. Sometimes it seems more convenient to launch QEMU as -root to give it access to host devices (e.g. @code{/dev/net/tun}) but this poses a +root to give it access to host devices (e.g. ``/dev/net/tun``) but this poses a huge security risk. File descriptor passing can be used to give an otherwise unprivileged QEMU process access to host devices without running QEMU as root. It is also possible to launch QEMU as a non-root user and configure UNIX groups -for access to @code{/dev/kvm}, @code{/dev/net/tun}, and other device nodes. +for access to ``/dev/kvm``, ``/dev/net/tun``, and other device nodes. Some Linux distros already ship with UNIX groups for these devices by default. -@itemize -@item SELinux and AppArmor make it possible to confine processes beyond the -traditional UNIX process and file permissions model. They restrict the QEMU -process from accessing processes and files on the host system that are not -needed by QEMU. +- SELinux and AppArmor make it possible to confine processes beyond the + traditional UNIX process and file permissions model. They restrict the QEMU + process from accessing processes and files on the host system that are not + needed by QEMU. -@item Resource limits and cgroup controllers provide throughput and utilization -limits on key resources such as CPU time, memory, and I/O bandwidth. +- Resource limits and cgroup controllers provide throughput and utilization + limits on key resources such as CPU time, memory, and I/O bandwidth. -@item Linux namespaces can be used to make process, file system, and other system -resources unavailable to QEMU. A namespaced QEMU process is restricted to only -those resources that were granted to it. +- Linux namespaces can be used to make process, file system, and other system + resources unavailable to QEMU. A namespaced QEMU process is restricted to only + those resources that were granted to it. -@item Linux seccomp is available via the QEMU @option{--sandbox} option. It disables -system calls that are not needed by QEMU, thereby reducing the host kernel -attack surface. -@end itemize +- Linux seccomp is available via the QEMU ``--sandbox`` option. It disables + system calls that are not needed by QEMU, thereby reducing the host kernel + attack surface. -@section Sensitive configurations +Sensitive configurations +------------------------ There are aspects of QEMU that can have security implications which users & management applications must be aware of. -@subsection Monitor console (QMP and HMP) +Monitor console (QMP and HMP) +''''''''''''''''''''''''''''' The monitor console (whether used with QMP or HMP) provides an interface to dynamically control many aspects of QEMU's runtime operation. Many of the commands exposed will instruct QEMU to access content on the host file system and/or trigger spawning of external processes. -For example, the @code{migrate} command allows for the spawning of arbitrary +For example, the ``migrate`` command allows for the spawning of arbitrary processes for the purpose of tunnelling the migration data stream. The -@code{blockdev-add} command instructs QEMU to open arbitrary files, exposing +``blockdev-add`` command instructs QEMU to open arbitrary files, exposing their content to the guest as a virtual disk. Unless QEMU is otherwise confined using technologies such as SELinux, AppArmor, diff --git a/docs/system/target-arm.rst b/docs/system/target-arm.rst new file mode 100644 index 0000000000..324e2af1cb --- /dev/null +++ b/docs/system/target-arm.rst @@ -0,0 +1,88 @@ +.. _ARM-System-emulator: + +Arm System emulator +------------------- + +QEMU can emulate both 32-bit and 64-bit Arm CPUs. Use the +``qemu-system-aarch64`` executable to simulate a 64-bit Arm machine. +You can use either ``qemu-system-arm`` or ``qemu-system-aarch64`` +to simulate a 32-bit Arm machine: in general, command lines that +work for ``qemu-system-arm`` will behave the same when used with +``qemu-system-aarch64``. + +QEMU has generally good support for Arm guests. It has support for +nearly fifty different machines. The reason we support so many is that +Arm hardware is much more widely varying than x86 hardware. Arm CPUs +are generally built into "system-on-chip" (SoC) designs created by +many different companies with different devices, and these SoCs are +then built into machines which can vary still further even if they use +the same SoC. Even with fifty boards QEMU does not cover more than a +small fraction of the Arm hardware ecosystem. + +The situation for 64-bit Arm is fairly similar, except that we don't +implement so many different machines. + +As well as the more common "A-profile" CPUs (which have MMUs and will +run Linux) QEMU also supports "M-profile" CPUs such as the Cortex-M0, +Cortex-M4 and Cortex-M33 (which are microcontrollers used in very +embedded boards). For most boards the CPU type is fixed (matching what +the hardware has), so typically you don't need to specify the CPU type +by hand, except for special cases like the ``virt`` board. + +Choosing a board model +====================== + +For QEMU's Arm system emulation, you must specify which board +model you want to use with the ``-M`` or ``--machine`` option; +there is no default. + +Because Arm systems differ so much and in fundamental ways, typically +operating system or firmware images intended to run on one machine +will not run at all on any other. This is often surprising for new +users who are used to the x86 world where every system looks like a +standard PC. (Once the kernel has booted, most userspace software +cares much less about the detail of the hardware.) + +If you already have a system image or a kernel that works on hardware +and you want to boot with QEMU, check whether QEMU lists that machine +in its ``-machine help`` output. If it is listed, then you can probably +use that board model. If it is not listed, then unfortunately your image +will almost certainly not boot on QEMU. (You might be able to +extract the filesystem and use that with a different kernel which +boots on a system that QEMU does emulate.) + +If you don't care about reproducing the idiosyncrasies of a particular +bit of hardware, such as small amount of RAM, no PCI or other hard +disk, etc., and just want to run Linux, the best option is to use the +``virt`` board. This is a platform which doesn't correspond to any +real hardware and is designed for use in virtual machines. You'll +need to compile Linux with a suitable configuration for running on +the ``virt`` board. ``virt`` supports PCI, virtio, recent CPUs and +large amounts of RAM. It also supports 64-bit CPUs. + +Board-specific documentation +============================ + +Unfortunately many of the Arm boards QEMU supports are currently +undocumented; you can get a complete list by running +``qemu-system-aarch64 --machine help``. + +.. toctree:: + :maxdepth: 1 + + arm/integratorcp + arm/versatile + arm/realview + arm/xscale + arm/palm + arm/nseries + arm/stellaris + arm/musicpal + arm/sx1 + arm/orangepi + +Arm CPU features +================ + +.. toctree:: + arm/cpu-features diff --git a/docs/system/target-i386-desc.rst.inc b/docs/system/target-i386-desc.rst.inc new file mode 100644 index 0000000000..47a169e0ae --- /dev/null +++ b/docs/system/target-i386-desc.rst.inc @@ -0,0 +1,62 @@ +The QEMU PC System emulator simulates the following peripherals: + +- i440FX host PCI bridge and PIIX3 PCI to ISA bridge + +- Cirrus CLGD 5446 PCI VGA card or dummy VGA card with Bochs VESA + extensions (hardware level, including all non standard modes). + +- PS/2 mouse and keyboard + +- 2 PCI IDE interfaces with hard disk and CD-ROM support + +- Floppy disk + +- PCI and ISA network adapters + +- Serial ports + +- IPMI BMC, either and internal or external one + +- Creative SoundBlaster 16 sound card + +- ENSONIQ AudioPCI ES1370 sound card + +- Intel 82801AA AC97 Audio compatible sound card + +- Intel HD Audio Controller and HDA codec + +- Adlib (OPL2) - Yamaha YM3812 compatible chip + +- Gravis Ultrasound GF1 sound card + +- CS4231A compatible sound card + +- PCI UHCI, OHCI, EHCI or XHCI USB controller and a virtual USB-1.1 + hub. + +SMP is supported with up to 255 CPUs. + +QEMU uses the PC BIOS from the Seabios project and the Plex86/Bochs LGPL +VGA BIOS. + +QEMU uses YM3812 emulation by Tatsuyuki Satoh. + +QEMU uses GUS emulation (GUSEMU32 http://www.deinmeister.de/gusemu/) by +Tibor \"TS\" Schütz. + +Note that, by default, GUS shares IRQ(7) with parallel ports and so QEMU +must be told to not have parallel ports to have working GUS. + +.. parsed-literal:: + + |qemu_system_x86| dos.img -soundhw gus -parallel none + +Alternatively: + +.. parsed-literal:: + + |qemu_system_x86| dos.img -device gus,irq=5 + +Or some other unclaimed IRQ. + +CS4231A is the chip used in Windows Sound System and GUSMAX products diff --git a/docs/system/target-i386.rst b/docs/system/target-i386.rst new file mode 100644 index 0000000000..51be03d881 --- /dev/null +++ b/docs/system/target-i386.rst @@ -0,0 +1,23 @@ +.. _QEMU-PC-System-emulator: + +x86 (PC) System emulator +------------------------ + +.. _pcsys_005fdevices: + +Peripherals +~~~~~~~~~~~ + +.. include:: target-i386-desc.rst.inc + +.. include:: cpu-models-x86.rst.inc + +.. _pcsys_005freq: + +OS requirements +~~~~~~~~~~~~~~~ + +On x86_64 hosts, the default set of CPU features enabled by the KVM +accelerator require the host to be running Linux v4.5 or newer. Red Hat +Enterprise Linux 7 is also supported, since the required +functionality was backported. diff --git a/docs/system/target-m68k.rst b/docs/system/target-m68k.rst new file mode 100644 index 0000000000..d28d3b92e5 --- /dev/null +++ b/docs/system/target-m68k.rst @@ -0,0 +1,21 @@ +.. _ColdFire-System-emulator: + +ColdFire System emulator +------------------------ + +Use the executable ``qemu-system-m68k`` to simulate a ColdFire machine. +The emulator is able to boot a uClinux kernel. + +The M5208EVB emulation includes the following devices: + +- MCF5208 ColdFire V2 Microprocessor (ISA A+ with EMAC). + +- Three Two on-chip UARTs. + +- Fast Ethernet Controller (FEC) + +The AN5206 emulation includes the following devices: + +- MCF5206 ColdFire V2 Microprocessor. + +- Two on-chip UARTs. diff --git a/docs/system/target-mips.rst b/docs/system/target-mips.rst new file mode 100644 index 0000000000..2736fd0509 --- /dev/null +++ b/docs/system/target-mips.rst @@ -0,0 +1,120 @@ +.. _MIPS-System-emulator: + +MIPS System emulator +-------------------- + +Four executables cover simulation of 32 and 64-bit MIPS systems in both +endian options, ``qemu-system-mips``, ``qemu-system-mipsel`` +``qemu-system-mips64`` and ``qemu-system-mips64el``. Five different +machine types are emulated: + +- A generic ISA PC-like machine \"mips\" + +- The MIPS Malta prototype board \"malta\" + +- An ACER Pica \"pica61\". This machine needs the 64-bit emulator. + +- MIPS emulator pseudo board \"mipssim\" + +- A MIPS Magnum R4000 machine \"magnum\". This machine needs the + 64-bit emulator. + +The generic emulation is supported by Debian 'Etch' and is able to +install Debian into a virtual disk image. The following devices are +emulated: + +- A range of MIPS CPUs, default is the 24Kf + +- PC style serial port + +- PC style IDE disk + +- NE2000 network card + +The Malta emulation supports the following devices: + +- Core board with MIPS 24Kf CPU and Galileo system controller + +- PIIX4 PCI/USB/SMbus controller + +- The Multi-I/O chip's serial device + +- PCI network cards (PCnet32 and others) + +- Malta FPGA serial device + +- Cirrus (default) or any other PCI VGA graphics card + +The Boston board emulation supports the following devices: + +- Xilinx FPGA, which includes a PCIe root port and an UART + +- Intel EG20T PCH connects the I/O peripherals, but only the SATA bus + is emulated + +The ACER Pica emulation supports: + +- MIPS R4000 CPU + +- PC-style IRQ and DMA controllers + +- PC Keyboard + +- IDE controller + +The MIPS Magnum R4000 emulation supports: + +- MIPS R4000 CPU + +- PC-style IRQ controller + +- PC Keyboard + +- SCSI controller + +- G364 framebuffer + +The Fulong 2E emulation supports: + +- Loongson 2E CPU + +- Bonito64 system controller as North Bridge + +- VT82C686 chipset as South Bridge + +- RTL8139D as a network card chipset + +The mipssim pseudo board emulation provides an environment similar to +what the proprietary MIPS emulator uses for running Linux. It supports: + +- A range of MIPS CPUs, default is the 24Kf + +- PC style serial port + +- MIPSnet network emulation + +.. include:: cpu-models-mips.rst.inc + +.. _nanoMIPS-System-emulator: + +nanoMIPS System emulator +~~~~~~~~~~~~~~~~~~~~~~~~ + +Executable ``qemu-system-mipsel`` also covers simulation of 32-bit +nanoMIPS system in little endian mode: + +- nanoMIPS I7200 CPU + +Example of ``qemu-system-mipsel`` usage for nanoMIPS is shown below: + +Download ```` from +https://mipsdistros.mips.com/LinuxDistro/nanomips/buildroot/index.html. + +Download ```` from +https://mipsdistros.mips.com/LinuxDistro/nanomips/kernels/v4.15.18-432-gb2eb9a8b07a1-20180627102142/index.html. + +Start system emulation of Malta board with nanoMIPS I7200 CPU:: + + qemu-system-mipsel -cpu I7200 -kernel \ + -M malta -serial stdio -m -hda \ + -append "mem=256m@0x0 rw console=ttyS0 vga=cirrus vesa=0x111 root=/dev/sda" diff --git a/docs/system/target-ppc.rst b/docs/system/target-ppc.rst new file mode 100644 index 0000000000..a2f04c533c --- /dev/null +++ b/docs/system/target-ppc.rst @@ -0,0 +1,47 @@ +.. _PowerPC-System-emulator: + +PowerPC System emulator +----------------------- + +Use the executable ``qemu-system-ppc`` to simulate a complete 40P (PREP) +or PowerMac PowerPC system. + +QEMU emulates the following PowerMac peripherals: + +- UniNorth or Grackle PCI Bridge + +- PCI VGA compatible card with VESA Bochs Extensions + +- 2 PMAC IDE interfaces with hard disk and CD-ROM support + +- NE2000 PCI adapters + +- Non Volatile RAM + +- VIA-CUDA with ADB keyboard and mouse. + +QEMU emulates the following 40P (PREP) peripherals: + +- PCI Bridge + +- PCI VGA compatible card with VESA Bochs Extensions + +- 2 IDE interfaces with hard disk and CD-ROM support + +- Floppy disk + +- PCnet network adapters + +- Serial port + +- PREP Non Volatile RAM + +- PC compatible keyboard and mouse. + +Since version 0.9.1, QEMU uses OpenBIOS https://www.openbios.org/ for +the g3beige and mac99 PowerMac and the 40p machines. OpenBIOS is a free +(GPL v2) portable firmware implementation. The goal is to implement a +100% IEEE 1275-1994 (referred to as Open Firmware) compliant firmware. + +More information is available at +http://perso.magic.fr/l_indien/qemu-ppc/. diff --git a/docs/system/target-s390x.rst b/docs/system/target-s390x.rst new file mode 100644 index 0000000000..4c8b7cdd66 --- /dev/null +++ b/docs/system/target-s390x.rst @@ -0,0 +1,26 @@ +.. _s390x-System-emulator: + +s390x System emulator +--------------------- + +QEMU can emulate z/Architecture (in particular, 64 bit) s390x systems +via the ``qemu-system-s390x`` binary. Only one machine type, +``s390-ccw-virtio``, is supported (with versioning for compatibility +handling). + +When using KVM as accelerator, QEMU can emulate CPUs up to the generation +of the host. When using the default cpu model with TCG as accelerator, +QEMU will emulate a subset of z13 cpu features that should be enough to run +distributions built for the z13. + +Device support +============== + +QEMU will not emulate most of the traditional devices found under LPAR or +z/VM; virtio devices (especially using virtio-ccw) make up the bulk of +the available devices. Passthrough of host devices via vfio-pci, vfio-ccw, +or vfio-ap is also available. + +.. toctree:: + s390x/vfio-ap + diff --git a/docs/system/target-sparc.rst b/docs/system/target-sparc.rst new file mode 100644 index 0000000000..b55f8d09e9 --- /dev/null +++ b/docs/system/target-sparc.rst @@ -0,0 +1,62 @@ +.. _Sparc32-System-emulator: + +Sparc32 System emulator +----------------------- + +Use the executable ``qemu-system-sparc`` to simulate the following Sun4m +architecture machines: + +- SPARCstation 4 + +- SPARCstation 5 + +- SPARCstation 10 + +- SPARCstation 20 + +- SPARCserver 600MP + +- SPARCstation LX + +- SPARCstation Voyager + +- SPARCclassic + +- SPARCbook + +The emulation is somewhat complete. SMP up to 16 CPUs is supported, but +Linux limits the number of usable CPUs to 4. + +QEMU emulates the following sun4m peripherals: + +- IOMMU + +- TCX or cgthree Frame buffer + +- Lance (Am7990) Ethernet + +- Non Volatile RAM M48T02/M48T08 + +- Slave I/O: timers, interrupt controllers, Zilog serial ports, + keyboard and power/reset logic + +- ESP SCSI controller with hard disk and CD-ROM support + +- Floppy drive (not on SS-600MP) + +- CS4231 sound device (only on SS-5, not working yet) + +The number of peripherals is fixed in the architecture. Maximum memory +size depends on the machine type, for SS-5 it is 256MB and for others +2047MB. + +Since version 0.8.2, QEMU uses OpenBIOS https://www.openbios.org/. +OpenBIOS is a free (GPL v2) portable firmware implementation. The goal +is to implement a 100% IEEE 1275-1994 (referred to as Open Firmware) +compliant firmware. + +A sample Linux 2.6 series kernel and ram disk image are available on the +QEMU web site. There are still issues with NetBSD and OpenBSD, but most +kernel versions work. Please note that currently older Solaris kernels +don't work probably due to interface issues between OpenBIOS and +Solaris. diff --git a/docs/system/target-sparc64.rst b/docs/system/target-sparc64.rst new file mode 100644 index 0000000000..97e334b930 --- /dev/null +++ b/docs/system/target-sparc64.rst @@ -0,0 +1,37 @@ +.. _Sparc64-System-emulator: + +Sparc64 System emulator +----------------------- + +Use the executable ``qemu-system-sparc64`` to simulate a Sun4u +(UltraSPARC PC-like machine), Sun4v (T1 PC-like machine), or generic +Niagara (T1) machine. The Sun4u emulator is mostly complete, being able +to run Linux, NetBSD and OpenBSD in headless (-nographic) mode. The +Sun4v emulator is still a work in progress. + +The Niagara T1 emulator makes use of firmware and OS binaries supplied +in the S10image/ directory of the OpenSPARC T1 project +http://download.oracle.com/technetwork/systems/opensparc/OpenSPARCT1_Arch.1.5.tar.bz2 +and is able to boot the disk.s10hw2 Solaris image. + +:: + + qemu-system-sparc64 -M niagara -L /path-to/S10image/ \ + -nographic -m 256 \ + -drive if=pflash,readonly=on,file=/S10image/disk.s10hw2 + +QEMU emulates the following peripherals: + +- UltraSparc IIi APB PCI Bridge + +- PCI VGA compatible card with VESA Bochs Extensions + +- PS/2 mouse and keyboard + +- Non Volatile RAM M48T59 + +- PC-compatible serial ports + +- 2 PCI IDE interfaces with hard disk and CD-ROM support + +- Floppy disk diff --git a/docs/system/target-xtensa.rst b/docs/system/target-xtensa.rst new file mode 100644 index 0000000000..8d703ad769 --- /dev/null +++ b/docs/system/target-xtensa.rst @@ -0,0 +1,27 @@ +.. _Xtensa-System-emulator: + +Xtensa System emulator +---------------------- + +Two executables cover simulation of both Xtensa endian options, +``qemu-system-xtensa`` and ``qemu-system-xtensaeb``. Two different +machine types are emulated: + +- Xtensa emulator pseudo board \"sim\" + +- Avnet LX60/LX110/LX200 board + +The sim pseudo board emulation provides an environment similar to one +provided by the proprietary Tensilica ISS. It supports: + +- A range of Xtensa CPUs, default is the DC232B + +- Console and filesystem access via semihosting calls + +The Avnet LX60/LX110/LX200 emulation supports: + +- A range of Xtensa CPUs, default is the DC232B + +- 16550 UART + +- OpenCores 10/100 Mbps Ethernet MAC diff --git a/docs/system/targets.rst b/docs/system/targets.rst new file mode 100644 index 0000000000..0d8f91580a --- /dev/null +++ b/docs/system/targets.rst @@ -0,0 +1,20 @@ +QEMU System Emulator Targets +============================ + +QEMU is a generic emulator and it emulates many machines. Most of the +options are similar for all machines. Specific information about the +various targets are mentioned in the following sections. + +Contents: + +.. toctree:: + + target-i386 + target-ppc + target-sparc + target-sparc64 + target-mips + target-arm + target-m68k + target-xtensa + target-s390x diff --git a/docs/system/tls.rst b/docs/system/tls.rst new file mode 100644 index 0000000000..dc2b94257f --- /dev/null +++ b/docs/system/tls.rst @@ -0,0 +1,328 @@ +.. _network_005ftls: + +TLS setup for network services +------------------------------ + +Almost all network services in QEMU have the ability to use TLS for +session data encryption, along with x509 certificates for simple client +authentication. What follows is a description of how to generate +certificates suitable for usage with QEMU, and applies to the VNC +server, character devices with the TCP backend, NBD server and client, +and migration server and client. + +At a high level, QEMU requires certificates and private keys to be +provided in PEM format. Aside from the core fields, the certificates +should include various extension data sets, including v3 basic +constraints data, key purpose, key usage and subject alt name. + +The GnuTLS package includes a command called ``certtool`` which can be +used to easily generate certificates and keys in the required format +with expected data present. Alternatively a certificate management +service may be used. + +At a minimum it is necessary to setup a certificate authority, and issue +certificates to each server. If using x509 certificates for +authentication, then each client will also need to be issued a +certificate. + +Assuming that the QEMU network services will only ever be exposed to +clients on a private intranet, there is no need to use a commercial +certificate authority to create certificates. A self-signed CA is +sufficient, and in fact likely to be more secure since it removes the +ability of malicious 3rd parties to trick the CA into mis-issuing certs +for impersonating your services. The only likely exception where a +commercial CA might be desirable is if enabling the VNC websockets +server and exposing it directly to remote browser clients. In such a +case it might be useful to use a commercial CA to avoid needing to +install custom CA certs in the web browsers. + +The recommendation is for the server to keep its certificates in either +``/etc/pki/qemu`` or for unprivileged users in ``$HOME/.pki/qemu``. + +.. _tls_005fgenerate_005fca: + +Setup the Certificate Authority +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This step only needs to be performed once per organization / +organizational unit. First the CA needs a private key. This key must be +kept VERY secret and secure. If this key is compromised the entire trust +chain of the certificates issued with it is lost. + +:: + + # certtool --generate-privkey > ca-key.pem + +To generate a self-signed certificate requires one core piece of +information, the name of the organization. A template file ``ca.info`` +should be populated with the desired data to avoid having to deal with +interactive prompts from certtool:: + + # cat > ca.info < server-hostNNN.info < server-hostNNN-key.pem + # certtool --generate-certificate \ + --load-ca-certificate ca-cert.pem \ + --load-ca-privkey ca-key.pem \ + --load-privkey server-hostNNN-key.pem \ + --template server-hostNNN.info \ + --outfile server-hostNNN-cert.pem + +The ``dns_name`` and ``ip_address`` fields in the template are setting +the subject alt name extension data. The ``tls_www_server`` keyword is +the key purpose extension to indicate this certificate is intended for +usage in a web server. Although QEMU network services are not in fact +HTTP servers (except for VNC websockets), setting this key purpose is +still recommended. The ``encryption_key`` and ``signing_key`` keyword is +the key usage extension to indicate this certificate is intended for +usage in the data session. + +The ``server-hostNNN-key.pem`` and ``server-hostNNN-cert.pem`` files +should now be securely copied to the server for which they were +generated, and renamed to ``server-key.pem`` and ``server-cert.pem`` +when added to the ``/etc/pki/qemu`` directory on the target host. The +``server-key.pem`` file is security sensitive and should be kept +protected with file mode 0600 to prevent disclosure. + +.. _tls_005fgenerate_005fclient: + +Issuing client certificates +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The QEMU x509 TLS credential setup defaults to enabling client +verification using certificates, providing a simple authentication +mechanism. If this default is used, each client also needs to be issued +a certificate. The client certificate contains enough metadata to +uniquely identify the client with the scope of the certificate +authority. The client certificate would typically include fields for +organization, state, city, building, etc. + +Once again on the host holding the CA, create template files containing +the information for each client, and use it to issue client +certificates. + +:: + + # cat > client-hostNNN.info < client-hostNNN-key.pem + # certtool --generate-certificate \ + --load-ca-certificate ca-cert.pem \ + --load-ca-privkey ca-key.pem \ + --load-privkey client-hostNNN-key.pem \ + --template client-hostNNN.info \ + --outfile client-hostNNN-cert.pem + +The subject alt name extension data is not required for clients, so the +the ``dns_name`` and ``ip_address`` fields are not included. The +``tls_www_client`` keyword is the key purpose extension to indicate this +certificate is intended for usage in a web client. Although QEMU network +clients are not in fact HTTP clients, setting this key purpose is still +recommended. The ``encryption_key`` and ``signing_key`` keyword is the +key usage extension to indicate this certificate is intended for usage +in the data session. + +The ``client-hostNNN-key.pem`` and ``client-hostNNN-cert.pem`` files +should now be securely copied to the client for which they were +generated, and renamed to ``client-key.pem`` and ``client-cert.pem`` +when added to the ``/etc/pki/qemu`` directory on the target host. The +``client-key.pem`` file is security sensitive and should be kept +protected with file mode 0600 to prevent disclosure. + +If a single host is going to be using TLS in both a client and server +role, it is possible to create a single certificate to cover both roles. +This would be quite common for the migration and NBD services, where a +QEMU process will be started by accepting a TLS protected incoming +migration, and later itself be migrated out to another host. To generate +a single certificate, simply include the template data from both the +client and server instructions in one. + +:: + + # cat > both-hostNNN.info < both-hostNNN-key.pem + # certtool --generate-certificate \ + --load-ca-certificate ca-cert.pem \ + --load-ca-privkey ca-key.pem \ + --load-privkey both-hostNNN-key.pem \ + --template both-hostNNN.info \ + --outfile both-hostNNN-cert.pem + +When copying the PEM files to the target host, save them twice, once as +``server-cert.pem`` and ``server-key.pem``, and again as +``client-cert.pem`` and ``client-key.pem``. + +.. _tls_005fcreds_005fsetup: + +TLS x509 credential configuration +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +QEMU has a standard mechanism for loading x509 credentials that will be +used for network services and clients. It requires specifying the +``tls-creds-x509`` class name to the ``--object`` command line argument +for the system emulators. Each set of credentials loaded should be given +a unique string identifier via the ``id`` parameter. A single set of TLS +credentials can be used for multiple network backends, so VNC, +migration, NBD, character devices can all share the same credentials. +Note, however, that credentials for use in a client endpoint must be +loaded separately from those used in a server endpoint. + +When specifying the object, the ``dir`` parameters specifies which +directory contains the credential files. This directory is expected to +contain files with the names mentioned previously, ``ca-cert.pem``, +``server-key.pem``, ``server-cert.pem``, ``client-key.pem`` and +``client-cert.pem`` as appropriate. It is also possible to include a set +of pre-generated Diffie-Hellman (DH) parameters in a file +``dh-params.pem``, which can be created using the +``certtool --generate-dh-params`` command. If omitted, QEMU will +dynamically generate DH parameters when loading the credentials. + +The ``endpoint`` parameter indicates whether the credentials will be +used for a network client or server, and determines which PEM files are +loaded. + +The ``verify`` parameter determines whether x509 certificate validation +should be performed. This defaults to enabled, meaning clients will +always validate the server hostname against the certificate subject alt +name fields and/or CN field. It also means that servers will request +that clients provide a certificate and validate them. Verification +should never be turned off for client endpoints, however, it may be +turned off for server endpoints if an alternative mechanism is used to +authenticate clients. For example, the VNC server can use SASL to +authenticate clients instead. + +To load server credentials with client certificate validation enabled + +.. parsed-literal:: + + |qemu_system| -object tls-creds-x509,id=tls0,dir=/etc/pki/qemu,endpoint=server + +while to load client credentials use + +.. parsed-literal:: + + |qemu_system| -object tls-creds-x509,id=tls0,dir=/etc/pki/qemu,endpoint=client + +Network services which support TLS will all have a ``tls-creds`` +parameter which expects the ID of the TLS credentials object. For +example with VNC: + +.. parsed-literal:: + + |qemu_system| -vnc 0.0.0.0:0,tls-creds=tls0 + +.. _tls_005fpsk: + +TLS Pre-Shared Keys (PSK) +~~~~~~~~~~~~~~~~~~~~~~~~~ + +Instead of using certificates, you may also use TLS Pre-Shared Keys +(TLS-PSK). This can be simpler to set up than certificates but is less +scalable. + +Use the GnuTLS ``psktool`` program to generate a ``keys.psk`` file +containing one or more usernames and random keys:: + + mkdir -m 0700 /tmp/keys + psktool -u rich -p /tmp/keys/keys.psk + +TLS-enabled servers such as qemu-nbd can use this directory like so:: + + qemu-nbd \ + -t -x / \ + --object tls-creds-psk,id=tls0,endpoint=server,dir=/tmp/keys \ + --tls-creds tls0 \ + image.qcow2 + +When connecting from a qemu-based client you must specify the directory +containing ``keys.psk`` and an optional username (defaults to "qemu"):: + + qemu-img info \ + --object tls-creds-psk,id=tls0,dir=/tmp/keys,username=rich,endpoint=client \ + --image-opts \ + file.driver=nbd,file.host=localhost,file.port=10809,file.tls-creds=tls0,file.export=/ diff --git a/docs/system/usb.rst b/docs/system/usb.rst new file mode 100644 index 0000000000..ddfa828d74 --- /dev/null +++ b/docs/system/usb.rst @@ -0,0 +1,137 @@ +.. _pcsys_005fusb: + +USB emulation +------------- + +QEMU can emulate a PCI UHCI, OHCI, EHCI or XHCI USB controller. You can +plug virtual USB devices or real host USB devices (only works with +certain host operating systems). QEMU will automatically create and +connect virtual USB hubs as necessary to connect multiple USB devices. + +.. _usb_005fdevices: + +Connecting USB devices +~~~~~~~~~~~~~~~~~~~~~~ + +USB devices can be connected with the ``-device usb-...`` command line +option or the ``device_add`` monitor command. Available devices are: + +``usb-mouse`` + Virtual Mouse. This will override the PS/2 mouse emulation when + activated. + +``usb-tablet`` + Pointer device that uses absolute coordinates (like a touchscreen). + This means QEMU is able to report the mouse position without having + to grab the mouse. Also overrides the PS/2 mouse emulation when + activated. + +``usb-storage,drive=drive_id`` + Mass storage device backed by drive_id (see + :ref:`disk_005fimages`) + +``usb-uas`` + USB attached SCSI device, see + `usb-storage.txt `__ + for details + +``usb-bot`` + Bulk-only transport storage device, see + `usb-storage.txt `__ + for details here, too + +``usb-mtp,rootdir=dir`` + Media transfer protocol device, using dir as root of the file tree + that is presented to the guest. + +``usb-host,hostbus=bus,hostaddr=addr`` + Pass through the host device identified by bus and addr + +``usb-host,vendorid=vendor,productid=product`` + Pass through the host device identified by vendor and product ID + +``usb-wacom-tablet`` + Virtual Wacom PenPartner tablet. This device is similar to the + ``tablet`` above but it can be used with the tslib library because in + addition to touch coordinates it reports touch pressure. + +``usb-kbd`` + Standard USB keyboard. Will override the PS/2 keyboard (if present). + +``usb-serial,chardev=id`` + Serial converter. This emulates an FTDI FT232BM chip connected to + host character device id. + +``usb-braille,chardev=id`` + Braille device. This will use BrlAPI to display the braille output on + a real or fake device referenced by id. + +``usb-net[,netdev=id]`` + Network adapter that supports CDC ethernet and RNDIS protocols. id + specifies a netdev defined with ``-netdev …,id=id``. For instance, + user-mode networking can be used with + + .. parsed-literal:: + + |qemu_system| [...] -netdev user,id=net0 -device usb-net,netdev=net0 + +``usb-ccid`` + Smartcard reader device + +``usb-audio`` + USB audio device + +.. _host_005fusb_005fdevices: + +Using host USB devices on a Linux host +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +WARNING: this is an experimental feature. QEMU will slow down when using +it. USB devices requiring real time streaming (i.e. USB Video Cameras) +are not supported yet. + +1. If you use an early Linux 2.4 kernel, verify that no Linux driver is + actually using the USB device. A simple way to do that is simply to + disable the corresponding kernel module by renaming it from + ``mydriver.o`` to ``mydriver.o.disabled``. + +2. Verify that ``/proc/bus/usb`` is working (most Linux distributions + should enable it by default). You should see something like that: + + :: + + ls /proc/bus/usb + 001 devices drivers + +3. Since only root can access to the USB devices directly, you can + either launch QEMU as root or change the permissions of the USB + devices you want to use. For testing, the following suffices: + + :: + + chown -R myuid /proc/bus/usb + +4. Launch QEMU and do in the monitor: + + :: + + info usbhost + Device 1.2, speed 480 Mb/s + Class 00: USB device 1234:5678, USB DISK + + You should see the list of the devices you can use (Never try to use + hubs, it won't work). + +5. Add the device in QEMU by using: + + :: + + device_add usb-host,vendorid=0x1234,productid=0x5678 + + Normally the guest OS should report that a new USB device is plugged. + You can use the option ``-device usb-host,...`` to do the same. + +6. Now you can try to use the host USB device in QEMU. + +When relaunching QEMU, you may have to unplug and plug again the USB +device to make it work again (this is a bug). diff --git a/docs/system/vnc-security.rst b/docs/system/vnc-security.rst new file mode 100644 index 0000000000..b237b07330 --- /dev/null +++ b/docs/system/vnc-security.rst @@ -0,0 +1,202 @@ +.. _vnc_005fsecurity: + +VNC security +------------ + +The VNC server capability provides access to the graphical console of +the guest VM across the network. This has a number of security +considerations depending on the deployment scenarios. + +.. _vnc_005fsec_005fnone: + +Without passwords +~~~~~~~~~~~~~~~~~ + +The simplest VNC server setup does not include any form of +authentication. For this setup it is recommended to restrict it to +listen on a UNIX domain socket only. For example + +.. parsed-literal:: + + |qemu_system| [...OPTIONS...] -vnc unix:/home/joebloggs/.qemu-myvm-vnc + +This ensures that only users on local box with read/write access to that +path can access the VNC server. To securely access the VNC server from a +remote machine, a combination of netcat+ssh can be used to provide a +secure tunnel. + +.. _vnc_005fsec_005fpassword: + +With passwords +~~~~~~~~~~~~~~ + +The VNC protocol has limited support for password based authentication. +Since the protocol limits passwords to 8 characters it should not be +considered to provide high security. The password can be fairly easily +brute-forced by a client making repeat connections. For this reason, a +VNC server using password authentication should be restricted to only +listen on the loopback interface or UNIX domain sockets. Password +authentication is not supported when operating in FIPS 140-2 compliance +mode as it requires the use of the DES cipher. Password authentication +is requested with the ``password`` option, and then once QEMU is running +the password is set with the monitor. Until the monitor is used to set +the password all clients will be rejected. + +.. parsed-literal:: + + |qemu_system| [...OPTIONS...] -vnc :1,password -monitor stdio + (qemu) change vnc password + Password: ******** + (qemu) + +.. _vnc_005fsec_005fcertificate: + +With x509 certificates +~~~~~~~~~~~~~~~~~~~~~~ + +The QEMU VNC server also implements the VeNCrypt extension allowing use +of TLS for encryption of the session, and x509 certificates for +authentication. The use of x509 certificates is strongly recommended, +because TLS on its own is susceptible to man-in-the-middle attacks. +Basic x509 certificate support provides a secure session, but no +authentication. This allows any client to connect, and provides an +encrypted session. + +.. parsed-literal:: + + |qemu_system| [...OPTIONS...] \ + -object tls-creds-x509,id=tls0,dir=/etc/pki/qemu,endpoint=server,verify-peer=no \ + -vnc :1,tls-creds=tls0 -monitor stdio + +In the above example ``/etc/pki/qemu`` should contain at least three +files, ``ca-cert.pem``, ``server-cert.pem`` and ``server-key.pem``. +Unprivileged users will want to use a private directory, for example +``$HOME/.pki/qemu``. NB the ``server-key.pem`` file should be protected +with file mode 0600 to only be readable by the user owning it. + +.. _vnc_005fsec_005fcertificate_005fverify: + +With x509 certificates and client verification +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Certificates can also provide a means to authenticate the client +connecting. The server will request that the client provide a +certificate, which it will then validate against the CA certificate. +This is a good choice if deploying in an environment with a private +internal certificate authority. It uses the same syntax as previously, +but with ``verify-peer`` set to ``yes`` instead. + +.. parsed-literal:: + + |qemu_system| [...OPTIONS...] \ + -object tls-creds-x509,id=tls0,dir=/etc/pki/qemu,endpoint=server,verify-peer=yes \ + -vnc :1,tls-creds=tls0 -monitor stdio + +.. _vnc_005fsec_005fcertificate_005fpw: + +With x509 certificates, client verification and passwords +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Finally, the previous method can be combined with VNC password +authentication to provide two layers of authentication for clients. + +.. parsed-literal:: + + |qemu_system| [...OPTIONS...] \ + -object tls-creds-x509,id=tls0,dir=/etc/pki/qemu,endpoint=server,verify-peer=yes \ + -vnc :1,tls-creds=tls0,password -monitor stdio + (qemu) change vnc password + Password: ******** + (qemu) + +.. _vnc_005fsec_005fsasl: + +With SASL authentication +~~~~~~~~~~~~~~~~~~~~~~~~ + +The SASL authentication method is a VNC extension, that provides an +easily extendable, pluggable authentication method. This allows for +integration with a wide range of authentication mechanisms, such as PAM, +GSSAPI/Kerberos, LDAP, SQL databases, one-time keys and more. The +strength of the authentication depends on the exact mechanism +configured. If the chosen mechanism also provides a SSF layer, then it +will encrypt the datastream as well. + +Refer to the later docs on how to choose the exact SASL mechanism used +for authentication, but assuming use of one supporting SSF, then QEMU +can be launched with: + +.. parsed-literal:: + + |qemu_system| [...OPTIONS...] -vnc :1,sasl -monitor stdio + +.. _vnc_005fsec_005fcertificate_005fsasl: + +With x509 certificates and SASL authentication +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If the desired SASL authentication mechanism does not supported SSF +layers, then it is strongly advised to run it in combination with TLS +and x509 certificates. This provides securely encrypted data stream, +avoiding risk of compromising of the security credentials. This can be +enabled, by combining the 'sasl' option with the aforementioned TLS + +x509 options: + +.. parsed-literal:: + + |qemu_system| [...OPTIONS...] \ + -object tls-creds-x509,id=tls0,dir=/etc/pki/qemu,endpoint=server,verify-peer=yes \ + -vnc :1,tls-creds=tls0,sasl -monitor stdio + +.. _vnc_005fsetup_005fsasl: + +Configuring SASL mechanisms +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The following documentation assumes use of the Cyrus SASL implementation +on a Linux host, but the principles should apply to any other SASL +implementation or host. When SASL is enabled, the mechanism +configuration will be loaded from system default SASL service config +/etc/sasl2/qemu.conf. If running QEMU as an unprivileged user, an +environment variable SASL_CONF_PATH can be used to make it search +alternate locations for the service config file. + +If the TLS option is enabled for VNC, then it will provide session +encryption, otherwise the SASL mechanism will have to provide +encryption. In the latter case the list of possible plugins that can be +used is drastically reduced. In fact only the GSSAPI SASL mechanism +provides an acceptable level of security by modern standards. Previous +versions of QEMU referred to the DIGEST-MD5 mechanism, however, it has +multiple serious flaws described in detail in RFC 6331 and thus should +never be used any more. The SCRAM-SHA-1 mechanism provides a simple +username/password auth facility similar to DIGEST-MD5, but does not +support session encryption, so can only be used in combination with TLS. + +When not using TLS the recommended configuration is + +:: + + mech_list: gssapi + keytab: /etc/qemu/krb5.tab + +This says to use the 'GSSAPI' mechanism with the Kerberos v5 protocol, +with the server principal stored in /etc/qemu/krb5.tab. For this to work +the administrator of your KDC must generate a Kerberos principal for the +server, with a name of 'qemu/somehost.example.com@EXAMPLE.COM' replacing +'somehost.example.com' with the fully qualified host name of the machine +running QEMU, and 'EXAMPLE.COM' with the Kerberos Realm. + +When using TLS, if username+password authentication is desired, then a +reasonable configuration is + +:: + + mech_list: scram-sha-1 + sasldb_path: /etc/qemu/passwd.db + +The ``saslpasswd2`` program can be used to populate the ``passwd.db`` +file with accounts. + +Other SASL configurations will be left as an exercise for the reader. +Note that all mechanisms, except GSSAPI, should be combined with use of +TLS to ensure a secure data channel. diff --git a/docs/tools/conf.py b/docs/tools/conf.py new file mode 100644 index 0000000000..9052d17d6d --- /dev/null +++ b/docs/tools/conf.py @@ -0,0 +1,33 @@ +# -*- coding: utf-8 -*- +# +# QEMU documentation build configuration file for the 'tools' manual. +# +# This includes the top level conf file and then makes any necessary tweaks. +import sys +import os + +qemu_docdir = os.path.abspath("..") +parent_config = os.path.join(qemu_docdir, "conf.py") +exec(compile(open(parent_config, "rb").read(), parent_config, 'exec')) + +# This slightly misuses the 'description', but is the best way to get +# the manual title to appear in the sidebar. +html_theme_options['description'] = \ + u'Tools Guide' + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + ('qemu-img', 'qemu-img', u'QEMU disk image utility', + ['Fabrice Bellard'], 1), + ('qemu-nbd', 'qemu-nbd', u'QEMU Disk Network Block Device Server', + ['Anthony Liguori '], 8), + ('qemu-trace-stap', 'qemu-trace-stap', u'QEMU SystemTap trace tool', + [], 1), + ('virtfs-proxy-helper', 'virtfs-proxy-helper', + u'QEMU 9p virtfs proxy filesystem helper', + ['M. Mohan Kumar'], 1), + ('virtiofsd', 'virtiofsd', u'QEMU virtio-fs shared file system daemon', + ['Stefan Hajnoczi ', + 'Masayoshi Mizuma '], 1), +] diff --git a/docs/tools/index.rst b/docs/tools/index.rst new file mode 100644 index 0000000000..232ce9f3e4 --- /dev/null +++ b/docs/tools/index.rst @@ -0,0 +1,17 @@ +.. This is the top level page for the 'tools' manual + + +QEMU Tools Guide +================ + + +Contents: + +.. toctree:: + :maxdepth: 2 + + qemu-img + qemu-nbd + qemu-trace-stap + virtfs-proxy-helper + virtiofsd diff --git a/docs/tools/qemu-img.rst b/docs/tools/qemu-img.rst new file mode 100644 index 0000000000..0080f83a76 --- /dev/null +++ b/docs/tools/qemu-img.rst @@ -0,0 +1,832 @@ +QEMU disk image utility +======================= + +Synopsis +-------- + +**qemu-img** [*standard options*] *command* [*command options*] + +Description +----------- + +qemu-img allows you to create, convert and modify images offline. It can handle +all image formats supported by QEMU. + +**Warning:** Never use qemu-img to modify images in use by a running virtual +machine or any other process; this may destroy the image. Also, be aware that +querying an image that is being modified by another process may encounter +inconsistent state. + +Options +------- + +.. program:: qemu-img + +Standard options: + +.. option:: -h, --help + + Display this help and exit + +.. option:: -V, --version + + Display version information and exit + +.. option:: -T, --trace [[enable=]PATTERN][,events=FILE][,file=FILE] + + .. include:: ../qemu-option-trace.rst.inc + +The following commands are supported: + +.. hxtool-doc:: qemu-img-cmds.hx + +Command parameters: + +*FILENAME* is a disk image filename. + +*FMT* is the disk image format. It is guessed automatically in most +cases. See below for a description of the supported disk formats. + +*SIZE* is the disk image size in bytes. Optional suffixes ``k`` or +``K`` (kilobyte, 1024) ``M`` (megabyte, 1024k) and ``G`` (gigabyte, +1024M) and T (terabyte, 1024G) are supported. ``b`` is ignored. + +*OUTPUT_FILENAME* is the destination disk image filename. + +*OUTPUT_FMT* is the destination format. + +*OPTIONS* is a comma separated list of format specific options in a +name=value format. Use ``-o ?`` for an overview of the options supported +by the used format or see the format descriptions below for details. + +*SNAPSHOT_PARAM* is param used for internal snapshot, format is +'snapshot.id=[ID],snapshot.name=[NAME]' or '[ID_OR_NAME]'. + +.. + Note the use of a new 'program'; otherwise Sphinx complains about + the -h option appearing both in the above option list and this one. + +.. program:: qemu-img-common-opts + +.. option:: --object OBJECTDEF + + is a QEMU user creatable object definition. See the :manpage:`qemu(1)` + manual page for a description of the object properties. The most common + object type is a ``secret``, which is used to supply passwords and/or + encryption keys. + +.. option:: --image-opts + + Indicates that the source *FILENAME* parameter is to be interpreted as a + full option string, not a plain filename. This parameter is mutually + exclusive with the *-f* parameter. + +.. option:: --target-image-opts + + Indicates that the OUTPUT_FILENAME parameter(s) are to be interpreted as + a full option string, not a plain filename. This parameter is mutually + exclusive with the *-O* parameters. It is currently required to also use + the *-n* parameter to skip image creation. This restriction may be relaxed + in a future release. + +.. option:: --force-share (-U) + + If specified, ``qemu-img`` will open the image in shared mode, allowing + other QEMU processes to open it in write mode. For example, this can be used to + get the image information (with 'info' subcommand) when the image is used by a + running guest. Note that this could produce inconsistent results because of + concurrent metadata changes, etc. This option is only allowed when opening + images in read-only mode. + +.. option:: --backing-chain + + Will enumerate information about backing files in a disk image chain. Refer + below for further description. + +.. option:: -c + + Indicates that target image must be compressed (qcow format only). + +.. option:: -h + + With or without a command, shows help and lists the supported formats. + +.. option:: -p + + Display progress bar (compare, convert and rebase commands only). + If the *-p* option is not used for a command that supports it, the + progress is reported when the process receives a ``SIGUSR1`` or + ``SIGINFO`` signal. + +.. option:: -q + + Quiet mode - do not print any output (except errors). There's no progress bar + in case both *-q* and *-p* options are used. + +.. option:: -S SIZE + + Indicates the consecutive number of bytes that must contain only zeros + for qemu-img to create a sparse image during conversion. This value is rounded + down to the nearest 512 bytes. You may use the common size suffixes like + ``k`` for kilobytes. + +.. option:: -t CACHE + + Specifies the cache mode that should be used with the (destination) file. See + the documentation of the emulator's ``-drive cache=...`` option for allowed + values. + +.. option:: -T SRC_CACHE + + Specifies the cache mode that should be used with the source file(s). See + the documentation of the emulator's ``-drive cache=...`` option for allowed + values. + +Parameters to snapshot subcommand: + +.. program:: qemu-img-snapshot + +.. option:: snapshot + + Is the name of the snapshot to create, apply or delete + +.. option:: -a + + Applies a snapshot (revert disk to saved state) + +.. option:: -c + + Creates a snapshot + +.. option:: -d + + Deletes a snapshot + +.. option:: -l + + Lists all snapshots in the given image + +Parameters to compare subcommand: + +.. program:: qemu-img-compare + +.. option:: -f + + First image format + +.. option:: -F + + Second image format + +.. option:: -s + + Strict mode - fail on different image size or sector allocation + +Parameters to convert subcommand: + +.. program:: qemu-img-convert + +.. option:: -n + + Skip the creation of the target volume + +.. option:: -m + + Number of parallel coroutines for the convert process + +.. option:: -W + + Allow out-of-order writes to the destination. This option improves performance, + but is only recommended for preallocated devices like host devices or other + raw block devices. + +.. option:: -C + + Try to use copy offloading to move data from source image to target. This may + improve performance if the data is remote, such as with NFS or iSCSI backends, + but will not automatically sparsify zero sectors, and may result in a fully + allocated target image depending on the host support for getting allocation + information. + +.. option:: --salvage + + Try to ignore I/O errors when reading. Unless in quiet mode (``-q``), errors + will still be printed. Areas that cannot be read from the source will be + treated as containing only zeroes. + +.. option:: --target-is-zero + + Assume that reading the destination image will always return + zeros. This parameter is mutually exclusive with a destination image + that has a backing file. It is required to also use the ``-n`` + parameter to skip image creation. + +Parameters to dd subcommand: + +.. program:: qemu-img-dd + +.. option:: bs=BLOCK_SIZE + + Defines the block size + +.. option:: count=BLOCKS + + Sets the number of input blocks to copy + +.. option:: if=INPUT + + Sets the input file + +.. option:: of=OUTPUT + + Sets the output file + +.. option:: skip=BLOCKS + + Sets the number of input blocks to skip + +Command description: + +.. program:: qemu-img-commands + +.. option:: amend [--object OBJECTDEF] [--image-opts] [-p] [-q] [-f FMT] [-t CACHE] -o OPTIONS FILENAME + + 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] [-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. + + A total number of *COUNT* I/O requests is performed, each *BUFFER_SIZE* + bytes in size, and with *DEPTH* requests in parallel. The first request + starts at the position given by *OFFSET*, each following request increases + the current position by *STEP_SIZE*. If *STEP_SIZE* is not given, + *BUFFER_SIZE* is used for its value. + + If *FLUSH_INTERVAL* is specified for a write test, the request queue is + drained and a flush is issued before new writes are made whenever the number of + remaining requests is a multiple of *FLUSH_INTERVAL*. If additionally + ``--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. + + For write tests, by default a buffer filled with zeros is written. This can be + overridden with a pattern byte specified by *PATTERN*. + +.. option:: check [--object OBJECTDEF] [--image-opts] [-q] [-f FMT] [--output=OFMT] [-r [leaks | all]] [-T SRC_CACHE] [-U] FILENAME + + Perform a consistency check on the disk image *FILENAME*. The command can + output in the format *OFMT* which is either ``human`` or ``json``. + The JSON output is an object of QAPI type ``ImageCheck``. + + If ``-r`` is specified, qemu-img tries to repair any inconsistencies found + during the check. ``-r leaks`` repairs only cluster leaks, whereas + ``-r all`` fixes all kinds of errors, with a higher risk of choosing the + wrong fix or hiding corruption that has already occurred. + + Only the formats ``qcow2``, ``qed`` and ``vdi`` support + consistency checks. + + In case the image does not have any inconsistencies, check exits with ``0``. + Other exit codes indicate the kind of inconsistency found or if another error + occurred. The following table summarizes all exit codes of the check subcommand: + + 0 + Check completed, the image is (now) consistent + 1 + Check not completed because of internal errors + 2 + Check completed, image is corrupted + 3 + Check completed, image has leaked clusters, but is not corrupted + 63 + Checks are not supported by the image format + + If ``-r`` is specified, exit codes representing the image state refer to the + state after (the attempt at) repairing it. That is, a successful ``-r all`` + will yield the exit code 0, independently of the image state before. + +.. option:: commit [--object OBJECTDEF] [--image-opts] [-q] [-f FMT] [-t CACHE] [-b BASE] [-d] [-p] FILENAME + + Commit the changes recorded in *FILENAME* in its base image or backing file. + If the backing file is smaller than the snapshot, then the backing file will be + resized to be the same size as the snapshot. If the snapshot is smaller than + the backing file, the backing file will not be truncated. If you want the + backing file to match the size of the smaller snapshot, you can safely truncate + it yourself once the commit operation successfully completes. + + The image *FILENAME* is emptied after the operation has succeeded. If you do + not need *FILENAME* afterwards and intend to drop it, you may skip emptying + *FILENAME* by specifying the ``-d`` flag. + + If the backing chain of the given image file *FILENAME* has more than one + layer, the backing file into which the changes will be committed may be + specified as *BASE* (which has to be part of *FILENAME*'s backing + chain). If *BASE* is not specified, the immediate backing file of the top + image (which is *FILENAME*) will be used. Note that after a commit operation + all images between *BASE* and the top image will be invalid and may return + garbage data when read. For this reason, ``-b`` implies ``-d`` (so that + the top image stays valid). + +.. option:: compare [--object OBJECTDEF] [--image-opts] [-f FMT] [-F FMT] [-T SRC_CACHE] [-p] [-q] [-s] [-U] FILENAME1 FILENAME2 + + Check if two images have the same content. You can compare images with + different format or settings. + + The format is probed unless you specify it by ``-f`` (used for + *FILENAME1*) and/or ``-F`` (used for *FILENAME2*) option. + + By default, images with different size are considered identical if the larger + image contains only unallocated and/or zeroed sectors in the area after the end + of the other image. In addition, if any sector is not allocated in one image + and contains only zero bytes in the second one, it is evaluated as equal. You + can use Strict mode by specifying the ``-s`` option. When compare runs in + Strict mode, it fails in case image size differs or a sector is allocated in + one image and is not allocated in the second one. + + By default, compare prints out a result message. This message displays + information that both images are same or the position of the first different + byte. In addition, result message can report different image size in case + Strict mode is used. + + Compare exits with ``0`` in case the images are equal and with ``1`` + in case the images differ. Other exit codes mean an error occurred during + execution and standard error output should contain an error message. + The following table sumarizes all exit codes of the compare subcommand: + + 0 + Images are identical + 1 + Images differ + 2 + Error on opening an image + 3 + Error on checking a sector allocation + 4 + Error on reading data + +.. option:: convert [--object OBJECTDEF] [--image-opts] [--target-image-opts] [--target-is-zero] [-U] [-C] [-c] [-p] [-q] [-n] [-f FMT] [-t CACHE] [-T SRC_CACHE] [-O OUTPUT_FMT] [-B BACKING_FILE] [-o OPTIONS] [-l SNAPSHOT_PARAM] [-S SPARSE_SIZE] [-m NUM_COROUTINES] [-W] FILENAME [FILENAME2 [...]] OUTPUT_FILENAME + + Convert the disk image *FILENAME* or a snapshot *SNAPSHOT_PARAM* + to disk image *OUTPUT_FILENAME* using format *OUTPUT_FMT*. It can + be optionally compressed (``-c`` option) or use any format specific + options like encryption (``-o`` option). + + Only the formats ``qcow`` and ``qcow2`` support compression. The + compression is read-only. It means that if a compressed sector is + rewritten, then it is rewritten as uncompressed data. + + Image conversion is also useful to get smaller image when using a + growable format such as ``qcow``: the empty sectors are detected and + suppressed from the destination image. + + *SPARSE_SIZE* indicates the consecutive number of bytes (defaults to 4k) + that must contain only zeros for qemu-img to create a sparse image during + conversion. If *SPARSE_SIZE* is 0, the source will not be scanned for + unallocated or zero sectors, and the destination image will always be + fully allocated. + + You can use the *BACKING_FILE* option to force the output image to be + created as a copy on write image of the specified base image; the + *BACKING_FILE* should have the same content as the input's base image, + however the path, image format, etc may differ. + + If a relative path name is given, the backing file is looked up relative to + the directory containing *OUTPUT_FILENAME*. + + If the ``-n`` option is specified, the target volume creation will be + skipped. This is useful for formats such as ``rbd`` if the target + volume has already been created with site specific options that cannot + be supplied through qemu-img. + + Out of order writes can be enabled with ``-W`` to improve performance. + This is only recommended for preallocated devices like host devices or other + raw block devices. Out of order write does not work in combination with + creating compressed images. + + *NUM_COROUTINES* specifies how many coroutines work in parallel during + the convert process (defaults to 8). + +.. option:: create [--object OBJECTDEF] [-q] [-f FMT] [-b BACKING_FILE] [-F BACKING_FMT] [-u] [-o OPTIONS] FILENAME [SIZE] + + Create the new disk image *FILENAME* of size *SIZE* and format + *FMT*. Depending on the file format, you can add one or more *OPTIONS* + that enable additional features of this format. + + If the option *BACKING_FILE* is specified, then the image will record + only the differences from *BACKING_FILE*. No size needs to be specified in + this case. *BACKING_FILE* will never be modified unless you use the + ``commit`` monitor command (or qemu-img commit). + + If a relative path name is given, the backing file is looked up relative to + the directory containing *FILENAME*. + + Note that a given backing file will be opened to check that it is valid. Use + the ``-u`` option to enable unsafe backing file mode, which means that the + image will be created even if the associated backing file cannot be opened. A + matching backing file must be created or additional options be used to make the + backing file specification valid when you want to use an image created this + way. + + The size can also be specified using the *SIZE* option with ``-o``, + it doesn't need to be specified separately in this case. + + +.. option:: dd [--image-opts] [-U] [-f FMT] [-O OUTPUT_FMT] [bs=BLOCK_SIZE] [count=BLOCKS] [skip=BLOCKS] if=INPUT of=OUTPUT + + dd copies from *INPUT* file to *OUTPUT* file converting it from + *FMT* format to *OUTPUT_FMT* format. + + The data is by default read and written using blocks of 512 bytes but can be + modified by specifying *BLOCK_SIZE*. If count=\ *BLOCKS* is specified + dd will stop reading input after reading *BLOCKS* input blocks. + + The size syntax is similar to :manpage:`dd(1)`'s size syntax. + +.. option:: info [--object OBJECTDEF] [--image-opts] [-f FMT] [--output=OFMT] [--backing-chain] [-U] FILENAME + + Give information about the disk image *FILENAME*. Use it in + particular to know the size reserved on disk which can be different + from the displayed size. If VM snapshots are stored in the disk image, + they are displayed too. + + If a disk image has a backing file chain, information about each disk image in + the chain can be recursively enumerated by using the option ``--backing-chain``. + + For instance, if you have an image chain like: + + :: + + base.qcow2 <- snap1.qcow2 <- snap2.qcow2 + + To enumerate information about each disk image in the above chain, starting from top to base, do: + + :: + + qemu-img info --backing-chain snap2.qcow2 + + The command can output in the format *OFMT* which is either ``human`` or + ``json``. The JSON output is an object of QAPI type ``ImageInfo``; with + ``--backing-chain``, it is an array of ``ImageInfo`` objects. + + ``--output=human`` reports the following information (for every image in the + chain): + + *image* + The image file name + + *file format* + The image format + + *virtual size* + The size of the guest disk + + *disk size* + How much space the image file occupies on the host file system (may be + shown as 0 if this information is unavailable, e.g. because there is no + file system) + + *cluster_size* + Cluster size of the image format, if applicable + + *encrypted* + Whether the image is encrypted (only present if so) + + *cleanly shut down* + This is shown as ``no`` if the image is dirty and will have to be + auto-repaired the next time it is opened in qemu. + + *backing file* + The backing file name, if present + + *backing file format* + The format of the backing file, if the image enforces it + + *Snapshot list* + A list of all internal snapshots + + *Format specific information* + Further information whose structure depends on the image format. This + section is a textual representation of the respective + ``ImageInfoSpecific*`` QAPI object (e.g. ``ImageInfoSpecificQCow2`` + for qcow2 images). + +.. option:: map [--object OBJECTDEF] [--image-opts] [-f FMT] [--output=OFMT] [-U] FILENAME + + Dump the metadata of image *FILENAME* and its backing file chain. + In particular, this commands dumps the allocation state of every sector + of *FILENAME*, together with the topmost file that allocates it in + the backing file chain. + + Two option formats are possible. The default format (``human``) + only dumps known-nonzero areas of the file. Known-zero parts of the + file are omitted altogether, and likewise for parts that are not allocated + throughout the chain. ``qemu-img`` output will identify a file + from where the data can be read, and the offset in the file. Each line + will include four fields, the first three of which are hexadecimal + numbers. For example the first line of: + + :: + + Offset Length Mapped to File + 0 0x20000 0x50000 /tmp/overlay.qcow2 + 0x100000 0x10000 0x95380000 /tmp/backing.qcow2 + + means that 0x20000 (131072) bytes starting at offset 0 in the image are + available in /tmp/overlay.qcow2 (opened in ``raw`` format) starting + at offset 0x50000 (327680). Data that is compressed, encrypted, or + otherwise not available in raw format will cause an error if ``human`` + format is in use. Note that file names can include newlines, thus it is + not safe to parse this output format in scripts. + + The alternative format ``json`` will return an array of dictionaries + in JSON format. It will include similar information in + the ``start``, ``length``, ``offset`` fields; + it will also include other more specific information: + + - whether the sectors contain actual data or not (boolean field ``data``; + if false, the sectors are either unallocated or stored as optimized + all-zero clusters); + - whether the data is known to read as zero (boolean field ``zero``); + - in order to make the output shorter, the target file is expressed as + a ``depth``; for example, a depth of 2 refers to the backing file + of the backing file of *FILENAME*. + + In JSON format, the ``offset`` field is optional; it is absent in + cases where ``human`` format would omit the entry or exit with an error. + If ``data`` is false and the ``offset`` field is present, the + corresponding sectors in the file are not yet in use, but they are + preallocated. + + For more information, consult ``include/block/block.h`` in QEMU's + source code. + +.. option:: measure [--output=OFMT] [-O OUTPUT_FMT] [-o OPTIONS] [--size N | [--object OBJECTDEF] [--image-opts] [-f FMT] [-l SNAPSHOT_PARAM] FILENAME] + + Calculate the file size required for a new image. This information + can be used to size logical volumes or SAN LUNs appropriately for + the image that will be placed in them. The values reported are + guaranteed to be large enough to fit the image. The command can + output in the format *OFMT* which is either ``human`` or ``json``. + The JSON output is an object of QAPI type ``BlockMeasureInfo``. + + If the size *N* is given then act as if creating a new empty image file + using ``qemu-img create``. If *FILENAME* is given then act as if + converting an existing image file using ``qemu-img convert``. The format + of the new file is given by *OUTPUT_FMT* while the format of an existing + file is given by *FMT*. + + A snapshot in an existing image can be specified using *SNAPSHOT_PARAM*. + + The following fields are reported: + + :: + + required size: 524288 + fully allocated size: 1074069504 + + The ``required size`` is the file size of the new image. It may be smaller + than the virtual disk size if the image format supports compact representation. + + The ``fully allocated size`` is the file size of the new image once data has + been written to all sectors. This is the maximum size that the image file can + occupy with the exception of internal snapshots, dirty bitmaps, vmstate data, + and other advanced image format features. + +.. option:: snapshot [--object OBJECTDEF] [--image-opts] [-U] [-q] [-l | -a SNAPSHOT | -c SNAPSHOT | -d SNAPSHOT] FILENAME + + List, apply, create or delete snapshots in image *FILENAME*. + +.. option:: rebase [--object OBJECTDEF] [--image-opts] [-U] [-q] [-f FMT] [-t CACHE] [-T SRC_CACHE] [-p] [-u] -b BACKING_FILE [-F BACKING_FMT] FILENAME + + Changes the backing file of an image. Only the formats ``qcow2`` and + ``qed`` support changing the backing file. + + The backing file is changed to *BACKING_FILE* and (if the image format of + *FILENAME* supports this) the backing file format is changed to + *BACKING_FMT*. If *BACKING_FILE* is specified as "" (the empty + string), then the image is rebased onto no backing file (i.e. it will exist + independently of any backing file). + + If a relative path name is given, the backing file is looked up relative to + the directory containing *FILENAME*. + + *CACHE* specifies the cache mode to be used for *FILENAME*, whereas + *SRC_CACHE* specifies the cache mode for reading backing files. + + There are two different modes in which ``rebase`` can operate: + + Safe mode + This is the default mode and performs a real rebase operation. The + new backing file may differ from the old one and qemu-img rebase + will take care of keeping the guest-visible content of *FILENAME* + unchanged. + + In order to achieve this, any clusters that differ between + *BACKING_FILE* and the old backing file of *FILENAME* are merged + into *FILENAME* before actually changing the backing file. + + Note that the safe mode is an expensive operation, comparable to + converting an image. It only works if the old backing file still + exists. + + Unsafe mode + qemu-img uses the unsafe mode if ``-u`` is specified. In this + mode, only the backing file name and format of *FILENAME* is changed + without any checks on the file contents. The user must take care of + specifying the correct new backing file, or the guest-visible + content of the image will be corrupted. + + This mode is useful for renaming or moving the backing file to + somewhere else. It can be used without an accessible old backing + file, i.e. you can use it to fix an image whose backing file has + already been moved/renamed. + + You can use ``rebase`` to perform a "diff" operation on two + disk images. This can be useful when you have copied or cloned + a guest, and you want to get back to a thin image on top of a + template or base image. + + Say that ``base.img`` has been cloned as ``modified.img`` by + copying it, and that the ``modified.img`` guest has run so there + are now some changes compared to ``base.img``. To construct a thin + image called ``diff.qcow2`` that contains just the differences, do: + + :: + + qemu-img create -f qcow2 -b modified.img diff.qcow2 + qemu-img rebase -b base.img diff.qcow2 + + At this point, ``modified.img`` can be discarded, since + ``base.img + diff.qcow2`` contains the same information. + +.. option:: resize [--object OBJECTDEF] [--image-opts] [-f FMT] [--preallocation=PREALLOC] [-q] [--shrink] FILENAME [+ | -]SIZE + + Change the disk image as if it had been created with *SIZE*. + + Before using this command to shrink a disk image, you MUST use file system and + partitioning tools inside the VM to reduce allocated file systems and partition + sizes accordingly. Failure to do so will result in data loss! + + When shrinking images, the ``--shrink`` option must be given. This informs + qemu-img that the user acknowledges all loss of data beyond the truncated + image's end. + + After using this command to grow a disk image, you must use file system and + partitioning tools inside the VM to actually begin using the new space on the + device. + + When growing an image, the ``--preallocation`` option may be used to specify + how the additional image area should be allocated on the host. See the format + description in the :ref:`notes` section which values are allowed. Using this + option may result in slightly more data being allocated than necessary. + +.. _notes: + +Notes +----- + +Supported image file formats: + +``raw`` + + Raw disk image format (default). This format has the advantage of + being simple and easily exportable to all other emulators. If your + file system supports *holes* (for example in ext2 or ext3 on + Linux or NTFS on Windows), then only the written sectors will reserve + space. Use ``qemu-img info`` to know the real size used by the + image or ``ls -ls`` on Unix/Linux. + + Supported options: + + ``preallocation`` + Preallocation mode (allowed values: ``off``, ``falloc``, + ``full``). ``falloc`` mode preallocates space for image by + calling ``posix_fallocate()``. ``full`` mode preallocates space + for image by writing data to underlying storage. This data may or + may not be zero, depending on the storage location. + +``qcow2`` + + QEMU image format, the most versatile format. Use it to have smaller + images (useful if your filesystem does not supports holes, for example + on Windows), optional AES encryption, zlib based compression and + support of multiple VM snapshots. + + Supported options: + + ``compat`` + Determines the qcow2 version to use. ``compat=0.10`` uses the + traditional image format that can be read by any QEMU since 0.10. + ``compat=1.1`` enables image format extensions that only QEMU 1.1 and + newer understand (this is the default). Amongst others, this includes zero + clusters, which allow efficient copy-on-read for sparse images. + + ``backing_file`` + File name of a base image (see ``create`` subcommand) + + ``backing_fmt`` + Image format of the base image + + ``encryption`` + If this option is set to ``on``, the image is encrypted with + 128-bit AES-CBC. + + The use of encryption in qcow and qcow2 images is considered to be + flawed by modern cryptography standards, suffering from a number + of design problems: + + - The AES-CBC cipher is used with predictable initialization + vectors based on the sector number. This makes it vulnerable to + chosen plaintext attacks which can reveal the existence of + encrypted data. + + - The user passphrase is directly used as the encryption key. A + poorly chosen or short passphrase will compromise the security + of the encryption. + + - In the event of the passphrase being compromised there is no way + to change the passphrase to protect data in any qcow images. The + files must be cloned, using a different encryption passphrase in + the new file. The original file must then be securely erased + using a program like shred, though even this is ineffective with + many modern storage technologies. + + - Initialization vectors used to encrypt sectors are based on the + guest virtual sector number, instead of the host physical + sector. When a disk image has multiple internal snapshots this + means that data in multiple physical sectors is encrypted with + the same initialization vector. With the CBC mode, this opens + the possibility of watermarking attacks if the attack can + collect multiple sectors encrypted with the same IV and some + predictable data. Having multiple qcow2 images with the same + passphrase also exposes this weakness since the passphrase is + directly used as the key. + + Use of qcow / qcow2 encryption is thus strongly discouraged. Users are + recommended to use an alternative encryption technology such as the + Linux dm-crypt / LUKS system. + + ``cluster_size`` + Changes the qcow2 cluster size (must be between 512 and + 2M). Smaller cluster sizes can improve the image file size whereas + larger cluster sizes generally provide better performance. + + ``preallocation`` + Preallocation mode (allowed values: ``off``, ``metadata``, + ``falloc``, ``full``). An image with preallocated metadata is + initially larger but can improve performance when the image needs + to grow. ``falloc`` and ``full`` preallocations are like the same + options of ``raw`` format, but sets up metadata also. + + ``lazy_refcounts`` + If this option is set to ``on``, reference count updates are + postponed with the goal of avoiding metadata I/O and improving + performance. This is particularly interesting with + ``cache=writethrough`` which doesn't batch metadata + updates. The tradeoff is that after a host crash, the reference + count tables must be rebuilt, i.e. on the next open an (automatic) + ``qemu-img check -r all`` is required, which may take some time. + + This option can only be enabled if ``compat=1.1`` is specified. + + ``nocow`` + If this option is set to ``on``, it will turn off COW of the file. It's + only valid on btrfs, no effect on other file systems. + + Btrfs has low performance when hosting a VM image file, even more + when the guest on the VM also using btrfs as file system. Turning + off COW is a way to mitigate this bad performance. Generally there + are two ways to turn off COW on btrfs: + + - Disable it by mounting with nodatacow, then all newly created files + will be NOCOW + - For an empty file, add the NOCOW file attribute. That's what this + option does. + + Note: this option is only valid to new or empty files. If there is + an existing file which is COW and has data blocks already, it + couldn't be changed to NOCOW by setting ``nocow=on``. One can + issue ``lsattr filename`` to check if the NOCOW flag is set or not + (Capital 'C' is NOCOW flag). + +``Other`` + + QEMU also supports various other image file formats for + compatibility with older QEMU versions or other hypervisors, + including VMDK, VDI, VHD (vpc), VHDX, qcow1 and QED. For a full list + of supported formats see ``qemu-img --help``. For a more detailed + description of these formats, see the QEMU block drivers reference + documentation. + + The main purpose of the block drivers for these formats is image + conversion. For running VMs, it is recommended to convert the disk + images to either raw or qcow2 in order to achieve good performance. diff --git a/docs/tools/qemu-nbd.rst b/docs/tools/qemu-nbd.rst new file mode 100644 index 0000000000..667861cb22 --- /dev/null +++ b/docs/tools/qemu-nbd.rst @@ -0,0 +1,256 @@ +QEMU Disk Network Block Device Server +===================================== + +Synopsis +-------- + +**qemu-nbd** [*OPTION*]... *filename* + +**qemu-nbd** -L [*OPTION*]... + +**qemu-nbd** -d *dev* + +Description +----------- + +Export a QEMU disk image using the NBD protocol. + +Other uses: + +- Bind a /dev/nbdX block device to a QEMU server (on Linux). +- As a client to query exports of a remote NBD server. + +Options +------- + +.. program:: qemu-nbd + +*filename* is a disk image filename, or a set of block +driver options if ``--image-opts`` is specified. + +*dev* is an NBD device. + +.. option:: --object type,id=ID,...props... + + Define a new instance of the *type* object class identified by *ID*. + See the :manpage:`qemu(1)` manual page for full details of the properties + supported. The common object types that it makes sense to define are the + ``secret`` object, which is used to supply passwords and/or encryption + keys, and the ``tls-creds`` object, which is used to supply TLS + credentials for the qemu-nbd server or client. + +.. option:: -p, --port=PORT + + TCP port to listen on as a server, or connect to as a client + (default ``10809``). + +.. option:: -o, --offset=OFFSET + + The offset into the image. + +.. option:: -b, --bind=IFACE + + The interface to bind to as a server, or connect to as a client + (default ``0.0.0.0``). + +.. option:: -k, --socket=PATH + + Use a unix socket with path *PATH*. + +.. option:: --image-opts + + Treat *filename* as a set of image options, instead of a plain + filename. If this flag is specified, the ``-f`` flag should + not be used, instead the :option:`format=` option should be set. + +.. option:: -f, --format=FMT + + Force the use of the block driver for format *FMT* instead of + auto-detecting. + +.. option:: -r, --read-only + + Export the disk as read-only. + +.. option:: -B, --bitmap=NAME + + If *filename* has a qcow2 persistent bitmap *NAME*, expose + that bitmap via the ``qemu:dirty-bitmap:NAME`` context + accessible through NBD_OPT_SET_META_CONTEXT. + +.. option:: -s, --snapshot + + Use *filename* as an external snapshot, create a temporary + file with ``backing_file=``\ *filename*, redirect the write to + the temporary one. + +.. option:: -l, --load-snapshot=SNAPSHOT_PARAM + + Load an internal snapshot inside *filename* and export it + as an read-only device, SNAPSHOT_PARAM format is + ``snapshot.id=[ID],snapshot.name=[NAME]`` or ``[ID_OR_NAME]`` + +.. option:: --cache=CACHE + + The cache mode to be used with the file. See the documentation of + the emulator's ``-drive cache=...`` option for allowed values. + +.. option:: -n, --nocache + + Equivalent to :option:`--cache=none`. + +.. option:: --aio=AIO + + Set the asynchronous I/O mode between ``threads`` (the default), + ``native`` (Linux only), and ``io_uring`` (Linux 5.1+). + +.. option:: --discard=DISCARD + + Control whether ``discard`` (also known as ``trim`` or ``unmap``) + requests are ignored or passed to the filesystem. *DISCARD* is one of + ``ignore`` (or ``off``), ``unmap`` (or ``on``). The default is + ``ignore``. + +.. option:: --detect-zeroes=DETECT_ZEROES + + Control the automatic conversion of plain zero writes by the OS to + driver-specific optimized zero write commands. *DETECT_ZEROES* is one of + ``off``, ``on``, or ``unmap``. ``unmap`` + converts a zero write to an unmap operation and can only be used if + *DISCARD* is set to ``unmap``. The default is ``off``. + +.. option:: -c, --connect=DEV + + Connect *filename* to NBD device *DEV* (Linux only). + +.. option:: -d, --disconnect + + Disconnect the device *DEV* (Linux only). + +.. option:: -e, --shared=NUM + + Allow up to *NUM* clients to share the device (default + ``1``). Safe for readers, but for now, consistency is not + guaranteed between multiple writers. + +.. option:: -t, --persistent + + Don't exit on the last connection. + +.. option:: -x, --export-name=NAME + + Set the NBD volume export name (default of a zero-length string). + +.. option:: -D, --description=DESCRIPTION + + Set the NBD volume export description, as a human-readable + string. + +.. option:: -L, --list + + Connect as a client and list all details about the exports exposed by + a remote NBD server. This enables list mode, and is incompatible + with options that change behavior related to a specific export (such as + :option:`--export-name`, :option:`--offset`, ...). + +.. option:: --tls-creds=ID + + Enable mandatory TLS encryption for the server by setting the ID + of the TLS credentials object previously created with the --object + option; or provide the credentials needed for connecting as a client + in list mode. + +.. option:: --fork + + Fork off the server process and exit the parent once the server is running. + +.. option:: --pid-file=PATH + + Store the server's process ID in the given file. + +.. option:: --tls-authz=ID + + Specify the ID of a qauthz object previously created with the + :option:`--object` option. This will be used to authorize connecting users + against their x509 distinguished name. + +.. option:: -v, --verbose + + Display extra debugging information. + +.. option:: -h, --help + + Display this help and exit. + +.. option:: -V, --version + + Display version information and exit. + +.. option:: -T, --trace [[enable=]PATTERN][,events=FILE][,file=FILE] + + .. include:: ../qemu-option-trace.rst.inc + +Examples +-------- + +Start a server listening on port 10809 that exposes only the +guest-visible contents of a qcow2 file, with no TLS encryption, and +with the default export name (an empty string). The command is +one-shot, and will block until the first successful client +disconnects: + +:: + + qemu-nbd -f qcow2 file.qcow2 + +Start a long-running server listening with encryption on port 10810, +and whitelist clients with a specific X.509 certificate to connect to +a 1 megabyte subset of a raw file, using the export name 'subset': + +:: + + qemu-nbd \ + --object tls-creds-x509,id=tls0,endpoint=server,dir=/path/to/qemutls \ + --object 'authz-simple,id=auth0,identity=CN=laptop.example.com,,\ + O=Example Org,,L=London,,ST=London,,C=GB' \ + --tls-creds tls0 --tls-authz auth0 \ + -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 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 \ + --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 +partitions found within), then disconnect the device when done. +Access to bind qemu-nbd to an /dev/nbd device generally requires root +privileges, and may also require the execution of ``modprobe nbd`` +to enable the kernel NBD client module. *CAUTION*: Do not use +this method to mount filesystems from an untrusted guest image - a +malicious guest may have prepared the image to attempt to trigger +kernel bugs in partition probing or file system mounting. + +:: + + qemu-nbd -c /dev/nbd0 -f qcow2 file.qcow2 + qemu-nbd -d /dev/nbd0 + +Query a remote server to see details about what export(s) it is +serving on port 10809, and authenticating via PSK: + +:: + + qemu-nbd \ + --object tls-creds-psk,id=tls0,dir=/tmp/keys,username=eblake,endpoint=client \ + --tls-creds tls0 -L -b remote.example.com + +See also +-------- + +:manpage:`qemu(1)`, :manpage:`qemu-img(1)` diff --git a/docs/tools/qemu-trace-stap.rst b/docs/tools/qemu-trace-stap.rst new file mode 100644 index 0000000000..fb70445c75 --- /dev/null +++ b/docs/tools/qemu-trace-stap.rst @@ -0,0 +1,124 @@ +QEMU SystemTap trace tool +========================= + +Synopsis +-------- + +**qemu-trace-stap** [*GLOBAL-OPTIONS*] *COMMAND* [*COMMAND-OPTIONS*] *ARGS*... + +Description +----------- + +The ``qemu-trace-stap`` program facilitates tracing of the execution +of QEMU emulators using SystemTap. + +It is required to have the SystemTap runtime environment installed to use +this program, since it is a wrapper around execution of the ``stap`` +program. + +Options +------- + +.. program:: qemu-trace-stap + +The following global options may be used regardless of which command +is executed: + +.. option:: --verbose, -v + + Display verbose information about command execution. + +The following commands are valid: + +.. option:: list BINARY PATTERN... + + List all the probe names provided by *BINARY* that match + *PATTERN*. + + If *BINARY* is not an absolute path, it will be located by searching + the directories listed in the ``$PATH`` environment variable. + + *PATTERN* is a plain string that is used to filter the results of + this command. It may optionally contain a ``*`` wildcard to facilitate + matching multiple probes without listing each one explicitly. Multiple + *PATTERN* arguments may be given, causing listing of probes that match + any of the listed names. If no *PATTERN* is given, the all possible + probes will be listed. + + For example, to list all probes available in the ``qemu-system-x86_64`` + binary: + + :: + + $ qemu-trace-stap list qemu-system-x86_64 + + To filter the list to only cover probes related to QEMU's cryptographic + subsystem, in a binary outside ``$PATH`` + + :: + + $ qemu-trace-stap list /opt/qemu/4.0.0/bin/qemu-system-x86_64 'qcrypto*' + +.. option:: run OPTIONS BINARY PATTERN... + + Run a trace session, printing formatted output any time a process that is + executing *BINARY* triggers a probe matching *PATTERN*. + + If *BINARY* is not an absolute path, it will be located by searching + the directories listed in the ``$PATH`` environment variable. + + *PATTERN* is a plain string that matches a probe name shown by the + *LIST* command. It may optionally contain a ``*`` wildcard to + facilitate matching multiple probes without listing each one explicitly. + Multiple *PATTERN* arguments may be given, causing all matching probes + to be monitored. At least one *PATTERN* is required, since stap is not + capable of tracing all known QEMU probes concurrently without overflowing + its trace buffer. + + Invocation of this command does not need to be synchronized with + invocation of the QEMU process(es). It will match probes on all + existing running processes and all future launched processes, + unless told to only monitor a specific process. + + Valid command specific options are: + + .. program:: qemu-trace-stap-run + + .. option:: --pid=PID, -p PID + + Restrict the tracing session so that it only triggers for the process + identified by *PID*. + + For example, to monitor all processes executing ``qemu-system-x86_64`` + as found on ``$PATH``, displaying all I/O related probes: + + :: + + $ qemu-trace-stap run qemu-system-x86_64 'qio*' + + To monitor only the QEMU process with PID 1732 + + :: + + $ qemu-trace-stap run --pid=1732 qemu-system-x86_64 'qio*' + + To monitor QEMU processes running an alternative binary outside of + ``$PATH``, displaying verbose information about setup of the + tracing environment: + + :: + + $ qemu-trace-stap -v run /opt/qemu/4.0.0/qemu-system-x86_64 'qio*' + +See also +-------- + +:manpage:`qemu(1)`, :manpage:`stap(1)` + +.. + Copyright (C) 2019 Red Hat, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. diff --git a/docs/tools/virtfs-proxy-helper.rst b/docs/tools/virtfs-proxy-helper.rst new file mode 100644 index 0000000000..6cdeedf8e9 --- /dev/null +++ b/docs/tools/virtfs-proxy-helper.rst @@ -0,0 +1,72 @@ +QEMU 9p virtfs proxy filesystem helper +====================================== + +Synopsis +-------- + +**virtfs-proxy-helper** [*OPTIONS*] + +Description +----------- + +Pass-through security model in QEMU 9p server needs root privilege to do +few file operations (like chown, chmod to any mode/uid:gid). There are two +issues in pass-through security model: + +- TOCTTOU vulnerability: Following symbolic links in the server could + provide access to files beyond 9p export path. + +- Running QEMU with root privilege could be a security issue. + +To overcome above issues, following approach is used: A new filesystem +type 'proxy' is introduced. Proxy FS uses chroot + socket combination +for securing the vulnerability known with following symbolic links. +Intention of adding a new filesystem type is to allow qemu to run +in non-root mode, but doing privileged operations using socket IO. + +Proxy helper (a stand alone binary part of qemu) is invoked with +root privileges. Proxy helper chroots into 9p export path and creates +a socket pair or a named socket based on the command line parameter. +QEMU and proxy helper communicate using this socket. QEMU proxy fs +driver sends filesystem request to proxy helper and receives the +response from it. + +The proxy helper is designed so that it can drop root privileges except +for the capabilities needed for doing filesystem operations. + +Options +------- + +The following options are supported: + +.. program:: virtfs-proxy-helper + +.. option:: -h + + Display help and exit + +.. option:: -p, --path PATH + + Path to export for proxy filesystem driver + +.. option:: -f, --fd SOCKET_ID + + Use given file descriptor as socket descriptor for communicating with + qemu proxy fs drier. Usually a helper like libvirt will create + socketpair and pass one of the fds as parameter to this option. + +.. option:: -s, --socket SOCKET_FILE + + Creates named socket file for communicating with qemu proxy fs driver + +.. option:: -u, --uid UID + + uid to give access to named socket file; used in combination with -g. + +.. option:: -g, --gid GID + + gid to give access to named socket file; used in combination with -u. + +.. option:: -n, --nodaemon + + Run as a normal program. By default program will run in daemon mode diff --git a/docs/tools/virtiofsd.rst b/docs/tools/virtiofsd.rst new file mode 100644 index 0000000000..378594c422 --- /dev/null +++ b/docs/tools/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/docs/user/conf.py b/docs/user/conf.py new file mode 100644 index 0000000000..4b09aedd45 --- /dev/null +++ b/docs/user/conf.py @@ -0,0 +1,15 @@ +# -*- coding: utf-8 -*- +# +# QEMU documentation build configuration file for the 'user' manual. +# +# This includes the top level conf file and then makes any necessary tweaks. +import sys +import os + +qemu_docdir = os.path.abspath("..") +parent_config = os.path.join(qemu_docdir, "conf.py") +exec(compile(open(parent_config, "rb").read(), parent_config, 'exec')) + +# This slightly misuses the 'description', but is the best way to get +# the manual title to appear in the sidebar. +html_theme_options['description'] = u'User Mode Emulation User''s Guide' diff --git a/docs/user/index.rst b/docs/user/index.rst new file mode 100644 index 0000000000..e030dadf65 --- /dev/null +++ b/docs/user/index.rst @@ -0,0 +1,16 @@ +.. This is the top level page for the 'user' manual. + + +QEMU User Mode Emulation User's Guide +===================================== + +This manual is the overall guide for users using QEMU +for user-mode emulation. In this mode, QEMU can launch +processes compiled for one CPU on another CPU. + +Contents: + +.. toctree:: + :maxdepth: 2 + + main diff --git a/docs/user/main.rst b/docs/user/main.rst new file mode 100644 index 0000000000..bd99b0fdbe --- /dev/null +++ b/docs/user/main.rst @@ -0,0 +1,295 @@ +QEMU User space emulator +======================== + +Supported Operating Systems +--------------------------- + +The following OS are supported in user space emulation: + +- Linux (referred as qemu-linux-user) + +- BSD (referred as qemu-bsd-user) + +Features +-------- + +QEMU user space emulation has the following notable features: + +**System call translation:** + QEMU includes a generic system call translator. This means that the + parameters of the system calls can be converted to fix endianness and + 32/64-bit mismatches between hosts and targets. IOCTLs can be + converted too. + +**POSIX signal handling:** + QEMU can redirect to the running program all signals coming from the + host (such as ``SIGALRM``), as well as synthesize signals from + virtual CPU exceptions (for example ``SIGFPE`` when the program + executes a division by zero). + + QEMU relies on the host kernel to emulate most signal system calls, + for example to emulate the signal mask. On Linux, QEMU supports both + normal and real-time signals. + +**Threading:** + On Linux, QEMU can emulate the ``clone`` syscall and create a real + host thread (with a separate virtual CPU) for each emulated thread. + Note that not all targets currently emulate atomic operations + correctly. x86 and Arm use a global lock in order to preserve their + semantics. + +QEMU was conceived so that ultimately it can emulate itself. Although it +is not very useful, it is an important test to show the power of the +emulator. + +Linux User space emulator +------------------------- + +Quick Start +~~~~~~~~~~~ + +In order to launch a Linux process, QEMU needs the process executable +itself and all the target (x86) dynamic libraries used by it. + +- On x86, you can just try to launch any process by using the native + libraries:: + + qemu-i386 -L / /bin/ls + + ``-L /`` tells that the x86 dynamic linker must be searched with a + ``/`` prefix. + +- Since QEMU is also a linux process, you can launch QEMU with QEMU + (NOTE: you can only do that if you compiled QEMU from the sources):: + + qemu-i386 -L / qemu-i386 -L / /bin/ls + +- On non x86 CPUs, you need first to download at least an x86 glibc + (``qemu-runtime-i386-XXX-.tar.gz`` on the QEMU web page). Ensure that + ``LD_LIBRARY_PATH`` is not set:: + + unset LD_LIBRARY_PATH + + Then you can launch the precompiled ``ls`` x86 executable:: + + qemu-i386 tests/i386/ls + + You can look at ``scripts/qemu-binfmt-conf.sh`` so that QEMU is + automatically launched by the Linux kernel when you try to launch x86 + executables. It requires the ``binfmt_misc`` module in the Linux + kernel. + +- The x86 version of QEMU is also included. You can try weird things + such as:: + + qemu-i386 /usr/local/qemu-i386/bin/qemu-i386 \ + /usr/local/qemu-i386/bin/ls-i386 + +Wine launch +~~~~~~~~~~~ + +- Ensure that you have a working QEMU with the x86 glibc distribution + (see previous section). In order to verify it, you must be able to + do:: + + qemu-i386 /usr/local/qemu-i386/bin/ls-i386 + +- Download the binary x86 Wine install (``qemu-XXX-i386-wine.tar.gz`` + on the QEMU web page). + +- Configure Wine on your account. Look at the provided script + ``/usr/local/qemu-i386/bin/wine-conf.sh``. Your previous + ``${HOME}/.wine`` directory is saved to ``${HOME}/.wine.org``. + +- Then you can try the example ``putty.exe``:: + + qemu-i386 /usr/local/qemu-i386/wine/bin/wine \ + /usr/local/qemu-i386/wine/c/Program\ Files/putty.exe + +Command line options +~~~~~~~~~~~~~~~~~~~~ + +:: + + qemu-i386 [-h] [-d] [-L path] [-s size] [-cpu model] [-g port] [-B offset] [-R size] program [arguments...] + +``-h`` + Print the help + +``-L path`` + Set the x86 elf interpreter prefix (default=/usr/local/qemu-i386) + +``-s size`` + Set the x86 stack size in bytes (default=524288) + +``-cpu model`` + Select CPU model (-cpu help for list and additional feature + selection) + +``-E var=value`` + Set environment var to value. + +``-U var`` + Remove var from the environment. + +``-B offset`` + Offset guest address by the specified number of bytes. This is useful + when the address region required by guest applications is reserved on + the host. This option is currently only supported on some hosts. + +``-R size`` + Pre-allocate a guest virtual address space of the given size (in + bytes). \"G\", \"M\", and \"k\" suffixes may be used when specifying + the size. + +Debug options: + +``-d item1,...`` + Activate logging of the specified items (use '-d help' for a list of + log items) + +``-p pagesize`` + Act as if the host page size was 'pagesize' bytes + +``-g port`` + Wait gdb connection to port + +``-singlestep`` + Run the emulation in single step mode. + +Environment variables: + +QEMU_STRACE + Print system calls and arguments similar to the 'strace' program + (NOTE: the actual 'strace' program will not work because the user + space emulator hasn't implemented ptrace). At the moment this is + incomplete. All system calls that don't have a specific argument + format are printed with information for six arguments. Many + flag-style arguments don't have decoders and will show up as numbers. + +Other binaries +~~~~~~~~~~~~~~ + +user mode (Alpha) +``qemu-alpha`` TODO. + +user mode (Arm) +``qemu-armeb`` TODO. + +user mode (Arm) +``qemu-arm`` is also capable of running Arm \"Angel\" semihosted ELF +binaries (as implemented by the arm-elf and arm-eabi Newlib/GDB +configurations), and arm-uclinux bFLT format binaries. + +user mode (ColdFire) +user mode (M68K) +``qemu-m68k`` is capable of running semihosted binaries using the BDM +(m5xxx-ram-hosted.ld) or m68k-sim (sim.ld) syscall interfaces, and +coldfire uClinux bFLT format binaries. + +The binary format is detected automatically. + +user mode (Cris) +``qemu-cris`` TODO. + +user mode (i386) +``qemu-i386`` TODO. ``qemu-x86_64`` TODO. + +user mode (Microblaze) +``qemu-microblaze`` TODO. + +user mode (MIPS) +``qemu-mips`` executes 32-bit big endian MIPS binaries (MIPS O32 ABI). + +``qemu-mipsel`` executes 32-bit little endian MIPS binaries (MIPS O32 +ABI). + +``qemu-mips64`` executes 64-bit big endian MIPS binaries (MIPS N64 ABI). + +``qemu-mips64el`` executes 64-bit little endian MIPS binaries (MIPS N64 +ABI). + +``qemu-mipsn32`` executes 32-bit big endian MIPS binaries (MIPS N32 +ABI). + +``qemu-mipsn32el`` executes 32-bit little endian MIPS binaries (MIPS N32 +ABI). + +user mode (NiosII) +``qemu-nios2`` TODO. + +user mode (PowerPC) +``qemu-ppc64abi32`` TODO. ``qemu-ppc64`` TODO. ``qemu-ppc`` TODO. + +user mode (SH4) +``qemu-sh4eb`` TODO. ``qemu-sh4`` TODO. + +user mode (SPARC) +``qemu-sparc`` can execute Sparc32 binaries (Sparc32 CPU, 32 bit ABI). + +``qemu-sparc32plus`` can execute Sparc32 and SPARC32PLUS binaries +(Sparc64 CPU, 32 bit ABI). + +``qemu-sparc64`` can execute some Sparc64 (Sparc64 CPU, 64 bit ABI) and +SPARC32PLUS binaries (Sparc64 CPU, 32 bit ABI). + +BSD User space emulator +----------------------- + +BSD Status +~~~~~~~~~~ + +- target Sparc64 on Sparc64: Some trivial programs work. + +Quick Start +~~~~~~~~~~~ + +In order to launch a BSD process, QEMU needs the process executable +itself and all the target dynamic libraries used by it. + +- On Sparc64, you can just try to launch any process by using the + native libraries:: + + qemu-sparc64 /bin/ls + +Command line options +~~~~~~~~~~~~~~~~~~~~ + +:: + + qemu-sparc64 [-h] [-d] [-L path] [-s size] [-bsd type] program [arguments...] + +``-h`` + Print the help + +``-L path`` + Set the library root path (default=/) + +``-s size`` + Set the stack size in bytes (default=524288) + +``-ignore-environment`` + Start with an empty environment. Without this option, the initial + environment is a copy of the caller's environment. + +``-E var=value`` + Set environment var to value. + +``-U var`` + Remove var from the environment. + +``-bsd type`` + Set the type of the emulated BSD Operating system. Valid values are + FreeBSD, NetBSD and OpenBSD (default). + +Debug options: + +``-d item1,...`` + Activate logging of the specified items (use '-d help' for a list of + log items) + +``-p pagesize`` + Act as if the host page size was 'pagesize' bytes + +``-singlestep`` + Run the emulation in single step mode. diff --git a/docs/virtio-net-failover.rst b/docs/virtio-net-failover.rst index 22f64c7bc8..6002dc5d96 100644 --- a/docs/virtio-net-failover.rst +++ b/docs/virtio-net-failover.rst @@ -1,6 +1,6 @@ -======================== +====================================== QEMU virtio-net standby (net_failover) -======================== +====================================== This document explains the setup and usage of virtio-net standby feature which is used to create a net_failover pair of devices. diff --git a/docs/virtio-pmem.rst b/docs/virtio-pmem.rst index e77881b26f..4bf5d00443 100644 --- a/docs/virtio-pmem.rst +++ b/docs/virtio-pmem.rst @@ -27,17 +27,18 @@ virtio pmem usage ----------------- A virtio pmem device backed by a memory-backend-file can be created on - the QEMU command line as in the following example: + the QEMU command line as in the following example:: - -object memory-backend-file,id=mem1,share,mem-path=./virtio_pmem.img,size=4G - -device virtio-pmem-pci,memdev=mem1,id=nv1 + -object memory-backend-file,id=mem1,share,mem-path=./virtio_pmem.img,size=4G + -device virtio-pmem-pci,memdev=mem1,id=nv1 - where: - - "object memory-backend-file,id=mem1,share,mem-path=, size=" - creates a backend file with the specified size. + where: - - "device virtio-pmem-pci,id=nvdimm1,memdev=mem1" creates a virtio pmem - pci device whose storage is provided by above memory backend device. + - "object memory-backend-file,id=mem1,share,mem-path=, size=" + creates a backend file with the specified size. + + - "device virtio-pmem-pci,id=nvdimm1,memdev=mem1" creates a virtio pmem + pci device whose storage is provided by above memory backend device. Multiple virtio pmem devices can be created if multiple pairs of "-object" and "-device" are provided. @@ -50,7 +51,7 @@ memory backing has to be added via 'object_add'; afterwards, the virtio pmem device can be added via 'device_add'. For example, the following commands add another 4GB virtio pmem device to -the guest: +the guest:: (qemu) object_add memory-backend-file,id=mem2,share=on,mem-path=virtio_pmem2.img,size=4G (qemu) device_add virtio-pmem-pci,id=virtio_pmem2,memdev=mem2 diff --git a/docs/xbzrle.txt b/docs/xbzrle.txt index c0a7dfd44c..b431bdaf0f 100644 --- a/docs/xbzrle.txt +++ b/docs/xbzrle.txt @@ -92,6 +92,11 @@ Usage power of 2. The cache default value is 64MBytes. (on source only) {qemu} migrate_set_cache_size 256m +Commit 73af8dd8d7 "migration: Make xbzrle_cache_size a migration parameter" +(v2.11.0) deprecated migrate-set-cache-size, therefore, the new parameter +is recommended. + {qemu} migrate_set_parameter xbzrle-cache-size 256m + 4. Start outgoing migration {qemu} migrate -d tcp:destination.host:4444 {qemu} info migrate @@ -108,7 +113,7 @@ power of 2. The cache default value is 64MBytes. (on source only) xbzrle transferred: I kbytes xbzrle pages: J pages xbzrle cache miss: K - xbzrle overflow : L + xbzrle overflow: L xbzrle cache-miss: the number of cache misses to date - high cache-miss rate indicates that the cache size is set too low. diff --git a/dump/dump-hmp-cmds.c b/dump/dump-hmp-cmds.c index 3dbf44372c..e5053b04cd 100644 --- a/dump/dump-hmp-cmds.c +++ b/dump/dump-hmp-cmds.c @@ -32,7 +32,7 @@ void hmp_dump_guest_memory(Monitor *mon, const QDict *qdict) if (zlib + lzo + snappy + win_dmp > 1) { error_setg(&err, "only one of '-z|-l|-s|-w' can be set"); - hmp_handle_error(mon, &err); + hmp_handle_error(mon, err); return; } @@ -66,7 +66,7 @@ void hmp_dump_guest_memory(Monitor *mon, const QDict *qdict) qmp_dump_guest_memory(paging, prot, true, detach, has_begin, begin, has_length, length, true, dump_format, &err); - hmp_handle_error(mon, &err); + hmp_handle_error(mon, err); g_free(prot); } diff --git a/dump/dump.c b/dump/dump.c index 6fb6e1245a..22ed1d3b0d 100644 --- a/dump/dump.c +++ b/dump/dump.c @@ -364,7 +364,7 @@ static void write_elf_section(DumpState *s, int type, Error **errp) shdr = &shdr64; } - ret = fd_write_vmcore(&shdr, shdr_size, s); + ret = fd_write_vmcore(shdr, shdr_size, s); if (ret < 0) { error_setg_errno(errp, -ret, "dump: failed to write section header table"); diff --git a/dump/win_dump.c b/dump/win_dump.c index eda2a48974..652c7bad99 100644 --- a/dump/win_dump.c +++ b/dump/win_dump.c @@ -304,13 +304,11 @@ static void restore_context(WinDumpHeader64 *h, struct saved_context *saved_ctx) { int i; - Error *err = NULL; for (i = 0; i < h->NumberProcessors; i++) { if (cpu_memory_rw_debug(first_cpu, saved_ctx[i].addr, (uint8_t *)&saved_ctx[i].ctx, sizeof(WinContext), 1)) { - error_setg(&err, "win-dump: failed to restore CPU #%d context", i); - warn_report_err(err); + warn_report("win-dump: failed to restore CPU #%d context", i); } } } diff --git a/exec.c b/exec.c index f3b9c5154b..2874bb5088 100644 --- a/exec.c +++ b/exec.c @@ -25,7 +25,7 @@ #include "cpu.h" #include "exec/exec-all.h" #include "exec/target_page.h" -#include "tcg.h" +#include "tcg/tcg.h" #include "hw/qdev-core.h" #include "hw/qdev-properties.h" #if !defined(CONFIG_USER_ONLY) @@ -35,6 +35,7 @@ #include "sysemu/kvm.h" #include "sysemu/sysemu.h" #include "sysemu/tcg.h" +#include "sysemu/qtest.h" #include "qemu/timer.h" #include "qemu/config-file.h" #include "qemu/error-report.h" @@ -65,6 +66,8 @@ #include "exec/ram_addr.h" #include "exec/log.h" +#include "qemu/pmem.h" + #include "migration/vmstate.h" #include "qemu/range.h" @@ -96,15 +99,15 @@ CPUTailQ cpus = QTAILQ_HEAD_INITIALIZER(cpus); /* current CPU in the current thread. It is only valid inside cpu_exec() */ __thread CPUState *current_cpu; -/* 0 = Do not count executed instructions. - 1 = Precise instruction counting. - 2 = Adaptive rate instruction counting. */ -int use_icount; uintptr_t qemu_host_page_size; intptr_t qemu_host_page_mask; #if !defined(CONFIG_USER_ONLY) +/* 0 = Do not count executed instructions. + 1 = Precise instruction counting. + 2 = Adaptive rate instruction counting. */ +int use_icount; typedef struct PhysPageEntry PhysPageEntry; @@ -1017,14 +1020,13 @@ void tb_invalidate_phys_addr(AddressSpace *as, hwaddr addr, MemTxAttrs attrs) static void breakpoint_invalidate(CPUState *cpu, target_ulong pc) { - MemTxAttrs attrs; - hwaddr phys = cpu_get_phys_page_attrs_debug(cpu, pc, &attrs); - int asidx = cpu_asidx_from_attrs(cpu, attrs); - if (phys != -1) { - /* Locks grabbed by tb_invalidate_phys_addr */ - tb_invalidate_phys_addr(cpu->cpu_ases[asidx].as, - phys | (pc & ~TARGET_PAGE_MASK), attrs); - } + /* + * There may not be a virtual to physical translation for the pc + * right now, but there may exist cached TB for this pc. + * Flush the whole TB cache to force re-translation of such TBs. + * This is heavyweight, but we're debugging anyway. + */ + tb_flush(cpu); } #endif @@ -1223,13 +1225,13 @@ void cpu_abort(CPUState *cpu, const char *fmt, ...) fprintf(stderr, "\n"); cpu_dump_state(cpu, stderr, CPU_DUMP_FPU | CPU_DUMP_CCOP); if (qemu_log_separate()) { - qemu_log_lock(); + FILE *logfile = qemu_log_lock(); qemu_log("qemu: fatal: "); qemu_log_vprintf(fmt, ap2); qemu_log("\n"); log_cpu_state(cpu, CPU_DUMP_FPU | CPU_DUMP_CCOP); qemu_log_flush(); - qemu_log_unlock(); + qemu_log_unlock(logfile); qemu_log_close(); } va_end(ap2); @@ -1667,59 +1669,18 @@ static int find_max_backend_pagesize(Object *obj, void *opaque) long qemu_minrampagesize(void) { long hpsize = LONG_MAX; - long mainrampagesize; - Object *memdev_root; - MachineState *ms = MACHINE(qdev_get_machine()); - - mainrampagesize = qemu_mempath_getpagesize(mem_path); - - /* it's possible we have memory-backend objects with - * hugepage-backed RAM. these may get mapped into system - * address space via -numa parameters or memory hotplug - * hooks. we want to take these into account, but we - * also want to make sure these supported hugepage - * sizes are applicable across the entire range of memory - * we may boot from, so we take the min across all - * backends, and assume normal pages in cases where a - * backend isn't backed by hugepages. - */ - memdev_root = object_resolve_path("/objects", NULL); - if (memdev_root) { - object_child_foreach(memdev_root, find_min_backend_pagesize, &hpsize); - } - if (hpsize == LONG_MAX) { - /* No additional memory regions found ==> Report main RAM page size */ - return mainrampagesize; - } - - /* If NUMA is disabled or the NUMA nodes are not backed with a - * memory-backend, then there is at least one node using "normal" RAM, - * so if its page size is smaller we have got to report that size instead. - */ - if (hpsize > mainrampagesize && - (ms->numa_state == NULL || - ms->numa_state->num_nodes == 0 || - ms->numa_state->nodes[0].node_memdev == NULL)) { - static bool warned; - if (!warned) { - error_report("Huge page support disabled (n/a for main memory)."); - warned = true; - } - return mainrampagesize; - } + Object *memdev_root = object_resolve_path("/objects", NULL); + object_child_foreach(memdev_root, find_min_backend_pagesize, &hpsize); return hpsize; } long qemu_maxrampagesize(void) { - long pagesize = qemu_mempath_getpagesize(mem_path); + long pagesize = 0; Object *memdev_root = object_resolve_path("/objects", NULL); - if (memdev_root) { - object_child_foreach(memdev_root, find_max_backend_pagesize, - &pagesize); - } + object_child_foreach(memdev_root, find_max_backend_pagesize, &pagesize); return pagesize; } #else @@ -1842,7 +1803,6 @@ static void *file_ram_alloc(RAMBlock *block, bool truncate, Error **errp) { - MachineState *ms = MACHINE(qdev_get_machine()); void *area; block->page_size = qemu_fd_getpagesize(fd); @@ -1898,14 +1858,6 @@ static void *file_ram_alloc(RAMBlock *block, return NULL; } - if (mem_prealloc) { - os_mem_prealloc(fd, area, memory, ms->smp.cpus, errp); - if (errp && *errp) { - qemu_ram_munmap(fd, area, memory); - return NULL; - } - } - block->fd = fd; return area; } @@ -2122,11 +2074,23 @@ static int memory_try_enable_merging(void *addr, size_t len) */ int qemu_ram_resize(RAMBlock *block, ram_addr_t newsize, Error **errp) { + const ram_addr_t unaligned_size = newsize; + assert(block); newsize = HOST_PAGE_ALIGN(newsize); if (block->used_length == newsize) { + /* + * We don't have to resize the ram block (which only knows aligned + * sizes), however, we have to notify if the unaligned size changed. + */ + if (unaligned_size != memory_region_size(block->mr)) { + memory_region_set_size(block->mr, unaligned_size); + if (block->resized) { + block->resized(block->idstr, unaligned_size, block->host); + } + } return 0; } @@ -2150,13 +2114,47 @@ int qemu_ram_resize(RAMBlock *block, ram_addr_t newsize, Error **errp) block->used_length = newsize; cpu_physical_memory_set_dirty_range(block->offset, block->used_length, DIRTY_CLIENTS_ALL); - memory_region_set_size(block->mr, newsize); + memory_region_set_size(block->mr, unaligned_size); if (block->resized) { - block->resized(block->idstr, newsize, block->host); + block->resized(block->idstr, unaligned_size, block->host); } return 0; } +/* + * Trigger sync on the given ram block for range [start, start + length] + * with the backing store if one is available. + * Otherwise no-op. + * @Note: this is supposed to be a synchronous op. + */ +void qemu_ram_writeback(RAMBlock *block, ram_addr_t start, ram_addr_t length) +{ + /* The requested range should fit in within the block range */ + g_assert((start + length) <= block->used_length); + +#ifdef CONFIG_LIBPMEM + /* The lack of support for pmem should not block the sync */ + if (ramblock_is_pmem(block)) { + void *addr = ramblock_ptr(block, start); + pmem_persist(addr, length); + return; + } +#endif + if (block->fd >= 0) { + /** + * Case there is no support for PMEM or the memory has not been + * specified as persistent (or is not one) - use the msync. + * Less optimal but still achieves the same goal + */ + void *addr = ramblock_ptr(block, start); + if (qemu_msync(addr, length, block->fd)) { + warn_report("%s: failed to sync memory range: start: " + RAM_ADDR_FMT " length: " RAM_ADDR_FMT, + __func__, start, length); + } + } +} + /* Called with ram_list.mutex held */ static void dirty_memory_extend(ram_addr_t old_ram_size, ram_addr_t new_ram_size) @@ -2269,8 +2267,15 @@ static void ram_block_add(RAMBlock *new_block, Error **errp, bool shared) if (new_block->host) { qemu_ram_setup_dump(new_block->host, new_block->max_length); qemu_madvise(new_block->host, new_block->max_length, QEMU_MADV_HUGEPAGE); - /* MADV_DONTFORK is also needed by KVM in absence of synchronous MMU */ - qemu_madvise(new_block->host, new_block->max_length, QEMU_MADV_DONTFORK); + /* + * MADV_DONTFORK is also needed by KVM in absence of synchronous MMU + * Configure it unless the machine is a qtest server, in which case + * KVM is not used and it may be forked (eg for fuzzing purposes). + */ + if (!qtest_enabled()) { + qemu_madvise(new_block->host, new_block->max_length, + QEMU_MADV_DONTFORK); + } ram_block_notify_add(new_block->host, new_block->max_length); } } @@ -2312,9 +2317,9 @@ RAMBlock *qemu_ram_alloc_from_fd(ram_addr_t size, MemoryRegion *mr, size = HOST_PAGE_ALIGN(size); file_size = get_file_size(fd); if (file_size > 0 && file_size < size) { - error_setg(errp, "backing store %s size 0x%" PRIx64 + error_setg(errp, "backing store size 0x%" PRIx64 " does not match 'size' option 0x" RAM_ADDR_FMT, - mem_path, file_size, size); + file_size, size); return NULL; } @@ -2744,9 +2749,9 @@ void cpu_check_watchpoint(CPUState *cpu, vaddr addr, vaddr len, } static MemTxResult flatview_read(FlatView *fv, hwaddr addr, - MemTxAttrs attrs, uint8_t *buf, hwaddr len); + MemTxAttrs attrs, void *buf, hwaddr len); static MemTxResult flatview_write(FlatView *fv, hwaddr addr, MemTxAttrs attrs, - const uint8_t *buf, hwaddr len); + const void *buf, hwaddr len); static bool flatview_access_valid(FlatView *fv, hwaddr addr, hwaddr len, bool is_write, MemTxAttrs attrs); @@ -2983,11 +2988,12 @@ MemoryRegion *get_system_io(void) /* physical memory access (slow version, mainly for debug) */ #if defined(CONFIG_USER_ONLY) int cpu_memory_rw_debug(CPUState *cpu, target_ulong addr, - uint8_t *buf, target_ulong len, int is_write) + void *ptr, target_ulong len, bool is_write) { int flags; target_ulong l, page; void * p; + uint8_t *buf = ptr; while (len > 0) { page = addr & TARGET_PAGE_MASK; @@ -3111,14 +3117,15 @@ static bool prepare_mmio_access(MemoryRegion *mr) /* Called within RCU critical section. */ static MemTxResult flatview_write_continue(FlatView *fv, hwaddr addr, MemTxAttrs attrs, - const uint8_t *buf, + const void *ptr, hwaddr len, hwaddr addr1, hwaddr l, MemoryRegion *mr) { - uint8_t *ptr; + uint8_t *ram_ptr; uint64_t val; MemTxResult result = MEMTX_OK; bool release_lock = false; + const uint8_t *buf = ptr; for (;;) { if (!memory_access_is_direct(mr, true)) { @@ -3131,8 +3138,8 @@ static MemTxResult flatview_write_continue(FlatView *fv, hwaddr addr, size_memop(l), attrs); } else { /* RAM case */ - ptr = qemu_ram_ptr_length(mr->ram_block, addr1, &l, false); - memcpy(ptr, buf, l); + ram_ptr = qemu_ram_ptr_length(mr->ram_block, addr1, &l, false); + memcpy(ram_ptr, buf, l); invalidate_and_set_dirty(mr, addr1, l); } @@ -3158,7 +3165,7 @@ static MemTxResult flatview_write_continue(FlatView *fv, hwaddr addr, /* Called from RCU critical section. */ static MemTxResult flatview_write(FlatView *fv, hwaddr addr, MemTxAttrs attrs, - const uint8_t *buf, hwaddr len) + const void *buf, hwaddr len) { hwaddr l; hwaddr addr1; @@ -3175,14 +3182,15 @@ static MemTxResult flatview_write(FlatView *fv, hwaddr addr, MemTxAttrs attrs, /* Called within RCU critical section. */ MemTxResult flatview_read_continue(FlatView *fv, hwaddr addr, - MemTxAttrs attrs, uint8_t *buf, + MemTxAttrs attrs, void *ptr, hwaddr len, hwaddr addr1, hwaddr l, MemoryRegion *mr) { - uint8_t *ptr; + uint8_t *ram_ptr; uint64_t val; MemTxResult result = MEMTX_OK; bool release_lock = false; + uint8_t *buf = ptr; for (;;) { if (!memory_access_is_direct(mr, false)) { @@ -3194,8 +3202,8 @@ MemTxResult flatview_read_continue(FlatView *fv, hwaddr addr, stn_he_p(buf, l, val); } else { /* RAM case */ - ptr = qemu_ram_ptr_length(mr->ram_block, addr1, &l, false); - memcpy(buf, ptr, l); + ram_ptr = qemu_ram_ptr_length(mr->ram_block, addr1, &l, false); + memcpy(buf, ram_ptr, l); } if (release_lock) { @@ -3220,7 +3228,7 @@ MemTxResult flatview_read_continue(FlatView *fv, hwaddr addr, /* Called from RCU critical section. */ static MemTxResult flatview_read(FlatView *fv, hwaddr addr, - MemTxAttrs attrs, uint8_t *buf, hwaddr len) + MemTxAttrs attrs, void *buf, hwaddr len) { hwaddr l; hwaddr addr1; @@ -3233,7 +3241,7 @@ static MemTxResult flatview_read(FlatView *fv, hwaddr addr, } MemTxResult address_space_read_full(AddressSpace *as, hwaddr addr, - MemTxAttrs attrs, uint8_t *buf, hwaddr len) + MemTxAttrs attrs, void *buf, hwaddr len) { MemTxResult result = MEMTX_OK; FlatView *fv; @@ -3249,7 +3257,7 @@ MemTxResult address_space_read_full(AddressSpace *as, hwaddr addr, MemTxResult address_space_write(AddressSpace *as, hwaddr addr, MemTxAttrs attrs, - const uint8_t *buf, hwaddr len) + const void *buf, hwaddr len) { MemTxResult result = MEMTX_OK; FlatView *fv; @@ -3264,7 +3272,7 @@ MemTxResult address_space_write(AddressSpace *as, hwaddr addr, } MemTxResult address_space_rw(AddressSpace *as, hwaddr addr, MemTxAttrs attrs, - uint8_t *buf, hwaddr len, bool is_write) + void *buf, hwaddr len, bool is_write) { if (is_write) { return address_space_write(as, addr, attrs, buf, len); @@ -3273,8 +3281,8 @@ MemTxResult address_space_rw(AddressSpace *as, hwaddr addr, MemTxAttrs attrs, } } -void cpu_physical_memory_rw(hwaddr addr, uint8_t *buf, - hwaddr len, int is_write) +void cpu_physical_memory_rw(hwaddr addr, void *buf, + hwaddr len, bool is_write) { address_space_rw(&address_space_memory, addr, MEMTXATTRS_UNSPECIFIED, buf, len, is_write); @@ -3288,14 +3296,15 @@ enum write_rom_type { static inline MemTxResult address_space_write_rom_internal(AddressSpace *as, hwaddr addr, MemTxAttrs attrs, - const uint8_t *buf, + const void *ptr, hwaddr len, enum write_rom_type type) { hwaddr l; - uint8_t *ptr; + uint8_t *ram_ptr; hwaddr addr1; MemoryRegion *mr; + const uint8_t *buf = ptr; RCU_READ_LOCK_GUARD(); while (len > 0) { @@ -3307,14 +3316,14 @@ static inline MemTxResult address_space_write_rom_internal(AddressSpace *as, l = memory_access_size(mr, l, addr1); } else { /* ROM/RAM case */ - ptr = qemu_map_ram_ptr(mr->ram_block, addr1); + ram_ptr = qemu_map_ram_ptr(mr->ram_block, addr1); switch (type) { case WRITE_DATA: - memcpy(ptr, buf, l); + memcpy(ram_ptr, buf, l); invalidate_and_set_dirty(mr, addr1, l); break; case FLUSH_CACHE: - flush_icache_range((uintptr_t)ptr, (uintptr_t)ptr + l); + flush_icache_range((uintptr_t)ram_ptr, (uintptr_t)ram_ptr + l); break; } } @@ -3328,7 +3337,7 @@ static inline MemTxResult address_space_write_rom_internal(AddressSpace *as, /* used for ROM loading : can write in RAM and ROM */ MemTxResult address_space_write_rom(AddressSpace *as, hwaddr addr, MemTxAttrs attrs, - const uint8_t *buf, hwaddr len) + const void *buf, hwaddr len) { return address_space_write_rom_internal(as, addr, attrs, buf, len, WRITE_DATA); @@ -3558,11 +3567,11 @@ void *address_space_map(AddressSpace *as, } /* Unmaps a memory region previously mapped by address_space_map(). - * Will also mark the memory as dirty if is_write == 1. access_len gives + * Will also mark the memory as dirty if is_write is true. access_len gives * the amount of memory that was actually read or written by the caller. */ void address_space_unmap(AddressSpace *as, void *buffer, hwaddr len, - int is_write, hwaddr access_len) + bool is_write, hwaddr access_len) { if (buffer != bounce.buffer) { MemoryRegion *mr; @@ -3592,14 +3601,14 @@ void address_space_unmap(AddressSpace *as, void *buffer, hwaddr len, void *cpu_physical_memory_map(hwaddr addr, hwaddr *plen, - int is_write) + bool is_write) { return address_space_map(&address_space_memory, addr, plen, is_write, MEMTXATTRS_UNSPECIFIED); } void cpu_physical_memory_unmap(void *buffer, hwaddr len, - int is_write, hwaddr access_len) + bool is_write, hwaddr access_len) { return address_space_unmap(&address_space_memory, buffer, len, is_write, access_len); } @@ -3750,10 +3759,11 @@ address_space_write_cached_slow(MemoryRegionCache *cache, hwaddr addr, /* virtual memory access for debug (includes writing to ROM) */ int cpu_memory_rw_debug(CPUState *cpu, target_ulong addr, - uint8_t *buf, target_ulong len, int is_write) + void *ptr, target_ulong len, bool is_write) { hwaddr phys_addr; target_ulong l, page; + uint8_t *buf = ptr; cpu_synchronize_state(cpu); while (len > 0) { @@ -3774,8 +3784,8 @@ int cpu_memory_rw_debug(CPUState *cpu, target_ulong addr, address_space_write_rom(cpu->cpu_ases[asidx].as, phys_addr, attrs, buf, l); } else { - address_space_rw(cpu->cpu_ases[asidx].as, phys_addr, - attrs, buf, l, 0); + address_space_read(cpu->cpu_ases[asidx].as, phys_addr, attrs, buf, + l); } len -= l; buf += l; @@ -3858,7 +3868,7 @@ int ram_block_discard_range(RAMBlock *rb, uint64_t start, size_t length) uint8_t *host_startaddr = rb->host + start; - if ((uintptr_t)host_startaddr & (rb->page_size - 1)) { + if (!QEMU_PTR_IS_ALIGNED(host_startaddr, rb->page_size)) { error_report("ram_block_discard_range: Unaligned start address: %p", host_startaddr); goto err; @@ -3866,10 +3876,9 @@ int ram_block_discard_range(RAMBlock *rb, uint64_t start, size_t length) if ((start + length) <= rb->used_length) { bool need_madvise, need_fallocate; - uint8_t *host_endaddr = host_startaddr + length; - if ((uintptr_t)host_endaddr & (rb->page_size - 1)) { - error_report("ram_block_discard_range: Unaligned end address: %p", - host_endaddr); + if (!QEMU_IS_ALIGNED(length, rb->page_size)) { + error_report("ram_block_discard_range: Unaligned length: %zx", + length); goto err; } diff --git a/fpu/softfloat.c b/fpu/softfloat.c index 301ce3b537..ae6ba71854 100644 --- a/fpu/softfloat.c +++ b/fpu/softfloat.c @@ -5856,6 +5856,9 @@ static floatx80 addFloatx80Sigs(floatx80 a, floatx80 b, flag zSign, zSig1 = 0; zSig0 = aSig + bSig; if ( aExp == 0 ) { + if (zSig0 == 0) { + return packFloatx80(zSign, 0, 0); + } normalizeFloatx80Subnormal( zSig0, &zExp, &zSig0 ); goto roundAndPack; } diff --git a/fsdev/virtfs-proxy-helper.c b/fsdev/virtfs-proxy-helper.c index 6f132c5ff1..de061a8a0e 100644 --- a/fsdev/virtfs-proxy-helper.c +++ b/fsdev/virtfs-proxy-helper.c @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include @@ -21,6 +20,7 @@ #ifdef CONFIG_LINUX_MAGIC_H #include #endif +#include #include "qemu-common.h" #include "qemu/sockets.h" #include "qemu/xattr.h" @@ -43,7 +43,7 @@ #define BTRFS_SUPER_MAGIC 0x9123683E #endif -static struct option helper_opts[] = { +static const struct option helper_opts[] = { {"fd", required_argument, NULL, 'f'}, {"path", required_argument, NULL, 'p'}, {"nodaemon", no_argument, NULL, 'n'}, @@ -79,49 +79,10 @@ static void do_perror(const char *string) } } -static int do_cap_set(cap_value_t *cap_value, int size, int reset) -{ - cap_t caps; - if (reset) { - /* - * Start with an empty set and set permitted and effective - */ - caps = cap_init(); - if (caps == NULL) { - do_perror("cap_init"); - return -1; - } - if (cap_set_flag(caps, CAP_PERMITTED, size, cap_value, CAP_SET) < 0) { - do_perror("cap_set_flag"); - goto error; - } - } else { - caps = cap_get_proc(); - if (!caps) { - do_perror("cap_get_proc"); - return -1; - } - } - if (cap_set_flag(caps, CAP_EFFECTIVE, size, cap_value, CAP_SET) < 0) { - do_perror("cap_set_flag"); - goto error; - } - if (cap_set_proc(caps) < 0) { - do_perror("cap_set_proc"); - goto error; - } - cap_free(caps); - return 0; - -error: - cap_free(caps); - return -1; -} - static int init_capabilities(void) { /* helper needs following capabilities only */ - cap_value_t cap_list[] = { + int cap_list[] = { CAP_CHOWN, CAP_DAC_OVERRIDE, CAP_FOWNER, @@ -130,7 +91,34 @@ static int init_capabilities(void) CAP_MKNOD, CAP_SETUID, }; - return do_cap_set(cap_list, ARRAY_SIZE(cap_list), 1); + int i; + + capng_clear(CAPNG_SELECT_BOTH); + for (i = 0; i < ARRAY_SIZE(cap_list); i++) { + if (capng_update(CAPNG_ADD, CAPNG_EFFECTIVE | CAPNG_PERMITTED, + cap_list[i]) < 0) { + do_perror("capng_update"); + return -1; + } + } + if (capng_apply(CAPNG_SELECT_BOTH) < 0) { + do_perror("capng_apply"); + return -1; + } + + /* Prepare effective set for setugid. */ + for (i = 0; i < ARRAY_SIZE(cap_list); i++) { + if (cap_list[i] == CAP_DAC_OVERRIDE) { + continue; + } + + if (capng_update(CAPNG_DROP, CAPNG_EFFECTIVE, + cap_list[i]) < 0) { + do_perror("capng_update"); + return -1; + } + } + return 0; } static int socket_read(int sockfd, void *buff, ssize_t size) @@ -295,20 +283,11 @@ static int setugid(int uid, int gid, int *suid, int *sgid) { int retval; - /* - * We still need DAC_OVERRIDE because we don't change - * supplementary group ids, and hence may be subjected DAC rules - */ - cap_value_t cap_list[] = { - CAP_DAC_OVERRIDE, - }; - *suid = geteuid(); *sgid = getegid(); if (setresgid(-1, gid, *sgid) == -1) { - retval = -errno; - goto err_out; + return -errno; } if (setresuid(-1, uid, *suid) == -1) { @@ -316,11 +295,21 @@ static int setugid(int uid, int gid, int *suid, int *sgid) goto err_sgid; } - if (uid != 0 || gid != 0) { - if (do_cap_set(cap_list, ARRAY_SIZE(cap_list), 0) < 0) { - retval = -errno; - goto err_suid; - } + if (uid == 0 && gid == 0) { + /* Linux has already copied the permitted set to the effective set. */ + return 0; + } + + /* + * All capabilities have been cleared from the effective set. However + * we still need DAC_OVERRIDE because we don't change supplementary + * group ids, and hence may be subject to DAC rules. init_capabilities + * left the set of capabilities that we want in libcap-ng's state. + */ + if (capng_apply(CAPNG_SELECT_CAPS) < 0) { + retval = -errno; + do_perror("capng_apply"); + goto err_suid; } return 0; @@ -332,7 +321,6 @@ err_sgid: if (setresgid(-1, *sgid, *sgid) == -1) { abort(); } -err_out: return retval; } diff --git a/fsdev/virtfs-proxy-helper.texi b/fsdev/virtfs-proxy-helper.texi deleted file mode 100644 index f4cbb60623..0000000000 --- a/fsdev/virtfs-proxy-helper.texi +++ /dev/null @@ -1,63 +0,0 @@ -@example -@c man begin SYNOPSIS -@command{virtfs-proxy-helper} @var{options} -@c man end -@end example - -@c man begin DESCRIPTION -@table @description -Pass-through security model in QEMU 9p server needs root privilege to do -few file operations (like chown, chmod to any mode/uid:gid). There are two -issues in pass-through security model - -1) TOCTTOU vulnerability: Following symbolic links in the server could -provide access to files beyond 9p export path. - -2) Running QEMU with root privilege could be a security issue. - -To overcome above issues, following approach is used: A new filesystem -type 'proxy' is introduced. Proxy FS uses chroot + socket combination -for securing the vulnerability known with following symbolic links. -Intention of adding a new filesystem type is to allow qemu to run -in non-root mode, but doing privileged operations using socket IO. - -Proxy helper(a stand alone binary part of qemu) is invoked with -root privileges. Proxy helper chroots into 9p export path and creates -a socket pair or a named socket based on the command line parameter. -QEMU and proxy helper communicate using this socket. QEMU proxy fs -driver sends filesystem request to proxy helper and receives the -response from it. - -The proxy helper is designed so that it can drop root privileges except -for the capabilities needed for doing filesystem operations. - -@end table -@c man end - -@c man begin OPTIONS -The following options are supported: -@table @option -@item -h -@findex -h -Display help and exit -@item -p|--path path -Path to export for proxy filesystem driver -@item -f|--fd socket-id -Use given file descriptor as socket descriptor for communicating with -qemu proxy fs drier. Usually a helper like libvirt will create -socketpair and pass one of the fds as parameter to -f|--fd -@item -s|--socket socket-file -Creates named socket file for communicating with qemu proxy fs driver -@item -u|--uid uid -g|--gid gid -uid:gid combination to give access to named socket file -@item -n|--nodaemon -Run as a normal program. By default program will run in daemon mode -@end table -@c man end - -@setfilename virtfs-proxy-helper -@settitle QEMU 9p virtfs proxy filesystem helper - -@c man begin AUTHOR -M. Mohan Kumar -@c man end diff --git a/gdb-xml/rx-core.xml b/gdb-xml/rx-core.xml new file mode 100644 index 0000000000..b5aa9ac4a8 --- /dev/null +++ b/gdb-xml/rx-core.xml @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/gdbstub.c b/gdbstub.c index 4cf8af365e..171e150950 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -319,8 +319,8 @@ static int gdb_signal_to_target (int sig) typedef struct GDBRegisterState { int base_reg; int num_regs; - gdb_reg_cb get_reg; - gdb_reg_cb set_reg; + gdb_get_reg_cb get_reg; + gdb_set_reg_cb set_reg; const char *xml; struct GDBRegisterState *next; } GDBRegisterState; @@ -342,6 +342,7 @@ enum RSState { RS_CHKSUM2, }; typedef struct GDBState { + bool init; /* have we been initialised? */ CPUState *c_cpu; /* current CPU for step/continue ops */ CPUState *g_cpu; /* current CPU for other ops */ CPUState *query_cpu; /* for q{f|s}ThreadInfo */ @@ -350,8 +351,7 @@ typedef struct GDBState { int line_buf_index; int line_sum; /* running checksum */ int line_csum; /* checksum at the end of the packet */ - uint8_t last_packet[MAX_PACKET_LENGTH + 4]; - int last_packet_len; + GByteArray *last_packet; int signal; #ifdef CONFIG_USER_ONLY int fd; @@ -365,6 +365,8 @@ typedef struct GDBState { int process_num; char syscall_buf[256]; gdb_syscall_complete_cb current_syscall_cb; + GString *str_buf; + GByteArray *mem_buf; } GDBState; /* By default use no IRQs and no timers while single stepping so as to @@ -372,7 +374,26 @@ typedef struct GDBState { */ static int sstep_flags = SSTEP_ENABLE|SSTEP_NOIRQ|SSTEP_NOTIMER; -static GDBState *gdbserver_state; +static GDBState gdbserver_state; + +static void init_gdbserver_state(void) +{ + g_assert(!gdbserver_state.init); + memset(&gdbserver_state, 0, sizeof(GDBState)); + gdbserver_state.init = true; + gdbserver_state.str_buf = g_string_new(NULL); + gdbserver_state.mem_buf = g_byte_array_sized_new(MAX_PACKET_LENGTH); + gdbserver_state.last_packet = g_byte_array_sized_new(MAX_PACKET_LENGTH + 4); +} + +#ifndef CONFIG_USER_ONLY +static void reset_gdbserver_state(void) +{ + g_free(gdbserver_state.processes); + gdbserver_state.processes = NULL; + gdbserver_state.process_num = 0; +} +#endif bool gdb_has_xml; @@ -380,21 +401,21 @@ bool gdb_has_xml; /* XXX: This is not thread safe. Do we care? */ static int gdbserver_fd = -1; -static int get_char(GDBState *s) +static int get_char(void) { uint8_t ch; int ret; for(;;) { - ret = qemu_recv(s->fd, &ch, 1, 0); + ret = qemu_recv(gdbserver_state.fd, &ch, 1, 0); if (ret < 0) { if (errno == ECONNRESET) - s->fd = -1; + gdbserver_state.fd = -1; if (errno != EINTR) return -1; } else if (ret == 0) { - close(s->fd); - s->fd = -1; + close(gdbserver_state.fd); + gdbserver_state.fd = -1; return -1; } else { break; @@ -425,18 +446,18 @@ int use_gdb_syscalls(void) /* -semihosting-config target=auto */ /* On the first call check if gdb is connected and remember. */ if (gdb_syscall_mode == GDB_SYS_UNKNOWN) { - gdb_syscall_mode = (gdbserver_state ? GDB_SYS_ENABLED - : GDB_SYS_DISABLED); + gdb_syscall_mode = gdbserver_state.init ? + GDB_SYS_ENABLED : GDB_SYS_DISABLED; } return gdb_syscall_mode == GDB_SYS_ENABLED; } /* Resume execution. */ -static inline void gdb_continue(GDBState *s) +static inline void gdb_continue(void) { #ifdef CONFIG_USER_ONLY - s->running_state = 1; + gdbserver_state.running_state = 1; trace_gdbstub_op_continue(); #else if (!runstate_needs_reset()) { @@ -450,7 +471,7 @@ static inline void gdb_continue(GDBState *s) * Resume execution, per CPU actions. For user-mode emulation it's * equivalent to gdb_continue. */ -static int gdb_continue_partial(GDBState *s, char *newstates) +static int gdb_continue_partial(char *newstates) { CPUState *cpu; int res = 0; @@ -465,7 +486,7 @@ static int gdb_continue_partial(GDBState *s, char *newstates) cpu_single_step(cpu, sstep_flags); } } - s->running_state = 1; + gdbserver_state.running_state = 1; #else int flag = 0; @@ -503,13 +524,13 @@ static int gdb_continue_partial(GDBState *s, char *newstates) return res; } -static void put_buffer(GDBState *s, const uint8_t *buf, int len) +static void put_buffer(const uint8_t *buf, int len) { #ifdef CONFIG_USER_ONLY int ret; while (len > 0) { - ret = send(s->fd, buf, len, 0); + ret = send(gdbserver_state.fd, buf, len, 0); if (ret < 0) { if (errno != EINTR) return; @@ -521,7 +542,7 @@ static void put_buffer(GDBState *s, const uint8_t *buf, int len) #else /* XXX this blocks entire thread. Rewrite to use * qemu_chr_fe_write and background I/O callbacks */ - qemu_chr_fe_write_all(&s->chr, buf, len); + qemu_chr_fe_write_all(&gdbserver_state.chr, buf, len); #endif } @@ -546,25 +567,24 @@ static inline int tohex(int v) } /* writes 2*len+1 bytes in buf */ -static void memtohex(char *buf, const uint8_t *mem, int len) +static void memtohex(GString *buf, const uint8_t *mem, int len) { int i, c; - char *q; - q = buf; for(i = 0; i < len; i++) { c = mem[i]; - *q++ = tohex(c >> 4); - *q++ = tohex(c & 0xf); + g_string_append_c(buf, tohex(c >> 4)); + g_string_append_c(buf, tohex(c & 0xf)); } - *q = '\0'; + g_string_append_c(buf, '\0'); } -static void hextomem(uint8_t *mem, const char *buf, int len) +static void hextomem(GByteArray *mem, const char *buf, int len) { int i; for(i = 0; i < len; i++) { - mem[i] = (fromhex(buf[0]) << 4) | fromhex(buf[1]); + guint8 byte = fromhex(buf[0]) << 4 | fromhex(buf[1]); + g_byte_array_append(mem, &byte, 1); buf += 2; } } @@ -603,33 +623,35 @@ static void hexdump(const char *buf, int len, } /* return -1 if error, 0 if OK */ -static int put_packet_binary(GDBState *s, const char *buf, int len, bool dump) +static int put_packet_binary(const char *buf, int len, bool dump) { int csum, i; - uint8_t *p; + uint8_t footer[3]; if (dump && trace_event_get_state_backends(TRACE_GDBSTUB_IO_BINARYREPLY)) { hexdump(buf, len, trace_gdbstub_io_binaryreply); } for(;;) { - p = s->last_packet; - *(p++) = '$'; - memcpy(p, buf, len); - p += len; + g_byte_array_set_size(gdbserver_state.last_packet, 0); + g_byte_array_append(gdbserver_state.last_packet, + (const uint8_t *) "$", 1); + g_byte_array_append(gdbserver_state.last_packet, + (const uint8_t *) buf, len); csum = 0; for(i = 0; i < len; i++) { csum += buf[i]; } - *(p++) = '#'; - *(p++) = tohex((csum >> 4) & 0xf); - *(p++) = tohex((csum) & 0xf); + footer[0] = '#'; + footer[1] = tohex((csum >> 4) & 0xf); + footer[2] = tohex((csum) & 0xf); + g_byte_array_append(gdbserver_state.last_packet, footer, 3); - s->last_packet_len = p - s->last_packet; - put_buffer(s, (uint8_t *)s->last_packet, s->last_packet_len); + put_buffer(gdbserver_state.last_packet->data, + gdbserver_state.last_packet->len); #ifdef CONFIG_USER_ONLY - i = get_char(s); + i = get_char(); if (i < 0) return -1; if (i == '+') @@ -642,65 +664,69 @@ static int put_packet_binary(GDBState *s, const char *buf, int len, bool dump) } /* return -1 if error, 0 if OK */ -static int put_packet(GDBState *s, const char *buf) +static int put_packet(const char *buf) { trace_gdbstub_io_reply(buf); - return put_packet_binary(s, buf, strlen(buf), false); + return put_packet_binary(buf, strlen(buf), false); +} + +static void put_strbuf(void) +{ + put_packet(gdbserver_state.str_buf->str); } /* Encode data using the encoding for 'x' packets. */ -static int memtox(char *buf, const char *mem, int len) +static void memtox(GString *buf, const char *mem, int len) { - char *p = buf; char c; while (len--) { c = *(mem++); switch (c) { case '#': case '$': case '*': case '}': - *(p++) = '}'; - *(p++) = c ^ 0x20; + g_string_append_c(buf, '}'); + g_string_append_c(buf, c ^ 0x20); break; default: - *(p++) = c; + g_string_append_c(buf, c); break; } } - return p - buf; } -static uint32_t gdb_get_cpu_pid(const GDBState *s, CPUState *cpu) +static uint32_t gdb_get_cpu_pid(CPUState *cpu) { /* TODO: In user mode, we should use the task state PID */ if (cpu->cluster_index == UNASSIGNED_CLUSTER_INDEX) { /* Return the default process' PID */ - return s->processes[s->process_num - 1].pid; + int index = gdbserver_state.process_num - 1; + return gdbserver_state.processes[index].pid; } return cpu->cluster_index + 1; } -static GDBProcess *gdb_get_process(const GDBState *s, uint32_t pid) +static GDBProcess *gdb_get_process(uint32_t pid) { int i; if (!pid) { /* 0 means any process, we take the first one */ - return &s->processes[0]; + return &gdbserver_state.processes[0]; } - for (i = 0; i < s->process_num; i++) { - if (s->processes[i].pid == pid) { - return &s->processes[i]; + for (i = 0; i < gdbserver_state.process_num; i++) { + if (gdbserver_state.processes[i].pid == pid) { + return &gdbserver_state.processes[i]; } } return NULL; } -static GDBProcess *gdb_get_cpu_process(const GDBState *s, CPUState *cpu) +static GDBProcess *gdb_get_cpu_process(CPUState *cpu) { - return gdb_get_process(s, gdb_get_cpu_pid(s, cpu)); + return gdb_get_process(gdb_get_cpu_pid(cpu)); } static CPUState *find_cpu(uint32_t thread_id) @@ -716,13 +742,12 @@ static CPUState *find_cpu(uint32_t thread_id) return NULL; } -static CPUState *get_first_cpu_in_process(const GDBState *s, - GDBProcess *process) +static CPUState *get_first_cpu_in_process(GDBProcess *process) { CPUState *cpu; CPU_FOREACH(cpu) { - if (gdb_get_cpu_pid(s, cpu) == process->pid) { + if (gdb_get_cpu_pid(cpu) == process->pid) { return cpu; } } @@ -730,13 +755,13 @@ static CPUState *get_first_cpu_in_process(const GDBState *s, return NULL; } -static CPUState *gdb_next_cpu_in_process(const GDBState *s, CPUState *cpu) +static CPUState *gdb_next_cpu_in_process(CPUState *cpu) { - uint32_t pid = gdb_get_cpu_pid(s, cpu); + uint32_t pid = gdb_get_cpu_pid(cpu); cpu = CPU_NEXT(cpu); while (cpu) { - if (gdb_get_cpu_pid(s, cpu) == pid) { + if (gdb_get_cpu_pid(cpu) == pid) { break; } @@ -747,12 +772,12 @@ static CPUState *gdb_next_cpu_in_process(const GDBState *s, CPUState *cpu) } /* Return the cpu following @cpu, while ignoring unattached processes. */ -static CPUState *gdb_next_attached_cpu(const GDBState *s, CPUState *cpu) +static CPUState *gdb_next_attached_cpu(CPUState *cpu) { cpu = CPU_NEXT(cpu); while (cpu) { - if (gdb_get_cpu_process(s, cpu)->attached) { + if (gdb_get_cpu_process(cpu)->attached) { break; } @@ -763,29 +788,29 @@ static CPUState *gdb_next_attached_cpu(const GDBState *s, CPUState *cpu) } /* Return the first attached cpu */ -static CPUState *gdb_first_attached_cpu(const GDBState *s) +static CPUState *gdb_first_attached_cpu(void) { CPUState *cpu = first_cpu; - GDBProcess *process = gdb_get_cpu_process(s, cpu); + GDBProcess *process = gdb_get_cpu_process(cpu); if (!process->attached) { - return gdb_next_attached_cpu(s, cpu); + return gdb_next_attached_cpu(cpu); } return cpu; } -static CPUState *gdb_get_cpu(const GDBState *s, uint32_t pid, uint32_t tid) +static CPUState *gdb_get_cpu(uint32_t pid, uint32_t tid) { GDBProcess *process; CPUState *cpu; if (!pid && !tid) { /* 0 means any process/thread, we take the first attached one */ - return gdb_first_attached_cpu(s); + return gdb_first_attached_cpu(); } else if (pid && !tid) { /* any thread in a specific process */ - process = gdb_get_process(s, pid); + process = gdb_get_process(pid); if (process == NULL) { return NULL; @@ -795,7 +820,7 @@ static CPUState *gdb_get_cpu(const GDBState *s, uint32_t pid, uint32_t tid) return NULL; } - return get_first_cpu_in_process(s, process); + return get_first_cpu_in_process(process); } else { /* a specific thread */ cpu = find_cpu(tid); @@ -804,7 +829,7 @@ static CPUState *gdb_get_cpu(const GDBState *s, uint32_t pid, uint32_t tid) return NULL; } - process = gdb_get_cpu_process(s, cpu); + process = gdb_get_cpu_process(cpu); if (pid && process->pid != pid) { return NULL; @@ -818,13 +843,13 @@ static CPUState *gdb_get_cpu(const GDBState *s, uint32_t pid, uint32_t tid) } } -static const char *get_feature_xml(const GDBState *s, const char *p, - const char **newp, GDBProcess *process) +static const char *get_feature_xml(const char *p, const char **newp, + GDBProcess *process) { size_t len; int i; const char *name; - CPUState *cpu = get_first_cpu_in_process(s, process); + CPUState *cpu = get_first_cpu_in_process(process); CPUClass *cc = CPU_GET_CLASS(cpu); len = 0; @@ -881,19 +906,19 @@ static const char *get_feature_xml(const GDBState *s, const char *p, return name ? xml_builtin[i][1] : NULL; } -static int gdb_read_register(CPUState *cpu, uint8_t *mem_buf, int reg) +static int gdb_read_register(CPUState *cpu, GByteArray *buf, int reg) { CPUClass *cc = CPU_GET_CLASS(cpu); CPUArchState *env = cpu->env_ptr; GDBRegisterState *r; if (reg < cc->gdb_num_core_regs) { - return cc->gdb_read_register(cpu, mem_buf, reg); + return cc->gdb_read_register(cpu, buf, reg); } for (r = cpu->gdb_regs; r; r = r->next) { if (r->base_reg <= reg && reg < r->base_reg + r->num_regs) { - return r->get_reg(env, mem_buf, reg - r->base_reg); + return r->get_reg(env, buf, reg - r->base_reg); } } return 0; @@ -924,7 +949,7 @@ static int gdb_write_register(CPUState *cpu, uint8_t *mem_buf, int reg) */ void gdb_register_coprocessor(CPUState *cpu, - gdb_reg_cb get_reg, gdb_reg_cb set_reg, + gdb_get_reg_cb get_reg, gdb_set_reg_cb set_reg, int num_regs, const char *xml, int g_pos) { GDBRegisterState *s; @@ -984,7 +1009,7 @@ static int gdb_breakpoint_insert(int type, target_ulong addr, target_ulong len) int err = 0; if (kvm_enabled()) { - return kvm_insert_breakpoint(gdbserver_state->c_cpu, addr, len, type); + return kvm_insert_breakpoint(gdbserver_state.c_cpu, addr, len, type); } switch (type) { @@ -1021,7 +1046,7 @@ static int gdb_breakpoint_remove(int type, target_ulong addr, target_ulong len) int err = 0; if (kvm_enabled()) { - return kvm_remove_breakpoint(gdbserver_state->c_cpu, addr, len, type); + return kvm_remove_breakpoint(gdbserver_state.c_cpu, addr, len, type); } switch (type) { @@ -1059,13 +1084,13 @@ static inline void gdb_cpu_breakpoint_remove_all(CPUState *cpu) #endif } -static void gdb_process_breakpoint_remove_all(const GDBState *s, GDBProcess *p) +static void gdb_process_breakpoint_remove_all(GDBProcess *p) { - CPUState *cpu = get_first_cpu_in_process(s, p); + CPUState *cpu = get_first_cpu_in_process(p); while (cpu) { gdb_cpu_breakpoint_remove_all(cpu); - cpu = gdb_next_cpu_in_process(s, cpu); + cpu = gdb_next_cpu_in_process(cpu); } } @@ -1074,7 +1099,7 @@ static void gdb_breakpoint_remove_all(void) CPUState *cpu; if (kvm_enabled()) { - kvm_remove_all_breakpoints(gdbserver_state->c_cpu); + kvm_remove_all_breakpoints(gdbserver_state.c_cpu); return; } @@ -1083,25 +1108,22 @@ static void gdb_breakpoint_remove_all(void) } } -static void gdb_set_cpu_pc(GDBState *s, target_ulong pc) +static void gdb_set_cpu_pc(target_ulong pc) { - CPUState *cpu = s->c_cpu; + CPUState *cpu = gdbserver_state.c_cpu; cpu_synchronize_state(cpu); cpu_set_pc(cpu, pc); } -static char *gdb_fmt_thread_id(const GDBState *s, CPUState *cpu, - char *buf, size_t buf_size) +static void gdb_append_thread_id(CPUState *cpu, GString *buf) { - if (s->multiprocess) { - snprintf(buf, buf_size, "p%02x.%02x", - gdb_get_cpu_pid(s, cpu), cpu_gdb_index(cpu)); + if (gdbserver_state.multiprocess) { + g_string_append_printf(buf, "p%02x.%02x", + gdb_get_cpu_pid(cpu), cpu_gdb_index(cpu)); } else { - snprintf(buf, buf_size, "%02x", cpu_gdb_index(cpu)); + g_string_append_printf(buf, "%02x", cpu_gdb_index(cpu)); } - - return buf; } typedef enum GDBThreadIdKind { @@ -1163,7 +1185,7 @@ static GDBThreadIdKind read_thread_id(const char *buf, const char **end_buf, * returns -ENOTSUP if a command is unsupported, -EINVAL or -ERANGE if there is * a format error, 0 on success. */ -static int gdb_handle_vcont(GDBState *s, const char *p) +static int gdb_handle_vcont(const char *p) { int res, signal = 0; char cur_action; @@ -1238,36 +1260,36 @@ static int gdb_handle_vcont(GDBState *s, const char *p) goto out; case GDB_ALL_PROCESSES: - cpu = gdb_first_attached_cpu(s); + cpu = gdb_first_attached_cpu(); while (cpu) { if (newstates[cpu->cpu_index] == 1) { newstates[cpu->cpu_index] = cur_action; } - cpu = gdb_next_attached_cpu(s, cpu); + cpu = gdb_next_attached_cpu(cpu); } break; case GDB_ALL_THREADS: - process = gdb_get_process(s, pid); + process = gdb_get_process(pid); if (!process->attached) { res = -EINVAL; goto out; } - cpu = get_first_cpu_in_process(s, process); + cpu = get_first_cpu_in_process(process); while (cpu) { if (newstates[cpu->cpu_index] == 1) { newstates[cpu->cpu_index] = cur_action; } - cpu = gdb_next_cpu_in_process(s, cpu); + cpu = gdb_next_cpu_in_process(cpu); } break; case GDB_ONE_THREAD: - cpu = gdb_get_cpu(s, pid, tid); + cpu = gdb_get_cpu(pid, tid); /* invalid CPU/thread specified */ if (!cpu) { @@ -1282,8 +1304,8 @@ static int gdb_handle_vcont(GDBState *s, const char *p) break; } } - s->signal = signal; - gdb_continue_partial(s, newstates); + gdbserver_state.signal = signal; + gdb_continue_partial(newstates); out: g_free(newstates); @@ -1392,11 +1414,8 @@ static int cmd_parse_params(const char *data, const char *schema, } typedef struct GdbCmdContext { - GDBState *s; GdbCmdVariant *params; int num_params; - uint8_t mem_buf[MAX_PACKET_LENGTH]; - char str_buf[MAX_PACKET_LENGTH + 1]; } GdbCmdContext; typedef void (*GdbCmdHandler)(GdbCmdContext *gdb_ctx, void *user_ctx); @@ -1436,7 +1455,7 @@ static inline int startswith(const char *string, const char *pattern) return !strncmp(string, pattern, strlen(pattern)); } -static int process_string_cmd(GDBState *s, void *user_ctx, const char *data, +static int process_string_cmd(void *user_ctx, const char *data, const GdbCmdParseEntry *cmds, int num_cmds) { int i, schema_len, max_num_params = 0; @@ -1473,7 +1492,6 @@ static int process_string_cmd(GDBState *s, void *user_ctx, const char *data, return -1; } - gdb_ctx.s = s; cmd->handler(&gdb_ctx, user_ctx); return 0; } @@ -1481,53 +1499,54 @@ static int process_string_cmd(GDBState *s, void *user_ctx, const char *data, return -1; } -static void run_cmd_parser(GDBState *s, const char *data, - const GdbCmdParseEntry *cmd) +static void run_cmd_parser(const char *data, const GdbCmdParseEntry *cmd) { if (!data) { return; } + g_string_set_size(gdbserver_state.str_buf, 0); + g_byte_array_set_size(gdbserver_state.mem_buf, 0); + /* In case there was an error during the command parsing we must * send a NULL packet to indicate the command is not supported */ - if (process_string_cmd(s, NULL, data, cmd, 1)) { - put_packet(s, ""); + if (process_string_cmd(NULL, data, cmd, 1)) { + put_packet(""); } } static void handle_detach(GdbCmdContext *gdb_ctx, void *user_ctx) { GDBProcess *process; - GDBState *s = gdb_ctx->s; uint32_t pid = 1; - if (s->multiprocess) { + if (gdbserver_state.multiprocess) { if (!gdb_ctx->num_params) { - put_packet(s, "E22"); + put_packet("E22"); return; } pid = gdb_ctx->params[0].val_ul; } - process = gdb_get_process(s, pid); - gdb_process_breakpoint_remove_all(s, process); + process = gdb_get_process(pid); + gdb_process_breakpoint_remove_all(process); process->attached = false; - if (pid == gdb_get_cpu_pid(s, s->c_cpu)) { - s->c_cpu = gdb_first_attached_cpu(s); + if (pid == gdb_get_cpu_pid(gdbserver_state.c_cpu)) { + gdbserver_state.c_cpu = gdb_first_attached_cpu(); } - if (pid == gdb_get_cpu_pid(s, s->g_cpu)) { - s->g_cpu = gdb_first_attached_cpu(s); + if (pid == gdb_get_cpu_pid(gdbserver_state.g_cpu)) { + gdbserver_state.g_cpu = gdb_first_attached_cpu(); } - if (!s->c_cpu) { + if (!gdbserver_state.c_cpu) { /* No more process attached */ gdb_syscall_mode = GDB_SYS_DISABLED; - gdb_continue(s); + gdb_continue(); } - put_packet(s, "OK"); + put_packet("OK"); } static void handle_thread_alive(GdbCmdContext *gdb_ctx, void *user_ctx) @@ -1535,33 +1554,33 @@ static void handle_thread_alive(GdbCmdContext *gdb_ctx, void *user_ctx) CPUState *cpu; if (!gdb_ctx->num_params) { - put_packet(gdb_ctx->s, "E22"); + put_packet("E22"); return; } if (gdb_ctx->params[0].thread_id.kind == GDB_READ_THREAD_ERR) { - put_packet(gdb_ctx->s, "E22"); + put_packet("E22"); return; } - cpu = gdb_get_cpu(gdb_ctx->s, gdb_ctx->params[0].thread_id.pid, + cpu = gdb_get_cpu(gdb_ctx->params[0].thread_id.pid, gdb_ctx->params[0].thread_id.tid); if (!cpu) { - put_packet(gdb_ctx->s, "E22"); + put_packet("E22"); return; } - put_packet(gdb_ctx->s, "OK"); + put_packet("OK"); } static void handle_continue(GdbCmdContext *gdb_ctx, void *user_ctx) { if (gdb_ctx->num_params) { - gdb_set_cpu_pc(gdb_ctx->s, gdb_ctx->params[0].val_ull); + gdb_set_cpu_pc(gdb_ctx->params[0].val_ull); } - gdb_ctx->s->signal = 0; - gdb_continue(gdb_ctx->s); + gdbserver_state.signal = 0; + gdb_continue(); } static void handle_cont_with_sig(GdbCmdContext *gdb_ctx, void *user_ctx) @@ -1576,11 +1595,11 @@ static void handle_cont_with_sig(GdbCmdContext *gdb_ctx, void *user_ctx) signal = gdb_ctx->params[0].val_ul; } - gdb_ctx->s->signal = gdb_signal_to_target(signal); - if (gdb_ctx->s->signal == -1) { - gdb_ctx->s->signal = 0; + gdbserver_state.signal = gdb_signal_to_target(signal); + if (gdbserver_state.signal == -1) { + gdbserver_state.signal = 0; } - gdb_continue(gdb_ctx->s); + gdb_continue(); } static void handle_set_thread(GdbCmdContext *gdb_ctx, void *user_ctx) @@ -1588,24 +1607,24 @@ static void handle_set_thread(GdbCmdContext *gdb_ctx, void *user_ctx) CPUState *cpu; if (gdb_ctx->num_params != 2) { - put_packet(gdb_ctx->s, "E22"); + put_packet("E22"); return; } if (gdb_ctx->params[1].thread_id.kind == GDB_READ_THREAD_ERR) { - put_packet(gdb_ctx->s, "E22"); + put_packet("E22"); return; } if (gdb_ctx->params[1].thread_id.kind != GDB_ONE_THREAD) { - put_packet(gdb_ctx->s, "OK"); + put_packet("OK"); return; } - cpu = gdb_get_cpu(gdb_ctx->s, gdb_ctx->params[1].thread_id.pid, + cpu = gdb_get_cpu(gdb_ctx->params[1].thread_id.pid, gdb_ctx->params[1].thread_id.tid); if (!cpu) { - put_packet(gdb_ctx->s, "E22"); + put_packet("E22"); return; } @@ -1615,15 +1634,15 @@ static void handle_set_thread(GdbCmdContext *gdb_ctx, void *user_ctx) */ switch (gdb_ctx->params[0].opcode) { case 'c': - gdb_ctx->s->c_cpu = cpu; - put_packet(gdb_ctx->s, "OK"); + gdbserver_state.c_cpu = cpu; + put_packet("OK"); break; case 'g': - gdb_ctx->s->g_cpu = cpu; - put_packet(gdb_ctx->s, "OK"); + gdbserver_state.g_cpu = cpu; + put_packet("OK"); break; default: - put_packet(gdb_ctx->s, "E22"); + put_packet("E22"); break; } } @@ -1633,7 +1652,7 @@ static void handle_insert_bp(GdbCmdContext *gdb_ctx, void *user_ctx) int res; if (gdb_ctx->num_params != 3) { - put_packet(gdb_ctx->s, "E22"); + put_packet("E22"); return; } @@ -1641,14 +1660,14 @@ static void handle_insert_bp(GdbCmdContext *gdb_ctx, void *user_ctx) gdb_ctx->params[1].val_ull, gdb_ctx->params[2].val_ull); if (res >= 0) { - put_packet(gdb_ctx->s, "OK"); + put_packet("OK"); return; } else if (res == -ENOSYS) { - put_packet(gdb_ctx->s, ""); + put_packet(""); return; } - put_packet(gdb_ctx->s, "E22"); + put_packet("E22"); } static void handle_remove_bp(GdbCmdContext *gdb_ctx, void *user_ctx) @@ -1656,7 +1675,7 @@ static void handle_remove_bp(GdbCmdContext *gdb_ctx, void *user_ctx) int res; if (gdb_ctx->num_params != 3) { - put_packet(gdb_ctx->s, "E22"); + put_packet("E22"); return; } @@ -1664,14 +1683,14 @@ static void handle_remove_bp(GdbCmdContext *gdb_ctx, void *user_ctx) gdb_ctx->params[1].val_ull, gdb_ctx->params[2].val_ull); if (res >= 0) { - put_packet(gdb_ctx->s, "OK"); + put_packet("OK"); return; } else if (res == -ENOSYS) { - put_packet(gdb_ctx->s, ""); + put_packet(""); return; } - put_packet(gdb_ctx->s, "E22"); + put_packet("E22"); } /* @@ -1690,20 +1709,20 @@ static void handle_set_reg(GdbCmdContext *gdb_ctx, void *user_ctx) int reg_size; if (!gdb_has_xml) { - put_packet(gdb_ctx->s, ""); + put_packet(""); return; } if (gdb_ctx->num_params != 2) { - put_packet(gdb_ctx->s, "E22"); + put_packet("E22"); return; } reg_size = strlen(gdb_ctx->params[1].data) / 2; - hextomem(gdb_ctx->mem_buf, gdb_ctx->params[1].data, reg_size); - gdb_write_register(gdb_ctx->s->g_cpu, gdb_ctx->mem_buf, + hextomem(gdbserver_state.mem_buf, gdb_ctx->params[1].data, reg_size); + gdb_write_register(gdbserver_state.g_cpu, gdbserver_state.mem_buf->data, gdb_ctx->params[0].val_ull); - put_packet(gdb_ctx->s, "OK"); + put_packet("OK"); } static void handle_get_reg(GdbCmdContext *gdb_ctx, void *user_ctx) @@ -1711,73 +1730,79 @@ static void handle_get_reg(GdbCmdContext *gdb_ctx, void *user_ctx) int reg_size; if (!gdb_has_xml) { - put_packet(gdb_ctx->s, ""); + put_packet(""); return; } if (!gdb_ctx->num_params) { - put_packet(gdb_ctx->s, "E14"); + put_packet("E14"); return; } - reg_size = gdb_read_register(gdb_ctx->s->g_cpu, gdb_ctx->mem_buf, + reg_size = gdb_read_register(gdbserver_state.g_cpu, + gdbserver_state.mem_buf, gdb_ctx->params[0].val_ull); if (!reg_size) { - put_packet(gdb_ctx->s, "E14"); + put_packet("E14"); return; + } else { + g_byte_array_set_size(gdbserver_state.mem_buf, reg_size); } - memtohex(gdb_ctx->str_buf, gdb_ctx->mem_buf, reg_size); - put_packet(gdb_ctx->s, gdb_ctx->str_buf); + memtohex(gdbserver_state.str_buf, gdbserver_state.mem_buf->data, reg_size); + put_strbuf(); } static void handle_write_mem(GdbCmdContext *gdb_ctx, void *user_ctx) { if (gdb_ctx->num_params != 3) { - put_packet(gdb_ctx->s, "E22"); + put_packet("E22"); return; } /* hextomem() reads 2*len bytes */ if (gdb_ctx->params[1].val_ull > strlen(gdb_ctx->params[2].data) / 2) { - put_packet(gdb_ctx->s, "E22"); + put_packet("E22"); return; } - hextomem(gdb_ctx->mem_buf, gdb_ctx->params[2].data, + hextomem(gdbserver_state.mem_buf, gdb_ctx->params[2].data, gdb_ctx->params[1].val_ull); - if (target_memory_rw_debug(gdb_ctx->s->g_cpu, gdb_ctx->params[0].val_ull, - gdb_ctx->mem_buf, - gdb_ctx->params[1].val_ull, true)) { - put_packet(gdb_ctx->s, "E14"); + if (target_memory_rw_debug(gdbserver_state.g_cpu, gdb_ctx->params[0].val_ull, + gdbserver_state.mem_buf->data, + gdbserver_state.mem_buf->len, true)) { + put_packet("E14"); return; } - put_packet(gdb_ctx->s, "OK"); + put_packet("OK"); } static void handle_read_mem(GdbCmdContext *gdb_ctx, void *user_ctx) { if (gdb_ctx->num_params != 2) { - put_packet(gdb_ctx->s, "E22"); + put_packet("E22"); return; } /* memtohex() doubles the required space */ if (gdb_ctx->params[1].val_ull > MAX_PACKET_LENGTH / 2) { - put_packet(gdb_ctx->s, "E22"); + put_packet("E22"); return; } - if (target_memory_rw_debug(gdb_ctx->s->g_cpu, gdb_ctx->params[0].val_ull, - gdb_ctx->mem_buf, - gdb_ctx->params[1].val_ull, false)) { - put_packet(gdb_ctx->s, "E14"); + g_byte_array_set_size(gdbserver_state.mem_buf, gdb_ctx->params[1].val_ull); + + if (target_memory_rw_debug(gdbserver_state.g_cpu, gdb_ctx->params[0].val_ull, + gdbserver_state.mem_buf->data, + gdbserver_state.mem_buf->len, false)) { + put_packet("E14"); return; } - memtohex(gdb_ctx->str_buf, gdb_ctx->mem_buf, gdb_ctx->params[1].val_ull); - put_packet(gdb_ctx->s, gdb_ctx->str_buf); + memtohex(gdbserver_state.str_buf, gdbserver_state.mem_buf->data, + gdbserver_state.mem_buf->len); + put_strbuf(); } static void handle_write_all_regs(GdbCmdContext *gdb_ctx, void *user_ctx) @@ -1790,37 +1815,40 @@ static void handle_write_all_regs(GdbCmdContext *gdb_ctx, void *user_ctx) return; } - cpu_synchronize_state(gdb_ctx->s->g_cpu); - registers = gdb_ctx->mem_buf; + cpu_synchronize_state(gdbserver_state.g_cpu); len = strlen(gdb_ctx->params[0].data) / 2; - hextomem(registers, gdb_ctx->params[0].data, len); - for (addr = 0; addr < gdb_ctx->s->g_cpu->gdb_num_g_regs && len > 0; + hextomem(gdbserver_state.mem_buf, gdb_ctx->params[0].data, len); + registers = gdbserver_state.mem_buf->data; + for (addr = 0; addr < gdbserver_state.g_cpu->gdb_num_g_regs && len > 0; addr++) { - reg_size = gdb_write_register(gdb_ctx->s->g_cpu, registers, addr); + reg_size = gdb_write_register(gdbserver_state.g_cpu, registers, addr); len -= reg_size; registers += reg_size; } - put_packet(gdb_ctx->s, "OK"); + put_packet("OK"); } static void handle_read_all_regs(GdbCmdContext *gdb_ctx, void *user_ctx) { target_ulong addr, len; - cpu_synchronize_state(gdb_ctx->s->g_cpu); + cpu_synchronize_state(gdbserver_state.g_cpu); + g_byte_array_set_size(gdbserver_state.mem_buf, 0); len = 0; - for (addr = 0; addr < gdb_ctx->s->g_cpu->gdb_num_g_regs; addr++) { - len += gdb_read_register(gdb_ctx->s->g_cpu, gdb_ctx->mem_buf + len, + for (addr = 0; addr < gdbserver_state.g_cpu->gdb_num_g_regs; addr++) { + len += gdb_read_register(gdbserver_state.g_cpu, + gdbserver_state.mem_buf, addr); } + g_assert(len == gdbserver_state.mem_buf->len); - memtohex(gdb_ctx->str_buf, gdb_ctx->mem_buf, len); - put_packet(gdb_ctx->s, gdb_ctx->str_buf); + memtohex(gdbserver_state.str_buf, gdbserver_state.mem_buf->data, len); + put_strbuf(); } static void handle_file_io(GdbCmdContext *gdb_ctx, void *user_ctx) { - if (gdb_ctx->num_params >= 1 && gdb_ctx->s->current_syscall_cb) { + if (gdb_ctx->num_params >= 1 && gdbserver_state.current_syscall_cb) { target_ulong ret, err; ret = (target_ulong)gdb_ctx->params[0].val_ull; @@ -1829,31 +1857,31 @@ static void handle_file_io(GdbCmdContext *gdb_ctx, void *user_ctx) } else { err = 0; } - gdb_ctx->s->current_syscall_cb(gdb_ctx->s->c_cpu, ret, err); - gdb_ctx->s->current_syscall_cb = NULL; + gdbserver_state.current_syscall_cb(gdbserver_state.c_cpu, ret, err); + gdbserver_state.current_syscall_cb = NULL; } if (gdb_ctx->num_params >= 3 && gdb_ctx->params[2].opcode == (uint8_t)'C') { - put_packet(gdb_ctx->s, "T02"); + put_packet("T02"); return; } - gdb_continue(gdb_ctx->s); + gdb_continue(); } static void handle_step(GdbCmdContext *gdb_ctx, void *user_ctx) { if (gdb_ctx->num_params) { - gdb_set_cpu_pc(gdb_ctx->s, (target_ulong)gdb_ctx->params[0].val_ull); + gdb_set_cpu_pc((target_ulong)gdb_ctx->params[0].val_ull); } - cpu_single_step(gdb_ctx->s->c_cpu, sstep_flags); - gdb_continue(gdb_ctx->s); + cpu_single_step(gdbserver_state.c_cpu, sstep_flags); + gdb_continue(); } static void handle_v_cont_query(GdbCmdContext *gdb_ctx, void *user_ctx) { - put_packet(gdb_ctx->s, "vCont;c;C;s;S"); + put_packet("vCont;c;C;s;S"); } static void handle_v_cont(GdbCmdContext *gdb_ctx, void *user_ctx) @@ -1864,11 +1892,11 @@ static void handle_v_cont(GdbCmdContext *gdb_ctx, void *user_ctx) return; } - res = gdb_handle_vcont(gdb_ctx->s, gdb_ctx->params[0].data); + res = gdb_handle_vcont(gdb_ctx->params[0].data); if ((res == -EINVAL) || (res == -ERANGE)) { - put_packet(gdb_ctx->s, "E22"); + put_packet("E22"); } else if (res) { - put_packet(gdb_ctx->s, ""); + put_packet(""); } } @@ -1876,38 +1904,37 @@ static void handle_v_attach(GdbCmdContext *gdb_ctx, void *user_ctx) { GDBProcess *process; CPUState *cpu; - char thread_id[16]; - pstrcpy(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), "E22"); + g_string_assign(gdbserver_state.str_buf, "E22"); if (!gdb_ctx->num_params) { goto cleanup; } - process = gdb_get_process(gdb_ctx->s, gdb_ctx->params[0].val_ul); + process = gdb_get_process(gdb_ctx->params[0].val_ul); if (!process) { goto cleanup; } - cpu = get_first_cpu_in_process(gdb_ctx->s, process); + cpu = get_first_cpu_in_process(process); if (!cpu) { goto cleanup; } process->attached = true; - gdb_ctx->s->g_cpu = cpu; - gdb_ctx->s->c_cpu = cpu; + gdbserver_state.g_cpu = cpu; + gdbserver_state.c_cpu = cpu; - gdb_fmt_thread_id(gdb_ctx->s, cpu, thread_id, sizeof(thread_id)); - snprintf(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), "T%02xthread:%s;", - GDB_SIGNAL_TRAP, thread_id); + g_string_printf(gdbserver_state.str_buf, "T%02xthread:", GDB_SIGNAL_TRAP); + gdb_append_thread_id(cpu, gdbserver_state.str_buf); + g_string_append_c(gdbserver_state.str_buf, ';'); cleanup: - put_packet(gdb_ctx->s, gdb_ctx->str_buf); + put_strbuf(); } static void handle_v_kill(GdbCmdContext *gdb_ctx, void *user_ctx) { /* Kill the target */ - put_packet(gdb_ctx->s, "OK"); + put_packet("OK"); error_report("QEMU: Terminated via GDBstub"); exit(0); } @@ -1944,19 +1971,18 @@ static void handle_v_commands(GdbCmdContext *gdb_ctx, void *user_ctx) return; } - if (process_string_cmd(gdb_ctx->s, NULL, gdb_ctx->params[0].data, + if (process_string_cmd(NULL, gdb_ctx->params[0].data, gdb_v_commands_table, ARRAY_SIZE(gdb_v_commands_table))) { - put_packet(gdb_ctx->s, ""); + put_packet(""); } } static void handle_query_qemu_sstepbits(GdbCmdContext *gdb_ctx, void *user_ctx) { - snprintf(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), - "ENABLE=%x,NOIRQ=%x,NOTIMER=%x", SSTEP_ENABLE, - SSTEP_NOIRQ, SSTEP_NOTIMER); - put_packet(gdb_ctx->s, gdb_ctx->str_buf); + g_string_printf(gdbserver_state.str_buf, "ENABLE=%x,NOIRQ=%x,NOTIMER=%x", + SSTEP_ENABLE, SSTEP_NOIRQ, SSTEP_NOTIMER); + put_strbuf(); } static void handle_set_qemu_sstep(GdbCmdContext *gdb_ctx, void *user_ctx) @@ -1966,68 +1992,63 @@ static void handle_set_qemu_sstep(GdbCmdContext *gdb_ctx, void *user_ctx) } sstep_flags = gdb_ctx->params[0].val_ul; - put_packet(gdb_ctx->s, "OK"); + put_packet("OK"); } static void handle_query_qemu_sstep(GdbCmdContext *gdb_ctx, void *user_ctx) { - snprintf(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), "0x%x", sstep_flags); - put_packet(gdb_ctx->s, gdb_ctx->str_buf); + g_string_printf(gdbserver_state.str_buf, "0x%x", sstep_flags); + put_strbuf(); } static void handle_query_curr_tid(GdbCmdContext *gdb_ctx, void *user_ctx) { CPUState *cpu; GDBProcess *process; - char thread_id[16]; /* * "Current thread" remains vague in the spec, so always return * the first thread of the current process (gdb returns the * first thread). */ - process = gdb_get_cpu_process(gdb_ctx->s, gdb_ctx->s->g_cpu); - cpu = get_first_cpu_in_process(gdb_ctx->s, process); - gdb_fmt_thread_id(gdb_ctx->s, cpu, thread_id, sizeof(thread_id)); - snprintf(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), "QC%s", thread_id); - put_packet(gdb_ctx->s, gdb_ctx->str_buf); + process = gdb_get_cpu_process(gdbserver_state.g_cpu); + cpu = get_first_cpu_in_process(process); + g_string_assign(gdbserver_state.str_buf, "QC"); + gdb_append_thread_id(cpu, gdbserver_state.str_buf); + put_strbuf(); } static void handle_query_threads(GdbCmdContext *gdb_ctx, void *user_ctx) { - char thread_id[16]; - - if (!gdb_ctx->s->query_cpu) { - put_packet(gdb_ctx->s, "l"); + if (!gdbserver_state.query_cpu) { + put_packet("l"); return; } - gdb_fmt_thread_id(gdb_ctx->s, gdb_ctx->s->query_cpu, thread_id, - sizeof(thread_id)); - snprintf(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), "m%s", thread_id); - put_packet(gdb_ctx->s, gdb_ctx->str_buf); - gdb_ctx->s->query_cpu = - gdb_next_attached_cpu(gdb_ctx->s, gdb_ctx->s->query_cpu); + g_string_assign(gdbserver_state.str_buf, "m"); + gdb_append_thread_id(gdbserver_state.query_cpu, gdbserver_state.str_buf); + put_strbuf(); + gdbserver_state.query_cpu = gdb_next_attached_cpu(gdbserver_state.query_cpu); } static void handle_query_first_threads(GdbCmdContext *gdb_ctx, void *user_ctx) { - gdb_ctx->s->query_cpu = gdb_first_attached_cpu(gdb_ctx->s); + gdbserver_state.query_cpu = gdb_first_attached_cpu(); handle_query_threads(gdb_ctx, user_ctx); } static void handle_query_thread_extra(GdbCmdContext *gdb_ctx, void *user_ctx) { + g_autoptr(GString) rs = g_string_new(NULL); CPUState *cpu; - int len; if (!gdb_ctx->num_params || gdb_ctx->params[0].thread_id.kind == GDB_READ_THREAD_ERR) { - put_packet(gdb_ctx->s, "E22"); + put_packet("E22"); return; } - cpu = gdb_get_cpu(gdb_ctx->s, gdb_ctx->params[0].thread_id.pid, + cpu = gdb_get_cpu(gdb_ctx->params[0].thread_id.pid, gdb_ctx->params[0].thread_id.tid); if (!cpu) { return; @@ -2035,24 +2056,21 @@ static void handle_query_thread_extra(GdbCmdContext *gdb_ctx, void *user_ctx) cpu_synchronize_state(cpu); - if (gdb_ctx->s->multiprocess && (gdb_ctx->s->process_num > 1)) { + if (gdbserver_state.multiprocess && (gdbserver_state.process_num > 1)) { /* Print the CPU model and name in multiprocess mode */ ObjectClass *oc = object_get_class(OBJECT(cpu)); const char *cpu_model = object_class_get_name(oc); - char *cpu_name = object_get_canonical_path_component(OBJECT(cpu)); - len = snprintf((char *)gdb_ctx->mem_buf, sizeof(gdb_ctx->str_buf) / 2, - "%s %s [%s]", cpu_model, cpu_name, - cpu->halted ? "halted " : "running"); - g_free(cpu_name); + g_autofree char *cpu_name = + object_get_canonical_path_component(OBJECT(cpu)); + g_string_printf(rs, "%s %s [%s]", cpu_model, cpu_name, + cpu->halted ? "halted " : "running"); } else { - /* memtohex() doubles the required space */ - len = snprintf((char *)gdb_ctx->mem_buf, sizeof(gdb_ctx->str_buf) / 2, - "CPU#%d [%s]", cpu->cpu_index, + g_string_printf(rs, "CPU#%d [%s]", cpu->cpu_index, cpu->halted ? "halted " : "running"); } - trace_gdbstub_op_extra_info((char *)gdb_ctx->mem_buf); - memtohex(gdb_ctx->str_buf, gdb_ctx->mem_buf, len); - put_packet(gdb_ctx->s, gdb_ctx->str_buf); + trace_gdbstub_op_extra_info(rs->str); + memtohex(gdbserver_state.str_buf, (uint8_t *)rs->str, rs->len); + put_strbuf(); } #ifdef CONFIG_USER_ONLY @@ -2060,37 +2078,40 @@ static void handle_query_offsets(GdbCmdContext *gdb_ctx, void *user_ctx) { TaskState *ts; - ts = gdb_ctx->s->c_cpu->opaque; - snprintf(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), - "Text=" TARGET_ABI_FMT_lx ";Data=" TARGET_ABI_FMT_lx - ";Bss=" TARGET_ABI_FMT_lx, - ts->info->code_offset, - ts->info->data_offset, - ts->info->data_offset); - put_packet(gdb_ctx->s, gdb_ctx->str_buf); + ts = gdbserver_state.c_cpu->opaque; + g_string_printf(gdbserver_state.str_buf, + "Text=" TARGET_ABI_FMT_lx + ";Data=" TARGET_ABI_FMT_lx + ";Bss=" TARGET_ABI_FMT_lx, + ts->info->code_offset, + ts->info->data_offset, + ts->info->data_offset); + put_strbuf(); } #else static void handle_query_rcmd(GdbCmdContext *gdb_ctx, void *user_ctx) { + const guint8 zero = 0; int len; if (!gdb_ctx->num_params) { - put_packet(gdb_ctx->s, "E22"); + put_packet("E22"); return; } len = strlen(gdb_ctx->params[0].data); if (len % 2) { - put_packet(gdb_ctx->s, "E01"); + put_packet("E01"); return; } + g_assert(gdbserver_state.mem_buf->len == 0); len = len / 2; - hextomem(gdb_ctx->mem_buf, gdb_ctx->params[0].data, len); - gdb_ctx->mem_buf[len++] = 0; - qemu_chr_be_write(gdb_ctx->s->mon_chr, gdb_ctx->mem_buf, len); - put_packet(gdb_ctx->s, "OK"); - + hextomem(gdbserver_state.mem_buf, gdb_ctx->params[0].data, len); + g_byte_array_append(gdbserver_state.mem_buf, &zero, 1); + qemu_chr_be_write(gdbserver_state.mon_chr, gdbserver_state.mem_buf->data, + gdbserver_state.mem_buf->len); + put_packet("OK"); } #endif @@ -2098,21 +2119,19 @@ static void handle_query_supported(GdbCmdContext *gdb_ctx, void *user_ctx) { CPUClass *cc; - snprintf(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), "PacketSize=%x", - MAX_PACKET_LENGTH); + g_string_printf(gdbserver_state.str_buf, "PacketSize=%x", MAX_PACKET_LENGTH); cc = CPU_GET_CLASS(first_cpu); if (cc->gdb_core_xml_file) { - pstrcat(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), - ";qXfer:features:read+"); + g_string_append(gdbserver_state.str_buf, ";qXfer:features:read+"); } if (gdb_ctx->num_params && strstr(gdb_ctx->params[0].data, "multiprocess+")) { - gdb_ctx->s->multiprocess = true; + gdbserver_state.multiprocess = true; } - pstrcat(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), ";multiprocess+"); - put_packet(gdb_ctx->s, gdb_ctx->str_buf); + g_string_append(gdbserver_state.str_buf, ";vContSupported+;multiprocess+"); + put_strbuf(); } static void handle_query_xfer_features(GdbCmdContext *gdb_ctx, void *user_ctx) @@ -2124,22 +2143,22 @@ static void handle_query_xfer_features(GdbCmdContext *gdb_ctx, void *user_ctx) const char *p; if (gdb_ctx->num_params < 3) { - put_packet(gdb_ctx->s, "E22"); + put_packet("E22"); return; } - process = gdb_get_cpu_process(gdb_ctx->s, gdb_ctx->s->g_cpu); - cc = CPU_GET_CLASS(gdb_ctx->s->g_cpu); + process = gdb_get_cpu_process(gdbserver_state.g_cpu); + cc = CPU_GET_CLASS(gdbserver_state.g_cpu); if (!cc->gdb_core_xml_file) { - put_packet(gdb_ctx->s, ""); + put_packet(""); return; } gdb_has_xml = true; p = gdb_ctx->params[0].data; - xml = get_feature_xml(gdb_ctx->s, p, &p, process); + xml = get_feature_xml(p, &p, process); if (!xml) { - put_packet(gdb_ctx->s, "E00"); + put_packet("E00"); return; } @@ -2147,7 +2166,7 @@ static void handle_query_xfer_features(GdbCmdContext *gdb_ctx, void *user_ctx) len = gdb_ctx->params[2].val_ul; total_len = strlen(xml); if (addr > total_len) { - put_packet(gdb_ctx->s, "E00"); + put_packet("E00"); return; } @@ -2156,42 +2175,43 @@ static void handle_query_xfer_features(GdbCmdContext *gdb_ctx, void *user_ctx) } if (len < total_len - addr) { - gdb_ctx->str_buf[0] = 'm'; - len = memtox(gdb_ctx->str_buf + 1, xml + addr, len); + g_string_assign(gdbserver_state.str_buf, "m"); + memtox(gdbserver_state.str_buf, xml + addr, len); } else { - gdb_ctx->str_buf[0] = 'l'; - len = memtox(gdb_ctx->str_buf + 1, xml + addr, total_len - addr); + g_string_assign(gdbserver_state.str_buf, "l"); + memtox(gdbserver_state.str_buf, xml + addr, total_len - addr); } - put_packet_binary(gdb_ctx->s, gdb_ctx->str_buf, len + 1, true); + put_packet_binary(gdbserver_state.str_buf->str, + gdbserver_state.str_buf->len, true); } static void handle_query_attached(GdbCmdContext *gdb_ctx, void *user_ctx) { - put_packet(gdb_ctx->s, GDB_ATTACHED); + put_packet(GDB_ATTACHED); } static void handle_query_qemu_supported(GdbCmdContext *gdb_ctx, void *user_ctx) { - snprintf(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), "sstepbits;sstep"); + g_string_printf(gdbserver_state.str_buf, "sstepbits;sstep"); #ifndef CONFIG_USER_ONLY - pstrcat(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), ";PhyMemMode"); + g_string_append(gdbserver_state.str_buf, ";PhyMemMode"); #endif - put_packet(gdb_ctx->s, gdb_ctx->str_buf); + put_strbuf(); } #ifndef CONFIG_USER_ONLY static void handle_query_qemu_phy_mem_mode(GdbCmdContext *gdb_ctx, void *user_ctx) { - snprintf(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), "%d", phy_memory_mode); - put_packet(gdb_ctx->s, gdb_ctx->str_buf); + g_string_printf(gdbserver_state.str_buf, "%d", phy_memory_mode); + put_strbuf(); } static void handle_set_qemu_phy_mem_mode(GdbCmdContext *gdb_ctx, void *user_ctx) { if (!gdb_ctx->num_params) { - put_packet(gdb_ctx->s, "E22"); + put_packet("E22"); return; } @@ -2200,7 +2220,7 @@ static void handle_set_qemu_phy_mem_mode(GdbCmdContext *gdb_ctx, void *user_ctx) } else { phy_memory_mode = 1; } - put_packet(gdb_ctx->s, "OK"); + put_packet("OK"); } #endif @@ -2316,16 +2336,16 @@ static void handle_gen_query(GdbCmdContext *gdb_ctx, void *user_ctx) return; } - if (!process_string_cmd(gdb_ctx->s, NULL, gdb_ctx->params[0].data, + if (!process_string_cmd(NULL, gdb_ctx->params[0].data, gdb_gen_query_set_common_table, ARRAY_SIZE(gdb_gen_query_set_common_table))) { return; } - if (process_string_cmd(gdb_ctx->s, NULL, gdb_ctx->params[0].data, + if (process_string_cmd(NULL, gdb_ctx->params[0].data, gdb_gen_query_table, ARRAY_SIZE(gdb_gen_query_table))) { - put_packet(gdb_ctx->s, ""); + put_packet(""); } } @@ -2335,28 +2355,25 @@ static void handle_gen_set(GdbCmdContext *gdb_ctx, void *user_ctx) return; } - if (!process_string_cmd(gdb_ctx->s, NULL, gdb_ctx->params[0].data, + if (!process_string_cmd(NULL, gdb_ctx->params[0].data, gdb_gen_query_set_common_table, ARRAY_SIZE(gdb_gen_query_set_common_table))) { return; } - if (process_string_cmd(gdb_ctx->s, NULL, gdb_ctx->params[0].data, + if (process_string_cmd(NULL, gdb_ctx->params[0].data, gdb_gen_set_table, ARRAY_SIZE(gdb_gen_set_table))) { - put_packet(gdb_ctx->s, ""); + put_packet(""); } } static void handle_target_halt(GdbCmdContext *gdb_ctx, void *user_ctx) { - char thread_id[16]; - - gdb_fmt_thread_id(gdb_ctx->s, gdb_ctx->s->c_cpu, thread_id, - sizeof(thread_id)); - snprintf(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), "T%02xthread:%s;", - GDB_SIGNAL_TRAP, thread_id); - put_packet(gdb_ctx->s, gdb_ctx->str_buf); + g_string_printf(gdbserver_state.str_buf, "T%02xthread:", GDB_SIGNAL_TRAP); + gdb_append_thread_id(gdbserver_state.c_cpu, gdbserver_state.str_buf); + g_string_append_c(gdbserver_state.str_buf, ';'); + put_strbuf(); /* * Remove all the breakpoints when this query is issued, * because gdb is doing an initial connect and the state @@ -2365,7 +2382,7 @@ static void handle_target_halt(GdbCmdContext *gdb_ctx, void *user_ctx) gdb_breakpoint_remove_all(); } -static int gdb_handle_packet(GDBState *s, const char *line_buf) +static int gdb_handle_packet(const char *line_buf) { const GdbCmdParseEntry *cmd_parser = NULL; @@ -2373,7 +2390,7 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) switch (line_buf[0]) { case '!': - put_packet(s, "OK"); + put_packet("OK"); break; case '?': { @@ -2588,12 +2605,12 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) break; default: /* put empty packet */ - put_packet(s, ""); + put_packet(""); break; } if (cmd_parser) { - run_cmd_parser(s, line_buf, cmd_parser); + run_cmd_parser(line_buf, cmd_parser); } return RS_IDLE; @@ -2601,7 +2618,7 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) void gdb_set_stop_cpu(CPUState *cpu) { - GDBProcess *p = gdb_get_cpu_process(gdbserver_state, cpu); + GDBProcess *p = gdb_get_cpu_process(cpu); if (!p->attached) { /* @@ -2611,26 +2628,25 @@ void gdb_set_stop_cpu(CPUState *cpu) return; } - gdbserver_state->c_cpu = cpu; - gdbserver_state->g_cpu = cpu; + gdbserver_state.c_cpu = cpu; + gdbserver_state.g_cpu = cpu; } #ifndef CONFIG_USER_ONLY static void gdb_vm_state_change(void *opaque, int running, RunState state) { - GDBState *s = gdbserver_state; - CPUState *cpu = s->c_cpu; - char buf[256]; - char thread_id[16]; + CPUState *cpu = gdbserver_state.c_cpu; + g_autoptr(GString) buf = g_string_new(NULL); + g_autoptr(GString) tid = g_string_new(NULL); const char *type; int ret; - if (running || s->state == RS_INACTIVE) { + if (running || gdbserver_state.state == RS_INACTIVE) { return; } /* Is there a GDB syscall waiting to be sent? */ - if (s->current_syscall_cb) { - put_packet(s, s->syscall_buf); + if (gdbserver_state.current_syscall_cb) { + put_packet(gdbserver_state.syscall_buf); return; } @@ -2639,7 +2655,7 @@ static void gdb_vm_state_change(void *opaque, int running, RunState state) return; } - gdb_fmt_thread_id(s, cpu, thread_id, sizeof(thread_id)); + gdb_append_thread_id(cpu, tid); switch (state) { case RUN_STATE_DEBUG: @@ -2657,10 +2673,9 @@ static void gdb_vm_state_change(void *opaque, int running, RunState state) } trace_gdbstub_hit_watchpoint(type, cpu_gdb_index(cpu), (target_ulong)cpu->watchpoint_hit->vaddr); - snprintf(buf, sizeof(buf), - "T%02xthread:%s;%swatch:" TARGET_FMT_lx ";", - GDB_SIGNAL_TRAP, thread_id, type, - (target_ulong)cpu->watchpoint_hit->vaddr); + g_string_printf(buf, "T%02xthread:%s;%swatch:" TARGET_FMT_lx ";", + GDB_SIGNAL_TRAP, tid->str, type, + (target_ulong)cpu->watchpoint_hit->vaddr); cpu->watchpoint_hit = NULL; goto send_packet; } else { @@ -2701,10 +2716,10 @@ static void gdb_vm_state_change(void *opaque, int running, RunState state) break; } gdb_set_stop_cpu(cpu); - snprintf(buf, sizeof(buf), "T%02xthread:%s;", ret, thread_id); + g_string_printf(buf, "T%02xthread:%s;", ret, tid->str); send_packet: - put_packet(s, buf); + put_packet(buf->str); /* disable single step if it was enabled */ cpu_single_step(cpu, 0); @@ -2722,17 +2737,17 @@ void gdb_do_syscallv(gdb_syscall_complete_cb cb, const char *fmt, va_list va) char *p_end; target_ulong addr; uint64_t i64; - GDBState *s; - s = gdbserver_state; - if (!s) + if (!gdbserver_state.init) { return; - s->current_syscall_cb = cb; + } + + gdbserver_state.current_syscall_cb = cb; #ifndef CONFIG_USER_ONLY vm_stop(RUN_STATE_DEBUG); #endif - p = s->syscall_buf; - p_end = &s->syscall_buf[sizeof(s->syscall_buf)]; + p = &gdbserver_state.syscall_buf[0]; + p_end = &gdbserver_state.syscall_buf[sizeof(gdbserver_state.syscall_buf)]; *(p++) = 'F'; while (*fmt) { if (*fmt == '%') { @@ -2765,14 +2780,14 @@ void gdb_do_syscallv(gdb_syscall_complete_cb cb, const char *fmt, va_list va) } *p = 0; #ifdef CONFIG_USER_ONLY - put_packet(s, s->syscall_buf); + put_packet(gdbserver_state.syscall_buf); /* Return control to gdb for it to process the syscall request. * Since the protocol requires that gdb hands control back to us * using a "here are the results" F packet, we don't need to check * gdb_handlesig's return value (which is the signal to deliver if * execution was resumed via a continue packet). */ - gdb_handlesig(s->c_cpu, 0); + gdb_handlesig(gdbserver_state.c_cpu, 0); #else /* In this case wait to send the syscall packet until notification that the CPU has stopped. This must be done because if the packet is sent @@ -2780,7 +2795,7 @@ void gdb_do_syscallv(gdb_syscall_complete_cb cb, const char *fmt, va_list va) is still in the running state, which can cause packets to be dropped and state transition 'T' packets to be sent while the syscall is still being processed. */ - qemu_cpu_kick(s->c_cpu); + qemu_cpu_kick(gdbserver_state.c_cpu); #endif } @@ -2793,25 +2808,27 @@ void gdb_do_syscall(gdb_syscall_complete_cb cb, const char *fmt, ...) va_end(va); } -static void gdb_read_byte(GDBState *s, uint8_t ch) +static void gdb_read_byte(uint8_t ch) { uint8_t reply; #ifndef CONFIG_USER_ONLY - if (s->last_packet_len) { + if (gdbserver_state.last_packet->len) { /* Waiting for a response to the last packet. If we see the start of a new command then abandon the previous response. */ if (ch == '-') { trace_gdbstub_err_got_nack(); - put_buffer(s, (uint8_t *)s->last_packet, s->last_packet_len); + put_buffer(gdbserver_state.last_packet->data, + gdbserver_state.last_packet->len); } else if (ch == '+') { trace_gdbstub_io_got_ack(); } else { trace_gdbstub_io_got_unexpected(ch); } - if (ch == '+' || ch == '$') - s->last_packet_len = 0; + if (ch == '+' || ch == '$') { + g_byte_array_set_size(gdbserver_state.last_packet, 0); + } if (ch != '$') return; } @@ -2822,13 +2839,13 @@ static void gdb_read_byte(GDBState *s, uint8_t ch) } else #endif { - switch(s->state) { + switch(gdbserver_state.state) { case RS_IDLE: if (ch == '$') { /* start of command packet */ - s->line_buf_index = 0; - s->line_sum = 0; - s->state = RS_GETLINE; + gdbserver_state.line_buf_index = 0; + gdbserver_state.line_sum = 0; + gdbserver_state.state = RS_GETLINE; } else { trace_gdbstub_err_garbage(ch); } @@ -2836,37 +2853,37 @@ static void gdb_read_byte(GDBState *s, uint8_t ch) case RS_GETLINE: if (ch == '}') { /* start escape sequence */ - s->state = RS_GETLINE_ESC; - s->line_sum += ch; + gdbserver_state.state = RS_GETLINE_ESC; + gdbserver_state.line_sum += ch; } else if (ch == '*') { /* start run length encoding sequence */ - s->state = RS_GETLINE_RLE; - s->line_sum += ch; + gdbserver_state.state = RS_GETLINE_RLE; + gdbserver_state.line_sum += ch; } else if (ch == '#') { /* end of command, start of checksum*/ - s->state = RS_CHKSUM1; - } else if (s->line_buf_index >= sizeof(s->line_buf) - 1) { + gdbserver_state.state = RS_CHKSUM1; + } else if (gdbserver_state.line_buf_index >= sizeof(gdbserver_state.line_buf) - 1) { trace_gdbstub_err_overrun(); - s->state = RS_IDLE; + gdbserver_state.state = RS_IDLE; } else { /* unescaped command character */ - s->line_buf[s->line_buf_index++] = ch; - s->line_sum += ch; + gdbserver_state.line_buf[gdbserver_state.line_buf_index++] = ch; + gdbserver_state.line_sum += ch; } break; case RS_GETLINE_ESC: if (ch == '#') { /* unexpected end of command in escape sequence */ - s->state = RS_CHKSUM1; - } else if (s->line_buf_index >= sizeof(s->line_buf) - 1) { + gdbserver_state.state = RS_CHKSUM1; + } else if (gdbserver_state.line_buf_index >= sizeof(gdbserver_state.line_buf) - 1) { /* command buffer overrun */ trace_gdbstub_err_overrun(); - s->state = RS_IDLE; + gdbserver_state.state = RS_IDLE; } else { /* parse escaped character and leave escape state */ - s->line_buf[s->line_buf_index++] = ch ^ 0x20; - s->line_sum += ch; - s->state = RS_GETLINE; + gdbserver_state.line_buf[gdbserver_state.line_buf_index++] = ch ^ 0x20; + gdbserver_state.line_sum += ch; + gdbserver_state.state = RS_GETLINE; } break; case RS_GETLINE_RLE: @@ -2877,25 +2894,25 @@ static void gdb_read_byte(GDBState *s, uint8_t ch) if (ch < ' ' || ch == '#' || ch == '$' || ch > 126) { /* invalid RLE count encoding */ trace_gdbstub_err_invalid_repeat(ch); - s->state = RS_GETLINE; + gdbserver_state.state = RS_GETLINE; } else { /* decode repeat length */ int repeat = ch - ' ' + 3; - if (s->line_buf_index + repeat >= sizeof(s->line_buf) - 1) { + if (gdbserver_state.line_buf_index + repeat >= sizeof(gdbserver_state.line_buf) - 1) { /* that many repeats would overrun the command buffer */ trace_gdbstub_err_overrun(); - s->state = RS_IDLE; - } else if (s->line_buf_index < 1) { + gdbserver_state.state = RS_IDLE; + } else if (gdbserver_state.line_buf_index < 1) { /* got a repeat but we have nothing to repeat */ trace_gdbstub_err_invalid_rle(); - s->state = RS_GETLINE; + gdbserver_state.state = RS_GETLINE; } else { /* repeat the last character */ - memset(s->line_buf + s->line_buf_index, - s->line_buf[s->line_buf_index - 1], repeat); - s->line_buf_index += repeat; - s->line_sum += ch; - s->state = RS_GETLINE; + memset(gdbserver_state.line_buf + gdbserver_state.line_buf_index, + gdbserver_state.line_buf[gdbserver_state.line_buf_index - 1], repeat); + gdbserver_state.line_buf_index += repeat; + gdbserver_state.line_sum += ch; + gdbserver_state.state = RS_GETLINE; } } break; @@ -2903,33 +2920,33 @@ static void gdb_read_byte(GDBState *s, uint8_t ch) /* get high hex digit of checksum */ if (!isxdigit(ch)) { trace_gdbstub_err_checksum_invalid(ch); - s->state = RS_GETLINE; + gdbserver_state.state = RS_GETLINE; break; } - s->line_buf[s->line_buf_index] = '\0'; - s->line_csum = fromhex(ch) << 4; - s->state = RS_CHKSUM2; + gdbserver_state.line_buf[gdbserver_state.line_buf_index] = '\0'; + gdbserver_state.line_csum = fromhex(ch) << 4; + gdbserver_state.state = RS_CHKSUM2; break; case RS_CHKSUM2: /* get low hex digit of checksum */ if (!isxdigit(ch)) { trace_gdbstub_err_checksum_invalid(ch); - s->state = RS_GETLINE; + gdbserver_state.state = RS_GETLINE; break; } - s->line_csum |= fromhex(ch); + gdbserver_state.line_csum |= fromhex(ch); - if (s->line_csum != (s->line_sum & 0xff)) { - trace_gdbstub_err_checksum_incorrect(s->line_sum, s->line_csum); + if (gdbserver_state.line_csum != (gdbserver_state.line_sum & 0xff)) { + trace_gdbstub_err_checksum_incorrect(gdbserver_state.line_sum, gdbserver_state.line_csum); /* send NAK reply */ reply = '-'; - put_buffer(s, &reply, 1); - s->state = RS_IDLE; + put_buffer(&reply, 1); + gdbserver_state.state = RS_IDLE; } else { /* send ACK reply */ reply = '+'; - put_buffer(s, &reply, 1); - s->state = gdb_handle_packet(s, s->line_buf); + put_buffer(&reply, 1); + gdbserver_state.state = gdb_handle_packet(gdbserver_state.line_buf); } break; default: @@ -2941,15 +2958,13 @@ static void gdb_read_byte(GDBState *s, uint8_t ch) /* Tell the remote gdb that the process has exited. */ void gdb_exit(CPUArchState *env, int code) { - GDBState *s; char buf[4]; - s = gdbserver_state; - if (!s) { + if (!gdbserver_state.init) { return; } #ifdef CONFIG_USER_ONLY - if (gdbserver_fd < 0 || s->fd < 0) { + if (gdbserver_fd < 0 || gdbserver_state.fd < 0) { return; } #endif @@ -2957,10 +2972,10 @@ void gdb_exit(CPUArchState *env, int code) trace_gdbstub_op_exiting((uint8_t)code); snprintf(buf, sizeof(buf), "W%02x", (uint8_t)code); - put_packet(s, buf); + put_packet(buf); #ifndef CONFIG_USER_ONLY - qemu_chr_fe_deinit(&s->chr, true); + qemu_chr_fe_deinit(&gdbserver_state.chr, true); #endif } @@ -2974,7 +2989,7 @@ static void create_default_process(GDBState *s) GDBProcess *process; int max_pid = 0; - if (s->process_num) { + if (gdbserver_state.process_num) { max_pid = s->processes[s->process_num - 1].pid; } @@ -2993,12 +3008,10 @@ static void create_default_process(GDBState *s) int gdb_handlesig(CPUState *cpu, int sig) { - GDBState *s; char buf[256]; int n; - s = gdbserver_state; - if (gdbserver_fd < 0 || s->fd < 0) { + if (gdbserver_fd < 0 || gdbserver_state.fd < 0) { return sig; } @@ -3008,58 +3021,55 @@ gdb_handlesig(CPUState *cpu, int sig) if (sig != 0) { snprintf(buf, sizeof(buf), "S%02x", target_signal_to_gdb(sig)); - put_packet(s, buf); + put_packet(buf); } /* put_packet() might have detected that the peer terminated the connection. */ - if (s->fd < 0) { + if (gdbserver_state.fd < 0) { return sig; } sig = 0; - s->state = RS_IDLE; - s->running_state = 0; - while (s->running_state == 0) { - n = read(s->fd, buf, 256); + gdbserver_state.state = RS_IDLE; + gdbserver_state.running_state = 0; + while (gdbserver_state.running_state == 0) { + n = read(gdbserver_state.fd, buf, 256); if (n > 0) { int i; for (i = 0; i < n; i++) { - gdb_read_byte(s, buf[i]); + gdb_read_byte(buf[i]); } } else { /* XXX: Connection closed. Should probably wait for another connection before continuing. */ if (n == 0) { - close(s->fd); + close(gdbserver_state.fd); } - s->fd = -1; + gdbserver_state.fd = -1; return sig; } } - sig = s->signal; - s->signal = 0; + sig = gdbserver_state.signal; + gdbserver_state.signal = 0; return sig; } /* Tell the remote gdb that the process has exited due to SIG. */ void gdb_signalled(CPUArchState *env, int sig) { - GDBState *s; char buf[4]; - s = gdbserver_state; - if (gdbserver_fd < 0 || s->fd < 0) { + if (gdbserver_fd < 0 || gdbserver_state.fd < 0) { return; } snprintf(buf, sizeof(buf), "X%02x", target_signal_to_gdb(sig)); - put_packet(s, buf); + put_packet(buf); } static bool gdb_accept(void) { - GDBState *s; struct sockaddr_in sockaddr; socklen_t len; int fd; @@ -3083,15 +3093,13 @@ static bool gdb_accept(void) return false; } - s = g_malloc0(sizeof(GDBState)); - create_default_process(s); - s->processes[0].attached = true; - s->c_cpu = gdb_first_attached_cpu(s); - s->g_cpu = s->c_cpu; - s->fd = fd; + init_gdbserver_state(); + create_default_process(&gdbserver_state); + gdbserver_state.processes[0].attached = true; + gdbserver_state.c_cpu = gdb_first_attached_cpu(); + gdbserver_state.g_cpu = gdbserver_state.c_cpu; + gdbserver_state.fd = fd; gdb_has_xml = false; - - gdbserver_state = s; return true; } @@ -3144,13 +3152,11 @@ int gdbserver_start(int port) /* Disable gdb stub for child processes. */ void gdbserver_fork(CPUState *cpu) { - GDBState *s = gdbserver_state; - - if (gdbserver_fd < 0 || s->fd < 0) { + if (gdbserver_fd < 0 || gdbserver_state.fd < 0) { return; } - close(s->fd); - s->fd = -1; + close(gdbserver_state.fd); + gdbserver_state.fd = -1; cpu_breakpoint_remove_all(cpu, BP_GDB); cpu_watchpoint_remove_all(cpu, BP_GDB); } @@ -3167,11 +3173,11 @@ static void gdb_chr_receive(void *opaque, const uint8_t *buf, int size) int i; for (i = 0; i < size; i++) { - gdb_read_byte(gdbserver_state, buf[i]); + gdb_read_byte(buf[i]); } } -static void gdb_chr_event(void *opaque, int event) +static void gdb_chr_event(void *opaque, QEMUChrEvent event) { int i; GDBState *s = (GDBState *) opaque; @@ -3183,7 +3189,7 @@ static void gdb_chr_event(void *opaque, int event) s->processes[i].attached = !i; } - s->c_cpu = gdb_first_attached_cpu(s); + s->c_cpu = gdb_first_attached_cpu(); s->g_cpu = s->c_cpu; vm_stop(RUN_STATE_PAUSED); @@ -3194,32 +3200,11 @@ static void gdb_chr_event(void *opaque, int event) } } -static void gdb_monitor_output(GDBState *s, const char *msg, int len) -{ - char buf[MAX_PACKET_LENGTH]; - - buf[0] = 'O'; - if (len > (MAX_PACKET_LENGTH/2) - 1) - len = (MAX_PACKET_LENGTH/2) - 1; - memtohex(buf + 1, (uint8_t *)msg, len); - put_packet(s, buf); -} - static int gdb_monitor_write(Chardev *chr, const uint8_t *buf, int len) { - const char *p = (const char *)buf; - int max_sz; - - max_sz = (sizeof(gdbserver_state->last_packet) - 2) / 2; - for (;;) { - if (len <= max_sz) { - gdb_monitor_output(gdbserver_state, p, len); - break; - } - gdb_monitor_output(gdbserver_state, p, max_sz); - p += max_sz; - len -= max_sz; - } + g_autoptr(GString) hex_buf = g_string_new("O"); + memtohex(hex_buf, buf, len); + put_packet(hex_buf->str); return len; } @@ -3300,26 +3285,18 @@ static void create_processes(GDBState *s) { object_child_foreach(object_get_root(), find_cpu_clusters, s); - if (s->processes) { + if (gdbserver_state.processes) { /* Sort by PID */ - qsort(s->processes, s->process_num, sizeof(s->processes[0]), pid_order); + qsort(gdbserver_state.processes, gdbserver_state.process_num, sizeof(gdbserver_state.processes[0]), pid_order); } create_default_process(s); } -static void cleanup_processes(GDBState *s) -{ - g_free(s->processes); - s->process_num = 0; - s->processes = NULL; -} - int gdbserver_start(const char *device) { trace_gdbstub_op_start(device); - GDBState *s; char gdbstub_device_name[128]; Chardev *chr = NULL; Chardev *mon_chr; @@ -3357,43 +3334,40 @@ int gdbserver_start(const char *device) return -1; } - s = gdbserver_state; - if (!s) { - s = g_malloc0(sizeof(GDBState)); - gdbserver_state = s; + if (!gdbserver_state.init) { + init_gdbserver_state(); qemu_add_vm_change_state_handler(gdb_vm_state_change, NULL); /* Initialize a monitor terminal for gdb */ mon_chr = qemu_chardev_new(NULL, TYPE_CHARDEV_GDB, NULL, NULL, &error_abort); - monitor_init_hmp(mon_chr, false); + monitor_init_hmp(mon_chr, false, &error_abort); } else { - qemu_chr_fe_deinit(&s->chr, true); - mon_chr = s->mon_chr; - cleanup_processes(s); - memset(s, 0, sizeof(GDBState)); - s->mon_chr = mon_chr; + qemu_chr_fe_deinit(&gdbserver_state.chr, true); + mon_chr = gdbserver_state.mon_chr; + reset_gdbserver_state(); } - create_processes(s); + create_processes(&gdbserver_state); if (chr) { - qemu_chr_fe_init(&s->chr, chr, &error_abort); - qemu_chr_fe_set_handlers(&s->chr, gdb_chr_can_receive, gdb_chr_receive, - gdb_chr_event, NULL, s, NULL, true); + qemu_chr_fe_init(&gdbserver_state.chr, chr, &error_abort); + qemu_chr_fe_set_handlers(&gdbserver_state.chr, gdb_chr_can_receive, + gdb_chr_receive, gdb_chr_event, + NULL, &gdbserver_state, NULL, true); } - s->state = chr ? RS_IDLE : RS_INACTIVE; - s->mon_chr = mon_chr; - s->current_syscall_cb = NULL; + gdbserver_state.state = chr ? RS_IDLE : RS_INACTIVE; + gdbserver_state.mon_chr = mon_chr; + gdbserver_state.current_syscall_cb = NULL; return 0; } void gdbserver_cleanup(void) { - if (gdbserver_state) { - put_packet(gdbserver_state, "W00"); + if (gdbserver_state.init) { + put_packet("W00"); } } diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx index 257ee7d7a3..ca5198438d 100644 --- a/hmp-commands-info.hx +++ b/hmp-commands-info.hx @@ -1,17 +1,21 @@ -HXCOMM Use DEFHEADING() to define headings in both help text and texi -HXCOMM Text between STEXI and ETEXI are copied to texi version and -HXCOMM discarded from C version +HXCOMM Use DEFHEADING() to define headings in both help text and rST. +HXCOMM Text between SRST and ERST is copied to the rST version and +HXCOMM discarded from C version. HXCOMM DEF(command, args, callback, arg_string, help) is used to construct HXCOMM monitor info commands -HXCOMM HXCOMM can be used for comments, discarded from both texi and C +HXCOMM HXCOMM can be used for comments, discarded from both rST and C. +HXCOMM +HXCOMM In this file, generally SRST fragments should have two extra +HXCOMM spaces of indent, so that the documentation list item for "info foo" +HXCOMM appears inside the documentation list item for the top level +HXCOMM "info" documentation entry. The exception is the first SRST +HXCOMM fragment that defines that top level entry. -STEXI -@table @option -@item info @var{subcommand} -@findex info -Show various information about the system state. -@table @option -ETEXI +SRST +``info`` *subcommand* + Show various information about the system state. + +ERST { .name = "version", @@ -22,11 +26,10 @@ ETEXI .flags = "p", }, -STEXI -@item info version -@findex info version -Show the version of QEMU. -ETEXI +SRST + ``info version`` + Show the version of QEMU. +ERST { .name = "network", @@ -36,11 +39,10 @@ ETEXI .cmd = hmp_info_network, }, -STEXI -@item info network -@findex info network -Show the network state. -ETEXI +SRST + ``info network`` + Show the network state. +ERST { .name = "chardev", @@ -51,11 +53,10 @@ ETEXI .flags = "p", }, -STEXI -@item info chardev -@findex info chardev -Show the character devices. -ETEXI +SRST + ``info chardev`` + Show the character devices. +ERST { .name = "block", @@ -66,11 +67,10 @@ ETEXI .cmd = hmp_info_block, }, -STEXI -@item info block -@findex info block -Show info of one block device or all block devices. -ETEXI +SRST + ``info block`` + Show info of one block device or all block devices. +ERST { .name = "blockstats", @@ -80,11 +80,10 @@ ETEXI .cmd = hmp_info_blockstats, }, -STEXI -@item info blockstats -@findex info blockstats -Show block device statistics. -ETEXI +SRST + ``info blockstats`` + Show block device statistics. +ERST { .name = "block-jobs", @@ -94,11 +93,10 @@ ETEXI .cmd = hmp_info_block_jobs, }, -STEXI -@item info block-jobs -@findex info block-jobs -Show progress of ongoing block device operations. -ETEXI +SRST + ``info block-jobs`` + Show progress of ongoing block device operations. +ERST { .name = "registers", @@ -108,11 +106,10 @@ ETEXI .cmd = hmp_info_registers, }, -STEXI -@item info registers -@findex info registers -Show the cpu registers. -ETEXI +SRST + ``info registers`` + Show the cpu registers. +ERST #if defined(TARGET_I386) { @@ -125,11 +122,10 @@ ETEXI }, #endif -STEXI -@item info lapic -@findex info lapic -Show local APIC state -ETEXI +SRST + ``info lapic`` + Show local APIC state +ERST #if defined(TARGET_I386) { @@ -141,11 +137,10 @@ ETEXI }, #endif -STEXI -@item info ioapic -@findex info ioapic -Show io APIC state -ETEXI +SRST + ``info ioapic`` + Show io APIC state +ERST { .name = "cpus", @@ -155,11 +150,10 @@ ETEXI .cmd = hmp_info_cpus, }, -STEXI -@item info cpus -@findex info cpus -Show infos for each CPU. -ETEXI +SRST + ``info cpus`` + Show infos for each CPU. +ERST { .name = "history", @@ -170,11 +164,10 @@ ETEXI .flags = "p", }, -STEXI -@item info history -@findex info history -Show the command line history. -ETEXI +SRST + ``info history`` + Show the command line history. +ERST { .name = "irq", @@ -184,11 +177,10 @@ ETEXI .cmd = hmp_info_irq, }, -STEXI -@item info irq -@findex info irq -Show the interrupts statistics (if available). -ETEXI +SRST + ``info irq`` + Show the interrupts statistics (if available). +ERST { .name = "pic", @@ -198,11 +190,10 @@ ETEXI .cmd = hmp_info_pic, }, -STEXI -@item info pic -@findex info pic -Show PIC state. -ETEXI +SRST + ``info pic`` + Show PIC state. +ERST { .name = "rdma", @@ -212,11 +203,10 @@ ETEXI .cmd = hmp_info_rdma, }, -STEXI -@item info rdma -@findex info rdma -Show RDMA state. -ETEXI +SRST + ``info rdma`` + Show RDMA state. +ERST { .name = "pci", @@ -226,11 +216,10 @@ ETEXI .cmd = hmp_info_pci, }, -STEXI -@item info pci -@findex info pci -Show PCI information. -ETEXI +SRST + ``info pci`` + Show PCI information. +ERST #if defined(TARGET_I386) || defined(TARGET_SH4) || defined(TARGET_SPARC) || \ defined(TARGET_PPC) || defined(TARGET_XTENSA) || defined(TARGET_M68K) @@ -243,11 +232,10 @@ ETEXI }, #endif -STEXI -@item info tlb -@findex info tlb -Show virtual to physical memory mappings. -ETEXI +SRST + ``info tlb`` + Show virtual to physical memory mappings. +ERST #if defined(TARGET_I386) || defined(TARGET_RISCV) { @@ -259,11 +247,10 @@ ETEXI }, #endif -STEXI -@item info mem -@findex info mem -Show the active virtual memory mappings. -ETEXI +SRST + ``info mem`` + Show the active virtual memory mappings. +ERST { .name = "mtree", @@ -275,11 +262,10 @@ ETEXI .cmd = hmp_info_mtree, }, -STEXI -@item info mtree -@findex info mtree -Show memory tree. -ETEXI +SRST + ``info mtree`` + Show memory tree. +ERST #if defined(CONFIG_TCG) { @@ -291,11 +277,10 @@ ETEXI }, #endif -STEXI -@item info jit -@findex info jit -Show dynamic compiler info. -ETEXI +SRST + ``info jit`` + Show dynamic compiler info. +ERST #if defined(CONFIG_TCG) { @@ -307,11 +292,10 @@ ETEXI }, #endif -STEXI -@item info opcount -@findex info opcount -Show dynamic compiler opcode counters -ETEXI +SRST + ``info opcount`` + Show dynamic compiler opcode counters +ERST { .name = "sync-profile", @@ -324,16 +308,20 @@ ETEXI .cmd = hmp_info_sync_profile, }, -STEXI -@item info sync-profile [-m|-n] [@var{max}] -@findex info sync-profile -Show synchronization profiling info, up to @var{max} entries (default: 10), -sorted by total wait time. - -m: sort by mean wait time - -n: do not coalesce objects with the same call site -When different objects that share the same call site are coalesced, the "Object" -field shows---enclosed in brackets---the number of objects being coalesced. -ETEXI +SRST + ``info sync-profile [-m|-n]`` [*max*] + Show synchronization profiling info, up to *max* entries (default: 10), + sorted by total wait time. + + ``-m`` + sort by mean wait time + ``-n`` + do not coalesce objects with the same call site + + When different objects that share the same call site are coalesced, + the "Object" field shows---enclosed in brackets---the number of objects + being coalesced. +ERST { .name = "kvm", @@ -343,11 +331,10 @@ ETEXI .cmd = hmp_info_kvm, }, -STEXI -@item info kvm -@findex info kvm -Show KVM information. -ETEXI +SRST + ``info kvm`` + Show KVM information. +ERST { .name = "numa", @@ -357,11 +344,10 @@ ETEXI .cmd = hmp_info_numa, }, -STEXI -@item info numa -@findex info numa -Show NUMA information. -ETEXI +SRST + ``info numa`` + Show NUMA information. +ERST { .name = "usb", @@ -371,11 +357,10 @@ ETEXI .cmd = hmp_info_usb, }, -STEXI -@item info usb -@findex info usb -Show guest USB devices. -ETEXI +SRST + ``info usb`` + Show guest USB devices. +ERST { .name = "usbhost", @@ -385,11 +370,10 @@ ETEXI .cmd = hmp_info_usbhost, }, -STEXI -@item info usbhost -@findex info usbhost -Show host USB devices. -ETEXI +SRST + ``info usbhost`` + Show host USB devices. +ERST { .name = "profile", @@ -399,11 +383,10 @@ ETEXI .cmd = hmp_info_profile, }, -STEXI -@item info profile -@findex info profile -Show profiling information. -ETEXI +SRST + ``info profile`` + Show profiling information. +ERST { .name = "capture", @@ -413,11 +396,10 @@ ETEXI .cmd = hmp_info_capture, }, -STEXI -@item info capture -@findex info capture -Show capture information. -ETEXI +SRST + ``info capture`` + Show capture information. +ERST { .name = "snapshots", @@ -427,11 +409,10 @@ ETEXI .cmd = hmp_info_snapshots, }, -STEXI -@item info snapshots -@findex info snapshots -Show the currently saved VM snapshots. -ETEXI +SRST + ``info snapshots`` + Show the currently saved VM snapshots. +ERST { .name = "status", @@ -442,11 +423,10 @@ ETEXI .flags = "p", }, -STEXI -@item info status -@findex info status -Show the current VM status (running|paused). -ETEXI +SRST + ``info status`` + Show the current VM status (running|paused). +ERST { .name = "mice", @@ -456,11 +436,10 @@ ETEXI .cmd = hmp_info_mice, }, -STEXI -@item info mice -@findex info mice -Show which guest mouse is receiving events. -ETEXI +SRST + ``info mice`` + Show which guest mouse is receiving events. +ERST #if defined(CONFIG_VNC) { @@ -472,11 +451,10 @@ ETEXI }, #endif -STEXI -@item info vnc -@findex info vnc -Show the vnc server status. -ETEXI +SRST + ``info vnc`` + Show the vnc server status. +ERST #if defined(CONFIG_SPICE) { @@ -488,11 +466,10 @@ ETEXI }, #endif -STEXI -@item info spice -@findex info spice -Show the spice server status. -ETEXI +SRST + ``info spice`` + Show the spice server status. +ERST { .name = "name", @@ -503,11 +480,10 @@ ETEXI .flags = "p", }, -STEXI -@item info name -@findex info name -Show the current VM name. -ETEXI +SRST + ``info name`` + Show the current VM name. +ERST { .name = "uuid", @@ -518,11 +494,10 @@ ETEXI .flags = "p", }, -STEXI -@item info uuid -@findex info uuid -Show the current VM UUID. -ETEXI +SRST + ``info uuid`` + Show the current VM UUID. +ERST { .name = "cpustats", @@ -532,11 +507,10 @@ ETEXI .cmd = hmp_info_cpustats, }, -STEXI -@item info cpustats -@findex info cpustats -Show CPU statistics. -ETEXI +SRST + ``info cpustats`` + Show CPU statistics. +ERST #if defined(CONFIG_SLIRP) { @@ -548,11 +522,10 @@ ETEXI }, #endif -STEXI -@item info usernet -@findex info usernet -Show user network stack connection states. -ETEXI +SRST + ``info usernet`` + Show user network stack connection states. +ERST { .name = "migrate", @@ -562,11 +535,10 @@ ETEXI .cmd = hmp_info_migrate, }, -STEXI -@item info migrate -@findex info migrate -Show migration status. -ETEXI +SRST + ``info migrate`` + Show migration status. +ERST { .name = "migrate_capabilities", @@ -576,11 +548,10 @@ ETEXI .cmd = hmp_info_migrate_capabilities, }, -STEXI -@item info migrate_capabilities -@findex info migrate_capabilities -Show current migration capabilities. -ETEXI +SRST + ``info migrate_capabilities`` + Show current migration capabilities. +ERST { .name = "migrate_parameters", @@ -590,11 +561,10 @@ ETEXI .cmd = hmp_info_migrate_parameters, }, -STEXI -@item info migrate_parameters -@findex info migrate_parameters -Show current migration parameters. -ETEXI +SRST + ``info migrate_parameters`` + Show current migration parameters. +ERST { .name = "migrate_cache_size", @@ -604,11 +574,10 @@ ETEXI .cmd = hmp_info_migrate_cache_size, }, -STEXI -@item info migrate_cache_size -@findex info migrate_cache_size -Show current migration xbzrle cache size. -ETEXI +SRST + ``info migrate_cache_size`` + Show current migration xbzrle cache size. +ERST { .name = "balloon", @@ -618,11 +587,10 @@ ETEXI .cmd = hmp_info_balloon, }, -STEXI -@item info balloon -@findex info balloon -Show balloon information. -ETEXI +SRST + ``info balloon`` + Show balloon information. +ERST { .name = "qtree", @@ -632,11 +600,10 @@ ETEXI .cmd = hmp_info_qtree, }, -STEXI -@item info qtree -@findex info qtree -Show device tree. -ETEXI +SRST + ``info qtree`` + Show device tree. +ERST { .name = "qdm", @@ -646,11 +613,10 @@ ETEXI .cmd = hmp_info_qdm, }, -STEXI -@item info qdm -@findex info qdm -Show qdev device model list. -ETEXI +SRST + ``info qdm`` + Show qdev device model list. +ERST { .name = "qom-tree", @@ -661,11 +627,10 @@ ETEXI .flags = "p", }, -STEXI -@item info qom-tree -@findex info qom-tree -Show QOM composition tree. -ETEXI +SRST + ``info qom-tree`` + Show QOM composition tree. +ERST { .name = "roms", @@ -675,11 +640,10 @@ ETEXI .cmd = hmp_info_roms, }, -STEXI -@item info roms -@findex info roms -Show roms. -ETEXI +SRST + ``info roms`` + Show roms. +ERST { .name = "trace-events", @@ -691,11 +655,10 @@ ETEXI .command_completion = info_trace_events_completion, }, -STEXI -@item info trace-events -@findex info trace-events -Show available trace-events & their state. -ETEXI +SRST + ``info trace-events`` + Show available trace-events & their state. +ERST { .name = "tpm", @@ -705,11 +668,10 @@ ETEXI .cmd = hmp_info_tpm, }, -STEXI -@item info tpm -@findex info tpm -Show the TPM device. -ETEXI +SRST + ``info tpm`` + Show the TPM device. +ERST { .name = "memdev", @@ -720,11 +682,10 @@ ETEXI .flags = "p", }, -STEXI -@item info memdev -@findex info memdev -Show memory backends -ETEXI +SRST + ``info memdev`` + Show memory backends +ERST { .name = "memory-devices", @@ -734,11 +695,10 @@ ETEXI .cmd = hmp_info_memory_devices, }, -STEXI -@item info memory-devices -@findex info memory-devices -Show memory devices. -ETEXI +SRST + ``info memory-devices`` + Show memory devices. +ERST { .name = "iothreads", @@ -749,11 +709,10 @@ ETEXI .flags = "p", }, -STEXI -@item info iothreads -@findex info iothreads -Show iothread's identifiers. -ETEXI +SRST + ``info iothreads`` + Show iothread's identifiers. +ERST { .name = "rocker", @@ -763,11 +722,10 @@ ETEXI .cmd = hmp_rocker, }, -STEXI -@item info rocker @var{name} -@findex info rocker -Show rocker switch. -ETEXI +SRST + ``info rocker`` *name* + Show rocker switch. +ERST { .name = "rocker-ports", @@ -777,11 +735,10 @@ ETEXI .cmd = hmp_rocker_ports, }, -STEXI -@item info rocker-ports @var{name}-ports -@findex info rocker-ports -Show rocker ports. -ETEXI +SRST + ``info rocker-ports`` *name*-ports + Show rocker ports. +ERST { .name = "rocker-of-dpa-flows", @@ -791,11 +748,10 @@ ETEXI .cmd = hmp_rocker_of_dpa_flows, }, -STEXI -@item info rocker-of-dpa-flows @var{name} [@var{tbl_id}] -@findex info rocker-of-dpa-flows -Show rocker OF-DPA flow tables. -ETEXI +SRST + ``info rocker-of-dpa-flows`` *name* [*tbl_id*] + Show rocker OF-DPA flow tables. +ERST { .name = "rocker-of-dpa-groups", @@ -805,11 +761,10 @@ ETEXI .cmd = hmp_rocker_of_dpa_groups, }, -STEXI -@item info rocker-of-dpa-groups @var{name} [@var{type}] -@findex info rocker-of-dpa-groups -Show rocker OF-DPA groups. -ETEXI +SRST + ``info rocker-of-dpa-groups`` *name* [*type*] + Show rocker OF-DPA groups. +ERST #if defined(TARGET_S390X) { @@ -821,11 +776,10 @@ ETEXI }, #endif -STEXI -@item info skeys @var{address} -@findex info skeys -Display the value of a storage key (s390 only) -ETEXI +SRST + ``info skeys`` *address* + Display the value of a storage key (s390 only) +ERST #if defined(TARGET_S390X) { @@ -837,11 +791,11 @@ ETEXI }, #endif -STEXI -@item info cmma @var{address} -@findex info cmma -Display the values of the CMMA storage attributes for a range of pages (s390 only) -ETEXI +SRST + ``info cmma`` *address* + Display the values of the CMMA storage attributes for a range of + pages (s390 only) +ERST { .name = "dump", @@ -851,11 +805,10 @@ ETEXI .cmd = hmp_info_dump, }, -STEXI -@item info dump -@findex info dump -Display the latest dump status. -ETEXI +SRST + ``info dump`` + Display the latest dump status. +ERST { .name = "ramblock", @@ -865,11 +818,10 @@ ETEXI .cmd = hmp_info_ramblock, }, -STEXI -@item info ramblock -@findex info ramblock -Dump all the ramblocks of the system. -ETEXI +SRST + ``info ramblock`` + Dump all the ramblocks of the system. +ERST { .name = "hotpluggable-cpus", @@ -880,11 +832,10 @@ ETEXI .flags = "p", }, -STEXI -@item info hotpluggable-cpus -@findex info hotpluggable-cpus -Show information about hotpluggable CPUs -ETEXI +SRST + ``info hotpluggable-cpus`` + Show information about hotpluggable CPUs +ERST { .name = "vm-generation-id", @@ -894,11 +845,10 @@ ETEXI .cmd = hmp_info_vm_generation_id, }, -STEXI -@item info vm-generation-id -@findex info vm-generation-id -Show Virtual Machine Generation ID -ETEXI +SRST + ``info vm-generation-id`` + Show Virtual Machine Generation ID +ERST { .name = "memory_size_summary", @@ -909,12 +859,11 @@ ETEXI .cmd = hmp_info_memory_size_summary, }, -STEXI -@item info memory_size_summary -@findex info memory_size_summary -Display the amount of initially allocated and present hotpluggable (if -enabled) memory in bytes. -ETEXI +SRST + ``info memory_size_summary`` + Display the amount of initially allocated and present hotpluggable (if + enabled) memory in bytes. +ERST #if defined(TARGET_I386) { @@ -926,16 +875,9 @@ ETEXI }, #endif -STEXI -@item info sev -@findex info sev -Show SEV information. -ETEXI +SRST + ``info sev`` + Show SEV information. +ERST -STEXI -@end table -ETEXI -STEXI -@end table -ETEXI diff --git a/hmp-commands.hx b/hmp-commands.hx index cfcc044ce4..7f0f3974ad 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -1,13 +1,10 @@ -HXCOMM Use DEFHEADING() to define headings in both help text and texi -HXCOMM Text between STEXI and ETEXI are copied to texi version and -HXCOMM discarded from C version +HXCOMM Use DEFHEADING() to define headings in both help text and rST. +HXCOMM Text between SRST and ERST is copied to the rST version and +HXCOMM discarded from C version. HXCOMM DEF(command, args, callback, arg_string, help) is used to construct HXCOMM monitor commands -HXCOMM HXCOMM can be used for comments, discarded from both texi and C +HXCOMM HXCOMM can be used for comments, discarded from both rST and C. -STEXI -@table @option -ETEXI { .name = "help|?", @@ -18,11 +15,10 @@ ETEXI .flags = "p", }, -STEXI -@item help or ? [@var{cmd}] -@findex help -Show the help for all commands or just for command @var{cmd}. -ETEXI +SRST +``help`` or ``?`` [*cmd*] + Show the help for all commands or just for command *cmd*. +ERST { .name = "commit", @@ -32,16 +28,16 @@ ETEXI .cmd = hmp_commit, }, -STEXI -@item commit -@findex commit -Commit changes to the disk images (if -snapshot is used) or backing files. -If the backing file is smaller than the snapshot, then the backing file will be -resized to be the same size as the snapshot. If the snapshot is smaller than -the backing file, the backing file will not be truncated. If you want the -backing file to match the size of the smaller snapshot, you can safely truncate -it yourself once the commit operation successfully completes. -ETEXI +SRST +``commit`` + Commit changes to the disk images (if -snapshot is used) or backing files. + If the backing file is smaller than the snapshot, then the backing file + will be resized to be the same size as the snapshot. If the snapshot is + smaller than the backing file, the backing file will not be truncated. + If you want the backing file to match the size of the smaller snapshot, + you can safely truncate it yourself once the commit operation successfully + completes. +ERST { .name = "q|quit", @@ -51,11 +47,10 @@ ETEXI .cmd = hmp_quit, }, -STEXI -@item q or quit -@findex quit -Quit the emulator. -ETEXI +SRST +``q`` or ``quit`` + Quit the emulator. +ERST { .name = "exit_preconfig", @@ -66,15 +61,14 @@ ETEXI .flags = "p", }, -STEXI -@item exit_preconfig -@findex exit_preconfig -This command makes QEMU exit the preconfig state and proceed with -VM initialization using configuration data provided on the command line -and via the QMP monitor during the preconfig state. The command is only -available during the preconfig state (i.e. when the --preconfig command -line option was in use). -ETEXI +SRST +``exit_preconfig`` + This command makes QEMU exit the preconfig state and proceed with + VM initialization using configuration data provided on the command line + and via the QMP monitor during the preconfig state. The command is only + available during the preconfig state (i.e. when the --preconfig command + line option was in use). +ERST { .name = "block_resize", @@ -84,14 +78,13 @@ ETEXI .cmd = hmp_block_resize, }, -STEXI -@item block_resize -@findex block_resize -Resize a block image while a guest is running. Usually requires guest -action to see the updated size. Resize to a lower size is supported, -but should be used with extreme caution. Note that this command only -resizes image files, it can not resize block devices like LVM volumes. -ETEXI +SRST +``block_resize`` + Resize a block image while a guest is running. Usually requires guest + action to see the updated size. Resize to a lower size is supported, + but should be used with extreme caution. Note that this command only + resizes image files, it can not resize block devices like LVM volumes. +ERST { .name = "block_stream", @@ -101,11 +94,10 @@ ETEXI .cmd = hmp_block_stream, }, -STEXI -@item block_stream -@findex block_stream -Copy data from a backing file into a block device. -ETEXI +SRST +``block_stream`` + Copy data from a backing file into a block device. +ERST { .name = "block_job_set_speed", @@ -115,11 +107,10 @@ ETEXI .cmd = hmp_block_job_set_speed, }, -STEXI -@item block_job_set_speed -@findex block_job_set_speed -Set maximum speed for a background block operation. -ETEXI +SRST +``block_job_set_speed`` + Set maximum speed for a background block operation. +ERST { .name = "block_job_cancel", @@ -131,11 +122,10 @@ ETEXI .cmd = hmp_block_job_cancel, }, -STEXI -@item block_job_cancel -@findex block_job_cancel -Stop an active background block operation (streaming, mirroring). -ETEXI +SRST +``block_job_cancel`` + Stop an active background block operation (streaming, mirroring). +ERST { .name = "block_job_complete", @@ -145,12 +135,11 @@ ETEXI .cmd = hmp_block_job_complete, }, -STEXI -@item block_job_complete -@findex block_job_complete -Manually trigger completion of an active background block operation. -For mirroring, this will switch the device to the destination path. -ETEXI +SRST +``block_job_complete`` + Manually trigger completion of an active background block operation. + For mirroring, this will switch the device to the destination path. +ERST { .name = "block_job_pause", @@ -160,11 +149,10 @@ ETEXI .cmd = hmp_block_job_pause, }, -STEXI -@item block_job_pause -@findex block_job_pause -Pause an active block streaming operation. -ETEXI +SRST +``block_job_pause`` + Pause an active block streaming operation. +ERST { .name = "block_job_resume", @@ -174,11 +162,10 @@ ETEXI .cmd = hmp_block_job_resume, }, -STEXI -@item block_job_resume -@findex block_job_resume -Resume a paused block streaming operation. -ETEXI +SRST +``block_job_resume`` + Resume a paused block streaming operation. +ERST { .name = "eject", @@ -188,11 +175,10 @@ ETEXI .cmd = hmp_eject, }, -STEXI -@item eject [-f] @var{device} -@findex eject -Eject a removable medium (use -f to force it). -ETEXI +SRST +``eject [-f]`` *device* + Eject a removable medium (use -f to force it). +ERST { .name = "drive_del", @@ -202,16 +188,15 @@ ETEXI .cmd = hmp_drive_del, }, -STEXI -@item drive_del @var{device} -@findex drive_del -Remove host block device. The result is that guest generated IO is no longer -submitted against the host device underlying the disk. Once a drive has -been deleted, the QEMU Block layer returns -EIO which results in IO -errors in the guest for applications that are reading/writing to the device. -These errors are always reported to the guest, regardless of the drive's error -actions (drive options rerror, werror). -ETEXI +SRST +``drive_del`` *device* + Remove host block device. The result is that guest generated IO is no longer + submitted against the host device underlying the disk. Once a drive has + been deleted, the QEMU Block layer returns -EIO which results in IO + errors in the guest for applications that are reading/writing to the device. + These errors are always reported to the guest, regardless of the drive's error + actions (drive options rerror, werror). +ERST { .name = "change", @@ -221,56 +206,45 @@ ETEXI .cmd = hmp_change, }, -STEXI -@item change @var{device} @var{setting} -@findex change -Change the configuration of a device. +SRST +``change`` *device* *setting* + Change the configuration of a device. -@table @option -@item change @var{diskdevice} @var{filename} [@var{format} [@var{read-only-mode}]] -Change the medium for a removable disk device to point to @var{filename}. eg + ``change`` *diskdevice* *filename* [*format* [*read-only-mode*]] + Change the medium for a removable disk device to point to *filename*. eg:: -@example -(qemu) change ide1-cd0 /path/to/some.iso -@end example + (qemu) change ide1-cd0 /path/to/some.iso -@var{format} is optional. + *format* is optional. -@var{read-only-mode} may be used to change the read-only status of the device. -It accepts the following values: + *read-only-mode* may be used to change the read-only status of the device. + It accepts the following values: -@table @var -@item retain -Retains the current status; this is the default. + retain + Retains the current status; this is the default. -@item read-only -Makes the device read-only. + read-only + Makes the device read-only. -@item read-write -Makes the device writable. -@end table + read-write + Makes the device writable. -@item change vnc @var{display},@var{options} -Change the configuration of the VNC server. The valid syntax for @var{display} -and @var{options} are described at @ref{sec_invocation}. eg + ``change vnc`` *display*,\ *options* + Change the configuration of the VNC server. The valid syntax for *display* + and *options* are described at :ref:`sec_005finvocation`. eg:: -@example -(qemu) change vnc localhost:1 -@end example + (qemu) change vnc localhost:1 -@item change vnc password [@var{password}] + ``change vnc password`` [*password*] -Change the password associated with the VNC server. If the new password is not -supplied, the monitor will prompt for it to be entered. VNC passwords are only -significant up to 8 letters. eg + Change the password associated with the VNC server. If the new password + is not supplied, the monitor will prompt for it to be entered. VNC + passwords are only significant up to 8 letters. eg:: -@example -(qemu) change vnc password -Password: ******** -@end example + (qemu) change vnc password + Password: ******** -@end table -ETEXI +ERST { .name = "screendump", @@ -281,11 +255,10 @@ ETEXI .cmd = hmp_screendump, }, -STEXI -@item screendump @var{filename} -@findex screendump -Save screen into PPM image @var{filename}. -ETEXI +SRST +``screendump`` *filename* + Save screen into PPM image *filename*. +ERST { .name = "logfile", @@ -295,11 +268,10 @@ ETEXI .cmd = hmp_logfile, }, -STEXI -@item logfile @var{filename} -@findex logfile -Output logs to @var{filename}. -ETEXI +SRST +``logfile`` *filename* + Output logs to *filename*. +ERST { .name = "trace-event", @@ -311,11 +283,10 @@ ETEXI .command_completion = trace_event_completion, }, -STEXI -@item trace-event -@findex trace-event -changes status of a trace event -ETEXI +SRST +``trace-event`` + changes status of a trace event +ERST #if defined(CONFIG_TRACE_SIMPLE) { @@ -326,11 +297,11 @@ ETEXI .cmd = hmp_trace_file, }, -STEXI -@item trace-file on|off|flush -@findex trace-file -Open, close, or flush the trace file. If no argument is given, the status of the trace file is displayed. -ETEXI +SRST +``trace-file on|off|flush`` + Open, close, or flush the trace file. If no argument is given, the + status of the trace file is displayed. +ERST #endif { @@ -341,11 +312,10 @@ ETEXI .cmd = hmp_log, }, -STEXI -@item log @var{item1}[,...] -@findex log -Activate logging of the specified items. -ETEXI +SRST +``log`` *item1*\ [,...] + Activate logging of the specified items. +ERST { .name = "savevm", @@ -355,17 +325,16 @@ ETEXI .cmd = hmp_savevm, }, -STEXI -@item savevm @var{tag} -@findex savevm -Create a snapshot of the whole virtual machine. If @var{tag} is -provided, it is used as human readable identifier. If there is already -a snapshot with the same tag, it is replaced. More info at -@ref{vm_snapshots}. +SRST +``savevm`` *tag* + Create a snapshot of the whole virtual machine. If *tag* is + provided, it is used as human readable identifier. If there is already + a snapshot with the same tag, it is replaced. More info at + :ref:`vm_005fsnapshots`. -Since 4.0, savevm stopped allowing the snapshot id to be set, accepting -only @var{tag} as parameter. -ETEXI + Since 4.0, savevm stopped allowing the snapshot id to be set, accepting + only *tag* as parameter. +ERST { .name = "loadvm", @@ -376,14 +345,13 @@ ETEXI .command_completion = loadvm_completion, }, -STEXI -@item loadvm @var{tag} -@findex loadvm -Set the whole virtual machine to the snapshot identified by the tag -@var{tag}. +SRST +``loadvm`` *tag* + Set the whole virtual machine to the snapshot identified by the tag + *tag*. -Since 4.0, loadvm stopped accepting snapshot id as parameter. -ETEXI + Since 4.0, loadvm stopped accepting snapshot id as parameter. +ERST { .name = "delvm", @@ -394,14 +362,13 @@ ETEXI .command_completion = delvm_completion, }, -STEXI -@item delvm @var{tag} -@findex delvm -Delete the snapshot identified by @var{tag}. +SRST +``delvm`` *tag* + Delete the snapshot identified by *tag*. -Since 4.0, delvm stopped deleting snapshots by snapshot id, accepting -only @var{tag} as parameter. -ETEXI + Since 4.0, delvm stopped deleting snapshots by snapshot id, accepting + only *tag* as parameter. +ERST { .name = "singlestep", @@ -411,12 +378,11 @@ ETEXI .cmd = hmp_singlestep, }, -STEXI -@item singlestep [off] -@findex singlestep -Run the emulation in single step mode. -If called with option off, the emulation returns to normal mode. -ETEXI +SRST +``singlestep [off]`` + Run the emulation in single step mode. + If called with option off, the emulation returns to normal mode. +ERST { .name = "stop", @@ -426,11 +392,10 @@ ETEXI .cmd = hmp_stop, }, -STEXI -@item stop -@findex stop -Stop emulation. -ETEXI +SRST +``stop`` + Stop emulation. +ERST { .name = "c|cont", @@ -440,11 +405,10 @@ ETEXI .cmd = hmp_cont, }, -STEXI -@item c or cont -@findex cont -Resume emulation. -ETEXI +SRST +``c`` or ``cont`` + Resume emulation. +ERST { .name = "system_wakeup", @@ -454,11 +418,10 @@ ETEXI .cmd = hmp_system_wakeup, }, -STEXI -@item system_wakeup -@findex system_wakeup -Wakeup guest from suspend. -ETEXI +SRST +``system_wakeup`` + Wakeup guest from suspend. +ERST { .name = "gdbserver", @@ -468,11 +431,10 @@ ETEXI .cmd = hmp_gdbserver, }, -STEXI -@item gdbserver [@var{port}] -@findex gdbserver -Start gdbserver session (default @var{port}=1234) -ETEXI +SRST +``gdbserver`` [*port*] + Start gdbserver session (default *port*\=1234) +ERST { .name = "x", @@ -482,11 +444,10 @@ ETEXI .cmd = hmp_memory_dump, }, -STEXI -@item x/fmt @var{addr} -@findex x -Virtual memory dump starting at @var{addr}. -ETEXI +SRST +``x/``\ *fmt* *addr* + Virtual memory dump starting at *addr*. +ERST { .name = "xp", @@ -496,64 +457,54 @@ ETEXI .cmd = hmp_physical_memory_dump, }, -STEXI -@item xp /@var{fmt} @var{addr} -@findex xp -Physical memory dump starting at @var{addr}. +SRST +``xp /``\ *fmt* *addr* + Physical memory dump starting at *addr*. -@var{fmt} is a format which tells the command how to format the -data. Its syntax is: @option{/@{count@}@{format@}@{size@}} + *fmt* is a format which tells the command how to format the + data. Its syntax is: ``/{count}{format}{size}`` -@table @var -@item count -is the number of items to be dumped. + *count* + is the number of items to be dumped. + *format* + can be x (hex), d (signed decimal), u (unsigned decimal), o (octal), + c (char) or i (asm instruction). + *size* + can be b (8 bits), h (16 bits), w (32 bits) or g (64 bits). On x86, + ``h`` or ``w`` can be specified with the ``i`` format to + respectively select 16 or 32 bit code instruction size. -@item format -can be x (hex), d (signed decimal), u (unsigned decimal), o (octal), -c (char) or i (asm instruction). + Examples: -@item size -can be b (8 bits), h (16 bits), w (32 bits) or g (64 bits). On x86, -@code{h} or @code{w} can be specified with the @code{i} format to -respectively select 16 or 32 bit code instruction size. + Dump 10 instructions at the current instruction pointer:: -@end table + (qemu) x/10i $eip + 0x90107063: ret + 0x90107064: sti + 0x90107065: lea 0x0(%esi,1),%esi + 0x90107069: lea 0x0(%edi,1),%edi + 0x90107070: ret + 0x90107071: jmp 0x90107080 + 0x90107073: nop + 0x90107074: nop + 0x90107075: nop + 0x90107076: nop -Examples: -@itemize -@item -Dump 10 instructions at the current instruction pointer: -@example -(qemu) x/10i $eip -0x90107063: ret -0x90107064: sti -0x90107065: lea 0x0(%esi,1),%esi -0x90107069: lea 0x0(%edi,1),%edi -0x90107070: ret -0x90107071: jmp 0x90107080 -0x90107073: nop -0x90107074: nop -0x90107075: nop -0x90107076: nop -@end example + Dump 80 16 bit values at the start of the video memory:: -@item -Dump 80 16 bit values at the start of the video memory. -@smallexample -(qemu) xp/80hx 0xb8000 -0x000b8000: 0x0b50 0x0b6c 0x0b65 0x0b78 0x0b38 0x0b36 0x0b2f 0x0b42 -0x000b8010: 0x0b6f 0x0b63 0x0b68 0x0b73 0x0b20 0x0b56 0x0b47 0x0b41 -0x000b8020: 0x0b42 0x0b69 0x0b6f 0x0b73 0x0b20 0x0b63 0x0b75 0x0b72 -0x000b8030: 0x0b72 0x0b65 0x0b6e 0x0b74 0x0b2d 0x0b63 0x0b76 0x0b73 -0x000b8040: 0x0b20 0x0b30 0x0b35 0x0b20 0x0b4e 0x0b6f 0x0b76 0x0b20 -0x000b8050: 0x0b32 0x0b30 0x0b30 0x0b33 0x0720 0x0720 0x0720 0x0720 -0x000b8060: 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 -0x000b8070: 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 -0x000b8080: 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 -0x000b8090: 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 -@end smallexample -@end itemize -ETEXI + (qemu) xp/80hx 0xb8000 + 0x000b8000: 0x0b50 0x0b6c 0x0b65 0x0b78 0x0b38 0x0b36 0x0b2f 0x0b42 + 0x000b8010: 0x0b6f 0x0b63 0x0b68 0x0b73 0x0b20 0x0b56 0x0b47 0x0b41 + 0x000b8020: 0x0b42 0x0b69 0x0b6f 0x0b73 0x0b20 0x0b63 0x0b75 0x0b72 + 0x000b8030: 0x0b72 0x0b65 0x0b6e 0x0b74 0x0b2d 0x0b63 0x0b76 0x0b73 + 0x000b8040: 0x0b20 0x0b30 0x0b35 0x0b20 0x0b4e 0x0b6f 0x0b76 0x0b20 + 0x000b8050: 0x0b32 0x0b30 0x0b30 0x0b33 0x0720 0x0720 0x0720 0x0720 + 0x000b8060: 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 + 0x000b8070: 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 + 0x000b8080: 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 + 0x000b8090: 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 + +ERST { .name = "gpa2hva", @@ -563,12 +514,11 @@ ETEXI .cmd = hmp_gpa2hva, }, -STEXI -@item gpa2hva @var{addr} -@findex gpa2hva -Print the host virtual address at which the guest's physical address @var{addr} -is mapped. -ETEXI +SRST +``gpa2hva`` *addr* + Print the host virtual address at which the guest's physical address *addr* + is mapped. +ERST #ifdef CONFIG_LINUX { @@ -580,12 +530,11 @@ ETEXI }, #endif -STEXI -@item gpa2hpa @var{addr} -@findex gpa2hpa -Print the host physical address at which the guest's physical address @var{addr} -is mapped. -ETEXI +SRST +``gpa2hpa`` *addr* + Print the host physical address at which the guest's physical address *addr* + is mapped. +ERST { .name = "gva2gpa", @@ -595,12 +544,11 @@ ETEXI .cmd = hmp_gva2gpa, }, -STEXI -@item gva2gpa @var{addr} -@findex gva2gpa -Print the guest physical address at which the guest's virtual address @var{addr} -is mapped based on the mapping for the current CPU. -ETEXI +SRST +``gva2gpa`` *addr* + Print the guest physical address at which the guest's virtual address *addr* + is mapped based on the mapping for the current CPU. +ERST { .name = "p|print", @@ -610,12 +558,11 @@ ETEXI .cmd = do_print, }, -STEXI -@item p or print/@var{fmt} @var{expr} -@findex print -Print expression value. Only the @var{format} part of @var{fmt} is -used. -ETEXI +SRST +``p`` or ``print/``\ *fmt* *expr* + Print expression value. Only the *format* part of *fmt* is + used. +ERST { .name = "i", @@ -625,11 +572,10 @@ ETEXI .cmd = hmp_ioport_read, }, -STEXI -@item i/@var{fmt} @var{addr} [.@var{index}] -@findex i -Read I/O port. -ETEXI +SRST +``i/``\ *fmt* *addr* [.\ *index*\ ] + Read I/O port. +ERST { .name = "o", @@ -639,11 +585,10 @@ ETEXI .cmd = hmp_ioport_write, }, -STEXI -@item o/@var{fmt} @var{addr} @var{val} -@findex o -Write to I/O port. -ETEXI +SRST +``o/``\ *fmt* *addr* *val* + Write to I/O port. +ERST { .name = "sendkey", @@ -654,19 +599,17 @@ ETEXI .command_completion = sendkey_completion, }, -STEXI -@item sendkey @var{keys} -@findex sendkey -Send @var{keys} to the guest. @var{keys} could be the name of the -key or the raw value in hexadecimal format. Use @code{-} to press -several keys simultaneously. Example: -@example -sendkey ctrl-alt-f1 -@end example +SRST +``sendkey`` *keys* + Send *keys* to the guest. *keys* could be the name of the + key or the raw value in hexadecimal format. Use ``-`` to press + several keys simultaneously. Example:: -This command is useful to send keys that your graphical user interface -intercepts at low level, such as @code{ctrl-alt-f1} in X Window. -ETEXI + sendkey ctrl-alt-f1 + + This command is useful to send keys that your graphical user interface + intercepts at low level, such as ``ctrl-alt-f1`` in X Window. +ERST { .name = "sync-profile", .args_type = "op:s?", @@ -676,12 +619,11 @@ ETEXI .cmd = hmp_sync_profile, }, -STEXI -@item sync-profile [on|off|reset] -@findex sync-profile -Enable, disable or reset synchronization profiling. With no arguments, prints -whether profiling is on or off. -ETEXI +SRST +``sync-profile [on|off|reset]`` + Enable, disable or reset synchronization profiling. With no arguments, prints + whether profiling is on or off. +ERST { .name = "system_reset", @@ -691,11 +633,10 @@ ETEXI .cmd = hmp_system_reset, }, -STEXI -@item system_reset -@findex system_reset -Reset the system. -ETEXI +SRST +``system_reset`` + Reset the system. +ERST { .name = "system_powerdown", @@ -705,11 +646,10 @@ ETEXI .cmd = hmp_system_powerdown, }, -STEXI -@item system_powerdown -@findex system_powerdown -Power down the system (if supported). -ETEXI +SRST +``system_powerdown`` + Power down the system (if supported). +ERST { .name = "sum", @@ -719,11 +659,10 @@ ETEXI .cmd = hmp_sum, }, -STEXI -@item sum @var{addr} @var{size} -@findex sum -Compute the checksum of a memory region. -ETEXI +SRST +``sum`` *addr* *size* + Compute the checksum of a memory region. +ERST { .name = "device_add", @@ -734,11 +673,10 @@ ETEXI .command_completion = device_add_completion, }, -STEXI -@item device_add @var{config} -@findex device_add -Add device. -ETEXI +SRST +``device_add`` *config* + Add device. +ERST { .name = "device_del", @@ -749,12 +687,11 @@ ETEXI .command_completion = device_del_completion, }, -STEXI -@item device_del @var{id} -@findex device_del -Remove device @var{id}. @var{id} may be a short ID -or a QOM object path. -ETEXI +SRST +``device_del`` *id* + Remove device *id*. *id* may be a short ID + or a QOM object path. +ERST { .name = "cpu", @@ -764,11 +701,10 @@ ETEXI .cmd = hmp_cpu, }, -STEXI -@item cpu @var{index} -@findex cpu -Set the default CPU. -ETEXI +SRST +``cpu`` *index* + Set the default CPU. +ERST { .name = "mouse_move", @@ -778,12 +714,11 @@ ETEXI .cmd = hmp_mouse_move, }, -STEXI -@item mouse_move @var{dx} @var{dy} [@var{dz}] -@findex mouse_move -Move the active mouse to the specified coordinates @var{dx} @var{dy} -with optional scroll axis @var{dz}. -ETEXI +SRST +``mouse_move`` *dx* *dy* [*dz*] + Move the active mouse to the specified coordinates *dx* *dy* + with optional scroll axis *dz*. +ERST { .name = "mouse_button", @@ -793,11 +728,10 @@ ETEXI .cmd = hmp_mouse_button, }, -STEXI -@item mouse_button @var{val} -@findex mouse_button -Change the active mouse button state @var{val} (1=L, 2=M, 4=R). -ETEXI +SRST +``mouse_button`` *val* + Change the active mouse button state *val* (1=L, 2=M, 4=R). +ERST { .name = "mouse_set", @@ -807,15 +741,14 @@ ETEXI .cmd = hmp_mouse_set, }, -STEXI -@item mouse_set @var{index} -@findex mouse_set -Set which mouse device receives events at given @var{index}, index -can be obtained with -@example -info mice -@end example -ETEXI +SRST +``mouse_set`` *index* + Set which mouse device receives events at given *index*, index + can be obtained with:: + + info mice + +ERST { .name = "wavcapture", @@ -824,20 +757,18 @@ ETEXI .help = "capture audio to a wave file (default frequency=44100 bits=16 channels=2)", .cmd = hmp_wavcapture, }, -STEXI -@item wavcapture @var{filename} @var{audiodev} [@var{frequency} [@var{bits} [@var{channels}]]] -@findex wavcapture -Capture audio into @var{filename} from @var{audiodev}, using sample rate -@var{frequency} bits per sample @var{bits} and number of channels -@var{channels}. +SRST +``wavcapture`` *filename* *audiodev* [*frequency* [*bits* [*channels*]]] + Capture audio into *filename* from *audiodev*, using sample rate + *frequency* bits per sample *bits* and number of channels + *channels*. -Defaults: -@itemize @minus -@item Sample rate = 44100 Hz - CD quality -@item Bits = 16 -@item Number of channels = 2 - Stereo -@end itemize -ETEXI + Defaults: + + - Sample rate = 44100 Hz - CD quality + - Bits = 16 + - Number of channels = 2 - Stereo +ERST { .name = "stopcapture", @@ -846,14 +777,13 @@ ETEXI .help = "stop capture", .cmd = hmp_stopcapture, }, -STEXI -@item stopcapture @var{index} -@findex stopcapture -Stop capture with a given @var{index}, index can be obtained with -@example -info capture -@end example -ETEXI +SRST +``stopcapture`` *index* + Stop capture with a given *index*, index can be obtained with:: + + info capture + +ERST { .name = "memsave", @@ -863,11 +793,10 @@ ETEXI .cmd = hmp_memsave, }, -STEXI -@item memsave @var{addr} @var{size} @var{file} -@findex memsave -save to disk virtual memory dump starting at @var{addr} of size @var{size}. -ETEXI +SRST +``memsave`` *addr* *size* *file* + save to disk virtual memory dump starting at *addr* of size *size*. +ERST { .name = "pmemsave", @@ -877,11 +806,10 @@ ETEXI .cmd = hmp_pmemsave, }, -STEXI -@item pmemsave @var{addr} @var{size} @var{file} -@findex pmemsave -save to disk physical memory dump starting at @var{addr} of size @var{size}. -ETEXI +SRST +``pmemsave`` *addr* *size* *file* + save to disk physical memory dump starting at *addr* of size *size*. +ERST { .name = "boot_set", @@ -891,15 +819,14 @@ ETEXI .cmd = hmp_boot_set, }, -STEXI -@item boot_set @var{bootdevicelist} -@findex boot_set -Define new values for the boot device list. Those values will override -the values specified on the command line through the @code{-boot} option. +SRST +``boot_set`` *bootdevicelist* + Define new values for the boot device list. Those values will override + the values specified on the command line through the ``-boot`` option. -The values that can be specified here depend on the machine type, but are -the same that can be specified in the @code{-boot} command line option. -ETEXI + The values that can be specified here depend on the machine type, but are + the same that can be specified in the ``-boot`` command line option. +ERST { .name = "nmi", @@ -908,12 +835,10 @@ ETEXI .help = "inject an NMI", .cmd = hmp_nmi, }, -STEXI -@item nmi @var{cpu} -@findex nmi -Inject an NMI on the default CPU (x86/s390) or all CPUs (ppc64). - -ETEXI +SRST +``nmi`` *cpu* + Inject an NMI on the default CPU (x86/s390) or all CPUs (ppc64). +ERST { .name = "ringbuf_write", @@ -924,13 +849,11 @@ ETEXI .command_completion = ringbuf_write_completion, }, -STEXI -@item ringbuf_write @var{device} @var{data} -@findex ringbuf_write -Write @var{data} to ring buffer character device @var{device}. -@var{data} must be a UTF-8 string. - -ETEXI +SRST +``ringbuf_write`` *device* *data* + Write *data* to ring buffer character device *device*. + *data* must be a UTF-8 string. +ERST { .name = "ringbuf_read", @@ -941,18 +864,16 @@ ETEXI .command_completion = ringbuf_write_completion, }, -STEXI -@item ringbuf_read @var{device} -@findex ringbuf_read -Read and print up to @var{size} bytes from ring buffer character -device @var{device}. -Certain non-printable characters are printed \uXXXX, where XXXX is the -character code in hexadecimal. Character \ is printed \\. -Bug: can screw up when the buffer contains invalid UTF-8 sequences, -NUL characters, after the ring buffer lost data, and when reading -stops because the size limit is reached. - -ETEXI +SRST +``ringbuf_read`` *device* + Read and print up to *size* bytes from ring buffer character + device *device*. + Certain non-printable characters are printed ``\uXXXX``, where ``XXXX`` is the + character code in hexadecimal. Character ``\`` is printed ``\\``. + Bug: can screw up when the buffer contains invalid UTF-8 sequences, + NUL characters, after the ring buffer lost data, and when reading + stops because the size limit is reached. +ERST { .name = "announce_self", @@ -962,16 +883,15 @@ ETEXI .cmd = hmp_announce_self, }, -STEXI -@item announce_self -@findex announce_self -Trigger a round of GARP/RARP broadcasts; this is useful for explicitly updating the -network infrastructure after a reconfiguration or some forms of migration. -The timings of the round are set by the migration announce parameters. -An optional comma separated @var{interfaces} list restricts the announce to the -named set of interfaces. An optional @var{id} can be used to start a separate announce -timer and to change the parameters of it later. -ETEXI +SRST +``announce_self`` + Trigger a round of GARP/RARP broadcasts; this is useful for explicitly + updating the network infrastructure after a reconfiguration or some forms + of migration. The timings of the round are set by the migration announce + parameters. An optional comma separated *interfaces* list restricts the + announce to the named set of interfaces. An optional *id* can be used to + start a separate announce timer and to change the parameters of it later. +ERST { .name = "migrate", @@ -987,13 +907,15 @@ ETEXI }, -STEXI -@item migrate [-d] [-b] [-i] @var{uri} -@findex migrate -Migrate to @var{uri} (using -d to not wait for completion). - -b for migration with full copy of disk - -i for migration with incremental copy of disk (base image is shared) -ETEXI +SRST +``migrate [-d] [-b] [-i]`` *uri* + Migrate to *uri* (using -d to not wait for completion). + + ``-b`` + for migration with full copy of disk + ``-i`` + for migration with incremental copy of disk (base image is shared) +ERST { .name = "migrate_cancel", @@ -1003,11 +925,10 @@ ETEXI .cmd = hmp_migrate_cancel, }, -STEXI -@item migrate_cancel -@findex migrate_cancel -Cancel the current VM migration. -ETEXI +SRST +``migrate_cancel`` + Cancel the current VM migration. +ERST { .name = "migrate_continue", @@ -1016,11 +937,10 @@ ETEXI .help = "Continue migration from the given paused state", .cmd = hmp_migrate_continue, }, -STEXI -@item migrate_continue @var{state} -@findex migrate_continue -Continue migration from the paused state @var{state} -ETEXI +SRST +``migrate_continue`` *state* + Continue migration from the paused state *state* +ERST { .name = "migrate_incoming", @@ -1030,12 +950,11 @@ ETEXI .cmd = hmp_migrate_incoming, }, -STEXI -@item migrate_incoming @var{uri} -@findex migrate_incoming -Continue an incoming migration using the @var{uri} (that has the same syntax -as the -incoming option). -ETEXI +SRST +``migrate_incoming`` *uri* + Continue an incoming migration using the *uri* (that has the same syntax + as the ``-incoming`` option). +ERST { .name = "migrate_recover", @@ -1045,11 +964,10 @@ ETEXI .cmd = hmp_migrate_recover, }, -STEXI -@item migrate_recover @var{uri} -@findex migrate_recover -Continue a paused incoming postcopy migration using the @var{uri}. -ETEXI +SRST +``migrate_recover`` *uri* + Continue a paused incoming postcopy migration using the *uri*. +ERST { .name = "migrate_pause", @@ -1059,11 +977,10 @@ ETEXI .cmd = hmp_migrate_pause, }, -STEXI -@item migrate_pause -@findex migrate_pause -Pause an ongoing migration. Currently it only supports postcopy. -ETEXI +SRST +``migrate_pause`` + Pause an ongoing migration. Currently it only supports postcopy. +ERST { .name = "migrate_set_cache_size", @@ -1078,11 +995,10 @@ ETEXI .cmd = hmp_migrate_set_cache_size, }, -STEXI -@item migrate_set_cache_size @var{value} -@findex migrate_set_cache_size -Set cache size to @var{value} (in bytes) for xbzrle migrations. -ETEXI +SRST +``migrate_set_cache_size`` *value* + Set cache size to *value* (in bytes) for xbzrle migrations. +ERST { .name = "migrate_set_speed", @@ -1093,11 +1009,10 @@ ETEXI .cmd = hmp_migrate_set_speed, }, -STEXI -@item migrate_set_speed @var{value} -@findex migrate_set_speed -Set maximum speed to @var{value} (in bytes) for migrations. -ETEXI +SRST +``migrate_set_speed`` *value* + Set maximum speed to *value* (in bytes) for migrations. +ERST { .name = "migrate_set_downtime", @@ -1107,11 +1022,10 @@ ETEXI .cmd = hmp_migrate_set_downtime, }, -STEXI -@item migrate_set_downtime @var{second} -@findex migrate_set_downtime -Set maximum tolerated downtime (in seconds) for migration. -ETEXI +SRST +``migrate_set_downtime`` *second* + Set maximum tolerated downtime (in seconds) for migration. +ERST { .name = "migrate_set_capability", @@ -1122,11 +1036,10 @@ ETEXI .command_completion = migrate_set_capability_completion, }, -STEXI -@item migrate_set_capability @var{capability} @var{state} -@findex migrate_set_capability -Enable/Disable the usage of a capability @var{capability} for migration. -ETEXI +SRST +``migrate_set_capability`` *capability* *state* + Enable/Disable the usage of a capability *capability* for migration. +ERST { .name = "migrate_set_parameter", @@ -1137,11 +1050,10 @@ ETEXI .command_completion = migrate_set_parameter_completion, }, -STEXI -@item migrate_set_parameter @var{parameter} @var{value} -@findex migrate_set_parameter -Set the parameter @var{parameter} for migration. -ETEXI +SRST +``migrate_set_parameter`` *parameter* *value* + Set the parameter *parameter* for migration. +ERST { .name = "migrate_start_postcopy", @@ -1154,12 +1066,11 @@ ETEXI .cmd = hmp_migrate_start_postcopy, }, -STEXI -@item migrate_start_postcopy -@findex migrate_start_postcopy -Switch in-progress migration to postcopy mode. Ignored after the end of -migration (or once already in postcopy). -ETEXI +SRST +``migrate_start_postcopy`` + Switch in-progress migration to postcopy mode. Ignored after the end of + migration (or once already in postcopy). +ERST { .name = "x_colo_lost_heartbeat", @@ -1170,11 +1081,10 @@ ETEXI .cmd = hmp_x_colo_lost_heartbeat, }, -STEXI -@item x_colo_lost_heartbeat -@findex x_colo_lost_heartbeat -Tell COLO that heartbeat is lost, a failover or takeover is needed. -ETEXI +SRST +``x_colo_lost_heartbeat`` + Tell COLO that heartbeat is lost, a failover or takeover is needed. +ERST { .name = "client_migrate_info", @@ -1184,13 +1094,12 @@ ETEXI .cmd = hmp_client_migrate_info, }, -STEXI -@item client_migrate_info @var{protocol} @var{hostname} @var{port} @var{tls-port} @var{cert-subject} -@findex client_migrate_info -Set migration information for remote display. This makes the server -ask the client to automatically reconnect using the new parameters -once migration finished successfully. Only implemented for SPICE. -ETEXI +SRST +``client_migrate_info`` *protocol* *hostname* *port* *tls-port* *cert-subject* + Set migration information for remote display. This makes the server + ask the client to automatically reconnect using the new parameters + once migration finished successfully. Only implemented for SPICE. +ERST { .name = "dump-guest-memory", @@ -1209,24 +1118,34 @@ ETEXI .cmd = hmp_dump_guest_memory, }, -STEXI -@item dump-guest-memory [-p] @var{filename} @var{begin} @var{length} -@item dump-guest-memory [-z|-l|-s|-w] @var{filename} -@findex dump-guest-memory -Dump guest memory to @var{protocol}. The file can be processed with crash or -gdb. Without -z|-l|-s|-w, the dump format is ELF. - -p: do paging to get guest's memory mapping. - -z: dump in kdump-compressed format, with zlib compression. - -l: dump in kdump-compressed format, with lzo compression. - -s: dump in kdump-compressed format, with snappy compression. - -w: dump in Windows crashdump format (can be used instead of ELF-dump converting), - for Windows x64 guests with vmcoreinfo driver only - filename: dump file name. - begin: the starting physical address. It's optional, and should be - specified together with length. - length: the memory size, in bytes. It's optional, and should be specified - together with begin. -ETEXI +SRST +``dump-guest-memory [-p]`` *filename* *begin* *length* + \ +``dump-guest-memory [-z|-l|-s|-w]`` *filename* + Dump guest memory to *protocol*. The file can be processed with crash or + gdb. Without ``-z|-l|-s|-w``, the dump format is ELF. + + ``-p`` + do paging to get guest's memory mapping. + ``-z`` + dump in kdump-compressed format, with zlib compression. + ``-l`` + dump in kdump-compressed format, with lzo compression. + ``-s`` + dump in kdump-compressed format, with snappy compression. + ``-w`` + dump in Windows crashdump format (can be used instead of ELF-dump converting), + for Windows x64 guests with vmcoreinfo driver only + *filename* + dump file name. + *begin* + the starting physical address. It's optional, and should be + specified together with *length*. + *length* + the memory size, in bytes. It's optional, and should be specified + together with *begin*. + +ERST #if defined(TARGET_S390X) { @@ -1238,11 +1157,10 @@ ETEXI }, #endif -STEXI -@item dump-skeys @var{filename} -@findex dump-skeys -Save guest storage keys to a file. -ETEXI +SRST +``dump-skeys`` *filename* + Save guest storage keys to a file. +ERST #if defined(TARGET_S390X) { @@ -1254,11 +1172,10 @@ ETEXI }, #endif -STEXI -@item migration_mode @var{mode} -@findex migration_mode -Enables or disables migration mode. -ETEXI +SRST +``migration_mode`` *mode* + Enables or disables migration mode. +ERST { .name = "snapshot_blkdev", @@ -1275,11 +1192,10 @@ ETEXI .cmd = hmp_snapshot_blkdev, }, -STEXI -@item snapshot_blkdev -@findex snapshot_blkdev -Snapshot device, using snapshot file as target if provided -ETEXI +SRST +``snapshot_blkdev`` + Snapshot device, using snapshot file as target if provided +ERST { .name = "snapshot_blkdev_internal", @@ -1291,11 +1207,10 @@ ETEXI .cmd = hmp_snapshot_blkdev_internal, }, -STEXI -@item snapshot_blkdev_internal -@findex snapshot_blkdev_internal -Take an internal snapshot on device if it support -ETEXI +SRST +``snapshot_blkdev_internal`` + Take an internal snapshot on device if it support +ERST { .name = "snapshot_delete_blkdev_internal", @@ -1309,11 +1224,10 @@ ETEXI .cmd = hmp_snapshot_delete_blkdev_internal, }, -STEXI -@item snapshot_delete_blkdev_internal -@findex snapshot_delete_blkdev_internal -Delete an internal snapshot on device if it support -ETEXI +SRST +``snapshot_delete_blkdev_internal`` + Delete an internal snapshot on device if it support +ERST { .name = "drive_mirror", @@ -1329,12 +1243,11 @@ ETEXI "so that the result does not need a backing file.\n\t\t\t", .cmd = hmp_drive_mirror, }, -STEXI -@item drive_mirror -@findex drive_mirror -Start mirroring a block device's writes to a new destination, -using the specified target. -ETEXI +SRST +``drive_mirror`` + Start mirroring a block device's writes to a new destination, + using the specified target. +ERST { .name = "drive_backup", @@ -1352,11 +1265,10 @@ ETEXI "(if the target format supports it).\n\t\t\t", .cmd = hmp_drive_backup, }, -STEXI -@item drive_backup -@findex drive_backup -Start a point-in-time copy of a block device to a specificed target. -ETEXI +SRST +``drive_backup`` + Start a point-in-time copy of a block device to a specificed target. +ERST { .name = "drive_add", @@ -1370,11 +1282,10 @@ ETEXI .cmd = hmp_drive_add, }, -STEXI -@item drive_add -@findex drive_add -Add drive to PCI storage controller. -ETEXI +SRST +``drive_add`` + Add drive to PCI storage controller. +ERST { .name = "pcie_aer_inject_error", @@ -1394,11 +1305,10 @@ ETEXI .cmd = hmp_pcie_aer_inject_error, }, -STEXI -@item pcie_aer_inject_error -@findex pcie_aer_inject_error -Inject PCIe AER error -ETEXI +SRST +``pcie_aer_inject_error`` + Inject PCIe AER error +ERST { .name = "netdev_add", @@ -1409,11 +1319,10 @@ ETEXI .command_completion = netdev_add_completion, }, -STEXI -@item netdev_add -@findex netdev_add -Add host network device. -ETEXI +SRST +``netdev_add`` + Add host network device. +ERST { .name = "netdev_del", @@ -1424,11 +1333,10 @@ ETEXI .command_completion = netdev_del_completion, }, -STEXI -@item netdev_del -@findex netdev_del -Remove host network device. -ETEXI +SRST +``netdev_del`` + Remove host network device. +ERST { .name = "object_add", @@ -1439,11 +1347,10 @@ ETEXI .command_completion = object_add_completion, }, -STEXI -@item object_add -@findex object_add -Create QOM object. -ETEXI +SRST +``object_add`` + Create QOM object. +ERST { .name = "object_del", @@ -1454,42 +1361,39 @@ ETEXI .command_completion = object_del_completion, }, -STEXI -@item object_del -@findex object_del -Destroy QOM object. -ETEXI +SRST +``object_del`` + Destroy QOM object. +ERST #ifdef CONFIG_SLIRP { .name = "hostfwd_add", - .args_type = "arg1:s,arg2:s?,arg3:s?", - .params = "[hub_id name]|[netdev_id] [tcp|udp]:[hostaddr]:hostport-[guestaddr]:guestport", + .args_type = "arg1:s,arg2:s?", + .params = "[netdev_id] [tcp|udp]:[hostaddr]:hostport-[guestaddr]:guestport", .help = "redirect TCP or UDP connections from host to guest (requires -net user)", .cmd = hmp_hostfwd_add, }, #endif -STEXI -@item hostfwd_add -@findex hostfwd_add -Redirect TCP or UDP connections from host to guest (requires -net user). -ETEXI +SRST +``hostfwd_add`` + Redirect TCP or UDP connections from host to guest (requires -net user). +ERST #ifdef CONFIG_SLIRP { .name = "hostfwd_remove", - .args_type = "arg1:s,arg2:s?,arg3:s?", - .params = "[hub_id name]|[netdev_id] [tcp|udp]:[hostaddr]:hostport", + .args_type = "arg1:s,arg2:s?", + .params = "[netdev_id] [tcp|udp]:[hostaddr]:hostport", .help = "remove host-to-guest TCP or UDP redirection", .cmd = hmp_hostfwd_remove, }, #endif -STEXI -@item hostfwd_remove -@findex hostfwd_remove -Remove host-to-guest TCP or UDP redirection. -ETEXI +SRST +``hostfwd_remove`` + Remove host-to-guest TCP or UDP redirection. +ERST { .name = "balloon", @@ -1499,11 +1403,10 @@ ETEXI .cmd = hmp_balloon, }, -STEXI -@item balloon @var{value} -@findex balloon -Request VM to change its memory allocation to @var{value} (in MB). -ETEXI +SRST +``balloon`` *value* + Request VM to change its memory allocation to *value* (in MB). +ERST { .name = "set_link", @@ -1514,11 +1417,10 @@ ETEXI .command_completion = set_link_completion, }, -STEXI -@item set_link @var{name} [on|off] -@findex set_link -Switch link @var{name} on (i.e. up) or off (i.e. down). -ETEXI +SRST +``set_link`` *name* ``[on|off]`` + Switch link *name* on (i.e. up) or off (i.e. down). +ERST { .name = "watchdog_action", @@ -1529,11 +1431,10 @@ ETEXI .command_completion = watchdog_action_completion, }, -STEXI -@item watchdog_action -@findex watchdog_action -Change watchdog action. -ETEXI +SRST +``watchdog_action`` + Change watchdog action. +ERST { .name = "acl_show", @@ -1543,14 +1444,13 @@ ETEXI .cmd = hmp_acl_show, }, -STEXI -@item acl_show @var{aclname} -@findex acl_show -List all the matching rules in the access control list, and the default -policy. There are currently two named access control lists, -@var{vnc.x509dname} and @var{vnc.username} matching on the x509 client -certificate distinguished name, and SASL username respectively. -ETEXI +SRST +``acl_show`` *aclname* + List all the matching rules in the access control list, and the default + policy. There are currently two named access control lists, + *vnc.x509dname* and *vnc.username* matching on the x509 client + certificate distinguished name, and SASL username respectively. +ERST { .name = "acl_policy", @@ -1560,13 +1460,12 @@ ETEXI .cmd = hmp_acl_policy, }, -STEXI -@item acl_policy @var{aclname} @code{allow|deny} -@findex acl_policy -Set the default access control list policy, used in the event that -none of the explicit rules match. The default policy at startup is -always @code{deny}. -ETEXI +SRST +``acl_policy`` *aclname* ``allow|deny`` + Set the default access control list policy, used in the event that + none of the explicit rules match. The default policy at startup is + always ``deny``. +ERST { .name = "acl_add", @@ -1576,16 +1475,15 @@ ETEXI .cmd = hmp_acl_add, }, -STEXI -@item acl_add @var{aclname} @var{match} @code{allow|deny} [@var{index}] -@findex acl_add -Add a match rule to the access control list, allowing or denying access. -The match will normally be an exact username or x509 distinguished name, -but can optionally include wildcard globs. eg @code{*@@EXAMPLE.COM} to -allow all users in the @code{EXAMPLE.COM} kerberos realm. The match will -normally be appended to the end of the ACL, but can be inserted -earlier in the list if the optional @var{index} parameter is supplied. -ETEXI +SRST +``acl_add`` *aclname* *match* ``allow|deny`` [*index*] + Add a match rule to the access control list, allowing or denying access. + The match will normally be an exact username or x509 distinguished name, + but can optionally include wildcard globs. eg ``*@EXAMPLE.COM`` to + allow all users in the ``EXAMPLE.COM`` kerberos realm. The match will + normally be appended to the end of the ACL, but can be inserted + earlier in the list if the optional *index* parameter is supplied. +ERST { .name = "acl_remove", @@ -1595,11 +1493,10 @@ ETEXI .cmd = hmp_acl_remove, }, -STEXI -@item acl_remove @var{aclname} @var{match} -@findex acl_remove -Remove the specified match rule from the access control list. -ETEXI +SRST +``acl_remove`` *aclname* *match* + Remove the specified match rule from the access control list. +ERST { .name = "acl_reset", @@ -1609,12 +1506,11 @@ ETEXI .cmd = hmp_acl_reset, }, -STEXI -@item acl_reset @var{aclname} -@findex acl_reset -Remove all matches from the access control list, and set the default -policy back to @code{deny}. -ETEXI +SRST +``acl_reset`` *aclname* + Remove all matches from the access control list, and set the default + policy back to ``deny``. +ERST { .name = "nbd_server_start", @@ -1623,14 +1519,13 @@ ETEXI .help = "serve block devices on the given host and port", .cmd = hmp_nbd_server_start, }, -STEXI -@item nbd_server_start @var{host}:@var{port} -@findex nbd_server_start -Start an NBD server on the given host and/or port. If the @option{-a} -option is included, all of the virtual machine's block devices that -have an inserted media on them are automatically exported; in this case, -the @option{-w} option makes the devices writable too. -ETEXI +SRST +``nbd_server_start`` *host*:*port* + Start an NBD server on the given host and/or port. If the ``-a`` + option is included, all of the virtual machine's block devices that + have an inserted media on them are automatically exported; in this case, + the ``-w`` option makes the devices writable too. +ERST { .name = "nbd_server_add", @@ -1639,14 +1534,13 @@ ETEXI .help = "export a block device via NBD", .cmd = hmp_nbd_server_add, }, -STEXI -@item nbd_server_add @var{device} [ @var{name} ] -@findex nbd_server_add -Export a block device through QEMU's NBD server, which must be started -beforehand with @command{nbd_server_start}. The @option{-w} option makes the -exported device writable too. The export name is controlled by @var{name}, -defaulting to @var{device}. -ETEXI +SRST +``nbd_server_add`` *device* [ *name* ] + Export a block device through QEMU's NBD server, which must be started + beforehand with ``nbd_server_start``. The ``-w`` option makes the + exported device writable too. The export name is controlled by *name*, + defaulting to *device*. +ERST { .name = "nbd_server_remove", @@ -1655,15 +1549,14 @@ ETEXI .help = "remove an export previously exposed via NBD", .cmd = hmp_nbd_server_remove, }, -STEXI -@item nbd_server_remove [-f] @var{name} -@findex nbd_server_remove -Stop exporting a block device through QEMU's NBD server, which was -previously started with @command{nbd_server_add}. The @option{-f} -option forces the server to drop the export immediately even if -clients are connected; otherwise the command fails unless there are no -clients. -ETEXI +SRST +``nbd_server_remove [-f]`` *name* + Stop exporting a block device through QEMU's NBD server, which was + previously started with ``nbd_server_add``. The ``-f`` + option forces the server to drop the export immediately even if + clients are connected; otherwise the command fails unless there are no + clients. +ERST { .name = "nbd_server_stop", @@ -1672,11 +1565,10 @@ ETEXI .help = "stop serving block devices using the NBD protocol", .cmd = hmp_nbd_server_stop, }, -STEXI -@item nbd_server_stop -@findex nbd_server_stop -Stop the QEMU embedded NBD server. -ETEXI +SRST +``nbd_server_stop`` + Stop the QEMU embedded NBD server. +ERST #if defined(TARGET_I386) @@ -1690,11 +1582,10 @@ ETEXI }, #endif -STEXI -@item mce @var{cpu} @var{bank} @var{status} @var{mcgstatus} @var{addr} @var{misc} -@findex mce (x86) -Inject an MCE on the given CPU (x86 only). -ETEXI +SRST +``mce`` *cpu* *bank* *status* *mcgstatus* *addr* *misc* + Inject an MCE on the given CPU (x86 only). +ERST { .name = "getfd", @@ -1704,13 +1595,12 @@ ETEXI .cmd = hmp_getfd, }, -STEXI -@item getfd @var{fdname} -@findex getfd -If a file descriptor is passed alongside this command using the SCM_RIGHTS -mechanism on unix sockets, it is stored using the name @var{fdname} for -later use by other monitor commands. -ETEXI +SRST +``getfd`` *fdname* + If a file descriptor is passed alongside this command using the SCM_RIGHTS + mechanism on unix sockets, it is stored using the name *fdname* for + later use by other monitor commands. +ERST { .name = "closefd", @@ -1720,13 +1610,12 @@ ETEXI .cmd = hmp_closefd, }, -STEXI -@item closefd @var{fdname} -@findex closefd -Close the file descriptor previously assigned to @var{fdname} using the -@code{getfd} command. This is only needed if the file descriptor was never -used by another monitor command. -ETEXI +SRST +``closefd`` *fdname* + Close the file descriptor previously assigned to *fdname* using the + ``getfd`` command. This is only needed if the file descriptor was never + used by another monitor command. +ERST { .name = "block_passwd", @@ -1736,13 +1625,12 @@ ETEXI .cmd = hmp_block_passwd, }, -STEXI -@item block_passwd @var{device} @var{password} -@findex block_passwd -Set the encrypted device @var{device} password to @var{password} +SRST +``block_passwd`` *device* *password* + Set the encrypted device *device* password to *password* -This command is now obsolete and will always return an error since 2.10 -ETEXI + This command is now obsolete and will always return an error since 2.10 +ERST { .name = "block_set_io_throttle", @@ -1752,12 +1640,12 @@ ETEXI .cmd = hmp_block_set_io_throttle, }, -STEXI -@item block_set_io_throttle @var{device} @var{bps} @var{bps_rd} @var{bps_wr} @var{iops} @var{iops_rd} @var{iops_wr} -@findex block_set_io_throttle -Change I/O throttle limits for a block drive to @var{bps} @var{bps_rd} @var{bps_wr} @var{iops} @var{iops_rd} @var{iops_wr}. -@var{device} can be a block device name, a qdev ID or a QOM path. -ETEXI +SRST +``block_set_io_throttle`` *device* *bps* *bps_rd* *bps_wr* *iops* *iops_rd* *iops_wr* + Change I/O throttle limits for a block drive to + *bps* *bps_rd* *bps_wr* *iops* *iops_rd* *iops_wr*. + *device* can be a block device name, a qdev ID or a QOM path. +ERST { .name = "set_password", @@ -1767,16 +1655,15 @@ ETEXI .cmd = hmp_set_password, }, -STEXI -@item set_password [ vnc | spice ] password [ action-if-connected ] -@findex set_password -Change spice/vnc password. Use zero to make the password stay valid -forever. @var{action-if-connected} specifies what should happen in -case a connection is established: @var{fail} makes the password change -fail. @var{disconnect} changes the password and disconnects the -client. @var{keep} changes the password and keeps the connection up. -@var{keep} is the default. -ETEXI +SRST +``set_password [ vnc | spice ] password [ action-if-connected ]`` + Change spice/vnc password. Use zero to make the password stay valid + forever. *action-if-connected* specifies what should happen in + case a connection is established: *fail* makes the password change + fail. *disconnect* changes the password and disconnects the + client. *keep* changes the password and keeps the connection up. + *keep* is the default. +ERST { .name = "expire_password", @@ -1786,28 +1673,22 @@ ETEXI .cmd = hmp_expire_password, }, -STEXI -@item expire_password [ vnc | spice ] expire-time -@findex expire_password -Specify when a password for spice/vnc becomes -invalid. @var{expire-time} accepts: +SRST +``expire_password [ vnc | spice ]`` *expire-time* + Specify when a password for spice/vnc becomes + invalid. *expire-time* accepts: -@table @var -@item now -Invalidate password instantly. + ``now`` + Invalidate password instantly. + ``never`` + Password stays valid forever. + ``+``\ *nsec* + Password stays valid for *nsec* seconds starting now. + *nsec* + Password is invalidated at the given time. *nsec* are the seconds + passed since 1970, i.e. unix epoch. -@item never -Password stays valid forever. - -@item +nsec -Password stays valid for @var{nsec} seconds starting now. - -@item nsec -Password is invalidated at the given time. @var{nsec} are the seconds -passed since 1970, i.e. unix epoch. - -@end table -ETEXI +ERST { .name = "chardev-add", @@ -1818,12 +1699,10 @@ ETEXI .command_completion = chardev_add_completion, }, -STEXI -@item chardev-add args -@findex chardev-add -chardev-add accepts the same parameters as the -chardev command line switch. - -ETEXI +SRST +``chardev-add`` *args* + chardev-add accepts the same parameters as the -chardev command line switch. +ERST { .name = "chardev-change", @@ -1833,13 +1712,11 @@ ETEXI .cmd = hmp_chardev_change, }, -STEXI -@item chardev-change args -@findex chardev-change -chardev-change accepts existing chardev @var{id} and then the same arguments -as the -chardev command line switch (except for "id"). - -ETEXI +SRST +``chardev-change`` *args* + chardev-change accepts existing chardev *id* and then the same arguments + as the -chardev command line switch (except for "id"). +ERST { .name = "chardev-remove", @@ -1850,12 +1727,10 @@ ETEXI .command_completion = chardev_remove_completion, }, -STEXI -@item chardev-remove id -@findex chardev-remove -Removes the chardev @var{id}. - -ETEXI +SRST +``chardev-remove`` *id* + Removes the chardev *id*. +ERST { .name = "chardev-send-break", @@ -1866,27 +1741,25 @@ ETEXI .command_completion = chardev_remove_completion, }, -STEXI -@item chardev-send-break id -@findex chardev-send-break -Send a break on the chardev @var{id}. - -ETEXI +SRST +``chardev-send-break`` *id* + Send a break on the chardev *id*. +ERST { .name = "qemu-io", - .args_type = "device:B,command:s", - .params = "[device] \"[command]\"", - .help = "run a qemu-io command on a block device", + .args_type = "qdev:-d,device:B,command:s", + .params = "[-d] [device] \"[command]\"", + .help = "run a qemu-io command on a block device\n\t\t\t" + "-d: [device] is a device ID rather than a " + "drive ID or node name", .cmd = hmp_qemu_io, }, -STEXI -@item qemu-io @var{device} @var{command} -@findex qemu-io -Executes a qemu-io command on the given block device. - -ETEXI +SRST +``qemu-io`` *device* *command* + Executes a qemu-io command on the given block device. +ERST { .name = "cpu-add", @@ -1896,13 +1769,12 @@ ETEXI .cmd = hmp_cpu_add, }, -STEXI -@item cpu-add @var{id} -@findex cpu-add -Add CPU with id @var{id}. This command is deprecated, please -+use @code{device_add} instead. For details, refer to -'docs/cpu-hotplug.rst'. -ETEXI +SRST +``cpu-add`` *id* + Add CPU with id *id*. This command is deprecated, please + +use ``device_add`` instead. For details, refer to + 'docs/cpu-hotplug.rst'. +ERST { .name = "qom-list", @@ -1913,10 +1785,10 @@ ETEXI .flags = "p", }, -STEXI -@item qom-list [@var{path}] -Print QOM properties of object at location @var{path} -ETEXI +SRST +``qom-list`` [*path*] + Print QOM properties of object at location *path* +ERST { .name = "qom-set", @@ -1927,10 +1799,10 @@ ETEXI .flags = "p", }, -STEXI -@item qom-set @var{path} @var{property} @var{value} -Set QOM property @var{property} of object at location @var{path} to value @var{value} -ETEXI +SRST +``qom-set`` *path* *property* *value* + Set QOM property *property* of object at location *path* to value *value* +ERST { .name = "info", @@ -1942,6 +1814,3 @@ ETEXI .flags = "p", }, -STEXI -@end table -ETEXI diff --git a/hw/9pfs/9p-local.c b/hw/9pfs/9p-local.c index 4708c0bd89..54e012e5b4 100644 --- a/hw/9pfs/9p-local.c +++ b/hw/9pfs/9p-local.c @@ -947,7 +947,7 @@ static int local_link(FsContext *ctx, V9fsPath *oldpath, if (ctx->export_flags & V9FS_SM_MAPPED_FILE && local_is_mapped_file_metadata(ctx, name)) { errno = EINVAL; - return -1; + goto out; } odirfd = local_opendir_nofollow(ctx, odirpath); @@ -1076,7 +1076,7 @@ out: static int local_unlinkat_common(FsContext *ctx, int dirfd, const char *name, int flags) { - int ret = -1; + int ret; if (ctx->export_flags & V9FS_SM_MAPPED_FILE) { int map_dirfd; @@ -1094,12 +1094,12 @@ static int local_unlinkat_common(FsContext *ctx, int dirfd, const char *name, fd = openat_dir(dirfd, name); if (fd == -1) { - goto err_out; + return -1; } ret = unlinkat(fd, VIRTFS_META_DIR, AT_REMOVEDIR); close_preserve_errno(fd); if (ret < 0 && errno != ENOENT) { - goto err_out; + return -1; } } map_dirfd = openat_dir(dirfd, VIRTFS_META_DIR); @@ -1107,16 +1107,14 @@ static int local_unlinkat_common(FsContext *ctx, int dirfd, const char *name, ret = unlinkat(map_dirfd, name, 0); close_preserve_errno(map_dirfd); if (ret < 0 && errno != ENOENT) { - goto err_out; + return -1; } } else if (errno != ENOENT) { - goto err_out; + return -1; } } - ret = unlinkat(dirfd, name, flags); -err_out: - return ret; + return unlinkat(dirfd, name, flags); } static int local_remove(FsContext *ctx, const char *path) @@ -1473,7 +1471,7 @@ static void local_cleanup(FsContext *ctx) g_free(data); } -static void error_append_security_model_hint(Error **errp) +static void error_append_security_model_hint(Error *const *errp) { error_append_hint(errp, "Valid options are: security_model=" "[passthrough|mapped-xattr|mapped-file|none]\n"); diff --git a/hw/9pfs/9p-proxy.c b/hw/9pfs/9p-proxy.c index 97ab9c58a5..6f598a0f11 100644 --- a/hw/9pfs/9p-proxy.c +++ b/hw/9pfs/9p-proxy.c @@ -1114,7 +1114,7 @@ static int connect_namedsocket(const char *path, Error **errp) return sockfd; } -static void error_append_socket_sockfd_hint(Error **errp) +static void error_append_socket_sockfd_hint(Error *const *errp) { error_append_hint(errp, "Either specify socket=/some/path where /some/path" " points to a listening AF_UNIX socket or sock_fd=fd" @@ -1139,10 +1139,10 @@ static int proxy_parse_opts(QemuOpts *opts, FsDriverEntry *fs, Error **errp) } if (socket) { fs->path = g_strdup(socket); - fs->export_flags = V9FS_PROXY_SOCK_NAME; + fs->export_flags |= V9FS_PROXY_SOCK_NAME; } else { fs->path = g_strdup(sock_fd); - fs->export_flags = V9FS_PROXY_SOCK_FD; + fs->export_flags |= V9FS_PROXY_SOCK_FD; } return 0; } 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 520177f40c..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; @@ -2090,22 +2102,29 @@ out_nofid: * with qemu_iovec_destroy(). */ static void v9fs_init_qiov_from_pdu(QEMUIOVector *qiov, V9fsPDU *pdu, - size_t skip, size_t size, + size_t skip, size_t *size, bool is_write) { QEMUIOVector elem; struct iovec *iov; unsigned int niov; + size_t alloc_size = *size + skip; if (is_write) { - pdu->s->transport->init_out_iov_from_pdu(pdu, &iov, &niov, size + skip); + pdu->s->transport->init_out_iov_from_pdu(pdu, &iov, &niov, alloc_size); } else { - pdu->s->transport->init_in_iov_from_pdu(pdu, &iov, &niov, size + skip); + pdu->s->transport->init_in_iov_from_pdu(pdu, &iov, &niov, &alloc_size); + } + + if (alloc_size < skip) { + *size = 0; + } else { + *size = alloc_size - skip; } qemu_iovec_init_external(&elem, iov, niov); qemu_iovec_init(qiov, niov); - qemu_iovec_concat(qiov, &elem, skip, size); + qemu_iovec_concat(qiov, &elem, skip, *size); } static int v9fs_xattr_read(V9fsState *s, V9fsPDU *pdu, V9fsFidState *fidp, @@ -2113,15 +2132,14 @@ static int v9fs_xattr_read(V9fsState *s, V9fsPDU *pdu, V9fsFidState *fidp, { ssize_t err; size_t offset = 7; - uint64_t read_count; + size_t read_count; QEMUIOVector qiov_full; if (fidp->fs.xattr.len < off) { read_count = 0; - } else { + } else if (fidp->fs.xattr.len - off < max_count) { read_count = fidp->fs.xattr.len - off; - } - if (read_count > max_count) { + } else { read_count = max_count; } err = pdu_marshal(pdu, offset, "d", read_count); @@ -2130,7 +2148,7 @@ static int v9fs_xattr_read(V9fsState *s, V9fsPDU *pdu, V9fsFidState *fidp, } offset += err; - v9fs_init_qiov_from_pdu(&qiov_full, pdu, offset, read_count, false); + v9fs_init_qiov_from_pdu(&qiov_full, pdu, offset, &read_count, false); err = v9fs_pack(qiov_full.iov, qiov_full.niov, 0, ((char *)fidp->fs.xattr.value) + off, read_count); @@ -2259,9 +2277,11 @@ static void coroutine_fn v9fs_read(void *opaque) QEMUIOVector qiov_full; QEMUIOVector qiov; int32_t len; + size_t size = max_count; - v9fs_init_qiov_from_pdu(&qiov_full, pdu, offset + 4, max_count, false); + v9fs_init_qiov_from_pdu(&qiov_full, pdu, offset + 4, &size, false); qemu_iovec_init(&qiov, qiov_full.niov); + max_count = size; do { qemu_iovec_reset(&qiov); qemu_iovec_concat(&qiov, &qiov_full, count, qiov_full.size - count); @@ -2414,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); @@ -2422,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; @@ -2464,8 +2493,7 @@ static int v9fs_xattr_write(V9fsState *s, V9fsPDU *pdu, V9fsFidState *fidp, if (fidp->fs.xattr.len < off) { - err = -ENOSPC; - goto out; + return -ENOSPC; } write_count = fidp->fs.xattr.len - off; if (write_count > count) { @@ -2491,7 +2519,7 @@ static int v9fs_xattr_write(V9fsState *s, V9fsPDU *pdu, V9fsFidState *fidp, off += to_copy; write_count -= to_copy; } -out: + return err; } @@ -2504,6 +2532,7 @@ static void coroutine_fn v9fs_write(void *opaque) int32_t len = 0; int32_t total = 0; size_t offset = 7; + size_t size; V9fsFidState *fidp; V9fsPDU *pdu = opaque; V9fsState *s = pdu->s; @@ -2516,7 +2545,9 @@ static void coroutine_fn v9fs_write(void *opaque) return; } offset += err; - v9fs_init_qiov_from_pdu(&qiov_full, pdu, offset, count, true); + size = count; + v9fs_init_qiov_from_pdu(&qiov_full, pdu, offset, &size, true); + count = size; trace_v9fs_write(pdu->tag, pdu->id, fid, off, count, qiov_full.niov); fidp = get_fid(pdu, fid); @@ -3056,8 +3087,7 @@ static int coroutine_fn v9fs_complete_rename(V9fsPDU *pdu, V9fsFidState *fidp, if (newdirfid != -1) { dirfidp = get_fid(pdu, newdirfid); if (dirfidp == NULL) { - err = -ENOENT; - goto out_nofid; + return -ENOENT; } if (fidp->fid_type != P9_FID_NONE) { err = -EINVAL; @@ -3100,7 +3130,6 @@ out: put_fid(pdu, dirfidp); } v9fs_path_free(&new_path); -out_nofid: return err; } diff --git a/hw/9pfs/9p.h b/hw/9pfs/9p.h index 3904f82901..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 @@ -425,7 +436,7 @@ struct V9fsTransport { ssize_t (*pdu_vunmarshal)(V9fsPDU *pdu, size_t offset, const char *fmt, va_list ap); void (*init_in_iov_from_pdu)(V9fsPDU *pdu, struct iovec **piov, - unsigned int *pniov, size_t size); + unsigned int *pniov, size_t *size); void (*init_out_iov_from_pdu)(V9fsPDU *pdu, struct iovec **piov, unsigned int *pniov, size_t size); void (*push_and_notify)(V9fsPDU *pdu); diff --git a/hw/9pfs/virtio-9p-device.c b/hw/9pfs/virtio-9p-device.c index b5a7c03f26..536447a355 100644 --- a/hw/9pfs/virtio-9p-device.c +++ b/hw/9pfs/virtio-9p-device.c @@ -147,19 +147,22 @@ static ssize_t virtio_pdu_vunmarshal(V9fsPDU *pdu, size_t offset, } static void virtio_init_in_iov_from_pdu(V9fsPDU *pdu, struct iovec **piov, - unsigned int *pniov, size_t size) + unsigned int *pniov, size_t *size) { V9fsState *s = pdu->s; V9fsVirtioState *v = container_of(s, V9fsVirtioState, state); VirtQueueElement *elem = v->elems[pdu->idx]; size_t buf_size = iov_size(elem->in_sg, elem->in_num); - if (buf_size < size) { + if (buf_size < P9_IOHDRSZ) { VirtIODevice *vdev = VIRTIO_DEVICE(v); virtio_error(vdev, - "VirtFS reply type %d needs %zu bytes, buffer has %zu", - pdu->id + 1, size, buf_size); + "VirtFS reply type %d needs %zu bytes, buffer has %zu, less than minimum", + pdu->id + 1, *size, buf_size); + } + if (buf_size < *size) { + *size = buf_size; } *piov = elem->in_sg; @@ -215,6 +218,7 @@ static void virtio_9p_device_unrealize(DeviceState *dev, Error **errp) V9fsVirtioState *v = VIRTIO_9P(dev); V9fsState *s = &v->state; + virtio_delete_queue(v->vq); virtio_cleanup(vdev); v9fs_device_unrealize_common(s, errp); } @@ -242,7 +246,7 @@ static void virtio_9p_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); - dc->props = virtio_9p_properties; + device_class_set_props(dc, virtio_9p_properties); dc->vmsd = &vmstate_virtio_9p; set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); vdc->realize = virtio_9p_device_realize; diff --git a/hw/9pfs/xen-9p-backend.c b/hw/9pfs/xen-9p-backend.c index 71eebe12dd..18fe5b7c92 100644 --- a/hw/9pfs/xen-9p-backend.c +++ b/hw/9pfs/xen-9p-backend.c @@ -187,7 +187,7 @@ static void xen_9pfs_init_out_iov_from_pdu(V9fsPDU *pdu, static void xen_9pfs_init_in_iov_from_pdu(V9fsPDU *pdu, struct iovec **piov, unsigned int *pniov, - size_t size) + size_t *size) { Xen9pfsDev *xen_9pfs = container_of(pdu->s, Xen9pfsDev, state); Xen9pfsRing *ring = &xen_9pfs->rings[pdu->tag % xen_9pfs->num_rings]; @@ -197,16 +197,19 @@ static void xen_9pfs_init_in_iov_from_pdu(V9fsPDU *pdu, g_free(ring->sg); ring->sg = g_new0(struct iovec, 2); - xen_9pfs_in_sg(ring, ring->sg, &num, pdu->idx, size); + xen_9pfs_in_sg(ring, ring->sg, &num, pdu->idx, *size); buf_size = iov_size(ring->sg, num); - if (buf_size < size) { + if (buf_size < P9_IOHDRSZ) { xen_pv_printf(&xen_9pfs->xendev, 0, "Xen 9pfs request type %d" - "needs %zu bytes, buffer has %zu\n", pdu->id, size, - buf_size); + "needs %zu bytes, buffer has %zu, less than minimum\n", + pdu->id, *size, buf_size); xen_be_set_state(&xen_9pfs->xendev, XenbusStateClosing); xen_9pfs_disconnect(&xen_9pfs->xendev); } + if (buf_size < *size) { + *size = buf_size; + } *piov = ring->sg; *pniov = num; diff --git a/hw/Kconfig b/hw/Kconfig index b9685b3944..ecf491bf04 100644 --- a/hw/Kconfig +++ b/hw/Kconfig @@ -4,7 +4,6 @@ source acpi/Kconfig source adc/Kconfig source audio/Kconfig source block/Kconfig -source bt/Kconfig source char/Kconfig source core/Kconfig source display/Kconfig diff --git a/hw/Makefile.objs b/hw/Makefile.objs index ac140c2de4..77954ac7db 100644 --- a/hw/Makefile.objs +++ b/hw/Makefile.objs @@ -5,7 +5,6 @@ devices-dirs-y += acpi/ devices-dirs-y += adc/ devices-dirs-y += audio/ devices-dirs-y += block/ -devices-dirs-y += bt/ devices-dirs-y += char/ devices-dirs-y += cpu/ devices-dirs-y += display/ diff --git a/hw/acpi/Kconfig b/hw/acpi/Kconfig index 12e3f1e86e..54209c6f2f 100644 --- a/hw/acpi/Kconfig +++ b/hw/acpi/Kconfig @@ -7,6 +7,7 @@ config ACPI_X86 select ACPI_NVDIMM select ACPI_CPU_HOTPLUG select ACPI_MEMORY_HOTPLUG + select ACPI_HMAT config ACPI_X86_ICH bool @@ -23,6 +24,10 @@ config ACPI_NVDIMM bool depends on ACPI +config ACPI_HMAT + bool + depends on ACPI + config ACPI_PCI bool depends on ACPI && PCI @@ -33,5 +38,3 @@ config ACPI_VMGENID depends on PC config ACPI_HW_REDUCED - bool - depends on ACPI diff --git a/hw/acpi/Makefile.objs b/hw/acpi/Makefile.objs index 655a9c1973..777da07f4d 100644 --- a/hw/acpi/Makefile.objs +++ b/hw/acpi/Makefile.objs @@ -7,7 +7,9 @@ common-obj-$(CONFIG_ACPI_CPU_HOTPLUG) += cpu.o common-obj-$(CONFIG_ACPI_NVDIMM) += nvdimm.o common-obj-$(CONFIG_ACPI_VMGENID) += vmgenid.o common-obj-$(CONFIG_ACPI_HW_REDUCED) += generic_event_device.o +common-obj-$(CONFIG_ACPI_HMAT) += hmat.o common-obj-$(call lnot,$(CONFIG_ACPI_X86)) += acpi-stub.o +common-obj-$(call lnot,$(CONFIG_PC)) += acpi-x86-stub.o common-obj-y += acpi_interface.o common-obj-y += bios-linker-loader.o @@ -20,4 +22,4 @@ common-obj-$(call lnot,$(CONFIG_IPMI)) += ipmi-stub.o else common-obj-y += acpi-stub.o endif -common-obj-$(CONFIG_ALL) += acpi-stub.o ipmi-stub.o +common-obj-$(CONFIG_ALL) += acpi-stub.o acpi-x86-stub.o ipmi-stub.o diff --git a/stubs/pc_madt_cpu_entry.c b/hw/acpi/acpi-x86-stub.c similarity index 100% rename from stubs/pc_madt_cpu_entry.c rename to hw/acpi/acpi-x86-stub.c diff --git a/hw/acpi/cpu.c b/hw/acpi/cpu.c index 87f30a31d7..e2c957ce00 100644 --- a/hw/acpi/cpu.c +++ b/hw/acpi/cpu.c @@ -12,11 +12,13 @@ #define ACPI_CPU_FLAGS_OFFSET_RW 4 #define ACPI_CPU_CMD_OFFSET_WR 5 #define ACPI_CPU_CMD_DATA_OFFSET_RW 8 +#define ACPI_CPU_CMD_DATA2_OFFSET_R 0 enum { CPHP_GET_NEXT_CPU_WITH_EVENT_CMD = 0, CPHP_OST_EVENT_CMD = 1, CPHP_OST_STATUS_CMD = 2, + CPHP_GET_CPU_ID_CMD = 3, CPHP_CMD_MAX }; @@ -74,11 +76,27 @@ static uint64_t cpu_hotplug_rd(void *opaque, hwaddr addr, unsigned size) case CPHP_GET_NEXT_CPU_WITH_EVENT_CMD: val = cpu_st->selector; break; + case CPHP_GET_CPU_ID_CMD: + val = cdev->arch_id & 0xFFFFFFFF; + break; default: break; } trace_cpuhp_acpi_read_cmd_data(cpu_st->selector, val); break; + case ACPI_CPU_CMD_DATA2_OFFSET_R: + switch (cpu_st->command) { + case CPHP_GET_NEXT_CPU_WITH_EVENT_CMD: + val = 0; + break; + case CPHP_GET_CPU_ID_CMD: + val = cdev->arch_id >> 32; + break; + default: + break; + } + trace_cpuhp_acpi_read_cmd_data2(cpu_st->selector, val); + break; default: break; } diff --git a/hw/acpi/cpu_hotplug.c b/hw/acpi/cpu_hotplug.c index 3ac2045a95..3e687d227a 100644 --- a/hw/acpi/cpu_hotplug.c +++ b/hw/acpi/cpu_hotplug.c @@ -14,6 +14,7 @@ #include "qapi/error.h" #include "hw/core/cpu.h" #include "hw/i386/pc.h" +#include "hw/pci/pci.h" #include "qemu/error-report.h" #define CPU_EJECT_METHOD "CPEJ" @@ -55,8 +56,7 @@ static const MemoryRegionOps AcpiCpuHotplug_ops = { }, }; -static void acpi_set_cpu_present_bit(AcpiCpuHotplug *g, CPUState *cpu, - Error **errp) +static void acpi_set_cpu_present_bit(AcpiCpuHotplug *g, CPUState *cpu) { CPUClass *k = CPU_GET_CLASS(cpu); int64_t cpu_id; @@ -74,10 +74,7 @@ static void acpi_set_cpu_present_bit(AcpiCpuHotplug *g, CPUState *cpu, void legacy_acpi_cpu_plug_cb(HotplugHandler *hotplug_dev, AcpiCpuHotplug *g, DeviceState *dev, Error **errp) { - acpi_set_cpu_present_bit(g, CPU(dev), errp); - if (*errp != NULL) { - return; - } + acpi_set_cpu_present_bit(g, CPU(dev)); acpi_send_event(DEVICE(hotplug_dev), ACPI_CPU_HOTPLUG_STATUS); } @@ -92,7 +89,7 @@ void legacy_acpi_cpu_hotplug_init(MemoryRegion *parent, Object *owner, gpe_cpu->device = owner; CPU_FOREACH(cpu) { - acpi_set_cpu_present_bit(gpe_cpu, cpu, &error_abort); + acpi_set_cpu_present_bit(gpe_cpu, cpu); } } diff --git a/hw/acpi/generic_event_device.c b/hw/acpi/generic_event_device.c index 9cee90cc70..021ed2bf23 100644 --- a/hw/acpi/generic_event_device.c +++ b/hw/acpi/generic_event_device.c @@ -175,7 +175,7 @@ static void acpi_ged_device_plug_cb(HotplugHandler *hotplug_dev, AcpiGedState *s = ACPI_GED(hotplug_dev); if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { - acpi_memory_plug_cb(hotplug_dev, &s->memhp_state, dev, errp); + acpi_memory_plug_cb(hotplug_dev, &s->memhp_state, dev, errp); } else { error_setg(errp, "virt: device plug request for unsupported device" " type: %s", object_get_typename(OBJECT(dev))); @@ -282,7 +282,7 @@ static void acpi_ged_class_init(ObjectClass *class, void *data) AcpiDeviceIfClass *adevc = ACPI_DEVICE_IF_CLASS(class); dc->desc = "ACPI Generic Event Device"; - dc->props = acpi_ged_properties; + device_class_set_props(dc, acpi_ged_properties); dc->vmsd = &vmstate_acpi_ged; hc->plug = acpi_ged_device_plug_cb; diff --git a/hw/acpi/hmat.c b/hw/acpi/hmat.c new file mode 100644 index 0000000000..7c24bb5371 --- /dev/null +++ b/hw/acpi/hmat.c @@ -0,0 +1,268 @@ +/* + * HMAT ACPI Implementation + * + * Copyright(C) 2019 Intel Corporation. + * + * Author: + * Liu jingqi + * Tao Xu + * + * HMAT is defined in ACPI 6.3: 5.2.27 Heterogeneous Memory Attribute Table + * (HMAT) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + */ + +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "sysemu/numa.h" +#include "hw/acpi/hmat.h" + +/* + * ACPI 6.3: + * 5.2.27.3 Memory Proximity Domain Attributes Structure: Table 5-145 + */ +static void build_hmat_mpda(GArray *table_data, uint16_t flags, + uint32_t initiator, uint32_t mem_node) +{ + + /* Memory Proximity Domain Attributes Structure */ + /* Type */ + build_append_int_noprefix(table_data, 0, 2); + /* Reserved */ + build_append_int_noprefix(table_data, 0, 2); + /* Length */ + build_append_int_noprefix(table_data, 40, 4); + /* Flags */ + build_append_int_noprefix(table_data, flags, 2); + /* Reserved */ + build_append_int_noprefix(table_data, 0, 2); + /* Proximity Domain for the Attached Initiator */ + build_append_int_noprefix(table_data, initiator, 4); + /* Proximity Domain for the Memory */ + build_append_int_noprefix(table_data, mem_node, 4); + /* Reserved */ + build_append_int_noprefix(table_data, 0, 4); + /* + * Reserved: + * Previously defined as the Start Address of the System Physical + * Address Range. Deprecated since ACPI Spec 6.3. + */ + build_append_int_noprefix(table_data, 0, 8); + /* + * Reserved: + * Previously defined as the Range Length of the region in bytes. + * Deprecated since ACPI Spec 6.3. + */ + build_append_int_noprefix(table_data, 0, 8); +} + +/* + * ACPI 6.3: 5.2.27.4 System Locality Latency and Bandwidth Information + * Structure: Table 5-146 + */ +static void build_hmat_lb(GArray *table_data, HMAT_LB_Info *hmat_lb, + uint32_t num_initiator, uint32_t num_target, + uint32_t *initiator_list) +{ + int i, index; + HMAT_LB_Data *lb_data; + uint16_t *entry_list; + uint32_t base; + /* Length in bytes for entire structure */ + uint32_t lb_length + = 32 /* Table length upto and including Entry Base Unit */ + + 4 * num_initiator /* Initiator Proximity Domain List */ + + 4 * num_target /* Target Proximity Domain List */ + + 2 * num_initiator * num_target; /* Latency or Bandwidth Entries */ + + /* Type */ + build_append_int_noprefix(table_data, 1, 2); + /* Reserved */ + build_append_int_noprefix(table_data, 0, 2); + /* Length */ + build_append_int_noprefix(table_data, lb_length, 4); + /* Flags: Bits [3:0] Memory Hierarchy, Bits[7:4] Reserved */ + assert(!(hmat_lb->hierarchy >> 4)); + build_append_int_noprefix(table_data, hmat_lb->hierarchy, 1); + /* Data Type */ + build_append_int_noprefix(table_data, hmat_lb->data_type, 1); + /* Reserved */ + build_append_int_noprefix(table_data, 0, 2); + /* Number of Initiator Proximity Domains (s) */ + build_append_int_noprefix(table_data, num_initiator, 4); + /* Number of Target Proximity Domains (t) */ + build_append_int_noprefix(table_data, num_target, 4); + /* Reserved */ + build_append_int_noprefix(table_data, 0, 4); + + /* Entry Base Unit */ + if (hmat_lb->data_type <= HMAT_LB_DATA_WRITE_LATENCY) { + /* Convert latency base from nanoseconds to picosecond */ + base = hmat_lb->base * 1000; + } else { + /* Convert bandwidth base from Byte to Megabyte */ + base = hmat_lb->base / MiB; + } + build_append_int_noprefix(table_data, base, 8); + + /* Initiator Proximity Domain List */ + for (i = 0; i < num_initiator; i++) { + build_append_int_noprefix(table_data, initiator_list[i], 4); + } + + /* Target Proximity Domain List */ + for (i = 0; i < num_target; i++) { + build_append_int_noprefix(table_data, i, 4); + } + + /* Latency or Bandwidth Entries */ + entry_list = g_malloc0(num_initiator * num_target * sizeof(uint16_t)); + for (i = 0; i < hmat_lb->list->len; i++) { + lb_data = &g_array_index(hmat_lb->list, HMAT_LB_Data, i); + index = lb_data->initiator * num_target + lb_data->target; + + entry_list[index] = (uint16_t)(lb_data->data / hmat_lb->base); + } + + for (i = 0; i < num_initiator * num_target; i++) { + build_append_int_noprefix(table_data, entry_list[i], 2); + } + + g_free(entry_list); +} + +/* ACPI 6.3: 5.2.27.5 Memory Side Cache Information Structure: Table 5-147 */ +static void build_hmat_cache(GArray *table_data, uint8_t total_levels, + NumaHmatCacheOptions *hmat_cache) +{ + /* + * Cache Attributes: Bits [3:0] – Total Cache Levels + * for this Memory Proximity Domain + */ + uint32_t cache_attr = total_levels; + + /* Bits [7:4] : Cache Level described in this structure */ + cache_attr |= (uint32_t) hmat_cache->level << 4; + + /* Bits [11:8] - Cache Associativity */ + cache_attr |= (uint32_t) hmat_cache->associativity << 8; + + /* Bits [15:12] - Write Policy */ + cache_attr |= (uint32_t) hmat_cache->policy << 12; + + /* Bits [31:16] - Cache Line size in bytes */ + cache_attr |= (uint32_t) hmat_cache->line << 16; + + /* Type */ + build_append_int_noprefix(table_data, 2, 2); + /* Reserved */ + build_append_int_noprefix(table_data, 0, 2); + /* Length */ + build_append_int_noprefix(table_data, 32, 4); + /* Proximity Domain for the Memory */ + build_append_int_noprefix(table_data, hmat_cache->node_id, 4); + /* Reserved */ + build_append_int_noprefix(table_data, 0, 4); + /* Memory Side Cache Size */ + build_append_int_noprefix(table_data, hmat_cache->size, 8); + /* Cache Attributes */ + build_append_int_noprefix(table_data, cache_attr, 4); + /* Reserved */ + build_append_int_noprefix(table_data, 0, 2); + /* + * Number of SMBIOS handles (n) + * Linux kernel uses Memory Side Cache Information Structure + * without SMBIOS entries for now, so set Number of SMBIOS handles + * as 0. + */ + build_append_int_noprefix(table_data, 0, 2); +} + +/* Build HMAT sub table structures */ +static void hmat_build_table_structs(GArray *table_data, NumaState *numa_state) +{ + uint16_t flags; + uint32_t num_initiator = 0; + uint32_t initiator_list[MAX_NODES]; + int i, hierarchy, type, cache_level, total_levels; + HMAT_LB_Info *hmat_lb; + NumaHmatCacheOptions *hmat_cache; + + for (i = 0; i < numa_state->num_nodes; i++) { + flags = 0; + + if (numa_state->nodes[i].initiator < MAX_NODES) { + flags |= HMAT_PROXIMITY_INITIATOR_VALID; + } + + build_hmat_mpda(table_data, flags, numa_state->nodes[i].initiator, i); + } + + for (i = 0; i < numa_state->num_nodes; i++) { + if (numa_state->nodes[i].has_cpu) { + initiator_list[num_initiator++] = i; + } + } + + /* + * ACPI 6.3: 5.2.27.4 System Locality Latency and Bandwidth Information + * Structure: Table 5-146 + */ + for (hierarchy = HMAT_LB_MEM_MEMORY; + hierarchy <= HMAT_LB_MEM_CACHE_3RD_LEVEL; hierarchy++) { + for (type = HMAT_LB_DATA_ACCESS_LATENCY; + type <= HMAT_LB_DATA_WRITE_BANDWIDTH; type++) { + hmat_lb = numa_state->hmat_lb[hierarchy][type]; + + if (hmat_lb && hmat_lb->list->len) { + build_hmat_lb(table_data, hmat_lb, num_initiator, + numa_state->num_nodes, initiator_list); + } + } + } + + /* + * ACPI 6.3: 5.2.27.5 Memory Side Cache Information Structure: + * Table 5-147 + */ + for (i = 0; i < numa_state->num_nodes; i++) { + total_levels = 0; + for (cache_level = 1; cache_level < HMAT_LB_LEVELS; cache_level++) { + if (numa_state->hmat_cache[i][cache_level]) { + total_levels++; + } + } + for (cache_level = 0; cache_level <= total_levels; cache_level++) { + hmat_cache = numa_state->hmat_cache[i][cache_level]; + if (hmat_cache) { + build_hmat_cache(table_data, total_levels, hmat_cache); + } + } + } +} + +void build_hmat(GArray *table_data, BIOSLinker *linker, NumaState *numa_state) +{ + int hmat_start = table_data->len; + + /* reserve space for HMAT header */ + acpi_data_push(table_data, 40); + + hmat_build_table_structs(table_data, numa_state); + + build_header(linker, table_data, + (void *)(table_data->data + hmat_start), + "HMAT", table_data->len - hmat_start, 2, NULL, NULL); +} diff --git a/hw/acpi/hmat.h b/hw/acpi/hmat.h new file mode 100644 index 0000000000..437dbc6872 --- /dev/null +++ b/hw/acpi/hmat.h @@ -0,0 +1,42 @@ +/* + * HMAT ACPI Implementation Header + * + * Copyright(C) 2019 Intel Corporation. + * + * Author: + * Liu jingqi + * Tao Xu + * + * HMAT is defined in ACPI 6.3: 5.2.27 Heterogeneous Memory Attribute Table + * (HMAT) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + */ + +#ifndef HMAT_H +#define HMAT_H + +#include "hw/acpi/aml-build.h" + +/* + * ACPI 6.3: 5.2.27.3 Memory Proximity Domain Attributes Structure, + * Table 5-145, Field "flag", Bit [0]: set to 1 to indicate that data in + * the Proximity Domain for the Attached Initiator field is valid. + * Other bits reserved. + */ +#define HMAT_PROXIMITY_INITIATOR_VALID 0x1 + +void build_hmat(GArray *table_data, BIOSLinker *linker, NumaState *numa_state); + +#endif diff --git a/hw/acpi/ich9.c b/hw/acpi/ich9.c index 2034dd749e..336cacea41 100644 --- a/hw/acpi/ich9.c +++ b/hw/acpi/ich9.c @@ -27,7 +27,6 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qapi/visitor.h" -#include "hw/i386/pc.h" #include "hw/pci/pci.h" #include "migration/vmstate.h" #include "qemu/timer.h" @@ -40,6 +39,7 @@ #include "hw/i386/ich9.h" #include "hw/mem/pc-dimm.h" +#include "hw/mem/nvdimm.h" //#define DEBUG @@ -357,81 +357,6 @@ static void ich9_pm_set_cpu_hotplug_legacy(Object *obj, bool value, s->pm.cpu_hotplug_legacy = value; } -static void ich9_pm_get_disable_s3(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - ICH9LPCPMRegs *pm = opaque; - uint8_t value = pm->disable_s3; - - visit_type_uint8(v, name, &value, errp); -} - -static void ich9_pm_set_disable_s3(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - ICH9LPCPMRegs *pm = opaque; - Error *local_err = NULL; - uint8_t value; - - visit_type_uint8(v, name, &value, &local_err); - if (local_err) { - goto out; - } - pm->disable_s3 = value; -out: - error_propagate(errp, local_err); -} - -static void ich9_pm_get_disable_s4(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - ICH9LPCPMRegs *pm = opaque; - uint8_t value = pm->disable_s4; - - visit_type_uint8(v, name, &value, errp); -} - -static void ich9_pm_set_disable_s4(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - ICH9LPCPMRegs *pm = opaque; - Error *local_err = NULL; - uint8_t value; - - visit_type_uint8(v, name, &value, &local_err); - if (local_err) { - goto out; - } - pm->disable_s4 = value; -out: - error_propagate(errp, local_err); -} - -static void ich9_pm_get_s4_val(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - ICH9LPCPMRegs *pm = opaque; - uint8_t value = pm->s4_val; - - visit_type_uint8(v, name, &value, errp); -} - -static void ich9_pm_set_s4_val(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - ICH9LPCPMRegs *pm = opaque; - Error *local_err = NULL; - uint8_t value; - - visit_type_uint8(v, name, &value, &local_err); - if (local_err) { - goto out; - } - pm->s4_val = value; -out: - error_propagate(errp, local_err); -} - static bool ich9_pm_get_enable_tco(Object *obj, Error **errp) { ICH9LPCState *s = ICH9_LPC_DEVICE(obj); @@ -454,12 +379,12 @@ void ich9_pm_add_properties(Object *obj, ICH9LPCPMRegs *pm, Error **errp) pm->s4_val = 2; object_property_add_uint32_ptr(obj, ACPI_PM_PROP_PM_IO_BASE, - &pm->pm_io_base, errp); + &pm->pm_io_base, OBJ_PROP_FLAG_READ, errp); object_property_add(obj, ACPI_PM_PROP_GPE0_BLK, "uint32", ich9_pm_get_gpe0_blk, NULL, NULL, pm, NULL); object_property_add_uint32_ptr(obj, ACPI_PM_PROP_GPE0_BLK_LEN, - &gpe0_len, errp); + &gpe0_len, OBJ_PROP_FLAG_READ, errp); object_property_add_bool(obj, "memory-hotplug-support", ich9_pm_get_memory_hotplug_support, ich9_pm_set_memory_hotplug_support, @@ -468,18 +393,14 @@ void ich9_pm_add_properties(Object *obj, ICH9LPCPMRegs *pm, Error **errp) ich9_pm_get_cpu_hotplug_legacy, ich9_pm_set_cpu_hotplug_legacy, NULL); - object_property_add(obj, ACPI_PM_PROP_S3_DISABLED, "uint8", - ich9_pm_get_disable_s3, - ich9_pm_set_disable_s3, - NULL, pm, NULL); - object_property_add(obj, ACPI_PM_PROP_S4_DISABLED, "uint8", - ich9_pm_get_disable_s4, - ich9_pm_set_disable_s4, - NULL, pm, NULL); - object_property_add(obj, ACPI_PM_PROP_S4_VAL, "uint8", - ich9_pm_get_s4_val, - ich9_pm_set_s4_val, - NULL, pm, NULL); + object_property_add_uint8_ptr(obj, ACPI_PM_PROP_S3_DISABLED, + &pm->disable_s3, OBJ_PROP_FLAG_READWRITE, + NULL); + object_property_add_uint8_ptr(obj, ACPI_PM_PROP_S4_DISABLED, + &pm->disable_s4, OBJ_PROP_FLAG_READWRITE, + NULL); + object_property_add_uint8_ptr(obj, ACPI_PM_PROP_S4_VAL, + &pm->s4_val, OBJ_PROP_FLAG_READWRITE, NULL); object_property_add_bool(obj, ACPI_PM_PROP_TCO_ENABLED, ich9_pm_get_enable_tco, ich9_pm_set_enable_tco, diff --git a/hw/acpi/nvdimm.c b/hw/acpi/nvdimm.c index 9fdad6dc3f..eb6a37b14e 100644 --- a/hw/acpi/nvdimm.c +++ b/hw/acpi/nvdimm.c @@ -32,33 +32,7 @@ #include "hw/acpi/bios-linker-loader.h" #include "hw/nvram/fw_cfg.h" #include "hw/mem/nvdimm.h" - -static int nvdimm_device_list(Object *obj, void *opaque) -{ - GSList **list = opaque; - - if (object_dynamic_cast(obj, TYPE_NVDIMM)) { - *list = g_slist_append(*list, DEVICE(obj)); - } - - object_child_foreach(obj, nvdimm_device_list, opaque); - return 0; -} - -/* - * inquire NVDIMM devices and link them into the list which is - * returned to the caller. - * - * Note: it is the caller's responsibility to free the list to avoid - * memory leak. - */ -static GSList *nvdimm_get_device_list(void) -{ - GSList *list = NULL; - - object_child_foreach(qdev_get_machine(), nvdimm_device_list, &list); - return list; -} +#include "qemu/nvdimm-utils.h" #define NVDIMM_UUID_LE(a, b, c, d0, d1, d2, d3, d4, d5, d6, d7) \ { (a) & 0xff, ((a) >> 8) & 0xff, ((a) >> 16) & 0xff, ((a) >> 24) & 0xff, \ @@ -511,7 +485,7 @@ struct NvdimmFuncGetLabelDataOut { /* the size of buffer filled by QEMU. */ uint32_t len; uint32_t func_ret_status; /* return status code. */ - uint8_t out_buf[0]; /* the data got via Get Namesapce Label function. */ + uint8_t out_buf[]; /* the data got via Get Namesapce Label function. */ } QEMU_PACKED; typedef struct NvdimmFuncGetLabelDataOut NvdimmFuncGetLabelDataOut; QEMU_BUILD_BUG_ON(sizeof(NvdimmFuncGetLabelDataOut) > NVDIMM_DSM_MEMORY_SIZE); @@ -519,7 +493,7 @@ QEMU_BUILD_BUG_ON(sizeof(NvdimmFuncGetLabelDataOut) > NVDIMM_DSM_MEMORY_SIZE); struct NvdimmFuncSetLabelDataIn { uint32_t offset; /* the offset in the namespace label data area. */ uint32_t length; /* the size of data is to be written via the function. */ - uint8_t in_buf[0]; /* the data written to label data area. */ + uint8_t in_buf[]; /* the data written to label data area. */ } QEMU_PACKED; typedef struct NvdimmFuncSetLabelDataIn NvdimmFuncSetLabelDataIn; QEMU_BUILD_BUG_ON(sizeof(NvdimmFuncSetLabelDataIn) + @@ -536,7 +510,7 @@ struct NvdimmFuncReadFITOut { /* the size of buffer filled by QEMU. */ uint32_t len; uint32_t func_ret_status; /* return status code. */ - uint8_t fit[0]; /* the FIT data. */ + uint8_t fit[]; /* the FIT data. */ } QEMU_PACKED; typedef struct NvdimmFuncReadFITOut NvdimmFuncReadFITOut; QEMU_BUILD_BUG_ON(sizeof(NvdimmFuncReadFITOut) > NVDIMM_DSM_MEMORY_SIZE); diff --git a/hw/acpi/pcihp.c b/hw/acpi/pcihp.c index 8413348a33..0dc963e983 100644 --- a/hw/acpi/pcihp.c +++ b/hw/acpi/pcihp.c @@ -80,7 +80,8 @@ static void *acpi_set_bsel(PCIBus *bus, void *opaque) *bus_bsel = (*bsel_alloc)++; object_property_add_uint32_ptr(OBJECT(bus), ACPI_PCIHP_PROP_BSEL, - bus_bsel, &error_abort); + bus_bsel, OBJ_PROP_FLAG_READ, + &error_abort); } return bsel_alloc; @@ -153,7 +154,7 @@ static void acpi_pcihp_eject_slot(AcpiPciHpState *s, unsigned bsel, unsigned slo trace_acpi_pci_eject_slot(bsel, slot); - if (!bus) { + if (!bus || slot > 31) { return; } @@ -373,9 +374,9 @@ void acpi_pcihp_init(Object *owner, AcpiPciHpState *s, PCIBus *root_bus, memory_region_add_subregion(address_space_io, s->io_base, &s->io); object_property_add_uint16_ptr(owner, ACPI_PCIHP_IO_BASE_PROP, &s->io_base, - &error_abort); + OBJ_PROP_FLAG_READ, &error_abort); object_property_add_uint16_ptr(owner, ACPI_PCIHP_IO_LEN_PROP, &s->io_len, - &error_abort); + OBJ_PROP_FLAG_READ, &error_abort); } const VMStateDescription vmstate_acpi_pcihp_pci_status = { diff --git a/hw/acpi/piix4.c b/hw/acpi/piix4.c index 93aec2dd2c..964d6f5990 100644 --- a/hw/acpi/piix4.c +++ b/hw/acpi/piix4.c @@ -38,6 +38,7 @@ #include "hw/acpi/cpu.h" #include "hw/hotplug.h" #include "hw/mem/pc-dimm.h" +#include "hw/mem/nvdimm.h" #include "hw/acpi/memory_hotplug.h" #include "hw/acpi/acpi_dev_interface.h" #include "hw/xen/xen.h" @@ -443,17 +444,17 @@ static void piix4_pm_add_propeties(PIIX4PMState *s) static const uint16_t sci_int = 9; object_property_add_uint8_ptr(OBJECT(s), ACPI_PM_PROP_ACPI_ENABLE_CMD, - &acpi_enable_cmd, NULL); + &acpi_enable_cmd, OBJ_PROP_FLAG_READ, NULL); object_property_add_uint8_ptr(OBJECT(s), ACPI_PM_PROP_ACPI_DISABLE_CMD, - &acpi_disable_cmd, NULL); + &acpi_disable_cmd, OBJ_PROP_FLAG_READ, NULL); object_property_add_uint32_ptr(OBJECT(s), ACPI_PM_PROP_GPE0_BLK, - &gpe0_blk, NULL); + &gpe0_blk, OBJ_PROP_FLAG_READ, NULL); object_property_add_uint32_ptr(OBJECT(s), ACPI_PM_PROP_GPE0_BLK_LEN, - &gpe0_blk_len, NULL); + &gpe0_blk_len, OBJ_PROP_FLAG_READ, NULL); object_property_add_uint16_ptr(OBJECT(s), ACPI_PM_PROP_SCI_INT, - &sci_int, NULL); + &sci_int, OBJ_PROP_FLAG_READ, NULL); object_property_add_uint32_ptr(OBJECT(s), ACPI_PM_PROP_PM_IO_BASE, - &s->io_base, NULL); + &s->io_base, OBJ_PROP_FLAG_READ, NULL); } static void piix4_pm_realize(PCIDevice *dev, Error **errp) @@ -653,7 +654,7 @@ static void piix4_pm_class_init(ObjectClass *klass, void *data) dc->reset = piix4_pm_reset; dc->desc = "PM"; dc->vmsd = &vmstate_acpi; - dc->props = piix4_pm_properties; + device_class_set_props(dc, piix4_pm_properties); /* * Reason: part of PIIX4 southbridge, needs to be wired up, * e.g. by mips_malta_init() diff --git a/hw/acpi/trace-events b/hw/acpi/trace-events index 96b8273297..afbc77de1c 100644 --- a/hw/acpi/trace-events +++ b/hw/acpi/trace-events @@ -23,6 +23,7 @@ cpuhp_acpi_read_flags(uint32_t idx, uint8_t flags) "idx[0x%"PRIx32"] flags: 0x%" cpuhp_acpi_write_idx(uint32_t idx) "set active cpu idx: 0x%"PRIx32 cpuhp_acpi_write_cmd(uint32_t idx, uint8_t cmd) "idx[0x%"PRIx32"] cmd: 0x%"PRIx8 cpuhp_acpi_read_cmd_data(uint32_t idx, uint32_t data) "idx[0x%"PRIx32"] data: 0x%"PRIx32 +cpuhp_acpi_read_cmd_data2(uint32_t idx, uint32_t data) "idx[0x%"PRIx32"] data: 0x%"PRIx32 cpuhp_acpi_cpu_has_events(uint32_t idx, bool ins, bool rm) "idx[0x%"PRIx32"] inserting: %d, removing: %d" cpuhp_acpi_clear_inserting_evt(uint32_t idx) "idx[0x%"PRIx32"]" cpuhp_acpi_clear_remove_evt(uint32_t idx) "idx[0x%"PRIx32"]" diff --git a/hw/acpi/vmgenid.c b/hw/acpi/vmgenid.c index 6e11b0fa9b..2df7623d74 100644 --- a/hw/acpi/vmgenid.c +++ b/hw/acpi/vmgenid.c @@ -226,7 +226,7 @@ static void vmgenid_device_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_vmgenid; dc->realize = vmgenid_realize; - dc->props = vmgenid_device_properties; + device_class_set_props(dc, vmgenid_device_properties); dc->hotpluggable = false; set_bit(DEVICE_CATEGORY_MISC, dc->categories); } diff --git a/hw/adc/Makefile.objs b/hw/adc/Makefile.objs index 3f6dfdedae..2b9dc36c7f 100644 --- a/hw/adc/Makefile.objs +++ b/hw/adc/Makefile.objs @@ -1 +1 @@ -obj-$(CONFIG_STM32F2XX_ADC) += stm32f2xx_adc.o +common-obj-$(CONFIG_STM32F2XX_ADC) += stm32f2xx_adc.o diff --git a/hw/alpha/alpha_sys.h b/hw/alpha/alpha_sys.h index 4e127a6de8..e2c02e2bbe 100644 --- a/hw/alpha/alpha_sys.h +++ b/hw/alpha/alpha_sys.h @@ -6,11 +6,11 @@ #include "target/alpha/cpu-qom.h" #include "hw/pci/pci.h" #include "hw/pci/pci_host.h" -#include "hw/ide.h" -#include "hw/i386/pc.h" +#include "hw/boards.h" +#include "hw/intc/i8259.h" -PCIBus *typhoon_init(ram_addr_t, ISABus **, qemu_irq *, AlphaCPU *[4], +PCIBus *typhoon_init(MemoryRegion *, ISABus **, qemu_irq *, AlphaCPU *[4], pci_map_irq_fn); /* alpha_pci.c. */ diff --git a/hw/alpha/dp264.c b/hw/alpha/dp264.c index 51b3cf7a61..f7751b18f6 100644 --- a/hw/alpha/dp264.c +++ b/hw/alpha/dp264.c @@ -15,11 +15,13 @@ #include "qemu/error-report.h" #include "sysemu/sysemu.h" #include "hw/rtc/mc146818rtc.h" -#include "hw/ide.h" +#include "hw/ide/pci.h" #include "hw/timer/i8254.h" #include "hw/isa/superio.h" #include "hw/dma/i8257.h" +#include "net/net.h" #include "qemu/cutils.h" +#include "net/net.h" #define MAX_IDE_BUS 2 @@ -55,6 +57,7 @@ static void clipper_init(MachineState *machine) const char *initrd_filename = machine->initrd_filename; AlphaCPU *cpus[4]; PCIBus *pci_bus; + PCIDevice *pci_dev; ISABus *isa_bus; qemu_irq rtc_irq; long size, i; @@ -74,7 +77,7 @@ static void clipper_init(MachineState *machine) cpus[0]->env.trap_arg2 = smp_cpus; /* Init the chipset. */ - pci_bus = typhoon_init(ram_size, &isa_bus, &rtc_irq, cpus, + pci_bus = typhoon_init(machine->ram, &isa_bus, &rtc_irq, cpus, clipper_pci_map_irq); /* Since we have an SRM-compatible PALcode, use the SRM epoch. */ @@ -97,12 +100,8 @@ static void clipper_init(MachineState *machine) isa_create_simple(isa_bus, TYPE_SMC37C669_SUPERIO); /* IDE disk setup. */ - { - DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; - ide_drive_get(hd, ARRAY_SIZE(hd)); - - pci_cmd646_ide_init(pci_bus, hd, 0); - } + pci_dev = pci_create_simple(pci_bus, -1, "cmd646-ide"); + pci_ide_create_devs(pci_dev); /* Load PALcode. Given that this is not "real" cpu palcode, but one explicitly written for the emulation, we might as @@ -114,7 +113,7 @@ static void clipper_init(MachineState *machine) exit(1); } size = load_elf(palcode_filename, NULL, cpu_alpha_superpage_to_phys, - NULL, &palcode_entry, &palcode_low, &palcode_high, + NULL, &palcode_entry, &palcode_low, &palcode_high, NULL, 0, EM_ALPHA, 0, 0); if (size < 0) { error_report("could not load palcode '%s'", palcode_filename); @@ -133,7 +132,7 @@ static void clipper_init(MachineState *machine) uint64_t param_offset; size = load_elf(kernel_filename, NULL, cpu_alpha_superpage_to_phys, - NULL, &kernel_entry, &kernel_low, &kernel_high, + NULL, &kernel_entry, &kernel_low, &kernel_high, NULL, 0, EM_ALPHA, 0, 0); if (size < 0) { error_report("could not load kernel '%s'", kernel_filename); @@ -180,8 +179,9 @@ static void clipper_machine_init(MachineClass *mc) mc->init = clipper_init; mc->block_default_type = IF_IDE; mc->max_cpus = 4; - mc->is_default = 1; + mc->is_default = true; mc->default_cpu_type = ALPHA_CPU_TYPE_NAME("ev67"); + mc->default_ram_id = "ram"; } DEFINE_MACHINE("clipper", clipper_machine_init) diff --git a/hw/alpha/typhoon.c b/hw/alpha/typhoon.c index 179e1f7658..1795e2f29d 100644 --- a/hw/alpha/typhoon.c +++ b/hw/alpha/typhoon.c @@ -58,7 +58,6 @@ typedef struct TyphoonState { TyphoonCchip cchip; TyphoonPchip pchip; MemoryRegion dchip_region; - MemoryRegion ram_region; } TyphoonState; /* Called when one of DRIR or DIM changes. */ @@ -817,8 +816,7 @@ static void typhoon_alarm_timer(void *opaque) cpu_interrupt(CPU(s->cchip.cpu[cpu]), CPU_INTERRUPT_TIMER); } -PCIBus *typhoon_init(ram_addr_t ram_size, ISABus **isa_bus, - qemu_irq *p_rtc_irq, +PCIBus *typhoon_init(MemoryRegion *ram, ISABus **isa_bus, qemu_irq *p_rtc_irq, AlphaCPU *cpus[4], pci_map_irq_fn sys_map_irq) { MemoryRegion *addr_space = get_system_memory(); @@ -851,9 +849,7 @@ PCIBus *typhoon_init(ram_addr_t ram_size, ISABus **isa_bus, /* Main memory region, 0x00.0000.0000. Real hardware supports 32GB, but the address space hole reserved at this point is 8TB. */ - memory_region_allocate_system_memory(&s->ram_region, OBJECT(s), "ram", - ram_size); - memory_region_add_subregion(addr_space, 0, &s->ram_region); + memory_region_add_subregion(addr_space, 0, ram); /* TIGbus, 0x801.0000.0000, 1GB. */ /* ??? The TIGbus is used for delivering interrupts, and access to diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index c6e7782580..188419dc1e 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -5,6 +5,7 @@ config ARM_VIRT imply VFIO_AMD_XGBE imply VFIO_PLATFORM imply VFIO_XGMAC + imply TPM_TIS_SYSBUS select A15MPCORE select ACPI select ARM_SMMUV3 @@ -69,6 +70,7 @@ config INTEGRATOR select INTEGRATOR_DEBUG select PL011 # UART select PL031 # RTC + select PL041 # audio select PL050 # keyboard/mouse select PL110 # pl111 LCD controller select PL181 # display @@ -101,6 +103,10 @@ config NETDUINO2 bool select STM32F205_SOC +config NETDUINOPLUS2 + bool + select STM32F405_SOC + config NSERIES bool select OMAP @@ -291,6 +297,18 @@ config ALLWINNER_A10 select SERIAL select UNIMP +config ALLWINNER_H3 + bool + select ALLWINNER_A10_PIT + select ALLWINNER_SUN8I_EMAC + select SERIAL + select ARM_TIMER + select ARM_GIC + select UNIMP + select USB_OHCI + select USB_EHCI_SYSBUS + select SD + config RASPI bool select FRAMEBUFFER @@ -307,6 +325,12 @@ config STM32F205_SOC select STM32F2XX_ADC select STM32F2XX_SPI +config STM32F405_SOC + bool + select ARM_V7M + select STM32F4XX_SYSCFG + select STM32F4XX_EXTI + config XLNX_ZYNQMP_ARM bool select AHCI @@ -349,6 +373,7 @@ config FSL_IMX6 select IMX select IMX_FEC select IMX_I2C + select IMX_USBPHY select SDHCI config ASPEED_SOC diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs index fe749f65fd..534a6a119e 100644 --- a/hw/arm/Makefile.objs +++ b/hw/arm/Makefile.objs @@ -11,6 +11,7 @@ obj-$(CONFIG_MAINSTONE) += mainstone.o obj-$(CONFIG_MICROBIT) += microbit.o obj-$(CONFIG_MUSICPAL) += musicpal.o obj-$(CONFIG_NETDUINO2) += netduino2.o +obj-$(CONFIG_NETDUINOPLUS2) += netduinoplus2.o obj-$(CONFIG_NSERIES) += nseries.o obj-$(CONFIG_SX1) += omap_sx1.o obj-$(CONFIG_CHEETAH) += palm.o @@ -34,8 +35,10 @@ obj-$(CONFIG_DIGIC) += digic.o obj-$(CONFIG_OMAP) += omap1.o omap2.o obj-$(CONFIG_STRONGARM) += strongarm.o obj-$(CONFIG_ALLWINNER_A10) += allwinner-a10.o cubieboard.o +obj-$(CONFIG_ALLWINNER_H3) += allwinner-h3.o orangepi.o obj-$(CONFIG_RASPI) += bcm2835_peripherals.o bcm2836.o raspi.o obj-$(CONFIG_STM32F205_SOC) += stm32f205_soc.o +obj-$(CONFIG_STM32F405_SOC) += stm32f405_soc.o obj-$(CONFIG_XLNX_ZYNQMP_ARM) += xlnx-zynqmp.o xlnx-zcu102.o obj-$(CONFIG_XLNX_VERSAL) += xlnx-versal.o xlnx-versal-virt.o obj-$(CONFIG_FSL_IMX25) += fsl-imx25.o imx25_pdk.o diff --git a/hw/arm/allwinner-a10.c b/hw/arm/allwinner-a10.c index 118032c8c7..62a67a3e1a 100644 --- a/hw/arm/allwinner-a10.c +++ b/hw/arm/allwinner-a10.c @@ -24,6 +24,18 @@ #include "hw/arm/allwinner-a10.h" #include "hw/misc/unimp.h" #include "sysemu/sysemu.h" +#include "hw/boards.h" +#include "hw/usb/hcd-ohci.h" + +#define AW_A10_MMC0_BASE 0x01c0f000 +#define AW_A10_PIC_REG_BASE 0x01c20400 +#define AW_A10_PIT_REG_BASE 0x01c20c00 +#define AW_A10_UART0_REG_BASE 0x01c28000 +#define AW_A10_EMAC_BASE 0x01c0b000 +#define AW_A10_EHCI_BASE 0x01c14000 +#define AW_A10_OHCI_BASE 0x01c14400 +#define AW_A10_SATA_BASE 0x01c18000 +#define AW_A10_RTC_BASE 0x01c20d00 static void aw_a10_init(Object *obj) { @@ -43,14 +55,29 @@ static void aw_a10_init(Object *obj) sysbus_init_child_obj(obj, "sata", &s->sata, sizeof(s->sata), TYPE_ALLWINNER_AHCI); + + if (machine_usb(current_machine)) { + int i; + + for (i = 0; i < AW_A10_NUM_USB; i++) { + sysbus_init_child_obj(obj, "ehci[*]", OBJECT(&s->ehci[i]), + sizeof(s->ehci[i]), TYPE_PLATFORM_EHCI); + sysbus_init_child_obj(obj, "ohci[*]", OBJECT(&s->ohci[i]), + sizeof(s->ohci[i]), TYPE_SYSBUS_OHCI); + } + } + + sysbus_init_child_obj(obj, "mmc0", &s->mmc0, sizeof(s->mmc0), + TYPE_AW_SDHOST_SUN4I); + + sysbus_init_child_obj(obj, "rtc", &s->rtc, sizeof(s->rtc), + TYPE_AW_RTC_SUN4I); } static void aw_a10_realize(DeviceState *dev, Error **errp) { AwA10State *s = AW_A10(dev); SysBusDevice *sysbusdev; - uint8_t i; - qemu_irq fiq, irq; Error *err = NULL; object_property_set_bool(OBJECT(&s->cpu), true, "realized", &err); @@ -58,8 +85,6 @@ static void aw_a10_realize(DeviceState *dev, Error **errp) error_propagate(errp, err); return; } - irq = qdev_get_gpio_in(DEVICE(&s->cpu), ARM_CPU_IRQ); - fiq = qdev_get_gpio_in(DEVICE(&s->cpu), ARM_CPU_FIQ); object_property_set_bool(OBJECT(&s->intc), true, "realized", &err); if (err != NULL) { @@ -68,11 +93,11 @@ static void aw_a10_realize(DeviceState *dev, Error **errp) } sysbusdev = SYS_BUS_DEVICE(&s->intc); sysbus_mmio_map(sysbusdev, 0, AW_A10_PIC_REG_BASE); - sysbus_connect_irq(sysbusdev, 0, irq); - sysbus_connect_irq(sysbusdev, 1, fiq); - for (i = 0; i < AW_A10_PIC_INT_NR; i++) { - s->irq[i] = qdev_get_gpio_in(DEVICE(&s->intc), i); - } + sysbus_connect_irq(sysbusdev, 0, + qdev_get_gpio_in(DEVICE(&s->cpu), ARM_CPU_IRQ)); + sysbus_connect_irq(sysbusdev, 1, + qdev_get_gpio_in(DEVICE(&s->cpu), ARM_CPU_FIQ)); + qdev_pass_gpios(DEVICE(&s->intc), dev, NULL); object_property_set_bool(OBJECT(&s->timer), true, "realized", &err); if (err != NULL) { @@ -81,12 +106,12 @@ static void aw_a10_realize(DeviceState *dev, Error **errp) } sysbusdev = SYS_BUS_DEVICE(&s->timer); sysbus_mmio_map(sysbusdev, 0, AW_A10_PIT_REG_BASE); - sysbus_connect_irq(sysbusdev, 0, s->irq[22]); - sysbus_connect_irq(sysbusdev, 1, s->irq[23]); - sysbus_connect_irq(sysbusdev, 2, s->irq[24]); - sysbus_connect_irq(sysbusdev, 3, s->irq[25]); - sysbus_connect_irq(sysbusdev, 4, s->irq[67]); - sysbus_connect_irq(sysbusdev, 5, s->irq[68]); + sysbus_connect_irq(sysbusdev, 0, qdev_get_gpio_in(dev, 22)); + sysbus_connect_irq(sysbusdev, 1, qdev_get_gpio_in(dev, 23)); + sysbus_connect_irq(sysbusdev, 2, qdev_get_gpio_in(dev, 24)); + sysbus_connect_irq(sysbusdev, 3, qdev_get_gpio_in(dev, 25)); + sysbus_connect_irq(sysbusdev, 4, qdev_get_gpio_in(dev, 67)); + sysbus_connect_irq(sysbusdev, 5, qdev_get_gpio_in(dev, 68)); memory_region_init_ram(&s->sram_a, OBJECT(dev), "sram A", 48 * KiB, &error_fatal); @@ -105,7 +130,7 @@ static void aw_a10_realize(DeviceState *dev, Error **errp) } sysbusdev = SYS_BUS_DEVICE(&s->emac); sysbus_mmio_map(sysbusdev, 0, AW_A10_EMAC_BASE); - sysbus_connect_irq(sysbusdev, 0, s->irq[55]); + sysbus_connect_irq(sysbusdev, 0, qdev_get_gpio_in(dev, 55)); object_property_set_bool(OBJECT(&s->sata), true, "realized", &err); if (err) { @@ -113,11 +138,51 @@ static void aw_a10_realize(DeviceState *dev, Error **errp) return; } sysbus_mmio_map(SYS_BUS_DEVICE(&s->sata), 0, AW_A10_SATA_BASE); - sysbus_connect_irq(SYS_BUS_DEVICE(&s->sata), 0, s->irq[56]); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->sata), 0, qdev_get_gpio_in(dev, 56)); /* FIXME use a qdev chardev prop instead of serial_hd() */ - serial_mm_init(get_system_memory(), AW_A10_UART0_REG_BASE, 2, s->irq[1], + serial_mm_init(get_system_memory(), AW_A10_UART0_REG_BASE, 2, + qdev_get_gpio_in(dev, 1), 115200, serial_hd(0), DEVICE_NATIVE_ENDIAN); + + if (machine_usb(current_machine)) { + int i; + + for (i = 0; i < AW_A10_NUM_USB; i++) { + char bus[16]; + + sprintf(bus, "usb-bus.%d", i); + + object_property_set_bool(OBJECT(&s->ehci[i]), true, + "companion-enable", &error_fatal); + object_property_set_bool(OBJECT(&s->ehci[i]), true, "realized", + &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->ehci[i]), 0, + AW_A10_EHCI_BASE + i * 0x8000); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->ehci[i]), 0, + qdev_get_gpio_in(dev, 39 + i)); + + object_property_set_str(OBJECT(&s->ohci[i]), bus, "masterbus", + &error_fatal); + object_property_set_bool(OBJECT(&s->ohci[i]), true, "realized", + &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->ohci[i]), 0, + AW_A10_OHCI_BASE + i * 0x8000); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->ohci[i]), 0, + qdev_get_gpio_in(dev, 64 + i)); + } + } + + /* SD/MMC */ + qdev_init_nofail(DEVICE(&s->mmc0)); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->mmc0), 0, AW_A10_MMC0_BASE); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->mmc0), 0, qdev_get_gpio_in(dev, 32)); + object_property_add_alias(OBJECT(s), "sd-bus", OBJECT(&s->mmc0), + "sd-bus", &error_abort); + + /* RTC */ + qdev_init_nofail(DEVICE(&s->rtc)); + sysbus_mmio_map_overlap(SYS_BUS_DEVICE(&s->rtc), 0, AW_A10_RTC_BASE, 10); } static void aw_a10_class_init(ObjectClass *oc, void *data) diff --git a/hw/arm/allwinner-h3.c b/hw/arm/allwinner-h3.c new file mode 100644 index 0000000000..9e4ce36093 --- /dev/null +++ b/hw/arm/allwinner-h3.c @@ -0,0 +1,465 @@ +/* + * Allwinner H3 System on Chip emulation + * + * Copyright (C) 2019 Niek Linnenbank + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "qemu/osdep.h" +#include "exec/address-spaces.h" +#include "qapi/error.h" +#include "qemu/error-report.h" +#include "qemu/module.h" +#include "qemu/units.h" +#include "hw/qdev-core.h" +#include "cpu.h" +#include "hw/sysbus.h" +#include "hw/char/serial.h" +#include "hw/misc/unimp.h" +#include "hw/usb/hcd-ehci.h" +#include "hw/loader.h" +#include "sysemu/sysemu.h" +#include "hw/arm/allwinner-h3.h" + +/* Memory map */ +const hwaddr allwinner_h3_memmap[] = { + [AW_H3_SRAM_A1] = 0x00000000, + [AW_H3_SRAM_A2] = 0x00044000, + [AW_H3_SRAM_C] = 0x00010000, + [AW_H3_SYSCTRL] = 0x01c00000, + [AW_H3_MMC0] = 0x01c0f000, + [AW_H3_SID] = 0x01c14000, + [AW_H3_EHCI0] = 0x01c1a000, + [AW_H3_OHCI0] = 0x01c1a400, + [AW_H3_EHCI1] = 0x01c1b000, + [AW_H3_OHCI1] = 0x01c1b400, + [AW_H3_EHCI2] = 0x01c1c000, + [AW_H3_OHCI2] = 0x01c1c400, + [AW_H3_EHCI3] = 0x01c1d000, + [AW_H3_OHCI3] = 0x01c1d400, + [AW_H3_CCU] = 0x01c20000, + [AW_H3_PIT] = 0x01c20c00, + [AW_H3_UART0] = 0x01c28000, + [AW_H3_UART1] = 0x01c28400, + [AW_H3_UART2] = 0x01c28800, + [AW_H3_UART3] = 0x01c28c00, + [AW_H3_EMAC] = 0x01c30000, + [AW_H3_DRAMCOM] = 0x01c62000, + [AW_H3_DRAMCTL] = 0x01c63000, + [AW_H3_DRAMPHY] = 0x01c65000, + [AW_H3_GIC_DIST] = 0x01c81000, + [AW_H3_GIC_CPU] = 0x01c82000, + [AW_H3_GIC_HYP] = 0x01c84000, + [AW_H3_GIC_VCPU] = 0x01c86000, + [AW_H3_RTC] = 0x01f00000, + [AW_H3_CPUCFG] = 0x01f01c00, + [AW_H3_SDRAM] = 0x40000000 +}; + +/* List of unimplemented devices */ +struct AwH3Unimplemented { + const char *device_name; + hwaddr base; + hwaddr size; +} unimplemented[] = { + { "d-engine", 0x01000000, 4 * MiB }, + { "d-inter", 0x01400000, 128 * KiB }, + { "dma", 0x01c02000, 4 * KiB }, + { "nfdc", 0x01c03000, 4 * KiB }, + { "ts", 0x01c06000, 4 * KiB }, + { "keymem", 0x01c0b000, 4 * KiB }, + { "lcd0", 0x01c0c000, 4 * KiB }, + { "lcd1", 0x01c0d000, 4 * KiB }, + { "ve", 0x01c0e000, 4 * KiB }, + { "mmc1", 0x01c10000, 4 * KiB }, + { "mmc2", 0x01c11000, 4 * KiB }, + { "crypto", 0x01c15000, 4 * KiB }, + { "msgbox", 0x01c17000, 4 * KiB }, + { "spinlock", 0x01c18000, 4 * KiB }, + { "usb0-otg", 0x01c19000, 4 * KiB }, + { "usb0-phy", 0x01c1a000, 4 * KiB }, + { "usb1-phy", 0x01c1b000, 4 * KiB }, + { "usb2-phy", 0x01c1c000, 4 * KiB }, + { "usb3-phy", 0x01c1d000, 4 * KiB }, + { "smc", 0x01c1e000, 4 * KiB }, + { "pio", 0x01c20800, 1 * KiB }, + { "owa", 0x01c21000, 1 * KiB }, + { "pwm", 0x01c21400, 1 * KiB }, + { "keyadc", 0x01c21800, 1 * KiB }, + { "pcm0", 0x01c22000, 1 * KiB }, + { "pcm1", 0x01c22400, 1 * KiB }, + { "pcm2", 0x01c22800, 1 * KiB }, + { "audio", 0x01c22c00, 2 * KiB }, + { "smta", 0x01c23400, 1 * KiB }, + { "ths", 0x01c25000, 1 * KiB }, + { "uart0", 0x01c28000, 1 * KiB }, + { "uart1", 0x01c28400, 1 * KiB }, + { "uart2", 0x01c28800, 1 * KiB }, + { "uart3", 0x01c28c00, 1 * KiB }, + { "twi0", 0x01c2ac00, 1 * KiB }, + { "twi1", 0x01c2b000, 1 * KiB }, + { "twi2", 0x01c2b400, 1 * KiB }, + { "scr", 0x01c2c400, 1 * KiB }, + { "gpu", 0x01c40000, 64 * KiB }, + { "hstmr", 0x01c60000, 4 * KiB }, + { "spi0", 0x01c68000, 4 * KiB }, + { "spi1", 0x01c69000, 4 * KiB }, + { "csi", 0x01cb0000, 320 * KiB }, + { "tve", 0x01e00000, 64 * KiB }, + { "hdmi", 0x01ee0000, 128 * KiB }, + { "r_timer", 0x01f00800, 1 * KiB }, + { "r_intc", 0x01f00c00, 1 * KiB }, + { "r_wdog", 0x01f01000, 1 * KiB }, + { "r_prcm", 0x01f01400, 1 * KiB }, + { "r_twd", 0x01f01800, 1 * KiB }, + { "r_cir-rx", 0x01f02000, 1 * KiB }, + { "r_twi", 0x01f02400, 1 * KiB }, + { "r_uart", 0x01f02800, 1 * KiB }, + { "r_pio", 0x01f02c00, 1 * KiB }, + { "r_pwm", 0x01f03800, 1 * KiB }, + { "core-dbg", 0x3f500000, 128 * KiB }, + { "tsgen-ro", 0x3f506000, 4 * KiB }, + { "tsgen-ctl", 0x3f507000, 4 * KiB }, + { "ddr-mem", 0x40000000, 2 * GiB }, + { "n-brom", 0xffff0000, 32 * KiB }, + { "s-brom", 0xffff0000, 64 * KiB } +}; + +/* Per Processor Interrupts */ +enum { + AW_H3_GIC_PPI_MAINT = 9, + AW_H3_GIC_PPI_HYPTIMER = 10, + AW_H3_GIC_PPI_VIRTTIMER = 11, + AW_H3_GIC_PPI_SECTIMER = 13, + AW_H3_GIC_PPI_PHYSTIMER = 14 +}; + +/* Shared Processor Interrupts */ +enum { + AW_H3_GIC_SPI_UART0 = 0, + AW_H3_GIC_SPI_UART1 = 1, + AW_H3_GIC_SPI_UART2 = 2, + AW_H3_GIC_SPI_UART3 = 3, + AW_H3_GIC_SPI_TIMER0 = 18, + AW_H3_GIC_SPI_TIMER1 = 19, + AW_H3_GIC_SPI_MMC0 = 60, + AW_H3_GIC_SPI_EHCI0 = 72, + AW_H3_GIC_SPI_OHCI0 = 73, + AW_H3_GIC_SPI_EHCI1 = 74, + AW_H3_GIC_SPI_OHCI1 = 75, + AW_H3_GIC_SPI_EHCI2 = 76, + AW_H3_GIC_SPI_OHCI2 = 77, + AW_H3_GIC_SPI_EHCI3 = 78, + AW_H3_GIC_SPI_OHCI3 = 79, + AW_H3_GIC_SPI_EMAC = 82 +}; + +/* Allwinner H3 general constants */ +enum { + AW_H3_GIC_NUM_SPI = 128 +}; + +void allwinner_h3_bootrom_setup(AwH3State *s, BlockBackend *blk) +{ + const int64_t rom_size = 32 * KiB; + g_autofree uint8_t *buffer = g_new0(uint8_t, rom_size); + + if (blk_pread(blk, 8 * KiB, buffer, rom_size) < 0) { + error_setg(&error_fatal, "%s: failed to read BlockBackend data", + __func__); + return; + } + + rom_add_blob("allwinner-h3.bootrom", buffer, rom_size, + rom_size, s->memmap[AW_H3_SRAM_A1], + NULL, NULL, NULL, NULL, false); +} + +static void allwinner_h3_init(Object *obj) +{ + AwH3State *s = AW_H3(obj); + + s->memmap = allwinner_h3_memmap; + + for (int i = 0; i < AW_H3_NUM_CPUS; i++) { + object_initialize_child(obj, "cpu[*]", &s->cpus[i], sizeof(s->cpus[i]), + ARM_CPU_TYPE_NAME("cortex-a7"), + &error_abort, NULL); + } + + sysbus_init_child_obj(obj, "gic", &s->gic, sizeof(s->gic), + TYPE_ARM_GIC); + + sysbus_init_child_obj(obj, "timer", &s->timer, sizeof(s->timer), + TYPE_AW_A10_PIT); + object_property_add_alias(obj, "clk0-freq", OBJECT(&s->timer), + "clk0-freq", &error_abort); + object_property_add_alias(obj, "clk1-freq", OBJECT(&s->timer), + "clk1-freq", &error_abort); + + sysbus_init_child_obj(obj, "ccu", &s->ccu, sizeof(s->ccu), + TYPE_AW_H3_CCU); + + sysbus_init_child_obj(obj, "sysctrl", &s->sysctrl, sizeof(s->sysctrl), + TYPE_AW_H3_SYSCTRL); + + sysbus_init_child_obj(obj, "cpucfg", &s->cpucfg, sizeof(s->cpucfg), + TYPE_AW_CPUCFG); + + sysbus_init_child_obj(obj, "sid", &s->sid, sizeof(s->sid), + TYPE_AW_SID); + object_property_add_alias(obj, "identifier", OBJECT(&s->sid), + "identifier", &error_abort); + + sysbus_init_child_obj(obj, "mmc0", &s->mmc0, sizeof(s->mmc0), + TYPE_AW_SDHOST_SUN5I); + + sysbus_init_child_obj(obj, "emac", &s->emac, sizeof(s->emac), + TYPE_AW_SUN8I_EMAC); + + sysbus_init_child_obj(obj, "dramc", &s->dramc, sizeof(s->dramc), + TYPE_AW_H3_DRAMC); + object_property_add_alias(obj, "ram-addr", OBJECT(&s->dramc), + "ram-addr", &error_abort); + object_property_add_alias(obj, "ram-size", OBJECT(&s->dramc), + "ram-size", &error_abort); + + sysbus_init_child_obj(obj, "rtc", &s->rtc, sizeof(s->rtc), + TYPE_AW_RTC_SUN6I); +} + +static void allwinner_h3_realize(DeviceState *dev, Error **errp) +{ + AwH3State *s = AW_H3(dev); + unsigned i; + + /* CPUs */ + for (i = 0; i < AW_H3_NUM_CPUS; i++) { + + /* Provide Power State Coordination Interface */ + qdev_prop_set_int32(DEVICE(&s->cpus[i]), "psci-conduit", + QEMU_PSCI_CONDUIT_HVC); + + /* Disable secondary CPUs */ + qdev_prop_set_bit(DEVICE(&s->cpus[i]), "start-powered-off", + i > 0); + + /* All exception levels required */ + qdev_prop_set_bit(DEVICE(&s->cpus[i]), "has_el3", true); + qdev_prop_set_bit(DEVICE(&s->cpus[i]), "has_el2", true); + + /* Mark realized */ + qdev_init_nofail(DEVICE(&s->cpus[i])); + } + + /* Generic Interrupt Controller */ + qdev_prop_set_uint32(DEVICE(&s->gic), "num-irq", AW_H3_GIC_NUM_SPI + + GIC_INTERNAL); + qdev_prop_set_uint32(DEVICE(&s->gic), "revision", 2); + qdev_prop_set_uint32(DEVICE(&s->gic), "num-cpu", AW_H3_NUM_CPUS); + qdev_prop_set_bit(DEVICE(&s->gic), "has-security-extensions", false); + qdev_prop_set_bit(DEVICE(&s->gic), "has-virtualization-extensions", true); + qdev_init_nofail(DEVICE(&s->gic)); + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gic), 0, s->memmap[AW_H3_GIC_DIST]); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gic), 1, s->memmap[AW_H3_GIC_CPU]); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gic), 2, s->memmap[AW_H3_GIC_HYP]); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gic), 3, s->memmap[AW_H3_GIC_VCPU]); + + /* + * Wire the outputs from each CPU's generic timer and the GICv3 + * maintenance interrupt signal to the appropriate GIC PPI inputs, + * and the GIC's IRQ/FIQ/VIRQ/VFIQ interrupt outputs to the CPU's inputs. + */ + for (i = 0; i < AW_H3_NUM_CPUS; i++) { + DeviceState *cpudev = DEVICE(&s->cpus[i]); + int ppibase = AW_H3_GIC_NUM_SPI + i * GIC_INTERNAL + GIC_NR_SGIS; + int irq; + /* + * Mapping from the output timer irq lines from the CPU to the + * GIC PPI inputs used for this board. + */ + const int timer_irq[] = { + [GTIMER_PHYS] = AW_H3_GIC_PPI_PHYSTIMER, + [GTIMER_VIRT] = AW_H3_GIC_PPI_VIRTTIMER, + [GTIMER_HYP] = AW_H3_GIC_PPI_HYPTIMER, + [GTIMER_SEC] = AW_H3_GIC_PPI_SECTIMER, + }; + + /* Connect CPU timer outputs to GIC PPI inputs */ + for (irq = 0; irq < ARRAY_SIZE(timer_irq); irq++) { + qdev_connect_gpio_out(cpudev, irq, + qdev_get_gpio_in(DEVICE(&s->gic), + ppibase + timer_irq[irq])); + } + + /* Connect GIC outputs to CPU interrupt inputs */ + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gic), i, + qdev_get_gpio_in(cpudev, ARM_CPU_IRQ)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gic), i + AW_H3_NUM_CPUS, + qdev_get_gpio_in(cpudev, ARM_CPU_FIQ)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gic), i + (2 * AW_H3_NUM_CPUS), + qdev_get_gpio_in(cpudev, ARM_CPU_VIRQ)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gic), i + (3 * AW_H3_NUM_CPUS), + qdev_get_gpio_in(cpudev, ARM_CPU_VFIQ)); + + /* GIC maintenance signal */ + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gic), i + (4 * AW_H3_NUM_CPUS), + qdev_get_gpio_in(DEVICE(&s->gic), + ppibase + AW_H3_GIC_PPI_MAINT)); + } + + /* Timer */ + qdev_init_nofail(DEVICE(&s->timer)); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->timer), 0, s->memmap[AW_H3_PIT]); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->timer), 0, + qdev_get_gpio_in(DEVICE(&s->gic), AW_H3_GIC_SPI_TIMER0)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->timer), 1, + qdev_get_gpio_in(DEVICE(&s->gic), AW_H3_GIC_SPI_TIMER1)); + + /* SRAM */ + memory_region_init_ram(&s->sram_a1, OBJECT(dev), "sram A1", + 64 * KiB, &error_abort); + memory_region_init_ram(&s->sram_a2, OBJECT(dev), "sram A2", + 32 * KiB, &error_abort); + memory_region_init_ram(&s->sram_c, OBJECT(dev), "sram C", + 44 * KiB, &error_abort); + memory_region_add_subregion(get_system_memory(), s->memmap[AW_H3_SRAM_A1], + &s->sram_a1); + memory_region_add_subregion(get_system_memory(), s->memmap[AW_H3_SRAM_A2], + &s->sram_a2); + memory_region_add_subregion(get_system_memory(), s->memmap[AW_H3_SRAM_C], + &s->sram_c); + + /* Clock Control Unit */ + qdev_init_nofail(DEVICE(&s->ccu)); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->ccu), 0, s->memmap[AW_H3_CCU]); + + /* System Control */ + qdev_init_nofail(DEVICE(&s->sysctrl)); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->sysctrl), 0, s->memmap[AW_H3_SYSCTRL]); + + /* CPU Configuration */ + qdev_init_nofail(DEVICE(&s->cpucfg)); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->cpucfg), 0, s->memmap[AW_H3_CPUCFG]); + + /* Security Identifier */ + qdev_init_nofail(DEVICE(&s->sid)); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->sid), 0, s->memmap[AW_H3_SID]); + + /* SD/MMC */ + qdev_init_nofail(DEVICE(&s->mmc0)); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->mmc0), 0, s->memmap[AW_H3_MMC0]); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->mmc0), 0, + qdev_get_gpio_in(DEVICE(&s->gic), AW_H3_GIC_SPI_MMC0)); + + object_property_add_alias(OBJECT(s), "sd-bus", OBJECT(&s->mmc0), + "sd-bus", &error_abort); + + /* EMAC */ + if (nd_table[0].used) { + qemu_check_nic_model(&nd_table[0], TYPE_AW_SUN8I_EMAC); + qdev_set_nic_properties(DEVICE(&s->emac), &nd_table[0]); + } + qdev_init_nofail(DEVICE(&s->emac)); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->emac), 0, s->memmap[AW_H3_EMAC]); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->emac), 0, + qdev_get_gpio_in(DEVICE(&s->gic), AW_H3_GIC_SPI_EMAC)); + + /* Universal Serial Bus */ + sysbus_create_simple(TYPE_AW_H3_EHCI, s->memmap[AW_H3_EHCI0], + qdev_get_gpio_in(DEVICE(&s->gic), + AW_H3_GIC_SPI_EHCI0)); + sysbus_create_simple(TYPE_AW_H3_EHCI, s->memmap[AW_H3_EHCI1], + qdev_get_gpio_in(DEVICE(&s->gic), + AW_H3_GIC_SPI_EHCI1)); + sysbus_create_simple(TYPE_AW_H3_EHCI, s->memmap[AW_H3_EHCI2], + qdev_get_gpio_in(DEVICE(&s->gic), + AW_H3_GIC_SPI_EHCI2)); + sysbus_create_simple(TYPE_AW_H3_EHCI, s->memmap[AW_H3_EHCI3], + qdev_get_gpio_in(DEVICE(&s->gic), + AW_H3_GIC_SPI_EHCI3)); + + sysbus_create_simple("sysbus-ohci", s->memmap[AW_H3_OHCI0], + qdev_get_gpio_in(DEVICE(&s->gic), + AW_H3_GIC_SPI_OHCI0)); + sysbus_create_simple("sysbus-ohci", s->memmap[AW_H3_OHCI1], + qdev_get_gpio_in(DEVICE(&s->gic), + AW_H3_GIC_SPI_OHCI1)); + sysbus_create_simple("sysbus-ohci", s->memmap[AW_H3_OHCI2], + qdev_get_gpio_in(DEVICE(&s->gic), + AW_H3_GIC_SPI_OHCI2)); + sysbus_create_simple("sysbus-ohci", s->memmap[AW_H3_OHCI3], + qdev_get_gpio_in(DEVICE(&s->gic), + AW_H3_GIC_SPI_OHCI3)); + + /* UART0. For future clocktree API: All UARTS are connected to APB2_CLK. */ + serial_mm_init(get_system_memory(), s->memmap[AW_H3_UART0], 2, + qdev_get_gpio_in(DEVICE(&s->gic), AW_H3_GIC_SPI_UART0), + 115200, serial_hd(0), DEVICE_NATIVE_ENDIAN); + /* UART1 */ + serial_mm_init(get_system_memory(), s->memmap[AW_H3_UART1], 2, + qdev_get_gpio_in(DEVICE(&s->gic), AW_H3_GIC_SPI_UART1), + 115200, serial_hd(1), DEVICE_NATIVE_ENDIAN); + /* UART2 */ + serial_mm_init(get_system_memory(), s->memmap[AW_H3_UART2], 2, + qdev_get_gpio_in(DEVICE(&s->gic), AW_H3_GIC_SPI_UART2), + 115200, serial_hd(2), DEVICE_NATIVE_ENDIAN); + /* UART3 */ + serial_mm_init(get_system_memory(), s->memmap[AW_H3_UART3], 2, + qdev_get_gpio_in(DEVICE(&s->gic), AW_H3_GIC_SPI_UART3), + 115200, serial_hd(3), DEVICE_NATIVE_ENDIAN); + + /* DRAMC */ + qdev_init_nofail(DEVICE(&s->dramc)); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->dramc), 0, s->memmap[AW_H3_DRAMCOM]); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->dramc), 1, s->memmap[AW_H3_DRAMCTL]); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->dramc), 2, s->memmap[AW_H3_DRAMPHY]); + + /* RTC */ + qdev_init_nofail(DEVICE(&s->rtc)); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->rtc), 0, s->memmap[AW_H3_RTC]); + + /* Unimplemented devices */ + for (i = 0; i < ARRAY_SIZE(unimplemented); i++) { + create_unimplemented_device(unimplemented[i].device_name, + unimplemented[i].base, + unimplemented[i].size); + } +} + +static void allwinner_h3_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = allwinner_h3_realize; + /* Reason: uses serial_hd() in realize function */ + dc->user_creatable = false; +} + +static const TypeInfo allwinner_h3_type_info = { + .name = TYPE_AW_H3, + .parent = TYPE_DEVICE, + .instance_size = sizeof(AwH3State), + .instance_init = allwinner_h3_init, + .class_init = allwinner_h3_class_init, +}; + +static void allwinner_h3_register_types(void) +{ + type_register_static(&allwinner_h3_type_info); +} + +type_init(allwinner_h3_register_types) diff --git a/hw/arm/armsse.c b/hw/arm/armsse.c index e5263aa33d..174ca7effc 100644 --- a/hw/arm/armsse.c +++ b/hw/arm/armsse.c @@ -1283,7 +1283,7 @@ static void armsse_class_init(ObjectClass *klass, void *data) dc->realize = armsse_realize; dc->vmsd = &armsse_vmstate; - dc->props = info->props; + device_class_set_props(dc, info->props); dc->reset = armsse_reset; iic->check = armsse_idau_check; asc->info = info; diff --git a/hw/arm/armv7m.c b/hw/arm/armv7m.c index 7a3c48f002..7531b97ccd 100644 --- a/hw/arm/armv7m.c +++ b/hw/arm/armv7m.c @@ -288,7 +288,7 @@ static void armv7m_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = armv7m_realize; - dc->props = armv7m_properties; + device_class_set_props(dc, armv7m_properties); } static const TypeInfo armv7m_info = { @@ -331,7 +331,7 @@ void armv7m_load_kernel(ARMCPU *cpu, const char *kernel_filename, int mem_size) if (kernel_filename) { image_size = load_elf_as(kernel_filename, NULL, NULL, NULL, - &entry, &lowaddr, + &entry, &lowaddr, NULL, NULL, big_endian, EM_ARM, 1, 0, as); if (image_size < 0) { image_size = load_image_targphys_as(kernel_filename, 0, @@ -367,7 +367,7 @@ static void bitband_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = bitband_realize; - dc->props = bitband_properties; + device_class_set_props(dc, bitband_properties); } static const TypeInfo bitband_info = { diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c index 028191ff36..a6a2102a93 100644 --- a/hw/arm/aspeed.c +++ b/hw/arm/aspeed.c @@ -35,7 +35,6 @@ static struct arm_boot_info aspeed_board_binfo = { struct AspeedBoardState { AspeedSoCState soc; MemoryRegion ram_container; - MemoryRegion ram; MemoryRegion max_ram; }; @@ -92,6 +91,10 @@ struct AspeedBoardState { #define AST2600_EVB_HW_STRAP1 0x000000C0 #define AST2600_EVB_HW_STRAP2 0x00000003 +/* Tacoma hardware value */ +#define TACOMA_BMC_HW_STRAP1 0x00000000 +#define TACOMA_BMC_HW_STRAP2 0x00000000 + /* * The max ram region is for firmwares that scan the address space * with load/store to guess how much RAM the SoC has. @@ -167,10 +170,23 @@ static void aspeed_board_init_flashes(AspeedSMCState *s, const char *flashtype, } } -static void aspeed_board_init(MachineState *machine, - const AspeedBoardConfig *cfg) +static void sdhci_attach_drive(SDHCIState *sdhci, DriveInfo *dinfo) +{ + DeviceState *card; + + card = qdev_create(qdev_get_child_bus(DEVICE(sdhci), "sd-bus"), + TYPE_SD_CARD); + if (dinfo) { + qdev_prop_set_drive(card, "drive", blk_by_legacy_dinfo(dinfo), + &error_fatal); + } + object_property_set_bool(OBJECT(card), true, "realized", &error_fatal); +} + +static void aspeed_machine_init(MachineState *machine) { AspeedBoardState *bmc; + AspeedMachineClass *amc = ASPEED_MACHINE_GET_CLASS(machine); AspeedSoCClass *sc; DriveInfo *drive0 = drive_get(IF_MTD, 0, 0); ram_addr_t max_ram_size; @@ -180,20 +196,25 @@ static void aspeed_board_init(MachineState *machine, memory_region_init(&bmc->ram_container, NULL, "aspeed-ram-container", UINT32_MAX); + memory_region_add_subregion(&bmc->ram_container, 0, machine->ram); object_initialize_child(OBJECT(machine), "soc", &bmc->soc, - (sizeof(bmc->soc)), cfg->soc_name, &error_abort, + (sizeof(bmc->soc)), amc->soc_name, &error_abort, NULL); sc = ASPEED_SOC_GET_CLASS(&bmc->soc); + /* + * This will error out if isize is not supported by memory controller. + */ object_property_set_uint(OBJECT(&bmc->soc), ram_size, "ram-size", - &error_abort); - object_property_set_int(OBJECT(&bmc->soc), cfg->hw_strap1, "hw-strap1", + &error_fatal); + + object_property_set_int(OBJECT(&bmc->soc), amc->hw_strap1, "hw-strap1", &error_abort); - object_property_set_int(OBJECT(&bmc->soc), cfg->hw_strap2, "hw-strap2", + object_property_set_int(OBJECT(&bmc->soc), amc->hw_strap2, "hw-strap2", &error_abort); - object_property_set_int(OBJECT(&bmc->soc), cfg->num_cs, "num-cs", + object_property_set_int(OBJECT(&bmc->soc), amc->num_cs, "num-cs", &error_abort); object_property_set_int(OBJECT(&bmc->soc), machine->smp.cpus, "num-cpus", &error_abort); @@ -211,15 +232,6 @@ static void aspeed_board_init(MachineState *machine, object_property_set_bool(OBJECT(&bmc->soc), true, "realized", &error_abort); - /* - * Allocate RAM after the memory controller has checked the size - * was valid. If not, a default value is used. - */ - ram_size = object_property_get_uint(OBJECT(&bmc->soc), "ram-size", - &error_abort); - - memory_region_allocate_system_memory(&bmc->ram, NULL, "ram", ram_size); - memory_region_add_subregion(&bmc->ram_container, 0, &bmc->ram); memory_region_add_subregion(get_system_memory(), sc->memmap[ASPEED_SDRAM], &bmc->ram_container); @@ -230,8 +242,8 @@ static void aspeed_board_init(MachineState *machine, "max_ram", max_ram_size - ram_size); memory_region_add_subregion(&bmc->ram_container, ram_size, &bmc->max_ram); - aspeed_board_init_flashes(&bmc->soc.fmc, cfg->fmc_model, &error_abort); - aspeed_board_init_flashes(&bmc->soc.spi[0], cfg->spi_model, &error_abort); + aspeed_board_init_flashes(&bmc->soc.fmc, amc->fmc_model, &error_abort); + aspeed_board_init_flashes(&bmc->soc.spi[0], amc->spi_model, &error_abort); /* Install first FMC flash content as a boot rom. */ if (drive0) { @@ -244,32 +256,34 @@ static void aspeed_board_init(MachineState *machine, * SoC and 128MB for the AST2500 SoC, which is twice as big as * needed by the flash modules of the Aspeed machines. */ - memory_region_init_rom(boot_rom, OBJECT(bmc), "aspeed.boot_rom", - fl->size, &error_abort); - memory_region_add_subregion(get_system_memory(), FIRMWARE_ADDR, - boot_rom); - write_boot_rom(drive0, FIRMWARE_ADDR, fl->size, &error_abort); + if (ASPEED_MACHINE(machine)->mmio_exec) { + memory_region_init_alias(boot_rom, OBJECT(bmc), "aspeed.boot_rom", + &fl->mmio, 0, fl->size); + memory_region_add_subregion(get_system_memory(), FIRMWARE_ADDR, + boot_rom); + } else { + memory_region_init_rom(boot_rom, OBJECT(bmc), "aspeed.boot_rom", + fl->size, &error_abort); + memory_region_add_subregion(get_system_memory(), FIRMWARE_ADDR, + boot_rom); + write_boot_rom(drive0, FIRMWARE_ADDR, fl->size, &error_abort); + } } aspeed_board_binfo.ram_size = ram_size; aspeed_board_binfo.loader_start = sc->memmap[ASPEED_SDRAM]; aspeed_board_binfo.nb_cpus = bmc->soc.num_cpus; - if (cfg->i2c_init) { - cfg->i2c_init(bmc); + if (amc->i2c_init) { + amc->i2c_init(bmc); } - for (i = 0; i < ARRAY_SIZE(bmc->soc.sdhci.slots); i++) { - SDHCIState *sdhci = &bmc->soc.sdhci.slots[i]; - DriveInfo *dinfo = drive_get_next(IF_SD); - BlockBackend *blk; - DeviceState *card; + for (i = 0; i < bmc->soc.sdhci.num_slots; i++) { + sdhci_attach_drive(&bmc->soc.sdhci.slots[i], drive_get_next(IF_SD)); + } - blk = dinfo ? blk_by_legacy_dinfo(dinfo) : NULL; - card = qdev_create(qdev_get_child_bus(DEVICE(sdhci), "sd-bus"), - TYPE_SD_CARD); - qdev_prop_set_drive(card, "drive", blk, &error_fatal); - object_property_set_bool(OBJECT(card), true, "realized", &error_fatal); + if (bmc->soc.emmc.num_slots) { + sdhci_attach_drive(&bmc->soc.emmc.slots[0], drive_get_next(IF_SD)); } arm_load_kernel(ARM_CPU(first_cpu), machine, &aspeed_board_binfo); @@ -363,6 +377,9 @@ static void witherspoon_bmc_i2c_init(AspeedBoardState *bmc) AspeedSoCState *soc = &bmc->soc; uint8_t *eeprom_buf = g_malloc0(8 * 1024); + /* Bus 3: TODO bmp280@77 */ + /* Bus 3: TODO max31785@52 */ + /* Bus 3: TODO dps310@76 */ i2c_create_slave(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 3), TYPE_PCA9552, 0x60); @@ -381,120 +398,192 @@ static void witherspoon_bmc_i2c_init(AspeedBoardState *bmc) eeprom_buf); i2c_create_slave(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 11), TYPE_PCA9552, 0x60); + /* Bus 11: TODO ucd90160@64 */ } -static void aspeed_machine_init(MachineState *machine) +static bool aspeed_get_mmio_exec(Object *obj, Error **errp) { - AspeedMachineClass *amc = ASPEED_MACHINE_GET_CLASS(machine); + return ASPEED_MACHINE(obj)->mmio_exec; +} - aspeed_board_init(machine, amc->board); +static void aspeed_set_mmio_exec(Object *obj, bool value, Error **errp) +{ + ASPEED_MACHINE(obj)->mmio_exec = value; +} + +static void aspeed_machine_instance_init(Object *obj) +{ + ASPEED_MACHINE(obj)->mmio_exec = false; +} + +static void aspeed_machine_class_props_init(ObjectClass *oc) +{ + object_class_property_add_bool(oc, "execute-in-place", + aspeed_get_mmio_exec, + aspeed_set_mmio_exec, &error_abort); + object_class_property_set_description(oc, "execute-in-place", + "boot directly from CE0 flash device", &error_abort); } static void aspeed_machine_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); - AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc); - const AspeedBoardConfig *board = data; - mc->desc = board->desc; mc->init = aspeed_machine_init; mc->max_cpus = ASPEED_CPUS_NUM; mc->no_floppy = 1; mc->no_cdrom = 1; mc->no_parallel = 1; - if (board->ram) { - mc->default_ram_size = board->ram; - } - amc->board = board; + mc->default_ram_id = "ram"; + + aspeed_machine_class_props_init(oc); } -static const TypeInfo aspeed_machine_type = { - .name = TYPE_ASPEED_MACHINE, - .parent = TYPE_MACHINE, - .instance_size = sizeof(AspeedMachine), - .class_size = sizeof(AspeedMachineClass), - .abstract = true, -}; - -static const AspeedBoardConfig aspeed_boards[] = { - { - .name = MACHINE_TYPE_NAME("palmetto-bmc"), - .desc = "OpenPOWER Palmetto BMC (ARM926EJ-S)", - .soc_name = "ast2400-a1", - .hw_strap1 = PALMETTO_BMC_HW_STRAP1, - .fmc_model = "n25q256a", - .spi_model = "mx25l25635e", - .num_cs = 1, - .i2c_init = palmetto_bmc_i2c_init, - .ram = 256 * MiB, - }, { - .name = MACHINE_TYPE_NAME("ast2500-evb"), - .desc = "Aspeed AST2500 EVB (ARM1176)", - .soc_name = "ast2500-a1", - .hw_strap1 = AST2500_EVB_HW_STRAP1, - .fmc_model = "w25q256", - .spi_model = "mx25l25635e", - .num_cs = 1, - .i2c_init = ast2500_evb_i2c_init, - .ram = 512 * MiB, - }, { - .name = MACHINE_TYPE_NAME("romulus-bmc"), - .desc = "OpenPOWER Romulus BMC (ARM1176)", - .soc_name = "ast2500-a1", - .hw_strap1 = ROMULUS_BMC_HW_STRAP1, - .fmc_model = "n25q256a", - .spi_model = "mx66l1g45g", - .num_cs = 2, - .i2c_init = romulus_bmc_i2c_init, - .ram = 512 * MiB, - }, { - .name = MACHINE_TYPE_NAME("swift-bmc"), - .desc = "OpenPOWER Swift BMC (ARM1176)", - .soc_name = "ast2500-a1", - .hw_strap1 = SWIFT_BMC_HW_STRAP1, - .fmc_model = "mx66l1g45g", - .spi_model = "mx66l1g45g", - .num_cs = 2, - .i2c_init = swift_bmc_i2c_init, - .ram = 512 * MiB, - }, { - .name = MACHINE_TYPE_NAME("witherspoon-bmc"), - .desc = "OpenPOWER Witherspoon BMC (ARM1176)", - .soc_name = "ast2500-a1", - .hw_strap1 = WITHERSPOON_BMC_HW_STRAP1, - .fmc_model = "mx25l25635e", - .spi_model = "mx66l1g45g", - .num_cs = 2, - .i2c_init = witherspoon_bmc_i2c_init, - .ram = 512 * MiB, - }, { - .name = MACHINE_TYPE_NAME("ast2600-evb"), - .desc = "Aspeed AST2600 EVB (Cortex A7)", - .soc_name = "ast2600-a0", - .hw_strap1 = AST2600_EVB_HW_STRAP1, - .hw_strap2 = AST2600_EVB_HW_STRAP2, - .fmc_model = "w25q512jv", - .spi_model = "mx66u51235f", - .num_cs = 1, - .i2c_init = ast2600_evb_i2c_init, - .ram = 1 * GiB, - }, -}; - -static void aspeed_machine_types(void) +static void aspeed_machine_palmetto_class_init(ObjectClass *oc, void *data) { - int i; + MachineClass *mc = MACHINE_CLASS(oc); + AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc); - type_register_static(&aspeed_machine_type); - for (i = 0; i < ARRAY_SIZE(aspeed_boards); ++i) { - TypeInfo ti = { - .name = aspeed_boards[i].name, - .parent = TYPE_ASPEED_MACHINE, - .class_init = aspeed_machine_class_init, - .class_data = (void *)&aspeed_boards[i], - }; - type_register(&ti); + mc->desc = "OpenPOWER Palmetto BMC (ARM926EJ-S)"; + amc->soc_name = "ast2400-a1"; + amc->hw_strap1 = PALMETTO_BMC_HW_STRAP1; + amc->fmc_model = "n25q256a"; + amc->spi_model = "mx25l25635e"; + amc->num_cs = 1; + amc->i2c_init = palmetto_bmc_i2c_init; + mc->default_ram_size = 256 * MiB; +}; + +static void aspeed_machine_ast2500_evb_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc); + + mc->desc = "Aspeed AST2500 EVB (ARM1176)"; + amc->soc_name = "ast2500-a1"; + amc->hw_strap1 = AST2500_EVB_HW_STRAP1; + amc->fmc_model = "w25q256"; + amc->spi_model = "mx25l25635e"; + amc->num_cs = 1; + amc->i2c_init = ast2500_evb_i2c_init; + mc->default_ram_size = 512 * MiB; +}; + +static void aspeed_machine_romulus_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc); + + mc->desc = "OpenPOWER Romulus BMC (ARM1176)"; + amc->soc_name = "ast2500-a1"; + amc->hw_strap1 = ROMULUS_BMC_HW_STRAP1; + amc->fmc_model = "n25q256a"; + amc->spi_model = "mx66l1g45g"; + amc->num_cs = 2; + amc->i2c_init = romulus_bmc_i2c_init; + mc->default_ram_size = 512 * MiB; +}; + +static void aspeed_machine_swift_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc); + + mc->desc = "OpenPOWER Swift BMC (ARM1176)"; + amc->soc_name = "ast2500-a1"; + amc->hw_strap1 = SWIFT_BMC_HW_STRAP1; + amc->fmc_model = "mx66l1g45g"; + amc->spi_model = "mx66l1g45g"; + amc->num_cs = 2; + amc->i2c_init = swift_bmc_i2c_init; + mc->default_ram_size = 512 * MiB; +}; + +static void aspeed_machine_witherspoon_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc); + + mc->desc = "OpenPOWER Witherspoon BMC (ARM1176)"; + amc->soc_name = "ast2500-a1"; + amc->hw_strap1 = WITHERSPOON_BMC_HW_STRAP1; + amc->fmc_model = "mx25l25635e"; + amc->spi_model = "mx66l1g45g"; + amc->num_cs = 2; + amc->i2c_init = witherspoon_bmc_i2c_init; + mc->default_ram_size = 512 * MiB; +}; + +static void aspeed_machine_ast2600_evb_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc); + + mc->desc = "Aspeed AST2600 EVB (Cortex A7)"; + amc->soc_name = "ast2600-a0"; + amc->hw_strap1 = AST2600_EVB_HW_STRAP1; + amc->hw_strap2 = AST2600_EVB_HW_STRAP2; + amc->fmc_model = "w25q512jv"; + amc->spi_model = "mx66u51235f"; + amc->num_cs = 1; + amc->i2c_init = ast2600_evb_i2c_init; + mc->default_ram_size = 1 * GiB; +}; + +static void aspeed_machine_tacoma_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc); + + mc->desc = "Aspeed AST2600 EVB (Cortex A7)"; + amc->soc_name = "ast2600-a0"; + amc->hw_strap1 = TACOMA_BMC_HW_STRAP1; + amc->hw_strap2 = TACOMA_BMC_HW_STRAP2; + amc->fmc_model = "mx66l1g45g"; + amc->spi_model = "mx66l1g45g"; + amc->num_cs = 2; + amc->i2c_init = witherspoon_bmc_i2c_init; /* Same board layout */ + mc->default_ram_size = 1 * GiB; +}; + +static const TypeInfo aspeed_machine_types[] = { + { + .name = MACHINE_TYPE_NAME("palmetto-bmc"), + .parent = TYPE_ASPEED_MACHINE, + .class_init = aspeed_machine_palmetto_class_init, + }, { + .name = MACHINE_TYPE_NAME("ast2500-evb"), + .parent = TYPE_ASPEED_MACHINE, + .class_init = aspeed_machine_ast2500_evb_class_init, + }, { + .name = MACHINE_TYPE_NAME("romulus-bmc"), + .parent = TYPE_ASPEED_MACHINE, + .class_init = aspeed_machine_romulus_class_init, + }, { + .name = MACHINE_TYPE_NAME("swift-bmc"), + .parent = TYPE_ASPEED_MACHINE, + .class_init = aspeed_machine_swift_class_init, + }, { + .name = MACHINE_TYPE_NAME("witherspoon-bmc"), + .parent = TYPE_ASPEED_MACHINE, + .class_init = aspeed_machine_witherspoon_class_init, + }, { + .name = MACHINE_TYPE_NAME("ast2600-evb"), + .parent = TYPE_ASPEED_MACHINE, + .class_init = aspeed_machine_ast2600_evb_class_init, + }, { + .name = MACHINE_TYPE_NAME("tacoma-bmc"), + .parent = TYPE_ASPEED_MACHINE, + .class_init = aspeed_machine_tacoma_class_init, + }, { + .name = TYPE_ASPEED_MACHINE, + .parent = TYPE_MACHINE, + .instance_size = sizeof(AspeedMachine), + .instance_init = aspeed_machine_instance_init, + .class_size = sizeof(AspeedMachineClass), + .class_init = aspeed_machine_class_init, + .abstract = true, } -} +}; -type_init(aspeed_machine_types) +DEFINE_TYPES(aspeed_machine_types) diff --git a/hw/arm/aspeed_ast2600.c b/hw/arm/aspeed_ast2600.c index 931887ac68..1a869e09b9 100644 --- a/hw/arm/aspeed_ast2600.c +++ b/hw/arm/aspeed_ast2600.c @@ -31,6 +31,8 @@ static const hwaddr aspeed_soc_ast2600_memmap[] = { [ASPEED_FMC] = 0x1E620000, [ASPEED_SPI1] = 0x1E630000, [ASPEED_SPI2] = 0x1E641000, + [ASPEED_EHCI1] = 0x1E6A1000, + [ASPEED_EHCI2] = 0x1E6A3000, [ASPEED_MII1] = 0x1E650000, [ASPEED_MII2] = 0x1E650008, [ASPEED_MII3] = 0x1E650010, @@ -46,6 +48,7 @@ static const hwaddr aspeed_soc_ast2600_memmap[] = { [ASPEED_ADC] = 0x1E6E9000, [ASPEED_VIDEO] = 0x1E700000, [ASPEED_SDHCI] = 0x1E740000, + [ASPEED_EMMC] = 0x1E750000, [ASPEED_GPIO] = 0x1E780000, [ASPEED_GPIO_1_8V] = 0x1E780800, [ASPEED_RTC] = 0x1E781000, @@ -64,6 +67,7 @@ static const hwaddr aspeed_soc_ast2600_memmap[] = { #define ASPEED_SOC_AST2600_MAX_IRQ 128 +/* Shared Peripheral Interrupt values below are offset by -32 from datasheet */ static const int aspeed_soc_ast2600_irqmap[] = { [ASPEED_UART1] = 47, [ASPEED_UART2] = 48, @@ -77,6 +81,9 @@ static const int aspeed_soc_ast2600_irqmap[] = { [ASPEED_ADC] = 78, [ASPEED_XDMA] = 6, [ASPEED_SDHCI] = 43, + [ASPEED_EHCI1] = 5, + [ASPEED_EHCI2] = 9, + [ASPEED_EMMC] = 15, [ASPEED_GPIO] = 40, [ASPEED_GPIO_1_8V] = 11, [ASPEED_RTC] = 13, @@ -146,8 +153,6 @@ static void aspeed_soc_ast2600_init(Object *obj) snprintf(typename, sizeof(typename), "aspeed.timer-%s", socname); sysbus_init_child_obj(obj, "timerctrl", OBJECT(&s->timerctrl), sizeof(s->timerctrl), typename); - object_property_add_const_link(OBJECT(&s->timerctrl), "scu", - OBJECT(&s->scu), &error_abort); snprintf(typename, sizeof(typename), "aspeed.i2c-%s", socname); sysbus_init_child_obj(obj, "i2c", OBJECT(&s->i2c), sizeof(s->i2c), @@ -158,8 +163,6 @@ static void aspeed_soc_ast2600_init(Object *obj) typename); object_property_add_alias(obj, "num-cs", OBJECT(&s->fmc), "num-cs", &error_abort); - object_property_add_alias(obj, "dram", OBJECT(&s->fmc), "dram", - &error_abort); for (i = 0; i < sc->spis_num; i++) { snprintf(typename, sizeof(typename), "aspeed.spi%d-%s", i + 1, socname); @@ -167,6 +170,11 @@ static void aspeed_soc_ast2600_init(Object *obj) sizeof(s->spi[i]), typename); } + for (i = 0; i < sc->ehcis_num; i++) { + sysbus_init_child_obj(obj, "ehci[*]", OBJECT(&s->ehci[i]), + sizeof(s->ehci[i]), TYPE_PLATFORM_EHCI); + } + snprintf(typename, sizeof(typename), "aspeed.sdmc-%s", socname); sysbus_init_child_obj(obj, "sdmc", OBJECT(&s->sdmc), sizeof(s->sdmc), typename); @@ -179,8 +187,6 @@ static void aspeed_soc_ast2600_init(Object *obj) snprintf(typename, sizeof(typename), "aspeed.wdt-%s", socname); sysbus_init_child_obj(obj, "wdt[*]", OBJECT(&s->wdt[i]), sizeof(s->wdt[i]), typename); - object_property_add_const_link(OBJECT(&s->wdt[i]), "scu", - OBJECT(&s->scu), &error_abort); } for (i = 0; i < sc->macs_num; i++) { @@ -189,9 +195,6 @@ static void aspeed_soc_ast2600_init(Object *obj) sysbus_init_child_obj(obj, "mii[*]", &s->mii[i], sizeof(s->mii[i]), TYPE_ASPEED_MII); - object_property_add_const_link(OBJECT(&s->mii[i]), "nic", - OBJECT(&s->ftgmac100[i]), - &error_abort); } sysbus_init_child_obj(obj, "xdma", OBJECT(&s->xdma), sizeof(s->xdma), @@ -205,14 +208,26 @@ static void aspeed_soc_ast2600_init(Object *obj) sysbus_init_child_obj(obj, "gpio_1_8v", OBJECT(&s->gpio_1_8v), sizeof(s->gpio_1_8v), typename); - sysbus_init_child_obj(obj, "sdc", OBJECT(&s->sdhci), sizeof(s->sdhci), - TYPE_ASPEED_SDHCI); + sysbus_init_child_obj(obj, "sd-controller", OBJECT(&s->sdhci), + sizeof(s->sdhci), TYPE_ASPEED_SDHCI); + + object_property_set_int(OBJECT(&s->sdhci), 2, "num-slots", &error_abort); /* Init sd card slot class here so that they're under the correct parent */ for (i = 0; i < ASPEED_SDHCI_NUM_SLOTS; ++i) { - sysbus_init_child_obj(obj, "sdhci[*]", OBJECT(&s->sdhci.slots[i]), + sysbus_init_child_obj(obj, "sd-controller.sdhci[*]", + OBJECT(&s->sdhci.slots[i]), sizeof(s->sdhci.slots[i]), TYPE_SYSBUS_SDHCI); } + + sysbus_init_child_obj(obj, "emmc-controller", OBJECT(&s->emmc), + sizeof(s->emmc), TYPE_ASPEED_SDHCI); + + object_property_set_int(OBJECT(&s->emmc), 1, "num-slots", &error_abort); + + sysbus_init_child_obj(obj, "emmc-controller.sdhci", + OBJECT(&s->emmc.slots[0]), sizeof(s->emmc.slots[0]), + TYPE_SYSBUS_SDHCI); } /* @@ -259,6 +274,9 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) object_property_set_int(OBJECT(&s->cpu[i]), aspeed_calc_affinity(i), "mp-affinity", &error_abort); + object_property_set_int(OBJECT(&s->cpu[i]), 1125000000, "cntfrq", + &error_abort); + /* * TODO: the secondary CPUs are started and a boot helper * is needed when using -kernel @@ -325,6 +343,8 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) aspeed_soc_get_irq(s, ASPEED_RTC)); /* Timer */ + object_property_set_link(OBJECT(&s->timerctrl), + OBJECT(&s->scu), "scu", &error_abort); object_property_set_bool(OBJECT(&s->timerctrl), true, "realized", &err); if (err) { error_propagate(errp, err); @@ -345,6 +365,11 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) } /* I2C */ + object_property_set_link(OBJECT(&s->i2c), OBJECT(s->dram_mr), "dram", &err); + if (err) { + error_propagate(errp, err); + return; + } object_property_set_bool(OBJECT(&s->i2c), true, "realized", &err); if (err) { error_propagate(errp, err); @@ -362,6 +387,11 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) } /* FMC, The number of CS is set at the board level */ + object_property_set_link(OBJECT(&s->fmc), OBJECT(s->dram_mr), "dram", &err); + if (err) { + error_propagate(errp, err); + return; + } object_property_set_int(OBJECT(&s->fmc), sc->memmap[ASPEED_SDRAM], "sdram-base", &err); if (err) { @@ -381,6 +411,12 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) /* SPI */ for (i = 0; i < sc->spis_num; i++) { + object_property_set_link(OBJECT(&s->spi[i]), OBJECT(s->dram_mr), + "dram", &err); + if (err) { + error_propagate(errp, err); + return; + } object_property_set_int(OBJECT(&s->spi[i]), 1, "num-cs", &err); object_property_set_bool(OBJECT(&s->spi[i]), true, "realized", &local_err); @@ -395,6 +431,19 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) s->spi[i].ctrl->flash_window_base); } + /* EHCI */ + for (i = 0; i < sc->ehcis_num; i++) { + object_property_set_bool(OBJECT(&s->ehci[i]), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->ehci[i]), 0, + sc->memmap[ASPEED_EHCI1 + i]); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->ehci[i]), 0, + aspeed_soc_get_irq(s, ASPEED_EHCI1 + i)); + } + /* SDMC - SDRAM Memory Controller */ object_property_set_bool(OBJECT(&s->sdmc), true, "realized", &err); if (err) { @@ -407,6 +456,8 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) for (i = 0; i < sc->wdts_num; i++) { AspeedWDTClass *awc = ASPEED_WDT_GET_CLASS(&s->wdt[i]); + object_property_set_link(OBJECT(&s->wdt[i]), + OBJECT(&s->scu), "scu", &error_abort); object_property_set_bool(OBJECT(&s->wdt[i]), true, "realized", &err); if (err) { error_propagate(errp, err); @@ -433,6 +484,8 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) sysbus_connect_irq(SYS_BUS_DEVICE(&s->ftgmac100[i]), 0, aspeed_soc_get_irq(s, ASPEED_ETH1 + i)); + object_property_set_link(OBJECT(&s->mii[i]), OBJECT(&s->ftgmac100[i]), + "nic", &error_abort); object_property_set_bool(OBJECT(&s->mii[i]), true, "realized", &err); if (err) { @@ -485,6 +538,16 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) sc->memmap[ASPEED_SDHCI]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->sdhci), 0, aspeed_soc_get_irq(s, ASPEED_SDHCI)); + + /* eMMC */ + object_property_set_bool(OBJECT(&s->emmc), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->emmc), 0, sc->memmap[ASPEED_EMMC]); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->emmc), 0, + aspeed_soc_get_irq(s, ASPEED_EMMC)); } static void aspeed_soc_ast2600_class_init(ObjectClass *oc, void *data) @@ -499,6 +562,7 @@ static void aspeed_soc_ast2600_class_init(ObjectClass *oc, void *data) sc->silicon_rev = AST2600_A0_SILICON_REV; sc->sram_size = 0x10000; sc->spis_num = 2; + sc->ehcis_num = 2; sc->wdts_num = 4; sc->macs_num = 4; sc->irqmap = aspeed_soc_ast2600_irqmap; diff --git a/hw/arm/aspeed_soc.c b/hw/arm/aspeed_soc.c index f4fe243458..696c7fda14 100644 --- a/hw/arm/aspeed_soc.c +++ b/hw/arm/aspeed_soc.c @@ -30,6 +30,7 @@ static const hwaddr aspeed_soc_ast2400_memmap[] = { [ASPEED_IOMEM] = 0x1E600000, [ASPEED_FMC] = 0x1E620000, [ASPEED_SPI1] = 0x1E630000, + [ASPEED_EHCI1] = 0x1E6A1000, [ASPEED_VIC] = 0x1E6C0000, [ASPEED_SDMC] = 0x1E6E0000, [ASPEED_SCU] = 0x1E6E2000, @@ -59,6 +60,8 @@ static const hwaddr aspeed_soc_ast2500_memmap[] = { [ASPEED_FMC] = 0x1E620000, [ASPEED_SPI1] = 0x1E630000, [ASPEED_SPI2] = 0x1E631000, + [ASPEED_EHCI1] = 0x1E6A1000, + [ASPEED_EHCI2] = 0x1E6A3000, [ASPEED_VIC] = 0x1E6C0000, [ASPEED_SDMC] = 0x1E6E0000, [ASPEED_SCU] = 0x1E6E2000, @@ -91,6 +94,8 @@ static const int aspeed_soc_ast2400_irqmap[] = { [ASPEED_UART5] = 10, [ASPEED_VUART] = 8, [ASPEED_FMC] = 19, + [ASPEED_EHCI1] = 5, + [ASPEED_EHCI2] = 13, [ASPEED_SDMC] = 0, [ASPEED_SCU] = 21, [ASPEED_ADC] = 31, @@ -163,8 +168,6 @@ static void aspeed_soc_init(Object *obj) snprintf(typename, sizeof(typename), "aspeed.timer-%s", socname); sysbus_init_child_obj(obj, "timerctrl", OBJECT(&s->timerctrl), sizeof(s->timerctrl), typename); - object_property_add_const_link(OBJECT(&s->timerctrl), "scu", - OBJECT(&s->scu), &error_abort); snprintf(typename, sizeof(typename), "aspeed.i2c-%s", socname); sysbus_init_child_obj(obj, "i2c", OBJECT(&s->i2c), sizeof(s->i2c), @@ -175,8 +178,6 @@ static void aspeed_soc_init(Object *obj) typename); object_property_add_alias(obj, "num-cs", OBJECT(&s->fmc), "num-cs", &error_abort); - object_property_add_alias(obj, "dram", OBJECT(&s->fmc), "dram", - &error_abort); for (i = 0; i < sc->spis_num; i++) { snprintf(typename, sizeof(typename), "aspeed.spi%d-%s", i + 1, socname); @@ -184,6 +185,11 @@ static void aspeed_soc_init(Object *obj) sizeof(s->spi[i]), typename); } + for (i = 0; i < sc->ehcis_num; i++) { + sysbus_init_child_obj(obj, "ehci[*]", OBJECT(&s->ehci[i]), + sizeof(s->ehci[i]), TYPE_PLATFORM_EHCI); + } + snprintf(typename, sizeof(typename), "aspeed.sdmc-%s", socname); sysbus_init_child_obj(obj, "sdmc", OBJECT(&s->sdmc), sizeof(s->sdmc), typename); @@ -196,8 +202,6 @@ static void aspeed_soc_init(Object *obj) snprintf(typename, sizeof(typename), "aspeed.wdt-%s", socname); sysbus_init_child_obj(obj, "wdt[*]", OBJECT(&s->wdt[i]), sizeof(s->wdt[i]), typename); - object_property_add_const_link(OBJECT(&s->wdt[i]), "scu", - OBJECT(&s->scu), &error_abort); } for (i = 0; i < sc->macs_num; i++) { @@ -215,6 +219,8 @@ static void aspeed_soc_init(Object *obj) sysbus_init_child_obj(obj, "sdc", OBJECT(&s->sdhci), sizeof(s->sdhci), TYPE_ASPEED_SDHCI); + object_property_set_int(OBJECT(&s->sdhci), 2, "num-slots", &error_abort); + /* Init sd card slot class here so that they're under the correct parent */ for (i = 0; i < ASPEED_SDHCI_NUM_SLOTS; ++i) { sysbus_init_child_obj(obj, "sdhci[*]", OBJECT(&s->sdhci.slots[i]), @@ -293,6 +299,8 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp) aspeed_soc_get_irq(s, ASPEED_RTC)); /* Timer */ + object_property_set_link(OBJECT(&s->timerctrl), + OBJECT(&s->scu), "scu", &error_abort); object_property_set_bool(OBJECT(&s->timerctrl), true, "realized", &err); if (err) { error_propagate(errp, err); @@ -313,6 +321,11 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp) } /* I2C */ + object_property_set_link(OBJECT(&s->i2c), OBJECT(s->dram_mr), "dram", &err); + if (err) { + error_propagate(errp, err); + return; + } object_property_set_bool(OBJECT(&s->i2c), true, "realized", &err); if (err) { error_propagate(errp, err); @@ -323,6 +336,11 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp) aspeed_soc_get_irq(s, ASPEED_I2C)); /* FMC, The number of CS is set at the board level */ + object_property_set_link(OBJECT(&s->fmc), OBJECT(s->dram_mr), "dram", &err); + if (err) { + error_propagate(errp, err); + return; + } object_property_set_int(OBJECT(&s->fmc), sc->memmap[ASPEED_SDRAM], "sdram-base", &err); if (err) { @@ -356,6 +374,19 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp) s->spi[i].ctrl->flash_window_base); } + /* EHCI */ + for (i = 0; i < sc->ehcis_num; i++) { + object_property_set_bool(OBJECT(&s->ehci[i]), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->ehci[i]), 0, + sc->memmap[ASPEED_EHCI1 + i]); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->ehci[i]), 0, + aspeed_soc_get_irq(s, ASPEED_EHCI1 + i)); + } + /* SDMC - SDRAM Memory Controller */ object_property_set_bool(OBJECT(&s->sdmc), true, "realized", &err); if (err) { @@ -368,6 +399,8 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp) for (i = 0; i < sc->wdts_num; i++) { AspeedWDTClass *awc = ASPEED_WDT_GET_CLASS(&s->wdt[i]); + object_property_set_link(OBJECT(&s->wdt[i]), + OBJECT(&s->scu), "scu", &error_abort); object_property_set_bool(OBJECT(&s->wdt[i]), true, "realized", &err); if (err) { error_propagate(errp, err); @@ -429,6 +462,8 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp) } static Property aspeed_soc_properties[] = { DEFINE_PROP_UINT32("num-cpus", AspeedSoCState, num_cpus, 0), + DEFINE_PROP_LINK("dram", AspeedSoCState, dram_mr, TYPE_MEMORY_REGION, + MemoryRegion *), DEFINE_PROP_END_OF_LIST(), }; @@ -439,7 +474,7 @@ static void aspeed_soc_class_init(ObjectClass *oc, void *data) dc->realize = aspeed_soc_realize; /* Reason: Uses serial_hds and nd_table in realize() directly */ dc->user_creatable = false; - dc->props = aspeed_soc_properties; + device_class_set_props(dc, aspeed_soc_properties); } static const TypeInfo aspeed_soc_type_info = { @@ -460,6 +495,7 @@ static void aspeed_soc_ast2400_class_init(ObjectClass *oc, void *data) sc->silicon_rev = AST2400_A1_SILICON_REV; sc->sram_size = 0x8000; sc->spis_num = 1; + sc->ehcis_num = 1; sc->wdts_num = 2; sc->macs_num = 2; sc->irqmap = aspeed_soc_ast2400_irqmap; @@ -484,6 +520,7 @@ static void aspeed_soc_ast2500_class_init(ObjectClass *oc, void *data) sc->silicon_rev = AST2500_A1_SILICON_REV; sc->sram_size = 0x9000; sc->spis_num = 2; + sc->ehcis_num = 2; sc->wdts_num = 3; sc->macs_num = 2; sc->irqmap = aspeed_soc_ast2500_irqmap; diff --git a/hw/arm/bcm2835_peripherals.c b/hw/arm/bcm2835_peripherals.c index 17207ae07e..edcaa4916d 100644 --- a/hw/arm/bcm2835_peripherals.c +++ b/hw/arm/bcm2835_peripherals.c @@ -5,7 +5,8 @@ * Rasperry Pi 2 emulation and refactoring Copyright (c) 2015, Microsoft * Written by Andrew Baumann * - * This code is licensed under the GNU GPLv2 and later. + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. */ #include "qemu/osdep.h" diff --git a/hw/arm/bcm2836.c b/hw/arm/bcm2836.c index 221ff06895..43022b83f5 100644 --- a/hw/arm/bcm2836.c +++ b/hw/arm/bcm2836.c @@ -5,7 +5,8 @@ * Rasperry Pi 2 emulation and refactoring Copyright (c) 2015, Microsoft * Written by Andrew Baumann * - * This code is licensed under the GNU GPLv2 and later. + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. */ #include "qemu/osdep.h" @@ -180,7 +181,7 @@ static void bcm283x_class_init(ObjectClass *oc, void *data) bc->info = data; dc->realize = bcm2836_realize; - dc->props = bcm2836_props; + device_class_set_props(dc, bcm2836_props); /* Reason: Must be wired up in code (see raspi_init() function) */ dc->user_creatable = false; } diff --git a/hw/arm/boot.c b/hw/arm/boot.c index 8fb4a63606..fef4072db1 100644 --- a/hw/arm/boot.c +++ b/hw/arm/boot.c @@ -327,8 +327,7 @@ static void set_kernel_args(const struct arm_boot_info *info, AddressSpace *as) cmdline_size = strlen(info->kernel_cmdline); address_space_write(as, p + 8, MEMTXATTRS_UNSPECIFIED, - (const uint8_t *)info->kernel_cmdline, - cmdline_size + 1); + info->kernel_cmdline, cmdline_size + 1); cmdline_size = (cmdline_size >> 2) + 1; WRITE_WORD(p, cmdline_size + 2); WRITE_WORD(p, 0x54410009); @@ -420,8 +419,7 @@ static void set_kernel_args_old(const struct arm_boot_info *info, } s = info->kernel_cmdline; if (s) { - address_space_write(as, p, MEMTXATTRS_UNSPECIFIED, - (const uint8_t *)s, strlen(s) + 1); + address_space_write(as, p, MEMTXATTRS_UNSPECIFIED, s, strlen(s) + 1); } else { WRITE_WORD(p, 0); } @@ -903,7 +901,7 @@ static int64_t arm_load_elf(struct arm_boot_info *info, uint64_t *pentry, } ret = load_elf_as(info->kernel_filename, NULL, NULL, NULL, - pentry, lowaddr, highaddr, big_endian, elf_machine, + pentry, lowaddr, highaddr, NULL, big_endian, elf_machine, 1, data_swab, as); if (ret <= 0) { /* The header loaded but the image didn't */ diff --git a/hw/arm/collie.c b/hw/arm/collie.c index 970a4405cc..4b35ef4bed 100644 --- a/hw/arm/collie.c +++ b/hw/arm/collie.c @@ -10,6 +10,7 @@ */ #include "qemu/osdep.h" #include "qemu/units.h" +#include "qemu/cutils.h" #include "hw/sysbus.h" #include "hw/boards.h" #include "strongarm.h" @@ -18,6 +19,16 @@ #include "exec/address-spaces.h" #include "cpu.h" +typedef struct { + MachineState parent; + + StrongARMState *sa1110; +} CollieMachineState; + +#define TYPE_COLLIE_MACHINE MACHINE_TYPE_NAME("collie") +#define COLLIE_MACHINE(obj) \ + OBJECT_CHECK(CollieMachineState, obj, TYPE_COLLIE_MACHINE) + static struct arm_boot_info collie_binfo = { .loader_start = SA_SDCS0, .ram_size = 0x20000000, @@ -25,15 +36,20 @@ static struct arm_boot_info collie_binfo = { static void collie_init(MachineState *machine) { - StrongARMState *s; DriveInfo *dinfo; - MemoryRegion *sdram = g_new(MemoryRegion, 1); + MachineClass *mc = MACHINE_GET_CLASS(machine); + CollieMachineState *cms = COLLIE_MACHINE(machine); - s = sa1110_init(machine->cpu_type); + if (machine->ram_size != mc->default_ram_size) { + char *sz = size_to_str(mc->default_ram_size); + error_report("Invalid RAM size, should be %s", sz); + g_free(sz); + exit(EXIT_FAILURE); + } - memory_region_allocate_system_memory(sdram, NULL, "strongarm.sdram", - collie_binfo.ram_size); - memory_region_add_subregion(get_system_memory(), SA_SDCS0, sdram); + cms->sa1110 = sa1110_init(machine->cpu_type); + + memory_region_add_subregion(get_system_memory(), SA_SDCS0, machine->ram); dinfo = drive_get(IF_PFLASH, 0, 0); pflash_cfi01_register(SA_CS0, "collie.fl1", 0x02000000, @@ -48,15 +64,30 @@ static void collie_init(MachineState *machine) sysbus_create_simple("scoop", 0x40800000, NULL); collie_binfo.board_id = 0x208; - arm_load_kernel(s->cpu, machine, &collie_binfo); + arm_load_kernel(cms->sa1110->cpu, machine, &collie_binfo); } -static void collie_machine_init(MachineClass *mc) +static void collie_machine_class_init(ObjectClass *oc, void *data) { + MachineClass *mc = MACHINE_CLASS(oc); + mc->desc = "Sharp SL-5500 (Collie) PDA (SA-1110)"; mc->init = collie_init; mc->ignore_memory_transaction_failures = true; mc->default_cpu_type = ARM_CPU_TYPE_NAME("sa1110"); + mc->default_ram_size = 0x20000000; + mc->default_ram_id = "strongarm.sdram"; } -DEFINE_MACHINE("collie", collie_machine_init) +static const TypeInfo collie_machine_typeinfo = { + .name = TYPE_COLLIE_MACHINE, + .parent = TYPE_MACHINE, + .class_init = collie_machine_class_init, + .instance_size = sizeof(CollieMachineState), +}; + +static void collie_machine_register_types(void) +{ + type_register_static(&collie_machine_typeinfo); +} +type_init(collie_machine_register_types); diff --git a/hw/arm/cubieboard.c b/hw/arm/cubieboard.c index 6dc2f1d6b6..0b8ba44976 100644 --- a/hw/arm/cubieboard.c +++ b/hw/arm/cubieboard.c @@ -19,8 +19,10 @@ #include "exec/address-spaces.h" #include "qapi/error.h" #include "cpu.h" +#include "sysemu/sysemu.h" #include "hw/sysbus.h" #include "hw/boards.h" +#include "hw/qdev-properties.h" #include "hw/arm/allwinner-a10.h" static struct arm_boot_info cubieboard_binfo = { @@ -28,62 +30,92 @@ static struct arm_boot_info cubieboard_binfo = { .board_id = 0x1008, }; -typedef struct CubieBoardState { - AwA10State *a10; - MemoryRegion sdram; -} CubieBoardState; - static void cubieboard_init(MachineState *machine) { - CubieBoardState *s = g_new(CubieBoardState, 1); + AwA10State *a10; Error *err = NULL; + DriveInfo *di; + BlockBackend *blk; + BusState *bus; + DeviceState *carddev; - s->a10 = AW_A10(object_new(TYPE_AW_A10)); + /* BIOS is not supported by this board */ + if (bios_name) { + error_report("BIOS not supported for this machine"); + exit(1); + } - object_property_set_int(OBJECT(&s->a10->emac), 1, "phy-addr", &err); + /* This board has fixed size RAM (512MiB or 1GiB) */ + if (machine->ram_size != 512 * MiB && + machine->ram_size != 1 * GiB) { + error_report("This machine can only be used with 512MiB or 1GiB RAM"); + exit(1); + } + + /* Only allow Cortex-A8 for this board */ + if (strcmp(machine->cpu_type, ARM_CPU_TYPE_NAME("cortex-a8")) != 0) { + error_report("This board can only be used with cortex-a8 CPU"); + exit(1); + } + + a10 = AW_A10(object_new(TYPE_AW_A10)); + object_property_add_child(OBJECT(machine), "soc", OBJECT(a10), + &error_abort); + object_unref(OBJECT(a10)); + + object_property_set_int(OBJECT(&a10->emac), 1, "phy-addr", &err); if (err != NULL) { error_reportf_err(err, "Couldn't set phy address: "); exit(1); } - object_property_set_int(OBJECT(&s->a10->timer), 32768, "clk0-freq", &err); + object_property_set_int(OBJECT(&a10->timer), 32768, "clk0-freq", &err); if (err != NULL) { error_reportf_err(err, "Couldn't set clk0 frequency: "); exit(1); } - object_property_set_int(OBJECT(&s->a10->timer), 24000000, "clk1-freq", - &err); + object_property_set_int(OBJECT(&a10->timer), 24000000, "clk1-freq", &err); if (err != NULL) { error_reportf_err(err, "Couldn't set clk1 frequency: "); exit(1); } - object_property_set_bool(OBJECT(s->a10), true, "realized", &err); + object_property_set_bool(OBJECT(a10), true, "realized", &err); if (err != NULL) { error_reportf_err(err, "Couldn't realize Allwinner A10: "); exit(1); } - memory_region_allocate_system_memory(&s->sdram, NULL, "cubieboard.ram", - machine->ram_size); + /* Retrieve SD bus */ + di = drive_get_next(IF_SD); + blk = di ? blk_by_legacy_dinfo(di) : NULL; + bus = qdev_get_child_bus(DEVICE(a10), "sd-bus"); + + /* Plug in SD card */ + carddev = qdev_create(bus, TYPE_SD_CARD); + qdev_prop_set_drive(carddev, "drive", blk, &error_fatal); + object_property_set_bool(OBJECT(carddev), true, "realized", &error_fatal); + memory_region_add_subregion(get_system_memory(), AW_A10_SDRAM_BASE, - &s->sdram); + machine->ram); /* TODO create and connect IDE devices for ide_drive_get() */ cubieboard_binfo.ram_size = machine->ram_size; - arm_load_kernel(&s->a10->cpu, machine, &cubieboard_binfo); + arm_load_kernel(&a10->cpu, machine, &cubieboard_binfo); } static void cubieboard_machine_init(MachineClass *mc) { - mc->desc = "cubietech cubieboard (Cortex-A9)"; - mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-a9"); + mc->desc = "cubietech cubieboard (Cortex-A8)"; + mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-a8"); + mc->default_ram_size = 1 * GiB; mc->init = cubieboard_init; mc->block_default_type = IF_IDE; mc->units_per_default_bus = 1; mc->ignore_memory_transaction_failures = true; + mc->default_ram_id = "cubieboard.ram"; } DEFINE_MACHINE("cubieboard", cubieboard_machine_init) diff --git a/hw/arm/digic_boards.c b/hw/arm/digic_boards.c index ef3fc2b6a5..518a63e61d 100644 --- a/hw/arm/digic_boards.c +++ b/hw/arm/digic_boards.c @@ -35,39 +35,40 @@ #include "hw/loader.h" #include "sysemu/sysemu.h" #include "sysemu/qtest.h" +#include "qemu/units.h" +#include "qemu/cutils.h" #define DIGIC4_ROM0_BASE 0xf0000000 #define DIGIC4_ROM1_BASE 0xf8000000 #define DIGIC4_ROM_MAX_SIZE 0x08000000 -typedef struct DigicBoardState { - DigicState *digic; - MemoryRegion ram; -} DigicBoardState; - typedef struct DigicBoard { - hwaddr ram_size; - void (*add_rom0)(DigicBoardState *, hwaddr, const char *); + void (*add_rom0)(DigicState *, hwaddr, const char *); const char *rom0_def_filename; - void (*add_rom1)(DigicBoardState *, hwaddr, const char *); + void (*add_rom1)(DigicState *, hwaddr, const char *); const char *rom1_def_filename; } DigicBoard; -static void digic4_board_init(DigicBoard *board) +static void digic4_board_init(MachineState *machine, DigicBoard *board) { Error *err = NULL; + DigicState *s = DIGIC(object_new(TYPE_DIGIC)); + MachineClass *mc = MACHINE_GET_CLASS(machine); - DigicBoardState *s = g_new(DigicBoardState, 1); + if (machine->ram_size != mc->default_ram_size) { + char *sz = size_to_str(mc->default_ram_size); + error_report("Invalid RAM size, should be %s", sz); + g_free(sz); + exit(EXIT_FAILURE); + } - s->digic = DIGIC(object_new(TYPE_DIGIC)); - object_property_set_bool(OBJECT(s->digic), true, "realized", &err); + object_property_set_bool(OBJECT(s), true, "realized", &err); if (err != NULL) { error_reportf_err(err, "Couldn't realize DIGIC SoC: "); exit(1); } - memory_region_allocate_system_memory(&s->ram, NULL, "ram", board->ram_size); - memory_region_add_subregion(get_system_memory(), 0, &s->ram); + memory_region_add_subregion(get_system_memory(), 0, machine->ram); if (board->add_rom0) { board->add_rom0(s, DIGIC4_ROM0_BASE, board->rom0_def_filename); @@ -78,7 +79,7 @@ static void digic4_board_init(DigicBoard *board) } } -static void digic_load_rom(DigicBoardState *s, hwaddr addr, +static void digic_load_rom(DigicState *s, hwaddr addr, hwaddr max_size, const char *def_filename) { target_long rom_size; @@ -118,7 +119,7 @@ static void digic_load_rom(DigicBoardState *s, hwaddr addr, * Samsung K8P3215UQB * 64M Bit (4Mx16) Page Mode / Multi-Bank NOR Flash Memory */ -static void digic4_add_k8p3215uqb_rom(DigicBoardState *s, hwaddr addr, +static void digic4_add_k8p3215uqb_rom(DigicState *s, hwaddr addr, const char *def_filename) { #define FLASH_K8P3215UQB_SIZE (4 * 1024 * 1024) @@ -135,14 +136,13 @@ static void digic4_add_k8p3215uqb_rom(DigicBoardState *s, hwaddr addr, } static DigicBoard digic4_board_canon_a1100 = { - .ram_size = 64 * 1024 * 1024, .add_rom1 = digic4_add_k8p3215uqb_rom, .rom1_def_filename = "canon-a1100-rom1.bin", }; static void canon_a1100_init(MachineState *machine) { - digic4_board_init(&digic4_board_canon_a1100); + digic4_board_init(machine, &digic4_board_canon_a1100); } static void canon_a1100_machine_init(MachineClass *mc) @@ -150,6 +150,8 @@ static void canon_a1100_machine_init(MachineClass *mc) mc->desc = "Canon PowerShot A1100 IS"; mc->init = &canon_a1100_init; mc->ignore_memory_transaction_failures = true; + mc->default_ram_size = 64 * MiB; + mc->default_ram_id = "ram"; } DEFINE_MACHINE("canon-a1100", canon_a1100_machine_init) diff --git a/hw/arm/exynos4210.c b/hw/arm/exynos4210.c index 77fbe1baab..1f7253ef6f 100644 --- a/hw/arm/exynos4210.c +++ b/hw/arm/exynos4210.c @@ -166,17 +166,37 @@ static uint64_t exynos4210_calc_affinity(int cpu) return (0x9 << ARM_AFF1_SHIFT) | cpu; } -static void pl330_create(uint32_t base, qemu_irq irq, int nreq) +static DeviceState *pl330_create(uint32_t base, qemu_or_irq *orgate, + qemu_irq irq, int nreq, int nevents, int width) { SysBusDevice *busdev; DeviceState *dev; + int i; dev = qdev_create(NULL, "pl330"); + qdev_prop_set_uint8(dev, "num_events", nevents); + qdev_prop_set_uint8(dev, "num_chnls", 8); qdev_prop_set_uint8(dev, "num_periph_req", nreq); + + qdev_prop_set_uint8(dev, "wr_cap", 4); + qdev_prop_set_uint8(dev, "wr_q_dep", 8); + qdev_prop_set_uint8(dev, "rd_cap", 4); + qdev_prop_set_uint8(dev, "rd_q_dep", 8); + qdev_prop_set_uint8(dev, "data_width", width); + qdev_prop_set_uint16(dev, "data_buffer_dep", width); qdev_init_nofail(dev); busdev = SYS_BUS_DEVICE(dev); sysbus_mmio_map(busdev, 0, base); - sysbus_connect_irq(busdev, 0, irq); + + object_property_set_int(OBJECT(orgate), nevents + 1, "num-lines", + &error_abort); + object_property_set_bool(OBJECT(orgate), true, "realized", &error_abort); + + for (i = 0; i < nevents + 1; i++) { + sysbus_connect_irq(busdev, i, qdev_get_gpio_in(DEVICE(orgate), i)); + } + qdev_connect_gpio_out(DEVICE(orgate), 0, irq); + return dev; } static void exynos4210_realize(DeviceState *socdev, Error **errp) @@ -185,7 +205,7 @@ static void exynos4210_realize(DeviceState *socdev, Error **errp) MemoryRegion *system_mem = get_system_memory(); qemu_irq gate_irq[EXYNOS4210_NCPUS][EXYNOS4210_IRQ_GATE_NINPUTS]; SysBusDevice *busdev; - DeviceState *dev; + DeviceState *dev, *uart[4], *pl330[3]; int i, n; for (n = 0; n < EXYNOS4210_NCPUS; n++) { @@ -285,23 +305,21 @@ static void exynos4210_realize(DeviceState *socdev, Error **errp) /*** Memory ***/ /* Chip-ID and OMR */ - memory_region_init_io(&s->chipid_mem, NULL, &exynos4210_chipid_and_omr_ops, - NULL, "exynos4210.chipid", sizeof(chipid_and_omr)); + memory_region_init_io(&s->chipid_mem, OBJECT(socdev), + &exynos4210_chipid_and_omr_ops, NULL, + "exynos4210.chipid", sizeof(chipid_and_omr)); memory_region_add_subregion(system_mem, EXYNOS4210_CHIPID_ADDR, &s->chipid_mem); /* Internal ROM */ - memory_region_init_ram(&s->irom_mem, NULL, "exynos4210.irom", + memory_region_init_rom(&s->irom_mem, OBJECT(socdev), "exynos4210.irom", EXYNOS4210_IROM_SIZE, &error_fatal); - memory_region_set_readonly(&s->irom_mem, true); memory_region_add_subregion(system_mem, EXYNOS4210_IROM_BASE_ADDR, &s->irom_mem); /* mirror of iROM */ - memory_region_init_alias(&s->irom_alias_mem, NULL, "exynos4210.irom_alias", - &s->irom_mem, - 0, + memory_region_init_alias(&s->irom_alias_mem, OBJECT(socdev), + "exynos4210.irom_alias", &s->irom_mem, 0, EXYNOS4210_IROM_SIZE); - memory_region_set_readonly(&s->irom_alias_mem, true); memory_region_add_subregion(system_mem, EXYNOS4210_IROM_MIRROR_BASE_ADDR, &s->irom_alias_mem); @@ -371,19 +389,19 @@ static void exynos4210_realize(DeviceState *socdev, Error **errp) /*** UARTs ***/ - exynos4210_uart_create(EXYNOS4210_UART0_BASE_ADDR, + uart[0] = exynos4210_uart_create(EXYNOS4210_UART0_BASE_ADDR, EXYNOS4210_UART0_FIFO_SIZE, 0, serial_hd(0), s->irq_table[exynos4210_get_irq(EXYNOS4210_UART_INT_GRP, 0)]); - exynos4210_uart_create(EXYNOS4210_UART1_BASE_ADDR, + uart[1] = exynos4210_uart_create(EXYNOS4210_UART1_BASE_ADDR, EXYNOS4210_UART1_FIFO_SIZE, 1, serial_hd(1), s->irq_table[exynos4210_get_irq(EXYNOS4210_UART_INT_GRP, 1)]); - exynos4210_uart_create(EXYNOS4210_UART2_BASE_ADDR, + uart[2] = exynos4210_uart_create(EXYNOS4210_UART2_BASE_ADDR, EXYNOS4210_UART2_FIFO_SIZE, 2, serial_hd(2), s->irq_table[exynos4210_get_irq(EXYNOS4210_UART_INT_GRP, 2)]); - exynos4210_uart_create(EXYNOS4210_UART3_BASE_ADDR, + uart[3] = exynos4210_uart_create(EXYNOS4210_UART3_BASE_ADDR, EXYNOS4210_UART3_FIFO_SIZE, 3, serial_hd(3), s->irq_table[exynos4210_get_irq(EXYNOS4210_UART_INT_GRP, 3)]); @@ -431,12 +449,42 @@ static void exynos4210_realize(DeviceState *socdev, Error **errp) s->irq_table[exynos4210_get_irq(28, 3)]); /*** DMA controllers ***/ - pl330_create(EXYNOS4210_PL330_BASE0_ADDR, - qemu_irq_invert(s->irq_table[exynos4210_get_irq(35, 1)]), 32); - pl330_create(EXYNOS4210_PL330_BASE1_ADDR, - qemu_irq_invert(s->irq_table[exynos4210_get_irq(36, 1)]), 32); - pl330_create(EXYNOS4210_PL330_BASE2_ADDR, - qemu_irq_invert(s->irq_table[exynos4210_get_irq(34, 1)]), 1); + pl330[0] = pl330_create(EXYNOS4210_PL330_BASE0_ADDR, + &s->pl330_irq_orgate[0], + s->irq_table[exynos4210_get_irq(21, 0)], + 32, 32, 32); + pl330[1] = pl330_create(EXYNOS4210_PL330_BASE1_ADDR, + &s->pl330_irq_orgate[1], + s->irq_table[exynos4210_get_irq(21, 1)], + 32, 32, 32); + pl330[2] = pl330_create(EXYNOS4210_PL330_BASE2_ADDR, + &s->pl330_irq_orgate[2], + s->irq_table[exynos4210_get_irq(20, 1)], + 1, 31, 64); + + sysbus_connect_irq(SYS_BUS_DEVICE(uart[0]), 1, + qdev_get_gpio_in(pl330[0], 15)); + sysbus_connect_irq(SYS_BUS_DEVICE(uart[1]), 1, + qdev_get_gpio_in(pl330[1], 15)); + sysbus_connect_irq(SYS_BUS_DEVICE(uart[2]), 1, + qdev_get_gpio_in(pl330[0], 17)); + sysbus_connect_irq(SYS_BUS_DEVICE(uart[3]), 1, + qdev_get_gpio_in(pl330[1], 17)); +} + +static void exynos4210_init(Object *obj) +{ + Exynos4210State *s = EXYNOS4210_SOC(obj); + int i; + + for (i = 0; i < ARRAY_SIZE(s->pl330_irq_orgate); i++) { + char *name = g_strdup_printf("pl330-irq-orgate%d", i); + qemu_or_irq *orgate = &s->pl330_irq_orgate[i]; + + object_initialize_child(obj, name, orgate, sizeof(*orgate), + TYPE_OR_IRQ, &error_abort, NULL); + g_free(name); + } } static void exynos4210_class_init(ObjectClass *klass, void *data) @@ -450,6 +498,7 @@ static const TypeInfo exynos4210_info = { .name = TYPE_EXYNOS4210_SOC, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(Exynos4210State), + .instance_init = exynos4210_init, .class_init = exynos4210_class_init, }; diff --git a/hw/arm/fsl-imx25.c b/hw/arm/fsl-imx25.c index 3cb5a8fdfd..6f1a82ce3d 100644 --- a/hw/arm/fsl-imx25.c +++ b/hw/arm/fsl-imx25.c @@ -31,6 +31,8 @@ #include "hw/qdev-properties.h" #include "chardev/char.h" +#define IMX25_ESDHC_CAPABILITIES 0x07e20000 + static void fsl_imx25_init(Object *obj) { FslIMX25State *s = FSL_IMX25(obj); @@ -62,6 +64,9 @@ static void fsl_imx25_init(Object *obj) sysbus_init_child_obj(obj, "fec", &s->fec, sizeof(s->fec), TYPE_IMX_FEC); + sysbus_init_child_obj(obj, "rngc", &s->rngc, sizeof(s->rngc), + TYPE_IMX_RNGC); + for (i = 0; i < FSL_IMX25_NUM_I2CS; i++) { sysbus_init_child_obj(obj, "i2c[*]", &s->i2c[i], sizeof(s->i2c[i]), TYPE_IMX_I2C); @@ -71,6 +76,17 @@ static void fsl_imx25_init(Object *obj) sysbus_init_child_obj(obj, "gpio[*]", &s->gpio[i], sizeof(s->gpio[i]), TYPE_IMX_GPIO); } + + for (i = 0; i < FSL_IMX25_NUM_ESDHCS; i++) { + sysbus_init_child_obj(obj, "sdhc[*]", &s->esdhc[i], sizeof(s->esdhc[i]), + TYPE_IMX_USDHC); + } + + for (i = 0; i < FSL_IMX25_NUM_USBS; i++) { + sysbus_init_child_obj(obj, "usb[*]", &s->usb[i], sizeof(s->usb[i]), + TYPE_CHIPIDEA); + } + } static void fsl_imx25_realize(DeviceState *dev, Error **errp) @@ -188,6 +204,14 @@ static void fsl_imx25_realize(DeviceState *dev, Error **errp) sysbus_connect_irq(SYS_BUS_DEVICE(&s->fec), 0, qdev_get_gpio_in(DEVICE(&s->avic), FSL_IMX25_FEC_IRQ)); + object_property_set_bool(OBJECT(&s->rngc), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->rngc), 0, FSL_IMX25_RNGC_ADDR); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->rngc), 0, + qdev_get_gpio_in(DEVICE(&s->avic), FSL_IMX25_RNGC_IRQ)); /* Initialize all I2C */ for (i = 0; i < FSL_IMX25_NUM_I2CS; i++) { @@ -235,17 +259,60 @@ static void fsl_imx25_realize(DeviceState *dev, Error **errp) gpio_table[i].irq)); } + /* Initialize all SDHC */ + for (i = 0; i < FSL_IMX25_NUM_ESDHCS; i++) { + static const struct { + hwaddr addr; + unsigned int irq; + } esdhc_table[FSL_IMX25_NUM_ESDHCS] = { + { FSL_IMX25_ESDHC1_ADDR, FSL_IMX25_ESDHC1_IRQ }, + { FSL_IMX25_ESDHC2_ADDR, FSL_IMX25_ESDHC2_IRQ }, + }; + + object_property_set_uint(OBJECT(&s->esdhc[i]), 2, "sd-spec-version", + &err); + object_property_set_uint(OBJECT(&s->esdhc[i]), IMX25_ESDHC_CAPABILITIES, + "capareg", &err); + object_property_set_bool(OBJECT(&s->esdhc[i]), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->esdhc[i]), 0, esdhc_table[i].addr); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->esdhc[i]), 0, + qdev_get_gpio_in(DEVICE(&s->avic), + esdhc_table[i].irq)); + } + + /* USB */ + for (i = 0; i < FSL_IMX25_NUM_USBS; i++) { + static const struct { + hwaddr addr; + unsigned int irq; + } usb_table[FSL_IMX25_NUM_USBS] = { + { FSL_IMX25_USB1_ADDR, FSL_IMX25_USB1_IRQ }, + { FSL_IMX25_USB2_ADDR, FSL_IMX25_USB2_IRQ }, + }; + + object_property_set_bool(OBJECT(&s->usb[i]), true, "realized", + &error_abort); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->usb[i]), 0, usb_table[i].addr); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->usb[i]), 0, + qdev_get_gpio_in(DEVICE(&s->avic), + usb_table[i].irq)); + } + /* initialize 2 x 16 KB ROM */ - memory_region_init_rom(&s->rom[0], NULL, - "imx25.rom0", FSL_IMX25_ROM0_SIZE, &err); + memory_region_init_rom(&s->rom[0], OBJECT(dev), "imx25.rom0", + FSL_IMX25_ROM0_SIZE, &err); if (err) { error_propagate(errp, err); return; } memory_region_add_subregion(get_system_memory(), FSL_IMX25_ROM0_ADDR, &s->rom[0]); - memory_region_init_rom(&s->rom[1], NULL, - "imx25.rom1", FSL_IMX25_ROM1_SIZE, &err); + memory_region_init_rom(&s->rom[1], OBJECT(dev), "imx25.rom1", + FSL_IMX25_ROM1_SIZE, &err); if (err) { error_propagate(errp, err); return; @@ -264,7 +331,7 @@ static void fsl_imx25_realize(DeviceState *dev, Error **errp) &s->iram); /* internal RAM (128 KB) is aliased over 128 MB - 128 KB */ - memory_region_init_alias(&s->iram_alias, NULL, "imx25.iram_alias", + memory_region_init_alias(&s->iram_alias, OBJECT(dev), "imx25.iram_alias", &s->iram, 0, FSL_IMX25_IRAM_ALIAS_SIZE); memory_region_add_subregion(get_system_memory(), FSL_IMX25_IRAM_ALIAS_ADDR, &s->iram_alias); diff --git a/hw/arm/fsl-imx31.c b/hw/arm/fsl-imx31.c index 55e90d104b..8472d2e911 100644 --- a/hw/arm/fsl-imx31.c +++ b/hw/arm/fsl-imx31.c @@ -206,7 +206,7 @@ static void fsl_imx31_realize(DeviceState *dev, Error **errp) } /* On a real system, the first 16k is a `secure boot rom' */ - memory_region_init_rom(&s->secure_rom, NULL, "imx31.secure_rom", + memory_region_init_rom(&s->secure_rom, OBJECT(dev), "imx31.secure_rom", FSL_IMX31_SECURE_ROM_SIZE, &err); if (err) { error_propagate(errp, err); @@ -216,7 +216,7 @@ static void fsl_imx31_realize(DeviceState *dev, Error **errp) &s->secure_rom); /* There is also a 16k ROM */ - memory_region_init_rom(&s->rom, NULL, "imx31.rom", + memory_region_init_rom(&s->rom, OBJECT(dev), "imx31.rom", FSL_IMX31_ROM_SIZE, &err); if (err) { error_propagate(errp, err); @@ -236,7 +236,7 @@ static void fsl_imx31_realize(DeviceState *dev, Error **errp) &s->iram); /* internal RAM (16 KB) is aliased over 256 MB - 16 KB */ - memory_region_init_alias(&s->iram_alias, NULL, "imx31.iram_alias", + memory_region_init_alias(&s->iram_alias, OBJECT(dev), "imx31.iram_alias", &s->iram, 0, FSL_IMX31_IRAM_ALIAS_SIZE); memory_region_add_subregion(get_system_memory(), FSL_IMX31_IRAM_ALIAS_ADDR, &s->iram_alias); diff --git a/hw/arm/fsl-imx6.c b/hw/arm/fsl-imx6.c index 552145b24e..13f1bf23a6 100644 --- a/hw/arm/fsl-imx6.c +++ b/hw/arm/fsl-imx6.c @@ -22,6 +22,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "hw/arm/fsl-imx6.h" +#include "hw/usb/imx-usb-phy.h" #include "hw/boards.h" #include "hw/qdev-properties.h" #include "sysemu/sysemu.h" @@ -86,11 +87,28 @@ static void fsl_imx6_init(Object *obj) TYPE_IMX_USDHC); } + for (i = 0; i < FSL_IMX6_NUM_USB_PHYS; i++) { + snprintf(name, NAME_SIZE, "usbphy%d", i); + sysbus_init_child_obj(obj, name, &s->usbphy[i], sizeof(s->usbphy[i]), + TYPE_IMX_USBPHY); + } + for (i = 0; i < FSL_IMX6_NUM_USBS; i++) { + snprintf(name, NAME_SIZE, "usb%d", i); + sysbus_init_child_obj(obj, name, &s->usb[i], sizeof(s->usb[i]), + TYPE_CHIPIDEA); + } + for (i = 0; i < FSL_IMX6_NUM_ECSPIS; i++) { snprintf(name, NAME_SIZE, "spi%d", i + 1); sysbus_init_child_obj(obj, name, &s->spi[i], sizeof(s->spi[i]), TYPE_IMX_SPI); } + for (i = 0; i < FSL_IMX6_NUM_WDTS; i++) { + snprintf(name, NAME_SIZE, "wdt%d", i); + sysbus_init_child_obj(obj, name, &s->wdt[i], sizeof(s->wdt[i]), + TYPE_IMX2_WDT); + } + sysbus_init_child_obj(obj, "eth", &s->eth, sizeof(s->eth), TYPE_IMX_ENET); } @@ -343,6 +361,30 @@ static void fsl_imx6_realize(DeviceState *dev, Error **errp) esdhc_table[i].irq)); } + /* USB */ + for (i = 0; i < FSL_IMX6_NUM_USB_PHYS; i++) { + object_property_set_bool(OBJECT(&s->usbphy[i]), true, "realized", + &error_abort); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->usbphy[i]), 0, + FSL_IMX6_USBPHY1_ADDR + i * 0x1000); + } + for (i = 0; i < FSL_IMX6_NUM_USBS; i++) { + static const int FSL_IMX6_USBn_IRQ[] = { + FSL_IMX6_USB_OTG_IRQ, + FSL_IMX6_USB_HOST1_IRQ, + FSL_IMX6_USB_HOST2_IRQ, + FSL_IMX6_USB_HOST3_IRQ, + }; + + object_property_set_bool(OBJECT(&s->usb[i]), true, "realized", + &error_abort); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->usb[i]), 0, + FSL_IMX6_USBOH3_USB_ADDR + i * 0x200); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->usb[i]), 0, + qdev_get_gpio_in(DEVICE(&s->a9mpcore), + FSL_IMX6_USBn_IRQ[i])); + } + /* Initialize all ECSPI */ for (i = 0; i < FSL_IMX6_NUM_ECSPIS; i++) { static const struct { @@ -383,8 +425,23 @@ static void fsl_imx6_realize(DeviceState *dev, Error **errp) qdev_get_gpio_in(DEVICE(&s->a9mpcore), FSL_IMX6_ENET_MAC_1588_IRQ)); + /* + * Watchdog + */ + for (i = 0; i < FSL_IMX6_NUM_WDTS; i++) { + static const hwaddr FSL_IMX6_WDOGn_ADDR[FSL_IMX6_NUM_WDTS] = { + FSL_IMX6_WDOG1_ADDR, + FSL_IMX6_WDOG2_ADDR, + }; + + object_property_set_bool(OBJECT(&s->wdt[i]), true, "realized", + &error_abort); + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->wdt[i]), 0, FSL_IMX6_WDOGn_ADDR[i]); + } + /* ROM memory */ - memory_region_init_rom(&s->rom, NULL, "imx6.rom", + memory_region_init_rom(&s->rom, OBJECT(dev), "imx6.rom", FSL_IMX6_ROM_SIZE, &err); if (err) { error_propagate(errp, err); @@ -394,7 +451,7 @@ static void fsl_imx6_realize(DeviceState *dev, Error **errp) &s->rom); /* CAAM memory */ - memory_region_init_rom(&s->caam, NULL, "imx6.caam", + memory_region_init_rom(&s->caam, OBJECT(dev), "imx6.caam", FSL_IMX6_CAAM_MEM_SIZE, &err); if (err) { error_propagate(errp, err); @@ -414,7 +471,7 @@ static void fsl_imx6_realize(DeviceState *dev, Error **errp) &s->ocram); /* internal OCRAM (256 KB) is aliased over 1 MB */ - memory_region_init_alias(&s->ocram_alias, NULL, "imx6.ocram_alias", + memory_region_init_alias(&s->ocram_alias, OBJECT(dev), "imx6.ocram_alias", &s->ocram, 0, FSL_IMX6_OCRAM_ALIAS_SIZE); memory_region_add_subregion(get_system_memory(), FSL_IMX6_OCRAM_ALIAS_ADDR, &s->ocram_alias); diff --git a/hw/arm/fsl-imx6ul.c b/hw/arm/fsl-imx6ul.c index c405b68d1d..56dfd7cecc 100644 --- a/hw/arm/fsl-imx6ul.c +++ b/hw/arm/fsl-imx6ul.c @@ -20,6 +20,7 @@ #include "qapi/error.h" #include "hw/arm/fsl-imx6ul.h" #include "hw/misc/unimp.h" +#include "hw/usb/imx-usb-phy.h" #include "hw/boards.h" #include "sysemu/sysemu.h" #include "qemu/error-report.h" @@ -133,6 +134,18 @@ static void fsl_imx6ul_init(Object *obj) TYPE_IMX_ENET); } + /* USB */ + for (i = 0; i < FSL_IMX6UL_NUM_USB_PHYS; i++) { + snprintf(name, NAME_SIZE, "usbphy%d", i); + sysbus_init_child_obj(obj, name, &s->usbphy[i], sizeof(s->usbphy[i]), + TYPE_IMX_USBPHY); + } + for (i = 0; i < FSL_IMX6UL_NUM_USBS; i++) { + snprintf(name, NAME_SIZE, "usb%d", i); + sysbus_init_child_obj(obj, name, &s->usb[i], sizeof(s->usb[i]), + TYPE_CHIPIDEA); + } + /* * SDHCI */ @@ -456,6 +469,28 @@ static void fsl_imx6ul_realize(DeviceState *dev, Error **errp) FSL_IMX6UL_ENETn_TIMER_IRQ[i])); } + /* USB */ + for (i = 0; i < FSL_IMX6UL_NUM_USB_PHYS; i++) { + object_property_set_bool(OBJECT(&s->usbphy[i]), true, "realized", + &error_abort); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->usbphy[i]), 0, + FSL_IMX6UL_USBPHY1_ADDR + i * 0x1000); + } + + for (i = 0; i < FSL_IMX6UL_NUM_USBS; i++) { + static const int FSL_IMX6UL_USBn_IRQ[] = { + FSL_IMX6UL_USB1_IRQ, + FSL_IMX6UL_USB2_IRQ, + }; + object_property_set_bool(OBJECT(&s->usb[i]), true, "realized", + &error_abort); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->usb[i]), 0, + FSL_IMX6UL_USBO2_USB_ADDR + i * 0x200); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->usb[i]), 0, + qdev_get_gpio_in(DEVICE(&s->a7mpcore), + FSL_IMX6UL_USBn_IRQ[i])); + } + /* * USDHC */ @@ -516,6 +551,20 @@ static void fsl_imx6ul_realize(DeviceState *dev, Error **errp) */ create_unimplemented_device("sdma", FSL_IMX6UL_SDMA_ADDR, 0x4000); + /* + * PWM + */ + create_unimplemented_device("pwm1", FSL_IMX6UL_PWM1_ADDR, 0x4000); + create_unimplemented_device("pwm2", FSL_IMX6UL_PWM2_ADDR, 0x4000); + create_unimplemented_device("pwm3", FSL_IMX6UL_PWM3_ADDR, 0x4000); + create_unimplemented_device("pwm4", FSL_IMX6UL_PWM4_ADDR, 0x4000); + + /* + * CAN + */ + create_unimplemented_device("can1", FSL_IMX6UL_CAN1_ADDR, 0x4000); + create_unimplemented_device("can2", FSL_IMX6UL_CAN2_ADDR, 0x4000); + /* * APHB_DMA */ @@ -543,7 +592,7 @@ static void fsl_imx6ul_realize(DeviceState *dev, Error **errp) /* * ROM memory */ - memory_region_init_rom(&s->rom, NULL, "imx6ul.rom", + memory_region_init_rom(&s->rom, OBJECT(dev), "imx6ul.rom", FSL_IMX6UL_ROM_SIZE, &error_abort); memory_region_add_subregion(get_system_memory(), FSL_IMX6UL_ROM_ADDR, &s->rom); @@ -551,7 +600,7 @@ static void fsl_imx6ul_realize(DeviceState *dev, Error **errp) /* * CAAM memory */ - memory_region_init_rom(&s->caam, NULL, "imx6ul.caam", + memory_region_init_rom(&s->caam, OBJECT(dev), "imx6ul.caam", FSL_IMX6UL_CAAM_MEM_SIZE, &error_abort); memory_region_add_subregion(get_system_memory(), FSL_IMX6UL_CAAM_MEM_ADDR, &s->caam); @@ -568,8 +617,9 @@ static void fsl_imx6ul_realize(DeviceState *dev, Error **errp) /* * internal OCRAM (128 KB) is aliased over 512 KB */ - memory_region_init_alias(&s->ocram_alias, NULL, "imx6ul.ocram_alias", - &s->ocram, 0, FSL_IMX6UL_OCRAM_ALIAS_SIZE); + memory_region_init_alias(&s->ocram_alias, OBJECT(dev), + "imx6ul.ocram_alias", &s->ocram, 0, + FSL_IMX6UL_OCRAM_ALIAS_SIZE); memory_region_add_subregion(get_system_memory(), FSL_IMX6UL_OCRAM_ALIAS_ADDR, &s->ocram_alias); } diff --git a/hw/arm/gumstix.c b/hw/arm/gumstix.c index f26a0e8010..3a4bc332c4 100644 --- a/hw/arm/gumstix.c +++ b/hw/arm/gumstix.c @@ -51,7 +51,6 @@ static void connex_init(MachineState *machine) { PXA2xxState *cpu; DriveInfo *dinfo; - int be; MemoryRegion *address_space_mem = get_system_memory(); uint32_t connex_rom = 0x01000000; @@ -66,14 +65,9 @@ static void connex_init(MachineState *machine) exit(1); } -#ifdef TARGET_WORDS_BIGENDIAN - be = 1; -#else - be = 0; -#endif if (!pflash_cfi01_register(0x00000000, "connext.rom", connex_rom, dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, - sector_len, 2, 0, 0, 0, 0, be)) { + sector_len, 2, 0, 0, 0, 0, 0)) { error_report("Error registering flash memory"); exit(1); } @@ -87,7 +81,6 @@ static void verdex_init(MachineState *machine) { PXA2xxState *cpu; DriveInfo *dinfo; - int be; MemoryRegion *address_space_mem = get_system_memory(); uint32_t verdex_rom = 0x02000000; @@ -102,14 +95,9 @@ static void verdex_init(MachineState *machine) exit(1); } -#ifdef TARGET_WORDS_BIGENDIAN - be = 1; -#else - be = 0; -#endif if (!pflash_cfi01_register(0x00000000, "verdex.rom", verdex_rom, dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, - sector_len, 2, 0, 0, 0, 0, be)) { + sector_len, 2, 0, 0, 0, 0, 0)) { error_report("Error registering flash memory"); exit(1); } diff --git a/hw/arm/highbank.c b/hw/arm/highbank.c index 518d935fdf..ac9de9411e 100644 --- a/hw/arm/highbank.c +++ b/hw/arm/highbank.c @@ -236,7 +236,6 @@ enum cxmachines { */ static void calxeda_init(MachineState *machine, enum cxmachines machine_id) { - ram_addr_t ram_size = machine->ram_size; DeviceState *dev = NULL; SysBusDevice *busdev; qemu_irq pic[128]; @@ -247,7 +246,6 @@ static void calxeda_init(MachineState *machine, enum cxmachines machine_id) qemu_irq cpu_virq[4]; qemu_irq cpu_vfiq[4]; MemoryRegion *sysram; - MemoryRegion *dram; MemoryRegion *sysmem; char *sysboot_filename; @@ -290,10 +288,8 @@ static void calxeda_init(MachineState *machine, enum cxmachines machine_id) } sysmem = get_system_memory(); - dram = g_new(MemoryRegion, 1); - memory_region_allocate_system_memory(dram, NULL, "highbank.dram", ram_size); /* SDRAM at address zero. */ - memory_region_add_subregion(sysmem, 0, dram); + memory_region_add_subregion(sysmem, 0, machine->ram); sysram = g_new(MemoryRegion, 1); memory_region_init_ram(sysram, NULL, "highbank.sysram", 0x8000, @@ -387,7 +383,7 @@ static void calxeda_init(MachineState *machine, enum cxmachines machine_id) /* TODO create and connect IDE devices for ide_drive_get() */ - highbank_binfo.ram_size = ram_size; + highbank_binfo.ram_size = machine->ram_size; /* highbank requires a dtb in order to boot, and the dtb will override * the board ID. The following value is ignored, so set it to -1 to be * clear that the value is meaningless. @@ -430,6 +426,7 @@ static void highbank_class_init(ObjectClass *oc, void *data) mc->units_per_default_bus = 1; mc->max_cpus = 4; mc->ignore_memory_transaction_failures = true; + mc->default_ram_id = "highbank.dram"; } static const TypeInfo highbank_type = { @@ -448,6 +445,7 @@ static void midway_class_init(ObjectClass *oc, void *data) mc->units_per_default_bus = 1; mc->max_cpus = 4; mc->ignore_memory_transaction_failures = true; + mc->default_ram_id = "highbank.dram"; } static const TypeInfo midway_type = { diff --git a/hw/arm/imx25_pdk.c b/hw/arm/imx25_pdk.c index c76fc2bd94..b3ca82bafa 100644 --- a/hw/arm/imx25_pdk.c +++ b/hw/arm/imx25_pdk.c @@ -26,12 +26,14 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "cpu.h" +#include "hw/qdev-properties.h" #include "hw/arm/fsl-imx25.h" #include "hw/boards.h" #include "qemu/error-report.h" #include "exec/address-spaces.h" #include "sysemu/qtest.h" #include "hw/i2c/i2c.h" +#include "qemu/cutils.h" /* Memory map for PDK Emulation Baseboard: * 0x00000000-0x7fffffff See i.MX25 SOC fr support @@ -58,7 +60,6 @@ typedef struct IMX25PDK { FslIMX25State soc; - MemoryRegion ram; MemoryRegion ram_alias; } IMX25PDK; @@ -66,6 +67,7 @@ static struct arm_boot_info imx25_pdk_binfo; static void imx25_pdk_init(MachineState *machine) { + MachineClass *mc = MACHINE_GET_CLASS(machine); IMX25PDK *s = g_new0(IMX25PDK, 1); unsigned int ram_size; unsigned int alias_offset; @@ -78,16 +80,14 @@ static void imx25_pdk_init(MachineState *machine) /* We need to initialize our memory */ if (machine->ram_size > (FSL_IMX25_SDRAM0_SIZE + FSL_IMX25_SDRAM1_SIZE)) { - warn_report("RAM size " RAM_ADDR_FMT " above max supported, " - "reduced to %x", machine->ram_size, - FSL_IMX25_SDRAM0_SIZE + FSL_IMX25_SDRAM1_SIZE); - machine->ram_size = FSL_IMX25_SDRAM0_SIZE + FSL_IMX25_SDRAM1_SIZE; + char *sz = size_to_str(mc->default_ram_size); + error_report("Invalid RAM size, should be %s", sz); + g_free(sz); + exit(EXIT_FAILURE); } - memory_region_allocate_system_memory(&s->ram, NULL, "imx25.ram", - machine->ram_size); memory_region_add_subregion(get_system_memory(), FSL_IMX25_SDRAM0_ADDR, - &s->ram); + machine->ram); /* initialize the alias memory if any */ for (i = 0, ram_size = machine->ram_size, alias_offset = 0; @@ -107,7 +107,8 @@ static void imx25_pdk_init(MachineState *machine) if (size < ram[i].size) { memory_region_init_alias(&s->ram_alias, NULL, "ram.alias", - &s->ram, alias_offset, ram[i].size - size); + machine->ram, + alias_offset, ram[i].size - size); memory_region_add_subregion(get_system_memory(), ram[i].addr + size, &s->ram_alias); } @@ -120,6 +121,21 @@ static void imx25_pdk_init(MachineState *machine) imx25_pdk_binfo.board_id = 1771, imx25_pdk_binfo.nb_cpus = 1; + for (i = 0; i < FSL_IMX25_NUM_ESDHCS; i++) { + BusState *bus; + DeviceState *carddev; + DriveInfo *di; + BlockBackend *blk; + + di = drive_get_next(IF_SD); + blk = di ? blk_by_legacy_dinfo(di) : NULL; + bus = qdev_get_child_bus(DEVICE(&s->soc.esdhc[i]), "sd-bus"); + carddev = qdev_create(bus, TYPE_SD_CARD); + qdev_prop_set_drive(carddev, "drive", blk, &error_fatal); + object_property_set_bool(OBJECT(carddev), true, + "realized", &error_fatal); + } + /* * We test explicitly for qtest here as it is not done (yet?) in * arm_load_kernel(). Without this the "make check" command would @@ -135,6 +151,7 @@ static void imx25_pdk_machine_init(MachineClass *mc) mc->desc = "ARM i.MX25 PDK board (ARM926)"; mc->init = imx25_pdk_init; mc->ignore_memory_transaction_failures = true; + mc->default_ram_id = "imx25.ram"; } DEFINE_MACHINE("imx25-pdk", imx25_pdk_machine_init) diff --git a/hw/arm/integratorcp.c b/hw/arm/integratorcp.c index 524970840d..6d69010d06 100644 --- a/hw/arm/integratorcp.c +++ b/hw/arm/integratorcp.c @@ -585,7 +585,6 @@ static void integratorcp_init(MachineState *machine) Object *cpuobj; ARMCPU *cpu; MemoryRegion *address_space_mem = get_system_memory(); - MemoryRegion *ram = g_new(MemoryRegion, 1); MemoryRegion *ram_alias = g_new(MemoryRegion, 1); qemu_irq pic[32]; DeviceState *dev, *sic, *icp; @@ -605,14 +604,13 @@ static void integratorcp_init(MachineState *machine) cpu = ARM_CPU(cpuobj); - memory_region_allocate_system_memory(ram, NULL, "integrator.ram", - ram_size); /* ??? On a real system the first 1Mb is mapped as SSRAM or boot flash. */ /* ??? RAM should repeat to fill physical memory space. */ /* SDRAM at address zero*/ - memory_region_add_subregion(address_space_mem, 0, ram); + memory_region_add_subregion(address_space_mem, 0, machine->ram); /* And again at address 0x80000000 */ - memory_region_init_alias(ram_alias, NULL, "ram.alias", ram, 0, ram_size); + memory_region_init_alias(ram_alias, NULL, "ram.alias", machine->ram, + 0, ram_size); memory_region_add_subregion(address_space_mem, 0x80000000, ram_alias); dev = qdev_create(NULL, TYPE_INTEGRATOR_CM); @@ -644,6 +642,7 @@ static void integratorcp_init(MachineState *machine) qdev_get_gpio_in_named(icp, ICP_GPIO_MMC_WPROT, 0)); qdev_connect_gpio_out(dev, 1, qdev_get_gpio_in_named(icp, ICP_GPIO_MMC_CARDIN, 0)); + sysbus_create_varargs("pl041", 0x1d000000, pic[25], NULL); if (nd_table[0].used) smc91c111_init(&nd_table[0], 0xc8000000, pic[27]); @@ -660,6 +659,7 @@ static void integratorcp_machine_init(MachineClass *mc) mc->init = integratorcp_init; mc->ignore_memory_transaction_failures = true; mc->default_cpu_type = ARM_CPU_TYPE_NAME("arm926"); + mc->default_ram_id = "integrator.ram"; } DEFINE_MACHINE("integratorcp", integratorcp_machine_init) @@ -673,7 +673,7 @@ static void core_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->props = core_properties; + device_class_set_props(dc, core_properties); dc->realize = integratorcm_realize; dc->vmsd = &vmstate_integratorcm; } diff --git a/hw/arm/kzm.c b/hw/arm/kzm.c index 1d5ef289d5..34f6bcb491 100644 --- a/hw/arm/kzm.c +++ b/hw/arm/kzm.c @@ -25,6 +25,7 @@ #include "hw/char/serial.h" #include "sysemu/qtest.h" #include "sysemu/sysemu.h" +#include "qemu/cutils.h" /* Memory map for Kzm Emulation Baseboard: * 0x00000000-0x7fffffff See i.MX31 SOC for support @@ -51,7 +52,6 @@ typedef struct IMX31KZM { FslIMX31State soc; - MemoryRegion ram; MemoryRegion ram_alias; } IMX31KZM; @@ -78,16 +78,14 @@ static void kzm_init(MachineState *machine) /* Check the amount of memory is compatible with the SOC */ if (machine->ram_size > (FSL_IMX31_SDRAM0_SIZE + FSL_IMX31_SDRAM1_SIZE)) { - warn_report("RAM size " RAM_ADDR_FMT " above max supported, " - "reduced to %x", machine->ram_size, - FSL_IMX31_SDRAM0_SIZE + FSL_IMX31_SDRAM1_SIZE); - machine->ram_size = FSL_IMX31_SDRAM0_SIZE + FSL_IMX31_SDRAM1_SIZE; + char *sz = size_to_str(FSL_IMX31_SDRAM0_SIZE + FSL_IMX31_SDRAM1_SIZE); + error_report("RAM size more than %s is not supported", sz); + g_free(sz); + exit(EXIT_FAILURE); } - memory_region_allocate_system_memory(&s->ram, NULL, "kzm.ram", - machine->ram_size); memory_region_add_subregion(get_system_memory(), FSL_IMX31_SDRAM0_ADDR, - &s->ram); + machine->ram); /* initialize the alias memory if any */ for (i = 0, ram_size = machine->ram_size, alias_offset = 0; @@ -107,7 +105,8 @@ static void kzm_init(MachineState *machine) if (size < ram[i].size) { memory_region_init_alias(&s->ram_alias, NULL, "ram.alias", - &s->ram, alias_offset, ram[i].size - size); + machine->ram, + alias_offset, ram[i].size - size); memory_region_add_subregion(get_system_memory(), ram[i].addr + size, &s->ram_alias); } @@ -139,6 +138,7 @@ static void kzm_machine_init(MachineClass *mc) mc->desc = "ARM KZM Emulation Baseboard (ARM1136)"; mc->init = kzm_init; mc->ignore_memory_transaction_failures = true; + mc->default_ram_id = "kzm.ram"; } DEFINE_MACHINE("kzm", kzm_machine_init) diff --git a/hw/arm/mainstone.c b/hw/arm/mainstone.c index b01ce3ce08..6bc643651b 100644 --- a/hw/arm/mainstone.c +++ b/hw/arm/mainstone.c @@ -119,39 +119,23 @@ static void mainstone_common_init(MemoryRegion *address_space_mem, DeviceState *mst_irq; DriveInfo *dinfo; int i; - int be; MemoryRegion *rom = g_new(MemoryRegion, 1); /* Setup CPU & memory */ mpu = pxa270_init(address_space_mem, mainstone_binfo.ram_size, machine->cpu_type); - memory_region_init_ram(rom, NULL, "mainstone.rom", MAINSTONE_ROM, + memory_region_init_rom(rom, NULL, "mainstone.rom", MAINSTONE_ROM, &error_fatal); - memory_region_set_readonly(rom, true); memory_region_add_subregion(address_space_mem, 0, rom); -#ifdef TARGET_WORDS_BIGENDIAN - be = 1; -#else - be = 0; -#endif /* There are two 32MiB flash devices on the board */ for (i = 0; i < 2; i ++) { dinfo = drive_get(IF_PFLASH, 0, i); - if (!dinfo) { - if (qtest_enabled()) { - break; - } - error_report("Two flash images must be given with the " - "'pflash' parameter"); - exit(1); - } - if (!pflash_cfi01_register(mainstone_flash_base[i], i ? "mainstone.flash1" : "mainstone.flash0", MAINSTONE_FLASH, - blk_by_legacy_dinfo(dinfo), - sector_len, 4, 0, 0, 0, 0, be)) { + dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, + sector_len, 4, 0, 0, 0, 0, 0)) { error_report("Error registering flash memory"); exit(1); } diff --git a/hw/arm/mcimx6ul-evk.c b/hw/arm/mcimx6ul-evk.c index e90b393a44..23a71ed378 100644 --- a/hw/arm/mcimx6ul-evk.c +++ b/hw/arm/mcimx6ul-evk.c @@ -19,15 +19,10 @@ #include "qemu/error-report.h" #include "sysemu/qtest.h" -typedef struct { - FslIMX6ULState soc; - MemoryRegion ram; -} MCIMX6ULEVK; - static void mcimx6ul_evk_init(MachineState *machine) { static struct arm_boot_info boot_info; - MCIMX6ULEVK *s = g_new0(MCIMX6ULEVK, 1); + FslIMX6ULState *s; int i; if (machine->ram_size > FSL_IMX6UL_MMDC_SIZE) { @@ -43,15 +38,12 @@ static void mcimx6ul_evk_init(MachineState *machine) .nb_cpus = machine->smp.cpus, }; - object_initialize_child(OBJECT(machine), "soc", &s->soc, sizeof(s->soc), - TYPE_FSL_IMX6UL, &error_fatal, NULL); + s = FSL_IMX6UL(object_new(TYPE_FSL_IMX6UL)); + object_property_add_child(OBJECT(machine), "soc", OBJECT(s), &error_fatal); + object_property_set_bool(OBJECT(s), true, "realized", &error_fatal); - object_property_set_bool(OBJECT(&s->soc), true, "realized", &error_fatal); - - memory_region_allocate_system_memory(&s->ram, NULL, "mcimx6ul-evk.ram", - machine->ram_size); - memory_region_add_subregion(get_system_memory(), - FSL_IMX6UL_MMDC_ADDR, &s->ram); + memory_region_add_subregion(get_system_memory(), FSL_IMX6UL_MMDC_ADDR, + machine->ram); for (i = 0; i < FSL_IMX6UL_NUM_USDHCS; i++) { BusState *bus; @@ -61,7 +53,7 @@ static void mcimx6ul_evk_init(MachineState *machine) di = drive_get_next(IF_SD); blk = di ? blk_by_legacy_dinfo(di) : NULL; - bus = qdev_get_child_bus(DEVICE(&s->soc.usdhc[i]), "sd-bus"); + bus = qdev_get_child_bus(DEVICE(&s->usdhc[i]), "sd-bus"); carddev = qdev_create(bus, TYPE_SD_CARD); qdev_prop_set_drive(carddev, "drive", blk, &error_fatal); object_property_set_bool(OBJECT(carddev), true, @@ -69,7 +61,7 @@ static void mcimx6ul_evk_init(MachineState *machine) } if (!qtest_enabled()) { - arm_load_kernel(&s->soc.cpu, machine, &boot_info); + arm_load_kernel(&s->cpu, machine, &boot_info); } } @@ -78,5 +70,6 @@ static void mcimx6ul_evk_machine_init(MachineClass *mc) mc->desc = "Freescale i.MX6UL Evaluation Kit (Cortex A7)"; mc->init = mcimx6ul_evk_init; mc->max_cpus = FSL_IMX6UL_NUM_CPUS; + mc->default_ram_id = "mcimx6ul-evk.ram"; } DEFINE_MACHINE("mcimx6ul-evk", mcimx6ul_evk_machine_init) diff --git a/hw/arm/mcimx7d-sabre.c b/hw/arm/mcimx7d-sabre.c index 0d1f62d30a..de1e264217 100644 --- a/hw/arm/mcimx7d-sabre.c +++ b/hw/arm/mcimx7d-sabre.c @@ -21,15 +21,10 @@ #include "qemu/error-report.h" #include "sysemu/qtest.h" -typedef struct { - FslIMX7State soc; - MemoryRegion ram; -} MCIMX7Sabre; - static void mcimx7d_sabre_init(MachineState *machine) { static struct arm_boot_info boot_info; - MCIMX7Sabre *s = g_new0(MCIMX7Sabre, 1); + FslIMX7State *s; int i; if (machine->ram_size > FSL_IMX7_MMDC_SIZE) { @@ -45,15 +40,12 @@ static void mcimx7d_sabre_init(MachineState *machine) .nb_cpus = machine->smp.cpus, }; - object_initialize_child(OBJECT(machine), "soc", - &s->soc, sizeof(s->soc), - TYPE_FSL_IMX7, &error_fatal, NULL); - object_property_set_bool(OBJECT(&s->soc), true, "realized", &error_fatal); + s = FSL_IMX7(object_new(TYPE_FSL_IMX7)); + object_property_add_child(OBJECT(machine), "soc", OBJECT(s), &error_fatal); + object_property_set_bool(OBJECT(s), true, "realized", &error_fatal); - memory_region_allocate_system_memory(&s->ram, NULL, "mcimx7d-sabre.ram", - machine->ram_size); - memory_region_add_subregion(get_system_memory(), - FSL_IMX7_MMDC_ADDR, &s->ram); + memory_region_add_subregion(get_system_memory(), FSL_IMX7_MMDC_ADDR, + machine->ram); for (i = 0; i < FSL_IMX7_NUM_USDHCS; i++) { BusState *bus; @@ -63,7 +55,7 @@ static void mcimx7d_sabre_init(MachineState *machine) di = drive_get_next(IF_SD); blk = di ? blk_by_legacy_dinfo(di) : NULL; - bus = qdev_get_child_bus(DEVICE(&s->soc.usdhc[i]), "sd-bus"); + bus = qdev_get_child_bus(DEVICE(&s->usdhc[i]), "sd-bus"); carddev = qdev_create(bus, TYPE_SD_CARD); qdev_prop_set_drive(carddev, "drive", blk, &error_fatal); object_property_set_bool(OBJECT(carddev), true, @@ -71,7 +63,7 @@ static void mcimx7d_sabre_init(MachineState *machine) } if (!qtest_enabled()) { - arm_load_kernel(&s->soc.cpu[0], machine, &boot_info); + arm_load_kernel(&s->cpu[0], machine, &boot_info); } } @@ -80,5 +72,6 @@ static void mcimx7d_sabre_machine_init(MachineClass *mc) mc->desc = "Freescale i.MX7 DUAL SABRE (Cortex A7)"; mc->init = mcimx7d_sabre_init; mc->max_cpus = FSL_IMX7_NUM_CPUS; + mc->default_ram_id = "mcimx7d-sabre.ram"; } DEFINE_MACHINE("mcimx7d-sabre", mcimx7d_sabre_machine_init) diff --git a/hw/arm/mps2-tz.c b/hw/arm/mps2-tz.c index f8b620bcc6..a8dea7dde1 100644 --- a/hw/arm/mps2-tz.c +++ b/hw/arm/mps2-tz.c @@ -39,6 +39,7 @@ #include "qemu/osdep.h" #include "qemu/units.h" +#include "qemu/cutils.h" #include "qapi/error.h" #include "qemu/error-report.h" #include "hw/arm/boot.h" @@ -79,7 +80,6 @@ typedef struct { MachineState parent; ARMSSE iotkit; - MemoryRegion psram; MemoryRegion ssram[3]; MemoryRegion ssram1_m; MPS2SCC scc; @@ -388,6 +388,13 @@ static void mps2tz_common_init(MachineState *machine) exit(1); } + if (machine->ram_size != mc->default_ram_size) { + char *sz = size_to_str(mc->default_ram_size); + error_report("Invalid RAM size, should be %s", sz); + g_free(sz); + exit(EXIT_FAILURE); + } + sysbus_init_child_obj(OBJECT(machine), "iotkit", &mms->iotkit, sizeof(mms->iotkit), mmc->armsse_type); iotkitdev = DEVICE(&mms->iotkit); @@ -458,9 +465,7 @@ static void mps2tz_common_init(MachineState *machine) * tradeoffs. For QEMU they're all just RAM, though. We arbitrarily * call the 16MB our "system memory", as it's the largest lump. */ - memory_region_allocate_system_memory(&mms->psram, - NULL, "mps.ram", 16 * MiB); - memory_region_add_subregion(system_memory, 0x80000000, &mms->psram); + memory_region_add_subregion(system_memory, 0x80000000, machine->ram); /* The overflow IRQs for all UARTs are ORed together. * Tx, Rx and "combined" IRQs are sent to the NVIC separately. @@ -642,6 +647,8 @@ static void mps2tz_class_init(ObjectClass *oc, void *data) mc->init = mps2tz_common_init; iic->check = mps2_tz_idau_check; + mc->default_ram_size = 16 * MiB; + mc->default_ram_id = "mps.ram"; } static void mps2tz_an505_class_init(ObjectClass *oc, void *data) diff --git a/hw/arm/mps2.c b/hw/arm/mps2.c index d002b126d3..f246213206 100644 --- a/hw/arm/mps2.c +++ b/hw/arm/mps2.c @@ -24,6 +24,7 @@ #include "qemu/osdep.h" #include "qemu/units.h" +#include "qemu/cutils.h" #include "qapi/error.h" #include "qemu/error-report.h" #include "hw/arm/boot.h" @@ -55,7 +56,6 @@ typedef struct { MachineState parent; ARMv7MState armv7m; - MemoryRegion psram; MemoryRegion ssram1; MemoryRegion ssram1_m; MemoryRegion ssram23; @@ -118,6 +118,13 @@ static void mps2_common_init(MachineState *machine) exit(1); } + if (machine->ram_size != mc->default_ram_size) { + char *sz = size_to_str(mc->default_ram_size); + error_report("Invalid RAM size, should be %s", sz); + g_free(sz); + exit(EXIT_FAILURE); + } + /* The FPGA images have an odd combination of different RAMs, * because in hardware they are different implementations and * connected to different buses, giving varying performance/size @@ -146,9 +153,7 @@ static void mps2_common_init(MachineState *machine) * This is of no use for QEMU so we don't implement it (as if * zbt_boot_ctrl is always zero). */ - memory_region_allocate_system_memory(&mms->psram, - NULL, "mps.ram", 16 * MiB); - memory_region_add_subregion(system_memory, 0x21000000, &mms->psram); + memory_region_add_subregion(system_memory, 0x21000000, machine->ram); switch (mmc->fpga_type) { case FPGA_AN385: @@ -338,6 +343,8 @@ static void mps2_class_init(ObjectClass *oc, void *data) mc->init = mps2_common_init; mc->max_cpus = 1; + mc->default_ram_size = 16 * MiB; + mc->default_ram_id = "mps.ram"; } static void mps2_an385_class_init(ObjectClass *oc, void *data) diff --git a/hw/arm/msf2-soc.c b/hw/arm/msf2-soc.c index 008fd9327a..588d643b8d 100644 --- a/hw/arm/msf2-soc.c +++ b/hw/arm/msf2-soc.c @@ -96,7 +96,7 @@ static void m2sxxx_soc_realize(DeviceState *dev_soc, Error **errp) MemoryRegion *nvm_alias = g_new(MemoryRegion, 1); MemoryRegion *sram = g_new(MemoryRegion, 1); - memory_region_init_rom(nvm, NULL, "MSF2.eNVM", s->envm_size, + memory_region_init_rom(nvm, OBJECT(dev_soc), "MSF2.eNVM", s->envm_size, &error_fatal); /* * On power-on, the eNVM region 0x60000000 is automatically @@ -104,8 +104,8 @@ static void m2sxxx_soc_realize(DeviceState *dev_soc, Error **errp) * start address (0x0). We do not support remapping other eNVM, * eSRAM and DDR regions by guest(via Sysreg) currently. */ - memory_region_init_alias(nvm_alias, NULL, "MSF2.eNVM", - nvm, 0, s->envm_size); + memory_region_init_alias(nvm_alias, OBJECT(dev_soc), "MSF2.eNVM", nvm, 0, + s->envm_size); memory_region_add_subregion(system_memory, ENVM_BASE_ADDRESS, nvm); memory_region_add_subregion(system_memory, 0, nvm_alias); @@ -229,7 +229,7 @@ static void m2sxxx_soc_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = m2sxxx_soc_realize; - dc->props = m2sxxx_soc_properties; + device_class_set_props(dc, m2sxxx_soc_properties); } static const TypeInfo m2sxxx_soc_info = { diff --git a/hw/arm/musicpal.c b/hw/arm/musicpal.c index f68a399a98..b2d0cfdac8 100644 --- a/hw/arm/musicpal.c +++ b/hw/arm/musicpal.c @@ -32,6 +32,7 @@ #include "sysemu/runstate.h" #include "exec/address-spaces.h" #include "ui/pixel_ops.h" +#include "qemu/cutils.h" #define MP_MISC_BASE 0x80002000 #define MP_MISC_SIZE 0x00001000 @@ -435,7 +436,7 @@ static void mv88w8618_eth_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->vmsd = &mv88w8618_eth_vmsd; - dc->props = mv88w8618_eth_properties; + device_class_set_props(dc, mv88w8618_eth_properties); dc->realize = mv88w8618_eth_realize; } @@ -1589,16 +1590,21 @@ static void musicpal_init(MachineState *machine) int i; unsigned long flash_size; DriveInfo *dinfo; + MachineClass *mc = MACHINE_GET_CLASS(machine); MemoryRegion *address_space_mem = get_system_memory(); - MemoryRegion *ram = g_new(MemoryRegion, 1); MemoryRegion *sram = g_new(MemoryRegion, 1); + /* For now we use a fixed - the original - RAM size */ + if (machine->ram_size != mc->default_ram_size) { + char *sz = size_to_str(mc->default_ram_size); + error_report("Invalid RAM size, should be %s", sz); + g_free(sz); + exit(EXIT_FAILURE); + } + cpu = ARM_CPU(cpu_create(machine->cpu_type)); - /* For now we use a fixed - the original - RAM size */ - memory_region_allocate_system_memory(ram, NULL, "musicpal.ram", - MP_RAM_DEFAULT_SIZE); - memory_region_add_subregion(address_space_mem, 0, ram); + memory_region_add_subregion(address_space_mem, 0, machine->ram); memory_region_init_ram(sram, NULL, "musicpal.sram", MP_SRAM_SIZE, &error_fatal); @@ -1639,22 +1645,12 @@ static void musicpal_init(MachineState *machine) * 0xFF800000 (if there is 8 MB flash). So remap flash access if the * image is smaller than 32 MB. */ -#ifdef TARGET_WORDS_BIGENDIAN - pflash_cfi02_register(0x100000000ULL - MP_FLASH_SIZE_MAX, - "musicpal.flash", flash_size, - blk, 0x10000, - MP_FLASH_SIZE_MAX / flash_size, - 2, 0x00BF, 0x236D, 0x0000, 0x0000, - 0x5555, 0x2AAA, 1); -#else pflash_cfi02_register(0x100000000ULL - MP_FLASH_SIZE_MAX, "musicpal.flash", flash_size, blk, 0x10000, MP_FLASH_SIZE_MAX / flash_size, 2, 0x00BF, 0x236D, 0x0000, 0x0000, 0x5555, 0x2AAA, 0); -#endif - } sysbus_create_simple(TYPE_MV88W8618_FLASHCFG, MP_FLASHCFG_BASE, NULL); @@ -1714,6 +1710,8 @@ static void musicpal_machine_init(MachineClass *mc) mc->init = musicpal_init; mc->ignore_memory_transaction_failures = true; mc->default_cpu_type = ARM_CPU_TYPE_NAME("arm926"); + mc->default_ram_size = MP_RAM_DEFAULT_SIZE; + mc->default_ram_id = "musicpal.ram"; } DEFINE_MACHINE("musicpal", musicpal_machine_init) diff --git a/hw/arm/netduinoplus2.c b/hw/arm/netduinoplus2.c new file mode 100644 index 0000000000..e5e247edbe --- /dev/null +++ b/hw/arm/netduinoplus2.c @@ -0,0 +1,52 @@ +/* + * Netduino Plus 2 Machine Model + * + * Copyright (c) 2014 Alistair Francis + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/boards.h" +#include "hw/qdev-properties.h" +#include "qemu/error-report.h" +#include "hw/arm/stm32f405_soc.h" +#include "hw/arm/boot.h" + +static void netduinoplus2_init(MachineState *machine) +{ + DeviceState *dev; + + dev = qdev_create(NULL, TYPE_STM32F405_SOC); + qdev_prop_set_string(dev, "cpu-type", ARM_CPU_TYPE_NAME("cortex-m4")); + object_property_set_bool(OBJECT(dev), true, "realized", &error_fatal); + + armv7m_load_kernel(ARM_CPU(first_cpu), + machine->kernel_filename, + FLASH_SIZE); +} + +static void netduinoplus2_machine_init(MachineClass *mc) +{ + mc->desc = "Netduino Plus 2 Machine"; + mc->init = netduinoplus2_init; +} + +DEFINE_MACHINE("netduinoplus2", netduinoplus2_machine_init) diff --git a/hw/arm/nrf51_soc.c b/hw/arm/nrf51_soc.c index 74029169d0..57eff63f0d 100644 --- a/hw/arm/nrf51_soc.c +++ b/hw/arm/nrf51_soc.c @@ -165,7 +165,7 @@ static void nrf51_soc_realize(DeviceState *dev_soc, Error **errp) } /* STUB Peripherals */ - memory_region_init_io(&s->clock, NULL, &clock_ops, NULL, + memory_region_init_io(&s->clock, OBJECT(dev_soc), &clock_ops, NULL, "nrf51_soc.clock", 0x1000); memory_region_add_subregion_overlap(&s->container, NRF51_IOMEM_BASE, &s->clock, -1); @@ -224,7 +224,7 @@ static void nrf51_soc_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = nrf51_soc_realize; - dc->props = nrf51_soc_properties; + device_class_set_props(dc, nrf51_soc_properties); } static const TypeInfo nrf51_soc_info = { diff --git a/hw/arm/nseries.c b/hw/arm/nseries.c index 7e361936a9..eae800b5c1 100644 --- a/hw/arm/nseries.c +++ b/hw/arm/nseries.c @@ -21,6 +21,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "cpu.h" +#include "chardev/char.h" #include "qemu/cutils.h" #include "qemu/bswap.h" #include "sysemu/reset.h" @@ -39,7 +40,6 @@ #include "hw/qdev-properties.h" #include "hw/block/flash.h" #include "hw/hw.h" -#include "hw/bt.h" #include "hw/loader.h" #include "hw/sysbus.h" #include "qemu/log.h" @@ -47,7 +47,6 @@ /* Nokia N8x0 support */ struct n800_s { - MemoryRegion sdram; struct omap_mpu_state_s *mpu; struct rfbi_chip_s blizzard; @@ -792,13 +791,11 @@ static void n8x0_cbus_setup(struct n800_s *s) static void n8x0_uart_setup(struct n800_s *s) { - Chardev *radio = uart_hci_init(); - - qdev_connect_gpio_out(s->mpu->gpio, N8X0_BT_RESET_GPIO, - csrhci_pins_get(radio)[csrhci_pin_reset]); - qdev_connect_gpio_out(s->mpu->gpio, N8X0_BT_WKUP_GPIO, - csrhci_pins_get(radio)[csrhci_pin_wakeup]); - + Chardev *radio = qemu_chr_new("bt-dummy-uart", "null", NULL); + /* + * Note: We used to connect N8X0_BT_RESET_GPIO and N8X0_BT_WKUP_GPIO + * here, but this code has been removed with the bluetooth backend. + */ omap_uart_attach(s->mpu->uart[BT_UART], radio); } @@ -1137,7 +1134,7 @@ static struct omap_partition_info_s { { 0, 0, 0, NULL } }; -static bdaddr_t n8x0_bd_addr = {{ N8X0_BD_ADDR }}; +static uint8_t n8x0_bd_addr[6] = { N8X0_BD_ADDR }; static int n8x0_atag_setup(void *p, int model) { @@ -1313,13 +1310,19 @@ static void n8x0_init(MachineState *machine, struct arm_boot_info *binfo, int model) { struct n800_s *s = (struct n800_s *) g_malloc0(sizeof(*s)); - uint64_t sdram_size = binfo->ram_size; + MachineClass *mc = MACHINE_GET_CLASS(machine); - memory_region_allocate_system_memory(&s->sdram, NULL, "omap2.dram", - sdram_size); - memory_region_add_subregion(get_system_memory(), OMAP2_Q2_BASE, &s->sdram); + if (machine->ram_size != mc->default_ram_size) { + char *sz = size_to_str(mc->default_ram_size); + error_report("Invalid RAM size, should be %s", sz); + g_free(sz); + exit(EXIT_FAILURE); + } - s->mpu = omap2420_mpu_init(&s->sdram, machine->cpu_type); + memory_region_add_subregion(get_system_memory(), OMAP2_Q2_BASE, + machine->ram); + + s->mpu = omap2420_mpu_init(machine->ram, machine->cpu_type); /* Setup peripherals * @@ -1385,9 +1388,8 @@ static void n8x0_init(MachineState *machine, * * The code above is for loading the `zImage' file from Nokia * images. */ - load_image_targphys(option_rom[0].name, - OMAP2_Q2_BASE + 0x400000, - sdram_size - 0x400000); + load_image_targphys(option_rom[0].name, OMAP2_Q2_BASE + 0x400000, + machine->ram_size - 0x400000); n800_setup_nolo_tags(nolo_tags); cpu_physical_memory_write(OMAP2_SRAM_BASE, nolo_tags, 0x10000); @@ -1397,16 +1399,12 @@ static void n8x0_init(MachineState *machine, static struct arm_boot_info n800_binfo = { .loader_start = OMAP2_Q2_BASE, - /* Actually two chips of 0x4000000 bytes each */ - .ram_size = 0x08000000, .board_id = 0x4f7, .atag_board = n800_atag_setup, }; static struct arm_boot_info n810_binfo = { .loader_start = OMAP2_Q2_BASE, - /* Actually two chips of 0x4000000 bytes each */ - .ram_size = 0x08000000, /* 0x60c and 0x6bf (WiMAX Edition) have been assigned but are not * used by some older versions of the bootloader and 5555 is used * instead (including versions that shipped with many devices). */ @@ -1433,6 +1431,9 @@ static void n800_class_init(ObjectClass *oc, void *data) mc->default_boot_order = ""; mc->ignore_memory_transaction_failures = true; mc->default_cpu_type = ARM_CPU_TYPE_NAME("arm1136-r2"); + /* Actually two chips of 0x4000000 bytes each */ + mc->default_ram_size = 0x08000000; + mc->default_ram_id = "omap2.dram"; } static const TypeInfo n800_type = { @@ -1450,6 +1451,9 @@ static void n810_class_init(ObjectClass *oc, void *data) mc->default_boot_order = ""; mc->ignore_memory_transaction_failures = true; mc->default_cpu_type = ARM_CPU_TYPE_NAME("arm1136-r2"); + /* Actually two chips of 0x4000000 bytes each */ + mc->default_ram_size = 0x08000000; + mc->default_ram_id = "omap2.dram"; } static const TypeInfo n810_type = { diff --git a/hw/arm/omap1.c b/hw/arm/omap1.c index 6ce038a453..761cc17ea9 100644 --- a/hw/arm/omap1.c +++ b/hw/arm/omap1.c @@ -3889,7 +3889,7 @@ struct omap_mpu_state_s *omap310_mpu_init(MemoryRegion *dram, s->ih[0] = qdev_create(NULL, "omap-intc"); qdev_prop_set_uint32(s->ih[0], "size", 0x100); - qdev_prop_set_ptr(s->ih[0], "clk", omap_findclk(s, "arminth_ck")); + omap_intc_set_iclk(OMAP_INTC(s->ih[0]), omap_findclk(s, "arminth_ck")); qdev_init_nofail(s->ih[0]); busdev = SYS_BUS_DEVICE(s->ih[0]); sysbus_connect_irq(busdev, 0, @@ -3899,7 +3899,7 @@ struct omap_mpu_state_s *omap310_mpu_init(MemoryRegion *dram, sysbus_mmio_map(busdev, 0, 0xfffecb00); s->ih[1] = qdev_create(NULL, "omap-intc"); qdev_prop_set_uint32(s->ih[1], "size", 0x800); - qdev_prop_set_ptr(s->ih[1], "clk", omap_findclk(s, "arminth_ck")); + omap_intc_set_iclk(OMAP_INTC(s->ih[1]), omap_findclk(s, "arminth_ck")); qdev_init_nofail(s->ih[1]); busdev = SYS_BUS_DEVICE(s->ih[1]); sysbus_connect_irq(busdev, 0, @@ -4012,7 +4012,7 @@ struct omap_mpu_state_s *omap310_mpu_init(MemoryRegion *dram, s->gpio = qdev_create(NULL, "omap-gpio"); qdev_prop_set_int32(s->gpio, "mpu_model", s->mpu_model); - qdev_prop_set_ptr(s->gpio, "clk", omap_findclk(s, "arm_gpio_ck")); + omap_gpio_set_clk(OMAP1_GPIO(s->gpio), omap_findclk(s, "arm_gpio_ck")); qdev_init_nofail(s->gpio); sysbus_connect_irq(SYS_BUS_DEVICE(s->gpio), 0, qdev_get_gpio_in(s->ih[0], OMAP_INT_GPIO_BANK1)); @@ -4030,7 +4030,7 @@ struct omap_mpu_state_s *omap310_mpu_init(MemoryRegion *dram, s->i2c[0] = qdev_create(NULL, "omap_i2c"); qdev_prop_set_uint8(s->i2c[0], "revision", 0x11); - qdev_prop_set_ptr(s->i2c[0], "fclk", omap_findclk(s, "mpuper_ck")); + omap_i2c_set_fclk(OMAP_I2C(s->i2c[0]), omap_findclk(s, "mpuper_ck")); qdev_init_nofail(s->i2c[0]); busdev = SYS_BUS_DEVICE(s->i2c[0]); sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(s->ih[1], OMAP_INT_I2C)); diff --git a/hw/arm/omap2.c b/hw/arm/omap2.c index 457f152bac..e1c11de5ce 100644 --- a/hw/arm/omap2.c +++ b/hw/arm/omap2.c @@ -2308,8 +2308,8 @@ struct omap_mpu_state_s *omap2420_mpu_init(MemoryRegion *sdram, /* Actually mapped at any 2K boundary in the ARM11 private-peripheral if */ s->ih[0] = qdev_create(NULL, "omap2-intc"); qdev_prop_set_uint8(s->ih[0], "revision", 0x21); - qdev_prop_set_ptr(s->ih[0], "fclk", omap_findclk(s, "mpu_intc_fclk")); - qdev_prop_set_ptr(s->ih[0], "iclk", omap_findclk(s, "mpu_intc_iclk")); + omap_intc_set_fclk(OMAP_INTC(s->ih[0]), omap_findclk(s, "mpu_intc_fclk")); + omap_intc_set_iclk(OMAP_INTC(s->ih[0]), omap_findclk(s, "mpu_intc_iclk")); qdev_init_nofail(s->ih[0]); busdev = SYS_BUS_DEVICE(s->ih[0]); sysbus_connect_irq(busdev, 0, @@ -2425,8 +2425,8 @@ struct omap_mpu_state_s *omap2420_mpu_init(MemoryRegion *sdram, s->i2c[0] = qdev_create(NULL, "omap_i2c"); qdev_prop_set_uint8(s->i2c[0], "revision", 0x34); - qdev_prop_set_ptr(s->i2c[0], "iclk", omap_findclk(s, "i2c1.iclk")); - qdev_prop_set_ptr(s->i2c[0], "fclk", omap_findclk(s, "i2c1.fclk")); + omap_i2c_set_iclk(OMAP_I2C(s->i2c[0]), omap_findclk(s, "i2c1.iclk")); + omap_i2c_set_fclk(OMAP_I2C(s->i2c[0]), omap_findclk(s, "i2c1.fclk")); qdev_init_nofail(s->i2c[0]); busdev = SYS_BUS_DEVICE(s->i2c[0]); sysbus_connect_irq(busdev, 0, @@ -2437,8 +2437,8 @@ struct omap_mpu_state_s *omap2420_mpu_init(MemoryRegion *sdram, s->i2c[1] = qdev_create(NULL, "omap_i2c"); qdev_prop_set_uint8(s->i2c[1], "revision", 0x34); - qdev_prop_set_ptr(s->i2c[1], "iclk", omap_findclk(s, "i2c2.iclk")); - qdev_prop_set_ptr(s->i2c[1], "fclk", omap_findclk(s, "i2c2.fclk")); + omap_i2c_set_iclk(OMAP_I2C(s->i2c[1]), omap_findclk(s, "i2c2.iclk")); + omap_i2c_set_fclk(OMAP_I2C(s->i2c[1]), omap_findclk(s, "i2c2.fclk")); qdev_init_nofail(s->i2c[1]); busdev = SYS_BUS_DEVICE(s->i2c[1]); sysbus_connect_irq(busdev, 0, @@ -2449,13 +2449,14 @@ struct omap_mpu_state_s *omap2420_mpu_init(MemoryRegion *sdram, s->gpio = qdev_create(NULL, "omap2-gpio"); qdev_prop_set_int32(s->gpio, "mpu_model", s->mpu_model); - qdev_prop_set_ptr(s->gpio, "iclk", omap_findclk(s, "gpio_iclk")); - qdev_prop_set_ptr(s->gpio, "fclk0", omap_findclk(s, "gpio1_dbclk")); - qdev_prop_set_ptr(s->gpio, "fclk1", omap_findclk(s, "gpio2_dbclk")); - qdev_prop_set_ptr(s->gpio, "fclk2", omap_findclk(s, "gpio3_dbclk")); - qdev_prop_set_ptr(s->gpio, "fclk3", omap_findclk(s, "gpio4_dbclk")); + omap2_gpio_set_iclk(OMAP2_GPIO(s->gpio), omap_findclk(s, "gpio_iclk")); + omap2_gpio_set_fclk(OMAP2_GPIO(s->gpio), 0, omap_findclk(s, "gpio1_dbclk")); + omap2_gpio_set_fclk(OMAP2_GPIO(s->gpio), 1, omap_findclk(s, "gpio2_dbclk")); + omap2_gpio_set_fclk(OMAP2_GPIO(s->gpio), 2, omap_findclk(s, "gpio3_dbclk")); + omap2_gpio_set_fclk(OMAP2_GPIO(s->gpio), 3, omap_findclk(s, "gpio4_dbclk")); if (s->mpu_model == omap2430) { - qdev_prop_set_ptr(s->gpio, "fclk4", omap_findclk(s, "gpio5_dbclk")); + omap2_gpio_set_fclk(OMAP2_GPIO(s->gpio), 4, + omap_findclk(s, "gpio5_dbclk")); } qdev_init_nofail(s->gpio); busdev = SYS_BUS_DEVICE(s->gpio); diff --git a/hw/arm/omap_sx1.c b/hw/arm/omap_sx1.c index be245714db..57829b3744 100644 --- a/hw/arm/omap_sx1.c +++ b/hw/arm/omap_sx1.c @@ -35,6 +35,7 @@ #include "sysemu/qtest.h" #include "exec/address-spaces.h" #include "cpu.h" +#include "qemu/cutils.h" /*****************************************************************************/ /* Siemens SX1 Cellphone V1 */ @@ -102,8 +103,8 @@ static struct arm_boot_info sx1_binfo = { static void sx1_init(MachineState *machine, const int version) { struct omap_mpu_state_s *mpu; + MachineClass *mc = MACHINE_GET_CLASS(machine); MemoryRegion *address_space = get_system_memory(); - MemoryRegion *dram = g_new(MemoryRegion, 1); MemoryRegion *flash = g_new(MemoryRegion, 1); MemoryRegion *cs = g_new(MemoryRegion, 4); static uint32_t cs0val = 0x00213090; @@ -113,22 +114,25 @@ static void sx1_init(MachineState *machine, const int version) DriveInfo *dinfo; int fl_idx; uint32_t flash_size = flash0_size; - int be; + + if (machine->ram_size != mc->default_ram_size) { + char *sz = size_to_str(mc->default_ram_size); + error_report("Invalid RAM size, should be %s", sz); + g_free(sz); + exit(EXIT_FAILURE); + } if (version == 2) { flash_size = flash2_size; } - memory_region_allocate_system_memory(dram, NULL, "omap1.dram", - sx1_binfo.ram_size); - memory_region_add_subregion(address_space, OMAP_EMIFF_BASE, dram); + memory_region_add_subregion(address_space, OMAP_EMIFF_BASE, machine->ram); - mpu = omap310_mpu_init(dram, machine->cpu_type); + mpu = omap310_mpu_init(machine->ram, machine->cpu_type); /* External Flash (EMIFS) */ - memory_region_init_ram(flash, NULL, "omap_sx1.flash0-0", flash_size, + memory_region_init_rom(flash, NULL, "omap_sx1.flash0-0", flash_size, &error_fatal); - memory_region_set_readonly(flash, true); memory_region_add_subregion(address_space, OMAP_CS0_BASE, flash); memory_region_init_io(&cs[0], NULL, &static_ops, &cs0val, @@ -148,17 +152,11 @@ static void sx1_init(MachineState *machine, const int version) OMAP_CS2_BASE, &cs[3]); fl_idx = 0; -#ifdef TARGET_WORDS_BIGENDIAN - be = 1; -#else - be = 0; -#endif - if ((dinfo = drive_get(IF_PFLASH, 0, fl_idx)) != NULL) { if (!pflash_cfi01_register(OMAP_CS0_BASE, "omap_sx1.flash0-1", flash_size, blk_by_legacy_dinfo(dinfo), - sector_size, 4, 0, 0, 0, 0, be)) { + sector_size, 4, 0, 0, 0, 0, 0)) { fprintf(stderr, "qemu: Error registering flash memory %d.\n", fl_idx); } @@ -168,9 +166,8 @@ static void sx1_init(MachineState *machine, const int version) if ((version == 1) && (dinfo = drive_get(IF_PFLASH, 0, fl_idx)) != NULL) { MemoryRegion *flash_1 = g_new(MemoryRegion, 1); - memory_region_init_ram(flash_1, NULL, "omap_sx1.flash1-0", + memory_region_init_rom(flash_1, NULL, "omap_sx1.flash1-0", flash1_size, &error_fatal); - memory_region_set_readonly(flash_1, true); memory_region_add_subregion(address_space, OMAP_CS1_BASE, flash_1); memory_region_init_io(&cs[1], NULL, &static_ops, &cs1val, @@ -181,7 +178,7 @@ static void sx1_init(MachineState *machine, const int version) if (!pflash_cfi01_register(OMAP_CS1_BASE, "omap_sx1.flash1-1", flash1_size, blk_by_legacy_dinfo(dinfo), - sector_size, 4, 0, 0, 0, 0, be)) { + sector_size, 4, 0, 0, 0, 0, 0)) { fprintf(stderr, "qemu: Error registering flash memory %d.\n", fl_idx); } @@ -223,6 +220,8 @@ static void sx1_machine_v2_class_init(ObjectClass *oc, void *data) mc->init = sx1_init_v2; mc->ignore_memory_transaction_failures = true; mc->default_cpu_type = ARM_CPU_TYPE_NAME("ti925t"); + mc->default_ram_size = sdram_size; + mc->default_ram_id = "omap1.dram"; } static const TypeInfo sx1_machine_v2_type = { @@ -239,6 +238,8 @@ static void sx1_machine_v1_class_init(ObjectClass *oc, void *data) mc->init = sx1_init_v1; mc->ignore_memory_transaction_failures = true; mc->default_cpu_type = ARM_CPU_TYPE_NAME("ti925t"); + mc->default_ram_size = sdram_size; + mc->default_ram_id = "omap1.dram"; } static const TypeInfo sx1_machine_v1_type = { diff --git a/hw/arm/orangepi.c b/hw/arm/orangepi.c new file mode 100644 index 0000000000..a9f64c5680 --- /dev/null +++ b/hw/arm/orangepi.c @@ -0,0 +1,130 @@ +/* + * Orange Pi emulation + * + * Copyright (C) 2019 Niek Linnenbank + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "exec/address-spaces.h" +#include "qapi/error.h" +#include "cpu.h" +#include "hw/sysbus.h" +#include "hw/boards.h" +#include "hw/qdev-properties.h" +#include "hw/arm/allwinner-h3.h" +#include "sysemu/sysemu.h" + +static struct arm_boot_info orangepi_binfo = { + .nb_cpus = AW_H3_NUM_CPUS, +}; + +static void orangepi_init(MachineState *machine) +{ + AwH3State *h3; + DriveInfo *di; + BlockBackend *blk; + BusState *bus; + DeviceState *carddev; + + /* BIOS is not supported by this board */ + if (bios_name) { + error_report("BIOS not supported for this machine"); + exit(1); + } + + /* This board has fixed size RAM */ + if (machine->ram_size != 1 * GiB) { + error_report("This machine can only be used with 1GiB of RAM"); + exit(1); + } + + /* Only allow Cortex-A7 for this board */ + if (strcmp(machine->cpu_type, ARM_CPU_TYPE_NAME("cortex-a7")) != 0) { + error_report("This board can only be used with cortex-a7 CPU"); + exit(1); + } + + h3 = AW_H3(object_new(TYPE_AW_H3)); + object_property_add_child(OBJECT(machine), "soc", OBJECT(h3), + &error_abort); + object_unref(OBJECT(h3)); + + /* Setup timer properties */ + object_property_set_int(OBJECT(h3), 32768, "clk0-freq", + &error_abort); + object_property_set_int(OBJECT(h3), 24 * 1000 * 1000, "clk1-freq", + &error_abort); + + /* Setup SID properties. Currently using a default fixed SID identifier. */ + if (qemu_uuid_is_null(&h3->sid.identifier)) { + qdev_prop_set_string(DEVICE(h3), "identifier", + "02c00081-1111-2222-3333-000044556677"); + } else if (ldl_be_p(&h3->sid.identifier.data[0]) != 0x02c00081) { + warn_report("Security Identifier value does not include H3 prefix"); + } + + /* Setup EMAC properties */ + object_property_set_int(OBJECT(&h3->emac), 1, "phy-addr", &error_abort); + + /* DRAMC */ + object_property_set_uint(OBJECT(h3), h3->memmap[AW_H3_SDRAM], + "ram-addr", &error_abort); + object_property_set_int(OBJECT(h3), machine->ram_size / MiB, "ram-size", + &error_abort); + + /* Mark H3 object realized */ + object_property_set_bool(OBJECT(h3), true, "realized", &error_abort); + + /* Retrieve SD bus */ + di = drive_get_next(IF_SD); + blk = di ? blk_by_legacy_dinfo(di) : NULL; + bus = qdev_get_child_bus(DEVICE(h3), "sd-bus"); + + /* Plug in SD card */ + carddev = qdev_create(bus, TYPE_SD_CARD); + qdev_prop_set_drive(carddev, "drive", blk, &error_fatal); + object_property_set_bool(OBJECT(carddev), true, "realized", &error_fatal); + + /* SDRAM */ + memory_region_add_subregion(get_system_memory(), h3->memmap[AW_H3_SDRAM], + machine->ram); + + /* Load target kernel or start using BootROM */ + if (!machine->kernel_filename && blk && blk_is_available(blk)) { + /* Use Boot ROM to copy data from SD card to SRAM */ + allwinner_h3_bootrom_setup(h3, blk); + } + orangepi_binfo.loader_start = h3->memmap[AW_H3_SDRAM]; + orangepi_binfo.ram_size = machine->ram_size; + arm_load_kernel(ARM_CPU(first_cpu), machine, &orangepi_binfo); +} + +static void orangepi_machine_init(MachineClass *mc) +{ + mc->desc = "Orange Pi PC"; + mc->init = orangepi_init; + mc->block_default_type = IF_SD; + mc->units_per_default_bus = 1; + mc->min_cpus = AW_H3_NUM_CPUS; + mc->max_cpus = AW_H3_NUM_CPUS; + mc->default_cpus = AW_H3_NUM_CPUS; + mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-a7"); + mc->default_ram_size = 1 * GiB; + mc->default_ram_id = "orangepi.ram"; +} + +DEFINE_MACHINE("orangepi-pc", orangepi_machine_init) diff --git a/hw/arm/palm.c b/hw/arm/palm.c index 72eca8cc55..97ca105d29 100644 --- a/hw/arm/palm.c +++ b/hw/arm/palm.c @@ -31,6 +31,7 @@ #include "hw/loader.h" #include "exec/address-spaces.h" #include "cpu.h" +#include "qemu/cutils.h" static uint64_t static_read(void *opaque, hwaddr offset, unsigned size) { @@ -195,20 +196,25 @@ static void palmte_init(MachineState *machine) static uint32_t cs2val = 0x0000e1a0; static uint32_t cs3val = 0xe1a0e1a0; int rom_size, rom_loaded = 0; - MemoryRegion *dram = g_new(MemoryRegion, 1); + MachineClass *mc = MACHINE_GET_CLASS(machine); MemoryRegion *flash = g_new(MemoryRegion, 1); MemoryRegion *cs = g_new(MemoryRegion, 4); - memory_region_allocate_system_memory(dram, NULL, "omap1.dram", - palmte_binfo.ram_size); - memory_region_add_subregion(address_space_mem, OMAP_EMIFF_BASE, dram); + if (machine->ram_size != mc->default_ram_size) { + char *sz = size_to_str(mc->default_ram_size); + error_report("Invalid RAM size, should be %s", sz); + g_free(sz); + exit(EXIT_FAILURE); + } - mpu = omap310_mpu_init(dram, machine->cpu_type); + memory_region_add_subregion(address_space_mem, OMAP_EMIFF_BASE, + machine->ram); + + mpu = omap310_mpu_init(machine->ram, machine->cpu_type); /* External Flash (EMIFS) */ - memory_region_init_ram(flash, NULL, "palmte.flash", flash_size, + memory_region_init_rom(flash, NULL, "palmte.flash", flash_size, &error_fatal); - memory_region_set_readonly(flash, true); memory_region_add_subregion(address_space_mem, OMAP_CS0_BASE, flash); memory_region_init_io(&cs[0], NULL, &static_ops, &cs0val, "palmte-cs0", @@ -265,6 +271,8 @@ static void palmte_machine_init(MachineClass *mc) mc->init = palmte_init; mc->ignore_memory_transaction_failures = true; mc->default_cpu_type = ARM_CPU_TYPE_NAME("ti925t"); + mc->default_ram_size = 0x02000000; + mc->default_ram_id = "omap1.dram"; } DEFINE_MACHINE("cheetah", palmte_machine_init) diff --git a/hw/arm/pxa2xx.c b/hw/arm/pxa2xx.c index cdafde2f76..336c9bad4a 100644 --- a/hw/arm/pxa2xx.c +++ b/hw/arm/pxa2xx.c @@ -1134,18 +1134,22 @@ static void pxa2xx_rtc_init(Object *obj) s->last_rtcpicr = 0; s->last_hz = s->last_sw = s->last_pi = qemu_clock_get_ms(rtc_clock); + sysbus_init_irq(dev, &s->rtc_irq); + + memory_region_init_io(&s->iomem, obj, &pxa2xx_rtc_ops, s, + "pxa2xx-rtc", 0x10000); + sysbus_init_mmio(dev, &s->iomem); +} + +static void pxa2xx_rtc_realize(DeviceState *dev, Error **errp) +{ + PXA2xxRTCState *s = PXA2XX_RTC(dev); s->rtc_hz = timer_new_ms(rtc_clock, pxa2xx_rtc_hz_tick, s); s->rtc_rdal1 = timer_new_ms(rtc_clock, pxa2xx_rtc_rdal1_tick, s); s->rtc_rdal2 = timer_new_ms(rtc_clock, pxa2xx_rtc_rdal2_tick, s); s->rtc_swal1 = timer_new_ms(rtc_clock, pxa2xx_rtc_swal1_tick, s); s->rtc_swal2 = timer_new_ms(rtc_clock, pxa2xx_rtc_swal2_tick, s); s->rtc_pi = timer_new_ms(rtc_clock, pxa2xx_rtc_pi_tick, s); - - sysbus_init_irq(dev, &s->rtc_irq); - - memory_region_init_io(&s->iomem, obj, &pxa2xx_rtc_ops, s, - "pxa2xx-rtc", 0x10000); - sysbus_init_mmio(dev, &s->iomem); } static int pxa2xx_rtc_pre_save(void *opaque) @@ -1203,6 +1207,7 @@ static void pxa2xx_rtc_sysbus_class_init(ObjectClass *klass, void *data) dc->desc = "PXA2xx RTC Controller"; dc->vmsd = &vmstate_pxa2xx_rtc_regs; + dc->realize = pxa2xx_rtc_realize; } static const TypeInfo pxa2xx_rtc_sysbus_info = { @@ -1531,7 +1536,7 @@ static void pxa2xx_i2c_class_init(ObjectClass *klass, void *data) dc->desc = "PXA2xx I2C Bus Controller"; dc->vmsd = &vmstate_pxa2xx_i2c; - dc->props = pxa2xx_i2c_properties; + device_class_set_props(dc, pxa2xx_i2c_properties); } static const TypeInfo pxa2xx_i2c_info = { @@ -1955,7 +1960,7 @@ static void pxa2xx_fir_rx(void *opaque, const uint8_t *buf, int size) pxa2xx_fir_update(s); } -static void pxa2xx_fir_event(void *opaque, int event) +static void pxa2xx_fir_event(void *opaque, QEMUChrEvent event) { } @@ -2015,7 +2020,7 @@ static void pxa2xx_fir_class_init(ObjectClass *klass, void *data) dc->realize = pxa2xx_fir_realize; dc->vmsd = &pxa2xx_fir_vmsd; - dc->props = pxa2xx_fir_properties; + device_class_set_props(dc, pxa2xx_fir_properties); dc->reset = pxa2xx_fir_reset; } @@ -2285,9 +2290,6 @@ PXA2xxState *pxa255_init(MemoryRegion *address_space, unsigned int sdram_size) s->ssp[i] = (SSIBus *)qdev_get_child_bus(dev, "ssi"); } - sysbus_create_simple("sysbus-ohci", 0x4c000000, - qdev_get_gpio_in(s->pic, PXA2XX_PIC_USBH1)); - s->pcmcia[0] = pxa2xx_pcmcia_init(address_space, 0x20000000); s->pcmcia[1] = pxa2xx_pcmcia_init(address_space, 0x30000000); diff --git a/hw/arm/pxa2xx_gpio.c b/hw/arm/pxa2xx_gpio.c index 86a0e86c4c..f8df3cc227 100644 --- a/hw/arm/pxa2xx_gpio.c +++ b/hw/arm/pxa2xx_gpio.c @@ -347,7 +347,7 @@ static void pxa2xx_gpio_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->desc = "PXA2xx GPIO controller"; - dc->props = pxa2xx_gpio_properties; + device_class_set_props(dc, pxa2xx_gpio_properties); dc->vmsd = &vmstate_pxa2xx_gpio_regs; dc->realize = pxa2xx_gpio_realize; } diff --git a/hw/arm/raspi.c b/hw/arm/raspi.c index 6a510aafc1..fe3b9bc78b 100644 --- a/hw/arm/raspi.c +++ b/hw/arm/raspi.c @@ -8,14 +8,17 @@ * Raspberry Pi 3 emulation Copyright (c) 2018 Zoltán Baldaszti * Upstream code cleanup (c) 2018 Pekka Enberg * - * This code is licensed under the GNU GPLv2 and later. + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. */ #include "qemu/osdep.h" #include "qemu/units.h" +#include "qemu/cutils.h" #include "qapi/error.h" #include "cpu.h" #include "hw/arm/bcm2836.h" +#include "hw/registerfields.h" #include "qemu/error-report.h" #include "hw/boards.h" #include "hw/loader.h" @@ -29,13 +32,103 @@ #define FIRMWARE_ADDR_3 0x80000 /* Pi 3 loads kernel.img here by default */ #define SPINTABLE_ADDR 0xd8 /* Pi 3 bootloader spintable */ -/* Table of Linux board IDs for different Pi versions */ -static const int raspi_boardid[] = {[1] = 0xc42, [2] = 0xc43, [3] = 0xc44}; +/* Registered machine type (matches RPi Foundation bootloader and U-Boot) */ +#define MACH_TYPE_BCM2708 3138 -typedef struct RasPiState { +typedef struct RaspiMachineState { + /*< private >*/ + MachineState parent_obj; + /*< public >*/ BCM283XState soc; - MemoryRegion ram; -} RasPiState; +} RaspiMachineState; + +typedef struct RaspiMachineClass { + /*< private >*/ + MachineClass parent_obj; + /*< public >*/ + uint32_t board_rev; +} RaspiMachineClass; + +#define TYPE_RASPI_MACHINE MACHINE_TYPE_NAME("raspi-common") +#define RASPI_MACHINE(obj) \ + OBJECT_CHECK(RaspiMachineState, (obj), TYPE_RASPI_MACHINE) + +#define RASPI_MACHINE_CLASS(klass) \ + OBJECT_CLASS_CHECK(RaspiMachineClass, (klass), TYPE_RASPI_MACHINE) +#define RASPI_MACHINE_GET_CLASS(obj) \ + OBJECT_GET_CLASS(RaspiMachineClass, (obj), TYPE_RASPI_MACHINE) + +/* + * Board revision codes: + * www.raspberrypi.org/documentation/hardware/raspberrypi/revision-codes/ + */ +FIELD(REV_CODE, REVISION, 0, 4); +FIELD(REV_CODE, TYPE, 4, 8); +FIELD(REV_CODE, PROCESSOR, 12, 4); +FIELD(REV_CODE, MANUFACTURER, 16, 4); +FIELD(REV_CODE, MEMORY_SIZE, 20, 3); +FIELD(REV_CODE, STYLE, 23, 1); + +static uint64_t board_ram_size(uint32_t board_rev) +{ + assert(FIELD_EX32(board_rev, REV_CODE, STYLE)); /* Only new style */ + return 256 * MiB << FIELD_EX32(board_rev, REV_CODE, MEMORY_SIZE); +} + +static int board_processor_id(uint32_t board_rev) +{ + assert(FIELD_EX32(board_rev, REV_CODE, STYLE)); /* Only new style */ + return FIELD_EX32(board_rev, REV_CODE, PROCESSOR); +} + +static int board_version(uint32_t board_rev) +{ + return board_processor_id(board_rev) + 1; +} + +static const char *board_soc_type(uint32_t board_rev) +{ + static const char *soc_types[] = { + NULL, TYPE_BCM2836, TYPE_BCM2837, + }; + int proc_id = board_processor_id(board_rev); + + if (proc_id >= ARRAY_SIZE(soc_types) || !soc_types[proc_id]) { + error_report("Unsupported processor id '%d' (board revision: 0x%x)", + proc_id, board_rev); + exit(1); + } + return soc_types[proc_id]; +} + +static int cores_count(uint32_t board_rev) +{ + static const int soc_cores_count[] = { + 0, BCM283X_NCPUS, BCM283X_NCPUS, + }; + int proc_id = board_processor_id(board_rev); + + if (proc_id >= ARRAY_SIZE(soc_cores_count) || !soc_cores_count[proc_id]) { + error_report("Unsupported processor id '%d' (board revision: 0x%x)", + proc_id, board_rev); + exit(1); + } + return soc_cores_count[proc_id]; +} + +static const char *board_type(uint32_t board_rev) +{ + static const char *types[] = { + "A", "B", "A+", "B+", "2B", "Alpha", "CM1", NULL, "3B", "Zero", + "CM3", NULL, "Zero W", "3B+", "3A+", NULL, "CM3+", "4B", + }; + assert(FIELD_EX32(board_rev, REV_CODE, STYLE)); /* Only new style */ + int bt = FIELD_EX32(board_rev, REV_CODE, TYPE); + if (bt >= ARRAY_SIZE(types) || !types[bt]) { + return "Unknown"; + } + return types[bt]; +} static void write_smpboot(ARMCPU *cpu, const struct arm_boot_info *info) { @@ -116,7 +209,7 @@ static void setup_boot(MachineState *machine, int version, size_t ram_size) static struct arm_boot_info binfo; int r; - binfo.board_id = raspi_boardid[version]; + binfo.board_id = MACH_TYPE_BCM2708; binfo.ram_size = ram_size; binfo.nb_cpus = machine->smp.cpus; @@ -164,37 +257,35 @@ static void setup_boot(MachineState *machine, int version, size_t ram_size) arm_load_kernel(ARM_CPU(first_cpu), machine, &binfo); } -static void raspi_init(MachineState *machine, int version) +static void raspi_machine_init(MachineState *machine) { - RasPiState *s = g_new0(RasPiState, 1); + RaspiMachineClass *mc = RASPI_MACHINE_GET_CLASS(machine); + RaspiMachineState *s = RASPI_MACHINE(machine); + uint32_t board_rev = mc->board_rev; + int version = board_version(board_rev); + uint64_t ram_size = board_ram_size(board_rev); uint32_t vcram_size; DriveInfo *di; BlockBackend *blk; BusState *bus; DeviceState *carddev; - if (machine->ram_size > 1 * GiB) { - error_report("Requested ram size is too large for this machine: " - "maximum is 1GB"); + if (machine->ram_size != ram_size) { + char *size_str = size_to_str(ram_size); + error_report("Invalid RAM size, should be %s", size_str); + g_free(size_str); exit(1); } - object_initialize_child(OBJECT(machine), "soc", &s->soc, sizeof(s->soc), - version == 3 ? TYPE_BCM2837 : TYPE_BCM2836, - &error_abort, NULL); - - /* Allocate and map RAM */ - memory_region_allocate_system_memory(&s->ram, OBJECT(machine), "ram", - machine->ram_size); /* FIXME: Remove when we have custom CPU address space support */ - memory_region_add_subregion_overlap(get_system_memory(), 0, &s->ram, 0); + memory_region_add_subregion_overlap(get_system_memory(), 0, + machine->ram, 0); /* Setup the SOC */ - object_property_add_const_link(OBJECT(&s->soc), "ram", OBJECT(&s->ram), + object_initialize_child(OBJECT(machine), "soc", &s->soc, sizeof(s->soc), + board_soc_type(board_rev), &error_abort, NULL); + object_property_add_const_link(OBJECT(&s->soc), "ram", OBJECT(machine->ram), &error_abort); - object_property_set_int(OBJECT(&s->soc), machine->smp.cpus, "enabled-cpus", - &error_abort); - int board_rev = version == 3 ? 0xa02082 : 0xa21041; object_property_set_int(OBJECT(&s->soc), board_rev, "board-rev", &error_abort); object_property_set_bool(OBJECT(&s->soc), true, "realized", &error_abort); @@ -216,45 +307,47 @@ static void raspi_init(MachineState *machine, int version) setup_boot(machine, version, machine->ram_size - vcram_size); } -static void raspi2_init(MachineState *machine) +static void raspi_machine_class_init(ObjectClass *oc, void *data) { - raspi_init(machine, 2); -} + MachineClass *mc = MACHINE_CLASS(oc); + RaspiMachineClass *rmc = RASPI_MACHINE_CLASS(oc); + uint32_t board_rev = (uint32_t)(uintptr_t)data; -static void raspi2_machine_init(MachineClass *mc) -{ - mc->desc = "Raspberry Pi 2"; - mc->init = raspi2_init; + rmc->board_rev = board_rev; + mc->desc = g_strdup_printf("Raspberry Pi %s", board_type(board_rev)); + mc->init = raspi_machine_init; mc->block_default_type = IF_SD; mc->no_parallel = 1; mc->no_floppy = 1; mc->no_cdrom = 1; - mc->max_cpus = BCM283X_NCPUS; - mc->min_cpus = BCM283X_NCPUS; - mc->default_cpus = BCM283X_NCPUS; - mc->default_ram_size = 1 * GiB; - mc->ignore_memory_transaction_failures = true; + mc->default_cpus = mc->min_cpus = mc->max_cpus = cores_count(board_rev); + mc->default_ram_size = board_ram_size(board_rev); + mc->default_ram_id = "ram"; + if (board_version(board_rev) == 2) { + mc->ignore_memory_transaction_failures = true; + } }; -DEFINE_MACHINE("raspi2", raspi2_machine_init) +static const TypeInfo raspi_machine_types[] = { + { + .name = MACHINE_TYPE_NAME("raspi2"), + .parent = TYPE_RASPI_MACHINE, + .class_init = raspi_machine_class_init, + .class_data = (void *)0xa21041, #ifdef TARGET_AARCH64 -static void raspi3_init(MachineState *machine) -{ - raspi_init(machine, 3); -} - -static void raspi3_machine_init(MachineClass *mc) -{ - mc->desc = "Raspberry Pi 3"; - mc->init = raspi3_init; - mc->block_default_type = IF_SD; - mc->no_parallel = 1; - mc->no_floppy = 1; - mc->no_cdrom = 1; - mc->max_cpus = BCM283X_NCPUS; - mc->min_cpus = BCM283X_NCPUS; - mc->default_cpus = BCM283X_NCPUS; - mc->default_ram_size = 1 * GiB; -} -DEFINE_MACHINE("raspi3", raspi3_machine_init) + }, { + .name = MACHINE_TYPE_NAME("raspi3"), + .parent = TYPE_RASPI_MACHINE, + .class_init = raspi_machine_class_init, + .class_data = (void *)0xa02082, #endif + }, { + .name = TYPE_RASPI_MACHINE, + .parent = TYPE_MACHINE, + .instance_size = sizeof(RaspiMachineState), + .class_size = sizeof(RaspiMachineClass), + .abstract = true, + } +}; + +DEFINE_TYPES(raspi_machine_types) diff --git a/hw/arm/sabrelite.c b/hw/arm/sabrelite.c index 96cc455c5c..e31694bb92 100644 --- a/hw/arm/sabrelite.c +++ b/hw/arm/sabrelite.c @@ -19,11 +19,6 @@ #include "qemu/error-report.h" #include "sysemu/qtest.h" -typedef struct IMX6Sabrelite { - FslIMX6State soc; - MemoryRegion ram; -} IMX6Sabrelite; - static struct arm_boot_info sabrelite_binfo = { /* DDR memory start */ .loader_start = FSL_IMX6_MMDC_ADDR, @@ -45,7 +40,7 @@ static void sabrelite_reset_secondary(ARMCPU *cpu, static void sabrelite_init(MachineState *machine) { - IMX6Sabrelite *s = g_new0(IMX6Sabrelite, 1); + FslIMX6State *s; Error *err = NULL; /* Check the amount of memory is compatible with the SOC */ @@ -55,19 +50,16 @@ static void sabrelite_init(MachineState *machine) exit(1); } - object_initialize_child(OBJECT(machine), "soc", &s->soc, sizeof(s->soc), - TYPE_FSL_IMX6, &error_abort, NULL); - - object_property_set_bool(OBJECT(&s->soc), true, "realized", &err); + s = FSL_IMX6(object_new(TYPE_FSL_IMX6)); + object_property_add_child(OBJECT(machine), "soc", OBJECT(s), &error_fatal); + object_property_set_bool(OBJECT(s), true, "realized", &err); if (err != NULL) { error_report("%s", error_get_pretty(err)); exit(1); } - memory_region_allocate_system_memory(&s->ram, NULL, "sabrelite.ram", - machine->ram_size); memory_region_add_subregion(get_system_memory(), FSL_IMX6_MMDC_ADDR, - &s->ram); + machine->ram); { /* @@ -78,7 +70,7 @@ static void sabrelite_init(MachineState *machine) /* Add the sst25vf016b NOR FLASH memory to first SPI */ Object *spi_dev; - spi_dev = object_resolve_path_component(OBJECT(&s->soc), "spi1"); + spi_dev = object_resolve_path_component(OBJECT(s), "spi1"); if (spi_dev) { SSIBus *spi_bus; @@ -109,7 +101,7 @@ static void sabrelite_init(MachineState *machine) sabrelite_binfo.secondary_cpu_reset_hook = sabrelite_reset_secondary; if (!qtest_enabled()) { - arm_load_kernel(&s->soc.cpu[0], machine, &sabrelite_binfo); + arm_load_kernel(&s->cpu[0], machine, &sabrelite_binfo); } } @@ -119,6 +111,7 @@ static void sabrelite_machine_init(MachineClass *mc) mc->init = sabrelite_init; mc->max_cpus = FSL_IMX6_NUM_CPUS; mc->ignore_memory_transaction_failures = true; + mc->default_ram_id = "sabrelite.ram"; } DEFINE_MACHINE("sabrelite", sabrelite_machine_init) diff --git a/hw/arm/sbsa-ref.c b/hw/arm/sbsa-ref.c index 27046cc284..8409ba853d 100644 --- a/hw/arm/sbsa-ref.c +++ b/hw/arm/sbsa-ref.c @@ -39,6 +39,7 @@ #include "hw/pci-host/gpex.h" #include "hw/qdev-properties.h" #include "hw/usb.h" +#include "hw/char/pl011.h" #include "net/net.h" #define RAMLIMIT_GB 8192 @@ -89,6 +90,7 @@ typedef struct { void *fdt; int fdt_size; int psci_conduit; + DeviceState *gic; PFlashCFI01 *flash[2]; } SBSAMachineState; @@ -328,10 +330,9 @@ static void create_secure_ram(SBSAMachineState *sms, memory_region_add_subregion(secure_sysmem, base, secram); } -static void create_gic(SBSAMachineState *sms, qemu_irq *pic) +static void create_gic(SBSAMachineState *sms) { unsigned int smp_cpus = MACHINE(sms)->smp.cpus; - DeviceState *gicdev; SysBusDevice *gicbusdev; const char *gictype; uint32_t redist0_capacity, redist0_count; @@ -339,25 +340,25 @@ static void create_gic(SBSAMachineState *sms, qemu_irq *pic) gictype = gicv3_class_name(); - gicdev = qdev_create(NULL, gictype); - qdev_prop_set_uint32(gicdev, "revision", 3); - qdev_prop_set_uint32(gicdev, "num-cpu", smp_cpus); + sms->gic = qdev_create(NULL, gictype); + qdev_prop_set_uint32(sms->gic, "revision", 3); + qdev_prop_set_uint32(sms->gic, "num-cpu", smp_cpus); /* * Note that the num-irq property counts both internal and external * interrupts; there are always 32 of the former (mandated by GIC spec). */ - qdev_prop_set_uint32(gicdev, "num-irq", NUM_IRQS + 32); - qdev_prop_set_bit(gicdev, "has-security-extensions", true); + qdev_prop_set_uint32(sms->gic, "num-irq", NUM_IRQS + 32); + qdev_prop_set_bit(sms->gic, "has-security-extensions", true); redist0_capacity = sbsa_ref_memmap[SBSA_GIC_REDIST].size / GICV3_REDIST_SIZE; redist0_count = MIN(smp_cpus, redist0_capacity); - qdev_prop_set_uint32(gicdev, "len-redist-region-count", 1); - qdev_prop_set_uint32(gicdev, "redist-region-count[0]", redist0_count); + qdev_prop_set_uint32(sms->gic, "len-redist-region-count", 1); + qdev_prop_set_uint32(sms->gic, "redist-region-count[0]", redist0_count); - qdev_init_nofail(gicdev); - gicbusdev = SYS_BUS_DEVICE(gicdev); + qdev_init_nofail(sms->gic); + gicbusdev = SYS_BUS_DEVICE(sms->gic); sysbus_mmio_map(gicbusdev, 0, sbsa_ref_memmap[SBSA_GIC_DIST].base); sysbus_mmio_map(gicbusdev, 1, sbsa_ref_memmap[SBSA_GIC_REDIST].base); @@ -383,15 +384,15 @@ static void create_gic(SBSAMachineState *sms, qemu_irq *pic) for (irq = 0; irq < ARRAY_SIZE(timer_irq); irq++) { qdev_connect_gpio_out(cpudev, irq, - qdev_get_gpio_in(gicdev, + qdev_get_gpio_in(sms->gic, ppibase + timer_irq[irq])); } qdev_connect_gpio_out_named(cpudev, "gicv3-maintenance-interrupt", 0, - qdev_get_gpio_in(gicdev, ppibase + qdev_get_gpio_in(sms->gic, ppibase + ARCH_GIC_MAINT_IRQ)); qdev_connect_gpio_out_named(cpudev, "pmu-interrupt", 0, - qdev_get_gpio_in(gicdev, ppibase + qdev_get_gpio_in(sms->gic, ppibase + VIRTUAL_PMU_IRQ)); sysbus_connect_irq(gicbusdev, i, qdev_get_gpio_in(cpudev, ARM_CPU_IRQ)); @@ -402,33 +403,29 @@ static void create_gic(SBSAMachineState *sms, qemu_irq *pic) sysbus_connect_irq(gicbusdev, i + 3 * smp_cpus, qdev_get_gpio_in(cpudev, ARM_CPU_VFIQ)); } - - for (i = 0; i < NUM_IRQS; i++) { - pic[i] = qdev_get_gpio_in(gicdev, i); - } } -static void create_uart(const SBSAMachineState *sms, qemu_irq *pic, int uart, +static void create_uart(const SBSAMachineState *sms, int uart, MemoryRegion *mem, Chardev *chr) { hwaddr base = sbsa_ref_memmap[uart].base; int irq = sbsa_ref_irqmap[uart]; - DeviceState *dev = qdev_create(NULL, "pl011"); + DeviceState *dev = qdev_create(NULL, TYPE_PL011); SysBusDevice *s = SYS_BUS_DEVICE(dev); qdev_prop_set_chr(dev, "chardev", chr); qdev_init_nofail(dev); memory_region_add_subregion(mem, base, sysbus_mmio_get_region(s, 0)); - sysbus_connect_irq(s, 0, pic[irq]); + sysbus_connect_irq(s, 0, qdev_get_gpio_in(sms->gic, irq)); } -static void create_rtc(const SBSAMachineState *sms, qemu_irq *pic) +static void create_rtc(const SBSAMachineState *sms) { hwaddr base = sbsa_ref_memmap[SBSA_RTC].base; int irq = sbsa_ref_irqmap[SBSA_RTC]; - sysbus_create_simple("pl031", base, pic[irq]); + sysbus_create_simple("pl031", base, qdev_get_gpio_in(sms->gic, irq)); } static DeviceState *gpio_key_dev; @@ -442,13 +439,14 @@ static Notifier sbsa_ref_powerdown_notifier = { .notify = sbsa_ref_powerdown_req }; -static void create_gpio(const SBSAMachineState *sms, qemu_irq *pic) +static void create_gpio(const SBSAMachineState *sms) { DeviceState *pl061_dev; hwaddr base = sbsa_ref_memmap[SBSA_GPIO].base; int irq = sbsa_ref_irqmap[SBSA_GPIO]; - pl061_dev = sysbus_create_simple("pl061", base, pic[irq]); + pl061_dev = sysbus_create_simple("pl061", base, + qdev_get_gpio_in(sms->gic, irq)); gpio_key_dev = sysbus_create_simple("gpio-key", -1, qdev_get_gpio_in(pl061_dev, 3)); @@ -457,7 +455,7 @@ static void create_gpio(const SBSAMachineState *sms, qemu_irq *pic) qemu_register_powerdown_notifier(&sbsa_ref_powerdown_notifier); } -static void create_ahci(const SBSAMachineState *sms, qemu_irq *pic) +static void create_ahci(const SBSAMachineState *sms) { hwaddr base = sbsa_ref_memmap[SBSA_AHCI].base; int irq = sbsa_ref_irqmap[SBSA_AHCI]; @@ -471,7 +469,7 @@ static void create_ahci(const SBSAMachineState *sms, qemu_irq *pic) qdev_prop_set_uint32(dev, "num-ports", NUM_SATA_PORTS); qdev_init_nofail(dev); sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[irq]); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, qdev_get_gpio_in(sms->gic, irq)); sysahci = SYSBUS_AHCI(dev); ahci = &sysahci->ahci; @@ -484,16 +482,16 @@ static void create_ahci(const SBSAMachineState *sms, qemu_irq *pic) } } -static void create_ehci(const SBSAMachineState *sms, qemu_irq *pic) +static void create_ehci(const SBSAMachineState *sms) { hwaddr base = sbsa_ref_memmap[SBSA_EHCI].base; int irq = sbsa_ref_irqmap[SBSA_EHCI]; - sysbus_create_simple("platform-ehci-usb", base, pic[irq]); + sysbus_create_simple("platform-ehci-usb", base, + qdev_get_gpio_in(sms->gic, irq)); } -static void create_smmu(const SBSAMachineState *sms, qemu_irq *pic, - PCIBus *bus) +static void create_smmu(const SBSAMachineState *sms, PCIBus *bus) { hwaddr base = sbsa_ref_memmap[SBSA_SMMU].base; int irq = sbsa_ref_irqmap[SBSA_SMMU]; @@ -507,11 +505,12 @@ static void create_smmu(const SBSAMachineState *sms, qemu_irq *pic, qdev_init_nofail(dev); sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); for (i = 0; i < NUM_SMMU_IRQS; i++) { - sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, pic[irq + i]); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, + qdev_get_gpio_in(sms->gic, irq + 1)); } } -static void create_pcie(SBSAMachineState *sms, qemu_irq *pic) +static void create_pcie(SBSAMachineState *sms) { hwaddr base_ecam = sbsa_ref_memmap[SBSA_PCIE_ECAM].base; hwaddr size_ecam = sbsa_ref_memmap[SBSA_PCIE_ECAM].size; @@ -555,7 +554,8 @@ static void create_pcie(SBSAMachineState *sms, qemu_irq *pic) sysbus_mmio_map(SYS_BUS_DEVICE(dev), 2, base_pio); for (i = 0; i < GPEX_NUM_IRQS; i++) { - sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, pic[irq + i]); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, + qdev_get_gpio_in(sms->gic, irq + 1)); gpex_set_irq_num(GPEX_HOST(dev), i, irq + i); } @@ -574,7 +574,7 @@ static void create_pcie(SBSAMachineState *sms, qemu_irq *pic) pci_create_simple(pci->bus, -1, "VGA"); - create_smmu(sms, pic, pci->bus); + create_smmu(sms, pci->bus); } static void *sbsa_ref_dtb(const struct arm_boot_info *binfo, int *fdt_size) @@ -594,11 +594,9 @@ static void sbsa_ref_init(MachineState *machine) MachineClass *mc = MACHINE_GET_CLASS(machine); MemoryRegion *sysmem = get_system_memory(); MemoryRegion *secure_sysmem = g_new(MemoryRegion, 1); - MemoryRegion *ram = g_new(MemoryRegion, 1); bool firmware_loaded; const CPUArchIdList *possible_cpus; int n, sbsa_max_cpus; - qemu_irq pic[NUM_IRQS]; if (strcmp(machine->cpu_type, ARM_CPU_TYPE_NAME("cortex-a57"))) { error_report("sbsa-ref: CPU type other than the built-in " @@ -687,30 +685,29 @@ static void sbsa_ref_init(MachineState *machine) object_unref(cpuobj); } - memory_region_allocate_system_memory(ram, NULL, "sbsa-ref.ram", - machine->ram_size); - memory_region_add_subregion(sysmem, sbsa_ref_memmap[SBSA_MEM].base, ram); + memory_region_add_subregion(sysmem, sbsa_ref_memmap[SBSA_MEM].base, + machine->ram); create_fdt(sms); create_secure_ram(sms, secure_sysmem); - create_gic(sms, pic); + create_gic(sms); - create_uart(sms, pic, SBSA_UART, sysmem, serial_hd(0)); - create_uart(sms, pic, SBSA_SECURE_UART, secure_sysmem, serial_hd(1)); + create_uart(sms, SBSA_UART, sysmem, serial_hd(0)); + create_uart(sms, SBSA_SECURE_UART, secure_sysmem, serial_hd(1)); /* Second secure UART for RAS and MM from EL0 */ - create_uart(sms, pic, SBSA_SECURE_UART_MM, secure_sysmem, serial_hd(2)); + create_uart(sms, SBSA_SECURE_UART_MM, secure_sysmem, serial_hd(2)); - create_rtc(sms, pic); + create_rtc(sms); - create_gpio(sms, pic); + create_gpio(sms); - create_ahci(sms, pic); + create_ahci(sms); - create_ehci(sms, pic); + create_ehci(sms); - create_pcie(sms, pic); + create_pcie(sms); sms->bootinfo.ram_size = machine->ram_size; sms->bootinfo.nb_cpus = smp_cpus; @@ -787,11 +784,11 @@ static void sbsa_ref_class_init(ObjectClass *oc, void *data) mc->block_default_type = IF_IDE; mc->no_cdrom = 1; mc->default_ram_size = 1 * GiB; + mc->default_ram_id = "sbsa-ref.ram"; mc->default_cpus = 4; mc->possible_cpu_arch_ids = sbsa_ref_possible_cpu_arch_ids; mc->cpu_index_to_instance_props = sbsa_ref_cpu_index_to_props; mc->get_default_cpu_node_id = sbsa_ref_get_default_cpu_node_id; - mc->numa_mem_supported = true; } static const TypeInfo sbsa_ref_info = { diff --git a/hw/arm/smmu-common.c b/hw/arm/smmu-common.c index 245817d23e..e13a5f4a7c 100644 --- a/hw/arm/smmu-common.c +++ b/hw/arm/smmu-common.c @@ -74,8 +74,7 @@ static int get_pte(dma_addr_t baseaddr, uint32_t index, uint64_t *pte, dma_addr_t addr = baseaddr + index * sizeof(*pte); /* TODO: guarantee 64-bit single-copy atomicity */ - ret = dma_memory_read(&address_space_memory, addr, - (uint8_t *)pte, sizeof(*pte)); + ret = dma_memory_read(&address_space_memory, addr, pte, sizeof(*pte)); if (ret != MEMTX_OK) { info->type = SMMU_PTW_ERR_WALK_EABT; @@ -291,19 +290,21 @@ inline int smmu_ptw(SMMUTransCfg *cfg, dma_addr_t iova, IOMMUAccessFlags perm, SMMUPciBus *smmu_find_smmu_pcibus(SMMUState *s, uint8_t bus_num) { SMMUPciBus *smmu_pci_bus = s->smmu_pcibus_by_bus_num[bus_num]; + GHashTableIter iter; - if (!smmu_pci_bus) { - GHashTableIter iter; + if (smmu_pci_bus) { + return smmu_pci_bus; + } - g_hash_table_iter_init(&iter, s->smmu_pcibus_by_busptr); - while (g_hash_table_iter_next(&iter, NULL, (void **)&smmu_pci_bus)) { - if (pci_bus_num(smmu_pci_bus->bus) == bus_num) { - s->smmu_pcibus_by_bus_num[bus_num] = smmu_pci_bus; - return smmu_pci_bus; - } + g_hash_table_iter_init(&iter, s->smmu_pcibus_by_busptr); + while (g_hash_table_iter_next(&iter, NULL, (void **)&smmu_pci_bus)) { + if (pci_bus_num(smmu_pci_bus->bus) == bus_num) { + s->smmu_pcibus_by_bus_num[bus_num] = smmu_pci_bus; + return smmu_pci_bus; } } - return smmu_pci_bus; + + return NULL; } static AddressSpace *smmu_find_add_as(PCIBus *bus, void *opaque, int devfn) @@ -461,7 +462,7 @@ static void smmu_base_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); SMMUBaseClass *sbc = ARM_SMMU_CLASS(klass); - dc->props = smmu_dev_properties; + device_class_set_props(dc, smmu_dev_properties); device_class_set_parent_realize(dc, smmu_base_realize, &sbc->parent_realize); dc->reset = smmu_base_reset; diff --git a/hw/arm/smmuv3-internal.h b/hw/arm/smmuv3-internal.h index d190181ef1..4112394129 100644 --- a/hw/arm/smmuv3-internal.h +++ b/hw/arm/smmuv3-internal.h @@ -99,7 +99,7 @@ REG32(GERROR_IRQ_CFG2, 0x74) #define A_STRTAB_BASE 0x80 /* 64b */ -#define SMMU_BASE_ADDR_MASK 0xffffffffffe0 +#define SMMU_BASE_ADDR_MASK 0xfffffffffffc0 REG32(STRTAB_BASE_CFG, 0x88) FIELD(STRTAB_BASE_CFG, FMT, 16, 2) @@ -461,8 +461,8 @@ typedef struct SMMUEventInfo { } while (0) #define EVT_SET_ADDR2(x, addr) \ do { \ - (x)->word[7] = deposit32((x)->word[7], 3, 29, addr >> 16); \ - (x)->word[7] = deposit32((x)->word[7], 0, 16, addr & 0xffff);\ + (x)->word[7] = (uint32_t)(addr >> 32); \ + (x)->word[6] = (uint32_t)(addr & 0xffffffff); \ } while (0) void smmuv3_record_event(SMMUv3State *s, SMMUEventInfo *event); diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c index e2fbb8357e..57a79df55b 100644 --- a/hw/arm/smmuv3.c +++ b/hw/arm/smmuv3.c @@ -172,7 +172,7 @@ void smmuv3_record_event(SMMUv3State *s, SMMUEventInfo *info) case SMMU_EVT_F_STE_FETCH: EVT_SET_SSID(&evt, info->u.f_ste_fetch.ssid); EVT_SET_SSV(&evt, info->u.f_ste_fetch.ssv); - EVT_SET_ADDR(&evt, info->u.f_ste_fetch.addr); + EVT_SET_ADDR2(&evt, info->u.f_ste_fetch.addr); break; case SMMU_EVT_C_BAD_STE: EVT_SET_SSID(&evt, info->u.c_bad_ste.ssid); @@ -279,8 +279,7 @@ static int smmu_get_ste(SMMUv3State *s, dma_addr_t addr, STE *buf, trace_smmuv3_get_ste(addr); /* TODO: guarantee 64-bit single-copy atomicity */ - ret = dma_memory_read(&address_space_memory, addr, - (void *)buf, sizeof(*buf)); + ret = dma_memory_read(&address_space_memory, addr, buf, sizeof(*buf)); if (ret != MEMTX_OK) { qemu_log_mask(LOG_GUEST_ERROR, "Cannot fetch pte at address=0x%"PRIx64"\n", addr); @@ -301,8 +300,7 @@ static int smmu_get_cd(SMMUv3State *s, STE *ste, uint32_t ssid, trace_smmuv3_get_cd(addr); /* TODO: guarantee 64-bit single-copy atomicity */ - ret = dma_memory_read(&address_space_memory, addr, - (void *)buf, sizeof(*buf)); + ret = dma_memory_read(&address_space_memory, addr, buf, sizeof(*buf)); if (ret != MEMTX_OK) { qemu_log_mask(LOG_GUEST_ERROR, "Cannot fetch pte at address=0x%"PRIx64"\n", addr); @@ -376,27 +374,38 @@ bad_ste: static int smmu_find_ste(SMMUv3State *s, uint32_t sid, STE *ste, SMMUEventInfo *event) { - dma_addr_t addr; + dma_addr_t addr, strtab_base; + uint32_t log2size; + int strtab_size_shift; int ret; trace_smmuv3_find_ste(sid, s->features, s->sid_split); - /* Check SID range */ - if (sid > (1 << SMMU_IDR1_SIDSIZE)) { + log2size = FIELD_EX32(s->strtab_base_cfg, STRTAB_BASE_CFG, LOG2SIZE); + /* + * Check SID range against both guest-configured and implementation limits + */ + if (sid >= (1 << MIN(log2size, SMMU_IDR1_SIDSIZE))) { event->type = SMMU_EVT_C_BAD_STREAMID; return -EINVAL; } if (s->features & SMMU_FEATURE_2LVL_STE) { int l1_ste_offset, l2_ste_offset, max_l2_ste, span; - dma_addr_t strtab_base, l1ptr, l2ptr; + dma_addr_t l1ptr, l2ptr; STEDesc l1std; - strtab_base = s->strtab_base & SMMU_BASE_ADDR_MASK; + /* + * Align strtab base address to table size. For this purpose, assume it + * is not bounded by SMMU_IDR1_SIDSIZE. + */ + strtab_size_shift = MAX(5, (int)log2size - s->sid_split - 1 + 3); + strtab_base = s->strtab_base & SMMU_BASE_ADDR_MASK & + ~MAKE_64BIT_MASK(0, strtab_size_shift); l1_ste_offset = sid >> s->sid_split; l2_ste_offset = sid & ((1 << s->sid_split) - 1); l1ptr = (dma_addr_t)(strtab_base + l1_ste_offset * sizeof(l1std)); /* TODO: guarantee 64-bit single-copy atomicity */ - ret = dma_memory_read(&address_space_memory, l1ptr, - (uint8_t *)&l1std, sizeof(l1std)); + ret = dma_memory_read(&address_space_memory, l1ptr, &l1std, + sizeof(l1std)); if (ret != MEMTX_OK) { qemu_log_mask(LOG_GUEST_ERROR, "Could not read L1PTR at 0X%"PRIx64"\n", l1ptr); @@ -429,7 +438,10 @@ static int smmu_find_ste(SMMUv3State *s, uint32_t sid, STE *ste, } addr = l2ptr + l2_ste_offset * sizeof(*ste); } else { - addr = s->strtab_base + sid * sizeof(*ste); + strtab_size_shift = log2size + 5; + strtab_base = s->strtab_base & SMMU_BASE_ADDR_MASK & + ~MAKE_64BIT_MASK(0, strtab_size_shift); + addr = strtab_base + sid * sizeof(*ste); } if (smmu_get_ste(s, addr, ste, event)) { diff --git a/hw/arm/spitz.c b/hw/arm/spitz.c index 25bd0f5d9d..c28d9b5ed7 100644 --- a/hw/arm/spitz.c +++ b/hw/arm/spitz.c @@ -524,11 +524,16 @@ static void spitz_keyboard_init(Object *obj) spitz_keyboard_pre_map(s); - s->kbdtimer = timer_new_ns(QEMU_CLOCK_VIRTUAL, spitz_keyboard_tick, s); qdev_init_gpio_in(dev, spitz_keyboard_strobe, SPITZ_KEY_STROBE_NUM); qdev_init_gpio_out(dev, s->sense, SPITZ_KEY_SENSE_NUM); } +static void spitz_keyboard_realize(DeviceState *dev, Error **errp) +{ + SpitzKeyboardState *s = SPITZ_KEYBOARD(dev); + s->kbdtimer = timer_new_ns(QEMU_CLOCK_VIRTUAL, spitz_keyboard_tick, s); +} + /* LCD backlight controller */ #define LCDTG_RESCTL 0x00 @@ -924,8 +929,7 @@ static void spitz_common_init(MachineState *machine, sl_flash_register(mpu, (model == spitz) ? FLASH_128M : FLASH_1024M); - memory_region_init_ram(rom, NULL, "spitz.rom", SPITZ_ROM, &error_fatal); - memory_region_set_readonly(rom, true); + memory_region_init_rom(rom, NULL, "spitz.rom", SPITZ_ROM, &error_fatal); memory_region_add_subregion(address_space_mem, 0, rom); /* Setup peripherals */ @@ -1083,7 +1087,7 @@ static void sl_nand_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->vmsd = &vmstate_sl_nand_info; - dc->props = sl_nand_properties; + device_class_set_props(dc, sl_nand_properties); dc->realize = sl_nand_realize; /* Reason: init() method uses drive_get() */ dc->user_creatable = false; @@ -1115,6 +1119,7 @@ static void spitz_keyboard_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->vmsd = &vmstate_spitz_kbd; + dc->realize = spitz_keyboard_realize; } static const TypeInfo spitz_keyboard_info = { diff --git a/hw/arm/stellaris.c b/hw/arm/stellaris.c index b198066b54..d136ba1a92 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 { @@ -708,7 +712,7 @@ static int stellaris_sys_init(uint32_t base, qemu_irq irq, memory_region_init_io(&s->iomem, NULL, &ssys_ops, s, "ssys", 0x00001000); memory_region_add_subregion(get_system_memory(), base, &s->iomem); ssys_reset(s); - vmstate_register(NULL, -1, &vmstate_stellaris_sys, s); + vmstate_register(NULL, VMSTATE_INSTANCE_ID_ANY, &vmstate_stellaris_sys, s); return 0; } @@ -1296,9 +1300,8 @@ static void stellaris_init(MachineState *ms, stellaris_board_info *board) sram_size = ((board->dc0 >> 18) + 1) * 1024; /* Flash programming is done via the SCU, so pretend it is ROM. */ - memory_region_init_ram(flash, NULL, "stellaris.flash", flash_size, + memory_region_init_rom(flash, NULL, "stellaris.flash", flash_size, &error_fatal); - memory_region_set_readonly(flash, true); memory_region_add_subregion(system_memory, 0, flash); memory_region_init_ram(sram, NULL, "stellaris.sram", sram_size, @@ -1536,6 +1539,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/arm/stm32f205_soc.c b/hw/arm/stm32f205_soc.c index f5a5c2d80c..118c342559 100644 --- a/hw/arm/stm32f205_soc.c +++ b/hw/arm/stm32f205_soc.c @@ -93,13 +93,10 @@ static void stm32f205_soc_realize(DeviceState *dev_soc, Error **errp) MemoryRegion *flash = g_new(MemoryRegion, 1); MemoryRegion *flash_alias = g_new(MemoryRegion, 1); - memory_region_init_ram(flash, NULL, "STM32F205.flash", FLASH_SIZE, - &error_fatal); - memory_region_init_alias(flash_alias, NULL, "STM32F205.flash.alias", - flash, 0, FLASH_SIZE); - - memory_region_set_readonly(flash, true); - memory_region_set_readonly(flash_alias, true); + memory_region_init_rom(flash, OBJECT(dev_soc), "STM32F205.flash", + FLASH_SIZE, &error_fatal); + memory_region_init_alias(flash_alias, OBJECT(dev_soc), + "STM32F205.flash.alias", flash, 0, FLASH_SIZE); memory_region_add_subregion(system_memory, FLASH_BASE_ADDRESS, flash); memory_region_add_subregion(system_memory, 0, flash_alias); @@ -207,7 +204,7 @@ static void stm32f205_soc_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = stm32f205_soc_realize; - dc->props = stm32f205_soc_properties; + device_class_set_props(dc, stm32f205_soc_properties); } static const TypeInfo stm32f205_soc_info = { diff --git a/hw/arm/stm32f405_soc.c b/hw/arm/stm32f405_soc.c new file mode 100644 index 0000000000..4f10ce6176 --- /dev/null +++ b/hw/arm/stm32f405_soc.c @@ -0,0 +1,300 @@ +/* + * STM32F405 SoC + * + * Copyright (c) 2014 Alistair Francis + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "exec/address-spaces.h" +#include "sysemu/sysemu.h" +#include "hw/arm/stm32f405_soc.h" +#include "hw/misc/unimp.h" + +#define SYSCFG_ADD 0x40013800 +static const uint32_t usart_addr[] = { 0x40011000, 0x40004400, 0x40004800, + 0x40004C00, 0x40005000, 0x40011400, + 0x40007800, 0x40007C00 }; +/* At the moment only Timer 2 to 5 are modelled */ +static const uint32_t timer_addr[] = { 0x40000000, 0x40000400, + 0x40000800, 0x40000C00 }; +#define ADC_ADDR 0x40012000 +static const uint32_t spi_addr[] = { 0x40013000, 0x40003800, 0x40003C00, + 0x40013400, 0x40015000, 0x40015400 }; +#define EXTI_ADDR 0x40013C00 + +#define SYSCFG_IRQ 71 +static const int usart_irq[] = { 37, 38, 39, 52, 53, 71, 82, 83 }; +static const int timer_irq[] = { 28, 29, 30, 50 }; +#define ADC_IRQ 18 +static const int spi_irq[] = { 35, 36, 51, 0, 0, 0 }; +static const int exti_irq[] = { 6, 7, 8, 9, 10, 23, 23, 23, 23, 23, 40, + 40, 40, 40, 40, 40} ; + + +static void stm32f405_soc_initfn(Object *obj) +{ + STM32F405State *s = STM32F405_SOC(obj); + int i; + + sysbus_init_child_obj(obj, "armv7m", &s->armv7m, sizeof(s->armv7m), + TYPE_ARMV7M); + + sysbus_init_child_obj(obj, "syscfg", &s->syscfg, sizeof(s->syscfg), + TYPE_STM32F4XX_SYSCFG); + + for (i = 0; i < STM_NUM_USARTS; i++) { + sysbus_init_child_obj(obj, "usart[*]", &s->usart[i], + sizeof(s->usart[i]), TYPE_STM32F2XX_USART); + } + + for (i = 0; i < STM_NUM_TIMERS; i++) { + sysbus_init_child_obj(obj, "timer[*]", &s->timer[i], + sizeof(s->timer[i]), TYPE_STM32F2XX_TIMER); + } + + for (i = 0; i < STM_NUM_ADCS; i++) { + sysbus_init_child_obj(obj, "adc[*]", &s->adc[i], sizeof(s->adc[i]), + TYPE_STM32F2XX_ADC); + } + + for (i = 0; i < STM_NUM_SPIS; i++) { + sysbus_init_child_obj(obj, "spi[*]", &s->spi[i], sizeof(s->spi[i]), + TYPE_STM32F2XX_SPI); + } + + sysbus_init_child_obj(obj, "exti", &s->exti, sizeof(s->exti), + TYPE_STM32F4XX_EXTI); +} + +static void stm32f405_soc_realize(DeviceState *dev_soc, Error **errp) +{ + STM32F405State *s = STM32F405_SOC(dev_soc); + MemoryRegion *system_memory = get_system_memory(); + DeviceState *dev, *armv7m; + SysBusDevice *busdev; + Error *err = NULL; + int i; + + memory_region_init_rom(&s->flash, OBJECT(dev_soc), "STM32F405.flash", + FLASH_SIZE, &err); + if (err != NULL) { + error_propagate(errp, err); + return; + } + memory_region_init_alias(&s->flash_alias, OBJECT(dev_soc), + "STM32F405.flash.alias", &s->flash, 0, + FLASH_SIZE); + + memory_region_add_subregion(system_memory, FLASH_BASE_ADDRESS, &s->flash); + memory_region_add_subregion(system_memory, 0, &s->flash_alias); + + memory_region_init_ram(&s->sram, NULL, "STM32F405.sram", SRAM_SIZE, + &err); + if (err != NULL) { + error_propagate(errp, err); + return; + } + memory_region_add_subregion(system_memory, SRAM_BASE_ADDRESS, &s->sram); + + armv7m = DEVICE(&s->armv7m); + qdev_prop_set_uint32(armv7m, "num-irq", 96); + qdev_prop_set_string(armv7m, "cpu-type", s->cpu_type); + qdev_prop_set_bit(armv7m, "enable-bitband", true); + object_property_set_link(OBJECT(&s->armv7m), OBJECT(system_memory), + "memory", &error_abort); + object_property_set_bool(OBJECT(&s->armv7m), true, "realized", &err); + if (err != NULL) { + error_propagate(errp, err); + return; + } + + /* System configuration controller */ + dev = DEVICE(&s->syscfg); + object_property_set_bool(OBJECT(&s->syscfg), true, "realized", &err); + if (err != NULL) { + error_propagate(errp, err); + return; + } + busdev = SYS_BUS_DEVICE(dev); + sysbus_mmio_map(busdev, 0, SYSCFG_ADD); + sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(armv7m, SYSCFG_IRQ)); + + /* Attach UART (uses USART registers) and USART controllers */ + for (i = 0; i < STM_NUM_USARTS; i++) { + dev = DEVICE(&(s->usart[i])); + qdev_prop_set_chr(dev, "chardev", serial_hd(i)); + object_property_set_bool(OBJECT(&s->usart[i]), true, "realized", &err); + if (err != NULL) { + error_propagate(errp, err); + return; + } + busdev = SYS_BUS_DEVICE(dev); + sysbus_mmio_map(busdev, 0, usart_addr[i]); + sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(armv7m, usart_irq[i])); + } + + /* Timer 2 to 5 */ + for (i = 0; i < STM_NUM_TIMERS; i++) { + dev = DEVICE(&(s->timer[i])); + qdev_prop_set_uint64(dev, "clock-frequency", 1000000000); + object_property_set_bool(OBJECT(&s->timer[i]), true, "realized", &err); + if (err != NULL) { + error_propagate(errp, err); + return; + } + busdev = SYS_BUS_DEVICE(dev); + sysbus_mmio_map(busdev, 0, timer_addr[i]); + sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(armv7m, timer_irq[i])); + } + + /* ADC device, the IRQs are ORed together */ + object_initialize_child(OBJECT(s), "adc-orirq", &s->adc_irqs, + sizeof(s->adc_irqs), TYPE_OR_IRQ, + &err, NULL); + if (err != NULL) { + error_propagate(errp, err); + return; + } + object_property_set_int(OBJECT(&s->adc_irqs), STM_NUM_ADCS, + "num-lines", &err); + object_property_set_bool(OBJECT(&s->adc_irqs), true, "realized", &err); + if (err != NULL) { + error_propagate(errp, err); + return; + } + qdev_connect_gpio_out(DEVICE(&s->adc_irqs), 0, + qdev_get_gpio_in(armv7m, ADC_IRQ)); + + dev = DEVICE(&(s->adc[i])); + object_property_set_bool(OBJECT(&s->adc[i]), true, "realized", &err); + if (err != NULL) { + error_propagate(errp, err); + return; + } + busdev = SYS_BUS_DEVICE(dev); + sysbus_mmio_map(busdev, 0, ADC_ADDR); + sysbus_connect_irq(busdev, 0, + qdev_get_gpio_in(DEVICE(&s->adc_irqs), i)); + + /* SPI devices */ + for (i = 0; i < STM_NUM_SPIS; i++) { + dev = DEVICE(&(s->spi[i])); + object_property_set_bool(OBJECT(&s->spi[i]), true, "realized", &err); + if (err != NULL) { + error_propagate(errp, err); + return; + } + busdev = SYS_BUS_DEVICE(dev); + sysbus_mmio_map(busdev, 0, spi_addr[i]); + sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(armv7m, spi_irq[i])); + } + + /* EXTI device */ + dev = DEVICE(&s->exti); + object_property_set_bool(OBJECT(&s->exti), true, "realized", &err); + if (err != NULL) { + error_propagate(errp, err); + return; + } + busdev = SYS_BUS_DEVICE(dev); + sysbus_mmio_map(busdev, 0, EXTI_ADDR); + for (i = 0; i < 16; i++) { + sysbus_connect_irq(busdev, i, qdev_get_gpio_in(armv7m, exti_irq[i])); + } + for (i = 0; i < 16; i++) { + qdev_connect_gpio_out(DEVICE(&s->syscfg), i, qdev_get_gpio_in(dev, i)); + } + + create_unimplemented_device("timer[7]", 0x40001400, 0x400); + create_unimplemented_device("timer[12]", 0x40001800, 0x400); + create_unimplemented_device("timer[6]", 0x40001000, 0x400); + create_unimplemented_device("timer[13]", 0x40001C00, 0x400); + create_unimplemented_device("timer[14]", 0x40002000, 0x400); + create_unimplemented_device("RTC and BKP", 0x40002800, 0x400); + create_unimplemented_device("WWDG", 0x40002C00, 0x400); + create_unimplemented_device("IWDG", 0x40003000, 0x400); + create_unimplemented_device("I2S2ext", 0x40003000, 0x400); + create_unimplemented_device("I2S3ext", 0x40004000, 0x400); + create_unimplemented_device("I2C1", 0x40005400, 0x400); + create_unimplemented_device("I2C2", 0x40005800, 0x400); + create_unimplemented_device("I2C3", 0x40005C00, 0x400); + create_unimplemented_device("CAN1", 0x40006400, 0x400); + create_unimplemented_device("CAN2", 0x40006800, 0x400); + create_unimplemented_device("PWR", 0x40007000, 0x400); + create_unimplemented_device("DAC", 0x40007400, 0x400); + create_unimplemented_device("timer[1]", 0x40010000, 0x400); + create_unimplemented_device("timer[8]", 0x40010400, 0x400); + create_unimplemented_device("SDIO", 0x40012C00, 0x400); + create_unimplemented_device("timer[9]", 0x40014000, 0x400); + create_unimplemented_device("timer[10]", 0x40014400, 0x400); + create_unimplemented_device("timer[11]", 0x40014800, 0x400); + create_unimplemented_device("GPIOA", 0x40020000, 0x400); + create_unimplemented_device("GPIOB", 0x40020400, 0x400); + create_unimplemented_device("GPIOC", 0x40020800, 0x400); + create_unimplemented_device("GPIOD", 0x40020C00, 0x400); + create_unimplemented_device("GPIOE", 0x40021000, 0x400); + create_unimplemented_device("GPIOF", 0x40021400, 0x400); + create_unimplemented_device("GPIOG", 0x40021800, 0x400); + create_unimplemented_device("GPIOH", 0x40021C00, 0x400); + create_unimplemented_device("GPIOI", 0x40022000, 0x400); + create_unimplemented_device("CRC", 0x40023000, 0x400); + create_unimplemented_device("RCC", 0x40023800, 0x400); + create_unimplemented_device("Flash Int", 0x40023C00, 0x400); + create_unimplemented_device("BKPSRAM", 0x40024000, 0x400); + create_unimplemented_device("DMA1", 0x40026000, 0x400); + create_unimplemented_device("DMA2", 0x40026400, 0x400); + create_unimplemented_device("Ethernet", 0x40028000, 0x1400); + create_unimplemented_device("USB OTG HS", 0x40040000, 0x30000); + create_unimplemented_device("USB OTG FS", 0x50000000, 0x31000); + create_unimplemented_device("DCMI", 0x50050000, 0x400); + create_unimplemented_device("RNG", 0x50060800, 0x400); +} + +static Property stm32f405_soc_properties[] = { + DEFINE_PROP_STRING("cpu-type", STM32F405State, cpu_type), + DEFINE_PROP_END_OF_LIST(), +}; + +static void stm32f405_soc_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = stm32f405_soc_realize; + device_class_set_props(dc, stm32f405_soc_properties); + /* No vmstate or reset required: device has no internal state */ +} + +static const TypeInfo stm32f405_soc_info = { + .name = TYPE_STM32F405_SOC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(STM32F405State), + .instance_init = stm32f405_soc_initfn, + .class_init = stm32f405_soc_class_init, +}; + +static void stm32f405_soc_types(void) +{ + type_register_static(&stm32f405_soc_info); +} + +type_init(stm32f405_soc_types) diff --git a/hw/arm/strongarm.c b/hw/arm/strongarm.c index 6bee034914..3010d765bb 100644 --- a/hw/arm/strongarm.c +++ b/hw/arm/strongarm.c @@ -399,9 +399,6 @@ static void strongarm_rtc_init(Object *obj) s->last_rcnr = (uint32_t) mktimegm(&tm); s->last_hz = qemu_clock_get_ms(rtc_clock); - s->rtc_alarm = timer_new_ms(rtc_clock, strongarm_rtc_alarm_tick, s); - s->rtc_hz = timer_new_ms(rtc_clock, strongarm_rtc_hz_tick, s); - sysbus_init_irq(dev, &s->rtc_irq); sysbus_init_irq(dev, &s->rtc_hz_irq); @@ -410,6 +407,13 @@ static void strongarm_rtc_init(Object *obj) sysbus_init_mmio(dev, &s->iomem); } +static void strongarm_rtc_realize(DeviceState *dev, Error **errp) +{ + StrongARMRTCState *s = STRONGARM_RTC(dev); + s->rtc_alarm = timer_new_ms(rtc_clock, strongarm_rtc_alarm_tick, s); + s->rtc_hz = timer_new_ms(rtc_clock, strongarm_rtc_hz_tick, s); +} + static int strongarm_rtc_pre_save(void *opaque) { StrongARMRTCState *s = opaque; @@ -451,6 +455,7 @@ static void strongarm_rtc_sysbus_class_init(ObjectClass *klass, void *data) dc->desc = "StrongARM RTC Controller"; dc->vmsd = &vmstate_strongarm_rtc_regs; + dc->realize = strongarm_rtc_realize; } static const TypeInfo strongarm_rtc_sysbus_info = { @@ -1093,7 +1098,7 @@ static void strongarm_uart_receive(void *opaque, const uint8_t *buf, int size) strongarm_uart_update_int_status(s); } -static void strongarm_uart_event(void *opaque, int event) +static void strongarm_uart_event(void *opaque, QEMUChrEvent event) { StrongARMUARTState *s = opaque; if (event == CHR_EVENT_BREAK) { @@ -1240,15 +1245,16 @@ static void strongarm_uart_init(Object *obj) "uart", 0x10000); sysbus_init_mmio(dev, &s->iomem); sysbus_init_irq(dev, &s->irq); - - s->rx_timeout_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, strongarm_uart_rx_to, s); - s->tx_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, strongarm_uart_tx, s); } static void strongarm_uart_realize(DeviceState *dev, Error **errp) { StrongARMUARTState *s = STRONGARM_UART(dev); + s->rx_timeout_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, + strongarm_uart_rx_to, + s); + s->tx_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, strongarm_uart_tx, s); qemu_chr_fe_set_handlers(&s->chr, strongarm_uart_can_receive, strongarm_uart_receive, @@ -1327,7 +1333,7 @@ static void strongarm_uart_class_init(ObjectClass *klass, void *data) dc->desc = "StrongARM UART controller"; dc->reset = strongarm_uart_reset; dc->vmsd = &vmstate_strongarm_uart_regs; - dc->props = strongarm_uart_properties; + device_class_set_props(dc, strongarm_uart_properties); dc->realize = strongarm_uart_realize; } diff --git a/hw/arm/sysbus-fdt.c b/hw/arm/sysbus-fdt.c index 022fc97ecd..6b6906f4cf 100644 --- a/hw/arm/sysbus-fdt.c +++ b/hw/arm/sysbus-fdt.c @@ -30,6 +30,7 @@ #include "hw/arm/sysbus-fdt.h" #include "qemu/error-report.h" #include "sysemu/device_tree.h" +#include "sysemu/tpm.h" #include "hw/platform-bus.h" #include "hw/vfio/vfio-platform.h" #include "hw/vfio/vfio-calxeda-xgmac.h" @@ -436,6 +437,37 @@ static bool vfio_platform_match(SysBusDevice *sbdev, #endif /* CONFIG_LINUX */ +/* + * add_tpm_tis_fdt_node: Create a DT node for TPM TIS + * + * See kernel documentation: + * Documentation/devicetree/bindings/security/tpm/tpm_tis_mmio.txt + * Optional interrupt for command completion is not exposed + */ +static int add_tpm_tis_fdt_node(SysBusDevice *sbdev, void *opaque) +{ + PlatformBusFDTData *data = opaque; + PlatformBusDevice *pbus = data->pbus; + void *fdt = data->fdt; + const char *parent_node = data->pbus_node_name; + char *nodename; + uint32_t reg_attr[2]; + uint64_t mmio_base; + + mmio_base = platform_bus_get_mmio_addr(pbus, sbdev, 0); + nodename = g_strdup_printf("%s/tpm_tis@%" PRIx64, parent_node, mmio_base); + qemu_fdt_add_subnode(fdt, nodename); + + qemu_fdt_setprop_string(fdt, nodename, "compatible", "tcg,tpm-tis-mmio"); + + reg_attr[0] = cpu_to_be32(mmio_base); + reg_attr[1] = cpu_to_be32(0x5000); + qemu_fdt_setprop(fdt, nodename, "reg", reg_attr, 2 * sizeof(uint32_t)); + + g_free(nodename); + return 0; +} + static int no_fdt_node(SysBusDevice *sbdev, void *opaque) { return 0; @@ -456,6 +488,7 @@ static const BindingEntry bindings[] = { TYPE_BINDING(TYPE_VFIO_AMD_XGBE, add_amd_xgbe_fdt_node), VFIO_PLATFORM_BINDING("amd,xgbe-seattle-v1a", add_amd_xgbe_fdt_node), #endif + TYPE_BINDING(TYPE_TPM_TIS_SYSBUS, add_tpm_tis_fdt_node), TYPE_BINDING(TYPE_RAMFB_DEVICE, no_fdt_node), TYPE_BINDING("", NULL), /* last element */ }; diff --git a/hw/arm/tosa.c b/hw/arm/tosa.c index 4d95a1f3e2..5dee2d76c6 100644 --- a/hw/arm/tosa.c +++ b/hw/arm/tosa.c @@ -226,8 +226,7 @@ static void tosa_init(MachineState *machine) mpu = pxa255_init(address_space_mem, tosa_binfo.ram_size); - memory_region_init_ram(rom, NULL, "tosa.rom", TOSA_ROM, &error_fatal); - memory_region_set_readonly(rom, true); + memory_region_init_rom(rom, NULL, "tosa.rom", TOSA_ROM, &error_fatal); memory_region_add_subregion(address_space_mem, 0, rom); tmio = tc6393xb_init(address_space_mem, 0x10000000, diff --git a/hw/arm/versatilepb.c b/hw/arm/versatilepb.c index e86af01537..f3c4a50b19 100644 --- a/hw/arm/versatilepb.c +++ b/hw/arm/versatilepb.c @@ -184,7 +184,6 @@ static void versatile_init(MachineState *machine, int board_id) Object *cpuobj; ARMCPU *cpu; MemoryRegion *sysmem = get_system_memory(); - MemoryRegion *ram = g_new(MemoryRegion, 1); qemu_irq pic[32]; qemu_irq sic[32]; DeviceState *dev, *sysctl; @@ -220,11 +219,9 @@ static void versatile_init(MachineState *machine, int board_id) cpu = ARM_CPU(cpuobj); - memory_region_allocate_system_memory(ram, NULL, "versatile.ram", - machine->ram_size); /* ??? RAM should repeat to fill physical memory space. */ /* SDRAM at address zero. */ - memory_region_add_subregion(sysmem, 0, ram); + memory_region_add_subregion(sysmem, 0, machine->ram); sysctl = qdev_create(NULL, "realview_sysctl"); qdev_prop_set_uint32(sysctl, "sys_id", 0x41007004); @@ -398,6 +395,7 @@ static void versatilepb_class_init(ObjectClass *oc, void *data) mc->block_default_type = IF_SCSI; mc->ignore_memory_transaction_failures = true; mc->default_cpu_type = ARM_CPU_TYPE_NAME("arm926"); + mc->default_ram_id = "versatile.ram"; } static const TypeInfo versatilepb_type = { @@ -415,6 +413,7 @@ static void versatileab_class_init(ObjectClass *oc, void *data) mc->block_default_type = IF_SCSI; mc->ignore_memory_transaction_failures = true; mc->default_cpu_type = ARM_CPU_TYPE_NAME("arm926"); + mc->default_ram_id = "versatile.ram"; } static const TypeInfo versatileab_type = { diff --git a/hw/arm/vexpress.c b/hw/arm/vexpress.c index 4673a88a8d..ed683eeea5 100644 --- a/hw/arm/vexpress.c +++ b/hw/arm/vexpress.c @@ -273,7 +273,6 @@ static void a9_daughterboard_init(const VexpressMachineState *vms, { MachineState *machine = MACHINE(vms); MemoryRegion *sysmem = get_system_memory(); - MemoryRegion *ram = g_new(MemoryRegion, 1); MemoryRegion *lowram = g_new(MemoryRegion, 1); ram_addr_t low_ram_size; @@ -283,8 +282,6 @@ static void a9_daughterboard_init(const VexpressMachineState *vms, exit(1); } - memory_region_allocate_system_memory(ram, NULL, "vexpress.highmem", - ram_size); low_ram_size = ram_size; if (low_ram_size > 0x4000000) { low_ram_size = 0x4000000; @@ -293,9 +290,10 @@ static void a9_daughterboard_init(const VexpressMachineState *vms, * address space should in theory be remappable to various * things including ROM or RAM; we always map the RAM there. */ - memory_region_init_alias(lowram, NULL, "vexpress.lowmem", ram, 0, low_ram_size); + memory_region_init_alias(lowram, NULL, "vexpress.lowmem", machine->ram, + 0, low_ram_size); memory_region_add_subregion(sysmem, 0x0, lowram); - memory_region_add_subregion(sysmem, 0x60000000, ram); + memory_region_add_subregion(sysmem, 0x60000000, machine->ram); /* 0x1e000000 A9MPCore (SCU) private memory region */ init_cpus(machine, cpu_type, TYPE_A9MPCORE_PRIV, 0x1e000000, pic, @@ -360,7 +358,6 @@ static void a15_daughterboard_init(const VexpressMachineState *vms, { MachineState *machine = MACHINE(vms); MemoryRegion *sysmem = get_system_memory(); - MemoryRegion *ram = g_new(MemoryRegion, 1); MemoryRegion *sram = g_new(MemoryRegion, 1); { @@ -375,10 +372,8 @@ static void a15_daughterboard_init(const VexpressMachineState *vms, } } - memory_region_allocate_system_memory(ram, NULL, "vexpress.highmem", - ram_size); /* RAM is from 0x80000000 upwards; there is no low-memory alias for it. */ - memory_region_add_subregion(sysmem, 0x80000000, ram); + memory_region_add_subregion(sysmem, 0x80000000, machine->ram); /* 0x2c000000 A15MPCore private memory region (GIC) */ init_cpus(machine, cpu_type, TYPE_A15MPCORE_PRIV, @@ -795,6 +790,7 @@ static void vexpress_class_init(ObjectClass *oc, void *data) mc->init = vexpress_common_init; mc->max_cpus = 4; mc->ignore_memory_transaction_failures = true; + mc->default_ram_id = "vexpress.highmem"; } static void vexpress_a9_class_init(ObjectClass *oc, void *data) diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index 4cd50175e0..81d41a3990 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -78,11 +78,6 @@ static void acpi_dsdt_add_uart(Aml *scope, const MemMapEntry *uart_memmap, AML_EXCLUSIVE, &uart_irq, 1)); aml_append(dev, aml_name_decl("_CRS", crs)); - /* The _ADR entry is used to link this device to the UART described - * in the SPCR table, i.e. SPCR.base_address.address == _ADR. - */ - aml_append(dev, aml_name_decl("_ADR", aml_int(uart_memmap->base))); - aml_append(scope, dev); } @@ -156,7 +151,7 @@ static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap, { int ecam_id = VIRT_ECAM_ID(highmem_ecam); Aml *method, *crs, *ifctx, *UUID, *ifctx1, *elsectx, *buf; - int i, bus_no; + int i, slot_no; hwaddr base_mmio = memmap[VIRT_PCIE_MMIO].base; hwaddr size_mmio = memmap[VIRT_PCIE_MMIO].size; hwaddr base_pio = memmap[VIRT_PCIE_PIO].base; @@ -170,18 +165,17 @@ static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap, aml_append(dev, aml_name_decl("_CID", aml_string("PNP0A03"))); aml_append(dev, aml_name_decl("_SEG", aml_int(0))); aml_append(dev, aml_name_decl("_BBN", aml_int(0))); - aml_append(dev, aml_name_decl("_ADR", aml_int(0))); aml_append(dev, aml_name_decl("_UID", aml_string("PCI0"))); aml_append(dev, aml_name_decl("_STR", aml_unicode("PCIe 0 Device"))); aml_append(dev, aml_name_decl("_CCA", aml_int(1))); /* Declare the PCI Routing Table. */ - Aml *rt_pkg = aml_varpackage(nr_pcie_buses * PCI_NUM_PINS); - for (bus_no = 0; bus_no < nr_pcie_buses; bus_no++) { + Aml *rt_pkg = aml_varpackage(PCI_SLOT_MAX * PCI_NUM_PINS); + for (slot_no = 0; slot_no < PCI_SLOT_MAX; slot_no++) { for (i = 0; i < PCI_NUM_PINS; i++) { - int gsi = (i + bus_no) % PCI_NUM_PINS; + int gsi = (i + slot_no) % PCI_NUM_PINS; Aml *pkg = aml_package(4); - aml_append(pkg, aml_int((bus_no << 16) | 0xFFFF)); + aml_append(pkg, aml_int((slot_no << 16) | 0xFFFF)); aml_append(pkg, aml_int(i)); aml_append(pkg, aml_name("GSI%d", gsi)); aml_append(pkg, aml_int(0)); @@ -195,7 +189,7 @@ static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap, uint32_t irqs = irq + i; Aml *dev_gsi = aml_device("GSI%d", i); aml_append(dev_gsi, aml_name_decl("_HID", aml_string("PNP0C0F"))); - aml_append(dev_gsi, aml_name_decl("_UID", aml_int(0))); + aml_append(dev_gsi, aml_name_decl("_UID", aml_int(i))); crs = aml_resource_template(); aml_append(crs, aml_interrupt(AML_CONSUMER, AML_LEVEL, AML_ACTIVE_HIGH, @@ -242,7 +236,6 @@ static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap, size_mmio_high)); } - aml_append(method, aml_name_decl("RBUF", rbuf)); aml_append(method, aml_return(rbuf)); aml_append(dev, method); @@ -267,17 +260,22 @@ static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap, aml_create_dword_field(aml_arg(3), aml_int(8), "CDW3")); aml_append(ifctx, aml_store(aml_name("CDW2"), aml_name("SUPP"))); aml_append(ifctx, aml_store(aml_name("CDW3"), aml_name("CTRL"))); - aml_append(ifctx, aml_store(aml_and(aml_name("CTRL"), aml_int(0x1D), NULL), - aml_name("CTRL"))); + + /* + * Allow OS control for all 5 features: + * PCIeHotplug SHPCHotplug PME AER PCIeCapability. + */ + aml_append(ifctx, aml_and(aml_name("CTRL"), aml_int(0x1F), + aml_name("CTRL"))); ifctx1 = aml_if(aml_lnot(aml_equal(aml_arg(1), aml_int(0x1)))); - aml_append(ifctx1, aml_store(aml_or(aml_name("CDW1"), aml_int(0x08), NULL), - aml_name("CDW1"))); + aml_append(ifctx1, aml_or(aml_name("CDW1"), aml_int(0x08), + aml_name("CDW1"))); aml_append(ifctx, ifctx1); ifctx1 = aml_if(aml_lnot(aml_equal(aml_name("CDW3"), aml_name("CTRL")))); - aml_append(ifctx1, aml_store(aml_or(aml_name("CDW1"), aml_int(0x10), NULL), - aml_name("CDW1"))); + aml_append(ifctx1, aml_or(aml_name("CDW1"), aml_int(0x10), + aml_name("CDW1"))); aml_append(ifctx, ifctx1); aml_append(ifctx, aml_store(aml_name("CTRL"), aml_name("CDW3"))); @@ -285,8 +283,8 @@ static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap, aml_append(method, ifctx); elsectx = aml_else(); - aml_append(elsectx, aml_store(aml_or(aml_name("CDW1"), aml_int(4), NULL), - aml_name("CDW1"))); + aml_append(elsectx, aml_or(aml_name("CDW1"), aml_int(4), + aml_name("CDW1"))); aml_append(elsectx, aml_return(aml_arg(3))); aml_append(method, elsectx); aml_append(dev, method); @@ -312,10 +310,6 @@ static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap, aml_append(method, aml_return(buf)); aml_append(dev, method); - Aml *dev_rp0 = aml_device("%s", "RP0"); - aml_append(dev_rp0, aml_name_decl("_ADR", aml_int(0))); - aml_append(dev, dev_rp0); - Aml *dev_res0 = aml_device("%s", "RES0"); aml_append(dev_res0, aml_name_decl("_HID", aml_string("PNP0C02"))); crs = aml_resource_template(); @@ -333,7 +327,6 @@ static void acpi_dsdt_add_gpio(Aml *scope, const MemMapEntry *gpio_memmap, { Aml *dev = aml_device("GPO0"); aml_append(dev, aml_name_decl("_HID", aml_string("ARMH0061"))); - aml_append(dev, aml_name_decl("_ADR", aml_int(0))); aml_append(dev, aml_name_decl("_UID", aml_int(0))); Aml *crs = aml_resource_template(); @@ -363,7 +356,6 @@ static void acpi_dsdt_add_power_button(Aml *scope) { Aml *dev = aml_device(ACPI_POWER_BUTTON_DEVICE); aml_append(dev, aml_name_decl("_HID", aml_string("PNP0C0C"))); - aml_append(dev, aml_name_decl("_ADR", aml_int(0))); aml_append(dev, aml_name_decl("_UID", aml_int(0))); aml_append(scope, dev); } @@ -918,7 +910,7 @@ void virt_acpi_setup(VirtMachineState *vms) return; } - if (!acpi_enabled) { + if (!virt_is_acpi_enabled(vms)) { trace_virt_acpi_setup(); return; } @@ -937,7 +929,7 @@ void virt_acpi_setup(VirtMachineState *vms) build_state->linker_mr = acpi_add_rom_blob(virt_acpi_build_update, build_state, - tables.linker->cmd_blob, "etc/table-loader", 0); + tables.linker->cmd_blob, ACPI_BUILD_LOADER_FILE, 0); fw_cfg_add_file(vms->fw_cfg, ACPI_BUILD_TPMLOG_FILE, tables.tcpalog->data, acpi_data_len(tables.tcpalog)); diff --git a/hw/arm/virt.c b/hw/arm/virt.c index d4bedc2607..7dc96abf72 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -32,6 +32,7 @@ #include "qemu-common.h" #include "qemu/units.h" #include "qemu/option.h" +#include "monitor/qdev.h" #include "qapi/error.h" #include "hw/sysbus.h" #include "hw/boards.h" @@ -47,6 +48,7 @@ #include "sysemu/numa.h" #include "sysemu/runstate.h" #include "sysemu/sysemu.h" +#include "sysemu/tpm.h" #include "sysemu/kvm.h" #include "hw/loader.h" #include "exec/address-spaces.h" @@ -54,6 +56,7 @@ #include "qemu/error-report.h" #include "qemu/module.h" #include "hw/pci-host/gpex.h" +#include "hw/virtio/virtio-pci.h" #include "hw/arm/sysbus-fdt.h" #include "hw/platform-bus.h" #include "hw/qdev-properties.h" @@ -64,6 +67,7 @@ #include "kvm_arm.h" #include "hw/firmware/smbios.h" #include "qapi/visitor.h" +#include "qapi/qapi-visit-common.h" #include "standard-headers/linux/input.h" #include "hw/arm/smmuv3.h" #include "hw/acpi/acpi.h" @@ -71,6 +75,8 @@ #include "hw/mem/pc-dimm.h" #include "hw/mem/nvdimm.h" #include "hw/acpi/generic_event_device.h" +#include "hw/virtio/virtio-iommu.h" +#include "hw/char/pl011.h" #define DEFINE_VIRT_MACHINE_LATEST(major, minor, latest) \ static void virt_##major##_##minor##_class_init(ObjectClass *oc, \ @@ -294,7 +300,7 @@ static void fdt_add_timer_nodes(const VirtMachineState *vms) irqflags = GIC_FDT_IRQ_FLAGS_EDGE_LO_HI; } - if (vms->gic_version == 2) { + if (vms->gic_version == VIRT_GIC_VERSION_2) { irqflags = deposit32(irqflags, GIC_FDT_IRQ_PPI_CPU_START, GIC_FDT_IRQ_PPI_CPU_WIDTH, (1 << vms->smp_cpus) - 1); @@ -435,7 +441,7 @@ static void fdt_add_gic_node(VirtMachineState *vms) qemu_fdt_setprop_cell(vms->fdt, nodename, "#address-cells", 0x2); qemu_fdt_setprop_cell(vms->fdt, nodename, "#size-cells", 0x2); qemu_fdt_setprop(vms->fdt, nodename, "ranges", NULL, 0); - if (vms->gic_version == 3) { + if (vms->gic_version == VIRT_GIC_VERSION_3) { int nb_redist_regions = virt_gicv3_redist_region_count(vms); qemu_fdt_setprop_string(vms->fdt, nodename, "compatible", @@ -514,7 +520,7 @@ static void fdt_add_pmu_nodes(const VirtMachineState *vms) } } - if (vms->gic_version == 2) { + if (vms->gic_version == VIRT_GIC_VERSION_2) { irqflags = deposit32(irqflags, GIC_FDT_IRQ_PPI_CPU_START, GIC_FDT_IRQ_PPI_CPU_WIDTH, (1 << vms->smp_cpus) - 1); @@ -531,7 +537,7 @@ static void fdt_add_pmu_nodes(const VirtMachineState *vms) } } -static inline DeviceState *create_acpi_ged(VirtMachineState *vms, qemu_irq *pic) +static inline DeviceState *create_acpi_ged(VirtMachineState *vms) { DeviceState *dev; MachineState *ms = MACHINE(vms); @@ -547,14 +553,14 @@ static inline DeviceState *create_acpi_ged(VirtMachineState *vms, qemu_irq *pic) sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, vms->memmap[VIRT_ACPI_GED].base); sysbus_mmio_map(SYS_BUS_DEVICE(dev), 1, vms->memmap[VIRT_PCDIMM_ACPI].base); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[irq]); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, qdev_get_gpio_in(vms->gic, irq)); qdev_init_nofail(dev); return dev; } -static void create_its(VirtMachineState *vms, DeviceState *gicdev) +static void create_its(VirtMachineState *vms) { const char *itsclass = its_class_name(); DeviceState *dev; @@ -566,7 +572,7 @@ static void create_its(VirtMachineState *vms, DeviceState *gicdev) dev = qdev_create(NULL, itsclass); - object_property_set_link(OBJECT(dev), OBJECT(gicdev), "parent-gicv3", + object_property_set_link(OBJECT(dev), OBJECT(vms->gic), "parent-gicv3", &error_abort); qdev_init_nofail(dev); sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, vms->memmap[VIRT_GIC_ITS].base); @@ -574,7 +580,7 @@ static void create_its(VirtMachineState *vms, DeviceState *gicdev) fdt_add_its_gic_node(vms); } -static void create_v2m(VirtMachineState *vms, qemu_irq *pic) +static void create_v2m(VirtMachineState *vms) { int i; int irq = vms->irqmap[VIRT_GIC_V2M]; @@ -587,17 +593,17 @@ static void create_v2m(VirtMachineState *vms, qemu_irq *pic) qdev_init_nofail(dev); for (i = 0; i < NUM_GICV2M_SPIS; i++) { - sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, pic[irq + i]); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, + qdev_get_gpio_in(vms->gic, irq + i)); } fdt_add_v2m_gic_node(vms); } -static void create_gic(VirtMachineState *vms, qemu_irq *pic) +static void create_gic(VirtMachineState *vms) { MachineState *ms = MACHINE(vms); /* We create a standalone GIC */ - DeviceState *gicdev; SysBusDevice *gicbusdev; const char *gictype; int type = vms->gic_version, i; @@ -606,15 +612,15 @@ static void create_gic(VirtMachineState *vms, qemu_irq *pic) gictype = (type == 3) ? gicv3_class_name() : gic_class_name(); - gicdev = qdev_create(NULL, gictype); - qdev_prop_set_uint32(gicdev, "revision", type); - qdev_prop_set_uint32(gicdev, "num-cpu", smp_cpus); + vms->gic = qdev_create(NULL, gictype); + qdev_prop_set_uint32(vms->gic, "revision", type); + qdev_prop_set_uint32(vms->gic, "num-cpu", smp_cpus); /* Note that the num-irq property counts both internal and external * interrupts; there are always 32 of the former (mandated by GIC spec). */ - qdev_prop_set_uint32(gicdev, "num-irq", NUM_IRQS + 32); + qdev_prop_set_uint32(vms->gic, "num-irq", NUM_IRQS + 32); if (!kvm_irqchip_in_kernel()) { - qdev_prop_set_bit(gicdev, "has-security-extensions", vms->secure); + qdev_prop_set_bit(vms->gic, "has-security-extensions", vms->secure); } if (type == 3) { @@ -624,25 +630,25 @@ static void create_gic(VirtMachineState *vms, qemu_irq *pic) nb_redist_regions = virt_gicv3_redist_region_count(vms); - qdev_prop_set_uint32(gicdev, "len-redist-region-count", + qdev_prop_set_uint32(vms->gic, "len-redist-region-count", nb_redist_regions); - qdev_prop_set_uint32(gicdev, "redist-region-count[0]", redist0_count); + qdev_prop_set_uint32(vms->gic, "redist-region-count[0]", redist0_count); if (nb_redist_regions == 2) { uint32_t redist1_capacity = vms->memmap[VIRT_HIGH_GIC_REDIST2].size / GICV3_REDIST_SIZE; - qdev_prop_set_uint32(gicdev, "redist-region-count[1]", + qdev_prop_set_uint32(vms->gic, "redist-region-count[1]", MIN(smp_cpus - redist0_count, redist1_capacity)); } } else { if (!kvm_irqchip_in_kernel()) { - qdev_prop_set_bit(gicdev, "has-virtualization-extensions", + qdev_prop_set_bit(vms->gic, "has-virtualization-extensions", vms->virt); } } - qdev_init_nofail(gicdev); - gicbusdev = SYS_BUS_DEVICE(gicdev); + qdev_init_nofail(vms->gic); + gicbusdev = SYS_BUS_DEVICE(vms->gic); sysbus_mmio_map(gicbusdev, 0, vms->memmap[VIRT_GIC_DIST].base); if (type == 3) { sysbus_mmio_map(gicbusdev, 1, vms->memmap[VIRT_GIC_REDIST].base); @@ -678,23 +684,23 @@ static void create_gic(VirtMachineState *vms, qemu_irq *pic) for (irq = 0; irq < ARRAY_SIZE(timer_irq); irq++) { qdev_connect_gpio_out(cpudev, irq, - qdev_get_gpio_in(gicdev, + qdev_get_gpio_in(vms->gic, ppibase + timer_irq[irq])); } if (type == 3) { - qemu_irq irq = qdev_get_gpio_in(gicdev, + qemu_irq irq = qdev_get_gpio_in(vms->gic, ppibase + ARCH_GIC_MAINT_IRQ); qdev_connect_gpio_out_named(cpudev, "gicv3-maintenance-interrupt", 0, irq); } else if (vms->virt) { - qemu_irq irq = qdev_get_gpio_in(gicdev, + qemu_irq irq = qdev_get_gpio_in(vms->gic, ppibase + ARCH_GIC_MAINT_IRQ); sysbus_connect_irq(gicbusdev, i + 4 * smp_cpus, irq); } qdev_connect_gpio_out_named(cpudev, "pmu-interrupt", 0, - qdev_get_gpio_in(gicdev, ppibase + qdev_get_gpio_in(vms->gic, ppibase + VIRTUAL_PMU_IRQ)); sysbus_connect_irq(gicbusdev, i, qdev_get_gpio_in(cpudev, ARM_CPU_IRQ)); @@ -706,20 +712,16 @@ static void create_gic(VirtMachineState *vms, qemu_irq *pic) qdev_get_gpio_in(cpudev, ARM_CPU_VFIQ)); } - for (i = 0; i < NUM_IRQS; i++) { - pic[i] = qdev_get_gpio_in(gicdev, i); - } - fdt_add_gic_node(vms); if (type == 3 && vms->its) { - create_its(vms, gicdev); + create_its(vms); } else if (type == 2) { - create_v2m(vms, pic); + create_v2m(vms); } } -static void create_uart(const VirtMachineState *vms, qemu_irq *pic, int uart, +static void create_uart(const VirtMachineState *vms, int uart, MemoryRegion *mem, Chardev *chr) { char *nodename; @@ -728,14 +730,14 @@ static void create_uart(const VirtMachineState *vms, qemu_irq *pic, int uart, int irq = vms->irqmap[uart]; const char compat[] = "arm,pl011\0arm,primecell"; const char clocknames[] = "uartclk\0apb_pclk"; - DeviceState *dev = qdev_create(NULL, "pl011"); + DeviceState *dev = qdev_create(NULL, TYPE_PL011); SysBusDevice *s = SYS_BUS_DEVICE(dev); qdev_prop_set_chr(dev, "chardev", chr); qdev_init_nofail(dev); memory_region_add_subregion(mem, base, sysbus_mmio_get_region(s, 0)); - sysbus_connect_irq(s, 0, pic[irq]); + sysbus_connect_irq(s, 0, qdev_get_gpio_in(vms->gic, irq)); nodename = g_strdup_printf("/pl011@%" PRIx64, base); qemu_fdt_add_subnode(vms->fdt, nodename); @@ -767,7 +769,7 @@ static void create_uart(const VirtMachineState *vms, qemu_irq *pic, int uart, g_free(nodename); } -static void create_rtc(const VirtMachineState *vms, qemu_irq *pic) +static void create_rtc(const VirtMachineState *vms) { char *nodename; hwaddr base = vms->memmap[VIRT_RTC].base; @@ -775,7 +777,7 @@ static void create_rtc(const VirtMachineState *vms, qemu_irq *pic) int irq = vms->irqmap[VIRT_RTC]; const char compat[] = "arm,pl031\0arm,primecell"; - sysbus_create_simple("pl031", base, pic[irq]); + sysbus_create_simple("pl031", base, qdev_get_gpio_in(vms->gic, irq)); nodename = g_strdup_printf("/pl031@%" PRIx64, base); qemu_fdt_add_subnode(vms->fdt, nodename); @@ -803,7 +805,7 @@ static void virt_powerdown_req(Notifier *n, void *opaque) } } -static void create_gpio(const VirtMachineState *vms, qemu_irq *pic) +static void create_gpio(const VirtMachineState *vms) { char *nodename; DeviceState *pl061_dev; @@ -812,7 +814,8 @@ static void create_gpio(const VirtMachineState *vms, qemu_irq *pic) int irq = vms->irqmap[VIRT_GPIO]; const char compat[] = "arm,pl061\0arm,primecell"; - pl061_dev = sysbus_create_simple("pl061", base, pic[irq]); + pl061_dev = sysbus_create_simple("pl061", base, + qdev_get_gpio_in(vms->gic, irq)); uint32_t phandle = qemu_fdt_alloc_phandle(vms->fdt); nodename = g_strdup_printf("/pl061@%" PRIx64, base); @@ -846,7 +849,7 @@ static void create_gpio(const VirtMachineState *vms, qemu_irq *pic) g_free(nodename); } -static void create_virtio_devices(const VirtMachineState *vms, qemu_irq *pic) +static void create_virtio_devices(const VirtMachineState *vms) { int i; hwaddr size = vms->memmap[VIRT_MMIO].size; @@ -882,7 +885,8 @@ static void create_virtio_devices(const VirtMachineState *vms, qemu_irq *pic) int irq = vms->irqmap[VIRT_MMIO] + i; hwaddr base = vms->memmap[VIRT_MMIO].base + i * size; - sysbus_create_simple("virtio-mmio", base, pic[irq]); + sysbus_create_simple("virtio-mmio", base, + qdev_get_gpio_in(vms->gic, irq)); } /* We add dtb nodes in reverse order so that they appear in the finished @@ -1131,7 +1135,7 @@ static void create_pcie_irq_map(const VirtMachineState *vms, 0x7 /* PCI irq */); } -static void create_smmu(const VirtMachineState *vms, qemu_irq *pic, +static void create_smmu(const VirtMachineState *vms, PCIBus *bus) { char *node; @@ -1154,7 +1158,8 @@ static void create_smmu(const VirtMachineState *vms, qemu_irq *pic, qdev_init_nofail(dev); sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); for (i = 0; i < NUM_SMMU_IRQS; i++) { - sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, pic[irq + i]); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, + qdev_get_gpio_in(vms->gic, irq + i)); } node = g_strdup_printf("/smmuv3@%" PRIx64, base); @@ -1181,7 +1186,31 @@ static void create_smmu(const VirtMachineState *vms, qemu_irq *pic, g_free(node); } -static void create_pcie(VirtMachineState *vms, qemu_irq *pic) +static void create_virtio_iommu_dt_bindings(VirtMachineState *vms, Error **errp) +{ + const char compat[] = "virtio,pci-iommu"; + uint16_t bdf = vms->virtio_iommu_bdf; + char *node; + + vms->iommu_phandle = qemu_fdt_alloc_phandle(vms->fdt); + + node = g_strdup_printf("%s/virtio_iommu@%d", vms->pciehb_nodename, bdf); + qemu_fdt_add_subnode(vms->fdt, node); + qemu_fdt_setprop(vms->fdt, node, "compatible", compat, sizeof(compat)); + qemu_fdt_setprop_sized_cells(vms->fdt, node, "reg", + 1, bdf << 8, 1, 0, 1, 0, + 1, 0, 1, 0); + + qemu_fdt_setprop_cell(vms->fdt, node, "#iommu-cells", 1); + qemu_fdt_setprop_cell(vms->fdt, node, "phandle", vms->iommu_phandle); + g_free(node); + + qemu_fdt_setprop_cells(vms->fdt, vms->pciehb_nodename, "iommu-map", + 0x0, vms->iommu_phandle, 0x0, bdf, + bdf + 1, vms->iommu_phandle, bdf + 1, 0xffff - bdf); +} + +static void create_pcie(VirtMachineState *vms) { hwaddr base_mmio = vms->memmap[VIRT_PCIE_MMIO].base; hwaddr size_mmio = vms->memmap[VIRT_PCIE_MMIO].size; @@ -1241,7 +1270,8 @@ static void create_pcie(VirtMachineState *vms, qemu_irq *pic) sysbus_mmio_map(SYS_BUS_DEVICE(dev), 2, base_pio); for (i = 0; i < GPEX_NUM_IRQS; i++) { - sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, pic[irq + i]); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, + qdev_get_gpio_in(vms->gic, irq + i)); gpex_set_irq_num(GPEX_HOST(dev), i, irq + i); } @@ -1258,7 +1288,7 @@ static void create_pcie(VirtMachineState *vms, qemu_irq *pic) } } - nodename = g_strdup_printf("/pcie@%" PRIx64, base); + nodename = vms->pciehb_nodename = g_strdup_printf("/pcie@%" PRIx64, base); qemu_fdt_add_subnode(vms->fdt, nodename); qemu_fdt_setprop_string(vms->fdt, nodename, "compatible", "pci-host-ecam-generic"); @@ -1301,16 +1331,19 @@ static void create_pcie(VirtMachineState *vms, qemu_irq *pic) if (vms->iommu) { vms->iommu_phandle = qemu_fdt_alloc_phandle(vms->fdt); - create_smmu(vms, pic, pci->bus); - - qemu_fdt_setprop_cells(vms->fdt, nodename, "iommu-map", - 0x0, vms->iommu_phandle, 0x0, 0x10000); + switch (vms->iommu) { + case VIRT_IOMMU_SMMUV3: + create_smmu(vms, pci->bus); + qemu_fdt_setprop_cells(vms->fdt, nodename, "iommu-map", + 0x0, vms->iommu_phandle, 0x0, 0x10000); + break; + default: + g_assert_not_reached(); + } } - - g_free(nodename); } -static void create_platform_bus(VirtMachineState *vms, qemu_irq *pic) +static void create_platform_bus(VirtMachineState *vms) { DeviceState *dev; SysBusDevice *s; @@ -1326,8 +1359,8 @@ static void create_platform_bus(VirtMachineState *vms, qemu_irq *pic) s = SYS_BUS_DEVICE(dev); for (i = 0; i < PLATFORM_BUS_NUM_IRQS; i++) { - int irqn = vms->irqmap[VIRT_PLATFORM_BUS] + i; - sysbus_connect_irq(s, i, pic[irqn]); + int irq = vms->irqmap[VIRT_PLATFORM_BUS] + i; + sysbus_connect_irq(s, i, qdev_get_gpio_in(vms->gic, irq)); } memory_region_add_subregion(sysmem, @@ -1438,7 +1471,7 @@ static uint64_t virt_cpu_mp_affinity(VirtMachineState *vms, int idx) * purposes are to make TCG consistent (with 64-bit KVM hosts) * and to improve SGI efficiency. */ - if (vms->gic_version == 3) { + if (vms->gic_version == VIRT_GIC_VERSION_3) { clustersz = GICV3_TARGETLIST_BITS; } else { clustersz = GIC_TARGETLIST_BITS; @@ -1503,17 +1536,114 @@ static void virt_set_memmap(VirtMachineState *vms) } } +/* + * finalize_gic_version - Determines the final gic_version + * according to the gic-version property + * + * Default GIC type is v2 + */ +static void finalize_gic_version(VirtMachineState *vms) +{ + unsigned int max_cpus = MACHINE(vms)->smp.max_cpus; + + if (kvm_enabled()) { + int probe_bitmap; + + if (!kvm_irqchip_in_kernel()) { + switch (vms->gic_version) { + case VIRT_GIC_VERSION_HOST: + warn_report( + "gic-version=host not relevant with kernel-irqchip=off " + "as only userspace GICv2 is supported. Using v2 ..."); + return; + case VIRT_GIC_VERSION_MAX: + case VIRT_GIC_VERSION_NOSEL: + vms->gic_version = VIRT_GIC_VERSION_2; + return; + case VIRT_GIC_VERSION_2: + return; + case VIRT_GIC_VERSION_3: + error_report( + "gic-version=3 is not supported with kernel-irqchip=off"); + exit(1); + } + } + + probe_bitmap = kvm_arm_vgic_probe(); + if (!probe_bitmap) { + error_report("Unable to determine GIC version supported by host"); + exit(1); + } + + switch (vms->gic_version) { + case VIRT_GIC_VERSION_HOST: + case VIRT_GIC_VERSION_MAX: + if (probe_bitmap & KVM_ARM_VGIC_V3) { + vms->gic_version = VIRT_GIC_VERSION_3; + } else { + vms->gic_version = VIRT_GIC_VERSION_2; + } + return; + case VIRT_GIC_VERSION_NOSEL: + if ((probe_bitmap & KVM_ARM_VGIC_V2) && max_cpus <= GIC_NCPU) { + vms->gic_version = VIRT_GIC_VERSION_2; + } else if (probe_bitmap & KVM_ARM_VGIC_V3) { + /* + * in case the host does not support v2 in-kernel emulation or + * the end-user requested more than 8 VCPUs we now default + * to v3. In any case defaulting to v2 would be broken. + */ + vms->gic_version = VIRT_GIC_VERSION_3; + } else if (max_cpus > GIC_NCPU) { + error_report("host only supports in-kernel GICv2 emulation " + "but more than 8 vcpus are requested"); + exit(1); + } + break; + case VIRT_GIC_VERSION_2: + case VIRT_GIC_VERSION_3: + break; + } + + /* Check chosen version is effectively supported by the host */ + if (vms->gic_version == VIRT_GIC_VERSION_2 && + !(probe_bitmap & KVM_ARM_VGIC_V2)) { + error_report("host does not support in-kernel GICv2 emulation"); + exit(1); + } else if (vms->gic_version == VIRT_GIC_VERSION_3 && + !(probe_bitmap & KVM_ARM_VGIC_V3)) { + error_report("host does not support in-kernel GICv3 emulation"); + exit(1); + } + return; + } + + /* TCG mode */ + switch (vms->gic_version) { + case VIRT_GIC_VERSION_NOSEL: + vms->gic_version = VIRT_GIC_VERSION_2; + break; + case VIRT_GIC_VERSION_MAX: + vms->gic_version = VIRT_GIC_VERSION_3; + break; + case VIRT_GIC_VERSION_HOST: + error_report("gic-version=host requires KVM"); + exit(1); + case VIRT_GIC_VERSION_2: + case VIRT_GIC_VERSION_3: + break; + } +} + static void machvirt_init(MachineState *machine) { VirtMachineState *vms = VIRT_MACHINE(machine); VirtMachineClass *vmc = VIRT_MACHINE_GET_CLASS(machine); MachineClass *mc = MACHINE_GET_CLASS(machine); const CPUArchIdList *possible_cpus; - qemu_irq pic[NUM_IRQS]; MemoryRegion *sysmem = get_system_memory(); MemoryRegion *secure_sysmem = NULL; int n, virt_max_cpus; - MemoryRegion *ram = g_new(MemoryRegion, 1); bool firmware_loaded; bool aarch64 = true; bool has_ged = !vmc->no_ged; @@ -1531,25 +1661,7 @@ static void machvirt_init(MachineState *machine) /* We can probe only here because during property set * KVM is not available yet */ - if (vms->gic_version <= 0) { - /* "host" or "max" */ - if (!kvm_enabled()) { - if (vms->gic_version == 0) { - error_report("gic-version=host requires KVM"); - exit(1); - } else { - /* "max": currently means 3 for TCG */ - vms->gic_version = 3; - } - } else { - vms->gic_version = kvm_arm_vgic_probe(); - if (!vms->gic_version) { - error_report( - "Unable to determine GIC version supported by host"); - exit(1); - } - } - } + finalize_gic_version(vms); if (!cpu_type_valid(machine->cpu_type)) { error_report("mach-virt: CPU type %s not supported", machine->cpu_type); @@ -1598,7 +1710,7 @@ static void machvirt_init(MachineState *machine) /* The maximum number of CPUs depends on the GIC version, or on how * many redistributors we can fit into the memory map. */ - if (vms->gic_version == 3) { + if (vms->gic_version == VIRT_GIC_VERSION_3) { virt_max_cpus = vms->memmap[VIRT_GIC_REDIST].size / GICV3_REDIST_SIZE; virt_max_cpus += @@ -1664,6 +1776,11 @@ static void machvirt_init(MachineState *machine) } } + if (vmc->kvm_no_adjvtime && + object_property_find(cpuobj, "kvm-no-adjvtime", NULL)) { + object_property_set_bool(cpuobj, true, "kvm-no-adjvtime", NULL); + } + if (vmc->no_pmu && object_property_find(cpuobj, "pmu", NULL)) { object_property_set_bool(cpuobj, false, "pmu", NULL); } @@ -1702,9 +1819,8 @@ static void machvirt_init(MachineState *machine) } } - memory_region_allocate_system_memory(ram, NULL, "mach-virt.ram", - machine->ram_size); - memory_region_add_subregion(sysmem, vms->memmap[VIRT_MEM].base, ram); + memory_region_add_subregion(sysmem, vms->memmap[VIRT_MEM].base, + machine->ram); if (machine->device_memory) { memory_region_add_subregion(sysmem, machine->device_memory->base, &machine->device_memory->mr); @@ -1712,27 +1828,27 @@ static void machvirt_init(MachineState *machine) virt_flash_fdt(vms, sysmem, secure_sysmem ?: sysmem); - create_gic(vms, pic); + create_gic(vms); fdt_add_pmu_nodes(vms); - create_uart(vms, pic, VIRT_UART, sysmem, serial_hd(0)); + create_uart(vms, VIRT_UART, sysmem, serial_hd(0)); if (vms->secure) { create_secure_ram(vms, secure_sysmem); - create_uart(vms, pic, VIRT_SECURE_UART, secure_sysmem, serial_hd(1)); + create_uart(vms, VIRT_SECURE_UART, secure_sysmem, serial_hd(1)); } vms->highmem_ecam &= vms->highmem && (!firmware_loaded || aarch64); - create_rtc(vms, pic); + create_rtc(vms); - create_pcie(vms, pic); + create_pcie(vms); - if (has_ged && aarch64 && firmware_loaded && acpi_enabled) { - vms->acpi_dev = create_acpi_ged(vms, pic); + if (has_ged && aarch64 && firmware_loaded && virt_is_acpi_enabled(vms)) { + vms->acpi_dev = create_acpi_ged(vms); } else { - create_gpio(vms, pic); + create_gpio(vms); } /* connect powerdown request */ @@ -1743,12 +1859,12 @@ static void machvirt_init(MachineState *machine) * (which will be automatically plugged in to the transports). If * no backend is created the transport will just sit harmlessly idle. */ - create_virtio_devices(vms, pic); + create_virtio_devices(vms); vms->fw_cfg = create_fw_cfg(vms, &address_space_memory); rom_set_fw(vms->fw_cfg); - create_platform_bus(vms, pic); + create_platform_bus(vms); vms->bootinfo.ram_size = machine->ram_size; vms->bootinfo.nb_cpus = smp_cpus; @@ -1819,10 +1935,35 @@ static void virt_set_its(Object *obj, bool value, Error **errp) vms->its = value; } +bool virt_is_acpi_enabled(VirtMachineState *vms) +{ + if (vms->acpi == ON_OFF_AUTO_OFF) { + return false; + } + return true; +} + +static void virt_get_acpi(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + VirtMachineState *vms = VIRT_MACHINE(obj); + OnOffAuto acpi = vms->acpi; + + visit_type_OnOffAuto(v, name, &acpi, errp); +} + +static void virt_set_acpi(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + VirtMachineState *vms = VIRT_MACHINE(obj); + + visit_type_OnOffAuto(v, name, &vms->acpi, errp); +} + static char *virt_get_gic_version(Object *obj, Error **errp) { VirtMachineState *vms = VIRT_MACHINE(obj); - const char *val = vms->gic_version == 3 ? "3" : "2"; + const char *val = vms->gic_version == VIRT_GIC_VERSION_3 ? "3" : "2"; return g_strdup(val); } @@ -1832,13 +1973,13 @@ static void virt_set_gic_version(Object *obj, const char *value, Error **errp) VirtMachineState *vms = VIRT_MACHINE(obj); if (!strcmp(value, "3")) { - vms->gic_version = 3; + vms->gic_version = VIRT_GIC_VERSION_3; } else if (!strcmp(value, "2")) { - vms->gic_version = 2; + vms->gic_version = VIRT_GIC_VERSION_2; } else if (!strcmp(value, "host")) { - vms->gic_version = 0; /* Will probe later */ + vms->gic_version = VIRT_GIC_VERSION_HOST; /* Will probe later */ } else if (!strcmp(value, "max")) { - vms->gic_version = -1; /* Will probe later */ + vms->gic_version = VIRT_GIC_VERSION_MAX; /* Will probe later */ } else { error_setg(errp, "Invalid gic-version value"); error_append_hint(errp, "Valid values are 3, 2, host, max.\n"); @@ -1935,7 +2076,6 @@ static void virt_memory_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, static void virt_memory_plug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { - HotplugHandlerClass *hhc; VirtMachineState *vms = VIRT_MACHINE(hotplug_dev); Error *local_err = NULL; @@ -1944,8 +2084,9 @@ static void virt_memory_plug(HotplugHandler *hotplug_dev, goto out; } - hhc = HOTPLUG_HANDLER_GET_CLASS(vms->acpi_dev); - hhc->plug(HOTPLUG_HANDLER(vms->acpi_dev), dev, &error_abort); + hotplug_handler_plug(HOTPLUG_HANDLER(vms->acpi_dev), + dev, &error_abort); + out: error_propagate(errp, local_err); } @@ -1972,6 +2113,13 @@ static void virt_machine_device_plug_cb(HotplugHandler *hotplug_dev, if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { virt_memory_plug(hotplug_dev, dev, errp); } + if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_IOMMU_PCI)) { + PCIDevice *pdev = PCI_DEVICE(dev); + + vms->iommu = VIRT_IOMMU_VIRTIO; + vms->virtio_iommu_bdf = pci_get_bdf(pdev); + create_virtio_iommu_dt_bindings(vms, errp); + } } static void virt_machine_device_unplug_request_cb(HotplugHandler *hotplug_dev, @@ -1988,7 +2136,13 @@ static HotplugHandler *virt_machine_get_hotplug_handler(MachineState *machine, (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM))) { return HOTPLUG_HANDLER(machine); } + if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_IOMMU_PCI)) { + VirtMachineState *vms = VIRT_MACHINE(machine); + if (!vms->bootinfo.firmware_loaded || !virt_is_acpi_enabled(vms)) { + return HOTPLUG_HANDLER(machine); + } + } return NULL; } @@ -2037,6 +2191,7 @@ static void virt_machine_class_init(ObjectClass *oc, void *data) machine_class_allow_dynamic_sysbus_dev(mc, TYPE_VFIO_AMD_XGBE); machine_class_allow_dynamic_sysbus_dev(mc, TYPE_RAMFB_DEVICE); machine_class_allow_dynamic_sysbus_dev(mc, TYPE_VFIO_PLATFORM); + machine_class_allow_dynamic_sysbus_dev(mc, TYPE_TPM_TIS_SYSBUS); mc->block_default_type = IF_VIRTIO; mc->no_cdrom = 1; mc->pci_allow_0_address = true; @@ -2054,6 +2209,13 @@ static void virt_machine_class_init(ObjectClass *oc, void *data) hc->unplug_request = virt_machine_device_unplug_request_cb; mc->numa_mem_supported = true; mc->auto_enable_numa_with_memhp = true; + mc->default_ram_id = "mach-virt.ram"; + + object_class_property_add(oc, "acpi", "OnOffAuto", + virt_get_acpi, virt_set_acpi, + NULL, NULL, &error_abort); + object_class_property_set_description(oc, "acpi", + "Enable ACPI", &error_abort); } static void virt_instance_init(Object *obj) @@ -2091,13 +2253,13 @@ static void virt_instance_init(Object *obj) "Set on/off to enable/disable using " "physical address space above 32 bits", NULL); - /* Default GIC type is v2 */ - vms->gic_version = 2; + vms->gic_version = VIRT_GIC_VERSION_NOSEL; object_property_add_str(obj, "gic-version", virt_get_gic_version, virt_set_gic_version, NULL); object_property_set_description(obj, "gic-version", "Set GIC version. " - "Valid values are 2, 3 and host", NULL); + "Valid values are 2, 3, host and max", + NULL); vms->highmem_ecam = !vmc->no_highmem_ecam; @@ -2147,10 +2309,25 @@ static void machvirt_machine_init(void) } type_init(machvirt_machine_init); +static void virt_machine_5_0_options(MachineClass *mc) +{ + static GlobalProperty compat[] = { + { TYPE_TPM_TIS_SYSBUS, "ppi", "false" }, + }; + + compat_props_add(mc->compat_props, compat, G_N_ELEMENTS(compat)); +} +DEFINE_VIRT_MACHINE_AS_LATEST(5, 0) + static void virt_machine_4_2_options(MachineClass *mc) { + VirtMachineClass *vmc = VIRT_MACHINE_CLASS(OBJECT_CLASS(mc)); + + virt_machine_5_0_options(mc); + compat_props_add(mc->compat_props, hw_compat_4_2, hw_compat_4_2_len); + vmc->kvm_no_adjvtime = true; } -DEFINE_VIRT_MACHINE_AS_LATEST(4, 2) +DEFINE_VIRT_MACHINE(4, 2) static void virt_machine_4_1_options(MachineClass *mc) { diff --git a/hw/arm/xilinx_zynq.c b/hw/arm/xilinx_zynq.c index 3a0fa5b23f..571cdcd599 100644 --- a/hw/arm/xilinx_zynq.c +++ b/hw/arm/xilinx_zynq.c @@ -29,6 +29,7 @@ #include "hw/loader.h" #include "hw/misc/zynq-xadc.h" #include "hw/ssi/ssi.h" +#include "hw/usb/chipidea.h" #include "qemu/error-report.h" #include "hw/sd/sdhci.h" #include "hw/char/cadence_uart.h" @@ -158,16 +159,20 @@ static inline void zynq_init_spi_flashes(uint32_t base_addr, qemu_irq irq, static void zynq_init(MachineState *machine) { - ram_addr_t ram_size = machine->ram_size; ARMCPU *cpu; MemoryRegion *address_space_mem = get_system_memory(); - MemoryRegion *ext_ram = g_new(MemoryRegion, 1); MemoryRegion *ocm_ram = g_new(MemoryRegion, 1); DeviceState *dev; SysBusDevice *busdev; qemu_irq pic[64]; int n; + /* max 2GB ram */ + if (machine->ram_size > 2 * GiB) { + error_report("RAM size more than 2 GiB is not supported"); + exit(EXIT_FAILURE); + } + cpu = ARM_CPU(object_new(machine->cpu_type)); /* By default A9 CPUs have EL3 enabled. This board does not @@ -184,15 +189,8 @@ static void zynq_init(MachineState *machine) &error_fatal); object_property_set_bool(OBJECT(cpu), true, "realized", &error_fatal); - /* max 2GB ram */ - if (ram_size > 0x80000000) { - ram_size = 0x80000000; - } - /* DDR remapped to address zero. */ - memory_region_allocate_system_memory(ext_ram, NULL, "zynq.ext_ram", - ram_size); - memory_region_add_subregion(address_space_mem, 0, ext_ram); + memory_region_add_subregion(address_space_mem, 0, machine->ram); /* 256K of on-chip memory */ memory_region_init_ram(ocm_ram, NULL, "zynq.ocm_ram", 256 * KiB, @@ -228,8 +226,8 @@ static void zynq_init(MachineState *machine) zynq_init_spi_flashes(0xE0007000, pic[81-IRQ_OFFSET], false); zynq_init_spi_flashes(0xE000D000, pic[51-IRQ_OFFSET], true); - sysbus_create_simple("xlnx,ps7-usb", 0xE0002000, pic[53-IRQ_OFFSET]); - sysbus_create_simple("xlnx,ps7-usb", 0xE0003000, pic[76-IRQ_OFFSET]); + sysbus_create_simple(TYPE_CHIPIDEA, 0xE0002000, pic[53 - IRQ_OFFSET]); + sysbus_create_simple(TYPE_CHIPIDEA, 0xE0003000, pic[76 - IRQ_OFFSET]); cadence_uart_create(0xE0000000, pic[59 - IRQ_OFFSET], serial_hd(0)); cadence_uart_create(0xE0001000, pic[82 - IRQ_OFFSET], serial_hd(1)); @@ -300,7 +298,7 @@ static void zynq_init(MachineState *machine) sysbus_connect_irq(busdev, 0, pic[40 - IRQ_OFFSET]); sysbus_mmio_map(busdev, 0, 0xF8007000); - zynq_binfo.ram_size = ram_size; + zynq_binfo.ram_size = machine->ram_size; zynq_binfo.nb_cpus = 1; zynq_binfo.board_id = 0xd32; zynq_binfo.loader_start = 0; @@ -318,6 +316,7 @@ static void zynq_machine_init(MachineClass *mc) mc->no_sdcard = 1; mc->ignore_memory_transaction_failures = true; mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-a9"); + mc->default_ram_id = "zynq.ext_ram"; } DEFINE_MACHINE("xilinx-zynq-a9", zynq_machine_init) diff --git a/hw/arm/xlnx-versal-virt.c b/hw/arm/xlnx-versal-virt.c index 462493c467..878a275140 100644 --- a/hw/arm/xlnx-versal-virt.c +++ b/hw/arm/xlnx-versal-virt.c @@ -30,7 +30,6 @@ typedef struct VersalVirt { MachineState parent_obj; Versal soc; - MemoryRegion mr_ddr; void *fdt; int fdt_size; @@ -230,6 +229,33 @@ static void fdt_add_gem_nodes(VersalVirt *s) } } +static void fdt_add_zdma_nodes(VersalVirt *s) +{ + const char clocknames[] = "clk_main\0clk_apb"; + const char compat[] = "xlnx,zynqmp-dma-1.0"; + int i; + + for (i = XLNX_VERSAL_NR_ADMAS - 1; i >= 0; i--) { + uint64_t addr = MM_ADMA_CH0 + MM_ADMA_CH0_SIZE * i; + char *name = g_strdup_printf("/dma@%" PRIx64, addr); + + qemu_fdt_add_subnode(s->fdt, name); + + qemu_fdt_setprop_cell(s->fdt, name, "xlnx,bus-width", 64); + qemu_fdt_setprop_cells(s->fdt, name, "clocks", + s->phandle.clk_25Mhz, s->phandle.clk_25Mhz); + qemu_fdt_setprop(s->fdt, name, "clock-names", + clocknames, sizeof(clocknames)); + qemu_fdt_setprop_cells(s->fdt, name, "interrupts", + GIC_FDT_IRQ_TYPE_SPI, VERSAL_ADMA_IRQ_0 + i, + GIC_FDT_IRQ_FLAGS_LEVEL_HI); + qemu_fdt_setprop_sized_cells(s->fdt, name, "reg", + 2, addr, 2, 0x1000); + qemu_fdt_setprop(s->fdt, name, "compatible", compat, sizeof(compat)); + g_free(name); + } +} + static void fdt_nop_memory_nodes(void *fdt, Error **errp) { Error *err = NULL; @@ -350,7 +376,7 @@ static void create_virtio_regions(VersalVirt *s) int i; for (i = 0; i < NUM_VIRTIO_TRANSPORT; i++) { - char *name = g_strdup_printf("virtio%d", i);; + char *name = g_strdup_printf("virtio%d", i); hwaddr base = MM_TOP_RSVD + i * virtio_mmio_size; int irq = VERSAL_RSVD_IRQ_FIRST + i; MemoryRegion *mr; @@ -414,12 +440,9 @@ static void versal_virt_init(MachineState *machine) psci_conduit = QEMU_PSCI_CONDUIT_SMC; } - memory_region_allocate_system_memory(&s->mr_ddr, NULL, "ddr", - machine->ram_size); - sysbus_init_child_obj(OBJECT(machine), "xlnx-ve", &s->soc, sizeof(s->soc), TYPE_XLNX_VERSAL); - object_property_set_link(OBJECT(&s->soc), OBJECT(&s->mr_ddr), + object_property_set_link(OBJECT(&s->soc), OBJECT(machine->ram), "ddr", &error_abort); object_property_set_int(OBJECT(&s->soc), psci_conduit, "psci-conduit", &error_abort); @@ -431,6 +454,7 @@ static void versal_virt_init(MachineState *machine) fdt_add_uart_nodes(s); fdt_add_gic_nodes(s); fdt_add_timer_nodes(s); + fdt_add_zdma_nodes(s); fdt_add_cpu_nodes(s, psci_conduit); fdt_add_clk_node(s, "/clk125", 125000000, s->phandle.clk_125Mhz); fdt_add_clk_node(s, "/clk25", 25000000, s->phandle.clk_25Mhz); @@ -473,6 +497,7 @@ static void versal_virt_machine_class_init(ObjectClass *oc, void *data) mc->max_cpus = XLNX_VERSAL_NR_ACPUS; mc->default_cpus = XLNX_VERSAL_NR_ACPUS; mc->no_cdrom = true; + mc->default_ram_id = "ddr"; } static const TypeInfo versal_virt_machine_init_typeinfo = { diff --git a/hw/arm/xlnx-versal.c b/hw/arm/xlnx-versal.c index 8b3d8d85b8..cb0122a3a6 100644 --- a/hw/arm/xlnx-versal.c +++ b/hw/arm/xlnx-versal.c @@ -22,6 +22,7 @@ #include "hw/misc/unimp.h" #include "hw/intc/arm_gicv3_common.h" #include "hw/arm/xlnx-versal.h" +#include "hw/char/pl011.h" #define XLNX_VERSAL_ACPU_TYPE ARM_CPU_TYPE_NAME("cortex-a72") #define GEM_REVISION 0x40070106 @@ -144,7 +145,7 @@ static void versal_create_uarts(Versal *s, qemu_irq *pic) DeviceState *dev; MemoryRegion *mr; - dev = qdev_create(NULL, "pl011"); + dev = qdev_create(NULL, TYPE_PL011); s->lpd.iou.uart[i] = SYS_BUS_DEVICE(dev); qdev_prop_set_chr(dev, "chardev", serial_hd(i)); object_property_add_child(OBJECT(s), name, OBJECT(dev), &error_fatal); @@ -193,6 +194,29 @@ static void versal_create_gems(Versal *s, qemu_irq *pic) } } +static void versal_create_admas(Versal *s, qemu_irq *pic) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(s->lpd.iou.adma); i++) { + char *name = g_strdup_printf("adma%d", i); + DeviceState *dev; + MemoryRegion *mr; + + dev = qdev_create(NULL, "xlnx.zdma"); + s->lpd.iou.adma[i] = SYS_BUS_DEVICE(dev); + object_property_add_child(OBJECT(s), name, OBJECT(dev), &error_fatal); + qdev_init_nofail(dev); + + mr = sysbus_mmio_get_region(s->lpd.iou.adma[i], 0); + memory_region_add_subregion(&s->mr_ps, + MM_ADMA_CH0 + i * MM_ADMA_CH0_SIZE, mr); + + sysbus_connect_irq(s->lpd.iou.adma[i], 0, pic[VERSAL_ADMA_IRQ_0 + i]); + g_free(name); + } +} + /* This takes the board allocated linear DDR memory and creates aliases * for each split DDR range/aperture on the Versal address map. */ @@ -274,6 +298,7 @@ static void versal_realize(DeviceState *dev, Error **errp) versal_create_apu_gic(s, pic); versal_create_uarts(s, pic); versal_create_gems(s, pic); + versal_create_admas(s, pic); versal_map_ddr(s); versal_unimp(s); @@ -305,7 +330,7 @@ static void versal_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = versal_realize; - dc->props = versal_properties; + device_class_set_props(dc, versal_properties); /* No VMSD since we haven't got any top-level SoC state to save. */ } diff --git a/hw/arm/xlnx-zcu102.c b/hw/arm/xlnx-zcu102.c index 53cfe7c1f1..bd645ad818 100644 --- a/hw/arm/xlnx-zcu102.c +++ b/hw/arm/xlnx-zcu102.c @@ -28,7 +28,6 @@ typedef struct XlnxZCU102 { MachineState parent_obj; XlnxZynqMPState soc; - MemoryRegion ddr_ram; bool secure; bool virt; @@ -87,13 +86,10 @@ static void xlnx_zcu102_init(MachineState *machine) ram_size); } - memory_region_allocate_system_memory(&s->ddr_ram, NULL, "ddr-ram", - ram_size); - object_initialize_child(OBJECT(machine), "soc", &s->soc, sizeof(s->soc), TYPE_XLNX_ZYNQMP, &error_abort, NULL); - object_property_set_link(OBJECT(&s->soc), OBJECT(&s->ddr_ram), + object_property_set_link(OBJECT(&s->soc), OBJECT(machine->ram), "ddr-ram", &error_abort); object_property_set_bool(OBJECT(&s->soc), s->secure, "secure", &error_fatal); @@ -211,6 +207,7 @@ static void xlnx_zcu102_machine_class_init(ObjectClass *oc, void *data) mc->ignore_memory_transaction_failures = true; mc->max_cpus = XLNX_ZYNQMP_NUM_APU_CPUS + XLNX_ZYNQMP_NUM_RPU_CPUS; mc->default_cpus = XLNX_ZYNQMP_NUM_APU_CPUS; + mc->default_ram_id = "ddr-ram"; } static const TypeInfo xlnx_zcu102_machine_init_typeinfo = { diff --git a/hw/arm/xlnx-zynqmp.c b/hw/arm/xlnx-zynqmp.c index fb03c60ebb..b84d153d56 100644 --- a/hw/arm/xlnx-zynqmp.c +++ b/hw/arm/xlnx-zynqmp.c @@ -318,9 +318,9 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) ddr_low_size = XLNX_ZYNQMP_MAX_LOW_RAM_SIZE; ddr_high_size = ram_size - XLNX_ZYNQMP_MAX_LOW_RAM_SIZE; - memory_region_init_alias(&s->ddr_ram_high, NULL, - "ddr-ram-high", s->ddr_ram, - ddr_low_size, ddr_high_size); + memory_region_init_alias(&s->ddr_ram_high, OBJECT(dev), + "ddr-ram-high", s->ddr_ram, ddr_low_size, + ddr_high_size); memory_region_add_subregion(get_system_memory(), XLNX_ZYNQMP_HIGH_RAM_START, &s->ddr_ram_high); @@ -330,9 +330,8 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) ddr_low_size = ram_size; } - memory_region_init_alias(&s->ddr_ram_low, NULL, - "ddr-ram-low", s->ddr_ram, - 0, ddr_low_size); + memory_region_init_alias(&s->ddr_ram_low, OBJECT(dev), "ddr-ram-low", + s->ddr_ram, 0, ddr_low_size); memory_region_add_subregion(get_system_memory(), 0, &s->ddr_ram_low); /* Create the four OCM banks */ @@ -521,7 +520,7 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) sysbus_connect_irq(SYS_BUS_DEVICE(&s->sata), 0, gic_spi[SATA_INTR]); for (i = 0; i < XLNX_ZYNQMP_NUM_SDHCI; i++) { - char *bus_name = g_strdup_printf("sd-bus%d", i); + char *bus_name; SysBusDevice *sbd = SYS_BUS_DEVICE(&s->sdhci[i]); Object *sdhci = OBJECT(&s->sdhci[i]); @@ -531,8 +530,20 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) * - eMMC Specification Version 4.51 */ object_property_set_uint(sdhci, 3, "sd-spec-version", &err); + if (err) { + error_propagate(errp, err); + return; + } object_property_set_uint(sdhci, SDHCI_CAPABILITIES, "capareg", &err); + if (err) { + error_propagate(errp, err); + return; + } object_property_set_uint(sdhci, UHS_I, "uhs", &err); + if (err) { + error_propagate(errp, err); + return; + } object_property_set_bool(sdhci, true, "realized", &err); if (err) { error_propagate(errp, err); @@ -542,6 +553,7 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) sysbus_connect_irq(sbd, 0, gic_spi[sdhci_intr[i]]); /* Alias controller SD bus to the SoC itself */ + bus_name = g_strdup_printf("sd-bus%d", i); object_property_add_alias(OBJECT(s), bus_name, sdhci, "sd-bus", &error_abort); g_free(bus_name); @@ -551,6 +563,10 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) gchar *bus_name; object_property_set_bool(OBJECT(&s->spi[i]), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi[i]), 0, spi_addr[i]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->spi[i]), 0, @@ -565,6 +581,10 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) } object_property_set_bool(OBJECT(&s->qspi), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } sysbus_mmio_map(SYS_BUS_DEVICE(&s->qspi), 0, QSPI_ADDR); sysbus_mmio_map(SYS_BUS_DEVICE(&s->qspi), 1, LQSPI_ADDR); sysbus_connect_irq(SYS_BUS_DEVICE(&s->qspi), 0, gic_spi[QSPI_IRQ]); @@ -619,6 +639,10 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) for (i = 0; i < XLNX_ZYNQMP_NUM_GDMA_CH; i++) { object_property_set_uint(OBJECT(&s->gdma[i]), 128, "bus-width", &err); + if (err) { + error_propagate(errp, err); + return; + } object_property_set_bool(OBJECT(&s->gdma[i]), true, "realized", &err); if (err) { error_propagate(errp, err); @@ -657,7 +681,7 @@ static void xlnx_zynqmp_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); - dc->props = xlnx_zynqmp_props; + device_class_set_props(dc, xlnx_zynqmp_props); dc->realize = xlnx_zynqmp_realize; /* Reason: Uses serial_hds in realize function, thus can't be used twice */ dc->user_creatable = false; diff --git a/hw/arm/z2.c b/hw/arm/z2.c index 34794fe3ae..a0f4095990 100644 --- a/hw/arm/z2.c +++ b/hw/arm/z2.c @@ -300,7 +300,6 @@ static void z2_init(MachineState *machine) uint32_t sector_len = 0x10000; PXA2xxState *mpu; DriveInfo *dinfo; - int be; void *z2_lcd; I2CBus *bus; DeviceState *wm; @@ -308,21 +307,10 @@ static void z2_init(MachineState *machine) /* Setup CPU & memory */ mpu = pxa270_init(address_space_mem, z2_binfo.ram_size, machine->cpu_type); -#ifdef TARGET_WORDS_BIGENDIAN - be = 1; -#else - be = 0; -#endif dinfo = drive_get(IF_PFLASH, 0, 0); - if (!dinfo && !qtest_enabled()) { - error_report("Flash image must be given with the " - "'pflash' parameter"); - exit(1); - } - if (!pflash_cfi01_register(Z2_FLASH_BASE, "z2.flash0", Z2_FLASH_SIZE, dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, - sector_len, 4, 0, 0, 0, 0, be)) { + sector_len, 4, 0, 0, 0, 0, 0)) { error_report("Error registering flash memory"); exit(1); } diff --git a/hw/audio/ac97.c b/hw/audio/ac97.c index a5550d10f3..8d396c1262 100644 --- a/hw/audio/ac97.c +++ b/hw/audio/ac97.c @@ -1301,8 +1301,6 @@ typedef struct AC97DeviceState { PCIDevice dev; AC97LinkState state; - uint32_t use_broken_id; - MemoryRegion io_nam; MemoryRegion io_nabm; } AC97DeviceState; @@ -1391,13 +1389,6 @@ static void ac97_realize (PCIDevice *dev, Error **errp) c[PCI_BASE_ADDRESS_0 + 6] = 0x00; c[PCI_BASE_ADDRESS_0 + 7] = 0x00; - if (s->use_broken_id) { - c[PCI_SUBSYSTEM_VENDOR_ID] = 0x86; - c[PCI_SUBSYSTEM_VENDOR_ID + 1] = 0x80; - c[PCI_SUBSYSTEM_ID] = 0x00; - c[PCI_SUBSYSTEM_ID + 1] = 0x00; - } - c[PCI_INTERRUPT_LINE] = 0x00; /* intr_ln interrupt line rw */ c[PCI_INTERRUPT_PIN] = 0x01; /* intr_pn interrupt pin ro */ @@ -1426,7 +1417,7 @@ static int ac97_init (PCIBus *bus) static Property ac97_properties[] = { DEFINE_AUDIO_PROPERTIES(AC97DeviceState, state.card), - DEFINE_PROP_UINT32 ("use_broken_id", AC97DeviceState, use_broken_id, 0), + DEFINE_PROP_END_OF_LIST (), }; static void ac97_class_init (ObjectClass *klass, void *data) @@ -1443,7 +1434,7 @@ static void ac97_class_init (ObjectClass *klass, void *data) set_bit(DEVICE_CATEGORY_SOUND, dc->categories); dc->desc = "Intel 82801AA AC97 Audio"; dc->vmsd = &vmstate_ac97; - dc->props = ac97_properties; + device_class_set_props(dc, ac97_properties); dc->reset = ac97_on_device_reset; } diff --git a/hw/audio/adlib.c b/hw/audio/adlib.c index cb4178d861..d6c1fb0586 100644 --- a/hw/audio/adlib.c +++ b/hw/audio/adlib.c @@ -312,7 +312,7 @@ static void adlib_class_initfn (ObjectClass *klass, void *data) dc->realize = adlib_realizefn; set_bit(DEVICE_CATEGORY_SOUND, dc->categories); dc->desc = ADLIB_DESC; - dc->props = adlib_properties; + device_class_set_props(dc, adlib_properties); } static const TypeInfo adlib_info = { diff --git a/hw/audio/cs4231.c b/hw/audio/cs4231.c index b5d3e895ce..11a6328fc2 100644 --- a/hw/audio/cs4231.c +++ b/hw/audio/cs4231.c @@ -168,7 +168,7 @@ static void cs4231_class_init(ObjectClass *klass, void *data) dc->reset = cs_reset; dc->vmsd = &vmstate_cs4231; - dc->props = cs4231_properties; + device_class_set_props(dc, cs4231_properties); } static const TypeInfo cs4231_info = { diff --git a/hw/audio/cs4231a.c b/hw/audio/cs4231a.c index c7b8067489..ffdbb58d6a 100644 --- a/hw/audio/cs4231a.c +++ b/hw/audio/cs4231a.c @@ -706,7 +706,7 @@ static void cs4231a_class_initfn (ObjectClass *klass, void *data) set_bit(DEVICE_CATEGORY_SOUND, dc->categories); dc->desc = "Crystal Semiconductor CS4231A"; dc->vmsd = &vmstate_cs4231a; - dc->props = cs4231a_properties; + device_class_set_props(dc, cs4231a_properties); } static const TypeInfo cs4231a_info = { diff --git a/hw/audio/es1370.c b/hw/audio/es1370.c index f9e9f2a3b3..89c4dabcd4 100644 --- a/hw/audio/es1370.c +++ b/hw/audio/es1370.c @@ -908,7 +908,7 @@ static void es1370_class_init (ObjectClass *klass, void *data) dc->desc = "ENSONIQ AudioPCI ES1370"; dc->vmsd = &vmstate_es1370; dc->reset = es1370_on_reset; - dc->props = es1370_properties; + device_class_set_props(dc, es1370_properties); } static const TypeInfo es1370_info = { diff --git a/hw/audio/fmopl.c b/hw/audio/fmopl.c index 9f50a89b4a..8a71a569fa 100644 --- a/hw/audio/fmopl.c +++ b/hw/audio/fmopl.c @@ -186,7 +186,7 @@ static int32_t *VIB_TABLE; /* envelope output curve table */ /* attack + decay + OFF */ -static int32_t ENV_CURVE[2*EG_ENT+1]; +static int32_t *ENV_CURVE; /* multiple table */ #define ML 2 @@ -627,6 +627,7 @@ static int OPLOpenTable( void ) free(AMS_TABLE); return 0; } + ENV_CURVE = g_new(int32_t, 2 * EG_ENT + 1); /* make total level table */ for (t = 0;t < EG_ENT-1 ;t++){ rate = ((1< voltage */ @@ -694,6 +695,7 @@ static int OPLOpenTable( void ) static void OPLCloseTable( void ) { + g_free(ENV_CURVE); free(TL_TABLE); free(SIN_TABLE); free(AMS_TABLE); @@ -1066,7 +1068,7 @@ static void OPLResetChip(FM_OPL *OPL) } } -/* ---------- Create one of vietual YM3812 ---------- */ +/* ---------- Create one of virtual YM3812 ---------- */ /* 'rate' is sampling rate and 'bufsiz' is the size of the */ FM_OPL *OPLCreate(int clock, int rate) { @@ -1115,7 +1117,7 @@ FM_OPL *OPLCreate(int clock, int rate) return OPL; } -/* ---------- Destroy one of vietual YM3812 ---------- */ +/* ---------- Destroy one of virtual YM3812 ---------- */ void OPLDestroy(FM_OPL *OPL) { #ifdef OPL_OUTPUT_LOG diff --git a/hw/audio/gus.c b/hw/audio/gus.c index 2b6b7c4e3f..eb4a803fb5 100644 --- a/hw/audio/gus.c +++ b/hw/audio/gus.c @@ -315,7 +315,7 @@ static void gus_class_initfn (ObjectClass *klass, void *data) set_bit(DEVICE_CATEGORY_SOUND, dc->categories); dc->desc = "Gravis Ultrasound GF1"; dc->vmsd = &vmstate_gus; - dc->props = gus_properties; + device_class_set_props(dc, gus_properties); } static const TypeInfo gus_info = { diff --git a/hw/audio/hda-codec.c b/hw/audio/hda-codec.c index f17e8d8dce..cbd92b72f2 100644 --- a/hw/audio/hda-codec.c +++ b/hw/audio/hda-codec.c @@ -265,8 +265,6 @@ static void hda_audio_input_cb(void *opaque, int avail) int64_t to_transfer = MIN(B_SIZE - (wpos - rpos), avail); - hda_timer_sync_adjust(st, -((wpos - rpos) + to_transfer - (B_SIZE >> 1))); - while (to_transfer) { uint32_t start = (uint32_t) (wpos & B_MASK); uint32_t chunk = (uint32_t) MIN(B_SIZE - start, to_transfer); @@ -278,6 +276,8 @@ static void hda_audio_input_cb(void *opaque, int avail) break; } } + + hda_timer_sync_adjust(st, -((wpos - rpos) - (B_SIZE >> 1))); } static void hda_audio_output_timer(void *opaque) @@ -338,8 +338,6 @@ static void hda_audio_output_cb(void *opaque, int avail) return; } - hda_timer_sync_adjust(st, (wpos - rpos) - to_transfer - (B_SIZE >> 1)); - while (to_transfer) { uint32_t start = (uint32_t) (rpos & B_MASK); uint32_t chunk = (uint32_t) MIN(B_SIZE - start, to_transfer); @@ -351,6 +349,8 @@ static void hda_audio_output_cb(void *opaque, int avail) break; } } + + hda_timer_sync_adjust(st, (wpos - rpos) - (B_SIZE >> 1)); } static void hda_audio_compat_input_cb(void *opaque, int avail) @@ -892,7 +892,7 @@ static void hda_audio_base_class_init(ObjectClass *klass, void *data) set_bit(DEVICE_CATEGORY_SOUND, dc->categories); dc->reset = hda_audio_reset; dc->vmsd = &vmstate_hda_audio; - dc->props = hda_audio_properties; + device_class_set_props(dc, hda_audio_properties); } static const TypeInfo hda_audio_info = { diff --git a/hw/audio/intel-hda.c b/hw/audio/intel-hda.c index 6ecd383540..e8d18b7c58 100644 --- a/hw/audio/intel-hda.c +++ b/hw/audio/intel-hda.c @@ -181,7 +181,9 @@ struct IntelHDAState { IntelHDAStream st[8]; /* state */ + MemoryRegion container; MemoryRegion mmio; + MemoryRegion alias; uint32_t rirb_count; int64_t wall_base_ns; @@ -670,12 +672,6 @@ static const struct IntelHDAReg regtab[] = { .offset = offsetof(IntelHDAState, wall_clk), .rhandler = intel_hda_get_wall_clk, }, - [ ICH6_REG_WALLCLK + 0x2000 ] = { - .name = "WALLCLK(alias)", - .size = 4, - .offset = offsetof(IntelHDAState, wall_clk), - .rhandler = intel_hda_get_wall_clk, - }, /* dma engine */ [ ICH6_REG_CORBLBASE ] = { @@ -837,12 +833,6 @@ static const struct IntelHDAReg regtab[] = { .size = 4, \ .offset = offsetof(IntelHDAState, st[_i].lpib), \ }, \ - [ ST_REG(_i, ICH6_REG_SD_LPIB) + 0x2000 ] = { \ - .stream = _i, \ - .name = _t stringify(_i) " LPIB(alias)", \ - .size = 4, \ - .offset = offsetof(IntelHDAState, st[_i].lpib), \ - }, \ [ ST_REG(_i, ICH6_REG_SD_CBL) ] = { \ .stream = _i, \ .name = _t stringify(_i) " CBL", \ @@ -1087,7 +1077,7 @@ static void intel_hda_reset(DeviceState *dev) QTAILQ_FOREACH(kid, &d->codecs.qbus.children, sibling) { DeviceState *qdev = kid->child; cdev = HDA_CODEC_DEVICE(qdev); - device_reset(DEVICE(cdev)); + device_legacy_reset(DEVICE(cdev)); d->state_sts |= (1 << cdev->cad); } intel_hda_update_irq(d); @@ -1125,9 +1115,15 @@ static void intel_hda_realize(PCIDevice *pci, Error **errp) error_free(err); } + memory_region_init(&d->container, OBJECT(d), + "intel-hda-container", 0x4000); memory_region_init_io(&d->mmio, OBJECT(d), &intel_hda_mmio_ops, d, - "intel-hda", 0x4000); - pci_register_bar(&d->pci, 0, 0, &d->mmio); + "intel-hda", 0x2000); + memory_region_add_subregion(&d->container, 0x0000, &d->mmio); + memory_region_init_alias(&d->alias, OBJECT(d), "intel-hda-alias", + &d->mmio, 0, 0x2000); + memory_region_add_subregion(&d->container, 0x2000, &d->alias); + pci_register_bar(&d->pci, 0, 0, &d->container); hda_codec_bus_init(DEVICE(pci), &d->codecs, sizeof(d->codecs), intel_hda_response, intel_hda_xfer); @@ -1233,7 +1229,7 @@ static void intel_hda_class_init(ObjectClass *klass, void *data) k->class_id = PCI_CLASS_MULTIMEDIA_HD_AUDIO; dc->reset = intel_hda_reset; dc->vmsd = &vmstate_intel_hda; - dc->props = intel_hda_properties; + device_class_set_props(dc, intel_hda_properties); } static void intel_hda_class_init_ich6(ObjectClass *klass, void *data) @@ -1289,7 +1285,7 @@ static void hda_codec_device_class_init(ObjectClass *klass, void *data) k->unrealize = hda_codec_dev_unrealize; set_bit(DEVICE_CATEGORY_SOUND, k->categories); k->bus_type = TYPE_HDA_BUS; - k->props = hda_props; + device_class_set_props(k, hda_props); } static const TypeInfo hda_codec_device_type_info = { diff --git a/hw/audio/milkymist-ac97.c b/hw/audio/milkymist-ac97.c index 6d409eff1b..0fa38adbe2 100644 --- a/hw/audio/milkymist-ac97.c +++ b/hw/audio/milkymist-ac97.c @@ -342,7 +342,7 @@ static void milkymist_ac97_class_init(ObjectClass *klass, void *data) dc->realize = milkymist_ac97_realize; dc->reset = milkymist_ac97_reset; dc->vmsd = &vmstate_milkymist_ac97; - dc->props = milkymist_ac97_properties; + device_class_set_props(dc, milkymist_ac97_properties); } static const TypeInfo milkymist_ac97_info = { diff --git a/hw/audio/pcspk.c b/hw/audio/pcspk.c index d773eb80de..29dc00bf8d 100644 --- a/hw/audio/pcspk.c +++ b/hw/audio/pcspk.c @@ -222,7 +222,7 @@ static void pcspk_class_initfn(ObjectClass *klass, void *data) dc->realize = pcspk_realizefn; set_bit(DEVICE_CATEGORY_SOUND, dc->categories); dc->vmsd = &vmstate_spk; - dc->props = pcspk_properties; + device_class_set_props(dc, pcspk_properties); /* Reason: realize sets global pcspk_state */ /* Reason: pit object link */ dc->user_creatable = false; diff --git a/hw/audio/pl041.c b/hw/audio/pl041.c index c30417d46d..c3d3eab6ed 100644 --- a/hw/audio/pl041.c +++ b/hw/audio/pl041.c @@ -640,7 +640,7 @@ static void pl041_device_class_init(ObjectClass *klass, void *data) set_bit(DEVICE_CATEGORY_SOUND, dc->categories); dc->reset = pl041_device_reset; dc->vmsd = &vmstate_pl041; - dc->props = pl041_device_properties; + device_class_set_props(dc, pl041_device_properties); } static const TypeInfo pl041_device_info = { diff --git a/hw/audio/sb16.c b/hw/audio/sb16.c index a354f94acb..df6f755a37 100644 --- a/hw/audio/sb16.c +++ b/hw/audio/sb16.c @@ -1439,7 +1439,7 @@ static void sb16_class_initfn (ObjectClass *klass, void *data) set_bit(DEVICE_CATEGORY_SOUND, dc->categories); dc->desc = "Creative Sound Blaster 16"; dc->vmsd = &vmstate_sb16; - dc->props = sb16_properties; + device_class_set_props(dc, sb16_properties); } static const TypeInfo sb16_info = { diff --git a/hw/audio/wm8750.c b/hw/audio/wm8750.c index 601ed04aff..92b2902a10 100644 --- a/hw/audio/wm8750.c +++ b/hw/audio/wm8750.c @@ -717,7 +717,7 @@ static void wm8750_class_init(ObjectClass *klass, void *data) sc->recv = wm8750_rx; sc->send = wm8750_tx; dc->vmsd = &vmstate_wm8750; - dc->props = wm8750_properties; + device_class_set_props(dc, wm8750_properties); } static const TypeInfo wm8750_info = { diff --git a/hw/block/Makefile.objs b/hw/block/Makefile.objs index 28c2495a00..4b4a2b338d 100644 --- a/hw/block/Makefile.objs +++ b/hw/block/Makefile.objs @@ -10,7 +10,7 @@ common-obj-$(CONFIG_ONENAND) += onenand.o common-obj-$(CONFIG_NVME_PCI) += nvme.o common-obj-$(CONFIG_SWIM) += swim.o -obj-$(CONFIG_SH4) += tc58128.o +common-obj-$(CONFIG_SH4) += tc58128.o obj-$(CONFIG_VIRTIO_BLK) += virtio-blk.o obj-$(CONFIG_VHOST_USER_BLK) += vhost-user-blk.o diff --git a/hw/block/dataplane/virtio-blk.c b/hw/block/dataplane/virtio-blk.c index 119906a5fe..1b52e8159c 100644 --- a/hw/block/dataplane/virtio-blk.c +++ b/hw/block/dataplane/virtio-blk.c @@ -67,7 +67,7 @@ static void notify_guest_bh(void *opaque) memset(s->batch_notify_vqs, 0, sizeof(bitmap)); for (j = 0; j < nvqs; j += BITS_PER_LONG) { - unsigned long bits = bitmap[j]; + unsigned long bits = bitmap[j / BITS_PER_LONG]; while (bits != 0) { unsigned i = j + ctzl(bits); diff --git a/hw/block/dataplane/xen-block.c b/hw/block/dataplane/xen-block.c index 3b9caeb2fa..5f8f15778b 100644 --- a/hw/block/dataplane/xen-block.c +++ b/hw/block/dataplane/xen-block.c @@ -64,6 +64,8 @@ struct XenBlockDataPlane { AioContext *ctx; }; +static int xen_block_send_response(XenBlockRequest *request); + static void reset_request(XenBlockRequest *request) { memset(&request->req, 0, sizeof(request->req)); @@ -115,23 +117,26 @@ out: return request; } -static void xen_block_finish_request(XenBlockRequest *request) +static void xen_block_complete_request(XenBlockRequest *request) { XenBlockDataPlane *dataplane = request->dataplane; + if (xen_block_send_response(request)) { + Error *local_err = NULL; + + xen_device_notify_event_channel(dataplane->xendev, + dataplane->event_channel, + &local_err); + if (local_err) { + error_report_err(local_err); + } + } + QLIST_REMOVE(request, list); dataplane->requests_inflight--; -} - -static void xen_block_release_request(XenBlockRequest *request) -{ - XenBlockDataPlane *dataplane = request->dataplane; - - QLIST_REMOVE(request, list); reset_request(request); request->dataplane = dataplane; QLIST_INSERT_HEAD(&dataplane->freelist, request, list); - dataplane->requests_inflight--; } /* @@ -246,7 +251,6 @@ static int xen_block_copy_request(XenBlockRequest *request) } static int xen_block_do_aio(XenBlockRequest *request); -static int xen_block_send_response(XenBlockRequest *request); static void xen_block_complete_aio(void *opaque, int ret) { @@ -286,7 +290,6 @@ static void xen_block_complete_aio(void *opaque, int ret) } request->status = request->aio_errors ? BLKIF_RSP_ERROR : BLKIF_RSP_OKAY; - xen_block_finish_request(request); switch (request->req.operation) { case BLKIF_OP_WRITE: @@ -306,17 +309,8 @@ static void xen_block_complete_aio(void *opaque, int ret) default: break; } - if (xen_block_send_response(request)) { - Error *local_err = NULL; - xen_device_notify_event_channel(dataplane->xendev, - dataplane->event_channel, - &local_err); - if (local_err) { - error_report_err(local_err); - } - } - xen_block_release_request(request); + xen_block_complete_request(request); if (dataplane->more_work) { qemu_bh_schedule(dataplane->bh); @@ -420,8 +414,8 @@ static int xen_block_do_aio(XenBlockRequest *request) return 0; err: - xen_block_finish_request(request); request->status = BLKIF_RSP_ERROR; + xen_block_complete_request(request); return -1; } @@ -575,17 +569,7 @@ static bool xen_block_handle_requests(XenBlockDataPlane *dataplane) break; }; - if (xen_block_send_response(request)) { - Error *local_err = NULL; - - xen_device_notify_event_channel(dataplane->xendev, - dataplane->event_channel, - &local_err); - if (local_err) { - error_report_err(local_err); - } - } - xen_block_release_request(request); + xen_block_complete_request(request); continue; } @@ -685,12 +669,24 @@ void xen_block_dataplane_stop(XenBlockDataPlane *dataplane) return; } + xendev = dataplane->xendev; + aio_context_acquire(dataplane->ctx); + if (dataplane->event_channel) { + /* Only reason for failure is a NULL channel */ + xen_device_set_event_channel_context(xendev, dataplane->event_channel, + qemu_get_aio_context(), + &error_abort); + } /* Xen doesn't have multiple users for nodes, so this can't fail */ blk_set_aio_context(dataplane->blk, qemu_get_aio_context(), &error_abort); aio_context_release(dataplane->ctx); - xendev = dataplane->xendev; + /* + * Now that the context has been moved onto the main thread, cancel + * further processing. + */ + qemu_bh_cancel(dataplane->bh); if (dataplane->event_channel) { Error *local_err = NULL; @@ -807,7 +803,7 @@ void xen_block_dataplane_start(XenBlockDataPlane *dataplane, } dataplane->event_channel = - xen_device_bind_event_channel(xendev, dataplane->ctx, event_channel, + xen_device_bind_event_channel(xendev, event_channel, xen_block_dataplane_event, dataplane, &local_err); if (local_err) { @@ -818,7 +814,11 @@ void xen_block_dataplane_start(XenBlockDataPlane *dataplane, aio_context_acquire(dataplane->ctx); /* If other users keep the BlockBackend in the iothread, that's ok */ blk_set_aio_context(dataplane->blk, dataplane->ctx, NULL); + /* Only reason for failure is a NULL channel */ + xen_device_set_event_channel_context(xendev, dataplane->event_channel, + dataplane->ctx, &error_abort); aio_context_release(dataplane->ctx); + return; stop: diff --git a/hw/block/fdc.c b/hw/block/fdc.c index ac5d31e8c1..33bc9e2f92 100644 --- a/hw/block/fdc.c +++ b/hw/block/fdc.c @@ -606,7 +606,7 @@ static void floppy_drive_class_init(ObjectClass *klass, void *data) k->realize = floppy_drive_realize; set_bit(DEVICE_CATEGORY_STORAGE, k->categories); k->bus_type = TYPE_FLOPPY_BUS; - k->props = floppy_drive_properties; + device_class_set_props(k, floppy_drive_properties); k->desc = "virtual floppy drive"; } @@ -1714,53 +1714,28 @@ static void fdctrl_start_transfer(FDCtrl *fdctrl, int direction) } fdctrl->eot = fdctrl->fifo[6]; if (fdctrl->dor & FD_DOR_DMAEN) { - IsaDmaTransferMode dma_mode; + /* DMA transfer is enabled. */ IsaDmaClass *k = ISADMA_GET_CLASS(fdctrl->dma); - bool dma_mode_ok; - /* DMA transfer are enabled. Check if DMA channel is well programmed */ - dma_mode = k->get_transfer_mode(fdctrl->dma, fdctrl->dma_chann); - FLOPPY_DPRINTF("dma_mode=%d direction=%d (%d - %d)\n", - dma_mode, direction, - (128 << fdctrl->fifo[5]) * + + FLOPPY_DPRINTF("direction=%d (%d - %d)\n", + direction, (128 << fdctrl->fifo[5]) * (cur_drv->last_sect - ks + 1), fdctrl->data_len); - switch (direction) { - case FD_DIR_SCANE: - case FD_DIR_SCANL: - case FD_DIR_SCANH: - dma_mode_ok = (dma_mode == ISADMA_TRANSFER_VERIFY); - break; - case FD_DIR_WRITE: - dma_mode_ok = (dma_mode == ISADMA_TRANSFER_WRITE); - break; - case FD_DIR_READ: - dma_mode_ok = (dma_mode == ISADMA_TRANSFER_READ); - break; - case FD_DIR_VERIFY: - dma_mode_ok = true; - break; - default: - dma_mode_ok = false; - break; - } - if (dma_mode_ok) { - /* No access is allowed until DMA transfer has completed */ - fdctrl->msr &= ~FD_MSR_RQM; - if (direction != FD_DIR_VERIFY) { - /* Now, we just have to wait for the DMA controller to - * recall us... - */ - k->hold_DREQ(fdctrl->dma, fdctrl->dma_chann); - k->schedule(fdctrl->dma); - } else { - /* Start transfer */ - fdctrl_transfer_handler(fdctrl, fdctrl->dma_chann, 0, - fdctrl->data_len); - } - return; + + /* No access is allowed until DMA transfer has completed */ + fdctrl->msr &= ~FD_MSR_RQM; + if (direction != FD_DIR_VERIFY) { + /* + * Now, we just have to wait for the DMA controller to + * recall us... + */ + k->hold_DREQ(fdctrl->dma, fdctrl->dma_chann); + k->schedule(fdctrl->dma); } else { - FLOPPY_DPRINTF("bad dma_mode=%d direction=%d\n", dma_mode, - direction); + /* Start transfer */ + fdctrl_transfer_handler(fdctrl, fdctrl->dma_chann, 0, + fdctrl->data_len); } + return; } FLOPPY_DPRINTF("start non-DMA transfer\n"); fdctrl->msr |= FD_MSR_NONDMA | FD_MSR_RQM; @@ -2827,7 +2802,7 @@ static void isabus_fdc_class_init(ObjectClass *klass, void *data) dc->fw_name = "fdc"; dc->reset = fdctrl_external_reset_isa; dc->vmsd = &vmstate_isa_fdc; - dc->props = isa_fdc_properties; + device_class_set_props(dc, isa_fdc_properties); set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); } @@ -2880,7 +2855,7 @@ static void sysbus_fdc_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->props = sysbus_fdc_properties; + device_class_set_props(dc, sysbus_fdc_properties); set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); } @@ -2906,7 +2881,7 @@ static void sun4m_fdc_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->props = sun4m_fdc_properties; + device_class_set_props(dc, sun4m_fdc_properties); set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); } diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c index 11ff5b9ad7..8227088441 100644 --- a/hw/block/m25p80.c +++ b/hw/block/m25p80.c @@ -32,17 +32,7 @@ #include "qemu/module.h" #include "qemu/error-report.h" #include "qapi/error.h" - -#ifndef M25P80_ERR_DEBUG -#define M25P80_ERR_DEBUG 0 -#endif - -#define DB_PRINT_L(level, ...) do { \ - if (M25P80_ERR_DEBUG > (level)) { \ - fprintf(stderr, ": %s: ", __func__); \ - fprintf(stderr, ## __VA_ARGS__); \ - } \ -} while (0) +#include "trace.h" /* Fields for FlashPartInfo->flags */ @@ -574,7 +564,8 @@ static void flash_erase(Flash *s, int offset, FlashCMD cmd) abort(); } - DB_PRINT_L(0, "offset = %#x, len = %d\n", offset, len); + trace_m25p80_flash_erase(s, offset, len); + if ((s->pi->flags & capa_to_assert) != capa_to_assert) { qemu_log_mask(LOG_GUEST_ERROR, "M25P80: %d erase size not supported by" " device\n", len); @@ -607,8 +598,7 @@ void flash_write8(Flash *s, uint32_t addr, uint8_t data) } if ((prev ^ data) & data) { - DB_PRINT_L(1, "programming zero to one! addr=%" PRIx32 " %" PRIx8 - " -> %" PRIx8 "\n", addr, prev, data); + trace_m25p80_programming_zero_to_one(s, addr, prev, data); } if (s->pi->flags & EEPROM) { @@ -662,6 +652,9 @@ static void complete_collecting_data(Flash *s) s->state = STATE_IDLE; + trace_m25p80_complete_collecting(s, s->cmd_in_progress, n, s->ear, + s->cur_addr); + switch (s->cmd_in_progress) { case DPP: case QPP: @@ -825,7 +818,7 @@ static void reset_memory(Flash *s) break; } - DB_PRINT_L(0, "Reset done.\n"); + trace_m25p80_reset_done(s); } static void decode_fast_read_cmd(Flash *s) @@ -941,9 +934,10 @@ static void decode_qio_read_cmd(Flash *s) static void decode_new_cmd(Flash *s, uint32_t value) { - s->cmd_in_progress = value; int i; - DB_PRINT_L(0, "decoded new command:%x\n", value); + + s->cmd_in_progress = value; + trace_m25p80_command_decoded(s, value); if (value != RESET_MEMORY) { s->reset_enable = false; @@ -1042,12 +1036,15 @@ static void decode_new_cmd(Flash *s, uint32_t value) break; case JEDEC_READ: - DB_PRINT_L(0, "populated jedec code\n"); + trace_m25p80_populated_jedec(s); for (i = 0; i < s->pi->id_len; i++) { s->data[i] = s->pi->id[i]; } + for (; i < SPI_NOR_MAX_ID_LEN; i++) { + s->data[i] = 0; + } - s->len = s->pi->id_len; + s->len = SPI_NOR_MAX_ID_LEN; s->pos = 0; s->state = STATE_READING_DATA; break; @@ -1063,7 +1060,7 @@ static void decode_new_cmd(Flash *s, uint32_t value) case BULK_ERASE_60: case BULK_ERASE: if (s->write_enable) { - DB_PRINT_L(0, "chip erase\n"); + trace_m25p80_chip_erase(s); flash_erase(s, 0, BULK_ERASE); } else { qemu_log_mask(LOG_GUEST_ERROR, "M25P80: chip erase with write " @@ -1164,6 +1161,11 @@ static void decode_new_cmd(Flash *s, uint32_t value) s->quad_enable = false; break; default: + s->pos = 0; + s->len = 1; + s->state = STATE_READING_DATA; + s->data_read_loop = true; + s->data[0] = 0; qemu_log_mask(LOG_GUEST_ERROR, "M25P80: Unknown cmd %x\n", value); break; } @@ -1184,7 +1186,7 @@ static int m25p80_cs(SSISlave *ss, bool select) s->data_read_loop = false; } - DB_PRINT_L(0, "%sselect\n", select ? "de" : ""); + trace_m25p80_select(s, select ? "de" : ""); return 0; } @@ -1194,19 +1196,20 @@ static uint32_t m25p80_transfer8(SSISlave *ss, uint32_t tx) Flash *s = M25P80(ss); uint32_t r = 0; + trace_m25p80_transfer(s, s->state, s->len, s->needed_bytes, s->pos, + s->cur_addr, (uint8_t)tx); + switch (s->state) { case STATE_PAGE_PROGRAM: - DB_PRINT_L(1, "page program cur_addr=%#" PRIx32 " data=%" PRIx8 "\n", - s->cur_addr, (uint8_t)tx); + trace_m25p80_page_program(s, s->cur_addr, (uint8_t)tx); flash_write8(s, s->cur_addr, (uint8_t)tx); s->cur_addr = (s->cur_addr + 1) & (s->size - 1); break; case STATE_READ: r = s->storage[s->cur_addr]; - DB_PRINT_L(1, "READ 0x%" PRIx32 "=%" PRIx8 "\n", s->cur_addr, - (uint8_t)r); + trace_m25p80_read_byte(s, s->cur_addr, (uint8_t)r); s->cur_addr = (s->cur_addr + 1) & (s->size - 1); break; @@ -1244,6 +1247,7 @@ static uint32_t m25p80_transfer8(SSISlave *ss, uint32_t tx) } r = s->data[s->pos]; + trace_m25p80_read_data(s, s->pos, (uint8_t)r); s->pos++; if (s->pos == s->len) { s->pos = 0; @@ -1281,7 +1285,7 @@ static void m25p80_realize(SSISlave *ss, Error **errp) return; } - DB_PRINT_L(0, "Binding to IF_MTD drive\n"); + trace_m25p80_binding(s); s->storage = blk_blockalign(s->blk, s->size); if (blk_pread(s->blk, 0, s->storage, s->size) != s->size) { @@ -1289,7 +1293,7 @@ static void m25p80_realize(SSISlave *ss, Error **errp) return; } } else { - DB_PRINT_L(0, "No BDRV - binding to RAM\n"); + trace_m25p80_binding_no_bdrv(s); s->storage = blk_blockalign(NULL, s->size); memset(s->storage, 0xFF, s->size); } @@ -1391,7 +1395,7 @@ static void m25p80_class_init(ObjectClass *klass, void *data) k->set_cs = m25p80_cs; k->cs_polarity = SSI_CS_LOW; dc->vmsd = &vmstate_m25p80; - dc->props = m25p80_properties; + device_class_set_props(dc, m25p80_properties); dc->reset = m25p80_reset; mc->pi = data; } diff --git a/hw/block/nand.c b/hw/block/nand.c index e396004315..bba89688ba 100644 --- a/hw/block/nand.c +++ b/hw/block/nand.c @@ -448,7 +448,7 @@ static void nand_class_init(ObjectClass *klass, void *data) dc->realize = nand_realize; dc->reset = nand_reset; dc->vmsd = &vmstate_nand; - dc->props = nand_properties; + device_class_set_props(dc, nand_properties); } static const TypeInfo nand_info = { diff --git a/hw/block/nvme.c b/hw/block/nvme.c index 12d8254250..d28335cbf3 100644 --- a/hw/block/nvme.c +++ b/hw/block/nvme.c @@ -1475,7 +1475,7 @@ static void nvme_class_init(ObjectClass *oc, void *data) set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); dc->desc = "Non-Volatile Memory Express"; - dc->props = nvme_props; + device_class_set_props(dc, nvme_props); dc->vmsd = &nvme_vmstate; } diff --git a/hw/block/onenand.c b/hw/block/onenand.c index fcc5a69b90..898ac563a3 100644 --- a/hw/block/onenand.c +++ b/hw/block/onenand.c @@ -822,7 +822,7 @@ static void onenand_realize(DeviceState *dev, Error **errp) onenand_mem_setup(s); sysbus_init_irq(sbd, &s->intr); sysbus_init_mmio(sbd, &s->container); - vmstate_register(dev, + vmstate_register(VMSTATE_IF(dev), ((s->shift & 0x7f) << 24) | ((s->id.man & 0xff) << 16) | ((s->id.dev & 0xff) << 8) @@ -845,7 +845,7 @@ static void onenand_class_init(ObjectClass *klass, void *data) dc->realize = onenand_realize; dc->reset = onenand_system_reset; - dc->props = onenand_properties; + device_class_set_props(dc, onenand_properties); } static const TypeInfo onenand_info = { diff --git a/hw/block/pflash_cfi01.c b/hw/block/pflash_cfi01.c index 54e6ebd385..24f3bce7ef 100644 --- a/hw/block/pflash_cfi01.c +++ b/hw/block/pflash_cfi01.c @@ -930,7 +930,7 @@ static void pflash_cfi01_class_init(ObjectClass *klass, void *data) dc->reset = pflash_cfi01_system_reset; dc->realize = pflash_cfi01_realize; - dc->props = pflash_cfi01_properties; + device_class_set_props(dc, pflash_cfi01_properties); dc->vmsd = &vmstate_pflash; set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); } diff --git a/hw/block/pflash_cfi02.c b/hw/block/pflash_cfi02.c index c7d92c3e79..12f18d401a 100644 --- a/hw/block/pflash_cfi02.c +++ b/hw/block/pflash_cfi02.c @@ -308,7 +308,6 @@ static uint64_t pflash_read(void *opaque, hwaddr offset, unsigned int width) hwaddr boff; uint64_t ret; - ret = -1; /* Lazy reset to ROMD mode after a certain amount of read accesses */ if (!pfl->rom_mode && pfl->wcycle == 0 && ++pfl->read_counter > PFLASH_LAZY_ROMD_THRESHOLD) { @@ -963,7 +962,7 @@ static void pflash_cfi02_class_init(ObjectClass *klass, void *data) dc->realize = pflash_cfi02_realize; dc->unrealize = pflash_cfi02_unrealize; - dc->props = pflash_cfi02_properties; + device_class_set_props(dc, pflash_cfi02_properties); set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); } diff --git a/hw/block/swim.c b/hw/block/swim.c index c6d117e89b..8f124782f4 100644 --- a/hw/block/swim.c +++ b/hw/block/swim.c @@ -239,7 +239,7 @@ static void swim_drive_class_init(ObjectClass *klass, void *data) k->realize = swim_drive_realize; set_bit(DEVICE_CATEGORY_STORAGE, k->categories); k->bus_type = TYPE_SWIM_BUS; - k->props = swim_drive_properties; + device_class_set_props(k, swim_drive_properties); k->desc = "virtual SWIM drive"; } diff --git a/hw/block/trace-events b/hw/block/trace-events index c03e80c2c9..bf6d11b58b 100644 --- a/hw/block/trace-events +++ b/hw/block/trace-events @@ -37,7 +37,7 @@ nvme_rw(const char *verb, uint32_t blk_count, uint64_t byte_count, uint64_t lba) nvme_create_sq(uint64_t addr, uint16_t sqid, uint16_t cqid, uint16_t qsize, uint16_t qflags) "create submission queue, addr=0x%"PRIx64", sqid=%"PRIu16", cqid=%"PRIu16", qsize=%"PRIu16", qflags=%"PRIu16"" nvme_create_cq(uint64_t addr, uint16_t cqid, uint16_t vector, uint16_t size, uint16_t qflags, int ien) "create completion queue, addr=0x%"PRIx64", cqid=%"PRIu16", vector=%"PRIu16", qsize=%"PRIu16", qflags=%"PRIu16", ien=%d" nvme_del_sq(uint16_t qid) "deleting submission queue sqid=%"PRIu16"" -nvme_del_cq(uint16_t cqid) "deleted completion queue, sqid=%"PRIu16"" +nvme_del_cq(uint16_t cqid) "deleted completion queue, cqid=%"PRIu16"" nvme_identify_ctrl(void) "identify controller" nvme_identify_ns(uint16_t ns) "identify namespace, nsid=%"PRIu16"" nvme_identify_nslist(uint16_t ns) "identify namespace list, nsid=%"PRIu16"" @@ -134,3 +134,19 @@ xen_block_blockdev_add(char *str) "%s" xen_block_blockdev_del(const char *node_name) "%s" xen_block_device_create(unsigned int number) "%u" xen_block_device_destroy(unsigned int number) "%u" + +# m25p80.c +m25p80_flash_erase(void *s, int offset, uint32_t len) "[%p] offset = 0x%"PRIx32", len = %u" +m25p80_programming_zero_to_one(void *s, uint32_t addr, uint8_t prev, uint8_t data) "[%p] programming zero to one! addr=0x%"PRIx32" 0x%"PRIx8" -> 0x%"PRIx8 +m25p80_reset_done(void *s) "[%p] Reset done." +m25p80_command_decoded(void *s, uint32_t cmd) "[%p] new command:0x%"PRIx32 +m25p80_complete_collecting(void *s, uint32_t cmd, int n, uint8_t ear, uint32_t cur_addr) "[%p] decode cmd: 0x%"PRIx32" len %d ear 0x%"PRIx8" addr 0x%"PRIx32 +m25p80_populated_jedec(void *s) "[%p] populated jedec code" +m25p80_chip_erase(void *s) "[%p] chip erase" +m25p80_select(void *s, const char *what) "[%p] %sselect" +m25p80_page_program(void *s, uint32_t addr, uint8_t tx) "[%p] page program cur_addr=0x%"PRIx32" data=0x%"PRIx8 +m25p80_transfer(void *s, uint8_t state, uint32_t len, uint8_t needed, uint32_t pos, uint32_t cur_addr, uint8_t t) "[%p] Transfer state 0x%"PRIx8" len 0x%"PRIx32" needed 0x%"PRIx8" pos 0x%"PRIx32" addr 0x%"PRIx32" tx 0x%"PRIx8 +m25p80_read_byte(void *s, uint32_t addr, uint8_t v) "[%p] Read byte 0x%"PRIx32"=0x%"PRIx8 +m25p80_read_data(void *s, uint32_t pos, uint8_t v) "[%p] Read data 0x%"PRIx32"=0x%"PRIx8 +m25p80_binding(void *s) "[%p] Binding to IF_MTD drive" +m25p80_binding_no_bdrv(void *s) "[%p] No BDRV - binding to RAM" diff --git a/hw/block/vhost-user-blk.c b/hw/block/vhost-user-blk.c index 63da9bb619..17df5338e7 100644 --- a/hw/block/vhost-user-blk.c +++ b/hw/block/vhost-user-blk.c @@ -306,7 +306,7 @@ static int vhost_user_blk_connect(DeviceState *dev) s->connected = true; s->dev.nvqs = s->num_queues; - s->dev.vqs = s->vqs; + s->dev.vqs = s->vhost_vqs; s->dev.vq_index = 0; s->dev.backend_features = 0; @@ -349,19 +349,7 @@ static void vhost_user_blk_disconnect(DeviceState *dev) vhost_dev_cleanup(&s->dev); } -static gboolean vhost_user_blk_watch(GIOChannel *chan, GIOCondition cond, - void *opaque) -{ - DeviceState *dev = opaque; - VirtIODevice *vdev = VIRTIO_DEVICE(dev); - VHostUserBlk *s = VHOST_USER_BLK(vdev); - - qemu_chr_fe_disconnect(&s->chardev); - - return true; -} - -static void vhost_user_blk_event(void *opaque, int event) +static void vhost_user_blk_event(void *opaque, QEMUChrEvent event) { DeviceState *dev = opaque; VirtIODevice *vdev = VIRTIO_DEVICE(dev); @@ -373,15 +361,14 @@ static void vhost_user_blk_event(void *opaque, int event) qemu_chr_fe_disconnect(&s->chardev); return; } - s->watch = qemu_chr_fe_add_watch(&s->chardev, G_IO_HUP, - vhost_user_blk_watch, dev); break; case CHR_EVENT_CLOSED: vhost_user_blk_disconnect(dev); - if (s->watch) { - g_source_remove(s->watch); - s->watch = 0; - } + break; + case CHR_EVENT_BREAK: + case CHR_EVENT_MUX_IN: + case CHR_EVENT_MUX_OUT: + /* Ignore */ break; } } @@ -415,14 +402,14 @@ static void vhost_user_blk_device_realize(DeviceState *dev, Error **errp) virtio_init(vdev, "virtio-blk", VIRTIO_ID_BLOCK, sizeof(struct virtio_blk_config)); + s->virtqs = g_new(VirtQueue *, s->num_queues); for (i = 0; i < s->num_queues; i++) { - virtio_add_queue(vdev, s->queue_size, - vhost_user_blk_handle_output); + s->virtqs[i] = virtio_add_queue(vdev, s->queue_size, + vhost_user_blk_handle_output); } s->inflight = g_new0(struct vhost_inflight, 1); - s->vqs = g_new0(struct vhost_virtqueue, s->num_queues); - s->watch = 0; + s->vhost_vqs = g_new0(struct vhost_virtqueue, s->num_queues); s->connected = false; qemu_chr_fe_set_handlers(&s->chardev, NULL, NULL, vhost_user_blk_event, @@ -453,8 +440,12 @@ reconnect: return; virtio_err: - g_free(s->vqs); + g_free(s->vhost_vqs); g_free(s->inflight); + for (i = 0; i < s->num_queues; i++) { + virtio_delete_queue(s->virtqs[i]); + } + g_free(s->virtqs); virtio_cleanup(vdev); vhost_user_cleanup(&s->vhost_user); } @@ -463,14 +454,20 @@ static void vhost_user_blk_device_unrealize(DeviceState *dev, Error **errp) { VirtIODevice *vdev = VIRTIO_DEVICE(dev); VHostUserBlk *s = VHOST_USER_BLK(dev); + int i; virtio_set_status(vdev, 0); qemu_chr_fe_set_handlers(&s->chardev, NULL, NULL, NULL, NULL, NULL, NULL, false); vhost_dev_cleanup(&s->dev); vhost_dev_free_inflight(s->inflight); - g_free(s->vqs); + g_free(s->vhost_vqs); g_free(s->inflight); + + for (i = 0; i < s->num_queues; i++) { + virtio_delete_queue(s->virtqs[i]); + } + g_free(s->virtqs); virtio_cleanup(vdev); vhost_user_cleanup(&s->vhost_user); } @@ -506,7 +503,7 @@ static void vhost_user_blk_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); - dc->props = vhost_user_blk_properties; + device_class_set_props(dc, vhost_user_blk_properties); dc->vmsd = &vmstate_vhost_user_blk; set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); vdc->realize = vhost_user_blk_device_realize; diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c index 4c357d2928..97ba8a2187 100644 --- a/hw/block/virtio-blk.c +++ b/hw/block/virtio-blk.c @@ -764,13 +764,16 @@ bool virtio_blk_handle_vq(VirtIOBlock *s, VirtQueue *vq) { VirtIOBlockReq *req; MultiReqBuffer mrb = {}; + bool suppress_notifications = virtio_queue_get_notification(vq); bool progress = false; aio_context_acquire(blk_get_aio_context(s->blk)); blk_io_plug(s->blk); do { - virtio_queue_set_notification(vq, 0); + if (suppress_notifications) { + virtio_queue_set_notification(vq, 0); + } while ((req = virtio_blk_get_request(s, vq))) { progress = true; @@ -781,7 +784,9 @@ bool virtio_blk_handle_vq(VirtIOBlock *s, VirtQueue *vq) } } - virtio_queue_set_notification(vq, 1); + if (suppress_notifications) { + virtio_queue_set_notification(vq, 1); + } } while (!virtio_queue_empty(vq)); if (mrb.num_reqs) { @@ -908,7 +913,8 @@ static void virtio_blk_update_config(VirtIODevice *vdev, uint8_t *config) blk_get_geometry(s->blk, &capacity); memset(&blkcfg, 0, sizeof(blkcfg)); virtio_stq_p(vdev, &blkcfg.capacity, capacity); - virtio_stl_p(vdev, &blkcfg.seg_max, 128 - 2); + virtio_stl_p(vdev, &blkcfg.seg_max, + s->conf.seg_max_adjust ? s->conf.queue_size - 2 : 128 - 2); virtio_stw_p(vdev, &blkcfg.geometry.cylinders, conf->cyls); virtio_stl_p(vdev, &blkcfg.blk_size, blk_size); virtio_stw_p(vdev, &blkcfg.min_io_size, conf->min_io_size / blk_size); @@ -991,7 +997,9 @@ static uint64_t virtio_blk_get_features(VirtIODevice *vdev, uint64_t features, virtio_add_feature(&features, VIRTIO_BLK_F_SCSI); } - if (blk_enable_write_cache(s->blk)) { + if (blk_enable_write_cache(s->blk) || + (s->conf.x_enable_wce_if_config_wce && + virtio_has_feature(features, VIRTIO_BLK_F_CONFIG_WCE))) { virtio_add_feature(&features, VIRTIO_BLK_F_WCE); } if (blk_is_read_only(s->blk)) { @@ -1131,6 +1139,11 @@ static void virtio_blk_device_realize(DeviceState *dev, Error **errp) error_setg(errp, "num-queues property must be larger than 0"); return; } + if (conf->queue_size <= 2) { + error_setg(errp, "invalid queue-size property (%" PRIu16 "), " + "must be > 2", conf->queue_size); + return; + } if (!is_power_of_2(conf->queue_size) || conf->queue_size > VIRTQUEUE_MAX_SIZE) { error_setg(errp, "invalid queue-size property (%" PRIu16 "), " @@ -1191,6 +1204,9 @@ static void virtio_blk_device_realize(DeviceState *dev, Error **errp) virtio_blk_data_plane_create(vdev, conf, &s->dataplane, &err); if (err != NULL) { error_propagate(errp, err); + for (i = 0; i < conf->num_queues; i++) { + virtio_del_queue(vdev, i); + } virtio_cleanup(vdev); return; } @@ -1259,7 +1275,8 @@ static Property virtio_blk_properties[] = { DEFINE_PROP_BIT("request-merging", VirtIOBlock, conf.request_merging, 0, true), DEFINE_PROP_UINT16("num-queues", VirtIOBlock, conf.num_queues, 1), - DEFINE_PROP_UINT16("queue-size", VirtIOBlock, conf.queue_size, 128), + DEFINE_PROP_UINT16("queue-size", VirtIOBlock, conf.queue_size, 256), + DEFINE_PROP_BOOL("seg-max-adjust", VirtIOBlock, conf.seg_max_adjust, true), DEFINE_PROP_LINK("iothread", VirtIOBlock, conf.iothread, TYPE_IOTHREAD, IOThread *), DEFINE_PROP_BIT64("discard", VirtIOBlock, host_features, @@ -1270,6 +1287,8 @@ static Property virtio_blk_properties[] = { conf.max_discard_sectors, BDRV_REQUEST_MAX_SECTORS), DEFINE_PROP_UINT32("max-write-zeroes-sectors", VirtIOBlock, conf.max_write_zeroes_sectors, BDRV_REQUEST_MAX_SECTORS), + DEFINE_PROP_BOOL("x-enable-wce-if-config-wce", VirtIOBlock, + conf.x_enable_wce_if_config_wce, true), DEFINE_PROP_END_OF_LIST(), }; @@ -1278,7 +1297,7 @@ static void virtio_blk_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); - dc->props = virtio_blk_properties; + device_class_set_props(dc, virtio_blk_properties); dc->vmsd = &vmstate_virtio_blk; set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); vdc->realize = virtio_blk_device_realize; diff --git a/hw/block/xen-block.c b/hw/block/xen-block.c index 879fc310a4..99cb4c67cb 100644 --- a/hw/block/xen-block.c +++ b/hw/block/xen-block.c @@ -18,6 +18,7 @@ #include "qapi/visitor.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qstring.h" +#include "qom/object_interfaces.h" #include "hw/xen/xen_common.h" #include "hw/block/xen_blkif.h" #include "hw/qdev-properties.h" @@ -554,7 +555,7 @@ static void xen_block_class_init(ObjectClass *class, void *data) xendev_class->frontend_changed = xen_block_frontend_changed; xendev_class->unrealize = xen_block_unrealize; - dev_class->props = xen_block_props; + device_class_set_props(dev_class, xen_block_props); } static const TypeInfo xen_block_type_info = { @@ -858,10 +859,18 @@ static XenBlockIOThread *xen_block_iothread_create(const char *id, { XenBlockIOThread *iothread = g_new(XenBlockIOThread, 1); Error *local_err = NULL; + QDict *opts; + QObject *ret_data = NULL; iothread->id = g_strdup(id); - qmp_object_add(TYPE_IOTHREAD, id, false, NULL, &local_err); + opts = qdict_new(); + qdict_put_str(opts, "qom-type", TYPE_IOTHREAD); + qdict_put_str(opts, "id", id); + qmp_object_add(opts, &ret_data, &local_err); + qobject_unref(opts); + qobject_unref(ret_data); + if (local_err) { error_propagate(errp, local_err); @@ -989,29 +998,27 @@ static void xen_block_device_destroy(XenBackendInstance *backend, XenBlockVdev *vdev = &blockdev->props.vdev; XenBlockDrive *drive = blockdev->drive; XenBlockIOThread *iothread = blockdev->iothread; + Error *local_err = NULL; trace_xen_block_device_destroy(vdev->number); object_unparent(OBJECT(xendev)); if (iothread) { - Error *local_err = NULL; - xen_block_iothread_destroy(iothread, &local_err); if (local_err) { error_propagate_prepend(errp, local_err, - "failed to destroy iothread: "); + "failed to destroy iothread: "); return; } } if (drive) { - Error *local_err = NULL; - xen_block_drive_destroy(drive, &local_err); if (local_err) { error_propagate_prepend(errp, local_err, - "failed to destroy drive: "); + "failed to destroy drive: "); + return; } } } diff --git a/hw/bt/Kconfig b/hw/bt/Kconfig deleted file mode 100644 index 554a9ee75e..0000000000 --- a/hw/bt/Kconfig +++ /dev/null @@ -1,2 +0,0 @@ -config BLUETOOTH - bool diff --git a/hw/bt/Makefile.objs b/hw/bt/Makefile.objs deleted file mode 100644 index 867a7d2e8a..0000000000 --- a/hw/bt/Makefile.objs +++ /dev/null @@ -1,3 +0,0 @@ -common-obj-y += core.o l2cap.o sdp.o hci.o hid.o -common-obj-y += hci-csr.o - diff --git a/hw/bt/core.c b/hw/bt/core.c deleted file mode 100644 index dfb196e2a4..0000000000 --- a/hw/bt/core.c +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Convenience functions for bluetooth. - * - * Copyright (C) 2008 Andrzej Zaborowski - * - * 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 or - * (at your option) version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#include "qemu/osdep.h" -#include "qemu/error-report.h" -#include "sysemu/bt.h" -#include "hw/bt.h" - -/* Slave implementations can ignore this */ -static void bt_dummy_lmp_mode_change(struct bt_link_s *link) -{ -} - -/* Slaves should never receive these PDUs */ -static void bt_dummy_lmp_connection_complete(struct bt_link_s *link) -{ - if (link->slave->reject_reason) - error_report("%s: stray LMP_not_accepted received, fixme", __func__); - else - error_report("%s: stray LMP_accepted received, fixme", __func__); - exit(-1); -} - -static void bt_dummy_lmp_disconnect_master(struct bt_link_s *link) -{ - error_report("%s: stray LMP_detach received, fixme", __func__); - exit(-1); -} - -static void bt_dummy_lmp_acl_resp(struct bt_link_s *link, - const uint8_t *data, int start, int len) -{ - error_report("%s: stray ACL response PDU, fixme", __func__); - exit(-1); -} - -/* Slaves that don't hold any additional per link state can use these */ -static void bt_dummy_lmp_connection_request(struct bt_link_s *req) -{ - struct bt_link_s *link = g_malloc0(sizeof(struct bt_link_s)); - - link->slave = req->slave; - link->host = req->host; - - req->host->reject_reason = 0; - req->host->lmp_connection_complete(link); -} - -static void bt_dummy_lmp_disconnect_slave(struct bt_link_s *link) -{ - g_free(link); -} - -static void bt_dummy_destroy(struct bt_device_s *device) -{ - bt_device_done(device); - g_free(device); -} - -static int bt_dev_idx = 0; - -void bt_device_init(struct bt_device_s *dev, struct bt_scatternet_s *net) -{ - memset(dev, 0, sizeof(*dev)); - dev->inquiry_scan = 1; - dev->page_scan = 1; - - dev->bd_addr.b[0] = bt_dev_idx & 0xff; - dev->bd_addr.b[1] = bt_dev_idx >> 8; - dev->bd_addr.b[2] = 0xd0; - dev->bd_addr.b[3] = 0xba; - dev->bd_addr.b[4] = 0xbe; - dev->bd_addr.b[5] = 0xba; - bt_dev_idx ++; - - /* Simple slave-only devices need to implement only .lmp_acl_data */ - dev->lmp_connection_complete = bt_dummy_lmp_connection_complete; - dev->lmp_disconnect_master = bt_dummy_lmp_disconnect_master; - dev->lmp_acl_resp = bt_dummy_lmp_acl_resp; - dev->lmp_mode_change = bt_dummy_lmp_mode_change; - dev->lmp_connection_request = bt_dummy_lmp_connection_request; - dev->lmp_disconnect_slave = bt_dummy_lmp_disconnect_slave; - - dev->handle_destroy = bt_dummy_destroy; - - dev->net = net; - dev->next = net->slave; - net->slave = dev; -} - -void bt_device_done(struct bt_device_s *dev) -{ - struct bt_device_s **p = &dev->net->slave; - - while (*p && *p != dev) - p = &(*p)->next; - if (*p != dev) { - error_report("%s: bad bt device \"%s\"", __func__, - dev->lmp_name ?: "(null)"); - exit(-1); - } - - *p = dev->next; -} - -static struct bt_vlan_s { - struct bt_scatternet_s net; - int id; - struct bt_vlan_s *next; -} *first_bt_vlan; - -/* find or alloc a new bluetooth "VLAN" */ -struct bt_scatternet_s *qemu_find_bt_vlan(int id) -{ - struct bt_vlan_s **pvlan, *vlan; - for (vlan = first_bt_vlan; vlan != NULL; vlan = vlan->next) { - if (vlan->id == id) - return &vlan->net; - } - vlan = g_malloc0(sizeof(struct bt_vlan_s)); - vlan->id = id; - pvlan = &first_bt_vlan; - while (*pvlan != NULL) - pvlan = &(*pvlan)->next; - *pvlan = vlan; - return &vlan->net; -} diff --git a/hw/bt/hci-csr.c b/hw/bt/hci-csr.c deleted file mode 100644 index 3d60654f44..0000000000 --- a/hw/bt/hci-csr.c +++ /dev/null @@ -1,512 +0,0 @@ -/* - * Bluetooth serial HCI transport. - * CSR41814 HCI with H4p vendor extensions. - * - * Copyright (C) 2008 Andrzej Zaborowski - * - * 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 or - * (at your option) version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#include "qemu/osdep.h" -#include "qemu/error-report.h" -#include "qemu/module.h" -#include "chardev/char-serial.h" -#include "qemu/timer.h" -#include "qemu/bswap.h" -#include "hw/irq.h" -#include "sysemu/bt.h" -#include "hw/bt.h" -#include "qapi/error.h" - -struct csrhci_s { - Chardev parent; - int enable; - qemu_irq *pins; - int pin_state; - int modem_state; -#define FIFO_LEN 4096 - int out_start; - int out_len; - int out_size; - uint8_t outfifo[FIFO_LEN * 2]; - uint8_t inpkt[FIFO_LEN]; - enum { - CSR_HDR_LEN, - CSR_DATA_LEN, - CSR_DATA - } in_state; - int in_len; - int in_hdr; - int in_needed; - QEMUTimer *out_tm; - int64_t baud_delay; - - bdaddr_t bd_addr; - struct HCIInfo *hci; -}; - -#define TYPE_CHARDEV_HCI "chardev-hci" -#define HCI_CHARDEV(obj) OBJECT_CHECK(struct csrhci_s, (obj), TYPE_CHARDEV_HCI) - -/* H4+ packet types */ -enum { - H4_CMD_PKT = 1, - H4_ACL_PKT = 2, - H4_SCO_PKT = 3, - H4_EVT_PKT = 4, - H4_NEG_PKT = 6, - H4_ALIVE_PKT = 7, -}; - -/* CSR41814 negotiation start magic packet */ -static const uint8_t csrhci_neg_packet[] = { - H4_NEG_PKT, 10, - 0x00, 0xa0, 0x01, 0x00, 0x00, - 0x4c, 0x00, 0x96, 0x00, 0x00, -}; - -/* CSR41814 vendor-specific command OCFs */ -enum { - OCF_CSR_SEND_FIRMWARE = 0x000, -}; - -static inline void csrhci_fifo_wake(struct csrhci_s *s) -{ - Chardev *chr = CHARDEV(s); - - if (!s->enable || !s->out_len) - return; - - /* XXX: Should wait for s->modem_state & CHR_TIOCM_RTS? */ - if (qemu_chr_be_can_write(chr)) { - qemu_chr_be_write(chr, s->outfifo + s->out_start++, 1); - s->out_len--; - if (s->out_start >= s->out_size) { - s->out_start = 0; - s->out_size = FIFO_LEN; - } - } - - if (s->out_len) - timer_mod(s->out_tm, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + s->baud_delay); -} - -#define csrhci_out_packetz(s, len) memset(csrhci_out_packet(s, len), 0, len) -static uint8_t *csrhci_out_packet(struct csrhci_s *s, int len) -{ - int off = s->out_start + s->out_len; - - /* TODO: do the padding here, i.e. align len */ - s->out_len += len; - - if (off < FIFO_LEN) { - if (off + len > FIFO_LEN && (s->out_size = off + len) > FIFO_LEN * 2) { - error_report("%s: can't alloc %i bytes", __func__, len); - exit(-1); - } - return s->outfifo + off; - } - - if (s->out_len > s->out_size) { - error_report("%s: can't alloc %i bytes", __func__, len); - exit(-1); - } - - return s->outfifo + off - s->out_size; -} - -static inline uint8_t *csrhci_out_packet_csr(struct csrhci_s *s, - int type, int len) -{ - uint8_t *ret = csrhci_out_packetz(s, len + 2); - - *ret ++ = type; - *ret ++ = len; - - return ret; -} - -static inline uint8_t *csrhci_out_packet_event(struct csrhci_s *s, - int evt, int len) -{ - uint8_t *ret = csrhci_out_packetz(s, - len + 1 + sizeof(struct hci_event_hdr)); - - *ret ++ = H4_EVT_PKT; - ((struct hci_event_hdr *) ret)->evt = evt; - ((struct hci_event_hdr *) ret)->plen = len; - - return ret + sizeof(struct hci_event_hdr); -} - -static void csrhci_in_packet_vendor(struct csrhci_s *s, int ocf, - uint8_t *data, int len) -{ - int offset; - uint8_t *rpkt; - - switch (ocf) { - case OCF_CSR_SEND_FIRMWARE: - /* Check if this is the bd_address packet */ - if (len >= 18 + 8 && data[12] == 0x01 && data[13] == 0x00) { - offset = 18; - s->bd_addr.b[0] = data[offset + 7]; /* Beyond cmd packet end(!?) */ - s->bd_addr.b[1] = data[offset + 6]; - s->bd_addr.b[2] = data[offset + 4]; - s->bd_addr.b[3] = data[offset + 0]; - s->bd_addr.b[4] = data[offset + 3]; - s->bd_addr.b[5] = data[offset + 2]; - - s->hci->bdaddr_set(s->hci, s->bd_addr.b); - error_report("%s: bd_address loaded from firmware: " - "%02x:%02x:%02x:%02x:%02x:%02x", __func__, - s->bd_addr.b[0], s->bd_addr.b[1], s->bd_addr.b[2], - s->bd_addr.b[3], s->bd_addr.b[4], s->bd_addr.b[5]); - } - - rpkt = csrhci_out_packet_event(s, EVT_VENDOR, 11); - /* Status bytes: no error */ - rpkt[9] = 0x00; - rpkt[10] = 0x00; - break; - - default: - error_report("%s: got a bad CMD packet", __func__); - return; - } - - csrhci_fifo_wake(s); -} - -static void csrhci_in_packet(struct csrhci_s *s, uint8_t *pkt) -{ - uint8_t *rpkt; - int opc; - - switch (*pkt ++) { - case H4_CMD_PKT: - opc = le16_to_cpu(((struct hci_command_hdr *) pkt)->opcode); - if (cmd_opcode_ogf(opc) == OGF_VENDOR_CMD) { - csrhci_in_packet_vendor(s, cmd_opcode_ocf(opc), - pkt + sizeof(struct hci_command_hdr), - s->in_len - sizeof(struct hci_command_hdr) - 1); - return; - } - - /* TODO: if the command is OCF_READ_LOCAL_COMMANDS or the likes, - * we need to send it to the HCI layer and then add our supported - * commands to the returned mask (such as OGF_VENDOR_CMD). With - * bt-hci.c we could just have hooks for this kind of commands but - * we can't with bt-host.c. */ - - s->hci->cmd_send(s->hci, pkt, s->in_len - 1); - break; - - case H4_EVT_PKT: - goto bad_pkt; - - case H4_ACL_PKT: - s->hci->acl_send(s->hci, pkt, s->in_len - 1); - break; - - case H4_SCO_PKT: - s->hci->sco_send(s->hci, pkt, s->in_len - 1); - break; - - case H4_NEG_PKT: - if (s->in_hdr != sizeof(csrhci_neg_packet) || - memcmp(pkt - 1, csrhci_neg_packet, s->in_hdr)) { - error_report("%s: got a bad NEG packet", __func__); - return; - } - pkt += 2; - - rpkt = csrhci_out_packet_csr(s, H4_NEG_PKT, 10); - - *rpkt ++ = 0x20; /* Operational settings negotiation Ok */ - memcpy(rpkt, pkt, 7); rpkt += 7; - *rpkt ++ = 0xff; - *rpkt = 0xff; - break; - - case H4_ALIVE_PKT: - if (s->in_hdr != 4 || pkt[1] != 0x55 || pkt[2] != 0x00) { - error_report("%s: got a bad ALIVE packet", __func__); - return; - } - - rpkt = csrhci_out_packet_csr(s, H4_ALIVE_PKT, 2); - - *rpkt ++ = 0xcc; - *rpkt = 0x00; - break; - - default: - bad_pkt: - /* TODO: error out */ - error_report("%s: got a bad packet", __func__); - break; - } - - csrhci_fifo_wake(s); -} - -static int csrhci_header_len(const uint8_t *pkt) -{ - switch (pkt[0]) { - case H4_CMD_PKT: - return HCI_COMMAND_HDR_SIZE; - case H4_EVT_PKT: - return HCI_EVENT_HDR_SIZE; - case H4_ACL_PKT: - return HCI_ACL_HDR_SIZE; - case H4_SCO_PKT: - return HCI_SCO_HDR_SIZE; - case H4_NEG_PKT: - return pkt[1] + 1; - case H4_ALIVE_PKT: - return 3; - } - - exit(-1); -} - -static int csrhci_data_len(const uint8_t *pkt) -{ - switch (*pkt ++) { - case H4_CMD_PKT: - /* It seems that vendor-specific command packets for H4+ are all - * one byte longer than indicated in the standard header. */ - if (le16_to_cpu(((struct hci_command_hdr *) pkt)->opcode) == 0xfc00) - return (((struct hci_command_hdr *) pkt)->plen + 1) & ~1; - - return ((struct hci_command_hdr *) pkt)->plen; - case H4_EVT_PKT: - return ((struct hci_event_hdr *) pkt)->plen; - case H4_ACL_PKT: - return le16_to_cpu(((struct hci_acl_hdr *) pkt)->dlen); - case H4_SCO_PKT: - return ((struct hci_sco_hdr *) pkt)->dlen; - case H4_NEG_PKT: - case H4_ALIVE_PKT: - return 0; - } - - exit(-1); -} - -static void csrhci_ready_for_next_inpkt(struct csrhci_s *s) -{ - s->in_state = CSR_HDR_LEN; - s->in_len = 0; - s->in_needed = 2; - s->in_hdr = INT_MAX; -} - -static int csrhci_write(struct Chardev *chr, - const uint8_t *buf, int len) -{ - struct csrhci_s *s = (struct csrhci_s *)chr; - int total = 0; - - if (!s->enable) - return 0; - - for (;;) { - int cnt = MIN(len, s->in_needed - s->in_len); - if (cnt) { - memcpy(s->inpkt + s->in_len, buf, cnt); - s->in_len += cnt; - buf += cnt; - len -= cnt; - total += cnt; - } - - if (s->in_len < s->in_needed) { - break; - } - - if (s->in_state == CSR_HDR_LEN) { - s->in_hdr = csrhci_header_len(s->inpkt) + 1; - assert(s->in_hdr >= s->in_needed); - s->in_needed = s->in_hdr; - s->in_state = CSR_DATA_LEN; - continue; - } - - if (s->in_state == CSR_DATA_LEN) { - s->in_needed += csrhci_data_len(s->inpkt); - /* hci_acl_hdr could specify more than 4096 bytes, so assert. */ - assert(s->in_needed <= sizeof(s->inpkt)); - s->in_state = CSR_DATA; - continue; - } - - if (s->in_state == CSR_DATA) { - csrhci_in_packet(s, s->inpkt); - csrhci_ready_for_next_inpkt(s); - } - } - - return total; -} - -static void csrhci_out_hci_packet_event(void *opaque, - const uint8_t *data, int len) -{ - struct csrhci_s *s = (struct csrhci_s *) opaque; - uint8_t *pkt = csrhci_out_packet(s, (len + 2) & ~1); /* Align */ - - *pkt ++ = H4_EVT_PKT; - memcpy(pkt, data, len); - - csrhci_fifo_wake(s); -} - -static void csrhci_out_hci_packet_acl(void *opaque, - const uint8_t *data, int len) -{ - struct csrhci_s *s = (struct csrhci_s *) opaque; - uint8_t *pkt = csrhci_out_packet(s, (len + 2) & ~1); /* Align */ - - *pkt ++ = H4_ACL_PKT; - pkt[len & ~1] = 0; - memcpy(pkt, data, len); - - csrhci_fifo_wake(s); -} - -static int csrhci_ioctl(struct Chardev *chr, int cmd, void *arg) -{ - QEMUSerialSetParams *ssp; - struct csrhci_s *s = (struct csrhci_s *) chr; - int prev_state = s->modem_state; - - switch (cmd) { - case CHR_IOCTL_SERIAL_SET_PARAMS: - ssp = (QEMUSerialSetParams *) arg; - s->baud_delay = NANOSECONDS_PER_SECOND / ssp->speed; - /* Moments later... (but shorter than 100ms) */ - s->modem_state |= CHR_TIOCM_CTS; - break; - - case CHR_IOCTL_SERIAL_GET_TIOCM: - *(int *) arg = s->modem_state; - break; - - case CHR_IOCTL_SERIAL_SET_TIOCM: - s->modem_state = *(int *) arg; - if (~s->modem_state & prev_state & CHR_TIOCM_RTS) - s->modem_state &= ~CHR_TIOCM_CTS; - break; - - default: - return -ENOTSUP; - } - return 0; -} - -static void csrhci_reset(struct csrhci_s *s) -{ - s->out_len = 0; - s->out_size = FIFO_LEN; - csrhci_ready_for_next_inpkt(s); - s->baud_delay = NANOSECONDS_PER_SECOND; - s->enable = 0; - - s->modem_state = 0; - /* After a while... (but sooner than 10ms) */ - s->modem_state |= CHR_TIOCM_CTS; - - memset(&s->bd_addr, 0, sizeof(bdaddr_t)); -} - -static void csrhci_out_tick(void *opaque) -{ - csrhci_fifo_wake((struct csrhci_s *) opaque); -} - -static void csrhci_pins(void *opaque, int line, int level) -{ - struct csrhci_s *s = (struct csrhci_s *) opaque; - int state = s->pin_state; - - s->pin_state &= ~(1 << line); - s->pin_state |= (!!level) << line; - - if ((state & ~s->pin_state) & (1 << csrhci_pin_reset)) { - /* TODO: Disappear from lower layers */ - csrhci_reset(s); - } - - if (s->pin_state == 3 && state != 3) { - s->enable = 1; - /* TODO: Wake lower layers up */ - } -} - -qemu_irq *csrhci_pins_get(Chardev *chr) -{ - struct csrhci_s *s = (struct csrhci_s *) chr; - - return s->pins; -} - -static void csrhci_open(Chardev *chr, - ChardevBackend *backend, - bool *be_opened, - Error **errp) -{ - struct csrhci_s *s = HCI_CHARDEV(chr); - - s->hci = qemu_next_hci(); - s->hci->opaque = s; - s->hci->evt_recv = csrhci_out_hci_packet_event; - s->hci->acl_recv = csrhci_out_hci_packet_acl; - - s->out_tm = timer_new_ns(QEMU_CLOCK_VIRTUAL, csrhci_out_tick, s); - s->pins = qemu_allocate_irqs(csrhci_pins, s, __csrhci_pins); - csrhci_reset(s); - *be_opened = false; -} - -static void char_hci_class_init(ObjectClass *oc, void *data) -{ - ChardevClass *cc = CHARDEV_CLASS(oc); - - cc->internal = true; - cc->open = csrhci_open; - cc->chr_write = csrhci_write; - cc->chr_ioctl = csrhci_ioctl; -} - -static const TypeInfo char_hci_type_info = { - .name = TYPE_CHARDEV_HCI, - .parent = TYPE_CHARDEV, - .instance_size = sizeof(struct csrhci_s), - .class_init = char_hci_class_init, -}; - -Chardev *uart_hci_init(void) -{ - return qemu_chardev_new(NULL, TYPE_CHARDEV_HCI, - NULL, NULL, &error_abort); -} - -static void register_types(void) -{ - type_register_static(&char_hci_type_info); -} - -type_init(register_types); diff --git a/hw/bt/hci.c b/hw/bt/hci.c deleted file mode 100644 index c7958f6c35..0000000000 --- a/hw/bt/hci.c +++ /dev/null @@ -1,2263 +0,0 @@ -/* - * QEMU Bluetooth HCI logic. - * - * Copyright (C) 2007 OpenMoko, Inc. - * Copyright (C) 2008 Andrzej Zaborowski - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - */ - -#include "qemu/osdep.h" -#include "qemu/error-report.h" -#include "qapi/error.h" -#include "qemu/timer.h" -#include "hw/usb.h" -#include "sysemu/bt.h" -#include "hw/bt.h" -#include "qapi/qmp/qerror.h" -#include "sysemu/replay.h" -#include "qemu/cutils.h" - -struct bt_hci_s { - uint8_t *(*evt_packet)(void *opaque); - void (*evt_submit)(void *opaque, int len); - void *opaque; - uint8_t evt_buf[256]; - - uint8_t acl_buf[4096]; - int acl_len; - - uint16_t asb_handle; - uint16_t psb_handle; - - int last_cmd; /* Note: Always little-endian */ - - struct bt_device_s *conn_req_host; - - struct { - int inquire; - int periodic; - int responses_left; - int responses; - QEMUTimer *inquiry_done; - QEMUTimer *inquiry_next; - int inquiry_length; - int inquiry_period; - int inquiry_mode; - -#define HCI_HANDLE_OFFSET 0x20 -#define HCI_HANDLES_MAX 0x10 - struct bt_hci_master_link_s { - struct bt_link_s *link; - void (*lmp_acl_data)(struct bt_link_s *link, - const uint8_t *data, int start, int len); - QEMUTimer *acl_mode_timer; - } handle[HCI_HANDLES_MAX]; - uint32_t role_bmp; - int last_handle; - int connecting; - bdaddr_t awaiting_bdaddr[HCI_HANDLES_MAX]; - } lm; - - uint8_t event_mask[8]; - uint16_t voice_setting; /* Notw: Always little-endian */ - uint16_t conn_accept_tout; - QEMUTimer *conn_accept_timer; - - struct HCIInfo info; - struct bt_device_s device; - - Error *replay_blocker; -}; - -#define DEFAULT_RSSI_DBM 20 - -#define hci_from_info(ptr) container_of((ptr), struct bt_hci_s, info) -#define hci_from_device(ptr) container_of((ptr), struct bt_hci_s, device) - -struct bt_hci_link_s { - struct bt_link_s btlink; - uint16_t handle; /* Local */ -}; - -/* LMP layer emulation */ -#if 0 -static void bt_submit_lmp(struct bt_device_s *bt, int length, uint8_t *data) -{ - int resp, resplen, error, op, tr; - uint8_t respdata[17]; - - if (length < 1) - return; - - tr = *data & 1; - op = *(data ++) >> 1; - resp = LMP_ACCEPTED; - resplen = 2; - respdata[1] = op; - error = 0; - length --; - - if (op >= 0x7c) { /* Extended opcode */ - op |= *(data ++) << 8; - resp = LMP_ACCEPTED_EXT; - resplen = 4; - respdata[0] = op >> 8; - respdata[1] = op & 0xff; - length --; - } - - switch (op) { - case LMP_ACCEPTED: - /* data[0] Op code - */ - if (length < 1) { - error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE; - goto not_accepted; - } - resp = 0; - break; - - case LMP_ACCEPTED_EXT: - /* data[0] Escape op code - * data[1] Extended op code - */ - if (length < 2) { - error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE; - goto not_accepted; - } - resp = 0; - break; - - case LMP_NOT_ACCEPTED: - /* data[0] Op code - * data[1] Error code - */ - if (length < 2) { - error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE; - goto not_accepted; - } - resp = 0; - break; - - case LMP_NOT_ACCEPTED_EXT: - /* data[0] Op code - * data[1] Extended op code - * data[2] Error code - */ - if (length < 3) { - error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE; - goto not_accepted; - } - resp = 0; - break; - - case LMP_HOST_CONNECTION_REQ: - break; - - case LMP_SETUP_COMPLETE: - resp = LMP_SETUP_COMPLETE; - resplen = 1; - bt->setup = 1; - break; - - case LMP_DETACH: - /* data[0] Error code - */ - if (length < 1) { - error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE; - goto not_accepted; - } - bt->setup = 0; - resp = 0; - break; - - case LMP_SUPERVISION_TIMEOUT: - /* data[0,1] Supervision timeout - */ - if (length < 2) { - error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE; - goto not_accepted; - } - resp = 0; - break; - - case LMP_QUALITY_OF_SERVICE: - resp = 0; - /* Fall through */ - case LMP_QOS_REQ: - /* data[0,1] Poll interval - * data[2] N(BC) - */ - if (length < 3) { - error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE; - goto not_accepted; - } - break; - - case LMP_MAX_SLOT: - resp = 0; - /* Fall through */ - case LMP_MAX_SLOT_REQ: - /* data[0] Max slots - */ - if (length < 1) { - error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE; - goto not_accepted; - } - break; - - case LMP_AU_RAND: - case LMP_IN_RAND: - case LMP_COMB_KEY: - /* data[0-15] Random number - */ - if (length < 16) { - error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE; - goto not_accepted; - } - if (op == LMP_AU_RAND) { - if (bt->key_present) { - resp = LMP_SRES; - resplen = 5; - /* XXX: [Part H] Section 6.1 on page 801 */ - } else { - error = HCI_PIN_OR_KEY_MISSING; - goto not_accepted; - } - } else if (op == LMP_IN_RAND) { - error = HCI_PAIRING_NOT_ALLOWED; - goto not_accepted; - } else { - /* XXX: [Part H] Section 3.2 on page 779 */ - resp = LMP_UNIT_KEY; - resplen = 17; - memcpy(respdata + 1, bt->key, 16); - - error = HCI_UNIT_LINK_KEY_USED; - goto not_accepted; - } - break; - - case LMP_UNIT_KEY: - /* data[0-15] Key - */ - if (length < 16) { - error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE; - goto not_accepted; - } - memcpy(bt->key, data, 16); - bt->key_present = 1; - break; - - case LMP_SRES: - /* data[0-3] Authentication response - */ - if (length < 4) { - error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE; - goto not_accepted; - } - break; - - case LMP_CLKOFFSET_REQ: - resp = LMP_CLKOFFSET_RES; - resplen = 3; - respdata[1] = 0x33; - respdata[2] = 0x33; - break; - - case LMP_CLKOFFSET_RES: - /* data[0,1] Clock offset - * (Slave to master only) - */ - if (length < 2) { - error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE; - goto not_accepted; - } - break; - - case LMP_VERSION_REQ: - case LMP_VERSION_RES: - /* data[0] VersNr - * data[1,2] CompId - * data[3,4] SubVersNr - */ - if (length < 5) { - error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE; - goto not_accepted; - } - if (op == LMP_VERSION_REQ) { - resp = LMP_VERSION_RES; - resplen = 6; - respdata[1] = 0x20; - respdata[2] = 0xff; - respdata[3] = 0xff; - respdata[4] = 0xff; - respdata[5] = 0xff; - } else - resp = 0; - break; - - case LMP_FEATURES_REQ: - case LMP_FEATURES_RES: - /* data[0-7] Features - */ - if (length < 8) { - error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE; - goto not_accepted; - } - if (op == LMP_FEATURES_REQ) { - resp = LMP_FEATURES_RES; - resplen = 9; - respdata[1] = (bt->lmp_caps >> 0) & 0xff; - respdata[2] = (bt->lmp_caps >> 8) & 0xff; - respdata[3] = (bt->lmp_caps >> 16) & 0xff; - respdata[4] = (bt->lmp_caps >> 24) & 0xff; - respdata[5] = (bt->lmp_caps >> 32) & 0xff; - respdata[6] = (bt->lmp_caps >> 40) & 0xff; - respdata[7] = (bt->lmp_caps >> 48) & 0xff; - respdata[8] = (bt->lmp_caps >> 56) & 0xff; - } else - resp = 0; - break; - - case LMP_NAME_REQ: - /* data[0] Name offset - */ - if (length < 1) { - error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE; - goto not_accepted; - } - resp = LMP_NAME_RES; - resplen = 17; - respdata[1] = data[0]; - respdata[2] = strlen(bt->lmp_name); - memset(respdata + 3, 0x00, 14); - if (respdata[2] > respdata[1]) - memcpy(respdata + 3, bt->lmp_name + respdata[1], - respdata[2] - respdata[1]); - break; - - case LMP_NAME_RES: - /* data[0] Name offset - * data[1] Name length - * data[2-15] Name fragment - */ - if (length < 16) { - error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE; - goto not_accepted; - } - resp = 0; - break; - - default: - error = HCI_UNKNOWN_LMP_PDU; - /* Fall through */ - not_accepted: - if (op >> 8) { - resp = LMP_NOT_ACCEPTED_EXT; - resplen = 5; - respdata[0] = op >> 8; - respdata[1] = op & 0xff; - respdata[2] = error; - } else { - resp = LMP_NOT_ACCEPTED; - resplen = 3; - respdata[0] = op & 0xff; - respdata[1] = error; - } - } - - if (resp == 0) - return; - - if (resp >> 8) { - respdata[0] = resp >> 8; - respdata[1] = resp & 0xff; - } else - respdata[0] = resp & 0xff; - - respdata[0] <<= 1; - respdata[0] |= tr; -} - -static void bt_submit_raw_acl(struct bt_piconet_s *net, int length, uint8_t *data) -{ - struct bt_device_s *slave; - if (length < 1) - return; - - slave = 0; -#if 0 - slave = net->slave; -#endif - - switch (data[0] & 3) { - case LLID_ACLC: - bt_submit_lmp(slave, length - 1, data + 1); - break; - case LLID_ACLU_START: -#if 0 - bt_sumbit_l2cap(slave, length - 1, data + 1, (data[0] >> 2) & 1); - breka; -#endif - default: - case LLID_ACLU_CONT: - break; - } -} -#endif - -/* HCI layer emulation */ - -/* Note: we could ignore endianness because unswapped handles will still - * be valid as connection identifiers for the guest - they don't have to - * be continuously allocated. We do it though, to preserve similar - * behaviour between hosts. Some things, like the BD_ADDR cannot be - * preserved though (for example if a real hci is used). */ -#define HNDL(raw) cpu_to_le16(raw) - -static const uint8_t bt_event_reserved_mask[8] = { - 0xff, 0x9f, 0xfb, 0xff, 0x07, 0x18, 0x00, 0x00, -}; - - -static void null_hci_send(struct HCIInfo *hci, const uint8_t *data, int len) -{ -} - -static int null_hci_addr_set(struct HCIInfo *hci, const uint8_t *bd_addr) -{ - return -ENOTSUP; -} - -struct HCIInfo null_hci = { - .cmd_send = null_hci_send, - .sco_send = null_hci_send, - .acl_send = null_hci_send, - .bdaddr_set = null_hci_addr_set, -}; - - -static inline uint8_t *bt_hci_event_start(struct bt_hci_s *hci, - int evt, int len) -{ - uint8_t *packet, mask; - int mask_byte; - - if (len > 255) { - error_report("%s: HCI event params too long (%ib)", __func__, len); - exit(-1); - } - - mask_byte = (evt - 1) >> 3; - mask = 1 << ((evt - 1) & 3); - if (mask & bt_event_reserved_mask[mask_byte] & ~hci->event_mask[mask_byte]) - return NULL; - - packet = hci->evt_packet(hci->opaque); - packet[0] = evt; - packet[1] = len; - - return &packet[2]; -} - -static inline void bt_hci_event(struct bt_hci_s *hci, int evt, - void *params, int len) -{ - uint8_t *packet = bt_hci_event_start(hci, evt, len); - - if (!packet) - return; - - if (len) - memcpy(packet, params, len); - - hci->evt_submit(hci->opaque, len + 2); -} - -static inline void bt_hci_event_status(struct bt_hci_s *hci, int status) -{ - evt_cmd_status params = { - .status = status, - .ncmd = 1, - .opcode = hci->last_cmd, - }; - - bt_hci_event(hci, EVT_CMD_STATUS, ¶ms, EVT_CMD_STATUS_SIZE); -} - -static inline void bt_hci_event_complete(struct bt_hci_s *hci, - void *ret, int len) -{ - uint8_t *packet = bt_hci_event_start(hci, EVT_CMD_COMPLETE, - len + EVT_CMD_COMPLETE_SIZE); - evt_cmd_complete *params = (evt_cmd_complete *) packet; - - if (!packet) - return; - - params->ncmd = 1; - params->opcode = hci->last_cmd; - if (len) - memcpy(&packet[EVT_CMD_COMPLETE_SIZE], ret, len); - - hci->evt_submit(hci->opaque, len + EVT_CMD_COMPLETE_SIZE + 2); -} - -static void bt_hci_inquiry_done(void *opaque) -{ - struct bt_hci_s *hci = (struct bt_hci_s *) opaque; - uint8_t status = HCI_SUCCESS; - - if (!hci->lm.periodic) - hci->lm.inquire = 0; - - /* The specification is inconsistent about this one. Page 565 reads - * "The event parameters of Inquiry Complete event will have a summary - * of the result from the Inquiry process, which reports the number of - * nearby Bluetooth devices that responded [so hci->responses].", but - * Event Parameters (see page 729) has only Status. */ - bt_hci_event(hci, EVT_INQUIRY_COMPLETE, &status, 1); -} - -static void bt_hci_inquiry_result_standard(struct bt_hci_s *hci, - struct bt_device_s *slave) -{ - inquiry_info params = { - .num_responses = 1, - .bdaddr = BAINIT(&slave->bd_addr), - .pscan_rep_mode = 0x00, /* R0 */ - .pscan_period_mode = 0x00, /* P0 - deprecated */ - .pscan_mode = 0x00, /* Standard scan - deprecated */ - .dev_class[0] = slave->class[0], - .dev_class[1] = slave->class[1], - .dev_class[2] = slave->class[2], - /* TODO: return the clkoff *differenece* */ - .clock_offset = slave->clkoff, /* Note: no swapping */ - }; - - bt_hci_event(hci, EVT_INQUIRY_RESULT, ¶ms, INQUIRY_INFO_SIZE); -} - -static void bt_hci_inquiry_result_with_rssi(struct bt_hci_s *hci, - struct bt_device_s *slave) -{ - inquiry_info_with_rssi params = { - .num_responses = 1, - .bdaddr = BAINIT(&slave->bd_addr), - .pscan_rep_mode = 0x00, /* R0 */ - .pscan_period_mode = 0x00, /* P0 - deprecated */ - .dev_class[0] = slave->class[0], - .dev_class[1] = slave->class[1], - .dev_class[2] = slave->class[2], - /* TODO: return the clkoff *differenece* */ - .clock_offset = slave->clkoff, /* Note: no swapping */ - .rssi = DEFAULT_RSSI_DBM, - }; - - bt_hci_event(hci, EVT_INQUIRY_RESULT_WITH_RSSI, - ¶ms, INQUIRY_INFO_WITH_RSSI_SIZE); -} - -static void bt_hci_inquiry_result(struct bt_hci_s *hci, - struct bt_device_s *slave) -{ - if (!slave->inquiry_scan || !hci->lm.responses_left) - return; - - hci->lm.responses_left --; - hci->lm.responses ++; - - switch (hci->lm.inquiry_mode) { - case 0x00: - bt_hci_inquiry_result_standard(hci, slave); - return; - case 0x01: - bt_hci_inquiry_result_with_rssi(hci, slave); - return; - default: - error_report("%s: bad inquiry mode %02x", __func__, - hci->lm.inquiry_mode); - exit(-1); - } -} - -static void bt_hci_mod_timer_1280ms(QEMUTimer *timer, int period) -{ - timer_mod(timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - (uint64_t)(period << 7) * 10000000); -} - -static void bt_hci_inquiry_start(struct bt_hci_s *hci, int length) -{ - struct bt_device_s *slave; - - hci->lm.inquiry_length = length; - for (slave = hci->device.net->slave; slave; slave = slave->next) - /* Don't uncover ourselves. */ - if (slave != &hci->device) - bt_hci_inquiry_result(hci, slave); - - /* TODO: register for a callback on a new device's addition to the - * scatternet so that if it's added before inquiry_length expires, - * an Inquiry Result is generated immediately. Alternatively re-loop - * through the devices on the inquiry_length expiration and report - * devices not seen before. */ - if (hci->lm.responses_left) - bt_hci_mod_timer_1280ms(hci->lm.inquiry_done, hci->lm.inquiry_length); - else - bt_hci_inquiry_done(hci); - - if (hci->lm.periodic) - bt_hci_mod_timer_1280ms(hci->lm.inquiry_next, hci->lm.inquiry_period); -} - -static void bt_hci_inquiry_next(void *opaque) -{ - struct bt_hci_s *hci = (struct bt_hci_s *) opaque; - - hci->lm.responses_left += hci->lm.responses; - hci->lm.responses = 0; - bt_hci_inquiry_start(hci, hci->lm.inquiry_length); -} - -static inline int bt_hci_handle_bad(struct bt_hci_s *hci, uint16_t handle) -{ - return !(handle & HCI_HANDLE_OFFSET) || - handle >= (HCI_HANDLE_OFFSET | HCI_HANDLES_MAX) || - !hci->lm.handle[handle & ~HCI_HANDLE_OFFSET].link; -} - -static inline int bt_hci_role_master(struct bt_hci_s *hci, uint16_t handle) -{ - return !!(hci->lm.role_bmp & (1 << (handle & ~HCI_HANDLE_OFFSET))); -} - -static inline struct bt_device_s *bt_hci_remote_dev(struct bt_hci_s *hci, - uint16_t handle) -{ - struct bt_link_s *link = hci->lm.handle[handle & ~HCI_HANDLE_OFFSET].link; - - return bt_hci_role_master(hci, handle) ? link->slave : link->host; -} - -static void bt_hci_mode_tick(void *opaque); -static void bt_hci_lmp_link_establish(struct bt_hci_s *hci, - struct bt_link_s *link, int master) -{ - hci->lm.handle[hci->lm.last_handle].link = link; - - if (master) { - /* We are the master side of an ACL link */ - hci->lm.role_bmp |= 1 << hci->lm.last_handle; - - hci->lm.handle[hci->lm.last_handle].lmp_acl_data = - link->slave->lmp_acl_data; - } else { - /* We are the slave side of an ACL link */ - hci->lm.role_bmp &= ~(1 << hci->lm.last_handle); - - hci->lm.handle[hci->lm.last_handle].lmp_acl_data = - link->host->lmp_acl_resp; - } - - /* Mode */ - if (master) { - link->acl_mode = acl_active; - hci->lm.handle[hci->lm.last_handle].acl_mode_timer = - timer_new_ns(QEMU_CLOCK_VIRTUAL, bt_hci_mode_tick, link); - } -} - -static void bt_hci_lmp_link_teardown(struct bt_hci_s *hci, uint16_t handle) -{ - handle &= ~HCI_HANDLE_OFFSET; - hci->lm.handle[handle].link = NULL; - - if (bt_hci_role_master(hci, handle)) { - timer_del(hci->lm.handle[handle].acl_mode_timer); - timer_free(hci->lm.handle[handle].acl_mode_timer); - } -} - -static int bt_hci_connect(struct bt_hci_s *hci, bdaddr_t *bdaddr) -{ - struct bt_device_s *slave; - struct bt_link_s link; - - for (slave = hci->device.net->slave; slave; slave = slave->next) - if (slave->page_scan && !bacmp(&slave->bd_addr, bdaddr)) - break; - if (!slave || slave == &hci->device) - return -ENODEV; - - bacpy(&hci->lm.awaiting_bdaddr[hci->lm.connecting ++], &slave->bd_addr); - - link.slave = slave; - link.host = &hci->device; - link.slave->lmp_connection_request(&link); /* Always last */ - - return 0; -} - -static void bt_hci_connection_reject(struct bt_hci_s *hci, - struct bt_device_s *host, uint8_t because) -{ - struct bt_link_s link = { - .slave = &hci->device, - .host = host, - /* Rest uninitialised */ - }; - - host->reject_reason = because; - host->lmp_connection_complete(&link); -} - -static void bt_hci_connection_reject_event(struct bt_hci_s *hci, - bdaddr_t *bdaddr) -{ - evt_conn_complete params; - - params.status = HCI_NO_CONNECTION; - params.handle = 0; - bacpy(¶ms.bdaddr, bdaddr); - params.link_type = ACL_LINK; - params.encr_mode = 0x00; /* Encryption not required */ - bt_hci_event(hci, EVT_CONN_COMPLETE, ¶ms, EVT_CONN_COMPLETE_SIZE); -} - -static void bt_hci_connection_accept(struct bt_hci_s *hci, - struct bt_device_s *host) -{ - struct bt_hci_link_s *link = g_malloc0(sizeof(struct bt_hci_link_s)); - evt_conn_complete params; - uint16_t handle; - uint8_t status = HCI_SUCCESS; - int tries = HCI_HANDLES_MAX; - - /* Make a connection handle */ - do { - while (hci->lm.handle[++ hci->lm.last_handle].link && -- tries) - hci->lm.last_handle &= HCI_HANDLES_MAX - 1; - handle = hci->lm.last_handle | HCI_HANDLE_OFFSET; - } while ((handle == hci->asb_handle || handle == hci->psb_handle) && - tries); - - if (!tries) { - g_free(link); - bt_hci_connection_reject(hci, host, HCI_REJECTED_LIMITED_RESOURCES); - status = HCI_NO_CONNECTION; - goto complete; - } - - link->btlink.slave = &hci->device; - link->btlink.host = host; - link->handle = handle; - - /* Link established */ - bt_hci_lmp_link_establish(hci, &link->btlink, 0); - -complete: - params.status = status; - params.handle = HNDL(handle); - bacpy(¶ms.bdaddr, &host->bd_addr); - params.link_type = ACL_LINK; - params.encr_mode = 0x00; /* Encryption not required */ - bt_hci_event(hci, EVT_CONN_COMPLETE, ¶ms, EVT_CONN_COMPLETE_SIZE); - - /* Neets to be done at the very end because it can trigger a (nested) - * disconnected, in case the other and had cancelled the request - * locally. */ - if (status == HCI_SUCCESS) { - host->reject_reason = 0; - host->lmp_connection_complete(&link->btlink); - } -} - -static void bt_hci_lmp_connection_request(struct bt_link_s *link) -{ - struct bt_hci_s *hci = hci_from_device(link->slave); - evt_conn_request params; - - if (hci->conn_req_host) { - bt_hci_connection_reject(hci, link->host, - HCI_REJECTED_LIMITED_RESOURCES); - return; - } - hci->conn_req_host = link->host; - /* TODO: if masked and auto-accept, then auto-accept, - * if masked and not auto-accept, then auto-reject */ - /* TODO: kick the hci->conn_accept_timer, timeout after - * hci->conn_accept_tout * 0.625 msec */ - - bacpy(¶ms.bdaddr, &link->host->bd_addr); - memcpy(¶ms.dev_class, &link->host->class, sizeof(params.dev_class)); - params.link_type = ACL_LINK; - bt_hci_event(hci, EVT_CONN_REQUEST, ¶ms, EVT_CONN_REQUEST_SIZE); -} - -static void bt_hci_conn_accept_timeout(void *opaque) -{ - struct bt_hci_s *hci = (struct bt_hci_s *) opaque; - - if (!hci->conn_req_host) - /* Already accepted or rejected. If the other end cancelled the - * connection request then we still have to reject or accept it - * and then we'll get a disconnect. */ - return; - - /* TODO */ -} - -/* Remove from the list of devices which we wanted to connect to and - * are awaiting a response from. If the callback sees a response from - * a device which is not on the list it will assume it's a connection - * that's been cancelled by the host in the meantime and immediately - * try to detach the link and send a Connection Complete. */ -static int bt_hci_lmp_connection_ready(struct bt_hci_s *hci, - bdaddr_t *bdaddr) -{ - int i; - - for (i = 0; i < hci->lm.connecting; i ++) - if (!bacmp(&hci->lm.awaiting_bdaddr[i], bdaddr)) { - if (i < -- hci->lm.connecting) - bacpy(&hci->lm.awaiting_bdaddr[i], - &hci->lm.awaiting_bdaddr[hci->lm.connecting]); - return 0; - } - - return 1; -} - -static void bt_hci_lmp_connection_complete(struct bt_link_s *link) -{ - struct bt_hci_s *hci = hci_from_device(link->host); - evt_conn_complete params; - uint16_t handle; - uint8_t status = HCI_SUCCESS; - int tries = HCI_HANDLES_MAX; - - if (bt_hci_lmp_connection_ready(hci, &link->slave->bd_addr)) { - if (!hci->device.reject_reason) - link->slave->lmp_disconnect_slave(link); - handle = 0; - status = HCI_NO_CONNECTION; - goto complete; - } - - if (hci->device.reject_reason) { - handle = 0; - status = hci->device.reject_reason; - goto complete; - } - - /* Make a connection handle */ - do { - while (hci->lm.handle[++ hci->lm.last_handle].link && -- tries) - hci->lm.last_handle &= HCI_HANDLES_MAX - 1; - handle = hci->lm.last_handle | HCI_HANDLE_OFFSET; - } while ((handle == hci->asb_handle || handle == hci->psb_handle) && - tries); - - if (!tries) { - link->slave->lmp_disconnect_slave(link); - status = HCI_NO_CONNECTION; - goto complete; - } - - /* Link established */ - link->handle = handle; - bt_hci_lmp_link_establish(hci, link, 1); - -complete: - params.status = status; - params.handle = HNDL(handle); - params.link_type = ACL_LINK; - bacpy(¶ms.bdaddr, &link->slave->bd_addr); - params.encr_mode = 0x00; /* Encryption not required */ - bt_hci_event(hci, EVT_CONN_COMPLETE, ¶ms, EVT_CONN_COMPLETE_SIZE); -} - -static void bt_hci_disconnect(struct bt_hci_s *hci, - uint16_t handle, int reason) -{ - struct bt_link_s *btlink = - hci->lm.handle[handle & ~HCI_HANDLE_OFFSET].link; - struct bt_hci_link_s *link; - evt_disconn_complete params; - - if (bt_hci_role_master(hci, handle)) { - btlink->slave->reject_reason = reason; - btlink->slave->lmp_disconnect_slave(btlink); - /* The link pointer is invalid from now on */ - - goto complete; - } - - btlink->host->reject_reason = reason; - btlink->host->lmp_disconnect_master(btlink); - - /* We are the slave, we get to clean this burden */ - link = (struct bt_hci_link_s *) btlink; - g_free(link); - -complete: - bt_hci_lmp_link_teardown(hci, handle); - - params.status = HCI_SUCCESS; - params.handle = HNDL(handle); - params.reason = HCI_CONNECTION_TERMINATED; - bt_hci_event(hci, EVT_DISCONN_COMPLETE, - ¶ms, EVT_DISCONN_COMPLETE_SIZE); -} - -/* TODO: use only one function */ -static void bt_hci_lmp_disconnect_host(struct bt_link_s *link) -{ - struct bt_hci_s *hci = hci_from_device(link->host); - uint16_t handle = link->handle; - evt_disconn_complete params; - - bt_hci_lmp_link_teardown(hci, handle); - - params.status = HCI_SUCCESS; - params.handle = HNDL(handle); - params.reason = hci->device.reject_reason; - bt_hci_event(hci, EVT_DISCONN_COMPLETE, - ¶ms, EVT_DISCONN_COMPLETE_SIZE); -} - -static void bt_hci_lmp_disconnect_slave(struct bt_link_s *btlink) -{ - struct bt_hci_link_s *link = (struct bt_hci_link_s *) btlink; - struct bt_hci_s *hci = hci_from_device(btlink->slave); - uint16_t handle = link->handle; - evt_disconn_complete params; - - g_free(link); - - bt_hci_lmp_link_teardown(hci, handle); - - params.status = HCI_SUCCESS; - params.handle = HNDL(handle); - params.reason = hci->device.reject_reason; - bt_hci_event(hci, EVT_DISCONN_COMPLETE, - ¶ms, EVT_DISCONN_COMPLETE_SIZE); -} - -static int bt_hci_name_req(struct bt_hci_s *hci, bdaddr_t *bdaddr) -{ - struct bt_device_s *slave; - evt_remote_name_req_complete params; - - for (slave = hci->device.net->slave; slave; slave = slave->next) - if (slave->page_scan && !bacmp(&slave->bd_addr, bdaddr)) - break; - if (!slave) - return -ENODEV; - - bt_hci_event_status(hci, HCI_SUCCESS); - - params.status = HCI_SUCCESS; - bacpy(¶ms.bdaddr, &slave->bd_addr); - pstrcpy(params.name, sizeof(params.name), slave->lmp_name ?: ""); - bt_hci_event(hci, EVT_REMOTE_NAME_REQ_COMPLETE, - ¶ms, EVT_REMOTE_NAME_REQ_COMPLETE_SIZE); - - return 0; -} - -static int bt_hci_features_req(struct bt_hci_s *hci, uint16_t handle) -{ - struct bt_device_s *slave; - evt_read_remote_features_complete params; - - if (bt_hci_handle_bad(hci, handle)) - return -ENODEV; - - slave = bt_hci_remote_dev(hci, handle); - - bt_hci_event_status(hci, HCI_SUCCESS); - - params.status = HCI_SUCCESS; - params.handle = HNDL(handle); - params.features[0] = (slave->lmp_caps >> 0) & 0xff; - params.features[1] = (slave->lmp_caps >> 8) & 0xff; - params.features[2] = (slave->lmp_caps >> 16) & 0xff; - params.features[3] = (slave->lmp_caps >> 24) & 0xff; - params.features[4] = (slave->lmp_caps >> 32) & 0xff; - params.features[5] = (slave->lmp_caps >> 40) & 0xff; - params.features[6] = (slave->lmp_caps >> 48) & 0xff; - params.features[7] = (slave->lmp_caps >> 56) & 0xff; - bt_hci_event(hci, EVT_READ_REMOTE_FEATURES_COMPLETE, - ¶ms, EVT_READ_REMOTE_FEATURES_COMPLETE_SIZE); - - return 0; -} - -static int bt_hci_version_req(struct bt_hci_s *hci, uint16_t handle) -{ - evt_read_remote_version_complete params; - - if (bt_hci_handle_bad(hci, handle)) - return -ENODEV; - - bt_hci_remote_dev(hci, handle); - - bt_hci_event_status(hci, HCI_SUCCESS); - - params.status = HCI_SUCCESS; - params.handle = HNDL(handle); - params.lmp_ver = 0x03; - params.manufacturer = cpu_to_le16(0xa000); - params.lmp_subver = cpu_to_le16(0xa607); - bt_hci_event(hci, EVT_READ_REMOTE_VERSION_COMPLETE, - ¶ms, EVT_READ_REMOTE_VERSION_COMPLETE_SIZE); - - return 0; -} - -static int bt_hci_clkoffset_req(struct bt_hci_s *hci, uint16_t handle) -{ - struct bt_device_s *slave; - evt_read_clock_offset_complete params; - - if (bt_hci_handle_bad(hci, handle)) - return -ENODEV; - - slave = bt_hci_remote_dev(hci, handle); - - bt_hci_event_status(hci, HCI_SUCCESS); - - params.status = HCI_SUCCESS; - params.handle = HNDL(handle); - /* TODO: return the clkoff *differenece* */ - params.clock_offset = slave->clkoff; /* Note: no swapping */ - bt_hci_event(hci, EVT_READ_CLOCK_OFFSET_COMPLETE, - ¶ms, EVT_READ_CLOCK_OFFSET_COMPLETE_SIZE); - - return 0; -} - -static void bt_hci_event_mode(struct bt_hci_s *hci, struct bt_link_s *link, - uint16_t handle) -{ - evt_mode_change params = { - .status = HCI_SUCCESS, - .handle = HNDL(handle), - .mode = link->acl_mode, - .interval = cpu_to_le16(link->acl_interval), - }; - - bt_hci_event(hci, EVT_MODE_CHANGE, ¶ms, EVT_MODE_CHANGE_SIZE); -} - -static void bt_hci_lmp_mode_change_master(struct bt_hci_s *hci, - struct bt_link_s *link, int mode, uint16_t interval) -{ - link->acl_mode = mode; - link->acl_interval = interval; - - bt_hci_event_mode(hci, link, link->handle); - - link->slave->lmp_mode_change(link); -} - -static void bt_hci_lmp_mode_change_slave(struct bt_link_s *btlink) -{ - struct bt_hci_link_s *link = (struct bt_hci_link_s *) btlink; - struct bt_hci_s *hci = hci_from_device(btlink->slave); - - bt_hci_event_mode(hci, btlink, link->handle); -} - -static int bt_hci_mode_change(struct bt_hci_s *hci, uint16_t handle, - int interval, int mode) -{ - struct bt_hci_master_link_s *link; - - if (bt_hci_handle_bad(hci, handle) || !bt_hci_role_master(hci, handle)) - return -ENODEV; - - link = &hci->lm.handle[handle & ~HCI_HANDLE_OFFSET]; - if (link->link->acl_mode != acl_active) { - bt_hci_event_status(hci, HCI_COMMAND_DISALLOWED); - return 0; - } - - bt_hci_event_status(hci, HCI_SUCCESS); - - timer_mod(link->acl_mode_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - ((uint64_t)interval * 625) * 1000); - bt_hci_lmp_mode_change_master(hci, link->link, mode, interval); - - return 0; -} - -static int bt_hci_mode_cancel(struct bt_hci_s *hci, uint16_t handle, int mode) -{ - struct bt_hci_master_link_s *link; - - if (bt_hci_handle_bad(hci, handle) || !bt_hci_role_master(hci, handle)) - return -ENODEV; - - link = &hci->lm.handle[handle & ~HCI_HANDLE_OFFSET]; - if (link->link->acl_mode != mode) { - bt_hci_event_status(hci, HCI_COMMAND_DISALLOWED); - - return 0; - } - - bt_hci_event_status(hci, HCI_SUCCESS); - - timer_del(link->acl_mode_timer); - bt_hci_lmp_mode_change_master(hci, link->link, acl_active, 0); - - return 0; -} - -static void bt_hci_mode_tick(void *opaque) -{ - struct bt_link_s *link = opaque; - struct bt_hci_s *hci = hci_from_device(link->host); - - bt_hci_lmp_mode_change_master(hci, link, acl_active, 0); -} - -static void bt_hci_reset(struct bt_hci_s *hci) -{ - hci->acl_len = 0; - hci->last_cmd = 0; - hci->lm.connecting = 0; - - hci->event_mask[0] = 0xff; - hci->event_mask[1] = 0xff; - hci->event_mask[2] = 0xff; - hci->event_mask[3] = 0xff; - hci->event_mask[4] = 0xff; - hci->event_mask[5] = 0x1f; - hci->event_mask[6] = 0x00; - hci->event_mask[7] = 0x00; - hci->device.inquiry_scan = 0; - hci->device.page_scan = 0; - g_free((void *) hci->device.lmp_name); - hci->device.lmp_name = NULL; - hci->device.class[0] = 0x00; - hci->device.class[1] = 0x00; - hci->device.class[2] = 0x00; - hci->voice_setting = 0x0000; - hci->conn_accept_tout = 0x1f40; - hci->lm.inquiry_mode = 0x00; - - hci->psb_handle = 0x000; - hci->asb_handle = 0x000; - - /* XXX: timer_del(sl->acl_mode_timer); for all links */ - timer_del(hci->lm.inquiry_done); - timer_del(hci->lm.inquiry_next); - timer_del(hci->conn_accept_timer); -} - -static void bt_hci_read_local_version_rp(struct bt_hci_s *hci) -{ - read_local_version_rp lv = { - .status = HCI_SUCCESS, - .hci_ver = 0x03, - .hci_rev = cpu_to_le16(0xa607), - .lmp_ver = 0x03, - .manufacturer = cpu_to_le16(0xa000), - .lmp_subver = cpu_to_le16(0xa607), - }; - - bt_hci_event_complete(hci, &lv, READ_LOCAL_VERSION_RP_SIZE); -} - -static void bt_hci_read_local_commands_rp(struct bt_hci_s *hci) -{ - read_local_commands_rp lc = { - .status = HCI_SUCCESS, - .commands = { - /* Keep updated! */ - /* Also, keep in sync with hci->device.lmp_caps in bt_new_hci */ - 0xbf, 0x80, 0xf9, 0x03, 0xb2, 0xc0, 0x03, 0xc3, - 0x00, 0x0f, 0x80, 0x00, 0xc0, 0x00, 0xe8, 0x13, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - }, - }; - - bt_hci_event_complete(hci, &lc, READ_LOCAL_COMMANDS_RP_SIZE); -} - -static void bt_hci_read_local_features_rp(struct bt_hci_s *hci) -{ - read_local_features_rp lf = { - .status = HCI_SUCCESS, - .features = { - (hci->device.lmp_caps >> 0) & 0xff, - (hci->device.lmp_caps >> 8) & 0xff, - (hci->device.lmp_caps >> 16) & 0xff, - (hci->device.lmp_caps >> 24) & 0xff, - (hci->device.lmp_caps >> 32) & 0xff, - (hci->device.lmp_caps >> 40) & 0xff, - (hci->device.lmp_caps >> 48) & 0xff, - (hci->device.lmp_caps >> 56) & 0xff, - }, - }; - - bt_hci_event_complete(hci, &lf, READ_LOCAL_FEATURES_RP_SIZE); -} - -static void bt_hci_read_local_ext_features_rp(struct bt_hci_s *hci, int page) -{ - read_local_ext_features_rp lef = { - .status = HCI_SUCCESS, - .page_num = page, - .max_page_num = 0x00, - .features = { - /* Keep updated! */ - 0x5f, 0x35, 0x85, 0x7e, 0x9b, 0x19, 0x00, 0x80, - }, - }; - if (page) - memset(lef.features, 0, sizeof(lef.features)); - - bt_hci_event_complete(hci, &lef, READ_LOCAL_EXT_FEATURES_RP_SIZE); -} - -static void bt_hci_read_buffer_size_rp(struct bt_hci_s *hci) -{ - read_buffer_size_rp bs = { - /* This can be made configurable, for one standard USB dongle HCI - * the four values are cpu_to_le16(0x0180), 0x40, - * cpu_to_le16(0x0008), cpu_to_le16(0x0008). */ - .status = HCI_SUCCESS, - .acl_mtu = cpu_to_le16(0x0200), - .sco_mtu = 0, - .acl_max_pkt = cpu_to_le16(0x0001), - .sco_max_pkt = cpu_to_le16(0x0000), - }; - - bt_hci_event_complete(hci, &bs, READ_BUFFER_SIZE_RP_SIZE); -} - -/* Deprecated in V2.0 (page 661) */ -static void bt_hci_read_country_code_rp(struct bt_hci_s *hci) -{ - read_country_code_rp cc ={ - .status = HCI_SUCCESS, - .country_code = 0x00, /* North America & Europe^1 and Japan */ - }; - - bt_hci_event_complete(hci, &cc, READ_COUNTRY_CODE_RP_SIZE); - - /* ^1. Except France, sorry */ -} - -static void bt_hci_read_bd_addr_rp(struct bt_hci_s *hci) -{ - read_bd_addr_rp ba = { - .status = HCI_SUCCESS, - .bdaddr = BAINIT(&hci->device.bd_addr), - }; - - bt_hci_event_complete(hci, &ba, READ_BD_ADDR_RP_SIZE); -} - -static int bt_hci_link_quality_rp(struct bt_hci_s *hci, uint16_t handle) -{ - read_link_quality_rp lq = { - .status = HCI_SUCCESS, - .handle = HNDL(handle), - .link_quality = 0xff, - }; - - if (bt_hci_handle_bad(hci, handle)) - lq.status = HCI_NO_CONNECTION; - - bt_hci_event_complete(hci, &lq, READ_LINK_QUALITY_RP_SIZE); - return 0; -} - -/* Generate a Command Complete event with only the Status parameter */ -static inline void bt_hci_event_complete_status(struct bt_hci_s *hci, - uint8_t status) -{ - bt_hci_event_complete(hci, &status, 1); -} - -static inline void bt_hci_event_complete_conn_cancel(struct bt_hci_s *hci, - uint8_t status, bdaddr_t *bd_addr) -{ - create_conn_cancel_rp params = { - .status = status, - .bdaddr = BAINIT(bd_addr), - }; - - bt_hci_event_complete(hci, ¶ms, CREATE_CONN_CANCEL_RP_SIZE); -} - -static inline void bt_hci_event_auth_complete(struct bt_hci_s *hci, - uint16_t handle) -{ - evt_auth_complete params = { - .status = HCI_SUCCESS, - .handle = HNDL(handle), - }; - - bt_hci_event(hci, EVT_AUTH_COMPLETE, ¶ms, EVT_AUTH_COMPLETE_SIZE); -} - -static inline void bt_hci_event_encrypt_change(struct bt_hci_s *hci, - uint16_t handle, uint8_t mode) -{ - evt_encrypt_change params = { - .status = HCI_SUCCESS, - .handle = HNDL(handle), - .encrypt = mode, - }; - - bt_hci_event(hci, EVT_ENCRYPT_CHANGE, ¶ms, EVT_ENCRYPT_CHANGE_SIZE); -} - -static inline void bt_hci_event_complete_name_cancel(struct bt_hci_s *hci, - bdaddr_t *bd_addr) -{ - remote_name_req_cancel_rp params = { - .status = HCI_INVALID_PARAMETERS, - .bdaddr = BAINIT(bd_addr), - }; - - bt_hci_event_complete(hci, ¶ms, REMOTE_NAME_REQ_CANCEL_RP_SIZE); -} - -static inline void bt_hci_event_read_remote_ext_features(struct bt_hci_s *hci, - uint16_t handle) -{ - evt_read_remote_ext_features_complete params = { - .status = HCI_UNSUPPORTED_FEATURE, - .handle = HNDL(handle), - /* Rest uninitialised */ - }; - - bt_hci_event(hci, EVT_READ_REMOTE_EXT_FEATURES_COMPLETE, - ¶ms, EVT_READ_REMOTE_EXT_FEATURES_COMPLETE_SIZE); -} - -static inline void bt_hci_event_complete_lmp_handle(struct bt_hci_s *hci, - uint16_t handle) -{ - read_lmp_handle_rp params = { - .status = HCI_NO_CONNECTION, - .handle = HNDL(handle), - .reserved = 0, - /* Rest uninitialised */ - }; - - bt_hci_event_complete(hci, ¶ms, READ_LMP_HANDLE_RP_SIZE); -} - -static inline void bt_hci_event_complete_role_discovery(struct bt_hci_s *hci, - int status, uint16_t handle, int master) -{ - role_discovery_rp params = { - .status = status, - .handle = HNDL(handle), - .role = master ? 0x00 : 0x01, - }; - - bt_hci_event_complete(hci, ¶ms, ROLE_DISCOVERY_RP_SIZE); -} - -static inline void bt_hci_event_complete_flush(struct bt_hci_s *hci, - int status, uint16_t handle) -{ - flush_rp params = { - .status = status, - .handle = HNDL(handle), - }; - - bt_hci_event_complete(hci, ¶ms, FLUSH_RP_SIZE); -} - -static inline void bt_hci_event_complete_read_local_name(struct bt_hci_s *hci) -{ - read_local_name_rp params; - params.status = HCI_SUCCESS; - memset(params.name, 0, sizeof(params.name)); - if (hci->device.lmp_name) - pstrcpy(params.name, sizeof(params.name), hci->device.lmp_name); - - bt_hci_event_complete(hci, ¶ms, READ_LOCAL_NAME_RP_SIZE); -} - -static inline void bt_hci_event_complete_read_conn_accept_timeout( - struct bt_hci_s *hci) -{ - read_conn_accept_timeout_rp params = { - .status = HCI_SUCCESS, - .timeout = cpu_to_le16(hci->conn_accept_tout), - }; - - bt_hci_event_complete(hci, ¶ms, READ_CONN_ACCEPT_TIMEOUT_RP_SIZE); -} - -static inline void bt_hci_event_complete_read_scan_enable(struct bt_hci_s *hci) -{ - read_scan_enable_rp params = { - .status = HCI_SUCCESS, - .enable = - (hci->device.inquiry_scan ? SCAN_INQUIRY : 0) | - (hci->device.page_scan ? SCAN_PAGE : 0), - }; - - bt_hci_event_complete(hci, ¶ms, READ_SCAN_ENABLE_RP_SIZE); -} - -static inline void bt_hci_event_complete_read_local_class(struct bt_hci_s *hci) -{ - read_class_of_dev_rp params; - - params.status = HCI_SUCCESS; - memcpy(params.dev_class, hci->device.class, sizeof(params.dev_class)); - - bt_hci_event_complete(hci, ¶ms, READ_CLASS_OF_DEV_RP_SIZE); -} - -static inline void bt_hci_event_complete_voice_setting(struct bt_hci_s *hci) -{ - read_voice_setting_rp params = { - .status = HCI_SUCCESS, - .voice_setting = hci->voice_setting, /* Note: no swapping */ - }; - - bt_hci_event_complete(hci, ¶ms, READ_VOICE_SETTING_RP_SIZE); -} - -static inline void bt_hci_event_complete_read_inquiry_mode( - struct bt_hci_s *hci) -{ - read_inquiry_mode_rp params = { - .status = HCI_SUCCESS, - .mode = hci->lm.inquiry_mode, - }; - - bt_hci_event_complete(hci, ¶ms, READ_INQUIRY_MODE_RP_SIZE); -} - -static inline void bt_hci_event_num_comp_pkts(struct bt_hci_s *hci, - uint16_t handle, int packets) -{ - uint16_t buf[EVT_NUM_COMP_PKTS_SIZE(1) / 2 + 1]; - evt_num_comp_pkts *params = (void *) ((uint8_t *) buf + 1); - - params->num_hndl = 1; - params->connection->handle = HNDL(handle); - params->connection->num_packets = cpu_to_le16(packets); - - bt_hci_event(hci, EVT_NUM_COMP_PKTS, params, EVT_NUM_COMP_PKTS_SIZE(1)); -} - -static void bt_submit_hci(struct HCIInfo *info, - const uint8_t *data, int length) -{ - struct bt_hci_s *hci = hci_from_info(info); - uint16_t cmd; - int paramlen, i; - - if (length < HCI_COMMAND_HDR_SIZE) - goto short_hci; - - memcpy(&hci->last_cmd, data, 2); - - cmd = (data[1] << 8) | data[0]; - paramlen = data[2]; - if (cmd_opcode_ogf(cmd) == 0 || cmd_opcode_ocf(cmd) == 0) /* NOP */ - return; - - data += HCI_COMMAND_HDR_SIZE; - length -= HCI_COMMAND_HDR_SIZE; - - if (paramlen > length) - return; - -#define PARAM(cmd, param) (((cmd##_cp *) data)->param) -#define PARAM16(cmd, param) lduw_le_p(&PARAM(cmd, param)) -#define PARAMHANDLE(cmd) PARAM16(cmd, handle) -#define LENGTH_CHECK(cmd) if (length < sizeof(cmd##_cp)) goto short_hci - /* Note: the supported commands bitmask in bt_hci_read_local_commands_rp - * needs to be updated every time a command is implemented here! */ - switch (cmd) { - case cmd_opcode_pack(OGF_LINK_CTL, OCF_INQUIRY): - LENGTH_CHECK(inquiry); - - if (PARAM(inquiry, length) < 1) { - bt_hci_event_complete_status(hci, HCI_INVALID_PARAMETERS); - break; - } - - hci->lm.inquire = 1; - hci->lm.periodic = 0; - hci->lm.responses_left = PARAM(inquiry, num_rsp) ?: INT_MAX; - hci->lm.responses = 0; - bt_hci_event_status(hci, HCI_SUCCESS); - bt_hci_inquiry_start(hci, PARAM(inquiry, length)); - break; - - case cmd_opcode_pack(OGF_LINK_CTL, OCF_INQUIRY_CANCEL): - if (!hci->lm.inquire || hci->lm.periodic) { - fprintf(stderr, "%s: Inquiry Cancel should only be issued after " - "the Inquiry command has been issued, a Command " - "Status event has been received for the Inquiry " - "command, and before the Inquiry Complete event " - "occurs", __func__); - bt_hci_event_complete_status(hci, HCI_COMMAND_DISALLOWED); - break; - } - - hci->lm.inquire = 0; - timer_del(hci->lm.inquiry_done); - bt_hci_event_complete_status(hci, HCI_SUCCESS); - break; - - case cmd_opcode_pack(OGF_LINK_CTL, OCF_PERIODIC_INQUIRY): - LENGTH_CHECK(periodic_inquiry); - - if (!(PARAM(periodic_inquiry, length) < - PARAM16(periodic_inquiry, min_period) && - PARAM16(periodic_inquiry, min_period) < - PARAM16(periodic_inquiry, max_period)) || - PARAM(periodic_inquiry, length) < 1 || - PARAM16(periodic_inquiry, min_period) < 2 || - PARAM16(periodic_inquiry, max_period) < 3) { - bt_hci_event_complete_status(hci, HCI_INVALID_PARAMETERS); - break; - } - - hci->lm.inquire = 1; - hci->lm.periodic = 1; - hci->lm.responses_left = PARAM(periodic_inquiry, num_rsp); - hci->lm.responses = 0; - hci->lm.inquiry_period = PARAM16(periodic_inquiry, max_period); - bt_hci_event_complete_status(hci, HCI_SUCCESS); - bt_hci_inquiry_start(hci, PARAM(periodic_inquiry, length)); - break; - - case cmd_opcode_pack(OGF_LINK_CTL, OCF_EXIT_PERIODIC_INQUIRY): - if (!hci->lm.inquire || !hci->lm.periodic) { - fprintf(stderr, "%s: Inquiry Cancel should only be issued after " - "the Inquiry command has been issued, a Command " - "Status event has been received for the Inquiry " - "command, and before the Inquiry Complete event " - "occurs", __func__); - bt_hci_event_complete_status(hci, HCI_COMMAND_DISALLOWED); - break; - } - hci->lm.inquire = 0; - timer_del(hci->lm.inquiry_done); - timer_del(hci->lm.inquiry_next); - bt_hci_event_complete_status(hci, HCI_SUCCESS); - break; - - case cmd_opcode_pack(OGF_LINK_CTL, OCF_CREATE_CONN): - LENGTH_CHECK(create_conn); - - if (hci->lm.connecting >= HCI_HANDLES_MAX) { - bt_hci_event_status(hci, HCI_REJECTED_LIMITED_RESOURCES); - break; - } - bt_hci_event_status(hci, HCI_SUCCESS); - - if (bt_hci_connect(hci, &PARAM(create_conn, bdaddr))) - bt_hci_connection_reject_event(hci, &PARAM(create_conn, bdaddr)); - break; - - case cmd_opcode_pack(OGF_LINK_CTL, OCF_DISCONNECT): - LENGTH_CHECK(disconnect); - - if (bt_hci_handle_bad(hci, PARAMHANDLE(disconnect))) { - bt_hci_event_status(hci, HCI_NO_CONNECTION); - break; - } - - bt_hci_event_status(hci, HCI_SUCCESS); - bt_hci_disconnect(hci, PARAMHANDLE(disconnect), - PARAM(disconnect, reason)); - break; - - case cmd_opcode_pack(OGF_LINK_CTL, OCF_CREATE_CONN_CANCEL): - LENGTH_CHECK(create_conn_cancel); - - if (bt_hci_lmp_connection_ready(hci, - &PARAM(create_conn_cancel, bdaddr))) { - for (i = 0; i < HCI_HANDLES_MAX; i ++) - if (bt_hci_role_master(hci, i) && hci->lm.handle[i].link && - !bacmp(&hci->lm.handle[i].link->slave->bd_addr, - &PARAM(create_conn_cancel, bdaddr))) - break; - - bt_hci_event_complete_conn_cancel(hci, i < HCI_HANDLES_MAX ? - HCI_ACL_CONNECTION_EXISTS : HCI_NO_CONNECTION, - &PARAM(create_conn_cancel, bdaddr)); - } else - bt_hci_event_complete_conn_cancel(hci, HCI_SUCCESS, - &PARAM(create_conn_cancel, bdaddr)); - break; - - case cmd_opcode_pack(OGF_LINK_CTL, OCF_ACCEPT_CONN_REQ): - LENGTH_CHECK(accept_conn_req); - - if (!hci->conn_req_host || - bacmp(&PARAM(accept_conn_req, bdaddr), - &hci->conn_req_host->bd_addr)) { - bt_hci_event_status(hci, HCI_INVALID_PARAMETERS); - break; - } - - bt_hci_event_status(hci, HCI_SUCCESS); - bt_hci_connection_accept(hci, hci->conn_req_host); - hci->conn_req_host = NULL; - break; - - case cmd_opcode_pack(OGF_LINK_CTL, OCF_REJECT_CONN_REQ): - LENGTH_CHECK(reject_conn_req); - - if (!hci->conn_req_host || - bacmp(&PARAM(reject_conn_req, bdaddr), - &hci->conn_req_host->bd_addr)) { - bt_hci_event_status(hci, HCI_INVALID_PARAMETERS); - break; - } - - bt_hci_event_status(hci, HCI_SUCCESS); - bt_hci_connection_reject(hci, hci->conn_req_host, - PARAM(reject_conn_req, reason)); - bt_hci_connection_reject_event(hci, &hci->conn_req_host->bd_addr); - hci->conn_req_host = NULL; - break; - - case cmd_opcode_pack(OGF_LINK_CTL, OCF_AUTH_REQUESTED): - LENGTH_CHECK(auth_requested); - - if (bt_hci_handle_bad(hci, PARAMHANDLE(auth_requested))) - bt_hci_event_status(hci, HCI_NO_CONNECTION); - else { - bt_hci_event_status(hci, HCI_SUCCESS); - bt_hci_event_auth_complete(hci, PARAMHANDLE(auth_requested)); - } - break; - - case cmd_opcode_pack(OGF_LINK_CTL, OCF_SET_CONN_ENCRYPT): - LENGTH_CHECK(set_conn_encrypt); - - if (bt_hci_handle_bad(hci, PARAMHANDLE(set_conn_encrypt))) - bt_hci_event_status(hci, HCI_NO_CONNECTION); - else { - bt_hci_event_status(hci, HCI_SUCCESS); - bt_hci_event_encrypt_change(hci, - PARAMHANDLE(set_conn_encrypt), - PARAM(set_conn_encrypt, encrypt)); - } - break; - - case cmd_opcode_pack(OGF_LINK_CTL, OCF_REMOTE_NAME_REQ): - LENGTH_CHECK(remote_name_req); - - if (bt_hci_name_req(hci, &PARAM(remote_name_req, bdaddr))) - bt_hci_event_status(hci, HCI_NO_CONNECTION); - break; - - case cmd_opcode_pack(OGF_LINK_CTL, OCF_REMOTE_NAME_REQ_CANCEL): - LENGTH_CHECK(remote_name_req_cancel); - - bt_hci_event_complete_name_cancel(hci, - &PARAM(remote_name_req_cancel, bdaddr)); - break; - - case cmd_opcode_pack(OGF_LINK_CTL, OCF_READ_REMOTE_FEATURES): - LENGTH_CHECK(read_remote_features); - - if (bt_hci_features_req(hci, PARAMHANDLE(read_remote_features))) - bt_hci_event_status(hci, HCI_NO_CONNECTION); - break; - - case cmd_opcode_pack(OGF_LINK_CTL, OCF_READ_REMOTE_EXT_FEATURES): - LENGTH_CHECK(read_remote_ext_features); - - if (bt_hci_handle_bad(hci, PARAMHANDLE(read_remote_ext_features))) - bt_hci_event_status(hci, HCI_NO_CONNECTION); - else { - bt_hci_event_status(hci, HCI_SUCCESS); - bt_hci_event_read_remote_ext_features(hci, - PARAMHANDLE(read_remote_ext_features)); - } - break; - - case cmd_opcode_pack(OGF_LINK_CTL, OCF_READ_REMOTE_VERSION): - LENGTH_CHECK(read_remote_version); - - if (bt_hci_version_req(hci, PARAMHANDLE(read_remote_version))) - bt_hci_event_status(hci, HCI_NO_CONNECTION); - break; - - case cmd_opcode_pack(OGF_LINK_CTL, OCF_READ_CLOCK_OFFSET): - LENGTH_CHECK(read_clock_offset); - - if (bt_hci_clkoffset_req(hci, PARAMHANDLE(read_clock_offset))) - bt_hci_event_status(hci, HCI_NO_CONNECTION); - break; - - case cmd_opcode_pack(OGF_LINK_CTL, OCF_READ_LMP_HANDLE): - LENGTH_CHECK(read_lmp_handle); - - /* TODO: */ - bt_hci_event_complete_lmp_handle(hci, PARAMHANDLE(read_lmp_handle)); - break; - - case cmd_opcode_pack(OGF_LINK_POLICY, OCF_HOLD_MODE): - LENGTH_CHECK(hold_mode); - - if (PARAM16(hold_mode, min_interval) > - PARAM16(hold_mode, max_interval) || - PARAM16(hold_mode, min_interval) < 0x0002 || - PARAM16(hold_mode, max_interval) > 0xff00 || - (PARAM16(hold_mode, min_interval) & 1) || - (PARAM16(hold_mode, max_interval) & 1)) { - bt_hci_event_status(hci, HCI_INVALID_PARAMETERS); - break; - } - - if (bt_hci_mode_change(hci, PARAMHANDLE(hold_mode), - PARAM16(hold_mode, max_interval), - acl_hold)) - bt_hci_event_status(hci, HCI_NO_CONNECTION); - break; - - case cmd_opcode_pack(OGF_LINK_POLICY, OCF_PARK_MODE): - LENGTH_CHECK(park_mode); - - if (PARAM16(park_mode, min_interval) > - PARAM16(park_mode, max_interval) || - PARAM16(park_mode, min_interval) < 0x000e || - (PARAM16(park_mode, min_interval) & 1) || - (PARAM16(park_mode, max_interval) & 1)) { - bt_hci_event_status(hci, HCI_INVALID_PARAMETERS); - break; - } - - if (bt_hci_mode_change(hci, PARAMHANDLE(park_mode), - PARAM16(park_mode, max_interval), - acl_parked)) - bt_hci_event_status(hci, HCI_NO_CONNECTION); - break; - - case cmd_opcode_pack(OGF_LINK_POLICY, OCF_EXIT_PARK_MODE): - LENGTH_CHECK(exit_park_mode); - - if (bt_hci_mode_cancel(hci, PARAMHANDLE(exit_park_mode), - acl_parked)) - bt_hci_event_status(hci, HCI_NO_CONNECTION); - break; - - case cmd_opcode_pack(OGF_LINK_POLICY, OCF_ROLE_DISCOVERY): - LENGTH_CHECK(role_discovery); - - if (bt_hci_handle_bad(hci, PARAMHANDLE(role_discovery))) - bt_hci_event_complete_role_discovery(hci, - HCI_NO_CONNECTION, PARAMHANDLE(role_discovery), 0); - else - bt_hci_event_complete_role_discovery(hci, - HCI_SUCCESS, PARAMHANDLE(role_discovery), - bt_hci_role_master(hci, - PARAMHANDLE(role_discovery))); - break; - - case cmd_opcode_pack(OGF_HOST_CTL, OCF_SET_EVENT_MASK): - LENGTH_CHECK(set_event_mask); - - memcpy(hci->event_mask, PARAM(set_event_mask, mask), 8); - bt_hci_event_complete_status(hci, HCI_SUCCESS); - break; - - case cmd_opcode_pack(OGF_HOST_CTL, OCF_RESET): - bt_hci_reset(hci); - bt_hci_event_status(hci, HCI_SUCCESS); - break; - - case cmd_opcode_pack(OGF_HOST_CTL, OCF_SET_EVENT_FLT): - if (length >= 1 && PARAM(set_event_flt, flt_type) == FLT_CLEAR_ALL) - /* No length check */; - else - LENGTH_CHECK(set_event_flt); - - /* Filters are not implemented */ - bt_hci_event_complete_status(hci, HCI_SUCCESS); - break; - - case cmd_opcode_pack(OGF_HOST_CTL, OCF_FLUSH): - LENGTH_CHECK(flush); - - if (bt_hci_handle_bad(hci, PARAMHANDLE(flush))) - bt_hci_event_complete_flush(hci, - HCI_NO_CONNECTION, PARAMHANDLE(flush)); - else { - /* TODO: ordering? */ - bt_hci_event(hci, EVT_FLUSH_OCCURRED, - &PARAM(flush, handle), - EVT_FLUSH_OCCURRED_SIZE); - bt_hci_event_complete_flush(hci, - HCI_SUCCESS, PARAMHANDLE(flush)); - } - break; - - case cmd_opcode_pack(OGF_HOST_CTL, OCF_CHANGE_LOCAL_NAME): - LENGTH_CHECK(change_local_name); - - g_free((void *) hci->device.lmp_name); - hci->device.lmp_name = g_strndup(PARAM(change_local_name, name), - sizeof(PARAM(change_local_name, name))); - bt_hci_event_complete_status(hci, HCI_SUCCESS); - break; - - case cmd_opcode_pack(OGF_HOST_CTL, OCF_READ_LOCAL_NAME): - bt_hci_event_complete_read_local_name(hci); - break; - - case cmd_opcode_pack(OGF_HOST_CTL, OCF_READ_CONN_ACCEPT_TIMEOUT): - bt_hci_event_complete_read_conn_accept_timeout(hci); - break; - - case cmd_opcode_pack(OGF_HOST_CTL, OCF_WRITE_CONN_ACCEPT_TIMEOUT): - /* TODO */ - LENGTH_CHECK(write_conn_accept_timeout); - - if (PARAM16(write_conn_accept_timeout, timeout) < 0x0001 || - PARAM16(write_conn_accept_timeout, timeout) > 0xb540) { - bt_hci_event_complete_status(hci, HCI_INVALID_PARAMETERS); - break; - } - - hci->conn_accept_tout = PARAM16(write_conn_accept_timeout, timeout); - bt_hci_event_complete_status(hci, HCI_SUCCESS); - break; - - case cmd_opcode_pack(OGF_HOST_CTL, OCF_READ_SCAN_ENABLE): - bt_hci_event_complete_read_scan_enable(hci); - break; - - case cmd_opcode_pack(OGF_HOST_CTL, OCF_WRITE_SCAN_ENABLE): - LENGTH_CHECK(write_scan_enable); - - /* TODO: check that the remaining bits are all 0 */ - hci->device.inquiry_scan = - !!(PARAM(write_scan_enable, scan_enable) & SCAN_INQUIRY); - hci->device.page_scan = - !!(PARAM(write_scan_enable, scan_enable) & SCAN_PAGE); - bt_hci_event_complete_status(hci, HCI_SUCCESS); - break; - - case cmd_opcode_pack(OGF_HOST_CTL, OCF_READ_CLASS_OF_DEV): - bt_hci_event_complete_read_local_class(hci); - break; - - case cmd_opcode_pack(OGF_HOST_CTL, OCF_WRITE_CLASS_OF_DEV): - LENGTH_CHECK(write_class_of_dev); - - memcpy(hci->device.class, PARAM(write_class_of_dev, dev_class), - sizeof(PARAM(write_class_of_dev, dev_class))); - bt_hci_event_complete_status(hci, HCI_SUCCESS); - break; - - case cmd_opcode_pack(OGF_HOST_CTL, OCF_READ_VOICE_SETTING): - bt_hci_event_complete_voice_setting(hci); - break; - - case cmd_opcode_pack(OGF_HOST_CTL, OCF_WRITE_VOICE_SETTING): - LENGTH_CHECK(write_voice_setting); - - hci->voice_setting = PARAM(write_voice_setting, voice_setting); - bt_hci_event_complete_status(hci, HCI_SUCCESS); - break; - - case cmd_opcode_pack(OGF_HOST_CTL, OCF_HOST_NUMBER_OF_COMPLETED_PACKETS): - if (length < data[0] * 2 + 1) - goto short_hci; - - for (i = 0; i < data[0]; i ++) - if (bt_hci_handle_bad(hci, - data[i * 2 + 1] | (data[i * 2 + 2] << 8))) - bt_hci_event_complete_status(hci, HCI_INVALID_PARAMETERS); - break; - - case cmd_opcode_pack(OGF_HOST_CTL, OCF_READ_INQUIRY_MODE): - /* Only if (local_features[3] & 0x40) && (local_commands[12] & 0x40) - * else - * goto unknown_command */ - bt_hci_event_complete_read_inquiry_mode(hci); - break; - - case cmd_opcode_pack(OGF_HOST_CTL, OCF_WRITE_INQUIRY_MODE): - /* Only if (local_features[3] & 0x40) && (local_commands[12] & 0x80) - * else - * goto unknown_command */ - LENGTH_CHECK(write_inquiry_mode); - - if (PARAM(write_inquiry_mode, mode) > 0x01) { - bt_hci_event_complete_status(hci, HCI_INVALID_PARAMETERS); - break; - } - - hci->lm.inquiry_mode = PARAM(write_inquiry_mode, mode); - bt_hci_event_complete_status(hci, HCI_SUCCESS); - break; - - case cmd_opcode_pack(OGF_INFO_PARAM, OCF_READ_LOCAL_VERSION): - bt_hci_read_local_version_rp(hci); - break; - - case cmd_opcode_pack(OGF_INFO_PARAM, OCF_READ_LOCAL_COMMANDS): - bt_hci_read_local_commands_rp(hci); - break; - - case cmd_opcode_pack(OGF_INFO_PARAM, OCF_READ_LOCAL_FEATURES): - bt_hci_read_local_features_rp(hci); - break; - - case cmd_opcode_pack(OGF_INFO_PARAM, OCF_READ_LOCAL_EXT_FEATURES): - LENGTH_CHECK(read_local_ext_features); - - bt_hci_read_local_ext_features_rp(hci, - PARAM(read_local_ext_features, page_num)); - break; - - case cmd_opcode_pack(OGF_INFO_PARAM, OCF_READ_BUFFER_SIZE): - bt_hci_read_buffer_size_rp(hci); - break; - - case cmd_opcode_pack(OGF_INFO_PARAM, OCF_READ_COUNTRY_CODE): - bt_hci_read_country_code_rp(hci); - break; - - case cmd_opcode_pack(OGF_INFO_PARAM, OCF_READ_BD_ADDR): - bt_hci_read_bd_addr_rp(hci); - break; - - case cmd_opcode_pack(OGF_STATUS_PARAM, OCF_READ_LINK_QUALITY): - LENGTH_CHECK(read_link_quality); - - bt_hci_link_quality_rp(hci, PARAMHANDLE(read_link_quality)); - break; - - default: - bt_hci_event_status(hci, HCI_UNKNOWN_COMMAND); - break; - - short_hci: - error_report("%s: HCI packet too short (%iB)", __func__, length); - bt_hci_event_status(hci, HCI_INVALID_PARAMETERS); - break; - } -} - -/* We could perform fragmentation here, we can't do "recombination" because - * at this layer the length of the payload is not know ahead, so we only - * know that a packet contained the last fragment of the SDU when the next - * SDU starts. */ -static inline void bt_hci_lmp_acl_data(struct bt_hci_s *hci, uint16_t handle, - const uint8_t *data, int start, int len) -{ - struct hci_acl_hdr *pkt = (void *) hci->acl_buf; - - /* TODO: packet flags */ - /* TODO: avoid memcpy'ing */ - - if (len + HCI_ACL_HDR_SIZE > sizeof(hci->acl_buf)) { - error_report("%s: can't take ACL packets %i bytes long", - __func__, len); - return; - } - memcpy(hci->acl_buf + HCI_ACL_HDR_SIZE, data, len); - - pkt->handle = cpu_to_le16( - acl_handle_pack(handle, start ? ACL_START : ACL_CONT)); - pkt->dlen = cpu_to_le16(len); - hci->info.acl_recv(hci->info.opaque, - hci->acl_buf, len + HCI_ACL_HDR_SIZE); -} - -static void bt_hci_lmp_acl_data_slave(struct bt_link_s *btlink, - const uint8_t *data, int start, int len) -{ - struct bt_hci_link_s *link = (struct bt_hci_link_s *) btlink; - - bt_hci_lmp_acl_data(hci_from_device(btlink->slave), - link->handle, data, start, len); -} - -static void bt_hci_lmp_acl_data_host(struct bt_link_s *link, - const uint8_t *data, int start, int len) -{ - bt_hci_lmp_acl_data(hci_from_device(link->host), - link->handle, data, start, len); -} - -static void bt_submit_acl(struct HCIInfo *info, - const uint8_t *data, int length) -{ - struct bt_hci_s *hci = hci_from_info(info); - uint16_t handle; - int datalen, flags; - struct bt_link_s *link; - - if (length < HCI_ACL_HDR_SIZE) { - error_report("%s: ACL packet too short (%iB)", __func__, length); - return; - } - - handle = acl_handle((data[1] << 8) | data[0]); - flags = acl_flags((data[1] << 8) | data[0]); - datalen = (data[3] << 8) | data[2]; - data += HCI_ACL_HDR_SIZE; - length -= HCI_ACL_HDR_SIZE; - - if (bt_hci_handle_bad(hci, handle)) { - error_report("%s: invalid ACL handle %03x", __func__, handle); - /* TODO: signal an error */ - return; - } - handle &= ~HCI_HANDLE_OFFSET; - - if (datalen > length) { - error_report("%s: ACL packet too short (%iB < %iB)", - __func__, length, datalen); - return; - } - - link = hci->lm.handle[handle].link; - - if ((flags & ~3) == ACL_ACTIVE_BCAST) { - if (!hci->asb_handle) - hci->asb_handle = handle; - else if (handle != hci->asb_handle) { - error_report("%s: Bad handle %03x in Active Slave Broadcast", - __func__, handle); - /* TODO: signal an error */ - return; - } - - /* TODO */ - } - - if ((flags & ~3) == ACL_PICO_BCAST) { - if (!hci->psb_handle) - hci->psb_handle = handle; - else if (handle != hci->psb_handle) { - error_report("%s: Bad handle %03x in Parked Slave Broadcast", - __func__, handle); - /* TODO: signal an error */ - return; - } - - /* TODO */ - } - - /* TODO: increase counter and send EVT_NUM_COMP_PKTS */ - bt_hci_event_num_comp_pkts(hci, handle | HCI_HANDLE_OFFSET, 1); - - /* Do this last as it can trigger further events even in this HCI */ - hci->lm.handle[handle].lmp_acl_data(link, data, - (flags & 3) == ACL_START, length); -} - -static void bt_submit_sco(struct HCIInfo *info, - const uint8_t *data, int length) -{ - struct bt_hci_s *hci = hci_from_info(info); - uint16_t handle; - int datalen; - - if (length < 3) - return; - - handle = acl_handle((data[1] << 8) | data[0]); - datalen = data[2]; - length -= 3; - - if (bt_hci_handle_bad(hci, handle)) { - error_report("%s: invalid SCO handle %03x", __func__, handle); - return; - } - - if (datalen > length) { - error_report("%s: SCO packet too short (%iB < %iB)", - __func__, length, datalen); - return; - } - - /* TODO */ - - /* TODO: increase counter and send EVT_NUM_COMP_PKTS if synchronous - * Flow Control is enabled. - * (See Read/Write_Synchronous_Flow_Control_Enable on page 513 and - * page 514.) */ -} - -static uint8_t *bt_hci_evt_packet(void *opaque) -{ - /* TODO: allocate a packet from upper layer */ - struct bt_hci_s *s = opaque; - - return s->evt_buf; -} - -static void bt_hci_evt_submit(void *opaque, int len) -{ - /* TODO: notify upper layer */ - struct bt_hci_s *s = opaque; - - s->info.evt_recv(s->info.opaque, s->evt_buf, len); -} - -static int bt_hci_bdaddr_set(struct HCIInfo *info, const uint8_t *bd_addr) -{ - struct bt_hci_s *hci = hci_from_info(info); - - bacpy(&hci->device.bd_addr, (const bdaddr_t *) bd_addr); - return 0; -} - -static void bt_hci_done(struct HCIInfo *info); -static void bt_hci_destroy(struct bt_device_s *dev) -{ - struct bt_hci_s *hci = hci_from_device(dev); - - bt_hci_done(&hci->info); -} - -struct HCIInfo *bt_new_hci(struct bt_scatternet_s *net) -{ - struct bt_hci_s *s = g_malloc0(sizeof(struct bt_hci_s)); - - s->lm.inquiry_done = timer_new_ns(QEMU_CLOCK_VIRTUAL, bt_hci_inquiry_done, s); - s->lm.inquiry_next = timer_new_ns(QEMU_CLOCK_VIRTUAL, bt_hci_inquiry_next, s); - s->conn_accept_timer = - timer_new_ns(QEMU_CLOCK_VIRTUAL, bt_hci_conn_accept_timeout, s); - - s->evt_packet = bt_hci_evt_packet; - s->evt_submit = bt_hci_evt_submit; - s->opaque = s; - - bt_device_init(&s->device, net); - s->device.lmp_connection_request = bt_hci_lmp_connection_request; - s->device.lmp_connection_complete = bt_hci_lmp_connection_complete; - s->device.lmp_disconnect_master = bt_hci_lmp_disconnect_host; - s->device.lmp_disconnect_slave = bt_hci_lmp_disconnect_slave; - s->device.lmp_acl_data = bt_hci_lmp_acl_data_slave; - s->device.lmp_acl_resp = bt_hci_lmp_acl_data_host; - s->device.lmp_mode_change = bt_hci_lmp_mode_change_slave; - - /* Keep updated! */ - /* Also keep in sync with supported commands bitmask in - * bt_hci_read_local_commands_rp */ - s->device.lmp_caps = 0x8000199b7e85355fll; - - bt_hci_reset(s); - - s->info.cmd_send = bt_submit_hci; - s->info.sco_send = bt_submit_sco; - s->info.acl_send = bt_submit_acl; - s->info.bdaddr_set = bt_hci_bdaddr_set; - - s->device.handle_destroy = bt_hci_destroy; - - error_setg(&s->replay_blocker, QERR_REPLAY_NOT_SUPPORTED, "-bt hci"); - replay_add_blocker(s->replay_blocker); - - return &s->info; -} - -struct HCIInfo *hci_init(const char *str) -{ - char *endp; - struct bt_scatternet_s *vlan = 0; - - if (!strcmp(str, "null")) - /* null */ - return &null_hci; - else if (!strncmp(str, "host", 4) && (str[4] == '\0' || str[4] == ':')) - /* host[:hciN] */ - return bt_host_hci(str[4] ? str + 5 : "hci0"); - else if (!strncmp(str, "hci", 3)) { - /* hci[,vlan=n] */ - if (str[3]) { - if (!strncmp(str + 3, ",vlan=", 6)) { - vlan = qemu_find_bt_vlan(strtol(str + 9, &endp, 0)); - if (*endp) - vlan = 0; - } - } else - vlan = qemu_find_bt_vlan(0); - if (vlan) - return bt_new_hci(vlan); - } - - error_report("Unknown bluetooth HCI `%s'.", str); - - return 0; -} - -static void bt_hci_done(struct HCIInfo *info) -{ - struct bt_hci_s *hci = hci_from_info(info); - int handle; - - bt_device_done(&hci->device); - - g_free((void *) hci->device.lmp_name); - - /* Be gentle and send DISCONNECT to all connected peers and those - * currently waiting for us to accept or reject a connection request. - * This frees the links. */ - if (hci->conn_req_host) { - bt_hci_connection_reject(hci, - hci->conn_req_host, HCI_OE_POWER_OFF); - return; - } - - for (handle = HCI_HANDLE_OFFSET; - handle < (HCI_HANDLE_OFFSET | HCI_HANDLES_MAX); handle ++) - if (!bt_hci_handle_bad(hci, handle)) - bt_hci_disconnect(hci, handle, HCI_OE_POWER_OFF); - - /* TODO: this is not enough actually, there may be slaves from whom - * we have requested a connection who will soon (or not) respond with - * an accept or a reject, so we should also check if hci->lm.connecting - * is non-zero and if so, avoid freeing the hci but otherwise disappear - * from all qemu social life (e.g. stop scanning and request to be - * removed from s->device.net) and arrange for - * s->device.lmp_connection_complete to free the remaining bits once - * hci->lm.awaiting_bdaddr[] is empty. */ - - timer_free(hci->lm.inquiry_done); - timer_free(hci->lm.inquiry_next); - timer_free(hci->conn_accept_timer); - - g_free(hci); -} diff --git a/hw/bt/hid.c b/hw/bt/hid.c deleted file mode 100644 index 066ca99ed2..0000000000 --- a/hw/bt/hid.c +++ /dev/null @@ -1,553 +0,0 @@ -/* - * QEMU Bluetooth HID Profile wrapper for USB HID. - * - * Copyright (C) 2007-2008 OpenMoko, Inc. - * Written by Andrzej Zaborowski - * - * 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 or - * (at your option) version 3 of the License. - * - * 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, if not, see . - */ - -#include "qemu/osdep.h" -#include "qemu/timer.h" -#include "ui/console.h" -#include "hw/input/hid.h" -#include "hw/bt.h" - -enum hid_transaction_req { - BT_HANDSHAKE = 0x0, - BT_HID_CONTROL = 0x1, - BT_GET_REPORT = 0x4, - BT_SET_REPORT = 0x5, - BT_GET_PROTOCOL = 0x6, - BT_SET_PROTOCOL = 0x7, - BT_GET_IDLE = 0x8, - BT_SET_IDLE = 0x9, - BT_DATA = 0xa, - BT_DATC = 0xb, -}; - -enum hid_transaction_handshake { - BT_HS_SUCCESSFUL = 0x0, - BT_HS_NOT_READY = 0x1, - BT_HS_ERR_INVALID_REPORT_ID = 0x2, - BT_HS_ERR_UNSUPPORTED_REQUEST = 0x3, - BT_HS_ERR_INVALID_PARAMETER = 0x4, - BT_HS_ERR_UNKNOWN = 0xe, - BT_HS_ERR_FATAL = 0xf, -}; - -enum hid_transaction_control { - BT_HC_NOP = 0x0, - BT_HC_HARD_RESET = 0x1, - BT_HC_SOFT_RESET = 0x2, - BT_HC_SUSPEND = 0x3, - BT_HC_EXIT_SUSPEND = 0x4, - BT_HC_VIRTUAL_CABLE_UNPLUG = 0x5, -}; - -enum hid_protocol { - BT_HID_PROTO_BOOT = 0, - BT_HID_PROTO_REPORT = 1, -}; - -enum hid_boot_reportid { - BT_HID_BOOT_INVALID = 0, - BT_HID_BOOT_KEYBOARD, - BT_HID_BOOT_MOUSE, -}; - -enum hid_data_pkt { - BT_DATA_OTHER = 0, - BT_DATA_INPUT, - BT_DATA_OUTPUT, - BT_DATA_FEATURE, -}; - -#define BT_HID_MTU 48 - -/* HID interface requests */ -#define GET_REPORT 0xa101 -#define GET_IDLE 0xa102 -#define GET_PROTOCOL 0xa103 -#define SET_REPORT 0x2109 -#define SET_IDLE 0x210a -#define SET_PROTOCOL 0x210b - -struct bt_hid_device_s { - struct bt_l2cap_device_s btdev; - struct bt_l2cap_conn_params_s *control; - struct bt_l2cap_conn_params_s *interrupt; - HIDState hid; - - int proto; - int connected; - int data_type; - int intr_state; - struct { - int len; - uint8_t buffer[1024]; - } dataother, datain, dataout, feature, intrdataout; - enum { - bt_state_ready, - bt_state_transaction, - bt_state_suspend, - } state; -}; - -static void bt_hid_reset(struct bt_hid_device_s *s) -{ - struct bt_scatternet_s *net = s->btdev.device.net; - - /* Go as far as... */ - bt_l2cap_device_done(&s->btdev); - bt_l2cap_device_init(&s->btdev, net); - - hid_reset(&s->hid); - s->proto = BT_HID_PROTO_REPORT; - s->state = bt_state_ready; - s->dataother.len = 0; - s->datain.len = 0; - s->dataout.len = 0; - s->feature.len = 0; - s->intrdataout.len = 0; - s->intr_state = 0; -} - -static int bt_hid_out(struct bt_hid_device_s *s) -{ - if (s->data_type == BT_DATA_OUTPUT) { - /* nothing */ - ; - } - - if (s->data_type == BT_DATA_FEATURE) { - /* XXX: - * does this send a USB_REQ_CLEAR_FEATURE/USB_REQ_SET_FEATURE - * or a SET_REPORT? */ - ; - } - - return -1; -} - -static int bt_hid_in(struct bt_hid_device_s *s) -{ - s->datain.len = hid_keyboard_poll(&s->hid, s->datain.buffer, - sizeof(s->datain.buffer)); - return s->datain.len; -} - -static void bt_hid_send_handshake(struct bt_hid_device_s *s, int result) -{ - *s->control->sdu_out(s->control, 1) = - (BT_HANDSHAKE << 4) | result; - s->control->sdu_submit(s->control); -} - -static void bt_hid_send_control(struct bt_hid_device_s *s, int operation) -{ - *s->control->sdu_out(s->control, 1) = - (BT_HID_CONTROL << 4) | operation; - s->control->sdu_submit(s->control); -} - -static void bt_hid_disconnect(struct bt_hid_device_s *s) -{ - /* Disconnect s->control and s->interrupt */ -} - -static void bt_hid_send_data(struct bt_l2cap_conn_params_s *ch, int type, - const uint8_t *data, int len) -{ - uint8_t *pkt, hdr = (BT_DATA << 4) | type; - int plen; - - do { - plen = MIN(len, ch->remote_mtu - 1); - pkt = ch->sdu_out(ch, plen + 1); - - pkt[0] = hdr; - if (plen) - memcpy(pkt + 1, data, plen); - ch->sdu_submit(ch); - - len -= plen; - data += plen; - hdr = (BT_DATC << 4) | type; - } while (plen == ch->remote_mtu - 1); -} - -static void bt_hid_control_transaction(struct bt_hid_device_s *s, - const uint8_t *data, int len) -{ - uint8_t type, parameter; - int rlen, ret = -1; - if (len < 1) - return; - - type = data[0] >> 4; - parameter = data[0] & 0xf; - - switch (type) { - case BT_HANDSHAKE: - case BT_DATA: - switch (parameter) { - default: - /* These are not expected to be sent this direction. */ - ret = BT_HS_ERR_INVALID_PARAMETER; - } - break; - - case BT_HID_CONTROL: - if (len != 1 || (parameter != BT_HC_VIRTUAL_CABLE_UNPLUG && - s->state == bt_state_transaction)) { - ret = BT_HS_ERR_INVALID_PARAMETER; - break; - } - switch (parameter) { - case BT_HC_NOP: - break; - case BT_HC_HARD_RESET: - case BT_HC_SOFT_RESET: - bt_hid_reset(s); - break; - case BT_HC_SUSPEND: - if (s->state == bt_state_ready) - s->state = bt_state_suspend; - else - ret = BT_HS_ERR_INVALID_PARAMETER; - break; - case BT_HC_EXIT_SUSPEND: - if (s->state == bt_state_suspend) - s->state = bt_state_ready; - else - ret = BT_HS_ERR_INVALID_PARAMETER; - break; - case BT_HC_VIRTUAL_CABLE_UNPLUG: - bt_hid_disconnect(s); - break; - default: - ret = BT_HS_ERR_INVALID_PARAMETER; - } - break; - - case BT_GET_REPORT: - /* No ReportIDs declared. */ - if (((parameter & 8) && len != 3) || - (!(parameter & 8) && len != 1) || - s->state != bt_state_ready) { - ret = BT_HS_ERR_INVALID_PARAMETER; - break; - } - if (parameter & 8) - rlen = data[2] | (data[3] << 8); - else - rlen = INT_MAX; - switch (parameter & 3) { - case BT_DATA_OTHER: - ret = BT_HS_ERR_INVALID_PARAMETER; - break; - case BT_DATA_INPUT: - /* Here we can as well poll s->usbdev */ - bt_hid_send_data(s->control, BT_DATA_INPUT, - s->datain.buffer, MIN(rlen, s->datain.len)); - break; - case BT_DATA_OUTPUT: - bt_hid_send_data(s->control, BT_DATA_OUTPUT, - s->dataout.buffer, MIN(rlen, s->dataout.len)); - break; - case BT_DATA_FEATURE: - bt_hid_send_data(s->control, BT_DATA_FEATURE, - s->feature.buffer, MIN(rlen, s->feature.len)); - break; - } - break; - - case BT_SET_REPORT: - if (len < 2 || len > BT_HID_MTU || s->state != bt_state_ready || - (parameter & 3) == BT_DATA_OTHER || - (parameter & 3) == BT_DATA_INPUT) { - ret = BT_HS_ERR_INVALID_PARAMETER; - break; - } - s->data_type = parameter & 3; - if (s->data_type == BT_DATA_OUTPUT) { - s->dataout.len = len - 1; - memcpy(s->dataout.buffer, data + 1, s->dataout.len); - } else { - s->feature.len = len - 1; - memcpy(s->feature.buffer, data + 1, s->feature.len); - } - if (len == BT_HID_MTU) - s->state = bt_state_transaction; - else - bt_hid_out(s); - break; - - case BT_GET_PROTOCOL: - if (len != 1 || s->state == bt_state_transaction) { - ret = BT_HS_ERR_INVALID_PARAMETER; - break; - } - *s->control->sdu_out(s->control, 1) = s->proto; - s->control->sdu_submit(s->control); - break; - - case BT_SET_PROTOCOL: - if (len != 1 || s->state == bt_state_transaction || - (parameter != BT_HID_PROTO_BOOT && - parameter != BT_HID_PROTO_REPORT)) { - ret = BT_HS_ERR_INVALID_PARAMETER; - break; - } - s->proto = parameter; - s->hid.protocol = parameter; - ret = BT_HS_SUCCESSFUL; - break; - - case BT_GET_IDLE: - if (len != 1 || s->state == bt_state_transaction) { - ret = BT_HS_ERR_INVALID_PARAMETER; - break; - } - *s->control->sdu_out(s->control, 1) = s->hid.idle; - s->control->sdu_submit(s->control); - break; - - case BT_SET_IDLE: - if (len != 2 || s->state == bt_state_transaction) { - ret = BT_HS_ERR_INVALID_PARAMETER; - break; - } - - s->hid.idle = data[1]; - /* XXX: Does this generate a handshake? */ - break; - - case BT_DATC: - if (len > BT_HID_MTU || s->state != bt_state_transaction) { - ret = BT_HS_ERR_INVALID_PARAMETER; - break; - } - if (s->data_type == BT_DATA_OUTPUT) { - memcpy(s->dataout.buffer + s->dataout.len, data + 1, len - 1); - s->dataout.len += len - 1; - } else { - memcpy(s->feature.buffer + s->feature.len, data + 1, len - 1); - s->feature.len += len - 1; - } - if (len < BT_HID_MTU) { - bt_hid_out(s); - s->state = bt_state_ready; - } - break; - - default: - ret = BT_HS_ERR_UNSUPPORTED_REQUEST; - } - - if (ret != -1) - bt_hid_send_handshake(s, ret); -} - -static void bt_hid_control_sdu(void *opaque, const uint8_t *data, int len) -{ - struct bt_hid_device_s *hid = opaque; - - bt_hid_control_transaction(hid, data, len); -} - -static void bt_hid_datain(HIDState *hs) -{ - struct bt_hid_device_s *hid = - container_of(hs, struct bt_hid_device_s, hid); - - /* If suspended, wake-up and send a wake-up event first. We might - * want to also inspect the input report and ignore event like - * mouse movements until a button event occurs. */ - if (hid->state == bt_state_suspend) { - hid->state = bt_state_ready; - } - - if (bt_hid_in(hid) > 0) - /* TODO: when in boot-mode precede any Input reports with the ReportID - * byte, here and in GetReport/SetReport on the Control channel. */ - bt_hid_send_data(hid->interrupt, BT_DATA_INPUT, - hid->datain.buffer, hid->datain.len); -} - -static void bt_hid_interrupt_sdu(void *opaque, const uint8_t *data, int len) -{ - struct bt_hid_device_s *hid = opaque; - - if (len > BT_HID_MTU || len < 1) - goto bad; - if ((data[0] & 3) != BT_DATA_OUTPUT) - goto bad; - if ((data[0] >> 4) == BT_DATA) { - if (hid->intr_state) - goto bad; - - hid->data_type = BT_DATA_OUTPUT; - hid->intrdataout.len = 0; - } else if ((data[0] >> 4) == BT_DATC) { - if (!hid->intr_state) - goto bad; - } else - goto bad; - - memcpy(hid->intrdataout.buffer + hid->intrdataout.len, data + 1, len - 1); - hid->intrdataout.len += len - 1; - hid->intr_state = (len == BT_HID_MTU); - if (!hid->intr_state) { - memcpy(hid->dataout.buffer, hid->intrdataout.buffer, - hid->dataout.len = hid->intrdataout.len); - bt_hid_out(hid); - } - - return; -bad: - error_report("%s: bad transaction on Interrupt channel.", - __func__); -} - -/* "Virtual cable" plug/unplug event. */ -static void bt_hid_connected_update(struct bt_hid_device_s *hid) -{ - int prev = hid->connected; - - hid->connected = hid->control && hid->interrupt; - - /* Stop page-/inquiry-scanning when a host is connected. */ - hid->btdev.device.page_scan = !hid->connected; - hid->btdev.device.inquiry_scan = !hid->connected; - - if (hid->connected && !prev) { - hid_reset(&hid->hid); - hid->proto = BT_HID_PROTO_REPORT; - } - - /* Should set HIDVirtualCable in SDP (possibly need to check that SDP - * isn't destroyed yet, in case we're being called from handle_destroy) */ -} - -static void bt_hid_close_control(void *opaque) -{ - struct bt_hid_device_s *hid = opaque; - - hid->control = NULL; - bt_hid_connected_update(hid); -} - -static void bt_hid_close_interrupt(void *opaque) -{ - struct bt_hid_device_s *hid = opaque; - - hid->interrupt = NULL; - bt_hid_connected_update(hid); -} - -static int bt_hid_new_control_ch(struct bt_l2cap_device_s *dev, - struct bt_l2cap_conn_params_s *params) -{ - struct bt_hid_device_s *hid = (struct bt_hid_device_s *) dev; - - if (hid->control) - return 1; - - hid->control = params; - hid->control->opaque = hid; - hid->control->close = bt_hid_close_control; - hid->control->sdu_in = bt_hid_control_sdu; - - bt_hid_connected_update(hid); - - return 0; -} - -static int bt_hid_new_interrupt_ch(struct bt_l2cap_device_s *dev, - struct bt_l2cap_conn_params_s *params) -{ - struct bt_hid_device_s *hid = (struct bt_hid_device_s *) dev; - - if (hid->interrupt) - return 1; - - hid->interrupt = params; - hid->interrupt->opaque = hid; - hid->interrupt->close = bt_hid_close_interrupt; - hid->interrupt->sdu_in = bt_hid_interrupt_sdu; - - bt_hid_connected_update(hid); - - return 0; -} - -static void bt_hid_destroy(struct bt_device_s *dev) -{ - struct bt_hid_device_s *hid = (struct bt_hid_device_s *) dev; - - if (hid->connected) - bt_hid_send_control(hid, BT_HC_VIRTUAL_CABLE_UNPLUG); - bt_l2cap_device_done(&hid->btdev); - - hid_free(&hid->hid); - - g_free(hid); -} - -enum peripheral_minor_class { - class_other = 0 << 4, - class_keyboard = 1 << 4, - class_pointing = 2 << 4, - class_combo = 3 << 4, -}; - -static struct bt_device_s *bt_hid_init(struct bt_scatternet_s *net, - enum peripheral_minor_class minor) -{ - struct bt_hid_device_s *s = g_malloc0(sizeof(*s)); - uint32_t class = - /* Format type */ - (0 << 0) | - /* Device class */ - (minor << 2) | - (5 << 8) | /* "Peripheral" */ - /* Service classes */ - (1 << 13) | /* Limited discoverable mode */ - (1 << 19); /* Capturing device (?) */ - - bt_l2cap_device_init(&s->btdev, net); - bt_l2cap_sdp_init(&s->btdev); - bt_l2cap_psm_register(&s->btdev, BT_PSM_HID_CTRL, - BT_HID_MTU, bt_hid_new_control_ch); - bt_l2cap_psm_register(&s->btdev, BT_PSM_HID_INTR, - BT_HID_MTU, bt_hid_new_interrupt_ch); - - hid_init(&s->hid, HID_KEYBOARD, bt_hid_datain); - s->btdev.device.lmp_name = "BT Keyboard"; - - s->btdev.device.handle_destroy = bt_hid_destroy; - - s->btdev.device.class[0] = (class >> 0) & 0xff; - s->btdev.device.class[1] = (class >> 8) & 0xff; - s->btdev.device.class[2] = (class >> 16) & 0xff; - - return &s->btdev.device; -} - -struct bt_device_s *bt_keyboard_init(struct bt_scatternet_s *net) -{ - return bt_hid_init(net, class_keyboard); -} diff --git a/hw/bt/l2cap.c b/hw/bt/l2cap.c deleted file mode 100644 index d67098a719..0000000000 --- a/hw/bt/l2cap.c +++ /dev/null @@ -1,1367 +0,0 @@ -/* - * QEMU Bluetooth L2CAP logic. - * - * Copyright (C) 2008 Andrzej Zaborowski - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - */ - -#include "qemu/osdep.h" -#include "qemu/error-report.h" -#include "qemu/timer.h" -#include "qemu/bswap.h" -#include "hw/bt.h" - -#define L2CAP_CID_MAX 0x100 /* Between 0x40 and 0x10000 */ - -struct l2cap_instance_s { - struct bt_link_s *link; - struct bt_l2cap_device_s *dev; - int role; - - uint8_t frame_in[65535 + L2CAP_HDR_SIZE] __attribute__ ((aligned (4))); - int frame_in_len; - - uint8_t frame_out[65535 + L2CAP_HDR_SIZE] __attribute__ ((aligned (4))); - int frame_out_len; - - /* Signalling channel timers. They exist per-request but we can make - * sure we have no more than one outstanding request at any time. */ - QEMUTimer *rtx; - QEMUTimer *ertx; - - int last_id; - int next_id; - - struct l2cap_chan_s { - struct bt_l2cap_conn_params_s params; - - void (*frame_in)(struct l2cap_chan_s *chan, uint16_t cid, - const l2cap_hdr *hdr, int len); - int mps; - int min_mtu; - - struct l2cap_instance_s *l2cap; - - /* Only allocated channels */ - uint16_t remote_cid; -#define L2CAP_CFG_INIT 2 -#define L2CAP_CFG_ACC 1 - int config_req_id; /* TODO: handle outgoing requests generically */ - int config; - - /* Only connection-oriented channels. Note: if we allow the tx and - * rx traffic to be in different modes at any time, we need two. */ - int mode; - - /* Only flow-controlled, connection-oriented channels */ - uint8_t sdu[65536]; /* TODO: dynamically allocate */ - int len_cur, len_total; - int rexmit; - int monitor_timeout; - QEMUTimer *monitor_timer; - QEMUTimer *retransmission_timer; - } *cid[L2CAP_CID_MAX]; - /* The channel state machine states map as following: - * CLOSED -> !cid[N] - * WAIT_CONNECT -> never occurs - * WAIT_CONNECT_RSP -> never occurs - * CONFIG -> cid[N] && config < 3 - * WAIT_CONFIG -> never occurs, cid[N] && config == 0 && !config_r - * WAIT_SEND_CONFIG -> never occurs, cid[N] && config == 1 && !config_r - * WAIT_CONFIG_REQ_RSP -> cid[N] && config == 0 && config_req_id - * WAIT_CONFIG_RSP -> cid[N] && config == 1 && config_req_id - * WAIT_CONFIG_REQ -> cid[N] && config == 2 - * OPEN -> cid[N] && config == 3 - * WAIT_DISCONNECT -> never occurs - */ - - struct l2cap_chan_s signalling_ch; - struct l2cap_chan_s group_ch; -}; - -struct slave_l2cap_instance_s { - struct bt_link_s link; /* Underlying logical link (ACL) */ - struct l2cap_instance_s l2cap; -}; - -struct bt_l2cap_psm_s { - int psm; - int min_mtu; - int (*new_channel)(struct bt_l2cap_device_s *device, - struct bt_l2cap_conn_params_s *params); - struct bt_l2cap_psm_s *next; -}; - -static const uint16_t l2cap_fcs16_table[256] = { - 0x0000, 0xc0c1, 0xc181, 0x0140, 0xc301, 0x03c0, 0x0280, 0xc241, - 0xc601, 0x06c0, 0x0780, 0xc741, 0x0500, 0xc5c1, 0xc481, 0x0440, - 0xcc01, 0x0cc0, 0x0d80, 0xcd41, 0x0f00, 0xcfc1, 0xce81, 0x0e40, - 0x0a00, 0xcac1, 0xcb81, 0x0b40, 0xc901, 0x09c0, 0x0880, 0xc841, - 0xd801, 0x18c0, 0x1980, 0xd941, 0x1b00, 0xdbc1, 0xda81, 0x1a40, - 0x1e00, 0xdec1, 0xdf81, 0x1f40, 0xdd01, 0x1dc0, 0x1c80, 0xdc41, - 0x1400, 0xd4c1, 0xd581, 0x1540, 0xd701, 0x17c0, 0x1680, 0xd641, - 0xd201, 0x12c0, 0x1380, 0xd341, 0x1100, 0xd1c1, 0xd081, 0x1040, - 0xf001, 0x30c0, 0x3180, 0xf141, 0x3300, 0xf3c1, 0xf281, 0x3240, - 0x3600, 0xf6c1, 0xf781, 0x3740, 0xf501, 0x35c0, 0x3480, 0xf441, - 0x3c00, 0xfcc1, 0xfd81, 0x3d40, 0xff01, 0x3fc0, 0x3e80, 0xfe41, - 0xfa01, 0x3ac0, 0x3b80, 0xfb41, 0x3900, 0xf9c1, 0xf881, 0x3840, - 0x2800, 0xe8c1, 0xe981, 0x2940, 0xeb01, 0x2bc0, 0x2a80, 0xea41, - 0xee01, 0x2ec0, 0x2f80, 0xef41, 0x2d00, 0xedc1, 0xec81, 0x2c40, - 0xe401, 0x24c0, 0x2580, 0xe541, 0x2700, 0xe7c1, 0xe681, 0x2640, - 0x2200, 0xe2c1, 0xe381, 0x2340, 0xe101, 0x21c0, 0x2080, 0xe041, - 0xa001, 0x60c0, 0x6180, 0xa141, 0x6300, 0xa3c1, 0xa281, 0x6240, - 0x6600, 0xa6c1, 0xa781, 0x6740, 0xa501, 0x65c0, 0x6480, 0xa441, - 0x6c00, 0xacc1, 0xad81, 0x6d40, 0xaf01, 0x6fc0, 0x6e80, 0xae41, - 0xaa01, 0x6ac0, 0x6b80, 0xab41, 0x6900, 0xa9c1, 0xa881, 0x6840, - 0x7800, 0xb8c1, 0xb981, 0x7940, 0xbb01, 0x7bc0, 0x7a80, 0xba41, - 0xbe01, 0x7ec0, 0x7f80, 0xbf41, 0x7d00, 0xbdc1, 0xbc81, 0x7c40, - 0xb401, 0x74c0, 0x7580, 0xb541, 0x7700, 0xb7c1, 0xb681, 0x7640, - 0x7200, 0xb2c1, 0xb381, 0x7340, 0xb101, 0x71c0, 0x7080, 0xb041, - 0x5000, 0x90c1, 0x9181, 0x5140, 0x9301, 0x53c0, 0x5280, 0x9241, - 0x9601, 0x56c0, 0x5780, 0x9741, 0x5500, 0x95c1, 0x9481, 0x5440, - 0x9c01, 0x5cc0, 0x5d80, 0x9d41, 0x5f00, 0x9fc1, 0x9e81, 0x5e40, - 0x5a00, 0x9ac1, 0x9b81, 0x5b40, 0x9901, 0x59c0, 0x5880, 0x9841, - 0x8801, 0x48c0, 0x4980, 0x8941, 0x4b00, 0x8bc1, 0x8a81, 0x4a40, - 0x4e00, 0x8ec1, 0x8f81, 0x4f40, 0x8d01, 0x4dc0, 0x4c80, 0x8c41, - 0x4400, 0x84c1, 0x8581, 0x4540, 0x8701, 0x47c0, 0x4680, 0x8641, - 0x8201, 0x42c0, 0x4380, 0x8341, 0x4100, 0x81c1, 0x8081, 0x4040, -}; - -static uint16_t l2cap_fcs16(const uint8_t *message, int len) -{ - uint16_t fcs = 0x0000; - - while (len --) -#if 0 - { - int i; - - fcs ^= *message ++; - for (i = 8; i; -- i) - if (fcs & 1) - fcs = (fcs >> 1) ^ 0xa001; - else - fcs = (fcs >> 1); - } -#else - fcs = (fcs >> 8) ^ l2cap_fcs16_table[(fcs ^ *message ++) & 0xff]; -#endif - - return fcs; -} - -/* L2CAP layer logic (protocol) */ - -static void l2cap_retransmission_timer_update(struct l2cap_chan_s *ch) -{ -#if 0 - if (ch->mode != L2CAP_MODE_BASIC && ch->rexmit) - timer_mod(ch->retransmission_timer); - else - timer_del(ch->retransmission_timer); -#endif -} - -static void l2cap_monitor_timer_update(struct l2cap_chan_s *ch) -{ -#if 0 - if (ch->mode != L2CAP_MODE_BASIC && !ch->rexmit) - timer_mod(ch->monitor_timer); - else - timer_del(ch->monitor_timer); -#endif -} - -static void l2cap_command_reject(struct l2cap_instance_s *l2cap, int id, - uint16_t reason, const void *data, int plen) -{ - uint8_t *pkt; - l2cap_cmd_hdr *hdr; - l2cap_cmd_rej *params; - uint16_t len; - - reason = cpu_to_le16(reason); - len = cpu_to_le16(L2CAP_CMD_REJ_SIZE + plen); - - pkt = l2cap->signalling_ch.params.sdu_out(&l2cap->signalling_ch.params, - L2CAP_CMD_HDR_SIZE + L2CAP_CMD_REJ_SIZE + plen); - hdr = (void *) (pkt + 0); - params = (void *) (pkt + L2CAP_CMD_HDR_SIZE); - - hdr->code = L2CAP_COMMAND_REJ; - hdr->ident = id; - memcpy(&hdr->len, &len, sizeof(hdr->len)); - memcpy(¶ms->reason, &reason, sizeof(reason)); - if (plen) - memcpy(pkt + L2CAP_CMD_HDR_SIZE + L2CAP_CMD_REJ_SIZE, data, plen); - - l2cap->signalling_ch.params.sdu_submit(&l2cap->signalling_ch.params); -} - -static void l2cap_command_reject_cid(struct l2cap_instance_s *l2cap, int id, - uint16_t reason, uint16_t dcid, uint16_t scid) -{ - l2cap_cmd_rej_cid params = { - .dcid = dcid, - .scid = scid, - }; - - l2cap_command_reject(l2cap, id, reason, ¶ms, L2CAP_CMD_REJ_CID_SIZE); -} - -static void l2cap_connection_response(struct l2cap_instance_s *l2cap, - int dcid, int scid, int result, int status) -{ - uint8_t *pkt; - l2cap_cmd_hdr *hdr; - l2cap_conn_rsp *params; - - pkt = l2cap->signalling_ch.params.sdu_out(&l2cap->signalling_ch.params, - L2CAP_CMD_HDR_SIZE + L2CAP_CONN_RSP_SIZE); - hdr = (void *) (pkt + 0); - params = (void *) (pkt + L2CAP_CMD_HDR_SIZE); - - hdr->code = L2CAP_CONN_RSP; - hdr->ident = l2cap->last_id; - hdr->len = cpu_to_le16(L2CAP_CONN_RSP_SIZE); - - params->dcid = cpu_to_le16(dcid); - params->scid = cpu_to_le16(scid); - params->result = cpu_to_le16(result); - params->status = cpu_to_le16(status); - - l2cap->signalling_ch.params.sdu_submit(&l2cap->signalling_ch.params); -} - -static void l2cap_configuration_request(struct l2cap_instance_s *l2cap, - int dcid, int flag, const uint8_t *data, int len) -{ - uint8_t *pkt; - l2cap_cmd_hdr *hdr; - l2cap_conf_req *params; - - pkt = l2cap->signalling_ch.params.sdu_out(&l2cap->signalling_ch.params, - L2CAP_CMD_HDR_SIZE + L2CAP_CONF_REQ_SIZE(len)); - hdr = (void *) (pkt + 0); - params = (void *) (pkt + L2CAP_CMD_HDR_SIZE); - - /* TODO: unify the id sequencing */ - l2cap->last_id = l2cap->next_id; - l2cap->next_id = l2cap->next_id == 255 ? 1 : l2cap->next_id + 1; - - hdr->code = L2CAP_CONF_REQ; - hdr->ident = l2cap->last_id; - hdr->len = cpu_to_le16(L2CAP_CONF_REQ_SIZE(len)); - - params->dcid = cpu_to_le16(dcid); - params->flags = cpu_to_le16(flag); - if (len) - memcpy(params->data, data, len); - - l2cap->signalling_ch.params.sdu_submit(&l2cap->signalling_ch.params); -} - -static void l2cap_configuration_response(struct l2cap_instance_s *l2cap, - int scid, int flag, int result, const uint8_t *data, int len) -{ - uint8_t *pkt; - l2cap_cmd_hdr *hdr; - l2cap_conf_rsp *params; - - pkt = l2cap->signalling_ch.params.sdu_out(&l2cap->signalling_ch.params, - L2CAP_CMD_HDR_SIZE + L2CAP_CONF_RSP_SIZE(len)); - hdr = (void *) (pkt + 0); - params = (void *) (pkt + L2CAP_CMD_HDR_SIZE); - - hdr->code = L2CAP_CONF_RSP; - hdr->ident = l2cap->last_id; - hdr->len = cpu_to_le16(L2CAP_CONF_RSP_SIZE(len)); - - params->scid = cpu_to_le16(scid); - params->flags = cpu_to_le16(flag); - params->result = cpu_to_le16(result); - if (len) - memcpy(params->data, data, len); - - l2cap->signalling_ch.params.sdu_submit(&l2cap->signalling_ch.params); -} - -static void l2cap_disconnection_response(struct l2cap_instance_s *l2cap, - int dcid, int scid) -{ - uint8_t *pkt; - l2cap_cmd_hdr *hdr; - l2cap_disconn_rsp *params; - - pkt = l2cap->signalling_ch.params.sdu_out(&l2cap->signalling_ch.params, - L2CAP_CMD_HDR_SIZE + L2CAP_DISCONN_RSP_SIZE); - hdr = (void *) (pkt + 0); - params = (void *) (pkt + L2CAP_CMD_HDR_SIZE); - - hdr->code = L2CAP_DISCONN_RSP; - hdr->ident = l2cap->last_id; - hdr->len = cpu_to_le16(L2CAP_DISCONN_RSP_SIZE); - - params->dcid = cpu_to_le16(dcid); - params->scid = cpu_to_le16(scid); - - l2cap->signalling_ch.params.sdu_submit(&l2cap->signalling_ch.params); -} - -static void l2cap_echo_response(struct l2cap_instance_s *l2cap, - const uint8_t *data, int len) -{ - uint8_t *pkt; - l2cap_cmd_hdr *hdr; - uint8_t *params; - - pkt = l2cap->signalling_ch.params.sdu_out(&l2cap->signalling_ch.params, - L2CAP_CMD_HDR_SIZE + len); - hdr = (void *) (pkt + 0); - params = (void *) (pkt + L2CAP_CMD_HDR_SIZE); - - hdr->code = L2CAP_ECHO_RSP; - hdr->ident = l2cap->last_id; - hdr->len = cpu_to_le16(len); - - memcpy(params, data, len); - - l2cap->signalling_ch.params.sdu_submit(&l2cap->signalling_ch.params); -} - -static void l2cap_info_response(struct l2cap_instance_s *l2cap, int type, - int result, const uint8_t *data, int len) -{ - uint8_t *pkt; - l2cap_cmd_hdr *hdr; - l2cap_info_rsp *params; - - pkt = l2cap->signalling_ch.params.sdu_out(&l2cap->signalling_ch.params, - L2CAP_CMD_HDR_SIZE + L2CAP_INFO_RSP_SIZE + len); - hdr = (void *) (pkt + 0); - params = (void *) (pkt + L2CAP_CMD_HDR_SIZE); - - hdr->code = L2CAP_INFO_RSP; - hdr->ident = l2cap->last_id; - hdr->len = cpu_to_le16(L2CAP_INFO_RSP_SIZE + len); - - params->type = cpu_to_le16(type); - params->result = cpu_to_le16(result); - if (len) - memcpy(params->data, data, len); - - l2cap->signalling_ch.params.sdu_submit(&l2cap->signalling_ch.params); -} - -static uint8_t *l2cap_bframe_out(struct bt_l2cap_conn_params_s *parm, int len); -static void l2cap_bframe_submit(struct bt_l2cap_conn_params_s *parms); -#if 0 -static uint8_t *l2cap_iframe_out(struct bt_l2cap_conn_params_s *parm, int len); -static void l2cap_iframe_submit(struct bt_l2cap_conn_params_s *parm); -#endif -static void l2cap_bframe_in(struct l2cap_chan_s *ch, uint16_t cid, - const l2cap_hdr *hdr, int len); -static void l2cap_iframe_in(struct l2cap_chan_s *ch, uint16_t cid, - const l2cap_hdr *hdr, int len); - -static int l2cap_cid_new(struct l2cap_instance_s *l2cap) -{ - int i; - - for (i = L2CAP_CID_ALLOC; i < L2CAP_CID_MAX; i ++) - if (!l2cap->cid[i]) - return i; - - return L2CAP_CID_INVALID; -} - -static inline struct bt_l2cap_psm_s *l2cap_psm( - struct bt_l2cap_device_s *device, int psm) -{ - struct bt_l2cap_psm_s *ret = device->first_psm; - - while (ret && ret->psm != psm) - ret = ret->next; - - return ret; -} - -static struct l2cap_chan_s *l2cap_channel_open(struct l2cap_instance_s *l2cap, - int psm, int source_cid) -{ - struct l2cap_chan_s *ch = NULL; - struct bt_l2cap_psm_s *psm_info; - int result, status; - int cid = l2cap_cid_new(l2cap); - - if (cid) { - /* See what the channel is to be used for.. */ - psm_info = l2cap_psm(l2cap->dev, psm); - - if (psm_info) { - /* Device supports this use-case. */ - ch = g_malloc0(sizeof(*ch)); - ch->params.sdu_out = l2cap_bframe_out; - ch->params.sdu_submit = l2cap_bframe_submit; - ch->frame_in = l2cap_bframe_in; - ch->mps = 65536; - ch->min_mtu = MAX(48, psm_info->min_mtu); - ch->params.remote_mtu = MAX(672, ch->min_mtu); - ch->remote_cid = source_cid; - ch->mode = L2CAP_MODE_BASIC; - ch->l2cap = l2cap; - - /* Does it feel like opening yet another channel though? */ - if (!psm_info->new_channel(l2cap->dev, &ch->params)) { - l2cap->cid[cid] = ch; - - result = L2CAP_CR_SUCCESS; - status = L2CAP_CS_NO_INFO; - } else { - g_free(ch); - ch = NULL; - result = L2CAP_CR_NO_MEM; - status = L2CAP_CS_NO_INFO; - } - } else { - result = L2CAP_CR_BAD_PSM; - status = L2CAP_CS_NO_INFO; - } - } else { - result = L2CAP_CR_NO_MEM; - status = L2CAP_CS_NO_INFO; - } - - l2cap_connection_response(l2cap, cid, source_cid, result, status); - - return ch; -} - -static void l2cap_channel_close(struct l2cap_instance_s *l2cap, - int cid, int source_cid) -{ - struct l2cap_chan_s *ch = NULL; - - /* According to Volume 3, section 6.1.1, pg 1048 of BT Core V2.0, a - * connection in CLOSED state still responds with a L2CAP_DisconnectRsp - * message on an L2CAP_DisconnectReq event. */ - if (unlikely(cid < L2CAP_CID_ALLOC)) { - l2cap_command_reject_cid(l2cap, l2cap->last_id, L2CAP_REJ_CID_INVAL, - cid, source_cid); - return; - } - if (likely(cid >= L2CAP_CID_ALLOC && cid < L2CAP_CID_MAX)) - ch = l2cap->cid[cid]; - - if (likely(ch)) { - if (ch->remote_cid != source_cid) { - error_report("%s: Ignoring a Disconnection Request with the " - "invalid SCID %04x.", __func__, source_cid); - return; - } - - l2cap->cid[cid] = NULL; - - ch->params.close(ch->params.opaque); - g_free(ch); - } - - l2cap_disconnection_response(l2cap, cid, source_cid); -} - -static void l2cap_channel_config_null(struct l2cap_instance_s *l2cap, - struct l2cap_chan_s *ch) -{ - l2cap_configuration_request(l2cap, ch->remote_cid, 0, NULL, 0); - ch->config_req_id = l2cap->last_id; - ch->config &= ~L2CAP_CFG_INIT; -} - -static void l2cap_channel_config_req_event(struct l2cap_instance_s *l2cap, - struct l2cap_chan_s *ch) -{ - /* Use all default channel options and terminate negotiation. */ - l2cap_channel_config_null(l2cap, ch); -} - -static int l2cap_channel_config(struct l2cap_instance_s *l2cap, - struct l2cap_chan_s *ch, int flag, - const uint8_t *data, int len) -{ - l2cap_conf_opt *opt; - l2cap_conf_opt_qos *qos; - uint32_t val; - uint8_t rsp[len]; - int result = L2CAP_CONF_SUCCESS; - - data = memcpy(rsp, data, len); - while (len) { - opt = (void *) data; - - if (len < L2CAP_CONF_OPT_SIZE || - len < L2CAP_CONF_OPT_SIZE + opt->len) { - result = L2CAP_CONF_REJECT; - break; - } - data += L2CAP_CONF_OPT_SIZE + opt->len; - len -= L2CAP_CONF_OPT_SIZE + opt->len; - - switch (opt->type & 0x7f) { - case L2CAP_CONF_MTU: - if (opt->len != 2) { - result = L2CAP_CONF_REJECT; - break; - } - - /* MTU */ - val = lduw_le_p(opt->val); - if (val < ch->min_mtu) { - stw_le_p(opt->val, ch->min_mtu); - result = L2CAP_CONF_UNACCEPT; - break; - } - - ch->params.remote_mtu = val; - break; - - case L2CAP_CONF_FLUSH_TO: - if (opt->len != 2) { - result = L2CAP_CONF_REJECT; - break; - } - - /* Flush Timeout */ - val = lduw_le_p(opt->val); - if (val < 0x0001) { - opt->val[0] = 0xff; - opt->val[1] = 0xff; - result = L2CAP_CONF_UNACCEPT; - break; - } - break; - - case L2CAP_CONF_QOS: - if (opt->len != L2CAP_CONF_OPT_QOS_SIZE) { - result = L2CAP_CONF_REJECT; - break; - } - qos = (void *) opt->val; - - /* Flags */ - val = qos->flags; - if (val) { - qos->flags = 0; - result = L2CAP_CONF_UNACCEPT; - } - - /* Service type */ - val = qos->service_type; - if (val != L2CAP_CONF_QOS_BEST_EFFORT && - val != L2CAP_CONF_QOS_NO_TRAFFIC) { - qos->service_type = L2CAP_CONF_QOS_BEST_EFFORT; - result = L2CAP_CONF_UNACCEPT; - } - - if (val != L2CAP_CONF_QOS_NO_TRAFFIC) { - /* XXX: These values should possibly be calculated - * based on LM / baseband properties also. */ - - /* Token rate */ - val = le32_to_cpu(qos->token_rate); - if (val == L2CAP_CONF_QOS_WILDCARD) - qos->token_rate = cpu_to_le32(0x100000); - - /* Token bucket size */ - val = le32_to_cpu(qos->token_bucket_size); - if (val == L2CAP_CONF_QOS_WILDCARD) - qos->token_bucket_size = cpu_to_le32(65500); - - /* Any Peak bandwidth value is correct to return as-is */ - /* Any Access latency value is correct to return as-is */ - /* Any Delay variation value is correct to return as-is */ - } - break; - - case L2CAP_CONF_RFC: - if (opt->len != 9) { - result = L2CAP_CONF_REJECT; - break; - } - - /* Mode */ - val = opt->val[0]; - switch (val) { - case L2CAP_MODE_BASIC: - ch->mode = val; - ch->frame_in = l2cap_bframe_in; - - /* All other parameters shall be ignored */ - break; - - case L2CAP_MODE_RETRANS: - case L2CAP_MODE_FLOWCTL: - ch->mode = val; - ch->frame_in = l2cap_iframe_in; - /* Note: most of these parameters refer to incoming traffic - * so we don't need to save them as long as we can accept - * incoming PDUs at any values of the parameters. */ - - /* TxWindow size */ - val = opt->val[1]; - if (val < 1 || val > 32) { - opt->val[1] = 32; - result = L2CAP_CONF_UNACCEPT; - break; - } - - /* MaxTransmit */ - val = opt->val[2]; - if (val < 1) { - opt->val[2] = 1; - result = L2CAP_CONF_UNACCEPT; - break; - } - - /* Remote Retransmission time-out shouldn't affect local - * operation (?) */ - - /* The Monitor time-out drives the local Monitor timer (?), - * so save the value. */ - val = (opt->val[6] << 8) | opt->val[5]; - if (val < 30) { - opt->val[5] = 100 & 0xff; - opt->val[6] = 100 >> 8; - result = L2CAP_CONF_UNACCEPT; - break; - } - ch->monitor_timeout = val; - l2cap_monitor_timer_update(ch); - - /* MPS */ - val = (opt->val[8] << 8) | opt->val[7]; - if (val < ch->min_mtu) { - opt->val[7] = ch->min_mtu & 0xff; - opt->val[8] = ch->min_mtu >> 8; - result = L2CAP_CONF_UNACCEPT; - break; - } - ch->mps = val; - break; - - default: - result = L2CAP_CONF_UNACCEPT; - break; - } - break; - - default: - if (!(opt->type >> 7)) - result = L2CAP_CONF_UNKNOWN; - break; - } - - if (result != L2CAP_CONF_SUCCESS) - break; /* XXX: should continue? */ - } - - l2cap_configuration_response(l2cap, ch->remote_cid, - flag, result, rsp, len); - - return result == L2CAP_CONF_SUCCESS && !flag; -} - -static void l2cap_channel_config_req_msg(struct l2cap_instance_s *l2cap, - int flag, int cid, const uint8_t *data, int len) -{ - struct l2cap_chan_s *ch; - - if (unlikely(cid >= L2CAP_CID_MAX || !l2cap->cid[cid])) { - l2cap_command_reject_cid(l2cap, l2cap->last_id, L2CAP_REJ_CID_INVAL, - cid, 0x0000); - return; - } - ch = l2cap->cid[cid]; - - /* From OPEN go to WAIT_CONFIG_REQ and from WAIT_CONFIG_REQ_RSP to - * WAIT_CONFIG_REQ_RSP. This is assuming the transition chart for OPEN - * on pg 1053, section 6.1.5, volume 3 of BT Core V2.0 has a mistake - * and on options-acceptable we go back to OPEN and otherwise to - * WAIT_CONFIG_REQ and not the other way. */ - ch->config &= ~L2CAP_CFG_ACC; - - if (l2cap_channel_config(l2cap, ch, flag, data, len)) - /* Go to OPEN or WAIT_CONFIG_RSP */ - ch->config |= L2CAP_CFG_ACC; - - /* TODO: if the incoming traffic flow control or retransmission mode - * changed then we probably need to also generate the - * ConfigureChannel_Req event and set the outgoing traffic to the same - * mode. */ - if (!(ch->config & L2CAP_CFG_INIT) && (ch->config & L2CAP_CFG_ACC) && - !ch->config_req_id) - l2cap_channel_config_req_event(l2cap, ch); -} - -static int l2cap_channel_config_rsp_msg(struct l2cap_instance_s *l2cap, - int result, int flag, int cid, const uint8_t *data, int len) -{ - struct l2cap_chan_s *ch; - - if (unlikely(cid >= L2CAP_CID_MAX || !l2cap->cid[cid])) { - l2cap_command_reject_cid(l2cap, l2cap->last_id, L2CAP_REJ_CID_INVAL, - cid, 0x0000); - return 0; - } - ch = l2cap->cid[cid]; - - if (ch->config_req_id != l2cap->last_id) - return 1; - ch->config_req_id = 0; - - if (result == L2CAP_CONF_SUCCESS) { - if (!flag) - ch->config |= L2CAP_CFG_INIT; - else - l2cap_channel_config_null(l2cap, ch); - } else - /* Retry until we succeed */ - l2cap_channel_config_req_event(l2cap, ch); - - return 0; -} - -static void l2cap_channel_open_req_msg(struct l2cap_instance_s *l2cap, - int psm, int source_cid) -{ - struct l2cap_chan_s *ch = l2cap_channel_open(l2cap, psm, source_cid); - - if (!ch) - return; - - /* Optional */ - if (!(ch->config & L2CAP_CFG_INIT) && !ch->config_req_id) - l2cap_channel_config_req_event(l2cap, ch); -} - -static void l2cap_info(struct l2cap_instance_s *l2cap, int type) -{ - uint8_t data[4]; - int len = 0; - int result = L2CAP_IR_SUCCESS; - - switch (type) { - case L2CAP_IT_CL_MTU: - data[len ++] = l2cap->group_ch.mps & 0xff; - data[len ++] = l2cap->group_ch.mps >> 8; - break; - - case L2CAP_IT_FEAT_MASK: - /* (Prematurely) report Flow control and Retransmission modes. */ - data[len ++] = 0x03; - data[len ++] = 0x00; - data[len ++] = 0x00; - data[len ++] = 0x00; - break; - - default: - result = L2CAP_IR_NOTSUPP; - } - - l2cap_info_response(l2cap, type, result, data, len); -} - -static void l2cap_command(struct l2cap_instance_s *l2cap, int code, int id, - const uint8_t *params, int len) -{ - int err; - -#if 0 - /* TODO: do the IDs really have to be in sequence? */ - if (!id || (id != l2cap->last_id && id != l2cap->next_id)) { - error_report("%s: out of sequence command packet ignored.", - __func__); - return; - } -#else - l2cap->next_id = id; -#endif - if (id == l2cap->next_id) { - l2cap->last_id = l2cap->next_id; - l2cap->next_id = l2cap->next_id == 255 ? 1 : l2cap->next_id + 1; - } else { - /* TODO: Need to re-send the same response, without re-executing - * the corresponding command! */ - } - - switch (code) { - case L2CAP_COMMAND_REJ: - if (unlikely(len != 2 && len != 4 && len != 6)) { - err = L2CAP_REJ_CMD_NOT_UNDERSTOOD; - goto reject; - } - - /* We never issue commands other than Command Reject currently. */ - error_report("%s: stray Command Reject (%02x, %04x) " - "packet, ignoring.", __func__, id, - le16_to_cpu(((l2cap_cmd_rej *) params)->reason)); - break; - - case L2CAP_CONN_REQ: - if (unlikely(len != L2CAP_CONN_REQ_SIZE)) { - err = L2CAP_REJ_CMD_NOT_UNDERSTOOD; - goto reject; - } - - l2cap_channel_open_req_msg(l2cap, - le16_to_cpu(((l2cap_conn_req *) params)->psm), - le16_to_cpu(((l2cap_conn_req *) params)->scid)); - break; - - case L2CAP_CONN_RSP: - if (unlikely(len != L2CAP_CONN_RSP_SIZE)) { - err = L2CAP_REJ_CMD_NOT_UNDERSTOOD; - goto reject; - } - - /* We never issue Connection Requests currently. TODO */ - error_report("%s: unexpected Connection Response (%02x) " - "packet, ignoring.", __func__, id); - break; - - case L2CAP_CONF_REQ: - if (unlikely(len < L2CAP_CONF_REQ_SIZE(0))) { - err = L2CAP_REJ_CMD_NOT_UNDERSTOOD; - goto reject; - } - - l2cap_channel_config_req_msg(l2cap, - le16_to_cpu(((l2cap_conf_req *) params)->flags) & 1, - le16_to_cpu(((l2cap_conf_req *) params)->dcid), - ((l2cap_conf_req *) params)->data, - len - L2CAP_CONF_REQ_SIZE(0)); - break; - - case L2CAP_CONF_RSP: - if (unlikely(len < L2CAP_CONF_RSP_SIZE(0))) { - err = L2CAP_REJ_CMD_NOT_UNDERSTOOD; - goto reject; - } - - if (l2cap_channel_config_rsp_msg(l2cap, - le16_to_cpu(((l2cap_conf_rsp *) params)->result), - le16_to_cpu(((l2cap_conf_rsp *) params)->flags) & 1, - le16_to_cpu(((l2cap_conf_rsp *) params)->scid), - ((l2cap_conf_rsp *) params)->data, - len - L2CAP_CONF_RSP_SIZE(0))) - error_report("%s: unexpected Configure Response (%02x) " - "packet, ignoring.", __func__, id); - break; - - case L2CAP_DISCONN_REQ: - if (unlikely(len != L2CAP_DISCONN_REQ_SIZE)) { - err = L2CAP_REJ_CMD_NOT_UNDERSTOOD; - goto reject; - } - - l2cap_channel_close(l2cap, - le16_to_cpu(((l2cap_disconn_req *) params)->dcid), - le16_to_cpu(((l2cap_disconn_req *) params)->scid)); - break; - - case L2CAP_DISCONN_RSP: - if (unlikely(len != L2CAP_DISCONN_RSP_SIZE)) { - err = L2CAP_REJ_CMD_NOT_UNDERSTOOD; - goto reject; - } - - /* We never issue Disconnection Requests currently. TODO */ - error_report("%s: unexpected Disconnection Response (%02x) " - "packet, ignoring.", __func__, id); - break; - - case L2CAP_ECHO_REQ: - l2cap_echo_response(l2cap, params, len); - break; - - case L2CAP_ECHO_RSP: - /* We never issue Echo Requests currently. TODO */ - error_report("%s: unexpected Echo Response (%02x) " - "packet, ignoring.", __func__, id); - break; - - case L2CAP_INFO_REQ: - if (unlikely(len != L2CAP_INFO_REQ_SIZE)) { - err = L2CAP_REJ_CMD_NOT_UNDERSTOOD; - goto reject; - } - - l2cap_info(l2cap, le16_to_cpu(((l2cap_info_req *) params)->type)); - break; - - case L2CAP_INFO_RSP: - if (unlikely(len != L2CAP_INFO_RSP_SIZE)) { - err = L2CAP_REJ_CMD_NOT_UNDERSTOOD; - goto reject; - } - - /* We never issue Information Requests currently. TODO */ - error_report("%s: unexpected Information Response (%02x) " - "packet, ignoring.", __func__, id); - break; - - default: - err = L2CAP_REJ_CMD_NOT_UNDERSTOOD; - reject: - l2cap_command_reject(l2cap, id, err, 0, 0); - break; - } -} - -static void l2cap_rexmit_enable(struct l2cap_chan_s *ch, int enable) -{ - ch->rexmit = enable; - - l2cap_retransmission_timer_update(ch); - l2cap_monitor_timer_update(ch); -} - -/* Command frame SDU */ -static void l2cap_cframe_in(void *opaque, const uint8_t *data, int len) -{ - struct l2cap_instance_s *l2cap = opaque; - const l2cap_cmd_hdr *hdr; - int clen; - - while (len) { - hdr = (void *) data; - if (len < L2CAP_CMD_HDR_SIZE) - /* TODO: signal an error */ - return; - len -= L2CAP_CMD_HDR_SIZE; - data += L2CAP_CMD_HDR_SIZE; - - clen = le16_to_cpu(hdr->len); - if (len < clen) { - l2cap_command_reject(l2cap, hdr->ident, - L2CAP_REJ_CMD_NOT_UNDERSTOOD, 0, 0); - break; - } - - l2cap_command(l2cap, hdr->code, hdr->ident, data, clen); - len -= clen; - data += clen; - } -} - -/* Group frame SDU */ -static void l2cap_gframe_in(void *opaque, const uint8_t *data, int len) -{ -} - -/* Supervisory frame */ -static void l2cap_sframe_in(struct l2cap_chan_s *ch, uint16_t ctrl) -{ -} - -/* Basic L2CAP mode Information frame */ -static void l2cap_bframe_in(struct l2cap_chan_s *ch, uint16_t cid, - const l2cap_hdr *hdr, int len) -{ - /* We have a full SDU, no further processing */ - ch->params.sdu_in(ch->params.opaque, hdr->data, len); -} - -/* Flow Control and Retransmission mode frame */ -static void l2cap_iframe_in(struct l2cap_chan_s *ch, uint16_t cid, - const l2cap_hdr *hdr, int len) -{ - uint16_t fcs = lduw_le_p(hdr->data + len - 2); - - if (len < 4) - goto len_error; - if (l2cap_fcs16((const uint8_t *) hdr, L2CAP_HDR_SIZE + len - 2) != fcs) - goto fcs_error; - - if ((hdr->data[0] >> 7) == ch->rexmit) - l2cap_rexmit_enable(ch, !(hdr->data[0] >> 7)); - - if (hdr->data[0] & 1) { - if (len != 4) { - /* TODO: Signal an error? */ - return; - } - l2cap_sframe_in(ch, lduw_le_p(hdr->data)); - return; - } - - switch (hdr->data[1] >> 6) { /* SAR */ - case L2CAP_SAR_NO_SEG: - if (ch->len_total) - goto seg_error; - if (len - 4 > ch->mps) - goto len_error; - - ch->params.sdu_in(ch->params.opaque, hdr->data + 2, len - 4); - break; - - case L2CAP_SAR_START: - if (ch->len_total || len < 6) - goto seg_error; - if (len - 6 > ch->mps) - goto len_error; - - ch->len_total = lduw_le_p(hdr->data + 2); - if (len >= 6 + ch->len_total) - goto seg_error; - - ch->len_cur = len - 6; - memcpy(ch->sdu, hdr->data + 4, ch->len_cur); - break; - - case L2CAP_SAR_END: - if (!ch->len_total || ch->len_cur + len - 4 < ch->len_total) - goto seg_error; - if (len - 4 > ch->mps) - goto len_error; - - memcpy(ch->sdu + ch->len_cur, hdr->data + 2, len - 4); - ch->params.sdu_in(ch->params.opaque, ch->sdu, ch->len_total); - break; - - case L2CAP_SAR_CONT: - if (!ch->len_total || ch->len_cur + len - 4 >= ch->len_total) - goto seg_error; - if (len - 4 > ch->mps) - goto len_error; - - memcpy(ch->sdu + ch->len_cur, hdr->data + 2, len - 4); - ch->len_cur += len - 4; - break; - - seg_error: - len_error: /* TODO */ - fcs_error: /* TODO */ - ch->len_cur = 0; - ch->len_total = 0; - break; - } -} - -static void l2cap_frame_in(struct l2cap_instance_s *l2cap, - const l2cap_hdr *frame) -{ - uint16_t cid = le16_to_cpu(frame->cid); - uint16_t len = le16_to_cpu(frame->len); - - if (unlikely(cid >= L2CAP_CID_MAX || !l2cap->cid[cid])) { - error_report("%s: frame addressed to a non-existent L2CAP " - "channel %04x received.", __func__, cid); - return; - } - - l2cap->cid[cid]->frame_in(l2cap->cid[cid], cid, frame, len); -} - -/* "Recombination" */ -static void l2cap_pdu_in(struct l2cap_instance_s *l2cap, - const uint8_t *data, int len) -{ - const l2cap_hdr *hdr = (void *) l2cap->frame_in; - - if (unlikely(len + l2cap->frame_in_len > sizeof(l2cap->frame_in))) { - if (l2cap->frame_in_len < sizeof(l2cap->frame_in)) { - memcpy(l2cap->frame_in + l2cap->frame_in_len, data, - sizeof(l2cap->frame_in) - l2cap->frame_in_len); - l2cap->frame_in_len = sizeof(l2cap->frame_in); - /* TODO: truncate */ - l2cap_frame_in(l2cap, hdr); - } - - return; - } - - memcpy(l2cap->frame_in + l2cap->frame_in_len, data, len); - l2cap->frame_in_len += len; - - if (len >= L2CAP_HDR_SIZE) - if (len >= L2CAP_HDR_SIZE + le16_to_cpu(hdr->len)) - l2cap_frame_in(l2cap, hdr); - /* There is never a start of a new PDU in the same ACL packet, so - * no need to memmove the remaining payload and loop. */ -} - -static inline uint8_t *l2cap_pdu_out(struct l2cap_instance_s *l2cap, - uint16_t cid, uint16_t len) -{ - l2cap_hdr *hdr = (void *) l2cap->frame_out; - - l2cap->frame_out_len = len + L2CAP_HDR_SIZE; - - hdr->cid = cpu_to_le16(cid); - hdr->len = cpu_to_le16(len); - - return l2cap->frame_out + L2CAP_HDR_SIZE; -} - -static inline void l2cap_pdu_submit(struct l2cap_instance_s *l2cap) -{ - /* TODO: Fragmentation */ - (l2cap->role ? - l2cap->link->slave->lmp_acl_data : l2cap->link->host->lmp_acl_resp) - (l2cap->link, l2cap->frame_out, 1, l2cap->frame_out_len); -} - -static uint8_t *l2cap_bframe_out(struct bt_l2cap_conn_params_s *parm, int len) -{ - struct l2cap_chan_s *chan = (struct l2cap_chan_s *) parm; - - if (len > chan->params.remote_mtu) { - error_report("%s: B-Frame for CID %04x longer than %i octets.", - __func__, - chan->remote_cid, chan->params.remote_mtu); - exit(-1); - } - - return l2cap_pdu_out(chan->l2cap, chan->remote_cid, len); -} - -static void l2cap_bframe_submit(struct bt_l2cap_conn_params_s *parms) -{ - struct l2cap_chan_s *chan = (struct l2cap_chan_s *) parms; - - l2cap_pdu_submit(chan->l2cap); -} - -#if 0 -/* Stub: Only used if an emulated device requests outgoing flow control */ -static uint8_t *l2cap_iframe_out(struct bt_l2cap_conn_params_s *parm, int len) -{ - struct l2cap_chan_s *chan = (struct l2cap_chan_s *) parm; - - if (len > chan->params.remote_mtu) { - /* TODO: slice into segments and queue each segment as a separate - * I-Frame in a FIFO of I-Frames, local to the CID. */ - } else { - /* TODO: add to the FIFO of I-Frames, local to the CID. */ - /* Possibly we need to return a pointer to a contiguous buffer - * for now and then memcpy from it into FIFOs in l2cap_iframe_submit - * while segmenting at the same time. */ - } - return 0; -} - -static void l2cap_iframe_submit(struct bt_l2cap_conn_params_s *parm) -{ - /* TODO: If flow control indicates clear to send, start submitting the - * invidual I-Frames from the FIFO, but don't remove them from there. - * Kick the appropriate timer until we get an S-Frame, and only then - * remove from FIFO or resubmit and re-kick the timer if the timer - * expired. */ -} -#endif - -static void l2cap_init(struct l2cap_instance_s *l2cap, - struct bt_link_s *link, int role) -{ - l2cap->link = link; - l2cap->role = role; - l2cap->dev = (struct bt_l2cap_device_s *) - (role ? link->host : link->slave); - - l2cap->next_id = 1; - - /* Establish the signalling channel */ - l2cap->signalling_ch.params.sdu_in = l2cap_cframe_in; - l2cap->signalling_ch.params.sdu_out = l2cap_bframe_out; - l2cap->signalling_ch.params.sdu_submit = l2cap_bframe_submit; - l2cap->signalling_ch.params.opaque = l2cap; - l2cap->signalling_ch.params.remote_mtu = 48; - l2cap->signalling_ch.remote_cid = L2CAP_CID_SIGNALLING; - l2cap->signalling_ch.frame_in = l2cap_bframe_in; - l2cap->signalling_ch.mps = 65536; - l2cap->signalling_ch.min_mtu = 48; - l2cap->signalling_ch.mode = L2CAP_MODE_BASIC; - l2cap->signalling_ch.l2cap = l2cap; - l2cap->cid[L2CAP_CID_SIGNALLING] = &l2cap->signalling_ch; - - /* Establish the connection-less data channel */ - l2cap->group_ch.params.sdu_in = l2cap_gframe_in; - l2cap->group_ch.params.opaque = l2cap; - l2cap->group_ch.frame_in = l2cap_bframe_in; - l2cap->group_ch.mps = 65533; - l2cap->group_ch.l2cap = l2cap; - l2cap->group_ch.remote_cid = L2CAP_CID_INVALID; - l2cap->cid[L2CAP_CID_GROUP] = &l2cap->group_ch; -} - -static void l2cap_teardown(struct l2cap_instance_s *l2cap, int send_disconnect) -{ - int cid; - - /* Don't send DISCONNECT if we are currently handling a DISCONNECT - * sent from the other side. */ - if (send_disconnect) { - if (l2cap->role) - l2cap->dev->device.lmp_disconnect_slave(l2cap->link); - /* l2cap->link is invalid from now on. */ - else - l2cap->dev->device.lmp_disconnect_master(l2cap->link); - } - - for (cid = L2CAP_CID_ALLOC; cid < L2CAP_CID_MAX; cid ++) - if (l2cap->cid[cid]) { - l2cap->cid[cid]->params.close(l2cap->cid[cid]->params.opaque); - g_free(l2cap->cid[cid]); - } - - if (l2cap->role) - g_free(l2cap); - else - g_free(l2cap->link); -} - -/* L2CAP glue to lower layers in bluetooth stack (LMP) */ - -static void l2cap_lmp_connection_request(struct bt_link_s *link) -{ - struct bt_l2cap_device_s *dev = (struct bt_l2cap_device_s *) link->slave; - struct slave_l2cap_instance_s *l2cap; - - /* Always accept - we only get called if (dev->device->page_scan). */ - - l2cap = g_malloc0(sizeof(struct slave_l2cap_instance_s)); - l2cap->link.slave = &dev->device; - l2cap->link.host = link->host; - l2cap_init(&l2cap->l2cap, &l2cap->link, 0); - - /* Always at the end */ - link->host->reject_reason = 0; - link->host->lmp_connection_complete(&l2cap->link); -} - -/* Stub */ -static void l2cap_lmp_connection_complete(struct bt_link_s *link) -{ - struct bt_l2cap_device_s *dev = (struct bt_l2cap_device_s *) link->host; - struct l2cap_instance_s *l2cap; - - if (dev->device.reject_reason) { - /* Signal to upper layer */ - return; - } - - l2cap = g_malloc0(sizeof(struct l2cap_instance_s)); - l2cap_init(l2cap, link, 1); - - link->acl_mode = acl_active; - - /* Signal to upper layer */ -} - -/* Stub */ -static void l2cap_lmp_disconnect_host(struct bt_link_s *link) -{ - struct bt_l2cap_device_s *dev = (struct bt_l2cap_device_s *) link->host; - struct l2cap_instance_s *l2cap = - /* TODO: Retrieve from upper layer */ (void *) dev; - - /* Signal to upper layer */ - - l2cap_teardown(l2cap, 0); -} - -static void l2cap_lmp_disconnect_slave(struct bt_link_s *link) -{ - struct slave_l2cap_instance_s *l2cap = - (struct slave_l2cap_instance_s *) link; - - l2cap_teardown(&l2cap->l2cap, 0); -} - -static void l2cap_lmp_acl_data_slave(struct bt_link_s *link, - const uint8_t *data, int start, int len) -{ - struct slave_l2cap_instance_s *l2cap = - (struct slave_l2cap_instance_s *) link; - - if (start) - l2cap->l2cap.frame_in_len = 0; - - l2cap_pdu_in(&l2cap->l2cap, data, len); -} - -/* Stub */ -static void l2cap_lmp_acl_data_host(struct bt_link_s *link, - const uint8_t *data, int start, int len) -{ - struct bt_l2cap_device_s *dev = (struct bt_l2cap_device_s *) link->host; - struct l2cap_instance_s *l2cap = - /* TODO: Retrieve from upper layer */ (void *) dev; - - if (start) - l2cap->frame_in_len = 0; - - l2cap_pdu_in(l2cap, data, len); -} - -static void l2cap_dummy_destroy(struct bt_device_s *dev) -{ - struct bt_l2cap_device_s *l2cap_dev = (struct bt_l2cap_device_s *) dev; - - bt_l2cap_device_done(l2cap_dev); -} - -void bt_l2cap_device_init(struct bt_l2cap_device_s *dev, - struct bt_scatternet_s *net) -{ - bt_device_init(&dev->device, net); - - dev->device.lmp_connection_request = l2cap_lmp_connection_request; - dev->device.lmp_connection_complete = l2cap_lmp_connection_complete; - dev->device.lmp_disconnect_master = l2cap_lmp_disconnect_host; - dev->device.lmp_disconnect_slave = l2cap_lmp_disconnect_slave; - dev->device.lmp_acl_data = l2cap_lmp_acl_data_slave; - dev->device.lmp_acl_resp = l2cap_lmp_acl_data_host; - - dev->device.handle_destroy = l2cap_dummy_destroy; -} - -void bt_l2cap_device_done(struct bt_l2cap_device_s *dev) -{ - bt_device_done(&dev->device); - - /* Should keep a list of all instances and go through it and - * invoke l2cap_teardown() for each. */ -} - -void bt_l2cap_psm_register(struct bt_l2cap_device_s *dev, int psm, int min_mtu, - int (*new_channel)(struct bt_l2cap_device_s *dev, - struct bt_l2cap_conn_params_s *params)) -{ - struct bt_l2cap_psm_s *new_psm = l2cap_psm(dev, psm); - - if (new_psm) { - error_report("%s: PSM %04x already registered for device `%s'.", - __func__, psm, dev->device.lmp_name); - exit(-1); - } - - new_psm = g_malloc0(sizeof(*new_psm)); - new_psm->psm = psm; - new_psm->min_mtu = min_mtu; - new_psm->new_channel = new_channel; - new_psm->next = dev->first_psm; - dev->first_psm = new_psm; -} diff --git a/hw/bt/sdp.c b/hw/bt/sdp.c deleted file mode 100644 index 2860d76c85..0000000000 --- a/hw/bt/sdp.c +++ /dev/null @@ -1,989 +0,0 @@ -/* - * Service Discover Protocol server for QEMU L2CAP devices - * - * Copyright (C) 2008 Andrzej Zaborowski - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#include "qemu/osdep.h" -#include "qemu/error-report.h" -#include "qemu/host-utils.h" -#include "hw/bt.h" - -struct bt_l2cap_sdp_state_s { - struct bt_l2cap_conn_params_s *channel; - - struct sdp_service_record_s { - int match; - - int *uuid; - int uuids; - struct sdp_service_attribute_s { - int match; - - int attribute_id; - int len; - void *pair; - } *attribute_list; - int attributes; - } *service_list; - int services; -}; - -static ssize_t sdp_datalen(const uint8_t **element, ssize_t *left) -{ - uint32_t len = *(*element) ++ & SDP_DSIZE_MASK; - - if (!*left) - return -1; - (*left) --; - - if (len < SDP_DSIZE_NEXT1) - return 1 << len; - else if (len == SDP_DSIZE_NEXT1) { - if (*left < 1) - return -1; - (*left) --; - - return *(*element) ++; - } else if (len == SDP_DSIZE_NEXT2) { - if (*left < 2) - return -1; - (*left) -= 2; - - len = (*(*element) ++) << 8; - return len | (*(*element) ++); - } else { - if (*left < 4) - return -1; - (*left) -= 4; - - len = (*(*element) ++) << 24; - len |= (*(*element) ++) << 16; - len |= (*(*element) ++) << 8; - return len | (*(*element) ++); - } -} - -static const uint8_t bt_base_uuid[12] = { - 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb, -}; - -static int sdp_uuid_match(struct sdp_service_record_s *record, - const uint8_t *uuid, ssize_t datalen) -{ - int *lo, hi, val; - - if (datalen == 16 || datalen == 4) { - if (datalen == 16 && memcmp(uuid + 4, bt_base_uuid, 12)) - return 0; - - if (uuid[0] | uuid[1]) - return 0; - uuid += 2; - } - - val = (uuid[0] << 8) | uuid[1]; - lo = record->uuid; - hi = record->uuids; - while (hi >>= 1) - if (lo[hi] <= val) - lo += hi; - - return *lo == val; -} - -#define CONTINUATION_PARAM_SIZE (1 + sizeof(int)) -#define MAX_PDU_OUT_SIZE 96 /* Arbitrary */ -#define PDU_HEADER_SIZE 5 -#define MAX_RSP_PARAM_SIZE (MAX_PDU_OUT_SIZE - PDU_HEADER_SIZE - \ - CONTINUATION_PARAM_SIZE) - -static int sdp_svc_match(struct bt_l2cap_sdp_state_s *sdp, - const uint8_t **req, ssize_t *len) -{ - size_t datalen; - int i; - - if ((**req & ~SDP_DSIZE_MASK) != SDP_DTYPE_UUID) - return 1; - - datalen = sdp_datalen(req, len); - if (datalen != 2 && datalen != 4 && datalen != 16) - return 1; - - for (i = 0; i < sdp->services; i ++) - if (sdp_uuid_match(&sdp->service_list[i], *req, datalen)) - sdp->service_list[i].match = 1; - - (*req) += datalen; - (*len) -= datalen; - - return 0; -} - -static ssize_t sdp_svc_search(struct bt_l2cap_sdp_state_s *sdp, - uint8_t *rsp, const uint8_t *req, ssize_t len) -{ - ssize_t seqlen; - int i, count, start, end, max; - int32_t handle; - - /* Perform the search */ - for (i = 0; i < sdp->services; i ++) - sdp->service_list[i].match = 0; - - if (len < 1) - return -SDP_INVALID_SYNTAX; - if ((*req & ~SDP_DSIZE_MASK) == SDP_DTYPE_SEQ) { - seqlen = sdp_datalen(&req, &len); - if (seqlen < 3 || len < seqlen) - return -SDP_INVALID_SYNTAX; - len -= seqlen; - while (seqlen) - if (sdp_svc_match(sdp, &req, &seqlen)) - return -SDP_INVALID_SYNTAX; - } else { - if (sdp_svc_match(sdp, &req, &len)) { - return -SDP_INVALID_SYNTAX; - } - } - - if (len < 3) - return -SDP_INVALID_SYNTAX; - max = (req[0] << 8) | req[1]; - req += 2; - len -= 2; - - if (*req) { - if (len <= sizeof(int)) - return -SDP_INVALID_SYNTAX; - len -= sizeof(int); - memcpy(&start, req + 1, sizeof(int)); - } else - start = 0; - - if (len > 1) - return -SDP_INVALID_SYNTAX; - - /* Output the results */ - len = 4; - count = 0; - end = start; - for (i = 0; i < sdp->services; i ++) - if (sdp->service_list[i].match) { - if (count >= start && count < max && len + 4 < MAX_RSP_PARAM_SIZE) { - handle = i; - memcpy(rsp + len, &handle, 4); - len += 4; - end = count + 1; - } - - count ++; - } - - rsp[0] = count >> 8; - rsp[1] = count & 0xff; - rsp[2] = (end - start) >> 8; - rsp[3] = (end - start) & 0xff; - - if (end < count) { - rsp[len ++] = sizeof(int); - memcpy(rsp + len, &end, sizeof(int)); - len += 4; - } else - rsp[len ++] = 0; - - return len; -} - -static int sdp_attr_match(struct sdp_service_record_s *record, - const uint8_t **req, ssize_t *len) -{ - int i, start, end; - - if (**req == (SDP_DTYPE_UINT | SDP_DSIZE_2)) { - (*req) ++; - if (*len < 3) - return 1; - - start = (*(*req) ++) << 8; - start |= *(*req) ++; - end = start; - *len -= 3; - } else if (**req == (SDP_DTYPE_UINT | SDP_DSIZE_4)) { - (*req) ++; - if (*len < 5) - return 1; - - start = (*(*req) ++) << 8; - start |= *(*req) ++; - end = (*(*req) ++) << 8; - end |= *(*req) ++; - *len -= 5; - } else - return 1; - - for (i = 0; i < record->attributes; i ++) - if (record->attribute_list[i].attribute_id >= start && - record->attribute_list[i].attribute_id <= end) - record->attribute_list[i].match = 1; - - return 0; -} - -static ssize_t sdp_attr_get(struct bt_l2cap_sdp_state_s *sdp, - uint8_t *rsp, const uint8_t *req, ssize_t len) -{ - ssize_t seqlen; - int i, start, end, max; - int32_t handle; - struct sdp_service_record_s *record; - uint8_t *lst; - - /* Perform the search */ - if (len < 7) - return -SDP_INVALID_SYNTAX; - memcpy(&handle, req, 4); - req += 4; - len -= 4; - - if (handle < 0 || handle > sdp->services) - return -SDP_INVALID_RECORD_HANDLE; - record = &sdp->service_list[handle]; - - for (i = 0; i < record->attributes; i ++) - record->attribute_list[i].match = 0; - - max = (req[0] << 8) | req[1]; - req += 2; - len -= 2; - if (max < 0x0007) - return -SDP_INVALID_SYNTAX; - - if ((*req & ~SDP_DSIZE_MASK) == SDP_DTYPE_SEQ) { - seqlen = sdp_datalen(&req, &len); - if (seqlen < 3 || len < seqlen) - return -SDP_INVALID_SYNTAX; - len -= seqlen; - - while (seqlen) - if (sdp_attr_match(record, &req, &seqlen)) - return -SDP_INVALID_SYNTAX; - } else { - if (sdp_attr_match(record, &req, &len)) { - return -SDP_INVALID_SYNTAX; - } - } - - if (len < 1) - return -SDP_INVALID_SYNTAX; - - if (*req) { - if (len <= sizeof(int)) - return -SDP_INVALID_SYNTAX; - len -= sizeof(int); - memcpy(&start, req + 1, sizeof(int)); - } else - start = 0; - - if (len > 1) - return -SDP_INVALID_SYNTAX; - - /* Output the results */ - lst = rsp + 2; - max = MIN(max, MAX_RSP_PARAM_SIZE); - len = 3 - start; - end = 0; - for (i = 0; i < record->attributes; i ++) - if (record->attribute_list[i].match) { - if (len >= 0 && len + record->attribute_list[i].len < max) { - memcpy(lst + len, record->attribute_list[i].pair, - record->attribute_list[i].len); - end = len + record->attribute_list[i].len; - } - len += record->attribute_list[i].len; - } - if (0 >= start) { - lst[0] = SDP_DTYPE_SEQ | SDP_DSIZE_NEXT2; - lst[1] = (len + start - 3) >> 8; - lst[2] = (len + start - 3) & 0xff; - } - - rsp[0] = end >> 8; - rsp[1] = end & 0xff; - - if (end < len) { - len = end + start; - lst[end ++] = sizeof(int); - memcpy(lst + end, &len, sizeof(int)); - end += sizeof(int); - } else - lst[end ++] = 0; - - return end + 2; -} - -static int sdp_svc_attr_match(struct bt_l2cap_sdp_state_s *sdp, - const uint8_t **req, ssize_t *len) -{ - int i, j, start, end; - struct sdp_service_record_s *record; - - if (**req == (SDP_DTYPE_UINT | SDP_DSIZE_2)) { - (*req) ++; - if (*len < 3) - return 1; - - start = (*(*req) ++) << 8; - start |= *(*req) ++; - end = start; - *len -= 3; - } else if (**req == (SDP_DTYPE_UINT | SDP_DSIZE_4)) { - (*req) ++; - if (*len < 5) - return 1; - - start = (*(*req) ++) << 8; - start |= *(*req) ++; - end = (*(*req) ++) << 8; - end |= *(*req) ++; - *len -= 5; - } else - return 1; - - for (i = 0; i < sdp->services; i ++) - if ((record = &sdp->service_list[i])->match) - for (j = 0; j < record->attributes; j ++) - if (record->attribute_list[j].attribute_id >= start && - record->attribute_list[j].attribute_id <= end) - record->attribute_list[j].match = 1; - - return 0; -} - -static ssize_t sdp_svc_search_attr_get(struct bt_l2cap_sdp_state_s *sdp, - uint8_t *rsp, const uint8_t *req, ssize_t len) -{ - ssize_t seqlen; - int i, j, start, end, max; - struct sdp_service_record_s *record; - uint8_t *lst; - - /* Perform the search */ - for (i = 0; i < sdp->services; i ++) { - sdp->service_list[i].match = 0; - for (j = 0; j < sdp->service_list[i].attributes; j ++) - sdp->service_list[i].attribute_list[j].match = 0; - } - - if (len < 1) - return -SDP_INVALID_SYNTAX; - if ((*req & ~SDP_DSIZE_MASK) == SDP_DTYPE_SEQ) { - seqlen = sdp_datalen(&req, &len); - if (seqlen < 3 || len < seqlen) - return -SDP_INVALID_SYNTAX; - len -= seqlen; - - while (seqlen) - if (sdp_svc_match(sdp, &req, &seqlen)) - return -SDP_INVALID_SYNTAX; - } else { - if (sdp_svc_match(sdp, &req, &len)) { - return -SDP_INVALID_SYNTAX; - } - } - - if (len < 3) - return -SDP_INVALID_SYNTAX; - max = (req[0] << 8) | req[1]; - req += 2; - len -= 2; - if (max < 0x0007) - return -SDP_INVALID_SYNTAX; - - if ((*req & ~SDP_DSIZE_MASK) == SDP_DTYPE_SEQ) { - seqlen = sdp_datalen(&req, &len); - if (seqlen < 3 || len < seqlen) - return -SDP_INVALID_SYNTAX; - len -= seqlen; - - while (seqlen) - if (sdp_svc_attr_match(sdp, &req, &seqlen)) - return -SDP_INVALID_SYNTAX; - } else { - if (sdp_svc_attr_match(sdp, &req, &len)) { - return -SDP_INVALID_SYNTAX; - } - } - - if (len < 1) - return -SDP_INVALID_SYNTAX; - - if (*req) { - if (len <= sizeof(int)) - return -SDP_INVALID_SYNTAX; - len -= sizeof(int); - memcpy(&start, req + 1, sizeof(int)); - } else - start = 0; - - if (len > 1) - return -SDP_INVALID_SYNTAX; - - /* Output the results */ - /* This assumes empty attribute lists are never to be returned even - * for matching Service Records. In practice this shouldn't happen - * as the requestor will usually include the always present - * ServiceRecordHandle AttributeID in AttributeIDList. */ - lst = rsp + 2; - max = MIN(max, MAX_RSP_PARAM_SIZE); - len = 3 - start; - end = 0; - for (i = 0; i < sdp->services; i ++) - if ((record = &sdp->service_list[i])->match) { - len += 3; - seqlen = len; - for (j = 0; j < record->attributes; j ++) - if (record->attribute_list[j].match) { - if (len >= 0) - if (len + record->attribute_list[j].len < max) { - memcpy(lst + len, record->attribute_list[j].pair, - record->attribute_list[j].len); - end = len + record->attribute_list[j].len; - } - len += record->attribute_list[j].len; - } - if (seqlen == len) - len -= 3; - else if (seqlen >= 3 && seqlen < max) { - lst[seqlen - 3] = SDP_DTYPE_SEQ | SDP_DSIZE_NEXT2; - lst[seqlen - 2] = (len - seqlen) >> 8; - lst[seqlen - 1] = (len - seqlen) & 0xff; - } - } - if (len == 3 - start) - len -= 3; - else if (0 >= start) { - lst[0] = SDP_DTYPE_SEQ | SDP_DSIZE_NEXT2; - lst[1] = (len + start - 3) >> 8; - lst[2] = (len + start - 3) & 0xff; - } - - rsp[0] = end >> 8; - rsp[1] = end & 0xff; - - if (end < len) { - len = end + start; - lst[end ++] = sizeof(int); - memcpy(lst + end, &len, sizeof(int)); - end += sizeof(int); - } else - lst[end ++] = 0; - - return end + 2; -} - -static void bt_l2cap_sdp_sdu_in(void *opaque, const uint8_t *data, int len) -{ - struct bt_l2cap_sdp_state_s *sdp = opaque; - enum bt_sdp_cmd pdu_id; - uint8_t rsp[MAX_PDU_OUT_SIZE - PDU_HEADER_SIZE], *sdu_out; - int transaction_id, plen; - int err = 0; - int rsp_len = 0; - - if (len < 5) { - error_report("%s: short SDP PDU (%iB).", __func__, len); - return; - } - - pdu_id = *data ++; - transaction_id = (data[0] << 8) | data[1]; - plen = (data[2] << 8) | data[3]; - data += 4; - len -= 5; - - if (len != plen) { - error_report("%s: wrong SDP PDU length (%iB != %iB).", - __func__, plen, len); - err = SDP_INVALID_PDU_SIZE; - goto respond; - } - - switch (pdu_id) { - case SDP_SVC_SEARCH_REQ: - rsp_len = sdp_svc_search(sdp, rsp, data, len); - pdu_id = SDP_SVC_SEARCH_RSP; - break; - - case SDP_SVC_ATTR_REQ: - rsp_len = sdp_attr_get(sdp, rsp, data, len); - pdu_id = SDP_SVC_ATTR_RSP; - break; - - case SDP_SVC_SEARCH_ATTR_REQ: - rsp_len = sdp_svc_search_attr_get(sdp, rsp, data, len); - pdu_id = SDP_SVC_SEARCH_ATTR_RSP; - break; - - case SDP_ERROR_RSP: - case SDP_SVC_ATTR_RSP: - case SDP_SVC_SEARCH_RSP: - case SDP_SVC_SEARCH_ATTR_RSP: - default: - error_report("%s: unexpected SDP PDU ID %02x.", - __func__, pdu_id); - err = SDP_INVALID_SYNTAX; - break; - } - - if (rsp_len < 0) { - err = -rsp_len; - rsp_len = 0; - } - -respond: - if (err) { - pdu_id = SDP_ERROR_RSP; - rsp[rsp_len ++] = err >> 8; - rsp[rsp_len ++] = err & 0xff; - } - - sdu_out = sdp->channel->sdu_out(sdp->channel, rsp_len + PDU_HEADER_SIZE); - - sdu_out[0] = pdu_id; - sdu_out[1] = transaction_id >> 8; - sdu_out[2] = transaction_id & 0xff; - sdu_out[3] = rsp_len >> 8; - sdu_out[4] = rsp_len & 0xff; - memcpy(sdu_out + PDU_HEADER_SIZE, rsp, rsp_len); - - sdp->channel->sdu_submit(sdp->channel); -} - -static void bt_l2cap_sdp_close_ch(void *opaque) -{ - struct bt_l2cap_sdp_state_s *sdp = opaque; - int i; - - for (i = 0; i < sdp->services; i ++) { - g_free(sdp->service_list[i].attribute_list[0].pair); - g_free(sdp->service_list[i].attribute_list); - g_free(sdp->service_list[i].uuid); - } - g_free(sdp->service_list); - g_free(sdp); -} - -struct sdp_def_service_s { - uint16_t class_uuid; - struct sdp_def_attribute_s { - uint16_t id; - struct sdp_def_data_element_s { - uint8_t type; - union { - uint32_t uint; - const char *str; - struct sdp_def_data_element_s *list; - } value; - } data; - } attributes[]; -}; - -/* Calculate a safe byte count to allocate that will store the given - * element, at the same time count elements of a UUID type. */ -static int sdp_attr_max_size(struct sdp_def_data_element_s *element, - int *uuids) -{ - int type = element->type & ~SDP_DSIZE_MASK; - int len; - - if (type == SDP_DTYPE_UINT || type == SDP_DTYPE_UUID || - type == SDP_DTYPE_BOOL) { - if (type == SDP_DTYPE_UUID) - (*uuids) ++; - return 1 + (1 << (element->type & SDP_DSIZE_MASK)); - } - - if (type == SDP_DTYPE_STRING || type == SDP_DTYPE_URL) { - if (element->type & SDP_DSIZE_MASK) { - for (len = 0; element->value.str[len] | - element->value.str[len + 1]; len ++); - return len; - } else - return 2 + strlen(element->value.str); - } - - if (type != SDP_DTYPE_SEQ) - exit(-1); - len = 2; - element = element->value.list; - while (element->type) - len += sdp_attr_max_size(element ++, uuids); - if (len > 255) - exit (-1); - - return len; -} - -static int sdp_attr_write(uint8_t *data, - struct sdp_def_data_element_s *element, int **uuid) -{ - int type = element->type & ~SDP_DSIZE_MASK; - int len = 0; - - if (type == SDP_DTYPE_UINT || type == SDP_DTYPE_BOOL) { - data[len ++] = element->type; - if ((element->type & SDP_DSIZE_MASK) == SDP_DSIZE_1) - data[len ++] = (element->value.uint >> 0) & 0xff; - else if ((element->type & SDP_DSIZE_MASK) == SDP_DSIZE_2) { - data[len ++] = (element->value.uint >> 8) & 0xff; - data[len ++] = (element->value.uint >> 0) & 0xff; - } else if ((element->type & SDP_DSIZE_MASK) == SDP_DSIZE_4) { - data[len ++] = (element->value.uint >> 24) & 0xff; - data[len ++] = (element->value.uint >> 16) & 0xff; - data[len ++] = (element->value.uint >> 8) & 0xff; - data[len ++] = (element->value.uint >> 0) & 0xff; - } - - return len; - } - - if (type == SDP_DTYPE_UUID) { - *(*uuid) ++ = element->value.uint; - - data[len ++] = element->type; - data[len ++] = (element->value.uint >> 24) & 0xff; - data[len ++] = (element->value.uint >> 16) & 0xff; - data[len ++] = (element->value.uint >> 8) & 0xff; - data[len ++] = (element->value.uint >> 0) & 0xff; - memcpy(data + len, bt_base_uuid, 12); - - return len + 12; - } - - data[0] = type | SDP_DSIZE_NEXT1; - if (type == SDP_DTYPE_STRING || type == SDP_DTYPE_URL) { - if (element->type & SDP_DSIZE_MASK) - for (len = 0; element->value.str[len] | - element->value.str[len + 1]; len ++); - else - len = strlen(element->value.str); - memcpy(data + 2, element->value.str, data[1] = len); - - return len + 2; - } - - len = 2; - element = element->value.list; - while (element->type) - len += sdp_attr_write(data + len, element ++, uuid); - data[1] = len - 2; - - return len; -} - -static int sdp_attributeid_compare(const struct sdp_service_attribute_s *a, - const struct sdp_service_attribute_s *b) -{ - return (int) b->attribute_id - a->attribute_id; -} - -static int sdp_uuid_compare(const int *a, const int *b) -{ - return *a - *b; -} - -static void sdp_service_record_build(struct sdp_service_record_s *record, - struct sdp_def_service_s *def, int handle) -{ - int len = 0; - uint8_t *data; - int *uuid; - - record->uuids = 0; - while (def->attributes[record->attributes].data.type) { - len += 3; - len += sdp_attr_max_size(&def->attributes[record->attributes ++].data, - &record->uuids); - } - - assert(len > 0); - record->uuids = pow2ceil(record->uuids); - record->attribute_list = - g_malloc0(record->attributes * sizeof(*record->attribute_list)); - record->uuid = - g_malloc0(record->uuids * sizeof(*record->uuid)); - data = g_malloc(len); - - record->attributes = 0; - uuid = record->uuid; - while (def->attributes[record->attributes].data.type) { - int attribute_id = def->attributes[record->attributes].id; - record->attribute_list[record->attributes].pair = data; - record->attribute_list[record->attributes].attribute_id = attribute_id; - - len = 0; - data[len ++] = SDP_DTYPE_UINT | SDP_DSIZE_2; - data[len ++] = attribute_id >> 8; - data[len ++] = attribute_id & 0xff; - len += sdp_attr_write(data + len, - &def->attributes[record->attributes].data, &uuid); - - /* Special case: assign a ServiceRecordHandle in sequence */ - if (def->attributes[record->attributes].id == SDP_ATTR_RECORD_HANDLE) - def->attributes[record->attributes].data.value.uint = handle; - /* Note: we could also assign a ServiceDescription based on - * sdp->device.device->lmp_name. */ - - record->attribute_list[record->attributes ++].len = len; - data += len; - } - - /* Sort the attribute list by the AttributeID. The first must be - * SDP_ATTR_RECORD_HANDLE so that bt_l2cap_sdp_close_ch can free - * the buffer. - */ - qsort(record->attribute_list, record->attributes, - sizeof(*record->attribute_list), - (void *) sdp_attributeid_compare); - assert(record->attribute_list[0].pair == data); - - /* Sort the searchable UUIDs list for bisection */ - qsort(record->uuid, record->uuids, - sizeof(*record->uuid), - (void *) sdp_uuid_compare); -} - -static void sdp_service_db_build(struct bt_l2cap_sdp_state_s *sdp, - struct sdp_def_service_s **service) -{ - sdp->services = 0; - while (service[sdp->services]) - sdp->services ++; - sdp->service_list = - g_malloc0(sdp->services * sizeof(*sdp->service_list)); - - sdp->services = 0; - while (*service) { - sdp_service_record_build(&sdp->service_list[sdp->services], - *service, sdp->services); - service ++; - sdp->services ++; - } -} - -#define LAST { .type = 0 } -#define SERVICE(name, attrs) \ - static struct sdp_def_service_s glue(glue(sdp_service_, name), _s) = { \ - .attributes = { attrs { .data = LAST } }, \ - }; -#define ATTRIBUTE(attrid, val) { .id = glue(SDP_ATTR_, attrid), .data = val }, -#define UINT8(val) { \ - .type = SDP_DTYPE_UINT | SDP_DSIZE_1, \ - .value.uint = val, \ - }, -#define UINT16(val) { \ - .type = SDP_DTYPE_UINT | SDP_DSIZE_2, \ - .value.uint = val, \ - }, -#define UINT32(val) { \ - .type = SDP_DTYPE_UINT | SDP_DSIZE_4, \ - .value.uint = val, \ - }, -#define UUID128(val) { \ - .type = SDP_DTYPE_UUID | SDP_DSIZE_16, \ - .value.uint = val, \ - }, -#define SDP_TRUE { \ - .type = SDP_DTYPE_BOOL | SDP_DSIZE_1, \ - .value.uint = 1, \ - }, -#define SDP_FALSE { \ - .type = SDP_DTYPE_BOOL | SDP_DSIZE_1, \ - .value.uint = 0, \ - }, -#define STRING(val) { \ - .type = SDP_DTYPE_STRING, \ - .value.str = val, \ - }, -#define ARRAY(...) { \ - .type = SDP_DTYPE_STRING | SDP_DSIZE_2, \ - .value.str = (char []) { __VA_ARGS__, 0, 0 }, \ - }, -#define URL(val) { \ - .type = SDP_DTYPE_URL, \ - .value.str = val, \ - }, -#if 1 -#define LIST(val) { \ - .type = SDP_DTYPE_SEQ, \ - .value.list = (struct sdp_def_data_element_s []) { val LAST }, \ - }, -#endif - -/* Try to keep each single attribute below MAX_PDU_OUT_SIZE bytes - * in resulting SDP data representation size. */ - -SERVICE(hid, - ATTRIBUTE(RECORD_HANDLE, UINT32(0)) /* Filled in later */ - ATTRIBUTE(SVCLASS_ID_LIST, LIST(UUID128(HID_SVCLASS_ID))) - ATTRIBUTE(RECORD_STATE, UINT32(1)) - ATTRIBUTE(PROTO_DESC_LIST, LIST( - LIST(UUID128(L2CAP_UUID) UINT16(BT_PSM_HID_CTRL)) - LIST(UUID128(HIDP_UUID)) - )) - ATTRIBUTE(BROWSE_GRP_LIST, LIST(UUID128(0x1002))) - ATTRIBUTE(LANG_BASE_ATTR_ID_LIST, LIST( - UINT16(0x656e) UINT16(0x006a) UINT16(0x0100) - )) - ATTRIBUTE(PFILE_DESC_LIST, LIST( - LIST(UUID128(HID_PROFILE_ID) UINT16(0x0100)) - )) - ATTRIBUTE(DOC_URL, URL("http://bellard.org/qemu/user-doc.html")) - ATTRIBUTE(SVCNAME_PRIMARY, STRING("QEMU Bluetooth HID")) - ATTRIBUTE(SVCDESC_PRIMARY, STRING("QEMU Keyboard/Mouse")) - ATTRIBUTE(SVCPROV_PRIMARY, STRING("QEMU")) - - /* Profile specific */ - ATTRIBUTE(DEVICE_RELEASE_NUMBER, UINT16(0x0091)) /* Deprecated, remove */ - ATTRIBUTE(PARSER_VERSION, UINT16(0x0111)) - /* TODO: extract from l2cap_device->device.class[0] */ - ATTRIBUTE(DEVICE_SUBCLASS, UINT8(0x40)) - ATTRIBUTE(COUNTRY_CODE, UINT8(0x15)) - ATTRIBUTE(VIRTUAL_CABLE, SDP_TRUE) - ATTRIBUTE(RECONNECT_INITIATE, SDP_FALSE) - /* TODO: extract from hid->usbdev->report_desc */ - ATTRIBUTE(DESCRIPTOR_LIST, LIST( - LIST(UINT8(0x22) ARRAY( - 0x05, 0x01, /* Usage Page (Generic Desktop) */ - 0x09, 0x06, /* Usage (Keyboard) */ - 0xa1, 0x01, /* Collection (Application) */ - 0x75, 0x01, /* Report Size (1) */ - 0x95, 0x08, /* Report Count (8) */ - 0x05, 0x07, /* Usage Page (Key Codes) */ - 0x19, 0xe0, /* Usage Minimum (224) */ - 0x29, 0xe7, /* Usage Maximum (231) */ - 0x15, 0x00, /* Logical Minimum (0) */ - 0x25, 0x01, /* Logical Maximum (1) */ - 0x81, 0x02, /* Input (Data, Variable, Absolute) */ - 0x95, 0x01, /* Report Count (1) */ - 0x75, 0x08, /* Report Size (8) */ - 0x81, 0x01, /* Input (Constant) */ - 0x95, 0x05, /* Report Count (5) */ - 0x75, 0x01, /* Report Size (1) */ - 0x05, 0x08, /* Usage Page (LEDs) */ - 0x19, 0x01, /* Usage Minimum (1) */ - 0x29, 0x05, /* Usage Maximum (5) */ - 0x91, 0x02, /* Output (Data, Variable, Absolute) */ - 0x95, 0x01, /* Report Count (1) */ - 0x75, 0x03, /* Report Size (3) */ - 0x91, 0x01, /* Output (Constant) */ - 0x95, 0x06, /* Report Count (6) */ - 0x75, 0x08, /* Report Size (8) */ - 0x15, 0x00, /* Logical Minimum (0) */ - 0x25, 0xff, /* Logical Maximum (255) */ - 0x05, 0x07, /* Usage Page (Key Codes) */ - 0x19, 0x00, /* Usage Minimum (0) */ - 0x29, 0xff, /* Usage Maximum (255) */ - 0x81, 0x00, /* Input (Data, Array) */ - 0xc0 /* End Collection */ - )))) - ATTRIBUTE(LANG_ID_BASE_LIST, LIST( - LIST(UINT16(0x0409) UINT16(0x0100)) - )) - ATTRIBUTE(SDP_DISABLE, SDP_FALSE) - ATTRIBUTE(BATTERY_POWER, SDP_TRUE) - ATTRIBUTE(REMOTE_WAKEUP, SDP_TRUE) - ATTRIBUTE(BOOT_DEVICE, SDP_TRUE) /* XXX: untested */ - ATTRIBUTE(SUPERVISION_TIMEOUT, UINT16(0x0c80)) - ATTRIBUTE(NORMALLY_CONNECTABLE, SDP_TRUE) - ATTRIBUTE(PROFILE_VERSION, UINT16(0x0100)) -) - -SERVICE(sdp, - ATTRIBUTE(RECORD_HANDLE, UINT32(0)) /* Filled in later */ - ATTRIBUTE(SVCLASS_ID_LIST, LIST(UUID128(SDP_SERVER_SVCLASS_ID))) - ATTRIBUTE(RECORD_STATE, UINT32(1)) - ATTRIBUTE(PROTO_DESC_LIST, LIST( - LIST(UUID128(L2CAP_UUID) UINT16(BT_PSM_SDP)) - LIST(UUID128(SDP_UUID)) - )) - ATTRIBUTE(BROWSE_GRP_LIST, LIST(UUID128(0x1002))) - ATTRIBUTE(LANG_BASE_ATTR_ID_LIST, LIST( - UINT16(0x656e) UINT16(0x006a) UINT16(0x0100) - )) - ATTRIBUTE(PFILE_DESC_LIST, LIST( - LIST(UUID128(SDP_SERVER_PROFILE_ID) UINT16(0x0100)) - )) - ATTRIBUTE(DOC_URL, URL("http://bellard.org/qemu/user-doc.html")) - ATTRIBUTE(SVCPROV_PRIMARY, STRING("QEMU")) - - /* Profile specific */ - ATTRIBUTE(VERSION_NUM_LIST, LIST(UINT16(0x0100))) - ATTRIBUTE(SVCDB_STATE , UINT32(1)) -) - -SERVICE(pnp, - ATTRIBUTE(RECORD_HANDLE, UINT32(0)) /* Filled in later */ - ATTRIBUTE(SVCLASS_ID_LIST, LIST(UUID128(PNP_INFO_SVCLASS_ID))) - ATTRIBUTE(RECORD_STATE, UINT32(1)) - ATTRIBUTE(PROTO_DESC_LIST, LIST( - LIST(UUID128(L2CAP_UUID) UINT16(BT_PSM_SDP)) - LIST(UUID128(SDP_UUID)) - )) - ATTRIBUTE(BROWSE_GRP_LIST, LIST(UUID128(0x1002))) - ATTRIBUTE(LANG_BASE_ATTR_ID_LIST, LIST( - UINT16(0x656e) UINT16(0x006a) UINT16(0x0100) - )) - ATTRIBUTE(PFILE_DESC_LIST, LIST( - LIST(UUID128(PNP_INFO_PROFILE_ID) UINT16(0x0100)) - )) - ATTRIBUTE(DOC_URL, URL("http://bellard.org/qemu/user-doc.html")) - ATTRIBUTE(SVCPROV_PRIMARY, STRING("QEMU")) - - /* Profile specific */ - ATTRIBUTE(SPECIFICATION_ID, UINT16(0x0100)) - ATTRIBUTE(VERSION, UINT16(0x0100)) - ATTRIBUTE(PRIMARY_RECORD, SDP_TRUE) -) - -static int bt_l2cap_sdp_new_ch(struct bt_l2cap_device_s *dev, - struct bt_l2cap_conn_params_s *params) -{ - struct bt_l2cap_sdp_state_s *sdp = g_malloc0(sizeof(*sdp)); - struct sdp_def_service_s *services[] = { - &sdp_service_sdp_s, - &sdp_service_hid_s, - &sdp_service_pnp_s, - NULL, - }; - - sdp->channel = params; - sdp->channel->opaque = sdp; - sdp->channel->close = bt_l2cap_sdp_close_ch; - sdp->channel->sdu_in = bt_l2cap_sdp_sdu_in; - - sdp_service_db_build(sdp, services); - - return 0; -} - -void bt_l2cap_sdp_init(struct bt_l2cap_device_s *dev) -{ - bt_l2cap_psm_register(dev, BT_PSM_SDP, - MAX_PDU_OUT_SIZE, bt_l2cap_sdp_new_ch); -} diff --git a/hw/char/Makefile.objs b/hw/char/Makefile.objs index 02d8a66925..9e9a6c1aff 100644 --- a/hw/char/Makefile.objs +++ b/hw/char/Makefile.objs @@ -13,14 +13,13 @@ common-obj-$(CONFIG_XILINX) += xilinx_uartlite.o common-obj-$(CONFIG_XEN) += xen_console.o common-obj-$(CONFIG_CADENCE) += cadence_uart.o -obj-$(CONFIG_EXYNOS4) += exynos4210_uart.o -obj-$(CONFIG_COLDFIRE) += mcf_uart.o -obj-$(CONFIG_OMAP) += omap_uart.o -obj-$(CONFIG_SH4) += sh_serial.o -obj-$(CONFIG_PSERIES) += spapr_vty.o -obj-$(CONFIG_DIGIC) += digic-uart.o -obj-$(CONFIG_STM32F2XX_USART) += stm32f2xx_usart.o -obj-$(CONFIG_RASPI) += bcm2835_aux.o +common-obj-$(CONFIG_EXYNOS4) += exynos4210_uart.o +common-obj-$(CONFIG_COLDFIRE) += mcf_uart.o +common-obj-$(CONFIG_OMAP) += omap_uart.o +common-obj-$(CONFIG_SH4) += sh_serial.o +common-obj-$(CONFIG_DIGIC) += digic-uart.o +common-obj-$(CONFIG_STM32F2XX_USART) += stm32f2xx_usart.o +common-obj-$(CONFIG_RASPI) += bcm2835_aux.o common-obj-$(CONFIG_CMSDK_APB_UART) += cmsdk-apb-uart.o common-obj-$(CONFIG_ETRAXFS) += etraxfs_ser.o @@ -33,4 +32,5 @@ common-obj-$(CONFIG_MILKYMIST) += milkymist-uart.o common-obj-$(CONFIG_SCLPCONSOLE) += sclpconsole.o sclpconsole-lm.o obj-$(CONFIG_VIRTIO) += virtio-serial-bus.o +obj-$(CONFIG_PSERIES) += spapr_vty.o obj-$(CONFIG_TERMINAL3270) += terminal3270.o diff --git a/hw/char/bcm2835_aux.c b/hw/char/bcm2835_aux.c index a6fc1bf152..ee3dd40e3c 100644 --- a/hw/char/bcm2835_aux.c +++ b/hw/char/bcm2835_aux.c @@ -299,7 +299,7 @@ static void bcm2835_aux_class_init(ObjectClass *oc, void *data) dc->realize = bcm2835_aux_realize; dc->vmsd = &vmstate_bcm2835_aux; set_bit(DEVICE_CATEGORY_INPUT, dc->categories); - dc->props = bcm2835_aux_props; + device_class_set_props(dc, bcm2835_aux_props); } static const TypeInfo bcm2835_aux_info = { diff --git a/hw/char/cadence_uart.c b/hw/char/cadence_uart.c index 0e315b2376..22e47972f1 100644 --- a/hw/char/cadence_uart.c +++ b/hw/char/cadence_uart.c @@ -348,7 +348,7 @@ static void uart_receive(void *opaque, const uint8_t *buf, int size) } } -static void uart_event(void *opaque, int event) +static void uart_event(void *opaque, QEMUChrEvent event) { CadenceUARTState *s = opaque; uint8_t buf = '\0'; @@ -550,7 +550,7 @@ static void cadence_uart_class_init(ObjectClass *klass, void *data) dc->realize = cadence_uart_realize; dc->vmsd = &vmstate_cadence_uart; dc->reset = cadence_uart_reset; - dc->props = cadence_uart_properties; + device_class_set_props(dc, cadence_uart_properties); } static const TypeInfo cadence_uart_info = { diff --git a/hw/char/cmsdk-apb-uart.c b/hw/char/cmsdk-apb-uart.c index 9e1aa43bd3..626b68f2ec 100644 --- a/hw/char/cmsdk-apb-uart.c +++ b/hw/char/cmsdk-apb-uart.c @@ -389,7 +389,7 @@ static void cmsdk_apb_uart_class_init(ObjectClass *klass, void *data) dc->realize = cmsdk_apb_uart_realize; dc->vmsd = &cmsdk_apb_uart_vmstate; dc->reset = cmsdk_apb_uart_reset; - dc->props = cmsdk_apb_uart_properties; + device_class_set_props(dc, cmsdk_apb_uart_properties); } static const TypeInfo cmsdk_apb_uart_info = { diff --git a/hw/char/debugcon.c b/hw/char/debugcon.c index 5c592e091b..c8d938efb5 100644 --- a/hw/char/debugcon.c +++ b/hw/char/debugcon.c @@ -125,7 +125,7 @@ static void debugcon_isa_class_initfn(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = debugcon_isa_realizefn; - dc->props = debugcon_isa_properties; + device_class_set_props(dc, debugcon_isa_properties); set_bit(DEVICE_CATEGORY_MISC, dc->categories); } diff --git a/hw/char/digic-uart.c b/hw/char/digic-uart.c index 974a2619dd..e130cb4692 100644 --- a/hw/char/digic-uart.c +++ b/hw/char/digic-uart.c @@ -131,7 +131,7 @@ static void uart_rx(void *opaque, const uint8_t *buf, int size) s->reg_rx = *buf; } -static void uart_event(void *opaque, int event) +static void uart_event(void *opaque, QEMUChrEvent event) { } @@ -183,7 +183,7 @@ static void digic_uart_class_init(ObjectClass *klass, void *data) dc->realize = digic_uart_realize; dc->reset = digic_uart_reset; dc->vmsd = &vmstate_digic_uart; - dc->props = digic_uart_properties; + device_class_set_props(dc, digic_uart_properties); } static const TypeInfo digic_uart_info = { diff --git a/hw/char/escc.c b/hw/char/escc.c index 8f7bf322cb..7d16ee8688 100644 --- a/hw/char/escc.c +++ b/hw/char/escc.c @@ -634,7 +634,7 @@ static void serial_receive1(void *opaque, const uint8_t *buf, int size) serial_receive_byte(s, buf[0]); } -static void serial_event(void *opaque, int event) +static void serial_event(void *opaque, QEMUChrEvent event) { ESCCChannelState *s = opaque; if (event == CHR_EVENT_BREAK) @@ -865,7 +865,7 @@ static void escc_class_init(ObjectClass *klass, void *data) dc->reset = escc_reset; dc->realize = escc_realize; dc->vmsd = &vmstate_escc; - dc->props = escc_properties; + device_class_set_props(dc, escc_properties); set_bit(DEVICE_CATEGORY_INPUT, dc->categories); } diff --git a/hw/char/etraxfs_ser.c b/hw/char/etraxfs_ser.c index 15ac12ef22..947bdb649a 100644 --- a/hw/char/etraxfs_ser.c +++ b/hw/char/etraxfs_ser.c @@ -202,7 +202,7 @@ static int serial_can_receive(void *opaque) return sizeof(s->rx_fifo) - s->rx_fifo_len; } -static void serial_event(void *opaque, int event) +static void serial_event(void *opaque, QEMUChrEvent event) { } @@ -244,7 +244,7 @@ static void etraxfs_ser_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->reset = etraxfs_ser_reset; - dc->props = etraxfs_ser_properties; + device_class_set_props(dc, etraxfs_ser_properties); dc->realize = etraxfs_ser_realize; } diff --git a/hw/char/exynos4210_uart.c b/hw/char/exynos4210_uart.c index d6b6b62366..96d5180e3e 100644 --- a/hw/char/exynos4210_uart.c +++ b/hw/char/exynos4210_uart.c @@ -24,6 +24,7 @@ #include "migration/vmstate.h" #include "qemu/error-report.h" #include "qemu/module.h" +#include "qemu/timer.h" #include "chardev/char-fe.h" #include "chardev/char-serial.h" @@ -31,45 +32,7 @@ #include "hw/irq.h" #include "hw/qdev-properties.h" -#undef DEBUG_UART -#undef DEBUG_UART_EXTEND -#undef DEBUG_IRQ -#undef DEBUG_Rx_DATA -#undef DEBUG_Tx_DATA - -#define DEBUG_UART 0 -#define DEBUG_UART_EXTEND 0 -#define DEBUG_IRQ 0 -#define DEBUG_Rx_DATA 0 -#define DEBUG_Tx_DATA 0 - -#if DEBUG_UART -#define PRINT_DEBUG(fmt, args...) \ - do { \ - fprintf(stderr, " [%s:%d] "fmt, __func__, __LINE__, ##args); \ - } while (0) - -#if DEBUG_UART_EXTEND -#define PRINT_DEBUG_EXTEND(fmt, args...) \ - do { \ - fprintf(stderr, " [%s:%d] "fmt, __func__, __LINE__, ##args); \ - } while (0) -#else -#define PRINT_DEBUG_EXTEND(fmt, args...) \ - do {} while (0) -#endif /* EXTEND */ - -#else -#define PRINT_DEBUG(fmt, args...) \ - do {} while (0) -#define PRINT_DEBUG_EXTEND(fmt, args...) \ - do {} while (0) -#endif - -#define PRINT_ERROR(fmt, args...) \ - do { \ - fprintf(stderr, " [%s:%d] "fmt, __func__, __LINE__, ##args); \ - } while (0) +#include "trace.h" /* * Offsets for UART registers relative to SFR base address @@ -156,6 +119,7 @@ static const Exynos4210UartReg exynos4210_uart_regs[] = { #define ULCON_STOP_BIT_SHIFT 1 /* UART Tx/Rx Status */ +#define UTRSTAT_Rx_TIMEOUT 0x8 #define UTRSTAT_TRANSMITTER_EMPTY 0x4 #define UTRSTAT_Tx_BUFFER_EMPTY 0x2 #define UTRSTAT_Rx_BUFFER_DATA_READY 0x1 @@ -185,16 +149,19 @@ typedef struct Exynos4210UartState { Exynos4210UartFIFO rx; Exynos4210UartFIFO tx; + QEMUTimer *fifo_timeout_timer; + uint64_t wordtime; /* word time in ns */ + CharBackend chr; qemu_irq irq; + qemu_irq dmairq; uint32_t channel; } Exynos4210UartState; -#if DEBUG_UART -/* Used only for debugging inside PRINT_DEBUG_... macros */ +/* Used only for tracing */ static const char *exynos4210_uart_regname(hwaddr offset) { @@ -208,7 +175,6 @@ static const char *exynos4210_uart_regname(hwaddr offset) return NULL; } -#endif static void fifo_store(Exynos4210UartFIFO *q, uint8_t ch) @@ -249,15 +215,12 @@ static void fifo_reset(Exynos4210UartFIFO *q) q->rp = 0; } -static uint32_t exynos4210_uart_Tx_FIFO_trigger_level(const Exynos4210UartState *s) +static uint32_t exynos4210_uart_FIFO_trigger_level(uint32_t channel, + uint32_t reg) { - uint32_t level = 0; - uint32_t reg; + uint32_t level; - reg = (s->reg[I_(UFCON)] & UFCON_Tx_FIFO_TRIGGER_LEVEL) >> - UFCON_Tx_FIFO_TRIGGER_LEVEL_SHIFT; - - switch (s->channel) { + switch (channel) { case 0: level = reg * 32; break; @@ -271,12 +234,52 @@ static uint32_t exynos4210_uart_Tx_FIFO_trigger_level(const Exynos4210UartState break; default: level = 0; - PRINT_ERROR("Wrong UART channel number: %d\n", s->channel); + trace_exynos_uart_channel_error(channel); + break; } - return level; } +static uint32_t +exynos4210_uart_Tx_FIFO_trigger_level(const Exynos4210UartState *s) +{ + uint32_t reg; + + reg = (s->reg[I_(UFCON)] & UFCON_Tx_FIFO_TRIGGER_LEVEL) >> + UFCON_Tx_FIFO_TRIGGER_LEVEL_SHIFT; + + return exynos4210_uart_FIFO_trigger_level(s->channel, reg); +} + +static uint32_t +exynos4210_uart_Rx_FIFO_trigger_level(const Exynos4210UartState *s) +{ + uint32_t reg; + + reg = ((s->reg[I_(UFCON)] & UFCON_Rx_FIFO_TRIGGER_LEVEL) >> + UFCON_Rx_FIFO_TRIGGER_LEVEL_SHIFT) + 1; + + return exynos4210_uart_FIFO_trigger_level(s->channel, reg); +} + +/* + * Update Rx DMA busy signal if Rx DMA is enabled. For simplicity, + * mark DMA as busy if DMA is enabled and the receive buffer is empty. + */ +static void exynos4210_uart_update_dmabusy(Exynos4210UartState *s) +{ + bool rx_dma_enabled = (s->reg[I_(UCON)] & 0x03) == 0x02; + uint32_t count = fifo_elements_number(&s->rx); + + if (rx_dma_enabled && !count) { + qemu_irq_raise(s->dmairq); + trace_exynos_uart_dmabusy(s->channel); + } else { + qemu_irq_lower(s->dmairq); + trace_exynos_uart_dmaready(s->channel); + } +} + static void exynos4210_uart_update_irq(Exynos4210UartState *s) { /* @@ -284,27 +287,53 @@ static void exynos4210_uart_update_irq(Exynos4210UartState *s) * transmit FIFO is smaller than the trigger level. */ if (s->reg[I_(UFCON)] & UFCON_FIFO_ENABLE) { - uint32_t count = (s->reg[I_(UFSTAT)] & UFSTAT_Tx_FIFO_COUNT) >> UFSTAT_Tx_FIFO_COUNT_SHIFT; if (count <= exynos4210_uart_Tx_FIFO_trigger_level(s)) { s->reg[I_(UINTSP)] |= UINTSP_TXD; } + + /* + * Rx interrupt if trigger level is reached or if rx timeout + * interrupt is disabled and there is data in the receive buffer + */ + count = fifo_elements_number(&s->rx); + if ((count && !(s->reg[I_(UCON)] & 0x80)) || + count >= exynos4210_uart_Rx_FIFO_trigger_level(s)) { + exynos4210_uart_update_dmabusy(s); + s->reg[I_(UINTSP)] |= UINTSP_RXD; + timer_del(s->fifo_timeout_timer); + } + } else if (s->reg[I_(UTRSTAT)] & UTRSTAT_Rx_BUFFER_DATA_READY) { + exynos4210_uart_update_dmabusy(s); + s->reg[I_(UINTSP)] |= UINTSP_RXD; } s->reg[I_(UINTP)] = s->reg[I_(UINTSP)] & ~s->reg[I_(UINTM)]; if (s->reg[I_(UINTP)]) { qemu_irq_raise(s->irq); - -#if DEBUG_IRQ - fprintf(stderr, "UART%d: IRQ has been raised: %08x\n", - s->channel, s->reg[I_(UINTP)]); -#endif - + trace_exynos_uart_irq_raised(s->channel, s->reg[I_(UINTP)]); } else { qemu_irq_lower(s->irq); + trace_exynos_uart_irq_lowered(s->channel); + } +} + +static void exynos4210_uart_timeout_int(void *opaque) +{ + Exynos4210UartState *s = opaque; + + trace_exynos_uart_rx_timeout(s->channel, s->reg[I_(UTRSTAT)], + s->reg[I_(UINTSP)]); + + if ((s->reg[I_(UTRSTAT)] & UTRSTAT_Rx_BUFFER_DATA_READY) || + (s->reg[I_(UCON)] & (1 << 11))) { + s->reg[I_(UINTSP)] |= UINTSP_RXD; + s->reg[I_(UTRSTAT)] |= UTRSTAT_Rx_TIMEOUT; + exynos4210_uart_update_dmabusy(s); + exynos4210_uart_update_irq(s); } } @@ -346,10 +375,24 @@ static void exynos4210_uart_update_parameters(Exynos4210UartState *s) ssp.data_bits = data_bits; ssp.stop_bits = stop_bits; + s->wordtime = NANOSECONDS_PER_SECOND * (data_bits + stop_bits + 1) / speed; + qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp); - PRINT_DEBUG("UART%d: speed: %d, parity: %c, data: %d, stop: %d\n", - s->channel, speed, parity, data_bits, stop_bits); + trace_exynos_uart_update_params( + s->channel, speed, parity, data_bits, stop_bits, s->wordtime); +} + +static void exynos4210_uart_rx_timeout_set(Exynos4210UartState *s) +{ + if (s->reg[I_(UCON)] & 0x80) { + uint32_t timeout = ((s->reg[I_(UCON)] >> 12) & 0x0f) * s->wordtime; + + timer_mod(s->fifo_timeout_timer, + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + timeout); + } else { + timer_del(s->fifo_timeout_timer); + } } static void exynos4210_uart_write(void *opaque, hwaddr offset, @@ -358,8 +401,8 @@ static void exynos4210_uart_write(void *opaque, hwaddr offset, Exynos4210UartState *s = (Exynos4210UartState *)opaque; uint8_t ch; - PRINT_DEBUG_EXTEND("UART%d: <0x%04x> %s <- 0x%08llx\n", s->channel, - offset, exynos4210_uart_regname(offset), (long long unsigned int)val); + trace_exynos_uart_write(s->channel, offset, + exynos4210_uart_regname(offset), val); switch (offset) { case ULCON: @@ -373,12 +416,12 @@ static void exynos4210_uart_write(void *opaque, hwaddr offset, if (val & UFCON_Rx_FIFO_RESET) { fifo_reset(&s->rx); s->reg[I_(UFCON)] &= ~UFCON_Rx_FIFO_RESET; - PRINT_DEBUG("UART%d: Rx FIFO Reset\n", s->channel); + trace_exynos_uart_rx_fifo_reset(s->channel); } if (val & UFCON_Tx_FIFO_RESET) { fifo_reset(&s->tx); s->reg[I_(UFCON)] &= ~UFCON_Tx_FIFO_RESET; - PRINT_DEBUG("UART%d: Tx FIFO Reset\n", s->channel); + trace_exynos_uart_tx_fifo_reset(s->channel); } break; @@ -390,9 +433,7 @@ static void exynos4210_uart_write(void *opaque, hwaddr offset, /* XXX this blocks entire thread. Rewrite to use * qemu_chr_fe_write and background I/O callbacks */ qemu_chr_fe_write_all(&s->chr, &ch, 1); -#if DEBUG_Tx_DATA - fprintf(stderr, "%c", ch); -#endif + trace_exynos_uart_tx(s->channel, ch); s->reg[I_(UTRSTAT)] |= UTRSTAT_TRANSMITTER_EMPTY | UTRSTAT_Tx_BUFFER_EMPTY; s->reg[I_(UINTSP)] |= UINTSP_TXD; @@ -403,16 +444,19 @@ static void exynos4210_uart_write(void *opaque, hwaddr offset, case UINTP: s->reg[I_(UINTP)] &= ~val; s->reg[I_(UINTSP)] &= ~val; - PRINT_DEBUG("UART%d: UINTP [%04x] have been cleared: %08x\n", - s->channel, offset, s->reg[I_(UINTP)]); + trace_exynos_uart_intclr(s->channel, s->reg[I_(UINTP)]); exynos4210_uart_update_irq(s); break; case UTRSTAT: + if (val & UTRSTAT_Rx_TIMEOUT) { + s->reg[I_(UTRSTAT)] &= ~UTRSTAT_Rx_TIMEOUT; + } + break; case UERSTAT: case UFSTAT: case UMSTAT: case URXH: - PRINT_DEBUG("UART%d: Trying to write into RO register: %s [%04x]\n", + trace_exynos_uart_ro_write( s->channel, exynos4210_uart_regname(offset), offset); break; case UINTSP: @@ -429,6 +473,7 @@ static void exynos4210_uart_write(void *opaque, hwaddr offset, break; } } + static uint64_t exynos4210_uart_read(void *opaque, hwaddr offset, unsigned size) { @@ -439,6 +484,8 @@ static uint64_t exynos4210_uart_read(void *opaque, hwaddr offset, case UERSTAT: /* Read Only */ res = s->reg[I_(UERSTAT)]; s->reg[I_(UERSTAT)] = 0; + trace_exynos_uart_read(s->channel, offset, + exynos4210_uart_regname(offset), res); return res; case UFSTAT: /* Read Only */ s->reg[I_(UFSTAT)] = fifo_elements_number(&s->rx) & 0xff; @@ -446,20 +493,22 @@ static uint64_t exynos4210_uart_read(void *opaque, hwaddr offset, s->reg[I_(UFSTAT)] |= UFSTAT_Rx_FIFO_FULL; s->reg[I_(UFSTAT)] &= ~0xff; } + trace_exynos_uart_read(s->channel, offset, + exynos4210_uart_regname(offset), + s->reg[I_(UFSTAT)]); return s->reg[I_(UFSTAT)]; case URXH: if (s->reg[I_(UFCON)] & UFCON_FIFO_ENABLE) { if (fifo_elements_number(&s->rx)) { res = fifo_retrieve(&s->rx); -#if DEBUG_Rx_DATA - fprintf(stderr, "%c", res); -#endif + trace_exynos_uart_rx(s->channel, res); if (!fifo_elements_number(&s->rx)) { s->reg[I_(UTRSTAT)] &= ~UTRSTAT_Rx_BUFFER_DATA_READY; } else { s->reg[I_(UTRSTAT)] |= UTRSTAT_Rx_BUFFER_DATA_READY; } } else { + trace_exynos_uart_rx_error(s->channel); s->reg[I_(UINTSP)] |= UINTSP_ERROR; exynos4210_uart_update_irq(s); res = 0; @@ -468,15 +517,23 @@ static uint64_t exynos4210_uart_read(void *opaque, hwaddr offset, s->reg[I_(UTRSTAT)] &= ~UTRSTAT_Rx_BUFFER_DATA_READY; res = s->reg[I_(URXH)]; } + exynos4210_uart_update_dmabusy(s); + trace_exynos_uart_read(s->channel, offset, + exynos4210_uart_regname(offset), res); return res; case UTXH: - PRINT_DEBUG("UART%d: Trying to read from WO register: %s [%04x]\n", - s->channel, exynos4210_uart_regname(offset), offset); + trace_exynos_uart_wo_read(s->channel, exynos4210_uart_regname(offset), + offset); break; default: + trace_exynos_uart_read(s->channel, offset, + exynos4210_uart_regname(offset), + s->reg[I_(offset)]); return s->reg[I_(offset)]; } + trace_exynos_uart_read(s->channel, offset, exynos4210_uart_regname(offset), + 0); return 0; } @@ -497,7 +554,6 @@ static int exynos4210_uart_can_receive(void *opaque) return fifo_empty_elements_number(&s->rx); } - static void exynos4210_uart_receive(void *opaque, const uint8_t *buf, int size) { Exynos4210UartState *s = (Exynos4210UartState *)opaque; @@ -505,30 +561,23 @@ static void exynos4210_uart_receive(void *opaque, const uint8_t *buf, int size) if (s->reg[I_(UFCON)] & UFCON_FIFO_ENABLE) { if (fifo_empty_elements_number(&s->rx) < size) { - for (i = 0; i < fifo_empty_elements_number(&s->rx); i++) { - fifo_store(&s->rx, buf[i]); - } + size = fifo_empty_elements_number(&s->rx); s->reg[I_(UINTSP)] |= UINTSP_ERROR; - s->reg[I_(UTRSTAT)] |= UTRSTAT_Rx_BUFFER_DATA_READY; - } else { - for (i = 0; i < size; i++) { - fifo_store(&s->rx, buf[i]); - } - s->reg[I_(UTRSTAT)] |= UTRSTAT_Rx_BUFFER_DATA_READY; } - /* XXX: Around here we maybe should check Rx trigger level */ - s->reg[I_(UINTSP)] |= UINTSP_RXD; + for (i = 0; i < size; i++) { + fifo_store(&s->rx, buf[i]); + } + exynos4210_uart_rx_timeout_set(s); } else { s->reg[I_(URXH)] = buf[0]; - s->reg[I_(UINTSP)] |= UINTSP_RXD; - s->reg[I_(UTRSTAT)] |= UTRSTAT_Rx_BUFFER_DATA_READY; } + s->reg[I_(UTRSTAT)] |= UTRSTAT_Rx_BUFFER_DATA_READY; exynos4210_uart_update_irq(s); } -static void exynos4210_uart_event(void *opaque, int event) +static void exynos4210_uart_event(void *opaque, QEMUChrEvent event) { Exynos4210UartState *s = (Exynos4210UartState *)opaque; @@ -555,13 +604,24 @@ static void exynos4210_uart_reset(DeviceState *dev) fifo_reset(&s->rx); fifo_reset(&s->tx); - PRINT_DEBUG("UART%d: Rx FIFO size: %d\n", s->channel, s->rx.size); + trace_exynos_uart_rxsize(s->channel, s->rx.size); +} + +static int exynos4210_uart_post_load(void *opaque, int version_id) +{ + Exynos4210UartState *s = (Exynos4210UartState *)opaque; + + exynos4210_uart_update_parameters(s); + exynos4210_uart_rx_timeout_set(s); + + return 0; } static const VMStateDescription vmstate_exynos4210_uart_fifo = { .name = "exynos4210.uart.fifo", .version_id = 1, .minimum_version_id = 1, + .post_load = exynos4210_uart_post_load, .fields = (VMStateField[]) { VMSTATE_UINT32(sp, Exynos4210UartFIFO), VMSTATE_UINT32(rp, Exynos4210UartFIFO), @@ -614,18 +674,24 @@ static void exynos4210_uart_init(Object *obj) SysBusDevice *dev = SYS_BUS_DEVICE(obj); Exynos4210UartState *s = EXYNOS4210_UART(dev); + s->wordtime = NANOSECONDS_PER_SECOND * 10 / 9600; + /* memory mapping */ memory_region_init_io(&s->iomem, obj, &exynos4210_uart_ops, s, "exynos4210.uart", EXYNOS4210_UART_REGS_MEM_SIZE); sysbus_init_mmio(dev, &s->iomem); sysbus_init_irq(dev, &s->irq); + sysbus_init_irq(dev, &s->dmairq); } static void exynos4210_uart_realize(DeviceState *dev, Error **errp) { Exynos4210UartState *s = EXYNOS4210_UART(dev); + s->fifo_timeout_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, + exynos4210_uart_timeout_int, s); + qemu_chr_fe_set_handlers(&s->chr, exynos4210_uart_can_receive, exynos4210_uart_receive, exynos4210_uart_event, NULL, s, NULL, true); @@ -645,7 +711,7 @@ static void exynos4210_uart_class_init(ObjectClass *klass, void *data) dc->realize = exynos4210_uart_realize; dc->reset = exynos4210_uart_reset; - dc->props = exynos4210_uart_properties; + device_class_set_props(dc, exynos4210_uart_properties); dc->vmsd = &vmstate_exynos4210_uart; } diff --git a/hw/char/grlib_apbuart.c b/hw/char/grlib_apbuart.c index fe3cbf41a3..16d0feac59 100644 --- a/hw/char/grlib_apbuart.c +++ b/hw/char/grlib_apbuart.c @@ -155,7 +155,7 @@ static void grlib_apbuart_receive(void *opaque, const uint8_t *buf, int size) } } -static void grlib_apbuart_event(void *opaque, int event) +static void grlib_apbuart_event(void *opaque, QEMUChrEvent event) { trace_grlib_apbuart_event(event); } @@ -285,7 +285,7 @@ static void grlib_apbuart_class_init(ObjectClass *klass, void *data) dc->realize = grlib_apbuart_realize; dc->reset = grlib_apbuart_reset; - dc->props = grlib_apbuart_properties; + device_class_set_props(dc, grlib_apbuart_properties); } static const TypeInfo grlib_apbuart_info = { diff --git a/hw/char/imx_serial.c b/hw/char/imx_serial.c index fddde9b43d..731b8fc64c 100644 --- a/hw/char/imx_serial.c +++ b/hw/char/imx_serial.c @@ -323,7 +323,7 @@ static void imx_receive(void *opaque, const uint8_t *buf, int size) imx_put_data(opaque, *buf); } -static void imx_event(void *opaque, int event) +static void imx_event(void *opaque, QEMUChrEvent event) { if (event == CHR_EVENT_BREAK) { imx_put_data(opaque, URXD_BRK | URXD_FRMERR | URXD_ERR); @@ -372,7 +372,7 @@ static void imx_serial_class_init(ObjectClass *klass, void *data) dc->reset = imx_serial_reset_at_boot; set_bit(DEVICE_CATEGORY_INPUT, dc->categories); dc->desc = "i.MX series UART"; - dc->props = imx_serial_properties; + device_class_set_props(dc, imx_serial_properties); } static const TypeInfo imx_serial_info = { diff --git a/hw/char/ipoctal232.c b/hw/char/ipoctal232.c index 66c163ba26..d7c497b939 100644 --- a/hw/char/ipoctal232.c +++ b/hw/char/ipoctal232.c @@ -503,7 +503,7 @@ static void hostdev_receive(void *opaque, const uint8_t *buf, int size) } } -static void hostdev_event(void *opaque, int event) +static void hostdev_event(void *opaque, QEMUChrEvent event) { SCC2698Channel *ch = opaque; switch (event) { @@ -588,7 +588,7 @@ static void ipoctal_class_init(ObjectClass *klass, void *data) set_bit(DEVICE_CATEGORY_INPUT, dc->categories); dc->desc = "GE IP-Octal 232 8-channel RS-232 IndustryPack"; - dc->props = ipoctal_properties; + device_class_set_props(dc, ipoctal_properties); dc->vmsd = &vmstate_ipoctal; } diff --git a/hw/char/lm32_juart.c b/hw/char/lm32_juart.c index e0b1bd6555..3f34861233 100644 --- a/hw/char/lm32_juart.c +++ b/hw/char/lm32_juart.c @@ -104,7 +104,7 @@ static int juart_can_rx(void *opaque) return !(s->jrx & JRX_FULL); } -static void juart_event(void *opaque, int event) +static void juart_event(void *opaque, QEMUChrEvent event) { } @@ -146,7 +146,7 @@ static void lm32_juart_class_init(ObjectClass *klass, void *data) dc->reset = juart_reset; dc->vmsd = &vmstate_lm32_juart; - dc->props = lm32_juart_properties; + device_class_set_props(dc, lm32_juart_properties); dc->realize = lm32_juart_realize; } diff --git a/hw/char/lm32_uart.c b/hw/char/lm32_uart.c index 32f29c44cf..b0b1092889 100644 --- a/hw/char/lm32_uart.c +++ b/hw/char/lm32_uart.c @@ -235,7 +235,7 @@ static int uart_can_rx(void *opaque) return !(s->regs[R_LSR] & LSR_DR); } -static void uart_event(void *opaque, int event) +static void uart_event(void *opaque, QEMUChrEvent event) { } @@ -293,7 +293,7 @@ static void lm32_uart_class_init(ObjectClass *klass, void *data) dc->reset = uart_reset; dc->vmsd = &vmstate_lm32_uart; - dc->props = lm32_uart_properties; + device_class_set_props(dc, lm32_uart_properties); dc->realize = lm32_uart_realize; } diff --git a/hw/char/mcf_uart.c b/hw/char/mcf_uart.c index 58323baf43..97e4bbc31a 100644 --- a/hw/char/mcf_uart.c +++ b/hw/char/mcf_uart.c @@ -256,7 +256,7 @@ static void mcf_uart_push_byte(mcf_uart_state *s, uint8_t data) mcf_uart_update(s); } -static void mcf_uart_event(void *opaque, int event) +static void mcf_uart_event(void *opaque, QEMUChrEvent event) { mcf_uart_state *s = (mcf_uart_state *)opaque; @@ -320,7 +320,7 @@ static void mcf_uart_class_init(ObjectClass *oc, void *data) dc->realize = mcf_uart_realize; dc->reset = mcf_uart_reset; - dc->props = mcf_uart_properties; + device_class_set_props(dc, mcf_uart_properties); set_bit(DEVICE_CATEGORY_INPUT, dc->categories); } diff --git a/hw/char/milkymist-uart.c b/hw/char/milkymist-uart.c index c358ca07f3..1439efb42a 100644 --- a/hw/char/milkymist-uart.c +++ b/hw/char/milkymist-uart.c @@ -180,7 +180,7 @@ static int uart_can_rx(void *opaque) return !(s->regs[R_STAT] & STAT_RX_EVT); } -static void uart_event(void *opaque, int event) +static void uart_event(void *opaque, QEMUChrEvent event) { } @@ -239,7 +239,7 @@ static void milkymist_uart_class_init(ObjectClass *klass, void *data) dc->realize = milkymist_uart_realize; dc->reset = milkymist_uart_reset; dc->vmsd = &vmstate_milkymist_uart; - dc->props = milkymist_uart_properties; + device_class_set_props(dc, milkymist_uart_properties); } static const TypeInfo milkymist_uart_info = { diff --git a/hw/char/nrf51_uart.c b/hw/char/nrf51_uart.c index 2777afe366..d1fef77acd 100644 --- a/hw/char/nrf51_uart.c +++ b/hw/char/nrf51_uart.c @@ -245,7 +245,7 @@ static int uart_can_receive(void *opaque) return s->rx_started ? (UART_FIFO_LENGTH - s->rx_fifo_len) : 0; } -static void uart_event(void *opaque, int event) +static void uart_event(void *opaque, QEMUChrEvent event) { NRF51UARTState *s = NRF51_UART(opaque); @@ -314,7 +314,7 @@ static void nrf51_uart_class_init(ObjectClass *klass, void *data) dc->reset = nrf51_uart_reset; dc->realize = nrf51_uart_realize; - dc->props = nrf51_uart_properties; + device_class_set_props(dc, nrf51_uart_properties); dc->vmsd = &nrf51_uart_vmstate; } diff --git a/hw/char/omap_uart.c b/hw/char/omap_uart.c index 13e4f43c4c..e8da933378 100644 --- a/hw/char/omap_uart.c +++ b/hw/char/omap_uart.c @@ -27,7 +27,7 @@ struct omap_uart_s { MemoryRegion iomem; hwaddr base; - SerialState *serial; /* TODO */ + SerialMM *serial; /* TODO */ struct omap_target_agent_s *ta; omap_clk fclk; qemu_irq irq; diff --git a/hw/char/parallel.c b/hw/char/parallel.c index 40174eeda1..8dd67d1375 100644 --- a/hw/char/parallel.c +++ b/hw/char/parallel.c @@ -627,7 +627,7 @@ static void parallel_isa_class_initfn(ObjectClass *klass, void *data) dc->realize = parallel_isa_realizefn; dc->vmsd = &vmstate_parallel_isa; - dc->props = parallel_isa_properties; + device_class_set_props(dc, parallel_isa_properties); set_bit(DEVICE_CATEGORY_INPUT, dc->categories); } diff --git a/hw/char/pl011.c b/hw/char/pl011.c index 84ad8ff9fb..13e784f9d9 100644 --- a/hw/char/pl011.c +++ b/hw/char/pl011.c @@ -280,7 +280,7 @@ static void pl011_receive(void *opaque, const uint8_t *buf, int size) pl011_put_fifo(opaque, *buf); } -static void pl011_event(void *opaque, int event) +static void pl011_event(void *opaque, QEMUChrEvent event) { if (event == CHR_EVENT_BREAK) pl011_put_fifo(opaque, 0x400); @@ -356,7 +356,7 @@ static void pl011_class_init(ObjectClass *oc, void *data) dc->realize = pl011_realize; dc->vmsd = &vmstate_pl011; - dc->props = pl011_properties; + device_class_set_props(dc, pl011_properties); } static const TypeInfo pl011_arm_info = { diff --git a/hw/char/sclpconsole-lm.c b/hw/char/sclpconsole-lm.c index 392606259d..2b5f37b6a2 100644 --- a/hw/char/sclpconsole-lm.c +++ b/hw/char/sclpconsole-lm.c @@ -31,7 +31,7 @@ typedef struct OprtnsCommand { EventBufferHeader header; MDMSU message_unit; - char data[0]; + char data[]; } QEMU_PACKED OprtnsCommand; /* max size for line-mode data in 4K SCCB page */ @@ -342,7 +342,7 @@ static void console_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); SCLPEventClass *ec = SCLP_EVENT_CLASS(klass); - dc->props = console_properties; + device_class_set_props(dc, console_properties); dc->reset = console_reset; dc->vmsd = &vmstate_sclplmconsole; ec->init = console_init; diff --git a/hw/char/sclpconsole.c b/hw/char/sclpconsole.c index da126f0133..5c7664905e 100644 --- a/hw/char/sclpconsole.c +++ b/hw/char/sclpconsole.c @@ -25,7 +25,7 @@ typedef struct ASCIIConsoleData { EventBufferHeader ebh; - char data[0]; + char data[]; } QEMU_PACKED ASCIIConsoleData; /* max size for ASCII data in 4K SCCB page */ @@ -258,7 +258,7 @@ static void console_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); SCLPEventClass *ec = SCLP_EVENT_CLASS(klass); - dc->props = console_properties; + device_class_set_props(dc, console_properties); dc->reset = console_reset; dc->vmsd = &vmstate_sclpconsole; ec->init = console_init; diff --git a/hw/char/serial-isa.c b/hw/char/serial-isa.c index 9e31c51bb6..f9b6eed783 100644 --- a/hw/char/serial-isa.c +++ b/hw/char/serial-isa.c @@ -73,9 +73,8 @@ static void serial_isa_realizefn(DeviceState *dev, Error **errp) } index++; - s->baudbase = 115200; isa_init_irq(isadev, &s->irq, isa->isairq); - serial_realize_core(s, errp); + object_property_set_bool(OBJECT(s), true, "realized", errp); qdev_set_legacy_instance_id(dev, isa->iobase, 3); memory_region_init_io(&s->io, OBJECT(isa), &serial_io_ops, s, "serial", 8); @@ -107,14 +106,23 @@ static void serial_isa_class_initfn(ObjectClass *klass, void *data) dc->realize = serial_isa_realizefn; dc->vmsd = &vmstate_isa_serial; - dc->props = serial_isa_properties; + device_class_set_props(dc, serial_isa_properties); set_bit(DEVICE_CATEGORY_INPUT, dc->categories); } +static void serial_isa_initfn(Object *o) +{ + ISASerialState *self = ISA_SERIAL(o); + + object_initialize_child(o, "serial", &self->state, sizeof(self->state), + TYPE_SERIAL, &error_abort, NULL); +} + static const TypeInfo serial_isa_info = { .name = TYPE_ISA_SERIAL, .parent = TYPE_ISA_DEVICE, .instance_size = sizeof(ISASerialState), + .instance_init = serial_isa_initfn, .class_init = serial_isa_class_initfn, }; diff --git a/hw/char/serial-pci-multi.c b/hw/char/serial-pci-multi.c index 5f13b5663b..1742cb77ec 100644 --- a/hw/char/serial-pci-multi.c +++ b/hw/char/serial-pci-multi.c @@ -56,7 +56,7 @@ static void multi_serial_pci_exit(PCIDevice *dev) for (i = 0; i < pci->ports; i++) { s = pci->state + i; - serial_exit_core(s); + object_property_set_bool(OBJECT(s), false, "realized", NULL); memory_region_del_subregion(&pci->iobar, &s->io); g_free(pci->name[i]); } @@ -77,43 +77,43 @@ static void multi_serial_irq_mux(void *opaque, int n, int level) pci_set_irq(&pci->dev, pending); } +static size_t multi_serial_get_port_count(PCIDeviceClass *pc) +{ + switch (pc->device_id) { + case 0x0003: + return 2; + case 0x0004: + return 4; + } + + g_assert_not_reached(); +} + + static void multi_serial_pci_realize(PCIDevice *dev, Error **errp) { PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(dev); PCIMultiSerialState *pci = DO_UPCAST(PCIMultiSerialState, dev, dev); SerialState *s; Error *err = NULL; - int i, nr_ports = 0; - - switch (pc->device_id) { - case 0x0003: - nr_ports = 2; - break; - case 0x0004: - nr_ports = 4; - break; - } - assert(nr_ports > 0); - assert(nr_ports <= PCI_SERIAL_MAX_PORTS); + size_t i, nports = multi_serial_get_port_count(pc); pci->dev.config[PCI_CLASS_PROG] = pci->prog_if; pci->dev.config[PCI_INTERRUPT_PIN] = 0x01; - memory_region_init(&pci->iobar, OBJECT(pci), "multiserial", 8 * nr_ports); + memory_region_init(&pci->iobar, OBJECT(pci), "multiserial", 8 * nports); pci_register_bar(&pci->dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &pci->iobar); - pci->irqs = qemu_allocate_irqs(multi_serial_irq_mux, pci, - nr_ports); + pci->irqs = qemu_allocate_irqs(multi_serial_irq_mux, pci, nports); - for (i = 0; i < nr_ports; i++) { + for (i = 0; i < nports; i++) { s = pci->state + i; - s->baudbase = 115200; - serial_realize_core(s, &err); + object_property_set_bool(OBJECT(s), true, "realized", &err); if (err != NULL) { error_propagate(errp, err); multi_serial_pci_exit(dev); return; } s->irq = pci->irqs[i]; - pci->name[i] = g_strdup_printf("uart #%d", i + 1); + pci->name[i] = g_strdup_printf("uart #%zu", i + 1); memory_region_init_io(&s->io, OBJECT(pci), &serial_io_ops, s, pci->name[i], 8); memory_region_add_subregion(&pci->iobar, 8 * i, &s->io); @@ -161,7 +161,7 @@ static void multi_2x_serial_pci_class_initfn(ObjectClass *klass, void *data) pc->revision = 1; pc->class_id = PCI_CLASS_COMMUNICATION_SERIAL; dc->vmsd = &vmstate_pci_multi_serial; - dc->props = multi_2x_serial_pci_properties; + device_class_set_props(dc, multi_2x_serial_pci_properties); set_bit(DEVICE_CATEGORY_INPUT, dc->categories); } @@ -176,14 +176,28 @@ static void multi_4x_serial_pci_class_initfn(ObjectClass *klass, void *data) pc->revision = 1; pc->class_id = PCI_CLASS_COMMUNICATION_SERIAL; dc->vmsd = &vmstate_pci_multi_serial; - dc->props = multi_4x_serial_pci_properties; + device_class_set_props(dc, multi_4x_serial_pci_properties); set_bit(DEVICE_CATEGORY_INPUT, dc->categories); } +static void multi_serial_init(Object *o) +{ + PCIDevice *dev = PCI_DEVICE(o); + PCIMultiSerialState *pms = DO_UPCAST(PCIMultiSerialState, dev, dev); + size_t i, nports = multi_serial_get_port_count(PCI_DEVICE_GET_CLASS(dev)); + + for (i = 0; i < nports; i++) { + object_initialize_child(o, "serial[*]", &pms->state[i], + sizeof(pms->state[i]), + TYPE_SERIAL, &error_abort, NULL); + } +} + static const TypeInfo multi_2x_serial_pci_info = { .name = "pci-serial-2x", .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIMultiSerialState), + .instance_init = multi_serial_init, .class_init = multi_2x_serial_pci_class_initfn, .interfaces = (InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, @@ -195,6 +209,7 @@ static const TypeInfo multi_4x_serial_pci_info = { .name = "pci-serial-4x", .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIMultiSerialState), + .instance_init = multi_serial_init, .class_init = multi_4x_serial_pci_class_initfn, .interfaces = (InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, diff --git a/hw/char/serial-pci.c b/hw/char/serial-pci.c index cb9b76e22b..743552c56b 100644 --- a/hw/char/serial-pci.c +++ b/hw/char/serial-pci.c @@ -40,6 +40,8 @@ typedef struct PCISerialState { uint8_t prog_if; } PCISerialState; +#define TYPE_PCI_SERIAL "pci-serial" +#define PCI_SERIAL(s) OBJECT_CHECK(PCISerialState, (s), TYPE_PCI_SERIAL) static void serial_pci_realize(PCIDevice *dev, Error **errp) { @@ -47,8 +49,7 @@ static void serial_pci_realize(PCIDevice *dev, Error **errp) SerialState *s = &pci->state; Error *err = NULL; - s->baudbase = 115200; - serial_realize_core(s, &err); + object_property_set_bool(OBJECT(s), true, "realized", &err); if (err != NULL) { error_propagate(errp, err); return; @@ -67,7 +68,7 @@ static void serial_pci_exit(PCIDevice *dev) PCISerialState *pci = DO_UPCAST(PCISerialState, dev, dev); SerialState *s = &pci->state; - serial_exit_core(s); + object_property_set_bool(OBJECT(s), false, "realized", NULL); qemu_free_irq(s->irq); } @@ -99,14 +100,23 @@ static void serial_pci_class_initfn(ObjectClass *klass, void *data) pc->revision = 1; pc->class_id = PCI_CLASS_COMMUNICATION_SERIAL; dc->vmsd = &vmstate_pci_serial; - dc->props = serial_pci_properties; + device_class_set_props(dc, serial_pci_properties); set_bit(DEVICE_CATEGORY_INPUT, dc->categories); } +static void serial_pci_init(Object *o) +{ + PCISerialState *ps = PCI_SERIAL(o); + + object_initialize_child(o, "serial", &ps->state, sizeof(ps->state), + TYPE_SERIAL, &error_abort, NULL); +} + static const TypeInfo serial_pci_info = { - .name = "pci-serial", + .name = TYPE_PCI_SERIAL, .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCISerialState), + .instance_init = serial_pci_init, .class_init = serial_pci_class_initfn, .interfaces = (InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, diff --git a/hw/char/serial.c b/hw/char/serial.c index b4aa250950..c822a9ae6c 100644 --- a/hw/char/serial.c +++ b/hw/char/serial.c @@ -34,6 +34,7 @@ #include "sysemu/runstate.h" #include "qemu/error-report.h" #include "trace.h" +#include "hw/qdev-properties.h" //#define DEBUG_SERIAL @@ -634,7 +635,7 @@ static void serial_receive1(void *opaque, const uint8_t *buf, int size) serial_update_irq(s); } -static void serial_event(void *opaque, int event) +static void serial_event(void *opaque, QEMUChrEvent event) { SerialState *s = opaque; DPRINTF("event %x\n", event); @@ -933,8 +934,10 @@ static int serial_be_change(void *opaque) return 0; } -void serial_realize_core(SerialState *s, Error **errp) +static void serial_realize(DeviceState *dev, Error **errp) { + SerialState *s = SERIAL(dev); + s->modem_status_poll = timer_new_ns(QEMU_CLOCK_VIRTUAL, (QEMUTimerCB *) serial_update_msl, s); s->fifo_timeout_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, (QEMUTimerCB *) fifo_timeout_int, s); @@ -947,8 +950,10 @@ void serial_realize_core(SerialState *s, Error **errp) serial_reset(s); } -void serial_exit_core(SerialState *s) +static void serial_unrealize(DeviceState *dev, Error **errp) { + SerialState *s = SERIAL(dev); + qemu_chr_fe_deinit(&s->chr, false); timer_del(s->modem_status_poll); @@ -980,40 +985,88 @@ const MemoryRegionOps serial_io_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; -SerialState *serial_init(int base, qemu_irq irq, int baudbase, - Chardev *chr, MemoryRegion *system_io) +static void serial_io_realize(DeviceState *dev, Error **errp) { - SerialState *s; + SerialIO *sio = SERIAL_IO(dev); + SerialState *s = &sio->serial; + Error *local_err = NULL; - s = g_malloc0(sizeof(SerialState)); + object_property_set_bool(OBJECT(s), true, "realized", &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } - s->irq = irq; - s->baudbase = baudbase; - qemu_chr_fe_init(&s->chr, chr, &error_abort); - serial_realize_core(s, &error_fatal); - - vmstate_register(NULL, base, &vmstate_serial, s); - - memory_region_init_io(&s->io, NULL, &serial_io_ops, s, "serial", 8); - memory_region_add_subregion(system_io, base, &s->io); - - return s; + memory_region_init_io(&s->io, OBJECT(dev), &serial_io_ops, s, "serial", 8); + sysbus_init_mmio(SYS_BUS_DEVICE(sio), &s->io); + sysbus_init_irq(SYS_BUS_DEVICE(sio), &s->irq); } +static void serial_io_class_init(ObjectClass *klass, void* data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = serial_io_realize; + /* No dc->vmsd: class has no migratable state */ +} + +static void serial_io_instance_init(Object *o) +{ + SerialIO *sio = SERIAL_IO(o); + + object_initialize_child(o, "serial", &sio->serial, sizeof(sio->serial), + TYPE_SERIAL, &error_abort, NULL); + + qdev_alias_all_properties(DEVICE(&sio->serial), o); +} + + +static const TypeInfo serial_io_info = { + .name = TYPE_SERIAL_IO, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(SerialIO), + .instance_init = serial_io_instance_init, + .class_init = serial_io_class_init, +}; + +static Property serial_properties[] = { + DEFINE_PROP_CHR("chardev", SerialState, chr), + DEFINE_PROP_UINT32("baudbase", SerialState, baudbase, 115200), + DEFINE_PROP_END_OF_LIST(), +}; + +static void serial_class_init(ObjectClass *klass, void* data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + /* internal device for serialio/serialmm, not user-creatable */ + dc->user_creatable = false; + dc->realize = serial_realize; + dc->unrealize = serial_unrealize; + device_class_set_props(dc, serial_properties); +} + +static const TypeInfo serial_info = { + .name = TYPE_SERIAL, + .parent = TYPE_DEVICE, + .instance_size = sizeof(SerialState), + .class_init = serial_class_init, +}; + /* Memory mapped interface */ static uint64_t serial_mm_read(void *opaque, hwaddr addr, unsigned size) { - SerialState *s = opaque; - return serial_ioport_read(s, addr >> s->it_shift, 1); + SerialMM *s = SERIAL_MM(opaque); + return serial_ioport_read(&s->serial, addr >> s->regshift, 1); } static void serial_mm_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) { - SerialState *s = opaque; + SerialMM *s = SERIAL_MM(opaque); value &= 255; - serial_ioport_write(s, addr >> s->it_shift, value, 1); + serial_ioport_write(&s->serial, addr >> s->regshift, value, 1); } static const MemoryRegionOps serial_mm_ops[3] = { @@ -1040,25 +1093,101 @@ static const MemoryRegionOps serial_mm_ops[3] = { }, }; -SerialState *serial_mm_init(MemoryRegion *address_space, - hwaddr base, int it_shift, - qemu_irq irq, int baudbase, - Chardev *chr, enum device_endian end) +static void serial_mm_realize(DeviceState *dev, Error **errp) { - SerialState *s; + SerialMM *smm = SERIAL_MM(dev); + SerialState *s = &smm->serial; + Error *local_err = NULL; - s = g_malloc0(sizeof(SerialState)); + object_property_set_bool(OBJECT(s), true, "realized", &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } - s->it_shift = it_shift; - s->irq = irq; - s->baudbase = baudbase; - qemu_chr_fe_init(&s->chr, chr, &error_abort); - - serial_realize_core(s, &error_fatal); - vmstate_register(NULL, base, &vmstate_serial, s); - - memory_region_init_io(&s->io, NULL, &serial_mm_ops[end], s, - "serial", 8 << it_shift); - memory_region_add_subregion(address_space, base, &s->io); - return s; + memory_region_init_io(&s->io, OBJECT(dev), + &serial_mm_ops[smm->endianness], smm, "serial", + 8 << smm->regshift); + sysbus_init_mmio(SYS_BUS_DEVICE(smm), &s->io); + sysbus_init_irq(SYS_BUS_DEVICE(smm), &smm->serial.irq); } + +static const VMStateDescription vmstate_serial_mm = { + .name = "serial", + .version_id = 3, + .minimum_version_id = 2, + .fields = (VMStateField[]) { + VMSTATE_STRUCT(serial, SerialMM, 0, vmstate_serial, SerialState), + VMSTATE_END_OF_LIST() + } +}; + +SerialMM *serial_mm_init(MemoryRegion *address_space, + hwaddr base, int regshift, + qemu_irq irq, int baudbase, + Chardev *chr, enum device_endian end) +{ + SerialMM *smm = SERIAL_MM(qdev_create(NULL, TYPE_SERIAL_MM)); + MemoryRegion *mr; + + qdev_prop_set_uint8(DEVICE(smm), "regshift", regshift); + qdev_prop_set_uint32(DEVICE(smm), "baudbase", baudbase); + qdev_prop_set_chr(DEVICE(smm), "chardev", chr); + qdev_set_legacy_instance_id(DEVICE(smm), base, 2); + qdev_prop_set_uint8(DEVICE(smm), "endianness", end); + qdev_init_nofail(DEVICE(smm)); + + sysbus_connect_irq(SYS_BUS_DEVICE(smm), 0, irq); + mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(smm), 0); + memory_region_add_subregion(address_space, base, mr); + + return smm; +} + +static void serial_mm_instance_init(Object *o) +{ + SerialMM *smm = SERIAL_MM(o); + + object_initialize_child(o, "serial", &smm->serial, sizeof(smm->serial), + TYPE_SERIAL, &error_abort, NULL); + + qdev_alias_all_properties(DEVICE(&smm->serial), o); +} + +static Property serial_mm_properties[] = { + /* + * Set the spacing between adjacent memory-mapped UART registers. + * Each register will be at (1 << regshift) bytes after the + * previous one. + */ + DEFINE_PROP_UINT8("regshift", SerialMM, regshift, 0), + DEFINE_PROP_UINT8("endianness", SerialMM, endianness, DEVICE_NATIVE_ENDIAN), + DEFINE_PROP_END_OF_LIST(), +}; + +static void serial_mm_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + device_class_set_props(dc, serial_mm_properties); + dc->realize = serial_mm_realize; + dc->vmsd = &vmstate_serial_mm; +} + +static const TypeInfo serial_mm_info = { + .name = TYPE_SERIAL_MM, + .parent = TYPE_SYS_BUS_DEVICE, + .class_init = serial_mm_class_init, + .instance_init = serial_mm_instance_init, + .instance_size = sizeof(SerialMM), + .class_init = serial_mm_class_init, +}; + +static void serial_register_types(void) +{ + type_register_static(&serial_info); + type_register_static(&serial_io_info); + type_register_static(&serial_mm_info); +} + +type_init(serial_register_types) diff --git a/hw/char/sh_serial.c b/hw/char/sh_serial.c index 07dc16be13..167f4d8cb9 100644 --- a/hw/char/sh_serial.c +++ b/hw/char/sh_serial.c @@ -358,7 +358,7 @@ static void sh_serial_receive1(void *opaque, const uint8_t *buf, int size) } } -static void sh_serial_event(void *opaque, int event) +static void sh_serial_event(void *opaque, QEMUChrEvent event) { sh_serial_state *s = opaque; if (event == CHR_EVENT_BREAK) diff --git a/hw/char/spapr_vty.c b/hw/char/spapr_vty.c index 8f4d9fe472..ecb94f5673 100644 --- a/hw/char/spapr_vty.c +++ b/hw/char/spapr_vty.c @@ -193,7 +193,7 @@ static void spapr_vty_class_init(ObjectClass *klass, void *data) k->dt_type = "serial"; k->dt_compatible = "hvterm1"; set_bit(DEVICE_CATEGORY_INPUT, dc->categories); - dc->props = spapr_vty_properties; + device_class_set_props(dc, spapr_vty_properties); dc->vmsd = &vmstate_spapr_vty; } diff --git a/hw/char/stm32f2xx_usart.c b/hw/char/stm32f2xx_usart.c index 4ec843de38..0d661be6d3 100644 --- a/hw/char/stm32f2xx_usart.c +++ b/hw/char/stm32f2xx_usart.c @@ -222,7 +222,7 @@ static void stm32f2xx_usart_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->reset = stm32f2xx_usart_reset; - dc->props = stm32f2xx_usart_properties; + device_class_set_props(dc, stm32f2xx_usart_properties); dc->realize = stm32f2xx_usart_realize; } diff --git a/hw/char/terminal3270.c b/hw/char/terminal3270.c index 6859c1bcb2..2c47ebf007 100644 --- a/hw/char/terminal3270.c +++ b/hw/char/terminal3270.c @@ -142,7 +142,7 @@ static void terminal_read(void *opaque, const uint8_t *buf, int size) } } -static void chr_event(void *opaque, int event) +static void chr_event(void *opaque, QEMUChrEvent event) { Terminal3270 *t = opaque; CcwDevice *ccw_dev = CCW_DEVICE(t); @@ -166,6 +166,11 @@ static void chr_event(void *opaque, int event) sch->curr_status.scsw.dstat = SCSW_DSTAT_DEVICE_END; css_conditional_io_interrupt(sch); break; + case CHR_EVENT_BREAK: + case CHR_EVENT_MUX_IN: + case CHR_EVENT_MUX_OUT: + /* Ignore */ + break; } } @@ -283,7 +288,7 @@ static void terminal_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); EmulatedCcw3270Class *ck = EMULATED_CCW_3270_CLASS(klass); - dc->props = terminal_properties; + device_class_set_props(dc, terminal_properties); dc->vmsd = &terminal3270_vmstate; ck->init = terminal_init; ck->read_payload_3270 = read_payload_3270; diff --git a/hw/char/trace-events b/hw/char/trace-events index 2ce7f2f998..6f938301d9 100644 --- a/hw/char/trace-events +++ b/hw/char/trace-events @@ -77,3 +77,23 @@ cmsdk_apb_uart_set_params(int speed) "CMSDK APB UART: params set to %d 8N1" # nrf51_uart.c nrf51_uart_read(uint64_t addr, uint64_t r, unsigned int size) "addr 0x%" PRIx64 " value 0x%" PRIx64 " size %u" nrf51_uart_write(uint64_t addr, uint64_t value, unsigned int size) "addr 0x%" PRIx64 " value 0x%" PRIx64 " size %u" + +# exynos4210_uart.c +exynos_uart_dmabusy(uint32_t channel) "UART%d: DMA busy (Rx buffer empty)" +exynos_uart_dmaready(uint32_t channel) "UART%d: DMA ready" +exynos_uart_irq_raised(uint32_t channel, uint32_t reg) "UART%d: IRQ raised: 0x%08"PRIx32 +exynos_uart_irq_lowered(uint32_t channel) "UART%d: IRQ lowered" +exynos_uart_update_params(uint32_t channel, int speed, uint8_t parity, int data, int stop, uint64_t wordtime) "UART%d: speed: %d, parity: %c, data bits: %d, stop bits: %d wordtime: %"PRId64"ns" +exynos_uart_write(uint32_t channel, uint32_t offset, const char *name, uint64_t val) "UART%d: <0x%04x> %s <- 0x%" PRIx64 +exynos_uart_read(uint32_t channel, uint32_t offset, const char *name, uint64_t val) "UART%d: <0x%04x> %s -> 0x%" PRIx64 +exynos_uart_rx_fifo_reset(uint32_t channel) "UART%d: Rx FIFO Reset" +exynos_uart_tx_fifo_reset(uint32_t channel) "UART%d: Tx FIFO Reset" +exynos_uart_tx(uint32_t channel, uint8_t ch) "UART%d: Tx 0x%02"PRIx32 +exynos_uart_intclr(uint32_t channel, uint32_t reg) "UART%d: interrupts cleared: 0x%08"PRIx32 +exynos_uart_ro_write(uint32_t channel, const char *name, uint32_t reg) "UART%d: Trying to write into RO register: %s [0x%04"PRIx32"]" +exynos_uart_rx(uint32_t channel, uint8_t ch) "UART%d: Rx 0x%02"PRIx32 +exynos_uart_rx_error(uint32_t channel) "UART%d: Rx error" +exynos_uart_wo_read(uint32_t channel, const char *name, uint32_t reg) "UART%d: Trying to read from WO register: %s [0x%04"PRIx32"]" +exynos_uart_rxsize(uint32_t channel, uint32_t size) "UART%d: Rx FIFO size: %d" +exynos_uart_channel_error(uint32_t channel) "Wrong UART channel number: %d" +exynos_uart_rx_timeout(uint32_t channel, uint32_t stat, uint32_t intsp) "UART%d: Rx timeout stat=0x%x intsp=0x%x" diff --git a/hw/char/virtio-console.c b/hw/char/virtio-console.c index c13649f1ef..b010c21de4 100644 --- a/hw/char/virtio-console.c +++ b/hw/char/virtio-console.c @@ -145,7 +145,7 @@ static void chr_read(void *opaque, const uint8_t *buf, int size) virtio_serial_write(port, buf, size); } -static void chr_event(void *opaque, int event) +static void chr_event(void *opaque, QEMUChrEvent event) { VirtConsole *vcon = opaque; VirtIOSerialPort *port = VIRTIO_SERIAL_PORT(vcon); @@ -162,6 +162,11 @@ static void chr_event(void *opaque, int event) } virtio_serial_close(port); break; + case CHR_EVENT_BREAK: + case CHR_EVENT_MUX_IN: + case CHR_EVENT_MUX_OUT: + /* Ignore */ + break; } } @@ -282,7 +287,7 @@ static void virtserialport_class_init(ObjectClass *klass, void *data) k->set_guest_connected = set_guest_connected; k->enable_backend = virtconsole_enable_backend; k->guest_writable = guest_writable; - dc->props = virtserialport_properties; + device_class_set_props(dc, virtserialport_properties); } static const TypeInfo virtserialport_info = { diff --git a/hw/char/virtio-serial-bus.c b/hw/char/virtio-serial-bus.c index 33259042a9..99a65bab7f 100644 --- a/hw/char/virtio-serial-bus.c +++ b/hw/char/virtio-serial-bus.c @@ -943,7 +943,6 @@ static void virtser_port_device_realize(DeviceState *dev, Error **errp) Error *err = NULL; port->vser = bus->vser; - port->bh = qemu_bh_new(flush_queued_data_bh, port); assert(vsc->have_data); @@ -992,6 +991,7 @@ static void virtser_port_device_realize(DeviceState *dev, Error **errp) return; } + port->bh = qemu_bh_new(flush_queued_data_bh, port); port->elem = NULL; } @@ -1110,7 +1110,7 @@ static void virtio_serial_port_class_init(ObjectClass *klass, void *data) k->bus_type = TYPE_VIRTIO_SERIAL_BUS; k->realize = virtser_port_device_realize; k->unrealize = virtser_port_device_unrealize; - k->props = virtser_props; + device_class_set_props(k, virtser_props); } static const TypeInfo virtio_serial_port_type_info = { @@ -1126,9 +1126,17 @@ static void virtio_serial_device_unrealize(DeviceState *dev, Error **errp) { VirtIODevice *vdev = VIRTIO_DEVICE(dev); VirtIOSerial *vser = VIRTIO_SERIAL(dev); + int i; QLIST_REMOVE(vser, next); + virtio_delete_queue(vser->c_ivq); + virtio_delete_queue(vser->c_ovq); + for (i = 0; i < vser->bus.max_nr_ports; i++) { + virtio_delete_queue(vser->ivqs[i]); + virtio_delete_queue(vser->ovqs[i]); + } + g_free(vser->ivqs); g_free(vser->ovqs); g_free(vser->ports_map); @@ -1171,7 +1179,7 @@ static void virtio_serial_class_init(ObjectClass *klass, void *data) QLIST_INIT(&vserdevices.devices); - dc->props = virtio_serial_properties; + device_class_set_props(dc, virtio_serial_properties); dc->vmsd = &vmstate_virtio_console; set_bit(DEVICE_CATEGORY_INPUT, dc->categories); vdc->realize = virtio_serial_device_realize; diff --git a/hw/char/xilinx_uartlite.c b/hw/char/xilinx_uartlite.c index 2c47275068..c6512285d7 100644 --- a/hw/char/xilinx_uartlite.c +++ b/hw/char/xilinx_uartlite.c @@ -206,7 +206,7 @@ static int uart_can_rx(void *opaque) return s->rx_fifo_len < sizeof(s->rx_fifo); } -static void uart_event(void *opaque, int event) +static void uart_event(void *opaque, QEMUChrEvent event) { } @@ -236,7 +236,7 @@ static void xilinx_uartlite_class_init(ObjectClass *klass, void *data) dc->reset = xilinx_uartlite_reset; dc->realize = xilinx_uartlite_realize; - dc->props = xilinx_uartlite_properties; + device_class_set_props(dc, xilinx_uartlite_properties); } static const TypeInfo xilinx_uartlite_info = { diff --git a/hw/core/Makefile.objs b/hw/core/Makefile.objs index fd0550d1d9..6215e7c208 100644 --- a/hw/core/Makefile.objs +++ b/hw/core/Makefile.objs @@ -1,30 +1,33 @@ # core qdev-related obj files, also used by *-user: common-obj-y += qdev.o qdev-properties.o -common-obj-y += bus.o reset.o -common-obj-$(CONFIG_SOFTMMU) += qdev-fw.o -common-obj-$(CONFIG_SOFTMMU) += fw-path-provider.o +common-obj-y += bus.o +common-obj-y += cpu.o +common-obj-y += resettable.o +common-obj-y += hotplug.o +common-obj-y += vmstate-if.o # irq.o needed for qdev GPIO handling: common-obj-y += irq.o -common-obj-y += hotplug.o + +common-obj-$(CONFIG_SOFTMMU) += reset.o +common-obj-$(CONFIG_SOFTMMU) += qdev-fw.o +common-obj-$(CONFIG_SOFTMMU) += fw-path-provider.o common-obj-$(CONFIG_SOFTMMU) += nmi.o common-obj-$(CONFIG_SOFTMMU) += vm-change-state-handler.o -common-obj-y += cpu.o +common-obj-$(CONFIG_SOFTMMU) += qdev-properties-system.o +common-obj-$(CONFIG_SOFTMMU) += sysbus.o +common-obj-$(CONFIG_SOFTMMU) += machine.o +common-obj-$(CONFIG_SOFTMMU) += null-machine.o +common-obj-$(CONFIG_SOFTMMU) += loader.o +common-obj-$(CONFIG_SOFTMMU) += machine-hmp-cmds.o +common-obj-$(CONFIG_SOFTMMU) += numa.o +obj-$(CONFIG_SOFTMMU) += machine-qmp-cmds.o common-obj-$(CONFIG_EMPTY_SLOT) += empty_slot.o common-obj-$(CONFIG_XILINX_AXI) += stream.o common-obj-$(CONFIG_PTIMER) += ptimer.o -common-obj-$(CONFIG_SOFTMMU) += sysbus.o -common-obj-$(CONFIG_SOFTMMU) += machine.o -common-obj-$(CONFIG_SOFTMMU) += loader.o common-obj-$(CONFIG_FITLOADER) += loader-fit.o -common-obj-$(CONFIG_SOFTMMU) += qdev-properties-system.o common-obj-$(CONFIG_REGISTER) += register.o common-obj-$(CONFIG_OR_IRQ) += or-irq.o common-obj-$(CONFIG_SPLIT_IRQ) += split-irq.o common-obj-$(CONFIG_PLATFORM_BUS) += platform-bus.o common-obj-$(CONFIG_GENERIC_LOADER) += generic-loader.o -common-obj-$(CONFIG_SOFTMMU) += null-machine.o - -obj-$(CONFIG_SOFTMMU) += machine-qmp-cmds.o -obj-$(CONFIG_SOFTMMU) += numa.o -common-obj-$(CONFIG_SOFTMMU) += machine-hmp-cmds.o diff --git a/hw/core/bus.c b/hw/core/bus.c index 7f3d2a3dbd..3dc0a825f0 100644 --- a/hw/core/bus.c +++ b/hw/core/bus.c @@ -68,6 +68,33 @@ int qbus_walk_children(BusState *bus, return 0; } +void bus_cold_reset(BusState *bus) +{ + resettable_reset(OBJECT(bus), RESET_TYPE_COLD); +} + +bool bus_is_in_reset(BusState *bus) +{ + return resettable_is_in_reset(OBJECT(bus)); +} + +static ResettableState *bus_get_reset_state(Object *obj) +{ + BusState *bus = BUS(obj); + return &bus->reset; +} + +static void bus_reset_child_foreach(Object *obj, ResettableChildCallback cb, + void *opaque, ResetType type) +{ + BusState *bus = BUS(obj); + BusChild *kid; + + QTAILQ_FOREACH(kid, &bus->children, sibling) { + cb(OBJECT(kid->child), opaque, type); + } +} + static void qbus_realize(BusState *bus, DeviceState *parent, const char *name) { const char *typename = object_get_typename(OBJECT(bus)); @@ -199,12 +226,83 @@ static char *default_bus_get_fw_dev_path(DeviceState *dev) return g_strdup(object_get_typename(OBJECT(dev))); } +/** + * bus_phases_reset: + * Transition reset method for buses to allow moving + * smoothly from legacy reset method to multi-phases + */ +static void bus_phases_reset(BusState *bus) +{ + ResettableClass *rc = RESETTABLE_GET_CLASS(bus); + + if (rc->phases.enter) { + rc->phases.enter(OBJECT(bus), RESET_TYPE_COLD); + } + if (rc->phases.hold) { + rc->phases.hold(OBJECT(bus)); + } + if (rc->phases.exit) { + rc->phases.exit(OBJECT(bus)); + } +} + +static void bus_transitional_reset(Object *obj) +{ + BusClass *bc = BUS_GET_CLASS(obj); + + /* + * This will call either @bus_phases_reset (for multi-phases transitioned + * buses) or a bus's specific method for not-yet transitioned buses. + * In both case, it does not reset children. + */ + if (bc->reset) { + bc->reset(BUS(obj)); + } +} + +/** + * bus_get_transitional_reset: + * check if the bus's class is ready for multi-phase + */ +static ResettableTrFunction bus_get_transitional_reset(Object *obj) +{ + BusClass *dc = BUS_GET_CLASS(obj); + if (dc->reset != bus_phases_reset) { + /* + * dc->reset has been overridden by a subclass, + * the bus is not ready for multi phase yet. + */ + return bus_transitional_reset; + } + return NULL; +} + static void bus_class_init(ObjectClass *class, void *data) { BusClass *bc = BUS_CLASS(class); + ResettableClass *rc = RESETTABLE_CLASS(class); class->unparent = bus_unparent; bc->get_fw_dev_path = default_bus_get_fw_dev_path; + + rc->get_state = bus_get_reset_state; + rc->child_foreach = bus_reset_child_foreach; + + /* + * @bus_phases_reset is put as the default reset method below, allowing + * to do the multi-phase transition from base classes to leaf classes. It + * allows a legacy-reset Bus class to extend a multi-phases-reset + * Bus class for the following reason: + * + If a base class B has been moved to multi-phase, then it does not + * override this default reset method and may have defined phase methods. + * + A child class C (extending class B) which uses + * bus_class_set_parent_reset() (or similar means) to override the + * reset method will still work as expected. @bus_phases_reset function + * will be registered as the parent reset method and effectively call + * parent reset phases. + */ + bc->reset = bus_phases_reset; + rc->get_transitional_function = bus_get_transitional_reset; } static void qbus_finalize(Object *obj) @@ -223,6 +321,10 @@ static const TypeInfo bus_info = { .instance_init = qbus_initfn, .instance_finalize = qbus_finalize, .class_init = bus_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_RESETTABLE_INTERFACE }, + { } + }, }; static void bus_register_types(void) diff --git a/hw/core/cpu.c b/hw/core/cpu.c index db1a03c6bb..786a1bec8a 100644 --- a/hw/core/cpu.c +++ b/hw/core/cpu.c @@ -177,7 +177,7 @@ static int cpu_common_write_elf64_note(WriteCoreDumpFunction f, } -static int cpu_common_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg) +static int cpu_common_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg) { return 0; } @@ -241,17 +241,14 @@ void cpu_dump_statistics(CPUState *cpu, int flags) void cpu_reset(CPUState *cpu) { - CPUClass *klass = CPU_GET_CLASS(cpu); - - if (klass->reset != NULL) { - (*klass->reset)(cpu); - } + device_cold_reset(DEVICE(cpu)); trace_guest_cpu_reset(cpu); } -static void cpu_common_reset(CPUState *cpu) +static void cpu_common_reset(DeviceState *dev) { + CPUState *cpu = CPU(dev); CPUClass *cc = CPU_GET_CLASS(cpu); if (qemu_loglevel_mask(CPU_LOG_RESET)) { @@ -411,7 +408,6 @@ static void cpu_class_init(ObjectClass *klass, void *data) CPUClass *k = CPU_CLASS(klass); k->parse_features = cpu_common_parse_features; - k->reset = cpu_common_reset; k->get_arch_id = cpu_common_get_arch_id; k->has_work = cpu_common_has_work; k->get_paging_enabled = cpu_common_get_paging_enabled; @@ -432,7 +428,8 @@ static void cpu_class_init(ObjectClass *klass, void *data) set_bit(DEVICE_CATEGORY_CPU, dc->categories); dc->realize = cpu_common_realizefn; dc->unrealize = cpu_common_unrealizefn; - dc->props = cpu_common_props; + dc->reset = cpu_common_reset; + device_class_set_props(dc, cpu_common_props); /* * Reason: CPUs still need special care by board code: wiring up * IRQs, adding reset handlers, halting non-first CPUs, ... diff --git a/hw/core/generic-loader.c b/hw/core/generic-loader.c index 4b1fc86a06..b9aaa52706 100644 --- a/hw/core/generic-loader.c +++ b/hw/core/generic-loader.c @@ -140,7 +140,7 @@ static void generic_loader_realize(DeviceState *dev, Error **errp) if (!s->force_raw) { size = load_elf_as(s->file, NULL, NULL, NULL, &entry, NULL, NULL, - big_endian, 0, 0, 0, as); + NULL, big_endian, 0, 0, 0, as); if (size < 0) { size = load_uimage_as(s->file, &entry, NULL, NULL, NULL, NULL, @@ -201,7 +201,7 @@ static void generic_loader_class_init(ObjectClass *klass, void *data) */ dc->realize = generic_loader_realize; dc->unrealize = generic_loader_unrealize; - dc->props = generic_loader_props; + device_class_set_props(dc, generic_loader_props); dc->desc = "Generic Loader"; set_bit(DEVICE_CATEGORY_MISC, dc->categories); } diff --git a/hw/core/loader-fit.c b/hw/core/loader-fit.c index 953b16bc82..c465921b8f 100644 --- a/hw/core/loader-fit.c +++ b/hw/core/loader-fit.c @@ -178,11 +178,12 @@ static int fit_load_fdt(const struct fit_loader *ldr, const void *itb, int cfg, void *opaque, const void *match_data, hwaddr kernel_end, Error **errp) { + Error *err = NULL; const char *name; const void *data; const void *load_data; hwaddr load_addr; - int img_off, err; + int img_off; size_t sz; int ret; @@ -197,13 +198,13 @@ static int fit_load_fdt(const struct fit_loader *ldr, const void *itb, return -EINVAL; } - err = fit_image_addr(itb, img_off, "load", &load_addr, errp); - if (err == -ENOENT) { + ret = fit_image_addr(itb, img_off, "load", &load_addr, &err); + if (ret == -ENOENT) { load_addr = ROUND_UP(kernel_end, 64 * KiB) + (10 * MiB); - error_free(*errp); - } else if (err) { - error_prepend(errp, "unable to read FDT load address from FIT: "); - ret = err; + error_free(err); + } else if (ret) { + error_propagate_prepend(errp, err, + "unable to read FDT load address from FIT: "); goto out; } diff --git a/hw/core/loader.c b/hw/core/loader.c index 5099f27dc8..8bbb1797a4 100644 --- a/hw/core/loader.c +++ b/hw/core/loader.c @@ -406,12 +406,12 @@ int load_elf(const char *filename, uint64_t (*elf_note_fn)(void *, void *, bool), uint64_t (*translate_fn)(void *, uint64_t), void *translate_opaque, uint64_t *pentry, uint64_t *lowaddr, - uint64_t *highaddr, int big_endian, int elf_machine, - int clear_lsb, int data_swab) + uint64_t *highaddr, uint32_t *pflags, int big_endian, + int elf_machine, int clear_lsb, int data_swab) { return load_elf_as(filename, elf_note_fn, translate_fn, translate_opaque, - pentry, lowaddr, highaddr, big_endian, elf_machine, - clear_lsb, data_swab, NULL); + pentry, lowaddr, highaddr, pflags, big_endian, + elf_machine, clear_lsb, data_swab, NULL); } /* return < 0 if error, otherwise the number of bytes loaded in memory */ @@ -419,12 +419,12 @@ int load_elf_as(const char *filename, uint64_t (*elf_note_fn)(void *, void *, bool), uint64_t (*translate_fn)(void *, uint64_t), void *translate_opaque, uint64_t *pentry, uint64_t *lowaddr, - uint64_t *highaddr, int big_endian, int elf_machine, - int clear_lsb, int data_swab, AddressSpace *as) + uint64_t *highaddr, uint32_t *pflags, int big_endian, + int elf_machine, int clear_lsb, int data_swab, AddressSpace *as) { return load_elf_ram(filename, elf_note_fn, translate_fn, translate_opaque, - pentry, lowaddr, highaddr, big_endian, elf_machine, - clear_lsb, data_swab, as, true); + pentry, lowaddr, highaddr, pflags, big_endian, + elf_machine, clear_lsb, data_swab, as, true); } /* return < 0 if error, otherwise the number of bytes loaded in memory */ @@ -432,13 +432,13 @@ int load_elf_ram(const char *filename, uint64_t (*elf_note_fn)(void *, void *, bool), uint64_t (*translate_fn)(void *, uint64_t), void *translate_opaque, uint64_t *pentry, uint64_t *lowaddr, - uint64_t *highaddr, int big_endian, int elf_machine, - int clear_lsb, int data_swab, AddressSpace *as, - bool load_rom) + uint64_t *highaddr, uint32_t *pflags, int big_endian, + int elf_machine, int clear_lsb, int data_swab, + AddressSpace *as, bool load_rom) { return load_elf_ram_sym(filename, elf_note_fn, translate_fn, translate_opaque, - pentry, lowaddr, highaddr, big_endian, + pentry, lowaddr, highaddr, pflags, big_endian, elf_machine, clear_lsb, data_swab, as, load_rom, NULL); } @@ -448,8 +448,9 @@ int load_elf_ram_sym(const char *filename, uint64_t (*elf_note_fn)(void *, void *, bool), uint64_t (*translate_fn)(void *, uint64_t), void *translate_opaque, uint64_t *pentry, - uint64_t *lowaddr, uint64_t *highaddr, int big_endian, - int elf_machine, int clear_lsb, int data_swab, + uint64_t *lowaddr, uint64_t *highaddr, uint32_t *pflags, + int big_endian, int elf_machine, + int clear_lsb, int data_swab, AddressSpace *as, bool load_rom, symbol_fn_t sym_cb) { int fd, data_order, target_data_order, must_swab, ret = ELF_LOAD_FAILED; @@ -490,13 +491,13 @@ int load_elf_ram_sym(const char *filename, if (e_ident[EI_CLASS] == ELFCLASS64) { ret = load_elf64(filename, fd, elf_note_fn, translate_fn, translate_opaque, must_swab, - pentry, lowaddr, highaddr, elf_machine, clear_lsb, - data_swab, as, load_rom, sym_cb); + pentry, lowaddr, highaddr, pflags, elf_machine, + clear_lsb, data_swab, as, load_rom, sym_cb); } else { ret = load_elf32(filename, fd, elf_note_fn, translate_fn, translate_opaque, must_swab, - pentry, lowaddr, highaddr, elf_machine, clear_lsb, - data_swab, as, load_rom, sym_cb); + pentry, lowaddr, highaddr, pflags, elf_machine, + clear_lsb, data_swab, as, load_rom, sym_cb); } fail: @@ -1118,19 +1119,26 @@ static void rom_reset(void *unused) { Rom *rom; - /* - * We don't need to fill in the RAM with ROM data because we'll fill - * the data in during the next incoming migration in all cases. Note - * that some of those RAMs can actually be modified by the guest on ARM - * so this is probably the only right thing to do here. - */ - if (runstate_check(RUN_STATE_INMIGRATE)) - return; - QTAILQ_FOREACH(rom, &roms, next) { if (rom->fw_file) { continue; } + /* + * We don't need to fill in the RAM with ROM data because we'll fill + * the data in during the next incoming migration in all cases. Note + * that some of those RAMs can actually be modified by the guest. + */ + if (runstate_check(RUN_STATE_INMIGRATE)) { + if (rom->data && rom->isrom) { + /* + * Free it so that a rom_reset after migration doesn't + * overwrite a potentially modified 'rom'. + */ + rom_free_data(rom); + } + continue; + } + if (rom->data == NULL) { continue; } @@ -1439,6 +1447,7 @@ typedef struct { uint32_t current_rom_index; uint32_t rom_start_address; AddressSpace *as; + bool complete; } HexParser; /* return size or -1 if error */ @@ -1476,6 +1485,7 @@ static int handle_record_type(HexParser *parser) parser->current_rom_index, parser->rom_start_address, parser->as); } + parser->complete = true; return parser->total_size; case EXT_SEG_ADDR_RECORD: case EXT_LINEAR_ADDR_RECORD: @@ -1540,11 +1550,12 @@ static int parse_hex_blob(const char *filename, hwaddr *addr, uint8_t *hex_blob, .bin_buf = g_malloc(hex_blob_size), .start_addr = addr, .as = as, + .complete = false }; rom_transaction_begin(); - for (; hex_blob < end; ++hex_blob) { + for (; hex_blob < end && !parser.complete; ++hex_blob) { switch (*hex_blob) { case '\r': case '\n': diff --git a/hw/core/machine-hmp-cmds.c b/hw/core/machine-hmp-cmds.c index cd970cc4c5..b76f7223af 100644 --- a/hw/core/machine-hmp-cmds.c +++ b/hw/core/machine-hmp-cmds.c @@ -55,7 +55,7 @@ void hmp_cpu_add(Monitor *mon, const QDict *qdict) cpuid = qdict_get_int(qdict, "id"); qmp_cpu_add(cpuid, &err); - hmp_handle_error(mon, &err); + hmp_handle_error(mon, err); } void hmp_hotpluggable_cpus(Monitor *mon, const QDict *qdict) @@ -66,7 +66,7 @@ void hmp_hotpluggable_cpus(Monitor *mon, const QDict *qdict) CpuInstanceProperties *c; if (err != NULL) { - hmp_handle_error(mon, &err); + hmp_handle_error(mon, err); return; } @@ -135,7 +135,7 @@ void hmp_info_memdev(Monitor *mon, const QDict *qdict) monitor_printf(mon, "\n"); qapi_free_MemdevList(memdev_list); - hmp_handle_error(mon, &err); + hmp_handle_error(mon, err); } void hmp_info_numa(Monitor *mon, const QDict *qdict) diff --git a/hw/core/machine.c b/hw/core/machine.c index 1689ad3bf8..c1a444cb75 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -26,9 +26,26 @@ #include "sysemu/qtest.h" #include "hw/pci/pci.h" #include "hw/mem/nvdimm.h" +#include "migration/vmstate.h" + +GlobalProperty hw_compat_4_2[] = { + { "virtio-blk-device", "queue-size", "128"}, + { "virtio-scsi-device", "virtqueue_size", "128"}, + { "virtio-blk-device", "x-enable-wce-if-config-wce", "off" }, + { "virtio-blk-device", "seg-max-adjust", "off"}, + { "virtio-scsi-device", "seg_max_adjust", "off"}, + { "vhost-blk-device", "seg_max_adjust", "off"}, + { "usb-host", "suppress-remote-wake", "off" }, + { "usb-redir", "suppress-remote-wake", "off" }, + { "qxl", "revision", "4" }, + { "qxl-vga", "revision", "4" }, + { "fw_cfg", "acpi-mr-restore", "false" }, +}; +const size_t hw_compat_4_2_len = G_N_ELEMENTS(hw_compat_4_2); GlobalProperty hw_compat_4_1[] = { { "virtio-pci", "x-pcie-flr-init", "off" }, + { "virtio-device", "use-disabled-flag", "false" }, }; const size_t hw_compat_4_1_len = G_N_ELEMENTS(hw_compat_4_1); @@ -37,7 +54,7 @@ GlobalProperty hw_compat_4_0[] = { { "secondary-vga", "edid", "false" }, { "bochs-display", "edid", "false" }, { "virtio-vga", "edid", "false" }, - { "virtio-gpu", "edid", "false" }, + { "virtio-gpu-device", "edid", "false" }, { "virtio-device", "use-started", "false" }, { "virtio-balloon-device", "qemu-4-0-config-size", "true" }, { "pl031", "migrate-tick-offset", "false" }, @@ -137,7 +154,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" }, @@ -173,86 +191,6 @@ GlobalProperty hw_compat_2_1[] = { }; const size_t hw_compat_2_1_len = G_N_ELEMENTS(hw_compat_2_1); -static char *machine_get_accel(Object *obj, Error **errp) -{ - MachineState *ms = MACHINE(obj); - - return g_strdup(ms->accel); -} - -static void machine_set_accel(Object *obj, const char *value, Error **errp) -{ - MachineState *ms = MACHINE(obj); - - g_free(ms->accel); - ms->accel = g_strdup(value); -} - -static void machine_set_kernel_irqchip(Object *obj, Visitor *v, - const char *name, void *opaque, - Error **errp) -{ - Error *err = NULL; - MachineState *ms = MACHINE(obj); - OnOffSplit mode; - - visit_type_OnOffSplit(v, name, &mode, &err); - if (err) { - error_propagate(errp, err); - return; - } else { - switch (mode) { - case ON_OFF_SPLIT_ON: - ms->kernel_irqchip_allowed = true; - ms->kernel_irqchip_required = true; - ms->kernel_irqchip_split = false; - break; - case ON_OFF_SPLIT_OFF: - ms->kernel_irqchip_allowed = false; - ms->kernel_irqchip_required = false; - ms->kernel_irqchip_split = false; - break; - case ON_OFF_SPLIT_SPLIT: - ms->kernel_irqchip_allowed = true; - ms->kernel_irqchip_required = true; - ms->kernel_irqchip_split = true; - break; - default: - /* The value was checked in visit_type_OnOffSplit() above. If - * we get here, then something is wrong in QEMU. - */ - abort(); - } - } -} - -static void machine_get_kvm_shadow_mem(Object *obj, Visitor *v, - const char *name, void *opaque, - Error **errp) -{ - MachineState *ms = MACHINE(obj); - int64_t value = ms->kvm_shadow_mem; - - visit_type_int(v, name, &value, errp); -} - -static void machine_set_kvm_shadow_mem(Object *obj, Visitor *v, - const char *name, void *opaque, - Error **errp) -{ - MachineState *ms = MACHINE(obj); - Error *error = NULL; - int64_t value; - - visit_type_int(v, name, &value, &error); - if (error) { - error_propagate(errp, error); - return; - } - - ms->kvm_shadow_mem = value; -} - static char *machine_get_kernel(Object *obj, Error **errp) { MachineState *ms = MACHINE(obj); @@ -427,20 +365,6 @@ static void machine_set_graphics(Object *obj, bool value, Error **errp) ms->enable_graphics = value; } -static bool machine_get_igd_gfx_passthru(Object *obj, Error **errp) -{ - MachineState *ms = MACHINE(obj); - - return ms->igd_gfx_passthru; -} - -static void machine_set_igd_gfx_passthru(Object *obj, bool value, Error **errp) -{ - MachineState *ms = MACHINE(obj); - - ms->igd_gfx_passthru = value; -} - static char *machine_get_firmware(Object *obj, Error **errp) { MachineState *ms = MACHINE(obj); @@ -502,6 +426,14 @@ static void machine_set_memory_encryption(Object *obj, const char *value, g_free(ms->memory_encryption); ms->memory_encryption = g_strdup(value); + + /* + * With memory encryption, the host can't see the real contents of RAM, + * so there's no point in it trying to merge areas. + */ + if (value) { + machine_set_mem_merge(obj, false, errp); + } } static bool machine_get_nvdimm(Object *obj, Error **errp) @@ -518,6 +450,20 @@ static void machine_set_nvdimm(Object *obj, bool value, Error **errp) ms->nvdimms_state->is_enabled = value; } +static bool machine_get_hmat(Object *obj, Error **errp) +{ + MachineState *ms = MACHINE(obj); + + return ms->numa_state->hmat_enabled; +} + +static void machine_set_hmat(Object *obj, bool value, Error **errp) +{ + MachineState *ms = MACHINE(obj); + + ms->numa_state->hmat_enabled = value; +} + static char *machine_get_nvdimm_persistence(Object *obj, Error **errp) { MachineState *ms = MACHINE(obj); @@ -574,6 +520,22 @@ static void validate_sysbus_device(SysBusDevice *sbdev, void *opaque) } } +static char *machine_get_memdev(Object *obj, Error **errp) +{ + MachineState *ms = MACHINE(obj); + + return g_strdup(ms->ram_memdev_id); +} + +static void machine_set_memdev(Object *obj, const char *value, Error **errp) +{ + MachineState *ms = MACHINE(obj); + + g_free(ms->ram_memdev_id); + ms->ram_memdev_id = g_strdup(value); +} + + static void machine_init_notify(Notifier *notifier, void *data) { MachineState *machine = MACHINE(qdev_get_machine()); @@ -645,6 +607,7 @@ void machine_set_cpu_numa_node(MachineState *machine, const CpuInstanceProperties *props, Error **errp) { MachineClass *mc = MACHINE_GET_CLASS(machine); + NodeInfo *numa_info = machine->numa_state->nodes; bool match = false; int i; @@ -714,6 +677,17 @@ void machine_set_cpu_numa_node(MachineState *machine, match = true; slot->props.node_id = props->node_id; slot->props.has_node_id = props->has_node_id; + + if (machine->numa_state->hmat_enabled) { + if ((numa_info[props->node_id].initiator < MAX_NODES) && + (props->node_id != numa_info[props->node_id].initiator)) { + error_setg(errp, "The initiator of CPU NUMA node %" PRId64 + " should be itself", props->node_id); + return; + } + numa_info[props->node_id].has_cpu = true; + numa_info[props->node_id].initiator = props->node_id; + } } if (!match) { @@ -784,6 +758,7 @@ static void smp_parse(MachineState *ms, QemuOpts *opts) ms->smp.cpus = cpus; ms->smp.cores = cores; ms->smp.threads = threads; + ms->smp.sockets = sockets; } if (ms->smp.cpus > 1) { @@ -808,23 +783,6 @@ static void machine_class_init(ObjectClass *oc, void *data) mc->numa_mem_align_shift = 23; mc->numa_auto_assign_ram = numa_default_auto_assign_ram; - object_class_property_add_str(oc, "accel", - machine_get_accel, machine_set_accel, &error_abort); - object_class_property_set_description(oc, "accel", - "Accelerator list", &error_abort); - - object_class_property_add(oc, "kernel-irqchip", "on|off|split", - NULL, machine_set_kernel_irqchip, - NULL, NULL, &error_abort); - object_class_property_set_description(oc, "kernel-irqchip", - "Configure KVM in-kernel irqchip", &error_abort); - - object_class_property_add(oc, "kvm-shadow-mem", "int", - machine_get_kvm_shadow_mem, machine_set_kvm_shadow_mem, - NULL, NULL, &error_abort); - object_class_property_set_description(oc, "kvm-shadow-mem", - "KVM shadow MMU size", &error_abort); - object_class_property_add_str(oc, "kernel", machine_get_kernel, machine_set_kernel, &error_abort); object_class_property_set_description(oc, "kernel", @@ -882,12 +840,6 @@ static void machine_class_init(ObjectClass *oc, void *data) object_class_property_set_description(oc, "graphics", "Set on/off to enable/disable graphics emulation", &error_abort); - object_class_property_add_bool(oc, "igd-passthru", - machine_get_igd_gfx_passthru, machine_set_igd_gfx_passthru, - &error_abort); - object_class_property_set_description(oc, "igd-passthru", - "Set on/off to enable/disable igd passthrou", &error_abort); - object_class_property_add_str(oc, "firmware", machine_get_firmware, machine_set_firmware, &error_abort); @@ -930,9 +882,6 @@ static void machine_initfn(Object *obj) MachineState *ms = MACHINE(obj); MachineClass *mc = MACHINE_GET_CLASS(obj); - ms->kernel_irqchip_allowed = true; - ms->kernel_irqchip_split = mc->default_kernel_irqchip_split; - ms->kvm_shadow_mem = -1; ms->dump_guest_core = true; ms->mem_merge = true; ms->enable_graphics = true; @@ -958,10 +907,25 @@ static void machine_initfn(Object *obj) NULL); } - if (mc->numa_mem_supported) { + if (mc->cpu_index_to_instance_props && mc->get_default_cpu_node_id) { ms->numa_state = g_new0(NumaState, 1); + object_property_add_bool(obj, "hmat", + machine_get_hmat, machine_set_hmat, + &error_abort); + object_property_set_description(obj, "hmat", + "Set on/off to enable/disable " + "ACPI Heterogeneous Memory Attribute " + "Table (HMAT)", NULL); } + object_property_add_str(obj, "memory-backend", + machine_get_memdev, machine_set_memdev, + &error_abort); + object_property_set_description(obj, "memory-backend", + "Set RAM backend" + "Valid value is ID of hostmem based backend", + &error_abort); + /* Register notifier when init is done for sysbus sanity checks */ ms->sysbus_notifier.notify = machine_init_notify; qemu_add_machine_init_done_notifier(&ms->sysbus_notifier); @@ -971,7 +935,6 @@ static void machine_finalize(Object *obj) { MachineState *ms = MACHINE(obj); - g_free(ms->accel); g_free(ms->kernel_filename); g_free(ms->initrd_filename); g_free(ms->kernel_cmdline); @@ -989,26 +952,6 @@ bool machine_usb(MachineState *machine) return machine->usb; } -bool machine_kernel_irqchip_allowed(MachineState *machine) -{ - return machine->kernel_irqchip_allowed; -} - -bool machine_kernel_irqchip_required(MachineState *machine) -{ - return machine->kernel_irqchip_required; -} - -bool machine_kernel_irqchip_split(MachineState *machine) -{ - return machine->kernel_irqchip_split; -} - -int machine_kvm_shadow_mem(MachineState *machine) -{ - return machine->kvm_shadow_mem; -} - int machine_phandle_start(MachineState *machine) { return machine->phandle_start; @@ -1048,6 +991,32 @@ static char *cpu_slot_to_string(const CPUArchId *cpu) return g_string_free(s, false); } +static void numa_validate_initiator(NumaState *numa_state) +{ + int i; + NodeInfo *numa_info = numa_state->nodes; + + for (i = 0; i < numa_state->num_nodes; i++) { + if (numa_info[i].initiator == MAX_NODES) { + error_report("The initiator of NUMA node %d is missing, use " + "'-numa node,initiator' option to declare it", i); + exit(1); + } + + if (!numa_info[numa_info[i].initiator].present) { + error_report("NUMA node %" PRIu16 " is missing, use " + "'-numa node' option to declare it first", + numa_info[i].initiator); + exit(1); + } + + if (!numa_info[numa_info[i].initiator].has_cpu) { + error_report("The initiator of NUMA node %d is invalid", i); + exit(1); + } + } +} + static void machine_numa_finish_cpu_init(MachineState *machine) { int i; @@ -1088,6 +1057,11 @@ static void machine_numa_finish_cpu_init(MachineState *machine) machine_set_cpu_numa_node(machine, &props, &error_fatal); } } + + if (machine->numa_state->hmat_enabled) { + numa_validate_initiator(machine->numa_state); + } + if (s->len && !qtest_enabled()) { warn_report("CPU(s) not present in any NUMA nodes: %s", s->str); @@ -1098,11 +1072,34 @@ static void machine_numa_finish_cpu_init(MachineState *machine) g_string_free(s, true); } +MemoryRegion *machine_consume_memdev(MachineState *machine, + HostMemoryBackend *backend) +{ + MemoryRegion *ret = host_memory_backend_get_memory(backend); + + if (memory_region_is_mapped(ret)) { + char *path = object_get_canonical_path_component(OBJECT(backend)); + error_report("memory backend %s can't be used multiple times.", path); + g_free(path); + exit(EXIT_FAILURE); + } + host_memory_backend_set_mapped(backend, true); + vmstate_register_ram_global(ret); + return ret; +} + void machine_run_board_init(MachineState *machine) { MachineClass *machine_class = MACHINE_GET_CLASS(machine); - if (machine_class->numa_mem_supported) { + if (machine->ram_memdev_id) { + Object *o; + o = object_resolve_path_type(machine->ram_memdev_id, + TYPE_MEMORY_BACKEND, NULL); + machine->ram = machine_consume_memdev(machine, MEMORY_BACKEND(o)); + } + + if (machine->numa_state) { numa_complete_configuration(machine); if (machine->numa_state->num_nodes) { machine_numa_finish_cpu_init(machine); diff --git a/hw/core/null-machine.c b/hw/core/null-machine.c index 1aa0a9a01a..cb47d9d4f8 100644 --- a/hw/core/null-machine.c +++ b/hw/core/null-machine.c @@ -32,11 +32,8 @@ static void machine_none_init(MachineState *mch) } /* RAM at address zero */ - if (mch->ram_size) { - MemoryRegion *ram = g_new(MemoryRegion, 1); - - memory_region_allocate_system_memory(ram, NULL, "ram", mch->ram_size); - memory_region_add_subregion(get_system_memory(), 0, ram); + if (mch->ram) { + memory_region_add_subregion(get_system_memory(), 0, mch->ram); } if (mch->kernel_filename) { @@ -52,6 +49,7 @@ static void machine_none_machine_init(MachineClass *mc) mc->init = machine_none_init; mc->max_cpus = 1; mc->default_ram_size = 0; + mc->default_ram_id = "ram"; } DEFINE_MACHINE("none", machine_none_machine_init) diff --git a/hw/core/numa.c b/hw/core/numa.c index e3332a984f..316bc50d75 100644 --- a/hw/core/numa.c +++ b/hw/core/numa.c @@ -23,6 +23,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "sysemu/hostmem.h" #include "sysemu/numa.h" #include "sysemu/sysemu.h" @@ -51,6 +52,11 @@ QemuOptsList qemu_numa_opts = { }; static int have_memdevs; +bool numa_uses_legacy_mem(void) +{ + return !have_memdevs; +} + static int have_mem; static int max_numa_nodeid; /* Highest specified NUMA node ID, plus one. * For all nodes, nodeid < max_numa_nodeid @@ -83,10 +89,6 @@ static void parse_numa_node(MachineState *ms, NumaNodeOptions *node, return; } - if (!mc->cpu_index_to_instance_props || !mc->get_default_cpu_node_id) { - error_setg(errp, "NUMA is not supported by this machine-type"); - return; - } for (cpus = node->cpus; cpus; cpus = cpus->next) { CpuInstanceProperties props; if (cpus->value >= max_cpus) { @@ -133,6 +135,29 @@ static void parse_numa_node(MachineState *ms, NumaNodeOptions *node, numa_info[nodenr].node_mem = object_property_get_uint(o, "size", NULL); numa_info[nodenr].node_memdev = MEMORY_BACKEND(o); } + + /* + * If not set the initiator, set it to MAX_NODES. And if + * HMAT is enabled and this node has no cpus, QEMU will raise error. + */ + numa_info[nodenr].initiator = MAX_NODES; + if (node->has_initiator) { + if (!ms->numa_state->hmat_enabled) { + error_setg(errp, "ACPI Heterogeneous Memory Attribute Table " + "(HMAT) is disabled, enable it with -machine hmat=on " + "before using any of hmat specific options"); + return; + } + + if (node->initiator >= MAX_NODES) { + error_report("The initiator id %" PRIu16 " expects an integer " + "between 0 and %d", node->initiator, + MAX_NODES - 1); + return; + } + + numa_info[nodenr].initiator = node->initiator; + } numa_info[nodenr].present = true; max_numa_nodeid = MAX(max_numa_nodeid, nodenr + 1); ms->numa_state->num_nodes++; @@ -175,12 +200,258 @@ void parse_numa_distance(MachineState *ms, NumaDistOptions *dist, Error **errp) ms->numa_state->have_numa_distance = true; } +void parse_numa_hmat_lb(NumaState *numa_state, NumaHmatLBOptions *node, + Error **errp) +{ + int i, first_bit, last_bit; + uint64_t max_entry, temp_base, bitmap_copy; + NodeInfo *numa_info = numa_state->nodes; + HMAT_LB_Info *hmat_lb = + numa_state->hmat_lb[node->hierarchy][node->data_type]; + HMAT_LB_Data lb_data = {}; + HMAT_LB_Data *lb_temp; + + /* Error checking */ + if (node->initiator > numa_state->num_nodes) { + error_setg(errp, "Invalid initiator=%d, it should be less than %d", + node->initiator, numa_state->num_nodes); + return; + } + if (node->target > numa_state->num_nodes) { + error_setg(errp, "Invalid target=%d, it should be less than %d", + node->target, numa_state->num_nodes); + return; + } + if (!numa_info[node->initiator].has_cpu) { + error_setg(errp, "Invalid initiator=%d, it isn't an " + "initiator proximity domain", node->initiator); + return; + } + if (!numa_info[node->target].present) { + error_setg(errp, "The target=%d should point to an existing node", + node->target); + return; + } + + if (!hmat_lb) { + hmat_lb = g_malloc0(sizeof(*hmat_lb)); + numa_state->hmat_lb[node->hierarchy][node->data_type] = hmat_lb; + hmat_lb->list = g_array_new(false, true, sizeof(HMAT_LB_Data)); + } + hmat_lb->hierarchy = node->hierarchy; + hmat_lb->data_type = node->data_type; + lb_data.initiator = node->initiator; + lb_data.target = node->target; + + if (node->data_type <= HMATLB_DATA_TYPE_WRITE_LATENCY) { + /* Input latency data */ + + if (!node->has_latency) { + error_setg(errp, "Missing 'latency' option"); + return; + } + if (node->has_bandwidth) { + error_setg(errp, "Invalid option 'bandwidth' since " + "the data type is latency"); + return; + } + + /* Detect duplicate configuration */ + for (i = 0; i < hmat_lb->list->len; i++) { + lb_temp = &g_array_index(hmat_lb->list, HMAT_LB_Data, i); + + if (node->initiator == lb_temp->initiator && + node->target == lb_temp->target) { + error_setg(errp, "Duplicate configuration of the latency for " + "initiator=%d and target=%d", node->initiator, + node->target); + return; + } + } + + hmat_lb->base = hmat_lb->base ? hmat_lb->base : UINT64_MAX; + + if (node->latency) { + /* Calculate the temporary base and compressed latency */ + max_entry = node->latency; + temp_base = 1; + while (QEMU_IS_ALIGNED(max_entry, 10)) { + max_entry /= 10; + temp_base *= 10; + } + + /* Calculate the max compressed latency */ + temp_base = MIN(hmat_lb->base, temp_base); + max_entry = node->latency / hmat_lb->base; + max_entry = MAX(hmat_lb->range_bitmap, max_entry); + + /* + * For latency hmat_lb->range_bitmap record the max compressed + * latency which should be less than 0xFFFF (UINT16_MAX) + */ + if (max_entry >= UINT16_MAX) { + error_setg(errp, "Latency %" PRIu64 " between initiator=%d and " + "target=%d should not differ from previously entered " + "min or max values on more than %d", node->latency, + node->initiator, node->target, UINT16_MAX - 1); + return; + } else { + hmat_lb->base = temp_base; + hmat_lb->range_bitmap = max_entry; + } + + /* + * Set lb_info_provided bit 0 as 1, + * latency information is provided + */ + numa_info[node->target].lb_info_provided |= BIT(0); + } + lb_data.data = node->latency; + } else if (node->data_type >= HMATLB_DATA_TYPE_ACCESS_BANDWIDTH) { + /* Input bandwidth data */ + if (!node->has_bandwidth) { + error_setg(errp, "Missing 'bandwidth' option"); + return; + } + if (node->has_latency) { + error_setg(errp, "Invalid option 'latency' since " + "the data type is bandwidth"); + return; + } + if (!QEMU_IS_ALIGNED(node->bandwidth, MiB)) { + error_setg(errp, "Bandwidth %" PRIu64 " between initiator=%d and " + "target=%d should be 1MB aligned", node->bandwidth, + node->initiator, node->target); + return; + } + + /* Detect duplicate configuration */ + for (i = 0; i < hmat_lb->list->len; i++) { + lb_temp = &g_array_index(hmat_lb->list, HMAT_LB_Data, i); + + if (node->initiator == lb_temp->initiator && + node->target == lb_temp->target) { + error_setg(errp, "Duplicate configuration of the bandwidth for " + "initiator=%d and target=%d", node->initiator, + node->target); + return; + } + } + + hmat_lb->base = hmat_lb->base ? hmat_lb->base : 1; + + if (node->bandwidth) { + /* Keep bitmap unchanged when bandwidth out of range */ + bitmap_copy = hmat_lb->range_bitmap; + bitmap_copy |= node->bandwidth; + first_bit = ctz64(bitmap_copy); + temp_base = UINT64_C(1) << first_bit; + max_entry = node->bandwidth / temp_base; + last_bit = 64 - clz64(bitmap_copy); + + /* + * For bandwidth, first_bit record the base unit of bandwidth bits, + * last_bit record the last bit of the max bandwidth. The max + * compressed bandwidth should be less than 0xFFFF (UINT16_MAX) + */ + if ((last_bit - first_bit) > UINT16_BITS || + max_entry >= UINT16_MAX) { + error_setg(errp, "Bandwidth %" PRIu64 " between initiator=%d " + "and target=%d should not differ from previously " + "entered values on more than %d", node->bandwidth, + node->initiator, node->target, UINT16_MAX - 1); + return; + } else { + hmat_lb->base = temp_base; + hmat_lb->range_bitmap = bitmap_copy; + } + + /* + * Set lb_info_provided bit 1 as 1, + * bandwidth information is provided + */ + numa_info[node->target].lb_info_provided |= BIT(1); + } + lb_data.data = node->bandwidth; + } else { + assert(0); + } + + g_array_append_val(hmat_lb->list, lb_data); +} + +void parse_numa_hmat_cache(MachineState *ms, NumaHmatCacheOptions *node, + Error **errp) +{ + int nb_numa_nodes = ms->numa_state->num_nodes; + NodeInfo *numa_info = ms->numa_state->nodes; + NumaHmatCacheOptions *hmat_cache = NULL; + + if (node->node_id >= nb_numa_nodes) { + error_setg(errp, "Invalid node-id=%" PRIu32 ", it should be less " + "than %d", node->node_id, nb_numa_nodes); + return; + } + + if (numa_info[node->node_id].lb_info_provided != (BIT(0) | BIT(1))) { + error_setg(errp, "The latency and bandwidth information of " + "node-id=%" PRIu32 " should be provided before memory side " + "cache attributes", node->node_id); + return; + } + + if (node->level < 1 || node->level >= HMAT_LB_LEVELS) { + error_setg(errp, "Invalid level=%" PRIu8 ", it should be larger than 0 " + "and less than or equal to %d", node->level, + HMAT_LB_LEVELS - 1); + return; + } + + assert(node->associativity < HMAT_CACHE_ASSOCIATIVITY__MAX); + assert(node->policy < HMAT_CACHE_WRITE_POLICY__MAX); + if (ms->numa_state->hmat_cache[node->node_id][node->level]) { + error_setg(errp, "Duplicate configuration of the side cache for " + "node-id=%" PRIu32 " and level=%" PRIu8, + node->node_id, node->level); + return; + } + + if ((node->level > 1) && + ms->numa_state->hmat_cache[node->node_id][node->level - 1] && + (node->size >= + ms->numa_state->hmat_cache[node->node_id][node->level - 1]->size)) { + error_setg(errp, "Invalid size=%" PRIu64 ", the size of level=%" PRIu8 + " should be less than the size(%" PRIu64 ") of " + "level=%u", node->size, node->level, + ms->numa_state->hmat_cache[node->node_id] + [node->level - 1]->size, + node->level - 1); + return; + } + + if ((node->level < HMAT_LB_LEVELS - 1) && + ms->numa_state->hmat_cache[node->node_id][node->level + 1] && + (node->size <= + ms->numa_state->hmat_cache[node->node_id][node->level + 1]->size)) { + error_setg(errp, "Invalid size=%" PRIu64 ", the size of level=%" PRIu8 + " should be larger than the size(%" PRIu64 ") of " + "level=%u", node->size, node->level, + ms->numa_state->hmat_cache[node->node_id] + [node->level + 1]->size, + node->level + 1); + return; + } + + hmat_cache = g_malloc0(sizeof(*hmat_cache)); + memcpy(hmat_cache, node, sizeof(*hmat_cache)); + ms->numa_state->hmat_cache[node->node_id][node->level] = hmat_cache; +} + void set_numa_options(MachineState *ms, NumaOptions *object, Error **errp) { Error *err = NULL; - MachineClass *mc = MACHINE_GET_CLASS(ms); - if (!mc->numa_mem_supported) { + if (!ms->numa_state) { error_setg(errp, "NUMA is not supported by this machine-type"); goto end; } @@ -213,6 +484,32 @@ void set_numa_options(MachineState *ms, NumaOptions *object, Error **errp) machine_set_cpu_numa_node(ms, qapi_NumaCpuOptions_base(&object->u.cpu), &err); break; + case NUMA_OPTIONS_TYPE_HMAT_LB: + if (!ms->numa_state->hmat_enabled) { + error_setg(errp, "ACPI Heterogeneous Memory Attribute Table " + "(HMAT) is disabled, enable it with -machine hmat=on " + "before using any of hmat specific options"); + return; + } + + parse_numa_hmat_lb(ms->numa_state, &object->u.hmat_lb, &err); + if (err) { + goto end; + } + break; + case NUMA_OPTIONS_TYPE_HMAT_CACHE: + if (!ms->numa_state->hmat_enabled) { + error_setg(errp, "ACPI Heterogeneous Memory Attribute Table " + "(HMAT) is disabled, enable it with -machine hmat=on " + "before using any of hmat specific options"); + return; + } + + parse_numa_hmat_cache(ms, &object->u.hmat_cache, &err); + if (err) { + goto end; + } + break; default: abort(); } @@ -360,6 +657,23 @@ void numa_default_auto_assign_ram(MachineClass *mc, NodeInfo *nodes, nodes[i].node_mem = size - usedmem; } +static void numa_init_memdev_container(MachineState *ms, MemoryRegion *ram) +{ + int i; + uint64_t addr = 0; + + for (i = 0; i < ms->numa_state->num_nodes; i++) { + uint64_t size = ms->numa_state->nodes[i].node_mem; + HostMemoryBackend *backend = ms->numa_state->nodes[i].node_memdev; + if (!backend) { + continue; + } + MemoryRegion *seg = machine_consume_memdev(ms, backend); + memory_region_add_subregion(ram, addr, seg); + addr += size; + } +} + void numa_complete_configuration(MachineState *ms) { int i; @@ -442,6 +756,12 @@ void numa_complete_configuration(MachineState *ms) exit(1); } + if (!numa_uses_legacy_mem() && mc->default_ram_id) { + ms->ram = g_new(MemoryRegion, 1); + memory_region_init(ms->ram, OBJECT(ms), mc->default_ram_id, + ram_size); + numa_init_memdev_container(ms, ms->ram); + } /* QEMU needs at least all unique node pair distances to build * the whole NUMA distance table. QEMU treats the distance table * as symmetric by default, i.e. distance A->B == distance B->A. @@ -486,79 +806,6 @@ void numa_cpu_pre_plug(const CPUArchId *slot, DeviceState *dev, Error **errp) } } -static void allocate_system_memory_nonnuma(MemoryRegion *mr, Object *owner, - const char *name, - uint64_t ram_size) -{ - if (mem_path) { -#ifdef __linux__ - Error *err = NULL; - memory_region_init_ram_from_file(mr, owner, name, ram_size, 0, 0, - mem_path, &err); - if (err) { - error_report_err(err); - if (mem_prealloc) { - exit(1); - } - warn_report("falling back to regular RAM allocation"); - error_printf("This is deprecated. Make sure that -mem-path " - " specified path has sufficient resources to allocate" - " -m specified RAM amount\n"); - /* Legacy behavior: if allocation failed, fall back to - * regular RAM allocation. - */ - mem_path = NULL; - memory_region_init_ram_nomigrate(mr, owner, name, ram_size, &error_fatal); - } -#else - fprintf(stderr, "-mem-path not supported on this host\n"); - exit(1); -#endif - } else { - memory_region_init_ram_nomigrate(mr, owner, name, ram_size, &error_fatal); - } - vmstate_register_ram_global(mr); -} - -void memory_region_allocate_system_memory(MemoryRegion *mr, Object *owner, - const char *name, - uint64_t ram_size) -{ - uint64_t addr = 0; - int i; - MachineState *ms = MACHINE(qdev_get_machine()); - - if (ms->numa_state == NULL || - ms->numa_state->num_nodes == 0 || !have_memdevs) { - allocate_system_memory_nonnuma(mr, owner, name, ram_size); - return; - } - - memory_region_init(mr, owner, name, ram_size); - for (i = 0; i < ms->numa_state->num_nodes; i++) { - uint64_t size = ms->numa_state->nodes[i].node_mem; - HostMemoryBackend *backend = ms->numa_state->nodes[i].node_memdev; - if (!backend) { - continue; - } - MemoryRegion *seg = host_memory_backend_get_memory(backend); - - if (memory_region_is_mapped(seg)) { - char *path = object_get_canonical_path_component(OBJECT(backend)); - error_report("memory backend %s is used multiple times. Each " - "-numa option must use a different memdev value.", - path); - g_free(path); - exit(1); - } - - host_memory_backend_set_mapped(backend, true); - memory_region_add_subregion(mr, addr, seg); - vmstate_register_ram_global(seg); - addr += size; - } -} - static void numa_stat_memory_devices(NumaNodeMem node_mem[]) { MemoryDeviceInfoList *info_list = qmp_memory_device_list(); diff --git a/hw/core/or-irq.c b/hw/core/or-irq.c index 18d63831cd..d8f3754e96 100644 --- a/hw/core/or-irq.c +++ b/hw/core/or-irq.c @@ -58,7 +58,7 @@ static void or_irq_realize(DeviceState *dev, Error **errp) { qemu_or_irq *s = OR_IRQ(dev); - assert(s->num_lines < MAX_OR_LINES); + assert(s->num_lines <= MAX_OR_LINES); qdev_init_gpio_in(dev, or_irq_handler, s->num_lines); } @@ -125,7 +125,7 @@ static void or_irq_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->reset = or_irq_reset; - dc->props = or_irq_properties; + device_class_set_props(dc, or_irq_properties); dc->realize = or_irq_realize; dc->vmsd = &vmstate_or_irq; diff --git a/hw/core/platform-bus.c b/hw/core/platform-bus.c index edb0da1de8..d494e5cec1 100644 --- a/hw/core/platform-bus.c +++ b/hw/core/platform-bus.c @@ -187,7 +187,8 @@ static void platform_bus_realize(DeviceState *dev, Error **errp) d = SYS_BUS_DEVICE(dev); pbus = PLATFORM_BUS_DEVICE(dev); - memory_region_init(&pbus->mmio, NULL, "platform bus", pbus->mmio_size); + memory_region_init(&pbus->mmio, OBJECT(dev), "platform bus", + pbus->mmio_size); sysbus_init_mmio(d, &pbus->mmio); pbus->used_irqs = bitmap_new(pbus->num_irqs); @@ -211,7 +212,7 @@ static void platform_bus_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = platform_bus_realize; - dc->props = platform_bus_properties; + device_class_set_props(dc, platform_bus_properties); } static const TypeInfo platform_bus_info = { diff --git a/hw/core/qdev-properties.c b/hw/core/qdev-properties.c index ac28890e5a..2047114fca 100644 --- a/hw/core/qdev-properties.c +++ b/hw/core/qdev-properties.c @@ -8,6 +8,7 @@ #include "qapi/qmp/qerror.h" #include "qemu/ctype.h" #include "qemu/error-report.h" +#include "qapi/qapi-types-migration.h" #include "hw/block/block.h" #include "net/hub.h" #include "qapi/visitor.h" @@ -73,12 +74,10 @@ static void set_enum(Object *obj, Visitor *v, const char *name, void *opaque, visit_type_enum(v, prop->name, ptr, prop->info->enum_table, errp); } -static void set_default_value_enum(Object *obj, const Property *prop) +static void set_default_value_enum(ObjectProperty *op, const Property *prop) { - object_property_set_str(obj, - qapi_enum_lookup(prop->info->enum_table, - prop->defval.i), - prop->name, &error_abort); + object_property_set_default_str(op, + qapi_enum_lookup(prop->info->enum_table, prop->defval.i)); } /* Bit */ @@ -132,9 +131,9 @@ static void prop_set_bit(Object *obj, Visitor *v, const char *name, bit_prop_set(dev, prop, value); } -static void set_default_value_bool(Object *obj, const Property *prop) +static void set_default_value_bool(ObjectProperty *op, const Property *prop) { - object_property_set_bool(obj, prop->defval.u, prop->name, &error_abort); + object_property_set_default_bool(op, prop->defval.u); } const PropertyInfo qdev_prop_bit = { @@ -265,14 +264,14 @@ static void set_uint8(Object *obj, Visitor *v, const char *name, void *opaque, visit_type_uint8(v, name, ptr, errp); } -static void set_default_value_int(Object *obj, const Property *prop) +static void set_default_value_int(ObjectProperty *op, const Property *prop) { - object_property_set_int(obj, prop->defval.i, prop->name, &error_abort); + object_property_set_default_int(op, prop->defval.i); } -static void set_default_value_uint(Object *obj, const Property *prop) +static void set_default_value_uint(ObjectProperty *op, const Property *prop) { - object_property_set_uint(obj, prop->defval.u, prop->name, &error_abort); + object_property_set_default_uint(op, prop->defval.u); } const PropertyInfo qdev_prop_uint8 = { @@ -501,13 +500,6 @@ const PropertyInfo qdev_prop_string = { .set = set_string, }; -/* --- pointer --- */ - -/* Not a proper property, just for dirty hacks. TODO Remove it! */ -const PropertyInfo qdev_prop_ptr = { - .name = "ptr", -}; - /* --- mac address --- */ /* @@ -648,6 +640,18 @@ const PropertyInfo qdev_prop_fdc_drive_type = { .set_default_value = set_default_value_enum, }; +/* --- MultiFDCompression --- */ + +const PropertyInfo qdev_prop_multifd_compression = { + .name = "MultiFDCompression", + .description = "multifd_compression values, " + "none/zlib/zstd", + .enum_table = &MultiFDCompression_lookup, + .get = get_enum, + .set = set_enum, + .set_default_value = set_default_value_enum, +}; + /* --- pci address --- */ /* @@ -932,9 +936,9 @@ static void set_uuid(Object *obj, Visitor *v, const char *name, void *opaque, g_free(str); } -static void set_default_uuid_auto(Object *obj, const Property *prop) +static void set_default_uuid_auto(ObjectProperty *op, const Property *prop) { - object_property_set_str(obj, UUID_VALUE_AUTO, prop->name, &error_abort); + object_property_set_default_str(op, UUID_VALUE_AUTO); } const PropertyInfo qdev_prop_uuid = { @@ -1078,7 +1082,7 @@ static Property *qdev_prop_find(DeviceState *dev, const char *name) /* device properties */ class = object_get_class(OBJECT(dev)); do { - prop = qdev_prop_walk(DEVICE_CLASS(class)->props, name); + prop = qdev_prop_walk(DEVICE_CLASS(class)->props_, name); if (prop) { return prop; } @@ -1165,17 +1169,6 @@ void qdev_prop_set_enum(DeviceState *dev, const char *name, int value) name, &error_abort); } -void qdev_prop_set_ptr(DeviceState *dev, const char *name, void *value) -{ - Property *prop; - void **ptr; - - prop = qdev_prop_find(dev, name); - assert(prop && prop->info == &qdev_prop_ptr); - ptr = qdev_get_prop_ptr(dev, prop); - *ptr = value; -} - static GPtrArray *global_props(void) { static GPtrArray *gp; @@ -1261,15 +1254,13 @@ const PropertyInfo qdev_prop_size = { /* --- object link property --- */ -static void create_link_property(Object *obj, Property *prop, Error **errp) +static void create_link_property(ObjectClass *oc, Property *prop, Error **errp) { - Object **child = qdev_get_prop_ptr(DEVICE(obj), prop); - - object_property_add_link(obj, prop->name, prop->link_type, - child, - qdev_prop_allow_set_link_before_realize, - OBJ_PROP_LINK_STRONG, - errp); + object_class_property_add_link(oc, prop->name, prop->link_type, + prop->offset, + qdev_prop_allow_set_link_before_realize, + OBJ_PROP_LINK_STRONG, + errp); } const PropertyInfo qdev_prop_link = { diff --git a/hw/core/qdev.c b/hw/core/qdev.c index cf1ba28fe3..85f062def7 100644 --- a/hw/core/qdev.c +++ b/hw/core/qdev.c @@ -38,6 +38,7 @@ #include "hw/boards.h" #include "hw/sysbus.h" #include "migration/vmstate.h" +#include "trace.h" bool qdev_hotplug = false; static bool qdev_hot_added = false; @@ -95,21 +96,31 @@ static void bus_add_child(BusState *bus, DeviceState *child) void qdev_set_parent_bus(DeviceState *dev, BusState *bus) { - bool replugging = dev->parent_bus != NULL; + BusState *old_parent_bus = dev->parent_bus; - if (replugging) { - /* Keep a reference to the device while it's not plugged into + if (old_parent_bus) { + trace_qdev_update_parent_bus(dev, object_get_typename(OBJECT(dev)), + old_parent_bus, object_get_typename(OBJECT(old_parent_bus)), + OBJECT(bus), object_get_typename(OBJECT(bus))); + /* + * Keep a reference to the device while it's not plugged into * any bus, to avoid it potentially evaporating when it is * dereffed in bus_remove_child(). + * Also keep the ref of the parent bus until the end, so that + * we can safely call resettable_change_parent() below. */ object_ref(OBJECT(dev)); bus_remove_child(dev->parent_bus, dev); - object_unref(OBJECT(dev->parent_bus)); } dev->parent_bus = bus; object_ref(OBJECT(bus)); bus_add_child(bus, dev); - if (replugging) { + if (dev->realized) { + resettable_change_parent(OBJECT(dev), OBJECT(bus), + OBJECT(old_parent_bus)); + } + if (old_parent_bus) { + object_unref(OBJECT(old_parent_bus)); object_unref(OBJECT(dev)); } } @@ -296,9 +307,21 @@ HotplugHandler *qdev_get_hotplug_handler(DeviceState *dev) return hotplug_ctrl; } +static int qdev_prereset(DeviceState *dev, void *opaque) +{ + trace_qdev_reset_tree(dev, object_get_typename(OBJECT(dev))); + return 0; +} + +static int qbus_prereset(BusState *bus, void *opaque) +{ + trace_qbus_reset_tree(bus, object_get_typename(OBJECT(bus))); + return 0; +} + static int qdev_reset_one(DeviceState *dev, void *opaque) { - device_reset(dev); + device_legacy_reset(dev); return 0; } @@ -306,6 +329,7 @@ static int qdev_reset_one(DeviceState *dev, void *opaque) static int qbus_reset_one(BusState *bus, void *opaque) { BusClass *bc = BUS_GET_CLASS(bus); + trace_qbus_reset(bus, object_get_typename(OBJECT(bus))); if (bc->reset) { bc->reset(bus); } @@ -314,7 +338,9 @@ static int qbus_reset_one(BusState *bus, void *opaque) void qdev_reset_all(DeviceState *dev) { - qdev_walk_children(dev, NULL, NULL, qdev_reset_one, qbus_reset_one, NULL); + trace_qdev_reset_all(dev, object_get_typename(OBJECT(dev))); + qdev_walk_children(dev, qdev_prereset, qbus_prereset, + qdev_reset_one, qbus_reset_one, NULL); } void qdev_reset_all_fn(void *opaque) @@ -324,7 +350,9 @@ void qdev_reset_all_fn(void *opaque) void qbus_reset_all(BusState *bus) { - qbus_walk_children(bus, NULL, NULL, qdev_reset_one, qbus_reset_one, NULL); + trace_qbus_reset_all(bus, object_get_typename(OBJECT(bus))); + qbus_walk_children(bus, qdev_prereset, qbus_prereset, + qdev_reset_one, qbus_reset_one, NULL); } void qbus_reset_all_fn(void *opaque) @@ -333,6 +361,33 @@ void qbus_reset_all_fn(void *opaque) qbus_reset_all(bus); } +void device_cold_reset(DeviceState *dev) +{ + resettable_reset(OBJECT(dev), RESET_TYPE_COLD); +} + +bool device_is_in_reset(DeviceState *dev) +{ + return resettable_is_in_reset(OBJECT(dev)); +} + +static ResettableState *device_get_reset_state(Object *obj) +{ + DeviceState *dev = DEVICE(obj); + return &dev->reset; +} + +static void device_reset_child_foreach(Object *obj, ResettableChildCallback cb, + void *opaque, ResetType type) +{ + DeviceState *dev = DEVICE(obj); + BusState *bus; + + QLIST_FOREACH(bus, &dev->child_bus, sibling) { + cb(OBJECT(bus), opaque, type); + } +} + /* can be used as ->unplug() callback for the simple cases */ void qdev_simple_device_unplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) @@ -394,11 +449,8 @@ static NamedGPIOList *qdev_get_named_gpio_list(DeviceState *dev, NamedGPIOList *ngl; QLIST_FOREACH(ngl, &dev->gpios, node) { - /* NULL is a valid and matchable name, otherwise do a normal - * strcmp match. - */ - if ((!ngl->name && !name) || - (name && ngl->name && strcmp(name, ngl->name) == 0)) { + /* NULL is a valid and matchable name. */ + if (g_strcmp0(name, ngl->name) == 0) { return ngl; } } @@ -505,7 +557,7 @@ void qdev_connect_gpio_out_named(DeviceState *dev, const char *name, int n, qemu_irq qdev_get_gpio_out_connector(DeviceState *dev, const char *name, int n) { - char *propname = g_strdup_printf("%s[%d]", + g_autofree char *propname = g_strdup_printf("%s[%d]", name ? name : "unnamed-gpio-out", n); qemu_irq ret = (qemu_irq)object_property_get_link(OBJECT(dev), propname, @@ -681,13 +733,11 @@ static void qdev_get_legacy_property(Object *obj, Visitor *v, } /** - * qdev_property_add_legacy: + * qdev_class_add_legacy_property: * @dev: Device to add the property to. * @prop: The qdev property definition. - * @errp: location to store error information. * * Add a legacy QOM property to @dev for qdev property @prop. - * On error, store error in @errp. * * Legacy properties are string versions of QOM properties. The format of * the string depends on the property type. Legacy properties are only @@ -696,77 +746,68 @@ static void qdev_get_legacy_property(Object *obj, Visitor *v, * Do not use this in new code! QOM Properties added through this interface * will be given names in the "legacy" namespace. */ -static void qdev_property_add_legacy(DeviceState *dev, Property *prop, - Error **errp) +static void qdev_class_add_legacy_property(DeviceClass *dc, Property *prop) { - gchar *name; + g_autofree char *name = NULL; /* Register pointer properties as legacy properties */ if (!prop->info->print && prop->info->get) { return; } - if (prop->info->create) { - return; - } - name = g_strdup_printf("legacy-%s", prop->name); - object_property_add(OBJECT(dev), name, "str", - prop->info->print ? qdev_get_legacy_property : prop->info->get, - NULL, - NULL, - prop, errp); - - g_free(name); + object_class_property_add(OBJECT_CLASS(dc), name, "str", + prop->info->print ? qdev_get_legacy_property : prop->info->get, + NULL, NULL, prop, &error_abort); } -/** - * qdev_property_add_static: - * @dev: Device to add the property to. - * @prop: The qdev property definition. - * @errp: location to store error information. - * - * Add a static QOM property to @dev for qdev property @prop. - * On error, store error in @errp. Static properties access data in a struct. - * The type of the QOM property is derived from prop->info. - */ -void qdev_property_add_static(DeviceState *dev, Property *prop, - Error **errp) +void qdev_property_add_static(DeviceState *dev, Property *prop) { - Error *local_err = NULL; Object *obj = OBJECT(dev); + ObjectProperty *op; - if (prop->info->create) { - prop->info->create(obj, prop, &local_err); - } else { - /* - * TODO qdev_prop_ptr does not have getters or setters. It must - * go now that it can be replaced with links. The test should be - * removed along with it: all static properties are read/write. - */ - if (!prop->info->get && !prop->info->set) { - return; - } - object_property_add(obj, prop->name, prop->info->name, - prop->info->get, prop->info->set, - prop->info->release, - prop, &local_err); - } + assert(!prop->info->create); - if (local_err) { - error_propagate(errp, local_err); - return; - } + op = object_property_add(obj, prop->name, prop->info->name, + prop->info->get, prop->info->set, + prop->info->release, + prop, &error_abort); object_property_set_description(obj, prop->name, prop->info->description, &error_abort); if (prop->set_default) { - prop->info->set_default_value(obj, prop); + prop->info->set_default_value(op, prop); + if (op->init) { + op->init(obj, op); + } } } +static void qdev_class_add_property(DeviceClass *klass, Property *prop) +{ + ObjectClass *oc = OBJECT_CLASS(klass); + + if (prop->info->create) { + prop->info->create(oc, prop, &error_abort); + } else { + ObjectProperty *op; + + op = object_class_property_add(oc, + prop->name, prop->info->name, + prop->info->get, prop->info->set, + prop->info->release, + prop, &error_abort); + if (prop->set_default) { + prop->info->set_default_value(op, prop); + } + } + object_class_property_set_description(oc, prop->name, + prop->info->description, + &error_abort); +} + /* @qdev_alias_all_properties - Add alias properties to the source object for * all qdev properties on the target DeviceState. */ @@ -779,7 +820,7 @@ void qdev_alias_all_properties(DeviceState *target, Object *source) do { DeviceClass *dc = DEVICE_CLASS(class); - for (prop = dc->props; prop && prop->name; prop++) { + for (prop = dc->props_; prop && prop->name; prop++) { object_property_add_alias(source, prop->name, OBJECT(target), prop->name, &error_abort); @@ -788,44 +829,18 @@ void qdev_alias_all_properties(DeviceState *target, Object *source) } while (class != object_class_by_name(TYPE_DEVICE)); } -static int qdev_add_hotpluggable_device(Object *obj, void *opaque) -{ - GSList **list = opaque; - DeviceState *dev = (DeviceState *)object_dynamic_cast(OBJECT(obj), - TYPE_DEVICE); - - if (dev == NULL) { - return 0; - } - - if (dev->realized && object_property_get_bool(obj, "hotpluggable", NULL)) { - *list = g_slist_append(*list, dev); - } - - return 0; -} - -GSList *qdev_build_hotpluggable_device_list(Object *peripheral) -{ - GSList *list = NULL; - - object_child_foreach(peripheral, qdev_add_hotpluggable_device, &list); - - return list; -} - static bool device_get_realized(Object *obj, Error **errp) { DeviceState *dev = DEVICE(obj); return dev->realized; } -static bool check_only_migratable(Object *obj, Error **err) +static bool check_only_migratable(Object *obj, Error **errp) { DeviceClass *dc = DEVICE_GET_CLASS(obj); if (!vmstate_check_only_migratable(dc->vmsd)) { - error_setg(err, "Device %s is not migratable, but " + error_setg(errp, "Device %s is not migratable, but " "--only-migratable was specified", object_get_typename(obj)); return false; @@ -874,10 +889,9 @@ static void device_set_realized(Object *obj, bool value, Error **errp) if (dc->realize) { dc->realize(dev, &local_err); - } - - if (local_err != NULL) { - goto fail; + if (local_err != NULL) { + goto fail; + } } DEVICE_LISTENER_CALL(realize, Forward, dev); @@ -890,7 +904,9 @@ static void device_set_realized(Object *obj, bool value, Error **errp) dev->canonical_path = object_get_canonical_path(OBJECT(dev)); if (qdev_get_vmsd(dev)) { - if (vmstate_register_with_alias_id(dev, -1, qdev_get_vmsd(dev), dev, + if (vmstate_register_with_alias_id(VMSTATE_IF(dev), + VMSTATE_INSTANCE_ID_ANY, + qdev_get_vmsd(dev), dev, dev->instance_id_alias, dev->alias_required_for_version, &local_err) < 0) { @@ -898,6 +914,12 @@ static void device_set_realized(Object *obj, bool value, Error **errp) } } + /* + * Clear the reset state, in case the object was previously unrealized + * with a dirty state. + */ + resettable_state_clear(&dev->reset); + QLIST_FOREACH(bus, &dev->child_bus, sibling) { object_property_set_bool(OBJECT(bus), true, "realized", &local_err); @@ -906,7 +928,14 @@ static void device_set_realized(Object *obj, bool value, Error **errp) } } if (dev->hotplugged) { - device_reset(dev); + /* + * Reset the device, as well as its subtree which, at this point, + * should be realized too. + */ + resettable_assert_reset(OBJECT(dev), RESET_TYPE_COLD); + resettable_change_parent(OBJECT(dev), OBJECT(dev->parent_bus), + NULL); + resettable_release_reset(OBJECT(dev), RESET_TYPE_COLD); } dev->pending_deleted_event = false; @@ -918,27 +947,26 @@ static void device_set_realized(Object *obj, bool value, Error **errp) } } else if (!value && dev->realized) { - Error **local_errp = NULL; + /* We want local_err to track only the first error */ QLIST_FOREACH(bus, &dev->child_bus, sibling) { - local_errp = local_err ? NULL : &local_err; object_property_set_bool(OBJECT(bus), false, "realized", - local_errp); + local_err ? NULL : &local_err); } if (qdev_get_vmsd(dev)) { - vmstate_unregister(dev, qdev_get_vmsd(dev), dev); + vmstate_unregister(VMSTATE_IF(dev), qdev_get_vmsd(dev), dev); } if (dc->unrealize) { - local_errp = local_err ? NULL : &local_err; - dc->unrealize(dev, local_errp); + dc->unrealize(dev, local_err ? NULL : &local_err); } dev->pending_deleted_event = true; DEVICE_LISTENER_CALL(unrealize, Reverse, dev); + + if (local_err != NULL) { + goto fail; + } } - if (local_err != NULL) { - goto fail; - } - + assert(local_err == NULL); dev->realized = value; return; @@ -949,7 +977,7 @@ child_realize_fail: } if (qdev_get_vmsd(dev)) { - vmstate_unregister(dev, qdev_get_vmsd(dev), dev); + vmstate_unregister(VMSTATE_IF(dev), qdev_get_vmsd(dev), dev); } post_realize_fail: @@ -976,7 +1004,7 @@ static bool device_get_hotpluggable(Object *obj, Error **errp) qbus_is_hotpluggable(dev->parent_bus)); } -static bool device_get_hotplugged(Object *obj, Error **err) +static bool device_get_hotplugged(Object *obj, Error **errp) { DeviceState *dev = DEVICE(obj); @@ -986,8 +1014,6 @@ static bool device_get_hotplugged(Object *obj, Error **err) static void device_initfn(Object *obj) { DeviceState *dev = DEVICE(obj); - ObjectClass *class; - Property *prop; if (qdev_hotplug) { dev->hotplugged = 1; @@ -998,26 +1024,6 @@ static void device_initfn(Object *obj) dev->realized = false; dev->allow_unplug_during_migration = false; - object_property_add_bool(obj, "realized", - device_get_realized, device_set_realized, NULL); - object_property_add_bool(obj, "hotpluggable", - device_get_hotpluggable, NULL, NULL); - object_property_add_bool(obj, "hotplugged", - device_get_hotplugged, NULL, - &error_abort); - - class = object_get_class(OBJECT(dev)); - do { - for (prop = DEVICE_CLASS(class)->props; prop && prop->name; prop++) { - qdev_property_add_legacy(dev, prop, &error_abort); - qdev_property_add_static(dev, prop, &error_abort); - } - class = object_class_get_parent(class); - } while (class != object_class_by_name(TYPE_DEVICE)); - - object_property_add_link(OBJECT(dev), "parent_bus", TYPE_BUS, - (Object **)&dev->parent_bus, NULL, 0, - &error_abort); QLIST_INIT(&dev->gpios); } @@ -1067,7 +1073,7 @@ static void device_class_base_init(ObjectClass *class, void *data) /* We explicitly look up properties in the superclasses, * so do not propagate them to the subclasses. */ - klass->props = NULL; + klass->props_ = NULL; } static void device_unparent(Object *obj) @@ -1089,9 +1095,70 @@ static void device_unparent(Object *obj) } } +static char * +device_vmstate_if_get_id(VMStateIf *obj) +{ + DeviceState *dev = DEVICE(obj); + + return qdev_get_dev_path(dev); +} + +/** + * device_phases_reset: + * Transition reset method for devices to allow moving + * smoothly from legacy reset method to multi-phases + */ +static void device_phases_reset(DeviceState *dev) +{ + ResettableClass *rc = RESETTABLE_GET_CLASS(dev); + + if (rc->phases.enter) { + rc->phases.enter(OBJECT(dev), RESET_TYPE_COLD); + } + if (rc->phases.hold) { + rc->phases.hold(OBJECT(dev)); + } + if (rc->phases.exit) { + rc->phases.exit(OBJECT(dev)); + } +} + +static void device_transitional_reset(Object *obj) +{ + DeviceClass *dc = DEVICE_GET_CLASS(obj); + + /* + * This will call either @device_phases_reset (for multi-phases transitioned + * devices) or a device's specific method for not-yet transitioned devices. + * In both case, it does not reset children. + */ + if (dc->reset) { + dc->reset(DEVICE(obj)); + } +} + +/** + * device_get_transitional_reset: + * check if the device's class is ready for multi-phase + */ +static ResettableTrFunction device_get_transitional_reset(Object *obj) +{ + DeviceClass *dc = DEVICE_GET_CLASS(obj); + if (dc->reset != device_phases_reset) { + /* + * dc->reset has been overridden by a subclass, + * the device is not ready for multi phase yet. + */ + return device_transitional_reset; + } + return NULL; +} + static void device_class_init(ObjectClass *class, void *data) { DeviceClass *dc = DEVICE_CLASS(class); + VMStateIfClass *vc = VMSTATE_IF_CLASS(class); + ResettableClass *rc = RESETTABLE_CLASS(class); class->unparent = device_unparent; @@ -1103,6 +1170,49 @@ static void device_class_init(ObjectClass *class, void *data) */ dc->hotpluggable = true; dc->user_creatable = true; + vc->get_id = device_vmstate_if_get_id; + rc->get_state = device_get_reset_state; + rc->child_foreach = device_reset_child_foreach; + + /* + * @device_phases_reset is put as the default reset method below, allowing + * to do the multi-phase transition from base classes to leaf classes. It + * allows a legacy-reset Device class to extend a multi-phases-reset + * Device class for the following reason: + * + If a base class B has been moved to multi-phase, then it does not + * override this default reset method and may have defined phase methods. + * + A child class C (extending class B) which uses + * device_class_set_parent_reset() (or similar means) to override the + * reset method will still work as expected. @device_phases_reset function + * will be registered as the parent reset method and effectively call + * parent reset phases. + */ + dc->reset = device_phases_reset; + rc->get_transitional_function = device_get_transitional_reset; + + object_class_property_add_bool(class, "realized", + device_get_realized, device_set_realized, + &error_abort); + object_class_property_add_bool(class, "hotpluggable", + device_get_hotpluggable, NULL, + &error_abort); + object_class_property_add_bool(class, "hotplugged", + device_get_hotplugged, NULL, + &error_abort); + object_class_property_add_link(class, "parent_bus", TYPE_BUS, + offsetof(DeviceState, parent_bus), NULL, 0, + &error_abort); +} + +void device_class_set_props(DeviceClass *dc, Property *props) +{ + Property *prop; + + dc->props_ = props; + for (prop = props; prop && prop->name; prop++) { + qdev_class_add_legacy_property(dc, prop); + qdev_class_add_property(dc, prop); + } } void device_class_set_parent_reset(DeviceClass *dc, @@ -1129,10 +1239,11 @@ void device_class_set_parent_unrealize(DeviceClass *dc, dc->unrealize = dev_unrealize; } -void device_reset(DeviceState *dev) +void device_legacy_reset(DeviceState *dev) { DeviceClass *klass = DEVICE_GET_CLASS(dev); + trace_qdev_reset(dev, object_get_typename(OBJECT(dev))); if (klass->reset) { klass->reset(dev); } @@ -1160,6 +1271,11 @@ static const TypeInfo device_type_info = { .class_init = device_class_init, .abstract = true, .class_size = sizeof(DeviceClass), + .interfaces = (InterfaceInfo[]) { + { TYPE_VMSTATE_IF }, + { TYPE_RESETTABLE_INTERFACE }, + { } + } }; static void qdev_register_types(void) diff --git a/hw/core/resettable.c b/hw/core/resettable.c new file mode 100644 index 0000000000..96a99ce39e --- /dev/null +++ b/hw/core/resettable.c @@ -0,0 +1,301 @@ +/* + * Resettable interface. + * + * Copyright (c) 2019 GreenSocs SAS + * + * Authors: + * Damien Hedde + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qemu/module.h" +#include "hw/resettable.h" +#include "trace.h" + +/** + * resettable_phase_enter/hold/exit: + * Function executing a phase recursively in a resettable object and its + * children. + */ +static void resettable_phase_enter(Object *obj, void *opaque, ResetType type); +static void resettable_phase_hold(Object *obj, void *opaque, ResetType type); +static void resettable_phase_exit(Object *obj, void *opaque, ResetType type); + +/** + * enter_phase_in_progress: + * True if we are currently in reset enter phase. + * + * exit_phase_in_progress: + * count the number of exit phase we are in. + * + * Note: These flags are only used to guarantee (using asserts) that the reset + * API is used correctly. We can use global variables because we rely on the + * iothread mutex to ensure only one reset operation is in a progress at a + * given time. + */ +static bool enter_phase_in_progress; +static unsigned exit_phase_in_progress; + +void resettable_reset(Object *obj, ResetType type) +{ + trace_resettable_reset(obj, type); + resettable_assert_reset(obj, type); + resettable_release_reset(obj, type); +} + +void resettable_assert_reset(Object *obj, ResetType type) +{ + /* TODO: change this assert when adding support for other reset types */ + assert(type == RESET_TYPE_COLD); + trace_resettable_reset_assert_begin(obj, type); + assert(!enter_phase_in_progress); + + enter_phase_in_progress = true; + resettable_phase_enter(obj, NULL, type); + enter_phase_in_progress = false; + + resettable_phase_hold(obj, NULL, type); + + trace_resettable_reset_assert_end(obj); +} + +void resettable_release_reset(Object *obj, ResetType type) +{ + /* TODO: change this assert when adding support for other reset types */ + assert(type == RESET_TYPE_COLD); + trace_resettable_reset_release_begin(obj, type); + assert(!enter_phase_in_progress); + + exit_phase_in_progress += 1; + resettable_phase_exit(obj, NULL, type); + exit_phase_in_progress -= 1; + + trace_resettable_reset_release_end(obj); +} + +bool resettable_is_in_reset(Object *obj) +{ + ResettableClass *rc = RESETTABLE_GET_CLASS(obj); + ResettableState *s = rc->get_state(obj); + + return s->count > 0; +} + +/** + * resettable_child_foreach: + * helper to avoid checking the existence of the method. + */ +static void resettable_child_foreach(ResettableClass *rc, Object *obj, + ResettableChildCallback cb, + void *opaque, ResetType type) +{ + if (rc->child_foreach) { + rc->child_foreach(obj, cb, opaque, type); + } +} + +/** + * resettable_get_tr_func: + * helper to fetch transitional reset callback if any. + */ +static ResettableTrFunction resettable_get_tr_func(ResettableClass *rc, + Object *obj) +{ + ResettableTrFunction tr_func = NULL; + if (rc->get_transitional_function) { + tr_func = rc->get_transitional_function(obj); + } + return tr_func; +} + +static void resettable_phase_enter(Object *obj, void *opaque, ResetType type) +{ + ResettableClass *rc = RESETTABLE_GET_CLASS(obj); + ResettableState *s = rc->get_state(obj); + const char *obj_typename = object_get_typename(obj); + bool action_needed = false; + + /* exit phase has to finish properly before entering back in reset */ + assert(!s->exit_phase_in_progress); + + trace_resettable_phase_enter_begin(obj, obj_typename, s->count, type); + + /* Only take action if we really enter reset for the 1st time. */ + /* + * TODO: if adding more ResetType support, some additional checks + * are probably needed here. + */ + if (s->count++ == 0) { + action_needed = true; + } + /* + * We limit the count to an arbitrary "big" value. The value is big + * enough not to be triggered normally. + * The assert will stop an infinite loop if there is a cycle in the + * reset tree. The loop goes through resettable_foreach_child below + * which at some point will call us again. + */ + assert(s->count <= 50); + + /* + * handle the children even if action_needed is at false so that + * child counts are incremented too + */ + resettable_child_foreach(rc, obj, resettable_phase_enter, NULL, type); + + /* execute enter phase for the object if needed */ + if (action_needed) { + trace_resettable_phase_enter_exec(obj, obj_typename, type, + !!rc->phases.enter); + if (rc->phases.enter && !resettable_get_tr_func(rc, obj)) { + rc->phases.enter(obj, type); + } + s->hold_phase_pending = true; + } + trace_resettable_phase_enter_end(obj, obj_typename, s->count); +} + +static void resettable_phase_hold(Object *obj, void *opaque, ResetType type) +{ + ResettableClass *rc = RESETTABLE_GET_CLASS(obj); + ResettableState *s = rc->get_state(obj); + const char *obj_typename = object_get_typename(obj); + + /* exit phase has to finish properly before entering back in reset */ + assert(!s->exit_phase_in_progress); + + trace_resettable_phase_hold_begin(obj, obj_typename, s->count, type); + + /* handle children first */ + resettable_child_foreach(rc, obj, resettable_phase_hold, NULL, type); + + /* exec hold phase */ + if (s->hold_phase_pending) { + s->hold_phase_pending = false; + ResettableTrFunction tr_func = resettable_get_tr_func(rc, obj); + trace_resettable_phase_hold_exec(obj, obj_typename, !!rc->phases.hold); + if (tr_func) { + trace_resettable_transitional_function(obj, obj_typename); + tr_func(obj); + } else if (rc->phases.hold) { + rc->phases.hold(obj); + } + } + trace_resettable_phase_hold_end(obj, obj_typename, s->count); +} + +static void resettable_phase_exit(Object *obj, void *opaque, ResetType type) +{ + ResettableClass *rc = RESETTABLE_GET_CLASS(obj); + ResettableState *s = rc->get_state(obj); + const char *obj_typename = object_get_typename(obj); + + assert(!s->exit_phase_in_progress); + trace_resettable_phase_exit_begin(obj, obj_typename, s->count, type); + + /* exit_phase_in_progress ensures this phase is 'atomic' */ + s->exit_phase_in_progress = true; + resettable_child_foreach(rc, obj, resettable_phase_exit, NULL, type); + + assert(s->count > 0); + if (s->count == 1) { + trace_resettable_phase_exit_exec(obj, obj_typename, !!rc->phases.exit); + if (rc->phases.exit && !resettable_get_tr_func(rc, obj)) { + rc->phases.exit(obj); + } + s->count = 0; + } + s->exit_phase_in_progress = false; + trace_resettable_phase_exit_end(obj, obj_typename, s->count); +} + +/* + * resettable_get_count: + * Get the count of the Resettable object @obj. Return 0 if @obj is NULL. + */ +static unsigned resettable_get_count(Object *obj) +{ + if (obj) { + ResettableClass *rc = RESETTABLE_GET_CLASS(obj); + return rc->get_state(obj)->count; + } + return 0; +} + +void resettable_change_parent(Object *obj, Object *newp, Object *oldp) +{ + ResettableClass *rc = RESETTABLE_GET_CLASS(obj); + ResettableState *s = rc->get_state(obj); + unsigned newp_count = resettable_get_count(newp); + unsigned oldp_count = resettable_get_count(oldp); + + /* + * Ensure we do not change parent when in enter or exit phase. + * During these phases, the reset subtree being updated is partly in reset + * and partly not in reset (it depends on the actual position in + * resettable_child_foreach()s). We are not able to tell in which part is a + * leaving or arriving device. Thus we cannot set the reset count of the + * moving device to the proper value. + */ + assert(!enter_phase_in_progress && !exit_phase_in_progress); + trace_resettable_change_parent(obj, oldp, oldp_count, newp, newp_count); + + /* + * At most one of the two 'for' loops will be executed below + * in order to cope with the difference between the two counts. + */ + /* if newp is more reset than oldp */ + for (unsigned i = oldp_count; i < newp_count; i++) { + resettable_assert_reset(obj, RESET_TYPE_COLD); + } + /* + * if obj is leaving a bus under reset, we need to ensure + * hold phase is not pending. + */ + if (oldp_count && s->hold_phase_pending) { + resettable_phase_hold(obj, NULL, RESET_TYPE_COLD); + } + /* if oldp is more reset than newp */ + for (unsigned i = newp_count; i < oldp_count; i++) { + resettable_release_reset(obj, RESET_TYPE_COLD); + } +} + +void resettable_cold_reset_fn(void *opaque) +{ + resettable_reset((Object *) opaque, RESET_TYPE_COLD); +} + +void resettable_class_set_parent_phases(ResettableClass *rc, + ResettableEnterPhase enter, + ResettableHoldPhase hold, + ResettableExitPhase exit, + ResettablePhases *parent_phases) +{ + *parent_phases = rc->phases; + if (enter) { + rc->phases.enter = enter; + } + if (hold) { + rc->phases.hold = hold; + } + if (exit) { + rc->phases.exit = exit; + } +} + +static const TypeInfo resettable_interface_info = { + .name = TYPE_RESETTABLE_INTERFACE, + .parent = TYPE_INTERFACE, + .class_size = sizeof(ResettableClass), +}; + +static void reset_register_types(void) +{ + type_register_static(&resettable_interface_info); +} + +type_init(reset_register_types) diff --git a/hw/core/split-irq.c b/hw/core/split-irq.c index b5acc669fb..3b90af2e8f 100644 --- a/hw/core/split-irq.c +++ b/hw/core/split-irq.c @@ -69,7 +69,7 @@ static void split_irq_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); /* No state to reset or migrate */ - dc->props = split_irq_properties; + device_class_set_props(dc, split_irq_properties); dc->realize = split_irq_realize; /* Reason: Needs to be wired up to work */ diff --git a/hw/core/sysbus.c b/hw/core/sysbus.c index 9e69c83aed..08b0311c5f 100644 --- a/hw/core/sysbus.c +++ b/hw/core/sysbus.c @@ -250,38 +250,6 @@ DeviceState *sysbus_create_varargs(const char *name, return dev; } -DeviceState *sysbus_try_create_varargs(const char *name, - hwaddr addr, ...) -{ - DeviceState *dev; - SysBusDevice *s; - va_list va; - qemu_irq irq; - int n; - - dev = qdev_try_create(NULL, name); - if (!dev) { - return NULL; - } - s = SYS_BUS_DEVICE(dev); - qdev_init_nofail(dev); - if (addr != (hwaddr)-1) { - sysbus_mmio_map(s, 0, addr); - } - va_start(va, addr); - n = 0; - while (1) { - irq = va_arg(va, qemu_irq); - if (!irq) { - break; - } - sysbus_connect_irq(s, n, irq); - n++; - } - va_end(va); - return dev; -} - static void sysbus_dev_print(Monitor *mon, DeviceState *dev, int indent) { SysBusDevice *s = SYS_BUS_DEVICE(dev); diff --git a/hw/core/trace-events b/hw/core/trace-events index fe47a9c8cb..aecd8e160e 100644 --- a/hw/core/trace-events +++ b/hw/core/trace-events @@ -1,2 +1,29 @@ # loader.c loader_write_rom(const char *name, uint64_t gpa, uint64_t size, bool isrom) "%s: @0x%"PRIx64" size=0x%"PRIx64" ROM=%d" + +# qdev.c +qdev_reset(void *obj, const char *objtype) "obj=%p(%s)" +qdev_reset_all(void *obj, const char *objtype) "obj=%p(%s)" +qdev_reset_tree(void *obj, const char *objtype) "obj=%p(%s)" +qbus_reset(void *obj, const char *objtype) "obj=%p(%s)" +qbus_reset_all(void *obj, const char *objtype) "obj=%p(%s)" +qbus_reset_tree(void *obj, const char *objtype) "obj=%p(%s)" +qdev_update_parent_bus(void *obj, const char *objtype, void *oldp, const char *oldptype, void *newp, const char *newptype) "obj=%p(%s) old_parent=%p(%s) new_parent=%p(%s)" + +# resettable.c +resettable_reset(void *obj, int cold) "obj=%p cold=%d" +resettable_reset_assert_begin(void *obj, int cold) "obj=%p cold=%d" +resettable_reset_assert_end(void *obj) "obj=%p" +resettable_reset_release_begin(void *obj, int cold) "obj=%p cold=%d" +resettable_reset_release_end(void *obj) "obj=%p" +resettable_change_parent(void *obj, void *o, unsigned oc, void *n, unsigned nc) "obj=%p from=%p(%d) to=%p(%d)" +resettable_phase_enter_begin(void *obj, const char *objtype, unsigned count, int type) "obj=%p(%s) count=%d type=%d" +resettable_phase_enter_exec(void *obj, const char *objtype, int type, int has_method) "obj=%p(%s) type=%d method=%d" +resettable_phase_enter_end(void *obj, const char *objtype, unsigned count) "obj=%p(%s) count=%d" +resettable_phase_hold_begin(void *obj, const char *objtype, unsigned count, int type) "obj=%p(%s) count=%d type=%d" +resettable_phase_hold_exec(void *obj, const char *objtype, int has_method) "obj=%p(%s) method=%d" +resettable_phase_hold_end(void *obj, const char *objtype, unsigned count) "obj=%p(%s) count=%d" +resettable_phase_exit_begin(void *obj, const char *objtype, unsigned count, int type) "obj=%p(%s) count=%d type=%d" +resettable_phase_exit_exec(void *obj, const char *objtype, int has_method) "obj=%p(%s) method=%d" +resettable_phase_exit_end(void *obj, const char *objtype, unsigned count) "obj=%p(%s) count=%d" +resettable_transitional_function(void *obj, const char *objtype) "obj=%p(%s)" diff --git a/hw/core/vmstate-if.c b/hw/core/vmstate-if.c new file mode 100644 index 0000000000..bf453620fe --- /dev/null +++ b/hw/core/vmstate-if.c @@ -0,0 +1,23 @@ +/* + * VMState interface + * + * Copyright (c) 2009-2019 Red Hat Inc + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "hw/vmstate-if.h" + +static const TypeInfo vmstate_if_info = { + .name = TYPE_VMSTATE_IF, + .parent = TYPE_INTERFACE, + .class_size = sizeof(VMStateIfClass), +}; + +static void vmstate_register_types(void) +{ + type_register_static(&vmstate_if_info); +} + +type_init(vmstate_register_types); diff --git a/hw/cpu/a15mpcore.c b/hw/cpu/a15mpcore.c index 7e9983aa19..4f659115b6 100644 --- a/hw/cpu/a15mpcore.c +++ b/hw/cpu/a15mpcore.c @@ -164,7 +164,7 @@ static void a15mp_priv_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = a15mp_priv_realize; - dc->props = a15mp_priv_properties; + device_class_set_props(dc, a15mp_priv_properties); /* We currently have no savable state */ } diff --git a/hw/cpu/a9mpcore.c b/hw/cpu/a9mpcore.c index 6872a3a00a..b4f6a7e8a5 100644 --- a/hw/cpu/a9mpcore.c +++ b/hw/cpu/a9mpcore.c @@ -16,6 +16,8 @@ #include "hw/qdev-properties.h" #include "hw/core/cpu.h" +#define A9_GIC_NUM_PRIORITY_BITS 5 + static void a9mp_priv_set_irq(void *opaque, int irq, int level) { A9MPPrivState *s = (A9MPPrivState *)opaque; @@ -68,6 +70,8 @@ static void a9mp_priv_realize(DeviceState *dev, Error **errp) gicdev = DEVICE(&s->gic); qdev_prop_set_uint32(gicdev, "num-cpu", s->num_cpu); qdev_prop_set_uint32(gicdev, "num-irq", s->num_irq); + qdev_prop_set_uint32(gicdev, "num-priority-bits", + A9_GIC_NUM_PRIORITY_BITS); /* Make the GIC's TZ support match the CPUs. We assume that * either all the CPUs have TZ, or none do. @@ -175,7 +179,7 @@ static void a9mp_priv_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = a9mp_priv_realize; - dc->props = a9mp_priv_properties; + device_class_set_props(dc, a9mp_priv_properties); } static const TypeInfo a9mp_priv_info = { diff --git a/hw/cpu/arm11mpcore.c b/hw/cpu/arm11mpcore.c index e78f5d080c..ab9fadb67c 100644 --- a/hw/cpu/arm11mpcore.c +++ b/hw/cpu/arm11mpcore.c @@ -15,6 +15,7 @@ #include "hw/irq.h" #include "hw/qdev-properties.h" +#define ARM11MPCORE_NUM_GIC_PRIORITY_BITS 4 static void mpcore_priv_set_irq(void *opaque, int irq, int level) { @@ -86,6 +87,10 @@ static void mpcore_priv_realize(DeviceState *dev, Error **errp) qdev_prop_set_uint32(gicdev, "num-cpu", s->num_cpu); qdev_prop_set_uint32(gicdev, "num-irq", s->num_irq); + qdev_prop_set_uint32(gicdev, "num-priority-bits", + ARM11MPCORE_NUM_GIC_PRIORITY_BITS); + + object_property_set_bool(OBJECT(&s->gic), true, "realized", &err); if (err != NULL) { error_propagate(errp, err); @@ -156,7 +161,7 @@ static void mpcore_priv_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = mpcore_priv_realize; - dc->props = mpcore_priv_properties; + device_class_set_props(dc, mpcore_priv_properties); } static const TypeInfo mpcore_priv_info = { diff --git a/hw/cpu/cluster.c b/hw/cpu/cluster.c index 349a883261..e444b7c29d 100644 --- a/hw/cpu/cluster.c +++ b/hw/cpu/cluster.c @@ -78,7 +78,7 @@ static void cpu_cluster_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->props = cpu_cluster_properties; + device_class_set_props(dc, cpu_cluster_properties); dc->realize = cpu_cluster_realize; /* This is not directly for users, CPU children must be attached by code */ diff --git a/hw/cpu/realview_mpcore.c b/hw/cpu/realview_mpcore.c index ae2c9913f6..cc2767c716 100644 --- a/hw/cpu/realview_mpcore.c +++ b/hw/cpu/realview_mpcore.c @@ -125,7 +125,7 @@ static void mpcore_rirq_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = realview_mpcore_realize; - dc->props = mpcore_rirq_properties; + device_class_set_props(dc, mpcore_rirq_properties); } static const TypeInfo mpcore_rirq_info = { diff --git a/hw/cris/axis_dev88.c b/hw/cris/axis_dev88.c index 940c7dd122..75e5c993b5 100644 --- a/hw/cris/axis_dev88.c +++ b/hw/cris/axis_dev88.c @@ -249,11 +249,9 @@ static struct cris_load_info li; static void axisdev88_init(MachineState *machine) { - ram_addr_t ram_size = machine->ram_size; const char *kernel_filename = machine->kernel_filename; const char *kernel_cmdline = machine->kernel_cmdline; CRISCPU *cpu; - CPUCRISState *env; DeviceState *dev; SysBusDevice *s; DriveInfo *nand; @@ -262,17 +260,12 @@ void axisdev88_init(MachineState *machine) struct etraxfs_dma_client *dma_eth; int i; MemoryRegion *address_space_mem = get_system_memory(); - MemoryRegion *phys_ram = g_new(MemoryRegion, 1); MemoryRegion *phys_intmem = g_new(MemoryRegion, 1); /* init CPUs */ cpu = CRIS_CPU(cpu_create(machine->cpu_type)); - env = &cpu->env; - /* allocate RAM */ - memory_region_allocate_system_memory(phys_ram, NULL, "axisdev88.ram", - ram_size); - memory_region_add_subregion(address_space_mem, 0x40000000, phys_ram); + memory_region_add_subregion(address_space_mem, 0x40000000, machine->ram); /* The ETRAX-FS has 128Kb on chip ram, the docs refer to it as the internal memory. */ @@ -297,8 +290,6 @@ void axisdev88_init(MachineState *machine) dev = qdev_create(NULL, "etraxfs,pic"); - /* FIXME: Is there a proper way to signal vectors to the CPU core? */ - qdev_prop_set_ptr(dev, "interrupt_vector", &env->interrupt_vector); qdev_init_nofail(dev); s = SYS_BUS_DEVICE(dev); sysbus_mmio_map(s, 0, 0x3001c000); @@ -353,8 +344,9 @@ static void axisdev88_machine_init(MachineClass *mc) { mc->desc = "AXIS devboard 88"; mc->init = axisdev88_init; - mc->is_default = 1; + mc->is_default = true; mc->default_cpu_type = CRIS_CPU_TYPE_NAME("crisv32"); + mc->default_ram_id = "axisdev88.ram"; } DEFINE_MACHINE("axis-dev88", axisdev88_machine_init) diff --git a/hw/cris/boot.c b/hw/cris/boot.c index 2d2cc0c7a5..b8947bc660 100644 --- a/hw/cris/boot.c +++ b/hw/cris/boot.c @@ -76,7 +76,7 @@ void cris_load_image(CRISCPU *cpu, struct cris_load_info *li) devboard SDK. */ image_size = load_elf(li->image_filename, NULL, translate_kernel_address, NULL, - &entry, NULL, &high, 0, EM_CRIS, 0, 0); + &entry, NULL, &high, NULL, 0, EM_CRIS, 0, 0); li->entry = entry; if (image_size < 0) { /* Takes a kimage from the axis devboard SDK. */ diff --git a/hw/display/Kconfig b/hw/display/Kconfig index c500d1fc6d..15d59e10dc 100644 --- a/hw/display/Kconfig +++ b/hw/display/Kconfig @@ -91,6 +91,10 @@ config TCX config CG3 bool +config ARTIST + bool + select FRAMEBUFFER + config VGA bool diff --git a/hw/display/Makefile.objs b/hw/display/Makefile.objs index f2182e3bef..77a7d622bd 100644 --- a/hw/display/Makefile.objs +++ b/hw/display/Makefile.objs @@ -24,7 +24,7 @@ common-obj-$(CONFIG_BOCHS_DISPLAY) += bochs-display.o common-obj-$(CONFIG_BLIZZARD) += blizzard.o common-obj-$(CONFIG_EXYNOS4) += exynos4210_fimd.o common-obj-$(CONFIG_FRAMEBUFFER) += framebuffer.o -obj-$(CONFIG_MILKYMIST) += milkymist-vgafb.o +common-obj-$(CONFIG_MILKYMIST) += milkymist-vgafb.o common-obj-$(CONFIG_ZAURUS) += tc6393xb.o common-obj-$(CONFIG_MACFB) += macfb.o @@ -40,6 +40,7 @@ common-obj-$(CONFIG_SM501) += sm501.o common-obj-$(CONFIG_TCX) += tcx.o common-obj-$(CONFIG_CG3) += cg3.o common-obj-$(CONFIG_NEXTCUBE) += next-fb.o +common-obj-$(CONFIG_ARTIST) += artist.o obj-$(CONFIG_VGA) += vga.o diff --git a/hw/display/ads7846.c b/hw/display/ads7846.c index c12272ae72..9228b40b1a 100644 --- a/hw/display/ads7846.c +++ b/hw/display/ads7846.c @@ -154,7 +154,7 @@ static void ads7846_realize(SSISlave *d, Error **errp) ads7846_int_update(s); - vmstate_register(NULL, -1, &vmstate_ads7846, s); + vmstate_register(NULL, VMSTATE_INSTANCE_ID_ANY, &vmstate_ads7846, s); } static void ads7846_class_init(ObjectClass *klass, void *data) diff --git a/hw/display/artist.c b/hw/display/artist.c new file mode 100644 index 0000000000..753dbb9a77 --- /dev/null +++ b/hw/display/artist.c @@ -0,0 +1,1443 @@ +/* + * QEMU HP Artist Emulation + * + * Copyright (c) 2019 Sven Schnelle + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + */ + +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "qemu/error-report.h" +#include "qemu/typedefs.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "qemu/units.h" +#include "qapi/error.h" +#include "hw/sysbus.h" +#include "hw/loader.h" +#include "hw/qdev-core.h" +#include "hw/qdev-properties.h" +#include "migration/vmstate.h" +#include "ui/console.h" +#include "trace.h" +#include "hw/display/framebuffer.h" + +#define TYPE_ARTIST "artist" +#define ARTIST(obj) OBJECT_CHECK(ARTISTState, (obj), TYPE_ARTIST) + +#ifdef HOST_WORDS_BIGENDIAN +#define ROP8OFF(_i) (3 - (_i)) +#else +#define ROP8OFF +#endif + +struct vram_buffer { + MemoryRegion mr; + uint8_t *data; + int size; + int width; + int height; +}; + +typedef struct ARTISTState { + SysBusDevice parent_obj; + + QemuConsole *con; + MemoryRegion vram_mem; + MemoryRegion mem_as_root; + MemoryRegion reg; + MemoryRegionSection fbsection; + + void *vram_int_mr; + AddressSpace as; + + struct vram_buffer vram_buffer[16]; + + uint16_t width; + uint16_t height; + uint16_t depth; + + uint32_t fg_color; + uint32_t bg_color; + + uint32_t vram_char_y; + uint32_t vram_bitmask; + + uint32_t vram_start; + uint32_t vram_pos; + + uint32_t vram_size; + + uint32_t blockmove_source; + uint32_t blockmove_dest; + uint32_t blockmove_size; + + uint32_t line_size; + uint32_t line_end; + uint32_t line_xy; + uint32_t line_pattern_start; + uint32_t line_pattern_skip; + + uint32_t cursor_pos; + + uint32_t cursor_height; + uint32_t cursor_width; + + uint32_t plane_mask; + + uint32_t reg_100080; + uint32_t reg_300200; + uint32_t reg_300208; + uint32_t reg_300218; + + uint32_t cmap_bm_access; + uint32_t dst_bm_access; + uint32_t src_bm_access; + uint32_t control_plane; + uint32_t transfer_data; + uint32_t image_bitmap_op; + + uint32_t font_write1; + uint32_t font_write2; + uint32_t font_write_pos_y; + + int draw_line_pattern; +} ARTISTState; + +typedef enum { + ARTIST_BUFFER_AP = 1, + ARTIST_BUFFER_OVERLAY = 2, + ARTIST_BUFFER_CURSOR1 = 6, + ARTIST_BUFFER_CURSOR2 = 7, + ARTIST_BUFFER_ATTRIBUTE = 13, + ARTIST_BUFFER_CMAP = 15, +} artist_buffer_t; + +typedef enum { + VRAM_IDX = 0x1004a0, + VRAM_BITMASK = 0x1005a0, + VRAM_WRITE_INCR_X = 0x100600, + VRAM_WRITE_INCR_X2 = 0x100604, + VRAM_WRITE_INCR_Y = 0x100620, + VRAM_START = 0x100800, + BLOCK_MOVE_SIZE = 0x100804, + BLOCK_MOVE_SOURCE = 0x100808, + TRANSFER_DATA = 0x100820, + FONT_WRITE_INCR_Y = 0x1008a0, + VRAM_START_TRIGGER = 0x100a00, + VRAM_SIZE_TRIGGER = 0x100a04, + FONT_WRITE_START = 0x100aa0, + BLOCK_MOVE_DEST_TRIGGER = 0x100b00, + BLOCK_MOVE_SIZE_TRIGGER = 0x100b04, + LINE_XY = 0x100ccc, + PATTERN_LINE_START = 0x100ecc, + LINE_SIZE = 0x100e04, + LINE_END = 0x100e44, + CMAP_BM_ACCESS = 0x118000, + DST_BM_ACCESS = 0x118004, + SRC_BM_ACCESS = 0x118008, + CONTROL_PLANE = 0x11800c, + FG_COLOR = 0x118010, + BG_COLOR = 0x118014, + PLANE_MASK = 0x118018, + IMAGE_BITMAP_OP = 0x11801c, + CURSOR_POS = 0x300100, + CURSOR_CTRL = 0x300104, +} artist_reg_t; + +typedef enum { + ARTIST_ROP_CLEAR = 0, + ARTIST_ROP_COPY = 3, + ARTIST_ROP_XOR = 6, + ARTIST_ROP_NOT_DST = 10, + ARTIST_ROP_SET = 15, +} artist_rop_t; + +#define REG_NAME(_x) case _x: return " "#_x; +static const char *artist_reg_name(uint64_t addr) +{ + switch ((artist_reg_t)addr) { + REG_NAME(VRAM_IDX); + REG_NAME(VRAM_BITMASK); + REG_NAME(VRAM_WRITE_INCR_X); + REG_NAME(VRAM_WRITE_INCR_X2); + REG_NAME(VRAM_WRITE_INCR_Y); + REG_NAME(VRAM_START); + REG_NAME(BLOCK_MOVE_SIZE); + REG_NAME(BLOCK_MOVE_SOURCE); + REG_NAME(FG_COLOR); + REG_NAME(BG_COLOR); + REG_NAME(PLANE_MASK); + REG_NAME(VRAM_START_TRIGGER); + REG_NAME(VRAM_SIZE_TRIGGER); + REG_NAME(BLOCK_MOVE_DEST_TRIGGER); + REG_NAME(BLOCK_MOVE_SIZE_TRIGGER); + REG_NAME(TRANSFER_DATA); + REG_NAME(CONTROL_PLANE); + REG_NAME(IMAGE_BITMAP_OP); + REG_NAME(CMAP_BM_ACCESS); + REG_NAME(DST_BM_ACCESS); + REG_NAME(SRC_BM_ACCESS); + REG_NAME(CURSOR_POS); + REG_NAME(CURSOR_CTRL); + REG_NAME(LINE_XY); + REG_NAME(PATTERN_LINE_START); + REG_NAME(LINE_SIZE); + REG_NAME(LINE_END); + REG_NAME(FONT_WRITE_INCR_Y); + REG_NAME(FONT_WRITE_START); + } + return ""; +} +#undef REG_NAME + +static int16_t artist_get_x(uint32_t reg) +{ + return reg >> 16; +} + +static int16_t artist_get_y(uint32_t reg) +{ + return reg & 0xffff; +} + +static void artist_invalidate_lines(struct vram_buffer *buf, + int starty, int height) +{ + int start = starty * buf->width; + int size = height * buf->width; + + if (start + size <= buf->size) { + memory_region_set_dirty(&buf->mr, start, size); + } +} + +static int vram_write_pix_per_transfer(ARTISTState *s) +{ + if (s->cmap_bm_access) { + return 1 << ((s->cmap_bm_access >> 27) & 0x0f); + } else { + return 1 << ((s->dst_bm_access >> 27) & 0x0f); + } +} + +static int vram_pixel_length(ARTISTState *s) +{ + if (s->cmap_bm_access) { + return (s->cmap_bm_access >> 24) & 0x07; + } else { + return (s->dst_bm_access >> 24) & 0x07; + } +} + +static int vram_write_bufidx(ARTISTState *s) +{ + if (s->cmap_bm_access) { + return (s->cmap_bm_access >> 12) & 0x0f; + } else { + return (s->dst_bm_access >> 12) & 0x0f; + } +} + +static int vram_read_bufidx(ARTISTState *s) +{ + if (s->cmap_bm_access) { + return (s->cmap_bm_access >> 12) & 0x0f; + } else { + return (s->src_bm_access >> 12) & 0x0f; + } +} + +static struct vram_buffer *vram_read_buffer(ARTISTState *s) +{ + return &s->vram_buffer[vram_read_bufidx(s)]; +} + +static struct vram_buffer *vram_write_buffer(ARTISTState *s) +{ + return &s->vram_buffer[vram_write_bufidx(s)]; +} + +static uint8_t artist_get_color(ARTISTState *s) +{ + if (s->image_bitmap_op & 2) { + return s->fg_color; + } else { + return s->bg_color; + } +} + +static artist_rop_t artist_get_op(ARTISTState *s) +{ + return (s->image_bitmap_op >> 8) & 0xf; +} + +static void artist_rop8(ARTISTState *s, uint8_t *dst, uint8_t val) +{ + + const artist_rop_t op = artist_get_op(s); + uint8_t plane_mask = s->plane_mask & 0xff; + + switch (op) { + case ARTIST_ROP_CLEAR: + *dst &= ~plane_mask; + break; + + case ARTIST_ROP_COPY: + *dst &= ~plane_mask; + *dst |= val & plane_mask; + break; + + case ARTIST_ROP_XOR: + *dst ^= val & plane_mask; + break; + + case ARTIST_ROP_NOT_DST: + *dst ^= plane_mask; + break; + + case ARTIST_ROP_SET: + *dst |= plane_mask; + break; + + default: + qemu_log_mask(LOG_UNIMP, "%s: unsupported rop %d\n", __func__, op); + break; + } +} + +static void artist_get_cursor_pos(ARTISTState *s, int *x, int *y) +{ + /* + * Don't know whether these magic offset values are configurable via + * some register. They are the same for all resolutions, so don't + * bother about it. + */ + + *y = 0x47a - artist_get_y(s->cursor_pos); + *x = ((artist_get_x(s->cursor_pos) - 338) / 2); + + if (*x > s->width) { + *x = 0; + } + + if (*y > s->height) { + *y = 0; + } +} + +static void artist_invalidate_cursor(ARTISTState *s) +{ + int x, y; + artist_get_cursor_pos(s, &x, &y); + artist_invalidate_lines(&s->vram_buffer[ARTIST_BUFFER_AP], + y, s->cursor_height); +} + +static void vram_bit_write(ARTISTState *s, int posx, int posy, bool incr_x, + int size, uint32_t data) +{ + struct vram_buffer *buf; + uint32_t vram_bitmask = s->vram_bitmask; + int mask, i, pix_count, pix_length, offset, height, width; + uint8_t *data8, *p; + + pix_count = vram_write_pix_per_transfer(s); + pix_length = vram_pixel_length(s); + + buf = vram_write_buffer(s); + height = buf->height; + width = buf->width; + + if (s->cmap_bm_access) { + offset = s->vram_pos; + } else { + offset = posy * width + posx; + } + + if (!buf->size) { + qemu_log("write to non-existent buffer\n"); + return; + } + + p = buf->data; + + if (pix_count > size * 8) { + pix_count = size * 8; + } + + if (posy * width + posx + pix_count > buf->size) { + qemu_log("write outside bounds: wants %dx%d, max size %dx%d\n", + posx, posy, width, height); + return; + } + + + switch (pix_length) { + case 0: + if (s->image_bitmap_op & 0x20000000) { + data &= vram_bitmask; + } + + for (i = 0; i < pix_count; i++) { + artist_rop8(s, p + offset + pix_count - 1 - i, + (data & 1) ? (s->plane_mask >> 24) : 0); + data >>= 1; + } + memory_region_set_dirty(&buf->mr, offset, pix_count); + break; + + case 3: + if (s->cmap_bm_access) { + *(uint32_t *)(p + offset) = data; + break; + } + data8 = (uint8_t *)&data; + + for (i = 3; i >= 0; i--) { + if (!(s->image_bitmap_op & 0x20000000) || + s->vram_bitmask & (1 << (28 + i))) { + artist_rop8(s, p + offset + 3 - i, data8[ROP8OFF(i)]); + } + } + memory_region_set_dirty(&buf->mr, offset, 3); + break; + + case 6: + switch (size) { + default: + case 4: + vram_bitmask = s->vram_bitmask; + break; + + case 2: + vram_bitmask = s->vram_bitmask >> 16; + break; + + case 1: + vram_bitmask = s->vram_bitmask >> 24; + break; + } + + for (i = 0; i < pix_count; i++) { + mask = 1 << (pix_count - 1 - i); + + if (!(s->image_bitmap_op & 0x20000000) || + (vram_bitmask & mask)) { + if (data & mask) { + artist_rop8(s, p + offset + i, s->fg_color); + } else { + if (!(s->image_bitmap_op & 0x10000002)) { + artist_rop8(s, p + offset + i, s->bg_color); + } + } + } + } + memory_region_set_dirty(&buf->mr, offset, pix_count); + break; + + default: + qemu_log_mask(LOG_UNIMP, "%s: unknown pixel length %d\n", + __func__, pix_length); + break; + } + + if (incr_x) { + if (s->cmap_bm_access) { + s->vram_pos += 4; + } else { + s->vram_pos += pix_count << 2; + } + } + + if (vram_write_bufidx(s) == ARTIST_BUFFER_CURSOR1 || + vram_write_bufidx(s) == ARTIST_BUFFER_CURSOR2) { + artist_invalidate_cursor(s); + } +} + +static void block_move(ARTISTState *s, int source_x, int source_y, int dest_x, + int dest_y, int width, int height) +{ + struct vram_buffer *buf; + int line, endline, lineincr, startcolumn, endcolumn, columnincr, column; + uint32_t dst, src; + + trace_artist_block_move(source_x, source_y, dest_x, dest_y, width, height); + + if (s->control_plane != 0) { + /* We don't support CONTROL_PLANE accesses */ + qemu_log_mask(LOG_UNIMP, "%s: CONTROL_PLANE: %08x\n", __func__, + s->control_plane); + return; + } + + buf = &s->vram_buffer[ARTIST_BUFFER_AP]; + + if (dest_y > source_y) { + /* move down */ + line = height - 1; + endline = -1; + lineincr = -1; + } else { + /* move up */ + line = 0; + endline = height; + lineincr = 1; + } + + if (dest_x > source_x) { + /* move right */ + startcolumn = width - 1; + endcolumn = -1; + columnincr = -1; + } else { + /* move left */ + startcolumn = 0; + endcolumn = width; + columnincr = 1; + } + + for ( ; line != endline; line += lineincr) { + src = source_x + ((line + source_y) * buf->width); + dst = dest_x + ((line + dest_y) * buf->width); + + for (column = startcolumn; column != endcolumn; column += columnincr) { + if (dst + column > buf->size || src + column > buf->size) { + continue; + } + artist_rop8(s, buf->data + dst + column, buf->data[src + column]); + } + } + + artist_invalidate_lines(buf, dest_y, height); +} + +static void fill_window(ARTISTState *s, int startx, int starty, + int width, int height) +{ + uint32_t offset; + uint8_t color = artist_get_color(s); + struct vram_buffer *buf; + int x, y; + + trace_artist_fill_window(startx, starty, width, height, + s->image_bitmap_op, s->control_plane); + + if (s->control_plane != 0) { + /* We don't support CONTROL_PLANE accesses */ + qemu_log_mask(LOG_UNIMP, "%s: CONTROL_PLANE: %08x\n", __func__, + s->control_plane); + return; + } + + if (s->reg_100080 == 0x7d) { + /* + * Not sure what this register really does, but + * 0x7d seems to enable autoincremt of the Y axis + * by the current block move height. + */ + height = artist_get_y(s->blockmove_size); + s->vram_start += height; + } + + buf = &s->vram_buffer[ARTIST_BUFFER_AP]; + + for (y = starty; y < starty + height; y++) { + offset = y * s->width; + + for (x = startx; x < startx + width; x++) { + artist_rop8(s, buf->data + offset + x, color); + } + } + artist_invalidate_lines(buf, starty, height); +} + +static void draw_line(ARTISTState *s, int x1, int y1, int x2, int y2, + bool update_start, int skip_pix, int max_pix) +{ + struct vram_buffer *buf; + uint8_t color; + int dx, dy, t, e, x, y, incy, diago, horiz; + bool c1; + uint8_t *p; + + trace_artist_draw_line(x1, y1, x2, y2); + + if (update_start) { + s->vram_start = (x2 << 16) | y2; + } + + if (x2 > x1) { + dx = x2 - x1; + } else { + dx = x1 - x2; + } + if (y2 > y1) { + dy = y2 - y1; + } else { + dy = y1 - y2; + } + if (!dx || !dy) { + return; + } + + c1 = false; + if (dy > dx) { + t = y2; + y2 = x2; + x2 = t; + + t = y1; + y1 = x1; + x1 = t; + + t = dx; + dx = dy; + dy = t; + + c1 = true; + } + + if (x1 > x2) { + t = y2; + y2 = y1; + y1 = t; + + t = x1; + x1 = x2; + x2 = t; + } + + horiz = dy << 1; + diago = (dy - dx) << 1; + e = (dy << 1) - dx; + + if (y1 <= y2) { + incy = 1; + } else { + incy = -1; + } + x = x1; + y = y1; + color = artist_get_color(s); + buf = &s->vram_buffer[ARTIST_BUFFER_AP]; + + do { + if (c1) { + p = buf->data + x * s->width + y; + } else { + p = buf->data + y * s->width + x; + } + + if (skip_pix > 0) { + skip_pix--; + } else { + artist_rop8(s, p, color); + } + + if (e > 0) { + artist_invalidate_lines(buf, y, 1); + y += incy; + e += diago; + } else { + e += horiz; + } + x++; + } while (x <= x2 && (max_pix == -1 || --max_pix > 0)); +} + +static void draw_line_pattern_start(ARTISTState *s) +{ + + int startx = artist_get_x(s->vram_start); + int starty = artist_get_y(s->vram_start); + int endx = artist_get_x(s->blockmove_size); + int endy = artist_get_y(s->blockmove_size); + int pstart = s->line_pattern_start >> 16; + + draw_line(s, startx, starty, endx, endy, false, -1, pstart); + s->line_pattern_skip = pstart; +} + +static void draw_line_pattern_next(ARTISTState *s) +{ + + int startx = artist_get_x(s->vram_start); + int starty = artist_get_y(s->vram_start); + int endx = artist_get_x(s->blockmove_size); + int endy = artist_get_y(s->blockmove_size); + int line_xy = s->line_xy >> 16; + + draw_line(s, startx, starty, endx, endy, false, s->line_pattern_skip, + s->line_pattern_skip + line_xy); + s->line_pattern_skip += line_xy; + s->image_bitmap_op ^= 2; +} + +static void draw_line_size(ARTISTState *s, bool update_start) +{ + + int startx = artist_get_x(s->vram_start); + int starty = artist_get_y(s->vram_start); + int endx = artist_get_x(s->line_size); + int endy = artist_get_y(s->line_size); + + draw_line(s, startx, starty, endx, endy, update_start, -1, -1); +} + +static void draw_line_xy(ARTISTState *s, bool update_start) +{ + + int startx = artist_get_x(s->vram_start); + int starty = artist_get_y(s->vram_start); + int sizex = artist_get_x(s->blockmove_size); + int sizey = artist_get_y(s->blockmove_size); + int linexy = s->line_xy >> 16; + int endx, endy; + + endx = startx; + endy = starty; + + if (sizex > 0) { + endx = startx + linexy; + } + + if (sizex < 0) { + endx = startx; + startx -= linexy; + } + + if (sizey > 0) { + endy = starty + linexy; + } + + if (sizey < 0) { + endy = starty; + starty -= linexy; + } + + if (startx < 0) { + startx = 0; + } + + if (endx < 0) { + endx = 0; + } + + if (starty < 0) { + starty = 0; + } + + if (endy < 0) { + endy = 0; + } + + draw_line(s, startx, starty, endx, endy, false, -1, -1); +} + +static void draw_line_end(ARTISTState *s, bool update_start) +{ + + int startx = artist_get_x(s->vram_start); + int starty = artist_get_y(s->vram_start); + int endx = artist_get_x(s->line_end); + int endy = artist_get_y(s->line_end); + + draw_line(s, startx, starty, endx, endy, update_start, -1, -1); +} + +static void font_write16(ARTISTState *s, uint16_t val) +{ + struct vram_buffer *buf; + uint32_t color = (s->image_bitmap_op & 2) ? s->fg_color : s->bg_color; + uint16_t mask; + int i; + + int startx = artist_get_x(s->vram_start); + int starty = artist_get_y(s->vram_start) + s->font_write_pos_y; + int offset = starty * s->width + startx; + + buf = &s->vram_buffer[ARTIST_BUFFER_AP]; + + if (offset + 16 > buf->size) { + return; + } + + for (i = 0; i < 16; i++) { + mask = 1 << (15 - i); + if (val & mask) { + artist_rop8(s, buf->data + offset + i, color); + } else { + if (!(s->image_bitmap_op & 0x20000000)) { + artist_rop8(s, buf->data + offset + i, s->bg_color); + } + } + } + artist_invalidate_lines(buf, starty, 1); +} + +static void font_write(ARTISTState *s, uint32_t val) +{ + font_write16(s, val >> 16); + if (++s->font_write_pos_y == artist_get_y(s->blockmove_size)) { + s->vram_start += (s->blockmove_size & 0xffff0000); + return; + } + + font_write16(s, val & 0xffff); + if (++s->font_write_pos_y == artist_get_y(s->blockmove_size)) { + s->vram_start += (s->blockmove_size & 0xffff0000); + return; + } +} + +static void combine_write_reg(hwaddr addr, uint64_t val, int size, void *out) +{ + /* + * FIXME: is there a qemu helper for this? + */ + +#ifndef HOST_WORDS_BIGENDIAN + addr ^= 3; +#endif + + switch (size) { + case 1: + *(uint8_t *)(out + (addr & 3)) = val; + break; + + case 2: + *(uint16_t *)(out + (addr & 2)) = val; + break; + + case 4: + *(uint32_t *)out = val; + break; + + default: + qemu_log_mask(LOG_UNIMP, "unsupported write size: %d\n", size); + } +} + +static void artist_reg_write(void *opaque, hwaddr addr, uint64_t val, + unsigned size) +{ + ARTISTState *s = opaque; + int posx, posy; + int width, height; + + trace_artist_reg_write(size, addr, artist_reg_name(addr & ~3ULL), val); + + switch (addr & ~3ULL) { + case 0x100080: + combine_write_reg(addr, val, size, &s->reg_100080); + break; + + case FG_COLOR: + combine_write_reg(addr, val, size, &s->fg_color); + break; + + case BG_COLOR: + combine_write_reg(addr, val, size, &s->bg_color); + break; + + case VRAM_BITMASK: + combine_write_reg(addr, val, size, &s->vram_bitmask); + break; + + case VRAM_WRITE_INCR_Y: + posx = (s->vram_pos >> 2) & 0x7ff; + posy = (s->vram_pos >> 13) & 0x3ff; + vram_bit_write(s, posx, posy + s->vram_char_y++, false, size, val); + break; + + case VRAM_WRITE_INCR_X: + case VRAM_WRITE_INCR_X2: + posx = (s->vram_pos >> 2) & 0x7ff; + posy = (s->vram_pos >> 13) & 0x3ff; + vram_bit_write(s, posx, posy + s->vram_char_y, true, size, val); + break; + + case VRAM_IDX: + combine_write_reg(addr, val, size, &s->vram_pos); + s->vram_char_y = 0; + s->draw_line_pattern = 0; + break; + + case VRAM_START: + combine_write_reg(addr, val, size, &s->vram_start); + s->draw_line_pattern = 0; + break; + + case VRAM_START_TRIGGER: + combine_write_reg(addr, val, size, &s->vram_start); + fill_window(s, artist_get_x(s->vram_start), + artist_get_y(s->vram_start), + artist_get_x(s->blockmove_size), + artist_get_y(s->blockmove_size)); + break; + + case VRAM_SIZE_TRIGGER: + combine_write_reg(addr, val, size, &s->vram_size); + + if (size == 2 && !(addr & 2)) { + height = artist_get_y(s->blockmove_size); + } else { + height = artist_get_y(s->vram_size); + } + + if (size == 2 && (addr & 2)) { + width = artist_get_x(s->blockmove_size); + } else { + width = artist_get_x(s->vram_size); + } + + fill_window(s, artist_get_x(s->vram_start), + artist_get_y(s->vram_start), + width, height); + break; + + case LINE_XY: + combine_write_reg(addr, val, size, &s->line_xy); + if (s->draw_line_pattern) { + draw_line_pattern_next(s); + } else { + draw_line_xy(s, true); + } + break; + + case PATTERN_LINE_START: + combine_write_reg(addr, val, size, &s->line_pattern_start); + s->draw_line_pattern = 1; + draw_line_pattern_start(s); + break; + + case LINE_SIZE: + combine_write_reg(addr, val, size, &s->line_size); + draw_line_size(s, true); + break; + + case LINE_END: + combine_write_reg(addr, val, size, &s->line_end); + draw_line_end(s, true); + break; + + case BLOCK_MOVE_SIZE: + combine_write_reg(addr, val, size, &s->blockmove_size); + break; + + case BLOCK_MOVE_SOURCE: + combine_write_reg(addr, val, size, &s->blockmove_source); + break; + + case BLOCK_MOVE_DEST_TRIGGER: + combine_write_reg(addr, val, size, &s->blockmove_dest); + + block_move(s, artist_get_x(s->blockmove_source), + artist_get_y(s->blockmove_source), + artist_get_x(s->blockmove_dest), + artist_get_y(s->blockmove_dest), + artist_get_x(s->blockmove_size), + artist_get_y(s->blockmove_size)); + break; + + case BLOCK_MOVE_SIZE_TRIGGER: + combine_write_reg(addr, val, size, &s->blockmove_size); + + block_move(s, + artist_get_x(s->blockmove_source), + artist_get_y(s->blockmove_source), + artist_get_x(s->vram_start), + artist_get_y(s->vram_start), + artist_get_x(s->blockmove_size), + artist_get_y(s->blockmove_size)); + break; + + case PLANE_MASK: + combine_write_reg(addr, val, size, &s->plane_mask); + break; + + case CMAP_BM_ACCESS: + combine_write_reg(addr, val, size, &s->cmap_bm_access); + break; + + case DST_BM_ACCESS: + combine_write_reg(addr, val, size, &s->dst_bm_access); + s->cmap_bm_access = 0; + break; + + case SRC_BM_ACCESS: + combine_write_reg(addr, val, size, &s->src_bm_access); + s->cmap_bm_access = 0; + break; + + case CONTROL_PLANE: + combine_write_reg(addr, val, size, &s->control_plane); + break; + + case TRANSFER_DATA: + combine_write_reg(addr, val, size, &s->transfer_data); + break; + + case 0x300200: + combine_write_reg(addr, val, size, &s->reg_300200); + break; + + case 0x300208: + combine_write_reg(addr, val, size, &s->reg_300208); + break; + + case 0x300218: + combine_write_reg(addr, val, size, &s->reg_300218); + break; + + case CURSOR_POS: + artist_invalidate_cursor(s); + combine_write_reg(addr, val, size, &s->cursor_pos); + artist_invalidate_cursor(s); + break; + + case CURSOR_CTRL: + break; + + case IMAGE_BITMAP_OP: + combine_write_reg(addr, val, size, &s->image_bitmap_op); + break; + + case FONT_WRITE_INCR_Y: + combine_write_reg(addr, val, size, &s->font_write1); + font_write(s, s->font_write1); + break; + + case FONT_WRITE_START: + combine_write_reg(addr, val, size, &s->font_write2); + s->font_write_pos_y = 0; + font_write(s, s->font_write2); + break; + + case 300104: + break; + + default: + qemu_log_mask(LOG_UNIMP, "%s: unknown register: reg=%08" HWADDR_PRIx + " val=%08" PRIx64 " size=%d\n", + __func__, addr, val, size); + break; + } +} + +static uint64_t combine_read_reg(hwaddr addr, int size, void *in) +{ + /* + * FIXME: is there a qemu helper for this? + */ + +#ifndef HOST_WORDS_BIGENDIAN + addr ^= 3; +#endif + + switch (size) { + case 1: + return *(uint8_t *)(in + (addr & 3)); + + case 2: + return *(uint16_t *)(in + (addr & 2)); + + case 4: + return *(uint32_t *)in; + + default: + qemu_log_mask(LOG_UNIMP, "unsupported read size: %d\n", size); + return 0; + } +} + +static uint64_t artist_reg_read(void *opaque, hwaddr addr, unsigned size) +{ + ARTISTState *s = opaque; + uint32_t val = 0; + + switch (addr & ~3ULL) { + /* Unknown status registers */ + case 0: + break; + + case 0x211110: + val = (s->width << 16) | s->height; + if (s->depth == 1) { + val |= 1 << 31; + } + break; + + case 0x100000: + case 0x300000: + case 0x300004: + case 0x300308: + case 0x380000: + break; + + case 0x300008: + case 0x380008: + /* + * FIFO ready flag. we're not emulating the FIFOs + * so we're always ready + */ + val = 0x10; + break; + + case 0x300200: + val = s->reg_300200; + break; + + case 0x300208: + val = s->reg_300208; + break; + + case 0x300218: + val = s->reg_300218; + break; + + case 0x30023c: + val = 0xac4ffdac; + break; + + case 0x380004: + /* 0x02000000 Buserror */ + val = 0x6dc20006; + break; + + default: + qemu_log_mask(LOG_UNIMP, "%s: unknown register: %08" HWADDR_PRIx + " size %d\n", __func__, addr, size); + break; + } + val = combine_read_reg(addr, size, &val); + trace_artist_reg_read(size, addr, artist_reg_name(addr & ~3ULL), val); + return val; +} + +static void artist_vram_write(void *opaque, hwaddr addr, uint64_t val, + unsigned size) +{ + ARTISTState *s = opaque; + struct vram_buffer *buf; + int posy = (addr >> 11) & 0x3ff; + int posx = addr & 0x7ff; + uint32_t offset; + trace_artist_vram_write(size, addr, val); + + if (s->cmap_bm_access) { + buf = &s->vram_buffer[ARTIST_BUFFER_CMAP]; + if (addr + 3 < buf->size) { + *(uint32_t *)(buf->data + addr) = val; + } + return; + } + + buf = vram_write_buffer(s); + if (!buf->size) { + return; + } + + if (posy > buf->height || posx > buf->width) { + return; + } + + offset = posy * buf->width + posx; + switch (size) { + case 4: + *(uint32_t *)(buf->data + offset) = be32_to_cpu(val); + memory_region_set_dirty(&buf->mr, offset, 4); + break; + case 2: + *(uint16_t *)(buf->data + offset) = be16_to_cpu(val); + memory_region_set_dirty(&buf->mr, offset, 2); + break; + case 1: + *(uint8_t *)(buf->data + offset) = val; + memory_region_set_dirty(&buf->mr, offset, 1); + break; + default: + break; + } +} + +static uint64_t artist_vram_read(void *opaque, hwaddr addr, unsigned size) +{ + ARTISTState *s = opaque; + struct vram_buffer *buf; + uint64_t val; + int posy, posx; + + if (s->cmap_bm_access) { + buf = &s->vram_buffer[ARTIST_BUFFER_CMAP]; + val = *(uint32_t *)(buf->data + addr); + trace_artist_vram_read(size, addr, 0, 0, val); + return 0; + } + + buf = vram_read_buffer(s); + if (!buf->size) { + return 0; + } + + posy = (addr >> 13) & 0x3ff; + posx = (addr >> 2) & 0x7ff; + + if (posy > buf->height || posx > buf->width) { + return 0; + } + + val = cpu_to_be32(*(uint32_t *)(buf->data + posy * buf->width + posx)); + trace_artist_vram_read(size, addr, posx, posy, val); + return val; +} + +static const MemoryRegionOps artist_reg_ops = { + .read = artist_reg_read, + .write = artist_reg_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 1, + .max_access_size = 4, + }, +}; + +static const MemoryRegionOps artist_vram_ops = { + .read = artist_vram_read, + .write = artist_vram_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 1, + .max_access_size = 4, + }, +}; + +static void artist_draw_cursor(ARTISTState *s) +{ + DisplaySurface *surface = qemu_console_surface(s->con); + uint32_t *data = (uint32_t *)surface_data(surface); + struct vram_buffer *cursor0, *cursor1 , *buf; + int cx, cy, cursor_pos_x, cursor_pos_y; + + cursor0 = &s->vram_buffer[ARTIST_BUFFER_CURSOR1]; + cursor1 = &s->vram_buffer[ARTIST_BUFFER_CURSOR2]; + buf = &s->vram_buffer[ARTIST_BUFFER_AP]; + + artist_get_cursor_pos(s, &cursor_pos_x, &cursor_pos_y); + + for (cy = 0; cy < s->cursor_height; cy++) { + + for (cx = 0; cx < s->cursor_width; cx++) { + + if (cursor_pos_y + cy < 0 || + cursor_pos_x + cx < 0 || + cursor_pos_y + cy > buf->height - 1 || + cursor_pos_x + cx > buf->width) { + continue; + } + + int dstoffset = (cursor_pos_y + cy) * s->width + + (cursor_pos_x + cx); + + if (cursor0->data[cy * cursor0->width + cx]) { + data[dstoffset] = 0; + } else { + if (cursor1->data[cy * cursor1->width + cx]) { + data[dstoffset] = 0xffffff; + } + } + } + } +} + +static void artist_draw_line(void *opaque, uint8_t *d, const uint8_t *src, + int width, int pitch) +{ + ARTISTState *s = ARTIST(opaque); + uint32_t *cmap, *data = (uint32_t *)d; + int x; + + cmap = (uint32_t *)(s->vram_buffer[ARTIST_BUFFER_CMAP].data + 0x400); + + for (x = 0; x < s->width; x++) { + *data++ = cmap[*src++]; + } +} + +static void artist_update_display(void *opaque) +{ + ARTISTState *s = opaque; + DisplaySurface *surface = qemu_console_surface(s->con); + int first = 0, last; + + + framebuffer_update_display(surface, &s->fbsection, s->width, s->height, + s->width, s->width * 4, 0, 0, artist_draw_line, + s, &first, &last); + + artist_draw_cursor(s); + + dpy_gfx_update(s->con, 0, 0, s->width, s->height); +} + +static void artist_invalidate(void *opaque) +{ + ARTISTState *s = ARTIST(opaque); + struct vram_buffer *buf = &s->vram_buffer[ARTIST_BUFFER_AP]; + memory_region_set_dirty(&buf->mr, 0, buf->size); +} + +static const GraphicHwOps artist_ops = { + .invalidate = artist_invalidate, + .gfx_update = artist_update_display, +}; + +static void artist_initfn(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + ARTISTState *s = ARTIST(obj); + + memory_region_init_io(&s->reg, obj, &artist_reg_ops, s, "artist.reg", + 4 * MiB); + memory_region_init_io(&s->vram_mem, obj, &artist_vram_ops, s, "artist.vram", + 8 * MiB); + sysbus_init_mmio(sbd, &s->reg); + sysbus_init_mmio(sbd, &s->vram_mem); +} + +static void artist_create_buffer(ARTISTState *s, const char *name, + hwaddr *offset, unsigned int idx, + int width, int height) +{ + struct vram_buffer *buf = s->vram_buffer + idx; + + memory_region_init_ram(&buf->mr, NULL, name, width * height, + &error_fatal); + memory_region_add_subregion_overlap(&s->mem_as_root, *offset, &buf->mr, 0); + + buf->data = memory_region_get_ram_ptr(&buf->mr); + buf->size = height * width; + buf->width = width; + buf->height = height; + + *offset += buf->size; +} + +static void artist_realizefn(DeviceState *dev, Error **errp) +{ + ARTISTState *s = ARTIST(dev); + struct vram_buffer *buf; + hwaddr offset = 0; + + memory_region_init(&s->mem_as_root, OBJECT(dev), "artist", ~0ull); + address_space_init(&s->as, &s->mem_as_root, "artist"); + + artist_create_buffer(s, "cmap", &offset, ARTIST_BUFFER_CMAP, 2048, 4); + artist_create_buffer(s, "ap", &offset, ARTIST_BUFFER_AP, + s->width, s->height); + artist_create_buffer(s, "cursor1", &offset, ARTIST_BUFFER_CURSOR1, 64, 64); + artist_create_buffer(s, "cursor2", &offset, ARTIST_BUFFER_CURSOR2, 64, 64); + artist_create_buffer(s, "attribute", &offset, ARTIST_BUFFER_ATTRIBUTE, + 64, 64); + + buf = &s->vram_buffer[ARTIST_BUFFER_AP]; + framebuffer_update_memory_section(&s->fbsection, &buf->mr, 0, + buf->width, buf->height); + /* + * no idea whether the cursor is fixed size or not, so assume 32x32 which + * seems sufficient for HP-UX X11. + */ + s->cursor_height = 32; + s->cursor_width = 32; + + s->con = graphic_console_init(DEVICE(dev), 0, &artist_ops, s); + qemu_console_resize(s->con, s->width, s->height); +} + +static int vmstate_artist_post_load(void *opaque, int version_id) +{ + artist_invalidate(opaque); + return 0; +} + +static const VMStateDescription vmstate_artist = { + .name = "artist", + .version_id = 1, + .minimum_version_id = 1, + .post_load = vmstate_artist_post_load, + .fields = (VMStateField[]) { + VMSTATE_UINT16(height, ARTISTState), + VMSTATE_UINT16(width, ARTISTState), + VMSTATE_UINT16(depth, ARTISTState), + VMSTATE_UINT32(fg_color, ARTISTState), + VMSTATE_UINT32(bg_color, ARTISTState), + VMSTATE_UINT32(vram_char_y, ARTISTState), + VMSTATE_UINT32(vram_bitmask, ARTISTState), + VMSTATE_UINT32(vram_start, ARTISTState), + VMSTATE_UINT32(vram_pos, ARTISTState), + VMSTATE_UINT32(vram_size, ARTISTState), + VMSTATE_UINT32(blockmove_source, ARTISTState), + VMSTATE_UINT32(blockmove_dest, ARTISTState), + VMSTATE_UINT32(blockmove_size, ARTISTState), + VMSTATE_UINT32(line_size, ARTISTState), + VMSTATE_UINT32(line_end, ARTISTState), + VMSTATE_UINT32(line_xy, ARTISTState), + VMSTATE_UINT32(cursor_pos, ARTISTState), + VMSTATE_UINT32(cursor_height, ARTISTState), + VMSTATE_UINT32(cursor_width, ARTISTState), + VMSTATE_UINT32(plane_mask, ARTISTState), + VMSTATE_UINT32(reg_100080, ARTISTState), + VMSTATE_UINT32(reg_300200, ARTISTState), + VMSTATE_UINT32(reg_300208, ARTISTState), + VMSTATE_UINT32(reg_300218, ARTISTState), + VMSTATE_UINT32(cmap_bm_access, ARTISTState), + VMSTATE_UINT32(dst_bm_access, ARTISTState), + VMSTATE_UINT32(src_bm_access, ARTISTState), + VMSTATE_UINT32(control_plane, ARTISTState), + VMSTATE_UINT32(transfer_data, ARTISTState), + VMSTATE_UINT32(image_bitmap_op, ARTISTState), + VMSTATE_UINT32(font_write1, ARTISTState), + VMSTATE_UINT32(font_write2, ARTISTState), + VMSTATE_UINT32(font_write_pos_y, ARTISTState), + VMSTATE_END_OF_LIST() + } +}; + +static Property artist_properties[] = { + DEFINE_PROP_UINT16("width", ARTISTState, width, 1280), + DEFINE_PROP_UINT16("height", ARTISTState, height, 1024), + DEFINE_PROP_UINT16("depth", ARTISTState, depth, 8), + DEFINE_PROP_END_OF_LIST(), +}; + +static void artist_reset(DeviceState *qdev) +{ +} + +static void artist_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = artist_realizefn; + dc->vmsd = &vmstate_artist; + dc->reset = artist_reset; + device_class_set_props(dc, artist_properties); +} + +static const TypeInfo artist_info = { + .name = TYPE_ARTIST, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(ARTISTState), + .instance_init = artist_initfn, + .class_init = artist_class_init, +}; + +static void artist_register_types(void) +{ + type_register_static(&artist_info); +} + +type_init(artist_register_types) diff --git a/hw/display/ati.c b/hw/display/ati.c index db3b254316..58ec8291d4 100644 --- a/hw/display/ati.c +++ b/hw/display/ati.c @@ -980,7 +980,7 @@ static void ati_vga_class_init(ObjectClass *klass, void *data) PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); dc->reset = ati_vga_reset; - dc->props = ati_vga_properties; + device_class_set_props(dc, ati_vga_properties); dc->hotpluggable = false; set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); diff --git a/hw/display/ati_2d.c b/hw/display/ati_2d.c index 42e82311eb..23a8ae0cd8 100644 --- a/hw/display/ati_2d.c +++ b/hw/display/ati_2d.c @@ -53,12 +53,20 @@ void ati_2d_blt(ATIVGAState *s) s->vga.vbe_start_addr, surface_data(ds), surface_stride(ds), surface_bits_per_pixel(ds), (s->regs.dp_mix & GMC_ROP3_MASK) >> 16); - int dst_x = (s->regs.dp_cntl & DST_X_LEFT_TO_RIGHT ? - s->regs.dst_x : s->regs.dst_x + 1 - s->regs.dst_width); - int dst_y = (s->regs.dp_cntl & DST_Y_TOP_TO_BOTTOM ? - s->regs.dst_y : s->regs.dst_y + 1 - s->regs.dst_height); + unsigned dst_x = (s->regs.dp_cntl & DST_X_LEFT_TO_RIGHT ? + s->regs.dst_x : s->regs.dst_x + 1 - s->regs.dst_width); + unsigned dst_y = (s->regs.dp_cntl & DST_Y_TOP_TO_BOTTOM ? + s->regs.dst_y : s->regs.dst_y + 1 - s->regs.dst_height); int bpp = ati_bpp_from_datatype(s); + if (!bpp) { + qemu_log_mask(LOG_GUEST_ERROR, "Invalid bpp\n"); + return; + } int dst_stride = DEFAULT_CNTL ? s->regs.dst_pitch : s->regs.default_pitch; + if (!dst_stride) { + qemu_log_mask(LOG_GUEST_ERROR, "Zero dest pitch\n"); + return; + } uint8_t *dst_bits = s->vga.vram_ptr + (DEFAULT_CNTL ? s->regs.dst_offset : s->regs.default_offset); @@ -82,12 +90,16 @@ void ati_2d_blt(ATIVGAState *s) switch (s->regs.dp_mix & GMC_ROP3_MASK) { case ROP3_SRCCOPY: { - int src_x = (s->regs.dp_cntl & DST_X_LEFT_TO_RIGHT ? - s->regs.src_x : s->regs.src_x + 1 - s->regs.dst_width); - int src_y = (s->regs.dp_cntl & DST_Y_TOP_TO_BOTTOM ? - s->regs.src_y : s->regs.src_y + 1 - s->regs.dst_height); + unsigned src_x = (s->regs.dp_cntl & DST_X_LEFT_TO_RIGHT ? + s->regs.src_x : s->regs.src_x + 1 - s->regs.dst_width); + unsigned src_y = (s->regs.dp_cntl & DST_Y_TOP_TO_BOTTOM ? + s->regs.src_y : s->regs.src_y + 1 - s->regs.dst_height); int src_stride = DEFAULT_CNTL ? s->regs.src_pitch : s->regs.default_pitch; + if (!src_stride) { + qemu_log_mask(LOG_GUEST_ERROR, "Zero source pitch\n"); + return; + } uint8_t *src_bits = s->vga.vram_ptr + (DEFAULT_CNTL ? s->regs.src_offset : s->regs.default_offset); @@ -137,8 +149,10 @@ void ati_2d_blt(ATIVGAState *s) dst_y * surface_stride(ds), s->regs.dst_height * surface_stride(ds)); } - s->regs.dst_x += s->regs.dst_width; - s->regs.dst_y += s->regs.dst_height; + s->regs.dst_x = (s->regs.dp_cntl & DST_X_LEFT_TO_RIGHT ? + dst_x + s->regs.dst_width : dst_x); + s->regs.dst_y = (s->regs.dp_cntl & DST_Y_TOP_TO_BOTTOM ? + dst_y + s->regs.dst_height : dst_y); break; } case ROP3_PATCOPY: @@ -179,7 +193,8 @@ void ati_2d_blt(ATIVGAState *s) dst_y * surface_stride(ds), s->regs.dst_height * surface_stride(ds)); } - s->regs.dst_y += s->regs.dst_height; + s->regs.dst_y = (s->regs.dp_cntl & DST_Y_TOP_TO_BOTTOM ? + dst_y + s->regs.dst_height : dst_y); break; } default: diff --git a/hw/display/bcm2835_fb.c b/hw/display/bcm2835_fb.c index 85aaa54330..c6263808a2 100644 --- a/hw/display/bcm2835_fb.c +++ b/hw/display/bcm2835_fb.c @@ -1,7 +1,6 @@ /* * Raspberry Pi emulation (c) 2012 Gregory Estrade * Refactoring for Pi2 Copyright (c) 2015, Microsoft. Written by Andrew Baumann. - * This code is licensed under the GNU GPLv2 and later. * * Heavily based on milkymist-vgafb.c, copyright terms below: * QEMU model of the Milkymist VGA framebuffer. @@ -451,7 +450,7 @@ static void bcm2835_fb_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->props = bcm2835_fb_props; + device_class_set_props(dc, bcm2835_fb_props); dc->realize = bcm2835_fb_realize; dc->reset = bcm2835_fb_reset; dc->vmsd = &vmstate_bcm2835_fb; diff --git a/hw/display/bochs-display.c b/hw/display/bochs-display.c index dc1bd1641d..70eb619ef4 100644 --- a/hw/display/bochs-display.c +++ b/hw/display/bochs-display.c @@ -252,6 +252,8 @@ static void bochs_display_update(void *opaque) dpy_gfx_update(s->con, 0, ys, mode.width, y - ys); } + + g_free(snap); } } @@ -282,8 +284,8 @@ static void bochs_display_realize(PCIDevice *dev, Error **errp) memory_region_init_io(&s->qext, obj, &bochs_display_qext_ops, s, "qemu extended regs", PCI_VGA_QEXT_SIZE); - memory_region_init(&s->mmio, obj, "bochs-display-mmio", - PCI_VGA_MMIO_SIZE); + memory_region_init_io(&s->mmio, obj, &unassigned_io_ops, NULL, + "bochs-display-mmio", PCI_VGA_MMIO_SIZE); memory_region_add_subregion(&s->mmio, PCI_VGA_BOCHS_OFFSET, &s->vbe); memory_region_add_subregion(&s->mmio, PCI_VGA_QEXT_OFFSET, &s->qext); @@ -362,7 +364,7 @@ static void bochs_display_class_init(ObjectClass *klass, void *data) k->romfile = "vgabios-bochs-display.bin"; k->exit = bochs_display_exit; dc->vmsd = &vmstate_bochs_display; - dc->props = bochs_display_properties; + device_class_set_props(dc, bochs_display_properties); set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); } diff --git a/hw/display/cg3.c b/hw/display/cg3.c index f90baae4de..a1ede10394 100644 --- a/hw/display/cg3.c +++ b/hw/display/cg3.c @@ -287,9 +287,8 @@ static void cg3_initfn(Object *obj) SysBusDevice *sbd = SYS_BUS_DEVICE(obj); CG3State *s = CG3(obj); - memory_region_init_ram_nomigrate(&s->rom, obj, "cg3.prom", FCODE_MAX_ROM_SIZE, - &error_fatal); - memory_region_set_readonly(&s->rom, true); + memory_region_init_rom_nomigrate(&s->rom, obj, "cg3.prom", + FCODE_MAX_ROM_SIZE, &error_fatal); sysbus_init_mmio(sbd, &s->rom); memory_region_init_io(&s->reg, obj, &cg3_reg_ops, s, "cg3.reg", @@ -382,7 +381,7 @@ static void cg3_class_init(ObjectClass *klass, void *data) dc->realize = cg3_realizefn; dc->reset = cg3_reset; dc->vmsd = &vmstate_cg3; - dc->props = cg3_properties; + device_class_set_props(dc, cg3_properties); } static const TypeInfo cg3_info = { diff --git a/hw/display/cirrus_vga.c b/hw/display/cirrus_vga.c index cd283e53b4..0d391e1300 100644 --- a/hw/display/cirrus_vga.c +++ b/hw/display/cirrus_vga.c @@ -3031,7 +3031,7 @@ static void cirrus_vga_class_init(ObjectClass *klass, void *data) set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); dc->desc = "Cirrus CLGD 54xx VGA"; dc->vmsd = &vmstate_pci_cirrus_vga; - dc->props = pci_vga_cirrus_properties; + device_class_set_props(dc, pci_vga_cirrus_properties); dc->hotpluggable = false; } diff --git a/hw/display/cirrus_vga_isa.c b/hw/display/cirrus_vga_isa.c index 7ef8e59224..825ba57298 100644 --- a/hw/display/cirrus_vga_isa.c +++ b/hw/display/cirrus_vga_isa.c @@ -80,7 +80,7 @@ static void isa_cirrus_vga_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_cirrus_vga; dc->realize = isa_cirrus_vga_realizefn; - dc->props = isa_cirrus_vga_properties; + device_class_set_props(dc, isa_cirrus_vga_properties); set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); } diff --git a/hw/display/edid-generate.c b/hw/display/edid-generate.c index 75c945a948..e58472fde5 100644 --- a/hw/display/edid-generate.c +++ b/hw/display/edid-generate.c @@ -360,8 +360,8 @@ void qemu_edid_generate(uint8_t *edid, size_t size, edid[20] = 0xa5; /* screen size: undefined */ - edid[21] = info->prefx * info->dpi / 2540; - edid[22] = info->prefy * info->dpi / 2540; + edid[21] = info->prefx * 254 / 100 / info->dpi; + edid[22] = info->prefy * 254 / 100 / info->dpi; /* display gamma: 2.2 */ edid[23] = 220 - 100; diff --git a/hw/display/exynos4210_fimd.c b/hw/display/exynos4210_fimd.c index c1071ecd46..1c0266ce9f 100644 --- a/hw/display/exynos4210_fimd.c +++ b/hw/display/exynos4210_fimd.c @@ -1164,7 +1164,8 @@ static void fimd_update_memory_section(Exynos4210fimdState *s, unsigned win) goto error_return; } - w->host_fb_addr = cpu_physical_memory_map(fb_start_addr, &fb_mapped_len, 0); + w->host_fb_addr = cpu_physical_memory_map(fb_start_addr, &fb_mapped_len, + false); if (!w->host_fb_addr) { DPRINT_ERROR("Failed to map window %u framebuffer\n", win); goto error_return; @@ -1310,7 +1311,6 @@ static void exynos4210_fimd_update(void *opaque) } host_fb_addr += inc_size; fb_line_addr += inc_size; - is_dirty = false; } g_free(snap); blend = true; diff --git a/hw/display/g364fb.c b/hw/display/g364fb.c index 839d26eca1..adcba96e34 100644 --- a/hw/display/g364fb.c +++ b/hw/display/g364fb.c @@ -477,7 +477,8 @@ static void g364fb_init(DeviceState *dev, G364State *s) s->con = graphic_console_init(dev, 0, &g364fb_ops, s); - memory_region_init_io(&s->mem_ctrl, NULL, &g364fb_ctrl_ops, s, "ctrl", 0x180000); + memory_region_init_io(&s->mem_ctrl, OBJECT(dev), &g364fb_ctrl_ops, s, + "ctrl", 0x180000); memory_region_init_ram_ptr(&s->mem_vram, NULL, "vram", s->vram_size, s->vram); vmstate_register_ram(&s->mem_vram, dev); @@ -526,7 +527,7 @@ static void g364fb_sysbus_class_init(ObjectClass *klass, void *data) dc->desc = "G364 framebuffer"; dc->reset = g364fb_sysbus_reset; dc->vmsd = &vmstate_g364fb; - dc->props = g364fb_sysbus_properties; + device_class_set_props(dc, g364fb_sysbus_properties); } static const TypeInfo g364fb_sysbus_info = { diff --git a/hw/display/i2c-ddc.c b/hw/display/i2c-ddc.c index fc70b7930c..13eb529fc1 100644 --- a/hw/display/i2c-ddc.c +++ b/hw/display/i2c-ddc.c @@ -107,7 +107,7 @@ static void i2c_ddc_class_init(ObjectClass *oc, void *data) dc->reset = i2c_ddc_reset; dc->vmsd = &vmstate_i2c_ddc; - dc->props = i2c_ddc_properties; + device_class_set_props(dc, i2c_ddc_properties); isc->event = i2c_ddc_event; isc->recv = i2c_ddc_rx; isc->send = i2c_ddc_tx; diff --git a/hw/display/jazz_led.c b/hw/display/jazz_led.c index 3e0112b1ca..1d845597f9 100644 --- a/hw/display/jazz_led.c +++ b/hw/display/jazz_led.c @@ -90,25 +90,25 @@ static void draw_horizontal_line(DisplaySurface *ds, bpp = (surface_bits_per_pixel(ds) + 7) >> 3; d = surface_data(ds) + surface_stride(ds) * posy + bpp * posx1; - switch(bpp) { - case 1: - for (x = posx1; x <= posx2; x++) { - *((uint8_t *)d) = color; - d++; - } - break; - case 2: - for (x = posx1; x <= posx2; x++) { - *((uint16_t *)d) = color; - d += 2; - } - break; - case 4: - for (x = posx1; x <= posx2; x++) { - *((uint32_t *)d) = color; - d += 4; - } - break; + switch (bpp) { + case 1: + for (x = posx1; x <= posx2; x++) { + *((uint8_t *)d) = color; + d++; + } + break; + case 2: + for (x = posx1; x <= posx2; x++) { + *((uint16_t *)d) = color; + d += 2; + } + break; + case 4: + for (x = posx1; x <= posx2; x++) { + *((uint32_t *)d) = color; + d += 4; + } + break; } } @@ -121,25 +121,25 @@ static void draw_vertical_line(DisplaySurface *ds, bpp = (surface_bits_per_pixel(ds) + 7) >> 3; d = surface_data(ds) + surface_stride(ds) * posy1 + bpp * posx; - switch(bpp) { - case 1: - for (y = posy1; y <= posy2; y++) { - *((uint8_t *)d) = color; - d += surface_stride(ds); - } - break; - case 2: - for (y = posy1; y <= posy2; y++) { - *((uint16_t *)d) = color; - d += surface_stride(ds); - } - break; - case 4: - for (y = posy1; y <= posy2; y++) { - *((uint32_t *)d) = color; - d += surface_stride(ds); - } - break; + switch (bpp) { + case 1: + for (y = posy1; y <= posy2; y++) { + *((uint8_t *)d) = color; + d += surface_stride(ds); + } + break; + case 2: + for (y = posy1; y <= posy2; y++) { + *((uint16_t *)d) = color; + d += surface_stride(ds); + } + break; + case 4: + for (y = posy1; y <= posy2; y++) { + *((uint32_t *)d) = color; + d += surface_stride(ds); + } + break; } } @@ -164,28 +164,28 @@ static void jazz_led_update_display(void *opaque) if (s->state & REDRAW_SEGMENTS) { /* set colors according to bpp */ switch (surface_bits_per_pixel(surface)) { - case 8: - color_segment = rgb_to_pixel8(0xaa, 0xaa, 0xaa); - color_led = rgb_to_pixel8(0x00, 0xff, 0x00); - break; - case 15: - color_segment = rgb_to_pixel15(0xaa, 0xaa, 0xaa); - color_led = rgb_to_pixel15(0x00, 0xff, 0x00); - break; - case 16: - color_segment = rgb_to_pixel16(0xaa, 0xaa, 0xaa); - color_led = rgb_to_pixel16(0x00, 0xff, 0x00); - break; - case 24: - color_segment = rgb_to_pixel24(0xaa, 0xaa, 0xaa); - color_led = rgb_to_pixel24(0x00, 0xff, 0x00); - break; - case 32: - color_segment = rgb_to_pixel32(0xaa, 0xaa, 0xaa); - color_led = rgb_to_pixel32(0x00, 0xff, 0x00); - break; - default: - return; + case 8: + color_segment = rgb_to_pixel8(0xaa, 0xaa, 0xaa); + color_led = rgb_to_pixel8(0x00, 0xff, 0x00); + break; + case 15: + color_segment = rgb_to_pixel15(0xaa, 0xaa, 0xaa); + color_led = rgb_to_pixel15(0x00, 0xff, 0x00); + break; + case 16: + color_segment = rgb_to_pixel16(0xaa, 0xaa, 0xaa); + color_led = rgb_to_pixel16(0x00, 0xff, 0x00); + break; + case 24: + color_segment = rgb_to_pixel24(0xaa, 0xaa, 0xaa); + color_led = rgb_to_pixel24(0x00, 0xff, 0x00); + break; + case 32: + color_segment = rgb_to_pixel32(0xaa, 0xaa, 0xaa); + color_led = rgb_to_pixel32(0x00, 0xff, 0x00); + break; + default: + return; } /* display segments */ @@ -205,8 +205,9 @@ static void jazz_led_update_display(void *opaque) (s->segments & 0x80) ? color_segment : 0); /* display led */ - if (!(s->segments & 0x01)) + if (!(s->segments & 0x01)) { color_led = 0; /* black */ + } draw_horizontal_line(surface, 68, 50, 50, color_led); draw_horizontal_line(surface, 69, 49, 51, color_led); draw_horizontal_line(surface, 70, 48, 52, color_led); diff --git a/hw/display/macfb.c b/hw/display/macfb.c index f4fa8e3206..b68faff4bb 100644 --- a/hw/display/macfb.c +++ b/hw/display/macfb.c @@ -362,8 +362,8 @@ static void macfb_common_realize(DeviceState *dev, MacfbState *s, Error **errp) return; } - memory_region_init_io(&s->mem_ctrl, NULL, &macfb_ctrl_ops, s, "macfb-ctrl", - 0x1000); + memory_region_init_io(&s->mem_ctrl, OBJECT(dev), &macfb_ctrl_ops, s, + "macfb-ctrl", 0x1000); memory_region_init_ram_nomigrate(&s->mem_vram, OBJECT(s), "macfb-vram", MACFB_VRAM_SIZE, errp); @@ -437,7 +437,7 @@ static void macfb_sysbus_class_init(ObjectClass *klass, void *data) dc->desc = "SysBus Macintosh framebuffer"; dc->reset = macfb_sysbus_reset; dc->vmsd = &vmstate_macfb; - dc->props = macfb_sysbus_properties; + device_class_set_props(dc, macfb_sysbus_properties); } static void macfb_nubus_class_init(ObjectClass *klass, void *data) @@ -450,7 +450,7 @@ static void macfb_nubus_class_init(ObjectClass *klass, void *data) dc->desc = "Nubus Macintosh framebuffer"; dc->reset = macfb_nubus_reset; dc->vmsd = &vmstate_macfb; - dc->props = macfb_nubus_properties; + device_class_set_props(dc, macfb_nubus_properties); } static TypeInfo macfb_sysbus_info = { diff --git a/hw/display/milkymist-tmu2.c b/hw/display/milkymist-tmu2.c index 199f1227e7..513c0d5bab 100644 --- a/hw/display/milkymist-tmu2.c +++ b/hw/display/milkymist-tmu2.c @@ -218,7 +218,7 @@ static void tmu2_start(MilkymistTMU2State *s) glGenTextures(1, &texture); glBindTexture(GL_TEXTURE_2D, texture); fb_len = 2ULL * s->regs[R_TEXHRES] * s->regs[R_TEXVRES]; - fb = cpu_physical_memory_map(s->regs[R_TEXFBUF], &fb_len, 0); + fb = cpu_physical_memory_map(s->regs[R_TEXFBUF], &fb_len, false); if (fb == NULL) { glDeleteTextures(1, &texture); glXMakeContextCurrent(s->dpy, None, None, NULL); @@ -262,7 +262,7 @@ static void tmu2_start(MilkymistTMU2State *s) /* Read the QEMU dest. framebuffer into the OpenGL framebuffer */ fb_len = 2ULL * s->regs[R_DSTHRES] * s->regs[R_DSTVRES]; - fb = cpu_physical_memory_map(s->regs[R_DSTFBUF], &fb_len, 0); + fb = cpu_physical_memory_map(s->regs[R_DSTFBUF], &fb_len, false); if (fb == NULL) { glDeleteTextures(1, &texture); glXMakeContextCurrent(s->dpy, None, None, NULL); @@ -281,7 +281,7 @@ static void tmu2_start(MilkymistTMU2State *s) /* Map the texture */ mesh_len = MESH_MAXSIZE*MESH_MAXSIZE*sizeof(struct vertex); - mesh = cpu_physical_memory_map(s->regs[R_VERTICESADDR], &mesh_len, 0); + mesh = cpu_physical_memory_map(s->regs[R_VERTICESADDR], &mesh_len, false); if (mesh == NULL) { glDeleteTextures(1, &texture); glXMakeContextCurrent(s->dpy, None, None, NULL); @@ -298,7 +298,7 @@ static void tmu2_start(MilkymistTMU2State *s) /* Write back the OpenGL framebuffer to the QEMU framebuffer */ fb_len = 2ULL * s->regs[R_DSTHRES] * s->regs[R_DSTVRES]; - fb = cpu_physical_memory_map(s->regs[R_DSTFBUF], &fb_len, 1); + fb = cpu_physical_memory_map(s->regs[R_DSTFBUF], &fb_len, true); if (fb == NULL) { glDeleteTextures(1, &texture); glXMakeContextCurrent(s->dpy, None, None, NULL); diff --git a/hw/display/milkymist-vgafb.c b/hw/display/milkymist-vgafb.c index 8b0da4660a..6a6441e6ea 100644 --- a/hw/display/milkymist-vgafb.c +++ b/hw/display/milkymist-vgafb.c @@ -341,7 +341,7 @@ static void milkymist_vgafb_class_init(ObjectClass *klass, void *data) dc->reset = milkymist_vgafb_reset; dc->vmsd = &vmstate_milkymist_vgafb; - dc->props = milkymist_vgafb_properties; + device_class_set_props(dc, milkymist_vgafb_properties); dc->realize = milkymist_vgafb_realize; } diff --git a/hw/display/omap_dss.c b/hw/display/omap_dss.c index 637aae8d39..32dc0d6aa7 100644 --- a/hw/display/omap_dss.c +++ b/hw/display/omap_dss.c @@ -632,7 +632,7 @@ static void omap_rfbi_transfer_start(struct omap_dss_s *s) len = s->rfbi.pixels * 2; data_addr = s->dispc.l[0].addr[0]; - data = cpu_physical_memory_map(data_addr, &len, 0); + data = cpu_physical_memory_map(data_addr, &len, false); if (data && len != s->rfbi.pixels * 2) { cpu_physical_memory_unmap(data, len, 0, 0); data = NULL; diff --git a/hw/display/omap_lcdc.c b/hw/display/omap_lcdc.c index 6ad13f2e9e..fa4a381db6 100644 --- a/hw/display/omap_lcdc.c +++ b/hw/display/omap_lcdc.c @@ -91,9 +91,9 @@ static void omap_update_display(void *opaque) frame_offset = 0; if (omap_lcd->plm != 2) { - cpu_physical_memory_read(omap_lcd->dma->phys_framebuffer[ - omap_lcd->dma->current_frame], - (void *)omap_lcd->palette, 0x200); + cpu_physical_memory_read( + omap_lcd->dma->phys_framebuffer[omap_lcd->dma->current_frame], + omap_lcd->palette, 0x200); switch (omap_lcd->palette[0] >> 12 & 7) { case 3 ... 7: frame_offset += 0x200; @@ -244,8 +244,8 @@ static void omap_lcd_update(struct omap_lcd_panel_s *s) { if (s->plm != 2 && !s->palette_done) { cpu_physical_memory_read( - s->dma->phys_framebuffer[s->dma->current_frame], - (void *)s->palette, 0x200); + s->dma->phys_framebuffer[s->dma->current_frame], + s->palette, 0x200); s->palette_done = 1; omap_lcd_interrupts(s); } diff --git a/hw/display/pxa2xx_lcd.c b/hw/display/pxa2xx_lcd.c index 05f5f84671..464e93161a 100644 --- a/hw/display/pxa2xx_lcd.c +++ b/hw/display/pxa2xx_lcd.c @@ -593,7 +593,6 @@ static void pxa2xx_palette_parse(PXA2xxLCDState *s, int ch, int bpp) n = 256; break; default: - format = 0; return; } diff --git a/hw/display/qxl-render.c b/hw/display/qxl-render.c index f7fdc4901e..3ce2e57b8f 100644 --- a/hw/display/qxl-render.c +++ b/hw/display/qxl-render.c @@ -109,7 +109,7 @@ static void qxl_render_update_area_unlocked(PCIQXLDevice *qxl) qxl->guest_primary.surface.mem, MEMSLOT_GROUP_GUEST); if (!qxl->guest_primary.data) { - return; + goto end; } qxl_set_rect_to_surface(qxl, &qxl->dirty[0]); qxl->num_dirty_rects = 1; @@ -137,7 +137,7 @@ static void qxl_render_update_area_unlocked(PCIQXLDevice *qxl) } if (!qxl->guest_primary.data) { - return; + goto end; } for (i = 0; i < qxl->num_dirty_rects; i++) { if (qemu_spice_rect_is_empty(qxl->dirty+i)) { @@ -158,6 +158,11 @@ static void qxl_render_update_area_unlocked(PCIQXLDevice *qxl) qxl->dirty[i].bottom - qxl->dirty[i].top); } qxl->num_dirty_rects = 0; + +end: + if (qxl->render_update_cookie_num == 0) { + graphic_hw_update_done(qxl->ssd.dcl.con); + } } /* diff --git a/hw/display/qxl.c b/hw/display/qxl.c index cd7eb39d20..227da69a50 100644 --- a/hw/display/qxl.c +++ b/hw/display/qxl.c @@ -1181,6 +1181,7 @@ static const QXLInterface qxl_interface = { static const GraphicHwOps qxl_ops = { .gfx_update = qxl_hw_update, + .gfx_update_async = true, }; static void qxl_enter_vga_mode(PCIQXLDevice *d) @@ -1308,7 +1309,8 @@ static void qxl_vga_ioport_write(void *opaque, uint32_t addr, uint32_t val) PCIQXLDevice *qxl = container_of(vga, PCIQXLDevice, vga); trace_qxl_io_write_vga(qxl->id, qxl_mode_to_string(qxl->mode), addr, val); - if (qxl->mode != QXL_MODE_VGA) { + if (qxl->mode != QXL_MODE_VGA && + qxl->revision <= QXL_REVISION_STABLE_V12) { qxl_destroy_primary(qxl, QXL_SYNC); qxl_soft_reset(qxl); } @@ -1629,7 +1631,7 @@ static void ioport_write(void *opaque, hwaddr addr, PCIQXLDevice *d = opaque; uint32_t io_port = addr; qxl_async_io async = QXL_SYNC; - uint32_t orig_io_port = io_port; + uint32_t orig_io_port; if (d->guest_bug && io_port != QXL_IO_RESET) { return; @@ -1763,7 +1765,7 @@ async_common: qxl_set_mode(d, val, 0); break; case QXL_IO_LOG: - if (TRACE_QXL_IO_LOG_ENABLED || d->guestdebug) { + if (trace_event_get_state_backends(TRACE_QXL_IO_LOG) || d->guestdebug) { /* We cannot trust the guest to NUL terminate d->ram->log_buf */ char *log_buf = g_strndup((const char *)d->ram->log_buf, sizeof(d->ram->log_buf)); @@ -2120,6 +2122,10 @@ static void qxl_realize_common(PCIQXLDevice *qxl, Error **errp) pci_device_rev = QXL_REVISION_STABLE_V12; io_size = pow2ceil(QXL_IO_RANGE_SIZE); break; + case 5: /* qxl-5 */ + pci_device_rev = QXL_REVISION_STABLE_V12 + 1; + io_size = pow2ceil(QXL_IO_RANGE_SIZE); + break; default: error_setg(errp, "Invalid revision %d for qxl device (max %d)", qxl->revision, QXL_DEFAULT_REVISION); @@ -2130,7 +2136,7 @@ static void qxl_realize_common(PCIQXLDevice *qxl, Error **errp) pci_set_byte(&config[PCI_INTERRUPT_PIN], 1); qxl->rom_size = qxl_rom_size(); - memory_region_init_ram(&qxl->rom_bar, OBJECT(qxl), "qxl.vrom", + memory_region_init_rom(&qxl->rom_bar, OBJECT(qxl), "qxl.vrom", qxl->rom_size, &error_fatal); init_qxl_rom(qxl); init_qxl_ram(qxl); @@ -2477,7 +2483,7 @@ static void qxl_pci_class_init(ObjectClass *klass, void *data) set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); dc->reset = qxl_reset_handler; dc->vmsd = &qxl_vmstate; - dc->props = qxl_properties; + device_class_set_props(dc, qxl_properties); } static const TypeInfo qxl_pci_type_info = { diff --git a/hw/display/qxl.h b/hw/display/qxl.h index 80eb0d2672..707631a1f5 100644 --- a/hw/display/qxl.h +++ b/hw/display/qxl.h @@ -144,7 +144,7 @@ typedef struct PCIQXLDevice { } \ } while (0) -#define QXL_DEFAULT_REVISION QXL_REVISION_STABLE_V12 +#define QXL_DEFAULT_REVISION (QXL_REVISION_STABLE_V12 + 1) /* qxl.c */ void *qxl_phys2virt(PCIQXLDevice *qxl, QXLPHYSICAL phys, int group_id); diff --git a/hw/display/ramfb-standalone.c b/hw/display/ramfb-standalone.c index 1f72450303..d76a9d0fe2 100644 --- a/hw/display/ramfb-standalone.c +++ b/hw/display/ramfb-standalone.c @@ -52,7 +52,7 @@ static void ramfb_class_initfn(ObjectClass *klass, void *data) set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); dc->realize = ramfb_realizefn; - dc->props = ramfb_properties; + device_class_set_props(dc, ramfb_properties); dc->desc = "ram framebuffer standalone device"; dc->user_creatable = true; } diff --git a/hw/display/ramfb.c b/hw/display/ramfb.c index cd94940223..7ba07c80f6 100644 --- a/hw/display/ramfb.c +++ b/hw/display/ramfb.c @@ -57,7 +57,7 @@ static DisplaySurface *ramfb_create_display_surface(int width, int height, } size = (hwaddr)linesize * height; - data = cpu_physical_memory_map(addr, &size, 0); + data = cpu_physical_memory_map(addr, &size, false); if (size != (hwaddr)linesize * height) { cpu_physical_memory_unmap(data, size, 0, 0); return NULL; diff --git a/hw/display/sm501.c b/hw/display/sm501.c index 1f33c87e65..de0ab9d977 100644 --- a/hw/display/sm501.c +++ b/hw/display/sm501.c @@ -1930,7 +1930,7 @@ typedef struct { SM501State state; uint32_t vram_size; uint32_t base; - void *chr_state; + SerialMM serial; } SM501SysBusState; static void sm501_realize_sysbus(DeviceState *dev, Error **errp) @@ -1938,6 +1938,7 @@ static void sm501_realize_sysbus(DeviceState *dev, Error **errp) SM501SysBusState *s = SYSBUS_SM501(dev); SysBusDevice *sbd = SYS_BUS_DEVICE(dev); DeviceState *usb_dev; + MemoryRegion *mr; sm501_init(&s->state, dev, s->vram_size); if (get_local_mem_size(&s->state) != s->vram_size) { @@ -1958,17 +1959,15 @@ static void sm501_realize_sysbus(DeviceState *dev, Error **errp) sysbus_pass_irq(sbd, SYS_BUS_DEVICE(usb_dev)); /* bridge to serial emulation module */ - if (s->chr_state) { - serial_mm_init(&s->state.mmio_region, SM501_UART0, 2, - NULL, /* TODO : chain irq to IRL */ - 115200, s->chr_state, DEVICE_LITTLE_ENDIAN); - } + qdev_init_nofail(DEVICE(&s->serial)); + mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->serial), 0); + memory_region_add_subregion(&s->state.mmio_region, SM501_UART0, mr); + /* TODO : chain irq to IRL */ } static Property sm501_sysbus_properties[] = { DEFINE_PROP_UINT32("vram-size", SM501SysBusState, vram_size, 0), DEFINE_PROP_UINT32("base", SM501SysBusState, base, 0), - DEFINE_PROP_PTR("chr-state", SM501SysBusState, chr_state), DEFINE_PROP_END_OF_LIST(), }; @@ -1996,12 +1995,23 @@ static void sm501_sysbus_class_init(ObjectClass *klass, void *data) dc->realize = sm501_realize_sysbus; set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); dc->desc = "SM501 Multimedia Companion"; - dc->props = sm501_sysbus_properties; + device_class_set_props(dc, sm501_sysbus_properties); dc->reset = sm501_reset_sysbus; dc->vmsd = &vmstate_sm501_sysbus; - /* Note: pointer property "chr-state" may remain null, thus - * no need for dc->user_creatable = false; - */ +} + +static void sm501_sysbus_init(Object *o) +{ + SM501SysBusState *sm501 = SYSBUS_SM501(o); + SerialMM *smm = &sm501->serial; + + sysbus_init_child_obj(o, "serial", smm, sizeof(SerialMM), TYPE_SERIAL_MM); + qdev_set_legacy_instance_id(DEVICE(smm), SM501_UART0, 2); + qdev_prop_set_uint8(DEVICE(smm), "regshift", 2); + qdev_prop_set_uint8(DEVICE(smm), "endianness", DEVICE_LITTLE_ENDIAN); + + object_property_add_alias(o, "chardev", + OBJECT(smm), "chardev", &error_abort); } static const TypeInfo sm501_sysbus_info = { @@ -2009,6 +2019,7 @@ static const TypeInfo sm501_sysbus_info = { .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(SM501SysBusState), .class_init = sm501_sysbus_class_init, + .instance_init = sm501_sysbus_init, }; #define TYPE_PCI_SM501 "sm501" @@ -2074,7 +2085,7 @@ static void sm501_pci_class_init(ObjectClass *klass, void *data) k->class_id = PCI_CLASS_DISPLAY_OTHER; set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); dc->desc = "SM501 Display Controller"; - dc->props = sm501_pci_properties; + device_class_set_props(dc, sm501_pci_properties); dc->reset = sm501_reset_pci; dc->hotpluggable = false; dc->vmsd = &vmstate_sm501_pci; diff --git a/hw/display/tcx.c b/hw/display/tcx.c index 14e829d3fa..76de16e8ea 100644 --- a/hw/display/tcx.c +++ b/hw/display/tcx.c @@ -410,6 +410,7 @@ static uint64_t tcx_dac_readl(void *opaque, hwaddr addr, case 2: val = s->b[s->dac_index] << 24; s->dac_index = (s->dac_index + 1) & 0xff; /* Index autoincrement */ + /* fall through */ default: s->dac_state = 0; break; @@ -451,6 +452,7 @@ static void tcx_dac_writel(void *opaque, hwaddr addr, uint64_t val, s->b[index] = val >> 24; update_palette_entries(s, index, index + 1); s->dac_index = (s->dac_index + 1) & 0xff; /* Index autoincrement */ + /* fall through */ default: s->dac_state = 0; break; @@ -753,9 +755,8 @@ static void tcx_initfn(Object *obj) SysBusDevice *sbd = SYS_BUS_DEVICE(obj); TCXState *s = TCX(obj); - memory_region_init_ram_nomigrate(&s->rom, obj, "tcx.prom", FCODE_MAX_ROM_SIZE, - &error_fatal); - memory_region_set_readonly(&s->rom, true); + memory_region_init_rom_nomigrate(&s->rom, obj, "tcx.prom", + FCODE_MAX_ROM_SIZE, &error_fatal); sysbus_init_mmio(sbd, &s->rom); /* 2/STIP : Stippler */ @@ -891,7 +892,7 @@ static void tcx_class_init(ObjectClass *klass, void *data) dc->realize = tcx_realizefn; dc->reset = tcx_reset; dc->vmsd = &vmstate_tcx; - dc->props = tcx_properties; + device_class_set_props(dc, tcx_properties); } static const TypeInfo tcx_info = { diff --git a/hw/display/trace-events b/hw/display/trace-events index ba7787b180..e6e22bef88 100644 --- a/hw/display/trace-events +++ b/hw/display/trace-events @@ -142,3 +142,12 @@ sii9022_switch_mode(const char *mode) "mode: %s" # ati.c ati_mm_read(unsigned int size, uint64_t addr, const char *name, uint64_t val) "%u 0x%"PRIx64 " %s -> 0x%"PRIx64 ati_mm_write(unsigned int size, uint64_t addr, const char *name, uint64_t val) "%u 0x%"PRIx64 " %s <- 0x%"PRIx64 + +# artist.c +artist_reg_read(unsigned int size, uint64_t addr, const char *name, uint64_t val) "%u 0x%"PRIx64 "%s -> 0x%"PRIx64 +artist_reg_write(unsigned int size, uint64_t addr, const char *name, uint64_t val) "%u 0x%"PRIx64 "%s <- 0x%"PRIx64 +artist_vram_read(unsigned int size, uint64_t addr, int posx, int posy, uint64_t val) "%u 0x%"PRIx64 " %ux%u-> 0x%"PRIx64 +artist_vram_write(unsigned int size, uint64_t addr, uint64_t val) "%u 0x%"PRIx64 " <- 0x%"PRIx64 +artist_fill_window(unsigned int start_x, unsigned int start_y, unsigned int width, unsigned int height, uint32_t op, uint32_t ctlpln) "start=%ux%u length=%ux%u op=0x%08x ctlpln=0x%08x" +artist_block_move(unsigned int start_x, unsigned int start_y, unsigned int dest_x, unsigned int dest_y, unsigned int width, unsigned int height) "source %ux%u -> dest %ux%u size %ux%u" +artist_draw_line(unsigned int start_x, unsigned int start_y, unsigned int end_x, unsigned int end_y) "%ux%u %ux%u" diff --git a/hw/display/vga-isa-mm.c b/hw/display/vga-isa-mm.c index e9c43e5530..7321b7a06d 100644 --- a/hw/display/vga-isa-mm.c +++ b/hw/display/vga-isa-mm.c @@ -106,6 +106,9 @@ int isa_vga_mm_init(hwaddr vram_base, s->vga.con = graphic_console_init(NULL, 0, s->vga.hw_ops, s); - vga_init_vbe(&s->vga, NULL, address_space); + memory_region_add_subregion(address_space, + VBE_DISPI_LFB_PHYSICAL_ADDRESS, + &s->vga.vram); + return 0; } diff --git a/hw/display/vga-isa.c b/hw/display/vga-isa.c index 873e5e9706..0633ed382c 100644 --- a/hw/display/vga-isa.c +++ b/hw/display/vga-isa.c @@ -76,7 +76,9 @@ static void vga_isa_realizefn(DeviceState *dev, Error **errp) memory_region_set_coalescing(vga_io_memory); s->con = graphic_console_init(DEVICE(dev), 0, s->hw_ops, s); - vga_init_vbe(s, OBJECT(dev), isa_address_space(isadev)); + memory_region_add_subregion(isa_address_space(isadev), + VBE_DISPI_LFB_PHYSICAL_ADDRESS, + &s->vram); /* ROM BIOS */ rom_add_vga(VGABIOS_FILENAME); } @@ -93,7 +95,7 @@ static void vga_isa_class_initfn(ObjectClass *klass, void *data) dc->realize = vga_isa_realizefn; dc->reset = vga_isa_reset; dc->vmsd = &vmstate_vga_common; - dc->props = vga_isa_properties; + device_class_set_props(dc, vga_isa_properties); set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); } diff --git a/hw/display/vga-pci.c b/hw/display/vga-pci.c index a27b88122d..6b9db86e36 100644 --- a/hw/display/vga-pci.c +++ b/hw/display/vga-pci.c @@ -249,8 +249,8 @@ static void pci_std_vga_realize(PCIDevice *dev, Error **errp) /* mmio bar for vga register access */ if (d->flags & (1 << PCI_VGA_FLAG_ENABLE_MMIO)) { - memory_region_init(&d->mmio, NULL, "vga.mmio", - PCI_VGA_MMIO_SIZE); + memory_region_init_io(&d->mmio, OBJECT(dev), &unassigned_io_ops, NULL, + "vga.mmio", PCI_VGA_MMIO_SIZE); if (d->flags & (1 << PCI_VGA_FLAG_ENABLE_QEXT)) { qext = true; @@ -264,11 +264,6 @@ static void pci_std_vga_realize(PCIDevice *dev, Error **errp) pci_register_bar(&d->dev, 2, PCI_BASE_ADDRESS_SPACE_MEMORY, &d->mmio); } - - if (!dev->rom_bar) { - /* compatibility with pc-0.13 and older */ - vga_init_vbe(s, OBJECT(dev), pci_address_space(dev)); - } } static void pci_std_vga_init(Object *obj) @@ -290,8 +285,8 @@ static void pci_secondary_vga_realize(PCIDevice *dev, Error **errp) s->con = graphic_console_init(DEVICE(dev), 0, s->hw_ops, s); /* mmio bar */ - memory_region_init(&d->mmio, OBJECT(dev), "vga.mmio", - PCI_VGA_MMIO_SIZE); + memory_region_init_io(&d->mmio, OBJECT(dev), &unassigned_io_ops, NULL, + "vga.mmio", PCI_VGA_MMIO_SIZE); if (d->flags & (1 << PCI_VGA_FLAG_ENABLE_QEXT)) { qext = true; @@ -388,7 +383,7 @@ static void vga_class_init(ObjectClass *klass, void *data) k->realize = pci_std_vga_realize; k->romfile = "vgabios-stdvga.bin"; k->class_id = PCI_CLASS_DISPLAY_VGA; - dc->props = vga_pci_properties; + device_class_set_props(dc, vga_pci_properties); dc->hotpluggable = false; } @@ -400,7 +395,7 @@ static void secondary_class_init(ObjectClass *klass, void *data) k->realize = pci_secondary_vga_realize; k->exit = pci_secondary_vga_exit; k->class_id = PCI_CLASS_DISPLAY_OTHER; - dc->props = secondary_pci_properties; + device_class_set_props(dc, secondary_pci_properties); dc->reset = pci_secondary_vga_reset; } diff --git a/hw/display/vga.c b/hw/display/vga.c index 82ebe53610..061fd9ab8f 100644 --- a/hw/display/vga.c +++ b/hw/display/vga.c @@ -2301,17 +2301,3 @@ void vga_init(VGACommonState *s, Object *obj, MemoryRegion *address_space, portio_list_add(&s->vbe_port_list, address_space_io, 0x1ce); } } - -void vga_init_vbe(VGACommonState *s, Object *obj, MemoryRegion *system_memory) -{ - /* With pc-0.12 and below we map both the PCI BAR and the fixed VBE region, - * so use an alias to avoid double-mapping the same region. - */ - memory_region_init_alias(&s->vram_vbe, obj, "vram.vbe", - &s->vram, 0, memory_region_size(&s->vram)); - /* XXX: use optimized standard vga accesses */ - memory_region_add_subregion(system_memory, - VBE_DISPI_LFB_PHYSICAL_ADDRESS, - &s->vram_vbe); - s->vbe_mapped = 1; -} diff --git a/hw/display/vga_int.h b/hw/display/vga_int.h index 55c418eab5..847e784ca6 100644 --- a/hw/display/vga_int.h +++ b/hw/display/vga_int.h @@ -60,7 +60,6 @@ typedef struct VGACommonState { MemoryRegion *legacy_address_space; uint8_t *vram_ptr; MemoryRegion vram; - MemoryRegion vram_vbe; uint32_t vram_size; uint32_t vram_size_mb; /* property */ uint32_t vbe_size; @@ -106,7 +105,6 @@ typedef struct VGACommonState { uint32_t vbe_start_addr; uint32_t vbe_line_offset; uint32_t vbe_bank_mask; - int vbe_mapped; /* display refresh support */ QemuConsole *con; uint32_t font_offsets[2]; @@ -178,7 +176,6 @@ void vga_invalidate_scanlines(VGACommonState *s, int y1, int y2); int vga_ioport_invalid(VGACommonState *s, uint32_t addr); -void vga_init_vbe(VGACommonState *s, Object *obj, MemoryRegion *address_space); uint32_t vbe_ioport_read_data(void *opaque, uint32_t addr); void vbe_ioport_write_index(void *opaque, uint32_t addr, uint32_t val); void vbe_ioport_write_data(void *opaque, uint32_t addr, uint32_t val); diff --git a/hw/display/vhost-user-gpu.c b/hw/display/vhost-user-gpu.c index 279877886e..f0c7c6fb9a 100644 --- a/hw/display/vhost-user-gpu.c +++ b/hw/display/vhost-user-gpu.c @@ -588,7 +588,7 @@ vhost_user_gpu_class_init(ObjectClass *klass, void *data) vdc->get_config = vhost_user_gpu_get_config; vdc->set_config = vhost_user_gpu_set_config; - dc->props = vhost_user_gpu_properties; + device_class_set_props(dc, vhost_user_gpu_properties); } static const TypeInfo vhost_user_gpu_info = { diff --git a/hw/display/virtio-gpu-pci.c b/hw/display/virtio-gpu-pci.c index 25e4038874..3d152ff5c8 100644 --- a/hw/display/virtio-gpu-pci.c +++ b/hw/display/virtio-gpu-pci.c @@ -56,7 +56,7 @@ static void virtio_gpu_pci_base_class_init(ObjectClass *klass, void *data) PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass); set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); - dc->props = virtio_gpu_pci_base_properties; + device_class_set_props(dc, virtio_gpu_pci_base_properties); dc->hotpluggable = false; k->realize = virtio_gpu_pci_base_realize; pcidev_k->class_id = PCI_CLASS_DISPLAY_OTHER; diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index 28e868c021..5f0dd7c150 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -1254,7 +1254,7 @@ static void virtio_gpu_class_init(ObjectClass *klass, void *data) vdc->set_config = virtio_gpu_set_config; dc->vmsd = &vmstate_virtio_gpu; - dc->props = virtio_gpu_properties; + device_class_set_props(dc, virtio_gpu_properties); } static const TypeInfo virtio_gpu_info = { diff --git a/hw/display/virtio-vga.c b/hw/display/virtio-vga.c index cc6e66ea1c..2b4c2aa126 100644 --- a/hw/display/virtio-vga.c +++ b/hw/display/virtio-vga.c @@ -184,7 +184,7 @@ static void virtio_vga_base_class_init(ObjectClass *klass, void *data) PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass); set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); - dc->props = virtio_vga_base_properties; + device_class_set_props(dc, virtio_vga_base_properties); dc->vmsd = &vmstate_virtio_vga_base; dc->hotpluggable = false; device_class_set_parent_reset(dc, virtio_vga_base_reset, diff --git a/hw/display/vmware_vga.c b/hw/display/vmware_vga.c index 23dc8910cc..58ea82e3e5 100644 --- a/hw/display/vmware_vga.c +++ b/hw/display/vmware_vga.c @@ -1312,11 +1312,6 @@ static void pci_vmsvga_realize(PCIDevice *dev, Error **errp) &s->chip.vga.vram); pci_register_bar(dev, 2, PCI_BASE_ADDRESS_MEM_PREFETCH, &s->chip.fifo_ram); - - if (!dev->rom_bar) { - /* compatibility with pc-0.13 and older */ - vga_init_vbe(&s->chip.vga, OBJECT(dev), pci_address_space(dev)); - } } static Property vga_vmware_properties[] = { @@ -1341,7 +1336,7 @@ static void vmsvga_class_init(ObjectClass *klass, void *data) k->subsystem_id = SVGA_PCI_DEVICE_ID; dc->reset = vmsvga_reset; dc->vmsd = &vmstate_vmware_vga; - dc->props = vga_vmware_properties; + device_class_set_props(dc, vga_vmware_properties); dc->hotpluggable = false; set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); } diff --git a/hw/dma/Makefile.objs b/hw/dma/Makefile.objs index b672e7a522..f4b1cfe26d 100644 --- a/hw/dma/Makefile.objs +++ b/hw/dma/Makefile.objs @@ -8,9 +8,9 @@ common-obj-$(CONFIG_XILINX_AXI) += xilinx_axidma.o common-obj-$(CONFIG_ZYNQ_DEVCFG) += xlnx-zynq-devcfg.o common-obj-$(CONFIG_ETRAXFS) += etraxfs_dma.o common-obj-$(CONFIG_STP2000) += sparc32_dma.o -obj-$(CONFIG_XLNX_ZYNQMP_ARM) += xlnx_dpdma.o +common-obj-$(CONFIG_XLNX_ZYNQMP_ARM) += xlnx_dpdma.o common-obj-$(CONFIG_XLNX_ZYNQMP_ARM) += xlnx-zdma.o -obj-$(CONFIG_OMAP) += omap_dma.o soc_dma.o -obj-$(CONFIG_PXA2XX) += pxa2xx_dma.o +common-obj-$(CONFIG_OMAP) += omap_dma.o soc_dma.o +common-obj-$(CONFIG_PXA2XX) += pxa2xx_dma.o common-obj-$(CONFIG_RASPI) += bcm2835_dma.o diff --git a/hw/dma/bcm2835_dma.c b/hw/dma/bcm2835_dma.c index 1e458d7fba..4cd9dab745 100644 --- a/hw/dma/bcm2835_dma.c +++ b/hw/dma/bcm2835_dma.c @@ -1,6 +1,8 @@ /* * Raspberry Pi emulation (c) 2012 Gregory Estrade - * This code is licensed under the GNU GPLv2 and later. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. */ #include "qemu/osdep.h" @@ -54,7 +56,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 +72,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 +120,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/dma/etraxfs_dma.c b/hw/dma/etraxfs_dma.c index 47e1c6df12..c4334e87bf 100644 --- a/hw/dma/etraxfs_dma.c +++ b/hw/dma/etraxfs_dma.c @@ -225,9 +225,8 @@ static void channel_load_g(struct fs_dma_ctrl *ctrl, int c) hwaddr addr = channel_reg(ctrl, c, RW_GROUP); /* Load and decode. FIXME: handle endianness. */ - cpu_physical_memory_read (addr, - (void *) &ctrl->channels[c].current_g, - sizeof ctrl->channels[c].current_g); + cpu_physical_memory_read(addr, &ctrl->channels[c].current_g, + sizeof(ctrl->channels[c].current_g)); } static void dump_c(int ch, struct dma_descr_context *c) @@ -257,9 +256,8 @@ static void channel_load_c(struct fs_dma_ctrl *ctrl, int c) hwaddr addr = channel_reg(ctrl, c, RW_GROUP_DOWN); /* Load and decode. FIXME: handle endianness. */ - cpu_physical_memory_read (addr, - (void *) &ctrl->channels[c].current_c, - sizeof ctrl->channels[c].current_c); + cpu_physical_memory_read(addr, &ctrl->channels[c].current_c, + sizeof(ctrl->channels[c].current_c)); D(dump_c(c, &ctrl->channels[c].current_c)); /* I guess this should update the current pos. */ @@ -275,9 +273,8 @@ static void channel_load_d(struct fs_dma_ctrl *ctrl, int c) /* Load and decode. FIXME: handle endianness. */ D(printf("%s ch=%d addr=" TARGET_FMT_plx "\n", __func__, c, addr)); - cpu_physical_memory_read (addr, - (void *) &ctrl->channels[c].current_d, - sizeof ctrl->channels[c].current_d); + cpu_physical_memory_read(addr, &ctrl->channels[c].current_d, + sizeof(ctrl->channels[c].current_d)); D(dump_d(c, &ctrl->channels[c].current_d)); ctrl->channels[c].regs[RW_DATA] = addr; @@ -290,9 +287,8 @@ static void channel_store_c(struct fs_dma_ctrl *ctrl, int c) /* Encode and store. FIXME: handle endianness. */ D(printf("%s ch=%d addr=" TARGET_FMT_plx "\n", __func__, c, addr)); D(dump_d(c, &ctrl->channels[c].current_d)); - cpu_physical_memory_write (addr, - (void *) &ctrl->channels[c].current_c, - sizeof ctrl->channels[c].current_c); + cpu_physical_memory_write(addr, &ctrl->channels[c].current_c, + sizeof(ctrl->channels[c].current_c)); } static void channel_store_d(struct fs_dma_ctrl *ctrl, int c) @@ -301,9 +297,8 @@ static void channel_store_d(struct fs_dma_ctrl *ctrl, int c) /* Encode and store. FIXME: handle endianness. */ D(printf("%s ch=%d addr=" TARGET_FMT_plx "\n", __func__, c, addr)); - cpu_physical_memory_write (addr, - (void *) &ctrl->channels[c].current_d, - sizeof ctrl->channels[c].current_d); + cpu_physical_memory_write(addr, &ctrl->channels[c].current_d, + sizeof(ctrl->channels[c].current_d)); } static inline void channel_stop(struct fs_dma_ctrl *ctrl, int c) diff --git a/hw/dma/i82374.c b/hw/dma/i82374.c index b788b3681a..6977d85ef8 100644 --- a/hw/dma/i82374.c +++ b/hw/dma/i82374.c @@ -149,7 +149,7 @@ static void i82374_class_init(ObjectClass *klass, void *data) dc->realize = i82374_realize; dc->vmsd = &vmstate_i82374; - dc->props = i82374_properties; + device_class_set_props(dc, i82374_properties); } static const TypeInfo i82374_info = { diff --git a/hw/dma/i8257.c b/hw/dma/i8257.c index 792f617eb4..1b3435ab58 100644 --- a/hw/dma/i8257.c +++ b/hw/dma/i8257.c @@ -292,12 +292,6 @@ static uint64_t i8257_read_cont(void *opaque, hwaddr nport, unsigned size) return val; } -static IsaDmaTransferMode i8257_dma_get_transfer_mode(IsaDma *obj, int nchan) -{ - I8257State *d = I8257(obj); - return (d->regs[nchan & 3].mode >> 2) & 3; -} - static bool i8257_dma_has_autoinitialization(IsaDma *obj, int nchan) { I8257State *d = I8257(obj); @@ -400,6 +394,11 @@ static void i8257_dma_register_channel(IsaDma *obj, int nchan, r->opaque = opaque; } +static bool i8257_is_verify_transfer(I8257Regs *r) +{ + return (r->mode & 0x0c) == 0; +} + static int i8257_dma_read_memory(IsaDma *obj, int nchan, void *buf, int pos, int len) { @@ -407,6 +406,10 @@ static int i8257_dma_read_memory(IsaDma *obj, int nchan, void *buf, int pos, I8257Regs *r = &d->regs[nchan & 3]; hwaddr addr = ((r->pageh & 0x7f) << 24) | (r->page << 16) | r->now[ADDR]; + if (i8257_is_verify_transfer(r)) { + return len; + } + if (r->mode & 0x20) { int i; uint8_t *p = buf; @@ -431,6 +434,10 @@ static int i8257_dma_write_memory(IsaDma *obj, int nchan, void *buf, int pos, I8257Regs *r = &s->regs[nchan & 3]; hwaddr addr = ((r->pageh & 0x7f) << 24) | (r->page << 16) | r->now[ADDR]; + if (i8257_is_verify_transfer(r)) { + return len; + } + if (r->mode & 0x20) { int i; uint8_t *p = buf; @@ -553,7 +560,7 @@ static void i8257_realize(DeviceState *dev, Error **errp) I8257State *d = I8257(dev); int i; - memory_region_init_io(&d->channel_io, NULL, &channel_io_ops, d, + memory_region_init_io(&d->channel_io, OBJECT(dev), &channel_io_ops, d, "dma-chan", 8 << d->dshift); memory_region_add_subregion(isa_address_space_io(isa), d->base, &d->channel_io); @@ -595,9 +602,8 @@ static void i8257_class_init(ObjectClass *klass, void *data) dc->realize = i8257_realize; dc->reset = i8257_reset; dc->vmsd = &vmstate_i8257; - dc->props = i8257_properties; + device_class_set_props(dc, i8257_properties); - idc->get_transfer_mode = i8257_dma_get_transfer_mode; idc->has_autoinitialization = i8257_dma_has_autoinitialization; idc->read_memory = i8257_dma_read_memory; idc->write_memory = i8257_dma_write_memory; diff --git a/hw/dma/pl080.c b/hw/dma/pl080.c index 52ba23f4bf..f1a586b1d7 100644 --- a/hw/dma/pl080.c +++ b/hw/dma/pl080.c @@ -421,7 +421,7 @@ static void pl080_class_init(ObjectClass *oc, void *data) dc->vmsd = &vmstate_pl080; dc->realize = pl080_realize; - dc->props = pl080_properties; + device_class_set_props(dc, pl080_properties); dc->reset = pl080_reset; } diff --git a/hw/dma/pl330.c b/hw/dma/pl330.c index f2bb2d9ac1..0bd63a43f5 100644 --- a/hw/dma/pl330.c +++ b/hw/dma/pl330.c @@ -25,19 +25,12 @@ #include "sysemu/dma.h" #include "qemu/log.h" #include "qemu/module.h" +#include "trace.h" #ifndef PL330_ERR_DEBUG #define PL330_ERR_DEBUG 0 #endif -#define DB_PRINT_L(lvl, fmt, args...) do {\ - if (PL330_ERR_DEBUG >= lvl) {\ - fprintf(stderr, "PL330: %s:" fmt, __func__, ## args);\ - } \ -} while (0) - -#define DB_PRINT(fmt, args...) DB_PRINT_L(1, fmt, ## args) - #define PL330_PERIPH_NUM 32 #define PL330_MAX_BURST_LEN 128 #define PL330_INSN_MAXSIZE 6 @@ -319,6 +312,26 @@ typedef struct PL330InsnDesc { void (*exec)(PL330Chan *, uint8_t opcode, uint8_t *args, int len); } PL330InsnDesc; +static void pl330_hexdump(uint8_t *buf, size_t size) +{ + unsigned int b, i, len; + char tmpbuf[80]; + + for (b = 0; b < size; b += 16) { + len = size - b; + if (len > 16) { + len = 16; + } + tmpbuf[0] = '\0'; + for (i = 0; i < len; i++) { + if ((i % 4) == 0) { + strcat(tmpbuf, " "); + } + sprintf(tmpbuf + strlen(tmpbuf), " %02x", buf[b + i]); + } + trace_pl330_hexdump(b, tmpbuf); + } +} /* MFIFO Implementation * @@ -582,7 +595,7 @@ static inline void pl330_queue_remove_tagged(PL330Queue *s, uint8_t tag) static inline void pl330_fault(PL330Chan *ch, uint32_t flags) { - DB_PRINT("ch: %p, flags: %" PRIx32 "\n", ch, flags); + trace_pl330_fault(ch, flags); ch->fault_type |= flags; if (ch->state == pl330_chan_fault) { return; @@ -590,7 +603,7 @@ static inline void pl330_fault(PL330Chan *ch, uint32_t flags) ch->state = pl330_chan_fault; ch->parent->num_faulting++; if (ch->parent->num_faulting == 1) { - DB_PRINT("abort interrupt raised\n"); + trace_pl330_fault_abort(); qemu_irq_raise(ch->parent->irq_abort); } } @@ -648,7 +661,7 @@ static void pl330_dmaend(PL330Chan *ch, uint8_t opcode, return; } } - DB_PRINT("DMA ending!\n"); + trace_pl330_dmaend(); pl330_fifo_tagged_remove(&s->fifo, ch->tag); pl330_queue_remove_tagged(&s->read_queue, ch->tag); pl330_queue_remove_tagged(&s->write_queue, ch->tag); @@ -683,7 +696,7 @@ static void pl330_dmago(PL330Chan *ch, uint8_t opcode, uint8_t *args, int len) uint32_t pc; PL330Chan *s; - DB_PRINT("\n"); + trace_pl330_dmago(); if (!ch->is_manager) { pl330_fault(ch, PL330_FAULT_UNDEF_INSTR); @@ -740,9 +753,7 @@ static void pl330_dmald(PL330Chan *ch, uint8_t opcode, uint8_t *args, int len) ch->stall = pl330_queue_put_insn(&ch->parent->read_queue, ch->src, size, num, inc, 0, ch->tag); if (!ch->stall) { - DB_PRINT("channel:%" PRId8 " address:%08" PRIx32 " size:%" PRIx32 - " num:%" PRId32 " %c\n", - ch->tag, ch->src, size, num, inc ? 'Y' : 'N'); + trace_pl330_dmald(ch->tag, ch->src, size, num, inc ? 'Y' : 'N'); ch->src += inc ? size * num - (ch->src & (size - 1)) : 0; } } @@ -782,7 +793,7 @@ static void pl330_dmakill(PL330Chan *ch, uint8_t opcode, uint8_t *args, int len) ch->fault_type = 0; ch->parent->num_faulting--; if (ch->parent->num_faulting == 0) { - DB_PRINT("abort interrupt lowered\n"); + trace_pl330_dmakill(); qemu_irq_lower(ch->parent->irq_abort); } } @@ -800,6 +811,8 @@ static void pl330_dmalpend(PL330Chan *ch, uint8_t opcode, uint8_t bs = opcode & 3; uint8_t lc = (opcode & 4) >> 2; + trace_pl330_dmalpend(nf, bs, lc, ch->lc[lc], ch->request_flag); + if (bs == 2) { pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); return; @@ -813,12 +826,12 @@ static void pl330_dmalpend(PL330Chan *ch, uint8_t opcode, if (nf) { ch->lc[lc]--; } - DB_PRINT("loop reiteration\n"); + trace_pl330_dmalpiter(); ch->pc -= args[0]; ch->pc -= len + 1; /* "ch->pc -= args[0] + len + 1" is incorrect when args[0] == 256 */ } else { - DB_PRINT("loop fallthrough\n"); + trace_pl330_dmalpfallthrough(); } } @@ -886,10 +899,10 @@ static void pl330_dmasev(PL330Chan *ch, uint8_t opcode, uint8_t *args, int len) } if (ch->parent->inten & (1 << ev_id)) { ch->parent->int_status |= (1 << ev_id); - DB_PRINT("event interrupt raised %" PRId8 "\n", ev_id); + trace_pl330_dmasev_evirq(ev_id); qemu_irq_raise(ch->parent->irq[ev_id]); } - DB_PRINT("event raised %" PRId8 "\n", ev_id); + trace_pl330_dmasev_event(ev_id); ch->parent->ev_status |= (1 << ev_id); } @@ -914,9 +927,7 @@ static void pl330_dmast(PL330Chan *ch, uint8_t opcode, uint8_t *args, int len) ch->stall = pl330_queue_put_insn(&ch->parent->write_queue, ch->dst, size, num, inc, 0, ch->tag); if (!ch->stall) { - DB_PRINT("channel:%" PRId8 " address:%08" PRIx32 " size:%" PRIx32 - " num:%" PRId32 " %c\n", - ch->tag, ch->dst, size, num, inc ? 'Y' : 'N'); + trace_pl330_dmast(ch->tag, ch->dst, size, num, inc ? 'Y' : 'N'); ch->dst += inc ? size * num - (ch->dst & (size - 1)) : 0; } } @@ -992,7 +1003,7 @@ static void pl330_dmawfe(PL330Chan *ch, uint8_t opcode, } } ch->parent->ev_status &= ~(1 << ev_id); - DB_PRINT("event lowered %" PRIx8 "\n", ev_id); + trace_pl330_dmawfe(ev_id); } else { ch->stall = 1; } @@ -1135,7 +1146,7 @@ static int pl330_chan_exec(PL330Chan *ch) ch->stall = 0; insn = pl330_fetch_insn(ch); if (!insn) { - DB_PRINT("pl330 undefined instruction\n"); + trace_pl330_chan_exec_undef(); pl330_fault(ch, PL330_FAULT_UNDEF_INSTR); return 0; } @@ -1175,10 +1186,9 @@ static int pl330_exec_cycle(PL330Chan *channel) int len = q->len - (q->addr & (q->len - 1)); dma_memory_read(&address_space_memory, q->addr, buf, len); - if (PL330_ERR_DEBUG > 1) { - DB_PRINT("PL330 read from memory @%08" PRIx32 " (size = %08x):\n", - q->addr, len); - qemu_hexdump((char *)buf, stderr, "", len); + trace_pl330_exec_cycle(q->addr, len); + if (trace_event_get_state_backends(TRACE_PL330_HEXDUMP)) { + pl330_hexdump(buf, len); } fifo_res = pl330_fifo_push(&s->fifo, buf, len, q->tag); if (fifo_res == PL330_FIFO_OK) { @@ -1207,10 +1217,9 @@ static int pl330_exec_cycle(PL330Chan *channel) } if (fifo_res == PL330_FIFO_OK || q->z) { dma_memory_write(&address_space_memory, q->addr, buf, len); - if (PL330_ERR_DEBUG > 1) { - DB_PRINT("PL330 read from memory @%08" PRIx32 - " (size = %08x):\n", q->addr, len); - qemu_hexdump((char *)buf, stderr, "", len); + trace_pl330_exec_cycle(q->addr, len); + if (trace_event_get_state_backends(TRACE_PL330_HEXDUMP)) { + pl330_hexdump(buf, len); } if (q->inc) { q->addr += len; @@ -1252,8 +1261,8 @@ static int pl330_exec_channel(PL330Chan *channel) static inline void pl330_exec(PL330State *s) { - DB_PRINT("\n"); int i, insr_exec; + trace_pl330_exec(); do { insr_exec = pl330_exec_channel(&s->manager); @@ -1298,7 +1307,7 @@ static void pl330_debug_exec(PL330State *s) args[2] = (s->dbg[1] >> 8) & 0xff; args[3] = (s->dbg[1] >> 16) & 0xff; args[4] = (s->dbg[1] >> 24) & 0xff; - DB_PRINT("chan id: %" PRIx8 "\n", chan_id); + trace_pl330_debug_exec(chan_id); if (s->dbg[0] & 1) { ch = &s->chan[chan_id]; } else { @@ -1320,6 +1329,7 @@ static void pl330_debug_exec(PL330State *s) ch->fault_type |= PL330_FAULT_DBG_INSTR; } if (ch->stall) { + trace_pl330_debug_exec_stall(); qemu_log_mask(LOG_UNIMP, "pl330: stall of debug instruction not " "implemented\n"); } @@ -1334,7 +1344,7 @@ static void pl330_iomem_write(void *opaque, hwaddr offset, PL330State *s = (PL330State *) opaque; int i; - DB_PRINT("addr: %08x data: %08x\n", (unsigned)offset, (unsigned)value); + trace_pl330_iomem_write((unsigned)offset, (unsigned)value); switch (offset) { case PL330_REG_INTEN: @@ -1343,7 +1353,7 @@ static void pl330_iomem_write(void *opaque, hwaddr offset, case PL330_REG_INTCLR: for (i = 0; i < s->num_events; i++) { if (s->int_status & s->inten & value & (1 << i)) { - DB_PRINT("event interrupt lowered %d\n", i); + trace_pl330_iomem_write_clr(i); qemu_irq_lower(s->irq[i]); } } @@ -1361,11 +1371,9 @@ static void pl330_iomem_write(void *opaque, hwaddr offset, } break; case PL330_REG_DBGINST0: - DB_PRINT("s->dbg[0] = %08x\n", (unsigned)value); s->dbg[0] = value; break; case PL330_REG_DBGINST1: - DB_PRINT("s->dbg[1] = %08x\n", (unsigned)value); s->dbg[1] = value; break; default: @@ -1489,7 +1497,7 @@ static uint64_t pl330_iomem_read(void *opaque, hwaddr offset, unsigned size) { uint32_t ret = pl330_iomem_read_imp(opaque, offset); - DB_PRINT("addr: %08" HWADDR_PRIx " data: %08" PRIx32 "\n", offset, ret); + trace_pl330_iomem_read((uint32_t)offset, ret); return ret; } @@ -1656,7 +1664,7 @@ static void pl330_class_init(ObjectClass *klass, void *data) dc->realize = pl330_realize; dc->reset = pl330_reset; - dc->props = pl330_properties; + device_class_set_props(dc, pl330_properties); dc->vmsd = &vmstate_pl330; } diff --git a/hw/dma/pxa2xx_dma.c b/hw/dma/pxa2xx_dma.c index 18e7a0b694..88ed4b6ff1 100644 --- a/hw/dma/pxa2xx_dma.c +++ b/hw/dma/pxa2xx_dma.c @@ -566,7 +566,7 @@ static void pxa2xx_dma_class_init(ObjectClass *klass, void *data) dc->desc = "PXA2xx DMA controller"; dc->vmsd = &vmstate_pxa2xx_dma; - dc->props = pxa2xx_dma_properties; + device_class_set_props(dc, pxa2xx_dma_properties); dc->realize = pxa2xx_dma_realize; } diff --git a/hw/dma/rc4030.c b/hw/dma/rc4030.c index d54e296d3a..7434d274aa 100644 --- a/hw/dma/rc4030.c +++ b/hw/dma/rc4030.c @@ -397,10 +397,11 @@ static void update_jazz_irq(rc4030State *s) pending = s->isr_jazz & s->imr_jazz; - if (pending != 0) + if (pending != 0) { qemu_irq_raise(s->jazz_bus_irq); - else + } else { qemu_irq_lower(s->jazz_bus_irq); + } } static void rc4030_irq_jazz_request(void *opaque, int irq, int level) @@ -512,8 +513,8 @@ static IOMMUTLBEntry rc4030_dma_translate(IOMMUMemoryRegion *iommu, hwaddr addr, if (i < s->dma_tl_limit / sizeof(entry)) { entry_address = (s->dma_tl_base & 0x7fffffff) + i * sizeof(entry); if (address_space_read(ret.target_as, entry_address, - MEMTXATTRS_UNSPECIFIED, (unsigned char *)&entry, - sizeof(entry)) == MEMTX_OK) { + MEMTXATTRS_UNSPECIFIED, &entry, sizeof(entry)) + == MEMTX_OK) { ret.translated_addr = entry.frame & ~(DMA_PAGESIZE - 1); ret.perm = IOMMU_RW; } @@ -588,7 +589,8 @@ static const VMStateDescription vmstate_rc4030 = { } }; -static void rc4030_do_dma(void *opaque, int n, uint8_t *buf, int len, int is_write) +static void rc4030_do_dma(void *opaque, int n, uint8_t *buf, + int len, bool is_write) { rc4030State *s = opaque; hwaddr dma_addr; @@ -628,13 +630,13 @@ struct rc4030DMAState { void rc4030_dma_read(void *dma, uint8_t *buf, int len) { rc4030_dma s = dma; - rc4030_do_dma(s->opaque, s->n, buf, len, 0); + rc4030_do_dma(s->opaque, s->n, buf, len, false); } void rc4030_dma_write(void *dma, uint8_t *buf, int len) { rc4030_dma s = dma; - rc4030_do_dma(s->opaque, s->n, buf, len, 1); + rc4030_do_dma(s->opaque, s->n, buf, len, true); } static rc4030_dma *rc4030_allocate_dmas(void *opaque, int n) @@ -643,8 +645,8 @@ static rc4030_dma *rc4030_allocate_dmas(void *opaque, int n) struct rc4030DMAState *p; int i; - s = (rc4030_dma *)g_malloc0(sizeof(rc4030_dma) * n); - p = (struct rc4030DMAState *)g_malloc0(sizeof(struct rc4030DMAState) * n); + s = (rc4030_dma *)g_new0(rc4030_dma, n); + p = (struct rc4030DMAState *)g_new0(struct rc4030DMAState, n); for (i = 0; i < n; i++) { p->opaque = opaque; p->n = i; @@ -677,9 +679,9 @@ static void rc4030_realize(DeviceState *dev, Error **errp) s->periodic_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, rc4030_periodic_timer, s); - memory_region_init_io(&s->iomem_chipset, NULL, &rc4030_ops, s, + memory_region_init_io(&s->iomem_chipset, o, &rc4030_ops, s, "rc4030.chipset", 0x300); - memory_region_init_io(&s->iomem_jazzio, NULL, &jazzio_ops, s, + memory_region_init_io(&s->iomem_jazzio, o, &jazzio_ops, s, "rc4030.jazzio", 0x00001000); memory_region_init_iommu(&s->dma_mr, sizeof(s->dma_mr), diff --git a/hw/dma/soc_dma.c b/hw/dma/soc_dma.c index c3e41581b6..3a430057f5 100644 --- a/hw/dma/soc_dma.c +++ b/hw/dma/soc_dma.c @@ -80,7 +80,7 @@ struct dma_s { } *memmap; int memmap_size; - struct soc_dma_ch_s ch[0]; + struct soc_dma_ch_s ch[]; }; static void soc_dma_ch_schedule(struct soc_dma_ch_s *ch, int delay_bytes) diff --git a/hw/dma/sparc32_dma.c b/hw/dma/sparc32_dma.c index 0e5bbcdc7f..3e4da0c47f 100644 --- a/hw/dma/sparc32_dma.c +++ b/hw/dma/sparc32_dma.c @@ -346,7 +346,7 @@ static void sparc32_ledma_device_realize(DeviceState *dev, Error **errp) d = qdev_create(NULL, TYPE_LANCE); object_property_add_child(OBJECT(dev), "lance", OBJECT(d), errp); qdev_set_nic_properties(d, nd); - qdev_prop_set_ptr(d, "dma", dev); + object_property_set_link(OBJECT(d), OBJECT(dev), "dma", errp); qdev_init_nofail(d); } diff --git a/hw/dma/trace-events b/hw/dma/trace-events index e4498428c5..44893995f6 100644 --- a/hw/dma/trace-events +++ b/hw/dma/trace-events @@ -20,3 +20,27 @@ sparc32_dma_enable_lower(void) "Lower DMA enable" # i8257.c i8257_unregistered_dma(int nchan, int dma_pos, int dma_len) "unregistered DMA channel used nchan=%d dma_pos=%d dma_len=%d" + +# pl330.c +pl330_fault(void *ptr, uint32_t flags) "ch: %p, flags: 0x%"PRIx32 +pl330_fault_abort(void) "abort interrupt raised" +pl330_dmaend(void) "DMA ending" +pl330_dmago(void) "DMA run" +pl330_dmald(uint8_t chan, uint32_t addr, uint32_t size, uint32_t num, char ch) "channel:%"PRId8" address:0x%08"PRIx32" size:0x%"PRIx32" num:%"PRId32"%c" +pl330_dmakill(void) "abort interrupt lowered" +pl330_dmalpend(uint8_t nf, uint8_t bs, uint8_t lc, uint8_t ch, uint8_t flag) "nf=0x%02x bs=0x%02x lc=0x%02x ch=0x%02x flag=0x%02x" +pl330_dmalpiter(void) "loop reiteration" +pl330_dmalpfallthrough(void) "loop fallthrough" +pl330_dmasev_evirq(uint8_t ev_id) "event interrupt raised %"PRId8 +pl330_dmasev_event(uint8_t ev_id) "event raised %"PRId8 +pl330_dmast(uint8_t chan, uint32_t addr, uint32_t sz, uint32_t num, char ch) "channel:%"PRId8" address:0x%08"PRIx32" size:0x%"PRIx32" num:%"PRId32" %c" +pl330_dmawfe(uint8_t ev_id) "event lowered 0x%"PRIx8 +pl330_chan_exec_undef(void) "undefined instruction" +pl330_exec_cycle(uint32_t addr, uint32_t size) "PL330 read from memory @0x%08"PRIx32" (size = 0x%08"PRIx32")" +pl330_hexdump(uint32_t offset, char *str) " 0x%04"PRIx32":%s" +pl330_exec(void) "pl330_exec" +pl330_debug_exec(uint8_t ch) "chan id: 0x%"PRIx8 +pl330_debug_exec_stall(void) "stall of debug instruction not implemented" +pl330_iomem_write(uint32_t offset, uint32_t value) "addr: 0x%08"PRIx32" data: 0x%08"PRIx32 +pl330_iomem_write_clr(int i) "event interrupt lowered %d" +pl330_iomem_read(uint32_t addr, uint32_t data) "addr: 0x%08"PRIx32" data: 0x%08"PRIx32 diff --git a/hw/dma/xilinx_axidma.c b/hw/dma/xilinx_axidma.c index fb3a978e28..018f36991b 100644 --- a/hw/dma/xilinx_axidma.c +++ b/hw/dma/xilinx_axidma.c @@ -599,7 +599,7 @@ static void axidma_class_init(ObjectClass *klass, void *data) dc->realize = xilinx_axidma_realize, dc->reset = xilinx_axidma_reset; - dc->props = axidma_properties; + device_class_set_props(dc, axidma_properties); } static StreamSlaveClass xilinx_axidma_data_stream_class = { diff --git a/hw/dma/xlnx-zdma.c b/hw/dma/xlnx-zdma.c index 75b660988f..1c45367f3c 100644 --- a/hw/dma/xlnx-zdma.c +++ b/hw/dma/xlnx-zdma.c @@ -311,8 +311,7 @@ static bool zdma_load_descriptor(XlnxZDMA *s, uint64_t addr, void *buf) return false; } - address_space_rw(s->dma_as, addr, s->attr, - buf, sizeof(XlnxZDMADescr), false); + address_space_read(s->dma_as, addr, s->attr, buf, sizeof(XlnxZDMADescr)); return true; } @@ -334,10 +333,28 @@ static void zdma_load_src_descriptor(XlnxZDMA *s) } } +static void zdma_update_descr_addr(XlnxZDMA *s, bool type, + unsigned int basereg) +{ + uint64_t addr, next; + + if (type == DTYPE_LINEAR) { + addr = zdma_get_regaddr64(s, basereg); + next = addr + sizeof(s->dsc_dst); + } else { + addr = zdma_get_regaddr64(s, basereg); + addr += sizeof(s->dsc_dst); + address_space_read(s->dma_as, addr, s->attr, (void *) &next, 8); + } + + zdma_put_regaddr64(s, basereg, next); +} + static void zdma_load_dst_descriptor(XlnxZDMA *s) { uint64_t dst_addr; unsigned int ptype = ARRAY_FIELD_EX32(s->regs, ZDMA_CH_CTRL0, POINT_TYPE); + bool dst_type; if (ptype == PT_REG) { memcpy(&s->dsc_dst, &s->regs[R_ZDMA_CH_DST_DSCR_WORD0], @@ -350,30 +367,16 @@ static void zdma_load_dst_descriptor(XlnxZDMA *s) if (!zdma_load_descriptor(s, dst_addr, &s->dsc_dst)) { ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, AXI_RD_DST_DSCR, true); } -} -static uint64_t zdma_update_descr_addr(XlnxZDMA *s, bool type, - unsigned int basereg) -{ - uint64_t addr, next; - - if (type == DTYPE_LINEAR) { - next = zdma_get_regaddr64(s, basereg); - next += sizeof(s->dsc_dst); - zdma_put_regaddr64(s, basereg, next); - } else { - addr = zdma_get_regaddr64(s, basereg); - addr += sizeof(s->dsc_dst); - address_space_rw(s->dma_as, addr, s->attr, (void *) &next, 8, false); - zdma_put_regaddr64(s, basereg, next); - } - return next; + /* Advance the descriptor pointer. */ + dst_type = FIELD_EX32(s->dsc_dst.words[3], ZDMA_CH_DST_DSCR_WORD3, TYPE); + zdma_update_descr_addr(s, dst_type, R_ZDMA_CH_DST_CUR_DSCR_LSB); } static void zdma_write_dst(XlnxZDMA *s, uint8_t *buf, uint32_t len) { uint32_t dst_size, dlen; - bool dst_intr, dst_type; + bool dst_intr; unsigned int ptype = ARRAY_FIELD_EX32(s->regs, ZDMA_CH_CTRL0, POINT_TYPE); unsigned int rw_mode = ARRAY_FIELD_EX32(s->regs, ZDMA_CH_CTRL0, MODE); unsigned int burst_type = ARRAY_FIELD_EX32(s->regs, ZDMA_CH_DATA_ATTR, @@ -387,17 +390,10 @@ static void zdma_write_dst(XlnxZDMA *s, uint8_t *buf, uint32_t len) while (len) { dst_size = FIELD_EX32(s->dsc_dst.words[2], ZDMA_CH_DST_DSCR_WORD2, SIZE); - dst_type = FIELD_EX32(s->dsc_dst.words[3], ZDMA_CH_DST_DSCR_WORD3, - TYPE); if (dst_size == 0 && ptype == PT_MEM) { - uint64_t next; - next = zdma_update_descr_addr(s, dst_type, - R_ZDMA_CH_DST_CUR_DSCR_LSB); - zdma_load_descriptor(s, next, &s->dsc_dst); + zdma_load_dst_descriptor(s); dst_size = FIELD_EX32(s->dsc_dst.words[2], ZDMA_CH_DST_DSCR_WORD2, SIZE); - dst_type = FIELD_EX32(s->dsc_dst.words[3], ZDMA_CH_DST_DSCR_WORD3, - TYPE); } /* Match what hardware does by ignoring the dst_size and only using @@ -416,8 +412,7 @@ static void zdma_write_dst(XlnxZDMA *s, uint8_t *buf, uint32_t len) } } - address_space_rw(s->dma_as, s->dsc_dst.addr, s->attr, buf, dlen, - true); + address_space_write(s->dma_as, s->dsc_dst.addr, s->attr, buf, dlen); if (burst_type == AXI_BURST_INCR) { s->dsc_dst.addr += dlen; } @@ -493,8 +488,7 @@ static void zdma_process_descr(XlnxZDMA *s) len = s->cfg.bus_width / 8; } } else { - address_space_rw(s->dma_as, src_addr, s->attr, s->buf, len, - false); + address_space_read(s->dma_as, src_addr, s->attr, s->buf, len); if (burst_type == AXI_BURST_INCR) { src_addr += len; } @@ -514,16 +508,15 @@ static void zdma_process_descr(XlnxZDMA *s) zdma_src_done(s); } - /* Load next descriptor. */ if (ptype == PT_REG || src_cmd == CMD_STOP) { ARRAY_FIELD_DP32(s->regs, ZDMA_CH_CTRL2, EN, 0); zdma_set_state(s, DISABLED); - return; } if (src_cmd == CMD_HALT) { zdma_set_state(s, PAUSED); ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, DMA_PAUSE, 1); + ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, DMA_DONE, false); zdma_ch_imr_update_irq(s); return; } @@ -684,6 +677,12 @@ static RegisterAccessInfo zdma_regs_info[] = { },{ .name = "ZDMA_CH_DBG0", .addr = A_ZDMA_CH_DBG0, .rsvd = 0xfffffe00, .ro = 0x1ff, + + /* + * There's SW out there that will check the debug regs for free space. + * Claim that we always have 0x100 free. + */ + .reset = 0x100 },{ .name = "ZDMA_CH_DBG1", .addr = A_ZDMA_CH_DBG1, .rsvd = 0xfffffe00, .ro = 0x1ff, @@ -820,7 +819,7 @@ static void zdma_class_init(ObjectClass *klass, void *data) dc->reset = zdma_reset; dc->realize = zdma_realize; - dc->props = zdma_props; + device_class_set_props(dc, zdma_props); dc->vmsd = &vmstate_zdma; } diff --git a/hw/gpio/Makefile.objs b/hw/gpio/Makefile.objs index d305b3b24b..3cfc261f9b 100644 --- a/hw/gpio/Makefile.objs +++ b/hw/gpio/Makefile.objs @@ -5,8 +5,8 @@ common-obj-$(CONFIG_ZAURUS) += zaurus.o common-obj-$(CONFIG_E500) += mpc8xxx.o common-obj-$(CONFIG_GPIO_KEY) += gpio_key.o -obj-$(CONFIG_OMAP) += omap_gpio.o -obj-$(CONFIG_IMX) += imx_gpio.o -obj-$(CONFIG_RASPI) += bcm2835_gpio.o -obj-$(CONFIG_NRF51_SOC) += nrf51_gpio.o -obj-$(CONFIG_ASPEED_SOC) += aspeed_gpio.o +common-obj-$(CONFIG_OMAP) += omap_gpio.o +common-obj-$(CONFIG_IMX) += imx_gpio.o +common-obj-$(CONFIG_RASPI) += bcm2835_gpio.o +common-obj-$(CONFIG_NRF51_SOC) += nrf51_gpio.o +common-obj-$(CONFIG_ASPEED_SOC) += aspeed_gpio.o diff --git a/hw/gpio/aspeed_gpio.c b/hw/gpio/aspeed_gpio.c index 7acc5fa8e2..e52fcfd9a0 100644 --- a/hw/gpio/aspeed_gpio.c +++ b/hw/gpio/aspeed_gpio.c @@ -6,8 +6,6 @@ * SPDX-License-Identifier: GPL-2.0-or-later */ -#include - #include "qemu/osdep.h" #include "qemu/host-utils.h" #include "qemu/log.h" @@ -876,6 +874,7 @@ static void aspeed_gpio_init(Object *obj) pin_idx % GPIOS_PER_GROUP); object_property_add(obj, name, "bool", aspeed_gpio_get_pin, aspeed_gpio_set_pin, NULL, NULL, NULL); + g_free(name); } } diff --git a/hw/gpio/imx_gpio.c b/hw/gpio/imx_gpio.c index ac8304ec56..7a591804a9 100644 --- a/hw/gpio/imx_gpio.c +++ b/hw/gpio/imx_gpio.c @@ -335,7 +335,7 @@ static void imx_gpio_class_init(ObjectClass *klass, void *data) dc->realize = imx_gpio_realize; dc->reset = imx_gpio_reset; - dc->props = imx_gpio_properties; + device_class_set_props(dc, imx_gpio_properties); dc->vmsd = &vmstate_imx_gpio; dc->desc = "i.MX GPIO controller"; } diff --git a/hw/gpio/omap_gpio.c b/hw/gpio/omap_gpio.c index 41e1aa798c..f662c4cb95 100644 --- a/hw/gpio/omap_gpio.c +++ b/hw/gpio/omap_gpio.c @@ -40,10 +40,6 @@ struct omap_gpio_s { uint16_t pins; }; -#define TYPE_OMAP1_GPIO "omap-gpio" -#define OMAP1_GPIO(obj) \ - OBJECT_CHECK(struct omap_gpif_s, (obj), TYPE_OMAP1_GPIO) - struct omap_gpif_s { SysBusDevice parent_obj; @@ -212,10 +208,6 @@ struct omap2_gpio_s { uint8_t delay; }; -#define TYPE_OMAP2_GPIO "omap2-gpio" -#define OMAP2_GPIO(obj) \ - OBJECT_CHECK(struct omap2_gpif_s, (obj), TYPE_OMAP2_GPIO) - struct omap2_gpif_s { SysBusDevice parent_obj; @@ -747,21 +739,13 @@ static void omap2_gpio_realize(DeviceState *dev, Error **errp) } } -/* Using qdev pointer properties for the clocks is not ideal. - * qdev should support a generic means of defining a 'port' with - * an arbitrary interface for connecting two devices. Then we - * could reframe the omap clock API in terms of clock ports, - * and get some type safety. For now the best qdev provides is - * passing an arbitrary pointer. - * (It's not possible to pass in the string which is the clock - * name, because this device does not have the necessary information - * (ie the struct omap_mpu_state_s*) to do the clockname to pointer - * translation.) - */ +void omap_gpio_set_clk(omap_gpif *gpio, omap_clk clk) +{ + gpio->clk = clk; +} static Property omap_gpio_properties[] = { DEFINE_PROP_INT32("mpu_model", struct omap_gpif_s, mpu_model, 0), - DEFINE_PROP_PTR("clk", struct omap_gpif_s, clk), DEFINE_PROP_END_OF_LIST(), }; @@ -771,7 +755,7 @@ static void omap_gpio_class_init(ObjectClass *klass, void *data) dc->realize = omap_gpio_realize; dc->reset = omap_gpif_reset; - dc->props = omap_gpio_properties; + device_class_set_props(dc, omap_gpio_properties); /* Reason: pointer property "clk" */ dc->user_creatable = false; } @@ -784,15 +768,19 @@ static const TypeInfo omap_gpio_info = { .class_init = omap_gpio_class_init, }; +void omap2_gpio_set_iclk(omap2_gpif *gpio, omap_clk clk) +{ + gpio->iclk = clk; +} + +void omap2_gpio_set_fclk(omap2_gpif *gpio, uint8_t i, omap_clk clk) +{ + assert(i <= 5); + gpio->fclk[i] = clk; +} + static Property omap2_gpio_properties[] = { DEFINE_PROP_INT32("mpu_model", struct omap2_gpif_s, mpu_model, 0), - DEFINE_PROP_PTR("iclk", struct omap2_gpif_s, iclk), - DEFINE_PROP_PTR("fclk0", struct omap2_gpif_s, fclk[0]), - DEFINE_PROP_PTR("fclk1", struct omap2_gpif_s, fclk[1]), - DEFINE_PROP_PTR("fclk2", struct omap2_gpif_s, fclk[2]), - DEFINE_PROP_PTR("fclk3", struct omap2_gpif_s, fclk[3]), - DEFINE_PROP_PTR("fclk4", struct omap2_gpif_s, fclk[4]), - DEFINE_PROP_PTR("fclk5", struct omap2_gpif_s, fclk[5]), DEFINE_PROP_END_OF_LIST(), }; @@ -802,7 +790,7 @@ static void omap2_gpio_class_init(ObjectClass *klass, void *data) dc->realize = omap2_gpio_realize; dc->reset = omap2_gpif_reset; - dc->props = omap2_gpio_properties; + device_class_set_props(dc, omap2_gpio_properties); /* Reason: pointer properties "iclk", "fclk0", ..., "fclk5" */ dc->user_creatable = false; } diff --git a/hw/hppa/Kconfig b/hw/hppa/Kconfig index 6e5d74a825..22948db025 100644 --- a/hw/hppa/Kconfig +++ b/hw/hppa/Kconfig @@ -10,3 +10,7 @@ config DINO select IDE_CMD646 select MC146818RTC select LSI_SCSI_PCI + select LASI_82596 + select LASIPS2 + select PARALLEL + select ARTIST diff --git a/hw/hppa/Makefile.objs b/hw/hppa/Makefile.objs index 67838f50a3..eac3467d8a 100644 --- a/hw/hppa/Makefile.objs +++ b/hw/hppa/Makefile.objs @@ -1 +1 @@ -obj-$(CONFIG_DINO) += pci.o machine.o dino.o +obj-$(CONFIG_DINO) += pci.o machine.o dino.o lasi.o diff --git a/hw/hppa/dino.c b/hw/hppa/dino.c index ab6969b45f..2b1b38c58a 100644 --- a/hw/hppa/dino.c +++ b/hw/hppa/dino.c @@ -1,7 +1,7 @@ /* - * HP-PARISC Dino PCI chipset emulation. + * HP-PARISC Dino PCI chipset emulation, as in B160L and similiar machines * - * (C) 2017 by Helge Deller + * (C) 2017-2019 by Helge Deller * * This work is licensed under the GNU GPL license version 2 or later. * @@ -21,6 +21,7 @@ #include "migration/vmstate.h" #include "hppa_sys.h" #include "exec/address-spaces.h" +#include "trace.h" #define TYPE_DINO_PCI_HOST_BRIDGE "dino-pcihost" @@ -82,11 +83,29 @@ #define DINO_PCI_HOST_BRIDGE(obj) \ OBJECT_CHECK(DinoState, (obj), TYPE_DINO_PCI_HOST_BRIDGE) +#define DINO800_REGS (1 + (DINO_TLTIM - DINO_GMASK) / 4) +static const uint32_t reg800_keep_bits[DINO800_REGS] = { + MAKE_64BIT_MASK(0, 1), /* GMASK */ + MAKE_64BIT_MASK(0, 7), /* PAMR */ + MAKE_64BIT_MASK(0, 7), /* PAPR */ + MAKE_64BIT_MASK(0, 8), /* DAMODE */ + MAKE_64BIT_MASK(0, 7), /* PCICMD */ + MAKE_64BIT_MASK(0, 9), /* PCISTS */ + MAKE_64BIT_MASK(0, 32), /* Undefined */ + MAKE_64BIT_MASK(0, 8), /* MLTIM */ + MAKE_64BIT_MASK(0, 30), /* BRDG_FEAT */ + MAKE_64BIT_MASK(0, 24), /* PCIROR */ + MAKE_64BIT_MASK(0, 22), /* PCIWOR */ + MAKE_64BIT_MASK(0, 32), /* Undocumented */ + MAKE_64BIT_MASK(0, 9), /* TLTIM */ +}; + typedef struct DinoState { PCIHostState parent_obj; /* PCI_CONFIG_ADDR is parent_obj.config_reg, via pci_host_conf_be_ops, so that we can map PCI_CONFIG_DATA to pci_host_data_be_ops. */ + uint32_t config_reg_dino; /* keep original copy, including 2 lowest bits */ uint32_t iar0; uint32_t iar1; @@ -94,8 +113,12 @@ typedef struct DinoState { uint32_t ipr; uint32_t icr; uint32_t ilr; + uint32_t io_fbb_en; uint32_t io_addr_en; uint32_t io_control; + uint32_t toc_addr; + + uint32_t reg800[DINO800_REGS]; MemoryRegion this_mem; MemoryRegion pci_mem; @@ -106,8 +129,6 @@ typedef struct DinoState { MemoryRegion bm_ram_alias; MemoryRegion bm_pci_alias; MemoryRegion bm_cpu_alias; - - MemoryRegion cpu0_eir_mem; } DinoState; /* @@ -122,6 +143,8 @@ static void gsc_to_pci_forwarding(DinoState *s) tmp = extract32(s->io_control, 7, 2); enabled = (tmp == 0x01); io_addr_en = s->io_addr_en; + /* Mask out first (=firmware) and last (=Dino) areas. */ + io_addr_en &= ~(BIT(31) | BIT(0)); memory_region_transaction_begin(); for (i = 1; i < 31; i++) { @@ -142,6 +165,8 @@ static bool dino_chip_mem_valid(void *opaque, hwaddr addr, unsigned size, bool is_write, MemTxAttrs attrs) { + bool ret = false; + switch (addr) { case DINO_IAR0: case DINO_IAR1: @@ -152,16 +177,24 @@ static bool dino_chip_mem_valid(void *opaque, hwaddr addr, case DINO_ICR: case DINO_ILR: case DINO_IO_CONTROL: + case DINO_IO_FBB_EN: case DINO_IO_ADDR_EN: case DINO_PCI_IO_DATA: - return true; + case DINO_TOC_ADDR: + case DINO_GMASK ... DINO_PCISTS: + case DINO_MLTIM ... DINO_PCIWOR: + case DINO_TLTIM: + ret = true; + break; case DINO_PCI_IO_DATA + 2: - return size <= 2; + ret = (size <= 2); + break; case DINO_PCI_IO_DATA + 1: case DINO_PCI_IO_DATA + 3: - return size == 1; + ret = (size == 1); } - return false; + trace_dino_chip_mem_valid(addr, ret); + return ret; } static MemTxResult dino_chip_read_with_attrs(void *opaque, hwaddr addr, @@ -194,6 +227,9 @@ static MemTxResult dino_chip_read_with_attrs(void *opaque, hwaddr addr, } break; + case DINO_IO_FBB_EN: + val = s->io_fbb_en; + break; case DINO_IO_ADDR_EN: val = s->io_addr_en; break; @@ -227,12 +263,28 @@ static MemTxResult dino_chip_read_with_attrs(void *opaque, hwaddr addr, case DINO_IRR1: val = s->ilr & s->imr & s->icr; break; + case DINO_TOC_ADDR: + val = s->toc_addr; + break; + case DINO_GMASK ... DINO_TLTIM: + val = s->reg800[(addr - DINO_GMASK) / 4]; + if (addr == DINO_PAMR) { + val &= ~0x01; /* LSB is hardwired to 0 */ + } + if (addr == DINO_MLTIM) { + val &= ~0x07; /* 3 LSB are hardwired to 0 */ + } + if (addr == DINO_BRDG_FEAT) { + val &= ~(0x10710E0ul | 8); /* bits 5-7, 24 & 15 reserved */ + } + break; default: /* Controlled by dino_chip_mem_valid above. */ g_assert_not_reached(); } + trace_dino_chip_read(addr, val); *data = val; return ret; } @@ -245,6 +297,9 @@ static MemTxResult dino_chip_write_with_attrs(void *opaque, hwaddr addr, AddressSpace *io; MemTxResult ret; uint16_t ioaddr; + int i; + + trace_dino_chip_write(addr, val); switch (addr) { case DINO_IO_DATA ... DINO_PCI_IO_DATA + 3: @@ -266,9 +321,11 @@ static MemTxResult dino_chip_write_with_attrs(void *opaque, hwaddr addr, } return ret; + case DINO_IO_FBB_EN: + s->io_fbb_en = val & 0x03; + break; case DINO_IO_ADDR_EN: - /* Never allow first (=firmware) and last (=Dino) areas. */ - s->io_addr_en = val & 0x7ffffffe; + s->io_addr_en = val; gsc_to_pci_forwarding(s); break; case DINO_IO_CONTROL: @@ -292,6 +349,10 @@ static MemTxResult dino_chip_write_with_attrs(void *opaque, hwaddr addr, /* Any write to IPR clears the register. */ s->ipr = 0; break; + case DINO_TOC_ADDR: + /* IO_COMMAND of CPU with client_id bits */ + s->toc_addr = 0xFFFA0030 | (val & 0x1e000); + break; case DINO_ILR: case DINO_IRR0: @@ -299,6 +360,12 @@ static MemTxResult dino_chip_write_with_attrs(void *opaque, hwaddr addr, /* These registers are read-only. */ break; + case DINO_GMASK ... DINO_TLTIM: + i = (addr - DINO_GMASK) / 4; + val &= reg800_keep_bits[i]; + s->reg800[i] = val; + break; + default: /* Controlled by dino_chip_mem_valid above. */ g_assert_not_reached(); @@ -323,7 +390,7 @@ static const MemoryRegionOps dino_chip_ops = { static const VMStateDescription vmstate_dino = { .name = "Dino", - .version_id = 1, + .version_id = 2, .minimum_version_id = 1, .fields = (VMStateField[]) { VMSTATE_UINT32(iar0, DinoState), @@ -332,13 +399,14 @@ static const VMStateDescription vmstate_dino = { VMSTATE_UINT32(ipr, DinoState), VMSTATE_UINT32(icr, DinoState), VMSTATE_UINT32(ilr, DinoState), + VMSTATE_UINT32(io_fbb_en, DinoState), VMSTATE_UINT32(io_addr_en, DinoState), VMSTATE_UINT32(io_control, DinoState), + VMSTATE_UINT32(toc_addr, DinoState), VMSTATE_END_OF_LIST() } }; - /* Unlike pci_config_data_le_ops, no check of high bit set in config_reg. */ static uint64_t dino_config_data_read(void *opaque, hwaddr addr, unsigned len) @@ -362,14 +430,16 @@ static const MemoryRegionOps dino_config_data_ops = { static uint64_t dino_config_addr_read(void *opaque, hwaddr addr, unsigned len) { - PCIHostState *s = opaque; - return s->config_reg; + DinoState *s = opaque; + return s->config_reg_dino; } static void dino_config_addr_write(void *opaque, hwaddr addr, uint64_t val, unsigned len) { PCIHostState *s = opaque; + DinoState *ds = opaque; + ds->config_reg_dino = val; /* keep a copy of original value */ s->config_reg = val & ~3U; } @@ -453,6 +523,8 @@ PCIBus *dino_init(MemoryRegion *addr_space, dev = qdev_create(NULL, TYPE_DINO_PCI_HOST_BRIDGE); s = DINO_PCI_HOST_BRIDGE(dev); + s->iar0 = s->iar1 = CPU_HPA + 3; + s->toc_addr = 0xFFFA0030; /* IO_COMMAND of CPU */ /* Dino PCI access from main memory. */ memory_region_init_io(&s->this_mem, OBJECT(s), &dino_chip_ops, diff --git a/hw/hppa/hppa_hardware.h b/hw/hppa/hppa_hardware.h index 507f91e05d..4a2fe2df60 100644 --- a/hw/hppa/hppa_hardware.h +++ b/hw/hppa/hppa_hardware.h @@ -22,6 +22,7 @@ #define LASI_PS2KBD_HPA 0xffd08000 #define LASI_PS2MOU_HPA 0xffd08100 #define LASI_GFX_HPA 0xf8000000 +#define ARTIST_FB_ADDR 0xf9000000 #define CPU_HPA 0xfffb0000 #define MEMORY_HPA 0xfffbf000 diff --git a/hw/hppa/hppa_sys.h b/hw/hppa/hppa_sys.h index 43d25d21fc..0b18271cc9 100644 --- a/hw/hppa/hppa_sys.h +++ b/hw/hppa/hppa_sys.h @@ -5,12 +5,14 @@ #include "hw/pci/pci.h" #include "hw/pci/pci_host.h" -#include "hw/ide.h" -#include "hw/i386/pc.h" +#include "hw/boards.h" +#include "hw/intc/i8259.h" #include "hppa_hardware.h" PCIBus *dino_init(MemoryRegion *, qemu_irq *, qemu_irq *); +DeviceState *lasi_init(MemoryRegion *); +#define enable_lasi_lan() 0 #define TYPE_DINO_PCI_HOST_BRIDGE "dino-pcihost" diff --git a/hw/hppa/lasi.c b/hw/hppa/lasi.c new file mode 100644 index 0000000000..d8d03f95c0 --- /dev/null +++ b/hw/hppa/lasi.c @@ -0,0 +1,368 @@ +/* + * HP-PARISC Lasi chipset emulation. + * + * (C) 2019 by Helge Deller + * + * This work is licensed under the GNU GPL license version 2 or later. + * + * Documentation available at: + * https://parisc.wiki.kernel.org/images-parisc/7/79/Lasi_ers.pdf + */ + +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "qapi/error.h" +#include "cpu.h" +#include "trace.h" +#include "hw/hw.h" +#include "hw/irq.h" +#include "sysemu/sysemu.h" +#include "sysemu/runstate.h" +#include "hppa_sys.h" +#include "hw/net/lasi_82596.h" +#include "hw/char/parallel.h" +#include "hw/char/serial.h" +#include "hw/input/lasips2.h" +#include "exec/address-spaces.h" +#include "migration/vmstate.h" + +#define TYPE_LASI_CHIP "lasi-chip" + +#define LASI_IRR 0x00 /* RO */ +#define LASI_IMR 0x04 +#define LASI_IPR 0x08 +#define LASI_ICR 0x0c +#define LASI_IAR 0x10 + +#define LASI_PCR 0x0C000 /* LASI Power Control register */ +#define LASI_ERRLOG 0x0C004 /* LASI Error Logging register */ +#define LASI_VER 0x0C008 /* LASI Version Control register */ +#define LASI_IORESET 0x0C00C /* LASI I/O Reset register */ +#define LASI_AMR 0x0C010 /* LASI Arbitration Mask register */ +#define LASI_IO_CONF 0x7FFFE /* LASI primary configuration register */ +#define LASI_IO_CONF2 0x7FFFF /* LASI secondary configuration register */ + +#define LASI_BIT(x) (1ul << (x)) +#define LASI_IRQ_BITS (LASI_BIT(5) | LASI_BIT(7) | LASI_BIT(8) | LASI_BIT(9) \ + | LASI_BIT(13) | LASI_BIT(14) | LASI_BIT(16) | LASI_BIT(17) \ + | LASI_BIT(18) | LASI_BIT(19) | LASI_BIT(20) | LASI_BIT(21) \ + | LASI_BIT(26)) + +#define ICR_BUS_ERROR_BIT LASI_BIT(8) /* bit 8 in ICR */ +#define ICR_TOC_BIT LASI_BIT(1) /* bit 1 in ICR */ + +#define LASI_CHIP(obj) \ + OBJECT_CHECK(LasiState, (obj), TYPE_LASI_CHIP) + +#define LASI_RTC_HPA (LASI_HPA + 0x9000) + +typedef struct LasiState { + PCIHostState parent_obj; + + uint32_t irr; + uint32_t imr; + uint32_t ipr; + uint32_t icr; + uint32_t iar; + + uint32_t errlog; + uint32_t amr; + uint32_t rtc; + time_t rtc_ref; + + MemoryRegion this_mem; +} LasiState; + +static bool lasi_chip_mem_valid(void *opaque, hwaddr addr, + unsigned size, bool is_write, + MemTxAttrs attrs) +{ + bool ret = false; + + switch (addr) { + case LASI_IRR: + case LASI_IMR: + case LASI_IPR: + case LASI_ICR: + case LASI_IAR: + + case (LASI_LAN_HPA - LASI_HPA): + case (LASI_LPT_HPA - LASI_HPA): + case (LASI_UART_HPA - LASI_HPA): + case (LASI_RTC_HPA - LASI_HPA): + + case LASI_PCR ... LASI_AMR: + ret = true; + } + + trace_lasi_chip_mem_valid(addr, ret); + return ret; +} + +static MemTxResult lasi_chip_read_with_attrs(void *opaque, hwaddr addr, + uint64_t *data, unsigned size, + MemTxAttrs attrs) +{ + LasiState *s = opaque; + MemTxResult ret = MEMTX_OK; + uint32_t val; + + switch (addr) { + case LASI_IRR: + val = s->irr; + break; + case LASI_IMR: + val = s->imr; + break; + case LASI_IPR: + val = s->ipr; + /* Any read to IPR clears the register. */ + s->ipr = 0; + break; + case LASI_ICR: + val = s->icr & ICR_BUS_ERROR_BIT; /* bus_error */ + break; + case LASI_IAR: + val = s->iar; + break; + + case (LASI_LAN_HPA - LASI_HPA): + case (LASI_LPT_HPA - LASI_HPA): + case (LASI_UART_HPA - LASI_HPA): + val = 0; + break; + case (LASI_RTC_HPA - LASI_HPA): + val = time(NULL); + val += s->rtc_ref; + break; + + case LASI_PCR: + case LASI_VER: /* only version 0 existed. */ + case LASI_IORESET: + val = 0; + break; + case LASI_ERRLOG: + val = s->errlog; + break; + case LASI_AMR: + val = s->amr; + break; + + default: + /* Controlled by lasi_chip_mem_valid above. */ + g_assert_not_reached(); + } + + trace_lasi_chip_read(addr, val); + + *data = val; + return ret; +} + +static MemTxResult lasi_chip_write_with_attrs(void *opaque, hwaddr addr, + uint64_t val, unsigned size, + MemTxAttrs attrs) +{ + LasiState *s = opaque; + + trace_lasi_chip_write(addr, val); + + switch (addr) { + case LASI_IRR: + /* read-only. */ + break; + case LASI_IMR: + s->imr = val; /* 0x20 ?? */ + assert((val & LASI_IRQ_BITS) == val); + break; + case LASI_IPR: + /* Any write to IPR clears the register. */ + s->ipr = 0; + break; + case LASI_ICR: + s->icr = val; + /* if (val & ICR_TOC_BIT) issue_toc(); */ + break; + case LASI_IAR: + s->iar = val; + break; + + case (LASI_LAN_HPA - LASI_HPA): + /* XXX: reset LAN card */ + break; + case (LASI_LPT_HPA - LASI_HPA): + /* XXX: reset parallel port */ + break; + case (LASI_UART_HPA - LASI_HPA): + /* XXX: reset serial port */ + break; + case (LASI_RTC_HPA - LASI_HPA): + s->rtc_ref = val - time(NULL); + break; + + case LASI_PCR: + if (val == 0x02) /* immediately power off */ + qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); + break; + case LASI_ERRLOG: + s->errlog = val; + break; + case LASI_VER: + /* read-only. */ + break; + case LASI_IORESET: + break; /* XXX: TODO: Reset various devices. */ + case LASI_AMR: + s->amr = val; + break; + + default: + /* Controlled by lasi_chip_mem_valid above. */ + g_assert_not_reached(); + } + return MEMTX_OK; +} + +static const MemoryRegionOps lasi_chip_ops = { + .read_with_attrs = lasi_chip_read_with_attrs, + .write_with_attrs = lasi_chip_write_with_attrs, + .endianness = DEVICE_BIG_ENDIAN, + .valid = { + .min_access_size = 1, + .max_access_size = 4, + .accepts = lasi_chip_mem_valid, + }, + .impl = { + .min_access_size = 1, + .max_access_size = 4, + }, +}; + +static const VMStateDescription vmstate_lasi = { + .name = "Lasi", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(irr, LasiState), + VMSTATE_UINT32(imr, LasiState), + VMSTATE_UINT32(ipr, LasiState), + VMSTATE_UINT32(icr, LasiState), + VMSTATE_UINT32(iar, LasiState), + VMSTATE_UINT32(errlog, LasiState), + VMSTATE_UINT32(amr, LasiState), + VMSTATE_END_OF_LIST() + } +}; + + +static void lasi_set_irq(void *opaque, int irq, int level) +{ + LasiState *s = opaque; + uint32_t bit = 1u << irq; + + if (level) { + s->ipr |= bit; + if (bit & s->imr) { + uint32_t iar = s->iar; + s->irr |= bit; + if ((s->icr & ICR_BUS_ERROR_BIT) == 0) { + stl_be_phys(&address_space_memory, iar & -32, iar & 31); + } + } + } +} + +static int lasi_get_irq(unsigned long hpa) +{ + switch (hpa) { + case LASI_HPA: + return 14; + case LASI_UART_HPA: + return 5; + case LASI_LPT_HPA: + return 7; + case LASI_LAN_HPA: + return 8; + case LASI_SCSI_HPA: + return 9; + case LASI_AUDIO_HPA: + return 13; + case LASI_PS2KBD_HPA: + case LASI_PS2MOU_HPA: + return 26; + default: + g_assert_not_reached(); + } +} + +DeviceState *lasi_init(MemoryRegion *address_space) +{ + DeviceState *dev; + LasiState *s; + + dev = qdev_create(NULL, TYPE_LASI_CHIP); + s = LASI_CHIP(dev); + s->iar = CPU_HPA + 3; + + /* Lasi access from main memory. */ + memory_region_init_io(&s->this_mem, OBJECT(s), &lasi_chip_ops, + s, "lasi", 0x100000); + memory_region_add_subregion(address_space, LASI_HPA, &s->this_mem); + + qdev_init_nofail(dev); + + /* LAN */ + if (enable_lasi_lan()) { + qemu_irq lan_irq = qemu_allocate_irq(lasi_set_irq, s, + lasi_get_irq(LASI_LAN_HPA)); + lasi_82596_init(address_space, LASI_LAN_HPA, lan_irq); + } + + /* Parallel port */ + qemu_irq lpt_irq = qemu_allocate_irq(lasi_set_irq, s, + lasi_get_irq(LASI_LPT_HPA)); + parallel_mm_init(address_space, LASI_LPT_HPA + 0x800, 0, + lpt_irq, parallel_hds[0]); + + /* Real time clock (RTC), it's only one 32-bit counter @9000 */ + + s->rtc = time(NULL); + s->rtc_ref = 0; + + if (serial_hd(1)) { + /* Serial port */ + qemu_irq serial_irq = qemu_allocate_irq(lasi_set_irq, s, + lasi_get_irq(LASI_UART_HPA)); + serial_mm_init(address_space, LASI_UART_HPA + 0x800, 0, + serial_irq, 8000000 / 16, + serial_hd(0), DEVICE_NATIVE_ENDIAN); + } + + /* PS/2 Keyboard/Mouse */ + qemu_irq ps2kbd_irq = qemu_allocate_irq(lasi_set_irq, s, + lasi_get_irq(LASI_PS2KBD_HPA)); + lasips2_init(address_space, LASI_PS2KBD_HPA, ps2kbd_irq); + + return dev; +} + +static void lasi_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->vmsd = &vmstate_lasi; +} + +static const TypeInfo lasi_pcihost_info = { + .name = TYPE_LASI_CHIP, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(LasiState), + .class_init = lasi_class_init, +}; + +static void lasi_register_types(void) +{ + type_register_static(&lasi_pcihost_info); +} + +type_init(lasi_register_types) diff --git a/hw/hppa/machine.c b/hw/hppa/machine.c index b30aba6d54..00dd9f58d6 100644 --- a/hw/hppa/machine.c +++ b/hw/hppa/machine.c @@ -13,13 +13,15 @@ #include "sysemu/reset.h" #include "sysemu/sysemu.h" #include "hw/rtc/mc146818rtc.h" -#include "hw/ide.h" #include "hw/timer/i8254.h" #include "hw/char/serial.h" +#include "hw/net/lasi_82596.h" #include "hppa_sys.h" #include "qemu/units.h" #include "qapi/error.h" +#include "net/net.h" #include "qemu/log.h" +#include "net/net.h" #define MAX_IDE_BUS 2 @@ -69,12 +71,10 @@ static void machine_hppa_init(MachineState *machine) uint64_t kernel_entry = 0, kernel_low, kernel_high; MemoryRegion *addr_space = get_system_memory(); MemoryRegion *rom_region; - MemoryRegion *ram_region; MemoryRegion *cpu_region; long i; unsigned int smp_cpus = machine->smp.cpus; - - ram_size = machine->ram_size; + SysBusDevice *s; /* Create CPUs. */ for (i = 0; i < smp_cpus; i++) { @@ -89,16 +89,16 @@ static void machine_hppa_init(MachineState *machine) g_free(name); } - /* Limit main memory. */ - if (ram_size > FIRMWARE_START) { - machine->ram_size = ram_size = FIRMWARE_START; - } - /* Main memory region. */ - ram_region = g_new(MemoryRegion, 1); - memory_region_allocate_system_memory(ram_region, OBJECT(machine), - "ram", ram_size); - memory_region_add_subregion(addr_space, 0, ram_region); + if (machine->ram_size > 3 * GiB) { + error_report("RAM size is currently restricted to 3GB"); + exit(EXIT_FAILURE); + } + memory_region_add_subregion_overlap(addr_space, 0, machine->ram, -1); + + + /* Init Lasi chip */ + lasi_init(addr_space); /* Init Dino (PCI host bus chip). */ pci_bus = dino_init(addr_space, &rtc_irq, &serial_irq); @@ -122,9 +122,20 @@ static void machine_hppa_init(MachineState *machine) dev = DEVICE(pci_create_simple(pci_bus, -1, "lsi53c895a")); lsi53c8xx_handle_legacy_cmdline(dev); - /* Network setup. e1000 is good enough, failing Tulip support. */ + /* Graphics setup. */ + if (machine->enable_graphics && vga_interface_type != VGA_NONE) { + dev = qdev_create(NULL, "artist"); + qdev_init_nofail(dev); + s = SYS_BUS_DEVICE(dev); + sysbus_mmio_map(s, 0, LASI_GFX_HPA); + sysbus_mmio_map(s, 1, ARTIST_FB_ADDR); + } + + /* Network setup. */ for (i = 0; i < nb_nics; i++) { - pci_nic_init_nofail(&nd_table[i], pci_bus, "e1000", NULL); + if (!enable_lasi_lan()) { + pci_nic_init_nofail(&nd_table[i], pci_bus, "tulip", NULL); + } } /* Load firmware. Given that this is not "real" firmware, @@ -139,7 +150,7 @@ static void machine_hppa_init(MachineState *machine) } size = load_elf(firmware_filename, NULL, NULL, NULL, - &firmware_entry, &firmware_low, &firmware_high, + &firmware_entry, &firmware_low, &firmware_high, NULL, true, EM_PARISC, 0, 0); /* Unfortunately, load_elf sign-extends reading elf32. */ @@ -154,7 +165,7 @@ static void machine_hppa_init(MachineState *machine) qemu_log_mask(CPU_LOG_PAGE, "Firmware loaded at 0x%08" PRIx64 "-0x%08" PRIx64 ", entry at 0x%08" PRIx64 ".\n", firmware_low, firmware_high, firmware_entry); - if (firmware_low < ram_size || firmware_high >= FIRMWARE_END) { + if (firmware_low < FIRMWARE_START || firmware_high >= FIRMWARE_END) { error_report("Firmware overlaps with memory or IO space"); exit(1); } @@ -168,7 +179,7 @@ static void machine_hppa_init(MachineState *machine) /* Load kernel */ if (kernel_filename) { size = load_elf(kernel_filename, NULL, &cpu_hppa_to_phys, - NULL, &kernel_entry, &kernel_low, &kernel_high, + NULL, &kernel_entry, &kernel_low, &kernel_high, NULL, true, EM_PARISC, 0, 0); /* Unfortunately, load_elf sign-extends reading elf32. */ @@ -279,9 +290,10 @@ static void machine_hppa_machine_init(MachineClass *mc) mc->block_default_type = IF_SCSI; mc->max_cpus = HPPA_MAX_CPUS; mc->default_cpus = 1; - mc->is_default = 1; + mc->is_default = true; mc->default_ram_size = 512 * MiB; mc->default_boot_order = "cd"; + mc->default_ram_id = "ram"; } DEFINE_MACHINE("hppa", machine_hppa_machine_init) diff --git a/hw/hppa/trace-events b/hw/hppa/trace-events index 4e2acb6176..3ff620319a 100644 --- a/hw/hppa/trace-events +++ b/hw/hppa/trace-events @@ -2,3 +2,13 @@ # pci.c hppa_pci_iack_write(void) "" + +# dino.c +dino_chip_mem_valid(uint64_t addr, uint32_t val) "access to addr 0x%"PRIx64" is %d" +dino_chip_read(uint64_t addr, uint32_t val) "addr 0x%"PRIx64" val 0x%08x" +dino_chip_write(uint64_t addr, uint32_t val) "addr 0x%"PRIx64" val 0x%08x" + +# lasi.c +lasi_chip_mem_valid(uint64_t addr, uint32_t val) "access to addr 0x%"PRIx64" is %d" +lasi_chip_read(uint64_t addr, uint32_t val) "addr 0x%"PRIx64" val 0x%08x" +lasi_chip_write(uint64_t addr, uint32_t val) "addr 0x%"PRIx64" val 0x%08x" diff --git a/hw/hyperv/hyperv.c b/hw/hyperv/hyperv.c index 6ebf31c310..8ca3706f5b 100644 --- a/hw/hyperv/hyperv.c +++ b/hw/hyperv/hyperv.c @@ -140,7 +140,7 @@ void hyperv_synic_reset(CPUState *cs) SynICState *synic = get_synic(cs); if (synic) { - device_reset(DEVICE(synic)); + device_legacy_reset(DEVICE(synic)); } } @@ -546,14 +546,14 @@ uint16_t hyperv_hcall_post_message(uint64_t param, bool fast) } ret = HV_STATUS_INVALID_CONNECTION_ID; - rcu_read_lock(); - QLIST_FOREACH_RCU(mh, &msg_handlers, link) { - if (mh->conn_id == (msg->connection_id & HV_CONNECTION_ID_MASK)) { - ret = mh->handler(msg, mh->data); - break; + WITH_RCU_READ_LOCK_GUARD() { + QLIST_FOREACH_RCU(mh, &msg_handlers, link) { + if (mh->conn_id == (msg->connection_id & HV_CONNECTION_ID_MASK)) { + ret = mh->handler(msg, mh->data); + break; + } } } - rcu_read_unlock(); unmap: cpu_physical_memory_unmap(msg, len, 0, 0); @@ -619,7 +619,6 @@ int hyperv_set_event_flag_handler(uint32_t conn_id, EventNotifier *notifier) uint16_t hyperv_hcall_signal_event(uint64_t param, bool fast) { - uint16_t ret; EventFlagHandler *handler; if (unlikely(!fast)) { @@ -645,15 +644,12 @@ uint16_t hyperv_hcall_signal_event(uint64_t param, bool fast) return HV_STATUS_INVALID_HYPERCALL_INPUT; } - ret = HV_STATUS_INVALID_CONNECTION_ID; - rcu_read_lock(); + RCU_READ_LOCK_GUARD(); QLIST_FOREACH_RCU(handler, &event_flag_handlers, link) { if (handler->conn_id == param) { event_notifier_set(handler->notifier); - ret = 0; - break; + return 0; } } - rcu_read_unlock(); - return ret; + return HV_STATUS_INVALID_CONNECTION_ID; } diff --git a/hw/i2c/Makefile.objs b/hw/i2c/Makefile.objs index d7073a401f..6ba976b257 100644 --- a/hw/i2c/Makefile.objs +++ b/hw/i2c/Makefile.objs @@ -9,5 +9,5 @@ common-obj-$(CONFIG_IMX_I2C) += imx_i2c.o common-obj-$(CONFIG_ASPEED_SOC) += aspeed_i2c.o common-obj-$(CONFIG_NRF51_SOC) += microbit_i2c.o common-obj-$(CONFIG_MPC_I2C) += mpc_i2c.o -obj-$(CONFIG_OMAP) += omap_i2c.o -obj-$(CONFIG_PPC4XX) += ppc4xx_i2c.o +common-obj-$(CONFIG_OMAP) += omap_i2c.o +common-obj-$(CONFIG_PPC4XX) += ppc4xx_i2c.o diff --git a/hw/i2c/aspeed_i2c.c b/hw/i2c/aspeed_i2c.c index 06c119f385..fb973a983d 100644 --- a/hw/i2c/aspeed_i2c.c +++ b/hw/i2c/aspeed_i2c.c @@ -23,20 +23,25 @@ #include "migration/vmstate.h" #include "qemu/log.h" #include "qemu/module.h" +#include "qemu/error-report.h" +#include "qapi/error.h" #include "hw/i2c/aspeed_i2c.h" #include "hw/irq.h" +#include "hw/qdev-properties.h" +#include "trace.h" /* I2C Global Register */ #define I2C_CTRL_STATUS 0x00 /* Device Interrupt Status */ #define I2C_CTRL_ASSIGN 0x08 /* Device Interrupt Target Assignment */ +#define I2C_CTRL_GLOBAL 0x0C /* Global Control Register */ +#define I2C_CTRL_SRAM_EN BIT(0) /* I2C Device (Bus) Register */ #define I2CD_FUN_CTRL_REG 0x00 /* I2CD Function Control */ -#define I2CD_BUFF_SEL_MASK (0x7 << 20) -#define I2CD_BUFF_SEL(x) (x << 20) +#define I2CD_POOL_PAGE_SEL(x) (((x) >> 20) & 0x7) /* AST2400 */ #define I2CD_M_SDA_LOCK_EN (0x1 << 16) #define I2CD_MULTI_MASTER_DIS (0x1 << 15) #define I2CD_M_SCL_DRIVE_EN (0x1 << 14) @@ -113,10 +118,12 @@ #define I2CD_SCL_O_OUT_DIR (0x1 << 12) #define I2CD_BUS_RECOVER_CMD_EN (0x1 << 11) #define I2CD_S_ALT_EN (0x1 << 10) -#define I2CD_RX_DMA_ENABLE (0x1 << 9) -#define I2CD_TX_DMA_ENABLE (0x1 << 8) /* Command Bit */ +#define I2CD_RX_DMA_ENABLE (0x1 << 9) +#define I2CD_TX_DMA_ENABLE (0x1 << 8) +#define I2CD_RX_BUFF_ENABLE (0x1 << 7) +#define I2CD_TX_BUFF_ENABLE (0x1 << 6) #define I2CD_M_STOP_CMD (0x1 << 5) #define I2CD_M_S_RX_CMD_LAST (0x1 << 4) #define I2CD_M_RX_CMD (0x1 << 3) @@ -125,13 +132,18 @@ #define I2CD_M_START_CMD (0x1) #define I2CD_DEV_ADDR_REG 0x18 /* Slave Device Address */ -#define I2CD_BUF_CTRL_REG 0x1c /* Pool Buffer Control */ +#define I2CD_POOL_CTRL_REG 0x1c /* Pool Buffer Control */ +#define I2CD_POOL_RX_COUNT(x) (((x) >> 24) & 0xff) +#define I2CD_POOL_RX_SIZE(x) ((((x) >> 16) & 0xff) + 1) +#define I2CD_POOL_TX_COUNT(x) ((((x) >> 8) & 0xff) + 1) +#define I2CD_POOL_OFFSET(x) (((x) & 0x3f) << 2) /* AST2400 */ #define I2CD_BYTE_BUF_REG 0x20 /* Transmit/Receive Byte Buffer */ #define I2CD_BYTE_BUF_TX_SHIFT 0 #define I2CD_BYTE_BUF_TX_MASK 0xff #define I2CD_BYTE_BUF_RX_SHIFT 8 #define I2CD_BYTE_BUF_RX_MASK 0xff - +#define I2CD_DMA_ADDR 0x24 /* DMA Buffer Address */ +#define I2CD_DMA_LEN 0x28 /* DMA Transfer Length < 4KB */ static inline bool aspeed_i2c_bus_is_master(AspeedI2CBus *bus) { @@ -147,6 +159,13 @@ static inline void aspeed_i2c_bus_raise_interrupt(AspeedI2CBus *bus) { AspeedI2CClass *aic = ASPEED_I2C_GET_CLASS(bus->controller); + trace_aspeed_i2c_bus_raise_interrupt(bus->intr_status, + bus->intr_status & I2CD_INTR_TX_NAK ? "nak|" : "", + bus->intr_status & I2CD_INTR_TX_ACK ? "ack|" : "", + bus->intr_status & I2CD_INTR_RX_DONE ? "done|" : "", + bus->intr_status & I2CD_INTR_NORMAL_STOP ? "normal|" : "", + bus->intr_status & I2CD_INTR_ABNORMAL ? "abnormal" : ""); + bus->intr_status &= bus->intr_ctrl; if (bus->intr_status) { bus->controller->intr_status |= 1 << bus->id; @@ -158,27 +177,58 @@ static uint64_t aspeed_i2c_bus_read(void *opaque, hwaddr offset, unsigned size) { AspeedI2CBus *bus = opaque; + AspeedI2CClass *aic = ASPEED_I2C_GET_CLASS(bus->controller); + uint64_t value = -1; switch (offset) { case I2CD_FUN_CTRL_REG: - return bus->ctrl; + value = bus->ctrl; + break; case I2CD_AC_TIMING_REG1: - return bus->timing[0]; + value = bus->timing[0]; + break; case I2CD_AC_TIMING_REG2: - return bus->timing[1]; + value = bus->timing[1]; + break; case I2CD_INTR_CTRL_REG: - return bus->intr_ctrl; + value = bus->intr_ctrl; + break; case I2CD_INTR_STS_REG: - return bus->intr_status; + value = bus->intr_status; + break; + case I2CD_POOL_CTRL_REG: + value = bus->pool_ctrl; + break; case I2CD_BYTE_BUF_REG: - return bus->buf; + value = bus->buf; + break; case I2CD_CMD_REG: - return bus->cmd | (i2c_bus_busy(bus->bus) << 16); + value = bus->cmd | (i2c_bus_busy(bus->bus) << 16); + break; + case I2CD_DMA_ADDR: + if (!aic->has_dma) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: No DMA support\n", __func__); + break; + } + value = bus->dma_addr; + break; + case I2CD_DMA_LEN: + if (!aic->has_dma) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: No DMA support\n", __func__); + break; + } + value = bus->dma_len; + break; + default: qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, offset); - return -1; + value = -1; + break; } + + trace_aspeed_i2c_bus_read(bus->id, offset, size, value); + return value; } static void aspeed_i2c_set_state(AspeedI2CBus *bus, uint8_t state) @@ -192,14 +242,114 @@ static uint8_t aspeed_i2c_get_state(AspeedI2CBus *bus) return (bus->cmd >> I2CD_TX_STATE_SHIFT) & I2CD_TX_STATE_MASK; } +static int aspeed_i2c_dma_read(AspeedI2CBus *bus, uint8_t *data) +{ + MemTxResult result; + AspeedI2CState *s = bus->controller; + + result = address_space_read(&s->dram_as, bus->dma_addr, + MEMTXATTRS_UNSPECIFIED, data, 1); + if (result != MEMTX_OK) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: DRAM read failed @%08x\n", + __func__, bus->dma_addr); + return -1; + } + + bus->dma_addr++; + bus->dma_len--; + return 0; +} + +static int aspeed_i2c_bus_send(AspeedI2CBus *bus, uint8_t pool_start) +{ + AspeedI2CClass *aic = ASPEED_I2C_GET_CLASS(bus->controller); + int ret = -1; + int i; + + if (bus->cmd & I2CD_TX_BUFF_ENABLE) { + for (i = pool_start; i < I2CD_POOL_TX_COUNT(bus->pool_ctrl); i++) { + uint8_t *pool_base = aic->bus_pool_base(bus); + + trace_aspeed_i2c_bus_send("BUF", i + 1, + I2CD_POOL_TX_COUNT(bus->pool_ctrl), + pool_base[i]); + ret = i2c_send(bus->bus, pool_base[i]); + if (ret) { + break; + } + } + bus->cmd &= ~I2CD_TX_BUFF_ENABLE; + } else if (bus->cmd & I2CD_TX_DMA_ENABLE) { + while (bus->dma_len) { + uint8_t data; + aspeed_i2c_dma_read(bus, &data); + trace_aspeed_i2c_bus_send("DMA", bus->dma_len, bus->dma_len, data); + ret = i2c_send(bus->bus, data); + if (ret) { + break; + } + } + bus->cmd &= ~I2CD_TX_DMA_ENABLE; + } else { + trace_aspeed_i2c_bus_send("BYTE", pool_start, 1, bus->buf); + ret = i2c_send(bus->bus, bus->buf); + } + + return ret; +} + +static void aspeed_i2c_bus_recv(AspeedI2CBus *bus) +{ + AspeedI2CState *s = bus->controller; + AspeedI2CClass *aic = ASPEED_I2C_GET_CLASS(s); + uint8_t data; + int i; + + if (bus->cmd & I2CD_RX_BUFF_ENABLE) { + uint8_t *pool_base = aic->bus_pool_base(bus); + + for (i = 0; i < I2CD_POOL_RX_SIZE(bus->pool_ctrl); i++) { + pool_base[i] = i2c_recv(bus->bus); + trace_aspeed_i2c_bus_recv("BUF", i + 1, + I2CD_POOL_RX_SIZE(bus->pool_ctrl), + pool_base[i]); + } + + /* Update RX count */ + bus->pool_ctrl &= ~(0xff << 24); + bus->pool_ctrl |= (i & 0xff) << 24; + bus->cmd &= ~I2CD_RX_BUFF_ENABLE; + } else if (bus->cmd & I2CD_RX_DMA_ENABLE) { + uint8_t data; + + while (bus->dma_len) { + MemTxResult result; + + data = i2c_recv(bus->bus); + trace_aspeed_i2c_bus_recv("DMA", bus->dma_len, bus->dma_len, data); + result = address_space_write(&s->dram_as, bus->dma_addr, + MEMTXATTRS_UNSPECIFIED, &data, 1); + if (result != MEMTX_OK) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: DRAM write failed @%08x\n", + __func__, bus->dma_addr); + return; + } + bus->dma_addr++; + bus->dma_len--; + } + bus->cmd &= ~I2CD_RX_DMA_ENABLE; + } else { + data = i2c_recv(bus->bus); + trace_aspeed_i2c_bus_recv("BYTE", 1, 1, bus->buf); + bus->buf = (data & I2CD_BYTE_BUF_RX_MASK) << I2CD_BYTE_BUF_RX_SHIFT; + } +} + static void aspeed_i2c_handle_rx_cmd(AspeedI2CBus *bus) { - uint8_t ret; - aspeed_i2c_set_state(bus, I2CD_MRXD); - ret = i2c_recv(bus->bus); + aspeed_i2c_bus_recv(bus); bus->intr_status |= I2CD_INTR_RX_DONE; - bus->buf = (ret & I2CD_BYTE_BUF_RX_MASK) << I2CD_BYTE_BUF_RX_SHIFT; if (bus->cmd & I2CD_M_S_RX_CMD_LAST) { i2c_nack(bus->bus); } @@ -207,31 +357,133 @@ static void aspeed_i2c_handle_rx_cmd(AspeedI2CBus *bus) aspeed_i2c_set_state(bus, I2CD_MACTIVE); } +static uint8_t aspeed_i2c_get_addr(AspeedI2CBus *bus) +{ + AspeedI2CClass *aic = ASPEED_I2C_GET_CLASS(bus->controller); + + if (bus->cmd & I2CD_TX_BUFF_ENABLE) { + uint8_t *pool_base = aic->bus_pool_base(bus); + + return pool_base[0]; + } else if (bus->cmd & I2CD_TX_DMA_ENABLE) { + uint8_t data; + + aspeed_i2c_dma_read(bus, &data); + return data; + } else { + return bus->buf; + } +} + +static bool aspeed_i2c_check_sram(AspeedI2CBus *bus) +{ + AspeedI2CState *s = bus->controller; + AspeedI2CClass *aic = ASPEED_I2C_GET_CLASS(s); + + if (!aic->check_sram) { + return true; + } + + /* + * AST2500: SRAM must be enabled before using the Buffer Pool or + * DMA mode. + */ + if (!(s->ctrl_global & I2C_CTRL_SRAM_EN) && + (bus->cmd & (I2CD_RX_DMA_ENABLE | I2CD_TX_DMA_ENABLE | + I2CD_RX_BUFF_ENABLE | I2CD_TX_BUFF_ENABLE))) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: SRAM is not enabled\n", __func__); + return false; + } + + return true; +} + +static void aspeed_i2c_bus_cmd_dump(AspeedI2CBus *bus) +{ + g_autofree char *cmd_flags = NULL; + uint32_t count; + + if (bus->cmd & (I2CD_RX_BUFF_ENABLE | I2CD_RX_BUFF_ENABLE)) { + count = I2CD_POOL_TX_COUNT(bus->pool_ctrl); + } else if (bus->cmd & (I2CD_RX_DMA_ENABLE | I2CD_RX_DMA_ENABLE)) { + count = bus->dma_len; + } else { /* BYTE mode */ + count = 1; + } + + cmd_flags = g_strdup_printf("%s%s%s%s%s%s%s%s%s", + bus->cmd & I2CD_M_START_CMD ? "start|" : "", + bus->cmd & I2CD_RX_DMA_ENABLE ? "rxdma|" : "", + bus->cmd & I2CD_TX_DMA_ENABLE ? "txdma|" : "", + bus->cmd & I2CD_RX_BUFF_ENABLE ? "rxbuf|" : "", + bus->cmd & I2CD_TX_BUFF_ENABLE ? "txbuf|" : "", + bus->cmd & I2CD_M_TX_CMD ? "tx|" : "", + bus->cmd & I2CD_M_RX_CMD ? "rx|" : "", + bus->cmd & I2CD_M_S_RX_CMD_LAST ? "last|" : "", + bus->cmd & I2CD_M_STOP_CMD ? "stop" : ""); + + trace_aspeed_i2c_bus_cmd(bus->cmd, cmd_flags, count, bus->intr_status); +} + /* * The state machine needs some refinement. It is only used to track * invalid STOP commands for the moment. */ static void aspeed_i2c_bus_handle_cmd(AspeedI2CBus *bus, uint64_t value) { + uint8_t pool_start = 0; + bus->cmd &= ~0xFFFF; bus->cmd |= value & 0xFFFF; + if (!aspeed_i2c_check_sram(bus)) { + return; + } + + if (trace_event_get_state_backends(TRACE_ASPEED_I2C_BUS_CMD)) { + aspeed_i2c_bus_cmd_dump(bus); + } + if (bus->cmd & I2CD_M_START_CMD) { uint8_t state = aspeed_i2c_get_state(bus) & I2CD_MACTIVE ? I2CD_MSTARTR : I2CD_MSTART; + uint8_t addr; aspeed_i2c_set_state(bus, state); - if (i2c_start_transfer(bus->bus, extract32(bus->buf, 1, 7), - extract32(bus->buf, 0, 1))) { + addr = aspeed_i2c_get_addr(bus); + + if (i2c_start_transfer(bus->bus, extract32(addr, 1, 7), + extract32(addr, 0, 1))) { bus->intr_status |= I2CD_INTR_TX_NAK; } else { bus->intr_status |= I2CD_INTR_TX_ACK; } - /* START command is also a TX command, as the slave address is - * sent on the bus */ - bus->cmd &= ~(I2CD_M_START_CMD | I2CD_M_TX_CMD); + bus->cmd &= ~I2CD_M_START_CMD; + + /* + * The START command is also a TX command, as the slave + * address is sent on the bus. Drop the TX flag if nothing + * else needs to be sent in this sequence. + */ + if (bus->cmd & I2CD_TX_BUFF_ENABLE) { + if (I2CD_POOL_TX_COUNT(bus->pool_ctrl) == 1) { + bus->cmd &= ~I2CD_M_TX_CMD; + } else { + /* + * Increase the start index in the TX pool buffer to + * skip the address byte. + */ + pool_start++; + } + } else if (bus->cmd & I2CD_TX_DMA_ENABLE) { + if (bus->dma_len == 0) { + bus->cmd &= ~I2CD_M_TX_CMD; + } + } else { + bus->cmd &= ~I2CD_M_TX_CMD; + } /* No slave found */ if (!i2c_bus_busy(bus->bus)) { @@ -242,7 +494,7 @@ static void aspeed_i2c_bus_handle_cmd(AspeedI2CBus *bus, uint64_t value) if (bus->cmd & I2CD_M_TX_CMD) { aspeed_i2c_set_state(bus, I2CD_MTXD); - if (i2c_send(bus->bus, bus->buf)) { + if (aspeed_i2c_bus_send(bus, pool_start)) { bus->intr_status |= (I2CD_INTR_TX_NAK); i2c_end_transfer(bus->bus); } else { @@ -278,6 +530,8 @@ static void aspeed_i2c_bus_write(void *opaque, hwaddr offset, AspeedI2CClass *aic = ASPEED_I2C_GET_CLASS(bus->controller); bool handle_rx; + trace_aspeed_i2c_bus_write(bus->id, offset, size, value); + switch (offset) { case I2CD_FUN_CTRL_REG: if (value & I2CD_SLAVE_EN) { @@ -313,6 +567,11 @@ static void aspeed_i2c_bus_write(void *opaque, hwaddr offset, qemu_log_mask(LOG_UNIMP, "%s: slave mode not implemented\n", __func__); break; + case I2CD_POOL_CTRL_REG: + bus->pool_ctrl &= ~0xffffff; + bus->pool_ctrl |= (value & 0xffffff); + break; + case I2CD_BYTE_BUF_REG: bus->buf = (value & I2CD_BYTE_BUF_TX_MASK) << I2CD_BYTE_BUF_TX_SHIFT; break; @@ -327,9 +586,35 @@ static void aspeed_i2c_bus_write(void *opaque, hwaddr offset, break; } + if (!aic->has_dma && + value & (I2CD_RX_DMA_ENABLE | I2CD_TX_DMA_ENABLE)) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: No DMA support\n", __func__); + break; + } + aspeed_i2c_bus_handle_cmd(bus, value); aspeed_i2c_bus_raise_interrupt(bus); break; + case I2CD_DMA_ADDR: + if (!aic->has_dma) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: No DMA support\n", __func__); + break; + } + + bus->dma_addr = value & 0xfffffffc; + break; + + case I2CD_DMA_LEN: + if (!aic->has_dma) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: No DMA support\n", __func__); + break; + } + + bus->dma_len = value & 0xfff; + if (!bus->dma_len) { + qemu_log_mask(LOG_UNIMP, "%s: invalid DMA length\n", __func__); + } + break; default: qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n", @@ -345,6 +630,8 @@ static uint64_t aspeed_i2c_ctrl_read(void *opaque, hwaddr offset, switch (offset) { case I2C_CTRL_STATUS: return s->intr_status; + case I2C_CTRL_GLOBAL: + return s->ctrl_global; default: qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, offset); @@ -357,7 +644,12 @@ static uint64_t aspeed_i2c_ctrl_read(void *opaque, hwaddr offset, static void aspeed_i2c_ctrl_write(void *opaque, hwaddr offset, uint64_t value, unsigned size) { + AspeedI2CState *s = opaque; + switch (offset) { + case I2C_CTRL_GLOBAL: + s->ctrl_global = value; + break; case I2C_CTRL_STATUS: default: qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n", @@ -378,10 +670,45 @@ static const MemoryRegionOps aspeed_i2c_ctrl_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; +static uint64_t aspeed_i2c_pool_read(void *opaque, hwaddr offset, + unsigned size) +{ + AspeedI2CState *s = opaque; + uint64_t ret = 0; + int i; + + for (i = 0; i < size; i++) { + ret |= (uint64_t) s->pool[offset + i] << (8 * i); + } + + return ret; +} + +static void aspeed_i2c_pool_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + AspeedI2CState *s = opaque; + int i; + + for (i = 0; i < size; i++) { + s->pool[offset + i] = (value >> (8 * i)) & 0xFF; + } +} + +static const MemoryRegionOps aspeed_i2c_pool_ops = { + .read = aspeed_i2c_pool_read, + .write = aspeed_i2c_pool_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 1, + .max_access_size = 4, + }, +}; + static const VMStateDescription aspeed_i2c_bus_vmstate = { .name = TYPE_ASPEED_I2C, - .version_id = 1, - .minimum_version_id = 1, + .version_id = 3, + .minimum_version_id = 3, .fields = (VMStateField[]) { VMSTATE_UINT8(id, AspeedI2CBus), VMSTATE_UINT32(ctrl, AspeedI2CBus), @@ -390,19 +717,23 @@ static const VMStateDescription aspeed_i2c_bus_vmstate = { VMSTATE_UINT32(intr_status, AspeedI2CBus), VMSTATE_UINT32(cmd, AspeedI2CBus), VMSTATE_UINT32(buf, AspeedI2CBus), + VMSTATE_UINT32(pool_ctrl, AspeedI2CBus), + VMSTATE_UINT32(dma_addr, AspeedI2CBus), + VMSTATE_UINT32(dma_len, AspeedI2CBus), VMSTATE_END_OF_LIST() } }; static const VMStateDescription aspeed_i2c_vmstate = { .name = TYPE_ASPEED_I2C, - .version_id = 1, - .minimum_version_id = 1, + .version_id = 2, + .minimum_version_id = 2, .fields = (VMStateField[]) { VMSTATE_UINT32(intr_status, AspeedI2CState), VMSTATE_STRUCT_ARRAY(busses, AspeedI2CState, ASPEED_I2C_NR_BUSSES, 1, aspeed_i2c_bus_vmstate, AspeedI2CBus), + VMSTATE_UINT8_ARRAY(pool, AspeedI2CState, ASPEED_I2C_MAX_POOL_SIZE), VMSTATE_END_OF_LIST() } }; @@ -420,6 +751,8 @@ static void aspeed_i2c_reset(DeviceState *dev) s->busses[i].intr_status = 0; s->busses[i].cmd = 0; s->busses[i].buf = 0; + s->busses[i].dma_addr = 0; + s->busses[i].dma_len = 0; i2c_end_transfer(s->busses[i].bus); } } @@ -472,14 +805,34 @@ static void aspeed_i2c_realize(DeviceState *dev, Error **errp) memory_region_add_subregion(&s->iomem, aic->reg_size * (i + offset), &s->busses[i].mr); } + + memory_region_init_io(&s->pool_iomem, OBJECT(s), &aspeed_i2c_pool_ops, s, + "aspeed.i2c-pool", aic->pool_size); + memory_region_add_subregion(&s->iomem, aic->pool_base, &s->pool_iomem); + + if (aic->has_dma) { + if (!s->dram_mr) { + error_setg(errp, TYPE_ASPEED_I2C ": 'dram' link not set"); + return; + } + + address_space_init(&s->dram_as, s->dram_mr, "dma-dram"); + } } +static Property aspeed_i2c_properties[] = { + DEFINE_PROP_LINK("dram", AspeedI2CState, dram_mr, + TYPE_MEMORY_REGION, MemoryRegion *), + DEFINE_PROP_END_OF_LIST(), +}; + static void aspeed_i2c_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->vmsd = &aspeed_i2c_vmstate; dc->reset = aspeed_i2c_reset; + device_class_set_props(dc, aspeed_i2c_properties); dc->realize = aspeed_i2c_realize; dc->desc = "Aspeed I2C Controller"; } @@ -498,6 +851,14 @@ static qemu_irq aspeed_2400_i2c_bus_get_irq(AspeedI2CBus *bus) return bus->controller->irq; } +static uint8_t *aspeed_2400_i2c_bus_pool_base(AspeedI2CBus *bus) +{ + uint8_t *pool_page = + &bus->controller->pool[I2CD_POOL_PAGE_SEL(bus->ctrl) * 0x100]; + + return &pool_page[I2CD_POOL_OFFSET(bus->pool_ctrl)]; +} + static void aspeed_2400_i2c_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -509,6 +870,9 @@ static void aspeed_2400_i2c_class_init(ObjectClass *klass, void *data) aic->reg_size = 0x40; aic->gap = 7; aic->bus_get_irq = aspeed_2400_i2c_bus_get_irq; + aic->pool_size = 0x800; + aic->pool_base = 0x800; + aic->bus_pool_base = aspeed_2400_i2c_bus_pool_base; } static const TypeInfo aspeed_2400_i2c_info = { @@ -522,6 +886,11 @@ static qemu_irq aspeed_2500_i2c_bus_get_irq(AspeedI2CBus *bus) return bus->controller->irq; } +static uint8_t *aspeed_2500_i2c_bus_pool_base(AspeedI2CBus *bus) +{ + return &bus->controller->pool[bus->id * 0x10]; +} + static void aspeed_2500_i2c_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -533,6 +902,11 @@ static void aspeed_2500_i2c_class_init(ObjectClass *klass, void *data) aic->reg_size = 0x40; aic->gap = 7; aic->bus_get_irq = aspeed_2500_i2c_bus_get_irq; + aic->pool_size = 0x100; + aic->pool_base = 0x200; + aic->bus_pool_base = aspeed_2500_i2c_bus_pool_base; + aic->check_sram = true; + aic->has_dma = true; } static const TypeInfo aspeed_2500_i2c_info = { @@ -546,6 +920,11 @@ static qemu_irq aspeed_2600_i2c_bus_get_irq(AspeedI2CBus *bus) return bus->irq; } +static uint8_t *aspeed_2600_i2c_bus_pool_base(AspeedI2CBus *bus) +{ + return &bus->controller->pool[bus->id * 0x20]; +} + static void aspeed_2600_i2c_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -557,6 +936,10 @@ static void aspeed_2600_i2c_class_init(ObjectClass *klass, void *data) aic->reg_size = 0x80; aic->gap = -1; /* no gap */ aic->bus_get_irq = aspeed_2600_i2c_bus_get_irq; + aic->pool_size = 0x200; + aic->pool_base = 0xC00; + aic->bus_pool_base = aspeed_2600_i2c_bus_pool_base; + aic->has_dma = true; } static const TypeInfo aspeed_2600_i2c_info = { diff --git a/hw/i2c/core.c b/hw/i2c/core.c index 92cd489069..d413a192ed 100644 --- a/hw/i2c/core.c +++ b/hw/i2c/core.c @@ -61,7 +61,7 @@ I2CBus *i2c_init_bus(DeviceState *parent, const char *name) bus = I2C_BUS(qbus_create(TYPE_I2C_BUS, parent, name)); QLIST_INIT(&bus->current_devs); - vmstate_register(NULL, -1, &vmstate_i2c_bus, bus); + vmstate_register(NULL, VMSTATE_INSTANCE_ID_ANY, &vmstate_i2c_bus, bus); return bus; } @@ -281,7 +281,7 @@ static void i2c_slave_class_init(ObjectClass *klass, void *data) DeviceClass *k = DEVICE_CLASS(klass); set_bit(DEVICE_CATEGORY_MISC, k->categories); k->bus_type = TYPE_I2C_BUS; - k->props = i2c_props; + device_class_set_props(k, i2c_props); } static const TypeInfo i2c_slave_type_info = { diff --git a/hw/i2c/omap_i2c.c b/hw/i2c/omap_i2c.c index 3ba965a58f..e5d205dda5 100644 --- a/hw/i2c/omap_i2c.c +++ b/hw/i2c/omap_i2c.c @@ -28,10 +28,7 @@ #include "qemu/error-report.h" #include "qapi/error.h" -#define TYPE_OMAP_I2C "omap_i2c" -#define OMAP_I2C(obj) OBJECT_CHECK(OMAPI2CState, (obj), TYPE_OMAP_I2C) - -typedef struct OMAPI2CState { +struct OMAPI2CState { SysBusDevice parent_obj; MemoryRegion iomem; @@ -56,7 +53,7 @@ typedef struct OMAPI2CState { uint8_t divider; uint8_t times[2]; uint16_t test; -} OMAPI2CState; +}; #define OMAP2_INTR_REV 0x34 #define OMAP2_GC_REV 0x34 @@ -504,10 +501,18 @@ static void omap_i2c_realize(DeviceState *dev, Error **errp) } } +void omap_i2c_set_iclk(OMAPI2CState *i2c, omap_clk clk) +{ + i2c->iclk = clk; +} + +void omap_i2c_set_fclk(OMAPI2CState *i2c, omap_clk clk) +{ + i2c->fclk = clk; +} + static Property omap_i2c_properties[] = { DEFINE_PROP_UINT8("revision", OMAPI2CState, revision, 0), - DEFINE_PROP_PTR("iclk", OMAPI2CState, iclk), - DEFINE_PROP_PTR("fclk", OMAPI2CState, fclk), DEFINE_PROP_END_OF_LIST(), }; @@ -515,7 +520,7 @@ static void omap_i2c_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->props = omap_i2c_properties; + device_class_set_props(dc, omap_i2c_properties); dc->reset = omap_i2c_reset; /* Reason: pointer properties "iclk", "fclk" */ dc->user_creatable = false; diff --git a/hw/i2c/ppc4xx_i2c.c b/hw/i2c/ppc4xx_i2c.c index 3f015a1581..c0a8e04567 100644 --- a/hw/i2c/ppc4xx_i2c.c +++ b/hw/i2c/ppc4xx_i2c.c @@ -27,7 +27,6 @@ #include "qemu/osdep.h" #include "qemu/log.h" #include "qemu/module.h" -#include "cpu.h" #include "hw/i2c/ppc4xx_i2c.h" #include "hw/irq.h" diff --git a/hw/i2c/smbus_eeprom.c b/hw/i2c/smbus_eeprom.c index 54c86a0112..5adf3b15b5 100644 --- a/hw/i2c/smbus_eeprom.c +++ b/hw/i2c/smbus_eeprom.c @@ -44,7 +44,7 @@ typedef struct SMBusEEPROMDevice { SMBusDevice smbusdev; uint8_t data[SMBUS_EEPROM_SIZE]; - void *init_data; + uint8_t *init_data; uint8_t offset; bool accessed; } SMBusEEPROMDevice; @@ -129,13 +129,13 @@ static void smbus_eeprom_reset(DeviceState *dev) static void smbus_eeprom_realize(DeviceState *dev, Error **errp) { - smbus_eeprom_reset(dev); -} + SMBusEEPROMDevice *eeprom = SMBUS_EEPROM(dev); -static Property smbus_eeprom_properties[] = { - DEFINE_PROP_PTR("data", SMBusEEPROMDevice, init_data), - DEFINE_PROP_END_OF_LIST(), -}; + smbus_eeprom_reset(dev); + if (eeprom->init_data == NULL) { + error_setg(errp, "init_data cannot be NULL"); + } +} static void smbus_eeprom_class_initfn(ObjectClass *klass, void *data) { @@ -146,9 +146,8 @@ static void smbus_eeprom_class_initfn(ObjectClass *klass, void *data) dc->reset = smbus_eeprom_reset; sc->receive_byte = eeprom_receive_byte; sc->write_data = eeprom_write_data; - dc->props = smbus_eeprom_properties; dc->vmsd = &vmstate_smbus_eeprom; - /* Reason: pointer property "data" */ + /* Reason: init_data */ dc->user_creatable = false; } @@ -172,7 +171,8 @@ void smbus_eeprom_init_one(I2CBus *smbus, uint8_t address, uint8_t *eeprom_buf) dev = qdev_create((BusState *) smbus, TYPE_SMBUS_EEPROM); qdev_prop_set_uint8(dev, "address", address); - qdev_prop_set_ptr(dev, "data", eeprom_buf); + /* FIXME: use an array of byte or block backend property? */ + SMBUS_EEPROM(dev)->init_data = eeprom_buf; qdev_init_nofail(dev); } diff --git a/hw/i2c/smbus_ich9.c b/hw/i2c/smbus_ich9.c index fd50fb851a..48f1ff4191 100644 --- a/hw/i2c/smbus_ich9.c +++ b/hw/i2c/smbus_ich9.c @@ -21,6 +21,7 @@ */ #include "qemu/osdep.h" +#include "qemu/range.h" #include "hw/i2c/pm_smbus.h" #include "hw/pci/pci.h" #include "migration/vmstate.h" diff --git a/hw/i2c/trace-events b/hw/i2c/trace-events index e1c810d5bd..08db8fa689 100644 --- a/hw/i2c/trace-events +++ b/hw/i2c/trace-events @@ -5,3 +5,12 @@ i2c_event(const char *event, uint8_t address) "%s(addr:0x%02x)" i2c_send(uint8_t address, uint8_t data) "send(addr:0x%02x) data:0x%02x" i2c_recv(uint8_t address, uint8_t data) "recv(addr:0x%02x) data:0x%02x" + +# aspeed_i2c.c + +aspeed_i2c_bus_cmd(uint32_t cmd, const char *cmd_flags, uint32_t count, uint32_t intr_status) "handling cmd=0x%x %s count=%d intr=0x%x" +aspeed_i2c_bus_raise_interrupt(uint32_t intr_status, const char *str1, const char *str2, const char *str3, const char *str4, const char *str5) "handled intr=0x%x %s%s%s%s%s" +aspeed_i2c_bus_read(uint32_t busid, uint64_t offset, unsigned size, uint64_t value) "bus[%d]: To 0x%" PRIx64 " of size %u: 0x%" PRIx64 +aspeed_i2c_bus_write(uint32_t busid, uint64_t offset, unsigned size, uint64_t value) "bus[%d]: To 0x%" PRIx64 " of size %u: 0x%" PRIx64 +aspeed_i2c_bus_send(const char *mode, int i, int count, uint8_t byte) "%s send %d/%d 0x%02x" +aspeed_i2c_bus_recv(const char *mode, int i, int count, uint8_t byte) "%s recv %d/%d 0x%02x" diff --git a/hw/i386/Kconfig b/hw/i386/Kconfig index fa8f4a0c37..e312904e34 100644 --- a/hw/i386/Kconfig +++ b/hw/i386/Kconfig @@ -20,7 +20,7 @@ config PC imply SGA imply TEST_DEVICES imply TPM_CRB - imply TPM_TIS + imply TPM_TIS_ISA imply VGA_PCI imply VIRTIO_VGA select FDC @@ -95,7 +95,7 @@ config Q35 config MICROVM bool - imply SERIAL_ISA + select SERIAL_ISA # for serial_hds_isa_init() select ISA_BUS select APIC select IOAPIC @@ -103,11 +103,17 @@ config MICROVM select MC146818RTC select VIRTIO_MMIO +config X86_IOMMU + bool + depends on PC + config VTD bool + select X86_IOMMU config AMD_IOMMU bool + select X86_IOMMU config VMPORT bool diff --git a/hw/i386/Makefile.objs b/hw/i386/Makefile.objs index 0d195b5210..8ce1b26533 100644 --- a/hw/i386/Makefile.objs +++ b/hw/i386/Makefile.objs @@ -1,17 +1,19 @@ obj-$(CONFIG_KVM) += kvm/ obj-y += e820_memory_layout.o multiboot.o obj-y += x86.o -obj-y += pc.o +obj-$(CONFIG_PC) += pc.o pc_sysfw.o obj-$(CONFIG_I440FX) += pc_piix.o obj-$(CONFIG_Q35) += pc_q35.o obj-$(CONFIG_MICROVM) += microvm.o -obj-y += fw_cfg.o pc_sysfw.o -obj-y += x86-iommu.o +obj-y += fw_cfg.o +obj-$(CONFIG_X86_IOMMU) += x86-iommu.o +obj-$(call lnot,$(CONFIG_X86_IOMMU)) += x86-iommu-stub.o obj-$(CONFIG_VTD) += intel_iommu.o obj-$(CONFIG_AMD_IOMMU) += amd_iommu.o obj-$(CONFIG_XEN) += ../xenpv/ xen/ obj-$(CONFIG_VMPORT) += vmport.o obj-$(CONFIG_VMMOUSE) += vmmouse.o +obj-$(CONFIG_PC) += port92.o obj-y += kvmvapic.o -obj-y += acpi-build.o +obj-$(CONFIG_PC) += acpi-build.o diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index 1fc45801f3..6b4f898fdc 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -47,12 +47,14 @@ #include "hw/rtc/mc146818rtc_regs.h" #include "migration/vmstate.h" #include "hw/mem/memory-device.h" +#include "hw/mem/nvdimm.h" #include "sysemu/numa.h" #include "sysemu/reset.h" /* Supported chipsets: */ #include "hw/southbridge/piix.h" #include "hw/acpi/pcihp.h" +#include "hw/i386/fw_cfg.h" #include "hw/i386/ich9.h" #include "hw/pci/pci_bus.h" #include "hw/pci-host/q35.h" @@ -67,6 +69,7 @@ #include "hw/i386/intel_iommu.h" #include "hw/acpi/ipmi.h" +#include "hw/acpi/hmat.h" /* These are used to size the ACPI tables for -M pc-i440fx-1.7 and * -M pc-i440fx-2.0. Even if the actual amount of AML generated grows @@ -1817,7 +1820,6 @@ static void build_smb0(Aml *table, I2CBus *smbus, int devnr, int func) Aml *scope = aml_scope("_SB.PCI0"); Aml *dev = aml_device("SMB0"); - aml_append(dev, aml_name_decl("_HID", aml_eisaid("APP0005"))); aml_append(dev, aml_name_decl("_ADR", aml_int(devnr << 16 | func))); build_acpi_ipmi_devices(dev, BUS(smbus), "\\_SB.PCI0.SMB0"); aml_append(scope, dev); @@ -2028,7 +2030,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, } } - if (TPM_IS_TIS(tpm_find())) { + if (TPM_IS_TIS_ISA(tpm_find())) { aml_append(crs, aml_memory32_fixed(TPM_TIS_ADDR_BASE, TPM_TIS_ADDR_SIZE, AML_READ_WRITE)); } @@ -2199,7 +2201,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, /* Scan all PCI buses. Generate tables to support hotplug. */ build_append_pci_bus_devices(scope, bus, pm->pcihp_bridge_en); - if (TPM_IS_TIS(tpm)) { + if (TPM_IS_TIS_ISA(tpm)) { if (misc->tpm_version == TPM_VERSION_2_0) { dev = aml_device("TPM"); aml_append(dev, aml_name_decl("_HID", @@ -2306,7 +2308,7 @@ build_tpm2(GArray *table_data, BIOSLinker *linker, GArray *tcpalog) (char *)&tpm2_ptr->log_area_start_address - table_data->data; tpm2_ptr->platform_class = cpu_to_le16(TPM2_ACPI_CLASS_CLIENT); - if (TPM_IS_TIS(tpm_find())) { + if (TPM_IS_TIS_ISA(tpm_find())) { tpm2_ptr->control_area_address = cpu_to_le64(0); tpm2_ptr->start_method = cpu_to_le32(TPM2_START_METHOD_MMIO); } else if (TPM_IS_CRB(tpm_find())) { @@ -2837,6 +2839,10 @@ void acpi_build(AcpiBuildTables *tables, MachineState *machine) acpi_add_table(table_offsets, tables_blob); build_slit(tables_blob, tables->linker, machine); } + if (machine->numa_state->hmat_enabled) { + acpi_add_table(table_offsets, tables_blob); + build_hmat(tables_blob, tables->linker, machine->numa_state); + } } if (acpi_get_mcfg(&mcfg)) { acpi_add_table(table_offsets, tables_blob); @@ -3021,7 +3027,7 @@ void acpi_setup(void) return; } - if (!acpi_enabled) { + if (!x86_machine_is_acpi_enabled(X86_MACHINE(pcms))) { ACPI_BUILD_DPRINTF("ACPI disabled. Bailing out.\n"); return; } @@ -3040,7 +3046,7 @@ void acpi_setup(void) build_state->linker_mr = acpi_add_rom_blob(acpi_build_update, build_state, - tables.linker->cmd_blob, "etc/table-loader", 0); + tables.linker->cmd_blob, ACPI_BUILD_LOADER_FILE, 0); fw_cfg_add_file(x86ms->fw_cfg, ACPI_BUILD_TPMLOG_FILE, tables.tcpalog->data, acpi_data_len(tables.tcpalog)); diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c index d55dbf07fc..fd75cae024 100644 --- a/hw/i386/amd_iommu.c +++ b/hw/i386/amd_iommu.c @@ -181,7 +181,7 @@ static void amdvi_log_event(AMDVIState *s, uint64_t *evt) } if (dma_memory_write(&address_space_memory, s->evtlog + s->evtlog_tail, - &evt, AMDVI_EVENT_LEN)) { + evt, AMDVI_EVENT_LEN)) { trace_amdvi_evntlog_fail(s->evtlog, s->evtlog_tail); } @@ -1533,7 +1533,7 @@ static void amdvi_reset(DeviceState *dev) amdvi_init(s); } -static void amdvi_realize(DeviceState *dev, Error **err) +static void amdvi_realize(DeviceState *dev, Error **errp) { int ret = 0; AMDVIState *s = AMD_IOMMU_DEVICE(dev); @@ -1549,21 +1549,21 @@ static void amdvi_realize(DeviceState *dev, Error **err) /* This device should take care of IOMMU PCI properties */ x86_iommu->type = TYPE_AMD; qdev_set_parent_bus(DEVICE(&s->pci), &bus->qbus); - object_property_set_bool(OBJECT(&s->pci), true, "realized", err); + object_property_set_bool(OBJECT(&s->pci), true, "realized", errp); ret = pci_add_capability(&s->pci.dev, AMDVI_CAPAB_ID_SEC, 0, - AMDVI_CAPAB_SIZE, err); + AMDVI_CAPAB_SIZE, errp); if (ret < 0) { return; } s->capab_offset = ret; ret = pci_add_capability(&s->pci.dev, PCI_CAP_ID_MSI, 0, - AMDVI_CAPAB_REG_SIZE, err); + AMDVI_CAPAB_REG_SIZE, errp); if (ret < 0) { return; } ret = pci_add_capability(&s->pci.dev, PCI_CAP_ID_HT, 0, - AMDVI_CAPAB_REG_SIZE, err); + AMDVI_CAPAB_REG_SIZE, errp); if (ret < 0) { return; } @@ -1578,8 +1578,8 @@ static void amdvi_realize(DeviceState *dev, Error **err) sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->mmio); sysbus_mmio_map(SYS_BUS_DEVICE(s), 0, AMDVI_BASE_ADDR); pci_setup_iommu(bus, amdvi_host_dma_iommu, s); - s->devid = object_property_get_int(OBJECT(&s->pci), "addr", err); - msi_init(&s->pci.dev, 0, 1, true, false, err); + s->devid = object_property_get_int(OBJECT(&s->pci), "addr", errp); + msi_init(&s->pci.dev, 0, 1, true, false, errp); amdvi_init(s); } diff --git a/hw/i386/fw_cfg.c b/hw/i386/fw_cfg.c index 39b6bc6052..da60ada594 100644 --- a/hw/i386/fw_cfg.c +++ b/hw/i386/fw_cfg.c @@ -16,12 +16,14 @@ #include "sysemu/numa.h" #include "hw/acpi/acpi.h" #include "hw/firmware/smbios.h" -#include "hw/i386/pc.h" #include "hw/i386/fw_cfg.h" #include "hw/timer/hpet.h" #include "hw/nvram/fw_cfg.h" #include "e820_memory_layout.h" #include "kvm_i386.h" +#include "config-devices.h" + +struct hpet_fw_config hpet_cfg = {.count = UINT8_MAX}; const char *fw_cfg_arch_key_name(uint16_t key) { @@ -46,6 +48,7 @@ const char *fw_cfg_arch_key_name(uint16_t key) void fw_cfg_build_smbios(MachineState *ms, FWCfgState *fw_cfg) { +#ifdef CONFIG_SMBIOS uint8_t *smbios_tables, *smbios_anchor; size_t smbios_tables_len, smbios_anchor_len; struct smbios_phys_mem_area *mem_array; @@ -83,6 +86,7 @@ void fw_cfg_build_smbios(MachineState *ms, FWCfgState *fw_cfg) fw_cfg_add_file(fw_cfg, "etc/smbios/smbios-anchor", smbios_anchor, smbios_anchor_len); } +#endif } FWCfgState *fw_cfg_arch_create(MachineState *ms, @@ -114,8 +118,10 @@ FWCfgState *fw_cfg_arch_create(MachineState *ms, */ fw_cfg_add_i16(fw_cfg, FW_CFG_MAX_CPUS, apic_id_limit); fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)ram_size); +#ifdef CONFIG_ACPI fw_cfg_add_bytes(fw_cfg, FW_CFG_ACPI_TABLES, acpi_tables, acpi_tables_len); +#endif fw_cfg_add_i32(fw_cfg, FW_CFG_IRQ0_OVERRIDE, kvm_allows_irq0_override()); fw_cfg_add_bytes(fw_cfg, FW_CFG_E820_TABLE, diff --git a/hw/i386/fw_cfg.h b/hw/i386/fw_cfg.h index e0856a3769..9e74278779 100644 --- a/hw/i386/fw_cfg.h +++ b/hw/i386/fw_cfg.h @@ -12,6 +12,8 @@ #include "hw/boards.h" #include "hw/nvram/fw_cfg.h" +#define FW_CFG_IO_BASE 0x510 + #define FW_CFG_ACPI_TABLES (FW_CFG_ARCH_LOCAL + 0) #define FW_CFG_SMBIOS_ENTRIES (FW_CFG_ARCH_LOCAL + 1) #define FW_CFG_IRQ0_OVERRIDE (FW_CFG_ARCH_LOCAL + 2) diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index 43c94b993b..df7ad254ac 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -686,9 +686,18 @@ static inline bool vtd_pe_type_check(X86IOMMUState *x86_iommu, return true; } -static int vtd_get_pasid_dire(dma_addr_t pasid_dir_base, - uint32_t pasid, - VTDPASIDDirEntry *pdire) +static inline bool vtd_pdire_present(VTDPASIDDirEntry *pdire) +{ + return pdire->val & 1; +} + +/** + * Caller of this function should check present bit if wants + * to use pdir entry for futher usage except for fpd bit check. + */ +static int vtd_get_pdire_from_pdir_table(dma_addr_t pasid_dir_base, + uint32_t pasid, + VTDPASIDDirEntry *pdire) { uint32_t index; dma_addr_t addr, entry_size; @@ -703,18 +712,22 @@ static int vtd_get_pasid_dire(dma_addr_t pasid_dir_base, return 0; } -static int vtd_get_pasid_entry(IntelIOMMUState *s, - uint32_t pasid, - VTDPASIDDirEntry *pdire, - VTDPASIDEntry *pe) +static inline bool vtd_pe_present(VTDPASIDEntry *pe) +{ + return pe->val[0] & VTD_PASID_ENTRY_P; +} + +static int vtd_get_pe_in_pasid_leaf_table(IntelIOMMUState *s, + uint32_t pasid, + dma_addr_t addr, + VTDPASIDEntry *pe) { uint32_t index; - dma_addr_t addr, entry_size; + dma_addr_t entry_size; X86IOMMUState *x86_iommu = X86_IOMMU_DEVICE(s); index = VTD_PASID_TABLE_INDEX(pasid); entry_size = VTD_PASID_ENTRY_SIZE; - addr = pdire->val & VTD_PASID_TABLE_BASE_ADDR_MASK; addr = addr + index * entry_size; if (dma_memory_read(&address_space_memory, addr, pe, entry_size)) { return -VTD_FR_PASID_TABLE_INV; @@ -732,25 +745,54 @@ static int vtd_get_pasid_entry(IntelIOMMUState *s, return 0; } -static int vtd_get_pasid_entry_from_pasid(IntelIOMMUState *s, - dma_addr_t pasid_dir_base, - uint32_t pasid, - VTDPASIDEntry *pe) +/** + * Caller of this function should check present bit if wants + * to use pasid entry for futher usage except for fpd bit check. + */ +static int vtd_get_pe_from_pdire(IntelIOMMUState *s, + uint32_t pasid, + VTDPASIDDirEntry *pdire, + VTDPASIDEntry *pe) +{ + dma_addr_t addr = pdire->val & VTD_PASID_TABLE_BASE_ADDR_MASK; + + return vtd_get_pe_in_pasid_leaf_table(s, pasid, addr, pe); +} + +/** + * This function gets a pasid entry from a specified pasid + * table (includes dir and leaf table) with a specified pasid. + * Sanity check should be done to ensure return a present + * pasid entry to caller. + */ +static int vtd_get_pe_from_pasid_table(IntelIOMMUState *s, + dma_addr_t pasid_dir_base, + uint32_t pasid, + VTDPASIDEntry *pe) { int ret; VTDPASIDDirEntry pdire; - ret = vtd_get_pasid_dire(pasid_dir_base, pasid, &pdire); + ret = vtd_get_pdire_from_pdir_table(pasid_dir_base, + pasid, &pdire); if (ret) { return ret; } - ret = vtd_get_pasid_entry(s, pasid, &pdire, pe); + if (!vtd_pdire_present(&pdire)) { + return -VTD_FR_PASID_TABLE_INV; + } + + ret = vtd_get_pe_from_pdire(s, pasid, &pdire, pe); if (ret) { return ret; } - return ret; + if (!vtd_pe_present(pe)) { + return -VTD_FR_PASID_TABLE_INV; + } + + return 0; } static int vtd_ce_get_rid2pasid_entry(IntelIOMMUState *s, @@ -763,7 +805,7 @@ static int vtd_ce_get_rid2pasid_entry(IntelIOMMUState *s, pasid = VTD_CE_GET_RID2PASID(ce); pasid_dir_base = VTD_CE_GET_PASID_DIR_TABLE(ce); - ret = vtd_get_pasid_entry_from_pasid(s, pasid_dir_base, pasid, pe); + ret = vtd_get_pe_from_pasid_table(s, pasid_dir_base, pasid, pe); return ret; } @@ -781,7 +823,11 @@ static int vtd_ce_get_pasid_fpd(IntelIOMMUState *s, pasid = VTD_CE_GET_RID2PASID(ce); pasid_dir_base = VTD_CE_GET_PASID_DIR_TABLE(ce); - ret = vtd_get_pasid_dire(pasid_dir_base, pasid, &pdire); + /* + * No present bit check since fpd is meaningful even + * if the present bit is clear. + */ + ret = vtd_get_pdire_from_pdir_table(pasid_dir_base, pasid, &pdire); if (ret) { return ret; } @@ -791,7 +837,15 @@ static int vtd_ce_get_pasid_fpd(IntelIOMMUState *s, return 0; } - ret = vtd_get_pasid_entry(s, pasid, &pdire, &pe); + if (!vtd_pdire_present(&pdire)) { + return -VTD_FR_PASID_TABLE_INV; + } + + /* + * No present bit check since fpd is meaningful even + * if the present bit is clear. + */ + ret = vtd_get_pe_from_pdire(s, pasid, &pdire, &pe); if (ret) { return ret; } @@ -933,23 +987,26 @@ static bool vtd_slpte_nonzero_rsvd(uint64_t slpte, uint32_t level) static VTDBus *vtd_find_as_from_bus_num(IntelIOMMUState *s, uint8_t bus_num) { VTDBus *vtd_bus = s->vtd_as_by_bus_num[bus_num]; - if (!vtd_bus) { - /* - * Iterate over the registered buses to find the one which - * currently hold this bus number, and update the bus_num - * lookup table: - */ - GHashTableIter iter; + GHashTableIter iter; - g_hash_table_iter_init(&iter, s->vtd_as_by_busptr); - while (g_hash_table_iter_next(&iter, NULL, (void **)&vtd_bus)) { - if (pci_bus_num(vtd_bus->bus) == bus_num) { - s->vtd_as_by_bus_num[bus_num] = vtd_bus; - return vtd_bus; - } + if (vtd_bus) { + return vtd_bus; + } + + /* + * Iterate over the registered buses to find the one which + * currently holds this bus number and update the bus_num + * lookup table. + */ + g_hash_table_iter_init(&iter, s->vtd_as_by_busptr); + while (g_hash_table_iter_next(&iter, NULL, (void **)&vtd_bus)) { + if (pci_bus_num(vtd_bus->bus) == bus_num) { + s->vtd_as_by_bus_num[bus_num] = vtd_bus; + return vtd_bus; } } - return vtd_bus; + + return NULL; } /* Given the @iova, get relevant @slptep. @slpte_level will be the last level @@ -2610,16 +2667,15 @@ static uint64_t vtd_mem_read(void *opaque, hwaddr addr, unsigned size) switch (addr) { /* Root Table Address Register, 64-bit */ case DMAR_RTADDR_REG: + val = vtd_get_quad_raw(s, DMAR_RTADDR_REG); if (size == 4) { - val = s->root & ((1ULL << 32) - 1); - } else { - val = s->root; + val = val & ((1ULL << 32) - 1); } break; case DMAR_RTADDR_REG_HI: assert(size == 4); - val = s->root >> 32; + val = vtd_get_quad_raw(s, DMAR_RTADDR_REG) >> 32; break; /* Invalidation Queue Address Register, 64-bit */ @@ -3038,6 +3094,12 @@ static int vtd_irte_get(IntelIOMMUState *iommu, uint16_t index, uint16_t mask, source_id; uint8_t bus, bus_max, bus_min; + if (index >= iommu->intr_size) { + error_report_once("%s: index too large: ind=0x%x", + __func__, index); + return -VTD_FR_IR_INDEX_OVER; + } + addr = iommu->intr_root + index * sizeof(*entry); if (dma_memory_read(&address_space_memory, addr, entry, sizeof(*entry))) { @@ -3791,7 +3853,7 @@ static void vtd_class_init(ObjectClass *klass, void *data) dc->reset = vtd_reset; dc->vmsd = &vtd_vmstate; - dc->props = vtd_properties; + device_class_set_props(dc, vtd_properties); dc->hotpluggable = false; x86_class->realize = vtd_realize; x86_class->int_remap = vtd_int_remap; diff --git a/hw/i386/intel_iommu_internal.h b/hw/i386/intel_iommu_internal.h index edcf9fc9bb..862033ebe6 100644 --- a/hw/i386/intel_iommu_internal.h +++ b/hw/i386/intel_iommu_internal.h @@ -479,6 +479,7 @@ typedef struct VTDRootEntry VTDRootEntry; #define VTD_PASID_ENTRY_FPD (1ULL << 1) /* Fault Processing Disable */ /* PASID Granular Translation Type Mask */ +#define VTD_PASID_ENTRY_P 1ULL #define VTD_SM_PASID_ENTRY_PGTT (7ULL << 6) #define VTD_SM_PASID_ENTRY_FLT (1ULL << 6) #define VTD_SM_PASID_ENTRY_SLT (2ULL << 6) diff --git a/hw/i386/kvm/Makefile.objs b/hw/i386/kvm/Makefile.objs index 4224ed900e..0c8d5f2dee 100644 --- a/hw/i386/kvm/Makefile.objs +++ b/hw/i386/kvm/Makefile.objs @@ -1 +1,5 @@ -obj-y += clock.o apic.o i8259.o ioapic.o i8254.o +obj-y += clock.o +obj-$(CONFIG_APIC) += apic.o +obj-$(CONFIG_IOAPIC) += ioapic.o +obj-$(CONFIG_I8254) += i8254.o +obj-$(CONFIG_I8259) += i8259.o diff --git a/hw/i386/kvm/clock.c b/hw/i386/kvm/clock.c index 2c59b6894b..64283358f9 100644 --- a/hw/i386/kvm/clock.c +++ b/hw/i386/kvm/clock.c @@ -317,7 +317,7 @@ static void kvmclock_class_init(ObjectClass *klass, void *data) dc->realize = kvmclock_realize; dc->vmsd = &kvmclock_vmsd; - dc->props = kvmclock_properties; + device_class_set_props(dc, kvmclock_properties); } static const TypeInfo kvmclock_info = { diff --git a/hw/i386/kvm/i8254.c b/hw/i386/kvm/i8254.c index 6a911e23f9..876f5aa6fa 100644 --- a/hw/i386/kvm/i8254.c +++ b/hw/i386/kvm/i8254.c @@ -322,7 +322,7 @@ static void kvm_pit_class_init(ObjectClass *klass, void *data) k->set_channel_gate = kvm_pit_set_gate; k->get_channel_info = kvm_pit_get_channel_info; dc->reset = kvm_pit_reset; - dc->props = kvm_pit_properties; + device_class_set_props(dc, kvm_pit_properties); } static const TypeInfo kvm_pit_info = { diff --git a/hw/i386/kvm/i8259.c b/hw/i386/kvm/i8259.c index d0c1b1deac..e404fdcdac 100644 --- a/hw/i386/kvm/i8259.c +++ b/hw/i386/kvm/i8259.c @@ -12,6 +12,7 @@ #include "qemu/osdep.h" #include "hw/isa/i8259_internal.h" +#include "hw/intc/i8259.h" #include "qemu/module.h" #include "hw/i386/apic_internal.h" #include "hw/irq.h" diff --git a/hw/i386/kvm/ioapic.c b/hw/i386/kvm/ioapic.c index f94729c565..4ba8e47251 100644 --- a/hw/i386/kvm/ioapic.c +++ b/hw/i386/kvm/ioapic.c @@ -12,7 +12,7 @@ #include "qemu/osdep.h" #include "monitor/monitor.h" -#include "hw/i386/pc.h" +#include "hw/i386/x86.h" #include "hw/irq.h" #include "hw/qdev-properties.h" #include "hw/i386/ioapic_internal.h" @@ -48,18 +48,6 @@ void kvm_pc_setup_irq_routing(bool pci_enabled) } } -void kvm_pc_gsi_handler(void *opaque, int n, int level) -{ - GSIState *s = opaque; - - if (n < ISA_NUM_IRQS) { - /* Kernel will forward to both PIC and IOAPIC */ - qemu_set_irq(s->i8259_irq[n], level); - } else { - qemu_set_irq(s->ioapic_irq[n], level); - } -} - typedef struct KVMIOAPICState KVMIOAPICState; struct KVMIOAPICState { @@ -161,7 +149,7 @@ static void kvm_ioapic_class_init(ObjectClass *klass, void *data) k->pre_save = kvm_ioapic_get; k->post_load = kvm_ioapic_put; dc->reset = kvm_ioapic_reset; - dc->props = kvm_ioapic_properties; + device_class_set_props(dc, kvm_ioapic_properties); } static const TypeInfo kvm_ioapic_info = { diff --git a/hw/i386/microvm.c b/hw/i386/microvm.c index def37e60f7..38d8e51703 100644 --- a/hw/i386/microvm.c +++ b/hw/i386/microvm.c @@ -32,8 +32,8 @@ #include "hw/kvm/clock.h" #include "hw/i386/microvm.h" #include "hw/i386/x86.h" -#include "hw/i386/pc.h" #include "target/i386/cpu.h" +#include "hw/intc/i8259.h" #include "hw/timer/i8254.h" #include "hw/rtc/mc146818rtc.h" #include "hw/char/serial.h" @@ -132,7 +132,7 @@ static void microvm_devices_init(MicrovmMachineState *mms) if (mms->pic == ON_OFF_AUTO_ON || mms->pic == ON_OFF_AUTO_AUTO) { qemu_irq *i8259; - i8259 = i8259_init(isa_bus, pc_allocate_cpu_irq()); + i8259 = i8259_init(isa_bus, x86_allocate_cpu_irq()); for (i = 0; i < ISA_NUM_IRQS; i++) { gsi_state->i8259_irq[i] = i8259[i]; } @@ -167,7 +167,7 @@ static void microvm_memory_init(MicrovmMachineState *mms) { MachineState *machine = MACHINE(mms); X86MachineState *x86ms = X86_MACHINE(mms); - MemoryRegion *ram, *ram_below_4g, *ram_above_4g; + MemoryRegion *ram_below_4g, *ram_above_4g; MemoryRegion *system_memory = get_system_memory(); FWCfgState *fw_cfg; ram_addr_t lowmem; @@ -214,12 +214,8 @@ static void microvm_memory_init(MicrovmMachineState *mms) x86ms->below_4g_mem_size = machine->ram_size; } - ram = g_malloc(sizeof(*ram)); - memory_region_allocate_system_memory(ram, NULL, "microvm.ram", - machine->ram_size); - ram_below_4g = g_malloc(sizeof(*ram_below_4g)); - memory_region_init_alias(ram_below_4g, NULL, "ram-below-4g", ram, + memory_region_init_alias(ram_below_4g, NULL, "ram-below-4g", machine->ram, 0, x86ms->below_4g_mem_size); memory_region_add_subregion(system_memory, 0, ram_below_4g); @@ -227,7 +223,8 @@ static void microvm_memory_init(MicrovmMachineState *mms) if (x86ms->above_4g_mem_size > 0) { ram_above_4g = g_malloc(sizeof(*ram_above_4g)); - memory_region_init_alias(ram_above_4g, NULL, "ram-above-4g", ram, + memory_region_init_alias(ram_above_4g, NULL, "ram-above-4g", + machine->ram, x86ms->below_4g_mem_size, x86ms->above_4g_mem_size); memory_region_add_subregion(system_memory, 0x100000000ULL, @@ -370,7 +367,7 @@ static void microvm_machine_reset(MachineState *machine) cpu = X86_CPU(cs); if (cpu->apic_state) { - device_reset(cpu->apic_state); + device_legacy_reset(cpu->apic_state); } } } @@ -502,6 +499,7 @@ static void microvm_class_init(ObjectClass *oc, void *data) mc->auto_enable_numa_with_memhp = false; mc->default_cpu_type = TARGET_DEFAULT_CPU_TYPE; mc->nvdimm_supported = false; + mc->default_ram_id = "microvm.ram"; /* Avoid relying too much on kernel components */ mc->default_kernel_irqchip_split = true; diff --git a/hw/i386/multiboot.c b/hw/i386/multiboot.c index 9a59f95497..9e7d69d470 100644 --- a/hw/i386/multiboot.c +++ b/hw/i386/multiboot.c @@ -199,7 +199,7 @@ int load_multiboot(FWCfgState *fw_cfg, } kernel_size = load_elf(kernel_filename, NULL, NULL, NULL, &elf_entry, - &elf_low, &elf_high, 0, I386_ELF_MACHINE, + &elf_low, &elf_high, NULL, 0, I386_ELF_MACHINE, 0, 0); if (kernel_size < 0) { error_report("Error while loading elf kernel"); diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 3ca5763427..5143c51653 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -44,6 +44,7 @@ #include "migration/vmstate.h" #include "multiboot.h" #include "hw/rtc/mc146818rtc.h" +#include "hw/intc/i8259.h" #include "hw/dma/i8257.h" #include "hw/timer/i8254.h" #include "hw/input/i8042.h" @@ -75,6 +76,7 @@ #include "hw/boards.h" #include "acpi-build.h" #include "hw/mem/pc-dimm.h" +#include "hw/mem/nvdimm.h" #include "qapi/error.h" #include "qapi/qapi-visit-common.h" #include "qapi/visitor.h" @@ -90,18 +92,12 @@ #include "config-devices.h" #include "e820_memory_layout.h" #include "fw_cfg.h" +#include "trace.h" -/* debug PC/ISA interrupts */ -//#define DEBUG_IRQ - -#ifdef DEBUG_IRQ -#define DPRINTF(fmt, ...) \ - do { printf("CPUIRQ: " fmt , ## __VA_ARGS__); } while (0) -#else -#define DPRINTF(fmt, ...) -#endif - -struct hpet_fw_config hpet_cfg = {.count = UINT8_MAX}; +GlobalProperty pc_compat_4_2[] = { + { "mch", "smbase-smram", "off" }, +}; +const size_t pc_compat_4_2_len = G_N_ELEMENTS(pc_compat_4_2); GlobalProperty pc_compat_4_1[] = {}; const size_t pc_compat_4_1_len = G_N_ELEMENTS(pc_compat_4_1); @@ -344,17 +340,6 @@ GlobalProperty pc_compat_1_4[] = { }; const size_t pc_compat_1_4_len = G_N_ELEMENTS(pc_compat_1_4); -void gsi_handler(void *opaque, int n, int level) -{ - GSIState *s = opaque; - - DPRINTF("pc: %s GSI %d\n", level ? "raising" : "lowering", n); - if (n < ISA_NUM_IRQS) { - qemu_set_irq(s->i8259_irq[n], level); - } - qemu_set_irq(s->ioapic_irq[n], level); -} - GSIState *pc_gsi_create(qemu_irq **irqs, bool pci_enabled) { GSIState *s; @@ -362,10 +347,8 @@ GSIState *pc_gsi_create(qemu_irq **irqs, bool pci_enabled) s = g_new0(GSIState, 1); if (kvm_ioapic_in_kernel()) { kvm_pc_setup_irq_routing(pci_enabled); - *irqs = qemu_allocate_irqs(kvm_pc_gsi_handler, s, GSI_NUM_PINS); - } else { - *irqs = qemu_allocate_irqs(gsi_handler, s, GSI_NUM_PINS); } + *irqs = qemu_allocate_irqs(gsi_handler, s, GSI_NUM_PINS); return s; } @@ -394,60 +377,6 @@ static uint64_t ioportF0_read(void *opaque, hwaddr addr, unsigned size) return 0xffffffffffffffffULL; } -/* TSC handling */ -uint64_t cpu_get_tsc(CPUX86State *env) -{ -#ifdef XBOX - return muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), 733333333, - NANOSECONDS_PER_SECOND); -#else - return cpu_get_ticks(); -#endif -} - -/* IRQ handling */ -int cpu_get_pic_interrupt(CPUX86State *env) -{ - X86CPU *cpu = env_archcpu(env); - int intno; - - if (!kvm_irqchip_in_kernel()) { - intno = apic_get_interrupt(cpu->apic_state); - if (intno >= 0) { - return intno; - } - /* read the irq from the PIC */ - if (!apic_accept_pic_intr(cpu->apic_state)) { - return -1; - } - } - - intno = pic_read_irq(isa_pic); - return intno; -} - -static void pic_irq_request(void *opaque, int irq, int level) -{ - CPUState *cs = first_cpu; - X86CPU *cpu = X86_CPU(cs); - - DPRINTF("pic_irqs: %s irq %d\n", level? "raise" : "lower", irq); - if (cpu->apic_state && !kvm_irqchip_in_kernel()) { - CPU_FOREACH(cs) { - cpu = X86_CPU(cs); - if (apic_accept_pic_intr(cpu->apic_state)) { - apic_deliver_pic_intr(cpu->apic_state, level); - } - } - } else { - if (level) { - cpu_interrupt(cs, CPU_INTERRUPT_HARD); - } else { - cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); - } - } -} - /* PC cmos mappings */ #define REG_EQUIPMENT_BYTE 0x14 @@ -747,124 +676,6 @@ void pc_cmos_init(PCMachineState *pcms, qemu_register_reset(pc_cmos_init_late, &arg); } -#define TYPE_PORT92 "port92" -#define PORT92(obj) OBJECT_CHECK(Port92State, (obj), TYPE_PORT92) - -/* port 92 stuff: could be split off */ -typedef struct Port92State { - ISADevice parent_obj; - - MemoryRegion io; - uint8_t outport; - qemu_irq a20_out; -} Port92State; - -static void port92_write(void *opaque, hwaddr addr, uint64_t val, - unsigned size) -{ - Port92State *s = opaque; - int oldval = s->outport; - - DPRINTF("port92: write 0x%02" PRIx64 "\n", val); - s->outport = val; - qemu_set_irq(s->a20_out, (val >> 1) & 1); - if ((val & 1) && !(oldval & 1)) { - qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); - } -} - -static uint64_t port92_read(void *opaque, hwaddr addr, - unsigned size) -{ - Port92State *s = opaque; - uint32_t ret; - - ret = s->outport; - DPRINTF("port92: read 0x%02x\n", ret); - return ret; -} - -static void port92_init(ISADevice *dev, qemu_irq a20_out) -{ - qdev_connect_gpio_out_named(DEVICE(dev), PORT92_A20_LINE, 0, a20_out); -} - -static const VMStateDescription vmstate_port92_isa = { - .name = "port92", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT8(outport, Port92State), - VMSTATE_END_OF_LIST() - } -}; - -static void port92_reset(DeviceState *d) -{ - Port92State *s = PORT92(d); - - s->outport &= ~1; -} - -static const MemoryRegionOps port92_ops = { - .read = port92_read, - .write = port92_write, - .impl = { - .min_access_size = 1, - .max_access_size = 1, - }, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static void port92_initfn(Object *obj) -{ - Port92State *s = PORT92(obj); - - memory_region_init_io(&s->io, OBJECT(s), &port92_ops, s, "port92", 1); - - s->outport = 0; - - qdev_init_gpio_out_named(DEVICE(obj), &s->a20_out, PORT92_A20_LINE, 1); -} - -static void port92_realizefn(DeviceState *dev, Error **errp) -{ - ISADevice *isadev = ISA_DEVICE(dev); - Port92State *s = PORT92(dev); - - isa_register_ioport(isadev, &s->io, 0x92); -} - -static void port92_class_initfn(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = port92_realizefn; - dc->reset = port92_reset; - dc->vmsd = &vmstate_port92_isa; - /* - * Reason: unlike ordinary ISA devices, this one needs additional - * wiring: its A20 output line needs to be wired up by - * port92_init(). - */ - dc->user_creatable = false; -} - -static const TypeInfo port92_info = { - .name = TYPE_PORT92, - .parent = TYPE_ISA_DEVICE, - .instance_size = sizeof(Port92State), - .instance_init = port92_initfn, - .class_init = port92_class_initfn, -}; - -static void port92_register_types(void) -{ - type_register_static(&port92_info); -} - -type_init(port92_register_types) - static void handle_a20_line_change(void *opaque, int irq, int level) { X86CPU *cpu = opaque; @@ -891,16 +702,6 @@ void pc_init_ne2k_isa(ISABus *bus, NICInfo *nd) nb_ne2k++; } -DeviceState *cpu_get_current_apic(void) -{ - if (current_cpu) { - X86CPU *cpu = X86_CPU(current_cpu); - return cpu->apic_state; - } else { - return NULL; - } -} - void pc_acpi_smi_interrupt(void *opaque, int irq, int level) { X86CPU *cpu = opaque; @@ -980,6 +781,7 @@ void pc_smp_parse(MachineState *ms, QemuOpts *opts) ms->smp.cpus = cpus; ms->smp.cores = cores; ms->smp.threads = threads; + ms->smp.sockets = sockets; x86ms->smp_dies = dies; } @@ -1137,7 +939,7 @@ void pc_memory_init(PCMachineState *pcms, MemoryRegion **ram_memory) { int linux_boot, i; - MemoryRegion *ram, *option_rom_mr; + MemoryRegion *option_rom_mr; MemoryRegion *ram_below_4g, *ram_above_4g; FWCfgState *fw_cfg; MachineState *machine = MACHINE(pcms); @@ -1150,22 +952,20 @@ void pc_memory_init(PCMachineState *pcms, linux_boot = (machine->kernel_filename != NULL); - /* Allocate RAM. We allocate it as a single memory region and use - * aliases to address portions of it, mostly for backwards compatibility - * with older qemus that used qemu_ram_alloc(). + /* + * Split single memory region and use aliases to address portions of it, + * done for backwards compatibility with older qemus. */ - ram = g_malloc(sizeof(*ram)); - memory_region_allocate_system_memory(ram, NULL, "pc.ram", - machine->ram_size); - *ram_memory = ram; + *ram_memory = machine->ram; ram_below_4g = g_malloc(sizeof(*ram_below_4g)); - memory_region_init_alias(ram_below_4g, NULL, "ram-below-4g", ram, + memory_region_init_alias(ram_below_4g, NULL, "ram-below-4g", machine->ram, 0, x86ms->below_4g_mem_size); memory_region_add_subregion(system_memory, 0, ram_below_4g); e820_add_entry(0, x86ms->below_4g_mem_size, E820_RAM); if (x86ms->above_4g_mem_size > 0) { ram_above_4g = g_malloc(sizeof(*ram_above_4g)); - memory_region_init_alias(ram_above_4g, NULL, "ram-above-4g", ram, + memory_region_init_alias(ram_above_4g, NULL, "ram-above-4g", + machine->ram, x86ms->below_4g_mem_size, x86ms->above_4g_mem_size); memory_region_add_subregion(system_memory, 0x100000000ULL, @@ -1296,11 +1096,6 @@ uint64_t pc_pci_hole64_start(void) return ROUND_UP(hole64_start, 1 * GiB); } -qemu_irq pc_allocate_cpu_irq(void) -{ - return qemu_allocate_irq(pic_irq_request, NULL, 0); -} - DeviceState *pc_vga_init(ISABus *isa_bus, PCIBus *pci_bus) { DeviceState *dev = NULL; @@ -1363,15 +1158,16 @@ static void pc_superio_init(ISABus *isa_bus, bool create_fdctrl, bool no_vmport) vmmouse = NULL; } if (vmmouse) { - DeviceState *dev = DEVICE(vmmouse); - qdev_prop_set_ptr(dev, "ps2_mouse", i8042); - qdev_init_nofail(dev); + object_property_set_link(OBJECT(vmmouse), OBJECT(i8042), + "i8042", &error_abort); + qdev_init_nofail(DEVICE(vmmouse)); } - port92 = isa_create_simple(isa_bus, "port92"); + port92 = isa_create_simple(isa_bus, TYPE_PORT92); a20_line = qemu_allocate_irqs(handle_a20_line_change, first_cpu, 2); i8042_setup_a20_line(i8042, a20_line[0]); - port92_init(port92, a20_line[1]); + qdev_connect_gpio_out_named(DEVICE(port92), + PORT92_A20_LINE, 0, a20_line[1]); g_free(a20_line); } @@ -1404,7 +1200,6 @@ void pc_basic_device_init(ISABus *isa_bus, qemu_irq *gsi, * when the HPET wants to take over. Thus we have to disable the latter. */ if (!no_hpet && (!kvm_irqchip_in_kernel() || kvm_has_pit_state2())) { - /* In order to set property, here not using sysbus_try_create_simple */ hpet = qdev_try_create(NULL, TYPE_HPET); if (hpet) { /* For pc-piix-*, hpet's intcap is always IRQ2. For pc-q35-1.7 @@ -1477,7 +1272,7 @@ void pc_i8259_create(ISABus *isa_bus, qemu_irq *i8259_irqs) } else if (xen_enabled()) { i8259 = xen_interrupt_controller_init(); } else { - i8259 = i8259_init(isa_bus, pc_allocate_cpu_irq()); + i8259 = i8259_init(isa_bus, x86_allocate_cpu_irq()); } for (size_t i = 0; i < ISA_NUM_IRQS; i++) { @@ -1487,30 +1282,6 @@ void pc_i8259_create(ISABus *isa_bus, qemu_irq *i8259_irqs) g_free(i8259); } -void ioapic_init_gsi(GSIState *gsi_state, const char *parent_name) -{ - DeviceState *dev; - SysBusDevice *d; - unsigned int i; - - if (kvm_ioapic_in_kernel()) { - dev = qdev_create(NULL, TYPE_KVM_IOAPIC); - } else { - dev = qdev_create(NULL, TYPE_IOAPIC); - } - if (parent_name) { - object_property_add_child(object_resolve_path(parent_name, NULL), - "ioapic", OBJECT(dev), NULL); - } - qdev_init_nofail(dev); - d = SYS_BUS_DEVICE(dev); - sysbus_mmio_map(d, 0, IO_APIC_DEFAULT_ADDRESS); - - for (i = 0; i < IOAPIC_NUM_PINS; i++) { - gsi_state->ioapic_irq[i] = qdev_get_gpio_in(dev, i); - } -} - static void pc_memory_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { @@ -1526,7 +1297,7 @@ static void pc_memory_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, * but pcms->acpi_dev is still created. Check !acpi_enabled in * addition to cover this case. */ - if (!pcms->acpi_dev || !acpi_enabled) { + if (!pcms->acpi_dev || !x86_machine_is_acpi_enabled(X86_MACHINE(pcms))) { error_setg(errp, "memory hotplug is not enabled: missing acpi device or acpi disabled"); return; @@ -1580,7 +1351,7 @@ static void pc_memory_unplug_request(HotplugHandler *hotplug_dev, * but pcms->acpi_dev is still created. Check !acpi_enabled in * addition to cover this case. */ - if (!pcms->acpi_dev || !acpi_enabled) { + if (!pcms->acpi_dev || !x86_machine_is_acpi_enabled(X86_MACHINE(pcms))) { error_setg(&local_err, "memory hotplug is not enabled: missing acpi device or acpi disabled"); goto out; @@ -1735,7 +1506,7 @@ static void pc_cpu_pre_plug(HotplugHandler *hotplug_dev, int idx; CPUState *cs; CPUArchId *cpu_slot; - X86CPUTopoInfo topo; + X86CPUTopoIDs topo_ids; X86CPU *cpu = X86_CPU(dev); CPUX86State *env = &cpu->env; MachineState *ms = MACHINE(hotplug_dev); @@ -1743,6 +1514,7 @@ static void pc_cpu_pre_plug(HotplugHandler *hotplug_dev, X86MachineState *x86ms = X86_MACHINE(pcms); unsigned int smp_cores = ms->smp.cores; unsigned int smp_threads = ms->smp.threads; + X86CPUTopoInfo topo_info; if(!object_dynamic_cast(OBJECT(cpu), ms->cpu_type)) { error_setg(errp, "Invalid CPU type, expected cpu type: '%s'", @@ -1750,7 +1522,11 @@ static void pc_cpu_pre_plug(HotplugHandler *hotplug_dev, return; } + init_topo_info(&topo_info, x86ms); + env->nr_dies = x86ms->smp_dies; + env->nr_nodes = topo_info.nodes_per_pkg; + env->pkg_offset = x86ms->apicid_pkg_offset(&topo_info); /* * If APIC ID is not set, @@ -1801,24 +1577,22 @@ static void pc_cpu_pre_plug(HotplugHandler *hotplug_dev, return; } - topo.pkg_id = cpu->socket_id; - topo.die_id = cpu->die_id; - topo.core_id = cpu->core_id; - topo.smt_id = cpu->thread_id; - cpu->apic_id = apicid_from_topo_ids(x86ms->smp_dies, smp_cores, - smp_threads, &topo); + topo_ids.pkg_id = cpu->socket_id; + topo_ids.die_id = cpu->die_id; + topo_ids.core_id = cpu->core_id; + topo_ids.smt_id = cpu->thread_id; + cpu->apic_id = x86ms->apicid_from_topo_ids(&topo_info, &topo_ids); } cpu_slot = pc_find_cpu_slot(MACHINE(pcms), cpu->apic_id, &idx); if (!cpu_slot) { MachineState *ms = MACHINE(pcms); - x86_topo_ids_from_apicid(cpu->apic_id, x86ms->smp_dies, - smp_cores, smp_threads, &topo); + x86ms->topo_ids_from_apicid(cpu->apic_id, &topo_info, &topo_ids); error_setg(errp, "Invalid CPU [socket: %u, die: %u, core: %u, thread: %u] with" " APIC ID %" PRIu32 ", valid index range 0:%d", - topo.pkg_id, topo.die_id, topo.core_id, topo.smt_id, + topo_ids.pkg_id, topo_ids.die_id, topo_ids.core_id, topo_ids.smt_id, cpu->apic_id, ms->possible_cpus->len - 1); return; } @@ -1835,35 +1609,37 @@ static void pc_cpu_pre_plug(HotplugHandler *hotplug_dev, /* TODO: move socket_id/core_id/thread_id checks into x86_cpu_realizefn() * once -smp refactoring is complete and there will be CPU private * CPUState::nr_cores and CPUState::nr_threads fields instead of globals */ - x86_topo_ids_from_apicid(cpu->apic_id, x86ms->smp_dies, - smp_cores, smp_threads, &topo); - if (cpu->socket_id != -1 && cpu->socket_id != topo.pkg_id) { + x86ms->topo_ids_from_apicid(cpu->apic_id, &topo_info, &topo_ids); + if (cpu->socket_id != -1 && cpu->socket_id != topo_ids.pkg_id) { error_setg(errp, "property socket-id: %u doesn't match set apic-id:" - " 0x%x (socket-id: %u)", cpu->socket_id, cpu->apic_id, topo.pkg_id); + " 0x%x (socket-id: %u)", cpu->socket_id, cpu->apic_id, + topo_ids.pkg_id); return; } - cpu->socket_id = topo.pkg_id; + cpu->socket_id = topo_ids.pkg_id; - if (cpu->die_id != -1 && cpu->die_id != topo.die_id) { + if (cpu->die_id != -1 && cpu->die_id != topo_ids.die_id) { error_setg(errp, "property die-id: %u doesn't match set apic-id:" - " 0x%x (die-id: %u)", cpu->die_id, cpu->apic_id, topo.die_id); + " 0x%x (die-id: %u)", cpu->die_id, cpu->apic_id, topo_ids.die_id); return; } - cpu->die_id = topo.die_id; + cpu->die_id = topo_ids.die_id; - if (cpu->core_id != -1 && cpu->core_id != topo.core_id) { + if (cpu->core_id != -1 && cpu->core_id != topo_ids.core_id) { error_setg(errp, "property core-id: %u doesn't match set apic-id:" - " 0x%x (core-id: %u)", cpu->core_id, cpu->apic_id, topo.core_id); + " 0x%x (core-id: %u)", cpu->core_id, cpu->apic_id, + topo_ids.core_id); return; } - cpu->core_id = topo.core_id; + cpu->core_id = topo_ids.core_id; - if (cpu->thread_id != -1 && cpu->thread_id != topo.smt_id) { + if (cpu->thread_id != -1 && cpu->thread_id != topo_ids.smt_id) { error_setg(errp, "property thread-id: %u doesn't match set apic-id:" - " 0x%x (thread-id: %u)", cpu->thread_id, cpu->apic_id, topo.smt_id); + " 0x%x (thread-id: %u)", cpu->thread_id, cpu->apic_id, + topo_ids.smt_id); return; } - cpu->thread_id = topo.smt_id; + cpu->thread_id = topo_ids.smt_id; if (hyperv_feat_enabled(cpu, HYPERV_FEAT_VPINDEX) && !kvm_hv_vpindex_settable()) { @@ -2034,48 +1810,6 @@ static void pc_machine_set_vmport(Object *obj, Visitor *v, const char *name, visit_type_OnOffAuto(v, name, &pcms->vmport, errp); } -bool pc_machine_is_smm_enabled(PCMachineState *pcms) -{ - bool smm_available = false; - - if (pcms->smm == ON_OFF_AUTO_OFF) { - return false; - } - - if (tcg_enabled() || qtest_enabled()) { - smm_available = true; - } else if (kvm_enabled()) { - smm_available = kvm_has_smm(); - } - - if (smm_available) { - return true; - } - - if (pcms->smm == ON_OFF_AUTO_ON) { - error_report("System Management Mode not supported by this hypervisor."); - exit(1); - } - return false; -} - -static void pc_machine_get_smm(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - PCMachineState *pcms = PC_MACHINE(obj); - OnOffAuto smm = pcms->smm; - - visit_type_OnOffAuto(v, name, &smm, errp); -} - -static void pc_machine_set_smm(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - PCMachineState *pcms = PC_MACHINE(obj); - - visit_type_OnOffAuto(v, name, &pcms->smm, errp); -} - static bool pc_machine_get_smbus(Object *obj, Error **errp) { PCMachineState *pcms = PC_MACHINE(obj); @@ -2122,7 +1856,6 @@ static void pc_machine_initfn(Object *obj) { PCMachineState *pcms = PC_MACHINE(obj); - pcms->smm = ON_OFF_AUTO_AUTO; #ifdef CONFIG_VMPORT pcms->vmport = ON_OFF_AUTO_AUTO; #else @@ -2151,7 +1884,7 @@ static void pc_machine_reset(MachineState *machine) cpu = X86_CPU(cs); if (cpu->apic_state) { - device_reset(cpu->apic_state); + device_legacy_reset(cpu->apic_state); } } } @@ -2224,17 +1957,12 @@ static void pc_machine_class_init(ObjectClass *oc, void *data) mc->default_cpu_type = TARGET_DEFAULT_CPU_TYPE; mc->nvdimm_supported = true; mc->numa_mem_supported = true; + mc->default_ram_id = "pc.ram"; object_class_property_add(oc, PC_MACHINE_DEVMEM_REGION_SIZE, "int", pc_machine_get_device_memory_region_size, NULL, NULL, NULL, &error_abort); - object_class_property_add(oc, PC_MACHINE_SMM, "OnOffAuto", - pc_machine_get_smm, pc_machine_set_smm, - NULL, NULL, &error_abort); - object_class_property_set_description(oc, PC_MACHINE_SMM, - "Enable SMM (pc & q35)", &error_abort); - object_class_property_add(oc, PC_MACHINE_VMPORT, "OnOffAuto", pc_machine_get_vmport, pc_machine_set_vmport, NULL, NULL, &error_abort); diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 1bd70d1abb..22dee0e76c 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -38,7 +38,7 @@ #include "hw/pci/pci_ids.h" #include "hw/usb.h" #include "net/net.h" -#include "hw/ide.h" +#include "hw/ide/pci.h" #include "hw/irq.h" #include "sysemu/kvm.h" #include "hw/kvm/clock.h" @@ -60,6 +60,7 @@ #include "migration/global_state.h" #include "migration/misc.h" #include "sysemu/numa.h" +#include "hw/mem/nvdimm.h" #define MAX_IDE_BUS 2 @@ -84,7 +85,6 @@ static void pc_init1(MachineState *machine, int piix3_devfn = -1; qemu_irq smi_irq; GSIState *gsi_state; - DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; BusState *idebus[MAX_IDE_BUS]; ISADevice *rtc_state; MemoryRegion *ram_memory; @@ -238,21 +238,22 @@ static void pc_init1(MachineState *machine, pc_nic_init(pcmc, isa_bus, pci_bus); - ide_drive_get(hd, ARRAY_SIZE(hd)); if (pcmc->pci_enabled) { PCIDevice *dev; - if (xen_enabled()) { - dev = pci_piix3_xen_ide_init(pci_bus, hd, piix3_devfn + 1); - } else { - dev = pci_piix3_ide_init(pci_bus, hd, piix3_devfn + 1); - } + + dev = pci_create_simple(pci_bus, piix3_devfn + 1, + xen_enabled() ? "piix3-ide-xen" : "piix3-ide"); + pci_ide_create_devs(dev); idebus[0] = qdev_get_child_bus(&dev->qdev, "ide.0"); idebus[1] = qdev_get_child_bus(&dev->qdev, "ide.1"); pc_cmos_init(pcms, idebus[0], idebus[1], rtc_state); } #ifdef CONFIG_IDE_ISA -else { + else { + DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; int i; + + ide_drive_get(hd, ARRAY_SIZE(hd)); for (i = 0; i < MAX_IDE_BUS; i++) { ISADevice *dev; char busname[] = "ide.0"; @@ -274,14 +275,14 @@ else { pci_create_simple(pci_bus, piix3_devfn + 2, "piix3-usb-uhci"); } - if (pcmc->pci_enabled && acpi_enabled) { + if (pcmc->pci_enabled && x86_machine_is_acpi_enabled(X86_MACHINE(pcms))) { DeviceState *piix4_pm; smi_irq = qemu_allocate_irq(pc_acpi_smi_interrupt, first_cpu, 0); /* TODO: Populate SPD eeprom data. */ pcms->smbus = piix4_pm_init(pci_bus, piix3_devfn + 3, 0xb100, x86ms->gsi[9], smi_irq, - pc_machine_is_smm_enabled(pcms), + x86_machine_is_smm_enabled(x86ms), &piix4_pm); smbus_eeprom_init(pcms->smbus, 8, NULL, 0); @@ -309,9 +310,9 @@ else { static void pc_compat_2_3_fn(MachineState *machine) { - PCMachineState *pcms = PC_MACHINE(machine); + X86MachineState *x86ms = X86_MACHINE(machine); if (kvm_enabled()) { - pcms->smm = ON_OFF_AUTO_OFF; + x86ms->smm = ON_OFF_AUTO_OFF; } } @@ -357,19 +358,13 @@ static void pc_compat_1_3(MachineState *machine) pc_compat_1_4_fn(machine); } -/* PC compat function for pc-0.14 to pc-1.2 */ +/* PC compat function for pc-1.0 to pc-1.2 */ static void pc_compat_1_2(MachineState *machine) { pc_compat_1_3(machine); x86_cpu_change_kvm_default("kvm-pv-eoi", NULL); } -/* PC compat function for pc-0.12 and pc-0.13 */ -static void pc_compat_0_13(MachineState *machine) -{ - pc_compat_1_2(machine); -} - static void pc_init_isa(MachineState *machine) { pc_init1(machine, TYPE_I440FX_PCI_HOST_BRIDGE, TYPE_I440FX_PCI_DEVICE); @@ -424,15 +419,27 @@ static void pc_i440fx_machine_options(MachineClass *m) machine_class_allow_dynamic_sysbus_dev(m, TYPE_RAMFB_DEVICE); } -static void pc_i440fx_4_2_machine_options(MachineClass *m) +static void pc_i440fx_5_0_machine_options(MachineClass *m) { PCMachineClass *pcmc = PC_MACHINE_CLASS(m); pc_i440fx_machine_options(m); m->alias = "pc"; - m->is_default = 1; + m->is_default = true; pcmc->default_cpu_version = 1; } +DEFINE_I440FX_MACHINE(v5_0, "pc-i440fx-5.0", NULL, + pc_i440fx_5_0_machine_options); + +static void pc_i440fx_4_2_machine_options(MachineClass *m) +{ + pc_i440fx_5_0_machine_options(m); + m->alias = NULL; + m->is_default = false; + compat_props_add(m->compat_props, hw_compat_4_2, hw_compat_4_2_len); + compat_props_add(m->compat_props, pc_compat_4_2, pc_compat_4_2_len); +} + DEFINE_I440FX_MACHINE(v4_2, "pc-i440fx-4.2", NULL, pc_i440fx_4_2_machine_options); @@ -440,7 +447,7 @@ static void pc_i440fx_4_1_machine_options(MachineClass *m) { pc_i440fx_4_2_machine_options(m); m->alias = NULL; - m->is_default = 0; + m->is_default = false; compat_props_add(m->compat_props, hw_compat_4_1, hw_compat_4_1_len); compat_props_add(m->compat_props, pc_compat_4_1, pc_compat_4_1_len); } @@ -453,7 +460,7 @@ static void pc_i440fx_4_0_machine_options(MachineClass *m) PCMachineClass *pcmc = PC_MACHINE_CLASS(m); pc_i440fx_4_1_machine_options(m); m->alias = NULL; - m->is_default = 0; + m->is_default = false; pcmc->default_cpu_version = CPU_VERSION_LEGACY; compat_props_add(m->compat_props, hw_compat_4_0, hw_compat_4_0_len); compat_props_add(m->compat_props, pc_compat_4_0, pc_compat_4_0_len); @@ -467,7 +474,7 @@ static void pc_i440fx_3_1_machine_options(MachineClass *m) PCMachineClass *pcmc = PC_MACHINE_CLASS(m); pc_i440fx_4_0_machine_options(m); - m->is_default = 0; + m->is_default = false; pcmc->do_not_add_smb_acpi = true; m->smbus_no_migration_support = true; m->alias = NULL; @@ -732,6 +739,7 @@ static void pc_i440fx_1_3_machine_options(MachineClass *m) pc_i440fx_1_4_machine_options(m); m->hw_version = "1.3.0"; + m->deprecation_reason = "use a newer machine type instead"; x86mc->compat_apic_id_mode = true; compat_props_add(m->compat_props, compat, G_N_ELEMENTS(compat)); } @@ -800,82 +808,6 @@ DEFINE_I440FX_MACHINE(v1_0, "pc-1.0", pc_compat_1_2, pc_i440fx_1_0_machine_options); -static void pc_i440fx_0_15_machine_options(MachineClass *m) -{ - static GlobalProperty compat[] = { - PC_CPU_MODEL_IDS("0.15") - }; - - pc_i440fx_1_0_machine_options(m); - m->hw_version = "0.15"; - m->deprecation_reason = "use a newer machine type instead"; - compat_props_add(m->compat_props, compat, G_N_ELEMENTS(compat)); -} - -DEFINE_I440FX_MACHINE(v0_15, "pc-0.15", pc_compat_1_2, - pc_i440fx_0_15_machine_options); - - -static void pc_i440fx_0_14_machine_options(MachineClass *m) -{ - static GlobalProperty compat[] = { - PC_CPU_MODEL_IDS("0.14") - { "virtio-blk-pci", "event_idx", "off" }, - { "virtio-serial-pci", "event_idx", "off" }, - { "virtio-net-pci", "event_idx", "off" }, - { "virtio-balloon-pci", "event_idx", "off" }, - { "qxl", "revision", "2" }, - { "qxl-vga", "revision", "2" }, - }; - - pc_i440fx_0_15_machine_options(m); - m->hw_version = "0.14"; - compat_props_add(m->compat_props, compat, G_N_ELEMENTS(compat)); -} - -DEFINE_I440FX_MACHINE(v0_14, "pc-0.14", pc_compat_1_2, - pc_i440fx_0_14_machine_options); - -static void pc_i440fx_0_13_machine_options(MachineClass *m) -{ - PCMachineClass *pcmc = PC_MACHINE_CLASS(m); - static GlobalProperty compat[] = { - PC_CPU_MODEL_IDS("0.13") - { TYPE_PCI_DEVICE, "command_serr_enable", "off" }, - { "AC97", "use_broken_id", "1" }, - { "virtio-9p-pci", "vectors", "0" }, - { "VGA", "rombar", "0" }, - { "vmware-svga", "rombar", "0" }, - }; - - pc_i440fx_0_14_machine_options(m); - m->hw_version = "0.13"; - compat_props_add(m->compat_props, compat, G_N_ELEMENTS(compat)); - pcmc->kvmclock_enabled = false; -} - -DEFINE_I440FX_MACHINE(v0_13, "pc-0.13", pc_compat_0_13, - pc_i440fx_0_13_machine_options); - -static void pc_i440fx_0_12_machine_options(MachineClass *m) -{ - static GlobalProperty compat[] = { - PC_CPU_MODEL_IDS("0.12") - { "virtio-serial-pci", "max_ports", "1" }, - { "virtio-serial-pci", "vectors", "0" }, - { "usb-mouse", "serial", "1" }, - { "usb-tablet", "serial", "1" }, - { "usb-kbd", "serial", "1" }, - }; - - pc_i440fx_0_13_machine_options(m); - m->hw_version = "0.12"; - compat_props_add(m->compat_props, compat, G_N_ELEMENTS(compat)); -} - -DEFINE_I440FX_MACHINE(v0_12, "pc-0.12", pc_compat_0_13, - pc_i440fx_0_12_machine_options); - typedef struct { uint16_t gpu_device_id; uint16_t pch_device_id; @@ -1016,13 +948,26 @@ DEFINE_PC_MACHINE(isapc, "isapc", pc_init_isa, #ifdef CONFIG_XEN -static void xenfv_machine_options(MachineClass *m) +static void xenfv_4_2_machine_options(MachineClass *m) { + pc_i440fx_4_2_machine_options(m); m->desc = "Xen Fully-virtualized PC"; m->max_cpus = HVM_MAX_VCPUS; m->default_machine_opts = "accel=xen"; } -DEFINE_PC_MACHINE(xenfv, "xenfv", pc_xen_hvm_init, - xenfv_machine_options); +DEFINE_PC_MACHINE(xenfv_4_2, "xenfv-4.2", pc_xen_hvm_init, + xenfv_4_2_machine_options); + +static void xenfv_3_1_machine_options(MachineClass *m) +{ + pc_i440fx_3_1_machine_options(m); + m->desc = "Xen Fully-virtualized PC"; + m->alias = "xenfv"; + m->max_cpus = HVM_MAX_VCPUS; + m->default_machine_opts = "accel=xen"; +} + +DEFINE_PC_MACHINE(xenfv, "xenfv-3.1", pc_xen_hvm_init, + xenfv_3_1_machine_options); #endif diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index 385e5cffb1..d37c425e22 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -53,6 +53,7 @@ #include "qapi/error.h" #include "qemu/error-report.h" #include "sysemu/numa.h" +#include "hw/mem/nvdimm.h" /* ICH9 AHCI has 6 ports */ #define MAX_SATA_PORTS 6 @@ -276,7 +277,7 @@ static void pc_q35_init(MachineState *machine) 0xff0104); /* connect pm stuff to lpc */ - ich9_lpc_pm_init(lpc, pc_machine_is_smm_enabled(pcms)); + ich9_lpc_pm_init(lpc, x86_machine_is_smm_enabled(x86ms)); if (pcms->sata_enabled) { /* ahci and SATA device, for q35 1 ahci controller is built-in */ @@ -348,7 +349,7 @@ static void pc_q35_machine_options(MachineClass *m) m->max_cpus = 288; } -static void pc_q35_4_2_machine_options(MachineClass *m) +static void pc_q35_5_0_machine_options(MachineClass *m) { PCMachineClass *pcmc = PC_MACHINE_CLASS(m); pc_q35_machine_options(m); @@ -356,6 +357,17 @@ static void pc_q35_4_2_machine_options(MachineClass *m) pcmc->default_cpu_version = 1; } +DEFINE_Q35_MACHINE(v5_0, "pc-q35-5.0", NULL, + pc_q35_5_0_machine_options); + +static void pc_q35_4_2_machine_options(MachineClass *m) +{ + pc_q35_5_0_machine_options(m); + m->alias = NULL; + compat_props_add(m->compat_props, hw_compat_4_2, hw_compat_4_2_len); + compat_props_add(m->compat_props, pc_compat_4_2, pc_compat_4_2_len); +} + DEFINE_Q35_MACHINE(v4_2, "pc-q35-4.2", NULL, pc_q35_4_2_machine_options); diff --git a/hw/i386/port92.c b/hw/i386/port92.c new file mode 100644 index 0000000000..19866c44ef --- /dev/null +++ b/hw/i386/port92.c @@ -0,0 +1,126 @@ +/* + * QEMU I/O port 0x92 (System Control Port A, to handle Fast Gate A20) + * + * Copyright (c) 2003-2004 Fabrice Bellard + * + * SPDX-License-Identifier: MIT + */ + +#include "qemu/osdep.h" +#include "sysemu/runstate.h" +#include "migration/vmstate.h" +#include "hw/irq.h" +#include "hw/i386/pc.h" +#include "trace.h" + +#define PORT92(obj) OBJECT_CHECK(Port92State, (obj), TYPE_PORT92) + +typedef struct Port92State { + ISADevice parent_obj; + + MemoryRegion io; + uint8_t outport; + qemu_irq a20_out; +} Port92State; + +static void port92_write(void *opaque, hwaddr addr, uint64_t val, + unsigned size) +{ + Port92State *s = opaque; + int oldval = s->outport; + + trace_port92_write(val); + s->outport = val; + qemu_set_irq(s->a20_out, (val >> 1) & 1); + if ((val & 1) && !(oldval & 1)) { + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); + } +} + +static uint64_t port92_read(void *opaque, hwaddr addr, + unsigned size) +{ + Port92State *s = opaque; + uint32_t ret; + + ret = s->outport; + trace_port92_read(ret); + + return ret; +} + +static const VMStateDescription vmstate_port92_isa = { + .name = "port92", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT8(outport, Port92State), + VMSTATE_END_OF_LIST() + } +}; + +static void port92_reset(DeviceState *d) +{ + Port92State *s = PORT92(d); + + s->outport &= ~1; +} + +static const MemoryRegionOps port92_ops = { + .read = port92_read, + .write = port92_write, + .impl = { + .min_access_size = 1, + .max_access_size = 1, + }, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void port92_initfn(Object *obj) +{ + Port92State *s = PORT92(obj); + + memory_region_init_io(&s->io, OBJECT(s), &port92_ops, s, "port92", 1); + + s->outport = 0; + + qdev_init_gpio_out_named(DEVICE(obj), &s->a20_out, PORT92_A20_LINE, 1); +} + +static void port92_realizefn(DeviceState *dev, Error **errp) +{ + ISADevice *isadev = ISA_DEVICE(dev); + Port92State *s = PORT92(dev); + + isa_register_ioport(isadev, &s->io, 0x92); +} + +static void port92_class_initfn(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = port92_realizefn; + dc->reset = port92_reset; + dc->vmsd = &vmstate_port92_isa; + /* + * Reason: unlike ordinary ISA devices, this one needs additional + * wiring: its A20 output line needs to be wired up with + * qdev_connect_gpio_out_named(). + */ + dc->user_creatable = false; +} + +static const TypeInfo port92_info = { + .name = TYPE_PORT92, + .parent = TYPE_ISA_DEVICE, + .instance_size = sizeof(Port92State), + .instance_init = port92_initfn, + .class_init = port92_class_initfn, +}; + +static void port92_register_types(void) +{ + type_register_static(&port92_info); +} + +type_init(port92_register_types) diff --git a/hw/i386/trace-events b/hw/i386/trace-events index c8bc464bc5..e48bef2b0d 100644 --- a/hw/i386/trace-events +++ b/hw/i386/trace-events @@ -111,3 +111,11 @@ amdvi_ir_irte_ga_val(uint64_t hi, uint64_t lo) "hi 0x%"PRIx64" lo 0x%"PRIx64 # vmport.c vmport_register(unsigned char command, void *func, void *opaque) "command: 0x%02x func: %p opaque: %p" vmport_command(unsigned char command) "command: 0x%02x" + +# x86.c +x86_gsi_interrupt(int irqn, int level) "GSI interrupt #%d level:%d" +x86_pic_interrupt(int irqn, int level) "PIC interrupt #%d level:%d" + +# port92.c +port92_read(uint8_t val) "port92: read 0x%02x" +port92_write(uint8_t val) "port92: write 0x%02x" diff --git a/hw/i386/vmmouse.c b/hw/i386/vmmouse.c index 41ad91ad53..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" @@ -66,7 +67,7 @@ typedef struct VMMouseState uint16_t status; uint8_t absolute; QEMUPutMouseEntry *entry; - void *ps2_mouse; + ISAKBDState *i8042; } VMMouseState; static uint32_t vmmouse_get_status(VMMouseState *s) @@ -105,7 +106,7 @@ static void vmmouse_mouse_event(void *opaque, int x, int y, int dz, int buttons_ /* need to still generate PS2 events to notify driver to read from queue */ - i8042_isa_mouse_fake_event(s->ps2_mouse); + i8042_isa_mouse_fake_event(s->i8042); } static void vmmouse_remove_handler(VMMouseState *s) @@ -269,13 +270,18 @@ 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); } static Property vmmouse_properties[] = { - DEFINE_PROP_PTR("ps2_mouse", VMMouseState, ps2_mouse), + DEFINE_PROP_LINK("i8042", VMMouseState, i8042, TYPE_I8042, ISAKBDState *), DEFINE_PROP_END_OF_LIST(), }; @@ -286,9 +292,7 @@ static void vmmouse_class_initfn(ObjectClass *klass, void *data) dc->realize = vmmouse_realizefn; dc->reset = vmmouse_reset; dc->vmsd = &vmstate_vmmouse; - dc->props = vmmouse_properties; - /* Reason: pointer property "ps2_mouse" */ - dc->user_creatable = false; + device_class_set_props(dc, vmmouse_properties); } static const TypeInfo vmmouse_info = { diff --git a/hw/i386/x86-iommu-stub.c b/hw/i386/x86-iommu-stub.c new file mode 100644 index 0000000000..c5ba077f9d --- /dev/null +++ b/hw/i386/x86-iommu-stub.c @@ -0,0 +1,43 @@ +/* + * Stubs for X86 IOMMU emulation + * + * Copyright (C) 2019 Red Hat, Inc. + * + * Author: Paolo Bonzini + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + */ + +#include "qemu/osdep.h" +#include "hw/i386/x86-iommu.h" + +void x86_iommu_iec_register_notifier(X86IOMMUState *iommu, + iec_notify_fn fn, void *data) +{ +} + +X86IOMMUState *x86_iommu_get_default(void) +{ + return NULL; +} + +bool x86_iommu_ir_supported(X86IOMMUState *s) +{ + return false; +} + +IommuType x86_iommu_get_type(void) +{ + abort(); +} diff --git a/hw/i386/x86-iommu.c b/hw/i386/x86-iommu.c index 1eaf2dd1bc..4104060e68 100644 --- a/hw/i386/x86-iommu.c +++ b/hw/i386/x86-iommu.c @@ -153,7 +153,7 @@ static void x86_iommu_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = x86_iommu_realize; - dc->props = x86_iommu_properties; + device_class_set_props(dc, x86_iommu_properties); } bool x86_iommu_ir_supported(X86IOMMUState *s) diff --git a/hw/i386/x86.c b/hw/i386/x86.c index 394edc2f72..445374df8f 100644 --- a/hw/i386/x86.c +++ b/hw/i386/x86.c @@ -34,24 +34,56 @@ #include "sysemu/numa.h" #include "sysemu/replay.h" #include "sysemu/sysemu.h" +#include "trace.h" #include "hw/i386/x86.h" #include "target/i386/cpu.h" #include "hw/i386/topology.h" #include "hw/i386/fw_cfg.h" +#include "hw/intc/i8259.h" #include "hw/acpi/cpu_hotplug.h" +#include "hw/irq.h" #include "hw/nmi.h" #include "hw/loader.h" #include "multiboot.h" #include "elf.h" #include "standard-headers/asm-x86/bootparam.h" +#include "config-devices.h" +#include "kvm_i386.h" #define BIOS_FILENAME "bios.bin" /* Physical Address of PVH entry point read from kernel ELF NOTE */ static size_t pvh_start_addr; +inline void init_topo_info(X86CPUTopoInfo *topo_info, + const X86MachineState *x86ms) +{ + MachineState *ms = MACHINE(x86ms); + + topo_info->nodes_per_pkg = ms->numa_state->num_nodes / ms->smp.sockets; + topo_info->dies_per_pkg = x86ms->smp_dies; + topo_info->cores_per_die = ms->smp.cores; + topo_info->threads_per_core = ms->smp.threads; +} + +/* + * Set up with the new EPYC topology handlers + * + * AMD uses different apic id encoding for EPYC based cpus. Override + * the default topo handlers with EPYC encoding handlers. + */ +static void x86_set_epyc_topo_handlers(MachineState *machine) +{ + X86MachineState *x86ms = X86_MACHINE(machine); + + x86ms->apicid_from_cpu_idx = x86_apicid_from_cpu_idx_epyc; + x86ms->topo_ids_from_apicid = x86_topo_ids_from_apicid_epyc; + x86ms->apicid_from_topo_ids = x86_apicid_from_topo_ids_epyc; + x86ms->apicid_pkg_offset = apicid_pkg_offset_epyc; +} + /* * Calculates initial APIC ID for a specific CPU index * @@ -63,13 +95,14 @@ static size_t pvh_start_addr; uint32_t x86_cpu_apic_id_from_index(X86MachineState *x86ms, unsigned int cpu_index) { - MachineState *ms = MACHINE(x86ms); X86MachineClass *x86mc = X86_MACHINE_GET_CLASS(x86ms); + X86CPUTopoInfo topo_info; uint32_t correct_id; static bool warned; - correct_id = x86_apicid_from_cpu_idx(x86ms->smp_dies, ms->smp.cores, - ms->smp.threads, cpu_index); + init_topo_info(&topo_info, x86ms); + + correct_id = x86ms->apicid_from_cpu_idx(&topo_info, cpu_index); if (x86mc->compat_apic_id_mode) { if (cpu_index != correct_id && !warned && !qtest_enabled()) { error_report("APIC IDs set in compatibility mode, " @@ -87,13 +120,9 @@ void x86_cpu_new(X86MachineState *x86ms, int64_t apic_id, Error **errp) { Object *cpu = NULL; Error *local_err = NULL; - CPUX86State *env = NULL; cpu = object_new(MACHINE(x86ms)->cpu_type); - env = &X86_CPU(cpu)->env; - env->nr_dies = x86ms->smp_dies; - object_property_set_uint(cpu, apic_id, "apic-id", &local_err); object_property_set_bool(cpu, true, "realized", &local_err); @@ -108,6 +137,11 @@ void x86_cpus_init(X86MachineState *x86ms, int default_cpu_version) MachineState *ms = MACHINE(x86ms); MachineClass *mc = MACHINE_GET_CLASS(x86ms); + /* Check for apicid encoding */ + if (cpu_x86_use_epyc_apic_id_encoding(ms->cpu_type)) { + x86_set_epyc_topo_handlers(ms); + } + x86_cpu_set_default_version(default_cpu_version); /* @@ -121,6 +155,12 @@ void x86_cpus_init(X86MachineState *x86ms, int default_cpu_version) x86ms->apic_id_limit = x86_cpu_apic_id_from_index(x86ms, ms->smp.max_cpus - 1) + 1; possible_cpus = mc->possible_cpu_arch_ids(ms); + + for (i = 0; i < ms->possible_cpus->len; i++) { + ms->possible_cpus->cpus[i].arch_id = + x86_cpu_apic_id_from_index(x86ms, i); + } + for (i = 0; i < ms->smp.cpus; i++) { x86_cpu_new(x86ms, possible_cpus->cpus[i].arch_id, &error_fatal); } @@ -138,21 +178,23 @@ x86_cpu_index_to_props(MachineState *ms, unsigned cpu_index) int64_t x86_get_default_cpu_node_id(const MachineState *ms, int idx) { - X86CPUTopoInfo topo; + X86CPUTopoIDs topo_ids; X86MachineState *x86ms = X86_MACHINE(ms); + X86CPUTopoInfo topo_info; + + init_topo_info(&topo_info, x86ms); assert(idx < ms->possible_cpus->len); - x86_topo_ids_from_apicid(ms->possible_cpus->cpus[idx].arch_id, - x86ms->smp_dies, ms->smp.cores, - ms->smp.threads, &topo); - return topo.pkg_id % ms->numa_state->num_nodes; + x86_topo_ids_from_idx(&topo_info, idx, &topo_ids); + return topo_ids.pkg_id % ms->numa_state->num_nodes; } const CPUArchIdList *x86_possible_cpu_arch_ids(MachineState *ms) { X86MachineState *x86ms = X86_MACHINE(ms); - int i; unsigned int max_cpus = ms->smp.max_cpus; + X86CPUTopoInfo topo_info; + int i; if (ms->possible_cpus) { /* @@ -166,26 +208,25 @@ const CPUArchIdList *x86_possible_cpu_arch_ids(MachineState *ms) ms->possible_cpus = g_malloc0(sizeof(CPUArchIdList) + sizeof(CPUArchId) * max_cpus); ms->possible_cpus->len = max_cpus; + + init_topo_info(&topo_info, x86ms); + for (i = 0; i < ms->possible_cpus->len; i++) { - X86CPUTopoInfo topo; + X86CPUTopoIDs topo_ids; ms->possible_cpus->cpus[i].type = ms->cpu_type; ms->possible_cpus->cpus[i].vcpus_count = 1; - ms->possible_cpus->cpus[i].arch_id = - x86_cpu_apic_id_from_index(x86ms, i); - x86_topo_ids_from_apicid(ms->possible_cpus->cpus[i].arch_id, - x86ms->smp_dies, ms->smp.cores, - ms->smp.threads, &topo); + x86_topo_ids_from_idx(&topo_info, i, &topo_ids); ms->possible_cpus->cpus[i].props.has_socket_id = true; - ms->possible_cpus->cpus[i].props.socket_id = topo.pkg_id; + ms->possible_cpus->cpus[i].props.socket_id = topo_ids.pkg_id; if (x86ms->smp_dies > 1) { ms->possible_cpus->cpus[i].props.has_die_id = true; - ms->possible_cpus->cpus[i].props.die_id = topo.die_id; + ms->possible_cpus->cpus[i].props.die_id = topo_ids.die_id; } ms->possible_cpus->cpus[i].props.has_core_id = true; - ms->possible_cpus->cpus[i].props.core_id = topo.core_id; + ms->possible_cpus->cpus[i].props.core_id = topo_ids.core_id; ms->possible_cpus->cpus[i].props.has_thread_id = true; - ms->possible_cpus->cpus[i].props.thread_id = topo.smt_id; + ms->possible_cpus->cpus[i].props.thread_id = topo_ids.smt_id; } return ms->possible_cpus; } @@ -220,11 +261,115 @@ static long get_file_size(FILE *f) return size; } +/* TSC handling */ +uint64_t cpu_get_tsc(CPUX86State *env) +{ +#ifdef XBOX + return muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), 733333333, + NANOSECONDS_PER_SECOND); +#else + return cpu_get_ticks(); +#endif +} + +/* IRQ handling */ +static void pic_irq_request(void *opaque, int irq, int level) +{ + CPUState *cs = first_cpu; + X86CPU *cpu = X86_CPU(cs); + + trace_x86_pic_interrupt(irq, level); + if (cpu->apic_state && !kvm_irqchip_in_kernel()) { + CPU_FOREACH(cs) { + cpu = X86_CPU(cs); + if (apic_accept_pic_intr(cpu->apic_state)) { + apic_deliver_pic_intr(cpu->apic_state, level); + } + } + } else { + if (level) { + cpu_interrupt(cs, CPU_INTERRUPT_HARD); + } else { + cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); + } + } +} + +qemu_irq x86_allocate_cpu_irq(void) +{ + return qemu_allocate_irq(pic_irq_request, NULL, 0); +} + +int cpu_get_pic_interrupt(CPUX86State *env) +{ + X86CPU *cpu = env_archcpu(env); + int intno; + + if (!kvm_irqchip_in_kernel()) { + intno = apic_get_interrupt(cpu->apic_state); + if (intno >= 0) { + return intno; + } + /* read the irq from the PIC */ + if (!apic_accept_pic_intr(cpu->apic_state)) { + return -1; + } + } + + intno = pic_read_irq(isa_pic); + return intno; +} + +DeviceState *cpu_get_current_apic(void) +{ + if (current_cpu) { + X86CPU *cpu = X86_CPU(current_cpu); + return cpu->apic_state; + } else { + return NULL; + } +} + +void gsi_handler(void *opaque, int n, int level) +{ + GSIState *s = opaque; + + trace_x86_gsi_interrupt(n, level); + if (n < ISA_NUM_IRQS) { + /* Under KVM, Kernel will forward to both PIC and IOAPIC */ + qemu_set_irq(s->i8259_irq[n], level); + } + qemu_set_irq(s->ioapic_irq[n], level); +} + +void ioapic_init_gsi(GSIState *gsi_state, const char *parent_name) +{ + DeviceState *dev; + SysBusDevice *d; + unsigned int i; + + assert(parent_name); + if (kvm_ioapic_in_kernel()) { + dev = qdev_create(NULL, TYPE_KVM_IOAPIC); + } else { + dev = qdev_create(NULL, TYPE_IOAPIC); + } + object_property_add_child(object_resolve_path(parent_name, NULL), + "ioapic", OBJECT(dev), NULL); + qdev_init_nofail(dev); + d = SYS_BUS_DEVICE(dev); + sysbus_mmio_map(d, 0, IO_APIC_DEFAULT_ADDRESS); + + for (i = 0; i < IOAPIC_NUM_PINS; i++) { + gsi_state->ioapic_irq[i] = qdev_get_gpio_in(dev, i); + } +} + struct setup_data { uint64_t next; uint32_t type; uint32_t len; - uint8_t data[0]; + uint8_t data[]; } __attribute__((packed)); @@ -309,7 +454,7 @@ static bool load_elfboot(const char *kernel_filename, uint64_t elf_note_type = XEN_ELFNOTE_PHYS32_ENTRY; kernel_size = load_elf(kernel_filename, read_pvh_start_addr, NULL, &elf_note_type, &elf_entry, - &elf_low, &elf_high, 0, I386_ELF_MACHINE, + &elf_low, &elf_high, NULL, 0, I386_ELF_MACHINE, 0, 0); if (kernel_size < 0) { @@ -508,6 +653,7 @@ void x86_load_linux(X86MachineState *x86ms, vmode = strstr(kernel_cmdline, "vga="); if (vmode) { unsigned int video_mode; + const char *end; int ret; /* skip "vga=" */ vmode += 4; @@ -518,10 +664,9 @@ void x86_load_linux(X86MachineState *x86ms, } else if (!strncmp(vmode, "ask", 3)) { video_mode = 0xfffd; } else { - ret = qemu_strtoui(vmode, NULL, 0, &video_mode); - if (ret != 0) { - fprintf(stderr, "qemu: can't parse 'vga' parameter: %s\n", - strerror(-ret)); + ret = qemu_strtoui(vmode, &end, 0, &video_mode); + if (ret != 0 || (*end && *end != ' ')) { + fprintf(stderr, "qemu: invalid 'vga=' kernel parameter.\n"); exit(1); } } @@ -745,12 +890,86 @@ static void x86_machine_set_max_ram_below_4g(Object *obj, Visitor *v, x86ms->max_ram_below_4g = value; } +bool x86_machine_is_smm_enabled(X86MachineState *x86ms) +{ + bool smm_available = false; + + if (x86ms->smm == ON_OFF_AUTO_OFF) { + return false; + } + + if (tcg_enabled() || qtest_enabled()) { + smm_available = true; + } else if (kvm_enabled()) { + smm_available = kvm_has_smm(); + } + + if (smm_available) { + return true; + } + + if (x86ms->smm == ON_OFF_AUTO_ON) { + error_report("System Management Mode not supported by this hypervisor."); + exit(1); + } + return false; +} + +static void x86_machine_get_smm(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + X86MachineState *x86ms = X86_MACHINE(obj); + OnOffAuto smm = x86ms->smm; + + visit_type_OnOffAuto(v, name, &smm, errp); +} + +static void x86_machine_set_smm(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + X86MachineState *x86ms = X86_MACHINE(obj); + + visit_type_OnOffAuto(v, name, &x86ms->smm, errp); +} + +bool x86_machine_is_acpi_enabled(X86MachineState *x86ms) +{ + if (x86ms->acpi == ON_OFF_AUTO_OFF) { + return false; + } + return true; +} + +static void x86_machine_get_acpi(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + X86MachineState *x86ms = X86_MACHINE(obj); + OnOffAuto acpi = x86ms->acpi; + + visit_type_OnOffAuto(v, name, &acpi, errp); +} + +static void x86_machine_set_acpi(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + X86MachineState *x86ms = X86_MACHINE(obj); + + visit_type_OnOffAuto(v, name, &x86ms->acpi, errp); +} + static void x86_machine_initfn(Object *obj) { X86MachineState *x86ms = X86_MACHINE(obj); + x86ms->smm = ON_OFF_AUTO_AUTO; + x86ms->acpi = ON_OFF_AUTO_AUTO; x86ms->max_ram_below_4g = 0; /* use default */ x86ms->smp_dies = 1; + + x86ms->apicid_from_cpu_idx = x86_apicid_from_cpu_idx; + x86ms->topo_ids_from_apicid = x86_topo_ids_from_apicid; + x86ms->apicid_from_topo_ids = x86_apicid_from_topo_ids; + x86ms->apicid_pkg_offset = apicid_pkg_offset; } static void x86_machine_class_init(ObjectClass *oc, void *data) @@ -769,9 +988,20 @@ static void x86_machine_class_init(ObjectClass *oc, void *data) object_class_property_add(oc, X86_MACHINE_MAX_RAM_BELOW_4G, "size", x86_machine_get_max_ram_below_4g, x86_machine_set_max_ram_below_4g, NULL, NULL, &error_abort); - object_class_property_set_description(oc, X86_MACHINE_MAX_RAM_BELOW_4G, "Maximum ram below the 4G boundary (32bit boundary)", &error_abort); + + object_class_property_add(oc, X86_MACHINE_SMM, "OnOffAuto", + x86_machine_get_smm, x86_machine_set_smm, + NULL, NULL, &error_abort); + object_class_property_set_description(oc, X86_MACHINE_SMM, + "Enable SMM", &error_abort); + + object_class_property_add(oc, X86_MACHINE_ACPI, "OnOffAuto", + x86_machine_get_acpi, x86_machine_set_acpi, + NULL, NULL, &error_abort); + object_class_property_set_description(oc, X86_MACHINE_ACPI, + "Enable ACPI", &error_abort); } static const TypeInfo x86_machine_info = { diff --git a/hw/i386/xen/xen_pvdevice.c b/hw/i386/xen/xen_pvdevice.c index 27f646da06..ee2610c7a0 100644 --- a/hw/i386/xen/xen_pvdevice.c +++ b/hw/i386/xen/xen_pvdevice.c @@ -131,7 +131,7 @@ static void xen_pv_class_init(ObjectClass *klass, void *data) k->realize = xen_pv_realize; k->class_id = PCI_CLASS_SYSTEM_OTHER; dc->desc = "Xen PV Device"; - dc->props = xen_pv_props; + device_class_set_props(dc, xen_pv_props); dc->vmsd = &vmstate_xen_pvdevice; } diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index d45393c019..13d91e109a 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -1461,7 +1461,7 @@ static void ahci_commit_buf(IDEDMA *dma, uint32_t tx_bytes) ad->cur_cmd->status = cpu_to_le32(tx_bytes); } -static int ahci_dma_rw_buf(IDEDMA *dma, int is_write) +static int ahci_dma_rw_buf(IDEDMA *dma, bool is_write) { AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma); IDEState *s = &ad->port.ifs[0]; @@ -1795,7 +1795,7 @@ static void sysbus_ahci_class_init(ObjectClass *klass, void *data) dc->realize = sysbus_ahci_realize; dc->vmsd = &vmstate_sysbus_ahci; - dc->props = sysbus_ahci_properties; + device_class_set_props(dc, sysbus_ahci_properties); dc->reset = sysbus_ahci_reset; set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); } diff --git a/hw/ide/ahci_internal.h b/hw/ide/ahci_internal.h index 73424516da..bab0459774 100644 --- a/hw/ide/ahci_internal.h +++ b/hw/ide/ahci_internal.h @@ -27,6 +27,7 @@ #include "hw/ide/ahci.h" #include "hw/ide/internal.h" #include "hw/sysbus.h" +#include "hw/pci/pci.h" #define AHCI_MEM_BAR_SIZE 0x1000 #define AHCI_MAX_PORTS 32 diff --git a/hw/ide/cmd646.c b/hw/ide/cmd646.c index 19984d2af9..c254631485 100644 --- a/hw/ide/cmd646.c +++ b/hw/ide/cmd646.c @@ -207,9 +207,9 @@ static void cmd646_set_irq(void *opaque, int channel, int level) cmd646_update_irq(pd); } -static void cmd646_reset(void *opaque) +static void cmd646_reset(DeviceState *dev) { - PCIIDEState *d = opaque; + PCIIDEState *d = PCI_IDE(dev); unsigned int i; for (i = 0; i < 2; i++) { @@ -249,8 +249,8 @@ static void cmd646_pci_config_write(PCIDevice *d, uint32_t addr, uint32_t val, static void pci_cmd646_ide_realize(PCIDevice *dev, Error **errp) { PCIIDEState *d = PCI_IDE(dev); + DeviceState *ds = DEVICE(dev); uint8_t *pci_conf = dev->config; - qemu_irq *irq; int i; pci_conf[PCI_CLASS_PROG] = 0x8f; @@ -291,19 +291,15 @@ static void pci_cmd646_ide_realize(PCIDevice *dev, Error **errp) /* TODO: RST# value should be 0 */ pci_conf[PCI_INTERRUPT_PIN] = 0x01; // interrupt on pin 1 - irq = qemu_allocate_irqs(cmd646_set_irq, d, 2); + qdev_init_gpio_in(ds, cmd646_set_irq, 2); for (i = 0; i < 2; i++) { - ide_bus_new(&d->bus[i], sizeof(d->bus[i]), DEVICE(dev), i, 2); - ide_init2(&d->bus[i], irq[i]); + ide_bus_new(&d->bus[i], sizeof(d->bus[i]), ds, i, 2); + ide_init2(&d->bus[i], qdev_get_gpio_in(ds, i)); bmdma_init(&d->bus[i], &d->bmdma[i], d); d->bmdma[i].bus = &d->bus[i]; ide_register_restart_cb(&d->bus[i]); } - g_free(irq); - - vmstate_register(DEVICE(dev), 0, &vmstate_ide_pci, d); - qemu_register_reset(cmd646_reset, d); } static void pci_cmd646_ide_exitfn(PCIDevice *dev) @@ -317,18 +313,6 @@ static void pci_cmd646_ide_exitfn(PCIDevice *dev) } } -void pci_cmd646_ide_init(PCIBus *bus, DriveInfo **hd_table, - int secondary_ide_enabled) -{ - PCIDevice *dev; - - dev = pci_create(bus, -1, "cmd646-ide"); - qdev_prop_set_uint32(&dev->qdev, "secondary", secondary_ide_enabled); - qdev_init_nofail(&dev->qdev); - - pci_ide_create_devs(dev, hd_table); -} - static Property cmd646_ide_properties[] = { DEFINE_PROP_UINT32("secondary", PCIIDEState, secondary, 0), DEFINE_PROP_END_OF_LIST(), @@ -339,6 +323,8 @@ static void cmd646_ide_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + dc->reset = cmd646_reset; + dc->vmsd = &vmstate_ide_pci; k->realize = pci_cmd646_ide_realize; k->exit = pci_cmd646_ide_exitfn; k->vendor_id = PCI_VENDOR_ID_CMD; @@ -347,7 +333,7 @@ static void cmd646_ide_class_init(ObjectClass *klass, void *data) k->class_id = PCI_CLASS_STORAGE_IDE; k->config_read = cmd646_pci_config_read; k->config_write = cmd646_pci_config_write; - dc->props = cmd646_ide_properties; + device_class_set_props(dc, cmd646_ide_properties); set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); } diff --git a/hw/ide/core.c b/hw/ide/core.c index 8181752810..39ab6fbcf3 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -857,6 +857,7 @@ static void ide_dma_cb(void *opaque, int ret) int64_t sector_num; uint64_t offset; bool stay_active = false; + int32_t prep_size = 0; if (ret == -EINVAL) { ide_dma_error(s); @@ -871,13 +872,15 @@ static void ide_dma_cb(void *opaque, int ret) } } - n = s->io_buffer_size >> 9; - if (n > s->nsector) { - /* The PRDs were longer than needed for this request. Shorten them so - * we don't get a negative remainder. The Active bit must remain set - * after the request completes. */ + if (s->io_buffer_size > s->nsector * 512) { + /* + * The PRDs were longer than needed for this request. + * The Active bit must remain set after the request completes. + */ n = s->nsector; stay_active = true; + } else { + n = s->io_buffer_size >> 9; } sector_num = ide_get_sector(s); @@ -900,9 +903,20 @@ static void ide_dma_cb(void *opaque, int ret) n = s->nsector; s->io_buffer_index = 0; s->io_buffer_size = n * 512; - if (s->bus->dma->ops->prepare_buf(s->bus->dma, s->io_buffer_size) < 512) { - /* The PRDs were too short. Reset the Active bit, but don't raise an - * interrupt. */ + prep_size = s->bus->dma->ops->prepare_buf(s->bus->dma, s->io_buffer_size); + /* prepare_buf() must succeed and respect the limit */ + assert(prep_size >= 0 && prep_size <= n * 512); + + /* + * Now prep_size stores the number of bytes in the sglist, and + * s->io_buffer_size stores the number of bytes described by the PRDs. + */ + + if (prep_size < n * 512) { + /* + * The PRDs are too short for this request. Error condition! + * Reset the Active bit and don't raise the interrupt. + */ s->status = READY_STAT | SEEK_STAT; dma_buf_commit(s, 0); goto eot; @@ -2586,7 +2600,7 @@ static void ide_init1(IDEBus *bus, int unit) ide_sector_write_timer_cb, s); } -static int ide_nop_int(IDEDMA *dma, int x) +static int ide_nop_int(IDEDMA *dma, bool is_write) { return 0; } diff --git a/hw/ide/isa.c b/hw/ide/isa.c index 7b6e283679..8395807b08 100644 --- a/hw/ide/isa.c +++ b/hw/ide/isa.c @@ -75,7 +75,7 @@ static void isa_ide_realizefn(DeviceState *dev, Error **errp) ide_init_ioport(&s->bus, isadev, s->iobase, s->iobase2); isa_init_irq(isadev, &s->irq, s->isairq); ide_init2(&s->bus, s->irq); - vmstate_register(dev, 0, &vmstate_ide_isa, s); + vmstate_register(VMSTATE_IF(dev), 0, &vmstate_ide_isa, s); ide_register_restart_cb(&s->bus); } @@ -117,7 +117,7 @@ static void isa_ide_class_initfn(ObjectClass *klass, void *data) dc->realize = isa_ide_realizefn; dc->fw_name = "ide"; dc->reset = isa_ide_reset; - dc->props = isa_ide_properties; + device_class_set_props(dc, isa_ide_properties); set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); } diff --git a/hw/ide/macio.c b/hw/ide/macio.c index 79f787c539..a9f25e5d02 100644 --- a/hw/ide/macio.c +++ b/hw/ide/macio.c @@ -376,7 +376,7 @@ static void macio_ide_reset(DeviceState *dev) ide_bus_reset(&d->bus); } -static int ide_nop_int(IDEDMA *dma, int x) +static int ide_nop_int(IDEDMA *dma, bool is_write) { return 0; } @@ -474,7 +474,7 @@ static void macio_ide_class_init(ObjectClass *oc, void *data) dc->realize = macio_ide_realizefn; dc->reset = macio_ide_reset; - dc->props = macio_ide_properties; + device_class_set_props(dc, macio_ide_properties); dc->vmsd = &vmstate_pmac; set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); } diff --git a/hw/ide/microdrive.c b/hw/ide/microdrive.c index b0272ea14b..6b30e36ed8 100644 --- a/hw/ide/microdrive.c +++ b/hw/ide/microdrive.c @@ -173,7 +173,7 @@ static void md_attr_write(PCMCIACardState *card, uint32_t at, uint8_t value) case 0x00: /* Configuration Option Register */ s->opt = value & 0xcf; if (value & OPT_SRESET) { - device_reset(DEVICE(s)); + device_legacy_reset(DEVICE(s)); } md_interrupt_update(s); break; @@ -316,7 +316,7 @@ static void md_common_write(PCMCIACardState *card, uint32_t at, uint16_t value) case 0xe: /* Device Control */ s->ctrl = value; if (value & CTRL_SRST) { - device_reset(DEVICE(s)); + device_legacy_reset(DEVICE(s)); } md_interrupt_update(s); break; @@ -541,7 +541,7 @@ static int dscm1xxxx_attach(PCMCIACardState *card) md->attr_base = pcc->cis[0x74] | (pcc->cis[0x76] << 8); md->io_base = 0x0; - device_reset(DEVICE(md)); + device_legacy_reset(DEVICE(md)); md_interrupt_update(md); return 0; @@ -551,7 +551,7 @@ static int dscm1xxxx_detach(PCMCIACardState *card) { MicroDriveState *md = MICRODRIVE(card); - device_reset(DEVICE(md)); + device_legacy_reset(DEVICE(md)); return 0; } diff --git a/hw/ide/mmio.c b/hw/ide/mmio.c index 7149a9cba6..d233bd8c01 100644 --- a/hw/ide/mmio.c +++ b/hw/ide/mmio.c @@ -154,7 +154,7 @@ static void mmio_ide_class_init(ObjectClass *oc, void *data) dc->realize = mmio_ide_realizefn; dc->reset = mmio_ide_reset; - dc->props = mmio_ide_properties; + device_class_set_props(dc, mmio_ide_properties); dc->vmsd = &vmstate_ide_mmio; } diff --git a/hw/ide/pci.c b/hw/ide/pci.c index cce1da804d..97347f07f1 100644 --- a/hw/ide/pci.c +++ b/hw/ide/pci.c @@ -181,7 +181,7 @@ static int32_t bmdma_prepare_buf(IDEDMA *dma, int32_t limit) } /* return 0 if buffer completed */ -static int bmdma_rw_buf(IDEDMA *dma, int is_write) +static int bmdma_rw_buf(IDEDMA *dma, bool is_write) { BMDMAState *bm = DO_UPCAST(BMDMAState, dma, dma); IDEState *s = bmdma_active_if(bm); @@ -476,17 +476,20 @@ const VMStateDescription vmstate_ide_pci = { } }; -void pci_ide_create_devs(PCIDevice *dev, DriveInfo **hd_table) +/* hd_table must contain 4 block drivers */ +void pci_ide_create_devs(PCIDevice *dev) { PCIIDEState *d = PCI_IDE(dev); + DriveInfo *hd_table[2 * MAX_IDE_DEVS]; static const int bus[4] = { 0, 0, 1, 1 }; static const int unit[4] = { 0, 1, 0, 1 }; int i; + ide_drive_get(hd_table, ARRAY_SIZE(hd_table)); for (i = 0; i < 4; i++) { - if (hd_table[i] == NULL) - continue; - ide_create_drive(d->bus+bus[i], unit[i], hd_table[i]); + if (hd_table[i]) { + ide_create_drive(d->bus + bus[i], unit[i], hd_table[i]); + } } } diff --git a/hw/ide/piix.c b/hw/ide/piix.c index db313dd3b1..3b2de4c312 100644 --- a/hw/ide/piix.c +++ b/hw/ide/piix.c @@ -156,7 +156,7 @@ static void pci_piix_ide_realize(PCIDevice *dev, Error **errp) bmdma_setup_bar(d); pci_register_bar(dev, 4, PCI_BASE_ADDRESS_SPACE_IO, &d->bmdma_bar); - vmstate_register(DEVICE(dev), 0, &vmstate_ide_pci, d); + vmstate_register(VMSTATE_IF(dev), 0, &vmstate_ide_pci, d); pci_piix_init_ports(d); } @@ -197,15 +197,6 @@ int pci_piix3_xen_ide_unplug(DeviceState *dev, bool aux) return 0; } -PCIDevice *pci_piix3_xen_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn) -{ - PCIDevice *dev; - - dev = pci_create_simple(bus, devfn, "piix3-ide-xen"); - pci_ide_create_devs(dev, hd_table); - return dev; -} - static void pci_piix_ide_exitfn(PCIDevice *dev) { PCIIDEState *d = PCI_IDE(dev); @@ -217,28 +208,7 @@ static void pci_piix_ide_exitfn(PCIDevice *dev) } } -/* hd_table must contain 4 block drivers */ /* NOTE: for the PIIX3, the IRQs and IOports are hardcoded */ -PCIDevice *pci_piix3_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn) -{ - PCIDevice *dev; - - dev = pci_create_simple(bus, devfn, "piix3-ide"); - pci_ide_create_devs(dev, hd_table); - return dev; -} - -/* hd_table must contain 4 block drivers */ -/* NOTE: for the PIIX4, the IRQs and IOports are hardcoded */ -PCIDevice *pci_piix4_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn) -{ - PCIDevice *dev; - - dev = pci_create_simple(bus, devfn, "piix4-ide"); - pci_ide_create_devs(dev, hd_table); - return dev; -} - static void piix3_ide_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -266,6 +236,7 @@ static const TypeInfo piix3_ide_xen_info = { .class_init = piix3_ide_class_init, }; +/* NOTE: for the PIIX4, the IRQs and IOports are hardcoded */ static void piix4_ide_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/ide/qdev.c b/hw/ide/qdev.c index d9b155420e..6b86219a1c 100644 --- a/hw/ide/qdev.c +++ b/hw/ide/qdev.c @@ -321,7 +321,7 @@ static void ide_hd_class_init(ObjectClass *klass, void *data) k->realize = ide_hd_realize; dc->fw_name = "drive"; dc->desc = "virtual IDE disk"; - dc->props = ide_hd_properties; + device_class_set_props(dc, ide_hd_properties); } static const TypeInfo ide_hd_info = { @@ -344,7 +344,7 @@ static void ide_cd_class_init(ObjectClass *klass, void *data) k->realize = ide_cd_realize; dc->fw_name = "drive"; dc->desc = "virtual IDE CD-ROM"; - dc->props = ide_cd_properties; + device_class_set_props(dc, ide_cd_properties); } static const TypeInfo ide_cd_info = { @@ -367,7 +367,7 @@ static void ide_drive_class_init(ObjectClass *klass, void *data) k->realize = ide_drive_realize; dc->fw_name = "drive"; dc->desc = "virtual IDE disk or CD-ROM (legacy)"; - dc->props = ide_drive_properties; + device_class_set_props(dc, ide_drive_properties); } static const TypeInfo ide_drive_info = { @@ -383,7 +383,7 @@ static void ide_device_class_init(ObjectClass *klass, void *data) k->realize = ide_qdev_realize; set_bit(DEVICE_CATEGORY_STORAGE, k->categories); k->bus_type = TYPE_IDE_BUS; - k->props = ide_props; + device_class_set_props(k, ide_props); } static const TypeInfo ide_device_type_info = { diff --git a/hw/ide/sii3112.c b/hw/ide/sii3112.c index 06605d7af2..d69079c3d9 100644 --- a/hw/ide/sii3112.c +++ b/hw/ide/sii3112.c @@ -251,8 +251,8 @@ static void sii3112_pci_realize(PCIDevice *dev, Error **errp) { SiI3112PCIState *d = SII3112_PCI(dev); PCIIDEState *s = PCI_IDE(dev); + DeviceState *ds = DEVICE(dev); MemoryRegion *mr; - qemu_irq *irq; int i; pci_config_set_interrupt_pin(dev->config, 1); @@ -280,10 +280,10 @@ static void sii3112_pci_realize(PCIDevice *dev, Error **errp) memory_region_init_alias(mr, OBJECT(d), "sii3112.bar4", &d->mmio, 0, 16); pci_register_bar(dev, 4, PCI_BASE_ADDRESS_SPACE_IO, mr); - irq = qemu_allocate_irqs(sii3112_set_irq, d, 2); + qdev_init_gpio_in(ds, sii3112_set_irq, 2); for (i = 0; i < 2; i++) { - ide_bus_new(&s->bus[i], sizeof(s->bus[i]), DEVICE(dev), i, 1); - ide_init2(&s->bus[i], irq[i]); + ide_bus_new(&s->bus[i], sizeof(s->bus[i]), ds, i, 1); + ide_init2(&s->bus[i], qdev_get_gpio_in(ds, i)); bmdma_init(&s->bus[i], &s->bmdma[i], s); s->bmdma[i].bus = &s->bus[i]; diff --git a/hw/ide/via.c b/hw/ide/via.c index 053622bd82..be09912b33 100644 --- a/hw/ide/via.c +++ b/hw/ide/via.c @@ -112,11 +112,7 @@ static void via_ide_set_irq(void *opaque, int n, int level) d->config[0x70 + n * 8] &= ~0x80; } - level = (d->config[0x70] & 0x80) || (d->config[0x78] & 0x80); - n = pci_get_byte(d->config + PCI_INTERRUPT_LINE); - if (n) { - qemu_set_irq(isa_get_irq(NULL, n), level); - } + qemu_set_irq(isa_get_irq(NULL, 14 + n), level); } static void via_ide_reset(DeviceState *dev) @@ -164,12 +160,14 @@ static void via_ide_reset(DeviceState *dev) static void via_ide_realize(PCIDevice *dev, Error **errp) { PCIIDEState *d = PCI_IDE(dev); + DeviceState *ds = DEVICE(dev); uint8_t *pci_conf = dev->config; int i; - pci_config_set_prog_interface(pci_conf, 0x8f); /* native PCI ATA mode */ + pci_config_set_prog_interface(pci_conf, 0x8a); /* legacy mode */ pci_set_long(pci_conf + PCI_CAPABILITY_LIST, 0x000000c0); - dev->wmask[PCI_INTERRUPT_LINE] = 0xf; + dev->wmask[PCI_INTERRUPT_LINE] = 0; + dev->wmask[PCI_CLASS_PROG] = 5; memory_region_init_io(&d->data_bar[0], OBJECT(d), &pci_ide_data_le_ops, &d->bus[0], "via-ide0-data", 8); @@ -190,11 +188,10 @@ static void via_ide_realize(PCIDevice *dev, Error **errp) bmdma_setup_bar(d); pci_register_bar(dev, 4, PCI_BASE_ADDRESS_SPACE_IO, &d->bmdma_bar); - vmstate_register(DEVICE(dev), 0, &vmstate_ide_pci, d); - + qdev_init_gpio_in(ds, via_ide_set_irq, 2); for (i = 0; i < 2; i++) { - ide_bus_new(&d->bus[i], sizeof(d->bus[i]), DEVICE(d), i, 2); - ide_init2(&d->bus[i], qemu_allocate_irq(via_ide_set_irq, d, i)); + ide_bus_new(&d->bus[i], sizeof(d->bus[i]), ds, i, 2); + ide_init2(&d->bus[i], qdev_get_gpio_in(ds, i)); bmdma_init(&d->bus[i], &d->bmdma[i], d); d->bmdma[i].bus = &d->bus[i]; @@ -213,20 +210,13 @@ static void via_ide_exitfn(PCIDevice *dev) } } -void via_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn) -{ - PCIDevice *dev; - - dev = pci_create_simple(bus, devfn, "via-ide"); - pci_ide_create_devs(dev, hd_table); -} - static void via_ide_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); dc->reset = via_ide_reset; + dc->vmsd = &vmstate_ide_pci; k->realize = via_ide_realize; k->exit = via_ide_exitfn; k->vendor_id = PCI_VENDOR_ID_VIA; diff --git a/hw/input/Kconfig b/hw/input/Kconfig index 287f08887b..64f14daabf 100644 --- a/hw/input/Kconfig +++ b/hw/input/Kconfig @@ -7,7 +7,6 @@ config LM832X config PCKBD bool - default y select PS2 depends on ISA_BUS @@ -41,3 +40,6 @@ config VHOST_USER_INPUT config TSC210X bool + +config LASIPS2 + select PS2 diff --git a/hw/input/Makefile.objs b/hw/input/Makefile.objs index a1bc502ed0..abc1ff03c0 100644 --- a/hw/input/Makefile.objs +++ b/hw/input/Makefile.objs @@ -12,6 +12,7 @@ common-obj-$(CONFIG_VIRTIO_INPUT) += virtio-input-hid.o common-obj-$(CONFIG_VIRTIO_INPUT_HOST) += virtio-input-host.o common-obj-$(CONFIG_VHOST_USER_INPUT) += vhost-user-input.o -obj-$(CONFIG_MILKYMIST) += milkymist-softusb.o -obj-$(CONFIG_PXA2XX) += pxa2xx_keypad.o -obj-$(CONFIG_TSC210X) += tsc210x.o +common-obj-$(CONFIG_MILKYMIST) += milkymist-softusb.o +common-obj-$(CONFIG_PXA2XX) += pxa2xx_keypad.o +common-obj-$(CONFIG_TSC210X) += tsc210x.o +common-obj-$(CONFIG_LASIPS2) += lasips2.o diff --git a/hw/input/adb.c b/hw/input/adb.c index 478a90fae2..b1ac4a3852 100644 --- a/hw/input/adb.c +++ b/hw/input/adb.c @@ -128,7 +128,7 @@ static void adb_device_class_init(ObjectClass *oc, void *data) DeviceClass *dc = DEVICE_CLASS(oc); dc->realize = adb_device_realizefn; - dc->props = adb_device_properties; + device_class_set_props(dc, adb_device_properties); dc->bus_type = TYPE_ADB_BUS; } diff --git a/hw/input/lasips2.c b/hw/input/lasips2.c new file mode 100644 index 0000000000..0786e57338 --- /dev/null +++ b/hw/input/lasips2.c @@ -0,0 +1,291 @@ +/* + * QEMU HP Lasi PS/2 interface emulation + * + * Copyright (c) 2019 Sven Schnelle + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "hw/qdev-properties.h" +#include "hw/hw.h" +#include "hw/input/ps2.h" +#include "hw/input/lasips2.h" +#include "hw/sysbus.h" +#include "exec/hwaddr.h" +#include "sysemu/sysemu.h" +#include "trace.h" +#include "exec/address-spaces.h" +#include "migration/vmstate.h" +#include "hw/irq.h" + + +struct LASIPS2State; +typedef struct LASIPS2Port { + struct LASIPS2State *parent; + MemoryRegion reg; + void *dev; + uint8_t id; + uint8_t control; + uint8_t buf; + bool loopback_rbne; + bool irq; +} LASIPS2Port; + +typedef struct LASIPS2State { + LASIPS2Port kbd; + LASIPS2Port mouse; + qemu_irq irq; +} LASIPS2State; + +static const VMStateDescription vmstate_lasips2 = { + .name = "lasips2", + .version_id = 0, + .minimum_version_id = 0, + .fields = (VMStateField[]) { + VMSTATE_UINT8(kbd.control, LASIPS2State), + VMSTATE_UINT8(kbd.id, LASIPS2State), + VMSTATE_BOOL(kbd.irq, LASIPS2State), + VMSTATE_UINT8(mouse.control, LASIPS2State), + VMSTATE_UINT8(mouse.id, LASIPS2State), + VMSTATE_BOOL(mouse.irq, LASIPS2State), + VMSTATE_END_OF_LIST() + } +}; + +typedef enum { + REG_PS2_ID = 0, + REG_PS2_RCVDATA = 4, + REG_PS2_CONTROL = 8, + REG_PS2_STATUS = 12, +} lasips2_read_reg_t; + +typedef enum { + REG_PS2_RESET = 0, + REG_PS2_XMTDATA = 4, +} lasips2_write_reg_t; + +typedef enum { + LASIPS2_CONTROL_ENABLE = 0x01, + LASIPS2_CONTROL_LOOPBACK = 0x02, + LASIPS2_CONTROL_DIAG = 0x20, + LASIPS2_CONTROL_DATDIR = 0x40, + LASIPS2_CONTROL_CLKDIR = 0x80, +} lasips2_control_reg_t; + +typedef enum { + LASIPS2_STATUS_RBNE = 0x01, + LASIPS2_STATUS_TBNE = 0x02, + LASIPS2_STATUS_TERR = 0x04, + LASIPS2_STATUS_PERR = 0x08, + LASIPS2_STATUS_CMPINTR = 0x10, + LASIPS2_STATUS_DATSHD = 0x40, + LASIPS2_STATUS_CLKSHD = 0x80, +} lasips2_status_reg_t; + +static const char *artist_read_reg_name(uint64_t addr) +{ + switch (addr & 0xc) { + case REG_PS2_ID: + return " PS2_ID"; + + case REG_PS2_RCVDATA: + return " PS2_RCVDATA"; + + case REG_PS2_CONTROL: + return " PS2_CONTROL"; + + case REG_PS2_STATUS: + return " PS2_STATUS"; + + default: + return ""; + } +} + +static const char *artist_write_reg_name(uint64_t addr) +{ + switch (addr & 0x0c) { + case REG_PS2_RESET: + return " PS2_RESET"; + + case REG_PS2_XMTDATA: + return " PS2_XMTDATA"; + + case REG_PS2_CONTROL: + return " PS2_CONTROL"; + + default: + return ""; + } +} + +static void lasips2_update_irq(LASIPS2State *s) +{ + trace_lasips2_intr(s->kbd.irq | s->mouse.irq); + qemu_set_irq(s->irq, s->kbd.irq | s->mouse.irq); +} + +static void lasips2_reg_write(void *opaque, hwaddr addr, uint64_t val, + unsigned size) +{ + LASIPS2Port *port = opaque; + + trace_lasips2_reg_write(size, port->id, addr, + artist_write_reg_name(addr), val); + + switch (addr & 0xc) { + case REG_PS2_CONTROL: + port->control = val; + break; + + case REG_PS2_XMTDATA: + if (port->control & LASIPS2_CONTROL_LOOPBACK) { + port->buf = val; + port->irq = true; + port->loopback_rbne = true; + lasips2_update_irq(port->parent); + break; + } + + if (port->id) { + ps2_write_mouse(port->dev, val); + } else { + ps2_write_keyboard(port->dev, val); + } + break; + + case REG_PS2_RESET: + break; + + default: + qemu_log_mask(LOG_UNIMP, "%s: unknown register 0x%02" HWADDR_PRIx "\n", + __func__, addr); + break; + } +} + +static uint64_t lasips2_reg_read(void *opaque, hwaddr addr, unsigned size) +{ + LASIPS2Port *port = opaque; + uint64_t ret = 0; + + switch (addr & 0xc) { + case REG_PS2_ID: + ret = port->id; + break; + + case REG_PS2_RCVDATA: + if (port->control & LASIPS2_CONTROL_LOOPBACK) { + port->irq = false; + port->loopback_rbne = false; + lasips2_update_irq(port->parent); + ret = port->buf; + break; + } + + ret = ps2_read_data(port->dev); + break; + + case REG_PS2_CONTROL: + ret = port->control; + break; + + case REG_PS2_STATUS: + + ret = LASIPS2_STATUS_DATSHD | LASIPS2_STATUS_CLKSHD; + + if (port->control & LASIPS2_CONTROL_DIAG) { + if (!(port->control & LASIPS2_CONTROL_DATDIR)) { + ret &= ~LASIPS2_STATUS_DATSHD; + } + + if (!(port->control & LASIPS2_CONTROL_CLKDIR)) { + ret &= ~LASIPS2_STATUS_CLKSHD; + } + } + + if (port->control & LASIPS2_CONTROL_LOOPBACK) { + if (port->loopback_rbne) { + ret |= LASIPS2_STATUS_RBNE; + } + } else { + if (!ps2_queue_empty(port->dev)) { + ret |= LASIPS2_STATUS_RBNE; + } + } + + if (port->parent->kbd.irq || port->parent->mouse.irq) { + ret |= LASIPS2_STATUS_CMPINTR; + } + break; + + default: + qemu_log_mask(LOG_UNIMP, "%s: unknown register 0x%02" HWADDR_PRIx "\n", + __func__, addr); + break; + } + trace_lasips2_reg_read(size, port->id, addr, + artist_read_reg_name(addr), ret); + + return ret; +} + +static const MemoryRegionOps lasips2_reg_ops = { + .read = lasips2_reg_read, + .write = lasips2_reg_write, + .impl = { + .min_access_size = 1, + .max_access_size = 4, + }, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static void ps2dev_update_irq(void *opaque, int level) +{ + LASIPS2Port *port = opaque; + port->irq = level; + lasips2_update_irq(port->parent); +} + +void lasips2_init(MemoryRegion *address_space, + hwaddr base, qemu_irq irq) +{ + LASIPS2State *s; + + s = g_malloc0(sizeof(LASIPS2State)); + + s->irq = irq; + s->mouse.id = 1; + s->kbd.parent = s; + s->mouse.parent = s; + + vmstate_register(NULL, base, &vmstate_lasips2, s); + + s->kbd.dev = ps2_kbd_init(ps2dev_update_irq, &s->kbd); + s->mouse.dev = ps2_mouse_init(ps2dev_update_irq, &s->mouse); + + memory_region_init_io(&s->kbd.reg, NULL, &lasips2_reg_ops, &s->kbd, + "lasips2-kbd", 0x100); + memory_region_add_subregion(address_space, base, &s->kbd.reg); + + memory_region_init_io(&s->mouse.reg, NULL, &lasips2_reg_ops, &s->mouse, + "lasips2-mouse", 0x100); + memory_region_add_subregion(address_space, base + 0x100, &s->mouse.reg); +} diff --git a/hw/input/milkymist-softusb.c b/hw/input/milkymist-softusb.c index 67fcb3595f..3e0a7eb0bd 100644 --- a/hw/input/milkymist-softusb.c +++ b/hw/input/milkymist-softusb.c @@ -302,7 +302,7 @@ static void milkymist_softusb_class_init(ObjectClass *klass, void *data) dc->realize = milkymist_softusb_realize; dc->reset = milkymist_softusb_reset; dc->vmsd = &vmstate_milkymist_softusb; - dc->props = milkymist_softusb_properties; + device_class_set_props(dc, milkymist_softusb_properties); } static const TypeInfo milkymist_softusb_info = { diff --git a/hw/input/pckbd.c b/hw/input/pckbd.c index f0acfd86f7..60a4130320 100644 --- a/hw/input/pckbd.c +++ b/hw/input/pckbd.c @@ -26,7 +26,6 @@ #include "qemu/log.h" #include "hw/isa/isa.h" #include "migration/vmstate.h" -#include "hw/i386/pc.h" #include "hw/input/ps2.h" #include "hw/irq.h" #include "hw/input/i8042.h" @@ -483,17 +482,15 @@ void i8042_mm_init(qemu_irq kbd_irq, qemu_irq mouse_irq, #define I8042(obj) OBJECT_CHECK(ISAKBDState, (obj), TYPE_I8042) -typedef struct ISAKBDState { +struct ISAKBDState { ISADevice parent_obj; KBDState kbd; MemoryRegion io[2]; -} ISAKBDState; +}; -void i8042_isa_mouse_fake_event(void *opaque) +void i8042_isa_mouse_fake_event(ISAKBDState *isa) { - ISADevice *dev = opaque; - ISAKBDState *isa = I8042(dev); KBDState *s = &isa->kbd; ps2_mouse_fake_event(s->mouse); diff --git a/hw/input/ps2.c b/hw/input/ps2.c index 67f92f6112..f8746d2f52 100644 --- a/hw/input/ps2.c +++ b/hw/input/ps2.c @@ -49,6 +49,8 @@ #define KBD_CMD_RESET_DISABLE 0xF5 /* reset and disable scanning */ #define KBD_CMD_RESET_ENABLE 0xF6 /* reset and enable scanning */ #define KBD_CMD_RESET 0xFF /* Reset */ +#define KBD_CMD_SET_MAKE_BREAK 0xFC /* Set Make and Break mode */ +#define KBD_CMD_SET_TYPEMATIC 0xFA /* Set Typematic Make and Break mode */ /* Keyboard Replies */ #define KBD_REPLY_POR 0xAA /* Power on reset */ @@ -190,6 +192,11 @@ static void ps2_reset_queue(PS2State *s) q->count = 0; } +int ps2_queue_empty(PS2State *s) +{ + return s->queue.count == 0; +} + void ps2_queue_noirq(PS2State *s, int b) { PS2Queue *q = &s->queue; @@ -573,6 +580,7 @@ void ps2_write_keyboard(void *opaque, int val) case KBD_CMD_SCANCODE: case KBD_CMD_SET_LEDS: case KBD_CMD_SET_RATE: + case KBD_CMD_SET_MAKE_BREAK: s->common.write_cmd = val; ps2_queue(&s->common, KBD_REPLY_ACK); break; @@ -592,11 +600,18 @@ void ps2_write_keyboard(void *opaque, int val) KBD_REPLY_ACK, KBD_REPLY_POR); break; + case KBD_CMD_SET_TYPEMATIC: + ps2_queue(&s->common, KBD_REPLY_ACK); + break; default: ps2_queue(&s->common, KBD_REPLY_RESEND); break; } break; + case KBD_CMD_SET_MAKE_BREAK: + ps2_queue(&s->common, KBD_REPLY_ACK); + s->common.write_cmd = -1; + break; case KBD_CMD_SCANCODE: if (val == 0) { if (s->common.queue.count <= PS2_QUEUE_SIZE - 2) { diff --git a/hw/input/stellaris_input.c b/hw/input/stellaris_input.c index 59892b07fc..e6ee5e11f1 100644 --- a/hw/input/stellaris_input.c +++ b/hw/input/stellaris_input.c @@ -88,5 +88,6 @@ void stellaris_gamepad_init(int n, qemu_irq *irq, const int *keycode) } s->num_buttons = n; qemu_add_kbd_event_handler(stellaris_gamepad_put_key, s); - vmstate_register(NULL, -1, &vmstate_stellaris_gamepad, s); + vmstate_register(NULL, VMSTATE_INSTANCE_ID_ANY, + &vmstate_stellaris_gamepad, s); } diff --git a/hw/input/trace-events b/hw/input/trace-events index cf072fa2f8..a2888fd10c 100644 --- a/hw/input/trace-events +++ b/hw/input/trace-events @@ -53,3 +53,8 @@ tsc2005_sense(const char *state) "touchscreen sense %s" # virtio-input.c virtio_input_queue_full(void) "queue full" + +# lasips2.c +lasips2_reg_read(unsigned int size, int id, uint64_t addr, const char *name, uint64_t val) "%u %d addr 0x%"PRIx64 "%s -> 0x%"PRIx64 +lasips2_reg_write(unsigned int size, int id, uint64_t addr, const char *name, uint64_t val) "%u %d addr 0x%"PRIx64 "%s <- 0x%"PRIx64 +lasips2_intr(unsigned int val) "%d" diff --git a/hw/input/virtio-input-hid.c b/hw/input/virtio-input-hid.c index 386a363a17..e8ae6c148a 100644 --- a/hw/input/virtio-input-hid.c +++ b/hw/input/virtio-input-hid.c @@ -222,7 +222,7 @@ static void virtio_input_hid_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); VirtIOInputClass *vic = VIRTIO_INPUT_CLASS(klass); - dc->props = virtio_input_hid_properties; + device_class_set_props(dc, virtio_input_hid_properties); vic->realize = virtio_input_hid_realize; vic->unrealize = virtio_input_hid_unrealize; vic->change_active = virtio_input_hid_change_active; @@ -362,7 +362,7 @@ static void virtio_mouse_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->props = virtio_mouse_properties; + device_class_set_props(dc, virtio_mouse_properties); } static void virtio_mouse_init(Object *obj) @@ -486,7 +486,7 @@ static void virtio_tablet_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->props = virtio_tablet_properties; + device_class_set_props(dc, virtio_tablet_properties); } static void virtio_tablet_init(Object *obj) diff --git a/hw/input/virtio-input-host.c b/hw/input/virtio-input-host.c index 1c3b12ba32..f2ab6df676 100644 --- a/hw/input/virtio-input-host.c +++ b/hw/input/virtio-input-host.c @@ -226,7 +226,7 @@ static void virtio_input_host_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->vmsd = &vmstate_virtio_input_host; - dc->props = virtio_input_host_properties; + device_class_set_props(dc, virtio_input_host_properties); vic->realize = virtio_input_host_realize; vic->unrealize = virtio_input_host_unrealize; vic->handle_status = virtio_input_host_handle_status; diff --git a/hw/input/virtio-input.c b/hw/input/virtio-input.c index ec54e46ad6..4d49790f50 100644 --- a/hw/input/virtio-input.c +++ b/hw/input/virtio-input.c @@ -280,6 +280,7 @@ static void virtio_input_device_unrealize(DeviceState *dev, Error **errp) { VirtIOInputClass *vic = VIRTIO_INPUT_GET_CLASS(dev); VirtIODevice *vdev = VIRTIO_DEVICE(dev); + VirtIOInput *vinput = VIRTIO_INPUT(dev); Error *local_err = NULL; if (vic->unrealize) { @@ -289,8 +290,8 @@ static void virtio_input_device_unrealize(DeviceState *dev, Error **errp) return; } } - virtio_del_queue(vdev, 0); - virtio_del_queue(vdev, 1); + virtio_delete_queue(vinput->evt); + virtio_delete_queue(vinput->sts); virtio_cleanup(vdev); } @@ -315,7 +316,7 @@ static void virtio_input_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); - dc->props = virtio_input_properties; + device_class_set_props(dc, virtio_input_properties); dc->vmsd = &vmstate_virtio_input; set_bit(DEVICE_CATEGORY_INPUT, dc->categories); vdc->realize = virtio_input_device_realize; diff --git a/hw/intc/Kconfig b/hw/intc/Kconfig index 5347f8412c..a189d6fedd 100644 --- a/hw/intc/Kconfig +++ b/hw/intc/Kconfig @@ -3,12 +3,14 @@ config HEATHROW_PIC config I8259 bool + select ISA_BUS config PL190 bool config IOAPIC bool + select I8259 config ARM_GIC bool @@ -21,6 +23,7 @@ config OPENPIC config APIC bool select MSI_NONBROKEN + select I8259 config ARM_GIC_KVM bool diff --git a/hw/intc/apic.c b/hw/intc/apic.c index 2a74f7b4bf..bd40467965 100644 --- a/hw/intc/apic.c +++ b/hw/intc/apic.c @@ -22,10 +22,10 @@ #include "hw/i386/apic_internal.h" #include "hw/i386/apic.h" #include "hw/i386/ioapic.h" +#include "hw/intc/i8259.h" #include "hw/pci/msi.h" #include "qemu/host-utils.h" #include "trace.h" -#include "hw/i386/pc.h" #include "hw/i386/apic-msidef.h" #include "qapi/error.h" diff --git a/hw/intc/apic_common.c b/hw/intc/apic_common.c index bec568dab2..7b92553436 100644 --- a/hw/intc/apic_common.c +++ b/hw/intc/apic_common.c @@ -289,7 +289,10 @@ static void apic_common_realize(DeviceState *dev, Error **errp) APICCommonState *s = APIC_COMMON(dev); APICCommonClass *info; static DeviceState *vapic; - int instance_id = s->id; + uint32_t instance_id = s->initial_apic_id; + + /* Normally initial APIC ID should be no more than hundreds */ + assert(instance_id != VMSTATE_INSTANCE_ID_ANY); info = APIC_COMMON_GET_CLASS(s); info->realize(dev, errp); @@ -305,7 +308,7 @@ static void apic_common_realize(DeviceState *dev, Error **errp) } if (s->legacy_instance_id) { - instance_id = -1; + instance_id = VMSTATE_INSTANCE_ID_ANY; } vmstate_register_with_alias_id(NULL, instance_id, &vmstate_apic_common, s, -1, 0, NULL); @@ -471,7 +474,7 @@ static void apic_common_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->reset = apic_reset_common; - dc->props = apic_properties_common; + device_class_set_props(dc, apic_properties_common); dc->realize = apic_common_realize; dc->unrealize = apic_common_unrealize; /* diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c index 1d7da7baa2..c60dc6b5e6 100644 --- a/hw/intc/arm_gic.c +++ b/hw/intc/arm_gic.c @@ -641,6 +641,23 @@ uint32_t gic_acknowledge_irq(GICState *s, int cpu, MemTxAttrs attrs) return ret; } +static uint32_t gic_fullprio_mask(GICState *s, int cpu) +{ + /* + * Return a mask word which clears the unimplemented priority + * bits from a priority value for an interrupt. (Not to be + * confused with the group priority, whose mask depends on BPR.) + */ + int priBits; + + if (gic_is_vcpu(cpu)) { + priBits = GIC_VIRT_MAX_GROUP_PRIO_BITS; + } else { + priBits = s->n_prio_bits; + } + return ~0U << (8 - priBits); +} + void gic_dist_set_priority(GICState *s, int cpu, int irq, uint8_t val, MemTxAttrs attrs) { @@ -651,6 +668,8 @@ void gic_dist_set_priority(GICState *s, int cpu, int irq, uint8_t val, val = 0x80 | (val >> 1); /* Non-secure view */ } + val &= gic_fullprio_mask(s, cpu); + if (irq < GIC_INTERNAL) { s->priority1[irq][cpu] = val; } else { @@ -669,7 +688,7 @@ static uint32_t gic_dist_get_priority(GICState *s, int cpu, int irq, } prio = (prio << 1) & 0xff; /* Non-secure view */ } - return prio; + return prio & gic_fullprio_mask(s, cpu); } static void gic_set_priority_mask(GICState *s, int cpu, uint8_t pmask, @@ -684,7 +703,7 @@ static void gic_set_priority_mask(GICState *s, int cpu, uint8_t pmask, return; } } - s->priority_mask[cpu] = pmask; + s->priority_mask[cpu] = pmask & gic_fullprio_mask(s, cpu); } static uint32_t gic_get_priority_mask(GICState *s, int cpu, MemTxAttrs attrs) @@ -2055,6 +2074,16 @@ static void arm_gic_realize(DeviceState *dev, Error **errp) return; } + if (s->n_prio_bits > GIC_MAX_PRIORITY_BITS || + (s->virt_extn ? s->n_prio_bits < GIC_VIRT_MAX_GROUP_PRIO_BITS : + s->n_prio_bits < GIC_MIN_PRIORITY_BITS)) { + error_setg(errp, "num-priority-bits cannot be greater than %d" + " or less than %d", GIC_MAX_PRIORITY_BITS, + s->virt_extn ? GIC_VIRT_MAX_GROUP_PRIO_BITS : + GIC_MIN_PRIORITY_BITS); + return; + } + /* This creates distributor, main CPU interface (s->cpuiomem[0]) and if * enabled, virtualization extensions related interfaces (main virtual * interface (s->vifaceiomem[0]) and virtual CPU interface). diff --git a/hw/intc/arm_gic_common.c b/hw/intc/arm_gic_common.c index f0d0d7c467..7b44d5625b 100644 --- a/hw/intc/arm_gic_common.c +++ b/hw/intc/arm_gic_common.c @@ -357,6 +357,7 @@ static Property arm_gic_common_properties[] = { DEFINE_PROP_BOOL("has-security-extensions", GICState, security_extn, 0), /* True if the GIC should implement the virtualization extensions */ DEFINE_PROP_BOOL("has-virtualization-extensions", GICState, virt_extn, 0), + DEFINE_PROP_UINT32("num-priority-bits", GICState, n_prio_bits, 8), DEFINE_PROP_END_OF_LIST(), }; @@ -367,7 +368,7 @@ static void arm_gic_common_class_init(ObjectClass *klass, void *data) dc->reset = arm_gic_common_reset; dc->realize = arm_gic_common_realize; - dc->props = arm_gic_common_properties; + device_class_set_props(dc, arm_gic_common_properties); dc->vmsd = &vmstate_gic; albifc->arm_linux_init = arm_gic_common_linux_init; } diff --git a/hw/intc/arm_gic_kvm.c b/hw/intc/arm_gic_kvm.c index 9deb15e7e6..d7df423a7a 100644 --- a/hw/intc/arm_gic_kvm.c +++ b/hw/intc/arm_gic_kvm.c @@ -551,7 +551,16 @@ static void kvm_arm_gic_realize(DeviceState *dev, Error **errp) KVM_DEV_ARM_VGIC_CTRL_INIT, NULL, true, &error_abort); } + } else if (kvm_check_extension(kvm_state, KVM_CAP_DEVICE_CTRL)) { + error_setg_errno(errp, -ret, "error creating in-kernel VGIC"); + error_append_hint(errp, + "Perhaps the host CPU does not support GICv2?\n"); } else if (ret != -ENODEV && ret != -ENOTSUP) { + /* + * Very ancient kernel without KVM_CAP_DEVICE_CTRL: assume that + * ENODEV or ENOTSUP mean "can't create GICv2 with KVM_CREATE_DEVICE", + * and that we will get a GICv2 via KVM_CREATE_IRQCHIP. + */ error_setg_errno(errp, -ret, "error creating in-kernel VGIC"); return; } diff --git a/hw/intc/arm_gicv2m.c b/hw/intc/arm_gicv2m.c index 6e45f4ff39..0b7e2b4f84 100644 --- a/hw/intc/arm_gicv2m.c +++ b/hw/intc/arm_gicv2m.c @@ -179,7 +179,7 @@ static void gicv2m_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->props = gicv2m_properties; + device_class_set_props(dc, gicv2m_properties); dc->realize = gicv2m_realize; } diff --git a/hw/intc/arm_gicv3_common.c b/hw/intc/arm_gicv3_common.c index f0c551d43f..58ef65f589 100644 --- a/hw/intc/arm_gicv3_common.c +++ b/hw/intc/arm_gicv3_common.c @@ -507,7 +507,7 @@ static void arm_gicv3_common_class_init(ObjectClass *klass, void *data) dc->reset = arm_gicv3_common_reset; dc->realize = arm_gicv3_common_realize; - dc->props = arm_gicv3_common_properties; + device_class_set_props(dc, arm_gicv3_common_properties); dc->vmsd = &vmstate_gicv3; albifc->arm_linux_init = arm_gic_common_linux_init; } diff --git a/hw/intc/arm_gicv3_cpuif.c b/hw/intc/arm_gicv3_cpuif.c index a254b0ce87..08e000e33c 100644 --- a/hw/intc/arm_gicv3_cpuif.c +++ b/hw/intc/arm_gicv3_cpuif.c @@ -664,6 +664,9 @@ static uint64_t icv_iar_read(CPUARMState *env, const ARMCPRegInfo *ri) trace_gicv3_icv_iar_read(ri->crm == 8 ? 0 : 1, gicv3_redist_affid(cs), intid); + + gicv3_cpuif_virt_update(cs); + return intid; } diff --git a/hw/intc/arm_gicv3_its_kvm.c b/hw/intc/arm_gicv3_its_kvm.c index d66f2431ee..ad0ebabc87 100644 --- a/hw/intc/arm_gicv3_its_kvm.c +++ b/hw/intc/arm_gicv3_its_kvm.c @@ -246,7 +246,7 @@ static void kvm_arm_its_class_init(ObjectClass *klass, void *data) KVMARMITSClass *ic = KVM_ARM_ITS_CLASS(klass); dc->realize = kvm_arm_its_realize; - dc->props = kvm_arm_its_props; + device_class_set_props(dc, kvm_arm_its_props); device_class_set_parent_reset(dc, kvm_arm_its_reset, &ic->parent_reset); icc->send_msi = kvm_its_send_msi; icc->pre_save = kvm_arm_its_pre_save; diff --git a/hw/intc/arm_gicv3_kvm.c b/hw/intc/arm_gicv3_kvm.c index 9c7f4ab871..49304ca589 100644 --- a/hw/intc/arm_gicv3_kvm.c +++ b/hw/intc/arm_gicv3_kvm.c @@ -336,7 +336,10 @@ static void kvm_arm_gicv3_put(GICv3State *s) kvm_gicd_access(s, GICD_CTLR, ®, true); if (redist_typer & GICR_TYPER_PLPIS) { - /* Set base addresses before LPIs are enabled by GICR_CTLR write */ + /* + * Restore base addresses before LPIs are potentially enabled by + * GICR_CTLR write + */ for (ncpu = 0; ncpu < s->num_cpu; ncpu++) { GICv3CPUState *c = &s->cpu[ncpu]; @@ -347,12 +350,6 @@ static void kvm_arm_gicv3_put(GICv3State *s) kvm_gicr_access(s, GICR_PROPBASER + 4, ncpu, ®h, true); reg64 = c->gicr_pendbaser; - if (!(c->gicr_ctlr & GICR_CTLR_ENABLE_LPIS)) { - /* Setting PTZ is advised if LPIs are disabled, to reduce - * GIC initialization time. - */ - reg64 |= GICR_PENDBASER_PTZ; - } regl = (uint32_t)reg64; kvm_gicr_access(s, GICR_PENDBASER, ncpu, ®l, true); regh = (uint32_t)(reg64 >> 32); diff --git a/hw/intc/armv7m_nvic.c b/hw/intc/armv7m_nvic.c index e8c74f9eba..1ad35e5529 100644 --- a/hw/intc/armv7m_nvic.c +++ b/hw/intc/armv7m_nvic.c @@ -1227,17 +1227,17 @@ static uint32_t nvic_readl(NVICState *s, uint32_t offset, MemTxAttrs attrs) case 0xd44: /* PFR1. */ return cpu->id_pfr1; case 0xd48: /* DFR0. */ - return cpu->id_dfr0; + return cpu->isar.id_dfr0; case 0xd4c: /* AFR0. */ return cpu->id_afr0; case 0xd50: /* MMFR0. */ - return cpu->id_mmfr0; + return cpu->isar.id_mmfr0; case 0xd54: /* MMFR1. */ - return cpu->id_mmfr1; + return cpu->isar.id_mmfr1; case 0xd58: /* MMFR2. */ - return cpu->id_mmfr2; + return cpu->isar.id_mmfr2; case 0xd5c: /* MMFR3. */ - return cpu->id_mmfr3; + return cpu->isar.id_mmfr3; case 0xd60: /* ISAR0. */ return cpu->isar.id_isar0; case 0xd64: /* ISAR1. */ @@ -1262,12 +1262,12 @@ static uint32_t nvic_readl(NVICState *s, uint32_t offset, MemTxAttrs attrs) case 0xd84: /* CSSELR */ return cpu->env.v7m.csselr[attrs.secure]; case 0xd88: /* CPACR */ - if (!arm_feature(&cpu->env, ARM_FEATURE_VFP)) { + if (!cpu_isar_feature(aa32_vfp_simd, cpu)) { return 0; } return cpu->env.v7m.cpacr[attrs.secure]; case 0xd8c: /* NSACR */ - if (!attrs.secure || !arm_feature(&cpu->env, ARM_FEATURE_VFP)) { + if (!attrs.secure || !cpu_isar_feature(aa32_vfp_simd, cpu)) { return 0; } return cpu->env.v7m.nsacr; @@ -1417,7 +1417,7 @@ static uint32_t nvic_readl(NVICState *s, uint32_t offset, MemTxAttrs attrs) } return cpu->env.v7m.sfar; case 0xf34: /* FPCCR */ - if (!arm_feature(&cpu->env, ARM_FEATURE_VFP)) { + if (!cpu_isar_feature(aa32_vfp_simd, cpu)) { return 0; } if (attrs.secure) { @@ -1444,12 +1444,12 @@ static uint32_t nvic_readl(NVICState *s, uint32_t offset, MemTxAttrs attrs) return value; } case 0xf38: /* FPCAR */ - if (!arm_feature(&cpu->env, ARM_FEATURE_VFP)) { + if (!cpu_isar_feature(aa32_vfp_simd, cpu)) { return 0; } return cpu->env.v7m.fpcar[attrs.secure]; case 0xf3c: /* FPDSCR */ - if (!arm_feature(&cpu->env, ARM_FEATURE_VFP)) { + if (!cpu_isar_feature(aa32_vfp_simd, cpu)) { return 0; } return cpu->env.v7m.fpdscr[attrs.secure]; @@ -1711,13 +1711,13 @@ static void nvic_writel(NVICState *s, uint32_t offset, uint32_t value, } break; case 0xd88: /* CPACR */ - if (arm_feature(&cpu->env, ARM_FEATURE_VFP)) { + if (cpu_isar_feature(aa32_vfp_simd, cpu)) { /* We implement only the Floating Point extension's CP10/CP11 */ cpu->env.v7m.cpacr[attrs.secure] = value & (0xf << 20); } break; case 0xd8c: /* NSACR */ - if (attrs.secure && arm_feature(&cpu->env, ARM_FEATURE_VFP)) { + if (attrs.secure && cpu_isar_feature(aa32_vfp_simd, cpu)) { /* We implement only the Floating Point extension's CP10/CP11 */ cpu->env.v7m.nsacr = value & (3 << 10); } @@ -1951,7 +1951,7 @@ static void nvic_writel(NVICState *s, uint32_t offset, uint32_t value, break; } case 0xf34: /* FPCCR */ - if (arm_feature(&cpu->env, ARM_FEATURE_VFP)) { + if (cpu_isar_feature(aa32_vfp_simd, cpu)) { /* Not all bits here are banked. */ uint32_t fpccr_s; @@ -2005,13 +2005,13 @@ static void nvic_writel(NVICState *s, uint32_t offset, uint32_t value, } break; case 0xf38: /* FPCAR */ - if (arm_feature(&cpu->env, ARM_FEATURE_VFP)) { + if (cpu_isar_feature(aa32_vfp_simd, cpu)) { value &= ~7; cpu->env.v7m.fpcar[attrs.secure] = value; } break; case 0xf3c: /* FPDSCR */ - if (arm_feature(&cpu->env, ARM_FEATURE_VFP)) { + if (cpu_isar_feature(aa32_vfp_simd, cpu)) { value &= 0x07c00000; cpu->env.v7m.fpdscr[attrs.secure] = value; } @@ -2593,6 +2593,12 @@ static void armv7m_nvic_reset(DeviceState *dev) s->itns[i] = true; } } + + /* + * We updated state that affects the CPU's MMUidx and thus its hflags; + * and we can't guarantee that we run before the CPU reset function. + */ + arm_rebuild_hflags(&s->cpu->env); } static void nvic_systick_trigger(void *opaque, int n, int level) @@ -2749,7 +2755,7 @@ static void armv7m_nvic_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->vmsd = &vmstate_nvic; - dc->props = props_nvic; + device_class_set_props(dc, props_nvic); dc->reset = armv7m_nvic_reset; dc->realize = armv7m_nvic_realize; } diff --git a/hw/intc/bcm2835_ic.c b/hw/intc/bcm2835_ic.c index 05bd28e4f9..53ab8f5881 100644 --- a/hw/intc/bcm2835_ic.c +++ b/hw/intc/bcm2835_ic.c @@ -1,7 +1,6 @@ /* * Raspberry Pi emulation (c) 2012 Gregory Estrade * Refactoring for Pi2 Copyright (c) 2015, Microsoft. Written by Andrew Baumann. - * This code is licensed under the GNU GPLv2 and later. * Heavily based on pl190.c, copyright terms below: * * Arm PrimeCell PL190 Vector Interrupt Controller @@ -9,7 +8,8 @@ * Copyright (c) 2006 CodeSourcery. * Written by Paul Brook * - * This code is licensed under the GPL. + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. */ #include "qemu/osdep.h" diff --git a/hw/intc/bcm2836_control.c b/hw/intc/bcm2836_control.c index 61f884ff9e..53dba0080c 100644 --- a/hw/intc/bcm2836_control.c +++ b/hw/intc/bcm2836_control.c @@ -4,7 +4,6 @@ * Written by Andrew Baumann * * Based on bcm2835_ic.c (Raspberry Pi emulation) (c) 2012 Gregory Estrade - * This code is licensed under the GNU GPLv2 and later. * * At present, only implements interrupt routing, and mailboxes (i.e., * not PMU interrupt, or AXI counters). @@ -13,6 +12,9 @@ * * Ref: * https://www.raspberrypi.org/documentation/hardware/raspberrypi/bcm2836/QA7_rev3.4.pdf + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. */ #include "qemu/osdep.h" diff --git a/hw/intc/etraxfs_pic.c b/hw/intc/etraxfs_pic.c index 77f652acec..12988c7aa9 100644 --- a/hw/intc/etraxfs_pic.c +++ b/hw/intc/etraxfs_pic.c @@ -27,8 +27,6 @@ #include "qemu/module.h" #include "hw/irq.h" #include "hw/qdev-properties.h" -//#include "pc.h" -//#include "etraxfs.h" #define D(x) @@ -48,7 +46,6 @@ struct etrax_pic SysBusDevice parent_obj; MemoryRegion mmio; - void *interrupt_vector; qemu_irq parent_irq; qemu_irq parent_nmi; uint32_t regs[R_MAX]; @@ -79,11 +76,7 @@ static void pic_update(struct etrax_pic *fs) } } - if (fs->interrupt_vector) { - /* hack alert: ptr property */ - *(uint32_t*)(fs->interrupt_vector) = vector; - } - qemu_set_irq(fs->parent_irq, !!vector); + qemu_set_irq(fs->parent_irq, vector); } static uint64_t @@ -163,28 +156,11 @@ static void etraxfs_pic_init(Object *obj) sysbus_init_mmio(sbd, &s->mmio); } -static Property etraxfs_pic_properties[] = { - DEFINE_PROP_PTR("interrupt_vector", struct etrax_pic, interrupt_vector), - DEFINE_PROP_END_OF_LIST(), -}; - -static void etraxfs_pic_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->props = etraxfs_pic_properties; - /* - * Note: pointer property "interrupt_vector" may remain null, thus - * no need for dc->user_creatable = false; - */ -} - static const TypeInfo etraxfs_pic_info = { .name = TYPE_ETRAX_FS_PIC, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(struct etrax_pic), .instance_init = etraxfs_pic_init, - .class_init = etraxfs_pic_class_init, }; static void etraxfs_pic_register_types(void) diff --git a/hw/intc/exynos4210_combiner.c b/hw/intc/exynos4210_combiner.c index 5b33ca6628..b8561e4180 100644 --- a/hw/intc/exynos4210_combiner.c +++ b/hw/intc/exynos4210_combiner.c @@ -442,7 +442,7 @@ static void exynos4210_combiner_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->reset = exynos4210_combiner_reset; - dc->props = exynos4210_combiner_properties; + device_class_set_props(dc, exynos4210_combiner_properties); dc->vmsd = &vmstate_exynos4210_combiner; } diff --git a/hw/intc/exynos4210_gic.c b/hw/intc/exynos4210_gic.c index a1b699b6ba..82c8f4192c 100644 --- a/hw/intc/exynos4210_gic.c +++ b/hw/intc/exynos4210_gic.c @@ -293,6 +293,7 @@ static void exynos4210_gic_realize(DeviceState *dev, Error **errp) char cpu_alias_name[sizeof(cpu_prefix) + 3]; char dist_alias_name[sizeof(cpu_prefix) + 3]; SysBusDevice *gicbusdev; + uint32_t n = s->num_cpu; uint32_t i; s->gic = qdev_create(NULL, "arm_gic"); @@ -313,7 +314,13 @@ static void exynos4210_gic_realize(DeviceState *dev, Error **errp) memory_region_init(&s->dist_container, obj, "exynos4210-dist-container", EXYNOS4210_EXT_GIC_DIST_REGION_SIZE); - for (i = 0; i < s->num_cpu; i++) { + /* + * This clues in gcc that our on-stack buffers do, in fact have + * enough room for the cpu numbers. gcc 9.2.1 on 32-bit x86 + * doesn't figure this out, otherwise and gives spurious warnings. + */ + assert(n <= EXYNOS4210_NCPUS); + for (i = 0; i < n; i++) { /* Map CPU interface per SMP Core */ sprintf(cpu_alias_name, "%s%x", cpu_prefix, i); memory_region_init_alias(&s->cpu_alias[i], obj, @@ -348,7 +355,7 @@ static void exynos4210_gic_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->props = exynos4210_gic_properties; + device_class_set_props(dc, exynos4210_gic_properties); dc->realize = exynos4210_gic_realize; } @@ -455,7 +462,7 @@ static void exynos4210_irq_gate_class_init(ObjectClass *klass, void *data) dc->reset = exynos4210_irq_gate_reset; dc->vmsd = &vmstate_exynos4210_irq_gate; - dc->props = exynos4210_irq_gate_properties; + device_class_set_props(dc, exynos4210_irq_gate_properties); dc->realize = exynos4210_irq_gate_realize; } diff --git a/hw/intc/grlib_irqmp.c b/hw/intc/grlib_irqmp.c index bc78e1a14f..794c643af2 100644 --- a/hw/intc/grlib_irqmp.c +++ b/hw/intc/grlib_irqmp.c @@ -25,6 +25,7 @@ */ #include "qemu/osdep.h" +#include "hw/irq.h" #include "hw/sysbus.h" #include "cpu.h" @@ -58,10 +59,8 @@ typedef struct IRQMP { MemoryRegion iomem; - void *set_pil_in; - void *set_pil_in_opaque; - IRQMPState *state; + qemu_irq irq; } IRQMP; struct IRQMPState { @@ -82,7 +81,6 @@ static void grlib_irqmp_check_irqs(IRQMPState *state) uint32_t pend = 0; uint32_t level0 = 0; uint32_t level1 = 0; - set_pil_in_fn set_pil_in; assert(state != NULL); assert(state->parent != NULL); @@ -97,14 +95,8 @@ static void grlib_irqmp_check_irqs(IRQMPState *state) trace_grlib_irqmp_check_irqs(state->pending, state->force[0], state->mask[0], level1, level0); - set_pil_in = (set_pil_in_fn)state->parent->set_pil_in; - /* Trigger level1 interrupt first and level0 if there is no level1 */ - if (level1 != 0) { - set_pil_in(state->parent->set_pil_in_opaque, level1); - } else { - set_pil_in(state->parent->set_pil_in_opaque, level0); - } + qemu_set_irq(state->parent->irq, level1 ?: level0); } static void grlib_irqmp_ack_mask(IRQMPState *state, uint32_t mask) @@ -335,6 +327,7 @@ static void grlib_irqmp_init(Object *obj) IRQMP *irqmp = GRLIB_IRQMP(obj); SysBusDevice *dev = SYS_BUS_DEVICE(obj); + qdev_init_gpio_out_named(DEVICE(obj), &irqmp->irq, "grlib-irq", 1); memory_region_init_io(&irqmp->iomem, obj, &grlib_irqmp_ops, irqmp, "irqmp", IRQMP_REG_SIZE); @@ -343,31 +336,11 @@ static void grlib_irqmp_init(Object *obj) sysbus_init_mmio(dev, &irqmp->iomem); } -static void grlib_irqmp_realize(DeviceState *dev, Error **errp) -{ - IRQMP *irqmp = GRLIB_IRQMP(dev); - - /* Check parameters */ - if (irqmp->set_pil_in == NULL) { - error_setg(errp, "set_pil_in cannot be NULL."); - } -} - -static Property grlib_irqmp_properties[] = { - DEFINE_PROP_PTR("set_pil_in", IRQMP, set_pil_in), - DEFINE_PROP_PTR("set_pil_in_opaque", IRQMP, set_pil_in_opaque), - DEFINE_PROP_END_OF_LIST(), -}; - static void grlib_irqmp_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->reset = grlib_irqmp_reset; - dc->props = grlib_irqmp_properties; - /* Reason: pointer properties "set_pil_in", "set_pil_in_opaque" */ - dc->user_creatable = false; - dc->realize = grlib_irqmp_realize; } static const TypeInfo grlib_irqmp_info = { diff --git a/hw/intc/i8259.c b/hw/intc/i8259.c index 03dfa87b6b..1cd0d00441 100644 --- a/hw/intc/i8259.c +++ b/hw/intc/i8259.c @@ -23,7 +23,7 @@ */ #include "qemu/osdep.h" -#include "hw/i386/pc.h" +#include "hw/intc/i8259.h" #include "hw/irq.h" #include "hw/isa/isa.h" #include "qemu/timer.h" diff --git a/hw/intc/i8259_common.c b/hw/intc/i8259_common.c index bf702ea6f7..d5d9f27d58 100644 --- a/hw/intc/i8259_common.c +++ b/hw/intc/i8259_common.c @@ -24,7 +24,7 @@ */ #include "qemu/osdep.h" -#include "hw/i386/pc.h" +#include "hw/intc/i8259.h" #include "hw/isa/i8259_internal.h" #include "hw/qdev-properties.h" #include "migration/vmstate.h" @@ -186,7 +186,7 @@ static void pic_common_class_init(ObjectClass *klass, void *data) InterruptStatsProviderClass *ic = INTERRUPT_STATS_PROVIDER_CLASS(klass); dc->vmsd = &vmstate_pic_common; - dc->props = pic_properties_common; + device_class_set_props(dc, pic_properties_common); dc->realize = pic_common_realize; /* * Reason: unlike ordinary ISA devices, the PICs need additional diff --git a/hw/intc/ioapic.c b/hw/intc/ioapic.c index ead14e1888..15747fe2c2 100644 --- a/hw/intc/ioapic.c +++ b/hw/intc/ioapic.c @@ -23,10 +23,11 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "monitor/monitor.h" -#include "hw/i386/pc.h" #include "hw/i386/apic.h" #include "hw/i386/ioapic.h" #include "hw/i386/ioapic_internal.h" +#include "hw/i386/x86.h" +#include "hw/intc/i8259.h" #include "hw/pci/msi.h" #include "hw/qdev-properties.h" #include "sysemu/kvm.h" @@ -476,7 +477,7 @@ static void ioapic_class_init(ObjectClass *klass, void *data) */ k->post_load = ioapic_update_kvm_routes; dc->reset = ioapic_reset_common; - dc->props = ioapic_properties; + device_class_set_props(dc, ioapic_properties); } static const TypeInfo ioapic_info = { diff --git a/hw/intc/mips_gic.c b/hw/intc/mips_gic.c index 54b3059f3f..bda4549925 100644 --- a/hw/intc/mips_gic.c +++ b/hw/intc/mips_gic.c @@ -448,7 +448,7 @@ static void mips_gic_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->props = mips_gic_properties; + device_class_set_props(dc, mips_gic_properties); dc->realize = mips_gic_realize; } diff --git a/hw/intc/omap_intc.c b/hw/intc/omap_intc.c index 854b709ca0..b8a1d1fd7d 100644 --- a/hw/intc/omap_intc.c +++ b/hw/intc/omap_intc.c @@ -38,10 +38,6 @@ struct omap_intr_handler_bank_s { unsigned char priority[32]; }; -#define TYPE_OMAP_INTC "common-omap-intc" -#define OMAP_INTC(obj) \ - OBJECT_CHECK(struct omap_intr_handler_s, (obj), TYPE_OMAP_INTC) - struct omap_intr_handler_s { SysBusDevice parent_obj; @@ -391,9 +387,18 @@ static void omap_intc_realize(DeviceState *dev, Error **errp) } } +void omap_intc_set_iclk(omap_intr_handler *intc, omap_clk clk) +{ + intc->iclk = clk; +} + +void omap_intc_set_fclk(omap_intr_handler *intc, omap_clk clk) +{ + intc->fclk = clk; +} + static Property omap_intc_properties[] = { DEFINE_PROP_UINT32("size", struct omap_intr_handler_s, size, 0x100), - DEFINE_PROP_PTR("clk", struct omap_intr_handler_s, iclk), DEFINE_PROP_END_OF_LIST(), }; @@ -402,7 +407,7 @@ static void omap_intc_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->reset = omap_inth_reset; - dc->props = omap_intc_properties; + device_class_set_props(dc, omap_intc_properties); /* Reason: pointer property "clk" */ dc->user_creatable = false; dc->realize = omap_intc_realize; @@ -647,8 +652,6 @@ static void omap2_intc_realize(DeviceState *dev, Error **errp) static Property omap2_intc_properties[] = { DEFINE_PROP_UINT8("revision", struct omap_intr_handler_s, revision, 0x21), - DEFINE_PROP_PTR("iclk", struct omap_intr_handler_s, iclk), - DEFINE_PROP_PTR("fclk", struct omap_intr_handler_s, fclk), DEFINE_PROP_END_OF_LIST(), }; @@ -657,7 +660,7 @@ static void omap2_intc_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->reset = omap_inth_reset; - dc->props = omap2_intc_properties; + device_class_set_props(dc, omap2_intc_properties); /* Reason: pointer property "iclk", "fclk" */ dc->user_creatable = false; dc->realize = omap2_intc_realize; diff --git a/hw/intc/ompic.c b/hw/intc/ompic.c index ca9614fda1..c354427a61 100644 --- a/hw/intc/ompic.c +++ b/hw/intc/ompic.c @@ -161,7 +161,7 @@ static void or1k_ompic_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->props = or1k_ompic_properties; + device_class_set_props(dc, or1k_ompic_properties); dc->realize = or1k_ompic_realize; dc->vmsd = &vmstate_or1k_ompic; } diff --git a/hw/intc/openpic.c b/hw/intc/openpic.c index c797ba78f3..65970e1b37 100644 --- a/hw/intc/openpic.c +++ b/hw/intc/openpic.c @@ -1628,7 +1628,7 @@ static void openpic_class_init(ObjectClass *oc, void *data) DeviceClass *dc = DEVICE_CLASS(oc); dc->realize = openpic_realize; - dc->props = openpic_properties; + device_class_set_props(dc, openpic_properties); dc->reset = openpic_reset; dc->vmsd = &vmstate_openpic; set_bit(DEVICE_CATEGORY_MISC, dc->categories); diff --git a/hw/intc/openpic_kvm.c b/hw/intc/openpic_kvm.c index c09bebedd1..e4bf47d885 100644 --- a/hw/intc/openpic_kvm.c +++ b/hw/intc/openpic_kvm.c @@ -274,7 +274,7 @@ static void kvm_openpic_class_init(ObjectClass *oc, void *data) DeviceClass *dc = DEVICE_CLASS(oc); dc->realize = kvm_openpic_realize; - dc->props = kvm_openpic_properties; + device_class_set_props(dc, kvm_openpic_properties); dc->reset = kvm_openpic_reset; set_bit(DEVICE_CATEGORY_MISC, dc->categories); } diff --git a/hw/intc/pnv_xive.c b/hw/intc/pnv_xive.c index 348f2fdd26..aeda488bd1 100644 --- a/hw/intc/pnv_xive.c +++ b/hw/intc/pnv_xive.c @@ -29,7 +29,7 @@ #include "pnv_xive_regs.h" -#define XIVE_DEBUG +#undef XIVE_DEBUG /* * Virtual structures table (VST) @@ -85,13 +85,30 @@ static inline uint64_t SETFIELD(uint64_t mask, uint64_t word, return (word & ~mask) | ((value << ctz64(mask)) & mask); } +/* + * When PC_TCTXT_CHIPID_OVERRIDE is configured, the PC_TCTXT_CHIPID + * field overrides the hardwired chip ID in the Powerbus operations + * and for CAM compares + */ +static uint8_t pnv_xive_block_id(PnvXive *xive) +{ + uint8_t blk = xive->chip->chip_id; + uint64_t cfg_val = xive->regs[PC_TCTXT_CFG >> 3]; + + if (cfg_val & PC_TCTXT_CHIPID_OVERRIDE) { + blk = GETFIELD(PC_TCTXT_CHIPID, cfg_val); + } + + return blk; +} + /* * Remote access to controllers. HW uses MMIOs. For now, a simple scan * of the chips is good enough. * * TODO: Block scope support */ -static PnvXive *pnv_xive_get_ic(uint8_t blk) +static PnvXive *pnv_xive_get_remote(uint8_t blk) { PnvMachineState *pnv = PNV_MACHINE(qdev_get_machine()); int i; @@ -100,7 +117,7 @@ static PnvXive *pnv_xive_get_ic(uint8_t blk) Pnv9Chip *chip9 = PNV9_CHIP(pnv->chips[i]); PnvXive *xive = &chip9->xive; - if (xive->chip->chip_id == blk) { + if (pnv_xive_block_id(xive) == blk) { return xive; } } @@ -123,36 +140,22 @@ static uint64_t pnv_xive_vst_page_size_allowed(uint32_t page_shift) page_shift == 21 || page_shift == 24; } -static uint64_t pnv_xive_vst_size(uint64_t vsd) -{ - uint64_t vst_tsize = 1ull << (GETFIELD(VSD_TSIZE, vsd) + 12); - - /* - * Read the first descriptor to get the page size of the indirect - * table. - */ - if (VSD_INDIRECT & vsd) { - uint32_t nr_pages = vst_tsize / XIVE_VSD_SIZE; - uint32_t page_shift; - - vsd = ldq_be_dma(&address_space_memory, vsd & VSD_ADDRESS_MASK); - page_shift = GETFIELD(VSD_TSIZE, vsd) + 12; - - if (!pnv_xive_vst_page_size_allowed(page_shift)) { - return 0; - } - - return nr_pages * (1ull << page_shift); - } - - return vst_tsize; -} - static uint64_t pnv_xive_vst_addr_direct(PnvXive *xive, uint32_t type, uint64_t vsd, uint32_t idx) { const XiveVstInfo *info = &vst_infos[type]; uint64_t vst_addr = vsd & VSD_ADDRESS_MASK; + uint64_t vst_tsize = 1ull << (GETFIELD(VSD_TSIZE, vsd) + 12); + uint32_t idx_max; + + idx_max = vst_tsize / info->size - 1; + if (idx > idx_max) { +#ifdef XIVE_DEBUG + xive_error(xive, "VST: %s entry %x out of range [ 0 .. %x ] !?", + info->name, idx, idx_max); +#endif + return 0; + } return vst_addr + idx * info->size; } @@ -171,7 +174,9 @@ static uint64_t pnv_xive_vst_addr_indirect(PnvXive *xive, uint32_t type, vsd = ldq_be_dma(&address_space_memory, vsd_addr); if (!(vsd & VSD_ADDRESS_MASK)) { +#ifdef XIVE_DEBUG xive_error(xive, "VST: invalid %s entry %x !?", info->name, idx); +#endif return 0; } @@ -192,7 +197,9 @@ static uint64_t pnv_xive_vst_addr_indirect(PnvXive *xive, uint32_t type, vsd = ldq_be_dma(&address_space_memory, vsd_addr); if (!(vsd & VSD_ADDRESS_MASK)) { +#ifdef XIVE_DEBUG xive_error(xive, "VST: invalid %s entry %x !?", info->name, idx); +#endif return 0; } @@ -215,7 +222,6 @@ static uint64_t pnv_xive_vst_addr(PnvXive *xive, uint32_t type, uint8_t blk, { const XiveVstInfo *info = &vst_infos[type]; uint64_t vsd; - uint32_t idx_max; if (blk >= info->max_blocks) { xive_error(xive, "VST: invalid block id %d for VST %s %d !?", @@ -227,20 +233,11 @@ static uint64_t pnv_xive_vst_addr(PnvXive *xive, uint32_t type, uint8_t blk, /* Remote VST access */ if (GETFIELD(VSD_MODE, vsd) == VSD_MODE_FORWARD) { - xive = pnv_xive_get_ic(blk); + xive = pnv_xive_get_remote(blk); return xive ? pnv_xive_vst_addr(xive, type, blk, idx) : 0; } - idx_max = pnv_xive_vst_size(vsd) / info->size - 1; - if (idx > idx_max) { -#ifdef XIVE_DEBUG - xive_error(xive, "VST: %s entry %x/%x out of range [ 0 .. %x ] !?", - info->name, blk, idx, idx_max); -#endif - return 0; - } - if (VSD_INDIRECT & vsd) { return pnv_xive_vst_addr_indirect(xive, type, vsd, idx); } @@ -384,7 +381,10 @@ static int pnv_xive_get_eas(XiveRouter *xrtr, uint8_t blk, uint32_t idx, { PnvXive *xive = PNV_XIVE(xrtr); - if (pnv_xive_get_ic(blk) != xive) { + /* + * EAT lookups should be local to the IC + */ + if (pnv_xive_block_id(xive) != blk) { xive_error(xive, "VST: EAS %x is remote !?", XIVE_EAS(blk, idx)); return -1; } @@ -392,31 +392,93 @@ static int pnv_xive_get_eas(XiveRouter *xrtr, uint8_t blk, uint32_t idx, return pnv_xive_vst_read(xive, VST_TSEL_IVT, blk, idx, eas); } -static XiveTCTX *pnv_xive_get_tctx(XiveRouter *xrtr, CPUState *cs) +/* + * One bit per thread id. The first register PC_THREAD_EN_REG0 covers + * the first cores 0-15 (normal) of the chip or 0-7 (fused). The + * second register covers cores 16-23 (normal) or 8-11 (fused). + */ +static bool pnv_xive_is_cpu_enabled(PnvXive *xive, PowerPCCPU *cpu) { - PowerPCCPU *cpu = POWERPC_CPU(cs); - XiveTCTX *tctx = XIVE_TCTX(pnv_cpu_state(cpu)->intc); - PnvXive *xive = NULL; - CPUPPCState *env = &cpu->env; - int pir = env->spr_cb[SPR_PIR].default_value; + int pir = ppc_cpu_pir(cpu); + uint32_t fc = PNV9_PIR2FUSEDCORE(pir); + uint64_t reg = fc < 8 ? PC_THREAD_EN_REG0 : PC_THREAD_EN_REG1; + uint32_t bit = pir & 0x3f; - /* - * Perform an extra check on the HW thread enablement. - * - * The TIMA is shared among the chips and to identify the chip - * from which the access is being done, we extract the chip id - * from the PIR. - */ - xive = pnv_xive_get_ic((pir >> 8) & 0xf); - if (!xive) { - return NULL; + return xive->regs[reg >> 3] & PPC_BIT(bit); +} + +static int pnv_xive_match_nvt(XivePresenter *xptr, uint8_t format, + uint8_t nvt_blk, uint32_t nvt_idx, + bool cam_ignore, uint8_t priority, + uint32_t logic_serv, XiveTCTXMatch *match) +{ + PnvXive *xive = PNV_XIVE(xptr); + PnvChip *chip = xive->chip; + int count = 0; + int i, j; + + for (i = 0; i < chip->nr_cores; i++) { + PnvCore *pc = chip->cores[i]; + CPUCore *cc = CPU_CORE(pc); + + for (j = 0; j < cc->nr_threads; j++) { + PowerPCCPU *cpu = pc->threads[j]; + XiveTCTX *tctx; + int ring; + + if (!pnv_xive_is_cpu_enabled(xive, cpu)) { + continue; + } + + tctx = XIVE_TCTX(pnv_cpu_state(cpu)->intc); + + /* + * Check the thread context CAM lines and record matches. + */ + ring = xive_presenter_tctx_match(xptr, tctx, format, nvt_blk, + nvt_idx, cam_ignore, logic_serv); + /* + * Save the context and follow on to catch duplicates, that we + * don't support yet. + */ + if (ring != -1) { + if (match->tctx) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: already found a " + "thread context NVT %x/%x\n", + nvt_blk, nvt_idx); + return -1; + } + + match->ring = ring; + match->tctx = tctx; + count++; + } + } } - if (!(xive->regs[PC_THREAD_EN_REG0 >> 3] & PPC_BIT(pir & 0x3f))) { - xive_error(PNV_XIVE(xrtr), "IC: CPU %x is not enabled", pir); - } + return count; +} - return tctx; +static uint8_t pnv_xive_get_block_id(XiveRouter *xrtr) +{ + return pnv_xive_block_id(PNV_XIVE(xrtr)); +} + +/* + * The TIMA MMIO space is shared among the chips and to identify the + * chip from which the access is being done, we extract the chip id + * from the PIR. + */ +static PnvXive *pnv_xive_tm_get_xive(PowerPCCPU *cpu) +{ + int pir = ppc_cpu_pir(cpu); + XivePresenter *xptr = XIVE_TCTX(pnv_cpu_state(cpu)->intc)->xptr; + PnvXive *xive = PNV_XIVE(xptr); + + if (!pnv_xive_is_cpu_enabled(xive, cpu)) { + xive_error(xive, "IC: CPU %x is not enabled", pir); + } + return xive; } /* @@ -429,7 +491,7 @@ static XiveTCTX *pnv_xive_get_tctx(XiveRouter *xrtr, CPUState *cs) static void pnv_xive_notify(XiveNotifier *xn, uint32_t srcno) { PnvXive *xive = PNV_XIVE(xn); - uint8_t blk = xive->chip->chip_id; + uint8_t blk = pnv_xive_block_id(xive); xive_router_notify(xn, XIVE_EAS(blk, srcno)); } @@ -453,19 +515,50 @@ static uint64_t pnv_xive_pc_size(PnvXive *xive) return (~xive->regs[CQ_PC_BARM >> 3] + 1) & CQ_PC_BARM_MASK; } -static uint32_t pnv_xive_nr_ipis(PnvXive *xive) +static uint32_t pnv_xive_nr_ipis(PnvXive *xive, uint8_t blk) { - uint8_t blk = xive->chip->chip_id; + uint64_t vsd = xive->vsds[VST_TSEL_SBE][blk]; + uint64_t vst_tsize = 1ull << (GETFIELD(VSD_TSIZE, vsd) + 12); - return pnv_xive_vst_size(xive->vsds[VST_TSEL_SBE][blk]) * SBE_PER_BYTE; + return VSD_INDIRECT & vsd ? 0 : vst_tsize * SBE_PER_BYTE; } -static uint32_t pnv_xive_nr_ends(PnvXive *xive) +/* + * Compute the number of entries per indirect subpage. + */ +static uint64_t pnv_xive_vst_per_subpage(PnvXive *xive, uint32_t type) { - uint8_t blk = xive->chip->chip_id; + uint8_t blk = pnv_xive_block_id(xive); + uint64_t vsd = xive->vsds[type][blk]; + const XiveVstInfo *info = &vst_infos[type]; + uint64_t vsd_addr; + uint32_t page_shift; - return pnv_xive_vst_size(xive->vsds[VST_TSEL_EQDT][blk]) - / vst_infos[VST_TSEL_EQDT].size; + /* For direct tables, fake a valid value */ + if (!(VSD_INDIRECT & vsd)) { + return 1; + } + + /* Get the page size of the indirect table. */ + vsd_addr = vsd & VSD_ADDRESS_MASK; + vsd = ldq_be_dma(&address_space_memory, vsd_addr); + + if (!(vsd & VSD_ADDRESS_MASK)) { +#ifdef XIVE_DEBUG + xive_error(xive, "VST: invalid %s entry %x !?", info->name, idx); +#endif + return 0; + } + + page_shift = GETFIELD(VSD_TSIZE, vsd) + 12; + + if (!pnv_xive_vst_page_size_allowed(page_shift)) { + xive_error(xive, "VST: invalid %s page shift %d", info->name, + page_shift); + return 0; + } + + return (1ull << page_shift) / info->size; } /* @@ -598,6 +691,7 @@ static void pnv_xive_vst_set_exclusive(PnvXive *xive, uint8_t type, XiveSource *xsrc = &xive->ipi_source; const XiveVstInfo *info = &vst_infos[type]; uint32_t page_shift = GETFIELD(VSD_TSIZE, vsd) + 12; + uint64_t vst_tsize = 1ull << page_shift; uint64_t vst_addr = vsd & VSD_ADDRESS_MASK; /* Basic checks */ @@ -633,11 +727,16 @@ static void pnv_xive_vst_set_exclusive(PnvXive *xive, uint8_t type, case VST_TSEL_EQDT: /* - * Backing store pages for the END. Compute the number of ENDs - * provisioned by FW and resize the END ESB window accordingly. + * Backing store pages for the END. + * + * If the table is direct, we can compute the number of PQ + * entries provisioned by FW (such as skiboot) and resize the + * END ESB window accordingly. */ - memory_region_set_size(&end_xsrc->esb_mmio, pnv_xive_nr_ends(xive) * - (1ull << (end_xsrc->esb_shift + 1))); + if (!(VSD_INDIRECT & vsd)) { + memory_region_set_size(&end_xsrc->esb_mmio, (vst_tsize / info->size) + * (1ull << xsrc->esb_shift)); + } memory_region_add_subregion(&xive->end_edt_mmio, 0, &end_xsrc->esb_mmio); break; @@ -646,11 +745,16 @@ static void pnv_xive_vst_set_exclusive(PnvXive *xive, uint8_t type, /* * Backing store pages for the source PQ bits. The model does * not use these PQ bits backed in RAM because the XiveSource - * model has its own. Compute the number of IRQs provisioned - * by FW and resize the IPI ESB window accordingly. + * model has its own. + * + * If the table is direct, we can compute the number of PQ + * entries provisioned by FW (such as skiboot) and resize the + * ESB window accordingly. */ - memory_region_set_size(&xsrc->esb_mmio, pnv_xive_nr_ipis(xive) * - (1ull << xsrc->esb_shift)); + if (!(VSD_INDIRECT & vsd)) { + memory_region_set_size(&xsrc->esb_mmio, vst_tsize * SBE_PER_BYTE + * (1ull << xsrc->esb_shift)); + } memory_region_add_subregion(&xive->ipi_edt_mmio, 0, &xsrc->esb_mmio); break; @@ -789,20 +893,7 @@ static void pnv_xive_ic_reg_write(void *opaque, hwaddr offset, case PC_TCTXT_CFG: /* * TODO: block group support - * - * PC_TCTXT_CFG_BLKGRP_EN - * PC_TCTXT_CFG_HARD_CHIPID_BLK : - * Moves the chipid into block field for hardwired CAM compares. - * Block offset value is adjusted to 0b0..01 & ThrdId - * - * Will require changes in xive_presenter_tctx_match(). I am - * not sure how to handle that yet. */ - - /* Overrides hardwired chip ID with the chip ID field */ - if (val & PC_TCTXT_CHIPID_OVERRIDE) { - xive->tctx_chipid = GETFIELD(PC_TCTXT_CHIPID, val); - } break; case PC_TCTXT_TRACK: /* @@ -1349,12 +1440,13 @@ static const MemoryRegionOps pnv_xive_ic_lsi_ops = { */ /* - * When the TIMA is accessed from the indirect page, the thread id - * (PIR) has to be configured in the IC registers before. This is used - * for resets and for debug purpose also. + * When the TIMA is accessed from the indirect page, the thread id of + * the target CPU is configured in the PC_TCTXT_INDIR0 register before + * use. This is used for resets and for debug purpose also. */ static XiveTCTX *pnv_xive_get_indirect_tctx(PnvXive *xive) { + PnvChip *chip = xive->chip; uint64_t tctxt_indir = xive->regs[PC_TCTXT_INDIR0 >> 3]; PowerPCCPU *cpu = NULL; int pir; @@ -1364,15 +1456,15 @@ static XiveTCTX *pnv_xive_get_indirect_tctx(PnvXive *xive) return NULL; } - pir = GETFIELD(PC_TCTXT_INDIR_THRDID, tctxt_indir) & 0xff; - cpu = ppc_get_vcpu_by_pir(pir); + pir = (chip->chip_id << 8) | GETFIELD(PC_TCTXT_INDIR_THRDID, tctxt_indir); + cpu = pnv_chip_find_cpu(chip, pir); if (!cpu) { xive_error(xive, "IC: invalid PIR %x for indirect access", pir); return NULL; } /* Check that HW thread is XIVE enabled */ - if (!(xive->regs[PC_THREAD_EN_REG0 >> 3] & PPC_BIT(pir & 0x3f))) { + if (!pnv_xive_is_cpu_enabled(xive, cpu)) { xive_error(xive, "IC: CPU %x is not enabled", pir); } @@ -1384,7 +1476,7 @@ static void xive_tm_indirect_write(void *opaque, hwaddr offset, { XiveTCTX *tctx = pnv_xive_get_indirect_tctx(PNV_XIVE(opaque)); - xive_tctx_tm_write(tctx, offset, value, size); + xive_tctx_tm_write(XIVE_PRESENTER(opaque), tctx, offset, value, size); } static uint64_t xive_tm_indirect_read(void *opaque, hwaddr offset, @@ -1392,7 +1484,7 @@ static uint64_t xive_tm_indirect_read(void *opaque, hwaddr offset, { XiveTCTX *tctx = pnv_xive_get_indirect_tctx(PNV_XIVE(opaque)); - return xive_tctx_tm_read(tctx, offset, size); + return xive_tctx_tm_read(XIVE_PRESENTER(opaque), tctx, offset, size); } static const MemoryRegionOps xive_tm_indirect_ops = { @@ -1409,6 +1501,39 @@ static const MemoryRegionOps xive_tm_indirect_ops = { }, }; +static void pnv_xive_tm_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + PowerPCCPU *cpu = POWERPC_CPU(current_cpu); + PnvXive *xive = pnv_xive_tm_get_xive(cpu); + XiveTCTX *tctx = XIVE_TCTX(pnv_cpu_state(cpu)->intc); + + xive_tctx_tm_write(XIVE_PRESENTER(xive), tctx, offset, value, size); +} + +static uint64_t pnv_xive_tm_read(void *opaque, hwaddr offset, unsigned size) +{ + PowerPCCPU *cpu = POWERPC_CPU(current_cpu); + PnvXive *xive = pnv_xive_tm_get_xive(cpu); + XiveTCTX *tctx = XIVE_TCTX(pnv_cpu_state(cpu)->intc); + + return xive_tctx_tm_read(XIVE_PRESENTER(xive), tctx, offset, size); +} + +const MemoryRegionOps pnv_xive_tm_ops = { + .read = pnv_xive_tm_read, + .write = pnv_xive_tm_write, + .endianness = DEVICE_BIG_ENDIAN, + .valid = { + .min_access_size = 1, + .max_access_size = 8, + }, + .impl = { + .min_access_size = 1, + .max_access_size = 8, + }, +}; + /* * Interrupt controller XSCOM region. */ @@ -1574,23 +1699,40 @@ static const MemoryRegionOps pnv_xive_pc_ops = { }, }; +static void xive_nvt_pic_print_info(XiveNVT *nvt, uint32_t nvt_idx, + Monitor *mon) +{ + uint8_t eq_blk = xive_get_field32(NVT_W1_EQ_BLOCK, nvt->w1); + uint32_t eq_idx = xive_get_field32(NVT_W1_EQ_INDEX, nvt->w1); + + if (!xive_nvt_is_valid(nvt)) { + return; + } + + monitor_printf(mon, " %08x end:%02x/%04x IPB:%02x\n", nvt_idx, + eq_blk, eq_idx, + xive_get_field32(NVT_W4_IPB, nvt->w4)); +} + void pnv_xive_pic_print_info(PnvXive *xive, Monitor *mon) { XiveRouter *xrtr = XIVE_ROUTER(xive); - uint8_t blk = xive->chip->chip_id; + uint8_t blk = pnv_xive_block_id(xive); + uint8_t chip_id = xive->chip->chip_id; uint32_t srcno0 = XIVE_EAS(blk, 0); - uint32_t nr_ipis = pnv_xive_nr_ipis(xive); - uint32_t nr_ends = pnv_xive_nr_ends(xive); + uint32_t nr_ipis = pnv_xive_nr_ipis(xive, blk); XiveEAS eas; XiveEND end; + XiveNVT nvt; int i; + uint64_t xive_nvt_per_subpage; - monitor_printf(mon, "XIVE[%x] Source %08x .. %08x\n", blk, srcno0, - srcno0 + nr_ipis - 1); + monitor_printf(mon, "XIVE[%x] #%d Source %08x .. %08x\n", chip_id, blk, + srcno0, srcno0 + nr_ipis - 1); xive_source_pic_print_info(&xive->ipi_source, srcno0, mon); - monitor_printf(mon, "XIVE[%x] EAT %08x .. %08x\n", blk, srcno0, - srcno0 + nr_ipis - 1); + monitor_printf(mon, "XIVE[%x] #%d EAT %08x .. %08x\n", chip_id, blk, + srcno0, srcno0 + nr_ipis - 1); for (i = 0; i < nr_ipis; i++) { if (xive_router_get_eas(xrtr, blk, i, &eas)) { break; @@ -1600,21 +1742,25 @@ void pnv_xive_pic_print_info(PnvXive *xive, Monitor *mon) } } - monitor_printf(mon, "XIVE[%x] ENDT %08x .. %08x\n", blk, 0, nr_ends - 1); - for (i = 0; i < nr_ends; i++) { - if (xive_router_get_end(xrtr, blk, i, &end)) { - break; - } - xive_end_pic_print_info(&end, i, mon); + monitor_printf(mon, "XIVE[%x] #%d ENDT\n", chip_id, blk); + i = 0; + while (!xive_router_get_end(xrtr, blk, i, &end)) { + xive_end_pic_print_info(&end, i++, mon); } - monitor_printf(mon, "XIVE[%x] END Escalation %08x .. %08x\n", blk, 0, - nr_ends - 1); - for (i = 0; i < nr_ends; i++) { - if (xive_router_get_end(xrtr, blk, i, &end)) { - break; + monitor_printf(mon, "XIVE[%x] #%d END Escalation EAT\n", chip_id, blk); + i = 0; + while (!xive_router_get_end(xrtr, blk, i, &end)) { + xive_end_eas_pic_print_info(&end, i++, mon); + } + + monitor_printf(mon, "XIVE[%x] #%d NVTT %08x .. %08x\n", chip_id, blk, + 0, XIVE_NVT_COUNT - 1); + xive_nvt_per_subpage = pnv_xive_vst_per_subpage(xive, VST_TSEL_VPDT); + for (i = 0; i < XIVE_NVT_COUNT; i += xive_nvt_per_subpage) { + while (!xive_router_get_nvt(xrtr, blk, i, &nvt)) { + xive_nvt_pic_print_info(&nvt, i++, mon); } - xive_end_eas_pic_print_info(&end, i, mon); } } @@ -1624,12 +1770,6 @@ static void pnv_xive_reset(void *dev) XiveSource *xsrc = &xive->ipi_source; XiveENDSource *end_xsrc = &xive->end_source; - /* - * Use the PnvChip id to identify the XIVE interrupt controller. - * It can be overriden by configuration at runtime. - */ - xive->tctx_chipid = xive->chip->chip_id; - /* Default page size (Should be changed at runtime to 64k) */ xive->ic_shift = xive->vc_shift = xive->pc_shift = 12; @@ -1672,20 +1812,18 @@ static void pnv_xive_init(Object *obj) static void pnv_xive_realize(DeviceState *dev, Error **errp) { PnvXive *xive = PNV_XIVE(dev); + PnvXiveClass *pxc = PNV_XIVE_GET_CLASS(dev); XiveSource *xsrc = &xive->ipi_source; XiveENDSource *end_xsrc = &xive->end_source; Error *local_err = NULL; - Object *obj; - obj = object_property_get_link(OBJECT(dev), "chip", &local_err); - if (!obj) { + pxc->parent_realize(dev, &local_err); + if (local_err) { error_propagate(errp, local_err); - error_prepend(errp, "required link 'chip' not found: "); return; } - /* The PnvChip id identifies the XIVE interrupt controller. */ - xive->chip = PNV_CHIP(obj); + assert(xive->chip); /* * The XiveSource and XiveENDSource objects are realized with the @@ -1695,8 +1833,8 @@ static void pnv_xive_realize(DeviceState *dev, Error **errp) */ object_property_set_int(OBJECT(xsrc), PNV_XIVE_NR_IRQS, "nr-irqs", &error_fatal); - object_property_add_const_link(OBJECT(xsrc), "xive", OBJECT(xive), - &error_fatal); + object_property_set_link(OBJECT(xsrc), OBJECT(xive), "xive", + &error_abort); object_property_set_bool(OBJECT(xsrc), true, "realized", &local_err); if (local_err) { error_propagate(errp, local_err); @@ -1705,8 +1843,8 @@ static void pnv_xive_realize(DeviceState *dev, Error **errp) object_property_set_int(OBJECT(end_xsrc), PNV_XIVE_NR_ENDS, "nr-ends", &error_fatal); - object_property_add_const_link(OBJECT(end_xsrc), "xive", OBJECT(xive), - &error_fatal); + object_property_set_link(OBJECT(end_xsrc), OBJECT(xive), "xive", + &error_abort); object_property_set_bool(OBJECT(end_xsrc), true, "realized", &local_err); if (local_err) { error_propagate(errp, local_err); @@ -1766,7 +1904,7 @@ static void pnv_xive_realize(DeviceState *dev, Error **errp) "xive-pc", PNV9_XIVE_PC_SIZE); /* Thread Interrupt Management Area (Direct) */ - memory_region_init_io(&xive->tm_mmio, OBJECT(xive), &xive_tm_ops, + memory_region_init_io(&xive->tm_mmio, OBJECT(xive), &pnv_xive_tm_ops, xive, "xive-tima", PNV9_XIVE_TM_SIZE); qemu_register_reset(pnv_xive_reset, dev); @@ -1800,6 +1938,8 @@ static Property pnv_xive_properties[] = { DEFINE_PROP_UINT64("vc-bar", PnvXive, vc_base, 0), DEFINE_PROP_UINT64("pc-bar", PnvXive, pc_base, 0), DEFINE_PROP_UINT64("tm-bar", PnvXive, tm_base, 0), + /* The PnvChip id identifies the XIVE interrupt controller. */ + DEFINE_PROP_LINK("chip", PnvXive, chip, TYPE_PNV_CHIP, PnvChip *), DEFINE_PROP_END_OF_LIST(), }; @@ -1809,21 +1949,25 @@ static void pnv_xive_class_init(ObjectClass *klass, void *data) PnvXScomInterfaceClass *xdc = PNV_XSCOM_INTERFACE_CLASS(klass); XiveRouterClass *xrc = XIVE_ROUTER_CLASS(klass); XiveNotifierClass *xnc = XIVE_NOTIFIER_CLASS(klass); + XivePresenterClass *xpc = XIVE_PRESENTER_CLASS(klass); + PnvXiveClass *pxc = PNV_XIVE_CLASS(klass); xdc->dt_xscom = pnv_xive_dt_xscom; dc->desc = "PowerNV XIVE Interrupt Controller"; + device_class_set_parent_realize(dc, pnv_xive_realize, &pxc->parent_realize); dc->realize = pnv_xive_realize; - dc->props = pnv_xive_properties; + device_class_set_props(dc, pnv_xive_properties); xrc->get_eas = pnv_xive_get_eas; xrc->get_end = pnv_xive_get_end; xrc->write_end = pnv_xive_write_end; xrc->get_nvt = pnv_xive_get_nvt; xrc->write_nvt = pnv_xive_write_nvt; - xrc->get_tctx = pnv_xive_get_tctx; + xrc->get_block_id = pnv_xive_get_block_id; xnc->notify = pnv_xive_notify; + xpc->match_nvt = pnv_xive_match_nvt; }; static const TypeInfo pnv_xive_info = { @@ -1832,6 +1976,7 @@ static const TypeInfo pnv_xive_info = { .instance_init = pnv_xive_init, .instance_size = sizeof(PnvXive), .class_init = pnv_xive_class_init, + .class_size = sizeof(PnvXiveClass), .interfaces = (InterfaceInfo[]) { { TYPE_PNV_XSCOM_INTERFACE }, { } diff --git a/hw/intc/s390_flic.c b/hw/intc/s390_flic.c index a159c0c7cf..5f290f5410 100644 --- a/hw/intc/s390_flic.c +++ b/hw/intc/s390_flic.c @@ -427,7 +427,7 @@ static void s390_flic_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); - dc->props = s390_flic_common_properties; + device_class_set_props(dc, s390_flic_common_properties); dc->realize = s390_flic_common_realize; } diff --git a/hw/intc/s390_flic_kvm.c b/hw/intc/s390_flic_kvm.c index c9ee80eaae..a306b26faa 100644 --- a/hw/intc/s390_flic_kvm.c +++ b/hw/intc/s390_flic_kvm.c @@ -331,6 +331,10 @@ static int kvm_s390_add_adapter_routes(S390FLICState *fs, int ret, i; uint64_t ind_offset = routes->adapter.ind_offset; + if (!kvm_gsi_routing_enabled()) { + return -ENOSYS; + } + for (i = 0; i < routes->num_routes; i++) { ret = kvm_irqchip_add_adapter_route(kvm_state, &routes->adapter); if (ret < 0) { @@ -358,6 +362,10 @@ static void kvm_s390_release_adapter_routes(S390FLICState *fs, { int i; + if (!kvm_gsi_routing_enabled()) { + return; + } + for (i = 0; i < routes->num_routes; i++) { if (routes->gsi[i] >= 0) { kvm_irqchip_release_virq(kvm_state, routes->gsi[i]); @@ -439,17 +447,14 @@ static int kvm_flic_load(QEMUFile *f, void *opaque, size_t size, count = qemu_get_be64(f); len = count * sizeof(struct kvm_s390_irq); if (count == FLIC_FAILED) { - r = -EINVAL; - goto out; + return -EINVAL; } if (count == 0) { - r = 0; - goto out; + return 0; } buf = g_try_malloc0(len); if (!buf) { - r = -ENOMEM; - goto out; + return -ENOMEM; } if (qemu_get_buffer(f, (uint8_t *) buf, len) != len) { @@ -460,7 +465,6 @@ static int kvm_flic_load(QEMUFile *f, void *opaque, size_t size, out_free: g_free(buf); -out: return r; } @@ -582,20 +586,21 @@ static void kvm_s390_flic_realize(DeviceState *dev, Error **errp) struct kvm_create_device cd = {0}; struct kvm_device_attr test_attr = {0}; int ret; - Error *errp_local = NULL; + Error *err = NULL; - KVM_S390_FLIC_GET_CLASS(dev)->parent_realize(dev, &errp_local); - if (errp_local) { - goto fail; + KVM_S390_FLIC_GET_CLASS(dev)->parent_realize(dev, &err); + if (err) { + error_propagate(errp, err); + return; } flic_state->fd = -1; cd.type = KVM_DEV_TYPE_FLIC; ret = kvm_vm_ioctl(kvm_state, KVM_CREATE_DEVICE, &cd); if (ret < 0) { - error_setg_errno(&errp_local, errno, "Creating the KVM device failed"); + error_setg_errno(errp, errno, "Creating the KVM device failed"); trace_flic_create_device(errno); - goto fail; + return; } flic_state->fd = cd.fd; @@ -603,9 +608,6 @@ static void kvm_s390_flic_realize(DeviceState *dev, Error **errp) test_attr.group = KVM_DEV_FLIC_CLEAR_IO_IRQ; flic_state->clear_io_supported = !ioctl(flic_state->fd, KVM_HAS_DEVICE_ATTR, test_attr); - return; -fail: - error_propagate(errp, errp_local); } static void kvm_s390_flic_reset(DeviceState *dev) diff --git a/hw/intc/spapr_xive.c b/hw/intc/spapr_xive.c index 9cb8d38a3b..6608d7220a 100644 --- a/hw/intc/spapr_xive.c +++ b/hw/intc/spapr_xive.c @@ -205,6 +205,35 @@ void spapr_xive_mmio_set_enabled(SpaprXive *xive, bool enable) memory_region_set_enabled(&xive->end_source.esb_mmio, false); } +static void spapr_xive_tm_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + XiveTCTX *tctx = spapr_cpu_state(POWERPC_CPU(current_cpu))->tctx; + + xive_tctx_tm_write(XIVE_PRESENTER(opaque), tctx, offset, value, size); +} + +static uint64_t spapr_xive_tm_read(void *opaque, hwaddr offset, unsigned size) +{ + XiveTCTX *tctx = spapr_cpu_state(POWERPC_CPU(current_cpu))->tctx; + + return xive_tctx_tm_read(XIVE_PRESENTER(opaque), tctx, offset, size); +} + +const MemoryRegionOps spapr_xive_tm_ops = { + .read = spapr_xive_tm_read, + .write = spapr_xive_tm_write, + .endianness = DEVICE_BIG_ENDIAN, + .valid = { + .min_access_size = 1, + .max_access_size = 8, + }, + .impl = { + .min_access_size = 1, + .max_access_size = 8, + }, +}; + static void spapr_xive_end_reset(XiveEND *end) { memset(end, 0, sizeof(*end)); @@ -257,10 +286,17 @@ static void spapr_xive_instance_init(Object *obj) static void spapr_xive_realize(DeviceState *dev, Error **errp) { SpaprXive *xive = SPAPR_XIVE(dev); + SpaprXiveClass *sxc = SPAPR_XIVE_GET_CLASS(xive); XiveSource *xsrc = &xive->source; XiveENDSource *end_xsrc = &xive->end_source; Error *local_err = NULL; + sxc->parent_realize(dev, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + if (!xive->nr_irqs) { error_setg(errp, "Number of interrupt needs to be greater 0"); return; @@ -276,8 +312,8 @@ static void spapr_xive_realize(DeviceState *dev, Error **errp) */ object_property_set_int(OBJECT(xsrc), xive->nr_irqs, "nr-irqs", &error_fatal); - object_property_add_const_link(OBJECT(xsrc), "xive", OBJECT(xive), - &error_fatal); + object_property_set_link(OBJECT(xsrc), OBJECT(xive), "xive", + &error_abort); object_property_set_bool(OBJECT(xsrc), true, "realized", &local_err); if (local_err) { error_propagate(errp, local_err); @@ -290,8 +326,8 @@ static void spapr_xive_realize(DeviceState *dev, Error **errp) */ object_property_set_int(OBJECT(end_xsrc), xive->nr_irqs, "nr-ends", &error_fatal); - object_property_add_const_link(OBJECT(end_xsrc), "xive", OBJECT(xive), - &error_fatal); + object_property_set_link(OBJECT(end_xsrc), OBJECT(xive), "xive", + &error_abort); object_property_set_bool(OBJECT(end_xsrc), true, "realized", &local_err); if (local_err) { error_propagate(errp, local_err); @@ -314,8 +350,8 @@ static void spapr_xive_realize(DeviceState *dev, Error **errp) qemu_register_reset(spapr_xive_reset, dev); /* TIMA initialization */ - memory_region_init_io(&xive->tm_mmio, OBJECT(xive), &xive_tm_ops, xive, - "xive.tima", 4ull << TM_SHIFT); + memory_region_init_io(&xive->tm_mmio, OBJECT(xive), &spapr_xive_tm_ops, + xive, "xive.tima", 4ull << TM_SHIFT); sysbus_init_mmio(SYS_BUS_DEVICE(xive), &xive->tm_mmio); /* @@ -398,11 +434,55 @@ static int spapr_xive_write_nvt(XiveRouter *xrtr, uint8_t nvt_blk, g_assert_not_reached(); } -static XiveTCTX *spapr_xive_get_tctx(XiveRouter *xrtr, CPUState *cs) +static int spapr_xive_match_nvt(XivePresenter *xptr, uint8_t format, + uint8_t nvt_blk, uint32_t nvt_idx, + bool cam_ignore, uint8_t priority, + uint32_t logic_serv, XiveTCTXMatch *match) { - PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUState *cs; + int count = 0; - return spapr_cpu_state(cpu)->tctx; + CPU_FOREACH(cs) { + PowerPCCPU *cpu = POWERPC_CPU(cs); + XiveTCTX *tctx = spapr_cpu_state(cpu)->tctx; + int ring; + + /* + * Skip partially initialized vCPUs. This can happen when + * vCPUs are hotplugged. + */ + if (!tctx) { + continue; + } + + /* + * Check the thread context CAM lines and record matches. + */ + ring = xive_presenter_tctx_match(xptr, tctx, format, nvt_blk, nvt_idx, + cam_ignore, logic_serv); + /* + * Save the matching thread interrupt context and follow on to + * check for duplicates which are invalid. + */ + if (ring != -1) { + if (match->tctx) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: already found a thread " + "context NVT %x/%x\n", nvt_blk, nvt_idx); + return -1; + } + + match->ring = ring; + match->tctx = tctx; + count++; + } + } + + return count; +} + +static uint8_t spapr_xive_get_block_id(XiveRouter *xrtr) +{ + return SPAPR_XIVE_BLOCK_ID; } static const VMStateDescription vmstate_spapr_xive_end = { @@ -521,7 +601,7 @@ static int spapr_xive_cpu_intc_create(SpaprInterruptController *intc, Object *obj; SpaprCpuState *spapr_cpu = spapr_cpu_state(cpu); - obj = xive_tctx_create(OBJECT(cpu), XIVE_ROUTER(xive), errp); + obj = xive_tctx_create(OBJECT(cpu), XIVE_PRESENTER(xive), errp); if (!obj) { return -1; } @@ -597,8 +677,8 @@ static void spapr_xive_dt(SpaprInterruptController *intc, uint32_t nr_servers, uint64_t timas[2 * 2]; /* Interrupt number ranges for the IPIs */ uint32_t lisn_ranges[] = { - cpu_to_be32(0), - cpu_to_be32(nr_servers), + cpu_to_be32(SPAPR_IRQ_IPI), + cpu_to_be32(SPAPR_IRQ_IPI + nr_servers), }; /* * EQ size - the sizes of pages supported by the system 4K, 64K, @@ -651,12 +731,14 @@ static void spapr_xive_dt(SpaprInterruptController *intc, uint32_t nr_servers, plat_res_int_priorities, sizeof(plat_res_int_priorities))); } -static int spapr_xive_activate(SpaprInterruptController *intc, Error **errp) +static int spapr_xive_activate(SpaprInterruptController *intc, + uint32_t nr_servers, Error **errp) { SpaprXive *xive = SPAPR_XIVE(intc); if (kvm_enabled()) { - int rc = spapr_irq_init_kvm(kvmppc_xive_connect, intc, errp); + int rc = spapr_irq_init_kvm(kvmppc_xive_connect, intc, nr_servers, + errp); if (rc < 0) { return rc; } @@ -684,10 +766,13 @@ static void spapr_xive_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); XiveRouterClass *xrc = XIVE_ROUTER_CLASS(klass); SpaprInterruptControllerClass *sicc = SPAPR_INTC_CLASS(klass); + XivePresenterClass *xpc = XIVE_PRESENTER_CLASS(klass); + SpaprXiveClass *sxc = SPAPR_XIVE_CLASS(klass); dc->desc = "sPAPR XIVE Interrupt Controller"; - dc->props = spapr_xive_properties; - dc->realize = spapr_xive_realize; + device_class_set_props(dc, spapr_xive_properties); + device_class_set_parent_realize(dc, spapr_xive_realize, + &sxc->parent_realize); dc->vmsd = &vmstate_spapr_xive; xrc->get_eas = spapr_xive_get_eas; @@ -695,7 +780,7 @@ static void spapr_xive_class_init(ObjectClass *klass, void *data) xrc->write_end = spapr_xive_write_end; xrc->get_nvt = spapr_xive_get_nvt; xrc->write_nvt = spapr_xive_write_nvt; - xrc->get_tctx = spapr_xive_get_tctx; + xrc->get_block_id = spapr_xive_get_block_id; sicc->activate = spapr_xive_activate; sicc->deactivate = spapr_xive_deactivate; @@ -708,6 +793,8 @@ static void spapr_xive_class_init(ObjectClass *klass, void *data) sicc->print_info = spapr_xive_print_info; sicc->dt = spapr_xive_dt; sicc->post_load = spapr_xive_post_load; + + xpc->match_nvt = spapr_xive_match_nvt; } static const TypeInfo spapr_xive_info = { @@ -716,6 +803,7 @@ static const TypeInfo spapr_xive_info = { .instance_init = spapr_xive_instance_init, .instance_size = sizeof(SpaprXive), .class_init = spapr_xive_class_init, + .class_size = sizeof(SpaprXiveClass), .interfaces = (InterfaceInfo[]) { { TYPE_SPAPR_INTC }, { } @@ -1678,7 +1766,7 @@ static target_ulong h_int_reset(PowerPCCPU *cpu, return H_PARAMETER; } - device_reset(DEVICE(xive)); + device_legacy_reset(DEVICE(xive)); if (kvm_irqchip_in_kernel()) { Error *local_err = NULL; diff --git a/hw/intc/spapr_xive_kvm.c b/hw/intc/spapr_xive_kvm.c index 08012ac7cd..edb7ee0e74 100644 --- a/hw/intc/spapr_xive_kvm.c +++ b/hw/intc/spapr_xive_kvm.c @@ -75,7 +75,7 @@ static void kvm_cpu_disable_all(void) void kvmppc_xive_cpu_set_state(XiveTCTX *tctx, Error **errp) { - SpaprXive *xive = SPAPR_MACHINE(qdev_get_machine())->xive; + SpaprXive *xive = SPAPR_XIVE(tctx->xptr); uint64_t state[2]; int ret; @@ -97,7 +97,7 @@ void kvmppc_xive_cpu_set_state(XiveTCTX *tctx, Error **errp) void kvmppc_xive_cpu_get_state(XiveTCTX *tctx, Error **errp) { - SpaprXive *xive = SPAPR_MACHINE(qdev_get_machine())->xive; + SpaprXive *xive = SPAPR_XIVE(tctx->xptr); uint64_t state[2] = { 0 }; int ret; @@ -152,7 +152,7 @@ void kvmppc_xive_cpu_synchronize_state(XiveTCTX *tctx, Error **errp) void kvmppc_xive_cpu_connect(XiveTCTX *tctx, Error **errp) { - SpaprXive *xive = SPAPR_MACHINE(qdev_get_machine())->xive; + SpaprXive *xive = SPAPR_XIVE(tctx->xptr); unsigned long vcpu_id; int ret; @@ -171,8 +171,16 @@ void kvmppc_xive_cpu_connect(XiveTCTX *tctx, Error **errp) ret = kvm_vcpu_enable_cap(tctx->cs, KVM_CAP_PPC_IRQ_XIVE, 0, xive->fd, vcpu_id, 0); if (ret < 0) { - error_setg(errp, "XIVE: unable to connect CPU%ld to KVM device: %s", + Error *local_err = NULL; + + error_setg(&local_err, + "XIVE: unable to connect CPU%ld to KVM device: %s", vcpu_id, strerror(errno)); + if (errno == ENOSPC) { + error_append_hint(&local_err, "Try -smp maxcpus=N with N < %u\n", + MACHINE(qdev_get_machine())->smp.max_cpus); + } + error_propagate(errp, local_err); return; } @@ -354,32 +362,20 @@ static void kvmppc_xive_source_get_state(XiveSource *xsrc) void kvmppc_xive_source_set_irq(void *opaque, int srcno, int val) { XiveSource *xsrc = opaque; - SpaprXive *xive = SPAPR_XIVE(xsrc->xive); - struct kvm_irq_level args; - int rc; - /* The KVM XIVE device should be in use */ - assert(xive->fd != -1); - - args.irq = srcno; if (!xive_source_irq_is_lsi(xsrc, srcno)) { if (!val) { return; } - args.level = KVM_INTERRUPT_SET; } else { if (val) { xsrc->status[srcno] |= XIVE_STATUS_ASSERTED; - args.level = KVM_INTERRUPT_SET_LEVEL; } else { xsrc->status[srcno] &= ~XIVE_STATUS_ASSERTED; - args.level = KVM_INTERRUPT_UNSET; } } - rc = kvm_vm_ioctl(kvm_state, KVM_IRQ_LINE, &args); - if (rc < 0) { - error_report("XIVE: kvm_irq_line() failed : %s", strerror(errno)); - } + + xive_esb_trigger(xsrc, srcno); } /* @@ -740,7 +736,8 @@ static void *kvmppc_xive_mmap(SpaprXive *xive, int pgoff, size_t len, * All the XIVE memory regions are now backed by mappings from the KVM * XIVE device. */ -int kvmppc_xive_connect(SpaprInterruptController *intc, Error **errp) +int kvmppc_xive_connect(SpaprInterruptController *intc, uint32_t nr_servers, + Error **errp) { SpaprXive *xive = SPAPR_XIVE(intc); XiveSource *xsrc = &xive->source; @@ -769,6 +766,16 @@ int kvmppc_xive_connect(SpaprInterruptController *intc, Error **errp) return -1; } + /* Tell KVM about the # of VCPUs we may have */ + if (kvm_device_check_attr(xive->fd, KVM_DEV_XIVE_GRP_CTRL, + KVM_DEV_XIVE_NR_SERVERS)) { + if (kvm_device_access(xive->fd, KVM_DEV_XIVE_GRP_CTRL, + KVM_DEV_XIVE_NR_SERVERS, &nr_servers, true, + &local_err)) { + goto fail; + } + } + /* * 1. Source ESB pages - KVM mapping */ diff --git a/hw/intc/xics.c b/hw/intc/xics.c index e7ac9ba618..c5d507e707 100644 --- a/hw/intc/xics.c +++ b/hw/intc/xics.c @@ -217,7 +217,7 @@ void icp_eoi(ICPState *icp, uint32_t xirr) } } -static void icp_irq(ICSState *ics, int server, int nr, uint8_t priority) +void icp_irq(ICSState *ics, int server, int nr, uint8_t priority) { ICPState *icp = xics_icp_get(ics->xics, server); @@ -289,9 +289,6 @@ void icp_reset(ICPState *icp) icp->pending_priority = 0xff; icp->mfrr = 0xff; - /* Make all outputs are deasserted */ - qemu_set_irq(icp->output, 0); - if (kvm_irqchip_in_kernel()) { Error *local_err = NULL; @@ -305,33 +302,13 @@ void icp_reset(ICPState *icp) static void icp_realize(DeviceState *dev, Error **errp) { ICPState *icp = ICP(dev); - PowerPCCPU *cpu; CPUPPCState *env; - Object *obj; Error *err = NULL; - obj = object_property_get_link(OBJECT(dev), ICP_PROP_XICS, &err); - if (!obj) { - error_propagate_prepend(errp, err, - "required link '" ICP_PROP_XICS - "' not found: "); - return; - } + assert(icp->xics); + assert(icp->cs); - icp->xics = XICS_FABRIC(obj); - - obj = object_property_get_link(OBJECT(dev), ICP_PROP_CPU, &err); - if (!obj) { - error_propagate_prepend(errp, err, - "required link '" ICP_PROP_CPU - "' not found: "); - return; - } - - cpu = POWERPC_CPU(obj); - icp->cs = CPU(obj); - - env = &cpu->env; + env = &POWERPC_CPU(icp->cs)->env; switch (PPC_INPUT(env)) { case PPC_FLAGS_INPUT_POWER7: icp->output = env->irq_inputs[POWER7_INPUT_INT]; @@ -368,12 +345,20 @@ static void icp_unrealize(DeviceState *dev, Error **errp) vmstate_unregister(NULL, &vmstate_icp_server, icp); } +static Property icp_properties[] = { + DEFINE_PROP_LINK(ICP_PROP_XICS, ICPState, xics, TYPE_XICS_FABRIC, + XICSFabric *), + DEFINE_PROP_LINK(ICP_PROP_CPU, ICPState, cs, TYPE_CPU, CPUState *), + DEFINE_PROP_END_OF_LIST(), +}; + static void icp_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = icp_realize; dc->unrealize = icp_unrealize; + device_class_set_props(dc, icp_properties); /* * Reason: part of XICS interrupt controller, needs to be wired up * by icp_create(). @@ -397,11 +382,8 @@ Object *icp_create(Object *cpu, const char *type, XICSFabric *xi, Error **errp) obj = object_new(type); object_property_add_child(cpu, type, obj, &error_abort); object_unref(obj); - object_ref(OBJECT(xi)); - object_property_add_const_link(obj, ICP_PROP_XICS, OBJECT(xi), - &error_abort); - object_ref(cpu); - object_property_add_const_link(obj, ICP_PROP_CPU, cpu, &error_abort); + object_property_set_link(obj, OBJECT(xi), ICP_PROP_XICS, &error_abort); + object_property_set_link(obj, cpu, ICP_PROP_CPU, &error_abort); object_property_set_bool(obj, true, "realized", &local_err); if (local_err) { object_unparent(obj); @@ -416,8 +398,6 @@ void icp_destroy(ICPState *icp) { Object *obj = OBJECT(icp); - object_unref(object_property_get_link(obj, ICP_PROP_CPU, &error_abort)); - object_unref(object_property_get_link(obj, ICP_PROP_XICS, &error_abort)); object_unparent(obj); } @@ -532,8 +512,14 @@ void ics_write_xive(ICSState *ics, int srcno, int server, static void ics_reject(ICSState *ics, uint32_t nr) { + ICSStateClass *isc = ICS_GET_CLASS(ics); ICSIRQState *irq = ics->irqs + nr - ics->offset; + if (isc->reject) { + isc->reject(ics, nr); + return; + } + trace_xics_ics_reject(nr, nr - ics->offset); if (irq->flags & XICS_FLAGS_IRQ_MSI) { irq->status |= XICS_STATUS_REJECTED; @@ -544,8 +530,14 @@ static void ics_reject(ICSState *ics, uint32_t nr) void ics_resend(ICSState *ics) { + ICSStateClass *isc = ICS_GET_CLASS(ics); int i; + if (isc->resend) { + isc->resend(ics); + return; + } + for (i = 0; i < ics->nr_irqs; i++) { /* FIXME: filter by server#? */ if (ics->irqs[i].flags & XICS_FLAGS_IRQ_LSI) { @@ -609,17 +601,8 @@ static void ics_reset_handler(void *dev) static void ics_realize(DeviceState *dev, Error **errp) { ICSState *ics = ICS(dev); - Error *local_err = NULL; - Object *obj; - obj = object_property_get_link(OBJECT(dev), ICS_PROP_XICS, &local_err); - if (!obj) { - error_propagate_prepend(errp, local_err, - "required link '" ICS_PROP_XICS - "' not found: "); - return; - } - ics->xics = XICS_FABRIC(obj); + assert(ics->xics); if (!ics->nr_irqs) { error_setg(errp, "Number of interrupts needs to be greater 0"); @@ -699,6 +682,8 @@ static const VMStateDescription vmstate_ics = { static Property ics_properties[] = { DEFINE_PROP_UINT32("nr-irqs", ICSState, nr_irqs, 0), + DEFINE_PROP_LINK(ICS_PROP_XICS, ICSState, xics, TYPE_XICS_FABRIC, + XICSFabric *), DEFINE_PROP_END_OF_LIST(), }; @@ -707,7 +692,7 @@ static void ics_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = ics_realize; - dc->props = ics_properties; + device_class_set_props(dc, ics_properties); dc->reset = ics_reset; dc->vmsd = &vmstate_ics; /* diff --git a/hw/intc/xics_kvm.c b/hw/intc/xics_kvm.c index 954c424b36..8d6156578f 100644 --- a/hw/intc/xics_kvm.c +++ b/hw/intc/xics_kvm.c @@ -165,8 +165,15 @@ void icp_kvm_realize(DeviceState *dev, Error **errp) ret = kvm_vcpu_enable_cap(cs, KVM_CAP_IRQ_XICS, 0, kernel_xics_fd, vcpu_id); if (ret < 0) { - error_setg(errp, "Unable to connect CPU%ld to kernel XICS: %s", vcpu_id, - strerror(errno)); + Error *local_err = NULL; + + error_setg(&local_err, "Unable to connect CPU%ld to kernel XICS: %s", + vcpu_id, strerror(errno)); + if (errno == ENOSPC) { + error_append_hint(&local_err, "Try -smp maxcpus=N with N < %u\n", + MACHINE(qdev_get_machine())->smp.max_cpus); + } + error_propagate(errp, local_err); return; } enabled_icp = g_malloc(sizeof(*enabled_icp)); @@ -342,7 +349,8 @@ void ics_kvm_set_irq(ICSState *ics, int srcno, int val) } } -int xics_kvm_connect(SpaprInterruptController *intc, Error **errp) +int xics_kvm_connect(SpaprInterruptController *intc, uint32_t nr_servers, + Error **errp) { ICSState *ics = ICS_SPAPR(intc); int rc; @@ -398,6 +406,16 @@ int xics_kvm_connect(SpaprInterruptController *intc, Error **errp) goto fail; } + /* Tell KVM about the # of VCPUs we may have (POWER9 and newer only) */ + if (kvm_device_check_attr(rc, KVM_DEV_XICS_GRP_CTRL, + KVM_DEV_XICS_NR_SERVERS)) { + if (kvm_device_access(rc, KVM_DEV_XICS_GRP_CTRL, + KVM_DEV_XICS_NR_SERVERS, &nr_servers, true, + &local_err)) { + goto fail; + } + } + kernel_xics_fd = rc; kvm_kernel_irqchip = true; kvm_msi_via_irqfd_allowed = true; diff --git a/hw/intc/xics_spapr.c b/hw/intc/xics_spapr.c index b3705dab0e..8ae4f41459 100644 --- a/hw/intc/xics_spapr.c +++ b/hw/intc/xics_spapr.c @@ -422,10 +422,11 @@ static int xics_spapr_post_load(SpaprInterruptController *intc, int version_id) return 0; } -static int xics_spapr_activate(SpaprInterruptController *intc, Error **errp) +static int xics_spapr_activate(SpaprInterruptController *intc, + uint32_t nr_servers, Error **errp) { if (kvm_enabled()) { - return spapr_irq_init_kvm(xics_kvm_connect, intc, errp); + return spapr_irq_init_kvm(xics_kvm_connect, intc, nr_servers, errp); } return 0; } diff --git a/hw/intc/xilinx_intc.c b/hw/intc/xilinx_intc.c index e15cb42b36..3e65e68619 100644 --- a/hw/intc/xilinx_intc.c +++ b/hw/intc/xilinx_intc.c @@ -185,7 +185,7 @@ static void xilinx_intc_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->props = xilinx_intc_properties; + device_class_set_props(dc, xilinx_intc_properties); } static const TypeInfo xilinx_intc_info = { diff --git a/hw/intc/xive.c b/hw/intc/xive.c index 75dce82fb2..d6183f8ae4 100644 --- a/hw/intc/xive.c +++ b/hw/intc/xive.c @@ -47,12 +47,6 @@ static uint8_t ipb_to_pipr(uint8_t ibp) return ibp ? clz32((uint32_t)ibp << 24) : 0xff; } -static void ipb_update(uint8_t *regs, uint8_t priority) -{ - regs[TM_IPB] |= priority_to_ipb(priority); - regs[TM_PIPR] = ipb_to_pipr(regs[TM_IPB]); -} - static uint8_t exception_mask(uint8_t ring) { switch (ring) { @@ -135,6 +129,15 @@ static void xive_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr) xive_tctx_notify(tctx, ring); } +void xive_tctx_ipb_update(XiveTCTX *tctx, uint8_t ring, uint8_t ipb) +{ + uint8_t *regs = &tctx->regs[ring]; + + regs[TM_IPB] |= ipb; + regs[TM_PIPR] = ipb_to_pipr(regs[TM_IPB]); + xive_tctx_notify(tctx, ring); +} + static inline uint32_t xive_tctx_word2(uint8_t *ring) { return *((uint32_t *) &ring[TM_WORD2]); @@ -144,19 +147,20 @@ static inline uint32_t xive_tctx_word2(uint8_t *ring) * XIVE Thread Interrupt Management Area (TIMA) */ -static void xive_tm_set_hv_cppr(XiveTCTX *tctx, hwaddr offset, - uint64_t value, unsigned size) +static void xive_tm_set_hv_cppr(XivePresenter *xptr, XiveTCTX *tctx, + hwaddr offset, uint64_t value, unsigned size) { xive_tctx_set_cppr(tctx, TM_QW3_HV_PHYS, value & 0xff); } -static uint64_t xive_tm_ack_hv_reg(XiveTCTX *tctx, hwaddr offset, unsigned size) +static uint64_t xive_tm_ack_hv_reg(XivePresenter *xptr, XiveTCTX *tctx, + hwaddr offset, unsigned size) { return xive_tctx_accept(tctx, TM_QW3_HV_PHYS); } -static uint64_t xive_tm_pull_pool_ctx(XiveTCTX *tctx, hwaddr offset, - unsigned size) +static uint64_t xive_tm_pull_pool_ctx(XivePresenter *xptr, XiveTCTX *tctx, + hwaddr offset, unsigned size) { uint32_t qw2w2_prev = xive_tctx_word2(&tctx->regs[TM_QW2_HV_POOL]); uint32_t qw2w2; @@ -166,13 +170,14 @@ static uint64_t xive_tm_pull_pool_ctx(XiveTCTX *tctx, hwaddr offset, return qw2w2; } -static void xive_tm_vt_push(XiveTCTX *tctx, hwaddr offset, +static void xive_tm_vt_push(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, uint64_t value, unsigned size) { tctx->regs[TM_QW3_HV_PHYS + TM_WORD2] = value & 0xff; } -static uint64_t xive_tm_vt_poll(XiveTCTX *tctx, hwaddr offset, unsigned size) +static uint64_t xive_tm_vt_poll(XivePresenter *xptr, XiveTCTX *tctx, + hwaddr offset, unsigned size) { return tctx->regs[TM_QW3_HV_PHYS + TM_WORD2] & 0xff; } @@ -315,13 +320,14 @@ static uint64_t xive_tm_raw_read(XiveTCTX *tctx, hwaddr offset, unsigned size) * state changes (side effects) in addition to setting/returning the * interrupt management area context of the processor thread. */ -static uint64_t xive_tm_ack_os_reg(XiveTCTX *tctx, hwaddr offset, unsigned size) +static uint64_t xive_tm_ack_os_reg(XivePresenter *xptr, XiveTCTX *tctx, + hwaddr offset, unsigned size) { return xive_tctx_accept(tctx, TM_QW1_OS); } -static void xive_tm_set_os_cppr(XiveTCTX *tctx, hwaddr offset, - uint64_t value, unsigned size) +static void xive_tm_set_os_cppr(XivePresenter *xptr, XiveTCTX *tctx, + hwaddr offset, uint64_t value, unsigned size) { xive_tctx_set_cppr(tctx, TM_QW1_OS, value & 0xff); } @@ -330,24 +336,114 @@ static void xive_tm_set_os_cppr(XiveTCTX *tctx, hwaddr offset, * Adjust the IPB to allow a CPU to process event queues of other * priorities during one physical interrupt cycle. */ -static void xive_tm_set_os_pending(XiveTCTX *tctx, hwaddr offset, - uint64_t value, unsigned size) +static void xive_tm_set_os_pending(XivePresenter *xptr, XiveTCTX *tctx, + hwaddr offset, uint64_t value, unsigned size) { - ipb_update(&tctx->regs[TM_QW1_OS], value & 0xff); - xive_tctx_notify(tctx, TM_QW1_OS); + xive_tctx_ipb_update(tctx, TM_QW1_OS, priority_to_ipb(value & 0xff)); } -static uint64_t xive_tm_pull_os_ctx(XiveTCTX *tctx, hwaddr offset, - unsigned size) +static void xive_os_cam_decode(uint32_t cam, uint8_t *nvt_blk, + uint32_t *nvt_idx, bool *vo) { - uint32_t qw1w2_prev = xive_tctx_word2(&tctx->regs[TM_QW1_OS]); - uint32_t qw1w2; + if (nvt_blk) { + *nvt_blk = xive_nvt_blk(cam); + } + if (nvt_idx) { + *nvt_idx = xive_nvt_idx(cam); + } + if (vo) { + *vo = !!(cam & TM_QW1W2_VO); + } +} - qw1w2 = xive_set_field32(TM_QW1W2_VO, qw1w2_prev, 0); - memcpy(&tctx->regs[TM_QW1_OS + TM_WORD2], &qw1w2, 4); +static uint32_t xive_tctx_get_os_cam(XiveTCTX *tctx, uint8_t *nvt_blk, + uint32_t *nvt_idx, bool *vo) +{ + uint32_t qw1w2 = xive_tctx_word2(&tctx->regs[TM_QW1_OS]); + uint32_t cam = be32_to_cpu(qw1w2); + + xive_os_cam_decode(cam, nvt_blk, nvt_idx, vo); return qw1w2; } +static void xive_tctx_set_os_cam(XiveTCTX *tctx, uint32_t qw1w2) +{ + memcpy(&tctx->regs[TM_QW1_OS + TM_WORD2], &qw1w2, 4); +} + +static uint64_t xive_tm_pull_os_ctx(XivePresenter *xptr, XiveTCTX *tctx, + hwaddr offset, unsigned size) +{ + uint32_t qw1w2; + uint32_t qw1w2_new; + uint8_t nvt_blk; + uint32_t nvt_idx; + bool vo; + + qw1w2 = xive_tctx_get_os_cam(tctx, &nvt_blk, &nvt_idx, &vo); + + if (!vo) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: pulling invalid NVT %x/%x !?\n", + nvt_blk, nvt_idx); + } + + /* Invalidate CAM line */ + qw1w2_new = xive_set_field32(TM_QW1W2_VO, qw1w2, 0); + xive_tctx_set_os_cam(tctx, qw1w2_new); + return qw1w2; +} + +static void xive_tctx_need_resend(XiveRouter *xrtr, XiveTCTX *tctx, + uint8_t nvt_blk, uint32_t nvt_idx) +{ + XiveNVT nvt; + uint8_t ipb; + + /* + * Grab the associated NVT to pull the pending bits, and merge + * them with the IPB of the thread interrupt context registers + */ + if (xive_router_get_nvt(xrtr, nvt_blk, nvt_idx, &nvt)) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid NVT %x/%x\n", + nvt_blk, nvt_idx); + return; + } + + ipb = xive_get_field32(NVT_W4_IPB, nvt.w4); + + if (ipb) { + /* Reset the NVT value */ + nvt.w4 = xive_set_field32(NVT_W4_IPB, nvt.w4, 0); + xive_router_write_nvt(xrtr, nvt_blk, nvt_idx, &nvt, 4); + + /* Merge in current context */ + xive_tctx_ipb_update(tctx, TM_QW1_OS, ipb); + } +} + +/* + * Updating the OS CAM line can trigger a resend of interrupt + */ +static void xive_tm_push_os_ctx(XivePresenter *xptr, XiveTCTX *tctx, + hwaddr offset, uint64_t value, unsigned size) +{ + uint32_t cam = value; + uint32_t qw1w2 = cpu_to_be32(cam); + uint8_t nvt_blk; + uint32_t nvt_idx; + bool vo; + + xive_os_cam_decode(cam, &nvt_blk, &nvt_idx, &vo); + + /* First update the registers */ + xive_tctx_set_os_cam(tctx, qw1w2); + + /* Check the interrupt pending bits */ + if (vo) { + xive_tctx_need_resend(XIVE_ROUTER(xptr), tctx, nvt_blk, nvt_idx); + } +} + /* * Define a mapping of "special" operations depending on the TIMA page * offset and the size of the operation. @@ -356,9 +452,11 @@ typedef struct XiveTmOp { uint8_t page_offset; uint32_t op_offset; unsigned size; - void (*write_handler)(XiveTCTX *tctx, hwaddr offset, uint64_t value, - unsigned size); - uint64_t (*read_handler)(XiveTCTX *tctx, hwaddr offset, unsigned size); + void (*write_handler)(XivePresenter *xptr, XiveTCTX *tctx, + hwaddr offset, + uint64_t value, unsigned size); + uint64_t (*read_handler)(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, + unsigned size); } XiveTmOp; static const XiveTmOp xive_tm_operations[] = { @@ -367,6 +465,7 @@ static const XiveTmOp xive_tm_operations[] = { * effects */ { XIVE_TM_OS_PAGE, TM_QW1_OS + TM_CPPR, 1, xive_tm_set_os_cppr, NULL }, + { XIVE_TM_HV_PAGE, TM_QW1_OS + TM_WORD2, 4, xive_tm_push_os_ctx, NULL }, { XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_CPPR, 1, xive_tm_set_hv_cppr, NULL }, { XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_WORD2, 1, xive_tm_vt_push, NULL }, { XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_WORD2, 1, NULL, xive_tm_vt_poll }, @@ -404,8 +503,8 @@ static const XiveTmOp *xive_tm_find_op(hwaddr offset, unsigned size, bool write) /* * TIMA MMIO handlers */ -void xive_tctx_tm_write(XiveTCTX *tctx, hwaddr offset, uint64_t value, - unsigned size) +void xive_tctx_tm_write(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, + uint64_t value, unsigned size) { const XiveTmOp *xto; @@ -422,7 +521,7 @@ void xive_tctx_tm_write(XiveTCTX *tctx, hwaddr offset, uint64_t value, qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid write access at TIMA " "@%"HWADDR_PRIx"\n", offset); } else { - xto->write_handler(tctx, offset, value, size); + xto->write_handler(xptr, tctx, offset, value, size); } return; } @@ -432,7 +531,7 @@ void xive_tctx_tm_write(XiveTCTX *tctx, hwaddr offset, uint64_t value, */ xto = xive_tm_find_op(offset, size, true); if (xto) { - xto->write_handler(tctx, offset, value, size); + xto->write_handler(xptr, tctx, offset, value, size); return; } @@ -442,7 +541,8 @@ void xive_tctx_tm_write(XiveTCTX *tctx, hwaddr offset, uint64_t value, xive_tm_raw_write(tctx, offset, value, size); } -uint64_t xive_tctx_tm_read(XiveTCTX *tctx, hwaddr offset, unsigned size) +uint64_t xive_tctx_tm_read(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, + unsigned size) { const XiveTmOp *xto; @@ -460,7 +560,7 @@ uint64_t xive_tctx_tm_read(XiveTCTX *tctx, hwaddr offset, unsigned size) "@%"HWADDR_PRIx"\n", offset); return -1; } - return xto->read_handler(tctx, offset, size); + return xto->read_handler(xptr, tctx, offset, size); } /* @@ -468,7 +568,7 @@ uint64_t xive_tctx_tm_read(XiveTCTX *tctx, hwaddr offset, unsigned size) */ xto = xive_tm_find_op(offset, size, false); if (xto) { - return xto->read_handler(tctx, offset, size); + return xto->read_handler(xptr, tctx, offset, size); } /* @@ -477,35 +577,6 @@ uint64_t xive_tctx_tm_read(XiveTCTX *tctx, hwaddr offset, unsigned size) return xive_tm_raw_read(tctx, offset, size); } -static void xive_tm_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - XiveTCTX *tctx = xive_router_get_tctx(XIVE_ROUTER(opaque), current_cpu); - - xive_tctx_tm_write(tctx, offset, value, size); -} - -static uint64_t xive_tm_read(void *opaque, hwaddr offset, unsigned size) -{ - XiveTCTX *tctx = xive_router_get_tctx(XIVE_ROUTER(opaque), current_cpu); - - return xive_tctx_tm_read(tctx, offset, size); -} - -const MemoryRegionOps xive_tm_ops = { - .read = xive_tm_read, - .write = xive_tm_write, - .endianness = DEVICE_BIG_ENDIAN, - .valid = { - .min_access_size = 1, - .max_access_size = 8, - }, - .impl = { - .min_access_size = 1, - .max_access_size = 8, - }, -}; - static char *xive_tctx_ring_print(uint8_t *ring) { uint32_t w2 = xive_tctx_word2(ring); @@ -580,19 +651,12 @@ static void xive_tctx_realize(DeviceState *dev, Error **errp) XiveTCTX *tctx = XIVE_TCTX(dev); PowerPCCPU *cpu; CPUPPCState *env; - Object *obj; Error *local_err = NULL; - obj = object_property_get_link(OBJECT(dev), "cpu", &local_err); - if (!obj) { - error_propagate(errp, local_err); - error_prepend(errp, "required link 'cpu' not found: "); - return; - } - - cpu = POWERPC_CPU(obj); - tctx->cs = CPU(obj); + assert(tctx->cs); + assert(tctx->xptr); + cpu = POWERPC_CPU(tctx->cs); env = &cpu->env; switch (PPC_INPUT(env)) { case PPC_FLAGS_INPUT_POWER9: @@ -662,6 +726,13 @@ static const VMStateDescription vmstate_xive_tctx = { }, }; +static Property xive_tctx_properties[] = { + DEFINE_PROP_LINK("cpu", XiveTCTX, cs, TYPE_CPU, CPUState *), + DEFINE_PROP_LINK("presenter", XiveTCTX, xptr, TYPE_XIVE_PRESENTER, + XivePresenter *), + DEFINE_PROP_END_OF_LIST(), +}; + static void xive_tctx_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -669,6 +740,7 @@ static void xive_tctx_class_init(ObjectClass *klass, void *data) dc->desc = "XIVE Interrupt Thread Context"; dc->realize = xive_tctx_realize; dc->vmsd = &vmstate_xive_tctx; + device_class_set_props(dc, xive_tctx_properties); /* * Reason: part of XIVE interrupt controller, needs to be wired up * by xive_tctx_create(). @@ -683,7 +755,7 @@ static const TypeInfo xive_tctx_info = { .class_init = xive_tctx_class_init, }; -Object *xive_tctx_create(Object *cpu, XiveRouter *xrtr, Error **errp) +Object *xive_tctx_create(Object *cpu, XivePresenter *xptr, Error **errp) { Error *local_err = NULL; Object *obj; @@ -691,8 +763,8 @@ Object *xive_tctx_create(Object *cpu, XiveRouter *xrtr, Error **errp) obj = object_new(TYPE_XIVE_TCTX); object_property_add_child(cpu, TYPE_XIVE_TCTX, obj, &error_abort); object_unref(obj); - object_ref(cpu); - object_property_add_const_link(obj, "cpu", cpu, &error_abort); + object_property_set_link(obj, cpu, "cpu", &error_abort); + object_property_set_link(obj, OBJECT(xptr), "presenter", &error_abort); object_property_set_bool(obj, true, "realized", &local_err); if (local_err) { goto error; @@ -710,7 +782,6 @@ void xive_tctx_destroy(XiveTCTX *tctx) { Object *obj = OBJECT(tctx); - object_unref(object_property_get_link(obj, "cpu", &error_abort)); object_unparent(obj); } @@ -1064,17 +1135,8 @@ static void xive_source_reset(void *dev) static void xive_source_realize(DeviceState *dev, Error **errp) { XiveSource *xsrc = XIVE_SOURCE(dev); - Object *obj; - Error *local_err = NULL; - obj = object_property_get_link(OBJECT(dev), "xive", &local_err); - if (!obj) { - error_propagate(errp, local_err); - error_prepend(errp, "required link 'xive' not found: "); - return; - } - - xsrc->xive = XIVE_NOTIFIER(obj); + assert(xsrc->xive); if (!xsrc->nr_irqs) { error_setg(errp, "Number of interrupt needs to be greater than 0"); @@ -1120,6 +1182,8 @@ static Property xive_source_properties[] = { DEFINE_PROP_UINT64("flags", XiveSource, esb_flags, 0), DEFINE_PROP_UINT32("nr-irqs", XiveSource, nr_irqs, 0), DEFINE_PROP_UINT32("shift", XiveSource, esb_shift, XIVE_ESB_64K_2PAGE), + DEFINE_PROP_LINK("xive", XiveSource, xive, TYPE_XIVE_NOTIFIER, + XiveNotifier *), DEFINE_PROP_END_OF_LIST(), }; @@ -1128,7 +1192,7 @@ static void xive_source_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->desc = "XIVE Interrupt Source"; - dc->props = xive_source_properties; + device_class_set_props(dc, xive_source_properties); dc->realize = xive_source_realize; dc->vmsd = &vmstate_xive_source; /* @@ -1311,11 +1375,18 @@ int xive_router_write_nvt(XiveRouter *xrtr, uint8_t nvt_blk, uint32_t nvt_idx, return xrc->write_nvt(xrtr, nvt_blk, nvt_idx, nvt, word_number); } -XiveTCTX *xive_router_get_tctx(XiveRouter *xrtr, CPUState *cs) +static int xive_router_get_block_id(XiveRouter *xrtr) { - XiveRouterClass *xrc = XIVE_ROUTER_GET_CLASS(xrtr); + XiveRouterClass *xrc = XIVE_ROUTER_GET_CLASS(xrtr); - return xrc->get_tctx(xrtr, cs); + return xrc->get_block_id(xrtr); +} + +static void xive_router_realize(DeviceState *dev, Error **errp) +{ + XiveRouter *xrtr = XIVE_ROUTER(dev); + + assert(xrtr->xfb); } /* @@ -1323,20 +1394,22 @@ XiveTCTX *xive_router_get_tctx(XiveRouter *xrtr, CPUState *cs) * * chip << 19 | 0000000 0 0001 thread (7Bit) */ -static uint32_t xive_tctx_hw_cam_line(XiveTCTX *tctx) +static uint32_t xive_tctx_hw_cam_line(XivePresenter *xptr, XiveTCTX *tctx) { CPUPPCState *env = &POWERPC_CPU(tctx->cs)->env; uint32_t pir = env->spr_cb[SPR_PIR].default_value; + uint8_t blk = xive_router_get_block_id(XIVE_ROUTER(xptr)); - return xive_nvt_cam_line((pir >> 8) & 0xf, 1 << 7 | (pir & 0x7f)); + return xive_nvt_cam_line(blk, 1 << 7 | (pir & 0x7f)); } /* * The thread context register words are in big-endian format. */ -static int xive_presenter_tctx_match(XiveTCTX *tctx, uint8_t format, - uint8_t nvt_blk, uint32_t nvt_idx, - bool cam_ignore, uint32_t logic_serv) +int xive_presenter_tctx_match(XivePresenter *xptr, XiveTCTX *tctx, + uint8_t format, + uint8_t nvt_blk, uint32_t nvt_idx, + bool cam_ignore, uint32_t logic_serv) { uint32_t cam = xive_nvt_cam_line(nvt_blk, nvt_idx); uint32_t qw3w2 = xive_tctx_word2(&tctx->regs[TM_QW3_HV_PHYS]); @@ -1364,7 +1437,7 @@ static int xive_presenter_tctx_match(XiveTCTX *tctx, uint8_t format, /* PHYS ring */ if ((be32_to_cpu(qw3w2) & TM_QW3W2_VT) && - cam == xive_tctx_hw_cam_line(tctx)) { + cam == xive_tctx_hw_cam_line(xptr, tctx)) { return TM_QW3_HV_PHYS; } @@ -1393,71 +1466,6 @@ static int xive_presenter_tctx_match(XiveTCTX *tctx, uint8_t format, return -1; } -typedef struct XiveTCTXMatch { - XiveTCTX *tctx; - uint8_t ring; -} XiveTCTXMatch; - -static bool xive_presenter_match(XiveRouter *xrtr, uint8_t format, - uint8_t nvt_blk, uint32_t nvt_idx, - bool cam_ignore, uint8_t priority, - uint32_t logic_serv, XiveTCTXMatch *match) -{ - CPUState *cs; - - /* - * TODO (PowerNV): handle chip_id overwrite of block field for - * hardwired CAM compares - */ - - CPU_FOREACH(cs) { - XiveTCTX *tctx = xive_router_get_tctx(xrtr, cs); - int ring; - - /* - * Skip partially initialized vCPUs. This can happen when - * vCPUs are hotplugged. - */ - if (!tctx) { - continue; - } - - /* - * HW checks that the CPU is enabled in the Physical Thread - * Enable Register (PTER). - */ - - /* - * Check the thread context CAM lines and record matches. We - * will handle CPU exception delivery later - */ - ring = xive_presenter_tctx_match(tctx, format, nvt_blk, nvt_idx, - cam_ignore, logic_serv); - /* - * Save the context and follow on to catch duplicates, that we - * don't support yet. - */ - if (ring != -1) { - if (match->tctx) { - qemu_log_mask(LOG_GUEST_ERROR, "XIVE: already found a thread " - "context NVT %x/%x\n", nvt_blk, nvt_idx); - return false; - } - - match->ring = ring; - match->tctx = tctx; - } - } - - if (!match->tctx) { - qemu_log_mask(LOG_UNIMP, "XIVE: NVT %x/%x is not dispatched\n", - nvt_blk, nvt_idx); - return false; - } - - return true; -} - /* * This is our simple Xive Presenter Engine model. It is merged in the * Router as it does not require an extra object. @@ -1473,22 +1481,30 @@ static bool xive_presenter_match(XiveRouter *xrtr, uint8_t format, * * The parameters represent what is sent on the PowerBus */ -static bool xive_presenter_notify(XiveRouter *xrtr, uint8_t format, +static bool xive_presenter_notify(XiveFabric *xfb, uint8_t format, uint8_t nvt_blk, uint32_t nvt_idx, bool cam_ignore, uint8_t priority, uint32_t logic_serv) { + XiveFabricClass *xfc = XIVE_FABRIC_GET_CLASS(xfb); XiveTCTXMatch match = { .tctx = NULL, .ring = 0 }; - bool found; + int count; - found = xive_presenter_match(xrtr, format, nvt_blk, nvt_idx, cam_ignore, - priority, logic_serv, &match); - if (found) { - ipb_update(&match.tctx->regs[match.ring], priority); - xive_tctx_notify(match.tctx, match.ring); + /* + * Ask the machine to scan the interrupt controllers for a match + */ + count = xfc->match_nvt(xfb, format, nvt_blk, nvt_idx, cam_ignore, + priority, logic_serv, &match); + if (count < 0) { + return false; } - return found; + /* handle CPU exception delivery */ + if (count) { + xive_tctx_ipb_update(match.tctx, match.ring, priority_to_ipb(priority)); + } + + return !!count; } /* @@ -1601,7 +1617,7 @@ static void xive_router_end_notify(XiveRouter *xrtr, uint8_t end_blk, return; } - found = xive_presenter_notify(xrtr, format, nvt_blk, nvt_idx, + found = xive_presenter_notify(xrtr->xfb, format, nvt_blk, nvt_idx, xive_get_field32(END_W7_F0_IGNORE, end.w7), priority, xive_get_field32(END_W7_F1_LOG_SERVER_ID, end.w7)); @@ -1618,14 +1634,21 @@ static void xive_router_end_notify(XiveRouter *xrtr, uint8_t end_blk, * - logical server : forward request to IVPE (not supported) */ if (xive_end_is_backlog(&end)) { + uint8_t ipb; + if (format == 1) { qemu_log_mask(LOG_GUEST_ERROR, "XIVE: END %x/%x invalid config: F1 & backlog\n", end_blk, end_idx); return; } - /* Record the IPB in the associated NVT structure */ - ipb_update((uint8_t *) &nvt.w4, priority); + /* + * Record the IPB in the associated NVT structure for later + * use. The presenter will resend the interrupt when the vCPU + * is dispatched again on a HW thread. + */ + ipb = xive_get_field32(NVT_W4_IPB, nvt.w4) | priority_to_ipb(priority); + nvt.w4 = xive_set_field32(NVT_W4_IPB, nvt.w4, ipb); xive_router_write_nvt(xrtr, nvt_blk, nvt_idx, &nvt, 4); /* @@ -1701,12 +1724,21 @@ void xive_router_notify(XiveNotifier *xn, uint32_t lisn) xive_get_field64(EAS_END_DATA, eas.w)); } +static Property xive_router_properties[] = { + DEFINE_PROP_LINK("xive-fabric", XiveRouter, xfb, + TYPE_XIVE_FABRIC, XiveFabric *), + DEFINE_PROP_END_OF_LIST(), +}; + static void xive_router_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); XiveNotifierClass *xnc = XIVE_NOTIFIER_CLASS(klass); dc->desc = "XIVE Router Engine"; + device_class_set_props(dc, xive_router_properties); + /* Parent is SysBusDeviceClass. No need to call its realize hook */ + dc->realize = xive_router_realize; xnc->notify = xive_router_notify; } @@ -1714,10 +1746,12 @@ static const TypeInfo xive_router_info = { .name = TYPE_XIVE_ROUTER, .parent = TYPE_SYS_BUS_DEVICE, .abstract = true, + .instance_size = sizeof(XiveRouter), .class_size = sizeof(XiveRouterClass), .class_init = xive_router_class_init, .interfaces = (InterfaceInfo[]) { { TYPE_XIVE_NOTIFIER }, + { TYPE_XIVE_PRESENTER }, { } } }; @@ -1749,7 +1783,11 @@ static uint64_t xive_end_source_read(void *opaque, hwaddr addr, unsigned size) uint8_t pq; uint64_t ret = -1; - end_blk = xsrc->block_id; + /* + * The block id should be deduced from the load address on the END + * ESB MMIO but our model only supports a single block per XIVE chip. + */ + end_blk = xive_router_get_block_id(xsrc->xrtr); end_idx = addr >> (xsrc->esb_shift + 1); if (xive_router_get_end(xsrc->xrtr, end_blk, end_idx, &end)) { @@ -1825,17 +1863,8 @@ static const MemoryRegionOps xive_end_source_ops = { static void xive_end_source_realize(DeviceState *dev, Error **errp) { XiveENDSource *xsrc = XIVE_END_SOURCE(dev); - Object *obj; - Error *local_err = NULL; - obj = object_property_get_link(OBJECT(dev), "xive", &local_err); - if (!obj) { - error_propagate(errp, local_err); - error_prepend(errp, "required link 'xive' not found: "); - return; - } - - xsrc->xrtr = XIVE_ROUTER(obj); + assert(xsrc->xrtr); if (!xsrc->nr_ends) { error_setg(errp, "Number of interrupt needs to be greater than 0"); @@ -1858,9 +1887,10 @@ static void xive_end_source_realize(DeviceState *dev, Error **errp) } static Property xive_end_source_properties[] = { - DEFINE_PROP_UINT8("block-id", XiveENDSource, block_id, 0), DEFINE_PROP_UINT32("nr-ends", XiveENDSource, nr_ends, 0), DEFINE_PROP_UINT32("shift", XiveENDSource, esb_shift, XIVE_ESB_64K), + DEFINE_PROP_LINK("xive", XiveENDSource, xrtr, TYPE_XIVE_ROUTER, + XiveRouter *), DEFINE_PROP_END_OF_LIST(), }; @@ -1869,7 +1899,7 @@ static void xive_end_source_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->desc = "XIVE END Source"; - dc->props = xive_end_source_properties; + device_class_set_props(dc, xive_end_source_properties); dc->realize = xive_end_source_realize; /* * Reason: part of XIVE interrupt controller, needs to be wired up, @@ -1894,10 +1924,30 @@ static const TypeInfo xive_notifier_info = { .class_size = sizeof(XiveNotifierClass), }; +/* + * XIVE Presenter + */ +static const TypeInfo xive_presenter_info = { + .name = TYPE_XIVE_PRESENTER, + .parent = TYPE_INTERFACE, + .class_size = sizeof(XivePresenterClass), +}; + +/* + * XIVE Fabric + */ +static const TypeInfo xive_fabric_info = { + .name = TYPE_XIVE_FABRIC, + .parent = TYPE_INTERFACE, + .class_size = sizeof(XiveFabricClass), +}; + static void xive_register_types(void) { + type_register_static(&xive_fabric_info); type_register_static(&xive_source_info); type_register_static(&xive_notifier_info); + type_register_static(&xive_presenter_info); type_register_static(&xive_router_info); type_register_static(&xive_end_source_info); type_register_static(&xive_tctx_info); diff --git a/hw/intc/xlnx-pmu-iomod-intc.c b/hw/intc/xlnx-pmu-iomod-intc.c index f9a1401a94..acaa1c3e6f 100644 --- a/hw/intc/xlnx-pmu-iomod-intc.c +++ b/hw/intc/xlnx-pmu-iomod-intc.c @@ -539,7 +539,7 @@ static void xlnx_pmu_io_intc_class_init(ObjectClass *klass, void *data) dc->reset = xlnx_pmu_io_intc_reset; dc->realize = xlnx_pmu_io_intc_realize; dc->vmsd = &vmstate_xlnx_pmu_io_intc; - dc->props = xlnx_pmu_io_intc_properties; + device_class_set_props(dc, xlnx_pmu_io_intc_properties); } static const TypeInfo xlnx_pmu_io_intc_info = { diff --git a/hw/ipack/ipack.c b/hw/ipack/ipack.c index 30e16696c0..49787a13bc 100644 --- a/hw/ipack/ipack.c +++ b/hw/ipack/ipack.c @@ -88,7 +88,7 @@ static void ipack_device_class_init(ObjectClass *klass, void *data) k->bus_type = TYPE_IPACK_BUS; k->realize = ipack_device_realize; k->unrealize = ipack_device_unrealize; - k->props = ipack_device_props; + device_class_set_props(k, ipack_device_props); } const VMStateDescription vmstate_ipack_device = { diff --git a/hw/ipmi/ipmi.c b/hw/ipmi/ipmi.c index cbe158f815..46c6a79527 100644 --- a/hw/ipmi/ipmi.c +++ b/hw/ipmi/ipmi.c @@ -118,7 +118,7 @@ static void bmc_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); - dc->props = ipmi_bmc_properties; + device_class_set_props(dc, ipmi_bmc_properties); } static TypeInfo ipmi_bmc_type_info = { diff --git a/hw/ipmi/ipmi_bmc_extern.c b/hw/ipmi/ipmi_bmc_extern.c index 87da9ff99c..f9a13e0a44 100644 --- a/hw/ipmi/ipmi_bmc_extern.c +++ b/hw/ipmi/ipmi_bmc_extern.c @@ -87,10 +87,6 @@ typedef struct IPMIBmcExtern { bool send_reset; } IPMIBmcExtern; -static int can_receive(void *opaque); -static void receive(void *opaque, const uint8_t *buf, int size); -static void chr_event(void *opaque, int event); - static unsigned char ipmb_checksum(const unsigned char *data, int size, unsigned char start) { @@ -383,7 +379,7 @@ static void receive(void *opaque, const uint8_t *buf, int size) handle_hw_op(ibe, hw_op); } -static void chr_event(void *opaque, int event) +static void chr_event(void *opaque, QEMUChrEvent event) { IPMIBmcExtern *ibe = opaque; IPMIInterface *s = ibe->parent.intf; @@ -439,6 +435,12 @@ static void chr_event(void *opaque, int event) k->handle_rsp(s, ibe->outbuf[0], ibe->inbuf + 1, 3); } break; + + case CHR_EVENT_BREAK: + case CHR_EVENT_MUX_IN: + case CHR_EVENT_MUX_OUT: + /* Ignore */ + break; } } @@ -526,7 +528,7 @@ static void ipmi_bmc_extern_class_init(ObjectClass *oc, void *data) bk->handle_reset = ipmi_bmc_extern_handle_reset; dc->hotpluggable = false; dc->realize = ipmi_bmc_extern_realize; - dc->props = ipmi_bmc_extern_properties; + device_class_set_props(dc, ipmi_bmc_extern_properties); } static const TypeInfo ipmi_bmc_extern_type = { diff --git a/hw/ipmi/ipmi_bmc_sim.c b/hw/ipmi/ipmi_bmc_sim.c index 71e56f3b13..0b97e04774 100644 --- a/hw/ipmi/ipmi_bmc_sim.c +++ b/hw/ipmi/ipmi_bmc_sim.c @@ -167,32 +167,14 @@ typedef struct IPMISensor { #define MAX_SENSORS 20 #define IPMI_WATCHDOG_SENSOR 0 -typedef struct IPMIBmcSim IPMIBmcSim; -typedef struct RspBuffer RspBuffer; - #define MAX_NETFNS 64 -typedef struct IPMICmdHandler { - void (*cmd_handler)(IPMIBmcSim *s, - uint8_t *cmd, unsigned int cmd_len, - RspBuffer *rsp); - unsigned int cmd_len_min; -} IPMICmdHandler; - -typedef struct IPMINetfn { - unsigned int cmd_nums; - const IPMICmdHandler *cmd_handlers; -} IPMINetfn; - typedef struct IPMIRcvBufEntry { QTAILQ_ENTRY(IPMIRcvBufEntry) entry; uint8_t len; uint8_t buf[MAX_IPMI_MSG_SIZE]; } IPMIRcvBufEntry; -#define TYPE_IPMI_BMC_SIMULATOR "ipmi-bmc-sim" -#define IPMI_BMC_SIMULATOR(obj) OBJECT_CHECK(IPMIBmcSim, (obj), \ - TYPE_IPMI_BMC_SIMULATOR) struct IPMIBmcSim { IPMIBmc parent; @@ -279,28 +261,8 @@ struct IPMIBmcSim { #define IPMI_BMC_WATCHDOG_ACTION_POWER_DOWN 2 #define IPMI_BMC_WATCHDOG_ACTION_POWER_CYCLE 3 -struct RspBuffer { - uint8_t buffer[MAX_IPMI_MSG_SIZE]; - unsigned int len; -}; - #define RSP_BUFFER_INITIALIZER { } -static inline void rsp_buffer_set_error(RspBuffer *rsp, uint8_t byte) -{ - rsp->buffer[2] = byte; -} - -/* Add a byte to the response. */ -static inline void rsp_buffer_push(RspBuffer *rsp, uint8_t byte) -{ - if (rsp->len >= sizeof(rsp->buffer)) { - rsp_buffer_set_error(rsp, IPMI_CC_REQUEST_DATA_TRUNCATED); - return; - } - rsp->buffer[rsp->len++] = byte; -} - static inline void rsp_buffer_pushmore(RspBuffer *rsp, uint8_t *bytes, unsigned int n) { @@ -630,8 +592,8 @@ static void ipmi_init_sensors_from_sdrs(IPMIBmcSim *s) } } -static int ipmi_register_netfn(IPMIBmcSim *s, unsigned int netfn, - const IPMINetfn *netfnd) +int ipmi_sim_register_netfn(IPMIBmcSim *s, unsigned int netfn, + const IPMINetfn *netfnd) { if ((netfn & 1) || (netfn >= MAX_NETFNS) || (s->netfns[netfn / 2])) { return -1; @@ -1860,10 +1822,10 @@ static const IPMINetfn storage_netfn = { static void register_cmds(IPMIBmcSim *s) { - ipmi_register_netfn(s, IPMI_NETFN_CHASSIS, &chassis_netfn); - ipmi_register_netfn(s, IPMI_NETFN_SENSOR_EVENT, &sensor_event_netfn); - ipmi_register_netfn(s, IPMI_NETFN_APP, &app_netfn); - ipmi_register_netfn(s, IPMI_NETFN_STORAGE, &storage_netfn); + ipmi_sim_register_netfn(s, IPMI_NETFN_CHASSIS, &chassis_netfn); + ipmi_sim_register_netfn(s, IPMI_NETFN_SENSOR_EVENT, &sensor_event_netfn); + ipmi_sim_register_netfn(s, IPMI_NETFN_APP, &app_netfn); + ipmi_sim_register_netfn(s, IPMI_NETFN_STORAGE, &storage_netfn); } static uint8_t init_sdrs[] = { @@ -2027,7 +1989,7 @@ static void ipmi_sim_class_init(ObjectClass *oc, void *data) dc->hotpluggable = false; dc->realize = ipmi_sim_realize; - dc->props = ipmi_sim_properties; + device_class_set_props(dc, ipmi_sim_properties); bk->handle_command = ipmi_sim_handle_command; } diff --git a/hw/ipmi/isa_ipmi_bt.c b/hw/ipmi/isa_ipmi_bt.c index 9a87ffd3f0..c8dc0a09dc 100644 --- a/hw/ipmi/isa_ipmi_bt.c +++ b/hw/ipmi/isa_ipmi_bt.c @@ -70,6 +70,7 @@ static void isa_ipmi_bt_lower_irq(IPMIBT *ib) static void isa_ipmi_bt_realize(DeviceState *dev, Error **errp) { + Error *err = NULL; ISADevice *isadev = ISA_DEVICE(dev); ISAIPMIBTDevice *iib = ISA_IPMI_BT(dev); IPMIInterface *ii = IPMI_INTERFACE(dev); @@ -85,9 +86,11 @@ static void isa_ipmi_bt_realize(DeviceState *dev, Error **errp) iib->bt.bmc->intf = ii; iib->bt.opaque = iib; - iic->init(ii, 0, errp); - if (*errp) + iic->init(ii, 0, &err); + if (err) { + error_propagate(errp, err); return; + } if (iib->isairq > 0) { isa_init_irq(isadev, &iib->irq, iib->isairq); @@ -144,7 +147,7 @@ static void isa_ipmi_bt_class_init(ObjectClass *oc, void *data) IPMIInterfaceClass *iic = IPMI_INTERFACE_CLASS(oc); dc->realize = isa_ipmi_bt_realize; - dc->props = ipmi_isa_properties; + device_class_set_props(dc, ipmi_isa_properties); iic->get_backend_data = isa_ipmi_bt_get_backend_data; ipmi_bt_class_init(iic); diff --git a/hw/ipmi/isa_ipmi_kcs.c b/hw/ipmi/isa_ipmi_kcs.c index ca3ea36a3f..4b421c33f4 100644 --- a/hw/ipmi/isa_ipmi_kcs.c +++ b/hw/ipmi/isa_ipmi_kcs.c @@ -69,6 +69,7 @@ static void isa_ipmi_kcs_lower_irq(IPMIKCS *ik) static void ipmi_isa_realize(DeviceState *dev, Error **errp) { + Error *err = NULL; ISADevice *isadev = ISA_DEVICE(dev); ISAIPMIKCSDevice *iik = ISA_IPMI_KCS(dev); IPMIInterface *ii = IPMI_INTERFACE(dev); @@ -84,9 +85,11 @@ static void ipmi_isa_realize(DeviceState *dev, Error **errp) iik->kcs.bmc->intf = ii; iik->kcs.opaque = iik; - iic->init(ii, 0, errp); - if (*errp) + iic->init(ii, 0, &err); + if (err) { + error_propagate(errp, err); return; + } if (iik->isairq > 0) { isa_init_irq(isadev, &iik->irq, iik->isairq); @@ -151,7 +154,7 @@ static void isa_ipmi_kcs_class_init(ObjectClass *oc, void *data) IPMIInterfaceClass *iic = IPMI_INTERFACE_CLASS(oc); dc->realize = ipmi_isa_realize; - dc->props = ipmi_isa_properties; + device_class_set_props(dc, ipmi_isa_properties); iic->get_backend_data = isa_ipmi_kcs_get_backend_data; ipmi_kcs_class_init(iic); diff --git a/hw/ipmi/pci_ipmi_bt.c b/hw/ipmi/pci_ipmi_bt.c index 6ed925a665..ba9cf016b5 100644 --- a/hw/ipmi/pci_ipmi_bt.c +++ b/hw/ipmi/pci_ipmi_bt.c @@ -54,6 +54,7 @@ static void pci_ipmi_lower_irq(IPMIBT *ik) static void pci_ipmi_bt_realize(PCIDevice *pd, Error **errp) { + Error *err = NULL; PCIIPMIBTDevice *pik = PCI_IPMI_BT(pd); IPMIInterface *ii = IPMI_INTERFACE(pd); IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); @@ -74,8 +75,9 @@ static void pci_ipmi_bt_realize(PCIDevice *pd, Error **errp) pik->bt.raise_irq = pci_ipmi_raise_irq; pik->bt.lower_irq = pci_ipmi_lower_irq; - iic->init(ii, 8, errp); - if (*errp) { + iic->init(ii, 8, &err); + if (err) { + error_propagate(errp, err); return; } pci_register_bar(pd, 0, PCI_BASE_ADDRESS_SPACE_IO, &pik->bt.io); diff --git a/hw/ipmi/pci_ipmi_kcs.c b/hw/ipmi/pci_ipmi_kcs.c index eeba63baa4..99f46152f4 100644 --- a/hw/ipmi/pci_ipmi_kcs.c +++ b/hw/ipmi/pci_ipmi_kcs.c @@ -54,6 +54,7 @@ static void pci_ipmi_lower_irq(IPMIKCS *ik) static void pci_ipmi_kcs_realize(PCIDevice *pd, Error **errp) { + Error *err = NULL; PCIIPMIKCSDevice *pik = PCI_IPMI_KCS(pd); IPMIInterface *ii = IPMI_INTERFACE(pd); IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); @@ -74,8 +75,9 @@ static void pci_ipmi_kcs_realize(PCIDevice *pd, Error **errp) pik->kcs.raise_irq = pci_ipmi_raise_irq; pik->kcs.lower_irq = pci_ipmi_lower_irq; - iic->init(ii, 8, errp); - if (*errp) { + iic->init(ii, 8, &err); + if (err) { + error_propagate(errp, err); return; } pci_register_bar(pd, 0, PCI_BASE_ADDRESS_SPACE_IO, &pik->kcs.io); diff --git a/hw/isa/Kconfig b/hw/isa/Kconfig index 8a38813cc1..c7f07854f7 100644 --- a/hw/isa/Kconfig +++ b/hw/isa/Kconfig @@ -16,6 +16,7 @@ config I82378 config ISA_SUPERIO bool select ISA_BUS + select PCKBD config PC87312 bool diff --git a/hw/isa/i82378.c b/hw/isa/i82378.c index de276cdf10..dcb6b479ea 100644 --- a/hw/isa/i82378.c +++ b/hw/isa/i82378.c @@ -19,8 +19,8 @@ #include "qemu/osdep.h" #include "hw/pci/pci.h" -#include "hw/i386/pc.h" #include "hw/irq.h" +#include "hw/intc/i8259.h" #include "hw/timer/i8254.h" #include "migration/vmstate.h" #include "hw/audio/pcspk.h" diff --git a/hw/isa/isa-bus.c b/hw/isa/isa-bus.c index 388800603b..798dd9194e 100644 --- a/hw/isa/isa-bus.c +++ b/hw/isa/isa-bus.c @@ -82,24 +82,27 @@ void isa_bus_irqs(ISABus *bus, qemu_irq *irqs) * This function is only for special cases such as the 'ferr', and * temporary use for normal devices until they are converted to qdev. */ -qemu_irq isa_get_irq(ISADevice *dev, int isairq) +qemu_irq isa_get_irq(ISADevice *dev, unsigned isairq) { assert(!dev || ISA_BUS(qdev_get_parent_bus(DEVICE(dev))) == isabus); - if (isairq < 0 || isairq > 15) { + if (isairq >= ISA_NUM_IRQS) { hw_error("isa irq %d invalid", isairq); } return isabus->irqs[isairq]; } -void isa_init_irq(ISADevice *dev, qemu_irq *p, int isairq) +void isa_init_irq(ISADevice *dev, qemu_irq *p, unsigned isairq) { assert(dev->nirqs < ARRAY_SIZE(dev->isairq)); + if (isairq >= ISA_NUM_IRQS) { + hw_error("isa irq %d invalid", isairq); + } dev->isairq[dev->nirqs] = isairq; *p = isa_get_irq(dev, isairq); dev->nirqs++; } -void isa_connect_gpio_out(ISADevice *isadev, int gpioirq, int isairq) +void isa_connect_gpio_out(ISADevice *isadev, int gpioirq, unsigned isairq) { qemu_irq irq; isa_init_irq(isadev, &irq, isairq); diff --git a/hw/isa/isa-superio.c b/hw/isa/isa-superio.c index c4e391916c..180a8b9625 100644 --- a/hw/isa/isa-superio.c +++ b/hw/isa/isa-superio.c @@ -5,7 +5,7 @@ * Copyright (c) 2011-2012 Andreas Färber * Copyright (c) 2018 Philippe Mathieu-Daudé * - * This code is licensed under the GNU GPLv2 and later. + * This work is licensed under the terms of the GNU GPL, version 2 or later. * See the COPYING file in the top-level directory. * SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/hw/isa/lpc_ich9.c b/hw/isa/lpc_ich9.c index 17c292e306..fbc3165d03 100644 --- a/hw/isa/lpc_ich9.c +++ b/hw/isa/lpc_ich9.c @@ -35,10 +35,8 @@ #include "hw/isa/isa.h" #include "hw/sysbus.h" #include "migration/vmstate.h" -#include "hw/i386/pc.h" #include "hw/irq.h" #include "hw/isa/apm.h" -#include "hw/i386/ioapic.h" #include "hw/pci/pci.h" #include "hw/pci/pci_bridge.h" #include "hw/i386/ich9.h" @@ -627,36 +625,21 @@ static const MemoryRegionOps ich9_rst_cnt_ops = { .endianness = DEVICE_LITTLE_ENDIAN }; -static void ich9_lpc_get_sci_int(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - ICH9LPCState *lpc = ICH9_LPC_DEVICE(obj); - uint32_t value = lpc->sci_gsi; - - visit_type_uint32(v, name, &value, errp); -} - -static void ich9_lpc_add_properties(ICH9LPCState *lpc) -{ - static const uint8_t acpi_enable_cmd = ICH9_APM_ACPI_ENABLE; - static const uint8_t acpi_disable_cmd = ICH9_APM_ACPI_DISABLE; - - object_property_add(OBJECT(lpc), ACPI_PM_PROP_SCI_INT, "uint32", - ich9_lpc_get_sci_int, - NULL, NULL, NULL, NULL); - object_property_add_uint8_ptr(OBJECT(lpc), ACPI_PM_PROP_ACPI_ENABLE_CMD, - &acpi_enable_cmd, NULL); - object_property_add_uint8_ptr(OBJECT(lpc), ACPI_PM_PROP_ACPI_DISABLE_CMD, - &acpi_disable_cmd, NULL); - - ich9_pm_add_properties(OBJECT(lpc), &lpc->pm, NULL); -} - static void ich9_lpc_initfn(Object *obj) { ICH9LPCState *lpc = ICH9_LPC_DEVICE(obj); - ich9_lpc_add_properties(lpc); + static const uint8_t acpi_enable_cmd = ICH9_APM_ACPI_ENABLE; + static const uint8_t acpi_disable_cmd = ICH9_APM_ACPI_DISABLE; + + object_property_add_uint8_ptr(obj, ACPI_PM_PROP_SCI_INT, + &lpc->sci_gsi, OBJ_PROP_FLAG_READ, NULL); + object_property_add_uint8_ptr(OBJECT(lpc), ACPI_PM_PROP_ACPI_ENABLE_CMD, + &acpi_enable_cmd, OBJ_PROP_FLAG_READ, NULL); + object_property_add_uint8_ptr(OBJECT(lpc), ACPI_PM_PROP_ACPI_DISABLE_CMD, + &acpi_disable_cmd, OBJ_PROP_FLAG_READ, NULL); + + ich9_pm_add_properties(obj, &lpc->pm, NULL); } static void ich9_lpc_realize(PCIDevice *d, Error **errp) @@ -785,7 +768,7 @@ static void ich9_lpc_class_init(ObjectClass *klass, void *data) dc->reset = ich9_lpc_reset; k->realize = ich9_lpc_realize; dc->vmsd = &vmstate_ich9_lpc; - dc->props = ich9_lpc_properties; + device_class_set_props(dc, ich9_lpc_properties); k->config_write = ich9_lpc_config_write; dc->desc = "ICH9 LPC bridge"; k->vendor_id = PCI_VENDOR_ID_INTEL; diff --git a/hw/isa/pc87312.c b/hw/isa/pc87312.c index b9bd57471e..0cacbbc91b 100644 --- a/hw/isa/pc87312.c +++ b/hw/isa/pc87312.c @@ -342,7 +342,7 @@ static void pc87312_class_init(ObjectClass *klass, void *data) dc->realize = pc87312_realize; dc->reset = pc87312_reset; dc->vmsd = &vmstate_pc87312; - dc->props = pc87312_properties; + device_class_set_props(dc, pc87312_properties); sc->parallel = (ISASuperIOFuncs){ .count = 1, diff --git a/hw/isa/piix4.c b/hw/isa/piix4.c index 86678e6829..9a10fb9b3c 100644 --- a/hw/isa/piix4.c +++ b/hw/isa/piix4.c @@ -26,15 +26,15 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "hw/irq.h" -#include "hw/i386/pc.h" #include "hw/southbridge/piix.h" #include "hw/pci/pci.h" #include "hw/isa/isa.h" #include "hw/sysbus.h" +#include "hw/intc/i8259.h" #include "hw/dma/i8257.h" #include "hw/timer/i8254.h" #include "hw/rtc/mc146818rtc.h" -#include "hw/ide.h" +#include "hw/ide/pci.h" #include "migration/vmstate.h" #include "sysemu/reset.h" #include "sysemu/runstate.h" @@ -240,28 +240,25 @@ static void piix4_register_types(void) type_init(piix4_register_types) -DeviceState *piix4_create(PCIBus *pci_bus, ISABus **isa_bus, - I2CBus **smbus, size_t ide_buses) +DeviceState *piix4_create(PCIBus *pci_bus, ISABus **isa_bus, I2CBus **smbus) { - size_t ide_drives = ide_buses * MAX_IDE_DEVS; - DriveInfo **hd; PCIDevice *pci; DeviceState *dev; + int devfn = PCI_DEVFN(10, 0); - pci = pci_create_simple_multifunction(pci_bus, PCI_DEVFN(10, 0), - true, TYPE_PIIX4_PCI_DEVICE); + pci = pci_create_simple_multifunction(pci_bus, devfn, true, + TYPE_PIIX4_PCI_DEVICE); dev = DEVICE(pci); if (isa_bus) { *isa_bus = ISA_BUS(qdev_get_child_bus(dev, "isa.0")); } - hd = g_new(DriveInfo *, ide_drives); - ide_drive_get(hd, ide_drives); - pci_piix4_ide_init(pci_bus, hd, pci->devfn + 1); - g_free(hd); - pci_create_simple(pci_bus, pci->devfn + 2, "piix4-usb-uhci"); + pci = pci_create_simple(pci_bus, devfn + 1, "piix4-ide"); + pci_ide_create_devs(pci); + + pci_create_simple(pci_bus, devfn + 2, "piix4-usb-uhci"); if (smbus) { - *smbus = piix4_pm_init(pci_bus, pci->devfn + 3, 0x1100, + *smbus = piix4_pm_init(pci_bus, devfn + 3, 0x1100, isa_get_irq(NULL, 9), NULL, 0, NULL); } diff --git a/hw/isa/smc37c669-superio.c b/hw/isa/smc37c669-superio.c index 901a9f8e65..18287741cb 100644 --- a/hw/isa/smc37c669-superio.c +++ b/hw/isa/smc37c669-superio.c @@ -3,7 +3,7 @@ * * Copyright (c) 2018 Philippe Mathieu-Daudé * - * This code is licensed under the GNU GPLv2 and later. + * This work is licensed under the terms of the GNU GPL, version 2 or later. * See the COPYING file in the top-level directory. * SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/hw/isa/vt82c686.c b/hw/isa/vt82c686.c index 616f67f347..d9b51fce8d 100644 --- a/hw/isa/vt82c686.c +++ b/hw/isa/vt82c686.c @@ -27,7 +27,7 @@ #include "qemu/timer.h" #include "exec/address-spaces.h" -//#define DEBUG_VT82C686B +/* #define DEBUG_VT82C686B */ #ifdef DEBUG_VT82C686B #define DPRINTF(fmt, ...) fprintf(stderr, "%s: " fmt, __func__, ##__VA_ARGS__) @@ -35,8 +35,7 @@ #define DPRINTF(fmt, ...) #endif -typedef struct SuperIOConfig -{ +typedef struct SuperIOConfig { uint8_t config[0x100]; uint8_t index; uint8_t data; @@ -102,7 +101,7 @@ static uint64_t superio_ioport_readb(void *opaque, hwaddr addr, unsigned size) SuperIOConfig *superio_conf = opaque; DPRINTF("superio_ioport_readb address 0x%x\n", addr); - return (superio_conf->config[superio_conf->index]); + return superio_conf->config[superio_conf->index]; } static const MemoryRegionOps superio_ops = { @@ -143,7 +142,7 @@ static void vt82c686b_isa_reset(DeviceState *dev) } /* write config pci function0 registers. PCI-ISA bridge */ -static void vt82c686b_write_config(PCIDevice * d, uint32_t address, +static void vt82c686b_write_config(PCIDevice *d, uint32_t address, uint32_t val, int len) { VT82C686BState *vt686 = VT82C686B_DEVICE(d); @@ -365,7 +364,7 @@ static void vt82c686b_pm_realize(PCIDevice *dev, Error **errp) pci_set_long(pci_conf + 0x48, 0x00000001); /* SMB ports:0xeee0~0xeeef */ - s->smb_io_base =((s->smb_io_base & 0xfff0) + 0x0); + s->smb_io_base = ((s->smb_io_base & 0xfff0) + 0x0); pci_conf[0x90] = s->smb_io_base | 1; pci_conf[0x91] = s->smb_io_base >> 8; pci_conf[0xd2] = 0x90; @@ -418,7 +417,7 @@ static void via_pm_class_init(ObjectClass *klass, void *data) dc->desc = "PM"; dc->vmsd = &vmstate_acpi; set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); - dc->props = via_pm_properties; + device_class_set_props(dc, via_pm_properties); } static const TypeInfo via_pm_info = { @@ -462,16 +461,18 @@ static void vt82c686b_realize(PCIDevice *d, Error **errp) wmask = d->wmask; for (i = 0x00; i < 0xff; i++) { - if (i<=0x03 || (i>=0x08 && i<=0x3f)) { - wmask[i] = 0x00; - } + if (i <= 0x03 || (i >= 0x08 && i <= 0x3f)) { + wmask[i] = 0x00; + } } memory_region_init_io(&vt82c->superio, OBJECT(d), &superio_ops, &vt82c->superio_conf, "superio", 2); memory_region_set_enabled(&vt82c->superio, false); - /* The floppy also uses 0x3f0 and 0x3f1. - * But we do not emulate a floppy, so just set it here. */ + /* + * The floppy also uses 0x3f0 and 0x3f1. + * But we do not emulate a floppy, so just set it here. + */ memory_region_add_subregion(isa_bus->address_space_io, 0x3f0, &vt82c->superio); } diff --git a/hw/lm32/lm32_boards.c b/hw/lm32/lm32_boards.c index 5ae308bfcf..b842f74344 100644 --- a/hw/lm32/lm32_boards.c +++ b/hw/lm32/lm32_boards.c @@ -19,6 +19,7 @@ #include "qemu/osdep.h" #include "qemu/units.h" +#include "qemu/cutils.h" #include "qemu/error-report.h" #include "cpu.h" #include "hw/sysbus.h" @@ -75,22 +76,28 @@ static void main_cpu_reset(void *opaque) static void lm32_evr_init(MachineState *machine) { + MachineClass *mc = MACHINE_GET_CLASS(machine); const char *kernel_filename = machine->kernel_filename; LM32CPU *cpu; CPULM32State *env; DriveInfo *dinfo; MemoryRegion *address_space_mem = get_system_memory(); - MemoryRegion *phys_ram = g_new(MemoryRegion, 1); qemu_irq irq[32]; ResetInfo *reset_info; int i; + if (machine->ram_size != mc->default_ram_size) { + char *sz = size_to_str(mc->default_ram_size); + error_report("Invalid RAM size, should be %s", sz); + g_free(sz); + exit(EXIT_FAILURE); + } + /* memory map */ hwaddr flash_base = 0x04000000; size_t flash_sector_size = 256 * KiB; size_t flash_size = 32 * MiB; hwaddr ram_base = 0x08000000; - size_t ram_size = 64 * MiB; hwaddr timer0_base = 0x80002000; hwaddr uart0_base = 0x80006000; hwaddr timer1_base = 0x8000a000; @@ -107,9 +114,7 @@ static void lm32_evr_init(MachineState *machine) reset_info->flash_base = flash_base; - memory_region_allocate_system_memory(phys_ram, NULL, "lm32_evr.sdram", - ram_size); - memory_region_add_subregion(address_space_mem, ram_base, phys_ram); + memory_region_add_subregion(address_space_mem, ram_base, machine->ram); dinfo = drive_get(IF_PFLASH, 0, 0); /* Spansion S29NS128P */ @@ -138,13 +143,13 @@ static void lm32_evr_init(MachineState *machine) int kernel_size; kernel_size = load_elf(kernel_filename, NULL, NULL, NULL, - &entry, NULL, NULL, + &entry, NULL, NULL, NULL, 1, EM_LATTICEMICO32, 0, 0); reset_info->bootstrap_pc = entry; if (kernel_size < 0) { kernel_size = load_image_targphys(kernel_filename, ram_base, - ram_size); + machine->ram_size); reset_info->bootstrap_pc = ram_base; } @@ -159,6 +164,7 @@ static void lm32_evr_init(MachineState *machine) static void lm32_uclinux_init(MachineState *machine) { + MachineClass *mc = MACHINE_GET_CLASS(machine); const char *kernel_filename = machine->kernel_filename; const char *kernel_cmdline = machine->kernel_cmdline; const char *initrd_filename = machine->initrd_filename; @@ -166,18 +172,23 @@ static void lm32_uclinux_init(MachineState *machine) CPULM32State *env; DriveInfo *dinfo; MemoryRegion *address_space_mem = get_system_memory(); - MemoryRegion *phys_ram = g_new(MemoryRegion, 1); qemu_irq irq[32]; HWSetup *hw; ResetInfo *reset_info; int i; + if (machine->ram_size != mc->default_ram_size) { + char *sz = size_to_str(mc->default_ram_size); + error_report("Invalid RAM size, should be %s", sz); + g_free(sz); + exit(EXIT_FAILURE); + } + /* memory map */ hwaddr flash_base = 0x04000000; size_t flash_sector_size = 256 * KiB; size_t flash_size = 32 * MiB; hwaddr ram_base = 0x08000000; - size_t ram_size = 64 * MiB; hwaddr uart0_base = 0x80000000; hwaddr timer0_base = 0x80002000; hwaddr timer1_base = 0x80010000; @@ -200,9 +211,7 @@ static void lm32_uclinux_init(MachineState *machine) reset_info->flash_base = flash_base; - memory_region_allocate_system_memory(phys_ram, NULL, - "lm32_uclinux.sdram", ram_size); - memory_region_add_subregion(address_space_mem, ram_base, phys_ram); + memory_region_add_subregion(address_space_mem, ram_base, machine->ram); dinfo = drive_get(IF_PFLASH, 0, 0); /* Spansion S29NS128P */ @@ -232,13 +241,13 @@ static void lm32_uclinux_init(MachineState *machine) int kernel_size; kernel_size = load_elf(kernel_filename, NULL, NULL, NULL, - &entry, NULL, NULL, + &entry, NULL, NULL, NULL, 1, EM_LATTICEMICO32, 0, 0); reset_info->bootstrap_pc = entry; if (kernel_size < 0) { kernel_size = load_image_targphys(kernel_filename, ram_base, - ram_size); + machine->ram_size); reset_info->bootstrap_pc = ram_base; } @@ -252,7 +261,7 @@ static void lm32_uclinux_init(MachineState *machine) hw = hwsetup_init(); hwsetup_add_cpu(hw, "LM32", 75000000); hwsetup_add_flash(hw, "flash", flash_base, flash_size); - hwsetup_add_ddr_sdram(hw, "ddr_sdram", ram_base, ram_size); + hwsetup_add_ddr_sdram(hw, "ddr_sdram", ram_base, machine->ram_size); hwsetup_add_timer(hw, "timer0", timer0_base, timer0_irq); hwsetup_add_timer(hw, "timer1_dev_only", timer1_base, timer1_irq); hwsetup_add_timer(hw, "timer2_dev_only", timer2_base, timer2_irq); @@ -286,8 +295,10 @@ static void lm32_evr_class_init(ObjectClass *oc, void *data) mc->desc = "LatticeMico32 EVR32 eval system"; mc->init = lm32_evr_init; - mc->is_default = 1; + mc->is_default = true; mc->default_cpu_type = LM32_CPU_TYPE_NAME("lm32-full"); + mc->default_ram_size = 64 * MiB; + mc->default_ram_id = "lm32_evr.sdram"; } static const TypeInfo lm32_evr_type = { @@ -302,8 +313,9 @@ static void lm32_uclinux_class_init(ObjectClass *oc, void *data) mc->desc = "lm32 platform for uClinux and u-boot by Theobroma Systems"; mc->init = lm32_uclinux_init; - mc->is_default = 0; mc->default_cpu_type = LM32_CPU_TYPE_NAME("lm32-full"); + mc->default_ram_size = 64 * MiB; + mc->default_ram_id = "lm32_uclinux.sdram"; } static const TypeInfo lm32_uclinux_type = { diff --git a/hw/lm32/milkymist.c b/hw/lm32/milkymist.c index 460d322de5..85913bb68b 100644 --- a/hw/lm32/milkymist.c +++ b/hw/lm32/milkymist.c @@ -36,6 +36,7 @@ #include "hw/display/milkymist_tmu2.h" #include "lm32.h" #include "exec/address-spaces.h" +#include "qemu/cutils.h" #define BIOS_FILENAME "mmone-bios.bin" #define BIOS_OFFSET 0x00860000 @@ -82,6 +83,7 @@ static void main_cpu_reset(void *opaque) static void milkymist_init(MachineState *machine) { + MachineClass *mc = MACHINE_GET_CLASS(machine); const char *kernel_filename = machine->kernel_filename; const char *kernel_cmdline = machine->kernel_cmdline; const char *initrd_filename = machine->initrd_filename; @@ -90,22 +92,27 @@ milkymist_init(MachineState *machine) int kernel_size; DriveInfo *dinfo; MemoryRegion *address_space_mem = get_system_memory(); - MemoryRegion *phys_sdram = g_new(MemoryRegion, 1); qemu_irq irq[32]; int i; char *bios_filename; ResetInfo *reset_info; + if (machine->ram_size != mc->default_ram_size) { + char *sz = size_to_str(mc->default_ram_size); + error_report("Invalid RAM size, should be %s", sz); + g_free(sz); + exit(EXIT_FAILURE); + } + /* memory map */ hwaddr flash_base = 0x00000000; size_t flash_sector_size = 128 * KiB; size_t flash_size = 32 * MiB; hwaddr sdram_base = 0x40000000; - size_t sdram_size = 128 * MiB; hwaddr initrd_base = sdram_base + 0x1002000; hwaddr cmdline_base = sdram_base + 0x1000000; - size_t initrd_max = sdram_size - 0x1002000; + size_t initrd_max = machine->ram_size - 0x1002000; reset_info = g_malloc0(sizeof(ResetInfo)); @@ -116,9 +123,7 @@ milkymist_init(MachineState *machine) cpu_lm32_set_phys_msb_ignore(env, 1); - memory_region_allocate_system_memory(phys_sdram, NULL, "milkymist.sdram", - sdram_size); - memory_region_add_subregion(address_space_mem, sdram_base, phys_sdram); + memory_region_add_subregion(address_space_mem, sdram_base, machine->ram); dinfo = drive_get(IF_PFLASH, 0, 0); /* Numonyx JS28F256J3F105 */ @@ -177,13 +182,13 @@ milkymist_init(MachineState *machine) /* Boots a kernel elf binary. */ kernel_size = load_elf(kernel_filename, NULL, NULL, NULL, - &entry, NULL, NULL, + &entry, NULL, NULL, NULL, 1, EM_LATTICEMICO32, 0, 0); reset_info->bootstrap_pc = entry; if (kernel_size < 0) { kernel_size = load_image_targphys(kernel_filename, sdram_base, - sdram_size); + machine->ram_size); reset_info->bootstrap_pc = sdram_base; } @@ -214,8 +219,9 @@ static void milkymist_machine_init(MachineClass *mc) { mc->desc = "Milkymist One"; mc->init = milkymist_init; - mc->is_default = 0; mc->default_cpu_type = LM32_CPU_TYPE_NAME("lm32-full"); + mc->default_ram_size = 128 * MiB; + mc->default_ram_id = "milkymist.sdram"; } DEFINE_MACHINE("milkymist", milkymist_machine_init) diff --git a/hw/m68k/an5206.c b/hw/m68k/an5206.c index 54ccbe1a82..846f4e40c6 100644 --- a/hw/m68k/an5206.c +++ b/hw/m68k/an5206.c @@ -33,7 +33,6 @@ static void an5206_init(MachineState *machine) uint64_t elf_entry; hwaddr entry; MemoryRegion *address_space_mem = get_system_memory(); - MemoryRegion *ram = g_new(MemoryRegion, 1); MemoryRegion *sram = g_new(MemoryRegion, 1); cpu = M68K_CPU(cpu_create(machine->cpu_type)); @@ -46,8 +45,7 @@ static void an5206_init(MachineState *machine) env->rambar0 = AN5206_RAMBAR_ADDR | 1; /* DRAM at address zero */ - memory_region_allocate_system_memory(ram, NULL, "an5206.ram", ram_size); - memory_region_add_subregion(address_space_mem, 0, ram); + memory_region_add_subregion(address_space_mem, 0, machine->ram); /* Internal SRAM. */ memory_region_init_ram(sram, NULL, "an5206.sram", 512, &error_fatal); @@ -65,7 +63,7 @@ static void an5206_init(MachineState *machine) } kernel_size = load_elf(kernel_filename, NULL, NULL, NULL, &elf_entry, - NULL, NULL, 1, EM_68K, 0, 0); + NULL, NULL, NULL, 1, EM_68K, 0, 0); entry = elf_entry; if (kernel_size < 0) { kernel_size = load_uimage(kernel_filename, &entry, NULL, NULL, @@ -89,6 +87,7 @@ static void an5206_machine_init(MachineClass *mc) mc->desc = "Arnewsh 5206"; mc->init = an5206_init; mc->default_cpu_type = M68K_CPU_TYPE_NAME("m5206"); + mc->default_ram_id = "an5206.ram"; } DEFINE_MACHINE("an5206", an5206_machine_init) diff --git a/hw/m68k/bootinfo.h b/hw/m68k/bootinfo.h index 5f8ded2686..c954270aad 100644 --- a/hw/m68k/bootinfo.h +++ b/hw/m68k/bootinfo.h @@ -14,7 +14,7 @@ struct bi_record { uint16_t tag; /* tag ID */ uint16_t size; /* size of record */ - uint32_t data[0]; /* data */ + uint32_t data[]; /* data */ }; /* machine independent tags */ diff --git a/hw/m68k/mcf5208.c b/hw/m68k/mcf5208.c index 158c5e4be7..b84c152ce3 100644 --- a/hw/m68k/mcf5208.c +++ b/hw/m68k/mcf5208.c @@ -234,7 +234,6 @@ static void mcf5208evb_init(MachineState *machine) qemu_irq *pic; MemoryRegion *address_space_mem = get_system_memory(); MemoryRegion *rom = g_new(MemoryRegion, 1); - MemoryRegion *ram = g_new(MemoryRegion, 1); MemoryRegion *sram = g_new(MemoryRegion, 1); cpu = M68K_CPU(cpu_create(machine->cpu_type)); @@ -249,8 +248,7 @@ static void mcf5208evb_init(MachineState *machine) memory_region_add_subregion(address_space_mem, 0x00000000, rom); /* DRAM at 0x40000000 */ - memory_region_allocate_system_memory(ram, NULL, "mcf5208.ram", ram_size); - memory_region_add_subregion(address_space_mem, 0x40000000, ram); + memory_region_add_subregion(address_space_mem, 0x40000000, machine->ram); /* Internal SRAM. */ memory_region_init_ram(sram, NULL, "mcf5208.sram", 16 * KiB, &error_fatal); @@ -329,7 +327,7 @@ static void mcf5208evb_init(MachineState *machine) } kernel_size = load_elf(kernel_filename, NULL, NULL, NULL, &elf_entry, - NULL, NULL, 1, EM_68K, 0, 0); + NULL, NULL, NULL, 1, EM_68K, 0, 0); entry = elf_entry; if (kernel_size < 0) { kernel_size = load_uimage(kernel_filename, &entry, NULL, NULL, @@ -352,8 +350,9 @@ static void mcf5208evb_machine_init(MachineClass *mc) { mc->desc = "MCF5208EVB"; mc->init = mcf5208evb_init; - mc->is_default = 1; + mc->is_default = true; mc->default_cpu_type = M68K_CPU_TYPE_NAME("m5208"); + mc->default_ram_id = "mcf5208.ram"; } DEFINE_MACHINE("mcf5208evb", mcf5208evb_machine_init) diff --git a/hw/m68k/next-cube.c b/hw/m68k/next-cube.c index e5343348d0..14b99ed25d 100644 --- a/hw/m68k/next-cube.c +++ b/hw/m68k/next-cube.c @@ -734,7 +734,7 @@ void next_irq(void *opaque, int number, int level) switch (number) { /* level 3 - floppy, kbd/mouse, power, ether rx/tx, scsi, clock */ case NEXT_FD_I: - shift = 7;; + shift = 7; break; case NEXT_KBD_I: shift = 3; @@ -860,7 +860,6 @@ static void next_cube_init(MachineState *machine) { M68kCPU *cpu; CPUM68KState *env; - MemoryRegion *ram = g_new(MemoryRegion, 1); MemoryRegion *rom = g_new(MemoryRegion, 1); MemoryRegion *mmiomem = g_new(MemoryRegion, 1); MemoryRegion *scrmem = g_new(MemoryRegion, 1); @@ -893,8 +892,7 @@ static void next_cube_init(MachineState *machine) memcpy(ns->rtc.ram, rtc_ram2, 32); /* 64MB RAM starting at 0x04000000 */ - memory_region_allocate_system_memory(ram, NULL, "next.ram", ram_size); - memory_region_add_subregion(sysmem, 0x04000000, ram); + memory_region_add_subregion(sysmem, 0x04000000, machine->ram); /* Framebuffer */ dev = qdev_create(NULL, TYPE_NEXTFB); @@ -967,6 +965,7 @@ static void next_machine_class_init(ObjectClass *oc, void *data) mc->desc = "NeXT Cube"; mc->init = next_cube_init; mc->default_ram_size = RAM_SIZE; + mc->default_ram_id = "next.ram"; mc->default_cpu_type = M68K_CPU_TYPE_NAME("m68040"); } diff --git a/hw/m68k/q800.c b/hw/m68k/q800.c index 4ca8678007..81749e7ec6 100644 --- a/hw/m68k/q800.c +++ b/hw/m68k/q800.c @@ -47,7 +47,7 @@ #include "sysemu/runstate.h" #include "sysemu/reset.h" -#define MACROM_ADDR 0x40000000 +#define MACROM_ADDR 0x40800000 #define MACROM_SIZE 0x00100000 #define MACROM_FILENAME "MacROM.bin" @@ -128,6 +128,27 @@ static void main_cpu_reset(void *opaque) cpu->env.pc = ldl_phys(cs->as, 4); } +static uint8_t fake_mac_rom[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + + /* offset: 0xa - mac_reset */ + + /* via2[vDirB] |= VIA2B_vPower */ + 0x20, 0x7C, 0x50, 0xF0, 0x24, 0x00, /* moveal VIA2_BASE+vDirB,%a0 */ + 0x10, 0x10, /* moveb %a0@,%d0 */ + 0x00, 0x00, 0x00, 0x04, /* orib #4,%d0 */ + 0x10, 0x80, /* moveb %d0,%a0@ */ + + /* via2[vBufB] &= ~VIA2B_vPower */ + 0x20, 0x7C, 0x50, 0xF0, 0x20, 0x00, /* moveal VIA2_BASE+vBufB,%a0 */ + 0x10, 0x10, /* moveb %a0@,%d0 */ + 0x02, 0x00, 0xFF, 0xFB, /* andib #-5,%d0 */ + 0x10, 0x80, /* moveb %d0,%a0@ */ + + /* while (true) ; */ + 0x60, 0xFE /* bras [self] */ +}; + static void q800_init(MachineState *machine) { M68kCPU *cpu = NULL; @@ -139,7 +160,6 @@ static void q800_init(MachineState *machine) ram_addr_t initrd_base; int32_t initrd_size; MemoryRegion *rom; - MemoryRegion *ram; MemoryRegion *io; const int io_slice_nb = (IO_SIZE / IO_SLICE) - 1; int i; @@ -158,6 +178,7 @@ static void q800_init(MachineState *machine) NubusBus *nubus; GLUEState *irq; qemu_irq *pic; + DriveInfo *dinfo; linux_boot = (kernel_filename != NULL); @@ -172,9 +193,7 @@ static void q800_init(MachineState *machine) qemu_register_reset(main_cpu_reset, cpu); /* RAM */ - ram = g_malloc(sizeof(*ram)); - memory_region_init_ram(ram, NULL, "m68k_mac.ram", ram_size, &error_abort); - memory_region_add_subregion(get_system_memory(), 0, ram); + memory_region_add_subregion(get_system_memory(), 0, machine->ram); /* * Memory from IO_BASE to IO_BASE + IO_SLICE is repeated @@ -200,6 +219,11 @@ static void q800_init(MachineState *machine) /* VIA */ via_dev = qdev_create(NULL, TYPE_MAC_VIA); + dinfo = drive_get(IF_MTD, 0, 0); + if (dinfo) { + qdev_prop_set_drive(via_dev, "drive", blk_by_legacy_dinfo(dinfo), + &error_abort); + } qdev_init_nofail(via_dev); sysbus = SYS_BUS_DEVICE(via_dev); sysbus_mmio_map(sysbus, 0, VIA_BASE); @@ -239,7 +263,8 @@ static void q800_init(MachineState *machine) qdev_set_nic_properties(dev, &nd_table[0]); qdev_prop_set_uint8(dev, "it_shift", 2); qdev_prop_set_bit(dev, "big_endian", true); - qdev_prop_set_ptr(dev, "dma_mr", get_system_memory()); + object_property_set_link(OBJECT(dev), OBJECT(get_system_memory()), + "dma_mr", &error_abort); qdev_init_nofail(dev); sysbus = SYS_BUS_DEVICE(dev); sysbus_mmio_map(sysbus, 0, SONIC_BASE); @@ -314,7 +339,7 @@ static void q800_init(MachineState *machine) if (linux_boot) { uint64_t high; kernel_size = load_elf(kernel_filename, NULL, NULL, NULL, - &elf_entry, NULL, &high, 1, + &elf_entry, NULL, &high, NULL, 1, EM_68K, 0, 0); if (kernel_size < 0) { error_report("could not load kernel '%s'", kernel_filename); @@ -340,6 +365,12 @@ static void q800_init(MachineState *machine) (graphic_width * graphic_depth + 7) / 8); BOOTINFO1(cs->as, parameters_base, BI_MAC_SCCBASE, SCC_BASE); + rom = g_malloc(sizeof(*rom)); + memory_region_init_ram_ptr(rom, NULL, "m68k_fake_mac.rom", + sizeof(fake_mac_rom), fake_mac_rom); + memory_region_set_readonly(rom, true); + memory_region_add_subregion(get_system_memory(), MACROM_ADDR, rom); + if (kernel_cmdline) { BOOTINFOSTR(cs->as, parameters_base, BI_COMMAND_LINE, kernel_cmdline); @@ -368,13 +399,12 @@ static void q800_init(MachineState *machine) uint8_t *ptr; /* allocate and load BIOS */ rom = g_malloc(sizeof(*rom)); - memory_region_init_ram(rom, NULL, "m68k_mac.rom", MACROM_SIZE, + memory_region_init_rom(rom, NULL, "m68k_mac.rom", MACROM_SIZE, &error_abort); if (bios_name == NULL) { bios_name = MACROM_FILENAME; } filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); - memory_region_set_readonly(rom, true); memory_region_add_subregion(get_system_memory(), MACROM_ADDR, rom); /* Load MacROM binary */ @@ -407,8 +437,8 @@ static void q800_machine_class_init(ObjectClass *oc, void *data) mc->init = q800_init; mc->default_cpu_type = M68K_CPU_TYPE_NAME("m68040"); mc->max_cpus = 1; - mc->is_default = 0; mc->block_default_type = IF_SCSI; + mc->default_ram_id = "m68k_mac.ram"; } static const TypeInfo q800_machine_typeinfo = { diff --git a/hw/mem/Kconfig b/hw/mem/Kconfig index 620fd4cb59..2ad052a536 100644 --- a/hw/mem/Kconfig +++ b/hw/mem/Kconfig @@ -8,4 +8,4 @@ config MEM_DEVICE config NVDIMM bool default y - depends on PC + depends on (PC || PSERIES) diff --git a/hw/mem/memory-device.c b/hw/mem/memory-device.c index aef148c1d7..4bc9cf0917 100644 --- a/hw/mem/memory-device.c +++ b/hw/mem/memory-device.c @@ -99,6 +99,7 @@ static uint64_t memory_device_get_free_addr(MachineState *ms, uint64_t align, uint64_t size, Error **errp) { + Error *err = NULL; GSList *list = NULL, *item; Range as, new = range_empty; @@ -123,8 +124,9 @@ static uint64_t memory_device_get_free_addr(MachineState *ms, return 0; } - memory_device_check_addable(ms, size, errp); - if (*errp) { + memory_device_check_addable(ms, size, &err); + if (err) { + error_propagate(errp, err); return 0; } diff --git a/hw/mem/nvdimm.c b/hw/mem/nvdimm.c index 375f9a588a..8e426d24bb 100644 --- a/hw/mem/nvdimm.c +++ b/hw/mem/nvdimm.c @@ -69,11 +69,51 @@ out: error_propagate(errp, local_err); } +static void nvdimm_get_uuid(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + NVDIMMDevice *nvdimm = NVDIMM(obj); + char *value = NULL; + + value = qemu_uuid_unparse_strdup(&nvdimm->uuid); + + visit_type_str(v, name, &value, errp); + g_free(value); +} + + +static void nvdimm_set_uuid(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + NVDIMMDevice *nvdimm = NVDIMM(obj); + Error *local_err = NULL; + char *value; + + visit_type_str(v, name, &value, &local_err); + if (local_err) { + goto out; + } + + if (qemu_uuid_parse(value, &nvdimm->uuid) != 0) { + error_setg(errp, "Property '%s.%s' has invalid value", + object_get_typename(obj), name); + goto out; + } + g_free(value); + +out: + error_propagate(errp, local_err); +} + + static void nvdimm_init(Object *obj) { object_property_add(obj, NVDIMM_LABEL_SIZE_PROP, "int", nvdimm_get_label_size, nvdimm_set_label_size, NULL, NULL, NULL); + + object_property_add(obj, NVDIMM_UUID_PROP, "QemuUUID", nvdimm_get_uuid, + nvdimm_set_uuid, NULL, NULL, NULL); } static void nvdimm_finalize(Object *obj) @@ -202,7 +242,7 @@ static void nvdimm_class_init(ObjectClass *oc, void *data) ddc->realize = nvdimm_realize; mdc->get_memory_region = nvdimm_md_get_memory_region; - dc->props = nvdimm_properties; + device_class_set_props(dc, nvdimm_properties); nvc->read_label_data = nvdimm_read_label_data; nvc->write_label_data = nvdimm_write_label_data; diff --git a/hw/mem/pc-dimm.c b/hw/mem/pc-dimm.c index 99e2faf01b..8f50b8afea 100644 --- a/hw/mem/pc-dimm.c +++ b/hw/mem/pc-dimm.c @@ -271,7 +271,7 @@ static void pc_dimm_class_init(ObjectClass *oc, void *data) dc->realize = pc_dimm_realize; dc->unrealize = pc_dimm_unrealize; - dc->props = pc_dimm_properties; + device_class_set_props(dc, pc_dimm_properties); dc->desc = "DIMM memory module"; ddc->get_vmstate_memory_region = pc_dimm_get_memory_region; diff --git a/hw/microblaze/boot.c b/hw/microblaze/boot.c index d1d7dfbbb9..925e3f7c9d 100644 --- a/hw/microblaze/boot.c +++ b/hw/microblaze/boot.c @@ -145,13 +145,13 @@ void microblaze_load_kernel(MicroBlazeCPU *cpu, hwaddr ddr_base, /* Boots a kernel elf binary. */ kernel_size = load_elf(kernel_filename, NULL, NULL, NULL, - &entry, &low, &high, + &entry, &low, &high, NULL, big_endian, EM_MICROBLAZE, 0, 0); base32 = entry; if (base32 == 0xc0000000) { kernel_size = load_elf(kernel_filename, NULL, translate_kernel_address, NULL, - &entry, NULL, NULL, + &entry, NULL, NULL, NULL, big_endian, EM_MICROBLAZE, 0, 0); } /* Always boot into physical ram. */ diff --git a/hw/microblaze/petalogix_ml605_mmu.c b/hw/microblaze/petalogix_ml605_mmu.c index 09486bc8bf..0a2640c40b 100644 --- a/hw/microblaze/petalogix_ml605_mmu.c +++ b/hw/microblaze/petalogix_ml605_mmu.c @@ -216,7 +216,6 @@ static void petalogix_ml605_machine_init(MachineClass *mc) { mc->desc = "PetaLogix linux refdesign for xilinx ml605 little endian"; mc->init = petalogix_ml605_init; - mc->is_default = 0; } DEFINE_MACHINE("petalogix-ml605", petalogix_ml605_machine_init) diff --git a/hw/microblaze/petalogix_s3adsp1800_mmu.c b/hw/microblaze/petalogix_s3adsp1800_mmu.c index 849bafc186..0bb6cdea8d 100644 --- a/hw/microblaze/petalogix_s3adsp1800_mmu.c +++ b/hw/microblaze/petalogix_s3adsp1800_mmu.c @@ -132,7 +132,7 @@ static void petalogix_s3adsp1800_machine_init(MachineClass *mc) { mc->desc = "PetaLogix linux refdesign for xilinx Spartan 3ADSP1800"; mc->init = petalogix_s3adsp1800_init; - mc->is_default = 1; + mc->is_default = true; } DEFINE_MACHINE("petalogix-s3adsp1800", petalogix_s3adsp1800_machine_init) diff --git a/hw/mips/boston.c b/hw/mips/boston.c index ca7d813a52..98ecd25e8e 100644 --- a/hw/mips/boston.c +++ b/hw/mips/boston.c @@ -50,7 +50,7 @@ typedef struct { MachineState *mach; MIPSCPSState cps; - SerialState *uart; + SerialMM *uart; CharBackend lcd_display; char lcd_content[8]; @@ -98,7 +98,7 @@ enum boston_plat_reg { PLAT_SYS_CTL = 0x48, }; -static void boston_lcd_event(void *opaque, int event) +static void boston_lcd_event(void *opaque, QEMUChrEvent event) { BostonState *s = opaque; if (event == CHR_EVENT_OPENED && !s->lcd_inited) { @@ -427,7 +427,7 @@ static void boston_mach_init(MachineState *machine) DeviceState *dev; BostonState *s; Error *err = NULL; - MemoryRegion *flash, *ddr, *ddr_low_alias, *lcd, *platreg; + MemoryRegion *flash, *ddr_low_alias, *lcd, *platreg; MemoryRegion *sys_mem = get_system_memory(); XilinxPCIEHost *pcie2; PCIDevice *ahci; @@ -473,14 +473,12 @@ static void boston_mach_init(MachineState *machine) memory_region_init_rom(flash, NULL, "boston.flash", 128 * MiB, &err); memory_region_add_subregion_overlap(sys_mem, 0x18000000, flash, 0); - ddr = g_new(MemoryRegion, 1); - memory_region_allocate_system_memory(ddr, NULL, "boston.ddr", - machine->ram_size); - memory_region_add_subregion_overlap(sys_mem, 0x80000000, ddr, 0); + memory_region_add_subregion_overlap(sys_mem, 0x80000000, machine->ram, 0); ddr_low_alias = g_new(MemoryRegion, 1); memory_region_init_alias(ddr_low_alias, NULL, "boston_low.ddr", - ddr, 0, MIN(machine->ram_size, (256 * MiB))); + machine->ram, 0, + MIN(machine->ram_size, (256 * MiB))); memory_region_add_subregion_overlap(sys_mem, 0, ddr_low_alias, 0); xilinx_pcie_init(sys_mem, 0, @@ -552,6 +550,7 @@ static void boston_mach_class_init(MachineClass *mc) mc->init = boston_mach_init; mc->block_default_type = IF_IDE; mc->default_ram_size = 1 * GiB; + mc->default_ram_id = "boston.ddr"; mc->max_cpus = 16; mc->default_cpu_type = MIPS_CPU_TYPE_NAME("I6400"); } diff --git a/hw/mips/cps.c b/hw/mips/cps.c index 1660f86908..92b9b1a5f6 100644 --- a/hw/mips/cps.c +++ b/hw/mips/cps.c @@ -106,7 +106,7 @@ static void mips_cps_realize(DeviceState *dev, Error **errp) object_property_set_bool(OBJECT(&s->itu), saar_present, "saar-present", &err); if (saar_present) { - qdev_prop_set_ptr(DEVICE(&s->itu), "saar", (void *)&env->CP0_SAAR); + s->itu.saar = &env->CP0_SAAR; } object_property_set_bool(OBJECT(&s->itu), true, "realized", &err); if (err != NULL) { @@ -178,7 +178,7 @@ static void mips_cps_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = mips_cps_realize; - dc->props = mips_cps_properties; + device_class_set_props(dc, mips_cps_properties); } static const TypeInfo mips_cps_info = { diff --git a/hw/mips/gt64xxx_pci.c b/hw/mips/gt64xxx_pci.c index f1af840d8e..b2ea13f09d 100644 --- a/hw/mips/gt64xxx_pci.c +++ b/hw/mips/gt64xxx_pci.c @@ -30,7 +30,7 @@ #include "hw/pci/pci_host.h" #include "hw/southbridge/piix.h" #include "migration/vmstate.h" -#include "hw/i386/pc.h" +#include "hw/intc/i8259.h" #include "hw/irq.h" #include "exec/address-spaces.h" #include "trace.h" diff --git a/hw/mips/mips_fulong2e.c b/hw/mips/mips_fulong2e.c index 03a27e1767..5040afd581 100644 --- a/hw/mips/mips_fulong2e.c +++ b/hw/mips/mips_fulong2e.c @@ -23,7 +23,7 @@ #include "qemu/units.h" #include "qapi/error.h" #include "cpu.h" -#include "hw/i386/pc.h" +#include "hw/intc/i8259.h" #include "hw/dma/i8257.h" #include "hw/isa/superio.h" #include "net/net.h" @@ -36,7 +36,7 @@ #include "audio/audio.h" #include "qemu/log.h" #include "hw/loader.h" -#include "hw/ide.h" +#include "hw/ide/pci.h" #include "elf.h" #include "hw/isa/vt82c686.h" #include "hw/rtc/mc146818rtc.h" @@ -119,7 +119,7 @@ static int64_t load_kernel(CPUMIPSState *env) cpu_mips_kseg0_to_phys, NULL, (uint64_t *)&kernel_entry, (uint64_t *)&kernel_low, (uint64_t *)&kernel_high, - 0, EM_MIPS, 1, 0); + NULL, 0, EM_MIPS, 1, 0); if (kernel_size < 0) { error_report("could not load kernel '%s': %s", loaderparams.kernel_filename, @@ -238,7 +238,7 @@ static void vt82c686b_southbridge_init(PCIBus *pci_bus, int slot, qemu_irq intc, { qemu_irq *i8259; ISABus *isa_bus; - DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; + PCIDevice *dev; isa_bus = vt82c686b_isa_init(pci_bus, PCI_DEVFN(slot, 0)); if (!isa_bus) { @@ -256,8 +256,8 @@ static void vt82c686b_southbridge_init(PCIBus *pci_bus, int slot, qemu_irq intc, /* Super I/O */ isa_create_simple(isa_bus, TYPE_VT82C686B_SUPERIO); - ide_drive_get(hd, ARRAY_SIZE(hd)); - via_ide_init(pci_bus, hd, PCI_DEVFN(slot, 1)); + dev = pci_create_simple(pci_bus, PCI_DEVFN(slot, 1), "via-ide"); + pci_ide_create_devs(dev); pci_create_simple(pci_bus, PCI_DEVFN(slot, 2), "vt82c686b-usb-uhci"); pci_create_simple(pci_bus, PCI_DEVFN(slot, 3), "vt82c686b-usb-uhci"); @@ -294,9 +294,7 @@ static void mips_fulong2e_init(MachineState *machine) const char *initrd_filename = machine->initrd_filename; char *filename; MemoryRegion *address_space_mem = get_system_memory(); - MemoryRegion *ram = g_new(MemoryRegion, 1); MemoryRegion *bios = g_new(MemoryRegion, 1); - ram_addr_t ram_size = machine->ram_size; long bios_size; uint8_t *spd_data; Error *err = NULL; @@ -315,15 +313,16 @@ static void mips_fulong2e_init(MachineState *machine) qemu_register_reset(main_cpu_reset, cpu); /* TODO: support more than 256M RAM as highmem */ - ram_size = 256 * MiB; + if (machine->ram_size != 256 * MiB) { + error_report("Invalid RAM size, should be 256MB"); + exit(EXIT_FAILURE); + } /* allocate RAM */ - memory_region_allocate_system_memory(ram, NULL, "fulong2e.ram", ram_size); - memory_region_init_ram(bios, NULL, "fulong2e.bios", BIOS_SIZE, + memory_region_init_rom(bios, NULL, "fulong2e.bios", BIOS_SIZE, &error_fatal); - memory_region_set_readonly(bios, true); - memory_region_add_subregion(address_space_mem, 0, ram); + memory_region_add_subregion(address_space_mem, 0, machine->ram); memory_region_add_subregion(address_space_mem, 0x1fc00000LL, bios); /* @@ -332,7 +331,7 @@ static void mips_fulong2e_init(MachineState *machine) */ if (kernel_filename) { - loaderparams.ram_size = ram_size; + loaderparams.ram_size = machine->ram_size; loaderparams.kernel_filename = kernel_filename; loaderparams.kernel_cmdline = kernel_cmdline; loaderparams.initrd_filename = initrd_filename; @@ -378,7 +377,7 @@ static void mips_fulong2e_init(MachineState *machine) } /* Populate SPD eeprom data */ - spd_data = spd_data_generate(DDR, ram_size, &err); + spd_data = spd_data_generate(DDR, machine->ram_size, &err); if (err) { warn_report_err(err); } @@ -399,6 +398,7 @@ static void mips_fulong2e_machine_init(MachineClass *mc) mc->block_default_type = IF_IDE; mc->default_cpu_type = MIPS_CPU_TYPE_NAME("Loongson-2E"); mc->default_ram_size = 256 * MiB; + mc->default_ram_id = "fulong2e.ram"; } DEFINE_MACHINE("fulong2e", mips_fulong2e_machine_init) diff --git a/hw/mips/mips_int.c b/hw/mips/mips_int.c index 863ed45659..796730b11d 100644 --- a/hw/mips/mips_int.c +++ b/hw/mips/mips_int.c @@ -77,7 +77,7 @@ void cpu_mips_irq_init_cpu(MIPSCPU *cpu) qemu_irq *qi; int i; - qi = qemu_allocate_irqs(cpu_mips_irq_request, env_archcpu(env), 8); + qi = qemu_allocate_irqs(cpu_mips_irq_request, cpu, 8); for (i = 0; i < 8; i++) { env->irq[i] = qi[i]; } diff --git a/hw/mips/mips_jazz.c b/hw/mips/mips_jazz.c index d978bb64a0..afea52b41b 100644 --- a/hw/mips/mips_jazz.c +++ b/hw/mips/mips_jazz.c @@ -26,7 +26,7 @@ #include "qemu-common.h" #include "hw/mips/mips.h" #include "hw/mips/cpudevs.h" -#include "hw/i386/pc.h" +#include "hw/intc/i8259.h" #include "hw/dma/i8257.h" #include "hw/char/serial.h" #include "hw/char/parallel.h" @@ -52,8 +52,7 @@ #include "qemu/error-report.h" #include "qemu/help_option.h" -enum jazz_model_e -{ +enum jazz_model_e { JAZZ_MAGNUM, JAZZ_PICA61, }; @@ -90,16 +89,20 @@ static const MemoryRegionOps rtc_ops = { static uint64_t dma_dummy_read(void *opaque, hwaddr addr, unsigned size) { - /* Nothing to do. That is only to ensure that - * the current DMA acknowledge cycle is completed. */ + /* + * Nothing to do. That is only to ensure that + * the current DMA acknowledge cycle is completed. + */ return 0xff; } static void dma_dummy_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) { - /* Nothing to do. That is only to ensure that - * the current DMA acknowledge cycle is completed. */ + /* + * Nothing to do. That is only to ensure that + * the current DMA acknowledge cycle is completed. + */ } static const MemoryRegionOps dma_dummy_ops = { @@ -109,8 +112,8 @@ static const MemoryRegionOps dma_dummy_ops = { }; #define MAGNUM_BIOS_SIZE_MAX 0x7e000 -#define MAGNUM_BIOS_SIZE (BIOS_SIZE < MAGNUM_BIOS_SIZE_MAX ? BIOS_SIZE : MAGNUM_BIOS_SIZE_MAX) - +#define MAGNUM_BIOS_SIZE \ + (BIOS_SIZE < MAGNUM_BIOS_SIZE_MAX ? BIOS_SIZE : MAGNUM_BIOS_SIZE_MAX) static void (*real_do_transaction_failed)(CPUState *cpu, hwaddr physaddr, vaddr addr, unsigned size, MMUAccessType access_type, @@ -156,12 +159,16 @@ static void mips_jazz_init(MachineState *machine, ISABus *isa_bus; ISADevice *pit; DriveInfo *fds[MAX_FD]; - MemoryRegion *ram = g_new(MemoryRegion, 1); MemoryRegion *bios = g_new(MemoryRegion, 1); MemoryRegion *bios2 = g_new(MemoryRegion, 1); SysBusESPState *sysbus_esp; ESPState *esp; + if (machine->ram_size > 256 * MiB) { + error_report("RAM size more than 256Mb is not supported"); + exit(EXIT_FAILURE); + } + /* init CPUs */ cpu = MIPS_CPU(cpu_create(machine->cpu_type)); env = &cpu->env; @@ -188,21 +195,19 @@ static void mips_jazz_init(MachineState *machine, cc->do_transaction_failed = mips_jazz_do_transaction_failed; /* allocate RAM */ - memory_region_allocate_system_memory(ram, NULL, "mips_jazz.ram", - machine->ram_size); - memory_region_add_subregion(address_space, 0, ram); + memory_region_add_subregion(address_space, 0, machine->ram); - memory_region_init_ram(bios, NULL, "mips_jazz.bios", MAGNUM_BIOS_SIZE, + memory_region_init_rom(bios, NULL, "mips_jazz.bios", MAGNUM_BIOS_SIZE, &error_fatal); - memory_region_set_readonly(bios, true); memory_region_init_alias(bios2, NULL, "mips_jazz.bios", bios, 0, MAGNUM_BIOS_SIZE); memory_region_add_subregion(address_space, 0x1fc00000LL, bios); memory_region_add_subregion(address_space, 0xfff00000LL, bios2); /* load the BIOS image. */ - if (bios_name == NULL) + if (bios_name == NULL) { bios_name = BIOS_FILENAME; + } filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); if (filename) { bios_size = load_image_targphys(filename, 0xfff00000LL, @@ -229,7 +234,8 @@ static void mips_jazz_init(MachineState *machine, sysbus_mmio_get_region(sysbus, 0)); memory_region_add_subregion(address_space, 0xf0000000, sysbus_mmio_get_region(sysbus, 1)); - memory_region_init_io(dma_dummy, NULL, &dma_dummy_ops, NULL, "dummy_dma", 0x1000); + memory_region_init_io(dma_dummy, NULL, &dma_dummy_ops, + NULL, "dummy_dma", 0x1000); memory_region_add_subregion(address_space, 0x8000d000, dma_dummy); /* ISA bus: IO space at 0x90000000, mem space at 0x91000000 */ @@ -258,9 +264,8 @@ static void mips_jazz_init(MachineState *machine, { /* Simple ROM, so user doesn't have to provide one */ MemoryRegion *rom_mr = g_new(MemoryRegion, 1); - memory_region_init_ram(rom_mr, NULL, "g364fb.rom", 0x80000, + memory_region_init_rom(rom_mr, NULL, "g364fb.rom", 0x80000, &error_fatal); - memory_region_set_readonly(rom_mr, true); uint8_t *rom = memory_region_get_ram_ptr(rom_mr); memory_region_add_subregion(address_space, 0x60000000, rom_mr); rom[0] = 0x10; /* Mips G364 */ @@ -276,15 +281,17 @@ static void mips_jazz_init(MachineState *machine, /* Network controller */ for (n = 0; n < nb_nics; n++) { nd = &nd_table[n]; - if (!nd->model) + if (!nd->model) { nd->model = g_strdup("dp83932"); + } if (strcmp(nd->model, "dp83932") == 0) { qemu_check_nic_model(nd, "dp83932"); dev = qdev_create(NULL, "dp8393x"); qdev_set_nic_properties(dev, nd); qdev_prop_set_uint8(dev, "it_shift", 2); - qdev_prop_set_ptr(dev, "dma_mr", rc4030_dma_mr); + object_property_set_link(OBJECT(dev), OBJECT(rc4030_dma_mr), + "dma_mr", &error_abort); qdev_init_nofail(dev); sysbus = SYS_BUS_DEVICE(dev); sysbus_mmio_map(sysbus, 0, 0x80001000); @@ -338,12 +345,12 @@ static void mips_jazz_init(MachineState *machine, /* Serial ports */ if (serial_hd(0)) { serial_mm_init(address_space, 0x80006000, 0, - qdev_get_gpio_in(rc4030, 8), 8000000/16, + qdev_get_gpio_in(rc4030, 8), 8000000 / 16, serial_hd(0), DEVICE_NATIVE_ENDIAN); } if (serial_hd(1)) { serial_mm_init(address_space, 0x80007000, 0, - qdev_get_gpio_in(rc4030, 9), 8000000/16, + qdev_get_gpio_in(rc4030, 9), 8000000 / 16, serial_hd(1), DEVICE_NATIVE_ENDIAN); } @@ -386,6 +393,7 @@ static void mips_magnum_class_init(ObjectClass *oc, void *data) mc->init = mips_magnum_init; mc->block_default_type = IF_SCSI; mc->default_cpu_type = MIPS_CPU_TYPE_NAME("R4000"); + mc->default_ram_id = "mips_jazz.ram"; } static const TypeInfo mips_magnum_type = { @@ -402,6 +410,7 @@ static void mips_pica61_class_init(ObjectClass *oc, void *data) mc->init = mips_pica61_init; mc->block_default_type = IF_SCSI; mc->default_cpu_type = MIPS_CPU_TYPE_NAME("R4000"); + mc->default_ram_id = "mips_jazz.ram"; } static const TypeInfo mips_pica61_type = { diff --git a/hw/mips/mips_malta.c b/hw/mips/mips_malta.c index 92e9ca5bfa..e4c4de1b4e 100644 --- a/hw/mips/mips_malta.c +++ b/hw/mips/mips_malta.c @@ -83,7 +83,7 @@ typedef struct { uint32_t i2csel; CharBackend display; char display_text[9]; - SerialState *uart; + SerialMM *uart; bool display_inited; } MaltaFPGAState; @@ -137,7 +137,8 @@ static void malta_fpga_update_display(void *opaque) */ #if defined(DEBUG) -# define logout(fmt, ...) fprintf(stderr, "MALTA\t%-24s" fmt, __func__, ## __VA_ARGS__) +# define logout(fmt, ...) \ + fprintf(stderr, "MALTA\t%-24s" fmt, __func__, ## __VA_ARGS__) #else # define logout(fmt, ...) ((void)0) #endif @@ -359,7 +360,6 @@ static uint64_t malta_fpga_read(void *opaque, hwaddr addr, /* SWITCH Register */ case 0x00200: - /* ori a3, a3, low(ram_low_size) */ val = 0x00000000; break; @@ -545,7 +545,7 @@ static void malta_fpga_reset(void *opaque) snprintf(s->display_text, 9, " "); } -static void malta_fgpa_display_event(void *opaque, int event) +static void malta_fgpa_display_event(void *opaque, QEMUChrEvent event) { MaltaFPGAState *s = opaque; @@ -569,7 +569,7 @@ static MaltaFPGAState *malta_fpga_init(MemoryRegion *address_space, MaltaFPGAState *s; Chardev *chr; - s = (MaltaFPGAState *)g_malloc0(sizeof(MaltaFPGAState)); + s = g_new0(MaltaFPGAState, 1); memory_region_init_io(&s->iomem, NULL, &malta_fpga_ops, s, "malta-fpga", 0x100000); @@ -844,24 +844,24 @@ static void write_bootloader(uint8_t *base, int64_t run_addr, /* Small bootloader */ p = (uint32_t *)base; - stl_p(p++, 0x08000000 | /* j 0x1fc00580 */ + stl_p(p++, 0x08000000 | /* j 0x1fc00580 */ ((run_addr + 0x580) & 0x0fffffff) >> 2); - stl_p(p++, 0x00000000); /* nop */ + stl_p(p++, 0x00000000); /* nop */ /* YAMON service vector */ - stl_p(base + 0x500, run_addr + 0x0580); /* start: */ - stl_p(base + 0x504, run_addr + 0x083c); /* print_count: */ - stl_p(base + 0x520, run_addr + 0x0580); /* start: */ - stl_p(base + 0x52c, run_addr + 0x0800); /* flush_cache: */ - stl_p(base + 0x534, run_addr + 0x0808); /* print: */ - stl_p(base + 0x538, run_addr + 0x0800); /* reg_cpu_isr: */ - stl_p(base + 0x53c, run_addr + 0x0800); /* unred_cpu_isr: */ - stl_p(base + 0x540, run_addr + 0x0800); /* reg_ic_isr: */ - stl_p(base + 0x544, run_addr + 0x0800); /* unred_ic_isr: */ - stl_p(base + 0x548, run_addr + 0x0800); /* reg_esr: */ - stl_p(base + 0x54c, run_addr + 0x0800); /* unreg_esr: */ - stl_p(base + 0x550, run_addr + 0x0800); /* getchar: */ - stl_p(base + 0x554, run_addr + 0x0800); /* syscon_read: */ + stl_p(base + 0x500, run_addr + 0x0580); /* start: */ + stl_p(base + 0x504, run_addr + 0x083c); /* print_count: */ + stl_p(base + 0x520, run_addr + 0x0580); /* start: */ + stl_p(base + 0x52c, run_addr + 0x0800); /* flush_cache: */ + stl_p(base + 0x534, run_addr + 0x0808); /* print: */ + stl_p(base + 0x538, run_addr + 0x0800); /* reg_cpu_isr: */ + stl_p(base + 0x53c, run_addr + 0x0800); /* unred_cpu_isr: */ + stl_p(base + 0x540, run_addr + 0x0800); /* reg_ic_isr: */ + stl_p(base + 0x544, run_addr + 0x0800); /* unred_ic_isr: */ + stl_p(base + 0x548, run_addr + 0x0800); /* reg_esr: */ + stl_p(base + 0x54c, run_addr + 0x0800); /* unreg_esr: */ + stl_p(base + 0x550, run_addr + 0x0800); /* getchar: */ + stl_p(base + 0x554, run_addr + 0x0800); /* syscon_read: */ /* Second part of the bootloader */ @@ -869,9 +869,9 @@ static void write_bootloader(uint8_t *base, int64_t run_addr, if (semihosting_get_argc()) { /* Preserve a0 content as arguments have been passed */ - stl_p(p++, 0x00000000); /* nop */ + stl_p(p++, 0x00000000); /* nop */ } else { - stl_p(p++, 0x24040002); /* addiu a0, zero, 2 */ + stl_p(p++, 0x24040002); /* addiu a0, zero, 2 */ } /* lui sp, high(ENVP_ADDR) */ @@ -892,104 +892,106 @@ static void write_bootloader(uint8_t *base, int64_t run_addr, stl_p(p++, 0x34e70000 | (loaderparams.ram_low_size & 0xffff)); /* Load BAR registers as done by YAMON */ - stl_p(p++, 0x3c09b400); /* lui t1, 0xb400 */ + stl_p(p++, 0x3c09b400); /* lui t1, 0xb400 */ #ifdef TARGET_WORDS_BIGENDIAN - stl_p(p++, 0x3c08df00); /* lui t0, 0xdf00 */ + stl_p(p++, 0x3c08df00); /* lui t0, 0xdf00 */ #else - stl_p(p++, 0x340800df); /* ori t0, r0, 0x00df */ + stl_p(p++, 0x340800df); /* ori t0, r0, 0x00df */ #endif - stl_p(p++, 0xad280068); /* sw t0, 0x0068(t1) */ + stl_p(p++, 0xad280068); /* sw t0, 0x0068(t1) */ - stl_p(p++, 0x3c09bbe0); /* lui t1, 0xbbe0 */ + stl_p(p++, 0x3c09bbe0); /* lui t1, 0xbbe0 */ #ifdef TARGET_WORDS_BIGENDIAN - stl_p(p++, 0x3c08c000); /* lui t0, 0xc000 */ + stl_p(p++, 0x3c08c000); /* lui t0, 0xc000 */ #else - stl_p(p++, 0x340800c0); /* ori t0, r0, 0x00c0 */ + stl_p(p++, 0x340800c0); /* ori t0, r0, 0x00c0 */ #endif - stl_p(p++, 0xad280048); /* sw t0, 0x0048(t1) */ + stl_p(p++, 0xad280048); /* sw t0, 0x0048(t1) */ #ifdef TARGET_WORDS_BIGENDIAN - stl_p(p++, 0x3c084000); /* lui t0, 0x4000 */ + stl_p(p++, 0x3c084000); /* lui t0, 0x4000 */ #else - stl_p(p++, 0x34080040); /* ori t0, r0, 0x0040 */ + stl_p(p++, 0x34080040); /* ori t0, r0, 0x0040 */ #endif - stl_p(p++, 0xad280050); /* sw t0, 0x0050(t1) */ + stl_p(p++, 0xad280050); /* sw t0, 0x0050(t1) */ #ifdef TARGET_WORDS_BIGENDIAN - stl_p(p++, 0x3c088000); /* lui t0, 0x8000 */ + stl_p(p++, 0x3c088000); /* lui t0, 0x8000 */ #else - stl_p(p++, 0x34080080); /* ori t0, r0, 0x0080 */ + stl_p(p++, 0x34080080); /* ori t0, r0, 0x0080 */ #endif - stl_p(p++, 0xad280058); /* sw t0, 0x0058(t1) */ + stl_p(p++, 0xad280058); /* sw t0, 0x0058(t1) */ #ifdef TARGET_WORDS_BIGENDIAN - stl_p(p++, 0x3c083f00); /* lui t0, 0x3f00 */ + stl_p(p++, 0x3c083f00); /* lui t0, 0x3f00 */ #else - stl_p(p++, 0x3408003f); /* ori t0, r0, 0x003f */ + stl_p(p++, 0x3408003f); /* ori t0, r0, 0x003f */ #endif - stl_p(p++, 0xad280060); /* sw t0, 0x0060(t1) */ + stl_p(p++, 0xad280060); /* sw t0, 0x0060(t1) */ #ifdef TARGET_WORDS_BIGENDIAN - stl_p(p++, 0x3c08c100); /* lui t0, 0xc100 */ + stl_p(p++, 0x3c08c100); /* lui t0, 0xc100 */ #else - stl_p(p++, 0x340800c1); /* ori t0, r0, 0x00c1 */ + stl_p(p++, 0x340800c1); /* ori t0, r0, 0x00c1 */ #endif - stl_p(p++, 0xad280080); /* sw t0, 0x0080(t1) */ + stl_p(p++, 0xad280080); /* sw t0, 0x0080(t1) */ #ifdef TARGET_WORDS_BIGENDIAN - stl_p(p++, 0x3c085e00); /* lui t0, 0x5e00 */ + stl_p(p++, 0x3c085e00); /* lui t0, 0x5e00 */ #else - stl_p(p++, 0x3408005e); /* ori t0, r0, 0x005e */ + stl_p(p++, 0x3408005e); /* ori t0, r0, 0x005e */ #endif - stl_p(p++, 0xad280088); /* sw t0, 0x0088(t1) */ + stl_p(p++, 0xad280088); /* sw t0, 0x0088(t1) */ /* Jump to kernel code */ - stl_p(p++, 0x3c1f0000 | ((kernel_entry >> 16) & 0xffff)); /* lui ra, high(kernel_entry) */ - stl_p(p++, 0x37ff0000 | (kernel_entry & 0xffff)); /* ori ra, ra, low(kernel_entry) */ - stl_p(p++, 0x03e00009); /* jalr ra */ - stl_p(p++, 0x00000000); /* nop */ + stl_p(p++, 0x3c1f0000 | + ((kernel_entry >> 16) & 0xffff)); /* lui ra, high(kernel_entry) */ + stl_p(p++, 0x37ff0000 | + (kernel_entry & 0xffff)); /* ori ra, ra, low(kernel_entry) */ + stl_p(p++, 0x03e00009); /* jalr ra */ + stl_p(p++, 0x00000000); /* nop */ /* YAMON subroutines */ p = (uint32_t *) (base + 0x800); - stl_p(p++, 0x03e00009); /* jalr ra */ - stl_p(p++, 0x24020000); /* li v0,0 */ + stl_p(p++, 0x03e00009); /* jalr ra */ + stl_p(p++, 0x24020000); /* li v0,0 */ /* 808 YAMON print */ - stl_p(p++, 0x03e06821); /* move t5,ra */ - stl_p(p++, 0x00805821); /* move t3,a0 */ - stl_p(p++, 0x00a05021); /* move t2,a1 */ - stl_p(p++, 0x91440000); /* lbu a0,0(t2) */ - stl_p(p++, 0x254a0001); /* addiu t2,t2,1 */ - stl_p(p++, 0x10800005); /* beqz a0,834 */ - stl_p(p++, 0x00000000); /* nop */ - stl_p(p++, 0x0ff0021c); /* jal 870 */ - stl_p(p++, 0x00000000); /* nop */ - stl_p(p++, 0x1000fff9); /* b 814 */ - stl_p(p++, 0x00000000); /* nop */ - stl_p(p++, 0x01a00009); /* jalr t5 */ - stl_p(p++, 0x01602021); /* move a0,t3 */ + stl_p(p++, 0x03e06821); /* move t5,ra */ + stl_p(p++, 0x00805821); /* move t3,a0 */ + stl_p(p++, 0x00a05021); /* move t2,a1 */ + stl_p(p++, 0x91440000); /* lbu a0,0(t2) */ + stl_p(p++, 0x254a0001); /* addiu t2,t2,1 */ + stl_p(p++, 0x10800005); /* beqz a0,834 */ + stl_p(p++, 0x00000000); /* nop */ + stl_p(p++, 0x0ff0021c); /* jal 870 */ + stl_p(p++, 0x00000000); /* nop */ + stl_p(p++, 0x1000fff9); /* b 814 */ + stl_p(p++, 0x00000000); /* nop */ + stl_p(p++, 0x01a00009); /* jalr t5 */ + stl_p(p++, 0x01602021); /* move a0,t3 */ /* 0x83c YAMON print_count */ - stl_p(p++, 0x03e06821); /* move t5,ra */ - stl_p(p++, 0x00805821); /* move t3,a0 */ - stl_p(p++, 0x00a05021); /* move t2,a1 */ - stl_p(p++, 0x00c06021); /* move t4,a2 */ - stl_p(p++, 0x91440000); /* lbu a0,0(t2) */ - stl_p(p++, 0x0ff0021c); /* jal 870 */ - stl_p(p++, 0x00000000); /* nop */ - stl_p(p++, 0x254a0001); /* addiu t2,t2,1 */ - stl_p(p++, 0x258cffff); /* addiu t4,t4,-1 */ - stl_p(p++, 0x1580fffa); /* bnez t4,84c */ - stl_p(p++, 0x00000000); /* nop */ - stl_p(p++, 0x01a00009); /* jalr t5 */ - stl_p(p++, 0x01602021); /* move a0,t3 */ + stl_p(p++, 0x03e06821); /* move t5,ra */ + stl_p(p++, 0x00805821); /* move t3,a0 */ + stl_p(p++, 0x00a05021); /* move t2,a1 */ + stl_p(p++, 0x00c06021); /* move t4,a2 */ + stl_p(p++, 0x91440000); /* lbu a0,0(t2) */ + stl_p(p++, 0x0ff0021c); /* jal 870 */ + stl_p(p++, 0x00000000); /* nop */ + stl_p(p++, 0x254a0001); /* addiu t2,t2,1 */ + stl_p(p++, 0x258cffff); /* addiu t4,t4,-1 */ + stl_p(p++, 0x1580fffa); /* bnez t4,84c */ + stl_p(p++, 0x00000000); /* nop */ + stl_p(p++, 0x01a00009); /* jalr t5 */ + stl_p(p++, 0x01602021); /* move a0,t3 */ /* 0x870 */ - stl_p(p++, 0x3c08b800); /* lui t0,0xb400 */ - stl_p(p++, 0x350803f8); /* ori t0,t0,0x3f8 */ - stl_p(p++, 0x91090005); /* lbu t1,5(t0) */ - stl_p(p++, 0x00000000); /* nop */ - stl_p(p++, 0x31290040); /* andi t1,t1,0x40 */ - stl_p(p++, 0x1120fffc); /* beqz t1,878 */ - stl_p(p++, 0x00000000); /* nop */ - stl_p(p++, 0x03e00009); /* jalr ra */ - stl_p(p++, 0xa1040000); /* sb a0,0(t0) */ + stl_p(p++, 0x3c08b800); /* lui t0,0xb400 */ + stl_p(p++, 0x350803f8); /* ori t0,t0,0x3f8 */ + stl_p(p++, 0x91090005); /* lbu t1,5(t0) */ + stl_p(p++, 0x00000000); /* nop */ + stl_p(p++, 0x31290040); /* andi t1,t1,0x40 */ + stl_p(p++, 0x1120fffc); /* beqz t1,878 */ + stl_p(p++, 0x00000000); /* nop */ + stl_p(p++, 0x03e00009); /* jalr ra */ + stl_p(p++, 0xa1040000); /* sb a0,0(t0) */ } @@ -1037,7 +1039,8 @@ static int64_t load_kernel(void) kernel_size = load_elf(loaderparams.kernel_filename, NULL, cpu_mips_kseg0_to_phys, NULL, (uint64_t *)&kernel_entry, NULL, - (uint64_t *)&kernel_high, big_endian, EM_MIPS, 1, 0); + (uint64_t *)&kernel_high, NULL, big_endian, EM_MIPS, + 1, 0); if (kernel_size < 0) { error_report("could not load kernel '%s': %s", loaderparams.kernel_filename, @@ -1221,7 +1224,6 @@ void mips_malta_init(MachineState *machine) char *filename; PFlashCFI01 *fl; MemoryRegion *system_memory = get_system_memory(); - MemoryRegion *ram_high = g_new(MemoryRegion, 1); MemoryRegion *ram_low_preio = g_new(MemoryRegion, 1); MemoryRegion *ram_low_postio; MemoryRegion *bios, *bios_copy = g_new(MemoryRegion, 1); @@ -1242,7 +1244,7 @@ void mips_malta_init(MachineState *machine) /* * The whole address space decoded by the GT-64120A doesn't generate * exception when accessing invalid memory. Create an empty slot to - * emulate this feature.\ + * emulate this feature. */ empty_slot_init(0, 0x20000000); @@ -1259,13 +1261,11 @@ void mips_malta_init(MachineState *machine) } /* register RAM at high address where it is undisturbed by IO */ - memory_region_allocate_system_memory(ram_high, NULL, "mips_malta.ram", - ram_size); - memory_region_add_subregion(system_memory, 0x80000000, ram_high); + memory_region_add_subregion(system_memory, 0x80000000, machine->ram); /* alias for pre IO hole access */ memory_region_init_alias(ram_low_preio, NULL, "mips_malta_low_preio.ram", - ram_high, 0, MIN(ram_size, 256 * MiB)); + machine->ram, 0, MIN(ram_size, 256 * MiB)); memory_region_add_subregion(system_memory, 0, ram_low_preio); /* alias for post IO hole access, if there is enough RAM */ @@ -1273,7 +1273,7 @@ void mips_malta_init(MachineState *machine) ram_low_postio = g_new(MemoryRegion, 1); memory_region_init_alias(ram_low_postio, NULL, "mips_malta_low_postio.ram", - ram_high, 512 * MiB, + machine->ram, 512 * MiB, ram_size - 512 * MiB); memory_region_add_subregion(system_memory, 512 * MiB, ram_low_postio); @@ -1403,7 +1403,7 @@ void mips_malta_init(MachineState *machine) pci_bus = gt64120_register(s->i8259); /* Southbridge */ - dev = piix4_create(pci_bus, &isa_bus, &smbus, MAX_IDE_BUS); + dev = piix4_create(pci_bus, &isa_bus, &smbus); /* Interrupt controller */ qdev_connect_gpio_out_named(dev, "intr", 0, i8259_irq); @@ -1439,12 +1439,13 @@ static void mips_malta_machine_init(MachineClass *mc) mc->init = mips_malta_init; mc->block_default_type = IF_IDE; mc->max_cpus = 16; - mc->is_default = 1; + mc->is_default = true; #ifdef TARGET_MIPS64 mc->default_cpu_type = MIPS_CPU_TYPE_NAME("20Kc"); #else mc->default_cpu_type = MIPS_CPU_TYPE_NAME("24Kf"); #endif + mc->default_ram_id = "mips_malta.ram"; } DEFINE_MACHINE("malta", mips_malta_machine_init) diff --git a/hw/mips/mips_mipssim.c b/hw/mips/mips_mipssim.c index 282bbecb24..d220318939 100644 --- a/hw/mips/mips_mipssim.c +++ b/hw/mips/mips_mipssim.c @@ -40,6 +40,7 @@ #include "hw/loader.h" #include "elf.h" #include "hw/sysbus.h" +#include "hw/qdev-properties.h" #include "exec/address-spaces.h" #include "qemu/error-report.h" #include "sysemu/qtest.h" @@ -73,7 +74,7 @@ static int64_t load_kernel(void) kernel_size = load_elf(loaderparams.kernel_filename, NULL, cpu_mips_kseg0_to_phys, NULL, (uint64_t *)&entry, NULL, - (uint64_t *)&kernel_high, big_endian, + (uint64_t *)&kernel_high, NULL, big_endian, EM_MIPS, 1, 0); if (kernel_size >= 0) { if ((entry & ~0x7fffffffULL) == 0x80000000) { @@ -142,14 +143,12 @@ static void mipsnet_init(int base, qemu_irq irq, NICInfo *nd) static void mips_mipssim_init(MachineState *machine) { - ram_addr_t ram_size = machine->ram_size; const char *kernel_filename = machine->kernel_filename; const char *kernel_cmdline = machine->kernel_cmdline; const char *initrd_filename = machine->initrd_filename; char *filename; MemoryRegion *address_space_mem = get_system_memory(); MemoryRegion *isa = g_new(MemoryRegion, 1); - MemoryRegion *ram = g_new(MemoryRegion, 1); MemoryRegion *bios = g_new(MemoryRegion, 1); MIPSCPU *cpu; CPUMIPSState *env; @@ -166,13 +165,10 @@ mips_mipssim_init(MachineState *machine) qemu_register_reset(main_cpu_reset, reset_info); /* Allocate RAM. */ - memory_region_allocate_system_memory(ram, NULL, "mips_mipssim.ram", - ram_size); - memory_region_init_ram(bios, NULL, "mips_mipssim.bios", BIOS_SIZE, + memory_region_init_rom(bios, NULL, "mips_mipssim.bios", BIOS_SIZE, &error_fatal); - memory_region_set_readonly(bios, true); - memory_region_add_subregion(address_space_mem, 0, ram); + memory_region_add_subregion(address_space_mem, 0, machine->ram); /* Map the BIOS / boot exception handler. */ memory_region_add_subregion(address_space_mem, 0x1fc00000LL, bios); @@ -199,7 +195,7 @@ mips_mipssim_init(MachineState *machine) } if (kernel_filename) { - loaderparams.ram_size = ram_size; + loaderparams.ram_size = machine->ram_size; loaderparams.kernel_filename = kernel_filename; loaderparams.kernel_cmdline = kernel_cmdline; loaderparams.initrd_filename = initrd_filename; @@ -219,9 +215,16 @@ mips_mipssim_init(MachineState *machine) * A single 16450 sits at offset 0x3f8. It is attached to * MIPS CPU INT2, which is interrupt 4. */ - if (serial_hd(0)) - serial_init(0x3f8, env->irq[4], 115200, serial_hd(0), - get_system_io()); + if (serial_hd(0)) { + DeviceState *dev = qdev_create(NULL, TYPE_SERIAL_IO); + + qdev_prop_set_chr(dev, "chardev", serial_hd(0)); + qdev_set_legacy_instance_id(dev, 0x3f8, 2); + qdev_init_nofail(dev); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, env->irq[4]); + sysbus_add_io(SYS_BUS_DEVICE(dev), 0x3f8, + sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0)); + } if (nd_table[0].used) /* MIPSnet uses the MIPS CPU INT0, which is interrupt 2. */ @@ -237,6 +240,7 @@ static void mips_mipssim_machine_init(MachineClass *mc) #else mc->default_cpu_type = MIPS_CPU_TYPE_NAME("24Kf"); #endif + mc->default_ram_id = "mips_mipssim.ram"; } DEFINE_MACHINE("mipssim", mips_mipssim_machine_init) diff --git a/hw/mips/mips_r4k.c b/hw/mips/mips_r4k.c index 70024235ae..3487013a4a 100644 --- a/hw/mips/mips_r4k.c +++ b/hw/mips/mips_r4k.c @@ -6,7 +6,7 @@ * ISA memory at the 0x10000000 (PHYS, 16Mb in size). * All peripherial devices are attached to this "bus" with * the standard PC ISA addresses. -*/ + */ #include "qemu/osdep.h" #include "qemu/units.h" @@ -15,7 +15,7 @@ #include "cpu.h" #include "hw/mips/mips.h" #include "hw/mips/cpudevs.h" -#include "hw/i386/pc.h" +#include "hw/intc/i8259.h" #include "hw/char/serial.h" #include "hw/isa/isa.h" #include "net/net.h" @@ -26,6 +26,7 @@ #include "qemu/log.h" #include "hw/mips/bios.h" #include "hw/ide.h" +#include "hw/ide/internal.h" #include "hw/loader.h" #include "elf.h" #include "hw/rtc/mc146818rtc.h" @@ -54,17 +55,18 @@ static struct _loaderparams { const char *initrd_filename; } loaderparams; -static void mips_qemu_write (void *opaque, hwaddr addr, - uint64_t val, unsigned size) +static void mips_qemu_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) { - if ((addr & 0xffff) == 0 && val == 42) + if ((addr & 0xffff) == 0 && val == 42) { qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); - else if ((addr & 0xffff) == 4 && val == 42) + } else if ((addr & 0xffff) == 4 && val == 42) { qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); + } } -static uint64_t mips_qemu_read (void *opaque, hwaddr addr, - unsigned size) +static uint64_t mips_qemu_read(void *opaque, hwaddr addr, + unsigned size) { return 0; } @@ -97,11 +99,12 @@ static int64_t load_kernel(void) kernel_size = load_elf(loaderparams.kernel_filename, NULL, cpu_mips_kseg0_to_phys, NULL, (uint64_t *)&entry, NULL, - (uint64_t *)&kernel_high, big_endian, + (uint64_t *)&kernel_high, NULL, big_endian, EM_MIPS, 1, 0); if (kernel_size >= 0) { - if ((entry & ~0x7fffffffULL) == 0x80000000) + if ((entry & ~0x7fffffffULL) == 0x80000000) { entry = (int32_t)entry; + } } else { error_report("could not load kernel '%s': %s", loaderparams.kernel_filename, @@ -113,9 +116,10 @@ static int64_t load_kernel(void) initrd_size = 0; initrd_offset = 0; if (loaderparams.initrd_filename) { - initrd_size = get_image_size (loaderparams.initrd_filename); + initrd_size = get_image_size(loaderparams.initrd_filename); if (initrd_size > 0) { - initrd_offset = (kernel_high + ~INITRD_PAGE_MASK) & INITRD_PAGE_MASK; + initrd_offset = (kernel_high + ~INITRD_PAGE_MASK) & + INITRD_PAGE_MASK; if (initrd_offset + initrd_size > ram_size) { error_report("memory too small for initial ram disk '%s'", loaderparams.initrd_filename); @@ -139,11 +143,13 @@ static int64_t load_kernel(void) params_buf[1] = tswap32(0x12345678); if (initrd_size > 0) { - snprintf((char *)params_buf + 8, 256, "rd_start=0x%" PRIx64 " rd_size=%" PRId64 " %s", + snprintf((char *)params_buf + 8, 256, + "rd_start=0x%" PRIx64 " rd_size=%" PRId64 " %s", cpu_mips_phys_to_kseg0(NULL, initrd_offset), initrd_size, loaderparams.kernel_cmdline); } else { - snprintf((char *)params_buf + 8, 256, "%s", loaderparams.kernel_cmdline); + snprintf((char *)params_buf + 8, 256, + "%s", loaderparams.kernel_cmdline); } rom_add_blob_fixed("params", params_buf, params_size, @@ -166,13 +172,11 @@ static const int sector_len = 32 * KiB; static void mips_r4k_init(MachineState *machine) { - ram_addr_t ram_size = machine->ram_size; const char *kernel_filename = machine->kernel_filename; const char *kernel_cmdline = machine->kernel_cmdline; const char *initrd_filename = machine->initrd_filename; char *filename; MemoryRegion *address_space_mem = get_system_memory(); - MemoryRegion *ram = g_new(MemoryRegion, 1); MemoryRegion *bios; MemoryRegion *iomem = g_new(MemoryRegion, 1); MemoryRegion *isa_io = g_new(MemoryRegion, 1); @@ -198,24 +202,28 @@ void mips_r4k_init(MachineState *machine) qemu_register_reset(main_cpu_reset, reset_info); /* allocate RAM */ - if (ram_size > 256 * MiB) { + if (machine->ram_size > 256 * MiB) { error_report("Too much memory for this machine: %" PRId64 "MB," " maximum 256MB", ram_size / MiB); exit(1); } - memory_region_allocate_system_memory(ram, NULL, "mips_r4k.ram", ram_size); + memory_region_add_subregion(address_space_mem, 0, machine->ram); - memory_region_add_subregion(address_space_mem, 0, ram); + memory_region_init_io(iomem, NULL, &mips_qemu_ops, + NULL, "mips-qemu", 0x10000); - memory_region_init_io(iomem, NULL, &mips_qemu_ops, NULL, "mips-qemu", 0x10000); memory_region_add_subregion(address_space_mem, 0x1fbf0000, iomem); - /* Try to load a BIOS image. If this fails, we continue regardless, - but initialize the hardware ourselves. When a kernel gets - preloaded we also initialize the hardware, since the BIOS wasn't - run. */ - if (bios_name == NULL) + /* + * Try to load a BIOS image. If this fails, we continue regardless, + * but initialize the hardware ourselves. When a kernel gets + * preloaded we also initialize the hardware, since the BIOS wasn't + * run. + */ + + if (bios_name == NULL) { bios_name = BIOS_FILENAME; + } filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); if (filename) { bios_size = get_image_size(filename); @@ -227,15 +235,15 @@ void mips_r4k_init(MachineState *machine) #else be = 0; #endif + dinfo = drive_get(IF_PFLASH, 0, 0); if ((bios_size > 0) && (bios_size <= BIOS_SIZE)) { bios = g_new(MemoryRegion, 1); - memory_region_init_ram(bios, NULL, "mips_r4k.bios", BIOS_SIZE, + memory_region_init_rom(bios, NULL, "mips_r4k.bios", BIOS_SIZE, &error_fatal); - memory_region_set_readonly(bios, true); memory_region_add_subregion(get_system_memory(), 0x1fc00000, bios); load_image_targphys(filename, 0x1fc00000, BIOS_SIZE); - } else if ((dinfo = drive_get(IF_PFLASH, 0, 0)) != NULL) { + } else if (dinfo != NULL) { uint32_t mips_rom = 0x00400000; if (!pflash_cfi01_register(0x1fc00000, "mips_r4k.bios", mips_rom, blk_by_legacy_dinfo(dinfo), @@ -249,7 +257,7 @@ void mips_r4k_init(MachineState *machine) g_free(filename); if (kernel_filename) { - loaderparams.ram_size = ram_size; + loaderparams.ram_size = machine->ram_size; loaderparams.kernel_filename = kernel_filename; loaderparams.kernel_cmdline = kernel_cmdline; loaderparams.initrd_filename = initrd_filename; @@ -280,11 +288,12 @@ void mips_r4k_init(MachineState *machine) isa_vga_init(isa_bus); - if (nd_table[0].used) + if (nd_table[0].used) { isa_ne2000_init(isa_bus, 0x300, 9, &nd_table[0]); + } ide_drive_get(hd, ARRAY_SIZE(hd)); - for(i = 0; i < MAX_IDE_BUS; i++) + for (i = 0; i < MAX_IDE_BUS; i++) isa_ide_init(isa_bus, ide_iobase[i], ide_iobase2[i], ide_irq[i], hd[MAX_IDE_DEVS * i], hd[MAX_IDE_DEVS * i + 1]); @@ -294,6 +303,7 @@ void mips_r4k_init(MachineState *machine) static void mips_machine_init(MachineClass *mc) { + mc->deprecation_reason = "use malta machine type instead"; mc->desc = "mips r4k platform"; mc->init = mips_r4k_init; mc->block_default_type = IF_IDE; @@ -302,7 +312,7 @@ static void mips_machine_init(MachineClass *mc) #else mc->default_cpu_type = MIPS_CPU_TYPE_NAME("24Kf"); #endif - + mc->default_ram_id = "mips_r4k.ram"; } DEFINE_MACHINE("mips", mips_machine_init) diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig index 2164646553..bdd77d8020 100644 --- a/hw/misc/Kconfig +++ b/hw/misc/Kconfig @@ -82,6 +82,12 @@ config IMX config STM32F2XX_SYSCFG bool +config STM32F4XX_SYSCFG + bool + +config STM32F4XX_EXTI + bool + config MIPS_ITU bool diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs index ba898a5781..68aae2eabb 100644 --- a/hw/misc/Makefile.objs +++ b/hw/misc/Makefile.objs @@ -28,6 +28,11 @@ common-obj-$(CONFIG_MACIO) += macio/ common-obj-$(CONFIG_IVSHMEM_DEVICE) += ivshmem.o +common-obj-$(CONFIG_ALLWINNER_H3) += allwinner-h3-ccu.o +obj-$(CONFIG_ALLWINNER_H3) += allwinner-cpucfg.o +common-obj-$(CONFIG_ALLWINNER_H3) += allwinner-h3-dramc.o +common-obj-$(CONFIG_ALLWINNER_H3) += allwinner-h3-sysctrl.o +common-obj-$(CONFIG_ALLWINNER_H3) += allwinner-sid.o common-obj-$(CONFIG_REALVIEW) += arm_sysctl.o common-obj-$(CONFIG_NSERIES) += cbus.o common-obj-$(CONFIG_ECCMEMCTL) += eccmemctl.o @@ -42,6 +47,7 @@ common-obj-$(CONFIG_IMX) += imx7_ccm.o common-obj-$(CONFIG_IMX) += imx2_wdt.o common-obj-$(CONFIG_IMX) += imx7_snvs.o common-obj-$(CONFIG_IMX) += imx7_gpr.o +common-obj-$(CONFIG_IMX) += imx_rngc.o common-obj-$(CONFIG_MILKYMIST) += milkymist-hpdmc.o common-obj-$(CONFIG_MILKYMIST) += milkymist-pfpu.o common-obj-$(CONFIG_MAINSTONE) += mst_fpga.o @@ -58,6 +64,8 @@ common-obj-$(CONFIG_SLAVIO) += slavio_misc.o common-obj-$(CONFIG_ZYNQ) += zynq_slcr.o common-obj-$(CONFIG_ZYNQ) += zynq-xadc.o common-obj-$(CONFIG_STM32F2XX_SYSCFG) += stm32f2xx_syscfg.o +common-obj-$(CONFIG_STM32F4XX_SYSCFG) += stm32f4xx_syscfg.o +common-obj-$(CONFIG_STM32F4XX_EXTI) += stm32f4xx_exti.o obj-$(CONFIG_MIPS_CPS) += mips_cmgcr.o obj-$(CONFIG_MIPS_CPS) += mips_cpc.o obj-$(CONFIG_MIPS_ITU) += mips_itu.o diff --git a/hw/misc/a9scu.c b/hw/misc/a9scu.c index 45c91db303..324371a1c0 100644 --- a/hw/misc/a9scu.c +++ b/hw/misc/a9scu.c @@ -135,7 +135,7 @@ static void a9_scu_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->props = a9_scu_properties; + device_class_set_props(dc, a9_scu_properties); dc->vmsd = &vmstate_a9_scu; dc->reset = a9_scu_reset; } diff --git a/hw/misc/allwinner-cpucfg.c b/hw/misc/allwinner-cpucfg.c new file mode 100644 index 0000000000..bbd33a7dac --- /dev/null +++ b/hw/misc/allwinner-cpucfg.c @@ -0,0 +1,282 @@ +/* + * Allwinner CPU Configuration Module emulation + * + * Copyright (C) 2019 Niek Linnenbank + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "hw/sysbus.h" +#include "migration/vmstate.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "qemu/error-report.h" +#include "qemu/timer.h" +#include "hw/core/cpu.h" +#include "target/arm/arm-powerctl.h" +#include "target/arm/cpu.h" +#include "hw/misc/allwinner-cpucfg.h" +#include "trace.h" + +/* CPUCFG register offsets */ +enum { + REG_CPUS_RST_CTRL = 0x0000, /* CPUs Reset Control */ + REG_CPU0_RST_CTRL = 0x0040, /* CPU#0 Reset Control */ + REG_CPU0_CTRL = 0x0044, /* CPU#0 Control */ + REG_CPU0_STATUS = 0x0048, /* CPU#0 Status */ + REG_CPU1_RST_CTRL = 0x0080, /* CPU#1 Reset Control */ + REG_CPU1_CTRL = 0x0084, /* CPU#1 Control */ + REG_CPU1_STATUS = 0x0088, /* CPU#1 Status */ + REG_CPU2_RST_CTRL = 0x00C0, /* CPU#2 Reset Control */ + REG_CPU2_CTRL = 0x00C4, /* CPU#2 Control */ + REG_CPU2_STATUS = 0x00C8, /* CPU#2 Status */ + REG_CPU3_RST_CTRL = 0x0100, /* CPU#3 Reset Control */ + REG_CPU3_CTRL = 0x0104, /* CPU#3 Control */ + REG_CPU3_STATUS = 0x0108, /* CPU#3 Status */ + REG_CPU_SYS_RST = 0x0140, /* CPU System Reset */ + REG_CLK_GATING = 0x0144, /* CPU Clock Gating */ + REG_GEN_CTRL = 0x0184, /* General Control */ + REG_SUPER_STANDBY = 0x01A0, /* Super Standby Flag */ + REG_ENTRY_ADDR = 0x01A4, /* Reset Entry Address */ + REG_DBG_EXTERN = 0x01E4, /* Debug External */ + REG_CNT64_CTRL = 0x0280, /* 64-bit Counter Control */ + REG_CNT64_LOW = 0x0284, /* 64-bit Counter Low */ + REG_CNT64_HIGH = 0x0288, /* 64-bit Counter High */ +}; + +/* CPUCFG register flags */ +enum { + CPUX_RESET_RELEASED = ((1 << 1) | (1 << 0)), + CPUX_STATUS_SMP = (1 << 0), + CPU_SYS_RESET_RELEASED = (1 << 0), + CLK_GATING_ENABLE = ((1 << 8) | 0xF), +}; + +/* CPUCFG register reset values */ +enum { + REG_CLK_GATING_RST = 0x0000010F, + REG_GEN_CTRL_RST = 0x00000020, + REG_SUPER_STANDBY_RST = 0x0, + REG_CNT64_CTRL_RST = 0x0, +}; + +/* CPUCFG constants */ +enum { + CPU_EXCEPTION_LEVEL_ON_RESET = 3, /* EL3 */ +}; + +static void allwinner_cpucfg_cpu_reset(AwCpuCfgState *s, uint8_t cpu_id) +{ + int ret; + + trace_allwinner_cpucfg_cpu_reset(cpu_id, s->entry_addr); + + ARMCPU *target_cpu = ARM_CPU(arm_get_cpu_by_id(cpu_id)); + if (!target_cpu) { + /* + * Called with a bogus value for cpu_id. Guest error will + * already have been logged, we can simply return here. + */ + return; + } + bool target_aa64 = arm_feature(&target_cpu->env, ARM_FEATURE_AARCH64); + + ret = arm_set_cpu_on(cpu_id, s->entry_addr, 0, + CPU_EXCEPTION_LEVEL_ON_RESET, target_aa64); + if (ret != QEMU_ARM_POWERCTL_RET_SUCCESS) { + error_report("%s: failed to bring up CPU %d: err %d", + __func__, cpu_id, ret); + return; + } +} + +static uint64_t allwinner_cpucfg_read(void *opaque, hwaddr offset, + unsigned size) +{ + const AwCpuCfgState *s = AW_CPUCFG(opaque); + uint64_t val = 0; + + switch (offset) { + case REG_CPUS_RST_CTRL: /* CPUs Reset Control */ + case REG_CPU_SYS_RST: /* CPU System Reset */ + val = CPU_SYS_RESET_RELEASED; + break; + case REG_CPU0_RST_CTRL: /* CPU#0 Reset Control */ + case REG_CPU1_RST_CTRL: /* CPU#1 Reset Control */ + case REG_CPU2_RST_CTRL: /* CPU#2 Reset Control */ + case REG_CPU3_RST_CTRL: /* CPU#3 Reset Control */ + val = CPUX_RESET_RELEASED; + break; + case REG_CPU0_CTRL: /* CPU#0 Control */ + case REG_CPU1_CTRL: /* CPU#1 Control */ + case REG_CPU2_CTRL: /* CPU#2 Control */ + case REG_CPU3_CTRL: /* CPU#3 Control */ + val = 0; + break; + case REG_CPU0_STATUS: /* CPU#0 Status */ + case REG_CPU1_STATUS: /* CPU#1 Status */ + case REG_CPU2_STATUS: /* CPU#2 Status */ + case REG_CPU3_STATUS: /* CPU#3 Status */ + val = CPUX_STATUS_SMP; + break; + case REG_CLK_GATING: /* CPU Clock Gating */ + val = CLK_GATING_ENABLE; + break; + case REG_GEN_CTRL: /* General Control */ + val = s->gen_ctrl; + break; + case REG_SUPER_STANDBY: /* Super Standby Flag */ + val = s->super_standby; + break; + case REG_ENTRY_ADDR: /* Reset Entry Address */ + val = s->entry_addr; + break; + case REG_DBG_EXTERN: /* Debug External */ + case REG_CNT64_CTRL: /* 64-bit Counter Control */ + case REG_CNT64_LOW: /* 64-bit Counter Low */ + case REG_CNT64_HIGH: /* 64-bit Counter High */ + qemu_log_mask(LOG_UNIMP, "%s: unimplemented register at 0x%04x\n", + __func__, (uint32_t)offset); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n", + __func__, (uint32_t)offset); + break; + } + + trace_allwinner_cpucfg_read(offset, val, size); + + return val; +} + +static void allwinner_cpucfg_write(void *opaque, hwaddr offset, + uint64_t val, unsigned size) +{ + AwCpuCfgState *s = AW_CPUCFG(opaque); + + trace_allwinner_cpucfg_write(offset, val, size); + + switch (offset) { + case REG_CPUS_RST_CTRL: /* CPUs Reset Control */ + case REG_CPU_SYS_RST: /* CPU System Reset */ + break; + case REG_CPU0_RST_CTRL: /* CPU#0 Reset Control */ + case REG_CPU1_RST_CTRL: /* CPU#1 Reset Control */ + case REG_CPU2_RST_CTRL: /* CPU#2 Reset Control */ + case REG_CPU3_RST_CTRL: /* CPU#3 Reset Control */ + if (val) { + allwinner_cpucfg_cpu_reset(s, (offset - REG_CPU0_RST_CTRL) >> 6); + } + break; + case REG_CPU0_CTRL: /* CPU#0 Control */ + case REG_CPU1_CTRL: /* CPU#1 Control */ + case REG_CPU2_CTRL: /* CPU#2 Control */ + case REG_CPU3_CTRL: /* CPU#3 Control */ + case REG_CPU0_STATUS: /* CPU#0 Status */ + case REG_CPU1_STATUS: /* CPU#1 Status */ + case REG_CPU2_STATUS: /* CPU#2 Status */ + case REG_CPU3_STATUS: /* CPU#3 Status */ + case REG_CLK_GATING: /* CPU Clock Gating */ + break; + case REG_GEN_CTRL: /* General Control */ + s->gen_ctrl = val; + break; + case REG_SUPER_STANDBY: /* Super Standby Flag */ + s->super_standby = val; + break; + case REG_ENTRY_ADDR: /* Reset Entry Address */ + s->entry_addr = val; + break; + case REG_DBG_EXTERN: /* Debug External */ + case REG_CNT64_CTRL: /* 64-bit Counter Control */ + case REG_CNT64_LOW: /* 64-bit Counter Low */ + case REG_CNT64_HIGH: /* 64-bit Counter High */ + qemu_log_mask(LOG_UNIMP, "%s: unimplemented register at 0x%04x\n", + __func__, (uint32_t)offset); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n", + __func__, (uint32_t)offset); + break; + } +} + +static const MemoryRegionOps allwinner_cpucfg_ops = { + .read = allwinner_cpucfg_read, + .write = allwinner_cpucfg_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, + .impl.min_access_size = 4, +}; + +static void allwinner_cpucfg_reset(DeviceState *dev) +{ + AwCpuCfgState *s = AW_CPUCFG(dev); + + /* Set default values for registers */ + s->gen_ctrl = REG_GEN_CTRL_RST; + s->super_standby = REG_SUPER_STANDBY_RST; + s->entry_addr = 0; +} + +static void allwinner_cpucfg_init(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + AwCpuCfgState *s = AW_CPUCFG(obj); + + /* Memory mapping */ + memory_region_init_io(&s->iomem, OBJECT(s), &allwinner_cpucfg_ops, s, + TYPE_AW_CPUCFG, 1 * KiB); + sysbus_init_mmio(sbd, &s->iomem); +} + +static const VMStateDescription allwinner_cpucfg_vmstate = { + .name = "allwinner-cpucfg", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(gen_ctrl, AwCpuCfgState), + VMSTATE_UINT32(super_standby, AwCpuCfgState), + VMSTATE_UINT32(entry_addr, AwCpuCfgState), + VMSTATE_END_OF_LIST() + } +}; + +static void allwinner_cpucfg_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = allwinner_cpucfg_reset; + dc->vmsd = &allwinner_cpucfg_vmstate; +} + +static const TypeInfo allwinner_cpucfg_info = { + .name = TYPE_AW_CPUCFG, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_init = allwinner_cpucfg_init, + .instance_size = sizeof(AwCpuCfgState), + .class_init = allwinner_cpucfg_class_init, +}; + +static void allwinner_cpucfg_register(void) +{ + type_register_static(&allwinner_cpucfg_info); +} + +type_init(allwinner_cpucfg_register) diff --git a/hw/misc/allwinner-h3-ccu.c b/hw/misc/allwinner-h3-ccu.c new file mode 100644 index 0000000000..18d1074545 --- /dev/null +++ b/hw/misc/allwinner-h3-ccu.c @@ -0,0 +1,242 @@ +/* + * Allwinner H3 Clock Control Unit emulation + * + * Copyright (C) 2019 Niek Linnenbank + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "hw/sysbus.h" +#include "migration/vmstate.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "hw/misc/allwinner-h3-ccu.h" + +/* CCU register offsets */ +enum { + REG_PLL_CPUX = 0x0000, /* PLL CPUX Control */ + REG_PLL_AUDIO = 0x0008, /* PLL Audio Control */ + REG_PLL_VIDEO = 0x0010, /* PLL Video Control */ + REG_PLL_VE = 0x0018, /* PLL VE Control */ + REG_PLL_DDR = 0x0020, /* PLL DDR Control */ + REG_PLL_PERIPH0 = 0x0028, /* PLL Peripherals 0 Control */ + REG_PLL_GPU = 0x0038, /* PLL GPU Control */ + REG_PLL_PERIPH1 = 0x0044, /* PLL Peripherals 1 Control */ + REG_PLL_DE = 0x0048, /* PLL Display Engine Control */ + REG_CPUX_AXI = 0x0050, /* CPUX/AXI Configuration */ + REG_APB1 = 0x0054, /* ARM Peripheral Bus 1 Config */ + REG_APB2 = 0x0058, /* ARM Peripheral Bus 2 Config */ + REG_DRAM_CFG = 0x00F4, /* DRAM Configuration */ + REG_MBUS = 0x00FC, /* MBUS Reset */ + REG_PLL_TIME0 = 0x0200, /* PLL Stable Time 0 */ + REG_PLL_TIME1 = 0x0204, /* PLL Stable Time 1 */ + REG_PLL_CPUX_BIAS = 0x0220, /* PLL CPUX Bias */ + REG_PLL_AUDIO_BIAS = 0x0224, /* PLL Audio Bias */ + REG_PLL_VIDEO_BIAS = 0x0228, /* PLL Video Bias */ + REG_PLL_VE_BIAS = 0x022C, /* PLL VE Bias */ + REG_PLL_DDR_BIAS = 0x0230, /* PLL DDR Bias */ + REG_PLL_PERIPH0_BIAS = 0x0234, /* PLL Peripherals 0 Bias */ + REG_PLL_GPU_BIAS = 0x023C, /* PLL GPU Bias */ + REG_PLL_PERIPH1_BIAS = 0x0244, /* PLL Peripherals 1 Bias */ + REG_PLL_DE_BIAS = 0x0248, /* PLL Display Engine Bias */ + REG_PLL_CPUX_TUNING = 0x0250, /* PLL CPUX Tuning */ + REG_PLL_DDR_TUNING = 0x0260, /* PLL DDR Tuning */ +}; + +#define REG_INDEX(offset) (offset / sizeof(uint32_t)) + +/* CCU register flags */ +enum { + REG_DRAM_CFG_UPDATE = (1 << 16), +}; + +enum { + REG_PLL_ENABLE = (1 << 31), + REG_PLL_LOCK = (1 << 28), +}; + + +/* CCU register reset values */ +enum { + REG_PLL_CPUX_RST = 0x00001000, + REG_PLL_AUDIO_RST = 0x00035514, + REG_PLL_VIDEO_RST = 0x03006207, + REG_PLL_VE_RST = 0x03006207, + REG_PLL_DDR_RST = 0x00001000, + REG_PLL_PERIPH0_RST = 0x00041811, + REG_PLL_GPU_RST = 0x03006207, + REG_PLL_PERIPH1_RST = 0x00041811, + REG_PLL_DE_RST = 0x03006207, + REG_CPUX_AXI_RST = 0x00010000, + REG_APB1_RST = 0x00001010, + REG_APB2_RST = 0x01000000, + REG_DRAM_CFG_RST = 0x00000000, + REG_MBUS_RST = 0x80000000, + REG_PLL_TIME0_RST = 0x000000FF, + REG_PLL_TIME1_RST = 0x000000FF, + REG_PLL_CPUX_BIAS_RST = 0x08100200, + REG_PLL_AUDIO_BIAS_RST = 0x10100000, + REG_PLL_VIDEO_BIAS_RST = 0x10100000, + REG_PLL_VE_BIAS_RST = 0x10100000, + REG_PLL_DDR_BIAS_RST = 0x81104000, + REG_PLL_PERIPH0_BIAS_RST = 0x10100010, + REG_PLL_GPU_BIAS_RST = 0x10100000, + REG_PLL_PERIPH1_BIAS_RST = 0x10100010, + REG_PLL_DE_BIAS_RST = 0x10100000, + REG_PLL_CPUX_TUNING_RST = 0x0A101000, + REG_PLL_DDR_TUNING_RST = 0x14880000, +}; + +static uint64_t allwinner_h3_ccu_read(void *opaque, hwaddr offset, + unsigned size) +{ + const AwH3ClockCtlState *s = AW_H3_CCU(opaque); + const uint32_t idx = REG_INDEX(offset); + + switch (offset) { + case 0x308 ... AW_H3_CCU_IOSIZE: + qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n", + __func__, (uint32_t)offset); + return 0; + } + + return s->regs[idx]; +} + +static void allwinner_h3_ccu_write(void *opaque, hwaddr offset, + uint64_t val, unsigned size) +{ + AwH3ClockCtlState *s = AW_H3_CCU(opaque); + const uint32_t idx = REG_INDEX(offset); + + switch (offset) { + case REG_DRAM_CFG: /* DRAM Configuration */ + val &= ~REG_DRAM_CFG_UPDATE; + break; + case REG_PLL_CPUX: /* PLL CPUX Control */ + case REG_PLL_AUDIO: /* PLL Audio Control */ + case REG_PLL_VIDEO: /* PLL Video Control */ + case REG_PLL_VE: /* PLL VE Control */ + case REG_PLL_DDR: /* PLL DDR Control */ + case REG_PLL_PERIPH0: /* PLL Peripherals 0 Control */ + case REG_PLL_GPU: /* PLL GPU Control */ + case REG_PLL_PERIPH1: /* PLL Peripherals 1 Control */ + case REG_PLL_DE: /* PLL Display Engine Control */ + if (val & REG_PLL_ENABLE) { + val |= REG_PLL_LOCK; + } + break; + case 0x308 ... AW_H3_CCU_IOSIZE: + qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n", + __func__, (uint32_t)offset); + break; + default: + qemu_log_mask(LOG_UNIMP, "%s: unimplemented write offset 0x%04x\n", + __func__, (uint32_t)offset); + break; + } + + s->regs[idx] = (uint32_t) val; +} + +static const MemoryRegionOps allwinner_h3_ccu_ops = { + .read = allwinner_h3_ccu_read, + .write = allwinner_h3_ccu_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, + .impl.min_access_size = 4, +}; + +static void allwinner_h3_ccu_reset(DeviceState *dev) +{ + AwH3ClockCtlState *s = AW_H3_CCU(dev); + + /* Set default values for registers */ + s->regs[REG_INDEX(REG_PLL_CPUX)] = REG_PLL_CPUX_RST; + s->regs[REG_INDEX(REG_PLL_AUDIO)] = REG_PLL_AUDIO_RST; + s->regs[REG_INDEX(REG_PLL_VIDEO)] = REG_PLL_VIDEO_RST; + s->regs[REG_INDEX(REG_PLL_VE)] = REG_PLL_VE_RST; + s->regs[REG_INDEX(REG_PLL_DDR)] = REG_PLL_DDR_RST; + s->regs[REG_INDEX(REG_PLL_PERIPH0)] = REG_PLL_PERIPH0_RST; + s->regs[REG_INDEX(REG_PLL_GPU)] = REG_PLL_GPU_RST; + s->regs[REG_INDEX(REG_PLL_PERIPH1)] = REG_PLL_PERIPH1_RST; + s->regs[REG_INDEX(REG_PLL_DE)] = REG_PLL_DE_RST; + s->regs[REG_INDEX(REG_CPUX_AXI)] = REG_CPUX_AXI_RST; + s->regs[REG_INDEX(REG_APB1)] = REG_APB1_RST; + s->regs[REG_INDEX(REG_APB2)] = REG_APB2_RST; + s->regs[REG_INDEX(REG_DRAM_CFG)] = REG_DRAM_CFG_RST; + s->regs[REG_INDEX(REG_MBUS)] = REG_MBUS_RST; + s->regs[REG_INDEX(REG_PLL_TIME0)] = REG_PLL_TIME0_RST; + s->regs[REG_INDEX(REG_PLL_TIME1)] = REG_PLL_TIME1_RST; + s->regs[REG_INDEX(REG_PLL_CPUX_BIAS)] = REG_PLL_CPUX_BIAS_RST; + s->regs[REG_INDEX(REG_PLL_AUDIO_BIAS)] = REG_PLL_AUDIO_BIAS_RST; + s->regs[REG_INDEX(REG_PLL_VIDEO_BIAS)] = REG_PLL_VIDEO_BIAS_RST; + s->regs[REG_INDEX(REG_PLL_VE_BIAS)] = REG_PLL_VE_BIAS_RST; + s->regs[REG_INDEX(REG_PLL_DDR_BIAS)] = REG_PLL_DDR_BIAS_RST; + s->regs[REG_INDEX(REG_PLL_PERIPH0_BIAS)] = REG_PLL_PERIPH0_BIAS_RST; + s->regs[REG_INDEX(REG_PLL_GPU_BIAS)] = REG_PLL_GPU_BIAS_RST; + s->regs[REG_INDEX(REG_PLL_PERIPH1_BIAS)] = REG_PLL_PERIPH1_BIAS_RST; + s->regs[REG_INDEX(REG_PLL_DE_BIAS)] = REG_PLL_DE_BIAS_RST; + s->regs[REG_INDEX(REG_PLL_CPUX_TUNING)] = REG_PLL_CPUX_TUNING_RST; + s->regs[REG_INDEX(REG_PLL_DDR_TUNING)] = REG_PLL_DDR_TUNING_RST; +} + +static void allwinner_h3_ccu_init(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + AwH3ClockCtlState *s = AW_H3_CCU(obj); + + /* Memory mapping */ + memory_region_init_io(&s->iomem, OBJECT(s), &allwinner_h3_ccu_ops, s, + TYPE_AW_H3_CCU, AW_H3_CCU_IOSIZE); + sysbus_init_mmio(sbd, &s->iomem); +} + +static const VMStateDescription allwinner_h3_ccu_vmstate = { + .name = "allwinner-h3-ccu", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, AwH3ClockCtlState, AW_H3_CCU_REGS_NUM), + VMSTATE_END_OF_LIST() + } +}; + +static void allwinner_h3_ccu_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = allwinner_h3_ccu_reset; + dc->vmsd = &allwinner_h3_ccu_vmstate; +} + +static const TypeInfo allwinner_h3_ccu_info = { + .name = TYPE_AW_H3_CCU, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_init = allwinner_h3_ccu_init, + .instance_size = sizeof(AwH3ClockCtlState), + .class_init = allwinner_h3_ccu_class_init, +}; + +static void allwinner_h3_ccu_register(void) +{ + type_register_static(&allwinner_h3_ccu_info); +} + +type_init(allwinner_h3_ccu_register) diff --git a/hw/misc/allwinner-h3-dramc.c b/hw/misc/allwinner-h3-dramc.c new file mode 100644 index 0000000000..1d37cf422c --- /dev/null +++ b/hw/misc/allwinner-h3-dramc.c @@ -0,0 +1,358 @@ +/* + * Allwinner H3 SDRAM Controller emulation + * + * Copyright (C) 2019 Niek Linnenbank + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "qemu/error-report.h" +#include "hw/sysbus.h" +#include "migration/vmstate.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "exec/address-spaces.h" +#include "hw/qdev-properties.h" +#include "qapi/error.h" +#include "hw/misc/allwinner-h3-dramc.h" +#include "trace.h" + +#define REG_INDEX(offset) (offset / sizeof(uint32_t)) + +/* DRAMCOM register offsets */ +enum { + REG_DRAMCOM_CR = 0x0000, /* Control Register */ +}; + +/* DRAMCTL register offsets */ +enum { + REG_DRAMCTL_PIR = 0x0000, /* PHY Initialization Register */ + REG_DRAMCTL_PGSR = 0x0010, /* PHY General Status Register */ + REG_DRAMCTL_STATR = 0x0018, /* Status Register */ +}; + +/* DRAMCTL register flags */ +enum { + REG_DRAMCTL_PGSR_INITDONE = (1 << 0), +}; + +enum { + REG_DRAMCTL_STATR_ACTIVE = (1 << 0), +}; + +static void allwinner_h3_dramc_map_rows(AwH3DramCtlState *s, uint8_t row_bits, + uint8_t bank_bits, uint16_t page_size) +{ + /* + * This function simulates row addressing behavior when bootloader + * software attempts to detect the amount of available SDRAM. In U-Boot + * the controller is configured with the widest row addressing available. + * Then a pattern is written to RAM at an offset on the row boundary size. + * If the value read back equals the value read back from the + * start of RAM, the bootloader knows the amount of row bits. + * + * This function inserts a mirrored memory region when the configured row + * bits are not matching the actual emulated memory, to simulate the + * same behavior on hardware as expected by the bootloader. + */ + uint8_t row_bits_actual = 0; + + /* Calculate the actual row bits using the ram_size property */ + for (uint8_t i = 8; i < 12; i++) { + if (1 << i == s->ram_size) { + row_bits_actual = i + 3; + break; + } + } + + if (s->ram_size == (1 << (row_bits - 3))) { + /* When row bits is the expected value, remove the mirror */ + memory_region_set_enabled(&s->row_mirror_alias, false); + trace_allwinner_h3_dramc_rowmirror_disable(); + + } else if (row_bits_actual) { + /* Row bits not matching ram_size, install the rows mirror */ + hwaddr row_mirror = s->ram_addr + ((1ULL << (row_bits_actual + + bank_bits)) * page_size); + + memory_region_set_enabled(&s->row_mirror_alias, true); + memory_region_set_address(&s->row_mirror_alias, row_mirror); + + trace_allwinner_h3_dramc_rowmirror_enable(row_mirror); + } +} + +static uint64_t allwinner_h3_dramcom_read(void *opaque, hwaddr offset, + unsigned size) +{ + const AwH3DramCtlState *s = AW_H3_DRAMC(opaque); + const uint32_t idx = REG_INDEX(offset); + + if (idx >= AW_H3_DRAMCOM_REGS_NUM) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n", + __func__, (uint32_t)offset); + return 0; + } + + trace_allwinner_h3_dramcom_read(offset, s->dramcom[idx], size); + + return s->dramcom[idx]; +} + +static void allwinner_h3_dramcom_write(void *opaque, hwaddr offset, + uint64_t val, unsigned size) +{ + AwH3DramCtlState *s = AW_H3_DRAMC(opaque); + const uint32_t idx = REG_INDEX(offset); + + trace_allwinner_h3_dramcom_write(offset, val, size); + + if (idx >= AW_H3_DRAMCOM_REGS_NUM) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n", + __func__, (uint32_t)offset); + return; + } + + switch (offset) { + case REG_DRAMCOM_CR: /* Control Register */ + allwinner_h3_dramc_map_rows(s, ((val >> 4) & 0xf) + 1, + ((val >> 2) & 0x1) + 2, + 1 << (((val >> 8) & 0xf) + 3)); + break; + default: + break; + }; + + s->dramcom[idx] = (uint32_t) val; +} + +static uint64_t allwinner_h3_dramctl_read(void *opaque, hwaddr offset, + unsigned size) +{ + const AwH3DramCtlState *s = AW_H3_DRAMC(opaque); + const uint32_t idx = REG_INDEX(offset); + + if (idx >= AW_H3_DRAMCTL_REGS_NUM) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n", + __func__, (uint32_t)offset); + return 0; + } + + trace_allwinner_h3_dramctl_read(offset, s->dramctl[idx], size); + + return s->dramctl[idx]; +} + +static void allwinner_h3_dramctl_write(void *opaque, hwaddr offset, + uint64_t val, unsigned size) +{ + AwH3DramCtlState *s = AW_H3_DRAMC(opaque); + const uint32_t idx = REG_INDEX(offset); + + trace_allwinner_h3_dramctl_write(offset, val, size); + + if (idx >= AW_H3_DRAMCTL_REGS_NUM) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n", + __func__, (uint32_t)offset); + return; + } + + switch (offset) { + case REG_DRAMCTL_PIR: /* PHY Initialization Register */ + s->dramctl[REG_INDEX(REG_DRAMCTL_PGSR)] |= REG_DRAMCTL_PGSR_INITDONE; + s->dramctl[REG_INDEX(REG_DRAMCTL_STATR)] |= REG_DRAMCTL_STATR_ACTIVE; + break; + default: + break; + } + + s->dramctl[idx] = (uint32_t) val; +} + +static uint64_t allwinner_h3_dramphy_read(void *opaque, hwaddr offset, + unsigned size) +{ + const AwH3DramCtlState *s = AW_H3_DRAMC(opaque); + const uint32_t idx = REG_INDEX(offset); + + if (idx >= AW_H3_DRAMPHY_REGS_NUM) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n", + __func__, (uint32_t)offset); + return 0; + } + + trace_allwinner_h3_dramphy_read(offset, s->dramphy[idx], size); + + return s->dramphy[idx]; +} + +static void allwinner_h3_dramphy_write(void *opaque, hwaddr offset, + uint64_t val, unsigned size) +{ + AwH3DramCtlState *s = AW_H3_DRAMC(opaque); + const uint32_t idx = REG_INDEX(offset); + + trace_allwinner_h3_dramphy_write(offset, val, size); + + if (idx >= AW_H3_DRAMPHY_REGS_NUM) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n", + __func__, (uint32_t)offset); + return; + } + + s->dramphy[idx] = (uint32_t) val; +} + +static const MemoryRegionOps allwinner_h3_dramcom_ops = { + .read = allwinner_h3_dramcom_read, + .write = allwinner_h3_dramcom_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, + .impl.min_access_size = 4, +}; + +static const MemoryRegionOps allwinner_h3_dramctl_ops = { + .read = allwinner_h3_dramctl_read, + .write = allwinner_h3_dramctl_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, + .impl.min_access_size = 4, +}; + +static const MemoryRegionOps allwinner_h3_dramphy_ops = { + .read = allwinner_h3_dramphy_read, + .write = allwinner_h3_dramphy_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, + .impl.min_access_size = 4, +}; + +static void allwinner_h3_dramc_reset(DeviceState *dev) +{ + AwH3DramCtlState *s = AW_H3_DRAMC(dev); + + /* Set default values for registers */ + memset(&s->dramcom, 0, sizeof(s->dramcom)); + memset(&s->dramctl, 0, sizeof(s->dramctl)); + memset(&s->dramphy, 0, sizeof(s->dramphy)); +} + +static void allwinner_h3_dramc_realize(DeviceState *dev, Error **errp) +{ + AwH3DramCtlState *s = AW_H3_DRAMC(dev); + + /* Only power of 2 RAM sizes from 256MiB up to 2048MiB are supported */ + for (uint8_t i = 8; i < 13; i++) { + if (1 << i == s->ram_size) { + break; + } else if (i == 12) { + error_report("%s: ram-size %u MiB is not supported", + __func__, s->ram_size); + exit(1); + } + } + + /* Setup row mirror mappings */ + memory_region_init_ram(&s->row_mirror, OBJECT(s), + "allwinner-h3-dramc.row-mirror", + 4 * KiB, &error_abort); + memory_region_add_subregion_overlap(get_system_memory(), s->ram_addr, + &s->row_mirror, 10); + + memory_region_init_alias(&s->row_mirror_alias, OBJECT(s), + "allwinner-h3-dramc.row-mirror-alias", + &s->row_mirror, 0, 4 * KiB); + memory_region_add_subregion_overlap(get_system_memory(), + s->ram_addr + 1 * MiB, + &s->row_mirror_alias, 10); + memory_region_set_enabled(&s->row_mirror_alias, false); +} + +static void allwinner_h3_dramc_init(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + AwH3DramCtlState *s = AW_H3_DRAMC(obj); + + /* DRAMCOM registers */ + memory_region_init_io(&s->dramcom_iomem, OBJECT(s), + &allwinner_h3_dramcom_ops, s, + TYPE_AW_H3_DRAMC, 4 * KiB); + sysbus_init_mmio(sbd, &s->dramcom_iomem); + + /* DRAMCTL registers */ + memory_region_init_io(&s->dramctl_iomem, OBJECT(s), + &allwinner_h3_dramctl_ops, s, + TYPE_AW_H3_DRAMC, 4 * KiB); + sysbus_init_mmio(sbd, &s->dramctl_iomem); + + /* DRAMPHY registers */ + memory_region_init_io(&s->dramphy_iomem, OBJECT(s), + &allwinner_h3_dramphy_ops, s, + TYPE_AW_H3_DRAMC, 4 * KiB); + sysbus_init_mmio(sbd, &s->dramphy_iomem); +} + +static Property allwinner_h3_dramc_properties[] = { + DEFINE_PROP_UINT64("ram-addr", AwH3DramCtlState, ram_addr, 0x0), + DEFINE_PROP_UINT32("ram-size", AwH3DramCtlState, ram_size, 256 * MiB), + DEFINE_PROP_END_OF_LIST() +}; + +static const VMStateDescription allwinner_h3_dramc_vmstate = { + .name = "allwinner-h3-dramc", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(dramcom, AwH3DramCtlState, AW_H3_DRAMCOM_REGS_NUM), + VMSTATE_UINT32_ARRAY(dramctl, AwH3DramCtlState, AW_H3_DRAMCTL_REGS_NUM), + VMSTATE_UINT32_ARRAY(dramphy, AwH3DramCtlState, AW_H3_DRAMPHY_REGS_NUM), + VMSTATE_END_OF_LIST() + } +}; + +static void allwinner_h3_dramc_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = allwinner_h3_dramc_reset; + dc->vmsd = &allwinner_h3_dramc_vmstate; + dc->realize = allwinner_h3_dramc_realize; + device_class_set_props(dc, allwinner_h3_dramc_properties); +} + +static const TypeInfo allwinner_h3_dramc_info = { + .name = TYPE_AW_H3_DRAMC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_init = allwinner_h3_dramc_init, + .instance_size = sizeof(AwH3DramCtlState), + .class_init = allwinner_h3_dramc_class_init, +}; + +static void allwinner_h3_dramc_register(void) +{ + type_register_static(&allwinner_h3_dramc_info); +} + +type_init(allwinner_h3_dramc_register) diff --git a/hw/misc/allwinner-h3-sysctrl.c b/hw/misc/allwinner-h3-sysctrl.c new file mode 100644 index 0000000000..1d07efa880 --- /dev/null +++ b/hw/misc/allwinner-h3-sysctrl.c @@ -0,0 +1,140 @@ +/* + * Allwinner H3 System Control emulation + * + * Copyright (C) 2019 Niek Linnenbank + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "hw/sysbus.h" +#include "migration/vmstate.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "hw/misc/allwinner-h3-sysctrl.h" + +/* System Control register offsets */ +enum { + REG_VER = 0x24, /* Version */ + REG_EMAC_PHY_CLK = 0x30, /* EMAC PHY Clock */ +}; + +#define REG_INDEX(offset) (offset / sizeof(uint32_t)) + +/* System Control register reset values */ +enum { + REG_VER_RST = 0x0, + REG_EMAC_PHY_CLK_RST = 0x58000, +}; + +static uint64_t allwinner_h3_sysctrl_read(void *opaque, hwaddr offset, + unsigned size) +{ + const AwH3SysCtrlState *s = AW_H3_SYSCTRL(opaque); + const uint32_t idx = REG_INDEX(offset); + + if (idx >= AW_H3_SYSCTRL_REGS_NUM) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n", + __func__, (uint32_t)offset); + return 0; + } + + return s->regs[idx]; +} + +static void allwinner_h3_sysctrl_write(void *opaque, hwaddr offset, + uint64_t val, unsigned size) +{ + AwH3SysCtrlState *s = AW_H3_SYSCTRL(opaque); + const uint32_t idx = REG_INDEX(offset); + + if (idx >= AW_H3_SYSCTRL_REGS_NUM) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n", + __func__, (uint32_t)offset); + return; + } + + switch (offset) { + case REG_VER: /* Version */ + break; + default: + s->regs[idx] = (uint32_t) val; + break; + } +} + +static const MemoryRegionOps allwinner_h3_sysctrl_ops = { + .read = allwinner_h3_sysctrl_read, + .write = allwinner_h3_sysctrl_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, + .impl.min_access_size = 4, +}; + +static void allwinner_h3_sysctrl_reset(DeviceState *dev) +{ + AwH3SysCtrlState *s = AW_H3_SYSCTRL(dev); + + /* Set default values for registers */ + s->regs[REG_INDEX(REG_VER)] = REG_VER_RST; + s->regs[REG_INDEX(REG_EMAC_PHY_CLK)] = REG_EMAC_PHY_CLK_RST; +} + +static void allwinner_h3_sysctrl_init(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + AwH3SysCtrlState *s = AW_H3_SYSCTRL(obj); + + /* Memory mapping */ + memory_region_init_io(&s->iomem, OBJECT(s), &allwinner_h3_sysctrl_ops, s, + TYPE_AW_H3_SYSCTRL, 4 * KiB); + sysbus_init_mmio(sbd, &s->iomem); +} + +static const VMStateDescription allwinner_h3_sysctrl_vmstate = { + .name = "allwinner-h3-sysctrl", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, AwH3SysCtrlState, AW_H3_SYSCTRL_REGS_NUM), + VMSTATE_END_OF_LIST() + } +}; + +static void allwinner_h3_sysctrl_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = allwinner_h3_sysctrl_reset; + dc->vmsd = &allwinner_h3_sysctrl_vmstate; +} + +static const TypeInfo allwinner_h3_sysctrl_info = { + .name = TYPE_AW_H3_SYSCTRL, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_init = allwinner_h3_sysctrl_init, + .instance_size = sizeof(AwH3SysCtrlState), + .class_init = allwinner_h3_sysctrl_class_init, +}; + +static void allwinner_h3_sysctrl_register(void) +{ + type_register_static(&allwinner_h3_sysctrl_info); +} + +type_init(allwinner_h3_sysctrl_register) diff --git a/hw/misc/allwinner-sid.c b/hw/misc/allwinner-sid.c new file mode 100644 index 0000000000..196380c33a --- /dev/null +++ b/hw/misc/allwinner-sid.c @@ -0,0 +1,168 @@ +/* + * Allwinner Security ID emulation + * + * Copyright (C) 2019 Niek Linnenbank + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "hw/sysbus.h" +#include "migration/vmstate.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "qemu/guest-random.h" +#include "qapi/error.h" +#include "hw/qdev-properties.h" +#include "hw/misc/allwinner-sid.h" +#include "trace.h" + +/* SID register offsets */ +enum { + REG_PRCTL = 0x40, /* Control */ + REG_RDKEY = 0x60, /* Read Key */ +}; + +/* SID register flags */ +enum { + REG_PRCTL_WRITE = 0x0002, /* Unknown write flag */ + REG_PRCTL_OP_LOCK = 0xAC00, /* Lock operation */ +}; + +static uint64_t allwinner_sid_read(void *opaque, hwaddr offset, + unsigned size) +{ + const AwSidState *s = AW_SID(opaque); + uint64_t val = 0; + + switch (offset) { + case REG_PRCTL: /* Control */ + val = s->control; + break; + case REG_RDKEY: /* Read Key */ + val = s->rdkey; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n", + __func__, (uint32_t)offset); + return 0; + } + + trace_allwinner_sid_read(offset, val, size); + + return val; +} + +static void allwinner_sid_write(void *opaque, hwaddr offset, + uint64_t val, unsigned size) +{ + AwSidState *s = AW_SID(opaque); + + trace_allwinner_sid_write(offset, val, size); + + switch (offset) { + case REG_PRCTL: /* Control */ + s->control = val; + + if ((s->control & REG_PRCTL_OP_LOCK) && + (s->control & REG_PRCTL_WRITE)) { + uint32_t id = s->control >> 16; + + if (id <= sizeof(QemuUUID) - sizeof(s->rdkey)) { + s->rdkey = ldl_be_p(&s->identifier.data[id]); + } + } + s->control &= ~REG_PRCTL_WRITE; + break; + case REG_RDKEY: /* Read Key */ + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n", + __func__, (uint32_t)offset); + break; + } +} + +static const MemoryRegionOps allwinner_sid_ops = { + .read = allwinner_sid_read, + .write = allwinner_sid_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, + .impl.min_access_size = 4, +}; + +static void allwinner_sid_reset(DeviceState *dev) +{ + AwSidState *s = AW_SID(dev); + + /* Set default values for registers */ + s->control = 0; + s->rdkey = 0; +} + +static void allwinner_sid_init(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + AwSidState *s = AW_SID(obj); + + /* Memory mapping */ + memory_region_init_io(&s->iomem, OBJECT(s), &allwinner_sid_ops, s, + TYPE_AW_SID, 1 * KiB); + sysbus_init_mmio(sbd, &s->iomem); +} + +static Property allwinner_sid_properties[] = { + DEFINE_PROP_UUID_NODEFAULT("identifier", AwSidState, identifier), + DEFINE_PROP_END_OF_LIST() +}; + +static const VMStateDescription allwinner_sid_vmstate = { + .name = "allwinner-sid", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(control, AwSidState), + VMSTATE_UINT32(rdkey, AwSidState), + VMSTATE_UINT8_ARRAY_V(identifier.data, AwSidState, sizeof(QemuUUID), 1), + VMSTATE_END_OF_LIST() + } +}; + +static void allwinner_sid_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = allwinner_sid_reset; + dc->vmsd = &allwinner_sid_vmstate; + device_class_set_props(dc, allwinner_sid_properties); +} + +static const TypeInfo allwinner_sid_info = { + .name = TYPE_AW_SID, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_init = allwinner_sid_init, + .instance_size = sizeof(AwSidState), + .class_init = allwinner_sid_class_init, +}; + +static void allwinner_sid_register(void) +{ + type_register_static(&allwinner_sid_info); +} + +type_init(allwinner_sid_register) diff --git a/hw/misc/applesmc.c b/hw/misc/applesmc.c index 24d57e8677..1c4addb201 100644 --- a/hw/misc/applesmc.c +++ b/hw/misc/applesmc.c @@ -353,7 +353,7 @@ static void qdev_applesmc_class_init(ObjectClass *klass, void *data) dc->realize = applesmc_isa_realize; dc->reset = qdev_applesmc_isa_reset; - dc->props = applesmc_isa_properties; + device_class_set_props(dc, applesmc_isa_properties); set_bit(DEVICE_CATEGORY_MISC, dc->categories); } diff --git a/hw/misc/arm11scu.c b/hw/misc/arm11scu.c index 3023284d6f..17c36a0545 100644 --- a/hw/misc/arm11scu.c +++ b/hw/misc/arm11scu.c @@ -85,7 +85,7 @@ static void arm11_scu_class_init(ObjectClass *oc, void *data) DeviceClass *dc = DEVICE_CLASS(oc); dc->realize = arm11_scu_realize; - dc->props = arm11_scu_properties; + device_class_set_props(dc, arm11_scu_properties); } static const TypeInfo arm11_scu_type_info = { diff --git a/hw/misc/arm_l2x0.c b/hw/misc/arm_l2x0.c index af2c7af4a0..2066c97f5f 100644 --- a/hw/misc/arm_l2x0.c +++ b/hw/misc/arm_l2x0.c @@ -182,7 +182,7 @@ static void l2x0_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->vmsd = &vmstate_l2x0; - dc->props = l2x0_properties; + device_class_set_props(dc, l2x0_properties); dc->reset = l2x0_priv_reset; } diff --git a/hw/misc/arm_sysctl.c b/hw/misc/arm_sysctl.c index fc79522ece..a474bbdd19 100644 --- a/hw/misc/arm_sysctl.c +++ b/hw/misc/arm_sysctl.c @@ -642,7 +642,7 @@ static void arm_sysctl_class_init(ObjectClass *klass, void *data) dc->realize = arm_sysctl_realize; dc->reset = arm_sysctl_reset; dc->vmsd = &vmstate_arm_sysctl; - dc->props = arm_sysctl_properties; + device_class_set_props(dc, arm_sysctl_properties); } static const TypeInfo arm_sysctl_info = { diff --git a/hw/misc/armsse-cpuid.c b/hw/misc/armsse-cpuid.c index 8ab15fea81..d58138dc28 100644 --- a/hw/misc/armsse-cpuid.c +++ b/hw/misc/armsse-cpuid.c @@ -116,7 +116,7 @@ static void armsse_cpuid_class_init(ObjectClass *klass, void *data) * does not need a reset function or VMState. */ - dc->props = armsse_cpuid_props; + device_class_set_props(dc, armsse_cpuid_props); } static const TypeInfo armsse_cpuid_info = { diff --git a/hw/misc/aspeed_scu.c b/hw/misc/aspeed_scu.c index 717509bc54..9d7482a9df 100644 --- a/hw/misc/aspeed_scu.c +++ b/hw/misc/aspeed_scu.c @@ -77,6 +77,8 @@ #define CPU2_BASE_SEG4 TO_REG(0x110) #define CPU2_BASE_SEG5 TO_REG(0x114) #define CPU2_CACHE_CTRL TO_REG(0x118) +#define CHIP_ID0 TO_REG(0x150) +#define CHIP_ID1 TO_REG(0x154) #define UART_HPLL_CLK TO_REG(0x160) #define PCIE_CTRL TO_REG(0x180) #define BMC_MMIO_CTRL TO_REG(0x184) @@ -98,7 +100,7 @@ #define AST2600_CLK_STOP_CTRL TO_REG(0x80) #define AST2600_CLK_STOP_CTRL_CLR TO_REG(0x84) #define AST2600_CLK_STOP_CTRL2 TO_REG(0x90) -#define AST2600_CLK_STOP_CTR2L_CLR TO_REG(0x94) +#define AST2600_CLK_STOP_CTRL2_CLR TO_REG(0x94) #define AST2600_SDRAM_HANDSHAKE TO_REG(0x100) #define AST2600_HPLL_PARAM TO_REG(0x200) #define AST2600_HPLL_EXT TO_REG(0x204) @@ -115,6 +117,8 @@ #define AST2600_HW_STRAP2_PROT TO_REG(0x518) #define AST2600_RNG_CTRL TO_REG(0x524) #define AST2600_RNG_DATA TO_REG(0x540) +#define AST2600_CHIP_ID0 TO_REG(0x5B0) +#define AST2600_CHIP_ID1 TO_REG(0x5B4) #define AST2600_CLK TO_REG(0x40) @@ -182,6 +186,8 @@ static const uint32_t ast2500_a1_resets[ASPEED_SCU_NR_REGS] = { [CPU2_BASE_SEG1] = 0x80000000U, [CPU2_BASE_SEG4] = 0x1E600000U, [CPU2_BASE_SEG5] = 0xC0000000U, + [CHIP_ID0] = 0x1234ABCDU, + [CHIP_ID1] = 0x88884444U, [UART_HPLL_CLK] = 0x00001903U, [PCIE_CTRL] = 0x0000007BU, [BMC_DEV_ID] = 0x00002402U @@ -232,8 +238,47 @@ static uint64_t aspeed_scu_read(void *opaque, hwaddr offset, unsigned size) return s->regs[reg]; } -static void aspeed_scu_write(void *opaque, hwaddr offset, uint64_t data, - unsigned size) +static void aspeed_ast2400_scu_write(void *opaque, hwaddr offset, + uint64_t data, unsigned size) +{ + AspeedSCUState *s = ASPEED_SCU(opaque); + int reg = TO_REG(offset); + + if (reg >= ASPEED_SCU_NR_REGS) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Out-of-bounds write at offset 0x%" HWADDR_PRIx "\n", + __func__, offset); + return; + } + + if (reg > PROT_KEY && reg < CPU2_BASE_SEG1 && + !s->regs[PROT_KEY]) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: SCU is locked!\n", __func__); + } + + trace_aspeed_scu_write(offset, size, data); + + switch (reg) { + case PROT_KEY: + s->regs[reg] = (data == ASPEED_SCU_PROT_KEY) ? 1 : 0; + return; + case SILICON_REV: + case FREQ_CNTR_EVAL: + case VGA_SCRATCH1 ... VGA_SCRATCH8: + case RNG_DATA: + case FREE_CNTR4: + case FREE_CNTR4_EXT: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Write to read-only offset 0x%" HWADDR_PRIx "\n", + __func__, offset); + return; + } + + s->regs[reg] = data; +} + +static void aspeed_ast2500_scu_write(void *opaque, hwaddr offset, + uint64_t data, unsigned size) { AspeedSCUState *s = ASPEED_SCU(opaque); int reg = TO_REG(offset); @@ -257,31 +302,19 @@ static void aspeed_scu_write(void *opaque, hwaddr offset, uint64_t data, case PROT_KEY: s->regs[reg] = (data == ASPEED_SCU_PROT_KEY) ? 1 : 0; return; - case CLK_SEL: - s->regs[reg] = data; - break; case HW_STRAP1: - if (ASPEED_IS_AST2500(s->regs[SILICON_REV])) { - s->regs[HW_STRAP1] |= data; - return; - } - /* Jump to assignment below */ - break; + s->regs[HW_STRAP1] |= data; + return; case SILICON_REV: - if (ASPEED_IS_AST2500(s->regs[SILICON_REV])) { - s->regs[HW_STRAP1] &= ~data; - } else { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: Write to read-only offset 0x%" HWADDR_PRIx "\n", - __func__, offset); - } - /* Avoid assignment below, we've handled everything */ + s->regs[HW_STRAP1] &= ~data; return; case FREQ_CNTR_EVAL: case VGA_SCRATCH1 ... VGA_SCRATCH8: case RNG_DATA: case FREE_CNTR4: case FREE_CNTR4_EXT: + case CHIP_ID0: + case CHIP_ID1: qemu_log_mask(LOG_GUEST_ERROR, "%s: Write to read-only offset 0x%" HWADDR_PRIx "\n", __func__, offset); @@ -291,9 +324,18 @@ static void aspeed_scu_write(void *opaque, hwaddr offset, uint64_t data, s->regs[reg] = data; } -static const MemoryRegionOps aspeed_scu_ops = { +static const MemoryRegionOps aspeed_ast2400_scu_ops = { .read = aspeed_scu_read, - .write = aspeed_scu_write, + .write = aspeed_ast2400_scu_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid.min_access_size = 4, + .valid.max_access_size = 4, + .valid.unaligned = false, +}; + +static const MemoryRegionOps aspeed_ast2500_scu_ops = { + .read = aspeed_scu_read, + .write = aspeed_ast2500_scu_write, .endianness = DEVICE_LITTLE_ENDIAN, .valid.min_access_size = 4, .valid.max_access_size = 4, @@ -447,7 +489,7 @@ static void aspeed_scu_class_init(ObjectClass *klass, void *data) dc->reset = aspeed_scu_reset; dc->desc = "ASPEED System Control Unit"; dc->vmsd = &vmstate_aspeed_scu; - dc->props = aspeed_scu_properties; + device_class_set_props(dc, aspeed_scu_properties); } static const TypeInfo aspeed_scu_info = { @@ -469,7 +511,7 @@ static void aspeed_2400_scu_class_init(ObjectClass *klass, void *data) asc->calc_hpll = aspeed_2400_scu_calc_hpll; asc->apb_divider = 2; asc->nr_regs = ASPEED_SCU_NR_REGS; - asc->ops = &aspeed_scu_ops; + asc->ops = &aspeed_ast2400_scu_ops; } static const TypeInfo aspeed_2400_scu_info = { @@ -489,7 +531,7 @@ static void aspeed_2500_scu_class_init(ObjectClass *klass, void *data) asc->calc_hpll = aspeed_2500_scu_calc_hpll; asc->apb_divider = 4; asc->nr_regs = ASPEED_SCU_NR_REGS; - asc->ops = &aspeed_scu_ops; + asc->ops = &aspeed_ast2500_scu_ops; } static const TypeInfo aspeed_2500_scu_info = { @@ -532,11 +574,13 @@ static uint64_t aspeed_ast2600_scu_read(void *opaque, hwaddr offset, return s->regs[reg]; } -static void aspeed_ast2600_scu_write(void *opaque, hwaddr offset, uint64_t data, - unsigned size) +static void aspeed_ast2600_scu_write(void *opaque, hwaddr offset, + uint64_t data64, unsigned size) { AspeedSCUState *s = ASPEED_SCU(opaque); int reg = TO_REG(offset); + /* Truncate here so bitwise operations below behave as expected */ + uint32_t data = data64; if (reg >= ASPEED_AST2600_SCU_NR_REGS) { qemu_log_mask(LOG_GUEST_ERROR, @@ -563,20 +607,29 @@ static void aspeed_ast2600_scu_write(void *opaque, hwaddr offset, uint64_t data, /* fall through */ case AST2600_SYS_RST_CTRL: case AST2600_SYS_RST_CTRL2: + case AST2600_CLK_STOP_CTRL: + case AST2600_CLK_STOP_CTRL2: /* W1S (Write 1 to set) registers */ s->regs[reg] |= data; return; case AST2600_SYS_RST_CTRL_CLR: case AST2600_SYS_RST_CTRL2_CLR: + case AST2600_CLK_STOP_CTRL_CLR: + case AST2600_CLK_STOP_CTRL2_CLR: case AST2600_HW_STRAP1_CLR: case AST2600_HW_STRAP2_CLR: - /* W1C (Write 1 to clear) registers */ - s->regs[reg] &= ~data; + /* + * W1C (Write 1 to clear) registers are offset by one address from + * the data register + */ + s->regs[reg - 1] &= ~data; return; case AST2600_RNG_DATA: case AST2600_SILICON_REV: case AST2600_SILICON_REV2: + case AST2600_CHIP_ID0: + case AST2600_CHIP_ID1: /* Add read only registers here */ qemu_log_mask(LOG_GUEST_ERROR, "%s: Write to read-only offset 0x%" HWADDR_PRIx "\n", @@ -605,6 +658,9 @@ static const uint32_t ast2600_a0_resets[ASPEED_AST2600_SCU_NR_REGS] = { [AST2600_CLK_STOP_CTRL2] = 0xFFF0FFF0, [AST2600_SDRAM_HANDSHAKE] = 0x00000040, /* SoC completed DRAM init */ [AST2600_HPLL_PARAM] = 0x1000405F, + [AST2600_CHIP_ID0] = 0x1234ABCD, + [AST2600_CHIP_ID1] = 0x88884444, + }; static void aspeed_ast2600_scu_reset(DeviceState *dev) diff --git a/hw/misc/aspeed_sdmc.c b/hw/misc/aspeed_sdmc.c index f3a63a2e01..7b466bf19a 100644 --- a/hw/misc/aspeed_sdmc.c +++ b/hw/misc/aspeed_sdmc.c @@ -17,6 +17,9 @@ #include "migration/vmstate.h" #include "qapi/error.h" #include "trace.h" +#include "qemu/units.h" +#include "qemu/cutils.h" +#include "qapi/visitor.h" /* Protection Key Register */ #define R_PROT (0x00 / 4) @@ -160,14 +163,9 @@ static int ast2400_rambits(AspeedSDMCState *s) case 512: return ASPEED_SDMC_DRAM_512MB; default: + g_assert_not_reached(); break; } - - /* use a common default */ - warn_report("Invalid RAM size 0x%" PRIx64 ". Using default 256M", - s->ram_size); - s->ram_size = 256 << 20; - return ASPEED_SDMC_DRAM_256MB; } static int ast2500_rambits(AspeedSDMCState *s) @@ -182,14 +180,9 @@ static int ast2500_rambits(AspeedSDMCState *s) case 1024: return ASPEED_SDMC_AST2500_1024MB; default: + g_assert_not_reached(); break; } - - /* use a common default */ - warn_report("Invalid RAM size 0x%" PRIx64 ". Using default 512M", - s->ram_size); - s->ram_size = 512 << 20; - return ASPEED_SDMC_AST2500_512MB; } static int ast2600_rambits(AspeedSDMCState *s) @@ -204,14 +197,9 @@ static int ast2600_rambits(AspeedSDMCState *s) case 2048: return ASPEED_SDMC_AST2600_2048MB; default: + g_assert_not_reached(); break; } - - /* use a common default */ - warn_report("Invalid RAM size 0x%" PRIx64 ". Using default 512M", - s->ram_size); - s->ram_size = 512 << 20; - return ASPEED_SDMC_AST2600_512MB; } static void aspeed_sdmc_reset(DeviceState *dev) @@ -225,6 +213,51 @@ static void aspeed_sdmc_reset(DeviceState *dev) s->regs[R_CONF] = asc->compute_conf(s, 0); } +static void aspeed_sdmc_get_ram_size(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + AspeedSDMCState *s = ASPEED_SDMC(obj); + int64_t value = s->ram_size; + + visit_type_int(v, name, &value, errp); +} + +static void aspeed_sdmc_set_ram_size(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + int i; + char *sz; + int64_t value; + Error *local_err = NULL; + AspeedSDMCState *s = ASPEED_SDMC(obj); + AspeedSDMCClass *asc = ASPEED_SDMC_GET_CLASS(s); + + visit_type_int(v, name, &value, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + for (i = 0; asc->valid_ram_sizes[i]; i++) { + if (value == asc->valid_ram_sizes[i]) { + s->ram_size = value; + return; + } + } + + sz = size_to_str(value); + error_setg(&local_err, "Invalid RAM size %s", sz); + g_free(sz); + error_propagate(errp, local_err); +} + +static void aspeed_sdmc_initfn(Object *obj) +{ + object_property_add(obj, "ram-size", "int", + aspeed_sdmc_get_ram_size, aspeed_sdmc_set_ram_size, + NULL, NULL, NULL); +} + static void aspeed_sdmc_realize(DeviceState *dev, Error **errp) { SysBusDevice *sbd = SYS_BUS_DEVICE(dev); @@ -249,7 +282,6 @@ static const VMStateDescription vmstate_aspeed_sdmc = { }; static Property aspeed_sdmc_properties[] = { - DEFINE_PROP_UINT64("ram-size", AspeedSDMCState, ram_size, 0), DEFINE_PROP_UINT64("max-ram-size", AspeedSDMCState, max_ram_size, 0), DEFINE_PROP_END_OF_LIST(), }; @@ -261,13 +293,14 @@ static void aspeed_sdmc_class_init(ObjectClass *klass, void *data) dc->reset = aspeed_sdmc_reset; dc->desc = "ASPEED SDRAM Memory Controller"; dc->vmsd = &vmstate_aspeed_sdmc; - dc->props = aspeed_sdmc_properties; + device_class_set_props(dc, aspeed_sdmc_properties); } static const TypeInfo aspeed_sdmc_info = { .name = TYPE_ASPEED_SDMC, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(AspeedSDMCState), + .instance_init = aspeed_sdmc_initfn, .class_init = aspeed_sdmc_class_init, .class_size = sizeof(AspeedSDMCClass), .abstract = true, @@ -298,6 +331,9 @@ static void aspeed_2400_sdmc_write(AspeedSDMCState *s, uint32_t reg, s->regs[reg] = data; } +static const uint64_t +aspeed_2400_ram_sizes[] = { 64 * MiB, 128 * MiB, 256 * MiB, 512 * MiB, 0}; + static void aspeed_2400_sdmc_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -307,6 +343,7 @@ static void aspeed_2400_sdmc_class_init(ObjectClass *klass, void *data) asc->max_ram_size = 512 << 20; asc->compute_conf = aspeed_2400_sdmc_compute_conf; asc->write = aspeed_2400_sdmc_write; + asc->valid_ram_sizes = aspeed_2400_ram_sizes; } static const TypeInfo aspeed_2400_sdmc_info = { @@ -351,6 +388,9 @@ static void aspeed_2500_sdmc_write(AspeedSDMCState *s, uint32_t reg, s->regs[reg] = data; } +static const uint64_t +aspeed_2500_ram_sizes[] = { 128 * MiB, 256 * MiB, 512 * MiB, 1024 * MiB, 0}; + static void aspeed_2500_sdmc_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -360,6 +400,7 @@ static void aspeed_2500_sdmc_class_init(ObjectClass *klass, void *data) asc->max_ram_size = 1024 << 20; asc->compute_conf = aspeed_2500_sdmc_compute_conf; asc->write = aspeed_2500_sdmc_write; + asc->valid_ram_sizes = aspeed_2500_ram_sizes; } static const TypeInfo aspeed_2500_sdmc_info = { @@ -404,6 +445,9 @@ static void aspeed_2600_sdmc_write(AspeedSDMCState *s, uint32_t reg, s->regs[reg] = data; } +static const uint64_t +aspeed_2600_ram_sizes[] = { 256 * MiB, 512 * MiB, 1024 * MiB, 2048 * MiB, 0}; + static void aspeed_2600_sdmc_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -413,6 +457,7 @@ static void aspeed_2600_sdmc_class_init(ObjectClass *klass, void *data) asc->max_ram_size = 2048 << 20; asc->compute_conf = aspeed_2600_sdmc_compute_conf; asc->write = aspeed_2600_sdmc_write; + asc->valid_ram_sizes = aspeed_2600_ram_sizes; } static const TypeInfo aspeed_2600_sdmc_info = { diff --git a/hw/misc/bcm2835_mbox.c b/hw/misc/bcm2835_mbox.c index 77d2d80706..2afa06a746 100644 --- a/hw/misc/bcm2835_mbox.c +++ b/hw/misc/bcm2835_mbox.c @@ -1,11 +1,13 @@ /* * Raspberry Pi emulation (c) 2012 Gregory Estrade - * This code is licensed under the GNU GPLv2 and later. * * This file models the system mailboxes, which are used for * communication with low-bandwidth GPU peripherals. Refs: * https://github.com/raspberrypi/firmware/wiki/Mailboxes * https://github.com/raspberrypi/firmware/wiki/Accessing-mailboxes + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. */ #include "qemu/osdep.h" diff --git a/hw/misc/bcm2835_property.c b/hw/misc/bcm2835_property.c index 0eea2e20f7..3e228ca0ae 100644 --- a/hw/misc/bcm2835_property.c +++ b/hw/misc/bcm2835_property.c @@ -1,6 +1,8 @@ /* * Raspberry Pi emulation (c) 2012 Gregory Estrade - * This code is licensed under the GNU GPLv2 and later. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. */ #include "qemu/osdep.h" @@ -426,7 +428,7 @@ static void bcm2835_property_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->props = bcm2835_property_props; + device_class_set_props(dc, bcm2835_property_props); dc->realize = bcm2835_property_realize; dc->vmsd = &vmstate_bcm2835_property; } diff --git a/hw/misc/debugexit.c b/hw/misc/debugexit.c index ccf02bdbd4..99a814f10c 100644 --- a/hw/misc/debugexit.c +++ b/hw/misc/debugexit.c @@ -65,7 +65,7 @@ static void debug_exit_class_initfn(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = debug_exit_realizefn; - dc->props = debug_exit_properties; + device_class_set_props(dc, debug_exit_properties); set_bit(DEVICE_CATEGORY_MISC, dc->categories); } diff --git a/hw/misc/eccmemctl.c b/hw/misc/eccmemctl.c index 4b1f2b675a..aec447368e 100644 --- a/hw/misc/eccmemctl.c +++ b/hw/misc/eccmemctl.c @@ -336,7 +336,7 @@ static void ecc_class_init(ObjectClass *klass, void *data) dc->realize = ecc_realize; dc->reset = ecc_reset; dc->vmsd = &vmstate_ecc; - dc->props = ecc_properties; + device_class_set_props(dc, ecc_properties); } static const TypeInfo ecc_info = { diff --git a/hw/misc/edu.c b/hw/misc/edu.c index d5e2bdbb57..ff10f5b794 100644 --- a/hw/misc/edu.c +++ b/hw/misc/edu.c @@ -396,21 +396,14 @@ static void pci_edu_uninit(PCIDevice *pdev) msi_uninit(pdev); } -static void edu_obj_uint64(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - uint64_t *val = opaque; - - visit_type_uint64(v, name, val, errp); -} - static void edu_instance_init(Object *obj) { EduState *edu = EDU(obj); edu->dma_mask = (1UL << 28) - 1; - object_property_add(obj, "dma_mask", "uint64", edu_obj_uint64, - edu_obj_uint64, NULL, &edu->dma_mask, NULL); + object_property_add_uint64_ptr(obj, "dma_mask", + &edu->dma_mask, OBJ_PROP_FLAG_READWRITE, + NULL); } static void edu_class_init(ObjectClass *class, void *data) diff --git a/hw/misc/imx2_wdt.c b/hw/misc/imx2_wdt.c index 5576778a32..2aedfe803a 100644 --- a/hw/misc/imx2_wdt.c +++ b/hw/misc/imx2_wdt.c @@ -29,7 +29,7 @@ static void imx2_wdt_write(void *opaque, hwaddr addr, uint64_t value, unsigned int size) { if (addr == IMX2_WDT_WCR && - (value & (IMX2_WDT_WCR_WDA | IMX2_WDT_WCR_SRS))) { + (~value & (IMX2_WDT_WCR_WDA | IMX2_WDT_WCR_SRS))) { watchdog_perform_action(); } } diff --git a/hw/misc/imx_rngc.c b/hw/misc/imx_rngc.c new file mode 100644 index 0000000000..4c270df2db --- /dev/null +++ b/hw/misc/imx_rngc.c @@ -0,0 +1,278 @@ +/* + * Freescale i.MX RNGC emulation + * + * Copyright (C) 2020 Martin Kaiser + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + * This driver provides the minimum functionality to initialize and seed + * an rngc and to read random numbers. The rngb that is found in imx25 + * chipsets is also supported. + */ + +#include "qemu/osdep.h" +#include "qemu/main-loop.h" +#include "qemu/module.h" +#include "qemu/log.h" +#include "qemu/guest-random.h" +#include "hw/irq.h" +#include "hw/misc/imx_rngc.h" +#include "migration/vmstate.h" + +#define RNGC_NAME "i.MX RNGC" + +#define RNGC_VER_ID 0x00 +#define RNGC_COMMAND 0x04 +#define RNGC_CONTROL 0x08 +#define RNGC_STATUS 0x0C +#define RNGC_FIFO 0x14 + +/* These version info are reported by the rngb in an imx258 chip. */ +#define RNG_TYPE_RNGB 0x1 +#define V_MAJ 0x2 +#define V_MIN 0x40 + +#define RNGC_CMD_BIT_SW_RST 0x40 +#define RNGC_CMD_BIT_CLR_ERR 0x20 +#define RNGC_CMD_BIT_CLR_INT 0x10 +#define RNGC_CMD_BIT_SEED 0x02 +#define RNGC_CMD_BIT_SELF_TEST 0x01 + +#define RNGC_CTRL_BIT_MASK_ERR 0x40 +#define RNGC_CTRL_BIT_MASK_DONE 0x20 +#define RNGC_CTRL_BIT_AUTO_SEED 0x10 + +/* the current status for self-test and seed operations */ +#define OP_IDLE 0 +#define OP_RUN 1 +#define OP_DONE 2 + +static uint64_t imx_rngc_read(void *opaque, hwaddr offset, unsigned size) +{ + IMXRNGCState *s = IMX_RNGC(opaque); + uint64_t val = 0; + + switch (offset) { + case RNGC_VER_ID: + val |= RNG_TYPE_RNGB << 28 | V_MAJ << 8 | V_MIN; + break; + + case RNGC_COMMAND: + if (s->op_seed == OP_RUN) { + val |= RNGC_CMD_BIT_SEED; + } + if (s->op_self_test == OP_RUN) { + val |= RNGC_CMD_BIT_SELF_TEST; + } + break; + + case RNGC_CONTROL: + /* + * The CTL_ACC and VERIF_MODE bits are not supported yet. + * They read as 0. + */ + val |= s->mask; + if (s->auto_seed) { + val |= RNGC_CTRL_BIT_AUTO_SEED; + } + /* + * We don't have an internal fifo like the real hardware. + * There's no need for strategy to handle fifo underflows. + * We return the FIFO_UFLOW_RESPONSE bits as 0. + */ + break; + + case RNGC_STATUS: + /* + * We never report any statistics test or self-test errors or any + * other errors. STAT_TEST_PF, ST_PF and ERROR are always 0. + */ + + /* + * We don't have an internal fifo, see above. Therefore, we + * report back the default fifo size (5 32-bit words) and + * indicate that our fifo is always full. + */ + val |= 5 << 12 | 5 << 8; + + /* We always have a new seed available. */ + val |= 1 << 6; + + if (s->op_seed == OP_DONE) { + val |= 1 << 5; + } + if (s->op_self_test == OP_DONE) { + val |= 1 << 4; + } + if (s->op_seed == OP_RUN || s->op_self_test == OP_RUN) { + /* + * We're busy if self-test is running or if we're + * seeding the prng. + */ + val |= 1 << 1; + } else { + /* + * We're ready to provide secure random numbers whenever + * we're not busy. + */ + val |= 1; + } + break; + + case RNGC_FIFO: + qemu_guest_getrandom_nofail(&val, sizeof(val)); + break; + } + + return val; +} + +static void imx_rngc_do_reset(IMXRNGCState *s) +{ + s->op_self_test = OP_IDLE; + s->op_seed = OP_IDLE; + s->mask = 0; + s->auto_seed = false; +} + +static void imx_rngc_write(void *opaque, hwaddr offset, uint64_t value, + unsigned size) +{ + IMXRNGCState *s = IMX_RNGC(opaque); + + switch (offset) { + case RNGC_COMMAND: + if (value & RNGC_CMD_BIT_SW_RST) { + imx_rngc_do_reset(s); + } + + /* + * For now, both CLR_ERR and CLR_INT clear the interrupt. We + * don't report any errors yet. + */ + if (value & (RNGC_CMD_BIT_CLR_ERR | RNGC_CMD_BIT_CLR_INT)) { + qemu_irq_lower(s->irq); + } + + if (value & RNGC_CMD_BIT_SEED) { + s->op_seed = OP_RUN; + qemu_bh_schedule(s->seed_bh); + } + + if (value & RNGC_CMD_BIT_SELF_TEST) { + s->op_self_test = OP_RUN; + qemu_bh_schedule(s->self_test_bh); + } + break; + + case RNGC_CONTROL: + /* + * The CTL_ACC and VERIF_MODE bits are not supported yet. + * We ignore them if they're set by the caller. + */ + + if (value & RNGC_CTRL_BIT_MASK_ERR) { + s->mask |= RNGC_CTRL_BIT_MASK_ERR; + } else { + s->mask &= ~RNGC_CTRL_BIT_MASK_ERR; + } + + if (value & RNGC_CTRL_BIT_MASK_DONE) { + s->mask |= RNGC_CTRL_BIT_MASK_DONE; + } else { + s->mask &= ~RNGC_CTRL_BIT_MASK_DONE; + } + + if (value & RNGC_CTRL_BIT_AUTO_SEED) { + s->auto_seed = true; + } else { + s->auto_seed = false; + } + break; + } +} + +static const MemoryRegionOps imx_rngc_ops = { + .read = imx_rngc_read, + .write = imx_rngc_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static void imx_rngc_self_test(void *opaque) +{ + IMXRNGCState *s = IMX_RNGC(opaque); + + s->op_self_test = OP_DONE; + if (!(s->mask & RNGC_CTRL_BIT_MASK_DONE)) { + qemu_irq_raise(s->irq); + } +} + +static void imx_rngc_seed(void *opaque) +{ + IMXRNGCState *s = IMX_RNGC(opaque); + + s->op_seed = OP_DONE; + if (!(s->mask & RNGC_CTRL_BIT_MASK_DONE)) { + qemu_irq_raise(s->irq); + } +} + +static void imx_rngc_realize(DeviceState *dev, Error **errp) +{ + IMXRNGCState *s = IMX_RNGC(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + + memory_region_init_io(&s->iomem, OBJECT(s), &imx_rngc_ops, s, + TYPE_IMX_RNGC, 0x1000); + sysbus_init_mmio(sbd, &s->iomem); + + sysbus_init_irq(sbd, &s->irq); + s->self_test_bh = qemu_bh_new(imx_rngc_self_test, s); + s->seed_bh = qemu_bh_new(imx_rngc_seed, s); +} + +static void imx_rngc_reset(DeviceState *dev) +{ + IMXRNGCState *s = IMX_RNGC(dev); + + imx_rngc_do_reset(s); +} + +static const VMStateDescription vmstate_imx_rngc = { + .name = RNGC_NAME, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT8(op_self_test, IMXRNGCState), + VMSTATE_UINT8(op_seed, IMXRNGCState), + VMSTATE_UINT8(mask, IMXRNGCState), + VMSTATE_BOOL(auto_seed, IMXRNGCState), + VMSTATE_END_OF_LIST() + } +}; + +static void imx_rngc_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = imx_rngc_realize; + dc->reset = imx_rngc_reset; + dc->desc = RNGC_NAME, + dc->vmsd = &vmstate_imx_rngc; +} + +static const TypeInfo imx_rngc_info = { + .name = TYPE_IMX_RNGC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(IMXRNGCState), + .class_init = imx_rngc_class_init, +}; + +static void imx_rngc_register_types(void) +{ + type_register_static(&imx_rngc_info); +} + +type_init(imx_rngc_register_types) diff --git a/hw/misc/iotkit-secctl.c b/hw/misc/iotkit-secctl.c index 609869821a..9fdb82056a 100644 --- a/hw/misc/iotkit-secctl.c +++ b/hw/misc/iotkit-secctl.c @@ -340,7 +340,7 @@ static MemTxResult iotkit_secctl_s_write(void *opaque, hwaddr addr, qemu_set_irq(s->sec_resp_cfg, s->secrespcfg); break; case A_SECPPCINTCLR: - value &= 0x00f000f3; + s->secppcintstat &= ~(value & 0x00f000f3); foreach_ppc(s, iotkit_secctl_ppc_update_irq_clear); break; case A_SECPPCINTEN: diff --git a/hw/misc/iotkit-sysctl.c b/hw/misc/iotkit-sysctl.c index 9aa8109463..ec1cc1931a 100644 --- a/hw/misc/iotkit-sysctl.c +++ b/hw/misc/iotkit-sysctl.c @@ -508,7 +508,7 @@ static void iotkit_sysctl_class_init(ObjectClass *klass, void *data) dc->vmsd = &iotkit_sysctl_vmstate; dc->reset = iotkit_sysctl_reset; - dc->props = iotkit_sysctl_props; + device_class_set_props(dc, iotkit_sysctl_props); dc->realize = iotkit_sysctl_realize; } diff --git a/hw/misc/iotkit-sysinfo.c b/hw/misc/iotkit-sysinfo.c index 783e613959..b2dcfc4376 100644 --- a/hw/misc/iotkit-sysinfo.c +++ b/hw/misc/iotkit-sysinfo.c @@ -121,7 +121,7 @@ static void iotkit_sysinfo_class_init(ObjectClass *klass, void *data) * does not need a reset function or VMState. */ - dc->props = iotkit_sysinfo_props; + device_class_set_props(dc, iotkit_sysinfo_props); } static const TypeInfo iotkit_sysinfo_info = { diff --git a/hw/misc/ivshmem.c b/hw/misc/ivshmem.c index 5e3b05eae0..a8dc9b377d 100644 --- a/hw/misc/ivshmem.c +++ b/hw/misc/ivshmem.c @@ -136,44 +136,11 @@ static inline bool ivshmem_is_master(IVShmemState *s) return s->master == ON_OFF_AUTO_ON; } -static void ivshmem_update_irq(IVShmemState *s) -{ - PCIDevice *d = PCI_DEVICE(s); - uint32_t isr = s->intrstatus & s->intrmask; - - /* - * Do nothing unless the device actually uses INTx. Here's how - * the device variants signal interrupts, what they put in PCI - * config space: - * Device variant Interrupt Interrupt Pin MSI-X cap. - * ivshmem-plain none 0 no - * ivshmem-doorbell MSI-X 1 yes(1) - * ivshmem,msi=off INTx 1 no - * ivshmem,msi=on MSI-X 1(2) yes(1) - * (1) if guest enabled MSI-X - * (2) the device lies - * Leads to the condition for doing nothing: - */ - if (ivshmem_has_feature(s, IVSHMEM_MSI) - || !d->config[PCI_INTERRUPT_PIN]) { - return; - } - - /* don't print ISR resets */ - if (isr) { - IVSHMEM_DPRINTF("Set IRQ to %d (%04x %04x)\n", - isr ? 1 : 0, s->intrstatus, s->intrmask); - } - - pci_set_irq(d, isr != 0); -} - static void ivshmem_IntrMask_write(IVShmemState *s, uint32_t val) { IVSHMEM_DPRINTF("IntrMask write(w) val = 0x%04x\n", val); s->intrmask = val; - ivshmem_update_irq(s); } static uint32_t ivshmem_IntrMask_read(IVShmemState *s) @@ -189,7 +156,6 @@ static void ivshmem_IntrStatus_write(IVShmemState *s, uint32_t val) IVSHMEM_DPRINTF("IntrStatus write(w) val = 0x%04x\n", val); s->intrstatus = val; - ivshmem_update_irq(s); } static uint32_t ivshmem_IntrStatus_read(IVShmemState *s) @@ -198,7 +164,6 @@ static uint32_t ivshmem_IntrStatus_read(IVShmemState *s) /* reading ISR clears all interrupts */ s->intrstatus = 0; - ivshmem_update_irq(s); return ret; } @@ -867,7 +832,6 @@ static void ivshmem_common_realize(PCIDevice *dev, Error **errp) IVShmemState *s = IVSHMEM_COMMON(dev); Error *err = NULL; uint8_t *pci_conf; - Error *local_err = NULL; /* IRQFD requires MSI */ if (ivshmem_has_feature(s, IVSHMEM_IOEVENTFD) && @@ -934,9 +898,9 @@ static void ivshmem_common_realize(PCIDevice *dev, Error **errp) if (!ivshmem_is_master(s)) { error_setg(&s->migration_blocker, "Migration is disabled when using feature 'peer mode' in device 'ivshmem'"); - migrate_add_blocker(s->migration_blocker, &local_err); - if (local_err) { - error_propagate(errp, local_err); + migrate_add_blocker(s->migration_blocker, &err); + if (err) { + error_propagate(errp, err); error_free(s->migration_blocker); return; } @@ -1090,7 +1054,7 @@ static void ivshmem_plain_class_init(ObjectClass *klass, void *data) PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); k->realize = ivshmem_plain_realize; - dc->props = ivshmem_plain_properties; + device_class_set_props(dc, ivshmem_plain_properties); dc->vmsd = &ivshmem_plain_vmsd; } @@ -1150,7 +1114,7 @@ static void ivshmem_doorbell_class_init(ObjectClass *klass, void *data) PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); k->realize = ivshmem_doorbell_realize; - dc->props = ivshmem_doorbell_properties; + device_class_set_props(dc, ivshmem_doorbell_properties); dc->vmsd = &ivshmem_doorbell_vmsd; } diff --git a/hw/misc/mac_via.c b/hw/misc/mac_via.c index f3f130ad96..81343301b1 100644 --- a/hw/misc/mac_via.c +++ b/hw/misc/mac_via.c @@ -27,7 +27,10 @@ #include "sysemu/runstate.h" #include "qapi/error.h" #include "qemu/cutils.h" - +#include "hw/qdev-properties.h" +#include "sysemu/block-backend.h" +#include "trace.h" +#include "qemu/log.h" /* * VIAs: There are two in every machine, @@ -278,6 +281,21 @@ /* VIA returns time offset from Jan 1, 1904, not 1970 */ #define RTC_OFFSET 2082844800 +enum { + REG_0, + REG_1, + REG_2, + REG_3, + REG_TEST, + REG_WPROTECT, + REG_PRAM_ADDR, + REG_PRAM_ADDR_LAST = REG_PRAM_ADDR + 19, + REG_PRAM_SECT, + REG_PRAM_SECT_LAST = REG_PRAM_SECT + 7, + REG_INVALID, + REG_EMPTY = 0xff, +}; + static void via1_VBL_update(MOS6522Q800VIA1State *v1s) { MOS6522State *s = MOS6522(v1s); @@ -360,10 +378,73 @@ static void via2_irq_request(void *opaque, int irq, int level) mdc->update_irq(s); } + +static void pram_update(MacVIAState *m) +{ + if (m->blk) { + if (blk_pwrite(m->blk, 0, m->mos6522_via1.PRAM, + sizeof(m->mos6522_via1.PRAM), 0) < 0) { + qemu_log("pram_update: cannot write to file\n"); + } + } +} + +/* + * RTC Commands + * + * Command byte Register addressed by the command + * + * z0000001 Seconds register 0 (lowest-order byte) + * z0000101 Seconds register 1 + * z0001001 Seconds register 2 + * z0001101 Seconds register 3 (highest-order byte) + * 00110001 Test register (write-only) + * 00110101 Write-Protect Register (write-only) + * z010aa01 RAM address 100aa ($10-$13) (first 20 bytes only) + * z1aaaa01 RAM address 0aaaa ($00-$0F) (first 20 bytes only) + * z0111aaa Extended memory designator and sector number + * + * For a read request, z=1, for a write z=0 + * The letter a indicates bits whose value depend on what parameter + * RAM byte you want to address + */ +static int via1_rtc_compact_cmd(uint8_t value) +{ + uint8_t read = value & 0x80; + + value &= 0x7f; + + /* the last 2 bits of a command byte must always be 0b01 ... */ + if ((value & 0x78) == 0x38) { + /* except for the extended memory designator */ + return read | (REG_PRAM_SECT + (value & 0x07)); + } + if ((value & 0x03) == 0x01) { + value >>= 2; + if ((value & 0x1c) == 0) { + /* seconds registers */ + return read | (REG_0 + (value & 0x03)); + } else if ((value == 0x0c) && !read) { + return REG_TEST; + } else if ((value == 0x0d) && !read) { + return REG_WPROTECT; + } else if ((value & 0x1c) == 0x08) { + /* RAM address 0x10 to 0x13 */ + return read | (REG_PRAM_ADDR + 0x10 + (value & 0x03)); + } else if ((value & 0x43) == 0x41) { + /* RAM address 0x00 to 0x0f */ + return read | (REG_PRAM_ADDR + (value & 0x0f)); + } + } + return REG_INVALID; +} + static void via1_rtc_update(MacVIAState *m) { MOS6522Q800VIA1State *v1s = &m->mos6522_via1; MOS6522State *s = MOS6522(v1s); + int cmd, sector, addr; + uint32_t time; if (s->b & VIA1B_vRTCEnb) { return; @@ -376,7 +457,9 @@ static void via1_rtc_update(MacVIAState *m) m->data_out |= s->b & VIA1B_vRTCData; m->data_out_cnt++; } + trace_via1_rtc_update_data_out(m->data_out_cnt, m->data_out); } else { + trace_via1_rtc_update_data_in(m->data_in_cnt, m->data_in); /* receive bits from the RTC */ if ((v1s->last_b & VIA1B_vRTCClk) && !(s->b & VIA1B_vRTCClk) && @@ -386,96 +469,134 @@ static void via1_rtc_update(MacVIAState *m) m->data_in <<= 1; m->data_in_cnt--; } + return; } - if (m->data_out_cnt == 8) { - m->data_out_cnt = 0; + if (m->data_out_cnt != 8) { + return; + } - if (m->cmd == 0) { - if (m->data_out & 0x80) { - /* this is a read command */ - uint32_t time = m->tick_offset + - (qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) / - NANOSECONDS_PER_SECOND); - if (m->data_out == 0x81) { /* seconds register 0 */ - m->data_in = time & 0xff; - m->data_in_cnt = 8; - } else if (m->data_out == 0x85) { /* seconds register 1 */ - m->data_in = (time >> 8) & 0xff; - m->data_in_cnt = 8; - } else if (m->data_out == 0x89) { /* seconds register 2 */ - m->data_in = (time >> 16) & 0xff; - m->data_in_cnt = 8; - } else if (m->data_out == 0x8d) { /* seconds register 3 */ - m->data_in = (time >> 24) & 0xff; - m->data_in_cnt = 8; - } else if ((m->data_out & 0xf3) == 0xa1) { - /* PRAM address 0x10 -> 0x13 */ - int addr = (m->data_out >> 2) & 0x03; - m->data_in = v1s->PRAM[addr]; - m->data_in_cnt = 8; - } else if ((m->data_out & 0xf3) == 0xa1) { - /* PRAM address 0x00 -> 0x0f */ - int addr = (m->data_out >> 2) & 0x0f; - m->data_in = v1s->PRAM[addr]; - m->data_in_cnt = 8; - } else if ((m->data_out & 0xf8) == 0xb8) { - /* extended memory designator and sector number */ - m->cmd = m->data_out; - } - } else { - /* this is a write command */ - m->cmd = m->data_out; - } - } else { - if (m->cmd & 0x80) { - if ((m->cmd & 0xf8) == 0xb8) { - /* extended memory designator and sector number */ - int sector = m->cmd & 0x07; - int addr = (m->data_out >> 2) & 0x1f; + m->data_out_cnt = 0; - m->data_in = v1s->PRAM[sector * 8 + addr]; - m->data_in_cnt = 8; - } - } else if (!m->wprotect) { - /* this is a write command */ - if (m->alt != 0) { - /* extended memory designator and sector number */ - int sector = m->cmd & 0x07; - int addr = (m->alt >> 2) & 0x1f; + trace_via1_rtc_internal_status(m->cmd, m->alt, m->data_out); + /* first byte: it's a command */ + if (m->cmd == REG_EMPTY) { - v1s->PRAM[sector * 8 + addr] = m->data_out; + cmd = via1_rtc_compact_cmd(m->data_out); + trace_via1_rtc_internal_cmd(cmd); - m->alt = 0; - } else if (m->cmd == 0x01) { /* seconds register 0 */ - /* FIXME */ - } else if (m->cmd == 0x05) { /* seconds register 1 */ - /* FIXME */ - } else if (m->cmd == 0x09) { /* seconds register 2 */ - /* FIXME */ - } else if (m->cmd == 0x0d) { /* seconds register 3 */ - /* FIXME */ - } else if (m->cmd == 0x31) { - /* Test Register */ - } else if (m->cmd == 0x35) { - /* Write Protect register */ - m->wprotect = m->data_out & 1; - } else if ((m->cmd & 0xf3) == 0xa1) { - /* PRAM address 0x10 -> 0x13 */ - int addr = (m->cmd >> 2) & 0x03; - v1s->PRAM[addr] = m->data_out; - } else if ((m->cmd & 0xf3) == 0xa1) { - /* PRAM address 0x00 -> 0x0f */ - int addr = (m->cmd >> 2) & 0x0f; - v1s->PRAM[addr] = m->data_out; - } else if ((m->cmd & 0xf8) == 0xb8) { - /* extended memory designator and sector number */ - m->alt = m->cmd; - } - } + if (cmd == REG_INVALID) { + trace_via1_rtc_cmd_invalid(m->data_out); + return; } - m->data_out = 0; + + if (cmd & 0x80) { /* this is a read command */ + switch (cmd & 0x7f) { + case REG_0...REG_3: /* seconds registers */ + /* + * register 0 is lowest-order byte + * register 3 is highest-order byte + */ + + time = m->tick_offset + (qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + / NANOSECONDS_PER_SECOND); + trace_via1_rtc_internal_time(time); + m->data_in = (time >> ((cmd & 0x03) << 3)) & 0xff; + m->data_in_cnt = 8; + trace_via1_rtc_cmd_seconds_read((cmd & 0x7f) - REG_0, + m->data_in); + break; + case REG_PRAM_ADDR...REG_PRAM_ADDR_LAST: + /* PRAM address 0x00 -> 0x13 */ + m->data_in = v1s->PRAM[(cmd & 0x7f) - REG_PRAM_ADDR]; + m->data_in_cnt = 8; + trace_via1_rtc_cmd_pram_read((cmd & 0x7f) - REG_PRAM_ADDR, + m->data_in); + break; + case REG_PRAM_SECT...REG_PRAM_SECT_LAST: + /* + * extended memory designator and sector number + * the only two-byte read command + */ + trace_via1_rtc_internal_set_cmd(cmd); + m->cmd = cmd; + break; + default: + g_assert_not_reached(); + break; + } + return; + } + + /* this is a write command, needs a parameter */ + if (cmd == REG_WPROTECT || !m->wprotect) { + trace_via1_rtc_internal_set_cmd(cmd); + m->cmd = cmd; + } else { + trace_via1_rtc_internal_ignore_cmd(cmd); + } + return; } + + /* second byte: it's a parameter */ + if (m->alt == REG_EMPTY) { + switch (m->cmd & 0x7f) { + case REG_0...REG_3: /* seconds register */ + /* FIXME */ + trace_via1_rtc_cmd_seconds_write(m->cmd - REG_0, m->data_out); + m->cmd = REG_EMPTY; + break; + case REG_TEST: + /* device control: nothing to do */ + trace_via1_rtc_cmd_test_write(m->data_out); + m->cmd = REG_EMPTY; + break; + case REG_WPROTECT: + /* Write Protect register */ + trace_via1_rtc_cmd_wprotect_write(m->data_out); + m->wprotect = !!(m->data_out & 0x80); + m->cmd = REG_EMPTY; + break; + case REG_PRAM_ADDR...REG_PRAM_ADDR_LAST: + /* PRAM address 0x00 -> 0x13 */ + trace_via1_rtc_cmd_pram_write(m->cmd - REG_PRAM_ADDR, m->data_out); + v1s->PRAM[m->cmd - REG_PRAM_ADDR] = m->data_out; + pram_update(m); + m->cmd = REG_EMPTY; + break; + case REG_PRAM_SECT...REG_PRAM_SECT_LAST: + addr = (m->data_out >> 2) & 0x1f; + sector = (m->cmd & 0x7f) - REG_PRAM_SECT; + if (m->cmd & 0x80) { + /* it's a read */ + m->data_in = v1s->PRAM[sector * 32 + addr]; + m->data_in_cnt = 8; + trace_via1_rtc_cmd_pram_sect_read(sector, addr, + sector * 32 + addr, + m->data_in); + m->cmd = REG_EMPTY; + } else { + /* it's a write, we need one more parameter */ + trace_via1_rtc_internal_set_alt(addr, sector, addr); + m->alt = addr; + } + break; + default: + g_assert_not_reached(); + break; + } + return; + } + + /* third byte: it's the data of a REG_PRAM_SECT write */ + g_assert(REG_PRAM_SECT <= m->cmd && m->cmd <= REG_PRAM_SECT_LAST); + sector = m->cmd - REG_PRAM_SECT; + v1s->PRAM[sector * 32 + m->alt] = m->data_out; + pram_update(m); + trace_via1_rtc_cmd_pram_sect_write(sector, m->alt, sector * 32 + m->alt, + m->data_out); + m->alt = REG_EMPTY; + m->cmd = REG_EMPTY; } static int adb_via_poll(MacVIAState *s, int state, uint8_t *data) @@ -742,6 +863,9 @@ static void mac_via_reset(DeviceState *dev) v1s->next_VBL = 0; timer_del(v1s->one_second_timer); v1s->next_second = 0; + + m->cmd = REG_EMPTY; + m->alt = REG_EMPTY; } static void mac_via_realize(DeviceState *dev, Error **errp) @@ -749,6 +873,7 @@ static void mac_via_realize(DeviceState *dev, Error **errp) MacVIAState *m = MAC_VIA(dev); MOS6522State *ms; struct tm tm; + int ret; /* Init VIAs 1 and 2 */ sysbus_init_child_obj(OBJECT(dev), "via1", &m->mos6522_via1, @@ -782,6 +907,28 @@ static void mac_via_realize(DeviceState *dev, Error **errp) m->adb_poll_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, via_adb_poll, m); m->adb_data_ready = qdev_get_gpio_in_named(dev, "via1-irq", VIA1_IRQ_ADB_READY_BIT); + + if (m->blk) { + int64_t len = blk_getlength(m->blk); + if (len < 0) { + error_setg_errno(errp, -len, + "could not get length of backing image"); + return; + } + ret = blk_set_perm(m->blk, + BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE, + BLK_PERM_ALL, errp); + if (ret < 0) { + return; + } + + len = blk_pread(m->blk, 0, m->mos6522_via1.PRAM, + sizeof(m->mos6522_via1.PRAM)); + if (len != sizeof(m->mos6522_via1.PRAM)) { + error_setg(errp, "can't read PRAM contents"); + return; + } + } } static void mac_via_init(Object *obj) @@ -806,10 +953,33 @@ static void mac_via_init(Object *obj) TYPE_ADB_BUS, DEVICE(obj), "adb.0"); } +static void postload_update_cb(void *opaque, int running, RunState state) +{ + MacVIAState *m = MAC_VIA(opaque); + + qemu_del_vm_change_state_handler(m->vmstate); + m->vmstate = NULL; + + pram_update(m); +} + +static int mac_via_post_load(void *opaque, int version_id) +{ + MacVIAState *m = MAC_VIA(opaque); + + if (m->blk) { + m->vmstate = qemu_add_vm_change_state_handler(postload_update_cb, + m); + } + + return 0; +} + static const VMStateDescription vmstate_mac_via = { .name = "mac-via", .version_id = 1, .minimum_version_id = 1, + .post_load = mac_via_post_load, .fields = (VMStateField[]) { /* VIAs */ VMSTATE_STRUCT(mos6522_via1.parent_obj, MacVIAState, 0, vmstate_mos6522, @@ -842,6 +1012,11 @@ static const VMStateDescription vmstate_mac_via = { } }; +static Property mac_via_properties[] = { + DEFINE_PROP_DRIVE("drive", MacVIAState, blk), + DEFINE_PROP_END_OF_LIST(), +}; + static void mac_via_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); @@ -849,6 +1024,7 @@ static void mac_via_class_init(ObjectClass *oc, void *data) dc->realize = mac_via_realize; dc->reset = mac_via_reset; dc->vmsd = &vmstate_mac_via; + device_class_set_props(dc, mac_via_properties); } static TypeInfo mac_via_info = { diff --git a/hw/misc/macio/cuda.c b/hw/misc/macio/cuda.c index e38becba6a..e0cc0aac5d 100644 --- a/hw/misc/macio/cuda.c +++ b/hw/misc/macio/cuda.c @@ -571,7 +571,7 @@ static void cuda_class_init(ObjectClass *oc, void *data) dc->realize = cuda_realize; dc->reset = cuda_reset; dc->vmsd = &vmstate_cuda; - dc->props = cuda_properties; + device_class_set_props(dc, cuda_properties); set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); } diff --git a/hw/misc/macio/macio.c b/hw/misc/macio/macio.c index 50f20d8206..79222192e8 100644 --- a/hw/misc/macio/macio.c +++ b/hw/misc/macio/macio.c @@ -466,7 +466,7 @@ static void macio_newworld_class_init(ObjectClass *oc, void *data) pdc->realize = macio_newworld_realize; pdc->device_id = PCI_DEVICE_ID_APPLE_UNI_N_KEYL; dc->vmsd = &vmstate_macio_newworld; - dc->props = macio_newworld_properties; + device_class_set_props(dc, macio_newworld_properties); } static Property macio_properties[] = { @@ -481,7 +481,7 @@ static void macio_class_init(ObjectClass *klass, void *data) k->vendor_id = PCI_VENDOR_ID_APPLE; k->class_id = PCI_CLASS_OTHERS << 8; - dc->props = macio_properties; + device_class_set_props(dc, macio_properties); set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); /* Reason: Uses serial_hds in macio_instance_init */ dc->user_creatable = false; diff --git a/hw/misc/macio/pmu.c b/hw/misc/macio/pmu.c index 769aed8052..b8466a4a3f 100644 --- a/hw/misc/macio/pmu.c +++ b/hw/misc/macio/pmu.c @@ -795,7 +795,7 @@ static void pmu_class_init(ObjectClass *oc, void *data) dc->realize = pmu_realize; dc->reset = pmu_reset; dc->vmsd = &vmstate_pmu; - dc->props = pmu_properties; + device_class_set_props(dc, pmu_properties); set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); } diff --git a/hw/misc/max111x.c b/hw/misc/max111x.c index a713149f16..2b87bdee5b 100644 --- a/hw/misc/max111x.c +++ b/hw/misc/max111x.c @@ -146,7 +146,8 @@ static int max111x_init(SSISlave *d, int inputs) s->input[7] = 0x80; s->com = 0; - vmstate_register(dev, -1, &vmstate_max111x, s); + vmstate_register(VMSTATE_IF(dev), VMSTATE_INSTANCE_ID_ANY, + &vmstate_max111x, s); return 0; } diff --git a/hw/misc/mips_cmgcr.c b/hw/misc/mips_cmgcr.c index 8176d91c0c..3c8b37f700 100644 --- a/hw/misc/mips_cmgcr.c +++ b/hw/misc/mips_cmgcr.c @@ -233,7 +233,7 @@ static void mips_gcr_realize(DeviceState *dev, Error **errp) static void mips_gcr_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->props = mips_gcr_properties; + device_class_set_props(dc, mips_gcr_properties); dc->vmsd = &vmstate_mips_gcr; dc->reset = mips_gcr_reset; dc->realize = mips_gcr_realize; diff --git a/hw/misc/mips_cpc.c b/hw/misc/mips_cpc.c index 19ea016b87..2f7b2c9592 100644 --- a/hw/misc/mips_cpc.c +++ b/hw/misc/mips_cpc.c @@ -175,7 +175,7 @@ static void mips_cpc_class_init(ObjectClass *klass, void *data) dc->realize = mips_cpc_realize; dc->reset = mips_cpc_reset; dc->vmsd = &vmstate_mips_cpc; - dc->props = mips_cpc_properties; + device_class_set_props(dc, mips_cpc_properties); } static const TypeInfo mips_cpc_info = { diff --git a/hw/misc/mips_itu.c b/hw/misc/mips_itu.c index ea0cf9726b..3540985258 100644 --- a/hw/misc/mips_itu.c +++ b/hw/misc/mips_itu.c @@ -561,7 +561,7 @@ static void mips_itu_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->props = mips_itu_properties; + device_class_set_props(dc, mips_itu_properties); dc->realize = mips_itu_realize; dc->reset = mips_itu_reset; } diff --git a/hw/misc/mos6522.c b/hw/misc/mos6522.c index cecf0be59e..19e154b870 100644 --- a/hw/misc/mos6522.c +++ b/hw/misc/mos6522.c @@ -244,6 +244,9 @@ uint64_t mos6522_read(void *opaque, hwaddr addr, unsigned size) val = s->b; break; case VIA_REG_A: + qemu_log_mask(LOG_UNIMP, "Read access to register A with handshake"); + /* fall through */ + case VIA_REG_ANH: val = s->a; break; case VIA_REG_DIRB: @@ -297,9 +300,7 @@ uint64_t mos6522_read(void *opaque, hwaddr addr, unsigned size) val = s->ier | 0x80; break; default: - case VIA_REG_ANH: - val = s->anh; - break; + g_assert_not_reached(); } if (addr != VIA_REG_IFR || val != 0) { @@ -322,6 +323,9 @@ void mos6522_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) mdc->portB_write(s); break; case VIA_REG_A: + qemu_log_mask(LOG_UNIMP, "Write access to register A with handshake"); + /* fall through */ + case VIA_REG_ANH: s->a = (s->a & ~s->dira) | (val & s->dira); mdc->portA_write(s); break; @@ -395,9 +399,7 @@ void mos6522_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)); break; default: - case VIA_REG_ANH: - s->anh = val; - break; + g_assert_not_reached(); } } @@ -439,7 +441,6 @@ const VMStateDescription vmstate_mos6522 = { VMSTATE_UINT8(pcr, MOS6522State), VMSTATE_UINT8(ifr, MOS6522State), VMSTATE_UINT8(ier, MOS6522State), - VMSTATE_UINT8(anh, MOS6522State), VMSTATE_STRUCT_ARRAY(timers, MOS6522State, 2, 0, vmstate_mos6522_timer, MOS6522Timer), VMSTATE_END_OF_LIST() @@ -460,7 +461,6 @@ static void mos6522_reset(DeviceState *dev) s->ifr = 0; s->ier = 0; /* s->ier = T1_INT | SR_INT; */ - s->anh = 0; s->timers[0].frequency = s->frequency; s->timers[0].latch = 0xffff; @@ -502,7 +502,7 @@ static void mos6522_class_init(ObjectClass *oc, void *data) dc->reset = mos6522_reset; dc->vmsd = &vmstate_mos6522; - dc->props = mos6522_properties; + device_class_set_props(dc, mos6522_properties); mdc->parent_reset = dc->reset; mdc->set_sr_int = mos6522_set_sr_int; mdc->portB_write = mos6522_portB_write; diff --git a/hw/misc/mps2-fpgaio.c b/hw/misc/mps2-fpgaio.c index 5e8b103914..2f3fbeef34 100644 --- a/hw/misc/mps2-fpgaio.c +++ b/hw/misc/mps2-fpgaio.c @@ -300,7 +300,7 @@ static void mps2_fpgaio_class_init(ObjectClass *klass, void *data) dc->vmsd = &mps2_fpgaio_vmstate; dc->reset = mps2_fpgaio_reset; - dc->props = mps2_fpgaio_properties; + device_class_set_props(dc, mps2_fpgaio_properties); } static const TypeInfo mps2_fpgaio_info = { diff --git a/hw/misc/mps2-scc.c b/hw/misc/mps2-scc.c index 25779a1dca..9d0909e7b3 100644 --- a/hw/misc/mps2-scc.c +++ b/hw/misc/mps2-scc.c @@ -293,7 +293,7 @@ static void mps2_scc_class_init(ObjectClass *klass, void *data) dc->realize = mps2_scc_realize; dc->vmsd = &mps2_scc_vmstate; dc->reset = mps2_scc_reset; - dc->props = mps2_scc_properties; + device_class_set_props(dc, mps2_scc_properties); } static const TypeInfo mps2_scc_info = { diff --git a/hw/misc/msf2-sysreg.c b/hw/misc/msf2-sysreg.c index ddc5a30c80..2dce55c364 100644 --- a/hw/misc/msf2-sysreg.c +++ b/hw/misc/msf2-sysreg.c @@ -143,7 +143,7 @@ static void msf2_sysreg_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_msf2_sysreg; dc->reset = msf2_sysreg_reset; - dc->props = msf2_sysreg_properties; + device_class_set_props(dc, msf2_sysreg_properties); dc->realize = msf2_sysreg_realize; } diff --git a/hw/misc/nrf51_rng.c b/hw/misc/nrf51_rng.c index 5140981c96..fc86e1b697 100644 --- a/hw/misc/nrf51_rng.c +++ b/hw/misc/nrf51_rng.c @@ -245,7 +245,7 @@ static void nrf51_rng_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->props = nrf51_rng_properties; + device_class_set_props(dc, nrf51_rng_properties); dc->vmsd = &vmstate_rng; dc->reset = nrf51_rng_reset; } diff --git a/hw/misc/omap_l4.c b/hw/misc/omap_l4.c index 61b6df564a..54aeaecd69 100644 --- a/hw/misc/omap_l4.c +++ b/hw/misc/omap_l4.c @@ -24,7 +24,7 @@ struct omap_l4_s { MemoryRegion *address_space; hwaddr base; int ta_num; - struct omap_target_agent_s ta[0]; + struct omap_target_agent_s ta[]; }; struct omap_l4_s *omap_l4_init(MemoryRegion *address_space, diff --git a/hw/misc/pc-testdev.c b/hw/misc/pc-testdev.c index 0fb84ddc6b..8aa8e6549f 100644 --- a/hw/misc/pc-testdev.c +++ b/hw/misc/pc-testdev.c @@ -125,7 +125,7 @@ static void test_flush_page_write(void *opaque, hwaddr addr, uint64_t data, unsigned len) { hwaddr page = 4096; - void *a = cpu_physical_memory_map(data & ~0xffful, &page, 0); + void *a = cpu_physical_memory_map(data & ~0xffful, &page, false); /* We might not be able to get the full page, only mprotect what we actually have mapped */ diff --git a/hw/misc/pca9552.c b/hw/misc/pca9552.c index 73be28d936..efd961e041 100644 --- a/hw/misc/pca9552.c +++ b/hw/misc/pca9552.c @@ -15,12 +15,16 @@ #include "hw/misc/pca9552.h" #include "hw/misc/pca9552_regs.h" #include "migration/vmstate.h" +#include "qapi/error.h" +#include "qapi/visitor.h" #define PCA9552_LED_ON 0x0 #define PCA9552_LED_OFF 0x1 #define PCA9552_LED_PWM0 0x2 #define PCA9552_LED_PWM1 0x3 +static const char *led_state[] = {"on", "off", "pwm0", "pwm1"}; + static uint8_t pca9552_pin_get_config(PCA9552State *s, int pin) { uint8_t reg = PCA9552_LS0 + (pin / 4); @@ -169,6 +173,82 @@ static int pca9552_event(I2CSlave *i2c, enum i2c_event event) return 0; } +static void pca9552_get_led(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + PCA9552State *s = PCA9552(obj); + int led, rc, reg; + uint8_t state; + + rc = sscanf(name, "led%2d", &led); + if (rc != 1) { + error_setg(errp, "%s: error reading %s", __func__, name); + return; + } + if (led < 0 || led > s->nr_leds) { + error_setg(errp, "%s invalid led %s", __func__, name); + return; + } + /* + * Get the LSx register as the qom interface should expose the device + * state, not the modeled 'input line' behaviour which would come from + * reading the INPUTx reg + */ + reg = PCA9552_LS0 + led / 4; + state = (pca9552_read(s, reg) >> (led % 8)) & 0x3; + visit_type_str(v, name, (char **)&led_state[state], errp); +} + +/* + * Return an LED selector register value based on an existing one, with + * the appropriate 2-bit state value set for the given LED number (0-3). + */ +static inline uint8_t pca955x_ledsel(uint8_t oldval, int led_num, int state) +{ + return (oldval & (~(0x3 << (led_num << 1)))) | + ((state & 0x3) << (led_num << 1)); +} + +static void pca9552_set_led(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + PCA9552State *s = PCA9552(obj); + Error *local_err = NULL; + int led, rc, reg, val; + uint8_t state; + char *state_str; + + visit_type_str(v, name, &state_str, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + rc = sscanf(name, "led%2d", &led); + if (rc != 1) { + error_setg(errp, "%s: error reading %s", __func__, name); + return; + } + if (led < 0 || led > s->nr_leds) { + error_setg(errp, "%s invalid led %s", __func__, name); + return; + } + + for (state = 0; state < ARRAY_SIZE(led_state); state++) { + if (!strcmp(state_str, led_state[state])) { + break; + } + } + if (state >= ARRAY_SIZE(led_state)) { + error_setg(errp, "%s invalid led state %s", __func__, state_str); + return; + } + + reg = PCA9552_LS0 + led / 4; + val = pca9552_read(s, reg); + val = pca955x_ledsel(val, led % 4, state); + pca9552_write(s, reg, val); +} + static const VMStateDescription pca9552_vmstate = { .name = "PCA9552", .version_id = 0, @@ -204,6 +284,7 @@ static void pca9552_reset(DeviceState *dev) static void pca9552_initfn(Object *obj) { PCA9552State *s = PCA9552(obj); + int led; /* If support for the other PCA955X devices are implemented, these * constant values might be part of class structure describing the @@ -211,6 +292,15 @@ static void pca9552_initfn(Object *obj) */ s->max_reg = PCA9552_LS3; s->nr_leds = 16; + + for (led = 0; led < s->nr_leds; led++) { + char *name; + + name = g_strdup_printf("led%d", led); + object_property_add(obj, name, "bool", pca9552_get_led, pca9552_set_led, + NULL, NULL, NULL); + g_free(name); + } } static void pca9552_class_init(ObjectClass *klass, void *data) diff --git a/hw/misc/pci-testdev.c b/hw/misc/pci-testdev.c index 74d8571a73..188de4d9cc 100644 --- a/hw/misc/pci-testdev.c +++ b/hw/misc/pci-testdev.c @@ -339,7 +339,7 @@ static void pci_testdev_class_init(ObjectClass *klass, void *data) dc->desc = "PCI Test Device"; set_bit(DEVICE_CATEGORY_MISC, dc->categories); dc->reset = qdev_pci_testdev_reset; - dc->props = pci_testdev_properties; + device_class_set_props(dc, pci_testdev_properties); } static const TypeInfo pci_testdev_info = { diff --git a/hw/misc/pvpanic.c b/hw/misc/pvpanic.c index d65ac86478..abb10bbcaf 100644 --- a/hw/misc/pvpanic.c +++ b/hw/misc/pvpanic.c @@ -21,11 +21,13 @@ #include "hw/qdev-properties.h" #include "hw/misc/pvpanic.h" -/* The bit of supported pv event */ +/* The bit of supported pv event, TODO: include uapi header and remove this */ #define PVPANIC_F_PANICKED 0 +#define PVPANIC_F_CRASHLOADED 1 /* The pv event value */ #define PVPANIC_PANICKED (1 << PVPANIC_F_PANICKED) +#define PVPANIC_CRASHLOADED (1 << PVPANIC_F_CRASHLOADED) #define ISA_PVPANIC_DEVICE(obj) \ OBJECT_CHECK(PVPanicState, (obj), TYPE_PVPANIC) @@ -34,7 +36,7 @@ static void handle_event(int event) { static bool logged; - if (event & ~PVPANIC_PANICKED && !logged) { + if (event & ~(PVPANIC_PANICKED | PVPANIC_CRASHLOADED) && !logged) { qemu_log_mask(LOG_GUEST_ERROR, "pvpanic: unknown event %#x.\n", event); logged = true; } @@ -43,6 +45,11 @@ static void handle_event(int event) qemu_system_guest_panicked(NULL); return; } + + if (event & PVPANIC_CRASHLOADED) { + qemu_system_guest_crashloaded(NULL); + return; + } } #include "hw/isa/isa.h" @@ -111,7 +118,7 @@ static void pvpanic_isa_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = pvpanic_isa_realizefn; - dc->props = pvpanic_isa_properties; + device_class_set_props(dc, pvpanic_isa_properties); set_bit(DEVICE_CATEGORY_MISC, dc->categories); } diff --git a/hw/misc/stm32f4xx_exti.c b/hw/misc/stm32f4xx_exti.c new file mode 100644 index 0000000000..02e7810046 --- /dev/null +++ b/hw/misc/stm32f4xx_exti.c @@ -0,0 +1,188 @@ +/* + * STM32F4XX EXTI + * + * Copyright (c) 2014 Alistair Francis + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "trace.h" +#include "hw/irq.h" +#include "migration/vmstate.h" +#include "hw/misc/stm32f4xx_exti.h" + +static void stm32f4xx_exti_reset(DeviceState *dev) +{ + STM32F4xxExtiState *s = STM32F4XX_EXTI(dev); + + s->exti_imr = 0x00000000; + s->exti_emr = 0x00000000; + s->exti_rtsr = 0x00000000; + s->exti_ftsr = 0x00000000; + s->exti_swier = 0x00000000; + s->exti_pr = 0x00000000; +} + +static void stm32f4xx_exti_set_irq(void *opaque, int irq, int level) +{ + STM32F4xxExtiState *s = opaque; + + trace_stm32f4xx_exti_set_irq(irq, level); + + if (((1 << irq) & s->exti_rtsr) && level) { + /* Rising Edge */ + s->exti_pr |= 1 << irq; + } + + if (((1 << irq) & s->exti_ftsr) && !level) { + /* Falling Edge */ + s->exti_pr |= 1 << irq; + } + + if (!((1 << irq) & s->exti_imr)) { + /* Interrupt is masked */ + return; + } + qemu_irq_pulse(s->irq[irq]); +} + +static uint64_t stm32f4xx_exti_read(void *opaque, hwaddr addr, + unsigned int size) +{ + STM32F4xxExtiState *s = opaque; + + trace_stm32f4xx_exti_read(addr); + + switch (addr) { + case EXTI_IMR: + return s->exti_imr; + case EXTI_EMR: + return s->exti_emr; + case EXTI_RTSR: + return s->exti_rtsr; + case EXTI_FTSR: + return s->exti_ftsr; + case EXTI_SWIER: + return s->exti_swier; + case EXTI_PR: + return s->exti_pr; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "STM32F4XX_exti_read: Bad offset %x\n", (int)addr); + return 0; + } + return 0; +} + +static void stm32f4xx_exti_write(void *opaque, hwaddr addr, + uint64_t val64, unsigned int size) +{ + STM32F4xxExtiState *s = opaque; + uint32_t value = (uint32_t) val64; + + trace_stm32f4xx_exti_write(addr, value); + + switch (addr) { + case EXTI_IMR: + s->exti_imr = value; + return; + case EXTI_EMR: + s->exti_emr = value; + return; + case EXTI_RTSR: + s->exti_rtsr = value; + return; + case EXTI_FTSR: + s->exti_ftsr = value; + return; + case EXTI_SWIER: + s->exti_swier = value; + return; + case EXTI_PR: + /* This bit is cleared by writing a 1 to it */ + s->exti_pr &= ~value; + return; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "STM32F4XX_exti_write: Bad offset %x\n", (int)addr); + } +} + +static const MemoryRegionOps stm32f4xx_exti_ops = { + .read = stm32f4xx_exti_read, + .write = stm32f4xx_exti_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static void stm32f4xx_exti_init(Object *obj) +{ + STM32F4xxExtiState *s = STM32F4XX_EXTI(obj); + int i; + + for (i = 0; i < NUM_INTERRUPT_OUT_LINES; i++) { + sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq[i]); + } + + memory_region_init_io(&s->mmio, obj, &stm32f4xx_exti_ops, s, + TYPE_STM32F4XX_EXTI, 0x400); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); + + qdev_init_gpio_in(DEVICE(obj), stm32f4xx_exti_set_irq, + NUM_GPIO_EVENT_IN_LINES); +} + +static const VMStateDescription vmstate_stm32f4xx_exti = { + .name = TYPE_STM32F4XX_EXTI, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(exti_imr, STM32F4xxExtiState), + VMSTATE_UINT32(exti_emr, STM32F4xxExtiState), + VMSTATE_UINT32(exti_rtsr, STM32F4xxExtiState), + VMSTATE_UINT32(exti_ftsr, STM32F4xxExtiState), + VMSTATE_UINT32(exti_swier, STM32F4xxExtiState), + VMSTATE_UINT32(exti_pr, STM32F4xxExtiState), + VMSTATE_END_OF_LIST() + } +}; + +static void stm32f4xx_exti_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = stm32f4xx_exti_reset; + dc->vmsd = &vmstate_stm32f4xx_exti; +} + +static const TypeInfo stm32f4xx_exti_info = { + .name = TYPE_STM32F4XX_EXTI, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(STM32F4xxExtiState), + .instance_init = stm32f4xx_exti_init, + .class_init = stm32f4xx_exti_class_init, +}; + +static void stm32f4xx_exti_register_types(void) +{ + type_register_static(&stm32f4xx_exti_info); +} + +type_init(stm32f4xx_exti_register_types) diff --git a/hw/misc/stm32f4xx_syscfg.c b/hw/misc/stm32f4xx_syscfg.c new file mode 100644 index 0000000000..f960e4ea1e --- /dev/null +++ b/hw/misc/stm32f4xx_syscfg.c @@ -0,0 +1,171 @@ +/* + * STM32F4xx SYSCFG + * + * Copyright (c) 2014 Alistair Francis + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "trace.h" +#include "hw/irq.h" +#include "migration/vmstate.h" +#include "hw/misc/stm32f4xx_syscfg.h" + +static void stm32f4xx_syscfg_reset(DeviceState *dev) +{ + STM32F4xxSyscfgState *s = STM32F4XX_SYSCFG(dev); + + s->syscfg_memrmp = 0x00000000; + s->syscfg_pmc = 0x00000000; + s->syscfg_exticr[0] = 0x00000000; + s->syscfg_exticr[1] = 0x00000000; + s->syscfg_exticr[2] = 0x00000000; + s->syscfg_exticr[3] = 0x00000000; + s->syscfg_cmpcr = 0x00000000; +} + +static void stm32f4xx_syscfg_set_irq(void *opaque, int irq, int level) +{ + STM32F4xxSyscfgState *s = opaque; + int icrreg = irq / 4; + int startbit = (irq & 3) * 4; + uint8_t config = irq / 16; + + trace_stm32f4xx_syscfg_set_irq(irq / 16, irq % 16, level); + + g_assert(icrreg < SYSCFG_NUM_EXTICR); + + if (extract32(s->syscfg_exticr[icrreg], startbit, 4) == config) { + qemu_set_irq(s->gpio_out[irq], level); + trace_stm32f4xx_pulse_exti(irq); + } +} + +static uint64_t stm32f4xx_syscfg_read(void *opaque, hwaddr addr, + unsigned int size) +{ + STM32F4xxSyscfgState *s = opaque; + + trace_stm32f4xx_syscfg_read(addr); + + switch (addr) { + case SYSCFG_MEMRMP: + return s->syscfg_memrmp; + case SYSCFG_PMC: + return s->syscfg_pmc; + case SYSCFG_EXTICR1...SYSCFG_EXTICR4: + return s->syscfg_exticr[addr / 4 - SYSCFG_EXTICR1 / 4]; + case SYSCFG_CMPCR: + return s->syscfg_cmpcr; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Bad offset 0x%"HWADDR_PRIx"\n", __func__, addr); + return 0; + } +} + +static void stm32f4xx_syscfg_write(void *opaque, hwaddr addr, + uint64_t val64, unsigned int size) +{ + STM32F4xxSyscfgState *s = opaque; + uint32_t value = val64; + + trace_stm32f4xx_syscfg_write(value, addr); + + switch (addr) { + case SYSCFG_MEMRMP: + qemu_log_mask(LOG_UNIMP, + "%s: Changing the memory mapping isn't supported " \ + "in QEMU\n", __func__); + return; + case SYSCFG_PMC: + qemu_log_mask(LOG_UNIMP, + "%s: Changing the memory mapping isn't supported " \ + "in QEMU\n", __func__); + return; + case SYSCFG_EXTICR1...SYSCFG_EXTICR4: + s->syscfg_exticr[addr / 4 - SYSCFG_EXTICR1 / 4] = (value & 0xFFFF); + return; + case SYSCFG_CMPCR: + s->syscfg_cmpcr = value; + return; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Bad offset 0x%"HWADDR_PRIx"\n", __func__, addr); + } +} + +static const MemoryRegionOps stm32f4xx_syscfg_ops = { + .read = stm32f4xx_syscfg_read, + .write = stm32f4xx_syscfg_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static void stm32f4xx_syscfg_init(Object *obj) +{ + STM32F4xxSyscfgState *s = STM32F4XX_SYSCFG(obj); + + sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq); + + memory_region_init_io(&s->mmio, obj, &stm32f4xx_syscfg_ops, s, + TYPE_STM32F4XX_SYSCFG, 0x400); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); + + qdev_init_gpio_in(DEVICE(obj), stm32f4xx_syscfg_set_irq, 16 * 9); + qdev_init_gpio_out(DEVICE(obj), s->gpio_out, 16); +} + +static const VMStateDescription vmstate_stm32f4xx_syscfg = { + .name = TYPE_STM32F4XX_SYSCFG, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(syscfg_memrmp, STM32F4xxSyscfgState), + VMSTATE_UINT32(syscfg_pmc, STM32F4xxSyscfgState), + VMSTATE_UINT32_ARRAY(syscfg_exticr, STM32F4xxSyscfgState, + SYSCFG_NUM_EXTICR), + VMSTATE_UINT32(syscfg_cmpcr, STM32F4xxSyscfgState), + VMSTATE_END_OF_LIST() + } +}; + +static void stm32f4xx_syscfg_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = stm32f4xx_syscfg_reset; + dc->vmsd = &vmstate_stm32f4xx_syscfg; +} + +static const TypeInfo stm32f4xx_syscfg_info = { + .name = TYPE_STM32F4XX_SYSCFG, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(STM32F4xxSyscfgState), + .instance_init = stm32f4xx_syscfg_init, + .class_init = stm32f4xx_syscfg_class_init, +}; + +static void stm32f4xx_syscfg_register_types(void) +{ + type_register_static(&stm32f4xx_syscfg_info); +} + +type_init(stm32f4xx_syscfg_register_types) diff --git a/hw/misc/trace-events b/hw/misc/trace-events index 1deb1d08c1..a5862b2bed 100644 --- a/hw/misc/trace-events +++ b/hw/misc/trace-events @@ -1,5 +1,24 @@ # See docs/devel/tracing.txt for syntax documentation. +# allwinner-cpucfg.c +allwinner_cpucfg_cpu_reset(uint8_t cpu_id, uint32_t reset_addr) "id %u, reset_addr 0x%" PRIu32 +allwinner_cpucfg_read(uint64_t offset, uint64_t data, unsigned size) "offset 0x%" PRIx64 " data 0x%" PRIx64 " size %" PRIu32 +allwinner_cpucfg_write(uint64_t offset, uint64_t data, unsigned size) "offset 0x%" PRIx64 " data 0x%" PRIx64 " size %" PRIu32 + +# allwinner-h3-dramc.c +allwinner_h3_dramc_rowmirror_disable(void) "Disable row mirror" +allwinner_h3_dramc_rowmirror_enable(uint64_t addr) "Enable row mirror: addr 0x%" PRIx64 +allwinner_h3_dramcom_read(uint64_t offset, uint64_t data, unsigned size) "Read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %" PRIu32 +allwinner_h3_dramcom_write(uint64_t offset, uint64_t data, unsigned size) "Write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %" PRIu32 +allwinner_h3_dramctl_read(uint64_t offset, uint64_t data, unsigned size) "Read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %" PRIu32 +allwinner_h3_dramctl_write(uint64_t offset, uint64_t data, unsigned size) "Write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %" PRIu32 +allwinner_h3_dramphy_read(uint64_t offset, uint64_t data, unsigned size) "Read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %" PRIu32 +allwinner_h3_dramphy_write(uint64_t offset, uint64_t data, unsigned size) "write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %" PRIu32 + +# allwinner-sid.c +allwinner_sid_read(uint64_t offset, uint64_t data, unsigned size) "offset 0x%" PRIx64 " data 0x%" PRIx64 " size %" PRIu32 +allwinner_sid_write(uint64_t offset, uint64_t data, unsigned size) "offset 0x%" PRIx64 " data 0x%" PRIx64 " size %" PRIu32 + # eccmemctl.c ecc_mem_writel_mer(uint32_t val) "Write memory enable 0x%08x" ecc_mem_writel_mdr(uint32_t val) "Write memory delay 0x%08x" @@ -84,6 +103,17 @@ mos6522_set_sr_int(void) "set sr_int" mos6522_write(uint64_t addr, uint64_t val) "reg=0x%"PRIx64 " val=0x%"PRIx64 mos6522_read(uint64_t addr, unsigned val) "reg=0x%"PRIx64 " val=0x%x" +# stm32f4xx_syscfg +stm32f4xx_syscfg_set_irq(int gpio, int line, int level) "Interupt: GPIO: %d, Line: %d; Level: %d" +stm32f4xx_pulse_exti(int irq) "Pulse EXTI: %d" +stm32f4xx_syscfg_read(uint64_t addr) "reg read: addr: 0x%" PRIx64 " " +stm32f4xx_syscfg_write(uint64_t addr, uint64_t data) "reg write: addr: 0x%" PRIx64 " val: 0x%" PRIx64 "" + +# stm32f4xx_exti +stm32f4xx_exti_set_irq(int irq, int leve) "Set EXTI: %d to %d" +stm32f4xx_exti_read(uint64_t addr) "reg read: addr: 0x%" PRIx64 " " +stm32f4xx_exti_write(uint64_t addr, uint64_t data) "reg write: addr: 0x%" PRIx64 " val: 0x%" PRIx64 "" + # tz-mpc.c tz_mpc_reg_read(uint32_t offset, uint64_t data, unsigned size) "TZ MPC regs read: offset 0x%x data 0x%" PRIx64 " size %u" tz_mpc_reg_write(uint32_t offset, uint64_t data, unsigned size) "TZ MPC regs write: offset 0x%x data 0x%" PRIx64 " size %u" @@ -149,3 +179,22 @@ bcm2835_mbox_write(unsigned int size, uint64_t addr, uint64_t value) "mbox write bcm2835_mbox_read(unsigned int size, uint64_t addr, uint64_t value) "mbox read sz:%u addr:0x%"PRIx64" data:0x%"PRIx64 bcm2835_mbox_irq(unsigned level) "mbox irq:ARM level:%u" bcm2835_mbox_property(uint32_t tag, uint32_t bufsize, size_t resplen) "mbox property tag:0x%08x in_sz:%u out_sz:%zu" + +# mac_via.c +via1_rtc_update_data_out(int count, int value) "count=%d value=0x%02x" +via1_rtc_update_data_in(int count, int value) "count=%d value=0x%02x" +via1_rtc_internal_status(int cmd, int alt, int value) "cmd=0x%02x alt=0x%02x value=0x%02x" +via1_rtc_internal_cmd(int cmd) "cmd=0x%02x" +via1_rtc_cmd_invalid(int value) "value=0x%02x" +via1_rtc_internal_time(uint32_t time) "time=0x%08x" +via1_rtc_internal_set_cmd(int cmd) "cmd=0x%02x" +via1_rtc_internal_ignore_cmd(int cmd) "cmd=0x%02x" +via1_rtc_internal_set_alt(int alt, int sector, int offset) "alt=0x%02x sector=%u offset=%u" +via1_rtc_cmd_seconds_read(int reg, int value) "reg=%d value=0x%02x" +via1_rtc_cmd_seconds_write(int reg, int value) "reg=%d value=0x%02x" +via1_rtc_cmd_test_write(int value) "value=0x%02x" +via1_rtc_cmd_wprotect_write(int value) "value=0x%02x" +via1_rtc_cmd_pram_read(int addr, int value) "addr=%u value=0x%02x" +via1_rtc_cmd_pram_write(int addr, int value) "addr=%u value=0x%02x" +via1_rtc_cmd_pram_sect_read(int sector, int offset, int addr, int value) "sector=%u offset=%u addr=%d value=0x%02x" +via1_rtc_cmd_pram_sect_write(int sector, int offset, int addr, int value) "sector=%u offset=%u addr=%d value=0x%02x" diff --git a/hw/misc/tz-mpc.c b/hw/misc/tz-mpc.c index 49dd6050bd..98f151237f 100644 --- a/hw/misc/tz-mpc.c +++ b/hw/misc/tz-mpc.c @@ -596,7 +596,7 @@ static void tz_mpc_class_init(ObjectClass *klass, void *data) dc->realize = tz_mpc_realize; dc->vmsd = &tz_mpc_vmstate; dc->reset = tz_mpc_reset; - dc->props = tz_mpc_properties; + device_class_set_props(dc, tz_mpc_properties); } static const TypeInfo tz_mpc_info = { diff --git a/hw/misc/tz-msc.c b/hw/misc/tz-msc.c index 7d522ac0ec..acbe94400b 100644 --- a/hw/misc/tz-msc.c +++ b/hw/misc/tz-msc.c @@ -293,7 +293,7 @@ static void tz_msc_class_init(ObjectClass *klass, void *data) dc->realize = tz_msc_realize; dc->vmsd = &tz_msc_vmstate; dc->reset = tz_msc_reset; - dc->props = tz_msc_properties; + device_class_set_props(dc, tz_msc_properties); } static const TypeInfo tz_msc_info = { diff --git a/hw/misc/tz-ppc.c b/hw/misc/tz-ppc.c index 181a5f1e81..6431257b52 100644 --- a/hw/misc/tz-ppc.c +++ b/hw/misc/tz-ppc.c @@ -319,7 +319,7 @@ static void tz_ppc_class_init(ObjectClass *klass, void *data) dc->realize = tz_ppc_realize; dc->vmsd = &tz_ppc_vmstate; dc->reset = tz_ppc_reset; - dc->props = tz_ppc_properties; + device_class_set_props(dc, tz_ppc_properties); } static const TypeInfo tz_ppc_info = { diff --git a/hw/misc/unimp.c b/hw/misc/unimp.c index 0e1cb24629..bc4084d344 100644 --- a/hw/misc/unimp.c +++ b/hw/misc/unimp.c @@ -79,7 +79,7 @@ static void unimp_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = unimp_realize; - dc->props = unimp_properties; + device_class_set_props(dc, unimp_properties); } static const TypeInfo unimp_info = { diff --git a/hw/moxie/moxiesim.c b/hw/moxie/moxiesim.c index 57af1b4891..51a98287b5 100644 --- a/hw/moxie/moxiesim.c +++ b/hw/moxie/moxiesim.c @@ -58,7 +58,7 @@ static void load_kernel(MoxieCPU *cpu, LoaderParams *loader_params) ram_addr_t initrd_offset; kernel_size = load_elf(loader_params->kernel_filename, NULL, NULL, NULL, - &entry, &kernel_low, &kernel_high, 1, EM_MOXIE, + &entry, &kernel_low, &kernel_high, NULL, 1, EM_MOXIE, 0, 0); if (kernel_size <= 0) { @@ -150,7 +150,7 @@ static void moxiesim_machine_init(MachineClass *mc) { mc->desc = "Moxie simulator platform"; mc->init = moxiesim_init; - mc->is_default = 1; + mc->is_default = true; mc->default_cpu_type = MOXIE_CPU_TYPE_NAME("MoxieLite"); } diff --git a/hw/net/Kconfig b/hw/net/Kconfig index 3856417d42..e43c96dae0 100644 --- a/hw/net/Kconfig +++ b/hw/net/Kconfig @@ -14,6 +14,7 @@ config EEPRO100_PCI bool default y if PCI_DEVICES depends on PCI + select NMC93XX_EEPROM config PCNET_PCI bool @@ -28,6 +29,10 @@ config TULIP bool default y if PCI_DEVICES depends on PCI + select NMC93XX_EEPROM + +config I82596_COMMON + bool config E1000_PCI bool @@ -74,6 +79,9 @@ config MIPSNET config ALLWINNER_EMAC bool +config ALLWINNER_SUN8I_EMAC + bool + config IMX_FEC bool @@ -87,6 +95,10 @@ config LANCE bool select PCNET_COMMON +config LASI_82596 + bool + select I82596_COMMON + config SUNHME bool diff --git a/hw/net/Makefile.objs b/hw/net/Makefile.objs index 7907d2c199..af4d194866 100644 --- a/hw/net/Makefile.objs +++ b/hw/net/Makefile.objs @@ -23,17 +23,20 @@ common-obj-$(CONFIG_XGMAC) += xgmac.o common-obj-$(CONFIG_MIPSNET) += mipsnet.o common-obj-$(CONFIG_XILINX_AXI) += xilinx_axienet.o common-obj-$(CONFIG_ALLWINNER_EMAC) += allwinner_emac.o +common-obj-$(CONFIG_ALLWINNER_SUN8I_EMAC) += allwinner-sun8i-emac.o common-obj-$(CONFIG_IMX_FEC) += imx_fec.o common-obj-$(CONFIG_CADENCE) += cadence_gem.o common-obj-$(CONFIG_STELLARIS_ENET) += stellaris_enet.o common-obj-$(CONFIG_LANCE) += lance.o +common-obj-$(CONFIG_LASI_82596) += lasi_i82596.o +common-obj-$(CONFIG_I82596_COMMON) += i82596.o common-obj-$(CONFIG_SUNHME) += sunhme.o common-obj-$(CONFIG_FTGMAC100) += ftgmac100.o common-obj-$(CONFIG_SUNGEM) += sungem.o -obj-$(CONFIG_ETRAXFS) += etraxfs_eth.o -obj-$(CONFIG_COLDFIRE) += mcf_fec.o +common-obj-$(CONFIG_ETRAXFS) += etraxfs_eth.o +common-obj-$(CONFIG_COLDFIRE) += mcf_fec.o obj-$(CONFIG_MILKYMIST) += milkymist-minimac2.o obj-$(CONFIG_PSERIES) += spapr_llan.o obj-$(CONFIG_XILINX_ETHLITE) += xilinx_ethlite.o @@ -43,7 +46,7 @@ common-obj-$(call land,$(CONFIG_VIRTIO_NET),$(CONFIG_VHOST_NET)) += vhost_net.o common-obj-$(call lnot,$(call land,$(CONFIG_VIRTIO_NET),$(CONFIG_VHOST_NET))) += vhost_net-stub.o common-obj-$(CONFIG_ALL) += vhost_net-stub.o -obj-$(CONFIG_ETSEC) += fsl_etsec/etsec.o fsl_etsec/registers.o \ +common-obj-$(CONFIG_ETSEC) += fsl_etsec/etsec.o fsl_etsec/registers.o \ fsl_etsec/rings.o fsl_etsec/miim.o common-obj-$(CONFIG_ROCKER) += rocker/rocker.o rocker/rocker_fp.o \ diff --git a/hw/net/allwinner-sun8i-emac.c b/hw/net/allwinner-sun8i-emac.c new file mode 100644 index 0000000000..28637ff4c1 --- /dev/null +++ b/hw/net/allwinner-sun8i-emac.c @@ -0,0 +1,867 @@ +/* + * Allwinner Sun8i Ethernet MAC emulation + * + * Copyright (C) 2019 Niek Linnenbank + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "hw/sysbus.h" +#include "migration/vmstate.h" +#include "net/net.h" +#include "hw/irq.h" +#include "hw/qdev-properties.h" +#include "qemu/log.h" +#include "trace.h" +#include "net/checksum.h" +#include "qemu/module.h" +#include "exec/cpu-common.h" +#include "hw/net/allwinner-sun8i-emac.h" + +/* EMAC register offsets */ +enum { + REG_BASIC_CTL_0 = 0x0000, /* Basic Control 0 */ + REG_BASIC_CTL_1 = 0x0004, /* Basic Control 1 */ + REG_INT_STA = 0x0008, /* Interrupt Status */ + REG_INT_EN = 0x000C, /* Interrupt Enable */ + REG_TX_CTL_0 = 0x0010, /* Transmit Control 0 */ + REG_TX_CTL_1 = 0x0014, /* Transmit Control 1 */ + REG_TX_FLOW_CTL = 0x001C, /* Transmit Flow Control */ + REG_TX_DMA_DESC_LIST = 0x0020, /* Transmit Descriptor List Address */ + REG_RX_CTL_0 = 0x0024, /* Receive Control 0 */ + REG_RX_CTL_1 = 0x0028, /* Receive Control 1 */ + REG_RX_DMA_DESC_LIST = 0x0034, /* Receive Descriptor List Address */ + REG_FRM_FLT = 0x0038, /* Receive Frame Filter */ + REG_RX_HASH_0 = 0x0040, /* Receive Hash Table 0 */ + REG_RX_HASH_1 = 0x0044, /* Receive Hash Table 1 */ + REG_MII_CMD = 0x0048, /* Management Interface Command */ + REG_MII_DATA = 0x004C, /* Management Interface Data */ + REG_ADDR_HIGH = 0x0050, /* MAC Address High */ + REG_ADDR_LOW = 0x0054, /* MAC Address Low */ + REG_TX_DMA_STA = 0x00B0, /* Transmit DMA Status */ + REG_TX_CUR_DESC = 0x00B4, /* Transmit Current Descriptor */ + REG_TX_CUR_BUF = 0x00B8, /* Transmit Current Buffer */ + REG_RX_DMA_STA = 0x00C0, /* Receive DMA Status */ + REG_RX_CUR_DESC = 0x00C4, /* Receive Current Descriptor */ + REG_RX_CUR_BUF = 0x00C8, /* Receive Current Buffer */ + REG_RGMII_STA = 0x00D0, /* RGMII Status */ +}; + +/* EMAC register flags */ +enum { + BASIC_CTL0_100Mbps = (0b11 << 2), + BASIC_CTL0_FD = (1 << 0), + BASIC_CTL1_SOFTRST = (1 << 0), +}; + +enum { + INT_STA_RGMII_LINK = (1 << 16), + INT_STA_RX_EARLY = (1 << 13), + INT_STA_RX_OVERFLOW = (1 << 12), + INT_STA_RX_TIMEOUT = (1 << 11), + INT_STA_RX_DMA_STOP = (1 << 10), + INT_STA_RX_BUF_UA = (1 << 9), + INT_STA_RX = (1 << 8), + INT_STA_TX_EARLY = (1 << 5), + INT_STA_TX_UNDERFLOW = (1 << 4), + INT_STA_TX_TIMEOUT = (1 << 3), + INT_STA_TX_BUF_UA = (1 << 2), + INT_STA_TX_DMA_STOP = (1 << 1), + INT_STA_TX = (1 << 0), +}; + +enum { + INT_EN_RX_EARLY = (1 << 13), + INT_EN_RX_OVERFLOW = (1 << 12), + INT_EN_RX_TIMEOUT = (1 << 11), + INT_EN_RX_DMA_STOP = (1 << 10), + INT_EN_RX_BUF_UA = (1 << 9), + INT_EN_RX = (1 << 8), + INT_EN_TX_EARLY = (1 << 5), + INT_EN_TX_UNDERFLOW = (1 << 4), + INT_EN_TX_TIMEOUT = (1 << 3), + INT_EN_TX_BUF_UA = (1 << 2), + INT_EN_TX_DMA_STOP = (1 << 1), + INT_EN_TX = (1 << 0), +}; + +enum { + TX_CTL0_TX_EN = (1 << 31), + TX_CTL1_TX_DMA_START = (1 << 31), + TX_CTL1_TX_DMA_EN = (1 << 30), + TX_CTL1_TX_FLUSH = (1 << 0), +}; + +enum { + RX_CTL0_RX_EN = (1 << 31), + RX_CTL0_STRIP_FCS = (1 << 28), + RX_CTL0_CRC_IPV4 = (1 << 27), +}; + +enum { + RX_CTL1_RX_DMA_START = (1 << 31), + RX_CTL1_RX_DMA_EN = (1 << 30), + RX_CTL1_RX_MD = (1 << 1), +}; + +enum { + RX_FRM_FLT_DIS_ADDR = (1 << 31), +}; + +enum { + MII_CMD_PHY_ADDR_SHIFT = (12), + MII_CMD_PHY_ADDR_MASK = (0xf000), + MII_CMD_PHY_REG_SHIFT = (4), + MII_CMD_PHY_REG_MASK = (0xf0), + MII_CMD_PHY_RW = (1 << 1), + MII_CMD_PHY_BUSY = (1 << 0), +}; + +enum { + TX_DMA_STA_STOP = (0b000), + TX_DMA_STA_RUN_FETCH = (0b001), + TX_DMA_STA_WAIT_STA = (0b010), +}; + +enum { + RX_DMA_STA_STOP = (0b000), + RX_DMA_STA_RUN_FETCH = (0b001), + RX_DMA_STA_WAIT_FRM = (0b011), +}; + +/* EMAC register reset values */ +enum { + REG_BASIC_CTL_1_RST = 0x08000000, +}; + +/* EMAC constants */ +enum { + AW_SUN8I_EMAC_MIN_PKT_SZ = 64 +}; + +/* Transmit/receive frame descriptor */ +typedef struct FrameDescriptor { + uint32_t status; + uint32_t status2; + uint32_t addr; + uint32_t next; +} FrameDescriptor; + +/* Frame descriptor flags */ +enum { + DESC_STATUS_CTL = (1 << 31), + DESC_STATUS2_BUF_SIZE_MASK = (0x7ff), +}; + +/* Transmit frame descriptor flags */ +enum { + TX_DESC_STATUS_LENGTH_ERR = (1 << 14), + TX_DESC_STATUS2_FIRST_DESC = (1 << 29), + TX_DESC_STATUS2_LAST_DESC = (1 << 30), + TX_DESC_STATUS2_CHECKSUM_MASK = (0x3 << 27), +}; + +/* Receive frame descriptor flags */ +enum { + RX_DESC_STATUS_FIRST_DESC = (1 << 9), + RX_DESC_STATUS_LAST_DESC = (1 << 8), + RX_DESC_STATUS_FRM_LEN_MASK = (0x3fff0000), + RX_DESC_STATUS_FRM_LEN_SHIFT = (16), + RX_DESC_STATUS_NO_BUF = (1 << 14), + RX_DESC_STATUS_HEADER_ERR = (1 << 7), + RX_DESC_STATUS_LENGTH_ERR = (1 << 4), + RX_DESC_STATUS_CRC_ERR = (1 << 1), + RX_DESC_STATUS_PAYLOAD_ERR = (1 << 0), + RX_DESC_STATUS2_RX_INT_CTL = (1 << 31), +}; + +/* MII register offsets */ +enum { + MII_REG_CR = (0x0), /* Control */ + MII_REG_ST = (0x1), /* Status */ + MII_REG_ID_HIGH = (0x2), /* Identifier High */ + MII_REG_ID_LOW = (0x3), /* Identifier Low */ + MII_REG_ADV = (0x4), /* Advertised abilities */ + MII_REG_LPA = (0x5), /* Link partner abilities */ +}; + +/* MII register flags */ +enum { + MII_REG_CR_RESET = (1 << 15), + MII_REG_CR_POWERDOWN = (1 << 11), + MII_REG_CR_10Mbit = (0), + MII_REG_CR_100Mbit = (1 << 13), + MII_REG_CR_1000Mbit = (1 << 6), + MII_REG_CR_AUTO_NEG = (1 << 12), + MII_REG_CR_AUTO_NEG_RESTART = (1 << 9), + MII_REG_CR_FULLDUPLEX = (1 << 8), +}; + +enum { + MII_REG_ST_100BASE_T4 = (1 << 15), + MII_REG_ST_100BASE_X_FD = (1 << 14), + MII_REG_ST_100BASE_X_HD = (1 << 13), + MII_REG_ST_10_FD = (1 << 12), + MII_REG_ST_10_HD = (1 << 11), + MII_REG_ST_100BASE_T2_FD = (1 << 10), + MII_REG_ST_100BASE_T2_HD = (1 << 9), + MII_REG_ST_AUTONEG_COMPLETE = (1 << 5), + MII_REG_ST_AUTONEG_AVAIL = (1 << 3), + MII_REG_ST_LINK_UP = (1 << 2), +}; + +enum { + MII_REG_LPA_10_HD = (1 << 5), + MII_REG_LPA_10_FD = (1 << 6), + MII_REG_LPA_100_HD = (1 << 7), + MII_REG_LPA_100_FD = (1 << 8), + MII_REG_LPA_PAUSE = (1 << 10), + MII_REG_LPA_ASYMPAUSE = (1 << 11), +}; + +/* MII constants */ +enum { + MII_PHY_ID_HIGH = 0x0044, + MII_PHY_ID_LOW = 0x1400, +}; + +static void allwinner_sun8i_emac_mii_set_link(AwSun8iEmacState *s, + bool link_active) +{ + if (link_active) { + s->mii_st |= MII_REG_ST_LINK_UP; + } else { + s->mii_st &= ~MII_REG_ST_LINK_UP; + } +} + +static void allwinner_sun8i_emac_mii_reset(AwSun8iEmacState *s, + bool link_active) +{ + s->mii_cr = MII_REG_CR_100Mbit | MII_REG_CR_AUTO_NEG | + MII_REG_CR_FULLDUPLEX; + s->mii_st = MII_REG_ST_100BASE_T4 | MII_REG_ST_100BASE_X_FD | + MII_REG_ST_100BASE_X_HD | MII_REG_ST_10_FD | MII_REG_ST_10_HD | + MII_REG_ST_100BASE_T2_FD | MII_REG_ST_100BASE_T2_HD | + MII_REG_ST_AUTONEG_COMPLETE | MII_REG_ST_AUTONEG_AVAIL; + s->mii_adv = 0; + + allwinner_sun8i_emac_mii_set_link(s, link_active); +} + +static void allwinner_sun8i_emac_mii_cmd(AwSun8iEmacState *s) +{ + uint8_t addr, reg; + + addr = (s->mii_cmd & MII_CMD_PHY_ADDR_MASK) >> MII_CMD_PHY_ADDR_SHIFT; + reg = (s->mii_cmd & MII_CMD_PHY_REG_MASK) >> MII_CMD_PHY_REG_SHIFT; + + if (addr != s->mii_phy_addr) { + return; + } + + /* Read or write a PHY register? */ + if (s->mii_cmd & MII_CMD_PHY_RW) { + trace_allwinner_sun8i_emac_mii_write_reg(reg, s->mii_data); + + switch (reg) { + case MII_REG_CR: + if (s->mii_data & MII_REG_CR_RESET) { + allwinner_sun8i_emac_mii_reset(s, s->mii_st & + MII_REG_ST_LINK_UP); + } else { + s->mii_cr = s->mii_data & ~(MII_REG_CR_RESET | + MII_REG_CR_AUTO_NEG_RESTART); + } + break; + case MII_REG_ADV: + s->mii_adv = s->mii_data; + break; + case MII_REG_ID_HIGH: + case MII_REG_ID_LOW: + case MII_REG_LPA: + break; + default: + qemu_log_mask(LOG_UNIMP, "allwinner-h3-emac: write access to " + "unknown MII register 0x%x\n", reg); + break; + } + } else { + switch (reg) { + case MII_REG_CR: + s->mii_data = s->mii_cr; + break; + case MII_REG_ST: + s->mii_data = s->mii_st; + break; + case MII_REG_ID_HIGH: + s->mii_data = MII_PHY_ID_HIGH; + break; + case MII_REG_ID_LOW: + s->mii_data = MII_PHY_ID_LOW; + break; + case MII_REG_ADV: + s->mii_data = s->mii_adv; + break; + case MII_REG_LPA: + s->mii_data = MII_REG_LPA_10_HD | MII_REG_LPA_10_FD | + MII_REG_LPA_100_HD | MII_REG_LPA_100_FD | + MII_REG_LPA_PAUSE | MII_REG_LPA_ASYMPAUSE; + break; + default: + qemu_log_mask(LOG_UNIMP, "allwinner-h3-emac: read access to " + "unknown MII register 0x%x\n", reg); + s->mii_data = 0; + break; + } + + trace_allwinner_sun8i_emac_mii_read_reg(reg, s->mii_data); + } +} + +static void allwinner_sun8i_emac_update_irq(AwSun8iEmacState *s) +{ + qemu_set_irq(s->irq, (s->int_sta & s->int_en) != 0); +} + +static uint32_t allwinner_sun8i_emac_next_desc(FrameDescriptor *desc, + size_t min_size) +{ + uint32_t paddr = desc->next; + + cpu_physical_memory_read(paddr, desc, sizeof(*desc)); + + if ((desc->status & DESC_STATUS_CTL) && + (desc->status2 & DESC_STATUS2_BUF_SIZE_MASK) >= min_size) { + return paddr; + } else { + return 0; + } +} + +static uint32_t allwinner_sun8i_emac_get_desc(FrameDescriptor *desc, + uint32_t start_addr, + size_t min_size) +{ + uint32_t desc_addr = start_addr; + + /* Note that the list is a cycle. Last entry points back to the head. */ + while (desc_addr != 0) { + cpu_physical_memory_read(desc_addr, desc, sizeof(*desc)); + + if ((desc->status & DESC_STATUS_CTL) && + (desc->status2 & DESC_STATUS2_BUF_SIZE_MASK) >= min_size) { + return desc_addr; + } else if (desc->next == start_addr) { + break; + } else { + desc_addr = desc->next; + } + } + + return 0; +} + +static uint32_t allwinner_sun8i_emac_rx_desc(AwSun8iEmacState *s, + FrameDescriptor *desc, + size_t min_size) +{ + return allwinner_sun8i_emac_get_desc(desc, s->rx_desc_curr, min_size); +} + +static uint32_t allwinner_sun8i_emac_tx_desc(AwSun8iEmacState *s, + FrameDescriptor *desc, + size_t min_size) +{ + return allwinner_sun8i_emac_get_desc(desc, s->tx_desc_head, min_size); +} + +static void allwinner_sun8i_emac_flush_desc(FrameDescriptor *desc, + uint32_t phys_addr) +{ + cpu_physical_memory_write(phys_addr, desc, sizeof(*desc)); +} + +static bool allwinner_sun8i_emac_can_receive(NetClientState *nc) +{ + AwSun8iEmacState *s = qemu_get_nic_opaque(nc); + FrameDescriptor desc; + + return (s->rx_ctl0 & RX_CTL0_RX_EN) && + (allwinner_sun8i_emac_rx_desc(s, &desc, 0) != 0); +} + +static ssize_t allwinner_sun8i_emac_receive(NetClientState *nc, + const uint8_t *buf, + size_t size) +{ + AwSun8iEmacState *s = qemu_get_nic_opaque(nc); + FrameDescriptor desc; + size_t bytes_left = size; + size_t desc_bytes = 0; + size_t pad_fcs_size = 4; + size_t padding = 0; + + if (!(s->rx_ctl0 & RX_CTL0_RX_EN)) { + return -1; + } + + s->rx_desc_curr = allwinner_sun8i_emac_rx_desc(s, &desc, + AW_SUN8I_EMAC_MIN_PKT_SZ); + if (!s->rx_desc_curr) { + s->int_sta |= INT_STA_RX_BUF_UA; + } + + /* Keep filling RX descriptors until the whole frame is written */ + while (s->rx_desc_curr && bytes_left > 0) { + desc.status &= ~DESC_STATUS_CTL; + desc.status &= ~RX_DESC_STATUS_FRM_LEN_MASK; + + if (bytes_left == size) { + desc.status |= RX_DESC_STATUS_FIRST_DESC; + } + + if ((desc.status2 & DESC_STATUS2_BUF_SIZE_MASK) < + (bytes_left + pad_fcs_size)) { + desc_bytes = desc.status2 & DESC_STATUS2_BUF_SIZE_MASK; + desc.status |= desc_bytes << RX_DESC_STATUS_FRM_LEN_SHIFT; + } else { + padding = pad_fcs_size; + if (bytes_left < AW_SUN8I_EMAC_MIN_PKT_SZ) { + padding += (AW_SUN8I_EMAC_MIN_PKT_SZ - bytes_left); + } + + desc_bytes = (bytes_left); + desc.status |= RX_DESC_STATUS_LAST_DESC; + desc.status |= (bytes_left + padding) + << RX_DESC_STATUS_FRM_LEN_SHIFT; + } + + cpu_physical_memory_write(desc.addr, buf, desc_bytes); + allwinner_sun8i_emac_flush_desc(&desc, s->rx_desc_curr); + trace_allwinner_sun8i_emac_receive(s->rx_desc_curr, desc.addr, + desc_bytes); + + /* Check if frame needs to raise the receive interrupt */ + if (!(desc.status2 & RX_DESC_STATUS2_RX_INT_CTL)) { + s->int_sta |= INT_STA_RX; + } + + /* Increment variables */ + buf += desc_bytes; + bytes_left -= desc_bytes; + + /* Move to the next descriptor */ + s->rx_desc_curr = allwinner_sun8i_emac_next_desc(&desc, 64); + if (!s->rx_desc_curr) { + /* Not enough buffer space available */ + s->int_sta |= INT_STA_RX_BUF_UA; + s->rx_desc_curr = s->rx_desc_head; + break; + } + } + + /* Report receive DMA is finished */ + s->rx_ctl1 &= ~RX_CTL1_RX_DMA_START; + allwinner_sun8i_emac_update_irq(s); + + return size; +} + +static void allwinner_sun8i_emac_transmit(AwSun8iEmacState *s) +{ + NetClientState *nc = qemu_get_queue(s->nic); + FrameDescriptor desc; + size_t bytes = 0; + size_t packet_bytes = 0; + size_t transmitted = 0; + static uint8_t packet_buf[2048]; + + s->tx_desc_curr = allwinner_sun8i_emac_tx_desc(s, &desc, 0); + + /* Read all transmit descriptors */ + while (s->tx_desc_curr != 0) { + + /* Read from physical memory into packet buffer */ + bytes = desc.status2 & DESC_STATUS2_BUF_SIZE_MASK; + if (bytes + packet_bytes > sizeof(packet_buf)) { + desc.status |= TX_DESC_STATUS_LENGTH_ERR; + break; + } + cpu_physical_memory_read(desc.addr, packet_buf + packet_bytes, bytes); + packet_bytes += bytes; + desc.status &= ~DESC_STATUS_CTL; + allwinner_sun8i_emac_flush_desc(&desc, s->tx_desc_curr); + + /* After the last descriptor, send the packet */ + if (desc.status2 & TX_DESC_STATUS2_LAST_DESC) { + if (desc.status2 & TX_DESC_STATUS2_CHECKSUM_MASK) { + net_checksum_calculate(packet_buf, packet_bytes); + } + + qemu_send_packet(nc, packet_buf, packet_bytes); + trace_allwinner_sun8i_emac_transmit(s->tx_desc_curr, desc.addr, + bytes); + + packet_bytes = 0; + transmitted++; + } + s->tx_desc_curr = allwinner_sun8i_emac_next_desc(&desc, 0); + } + + /* Raise transmit completed interrupt */ + if (transmitted > 0) { + s->int_sta |= INT_STA_TX; + s->tx_ctl1 &= ~TX_CTL1_TX_DMA_START; + allwinner_sun8i_emac_update_irq(s); + } +} + +static void allwinner_sun8i_emac_reset(DeviceState *dev) +{ + AwSun8iEmacState *s = AW_SUN8I_EMAC(dev); + NetClientState *nc = qemu_get_queue(s->nic); + + trace_allwinner_sun8i_emac_reset(); + + s->mii_cmd = 0; + s->mii_data = 0; + s->basic_ctl0 = 0; + s->basic_ctl1 = REG_BASIC_CTL_1_RST; + s->int_en = 0; + s->int_sta = 0; + s->frm_flt = 0; + s->rx_ctl0 = 0; + s->rx_ctl1 = RX_CTL1_RX_MD; + s->rx_desc_head = 0; + s->rx_desc_curr = 0; + s->tx_ctl0 = 0; + s->tx_ctl1 = 0; + s->tx_desc_head = 0; + s->tx_desc_curr = 0; + s->tx_flowctl = 0; + + allwinner_sun8i_emac_mii_reset(s, !nc->link_down); +} + +static uint64_t allwinner_sun8i_emac_read(void *opaque, hwaddr offset, + unsigned size) +{ + AwSun8iEmacState *s = AW_SUN8I_EMAC(opaque); + uint64_t value = 0; + FrameDescriptor desc; + + switch (offset) { + case REG_BASIC_CTL_0: /* Basic Control 0 */ + value = s->basic_ctl0; + break; + case REG_BASIC_CTL_1: /* Basic Control 1 */ + value = s->basic_ctl1; + break; + case REG_INT_STA: /* Interrupt Status */ + value = s->int_sta; + break; + case REG_INT_EN: /* Interupt Enable */ + value = s->int_en; + break; + case REG_TX_CTL_0: /* Transmit Control 0 */ + value = s->tx_ctl0; + break; + case REG_TX_CTL_1: /* Transmit Control 1 */ + value = s->tx_ctl1; + break; + case REG_TX_FLOW_CTL: /* Transmit Flow Control */ + value = s->tx_flowctl; + break; + case REG_TX_DMA_DESC_LIST: /* Transmit Descriptor List Address */ + value = s->tx_desc_head; + break; + case REG_RX_CTL_0: /* Receive Control 0 */ + value = s->rx_ctl0; + break; + case REG_RX_CTL_1: /* Receive Control 1 */ + value = s->rx_ctl1; + break; + case REG_RX_DMA_DESC_LIST: /* Receive Descriptor List Address */ + value = s->rx_desc_head; + break; + case REG_FRM_FLT: /* Receive Frame Filter */ + value = s->frm_flt; + break; + case REG_RX_HASH_0: /* Receive Hash Table 0 */ + case REG_RX_HASH_1: /* Receive Hash Table 1 */ + break; + case REG_MII_CMD: /* Management Interface Command */ + value = s->mii_cmd; + break; + case REG_MII_DATA: /* Management Interface Data */ + value = s->mii_data; + break; + case REG_ADDR_HIGH: /* MAC Address High */ + value = lduw_le_p(s->conf.macaddr.a + 4); + break; + case REG_ADDR_LOW: /* MAC Address Low */ + value = ldl_le_p(s->conf.macaddr.a); + break; + case REG_TX_DMA_STA: /* Transmit DMA Status */ + break; + case REG_TX_CUR_DESC: /* Transmit Current Descriptor */ + value = s->tx_desc_curr; + break; + case REG_TX_CUR_BUF: /* Transmit Current Buffer */ + if (s->tx_desc_curr != 0) { + cpu_physical_memory_read(s->tx_desc_curr, &desc, sizeof(desc)); + value = desc.addr; + } else { + value = 0; + } + break; + case REG_RX_DMA_STA: /* Receive DMA Status */ + break; + case REG_RX_CUR_DESC: /* Receive Current Descriptor */ + value = s->rx_desc_curr; + break; + case REG_RX_CUR_BUF: /* Receive Current Buffer */ + if (s->rx_desc_curr != 0) { + cpu_physical_memory_read(s->rx_desc_curr, &desc, sizeof(desc)); + value = desc.addr; + } else { + value = 0; + } + break; + case REG_RGMII_STA: /* RGMII Status */ + break; + default: + qemu_log_mask(LOG_UNIMP, "allwinner-h3-emac: read access to unknown " + "EMAC register 0x" TARGET_FMT_plx "\n", + offset); + } + + trace_allwinner_sun8i_emac_read(offset, value); + return value; +} + +static void allwinner_sun8i_emac_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + AwSun8iEmacState *s = AW_SUN8I_EMAC(opaque); + NetClientState *nc = qemu_get_queue(s->nic); + + trace_allwinner_sun8i_emac_write(offset, value); + + switch (offset) { + case REG_BASIC_CTL_0: /* Basic Control 0 */ + s->basic_ctl0 = value; + break; + case REG_BASIC_CTL_1: /* Basic Control 1 */ + if (value & BASIC_CTL1_SOFTRST) { + allwinner_sun8i_emac_reset(DEVICE(s)); + value &= ~BASIC_CTL1_SOFTRST; + } + s->basic_ctl1 = value; + if (allwinner_sun8i_emac_can_receive(nc)) { + qemu_flush_queued_packets(nc); + } + break; + case REG_INT_STA: /* Interrupt Status */ + s->int_sta &= ~value; + allwinner_sun8i_emac_update_irq(s); + break; + case REG_INT_EN: /* Interrupt Enable */ + s->int_en = value; + allwinner_sun8i_emac_update_irq(s); + break; + case REG_TX_CTL_0: /* Transmit Control 0 */ + s->tx_ctl0 = value; + break; + case REG_TX_CTL_1: /* Transmit Control 1 */ + s->tx_ctl1 = value; + if (value & TX_CTL1_TX_DMA_EN) { + allwinner_sun8i_emac_transmit(s); + } + break; + case REG_TX_FLOW_CTL: /* Transmit Flow Control */ + s->tx_flowctl = value; + break; + case REG_TX_DMA_DESC_LIST: /* Transmit Descriptor List Address */ + s->tx_desc_head = value; + s->tx_desc_curr = value; + break; + case REG_RX_CTL_0: /* Receive Control 0 */ + s->rx_ctl0 = value; + break; + case REG_RX_CTL_1: /* Receive Control 1 */ + s->rx_ctl1 = value | RX_CTL1_RX_MD; + if ((value & RX_CTL1_RX_DMA_EN) && + allwinner_sun8i_emac_can_receive(nc)) { + qemu_flush_queued_packets(nc); + } + break; + case REG_RX_DMA_DESC_LIST: /* Receive Descriptor List Address */ + s->rx_desc_head = value; + s->rx_desc_curr = value; + break; + case REG_FRM_FLT: /* Receive Frame Filter */ + s->frm_flt = value; + break; + case REG_RX_HASH_0: /* Receive Hash Table 0 */ + case REG_RX_HASH_1: /* Receive Hash Table 1 */ + break; + case REG_MII_CMD: /* Management Interface Command */ + s->mii_cmd = value & ~MII_CMD_PHY_BUSY; + allwinner_sun8i_emac_mii_cmd(s); + break; + case REG_MII_DATA: /* Management Interface Data */ + s->mii_data = value; + break; + case REG_ADDR_HIGH: /* MAC Address High */ + stw_le_p(s->conf.macaddr.a + 4, value); + break; + case REG_ADDR_LOW: /* MAC Address Low */ + stl_le_p(s->conf.macaddr.a, value); + break; + case REG_TX_DMA_STA: /* Transmit DMA Status */ + case REG_TX_CUR_DESC: /* Transmit Current Descriptor */ + case REG_TX_CUR_BUF: /* Transmit Current Buffer */ + case REG_RX_DMA_STA: /* Receive DMA Status */ + case REG_RX_CUR_DESC: /* Receive Current Descriptor */ + case REG_RX_CUR_BUF: /* Receive Current Buffer */ + case REG_RGMII_STA: /* RGMII Status */ + break; + default: + qemu_log_mask(LOG_UNIMP, "allwinner-h3-emac: write access to unknown " + "EMAC register 0x" TARGET_FMT_plx "\n", + offset); + } +} + +static void allwinner_sun8i_emac_set_link(NetClientState *nc) +{ + AwSun8iEmacState *s = qemu_get_nic_opaque(nc); + + trace_allwinner_sun8i_emac_set_link(!nc->link_down); + allwinner_sun8i_emac_mii_set_link(s, !nc->link_down); +} + +static const MemoryRegionOps allwinner_sun8i_emac_mem_ops = { + .read = allwinner_sun8i_emac_read, + .write = allwinner_sun8i_emac_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, + .impl.min_access_size = 4, +}; + +static NetClientInfo net_allwinner_sun8i_emac_info = { + .type = NET_CLIENT_DRIVER_NIC, + .size = sizeof(NICState), + .can_receive = allwinner_sun8i_emac_can_receive, + .receive = allwinner_sun8i_emac_receive, + .link_status_changed = allwinner_sun8i_emac_set_link, +}; + +static void allwinner_sun8i_emac_init(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + AwSun8iEmacState *s = AW_SUN8I_EMAC(obj); + + memory_region_init_io(&s->iomem, OBJECT(s), &allwinner_sun8i_emac_mem_ops, + s, TYPE_AW_SUN8I_EMAC, 64 * KiB); + sysbus_init_mmio(sbd, &s->iomem); + sysbus_init_irq(sbd, &s->irq); +} + +static void allwinner_sun8i_emac_realize(DeviceState *dev, Error **errp) +{ + AwSun8iEmacState *s = AW_SUN8I_EMAC(dev); + + qemu_macaddr_default_if_unset(&s->conf.macaddr); + s->nic = qemu_new_nic(&net_allwinner_sun8i_emac_info, &s->conf, + object_get_typename(OBJECT(dev)), dev->id, s); + qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); +} + +static Property allwinner_sun8i_emac_properties[] = { + DEFINE_NIC_PROPERTIES(AwSun8iEmacState, conf), + DEFINE_PROP_UINT8("phy-addr", AwSun8iEmacState, mii_phy_addr, 0), + DEFINE_PROP_END_OF_LIST(), +}; + +static int allwinner_sun8i_emac_post_load(void *opaque, int version_id) +{ + AwSun8iEmacState *s = opaque; + + allwinner_sun8i_emac_set_link(qemu_get_queue(s->nic)); + + return 0; +} + +static const VMStateDescription vmstate_aw_emac = { + .name = "allwinner-sun8i-emac", + .version_id = 1, + .minimum_version_id = 1, + .post_load = allwinner_sun8i_emac_post_load, + .fields = (VMStateField[]) { + VMSTATE_UINT8(mii_phy_addr, AwSun8iEmacState), + VMSTATE_UINT32(mii_cmd, AwSun8iEmacState), + VMSTATE_UINT32(mii_data, AwSun8iEmacState), + VMSTATE_UINT32(mii_cr, AwSun8iEmacState), + VMSTATE_UINT32(mii_st, AwSun8iEmacState), + VMSTATE_UINT32(mii_adv, AwSun8iEmacState), + VMSTATE_UINT32(basic_ctl0, AwSun8iEmacState), + VMSTATE_UINT32(basic_ctl1, AwSun8iEmacState), + VMSTATE_UINT32(int_en, AwSun8iEmacState), + VMSTATE_UINT32(int_sta, AwSun8iEmacState), + VMSTATE_UINT32(frm_flt, AwSun8iEmacState), + VMSTATE_UINT32(rx_ctl0, AwSun8iEmacState), + VMSTATE_UINT32(rx_ctl1, AwSun8iEmacState), + VMSTATE_UINT32(rx_desc_head, AwSun8iEmacState), + VMSTATE_UINT32(rx_desc_curr, AwSun8iEmacState), + VMSTATE_UINT32(tx_ctl0, AwSun8iEmacState), + VMSTATE_UINT32(tx_ctl1, AwSun8iEmacState), + VMSTATE_UINT32(tx_desc_head, AwSun8iEmacState), + VMSTATE_UINT32(tx_desc_curr, AwSun8iEmacState), + VMSTATE_UINT32(tx_flowctl, AwSun8iEmacState), + VMSTATE_END_OF_LIST() + } +}; + +static void allwinner_sun8i_emac_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = allwinner_sun8i_emac_realize; + dc->reset = allwinner_sun8i_emac_reset; + dc->vmsd = &vmstate_aw_emac; + device_class_set_props(dc, allwinner_sun8i_emac_properties); +} + +static const TypeInfo allwinner_sun8i_emac_info = { + .name = TYPE_AW_SUN8I_EMAC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(AwSun8iEmacState), + .instance_init = allwinner_sun8i_emac_init, + .class_init = allwinner_sun8i_emac_class_init, +}; + +static void allwinner_sun8i_emac_register_types(void) +{ + type_register_static(&allwinner_sun8i_emac_info); +} + +type_init(allwinner_sun8i_emac_register_types) diff --git a/hw/net/allwinner_emac.c b/hw/net/allwinner_emac.c index e055a661c4..ddddf35c45 100644 --- a/hw/net/allwinner_emac.c +++ b/hw/net/allwinner_emac.c @@ -178,7 +178,7 @@ static uint32_t fifo8_pop_word(Fifo8 *fifo) return ret; } -static int aw_emac_can_receive(NetClientState *nc) +static bool aw_emac_can_receive(NetClientState *nc) { AwEmacState *s = qemu_get_nic_opaque(nc); @@ -519,7 +519,7 @@ static void aw_emac_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = aw_emac_realize; - dc->props = aw_emac_properties; + device_class_set_props(dc, aw_emac_properties); dc->reset = aw_emac_reset; dc->vmsd = &vmstate_aw_emac; } diff --git a/hw/net/cadence_gem.c b/hw/net/cadence_gem.c index b8be73dc55..51ec5a072d 100644 --- a/hw/net/cadence_gem.c +++ b/hw/net/cadence_gem.c @@ -505,7 +505,7 @@ static void phy_update_link(CadenceGEMState *s) } } -static int gem_can_receive(NetClientState *nc) +static bool gem_can_receive(NetClientState *nc) { CadenceGEMState *s; int i; @@ -518,7 +518,7 @@ static int gem_can_receive(NetClientState *nc) s->can_rx_state = 1; DB_PRINT("can't receive - no enable\n"); } - return 0; + return false; } for (i = 0; i < s->num_priority_queues; i++) { @@ -532,14 +532,14 @@ static int gem_can_receive(NetClientState *nc) s->can_rx_state = 2; DB_PRINT("can't receive - all the buffer descriptors are busy\n"); } - return 0; + return false; } if (s->can_rx_state != 0) { s->can_rx_state = 0; DB_PRINT("can receive\n"); } - return 1; + return true; } /* @@ -871,7 +871,7 @@ static void gem_get_rx_desc(CadenceGEMState *s, int q) /* read current descriptor */ address_space_read(&s->dma_as, desc_addr, MEMTXATTRS_UNSPECIFIED, - (uint8_t *)s->rx_desc[q], + s->rx_desc[q], sizeof(uint32_t) * gem_get_desc_len(s, true)); /* Descriptor owned by software ? */ @@ -987,8 +987,9 @@ static ssize_t gem_receive(NetClientState *nc, const uint8_t *buf, size_t size) return -1; } - DB_PRINT("copy %d bytes to 0x%x\n", MIN(bytes_to_copy, rxbufsize), - rx_desc_get_buffer(s->rx_desc[q])); + DB_PRINT("copy %u bytes to 0x%" PRIx64 "\n", + MIN(bytes_to_copy, rxbufsize), + rx_desc_get_buffer(s, s->rx_desc[q])); /* Copy packet data to emulated DMA buffer */ address_space_write(&s->dma_as, rx_desc_get_buffer(s, s->rx_desc[q]) + @@ -1029,9 +1030,8 @@ static ssize_t gem_receive(NetClientState *nc, const uint8_t *buf, size_t size) /* Descriptor write-back. */ desc_addr = gem_get_rx_desc_addr(s, q); - address_space_write(&s->dma_as, desc_addr, - MEMTXATTRS_UNSPECIFIED, - (uint8_t *)s->rx_desc[q], + address_space_write(&s->dma_as, desc_addr, MEMTXATTRS_UNSPECIFIED, + s->rx_desc[q], sizeof(uint32_t) * gem_get_desc_len(s, true)); /* Next descriptor */ @@ -1137,7 +1137,7 @@ static void gem_transmit(CadenceGEMState *s) DB_PRINT("read descriptor 0x%" HWADDR_PRIx "\n", packet_desc_addr); address_space_read(&s->dma_as, packet_desc_addr, - MEMTXATTRS_UNSPECIFIED, (uint8_t *)desc, + MEMTXATTRS_UNSPECIFIED, desc, sizeof(uint32_t) * gem_get_desc_len(s, false)); /* Handle all descriptors owned by hardware */ while (tx_desc_get_used(desc) == 0) { @@ -1160,9 +1160,9 @@ static void gem_transmit(CadenceGEMState *s) if (tx_desc_get_length(desc) > sizeof(tx_packet) - (p - tx_packet)) { - DB_PRINT("TX descriptor @ 0x%x too large: size 0x%x space " \ - "0x%x\n", (unsigned)packet_desc_addr, - (unsigned)tx_desc_get_length(desc), + DB_PRINT("TX descriptor @ 0x%" HWADDR_PRIx \ + " too large: size 0x%x space 0x%zx\n", + packet_desc_addr, tx_desc_get_length(desc), sizeof(tx_packet) - (p - tx_packet)); break; } @@ -1185,14 +1185,12 @@ static void gem_transmit(CadenceGEMState *s) * the processor. */ address_space_read(&s->dma_as, desc_addr, - MEMTXATTRS_UNSPECIFIED, - (uint8_t *)desc_first, + MEMTXATTRS_UNSPECIFIED, desc_first, sizeof(desc_first)); tx_desc_set_used(desc_first); address_space_write(&s->dma_as, desc_addr, - MEMTXATTRS_UNSPECIFIED, - (uint8_t *)desc_first, - sizeof(desc_first)); + MEMTXATTRS_UNSPECIFIED, desc_first, + sizeof(desc_first)); /* Advance the hardware current descriptor past this packet */ if (tx_desc_get_wrap(desc)) { s->tx_desc_addr[q] = s->regs[GEM_TXQBASE]; @@ -1246,8 +1244,8 @@ static void gem_transmit(CadenceGEMState *s) } DB_PRINT("read descriptor 0x%" HWADDR_PRIx "\n", packet_desc_addr); address_space_read(&s->dma_as, packet_desc_addr, - MEMTXATTRS_UNSPECIFIED, (uint8_t *)desc, - sizeof(uint32_t) * gem_get_desc_len(s, false)); + MEMTXATTRS_UNSPECIFIED, desc, + sizeof(uint32_t) * gem_get_desc_len(s, false)); } if (tx_desc_get_used(desc)) { @@ -1627,7 +1625,7 @@ static void gem_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = gem_realize; - dc->props = gem_properties; + device_class_set_props(dc, gem_properties); dc->vmsd = &vmstate_cadence_gem; dc->reset = gem_reset; } diff --git a/hw/net/can/can_sja1000.c b/hw/net/can/can_sja1000.c index 1f81341554..ea915a023a 100644 --- a/hw/net/can/can_sja1000.c +++ b/hw/net/can/can_sja1000.c @@ -247,8 +247,8 @@ int can_sja_accept_filter(CanSJA1000State *s, static void can_display_msg(const char *prefix, const qemu_can_frame *msg) { int i; + FILE *logfile = qemu_log_lock(); - qemu_log_lock(); qemu_log("%s%03X [%01d] %s %s", prefix, msg->can_id & QEMU_CAN_EFF_MASK, @@ -261,7 +261,7 @@ static void can_display_msg(const char *prefix, const qemu_can_frame *msg) } qemu_log("\n"); qemu_log_flush(); - qemu_log_unlock(); + qemu_log_unlock(logfile); } static void buff2frame_pel(const uint8_t *buff, qemu_can_frame *frame) @@ -733,21 +733,21 @@ uint64_t can_sja_mem_read(CanSJA1000State *s, hwaddr addr, unsigned size) return temp; } -int can_sja_can_receive(CanBusClientState *client) +bool can_sja_can_receive(CanBusClientState *client) { CanSJA1000State *s = container_of(client, CanSJA1000State, bus_client); if (s->clock & 0x80) { /* PeliCAN Mode */ if (s->mode & 0x01) { /* reset mode. */ - return 0; + return false; } } else { /* BasicCAN mode */ if (s->control & 0x01) { - return 0; + return false; } } - return 1; /* always return 1, when operation mode */ + return true; /* always return true, when operation mode */ } ssize_t can_sja_receive(CanBusClientState *client, const qemu_can_frame *frames, diff --git a/hw/net/can/can_sja1000.h b/hw/net/can/can_sja1000.h index 220a622087..7ca9cd681e 100644 --- a/hw/net/can/can_sja1000.h +++ b/hw/net/can/can_sja1000.h @@ -137,7 +137,7 @@ void can_sja_disconnect(CanSJA1000State *s); int can_sja_init(CanSJA1000State *s, qemu_irq irq); -int can_sja_can_receive(CanBusClientState *client); +bool can_sja_can_receive(CanBusClientState *client); ssize_t can_sja_receive(CanBusClientState *client, const qemu_can_frame *frames, size_t frames_cnt); diff --git a/hw/net/dp8393x.c b/hw/net/dp8393x.c index 3d991af163..c54db0d62d 100644 --- a/hw/net/dp8393x.c +++ b/hw/net/dp8393x.c @@ -137,6 +137,7 @@ do { printf("sonic ERROR: %s: " fmt, __func__ , ## __VA_ARGS__); } while (0) #define SONIC_TCR_CRCI 0x2000 #define SONIC_TCR_PINT 0x8000 +#define SONIC_ISR_RBAE 0x0010 #define SONIC_ISR_RBE 0x0020 #define SONIC_ISR_RDE 0x0040 #define SONIC_ISR_TC 0x0080 @@ -145,6 +146,9 @@ do { printf("sonic ERROR: %s: " fmt, __func__ , ## __VA_ARGS__); } while (0) #define SONIC_ISR_PINT 0x0800 #define SONIC_ISR_LCD 0x1000 +#define SONIC_DESC_EOL 0x0001 +#define SONIC_DESC_ADDR 0xFFFE + #define TYPE_DP8393X "dp8393x" #define DP8393X(obj) OBJECT_CHECK(dp8393xState, (obj), TYPE_DP8393X) @@ -154,6 +158,7 @@ typedef struct dp8393xState { /* Hardware */ uint8_t it_shift; bool big_endian; + bool last_rba_is_full; qemu_irq irq; #ifdef DEBUG_SONIC int irq_level; @@ -175,7 +180,7 @@ typedef struct dp8393xState { int loopback_packet; /* Memory access */ - void *dma_mr; + MemoryRegion *dma_mr; AddressSpace as; } dp8393xState; @@ -197,7 +202,8 @@ static uint32_t dp8393x_crba(dp8393xState *s) static uint32_t dp8393x_crda(dp8393xState *s) { - return (s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA]; + return (s->regs[SONIC_URDA] << 16) | + (s->regs[SONIC_CRDA] & SONIC_DESC_ADDR); } static uint32_t dp8393x_rbwc(dp8393xState *s) @@ -217,7 +223,8 @@ static uint32_t dp8393x_tsa(dp8393xState *s) static uint32_t dp8393x_ttda(dp8393xState *s) { - return (s->regs[SONIC_UTDA] << 16) | s->regs[SONIC_TTDA]; + return (s->regs[SONIC_UTDA] << 16) | + (s->regs[SONIC_TTDA] & SONIC_DESC_ADDR); } static uint32_t dp8393x_wt(dp8393xState *s) @@ -241,9 +248,19 @@ static void dp8393x_put(dp8393xState *s, int width, int offset, uint16_t val) { if (s->big_endian) { - s->data[offset * width + width - 1] = cpu_to_be16(val); + if (width == 2) { + s->data[offset * 2] = 0; + s->data[offset * 2 + 1] = cpu_to_be16(val); + } else { + s->data[offset] = cpu_to_be16(val); + } } else { - s->data[offset * width] = cpu_to_le16(val); + if (width == 2) { + s->data[offset * 2] = cpu_to_le16(val); + s->data[offset * 2 + 1] = 0; + } else { + s->data[offset] = cpu_to_le16(val); + } } } @@ -275,8 +292,8 @@ static void dp8393x_do_load_cam(dp8393xState *s) while (s->regs[SONIC_CDC] & 0x1f) { /* Fill current entry */ - address_space_rw(&s->as, dp8393x_cdp(s), - MEMTXATTRS_UNSPECIFIED, (uint8_t *)s->data, size, 0); + address_space_read(&s->as, dp8393x_cdp(s), + MEMTXATTRS_UNSPECIFIED, s->data, size); s->cam[index][0] = dp8393x_get(s, width, 1) & 0xff; s->cam[index][1] = dp8393x_get(s, width, 1) >> 8; s->cam[index][2] = dp8393x_get(s, width, 2) & 0xff; @@ -293,8 +310,8 @@ static void dp8393x_do_load_cam(dp8393xState *s) } /* Read CAM enable */ - address_space_rw(&s->as, dp8393x_cdp(s), - MEMTXATTRS_UNSPECIFIED, (uint8_t *)s->data, size, 0); + address_space_read(&s->as, dp8393x_cdp(s), + MEMTXATTRS_UNSPECIFIED, s->data, size); s->regs[SONIC_CE] = dp8393x_get(s, width, 0); DPRINTF("load cam done. cam enable mask 0x%04x\n", s->regs[SONIC_CE]); @@ -311,8 +328,8 @@ static void dp8393x_do_read_rra(dp8393xState *s) /* Read memory */ width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1; size = sizeof(uint16_t) * 4 * width; - address_space_rw(&s->as, dp8393x_rrp(s), - MEMTXATTRS_UNSPECIFIED, (uint8_t *)s->data, size, 0); + address_space_read(&s->as, dp8393x_rrp(s), + MEMTXATTRS_UNSPECIFIED, s->data, size); /* Update SONIC registers */ s->regs[SONIC_CRBA0] = dp8393x_get(s, width, 0); @@ -331,15 +348,15 @@ static void dp8393x_do_read_rra(dp8393xState *s) s->regs[SONIC_RRP] = s->regs[SONIC_RSA]; } - /* Check resource exhaustion */ + /* Warn the host if CRBA now has the last available resource */ if (s->regs[SONIC_RRP] == s->regs[SONIC_RWP]) { s->regs[SONIC_ISR] |= SONIC_ISR_RBE; dp8393x_update_irq(s); } - /* Done */ - s->regs[SONIC_CR] &= ~SONIC_CR_RRRA; + /* Allow packet reception */ + s->last_rba_is_full = false; } static void dp8393x_do_software_reset(dp8393xState *s) @@ -397,7 +414,7 @@ static void dp8393x_do_stop_timer(dp8393xState *s) dp8393x_update_wt_regs(s); } -static int dp8393x_can_receive(NetClientState *nc); +static bool dp8393x_can_receive(NetClientState *nc); static void dp8393x_do_receiver_enable(dp8393xState *s) { @@ -426,8 +443,8 @@ static void dp8393x_do_transmit_packets(dp8393xState *s) size = sizeof(uint16_t) * 6 * width; s->regs[SONIC_TTDA] = s->regs[SONIC_CTDA]; DPRINTF("Transmit packet at %08x\n", dp8393x_ttda(s)); - address_space_rw(&s->as, dp8393x_ttda(s) + sizeof(uint16_t) * width, - MEMTXATTRS_UNSPECIFIED, (uint8_t *)s->data, size, 0); + address_space_read(&s->as, dp8393x_ttda(s) + sizeof(uint16_t) * width, + MEMTXATTRS_UNSPECIFIED, s->data, size); tx_len = 0; /* Update registers */ @@ -451,17 +468,19 @@ static void dp8393x_do_transmit_packets(dp8393xState *s) if (tx_len + len > sizeof(s->tx_buffer)) { len = sizeof(s->tx_buffer) - tx_len; } - address_space_rw(&s->as, dp8393x_tsa(s), - MEMTXATTRS_UNSPECIFIED, &s->tx_buffer[tx_len], len, 0); + address_space_read(&s->as, dp8393x_tsa(s), MEMTXATTRS_UNSPECIFIED, + &s->tx_buffer[tx_len], len); tx_len += len; i++; if (i != s->regs[SONIC_TFC]) { /* Read next fragment details */ size = sizeof(uint16_t) * 3 * width; - address_space_rw(&s->as, - dp8393x_ttda(s) + sizeof(uint16_t) * (4 + 3 * i) * width, - MEMTXATTRS_UNSPECIFIED, (uint8_t *)s->data, size, 0); + address_space_read(&s->as, + dp8393x_ttda(s) + + sizeof(uint16_t) * width * (4 + 3 * i), + MEMTXATTRS_UNSPECIFIED, s->data, + size); s->regs[SONIC_TSA0] = dp8393x_get(s, width, 0); s->regs[SONIC_TSA1] = dp8393x_get(s, width, 1); s->regs[SONIC_TFS] = dp8393x_get(s, width, 2); @@ -494,20 +513,20 @@ static void dp8393x_do_transmit_packets(dp8393xState *s) dp8393x_put(s, width, 0, s->regs[SONIC_TCR] & 0x0fff); /* status */ size = sizeof(uint16_t) * width; - address_space_rw(&s->as, - dp8393x_ttda(s), - MEMTXATTRS_UNSPECIFIED, (uint8_t *)s->data, size, 1); + address_space_write(&s->as, dp8393x_ttda(s), + MEMTXATTRS_UNSPECIFIED, s->data, size); if (!(s->regs[SONIC_CR] & SONIC_CR_HTX)) { /* Read footer of packet */ size = sizeof(uint16_t) * width; - address_space_rw(&s->as, - dp8393x_ttda(s) + - sizeof(uint16_t) * - (4 + 3 * s->regs[SONIC_TFC]) * width, - MEMTXATTRS_UNSPECIFIED, (uint8_t *)s->data, size, 0); - s->regs[SONIC_CTDA] = dp8393x_get(s, width, 0) & ~0x1; - if (dp8393x_get(s, width, 0) & 0x1) { + address_space_read(&s->as, + dp8393x_ttda(s) + + sizeof(uint16_t) * width + * (4 + 3 * s->regs[SONIC_TFC]), + MEMTXATTRS_UNSPECIFIED, s->data, + size); + s->regs[SONIC_CTDA] = dp8393x_get(s, width, 0); + if (s->regs[SONIC_CTDA] & SONIC_DESC_EOL) { /* EOL detected */ break; } @@ -548,8 +567,10 @@ static void dp8393x_do_command(dp8393xState *s, uint16_t command) dp8393x_do_start_timer(s); if (command & SONIC_CR_RST) dp8393x_do_software_reset(s); - if (command & SONIC_CR_RRRA) + if (command & SONIC_CR_RRRA) { dp8393x_do_read_rra(s); + s->regs[SONIC_CR] &= ~SONIC_CR_RRRA; + } if (command & SONIC_CR_LCAM) dp8393x_do_load_cam(s); } @@ -583,7 +604,7 @@ static uint64_t dp8393x_read(void *opaque, hwaddr addr, unsigned int size) DPRINTF("read 0x%04x from reg %s\n", val, reg_names[reg]); - return val; + return s->big_endian ? val << 16 : val; } static void dp8393x_write(void *opaque, hwaddr addr, uint64_t data, @@ -591,13 +612,14 @@ static void dp8393x_write(void *opaque, hwaddr addr, uint64_t data, { dp8393xState *s = opaque; int reg = addr >> s->it_shift; + uint32_t val = s->big_endian ? data >> 16 : data; - DPRINTF("write 0x%04x to reg %s\n", (uint16_t)data, reg_names[reg]); + DPRINTF("write 0x%04x to reg %s\n", (uint16_t)val, reg_names[reg]); switch (reg) { /* Command register */ case SONIC_CR: - dp8393x_do_command(s, data); + dp8393x_do_command(s, val); break; /* Prevent write to read-only registers */ case SONIC_CAP2: @@ -610,59 +632,60 @@ static void dp8393x_write(void *opaque, hwaddr addr, uint64_t data, /* Accept write to some registers only when in reset mode */ case SONIC_DCR: if (s->regs[SONIC_CR] & SONIC_CR_RST) { - s->regs[reg] = data & 0xbfff; + s->regs[reg] = val & 0xbfff; } else { DPRINTF("writing to DCR invalid\n"); } break; case SONIC_DCR2: if (s->regs[SONIC_CR] & SONIC_CR_RST) { - s->regs[reg] = data & 0xf017; + s->regs[reg] = val & 0xf017; } else { DPRINTF("writing to DCR2 invalid\n"); } break; /* 12 lower bytes are Read Only */ case SONIC_TCR: - s->regs[reg] = data & 0xf000; + s->regs[reg] = val & 0xf000; break; /* 9 lower bytes are Read Only */ case SONIC_RCR: - s->regs[reg] = data & 0xffe0; + s->regs[reg] = val & 0xffe0; break; /* Ignore most significant bit */ case SONIC_IMR: - s->regs[reg] = data & 0x7fff; + s->regs[reg] = val & 0x7fff; dp8393x_update_irq(s); break; /* Clear bits by writing 1 to them */ case SONIC_ISR: - data &= s->regs[reg]; - s->regs[reg] &= ~data; - if (data & SONIC_ISR_RBE) { + val &= s->regs[reg]; + s->regs[reg] &= ~val; + if (val & SONIC_ISR_RBE) { dp8393x_do_read_rra(s); } dp8393x_update_irq(s); - if (dp8393x_can_receive(s->nic->ncs)) { - qemu_flush_queued_packets(qemu_get_queue(s->nic)); - } break; - /* Ignore least significant bit */ + /* The guest is required to store aligned pointers here */ case SONIC_RSA: case SONIC_REA: case SONIC_RRP: case SONIC_RWP: - s->regs[reg] = data & 0xfffe; + if (s->regs[SONIC_DCR] & SONIC_DCR_DW) { + s->regs[reg] = val & 0xfffc; + } else { + s->regs[reg] = val & 0xfffe; + } break; /* Invert written value for some registers */ case SONIC_CRCT: case SONIC_FAET: case SONIC_MPT: - s->regs[reg] = data ^ 0xffff; + s->regs[reg] = val ^ 0xffff; break; /* All other registers have no special contrainst */ default: - s->regs[reg] = data; + s->regs[reg] = val; } if (reg == SONIC_WT0 || reg == SONIC_WT1) { @@ -673,8 +696,8 @@ static void dp8393x_write(void *opaque, hwaddr addr, uint64_t data, static const MemoryRegionOps dp8393x_ops = { .read = dp8393x_read, .write = dp8393x_write, - .impl.min_access_size = 2, - .impl.max_access_size = 2, + .impl.min_access_size = 4, + .impl.max_access_size = 4, .endianness = DEVICE_NATIVE_ENDIAN, }; @@ -695,15 +718,11 @@ static void dp8393x_watchdog(void *opaque) dp8393x_update_irq(s); } -static int dp8393x_can_receive(NetClientState *nc) +static bool dp8393x_can_receive(NetClientState *nc) { dp8393xState *s = qemu_get_nic_opaque(nc); - if (!(s->regs[SONIC_CR] & SONIC_CR_RXEN)) - return 0; - if (s->regs[SONIC_ISR] & SONIC_ISR_RBE) - return 0; - return 1; + return !!(s->regs[SONIC_CR] & SONIC_CR_RXEN); } static int dp8393x_receive_filter(dp8393xState *s, const uint8_t * buf, @@ -741,40 +760,69 @@ static int dp8393x_receive_filter(dp8393xState *s, const uint8_t * buf, } static ssize_t dp8393x_receive(NetClientState *nc, const uint8_t * buf, - size_t size) + size_t pkt_size) { dp8393xState *s = qemu_get_nic_opaque(nc); int packet_type; uint32_t available, address; - int width, rx_len = size; + int width, rx_len, padded_len; uint32_t checksum; - - width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1; + int size; s->regs[SONIC_RCR] &= ~(SONIC_RCR_PRX | SONIC_RCR_LBK | SONIC_RCR_FAER | SONIC_RCR_CRCR | SONIC_RCR_LPKT | SONIC_RCR_BC | SONIC_RCR_MC); - packet_type = dp8393x_receive_filter(s, buf, size); + if (s->last_rba_is_full) { + return pkt_size; + } + + rx_len = pkt_size + sizeof(checksum); + if (s->regs[SONIC_DCR] & SONIC_DCR_DW) { + width = 2; + padded_len = ((rx_len - 1) | 3) + 1; + } else { + width = 1; + padded_len = ((rx_len - 1) | 1) + 1; + } + + if (padded_len > dp8393x_rbwc(s) * 2) { + DPRINTF("oversize packet, pkt_size is %d\n", pkt_size); + s->regs[SONIC_ISR] |= SONIC_ISR_RBAE; + dp8393x_update_irq(s); + s->regs[SONIC_RCR] |= SONIC_RCR_LPKT; + goto done; + } + + packet_type = dp8393x_receive_filter(s, buf, pkt_size); if (packet_type < 0) { DPRINTF("packet not for netcard\n"); return -1; } - /* XXX: Check byte ordering */ - /* Check for EOL */ - if (s->regs[SONIC_LLFA] & 0x1) { + if (s->regs[SONIC_LLFA] & SONIC_DESC_EOL) { /* Are we still in resource exhaustion? */ size = sizeof(uint16_t) * 1 * width; address = dp8393x_crda(s) + sizeof(uint16_t) * 5 * width; - address_space_rw(&s->as, address, MEMTXATTRS_UNSPECIFIED, - (uint8_t *)s->data, size, 0); - if (dp8393x_get(s, width, 0) & 0x1) { + address_space_read(&s->as, address, MEMTXATTRS_UNSPECIFIED, + s->data, size); + s->regs[SONIC_LLFA] = dp8393x_get(s, width, 0); + if (s->regs[SONIC_LLFA] & SONIC_DESC_EOL) { /* Still EOL ; stop reception */ return -1; - } else { - s->regs[SONIC_CRDA] = s->regs[SONIC_LLFA]; } + /* Link has been updated by host */ + + /* Clear in_use */ + size = sizeof(uint16_t) * width; + address = dp8393x_crda(s) + sizeof(uint16_t) * 6 * width; + dp8393x_put(s, width, 0, 0); + address_space_rw(&s->as, address, MEMTXATTRS_UNSPECIFIED, + (uint8_t *)s->data, size, 1); + + /* Move to next descriptor */ + s->regs[SONIC_CRDA] = s->regs[SONIC_LLFA]; + s->regs[SONIC_ISR] |= SONIC_ISR_PKTRX; } /* Save current position */ @@ -782,21 +830,32 @@ static ssize_t dp8393x_receive(NetClientState *nc, const uint8_t * buf, s->regs[SONIC_TRBA0] = s->regs[SONIC_CRBA0]; /* Calculate the ethernet checksum */ - checksum = cpu_to_le32(crc32(0, buf, rx_len)); + checksum = cpu_to_le32(crc32(0, buf, pkt_size)); /* Put packet into RBA */ DPRINTF("Receive packet at %08x\n", dp8393x_crba(s)); address = dp8393x_crba(s); - address_space_rw(&s->as, address, - MEMTXATTRS_UNSPECIFIED, (uint8_t *)buf, rx_len, 1); - address += rx_len; - address_space_rw(&s->as, address, - MEMTXATTRS_UNSPECIFIED, (uint8_t *)&checksum, 4, 1); - rx_len += 4; + address_space_write(&s->as, address, MEMTXATTRS_UNSPECIFIED, + buf, pkt_size); + address += pkt_size; + + /* Put frame checksum into RBA */ + address_space_write(&s->as, address, MEMTXATTRS_UNSPECIFIED, + &checksum, sizeof(checksum)); + address += sizeof(checksum); + + /* Pad short packets to keep pointers aligned */ + if (rx_len < padded_len) { + size = padded_len - rx_len; + address_space_rw(&s->as, address, MEMTXATTRS_UNSPECIFIED, + (uint8_t *)"\xFF\xFF\xFF", size, 1); + address += size; + } + s->regs[SONIC_CRBA1] = address >> 16; s->regs[SONIC_CRBA0] = address & 0xffff; available = dp8393x_rbwc(s); - available -= rx_len / 2; + available -= padded_len >> 1; s->regs[SONIC_RBWC1] = available >> 16; s->regs[SONIC_RBWC0] = available & 0xffff; @@ -819,41 +878,50 @@ static ssize_t dp8393x_receive(NetClientState *nc, const uint8_t * buf, dp8393x_put(s, width, 3, s->regs[SONIC_TRBA1]); /* pkt_ptr1 */ dp8393x_put(s, width, 4, s->regs[SONIC_RSC]); /* seq_no */ size = sizeof(uint16_t) * 5 * width; - address_space_rw(&s->as, dp8393x_crda(s), - MEMTXATTRS_UNSPECIFIED, (uint8_t *)s->data, size, 1); + address_space_write(&s->as, dp8393x_crda(s), + MEMTXATTRS_UNSPECIFIED, + s->data, size); - /* Move to next descriptor */ + /* Check link field */ size = sizeof(uint16_t) * width; - address_space_rw(&s->as, dp8393x_crda(s) + sizeof(uint16_t) * 5 * width, - MEMTXATTRS_UNSPECIFIED, (uint8_t *)s->data, size, 0); + address_space_read(&s->as, + dp8393x_crda(s) + sizeof(uint16_t) * 5 * width, + MEMTXATTRS_UNSPECIFIED, s->data, size); s->regs[SONIC_LLFA] = dp8393x_get(s, width, 0); - if (s->regs[SONIC_LLFA] & 0x1) { + if (s->regs[SONIC_LLFA] & SONIC_DESC_EOL) { /* EOL detected */ s->regs[SONIC_ISR] |= SONIC_ISR_RDE; } else { - /* Clear in_use, but it is always 16bit wide */ - int offset = dp8393x_crda(s) + sizeof(uint16_t) * 6 * width; - if (s->big_endian && width == 2) { - /* we need to adjust the offset of the 16bit field */ - offset += sizeof(uint16_t); - } - s->data[0] = 0; - address_space_rw(&s->as, offset, MEMTXATTRS_UNSPECIFIED, - (uint8_t *)s->data, sizeof(uint16_t), 1); + /* Clear in_use */ + size = sizeof(uint16_t) * width; + address = dp8393x_crda(s) + sizeof(uint16_t) * 6 * width; + dp8393x_put(s, width, 0, 0); + address_space_write(&s->as, address, MEMTXATTRS_UNSPECIFIED, + s->data, size); + + /* Move to next descriptor */ s->regs[SONIC_CRDA] = s->regs[SONIC_LLFA]; s->regs[SONIC_ISR] |= SONIC_ISR_PKTRX; - s->regs[SONIC_RSC] = (s->regs[SONIC_RSC] & 0xff00) | (((s->regs[SONIC_RSC] & 0x00ff) + 1) & 0x00ff); + } - if (s->regs[SONIC_RCR] & SONIC_RCR_LPKT) { - /* Read next RRA */ + dp8393x_update_irq(s); + + s->regs[SONIC_RSC] = (s->regs[SONIC_RSC] & 0xff00) | + ((s->regs[SONIC_RSC] + 1) & 0x00ff); + +done: + + if (s->regs[SONIC_RCR] & SONIC_RCR_LPKT) { + if (s->regs[SONIC_RRP] == s->regs[SONIC_RWP]) { + /* Stop packet reception */ + s->last_rba_is_full = true; + } else { + /* Read next resource */ dp8393x_do_read_rra(s); } } - /* Done */ - dp8393x_update_irq(s); - - return size; + return pkt_size; } static void dp8393x_reset(DeviceState *dev) @@ -862,6 +930,7 @@ static void dp8393x_reset(DeviceState *dev) timer_del(s->watchdog); memset(s->regs, 0, sizeof(s->regs)); + s->regs[SONIC_SR] = 0x0004; /* only revision recognized by Linux/mips */ s->regs[SONIC_CR] = SONIC_CR_RST | SONIC_CR_STP | SONIC_CR_RXDIS; s->regs[SONIC_DCR] &= ~(SONIC_DCR_EXBUS | SONIC_DCR_LBR); s->regs[SONIC_RCR] &= ~(SONIC_RCR_LB0 | SONIC_RCR_LB1 | SONIC_RCR_BRD | SONIC_RCR_RNT); @@ -914,15 +983,13 @@ static void dp8393x_realize(DeviceState *dev, Error **errp) qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); s->watchdog = timer_new_ns(QEMU_CLOCK_VIRTUAL, dp8393x_watchdog, s); - s->regs[SONIC_SR] = 0x0004; /* only revision recognized by Linux */ - memory_region_init_ram(&s->prom, OBJECT(dev), - "dp8393x-prom", SONIC_PROM_SIZE, &local_err); + memory_region_init_rom(&s->prom, OBJECT(dev), "dp8393x-prom", + SONIC_PROM_SIZE, &local_err); if (local_err) { error_propagate(errp, local_err); return; } - memory_region_set_readonly(&s->prom, true); prom = memory_region_get_ram_ptr(&s->prom); checksum = 0; for (i = 0; i < 6; i++) { @@ -948,7 +1015,8 @@ static const VMStateDescription vmstate_dp8393x = { static Property dp8393x_properties[] = { DEFINE_NIC_PROPERTIES(dp8393xState, conf), - DEFINE_PROP_PTR("dma_mr", dp8393xState, dma_mr), + DEFINE_PROP_LINK("dma_mr", dp8393xState, dma_mr, + TYPE_MEMORY_REGION, MemoryRegion *), DEFINE_PROP_UINT8("it_shift", dp8393xState, it_shift, 0), DEFINE_PROP_BOOL("big_endian", dp8393xState, big_endian, false), DEFINE_PROP_END_OF_LIST(), @@ -962,9 +1030,7 @@ static void dp8393x_class_init(ObjectClass *klass, void *data) dc->realize = dp8393x_realize; dc->reset = dp8393x_reset; dc->vmsd = &vmstate_dp8393x; - dc->props = dp8393x_properties; - /* Reason: dma_mr property can't be set */ - dc->user_creatable = false; + device_class_set_props(dc, dp8393x_properties); } static const TypeInfo dp8393x_info = { diff --git a/hw/net/e1000.c b/hw/net/e1000.c index a73f8d404e..2a69eee63f 100644 --- a/hw/net/e1000.c +++ b/hw/net/e1000.c @@ -845,7 +845,7 @@ static bool e1000_has_rxbufs(E1000State *s, size_t total_size) return total_size <= bufs * s->rxbuf_size; } -static int +static bool e1000_can_receive(NetClientState *nc) { E1000State *s = qemu_get_nic_opaque(nc); @@ -1150,7 +1150,8 @@ set_ims(E1000State *s, int index, uint32_t val) } #define getreg(x) [x] = mac_readreg -static uint32_t (*macreg_readops[])(E1000State *, int) = { +typedef uint32_t (*readops)(E1000State *, int); +static const readops macreg_readops[] = { getreg(PBA), getreg(RCTL), getreg(TDH), getreg(TXDCTL), getreg(WUFC), getreg(TDT), getreg(CTRL), getreg(LEDCTL), getreg(MANC), getreg(MDIC), getreg(SWSM), getreg(STATUS), @@ -1205,7 +1206,8 @@ static uint32_t (*macreg_readops[])(E1000State *, int) = { enum { NREADOPS = ARRAY_SIZE(macreg_readops) }; #define putreg(x) [x] = mac_writereg -static void (*macreg_writeops[])(E1000State *, int, uint32_t) = { +typedef void (*writeops)(E1000State *, int, uint32_t); +static const writeops macreg_writeops[] = { putreg(PBA), putreg(EERD), putreg(SWSM), putreg(WUFC), putreg(TDBAL), putreg(TDBAH), putreg(TXDCTL), putreg(RDBAH), putreg(RDBAL), putreg(LEDCTL), putreg(VET), putreg(FCRUC), @@ -1764,7 +1766,7 @@ static void e1000_class_init(ObjectClass *klass, void *data) dc->desc = "Intel Gigabit Ethernet"; dc->reset = qdev_e1000_reset; dc->vmsd = &vmstate_e1000; - dc->props = e1000_properties; + device_class_set_props(dc, e1000_properties); } static void e1000_instance_init(Object *obj) diff --git a/hw/net/e1000e.c b/hw/net/e1000e.c index b69fd7d8ad..79ba158d41 100644 --- a/hw/net/e1000e.c +++ b/hw/net/e1000e.c @@ -199,7 +199,7 @@ static const MemoryRegionOps io_ops = { }, }; -static int +static bool e1000e_nc_can_receive(NetClientState *nc) { E1000EState *s = qemu_get_nic_opaque(nc); @@ -328,7 +328,7 @@ e1000e_init_net_peer(E1000EState *s, PCIDevice *pci_dev, uint8_t *macaddr) s->nic = qemu_new_nic(&net_e1000e_info, &s->conf, object_get_typename(OBJECT(s)), dev->id, s); - s->core.max_queue_num = s->conf.peers.queues - 1; + s->core.max_queue_num = s->conf.peers.queues ? s->conf.peers.queues - 1 : 0; trace_e1000e_mac_set_permanent(MAC_ARG(macaddr)); memcpy(s->core.permanent_mac, macaddr, sizeof(s->core.permanent_mac)); @@ -684,7 +684,6 @@ static void e1000e_class_init(ObjectClass *class, void *data) dc->desc = "Intel 82574L GbE Controller"; dc->reset = e1000e_qdev_reset; dc->vmsd = &e1000e_vmstate; - dc->props = e1000e_properties; e1000e_prop_disable_vnet = qdev_prop_uint8; e1000e_prop_disable_vnet.description = "Do not use virtio headers, " @@ -697,6 +696,7 @@ static void e1000e_class_init(ObjectClass *class, void *data) e1000e_prop_subsys = qdev_prop_uint16; e1000e_prop_subsys.description = "PCI device Subsystem ID"; + device_class_set_props(dc, e1000e_properties); set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); } diff --git a/hw/net/e1000e_core.c b/hw/net/e1000e_core.c index 9b76f82db5..d5676871fa 100644 --- a/hw/net/e1000e_core.c +++ b/hw/net/e1000e_core.c @@ -582,7 +582,7 @@ e1000e_rss_calc_hash(E1000ECore *core, type = NetPktRssIpV4Tcp; break; case E1000_MRQ_RSS_TYPE_IPV6TCP: - type = NetPktRssIpV6Tcp; + type = NetPktRssIpV6TcpEx; break; case E1000_MRQ_RSS_TYPE_IPV6: type = NetPktRssIpV6; @@ -967,7 +967,7 @@ e1000e_start_recv(E1000ECore *core) } } -int +bool e1000e_can_receive(E1000ECore *core) { int i; @@ -2813,12 +2813,15 @@ e1000e_set_eitr(E1000ECore *core, int index, uint32_t val) static void e1000e_set_psrctl(E1000ECore *core, int index, uint32_t val) { - if ((val & E1000_PSRCTL_BSIZE0_MASK) == 0) { - hw_error("e1000e: PSRCTL.BSIZE0 cannot be zero"); - } + if (core->mac[RCTL] & E1000_RCTL_DTYP_MASK) { - if ((val & E1000_PSRCTL_BSIZE1_MASK) == 0) { - hw_error("e1000e: PSRCTL.BSIZE1 cannot be zero"); + if ((val & E1000_PSRCTL_BSIZE0_MASK) == 0) { + hw_error("e1000e: PSRCTL.BSIZE0 cannot be zero"); + } + + if ((val & E1000_PSRCTL_BSIZE1_MASK) == 0) { + hw_error("e1000e: PSRCTL.BSIZE1 cannot be zero"); + } } core->mac[PSRCTL] = val; @@ -2852,7 +2855,8 @@ e1000e_set_gcr(E1000ECore *core, int index, uint32_t val) } #define e1000e_getreg(x) [x] = e1000e_mac_readreg -static uint32_t (*e1000e_macreg_readops[])(E1000ECore *, int) = { +typedef uint32_t (*readops)(E1000ECore *, int); +static const readops e1000e_macreg_readops[] = { e1000e_getreg(PBA), e1000e_getreg(WUFC), e1000e_getreg(MANC), @@ -3058,7 +3062,8 @@ static uint32_t (*e1000e_macreg_readops[])(E1000ECore *, int) = { enum { E1000E_NREADOPS = ARRAY_SIZE(e1000e_macreg_readops) }; #define e1000e_putreg(x) [x] = e1000e_mac_writereg -static void (*e1000e_macreg_writeops[])(E1000ECore *, int, uint32_t) = { +typedef void (*writeops)(E1000ECore *, int, uint32_t); +static const writeops e1000e_macreg_writeops[] = { e1000e_putreg(PBA), e1000e_putreg(SWSM), e1000e_putreg(WUFC), diff --git a/hw/net/e1000e_core.h b/hw/net/e1000e_core.h index 49abb136dd..aee32f7e48 100644 --- a/hw/net/e1000e_core.h +++ b/hw/net/e1000e_core.h @@ -143,7 +143,7 @@ e1000e_core_set_link_status(E1000ECore *core); void e1000e_core_pci_uninit(E1000ECore *core); -int +bool e1000e_can_receive(E1000ECore *core); ssize_t diff --git a/hw/net/eepro100.c b/hw/net/eepro100.c index cc2dd8b1c9..f6474f0e68 100644 --- a/hw/net/eepro100.c +++ b/hw/net/eepro100.c @@ -1815,7 +1815,7 @@ static void pci_nic_uninit(PCIDevice *pci_dev) { EEPRO100State *s = DO_UPCAST(EEPRO100State, dev, pci_dev); - vmstate_unregister(&pci_dev->qdev, s->vmstate, s); + vmstate_unregister(VMSTATE_IF(&pci_dev->qdev), s->vmstate, s); g_free(s->vmstate); eeprom93xx_free(&pci_dev->qdev, s->eeprom); qemu_del_nic(s->nic); @@ -1874,7 +1874,8 @@ static void e100_nic_realize(PCIDevice *pci_dev, Error **errp) s->vmstate = g_memdup(&vmstate_eepro100, sizeof(vmstate_eepro100)); s->vmstate->name = qemu_get_queue(s->nic)->model; - vmstate_register(&pci_dev->qdev, -1, s->vmstate, s); + vmstate_register(VMSTATE_IF(&pci_dev->qdev), VMSTATE_INSTANCE_ID_ANY, + s->vmstate, s); } static void eepro100_instance_init(Object *obj) @@ -2059,7 +2060,7 @@ static void eepro100_class_init(ObjectClass *klass, void *data) info = eepro100_get_class_by_name(object_class_get_name(klass)); set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); - dc->props = e100_properties; + device_class_set_props(dc, e100_properties); dc->desc = info->desc; k->vendor_id = PCI_VENDOR_ID_INTEL; k->class_id = PCI_CLASS_NETWORK_ETHERNET; diff --git a/hw/net/etraxfs_eth.c b/hw/net/etraxfs_eth.c index 4cfbf1135a..27fd069b96 100644 --- a/hw/net/etraxfs_eth.c +++ b/hw/net/etraxfs_eth.c @@ -338,14 +338,8 @@ typedef struct ETRAXFSEthState uint8_t macaddr[2][6]; uint32_t regs[FS_ETH_MAX_REGS]; - union { - void *vdma_out; - struct etraxfs_dma_client *dma_out; - }; - union { - void *vdma_in; - struct etraxfs_dma_client *dma_in; - }; + struct etraxfs_dma_client *dma_out; + struct etraxfs_dma_client *dma_in; /* MDIO bus. */ struct qemu_mdio mdio_bus; @@ -635,8 +629,6 @@ static void etraxfs_eth_realize(DeviceState *dev, Error **errp) static Property etraxfs_eth_properties[] = { DEFINE_PROP_UINT32("phyaddr", ETRAXFSEthState, phyaddr, 1), - DEFINE_PROP_PTR("dma_out", ETRAXFSEthState, vdma_out), - DEFINE_PROP_PTR("dma_in", ETRAXFSEthState, vdma_in), DEFINE_NIC_PROPERTIES(ETRAXFSEthState, conf), DEFINE_PROP_END_OF_LIST(), }; @@ -647,11 +639,41 @@ static void etraxfs_eth_class_init(ObjectClass *klass, void *data) dc->realize = etraxfs_eth_realize; dc->reset = etraxfs_eth_reset; - dc->props = etraxfs_eth_properties; - /* Reason: pointer properties "dma_out", "dma_in" */ + device_class_set_props(dc, etraxfs_eth_properties); + /* Reason: dma_out, dma_in are not user settable */ dc->user_creatable = false; } + +/* Instantiate an ETRAXFS Ethernet MAC. */ +DeviceState * +etraxfs_eth_init(NICInfo *nd, hwaddr base, int phyaddr, + struct etraxfs_dma_client *dma_out, + struct etraxfs_dma_client *dma_in) +{ + DeviceState *dev; + qemu_check_nic_model(nd, "fseth"); + + dev = qdev_create(NULL, "etraxfs-eth"); + qdev_set_nic_properties(dev, nd); + qdev_prop_set_uint32(dev, "phyaddr", phyaddr); + + /* + * TODO: QOM design, define a QOM interface for "I am an etraxfs + * DMA client" (which replaces the current 'struct + * etraxfs_dma_client' ad-hoc interface), implement it on the + * ethernet device, and then have QOM link properties on the DMA + * controller device so that you can pass the interface + * implementations to it. + */ + ETRAX_FS_ETH(dev)->dma_out = dma_out; + ETRAX_FS_ETH(dev)->dma_in = dma_in; + qdev_init_nofail(dev); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); + + return dev; +} + static const TypeInfo etraxfs_eth_info = { .name = TYPE_ETRAX_FS_ETH, .parent = TYPE_SYS_BUS_DEVICE, diff --git a/hw/net/fsl_etsec/etsec.c b/hw/net/fsl_etsec/etsec.c index 717de76569..475f3c887a 100644 --- a/hw/net/fsl_etsec/etsec.c +++ b/hw/net/fsl_etsec/etsec.c @@ -423,7 +423,7 @@ static void etsec_class_init(ObjectClass *klass, void *data) dc->realize = etsec_realize; dc->reset = etsec_reset; - dc->props = etsec_properties; + device_class_set_props(dc, etsec_properties); /* Supported by ppce500 machine */ dc->user_creatable = true; } diff --git a/hw/net/ftgmac100.c b/hw/net/ftgmac100.c index eb8b441461..041ed21017 100644 --- a/hw/net/ftgmac100.c +++ b/hw/net/ftgmac100.c @@ -198,6 +198,8 @@ typedef struct { uint32_t des3; } FTGMAC100Desc; +#define FTGMAC100_DESC_ALIGNMENT 16 + /* * Specific RTL8211E MII Registers */ @@ -560,18 +562,18 @@ static void ftgmac100_do_tx(FTGMAC100State *s, uint32_t tx_ring, ftgmac100_update_irq(s); } -static int ftgmac100_can_receive(NetClientState *nc) +static bool ftgmac100_can_receive(NetClientState *nc) { FTGMAC100State *s = FTGMAC100(qemu_get_nic_opaque(nc)); FTGMAC100Desc bd; if ((s->maccr & (FTGMAC100_MACCR_RXDMA_EN | FTGMAC100_MACCR_RXMAC_EN)) != (FTGMAC100_MACCR_RXDMA_EN | FTGMAC100_MACCR_RXMAC_EN)) { - return 0; + return false; } if (ftgmac100_read_bd(&bd, s->rx_descriptor)) { - return 0; + return false; } return !(bd.des0 & FTGMAC100_RXDES0_RXPKT_RDY); } @@ -722,6 +724,12 @@ static void ftgmac100_write(void *opaque, hwaddr addr, s->itc = value; break; case FTGMAC100_RXR_BADR: /* Ring buffer address */ + if (!QEMU_IS_ALIGNED(value, FTGMAC100_DESC_ALIGNMENT)) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad RX buffer alignment 0x%" + HWADDR_PRIx "\n", __func__, value); + return; + } + s->rx_ring = value; s->rx_descriptor = s->rx_ring; break; @@ -731,6 +739,11 @@ static void ftgmac100_write(void *opaque, hwaddr addr, break; case FTGMAC100_NPTXR_BADR: /* Transmit buffer address */ + if (!QEMU_IS_ALIGNED(value, FTGMAC100_DESC_ALIGNMENT)) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad TX buffer alignment 0x%" + HWADDR_PRIx "\n", __func__, value); + return; + } s->tx_ring = value; s->tx_descriptor = s->tx_ring; break; @@ -1075,7 +1088,7 @@ static void ftgmac100_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_ftgmac100; dc->reset = ftgmac100_reset; - dc->props = ftgmac100_properties; + device_class_set_props(dc, ftgmac100_properties); set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); dc->realize = ftgmac100_realize; dc->desc = "Faraday FTGMAC100 Gigabit Ethernet emulation"; @@ -1204,17 +1217,8 @@ static void aspeed_mii_realize(DeviceState *dev, Error **errp) { AspeedMiiState *s = ASPEED_MII(dev); SysBusDevice *sbd = SYS_BUS_DEVICE(dev); - Object *obj; - Error *local_err = NULL; - obj = object_property_get_link(OBJECT(dev), "nic", &local_err); - if (!obj) { - error_propagate(errp, local_err); - error_prepend(errp, "required link 'nic' not found: "); - return; - } - - s->nic = FTGMAC100(obj); + assert(s->nic); memory_region_init_io(&s->iomem, OBJECT(dev), &aspeed_mii_ops, s, TYPE_ASPEED_MII, 0x8); @@ -1231,6 +1235,13 @@ static const VMStateDescription vmstate_aspeed_mii = { VMSTATE_END_OF_LIST() } }; + +static Property aspeed_mii_properties[] = { + DEFINE_PROP_LINK("nic", AspeedMiiState, nic, TYPE_FTGMAC100, + FTGMAC100State *), + DEFINE_PROP_END_OF_LIST(), +}; + static void aspeed_mii_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -1239,6 +1250,7 @@ static void aspeed_mii_class_init(ObjectClass *klass, void *data) dc->reset = aspeed_mii_reset; dc->realize = aspeed_mii_realize; dc->desc = "Aspeed MII controller"; + device_class_set_props(dc, aspeed_mii_properties); } static const TypeInfo aspeed_mii_info = { diff --git a/hw/net/i82596.c b/hw/net/i82596.c new file mode 100644 index 0000000000..055c3a1470 --- /dev/null +++ b/hw/net/i82596.c @@ -0,0 +1,755 @@ +/* + * QEMU Intel i82596 (Apricot) emulation + * + * Copyright (c) 2019 Helge Deller + * This work is licensed under the GNU GPL license version 2 or later. + * + * This software was written to be compatible with the specification: + * https://www.intel.com/assets/pdf/general/82596ca.pdf + */ + +#include "qemu/osdep.h" +#include "qemu/timer.h" +#include "net/net.h" +#include "net/eth.h" +#include "sysemu/sysemu.h" +#include "hw/irq.h" +#include "hw/qdev-properties.h" +#include "migration/vmstate.h" +#include "qemu/module.h" +#include "trace.h" +#include "i82596.h" +#include /* For crc32 */ + +#if defined(ENABLE_DEBUG) +#define DBG(x) x +#else +#define DBG(x) do { } while (0) +#endif + +#define USE_TIMER 0 + +#define BITS(n, m) (((0xffffffffU << (31 - n)) >> (31 - n + m)) << m) + +#define PKT_BUF_SZ 1536 +#define MAX_MC_CNT 64 + +#define ISCP_BUSY 0x0001 + +#define I596_NULL ((uint32_t)0xffffffff) + +#define SCB_STATUS_CX 0x8000 /* CU finished command with I bit */ +#define SCB_STATUS_FR 0x4000 /* RU finished receiving a frame */ +#define SCB_STATUS_CNA 0x2000 /* CU left active state */ +#define SCB_STATUS_RNR 0x1000 /* RU left active state */ + +#define SCB_COMMAND_ACK_MASK \ + (SCB_STATUS_CX | SCB_STATUS_FR | SCB_STATUS_CNA | SCB_STATUS_RNR) + +#define CU_IDLE 0 +#define CU_SUSPENDED 1 +#define CU_ACTIVE 2 + +#define RX_IDLE 0 +#define RX_SUSPENDED 1 +#define RX_READY 4 + +#define CMD_EOL 0x8000 /* The last command of the list, stop. */ +#define CMD_SUSP 0x4000 /* Suspend after doing cmd. */ +#define CMD_INTR 0x2000 /* Interrupt after doing cmd. */ + +#define CMD_FLEX 0x0008 /* Enable flexible memory model */ + +enum commands { + CmdNOp = 0, CmdSASetup = 1, CmdConfigure = 2, CmdMulticastList = 3, + CmdTx = 4, CmdTDR = 5, CmdDump = 6, CmdDiagnose = 7 +}; + +#define STAT_C 0x8000 /* Set to 0 after execution */ +#define STAT_B 0x4000 /* Command being executed */ +#define STAT_OK 0x2000 /* Command executed ok */ +#define STAT_A 0x1000 /* Command aborted */ + +#define I596_EOF 0x8000 +#define SIZE_MASK 0x3fff + +#define ETHER_TYPE_LEN 2 +#define VLAN_TCI_LEN 2 +#define VLAN_HLEN (ETHER_TYPE_LEN + VLAN_TCI_LEN) + +/* various flags in the chip config registers */ +#define I596_PREFETCH (s->config[0] & 0x80) +#define I596_PROMISC (s->config[8] & 0x01) +#define I596_BC_DISABLE (s->config[8] & 0x02) /* broadcast disable */ +#define I596_NOCRC_INS (s->config[8] & 0x08) +#define I596_CRCINM (s->config[11] & 0x04) /* CRC appended */ +#define I596_MC_ALL (s->config[11] & 0x20) +#define I596_MULTIIA (s->config[13] & 0x40) + + +static uint8_t get_byte(uint32_t addr) +{ + return ldub_phys(&address_space_memory, addr); +} + +static void set_byte(uint32_t addr, uint8_t c) +{ + return stb_phys(&address_space_memory, addr, c); +} + +static uint16_t get_uint16(uint32_t addr) +{ + return lduw_be_phys(&address_space_memory, addr); +} + +static void set_uint16(uint32_t addr, uint16_t w) +{ + return stw_be_phys(&address_space_memory, addr, w); +} + +static uint32_t get_uint32(uint32_t addr) +{ + uint32_t lo = lduw_be_phys(&address_space_memory, addr); + uint32_t hi = lduw_be_phys(&address_space_memory, addr + 2); + return (hi << 16) | lo; +} + +static void set_uint32(uint32_t addr, uint32_t val) +{ + set_uint16(addr, (uint16_t) val); + set_uint16(addr + 2, val >> 16); +} + + +struct qemu_ether_header { + uint8_t ether_dhost[6]; + uint8_t ether_shost[6]; + uint16_t ether_type; +}; + +#define PRINT_PKTHDR(txt, BUF) do { \ + struct qemu_ether_header *hdr = (void *)(BUF); \ + printf(txt ": packet dhost=" MAC_FMT ", shost=" MAC_FMT ", type=0x%04x\n",\ + MAC_ARG(hdr->ether_dhost), MAC_ARG(hdr->ether_shost), \ + be16_to_cpu(hdr->ether_type)); \ +} while (0) + +static void i82596_transmit(I82596State *s, uint32_t addr) +{ + uint32_t tdb_p; /* Transmit Buffer Descriptor */ + + /* TODO: Check flexible mode */ + tdb_p = get_uint32(addr + 8); + while (tdb_p != I596_NULL) { + uint16_t size, len; + uint32_t tba; + + size = get_uint16(tdb_p); + len = size & SIZE_MASK; + tba = get_uint32(tdb_p + 8); + trace_i82596_transmit(len, tba); + + if (s->nic && len) { + assert(len <= sizeof(s->tx_buffer)); + address_space_read(&address_space_memory, tba, + MEMTXATTRS_UNSPECIFIED, s->tx_buffer, len); + DBG(PRINT_PKTHDR("Send", &s->tx_buffer)); + DBG(printf("Sending %d bytes\n", len)); + qemu_send_packet(qemu_get_queue(s->nic), s->tx_buffer, len); + } + + /* was this the last package? */ + if (size & I596_EOF) { + break; + } + + /* get next buffer pointer */ + tdb_p = get_uint32(tdb_p + 4); + } +} + +static void set_individual_address(I82596State *s, uint32_t addr) +{ + NetClientState *nc; + uint8_t *m; + + nc = qemu_get_queue(s->nic); + m = s->conf.macaddr.a; + address_space_read(&address_space_memory, addr + 8, + MEMTXATTRS_UNSPECIFIED, m, ETH_ALEN); + qemu_format_nic_info_str(nc, m); + trace_i82596_new_mac(nc->info_str); +} + +static void set_multicast_list(I82596State *s, uint32_t addr) +{ + uint16_t mc_count, i; + + memset(&s->mult[0], 0, sizeof(s->mult)); + mc_count = get_uint16(addr + 8) / ETH_ALEN; + addr += 10; + if (mc_count > MAX_MC_CNT) { + mc_count = MAX_MC_CNT; + } + for (i = 0; i < mc_count; i++) { + uint8_t multicast_addr[ETH_ALEN]; + address_space_read(&address_space_memory, addr + i * ETH_ALEN, + MEMTXATTRS_UNSPECIFIED, multicast_addr, ETH_ALEN); + DBG(printf("Add multicast entry " MAC_FMT "\n", + MAC_ARG(multicast_addr))); + unsigned mcast_idx = (net_crc32(multicast_addr, ETH_ALEN) & + BITS(7, 2)) >> 2; + assert(mcast_idx < 8 * sizeof(s->mult)); + s->mult[mcast_idx >> 3] |= (1 << (mcast_idx & 7)); + } + trace_i82596_set_multicast(mc_count); +} + +void i82596_set_link_status(NetClientState *nc) +{ + I82596State *d = qemu_get_nic_opaque(nc); + + d->lnkst = nc->link_down ? 0 : 0x8000; +} + +static void update_scb_status(I82596State *s) +{ + s->scb_status = (s->scb_status & 0xf000) + | (s->cu_status << 8) | (s->rx_status << 4); + set_uint16(s->scb, s->scb_status); +} + + +static void i82596_s_reset(I82596State *s) +{ + trace_i82596_s_reset(s); + s->scp = 0; + s->scb_status = 0; + s->cu_status = CU_IDLE; + s->rx_status = RX_SUSPENDED; + s->cmd_p = I596_NULL; + s->lnkst = 0x8000; /* initial link state: up */ + s->ca = s->ca_active = 0; + s->send_irq = 0; +} + + +static void command_loop(I82596State *s) +{ + uint16_t cmd; + uint16_t status; + uint8_t byte_cnt; + + DBG(printf("STARTING COMMAND LOOP cmd_p=%08x\n", s->cmd_p)); + + while (s->cmd_p != I596_NULL) { + /* set status */ + status = STAT_B; + set_uint16(s->cmd_p, status); + status = STAT_C | STAT_OK; /* update, but write later */ + + cmd = get_uint16(s->cmd_p + 2); + DBG(printf("Running command %04x at %08x\n", cmd, s->cmd_p)); + + switch (cmd & 0x07) { + case CmdNOp: + break; + case CmdSASetup: + set_individual_address(s, s->cmd_p); + break; + case CmdConfigure: + byte_cnt = get_byte(s->cmd_p + 8) & 0x0f; + byte_cnt = MAX(byte_cnt, 4); + byte_cnt = MIN(byte_cnt, sizeof(s->config)); + /* copy byte_cnt max. */ + address_space_read(&address_space_memory, s->cmd_p + 8, + MEMTXATTRS_UNSPECIFIED, s->config, byte_cnt); + /* config byte according to page 35ff */ + s->config[2] &= 0x82; /* mask valid bits */ + s->config[2] |= 0x40; + s->config[7] &= 0xf7; /* clear zero bit */ + assert(I596_NOCRC_INS == 0); /* do CRC insertion */ + s->config[10] = MAX(s->config[10], 5); /* min frame length */ + s->config[12] &= 0x40; /* only full duplex field valid */ + s->config[13] |= 0x3f; /* set ones in byte 13 */ + break; + case CmdTDR: + /* get signal LINK */ + set_uint32(s->cmd_p + 8, s->lnkst); + break; + case CmdTx: + i82596_transmit(s, s->cmd_p); + break; + case CmdMulticastList: + set_multicast_list(s, s->cmd_p); + break; + case CmdDump: + case CmdDiagnose: + printf("FIXME Command %d !!\n", cmd & 7); + assert(0); + } + + /* update status */ + set_uint16(s->cmd_p, status); + + s->cmd_p = get_uint32(s->cmd_p + 4); /* get link address */ + DBG(printf("NEXT addr would be %08x\n", s->cmd_p)); + if (s->cmd_p == 0) { + s->cmd_p = I596_NULL; + } + + /* Stop when last command of the list. */ + if (cmd & CMD_EOL) { + s->cmd_p = I596_NULL; + } + /* Suspend after doing cmd? */ + if (cmd & CMD_SUSP) { + s->cu_status = CU_SUSPENDED; + printf("FIXME SUSPEND !!\n"); + } + /* Interrupt after doing cmd? */ + if (cmd & CMD_INTR) { + s->scb_status |= SCB_STATUS_CX; + } else { + s->scb_status &= ~SCB_STATUS_CX; + } + update_scb_status(s); + + /* Interrupt after doing cmd? */ + if (cmd & CMD_INTR) { + s->send_irq = 1; + } + + if (s->cu_status != CU_ACTIVE) { + break; + } + } + DBG(printf("FINISHED COMMAND LOOP\n")); + qemu_flush_queued_packets(qemu_get_queue(s->nic)); +} + +static void i82596_flush_queue_timer(void *opaque) +{ + I82596State *s = opaque; + if (0) { + timer_del(s->flush_queue_timer); + qemu_flush_queued_packets(qemu_get_queue(s->nic)); + timer_mod(s->flush_queue_timer, + qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 1000); + } +} + +static void examine_scb(I82596State *s) +{ + uint16_t command, cuc, ruc; + + /* get the scb command word */ + command = get_uint16(s->scb + 2); + cuc = (command >> 8) & 0x7; + ruc = (command >> 4) & 0x7; + DBG(printf("MAIN COMMAND %04x cuc %02x ruc %02x\n", command, cuc, ruc)); + /* and clear the scb command word */ + set_uint16(s->scb + 2, 0); + + s->scb_status &= ~(command & SCB_COMMAND_ACK_MASK); + + switch (cuc) { + case 0: /* no change */ + break; + case 1: /* CUC_START */ + s->cu_status = CU_ACTIVE; + break; + case 4: /* CUC_ABORT */ + s->cu_status = CU_SUSPENDED; + s->scb_status |= SCB_STATUS_CNA; /* CU left active state */ + break; + default: + printf("WARNING: Unknown CUC %d!\n", cuc); + } + + switch (ruc) { + case 0: /* no change */ + break; + case 1: /* RX_START */ + case 2: /* RX_RESUME */ + s->rx_status = RX_IDLE; + if (USE_TIMER) { + timer_mod(s->flush_queue_timer, qemu_clock_get_ms( + QEMU_CLOCK_VIRTUAL) + 1000); + } + break; + case 3: /* RX_SUSPEND */ + case 4: /* RX_ABORT */ + s->rx_status = RX_SUSPENDED; + s->scb_status |= SCB_STATUS_RNR; /* RU left active state */ + break; + default: + printf("WARNING: Unknown RUC %d!\n", ruc); + } + + if (command & 0x80) { /* reset bit set? */ + i82596_s_reset(s); + } + + /* execute commands from SCBL */ + if (s->cu_status != CU_SUSPENDED) { + if (s->cmd_p == I596_NULL) { + s->cmd_p = get_uint32(s->scb + 4); + } + } + + /* update scb status */ + update_scb_status(s); + + command_loop(s); +} + +static void signal_ca(I82596State *s) +{ + uint32_t iscp = 0; + + /* trace_i82596_channel_attention(s); */ + if (s->scp) { + /* CA after reset -> do init with new scp. */ + s->sysbus = get_byte(s->scp + 3); /* big endian */ + DBG(printf("SYSBUS = %08x\n", s->sysbus)); + if (((s->sysbus >> 1) & 0x03) != 2) { + printf("WARNING: NO LINEAR MODE !!\n"); + } + if ((s->sysbus >> 7)) { + printf("WARNING: 32BIT LINMODE IN B-STEPPING NOT SUPPORTED !!\n"); + } + iscp = get_uint32(s->scp + 8); + s->scb = get_uint32(iscp + 4); + set_byte(iscp + 1, 0); /* clear BUSY flag in iscp */ + s->scp = 0; + } + + s->ca++; /* count ca() */ + if (!s->ca_active) { + s->ca_active = 1; + while (s->ca) { + examine_scb(s); + s->ca--; + } + s->ca_active = 0; + } + + if (s->send_irq) { + s->send_irq = 0; + qemu_set_irq(s->irq, 1); + } +} + +void i82596_ioport_writew(void *opaque, uint32_t addr, uint32_t val) +{ + I82596State *s = opaque; + /* printf("i82596_ioport_writew addr=0x%08x val=0x%04x\n", addr, val); */ + switch (addr) { + case PORT_RESET: /* Reset */ + i82596_s_reset(s); + break; + case PORT_ALTSCP: + s->scp = val; + break; + case PORT_CA: + signal_ca(s); + break; + } +} + +uint32_t i82596_ioport_readw(void *opaque, uint32_t addr) +{ + return -1; +} + +void i82596_h_reset(void *opaque) +{ + I82596State *s = opaque; + + i82596_s_reset(s); +} + +bool i82596_can_receive(NetClientState *nc) +{ + I82596State *s = qemu_get_nic_opaque(nc); + + if (s->rx_status == RX_SUSPENDED) { + return false; + } + + if (!s->lnkst) { + return false; + } + + if (USE_TIMER && !timer_pending(s->flush_queue_timer)) { + return true; + } + + return true; +} + +#define MIN_BUF_SIZE 60 + +ssize_t i82596_receive(NetClientState *nc, const uint8_t *buf, size_t sz) +{ + I82596State *s = qemu_get_nic_opaque(nc); + uint32_t rfd_p; + uint32_t rbd; + uint16_t is_broadcast = 0; + size_t len = sz; /* length of data for guest (including CRC) */ + size_t bufsz = sz; /* length of data in buf */ + uint32_t crc; + uint8_t *crc_ptr; + uint8_t buf1[MIN_BUF_SIZE + VLAN_HLEN]; + static const uint8_t broadcast_macaddr[6] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + + DBG(printf("i82596_receive() start\n")); + + if (USE_TIMER && timer_pending(s->flush_queue_timer)) { + return 0; + } + + /* first check if receiver is enabled */ + if (s->rx_status == RX_SUSPENDED) { + trace_i82596_receive_analysis(">>> Receiving suspended"); + return -1; + } + + if (!s->lnkst) { + trace_i82596_receive_analysis(">>> Link down"); + return -1; + } + + /* Received frame smaller than configured "min frame len"? */ + if (sz < s->config[10]) { + printf("Received frame too small, %zu vs. %u bytes\n", + sz, s->config[10]); + return -1; + } + + DBG(printf("Received %lu bytes\n", sz)); + + if (I596_PROMISC) { + + /* promiscuous: receive all */ + trace_i82596_receive_analysis( + ">>> packet received in promiscuous mode"); + + } else { + + if (!memcmp(buf, broadcast_macaddr, 6)) { + /* broadcast address */ + if (I596_BC_DISABLE) { + trace_i82596_receive_analysis(">>> broadcast packet rejected"); + + return len; + } + + trace_i82596_receive_analysis(">>> broadcast packet received"); + is_broadcast = 1; + + } else if (buf[0] & 0x01) { + /* multicast */ + if (!I596_MC_ALL) { + trace_i82596_receive_analysis(">>> multicast packet rejected"); + + return len; + } + + int mcast_idx = (net_crc32(buf, ETH_ALEN) & BITS(7, 2)) >> 2; + assert(mcast_idx < 8 * sizeof(s->mult)); + + if (!(s->mult[mcast_idx >> 3] & (1 << (mcast_idx & 7)))) { + trace_i82596_receive_analysis(">>> multicast address mismatch"); + + return len; + } + + trace_i82596_receive_analysis(">>> multicast packet received"); + is_broadcast = 1; + + } else if (!memcmp(s->conf.macaddr.a, buf, 6)) { + + /* match */ + trace_i82596_receive_analysis( + ">>> physical address matching packet received"); + + } else { + + trace_i82596_receive_analysis(">>> unknown packet"); + + return len; + } + } + + /* if too small buffer, then expand it */ + if (len < MIN_BUF_SIZE + VLAN_HLEN) { + memcpy(buf1, buf, len); + memset(buf1 + len, 0, MIN_BUF_SIZE + VLAN_HLEN - len); + buf = buf1; + if (len < MIN_BUF_SIZE) { + len = MIN_BUF_SIZE; + } + bufsz = len; + } + + /* Calculate the ethernet checksum (4 bytes) */ + len += 4; + crc = cpu_to_be32(crc32(~0, buf, sz)); + crc_ptr = (uint8_t *) &crc; + + rfd_p = get_uint32(s->scb + 8); /* get Receive Frame Descriptor */ + assert(rfd_p && rfd_p != I596_NULL); + + /* get first Receive Buffer Descriptor Address */ + rbd = get_uint32(rfd_p + 8); + assert(rbd && rbd != I596_NULL); + + trace_i82596_receive_packet(len); + /* PRINT_PKTHDR("Receive", buf); */ + + while (len) { + uint16_t command, status; + uint32_t next_rfd; + + command = get_uint16(rfd_p + 2); + assert(command & CMD_FLEX); /* assert Flex Mode */ + /* get first Receive Buffer Descriptor Address */ + rbd = get_uint32(rfd_p + 8); + assert(get_uint16(rfd_p + 14) == 0); + + /* printf("Receive: rfd is %08x\n", rfd_p); */ + + while (len) { + uint16_t buffer_size, num; + uint32_t rba; + size_t bufcount, crccount; + + /* printf("Receive: rbd is %08x\n", rbd); */ + buffer_size = get_uint16(rbd + 12); + /* printf("buffer_size is 0x%x\n", buffer_size); */ + assert(buffer_size != 0); + + num = buffer_size & SIZE_MASK; + if (num > len) { + num = len; + } + rba = get_uint32(rbd + 8); + /* printf("rba is 0x%x\n", rba); */ + /* + * Calculate how many bytes we want from buf[] and how many + * from the CRC. + */ + if ((len - num) >= 4) { + /* The whole guest buffer, we haven't hit the CRC yet */ + bufcount = num; + } else { + /* All that's left of buf[] */ + bufcount = len - 4; + } + crccount = num - bufcount; + + if (bufcount > 0) { + /* Still some of the actual data buffer to transfer */ + assert(bufsz >= bufcount); + bufsz -= bufcount; + address_space_write(&address_space_memory, rba, + MEMTXATTRS_UNSPECIFIED, buf, bufcount); + rba += bufcount; + buf += bufcount; + len -= bufcount; + } + + /* Write as much of the CRC as fits */ + if (crccount > 0) { + address_space_write(&address_space_memory, rba, + MEMTXATTRS_UNSPECIFIED, crc_ptr, crccount); + rba += crccount; + crc_ptr += crccount; + len -= crccount; + } + + num |= 0x4000; /* set F BIT */ + if (len == 0) { + num |= I596_EOF; /* set EOF BIT */ + } + set_uint16(rbd + 0, num); /* write actual count with flags */ + + /* get next rbd */ + rbd = get_uint32(rbd + 4); + /* printf("Next Receive: rbd is %08x\n", rbd); */ + + if (buffer_size & I596_EOF) /* last entry */ + break; + } + + /* Housekeeping, see pg. 18 */ + next_rfd = get_uint32(rfd_p + 4); + set_uint32(next_rfd + 8, rbd); + + status = STAT_C | STAT_OK | is_broadcast; + set_uint16(rfd_p, status); + + if (command & CMD_SUSP) { /* suspend after command? */ + s->rx_status = RX_SUSPENDED; + s->scb_status |= SCB_STATUS_RNR; /* RU left active state */ + break; + } + if (command & CMD_EOL) /* was it last Frame Descriptor? */ + break; + + assert(len == 0); + } + + assert(len == 0); + + s->scb_status |= SCB_STATUS_FR; /* set "RU finished receiving frame" bit. */ + update_scb_status(s); + + /* send IRQ that we received data */ + qemu_set_irq(s->irq, 1); + /* s->send_irq = 1; */ + + if (0) { + DBG(printf("Checking:\n")); + rfd_p = get_uint32(s->scb + 8); /* get Receive Frame Descriptor */ + DBG(printf("Next Receive: rfd is %08x\n", rfd_p)); + rfd_p = get_uint32(rfd_p + 4); /* get Next Receive Frame Descriptor */ + DBG(printf("Next Receive: rfd is %08x\n", rfd_p)); + /* get first Receive Buffer Descriptor Address */ + rbd = get_uint32(rfd_p + 8); + DBG(printf("Next Receive: rbd is %08x\n", rbd)); + } + + return sz; +} + + +const VMStateDescription vmstate_i82596 = { + .name = "i82596", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT16(lnkst, I82596State), + VMSTATE_TIMER_PTR(flush_queue_timer, I82596State), + VMSTATE_END_OF_LIST() + } +}; + +void i82596_common_init(DeviceState *dev, I82596State *s, NetClientInfo *info) +{ + if (s->conf.macaddr.a[0] == 0) { + qemu_macaddr_default_if_unset(&s->conf.macaddr); + } + s->nic = qemu_new_nic(info, &s->conf, object_get_typename(OBJECT(dev)), + dev->id, s); + qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); + + if (USE_TIMER) { + s->flush_queue_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, + i82596_flush_queue_timer, s); + } + s->lnkst = 0x8000; /* initial link state: up */ +} diff --git a/hw/net/i82596.h b/hw/net/i82596.h new file mode 100644 index 0000000000..f0bbe810eb --- /dev/null +++ b/hw/net/i82596.h @@ -0,0 +1,55 @@ +#ifndef HW_I82596_H +#define HW_I82596_H + +#define I82596_IOPORT_SIZE 0x20 + +#include "exec/memory.h" +#include "exec/address-spaces.h" + +#define PORT_RESET 0x00 /* reset 82596 */ +#define PORT_SELFTEST 0x01 /* selftest */ +#define PORT_ALTSCP 0x02 /* alternate SCB address */ +#define PORT_ALTDUMP 0x03 /* Alternate DUMP address */ +#define PORT_CA 0x10 /* QEMU-internal CA signal */ + +typedef struct I82596State_st I82596State; + +struct I82596State_st { + MemoryRegion mmio; + MemoryRegion *as; + qemu_irq irq; + NICState *nic; + NICConf conf; + QEMUTimer *flush_queue_timer; + + hwaddr scp; /* pointer to SCP */ + uint8_t sysbus; + uint32_t scb; /* SCB */ + uint16_t scb_status; + uint8_t cu_status, rx_status; + uint16_t lnkst; + + uint32_t cmd_p; /* addr of current command */ + int ca; + int ca_active; + int send_irq; + + /* Hash register (multicast mask array, multiple individual addresses). */ + uint8_t mult[8]; + uint8_t config[14]; /* config bytes from CONFIGURE command */ + + uint8_t tx_buffer[0x4000]; +}; + +void i82596_h_reset(void *opaque); +void i82596_ioport_writew(void *opaque, uint32_t addr, uint32_t val); +uint32_t i82596_ioport_readw(void *opaque, uint32_t addr); +void i82596_ioport_writel(void *opaque, uint32_t addr, uint32_t val); +uint32_t i82596_ioport_readl(void *opaque, uint32_t addr); +uint32_t i82596_bcr_readw(I82596State *s, uint32_t rap); +ssize_t i82596_receive(NetClientState *nc, const uint8_t *buf, size_t size_); +bool i82596_can_receive(NetClientState *nc); +void i82596_set_link_status(NetClientState *nc); +void i82596_common_init(DeviceState *dev, I82596State *s, NetClientInfo *info); +extern const VMStateDescription vmstate_i82596; +#endif diff --git a/hw/net/imx_fec.c b/hw/net/imx_fec.c index bd99236864..a35c33683e 100644 --- a/hw/net/imx_fec.c +++ b/hw/net/imx_fec.c @@ -855,13 +855,15 @@ static void imx_enet_write(IMXFECState *s, uint32_t index, uint32_t value) break; case ENET_TGSR: /* implement clear timer flag */ - value = value & 0x0000000f; + s->regs[index] &= ~(value & 0x0000000f); /* all bits W1C */ break; case ENET_TCSR0: case ENET_TCSR1: case ENET_TCSR2: case ENET_TCSR3: - value = value & 0x000000fd; + s->regs[index] &= ~(value & 0x00000080); /* W1C bits */ + s->regs[index] &= ~0x0000007d; /* writable fields */ + s->regs[index] |= (value & 0x0000007d); break; case ENET_TCCR0: case ENET_TCCR1: @@ -901,15 +903,16 @@ static void imx_eth_write(void *opaque, hwaddr offset, uint64_t value, s->regs[index] = 0; } break; - case ENET_TDAR1: /* FALLTHROUGH */ - case ENET_TDAR2: /* FALLTHROUGH */ + case ENET_TDAR1: + case ENET_TDAR2: if (unlikely(single_tx_ring)) { qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: trying to access TDAR2 or TDAR1\n", TYPE_IMX_FEC, __func__); return; } - case ENET_TDAR: /* FALLTHROUGH */ + /* fall through */ + case ENET_TDAR: if (s->regs[ENET_ECR] & ENET_ECR_ETHEREN) { s->regs[index] = ENET_TDAR_TDAR; imx_eth_do_tx(s, index); @@ -1046,7 +1049,7 @@ static void imx_eth_write(void *opaque, hwaddr offset, uint64_t value, imx_eth_update(s); } -static int imx_eth_can_receive(NetClientState *nc) +static bool imx_eth_can_receive(NetClientState *nc) { IMXFECState *s = IMX_FEC(qemu_get_nic_opaque(nc)); @@ -1337,7 +1340,7 @@ static void imx_eth_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_imx_eth; dc->reset = imx_eth_reset; - dc->props = imx_eth_properties; + device_class_set_props(dc, imx_eth_properties); dc->realize = imx_eth_realize; dc->desc = "i.MX FEC/ENET Ethernet Controller"; } diff --git a/hw/net/lan9118.c b/hw/net/lan9118.c index ed551f2178..da7e0bb0e8 100644 --- a/hw/net/lan9118.c +++ b/hw/net/lan9118.c @@ -1369,7 +1369,7 @@ static void lan9118_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->reset = lan9118_reset; - dc->props = lan9118_properties; + device_class_set_props(dc, lan9118_properties); dc->vmsd = &vmstate_lan9118; dc->realize = lan9118_realize; } diff --git a/hw/net/lance.c b/hw/net/lance.c index 6631e2a4e0..688724db0b 100644 --- a/hw/net/lance.c +++ b/hw/net/lance.c @@ -138,7 +138,8 @@ static void lance_instance_init(Object *obj) } static Property lance_properties[] = { - DEFINE_PROP_PTR("dma", SysBusPCNetState, state.dma_opaque), + DEFINE_PROP_LINK("dma", SysBusPCNetState, state.dma_opaque, + TYPE_DEVICE, DeviceState *), DEFINE_NIC_PROPERTIES(SysBusPCNetState, state.conf), DEFINE_PROP_END_OF_LIST(), }; @@ -152,9 +153,7 @@ static void lance_class_init(ObjectClass *klass, void *data) dc->fw_name = "ethernet"; dc->reset = lance_reset; dc->vmsd = &vmstate_lance; - dc->props = lance_properties; - /* Reason: pointer property "dma" */ - dc->user_creatable = false; + device_class_set_props(dc, lance_properties); } static const TypeInfo lance_info = { diff --git a/hw/net/lasi_i82596.c b/hw/net/lasi_i82596.c new file mode 100644 index 0000000000..52637a562d --- /dev/null +++ b/hw/net/lasi_i82596.c @@ -0,0 +1,189 @@ +/* + * QEMU LASI NIC i82596 emulation + * + * Copyright (c) 2019 Helge Deller + * This work is licensed under the GNU GPL license version 2 or later. + * + * + * On PA-RISC, this is the Network part of LASI chip. + * See: + * https://parisc.wiki.kernel.org/images-parisc/7/79/Lasi_ers.pdf + */ + +#include "qemu/osdep.h" +#include "qemu/timer.h" +#include "hw/sysbus.h" +#include "net/eth.h" +#include "hw/net/lasi_82596.h" +#include "hw/net/i82596.h" +#include "trace.h" +#include "sysemu/sysemu.h" +#include "hw/qdev-properties.h" +#include "migration/vmstate.h" + +#define PA_I82596_RESET 0 /* Offsets relative to LASI-LAN-Addr.*/ +#define PA_CPU_PORT_L_ACCESS 4 +#define PA_CHANNEL_ATTENTION 8 +#define PA_GET_MACADDR 12 + +#define SWAP32(x) (((uint32_t)(x) << 16) | ((((uint32_t)(x))) >> 16)) + +static void lasi_82596_mem_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + SysBusI82596State *d = opaque; + + trace_lasi_82596_mem_writew(addr, val); + switch (addr) { + case PA_I82596_RESET: + i82596_h_reset(&d->state); + break; + case PA_CPU_PORT_L_ACCESS: + d->val_index++; + if (d->val_index == 0) { + uint32_t v = d->last_val | (val << 16); + v = v & ~0xff; + i82596_ioport_writew(&d->state, d->last_val & 0xff, v); + } + d->last_val = val; + break; + case PA_CHANNEL_ATTENTION: + i82596_ioport_writew(&d->state, PORT_CA, val); + break; + case PA_GET_MACADDR: + /* + * Provided for SeaBIOS only. Write MAC of Network card to addr @val. + * Needed for the PDC_LAN_STATION_ID_READ PDC call. + */ + address_space_write(&address_space_memory, val, + MEMTXATTRS_UNSPECIFIED, d->state.conf.macaddr.a, + ETH_ALEN); + break; + } +} + +static uint64_t lasi_82596_mem_read(void *opaque, hwaddr addr, + unsigned size) +{ + SysBusI82596State *d = opaque; + uint32_t val; + + if (addr == PA_GET_MACADDR) { + val = 0xBEEFBABE; + } else { + val = i82596_ioport_readw(&d->state, addr); + } + trace_lasi_82596_mem_readw(addr, val); + return val; +} + +static const MemoryRegionOps lasi_82596_mem_ops = { + .read = lasi_82596_mem_read, + .write = lasi_82596_mem_write, + .endianness = DEVICE_BIG_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static NetClientInfo net_lasi_82596_info = { + .type = NET_CLIENT_DRIVER_NIC, + .size = sizeof(NICState), + .can_receive = i82596_can_receive, + .receive = i82596_receive, + .link_status_changed = i82596_set_link_status, +}; + +static const VMStateDescription vmstate_lasi_82596 = { + .name = "i82596", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_STRUCT(state, SysBusI82596State, 0, vmstate_i82596, + I82596State), + VMSTATE_END_OF_LIST() + } +}; + +static void lasi_82596_realize(DeviceState *dev, Error **errp) +{ + SysBusI82596State *d = SYSBUS_I82596(dev); + I82596State *s = &d->state; + + memory_region_init_io(&s->mmio, OBJECT(d), &lasi_82596_mem_ops, d, + "lasi_82596-mmio", PA_GET_MACADDR + 4); + + i82596_common_init(dev, s, &net_lasi_82596_info); +} + +SysBusI82596State *lasi_82596_init(MemoryRegion *addr_space, + hwaddr hpa, qemu_irq lan_irq) +{ + DeviceState *dev; + SysBusI82596State *s; + static const MACAddr HP_MAC = { + .a = { 0x08, 0x00, 0x09, 0xef, 0x34, 0xf6 } }; + + qemu_check_nic_model(&nd_table[0], TYPE_LASI_82596); + dev = qdev_create(NULL, TYPE_LASI_82596); + s = SYSBUS_I82596(dev); + s->state.irq = lan_irq; + qdev_set_nic_properties(dev, &nd_table[0]); + qdev_init_nofail(dev); + s->state.conf.macaddr = HP_MAC; /* set HP MAC prefix */ + + /* LASI 82596 ports in main memory. */ + memory_region_add_subregion(addr_space, hpa, &s->state.mmio); + return s; +} + +static void lasi_82596_reset(DeviceState *dev) +{ + SysBusI82596State *d = SYSBUS_I82596(dev); + + i82596_h_reset(&d->state); +} + +static void lasi_82596_instance_init(Object *obj) +{ + SysBusI82596State *d = SYSBUS_I82596(obj); + I82596State *s = &d->state; + + device_add_bootindex_property(obj, &s->conf.bootindex, + "bootindex", "/ethernet-phy@0", + DEVICE(obj), NULL); +} + +static Property lasi_82596_properties[] = { + DEFINE_NIC_PROPERTIES(SysBusI82596State, state.conf), + DEFINE_PROP_END_OF_LIST(), +}; + +static void lasi_82596_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = lasi_82596_realize; + set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); + dc->fw_name = "ethernet"; + dc->reset = lasi_82596_reset; + dc->vmsd = &vmstate_lasi_82596; + dc->user_creatable = false; + device_class_set_props(dc, lasi_82596_properties); +} + +static const TypeInfo lasi_82596_info = { + .name = TYPE_LASI_82596, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(SysBusI82596State), + .class_init = lasi_82596_class_init, + .instance_init = lasi_82596_instance_init, +}; + +static void lasi_82596_register_types(void) +{ + type_register_static(&lasi_82596_info); +} + +type_init(lasi_82596_register_types) diff --git a/hw/net/mcf_fec.c b/hw/net/mcf_fec.c index 8fcf354a4b..9327ac8a30 100644 --- a/hw/net/mcf_fec.c +++ b/hw/net/mcf_fec.c @@ -670,7 +670,7 @@ static void mcf_fec_class_init(ObjectClass *oc, void *data) dc->realize = mcf_fec_realize; dc->desc = "MCF Fast Ethernet Controller network device"; dc->reset = mcf_fec_reset; - dc->props = mcf_fec_properties; + device_class_set_props(dc, mcf_fec_properties); } static const TypeInfo mcf_fec_info = { diff --git a/hw/net/milkymist-minimac2.c b/hw/net/milkymist-minimac2.c index 86b6d28033..1ba01754ee 100644 --- a/hw/net/milkymist-minimac2.c +++ b/hw/net/milkymist-minimac2.c @@ -530,7 +530,7 @@ static void milkymist_minimac2_class_init(ObjectClass *klass, void *data) dc->realize = milkymist_minimac2_realize; dc->reset = milkymist_minimac2_reset; dc->vmsd = &vmstate_milkymist_minimac2; - dc->props = milkymist_minimac2_properties; + device_class_set_props(dc, milkymist_minimac2_properties); } static const TypeInfo milkymist_minimac2_info = { diff --git a/hw/net/mipsnet.c b/hw/net/mipsnet.c index f7ae1ced4d..0c578c430c 100644 --- a/hw/net/mipsnet.c +++ b/hw/net/mipsnet.c @@ -9,19 +9,19 @@ /* MIPSnet register offsets */ -#define MIPSNET_DEV_ID 0x00 -#define MIPSNET_BUSY 0x08 -#define MIPSNET_RX_DATA_COUNT 0x0c -#define MIPSNET_TX_DATA_COUNT 0x10 -#define MIPSNET_INT_CTL 0x14 -# define MIPSNET_INTCTL_TXDONE 0x00000001 -# define MIPSNET_INTCTL_RXDONE 0x00000002 -# define MIPSNET_INTCTL_TESTBIT 0x80000000 -#define MIPSNET_INTERRUPT_INFO 0x18 -#define MIPSNET_RX_DATA_BUFFER 0x1c -#define MIPSNET_TX_DATA_BUFFER 0x20 +#define MIPSNET_DEV_ID 0x00 +#define MIPSNET_BUSY 0x08 +#define MIPSNET_RX_DATA_COUNT 0x0c +#define MIPSNET_TX_DATA_COUNT 0x10 +#define MIPSNET_INT_CTL 0x14 +# define MIPSNET_INTCTL_TXDONE 0x00000001 +# define MIPSNET_INTCTL_RXDONE 0x00000002 +# define MIPSNET_INTCTL_TESTBIT 0x80000000 +#define MIPSNET_INTERRUPT_INFO 0x18 +#define MIPSNET_RX_DATA_BUFFER 0x1c +#define MIPSNET_TX_DATA_BUFFER 0x20 -#define MAX_ETH_FRAME_SIZE 1514 +#define MAX_ETH_FRAME_SIZE 1514 #define TYPE_MIPS_NET "mipsnet" #define MIPS_NET(obj) OBJECT_CHECK(MIPSnetState, (obj), TYPE_MIPS_NET) @@ -64,8 +64,9 @@ static void mipsnet_update_irq(MIPSnetState *s) static int mipsnet_buffer_full(MIPSnetState *s) { - if (s->rx_count >= MAX_ETH_FRAME_SIZE) + if (s->rx_count >= MAX_ETH_FRAME_SIZE) { return 1; + } return 0; } @@ -73,18 +74,21 @@ static int mipsnet_can_receive(NetClientState *nc) { MIPSnetState *s = qemu_get_nic_opaque(nc); - if (s->busy) + if (s->busy) { return 0; + } return !mipsnet_buffer_full(s); } -static ssize_t mipsnet_receive(NetClientState *nc, const uint8_t *buf, size_t size) +static ssize_t mipsnet_receive(NetClientState *nc, + const uint8_t *buf, size_t size) { MIPSnetState *s = qemu_get_nic_opaque(nc); trace_mipsnet_receive(size); - if (!mipsnet_can_receive(nc)) + if (!mipsnet_can_receive(nc)) { return 0; + } if (size >= sizeof(s->rx_buffer)) { return 0; @@ -115,10 +119,10 @@ static uint64_t mipsnet_ioport_read(void *opaque, hwaddr addr, addr &= 0x3f; switch (addr) { case MIPSNET_DEV_ID: - ret = be32_to_cpu(0x4d495053); /* MIPS */ + ret = be32_to_cpu(0x4d495053); /* MIPS */ break; case MIPSNET_DEV_ID + 4: - ret = be32_to_cpu(0x4e455430); /* NET0 */ + ret = be32_to_cpu(0x4e455430); /* NET0 */ break; case MIPSNET_BUSY: ret = s->busy; @@ -274,7 +278,7 @@ static void mipsnet_class_init(ObjectClass *klass, void *data) dc->desc = "MIPS Simulator network device"; dc->reset = mipsnet_sysbus_reset; dc->vmsd = &vmstate_mipsnet; - dc->props = mipsnet_properties; + device_class_set_props(dc, mipsnet_properties); } static const TypeInfo mipsnet_info = { diff --git a/hw/net/ne2000-isa.c b/hw/net/ne2000-isa.c index 6fcf3d5cd9..e744eff153 100644 --- a/hw/net/ne2000-isa.c +++ b/hw/net/ne2000-isa.c @@ -89,7 +89,7 @@ static void isa_ne2000_class_initfn(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = isa_ne2000_realizefn; - dc->props = ne2000_isa_properties; + device_class_set_props(dc, ne2000_isa_properties); dc->vmsd = &vmstate_isa_ne2000; set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); } diff --git a/hw/net/ne2000-pci.c b/hw/net/ne2000-pci.c index 9a20d427e3..e11d67bf75 100644 --- a/hw/net/ne2000-pci.c +++ b/hw/net/ne2000-pci.c @@ -112,7 +112,7 @@ static void ne2000_class_init(ObjectClass *klass, void *data) k->device_id = PCI_DEVICE_ID_REALTEK_8029; k->class_id = PCI_CLASS_NETWORK_ETHERNET; dc->vmsd = &vmstate_pci_ne2000; - dc->props = ne2000_properties; + device_class_set_props(dc, ne2000_properties); set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); } diff --git a/hw/net/net_rx_pkt.c b/hw/net/net_rx_pkt.c index 98a5030ace..1e1c504e42 100644 --- a/hw/net/net_rx_pkt.c +++ b/hw/net/net_rx_pkt.c @@ -307,6 +307,20 @@ _net_rx_rss_prepare_tcp(uint8_t *rss_input, &tcphdr->th_dport, sizeof(uint16_t)); } +static inline void +_net_rx_rss_prepare_udp(uint8_t *rss_input, + struct NetRxPkt *pkt, + size_t *bytes_written) +{ + struct udp_header *udphdr = &pkt->l4hdr_info.hdr.udp; + + _net_rx_rss_add_chunk(rss_input, bytes_written, + &udphdr->uh_sport, sizeof(uint16_t)); + + _net_rx_rss_add_chunk(rss_input, bytes_written, + &udphdr->uh_dport, sizeof(uint16_t)); +} + uint32_t net_rx_pkt_calc_rss_hash(struct NetRxPkt *pkt, NetRxPktRssType type, @@ -334,7 +348,7 @@ net_rx_pkt_calc_rss_hash(struct NetRxPkt *pkt, assert(pkt->isip6); assert(pkt->istcp); trace_net_rx_pkt_rss_ip6_tcp(); - _net_rx_rss_prepare_ip6(&rss_input[0], pkt, true, &rss_length); + _net_rx_rss_prepare_ip6(&rss_input[0], pkt, false, &rss_length); _net_rx_rss_prepare_tcp(&rss_input[0], pkt, &rss_length); break; case NetPktRssIpV6: @@ -347,6 +361,34 @@ net_rx_pkt_calc_rss_hash(struct NetRxPkt *pkt, trace_net_rx_pkt_rss_ip6_ex(); _net_rx_rss_prepare_ip6(&rss_input[0], pkt, true, &rss_length); break; + case NetPktRssIpV6TcpEx: + assert(pkt->isip6); + assert(pkt->istcp); + trace_net_rx_pkt_rss_ip6_ex_tcp(); + _net_rx_rss_prepare_ip6(&rss_input[0], pkt, true, &rss_length); + _net_rx_rss_prepare_tcp(&rss_input[0], pkt, &rss_length); + break; + case NetPktRssIpV4Udp: + assert(pkt->isip4); + assert(pkt->isudp); + trace_net_rx_pkt_rss_ip4_udp(); + _net_rx_rss_prepare_ip4(&rss_input[0], pkt, &rss_length); + _net_rx_rss_prepare_udp(&rss_input[0], pkt, &rss_length); + break; + case NetPktRssIpV6Udp: + assert(pkt->isip6); + assert(pkt->isudp); + trace_net_rx_pkt_rss_ip6_udp(); + _net_rx_rss_prepare_ip6(&rss_input[0], pkt, false, &rss_length); + _net_rx_rss_prepare_udp(&rss_input[0], pkt, &rss_length); + break; + case NetPktRssIpV6UdpEx: + assert(pkt->isip6); + assert(pkt->isudp); + trace_net_rx_pkt_rss_ip6_ex_udp(); + _net_rx_rss_prepare_ip6(&rss_input[0], pkt, true, &rss_length); + _net_rx_rss_prepare_udp(&rss_input[0], pkt, &rss_length); + break; default: assert(false); break; diff --git a/hw/net/net_rx_pkt.h b/hw/net/net_rx_pkt.h index 7adf0fad51..048e3461f0 100644 --- a/hw/net/net_rx_pkt.h +++ b/hw/net/net_rx_pkt.h @@ -133,7 +133,11 @@ typedef enum { NetPktRssIpV4Tcp, NetPktRssIpV6Tcp, NetPktRssIpV6, - NetPktRssIpV6Ex + NetPktRssIpV6Ex, + NetPktRssIpV6TcpEx, + NetPktRssIpV4Udp, + NetPktRssIpV6Udp, + NetPktRssIpV6UdpEx, } NetRxPktRssType; /** diff --git a/hw/net/opencores_eth.c b/hw/net/opencores_eth.c index 84a95da1d5..2ba0dc8c2f 100644 --- a/hw/net/opencores_eth.c +++ b/hw/net/opencores_eth.c @@ -349,12 +349,11 @@ static void open_eth_reset(void *opaque) open_eth_set_link_status(qemu_get_queue(s->nic)); } -static int open_eth_can_receive(NetClientState *nc) +static bool open_eth_can_receive(NetClientState *nc) { OpenEthState *s = qemu_get_nic_opaque(nc); - return GET_REGBIT(s, MODER, RXEN) && - (s->regs[TX_BD_NUM] < 0x80); + return GET_REGBIT(s, MODER, RXEN) && (s->regs[TX_BD_NUM] < 0x80); } static ssize_t open_eth_receive(NetClientState *nc, @@ -755,7 +754,7 @@ static void open_eth_class_init(ObjectClass *klass, void *data) set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); dc->desc = "Opencores 10/100 Mbit Ethernet"; dc->reset = qdev_open_eth_reset; - dc->props = open_eth_properties; + device_class_set_props(dc, open_eth_properties); } static const TypeInfo open_eth_info = { diff --git a/hw/net/pcnet-pci.c b/hw/net/pcnet-pci.c index 4723c30c79..d1f31e0272 100644 --- a/hw/net/pcnet-pci.c +++ b/hw/net/pcnet-pci.c @@ -231,7 +231,7 @@ static void pci_pcnet_realize(PCIDevice *pci_dev, Error **errp) s->irq = pci_allocate_irq(pci_dev); s->phys_mem_read = pci_physical_memory_read; s->phys_mem_write = pci_physical_memory_write; - s->dma_opaque = pci_dev; + s->dma_opaque = DEVICE(pci_dev); pcnet_common_init(DEVICE(pci_dev), s, &net_pci_pcnet_info); } @@ -272,7 +272,7 @@ static void pcnet_class_init(ObjectClass *klass, void *data) k->class_id = PCI_CLASS_NETWORK_ETHERNET; dc->reset = pci_reset; dc->vmsd = &vmstate_pci_pcnet; - dc->props = pcnet_properties; + device_class_set_props(dc, pcnet_properties); set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); } diff --git a/hw/net/pcnet.h b/hw/net/pcnet.h index 28d19a5c6f..f49b213c57 100644 --- a/hw/net/pcnet.h +++ b/hw/net/pcnet.h @@ -50,7 +50,7 @@ struct PCNetState_st { uint8_t *buf, int len, int do_bswap); void (*phys_mem_write)(void *dma_opaque, hwaddr addr, uint8_t *buf, int len, int do_bswap); - void *dma_opaque; + DeviceState *dma_opaque; int tx_busy; int looptest; }; diff --git a/hw/net/rocker/rocker.c b/hw/net/rocker/rocker.c index 50f9e33e2f..15d66f6cbc 100644 --- a/hw/net/rocker/rocker.c +++ b/hw/net/rocker/rocker.c @@ -27,6 +27,7 @@ #include "qemu/iov.h" #include "qemu/module.h" #include "qemu/bitops.h" +#include "qemu/log.h" #include "rocker.h" #include "rocker_hw.h" @@ -207,14 +208,22 @@ static int tx_consume(Rocker *r, DescInfo *info) if (tlvs[ROCKER_TLV_TX_L3_CSUM_OFF]) { tx_l3_csum_off = rocker_tlv_get_le16(tlvs[ROCKER_TLV_TX_L3_CSUM_OFF]); + qemu_log_mask(LOG_UNIMP, "rocker %s: L3 not implemented" + " (cksum off: %u)\n", + __func__, tx_l3_csum_off); } if (tlvs[ROCKER_TLV_TX_TSO_MSS]) { tx_tso_mss = rocker_tlv_get_le16(tlvs[ROCKER_TLV_TX_TSO_MSS]); + qemu_log_mask(LOG_UNIMP, "rocker %s: TSO not implemented (MSS: %u)\n", + __func__, tx_tso_mss); } if (tlvs[ROCKER_TLV_TX_TSO_HDR_LEN]) { tx_tso_hdr_len = rocker_tlv_get_le16(tlvs[ROCKER_TLV_TX_TSO_HDR_LEN]); + qemu_log_mask(LOG_UNIMP, "rocker %s: TSO not implemented" + " (hdr length: %u)\n", + __func__, tx_tso_hdr_len); } rocker_tlv_for_each_nested(tlv_frag, tlvs[ROCKER_TLV_TX_FRAGS], rem) { @@ -249,12 +258,6 @@ static int tx_consume(Rocker *r, DescInfo *info) iovcnt++; } - if (iovcnt) { - /* XXX perform Tx offloads */ - /* XXX silence compiler for now */ - tx_l3_csum_off += tx_tso_mss = tx_tso_hdr_len = 0; - } - err = fp_port_eg(r->fp_port[port], iov, iovcnt); err_too_many_frags: @@ -1519,7 +1522,7 @@ static void rocker_class_init(ObjectClass *klass, void *data) set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); dc->desc = "Rocker Switch"; dc->reset = rocker_reset; - dc->props = rocker_properties; + device_class_set_props(dc, rocker_properties); dc->vmsd = &rocker_vmsd; } diff --git a/hw/net/rtl8139.c b/hw/net/rtl8139.c index 88a97d756d..70aca7ec26 100644 --- a/hw/net/rtl8139.c +++ b/hw/net/rtl8139.c @@ -793,26 +793,28 @@ static bool rtl8139_cp_rx_valid(RTL8139State *s) return !(s->RxRingAddrLO == 0 && s->RxRingAddrHI == 0); } -static int rtl8139_can_receive(NetClientState *nc) +static bool rtl8139_can_receive(NetClientState *nc) { RTL8139State *s = qemu_get_nic_opaque(nc); int avail; /* Receive (drop) packets if card is disabled. */ - if (!s->clock_enabled) - return 1; - if (!rtl8139_receiver_enabled(s)) - return 1; + if (!s->clock_enabled) { + return true; + } + if (!rtl8139_receiver_enabled(s)) { + return true; + } if (rtl8139_cp_receiver_enabled(s) && rtl8139_cp_rx_valid(s)) { /* ??? Flow control not implemented in c+ mode. This is a hack to work around slirp deficiencies anyway. */ - return 1; - } else { - avail = MOD2(s->RxBufferSize + s->RxBufPtr - s->RxBufAddr, - s->RxBufferSize); - return (avail == 0 || avail >= 1514 || (s->IntrMask & RxOverflow)); + return true; } + + avail = MOD2(s->RxBufferSize + s->RxBufPtr - s->RxBufAddr, + s->RxBufferSize); + return avail == 0 || avail >= 1514 || (s->IntrMask & RxOverflow); } static ssize_t rtl8139_do_receive(NetClientState *nc, const uint8_t *buf, size_t size_, int do_interrupt) @@ -3435,7 +3437,7 @@ static void rtl8139_class_init(ObjectClass *klass, void *data) k->class_id = PCI_CLASS_NETWORK_ETHERNET; dc->reset = rtl8139_reset; dc->vmsd = &vmstate_rtl8139; - dc->props = rtl8139_properties; + device_class_set_props(dc, rtl8139_properties); set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); } diff --git a/hw/net/smc91c111.c b/hw/net/smc91c111.c index e574635969..b3240b9335 100644 --- a/hw/net/smc91c111.c +++ b/hw/net/smc91c111.c @@ -130,16 +130,16 @@ static void smc91c111_update(smc91c111_state *s) qemu_set_irq(s->irq, level); } -static int smc91c111_can_receive(smc91c111_state *s) +static bool smc91c111_can_receive(smc91c111_state *s) { if ((s->rcr & RCR_RXEN) == 0 || (s->rcr & RCR_SOFT_RST)) { - return 1; + return true; } if (s->allocated == (1 << NUM_PACKETS) - 1 || s->rx_fifo_len == NUM_PACKETS) { - return 0; + return false; } - return 1; + return true; } static inline void smc91c111_flush_queued_packets(smc91c111_state *s) @@ -667,7 +667,7 @@ static void smc91c111_writefn(void *opaque, hwaddr addr, } } -static int smc91c111_can_receive_nc(NetClientState *nc) +static bool smc91c111_can_receive_nc(NetClientState *nc) { smc91c111_state *s = qemu_get_nic_opaque(nc); @@ -798,7 +798,7 @@ static void smc91c111_class_init(ObjectClass *klass, void *data) dc->realize = smc91c111_realize; dc->reset = smc91c111_reset; dc->vmsd = &vmstate_smc91c111; - dc->props = smc91c111_properties; + device_class_set_props(dc, smc91c111_properties); } static const TypeInfo smc91c111_info = { diff --git a/hw/net/spapr_llan.c b/hw/net/spapr_llan.c index 3d96884d66..a2377025a7 100644 --- a/hw/net/spapr_llan.c +++ b/hw/net/spapr_llan.c @@ -110,11 +110,11 @@ typedef struct SpaprVioVlan { RxBufPool *rx_pool[RX_MAX_POOLS]; /* Receive buffer descriptor pools */ } SpaprVioVlan; -static int spapr_vlan_can_receive(NetClientState *nc) +static bool spapr_vlan_can_receive(NetClientState *nc) { SpaprVioVlan *dev = qemu_get_nic_opaque(nc); - return (dev->isopen && dev->rx_bufs > 0); + return dev->isopen && dev->rx_bufs > 0; } /** @@ -856,7 +856,7 @@ static void spapr_vlan_class_init(ObjectClass *klass, void *data) k->dt_compatible = "IBM,l-lan"; k->signal_mask = 0x1; set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); - dc->props = spapr_vlan_properties; + device_class_set_props(dc, spapr_vlan_properties); k->rtce_window_size = 0x10000000; dc->vmsd = &vmstate_spapr_llan; } diff --git a/hw/net/stellaris_enet.c b/hw/net/stellaris_enet.c index 3aca2a09f3..cb6e2509ea 100644 --- a/hw/net/stellaris_enet.c +++ b/hw/net/stellaris_enet.c @@ -507,7 +507,7 @@ static void stellaris_enet_class_init(ObjectClass *klass, void *data) dc->realize = stellaris_enet_realize; dc->reset = stellaris_enet_reset; - dc->props = stellaris_enet_properties; + device_class_set_props(dc, stellaris_enet_properties); dc->vmsd = &vmstate_stellaris_enet; } diff --git a/hw/net/sungem.c b/hw/net/sungem.c index f31d41ac5b..b01197d952 100644 --- a/hw/net/sungem.c +++ b/hw/net/sungem.c @@ -433,7 +433,7 @@ static bool sungem_rx_full(SunGEMState *s, uint32_t kick, uint32_t done) return kick == ((done + 1) & s->rx_mask); } -static int sungem_can_receive(NetClientState *nc) +static bool sungem_can_receive(NetClientState *nc) { SunGEMState *s = qemu_get_nic_opaque(nc); uint32_t kick, done, rxdma_cfg, rxmac_cfg; @@ -445,11 +445,11 @@ static int sungem_can_receive(NetClientState *nc) /* If MAC disabled, can't receive */ if ((rxmac_cfg & MAC_RXCFG_ENAB) == 0) { trace_sungem_rx_mac_disabled(); - return 0; + return false; } if ((rxdma_cfg & RXDMA_CFG_ENABLE) == 0) { trace_sungem_rx_txdma_disabled(); - return 0; + return false; } /* Check RX availability */ @@ -1429,7 +1429,7 @@ static void sungem_class_init(ObjectClass *klass, void *data) k->class_id = PCI_CLASS_NETWORK_ETHERNET; dc->vmsd = &vmstate_sungem; dc->reset = sungem_reset; - dc->props = sungem_properties; + device_class_set_props(dc, sungem_properties); set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); } diff --git a/hw/net/sunhme.c b/hw/net/sunhme.c index 2243b7cf7b..9c38583180 100644 --- a/hw/net/sunhme.c +++ b/hw/net/sunhme.c @@ -657,11 +657,11 @@ static void sunhme_transmit(SunHMEState *s) sunhme_update_irq(s); } -static int sunhme_can_receive(NetClientState *nc) +static bool sunhme_can_receive(NetClientState *nc) { SunHMEState *s = qemu_get_nic_opaque(nc); - return s->macregs[HME_MACI_RXCFG >> 2] & HME_MAC_RXCFG_ENABLE; + return !!(s->macregs[HME_MACI_RXCFG >> 2] & HME_MAC_RXCFG_ENABLE); } static void sunhme_link_status_changed(NetClientState *nc) @@ -958,7 +958,7 @@ static void sunhme_class_init(ObjectClass *klass, void *data) k->class_id = PCI_CLASS_NETWORK_ETHERNET; dc->vmsd = &vmstate_hme; dc->reset = sunhme_reset; - dc->props = sunhme_properties; + device_class_set_props(dc, sunhme_properties); set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); } diff --git a/hw/net/trace-events b/hw/net/trace-events index e70f12bee1..e18f883cfd 100644 --- a/hw/net/trace-events +++ b/hw/net/trace-events @@ -1,5 +1,15 @@ # See docs/devel/tracing.txt for syntax documentation. +# allwinner-sun8i-emac.c +allwinner_sun8i_emac_mii_write_reg(uint32_t reg, uint32_t value) "MII write: reg=0x%" PRIx32 " value=0x%" PRIx32 +allwinner_sun8i_emac_mii_read_reg(uint32_t reg, uint32_t value) "MII read: reg=0x%" PRIx32 " value=0x%" PRIx32 +allwinner_sun8i_emac_receive(uint32_t desc, uint32_t paddr, uint32_t bytes) "RX packet: desc=0x%" PRIx32 " paddr=0x%" PRIx32 " bytes=%" PRIu32 +allwinner_sun8i_emac_transmit(uint32_t desc, uint32_t paddr, uint32_t bytes) "TX packet: desc=0x%" PRIx32 " paddr=0x%" PRIx32 " bytes=%" PRIu32 +allwinner_sun8i_emac_reset(void) "HW reset" +allwinner_sun8i_emac_set_link(bool active) "Set link: active=%u" +allwinner_sun8i_emac_read(uint64_t offset, uint64_t val) "MMIO read: offset=0x%" PRIx64 " value=0x%" PRIx64 +allwinner_sun8i_emac_write(uint64_t offset, uint64_t val) "MMIO write: offset=0x%" PRIx64 " value=0x%" PRIx64 + # etraxfs_eth.c mdio_phy_read(int regnum, uint16_t value) "read phy_reg:%d value:0x%04x" mdio_phy_write(int regnum, uint16_t value) "write phy_reg:%d value:0x%04x" @@ -92,9 +102,13 @@ net_rx_pkt_l3_csum_validate_csum(size_t l3hdr_off, uint32_t csl, uint32_t cntr, net_rx_pkt_rss_ip4(void) "Calculating IPv4 RSS hash" net_rx_pkt_rss_ip4_tcp(void) "Calculating IPv4/TCP RSS hash" +net_rx_pkt_rss_ip4_udp(void) "Calculating IPv4/UDP RSS hash" net_rx_pkt_rss_ip6_tcp(void) "Calculating IPv6/TCP RSS hash" +net_rx_pkt_rss_ip6_udp(void) "Calculating IPv6/UDP RSS hash" net_rx_pkt_rss_ip6(void) "Calculating IPv6 RSS hash" net_rx_pkt_rss_ip6_ex(void) "Calculating IPv6/EX RSS hash" +net_rx_pkt_rss_ip6_ex_tcp(void) "Calculating IPv6/EX/TCP RSS hash" +net_rx_pkt_rss_ip6_ex_udp(void) "Calculating IPv6/EX/UDP RSS hash" net_rx_pkt_rss_hash(size_t rss_length, uint32_t rss_hash) "RSS hash for %zu bytes: 0x%X" net_rx_pkt_rss_add_chunk(void* ptr, size_t size, size_t input_offset) "Add RSS chunk %p, %zu bytes, RSS input offset %zu bytes" @@ -381,3 +395,16 @@ tulip_mii_read(int phy, int reg, uint16_t data) "phy 0x%x, reg 0x%x data 0x%04x" tulip_reset(void) "" tulip_setup_frame(void) "" tulip_setup_filter(int n, uint8_t a, uint8_t b, uint8_t c, uint8_t d, uint8_t e, uint8_t f) "%d: %02x:%02x:%02x:%02x:%02x:%02x" + +# lasi_i82596.c +lasi_82596_mem_readw(uint64_t addr, uint32_t ret) "addr=0x%"PRIx64" val=0x%04x" +lasi_82596_mem_writew(uint64_t addr, uint32_t val) "addr=0x%"PRIx64" val=0x%04x" + +# i82596.c +i82596_s_reset(void *s) "%p Reset chip" +i82596_transmit(uint32_t size, uint32_t addr) "size %u from addr 0x%04x" +i82596_receive_analysis(const char *s) "%s" +i82596_receive_packet(size_t sz) "len=%zu" +i82596_new_mac(const char *id_with_mac) "New MAC for: %s" +i82596_set_multicast(uint16_t count) "Added %d multicast entries" +i82596_channel_attention(void *s) "%p: Received CHANNEL ATTENTION" diff --git a/hw/net/tulip.c b/hw/net/tulip.c index f85f54341f..1295f51d07 100644 --- a/hw/net/tulip.c +++ b/hw/net/tulip.c @@ -170,6 +170,10 @@ static void tulip_copy_rx_bytes(TULIPState *s, struct tulip_descriptor *desc) } else { len = s->rx_frame_len; } + + if (s->rx_frame_len + len > sizeof(s->rx_frame)) { + return; + } pci_dma_write(&s->dev, desc->buf_addr1, s->rx_frame + (s->rx_frame_size - s->rx_frame_len), len); s->rx_frame_len -= len; @@ -181,6 +185,10 @@ static void tulip_copy_rx_bytes(TULIPState *s, struct tulip_descriptor *desc) } else { len = s->rx_frame_len; } + + if (s->rx_frame_len + len > sizeof(s->rx_frame)) { + return; + } pci_dma_write(&s->dev, desc->buf_addr2, s->rx_frame + (s->rx_frame_size - s->rx_frame_len), len); s->rx_frame_len -= len; @@ -227,7 +235,8 @@ static ssize_t tulip_receive(TULIPState *s, const uint8_t *buf, size_t size) trace_tulip_receive(buf, size); - if (size < 14 || size > 2048 || s->rx_frame_len || tulip_rx_stopped(s)) { + if (size < 14 || size > sizeof(s->rx_frame) - 4 + || s->rx_frame_len || tulip_rx_stopped(s)) { return 0; } @@ -275,7 +284,6 @@ static ssize_t tulip_receive_nc(NetClientState *nc, return tulip_receive(qemu_get_nic_opaque(nc), buf, size); } - static NetClientInfo net_tulip_info = { .type = NET_CLIENT_DRIVER_NIC, .size = sizeof(NICState), @@ -558,7 +566,7 @@ static void tulip_tx(TULIPState *s, struct tulip_descriptor *desc) if ((s->csr[6] >> CSR6_OM_SHIFT) & CSR6_OM_MASK) { /* Internal or external Loopback */ tulip_receive(s, s->tx_frame, s->tx_frame_len); - } else { + } else if (s->tx_frame_len <= sizeof(s->tx_frame)) { qemu_send_packet(qemu_get_queue(s->nic), s->tx_frame, s->tx_frame_len); } @@ -570,23 +578,31 @@ static void tulip_tx(TULIPState *s, struct tulip_descriptor *desc) } } -static void tulip_copy_tx_buffers(TULIPState *s, struct tulip_descriptor *desc) +static int tulip_copy_tx_buffers(TULIPState *s, struct tulip_descriptor *desc) { int len1 = (desc->control >> TDES1_BUF1_SIZE_SHIFT) & TDES1_BUF1_SIZE_MASK; int len2 = (desc->control >> TDES1_BUF2_SIZE_SHIFT) & TDES1_BUF2_SIZE_MASK; + if (s->tx_frame_len + len1 > sizeof(s->tx_frame)) { + return -1; + } if (len1) { pci_dma_read(&s->dev, desc->buf_addr1, s->tx_frame + s->tx_frame_len, len1); s->tx_frame_len += len1; } + if (s->tx_frame_len + len2 > sizeof(s->tx_frame)) { + return -1; + } if (len2) { pci_dma_read(&s->dev, desc->buf_addr2, s->tx_frame + s->tx_frame_len, len2); s->tx_frame_len += len2; } desc->status = (len1 + len2) ? 0 : 0x7fffffff; + + return 0; } static void tulip_setup_filter_addr(TULIPState *s, uint8_t *buf, int n) @@ -651,13 +667,15 @@ static uint32_t tulip_ts(TULIPState *s) static void tulip_xmit_list_update(TULIPState *s) { +#define TULIP_DESC_MAX 128 + uint8_t i = 0; struct tulip_descriptor desc; if (tulip_ts(s) != CSR5_TS_SUSPENDED) { return; } - for (;;) { + for (i = 0; i < TULIP_DESC_MAX; i++) { tulip_desc_read(s, s->current_tx_desc, &desc); tulip_dump_tx_descriptor(s, &desc); @@ -675,10 +693,10 @@ static void tulip_xmit_list_update(TULIPState *s) s->tx_frame_len = 0; } - tulip_copy_tx_buffers(s, &desc); - - if (desc.control & TDES1_LS) { - tulip_tx(s, &desc); + if (!tulip_copy_tx_buffers(s, &desc)) { + if (desc.control & TDES1_LS) { + tulip_tx(s, &desc); + } } } tulip_desc_write(s, s->current_tx_desc, &desc); @@ -1004,7 +1022,7 @@ static void tulip_class_init(ObjectClass *klass, void *data) k->subsystem_id = 0x104f; k->class_id = PCI_CLASS_NETWORK_ETHERNET; dc->vmsd = &vmstate_pci_tulip; - dc->props = tulip_properties; + device_class_set_props(dc, tulip_properties); dc->reset = tulip_qdev_reset; set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); } diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index db3d7c38e6..a46e3b37a7 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -1234,26 +1234,26 @@ static void virtio_net_handle_rx(VirtIODevice *vdev, VirtQueue *vq) qemu_flush_queued_packets(qemu_get_subqueue(n->nic, queue_index)); } -static int virtio_net_can_receive(NetClientState *nc) +static bool virtio_net_can_receive(NetClientState *nc) { VirtIONet *n = qemu_get_nic_opaque(nc); VirtIODevice *vdev = VIRTIO_DEVICE(n); VirtIONetQueue *q = virtio_net_get_subqueue(nc); if (!vdev->vm_running) { - return 0; + return false; } if (nc->queue_index >= n->curr_queues) { - return 0; + return false; } if (!virtio_queue_ready(q->rx_vq) || !(vdev->status & VIRTIO_CONFIG_S_DRIVER_OK)) { - return 0; + return false; } - return 1; + return true; } static int virtio_net_has_buffers(VirtIONetQueue *q, int bufsize) @@ -2853,7 +2853,8 @@ static void virtio_net_handle_migration_primary(VirtIONet *n, if (migration_in_setup(s) && !should_be_hidden) { if (failover_unplug_primary(n)) { - vmstate_unregister(n->primary_dev, qdev_get_vmsd(n->primary_dev), + vmstate_unregister(VMSTATE_IF(n->primary_dev), + qdev_get_vmsd(n->primary_dev), n->primary_dev); qapi_event_send_unplug_primary(n->primary_device_id); atomic_set(&n->primary_should_be_hidden, true); @@ -3101,7 +3102,8 @@ static void virtio_net_device_unrealize(DeviceState *dev, Error **errp) for (i = 0; i < max_queues; i++) { virtio_net_del_queue(n, i); } - + /* delete also control vq */ + virtio_del_queue(vdev, max_queues * 2); qemu_announce_timer_del(&n->announce_timer, false); g_free(n->vqs); qemu_del_nic(n->nic); @@ -3234,7 +3236,7 @@ static void virtio_net_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); - dc->props = virtio_net_properties; + device_class_set_props(dc, virtio_net_properties); dc->vmsd = &vmstate_virtio_net; set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); vdc->realize = virtio_net_device_realize; diff --git a/hw/net/vmxnet3.c b/hw/net/vmxnet3.c index 39ff6624c5..6d91cd8309 100644 --- a/hw/net/vmxnet3.c +++ b/hw/net/vmxnet3.c @@ -2506,7 +2506,7 @@ static void vmxnet3_class_init(ObjectClass *class, void *data) dc->desc = "VMWare Paravirtualized Ethernet v3"; dc->reset = vmxnet3_qdev_reset; dc->vmsd = &vmstate_vmxnet3; - dc->props = vmxnet3_properties; + device_class_set_props(dc, vmxnet3_properties); set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); } diff --git a/hw/net/xgmac.c b/hw/net/xgmac.c index 2ea8d2ec72..574dd47b41 100644 --- a/hw/net/xgmac.c +++ b/hw/net/xgmac.c @@ -414,7 +414,7 @@ static void xgmac_enet_class_init(ObjectClass *klass, void *data) dc->realize = xgmac_enet_realize; dc->vmsd = &vmstate_xgmac; - dc->props = xgmac_properties; + device_class_set_props(dc, xgmac_properties); } static const TypeInfo xgmac_enet_info = { diff --git a/hw/net/xilinx_axienet.c b/hw/net/xilinx_axienet.c index 2c8c065401..704788811a 100644 --- a/hw/net/xilinx_axienet.c +++ b/hw/net/xilinx_axienet.c @@ -1025,7 +1025,7 @@ static void xilinx_enet_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = xilinx_enet_realize; - dc->props = xilinx_enet_properties; + device_class_set_props(dc, xilinx_enet_properties); dc->reset = xilinx_axienet_reset; } diff --git a/hw/net/xilinx_ethlite.c b/hw/net/xilinx_ethlite.c index 384e72b157..71d16fef3d 100644 --- a/hw/net/xilinx_ethlite.c +++ b/hw/net/xilinx_ethlite.c @@ -175,7 +175,7 @@ static const MemoryRegionOps eth_ops = { } }; -static int eth_can_rx(NetClientState *nc) +static bool eth_can_rx(NetClientState *nc) { struct xlx_ethlite *s = qemu_get_nic_opaque(nc); unsigned int rxbase = s->rxbuf * (0x800 / 4); @@ -262,7 +262,7 @@ static void xilinx_ethlite_class_init(ObjectClass *klass, void *data) dc->realize = xilinx_ethlite_realize; dc->reset = xilinx_ethlite_reset; - dc->props = xilinx_ethlite_properties; + device_class_set_props(dc, xilinx_ethlite_properties); } static const TypeInfo xilinx_ethlite_info = { diff --git a/hw/nios2/10m50_devboard.c b/hw/nios2/10m50_devboard.c index ad8b2fc670..33dc2bf511 100644 --- a/hw/nios2/10m50_devboard.c +++ b/hw/nios2/10m50_devboard.c @@ -120,7 +120,7 @@ static void nios2_10m50_ghrd_machine_init(struct MachineClass *mc) { mc->desc = "Altera 10M50 GHRD Nios II design"; mc->init = nios2_10m50_ghrd_init; - mc->is_default = 1; + mc->is_default = true; } DEFINE_MACHINE("10m50-ghrd", nios2_10m50_ghrd_machine_init); diff --git a/hw/nios2/boot.c b/hw/nios2/boot.c index d78bc9ed0e..88224aa84c 100644 --- a/hw/nios2/boot.c +++ b/hw/nios2/boot.c @@ -109,6 +109,7 @@ static int nios2_load_dtb(struct nios2_boot_info bi, const uint32_t ramsize, } cpu_physical_memory_write(bi.fdt, fdt, fdt_size); + g_free(fdt); return fdt_size; } @@ -147,7 +148,7 @@ void nios2_load_kernel(Nios2CPU *cpu, hwaddr ddr_base, /* Boots a kernel elf binary. */ kernel_size = load_elf(kernel_filename, NULL, NULL, NULL, - &entry, &low, &high, + &entry, &low, &high, NULL, big_endian, EM_ALTERA_NIOS2, 0, 0); if ((uint32_t)entry == 0xc0000000) { /* @@ -158,7 +159,7 @@ void nios2_load_kernel(Nios2CPU *cpu, hwaddr ddr_base, */ kernel_size = load_elf(kernel_filename, NULL, translate_kernel_address, NULL, - &entry, NULL, NULL, + &entry, NULL, NULL, NULL, big_endian, EM_ALTERA_NIOS2, 0, 0); boot_info.bootstrap_pc = ddr_base + 0xc0000000 + (entry & 0x07ffffff); diff --git a/hw/nvram/Kconfig b/hw/nvram/Kconfig index ebaa749ce9..e872fcb194 100644 --- a/hw/nvram/Kconfig +++ b/hw/nvram/Kconfig @@ -7,3 +7,11 @@ config AT24C config MAC_NVRAM bool + select CHRP_NVRAM + +# NMC93XX uses the NS uWire interface (similar to SPI but less configurable) +config NMC93XX_EEPROM + bool + +config CHRP_NVRAM + bool diff --git a/hw/nvram/Makefile.objs b/hw/nvram/Makefile.objs index 26f7b4ca35..f3ad921382 100644 --- a/hw/nvram/Makefile.objs +++ b/hw/nvram/Makefile.objs @@ -1,8 +1,8 @@ common-obj-$(CONFIG_DS1225Y) += ds1225y.o -common-obj-y += eeprom93xx.o +common-obj-$(CONFIG_NMC93XX_EEPROM) += eeprom93xx.o common-obj-$(CONFIG_AT24C) += eeprom_at24c.o common-obj-y += fw_cfg.o -common-obj-y += chrp_nvram.o +common-obj-$(CONFIG_CHRP_NVRAM) += chrp_nvram.o common-obj-$(CONFIG_MAC_NVRAM) += mac_nvram.o +common-obj-$(CONFIG_NRF51_SOC) += nrf51_nvm.o obj-$(CONFIG_PSERIES) += spapr_nvram.o -obj-$(CONFIG_NRF51_SOC) += nrf51_nvm.o diff --git a/hw/nvram/ds1225y.c b/hw/nvram/ds1225y.c index 934e09bf75..d5cb922287 100644 --- a/hw/nvram/ds1225y.c +++ b/hw/nvram/ds1225y.c @@ -153,7 +153,7 @@ static void nvram_sysbus_class_init(ObjectClass *klass, void *data) dc->realize = nvram_sysbus_realize; dc->vmsd = &vmstate_nvram; - dc->props = nvram_sysbus_properties; + device_class_set_props(dc, nvram_sysbus_properties); } static const TypeInfo nvram_sysbus_info = { diff --git a/hw/nvram/eeprom93xx.c b/hw/nvram/eeprom93xx.c index 5b01b9b03f..ca6f591c84 100644 --- a/hw/nvram/eeprom93xx.c +++ b/hw/nvram/eeprom93xx.c @@ -86,7 +86,7 @@ struct _eeprom_t { uint8_t addrbits; uint16_t size; uint16_t data; - uint16_t contents[0]; + uint16_t contents[]; }; /* Code for saving and restoring of EEPROM state. */ @@ -321,7 +321,7 @@ eeprom_t *eeprom93xx_new(DeviceState *dev, uint16_t nwords) /* Output DO is tristate, read results in 1. */ eeprom->eedo = 1; logout("eeprom = 0x%p, nwords = %u\n", eeprom, nwords); - vmstate_register(dev, 0, &vmstate_eeprom, eeprom); + vmstate_register(VMSTATE_IF(dev), 0, &vmstate_eeprom, eeprom); return eeprom; } @@ -329,7 +329,7 @@ void eeprom93xx_free(DeviceState *dev, eeprom_t *eeprom) { /* Destroy EEPROM. */ logout("eeprom = 0x%p\n", eeprom); - vmstate_unregister(dev, &vmstate_eeprom, eeprom); + vmstate_unregister(VMSTATE_IF(dev), &vmstate_eeprom, eeprom); g_free(eeprom); } diff --git a/hw/nvram/eeprom_at24c.c b/hw/nvram/eeprom_at24c.c index 3416367523..d46a2bec3f 100644 --- a/hw/nvram/eeprom_at24c.c +++ b/hw/nvram/eeprom_at24c.c @@ -182,7 +182,7 @@ void at24c_eeprom_class_init(ObjectClass *klass, void *data) k->recv = &at24c_eeprom_recv; k->send = &at24c_eeprom_send; - dc->props = at24c_eeprom_props; + device_class_set_props(dc, at24c_eeprom_props); dc->reset = at24c_eeprom_reset; } diff --git a/hw/nvram/fw_cfg.c b/hw/nvram/fw_cfg.c index 5d879c471e..4be6c9d9fd 100644 --- a/hw/nvram/fw_cfg.c +++ b/hw/nvram/fw_cfg.c @@ -39,6 +39,7 @@ #include "qemu/config-file.h" #include "qemu/cutils.h" #include "qapi/error.h" +#include "hw/acpi/aml-build.h" #define FW_CFG_FILE_SLOTS_DFLT 0x20 @@ -610,6 +611,55 @@ bool fw_cfg_dma_enabled(void *opaque) return s->dma_enabled; } +static bool fw_cfg_acpi_mr_restore(void *opaque) +{ + FWCfgState *s = opaque; + bool mr_aligned; + + mr_aligned = QEMU_IS_ALIGNED(s->table_mr_size, qemu_real_host_page_size) && + QEMU_IS_ALIGNED(s->linker_mr_size, qemu_real_host_page_size) && + QEMU_IS_ALIGNED(s->rsdp_mr_size, qemu_real_host_page_size); + return s->acpi_mr_restore && !mr_aligned; +} + +static void fw_cfg_update_mr(FWCfgState *s, uint16_t key, size_t size) +{ + MemoryRegion *mr; + ram_addr_t offset; + int arch = !!(key & FW_CFG_ARCH_LOCAL); + void *ptr; + + key &= FW_CFG_ENTRY_MASK; + assert(key < fw_cfg_max_entry(s)); + + ptr = s->entries[arch][key].data; + mr = memory_region_from_host(ptr, &offset); + + memory_region_ram_resize(mr, size, &error_abort); +} + +static int fw_cfg_acpi_mr_restore_post_load(void *opaque, int version_id) +{ + FWCfgState *s = opaque; + int i, index; + + assert(s->files); + + index = be32_to_cpu(s->files->count); + + for (i = 0; i < index; i++) { + if (!strcmp(s->files->f[i].name, ACPI_BUILD_TABLE_FILE)) { + fw_cfg_update_mr(s, FW_CFG_FILE_FIRST + i, s->table_mr_size); + } else if (!strcmp(s->files->f[i].name, ACPI_BUILD_LOADER_FILE)) { + fw_cfg_update_mr(s, FW_CFG_FILE_FIRST + i, s->linker_mr_size); + } else if (!strcmp(s->files->f[i].name, ACPI_BUILD_RSDP_FILE)) { + fw_cfg_update_mr(s, FW_CFG_FILE_FIRST + i, s->rsdp_mr_size); + } + } + + return 0; +} + static const VMStateDescription vmstate_fw_cfg_dma = { .name = "fw_cfg/dma", .needed = fw_cfg_dma_enabled, @@ -619,6 +669,20 @@ static const VMStateDescription vmstate_fw_cfg_dma = { }, }; +static const VMStateDescription vmstate_fw_cfg_acpi_mr = { + .name = "fw_cfg/acpi_mr", + .version_id = 1, + .minimum_version_id = 1, + .needed = fw_cfg_acpi_mr_restore, + .post_load = fw_cfg_acpi_mr_restore_post_load, + .fields = (VMStateField[]) { + VMSTATE_UINT64(table_mr_size, FWCfgState), + VMSTATE_UINT64(linker_mr_size, FWCfgState), + VMSTATE_UINT64(rsdp_mr_size, FWCfgState), + VMSTATE_END_OF_LIST() + }, +}; + static const VMStateDescription vmstate_fw_cfg = { .name = "fw_cfg", .version_id = 2, @@ -631,6 +695,7 @@ static const VMStateDescription vmstate_fw_cfg = { }, .subsections = (const VMStateDescription*[]) { &vmstate_fw_cfg_dma, + &vmstate_fw_cfg_acpi_mr, NULL, } }; @@ -815,6 +880,23 @@ static struct { #define FW_CFG_ORDER_OVERRIDE_LAST 200 }; +/* + * Any sub-page size update to these table MRs will be lost during migration, + * as we use aligned size in ram_load_precopy() -> qemu_ram_resize() path. + * In order to avoid the inconsistency in sizes save them seperately and + * migrate over in vmstate post_load(). + */ +static void fw_cfg_acpi_mr_save(FWCfgState *s, const char *filename, size_t len) +{ + if (!strcmp(filename, ACPI_BUILD_TABLE_FILE)) { + s->table_mr_size = len; + } else if (!strcmp(filename, ACPI_BUILD_LOADER_FILE)) { + s->linker_mr_size = len; + } else if (!strcmp(filename, ACPI_BUILD_RSDP_FILE)) { + s->rsdp_mr_size = len; + } +} + static int get_fw_cfg_order(FWCfgState *s, const char *name) { int i; @@ -914,6 +996,7 @@ void fw_cfg_add_file_callback(FWCfgState *s, const char *filename, trace_fw_cfg_add_file(s, index, s->files->f[index].name, len); s->files->count = cpu_to_be32(count+1); + fw_cfg_acpi_mr_save(s, filename, len); } void fw_cfg_add_file(FWCfgState *s, const char *filename, @@ -937,6 +1020,7 @@ void *fw_cfg_modify_file(FWCfgState *s, const char *filename, ptr = fw_cfg_modify_bytes_read(s, FW_CFG_FILE_FIRST + i, data, len); s->files->f[i].size = cpu_to_be32(len); + fw_cfg_acpi_mr_save(s, filename, len); return ptr; } } @@ -973,7 +1057,10 @@ static void fw_cfg_machine_ready(struct Notifier *n, void *data) qemu_register_reset(fw_cfg_machine_reset, s); } - +static Property fw_cfg_properties[] = { + DEFINE_PROP_BOOL("acpi-mr-restore", FWCfgState, acpi_mr_restore, true), + DEFINE_PROP_END_OF_LIST(), +}; static void fw_cfg_common_realize(DeviceState *dev, Error **errp) { @@ -1097,6 +1184,8 @@ static void fw_cfg_class_init(ObjectClass *klass, void *data) dc->reset = fw_cfg_reset; dc->vmsd = &vmstate_fw_cfg; + + device_class_set_props(dc, fw_cfg_properties); } static const TypeInfo fw_cfg_info = { @@ -1171,7 +1260,7 @@ static void fw_cfg_io_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = fw_cfg_io_realize; - dc->props = fw_cfg_io_properties; + device_class_set_props(dc, fw_cfg_io_properties); } static const TypeInfo fw_cfg_io_info = { @@ -1234,7 +1323,7 @@ static void fw_cfg_mem_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = fw_cfg_mem_realize; - dc->props = fw_cfg_mem_properties; + device_class_set_props(dc, fw_cfg_mem_properties); } static const TypeInfo fw_cfg_mem_info = { diff --git a/hw/nvram/mac_nvram.c b/hw/nvram/mac_nvram.c index 9a47e35b8e..2e8a1e3812 100644 --- a/hw/nvram/mac_nvram.c +++ b/hw/nvram/mac_nvram.c @@ -128,7 +128,7 @@ static void macio_nvram_class_init(ObjectClass *oc, void *data) dc->unrealize = macio_nvram_unrealizefn; dc->reset = macio_nvram_reset; dc->vmsd = &vmstate_macio_nvram; - dc->props = macio_nvram_properties; + device_class_set_props(dc, macio_nvram_properties); set_bit(DEVICE_CATEGORY_MISC, dc->categories); } diff --git a/hw/nvram/nrf51_nvm.c b/hw/nvram/nrf51_nvm.c index 4d678f994e..f2283c1a8d 100644 --- a/hw/nvram/nrf51_nvm.c +++ b/hw/nvram/nrf51_nvm.c @@ -369,7 +369,7 @@ static void nrf51_nvm_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->props = nrf51_nvm_properties; + device_class_set_props(dc, nrf51_nvm_properties); dc->vmsd = &vmstate_nvm; dc->realize = nrf51_nvm_realize; dc->reset = nrf51_nvm_reset; diff --git a/hw/nvram/spapr_nvram.c b/hw/nvram/spapr_nvram.c index 838082b451..15d08281d4 100644 --- a/hw/nvram/spapr_nvram.c +++ b/hw/nvram/spapr_nvram.c @@ -89,7 +89,7 @@ static void rtas_nvram_fetch(PowerPCCPU *cpu, SpaprMachineState *spapr, assert(nvram->buf); - membuf = cpu_physical_memory_map(buffer, &len, 1); + membuf = cpu_physical_memory_map(buffer, &len, true); memcpy(membuf, nvram->buf + offset, len); cpu_physical_memory_unmap(membuf, len, 1, len); @@ -127,7 +127,7 @@ static void rtas_nvram_store(PowerPCCPU *cpu, SpaprMachineState *spapr, return; } - membuf = cpu_physical_memory_map(buffer, &len, 0); + membuf = cpu_physical_memory_map(buffer, &len, false); alen = len; if (nvram->blk) { @@ -269,7 +269,7 @@ static void spapr_nvram_class_init(ObjectClass *klass, void *data) k->dt_type = "nvram"; k->dt_compatible = "qemu,spapr-nvram"; set_bit(DEVICE_CATEGORY_MISC, dc->categories); - dc->props = spapr_nvram_properties; + device_class_set_props(dc, spapr_nvram_properties); dc->vmsd = &vmstate_spapr_nvram; /* Reason: Internal device only, uses spapr_rtas_register() in realize() */ dc->user_creatable = false; diff --git a/hw/openrisc/openrisc_sim.c b/hw/openrisc/openrisc_sim.c index 79e70493fc..d08ce61811 100644 --- a/hw/openrisc/openrisc_sim.c +++ b/hw/openrisc/openrisc_sim.c @@ -98,7 +98,7 @@ static void openrisc_load_kernel(ram_addr_t ram_size, if (kernel_filename && !qtest_enabled()) { kernel_size = load_elf(kernel_filename, NULL, NULL, NULL, - &elf_entry, NULL, NULL, 1, EM_OPENRISC, + &elf_entry, NULL, NULL, NULL, 1, EM_OPENRISC, 1, 0); entry = elf_entry; if (kernel_size < 0) { @@ -176,7 +176,7 @@ static void openrisc_sim_machine_init(MachineClass *mc) mc->desc = "or1k simulation"; mc->init = openrisc_sim_init; mc->max_cpus = 2; - mc->is_default = 1; + mc->is_default = true; mc->default_cpu_type = OPENRISC_CPU_TYPE_NAME("or1200"); } diff --git a/hw/pci-bridge/gen_pcie_root_port.c b/hw/pci-bridge/gen_pcie_root_port.c index 9eaefebca8..bb26e272c1 100644 --- a/hw/pci-bridge/gen_pcie_root_port.c +++ b/hw/pci-bridge/gen_pcie_root_port.c @@ -147,7 +147,7 @@ static void gen_rp_dev_class_init(ObjectClass *klass, void *data) k->device_id = PCI_DEVICE_ID_REDHAT_PCIE_RP; dc->desc = "PCI Express Root Port"; dc->vmsd = &vmstate_rp_dev; - dc->props = gen_rp_props; + device_class_set_props(dc, gen_rp_props); device_class_set_parent_realize(dc, gen_rp_realize, &rpc->parent_realize); diff --git a/hw/pci-bridge/pci_bridge_dev.c b/hw/pci-bridge/pci_bridge_dev.c index cc80cb4898..4a080b7c7b 100644 --- a/hw/pci-bridge/pci_bridge_dev.c +++ b/hw/pci-bridge/pci_bridge_dev.c @@ -258,7 +258,7 @@ static void pci_bridge_dev_class_init(ObjectClass *klass, void *data) k->is_bridge = true; dc->desc = "Standard PCI Bridge"; dc->reset = qdev_pci_bridge_dev_reset; - dc->props = pci_bridge_dev_properties; + device_class_set_props(dc, pci_bridge_dev_properties); dc->vmsd = &pci_bridge_dev_vmstate; set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); hc->plug = pci_bridge_dev_plug_cb; diff --git a/hw/pci-bridge/pci_expander_bridge.c b/hw/pci-bridge/pci_expander_bridge.c index 0592818447..47aaaf8fd1 100644 --- a/hw/pci-bridge/pci_expander_bridge.c +++ b/hw/pci-bridge/pci_expander_bridge.c @@ -309,7 +309,7 @@ static void pxb_dev_class_init(ObjectClass *klass, void *data) k->class_id = PCI_CLASS_BRIDGE_HOST; dc->desc = "PCI Expander Bridge"; - dc->props = pxb_dev_properties; + device_class_set_props(dc, pxb_dev_properties); dc->hotpluggable = false; set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); } @@ -347,7 +347,7 @@ static void pxb_pcie_dev_class_init(ObjectClass *klass, void *data) k->class_id = PCI_CLASS_BRIDGE_HOST; dc->desc = "PCI Express Expander Bridge"; - dc->props = pxb_dev_properties; + device_class_set_props(dc, pxb_dev_properties); dc->hotpluggable = false; set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); } diff --git a/hw/pci-bridge/pcie_pci_bridge.c b/hw/pci-bridge/pcie_pci_bridge.c index 7679bef6c1..eade133968 100644 --- a/hw/pci-bridge/pcie_pci_bridge.c +++ b/hw/pci-bridge/pcie_pci_bridge.c @@ -152,7 +152,7 @@ static void pcie_pci_bridge_class_init(ObjectClass *klass, void *data) k->exit = pcie_pci_bridge_exit; k->config_write = pcie_pci_bridge_write_config; dc->vmsd = &pcie_pci_bridge_dev_vmstate; - dc->props = pcie_pci_bridge_dev_properties; + device_class_set_props(dc, pcie_pci_bridge_dev_properties); dc->reset = &pcie_pci_bridge_reset; set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); hc->plug = pci_bridge_dev_plug_cb; diff --git a/hw/pci-bridge/pcie_root_port.c b/hw/pci-bridge/pcie_root_port.c index 012c2cb12c..f1cfe9d14a 100644 --- a/hw/pci-bridge/pcie_root_port.c +++ b/hw/pci-bridge/pcie_root_port.c @@ -94,7 +94,7 @@ static void rp_realize(PCIDevice *d, Error **errp) pcie_cap_arifwd_init(d); pcie_cap_deverr_init(d); - pcie_cap_slot_init(d, s->slot); + pcie_cap_slot_init(d, s); pcie_cap_root_init(d); pcie_chassis_create(s->chassis); @@ -174,7 +174,7 @@ static void rp_class_init(ObjectClass *klass, void *data) k->exit = rp_exit; set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); dc->reset = rp_reset; - dc->props = rp_props; + device_class_set_props(dc, rp_props); } static const TypeInfo rp_info = { diff --git a/hw/pci-bridge/xio3130_downstream.c b/hw/pci-bridge/xio3130_downstream.c index a9f084b863..04aae72cd6 100644 --- a/hw/pci-bridge/xio3130_downstream.c +++ b/hw/pci-bridge/xio3130_downstream.c @@ -94,7 +94,7 @@ static void xio3130_downstream_realize(PCIDevice *d, Error **errp) } pcie_cap_flr_init(d); pcie_cap_deverr_init(d); - pcie_cap_slot_init(d, s->slot); + pcie_cap_slot_init(d, s); pcie_cap_arifwd_init(d); pcie_chassis_create(s->chassis); @@ -169,7 +169,7 @@ static void xio3130_downstream_class_init(ObjectClass *klass, void *data) dc->desc = "TI X3130 Downstream Port of PCI Express Switch"; dc->reset = xio3130_downstream_reset; dc->vmsd = &vmstate_xio3130_downstream; - dc->props = xio3130_downstream_props; + device_class_set_props(dc, xio3130_downstream_props); } static const TypeInfo xio3130_downstream_info = { diff --git a/hw/pci-host/Kconfig b/hw/pci-host/Kconfig index b0aa8351c4..9642c77e98 100644 --- a/hw/pci-host/Kconfig +++ b/hw/pci-host/Kconfig @@ -1,6 +1,11 @@ config PAM bool +config XEN_IGD_PASSTHROUGH + bool + default y + depends on XEN && PCI_I440FX + config PREP_PCI bool select PCI diff --git a/hw/pci-host/Makefile.objs b/hw/pci-host/Makefile.objs index efd752b766..8c87e8494d 100644 --- a/hw/pci-host/Makefile.objs +++ b/hw/pci-host/Makefile.objs @@ -14,8 +14,11 @@ common-obj-$(CONFIG_VERSATILE_PCI) += versatile.o common-obj-$(CONFIG_PCI_SABRE) += sabre.o common-obj-$(CONFIG_FULONG) += bonito.o common-obj-$(CONFIG_PCI_I440FX) += i440fx.o +common-obj-$(CONFIG_XEN_IGD_PASSTHROUGH) += xen_igd_pt.o common-obj-$(CONFIG_PCI_EXPRESS_Q35) += q35.o common-obj-$(CONFIG_PCI_EXPRESS_GENERIC_BRIDGE) += gpex.o common-obj-$(CONFIG_PCI_EXPRESS_XILINX) += xilinx-pcie.o common-obj-$(CONFIG_PCI_EXPRESS_DESIGNWARE) += designware.o +obj-$(CONFIG_POWERNV) += pnv_phb4.o pnv_phb4_pec.o +obj-$(CONFIG_POWERNV) += pnv_phb3.o pnv_phb3_msi.o pnv_phb3_pbcq.o diff --git a/hw/pci-host/bonito.c b/hw/pci-host/bonito.c index ceee463a11..cc6545c8a8 100644 --- a/hw/pci-host/bonito.c +++ b/hw/pci-host/bonito.c @@ -14,7 +14,8 @@ * fulong 2e mini pc has a bonito north bridge. */ -/* what is the meaning of devfn in qemu and IDSEL in bonito northbridge? +/* + * what is the meaning of devfn in qemu and IDSEL in bonito northbridge? * * devfn pci_slot<<3 + funno * one pci bus can have 32 devices and each device can have 8 functions. @@ -40,7 +41,6 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" #include "hw/pci/pci.h" -#include "hw/i386/pc.h" #include "hw/irq.h" #include "hw/mips/mips.h" #include "hw/pci/pci_host.h" @@ -49,7 +49,7 @@ #include "sysemu/runstate.h" #include "exec/address-spaces.h" -//#define DEBUG_BONITO +/* #define DEBUG_BONITO */ #ifdef DEBUG_BONITO #define DPRINTF(fmt, ...) fprintf(stderr, "%s: " fmt, __func__, ##__VA_ARGS__) @@ -60,45 +60,45 @@ /* from linux soure code. include/asm-mips/mips-boards/bonito64.h*/ #define BONITO_BOOT_BASE 0x1fc00000 #define BONITO_BOOT_SIZE 0x00100000 -#define BONITO_BOOT_TOP (BONITO_BOOT_BASE+BONITO_BOOT_SIZE-1) +#define BONITO_BOOT_TOP (BONITO_BOOT_BASE + BONITO_BOOT_SIZE - 1) #define BONITO_FLASH_BASE 0x1c000000 #define BONITO_FLASH_SIZE 0x03000000 -#define BONITO_FLASH_TOP (BONITO_FLASH_BASE+BONITO_FLASH_SIZE-1) +#define BONITO_FLASH_TOP (BONITO_FLASH_BASE + BONITO_FLASH_SIZE - 1) #define BONITO_SOCKET_BASE 0x1f800000 #define BONITO_SOCKET_SIZE 0x00400000 -#define BONITO_SOCKET_TOP (BONITO_SOCKET_BASE+BONITO_SOCKET_SIZE-1) +#define BONITO_SOCKET_TOP (BONITO_SOCKET_BASE + BONITO_SOCKET_SIZE - 1) #define BONITO_REG_BASE 0x1fe00000 #define BONITO_REG_SIZE 0x00040000 -#define BONITO_REG_TOP (BONITO_REG_BASE+BONITO_REG_SIZE-1) +#define BONITO_REG_TOP (BONITO_REG_BASE + BONITO_REG_SIZE - 1) #define BONITO_DEV_BASE 0x1ff00000 #define BONITO_DEV_SIZE 0x00100000 -#define BONITO_DEV_TOP (BONITO_DEV_BASE+BONITO_DEV_SIZE-1) +#define BONITO_DEV_TOP (BONITO_DEV_BASE + BONITO_DEV_SIZE - 1) #define BONITO_PCILO_BASE 0x10000000 #define BONITO_PCILO_BASE_VA 0xb0000000 #define BONITO_PCILO_SIZE 0x0c000000 -#define BONITO_PCILO_TOP (BONITO_PCILO_BASE+BONITO_PCILO_SIZE-1) +#define BONITO_PCILO_TOP (BONITO_PCILO_BASE + BONITO_PCILO_SIZE - 1) #define BONITO_PCILO0_BASE 0x10000000 #define BONITO_PCILO1_BASE 0x14000000 #define BONITO_PCILO2_BASE 0x18000000 #define BONITO_PCIHI_BASE 0x20000000 #define BONITO_PCIHI_SIZE 0x20000000 -#define BONITO_PCIHI_TOP (BONITO_PCIHI_BASE+BONITO_PCIHI_SIZE-1) +#define BONITO_PCIHI_TOP (BONITO_PCIHI_BASE + BONITO_PCIHI_SIZE - 1) #define BONITO_PCIIO_BASE 0x1fd00000 #define BONITO_PCIIO_BASE_VA 0xbfd00000 #define BONITO_PCIIO_SIZE 0x00010000 -#define BONITO_PCIIO_TOP (BONITO_PCIIO_BASE+BONITO_PCIIO_SIZE-1) +#define BONITO_PCIIO_TOP (BONITO_PCIIO_BASE + BONITO_PCIIO_SIZE - 1) #define BONITO_PCICFG_BASE 0x1fe80000 #define BONITO_PCICFG_SIZE 0x00080000 -#define BONITO_PCICFG_TOP (BONITO_PCICFG_BASE+BONITO_PCICFG_SIZE-1) +#define BONITO_PCICFG_TOP (BONITO_PCICFG_BASE + BONITO_PCICFG_SIZE - 1) #define BONITO_PCICONFIGBASE 0x00 #define BONITO_REGBASE 0x100 -#define BONITO_PCICONFIG_BASE (BONITO_PCICONFIGBASE+BONITO_REG_BASE) +#define BONITO_PCICONFIG_BASE (BONITO_PCICONFIGBASE + BONITO_REG_BASE) #define BONITO_PCICONFIG_SIZE (0x100) -#define BONITO_INTERNAL_REG_BASE (BONITO_REGBASE+BONITO_REG_BASE) +#define BONITO_INTERNAL_REG_BASE (BONITO_REGBASE + BONITO_REG_BASE) #define BONITO_INTERNAL_REG_SIZE (0x70) #define BONITO_SPCICONFIG_BASE (BONITO_PCICFG_BASE) @@ -111,7 +111,7 @@ #define BONITO_BONPONCFG (0x00 >> 2) /* 0x100 */ #define BONITO_BONGENCFG_OFFSET 0x4 -#define BONITO_BONGENCFG (BONITO_BONGENCFG_OFFSET>>2) /*0x104 */ +#define BONITO_BONGENCFG (BONITO_BONGENCFG_OFFSET >> 2) /*0x104 */ /* 2. IO & IDE configuration */ #define BONITO_IODEVCFG (0x08 >> 2) /* 0x108 */ @@ -177,15 +177,15 @@ /* idsel BIT = pci slot number +12 */ #define PCI_SLOT_BASE 12 #define PCI_IDSEL_VIA686B_BIT (17) -#define PCI_IDSEL_VIA686B (1<> 2; - DPRINTF("bonito_writel "TARGET_FMT_plx" val %x saddr %x\n", addr, val, saddr); + DPRINTF("bonito_writel "TARGET_FMT_plx" val %x saddr %x\n", + addr, val, saddr); switch (saddr) { case BONITO_BONPONCFG: case BONITO_IODEVCFG: @@ -363,7 +364,7 @@ static uint64_t bonito_ldma_readl(void *opaque, hwaddr addr, return 0; } - val = ((uint32_t *)(&s->bonldma))[addr/sizeof(uint32_t)]; + val = ((uint32_t *)(&s->bonldma))[addr / sizeof(uint32_t)]; return val; } @@ -377,7 +378,7 @@ static void bonito_ldma_writel(void *opaque, hwaddr addr, return; } - ((uint32_t *)(&s->bonldma))[addr/sizeof(uint32_t)] = val & 0xffffffff; + ((uint32_t *)(&s->bonldma))[addr / sizeof(uint32_t)] = val & 0xffffffff; } static const MemoryRegionOps bonito_ldma_ops = { @@ -400,7 +401,7 @@ static uint64_t bonito_cop_readl(void *opaque, hwaddr addr, return 0; } - val = ((uint32_t *)(&s->boncop))[addr/sizeof(uint32_t)]; + val = ((uint32_t *)(&s->boncop))[addr / sizeof(uint32_t)]; return val; } @@ -414,7 +415,7 @@ static void bonito_cop_writel(void *opaque, hwaddr addr, return; } - ((uint32_t *)(&s->boncop))[addr/sizeof(uint32_t)] = val & 0xffffffff; + ((uint32_t *)(&s->boncop))[addr / sizeof(uint32_t)] = val & 0xffffffff; } static const MemoryRegionOps bonito_cop_ops = { @@ -446,7 +447,8 @@ static uint32_t bonito_sbridge_pciaddr(void *opaque, hwaddr addr) cfgaddr = addr & 0xffff; cfgaddr |= (s->regs[BONITO_PCIMAP_CFG] & 0xffff) << 16; - idsel = (cfgaddr & BONITO_PCICONF_IDSEL_MASK) >> BONITO_PCICONF_IDSEL_OFFSET; + idsel = (cfgaddr & BONITO_PCICONF_IDSEL_MASK) >> + BONITO_PCICONF_IDSEL_OFFSET; devno = ctz32(idsel); funno = (cfgaddr & BONITO_PCICONF_FUN_MASK) >> BONITO_PCICONF_FUN_OFFSET; regno = (cfgaddr & BONITO_PCICONF_REG_MASK) >> BONITO_PCICONF_REG_OFFSET; @@ -550,7 +552,7 @@ static void pci_bonito_set_irq(void *opaque, int irq_num, int level) } /* map the original irq (0~3) to bonito irq (16~47, but 16~31 are unused) */ -static int pci_bonito_map_irq(PCIDevice * pci_dev, int irq_num) +static int pci_bonito_map_irq(PCIDevice *pci_dev, int irq_num) { int slot; @@ -618,7 +620,10 @@ static void bonito_realize(PCIDevice *dev, Error **errp) SysBusDevice *sysbus = SYS_BUS_DEVICE(s->pcihost); PCIHostState *phb = PCI_HOST_BRIDGE(s->pcihost); - /* Bonito North Bridge, built on FPGA, VENDOR_ID/DEVICE_ID are "undefined" */ + /* + * Bonito North Bridge, built on FPGA, + * VENDOR_ID/DEVICE_ID are "undefined" + */ pci_config_set_prog_interface(dev->config, 0x00); /* set the north bridge register mapping */ diff --git a/hw/pci-host/designware.c b/hw/pci-host/designware.c index 71e9b0d9b5..dd245516dd 100644 --- a/hw/pci-host/designware.c +++ b/hw/pci-host/designware.c @@ -182,7 +182,7 @@ designware_pcie_root_config_read(PCIDevice *d, uint32_t address, int len) break; case DESIGNWARE_PCIE_ATU_CR1: - case DESIGNWARE_PCIE_ATU_CR2: /* FALLTHROUGH */ + case DESIGNWARE_PCIE_ATU_CR2: val = viewport->cr[(address - DESIGNWARE_PCIE_ATU_CR1) / sizeof(uint32_t)]; break; diff --git a/hw/pci-host/grackle.c b/hw/pci-host/grackle.c index 537273e578..24ccdf6ceb 100644 --- a/hw/pci-host/grackle.c +++ b/hw/pci-host/grackle.c @@ -168,7 +168,7 @@ static void grackle_class_init(ObjectClass *klass, void *data) SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass); dc->realize = grackle_realize; - dc->props = grackle_properties; + device_class_set_props(dc, grackle_properties); set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); dc->fw_name = "pci"; sbc->explicit_ofw_unit_address = grackle_ofw_unit_address; diff --git a/hw/pci-host/i440fx.c b/hw/pci-host/i440fx.c index f27131102d..d980c97049 100644 --- a/hw/pci-host/i440fx.c +++ b/hw/pci-host/i440fx.c @@ -1,5 +1,5 @@ /* - * QEMU i440FX/PIIX3 PCI Bridge Emulation + * QEMU i440FX PCI Bridge Emulation * * Copyright (c) 2006 Fabrice Bellard * @@ -23,6 +23,7 @@ */ #include "qemu/osdep.h" +#include "qemu/range.h" #include "hw/i386/pc.h" #include "hw/pci/pci.h" #include "hw/pci/pci_host.h" @@ -31,7 +32,6 @@ #include "hw/sysbus.h" #include "qapi/error.h" #include "migration/vmstate.h" -#include "hw/pci-host/pam.h" #include "qapi/visitor.h" #include "qemu/error-report.h" @@ -51,23 +51,6 @@ typedef struct I440FXState { uint32_t short_root_bus; } I440FXState; -#define I440FX_PCI_DEVICE(obj) \ - OBJECT_CHECK(PCII440FXState, (obj), TYPE_I440FX_PCI_DEVICE) - -struct PCII440FXState { - /*< private >*/ - PCIDevice parent_obj; - /*< public >*/ - - MemoryRegion *system_memory; - MemoryRegion *pci_address_space; - MemoryRegion *ram_memory; - PAMMemoryRegion pam_regions[13]; - MemoryRegion smram_region; - MemoryRegion smram, low_smram; -}; - - #define I440FX_PAM 0x59 #define I440FX_PAM_SIZE 7 #define I440FX_SMRAM 0x72 @@ -386,90 +369,6 @@ static const TypeInfo i440fx_info = { }, }; -/* IGD Passthrough Host Bridge. */ -typedef struct { - uint8_t offset; - uint8_t len; -} IGDHostInfo; - -/* Here we just expose minimal host bridge offset subset. */ -static const IGDHostInfo igd_host_bridge_infos[] = { - {0x08, 2}, /* revision id */ - {0x2c, 2}, /* sybsystem vendor id */ - {0x2e, 2}, /* sybsystem id */ - {0x50, 2}, /* SNB: processor graphics control register */ - {0x52, 2}, /* processor graphics control register */ - {0xa4, 4}, /* SNB: graphics base of stolen memory */ - {0xa8, 4}, /* SNB: base of GTT stolen memory */ -}; - -static void host_pci_config_read(int pos, int len, uint32_t *val, Error **errp) -{ - int rc, config_fd; - /* Access real host bridge. */ - char *path = g_strdup_printf("/sys/bus/pci/devices/%04x:%02x:%02x.%d/%s", - 0, 0, 0, 0, "config"); - - config_fd = open(path, O_RDWR); - if (config_fd < 0) { - error_setg_errno(errp, errno, "Failed to open: %s", path); - goto out; - } - - if (lseek(config_fd, pos, SEEK_SET) != pos) { - error_setg_errno(errp, errno, "Failed to seek: %s", path); - goto out_close_fd; - } - - do { - rc = read(config_fd, (uint8_t *)val, len); - } while (rc < 0 && (errno == EINTR || errno == EAGAIN)); - if (rc != len) { - error_setg_errno(errp, errno, "Failed to read: %s", path); - } - -out_close_fd: - close(config_fd); -out: - g_free(path); -} - -static void igd_pt_i440fx_realize(PCIDevice *pci_dev, Error **errp) -{ - uint32_t val = 0; - int i, num; - int pos, len; - Error *local_err = NULL; - - num = ARRAY_SIZE(igd_host_bridge_infos); - for (i = 0; i < num; i++) { - pos = igd_host_bridge_infos[i].offset; - len = igd_host_bridge_infos[i].len; - host_pci_config_read(pos, len, &val, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - pci_default_write_config(pci_dev, pos, val, len); - } -} - -static void igd_passthrough_i440fx_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->realize = igd_pt_i440fx_realize; - dc->desc = "IGD Passthrough Host bridge"; -} - -static const TypeInfo igd_passthrough_i440fx_info = { - .name = TYPE_IGD_PASSTHROUGH_I440FX_PCI_DEVICE, - .parent = TYPE_I440FX_PCI_DEVICE, - .instance_size = sizeof(PCII440FXState), - .class_init = igd_passthrough_i440fx_class_init, -}; - static const char *i440fx_pcihost_root_bus_path(PCIHostState *host_bridge, PCIBus *rootbus) { @@ -498,7 +397,7 @@ static void i440fx_pcihost_class_init(ObjectClass *klass, void *data) hc->root_bus_path = i440fx_pcihost_root_bus_path; dc->realize = i440fx_pcihost_realize; dc->fw_name = "pci"; - dc->props = i440fx_props; + device_class_set_props(dc, i440fx_props); /* Reason: needs to be wired up by pc_init1 */ dc->user_creatable = false; } @@ -514,7 +413,6 @@ static const TypeInfo i440fx_pcihost_info = { static void i440fx_register_types(void) { type_register_static(&i440fx_info); - type_register_static(&igd_passthrough_i440fx_info); type_register_static(&i440fx_pcihost_info); } diff --git a/hw/pci-host/pnv_phb3.c b/hw/pci-host/pnv_phb3.c new file mode 100644 index 0000000000..74618fadf0 --- /dev/null +++ b/hw/pci-host/pnv_phb3.c @@ -0,0 +1,1197 @@ +/* + * QEMU PowerPC PowerNV (POWER8) PHB3 model + * + * Copyright (c) 2014-2020, IBM Corporation. + * + * This code is licensed under the GPL version 2 or later. See the + * COPYING file in the top-level directory. + */ +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qapi/visitor.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "hw/pci-host/pnv_phb3_regs.h" +#include "hw/pci-host/pnv_phb3.h" +#include "hw/pci/pcie_host.h" +#include "hw/pci/pcie_port.h" +#include "hw/ppc/pnv.h" +#include "hw/irq.h" +#include "hw/qdev-properties.h" + +#define phb3_error(phb, fmt, ...) \ + qemu_log_mask(LOG_GUEST_ERROR, "phb3[%d:%d]: " fmt "\n", \ + (phb)->chip_id, (phb)->phb_id, ## __VA_ARGS__) + +static PCIDevice *pnv_phb3_find_cfg_dev(PnvPHB3 *phb) +{ + PCIHostState *pci = PCI_HOST_BRIDGE(phb); + uint64_t addr = phb->regs[PHB_CONFIG_ADDRESS >> 3]; + uint8_t bus, devfn; + + if (!(addr >> 63)) { + return NULL; + } + bus = (addr >> 52) & 0xff; + devfn = (addr >> 44) & 0xff; + + return pci_find_device(pci->bus, bus, devfn); +} + +/* + * The CONFIG_DATA register expects little endian accesses, but as the + * region is big endian, we have to swap the value. + */ +static void pnv_phb3_config_write(PnvPHB3 *phb, unsigned off, + unsigned size, uint64_t val) +{ + uint32_t cfg_addr, limit; + PCIDevice *pdev; + + pdev = pnv_phb3_find_cfg_dev(phb); + if (!pdev) { + return; + } + cfg_addr = (phb->regs[PHB_CONFIG_ADDRESS >> 3] >> 32) & 0xffc; + cfg_addr |= off; + limit = pci_config_size(pdev); + if (limit <= cfg_addr) { + /* + * conventional pci device can be behind pcie-to-pci bridge. + * 256 <= addr < 4K has no effects. + */ + return; + } + switch (size) { + case 1: + break; + case 2: + val = bswap16(val); + break; + case 4: + val = bswap32(val); + break; + default: + g_assert_not_reached(); + } + pci_host_config_write_common(pdev, cfg_addr, limit, val, size); +} + +static uint64_t pnv_phb3_config_read(PnvPHB3 *phb, unsigned off, + unsigned size) +{ + uint32_t cfg_addr, limit; + PCIDevice *pdev; + uint64_t val; + + pdev = pnv_phb3_find_cfg_dev(phb); + if (!pdev) { + return ~0ull; + } + cfg_addr = (phb->regs[PHB_CONFIG_ADDRESS >> 3] >> 32) & 0xffc; + cfg_addr |= off; + limit = pci_config_size(pdev); + if (limit <= cfg_addr) { + /* + * conventional pci device can be behind pcie-to-pci bridge. + * 256 <= addr < 4K has no effects. + */ + return ~0ull; + } + val = pci_host_config_read_common(pdev, cfg_addr, limit, size); + switch (size) { + case 1: + return val; + case 2: + return bswap16(val); + case 4: + return bswap32(val); + default: + g_assert_not_reached(); + } +} + +static void pnv_phb3_check_m32(PnvPHB3 *phb) +{ + uint64_t base, start, size; + MemoryRegion *parent; + PnvPBCQState *pbcq = &phb->pbcq; + + if (memory_region_is_mapped(&phb->mr_m32)) { + memory_region_del_subregion(phb->mr_m32.container, &phb->mr_m32); + } + + if (!(phb->regs[PHB_PHB3_CONFIG >> 3] & PHB_PHB3C_M32_EN)) { + return; + } + + /* Grab geometry from registers */ + base = phb->regs[PHB_M32_BASE_ADDR >> 3]; + start = phb->regs[PHB_M32_START_ADDR >> 3]; + size = ~(phb->regs[PHB_M32_BASE_MASK >> 3] | 0xfffc000000000000ull) + 1; + + /* Check if it matches an enabled MMIO region in the PBCQ */ + if (memory_region_is_mapped(&pbcq->mmbar0) && + base >= pbcq->mmio0_base && + (base + size) <= (pbcq->mmio0_base + pbcq->mmio0_size)) { + parent = &pbcq->mmbar0; + base -= pbcq->mmio0_base; + } else if (memory_region_is_mapped(&pbcq->mmbar1) && + base >= pbcq->mmio1_base && + (base + size) <= (pbcq->mmio1_base + pbcq->mmio1_size)) { + parent = &pbcq->mmbar1; + base -= pbcq->mmio1_base; + } else { + return; + } + + /* Create alias */ + memory_region_init_alias(&phb->mr_m32, OBJECT(phb), "phb3-m32", + &phb->pci_mmio, start, size); + memory_region_add_subregion(parent, base, &phb->mr_m32); +} + +static void pnv_phb3_check_m64(PnvPHB3 *phb, uint32_t index) +{ + uint64_t base, start, size, m64; + MemoryRegion *parent; + PnvPBCQState *pbcq = &phb->pbcq; + + if (memory_region_is_mapped(&phb->mr_m64[index])) { + /* Should we destroy it in RCU friendly way... ? */ + memory_region_del_subregion(phb->mr_m64[index].container, + &phb->mr_m64[index]); + } + + /* Get table entry */ + m64 = phb->ioda_M64BT[index]; + + if (!(m64 & IODA2_M64BT_ENABLE)) { + return; + } + + /* Grab geometry from registers */ + base = GETFIELD(IODA2_M64BT_BASE, m64) << 20; + if (m64 & IODA2_M64BT_SINGLE_PE) { + base &= ~0x1ffffffull; + } + size = GETFIELD(IODA2_M64BT_MASK, m64) << 20; + size |= 0xfffc000000000000ull; + size = ~size + 1; + start = base | (phb->regs[PHB_M64_UPPER_BITS >> 3]); + + /* Check if it matches an enabled MMIO region in the PBCQ */ + if (memory_region_is_mapped(&pbcq->mmbar0) && + base >= pbcq->mmio0_base && + (base + size) <= (pbcq->mmio0_base + pbcq->mmio0_size)) { + parent = &pbcq->mmbar0; + base -= pbcq->mmio0_base; + } else if (memory_region_is_mapped(&pbcq->mmbar1) && + base >= pbcq->mmio1_base && + (base + size) <= (pbcq->mmio1_base + pbcq->mmio1_size)) { + parent = &pbcq->mmbar1; + base -= pbcq->mmio1_base; + } else { + return; + } + + /* Create alias */ + memory_region_init_alias(&phb->mr_m64[index], OBJECT(phb), "phb3-m64", + &phb->pci_mmio, start, size); + memory_region_add_subregion(parent, base, &phb->mr_m64[index]); +} + +static void pnv_phb3_check_all_m64s(PnvPHB3 *phb) +{ + uint64_t i; + + for (i = 0; i < PNV_PHB3_NUM_M64; i++) { + pnv_phb3_check_m64(phb, i); + } +} + +static void pnv_phb3_lxivt_write(PnvPHB3 *phb, unsigned idx, uint64_t val) +{ + uint8_t server, prio; + + phb->ioda_LXIVT[idx] = val & (IODA2_LXIVT_SERVER | + IODA2_LXIVT_PRIORITY | + IODA2_LXIVT_NODE_ID); + server = GETFIELD(IODA2_LXIVT_SERVER, val); + prio = GETFIELD(IODA2_LXIVT_PRIORITY, val); + + /* + * The low order 2 bits are the link pointer (Type II interrupts). + * Shift back to get a valid IRQ server. + */ + server >>= 2; + + ics_write_xive(&phb->lsis, idx, server, prio, prio); +} + +static uint64_t *pnv_phb3_ioda_access(PnvPHB3 *phb, + unsigned *out_table, unsigned *out_idx) +{ + uint64_t adreg = phb->regs[PHB_IODA_ADDR >> 3]; + unsigned int index = GETFIELD(PHB_IODA_AD_TADR, adreg); + unsigned int table = GETFIELD(PHB_IODA_AD_TSEL, adreg); + unsigned int mask; + uint64_t *tptr = NULL; + + switch (table) { + case IODA2_TBL_LIST: + tptr = phb->ioda_LIST; + mask = 7; + break; + case IODA2_TBL_LXIVT: + tptr = phb->ioda_LXIVT; + mask = 7; + break; + case IODA2_TBL_IVC_CAM: + case IODA2_TBL_RBA: + mask = 31; + break; + case IODA2_TBL_RCAM: + mask = 63; + break; + case IODA2_TBL_MRT: + mask = 7; + break; + case IODA2_TBL_PESTA: + case IODA2_TBL_PESTB: + mask = 255; + break; + case IODA2_TBL_TVT: + tptr = phb->ioda_TVT; + mask = 511; + break; + case IODA2_TBL_TCAM: + case IODA2_TBL_TDR: + mask = 63; + break; + case IODA2_TBL_M64BT: + tptr = phb->ioda_M64BT; + mask = 15; + break; + case IODA2_TBL_M32DT: + tptr = phb->ioda_MDT; + mask = 255; + break; + case IODA2_TBL_PEEV: + tptr = phb->ioda_PEEV; + mask = 3; + break; + default: + phb3_error(phb, "invalid IODA table %d", table); + return NULL; + } + index &= mask; + if (out_idx) { + *out_idx = index; + } + if (out_table) { + *out_table = table; + } + if (tptr) { + tptr += index; + } + if (adreg & PHB_IODA_AD_AUTOINC) { + index = (index + 1) & mask; + adreg = SETFIELD(PHB_IODA_AD_TADR, adreg, index); + } + phb->regs[PHB_IODA_ADDR >> 3] = adreg; + return tptr; +} + +static uint64_t pnv_phb3_ioda_read(PnvPHB3 *phb) +{ + unsigned table; + uint64_t *tptr; + + tptr = pnv_phb3_ioda_access(phb, &table, NULL); + if (!tptr) { + /* Return 0 on unsupported tables, not ff's */ + return 0; + } + return *tptr; +} + +static void pnv_phb3_ioda_write(PnvPHB3 *phb, uint64_t val) +{ + unsigned table, idx; + uint64_t *tptr; + + tptr = pnv_phb3_ioda_access(phb, &table, &idx); + if (!tptr) { + return; + } + + /* Handle side effects */ + switch (table) { + case IODA2_TBL_LXIVT: + pnv_phb3_lxivt_write(phb, idx, val); + break; + case IODA2_TBL_M64BT: + *tptr = val; + pnv_phb3_check_m64(phb, idx); + break; + default: + *tptr = val; + } +} + +/* + * This is called whenever the PHB LSI, MSI source ID register or + * the PBCQ irq filters are written. + */ +void pnv_phb3_remap_irqs(PnvPHB3 *phb) +{ + ICSState *ics = &phb->lsis; + uint32_t local, global, count, mask, comp; + uint64_t baren; + PnvPBCQState *pbcq = &phb->pbcq; + + /* + * First check if we are enabled. Unlike real HW we don't separate + * TX and RX so we enable if both are set + */ + baren = pbcq->nest_regs[PBCQ_NEST_BAR_EN]; + if (!(baren & PBCQ_NEST_BAR_EN_IRSN_RX) || + !(baren & PBCQ_NEST_BAR_EN_IRSN_TX)) { + ics->offset = 0; + return; + } + + /* Grab local LSI source ID */ + local = GETFIELD(PHB_LSI_SRC_ID, phb->regs[PHB_LSI_SOURCE_ID >> 3]) << 3; + + /* Grab global one and compare */ + global = GETFIELD(PBCQ_NEST_LSI_SRC, + pbcq->nest_regs[PBCQ_NEST_LSI_SRC_ID]) << 3; + if (global != local) { + /* + * This happens during initialization, let's come back when we + * are properly configured + */ + ics->offset = 0; + return; + } + + /* Get the base on the powerbus */ + comp = GETFIELD(PBCQ_NEST_IRSN_COMP, + pbcq->nest_regs[PBCQ_NEST_IRSN_COMPARE]); + mask = GETFIELD(PBCQ_NEST_IRSN_COMP, + pbcq->nest_regs[PBCQ_NEST_IRSN_MASK]); + count = ((~mask) + 1) & 0x7ffff; + phb->total_irq = count; + + /* Sanity checks */ + if ((global + PNV_PHB3_NUM_LSI) > count) { + phb3_error(phb, "LSIs out of reach: LSI base=%d total irq=%d", global, + count); + } + + if (count > 2048) { + phb3_error(phb, "More interrupts than supported: %d", count); + } + + if ((comp & mask) != comp) { + phb3_error(phb, "IRQ compare bits not in mask: comp=0x%x mask=0x%x", + comp, mask); + comp &= mask; + } + /* Setup LSI offset */ + ics->offset = comp + global; + + /* Setup MSI offset */ + pnv_phb3_msi_update_config(&phb->msis, comp, count - PNV_PHB3_NUM_LSI); +} + +static void pnv_phb3_lsi_src_id_write(PnvPHB3 *phb, uint64_t val) +{ + /* Sanitize content */ + val &= PHB_LSI_SRC_ID; + phb->regs[PHB_LSI_SOURCE_ID >> 3] = val; + pnv_phb3_remap_irqs(phb); +} + +static void pnv_phb3_rtc_invalidate(PnvPHB3 *phb, uint64_t val) +{ + PnvPhb3DMASpace *ds; + + /* Always invalidate all for now ... */ + QLIST_FOREACH(ds, &phb->dma_spaces, list) { + ds->pe_num = PHB_INVALID_PE; + } +} + + +static void pnv_phb3_update_msi_regions(PnvPhb3DMASpace *ds) +{ + uint64_t cfg = ds->phb->regs[PHB_PHB3_CONFIG >> 3]; + + if (cfg & PHB_PHB3C_32BIT_MSI_EN) { + if (!memory_region_is_mapped(&ds->msi32_mr)) { + memory_region_add_subregion(MEMORY_REGION(&ds->dma_mr), + 0xffff0000, &ds->msi32_mr); + } + } else { + if (memory_region_is_mapped(&ds->msi32_mr)) { + memory_region_del_subregion(MEMORY_REGION(&ds->dma_mr), + &ds->msi32_mr); + } + } + + if (cfg & PHB_PHB3C_64BIT_MSI_EN) { + if (!memory_region_is_mapped(&ds->msi64_mr)) { + memory_region_add_subregion(MEMORY_REGION(&ds->dma_mr), + (1ull << 60), &ds->msi64_mr); + } + } else { + if (memory_region_is_mapped(&ds->msi64_mr)) { + memory_region_del_subregion(MEMORY_REGION(&ds->dma_mr), + &ds->msi64_mr); + } + } +} + +static void pnv_phb3_update_all_msi_regions(PnvPHB3 *phb) +{ + PnvPhb3DMASpace *ds; + + QLIST_FOREACH(ds, &phb->dma_spaces, list) { + pnv_phb3_update_msi_regions(ds); + } +} + +void pnv_phb3_reg_write(void *opaque, hwaddr off, uint64_t val, unsigned size) +{ + PnvPHB3 *phb = opaque; + bool changed; + + /* Special case configuration data */ + if ((off & 0xfffc) == PHB_CONFIG_DATA) { + pnv_phb3_config_write(phb, off & 0x3, size, val); + return; + } + + /* Other registers are 64-bit only */ + if (size != 8 || off & 0x7) { + phb3_error(phb, "Invalid register access, offset: 0x%"PRIx64" size: %d", + off, size); + return; + } + + /* Handle masking & filtering */ + switch (off) { + case PHB_M64_UPPER_BITS: + val &= 0xfffc000000000000ull; + break; + case PHB_Q_DMA_R: + /* + * This is enough logic to make SW happy but we aren't actually + * quiescing the DMAs + */ + if (val & PHB_Q_DMA_R_AUTORESET) { + val = 0; + } else { + val &= PHB_Q_DMA_R_QUIESCE_DMA; + } + break; + /* LEM stuff */ + case PHB_LEM_FIR_AND_MASK: + phb->regs[PHB_LEM_FIR_ACCUM >> 3] &= val; + return; + case PHB_LEM_FIR_OR_MASK: + phb->regs[PHB_LEM_FIR_ACCUM >> 3] |= val; + return; + case PHB_LEM_ERROR_AND_MASK: + phb->regs[PHB_LEM_ERROR_MASK >> 3] &= val; + return; + case PHB_LEM_ERROR_OR_MASK: + phb->regs[PHB_LEM_ERROR_MASK >> 3] |= val; + return; + case PHB_LEM_WOF: + val = 0; + break; + } + + /* Record whether it changed */ + changed = phb->regs[off >> 3] != val; + + /* Store in register cache first */ + phb->regs[off >> 3] = val; + + /* Handle side effects */ + switch (off) { + case PHB_PHB3_CONFIG: + if (changed) { + pnv_phb3_update_all_msi_regions(phb); + } + /* fall through */ + case PHB_M32_BASE_ADDR: + case PHB_M32_BASE_MASK: + case PHB_M32_START_ADDR: + if (changed) { + pnv_phb3_check_m32(phb); + } + break; + case PHB_M64_UPPER_BITS: + if (changed) { + pnv_phb3_check_all_m64s(phb); + } + break; + case PHB_LSI_SOURCE_ID: + if (changed) { + pnv_phb3_lsi_src_id_write(phb, val); + } + break; + + /* IODA table accesses */ + case PHB_IODA_DATA0: + pnv_phb3_ioda_write(phb, val); + break; + + /* RTC invalidation */ + case PHB_RTC_INVALIDATE: + pnv_phb3_rtc_invalidate(phb, val); + break; + + /* FFI request */ + case PHB_FFI_REQUEST: + pnv_phb3_msi_ffi(&phb->msis, val); + break; + + /* Silent simple writes */ + case PHB_CONFIG_ADDRESS: + case PHB_IODA_ADDR: + case PHB_TCE_KILL: + case PHB_TCE_SPEC_CTL: + case PHB_PEST_BAR: + case PHB_PELTV_BAR: + case PHB_RTT_BAR: + case PHB_RBA_BAR: + case PHB_IVT_BAR: + case PHB_FFI_LOCK: + case PHB_LEM_FIR_ACCUM: + case PHB_LEM_ERROR_MASK: + case PHB_LEM_ACTION0: + case PHB_LEM_ACTION1: + break; + + /* Noise on anything else */ + default: + qemu_log_mask(LOG_UNIMP, "phb3: reg_write 0x%"PRIx64"=%"PRIx64"\n", + off, val); + } +} + +uint64_t pnv_phb3_reg_read(void *opaque, hwaddr off, unsigned size) +{ + PnvPHB3 *phb = opaque; + PCIHostState *pci = PCI_HOST_BRIDGE(phb); + uint64_t val; + + if ((off & 0xfffc) == PHB_CONFIG_DATA) { + return pnv_phb3_config_read(phb, off & 0x3, size); + } + + /* Other registers are 64-bit only */ + if (size != 8 || off & 0x7) { + phb3_error(phb, "Invalid register access, offset: 0x%"PRIx64" size: %d", + off, size); + return ~0ull; + } + + /* Default read from cache */ + val = phb->regs[off >> 3]; + + switch (off) { + /* Simulate venice DD2.0 */ + case PHB_VERSION: + return 0x000000a300000005ull; + case PHB_PCIE_SYSTEM_CONFIG: + return 0x441100fc30000000; + + /* IODA table accesses */ + case PHB_IODA_DATA0: + return pnv_phb3_ioda_read(phb); + + /* Link training always appears trained */ + case PHB_PCIE_DLP_TRAIN_CTL: + if (!pci_find_device(pci->bus, 1, 0)) { + return 0; + } + return PHB_PCIE_DLP_INBAND_PRESENCE | PHB_PCIE_DLP_TC_DL_LINKACT; + + /* FFI Lock */ + case PHB_FFI_LOCK: + /* Set lock and return previous value */ + phb->regs[off >> 3] |= PHB_FFI_LOCK_STATE; + return val; + + /* DMA read sync: make it look like it's complete */ + case PHB_DMARD_SYNC: + return PHB_DMARD_SYNC_COMPLETE; + + /* Silent simple reads */ + case PHB_PHB3_CONFIG: + case PHB_M32_BASE_ADDR: + case PHB_M32_BASE_MASK: + case PHB_M32_START_ADDR: + case PHB_CONFIG_ADDRESS: + case PHB_IODA_ADDR: + case PHB_RTC_INVALIDATE: + case PHB_TCE_KILL: + case PHB_TCE_SPEC_CTL: + case PHB_PEST_BAR: + case PHB_PELTV_BAR: + case PHB_RTT_BAR: + case PHB_RBA_BAR: + case PHB_IVT_BAR: + case PHB_M64_UPPER_BITS: + case PHB_LEM_FIR_ACCUM: + case PHB_LEM_ERROR_MASK: + case PHB_LEM_ACTION0: + case PHB_LEM_ACTION1: + break; + + /* Noise on anything else */ + default: + qemu_log_mask(LOG_UNIMP, "phb3: reg_read 0x%"PRIx64"=%"PRIx64"\n", + off, val); + } + return val; +} + +static const MemoryRegionOps pnv_phb3_reg_ops = { + .read = pnv_phb3_reg_read, + .write = pnv_phb3_reg_write, + .valid.min_access_size = 1, + .valid.max_access_size = 8, + .impl.min_access_size = 1, + .impl.max_access_size = 8, + .endianness = DEVICE_BIG_ENDIAN, +}; + +static int pnv_phb3_map_irq(PCIDevice *pci_dev, int irq_num) +{ + /* Check that out properly ... */ + return irq_num & 3; +} + +static void pnv_phb3_set_irq(void *opaque, int irq_num, int level) +{ + PnvPHB3 *phb = opaque; + + /* LSI only ... */ + if (irq_num > 3) { + phb3_error(phb, "Unknown IRQ to set %d", irq_num); + } + qemu_set_irq(phb->qirqs[irq_num], level); +} + +static bool pnv_phb3_resolve_pe(PnvPhb3DMASpace *ds) +{ + uint64_t rtt, addr; + uint16_t rte; + int bus_num; + + /* Already resolved ? */ + if (ds->pe_num != PHB_INVALID_PE) { + return true; + } + + /* We need to lookup the RTT */ + rtt = ds->phb->regs[PHB_RTT_BAR >> 3]; + if (!(rtt & PHB_RTT_BAR_ENABLE)) { + phb3_error(ds->phb, "DMA with RTT BAR disabled !"); + /* Set error bits ? fence ? ... */ + return false; + } + + /* Read RTE */ + bus_num = pci_bus_num(ds->bus); + addr = rtt & PHB_RTT_BASE_ADDRESS_MASK; + addr += 2 * ((bus_num << 8) | ds->devfn); + if (dma_memory_read(&address_space_memory, addr, &rte, sizeof(rte))) { + phb3_error(ds->phb, "Failed to read RTT entry at 0x%"PRIx64, addr); + /* Set error bits ? fence ? ... */ + return false; + } + rte = be16_to_cpu(rte); + + /* Fail upon reading of invalid PE# */ + if (rte >= PNV_PHB3_NUM_PE) { + phb3_error(ds->phb, "RTE for RID 0x%x invalid (%04x", ds->devfn, rte); + /* Set error bits ? fence ? ... */ + return false; + } + ds->pe_num = rte; + return true; +} + +static void pnv_phb3_translate_tve(PnvPhb3DMASpace *ds, hwaddr addr, + bool is_write, uint64_t tve, + IOMMUTLBEntry *tlb) +{ + uint64_t tta = GETFIELD(IODA2_TVT_TABLE_ADDR, tve); + int32_t lev = GETFIELD(IODA2_TVT_NUM_LEVELS, tve); + uint32_t tts = GETFIELD(IODA2_TVT_TCE_TABLE_SIZE, tve); + uint32_t tps = GETFIELD(IODA2_TVT_IO_PSIZE, tve); + PnvPHB3 *phb = ds->phb; + + /* Invalid levels */ + if (lev > 4) { + phb3_error(phb, "Invalid #levels in TVE %d", lev); + return; + } + + /* IO Page Size of 0 means untranslated, else use TCEs */ + if (tps == 0) { + /* + * We only support non-translate in top window. + * + * TODO: Venice/Murano support it on bottom window above 4G and + * Naples suports it on everything + */ + if (!(tve & PPC_BIT(51))) { + phb3_error(phb, "xlate for invalid non-translate TVE"); + return; + } + /* TODO: Handle boundaries */ + + /* Use 4k pages like q35 ... for now */ + tlb->iova = addr & 0xfffffffffffff000ull; + tlb->translated_addr = addr & 0x0003fffffffff000ull; + tlb->addr_mask = 0xfffull; + tlb->perm = IOMMU_RW; + } else { + uint32_t tce_shift, tbl_shift, sh; + uint64_t base, taddr, tce, tce_mask; + + /* TVE disabled ? */ + if (tts == 0) { + phb3_error(phb, "xlate for invalid translated TVE"); + return; + } + + /* Address bits per bottom level TCE entry */ + tce_shift = tps + 11; + + /* Address bits per table level */ + tbl_shift = tts + 8; + + /* Top level table base address */ + base = tta << 12; + + /* Total shift to first level */ + sh = tbl_shift * lev + tce_shift; + + /* TODO: Multi-level untested */ + while ((lev--) >= 0) { + /* Grab the TCE address */ + taddr = base | (((addr >> sh) & ((1ul << tbl_shift) - 1)) << 3); + if (dma_memory_read(&address_space_memory, taddr, &tce, + sizeof(tce))) { + phb3_error(phb, "Failed to read TCE at 0x%"PRIx64, taddr); + return; + } + tce = be64_to_cpu(tce); + + /* Check permission for indirect TCE */ + if ((lev >= 0) && !(tce & 3)) { + phb3_error(phb, "Invalid indirect TCE at 0x%"PRIx64, taddr); + phb3_error(phb, " xlate %"PRIx64":%c TVE=%"PRIx64, addr, + is_write ? 'W' : 'R', tve); + phb3_error(phb, " tta=%"PRIx64" lev=%d tts=%d tps=%d", + tta, lev, tts, tps); + return; + } + sh -= tbl_shift; + base = tce & ~0xfffull; + } + + /* We exit the loop with TCE being the final TCE */ + tce_mask = ~((1ull << tce_shift) - 1); + tlb->iova = addr & tce_mask; + tlb->translated_addr = tce & tce_mask; + tlb->addr_mask = ~tce_mask; + tlb->perm = tce & 3; + if ((is_write & !(tce & 2)) || ((!is_write) && !(tce & 1))) { + phb3_error(phb, "TCE access fault at 0x%"PRIx64, taddr); + phb3_error(phb, " xlate %"PRIx64":%c TVE=%"PRIx64, addr, + is_write ? 'W' : 'R', tve); + phb3_error(phb, " tta=%"PRIx64" lev=%d tts=%d tps=%d", + tta, lev, tts, tps); + } + } +} + +static IOMMUTLBEntry pnv_phb3_translate_iommu(IOMMUMemoryRegion *iommu, + hwaddr addr, + IOMMUAccessFlags flag, + int iommu_idx) +{ + PnvPhb3DMASpace *ds = container_of(iommu, PnvPhb3DMASpace, dma_mr); + int tve_sel; + uint64_t tve, cfg; + IOMMUTLBEntry ret = { + .target_as = &address_space_memory, + .iova = addr, + .translated_addr = 0, + .addr_mask = ~(hwaddr)0, + .perm = IOMMU_NONE, + }; + PnvPHB3 *phb = ds->phb; + + /* Resolve PE# */ + if (!pnv_phb3_resolve_pe(ds)) { + phb3_error(phb, "Failed to resolve PE# for bus @%p (%d) devfn 0x%x", + ds->bus, pci_bus_num(ds->bus), ds->devfn); + return ret; + } + + /* Check top bits */ + switch (addr >> 60) { + case 00: + /* DMA or 32-bit MSI ? */ + cfg = ds->phb->regs[PHB_PHB3_CONFIG >> 3]; + if ((cfg & PHB_PHB3C_32BIT_MSI_EN) && + ((addr & 0xffffffffffff0000ull) == 0xffff0000ull)) { + phb3_error(phb, "xlate on 32-bit MSI region"); + return ret; + } + /* Choose TVE XXX Use PHB3 Control Register */ + tve_sel = (addr >> 59) & 1; + tve = ds->phb->ioda_TVT[ds->pe_num * 2 + tve_sel]; + pnv_phb3_translate_tve(ds, addr, flag & IOMMU_WO, tve, &ret); + break; + case 01: + phb3_error(phb, "xlate on 64-bit MSI region"); + break; + default: + phb3_error(phb, "xlate on unsupported address 0x%"PRIx64, addr); + } + return ret; +} + +#define TYPE_PNV_PHB3_IOMMU_MEMORY_REGION "pnv-phb3-iommu-memory-region" +#define PNV_PHB3_IOMMU_MEMORY_REGION(obj) \ + OBJECT_CHECK(IOMMUMemoryRegion, (obj), TYPE_PNV_PHB3_IOMMU_MEMORY_REGION) + +static void pnv_phb3_iommu_memory_region_class_init(ObjectClass *klass, + void *data) +{ + IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass); + + imrc->translate = pnv_phb3_translate_iommu; +} + +static const TypeInfo pnv_phb3_iommu_memory_region_info = { + .parent = TYPE_IOMMU_MEMORY_REGION, + .name = TYPE_PNV_PHB3_IOMMU_MEMORY_REGION, + .class_init = pnv_phb3_iommu_memory_region_class_init, +}; + +/* + * MSI/MSIX memory region implementation. + * The handler handles both MSI and MSIX. + */ +static void pnv_phb3_msi_write(void *opaque, hwaddr addr, + uint64_t data, unsigned size) +{ + PnvPhb3DMASpace *ds = opaque; + + /* Resolve PE# */ + if (!pnv_phb3_resolve_pe(ds)) { + phb3_error(ds->phb, "Failed to resolve PE# for bus @%p (%d) devfn 0x%x", + ds->bus, pci_bus_num(ds->bus), ds->devfn); + return; + } + + pnv_phb3_msi_send(&ds->phb->msis, addr, data, ds->pe_num); +} + +/* There is no .read as the read result is undefined by PCI spec */ +static uint64_t pnv_phb3_msi_read(void *opaque, hwaddr addr, unsigned size) +{ + PnvPhb3DMASpace *ds = opaque; + + phb3_error(ds->phb, "invalid read @ 0x%" HWADDR_PRIx, addr); + return -1; +} + +static const MemoryRegionOps pnv_phb3_msi_ops = { + .read = pnv_phb3_msi_read, + .write = pnv_phb3_msi_write, + .endianness = DEVICE_LITTLE_ENDIAN +}; + +static AddressSpace *pnv_phb3_dma_iommu(PCIBus *bus, void *opaque, int devfn) +{ + PnvPHB3 *phb = opaque; + PnvPhb3DMASpace *ds; + + QLIST_FOREACH(ds, &phb->dma_spaces, list) { + if (ds->bus == bus && ds->devfn == devfn) { + break; + } + } + + if (ds == NULL) { + ds = g_malloc0(sizeof(PnvPhb3DMASpace)); + ds->bus = bus; + ds->devfn = devfn; + ds->pe_num = PHB_INVALID_PE; + ds->phb = phb; + memory_region_init_iommu(&ds->dma_mr, sizeof(ds->dma_mr), + TYPE_PNV_PHB3_IOMMU_MEMORY_REGION, + OBJECT(phb), "phb3_iommu", UINT64_MAX); + address_space_init(&ds->dma_as, MEMORY_REGION(&ds->dma_mr), + "phb3_iommu"); + memory_region_init_io(&ds->msi32_mr, OBJECT(phb), &pnv_phb3_msi_ops, + ds, "msi32", 0x10000); + memory_region_init_io(&ds->msi64_mr, OBJECT(phb), &pnv_phb3_msi_ops, + ds, "msi64", 0x100000); + pnv_phb3_update_msi_regions(ds); + + QLIST_INSERT_HEAD(&phb->dma_spaces, ds, list); + } + return &ds->dma_as; +} + +static void pnv_phb3_instance_init(Object *obj) +{ + PnvPHB3 *phb = PNV_PHB3(obj); + + QLIST_INIT(&phb->dma_spaces); + + /* LSI sources */ + object_initialize_child(obj, "lsi", &phb->lsis, sizeof(phb->lsis), + TYPE_ICS, &error_abort, NULL); + + /* Default init ... will be fixed by HW inits */ + phb->lsis.offset = 0; + + /* MSI sources */ + object_initialize_child(obj, "msi", &phb->msis, sizeof(phb->msis), + TYPE_PHB3_MSI, &error_abort, NULL); + + /* Power Bus Common Queue */ + object_initialize_child(obj, "pbcq", &phb->pbcq, sizeof(phb->pbcq), + TYPE_PNV_PBCQ, &error_abort, NULL); + + /* Root Port */ + object_initialize_child(obj, "root", &phb->root, sizeof(phb->root), + TYPE_PNV_PHB3_ROOT_PORT, &error_abort, NULL); + qdev_prop_set_int32(DEVICE(&phb->root), "addr", PCI_DEVFN(0, 0)); + qdev_prop_set_bit(DEVICE(&phb->root), "multifunction", false); +} + +static void pnv_phb3_realize(DeviceState *dev, Error **errp) +{ + PnvPHB3 *phb = PNV_PHB3(dev); + PCIHostState *pci = PCI_HOST_BRIDGE(dev); + PnvMachineState *pnv = PNV_MACHINE(qdev_get_machine()); + Error *local_err = NULL; + int i; + + if (phb->phb_id >= PNV8_CHIP_PHB3_MAX) { + error_setg(errp, "invalid PHB index: %d", phb->phb_id); + return; + } + + /* LSI sources */ + object_property_set_link(OBJECT(&phb->lsis), OBJECT(pnv), "xics", + &error_abort); + object_property_set_int(OBJECT(&phb->lsis), PNV_PHB3_NUM_LSI, "nr-irqs", + &error_abort); + object_property_set_bool(OBJECT(&phb->lsis), true, "realized", &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + for (i = 0; i < phb->lsis.nr_irqs; i++) { + ics_set_irq_type(&phb->lsis, i, true); + } + + phb->qirqs = qemu_allocate_irqs(ics_set_irq, &phb->lsis, phb->lsis.nr_irqs); + + /* MSI sources */ + object_property_set_link(OBJECT(&phb->msis), OBJECT(phb), "phb", + &error_abort); + object_property_set_link(OBJECT(&phb->msis), OBJECT(pnv), "xics", + &error_abort); + object_property_set_int(OBJECT(&phb->msis), PHB3_MAX_MSI, "nr-irqs", + &error_abort); + object_property_set_bool(OBJECT(&phb->msis), true, "realized", &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + /* Power Bus Common Queue */ + object_property_set_link(OBJECT(&phb->pbcq), OBJECT(phb), "phb", + &error_abort); + object_property_set_bool(OBJECT(&phb->pbcq), true, "realized", &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + /* Controller Registers */ + memory_region_init_io(&phb->mr_regs, OBJECT(phb), &pnv_phb3_reg_ops, phb, + "phb3-regs", 0x1000); + + /* + * PHB3 doesn't support IO space. However, qemu gets very upset if + * we don't have an IO region to anchor IO BARs onto so we just + * initialize one which we never hook up to anything + */ + memory_region_init(&phb->pci_io, OBJECT(phb), "pci-io", 0x10000); + memory_region_init(&phb->pci_mmio, OBJECT(phb), "pci-mmio", + PCI_MMIO_TOTAL_SIZE); + + pci->bus = pci_register_root_bus(dev, "root-bus", + pnv_phb3_set_irq, pnv_phb3_map_irq, phb, + &phb->pci_mmio, &phb->pci_io, + 0, 4, TYPE_PNV_PHB3_ROOT_BUS); + + pci_setup_iommu(pci->bus, pnv_phb3_dma_iommu, phb); + + /* Add a single Root port */ + qdev_prop_set_uint8(DEVICE(&phb->root), "chassis", phb->chip_id); + qdev_prop_set_uint16(DEVICE(&phb->root), "slot", phb->phb_id); + qdev_set_parent_bus(DEVICE(&phb->root), BUS(pci->bus)); + qdev_init_nofail(DEVICE(&phb->root)); +} + +void pnv_phb3_update_regions(PnvPHB3 *phb) +{ + PnvPBCQState *pbcq = &phb->pbcq; + + /* Unmap first always */ + if (memory_region_is_mapped(&phb->mr_regs)) { + memory_region_del_subregion(&pbcq->phbbar, &phb->mr_regs); + } + + /* Map registers if enabled */ + if (memory_region_is_mapped(&pbcq->phbbar)) { + /* TODO: We should use the PHB BAR 2 register but we don't ... */ + memory_region_add_subregion(&pbcq->phbbar, 0, &phb->mr_regs); + } + + /* Check/update m32 */ + if (memory_region_is_mapped(&phb->mr_m32)) { + pnv_phb3_check_m32(phb); + } + pnv_phb3_check_all_m64s(phb); +} + +static const char *pnv_phb3_root_bus_path(PCIHostState *host_bridge, + PCIBus *rootbus) +{ + PnvPHB3 *phb = PNV_PHB3(host_bridge); + + snprintf(phb->bus_path, sizeof(phb->bus_path), "00%02x:%02x", + phb->chip_id, phb->phb_id); + return phb->bus_path; +} + +static Property pnv_phb3_properties[] = { + DEFINE_PROP_UINT32("index", PnvPHB3, phb_id, 0), + DEFINE_PROP_UINT32("chip-id", PnvPHB3, chip_id, 0), + DEFINE_PROP_END_OF_LIST(), +}; + +static void pnv_phb3_class_init(ObjectClass *klass, void *data) +{ + PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + + hc->root_bus_path = pnv_phb3_root_bus_path; + dc->realize = pnv_phb3_realize; + device_class_set_props(dc, pnv_phb3_properties); + set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); + dc->user_creatable = false; +} + +static const TypeInfo pnv_phb3_type_info = { + .name = TYPE_PNV_PHB3, + .parent = TYPE_PCIE_HOST_BRIDGE, + .instance_size = sizeof(PnvPHB3), + .class_init = pnv_phb3_class_init, + .instance_init = pnv_phb3_instance_init, +}; + +static void pnv_phb3_root_bus_class_init(ObjectClass *klass, void *data) +{ + BusClass *k = BUS_CLASS(klass); + + /* + * PHB3 has only a single root complex. Enforce the limit on the + * parent bus + */ + k->max_dev = 1; +} + +static const TypeInfo pnv_phb3_root_bus_info = { + .name = TYPE_PNV_PHB3_ROOT_BUS, + .parent = TYPE_PCIE_BUS, + .class_init = pnv_phb3_root_bus_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_PCIE_DEVICE }, + { } + }, +}; + +static void pnv_phb3_root_port_realize(DeviceState *dev, Error **errp) +{ + PCIERootPortClass *rpc = PCIE_ROOT_PORT_GET_CLASS(dev); + Error *local_err = NULL; + + rpc->parent_realize(dev, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } +} + +static void pnv_phb3_root_port_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + PCIERootPortClass *rpc = PCIE_ROOT_PORT_CLASS(klass); + + dc->desc = "IBM PHB3 PCIE Root Port"; + + device_class_set_parent_realize(dc, pnv_phb3_root_port_realize, + &rpc->parent_realize); + dc->user_creatable = false; + + k->vendor_id = PCI_VENDOR_ID_IBM; + k->device_id = 0x03dc; + k->revision = 0; + + rpc->exp_offset = 0x48; + rpc->aer_offset = 0x100; +} + +static const TypeInfo pnv_phb3_root_port_info = { + .name = TYPE_PNV_PHB3_ROOT_PORT, + .parent = TYPE_PCIE_ROOT_PORT, + .instance_size = sizeof(PnvPHB3RootPort), + .class_init = pnv_phb3_root_port_class_init, +}; + +static void pnv_phb3_register_types(void) +{ + type_register_static(&pnv_phb3_root_bus_info); + type_register_static(&pnv_phb3_root_port_info); + type_register_static(&pnv_phb3_type_info); + type_register_static(&pnv_phb3_iommu_memory_region_info); +} + +type_init(pnv_phb3_register_types) diff --git a/hw/pci-host/pnv_phb3_msi.c b/hw/pci-host/pnv_phb3_msi.c new file mode 100644 index 0000000000..d645468f4a --- /dev/null +++ b/hw/pci-host/pnv_phb3_msi.c @@ -0,0 +1,349 @@ +/* + * QEMU PowerPC PowerNV (POWER8) PHB3 model + * + * Copyright (c) 2014-2020, IBM Corporation. + * + * This code is licensed under the GPL version 2 or later. See the + * COPYING file in the top-level directory. + */ +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "hw/pci-host/pnv_phb3_regs.h" +#include "hw/pci-host/pnv_phb3.h" +#include "hw/ppc/pnv.h" +#include "hw/pci/msi.h" +#include "monitor/monitor.h" +#include "hw/irq.h" +#include "hw/qdev-properties.h" +#include "sysemu/reset.h" + +static uint64_t phb3_msi_ive_addr(PnvPHB3 *phb, int srcno) +{ + uint64_t ivtbar = phb->regs[PHB_IVT_BAR >> 3]; + uint64_t phbctl = phb->regs[PHB_CONTROL >> 3]; + + if (!(ivtbar & PHB_IVT_BAR_ENABLE)) { + qemu_log_mask(LOG_GUEST_ERROR, "Failed access to disable IVT BAR !"); + return 0; + } + + if (srcno >= (ivtbar & PHB_IVT_LENGTH_MASK)) { + qemu_log_mask(LOG_GUEST_ERROR, "MSI out of bounds (%d vs 0x%"PRIx64")", + srcno, (uint64_t) (ivtbar & PHB_IVT_LENGTH_MASK)); + return 0; + } + + ivtbar &= PHB_IVT_BASE_ADDRESS_MASK; + + if (phbctl & PHB_CTRL_IVE_128_BYTES) { + return ivtbar + 128 * srcno; + } else { + return ivtbar + 16 * srcno; + } +} + +static bool phb3_msi_read_ive(PnvPHB3 *phb, int srcno, uint64_t *out_ive) +{ + uint64_t ive_addr, ive; + + ive_addr = phb3_msi_ive_addr(phb, srcno); + if (!ive_addr) { + return false; + } + + if (dma_memory_read(&address_space_memory, ive_addr, &ive, sizeof(ive))) { + qemu_log_mask(LOG_GUEST_ERROR, "Failed to read IVE at 0x%" PRIx64, + ive_addr); + return false; + } + *out_ive = be64_to_cpu(ive); + + return true; +} + +static void phb3_msi_set_p(Phb3MsiState *msi, int srcno, uint8_t gen) +{ + uint64_t ive_addr; + uint8_t p = 0x01 | (gen << 1); + + ive_addr = phb3_msi_ive_addr(msi->phb, srcno); + if (!ive_addr) { + return; + } + + if (dma_memory_write(&address_space_memory, ive_addr + 4, &p, 1)) { + qemu_log_mask(LOG_GUEST_ERROR, + "Failed to write IVE (set P) at 0x%" PRIx64, ive_addr); + } +} + +static void phb3_msi_set_q(Phb3MsiState *msi, int srcno) +{ + uint64_t ive_addr; + uint8_t q = 0x01; + + ive_addr = phb3_msi_ive_addr(msi->phb, srcno); + if (!ive_addr) { + return; + } + + if (dma_memory_write(&address_space_memory, ive_addr + 5, &q, 1)) { + qemu_log_mask(LOG_GUEST_ERROR, + "Failed to write IVE (set Q) at 0x%" PRIx64, ive_addr); + } +} + +static void phb3_msi_try_send(Phb3MsiState *msi, int srcno, bool force) +{ + ICSState *ics = ICS(msi); + uint64_t ive; + uint64_t server, prio, pq, gen; + + if (!phb3_msi_read_ive(msi->phb, srcno, &ive)) { + return; + } + + server = GETFIELD(IODA2_IVT_SERVER, ive); + prio = GETFIELD(IODA2_IVT_PRIORITY, ive); + if (!force) { + pq = GETFIELD(IODA2_IVT_Q, ive) | (GETFIELD(IODA2_IVT_P, ive) << 1); + } else { + pq = 0; + } + gen = GETFIELD(IODA2_IVT_GEN, ive); + + /* + * The low order 2 bits are the link pointer (Type II interrupts). + * Shift back to get a valid IRQ server. + */ + server >>= 2; + + switch (pq) { + case 0: /* 00 */ + if (prio == 0xff) { + /* Masked, set Q */ + phb3_msi_set_q(msi, srcno); + } else { + /* Enabled, set P and send */ + phb3_msi_set_p(msi, srcno, gen); + icp_irq(ics, server, srcno + ics->offset, prio); + } + break; + case 2: /* 10 */ + /* Already pending, set Q */ + phb3_msi_set_q(msi, srcno); + break; + case 1: /* 01 */ + case 3: /* 11 */ + default: + /* Just drop stuff if Q already set */ + break; + } +} + +static void phb3_msi_set_irq(void *opaque, int srcno, int val) +{ + Phb3MsiState *msi = PHB3_MSI(opaque); + + if (val) { + phb3_msi_try_send(msi, srcno, false); + } +} + + +void pnv_phb3_msi_send(Phb3MsiState *msi, uint64_t addr, uint16_t data, + int32_t dev_pe) +{ + ICSState *ics = ICS(msi); + uint64_t ive; + uint16_t pe; + uint32_t src = ((addr >> 4) & 0xffff) | (data & 0x1f); + + if (src >= ics->nr_irqs) { + qemu_log_mask(LOG_GUEST_ERROR, "MSI %d out of bounds", src); + return; + } + if (dev_pe >= 0) { + if (!phb3_msi_read_ive(msi->phb, src, &ive)) { + return; + } + pe = GETFIELD(IODA2_IVT_PE, ive); + if (pe != dev_pe) { + qemu_log_mask(LOG_GUEST_ERROR, + "MSI %d send by PE#%d but assigned to PE#%d", + src, dev_pe, pe); + return; + } + } + qemu_irq_pulse(msi->qirqs[src]); +} + +void pnv_phb3_msi_ffi(Phb3MsiState *msi, uint64_t val) +{ + /* Emit interrupt */ + pnv_phb3_msi_send(msi, val, 0, -1); + + /* Clear FFI lock */ + msi->phb->regs[PHB_FFI_LOCK >> 3] = 0; +} + +static void phb3_msi_reject(ICSState *ics, uint32_t nr) +{ + Phb3MsiState *msi = PHB3_MSI(ics); + unsigned int srcno = nr - ics->offset; + unsigned int idx = srcno >> 6; + unsigned int bit = 1ull << (srcno & 0x3f); + + assert(srcno < PHB3_MAX_MSI); + + msi->rba[idx] |= bit; + msi->rba_sum |= (1u << idx); +} + +static void phb3_msi_resend(ICSState *ics) +{ + Phb3MsiState *msi = PHB3_MSI(ics); + unsigned int i, j; + + if (msi->rba_sum == 0) { + return; + } + + for (i = 0; i < 32; i++) { + if ((msi->rba_sum & (1u << i)) == 0) { + continue; + } + msi->rba_sum &= ~(1u << i); + for (j = 0; j < 64; j++) { + if ((msi->rba[i] & (1ull << j)) == 0) { + continue; + } + msi->rba[i] &= ~(1ull << j); + phb3_msi_try_send(msi, i * 64 + j, true); + } + } +} + +static void phb3_msi_reset(DeviceState *dev) +{ + Phb3MsiState *msi = PHB3_MSI(dev); + ICSStateClass *icsc = ICS_GET_CLASS(dev); + + icsc->parent_reset(dev); + + memset(msi->rba, 0, sizeof(msi->rba)); + msi->rba_sum = 0; +} + +static void phb3_msi_reset_handler(void *dev) +{ + phb3_msi_reset(dev); +} + +void pnv_phb3_msi_update_config(Phb3MsiState *msi, uint32_t base, + uint32_t count) +{ + ICSState *ics = ICS(msi); + + if (count > PHB3_MAX_MSI) { + count = PHB3_MAX_MSI; + } + ics->nr_irqs = count; + ics->offset = base; +} + +static void phb3_msi_realize(DeviceState *dev, Error **errp) +{ + Phb3MsiState *msi = PHB3_MSI(dev); + ICSState *ics = ICS(msi); + ICSStateClass *icsc = ICS_GET_CLASS(ics); + Error *local_err = NULL; + + assert(msi->phb); + + icsc->parent_realize(dev, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + msi->qirqs = qemu_allocate_irqs(phb3_msi_set_irq, msi, ics->nr_irqs); + + qemu_register_reset(phb3_msi_reset_handler, dev); +} + +static void phb3_msi_instance_init(Object *obj) +{ + Phb3MsiState *msi = PHB3_MSI(obj); + ICSState *ics = ICS(obj); + + object_property_add_link(obj, "phb", TYPE_PNV_PHB3, + (Object **)&msi->phb, + object_property_allow_set_link, + OBJ_PROP_LINK_STRONG, + &error_abort); + + /* Will be overriden later */ + ics->offset = 0; +} + +static void phb3_msi_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ICSStateClass *isc = ICS_CLASS(klass); + + device_class_set_parent_realize(dc, phb3_msi_realize, + &isc->parent_realize); + device_class_set_parent_reset(dc, phb3_msi_reset, + &isc->parent_reset); + + isc->reject = phb3_msi_reject; + isc->resend = phb3_msi_resend; +} + +static const TypeInfo phb3_msi_info = { + .name = TYPE_PHB3_MSI, + .parent = TYPE_ICS, + .instance_size = sizeof(Phb3MsiState), + .class_init = phb3_msi_class_init, + .class_size = sizeof(ICSStateClass), + .instance_init = phb3_msi_instance_init, +}; + +static void pnv_phb3_msi_register_types(void) +{ + type_register_static(&phb3_msi_info); +} + +type_init(pnv_phb3_msi_register_types); + +void pnv_phb3_msi_pic_print_info(Phb3MsiState *msi, Monitor *mon) +{ + ICSState *ics = ICS(msi); + int i; + + monitor_printf(mon, "ICS %4x..%4x %p\n", + ics->offset, ics->offset + ics->nr_irqs - 1, ics); + + for (i = 0; i < ics->nr_irqs; i++) { + uint64_t ive; + + if (!phb3_msi_read_ive(msi->phb, i, &ive)) { + return; + } + + if (GETFIELD(IODA2_IVT_PRIORITY, ive) == 0xff) { + continue; + } + + monitor_printf(mon, " %4x %c%c server=%04x prio=%02x gen=%d\n", + ics->offset + i, + GETFIELD(IODA2_IVT_P, ive) ? 'P' : '-', + GETFIELD(IODA2_IVT_Q, ive) ? 'Q' : '-', + (uint32_t) GETFIELD(IODA2_IVT_SERVER, ive) >> 2, + (uint32_t) GETFIELD(IODA2_IVT_PRIORITY, ive), + (uint32_t) GETFIELD(IODA2_IVT_GEN, ive)); + } +} diff --git a/hw/pci-host/pnv_phb3_pbcq.c b/hw/pci-host/pnv_phb3_pbcq.c new file mode 100644 index 0000000000..7b9a121246 --- /dev/null +++ b/hw/pci-host/pnv_phb3_pbcq.c @@ -0,0 +1,359 @@ +/* + * QEMU PowerPC PowerNV (POWER8) PHB3 model + * + * Copyright (c) 2014-2020, IBM Corporation. + * + * This code is licensed under the GPL version 2 or later. See the + * COPYING file in the top-level directory. + */ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "qemu/log.h" +#include "target/ppc/cpu.h" +#include "hw/ppc/fdt.h" +#include "hw/pci-host/pnv_phb3_regs.h" +#include "hw/pci-host/pnv_phb3.h" +#include "hw/ppc/pnv.h" +#include "hw/ppc/pnv_xscom.h" +#include "hw/pci/pci_bridge.h" +#include "hw/pci/pci_bus.h" + +#include + +#define phb3_pbcq_error(pbcq, fmt, ...) \ + qemu_log_mask(LOG_GUEST_ERROR, "phb3_pbcq[%d:%d]: " fmt "\n", \ + (pbcq)->phb->chip_id, (pbcq)->phb->phb_id, ## __VA_ARGS__) + +static uint64_t pnv_pbcq_nest_xscom_read(void *opaque, hwaddr addr, + unsigned size) +{ + PnvPBCQState *pbcq = PNV_PBCQ(opaque); + uint32_t offset = addr >> 3; + + return pbcq->nest_regs[offset]; +} + +static uint64_t pnv_pbcq_pci_xscom_read(void *opaque, hwaddr addr, + unsigned size) +{ + PnvPBCQState *pbcq = PNV_PBCQ(opaque); + uint32_t offset = addr >> 3; + + return pbcq->pci_regs[offset]; +} + +static uint64_t pnv_pbcq_spci_xscom_read(void *opaque, hwaddr addr, + unsigned size) +{ + PnvPBCQState *pbcq = PNV_PBCQ(opaque); + uint32_t offset = addr >> 3; + + if (offset == PBCQ_SPCI_ASB_DATA) { + return pnv_phb3_reg_read(pbcq->phb, + pbcq->spci_regs[PBCQ_SPCI_ASB_ADDR], 8); + } + return pbcq->spci_regs[offset]; +} + +static void pnv_pbcq_update_map(PnvPBCQState *pbcq) +{ + uint64_t bar_en = pbcq->nest_regs[PBCQ_NEST_BAR_EN]; + uint64_t bar, mask, size; + + /* + * NOTE: This will really not work well if those are remapped + * after the PHB has created its sub regions. We could do better + * if we had a way to resize regions but we don't really care + * that much in practice as the stuff below really only happens + * once early during boot + */ + + /* Handle unmaps */ + if (memory_region_is_mapped(&pbcq->mmbar0) && + !(bar_en & PBCQ_NEST_BAR_EN_MMIO0)) { + memory_region_del_subregion(get_system_memory(), &pbcq->mmbar0); + } + if (memory_region_is_mapped(&pbcq->mmbar1) && + !(bar_en & PBCQ_NEST_BAR_EN_MMIO1)) { + memory_region_del_subregion(get_system_memory(), &pbcq->mmbar1); + } + if (memory_region_is_mapped(&pbcq->phbbar) && + !(bar_en & PBCQ_NEST_BAR_EN_PHB)) { + memory_region_del_subregion(get_system_memory(), &pbcq->phbbar); + } + + /* Update PHB */ + pnv_phb3_update_regions(pbcq->phb); + + /* Handle maps */ + if (!memory_region_is_mapped(&pbcq->mmbar0) && + (bar_en & PBCQ_NEST_BAR_EN_MMIO0)) { + bar = pbcq->nest_regs[PBCQ_NEST_MMIO_BAR0] >> 14; + mask = pbcq->nest_regs[PBCQ_NEST_MMIO_MASK0]; + size = ((~mask) >> 14) + 1; + memory_region_init(&pbcq->mmbar0, OBJECT(pbcq), "pbcq-mmio0", size); + memory_region_add_subregion(get_system_memory(), bar, &pbcq->mmbar0); + pbcq->mmio0_base = bar; + pbcq->mmio0_size = size; + } + if (!memory_region_is_mapped(&pbcq->mmbar1) && + (bar_en & PBCQ_NEST_BAR_EN_MMIO1)) { + bar = pbcq->nest_regs[PBCQ_NEST_MMIO_BAR1] >> 14; + mask = pbcq->nest_regs[PBCQ_NEST_MMIO_MASK1]; + size = ((~mask) >> 14) + 1; + memory_region_init(&pbcq->mmbar1, OBJECT(pbcq), "pbcq-mmio1", size); + memory_region_add_subregion(get_system_memory(), bar, &pbcq->mmbar1); + pbcq->mmio1_base = bar; + pbcq->mmio1_size = size; + } + if (!memory_region_is_mapped(&pbcq->phbbar) + && (bar_en & PBCQ_NEST_BAR_EN_PHB)) { + bar = pbcq->nest_regs[PBCQ_NEST_PHB_BAR] >> 14; + size = 0x1000; + memory_region_init(&pbcq->phbbar, OBJECT(pbcq), "pbcq-phb", size); + memory_region_add_subregion(get_system_memory(), bar, &pbcq->phbbar); + } + + /* Update PHB */ + pnv_phb3_update_regions(pbcq->phb); +} + +static void pnv_pbcq_nest_xscom_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + PnvPBCQState *pbcq = PNV_PBCQ(opaque); + uint32_t reg = addr >> 3; + + switch (reg) { + case PBCQ_NEST_MMIO_BAR0: + case PBCQ_NEST_MMIO_BAR1: + case PBCQ_NEST_MMIO_MASK0: + case PBCQ_NEST_MMIO_MASK1: + if (pbcq->nest_regs[PBCQ_NEST_BAR_EN] & + (PBCQ_NEST_BAR_EN_MMIO0 | + PBCQ_NEST_BAR_EN_MMIO1)) { + phb3_pbcq_error(pbcq, "Changing enabled BAR unsupported"); + } + pbcq->nest_regs[reg] = val & 0xffffffffc0000000ull; + break; + case PBCQ_NEST_PHB_BAR: + if (pbcq->nest_regs[PBCQ_NEST_BAR_EN] & PBCQ_NEST_BAR_EN_PHB) { + phb3_pbcq_error(pbcq, "Changing enabled BAR unsupported"); + } + pbcq->nest_regs[reg] = val & 0xfffffffffc000000ull; + break; + case PBCQ_NEST_BAR_EN: + pbcq->nest_regs[reg] = val & 0xf800000000000000ull; + pnv_pbcq_update_map(pbcq); + pnv_phb3_remap_irqs(pbcq->phb); + break; + case PBCQ_NEST_IRSN_COMPARE: + case PBCQ_NEST_IRSN_MASK: + pbcq->nest_regs[reg] = val & PBCQ_NEST_IRSN_COMP; + pnv_phb3_remap_irqs(pbcq->phb); + break; + case PBCQ_NEST_LSI_SRC_ID: + pbcq->nest_regs[reg] = val & PBCQ_NEST_LSI_SRC; + pnv_phb3_remap_irqs(pbcq->phb); + break; + default: + phb3_pbcq_error(pbcq, "%s @0x%"HWADDR_PRIx"=%"PRIx64, __func__, + addr, val); + } +} + +static void pnv_pbcq_pci_xscom_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + PnvPBCQState *pbcq = PNV_PBCQ(opaque); + uint32_t reg = addr >> 3; + + switch (reg) { + case PBCQ_PCI_BAR2: + pbcq->pci_regs[reg] = val & 0xfffffffffc000000ull; + pnv_pbcq_update_map(pbcq); + break; + default: + phb3_pbcq_error(pbcq, "%s @0x%"HWADDR_PRIx"=%"PRIx64, __func__, + addr, val); + } +} + +static void pnv_pbcq_spci_xscom_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + PnvPBCQState *pbcq = PNV_PBCQ(opaque); + uint32_t reg = addr >> 3; + + switch (reg) { + case PBCQ_SPCI_ASB_ADDR: + pbcq->spci_regs[reg] = val & 0xfff; + break; + case PBCQ_SPCI_ASB_STATUS: + pbcq->spci_regs[reg] &= ~val; + break; + case PBCQ_SPCI_ASB_DATA: + pnv_phb3_reg_write(pbcq->phb, pbcq->spci_regs[PBCQ_SPCI_ASB_ADDR], + val, 8); + break; + case PBCQ_SPCI_AIB_CAPP_EN: + case PBCQ_SPCI_CAPP_SEC_TMR: + break; + default: + phb3_pbcq_error(pbcq, "%s @0x%"HWADDR_PRIx"=%"PRIx64, __func__, + addr, val); + } +} + +static const MemoryRegionOps pnv_pbcq_nest_xscom_ops = { + .read = pnv_pbcq_nest_xscom_read, + .write = pnv_pbcq_nest_xscom_write, + .valid.min_access_size = 8, + .valid.max_access_size = 8, + .impl.min_access_size = 8, + .impl.max_access_size = 8, + .endianness = DEVICE_BIG_ENDIAN, +}; + +static const MemoryRegionOps pnv_pbcq_pci_xscom_ops = { + .read = pnv_pbcq_pci_xscom_read, + .write = pnv_pbcq_pci_xscom_write, + .valid.min_access_size = 8, + .valid.max_access_size = 8, + .impl.min_access_size = 8, + .impl.max_access_size = 8, + .endianness = DEVICE_BIG_ENDIAN, +}; + +static const MemoryRegionOps pnv_pbcq_spci_xscom_ops = { + .read = pnv_pbcq_spci_xscom_read, + .write = pnv_pbcq_spci_xscom_write, + .valid.min_access_size = 8, + .valid.max_access_size = 8, + .impl.min_access_size = 8, + .impl.max_access_size = 8, + .endianness = DEVICE_BIG_ENDIAN, +}; + +static void pnv_pbcq_default_bars(PnvPBCQState *pbcq) +{ + uint64_t mm0, mm1, reg; + PnvPHB3 *phb = pbcq->phb; + + mm0 = 0x3d00000000000ull + 0x4000000000ull * phb->chip_id + + 0x1000000000ull * phb->phb_id; + mm1 = 0x3ff8000000000ull + 0x0200000000ull * phb->chip_id + + 0x0080000000ull * phb->phb_id; + reg = 0x3fffe40000000ull + 0x0000400000ull * phb->chip_id + + 0x0000100000ull * phb->phb_id; + + pbcq->nest_regs[PBCQ_NEST_MMIO_BAR0] = mm0 << 14; + pbcq->nest_regs[PBCQ_NEST_MMIO_BAR1] = mm1 << 14; + pbcq->nest_regs[PBCQ_NEST_PHB_BAR] = reg << 14; + pbcq->nest_regs[PBCQ_NEST_MMIO_MASK0] = 0x3fff000000000ull << 14; + pbcq->nest_regs[PBCQ_NEST_MMIO_MASK1] = 0x3ffff80000000ull << 14; + pbcq->pci_regs[PBCQ_PCI_BAR2] = reg << 14; +} + +static void pnv_pbcq_realize(DeviceState *dev, Error **errp) +{ + PnvPBCQState *pbcq = PNV_PBCQ(dev); + PnvPHB3 *phb; + char name[32]; + + assert(pbcq->phb); + phb = pbcq->phb; + + /* TODO: Fix OPAL to do that: establish default BAR values */ + pnv_pbcq_default_bars(pbcq); + + /* Initialize the XSCOM region for the PBCQ registers */ + snprintf(name, sizeof(name), "xscom-pbcq-nest-%d.%d", + phb->chip_id, phb->phb_id); + pnv_xscom_region_init(&pbcq->xscom_nest_regs, OBJECT(dev), + &pnv_pbcq_nest_xscom_ops, pbcq, name, + PNV_XSCOM_PBCQ_NEST_SIZE); + snprintf(name, sizeof(name), "xscom-pbcq-pci-%d.%d", + phb->chip_id, phb->phb_id); + pnv_xscom_region_init(&pbcq->xscom_pci_regs, OBJECT(dev), + &pnv_pbcq_pci_xscom_ops, pbcq, name, + PNV_XSCOM_PBCQ_PCI_SIZE); + snprintf(name, sizeof(name), "xscom-pbcq-spci-%d.%d", + phb->chip_id, phb->phb_id); + pnv_xscom_region_init(&pbcq->xscom_spci_regs, OBJECT(dev), + &pnv_pbcq_spci_xscom_ops, pbcq, name, + PNV_XSCOM_PBCQ_SPCI_SIZE); +} + +static int pnv_pbcq_dt_xscom(PnvXScomInterface *dev, void *fdt, + int xscom_offset) +{ + const char compat[] = "ibm,power8-pbcq"; + PnvPHB3 *phb = PNV_PBCQ(dev)->phb; + char *name; + int offset; + uint32_t lpc_pcba = PNV_XSCOM_PBCQ_NEST_BASE + 0x400 * phb->phb_id; + uint32_t reg[] = { + cpu_to_be32(lpc_pcba), + cpu_to_be32(PNV_XSCOM_PBCQ_NEST_SIZE), + cpu_to_be32(PNV_XSCOM_PBCQ_PCI_BASE + 0x400 * phb->phb_id), + cpu_to_be32(PNV_XSCOM_PBCQ_PCI_SIZE), + cpu_to_be32(PNV_XSCOM_PBCQ_SPCI_BASE + 0x040 * phb->phb_id), + cpu_to_be32(PNV_XSCOM_PBCQ_SPCI_SIZE) + }; + + name = g_strdup_printf("pbcq@%x", lpc_pcba); + offset = fdt_add_subnode(fdt, xscom_offset, name); + _FDT(offset); + g_free(name); + + _FDT((fdt_setprop(fdt, offset, "reg", reg, sizeof(reg)))); + + _FDT((fdt_setprop_cell(fdt, offset, "ibm,phb-index", phb->phb_id))); + _FDT((fdt_setprop_cell(fdt, offset, "ibm,chip-id", phb->chip_id))); + _FDT((fdt_setprop(fdt, offset, "compatible", compat, + sizeof(compat)))); + return 0; +} + +static void phb3_pbcq_instance_init(Object *obj) +{ + PnvPBCQState *pbcq = PNV_PBCQ(obj); + + object_property_add_link(obj, "phb", TYPE_PNV_PHB3, + (Object **)&pbcq->phb, + object_property_allow_set_link, + OBJ_PROP_LINK_STRONG, + &error_abort); +} + +static void pnv_pbcq_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PnvXScomInterfaceClass *xdc = PNV_XSCOM_INTERFACE_CLASS(klass); + + xdc->dt_xscom = pnv_pbcq_dt_xscom; + + dc->realize = pnv_pbcq_realize; + dc->user_creatable = false; +} + +static const TypeInfo pnv_pbcq_type_info = { + .name = TYPE_PNV_PBCQ, + .parent = TYPE_DEVICE, + .instance_size = sizeof(PnvPBCQState), + .instance_init = phb3_pbcq_instance_init, + .class_init = pnv_pbcq_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_PNV_XSCOM_INTERFACE }, + { } + } +}; + +static void pnv_pbcq_register_types(void) +{ + type_register_static(&pnv_pbcq_type_info); +} + +type_init(pnv_pbcq_register_types) diff --git a/hw/pci-host/pnv_phb4.c b/hw/pci-host/pnv_phb4.c new file mode 100644 index 0000000000..23cf093928 --- /dev/null +++ b/hw/pci-host/pnv_phb4.c @@ -0,0 +1,1439 @@ +/* + * QEMU PowerPC PowerNV (POWER9) PHB4 model + * + * Copyright (c) 2018-2020, IBM Corporation. + * + * This code is licensed under the GPL version 2 or later. See the + * COPYING file in the top-level directory. + */ +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qapi/visitor.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "monitor/monitor.h" +#include "target/ppc/cpu.h" +#include "hw/pci-host/pnv_phb4_regs.h" +#include "hw/pci-host/pnv_phb4.h" +#include "hw/pci/pcie_host.h" +#include "hw/pci/pcie_port.h" +#include "hw/ppc/pnv.h" +#include "hw/ppc/pnv_xscom.h" +#include "hw/irq.h" +#include "hw/qdev-properties.h" + +#define phb_error(phb, fmt, ...) \ + qemu_log_mask(LOG_GUEST_ERROR, "phb4[%d:%d]: " fmt "\n", \ + (phb)->chip_id, (phb)->phb_id, ## __VA_ARGS__) + +/* + * QEMU version of the GETFIELD/SETFIELD macros + * + * These are common with the PnvXive model. + */ +static inline uint64_t GETFIELD(uint64_t mask, uint64_t word) +{ + return (word & mask) >> ctz64(mask); +} + +static inline uint64_t SETFIELD(uint64_t mask, uint64_t word, + uint64_t value) +{ + return (word & ~mask) | ((value << ctz64(mask)) & mask); +} + +static PCIDevice *pnv_phb4_find_cfg_dev(PnvPHB4 *phb) +{ + PCIHostState *pci = PCI_HOST_BRIDGE(phb); + uint64_t addr = phb->regs[PHB_CONFIG_ADDRESS >> 3]; + uint8_t bus, devfn; + + if (!(addr >> 63)) { + return NULL; + } + bus = (addr >> 52) & 0xff; + devfn = (addr >> 44) & 0xff; + + /* We don't access the root complex this way */ + if (bus == 0 && devfn == 0) { + return NULL; + } + return pci_find_device(pci->bus, bus, devfn); +} + +/* + * The CONFIG_DATA register expects little endian accesses, but as the + * region is big endian, we have to swap the value. + */ +static void pnv_phb4_config_write(PnvPHB4 *phb, unsigned off, + unsigned size, uint64_t val) +{ + uint32_t cfg_addr, limit; + PCIDevice *pdev; + + pdev = pnv_phb4_find_cfg_dev(phb); + if (!pdev) { + return; + } + cfg_addr = (phb->regs[PHB_CONFIG_ADDRESS >> 3] >> 32) & 0xffc; + cfg_addr |= off; + limit = pci_config_size(pdev); + if (limit <= cfg_addr) { + /* + * conventional pci device can be behind pcie-to-pci bridge. + * 256 <= addr < 4K has no effects. + */ + return; + } + switch (size) { + case 1: + break; + case 2: + val = bswap16(val); + break; + case 4: + val = bswap32(val); + break; + default: + g_assert_not_reached(); + } + pci_host_config_write_common(pdev, cfg_addr, limit, val, size); +} + +static uint64_t pnv_phb4_config_read(PnvPHB4 *phb, unsigned off, + unsigned size) +{ + uint32_t cfg_addr, limit; + PCIDevice *pdev; + uint64_t val; + + pdev = pnv_phb4_find_cfg_dev(phb); + if (!pdev) { + return ~0ull; + } + cfg_addr = (phb->regs[PHB_CONFIG_ADDRESS >> 3] >> 32) & 0xffc; + cfg_addr |= off; + limit = pci_config_size(pdev); + if (limit <= cfg_addr) { + /* + * conventional pci device can be behind pcie-to-pci bridge. + * 256 <= addr < 4K has no effects. + */ + return ~0ull; + } + val = pci_host_config_read_common(pdev, cfg_addr, limit, size); + switch (size) { + case 1: + return val; + case 2: + return bswap16(val); + case 4: + return bswap32(val); + default: + g_assert_not_reached(); + } +} + +/* + * Root complex register accesses are memory mapped. + */ +static void pnv_phb4_rc_config_write(PnvPHB4 *phb, unsigned off, + unsigned size, uint64_t val) +{ + PCIHostState *pci = PCI_HOST_BRIDGE(phb); + PCIDevice *pdev; + + if (size != 4) { + phb_error(phb, "rc_config_write invalid size %d\n", size); + return; + } + + pdev = pci_find_device(pci->bus, 0, 0); + assert(pdev); + + pci_host_config_write_common(pdev, off, PHB_RC_CONFIG_SIZE, + bswap32(val), 4); +} + +static uint64_t pnv_phb4_rc_config_read(PnvPHB4 *phb, unsigned off, + unsigned size) +{ + PCIHostState *pci = PCI_HOST_BRIDGE(phb); + PCIDevice *pdev; + uint64_t val; + + if (size != 4) { + phb_error(phb, "rc_config_read invalid size %d\n", size); + return ~0ull; + } + + pdev = pci_find_device(pci->bus, 0, 0); + assert(pdev); + + val = pci_host_config_read_common(pdev, off, PHB_RC_CONFIG_SIZE, 4); + return bswap32(val); +} + +static void pnv_phb4_check_mbt(PnvPHB4 *phb, uint32_t index) +{ + uint64_t base, start, size, mbe0, mbe1; + MemoryRegion *parent; + char name[64]; + + /* Unmap first */ + if (memory_region_is_mapped(&phb->mr_mmio[index])) { + /* Should we destroy it in RCU friendly way... ? */ + memory_region_del_subregion(phb->mr_mmio[index].container, + &phb->mr_mmio[index]); + } + + /* Get table entry */ + mbe0 = phb->ioda_MBT[(index << 1)]; + mbe1 = phb->ioda_MBT[(index << 1) + 1]; + + if (!(mbe0 & IODA3_MBT0_ENABLE)) { + return; + } + + /* Grab geometry from registers */ + base = GETFIELD(IODA3_MBT0_BASE_ADDR, mbe0) << 12; + size = GETFIELD(IODA3_MBT1_MASK, mbe1) << 12; + size |= 0xff00000000000000ull; + size = ~size + 1; + + /* Calculate PCI side start address based on M32/M64 window type */ + if (mbe0 & IODA3_MBT0_TYPE_M32) { + start = phb->regs[PHB_M32_START_ADDR >> 3]; + if ((start + size) > 0x100000000ull) { + phb_error(phb, "M32 set beyond 4GB boundary !"); + size = 0x100000000 - start; + } + } else { + start = base | (phb->regs[PHB_M64_UPPER_BITS >> 3]); + } + + /* TODO: Figure out how to implemet/decode AOMASK */ + + /* Check if it matches an enabled MMIO region in the PEC stack */ + if (memory_region_is_mapped(&phb->stack->mmbar0) && + base >= phb->stack->mmio0_base && + (base + size) <= (phb->stack->mmio0_base + phb->stack->mmio0_size)) { + parent = &phb->stack->mmbar0; + base -= phb->stack->mmio0_base; + } else if (memory_region_is_mapped(&phb->stack->mmbar1) && + base >= phb->stack->mmio1_base && + (base + size) <= (phb->stack->mmio1_base + phb->stack->mmio1_size)) { + parent = &phb->stack->mmbar1; + base -= phb->stack->mmio1_base; + } else { + phb_error(phb, "PHB MBAR %d out of parent bounds", index); + return; + } + + /* Create alias (better name ?) */ + snprintf(name, sizeof(name), "phb4-mbar%d", index); + memory_region_init_alias(&phb->mr_mmio[index], OBJECT(phb), name, + &phb->pci_mmio, start, size); + memory_region_add_subregion(parent, base, &phb->mr_mmio[index]); +} + +static void pnv_phb4_check_all_mbt(PnvPHB4 *phb) +{ + uint64_t i; + uint32_t num_windows = phb->big_phb ? PNV_PHB4_MAX_MMIO_WINDOWS : + PNV_PHB4_MIN_MMIO_WINDOWS; + + for (i = 0; i < num_windows; i++) { + pnv_phb4_check_mbt(phb, i); + } +} + +static uint64_t *pnv_phb4_ioda_access(PnvPHB4 *phb, + unsigned *out_table, unsigned *out_idx) +{ + uint64_t adreg = phb->regs[PHB_IODA_ADDR >> 3]; + unsigned int index = GETFIELD(PHB_IODA_AD_TADR, adreg); + unsigned int table = GETFIELD(PHB_IODA_AD_TSEL, adreg); + unsigned int mask; + uint64_t *tptr = NULL; + + switch (table) { + case IODA3_TBL_LIST: + tptr = phb->ioda_LIST; + mask = 7; + break; + case IODA3_TBL_MIST: + tptr = phb->ioda_MIST; + mask = phb->big_phb ? PNV_PHB4_MAX_MIST : (PNV_PHB4_MAX_MIST >> 1); + mask -= 1; + break; + case IODA3_TBL_RCAM: + mask = phb->big_phb ? 127 : 63; + break; + case IODA3_TBL_MRT: + mask = phb->big_phb ? 15 : 7; + break; + case IODA3_TBL_PESTA: + case IODA3_TBL_PESTB: + mask = phb->big_phb ? PNV_PHB4_MAX_PEs : (PNV_PHB4_MAX_PEs >> 1); + mask -= 1; + break; + case IODA3_TBL_TVT: + tptr = phb->ioda_TVT; + mask = phb->big_phb ? PNV_PHB4_MAX_TVEs : (PNV_PHB4_MAX_TVEs >> 1); + mask -= 1; + break; + case IODA3_TBL_TCR: + case IODA3_TBL_TDR: + mask = phb->big_phb ? 1023 : 511; + break; + case IODA3_TBL_MBT: + tptr = phb->ioda_MBT; + mask = phb->big_phb ? PNV_PHB4_MAX_MBEs : (PNV_PHB4_MAX_MBEs >> 1); + mask -= 1; + break; + case IODA3_TBL_MDT: + tptr = phb->ioda_MDT; + mask = phb->big_phb ? PNV_PHB4_MAX_PEs : (PNV_PHB4_MAX_PEs >> 1); + mask -= 1; + break; + case IODA3_TBL_PEEV: + tptr = phb->ioda_PEEV; + mask = phb->big_phb ? PNV_PHB4_MAX_PEEVs : (PNV_PHB4_MAX_PEEVs >> 1); + mask -= 1; + break; + default: + phb_error(phb, "invalid IODA table %d", table); + return NULL; + } + index &= mask; + if (out_idx) { + *out_idx = index; + } + if (out_table) { + *out_table = table; + } + if (tptr) { + tptr += index; + } + if (adreg & PHB_IODA_AD_AUTOINC) { + index = (index + 1) & mask; + adreg = SETFIELD(PHB_IODA_AD_TADR, adreg, index); + } + + phb->regs[PHB_IODA_ADDR >> 3] = adreg; + return tptr; +} + +static uint64_t pnv_phb4_ioda_read(PnvPHB4 *phb) +{ + unsigned table, idx; + uint64_t *tptr; + + tptr = pnv_phb4_ioda_access(phb, &table, &idx); + if (!tptr) { + /* Special PESTA case */ + if (table == IODA3_TBL_PESTA) { + return ((uint64_t)(phb->ioda_PEST_AB[idx] & 1)) << 63; + } else if (table == IODA3_TBL_PESTB) { + return ((uint64_t)(phb->ioda_PEST_AB[idx] & 2)) << 62; + } + /* Return 0 on unsupported tables, not ff's */ + return 0; + } + return *tptr; +} + +static void pnv_phb4_ioda_write(PnvPHB4 *phb, uint64_t val) +{ + unsigned table, idx; + uint64_t *tptr; + + tptr = pnv_phb4_ioda_access(phb, &table, &idx); + if (!tptr) { + /* Special PESTA case */ + if (table == IODA3_TBL_PESTA) { + phb->ioda_PEST_AB[idx] &= ~1; + phb->ioda_PEST_AB[idx] |= (val >> 63) & 1; + } else if (table == IODA3_TBL_PESTB) { + phb->ioda_PEST_AB[idx] &= ~2; + phb->ioda_PEST_AB[idx] |= (val >> 62) & 2; + } + return; + } + + /* Handle side effects */ + switch (table) { + case IODA3_TBL_LIST: + break; + case IODA3_TBL_MIST: { + /* Special mask for MIST partial write */ + uint64_t adreg = phb->regs[PHB_IODA_ADDR >> 3]; + uint32_t mmask = GETFIELD(PHB_IODA_AD_MIST_PWV, adreg); + uint64_t v = *tptr; + if (mmask == 0) { + mmask = 0xf; + } + if (mmask & 8) { + v &= 0x0000ffffffffffffull; + v |= 0xcfff000000000000ull & val; + } + if (mmask & 4) { + v &= 0xffff0000ffffffffull; + v |= 0x0000cfff00000000ull & val; + } + if (mmask & 2) { + v &= 0xffffffff0000ffffull; + v |= 0x00000000cfff0000ull & val; + } + if (mmask & 1) { + v &= 0xffffffffffff0000ull; + v |= 0x000000000000cfffull & val; + } + *tptr = val; + break; + } + case IODA3_TBL_MBT: + *tptr = val; + + /* Copy accross the valid bit to the other half */ + phb->ioda_MBT[idx ^ 1] &= 0x7fffffffffffffffull; + phb->ioda_MBT[idx ^ 1] |= 0x8000000000000000ull & val; + + /* Update mappings */ + pnv_phb4_check_mbt(phb, idx >> 1); + break; + default: + *tptr = val; + } +} + +static void pnv_phb4_rtc_invalidate(PnvPHB4 *phb, uint64_t val) +{ + PnvPhb4DMASpace *ds; + + /* Always invalidate all for now ... */ + QLIST_FOREACH(ds, &phb->dma_spaces, list) { + ds->pe_num = PHB_INVALID_PE; + } +} + +static void pnv_phb4_update_msi_regions(PnvPhb4DMASpace *ds) +{ + uint64_t cfg = ds->phb->regs[PHB_PHB4_CONFIG >> 3]; + + if (cfg & PHB_PHB4C_32BIT_MSI_EN) { + if (!memory_region_is_mapped(MEMORY_REGION(&ds->msi32_mr))) { + memory_region_add_subregion(MEMORY_REGION(&ds->dma_mr), + 0xffff0000, &ds->msi32_mr); + } + } else { + if (memory_region_is_mapped(MEMORY_REGION(&ds->msi32_mr))) { + memory_region_del_subregion(MEMORY_REGION(&ds->dma_mr), + &ds->msi32_mr); + } + } + + if (cfg & PHB_PHB4C_64BIT_MSI_EN) { + if (!memory_region_is_mapped(MEMORY_REGION(&ds->msi64_mr))) { + memory_region_add_subregion(MEMORY_REGION(&ds->dma_mr), + (1ull << 60), &ds->msi64_mr); + } + } else { + if (memory_region_is_mapped(MEMORY_REGION(&ds->msi64_mr))) { + memory_region_del_subregion(MEMORY_REGION(&ds->dma_mr), + &ds->msi64_mr); + } + } +} + +static void pnv_phb4_update_all_msi_regions(PnvPHB4 *phb) +{ + PnvPhb4DMASpace *ds; + + QLIST_FOREACH(ds, &phb->dma_spaces, list) { + pnv_phb4_update_msi_regions(ds); + } +} + +static void pnv_phb4_update_xsrc(PnvPHB4 *phb) +{ + int shift, flags, i, lsi_base; + XiveSource *xsrc = &phb->xsrc; + + /* The XIVE source characteristics can be set at run time */ + if (phb->regs[PHB_CTRLR >> 3] & PHB_CTRLR_IRQ_PGSZ_64K) { + shift = XIVE_ESB_64K; + } else { + shift = XIVE_ESB_4K; + } + if (phb->regs[PHB_CTRLR >> 3] & PHB_CTRLR_IRQ_STORE_EOI) { + flags = XIVE_SRC_STORE_EOI; + } else { + flags = 0; + } + + phb->xsrc.esb_shift = shift; + phb->xsrc.esb_flags = flags; + + lsi_base = GETFIELD(PHB_LSI_SRC_ID, phb->regs[PHB_LSI_SOURCE_ID >> 3]); + lsi_base <<= 3; + + /* TODO: handle reset values of PHB_LSI_SRC_ID */ + if (!lsi_base) { + return; + } + + /* TODO: need a xive_source_irq_reset_lsi() */ + bitmap_zero(xsrc->lsi_map, xsrc->nr_irqs); + + for (i = 0; i < xsrc->nr_irqs; i++) { + bool msi = (i < lsi_base || i >= (lsi_base + 8)); + if (!msi) { + xive_source_irq_set_lsi(xsrc, i); + } + } +} + +static void pnv_phb4_reg_write(void *opaque, hwaddr off, uint64_t val, + unsigned size) +{ + PnvPHB4 *phb = PNV_PHB4(opaque); + bool changed; + + /* Special case outbound configuration data */ + if ((off & 0xfffc) == PHB_CONFIG_DATA) { + pnv_phb4_config_write(phb, off & 0x3, size, val); + return; + } + + /* Special case RC configuration space */ + if ((off & 0xf800) == PHB_RC_CONFIG_BASE) { + pnv_phb4_rc_config_write(phb, off & 0x7ff, size, val); + return; + } + + /* Other registers are 64-bit only */ + if (size != 8 || off & 0x7) { + phb_error(phb, "Invalid register access, offset: 0x%"PRIx64" size: %d", + off, size); + return; + } + + /* Handle masking */ + switch (off) { + case PHB_LSI_SOURCE_ID: + val &= PHB_LSI_SRC_ID; + break; + case PHB_M64_UPPER_BITS: + val &= 0xff00000000000000ull; + break; + /* TCE Kill */ + case PHB_TCE_KILL: + /* Clear top 3 bits which HW does to indicate successful queuing */ + val &= ~(PHB_TCE_KILL_ALL | PHB_TCE_KILL_PE | PHB_TCE_KILL_ONE); + break; + case PHB_Q_DMA_R: + /* + * This is enough logic to make SW happy but we aren't + * actually quiescing the DMAs + */ + if (val & PHB_Q_DMA_R_AUTORESET) { + val = 0; + } else { + val &= PHB_Q_DMA_R_QUIESCE_DMA; + } + break; + /* LEM stuff */ + case PHB_LEM_FIR_AND_MASK: + phb->regs[PHB_LEM_FIR_ACCUM >> 3] &= val; + return; + case PHB_LEM_FIR_OR_MASK: + phb->regs[PHB_LEM_FIR_ACCUM >> 3] |= val; + return; + case PHB_LEM_ERROR_AND_MASK: + phb->regs[PHB_LEM_ERROR_MASK >> 3] &= val; + return; + case PHB_LEM_ERROR_OR_MASK: + phb->regs[PHB_LEM_ERROR_MASK >> 3] |= val; + return; + case PHB_LEM_WOF: + val = 0; + break; + /* TODO: More regs ..., maybe create a table with masks... */ + + /* Read only registers */ + case PHB_CPU_LOADSTORE_STATUS: + case PHB_ETU_ERR_SUMMARY: + case PHB_PHB4_GEN_CAP: + case PHB_PHB4_TCE_CAP: + case PHB_PHB4_IRQ_CAP: + case PHB_PHB4_EEH_CAP: + return; + } + + /* Record whether it changed */ + changed = phb->regs[off >> 3] != val; + + /* Store in register cache first */ + phb->regs[off >> 3] = val; + + /* Handle side effects */ + switch (off) { + case PHB_PHB4_CONFIG: + if (changed) { + pnv_phb4_update_all_msi_regions(phb); + } + break; + case PHB_M32_START_ADDR: + case PHB_M64_UPPER_BITS: + if (changed) { + pnv_phb4_check_all_mbt(phb); + } + break; + + /* IODA table accesses */ + case PHB_IODA_DATA0: + pnv_phb4_ioda_write(phb, val); + break; + + /* RTC invalidation */ + case PHB_RTC_INVALIDATE: + pnv_phb4_rtc_invalidate(phb, val); + break; + + /* PHB Control (Affects XIVE source) */ + case PHB_CTRLR: + case PHB_LSI_SOURCE_ID: + pnv_phb4_update_xsrc(phb); + break; + + /* Silent simple writes */ + case PHB_ASN_CMPM: + case PHB_CONFIG_ADDRESS: + case PHB_IODA_ADDR: + case PHB_TCE_KILL: + case PHB_TCE_SPEC_CTL: + case PHB_PEST_BAR: + case PHB_PELTV_BAR: + case PHB_RTT_BAR: + case PHB_LEM_FIR_ACCUM: + case PHB_LEM_ERROR_MASK: + case PHB_LEM_ACTION0: + case PHB_LEM_ACTION1: + case PHB_TCE_TAG_ENABLE: + case PHB_INT_NOTIFY_ADDR: + case PHB_INT_NOTIFY_INDEX: + case PHB_DMARD_SYNC: + break; + + /* Noise on anything else */ + default: + qemu_log_mask(LOG_UNIMP, "phb4: reg_write 0x%"PRIx64"=%"PRIx64"\n", + off, val); + } +} + +static uint64_t pnv_phb4_reg_read(void *opaque, hwaddr off, unsigned size) +{ + PnvPHB4 *phb = PNV_PHB4(opaque); + uint64_t val; + + if ((off & 0xfffc) == PHB_CONFIG_DATA) { + return pnv_phb4_config_read(phb, off & 0x3, size); + } + + /* Special case RC configuration space */ + if ((off & 0xf800) == PHB_RC_CONFIG_BASE) { + return pnv_phb4_rc_config_read(phb, off & 0x7ff, size); + } + + /* Other registers are 64-bit only */ + if (size != 8 || off & 0x7) { + phb_error(phb, "Invalid register access, offset: 0x%"PRIx64" size: %d", + off, size); + return ~0ull; + } + + /* Default read from cache */ + val = phb->regs[off >> 3]; + + switch (off) { + case PHB_VERSION: + return phb->version; + + /* Read-only */ + case PHB_PHB4_GEN_CAP: + return 0xe4b8000000000000ull; + case PHB_PHB4_TCE_CAP: + return phb->big_phb ? 0x4008440000000400ull : 0x2008440000000200ull; + case PHB_PHB4_IRQ_CAP: + return phb->big_phb ? 0x0800000000001000ull : 0x0800000000000800ull; + case PHB_PHB4_EEH_CAP: + return phb->big_phb ? 0x2000000000000000ull : 0x1000000000000000ull; + + /* IODA table accesses */ + case PHB_IODA_DATA0: + return pnv_phb4_ioda_read(phb); + + /* Link training always appears trained */ + case PHB_PCIE_DLP_TRAIN_CTL: + /* TODO: Do something sensible with speed ? */ + return PHB_PCIE_DLP_INBAND_PRESENCE | PHB_PCIE_DLP_TL_LINKACT; + + /* DMA read sync: make it look like it's complete */ + case PHB_DMARD_SYNC: + return PHB_DMARD_SYNC_COMPLETE; + + /* Silent simple reads */ + case PHB_LSI_SOURCE_ID: + case PHB_CPU_LOADSTORE_STATUS: + case PHB_ASN_CMPM: + case PHB_PHB4_CONFIG: + case PHB_M32_START_ADDR: + case PHB_CONFIG_ADDRESS: + case PHB_IODA_ADDR: + case PHB_RTC_INVALIDATE: + case PHB_TCE_KILL: + case PHB_TCE_SPEC_CTL: + case PHB_PEST_BAR: + case PHB_PELTV_BAR: + case PHB_RTT_BAR: + case PHB_M64_UPPER_BITS: + case PHB_CTRLR: + case PHB_LEM_FIR_ACCUM: + case PHB_LEM_ERROR_MASK: + case PHB_LEM_ACTION0: + case PHB_LEM_ACTION1: + case PHB_TCE_TAG_ENABLE: + case PHB_INT_NOTIFY_ADDR: + case PHB_INT_NOTIFY_INDEX: + case PHB_Q_DMA_R: + case PHB_ETU_ERR_SUMMARY: + break; + + /* Noise on anything else */ + default: + qemu_log_mask(LOG_UNIMP, "phb4: reg_read 0x%"PRIx64"=%"PRIx64"\n", + off, val); + } + return val; +} + +static const MemoryRegionOps pnv_phb4_reg_ops = { + .read = pnv_phb4_reg_read, + .write = pnv_phb4_reg_write, + .valid.min_access_size = 1, + .valid.max_access_size = 8, + .impl.min_access_size = 1, + .impl.max_access_size = 8, + .endianness = DEVICE_BIG_ENDIAN, +}; + +static uint64_t pnv_phb4_xscom_read(void *opaque, hwaddr addr, unsigned size) +{ + PnvPHB4 *phb = PNV_PHB4(opaque); + uint32_t reg = addr >> 3; + uint64_t val; + hwaddr offset; + + switch (reg) { + case PHB_SCOM_HV_IND_ADDR: + return phb->scom_hv_ind_addr_reg; + + case PHB_SCOM_HV_IND_DATA: + if (!(phb->scom_hv_ind_addr_reg & PHB_SCOM_HV_IND_ADDR_VALID)) { + phb_error(phb, "Invalid indirect address"); + return ~0ull; + } + size = (phb->scom_hv_ind_addr_reg & PHB_SCOM_HV_IND_ADDR_4B) ? 4 : 8; + offset = GETFIELD(PHB_SCOM_HV_IND_ADDR_ADDR, phb->scom_hv_ind_addr_reg); + val = pnv_phb4_reg_read(phb, offset, size); + if (phb->scom_hv_ind_addr_reg & PHB_SCOM_HV_IND_ADDR_AUTOINC) { + offset += size; + offset &= 0x3fff; + phb->scom_hv_ind_addr_reg = SETFIELD(PHB_SCOM_HV_IND_ADDR_ADDR, + phb->scom_hv_ind_addr_reg, + offset); + } + return val; + case PHB_SCOM_ETU_LEM_FIR: + case PHB_SCOM_ETU_LEM_FIR_AND: + case PHB_SCOM_ETU_LEM_FIR_OR: + case PHB_SCOM_ETU_LEM_FIR_MSK: + case PHB_SCOM_ETU_LEM_ERR_MSK_AND: + case PHB_SCOM_ETU_LEM_ERR_MSK_OR: + case PHB_SCOM_ETU_LEM_ACT0: + case PHB_SCOM_ETU_LEM_ACT1: + case PHB_SCOM_ETU_LEM_WOF: + offset = ((reg - PHB_SCOM_ETU_LEM_FIR) << 3) + PHB_LEM_FIR_ACCUM; + return pnv_phb4_reg_read(phb, offset, size); + case PHB_SCOM_ETU_PMON_CONFIG: + case PHB_SCOM_ETU_PMON_CTR0: + case PHB_SCOM_ETU_PMON_CTR1: + case PHB_SCOM_ETU_PMON_CTR2: + case PHB_SCOM_ETU_PMON_CTR3: + offset = ((reg - PHB_SCOM_ETU_PMON_CONFIG) << 3) + PHB_PERFMON_CONFIG; + return pnv_phb4_reg_read(phb, offset, size); + + default: + qemu_log_mask(LOG_UNIMP, "phb4: xscom_read 0x%"HWADDR_PRIx"\n", addr); + return ~0ull; + } +} + +static void pnv_phb4_xscom_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + PnvPHB4 *phb = PNV_PHB4(opaque); + uint32_t reg = addr >> 3; + hwaddr offset; + + switch (reg) { + case PHB_SCOM_HV_IND_ADDR: + phb->scom_hv_ind_addr_reg = val & 0xe000000000001fff; + break; + case PHB_SCOM_HV_IND_DATA: + if (!(phb->scom_hv_ind_addr_reg & PHB_SCOM_HV_IND_ADDR_VALID)) { + phb_error(phb, "Invalid indirect address"); + break; + } + size = (phb->scom_hv_ind_addr_reg & PHB_SCOM_HV_IND_ADDR_4B) ? 4 : 8; + offset = GETFIELD(PHB_SCOM_HV_IND_ADDR_ADDR, phb->scom_hv_ind_addr_reg); + pnv_phb4_reg_write(phb, offset, val, size); + if (phb->scom_hv_ind_addr_reg & PHB_SCOM_HV_IND_ADDR_AUTOINC) { + offset += size; + offset &= 0x3fff; + phb->scom_hv_ind_addr_reg = SETFIELD(PHB_SCOM_HV_IND_ADDR_ADDR, + phb->scom_hv_ind_addr_reg, + offset); + } + break; + case PHB_SCOM_ETU_LEM_FIR: + case PHB_SCOM_ETU_LEM_FIR_AND: + case PHB_SCOM_ETU_LEM_FIR_OR: + case PHB_SCOM_ETU_LEM_FIR_MSK: + case PHB_SCOM_ETU_LEM_ERR_MSK_AND: + case PHB_SCOM_ETU_LEM_ERR_MSK_OR: + case PHB_SCOM_ETU_LEM_ACT0: + case PHB_SCOM_ETU_LEM_ACT1: + case PHB_SCOM_ETU_LEM_WOF: + offset = ((reg - PHB_SCOM_ETU_LEM_FIR) << 3) + PHB_LEM_FIR_ACCUM; + pnv_phb4_reg_write(phb, offset, val, size); + break; + case PHB_SCOM_ETU_PMON_CONFIG: + case PHB_SCOM_ETU_PMON_CTR0: + case PHB_SCOM_ETU_PMON_CTR1: + case PHB_SCOM_ETU_PMON_CTR2: + case PHB_SCOM_ETU_PMON_CTR3: + offset = ((reg - PHB_SCOM_ETU_PMON_CONFIG) << 3) + PHB_PERFMON_CONFIG; + pnv_phb4_reg_write(phb, offset, val, size); + break; + default: + qemu_log_mask(LOG_UNIMP, "phb4: xscom_write 0x%"HWADDR_PRIx + "=%"PRIx64"\n", addr, val); + } +} + +const MemoryRegionOps pnv_phb4_xscom_ops = { + .read = pnv_phb4_xscom_read, + .write = pnv_phb4_xscom_write, + .valid.min_access_size = 8, + .valid.max_access_size = 8, + .impl.min_access_size = 8, + .impl.max_access_size = 8, + .endianness = DEVICE_BIG_ENDIAN, +}; + +static int pnv_phb4_map_irq(PCIDevice *pci_dev, int irq_num) +{ + /* Check that out properly ... */ + return irq_num & 3; +} + +static void pnv_phb4_set_irq(void *opaque, int irq_num, int level) +{ + PnvPHB4 *phb = PNV_PHB4(opaque); + uint32_t lsi_base; + + /* LSI only ... */ + if (irq_num > 3) { + phb_error(phb, "IRQ %x is not an LSI", irq_num); + } + lsi_base = GETFIELD(PHB_LSI_SRC_ID, phb->regs[PHB_LSI_SOURCE_ID >> 3]); + lsi_base <<= 3; + qemu_set_irq(phb->qirqs[lsi_base + irq_num], level); +} + +static bool pnv_phb4_resolve_pe(PnvPhb4DMASpace *ds) +{ + uint64_t rtt, addr; + uint16_t rte; + int bus_num; + int num_PEs; + + /* Already resolved ? */ + if (ds->pe_num != PHB_INVALID_PE) { + return true; + } + + /* We need to lookup the RTT */ + rtt = ds->phb->regs[PHB_RTT_BAR >> 3]; + if (!(rtt & PHB_RTT_BAR_ENABLE)) { + phb_error(ds->phb, "DMA with RTT BAR disabled !"); + /* Set error bits ? fence ? ... */ + return false; + } + + /* Read RTE */ + bus_num = pci_bus_num(ds->bus); + addr = rtt & PHB_RTT_BASE_ADDRESS_MASK; + addr += 2 * ((bus_num << 8) | ds->devfn); + if (dma_memory_read(&address_space_memory, addr, &rte, sizeof(rte))) { + phb_error(ds->phb, "Failed to read RTT entry at 0x%"PRIx64, addr); + /* Set error bits ? fence ? ... */ + return false; + } + rte = be16_to_cpu(rte); + + /* Fail upon reading of invalid PE# */ + num_PEs = ds->phb->big_phb ? PNV_PHB4_MAX_PEs : (PNV_PHB4_MAX_PEs >> 1); + if (rte >= num_PEs) { + phb_error(ds->phb, "RTE for RID 0x%x invalid (%04x", ds->devfn, rte); + rte &= num_PEs - 1; + } + ds->pe_num = rte; + return true; +} + +static void pnv_phb4_translate_tve(PnvPhb4DMASpace *ds, hwaddr addr, + bool is_write, uint64_t tve, + IOMMUTLBEntry *tlb) +{ + uint64_t tta = GETFIELD(IODA3_TVT_TABLE_ADDR, tve); + int32_t lev = GETFIELD(IODA3_TVT_NUM_LEVELS, tve); + uint32_t tts = GETFIELD(IODA3_TVT_TCE_TABLE_SIZE, tve); + uint32_t tps = GETFIELD(IODA3_TVT_IO_PSIZE, tve); + + /* Invalid levels */ + if (lev > 4) { + phb_error(ds->phb, "Invalid #levels in TVE %d", lev); + return; + } + + /* Invalid entry */ + if (tts == 0) { + phb_error(ds->phb, "Access to invalid TVE"); + return; + } + + /* IO Page Size of 0 means untranslated, else use TCEs */ + if (tps == 0) { + /* TODO: Handle boundaries */ + + /* Use 4k pages like q35 ... for now */ + tlb->iova = addr & 0xfffffffffffff000ull; + tlb->translated_addr = addr & 0x0003fffffffff000ull; + tlb->addr_mask = 0xfffull; + tlb->perm = IOMMU_RW; + } else { + uint32_t tce_shift, tbl_shift, sh; + uint64_t base, taddr, tce, tce_mask; + + /* Address bits per bottom level TCE entry */ + tce_shift = tps + 11; + + /* Address bits per table level */ + tbl_shift = tts + 8; + + /* Top level table base address */ + base = tta << 12; + + /* Total shift to first level */ + sh = tbl_shift * lev + tce_shift; + + /* TODO: Limit to support IO page sizes */ + + /* TODO: Multi-level untested */ + while ((lev--) >= 0) { + /* Grab the TCE address */ + taddr = base | (((addr >> sh) & ((1ul << tbl_shift) - 1)) << 3); + if (dma_memory_read(&address_space_memory, taddr, &tce, + sizeof(tce))) { + phb_error(ds->phb, "Failed to read TCE at 0x%"PRIx64, taddr); + return; + } + tce = be64_to_cpu(tce); + + /* Check permission for indirect TCE */ + if ((lev >= 0) && !(tce & 3)) { + phb_error(ds->phb, "Invalid indirect TCE at 0x%"PRIx64, taddr); + phb_error(ds->phb, " xlate %"PRIx64":%c TVE=%"PRIx64, addr, + is_write ? 'W' : 'R', tve); + phb_error(ds->phb, " tta=%"PRIx64" lev=%d tts=%d tps=%d", + tta, lev, tts, tps); + return; + } + sh -= tbl_shift; + base = tce & ~0xfffull; + } + + /* We exit the loop with TCE being the final TCE */ + tce_mask = ~((1ull << tce_shift) - 1); + tlb->iova = addr & tce_mask; + tlb->translated_addr = tce & tce_mask; + tlb->addr_mask = ~tce_mask; + tlb->perm = tce & 3; + if ((is_write & !(tce & 2)) || ((!is_write) && !(tce & 1))) { + phb_error(ds->phb, "TCE access fault at 0x%"PRIx64, taddr); + phb_error(ds->phb, " xlate %"PRIx64":%c TVE=%"PRIx64, addr, + is_write ? 'W' : 'R', tve); + phb_error(ds->phb, " tta=%"PRIx64" lev=%d tts=%d tps=%d", + tta, lev, tts, tps); + } + } +} + +static IOMMUTLBEntry pnv_phb4_translate_iommu(IOMMUMemoryRegion *iommu, + hwaddr addr, + IOMMUAccessFlags flag, + int iommu_idx) +{ + PnvPhb4DMASpace *ds = container_of(iommu, PnvPhb4DMASpace, dma_mr); + int tve_sel; + uint64_t tve, cfg; + IOMMUTLBEntry ret = { + .target_as = &address_space_memory, + .iova = addr, + .translated_addr = 0, + .addr_mask = ~(hwaddr)0, + .perm = IOMMU_NONE, + }; + + /* Resolve PE# */ + if (!pnv_phb4_resolve_pe(ds)) { + phb_error(ds->phb, "Failed to resolve PE# for bus @%p (%d) devfn 0x%x", + ds->bus, pci_bus_num(ds->bus), ds->devfn); + return ret; + } + + /* Check top bits */ + switch (addr >> 60) { + case 00: + /* DMA or 32-bit MSI ? */ + cfg = ds->phb->regs[PHB_PHB4_CONFIG >> 3]; + if ((cfg & PHB_PHB4C_32BIT_MSI_EN) && + ((addr & 0xffffffffffff0000ull) == 0xffff0000ull)) { + phb_error(ds->phb, "xlate on 32-bit MSI region"); + return ret; + } + /* Choose TVE XXX Use PHB4 Control Register */ + tve_sel = (addr >> 59) & 1; + tve = ds->phb->ioda_TVT[ds->pe_num * 2 + tve_sel]; + pnv_phb4_translate_tve(ds, addr, flag & IOMMU_WO, tve, &ret); + break; + case 01: + phb_error(ds->phb, "xlate on 64-bit MSI region"); + break; + default: + phb_error(ds->phb, "xlate on unsupported address 0x%"PRIx64, addr); + } + return ret; +} + +#define TYPE_PNV_PHB4_IOMMU_MEMORY_REGION "pnv-phb4-iommu-memory-region" +#define PNV_PHB4_IOMMU_MEMORY_REGION(obj) \ + OBJECT_CHECK(IOMMUMemoryRegion, (obj), TYPE_PNV_PHB4_IOMMU_MEMORY_REGION) + +static void pnv_phb4_iommu_memory_region_class_init(ObjectClass *klass, + void *data) +{ + IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass); + + imrc->translate = pnv_phb4_translate_iommu; +} + +static const TypeInfo pnv_phb4_iommu_memory_region_info = { + .parent = TYPE_IOMMU_MEMORY_REGION, + .name = TYPE_PNV_PHB4_IOMMU_MEMORY_REGION, + .class_init = pnv_phb4_iommu_memory_region_class_init, +}; + +/* + * MSI/MSIX memory region implementation. + * The handler handles both MSI and MSIX. + */ +static void pnv_phb4_msi_write(void *opaque, hwaddr addr, + uint64_t data, unsigned size) +{ + PnvPhb4DMASpace *ds = opaque; + PnvPHB4 *phb = ds->phb; + + uint32_t src = ((addr >> 4) & 0xffff) | (data & 0x1f); + + /* Resolve PE# */ + if (!pnv_phb4_resolve_pe(ds)) { + phb_error(phb, "Failed to resolve PE# for bus @%p (%d) devfn 0x%x", + ds->bus, pci_bus_num(ds->bus), ds->devfn); + return; + } + + /* TODO: Check it doesn't collide with LSIs */ + if (src >= phb->xsrc.nr_irqs) { + phb_error(phb, "MSI %d out of bounds", src); + return; + } + + /* TODO: check PE/MSI assignement */ + + qemu_irq_pulse(phb->qirqs[src]); +} + +/* There is no .read as the read result is undefined by PCI spec */ +static uint64_t pnv_phb4_msi_read(void *opaque, hwaddr addr, unsigned size) +{ + PnvPhb4DMASpace *ds = opaque; + + phb_error(ds->phb, "Invalid MSI read @ 0x%" HWADDR_PRIx, addr); + return -1; +} + +static const MemoryRegionOps pnv_phb4_msi_ops = { + .read = pnv_phb4_msi_read, + .write = pnv_phb4_msi_write, + .endianness = DEVICE_LITTLE_ENDIAN +}; + +static PnvPhb4DMASpace *pnv_phb4_dma_find(PnvPHB4 *phb, PCIBus *bus, int devfn) +{ + PnvPhb4DMASpace *ds; + + QLIST_FOREACH(ds, &phb->dma_spaces, list) { + if (ds->bus == bus && ds->devfn == devfn) { + break; + } + } + return ds; +} + +static AddressSpace *pnv_phb4_dma_iommu(PCIBus *bus, void *opaque, int devfn) +{ + PnvPHB4 *phb = opaque; + PnvPhb4DMASpace *ds; + char name[32]; + + ds = pnv_phb4_dma_find(phb, bus, devfn); + + if (ds == NULL) { + ds = g_malloc0(sizeof(PnvPhb4DMASpace)); + ds->bus = bus; + ds->devfn = devfn; + ds->pe_num = PHB_INVALID_PE; + ds->phb = phb; + snprintf(name, sizeof(name), "phb4-%d.%d-iommu", phb->chip_id, + phb->phb_id); + memory_region_init_iommu(&ds->dma_mr, sizeof(ds->dma_mr), + TYPE_PNV_PHB4_IOMMU_MEMORY_REGION, + OBJECT(phb), name, UINT64_MAX); + address_space_init(&ds->dma_as, MEMORY_REGION(&ds->dma_mr), + name); + memory_region_init_io(&ds->msi32_mr, OBJECT(phb), &pnv_phb4_msi_ops, + ds, "msi32", 0x10000); + memory_region_init_io(&ds->msi64_mr, OBJECT(phb), &pnv_phb4_msi_ops, + ds, "msi64", 0x100000); + pnv_phb4_update_msi_regions(ds); + + QLIST_INSERT_HEAD(&phb->dma_spaces, ds, list); + } + return &ds->dma_as; +} + +static void pnv_phb4_instance_init(Object *obj) +{ + PnvPHB4 *phb = PNV_PHB4(obj); + + QLIST_INIT(&phb->dma_spaces); + + /* XIVE interrupt source object */ + object_initialize_child(obj, "source", &phb->xsrc, sizeof(XiveSource), + TYPE_XIVE_SOURCE, &error_abort, NULL); + + /* Root Port */ + object_initialize_child(obj, "root", &phb->root, sizeof(phb->root), + TYPE_PNV_PHB4_ROOT_PORT, &error_abort, NULL); + + qdev_prop_set_int32(DEVICE(&phb->root), "addr", PCI_DEVFN(0, 0)); + qdev_prop_set_bit(DEVICE(&phb->root), "multifunction", false); +} + +static void pnv_phb4_realize(DeviceState *dev, Error **errp) +{ + PnvPHB4 *phb = PNV_PHB4(dev); + PCIHostState *pci = PCI_HOST_BRIDGE(dev); + XiveSource *xsrc = &phb->xsrc; + Error *local_err = NULL; + int nr_irqs; + char name[32]; + + assert(phb->stack); + + /* Set the "big_phb" flag */ + phb->big_phb = phb->phb_id == 0 || phb->phb_id == 3; + + /* Controller Registers */ + snprintf(name, sizeof(name), "phb4-%d.%d-regs", phb->chip_id, + phb->phb_id); + memory_region_init_io(&phb->mr_regs, OBJECT(phb), &pnv_phb4_reg_ops, phb, + name, 0x2000); + + /* + * PHB4 doesn't support IO space. However, qemu gets very upset if + * we don't have an IO region to anchor IO BARs onto so we just + * initialize one which we never hook up to anything + */ + + snprintf(name, sizeof(name), "phb4-%d.%d-pci-io", phb->chip_id, + phb->phb_id); + memory_region_init(&phb->pci_io, OBJECT(phb), name, 0x10000); + + snprintf(name, sizeof(name), "phb4-%d.%d-pci-mmio", phb->chip_id, + phb->phb_id); + memory_region_init(&phb->pci_mmio, OBJECT(phb), name, + PCI_MMIO_TOTAL_SIZE); + + pci->bus = pci_register_root_bus(dev, "root-bus", + pnv_phb4_set_irq, pnv_phb4_map_irq, phb, + &phb->pci_mmio, &phb->pci_io, + 0, 4, TYPE_PNV_PHB4_ROOT_BUS); + pci_setup_iommu(pci->bus, pnv_phb4_dma_iommu, phb); + + /* Add a single Root port */ + qdev_prop_set_uint8(DEVICE(&phb->root), "chassis", phb->chip_id); + qdev_prop_set_uint16(DEVICE(&phb->root), "slot", phb->phb_id); + qdev_set_parent_bus(DEVICE(&phb->root), BUS(pci->bus)); + qdev_init_nofail(DEVICE(&phb->root)); + + /* Setup XIVE Source */ + if (phb->big_phb) { + nr_irqs = PNV_PHB4_MAX_INTs; + } else { + nr_irqs = PNV_PHB4_MAX_INTs >> 1; + } + object_property_set_int(OBJECT(xsrc), nr_irqs, "nr-irqs", &error_fatal); + object_property_set_link(OBJECT(xsrc), OBJECT(phb), "xive", &error_fatal); + object_property_set_bool(OBJECT(xsrc), true, "realized", &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + pnv_phb4_update_xsrc(phb); + + phb->qirqs = qemu_allocate_irqs(xive_source_set_irq, xsrc, xsrc->nr_irqs); +} + +static void pnv_phb4_reset(DeviceState *dev) +{ + PnvPHB4 *phb = PNV_PHB4(dev); + PCIDevice *root_dev = PCI_DEVICE(&phb->root); + + /* + * Configure PCI device id at reset using a property. + */ + pci_config_set_vendor_id(root_dev->config, PCI_VENDOR_ID_IBM); + pci_config_set_device_id(root_dev->config, phb->device_id); +} + +static const char *pnv_phb4_root_bus_path(PCIHostState *host_bridge, + PCIBus *rootbus) +{ + PnvPHB4 *phb = PNV_PHB4(host_bridge); + + snprintf(phb->bus_path, sizeof(phb->bus_path), "00%02x:%02x", + phb->chip_id, phb->phb_id); + return phb->bus_path; +} + +static void pnv_phb4_xive_notify(XiveNotifier *xf, uint32_t srcno) +{ + PnvPHB4 *phb = PNV_PHB4(xf); + uint64_t notif_port = phb->regs[PHB_INT_NOTIFY_ADDR >> 3]; + uint32_t offset = phb->regs[PHB_INT_NOTIFY_INDEX >> 3]; + uint64_t data = XIVE_TRIGGER_PQ | offset | srcno; + MemTxResult result; + + address_space_stq_be(&address_space_memory, notif_port, data, + MEMTXATTRS_UNSPECIFIED, &result); + if (result != MEMTX_OK) { + phb_error(phb, "trigger failed @%"HWADDR_PRIx "\n", notif_port); + return; + } +} + +static Property pnv_phb4_properties[] = { + DEFINE_PROP_UINT32("index", PnvPHB4, phb_id, 0), + DEFINE_PROP_UINT32("chip-id", PnvPHB4, chip_id, 0), + DEFINE_PROP_UINT64("version", PnvPHB4, version, 0), + DEFINE_PROP_UINT16("device-id", PnvPHB4, device_id, 0), + DEFINE_PROP_LINK("stack", PnvPHB4, stack, TYPE_PNV_PHB4_PEC_STACK, + PnvPhb4PecStack *), + DEFINE_PROP_END_OF_LIST(), +}; + +static void pnv_phb4_class_init(ObjectClass *klass, void *data) +{ + PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + XiveNotifierClass *xfc = XIVE_NOTIFIER_CLASS(klass); + + hc->root_bus_path = pnv_phb4_root_bus_path; + dc->realize = pnv_phb4_realize; + device_class_set_props(dc, pnv_phb4_properties); + set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); + dc->user_creatable = false; + dc->reset = pnv_phb4_reset; + + xfc->notify = pnv_phb4_xive_notify; +} + +static const TypeInfo pnv_phb4_type_info = { + .name = TYPE_PNV_PHB4, + .parent = TYPE_PCIE_HOST_BRIDGE, + .instance_init = pnv_phb4_instance_init, + .instance_size = sizeof(PnvPHB4), + .class_init = pnv_phb4_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_XIVE_NOTIFIER }, + { }, + } +}; + +static void pnv_phb4_root_bus_class_init(ObjectClass *klass, void *data) +{ + BusClass *k = BUS_CLASS(klass); + + /* + * PHB4 has only a single root complex. Enforce the limit on the + * parent bus + */ + k->max_dev = 1; +} + +static const TypeInfo pnv_phb4_root_bus_info = { + .name = TYPE_PNV_PHB4_ROOT_BUS, + .parent = TYPE_PCIE_BUS, + .class_init = pnv_phb4_root_bus_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_PCIE_DEVICE }, + { } + }, +}; + +static void pnv_phb4_root_port_reset(DeviceState *dev) +{ + PCIERootPortClass *rpc = PCIE_ROOT_PORT_GET_CLASS(dev); + PCIDevice *d = PCI_DEVICE(dev); + uint8_t *conf = d->config; + + rpc->parent_reset(dev); + + pci_byte_test_and_set_mask(conf + PCI_IO_BASE, + PCI_IO_RANGE_MASK & 0xff); + pci_byte_test_and_clear_mask(conf + PCI_IO_LIMIT, + PCI_IO_RANGE_MASK & 0xff); + pci_set_word(conf + PCI_MEMORY_BASE, 0); + pci_set_word(conf + PCI_MEMORY_LIMIT, 0xfff0); + pci_set_word(conf + PCI_PREF_MEMORY_BASE, 0x1); + pci_set_word(conf + PCI_PREF_MEMORY_LIMIT, 0xfff1); + pci_set_long(conf + PCI_PREF_BASE_UPPER32, 0x1); /* Hack */ + pci_set_long(conf + PCI_PREF_LIMIT_UPPER32, 0xffffffff); +} + +static void pnv_phb4_root_port_realize(DeviceState *dev, Error **errp) +{ + PCIERootPortClass *rpc = PCIE_ROOT_PORT_GET_CLASS(dev); + Error *local_err = NULL; + + rpc->parent_realize(dev, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } +} + +static void pnv_phb4_root_port_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + PCIERootPortClass *rpc = PCIE_ROOT_PORT_CLASS(klass); + + dc->desc = "IBM PHB4 PCIE Root Port"; + dc->user_creatable = false; + + device_class_set_parent_realize(dc, pnv_phb4_root_port_realize, + &rpc->parent_realize); + device_class_set_parent_reset(dc, pnv_phb4_root_port_reset, + &rpc->parent_reset); + + k->vendor_id = PCI_VENDOR_ID_IBM; + k->device_id = PNV_PHB4_DEVICE_ID; + k->revision = 0; + + rpc->exp_offset = 0x48; + rpc->aer_offset = 0x100; + + dc->reset = &pnv_phb4_root_port_reset; +} + +static const TypeInfo pnv_phb4_root_port_info = { + .name = TYPE_PNV_PHB4_ROOT_PORT, + .parent = TYPE_PCIE_ROOT_PORT, + .instance_size = sizeof(PnvPHB4RootPort), + .class_init = pnv_phb4_root_port_class_init, +}; + +static void pnv_phb4_register_types(void) +{ + type_register_static(&pnv_phb4_root_bus_info); + type_register_static(&pnv_phb4_root_port_info); + type_register_static(&pnv_phb4_type_info); + type_register_static(&pnv_phb4_iommu_memory_region_info); +} + +type_init(pnv_phb4_register_types); + +void pnv_phb4_update_regions(PnvPhb4PecStack *stack) +{ + PnvPHB4 *phb = &stack->phb; + + /* Unmap first always */ + if (memory_region_is_mapped(&phb->mr_regs)) { + memory_region_del_subregion(&stack->phbbar, &phb->mr_regs); + } + if (memory_region_is_mapped(&phb->xsrc.esb_mmio)) { + memory_region_del_subregion(&stack->intbar, &phb->xsrc.esb_mmio); + } + + /* Map registers if enabled */ + if (memory_region_is_mapped(&stack->phbbar)) { + memory_region_add_subregion(&stack->phbbar, 0, &phb->mr_regs); + } + + /* Map ESB if enabled */ + if (memory_region_is_mapped(&stack->intbar)) { + memory_region_add_subregion(&stack->intbar, 0, &phb->xsrc.esb_mmio); + } + + /* Check/update m32 */ + pnv_phb4_check_all_mbt(phb); +} + +void pnv_phb4_pic_print_info(PnvPHB4 *phb, Monitor *mon) +{ + uint32_t offset = phb->regs[PHB_INT_NOTIFY_INDEX >> 3]; + + monitor_printf(mon, "PHB4[%x:%x] Source %08x .. %08x\n", + phb->chip_id, phb->phb_id, + offset, offset + phb->xsrc.nr_irqs - 1); + xive_source_pic_print_info(&phb->xsrc, 0, mon); +} diff --git a/hw/pci-host/pnv_phb4_pec.c b/hw/pci-host/pnv_phb4_pec.c new file mode 100644 index 0000000000..911d147ffd --- /dev/null +++ b/hw/pci-host/pnv_phb4_pec.c @@ -0,0 +1,595 @@ +/* + * QEMU PowerPC PowerNV (POWER9) PHB4 model + * + * Copyright (c) 2018-2020, IBM Corporation. + * + * This code is licensed under the GPL version 2 or later. See the + * COPYING file in the top-level directory. + */ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "qemu/log.h" +#include "target/ppc/cpu.h" +#include "hw/ppc/fdt.h" +#include "hw/pci-host/pnv_phb4_regs.h" +#include "hw/pci-host/pnv_phb4.h" +#include "hw/ppc/pnv_xscom.h" +#include "hw/pci/pci_bridge.h" +#include "hw/pci/pci_bus.h" +#include "hw/ppc/pnv.h" +#include "hw/qdev-properties.h" + +#include + +#define phb_pec_error(pec, fmt, ...) \ + qemu_log_mask(LOG_GUEST_ERROR, "phb4_pec[%d:%d]: " fmt "\n", \ + (pec)->chip_id, (pec)->index, ## __VA_ARGS__) + + +static uint64_t pnv_pec_nest_xscom_read(void *opaque, hwaddr addr, + unsigned size) +{ + PnvPhb4PecState *pec = PNV_PHB4_PEC(opaque); + uint32_t reg = addr >> 3; + + /* TODO: add list of allowed registers and error out if not */ + return pec->nest_regs[reg]; +} + +static void pnv_pec_nest_xscom_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + PnvPhb4PecState *pec = PNV_PHB4_PEC(opaque); + uint32_t reg = addr >> 3; + + switch (reg) { + case PEC_NEST_PBCQ_HW_CONFIG: + case PEC_NEST_DROP_PRIO_CTRL: + case PEC_NEST_PBCQ_ERR_INJECT: + case PEC_NEST_PCI_NEST_CLK_TRACE_CTL: + case PEC_NEST_PBCQ_PMON_CTRL: + case PEC_NEST_PBCQ_PBUS_ADDR_EXT: + case PEC_NEST_PBCQ_PRED_VEC_TIMEOUT: + case PEC_NEST_CAPP_CTRL: + case PEC_NEST_PBCQ_READ_STK_OVR: + case PEC_NEST_PBCQ_WRITE_STK_OVR: + case PEC_NEST_PBCQ_STORE_STK_OVR: + case PEC_NEST_PBCQ_RETRY_BKOFF_CTRL: + pec->nest_regs[reg] = val; + break; + default: + phb_pec_error(pec, "%s @0x%"HWADDR_PRIx"=%"PRIx64"\n", __func__, + addr, val); + } +} + +static const MemoryRegionOps pnv_pec_nest_xscom_ops = { + .read = pnv_pec_nest_xscom_read, + .write = pnv_pec_nest_xscom_write, + .valid.min_access_size = 8, + .valid.max_access_size = 8, + .impl.min_access_size = 8, + .impl.max_access_size = 8, + .endianness = DEVICE_BIG_ENDIAN, +}; + +static uint64_t pnv_pec_pci_xscom_read(void *opaque, hwaddr addr, + unsigned size) +{ + PnvPhb4PecState *pec = PNV_PHB4_PEC(opaque); + uint32_t reg = addr >> 3; + + /* TODO: add list of allowed registers and error out if not */ + return pec->pci_regs[reg]; +} + +static void pnv_pec_pci_xscom_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + PnvPhb4PecState *pec = PNV_PHB4_PEC(opaque); + uint32_t reg = addr >> 3; + + switch (reg) { + case PEC_PCI_PBAIB_HW_CONFIG: + case PEC_PCI_PBAIB_READ_STK_OVR: + pec->pci_regs[reg] = val; + break; + default: + phb_pec_error(pec, "%s @0x%"HWADDR_PRIx"=%"PRIx64"\n", __func__, + addr, val); + } +} + +static const MemoryRegionOps pnv_pec_pci_xscom_ops = { + .read = pnv_pec_pci_xscom_read, + .write = pnv_pec_pci_xscom_write, + .valid.min_access_size = 8, + .valid.max_access_size = 8, + .impl.min_access_size = 8, + .impl.max_access_size = 8, + .endianness = DEVICE_BIG_ENDIAN, +}; + +static uint64_t pnv_pec_stk_nest_xscom_read(void *opaque, hwaddr addr, + unsigned size) +{ + PnvPhb4PecStack *stack = PNV_PHB4_PEC_STACK(opaque); + uint32_t reg = addr >> 3; + + /* TODO: add list of allowed registers and error out if not */ + return stack->nest_regs[reg]; +} + +static void pnv_pec_stk_update_map(PnvPhb4PecStack *stack) +{ + PnvPhb4PecState *pec = stack->pec; + MemoryRegion *sysmem = pec->system_memory; + uint64_t bar_en = stack->nest_regs[PEC_NEST_STK_BAR_EN]; + uint64_t bar, mask, size; + char name[64]; + + /* + * NOTE: This will really not work well if those are remapped + * after the PHB has created its sub regions. We could do better + * if we had a way to resize regions but we don't really care + * that much in practice as the stuff below really only happens + * once early during boot + */ + + /* Handle unmaps */ + if (memory_region_is_mapped(&stack->mmbar0) && + !(bar_en & PEC_NEST_STK_BAR_EN_MMIO0)) { + memory_region_del_subregion(sysmem, &stack->mmbar0); + } + if (memory_region_is_mapped(&stack->mmbar1) && + !(bar_en & PEC_NEST_STK_BAR_EN_MMIO1)) { + memory_region_del_subregion(sysmem, &stack->mmbar1); + } + if (memory_region_is_mapped(&stack->phbbar) && + !(bar_en & PEC_NEST_STK_BAR_EN_PHB)) { + memory_region_del_subregion(sysmem, &stack->phbbar); + } + if (memory_region_is_mapped(&stack->intbar) && + !(bar_en & PEC_NEST_STK_BAR_EN_INT)) { + memory_region_del_subregion(sysmem, &stack->intbar); + } + + /* Update PHB */ + pnv_phb4_update_regions(stack); + + /* Handle maps */ + if (!memory_region_is_mapped(&stack->mmbar0) && + (bar_en & PEC_NEST_STK_BAR_EN_MMIO0)) { + bar = stack->nest_regs[PEC_NEST_STK_MMIO_BAR0] >> 8; + mask = stack->nest_regs[PEC_NEST_STK_MMIO_BAR0_MASK]; + size = ((~mask) >> 8) + 1; + snprintf(name, sizeof(name), "pec-%d.%d-stack-%d-mmio0", + pec->chip_id, pec->index, stack->stack_no); + memory_region_init(&stack->mmbar0, OBJECT(stack), name, size); + memory_region_add_subregion(sysmem, bar, &stack->mmbar0); + stack->mmio0_base = bar; + stack->mmio0_size = size; + } + if (!memory_region_is_mapped(&stack->mmbar1) && + (bar_en & PEC_NEST_STK_BAR_EN_MMIO1)) { + bar = stack->nest_regs[PEC_NEST_STK_MMIO_BAR1] >> 8; + mask = stack->nest_regs[PEC_NEST_STK_MMIO_BAR1_MASK]; + size = ((~mask) >> 8) + 1; + snprintf(name, sizeof(name), "pec-%d.%d-stack-%d-mmio1", + pec->chip_id, pec->index, stack->stack_no); + memory_region_init(&stack->mmbar1, OBJECT(stack), name, size); + memory_region_add_subregion(sysmem, bar, &stack->mmbar1); + stack->mmio1_base = bar; + stack->mmio1_size = size; + } + if (!memory_region_is_mapped(&stack->phbbar) && + (bar_en & PEC_NEST_STK_BAR_EN_PHB)) { + bar = stack->nest_regs[PEC_NEST_STK_PHB_REGS_BAR] >> 8; + size = PNV_PHB4_NUM_REGS << 3; + snprintf(name, sizeof(name), "pec-%d.%d-stack-%d-phb", + pec->chip_id, pec->index, stack->stack_no); + memory_region_init(&stack->phbbar, OBJECT(stack), name, size); + memory_region_add_subregion(sysmem, bar, &stack->phbbar); + } + if (!memory_region_is_mapped(&stack->intbar) && + (bar_en & PEC_NEST_STK_BAR_EN_INT)) { + bar = stack->nest_regs[PEC_NEST_STK_INT_BAR] >> 8; + size = PNV_PHB4_MAX_INTs << 16; + snprintf(name, sizeof(name), "pec-%d.%d-stack-%d-int", + stack->pec->chip_id, stack->pec->index, stack->stack_no); + memory_region_init(&stack->intbar, OBJECT(stack), name, size); + memory_region_add_subregion(sysmem, bar, &stack->intbar); + } + + /* Update PHB */ + pnv_phb4_update_regions(stack); +} + +static void pnv_pec_stk_nest_xscom_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + PnvPhb4PecStack *stack = PNV_PHB4_PEC_STACK(opaque); + PnvPhb4PecState *pec = stack->pec; + uint32_t reg = addr >> 3; + + switch (reg) { + case PEC_NEST_STK_PCI_NEST_FIR: + stack->nest_regs[PEC_NEST_STK_PCI_NEST_FIR] = val; + break; + case PEC_NEST_STK_PCI_NEST_FIR_CLR: + stack->nest_regs[PEC_NEST_STK_PCI_NEST_FIR] &= val; + break; + case PEC_NEST_STK_PCI_NEST_FIR_SET: + stack->nest_regs[PEC_NEST_STK_PCI_NEST_FIR] |= val; + break; + case PEC_NEST_STK_PCI_NEST_FIR_MSK: + stack->nest_regs[PEC_NEST_STK_PCI_NEST_FIR_MSK] = val; + break; + case PEC_NEST_STK_PCI_NEST_FIR_MSKC: + stack->nest_regs[PEC_NEST_STK_PCI_NEST_FIR_MSK] &= val; + break; + case PEC_NEST_STK_PCI_NEST_FIR_MSKS: + stack->nest_regs[PEC_NEST_STK_PCI_NEST_FIR_MSK] |= val; + break; + case PEC_NEST_STK_PCI_NEST_FIR_ACT0: + case PEC_NEST_STK_PCI_NEST_FIR_ACT1: + stack->nest_regs[reg] = val; + break; + case PEC_NEST_STK_PCI_NEST_FIR_WOF: + stack->nest_regs[reg] = 0; + break; + case PEC_NEST_STK_ERR_REPORT_0: + case PEC_NEST_STK_ERR_REPORT_1: + case PEC_NEST_STK_PBCQ_GNRL_STATUS: + /* Flag error ? */ + break; + case PEC_NEST_STK_PBCQ_MODE: + stack->nest_regs[reg] = val & 0xff00000000000000ull; + break; + case PEC_NEST_STK_MMIO_BAR0: + case PEC_NEST_STK_MMIO_BAR0_MASK: + case PEC_NEST_STK_MMIO_BAR1: + case PEC_NEST_STK_MMIO_BAR1_MASK: + if (stack->nest_regs[PEC_NEST_STK_BAR_EN] & + (PEC_NEST_STK_BAR_EN_MMIO0 | + PEC_NEST_STK_BAR_EN_MMIO1)) { + phb_pec_error(pec, "Changing enabled BAR unsupported\n"); + } + stack->nest_regs[reg] = val & 0xffffffffff000000ull; + break; + case PEC_NEST_STK_PHB_REGS_BAR: + if (stack->nest_regs[PEC_NEST_STK_BAR_EN] & PEC_NEST_STK_BAR_EN_PHB) { + phb_pec_error(pec, "Changing enabled BAR unsupported\n"); + } + stack->nest_regs[reg] = val & 0xffffffffffc00000ull; + break; + case PEC_NEST_STK_INT_BAR: + if (stack->nest_regs[PEC_NEST_STK_BAR_EN] & PEC_NEST_STK_BAR_EN_INT) { + phb_pec_error(pec, "Changing enabled BAR unsupported\n"); + } + stack->nest_regs[reg] = val & 0xfffffff000000000ull; + break; + case PEC_NEST_STK_BAR_EN: + stack->nest_regs[reg] = val & 0xf000000000000000ull; + pnv_pec_stk_update_map(stack); + break; + case PEC_NEST_STK_DATA_FRZ_TYPE: + case PEC_NEST_STK_PBCQ_TUN_BAR: + /* Not used for now */ + stack->nest_regs[reg] = val; + break; + default: + qemu_log_mask(LOG_UNIMP, "phb4_pec: nest_xscom_write 0x%"HWADDR_PRIx + "=%"PRIx64"\n", addr, val); + } +} + +static const MemoryRegionOps pnv_pec_stk_nest_xscom_ops = { + .read = pnv_pec_stk_nest_xscom_read, + .write = pnv_pec_stk_nest_xscom_write, + .valid.min_access_size = 8, + .valid.max_access_size = 8, + .impl.min_access_size = 8, + .impl.max_access_size = 8, + .endianness = DEVICE_BIG_ENDIAN, +}; + +static uint64_t pnv_pec_stk_pci_xscom_read(void *opaque, hwaddr addr, + unsigned size) +{ + PnvPhb4PecStack *stack = PNV_PHB4_PEC_STACK(opaque); + uint32_t reg = addr >> 3; + + /* TODO: add list of allowed registers and error out if not */ + return stack->pci_regs[reg]; +} + +static void pnv_pec_stk_pci_xscom_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + PnvPhb4PecStack *stack = PNV_PHB4_PEC_STACK(opaque); + uint32_t reg = addr >> 3; + + switch (reg) { + case PEC_PCI_STK_PCI_FIR: + stack->nest_regs[reg] = val; + break; + case PEC_PCI_STK_PCI_FIR_CLR: + stack->nest_regs[PEC_PCI_STK_PCI_FIR] &= val; + break; + case PEC_PCI_STK_PCI_FIR_SET: + stack->nest_regs[PEC_PCI_STK_PCI_FIR] |= val; + break; + case PEC_PCI_STK_PCI_FIR_MSK: + stack->nest_regs[reg] = val; + break; + case PEC_PCI_STK_PCI_FIR_MSKC: + stack->nest_regs[PEC_PCI_STK_PCI_FIR_MSK] &= val; + break; + case PEC_PCI_STK_PCI_FIR_MSKS: + stack->nest_regs[PEC_PCI_STK_PCI_FIR_MSK] |= val; + break; + case PEC_PCI_STK_PCI_FIR_ACT0: + case PEC_PCI_STK_PCI_FIR_ACT1: + stack->nest_regs[reg] = val; + break; + case PEC_PCI_STK_PCI_FIR_WOF: + stack->nest_regs[reg] = 0; + break; + case PEC_PCI_STK_ETU_RESET: + stack->nest_regs[reg] = val & 0x8000000000000000ull; + /* TODO: Implement reset */ + break; + case PEC_PCI_STK_PBAIB_ERR_REPORT: + break; + case PEC_PCI_STK_PBAIB_TX_CMD_CRED: + case PEC_PCI_STK_PBAIB_TX_DAT_CRED: + stack->nest_regs[reg] = val; + break; + default: + qemu_log_mask(LOG_UNIMP, "phb4_pec_stk: pci_xscom_write 0x%"HWADDR_PRIx + "=%"PRIx64"\n", addr, val); + } +} + +static const MemoryRegionOps pnv_pec_stk_pci_xscom_ops = { + .read = pnv_pec_stk_pci_xscom_read, + .write = pnv_pec_stk_pci_xscom_write, + .valid.min_access_size = 8, + .valid.max_access_size = 8, + .impl.min_access_size = 8, + .impl.max_access_size = 8, + .endianness = DEVICE_BIG_ENDIAN, +}; + +static void pnv_pec_instance_init(Object *obj) +{ + PnvPhb4PecState *pec = PNV_PHB4_PEC(obj); + int i; + + for (i = 0; i < PHB4_PEC_MAX_STACKS; i++) { + object_initialize_child(obj, "stack[*]", &pec->stacks[i], + sizeof(pec->stacks[i]), TYPE_PNV_PHB4_PEC_STACK, + &error_abort, NULL); + } +} + +static void pnv_pec_realize(DeviceState *dev, Error **errp) +{ + PnvPhb4PecState *pec = PNV_PHB4_PEC(dev); + Error *local_err = NULL; + char name[64]; + int i; + + assert(pec->system_memory); + + /* Create stacks */ + for (i = 0; i < pec->num_stacks; i++) { + PnvPhb4PecStack *stack = &pec->stacks[i]; + Object *stk_obj = OBJECT(stack); + + object_property_set_int(stk_obj, i, "stack-no", &error_abort); + object_property_set_link(stk_obj, OBJECT(pec), "pec", &error_abort); + object_property_set_bool(stk_obj, true, "realized", &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + } + + /* Initialize the XSCOM regions for the PEC registers */ + snprintf(name, sizeof(name), "xscom-pec-%d.%d-nest", pec->chip_id, + pec->index); + pnv_xscom_region_init(&pec->nest_regs_mr, OBJECT(dev), + &pnv_pec_nest_xscom_ops, pec, name, + PHB4_PEC_NEST_REGS_COUNT); + + snprintf(name, sizeof(name), "xscom-pec-%d.%d-pci", pec->chip_id, + pec->index); + pnv_xscom_region_init(&pec->pci_regs_mr, OBJECT(dev), + &pnv_pec_pci_xscom_ops, pec, name, + PHB4_PEC_PCI_REGS_COUNT); +} + +static int pnv_pec_dt_xscom(PnvXScomInterface *dev, void *fdt, + int xscom_offset) +{ + PnvPhb4PecState *pec = PNV_PHB4_PEC(dev); + PnvPhb4PecClass *pecc = PNV_PHB4_PEC_GET_CLASS(dev); + uint32_t nbase = pecc->xscom_nest_base(pec); + uint32_t pbase = pecc->xscom_pci_base(pec); + int offset, i; + char *name; + uint32_t reg[] = { + cpu_to_be32(nbase), + cpu_to_be32(pecc->xscom_nest_size), + cpu_to_be32(pbase), + cpu_to_be32(pecc->xscom_pci_size), + }; + + name = g_strdup_printf("pbcq@%x", nbase); + offset = fdt_add_subnode(fdt, xscom_offset, name); + _FDT(offset); + g_free(name); + + _FDT((fdt_setprop(fdt, offset, "reg", reg, sizeof(reg)))); + + _FDT((fdt_setprop_cell(fdt, offset, "ibm,pec-index", pec->index))); + _FDT((fdt_setprop_cell(fdt, offset, "#address-cells", 1))); + _FDT((fdt_setprop_cell(fdt, offset, "#size-cells", 0))); + _FDT((fdt_setprop(fdt, offset, "compatible", pecc->compat, + pecc->compat_size))); + + for (i = 0; i < pec->num_stacks; i++) { + PnvPhb4PecStack *stack = &pec->stacks[i]; + PnvPHB4 *phb = &stack->phb; + int stk_offset; + + name = g_strdup_printf("stack@%x", i); + stk_offset = fdt_add_subnode(fdt, offset, name); + _FDT(stk_offset); + g_free(name); + _FDT((fdt_setprop(fdt, stk_offset, "compatible", pecc->stk_compat, + pecc->stk_compat_size))); + _FDT((fdt_setprop_cell(fdt, stk_offset, "reg", i))); + _FDT((fdt_setprop_cell(fdt, stk_offset, "ibm,phb-index", phb->phb_id))); + } + + return 0; +} + +static Property pnv_pec_properties[] = { + DEFINE_PROP_UINT32("index", PnvPhb4PecState, index, 0), + DEFINE_PROP_UINT32("num-stacks", PnvPhb4PecState, num_stacks, 0), + DEFINE_PROP_UINT32("chip-id", PnvPhb4PecState, chip_id, 0), + DEFINE_PROP_LINK("system-memory", PnvPhb4PecState, system_memory, + TYPE_MEMORY_REGION, MemoryRegion *), + DEFINE_PROP_END_OF_LIST(), +}; + +static uint32_t pnv_pec_xscom_pci_base(PnvPhb4PecState *pec) +{ + return PNV9_XSCOM_PEC_PCI_BASE + 0x1000000 * pec->index; +} + +static uint32_t pnv_pec_xscom_nest_base(PnvPhb4PecState *pec) +{ + return PNV9_XSCOM_PEC_NEST_BASE + 0x400 * pec->index; +} + +static void pnv_pec_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PnvXScomInterfaceClass *xdc = PNV_XSCOM_INTERFACE_CLASS(klass); + PnvPhb4PecClass *pecc = PNV_PHB4_PEC_CLASS(klass); + static const char compat[] = "ibm,power9-pbcq"; + static const char stk_compat[] = "ibm,power9-phb-stack"; + + xdc->dt_xscom = pnv_pec_dt_xscom; + + dc->realize = pnv_pec_realize; + device_class_set_props(dc, pnv_pec_properties); + dc->user_creatable = false; + + pecc->xscom_nest_base = pnv_pec_xscom_nest_base; + pecc->xscom_pci_base = pnv_pec_xscom_pci_base; + pecc->xscom_nest_size = PNV9_XSCOM_PEC_NEST_SIZE; + pecc->xscom_pci_size = PNV9_XSCOM_PEC_PCI_SIZE; + pecc->compat = compat; + pecc->compat_size = sizeof(compat); + pecc->stk_compat = stk_compat; + pecc->stk_compat_size = sizeof(stk_compat); +} + +static const TypeInfo pnv_pec_type_info = { + .name = TYPE_PNV_PHB4_PEC, + .parent = TYPE_DEVICE, + .instance_size = sizeof(PnvPhb4PecState), + .instance_init = pnv_pec_instance_init, + .class_init = pnv_pec_class_init, + .class_size = sizeof(PnvPhb4PecClass), + .interfaces = (InterfaceInfo[]) { + { TYPE_PNV_XSCOM_INTERFACE }, + { } + } +}; + +static void pnv_pec_stk_instance_init(Object *obj) +{ + PnvPhb4PecStack *stack = PNV_PHB4_PEC_STACK(obj); + + object_initialize_child(obj, "phb", &stack->phb, sizeof(stack->phb), + TYPE_PNV_PHB4, &error_abort, NULL); +} + +static void pnv_pec_stk_realize(DeviceState *dev, Error **errp) +{ + PnvPhb4PecStack *stack = PNV_PHB4_PEC_STACK(dev); + PnvPhb4PecState *pec = stack->pec; + char name[64]; + + assert(pec); + + /* Initialize the XSCOM regions for the stack registers */ + snprintf(name, sizeof(name), "xscom-pec-%d.%d-nest-stack-%d", + pec->chip_id, pec->index, stack->stack_no); + pnv_xscom_region_init(&stack->nest_regs_mr, OBJECT(stack), + &pnv_pec_stk_nest_xscom_ops, stack, name, + PHB4_PEC_NEST_STK_REGS_COUNT); + + snprintf(name, sizeof(name), "xscom-pec-%d.%d-pci-stack-%d", + pec->chip_id, pec->index, stack->stack_no); + pnv_xscom_region_init(&stack->pci_regs_mr, OBJECT(stack), + &pnv_pec_stk_pci_xscom_ops, stack, name, + PHB4_PEC_PCI_STK_REGS_COUNT); + + /* PHB pass-through */ + snprintf(name, sizeof(name), "xscom-pec-%d.%d-pci-stack-%d-phb", + pec->chip_id, pec->index, stack->stack_no); + pnv_xscom_region_init(&stack->phb_regs_mr, OBJECT(&stack->phb), + &pnv_phb4_xscom_ops, &stack->phb, name, 0x40); + + /* + * Let the machine/chip realize the PHB object to customize more + * easily some fields + */ +} + +static Property pnv_pec_stk_properties[] = { + DEFINE_PROP_UINT32("stack-no", PnvPhb4PecStack, stack_no, 0), + DEFINE_PROP_LINK("pec", PnvPhb4PecStack, pec, TYPE_PNV_PHB4_PEC, + PnvPhb4PecState *), + DEFINE_PROP_END_OF_LIST(), +}; + +static void pnv_pec_stk_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + device_class_set_props(dc, pnv_pec_stk_properties); + dc->realize = pnv_pec_stk_realize; + dc->user_creatable = false; + + /* TODO: reset regs ? */ +} + +static const TypeInfo pnv_pec_stk_type_info = { + .name = TYPE_PNV_PHB4_PEC_STACK, + .parent = TYPE_DEVICE, + .instance_size = sizeof(PnvPhb4PecStack), + .instance_init = pnv_pec_stk_instance_init, + .class_init = pnv_pec_stk_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_PNV_XSCOM_INTERFACE }, + { } + } +}; + +static void pnv_pec_register_types(void) +{ + type_register_static(&pnv_pec_type_info); + type_register_static(&pnv_pec_stk_type_info); +} + +type_init(pnv_pec_register_types); diff --git a/hw/pci-host/ppce500.c b/hw/pci-host/ppce500.c index 8bed8e8941..d71072731d 100644 --- a/hw/pci-host/ppce500.c +++ b/hw/pci-host/ppce500.c @@ -531,7 +531,7 @@ static void e500_pcihost_class_init(ObjectClass *klass, void *data) dc->realize = e500_pcihost_realize; set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); - dc->props = pcihost_properties; + device_class_set_props(dc, pcihost_properties); dc->vmsd = &vmstate_ppce500_pci; } diff --git a/hw/pci-host/prep.c b/hw/pci-host/prep.c index 85d7ba9037..1a02e9a670 100644 --- a/hw/pci-host/prep.c +++ b/hw/pci-host/prep.c @@ -32,7 +32,7 @@ #include "hw/pci/pci_host.h" #include "hw/qdev-properties.h" #include "migration/vmstate.h" -#include "hw/i386/pc.h" +#include "hw/intc/i8259.h" #include "hw/irq.h" #include "hw/loader.h" #include "hw/or-irq.h" @@ -325,9 +325,8 @@ static void raven_realize(PCIDevice *d, Error **errp) d->config[0x0D] = 0x10; // latency_timer d->config[0x34] = 0x00; // capabilities_pointer - memory_region_init_ram_nomigrate(&s->bios, OBJECT(s), "bios", BIOS_SIZE, - &error_fatal); - memory_region_set_readonly(&s->bios, true); + memory_region_init_rom_nomigrate(&s->bios, OBJECT(s), "bios", BIOS_SIZE, + &error_fatal); memory_region_add_subregion(get_system_memory(), (uint32_t)(-BIOS_SIZE), &s->bios); if (s->bios_name) { @@ -335,7 +334,8 @@ static void raven_realize(PCIDevice *d, Error **errp) if (filename) { if (s->elf_machine != EM_NONE) { bios_size = load_elf(filename, NULL, NULL, NULL, NULL, - NULL, NULL, 1, s->elf_machine, 0, 0); + NULL, NULL, NULL, 1, s->elf_machine, + 0, 0); } if (bios_size < 0) { bios_size = get_image_size(filename); @@ -415,7 +415,7 @@ static void raven_pcihost_class_init(ObjectClass *klass, void *data) set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); dc->realize = raven_pcihost_realizefn; - dc->props = raven_pcihost_properties; + device_class_set_props(dc, raven_pcihost_properties); dc->fw_name = "pci"; } diff --git a/hw/pci-host/q35.c b/hw/pci-host/q35.c index 158d270b9f..2bbc90b28f 100644 --- a/hw/pci-host/q35.c +++ b/hw/pci-host/q35.c @@ -29,6 +29,7 @@ */ #include "qemu/osdep.h" +#include "hw/i386/pc.h" #include "hw/pci-host/q35.h" #include "hw/qdev-properties.h" #include "migration/vmstate.h" @@ -165,14 +166,6 @@ static void q35_host_get_pci_hole64_end(Object *obj, Visitor *v, visit_type_uint64(v, name, &value, errp); } -static void q35_host_get_mmcfg_size(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - PCIExpressHost *e = PCIE_HOST_BRIDGE(obj); - - visit_type_uint64(v, name, &e->size, errp); -} - /* * NOTE: setting defaults for the mch.* fields in this table * doesn't work, because mch is a separate QOM object that is @@ -202,7 +195,7 @@ static void q35_host_class_init(ObjectClass *klass, void *data) hc->root_bus_path = q35_host_root_bus_path; dc->realize = q35_host_realize; - dc->props = q35_host_props; + device_class_set_props(dc, q35_host_props); /* Reason: needs to be wired up by pc_q35_init */ dc->user_creatable = false; set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); @@ -213,6 +206,7 @@ static void q35_host_initfn(Object *obj) { Q35PCIHost *s = Q35_HOST_DEVICE(obj); PCIHostState *phb = PCI_HOST_BRIDGE(obj); + PCIExpressHost *pehb = PCIE_HOST_BRIDGE(obj); memory_region_init_io(&phb->conf_mem, obj, &pci_host_conf_le_ops, phb, "pci-conf-idx", 4); @@ -242,9 +236,8 @@ static void q35_host_initfn(Object *obj) q35_host_get_pci_hole64_end, NULL, NULL, NULL, NULL); - object_property_add(obj, PCIE_HOST_MCFG_SIZE, "uint64", - q35_host_get_mmcfg_size, - NULL, NULL, NULL, NULL); + object_property_add_uint64_ptr(obj, PCIE_HOST_MCFG_SIZE, + &pehb->size, OBJ_PROP_FLAG_READ, NULL); object_property_add_link(obj, MCH_HOST_PROP_RAM_MEM, TYPE_MEMORY_REGION, (Object **) &s->mch.ram_memory, @@ -275,20 +268,20 @@ static const TypeInfo q35_host_info = { * MCH D0:F0 */ -static uint64_t tseg_blackhole_read(void *ptr, hwaddr reg, unsigned size) +static uint64_t blackhole_read(void *ptr, hwaddr reg, unsigned size) { return 0xffffffff; } -static void tseg_blackhole_write(void *opaque, hwaddr addr, uint64_t val, - unsigned width) +static void blackhole_write(void *opaque, hwaddr addr, uint64_t val, + unsigned width) { /* nothing */ } -static const MemoryRegionOps tseg_blackhole_ops = { - .read = tseg_blackhole_read, - .write = tseg_blackhole_write, +static const MemoryRegionOps blackhole_ops = { + .read = blackhole_read, + .write = blackhole_write, .endianness = DEVICE_NATIVE_ENDIAN, .valid.min_access_size = 1, .valid.max_access_size = 4, @@ -430,6 +423,46 @@ static void mch_update_ext_tseg_mbytes(MCHPCIState *mch) } } +static void mch_update_smbase_smram(MCHPCIState *mch) +{ + PCIDevice *pd = PCI_DEVICE(mch); + uint8_t *reg = pd->config + MCH_HOST_BRIDGE_F_SMBASE; + bool lck; + + if (!mch->has_smram_at_smbase) { + return; + } + + if (*reg == MCH_HOST_BRIDGE_F_SMBASE_QUERY) { + pd->wmask[MCH_HOST_BRIDGE_F_SMBASE] = + MCH_HOST_BRIDGE_F_SMBASE_LCK; + *reg = MCH_HOST_BRIDGE_F_SMBASE_IN_RAM; + return; + } + + /* + * default/reset state, discard written value + * which will disable SMRAM balackhole at SMBASE + */ + if (pd->wmask[MCH_HOST_BRIDGE_F_SMBASE] == 0xff) { + *reg = 0x00; + } + + memory_region_transaction_begin(); + if (*reg & MCH_HOST_BRIDGE_F_SMBASE_LCK) { + /* disable all writes */ + pd->wmask[MCH_HOST_BRIDGE_F_SMBASE] &= + ~MCH_HOST_BRIDGE_F_SMBASE_LCK; + *reg = MCH_HOST_BRIDGE_F_SMBASE_LCK; + lck = true; + } else { + lck = false; + } + memory_region_set_enabled(&mch->smbase_blackhole, lck); + memory_region_set_enabled(&mch->smbase_window, lck); + memory_region_transaction_commit(); +} + static void mch_write_config(PCIDevice *d, uint32_t address, uint32_t val, int len) { @@ -456,6 +489,10 @@ static void mch_write_config(PCIDevice *d, MCH_HOST_BRIDGE_EXT_TSEG_MBYTES_SIZE)) { mch_update_ext_tseg_mbytes(mch); } + + if (ranges_overlap(address, len, MCH_HOST_BRIDGE_F_SMBASE, 1)) { + mch_update_smbase_smram(mch); + } } static void mch_update(MCHPCIState *mch) @@ -464,6 +501,7 @@ static void mch_update(MCHPCIState *mch) mch_update_pam(mch); mch_update_smram(mch); mch_update_ext_tseg_mbytes(mch); + mch_update_smbase_smram(mch); /* * pci hole goes from end-of-low-ram to io-apic. @@ -514,6 +552,9 @@ static void mch_reset(DeviceState *qdev) MCH_HOST_BRIDGE_EXT_TSEG_MBYTES_QUERY); } + d->config[MCH_HOST_BRIDGE_F_SMBASE] = 0; + d->wmask[MCH_HOST_BRIDGE_F_SMBASE] = 0xff; + mch_update(mch); } @@ -563,7 +604,7 @@ static void mch_realize(PCIDevice *d, Error **errp) memory_region_add_subregion(&mch->smram, 0xfeda0000, &mch->high_smram); memory_region_init_io(&mch->tseg_blackhole, OBJECT(mch), - &tseg_blackhole_ops, NULL, + &blackhole_ops, NULL, "tseg-blackhole", 0); memory_region_set_enabled(&mch->tseg_blackhole, false); memory_region_add_subregion_overlap(mch->system_memory, @@ -575,6 +616,27 @@ static void mch_realize(PCIDevice *d, Error **errp) memory_region_set_enabled(&mch->tseg_window, false); memory_region_add_subregion(&mch->smram, mch->below_4g_mem_size, &mch->tseg_window); + + /* + * This is not what hardware does, so it's QEMU specific hack. + * See commit message for details. + */ + memory_region_init_io(&mch->smbase_blackhole, OBJECT(mch), &blackhole_ops, + NULL, "smbase-blackhole", + MCH_HOST_BRIDGE_SMBASE_SIZE); + memory_region_set_enabled(&mch->smbase_blackhole, false); + memory_region_add_subregion_overlap(mch->system_memory, + MCH_HOST_BRIDGE_SMBASE_ADDR, + &mch->smbase_blackhole, 1); + + memory_region_init_alias(&mch->smbase_window, OBJECT(mch), + "smbase-window", mch->ram_memory, + MCH_HOST_BRIDGE_SMBASE_ADDR, + MCH_HOST_BRIDGE_SMBASE_SIZE); + memory_region_set_enabled(&mch->smbase_window, false); + memory_region_add_subregion(&mch->smram, MCH_HOST_BRIDGE_SMBASE_ADDR, + &mch->smbase_window); + object_property_add_const_link(qdev_get_machine(), "smram", OBJECT(&mch->smram), &error_abort); @@ -601,6 +663,7 @@ uint64_t mch_mcfg_base(void) static Property mch_props[] = { DEFINE_PROP_UINT16("extended-tseg-mbytes", MCHPCIState, ext_tseg_mbytes, 16), + DEFINE_PROP_BOOL("smbase-smram", MCHPCIState, has_smram_at_smbase, true), DEFINE_PROP_END_OF_LIST(), }; @@ -612,7 +675,7 @@ static void mch_class_init(ObjectClass *klass, void *data) k->realize = mch_realize; k->config_write = mch_write_config; dc->reset = mch_reset; - dc->props = mch_props; + device_class_set_props(dc, mch_props); set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); dc->desc = "Host bridge"; dc->vmsd = &vmstate_mch; diff --git a/hw/pci-host/sabre.c b/hw/pci-host/sabre.c index fae20ee97c..2b8503b709 100644 --- a/hw/pci-host/sabre.c +++ b/hw/pci-host/sabre.c @@ -521,7 +521,7 @@ static void sabre_class_init(ObjectClass *klass, void *data) dc->realize = sabre_realize; dc->reset = sabre_reset; - dc->props = sabre_properties; + device_class_set_props(dc, sabre_properties); set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); dc->fw_name = "pci"; sbc->explicit_ofw_unit_address = sabre_ofw_unit_address; diff --git a/hw/pci-host/uninorth.c b/hw/pci-host/uninorth.c index 75bc506965..cf70b76fe2 100644 --- a/hw/pci-host/uninorth.c +++ b/hw/pci-host/uninorth.c @@ -476,7 +476,7 @@ static void pci_unin_main_class_init(ObjectClass *klass, void *data) SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass); dc->realize = pci_unin_main_realize; - dc->props = pci_unin_main_pci_host_props; + device_class_set_props(dc, pci_unin_main_pci_host_props); set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); dc->fw_name = "pci"; sbc->explicit_ofw_unit_address = pci_unin_main_ofw_unit_address; diff --git a/hw/pci-host/versatile.c b/hw/pci-host/versatile.c index b731d0544f..cfb9a78ea6 100644 --- a/hw/pci-host/versatile.c +++ b/hw/pci-host/versatile.c @@ -509,7 +509,7 @@ static void pci_vpb_class_init(ObjectClass *klass, void *data) dc->realize = pci_vpb_realize; dc->reset = pci_vpb_reset; dc->vmsd = &pci_vpb_vmstate; - dc->props = pci_vpb_properties; + device_class_set_props(dc, pci_vpb_properties); } static const TypeInfo pci_vpb_info = { diff --git a/hw/pci-host/xen_igd_pt.c b/hw/pci-host/xen_igd_pt.c new file mode 100644 index 0000000000..efcc9347ff --- /dev/null +++ b/hw/pci-host/xen_igd_pt.c @@ -0,0 +1,120 @@ +/* + * QEMU Intel IGD Passthrough Host Bridge Emulation + * + * Copyright (c) 2006 Fabrice Bellard + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include "hw/pci/pci.h" +#include "hw/pci/pci_host.h" +#include "hw/pci-host/i440fx.h" +#include "qapi/error.h" + +typedef struct { + uint8_t offset; + uint8_t len; +} IGDHostInfo; + +/* Here we just expose minimal host bridge offset subset. */ +static const IGDHostInfo igd_host_bridge_infos[] = { + {PCI_REVISION_ID, 2}, + {PCI_SUBSYSTEM_VENDOR_ID, 2}, + {PCI_SUBSYSTEM_ID, 2}, + {0x50, 2}, /* SNB: processor graphics control register */ + {0x52, 2}, /* processor graphics control register */ + {0xa4, 4}, /* SNB: graphics base of stolen memory */ + {0xa8, 4}, /* SNB: base of GTT stolen memory */ +}; + +static void host_pci_config_read(int pos, int len, uint32_t *val, Error **errp) +{ + int rc, config_fd; + /* Access real host bridge. */ + char *path = g_strdup_printf("/sys/bus/pci/devices/%04x:%02x:%02x.%d/%s", + 0, 0, 0, 0, "config"); + + config_fd = open(path, O_RDWR); + if (config_fd < 0) { + error_setg_errno(errp, errno, "Failed to open: %s", path); + goto out; + } + + if (lseek(config_fd, pos, SEEK_SET) != pos) { + error_setg_errno(errp, errno, "Failed to seek: %s", path); + goto out_close_fd; + } + + do { + rc = read(config_fd, (uint8_t *)val, len); + } while (rc < 0 && (errno == EINTR || errno == EAGAIN)); + if (rc != len) { + error_setg_errno(errp, errno, "Failed to read: %s", path); + } + + out_close_fd: + close(config_fd); + out: + g_free(path); +} + +static void igd_pt_i440fx_realize(PCIDevice *pci_dev, Error **errp) +{ + uint32_t val = 0; + size_t i; + int pos, len; + Error *local_err = NULL; + + for (i = 0; i < ARRAY_SIZE(igd_host_bridge_infos); i++) { + pos = igd_host_bridge_infos[i].offset; + len = igd_host_bridge_infos[i].len; + host_pci_config_read(pos, len, &val, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + pci_default_write_config(pci_dev, pos, val, len); + } +} + +static void igd_passthrough_i440fx_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + k->realize = igd_pt_i440fx_realize; + dc->desc = "IGD Passthrough Host bridge"; +} + +static const TypeInfo igd_passthrough_i440fx_info = { + .name = TYPE_IGD_PASSTHROUGH_I440FX_PCI_DEVICE, + .parent = TYPE_I440FX_PCI_DEVICE, + .instance_size = sizeof(PCII440FXState), + .class_init = igd_passthrough_i440fx_class_init, +}; + +static void igd_pt_i440fx_register_types(void) +{ + type_register_static(&igd_passthrough_i440fx_info); +} + +type_init(igd_pt_i440fx_register_types) diff --git a/hw/pci-host/xilinx-pcie.c b/hw/pci-host/xilinx-pcie.c index 17d5024349..e06f2b59cf 100644 --- a/hw/pci-host/xilinx-pcie.c +++ b/hw/pci-host/xilinx-pcie.c @@ -177,7 +177,7 @@ static void xilinx_pcie_host_class_init(ObjectClass *klass, void *data) dc->realize = xilinx_pcie_host_realize; set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); dc->fw_name = "pci"; - dc->props = xilinx_pcie_host_props; + device_class_set_props(dc, xilinx_pcie_host_props); } static const TypeInfo xilinx_pcie_host_info = { diff --git a/hw/pci/pci-stub.c b/hw/pci/pci-stub.c index c04a5df651..cc2a2e1f73 100644 --- a/hw/pci/pci-stub.c +++ b/hw/pci/pci-stub.c @@ -26,6 +26,7 @@ #include "qapi/qmp/qerror.h" #include "hw/pci/pci.h" #include "hw/pci/msi.h" +#include "hw/pci/msix.h" bool msi_nonbroken; bool pci_available; @@ -64,3 +65,29 @@ void msi_notify(PCIDevice *dev, unsigned int vector) { g_assert_not_reached(); } + +/* Required by target/i386/kvm.c */ +bool msi_is_masked(const PCIDevice *dev, unsigned vector) +{ + g_assert_not_reached(); +} + +MSIMessage msi_get_message(PCIDevice *dev, unsigned int vector) +{ + g_assert_not_reached(); +} + +int msix_enabled(PCIDevice *dev) +{ + return false; +} + +bool msix_is_masked(PCIDevice *dev, unsigned vector) +{ + g_assert_not_reached(); +} + +MSIMessage msix_get_message(PCIDevice *dev, unsigned int vector) +{ + g_assert_not_reached(); +} diff --git a/hw/pci/pci.c b/hw/pci/pci.c index cbc7a32568..b5bc842fac 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -69,8 +69,6 @@ static Property pci_props[] = { DEFINE_PROP_UINT32("rombar", PCIDevice, rom_bar, 1), DEFINE_PROP_BIT("multifunction", PCIDevice, cap_present, QEMU_PCI_CAP_MULTIFUNCTION_BITNR, false), - DEFINE_PROP_BIT("command_serr_enable", PCIDevice, cap_present, - QEMU_PCI_CAP_SERR_BITNR, true), DEFINE_PROP_BIT("x-pcie-lnksta-dllla", PCIDevice, cap_present, QEMU_PCIE_LNKSTA_DLLLA_BITNR, true), DEFINE_PROP_BIT("x-pcie-extcap-init", PCIDevice, cap_present, @@ -124,7 +122,7 @@ static void pci_bus_realize(BusState *qbus, Error **errp) bus->machine_done.notify = pcibus_machine_done; qemu_add_machine_init_done_notifier(&bus->machine_done); - vmstate_register(NULL, -1, &vmstate_pcibus, bus); + vmstate_register(NULL, VMSTATE_INSTANCE_ID_ANY, &vmstate_pcibus, bus); } static void pcie_bus_realize(BusState *qbus, Error **errp) @@ -304,8 +302,11 @@ static void pci_do_device_reset(PCIDevice *dev) pci_word_test_and_clear_mask(dev->config + PCI_STATUS, pci_get_word(dev->wmask + PCI_STATUS) | pci_get_word(dev->w1cmask + PCI_STATUS)); + /* Some devices make bits of PCI_INTERRUPT_LINE read only */ + pci_byte_test_and_clear_mask(dev->config + PCI_INTERRUPT_LINE, + pci_get_word(dev->wmask + PCI_INTERRUPT_LINE) | + pci_get_word(dev->w1cmask + PCI_INTERRUPT_LINE)); dev->config[PCI_CACHE_LINE_SIZE] = 0x0; - dev->config[PCI_INTERRUPT_LINE] = 0x0; for (r = 0; r < PCI_NUM_REGIONS; ++r) { PCIIORegion *region = &dev->io_regions[r]; if (!region->size) { @@ -751,9 +752,7 @@ static void pci_init_wmask(PCIDevice *dev) pci_set_word(dev->wmask + PCI_COMMAND, PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | PCI_COMMAND_INTX_DISABLE); - if (dev->cap_present & QEMU_PCI_CAP_SERR) { - pci_word_test_and_set_mask(dev->wmask + PCI_COMMAND, PCI_COMMAND_SERR); - } + pci_word_test_and_set_mask(dev->wmask + PCI_COMMAND, PCI_COMMAND_SERR); memset(dev->wmask + PCI_CONFIG_HEADER_SIZE, 0xff, config_size - PCI_CONFIG_HEADER_SIZE); @@ -2628,7 +2627,7 @@ static void pci_device_class_init(ObjectClass *klass, void *data) k->realize = pci_qdev_realize; k->unrealize = pci_qdev_unrealize; k->bus_type = TYPE_PCI_BUS; - k->props = pci_props; + device_class_set_props(k, pci_props); } static void pci_device_class_base_init(ObjectClass *klass, void *data) diff --git a/hw/pci/pci_host.c b/hw/pci/pci_host.c index c5f9244934..ce7bcdb1d5 100644 --- a/hw/pci/pci_host.c +++ b/hw/pci/pci_host.c @@ -106,7 +106,7 @@ uint32_t pci_host_config_read_common(PCIDevice *pci_dev, uint32_t addr, return ret; } -void pci_data_write(PCIBus *s, uint32_t addr, uint32_t val, int len) +void pci_data_write(PCIBus *s, uint32_t addr, uint32_t val, unsigned len) { PCIDevice *pci_dev = pci_dev_find_by_addr(s, addr); uint32_t config_addr = addr & (PCI_CONFIG_SPACE_SIZE - 1); @@ -115,28 +115,21 @@ void pci_data_write(PCIBus *s, uint32_t addr, uint32_t val, int len) return; } - PCI_DPRINTF("%s: %s: addr=%02" PRIx32 " val=%08" PRIx32 " len=%d\n", - __func__, pci_dev->name, config_addr, val, len); pci_host_config_write_common(pci_dev, config_addr, PCI_CONFIG_SPACE_SIZE, val, len); } -uint32_t pci_data_read(PCIBus *s, uint32_t addr, int len) +uint32_t pci_data_read(PCIBus *s, uint32_t addr, unsigned len) { PCIDevice *pci_dev = pci_dev_find_by_addr(s, addr); uint32_t config_addr = addr & (PCI_CONFIG_SPACE_SIZE - 1); - uint32_t val; if (!pci_dev) { return ~0x0; } - val = pci_host_config_read_common(pci_dev, config_addr, - PCI_CONFIG_SPACE_SIZE, len); - PCI_DPRINTF("%s: %s: addr=%02"PRIx32" val=%08"PRIx32" len=%d\n", - __func__, pci_dev->name, config_addr, val, len); - - return val; + return pci_host_config_read_common(pci_dev, config_addr, + PCI_CONFIG_SPACE_SIZE, len); } static void pci_host_config_write(void *opaque, hwaddr addr, @@ -167,8 +160,7 @@ static void pci_host_data_write(void *opaque, hwaddr addr, uint64_t val, unsigned len) { PCIHostState *s = opaque; - PCI_DPRINTF("write addr " TARGET_FMT_plx " len %d val %x\n", - addr, len, (unsigned)val); + if (s->config_reg & (1u << 31)) pci_data_write(s->bus, s->config_reg | (addr & 3), val, len); } @@ -177,14 +169,11 @@ static uint64_t pci_host_data_read(void *opaque, hwaddr addr, unsigned len) { PCIHostState *s = opaque; - uint32_t val; + if (!(s->config_reg & (1U << 31))) { return 0xffffffff; } - val = pci_data_read(s->bus, s->config_reg | (addr & 3), len); - PCI_DPRINTF("read addr " TARGET_FMT_plx " len %d val %x\n", - addr, len, val); - return val; + return pci_data_read(s->bus, s->config_reg | (addr & 3), len); } const MemoryRegionOps pci_host_conf_le_ops = { diff --git a/hw/pci/pcie.c b/hw/pci/pcie.c index 08718188bb..0eb3a2a5d2 100644 --- a/hw/pci/pcie.c +++ b/hw/pci/pcie.c @@ -495,7 +495,7 @@ void pcie_cap_slot_unplug_request_cb(HotplugHandler *hotplug_dev, /* pci express slot for pci express root/downstream port PCI express capability slot registers */ -void pcie_cap_slot_init(PCIDevice *dev, uint16_t slot) +void pcie_cap_slot_init(PCIDevice *dev, PCIESlot *s) { uint32_t pos = dev->exp.exp_cap; @@ -505,13 +505,16 @@ void pcie_cap_slot_init(PCIDevice *dev, uint16_t slot) pci_long_test_and_clear_mask(dev->config + pos + PCI_EXP_SLTCAP, ~PCI_EXP_SLTCAP_PSN); pci_long_test_and_set_mask(dev->config + pos + PCI_EXP_SLTCAP, - (slot << PCI_EXP_SLTCAP_PSN_SHIFT) | + (s->slot << PCI_EXP_SLTCAP_PSN_SHIFT) | PCI_EXP_SLTCAP_EIP | - PCI_EXP_SLTCAP_HPS | - PCI_EXP_SLTCAP_HPC | PCI_EXP_SLTCAP_PIP | PCI_EXP_SLTCAP_AIP | PCI_EXP_SLTCAP_ABP); + if (s->hotplug) { + pci_long_test_and_set_mask(dev->config + pos + PCI_EXP_SLTCAP, + PCI_EXP_SLTCAP_HPS | + PCI_EXP_SLTCAP_HPC); + } if (dev->cap_present & QEMU_PCIE_SLTCAP_PCP) { pci_long_test_and_set_mask(dev->config + pos + PCI_EXP_SLTCAP, diff --git a/hw/pci/pcie_port.c b/hw/pci/pcie_port.c index c19a9be592..eb563ad435 100644 --- a/hw/pci/pcie_port.c +++ b/hw/pci/pcie_port.c @@ -133,7 +133,7 @@ static void pcie_port_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); - dc->props = pcie_port_props; + device_class_set_props(dc, pcie_port_props); } static const TypeInfo pcie_port_type_info = { @@ -147,6 +147,7 @@ static const TypeInfo pcie_port_type_info = { static Property pcie_slot_props[] = { DEFINE_PROP_UINT8("chassis", PCIESlot, chassis, 0), DEFINE_PROP_UINT16("slot", PCIESlot, slot, 0), + DEFINE_PROP_BOOL("hotplug", PCIESlot, hotplug, true), DEFINE_PROP_END_OF_LIST() }; @@ -155,7 +156,7 @@ static void pcie_slot_class_init(ObjectClass *oc, void *data) DeviceClass *dc = DEVICE_CLASS(oc); HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc); - dc->props = pcie_slot_props; + device_class_set_props(dc, pcie_slot_props); hc->pre_plug = pcie_cap_slot_pre_plug_cb; hc->plug = pcie_cap_slot_plug_cb; hc->unplug = pcie_cap_slot_unplug_cb; diff --git a/hw/pcmcia/Makefile.objs b/hw/pcmcia/Makefile.objs index 4eac060c93..02cd986a2c 100644 --- a/hw/pcmcia/Makefile.objs +++ b/hw/pcmcia/Makefile.objs @@ -1,2 +1,2 @@ common-obj-y += pcmcia.o -obj-$(CONFIG_PXA2XX) += pxa2xx.o +common-obj-$(CONFIG_PXA2XX) += pxa2xx.o diff --git a/hw/ppc/Kconfig b/hw/ppc/Kconfig index f927ec9c74..dd86e664d2 100644 --- a/hw/ppc/Kconfig +++ b/hw/ppc/Kconfig @@ -10,6 +10,8 @@ config PSERIES select XICS_SPAPR select XIVE_SPAPR select MSI_NONBROKEN + select FDT_PPC + select CHRP_NVRAM config SPAPR_RNG bool @@ -26,6 +28,9 @@ config POWERNV select MC146818RTC select XICS select XIVE + select FDT_PPC + select PCI_EXPRESS + select MSI_NONBROKEN config PPC405 bool @@ -42,6 +47,7 @@ config PPC440 select PCI_EXPRESS select PPC4XX select SERIAL + select FDT_PPC config PPC4XX bool @@ -50,6 +56,7 @@ config PPC4XX config SAM460EX bool + select PPC405 select PFLASH_CFI01 select IDE_SII3112 select M41T80 @@ -59,6 +66,7 @@ config SAM460EX select SMBUS_EEPROM select USB_EHCI_SYSBUS select USB_OHCI + select FDT_PPC config PREP bool @@ -71,6 +79,7 @@ config PREP select M48T59 select PC87312 select RS6000_MC + select FW_CFG_PPC config RS6000_MC bool @@ -84,6 +93,7 @@ config MAC_OLDWORLD select GRACKLE_PCI select HEATHROW_PIC select MACIO + select FW_CFG_PPC config MAC_NEWWORLD bool @@ -95,6 +105,7 @@ config MAC_NEWWORLD select MACIO_GPIO select MAC_PMU select UNIN_PCI + select FW_CFG_PPC config E500 bool @@ -105,13 +116,17 @@ config E500 select PLATFORM_BUS select PPCE500_PCI select SERIAL + select MPC_I2C + select FDT_PPC config VIRTEX bool + select PPC4XX select PFLASH_CFI01 select SERIAL select XILINX select XILINX_ETHLITE + select FDT_PPC config XIVE bool @@ -127,3 +142,10 @@ config XIVE_KVM bool default y depends on XIVE_SPAPR && KVM + +# Only used by 64-bit targets +config FW_CFG_PPC + bool + +config FDT_PPC + bool diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs index 580bb4f0dd..c3d3cc56eb 100644 --- a/hw/ppc/Makefile.objs +++ b/hw/ppc/Makefile.objs @@ -1,18 +1,19 @@ # shared objects -obj-y += ppc.o ppc_booke.o fdt.o fw_cfg.o +obj-y += ppc.o ppc_booke.o +obj-$(CONFIG_FDT_PPC) += fdt.o +obj-$(CONFIG_FW_CFG_PPC) += fw_cfg.o # IBM pSeries (sPAPR) obj-$(CONFIG_PSERIES) += spapr.o spapr_caps.o spapr_vio.o spapr_events.o obj-$(CONFIG_PSERIES) += spapr_hcall.o spapr_iommu.o spapr_rtas.o obj-$(CONFIG_PSERIES) += spapr_pci.o spapr_rtc.o spapr_drc.o obj-$(CONFIG_PSERIES) += spapr_cpu_core.o spapr_ovec.o spapr_irq.o -obj-$(CONFIG_PSERIES) += spapr_tpm_proxy.o +obj-$(CONFIG_PSERIES) += spapr_tpm_proxy.o spapr_nvdimm.o obj-$(CONFIG_SPAPR_RNG) += spapr_rng.o +obj-$(call land,$(CONFIG_PSERIES),$(CONFIG_LINUX)) += spapr_pci_vfio.o spapr_pci_nvlink2.o # IBM PowerNV obj-$(CONFIG_POWERNV) += pnv.o pnv_xscom.o pnv_core.o pnv_lpc.o pnv_psi.o pnv_occ.o pnv_bmc.o -obj-$(CONFIG_POWERNV) += pnv_homer.o -ifeq ($(CONFIG_PCI)$(CONFIG_PSERIES)$(CONFIG_LINUX), yyy) -obj-y += spapr_pci_vfio.o spapr_pci_nvlink2.o -endif +obj-$(CONFIG_POWERNV) += pnv_homer.o pnv_pnor.o + obj-$(CONFIG_PSERIES) += spapr_rtas_ddw.o # PowerPC 4xx boards obj-$(CONFIG_PPC405) += ppc405_boards.o ppc405_uc.o diff --git a/hw/ppc/e500.c b/hw/ppc/e500.c index 91cd4c26f9..0d1f41197c 100644 --- a/hw/ppc/e500.c +++ b/hw/ppc/e500.c @@ -594,6 +594,7 @@ done: cpu_physical_memory_write(addr, fdt, fdt_size); } ret = fdt_size; + g_free(fdt); out: g_free(pci_map); @@ -793,7 +794,6 @@ static DeviceState *ppce500_init_mpic(PPCE500MachineState *pms, MemoryRegion *ccsr, IrqLines *irqs) { - MachineState *machine = MACHINE(pms); const PPCE500MachineClass *pmc = PPCE500_MACHINE_GET_CLASS(pms); DeviceState *dev = NULL; SysBusDevice *s; @@ -801,10 +801,10 @@ static DeviceState *ppce500_init_mpic(PPCE500MachineState *pms, if (kvm_enabled()) { Error *err = NULL; - if (machine_kernel_irqchip_allowed(machine)) { + if (kvm_kernel_irqchip_allowed()) { dev = ppce500_init_mpic_kvm(pmc, irqs, &err); } - if (machine_kernel_irqchip_required(machine) && !dev) { + if (kvm_kernel_irqchip_required() && !dev) { error_reportf_err(err, "kernel_irqchip requested but unavailable: "); exit(1); @@ -832,7 +832,6 @@ static void ppce500_power_off(void *opaque, int line, int on) void ppce500_init(MachineState *machine) { MemoryRegion *address_space_mem = get_system_memory(); - MemoryRegion *ram = g_new(MemoryRegion, 1); PPCE500MachineState *pms = PPCE500_MACHINE(machine); const PPCE500MachineClass *pmc = PPCE500_MACHINE_GET_CLASS(machine); PCIBus *pci_bus; @@ -907,13 +906,13 @@ void ppce500_init(MachineState *machine) env = firstenv; - /* Fixup Memory size on a alignment boundary */ - ram_size &= ~(RAM_SIZES_ALIGN - 1); - machine->ram_size = ram_size; + if (!QEMU_IS_ALIGNED(machine->ram_size, RAM_SIZES_ALIGN)) { + error_report("RAM size must be multiple of %" PRIu64, RAM_SIZES_ALIGN); + exit(EXIT_FAILURE); + } /* Register Memory */ - memory_region_allocate_system_memory(ram, NULL, "mpc8544ds.ram", ram_size); - memory_region_add_subregion(address_space_mem, 0, ram); + memory_region_add_subregion(address_space_mem, 0, machine->ram); dev = qdev_create(NULL, "e500-ccsr"); object_property_add_child(qdev_get_machine(), "e500-ccsr", @@ -1048,9 +1047,13 @@ void ppce500_init(MachineState *machine) } filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, payload_name); + if (!filename) { + error_report("could not find firmware/kernel file '%s'", payload_name); + exit(1); + } payload_size = load_elf(filename, NULL, NULL, NULL, - &bios_entry, &loadaddr, NULL, + &bios_entry, &loadaddr, NULL, NULL, 1, PPC_ELF_MACHINE, 0, 0); if (payload_size < 0) { /* @@ -1084,7 +1087,7 @@ void ppce500_init(MachineState *machine) kernel_base = cur_base; kernel_size = load_image_targphys(machine->kernel_filename, cur_base, - ram_size - cur_base); + machine->ram_size - cur_base); if (kernel_size < 0) { error_report("could not load kernel '%s'", machine->kernel_filename); @@ -1098,7 +1101,7 @@ void ppce500_init(MachineState *machine) if (machine->initrd_filename) { initrd_base = (cur_base + INITRD_LOAD_PAD) & ~INITRD_PAD_MASK; initrd_size = load_image_targphys(machine->initrd_filename, initrd_base, - ram_size - initrd_base); + machine->ram_size - initrd_base); if (initrd_size < 0) { error_report("could not load initial ram disk '%s'", @@ -1116,7 +1119,7 @@ void ppce500_init(MachineState *machine) * ensures enough space between kernel and initrd. */ dt_base = (loadaddr + payload_size + DTC_LOAD_PAD) & ~DTC_PAD_MASK; - if (dt_base + DTB_MAX_SIZE > ram_size) { + if (dt_base + DTB_MAX_SIZE > machine->ram_size) { error_report("not enough memory for device tree"); exit(1); } diff --git a/hw/ppc/e500plat.c b/hw/ppc/e500plat.c index 7078386300..bddd5e7c48 100644 --- a/hw/ppc/e500plat.c +++ b/hw/ppc/e500plat.c @@ -97,6 +97,7 @@ static void e500plat_machine_class_init(ObjectClass *oc, void *data) mc->init = e500plat_init; mc->max_cpus = 32; mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("e500v2_v30"); + mc->default_ram_id = "mpc8544ds.ram"; machine_class_allow_dynamic_sysbus_dev(mc, TYPE_ETSEC_COMMON); } diff --git a/hw/ppc/mac_newworld.c b/hw/ppc/mac_newworld.c index 3594517f0c..428cf63578 100644 --- a/hw/ppc/mac_newworld.c +++ b/hw/ppc/mac_newworld.c @@ -62,7 +62,6 @@ #include "hw/char/escc.h" #include "hw/misc/macio/macio.h" #include "hw/ppc/openpic.h" -#include "hw/ide.h" #include "hw/loader.h" #include "hw/fw-path-provider.h" #include "elf.h" @@ -118,7 +117,7 @@ static void ppc_core99_init(MachineState *machine) char *filename; IrqLines *openpic_irqs; int linux_boot, i, j, k; - MemoryRegion *ram = g_new(MemoryRegion, 1), *bios = g_new(MemoryRegion, 1); + MemoryRegion *bios = g_new(MemoryRegion, 1); hwaddr kernel_base, initrd_base, cmdline_base = 0; long kernel_size, initrd_size; UNINHostState *uninorth_pci; @@ -152,23 +151,21 @@ static void ppc_core99_init(MachineState *machine) } /* allocate RAM */ - memory_region_allocate_system_memory(ram, NULL, "ppc_core99.ram", ram_size); - memory_region_add_subregion(get_system_memory(), 0, ram); + memory_region_add_subregion(get_system_memory(), 0, machine->ram); /* allocate and load BIOS */ - memory_region_init_ram(bios, NULL, "ppc_core99.bios", BIOS_SIZE, + memory_region_init_rom(bios, NULL, "ppc_core99.bios", BIOS_SIZE, &error_fatal); if (bios_name == NULL) bios_name = PROM_FILENAME; filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); - memory_region_set_readonly(bios, true); memory_region_add_subregion(get_system_memory(), PROM_ADDR, bios); /* Load OpenBIOS (ELF) */ if (filename) { bios_size = load_elf(filename, NULL, NULL, NULL, NULL, - NULL, NULL, 1, PPC_ELF_MACHINE, 0, 0); + NULL, NULL, NULL, 1, PPC_ELF_MACHINE, 0, 0); g_free(filename); } else { @@ -192,7 +189,7 @@ static void ppc_core99_init(MachineState *machine) kernel_size = load_elf(kernel_filename, NULL, translate_kernel_address, NULL, - NULL, &lowaddr, NULL, 1, PPC_ELF_MACHINE, + NULL, &lowaddr, NULL, NULL, 1, PPC_ELF_MACHINE, 0, 0); if (kernel_size < 0) kernel_size = load_aout(kernel_filename, kernel_base, @@ -586,6 +583,7 @@ static void core99_machine_class_init(ObjectClass *oc, void *data) #else mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("7400_v2.9"); #endif + mc->default_ram_id = "ppc_core99.ram"; mc->ignore_boot_device_suffixes = true; fwc->get_dev_path = core99_fw_dev_path; } diff --git a/hw/ppc/mac_oldworld.c b/hw/ppc/mac_oldworld.c index 0fa680b749..101bdc5c4d 100644 --- a/hw/ppc/mac_oldworld.c +++ b/hw/ppc/mac_oldworld.c @@ -41,7 +41,6 @@ #include "hw/nvram/fw_cfg.h" #include "hw/char/escc.h" #include "hw/misc/macio/macio.h" -#include "hw/ide.h" #include "hw/loader.h" #include "hw/fw-path-provider.h" #include "elf.h" @@ -91,7 +90,6 @@ static void ppc_heathrow_init(MachineState *machine) CPUPPCState *env = NULL; char *filename; int linux_boot, i; - MemoryRegion *ram = g_new(MemoryRegion, 1); MemoryRegion *bios = g_new(MemoryRegion, 1); uint32_t kernel_base, initrd_base, cmdline_base = 0; int32_t kernel_size, initrd_size; @@ -127,23 +125,20 @@ static void ppc_heathrow_init(MachineState *machine) exit(1); } - memory_region_allocate_system_memory(ram, NULL, "ppc_heathrow.ram", - ram_size); - memory_region_add_subregion(sysmem, 0, ram); + memory_region_add_subregion(sysmem, 0, machine->ram); /* allocate and load BIOS */ - memory_region_init_ram(bios, NULL, "ppc_heathrow.bios", BIOS_SIZE, + memory_region_init_rom(bios, NULL, "ppc_heathrow.bios", BIOS_SIZE, &error_fatal); if (bios_name == NULL) bios_name = PROM_FILENAME; filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); - memory_region_set_readonly(bios, true); memory_region_add_subregion(sysmem, PROM_ADDR, bios); /* Load OpenBIOS (ELF) */ if (filename) { - bios_size = load_elf(filename, NULL, 0, NULL, NULL, NULL, NULL, + bios_size = load_elf(filename, NULL, 0, NULL, NULL, NULL, NULL, NULL, 1, PPC_ELF_MACHINE, 0, 0); g_free(filename); } else { @@ -166,7 +161,7 @@ static void ppc_heathrow_init(MachineState *machine) kernel_base = KERNEL_LOAD_ADDR; kernel_size = load_elf(kernel_filename, NULL, translate_kernel_address, NULL, - NULL, &lowaddr, NULL, 1, PPC_ELF_MACHINE, + NULL, &lowaddr, NULL, NULL, 1, PPC_ELF_MACHINE, 0, 0); if (kernel_size < 0) kernel_size = load_aout(kernel_filename, kernel_base, @@ -438,7 +433,7 @@ static void heathrow_class_init(ObjectClass *oc, void *data) mc->block_default_type = IF_IDE; mc->max_cpus = MAX_CPUS; #ifndef TARGET_PPC64 - mc->is_default = 1; + mc->is_default = true; #endif /* TOFIX "cad" when Mac floppy is implemented */ mc->default_boot_order = "cd"; @@ -446,6 +441,7 @@ static void heathrow_class_init(ObjectClass *oc, void *data) mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("750_v3.1"); mc->default_display = "std"; mc->ignore_boot_device_suffixes = true; + mc->default_ram_id = "ppc_heathrow.ram"; fwc->get_dev_path = heathrow_fw_dev_path; } diff --git a/hw/ppc/mpc8544ds.c b/hw/ppc/mpc8544ds.c index c2c5e11fa1..81177505f0 100644 --- a/hw/ppc/mpc8544ds.c +++ b/hw/ppc/mpc8544ds.c @@ -55,6 +55,7 @@ static void e500plat_machine_class_init(ObjectClass *oc, void *data) mc->init = mpc8544ds_init; mc->max_cpus = 15; mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("e500v2_v30"); + mc->default_ram_id = "mpc8544ds.ram"; } #define TYPE_MPC8544DS_MACHINE MACHINE_TYPE_NAME("mpc8544ds") diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index 627c08e5b9..c9cb6fa357 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -40,10 +40,12 @@ #include "hw/intc/intc.h" #include "hw/ipmi/ipmi.h" #include "target/ppc/mmu-hash64.h" +#include "hw/pci/msi.h" #include "hw/ppc/xics.h" #include "hw/qdev-properties.h" #include "hw/ppc/pnv_xscom.h" +#include "hw/ppc/pnv_pnor.h" #include "hw/isa/isa.h" #include "hw/boards.h" @@ -279,14 +281,16 @@ static void pnv_dt_icp(PnvChip *chip, void *fdt, uint32_t pir, static void pnv_chip_power8_dt_populate(PnvChip *chip, void *fdt) { - const char *typename = pnv_chip_core_typename(chip); - size_t typesize = object_type_get_instance_size(typename); + static const char compat[] = "ibm,power8-xscom\0ibm,xscom"; int i; - pnv_dt_xscom(chip, fdt, 0); + pnv_dt_xscom(chip, fdt, 0, + cpu_to_be64(PNV_XSCOM_BASE(chip)), + cpu_to_be64(PNV_XSCOM_SIZE), + compat, sizeof(compat)); for (i = 0; i < chip->nr_cores; i++) { - PnvCore *pnv_core = PNV_CORE(chip->cores + i * typesize); + PnvCore *pnv_core = chip->cores[i]; pnv_dt_core(chip, pnv_core, fdt); @@ -301,14 +305,16 @@ static void pnv_chip_power8_dt_populate(PnvChip *chip, void *fdt) static void pnv_chip_power9_dt_populate(PnvChip *chip, void *fdt) { - const char *typename = pnv_chip_core_typename(chip); - size_t typesize = object_type_get_instance_size(typename); + static const char compat[] = "ibm,power9-xscom\0ibm,xscom"; int i; - pnv_dt_xscom(chip, fdt, 0); + pnv_dt_xscom(chip, fdt, 0, + cpu_to_be64(PNV9_XSCOM_BASE(chip)), + cpu_to_be64(PNV9_XSCOM_SIZE), + compat, sizeof(compat)); for (i = 0; i < chip->nr_cores; i++) { - PnvCore *pnv_core = PNV_CORE(chip->cores + i * typesize); + PnvCore *pnv_core = chip->cores[i]; pnv_dt_core(chip, pnv_core, fdt); } @@ -317,7 +323,30 @@ static void pnv_chip_power9_dt_populate(PnvChip *chip, void *fdt) pnv_dt_memory(fdt, chip->chip_id, chip->ram_start, chip->ram_size); } - pnv_dt_lpc(chip, fdt, 0); + pnv_dt_lpc(chip, fdt, 0, PNV9_LPCM_BASE(chip), PNV9_LPCM_SIZE); +} + +static void pnv_chip_power10_dt_populate(PnvChip *chip, void *fdt) +{ + static const char compat[] = "ibm,power10-xscom\0ibm,xscom"; + int i; + + pnv_dt_xscom(chip, fdt, 0, + cpu_to_be64(PNV10_XSCOM_BASE(chip)), + cpu_to_be64(PNV10_XSCOM_SIZE), + compat, sizeof(compat)); + + for (i = 0; i < chip->nr_cores; i++) { + PnvCore *pnv_core = chip->cores[i]; + + pnv_dt_core(chip, pnv_core, fdt); + } + + if (chip->ram_size) { + pnv_dt_memory(fdt, chip->chip_id, chip->ram_start, chip->ram_size); + } + + pnv_dt_lpc(chip, fdt, 0, PNV10_LPCM_BASE(chip), PNV10_LPCM_SIZE); } static void pnv_dt_rtc(ISADevice *d, void *fdt, int lpc_off) @@ -456,7 +485,7 @@ static void pnv_dt_isa(PnvMachineState *pnv, void *fdt) &args); } -static void pnv_dt_power_mgt(void *fdt) +static void pnv_dt_power_mgt(PnvMachineState *pnv, void *fdt) { int off; @@ -468,8 +497,7 @@ static void pnv_dt_power_mgt(void *fdt) static void *pnv_dt_create(MachineState *machine) { - const char plat_compat8[] = "qemu,powernv8\0qemu,powernv\0ibm,powernv"; - const char plat_compat9[] = "qemu,powernv9\0ibm,powernv"; + PnvMachineClass *pmc = PNV_MACHINE_GET_CLASS(machine); PnvMachineState *pnv = PNV_MACHINE(machine); void *fdt; char *buf; @@ -479,19 +507,15 @@ static void *pnv_dt_create(MachineState *machine) fdt = g_malloc0(FDT_MAX_SIZE); _FDT((fdt_create_empty_tree(fdt, FDT_MAX_SIZE))); + /* /qemu node */ + _FDT((fdt_add_subnode(fdt, 0, "qemu"))); + /* Root node */ _FDT((fdt_setprop_cell(fdt, 0, "#address-cells", 0x2))); _FDT((fdt_setprop_cell(fdt, 0, "#size-cells", 0x2))); _FDT((fdt_setprop_string(fdt, 0, "model", "IBM PowerNV (emulated by qemu)"))); - if (pnv_is_power9(pnv)) { - _FDT((fdt_setprop(fdt, 0, "compatible", plat_compat9, - sizeof(plat_compat9)))); - } else { - _FDT((fdt_setprop(fdt, 0, "compatible", plat_compat8, - sizeof(plat_compat8)))); - } - + _FDT((fdt_setprop(fdt, 0, "compatible", pmc->compat, pmc->compat_size))); buf = qemu_uuid_unparse_strdup(&qemu_uuid); _FDT((fdt_setprop_string(fdt, 0, "vm,uuid", buf))); @@ -528,9 +552,9 @@ static void *pnv_dt_create(MachineState *machine) pnv_dt_bmc_sensors(pnv->bmc, fdt); } - /* Create an extra node for power management on Power9 */ - if (pnv_is_power9(pnv)) { - pnv_dt_power_mgt(fdt); + /* Create an extra node for power management on machines that support it */ + if (pmc->dt_power_mgt) { + pmc->dt_power_mgt(pnv, fdt); } return fdt; @@ -538,7 +562,7 @@ static void *pnv_dt_create(MachineState *machine) static void pnv_powerdown_notify(Notifier *n, void *opaque) { - PnvMachineState *pnv = PNV_MACHINE(qdev_get_machine()); + PnvMachineState *pnv = container_of(n, PnvMachineState, powerdown_notifier); if (pnv->bmc) { pnv_bmc_powerdown(pnv->bmc); @@ -548,23 +572,26 @@ static void pnv_powerdown_notify(Notifier *n, void *opaque) static void pnv_reset(MachineState *machine) { PnvMachineState *pnv = PNV_MACHINE(machine); + IPMIBmc *bmc; void *fdt; - Object *obj; qemu_devices_reset(); /* - * OpenPOWER systems have a BMC, which can be defined on the - * command line with: - * - * -device ipmi-bmc-sim,id=bmc0 - * - * This is the internal simulator but it could also be an external - * BMC. + * The machine should provide by default an internal BMC simulator. + * If not, try to use the BMC device that was provided on the command + * line. */ - obj = object_resolve_path_type("", "ipmi-bmc-sim", NULL); - if (obj) { - pnv->bmc = IPMI_BMC(obj); + bmc = pnv_bmc_find(&error_fatal); + if (!pnv->bmc) { + if (!bmc) { + warn_report("machine has no BMC device. Use '-device " + "ipmi-bmc-sim,id=bmc0 -device isa-ipmi-bt,bmc=bmc0,irq=10' " + "to define one"); + } else { + pnv_bmc_set_pnor(bmc, pnv->pnor); + pnv->bmc = bmc; + } } fdt = pnv_dt_create(machine); @@ -574,6 +601,8 @@ static void pnv_reset(MachineState *machine) qemu_fdt_dumpdtb(fdt, fdt_totalsize(fdt)); cpu_physical_memory_write(PNV_FDT_ADDR, fdt, fdt_totalsize(fdt)); + + g_free(fdt); } static ISABus *pnv_chip_power8_isa_create(PnvChip *chip, Error **errp) @@ -594,6 +623,12 @@ static ISABus *pnv_chip_power9_isa_create(PnvChip *chip, Error **errp) return pnv_lpc_isa_create(&chip9->lpc, false, errp); } +static ISABus *pnv_chip_power10_isa_create(PnvChip *chip, Error **errp) +{ + Pnv10Chip *chip10 = PNV10_CHIP(chip); + return pnv_lpc_isa_create(&chip10->lpc, false, errp); +} + static ISABus *pnv_isa_create(PnvChip *chip, Error **errp) { return PNV_CHIP_GET_CLASS(chip)->isa_create(chip, errp); @@ -602,16 +637,47 @@ static ISABus *pnv_isa_create(PnvChip *chip, Error **errp) static void pnv_chip_power8_pic_print_info(PnvChip *chip, Monitor *mon) { Pnv8Chip *chip8 = PNV8_CHIP(chip); + int i; ics_pic_print_info(&chip8->psi.ics, mon); + for (i = 0; i < chip->num_phbs; i++) { + pnv_phb3_msi_pic_print_info(&chip8->phbs[i].msis, mon); + ics_pic_print_info(&chip8->phbs[i].lsis, mon); + } } static void pnv_chip_power9_pic_print_info(PnvChip *chip, Monitor *mon) { Pnv9Chip *chip9 = PNV9_CHIP(chip); + int i, j; pnv_xive_pic_print_info(&chip9->xive, mon); pnv_psi_pic_print_info(&chip9->psi, mon); + + for (i = 0; i < PNV9_CHIP_MAX_PEC; i++) { + PnvPhb4PecState *pec = &chip9->pecs[i]; + for (j = 0; j < pec->num_stacks; j++) { + pnv_phb4_pic_print_info(&pec->stacks[j].phb, mon); + } + } +} + +static uint64_t pnv_chip_power8_xscom_core_base(PnvChip *chip, + uint32_t core_id) +{ + return PNV_XSCOM_EX_BASE(core_id); +} + +static uint64_t pnv_chip_power9_xscom_core_base(PnvChip *chip, + uint32_t core_id) +{ + return PNV9_XSCOM_EC_BASE(core_id); +} + +static uint64_t pnv_chip_power10_xscom_core_base(PnvChip *chip, + uint32_t core_id) +{ + return PNV10_XSCOM_EC_BASE(core_id); } static bool pnv_match_cpu(const char *default_type, const char *cpu_type) @@ -624,25 +690,50 @@ static bool pnv_match_cpu(const char *default_type, const char *cpu_type) return ppc_default->pvr_match(ppc_default, ppc->pvr); } +static void pnv_ipmi_bt_init(ISABus *bus, IPMIBmc *bmc, uint32_t irq) +{ + Object *obj; + + obj = OBJECT(isa_create(bus, "isa-ipmi-bt")); + object_property_set_link(obj, OBJECT(bmc), "bmc", &error_fatal); + object_property_set_int(obj, irq, "irq", &error_fatal); + object_property_set_bool(obj, true, "realized", &error_fatal); +} + +static void pnv_chip_power10_pic_print_info(PnvChip *chip, Monitor *mon) +{ + Pnv10Chip *chip10 = PNV10_CHIP(chip); + + pnv_psi_pic_print_info(&chip10->psi, mon); +} + static void pnv_init(MachineState *machine) { PnvMachineState *pnv = PNV_MACHINE(machine); MachineClass *mc = MACHINE_GET_CLASS(machine); - MemoryRegion *ram; char *fw_filename; long fw_size; int i; char *chip_typename; + DriveInfo *pnor = drive_get(IF_MTD, 0, 0); + DeviceState *dev; /* allocate RAM */ if (machine->ram_size < (1 * GiB)) { warn_report("skiboot may not work with < 1GB of RAM"); } + memory_region_add_subregion(get_system_memory(), 0, machine->ram); - ram = g_new(MemoryRegion, 1); - memory_region_allocate_system_memory(ram, NULL, "pnv.ram", - machine->ram_size); - memory_region_add_subregion(get_system_memory(), 0, ram); + /* + * Create our simple PNOR device + */ + dev = qdev_create(NULL, TYPE_PNV_PNOR); + if (pnor) { + qdev_prop_set_drive(dev, "drive", blk_by_legacy_dinfo(pnor), + &error_abort); + } + qdev_init_nofail(dev); + pnv->pnor = PNV_PNOR(dev); /* load skiboot firmware */ if (bios_name == NULL) { @@ -655,7 +746,7 @@ static void pnv_init(MachineState *machine) exit(1); } - fw_size = load_image_targphys(fw_filename, FW_LOAD_ADDR, FW_MAX_SIZE); + fw_size = load_image_targphys(fw_filename, pnv->fw_load_addr, FW_MAX_SIZE); if (fw_size < 0) { error_report("Could not load OPAL firmware '%s'", fw_filename); exit(1); @@ -687,6 +778,9 @@ static void pnv_init(MachineState *machine) } } + /* MSIs are supported on this platform */ + msi_nonbroken = true; + /* * Check compatibility of the specified CPU with the machine * default. @@ -707,6 +801,18 @@ static void pnv_init(MachineState *machine) exit(1); } + pnv->num_chips = + machine->smp.max_cpus / (machine->smp.cores * machine->smp.threads); + /* + * TODO: should we decide on how many chips we can create based + * on #cores and Venice vs. Murano vs. Naples chip type etc..., + */ + if (!is_power_of_2(pnv->num_chips) || pnv->num_chips > 4) { + error_report("invalid number of chips: '%d'", pnv->num_chips); + error_printf("Try '-smp sockets=N'. Valid values are : 1, 2 or 4.\n"); + exit(1); + } + pnv->chips = g_new0(PnvChip *, pnv->num_chips); for (i = 0; i < pnv->num_chips; i++) { char chip_name[32]; @@ -729,6 +835,19 @@ static void pnv_init(MachineState *machine) &error_fatal); object_property_set_int(chip, machine->smp.cores, "nr-cores", &error_fatal); + object_property_set_int(chip, machine->smp.threads, + "nr-threads", &error_fatal); + /* + * The POWER8 machine use the XICS interrupt interface. + * Propagate the XICS fabric to the chip and its controllers. + */ + if (object_dynamic_cast(OBJECT(pnv), TYPE_XICS_FABRIC)) { + object_property_set_link(chip, OBJECT(pnv), "xics", &error_abort); + } + if (object_dynamic_cast(OBJECT(pnv), TYPE_XIVE_FABRIC)) { + object_property_set_link(chip, OBJECT(pnv), "xive-fabric", + &error_abort); + } object_property_set_bool(chip, true, "realized", &error_fatal); } g_free(chip_typename); @@ -742,6 +861,15 @@ static void pnv_init(MachineState *machine) /* Create an RTC ISA device too */ mc146818_rtc_init(pnv->isa_bus, 2000, NULL); + /* + * Create the machine BMC simulator and the IPMI BT device for + * communication with the BMC + */ + if (defaults_enabled()) { + pnv->bmc = pnv_bmc_create(pnv->pnor); + pnv_ipmi_bt_init(pnv->isa_bus, pnv->bmc, 10); + } + /* * OpenPOWER systems use a IPMI SEL Event message to notify the * host to powerdown @@ -764,12 +892,12 @@ static uint32_t pnv_chip_core_pir_p8(PnvChip *chip, uint32_t core_id) static void pnv_chip_power8_intc_create(PnvChip *chip, PowerPCCPU *cpu, Error **errp) { + Pnv8Chip *chip8 = PNV8_CHIP(chip); Error *local_err = NULL; Object *obj; PnvCPUState *pnv_cpu = pnv_cpu_state(cpu); - obj = icp_create(OBJECT(cpu), TYPE_PNV_ICP, XICS_FABRIC(qdev_get_machine()), - &local_err); + obj = icp_create(OBJECT(cpu), TYPE_PNV_ICP, chip8->xics, &local_err); if (local_err) { error_propagate(errp, local_err); return; @@ -794,6 +922,12 @@ static void pnv_chip_power8_intc_destroy(PnvChip *chip, PowerPCCPU *cpu) pnv_cpu->intc = NULL; } +static void pnv_chip_power8_intc_print_info(PnvChip *chip, PowerPCCPU *cpu, + Monitor *mon) +{ + icp_pic_print_info(ICP(pnv_cpu_state(cpu)->intc), mon); +} + /* * 0:48 Reserved - Read as zeroes * 49:52 Node ID @@ -809,6 +943,11 @@ static uint32_t pnv_chip_core_pir_p9(PnvChip *chip, uint32_t core_id) return (chip->chip_id << 8) | (core_id << 2); } +static uint32_t pnv_chip_core_pir_p10(PnvChip *chip, uint32_t core_id) +{ + return (chip->chip_id << 8) | (core_id << 2); +} + static void pnv_chip_power9_intc_create(PnvChip *chip, PowerPCCPU *cpu, Error **errp) { @@ -822,7 +961,8 @@ static void pnv_chip_power9_intc_create(PnvChip *chip, PowerPCCPU *cpu, * controller object is initialized afterwards. Hopefully, it's * only used at runtime. */ - obj = xive_tctx_create(OBJECT(cpu), XIVE_ROUTER(&chip9->xive), &local_err); + obj = xive_tctx_create(OBJECT(cpu), XIVE_PRESENTER(&chip9->xive), + &local_err); if (local_err) { error_propagate(errp, local_err); return; @@ -846,6 +986,38 @@ static void pnv_chip_power9_intc_destroy(PnvChip *chip, PowerPCCPU *cpu) pnv_cpu->intc = NULL; } +static void pnv_chip_power9_intc_print_info(PnvChip *chip, PowerPCCPU *cpu, + Monitor *mon) +{ + xive_tctx_pic_print_info(XIVE_TCTX(pnv_cpu_state(cpu)->intc), mon); +} + +static void pnv_chip_power10_intc_create(PnvChip *chip, PowerPCCPU *cpu, + Error **errp) +{ + PnvCPUState *pnv_cpu = pnv_cpu_state(cpu); + + /* Will be defined when the interrupt controller is */ + pnv_cpu->intc = NULL; +} + +static void pnv_chip_power10_intc_reset(PnvChip *chip, PowerPCCPU *cpu) +{ + ; +} + +static void pnv_chip_power10_intc_destroy(PnvChip *chip, PowerPCCPU *cpu) +{ + PnvCPUState *pnv_cpu = pnv_cpu_state(cpu); + + pnv_cpu->intc = NULL; +} + +static void pnv_chip_power10_intc_print_info(PnvChip *chip, PowerPCCPU *cpu, + Monitor *mon) +{ +} + /* * Allowed core identifiers on a POWER8 Processor Chip : * @@ -873,40 +1045,52 @@ static void pnv_chip_power9_intc_destroy(PnvChip *chip, PowerPCCPU *cpu) */ #define POWER9_CORE_MASK (0xffffffffffffffull) + +#define POWER10_CORE_MASK (0xffffffffffffffull) + static void pnv_chip_power8_instance_init(Object *obj) { + PnvChip *chip = PNV_CHIP(obj); Pnv8Chip *chip8 = PNV8_CHIP(obj); + PnvChipClass *pcc = PNV_CHIP_GET_CLASS(obj); + int i; + + object_property_add_link(obj, "xics", TYPE_XICS_FABRIC, + (Object **)&chip8->xics, + object_property_allow_set_link, + OBJ_PROP_LINK_STRONG, + &error_abort); object_initialize_child(obj, "psi", &chip8->psi, sizeof(chip8->psi), TYPE_PNV8_PSI, &error_abort, NULL); - object_property_add_const_link(OBJECT(&chip8->psi), "xics", - OBJECT(qdev_get_machine()), &error_abort); object_initialize_child(obj, "lpc", &chip8->lpc, sizeof(chip8->lpc), TYPE_PNV8_LPC, &error_abort, NULL); - object_property_add_const_link(OBJECT(&chip8->lpc), "psi", - OBJECT(&chip8->psi), &error_abort); object_initialize_child(obj, "occ", &chip8->occ, sizeof(chip8->occ), TYPE_PNV8_OCC, &error_abort, NULL); - object_property_add_const_link(OBJECT(&chip8->occ), "psi", - OBJECT(&chip8->psi), &error_abort); object_initialize_child(obj, "homer", &chip8->homer, sizeof(chip8->homer), TYPE_PNV8_HOMER, &error_abort, NULL); - object_property_add_const_link(OBJECT(&chip8->homer), "chip", obj, - &error_abort); + + for (i = 0; i < pcc->num_phbs; i++) { + object_initialize_child(obj, "phb[*]", &chip8->phbs[i], + sizeof(chip8->phbs[i]), TYPE_PNV_PHB3, + &error_abort, NULL); + } + + /* + * Number of PHBs is the chip default + */ + chip->num_phbs = pcc->num_phbs; } static void pnv_chip_icp_realize(Pnv8Chip *chip8, Error **errp) { PnvChip *chip = PNV_CHIP(chip8); PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip); - const char *typename = pnv_chip_core_typename(chip); - size_t typesize = object_type_get_instance_size(typename); int i, j; char *name; - XICSFabric *xi = XICS_FABRIC(qdev_get_machine()); name = g_strdup_printf("icp-%x", chip->chip_id); memory_region_init(&chip8->icp_mmio, OBJECT(chip), name, PNV_ICP_SIZE); @@ -917,12 +1101,12 @@ static void pnv_chip_icp_realize(Pnv8Chip *chip8, Error **errp) /* Map the ICP registers for each thread */ for (i = 0; i < chip->nr_cores; i++) { - PnvCore *pnv_core = PNV_CORE(chip->cores + i * typesize); + PnvCore *pnv_core = chip->cores[i]; int core_hwid = CPU_CORE(pnv_core)->core_id; for (j = 0; j < CPU_CORE(pnv_core)->nr_threads; j++) { uint32_t pir = pcc->core_pir(chip, core_hwid) + j; - PnvICPState *icp = PNV_ICP(xics_icp_get(xi, pir)); + PnvICPState *icp = PNV_ICP(xics_icp_get(chip8->xics, pir)); memory_region_add_subregion(&chip8->icp_mmio, pir << 12, &icp->mmio); @@ -937,6 +1121,9 @@ static void pnv_chip_power8_realize(DeviceState *dev, Error **errp) Pnv8Chip *chip8 = PNV8_CHIP(dev); Pnv8Psi *psi8 = &chip8->psi; Error *local_err = NULL; + int i; + + assert(chip8->xics); /* XSCOM bridge is first */ pnv_xscom_realize(chip, PNV_XSCOM_SIZE, &local_err); @@ -955,6 +1142,8 @@ static void pnv_chip_power8_realize(DeviceState *dev, Error **errp) /* Processor Service Interface (PSI) Host Bridge */ object_property_set_int(OBJECT(&chip8->psi), PNV_PSIHB_BASE(chip), "bar", &error_fatal); + object_property_set_link(OBJECT(&chip8->psi), OBJECT(chip8->xics), + ICS_PROP_XICS, &error_abort); object_property_set_bool(OBJECT(&chip8->psi), true, "realized", &local_err); if (local_err) { error_propagate(errp, local_err); @@ -964,6 +1153,8 @@ static void pnv_chip_power8_realize(DeviceState *dev, Error **errp) &PNV_PSI(psi8)->xscom_regs); /* Create LPC controller */ + object_property_set_link(OBJECT(&chip8->lpc), OBJECT(&chip8->psi), "psi", + &error_abort); object_property_set_bool(OBJECT(&chip8->lpc), true, "realized", &error_fatal); pnv_xscom_add_subregion(chip, PNV_XSCOM_LPC_BASE, &chip8->lpc.xscom_regs); @@ -983,6 +1174,8 @@ static void pnv_chip_power8_realize(DeviceState *dev, Error **errp) } /* Create the simplified OCC model */ + object_property_set_link(OBJECT(&chip8->occ), OBJECT(&chip8->psi), "psi", + &error_abort); object_property_set_bool(OBJECT(&chip8->occ), true, "realized", &local_err); if (local_err) { error_propagate(errp, local_err); @@ -991,18 +1184,57 @@ static void pnv_chip_power8_realize(DeviceState *dev, Error **errp) pnv_xscom_add_subregion(chip, PNV_XSCOM_OCC_BASE, &chip8->occ.xscom_regs); /* OCC SRAM model */ - memory_region_add_subregion(get_system_memory(), PNV_OCC_COMMON_AREA(chip), + memory_region_add_subregion(get_system_memory(), PNV_OCC_SENSOR_BASE(chip), &chip8->occ.sram_regs); /* HOMER */ + object_property_set_link(OBJECT(&chip8->homer), OBJECT(chip), "chip", + &error_abort); object_property_set_bool(OBJECT(&chip8->homer), true, "realized", &local_err); if (local_err) { error_propagate(errp, local_err); return; } + /* Homer Xscom region */ + pnv_xscom_add_subregion(chip, PNV_XSCOM_PBA_BASE, &chip8->homer.pba_regs); + + /* Homer mmio region */ memory_region_add_subregion(get_system_memory(), PNV_HOMER_BASE(chip), &chip8->homer.regs); + + /* PHB3 controllers */ + for (i = 0; i < chip->num_phbs; i++) { + PnvPHB3 *phb = &chip8->phbs[i]; + PnvPBCQState *pbcq = &phb->pbcq; + + object_property_set_int(OBJECT(phb), i, "index", &error_fatal); + object_property_set_int(OBJECT(phb), chip->chip_id, "chip-id", + &error_fatal); + object_property_set_bool(OBJECT(phb), true, "realized", &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + qdev_set_parent_bus(DEVICE(phb), sysbus_get_default()); + + /* Populate the XSCOM address space. */ + pnv_xscom_add_subregion(chip, + PNV_XSCOM_PBCQ_NEST_BASE + 0x400 * phb->phb_id, + &pbcq->xscom_nest_regs); + pnv_xscom_add_subregion(chip, + PNV_XSCOM_PBCQ_PCI_BASE + 0x400 * phb->phb_id, + &pbcq->xscom_pci_regs); + pnv_xscom_add_subregion(chip, + PNV_XSCOM_PBCQ_SPCI_BASE + 0x040 * phb->phb_id, + &pbcq->xscom_spci_regs); + } +} + +static uint32_t pnv_chip_power8_xscom_pcba(PnvChip *chip, uint64_t addr) +{ + addr &= (PNV_XSCOM_SIZE - 1); + return ((addr >> 4) & ~0xfull) | ((addr >> 3) & 0xf); } static void pnv_chip_power8e_class_init(ObjectClass *klass, void *data) @@ -1010,16 +1242,19 @@ static void pnv_chip_power8e_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); PnvChipClass *k = PNV_CHIP_CLASS(klass); - k->chip_type = PNV_CHIP_POWER8E; k->chip_cfam_id = 0x221ef04980000000ull; /* P8 Murano DD2.1 */ k->cores_mask = POWER8E_CORE_MASK; + k->num_phbs = 3; k->core_pir = pnv_chip_core_pir_p8; k->intc_create = pnv_chip_power8_intc_create; k->intc_reset = pnv_chip_power8_intc_reset; k->intc_destroy = pnv_chip_power8_intc_destroy; + k->intc_print_info = pnv_chip_power8_intc_print_info; k->isa_create = pnv_chip_power8_isa_create; k->dt_populate = pnv_chip_power8_dt_populate; k->pic_print_info = pnv_chip_power8_pic_print_info; + k->xscom_core_base = pnv_chip_power8_xscom_core_base; + k->xscom_pcba = pnv_chip_power8_xscom_pcba; dc->desc = "PowerNV Chip POWER8E"; device_class_set_parent_realize(dc, pnv_chip_power8_realize, @@ -1031,16 +1266,19 @@ static void pnv_chip_power8_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); PnvChipClass *k = PNV_CHIP_CLASS(klass); - k->chip_type = PNV_CHIP_POWER8; k->chip_cfam_id = 0x220ea04980000000ull; /* P8 Venice DD2.0 */ k->cores_mask = POWER8_CORE_MASK; + k->num_phbs = 3; k->core_pir = pnv_chip_core_pir_p8; k->intc_create = pnv_chip_power8_intc_create; k->intc_reset = pnv_chip_power8_intc_reset; k->intc_destroy = pnv_chip_power8_intc_destroy; + k->intc_print_info = pnv_chip_power8_intc_print_info; k->isa_create = pnv_chip_power8_isa_create; k->dt_populate = pnv_chip_power8_dt_populate; k->pic_print_info = pnv_chip_power8_pic_print_info; + k->xscom_core_base = pnv_chip_power8_xscom_core_base; + k->xscom_pcba = pnv_chip_power8_xscom_pcba; dc->desc = "PowerNV Chip POWER8"; device_class_set_parent_realize(dc, pnv_chip_power8_realize, @@ -1052,16 +1290,19 @@ static void pnv_chip_power8nvl_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); PnvChipClass *k = PNV_CHIP_CLASS(klass); - k->chip_type = PNV_CHIP_POWER8NVL; k->chip_cfam_id = 0x120d304980000000ull; /* P8 Naples DD1.0 */ k->cores_mask = POWER8_CORE_MASK; + k->num_phbs = 3; k->core_pir = pnv_chip_core_pir_p8; k->intc_create = pnv_chip_power8_intc_create; k->intc_reset = pnv_chip_power8_intc_reset; k->intc_destroy = pnv_chip_power8_intc_destroy; + k->intc_print_info = pnv_chip_power8_intc_print_info; k->isa_create = pnv_chip_power8nvl_isa_create; k->dt_populate = pnv_chip_power8_dt_populate; k->pic_print_info = pnv_chip_power8_pic_print_info; + k->xscom_core_base = pnv_chip_power8_xscom_core_base; + k->xscom_pcba = pnv_chip_power8_xscom_pcba; dc->desc = "PowerNV Chip POWER8NVL"; device_class_set_parent_realize(dc, pnv_chip_power8_realize, @@ -1070,39 +1311,43 @@ static void pnv_chip_power8nvl_class_init(ObjectClass *klass, void *data) static void pnv_chip_power9_instance_init(Object *obj) { + PnvChip *chip = PNV_CHIP(obj); Pnv9Chip *chip9 = PNV9_CHIP(obj); + PnvChipClass *pcc = PNV_CHIP_GET_CLASS(obj); + int i; object_initialize_child(obj, "xive", &chip9->xive, sizeof(chip9->xive), TYPE_PNV_XIVE, &error_abort, NULL); - object_property_add_const_link(OBJECT(&chip9->xive), "chip", obj, - &error_abort); + object_property_add_alias(obj, "xive-fabric", OBJECT(&chip9->xive), + "xive-fabric", &error_abort); object_initialize_child(obj, "psi", &chip9->psi, sizeof(chip9->psi), TYPE_PNV9_PSI, &error_abort, NULL); - object_property_add_const_link(OBJECT(&chip9->psi), "chip", obj, - &error_abort); object_initialize_child(obj, "lpc", &chip9->lpc, sizeof(chip9->lpc), TYPE_PNV9_LPC, &error_abort, NULL); - object_property_add_const_link(OBJECT(&chip9->lpc), "psi", - OBJECT(&chip9->psi), &error_abort); object_initialize_child(obj, "occ", &chip9->occ, sizeof(chip9->occ), TYPE_PNV9_OCC, &error_abort, NULL); - object_property_add_const_link(OBJECT(&chip9->occ), "psi", - OBJECT(&chip9->psi), &error_abort); object_initialize_child(obj, "homer", &chip9->homer, sizeof(chip9->homer), TYPE_PNV9_HOMER, &error_abort, NULL); - object_property_add_const_link(OBJECT(&chip9->homer), "chip", obj, - &error_abort); + + for (i = 0; i < PNV9_CHIP_MAX_PEC; i++) { + object_initialize_child(obj, "pec[*]", &chip9->pecs[i], + sizeof(chip9->pecs[i]), TYPE_PNV_PHB4_PEC, + &error_abort, NULL); + } + + /* + * Number of PHBs is the chip default + */ + chip->num_phbs = pcc->num_phbs; } static void pnv_chip_quad_realize(Pnv9Chip *chip9, Error **errp) { PnvChip *chip = PNV_CHIP(chip9); - const char *typename = pnv_chip_core_typename(chip); - size_t typesize = object_type_get_instance_size(typename); int i; chip9->nr_quads = DIV_ROUND_UP(chip->nr_cores, 4); @@ -1111,7 +1356,7 @@ static void pnv_chip_quad_realize(Pnv9Chip *chip9, Error **errp) for (i = 0; i < chip9->nr_quads; i++) { char eq_name[32]; PnvQuad *eq = &chip9->quads[i]; - PnvCore *pnv_core = PNV_CORE(chip->cores + (i * 4) * typesize); + PnvCore *pnv_core = chip->cores[i * 4]; int core_id = CPU_CORE(pnv_core)->core_id; snprintf(eq_name, sizeof(eq_name), "eq[%d]", core_id); @@ -1126,6 +1371,78 @@ static void pnv_chip_quad_realize(Pnv9Chip *chip9, Error **errp) } } +static void pnv_chip_power9_phb_realize(PnvChip *chip, Error **errp) +{ + Pnv9Chip *chip9 = PNV9_CHIP(chip); + Error *local_err = NULL; + int i, j; + int phb_id = 0; + + for (i = 0; i < PNV9_CHIP_MAX_PEC; i++) { + PnvPhb4PecState *pec = &chip9->pecs[i]; + PnvPhb4PecClass *pecc = PNV_PHB4_PEC_GET_CLASS(pec); + uint32_t pec_nest_base; + uint32_t pec_pci_base; + + object_property_set_int(OBJECT(pec), i, "index", &error_fatal); + /* + * PEC0 -> 1 stack + * PEC1 -> 2 stacks + * PEC2 -> 3 stacks + */ + object_property_set_int(OBJECT(pec), i + 1, "num-stacks", + &error_fatal); + object_property_set_int(OBJECT(pec), chip->chip_id, "chip-id", + &error_fatal); + object_property_set_link(OBJECT(pec), OBJECT(get_system_memory()), + "system-memory", &error_abort); + object_property_set_bool(OBJECT(pec), true, "realized", &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + pec_nest_base = pecc->xscom_nest_base(pec); + pec_pci_base = pecc->xscom_pci_base(pec); + + pnv_xscom_add_subregion(chip, pec_nest_base, &pec->nest_regs_mr); + pnv_xscom_add_subregion(chip, pec_pci_base, &pec->pci_regs_mr); + + for (j = 0; j < pec->num_stacks && phb_id < chip->num_phbs; + j++, phb_id++) { + PnvPhb4PecStack *stack = &pec->stacks[j]; + Object *obj = OBJECT(&stack->phb); + + object_property_set_int(obj, phb_id, "index", &error_fatal); + object_property_set_int(obj, chip->chip_id, "chip-id", + &error_fatal); + object_property_set_int(obj, PNV_PHB4_VERSION, "version", + &error_fatal); + object_property_set_int(obj, PNV_PHB4_DEVICE_ID, "device-id", + &error_fatal); + object_property_set_link(obj, OBJECT(stack), "stack", &error_abort); + object_property_set_bool(obj, true, "realized", &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + qdev_set_parent_bus(DEVICE(obj), sysbus_get_default()); + + /* Populate the XSCOM address space. */ + pnv_xscom_add_subregion(chip, + pec_nest_base + 0x40 * (stack->stack_no + 1), + &stack->nest_regs_mr); + pnv_xscom_add_subregion(chip, + pec_pci_base + 0x40 * (stack->stack_no + 1), + &stack->pci_regs_mr); + pnv_xscom_add_subregion(chip, + pec_pci_base + PNV9_XSCOM_PEC_PCI_STK0 + + 0x40 * stack->stack_no, + &stack->phb_regs_mr); + } + } +} + static void pnv_chip_power9_realize(DeviceState *dev, Error **errp) { PnvChipClass *pcc = PNV_CHIP_GET_CLASS(dev); @@ -1163,6 +1480,8 @@ static void pnv_chip_power9_realize(DeviceState *dev, Error **errp) "pc-bar", &error_fatal); object_property_set_int(OBJECT(&chip9->xive), PNV9_XIVE_TM_BASE(chip), "tm-bar", &error_fatal); + object_property_set_link(OBJECT(&chip9->xive), OBJECT(chip), "chip", + &error_abort); object_property_set_bool(OBJECT(&chip9->xive), true, "realized", &local_err); if (local_err) { @@ -1184,6 +1503,8 @@ static void pnv_chip_power9_realize(DeviceState *dev, Error **errp) &PNV_PSI(psi9)->xscom_regs); /* LPC */ + object_property_set_link(OBJECT(&chip9->lpc), OBJECT(&chip9->psi), "psi", + &error_abort); object_property_set_bool(OBJECT(&chip9->lpc), true, "realized", &local_err); if (local_err) { error_propagate(errp, local_err); @@ -1196,6 +1517,8 @@ static void pnv_chip_power9_realize(DeviceState *dev, Error **errp) (uint64_t) PNV9_LPCM_BASE(chip)); /* Create the simplified OCC model */ + object_property_set_link(OBJECT(&chip9->occ), OBJECT(&chip9->psi), "psi", + &error_abort); object_property_set_bool(OBJECT(&chip9->occ), true, "realized", &local_err); if (local_err) { error_propagate(errp, local_err); @@ -1204,18 +1527,37 @@ static void pnv_chip_power9_realize(DeviceState *dev, Error **errp) pnv_xscom_add_subregion(chip, PNV9_XSCOM_OCC_BASE, &chip9->occ.xscom_regs); /* OCC SRAM model */ - memory_region_add_subregion(get_system_memory(), PNV9_OCC_COMMON_AREA(chip), + memory_region_add_subregion(get_system_memory(), PNV9_OCC_SENSOR_BASE(chip), &chip9->occ.sram_regs); /* HOMER */ + object_property_set_link(OBJECT(&chip9->homer), OBJECT(chip), "chip", + &error_abort); object_property_set_bool(OBJECT(&chip9->homer), true, "realized", &local_err); if (local_err) { error_propagate(errp, local_err); return; } + /* Homer Xscom region */ + pnv_xscom_add_subregion(chip, PNV9_XSCOM_PBA_BASE, &chip9->homer.pba_regs); + + /* Homer mmio region */ memory_region_add_subregion(get_system_memory(), PNV9_HOMER_BASE(chip), &chip9->homer.regs); + + /* PHBs */ + pnv_chip_power9_phb_realize(chip, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } +} + +static uint32_t pnv_chip_power9_xscom_pcba(PnvChip *chip, uint64_t addr) +{ + addr &= (PNV9_XSCOM_SIZE - 1); + return addr >> 3; } static void pnv_chip_power9_class_init(ObjectClass *klass, void *data) @@ -1223,22 +1565,113 @@ static void pnv_chip_power9_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); PnvChipClass *k = PNV_CHIP_CLASS(klass); - k->chip_type = PNV_CHIP_POWER9; k->chip_cfam_id = 0x220d104900008000ull; /* P9 Nimbus DD2.0 */ k->cores_mask = POWER9_CORE_MASK; k->core_pir = pnv_chip_core_pir_p9; k->intc_create = pnv_chip_power9_intc_create; k->intc_reset = pnv_chip_power9_intc_reset; k->intc_destroy = pnv_chip_power9_intc_destroy; + k->intc_print_info = pnv_chip_power9_intc_print_info; k->isa_create = pnv_chip_power9_isa_create; k->dt_populate = pnv_chip_power9_dt_populate; k->pic_print_info = pnv_chip_power9_pic_print_info; + k->xscom_core_base = pnv_chip_power9_xscom_core_base; + k->xscom_pcba = pnv_chip_power9_xscom_pcba; dc->desc = "PowerNV Chip POWER9"; + k->num_phbs = 6; device_class_set_parent_realize(dc, pnv_chip_power9_realize, &k->parent_realize); } +static void pnv_chip_power10_instance_init(Object *obj) +{ + Pnv10Chip *chip10 = PNV10_CHIP(obj); + + object_initialize_child(obj, "psi", &chip10->psi, sizeof(chip10->psi), + TYPE_PNV10_PSI, &error_abort, NULL); + object_initialize_child(obj, "lpc", &chip10->lpc, sizeof(chip10->lpc), + TYPE_PNV10_LPC, &error_abort, NULL); +} + +static void pnv_chip_power10_realize(DeviceState *dev, Error **errp) +{ + PnvChipClass *pcc = PNV_CHIP_GET_CLASS(dev); + PnvChip *chip = PNV_CHIP(dev); + Pnv10Chip *chip10 = PNV10_CHIP(dev); + Error *local_err = NULL; + + /* XSCOM bridge is first */ + pnv_xscom_realize(chip, PNV10_XSCOM_SIZE, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(chip), 0, PNV10_XSCOM_BASE(chip)); + + pcc->parent_realize(dev, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + /* Processor Service Interface (PSI) Host Bridge */ + object_property_set_int(OBJECT(&chip10->psi), PNV10_PSIHB_BASE(chip), + "bar", &error_fatal); + object_property_set_bool(OBJECT(&chip10->psi), true, "realized", + &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + pnv_xscom_add_subregion(chip, PNV10_XSCOM_PSIHB_BASE, + &PNV_PSI(&chip10->psi)->xscom_regs); + + /* LPC */ + object_property_set_link(OBJECT(&chip10->lpc), OBJECT(&chip10->psi), "psi", + &error_abort); + object_property_set_bool(OBJECT(&chip10->lpc), true, "realized", + &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + memory_region_add_subregion(get_system_memory(), PNV10_LPCM_BASE(chip), + &chip10->lpc.xscom_regs); + + chip->dt_isa_nodename = g_strdup_printf("/lpcm-opb@%" PRIx64 "/lpc@0", + (uint64_t) PNV10_LPCM_BASE(chip)); +} + +static uint32_t pnv_chip_power10_xscom_pcba(PnvChip *chip, uint64_t addr) +{ + addr &= (PNV10_XSCOM_SIZE - 1); + return addr >> 3; +} + +static void pnv_chip_power10_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PnvChipClass *k = PNV_CHIP_CLASS(klass); + + k->chip_cfam_id = 0x120da04900008000ull; /* P10 DD1.0 (with NX) */ + k->cores_mask = POWER10_CORE_MASK; + k->core_pir = pnv_chip_core_pir_p10; + k->intc_create = pnv_chip_power10_intc_create; + k->intc_reset = pnv_chip_power10_intc_reset; + k->intc_destroy = pnv_chip_power10_intc_destroy; + k->intc_print_info = pnv_chip_power10_intc_print_info; + k->isa_create = pnv_chip_power10_isa_create; + k->dt_populate = pnv_chip_power10_dt_populate; + k->pic_print_info = pnv_chip_power10_pic_print_info; + k->xscom_core_base = pnv_chip_power10_xscom_core_base; + k->xscom_pcba = pnv_chip_power10_xscom_pcba; + dc->desc = "PowerNV Chip POWER10"; + + device_class_set_parent_realize(dc, pnv_chip_power10_realize, + &k->parent_realize); +} + static void pnv_chip_core_sanitize(PnvChip *chip, Error **errp) { PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip); @@ -1271,12 +1704,11 @@ static void pnv_chip_core_sanitize(PnvChip *chip, Error **errp) static void pnv_chip_core_realize(PnvChip *chip, Error **errp) { - MachineState *ms = MACHINE(qdev_get_machine()); Error *error = NULL; PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip); const char *typename = pnv_chip_core_typename(chip); - size_t typesize = object_type_get_instance_size(typename); int i, core_hwid; + PnvMachineState *pnv = PNV_MACHINE(qdev_get_machine()); if (!object_class_by_name(typename)) { error_setg(errp, "Unable to find PowerNV CPU Core '%s'", typename); @@ -1290,42 +1722,43 @@ static void pnv_chip_core_realize(PnvChip *chip, Error **errp) return; } - chip->cores = g_malloc0(typesize * chip->nr_cores); + chip->cores = g_new0(PnvCore *, chip->nr_cores); for (i = 0, core_hwid = 0; (core_hwid < sizeof(chip->cores_mask) * 8) && (i < chip->nr_cores); core_hwid++) { char core_name[32]; - void *pnv_core = chip->cores + i * typesize; + PnvCore *pnv_core; uint64_t xscom_core_base; if (!(chip->cores_mask & (1ull << core_hwid))) { continue; } + pnv_core = PNV_CORE(object_new(typename)); + snprintf(core_name, sizeof(core_name), "core[%d]", core_hwid); - object_initialize_child(OBJECT(chip), core_name, pnv_core, typesize, - typename, &error_fatal, NULL); - object_property_set_int(OBJECT(pnv_core), ms->smp.threads, "nr-threads", - &error_fatal); + object_property_add_child(OBJECT(chip), core_name, OBJECT(pnv_core), + &error_abort); + chip->cores[i] = pnv_core; + object_property_set_int(OBJECT(pnv_core), chip->nr_threads, + "nr-threads", &error_fatal); object_property_set_int(OBJECT(pnv_core), core_hwid, CPU_CORE_PROP_CORE_ID, &error_fatal); object_property_set_int(OBJECT(pnv_core), pcc->core_pir(chip, core_hwid), "pir", &error_fatal); - object_property_add_const_link(OBJECT(pnv_core), "chip", - OBJECT(chip), &error_fatal); + object_property_set_int(OBJECT(pnv_core), pnv->fw_load_addr, + "hrmor", &error_fatal); + object_property_set_link(OBJECT(pnv_core), OBJECT(chip), "chip", + &error_abort); object_property_set_bool(OBJECT(pnv_core), true, "realized", &error_fatal); /* Each core has an XSCOM MMIO region */ - if (!pnv_chip_is_power9(chip)) { - xscom_core_base = PNV_XSCOM_EX_BASE(core_hwid); - } else { - xscom_core_base = PNV9_XSCOM_EC_BASE(core_hwid); - } + xscom_core_base = pcc->xscom_core_base(chip, core_hwid); pnv_xscom_add_subregion(chip, xscom_core_base, - &PNV_CORE(pnv_core)->xscom_regs); + &pnv_core->xscom_regs); i++; } } @@ -1349,6 +1782,8 @@ static Property pnv_chip_properties[] = { DEFINE_PROP_UINT64("ram-size", PnvChip, ram_size, 0), DEFINE_PROP_UINT32("nr-cores", PnvChip, nr_cores, 1), DEFINE_PROP_UINT64("cores-mask", PnvChip, cores_mask, 0x0), + DEFINE_PROP_UINT32("nr-threads", PnvChip, nr_threads, 1), + DEFINE_PROP_UINT32("num-phbs", PnvChip, num_phbs, 0), DEFINE_PROP_END_OF_LIST(), }; @@ -1358,21 +1793,47 @@ static void pnv_chip_class_init(ObjectClass *klass, void *data) set_bit(DEVICE_CATEGORY_CPU, dc->categories); dc->realize = pnv_chip_realize; - dc->props = pnv_chip_properties; + device_class_set_props(dc, pnv_chip_properties); dc->desc = "PowerNV Chip"; } +PowerPCCPU *pnv_chip_find_cpu(PnvChip *chip, uint32_t pir) +{ + int i, j; + + for (i = 0; i < chip->nr_cores; i++) { + PnvCore *pc = chip->cores[i]; + CPUCore *cc = CPU_CORE(pc); + + for (j = 0; j < cc->nr_threads; j++) { + if (ppc_cpu_pir(pc->threads[j]) == pir) { + return pc->threads[j]; + } + } + } + return NULL; +} + static ICSState *pnv_ics_get(XICSFabric *xi, int irq) { PnvMachineState *pnv = PNV_MACHINE(xi); - int i; + int i, j; for (i = 0; i < pnv->num_chips; i++) { + PnvChip *chip = pnv->chips[i]; Pnv8Chip *chip8 = PNV8_CHIP(pnv->chips[i]); if (ics_valid_irq(&chip8->psi.ics, irq)) { return &chip8->psi.ics; } + for (j = 0; j < chip->num_phbs; j++) { + if (ics_valid_irq(&chip8->phbs[j].lsis, irq)) { + return &chip8->phbs[j].lsis; + } + if (ics_valid_irq(ICS(&chip8->phbs[j].msis), irq)) { + return ICS(&chip8->phbs[j].msis); + } + } } return NULL; } @@ -1380,11 +1841,17 @@ static ICSState *pnv_ics_get(XICSFabric *xi, int irq) static void pnv_ics_resend(XICSFabric *xi) { PnvMachineState *pnv = PNV_MACHINE(xi); - int i; + int i, j; for (i = 0; i < pnv->num_chips; i++) { + PnvChip *chip = pnv->chips[i]; Pnv8Chip *chip8 = PNV8_CHIP(pnv->chips[i]); + ics_resend(&chip8->psi.ics); + for (j = 0; j < chip->num_phbs; j++) { + ics_resend(&chip8->phbs[j].lsis); + ics_resend(ICS(&chip8->phbs[j].msis)); + } } } @@ -1405,11 +1872,9 @@ static void pnv_pic_print_info(InterruptStatsProvider *obj, CPU_FOREACH(cs) { PowerPCCPU *cpu = POWERPC_CPU(cs); - if (pnv_chip_is_power9(pnv->chips[0])) { - xive_tctx_pic_print_info(XIVE_TCTX(pnv_cpu_state(cpu)->intc), mon); - } else { - icp_pic_print_info(ICP(pnv_cpu_state(cpu)->intc), mon); - } + /* XXX: loop on each chip/core/thread instead of CPU_FOREACH() */ + PNV_CHIP_GET_CLASS(pnv->chips[0])->intc_print_info(pnv->chips[0], cpu, + mon); } for (i = 0; i < pnv->num_chips; i++) { @@ -1417,57 +1882,41 @@ static void pnv_pic_print_info(InterruptStatsProvider *obj, } } -static void pnv_get_num_chips(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) +static int pnv_match_nvt(XiveFabric *xfb, uint8_t format, + uint8_t nvt_blk, uint32_t nvt_idx, + bool cam_ignore, uint8_t priority, + uint32_t logic_serv, + XiveTCTXMatch *match) { - visit_type_uint32(v, name, &PNV_MACHINE(obj)->num_chips, errp); -} + PnvMachineState *pnv = PNV_MACHINE(xfb); + int total_count = 0; + int i; -static void pnv_set_num_chips(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - PnvMachineState *pnv = PNV_MACHINE(obj); - uint32_t num_chips; - Error *local_err = NULL; + for (i = 0; i < pnv->num_chips; i++) { + Pnv9Chip *chip9 = PNV9_CHIP(pnv->chips[i]); + XivePresenter *xptr = XIVE_PRESENTER(&chip9->xive); + XivePresenterClass *xpc = XIVE_PRESENTER_GET_CLASS(xptr); + int count; - visit_type_uint32(v, name, &num_chips, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; + count = xpc->match_nvt(xptr, format, nvt_blk, nvt_idx, cam_ignore, + priority, logic_serv, match); + + if (count < 0) { + return count; + } + + total_count += count; } - /* - * TODO: should we decide on how many chips we can create based - * on #cores and Venice vs. Murano vs. Naples chip type etc..., - */ - if (!is_power_of_2(num_chips) || num_chips > 4) { - error_setg(errp, "invalid number of chips: '%d'", num_chips); - return; - } - - pnv->num_chips = num_chips; -} - -static void pnv_machine_instance_init(Object *obj) -{ - PnvMachineState *pnv = PNV_MACHINE(obj); - pnv->num_chips = 1; -} - -static void pnv_machine_class_props_init(ObjectClass *oc) -{ - object_class_property_add(oc, "num-chips", "uint32", - pnv_get_num_chips, pnv_set_num_chips, - NULL, NULL, NULL); - object_class_property_set_description(oc, "num-chips", - "Specifies the number of processor chips", - NULL); + return total_count; } static void pnv_machine_power8_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); XICSFabricClass *xic = XICS_FABRIC_CLASS(oc); + PnvMachineClass *pmc = PNV_MACHINE_CLASS(oc); + static const char compat[] = "qemu,powernv8\0qemu,powernv\0ibm,powernv"; mc->desc = "IBM PowerNV (Non-Virtualized) POWER8"; mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("power8_v2.0"); @@ -1475,16 +1924,57 @@ static void pnv_machine_power8_class_init(ObjectClass *oc, void *data) xic->icp_get = pnv_icp_get; xic->ics_get = pnv_ics_get; xic->ics_resend = pnv_ics_resend; + + pmc->compat = compat; + pmc->compat_size = sizeof(compat); } static void pnv_machine_power9_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); + XiveFabricClass *xfc = XIVE_FABRIC_CLASS(oc); + PnvMachineClass *pmc = PNV_MACHINE_CLASS(oc); + static const char compat[] = "qemu,powernv9\0ibm,powernv"; mc->desc = "IBM PowerNV (Non-Virtualized) POWER9"; mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("power9_v2.0"); + xfc->match_nvt = pnv_match_nvt; mc->alias = "powernv"; + + pmc->compat = compat; + pmc->compat_size = sizeof(compat); + pmc->dt_power_mgt = pnv_dt_power_mgt; +} + +static void pnv_machine_power10_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + PnvMachineClass *pmc = PNV_MACHINE_CLASS(oc); + static const char compat[] = "qemu,powernv10\0ibm,powernv"; + + mc->desc = "IBM PowerNV (Non-Virtualized) POWER10"; + mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("power10_v1.0"); + + pmc->compat = compat; + pmc->compat_size = sizeof(compat); + pmc->dt_power_mgt = pnv_dt_power_mgt; +} + +static bool pnv_machine_get_hb(Object *obj, Error **errp) +{ + PnvMachineState *pnv = PNV_MACHINE(obj); + + return !!pnv->fw_load_addr; +} + +static void pnv_machine_set_hb(Object *obj, bool value, Error **errp) +{ + PnvMachineState *pnv = PNV_MACHINE(obj); + + if (value) { + pnv->fw_load_addr = 0x8000000; + } } static void pnv_machine_class_init(ObjectClass *oc, void *data) @@ -1505,9 +1995,15 @@ static void pnv_machine_class_init(ObjectClass *oc, void *data) * enough to fit the maximum initrd size at it's load address */ mc->default_ram_size = INITRD_LOAD_ADDR + INITRD_MAX_SIZE; + mc->default_ram_id = "pnv.ram"; ispc->print_info = pnv_pic_print_info; - pnv_machine_class_props_init(oc); + object_class_property_add_bool(oc, "hb-mode", + pnv_machine_get_hb, pnv_machine_set_hb, + &error_abort); + object_class_property_set_description(oc, "hb-mode", + "Use a hostboot like boot loader", + NULL); } #define DEFINE_PNV8_CHIP_TYPE(type, class_initfn) \ @@ -1524,11 +2020,27 @@ static void pnv_machine_class_init(ObjectClass *oc, void *data) .parent = TYPE_PNV9_CHIP, \ } +#define DEFINE_PNV10_CHIP_TYPE(type, class_initfn) \ + { \ + .name = type, \ + .class_init = class_initfn, \ + .parent = TYPE_PNV10_CHIP, \ + } + static const TypeInfo types[] = { + { + .name = MACHINE_TYPE_NAME("powernv10"), + .parent = TYPE_PNV_MACHINE, + .class_init = pnv_machine_power10_class_init, + }, { .name = MACHINE_TYPE_NAME("powernv9"), .parent = TYPE_PNV_MACHINE, .class_init = pnv_machine_power9_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_XIVE_FABRIC }, + { }, + }, }, { .name = MACHINE_TYPE_NAME("powernv8"), @@ -1544,8 +2056,8 @@ static const TypeInfo types[] = { .parent = TYPE_MACHINE, .abstract = true, .instance_size = sizeof(PnvMachineState), - .instance_init = pnv_machine_instance_init, .class_init = pnv_machine_class_init, + .class_size = sizeof(PnvMachineClass), .interfaces = (InterfaceInfo[]) { { TYPE_INTERRUPT_STATS_PROVIDER }, { }, @@ -1560,6 +2072,17 @@ static const TypeInfo types[] = { .abstract = true, }, + /* + * P10 chip and variants + */ + { + .name = TYPE_PNV10_CHIP, + .parent = TYPE_PNV_CHIP, + .instance_init = pnv_chip_power10_instance_init, + .instance_size = sizeof(Pnv10Chip), + }, + DEFINE_PNV10_CHIP_TYPE(TYPE_PNV_CHIP_POWER10, pnv_chip_power10_class_init), + /* * P9 chip and variants */ diff --git a/hw/ppc/pnv_bmc.c b/hw/ppc/pnv_bmc.c index dc5e918cb7..4e018b8b70 100644 --- a/hw/ppc/pnv_bmc.c +++ b/hw/ppc/pnv_bmc.c @@ -17,6 +17,8 @@ */ #include "qemu/osdep.h" +#include "qemu-common.h" +#include "qapi/error.h" #include "target/ppc/cpu.h" #include "qemu/log.h" #include "hw/ipmi/ipmi.h" @@ -114,3 +116,164 @@ void pnv_dt_bmc_sensors(IPMIBmc *bmc, void *fdt) sdr->sensor_type))); } } + +/* + * HIOMAP protocol handler + */ +#define HIOMAP_C_RESET 1 +#define HIOMAP_C_GET_INFO 2 +#define HIOMAP_C_GET_FLASH_INFO 3 +#define HIOMAP_C_CREATE_READ_WINDOW 4 +#define HIOMAP_C_CLOSE_WINDOW 5 +#define HIOMAP_C_CREATE_WRITE_WINDOW 6 +#define HIOMAP_C_MARK_DIRTY 7 +#define HIOMAP_C_FLUSH 8 +#define HIOMAP_C_ACK 9 +#define HIOMAP_C_ERASE 10 +#define HIOMAP_C_DEVICE_NAME 11 +#define HIOMAP_C_LOCK 12 + +#define BLOCK_SHIFT 12 /* 4K */ + +static uint16_t bytes_to_blocks(uint32_t bytes) +{ + return bytes >> BLOCK_SHIFT; +} + +static void hiomap_cmd(IPMIBmcSim *ibs, uint8_t *cmd, unsigned int cmd_len, + RspBuffer *rsp) +{ + PnvPnor *pnor = PNV_PNOR(object_property_get_link(OBJECT(ibs), "pnor", + &error_abort)); + uint32_t pnor_size = pnor->size; + uint32_t pnor_addr = PNOR_SPI_OFFSET; + bool readonly = false; + + rsp_buffer_push(rsp, cmd[2]); + rsp_buffer_push(rsp, cmd[3]); + + switch (cmd[2]) { + case HIOMAP_C_MARK_DIRTY: + case HIOMAP_C_FLUSH: + case HIOMAP_C_ERASE: + case HIOMAP_C_ACK: + break; + + case HIOMAP_C_GET_INFO: + rsp_buffer_push(rsp, 2); /* Version 2 */ + rsp_buffer_push(rsp, BLOCK_SHIFT); /* block size */ + rsp_buffer_push(rsp, 0); /* Timeout */ + rsp_buffer_push(rsp, 0); /* Timeout */ + break; + + case HIOMAP_C_GET_FLASH_INFO: + rsp_buffer_push(rsp, bytes_to_blocks(pnor_size) & 0xFF); + rsp_buffer_push(rsp, bytes_to_blocks(pnor_size) >> 8); + rsp_buffer_push(rsp, 0x01); /* erase size */ + rsp_buffer_push(rsp, 0x00); /* erase size */ + break; + + case HIOMAP_C_CREATE_READ_WINDOW: + readonly = true; + /* Fall through */ + + case HIOMAP_C_CREATE_WRITE_WINDOW: + memory_region_set_readonly(&pnor->mmio, readonly); + memory_region_set_enabled(&pnor->mmio, true); + + rsp_buffer_push(rsp, bytes_to_blocks(pnor_addr) & 0xFF); + rsp_buffer_push(rsp, bytes_to_blocks(pnor_addr) >> 8); + rsp_buffer_push(rsp, bytes_to_blocks(pnor_size) & 0xFF); + rsp_buffer_push(rsp, bytes_to_blocks(pnor_size) >> 8); + rsp_buffer_push(rsp, 0x00); /* offset */ + rsp_buffer_push(rsp, 0x00); /* offset */ + break; + + case HIOMAP_C_CLOSE_WINDOW: + memory_region_set_enabled(&pnor->mmio, false); + break; + + case HIOMAP_C_DEVICE_NAME: + case HIOMAP_C_RESET: + case HIOMAP_C_LOCK: + default: + qemu_log_mask(LOG_GUEST_ERROR, "HIOMAP: unknow command %02X\n", cmd[2]); + break; + } +} + +#define HIOMAP 0x5a + +static const IPMICmdHandler hiomap_cmds[] = { + [HIOMAP] = { hiomap_cmd, 3 }, +}; + +static const IPMINetfn hiomap_netfn = { + .cmd_nums = ARRAY_SIZE(hiomap_cmds), + .cmd_handlers = hiomap_cmds +}; + + +void pnv_bmc_set_pnor(IPMIBmc *bmc, PnvPnor *pnor) +{ + object_ref(OBJECT(pnor)); + object_property_add_const_link(OBJECT(bmc), "pnor", OBJECT(pnor), + &error_abort); + + /* Install the HIOMAP protocol handlers to access the PNOR */ + ipmi_sim_register_netfn(IPMI_BMC_SIMULATOR(bmc), IPMI_NETFN_OEM, + &hiomap_netfn); +} + +/* + * Instantiate the machine BMC. PowerNV uses the QEMU internal + * simulator but it could also be external. + */ +IPMIBmc *pnv_bmc_create(PnvPnor *pnor) +{ + Object *obj; + + obj = object_new(TYPE_IPMI_BMC_SIMULATOR); + object_ref(OBJECT(pnor)); + object_property_add_const_link(obj, "pnor", OBJECT(pnor), &error_abort); + object_property_set_bool(obj, true, "realized", &error_fatal); + + /* Install the HIOMAP protocol handlers to access the PNOR */ + ipmi_sim_register_netfn(IPMI_BMC_SIMULATOR(obj), IPMI_NETFN_OEM, + &hiomap_netfn); + + return IPMI_BMC(obj); +} + +typedef struct ForeachArgs { + const char *name; + Object *obj; +} ForeachArgs; + +static int bmc_find(Object *child, void *opaque) +{ + ForeachArgs *args = opaque; + + if (object_dynamic_cast(child, args->name)) { + if (args->obj) { + return 1; + } + args->obj = child; + } + return 0; +} + +IPMIBmc *pnv_bmc_find(Error **errp) +{ + ForeachArgs args = { TYPE_IPMI_BMC_SIMULATOR, NULL }; + int ret; + + ret = object_child_foreach_recursive(object_get_root(), bmc_find, &args); + if (ret) { + error_setg(errp, "machine should have only one BMC device. " + "Use '-nodefaults'"); + return NULL; + } + + return args.obj ? IPMI_BMC(args.obj) : NULL; +} diff --git a/hw/ppc/pnv_core.c b/hw/ppc/pnv_core.c index 61b3d3ce22..234562040d 100644 --- a/hw/ppc/pnv_core.c +++ b/hw/ppc/pnv_core.c @@ -40,11 +40,11 @@ static const char *pnv_core_cpu_typename(PnvCore *pc) return cpu_type; } -static void pnv_core_cpu_reset(PowerPCCPU *cpu, PnvChip *chip) +static void pnv_core_cpu_reset(PnvCore *pc, PowerPCCPU *cpu) { CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; - PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip); + PnvChipClass *pcc = PNV_CHIP_GET_CLASS(pc->chip); cpu_reset(cs); @@ -56,7 +56,9 @@ static void pnv_core_cpu_reset(PowerPCCPU *cpu, PnvChip *chip) env->nip = 0x10; env->msr |= MSR_HVB; /* Hypervisor mode */ - pcc->intc_reset(chip, cpu); + env->spr[SPR_HRMOR] = pc->hrmor; + + pcc->intc_reset(pc->chip, cpu); } /* @@ -162,14 +164,14 @@ static const MemoryRegionOps pnv_core_power9_xscom_ops = { .endianness = DEVICE_BIG_ENDIAN, }; -static void pnv_core_cpu_realize(PowerPCCPU *cpu, PnvChip *chip, Error **errp) +static void pnv_core_cpu_realize(PnvCore *pc, PowerPCCPU *cpu, Error **errp) { CPUPPCState *env = &cpu->env; int core_pir; int thread_index = 0; /* TODO: TCG supports only one thread */ ppc_spr_t *pir = &env->spr_cb[SPR_PIR]; Error *local_err = NULL; - PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip); + PnvChipClass *pcc = PNV_CHIP_GET_CLASS(pc->chip); object_property_set_bool(OBJECT(cpu), true, "realized", &local_err); if (local_err) { @@ -177,13 +179,13 @@ static void pnv_core_cpu_realize(PowerPCCPU *cpu, PnvChip *chip, Error **errp) return; } - pcc->intc_create(chip, cpu, &local_err); + pcc->intc_create(pc->chip, cpu, &local_err); if (local_err) { error_propagate(errp, local_err); return; } - core_pir = object_property_get_uint(OBJECT(cpu), "core-pir", &error_abort); + core_pir = object_property_get_uint(OBJECT(pc), "pir", &error_abort); /* * The PIR of a thread is the core PIR + the thread index. We will @@ -203,7 +205,7 @@ static void pnv_core_reset(void *dev) int i; for (i = 0; i < cc->nr_threads; i++) { - pnv_core_cpu_reset(pc->threads[i], pc->chip); + pnv_core_cpu_reset(pc, pc->threads[i]); } } @@ -217,15 +219,8 @@ static void pnv_core_realize(DeviceState *dev, Error **errp) void *obj; int i, j; char name[32]; - Object *chip; - chip = object_property_get_link(OBJECT(dev), "chip", &local_err); - if (!chip) { - error_propagate_prepend(errp, local_err, - "required link 'chip' not found: "); - return; - } - pc->chip = PNV_CHIP(chip); + assert(pc->chip); pc->threads = g_new(PowerPCCPU *, cc->nr_threads); for (i = 0; i < cc->nr_threads; i++) { @@ -238,8 +233,6 @@ static void pnv_core_realize(DeviceState *dev, Error **errp) snprintf(name, sizeof(name), "thread[%d]", i); object_property_add_child(OBJECT(pc), name, obj, &error_abort); - object_property_add_alias(obj, "core-pir", OBJECT(pc), - "pir", &error_abort); cpu->machine_data = g_new0(PnvCPUState, 1); @@ -247,13 +240,14 @@ static void pnv_core_realize(DeviceState *dev, Error **errp) } for (j = 0; j < cc->nr_threads; j++) { - pnv_core_cpu_realize(pc->threads[j], pc->chip, &local_err); + pnv_core_cpu_realize(pc, pc->threads[j], &local_err); if (local_err) { goto err; } } snprintf(name, sizeof(name), "xscom-core.%d", cc->core_id); + /* TODO: check PNV_XSCOM_EX_SIZE for p10 */ pnv_xscom_region_init(&pc->xscom_regs, OBJECT(dev), pcc->xscom_ops, pc, name, PNV_XSCOM_EX_SIZE); @@ -269,12 +263,12 @@ err: error_propagate(errp, local_err); } -static void pnv_core_cpu_unrealize(PowerPCCPU *cpu, PnvChip *chip) +static void pnv_core_cpu_unrealize(PnvCore *pc, PowerPCCPU *cpu) { PnvCPUState *pnv_cpu = pnv_cpu_state(cpu); - PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip); + PnvChipClass *pcc = PNV_CHIP_GET_CLASS(pc->chip); - pcc->intc_destroy(chip, cpu); + pcc->intc_destroy(pc->chip, cpu); cpu_remove_sync(CPU(cpu)); cpu->machine_data = NULL; g_free(pnv_cpu); @@ -290,13 +284,15 @@ static void pnv_core_unrealize(DeviceState *dev, Error **errp) qemu_unregister_reset(pnv_core_reset, pc); for (i = 0; i < cc->nr_threads; i++) { - pnv_core_cpu_unrealize(pc->threads[i], pc->chip); + pnv_core_cpu_unrealize(pc, pc->threads[i]); } g_free(pc->threads); } static Property pnv_core_properties[] = { DEFINE_PROP_UINT32("pir", PnvCore, pir, 0), + DEFINE_PROP_UINT64("hrmor", PnvCore, hrmor, 0), + DEFINE_PROP_LINK("chip", PnvCore, chip, TYPE_PNV_CHIP, PnvChip *), DEFINE_PROP_END_OF_LIST(), }; @@ -314,13 +310,22 @@ static void pnv_core_power9_class_init(ObjectClass *oc, void *data) pcc->xscom_ops = &pnv_core_power9_xscom_ops; } +static void pnv_core_power10_class_init(ObjectClass *oc, void *data) +{ + PnvCoreClass *pcc = PNV_CORE_CLASS(oc); + + /* TODO: Use the P9 XSCOMs for now on P10 */ + pcc->xscom_ops = &pnv_core_power9_xscom_ops; +} + static void pnv_core_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); dc->realize = pnv_core_realize; dc->unrealize = pnv_core_unrealize; - dc->props = pnv_core_properties; + device_class_set_props(dc, pnv_core_properties); + dc->user_creatable = false; } #define DEFINE_PNV_CORE_TYPE(family, cpu_model) \ @@ -343,6 +348,7 @@ static const TypeInfo pnv_core_infos[] = { DEFINE_PNV_CORE_TYPE(power8, "power8_v2.0"), DEFINE_PNV_CORE_TYPE(power8, "power8nvl_v1.0"), DEFINE_PNV_CORE_TYPE(power9, "power9_v2.0"), + DEFINE_PNV_CORE_TYPE(power10, "power10_v1.0"), }; DEFINE_TYPES(pnv_core_infos) @@ -417,7 +423,8 @@ static void pnv_quad_class_init(ObjectClass *oc, void *data) DeviceClass *dc = DEVICE_CLASS(oc); dc->realize = pnv_quad_realize; - dc->props = pnv_quad_properties; + device_class_set_props(dc, pnv_quad_properties); + dc->user_creatable = false; } static const TypeInfo pnv_quad_info = { diff --git a/hw/ppc/pnv_homer.c b/hw/ppc/pnv_homer.c index cc881a3b32..9a262629b7 100644 --- a/hw/ppc/pnv_homer.c +++ b/hw/ppc/pnv_homer.c @@ -17,13 +17,16 @@ */ #include "qemu/osdep.h" +#include "qemu/log.h" #include "qapi/error.h" #include "exec/hwaddr.h" #include "exec/memory.h" #include "sysemu/cpus.h" #include "hw/qdev-core.h" +#include "hw/qdev-properties.h" #include "hw/ppc/pnv.h" #include "hw/ppc/pnv_homer.h" +#include "hw/ppc/pnv_xscom.h" static bool core_max_array(PnvHomer *homer, hwaddr addr) @@ -113,10 +116,67 @@ static const MemoryRegionOps pnv_power8_homer_ops = { .endianness = DEVICE_BIG_ENDIAN, }; +/* P8 PBA BARs */ +#define PBA_BAR0 0x00 +#define PBA_BAR1 0x01 +#define PBA_BAR2 0x02 +#define PBA_BAR3 0x03 +#define PBA_BARMASK0 0x04 +#define PBA_BARMASK1 0x05 +#define PBA_BARMASK2 0x06 +#define PBA_BARMASK3 0x07 + +static uint64_t pnv_homer_power8_pba_read(void *opaque, hwaddr addr, + unsigned size) +{ + PnvHomer *homer = PNV_HOMER(opaque); + PnvChip *chip = homer->chip; + uint32_t reg = addr >> 3; + uint64_t val = 0; + + switch (reg) { + case PBA_BAR0: + val = PNV_HOMER_BASE(chip); + break; + case PBA_BARMASK0: /* P8 homer region mask */ + val = (PNV_HOMER_SIZE - 1) & 0x300000; + break; + case PBA_BAR3: /* P8 occ common area */ + val = PNV_OCC_COMMON_AREA_BASE; + break; + case PBA_BARMASK3: /* P8 occ common area mask */ + val = (PNV_OCC_COMMON_AREA_SIZE - 1) & 0x700000; + break; + default: + qemu_log_mask(LOG_UNIMP, "PBA: read to unimplemented register: Ox%" + HWADDR_PRIx "\n", addr >> 3); + } + return val; +} + +static void pnv_homer_power8_pba_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + qemu_log_mask(LOG_UNIMP, "PBA: write to unimplemented register: Ox%" + HWADDR_PRIx "\n", addr >> 3); +} + +static const MemoryRegionOps pnv_homer_power8_pba_ops = { + .read = pnv_homer_power8_pba_read, + .write = pnv_homer_power8_pba_write, + .valid.min_access_size = 8, + .valid.max_access_size = 8, + .impl.min_access_size = 8, + .impl.max_access_size = 8, + .endianness = DEVICE_BIG_ENDIAN, +}; + static void pnv_homer_power8_class_init(ObjectClass *klass, void *data) { PnvHomerClass *homer = PNV_HOMER_CLASS(klass); + homer->pba_size = PNV_XSCOM_PBA_SIZE; + homer->pba_ops = &pnv_homer_power8_pba_ops; homer->homer_size = PNV_HOMER_SIZE; homer->homer_ops = &pnv_power8_homer_ops; homer->core_max_base = PNV8_CORE_MAX_BASE; @@ -209,10 +269,57 @@ static const MemoryRegionOps pnv_power9_homer_ops = { .endianness = DEVICE_BIG_ENDIAN, }; +static uint64_t pnv_homer_power9_pba_read(void *opaque, hwaddr addr, + unsigned size) +{ + PnvHomer *homer = PNV_HOMER(opaque); + PnvChip *chip = homer->chip; + uint32_t reg = addr >> 3; + uint64_t val = 0; + + switch (reg) { + case PBA_BAR0: + val = PNV9_HOMER_BASE(chip); + break; + case PBA_BARMASK0: /* P9 homer region mask */ + val = (PNV9_HOMER_SIZE - 1) & 0x300000; + break; + case PBA_BAR2: /* P9 occ common area */ + val = PNV9_OCC_COMMON_AREA_BASE; + break; + case PBA_BARMASK2: /* P9 occ common area size */ + val = (PNV9_OCC_COMMON_AREA_SIZE - 1) & 0x700000; + break; + default: + qemu_log_mask(LOG_UNIMP, "PBA: read to unimplemented register: Ox%" + HWADDR_PRIx "\n", addr >> 3); + } + return val; +} + +static void pnv_homer_power9_pba_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + qemu_log_mask(LOG_UNIMP, "PBA: write to unimplemented register: Ox%" + HWADDR_PRIx "\n", addr >> 3); +} + +static const MemoryRegionOps pnv_homer_power9_pba_ops = { + .read = pnv_homer_power9_pba_read, + .write = pnv_homer_power9_pba_write, + .valid.min_access_size = 8, + .valid.max_access_size = 8, + .impl.min_access_size = 8, + .impl.max_access_size = 8, + .endianness = DEVICE_BIG_ENDIAN, +}; + static void pnv_homer_power9_class_init(ObjectClass *klass, void *data) { PnvHomerClass *homer = PNV_HOMER_CLASS(klass); + homer->pba_size = PNV9_XSCOM_PBA_SIZE; + homer->pba_ops = &pnv_homer_power9_pba_ops; homer->homer_size = PNV9_HOMER_SIZE; homer->homer_ops = &pnv_power9_homer_ops; homer->core_max_base = PNV9_CORE_MAX_BASE; @@ -229,28 +336,31 @@ static void pnv_homer_realize(DeviceState *dev, Error **errp) { PnvHomer *homer = PNV_HOMER(dev); PnvHomerClass *hmrc = PNV_HOMER_GET_CLASS(homer); - Object *obj; - Error *local_err = NULL; - obj = object_property_get_link(OBJECT(dev), "chip", &local_err); - if (!obj) { - error_propagate(errp, local_err); - error_prepend(errp, "required link 'chip' not found: "); - return; - } - homer->chip = PNV_CHIP(obj); + assert(homer->chip); + + pnv_xscom_region_init(&homer->pba_regs, OBJECT(dev), hmrc->pba_ops, + homer, "xscom-pba", hmrc->pba_size); + /* homer region */ memory_region_init_io(&homer->regs, OBJECT(dev), hmrc->homer_ops, homer, "homer-main-memory", hmrc->homer_size); } +static Property pnv_homer_properties[] = { + DEFINE_PROP_LINK("chip", PnvHomer, chip, TYPE_PNV_CHIP, PnvChip *), + DEFINE_PROP_END_OF_LIST(), +}; + static void pnv_homer_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = pnv_homer_realize; dc->desc = "PowerNV HOMER Memory"; + device_class_set_props(dc, pnv_homer_properties); + dc->user_creatable = false; } static const TypeInfo pnv_homer_type_info = { diff --git a/hw/ppc/pnv_lpc.c b/hw/ppc/pnv_lpc.c index 9466d4a1be..b5ffa48dac 100644 --- a/hw/ppc/pnv_lpc.c +++ b/hw/ppc/pnv_lpc.c @@ -24,7 +24,7 @@ #include "qemu/module.h" #include "hw/irq.h" #include "hw/isa/isa.h" - +#include "hw/qdev-properties.h" #include "hw/ppc/pnv.h" #include "hw/ppc/pnv_lpc.h" #include "hw/ppc/pnv_xscom.h" @@ -86,7 +86,7 @@ enum { #define ISA_FW_SIZE 0x10000000 #define LPC_IO_OPB_ADDR 0xd0010000 #define LPC_IO_OPB_SIZE 0x00010000 -#define LPC_MEM_OPB_ADDR 0xe0010000 +#define LPC_MEM_OPB_ADDR 0xe0000000 #define LPC_MEM_OPB_SIZE 0x10000000 #define LPC_FW_OPB_ADDR 0xf0000000 #define LPC_FW_OPB_SIZE 0x10000000 @@ -122,26 +122,36 @@ static int pnv_lpc_dt_xscom(PnvXScomInterface *dev, void *fdt, int xscom_offset) } /* POWER9 only */ -int pnv_dt_lpc(PnvChip *chip, void *fdt, int root_offset) +int pnv_dt_lpc(PnvChip *chip, void *fdt, int root_offset, uint64_t lpcm_addr, + uint64_t lpcm_size) { const char compat[] = "ibm,power9-lpcm-opb\0simple-bus"; const char lpc_compat[] = "ibm,power9-lpc\0ibm,lpc"; char *name; int offset, lpcm_offset; - uint64_t lpcm_addr = PNV9_LPCM_BASE(chip); uint32_t opb_ranges[8] = { 0, cpu_to_be32(lpcm_addr >> 32), cpu_to_be32((uint32_t)lpcm_addr), - cpu_to_be32(PNV9_LPCM_SIZE / 2), - cpu_to_be32(PNV9_LPCM_SIZE / 2), + cpu_to_be32(lpcm_size / 2), + cpu_to_be32(lpcm_size / 2), cpu_to_be32(lpcm_addr >> 32), - cpu_to_be32(PNV9_LPCM_SIZE / 2), - cpu_to_be32(PNV9_LPCM_SIZE / 2), + cpu_to_be32(lpcm_size / 2), + cpu_to_be32(lpcm_size / 2), }; uint32_t opb_reg[4] = { cpu_to_be32(lpcm_addr >> 32), cpu_to_be32((uint32_t)lpcm_addr), - cpu_to_be32(PNV9_LPCM_SIZE >> 32), - cpu_to_be32((uint32_t)PNV9_LPCM_SIZE), + cpu_to_be32(lpcm_size >> 32), + cpu_to_be32((uint32_t)lpcm_size), + }; + uint32_t lpc_ranges[12] = { 0, 0, + cpu_to_be32(LPC_MEM_OPB_ADDR), + cpu_to_be32(LPC_MEM_OPB_SIZE), + cpu_to_be32(1), 0, + cpu_to_be32(LPC_IO_OPB_ADDR), + cpu_to_be32(LPC_IO_OPB_SIZE), + cpu_to_be32(3), 0, + cpu_to_be32(LPC_FW_OPB_ADDR), + cpu_to_be32(LPC_FW_OPB_SIZE), }; uint32_t reg[2]; @@ -211,6 +221,8 @@ int pnv_dt_lpc(PnvChip *chip, void *fdt, int root_offset) _FDT((fdt_setprop_cell(fdt, offset, "#size-cells", 1))); _FDT((fdt_setprop(fdt, offset, "compatible", lpc_compat, sizeof(lpc_compat)))); + _FDT((fdt_setprop(fdt, offset, "ranges", lpc_ranges, + sizeof(lpc_ranges)))); return 0; } @@ -226,16 +238,16 @@ static bool opb_read(PnvLpcController *lpc, uint32_t addr, uint8_t *data, int sz) { /* XXX Handle access size limits and FW read caching here */ - return !address_space_rw(&lpc->opb_as, addr, MEMTXATTRS_UNSPECIFIED, - data, sz, false); + return !address_space_read(&lpc->opb_as, addr, MEMTXATTRS_UNSPECIFIED, + data, sz); } static bool opb_write(PnvLpcController *lpc, uint32_t addr, uint8_t *data, int sz) { /* XXX Handle access size limits here */ - return !address_space_rw(&lpc->opb_as, addr, MEMTXATTRS_UNSPECIFIED, - data, sz, true); + return !address_space_write(&lpc->opb_as, addr, MEMTXATTRS_UNSPECIFIED, + data, sz); } #define ECCB_CTL_READ PPC_BIT(15) @@ -679,20 +691,24 @@ static const TypeInfo pnv_lpc_power9_info = { .class_init = pnv_lpc_power9_class_init, }; +static void pnv_lpc_power10_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->desc = "PowerNV LPC Controller POWER10"; +} + +static const TypeInfo pnv_lpc_power10_info = { + .name = TYPE_PNV10_LPC, + .parent = TYPE_PNV9_LPC, + .class_init = pnv_lpc_power10_class_init, +}; + static void pnv_lpc_realize(DeviceState *dev, Error **errp) { PnvLpcController *lpc = PNV_LPC(dev); - Object *obj; - Error *local_err = NULL; - obj = object_property_get_link(OBJECT(dev), "psi", &local_err); - if (!obj) { - error_propagate(errp, local_err); - error_prepend(errp, "required link 'psi' not found: "); - return; - } - /* The LPC controller needs PSI to generate interrupts */ - lpc->psi = PNV_PSI(obj); + assert(lpc->psi); /* Reg inits */ lpc->lpc_hc_fw_rd_acc_size = LPC_HC_FW_RD_4B; @@ -734,12 +750,19 @@ static void pnv_lpc_realize(DeviceState *dev, Error **errp) &lpc->lpc_hc_regs); } +static Property pnv_lpc_properties[] = { + DEFINE_PROP_LINK("psi", PnvLpcController, psi, TYPE_PNV_PSI, PnvPsi *), + DEFINE_PROP_END_OF_LIST(), +}; + static void pnv_lpc_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = pnv_lpc_realize; dc->desc = "PowerNV LPC Controller"; + device_class_set_props(dc, pnv_lpc_properties); + dc->user_creatable = false; } static const TypeInfo pnv_lpc_info = { @@ -755,6 +778,7 @@ static void pnv_lpc_register_types(void) type_register_static(&pnv_lpc_info); type_register_static(&pnv_lpc_power8_info); type_register_static(&pnv_lpc_power9_info); + type_register_static(&pnv_lpc_power10_info); } type_init(pnv_lpc_register_types) @@ -801,9 +825,11 @@ ISABus *pnv_lpc_isa_create(PnvLpcController *lpc, bool use_cpld, Error **errp) ISABus *isa_bus; qemu_irq *irqs; qemu_irq_handler handler; + PnvMachineState *pnv = PNV_MACHINE(qdev_get_machine()); + bool hostboot_mode = !!pnv->fw_load_addr; /* let isa_bus_new() create its own bridge on SysBus otherwise - * devices speficied on the command line won't find the bus and + * devices specified on the command line won't find the bus and * will fail to create. */ isa_bus = isa_bus_new(NULL, &lpc->isa_mem, &lpc->isa_io, &local_err); @@ -825,5 +851,19 @@ ISABus *pnv_lpc_isa_create(PnvLpcController *lpc, bool use_cpld, Error **errp) irqs = qemu_allocate_irqs(handler, lpc, ISA_NUM_IRQS); isa_bus_irqs(isa_bus, irqs); + + /* + * TODO: Map PNOR on the LPC FW address space on demand ? + */ + memory_region_add_subregion(&lpc->isa_fw, PNOR_SPI_OFFSET, + &pnv->pnor->mmio); + /* + * Start disabled. The HIOMAP protocol will activate the mapping + * with HIOMAP_C_CREATE_WRITE_WINDOW + */ + if (!hostboot_mode) { + memory_region_set_enabled(&pnv->pnor->mmio, false); + } + return isa_bus; } diff --git a/hw/ppc/pnv_occ.c b/hw/ppc/pnv_occ.c index 785653bb67..5a716c256e 100644 --- a/hw/ppc/pnv_occ.c +++ b/hw/ppc/pnv_occ.c @@ -21,7 +21,7 @@ #include "qapi/error.h" #include "qemu/log.h" #include "qemu/module.h" - +#include "hw/qdev-properties.h" #include "hw/ppc/pnv.h" #include "hw/ppc/pnv_xscom.h" #include "hw/ppc/pnv_occ.h" @@ -167,9 +167,7 @@ static void pnv_occ_power8_class_init(ObjectClass *klass, void *data) PnvOCCClass *poc = PNV_OCC_CLASS(klass); poc->xscom_size = PNV_XSCOM_OCC_SIZE; - poc->sram_size = PNV_OCC_COMMON_AREA_SIZE; poc->xscom_ops = &pnv_occ_power8_xscom_ops; - poc->sram_ops = &pnv_occ_sram_ops; poc->psi_irq = PSIHB_IRQ_OCC; } @@ -240,9 +238,7 @@ static void pnv_occ_power9_class_init(ObjectClass *klass, void *data) PnvOCCClass *poc = PNV_OCC_CLASS(klass); poc->xscom_size = PNV9_XSCOM_OCC_SIZE; - poc->sram_size = PNV9_OCC_COMMON_AREA_SIZE; poc->xscom_ops = &pnv_occ_power9_xscom_ops; - poc->sram_ops = &pnv_occ_sram_ops; poc->psi_irq = PSIHB9_IRQ_OCC; } @@ -257,34 +253,34 @@ static void pnv_occ_realize(DeviceState *dev, Error **errp) { PnvOCC *occ = PNV_OCC(dev); PnvOCCClass *poc = PNV_OCC_GET_CLASS(occ); - Object *obj; - Error *local_err = NULL; + + assert(occ->psi); occ->occmisc = 0; - obj = object_property_get_link(OBJECT(dev), "psi", &local_err); - if (!obj) { - error_propagate(errp, local_err); - error_prepend(errp, "required link 'psi' not found: "); - return; - } - occ->psi = PNV_PSI(obj); - /* XScom region for OCC registers */ pnv_xscom_region_init(&occ->xscom_regs, OBJECT(dev), poc->xscom_ops, occ, "xscom-occ", poc->xscom_size); - /* XScom region for OCC SRAM registers */ - pnv_xscom_region_init(&occ->sram_regs, OBJECT(dev), poc->sram_ops, - occ, "occ-common-area", poc->sram_size); + /* OCC common area mmio region for OCC SRAM registers */ + memory_region_init_io(&occ->sram_regs, OBJECT(dev), &pnv_occ_sram_ops, + occ, "occ-common-area", + PNV_OCC_SENSOR_DATA_BLOCK_SIZE); } +static Property pnv_occ_properties[] = { + DEFINE_PROP_LINK("psi", PnvOCC, psi, TYPE_PNV_PSI, PnvPsi *), + DEFINE_PROP_END_OF_LIST(), +}; + static void pnv_occ_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = pnv_occ_realize; dc->desc = "PowerNV OCC Controller"; + device_class_set_props(dc, pnv_occ_properties); + dc->user_creatable = false; } static const TypeInfo pnv_occ_type_info = { diff --git a/hw/ppc/pnv_pnor.c b/hw/ppc/pnv_pnor.c new file mode 100644 index 0000000000..c365ee58b8 --- /dev/null +++ b/hw/ppc/pnv_pnor.c @@ -0,0 +1,141 @@ +/* + * QEMU PowerNV PNOR simple model + * + * Copyright (c) 2015-2019, IBM Corporation. + * + * This code is licensed under the GPL version 2 or later. See the + * COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu/error-report.h" +#include "qemu/log.h" +#include "qemu/units.h" +#include "sysemu/block-backend.h" +#include "sysemu/blockdev.h" +#include "hw/loader.h" +#include "hw/ppc/pnv_pnor.h" +#include "hw/qdev-properties.h" + +static uint64_t pnv_pnor_read(void *opaque, hwaddr addr, unsigned size) +{ + PnvPnor *s = PNV_PNOR(opaque); + uint64_t ret = 0; + int i; + + for (i = 0; i < size; i++) { + ret |= (uint64_t) s->storage[addr + i] << (8 * (size - i - 1)); + } + + return ret; +} + +static void pnv_pnor_update(PnvPnor *s, int offset, int size) +{ + int offset_end; + int ret; + + if (s->blk) { + return; + } + + offset_end = offset + size; + offset = QEMU_ALIGN_DOWN(offset, BDRV_SECTOR_SIZE); + offset_end = QEMU_ALIGN_UP(offset_end, BDRV_SECTOR_SIZE); + + ret = blk_pwrite(s->blk, offset, s->storage + offset, + offset_end - offset, 0); + if (ret < 0) { + error_report("Could not update PNOR offset=0x%" PRIx32" : %s", offset, + strerror(-ret)); + } +} + +static void pnv_pnor_write(void *opaque, hwaddr addr, uint64_t data, + unsigned size) +{ + PnvPnor *s = PNV_PNOR(opaque); + int i; + + for (i = 0; i < size; i++) { + s->storage[addr + i] = (data >> (8 * (size - i - 1))) & 0xFF; + } + pnv_pnor_update(s, addr, size); +} + +/* + * TODO: Check endianness: skiboot is BIG, Aspeed AHB is LITTLE, flash + * is BIG. + */ +static const MemoryRegionOps pnv_pnor_ops = { + .read = pnv_pnor_read, + .write = pnv_pnor_write, + .endianness = DEVICE_BIG_ENDIAN, + .valid = { + .min_access_size = 1, + .max_access_size = 4, + }, +}; + +static void pnv_pnor_realize(DeviceState *dev, Error **errp) +{ + PnvPnor *s = PNV_PNOR(dev); + int ret; + + if (s->blk) { + uint64_t perm = BLK_PERM_CONSISTENT_READ | + (blk_is_read_only(s->blk) ? 0 : BLK_PERM_WRITE); + ret = blk_set_perm(s->blk, perm, BLK_PERM_ALL, errp); + if (ret < 0) { + return; + } + + s->size = blk_getlength(s->blk); + if (s->size <= 0) { + error_setg(errp, "failed to get flash size"); + return; + } + + s->storage = blk_blockalign(s->blk, s->size); + + if (blk_pread(s->blk, 0, s->storage, s->size) != s->size) { + error_setg(errp, "failed to read the initial flash content"); + return; + } + } else { + s->storage = blk_blockalign(NULL, s->size); + memset(s->storage, 0xFF, s->size); + } + + memory_region_init_io(&s->mmio, OBJECT(s), &pnv_pnor_ops, s, + TYPE_PNV_PNOR, s->size); +} + +static Property pnv_pnor_properties[] = { + DEFINE_PROP_INT64("size", PnvPnor, size, 128 * MiB), + DEFINE_PROP_DRIVE("drive", PnvPnor, blk), + DEFINE_PROP_END_OF_LIST(), +}; + +static void pnv_pnor_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = pnv_pnor_realize; + device_class_set_props(dc, pnv_pnor_properties); +} + +static const TypeInfo pnv_pnor_info = { + .name = TYPE_PNV_PNOR, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(PnvPnor), + .class_init = pnv_pnor_class_init, +}; + +static void pnv_pnor_register_types(void) +{ + type_register_static(&pnv_pnor_info); +} + +type_init(pnv_pnor_register_types) diff --git a/hw/ppc/pnv_psi.c b/hw/ppc/pnv_psi.c index 68d0dfacfe..c34a49b000 100644 --- a/hw/ppc/pnv_psi.c +++ b/hw/ppc/pnv_psi.c @@ -455,7 +455,7 @@ static const MemoryRegionOps pnv_psi_xscom_ops = { } }; -static void pnv_psi_reset(void *dev) +static void pnv_psi_reset(DeviceState *dev) { PnvPsi *psi = PNV_PSI(dev); @@ -464,12 +464,29 @@ static void pnv_psi_reset(void *dev) psi->regs[PSIHB_XSCOM_BAR] = psi->bar | PSIHB_BAR_EN; } +static void pnv_psi_reset_handler(void *dev) +{ + device_legacy_reset(DEVICE(dev)); +} + +static void pnv_psi_realize(DeviceState *dev, Error **errp) +{ + PnvPsi *psi = PNV_PSI(dev); + + /* Default BAR for MMIO region */ + pnv_psi_set_bar(psi, psi->bar | PSIHB_BAR_EN); + + qemu_register_reset(pnv_psi_reset_handler, dev); +} + static void pnv_psi_power8_instance_init(Object *obj) { Pnv8Psi *psi8 = PNV8_PSI(obj); object_initialize_child(obj, "ics-psi", &psi8->ics, sizeof(psi8->ics), TYPE_ICS, &error_abort, NULL); + object_property_add_alias(obj, ICS_PROP_XICS, OBJECT(&psi8->ics), + ICS_PROP_XICS, &error_abort); } static const uint8_t irq_to_xivr[] = { @@ -485,20 +502,10 @@ static void pnv_psi_power8_realize(DeviceState *dev, Error **errp) { PnvPsi *psi = PNV_PSI(dev); ICSState *ics = &PNV8_PSI(psi)->ics; - Object *obj; Error *err = NULL; unsigned int i; - obj = object_property_get_link(OBJECT(dev), "xics", &err); - if (!obj) { - error_setg(errp, "%s: required link 'xics' not found: %s", - __func__, error_get_pretty(err)); - return; - } - /* Create PSI interrupt control source */ - object_property_add_const_link(OBJECT(ics), ICS_PROP_XICS, obj, - &error_abort); object_property_set_int(OBJECT(ics), PSI_NUM_INTERRUPTS, "nr-irqs", &err); if (err) { error_propagate(errp, err); @@ -524,9 +531,6 @@ static void pnv_psi_power8_realize(DeviceState *dev, Error **errp) memory_region_init_io(&psi->regs_mr, OBJECT(dev), &psi_mmio_ops, psi, "psihb", PNV_PSIHB_SIZE); - /* Default BAR for MMIO region */ - pnv_psi_set_bar(psi, psi->bar | PSIHB_BAR_EN); - /* Default sources in XIVR */ for (i = 0; i < PSI_NUM_INTERRUPTS; i++) { uint8_t xivr = irq_to_xivr[i]; @@ -534,12 +538,9 @@ static void pnv_psi_power8_realize(DeviceState *dev, Error **errp) ((uint64_t) i << PSIHB_XIVR_SRC_SH); } - qemu_register_reset(pnv_psi_reset, dev); + pnv_psi_realize(dev, errp); } -static const char compat_p8[] = "ibm,power8-psihb-x\0ibm,psihb-x"; -static const char compat_p9[] = "ibm,power9-psihb-x\0ibm,psihb-x"; - static int pnv_psi_dt_xscom(PnvXScomInterface *dev, void *fdt, int xscom_offset) { PnvPsiClass *ppc = PNV_PSI_GET_CLASS(dev); @@ -558,13 +559,8 @@ static int pnv_psi_dt_xscom(PnvXScomInterface *dev, void *fdt, int xscom_offset) _FDT(fdt_setprop(fdt, offset, "reg", reg, sizeof(reg))); _FDT(fdt_setprop_cell(fdt, offset, "#address-cells", 2)); _FDT(fdt_setprop_cell(fdt, offset, "#size-cells", 1)); - if (ppc->chip_type == PNV_CHIP_POWER9) { - _FDT(fdt_setprop(fdt, offset, "compatible", compat_p9, - sizeof(compat_p9))); - } else { - _FDT(fdt_setprop(fdt, offset, "compatible", compat_p8, - sizeof(compat_p8))); - } + _FDT(fdt_setprop(fdt, offset, "compatible", ppc->compat, + ppc->compat_size)); return 0; } @@ -578,15 +574,17 @@ static void pnv_psi_power8_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PnvPsiClass *ppc = PNV_PSI_CLASS(klass); + static const char compat[] = "ibm,power8-psihb-x\0ibm,psihb-x"; dc->desc = "PowerNV PSI Controller POWER8"; dc->realize = pnv_psi_power8_realize; - ppc->chip_type = PNV_CHIP_POWER8; ppc->xscom_pcba = PNV_XSCOM_PSIHB_BASE; ppc->xscom_size = PNV_XSCOM_PSIHB_SIZE; ppc->bar_mask = PSIHB_BAR_MASK; ppc->irq_set = pnv_psi_power8_irq_set; + ppc->compat = compat; + ppc->compat_size = sizeof(compat); } static const TypeInfo pnv_psi_power8_info = { @@ -609,9 +607,12 @@ static const TypeInfo pnv_psi_power8_info = { #define PSIHB9_IRQ_METHOD PPC_BIT(0) #define PSIHB9_IRQ_RESET PPC_BIT(1) #define PSIHB9_ESB_CI_BASE 0x60 -#define PSIHB9_ESB_CI_VALID 1 +#define PSIHB9_ESB_CI_64K PPC_BIT(1) +#define PSIHB9_ESB_CI_ADDR_MASK PPC_BITMASK(8, 47) +#define PSIHB9_ESB_CI_VALID PPC_BIT(63) #define PSIHB9_ESB_NOTIF_ADDR 0x68 -#define PSIHB9_ESB_NOTIF_VALID 1 +#define PSIHB9_ESB_NOTIF_ADDR_MASK PPC_BITMASK(8, 60) +#define PSIHB9_ESB_NOTIF_VALID PPC_BIT(63) #define PSIHB9_IVT_OFFSET 0x70 #define PSIHB9_IVT_OFF_SHIFT 32 @@ -714,7 +715,7 @@ static void pnv_psi_p9_mmio_write(void *opaque, hwaddr addr, break; case PSIHB9_INTERRUPT_CONTROL: if (val & PSIHB9_IRQ_RESET) { - device_reset(DEVICE(&psi9->source)); + device_legacy_reset(DEVICE(&psi9->source)); } psi->regs[reg] = val; break; @@ -820,7 +821,7 @@ static void pnv_psi_power9_irq_set(PnvPsi *psi, int irq, bool state) qemu_set_irq(psi->qirqs[irq], state); } -static void pnv_psi_power9_reset(void *dev) +static void pnv_psi_power9_reset(DeviceState *dev) { Pnv9Psi *psi = PNV9_PSI(dev); @@ -851,8 +852,7 @@ static void pnv_psi_power9_realize(DeviceState *dev, Error **errp) &error_fatal); object_property_set_int(OBJECT(xsrc), PSIHB9_NUM_IRQS, "nr-irqs", &error_fatal); - object_property_add_const_link(OBJECT(xsrc), "xive", OBJECT(psi), - &error_fatal); + object_property_set_link(OBJECT(xsrc), OBJECT(psi), "xive", &error_abort); object_property_set_bool(OBJECT(xsrc), true, "realized", &local_err); if (local_err) { error_propagate(errp, local_err); @@ -873,9 +873,7 @@ static void pnv_psi_power9_realize(DeviceState *dev, Error **errp) memory_region_init_io(&psi->regs_mr, OBJECT(dev), &pnv_psi_p9_mmio_ops, psi, "psihb", PNV9_PSIHB_SIZE); - pnv_psi_set_bar(psi, psi->bar | PSIHB_BAR_EN); - - qemu_register_reset(pnv_psi_power9_reset, dev); + pnv_psi_realize(dev, errp); } static void pnv_psi_power9_class_init(ObjectClass *klass, void *data) @@ -883,15 +881,18 @@ static void pnv_psi_power9_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); PnvPsiClass *ppc = PNV_PSI_CLASS(klass); XiveNotifierClass *xfc = XIVE_NOTIFIER_CLASS(klass); + static const char compat[] = "ibm,power9-psihb-x\0ibm,psihb-x"; dc->desc = "PowerNV PSI Controller POWER9"; dc->realize = pnv_psi_power9_realize; + dc->reset = pnv_psi_power9_reset; - ppc->chip_type = PNV_CHIP_POWER9; ppc->xscom_pcba = PNV9_XSCOM_PSIHB_BASE; ppc->xscom_size = PNV9_XSCOM_PSIHB_SIZE; ppc->bar_mask = PSIHB9_BAR_MASK; ppc->irq_set = pnv_psi_power9_irq_set; + ppc->compat = compat; + ppc->compat_size = sizeof(compat); xfc->notify = pnv_psi_notify; } @@ -908,6 +909,26 @@ static const TypeInfo pnv_psi_power9_info = { }, }; +static void pnv_psi_power10_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PnvPsiClass *ppc = PNV_PSI_CLASS(klass); + static const char compat[] = "ibm,power10-psihb-x\0ibm,psihb-x"; + + dc->desc = "PowerNV PSI Controller POWER10"; + + ppc->xscom_pcba = PNV10_XSCOM_PSIHB_BASE; + ppc->xscom_size = PNV10_XSCOM_PSIHB_SIZE; + ppc->compat = compat; + ppc->compat_size = sizeof(compat); +} + +static const TypeInfo pnv_psi_power10_info = { + .name = TYPE_PNV10_PSI, + .parent = TYPE_PNV9_PSI, + .class_init = pnv_psi_power10_class_init, +}; + static void pnv_psi_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -916,7 +937,8 @@ static void pnv_psi_class_init(ObjectClass *klass, void *data) xdc->dt_xscom = pnv_psi_dt_xscom; dc->desc = "PowerNV PSI Controller"; - dc->props = pnv_psi_properties; + device_class_set_props(dc, pnv_psi_properties); + dc->reset = pnv_psi_reset; } static const TypeInfo pnv_psi_info = { @@ -937,6 +959,7 @@ static void pnv_psi_register_types(void) type_register_static(&pnv_psi_info); type_register_static(&pnv_psi_power8_info); type_register_static(&pnv_psi_power9_info); + type_register_static(&pnv_psi_power10_info); } type_init(pnv_psi_register_types); diff --git a/hw/ppc/pnv_xscom.c b/hw/ppc/pnv_xscom.c index f01d788a65..b681c72575 100644 --- a/hw/ppc/pnv_xscom.c +++ b/hw/ppc/pnv_xscom.c @@ -36,16 +36,6 @@ #define PRD_P9_IPOLL_REG_MASK 0x000F0033 #define PRD_P9_IPOLL_REG_STATUS 0x000F0034 -/* PBA BARs */ -#define P8_PBA_BAR0 0x2013f00 -#define P8_PBA_BAR2 0x2013f02 -#define P8_PBA_BARMASK0 0x2013f04 -#define P8_PBA_BARMASK2 0x2013f06 -#define P9_PBA_BAR0 0x5012b00 -#define P9_PBA_BAR2 0x5012b02 -#define P9_PBA_BARMASK0 0x5012b04 -#define P9_PBA_BARMASK2 0x5012b06 - static void xscom_complete(CPUState *cs, uint64_t hmer_bits) { /* @@ -67,13 +57,7 @@ static void xscom_complete(CPUState *cs, uint64_t hmer_bits) static uint32_t pnv_xscom_pcba(PnvChip *chip, uint64_t addr) { - addr &= (PNV_XSCOM_SIZE - 1); - - if (pnv_chip_is_power9(chip)) { - return addr >> 3; - } else { - return ((addr >> 4) & ~0xfull) | ((addr >> 3) & 0xf); - } + return PNV_CHIP_GET_CLASS(chip)->xscom_pcba(chip, addr); } static uint64_t xscom_read_default(PnvChip *chip, uint32_t pcba) @@ -84,26 +68,6 @@ static uint64_t xscom_read_default(PnvChip *chip, uint32_t pcba) case 0x18002: /* ECID2 */ return 0; - case P9_PBA_BAR0: - return PNV9_HOMER_BASE(chip); - case P8_PBA_BAR0: - return PNV_HOMER_BASE(chip); - - case P9_PBA_BARMASK0: /* P9 homer region size */ - return PNV9_HOMER_SIZE; - case P8_PBA_BARMASK0: /* P8 homer region size */ - return PNV_HOMER_SIZE; - - case P9_PBA_BAR2: /* P9 occ common area */ - return PNV9_OCC_COMMON_AREA(chip); - case P8_PBA_BAR2: /* P8 occ common area */ - return PNV_OCC_COMMON_AREA(chip); - - case P9_PBA_BARMASK2: /* P9 occ common area size */ - return PNV9_OCC_COMMON_AREA_SIZE; - case P8_PBA_BARMASK2: /* P8 occ common area size */ - return PNV_OCC_COMMON_AREA_SIZE; - case 0x1010c00: /* PIBAM FIR */ case 0x1010c03: /* PIBAM FIR MASK */ @@ -124,9 +88,7 @@ static uint64_t xscom_read_default(PnvChip *chip, uint32_t pcba) case 0x202000f: /* ADU stuff, receive status register*/ return 0; case 0x2013f01: /* PBA stuff */ - case 0x2013f03: /* PBA stuff */ case 0x2013f05: /* PBA stuff */ - case 0x2013f07: /* PBA stuff */ return 0; case 0x2013028: /* CAPP stuff */ case 0x201302a: /* CAPP stuff */ @@ -298,31 +260,25 @@ static int xscom_dt_child(Object *child, void *opaque) PnvXScomInterface *xd = PNV_XSCOM_INTERFACE(child); PnvXScomInterfaceClass *xc = PNV_XSCOM_INTERFACE_GET_CLASS(xd); - if (xc->dt_xscom) { + /* + * Only "realized" devices should be configured in the DT + */ + if (xc->dt_xscom && DEVICE(child)->realized) { _FDT((xc->dt_xscom(xd, args->fdt, args->xscom_offset))); } } return 0; } -static const char compat_p8[] = "ibm,power8-xscom\0ibm,xscom"; -static const char compat_p9[] = "ibm,power9-xscom\0ibm,xscom"; - -int pnv_dt_xscom(PnvChip *chip, void *fdt, int root_offset) +int pnv_dt_xscom(PnvChip *chip, void *fdt, int root_offset, + uint64_t xscom_base, uint64_t xscom_size, + const char *compat, int compat_size) { - uint64_t reg[2]; + uint64_t reg[] = { xscom_base, xscom_size }; int xscom_offset; ForeachPopulateArgs args; char *name; - if (pnv_chip_is_power9(chip)) { - reg[0] = cpu_to_be64(PNV9_XSCOM_BASE(chip)); - reg[1] = cpu_to_be64(PNV9_XSCOM_SIZE); - } else { - reg[0] = cpu_to_be64(PNV_XSCOM_BASE(chip)); - reg[1] = cpu_to_be64(PNV_XSCOM_SIZE); - } - name = g_strdup_printf("xscom@%" PRIx64, be64_to_cpu(reg[0])); xscom_offset = fdt_add_subnode(fdt, root_offset, name); _FDT(xscom_offset); @@ -331,21 +287,18 @@ int pnv_dt_xscom(PnvChip *chip, void *fdt, int root_offset) _FDT((fdt_setprop_cell(fdt, xscom_offset, "#address-cells", 1))); _FDT((fdt_setprop_cell(fdt, xscom_offset, "#size-cells", 1))); _FDT((fdt_setprop(fdt, xscom_offset, "reg", reg, sizeof(reg)))); - - if (pnv_chip_is_power9(chip)) { - _FDT((fdt_setprop(fdt, xscom_offset, "compatible", compat_p9, - sizeof(compat_p9)))); - } else { - _FDT((fdt_setprop(fdt, xscom_offset, "compatible", compat_p8, - sizeof(compat_p8)))); - } - + _FDT((fdt_setprop(fdt, xscom_offset, "compatible", compat, compat_size))); _FDT((fdt_setprop(fdt, xscom_offset, "scom-controller", NULL, 0))); args.fdt = fdt; args.xscom_offset = xscom_offset; - object_child_foreach(OBJECT(chip), xscom_dt_child, &args); + /* + * Loop on the whole object hierarchy to catch all + * PnvXScomInterface objects which can lie a bit deeper than the + * first layer. + */ + object_child_foreach_recursive(OBJECT(chip), xscom_dt_child, &args); return 0; } diff --git a/hw/ppc/ppc.c b/hw/ppc/ppc.c index 52a18eb7d7..4a11fb1640 100644 --- a/hw/ppc/ppc.c +++ b/hw/ppc/ppc.c @@ -275,10 +275,9 @@ void ppc970_irq_init(PowerPCCPU *cpu) static void power7_set_irq(void *opaque, int pin, int level) { PowerPCCPU *cpu = opaque; - CPUPPCState *env = &cpu->env; LOG_IRQ("%s: env %p pin %d level %d\n", __func__, - env, pin, level); + &cpu->env, pin, level); switch (pin) { case POWER7_INPUT_INT: @@ -292,11 +291,6 @@ static void power7_set_irq(void *opaque, int pin, int level) LOG_IRQ("%s: unknown IRQ pin %d\n", __func__, pin); return; } - if (level) { - env->irq_input_state |= 1 << pin; - } else { - env->irq_input_state &= ~(1 << pin); - } } void ppcPOWER7_irq_init(PowerPCCPU *cpu) @@ -311,10 +305,9 @@ void ppcPOWER7_irq_init(PowerPCCPU *cpu) static void power9_set_irq(void *opaque, int pin, int level) { PowerPCCPU *cpu = opaque; - CPUPPCState *env = &cpu->env; LOG_IRQ("%s: env %p pin %d level %d\n", __func__, - env, pin, level); + &cpu->env, pin, level); switch (pin) { case POWER9_INPUT_INT: @@ -334,11 +327,6 @@ static void power9_set_irq(void *opaque, int pin, int level) LOG_IRQ("%s: unknown IRQ pin %d\n", __func__, pin); return; } - if (level) { - env->irq_input_state |= 1 << pin; - } else { - env->irq_input_state &= ~(1 << pin); - } } void ppcPOWER9_irq_init(PowerPCCPU *cpu) @@ -694,6 +682,35 @@ void cpu_ppc_store_atbu (CPUPPCState *env, uint32_t value) &tb_env->atb_offset, ((uint64_t)value << 32) | tb); } +uint64_t cpu_ppc_load_vtb(CPUPPCState *env) +{ + ppc_tb_t *tb_env = env->tb_env; + + return cpu_ppc_get_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), + tb_env->vtb_offset); +} + +void cpu_ppc_store_vtb(CPUPPCState *env, uint64_t value) +{ + ppc_tb_t *tb_env = env->tb_env; + + cpu_ppc_store_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), + &tb_env->vtb_offset, value); +} + +void cpu_ppc_store_tbu40(CPUPPCState *env, uint64_t value) +{ + ppc_tb_t *tb_env = env->tb_env; + uint64_t tb; + + tb = cpu_ppc_get_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), + tb_env->tb_offset); + tb &= 0xFFFFFFUL; + tb |= (value & ~0xFFFFFFUL); + cpu_ppc_store_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), + &tb_env->tb_offset, tb); +} + static void cpu_ppc_tb_stop (CPUPPCState *env) { ppc_tb_t *tb_env = env->tb_env; @@ -805,12 +822,9 @@ target_ulong cpu_ppc_load_hdecr(CPUPPCState *env) uint64_t cpu_ppc_load_purr (CPUPPCState *env) { ppc_tb_t *tb_env = env->tb_env; - uint64_t diff; - diff = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - tb_env->purr_start; - - return tb_env->purr_load + - muldiv64(diff, tb_env->tb_freq, NANOSECONDS_PER_SECOND); + return cpu_ppc_get_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), + tb_env->purr_offset); } /* When decrementer expires, @@ -969,12 +983,12 @@ static void cpu_ppc_hdecr_cb(void *opaque) cpu_ppc_hdecr_excp(cpu); } -static void cpu_ppc_store_purr(PowerPCCPU *cpu, uint64_t value) +void cpu_ppc_store_purr(CPUPPCState *env, uint64_t value) { - ppc_tb_t *tb_env = cpu->env.tb_env; + ppc_tb_t *tb_env = env->tb_env; - tb_env->purr_load = value; - tb_env->purr_start = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + cpu_ppc_store_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), + &tb_env->purr_offset, value); } static void cpu_ppc_set_tb_clk (void *opaque, uint32_t freq) @@ -991,7 +1005,7 @@ static void cpu_ppc_set_tb_clk (void *opaque, uint32_t freq) */ _cpu_ppc_store_decr(cpu, 0xFFFFFFFF, 0xFFFFFFFF, 32); _cpu_ppc_store_hdecr(cpu, 0xFFFFFFFF, 0xFFFFFFFF, 32); - cpu_ppc_store_purr(cpu, 0x0000000000000000ULL); + cpu_ppc_store_purr(env, 0x0000000000000000ULL); } static void timebase_save(PPCTimebase *tb) @@ -1476,23 +1490,11 @@ int ppc_dcr_init (CPUPPCState *env, int (*read_error)(int dcrn), } /*****************************************************************************/ -/* Debug port */ -void PPC_debug_write (void *opaque, uint32_t addr, uint32_t val) + +int ppc_cpu_pir(PowerPCCPU *cpu) { - addr &= 0xF; - switch (addr) { - case 0: - printf("%c", val); - break; - case 1: - printf("\n"); - fflush(stdout); - break; - case 2: - printf("Set loglevel to %04" PRIx32 "\n", val); - qemu_set_log(val | 0x100); - break; - } + CPUPPCState *env = &cpu->env; + return env->spr_cb[SPR_PIR].default_value; } PowerPCCPU *ppc_get_vcpu_by_pir(int pir) @@ -1501,12 +1503,19 @@ PowerPCCPU *ppc_get_vcpu_by_pir(int pir) CPU_FOREACH(cs) { PowerPCCPU *cpu = POWERPC_CPU(cs); - CPUPPCState *env = &cpu->env; - if (env->spr_cb[SPR_PIR].default_value == pir) { + if (ppc_cpu_pir(cpu) == pir) { return cpu; } } return NULL; } + +void ppc_irq_reset(PowerPCCPU *cpu) +{ + CPUPPCState *env = &cpu->env; + + env->irq_input_state = 0; + kvmppc_set_interrupt(cpu, PPC_INTERRUPT_EXT, 0); +} diff --git a/hw/ppc/ppc405_boards.c b/hw/ppc/ppc405_boards.c index 1f721feed6..6198ec1035 100644 --- a/hw/ppc/ppc405_boards.c +++ b/hw/ppc/ppc405_boards.c @@ -40,6 +40,7 @@ #include "qemu/error-report.h" #include "hw/loader.h" #include "exec/address-spaces.h" +#include "qemu/cutils.h" #define BIOS_FILENAME "ppc405_rom.bin" #define BIOS_SIZE (2 * MiB) @@ -137,7 +138,7 @@ static void ref405ep_fpga_init(MemoryRegion *sysmem, uint32_t base) static void ref405ep_init(MachineState *machine) { - ram_addr_t ram_size = machine->ram_size; + MachineClass *mc = MACHINE_GET_CLASS(machine); const char *kernel_filename = machine->kernel_filename; const char *kernel_cmdline = machine->kernel_cmdline; const char *initrd_filename = machine->initrd_filename; @@ -161,15 +162,21 @@ static void ref405ep_init(MachineState *machine) DriveInfo *dinfo; MemoryRegion *sysmem = get_system_memory(); + if (machine->ram_size != mc->default_ram_size) { + char *sz = size_to_str(mc->default_ram_size); + error_report("Invalid RAM size, should be %s", sz); + g_free(sz); + exit(EXIT_FAILURE); + } + /* XXX: fix this */ - memory_region_allocate_system_memory(&ram_memories[0], NULL, "ef405ep.ram", - 0x08000000); + memory_region_init_alias(&ram_memories[0], NULL, "ef405ep.ram.alias", + machine->ram, 0, machine->ram_size); ram_bases[0] = 0; - ram_sizes[0] = 0x08000000; + ram_sizes[0] = machine->ram_size; memory_region_init(&ram_memories[1], NULL, "ef405ep.ram1", 0); ram_bases[1] = 0x00000000; ram_sizes[1] = 0x00000000; - ram_size = 128 * MiB; env = ppc405ep_init(sysmem, ram_memories, ram_bases, ram_sizes, 33333333, &pic, kernel_filename == NULL ? 0 : 1); /* allocate SRAM */ @@ -184,7 +191,7 @@ static void ref405ep_init(MachineState *machine) bios_size = 8 * MiB; pflash_cfi02_register((uint32_t)(-bios_size), "ef405ep.bios", bios_size, - dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, + blk_by_legacy_dinfo(dinfo), 64 * KiB, 1, 2, 0x0001, 0x22DA, 0x0000, 0x0000, 0x555, 0x2AA, 1); @@ -192,7 +199,7 @@ static void ref405ep_init(MachineState *machine) #endif { bios = g_new(MemoryRegion, 1); - memory_region_init_ram(bios, NULL, "ef405ep.bios", BIOS_SIZE, + memory_region_init_rom(bios, NULL, "ef405ep.bios", BIOS_SIZE, &error_fatal); if (bios_name == NULL) @@ -216,7 +223,6 @@ static void ref405ep_init(MachineState *machine) /* Avoid an uninitialized variable warning */ bios_size = -1; } - memory_region_set_readonly(bios, true); } /* Register FPGA */ ref405ep_fpga_init(sysmem, 0xF0300000); @@ -227,7 +233,7 @@ static void ref405ep_init(MachineState *machine) if (linux_boot) { memset(&bd, 0, sizeof(bd)); bd.bi_memstart = 0x00000000; - bd.bi_memsize = ram_size; + bd.bi_memsize = machine->ram_size; bd.bi_flashstart = -bios_size; bd.bi_flashsize = -bios_size; bd.bi_flashoffset = 0; @@ -255,7 +261,7 @@ static void ref405ep_init(MachineState *machine) kernel_base = KERNEL_LOAD_ADDR; /* now we can load the kernel */ kernel_size = load_image_targphys(kernel_filename, kernel_base, - ram_size - kernel_base); + machine->ram_size - kernel_base); if (kernel_size < 0) { error_report("could not load kernel '%s'", kernel_filename); exit(1); @@ -266,7 +272,7 @@ static void ref405ep_init(MachineState *machine) if (initrd_filename) { initrd_base = INITRD_LOAD_ADDR; initrd_size = load_image_targphys(initrd_filename, initrd_base, - ram_size - initrd_base); + machine->ram_size - initrd_base); if (initrd_size < 0) { error_report("could not load initial ram disk '%s'", initrd_filename); @@ -304,6 +310,8 @@ static void ref405ep_class_init(ObjectClass *oc, void *data) mc->desc = "ref405ep"; mc->init = ref405ep_init; + mc->default_ram_size = 0x08000000; + mc->default_ram_id = "ef405ep.ram"; } static const TypeInfo ref405ep_type = { @@ -408,7 +416,7 @@ static void taihu_cpld_init(MemoryRegion *sysmem, uint32_t base) static void taihu_405ep_init(MachineState *machine) { - ram_addr_t ram_size = machine->ram_size; + MachineClass *mc = MACHINE_GET_CLASS(machine); const char *kernel_filename = machine->kernel_filename; const char *initrd_filename = machine->initrd_filename; char *filename; @@ -416,7 +424,6 @@ static void taihu_405ep_init(MachineState *machine) MemoryRegion *sysmem = get_system_memory(); MemoryRegion *bios; MemoryRegion *ram_memories = g_new(MemoryRegion, 2); - MemoryRegion *ram = g_malloc0(sizeof(*ram)); hwaddr ram_bases[2], ram_sizes[2]; long bios_size; target_ulong kernel_base, initrd_base; @@ -425,20 +432,22 @@ static void taihu_405ep_init(MachineState *machine) int fl_idx; DriveInfo *dinfo; - /* RAM is soldered to the board so the size cannot be changed */ - ram_size = 0x08000000; - memory_region_allocate_system_memory(ram, NULL, "taihu_405ep.ram", - ram_size); + if (machine->ram_size != mc->default_ram_size) { + char *sz = size_to_str(mc->default_ram_size); + error_report("Invalid RAM size, should be %s", sz); + g_free(sz); + exit(EXIT_FAILURE); + } ram_bases[0] = 0; ram_sizes[0] = 0x04000000; memory_region_init_alias(&ram_memories[0], NULL, - "taihu_405ep.ram-0", ram, ram_bases[0], + "taihu_405ep.ram-0", machine->ram, ram_bases[0], ram_sizes[0]); ram_bases[1] = 0x04000000; ram_sizes[1] = 0x04000000; memory_region_init_alias(&ram_memories[1], NULL, - "taihu_405ep.ram-1", ram, ram_bases[1], + "taihu_405ep.ram-1", machine->ram, ram_bases[1], ram_sizes[1]); ppc405ep_init(sysmem, ram_memories, ram_bases, ram_sizes, 33333333, &pic, kernel_filename == NULL ? 0 : 1); @@ -450,7 +459,7 @@ static void taihu_405ep_init(MachineState *machine) bios_size = 2 * MiB; pflash_cfi02_register(0xFFE00000, "taihu_405ep.bios", bios_size, - dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, + blk_by_legacy_dinfo(dinfo), 64 * KiB, 1, 4, 0x0001, 0x22DA, 0x0000, 0x0000, 0x555, 0x2AA, 1); @@ -461,7 +470,7 @@ static void taihu_405ep_init(MachineState *machine) if (bios_name == NULL) bios_name = BIOS_FILENAME; bios = g_new(MemoryRegion, 1); - memory_region_init_ram(bios, NULL, "taihu_405ep.bios", BIOS_SIZE, + memory_region_init_rom(bios, NULL, "taihu_405ep.bios", BIOS_SIZE, &error_fatal); filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); if (filename) { @@ -479,14 +488,13 @@ static void taihu_405ep_init(MachineState *machine) error_report("Could not load PowerPC BIOS '%s'", bios_name); exit(1); } - memory_region_set_readonly(bios, true); } /* Register Linux flash */ dinfo = drive_get(IF_PFLASH, 0, fl_idx); if (dinfo) { bios_size = 32 * MiB; pflash_cfi02_register(0xfc000000, "taihu_405ep.flash", bios_size, - dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, + blk_by_legacy_dinfo(dinfo), 64 * KiB, 1, 4, 0x0001, 0x22DA, 0x0000, 0x0000, 0x555, 0x2AA, 1); @@ -500,7 +508,7 @@ static void taihu_405ep_init(MachineState *machine) kernel_base = KERNEL_LOAD_ADDR; /* now we can load the kernel */ kernel_size = load_image_targphys(kernel_filename, kernel_base, - ram_size - kernel_base); + machine->ram_size - kernel_base); if (kernel_size < 0) { error_report("could not load kernel '%s'", kernel_filename); exit(1); @@ -509,7 +517,7 @@ static void taihu_405ep_init(MachineState *machine) if (initrd_filename) { initrd_base = INITRD_LOAD_ADDR; initrd_size = load_image_targphys(initrd_filename, initrd_base, - ram_size - initrd_base); + machine->ram_size - initrd_base); if (initrd_size < 0) { error_report("could not load initial ram disk '%s'", initrd_filename); @@ -533,6 +541,8 @@ static void taihu_class_init(ObjectClass *oc, void *data) mc->desc = "taihu"; mc->init = taihu_405ep_init; + mc->default_ram_size = 0x08000000; + mc->default_ram_id = "taihu_405ep.ram"; } static const TypeInfo taihu_type = { diff --git a/hw/ppc/ppc440_bamboo.c b/hw/ppc/ppc440_bamboo.c index 4d95c0f8a8..4c5e9e4373 100644 --- a/hw/ppc/ppc440_bamboo.c +++ b/hw/ppc/ppc440_bamboo.c @@ -71,12 +71,12 @@ static int bamboo_load_device_tree(hwaddr addr, filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, BINARY_DEVICE_TREE_FILE); if (!filename) { - goto out; + return -1; } fdt = load_device_tree(filename, &fdt_size); g_free(filename); if (fdt == NULL) { - goto out; + return -1; } /* Manipulate device tree in memory. */ @@ -117,10 +117,6 @@ static int bamboo_load_device_tree(hwaddr addr, rom_add_blob_fixed(BINARY_DEVICE_TREE_FILE, fdt, fdt_size, addr); g_free(fdt); return 0; - -out: - - return ret; } /* Create reset TLB entries for BookE, spanning the 32bit addr space. */ @@ -162,7 +158,6 @@ static void main_cpu_reset(void *opaque) static void bamboo_init(MachineState *machine) { - ram_addr_t ram_size = machine->ram_size; const char *kernel_filename = machine->kernel_filename; const char *kernel_cmdline = machine->kernel_cmdline; const char *initrd_filename = machine->initrd_filename; @@ -207,10 +202,8 @@ static void bamboo_init(MachineState *machine) /* SDRAM controller */ memset(ram_bases, 0, sizeof(ram_bases)); memset(ram_sizes, 0, sizeof(ram_sizes)); - ram_size = ppc4xx_sdram_adjust(ram_size, PPC440EP_SDRAM_NR_BANKS, - ram_memories, - ram_bases, ram_sizes, - ppc440ep_sdram_bank_sizes); + ppc4xx_sdram_banks(machine->ram, PPC440EP_SDRAM_NR_BANKS, ram_memories, + ram_bases, ram_sizes, ppc440ep_sdram_bank_sizes); /* XXX 440EP's ECC interrupts are on UIC1, but we've only created UIC0. */ ppc4xx_sdram_init(env, pic[14], PPC440EP_SDRAM_NR_BANKS, ram_memories, ram_bases, ram_sizes, 1); @@ -257,7 +250,7 @@ static void bamboo_init(MachineState *machine) NULL, NULL); if (success < 0) { success = load_elf(kernel_filename, NULL, NULL, NULL, &elf_entry, - &elf_lowaddr, NULL, 1, PPC_ELF_MACHINE, + &elf_lowaddr, NULL, NULL, 1, PPC_ELF_MACHINE, 0, 0); entry = elf_entry; loadaddr = elf_lowaddr; @@ -272,7 +265,7 @@ static void bamboo_init(MachineState *machine) /* Load initrd. */ if (initrd_filename) { initrd_size = load_image_targphys(initrd_filename, RAMDISK_ADDR, - ram_size - RAMDISK_ADDR); + machine->ram_size - RAMDISK_ADDR); if (initrd_size < 0) { error_report("could not load ram disk '%s' at %x", @@ -283,7 +276,7 @@ static void bamboo_init(MachineState *machine) /* If we're loading a kernel directly, we must load the device tree too. */ if (kernel_filename) { - if (bamboo_load_device_tree(FDT_ADDR, ram_size, RAMDISK_ADDR, + if (bamboo_load_device_tree(FDT_ADDR, machine->ram_size, RAMDISK_ADDR, initrd_size, kernel_cmdline) < 0) { error_report("couldn't load device tree"); exit(1); @@ -296,6 +289,7 @@ static void bamboo_machine_init(MachineClass *mc) mc->desc = "bamboo"; mc->init = bamboo_init; mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("440epb"); + mc->default_ram_id = "ppc4xx.sdram"; } DEFINE_MACHINE("bamboo", bamboo_machine_init) diff --git a/hw/ppc/ppc440_uc.c b/hw/ppc/ppc440_uc.c index 95475c9c5f..b30e093cbb 100644 --- a/hw/ppc/ppc440_uc.c +++ b/hw/ppc/ppc440_uc.c @@ -13,7 +13,6 @@ #include "qemu/error-report.h" #include "qapi/error.h" #include "qemu/log.h" -#include "qemu/main-loop.h" #include "qemu/module.h" #include "cpu.h" #include "hw/irq.h" @@ -909,8 +908,10 @@ static void dcr_write_dma(void *opaque, int dcrn, uint32_t val) sidx = didx = 0; width = 1 << ((val & DMA0_CR_PW) >> 25); - rptr = cpu_physical_memory_map(dma->ch[chnl].sa, &rlen, 0); - wptr = cpu_physical_memory_map(dma->ch[chnl].da, &wlen, 1); + rptr = cpu_physical_memory_map(dma->ch[chnl].sa, &rlen, + false); + wptr = cpu_physical_memory_map(dma->ch[chnl].da, &wlen, + true); if (rptr && wptr) { if (!(val & DMA0_CR_DEC) && val & DMA0_CR_SAI && val & DMA0_CR_DAI) { @@ -1181,9 +1182,7 @@ static void dcr_write_pcie(void *opaque, int dcrn, uint32_t val) case PEGPL_CFGMSK: s->cfg_mask = val; size = ~(val & 0xfffffffe) + 1; - qemu_mutex_lock_iothread(); pcie_host_mmcfg_update(PCIE_HOST_BRIDGE(s), val & 1, s->cfg_base, size); - qemu_mutex_unlock_iothread(); break; case PEGPL_MSGBAH: s->msg_base = ((uint64_t)val << 32) | (s->msg_base & 0xffffffff); @@ -1296,7 +1295,7 @@ static void ppc460ex_pcie_class_init(ObjectClass *klass, void *data) set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); dc->realize = ppc460ex_pcie_realize; - dc->props = ppc460ex_pcie_props; + device_class_set_props(dc, ppc460ex_pcie_props); dc->hotpluggable = false; } diff --git a/hw/ppc/ppc4xx_devs.c b/hw/ppc/ppc4xx_devs.c index c2e50138aa..3376c43ff5 100644 --- a/hw/ppc/ppc4xx_devs.c +++ b/hw/ppc/ppc4xx_devs.c @@ -666,21 +666,22 @@ void ppc4xx_sdram_init (CPUPPCState *env, qemu_irq irq, int nbanks, sdram_map_bcr(sdram); } -/* Fill in consecutive SDRAM banks with 'ram_size' bytes of memory. +/* + * Split RAM between SDRAM banks. * - * sdram_bank_sizes[] must be 0-terminated. + * sdram_bank_sizes[] must be in descending order, that is sizes[i] > sizes[i+1] + * and must be 0-terminated. * * The 4xx SDRAM controller supports a small number of banks, and each bank * must be one of a small set of sizes. The number of banks and the supported - * sizes varies by SoC. */ -ram_addr_t ppc4xx_sdram_adjust(ram_addr_t ram_size, int nr_banks, - MemoryRegion ram_memories[], - hwaddr ram_bases[], - hwaddr ram_sizes[], - const ram_addr_t sdram_bank_sizes[]) + * sizes varies by SoC. + */ +void ppc4xx_sdram_banks(MemoryRegion *ram, int nr_banks, + MemoryRegion ram_memories[], + hwaddr ram_bases[], hwaddr ram_sizes[], + const ram_addr_t sdram_bank_sizes[]) { - MemoryRegion *ram = g_malloc0(sizeof(*ram)); - ram_addr_t size_left = ram_size; + ram_addr_t size_left = memory_region_size(ram); ram_addr_t base = 0; ram_addr_t bank_size; int i; @@ -690,7 +691,16 @@ ram_addr_t ppc4xx_sdram_adjust(ram_addr_t ram_size, int nr_banks, for (j = 0; sdram_bank_sizes[j] != 0; j++) { bank_size = sdram_bank_sizes[j]; if (bank_size <= size_left) { + char name[32]; + + ram_bases[i] = base; + ram_sizes[i] = bank_size; + base += bank_size; size_left -= bank_size; + snprintf(name, sizeof(name), "ppc4xx.sdram%d", i); + memory_region_init_alias(&ram_memories[i], NULL, name, ram, + ram_bases[i], ram_sizes[i]); + break; } } if (!size_left) { @@ -699,34 +709,23 @@ ram_addr_t ppc4xx_sdram_adjust(ram_addr_t ram_size, int nr_banks, } } - ram_size -= size_left; if (size_left) { - error_report("Truncating memory to %" PRId64 " MiB to fit SDRAM" - " controller limits", ram_size / MiB); - } + ram_addr_t used_size = memory_region_size(ram) - size_left; + GString *s = g_string_new(NULL); - memory_region_allocate_system_memory(ram, NULL, "ppc4xx.sdram", ram_size); - - size_left = ram_size; - for (i = 0; i < nr_banks && size_left; i++) { - for (j = 0; sdram_bank_sizes[j] != 0; j++) { - bank_size = sdram_bank_sizes[j]; - - if (bank_size <= size_left) { - char name[32]; - snprintf(name, sizeof(name), "ppc4xx.sdram%d", i); - memory_region_init_alias(&ram_memories[i], NULL, name, ram, - base, bank_size); - ram_bases[i] = base; - ram_sizes[i] = bank_size; - base += bank_size; - size_left -= bank_size; - break; - } + for (i = 0; sdram_bank_sizes[i]; i++) { + g_string_append_printf(s, "%" PRIi64 "%s", + sdram_bank_sizes[i] / MiB, + sdram_bank_sizes[i + 1] ? " ," : ""); } - } + error_report("Max %d banks of %s MB DIMM/bank supported", + nr_banks, s->str); + error_report("Possible valid RAM size: %" PRIi64, + used_size ? used_size / MiB : sdram_bank_sizes[i - 1] / MiB); - return ram_size; + g_string_free(s, true); + exit(EXIT_FAILURE); + } } /*****************************************************************************/ diff --git a/hw/ppc/prep.c b/hw/ppc/prep.c index 862345c2ac..44be9d25a2 100644 --- a/hw/ppc/prep.c +++ b/hw/ppc/prep.c @@ -37,12 +37,11 @@ #include "hw/boards.h" #include "qemu/error-report.h" #include "qemu/log.h" -#include "hw/ide.h" #include "hw/irq.h" #include "hw/loader.h" #include "hw/rtc/mc146818rtc.h" #include "hw/isa/pc87312.h" -#include "hw/net/ne2000-isa.h" +#include "hw/qdev-properties.h" #include "sysemu/arch_init.h" #include "sysemu/kvm.h" #include "sysemu/qtest.h" @@ -60,178 +59,9 @@ #define CFG_ADDR 0xf0000510 -#define BIOS_SIZE (1 * MiB) -#define BIOS_FILENAME "ppc_rom.bin" #define KERNEL_LOAD_ADDR 0x01000000 #define INITRD_LOAD_ADDR 0x01800000 -/* Constants for devices init */ -static const int ide_iobase[2] = { 0x1f0, 0x170 }; -static const int ide_iobase2[2] = { 0x3f6, 0x376 }; -static const int ide_irq[2] = { 13, 13 }; - -#define NE2000_NB_MAX 6 - -static uint32_t ne2000_io[NE2000_NB_MAX] = { 0x300, 0x320, 0x340, 0x360, 0x280, 0x380 }; -static int ne2000_irq[NE2000_NB_MAX] = { 9, 10, 11, 3, 4, 5 }; - -/* ISA IO ports bridge */ -#define PPC_IO_BASE 0x80000000 - -/* Fake super-io ports for PREP platform (Intel 82378ZB) */ -typedef struct sysctrl_t { - qemu_irq reset_irq; - Nvram *nvram; - uint8_t state; - uint8_t syscontrol; - int contiguous_map; - qemu_irq contiguous_map_irq; - int endian; -} sysctrl_t; - -enum { - STATE_HARDFILE = 0x01, -}; - -static sysctrl_t *sysctrl; - -static void PREP_io_800_writeb (void *opaque, uint32_t addr, uint32_t val) -{ - sysctrl_t *sysctrl = opaque; - - trace_prep_io_800_writeb(addr - PPC_IO_BASE, val); - switch (addr) { - case 0x0092: - /* Special port 92 */ - /* Check soft reset asked */ - if (val & 0x01) { - qemu_irq_raise(sysctrl->reset_irq); - } else { - qemu_irq_lower(sysctrl->reset_irq); - } - /* Check LE mode */ - if (val & 0x02) { - sysctrl->endian = 1; - } else { - sysctrl->endian = 0; - } - break; - case 0x0800: - /* Motorola CPU configuration register : read-only */ - break; - case 0x0802: - /* Motorola base module feature register : read-only */ - break; - case 0x0803: - /* Motorola base module status register : read-only */ - break; - case 0x0808: - /* Hardfile light register */ - if (val & 1) - sysctrl->state |= STATE_HARDFILE; - else - sysctrl->state &= ~STATE_HARDFILE; - break; - case 0x0810: - /* Password protect 1 register */ - if (sysctrl->nvram != NULL) { - NvramClass *k = NVRAM_GET_CLASS(sysctrl->nvram); - (k->toggle_lock)(sysctrl->nvram, 1); - } - break; - case 0x0812: - /* Password protect 2 register */ - if (sysctrl->nvram != NULL) { - NvramClass *k = NVRAM_GET_CLASS(sysctrl->nvram); - (k->toggle_lock)(sysctrl->nvram, 2); - } - break; - case 0x0814: - /* L2 invalidate register */ - // tlb_flush(first_cpu, 1); - break; - case 0x081C: - /* system control register */ - sysctrl->syscontrol = val & 0x0F; - break; - case 0x0850: - /* I/O map type register */ - sysctrl->contiguous_map = val & 0x01; - qemu_set_irq(sysctrl->contiguous_map_irq, sysctrl->contiguous_map); - break; - default: - printf("ERROR: unaffected IO port write: %04" PRIx32 - " => %02" PRIx32"\n", addr, val); - break; - } -} - -static uint32_t PREP_io_800_readb (void *opaque, uint32_t addr) -{ - sysctrl_t *sysctrl = opaque; - uint32_t retval = 0xFF; - - switch (addr) { - case 0x0092: - /* Special port 92 */ - retval = sysctrl->endian << 1; - break; - case 0x0800: - /* Motorola CPU configuration register */ - retval = 0xEF; /* MPC750 */ - break; - case 0x0802: - /* Motorola Base module feature register */ - retval = 0xAD; /* No ESCC, PMC slot neither ethernet */ - break; - case 0x0803: - /* Motorola base module status register */ - retval = 0xE0; /* Standard MPC750 */ - break; - case 0x080C: - /* Equipment present register: - * no L2 cache - * no upgrade processor - * no cards in PCI slots - * SCSI fuse is bad - */ - retval = 0x3C; - break; - case 0x0810: - /* Motorola base module extended feature register */ - retval = 0x39; /* No USB, CF and PCI bridge. NVRAM present */ - break; - case 0x0814: - /* L2 invalidate: don't care */ - break; - case 0x0818: - /* Keylock */ - retval = 0x00; - break; - case 0x081C: - /* system control register - * 7 - 6 / 1 - 0: L2 cache enable - */ - retval = sysctrl->syscontrol; - break; - case 0x0823: - /* */ - retval = 0x03; /* no L2 cache */ - break; - case 0x0850: - /* I/O map type register */ - retval = sysctrl->contiguous_map; - break; - default: - printf("ERROR: unaffected IO port: %04" PRIx32 " read\n", addr); - break; - } - trace_prep_io_800_readb(addr - PPC_IO_BASE, retval); - - return retval; -} - - #define NVRAM_SIZE 0x2000 static void fw_cfg_boot_set(void *opaque, const char *boot_device, @@ -247,17 +77,6 @@ static void ppc_prep_reset(void *opaque) cpu_reset(CPU(cpu)); } -static const MemoryRegionPortio prep_portio_list[] = { - /* System control ports */ - { 0x0092, 1, 1, .read = PREP_io_800_readb, .write = PREP_io_800_writeb, }, - { 0x0800, 0x52, 1, - .read = PREP_io_800_readb, .write = PREP_io_800_writeb, }, - /* Special port to get debug messages from Open-Firmware */ - { 0x0F00, 4, 1, .write = PPC_debug_write, }, - PORTIO_END_OF_LIST(), -}; - -static PortioList prep_port_list; /*****************************************************************************/ /* NVRAM helpers */ @@ -397,207 +216,6 @@ static int PPC_NVRAM_set_params (Nvram *nvram, uint16_t NVRAM_size, return 0; } -/* PowerPC PREP hardware initialisation */ -static void ppc_prep_init(MachineState *machine) -{ - ram_addr_t ram_size = machine->ram_size; - const char *kernel_filename = machine->kernel_filename; - const char *kernel_cmdline = machine->kernel_cmdline; - const char *initrd_filename = machine->initrd_filename; - const char *boot_device = machine->boot_order; - MemoryRegion *sysmem = get_system_memory(); - PowerPCCPU *cpu = NULL; - CPUPPCState *env = NULL; - Nvram *m48t59; -#if 0 - MemoryRegion *xcsr = g_new(MemoryRegion, 1); -#endif - int linux_boot, i, nb_nics1; - MemoryRegion *ram = g_new(MemoryRegion, 1); - uint32_t kernel_base, initrd_base; - long kernel_size, initrd_size; - DeviceState *dev; - PCIHostState *pcihost; - PCIBus *pci_bus; - PCIDevice *pci; - ISABus *isa_bus; - ISADevice *isa; - int ppc_boot_device; - DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; - - sysctrl = g_malloc0(sizeof(sysctrl_t)); - - linux_boot = (kernel_filename != NULL); - - /* init CPUs */ - for (i = 0; i < machine->smp.cpus; i++) { - cpu = POWERPC_CPU(cpu_create(machine->cpu_type)); - env = &cpu->env; - - if (env->flags & POWERPC_FLAG_RTC_CLK) { - /* POWER / PowerPC 601 RTC clock frequency is 7.8125 MHz */ - cpu_ppc_tb_init(env, 7812500UL); - } else { - /* Set time-base frequency to 100 Mhz */ - cpu_ppc_tb_init(env, 100UL * 1000UL * 1000UL); - } - qemu_register_reset(ppc_prep_reset, cpu); - } - - /* allocate RAM */ - memory_region_allocate_system_memory(ram, NULL, "ppc_prep.ram", ram_size); - memory_region_add_subregion(sysmem, 0, ram); - - if (linux_boot) { - kernel_base = KERNEL_LOAD_ADDR; - /* now we can load the kernel */ - kernel_size = load_image_targphys(kernel_filename, kernel_base, - ram_size - kernel_base); - if (kernel_size < 0) { - error_report("could not load kernel '%s'", kernel_filename); - exit(1); - } - /* load initrd */ - if (initrd_filename) { - initrd_base = INITRD_LOAD_ADDR; - initrd_size = load_image_targphys(initrd_filename, initrd_base, - ram_size - initrd_base); - if (initrd_size < 0) { - error_report("could not load initial ram disk '%s'", - initrd_filename); - exit(1); - } - } else { - initrd_base = 0; - initrd_size = 0; - } - ppc_boot_device = 'm'; - } else { - kernel_base = 0; - kernel_size = 0; - initrd_base = 0; - initrd_size = 0; - ppc_boot_device = '\0'; - /* For now, OHW cannot boot from the network. */ - for (i = 0; boot_device[i] != '\0'; i++) { - if (boot_device[i] >= 'a' && boot_device[i] <= 'f') { - ppc_boot_device = boot_device[i]; - break; - } - } - if (ppc_boot_device == '\0') { - error_report("No valid boot device for Mac99 machine"); - exit(1); - } - } - - if (PPC_INPUT(env) != PPC_FLAGS_INPUT_6xx) { - error_report("Only 6xx bus is supported on PREP machine"); - exit(1); - } - - dev = qdev_create(NULL, "raven-pcihost"); - if (bios_name == NULL) { - bios_name = BIOS_FILENAME; - } - qdev_prop_set_string(dev, "bios-name", bios_name); - qdev_prop_set_uint32(dev, "elf-machine", PPC_ELF_MACHINE); - qdev_prop_set_bit(dev, "is-legacy-prep", true); - pcihost = PCI_HOST_BRIDGE(dev); - object_property_add_child(qdev_get_machine(), "raven", OBJECT(dev), NULL); - qdev_init_nofail(dev); - pci_bus = (PCIBus *)qdev_get_child_bus(dev, "pci.0"); - if (pci_bus == NULL) { - error_report("Couldn't create PCI host controller"); - exit(1); - } - sysctrl->contiguous_map_irq = qdev_get_gpio_in(dev, 0); - - /* PCI -> ISA bridge */ - pci = pci_create_simple(pci_bus, PCI_DEVFN(1, 0), "i82378"); - cpu = POWERPC_CPU(first_cpu); - qdev_connect_gpio_out(&pci->qdev, 0, - cpu->env.irq_inputs[PPC6xx_INPUT_INT]); - sysbus_connect_irq(&pcihost->busdev, 0, qdev_get_gpio_in(&pci->qdev, 9)); - sysbus_connect_irq(&pcihost->busdev, 1, qdev_get_gpio_in(&pci->qdev, 11)); - sysbus_connect_irq(&pcihost->busdev, 2, qdev_get_gpio_in(&pci->qdev, 9)); - sysbus_connect_irq(&pcihost->busdev, 3, qdev_get_gpio_in(&pci->qdev, 11)); - isa_bus = ISA_BUS(qdev_get_child_bus(DEVICE(pci), "isa.0")); - - /* Super I/O (parallel + serial ports) */ - isa = isa_create(isa_bus, TYPE_PC87312_SUPERIO); - dev = DEVICE(isa); - qdev_prop_set_uint8(dev, "config", 13); /* fdc, ser0, ser1, par0 */ - qdev_init_nofail(dev); - - /* init basic PC hardware */ - pci_vga_init(pci_bus); - - nb_nics1 = nb_nics; - if (nb_nics1 > NE2000_NB_MAX) - nb_nics1 = NE2000_NB_MAX; - for(i = 0; i < nb_nics1; i++) { - if (nd_table[i].model == NULL) { - nd_table[i].model = g_strdup("ne2k_isa"); - } - if (strcmp(nd_table[i].model, "ne2k_isa") == 0) { - isa_ne2000_init(isa_bus, ne2000_io[i], ne2000_irq[i], - &nd_table[i]); - } else { - pci_nic_init_nofail(&nd_table[i], pci_bus, "ne2k_pci", NULL); - } - } - - ide_drive_get(hd, ARRAY_SIZE(hd)); - for(i = 0; i < MAX_IDE_BUS; i++) { - isa_ide_init(isa_bus, ide_iobase[i], ide_iobase2[i], ide_irq[i], - hd[2 * i], - hd[2 * i + 1]); - } - - cpu = POWERPC_CPU(first_cpu); - sysctrl->reset_irq = cpu->env.irq_inputs[PPC6xx_INPUT_HRESET]; - - portio_list_init(&prep_port_list, NULL, prep_portio_list, sysctrl, "prep"); - portio_list_add(&prep_port_list, isa_address_space_io(isa), 0x0); - - /* - * PowerPC control and status register group: unimplemented, - * would be at address 0xFEFF0000. - */ - - if (machine_usb(machine)) { - pci_create_simple(pci_bus, -1, "pci-ohci"); - } - - m48t59 = m48t59_init_isa(isa_bus, 0x0074, NVRAM_SIZE, 2000, 59); - if (m48t59 == NULL) - return; - sysctrl->nvram = m48t59; - - /* Initialise NVRAM */ - PPC_NVRAM_set_params(m48t59, NVRAM_SIZE, "PREP", ram_size, - ppc_boot_device, - kernel_base, kernel_size, - kernel_cmdline, - initrd_base, initrd_size, - /* XXX: need an option to load a NVRAM image */ - 0, - graphic_width, graphic_height, graphic_depth); -} - -static void prep_machine_init(MachineClass *mc) -{ - mc->deprecation_reason = "use 40p machine type instead"; - mc->desc = "PowerPC PREP platform"; - mc->init = ppc_prep_init; - mc->block_default_type = IF_IDE; - mc->max_cpus = MAX_CPUS; - mc->default_boot_order = "cad"; - mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("602"); - mc->default_display = "std"; -} - static int prep_set_cmos_checksum(DeviceState *dev, void *opaque) { uint16_t checksum = *(uint16_t *)opaque; @@ -821,4 +439,3 @@ static void ibm_40p_machine_init(MachineClass *mc) } DEFINE_MACHINE("40p", ibm_40p_machine_init) -DEFINE_MACHINE("prep", prep_machine_init) diff --git a/hw/ppc/prep_systemio.c b/hw/ppc/prep_systemio.c index 86e83e278f..bbc51b6e9a 100644 --- a/hw/ppc/prep_systemio.c +++ b/hw/ppc/prep_systemio.c @@ -289,7 +289,7 @@ static void prep_systemio_class_initfn(ObjectClass *klass, void *data) dc->realize = prep_systemio_realize; dc->vmsd = &vmstate_prep_systemio; - dc->props = prep_systemio_properties; + device_class_set_props(dc, prep_systemio_properties); } static TypeInfo prep_systemio800_info = { diff --git a/hw/ppc/rs6000_mc.c b/hw/ppc/rs6000_mc.c index 66b14db5fa..f8a498bd8a 100644 --- a/hw/ppc/rs6000_mc.c +++ b/hw/ppc/rs6000_mc.c @@ -222,7 +222,7 @@ static void rs6000mc_class_initfn(ObjectClass *klass, void *data) dc->realize = rs6000mc_realize; dc->vmsd = &vmstate_rs6000mc; - dc->props = rs6000mc_properties; + device_class_set_props(dc, rs6000mc_properties); } static const TypeInfo rs6000mc_info = { diff --git a/hw/ppc/sam460ex.c b/hw/ppc/sam460ex.c index 437e214210..898453cf30 100644 --- a/hw/ppc/sam460ex.c +++ b/hw/ppc/sam460ex.c @@ -324,9 +324,8 @@ static void sam460ex_init(MachineState *machine) /* SDRAM controller */ /* put all RAM on first bank because board has one slot * and firmware only checks that */ - machine->ram_size = ppc4xx_sdram_adjust(machine->ram_size, 1, - ram_memories, ram_bases, ram_sizes, - ppc460ex_sdram_bank_sizes); + ppc4xx_sdram_banks(machine->ram, 1, ram_memories, ram_bases, ram_sizes, + ppc460ex_sdram_bank_sizes); /* FIXME: does 460EX have ECC interrupts? */ ppc440_sdram_init(env, SDRAM_NR_BANKS, ram_memories, @@ -439,7 +438,8 @@ static void sam460ex_init(MachineState *machine) success = load_elf(machine->kernel_filename, NULL, NULL, NULL, &elf_entry, - &elf_lowaddr, NULL, 1, PPC_ELF_MACHINE, 0, 0); + &elf_lowaddr, NULL, NULL, 1, PPC_ELF_MACHINE, 0, + 0); entry = elf_entry; loadaddr = elf_lowaddr; } @@ -484,6 +484,7 @@ static void sam460ex_machine_init(MachineClass *mc) mc->init = sam460ex_init; mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("460exb"); mc->default_ram_size = 512 * MiB; + mc->default_ram_id = "ppc4xx.sdram"; } DEFINE_MACHINE("sam460ex", sam460ex_machine_init) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index e076f6023c..9a2bd501aa 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -46,6 +46,7 @@ #include "migration/qemu-file-types.h" #include "migration/global_state.h" #include "migration/register.h" +#include "migration/blocker.h" #include "mmu-hash64.h" #include "mmu-book3s-v3.h" #include "cpu-models.h" @@ -76,10 +77,10 @@ #include "hw/nmi.h" #include "hw/intc/intc.h" -#include "qemu/cutils.h" #include "hw/ppc/spapr_cpu_core.h" #include "hw/mem/memory-device.h" #include "hw/ppc/spapr_tpm_proxy.h" +#include "hw/ppc/spapr_nvdimm.h" #include "monitor/monitor.h" @@ -102,7 +103,7 @@ #define FW_OVERHEAD 0x2800000 #define KERNEL_LOAD_ADDR FW_MAX_SIZE -#define MIN_RMA_SLOF 128UL +#define MIN_RMA_SLOF (128 * MiB) #define PHANDLE_INTC 0x00001111 @@ -216,10 +217,9 @@ static int spapr_fixup_cpu_numa_dt(void *fdt, int offset, PowerPCCPU *cpu) sizeof(associativity)); } -/* Populate the "ibm,pa-features" property */ -static void spapr_populate_pa_features(SpaprMachineState *spapr, - PowerPCCPU *cpu, - void *fdt, int offset) +static void spapr_dt_pa_features(SpaprMachineState *spapr, + PowerPCCPU *cpu, + void *fdt, int offset) { uint8_t pa_features_206[] = { 6, 0, 0xf6, 0x1f, 0xc7, 0x00, 0x80, 0xc0 }; @@ -314,8 +314,8 @@ static void add_str(GString *s, const gchar *s1) g_string_append_len(s, s1, strlen(s1) + 1); } -static int spapr_populate_memory_node(void *fdt, int nodeid, hwaddr start, - hwaddr size) +static int spapr_dt_memory_node(void *fdt, int nodeid, hwaddr start, + hwaddr size) { uint32_t associativity[] = { cpu_to_be32(0x4), /* length */ @@ -340,9 +340,294 @@ static int spapr_populate_memory_node(void *fdt, int nodeid, hwaddr start, return off; } -static int spapr_populate_memory(SpaprMachineState *spapr, void *fdt) +static uint32_t spapr_pc_dimm_node(MemoryDeviceInfoList *list, ram_addr_t addr) +{ + MemoryDeviceInfoList *info; + + for (info = list; info; info = info->next) { + MemoryDeviceInfo *value = info->value; + + if (value && value->type == MEMORY_DEVICE_INFO_KIND_DIMM) { + PCDIMMDeviceInfo *pcdimm_info = value->u.dimm.data; + + if (addr >= pcdimm_info->addr && + addr < (pcdimm_info->addr + pcdimm_info->size)) { + return pcdimm_info->node; + } + } + } + + return -1; +} + +struct sPAPRDrconfCellV2 { + uint32_t seq_lmbs; + uint64_t base_addr; + uint32_t drc_index; + uint32_t aa_index; + uint32_t flags; +} QEMU_PACKED; + +typedef struct DrconfCellQueue { + struct sPAPRDrconfCellV2 cell; + QSIMPLEQ_ENTRY(DrconfCellQueue) entry; +} DrconfCellQueue; + +static DrconfCellQueue * +spapr_get_drconf_cell(uint32_t seq_lmbs, uint64_t base_addr, + uint32_t drc_index, uint32_t aa_index, + uint32_t flags) +{ + DrconfCellQueue *elem; + + elem = g_malloc0(sizeof(*elem)); + elem->cell.seq_lmbs = cpu_to_be32(seq_lmbs); + elem->cell.base_addr = cpu_to_be64(base_addr); + elem->cell.drc_index = cpu_to_be32(drc_index); + elem->cell.aa_index = cpu_to_be32(aa_index); + elem->cell.flags = cpu_to_be32(flags); + + return elem; +} + +static int spapr_dt_dynamic_memory_v2(SpaprMachineState *spapr, void *fdt, + int offset, MemoryDeviceInfoList *dimms) { MachineState *machine = MACHINE(spapr); + uint8_t *int_buf, *cur_index; + int ret; + uint64_t lmb_size = SPAPR_MEMORY_BLOCK_SIZE; + uint64_t addr, cur_addr, size; + uint32_t nr_boot_lmbs = (machine->device_memory->base / lmb_size); + uint64_t mem_end = machine->device_memory->base + + memory_region_size(&machine->device_memory->mr); + uint32_t node, buf_len, nr_entries = 0; + SpaprDrc *drc; + DrconfCellQueue *elem, *next; + MemoryDeviceInfoList *info; + QSIMPLEQ_HEAD(, DrconfCellQueue) drconf_queue + = QSIMPLEQ_HEAD_INITIALIZER(drconf_queue); + + /* Entry to cover RAM and the gap area */ + elem = spapr_get_drconf_cell(nr_boot_lmbs, 0, 0, -1, + SPAPR_LMB_FLAGS_RESERVED | + SPAPR_LMB_FLAGS_DRC_INVALID); + QSIMPLEQ_INSERT_TAIL(&drconf_queue, elem, entry); + nr_entries++; + + cur_addr = machine->device_memory->base; + for (info = dimms; info; info = info->next) { + PCDIMMDeviceInfo *di = info->value->u.dimm.data; + + addr = di->addr; + size = di->size; + node = di->node; + + /* + * The NVDIMM area is hotpluggable after the NVDIMM is unplugged. The + * area is marked hotpluggable in the next iteration for the bigger + * chunk including the NVDIMM occupied area. + */ + if (info->value->type == MEMORY_DEVICE_INFO_KIND_NVDIMM) + continue; + + /* Entry for hot-pluggable area */ + if (cur_addr < addr) { + drc = spapr_drc_by_id(TYPE_SPAPR_DRC_LMB, cur_addr / lmb_size); + g_assert(drc); + elem = spapr_get_drconf_cell((addr - cur_addr) / lmb_size, + cur_addr, spapr_drc_index(drc), -1, 0); + QSIMPLEQ_INSERT_TAIL(&drconf_queue, elem, entry); + nr_entries++; + } + + /* Entry for DIMM */ + drc = spapr_drc_by_id(TYPE_SPAPR_DRC_LMB, addr / lmb_size); + g_assert(drc); + elem = spapr_get_drconf_cell(size / lmb_size, addr, + spapr_drc_index(drc), node, + SPAPR_LMB_FLAGS_ASSIGNED); + QSIMPLEQ_INSERT_TAIL(&drconf_queue, elem, entry); + nr_entries++; + cur_addr = addr + size; + } + + /* Entry for remaining hotpluggable area */ + if (cur_addr < mem_end) { + drc = spapr_drc_by_id(TYPE_SPAPR_DRC_LMB, cur_addr / lmb_size); + g_assert(drc); + elem = spapr_get_drconf_cell((mem_end - cur_addr) / lmb_size, + cur_addr, spapr_drc_index(drc), -1, 0); + QSIMPLEQ_INSERT_TAIL(&drconf_queue, elem, entry); + nr_entries++; + } + + buf_len = nr_entries * sizeof(struct sPAPRDrconfCellV2) + sizeof(uint32_t); + int_buf = cur_index = g_malloc0(buf_len); + *(uint32_t *)int_buf = cpu_to_be32(nr_entries); + cur_index += sizeof(nr_entries); + + QSIMPLEQ_FOREACH_SAFE(elem, &drconf_queue, entry, next) { + memcpy(cur_index, &elem->cell, sizeof(elem->cell)); + cur_index += sizeof(elem->cell); + QSIMPLEQ_REMOVE(&drconf_queue, elem, DrconfCellQueue, entry); + g_free(elem); + } + + ret = fdt_setprop(fdt, offset, "ibm,dynamic-memory-v2", int_buf, buf_len); + g_free(int_buf); + if (ret < 0) { + return -1; + } + return 0; +} + +static int spapr_dt_dynamic_memory(SpaprMachineState *spapr, void *fdt, + int offset, MemoryDeviceInfoList *dimms) +{ + MachineState *machine = MACHINE(spapr); + int i, ret; + uint64_t lmb_size = SPAPR_MEMORY_BLOCK_SIZE; + uint32_t device_lmb_start = machine->device_memory->base / lmb_size; + uint32_t nr_lmbs = (machine->device_memory->base + + memory_region_size(&machine->device_memory->mr)) / + lmb_size; + uint32_t *int_buf, *cur_index, buf_len; + + /* + * Allocate enough buffer size to fit in ibm,dynamic-memory + */ + buf_len = (nr_lmbs * SPAPR_DR_LMB_LIST_ENTRY_SIZE + 1) * sizeof(uint32_t); + cur_index = int_buf = g_malloc0(buf_len); + int_buf[0] = cpu_to_be32(nr_lmbs); + cur_index++; + for (i = 0; i < nr_lmbs; i++) { + uint64_t addr = i * lmb_size; + uint32_t *dynamic_memory = cur_index; + + if (i >= device_lmb_start) { + SpaprDrc *drc; + + drc = spapr_drc_by_id(TYPE_SPAPR_DRC_LMB, i); + g_assert(drc); + + dynamic_memory[0] = cpu_to_be32(addr >> 32); + dynamic_memory[1] = cpu_to_be32(addr & 0xffffffff); + dynamic_memory[2] = cpu_to_be32(spapr_drc_index(drc)); + dynamic_memory[3] = cpu_to_be32(0); /* reserved */ + dynamic_memory[4] = cpu_to_be32(spapr_pc_dimm_node(dimms, addr)); + if (memory_region_present(get_system_memory(), addr)) { + dynamic_memory[5] = cpu_to_be32(SPAPR_LMB_FLAGS_ASSIGNED); + } else { + dynamic_memory[5] = cpu_to_be32(0); + } + } else { + /* + * LMB information for RMA, boot time RAM and gap b/n RAM and + * device memory region -- all these are marked as reserved + * and as having no valid DRC. + */ + dynamic_memory[0] = cpu_to_be32(addr >> 32); + dynamic_memory[1] = cpu_to_be32(addr & 0xffffffff); + dynamic_memory[2] = cpu_to_be32(0); + dynamic_memory[3] = cpu_to_be32(0); /* reserved */ + dynamic_memory[4] = cpu_to_be32(-1); + dynamic_memory[5] = cpu_to_be32(SPAPR_LMB_FLAGS_RESERVED | + SPAPR_LMB_FLAGS_DRC_INVALID); + } + + cur_index += SPAPR_DR_LMB_LIST_ENTRY_SIZE; + } + ret = fdt_setprop(fdt, offset, "ibm,dynamic-memory", int_buf, buf_len); + g_free(int_buf); + if (ret < 0) { + return -1; + } + return 0; +} + +/* + * Adds ibm,dynamic-reconfiguration-memory node. + * Refer to docs/specs/ppc-spapr-hotplug.txt for the documentation + * of this device tree node. + */ +static int spapr_dt_dynamic_reconfiguration_memory(SpaprMachineState *spapr, + void *fdt) +{ + MachineState *machine = MACHINE(spapr); + int nb_numa_nodes = machine->numa_state->num_nodes; + int ret, i, offset; + uint64_t lmb_size = SPAPR_MEMORY_BLOCK_SIZE; + uint32_t prop_lmb_size[] = {0, cpu_to_be32(lmb_size)}; + uint32_t *int_buf, *cur_index, buf_len; + int nr_nodes = nb_numa_nodes ? nb_numa_nodes : 1; + MemoryDeviceInfoList *dimms = NULL; + + /* + * Don't create the node if there is no device memory + */ + if (machine->ram_size == machine->maxram_size) { + return 0; + } + + offset = fdt_add_subnode(fdt, 0, "ibm,dynamic-reconfiguration-memory"); + + ret = fdt_setprop(fdt, offset, "ibm,lmb-size", prop_lmb_size, + sizeof(prop_lmb_size)); + if (ret < 0) { + return ret; + } + + ret = fdt_setprop_cell(fdt, offset, "ibm,memory-flags-mask", 0xff); + if (ret < 0) { + return ret; + } + + ret = fdt_setprop_cell(fdt, offset, "ibm,memory-preservation-time", 0x0); + if (ret < 0) { + return ret; + } + + /* ibm,dynamic-memory or ibm,dynamic-memory-v2 */ + dimms = qmp_memory_device_list(); + if (spapr_ovec_test(spapr->ov5_cas, OV5_DRMEM_V2)) { + ret = spapr_dt_dynamic_memory_v2(spapr, fdt, offset, dimms); + } else { + ret = spapr_dt_dynamic_memory(spapr, fdt, offset, dimms); + } + qapi_free_MemoryDeviceInfoList(dimms); + + if (ret < 0) { + return ret; + } + + /* ibm,associativity-lookup-arrays */ + buf_len = (nr_nodes * 4 + 2) * sizeof(uint32_t); + cur_index = int_buf = g_malloc0(buf_len); + int_buf[0] = cpu_to_be32(nr_nodes); + int_buf[1] = cpu_to_be32(4); /* Number of entries per associativity list */ + cur_index += 2; + for (i = 0; i < nr_nodes; i++) { + uint32_t associativity[] = { + cpu_to_be32(0x0), + cpu_to_be32(0x0), + cpu_to_be32(0x0), + cpu_to_be32(i) + }; + memcpy(cur_index, associativity, sizeof(associativity)); + cur_index += 4; + } + ret = fdt_setprop(fdt, offset, "ibm,associativity-lookup-arrays", int_buf, + (cur_index - int_buf) * sizeof(uint32_t)); + g_free(int_buf); + + return ret; +} + +static int spapr_dt_memory(SpaprMachineState *spapr, void *fdt) +{ + MachineState *machine = MACHINE(spapr); + SpaprMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr); hwaddr mem_start, node_size; int i, nb_nodes = machine->numa_state->num_nodes; NodeInfo *nodes = machine->numa_state->nodes; @@ -362,7 +647,7 @@ static int spapr_populate_memory(SpaprMachineState *spapr, void *fdt) if (!mem_start) { /* spapr_machine_init() checks for rma_size <= node0_size * already */ - spapr_populate_memory_node(fdt, i, 0, spapr->rma_size); + spapr_dt_memory_node(fdt, i, 0, spapr->rma_size); mem_start += spapr->rma_size; node_size -= spapr->rma_size; } @@ -374,17 +659,28 @@ static int spapr_populate_memory(SpaprMachineState *spapr, void *fdt) sizetmp = 1ULL << ctzl(mem_start); } - spapr_populate_memory_node(fdt, i, mem_start, sizetmp); + spapr_dt_memory_node(fdt, i, mem_start, sizetmp); node_size -= sizetmp; mem_start += sizetmp; } } + /* Generate ibm,dynamic-reconfiguration-memory node if required */ + if (spapr_ovec_test(spapr->ov5_cas, OV5_DRCONF_MEMORY)) { + int ret; + + g_assert(smc->dr_lmb_enabled); + ret = spapr_dt_dynamic_reconfiguration_memory(spapr, fdt); + if (ret) { + return ret; + } + } + return 0; } -static void spapr_populate_cpu_dt(CPUState *cs, void *fdt, int offset, - SpaprMachineState *spapr) +static void spapr_dt_cpu(CPUState *cs, void *fdt, int offset, + SpaprMachineState *spapr) { MachineState *ms = MACHINE(spapr); PowerPCCPU *cpu = POWERPC_CPU(cs); @@ -484,7 +780,7 @@ static void spapr_populate_cpu_dt(CPUState *cs, void *fdt, int offset, page_sizes_prop, page_sizes_prop_size))); } - spapr_populate_pa_features(spapr, cpu, fdt, offset); + spapr_dt_pa_features(spapr, cpu, fdt, offset); _FDT((fdt_setprop_cell(fdt, offset, "ibm,chip-id", cs->cpu_index / vcpus_per_socket))); @@ -518,7 +814,7 @@ static void spapr_populate_cpu_dt(CPUState *cs, void *fdt, int offset, pcc->lrg_decr_bits))); } -static void spapr_populate_cpus_dt_node(void *fdt, SpaprMachineState *spapr) +static void spapr_dt_cpus(void *fdt, SpaprMachineState *spapr) { CPUState **rev; CPUState *cs; @@ -562,13 +858,13 @@ static void spapr_populate_cpus_dt_node(void *fdt, SpaprMachineState *spapr) offset = fdt_add_subnode(fdt, cpus_offset, nodename); g_free(nodename); _FDT(offset); - spapr_populate_cpu_dt(cs, fdt, offset, spapr); + spapr_dt_cpu(cs, fdt, offset, spapr); } g_free(rev); } -static int spapr_rng_populate_dt(void *fdt) +static int spapr_dt_rng(void *fdt) { int node; int ret; @@ -591,375 +887,6 @@ static int spapr_rng_populate_dt(void *fdt) return ret ? -1 : 0; } -static uint32_t spapr_pc_dimm_node(MemoryDeviceInfoList *list, ram_addr_t addr) -{ - MemoryDeviceInfoList *info; - - for (info = list; info; info = info->next) { - MemoryDeviceInfo *value = info->value; - - if (value && value->type == MEMORY_DEVICE_INFO_KIND_DIMM) { - PCDIMMDeviceInfo *pcdimm_info = value->u.dimm.data; - - if (addr >= pcdimm_info->addr && - addr < (pcdimm_info->addr + pcdimm_info->size)) { - return pcdimm_info->node; - } - } - } - - return -1; -} - -struct sPAPRDrconfCellV2 { - uint32_t seq_lmbs; - uint64_t base_addr; - uint32_t drc_index; - uint32_t aa_index; - uint32_t flags; -} QEMU_PACKED; - -typedef struct DrconfCellQueue { - struct sPAPRDrconfCellV2 cell; - QSIMPLEQ_ENTRY(DrconfCellQueue) entry; -} DrconfCellQueue; - -static DrconfCellQueue * -spapr_get_drconf_cell(uint32_t seq_lmbs, uint64_t base_addr, - uint32_t drc_index, uint32_t aa_index, - uint32_t flags) -{ - DrconfCellQueue *elem; - - elem = g_malloc0(sizeof(*elem)); - elem->cell.seq_lmbs = cpu_to_be32(seq_lmbs); - elem->cell.base_addr = cpu_to_be64(base_addr); - elem->cell.drc_index = cpu_to_be32(drc_index); - elem->cell.aa_index = cpu_to_be32(aa_index); - elem->cell.flags = cpu_to_be32(flags); - - return elem; -} - -/* ibm,dynamic-memory-v2 */ -static int spapr_populate_drmem_v2(SpaprMachineState *spapr, void *fdt, - int offset, MemoryDeviceInfoList *dimms) -{ - MachineState *machine = MACHINE(spapr); - uint8_t *int_buf, *cur_index; - int ret; - uint64_t lmb_size = SPAPR_MEMORY_BLOCK_SIZE; - uint64_t addr, cur_addr, size; - uint32_t nr_boot_lmbs = (machine->device_memory->base / lmb_size); - uint64_t mem_end = machine->device_memory->base + - memory_region_size(&machine->device_memory->mr); - uint32_t node, buf_len, nr_entries = 0; - SpaprDrc *drc; - DrconfCellQueue *elem, *next; - MemoryDeviceInfoList *info; - QSIMPLEQ_HEAD(, DrconfCellQueue) drconf_queue - = QSIMPLEQ_HEAD_INITIALIZER(drconf_queue); - - /* Entry to cover RAM and the gap area */ - elem = spapr_get_drconf_cell(nr_boot_lmbs, 0, 0, -1, - SPAPR_LMB_FLAGS_RESERVED | - SPAPR_LMB_FLAGS_DRC_INVALID); - QSIMPLEQ_INSERT_TAIL(&drconf_queue, elem, entry); - nr_entries++; - - cur_addr = machine->device_memory->base; - for (info = dimms; info; info = info->next) { - PCDIMMDeviceInfo *di = info->value->u.dimm.data; - - addr = di->addr; - size = di->size; - node = di->node; - - /* Entry for hot-pluggable area */ - if (cur_addr < addr) { - drc = spapr_drc_by_id(TYPE_SPAPR_DRC_LMB, cur_addr / lmb_size); - g_assert(drc); - elem = spapr_get_drconf_cell((addr - cur_addr) / lmb_size, - cur_addr, spapr_drc_index(drc), -1, 0); - QSIMPLEQ_INSERT_TAIL(&drconf_queue, elem, entry); - nr_entries++; - } - - /* Entry for DIMM */ - drc = spapr_drc_by_id(TYPE_SPAPR_DRC_LMB, addr / lmb_size); - g_assert(drc); - elem = spapr_get_drconf_cell(size / lmb_size, addr, - spapr_drc_index(drc), node, - SPAPR_LMB_FLAGS_ASSIGNED); - QSIMPLEQ_INSERT_TAIL(&drconf_queue, elem, entry); - nr_entries++; - cur_addr = addr + size; - } - - /* Entry for remaining hotpluggable area */ - if (cur_addr < mem_end) { - drc = spapr_drc_by_id(TYPE_SPAPR_DRC_LMB, cur_addr / lmb_size); - g_assert(drc); - elem = spapr_get_drconf_cell((mem_end - cur_addr) / lmb_size, - cur_addr, spapr_drc_index(drc), -1, 0); - QSIMPLEQ_INSERT_TAIL(&drconf_queue, elem, entry); - nr_entries++; - } - - buf_len = nr_entries * sizeof(struct sPAPRDrconfCellV2) + sizeof(uint32_t); - int_buf = cur_index = g_malloc0(buf_len); - *(uint32_t *)int_buf = cpu_to_be32(nr_entries); - cur_index += sizeof(nr_entries); - - QSIMPLEQ_FOREACH_SAFE(elem, &drconf_queue, entry, next) { - memcpy(cur_index, &elem->cell, sizeof(elem->cell)); - cur_index += sizeof(elem->cell); - QSIMPLEQ_REMOVE(&drconf_queue, elem, DrconfCellQueue, entry); - g_free(elem); - } - - ret = fdt_setprop(fdt, offset, "ibm,dynamic-memory-v2", int_buf, buf_len); - g_free(int_buf); - if (ret < 0) { - return -1; - } - return 0; -} - -/* ibm,dynamic-memory */ -static int spapr_populate_drmem_v1(SpaprMachineState *spapr, void *fdt, - int offset, MemoryDeviceInfoList *dimms) -{ - MachineState *machine = MACHINE(spapr); - int i, ret; - uint64_t lmb_size = SPAPR_MEMORY_BLOCK_SIZE; - uint32_t device_lmb_start = machine->device_memory->base / lmb_size; - uint32_t nr_lmbs = (machine->device_memory->base + - memory_region_size(&machine->device_memory->mr)) / - lmb_size; - uint32_t *int_buf, *cur_index, buf_len; - - /* - * Allocate enough buffer size to fit in ibm,dynamic-memory - */ - buf_len = (nr_lmbs * SPAPR_DR_LMB_LIST_ENTRY_SIZE + 1) * sizeof(uint32_t); - cur_index = int_buf = g_malloc0(buf_len); - int_buf[0] = cpu_to_be32(nr_lmbs); - cur_index++; - for (i = 0; i < nr_lmbs; i++) { - uint64_t addr = i * lmb_size; - uint32_t *dynamic_memory = cur_index; - - if (i >= device_lmb_start) { - SpaprDrc *drc; - - drc = spapr_drc_by_id(TYPE_SPAPR_DRC_LMB, i); - g_assert(drc); - - dynamic_memory[0] = cpu_to_be32(addr >> 32); - dynamic_memory[1] = cpu_to_be32(addr & 0xffffffff); - dynamic_memory[2] = cpu_to_be32(spapr_drc_index(drc)); - dynamic_memory[3] = cpu_to_be32(0); /* reserved */ - dynamic_memory[4] = cpu_to_be32(spapr_pc_dimm_node(dimms, addr)); - if (memory_region_present(get_system_memory(), addr)) { - dynamic_memory[5] = cpu_to_be32(SPAPR_LMB_FLAGS_ASSIGNED); - } else { - dynamic_memory[5] = cpu_to_be32(0); - } - } else { - /* - * LMB information for RMA, boot time RAM and gap b/n RAM and - * device memory region -- all these are marked as reserved - * and as having no valid DRC. - */ - dynamic_memory[0] = cpu_to_be32(addr >> 32); - dynamic_memory[1] = cpu_to_be32(addr & 0xffffffff); - dynamic_memory[2] = cpu_to_be32(0); - dynamic_memory[3] = cpu_to_be32(0); /* reserved */ - dynamic_memory[4] = cpu_to_be32(-1); - dynamic_memory[5] = cpu_to_be32(SPAPR_LMB_FLAGS_RESERVED | - SPAPR_LMB_FLAGS_DRC_INVALID); - } - - cur_index += SPAPR_DR_LMB_LIST_ENTRY_SIZE; - } - ret = fdt_setprop(fdt, offset, "ibm,dynamic-memory", int_buf, buf_len); - g_free(int_buf); - if (ret < 0) { - return -1; - } - return 0; -} - -/* - * Adds ibm,dynamic-reconfiguration-memory node. - * Refer to docs/specs/ppc-spapr-hotplug.txt for the documentation - * of this device tree node. - */ -static int spapr_populate_drconf_memory(SpaprMachineState *spapr, void *fdt) -{ - MachineState *machine = MACHINE(spapr); - int nb_numa_nodes = machine->numa_state->num_nodes; - int ret, i, offset; - uint64_t lmb_size = SPAPR_MEMORY_BLOCK_SIZE; - uint32_t prop_lmb_size[] = {0, cpu_to_be32(lmb_size)}; - uint32_t *int_buf, *cur_index, buf_len; - int nr_nodes = nb_numa_nodes ? nb_numa_nodes : 1; - MemoryDeviceInfoList *dimms = NULL; - - /* - * Don't create the node if there is no device memory - */ - if (machine->ram_size == machine->maxram_size) { - return 0; - } - - offset = fdt_add_subnode(fdt, 0, "ibm,dynamic-reconfiguration-memory"); - - ret = fdt_setprop(fdt, offset, "ibm,lmb-size", prop_lmb_size, - sizeof(prop_lmb_size)); - if (ret < 0) { - return ret; - } - - ret = fdt_setprop_cell(fdt, offset, "ibm,memory-flags-mask", 0xff); - if (ret < 0) { - return ret; - } - - ret = fdt_setprop_cell(fdt, offset, "ibm,memory-preservation-time", 0x0); - if (ret < 0) { - return ret; - } - - /* ibm,dynamic-memory or ibm,dynamic-memory-v2 */ - dimms = qmp_memory_device_list(); - if (spapr_ovec_test(spapr->ov5_cas, OV5_DRMEM_V2)) { - ret = spapr_populate_drmem_v2(spapr, fdt, offset, dimms); - } else { - ret = spapr_populate_drmem_v1(spapr, fdt, offset, dimms); - } - qapi_free_MemoryDeviceInfoList(dimms); - - if (ret < 0) { - return ret; - } - - /* ibm,associativity-lookup-arrays */ - buf_len = (nr_nodes * 4 + 2) * sizeof(uint32_t); - cur_index = int_buf = g_malloc0(buf_len); - int_buf[0] = cpu_to_be32(nr_nodes); - int_buf[1] = cpu_to_be32(4); /* Number of entries per associativity list */ - cur_index += 2; - for (i = 0; i < nr_nodes; i++) { - uint32_t associativity[] = { - cpu_to_be32(0x0), - cpu_to_be32(0x0), - cpu_to_be32(0x0), - cpu_to_be32(i) - }; - memcpy(cur_index, associativity, sizeof(associativity)); - cur_index += 4; - } - ret = fdt_setprop(fdt, offset, "ibm,associativity-lookup-arrays", int_buf, - (cur_index - int_buf) * sizeof(uint32_t)); - g_free(int_buf); - - return ret; -} - -static int spapr_dt_cas_updates(SpaprMachineState *spapr, void *fdt, - SpaprOptionVector *ov5_updates) -{ - SpaprMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr); - int ret = 0, offset; - - /* Generate ibm,dynamic-reconfiguration-memory node if required */ - if (spapr_ovec_test(ov5_updates, OV5_DRCONF_MEMORY)) { - g_assert(smc->dr_lmb_enabled); - ret = spapr_populate_drconf_memory(spapr, fdt); - if (ret) { - goto out; - } - } - - offset = fdt_path_offset(fdt, "/chosen"); - if (offset < 0) { - offset = fdt_add_subnode(fdt, 0, "chosen"); - if (offset < 0) { - return offset; - } - } - ret = spapr_ovec_populate_dt(fdt, offset, spapr->ov5_cas, - "ibm,architecture-vec-5"); - -out: - return ret; -} - -static bool spapr_hotplugged_dev_before_cas(void) -{ - Object *drc_container, *obj; - ObjectProperty *prop; - ObjectPropertyIterator iter; - - drc_container = container_get(object_get_root(), "/dr-connector"); - object_property_iter_init(&iter, drc_container); - while ((prop = object_property_iter_next(&iter))) { - if (!strstart(prop->type, "link<", NULL)) { - continue; - } - obj = object_property_get_link(drc_container, prop->name, NULL); - if (spapr_drc_needed(obj)) { - return true; - } - } - return false; -} - -static void *spapr_build_fdt(SpaprMachineState *spapr, bool reset); - -int spapr_h_cas_compose_response(SpaprMachineState *spapr, - target_ulong addr, target_ulong size, - SpaprOptionVector *ov5_updates) -{ - void *fdt; - SpaprDeviceTreeUpdateHeader hdr = { .version_id = 1 }; - - if (spapr_hotplugged_dev_before_cas()) { - return 1; - } - - if (size < sizeof(hdr) || size > FW_MAX_SIZE) { - error_report("SLOF provided an unexpected CAS buffer size " - TARGET_FMT_lu " (min: %zu, max: %u)", - size, sizeof(hdr), FW_MAX_SIZE); - exit(EXIT_FAILURE); - } - - size -= sizeof(hdr); - - fdt = spapr_build_fdt(spapr, false); - _FDT((fdt_pack(fdt))); - - if (fdt_totalsize(fdt) + sizeof(hdr) > size) { - g_free(fdt); - trace_spapr_cas_failed(size); - return -1; - } - - cpu_physical_memory_write(addr, &hdr, sizeof(hdr)); - cpu_physical_memory_write(addr + sizeof(hdr), fdt, fdt_totalsize(fdt)); - trace_spapr_cas_continue(fdt_totalsize(fdt) + sizeof(hdr)); - - g_free(spapr->fdt_blob); - spapr->fdt_size = fdt_totalsize(fdt); - spapr->fdt_initial_size = spapr->fdt_size; - spapr->fdt_blob = fdt; - - return 0; -} - static void spapr_dt_rtas(SpaprMachineState *spapr, void *fdt) { MachineState *ms = MACHINE(spapr); @@ -1024,6 +951,29 @@ static void spapr_dt_rtas(SpaprMachineState *spapr, void *fdt) _FDT(fdt_setprop(fdt, rtas, "ibm,max-associativity-domains", maxdomains, sizeof(maxdomains))); + /* + * FWNMI reserves RTAS_ERROR_LOG_MAX for the machine check error log, + * and 16 bytes per CPU for system reset error log plus an extra 8 bytes. + * + * The system reset requirements are driven by existing Linux and PowerVM + * implementation which (contrary to PAPR) saves r3 in the error log + * structure like machine check, so Linux expects to find the saved r3 + * value at the address in r3 upon FWNMI-enabled sreset interrupt (and + * does not look at the error value). + * + * System reset interrupts are not subject to interlock like machine + * check, so this memory area could be corrupted if the sreset is + * interrupted by a machine check (or vice versa) if it was shared. To + * prevent this, system reset uses per-CPU areas for the sreset save + * area. A system reset that interrupts a system reset handler could + * still overwrite this area, but Linux doesn't try to recover in that + * case anyway. + * + * The extra 8 bytes is required because Linux's FWNMI error log check + * is off-by-one. + */ + _FDT(fdt_setprop_cell(fdt, rtas, "rtas-size", RTAS_ERROR_LOG_MAX + + ms->smp.max_cpus * sizeof(uint64_t)*2 + sizeof(uint64_t))); _FDT(fdt_setprop_cell(fdt, rtas, "rtas-error-log-max", RTAS_ERROR_LOG_MAX)); _FDT(fdt_setprop_cell(fdt, rtas, "rtas-event-scan-rate", @@ -1097,81 +1047,91 @@ static void spapr_dt_ov5_platform_support(SpaprMachineState *spapr, void *fdt, val, sizeof(val))); } -static void spapr_dt_chosen(SpaprMachineState *spapr, void *fdt) +static void spapr_dt_chosen(SpaprMachineState *spapr, void *fdt, bool reset) { MachineState *machine = MACHINE(spapr); SpaprMachineClass *smc = SPAPR_MACHINE_GET_CLASS(machine); int chosen; - const char *boot_device = machine->boot_order; - char *stdout_path = spapr_vio_stdout_path(spapr->vio_bus); - size_t cb = 0; - char *bootlist = get_boot_devices_list(&cb); _FDT(chosen = fdt_add_subnode(fdt, 0, "chosen")); - if (machine->kernel_cmdline && machine->kernel_cmdline[0]) { - _FDT(fdt_setprop_string(fdt, chosen, "bootargs", - machine->kernel_cmdline)); - } - if (spapr->initrd_size) { - _FDT(fdt_setprop_cell(fdt, chosen, "linux,initrd-start", - spapr->initrd_base)); - _FDT(fdt_setprop_cell(fdt, chosen, "linux,initrd-end", - spapr->initrd_base + spapr->initrd_size)); - } + if (reset) { + const char *boot_device = machine->boot_order; + char *stdout_path = spapr_vio_stdout_path(spapr->vio_bus); + size_t cb = 0; + char *bootlist = get_boot_devices_list(&cb); - if (spapr->kernel_size) { - uint64_t kprop[2] = { cpu_to_be64(KERNEL_LOAD_ADDR), - cpu_to_be64(spapr->kernel_size) }; - - _FDT(fdt_setprop(fdt, chosen, "qemu,boot-kernel", - &kprop, sizeof(kprop))); - if (spapr->kernel_le) { - _FDT(fdt_setprop(fdt, chosen, "qemu,boot-kernel-le", NULL, 0)); + if (machine->kernel_cmdline && machine->kernel_cmdline[0]) { + _FDT(fdt_setprop_string(fdt, chosen, "bootargs", + machine->kernel_cmdline)); } - } - if (boot_menu) { - _FDT((fdt_setprop_cell(fdt, chosen, "qemu,boot-menu", boot_menu))); - } - _FDT(fdt_setprop_cell(fdt, chosen, "qemu,graphic-width", graphic_width)); - _FDT(fdt_setprop_cell(fdt, chosen, "qemu,graphic-height", graphic_height)); - _FDT(fdt_setprop_cell(fdt, chosen, "qemu,graphic-depth", graphic_depth)); - if (cb && bootlist) { - int i; + if (spapr->initrd_size) { + _FDT(fdt_setprop_cell(fdt, chosen, "linux,initrd-start", + spapr->initrd_base)); + _FDT(fdt_setprop_cell(fdt, chosen, "linux,initrd-end", + spapr->initrd_base + spapr->initrd_size)); + } - for (i = 0; i < cb; i++) { - if (bootlist[i] == '\n') { - bootlist[i] = ' '; + if (spapr->kernel_size) { + uint64_t kprop[2] = { cpu_to_be64(spapr->kernel_addr), + cpu_to_be64(spapr->kernel_size) }; + + _FDT(fdt_setprop(fdt, chosen, "qemu,boot-kernel", + &kprop, sizeof(kprop))); + if (spapr->kernel_le) { + _FDT(fdt_setprop(fdt, chosen, "qemu,boot-kernel-le", NULL, 0)); } } - _FDT(fdt_setprop_string(fdt, chosen, "qemu,boot-list", bootlist)); - } + if (boot_menu) { + _FDT((fdt_setprop_cell(fdt, chosen, "qemu,boot-menu", boot_menu))); + } + _FDT(fdt_setprop_cell(fdt, chosen, "qemu,graphic-width", graphic_width)); + _FDT(fdt_setprop_cell(fdt, chosen, "qemu,graphic-height", graphic_height)); + _FDT(fdt_setprop_cell(fdt, chosen, "qemu,graphic-depth", graphic_depth)); - if (boot_device && strlen(boot_device)) { - _FDT(fdt_setprop_string(fdt, chosen, "qemu,boot-device", boot_device)); - } + if (cb && bootlist) { + int i; + + for (i = 0; i < cb; i++) { + if (bootlist[i] == '\n') { + bootlist[i] = ' '; + } + } + _FDT(fdt_setprop_string(fdt, chosen, "qemu,boot-list", bootlist)); + } + + if (boot_device && strlen(boot_device)) { + _FDT(fdt_setprop_string(fdt, chosen, "qemu,boot-device", boot_device)); + } + + if (!spapr->has_graphics && stdout_path) { + /* + * "linux,stdout-path" and "stdout" properties are + * deprecated by linux kernel. New platforms should only + * use the "stdout-path" property. Set the new property + * and continue using older property to remain compatible + * with the existing firmware. + */ + _FDT(fdt_setprop_string(fdt, chosen, "linux,stdout-path", stdout_path)); + _FDT(fdt_setprop_string(fdt, chosen, "stdout-path", stdout_path)); + } - if (!spapr->has_graphics && stdout_path) { /* - * "linux,stdout-path" and "stdout" properties are deprecated by linux - * kernel. New platforms should only use the "stdout-path" property. Set - * the new property and continue using older property to remain - * compatible with the existing firmware. + * We can deal with BAR reallocation just fine, advertise it + * to the guest */ - _FDT(fdt_setprop_string(fdt, chosen, "linux,stdout-path", stdout_path)); - _FDT(fdt_setprop_string(fdt, chosen, "stdout-path", stdout_path)); + if (smc->linux_pci_probe) { + _FDT(fdt_setprop_cell(fdt, chosen, "linux,pci-probe-only", 0)); + } + + spapr_dt_ov5_platform_support(spapr, fdt, chosen); + + g_free(stdout_path); + g_free(bootlist); } - /* We can deal with BAR reallocation just fine, advertise it to the guest */ - if (smc->linux_pci_probe) { - _FDT(fdt_setprop_cell(fdt, chosen, "linux,pci-probe-only", 0)); - } - - spapr_dt_ov5_platform_support(spapr, fdt, chosen); - - g_free(stdout_path); - g_free(bootlist); + _FDT(spapr_dt_ovec(fdt, chosen, spapr->ov5_cas, "ibm,architecture-vec-5")); } static void spapr_dt_hypervisor(SpaprMachineState *spapr, void *fdt) @@ -1197,7 +1157,7 @@ static void spapr_dt_hypervisor(SpaprMachineState *spapr, void *fdt) } } -static void *spapr_build_fdt(SpaprMachineState *spapr, bool reset) +void *spapr_build_fdt(SpaprMachineState *spapr, bool reset, size_t space) { MachineState *machine = MACHINE(spapr); MachineClass *mc = MACHINE_GET_CLASS(machine); @@ -1207,8 +1167,8 @@ static void *spapr_build_fdt(SpaprMachineState *spapr, bool reset) SpaprPhbState *phb; char *buf; - fdt = g_malloc0(FDT_MAX_SIZE); - _FDT((fdt_create_empty_tree(fdt, FDT_MAX_SIZE))); + fdt = g_malloc0(space); + _FDT((fdt_create_empty_tree(fdt, space))); /* Root node */ _FDT(fdt_setprop_string(fdt, 0, "device_type", "chrp")); @@ -1249,7 +1209,7 @@ static void *spapr_build_fdt(SpaprMachineState *spapr, bool reset) /* /interrupt controller */ spapr_irq_dt(spapr, spapr_max_server_number(spapr), fdt, PHANDLE_INTC); - ret = spapr_populate_memory(spapr, fdt); + ret = spapr_dt_memory(spapr, fdt); if (ret < 0) { error_report("couldn't setup memory nodes in fdt"); exit(1); @@ -1259,7 +1219,7 @@ static void *spapr_build_fdt(SpaprMachineState *spapr, bool reset) spapr_dt_vdevice(spapr->vio_bus, fdt); if (object_resolve_path_type("", TYPE_SPAPR_RNG, NULL)) { - ret = spapr_rng_populate_dt(fdt); + ret = spapr_dt_rng(fdt); if (ret < 0) { error_report("could not set up rng device in the fdt"); exit(1); @@ -1274,8 +1234,7 @@ static void *spapr_build_fdt(SpaprMachineState *spapr, bool reset) } } - /* cpus */ - spapr_populate_cpus_dt_node(fdt, spapr); + spapr_dt_cpus(fdt, spapr); if (smc->dr_lmb_enabled) { _FDT(spapr_dt_drc(fdt, 0, NULL, SPAPR_DR_CONNECTOR_TYPE_LMB)); @@ -1297,9 +1256,7 @@ static void *spapr_build_fdt(SpaprMachineState *spapr, bool reset) spapr_dt_rtas(spapr, fdt); /* /chosen */ - if (reset) { - spapr_dt_chosen(spapr, fdt); - } + spapr_dt_chosen(spapr, fdt, reset); /* /hypervisor */ if (kvm_enabled()) { @@ -1309,7 +1266,8 @@ static void *spapr_build_fdt(SpaprMachineState *spapr, bool reset) /* Build memory reserve map */ if (reset) { if (spapr->kernel_size) { - _FDT((fdt_add_mem_rsv(fdt, KERNEL_LOAD_ADDR, spapr->kernel_size))); + _FDT((fdt_add_mem_rsv(fdt, spapr->kernel_addr, + spapr->kernel_size))); } if (spapr->initrd_size) { _FDT((fdt_add_mem_rsv(fdt, spapr->initrd_base, @@ -1317,13 +1275,6 @@ static void *spapr_build_fdt(SpaprMachineState *spapr, bool reset) } } - /* ibm,client-architecture-support updates */ - ret = spapr_dt_cas_updates(spapr, fdt, spapr->ov5_cas); - if (ret < 0) { - error_report("couldn't setup CAS properties fdt"); - exit(1); - } - if (smc->dr_phb_enabled) { ret = spapr_dt_drc(fdt, 0, NULL, SPAPR_DR_CONNECTOR_TYPE_PHB); if (ret < 0) { @@ -1332,12 +1283,19 @@ static void *spapr_build_fdt(SpaprMachineState *spapr, bool reset) } } + /* NVDIMM devices */ + if (mc->nvdimm_supported) { + spapr_dt_persistent_memory(fdt); + } + return fdt; } static uint64_t translate_kernel_address(void *opaque, uint64_t addr) { - return (addr & 0x0fffffff) + KERNEL_LOAD_ADDR; + SpaprMachineState *spapr = opaque; + + return (addr & 0x0fffffff) + spapr->kernel_addr; } static void emulate_spapr_hypercall(PPCVirtualHypervisor *vhyp, @@ -1618,7 +1576,7 @@ void spapr_reallocate_hpt(SpaprMachineState *spapr, int shift, spapr_set_all_lpcrs(0, LPCR_HR | LPCR_UPRT); } -void spapr_setup_hpt_and_vrma(SpaprMachineState *spapr) +void spapr_setup_hpt(SpaprMachineState *spapr) { int hpt_shift; @@ -1634,9 +1592,16 @@ void spapr_setup_hpt_and_vrma(SpaprMachineState *spapr) } spapr_reallocate_hpt(spapr, hpt_shift, &error_fatal); - if (spapr->vrma_adjust) { - spapr->rma_size = kvmppc_rma_size(spapr_node0_size(MACHINE(spapr)), - spapr->htab_shift); + if (kvm_enabled()) { + hwaddr vrma_limit = kvmppc_vrma_limit(spapr->htab_shift); + + /* Check our RMA fits in the possible VRMA */ + if (vrma_limit < spapr->rma_size) { + error_report("Unable to create %" HWADDR_PRIu + "MiB RMA (VRMA only allows %" HWADDR_PRIu "MiB", + spapr->rma_size / MiB, vrma_limit / MiB); + exit(EXIT_FAILURE); + } } } @@ -1661,6 +1626,7 @@ static void spapr_machine_reset(MachineState *machine) void *fdt; int rc; + kvmppc_svm_off(&error_fatal); spapr_caps_apply(spapr); first_ppc_cpu = POWERPC_CPU(first_cpu); @@ -1675,7 +1641,7 @@ static void spapr_machine_reset(MachineState *machine) spapr->patb_entry = PATE1_GR; spapr_set_all_lpcrs(LPCR_HR | LPCR_UPRT, LPCR_HR | LPCR_UPRT); } else { - spapr_setup_hpt_and_vrma(spapr); + spapr_setup_hpt(spapr); } qemu_devices_reset(); @@ -1723,19 +1689,13 @@ static void spapr_machine_reset(MachineState *machine) */ fdt_addr = MIN(spapr->rma_size, RTAS_MAX_ADDR) - FDT_MAX_SIZE; - fdt = spapr_build_fdt(spapr, true); + fdt = spapr_build_fdt(spapr, true, FDT_MAX_SIZE); rc = fdt_pack(fdt); /* Should only fail if we've built a corrupted tree */ assert(rc == 0); - if (fdt_totalsize(fdt) > FDT_MAX_SIZE) { - error_report("FDT too big ! 0x%x bytes (max is 0x%x)", - fdt_totalsize(fdt), FDT_MAX_SIZE); - exit(1); - } - /* Load the fdt */ qemu_fdt_dumpdtb(fdt, fdt_totalsize(fdt)); cpu_physical_memory_write(fdt_addr, fdt, fdt_totalsize(fdt)); @@ -1745,10 +1705,19 @@ static void spapr_machine_reset(MachineState *machine) spapr->fdt_blob = fdt; /* Set up the entry state */ - spapr_cpu_set_entry_state(first_ppc_cpu, SPAPR_ENTRY_POINT, fdt_addr); + spapr_cpu_set_entry_state(first_ppc_cpu, SPAPR_ENTRY_POINT, 0, fdt_addr, 0); first_ppc_cpu->env.gpr[5] = 0; spapr->cas_reboot = false; + + spapr->fwnmi_system_reset_addr = -1; + spapr->fwnmi_machine_check_addr = -1; + spapr->fwnmi_machine_check_interlock = -1; + + /* Signal all vCPUs waiting on this condition */ + qemu_cond_broadcast(&spapr->fwnmi_machine_check_interlock_cond); + + migrate_del_blocker(spapr->fwnmi_migration_blocker); } static void spapr_create_nvram(SpaprMachineState *spapr) @@ -1910,8 +1879,6 @@ static bool spapr_ov5_cas_needed(void *opaque) { SpaprMachineState *spapr = opaque; SpaprOptionVector *ov5_mask = spapr_ovec_new(); - SpaprOptionVector *ov5_legacy = spapr_ovec_new(); - SpaprOptionVector *ov5_removed = spapr_ovec_new(); bool cas_needed; /* Prior to the introduction of SpaprOptionVector, we had two option @@ -1943,17 +1910,11 @@ static bool spapr_ov5_cas_needed(void *opaque) spapr_ovec_set(ov5_mask, OV5_DRCONF_MEMORY); spapr_ovec_set(ov5_mask, OV5_DRMEM_V2); - /* spapr_ovec_diff returns true if bits were removed. we avoid using - * the mask itself since in the future it's possible "legacy" bits may be - * removed via machine options, which could generate a false positive - * that breaks migration. - */ - spapr_ovec_intersect(ov5_legacy, spapr->ov5, ov5_mask); - cas_needed = spapr_ovec_diff(ov5_removed, spapr->ov5, ov5_legacy); + /* We need extra information if we have any bits outside the mask + * defined above */ + cas_needed = !spapr_ovec_subset(spapr->ov5, ov5_mask); spapr_ovec_cleanup(ov5_mask); - spapr_ovec_cleanup(ov5_legacy); - spapr_ovec_cleanup(ov5_removed); return cas_needed; } @@ -2039,6 +2000,43 @@ static const VMStateDescription vmstate_spapr_dtb = { }, }; +static bool spapr_fwnmi_needed(void *opaque) +{ + SpaprMachineState *spapr = (SpaprMachineState *)opaque; + + return spapr->fwnmi_machine_check_addr != -1; +} + +static int spapr_fwnmi_pre_save(void *opaque) +{ + SpaprMachineState *spapr = (SpaprMachineState *)opaque; + + /* + * Check if machine check handling is in progress and print a + * warning message. + */ + if (spapr->fwnmi_machine_check_interlock != -1) { + warn_report("A machine check is being handled during migration. The" + "handler may run and log hardware error on the destination"); + } + + return 0; +} + +static const VMStateDescription vmstate_spapr_fwnmi = { + .name = "spapr_fwnmi", + .version_id = 1, + .minimum_version_id = 1, + .needed = spapr_fwnmi_needed, + .pre_save = spapr_fwnmi_pre_save, + .fields = (VMStateField[]) { + VMSTATE_UINT64(fwnmi_system_reset_addr, SpaprMachineState), + VMSTATE_UINT64(fwnmi_machine_check_addr, SpaprMachineState), + VMSTATE_INT32(fwnmi_machine_check_interlock, SpaprMachineState), + VMSTATE_END_OF_LIST() + }, +}; + static const VMStateDescription vmstate_spapr = { .name = "spapr", .version_id = 3, @@ -2072,6 +2070,8 @@ static const VMStateDescription vmstate_spapr = { &vmstate_spapr_dtb, &vmstate_spapr_cap_large_decr, &vmstate_spapr_cap_ccf_assist, + &vmstate_spapr_cap_fwnmi, + &vmstate_spapr_fwnmi, NULL } }; @@ -2564,7 +2564,7 @@ static void spapr_set_vsmt_mode(SpaprMachineState *spapr, Error **errp) " requires the use of VSMT mode %d.\n", smp_threads, kvm_smt, spapr->vsmt); } - kvmppc_hint_smt_possible(&local_err); + kvmppc_error_append_smt_possible_hint(&local_err); goto out; } } @@ -2657,18 +2657,53 @@ static PCIHostState *spapr_create_default_phb(void) return PCI_HOST_BRIDGE(dev); } +static hwaddr spapr_rma_size(SpaprMachineState *spapr, Error **errp) +{ + MachineState *machine = MACHINE(spapr); + SpaprMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr); + hwaddr rma_size = machine->ram_size; + hwaddr node0_size = spapr_node0_size(machine); + + /* RMA has to fit in the first NUMA node */ + rma_size = MIN(rma_size, node0_size); + + /* + * VRMA access is via a special 1TiB SLB mapping, so the RMA can + * never exceed that + */ + rma_size = MIN(rma_size, 1 * TiB); + + /* + * Clamp the RMA size based on machine type. This is for + * migration compatibility with older qemu versions, which limited + * the RMA size for complicated and mostly bad reasons. + */ + if (smc->rma_limit) { + rma_size = MIN(rma_size, smc->rma_limit); + } + + if (rma_size < MIN_RMA_SLOF) { + error_setg(errp, + "pSeries SLOF firmware requires >= %" HWADDR_PRIx + "ldMiB guest RMA (Real Mode Area memory)", + MIN_RMA_SLOF / MiB); + return 0; + } + + return rma_size; +} + /* pSeries LPAR / sPAPR hardware init */ static void spapr_machine_init(MachineState *machine) { SpaprMachineState *spapr = SPAPR_MACHINE(machine); SpaprMachineClass *smc = SPAPR_MACHINE_GET_CLASS(machine); + MachineClass *mc = MACHINE_GET_CLASS(machine); const char *kernel_filename = machine->kernel_filename; const char *initrd_filename = machine->initrd_filename; PCIHostState *phb; int i; MemoryRegion *sysmem = get_system_memory(); - MemoryRegion *ram = g_new(MemoryRegion, 1); - hwaddr node0_size = spapr_node0_size(machine); long load_limit, fw_size; char *filename; Error *resize_hpt_err = NULL; @@ -2708,34 +2743,7 @@ static void spapr_machine_init(MachineState *machine) exit(1); } - spapr->rma_size = node0_size; - - /* With KVM, we don't actually know whether KVM supports an - * unbounded RMA (PR KVM) or is limited by the hash table size - * (HV KVM using VRMA), so we always assume the latter - * - * In that case, we also limit the initial allocations for RTAS - * etc... to 256M since we have no way to know what the VRMA size - * is going to be as it depends on the size of the hash table - * which isn't determined yet. - */ - if (kvm_enabled()) { - spapr->vrma_adjust = 1; - spapr->rma_size = MIN(spapr->rma_size, 0x10000000); - } - - /* Actually we don't support unbounded RMA anymore since we added - * proper emulation of HV mode. The max we can get is 16G which - * also happens to be what we configure for PAPR mode so make sure - * we don't do anything bigger than that - */ - spapr->rma_size = MIN(spapr->rma_size, 0x400000000ull); - - if (spapr->rma_size > node0_size) { - error_report("Numa node 0 has to span the RMA (%#08"HWADDR_PRIx")", - spapr->rma_size); - exit(1); - } + spapr->rma_size = spapr_rma_size(spapr, &error_fatal); /* Setup a load limit for the ramdisk leaving room for SLOF and FDT */ load_limit = MIN(spapr->rma_size, RTAS_MAX_ADDR) - FW_OVERHEAD; @@ -2846,10 +2854,8 @@ static void spapr_machine_init(MachineState *machine) kvmppc_enable_h_page_init(); } - /* allocate RAM */ - memory_region_allocate_system_memory(ram, NULL, "ppc_spapr.ram", - machine->ram_size); - memory_region_add_subregion(sysmem, 0, ram); + /* map RAM */ + memory_region_add_subregion(sysmem, 0, machine->ram); /* always allocate the device memory information */ machine->device_memory = g_malloc0(sizeof(*machine->device_memory)); @@ -2887,6 +2893,17 @@ static void spapr_machine_init(MachineState *machine) spapr_create_lmb_dr_connectors(spapr); } + if (spapr_get_cap(spapr, SPAPR_CAP_FWNMI) == SPAPR_CAP_ON) { + /* Create the error string for live migration blocker */ + error_setg(&spapr->fwnmi_migration_blocker, + "A machine check is being handled during migration. The handler" + "may run and log hardware error on the destination"); + } + + if (mc->nvdimm_supported) { + spapr_create_nvdimm_dr_connectors(spapr); + } + /* Set up RTAS event infrastructure */ spapr_events_init(spapr); @@ -2963,24 +2980,18 @@ static void spapr_machine_init(MachineState *machine) } } - if (spapr->rma_size < (MIN_RMA_SLOF * MiB)) { - error_report( - "pSeries SLOF firmware requires >= %ldM guest RMA (Real Mode Area memory)", - MIN_RMA_SLOF); - exit(1); - } - if (kernel_filename) { uint64_t lowaddr = 0; spapr->kernel_size = load_elf(kernel_filename, NULL, - translate_kernel_address, NULL, - NULL, &lowaddr, NULL, 1, + translate_kernel_address, spapr, + NULL, &lowaddr, NULL, NULL, 1, PPC_ELF_MACHINE, 0, 0); if (spapr->kernel_size == ELF_LOAD_WRONG_ENDIAN) { spapr->kernel_size = load_elf(kernel_filename, NULL, - translate_kernel_address, NULL, NULL, - &lowaddr, NULL, 0, PPC_ELF_MACHINE, + translate_kernel_address, spapr, NULL, + &lowaddr, NULL, NULL, 0, + PPC_ELF_MACHINE, 0, 0); spapr->kernel_le = spapr->kernel_size > 0; } @@ -2995,7 +3006,7 @@ static void spapr_machine_init(MachineState *machine) /* Try to locate the initrd in the gap between the kernel * and the firmware. Add a bit of space just in case */ - spapr->initrd_base = (KERNEL_LOAD_ADDR + spapr->kernel_size + spapr->initrd_base = (spapr->kernel_addr + spapr->kernel_size + 0x1ffff) & ~0xffff; spapr->initrd_size = load_image_targphys(initrd_filename, spapr->initrd_base, @@ -3028,7 +3039,7 @@ static void spapr_machine_init(MachineState *machine) * interface, this is a legacy from the sPAPREnvironment structure * which predated MachineState but had a similar function */ vmstate_register(NULL, 0, &vmstate_spapr, spapr); - register_savevm_live("spapr/htab", -1, 1, + register_savevm_live("spapr/htab", VMSTATE_INSTANCE_ID_ANY, 1, &savevm_htab_handlers, spapr); qbus_set_hotplug_handler(sysbus_get_default(), OBJECT(machine), @@ -3050,6 +3061,8 @@ static void spapr_machine_init(MachineState *machine) kvmppc_spapr_enable_inkernel_multitce(); } + + qemu_cond_init(&spapr->fwnmi_machine_check_interlock_cond); } static int spapr_kvm_type(MachineState *machine, const char *vm_type) @@ -3227,18 +3240,6 @@ static void spapr_set_resize_hpt(Object *obj, const char *value, Error **errp) } } -static void spapr_get_vsmt(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - visit_type_uint32(v, name, (uint32_t *)opaque, errp); -} - -static void spapr_set_vsmt(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - visit_type_uint32(v, name, (uint32_t *)opaque, errp); -} - static char *spapr_get_ic_mode(Object *obj, Error **errp) { SpaprMachineState *spapr = SPAPR_MACHINE(obj); @@ -3336,14 +3337,24 @@ static void spapr_instance_init(Object *obj) object_property_set_description(obj, "resize-hpt", "Resizing of the Hash Page Table (enabled, disabled, required)", NULL); - object_property_add(obj, "vsmt", "uint32", spapr_get_vsmt, - spapr_set_vsmt, NULL, &spapr->vsmt, &error_abort); + object_property_add_uint32_ptr(obj, "vsmt", + &spapr->vsmt, OBJ_PROP_FLAG_READWRITE, + &error_abort); object_property_set_description(obj, "vsmt", "Virtual SMT: KVM behaves as if this were" " the host's SMT mode", &error_abort); + object_property_add_bool(obj, "vfio-no-msix-emulation", spapr_get_msix_emulation, NULL, NULL); + object_property_add_uint64_ptr(obj, "kernel-addr", + &spapr->kernel_addr, OBJ_PROP_FLAG_READWRITE, + &error_abort); + object_property_set_description(obj, "kernel-addr", + stringify(KERNEL_LOAD_ADDR) + " for -kernel is the default", + NULL); + spapr->kernel_addr = KERNEL_LOAD_ADDR; /* The machine class defines the default interrupt controller mode */ spapr->irq = smc->irq; object_property_add_str(obj, "ic-mode", spapr_get_ic_mode, @@ -3373,8 +3384,28 @@ static void spapr_machine_finalizefn(Object *obj) void spapr_do_system_reset_on_cpu(CPUState *cs, run_on_cpu_data arg) { + SpaprMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); + cpu_synchronize_state(cs); - ppc_cpu_do_system_reset(cs); + /* If FWNMI is inactive, addr will be -1, which will deliver to 0x100 */ + if (spapr->fwnmi_system_reset_addr != -1) { + uint64_t rtas_addr, addr; + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; + + /* get rtas addr from fdt */ + rtas_addr = spapr_get_rtas_addr(); + if (!rtas_addr) { + qemu_system_guest_panicked(NULL); + return; + } + + addr = rtas_addr + RTAS_ERROR_LOG_MAX + cs->cpu_index * sizeof(uint64_t)*2; + stq_be_phys(&address_space_memory, addr, env->gpr[3]); + stq_be_phys(&address_space_memory, addr + sizeof(uint64_t), 0); + env->gpr[3] = addr; + } + ppc_cpu_do_system_reset(cs, spapr->fwnmi_system_reset_addr); } static void spapr_nmi(NMIState *n, int cpu_index, Error **errp) @@ -3395,8 +3426,8 @@ int spapr_lmb_dt_populate(SpaprDrc *drc, SpaprMachineState *spapr, addr = spapr_drc_index(drc) * SPAPR_MEMORY_BLOCK_SIZE; node = object_property_get_uint(OBJECT(drc->dev), PC_DIMM_NODE_PROP, &error_abort); - *fdt_start_offset = spapr_populate_memory_node(fdt, node, addr, - SPAPR_MEMORY_BLOCK_SIZE); + *fdt_start_offset = spapr_dt_memory_node(fdt, node, addr, + SPAPR_MEMORY_BLOCK_SIZE); return 0; } @@ -3454,7 +3485,8 @@ static void spapr_memory_plug(HotplugHandler *hotplug_dev, DeviceState *dev, Error *local_err = NULL; SpaprMachineState *ms = SPAPR_MACHINE(hotplug_dev); PCDIMMDevice *dimm = PC_DIMM(dev); - uint64_t size, addr; + uint64_t size, addr, slot; + bool is_nvdimm = object_dynamic_cast(OBJECT(dev), TYPE_NVDIMM); size = memory_device_get_region_size(MEMORY_DEVICE(dev), &error_abort); @@ -3463,14 +3495,24 @@ static void spapr_memory_plug(HotplugHandler *hotplug_dev, DeviceState *dev, goto out; } - addr = object_property_get_uint(OBJECT(dimm), - PC_DIMM_ADDR_PROP, &local_err); - if (local_err) { - goto out_unplug; + if (!is_nvdimm) { + addr = object_property_get_uint(OBJECT(dimm), + PC_DIMM_ADDR_PROP, &local_err); + if (local_err) { + goto out_unplug; + } + spapr_add_lmbs(dev, addr, size, + spapr_ovec_test(ms->ov5_cas, OV5_HP_EVT), + &local_err); + } else { + slot = object_property_get_uint(OBJECT(dimm), + PC_DIMM_SLOT_PROP, &local_err); + if (local_err) { + goto out_unplug; + } + spapr_add_nvdimm(dev, slot, &local_err); } - spapr_add_lmbs(dev, addr, size, spapr_ovec_test(ms->ov5_cas, OV5_HP_EVT), - &local_err); if (local_err) { goto out_unplug; } @@ -3488,6 +3530,8 @@ static void spapr_memory_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, { const SpaprMachineClass *smc = SPAPR_MACHINE_GET_CLASS(hotplug_dev); SpaprMachineState *spapr = SPAPR_MACHINE(hotplug_dev); + const MachineClass *mc = MACHINE_CLASS(smc); + bool is_nvdimm = object_dynamic_cast(OBJECT(dev), TYPE_NVDIMM); PCDIMMDevice *dimm = PC_DIMM(dev); Error *local_err = NULL; uint64_t size; @@ -3499,16 +3543,27 @@ static void spapr_memory_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, return; } + if (is_nvdimm && !mc->nvdimm_supported) { + error_setg(errp, "NVDIMM hotplug not supported for this machine"); + return; + } + size = memory_device_get_region_size(MEMORY_DEVICE(dimm), &local_err); if (local_err) { error_propagate(errp, local_err); return; } - if (size % SPAPR_MEMORY_BLOCK_SIZE) { + if (!is_nvdimm && size % SPAPR_MEMORY_BLOCK_SIZE) { error_setg(errp, "Hotplugged memory size must be a multiple of " - "%" PRIu64 " MB", SPAPR_MEMORY_BLOCK_SIZE / MiB); + "%" PRIu64 " MB", SPAPR_MEMORY_BLOCK_SIZE / MiB); return; + } else if (is_nvdimm) { + spapr_nvdimm_validate_opts(NVDIMM(dev), size, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } } memdev = object_property_get_link(OBJECT(dimm), PC_DIMM_MEMDEV_PROP, @@ -3648,6 +3703,12 @@ static void spapr_memory_unplug_request(HotplugHandler *hotplug_dev, int i; SpaprDrc *drc; + if (object_dynamic_cast(OBJECT(dev), TYPE_NVDIMM)) { + error_setg(&local_err, + "nvdimm device hot unplug is not supported yet."); + goto out; + } + size = memory_device_get_region_size(MEMORY_DEVICE(dimm), &error_abort); nr_lmbs = size / SPAPR_MEMORY_BLOCK_SIZE; @@ -3767,7 +3828,7 @@ int spapr_core_dt_populate(SpaprDrc *drc, SpaprMachineState *spapr, offset = fdt_add_subnode(fdt, 0, nodename); g_free(nodename); - spapr_populate_cpu_dt(cs, fdt, offset, spapr); + spapr_dt_cpu(cs, fdt, offset, spapr); *fdt_start_offset = offset; return 0; @@ -4275,6 +4336,42 @@ static void spapr_pic_print_info(InterruptStatsProvider *obj, kvm_irqchip_in_kernel() ? "in-kernel" : "emulated"); } +/* + * This is a XIVE only operation + */ +static int spapr_match_nvt(XiveFabric *xfb, uint8_t format, + uint8_t nvt_blk, uint32_t nvt_idx, + bool cam_ignore, uint8_t priority, + uint32_t logic_serv, XiveTCTXMatch *match) +{ + SpaprMachineState *spapr = SPAPR_MACHINE(xfb); + XivePresenter *xptr = XIVE_PRESENTER(spapr->active_intc); + XivePresenterClass *xpc = XIVE_PRESENTER_GET_CLASS(xptr); + int count; + + count = xpc->match_nvt(xptr, format, nvt_blk, nvt_idx, cam_ignore, + priority, logic_serv, match); + if (count < 0) { + return count; + } + + /* + * When we implement the save and restore of the thread interrupt + * contexts in the enter/exit CPU handlers of the machine and the + * escalations in QEMU, we should be able to handle non dispatched + * vCPUs. + * + * Until this is done, the sPAPR machine should find at least one + * matching context always. + */ + if (count == 0) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: NVT %x/%x is not dispatched\n", + nvt_blk, nvt_idx); + } + + return count; +} + int spapr_get_vcpu_id(PowerPCCPU *cpu) { return cpu->vcpu_id; @@ -4371,6 +4468,7 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data) PPCVirtualHypervisorClass *vhc = PPC_VIRTUAL_HYPERVISOR_CLASS(oc); XICSFabricClass *xic = XICS_FABRIC_CLASS(oc); InterruptStatsProviderClass *ispc = INTERRUPT_STATS_PROVIDER_CLASS(oc); + XiveFabricClass *xfc = XIVE_FABRIC_CLASS(oc); mc->desc = "pSeries Logical Partition (PAPR compliant)"; mc->ignore_boot_device_suffixes = true; @@ -4387,6 +4485,7 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data) mc->no_parallel = 1; mc->default_boot_order = ""; mc->default_ram_size = 512 * MiB; + mc->default_ram_id = "ppc_spapr.ram"; mc->default_display = "std"; mc->kvm_type = spapr_kvm_type; machine_class_allow_dynamic_sysbus_dev(mc, TYPE_SPAPR_PCI_HOST_BRIDGE); @@ -4405,6 +4504,7 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data) smc->update_dt_enabled = true; mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("power9_v2.0"); mc->has_hotpluggable_cpus = true; + mc->nvdimm_supported = true; smc->resize_hpt_default = SPAPR_RESIZE_HPT_ENABLED; fwc->get_dev_path = spapr_get_fw_dev_path; nc->nmi_monitor_handler = spapr_nmi; @@ -4440,13 +4540,15 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data) smc->default_caps.caps[SPAPR_CAP_HPT_MAXPAGESIZE] = 16; /* 64kiB */ smc->default_caps.caps[SPAPR_CAP_NESTED_KVM_HV] = SPAPR_CAP_OFF; smc->default_caps.caps[SPAPR_CAP_LARGE_DECREMENTER] = SPAPR_CAP_ON; - smc->default_caps.caps[SPAPR_CAP_CCF_ASSIST] = SPAPR_CAP_OFF; + smc->default_caps.caps[SPAPR_CAP_CCF_ASSIST] = SPAPR_CAP_ON; + smc->default_caps.caps[SPAPR_CAP_FWNMI] = SPAPR_CAP_ON; spapr_caps_add_properties(smc, &error_abort); smc->irq = &spapr_irq_dual; smc->dr_phb_enabled = true; smc->linux_pci_probe = true; smc->smp_threads_vsmt = true; smc->nr_xirqs = SPAPR_NR_XIRQS; + xfc->match_nvt = spapr_match_nvt; } static const TypeInfo spapr_machine_info = { @@ -4465,10 +4567,17 @@ static const TypeInfo spapr_machine_info = { { TYPE_PPC_VIRTUAL_HYPERVISOR }, { TYPE_XICS_FABRIC }, { TYPE_INTERRUPT_STATS_PROVIDER }, + { TYPE_XIVE_FABRIC }, { } }, }; +static void spapr_machine_latest_class_options(MachineClass *mc) +{ + mc->alias = "pseries"; + mc->is_default = true; +} + #define DEFINE_SPAPR_MACHINE(suffix, verstr, latest) \ static void spapr_machine_##suffix##_class_init(ObjectClass *oc, \ void *data) \ @@ -4476,8 +4585,7 @@ static const TypeInfo spapr_machine_info = { MachineClass *mc = MACHINE_CLASS(oc); \ spapr_machine_##suffix##_class_options(mc); \ if (latest) { \ - mc->alias = "pseries"; \ - mc->is_default = 1; \ + spapr_machine_latest_class_options(mc); \ } \ } \ static const TypeInfo spapr_machine_##suffix##_info = { \ @@ -4492,14 +4600,31 @@ static const TypeInfo spapr_machine_info = { type_init(spapr_machine_register_##suffix) /* - * pseries-4.2 + * pseries-5.0 */ -static void spapr_machine_4_2_class_options(MachineClass *mc) +static void spapr_machine_5_0_class_options(MachineClass *mc) { /* Defaults for the latest behaviour inherited from the base class */ } -DEFINE_SPAPR_MACHINE(4_2, "4.2", true); +DEFINE_SPAPR_MACHINE(5_0, "5.0", true); + +/* + * pseries-4.2 + */ +static void spapr_machine_4_2_class_options(MachineClass *mc) +{ + SpaprMachineClass *smc = SPAPR_MACHINE_CLASS(mc); + + spapr_machine_5_0_class_options(mc); + compat_props_add(mc->compat_props, hw_compat_4_2, hw_compat_4_2_len); + smc->default_caps.caps[SPAPR_CAP_CCF_ASSIST] = SPAPR_CAP_OFF; + smc->default_caps.caps[SPAPR_CAP_FWNMI] = SPAPR_CAP_OFF; + smc->rma_limit = 16 * GiB; + mc->nvdimm_supported = false; +} + +DEFINE_SPAPR_MACHINE(4_2, "4.2", false); /* * pseries-4.1 diff --git a/hw/ppc/spapr_caps.c b/hw/ppc/spapr_caps.c index 481dfd2a27..eb54f94227 100644 --- a/hw/ppc/spapr_caps.c +++ b/hw/ppc/spapr_caps.c @@ -485,17 +485,46 @@ static void cap_ccf_assist_apply(SpaprMachineState *spapr, uint8_t val, uint8_t kvm_val = kvmppc_get_cap_count_cache_flush_assist(); if (tcg_enabled() && val) { - /* TODO - for now only allow broken for TCG */ - error_setg(errp, -"Requested count cache flush assist capability level not supported by tcg," - " try appending -machine cap-ccf-assist=off"); + /* TCG doesn't implement anything here, but allow with a warning */ + warn_report("TCG doesn't support requested feature, cap-ccf-assist=on"); } else if (kvm_enabled() && (val > kvm_val)) { + uint8_t kvm_ibs = kvmppc_get_cap_safe_indirect_branch(); + + if (kvm_ibs == SPAPR_CAP_FIXED_CCD) { + /* + * If we don't have CCF assist on the host, the assist + * instruction is a harmless no-op. It won't correctly + * implement the cache count flush *but* if we have + * count-cache-disabled in the host, that flush is + * unnnecessary. So, specifically allow this case. This + * allows us to have better performance on POWER9 DD2.3, + * while still working on POWER9 DD2.2 and POWER8 host + * cpus. + */ + return; + } error_setg(errp, "Requested count cache flush assist capability level not supported by kvm," " try appending -machine cap-ccf-assist=off"); } } +static void cap_fwnmi_apply(SpaprMachineState *spapr, uint8_t val, + Error **errp) +{ + if (!val) { + return; /* Disabled by default */ + } + + if (kvm_enabled()) { + if (!kvmppc_get_fwnmi()) { + error_setg(errp, +"Firmware Assisted Non-Maskable Interrupts(FWNMI) not supported by KVM."); + error_append_hint(errp, "Try appending -machine cap-fwnmi=off\n"); + } + } +} + SpaprCapabilityInfo capability_table[SPAPR_CAP_NUM] = { [SPAPR_CAP_HTM] = { .name = "htm", @@ -595,6 +624,15 @@ SpaprCapabilityInfo capability_table[SPAPR_CAP_NUM] = { .type = "bool", .apply = cap_ccf_assist_apply, }, + [SPAPR_CAP_FWNMI] = { + .name = "fwnmi", + .description = "Implements PAPR FWNMI option", + .index = SPAPR_CAP_FWNMI, + .get = spapr_cap_get_bool, + .set = spapr_cap_set_bool, + .type = "bool", + .apply = cap_fwnmi_apply, + }, }; static SpaprCapabilities default_caps_with_cpu(SpaprMachineState *spapr, @@ -734,6 +772,7 @@ SPAPR_CAP_MIG_STATE(hpt_maxpagesize, SPAPR_CAP_HPT_MAXPAGESIZE); SPAPR_CAP_MIG_STATE(nested_kvm_hv, SPAPR_CAP_NESTED_KVM_HV); SPAPR_CAP_MIG_STATE(large_decr, SPAPR_CAP_LARGE_DECREMENTER); SPAPR_CAP_MIG_STATE(ccf_assist, SPAPR_CAP_CCF_ASSIST); +SPAPR_CAP_MIG_STATE(fwnmi, SPAPR_CAP_FWNMI); void spapr_caps_init(SpaprMachineState *spapr) { diff --git a/hw/ppc/spapr_cpu_core.c b/hw/ppc/spapr_cpu_core.c index 8339c4c0f8..ac1c109427 100644 --- a/hw/ppc/spapr_cpu_core.c +++ b/hw/ppc/spapr_cpu_core.c @@ -50,22 +50,14 @@ static void spapr_reset_vcpu(PowerPCCPU *cpu) * the settings below ensure proper operations with TCG in absence of * a real hypervisor. * - * Clearing VPM0 will also cause us to use RMOR in mmu-hash64.c for - * real mode accesses, which thankfully defaults to 0 and isn't - * accessible in guest mode. - * * Disable Power-saving mode Exit Cause exceptions for the CPU, so * we don't get spurious wakups before an RTAS start-cpu call. * For the same reason, set PSSCR_EC. */ - lpcr &= ~(LPCR_VPM0 | LPCR_VPM1 | LPCR_ISL | LPCR_KBV | pcc->lpcr_pm); + lpcr &= ~(LPCR_VPM1 | LPCR_ISL | LPCR_KBV | pcc->lpcr_pm); lpcr |= LPCR_LPES0 | LPCR_LPES1; env->spr[SPR_PSSCR] |= PSSCR_EC; - /* Set RMLS to the max (ie, 16G) */ - lpcr &= ~LPCR_RMLS; - lpcr |= 1ull << LPCR_RMLS_SHIFT; - ppc_store_lpcr(cpu, lpcr); /* Set a full AMOR so guest can use the AMR as it sees fit */ @@ -84,13 +76,17 @@ static void spapr_reset_vcpu(PowerPCCPU *cpu) spapr_irq_cpu_intc_reset(spapr, cpu); } -void spapr_cpu_set_entry_state(PowerPCCPU *cpu, target_ulong nip, target_ulong r3) +void spapr_cpu_set_entry_state(PowerPCCPU *cpu, target_ulong nip, + target_ulong r1, target_ulong r3, + target_ulong r4) { PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); CPUPPCState *env = &cpu->env; env->nip = nip; + env->gpr[1] = r1; env->gpr[3] = r3; + env->gpr[4] = r4; kvmppc_set_reg_ppc_online(cpu, 1); CPU(cpu)->halted = 0; /* Enable Power-saving mode Exit Cause exceptions */ @@ -383,7 +379,7 @@ static void spapr_cpu_core_class_init(ObjectClass *oc, void *data) dc->realize = spapr_cpu_core_realize; dc->unrealize = spapr_cpu_core_unrealize; dc->reset = spapr_cpu_core_reset; - dc->props = spapr_cpu_core_properties; + device_class_set_props(dc, spapr_cpu_core_properties); scc->cpu_type = data; } diff --git a/hw/ppc/spapr_drc.c b/hw/ppc/spapr_drc.c index 62f1a42592..47e6bb12f9 100644 --- a/hw/ppc/spapr_drc.c +++ b/hw/ppc/spapr_drc.c @@ -22,6 +22,7 @@ #include "qemu/error-report.h" #include "hw/ppc/spapr.h" /* for RTAS return codes */ #include "hw/pci-host/spapr.h" /* spapr_phb_remove_pci_device_cb callback */ +#include "hw/ppc/spapr_nvdimm.h" #include "sysemu/device_tree.h" #include "sysemu/reset.h" #include "trace.h" @@ -455,21 +456,46 @@ void spapr_drc_reset(SpaprDrc *drc) } } -bool spapr_drc_needed(void *opaque) +static bool spapr_drc_unplug_requested_needed(void *opaque) +{ + return spapr_drc_unplug_requested(opaque); +} + +static const VMStateDescription vmstate_spapr_drc_unplug_requested = { + .name = "spapr_drc/unplug_requested", + .version_id = 1, + .minimum_version_id = 1, + .needed = spapr_drc_unplug_requested_needed, + .fields = (VMStateField []) { + VMSTATE_BOOL(unplug_requested, SpaprDrc), + VMSTATE_END_OF_LIST() + } +}; + +bool spapr_drc_transient(SpaprDrc *drc) { - SpaprDrc *drc = (SpaprDrc *)opaque; SpaprDrcClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); - /* If no dev is plugged in there is no need to migrate the DRC state */ + /* + * If no dev is plugged in there is no need to migrate the DRC state + * nor to reset the DRC at CAS. + */ if (!drc->dev) { return false; } /* - * We need to migrate the state if it's not equal to the expected - * long-term state, which is the same as the coldplugged initial - * state */ - return (drc->state != drck->ready_state); + * We need to reset the DRC at CAS or to migrate the DRC state if it's + * not equal to the expected long-term state, which is the same as the + * coldplugged initial state, or if an unplug request is pending. + */ + return drc->state != drck->ready_state || + spapr_drc_unplug_requested(drc); +} + +static bool spapr_drc_needed(void *opaque) +{ + return spapr_drc_transient(opaque); } static const VMStateDescription vmstate_spapr_drc = { @@ -480,6 +506,10 @@ static const VMStateDescription vmstate_spapr_drc = { .fields = (VMStateField []) { VMSTATE_UINT32(state, SpaprDrc), VMSTATE_END_OF_LIST() + }, + .subsections = (const VMStateDescription * []) { + &vmstate_spapr_drc_unplug_requested, + NULL } }; @@ -511,7 +541,7 @@ static void realize(DeviceState *d, Error **errp) error_propagate(errp, err); return; } - vmstate_register(DEVICE(drc), spapr_drc_index(drc), &vmstate_spapr_drc, + vmstate_register(VMSTATE_IF(drc), spapr_drc_index(drc), &vmstate_spapr_drc, drc); trace_spapr_drc_realize_complete(spapr_drc_index(drc)); } @@ -523,7 +553,7 @@ static void unrealize(DeviceState *d, Error **errp) gchar *name; trace_spapr_drc_unrealize(spapr_drc_index(drc)); - vmstate_unregister(DEVICE(drc), &vmstate_spapr_drc, drc); + vmstate_unregister(VMSTATE_IF(drc), &vmstate_spapr_drc, drc); root_container = container_get(object_get_root(), DRC_CONTAINER_PATH); name = g_strdup_printf("%x", spapr_drc_index(drc)); object_property_del(root_container, name, errp); @@ -553,7 +583,8 @@ static void spapr_dr_connector_instance_init(Object *obj) SpaprDrc *drc = SPAPR_DR_CONNECTOR(obj); SpaprDrcClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); - object_property_add_uint32_ptr(obj, "id", &drc->id, NULL); + object_property_add_uint32_ptr(obj, "id", &drc->id, OBJ_PROP_FLAG_READ, + NULL); object_property_add(obj, "index", "uint32", prop_get_index, NULL, NULL, NULL, NULL); object_property_add(obj, "fdt", "struct", prop_get_fdt, @@ -619,7 +650,8 @@ static void realize_physical(DeviceState *d, Error **errp) return; } - vmstate_register(DEVICE(drcp), spapr_drc_index(SPAPR_DR_CONNECTOR(drcp)), + vmstate_register(VMSTATE_IF(drcp), + spapr_drc_index(SPAPR_DR_CONNECTOR(drcp)), &vmstate_spapr_drc_physical, drcp); qemu_register_reset(drc_physical_reset, drcp); } @@ -635,7 +667,7 @@ static void unrealize_physical(DeviceState *d, Error **errp) return; } - vmstate_unregister(DEVICE(drcp), &vmstate_spapr_drc_physical, drcp); + vmstate_unregister(VMSTATE_IF(drcp), &vmstate_spapr_drc_physical, drcp); qemu_unregister_reset(drc_physical_reset, drcp); } @@ -708,6 +740,17 @@ static void spapr_drc_phb_class_init(ObjectClass *k, void *data) drck->dt_populate = spapr_phb_dt_populate; } +static void spapr_drc_pmem_class_init(ObjectClass *k, void *data) +{ + SpaprDrcClass *drck = SPAPR_DR_CONNECTOR_CLASS(k); + + drck->typeshift = SPAPR_DR_CONNECTOR_TYPE_SHIFT_PMEM; + drck->typename = "PMEM"; + drck->drc_name_prefix = "PMEM "; + drck->release = NULL; + drck->dt_populate = spapr_pmem_dt_populate; +} + static const TypeInfo spapr_dr_connector_info = { .name = TYPE_SPAPR_DR_CONNECTOR, .parent = TYPE_DEVICE, @@ -758,6 +801,12 @@ static const TypeInfo spapr_drc_phb_info = { .class_init = spapr_drc_phb_class_init, }; +static const TypeInfo spapr_drc_pmem_info = { + .name = TYPE_SPAPR_DRC_PMEM, + .parent = TYPE_SPAPR_DRC_LOGICAL, + .class_init = spapr_drc_pmem_class_init, +}; + /* helper functions for external users */ SpaprDrc *spapr_drc_by_index(uint32_t index) @@ -1229,6 +1278,7 @@ static void spapr_drc_register_types(void) type_register_static(&spapr_drc_pci_info); type_register_static(&spapr_drc_lmb_info); type_register_static(&spapr_drc_phb_info); + type_register_static(&spapr_drc_pmem_info); spapr_rtas_register(RTAS_SET_INDICATOR, "set-indicator", rtas_set_indicator); diff --git a/hw/ppc/spapr_events.c b/hw/ppc/spapr_events.c index e355e000d0..1069d0197b 100644 --- a/hw/ppc/spapr_events.c +++ b/hw/ppc/spapr_events.c @@ -40,8 +40,10 @@ #include "hw/ppc/spapr_drc.h" #include "qemu/help_option.h" #include "qemu/bcd.h" +#include "qemu/main-loop.h" #include "hw/ppc/spapr_ovec.h" #include +#include "migration/blocker.h" #define RTAS_LOG_VERSION_MASK 0xff000000 #define RTAS_LOG_VERSION_6 0x06000000 @@ -194,6 +196,7 @@ struct rtas_event_log_v6_hp { #define RTAS_LOG_V6_HP_TYPE_SLOT 3 #define RTAS_LOG_V6_HP_TYPE_PHB 4 #define RTAS_LOG_V6_HP_TYPE_PCI 5 +#define RTAS_LOG_V6_HP_TYPE_PMEM 6 uint8_t hotplug_action; #define RTAS_LOG_V6_HP_ACTION_ADD 1 #define RTAS_LOG_V6_HP_ACTION_REMOVE 2 @@ -213,6 +216,112 @@ struct hp_extended_log { struct rtas_event_log_v6_hp hp; } QEMU_PACKED; +struct rtas_event_log_v6_mc { +#define RTAS_LOG_V6_SECTION_ID_MC 0x4D43 /* MC */ + struct rtas_event_log_v6_section_header hdr; + uint32_t fru_id; + uint32_t proc_id; + uint8_t error_type; +#define RTAS_LOG_V6_MC_TYPE_UE 0 +#define RTAS_LOG_V6_MC_TYPE_SLB 1 +#define RTAS_LOG_V6_MC_TYPE_ERAT 2 +#define RTAS_LOG_V6_MC_TYPE_TLB 4 +#define RTAS_LOG_V6_MC_TYPE_D_CACHE 5 +#define RTAS_LOG_V6_MC_TYPE_I_CACHE 7 + uint8_t sub_err_type; +#define RTAS_LOG_V6_MC_UE_INDETERMINATE 0 +#define RTAS_LOG_V6_MC_UE_IFETCH 1 +#define RTAS_LOG_V6_MC_UE_PAGE_TABLE_WALK_IFETCH 2 +#define RTAS_LOG_V6_MC_UE_LOAD_STORE 3 +#define RTAS_LOG_V6_MC_UE_PAGE_TABLE_WALK_LOAD_STORE 4 +#define RTAS_LOG_V6_MC_SLB_PARITY 0 +#define RTAS_LOG_V6_MC_SLB_MULTIHIT 1 +#define RTAS_LOG_V6_MC_SLB_INDETERMINATE 2 +#define RTAS_LOG_V6_MC_ERAT_PARITY 1 +#define RTAS_LOG_V6_MC_ERAT_MULTIHIT 2 +#define RTAS_LOG_V6_MC_ERAT_INDETERMINATE 3 +#define RTAS_LOG_V6_MC_TLB_PARITY 1 +#define RTAS_LOG_V6_MC_TLB_MULTIHIT 2 +#define RTAS_LOG_V6_MC_TLB_INDETERMINATE 3 +/* + * Per PAPR, + * For UE error type, set bit 1 of sub_err_type to indicate effective addr is + * provided. For other error types (SLB/ERAT/TLB), set bit 0 to indicate + * same. + */ +#define RTAS_LOG_V6_MC_UE_EA_ADDR_PROVIDED 0x40 +#define RTAS_LOG_V6_MC_EA_ADDR_PROVIDED 0x80 + uint8_t reserved_1[6]; + uint64_t effective_address; + uint64_t logical_address; +} QEMU_PACKED; + +struct mc_extended_log { + struct rtas_event_log_v6 v6hdr; + struct rtas_event_log_v6_mc mc; +} QEMU_PACKED; + +struct MC_ierror_table { + unsigned long srr1_mask; + unsigned long srr1_value; + bool nip_valid; /* nip is a valid indicator of faulting address */ + uint8_t error_type; + uint8_t error_subtype; + unsigned int initiator; + unsigned int severity; +}; + +static const struct MC_ierror_table mc_ierror_table[] = { +{ 0x00000000081c0000, 0x0000000000040000, true, + RTAS_LOG_V6_MC_TYPE_UE, RTAS_LOG_V6_MC_UE_IFETCH, + RTAS_LOG_INITIATOR_CPU, RTAS_LOG_SEVERITY_ERROR_SYNC, }, +{ 0x00000000081c0000, 0x0000000000080000, true, + RTAS_LOG_V6_MC_TYPE_SLB, RTAS_LOG_V6_MC_SLB_PARITY, + RTAS_LOG_INITIATOR_CPU, RTAS_LOG_SEVERITY_ERROR_SYNC, }, +{ 0x00000000081c0000, 0x00000000000c0000, true, + RTAS_LOG_V6_MC_TYPE_SLB, RTAS_LOG_V6_MC_SLB_MULTIHIT, + RTAS_LOG_INITIATOR_CPU, RTAS_LOG_SEVERITY_ERROR_SYNC, }, +{ 0x00000000081c0000, 0x0000000000100000, true, + RTAS_LOG_V6_MC_TYPE_ERAT, RTAS_LOG_V6_MC_ERAT_MULTIHIT, + RTAS_LOG_INITIATOR_CPU, RTAS_LOG_SEVERITY_ERROR_SYNC, }, +{ 0x00000000081c0000, 0x0000000000140000, true, + RTAS_LOG_V6_MC_TYPE_TLB, RTAS_LOG_V6_MC_TLB_MULTIHIT, + RTAS_LOG_INITIATOR_CPU, RTAS_LOG_SEVERITY_ERROR_SYNC, }, +{ 0x00000000081c0000, 0x0000000000180000, true, + RTAS_LOG_V6_MC_TYPE_UE, RTAS_LOG_V6_MC_UE_PAGE_TABLE_WALK_IFETCH, + RTAS_LOG_INITIATOR_CPU, RTAS_LOG_SEVERITY_ERROR_SYNC, } }; + +struct MC_derror_table { + unsigned long dsisr_value; + bool dar_valid; /* dar is a valid indicator of faulting address */ + uint8_t error_type; + uint8_t error_subtype; + unsigned int initiator; + unsigned int severity; +}; + +static const struct MC_derror_table mc_derror_table[] = { +{ 0x00008000, false, + RTAS_LOG_V6_MC_TYPE_UE, RTAS_LOG_V6_MC_UE_LOAD_STORE, + RTAS_LOG_INITIATOR_CPU, RTAS_LOG_SEVERITY_ERROR_SYNC, }, +{ 0x00004000, true, + RTAS_LOG_V6_MC_TYPE_UE, RTAS_LOG_V6_MC_UE_PAGE_TABLE_WALK_LOAD_STORE, + RTAS_LOG_INITIATOR_CPU, RTAS_LOG_SEVERITY_ERROR_SYNC, }, +{ 0x00000800, true, + RTAS_LOG_V6_MC_TYPE_ERAT, RTAS_LOG_V6_MC_ERAT_MULTIHIT, + RTAS_LOG_INITIATOR_CPU, RTAS_LOG_SEVERITY_ERROR_SYNC, }, +{ 0x00000400, true, + RTAS_LOG_V6_MC_TYPE_TLB, RTAS_LOG_V6_MC_TLB_MULTIHIT, + RTAS_LOG_INITIATOR_CPU, RTAS_LOG_SEVERITY_ERROR_SYNC, }, +{ 0x00000080, true, + RTAS_LOG_V6_MC_TYPE_SLB, RTAS_LOG_V6_MC_SLB_MULTIHIT, /* Before PARITY */ + RTAS_LOG_INITIATOR_CPU, RTAS_LOG_SEVERITY_ERROR_SYNC, }, +{ 0x00000100, true, + RTAS_LOG_V6_MC_TYPE_SLB, RTAS_LOG_V6_MC_SLB_PARITY, + RTAS_LOG_INITIATOR_CPU, RTAS_LOG_SEVERITY_ERROR_SYNC, } }; + +#define SRR1_MC_LOADSTORE(srr1) ((srr1) & PPC_BIT(42)) + typedef enum EventClass { EVENT_CLASS_INTERNAL_ERRORS = 0, EVENT_CLASS_EPOW = 1, @@ -531,6 +640,9 @@ static void spapr_hotplug_req_event(uint8_t hp_id, uint8_t hp_action, case SPAPR_DR_CONNECTOR_TYPE_PHB: hp->hotplug_type = RTAS_LOG_V6_HP_TYPE_PHB; break; + case SPAPR_DR_CONNECTOR_TYPE_PMEM: + hp->hotplug_type = RTAS_LOG_V6_HP_TYPE_PMEM; + break; default: /* we shouldn't be signaling hotplug events for resources * that don't support them @@ -622,6 +734,202 @@ void spapr_hotplug_req_remove_by_count_indexed(SpaprDrcType drc_type, RTAS_LOG_V6_HP_ACTION_REMOVE, drc_type, &drc_id); } +static void spapr_mc_set_ea_provided_flag(struct mc_extended_log *ext_elog) +{ + switch (ext_elog->mc.error_type) { + case RTAS_LOG_V6_MC_TYPE_UE: + ext_elog->mc.sub_err_type |= RTAS_LOG_V6_MC_UE_EA_ADDR_PROVIDED; + break; + case RTAS_LOG_V6_MC_TYPE_SLB: + case RTAS_LOG_V6_MC_TYPE_ERAT: + case RTAS_LOG_V6_MC_TYPE_TLB: + ext_elog->mc.sub_err_type |= RTAS_LOG_V6_MC_EA_ADDR_PROVIDED; + break; + default: + break; + } +} + +static uint32_t spapr_mce_get_elog_type(PowerPCCPU *cpu, bool recovered, + struct mc_extended_log *ext_elog) +{ + int i; + CPUPPCState *env = &cpu->env; + uint32_t summary; + uint64_t dsisr = env->spr[SPR_DSISR]; + + summary = RTAS_LOG_VERSION_6 | RTAS_LOG_OPTIONAL_PART_PRESENT; + if (recovered) { + summary |= RTAS_LOG_DISPOSITION_FULLY_RECOVERED; + } else { + summary |= RTAS_LOG_DISPOSITION_NOT_RECOVERED; + } + + if (SRR1_MC_LOADSTORE(env->spr[SPR_SRR1])) { + for (i = 0; i < ARRAY_SIZE(mc_derror_table); i++) { + if (!(dsisr & mc_derror_table[i].dsisr_value)) { + continue; + } + + ext_elog->mc.error_type = mc_derror_table[i].error_type; + ext_elog->mc.sub_err_type = mc_derror_table[i].error_subtype; + if (mc_derror_table[i].dar_valid) { + ext_elog->mc.effective_address = cpu_to_be64(env->spr[SPR_DAR]); + spapr_mc_set_ea_provided_flag(ext_elog); + } + + summary |= mc_derror_table[i].initiator + | mc_derror_table[i].severity; + + return summary; + } + } else { + for (i = 0; i < ARRAY_SIZE(mc_ierror_table); i++) { + if ((env->spr[SPR_SRR1] & mc_ierror_table[i].srr1_mask) != + mc_ierror_table[i].srr1_value) { + continue; + } + + ext_elog->mc.error_type = mc_ierror_table[i].error_type; + ext_elog->mc.sub_err_type = mc_ierror_table[i].error_subtype; + if (mc_ierror_table[i].nip_valid) { + ext_elog->mc.effective_address = cpu_to_be64(env->nip); + spapr_mc_set_ea_provided_flag(ext_elog); + } + + summary |= mc_ierror_table[i].initiator + | mc_ierror_table[i].severity; + + return summary; + } + } + + summary |= RTAS_LOG_INITIATOR_CPU; + return summary; +} + +static void spapr_mce_dispatch_elog(PowerPCCPU *cpu, bool recovered) +{ + SpaprMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); + CPUState *cs = CPU(cpu); + CPUPPCState *env = &cpu->env; + uint64_t rtas_addr; + struct rtas_error_log log; + struct mc_extended_log *ext_elog; + uint32_t summary; + + ext_elog = g_malloc0(sizeof(*ext_elog)); + summary = spapr_mce_get_elog_type(cpu, recovered, ext_elog); + + log.summary = cpu_to_be32(summary); + log.extended_length = cpu_to_be32(sizeof(*ext_elog)); + + spapr_init_v6hdr(&ext_elog->v6hdr); + ext_elog->mc.hdr.section_id = cpu_to_be16(RTAS_LOG_V6_SECTION_ID_MC); + ext_elog->mc.hdr.section_length = + cpu_to_be16(sizeof(struct rtas_event_log_v6_mc)); + ext_elog->mc.hdr.section_version = 1; + + /* get rtas addr from fdt */ + rtas_addr = spapr_get_rtas_addr(); + if (!rtas_addr) { + if (!recovered) { + error_report( +"FWNMI: Unable to deliver machine check to guest: rtas_addr not found."); + qemu_system_guest_panicked(NULL); + } else { + warn_report( +"FWNMI: Unable to deliver machine check to guest: rtas_addr not found. " +"Machine check recovered."); + } + g_free(ext_elog); + return; + } + + /* + * By taking the interlock, we assume that the MCE will be + * delivered to the guest. CAUTION: don't add anything that could + * prevent the MCE to be delivered after this line, otherwise the + * guest won't be able to release the interlock and ultimately + * hang/crash? + */ + spapr->fwnmi_machine_check_interlock = cpu->vcpu_id; + + stq_be_phys(&address_space_memory, rtas_addr + RTAS_ERROR_LOG_OFFSET, + env->gpr[3]); + cpu_physical_memory_write(rtas_addr + RTAS_ERROR_LOG_OFFSET + + sizeof(env->gpr[3]), &log, sizeof(log)); + cpu_physical_memory_write(rtas_addr + RTAS_ERROR_LOG_OFFSET + + sizeof(env->gpr[3]) + sizeof(log), ext_elog, + sizeof(*ext_elog)); + g_free(ext_elog); + + env->gpr[3] = rtas_addr + RTAS_ERROR_LOG_OFFSET; + + ppc_cpu_do_fwnmi_machine_check(cs, spapr->fwnmi_machine_check_addr); +} + +void spapr_mce_req_event(PowerPCCPU *cpu, bool recovered) +{ + SpaprMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); + CPUState *cs = CPU(cpu); + int ret; + Error *local_err = NULL; + + if (spapr->fwnmi_machine_check_addr == -1) { + /* Non-FWNMI case, deliver it like an architected CPU interrupt. */ + cs->exception_index = POWERPC_EXCP_MCHECK; + ppc_cpu_do_interrupt(cs); + return; + } + + /* Wait for FWNMI interlock. */ + while (spapr->fwnmi_machine_check_interlock != -1) { + /* + * Check whether the same CPU got machine check error + * while still handling the mc error (i.e., before + * that CPU called "ibm,nmi-interlock") + */ + if (spapr->fwnmi_machine_check_interlock == cpu->vcpu_id) { + if (!recovered) { + error_report( +"FWNMI: Unable to deliver machine check to guest: nested machine check."); + qemu_system_guest_panicked(NULL); + } else { + warn_report( +"FWNMI: Unable to deliver machine check to guest: nested machine check. " +"Machine check recovered."); + } + return; + } + qemu_cond_wait_iothread(&spapr->fwnmi_machine_check_interlock_cond); + if (spapr->fwnmi_machine_check_addr == -1) { + /* + * If the machine was reset while waiting for the interlock, + * abort the delivery. The machine check applies to a context + * that no longer exists, so it wouldn't make sense to deliver + * it now. + */ + return; + } + } + + ret = migrate_add_blocker(spapr->fwnmi_migration_blocker, &local_err); + if (ret == -EBUSY) { + /* + * We don't want to abort so we let the migration to continue. + * In a rare case, the machine check handler will run on the target. + * Though this is not preferable, it is better than aborting + * the migration or killing the VM. It is okay to call + * migrate_del_blocker on a blocker that was not added (which the + * nmi-interlock handler would do when it's called after this). + */ + warn_report("Received a fwnmi while migration was in progress"); + } + + spapr_mce_dispatch_elog(cpu, recovered); +} + static void check_exception(PowerPCCPU *cpu, SpaprMachineState *spapr, uint32_t token, uint32_t nargs, target_ulong args, @@ -710,6 +1018,19 @@ void spapr_clear_pending_events(SpaprMachineState *spapr) } } +void spapr_clear_pending_hotplug_events(SpaprMachineState *spapr) +{ + SpaprEventLogEntry *entry = NULL, *next_entry; + + QTAILQ_FOREACH_SAFE(entry, &spapr->pending_events, next, next_entry) { + if (spapr_event_log_entry_type(entry) == RTAS_LOG_TYPE_HOTPLUG) { + QTAILQ_REMOVE(&spapr->pending_events, entry, next); + g_free(entry->extended_log); + g_free(entry); + } + } +} + void spapr_events_init(SpaprMachineState *spapr) { int epow_irq = SPAPR_IRQ_EPOW; diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c index 140f05c1c6..0d50fc9117 100644 --- a/hw/ppc/spapr_hcall.c +++ b/hw/ppc/spapr_hcall.c @@ -1,4 +1,5 @@ #include "qemu/osdep.h" +#include "qemu/cutils.h" #include "qapi/error.h" #include "sysemu/hw_accel.h" #include "sysemu/runstate.h" @@ -15,6 +16,7 @@ #include "cpu-models.h" #include "trace.h" #include "kvm_ppc.h" +#include "hw/ppc/fdt.h" #include "hw/ppc/spapr_ovec.h" #include "mmu-book3s-v3.h" #include "hw/mem/memory-device.h" @@ -830,7 +832,7 @@ static target_ulong h_page_init(PowerPCCPU *cpu, SpaprMachineState *spapr, if (!is_ram_address(spapr, dst) || (dst & ~TARGET_PAGE_MASK) != 0) { return H_PARAMETER; } - pdst = cpu_physical_memory_map(dst, &len, 1); + pdst = cpu_physical_memory_map(dst, &len, true); if (!pdst || len != TARGET_PAGE_SIZE) { return H_PARAMETER; } @@ -841,7 +843,7 @@ static target_ulong h_page_init(PowerPCCPU *cpu, SpaprMachineState *spapr, ret = H_PARAMETER; goto unmap_out; } - psrc = cpu_physical_memory_map(src, &len, 0); + psrc = cpu_physical_memory_map(src, &len, false); if (!psrc || len != TARGET_PAGE_SIZE) { ret = H_PARAMETER; goto unmap_out; @@ -1456,7 +1458,7 @@ static void spapr_check_setup_free_hpt(SpaprMachineState *spapr, spapr_free_hpt(spapr); } else if (!(patbe_new & PATE1_GR)) { /* RADIX->HASH || NOTHING->HASH : Allocate HPT */ - spapr_setup_hpt_and_vrma(spapr); + spapr_setup_hpt(spapr); } return; } @@ -1638,6 +1640,31 @@ static uint32_t cas_check_pvr(SpaprMachineState *spapr, PowerPCCPU *cpu, return best_compat; } +static void spapr_handle_transient_dev_before_cas(SpaprMachineState *spapr) +{ + Object *drc_container; + ObjectProperty *prop; + ObjectPropertyIterator iter; + + drc_container = container_get(object_get_root(), "/dr-connector"); + object_property_iter_init(&iter, drc_container); + while ((prop = object_property_iter_next(&iter))) { + SpaprDrc *drc; + + if (!strstart(prop->type, "link<", NULL)) { + continue; + } + drc = SPAPR_DR_CONNECTOR(object_property_get_link(drc_container, + prop->name, NULL)); + + if (spapr_drc_transient(drc)) { + spapr_drc_reset(drc); + } + } + + spapr_clear_pending_hotplug_events(spapr); +} + static target_ulong h_client_architecture_support(PowerPCCPU *cpu, SpaprMachineState *spapr, target_ulong opcode, @@ -1645,13 +1672,27 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu, { /* Working address in data buffer */ target_ulong addr = ppc64_phys_to_real(args[0]); + target_ulong fdt_buf = args[1]; + target_ulong fdt_bufsize = args[2]; target_ulong ov_table; uint32_t cas_pvr; - SpaprOptionVector *ov1_guest, *ov5_guest, *ov5_cas_old, *ov5_updates; + SpaprOptionVector *ov1_guest, *ov5_guest, *ov5_cas_old; bool guest_radix; Error *local_err = NULL; bool raw_mode_supported = false; bool guest_xive; + CPUState *cs; + + /* CAS is supposed to be called early when only the boot vCPU is active. */ + CPU_FOREACH(cs) { + if (cs == CPU(cpu)) { + continue; + } + if (!cs->halted) { + warn_report("guest has multiple active vCPUs at CAS, which is not allowed"); + return H_MULTI_THREADS_ACTIVE; + } + } cas_pvr = cas_check_pvr(spapr, cpu, &addr, &raw_mode_supported, &local_err); if (local_err) { @@ -1679,7 +1720,16 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu, ov_table = addr; ov1_guest = spapr_ovec_parse_vector(ov_table, 1); + if (!ov1_guest) { + warn_report("guest didn't provide option vector 1"); + return H_PARAMETER; + } ov5_guest = spapr_ovec_parse_vector(ov_table, 5); + if (!ov5_guest) { + spapr_ovec_cleanup(ov1_guest); + warn_report("guest didn't provide option vector 5"); + return H_PARAMETER; + } if (spapr_ovec_test(ov5_guest, OV5_MMU_BOTH)) { error_report("guest requested hash and radix MMU, which is invalid."); exit(EXIT_FAILURE); @@ -1746,9 +1796,7 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu, /* capabilities that have been added since CAS-generated guest reset. * if capabilities have since been removed, generate another reset */ - ov5_updates = spapr_ovec_new(); - spapr->cas_reboot = spapr_ovec_diff(ov5_updates, - ov5_cas_old, spapr->ov5_cas); + spapr->cas_reboot = !spapr_ovec_subset(ov5_cas_old, spapr->ov5_cas); spapr_ovec_cleanup(ov5_cas_old); /* Now that processing is finished, set the radix/hash bit for the * guest if it requested a valid mode; otherwise terminate the boot. */ @@ -1767,21 +1815,10 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu, } spapr->cas_pre_isa3_guest = !spapr_ovec_test(ov1_guest, OV1_PPC_3_00); spapr_ovec_cleanup(ov1_guest); - if (!spapr->cas_reboot) { - /* If spapr_machine_reset() did not set up a HPT but one is necessary - * (because the guest isn't going to use radix) then set it up here. */ - if ((spapr->patb_entry & PATE1_GR) && !guest_radix) { - /* legacy hash or new hash: */ - spapr_setup_hpt_and_vrma(spapr); - } - spapr->cas_reboot = - (spapr_h_cas_compose_response(spapr, args[1], args[2], - ov5_updates) != 0); - } /* - * Ensure the guest asks for an interrupt mode we support; otherwise - * terminate the boot. + * Ensure the guest asks for an interrupt mode we support; + * otherwise terminate the boot. */ if (guest_xive) { if (!spapr->irq->xive) { @@ -1797,17 +1834,42 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu, } } - /* - * Generate a machine reset when we have an update of the - * interrupt mode. Only required when the machine supports both - * modes. - */ - if (!spapr->cas_reboot) { - spapr->cas_reboot = spapr_ovec_test(ov5_updates, OV5_XIVE_EXPLOIT) - && spapr->irq->xics && spapr->irq->xive; - } + spapr_irq_update_active_intc(spapr); - spapr_ovec_cleanup(ov5_updates); + spapr_handle_transient_dev_before_cas(spapr); + + if (!spapr->cas_reboot) { + void *fdt; + SpaprDeviceTreeUpdateHeader hdr = { .version_id = 1 }; + + /* If spapr_machine_reset() did not set up a HPT but one is necessary + * (because the guest isn't going to use radix) then set it up here. */ + if ((spapr->patb_entry & PATE1_GR) && !guest_radix) { + /* legacy hash or new hash: */ + spapr_setup_hpt(spapr); + } + + if (fdt_bufsize < sizeof(hdr)) { + error_report("SLOF provided insufficient CAS buffer " + TARGET_FMT_lu " (min: %zu)", fdt_bufsize, sizeof(hdr)); + exit(EXIT_FAILURE); + } + + fdt_bufsize -= sizeof(hdr); + + fdt = spapr_build_fdt(spapr, false, fdt_bufsize); + _FDT((fdt_pack(fdt))); + + cpu_physical_memory_write(fdt_buf, &hdr, sizeof(hdr)); + cpu_physical_memory_write(fdt_buf + sizeof(hdr), fdt, + fdt_totalsize(fdt)); + trace_spapr_cas_continue(fdt_totalsize(fdt) + sizeof(hdr)); + + g_free(spapr->fdt_blob); + spapr->fdt_size = fdt_totalsize(fdt); + spapr->fdt_initial_size = spapr->fdt_size; + spapr->fdt_blob = fdt; + } if (spapr->cas_reboot) { qemu_system_reset_request(SHUTDOWN_CAUSE_SUBSYSTEM_RESET); diff --git a/hw/ppc/spapr_iommu.c b/hw/ppc/spapr_iommu.c index 3d3bcc8649..5704fe6051 100644 --- a/hw/ppc/spapr_iommu.c +++ b/hw/ppc/spapr_iommu.c @@ -317,7 +317,7 @@ static void spapr_tce_table_realize(DeviceState *dev, Error **errp) QLIST_INSERT_HEAD(&spapr_tce_tables, tcet, list); - vmstate_register(DEVICE(tcet), tcet->liobn, &vmstate_spapr_tce_table, + vmstate_register(VMSTATE_IF(tcet), tcet->liobn, &vmstate_spapr_tce_table, tcet); } @@ -420,7 +420,7 @@ static void spapr_tce_table_unrealize(DeviceState *dev, Error **errp) { SpaprTceTable *tcet = SPAPR_TCE_TABLE(dev); - vmstate_unregister(DEVICE(tcet), &vmstate_spapr_tce_table, tcet); + vmstate_unregister(VMSTATE_IF(tcet), &vmstate_spapr_tce_table, tcet); QLIST_REMOVE(tcet, list); diff --git a/hw/ppc/spapr_irq.c b/hw/ppc/spapr_irq.c index d6bb7fd2d6..1f630f296b 100644 --- a/hw/ppc/spapr_irq.c +++ b/hw/ppc/spapr_irq.c @@ -70,16 +70,16 @@ void spapr_irq_msi_free(SpaprMachineState *spapr, int irq, uint32_t num) bitmap_clear(spapr->irq_map, irq - SPAPR_IRQ_MSI, num); } -int spapr_irq_init_kvm(int (*fn)(SpaprInterruptController *, Error **), +int spapr_irq_init_kvm(SpaprInterruptControllerInitKvm fn, SpaprInterruptController *intc, + uint32_t nr_servers, Error **errp) { - MachineState *machine = MACHINE(qdev_get_machine()); Error *local_err = NULL; - if (kvm_enabled() && machine_kernel_irqchip_allowed(machine)) { - if (fn(intc, &local_err) < 0) { - if (machine_kernel_irqchip_required(machine)) { + if (kvm_enabled() && kvm_kernel_irqchip_allowed()) { + if (fn(intc, nr_servers, &local_err) < 0) { + if (kvm_kernel_irqchip_required()) { error_prepend(&local_err, "kernel_irqchip requested but unavailable: "); error_propagate(errp, local_err); @@ -184,7 +184,7 @@ static int spapr_irq_check(SpaprMachineState *spapr, Error **errp) */ if (kvm_enabled() && spapr->irq == &spapr_irq_dual && - machine_kernel_irqchip_required(machine) && + kvm_kernel_irqchip_required() && xics_kvm_has_broken_disconnect(spapr)) { error_setg(errp, "KVM is too old to support ic-mode=dual,kernel-irqchip=on"); return -1; @@ -287,20 +287,13 @@ uint32_t spapr_irq_nr_msis(SpaprMachineState *spapr) void spapr_irq_init(SpaprMachineState *spapr, Error **errp) { - MachineState *machine = MACHINE(spapr); SpaprMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr); - if (machine_kernel_irqchip_split(machine)) { + if (kvm_enabled() && kvm_kernel_irqchip_split()) { error_setg(errp, "kernel_irqchip split mode not supported on pseries"); return; } - if (!kvm_enabled() && machine_kernel_irqchip_required(machine)) { - error_setg(errp, - "kernel_irqchip requested but only available with KVM"); - return; - } - if (spapr_irq_check(spapr, errp) < 0) { return; } @@ -313,25 +306,11 @@ void spapr_irq_init(SpaprMachineState *spapr, Error **errp) Object *obj; obj = object_new(TYPE_ICS_SPAPR); - object_property_add_child(OBJECT(spapr), "ics", obj, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - - object_property_add_const_link(obj, ICS_PROP_XICS, OBJECT(spapr), - &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - - object_property_set_int(obj, smc->nr_xirqs, "nr-irqs", &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } + object_property_add_child(OBJECT(spapr), "ics", obj, &error_abort); + object_property_set_link(obj, OBJECT(spapr), ICS_PROP_XICS, + &error_abort); + object_property_set_int(obj, smc->nr_xirqs, "nr-irqs", &error_abort); object_property_set_bool(obj, true, "realized", &local_err); if (local_err) { error_propagate(errp, local_err); @@ -353,6 +332,8 @@ void spapr_irq_init(SpaprMachineState *spapr, Error **errp) * priority */ qdev_prop_set_uint32(dev, "nr-ends", nr_servers << 3); + object_property_set_link(OBJECT(dev), OBJECT(spapr), "xive-fabric", + &error_abort); qdev_init_nofail(dev); spapr->xive = SPAPR_XIVE(dev); @@ -495,6 +476,7 @@ static void set_active_intc(SpaprMachineState *spapr, SpaprInterruptController *new_intc) { SpaprInterruptControllerClass *sicc; + uint32_t nr_servers = spapr_max_server_number(spapr); assert(new_intc); @@ -512,7 +494,7 @@ static void set_active_intc(SpaprMachineState *spapr, sicc = SPAPR_INTC_GET_CLASS(new_intc); if (sicc->activate) { - sicc->activate(new_intc, &error_fatal); + sicc->activate(new_intc, nr_servers, &error_fatal); } spapr->active_intc = new_intc; diff --git a/hw/ppc/spapr_nvdimm.c b/hw/ppc/spapr_nvdimm.c new file mode 100644 index 0000000000..25be8082d7 --- /dev/null +++ b/hw/ppc/spapr_nvdimm.c @@ -0,0 +1,478 @@ +/* + * QEMU PAPR Storage Class Memory Interfaces + * + * Copyright (c) 2019-2020, IBM Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/ppc/spapr_drc.h" +#include "hw/ppc/spapr_nvdimm.h" +#include "hw/mem/nvdimm.h" +#include "qemu/nvdimm-utils.h" +#include "hw/ppc/fdt.h" +#include "qemu/range.h" + +void spapr_nvdimm_validate_opts(NVDIMMDevice *nvdimm, uint64_t size, + Error **errp) +{ + char *uuidstr = NULL; + QemuUUID uuid; + int ret; + + if (size % SPAPR_MINIMUM_SCM_BLOCK_SIZE) { + error_setg(errp, "NVDIMM memory size excluding the label area" + " must be a multiple of %" PRIu64 "MB", + SPAPR_MINIMUM_SCM_BLOCK_SIZE / MiB); + return; + } + + uuidstr = object_property_get_str(OBJECT(nvdimm), NVDIMM_UUID_PROP, + &error_abort); + ret = qemu_uuid_parse(uuidstr, &uuid); + g_assert(!ret); + g_free(uuidstr); + + if (qemu_uuid_is_null(&uuid)) { + error_setg(errp, "NVDIMM device requires the uuid to be set"); + return; + } +} + + +void spapr_add_nvdimm(DeviceState *dev, uint64_t slot, Error **errp) +{ + SpaprDrc *drc; + bool hotplugged = spapr_drc_hotplugged(dev); + Error *local_err = NULL; + + drc = spapr_drc_by_id(TYPE_SPAPR_DRC_PMEM, slot); + g_assert(drc); + + spapr_drc_attach(drc, dev, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + if (hotplugged) { + spapr_hotplug_req_add_by_index(drc); + } +} + +int spapr_pmem_dt_populate(SpaprDrc *drc, SpaprMachineState *spapr, + void *fdt, int *fdt_start_offset, Error **errp) +{ + NVDIMMDevice *nvdimm = NVDIMM(drc->dev); + + *fdt_start_offset = spapr_dt_nvdimm(fdt, 0, nvdimm); + + return 0; +} + +void spapr_create_nvdimm_dr_connectors(SpaprMachineState *spapr) +{ + MachineState *machine = MACHINE(spapr); + int i; + + for (i = 0; i < machine->ram_slots; i++) { + spapr_dr_connector_new(OBJECT(spapr), TYPE_SPAPR_DRC_PMEM, i); + } +} + + +int spapr_dt_nvdimm(void *fdt, int parent_offset, + NVDIMMDevice *nvdimm) +{ + int child_offset; + char *buf; + SpaprDrc *drc; + uint32_t drc_idx; + uint32_t node = object_property_get_uint(OBJECT(nvdimm), PC_DIMM_NODE_PROP, + &error_abort); + uint64_t slot = object_property_get_uint(OBJECT(nvdimm), PC_DIMM_SLOT_PROP, + &error_abort); + uint32_t associativity[] = { + cpu_to_be32(0x4), /* length */ + cpu_to_be32(0x0), cpu_to_be32(0x0), + cpu_to_be32(0x0), cpu_to_be32(node) + }; + uint64_t lsize = nvdimm->label_size; + uint64_t size = object_property_get_int(OBJECT(nvdimm), PC_DIMM_SIZE_PROP, + NULL); + + drc = spapr_drc_by_id(TYPE_SPAPR_DRC_PMEM, slot); + g_assert(drc); + + drc_idx = spapr_drc_index(drc); + + buf = g_strdup_printf("ibm,pmemory@%x", drc_idx); + child_offset = fdt_add_subnode(fdt, parent_offset, buf); + g_free(buf); + + _FDT(child_offset); + + _FDT((fdt_setprop_cell(fdt, child_offset, "reg", drc_idx))); + _FDT((fdt_setprop_string(fdt, child_offset, "compatible", "ibm,pmemory"))); + _FDT((fdt_setprop_string(fdt, child_offset, "device_type", "ibm,pmemory"))); + + _FDT((fdt_setprop(fdt, child_offset, "ibm,associativity", associativity, + sizeof(associativity)))); + + buf = qemu_uuid_unparse_strdup(&nvdimm->uuid); + _FDT((fdt_setprop_string(fdt, child_offset, "ibm,unit-guid", buf))); + g_free(buf); + + _FDT((fdt_setprop_cell(fdt, child_offset, "ibm,my-drc-index", drc_idx))); + + _FDT((fdt_setprop_u64(fdt, child_offset, "ibm,block-size", + SPAPR_MINIMUM_SCM_BLOCK_SIZE))); + _FDT((fdt_setprop_u64(fdt, child_offset, "ibm,number-of-blocks", + size / SPAPR_MINIMUM_SCM_BLOCK_SIZE))); + _FDT((fdt_setprop_cell(fdt, child_offset, "ibm,metadata-size", lsize))); + + _FDT((fdt_setprop_string(fdt, child_offset, "ibm,pmem-application", + "operating-system"))); + _FDT(fdt_setprop(fdt, child_offset, "ibm,cache-flush-required", NULL, 0)); + + return child_offset; +} + +void spapr_dt_persistent_memory(void *fdt) +{ + int offset = fdt_subnode_offset(fdt, 0, "persistent-memory"); + GSList *iter, *nvdimms = nvdimm_get_device_list(); + + if (offset < 0) { + offset = fdt_add_subnode(fdt, 0, "persistent-memory"); + _FDT(offset); + _FDT((fdt_setprop_cell(fdt, offset, "#address-cells", 0x1))); + _FDT((fdt_setprop_cell(fdt, offset, "#size-cells", 0x0))); + _FDT((fdt_setprop_string(fdt, offset, "device_type", + "ibm,persistent-memory"))); + } + + /* Create DT entries for cold plugged NVDIMM devices */ + for (iter = nvdimms; iter; iter = iter->next) { + NVDIMMDevice *nvdimm = iter->data; + + spapr_dt_nvdimm(fdt, offset, nvdimm); + } + g_slist_free(nvdimms); + + return; +} + +static target_ulong h_scm_read_metadata(PowerPCCPU *cpu, + SpaprMachineState *spapr, + target_ulong opcode, + target_ulong *args) +{ + uint32_t drc_index = args[0]; + uint64_t offset = args[1]; + uint64_t len = args[2]; + SpaprDrc *drc = spapr_drc_by_index(drc_index); + NVDIMMDevice *nvdimm; + NVDIMMClass *ddc; + uint64_t data = 0; + uint8_t buf[8] = { 0 }; + + if (!drc || !drc->dev || + spapr_drc_type(drc) != SPAPR_DR_CONNECTOR_TYPE_PMEM) { + return H_PARAMETER; + } + + if (len != 1 && len != 2 && + len != 4 && len != 8) { + return H_P3; + } + + nvdimm = NVDIMM(drc->dev); + if ((offset + len < offset) || + (nvdimm->label_size < len + offset)) { + return H_P2; + } + + ddc = NVDIMM_GET_CLASS(nvdimm); + ddc->read_label_data(nvdimm, buf, len, offset); + + switch (len) { + case 1: + data = ldub_p(buf); + break; + case 2: + data = lduw_be_p(buf); + break; + case 4: + data = ldl_be_p(buf); + break; + case 8: + data = ldq_be_p(buf); + break; + default: + g_assert_not_reached(); + } + + args[0] = data; + + return H_SUCCESS; +} + +static target_ulong h_scm_write_metadata(PowerPCCPU *cpu, + SpaprMachineState *spapr, + target_ulong opcode, + target_ulong *args) +{ + uint32_t drc_index = args[0]; + uint64_t offset = args[1]; + uint64_t data = args[2]; + uint64_t len = args[3]; + SpaprDrc *drc = spapr_drc_by_index(drc_index); + NVDIMMDevice *nvdimm; + NVDIMMClass *ddc; + uint8_t buf[8] = { 0 }; + + if (!drc || !drc->dev || + spapr_drc_type(drc) != SPAPR_DR_CONNECTOR_TYPE_PMEM) { + return H_PARAMETER; + } + + if (len != 1 && len != 2 && + len != 4 && len != 8) { + return H_P4; + } + + nvdimm = NVDIMM(drc->dev); + if ((offset + len < offset) || + (nvdimm->label_size < len + offset)) { + return H_P2; + } + + switch (len) { + case 1: + if (data & 0xffffffffffffff00) { + return H_P2; + } + stb_p(buf, data); + break; + case 2: + if (data & 0xffffffffffff0000) { + return H_P2; + } + stw_be_p(buf, data); + break; + case 4: + if (data & 0xffffffff00000000) { + return H_P2; + } + stl_be_p(buf, data); + break; + case 8: + stq_be_p(buf, data); + break; + default: + g_assert_not_reached(); + } + + ddc = NVDIMM_GET_CLASS(nvdimm); + ddc->write_label_data(nvdimm, buf, len, offset); + + return H_SUCCESS; +} + +static target_ulong h_scm_bind_mem(PowerPCCPU *cpu, SpaprMachineState *spapr, + target_ulong opcode, target_ulong *args) +{ + uint32_t drc_index = args[0]; + uint64_t starting_idx = args[1]; + uint64_t no_of_scm_blocks_to_bind = args[2]; + uint64_t target_logical_mem_addr = args[3]; + uint64_t continue_token = args[4]; + uint64_t size; + uint64_t total_no_of_scm_blocks; + SpaprDrc *drc = spapr_drc_by_index(drc_index); + hwaddr addr; + NVDIMMDevice *nvdimm; + + if (!drc || !drc->dev || + spapr_drc_type(drc) != SPAPR_DR_CONNECTOR_TYPE_PMEM) { + return H_PARAMETER; + } + + /* + * Currently continue token should be zero qemu has already bound + * everything and this hcall doesnt return H_BUSY. + */ + if (continue_token > 0) { + return H_P5; + } + + /* Currently qemu assigns the address. */ + if (target_logical_mem_addr != 0xffffffffffffffff) { + return H_OVERLAP; + } + + nvdimm = NVDIMM(drc->dev); + + size = object_property_get_uint(OBJECT(nvdimm), + PC_DIMM_SIZE_PROP, &error_abort); + + total_no_of_scm_blocks = size / SPAPR_MINIMUM_SCM_BLOCK_SIZE; + + if (starting_idx > total_no_of_scm_blocks) { + return H_P2; + } + + if (((starting_idx + no_of_scm_blocks_to_bind) < starting_idx) || + ((starting_idx + no_of_scm_blocks_to_bind) > total_no_of_scm_blocks)) { + return H_P3; + } + + addr = object_property_get_uint(OBJECT(nvdimm), + PC_DIMM_ADDR_PROP, &error_abort); + + addr += starting_idx * SPAPR_MINIMUM_SCM_BLOCK_SIZE; + + /* Already bound, Return target logical address in R5 */ + args[1] = addr; + args[2] = no_of_scm_blocks_to_bind; + + return H_SUCCESS; +} + +static target_ulong h_scm_unbind_mem(PowerPCCPU *cpu, SpaprMachineState *spapr, + target_ulong opcode, target_ulong *args) +{ + uint32_t drc_index = args[0]; + uint64_t starting_scm_logical_addr = args[1]; + uint64_t no_of_scm_blocks_to_unbind = args[2]; + uint64_t continue_token = args[3]; + uint64_t size_to_unbind; + Range blockrange = range_empty; + Range nvdimmrange = range_empty; + SpaprDrc *drc = spapr_drc_by_index(drc_index); + NVDIMMDevice *nvdimm; + uint64_t size, addr; + + if (!drc || !drc->dev || + spapr_drc_type(drc) != SPAPR_DR_CONNECTOR_TYPE_PMEM) { + return H_PARAMETER; + } + + /* continue_token should be zero as this hcall doesn't return H_BUSY. */ + if (continue_token > 0) { + return H_P4; + } + + /* Check if starting_scm_logical_addr is block aligned */ + if (!QEMU_IS_ALIGNED(starting_scm_logical_addr, + SPAPR_MINIMUM_SCM_BLOCK_SIZE)) { + return H_P2; + } + + size_to_unbind = no_of_scm_blocks_to_unbind * SPAPR_MINIMUM_SCM_BLOCK_SIZE; + if (no_of_scm_blocks_to_unbind == 0 || no_of_scm_blocks_to_unbind != + size_to_unbind / SPAPR_MINIMUM_SCM_BLOCK_SIZE) { + return H_P3; + } + + nvdimm = NVDIMM(drc->dev); + size = object_property_get_int(OBJECT(nvdimm), PC_DIMM_SIZE_PROP, + &error_abort); + addr = object_property_get_int(OBJECT(nvdimm), PC_DIMM_ADDR_PROP, + &error_abort); + + range_init_nofail(&nvdimmrange, addr, size); + range_init_nofail(&blockrange, starting_scm_logical_addr, size_to_unbind); + + if (!range_contains_range(&nvdimmrange, &blockrange)) { + return H_P3; + } + + args[1] = no_of_scm_blocks_to_unbind; + + /* let unplug take care of actual unbind */ + return H_SUCCESS; +} + +#define H_UNBIND_SCOPE_ALL 0x1 +#define H_UNBIND_SCOPE_DRC 0x2 + +static target_ulong h_scm_unbind_all(PowerPCCPU *cpu, SpaprMachineState *spapr, + target_ulong opcode, target_ulong *args) +{ + uint64_t target_scope = args[0]; + uint32_t drc_index = args[1]; + uint64_t continue_token = args[2]; + NVDIMMDevice *nvdimm; + uint64_t size; + uint64_t no_of_scm_blocks_unbound = 0; + + /* continue_token should be zero as this hcall doesn't return H_BUSY. */ + if (continue_token > 0) { + return H_P4; + } + + if (target_scope == H_UNBIND_SCOPE_DRC) { + SpaprDrc *drc = spapr_drc_by_index(drc_index); + + if (!drc || !drc->dev || + spapr_drc_type(drc) != SPAPR_DR_CONNECTOR_TYPE_PMEM) { + return H_P2; + } + + nvdimm = NVDIMM(drc->dev); + size = object_property_get_int(OBJECT(nvdimm), PC_DIMM_SIZE_PROP, + &error_abort); + + no_of_scm_blocks_unbound = size / SPAPR_MINIMUM_SCM_BLOCK_SIZE; + } else if (target_scope == H_UNBIND_SCOPE_ALL) { + GSList *list, *nvdimms; + + nvdimms = nvdimm_get_device_list(); + for (list = nvdimms; list; list = list->next) { + nvdimm = list->data; + size = object_property_get_int(OBJECT(nvdimm), PC_DIMM_SIZE_PROP, + &error_abort); + + no_of_scm_blocks_unbound += size / SPAPR_MINIMUM_SCM_BLOCK_SIZE; + } + g_slist_free(nvdimms); + } else { + return H_PARAMETER; + } + + args[1] = no_of_scm_blocks_unbound; + + /* let unplug take care of actual unbind */ + return H_SUCCESS; +} + +static void spapr_scm_register_types(void) +{ + /* qemu/scm specific hcalls */ + spapr_register_hypercall(H_SCM_READ_METADATA, h_scm_read_metadata); + spapr_register_hypercall(H_SCM_WRITE_METADATA, h_scm_write_metadata); + spapr_register_hypercall(H_SCM_BIND_MEM, h_scm_bind_mem); + spapr_register_hypercall(H_SCM_UNBIND_MEM, h_scm_unbind_mem); + spapr_register_hypercall(H_SCM_UNBIND_ALL, h_scm_unbind_all); +} + +type_init(spapr_scm_register_types) diff --git a/hw/ppc/spapr_ovec.c b/hw/ppc/spapr_ovec.c index 811fadf143..dd003f1763 100644 --- a/hw/ppc/spapr_ovec.c +++ b/hw/ppc/spapr_ovec.c @@ -76,31 +76,21 @@ void spapr_ovec_intersect(SpaprOptionVector *ov, bitmap_and(ov->bitmap, ov1->bitmap, ov2->bitmap, OV_MAXBITS); } -/* returns true if options bits were removed, false otherwise */ -bool spapr_ovec_diff(SpaprOptionVector *ov, - SpaprOptionVector *ov_old, - SpaprOptionVector *ov_new) +/* returns true if ov1 has a subset of bits in ov2 */ +bool spapr_ovec_subset(SpaprOptionVector *ov1, SpaprOptionVector *ov2) { - unsigned long *change_mask = bitmap_new(OV_MAXBITS); - unsigned long *removed_bits = bitmap_new(OV_MAXBITS); - bool bits_were_removed = false; + unsigned long *tmp = bitmap_new(OV_MAXBITS); + bool result; - g_assert(ov); - g_assert(ov_old); - g_assert(ov_new); + g_assert(ov1); + g_assert(ov2); - bitmap_xor(change_mask, ov_old->bitmap, ov_new->bitmap, OV_MAXBITS); - bitmap_and(ov->bitmap, ov_new->bitmap, change_mask, OV_MAXBITS); - bitmap_and(removed_bits, ov_old->bitmap, change_mask, OV_MAXBITS); + bitmap_andnot(tmp, ov1->bitmap, ov2->bitmap, OV_MAXBITS); + result = bitmap_empty(tmp, OV_MAXBITS); - if (!bitmap_empty(removed_bits, OV_MAXBITS)) { - bits_were_removed = true; - } + g_free(tmp); - g_free(change_mask); - g_free(removed_bits); - - return bits_were_removed; + return result; } void spapr_ovec_cleanup(SpaprOptionVector *ov) @@ -210,8 +200,8 @@ SpaprOptionVector *spapr_ovec_parse_vector(target_ulong table_addr, int vector) return ov; } -int spapr_ovec_populate_dt(void *fdt, int fdt_offset, - SpaprOptionVector *ov, const char *name) +int spapr_dt_ovec(void *fdt, int fdt_offset, + SpaprOptionVector *ov, const char *name) { uint8_t vec[OV_MAXBYTES + 1]; uint16_t vec_len; diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c index f6fbcf99ed..55ca9dee1e 100644 --- a/hw/ppc/spapr_pci.c +++ b/hw/ppc/spapr_pci.c @@ -1663,6 +1663,7 @@ static void spapr_pci_unplug_request(HotplugHandler *plug_handler, if (pc->is_bridge) { error_setg(errp, "PCI: Hot unplug of PCI bridges not supported"); + return; } /* ensure any other present functions are pending unplug */ @@ -2014,7 +2015,7 @@ static int spapr_phb_children_reset(Object *child, void *opaque) DeviceState *dev = (DeviceState *) object_dynamic_cast(child, TYPE_DEVICE); if (dev) { - device_reset(dev); + device_legacy_reset(dev); } return 0; @@ -2042,13 +2043,13 @@ void spapr_phb_dma_reset(SpaprPhbState *sphb) static void spapr_phb_reset(DeviceState *qdev) { SpaprPhbState *sphb = SPAPR_PCI_HOST_BRIDGE(qdev); - Error *errp = NULL; + Error *err = NULL; spapr_phb_dma_reset(sphb); spapr_phb_nvgpu_free(sphb); - spapr_phb_nvgpu_setup(sphb, &errp); - if (errp) { - error_report_err(errp); + spapr_phb_nvgpu_setup(sphb, &err); + if (err) { + error_report_err(err); } /* Reset the IOMMU state */ @@ -2217,7 +2218,7 @@ static void spapr_phb_class_init(ObjectClass *klass, void *data) hc->root_bus_path = spapr_phb_root_bus_path; dc->realize = spapr_phb_realize; dc->unrealize = spapr_phb_unrealize; - dc->props = spapr_phb_properties; + device_class_set_props(dc, spapr_phb_properties); dc->reset = spapr_phb_reset; dc->vmsd = &vmstate_spapr_pci; /* Supported by TYPE_SPAPR_MACHINE */ @@ -2326,7 +2327,7 @@ int spapr_dt_phb(SpaprMachineState *spapr, SpaprPhbState *phb, cpu_to_be32(phb->numa_node)}; SpaprTceTable *tcet; SpaprDrc *drc; - Error *errp = NULL; + Error *err = NULL; /* Start populating the FDT */ _FDT(bus_off = fdt_add_subnode(fdt, 0, phb->dtbusname)); @@ -2408,9 +2409,9 @@ int spapr_dt_phb(SpaprMachineState *spapr, SpaprPhbState *phb, return ret; } - spapr_phb_nvgpu_populate_dt(phb, fdt, bus_off, &errp); - if (errp) { - error_report_err(errp); + spapr_phb_nvgpu_populate_dt(phb, fdt, bus_off, &err); + if (err) { + error_report_err(err); } spapr_phb_nvgpu_ram_populate_dt(phb, fdt); diff --git a/hw/ppc/spapr_pci_nvlink2.c b/hw/ppc/spapr_pci_nvlink2.c index 4aa89ede23..8332d5694e 100644 --- a/hw/ppc/spapr_pci_nvlink2.c +++ b/hw/ppc/spapr_pci_nvlink2.c @@ -57,7 +57,7 @@ struct SpaprPhbPciNvGpuConfig { uint64_t nv2_atsd_current; int num; /* number of non empty (i.e. tgt!=0) entries in slots[] */ SpaprPhbPciNvGpuSlot slots[NVGPU_MAX_NUM]; - Error *errp; + Error *err; }; static SpaprPhbPciNvGpuSlot * @@ -153,7 +153,7 @@ static void spapr_phb_pci_collect_nvgpu(PCIBus *bus, PCIDevice *pdev, spapr_pci_collect_nvnpu(nvgpus, pdev, tgt, MEMORY_REGION(mr_npu), &local_err); } - error_propagate(&nvgpus->errp, local_err); + error_propagate(&nvgpus->err, local_err); } if ((pci_default_read_config(pdev, PCI_HEADER_TYPE, 1) != PCI_HEADER_TYPE_BRIDGE)) { @@ -187,9 +187,9 @@ void spapr_phb_nvgpu_setup(SpaprPhbState *sphb, Error **errp) pci_for_each_device(bus, pci_bus_num(bus), spapr_phb_pci_collect_nvgpu, sphb->nvgpus); - if (sphb->nvgpus->errp) { - error_propagate(errp, sphb->nvgpus->errp); - sphb->nvgpus->errp = NULL; + if (sphb->nvgpus->err) { + error_propagate(errp, sphb->nvgpus->err); + sphb->nvgpus->err = NULL; goto cleanup_exit; } diff --git a/hw/ppc/spapr_rng.c b/hw/ppc/spapr_rng.c index 954e4aec4b..e8e8d65ec0 100644 --- a/hw/ppc/spapr_rng.c +++ b/hw/ppc/spapr_rng.c @@ -146,7 +146,7 @@ static void spapr_rng_class_init(ObjectClass *oc, void *data) dc->realize = spapr_rng_realize; set_bit(DEVICE_CATEGORY_MISC, dc->categories); - dc->props = spapr_rng_properties; + device_class_set_props(dc, spapr_rng_properties); dc->hotpluggable = false; } diff --git a/hw/ppc/spapr_rtas.c b/hw/ppc/spapr_rtas.c index 8d8d8cdfcb..bcac0d00e7 100644 --- a/hw/ppc/spapr_rtas.c +++ b/hw/ppc/spapr_rtas.c @@ -50,6 +50,7 @@ #include "hw/ppc/fdt.h" #include "target/ppc/mmu-hash64.h" #include "target/ppc/mmu-book3s-v3.h" +#include "migration/blocker.h" static void rtas_display_character(PowerPCCPU *cpu, SpaprMachineState *spapr, uint32_t token, uint32_t nargs, @@ -189,7 +190,7 @@ static void rtas_start_cpu(PowerPCCPU *callcpu, SpaprMachineState *spapr, */ newcpu->env.tb_env->tb_offset = callcpu->env.tb_env->tb_offset; - spapr_cpu_set_entry_state(newcpu, start, r3); + spapr_cpu_set_entry_state(newcpu, start, 0, r3, 0); qemu_cpu_kick(CPU(newcpu)); @@ -267,8 +268,7 @@ static void rtas_ibm_get_system_parameter(PowerPCCPU *cpu, uint32_t nret, target_ulong rets) { PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); - MachineState *ms = MACHINE(qdev_get_machine()); - unsigned int max_cpus = ms->smp.max_cpus; + MachineState *ms = MACHINE(spapr); target_ulong parameter = rtas_ld(args, 0); target_ulong buffer = rtas_ld(args, 1); target_ulong length = rtas_ld(args, 2); @@ -280,10 +280,10 @@ static void rtas_ibm_get_system_parameter(PowerPCCPU *cpu, "DesMem=%" PRIu64 "," "DesProcs=%d," "MaxPlatProcs=%d", - max_cpus, - current_machine->ram_size / MiB, + ms->smp.max_cpus, + ms->ram_size / MiB, ms->smp.cpus, - max_cpus); + ms->smp.max_cpus); if (pcc->n_host_threads > 0) { char *hostthr_val, *old = param_val; @@ -345,6 +345,13 @@ static void rtas_ibm_os_term(PowerPCCPU *cpu, target_ulong args, uint32_t nret, target_ulong rets) { + target_ulong msgaddr = rtas_ld(args, 0); + char msg[512]; + + cpu_physical_memory_read(msgaddr, msg, sizeof(msg) - 1); + msg[sizeof(msg) - 1] = 0; + + error_report("OS terminated: %s", msg); qemu_system_guest_panicked(NULL); rtas_st(rets, 0, RTAS_OUT_SUCCESS); @@ -400,6 +407,95 @@ static void rtas_get_power_level(PowerPCCPU *cpu, SpaprMachineState *spapr, rtas_st(rets, 1, 100); } +static void rtas_ibm_nmi_register(PowerPCCPU *cpu, + SpaprMachineState *spapr, + uint32_t token, uint32_t nargs, + target_ulong args, + uint32_t nret, target_ulong rets) +{ + hwaddr rtas_addr; + target_ulong sreset_addr, mce_addr; + + if (spapr_get_cap(spapr, SPAPR_CAP_FWNMI) == SPAPR_CAP_OFF) { + rtas_st(rets, 0, RTAS_OUT_NOT_SUPPORTED); + return; + } + + rtas_addr = spapr_get_rtas_addr(); + if (!rtas_addr) { + rtas_st(rets, 0, RTAS_OUT_NOT_SUPPORTED); + return; + } + + sreset_addr = rtas_ld(args, 0); + mce_addr = rtas_ld(args, 1); + + /* PAPR requires these are in the first 32M of memory and within RMA */ + if (sreset_addr >= 32 * MiB || sreset_addr >= spapr->rma_size || + mce_addr >= 32 * MiB || mce_addr >= spapr->rma_size) { + rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); + return; + } + + if (kvm_enabled()) { + if (kvmppc_set_fwnmi() < 0) { + rtas_st(rets, 0, RTAS_OUT_NOT_SUPPORTED); + return; + } + } + + spapr->fwnmi_system_reset_addr = sreset_addr; + spapr->fwnmi_machine_check_addr = mce_addr; + + rtas_st(rets, 0, RTAS_OUT_SUCCESS); +} + +static void rtas_ibm_nmi_interlock(PowerPCCPU *cpu, + SpaprMachineState *spapr, + uint32_t token, uint32_t nargs, + target_ulong args, + uint32_t nret, target_ulong rets) +{ + if (spapr_get_cap(spapr, SPAPR_CAP_FWNMI) == SPAPR_CAP_OFF) { + rtas_st(rets, 0, RTAS_OUT_NOT_SUPPORTED); + return; + } + + if (spapr->fwnmi_machine_check_addr == -1) { + qemu_log_mask(LOG_GUEST_ERROR, +"FWNMI: ibm,nmi-interlock RTAS called with FWNMI not registered.\n"); + + /* NMI register not called */ + rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); + return; + } + + if (spapr->fwnmi_machine_check_interlock != cpu->vcpu_id) { + /* + * The vCPU that hit the NMI should invoke "ibm,nmi-interlock" + * This should be PARAM_ERROR, but Linux calls "ibm,nmi-interlock" + * for system reset interrupts, despite them not being interlocked. + * PowerVM silently ignores this and returns success here. Returning + * failure causes Linux to print the error "FWNMI: nmi-interlock + * failed: -3", although no other apparent ill effects, this is a + * regression for the user when enabling FWNMI. So for now, match + * PowerVM. When most Linux clients are fixed, this could be + * changed. + */ + rtas_st(rets, 0, RTAS_OUT_SUCCESS); + return; + } + + /* + * vCPU issuing "ibm,nmi-interlock" is done with NMI handling, + * hence unset fwnmi_machine_check_interlock. + */ + spapr->fwnmi_machine_check_interlock = -1; + qemu_cond_signal(&spapr->fwnmi_machine_check_interlock_cond); + rtas_st(rets, 0, RTAS_OUT_SUCCESS); + migrate_del_blocker(spapr->fwnmi_migration_blocker); +} + static struct rtas_call { const char *name; spapr_rtas_fn fn; @@ -477,6 +573,32 @@ void spapr_dt_rtas_tokens(void *fdt, int rtas) } } +hwaddr spapr_get_rtas_addr(void) +{ + SpaprMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); + int rtas_node; + const fdt32_t *rtas_data; + void *fdt = spapr->fdt_blob; + + /* fetch rtas addr from fdt */ + rtas_node = fdt_path_offset(fdt, "/rtas"); + if (rtas_node < 0) { + return 0; + } + + rtas_data = fdt_getprop(fdt, rtas_node, "linux,rtas-base", NULL); + if (!rtas_data) { + return 0; + } + + /* + * We assume that the OS called RTAS instantiate-rtas, but some other + * OS might call RTAS instantiate-rtas-64 instead. This fine as of now + * as SLOF only supports 32-bit variant. + */ + return (hwaddr)fdt32_to_cpu(*rtas_data); +} + static void core_rtas_register_types(void) { spapr_rtas_register(RTAS_DISPLAY_CHARACTER, "display-character", @@ -502,6 +624,10 @@ static void core_rtas_register_types(void) rtas_set_power_level); spapr_rtas_register(RTAS_GET_POWER_LEVEL, "get-power-level", rtas_get_power_level); + spapr_rtas_register(RTAS_IBM_NMI_REGISTER, "ibm,nmi-register", + rtas_ibm_nmi_register); + spapr_rtas_register(RTAS_IBM_NMI_INTERLOCK, "ibm,nmi-interlock", + rtas_ibm_nmi_interlock); } type_init(core_rtas_register_types) diff --git a/hw/ppc/spapr_tpm_proxy.c b/hw/ppc/spapr_tpm_proxy.c index ca1caec113..991615d77a 100644 --- a/hw/ppc/spapr_tpm_proxy.c +++ b/hw/ppc/spapr_tpm_proxy.c @@ -159,7 +159,7 @@ static void spapr_tpm_proxy_class_init(ObjectClass *k, void *data) dk->realize = spapr_tpm_proxy_realize; dk->unrealize = spapr_tpm_proxy_unrealize; dk->user_creatable = true; - dk->props = spapr_tpm_proxy_properties; + device_class_set_props(dk, spapr_tpm_proxy_properties); } static const TypeInfo spapr_tpm_proxy_info = { diff --git a/hw/ppc/spapr_vio.c b/hw/ppc/spapr_vio.c index 554de9930d..0b085eabe4 100644 --- a/hw/ppc/spapr_vio.c +++ b/hw/ppc/spapr_vio.c @@ -87,6 +87,7 @@ static int vio_make_devnode(SpaprVioDevice *dev, SpaprVioDeviceClass *pc = VIO_SPAPR_DEVICE_GET_CLASS(dev); int vdevice_off, node_off, ret; char *dt_name; + const char *dt_compatible; vdevice_off = fdt_path_offset(fdt, "/vdevice"); if (vdevice_off < 0) { @@ -113,9 +114,15 @@ static int vio_make_devnode(SpaprVioDevice *dev, } } - if (pc->dt_compatible) { + if (pc->get_dt_compatible) { + dt_compatible = pc->get_dt_compatible(dev); + } else { + dt_compatible = pc->dt_compatible; + } + + if (dt_compatible) { ret = fdt_setprop_string(fdt, node_off, "compatible", - pc->dt_compatible); + dt_compatible); if (ret < 0) { return ret; } @@ -304,7 +311,7 @@ int spapr_vio_send_crq(SpaprVioDevice *dev, uint8_t *crq) static void spapr_vio_quiesce_one(SpaprVioDevice *dev) { if (dev->tcet) { - device_reset(DEVICE(dev->tcet)); + device_legacy_reset(DEVICE(dev->tcet)); } free_crq(dev); } diff --git a/hw/ppc/virtex_ml507.c b/hw/ppc/virtex_ml507.c index 68625522d8..0dacfcd236 100644 --- a/hw/ppc/virtex_ml507.c +++ b/hw/ppc/virtex_ml507.c @@ -89,10 +89,7 @@ static void mmubooke_create_initial_mapping(CPUPPCState *env, tlb->PID = 0; } -static PowerPCCPU *ppc440_init_xilinx(ram_addr_t *ram_size, - int do_init, - const char *cpu_type, - uint32_t sysclk) +static PowerPCCPU *ppc440_init_xilinx(const char *cpu_type, uint32_t sysclk) { PowerPCCPU *cpu; CPUPPCState *env; @@ -191,12 +188,12 @@ static int xilinx_load_device_tree(hwaddr addr, if (r < 0) fprintf(stderr, "couldn't set /chosen/bootargs\n"); cpu_physical_memory_write(addr, fdt, fdt_size); + g_free(fdt); return fdt_size; } static void virtex_init(MachineState *machine) { - ram_addr_t ram_size = machine->ram_size; const char *kernel_filename = machine->kernel_filename; const char *kernel_cmdline = machine->kernel_cmdline; hwaddr initrd_base = 0; @@ -207,13 +204,12 @@ static void virtex_init(MachineState *machine) CPUPPCState *env; hwaddr ram_base = 0; DriveInfo *dinfo; - MemoryRegion *phys_ram = g_new(MemoryRegion, 1); qemu_irq irq[32], *cpu_irq; int kernel_size; int i; /* init CPUs */ - cpu = ppc440_init_xilinx(&ram_size, 1, machine->cpu_type, 400000000); + cpu = ppc440_init_xilinx(machine->cpu_type, 400000000); env = &cpu->env; if (env->mmu_model != POWERPC_MMU_BOOKE) { @@ -224,8 +220,7 @@ static void virtex_init(MachineState *machine) qemu_register_reset(main_cpu_reset, cpu); - memory_region_allocate_system_memory(phys_ram, NULL, "ram", ram_size); - memory_region_add_subregion(address_space_mem, ram_base, phys_ram); + memory_region_add_subregion(address_space_mem, ram_base, machine->ram); dinfo = drive_get(IF_PFLASH, 0, 0); pflash_cfi01_register(PFLASH_BASEADDR, "virtex.flash", FLASH_SIZE, @@ -259,7 +254,7 @@ static void virtex_init(MachineState *machine) /* Boots a kernel elf binary. */ kernel_size = load_elf(kernel_filename, NULL, NULL, NULL, - &entry, &low, &high, 1, PPC_ELF_MACHINE, + &entry, &low, &high, NULL, 1, PPC_ELF_MACHINE, 0, 0); boot_info.bootstrap_pc = entry & 0x00ffffff; @@ -268,7 +263,7 @@ static void virtex_init(MachineState *machine) /* If we failed loading ELF's try a raw image. */ kernel_size = load_image_targphys(kernel_filename, boot_offset, - ram_size); + machine->ram_size); boot_info.bootstrap_pc = boot_offset; high = boot_info.bootstrap_pc + kernel_size + 8192; } @@ -279,7 +274,7 @@ static void virtex_init(MachineState *machine) if (machine->initrd_filename) { initrd_base = high = ROUND_UP(high, 4); initrd_size = load_image_targphys(machine->initrd_filename, - high, ram_size - high); + high, machine->ram_size - high); if (initrd_size < 0) { error_report("couldn't load ram disk '%s'", @@ -293,7 +288,7 @@ static void virtex_init(MachineState *machine) boot_info.fdt = high + (8192 * 2); boot_info.fdt &= ~8191; - xilinx_load_device_tree(boot_info.fdt, ram_size, + xilinx_load_device_tree(boot_info.fdt, machine->ram_size, initrd_base, initrd_size, kernel_cmdline); } @@ -305,6 +300,7 @@ static void virtex_machine_init(MachineClass *mc) mc->desc = "Xilinx Virtex ML507 reference design"; mc->init = virtex_init; mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("440-xilinx"); + mc->default_ram_id = "ram"; } DEFINE_MACHINE("virtex-ml507", virtex_machine_init) diff --git a/hw/rdma/rdma_backend.c b/hw/rdma/rdma_backend.c index c346407cd3..3dd39fe1a7 100644 --- a/hw/rdma/rdma_backend.c +++ b/hw/rdma/rdma_backend.c @@ -377,35 +377,42 @@ static void ah_cache_init(void) destroy_ah_hash_key, destroy_ah_hast_data); } +#ifdef LEGACY_RDMA_REG_MR static int build_host_sge_array(RdmaDeviceResources *rdma_dev_res, - struct ibv_sge *dsge, struct ibv_sge *ssge, - uint8_t num_sge, uint64_t *total_length) + struct ibv_sge *sge, uint8_t num_sge, + uint64_t *total_length) { RdmaRmMR *mr; - int ssge_idx; + int idx; - for (ssge_idx = 0; ssge_idx < num_sge; ssge_idx++) { - mr = rdma_rm_get_mr(rdma_dev_res, ssge[ssge_idx].lkey); + for (idx = 0; idx < num_sge; idx++) { + mr = rdma_rm_get_mr(rdma_dev_res, sge[idx].lkey); if (unlikely(!mr)) { - rdma_error_report("Invalid lkey 0x%x", ssge[ssge_idx].lkey); - return VENDOR_ERR_INVLKEY | ssge[ssge_idx].lkey; + rdma_error_report("Invalid lkey 0x%x", sge[idx].lkey); + return VENDOR_ERR_INVLKEY | sge[idx].lkey; } -#ifdef LEGACY_RDMA_REG_MR - dsge->addr = (uintptr_t)mr->virt + ssge[ssge_idx].addr - mr->start; -#else - dsge->addr = ssge[ssge_idx].addr; -#endif - dsge->length = ssge[ssge_idx].length; - dsge->lkey = rdma_backend_mr_lkey(&mr->backend_mr); + sge[idx].addr = (uintptr_t)mr->virt + sge[idx].addr - mr->start; + sge[idx].lkey = rdma_backend_mr_lkey(&mr->backend_mr); - *total_length += dsge->length; - - dsge++; + *total_length += sge[idx].length; } return 0; } +#else +static inline int build_host_sge_array(RdmaDeviceResources *rdma_dev_res, + struct ibv_sge *sge, uint8_t num_sge, + uint64_t *total_length) +{ + int idx; + + for (idx = 0; idx < num_sge; idx++) { + *total_length += sge[idx].length; + } + return 0; +} +#endif static void trace_mad_message(const char *title, char *buf, int len) { @@ -484,7 +491,6 @@ void rdma_backend_post_send(RdmaBackendDev *backend_dev, void *ctx) { BackendCtx *bctx; - struct ibv_sge new_sge[MAX_SGE]; uint32_t bctx_id; int rc; struct ibv_send_wr wr = {}, *bad_wr; @@ -518,7 +524,7 @@ void rdma_backend_post_send(RdmaBackendDev *backend_dev, rdma_protected_gslist_append_int32(&qp->cqe_ctx_list, bctx_id); - rc = build_host_sge_array(backend_dev->rdma_dev_res, new_sge, sge, num_sge, + rc = build_host_sge_array(backend_dev->rdma_dev_res, sge, num_sge, &backend_dev->rdma_dev_res->stats.tx_len); if (rc) { complete_work(IBV_WC_GENERAL_ERR, rc, ctx); @@ -538,7 +544,7 @@ void rdma_backend_post_send(RdmaBackendDev *backend_dev, wr.num_sge = num_sge; wr.opcode = IBV_WR_SEND; wr.send_flags = IBV_SEND_SIGNALED; - wr.sg_list = new_sge; + wr.sg_list = sge; wr.wr_id = bctx_id; rc = ibv_post_send(qp->ibqp, &wr, &bad_wr); @@ -601,7 +607,6 @@ void rdma_backend_post_recv(RdmaBackendDev *backend_dev, struct ibv_sge *sge, uint32_t num_sge, void *ctx) { BackendCtx *bctx; - struct ibv_sge new_sge[MAX_SGE]; uint32_t bctx_id; int rc; struct ibv_recv_wr wr = {}, *bad_wr; @@ -635,7 +640,7 @@ void rdma_backend_post_recv(RdmaBackendDev *backend_dev, rdma_protected_gslist_append_int32(&qp->cqe_ctx_list, bctx_id); - rc = build_host_sge_array(backend_dev->rdma_dev_res, new_sge, sge, num_sge, + rc = build_host_sge_array(backend_dev->rdma_dev_res, sge, num_sge, &backend_dev->rdma_dev_res->stats.rx_bufs_len); if (rc) { complete_work(IBV_WC_GENERAL_ERR, rc, ctx); @@ -643,7 +648,7 @@ void rdma_backend_post_recv(RdmaBackendDev *backend_dev, } wr.num_sge = num_sge; - wr.sg_list = new_sge; + wr.sg_list = sge; wr.wr_id = bctx_id; rc = ibv_post_recv(qp->ibqp, &wr, &bad_wr); if (rc) { @@ -671,7 +676,6 @@ void rdma_backend_post_srq_recv(RdmaBackendDev *backend_dev, uint32_t num_sge, void *ctx) { BackendCtx *bctx; - struct ibv_sge new_sge[MAX_SGE]; uint32_t bctx_id; int rc; struct ibv_recv_wr wr = {}, *bad_wr; @@ -688,7 +692,7 @@ void rdma_backend_post_srq_recv(RdmaBackendDev *backend_dev, rdma_protected_gslist_append_int32(&srq->cqe_ctx_list, bctx_id); - rc = build_host_sge_array(backend_dev->rdma_dev_res, new_sge, sge, num_sge, + rc = build_host_sge_array(backend_dev->rdma_dev_res, sge, num_sge, &backend_dev->rdma_dev_res->stats.rx_bufs_len); if (rc) { complete_work(IBV_WC_GENERAL_ERR, rc, ctx); @@ -696,7 +700,7 @@ void rdma_backend_post_srq_recv(RdmaBackendDev *backend_dev, } wr.num_sge = num_sge; - wr.sg_list = new_sge; + wr.sg_list = sge; wr.wr_id = bctx_id; rc = ibv_post_srq_recv(srq->ibsrq, &wr, &bad_wr); if (rc) { @@ -739,13 +743,8 @@ void rdma_backend_destroy_pd(RdmaBackendPD *pd) } } -#ifdef LEGACY_RDMA_REG_MR -int rdma_backend_create_mr(RdmaBackendMR *mr, RdmaBackendPD *pd, void *addr, - size_t length, int access) -#else int rdma_backend_create_mr(RdmaBackendMR *mr, RdmaBackendPD *pd, void *addr, size_t length, uint64_t guest_start, int access) -#endif { #ifdef LEGACY_RDMA_REG_MR mr->ibmr = ibv_reg_mr(pd->ibpd, addr, length, access); diff --git a/hw/rdma/rdma_backend.h b/hw/rdma/rdma_backend.h index 127f96e2d5..225af481e0 100644 --- a/hw/rdma/rdma_backend.h +++ b/hw/rdma/rdma_backend.h @@ -78,13 +78,8 @@ int rdma_backend_query_port(RdmaBackendDev *backend_dev, int rdma_backend_create_pd(RdmaBackendDev *backend_dev, RdmaBackendPD *pd); void rdma_backend_destroy_pd(RdmaBackendPD *pd); -#ifdef LEGACY_RDMA_REG_MR -int rdma_backend_create_mr(RdmaBackendMR *mr, RdmaBackendPD *pd, void *addr, - size_t length, int access); -#else int rdma_backend_create_mr(RdmaBackendMR *mr, RdmaBackendPD *pd, void *addr, size_t length, uint64_t guest_start, int access); -#endif void rdma_backend_destroy_mr(RdmaBackendMR *mr); int rdma_backend_create_cq(RdmaBackendDev *backend_dev, RdmaBackendCQ *cq, diff --git a/hw/rdma/rdma_rm.c b/hw/rdma/rdma_rm.c index 1524dfaeaa..7e9ea283c9 100644 --- a/hw/rdma/rdma_rm.c +++ b/hw/rdma/rdma_rm.c @@ -227,21 +227,20 @@ int rdma_rm_alloc_mr(RdmaDeviceResources *dev_res, uint32_t pd_handle, mr->length = guest_length; mr->virt += (mr->start & (TARGET_PAGE_SIZE - 1)); -#ifdef LEGACY_RDMA_REG_MR - ret = rdma_backend_create_mr(&mr->backend_mr, &pd->backend_pd, mr->virt, - mr->length, access_flags); -#else ret = rdma_backend_create_mr(&mr->backend_mr, &pd->backend_pd, mr->virt, mr->length, guest_start, access_flags); -#endif if (ret) { ret = -EIO; goto out_dealloc_mr; } +#ifdef LEGACY_RDMA_REG_MR + /* We keep mr_handle in lkey so send and recv get get mr ptr */ + *lkey = *mr_handle; +#else + *lkey = rdma_backend_mr_lkey(&mr->backend_mr); +#endif } - /* We keep mr_handle in lkey so send and recv get get mr ptr */ - *lkey = *mr_handle; *rkey = -1; mr->pd_handle = pd_handle; diff --git a/hw/rdma/vmw/pvrdma_dev_ring.c b/hw/rdma/vmw/pvrdma_dev_ring.c index d7bc7f5ccc..c122fe7035 100644 --- a/hw/rdma/vmw/pvrdma_dev_ring.c +++ b/hw/rdma/vmw/pvrdma_dev_ring.c @@ -14,8 +14,10 @@ */ #include "qemu/osdep.h" +#include "qemu/cutils.h" #include "hw/pci/pci.h" #include "cpu.h" +#include "qemu/cutils.h" #include "trace.h" @@ -30,8 +32,7 @@ int pvrdma_ring_init(PvrdmaRing *ring, const char *name, PCIDevice *dev, int i; int rc = 0; - strncpy(ring->name, name, MAX_RING_NAME_SZ); - ring->name[MAX_RING_NAME_SZ - 1] = 0; + pstrcpy(ring->name, MAX_RING_NAME_SZ, name); ring->dev = dev; ring->ring_state = ring_state; ring->max_elems = max_elems; diff --git a/hw/rdma/vmw/pvrdma_main.c b/hw/rdma/vmw/pvrdma_main.c index 6f0fc405c7..3254aadb6e 100644 --- a/hw/rdma/vmw/pvrdma_main.c +++ b/hw/rdma/vmw/pvrdma_main.c @@ -690,7 +690,7 @@ static void pvrdma_class_init(ObjectClass *klass, void *data) k->class_id = PCI_CLASS_NETWORK_OTHER; dc->desc = "RDMA Device"; - dc->props = pvrdma_dev_properties; + device_class_set_props(dc, pvrdma_dev_properties); set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); ir->print_statistics = pvrdma_print_statistics; diff --git a/hw/rdma/vmw/pvrdma_qp_ops.c b/hw/rdma/vmw/pvrdma_qp_ops.c index bd6db858de..8050287a6c 100644 --- a/hw/rdma/vmw/pvrdma_qp_ops.c +++ b/hw/rdma/vmw/pvrdma_qp_ops.c @@ -34,13 +34,13 @@ typedef struct CompHandlerCtx { /* Send Queue WQE */ typedef struct PvrdmaSqWqe { struct pvrdma_sq_wqe_hdr hdr; - struct pvrdma_sge sge[0]; + struct pvrdma_sge sge[]; } PvrdmaSqWqe; /* Recv Queue WQE */ typedef struct PvrdmaRqWqe { struct pvrdma_rq_wqe_hdr hdr; - struct pvrdma_sge sge[0]; + struct pvrdma_sge sge[]; } PvrdmaRqWqe; /* diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig index b12660b9f8..ff9fbe958a 100644 --- a/hw/riscv/Kconfig +++ b/hw/riscv/Kconfig @@ -34,6 +34,7 @@ config RISCV_VIRT select PCI select HART select SERIAL + select GOLDFISH_RTC select VIRTIO_MMIO select PCI_EXPRESS_GENERIC_BRIDGE select PFLASH_CFI01 diff --git a/hw/riscv/boot.c b/hw/riscv/boot.c index 027303d2a3..b8e765277d 100644 --- a/hw/riscv/boot.c +++ b/hw/riscv/boot.c @@ -101,7 +101,7 @@ target_ulong riscv_load_firmware(const char *firmware_filename, uint64_t firmware_entry, firmware_start, firmware_end; if (load_elf(firmware_filename, NULL, NULL, NULL, &firmware_entry, - &firmware_start, &firmware_end, 0, EM_RISCV, 1, 0) > 0) { + &firmware_start, &firmware_end, NULL, 0, EM_RISCV, 1, 0) > 0) { return firmware_entry; } @@ -119,7 +119,7 @@ target_ulong riscv_load_kernel(const char *kernel_filename, symbol_fn_t sym_cb) uint64_t kernel_entry, kernel_high; if (load_elf_ram_sym(kernel_filename, NULL, NULL, NULL, - &kernel_entry, NULL, &kernel_high, 0, + &kernel_entry, NULL, &kernel_high, NULL, 0, EM_RISCV, 1, 0, NULL, true, sym_cb) > 0) { return kernel_entry; } diff --git a/hw/riscv/riscv_hart.c b/hw/riscv/riscv_hart.c index 5b98227db6..276a9baca0 100644 --- a/hw/riscv/riscv_hart.c +++ b/hw/riscv/riscv_hart.c @@ -74,7 +74,7 @@ static void riscv_harts_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->props = riscv_harts_props; + device_class_set_props(dc, riscv_harts_props); dc->realize = riscv_harts_realize; } diff --git a/hw/riscv/riscv_htif.c b/hw/riscv/riscv_htif.c index 4f7b11dc37..ca87a5cf9f 100644 --- a/hw/riscv/riscv_htif.c +++ b/hw/riscv/riscv_htif.c @@ -96,7 +96,7 @@ static void htif_recv(void *opaque, const uint8_t *buf, int size) * Called by the char dev to supply special events to the HTIF console. * Not used for HTIF. */ -static void htif_event(void *opaque, int event) +static void htif_event(void *opaque, QEMUChrEvent event) { } diff --git a/hw/riscv/sifive_clint.c b/hw/riscv/sifive_clint.c index e5a8f75cee..e933d35092 100644 --- a/hw/riscv/sifive_clint.c +++ b/hw/riscv/sifive_clint.c @@ -205,7 +205,7 @@ static void sifive_clint_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = sifive_clint_realize; - dc->props = sifive_clint_properties; + device_class_set_props(dc, sifive_clint_properties); } static const TypeInfo sifive_clint_info = { @@ -227,7 +227,8 @@ type_init(sifive_clint_register_types) * Create CLINT device. */ DeviceState *sifive_clint_create(hwaddr addr, hwaddr size, uint32_t num_harts, - uint32_t sip_base, uint32_t timecmp_base, uint32_t time_base) + uint32_t sip_base, uint32_t timecmp_base, uint32_t time_base, + bool provide_rdtime) { int i; for (i = 0; i < num_harts; i++) { @@ -236,6 +237,9 @@ DeviceState *sifive_clint_create(hwaddr addr, hwaddr size, uint32_t num_harts, if (!env) { continue; } + if (provide_rdtime) { + riscv_cpu_set_rdtime_fn(env, cpu_riscv_read_rtc); + } env->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &sifive_clint_timer_cb, cpu); env->timecmp = 0; diff --git a/hw/riscv/sifive_e.c b/hw/riscv/sifive_e.c index 8a6b0348df..646553a7c3 100644 --- a/hw/riscv/sifive_e.c +++ b/hw/riscv/sifive_e.c @@ -145,8 +145,8 @@ static void riscv_sifive_e_soc_realize(DeviceState *dev, Error **errp) &error_abort); /* Mask ROM */ - memory_region_init_rom(&s->mask_rom, NULL, "riscv.sifive.e.mrom", - memmap[SIFIVE_E_MROM].size, &error_fatal); + memory_region_init_rom(&s->mask_rom, OBJECT(dev), "riscv.sifive.e.mrom", + memmap[SIFIVE_E_MROM].size, &error_fatal); memory_region_add_subregion(sys_mem, memmap[SIFIVE_E_MROM].base, &s->mask_rom); @@ -164,7 +164,7 @@ static void riscv_sifive_e_soc_realize(DeviceState *dev, Error **errp) memmap[SIFIVE_E_PLIC].size); sifive_clint_create(memmap[SIFIVE_E_CLINT].base, memmap[SIFIVE_E_CLINT].size, ms->smp.cpus, - SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE, SIFIVE_TIME_BASE); + SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE, SIFIVE_TIME_BASE, false); create_unimplemented_device("riscv.sifive.e.aon", memmap[SIFIVE_E_AON].base, memmap[SIFIVE_E_AON].size); sifive_e_prci_create(memmap[SIFIVE_E_PRCI].base); @@ -208,9 +208,8 @@ static void riscv_sifive_e_soc_realize(DeviceState *dev, Error **errp) memmap[SIFIVE_E_PWM2].base, memmap[SIFIVE_E_PWM2].size); /* Flash memory */ - memory_region_init_ram(&s->xip_mem, NULL, "riscv.sifive.e.xip", - memmap[SIFIVE_E_XIP].size, &error_fatal); - memory_region_set_readonly(&s->xip_mem, true); + memory_region_init_rom(&s->xip_mem, OBJECT(dev), "riscv.sifive.e.xip", + memmap[SIFIVE_E_XIP].size, &error_fatal); memory_region_add_subregion(sys_mem, memmap[SIFIVE_E_XIP].base, &s->xip_mem); } diff --git a/hw/riscv/sifive_plic.c b/hw/riscv/sifive_plic.c index 98e4304b66..c1e04cbb98 100644 --- a/hw/riscv/sifive_plic.c +++ b/hw/riscv/sifive_plic.c @@ -466,7 +466,7 @@ static void sifive_plic_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->props = sifive_plic_properties; + device_class_set_props(dc, sifive_plic_properties); dc->realize = sifive_plic_realize; } diff --git a/hw/riscv/sifive_u.c b/hw/riscv/sifive_u.c index 0140e95732..56351c4faa 100644 --- a/hw/riscv/sifive_u.c +++ b/hw/riscv/sifive_u.c @@ -56,7 +56,11 @@ #include -#define BIOS_FILENAME "opensbi-riscv64-sifive_u-fw_jump.bin" +#if defined(TARGET_RISCV32) +# define BIOS_FILENAME "opensbi-riscv32-sifive_u-fw_jump.bin" +#else +# define BIOS_FILENAME "opensbi-riscv64-sifive_u-fw_jump.bin" +#endif static const struct MemmapEntry { hwaddr base; @@ -497,7 +501,7 @@ static void riscv_sifive_u_soc_realize(DeviceState *dev, Error **errp) &error_abort); /* boot rom */ - memory_region_init_rom(mask_rom, NULL, "riscv.sifive.u.mrom", + memory_region_init_rom(mask_rom, OBJECT(dev), "riscv.sifive.u.mrom", memmap[SIFIVE_U_MROM].size, &error_fatal); memory_region_add_subregion(system_memory, memmap[SIFIVE_U_MROM].base, mask_rom); @@ -542,13 +546,14 @@ static void riscv_sifive_u_soc_realize(DeviceState *dev, Error **errp) SIFIVE_U_PLIC_CONTEXT_BASE, SIFIVE_U_PLIC_CONTEXT_STRIDE, memmap[SIFIVE_U_PLIC].size); + g_free(plic_hart_config); sifive_uart_create(system_memory, memmap[SIFIVE_U_UART0].base, serial_hd(0), qdev_get_gpio_in(DEVICE(s->plic), SIFIVE_U_UART0_IRQ)); sifive_uart_create(system_memory, memmap[SIFIVE_U_UART1].base, serial_hd(1), qdev_get_gpio_in(DEVICE(s->plic), SIFIVE_U_UART1_IRQ)); sifive_clint_create(memmap[SIFIVE_U_CLINT].base, memmap[SIFIVE_U_CLINT].size, ms->smp.cpus, - SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE, SIFIVE_TIME_BASE); + SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE, SIFIVE_TIME_BASE, false); object_property_set_bool(OBJECT(&s->prci), true, "realized", &err); sysbus_mmio_map(SYS_BUS_DEVICE(&s->prci), 0, memmap[SIFIVE_U_PRCI].base); diff --git a/hw/riscv/sifive_u_otp.c b/hw/riscv/sifive_u_otp.c index ea0eee5678..f6ecbaa2ca 100644 --- a/hw/riscv/sifive_u_otp.c +++ b/hw/riscv/sifive_u_otp.c @@ -171,7 +171,7 @@ static void sifive_u_otp_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->props = sifive_u_otp_properties; + device_class_set_props(dc, sifive_u_otp_properties); dc->realize = sifive_u_otp_realize; dc->reset = sifive_u_otp_reset; } diff --git a/hw/riscv/sifive_uart.c b/hw/riscv/sifive_uart.c index a403ae90f5..9350482662 100644 --- a/hw/riscv/sifive_uart.c +++ b/hw/riscv/sifive_uart.c @@ -162,7 +162,7 @@ static int uart_can_rx(void *opaque) return s->rx_fifo_len < sizeof(s->rx_fifo); } -static void uart_event(void *opaque, int event) +static void uart_event(void *opaque, QEMUChrEvent event) { } diff --git a/hw/riscv/spike.c b/hw/riscv/spike.c index 8823681783..5053fe4590 100644 --- a/hw/riscv/spike.c +++ b/hw/riscv/spike.c @@ -227,7 +227,8 @@ static void spike_board_init(MachineState *machine) /* Core Local Interruptor (timer and IPI) */ sifive_clint_create(memmap[SPIKE_CLINT].base, memmap[SPIKE_CLINT].size, - smp_cpus, SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE, SIFIVE_TIME_BASE); + smp_cpus, SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE, SIFIVE_TIME_BASE, + false); } static void spike_v1_10_0_board_init(MachineState *machine) @@ -316,7 +317,8 @@ static void spike_v1_10_0_board_init(MachineState *machine) /* Core Local Interruptor (timer and IPI) */ sifive_clint_create(memmap[SPIKE_CLINT].base, memmap[SPIKE_CLINT].size, - smp_cpus, SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE, SIFIVE_TIME_BASE); + smp_cpus, SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE, SIFIVE_TIME_BASE, + false); } static void spike_v1_09_1_board_init(MachineState *machine) @@ -424,7 +426,8 @@ static void spike_v1_09_1_board_init(MachineState *machine) /* Core Local Interruptor (timer and IPI) */ sifive_clint_create(memmap[SPIKE_CLINT].base, memmap[SPIKE_CLINT].size, - smp_cpus, SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE, SIFIVE_TIME_BASE); + smp_cpus, SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE, SIFIVE_TIME_BASE, + false); g_free(config_string); } @@ -448,7 +451,7 @@ static void spike_machine_init(MachineClass *mc) mc->desc = "RISC-V Spike Board"; mc->init = spike_board_init; mc->max_cpus = 1; - mc->is_default = 1; + mc->is_default = true; mc->default_cpu_type = SPIKE_V1_10_0_CPU; } diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index c44b865959..85ec9e22aa 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -58,6 +58,7 @@ static const struct MemmapEntry { [VIRT_DEBUG] = { 0x0, 0x100 }, [VIRT_MROM] = { 0x1000, 0x11000 }, [VIRT_TEST] = { 0x100000, 0x1000 }, + [VIRT_RTC] = { 0x101000, 0x1000 }, [VIRT_CLINT] = { 0x2000000, 0x10000 }, [VIRT_PLIC] = { 0xc000000, 0x4000000 }, [VIRT_UART0] = { 0x10000000, 0x100 }, @@ -182,11 +183,10 @@ static void create_fdt(RISCVVirtState *s, const struct MemmapEntry *memmap, uint64_t mem_size, const char *cmdline) { void *fdt; - int cpu; + int cpu, i; uint32_t *cells; char *nodename; - uint32_t plic_phandle, phandle = 1; - int i; + uint32_t plic_phandle, test_phandle, phandle = 1; hwaddr flashsize = virt_memmap[VIRT_FLASH].size / 2; hwaddr flashbase = virt_memmap[VIRT_FLASH].base; @@ -356,16 +356,35 @@ static void create_fdt(RISCVVirtState *s, const struct MemmapEntry *memmap, create_pcie_irq_map(fdt, nodename, plic_phandle); g_free(nodename); + test_phandle = phandle++; nodename = g_strdup_printf("/test@%lx", (long)memmap[VIRT_TEST].base); qemu_fdt_add_subnode(fdt, nodename); { - const char compat[] = "sifive,test1\0sifive,test0"; + const char compat[] = "sifive,test1\0sifive,test0\0syscon"; qemu_fdt_setprop(fdt, nodename, "compatible", compat, sizeof(compat)); } qemu_fdt_setprop_cells(fdt, nodename, "reg", 0x0, memmap[VIRT_TEST].base, 0x0, memmap[VIRT_TEST].size); + qemu_fdt_setprop_cell(fdt, nodename, "phandle", test_phandle); + test_phandle = qemu_fdt_get_phandle(fdt, nodename); + g_free(nodename); + + nodename = g_strdup_printf("/reboot"); + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_string(fdt, nodename, "compatible", "syscon-reboot"); + qemu_fdt_setprop_cell(fdt, nodename, "regmap", test_phandle); + qemu_fdt_setprop_cell(fdt, nodename, "offset", 0x0); + qemu_fdt_setprop_cell(fdt, nodename, "value", FINISHER_RESET); + g_free(nodename); + + nodename = g_strdup_printf("/poweroff"); + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_string(fdt, nodename, "compatible", "syscon-poweroff"); + qemu_fdt_setprop_cell(fdt, nodename, "regmap", test_phandle); + qemu_fdt_setprop_cell(fdt, nodename, "offset", 0x0); + qemu_fdt_setprop_cell(fdt, nodename, "value", FINISHER_PASS); g_free(nodename); nodename = g_strdup_printf("/uart@%lx", @@ -386,6 +405,18 @@ static void create_fdt(RISCVVirtState *s, const struct MemmapEntry *memmap, } g_free(nodename); + nodename = g_strdup_printf("/rtc@%lx", + (long)memmap[VIRT_RTC].base); + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_string(fdt, nodename, "compatible", + "google,goldfish-rtc"); + qemu_fdt_setprop_cells(fdt, nodename, "reg", + 0x0, memmap[VIRT_RTC].base, + 0x0, memmap[VIRT_RTC].size); + qemu_fdt_setprop_cell(fdt, nodename, "interrupt-parent", plic_phandle); + qemu_fdt_setprop_cell(fdt, nodename, "interrupts", RTC_IRQ); + g_free(nodename); + nodename = g_strdup_printf("/flash@%" PRIx64, flashbase); qemu_fdt_add_subnode(s->fdt, nodename); qemu_fdt_setprop_string(s->fdt, nodename, "compatible", "cfi-flash"); @@ -562,7 +593,7 @@ static void riscv_virt_board_init(MachineState *machine) memmap[VIRT_PLIC].size); sifive_clint_create(memmap[VIRT_CLINT].base, memmap[VIRT_CLINT].size, smp_cpus, - SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE, SIFIVE_TIME_BASE); + SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE, SIFIVE_TIME_BASE, true); sifive_test_create(memmap[VIRT_TEST].base); for (i = 0; i < VIRTIO_COUNT; i++) { @@ -583,6 +614,9 @@ static void riscv_virt_board_init(MachineState *machine) 0, qdev_get_gpio_in(DEVICE(s->plic), UART0_IRQ), 399193, serial_hd(0), DEVICE_LITTLE_ENDIAN); + sysbus_create_simple("goldfish_rtc", memmap[VIRT_RTC].base, + qdev_get_gpio_in(DEVICE(s->plic), RTC_IRQ)); + virt_flash_create(s); for (i = 0; i < ARRAY_SIZE(s->flash); i++) { @@ -607,6 +641,7 @@ static void riscv_virt_machine_class_init(ObjectClass *oc, void *data) mc->init = riscv_virt_board_init; mc->max_cpus = 8; mc->default_cpu_type = VIRT_CPU; + mc->pci_allow_0_address = true; } static const TypeInfo riscv_virt_machine_typeinfo = { diff --git a/hw/rtc/Kconfig b/hw/rtc/Kconfig index 45daa8d655..f06e133b8a 100644 --- a/hw/rtc/Kconfig +++ b/hw/rtc/Kconfig @@ -17,7 +17,11 @@ config TWL92230 depends on I2C config MC146818RTC + depends on ISA_BUS bool config SUN4V_RTC bool + +config GOLDFISH_RTC + bool diff --git a/hw/rtc/Makefile.objs b/hw/rtc/Makefile.objs index 8dc9fcd3a9..e4c1b8617c 100644 --- a/hw/rtc/Makefile.objs +++ b/hw/rtc/Makefile.objs @@ -11,3 +11,5 @@ common-obj-$(CONFIG_EXYNOS4) += exynos4210_rtc.o obj-$(CONFIG_MC146818RTC) += mc146818rtc.o common-obj-$(CONFIG_SUN4V_RTC) += sun4v-rtc.o common-obj-$(CONFIG_ASPEED_SOC) += aspeed_rtc.o +common-obj-$(CONFIG_GOLDFISH_RTC) += goldfish_rtc.o +common-obj-$(CONFIG_ALLWINNER_H3) += allwinner-rtc.o diff --git a/hw/rtc/allwinner-rtc.c b/hw/rtc/allwinner-rtc.c new file mode 100644 index 0000000000..5606a51d5c --- /dev/null +++ b/hw/rtc/allwinner-rtc.c @@ -0,0 +1,411 @@ +/* + * Allwinner Real Time Clock emulation + * + * Copyright (C) 2019 Niek Linnenbank + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "hw/sysbus.h" +#include "migration/vmstate.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "qemu-common.h" +#include "hw/qdev-properties.h" +#include "hw/rtc/allwinner-rtc.h" +#include "trace.h" + +/* RTC registers */ +enum { + REG_LOSC = 1, /* Low Oscillator Control */ + REG_YYMMDD, /* RTC Year-Month-Day */ + REG_HHMMSS, /* RTC Hour-Minute-Second */ + REG_ALARM1_WKHHMMSS, /* Alarm1 Week Hour-Minute-Second */ + REG_ALARM1_EN, /* Alarm1 Enable */ + REG_ALARM1_IRQ_EN, /* Alarm1 IRQ Enable */ + REG_ALARM1_IRQ_STA, /* Alarm1 IRQ Status */ + REG_GP0, /* General Purpose Register 0 */ + REG_GP1, /* General Purpose Register 1 */ + REG_GP2, /* General Purpose Register 2 */ + REG_GP3, /* General Purpose Register 3 */ + + /* sun4i registers */ + REG_ALARM1_DDHHMMSS, /* Alarm1 Day Hour-Minute-Second */ + REG_CPUCFG, /* CPU Configuration Register */ + + /* sun6i registers */ + REG_LOSC_AUTOSTA, /* LOSC Auto Switch Status */ + REG_INT_OSC_PRE, /* Internal OSC Clock Prescaler */ + REG_ALARM0_COUNTER, /* Alarm0 Counter */ + REG_ALARM0_CUR_VLU, /* Alarm0 Counter Current Value */ + REG_ALARM0_ENABLE, /* Alarm0 Enable */ + REG_ALARM0_IRQ_EN, /* Alarm0 IRQ Enable */ + REG_ALARM0_IRQ_STA, /* Alarm0 IRQ Status */ + REG_ALARM_CONFIG, /* Alarm Config */ + REG_LOSC_OUT_GATING, /* LOSC Output Gating Register */ + REG_GP4, /* General Purpose Register 4 */ + REG_GP5, /* General Purpose Register 5 */ + REG_GP6, /* General Purpose Register 6 */ + REG_GP7, /* General Purpose Register 7 */ + REG_RTC_DBG, /* RTC Debug Register */ + REG_GPL_HOLD_OUT, /* GPL Hold Output Register */ + REG_VDD_RTC, /* VDD RTC Regulate Register */ + REG_IC_CHARA, /* IC Characteristics Register */ +}; + +/* RTC register flags */ +enum { + REG_LOSC_YMD = (1 << 7), + REG_LOSC_HMS = (1 << 8), +}; + +/* RTC sun4i register map (offset to name) */ +const uint8_t allwinner_rtc_sun4i_regmap[] = { + [0x0000] = REG_LOSC, + [0x0004] = REG_YYMMDD, + [0x0008] = REG_HHMMSS, + [0x000C] = REG_ALARM1_DDHHMMSS, + [0x0010] = REG_ALARM1_WKHHMMSS, + [0x0014] = REG_ALARM1_EN, + [0x0018] = REG_ALARM1_IRQ_EN, + [0x001C] = REG_ALARM1_IRQ_STA, + [0x0020] = REG_GP0, + [0x0024] = REG_GP1, + [0x0028] = REG_GP2, + [0x002C] = REG_GP3, + [0x003C] = REG_CPUCFG, +}; + +/* RTC sun6i register map (offset to name) */ +const uint8_t allwinner_rtc_sun6i_regmap[] = { + [0x0000] = REG_LOSC, + [0x0004] = REG_LOSC_AUTOSTA, + [0x0008] = REG_INT_OSC_PRE, + [0x0010] = REG_YYMMDD, + [0x0014] = REG_HHMMSS, + [0x0020] = REG_ALARM0_COUNTER, + [0x0024] = REG_ALARM0_CUR_VLU, + [0x0028] = REG_ALARM0_ENABLE, + [0x002C] = REG_ALARM0_IRQ_EN, + [0x0030] = REG_ALARM0_IRQ_STA, + [0x0040] = REG_ALARM1_WKHHMMSS, + [0x0044] = REG_ALARM1_EN, + [0x0048] = REG_ALARM1_IRQ_EN, + [0x004C] = REG_ALARM1_IRQ_STA, + [0x0050] = REG_ALARM_CONFIG, + [0x0060] = REG_LOSC_OUT_GATING, + [0x0100] = REG_GP0, + [0x0104] = REG_GP1, + [0x0108] = REG_GP2, + [0x010C] = REG_GP3, + [0x0110] = REG_GP4, + [0x0114] = REG_GP5, + [0x0118] = REG_GP6, + [0x011C] = REG_GP7, + [0x0170] = REG_RTC_DBG, + [0x0180] = REG_GPL_HOLD_OUT, + [0x0190] = REG_VDD_RTC, + [0x01F0] = REG_IC_CHARA, +}; + +static bool allwinner_rtc_sun4i_read(AwRtcState *s, uint32_t offset) +{ + /* no sun4i specific registers currently implemented */ + return false; +} + +static bool allwinner_rtc_sun4i_write(AwRtcState *s, uint32_t offset, + uint32_t data) +{ + /* no sun4i specific registers currently implemented */ + return false; +} + +static bool allwinner_rtc_sun6i_read(AwRtcState *s, uint32_t offset) +{ + const AwRtcClass *c = AW_RTC_GET_CLASS(s); + + switch (c->regmap[offset]) { + case REG_GP4: /* General Purpose Register 4 */ + case REG_GP5: /* General Purpose Register 5 */ + case REG_GP6: /* General Purpose Register 6 */ + case REG_GP7: /* General Purpose Register 7 */ + return true; + default: + break; + } + return false; +} + +static bool allwinner_rtc_sun6i_write(AwRtcState *s, uint32_t offset, + uint32_t data) +{ + const AwRtcClass *c = AW_RTC_GET_CLASS(s); + + switch (c->regmap[offset]) { + case REG_GP4: /* General Purpose Register 4 */ + case REG_GP5: /* General Purpose Register 5 */ + case REG_GP6: /* General Purpose Register 6 */ + case REG_GP7: /* General Purpose Register 7 */ + return true; + default: + break; + } + return false; +} + +static uint64_t allwinner_rtc_read(void *opaque, hwaddr offset, + unsigned size) +{ + AwRtcState *s = AW_RTC(opaque); + const AwRtcClass *c = AW_RTC_GET_CLASS(s); + uint64_t val = 0; + + if (offset >= c->regmap_size) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n", + __func__, (uint32_t)offset); + return 0; + } + + if (!c->regmap[offset]) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid register 0x%04x\n", + __func__, (uint32_t)offset); + return 0; + } + + switch (c->regmap[offset]) { + case REG_LOSC: /* Low Oscillator Control */ + val = s->regs[REG_LOSC]; + s->regs[REG_LOSC] &= ~(REG_LOSC_YMD | REG_LOSC_HMS); + break; + case REG_YYMMDD: /* RTC Year-Month-Day */ + case REG_HHMMSS: /* RTC Hour-Minute-Second */ + case REG_GP0: /* General Purpose Register 0 */ + case REG_GP1: /* General Purpose Register 1 */ + case REG_GP2: /* General Purpose Register 2 */ + case REG_GP3: /* General Purpose Register 3 */ + val = s->regs[c->regmap[offset]]; + break; + default: + if (!c->read(s, offset)) { + qemu_log_mask(LOG_UNIMP, "%s: unimplemented register 0x%04x\n", + __func__, (uint32_t)offset); + } + val = s->regs[c->regmap[offset]]; + break; + } + + trace_allwinner_rtc_read(offset, val); + return val; +} + +static void allwinner_rtc_write(void *opaque, hwaddr offset, + uint64_t val, unsigned size) +{ + AwRtcState *s = AW_RTC(opaque); + const AwRtcClass *c = AW_RTC_GET_CLASS(s); + + if (offset >= c->regmap_size) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n", + __func__, (uint32_t)offset); + return; + } + + if (!c->regmap[offset]) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid register 0x%04x\n", + __func__, (uint32_t)offset); + return; + } + + trace_allwinner_rtc_write(offset, val); + + switch (c->regmap[offset]) { + case REG_YYMMDD: /* RTC Year-Month-Day */ + s->regs[REG_YYMMDD] = val; + s->regs[REG_LOSC] |= REG_LOSC_YMD; + break; + case REG_HHMMSS: /* RTC Hour-Minute-Second */ + s->regs[REG_HHMMSS] = val; + s->regs[REG_LOSC] |= REG_LOSC_HMS; + break; + case REG_GP0: /* General Purpose Register 0 */ + case REG_GP1: /* General Purpose Register 1 */ + case REG_GP2: /* General Purpose Register 2 */ + case REG_GP3: /* General Purpose Register 3 */ + s->regs[c->regmap[offset]] = val; + break; + default: + if (!c->write(s, offset, val)) { + qemu_log_mask(LOG_UNIMP, "%s: unimplemented register 0x%04x\n", + __func__, (uint32_t)offset); + } + break; + } +} + +static const MemoryRegionOps allwinner_rtc_ops = { + .read = allwinner_rtc_read, + .write = allwinner_rtc_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, + .impl.min_access_size = 4, +}; + +static void allwinner_rtc_reset(DeviceState *dev) +{ + AwRtcState *s = AW_RTC(dev); + struct tm now; + + /* Clear registers */ + memset(s->regs, 0, sizeof(s->regs)); + + /* Get current datetime */ + qemu_get_timedate(&now, 0); + + /* Set RTC with current datetime */ + if (s->base_year > 1900) { + s->regs[REG_YYMMDD] = ((now.tm_year + 1900 - s->base_year) << 16) | + ((now.tm_mon + 1) << 8) | + now.tm_mday; + s->regs[REG_HHMMSS] = (((now.tm_wday + 6) % 7) << 29) | + (now.tm_hour << 16) | + (now.tm_min << 8) | + now.tm_sec; + } +} + +static void allwinner_rtc_init(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + AwRtcState *s = AW_RTC(obj); + + /* Memory mapping */ + memory_region_init_io(&s->iomem, OBJECT(s), &allwinner_rtc_ops, s, + TYPE_AW_RTC, 1 * KiB); + sysbus_init_mmio(sbd, &s->iomem); +} + +static const VMStateDescription allwinner_rtc_vmstate = { + .name = "allwinner-rtc", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, AwRtcState, AW_RTC_REGS_NUM), + VMSTATE_END_OF_LIST() + } +}; + +static Property allwinner_rtc_properties[] = { + DEFINE_PROP_INT32("base-year", AwRtcState, base_year, 0), + DEFINE_PROP_END_OF_LIST(), +}; + +static void allwinner_rtc_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = allwinner_rtc_reset; + dc->vmsd = &allwinner_rtc_vmstate; + device_class_set_props(dc, allwinner_rtc_properties); +} + +static void allwinner_rtc_sun4i_init(Object *obj) +{ + AwRtcState *s = AW_RTC(obj); + s->base_year = 2010; +} + +static void allwinner_rtc_sun4i_class_init(ObjectClass *klass, void *data) +{ + AwRtcClass *arc = AW_RTC_CLASS(klass); + + arc->regmap = allwinner_rtc_sun4i_regmap; + arc->regmap_size = sizeof(allwinner_rtc_sun4i_regmap); + arc->read = allwinner_rtc_sun4i_read; + arc->write = allwinner_rtc_sun4i_write; +} + +static void allwinner_rtc_sun6i_init(Object *obj) +{ + AwRtcState *s = AW_RTC(obj); + s->base_year = 1970; +} + +static void allwinner_rtc_sun6i_class_init(ObjectClass *klass, void *data) +{ + AwRtcClass *arc = AW_RTC_CLASS(klass); + + arc->regmap = allwinner_rtc_sun6i_regmap; + arc->regmap_size = sizeof(allwinner_rtc_sun6i_regmap); + arc->read = allwinner_rtc_sun6i_read; + arc->write = allwinner_rtc_sun6i_write; +} + +static void allwinner_rtc_sun7i_init(Object *obj) +{ + AwRtcState *s = AW_RTC(obj); + s->base_year = 1970; +} + +static void allwinner_rtc_sun7i_class_init(ObjectClass *klass, void *data) +{ + AwRtcClass *arc = AW_RTC_CLASS(klass); + allwinner_rtc_sun4i_class_init(klass, arc); +} + +static const TypeInfo allwinner_rtc_info = { + .name = TYPE_AW_RTC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_init = allwinner_rtc_init, + .instance_size = sizeof(AwRtcState), + .class_init = allwinner_rtc_class_init, + .class_size = sizeof(AwRtcClass), + .abstract = true, +}; + +static const TypeInfo allwinner_rtc_sun4i_info = { + .name = TYPE_AW_RTC_SUN4I, + .parent = TYPE_AW_RTC, + .class_init = allwinner_rtc_sun4i_class_init, + .instance_init = allwinner_rtc_sun4i_init, +}; + +static const TypeInfo allwinner_rtc_sun6i_info = { + .name = TYPE_AW_RTC_SUN6I, + .parent = TYPE_AW_RTC, + .class_init = allwinner_rtc_sun6i_class_init, + .instance_init = allwinner_rtc_sun6i_init, +}; + +static const TypeInfo allwinner_rtc_sun7i_info = { + .name = TYPE_AW_RTC_SUN7I, + .parent = TYPE_AW_RTC, + .class_init = allwinner_rtc_sun7i_class_init, + .instance_init = allwinner_rtc_sun7i_init, +}; + +static void allwinner_rtc_register(void) +{ + type_register_static(&allwinner_rtc_info); + type_register_static(&allwinner_rtc_sun4i_info); + type_register_static(&allwinner_rtc_sun6i_info); + type_register_static(&allwinner_rtc_sun7i_info); +} + +type_init(allwinner_rtc_register) diff --git a/hw/rtc/goldfish_rtc.c b/hw/rtc/goldfish_rtc.c new file mode 100644 index 0000000000..01e9d2b083 --- /dev/null +++ b/hw/rtc/goldfish_rtc.c @@ -0,0 +1,285 @@ +/* + * Goldfish virtual platform RTC + * + * Copyright (C) 2019 Western Digital Corporation or its affiliates. + * + * For more details on Google Goldfish virtual platform refer: + * https://android.googlesource.com/platform/external/qemu/+/refs/heads/emu-2.0-release/docs/GOLDFISH-VIRTUAL-HARDWARE.TXT + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "hw/rtc/goldfish_rtc.h" +#include "migration/vmstate.h" +#include "hw/irq.h" +#include "hw/qdev-properties.h" +#include "hw/sysbus.h" +#include "qemu/bitops.h" +#include "qemu/timer.h" +#include "sysemu/sysemu.h" +#include "qemu/cutils.h" +#include "qemu/log.h" + +#include "trace.h" + +#define RTC_TIME_LOW 0x00 +#define RTC_TIME_HIGH 0x04 +#define RTC_ALARM_LOW 0x08 +#define RTC_ALARM_HIGH 0x0c +#define RTC_IRQ_ENABLED 0x10 +#define RTC_CLEAR_ALARM 0x14 +#define RTC_ALARM_STATUS 0x18 +#define RTC_CLEAR_INTERRUPT 0x1c + +static void goldfish_rtc_update(GoldfishRTCState *s) +{ + qemu_set_irq(s->irq, (s->irq_pending & s->irq_enabled) ? 1 : 0); +} + +static void goldfish_rtc_interrupt(void *opaque) +{ + GoldfishRTCState *s = (GoldfishRTCState *)opaque; + + s->alarm_running = 0; + s->irq_pending = 1; + goldfish_rtc_update(s); +} + +static uint64_t goldfish_rtc_get_count(GoldfishRTCState *s) +{ + return s->tick_offset + (uint64_t)qemu_clock_get_ns(rtc_clock); +} + +static void goldfish_rtc_clear_alarm(GoldfishRTCState *s) +{ + timer_del(s->timer); + s->alarm_running = 0; +} + +static void goldfish_rtc_set_alarm(GoldfishRTCState *s) +{ + uint64_t ticks = goldfish_rtc_get_count(s); + uint64_t event = s->alarm_next; + + if (event <= ticks) { + goldfish_rtc_clear_alarm(s); + goldfish_rtc_interrupt(s); + } else { + /* + * We should be setting timer expiry to: + * qemu_clock_get_ns(rtc_clock) + (event - ticks) + * but this is equivalent to: + * event - s->tick_offset + */ + timer_mod(s->timer, event - s->tick_offset); + s->alarm_running = 1; + } +} + +static uint64_t goldfish_rtc_read(void *opaque, hwaddr offset, + unsigned size) +{ + GoldfishRTCState *s = opaque; + uint64_t r = 0; + + switch (offset) { + case RTC_TIME_LOW: + r = goldfish_rtc_get_count(s) & 0xffffffff; + break; + case RTC_TIME_HIGH: + r = goldfish_rtc_get_count(s) >> 32; + break; + case RTC_ALARM_LOW: + r = s->alarm_next & 0xffffffff; + break; + case RTC_ALARM_HIGH: + r = s->alarm_next >> 32; + break; + case RTC_IRQ_ENABLED: + r = s->irq_enabled; + break; + case RTC_ALARM_STATUS: + r = s->alarm_running; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: offset 0x%x is UNIMP.\n", __func__, (uint32_t)offset); + break; + } + + trace_goldfish_rtc_read(offset, r); + + return r; +} + +static void goldfish_rtc_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + GoldfishRTCState *s = opaque; + uint64_t current_tick, new_tick; + + switch (offset) { + case RTC_TIME_LOW: + current_tick = goldfish_rtc_get_count(s); + new_tick = deposit64(current_tick, 0, 32, value); + s->tick_offset += new_tick - current_tick; + break; + case RTC_TIME_HIGH: + current_tick = goldfish_rtc_get_count(s); + new_tick = deposit64(current_tick, 32, 32, value); + s->tick_offset += new_tick - current_tick; + break; + case RTC_ALARM_LOW: + s->alarm_next = deposit64(s->alarm_next, 0, 32, value); + goldfish_rtc_set_alarm(s); + break; + case RTC_ALARM_HIGH: + s->alarm_next = deposit64(s->alarm_next, 32, 32, value); + break; + case RTC_IRQ_ENABLED: + s->irq_enabled = (uint32_t)(value & 0x1); + goldfish_rtc_update(s); + break; + case RTC_CLEAR_ALARM: + goldfish_rtc_clear_alarm(s); + break; + case RTC_CLEAR_INTERRUPT: + s->irq_pending = 0; + goldfish_rtc_update(s); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: offset 0x%x is UNIMP.\n", __func__, (uint32_t)offset); + break; + } + + trace_goldfish_rtc_write(offset, value); +} + +static int goldfish_rtc_pre_save(void *opaque) +{ + uint64_t delta; + GoldfishRTCState *s = opaque; + + /* + * We want to migrate this offset, which sounds straightforward. + * Unfortunately, we cannot directly pass tick_offset because + * rtc_clock on destination Host might not be same source Host. + * + * To tackle, this we pass tick_offset relative to vm_clock from + * source Host and make it relative to rtc_clock at destination Host. + */ + delta = qemu_clock_get_ns(rtc_clock) - + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + s->tick_offset_vmstate = s->tick_offset + delta; + + return 0; +} + +static int goldfish_rtc_post_load(void *opaque, int version_id) +{ + uint64_t delta; + GoldfishRTCState *s = opaque; + + /* + * We extract tick_offset from tick_offset_vmstate by doing + * reverse math compared to pre_save() function. + */ + delta = qemu_clock_get_ns(rtc_clock) - + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + s->tick_offset = s->tick_offset_vmstate - delta; + + return 0; +} + +static const MemoryRegionOps goldfish_rtc_ops = { + .read = goldfish_rtc_read, + .write = goldfish_rtc_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4 + } +}; + +static const VMStateDescription goldfish_rtc_vmstate = { + .name = TYPE_GOLDFISH_RTC, + .version_id = 1, + .pre_save = goldfish_rtc_pre_save, + .post_load = goldfish_rtc_post_load, + .fields = (VMStateField[]) { + VMSTATE_UINT64(tick_offset_vmstate, GoldfishRTCState), + VMSTATE_UINT64(alarm_next, GoldfishRTCState), + VMSTATE_UINT32(alarm_running, GoldfishRTCState), + VMSTATE_UINT32(irq_pending, GoldfishRTCState), + VMSTATE_UINT32(irq_enabled, GoldfishRTCState), + VMSTATE_END_OF_LIST() + } +}; + +static void goldfish_rtc_reset(DeviceState *dev) +{ + GoldfishRTCState *s = GOLDFISH_RTC(dev); + struct tm tm; + + timer_del(s->timer); + + qemu_get_timedate(&tm, 0); + s->tick_offset = mktimegm(&tm); + s->tick_offset *= NANOSECONDS_PER_SECOND; + s->tick_offset -= qemu_clock_get_ns(rtc_clock); + s->tick_offset_vmstate = 0; + s->alarm_next = 0; + s->alarm_running = 0; + s->irq_pending = 0; + s->irq_enabled = 0; +} + +static void goldfish_rtc_realize(DeviceState *d, Error **errp) +{ + SysBusDevice *dev = SYS_BUS_DEVICE(d); + GoldfishRTCState *s = GOLDFISH_RTC(d); + + memory_region_init_io(&s->iomem, OBJECT(s), &goldfish_rtc_ops, s, + "goldfish_rtc", 0x24); + sysbus_init_mmio(dev, &s->iomem); + + sysbus_init_irq(dev, &s->irq); + + s->timer = timer_new_ns(rtc_clock, goldfish_rtc_interrupt, s); +} + +static void goldfish_rtc_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = goldfish_rtc_realize; + dc->reset = goldfish_rtc_reset; + dc->vmsd = &goldfish_rtc_vmstate; +} + +static const TypeInfo goldfish_rtc_info = { + .name = TYPE_GOLDFISH_RTC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(GoldfishRTCState), + .class_init = goldfish_rtc_class_init, +}; + +static void goldfish_rtc_register_types(void) +{ + type_register_static(&goldfish_rtc_info); +} + +type_init(goldfish_rtc_register_types) 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-isa.c b/hw/rtc/m48t59-isa.c index 7fde854c0f..131eb5b7d3 100644 --- a/hw/rtc/m48t59-isa.c +++ b/hw/rtc/m48t59-isa.c @@ -137,7 +137,7 @@ static void m48txx_isa_class_init(ObjectClass *klass, void *data) dc->realize = m48t59_isa_realize; dc->reset = m48t59_reset_isa; - dc->props = m48t59_isa_properties; + device_class_set_props(dc, m48t59_isa_properties); nc->read = m48txx_isa_read; nc->write = m48txx_isa_write; nc->toggle_lock = m48txx_isa_toggle_lock; diff --git a/hw/rtc/m48t59.c b/hw/rtc/m48t59.c index fc592b9fb1..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; } @@ -667,7 +666,7 @@ static void m48txx_sysbus_class_init(ObjectClass *klass, void *data) dc->realize = m48t59_realize; dc->reset = m48t59_reset_sysbus; - dc->props = m48t59_sysbus_properties; + device_class_set_props(dc, m48t59_sysbus_properties); dc->vmsd = &vmstate_m48t59; nc->read = m48txx_sysbus_read; nc->write = m48txx_sysbus_write; diff --git a/hw/rtc/mc146818rtc.c b/hw/rtc/mc146818rtc.c index 857ec3807d..2dffbc675b 100644 --- a/hw/rtc/mc146818rtc.c +++ b/hw/rtc/mc146818rtc.c @@ -1033,7 +1033,7 @@ static void rtc_class_initfn(ObjectClass *klass, void *data) dc->realize = rtc_realizefn; dc->reset = rtc_resetdev; dc->vmsd = &vmstate_rtc; - dc->props = mc146818rtc_properties; + device_class_set_props(dc, mc146818rtc_properties); } static const TypeInfo mc146818rtc_info = { diff --git a/hw/rtc/pl031.c b/hw/rtc/pl031.c index c57cf83165..ae47f09635 100644 --- a/hw/rtc/pl031.c +++ b/hw/rtc/pl031.c @@ -321,7 +321,7 @@ static void pl031_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->vmsd = &vmstate_pl031; - dc->props = pl031_properties; + device_class_set_props(dc, pl031_properties); } static const TypeInfo pl031_info = { diff --git a/hw/rtc/trace-events b/hw/rtc/trace-events index d6749f4616..1bc7147d0e 100644 --- a/hw/rtc/trace-events +++ b/hw/rtc/trace-events @@ -1,5 +1,9 @@ # See docs/devel/tracing.txt for syntax documentation. +# allwinner-rtc.c +allwinner_rtc_read(uint64_t addr, uint64_t value) "addr 0x%" PRIx64 " value 0x%" PRIx64 +allwinner_rtc_write(uint64_t addr, uint64_t value) "addr 0x%" PRIx64 " value 0x%" PRIx64 + # sun4v-rtc.c sun4v_rtc_read(uint64_t addr, uint64_t value) "read: addr 0x%" PRIx64 " value 0x%" PRIx64 sun4v_rtc_write(uint64_t addr, uint64_t value) "write: addr 0x%" PRIx64 " value 0x%" PRIx64 @@ -17,3 +21,13 @@ 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" + +# goldfish_rtc.c +goldfish_rtc_read(uint64_t addr, uint64_t value) "addr 0x%02" PRIx64 " value 0x%08" PRIx64 +goldfish_rtc_write(uint64_t addr, uint64_t value) "addr 0x%02" PRIx64 " value 0x%08" PRIx64 diff --git a/hw/rtc/twl92230.c b/hw/rtc/twl92230.c index 63bd13d2ca..d0011be89e 100644 --- a/hw/rtc/twl92230.c +++ b/hw/rtc/twl92230.c @@ -27,7 +27,6 @@ #include "migration/qemu-file-types.h" #include "migration/vmstate.h" #include "sysemu/sysemu.h" -#include "ui/console.h" #include "qemu/bcd.h" #include "qemu/module.h" diff --git a/hw/s390x/3270-ccw.c b/hw/s390x/3270-ccw.c index c19a75b9b7..821319eee6 100644 --- a/hw/s390x/3270-ccw.c +++ b/hw/s390x/3270-ccw.c @@ -155,7 +155,7 @@ static void emulated_ccw_3270_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->props = emulated_ccw_3270_properties; + device_class_set_props(dc, emulated_ccw_3270_properties); dc->bus_type = TYPE_VIRTUAL_CSS_BUS; dc->realize = emulated_ccw_3270_realize; dc->hotpluggable = false; diff --git a/hw/s390x/ccw-device.c b/hw/s390x/ccw-device.c index 4d222ad202..c9707110e9 100644 --- a/hw/s390x/ccw-device.c +++ b/hw/s390x/ccw-device.c @@ -57,7 +57,7 @@ static void ccw_device_class_init(ObjectClass *klass, void *data) k->realize = ccw_device_realize; k->refill_ids = ccw_device_refill_ids; - dc->props = ccw_device_properties; + device_class_set_props(dc, ccw_device_properties); dc->reset = ccw_device_reset; } diff --git a/hw/s390x/css-bridge.c b/hw/s390x/css-bridge.c index 15a8ed96de..a306a78e6c 100644 --- a/hw/s390x/css-bridge.c +++ b/hw/s390x/css-bridge.c @@ -139,7 +139,7 @@ static void virtual_css_bridge_class_init(ObjectClass *klass, void *data) hc->unplug = ccw_device_unplug; set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); - dc->props = virtual_css_bridge_properties; + device_class_set_props(dc, virtual_css_bridge_properties); object_class_property_add_bool(klass, "cssid-unrestricted", prop_get_true, NULL, NULL); object_class_property_set_description(klass, "cssid-unrestricted", diff --git a/hw/s390x/css.c b/hw/s390x/css.c index 844caab408..5d8e08667e 100644 --- a/hw/s390x/css.c +++ b/hw/s390x/css.c @@ -874,18 +874,18 @@ static inline int ida_read_next_idaw(CcwDataStream *cds) if (idaw_addr & 0x07 || !cds_ccw_addrs_ok(idaw_addr, 0, ccw_fmt1)) { return -EINVAL; /* channel program check */ } - ret = address_space_rw(&address_space_memory, idaw_addr, - MEMTXATTRS_UNSPECIFIED, (void *) &idaw.fmt2, - sizeof(idaw.fmt2), false); + ret = address_space_read(&address_space_memory, idaw_addr, + MEMTXATTRS_UNSPECIFIED, &idaw.fmt2, + sizeof(idaw.fmt2)); cds->cda = be64_to_cpu(idaw.fmt2); } else { idaw_addr = cds->cda_orig + sizeof(idaw.fmt1) * cds->at_idaw; if (idaw_addr & 0x03 || !cds_ccw_addrs_ok(idaw_addr, 0, ccw_fmt1)) { return -EINVAL; /* channel program check */ } - ret = address_space_rw(&address_space_memory, idaw_addr, - MEMTXATTRS_UNSPECIFIED, (void *) &idaw.fmt1, - sizeof(idaw.fmt1), false); + ret = address_space_read(&address_space_memory, idaw_addr, + MEMTXATTRS_UNSPECIFIED, &idaw.fmt1, + sizeof(idaw.fmt1)); cds->cda = be64_to_cpu(idaw.fmt1); if (cds->cda & 0x80000000) { return -EINVAL; /* channel program check */ diff --git a/hw/s390x/event-facility.c b/hw/s390x/event-facility.c index 66205697ae..9d6972afa8 100644 --- a/hw/s390x/event-facility.c +++ b/hw/s390x/event-facility.c @@ -182,11 +182,11 @@ static void write_event_data(SCLPEventFacility *ef, SCCB *sccb) { if (sccb->h.function_code != SCLP_FC_NORMAL_WRITE) { sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_FUNCTION); - goto out; + return; } if (be16_to_cpu(sccb->h.length) < 8) { sccb->h.response_code = cpu_to_be16(SCLP_RC_INSUFFICIENT_SCCB_LENGTH); - goto out; + return; } /* first do a sanity check of the write events */ sccb->h.response_code = cpu_to_be16(write_event_length_check(sccb)); @@ -196,9 +196,6 @@ static void write_event_data(SCLPEventFacility *ef, SCCB *sccb) sccb->h.response_code = cpu_to_be16(handle_sccb_write_events(ef, sccb)); } - -out: - return; } static uint16_t handle_sccb_read_events(SCLPEventFacility *ef, SCCB *sccb, @@ -262,17 +259,18 @@ static void read_event_data(SCLPEventFacility *ef, SCCB *sccb) if (be16_to_cpu(sccb->h.length) != SCCB_SIZE) { sccb->h.response_code = cpu_to_be16(SCLP_RC_INSUFFICIENT_SCCB_LENGTH); - goto out; + return; } - sclp_cp_receive_mask = ef->receive_mask; - - /* get active selection mask */ switch (sccb->h.function_code) { case SCLP_UNCONDITIONAL_READ: - sclp_active_selection_mask = sclp_cp_receive_mask; + sccb->h.response_code = cpu_to_be16( + handle_sccb_read_events(ef, sccb, ef->receive_mask)); break; case SCLP_SELECTIVE_READ: + /* get active selection mask */ + sclp_cp_receive_mask = ef->receive_mask; + copy_mask((uint8_t *)&sclp_active_selection_mask, (uint8_t *)&red->mask, sizeof(sclp_active_selection_mask), ef->mask_length); sclp_active_selection_mask = be64_to_cpu(sclp_active_selection_mask); @@ -280,18 +278,14 @@ static void read_event_data(SCLPEventFacility *ef, SCCB *sccb) (sclp_active_selection_mask & ~sclp_cp_receive_mask)) { sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_SELECTION_MASK); - goto out; + } else { + sccb->h.response_code = cpu_to_be16( + handle_sccb_read_events(ef, sccb, sclp_active_selection_mask)); } break; default: sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_FUNCTION); - goto out; } - sccb->h.response_code = cpu_to_be16( - handle_sccb_read_events(ef, sccb, sclp_active_selection_mask)); - -out: - return; } static void write_event_mask(SCLPEventFacility *ef, SCCB *sccb) @@ -303,7 +297,7 @@ static void write_event_mask(SCLPEventFacility *ef, SCCB *sccb) if (!mask_length || (mask_length > SCLP_EVENT_MASK_LEN_MAX) || ((mask_length != 4) && !ef->allow_all_mask_sizes)) { sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_MASK_LENGTH); - goto out; + return; } /* @@ -328,9 +322,6 @@ static void write_event_mask(SCLPEventFacility *ef, SCCB *sccb) sccb->h.response_code = cpu_to_be16(SCLP_RC_NORMAL_COMPLETION); ef->mask_length = mask_length; - -out: - return; } /* qemu object creation and initialization functions */ @@ -339,14 +330,16 @@ out: static void sclp_events_bus_realize(BusState *bus, Error **errp) { + Error *err = NULL; BusChild *kid; /* TODO: recursive realization has to be done in common code */ QTAILQ_FOREACH(kid, &bus->children, sibling) { DeviceState *dev = kid->child; - object_property_set_bool(OBJECT(dev), true, "realized", errp); - if (*errp) { + object_property_set_bool(OBJECT(dev), true, "realized", &err); + if (err) { + error_propagate(errp, err); return; } } @@ -439,7 +432,7 @@ static void sclp_event_set_allow_all_mask_sizes(Object *obj, bool value, ef->allow_all_mask_sizes = value; } -static bool sclp_event_get_allow_all_mask_sizes(Object *obj, Error **e) +static bool sclp_event_get_allow_all_mask_sizes(Object *obj, Error **errp) { SCLPEventFacility *ef = (SCLPEventFacility *)obj; diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c index ca544d64c5..8c3e019571 100644 --- a/hw/s390x/ipl.c +++ b/hw/s390x/ipl.c @@ -139,7 +139,7 @@ static void s390_ipl_realize(DeviceState *dev, Error **errp) bios_size = load_elf(bios_filename, NULL, bios_translate_addr, &fwbase, - &ipl->bios_start_addr, NULL, NULL, 1, + &ipl->bios_start_addr, NULL, NULL, NULL, 1, EM_S390, 0, 0); if (bios_size > 0) { /* Adjust ELF start address to final location */ @@ -164,7 +164,7 @@ static void s390_ipl_realize(DeviceState *dev, Error **errp) if (ipl->kernel) { kernel_size = load_elf(ipl->kernel, NULL, NULL, NULL, &pentry, NULL, - NULL, 1, EM_S390, 0, 0); + NULL, NULL, 1, EM_S390, 0, 0); if (kernel_size < 0) { kernel_size = load_image_targphys(ipl->kernel, 0, ram_size); if (kernel_size < 0) { @@ -179,7 +179,7 @@ static void s390_ipl_realize(DeviceState *dev, Error **errp) /* if not Linux load the address of the (short) IPL PSW */ ipl_psw = rom_ptr(4, 4); if (ipl_psw) { - pentry = be32_to_cpu(*ipl_psw) & 0x7fffffffUL; + pentry = be32_to_cpu(*ipl_psw) & PSW_MASK_SHORT_ADDR; } else { error_setg(&err, "Could not get IPL PSW"); goto error; @@ -237,7 +237,15 @@ static void s390_ipl_realize(DeviceState *dev, Error **errp) */ ipl->compat_start_addr = ipl->start_addr; ipl->compat_bios_start_addr = ipl->bios_start_addr; - qemu_register_reset(qdev_reset_all_fn, dev); + /* + * Because this Device is not on any bus in the qbus tree (it is + * not a sysbus device and it's not on some other bus like a PCI + * bus) it will not be automatically reset by the 'reset the + * sysbus' hook registered by vl.c like most devices. So we must + * manually register a reset hook for it. + * TODO: there should be a better way to do this. + */ + qemu_register_reset(resettable_cold_reset_fn, dev); error: error_propagate(errp, err); } @@ -473,7 +481,8 @@ static int load_netboot_image(Error **errp) img_size = load_elf_ram(netboot_filename, NULL, NULL, NULL, &ipl->start_addr, - NULL, NULL, 1, EM_S390, 0, 0, NULL, false); + NULL, NULL, NULL, 1, EM_S390, 0, 0, NULL, + false); if (img_size < 0) { img_size = load_image_size(netboot_filename, ram_ptr, ram_size); @@ -529,6 +538,30 @@ static bool is_virtio_scsi_device(IplParameterBlock *iplb) return is_virtio_ccw_device_of_type(iplb, VIRTIO_ID_SCSI); } +static void update_machine_ipl_properties(IplParameterBlock *iplb) +{ + Object *machine = qdev_get_machine(); + Error *err = NULL; + + /* Sync loadparm */ + if (iplb->flags & DIAG308_FLAGS_LP_VALID) { + uint8_t *ebcdic_loadparm = iplb->loadparm; + char ascii_loadparm[9]; + int i; + + for (i = 0; i < 8 && ebcdic_loadparm[i]; i++) { + ascii_loadparm[i] = ebcdic2ascii[(uint8_t) ebcdic_loadparm[i]]; + } + ascii_loadparm[i] = 0; + object_property_set_str(machine, ascii_loadparm, "loadparm", &err); + } else { + object_property_set_str(machine, "", "loadparm", &err); + } + if (err) { + warn_report_err(err); + } +} + void s390_ipl_update_diag308(IplParameterBlock *iplb) { S390IPLState *ipl = get_ipl_device(); @@ -536,6 +569,7 @@ void s390_ipl_update_diag308(IplParameterBlock *iplb) ipl->iplb = *iplb; ipl->iplb_valid = true; ipl->netboot = is_virtio_net_device(iplb); + update_machine_ipl_properties(iplb); } IplParameterBlock *s390_ipl_get_iplb(void) @@ -617,7 +651,7 @@ static void s390_ipl_prepare_qipl(S390CPU *cpu) uint8_t *addr; uint64_t len = 4096; - addr = cpu_physical_memory_map(cpu->env.psa, &len, 1); + addr = cpu_physical_memory_map(cpu->env.psa, &len, true); if (!addr || len < QIPL_ADDRESS + sizeof(QemuIplParameters)) { error_report("Cannot set QEMU IPL parameters"); return; @@ -666,7 +700,7 @@ static void s390_ipl_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = s390_ipl_realize; - dc->props = s390_ipl_properties; + device_class_set_props(dc, s390_ipl_properties); dc->reset = s390_ipl_reset; dc->vmsd = &vmstate_ipl; set_bit(DEVICE_CATEGORY_MISC, dc->categories); diff --git a/hw/s390x/ipl.h b/hw/s390x/ipl.h index d4813105db..3e44abe1c6 100644 --- a/hw/s390x/ipl.h +++ b/hw/s390x/ipl.h @@ -173,16 +173,16 @@ static inline bool iplb_valid_len(IplParameterBlock *iplb) return be32_to_cpu(iplb->len) <= sizeof(IplParameterBlock); } -static inline bool iplb_valid_ccw(IplParameterBlock *iplb) +static inline bool iplb_valid(IplParameterBlock *iplb) { - return be32_to_cpu(iplb->len) >= S390_IPLB_MIN_CCW_LEN && - iplb->pbt == S390_IPL_TYPE_CCW; -} - -static inline bool iplb_valid_fcp(IplParameterBlock *iplb) -{ - return be32_to_cpu(iplb->len) >= S390_IPLB_MIN_FCP_LEN && - iplb->pbt == S390_IPL_TYPE_FCP; + switch (iplb->pbt) { + case S390_IPL_TYPE_FCP: + return be32_to_cpu(iplb->len) >= S390_IPLB_MIN_FCP_LEN; + case S390_IPL_TYPE_CCW: + return be32_to_cpu(iplb->len) >= S390_IPLB_MIN_CCW_LEN; + default: + return false; + } } #endif diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c index 2d2f4a7c41..ed8be124da 100644 --- a/hw/s390x/s390-pci-bus.c +++ b/hw/s390x/s390-pci-bus.c @@ -641,7 +641,7 @@ static uint8_t set_ind_atomic(uint64_t ind_loc, uint8_t to_be_set) hwaddr len = 1; uint8_t *ind_addr; - ind_addr = cpu_physical_memory_map(ind_loc, &len, 1); + ind_addr = cpu_physical_memory_map(ind_loc, &len, true); if (!ind_addr) { s390_pci_generate_error_event(ERR_EVENT_AIRERR, 0, 0, 0, 0); return -1; @@ -1314,7 +1314,7 @@ static void s390_pci_device_class_init(ObjectClass *klass, void *data) dc->reset = s390_pci_device_reset; dc->bus_type = TYPE_S390_PCI_BUS; dc->realize = s390_pci_device_realize; - dc->props = s390_pci_device_properties; + device_class_set_props(dc, s390_pci_device_properties); dc->vmsd = &s390_pci_device_vmstate; } diff --git a/hw/s390x/s390-pci-inst.c b/hw/s390x/s390-pci-inst.c index 92c7e45df5..2f7a7d7bd1 100644 --- a/hw/s390x/s390-pci-inst.c +++ b/hw/s390x/s390-pci-inst.c @@ -243,7 +243,7 @@ int clp_service_call(S390CPU *cpu, uint8_t r2, uintptr_t ra) stw_p(&ressetpci->hdr.rsp, CLP_RC_SETPCIFN_FHOP); goto out; } - device_reset(DEVICE(pbdev)); + device_legacy_reset(DEVICE(pbdev)); pbdev->fh &= ~FH_MASK_ENABLE; pbdev->state = ZPCI_FS_DISABLED; stl_p(&ressetpci->fh, pbdev->fh); diff --git a/hw/s390x/s390-skeys.c b/hw/s390x/s390-skeys.c index bd37f39120..a9a4ae7b39 100644 --- a/hw/s390x/s390-skeys.c +++ b/hw/s390x/s390-skeys.c @@ -176,7 +176,7 @@ static void qemu_s390_skeys_init(Object *obj) QEMUS390SKeysState *skeys = QEMU_S390_SKEYS(obj); MachineState *machine = MACHINE(qdev_get_machine()); - skeys->key_count = machine->maxram_size / TARGET_PAGE_SIZE; + skeys->key_count = machine->ram_size / TARGET_PAGE_SIZE; skeys->keydata = g_malloc0(skeys->key_count); } @@ -392,7 +392,7 @@ static inline void s390_skeys_set_migration_enabled(Object *obj, bool value, register_savevm_live(TYPE_S390_SKEYS, 0, 1, &savevm_s390_storage_keys, ss); } else { - unregister_savevm(DEVICE(ss), TYPE_S390_SKEYS, ss); + unregister_savevm(VMSTATE_IF(ss), TYPE_S390_SKEYS, ss); } } diff --git a/hw/s390x/s390-stattrib-kvm.c b/hw/s390x/s390-stattrib-kvm.c index c7e1f35524..f89d8d9d16 100644 --- a/hw/s390x/s390-stattrib-kvm.c +++ b/hw/s390x/s390-stattrib-kvm.c @@ -85,7 +85,7 @@ static int kvm_s390_stattrib_set_stattr(S390StAttribState *sa, { KVMS390StAttribState *sas = KVM_S390_STATTRIB(sa); MachineState *machine = MACHINE(qdev_get_machine()); - unsigned long max = machine->maxram_size / TARGET_PAGE_SIZE; + unsigned long max = machine->ram_size / TARGET_PAGE_SIZE; if (start_gfn + count > max) { error_report("Out of memory bounds when setting storage attributes"); @@ -104,7 +104,7 @@ static void kvm_s390_stattrib_synchronize(S390StAttribState *sa) { KVMS390StAttribState *sas = KVM_S390_STATTRIB(sa); MachineState *machine = MACHINE(qdev_get_machine()); - unsigned long max = machine->maxram_size / TARGET_PAGE_SIZE; + unsigned long max = machine->ram_size / TARGET_PAGE_SIZE; /* We do not need to reach the maximum buffer size allowed */ unsigned long cx, len = KVM_S390_SKEYS_MAX / 2; int r; diff --git a/hw/s390x/s390-stattrib.c b/hw/s390x/s390-stattrib.c index bf5ac014c4..58121b9f68 100644 --- a/hw/s390x/s390-stattrib.c +++ b/hw/s390x/s390-stattrib.c @@ -352,7 +352,8 @@ static void s390_stattrib_class_init(ObjectClass *oc, void *data) dc->realize = s390_stattrib_realize; } -static inline bool s390_stattrib_get_migration_enabled(Object *obj, Error **e) +static inline bool s390_stattrib_get_migration_enabled(Object *obj, + Error **errp) { S390StAttribState *s = S390_STATTRIB(obj); diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index d3edeef0ad..0fa00a9fff 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -26,6 +26,7 @@ #include "qemu/ctype.h" #include "qemu/error-report.h" #include "qemu/option.h" +#include "qemu/qemu-print.h" #include "s390-pci-bus.h" #include "sysemu/reset.h" #include "hw/s390x/storage-keys.h" @@ -154,14 +155,12 @@ static void virtio_ccw_register_hcalls(void) virtio_ccw_hcall_early_printk); } -static void s390_memory_init(ram_addr_t mem_size) +static void s390_memory_init(MemoryRegion *ram) { MemoryRegion *sysmem = get_system_memory(); - MemoryRegion *ram = g_new(MemoryRegion, 1); Error *local_err = NULL; /* allocate RAM for core */ - memory_region_allocate_system_memory(ram, NULL, "s390.ram", mem_size); memory_region_add_subregion(sysmem, 0, ram); /* @@ -245,7 +244,7 @@ static void ccw_init(MachineState *machine) s390_sclp_init(); /* init memory + setup max page size. Required for the CPU model */ - s390_memory_init(machine->ram_size); + s390_memory_init(machine->ram); /* init CPUs (incl. CPU model) early so s390_has_feature() works */ s390_init_cpus(machine); @@ -348,6 +347,9 @@ static void s390_machine_reset(MachineState *machine) break; case S390_RESET_LOAD_NORMAL: CPU_FOREACH(t) { + if (t == cs) { + continue; + } run_on_cpu(t, s390_do_cpu_reset, RUN_ON_CPU_NULL); } subsystem_reset(); @@ -438,6 +440,26 @@ static void s390_nmi(NMIState *n, int cpu_index, Error **errp) s390_cpu_restart(S390_CPU(cs)); } +static ram_addr_t s390_fixup_ram_size(ram_addr_t sz) +{ + /* same logic as in sclp.c */ + int increment_size = 20; + ram_addr_t newsz; + + while ((sz >> increment_size) > MAX_STORAGE_INCREMENTS) { + increment_size++; + } + newsz = sz >> increment_size << increment_size; + + if (sz != newsz) { + qemu_printf("Ram size %" PRIu64 "MB was fixed up to %" PRIu64 + "MB to match machine restrictions. Consider updating " + "the guest definition.\n", (uint64_t) (sz / MiB), + (uint64_t) (newsz / MiB)); + } + return newsz; +} + static void ccw_machine_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -468,6 +490,7 @@ static void ccw_machine_class_init(ObjectClass *oc, void *data) hc->plug = s390_machine_device_plug; hc->unplug_request = s390_machine_device_unplug_request; nc->nmi_monitor_handler = s390_nmi; + mc->default_ram_id = "s390.ram"; } static inline bool machine_get_aes_key_wrap(Object *obj, Error **errp) @@ -502,6 +525,19 @@ static inline void machine_set_dea_key_wrap(Object *obj, bool value, static S390CcwMachineClass *current_mc; +/* + * Get the class of the s390-ccw-virtio machine that is currently in use. + * Note: libvirt is using the "none" machine to probe for the features of the + * host CPU, so in case this is called with the "none" machine, the function + * returns the TYPE_S390_CCW_MACHINE base class. In this base class, all the + * various "*_allowed" variables are enabled, so that the *_allowed() wrappers + * below return the correct default value for the "none" machine. + * + * Attention! Do *not* add additional new wrappers for CPU features (e.g. like + * the ri_allowed() wrapper) via this mechanism anymore. CPU features should + * be handled via the CPU models, i.e. checking with cpu_model_allowed() during + * CPU initialization and s390_has_feat() later should be sufficient. + */ static S390CcwMachineClass *get_machine_class(void) { if (unlikely(!current_mc)) { @@ -518,19 +554,16 @@ static S390CcwMachineClass *get_machine_class(void) bool ri_allowed(void) { - /* for "none" machine this results in true */ return get_machine_class()->ri_allowed; } bool cpu_model_allowed(void) { - /* for "none" machine this results in true */ return get_machine_class()->cpu_model_allowed; } bool hpage_1m_allowed(void) { - /* for "none" machine this results in true */ return get_machine_class()->hpage_1m_allowed; } @@ -618,7 +651,7 @@ bool css_migration_enabled(void) mc->desc = "VirtIO-ccw based S390 machine v" verstr; \ if (latest) { \ mc->alias = "s390-ccw-virtio"; \ - mc->is_default = 1; \ + mc->is_default = true; \ } \ } \ static void ccw_machine_##suffix##_instance_init(Object *obj) \ @@ -639,14 +672,27 @@ bool css_migration_enabled(void) } \ type_init(ccw_machine_register_##suffix) +static void ccw_machine_5_0_instance_options(MachineState *machine) +{ +} + +static void ccw_machine_5_0_class_options(MachineClass *mc) +{ +} +DEFINE_CCW_MACHINE(5_0, "5.0", true); + static void ccw_machine_4_2_instance_options(MachineState *machine) { + ccw_machine_5_0_instance_options(machine); } static void ccw_machine_4_2_class_options(MachineClass *mc) { + ccw_machine_5_0_class_options(mc); + mc->fixup_ram_size = s390_fixup_ram_size; + compat_props_add(mc->compat_props, hw_compat_4_2, hw_compat_4_2_len); } -DEFINE_CCW_MACHINE(4_2, "4.2", true); +DEFINE_CCW_MACHINE(4_2, "4.2", false); static void ccw_machine_4_1_instance_options(MachineState *machine) { diff --git a/hw/s390x/sclp.c b/hw/s390x/sclp.c index f57ce7b739..f0c35aa57a 100644 --- a/hw/s390x/sclp.c +++ b/hw/s390x/sclp.c @@ -197,24 +197,20 @@ int sclp_service_call(CPUS390XState *env, uint64_t sccb, uint32_t code) { SCLPDevice *sclp = get_sclp_device(); SCLPDeviceClass *sclp_c = SCLP_GET_CLASS(sclp); - int r = 0; SCCB work_sccb; hwaddr sccb_len = sizeof(SCCB); /* first some basic checks on program checks */ if (env->psw.mask & PSW_MASK_PSTATE) { - r = -PGM_PRIVILEGED; - goto out; + return -PGM_PRIVILEGED; } if (cpu_physical_memory_is_io(sccb)) { - r = -PGM_ADDRESSING; - goto out; + return -PGM_ADDRESSING; } if ((sccb & ~0x1fffUL) == 0 || (sccb & ~0x1fffUL) == env->psa || (sccb & ~0x7ffffff8UL) != 0) { - r = -PGM_SPECIFICATION; - goto out; + return -PGM_SPECIFICATION; } /* @@ -226,8 +222,7 @@ int sclp_service_call(CPUS390XState *env, uint64_t sccb, uint32_t code) /* Valid sccb sizes */ if (be16_to_cpu(work_sccb.h.length) < sizeof(SCCBHeader)) { - r = -PGM_SPECIFICATION; - goto out; + return -PGM_SPECIFICATION; } switch (code & SCLP_CMD_CODE_MASK) { @@ -257,8 +252,7 @@ out_write: sclp_c->service_interrupt(sclp, sccb); -out: - return r; + return 0; } static void service_interrupt(SCLPDevice *sclp, uint32_t sccb) @@ -333,27 +327,20 @@ out: static void sclp_memory_init(SCLPDevice *sclp) { MachineState *machine = MACHINE(qdev_get_machine()); + MachineClass *machine_class = MACHINE_GET_CLASS(qdev_get_machine()); ram_addr_t initial_mem = machine->ram_size; int increment_size = 20; /* The storage increment size is a multiple of 1M and is a power of 2. - * The number of storage increments must be MAX_STORAGE_INCREMENTS or fewer. + * For some machine types, the number of storage increments must be + * MAX_STORAGE_INCREMENTS or fewer. * The variable 'increment_size' is an exponent of 2 that can be * used to calculate the size (in bytes) of an increment. */ - while ((initial_mem >> increment_size) > MAX_STORAGE_INCREMENTS) { + while (machine_class->fixup_ram_size != NULL && + (initial_mem >> increment_size) > MAX_STORAGE_INCREMENTS) { increment_size++; } sclp->increment_size = increment_size; - - /* The core memory area needs to be aligned with the increment size. - * In effect, this can cause the user-specified memory size to be rounded - * down to align with the nearest increment boundary. */ - initial_mem = initial_mem >> increment_size << increment_size; - - machine->ram_size = initial_mem; - machine->maxram_size = initial_mem; - /* let's propagate the changed ram size into the global variable. */ - ram_size = initial_mem; } static void sclp_init(Object *obj) diff --git a/hw/s390x/vhost-vsock-ccw.c b/hw/s390x/vhost-vsock-ccw.c index 1835812bd1..12dee15e11 100644 --- a/hw/s390x/vhost-vsock-ccw.c +++ b/hw/s390x/vhost-vsock-ccw.c @@ -35,7 +35,7 @@ static void vhost_vsock_ccw_class_init(ObjectClass *klass, void *data) k->realize = vhost_vsock_ccw_realize; set_bit(DEVICE_CATEGORY_MISC, dc->categories); - dc->props = vhost_vsock_ccw_properties; + device_class_set_props(dc, vhost_vsock_ccw_properties); } static void vhost_vsock_ccw_instance_init(Object *obj) diff --git a/hw/s390x/virtio-ccw-9p.c b/hw/s390x/virtio-ccw-9p.c index 5453a964d2..08e1d5d416 100644 --- a/hw/s390x/virtio-ccw-9p.c +++ b/hw/s390x/virtio-ccw-9p.c @@ -47,7 +47,7 @@ static void virtio_ccw_9p_class_init(ObjectClass *klass, void *data) VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); k->realize = virtio_ccw_9p_realize; - dc->props = virtio_ccw_9p_properties; + device_class_set_props(dc, virtio_ccw_9p_properties); set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); } diff --git a/hw/s390x/virtio-ccw-balloon.c b/hw/s390x/virtio-ccw-balloon.c index 7088612f6b..5d28e72345 100644 --- a/hw/s390x/virtio-ccw-balloon.c +++ b/hw/s390x/virtio-ccw-balloon.c @@ -52,7 +52,7 @@ static void virtio_ccw_balloon_class_init(ObjectClass *klass, void *data) VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); k->realize = virtio_ccw_balloon_realize; - dc->props = virtio_ccw_balloon_properties; + device_class_set_props(dc, virtio_ccw_balloon_properties); set_bit(DEVICE_CATEGORY_MISC, dc->categories); } diff --git a/hw/s390x/virtio-ccw-blk.c b/hw/s390x/virtio-ccw-blk.c index 1512af8974..bf8520e60a 100644 --- a/hw/s390x/virtio-ccw-blk.c +++ b/hw/s390x/virtio-ccw-blk.c @@ -49,7 +49,7 @@ static void virtio_ccw_blk_class_init(ObjectClass *klass, void *data) VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); k->realize = virtio_ccw_blk_realize; - dc->props = virtio_ccw_blk_properties; + device_class_set_props(dc, virtio_ccw_blk_properties); set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); } diff --git a/hw/s390x/virtio-ccw-crypto.c b/hw/s390x/virtio-ccw-crypto.c index 086b397ad2..1a2690cf9e 100644 --- a/hw/s390x/virtio-ccw-crypto.c +++ b/hw/s390x/virtio-ccw-crypto.c @@ -57,7 +57,7 @@ static void virtio_ccw_crypto_class_init(ObjectClass *klass, void *data) VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); k->realize = virtio_ccw_crypto_realize; - dc->props = virtio_ccw_crypto_properties; + device_class_set_props(dc, virtio_ccw_crypto_properties); set_bit(DEVICE_CATEGORY_MISC, dc->categories); } diff --git a/hw/s390x/virtio-ccw-gpu.c b/hw/s390x/virtio-ccw-gpu.c index be46ca7a96..f69e3ff5a0 100644 --- a/hw/s390x/virtio-ccw-gpu.c +++ b/hw/s390x/virtio-ccw-gpu.c @@ -48,7 +48,7 @@ static void virtio_ccw_gpu_class_init(ObjectClass *klass, void *data) VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); k->realize = virtio_ccw_gpu_realize; - dc->props = virtio_ccw_gpu_properties; + device_class_set_props(dc, virtio_ccw_gpu_properties); dc->hotpluggable = false; set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); } diff --git a/hw/s390x/virtio-ccw-input.c b/hw/s390x/virtio-ccw-input.c index 370b776790..b257dfd467 100644 --- a/hw/s390x/virtio-ccw-input.c +++ b/hw/s390x/virtio-ccw-input.c @@ -38,7 +38,7 @@ static void virtio_ccw_input_class_init(ObjectClass *klass, void *data) VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); k->realize = virtio_ccw_input_realize; - dc->props = virtio_ccw_input_properties; + device_class_set_props(dc, virtio_ccw_input_properties); set_bit(DEVICE_CATEGORY_INPUT, dc->categories); } diff --git a/hw/s390x/virtio-ccw-net.c b/hw/s390x/virtio-ccw-net.c index 12c03d73c4..cd02699934 100644 --- a/hw/s390x/virtio-ccw-net.c +++ b/hw/s390x/virtio-ccw-net.c @@ -52,7 +52,7 @@ static void virtio_ccw_net_class_init(ObjectClass *klass, void *data) VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); k->realize = virtio_ccw_net_realize; - dc->props = virtio_ccw_net_properties; + device_class_set_props(dc, virtio_ccw_net_properties); set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); } diff --git a/hw/s390x/virtio-ccw-rng.c b/hw/s390x/virtio-ccw-rng.c index 854254dd50..d575e30cc6 100644 --- a/hw/s390x/virtio-ccw-rng.c +++ b/hw/s390x/virtio-ccw-rng.c @@ -56,7 +56,7 @@ static void virtio_ccw_rng_class_init(ObjectClass *klass, void *data) VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); k->realize = virtio_ccw_rng_realize; - dc->props = virtio_ccw_rng_properties; + device_class_set_props(dc, virtio_ccw_rng_properties); set_bit(DEVICE_CATEGORY_MISC, dc->categories); } diff --git a/hw/s390x/virtio-ccw-scsi.c b/hw/s390x/virtio-ccw-scsi.c index 4662288b5b..3cb3ad669d 100644 --- a/hw/s390x/virtio-ccw-scsi.c +++ b/hw/s390x/virtio-ccw-scsi.c @@ -59,7 +59,7 @@ static void virtio_ccw_scsi_class_init(ObjectClass *klass, void *data) VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); k->realize = virtio_ccw_scsi_realize; - dc->props = virtio_ccw_scsi_properties; + device_class_set_props(dc, virtio_ccw_scsi_properties); set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); } @@ -102,7 +102,7 @@ static void vhost_ccw_scsi_class_init(ObjectClass *klass, void *data) VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); k->realize = vhost_ccw_scsi_realize; - dc->props = vhost_ccw_scsi_properties; + device_class_set_props(dc, vhost_ccw_scsi_properties); set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); } diff --git a/hw/s390x/virtio-ccw-serial.c b/hw/s390x/virtio-ccw-serial.c index eafb7d5c1f..1764db2e70 100644 --- a/hw/s390x/virtio-ccw-serial.c +++ b/hw/s390x/virtio-ccw-serial.c @@ -60,7 +60,7 @@ static void virtio_ccw_serial_class_init(ObjectClass *klass, void *data) VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); k->realize = virtio_ccw_serial_realize; - dc->props = virtio_ccw_serial_properties; + device_class_set_props(dc, virtio_ccw_serial_properties); set_bit(DEVICE_CATEGORY_INPUT, dc->categories); } diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c index 6580ce5907..64f928fc7d 100644 --- a/hw/s390x/virtio-ccw.c +++ b/hw/s390x/virtio-ccw.c @@ -193,7 +193,7 @@ typedef struct VirtioThinintInfo { typedef struct VirtioRevInfo { uint16_t revision; uint16_t length; - uint8_t data[0]; + uint8_t data[]; } QEMU_PACKED VirtioRevInfo; /* Specify where the virtqueues for the subchannel are in guest memory. */ @@ -697,6 +697,7 @@ static void virtio_ccw_device_realize(VirtioCcwDevice *dev, Error **errp) CCWDeviceClass *ck = CCW_DEVICE_GET_CLASS(ccw_dev); SubchDev *sch; Error *err = NULL; + int i; sch = css_create_sch(ccw_dev->devno, errp); if (!sch) { @@ -717,6 +718,9 @@ static void virtio_ccw_device_realize(VirtioCcwDevice *dev, Error **errp) ccw_dev->sch = sch; dev->indicators = NULL; dev->revision = -1; + for (i = 0; i < ADAPTER_ROUTES_MAX_GSI; i++) { + dev->routes.gsi[i] = -1; + } css_sch_build_virtual_schib(sch, 0, VIRTIO_CCW_CHPID_TYPE); trace_virtio_ccw_new_device( @@ -786,7 +790,7 @@ static uint8_t virtio_set_ind_atomic(SubchDev *sch, uint64_t ind_loc, hwaddr len = 1; uint8_t *ind_addr; - ind_addr = cpu_physical_memory_map(ind_loc, &len, 1); + ind_addr = cpu_physical_memory_map(ind_loc, &len, true); if (!ind_addr) { error_report("%s(%x.%x.%04x): unable to access indicator", __func__, sch->cssid, sch->ssid, sch->schid); diff --git a/hw/scsi/Kconfig b/hw/scsi/Kconfig index b3ba540c17..77d397c949 100644 --- a/hw/scsi/Kconfig +++ b/hw/scsi/Kconfig @@ -34,6 +34,7 @@ config ESP_PCI default y if PCI_DEVICES depends on PCI select ESP + select NMC93XX_EEPROM config SPAPR_VSCSI bool diff --git a/hw/scsi/esp.c b/hw/scsi/esp.c index f8fc30cccb..405f8b7cbc 100644 --- a/hw/scsi/esp.c +++ b/hw/scsi/esp.c @@ -293,7 +293,7 @@ static void handle_satn_stop(ESPState *s) s->dma_cb = handle_satn_stop; return; } - s->pdma_cb = satn_stop_pdma_cb;; + s->pdma_cb = satn_stop_pdma_cb; s->cmdlen = get_cmd(s, s->cmdbuf, sizeof(s->cmdbuf)); if (s->cmdlen) { trace_esp_handle_satn_stop(s->cmdlen); diff --git a/hw/scsi/megasas.c b/hw/scsi/megasas.c index de9bd20887..af18c88b65 100644 --- a/hw/scsi/megasas.c +++ b/hw/scsi/megasas.c @@ -2511,7 +2511,7 @@ static void megasas_class_init(ObjectClass *oc, void *data) e->osts = info->osts; e->product_name = info->product_name; e->product_version = info->product_version; - dc->props = info->props; + device_class_set_props(dc, info->props); dc->reset = megasas_scsi_reset; dc->vmsd = info->vmsd; set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); diff --git a/hw/scsi/mptsas.c b/hw/scsi/mptsas.c index b8a4b37cf3..14cbed84d0 100644 --- a/hw/scsi/mptsas.c +++ b/hw/scsi/mptsas.c @@ -1431,7 +1431,7 @@ static void mptsas1068_class_init(ObjectClass *oc, void *data) pc->subsystem_vendor_id = PCI_VENDOR_ID_LSI_LOGIC; pc->subsystem_id = 0x8000; pc->class_id = PCI_CLASS_STORAGE_SCSI; - dc->props = mptsas_properties; + device_class_set_props(dc, mptsas_properties); dc->reset = mptsas_reset; dc->vmsd = &vmstate_mptsas; dc->desc = "LSI SAS 1068"; diff --git a/hw/scsi/scsi-bus.c b/hw/scsi/scsi-bus.c index ad0e7f6d88..1c980cab38 100644 --- a/hw/scsi/scsi-bus.c +++ b/hw/scsi/scsi-bus.c @@ -1728,7 +1728,7 @@ static void scsi_device_class_init(ObjectClass *klass, void *data) k->bus_type = TYPE_SCSI_BUS; k->realize = scsi_qdev_realize; k->unrealize = scsi_qdev_unrealize; - k->props = scsi_props; + device_class_set_props(k, scsi_props); } static void scsi_dev_instance_init(Object *obj) diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c index e44c61eeb4..1c0cb63a6f 100644 --- a/hw/scsi/scsi-disk.c +++ b/hw/scsi/scsi-disk.c @@ -1915,7 +1915,6 @@ static int32_t scsi_disk_emulate_command(SCSIRequest *req, uint8_t *buf) r->iov.iov_base = blk_blockalign(s->qdev.conf.blk, r->buflen); } - buflen = req->cmd.xfer; outbuf = r->iov.iov_base; memset(outbuf, 0, r->buflen); switch (req->cmd.buf[0]) { @@ -3036,7 +3035,7 @@ static void scsi_hd_class_initfn(ObjectClass *klass, void *data) sc->alloc_req = scsi_new_request; sc->unit_attention_reported = scsi_disk_unit_attention_reported; dc->desc = "virtual SCSI disk"; - dc->props = scsi_hd_properties; + device_class_set_props(dc, scsi_hd_properties); dc->vmsd = &vmstate_scsi_disk_state; } @@ -3067,7 +3066,7 @@ static void scsi_cd_class_initfn(ObjectClass *klass, void *data) sc->alloc_req = scsi_new_request; sc->unit_attention_reported = scsi_disk_unit_attention_reported; dc->desc = "virtual SCSI CD-ROM"; - dc->props = scsi_cd_properties; + device_class_set_props(dc, scsi_cd_properties); dc->vmsd = &vmstate_scsi_disk_state; } @@ -3106,7 +3105,7 @@ static void scsi_block_class_initfn(ObjectClass *klass, void *data) sdc->update_sense = scsi_block_update_sense; sdc->need_fua_emulation = scsi_block_no_fua; dc->desc = "SCSI block device passthrough"; - dc->props = scsi_block_properties; + device_class_set_props(dc, scsi_block_properties); dc->vmsd = &vmstate_scsi_disk_state; } @@ -3146,7 +3145,7 @@ static void scsi_disk_class_initfn(ObjectClass *klass, void *data) dc->fw_name = "disk"; dc->desc = "virtual SCSI disk or CD-ROM (legacy)"; dc->reset = scsi_disk_reset; - dc->props = scsi_disk_properties; + device_class_set_props(dc, scsi_disk_properties); dc->vmsd = &vmstate_scsi_disk_state; } diff --git a/hw/scsi/scsi-generic.c b/hw/scsi/scsi-generic.c index e7798ebcd0..86ed0a3822 100644 --- a/hw/scsi/scsi-generic.c +++ b/hw/scsi/scsi-generic.c @@ -768,7 +768,7 @@ static void scsi_generic_class_initfn(ObjectClass *klass, void *data) dc->fw_name = "disk"; dc->desc = "pass through generic scsi device (/dev/sg*)"; dc->reset = scsi_generic_reset; - dc->props = scsi_generic_properties; + device_class_set_props(dc, scsi_generic_properties); dc->vmsd = &vmstate_scsi_device; } diff --git a/hw/scsi/spapr_vscsi.c b/hw/scsi/spapr_vscsi.c index 2120ceb051..923488beb2 100644 --- a/hw/scsi/spapr_vscsi.c +++ b/hw/scsi/spapr_vscsi.c @@ -55,6 +55,8 @@ #define VSCSI_MAX_SECTORS 4096 #define VSCSI_REQ_LIMIT 24 +/* Maximum size of a IU payload */ +#define SRP_MAX_IU_DATA_LEN (SRP_MAX_IU_LEN - sizeof(union srp_iu)) #define SRP_RSP_SENSE_DATA_LEN 18 #define SRP_REPORT_LUNS_WLUN 0xc10100000000000ULL @@ -66,7 +68,7 @@ typedef union vscsi_crq { typedef struct vscsi_req { vscsi_crq crq; - union viosrp_iu iu; + uint8_t viosrp_iu_buf[SRP_MAX_IU_LEN]; /* SCSI request tracking */ SCSIRequest *sreq; @@ -97,6 +99,11 @@ typedef struct { vscsi_req reqs[VSCSI_REQ_LIMIT]; } VSCSIState; +static union viosrp_iu *req_iu(vscsi_req *req) +{ + return (union viosrp_iu *)req->viosrp_iu_buf; +} + static struct vscsi_req *vscsi_get_req(VSCSIState *s) { vscsi_req *req; @@ -121,7 +128,7 @@ static struct vscsi_req *vscsi_find_req(VSCSIState *s, uint64_t srp_tag) for (i = 0; i < VSCSI_REQ_LIMIT; i++) { req = &s->reqs[i]; - if (req->iu.srp.cmd.tag == srp_tag) { + if (req_iu(req)->srp.cmd.tag == srp_tag) { return req; } } @@ -176,9 +183,11 @@ static int vscsi_send_iu(VSCSIState *s, vscsi_req *req, { long rc, rc1; + assert(length <= SRP_MAX_IU_LEN); + /* First copy the SRP */ rc = spapr_vio_dma_write(&s->vdev, req->crq.s.IU_data_ptr, - &req->iu, length); + &req->viosrp_iu_buf, length); if (rc) { fprintf(stderr, "vscsi_send_iu: DMA write failure !\n"); } @@ -188,7 +197,7 @@ static int vscsi_send_iu(VSCSIState *s, vscsi_req *req, req->crq.s.reserved = 0x00; req->crq.s.timeout = cpu_to_be16(0x0000); req->crq.s.IU_length = cpu_to_be16(length); - req->crq.s.IU_data_ptr = req->iu.srp.rsp.tag; /* right byte order */ + req->crq.s.IU_data_ptr = req_iu(req)->srp.rsp.tag; /* right byte order */ if (rc == 0) { req->crq.s.status = VIOSRP_OK; @@ -224,7 +233,7 @@ static void vscsi_makeup_sense(VSCSIState *s, vscsi_req *req, static int vscsi_send_rsp(VSCSIState *s, vscsi_req *req, uint8_t status, int32_t res_in, int32_t res_out) { - union viosrp_iu *iu = &req->iu; + union viosrp_iu *iu = req_iu(req); uint64_t tag = iu->srp.rsp.tag; int total_len = sizeof(iu->srp.rsp); uint8_t sol_not = iu->srp.cmd.sol_not; @@ -261,10 +270,12 @@ static int vscsi_send_rsp(VSCSIState *s, vscsi_req *req, if (status) { iu->srp.rsp.sol_not = (sol_not & 0x04) >> 2; if (req->senselen) { - req->iu.srp.rsp.flags |= SRP_RSP_FLAG_SNSVALID; - req->iu.srp.rsp.sense_data_len = cpu_to_be32(req->senselen); - memcpy(req->iu.srp.rsp.data, req->sense, req->senselen); - total_len += req->senselen; + int sense_data_len = MIN(req->senselen, SRP_MAX_IU_DATA_LEN); + + iu->srp.rsp.flags |= SRP_RSP_FLAG_SNSVALID; + iu->srp.rsp.sense_data_len = cpu_to_be32(sense_data_len); + memcpy(iu->srp.rsp.data, req->sense, sense_data_len); + total_len += sense_data_len; } } else { iu->srp.rsp.sol_not = (sol_not & 0x02) >> 1; @@ -285,7 +296,7 @@ static int vscsi_fetch_desc(VSCSIState *s, struct vscsi_req *req, unsigned n, unsigned buf_offset, struct srp_direct_buf *ret) { - struct srp_cmd *cmd = &req->iu.srp.cmd; + struct srp_cmd *cmd = &req_iu(req)->srp.cmd; switch (req->dma_fmt) { case SRP_NO_DATA_DESC: { @@ -473,7 +484,7 @@ static int data_out_desc_size(struct srp_cmd *cmd) static int vscsi_preprocess_desc(vscsi_req *req) { - struct srp_cmd *cmd = &req->iu.srp.cmd; + struct srp_cmd *cmd = &req_iu(req)->srp.cmd; req->cdb_offset = cmd->add_cdb_len & ~3; @@ -597,7 +608,7 @@ static const VMStateDescription vmstate_spapr_vscsi_req = { .minimum_version_id = 1, .fields = (VMStateField[]) { VMSTATE_BUFFER(crq.raw, vscsi_req), - VMSTATE_BUFFER(iu.srp.reserved, vscsi_req), + VMSTATE_BUFFER(viosrp_iu_buf, vscsi_req), VMSTATE_UINT32(qtag, vscsi_req), VMSTATE_BOOL(active, vscsi_req), VMSTATE_UINT32(data_len, vscsi_req), @@ -655,7 +666,7 @@ static void *vscsi_load_request(QEMUFile *f, SCSIRequest *sreq) static void vscsi_process_login(VSCSIState *s, vscsi_req *req) { - union viosrp_iu *iu = &req->iu; + union viosrp_iu *iu = req_iu(req); struct srp_login_rsp *rsp = &iu->srp.login_rsp; uint64_t tag = iu->srp.rsp.tag; @@ -671,8 +682,8 @@ static void vscsi_process_login(VSCSIState *s, vscsi_req *req) */ rsp->req_lim_delta = cpu_to_be32(VSCSI_REQ_LIMIT-2); rsp->tag = tag; - rsp->max_it_iu_len = cpu_to_be32(sizeof(union srp_iu)); - rsp->max_ti_iu_len = cpu_to_be32(sizeof(union srp_iu)); + rsp->max_it_iu_len = cpu_to_be32(SRP_MAX_IU_LEN); + rsp->max_ti_iu_len = cpu_to_be32(SRP_MAX_IU_LEN); /* direct and indirect */ rsp->buf_fmt = cpu_to_be16(SRP_BUF_FORMAT_DIRECT | SRP_BUF_FORMAT_INDIRECT); @@ -681,7 +692,7 @@ static void vscsi_process_login(VSCSIState *s, vscsi_req *req) static void vscsi_inquiry_no_target(VSCSIState *s, vscsi_req *req) { - uint8_t *cdb = req->iu.srp.cmd.cdb; + uint8_t *cdb = req_iu(req)->srp.cmd.cdb; uint8_t resp_data[36]; int rc, len, alen; @@ -770,7 +781,7 @@ static void vscsi_report_luns(VSCSIState *s, vscsi_req *req) static int vscsi_queue_cmd(VSCSIState *s, vscsi_req *req) { - union srp_iu *srp = &req->iu.srp; + union srp_iu *srp = &req_iu(req)->srp; SCSIDevice *sdev; int n, lun; @@ -821,17 +832,16 @@ static int vscsi_queue_cmd(VSCSIState *s, vscsi_req *req) static int vscsi_process_tsk_mgmt(VSCSIState *s, vscsi_req *req) { - union viosrp_iu *iu = &req->iu; + union viosrp_iu *iu = req_iu(req); vscsi_req *tmpreq; int i, lun = 0, resp = SRP_TSK_MGMT_COMPLETE; SCSIDevice *d; uint64_t tag = iu->srp.rsp.tag; uint8_t sol_not = iu->srp.cmd.sol_not; - fprintf(stderr, "vscsi_process_tsk_mgmt %02x\n", - iu->srp.tsk_mgmt.tsk_mgmt_func); - - d = vscsi_device_find(&s->bus, be64_to_cpu(req->iu.srp.tsk_mgmt.lun), &lun); + trace_spapr_vscsi_process_tsk_mgmt(iu->srp.tsk_mgmt.tsk_mgmt_func); + d = vscsi_device_find(&s->bus, + be64_to_cpu(req_iu(req)->srp.tsk_mgmt.lun), &lun); if (!d) { resp = SRP_TSK_MGMT_FIELDS_INVALID; } else { @@ -842,7 +852,7 @@ static int vscsi_process_tsk_mgmt(VSCSIState *s, vscsi_req *req) break; } - tmpreq = vscsi_find_req(s, req->iu.srp.tsk_mgmt.task_tag); + tmpreq = vscsi_find_req(s, req_iu(req)->srp.tsk_mgmt.task_tag); if (tmpreq && tmpreq->sreq) { assert(tmpreq->sreq->hba_private); scsi_req_cancel(tmpreq->sreq); @@ -867,7 +877,8 @@ static int vscsi_process_tsk_mgmt(VSCSIState *s, vscsi_req *req) for (i = 0; i < VSCSI_REQ_LIMIT; i++) { tmpreq = &s->reqs[i]; - if (tmpreq->iu.srp.cmd.lun != req->iu.srp.tsk_mgmt.lun) { + if (req_iu(tmpreq)->srp.cmd.lun + != req_iu(req)->srp.tsk_mgmt.lun) { continue; } if (!tmpreq->active || !tmpreq->sreq) { @@ -889,6 +900,7 @@ static int vscsi_process_tsk_mgmt(VSCSIState *s, vscsi_req *req) } /* Compose the response here as */ + QEMU_BUILD_BUG_ON(SRP_MAX_IU_DATA_LEN < 4); memset(iu, 0, sizeof(struct srp_rsp) + 4); iu->srp.rsp.opcode = SRP_RSP; iu->srp.rsp.req_lim_delta = cpu_to_be32(1); @@ -911,7 +923,7 @@ static int vscsi_process_tsk_mgmt(VSCSIState *s, vscsi_req *req) static int vscsi_handle_srp_req(VSCSIState *s, vscsi_req *req) { - union srp_iu *srp = &req->iu.srp; + union srp_iu *srp = &req_iu(req)->srp; int done = 1; uint8_t opcode = srp->rsp.opcode; @@ -948,7 +960,7 @@ static int vscsi_send_adapter_info(VSCSIState *s, vscsi_req *req) struct mad_adapter_info_data info; int rc; - sinfo = &req->iu.mad.adapter_info; + sinfo = &req_iu(req)->mad.adapter_info; #if 0 /* What for ? */ rc = spapr_vio_dma_read(&s->vdev, be64_to_cpu(sinfo->buffer), @@ -984,7 +996,7 @@ static int vscsi_send_capabilities(VSCSIState *s, vscsi_req *req) uint64_t buffer; int rc; - vcap = &req->iu.mad.capabilities; + vcap = &req_iu(req)->mad.capabilities; req_len = len = be16_to_cpu(vcap->common.length); buffer = be64_to_cpu(vcap->buffer); if (len > sizeof(cap)) { @@ -1029,7 +1041,7 @@ static int vscsi_send_capabilities(VSCSIState *s, vscsi_req *req) static int vscsi_handle_mad_req(VSCSIState *s, vscsi_req *req) { - union mad_iu *mad = &req->iu.mad; + union mad_iu *mad = &req_iu(req)->mad; bool request_handled = false; uint64_t retlen = 0; @@ -1088,7 +1100,7 @@ static void vscsi_got_payload(VSCSIState *s, vscsi_crq *crq) * in our 256 bytes IUs. If not we'll have to increase the size * of the structure. */ - if (crq->s.IU_length > sizeof(union viosrp_iu)) { + if (crq->s.IU_length > SRP_MAX_IU_LEN) { fprintf(stderr, "VSCSI: SRP IU too long (%d bytes) !\n", crq->s.IU_length); vscsi_put_req(req); @@ -1096,7 +1108,7 @@ static void vscsi_got_payload(VSCSIState *s, vscsi_crq *crq) } /* XXX Handle failure differently ? */ - if (spapr_vio_dma_read(&s->vdev, crq->s.IU_data_ptr, &req->iu, + if (spapr_vio_dma_read(&s->vdev, crq->s.IU_data_ptr, &req->viosrp_iu_buf, crq->s.IU_length)) { fprintf(stderr, "vscsi_got_payload: DMA read failure !\n"); vscsi_put_req(req); @@ -1267,7 +1279,7 @@ static void spapr_vscsi_class_init(ObjectClass *klass, void *data) k->dt_compatible = "IBM,v-scsi"; k->signal_mask = 0x00000001; set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); - dc->props = spapr_vscsi_properties; + device_class_set_props(dc, spapr_vscsi_properties); k->rtce_window_size = 0x10000000; dc->vmsd = &vmstate_spapr_vscsi; } diff --git a/hw/scsi/trace-events b/hw/scsi/trace-events index b0820052f8..9a4a60ca63 100644 --- a/hw/scsi/trace-events +++ b/hw/scsi/trace-events @@ -227,6 +227,7 @@ spapr_vscsi_command_complete_status(uint32_t status) "Command complete err=%"PRI spapr_vscsi_save_request(uint32_t qtag, unsigned desc, unsigned offset) "saving tag=%"PRIu32", current desc#%u, offset=0x%x" spapr_vscsi_load_request(uint32_t qtag, unsigned desc, unsigned offset) "restoring tag=%"PRIu32", current desc#%u, offset=0x%x" spapr_vscsi_process_login(void) "Got login, sending response !" +spapr_vscsi_process_tsk_mgmt(uint8_t func) "tsk_mgmt_func 0x%02x" spapr_vscsi_queue_cmd_no_drive(uint64_t lun) "Command for lun 0x%08" PRIx64 " with no drive" spapr_vscsi_queue_cmd(uint32_t qtag, unsigned cdb, const char *cmd, int lun, int ret) "Queued command tag 0x%"PRIx32" CMD 0x%x=%s LUN %d ret: %d" spapr_vscsi_do_crq(unsigned c0, unsigned c1) "crq: %02x %02x ..." diff --git a/hw/scsi/vhost-scsi.c b/hw/scsi/vhost-scsi.c index c693fc748a..f052377b7e 100644 --- a/hw/scsi/vhost-scsi.c +++ b/hw/scsi/vhost-scsi.c @@ -275,6 +275,8 @@ static Property vhost_scsi_properties[] = { DEFINE_PROP_UINT32("num_queues", VirtIOSCSICommon, conf.num_queues, 1), DEFINE_PROP_UINT32("virtqueue_size", VirtIOSCSICommon, conf.virtqueue_size, 128), + DEFINE_PROP_BOOL("seg_max_adjust", VirtIOSCSICommon, conf.seg_max_adjust, + true), DEFINE_PROP_UINT32("max_sectors", VirtIOSCSICommon, conf.max_sectors, 0xFFFF), DEFINE_PROP_UINT32("cmd_per_lun", VirtIOSCSICommon, conf.cmd_per_lun, 128), @@ -291,7 +293,7 @@ static void vhost_scsi_class_init(ObjectClass *klass, void *data) VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); FWPathProviderClass *fwc = FW_PATH_PROVIDER_CLASS(klass); - dc->props = vhost_scsi_properties; + device_class_set_props(dc, vhost_scsi_properties); dc->vmsd = &vmstate_virtio_vhost_scsi; set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); vdc->realize = vhost_scsi_realize; diff --git a/hw/scsi/vhost-user-scsi.c b/hw/scsi/vhost-user-scsi.c index 6a6c15dd32..a01bf63a08 100644 --- a/hw/scsi/vhost-user-scsi.c +++ b/hw/scsi/vhost-user-scsi.c @@ -39,6 +39,10 @@ static const int user_feature_bits[] = { VHOST_INVALID_FEATURE_BIT }; +enum VhostUserProtocolFeature { + VHOST_USER_PROTOCOL_F_RESET_DEVICE = 13, +}; + static void vhost_user_scsi_set_status(VirtIODevice *vdev, uint8_t status) { VHostUserSCSI *s = (VHostUserSCSI *)vdev; @@ -62,6 +66,25 @@ static void vhost_user_scsi_set_status(VirtIODevice *vdev, uint8_t status) } } +static void vhost_user_scsi_reset(VirtIODevice *vdev) +{ + VHostSCSICommon *vsc = VHOST_SCSI_COMMON(vdev); + struct vhost_dev *dev = &vsc->dev; + + /* + * Historically, reset was not implemented so only reset devices + * that are expecting it. + */ + if (!virtio_has_feature(dev->protocol_features, + VHOST_USER_PROTOCOL_F_RESET_DEVICE)) { + return; + } + + if (dev->vhost_ops->vhost_reset_device) { + dev->vhost_ops->vhost_reset_device(dev); + } +} + static void vhost_dummy_handle_output(VirtIODevice *vdev, VirtQueue *vq) { } @@ -174,7 +197,7 @@ static void vhost_user_scsi_class_init(ObjectClass *klass, void *data) VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); FWPathProviderClass *fwc = FW_PATH_PROVIDER_CLASS(klass); - dc->props = vhost_user_scsi_properties; + device_class_set_props(dc, vhost_user_scsi_properties); dc->vmsd = &vmstate_vhost_scsi; set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); vdc->realize = vhost_user_scsi_realize; @@ -182,6 +205,7 @@ static void vhost_user_scsi_class_init(ObjectClass *klass, void *data) vdc->get_features = vhost_scsi_common_get_features; vdc->set_config = vhost_scsi_common_set_config; vdc->set_status = vhost_user_scsi_set_status; + vdc->reset = vhost_user_scsi_reset; fwc->get_dev_path = vhost_scsi_common_get_fw_dev_path; } diff --git a/hw/scsi/viosrp.h b/hw/scsi/viosrp.h index d8e365db1e..e5f9768e8f 100644 --- a/hw/scsi/viosrp.h +++ b/hw/scsi/viosrp.h @@ -34,6 +34,8 @@ #ifndef PPC_VIOSRP_H #define PPC_VIOSRP_H +#include "hw/scsi/srp.h" + #define SRP_VERSION "16.a" #define SRP_MAX_IU_LEN 256 #define SRP_MAX_LOC_LEN 32 @@ -47,7 +49,6 @@ union srp_iu { struct srp_tsk_mgmt tsk_mgmt; struct srp_cmd cmd; struct srp_rsp rsp; - uint8_t reserved[SRP_MAX_IU_LEN]; }; enum viosrp_crq_formats { diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c index e8b2b64d09..472bbd233b 100644 --- a/hw/scsi/virtio-scsi.c +++ b/hw/scsi/virtio-scsi.c @@ -597,12 +597,15 @@ bool virtio_scsi_handle_cmd_vq(VirtIOSCSI *s, VirtQueue *vq) { VirtIOSCSIReq *req, *next; int ret = 0; + bool suppress_notifications = virtio_queue_get_notification(vq); bool progress = false; QTAILQ_HEAD(, VirtIOSCSIReq) reqs = QTAILQ_HEAD_INITIALIZER(reqs); do { - virtio_queue_set_notification(vq, 0); + if (suppress_notifications) { + virtio_queue_set_notification(vq, 0); + } while ((req = virtio_scsi_pop_req(s, vq))) { progress = true; @@ -622,7 +625,9 @@ bool virtio_scsi_handle_cmd_vq(VirtIOSCSI *s, VirtQueue *vq) } } - virtio_queue_set_notification(vq, 1); + if (suppress_notifications) { + virtio_queue_set_notification(vq, 1); + } } while (ret != -EINVAL && !virtio_queue_empty(vq)); QTAILQ_FOREACH_SAFE(req, &reqs, next, next) { @@ -654,7 +659,8 @@ static void virtio_scsi_get_config(VirtIODevice *vdev, VirtIOSCSICommon *s = VIRTIO_SCSI_COMMON(vdev); virtio_stl_p(vdev, &scsiconf->num_queues, s->conf.num_queues); - virtio_stl_p(vdev, &scsiconf->seg_max, 128 - 2); + virtio_stl_p(vdev, &scsiconf->seg_max, + s->conf.seg_max_adjust ? s->conf.virtqueue_size - 2 : 128 - 2); virtio_stl_p(vdev, &scsiconf->max_sectors, s->conf.max_sectors); virtio_stl_p(vdev, &scsiconf->cmd_per_lun, s->conf.cmd_per_lun); virtio_stl_p(vdev, &scsiconf->event_info_size, sizeof(VirtIOSCSIEvent)); @@ -893,6 +899,11 @@ void virtio_scsi_common_realize(DeviceState *dev, virtio_cleanup(vdev); return; } + if (s->conf.virtqueue_size <= 2) { + error_setg(errp, "invalid virtqueue_size property (= %" PRIu32 "), " + "must be > 2", s->conf.virtqueue_size); + return; + } s->cmd_vqs = g_new0(VirtQueue *, s->conf.num_queues); s->sense_size = VIRTIO_SCSI_SENSE_DEFAULT_SIZE; s->cdb_size = VIRTIO_SCSI_CDB_DEFAULT_SIZE; @@ -932,7 +943,13 @@ void virtio_scsi_common_unrealize(DeviceState *dev) { VirtIODevice *vdev = VIRTIO_DEVICE(dev); VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(dev); + int i; + virtio_delete_queue(vs->ctrl_vq); + virtio_delete_queue(vs->event_vq); + for (i = 0; i < vs->conf.num_queues; i++) { + virtio_delete_queue(vs->cmd_vqs[i]); + } g_free(vs->cmd_vqs); virtio_cleanup(vdev); } @@ -948,7 +965,9 @@ static void virtio_scsi_device_unrealize(DeviceState *dev, Error **errp) static Property virtio_scsi_properties[] = { DEFINE_PROP_UINT32("num_queues", VirtIOSCSI, parent_obj.conf.num_queues, 1), DEFINE_PROP_UINT32("virtqueue_size", VirtIOSCSI, - parent_obj.conf.virtqueue_size, 128), + parent_obj.conf.virtqueue_size, 256), + DEFINE_PROP_BOOL("seg_max_adjust", VirtIOSCSI, + parent_obj.conf.seg_max_adjust, true), DEFINE_PROP_UINT32("max_sectors", VirtIOSCSI, parent_obj.conf.max_sectors, 0xFFFF), DEFINE_PROP_UINT32("cmd_per_lun", VirtIOSCSI, parent_obj.conf.cmd_per_lun, @@ -987,7 +1006,7 @@ static void virtio_scsi_class_init(ObjectClass *klass, void *data) VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass); - dc->props = virtio_scsi_properties; + device_class_set_props(dc, virtio_scsi_properties); dc->vmsd = &vmstate_virtio_scsi; set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); vdc->realize = virtio_scsi_device_realize; diff --git a/hw/scsi/vmw_pvscsi.c b/hw/scsi/vmw_pvscsi.c index 452a3b63b2..ec5bf9ea34 100644 --- a/hw/scsi/vmw_pvscsi.c +++ b/hw/scsi/vmw_pvscsi.c @@ -404,8 +404,7 @@ pvscsi_cmp_ring_put(PVSCSIState *s, struct PVSCSIRingCmpDesc *cmp_desc) cmp_descr_pa = pvscsi_ring_pop_cmp_descr(&s->rings); trace_pvscsi_cmp_ring_put(cmp_descr_pa); - cpu_physical_memory_write(cmp_descr_pa, (void *)cmp_desc, - sizeof(*cmp_desc)); + cpu_physical_memory_write(cmp_descr_pa, cmp_desc, sizeof(*cmp_desc)); } static void @@ -415,8 +414,7 @@ pvscsi_msg_ring_put(PVSCSIState *s, struct PVSCSIRingMsgDesc *msg_desc) msg_descr_pa = pvscsi_ring_pop_msg_descr(&s->rings); trace_pvscsi_msg_ring_put(msg_descr_pa); - cpu_physical_memory_write(msg_descr_pa, (void *)msg_desc, - sizeof(*msg_desc)); + cpu_physical_memory_write(msg_descr_pa, msg_desc, sizeof(*msg_desc)); } static void @@ -491,7 +489,7 @@ pvscsi_get_next_sg_elem(PVSCSISGState *sg) { struct PVSCSISGElement elem; - cpu_physical_memory_read(sg->elemAddr, (void *)&elem, sizeof(elem)); + cpu_physical_memory_read(sg->elemAddr, &elem, sizeof(elem)); if ((elem.flags & ~PVSCSI_KNOWN_FLAGS) != 0) { /* * There is PVSCSI_SGE_FLAG_CHAIN_ELEMENT flag described in @@ -721,7 +719,10 @@ pvscsi_process_io(PVSCSIState *s) PVSCSIRingReqDesc descr; hwaddr next_descr_pa; - assert(s->rings_info_valid); + if (!s->rings_info_valid) { + return; + } + while ((next_descr_pa = pvscsi_ring_pop_req_descr(&s->rings)) != 0) { /* Only read after production index verification */ @@ -838,7 +839,7 @@ pvscsi_on_cmd_reset_device(PVSCSIState *s) if (sdev != NULL) { s->resetting++; - device_reset(&sdev->qdev); + device_legacy_reset(&sdev->qdev); s->resetting--; return PVSCSI_COMMAND_PROCESSING_SUCCEEDED; } @@ -1292,7 +1293,7 @@ static void pvscsi_class_init(ObjectClass *klass, void *data) &pvs_k->parent_dc_realize); dc->reset = pvscsi_reset; dc->vmsd = &vmstate_pvscsi; - dc->props = pvscsi_properties; + device_class_set_props(dc, pvscsi_properties); set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); hc->unplug = pvscsi_hot_unplug; hc->plug = pvscsi_hotplug; diff --git a/hw/sd/Makefile.objs b/hw/sd/Makefile.objs index a884c238df..0d1df1721c 100644 --- a/hw/sd/Makefile.objs +++ b/hw/sd/Makefile.objs @@ -4,8 +4,9 @@ common-obj-$(CONFIG_SD) += sd.o core.o sdmmc-internal.o common-obj-$(CONFIG_SDHCI) += sdhci.o common-obj-$(CONFIG_SDHCI_PCI) += sdhci-pci.o -obj-$(CONFIG_MILKYMIST) += milkymist-memcard.o -obj-$(CONFIG_OMAP) += omap_mmc.o -obj-$(CONFIG_PXA2XX) += pxa2xx_mmci.o -obj-$(CONFIG_RASPI) += bcm2835_sdhost.o -obj-$(CONFIG_ASPEED_SOC) += aspeed_sdhci.o +common-obj-$(CONFIG_ALLWINNER_H3) += allwinner-sdhost.o +common-obj-$(CONFIG_MILKYMIST) += milkymist-memcard.o +common-obj-$(CONFIG_OMAP) += omap_mmc.o +common-obj-$(CONFIG_PXA2XX) += pxa2xx_mmci.o +common-obj-$(CONFIG_RASPI) += bcm2835_sdhost.o +common-obj-$(CONFIG_ASPEED_SOC) += aspeed_sdhci.o diff --git a/hw/sd/allwinner-sdhost.c b/hw/sd/allwinner-sdhost.c new file mode 100644 index 0000000000..f404e1fdb4 --- /dev/null +++ b/hw/sd/allwinner-sdhost.c @@ -0,0 +1,854 @@ +/* + * Allwinner (sun4i and above) SD Host Controller emulation + * + * Copyright (C) 2019 Niek Linnenbank + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "qemu/units.h" +#include "sysemu/blockdev.h" +#include "hw/irq.h" +#include "hw/sd/allwinner-sdhost.h" +#include "migration/vmstate.h" +#include "trace.h" + +#define TYPE_AW_SDHOST_BUS "allwinner-sdhost-bus" +#define AW_SDHOST_BUS(obj) \ + OBJECT_CHECK(SDBus, (obj), TYPE_AW_SDHOST_BUS) + +/* SD Host register offsets */ +enum { + REG_SD_GCTL = 0x00, /* Global Control */ + REG_SD_CKCR = 0x04, /* Clock Control */ + REG_SD_TMOR = 0x08, /* Timeout */ + REG_SD_BWDR = 0x0C, /* Bus Width */ + REG_SD_BKSR = 0x10, /* Block Size */ + REG_SD_BYCR = 0x14, /* Byte Count */ + REG_SD_CMDR = 0x18, /* Command */ + REG_SD_CAGR = 0x1C, /* Command Argument */ + REG_SD_RESP0 = 0x20, /* Response Zero */ + REG_SD_RESP1 = 0x24, /* Response One */ + REG_SD_RESP2 = 0x28, /* Response Two */ + REG_SD_RESP3 = 0x2C, /* Response Three */ + REG_SD_IMKR = 0x30, /* Interrupt Mask */ + REG_SD_MISR = 0x34, /* Masked Interrupt Status */ + REG_SD_RISR = 0x38, /* Raw Interrupt Status */ + REG_SD_STAR = 0x3C, /* Status */ + REG_SD_FWLR = 0x40, /* FIFO Water Level */ + REG_SD_FUNS = 0x44, /* FIFO Function Select */ + REG_SD_DBGC = 0x50, /* Debug Enable */ + REG_SD_A12A = 0x58, /* Auto command 12 argument */ + REG_SD_NTSR = 0x5C, /* SD NewTiming Set */ + REG_SD_SDBG = 0x60, /* SD newTiming Set Debug */ + REG_SD_HWRST = 0x78, /* Hardware Reset Register */ + REG_SD_DMAC = 0x80, /* Internal DMA Controller Control */ + REG_SD_DLBA = 0x84, /* Descriptor List Base Address */ + REG_SD_IDST = 0x88, /* Internal DMA Controller Status */ + REG_SD_IDIE = 0x8C, /* Internal DMA Controller IRQ Enable */ + REG_SD_THLDC = 0x100, /* Card Threshold Control */ + REG_SD_DSBD = 0x10C, /* eMMC DDR Start Bit Detection Control */ + REG_SD_RES_CRC = 0x110, /* Response CRC from card/eMMC */ + REG_SD_DATA7_CRC = 0x114, /* CRC Data 7 from card/eMMC */ + REG_SD_DATA6_CRC = 0x118, /* CRC Data 6 from card/eMMC */ + REG_SD_DATA5_CRC = 0x11C, /* CRC Data 5 from card/eMMC */ + REG_SD_DATA4_CRC = 0x120, /* CRC Data 4 from card/eMMC */ + REG_SD_DATA3_CRC = 0x124, /* CRC Data 3 from card/eMMC */ + REG_SD_DATA2_CRC = 0x128, /* CRC Data 2 from card/eMMC */ + REG_SD_DATA1_CRC = 0x12C, /* CRC Data 1 from card/eMMC */ + REG_SD_DATA0_CRC = 0x130, /* CRC Data 0 from card/eMMC */ + REG_SD_CRC_STA = 0x134, /* CRC status from card/eMMC during write */ + REG_SD_FIFO = 0x200, /* Read/Write FIFO */ +}; + +/* SD Host register flags */ +enum { + SD_GCTL_FIFO_AC_MOD = (1 << 31), + SD_GCTL_DDR_MOD_SEL = (1 << 10), + SD_GCTL_CD_DBC_ENB = (1 << 8), + SD_GCTL_DMA_ENB = (1 << 5), + SD_GCTL_INT_ENB = (1 << 4), + SD_GCTL_DMA_RST = (1 << 2), + SD_GCTL_FIFO_RST = (1 << 1), + SD_GCTL_SOFT_RST = (1 << 0), +}; + +enum { + SD_CMDR_LOAD = (1 << 31), + SD_CMDR_CLKCHANGE = (1 << 21), + SD_CMDR_WRITE = (1 << 10), + SD_CMDR_AUTOSTOP = (1 << 12), + SD_CMDR_DATA = (1 << 9), + SD_CMDR_RESPONSE_LONG = (1 << 7), + SD_CMDR_RESPONSE = (1 << 6), + SD_CMDR_CMDID_MASK = (0x3f), +}; + +enum { + SD_RISR_CARD_REMOVE = (1 << 31), + SD_RISR_CARD_INSERT = (1 << 30), + SD_RISR_SDIO_INTR = (1 << 16), + SD_RISR_AUTOCMD_DONE = (1 << 14), + SD_RISR_DATA_COMPLETE = (1 << 3), + SD_RISR_CMD_COMPLETE = (1 << 2), + SD_RISR_NO_RESPONSE = (1 << 1), +}; + +enum { + SD_STAR_CARD_PRESENT = (1 << 8), +}; + +enum { + SD_IDST_INT_SUMMARY = (1 << 8), + SD_IDST_RECEIVE_IRQ = (1 << 1), + SD_IDST_TRANSMIT_IRQ = (1 << 0), + SD_IDST_IRQ_MASK = (1 << 1) | (1 << 0) | (1 << 8), + SD_IDST_WR_MASK = (0x3ff), +}; + +/* SD Host register reset values */ +enum { + REG_SD_GCTL_RST = 0x00000300, + REG_SD_CKCR_RST = 0x0, + REG_SD_TMOR_RST = 0xFFFFFF40, + REG_SD_BWDR_RST = 0x0, + REG_SD_BKSR_RST = 0x00000200, + REG_SD_BYCR_RST = 0x00000200, + REG_SD_CMDR_RST = 0x0, + REG_SD_CAGR_RST = 0x0, + REG_SD_RESP_RST = 0x0, + REG_SD_IMKR_RST = 0x0, + REG_SD_MISR_RST = 0x0, + REG_SD_RISR_RST = 0x0, + REG_SD_STAR_RST = 0x00000100, + REG_SD_FWLR_RST = 0x000F0000, + REG_SD_FUNS_RST = 0x0, + REG_SD_DBGC_RST = 0x0, + REG_SD_A12A_RST = 0x0000FFFF, + REG_SD_NTSR_RST = 0x00000001, + REG_SD_SDBG_RST = 0x0, + REG_SD_HWRST_RST = 0x00000001, + REG_SD_DMAC_RST = 0x0, + REG_SD_DLBA_RST = 0x0, + REG_SD_IDST_RST = 0x0, + REG_SD_IDIE_RST = 0x0, + REG_SD_THLDC_RST = 0x0, + REG_SD_DSBD_RST = 0x0, + REG_SD_RES_CRC_RST = 0x0, + REG_SD_DATA_CRC_RST = 0x0, + REG_SD_CRC_STA_RST = 0x0, + REG_SD_FIFO_RST = 0x0, +}; + +/* Data transfer descriptor for DMA */ +typedef struct TransferDescriptor { + uint32_t status; /* Status flags */ + uint32_t size; /* Data buffer size */ + uint32_t addr; /* Data buffer address */ + uint32_t next; /* Physical address of next descriptor */ +} TransferDescriptor; + +/* Data transfer descriptor flags */ +enum { + DESC_STATUS_HOLD = (1 << 31), /* Set when descriptor is in use by DMA */ + DESC_STATUS_ERROR = (1 << 30), /* Set when DMA transfer error occurred */ + DESC_STATUS_CHAIN = (1 << 4), /* Indicates chained descriptor. */ + DESC_STATUS_FIRST = (1 << 3), /* Set on the first descriptor */ + DESC_STATUS_LAST = (1 << 2), /* Set on the last descriptor */ + DESC_STATUS_NOIRQ = (1 << 1), /* Skip raising interrupt after transfer */ + DESC_SIZE_MASK = (0xfffffffc) +}; + +static void allwinner_sdhost_update_irq(AwSdHostState *s) +{ + uint32_t irq; + + if (s->global_ctl & SD_GCTL_INT_ENB) { + irq = s->irq_status & s->irq_mask; + } else { + irq = 0; + } + + trace_allwinner_sdhost_update_irq(irq); + qemu_set_irq(s->irq, irq); +} + +static void allwinner_sdhost_update_transfer_cnt(AwSdHostState *s, + uint32_t bytes) +{ + if (s->transfer_cnt > bytes) { + s->transfer_cnt -= bytes; + } else { + s->transfer_cnt = 0; + } + + if (!s->transfer_cnt) { + s->irq_status |= SD_RISR_DATA_COMPLETE; + } +} + +static void allwinner_sdhost_set_inserted(DeviceState *dev, bool inserted) +{ + AwSdHostState *s = AW_SDHOST(dev); + + trace_allwinner_sdhost_set_inserted(inserted); + + if (inserted) { + s->irq_status |= SD_RISR_CARD_INSERT; + s->irq_status &= ~SD_RISR_CARD_REMOVE; + s->status |= SD_STAR_CARD_PRESENT; + } else { + s->irq_status &= ~SD_RISR_CARD_INSERT; + s->irq_status |= SD_RISR_CARD_REMOVE; + s->status &= ~SD_STAR_CARD_PRESENT; + } + + allwinner_sdhost_update_irq(s); +} + +static void allwinner_sdhost_send_command(AwSdHostState *s) +{ + SDRequest request; + uint8_t resp[16]; + int rlen; + + /* Auto clear load flag */ + s->command &= ~SD_CMDR_LOAD; + + /* Clock change does not actually interact with the SD bus */ + if (!(s->command & SD_CMDR_CLKCHANGE)) { + + /* Prepare request */ + request.cmd = s->command & SD_CMDR_CMDID_MASK; + request.arg = s->command_arg; + + /* Send request to SD bus */ + rlen = sdbus_do_command(&s->sdbus, &request, resp); + if (rlen < 0) { + goto error; + } + + /* If the command has a response, store it in the response registers */ + if ((s->command & SD_CMDR_RESPONSE)) { + if (rlen == 4 && !(s->command & SD_CMDR_RESPONSE_LONG)) { + s->response[0] = ldl_be_p(&resp[0]); + s->response[1] = s->response[2] = s->response[3] = 0; + + } else if (rlen == 16 && (s->command & SD_CMDR_RESPONSE_LONG)) { + s->response[0] = ldl_be_p(&resp[12]); + s->response[1] = ldl_be_p(&resp[8]); + s->response[2] = ldl_be_p(&resp[4]); + s->response[3] = ldl_be_p(&resp[0]); + } else { + goto error; + } + } + } + + /* Set interrupt status bits */ + s->irq_status |= SD_RISR_CMD_COMPLETE; + return; + +error: + s->irq_status |= SD_RISR_NO_RESPONSE; +} + +static void allwinner_sdhost_auto_stop(AwSdHostState *s) +{ + /* + * The stop command (CMD12) ensures the SD bus + * returns to the transfer state. + */ + if ((s->command & SD_CMDR_AUTOSTOP) && (s->transfer_cnt == 0)) { + /* First save current command registers */ + uint32_t saved_cmd = s->command; + uint32_t saved_arg = s->command_arg; + + /* Prepare stop command (CMD12) */ + s->command &= ~SD_CMDR_CMDID_MASK; + s->command |= 12; /* CMD12 */ + s->command_arg = 0; + + /* Put the command on SD bus */ + allwinner_sdhost_send_command(s); + + /* Restore command values */ + s->command = saved_cmd; + s->command_arg = saved_arg; + + /* Set IRQ status bit for automatic stop done */ + s->irq_status |= SD_RISR_AUTOCMD_DONE; + } +} + +static uint32_t allwinner_sdhost_process_desc(AwSdHostState *s, + hwaddr desc_addr, + TransferDescriptor *desc, + bool is_write, uint32_t max_bytes) +{ + AwSdHostClass *klass = AW_SDHOST_GET_CLASS(s); + uint32_t num_done = 0; + uint32_t num_bytes = max_bytes; + uint8_t buf[1024]; + + /* Read descriptor */ + cpu_physical_memory_read(desc_addr, desc, sizeof(*desc)); + if (desc->size == 0) { + desc->size = klass->max_desc_size; + } else if (desc->size > klass->max_desc_size) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: DMA descriptor buffer size " + " is out-of-bounds: %" PRIu32 " > %zu", + __func__, desc->size, klass->max_desc_size); + desc->size = klass->max_desc_size; + } + if (desc->size < num_bytes) { + num_bytes = desc->size; + } + + trace_allwinner_sdhost_process_desc(desc_addr, desc->size, + is_write, max_bytes); + + while (num_done < num_bytes) { + /* Try to completely fill the local buffer */ + uint32_t buf_bytes = num_bytes - num_done; + if (buf_bytes > sizeof(buf)) { + buf_bytes = sizeof(buf); + } + + /* Write to SD bus */ + if (is_write) { + cpu_physical_memory_read((desc->addr & DESC_SIZE_MASK) + num_done, + buf, buf_bytes); + + for (uint32_t i = 0; i < buf_bytes; i++) { + sdbus_write_data(&s->sdbus, buf[i]); + } + + /* Read from SD bus */ + } else { + for (uint32_t i = 0; i < buf_bytes; i++) { + buf[i] = sdbus_read_data(&s->sdbus); + } + cpu_physical_memory_write((desc->addr & DESC_SIZE_MASK) + num_done, + buf, buf_bytes); + } + num_done += buf_bytes; + } + + /* Clear hold flag and flush descriptor */ + desc->status &= ~DESC_STATUS_HOLD; + cpu_physical_memory_write(desc_addr, desc, sizeof(*desc)); + + return num_done; +} + +static void allwinner_sdhost_dma(AwSdHostState *s) +{ + TransferDescriptor desc; + hwaddr desc_addr = s->desc_base; + bool is_write = (s->command & SD_CMDR_WRITE); + uint32_t bytes_done = 0; + + /* Check if DMA can be performed */ + if (s->byte_count == 0 || s->block_size == 0 || + !(s->global_ctl & SD_GCTL_DMA_ENB)) { + return; + } + + /* + * For read operations, data must be available on the SD bus + * If not, it is an error and we should not act at all + */ + if (!is_write && !sdbus_data_ready(&s->sdbus)) { + return; + } + + /* Process the DMA descriptors until all data is copied */ + while (s->byte_count > 0) { + bytes_done = allwinner_sdhost_process_desc(s, desc_addr, &desc, + is_write, s->byte_count); + allwinner_sdhost_update_transfer_cnt(s, bytes_done); + + if (bytes_done <= s->byte_count) { + s->byte_count -= bytes_done; + } else { + s->byte_count = 0; + } + + if (desc.status & DESC_STATUS_LAST) { + break; + } else { + desc_addr = desc.next; + } + } + + /* Raise IRQ to signal DMA is completed */ + s->irq_status |= SD_RISR_DATA_COMPLETE | SD_RISR_SDIO_INTR; + + /* Update DMAC bits */ + s->dmac_status |= SD_IDST_INT_SUMMARY; + + if (is_write) { + s->dmac_status |= SD_IDST_TRANSMIT_IRQ; + } else { + s->dmac_status |= SD_IDST_RECEIVE_IRQ; + } +} + +static uint64_t allwinner_sdhost_read(void *opaque, hwaddr offset, + unsigned size) +{ + AwSdHostState *s = AW_SDHOST(opaque); + uint32_t res = 0; + + switch (offset) { + case REG_SD_GCTL: /* Global Control */ + res = s->global_ctl; + break; + case REG_SD_CKCR: /* Clock Control */ + res = s->clock_ctl; + break; + case REG_SD_TMOR: /* Timeout */ + res = s->timeout; + break; + case REG_SD_BWDR: /* Bus Width */ + res = s->bus_width; + break; + case REG_SD_BKSR: /* Block Size */ + res = s->block_size; + break; + case REG_SD_BYCR: /* Byte Count */ + res = s->byte_count; + break; + case REG_SD_CMDR: /* Command */ + res = s->command; + break; + case REG_SD_CAGR: /* Command Argument */ + res = s->command_arg; + break; + case REG_SD_RESP0: /* Response Zero */ + res = s->response[0]; + break; + case REG_SD_RESP1: /* Response One */ + res = s->response[1]; + break; + case REG_SD_RESP2: /* Response Two */ + res = s->response[2]; + break; + case REG_SD_RESP3: /* Response Three */ + res = s->response[3]; + break; + case REG_SD_IMKR: /* Interrupt Mask */ + res = s->irq_mask; + break; + case REG_SD_MISR: /* Masked Interrupt Status */ + res = s->irq_status & s->irq_mask; + break; + case REG_SD_RISR: /* Raw Interrupt Status */ + res = s->irq_status; + break; + case REG_SD_STAR: /* Status */ + res = s->status; + break; + case REG_SD_FWLR: /* FIFO Water Level */ + res = s->fifo_wlevel; + break; + case REG_SD_FUNS: /* FIFO Function Select */ + res = s->fifo_func_sel; + break; + case REG_SD_DBGC: /* Debug Enable */ + res = s->debug_enable; + break; + case REG_SD_A12A: /* Auto command 12 argument */ + res = s->auto12_arg; + break; + case REG_SD_NTSR: /* SD NewTiming Set */ + res = s->newtiming_set; + break; + case REG_SD_SDBG: /* SD newTiming Set Debug */ + res = s->newtiming_debug; + break; + case REG_SD_HWRST: /* Hardware Reset Register */ + res = s->hardware_rst; + break; + case REG_SD_DMAC: /* Internal DMA Controller Control */ + res = s->dmac; + break; + case REG_SD_DLBA: /* Descriptor List Base Address */ + res = s->desc_base; + break; + case REG_SD_IDST: /* Internal DMA Controller Status */ + res = s->dmac_status; + break; + case REG_SD_IDIE: /* Internal DMA Controller Interrupt Enable */ + res = s->dmac_irq; + break; + case REG_SD_THLDC: /* Card Threshold Control */ + res = s->card_threshold; + break; + case REG_SD_DSBD: /* eMMC DDR Start Bit Detection Control */ + res = s->startbit_detect; + break; + case REG_SD_RES_CRC: /* Response CRC from card/eMMC */ + res = s->response_crc; + break; + case REG_SD_DATA7_CRC: /* CRC Data 7 from card/eMMC */ + case REG_SD_DATA6_CRC: /* CRC Data 6 from card/eMMC */ + case REG_SD_DATA5_CRC: /* CRC Data 5 from card/eMMC */ + case REG_SD_DATA4_CRC: /* CRC Data 4 from card/eMMC */ + case REG_SD_DATA3_CRC: /* CRC Data 3 from card/eMMC */ + case REG_SD_DATA2_CRC: /* CRC Data 2 from card/eMMC */ + case REG_SD_DATA1_CRC: /* CRC Data 1 from card/eMMC */ + case REG_SD_DATA0_CRC: /* CRC Data 0 from card/eMMC */ + res = s->data_crc[((offset - REG_SD_DATA7_CRC) / sizeof(uint32_t))]; + break; + case REG_SD_CRC_STA: /* CRC status from card/eMMC in write operation */ + res = s->status_crc; + break; + case REG_SD_FIFO: /* Read/Write FIFO */ + if (sdbus_data_ready(&s->sdbus)) { + res = sdbus_read_data(&s->sdbus); + res |= sdbus_read_data(&s->sdbus) << 8; + res |= sdbus_read_data(&s->sdbus) << 16; + res |= sdbus_read_data(&s->sdbus) << 24; + allwinner_sdhost_update_transfer_cnt(s, sizeof(uint32_t)); + allwinner_sdhost_auto_stop(s); + allwinner_sdhost_update_irq(s); + } else { + qemu_log_mask(LOG_GUEST_ERROR, "%s: no data ready on SD bus\n", + __func__); + } + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset %" + HWADDR_PRIx"\n", __func__, offset); + res = 0; + break; + } + + trace_allwinner_sdhost_read(offset, res, size); + return res; +} + +static void allwinner_sdhost_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + AwSdHostState *s = AW_SDHOST(opaque); + + trace_allwinner_sdhost_write(offset, value, size); + + switch (offset) { + case REG_SD_GCTL: /* Global Control */ + s->global_ctl = value; + s->global_ctl &= ~(SD_GCTL_DMA_RST | SD_GCTL_FIFO_RST | + SD_GCTL_SOFT_RST); + allwinner_sdhost_update_irq(s); + break; + case REG_SD_CKCR: /* Clock Control */ + s->clock_ctl = value; + break; + case REG_SD_TMOR: /* Timeout */ + s->timeout = value; + break; + case REG_SD_BWDR: /* Bus Width */ + s->bus_width = value; + break; + case REG_SD_BKSR: /* Block Size */ + s->block_size = value; + break; + case REG_SD_BYCR: /* Byte Count */ + s->byte_count = value; + s->transfer_cnt = value; + break; + case REG_SD_CMDR: /* Command */ + s->command = value; + if (value & SD_CMDR_LOAD) { + allwinner_sdhost_send_command(s); + allwinner_sdhost_dma(s); + allwinner_sdhost_auto_stop(s); + } + allwinner_sdhost_update_irq(s); + break; + case REG_SD_CAGR: /* Command Argument */ + s->command_arg = value; + break; + case REG_SD_RESP0: /* Response Zero */ + s->response[0] = value; + break; + case REG_SD_RESP1: /* Response One */ + s->response[1] = value; + break; + case REG_SD_RESP2: /* Response Two */ + s->response[2] = value; + break; + case REG_SD_RESP3: /* Response Three */ + s->response[3] = value; + break; + case REG_SD_IMKR: /* Interrupt Mask */ + s->irq_mask = value; + allwinner_sdhost_update_irq(s); + break; + case REG_SD_MISR: /* Masked Interrupt Status */ + case REG_SD_RISR: /* Raw Interrupt Status */ + s->irq_status &= ~value; + allwinner_sdhost_update_irq(s); + break; + case REG_SD_STAR: /* Status */ + s->status &= ~value; + allwinner_sdhost_update_irq(s); + break; + case REG_SD_FWLR: /* FIFO Water Level */ + s->fifo_wlevel = value; + break; + case REG_SD_FUNS: /* FIFO Function Select */ + s->fifo_func_sel = value; + break; + case REG_SD_DBGC: /* Debug Enable */ + s->debug_enable = value; + break; + case REG_SD_A12A: /* Auto command 12 argument */ + s->auto12_arg = value; + break; + case REG_SD_NTSR: /* SD NewTiming Set */ + s->newtiming_set = value; + break; + case REG_SD_SDBG: /* SD newTiming Set Debug */ + s->newtiming_debug = value; + break; + case REG_SD_HWRST: /* Hardware Reset Register */ + s->hardware_rst = value; + break; + case REG_SD_DMAC: /* Internal DMA Controller Control */ + s->dmac = value; + allwinner_sdhost_update_irq(s); + break; + case REG_SD_DLBA: /* Descriptor List Base Address */ + s->desc_base = value; + break; + case REG_SD_IDST: /* Internal DMA Controller Status */ + s->dmac_status &= (~SD_IDST_WR_MASK) | (~value & SD_IDST_WR_MASK); + allwinner_sdhost_update_irq(s); + break; + case REG_SD_IDIE: /* Internal DMA Controller Interrupt Enable */ + s->dmac_irq = value; + allwinner_sdhost_update_irq(s); + break; + case REG_SD_THLDC: /* Card Threshold Control */ + s->card_threshold = value; + break; + case REG_SD_DSBD: /* eMMC DDR Start Bit Detection Control */ + s->startbit_detect = value; + break; + case REG_SD_FIFO: /* Read/Write FIFO */ + sdbus_write_data(&s->sdbus, value & 0xff); + sdbus_write_data(&s->sdbus, (value >> 8) & 0xff); + sdbus_write_data(&s->sdbus, (value >> 16) & 0xff); + sdbus_write_data(&s->sdbus, (value >> 24) & 0xff); + allwinner_sdhost_update_transfer_cnt(s, sizeof(uint32_t)); + allwinner_sdhost_auto_stop(s); + allwinner_sdhost_update_irq(s); + break; + case REG_SD_RES_CRC: /* Response CRC from card/eMMC */ + case REG_SD_DATA7_CRC: /* CRC Data 7 from card/eMMC */ + case REG_SD_DATA6_CRC: /* CRC Data 6 from card/eMMC */ + case REG_SD_DATA5_CRC: /* CRC Data 5 from card/eMMC */ + case REG_SD_DATA4_CRC: /* CRC Data 4 from card/eMMC */ + case REG_SD_DATA3_CRC: /* CRC Data 3 from card/eMMC */ + case REG_SD_DATA2_CRC: /* CRC Data 2 from card/eMMC */ + case REG_SD_DATA1_CRC: /* CRC Data 1 from card/eMMC */ + case REG_SD_DATA0_CRC: /* CRC Data 0 from card/eMMC */ + case REG_SD_CRC_STA: /* CRC status from card/eMMC in write operation */ + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset %" + HWADDR_PRIx"\n", __func__, offset); + break; + } +} + +static const MemoryRegionOps allwinner_sdhost_ops = { + .read = allwinner_sdhost_read, + .write = allwinner_sdhost_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, + .impl.min_access_size = 4, +}; + +static const VMStateDescription vmstate_allwinner_sdhost = { + .name = "allwinner-sdhost", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(global_ctl, AwSdHostState), + VMSTATE_UINT32(clock_ctl, AwSdHostState), + VMSTATE_UINT32(timeout, AwSdHostState), + VMSTATE_UINT32(bus_width, AwSdHostState), + VMSTATE_UINT32(block_size, AwSdHostState), + VMSTATE_UINT32(byte_count, AwSdHostState), + VMSTATE_UINT32(transfer_cnt, AwSdHostState), + VMSTATE_UINT32(command, AwSdHostState), + VMSTATE_UINT32(command_arg, AwSdHostState), + VMSTATE_UINT32_ARRAY(response, AwSdHostState, 4), + VMSTATE_UINT32(irq_mask, AwSdHostState), + VMSTATE_UINT32(irq_status, AwSdHostState), + VMSTATE_UINT32(status, AwSdHostState), + VMSTATE_UINT32(fifo_wlevel, AwSdHostState), + VMSTATE_UINT32(fifo_func_sel, AwSdHostState), + VMSTATE_UINT32(debug_enable, AwSdHostState), + VMSTATE_UINT32(auto12_arg, AwSdHostState), + VMSTATE_UINT32(newtiming_set, AwSdHostState), + VMSTATE_UINT32(newtiming_debug, AwSdHostState), + VMSTATE_UINT32(hardware_rst, AwSdHostState), + VMSTATE_UINT32(dmac, AwSdHostState), + VMSTATE_UINT32(desc_base, AwSdHostState), + VMSTATE_UINT32(dmac_status, AwSdHostState), + VMSTATE_UINT32(dmac_irq, AwSdHostState), + VMSTATE_UINT32(card_threshold, AwSdHostState), + VMSTATE_UINT32(startbit_detect, AwSdHostState), + VMSTATE_UINT32(response_crc, AwSdHostState), + VMSTATE_UINT32_ARRAY(data_crc, AwSdHostState, 8), + VMSTATE_UINT32(status_crc, AwSdHostState), + VMSTATE_END_OF_LIST() + } +}; + +static void allwinner_sdhost_init(Object *obj) +{ + AwSdHostState *s = AW_SDHOST(obj); + + qbus_create_inplace(&s->sdbus, sizeof(s->sdbus), + TYPE_AW_SDHOST_BUS, DEVICE(s), "sd-bus"); + + memory_region_init_io(&s->iomem, obj, &allwinner_sdhost_ops, s, + TYPE_AW_SDHOST, 4 * KiB); + sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem); + sysbus_init_irq(SYS_BUS_DEVICE(s), &s->irq); +} + +static void allwinner_sdhost_reset(DeviceState *dev) +{ + AwSdHostState *s = AW_SDHOST(dev); + + s->global_ctl = REG_SD_GCTL_RST; + s->clock_ctl = REG_SD_CKCR_RST; + s->timeout = REG_SD_TMOR_RST; + s->bus_width = REG_SD_BWDR_RST; + s->block_size = REG_SD_BKSR_RST; + s->byte_count = REG_SD_BYCR_RST; + s->transfer_cnt = 0; + + s->command = REG_SD_CMDR_RST; + s->command_arg = REG_SD_CAGR_RST; + + for (int i = 0; i < ARRAY_SIZE(s->response); i++) { + s->response[i] = REG_SD_RESP_RST; + } + + s->irq_mask = REG_SD_IMKR_RST; + s->irq_status = REG_SD_RISR_RST; + s->status = REG_SD_STAR_RST; + + s->fifo_wlevel = REG_SD_FWLR_RST; + s->fifo_func_sel = REG_SD_FUNS_RST; + s->debug_enable = REG_SD_DBGC_RST; + s->auto12_arg = REG_SD_A12A_RST; + s->newtiming_set = REG_SD_NTSR_RST; + s->newtiming_debug = REG_SD_SDBG_RST; + s->hardware_rst = REG_SD_HWRST_RST; + s->dmac = REG_SD_DMAC_RST; + s->desc_base = REG_SD_DLBA_RST; + s->dmac_status = REG_SD_IDST_RST; + s->dmac_irq = REG_SD_IDIE_RST; + s->card_threshold = REG_SD_THLDC_RST; + s->startbit_detect = REG_SD_DSBD_RST; + s->response_crc = REG_SD_RES_CRC_RST; + + for (int i = 0; i < ARRAY_SIZE(s->data_crc); i++) { + s->data_crc[i] = REG_SD_DATA_CRC_RST; + } + + s->status_crc = REG_SD_CRC_STA_RST; +} + +static void allwinner_sdhost_bus_class_init(ObjectClass *klass, void *data) +{ + SDBusClass *sbc = SD_BUS_CLASS(klass); + + sbc->set_inserted = allwinner_sdhost_set_inserted; +} + +static void allwinner_sdhost_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = allwinner_sdhost_reset; + dc->vmsd = &vmstate_allwinner_sdhost; +} + +static void allwinner_sdhost_sun4i_class_init(ObjectClass *klass, void *data) +{ + AwSdHostClass *sc = AW_SDHOST_CLASS(klass); + sc->max_desc_size = 8 * KiB; +} + +static void allwinner_sdhost_sun5i_class_init(ObjectClass *klass, void *data) +{ + AwSdHostClass *sc = AW_SDHOST_CLASS(klass); + sc->max_desc_size = 64 * KiB; +} + +static TypeInfo allwinner_sdhost_info = { + .name = TYPE_AW_SDHOST, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_init = allwinner_sdhost_init, + .instance_size = sizeof(AwSdHostState), + .class_init = allwinner_sdhost_class_init, + .class_size = sizeof(AwSdHostClass), + .abstract = true, +}; + +static const TypeInfo allwinner_sdhost_sun4i_info = { + .name = TYPE_AW_SDHOST_SUN4I, + .parent = TYPE_AW_SDHOST, + .class_init = allwinner_sdhost_sun4i_class_init, +}; + +static const TypeInfo allwinner_sdhost_sun5i_info = { + .name = TYPE_AW_SDHOST_SUN5I, + .parent = TYPE_AW_SDHOST, + .class_init = allwinner_sdhost_sun5i_class_init, +}; + +static const TypeInfo allwinner_sdhost_bus_info = { + .name = TYPE_AW_SDHOST_BUS, + .parent = TYPE_SD_BUS, + .instance_size = sizeof(SDBus), + .class_init = allwinner_sdhost_bus_class_init, +}; + +static void allwinner_sdhost_register_types(void) +{ + type_register_static(&allwinner_sdhost_info); + type_register_static(&allwinner_sdhost_sun4i_info); + type_register_static(&allwinner_sdhost_sun5i_info); + type_register_static(&allwinner_sdhost_bus_info); +} + +type_init(allwinner_sdhost_register_types) diff --git a/hw/sd/aspeed_sdhci.c b/hw/sd/aspeed_sdhci.c index cff3eb7dd2..6a039a1d2f 100644 --- a/hw/sd/aspeed_sdhci.c +++ b/hw/sd/aspeed_sdhci.c @@ -13,6 +13,7 @@ #include "qapi/error.h" #include "hw/irq.h" #include "migration/vmstate.h" +#include "hw/qdev-properties.h" #define ASPEED_SDHCI_INFO 0x00 #define ASPEED_SDHCI_INFO_RESET 0x00030000 @@ -120,14 +121,14 @@ static void aspeed_sdhci_realize(DeviceState *dev, Error **errp) /* Create input irqs for the slots */ qdev_init_gpio_in_named_with_opaque(DEVICE(sbd), aspeed_sdhci_set_irq, - sdhci, NULL, ASPEED_SDHCI_NUM_SLOTS); + sdhci, NULL, sdhci->num_slots); sysbus_init_irq(sbd, &sdhci->irq); memory_region_init_io(&sdhci->iomem, OBJECT(sdhci), &aspeed_sdhci_ops, sdhci, TYPE_ASPEED_SDHCI, 0x1000); sysbus_init_mmio(sbd, &sdhci->iomem); - for (int i = 0; i < ASPEED_SDHCI_NUM_SLOTS; ++i) { + for (int i = 0; i < sdhci->num_slots; ++i) { Object *sdhci_slot = OBJECT(&sdhci->slots[i]); SysBusDevice *sbd_slot = SYS_BUS_DEVICE(&sdhci->slots[i]); @@ -174,6 +175,11 @@ static const VMStateDescription vmstate_aspeed_sdhci = { }, }; +static Property aspeed_sdhci_properties[] = { + DEFINE_PROP_UINT8("num-slots", AspeedSDHCIState, num_slots, 0), + DEFINE_PROP_END_OF_LIST(), +}; + static void aspeed_sdhci_class_init(ObjectClass *classp, void *data) { DeviceClass *dc = DEVICE_CLASS(classp); @@ -181,6 +187,7 @@ static void aspeed_sdhci_class_init(ObjectClass *classp, void *data) dc->realize = aspeed_sdhci_realize; dc->reset = aspeed_sdhci_reset; dc->vmsd = &vmstate_aspeed_sdhci; + device_class_set_props(dc, aspeed_sdhci_properties); } static TypeInfo aspeed_sdhci_info = { diff --git a/hw/sd/omap_mmc.c b/hw/sd/omap_mmc.c index c6e516b611..4088a8a80b 100644 --- a/hw/sd/omap_mmc.c +++ b/hw/sd/omap_mmc.c @@ -318,7 +318,7 @@ void omap_mmc_reset(struct omap_mmc_s *host) * into any bus, and we must reset it manually. When omap_mmc is * QOMified this must move into the QOM reset function. */ - device_reset(DEVICE(host->card)); + device_legacy_reset(DEVICE(host->card)); } static uint64_t omap_mmc_read(void *opaque, hwaddr offset, diff --git a/hw/sd/pl181.c b/hw/sd/pl181.c index 8033fe455d..2b3776a6a0 100644 --- a/hw/sd/pl181.c +++ b/hw/sd/pl181.c @@ -482,7 +482,7 @@ static void pl181_reset(DeviceState *d) /* Since we're still using the legacy SD API the card is not plugged * into any bus, and we must reset it manually. */ - device_reset(DEVICE(s->card)); + device_legacy_reset(DEVICE(s->card)); } static void pl181_init(Object *obj) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 49b83576e7..71a9af09ab 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -2118,7 +2118,7 @@ static void sd_class_init(ObjectClass *klass, void *data) SDCardClass *sc = SD_CARD_CLASS(klass); dc->realize = sd_realize; - dc->props = sd_properties; + device_class_set_props(dc, sd_properties); dc->vmsd = &sd_vmstate; dc->reset = sd_reset; dc->bus_type = TYPE_SD_BUS; diff --git a/hw/sd/sdhci-pci.c b/hw/sd/sdhci-pci.c index 2c8d8f59b5..19fa8bd8ed 100644 --- a/hw/sd/sdhci-pci.c +++ b/hw/sd/sdhci-pci.c @@ -64,7 +64,7 @@ static void sdhci_pci_class_init(ObjectClass *klass, void *data) k->vendor_id = PCI_VENDOR_ID_REDHAT; k->device_id = PCI_DEVICE_ID_REDHAT_SDHCI; k->class_id = PCI_CLASS_SYSTEM_SDHCI; - dc->props = sdhci_pci_properties; + device_class_set_props(dc, sdhci_pci_properties); sdhci_common_class_init(klass, data); } diff --git a/hw/sd/sdhci.c b/hw/sd/sdhci.c index 88404d0e9d..de63ffb037 100644 --- a/hw/sd/sdhci.c +++ b/hw/sd/sdhci.c @@ -701,8 +701,7 @@ static void get_adma_description(SDHCIState *s, ADMADescr *dscr) hwaddr entry_addr = (hwaddr)s->admasysaddr; switch (SDHC_DMA_TYPE(s->hostctl1)) { case SDHC_CTRL_ADMA2_32: - dma_memory_read(s->dma_as, entry_addr, (uint8_t *)&adma2, - sizeof(adma2)); + dma_memory_read(s->dma_as, entry_addr, &adma2, sizeof(adma2)); adma2 = le64_to_cpu(adma2); /* The spec does not specify endianness of descriptor table. * We currently assume that it is LE. @@ -713,8 +712,7 @@ static void get_adma_description(SDHCIState *s, ADMADescr *dscr) dscr->incr = 8; break; case SDHC_CTRL_ADMA1_32: - dma_memory_read(s->dma_as, entry_addr, (uint8_t *)&adma1, - sizeof(adma1)); + dma_memory_read(s->dma_as, entry_addr, &adma1, sizeof(adma1)); adma1 = le32_to_cpu(adma1); dscr->addr = (hwaddr)(adma1 & 0xFFFFF000); dscr->attr = (uint8_t)extract32(adma1, 0, 7); @@ -726,13 +724,10 @@ static void get_adma_description(SDHCIState *s, ADMADescr *dscr) } break; case SDHC_CTRL_ADMA2_64: - dma_memory_read(s->dma_as, entry_addr, - (uint8_t *)(&dscr->attr), 1); - dma_memory_read(s->dma_as, entry_addr + 2, - (uint8_t *)(&dscr->length), 2); + dma_memory_read(s->dma_as, entry_addr, &dscr->attr, 1); + dma_memory_read(s->dma_as, entry_addr + 2, &dscr->length, 2); dscr->length = le16_to_cpu(dscr->length); - dma_memory_read(s->dma_as, entry_addr + 4, - (uint8_t *)(&dscr->addr), 8); + dma_memory_read(s->dma_as, entry_addr + 4, &dscr->addr, 8); dscr->addr = le64_to_cpu(dscr->addr); dscr->attr &= (uint8_t) ~0xC0; dscr->incr = 12; @@ -1459,7 +1454,7 @@ static void sdhci_sysbus_finalize(Object *obj) sdhci_uninitfn(s); } -static void sdhci_sysbus_realize(DeviceState *dev, Error ** errp) +static void sdhci_sysbus_realize(DeviceState *dev, Error **errp) { SDHCIState *s = SYSBUS_SDHCI(dev); SysBusDevice *sbd = SYS_BUS_DEVICE(dev); @@ -1499,7 +1494,7 @@ static void sdhci_sysbus_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->props = sdhci_sysbus_properties; + device_class_set_props(dc, sdhci_sysbus_properties); dc->realize = sdhci_sysbus_realize; dc->unrealize = sdhci_sysbus_unrealize; diff --git a/hw/sd/ssi-sd.c b/hw/sd/ssi-sd.c index 91db069212..829797b597 100644 --- a/hw/sd/ssi-sd.c +++ b/hw/sd/ssi-sd.c @@ -255,13 +255,25 @@ static void ssi_sd_realize(SSISlave *d, Error **errp) carddev = qdev_create(BUS(&s->sdbus), TYPE_SD_CARD); if (dinfo) { qdev_prop_set_drive(carddev, "drive", blk_by_legacy_dinfo(dinfo), &err); + if (err) { + goto fail; + } } + object_property_set_bool(OBJECT(carddev), true, "spi", &err); + if (err) { + goto fail; + } + object_property_set_bool(OBJECT(carddev), true, "realized", &err); if (err) { - error_setg(errp, "failed to init SD card: %s", error_get_pretty(err)); - return; + goto fail; } + + return; + +fail: + error_propagate_prepend(errp, err, "failed to init SD card: "); } static void ssi_sd_reset(DeviceState *dev) diff --git a/hw/sd/trace-events b/hw/sd/trace-events index efcff666a2..5f09d32eb2 100644 --- a/hw/sd/trace-events +++ b/hw/sd/trace-events @@ -1,5 +1,12 @@ # See docs/devel/tracing.txt for syntax documentation. +# allwinner-sdhost.c +allwinner_sdhost_set_inserted(bool inserted) "inserted %u" +allwinner_sdhost_process_desc(uint64_t desc_addr, uint32_t desc_size, bool is_write, uint32_t max_bytes) "desc_addr 0x%" PRIx64 " desc_size %" PRIu32 " is_write %u max_bytes %" PRIu32 +allwinner_sdhost_read(uint64_t offset, uint64_t data, unsigned size) "offset 0x%" PRIx64 " data 0x%" PRIx64 " size %" PRIu32 +allwinner_sdhost_write(uint64_t offset, uint64_t data, unsigned size) "offset 0x%" PRIx64 " data 0x%" PRIx64 " size %" PRIu32 +allwinner_sdhost_update_irq(uint32_t irq) "IRQ bits 0x%" PRIx32 + # bcm2835_sdhost.c bcm2835_sdhost_read(uint64_t offset, uint64_t data, unsigned size) "offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" bcm2835_sdhost_write(uint64_t offset, uint64_t data, unsigned size) "offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" diff --git a/hw/semihosting/console.c b/hw/semihosting/console.c index b4b17c8afb..6346bd7f50 100644 --- a/hw/semihosting/console.c +++ b/hw/semihosting/console.c @@ -20,8 +20,15 @@ #include "hw/semihosting/semihost.h" #include "hw/semihosting/console.h" #include "exec/gdbstub.h" +#include "exec/exec-all.h" #include "qemu/log.h" #include "chardev/char.h" +#include +#include "chardev/char-fe.h" +#include "sysemu/sysemu.h" +#include "qemu/main-loop.h" +#include "qapi/error.h" +#include "qemu/fifo8.h" int qemu_semihosting_log_out(const char *s, int len) { @@ -98,3 +105,75 @@ void qemu_semihosting_console_outc(CPUArchState *env, target_ulong addr) __func__, addr); } } + +#define FIFO_SIZE 1024 + +/* Access to this structure is protected by the BQL */ +typedef struct SemihostingConsole { + CharBackend backend; + GSList *sleeping_cpus; + bool got; + Fifo8 fifo; +} SemihostingConsole; + +static SemihostingConsole console; + +static int console_can_read(void *opaque) +{ + SemihostingConsole *c = opaque; + int ret; + g_assert(qemu_mutex_iothread_locked()); + ret = (int) fifo8_num_free(&c->fifo); + return ret; +} + +static void console_wake_up(gpointer data, gpointer user_data) +{ + CPUState *cs = (CPUState *) data; + /* cpu_handle_halt won't know we have work so just unbung here */ + cs->halted = 0; + qemu_cpu_kick(cs); +} + +static void console_read(void *opaque, const uint8_t *buf, int size) +{ + SemihostingConsole *c = opaque; + g_assert(qemu_mutex_iothread_locked()); + while (size-- && !fifo8_is_full(&c->fifo)) { + fifo8_push(&c->fifo, *buf++); + } + g_slist_foreach(c->sleeping_cpus, console_wake_up, NULL); + c->sleeping_cpus = NULL; +} + +target_ulong qemu_semihosting_console_inc(CPUArchState *env) +{ + uint8_t ch; + SemihostingConsole *c = &console; + g_assert(qemu_mutex_iothread_locked()); + g_assert(current_cpu); + if (fifo8_is_empty(&c->fifo)) { + c->sleeping_cpus = g_slist_prepend(c->sleeping_cpus, current_cpu); + current_cpu->halted = 1; + current_cpu->exception_index = EXCP_HALTED; + cpu_loop_exit(current_cpu); + /* never returns */ + } + ch = fifo8_pop(&c->fifo); + return (target_ulong) ch; +} + +void qemu_semihosting_console_init(void) +{ + Chardev *chr = semihosting_get_chardev(); + + if (chr) { + fifo8_create(&console.fifo, FIFO_SIZE); + qemu_chr_fe_init(&console.backend, chr, &error_abort); + qemu_chr_fe_set_handlers(&console.backend, + console_can_read, + console_read, + NULL, NULL, &console, + NULL, true); + } +} diff --git a/hw/sh4/r2d.c b/hw/sh4/r2d.c index ee0840f380..72bb5285cc 100644 --- a/hw/sh4/r2d.c +++ b/hw/sh4/r2d.c @@ -272,7 +272,7 @@ static void r2d_init(MachineState *machine) busdev = SYS_BUS_DEVICE(dev); qdev_prop_set_uint32(dev, "vram-size", SM501_VRAM_SIZE); qdev_prop_set_uint32(dev, "base", 0x10000000); - qdev_prop_set_ptr(dev, "chr-state", serial_hd(2)); + qdev_prop_set_chr(dev, "chardev", serial_hd(2)); qdev_init_nofail(dev); sysbus_mmio_map(busdev, 0, 0x10000000); sysbus_mmio_map(busdev, 1, 0x13e00000); diff --git a/hw/sh4/sh_pci.c b/hw/sh4/sh_pci.c index 71afd23b67..08f2fc1dde 100644 --- a/hw/sh4/sh_pci.c +++ b/hw/sh4/sh_pci.c @@ -67,12 +67,8 @@ static void sh_pci_reg_write (void *p, hwaddr addr, uint64_t val, pcic->mbr = val & 0xff000001; break; case 0x1c8: - if ((val & 0xfffc0000) != (pcic->iobr & 0xfffc0000)) { - memory_region_del_subregion(get_system_memory(), &pcic->isa); - pcic->iobr = val & 0xfffc0001; - memory_region_add_subregion(get_system_memory(), - pcic->iobr & 0xfffc0000, &pcic->isa); - } + pcic->iobr = val & 0xfffc0001; + memory_region_set_alias_offset(&pcic->isa, val & 0xfffc0000); break; case 0x220: pci_data_write(phb->bus, pcic->par, val, 4); @@ -147,8 +143,7 @@ static void sh_pci_device_realize(DeviceState *dev, Error **errp) get_system_io(), 0, 0x40000); sysbus_init_mmio(sbd, &s->memconfig_p4); sysbus_init_mmio(sbd, &s->memconfig_a7); - s->iobr = 0xfe240000; - memory_region_add_subregion(get_system_memory(), s->iobr, &s->isa); + memory_region_add_subregion(get_system_memory(), 0xfe240000, &s->isa); s->dev = pci_create_simple(phb->bus, PCI_DEVFN(0, 0), "sh_pci_host"); } diff --git a/hw/sh4/shix.c b/hw/sh4/shix.c index 2fc2915428..f410c08883 100644 --- a/hw/sh4/shix.c +++ b/hw/sh4/shix.c @@ -53,8 +53,7 @@ static void shix_init(MachineState *machine) cpu = SUPERH_CPU(cpu_create(machine->cpu_type)); /* Allocate memory space */ - memory_region_init_ram(rom, NULL, "shix.rom", 0x4000, &error_fatal); - memory_region_set_readonly(rom, true); + memory_region_init_rom(rom, NULL, "shix.rom", 0x4000, &error_fatal); memory_region_add_subregion(sysmem, 0x00000000, rom); memory_region_init_ram(&sdram[0], NULL, "shix.sdram1", 0x01000000, &error_fatal); @@ -82,7 +81,7 @@ static void shix_machine_init(MachineClass *mc) { mc->desc = "shix card"; mc->init = shix_init; - mc->is_default = 1; + mc->is_default = true; mc->default_cpu_type = TYPE_SH7750R_CPU; } 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/sparc/Kconfig b/hw/sparc/Kconfig index 2a83a8010e..22aff2f5b7 100644 --- a/hw/sparc/Kconfig +++ b/hw/sparc/Kconfig @@ -12,6 +12,7 @@ config SUN4M select LANCE select M48T59 select STP2000 + select CHRP_NVRAM config LEON3 bool diff --git a/hw/sparc/leon3.c b/hw/sparc/leon3.c index c5f1b1ee72..8f024dab7b 100644 --- a/hw/sparc/leon3.c +++ b/hw/sparc/leon3.c @@ -143,9 +143,14 @@ void leon3_irq_ack(void *irq_manager, int intno) grlib_irqmp_ack((DeviceState *)irq_manager, intno); } -static void leon3_set_pil_in(void *opaque, uint32_t pil_in) +/* + * This device assumes that the incoming 'level' value on the + * qemu_irq is the interrupt number, not just a simple 0/1 level. + */ +static void leon3_set_pil_in(void *opaque, int n, int level) { - CPUSPARCState *env = (CPUSPARCState *)opaque; + CPUSPARCState *env = opaque; + uint32_t pil_in = level; CPUState *cs; assert(env != NULL); @@ -184,7 +189,6 @@ static void leon3_generic_hw_init(MachineState *machine) SPARCCPU *cpu; CPUSPARCState *env; MemoryRegion *address_space_mem = get_system_memory(); - MemoryRegion *ram = g_new(MemoryRegion, 1); MemoryRegion *prom = g_new(MemoryRegion, 1); int ret; char *filename; @@ -225,8 +229,10 @@ static void leon3_generic_hw_init(MachineState *machine) /* Allocate IRQ manager */ dev = qdev_create(NULL, TYPE_GRLIB_IRQMP); - qdev_prop_set_ptr(dev, "set_pil_in", leon3_set_pil_in); - qdev_prop_set_ptr(dev, "set_pil_in_opaque", env); + qdev_init_gpio_in_named_with_opaque(DEVICE(cpu), leon3_set_pil_in, + env, "pil", 1); + qdev_connect_gpio_out_named(dev, "grlib-irq", 0, + qdev_get_gpio_in_named(DEVICE(cpu), "pil", 0)); qdev_init_nofail(dev); sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, LEON3_IRQMP_OFFSET); env->irq_manager = dev; @@ -244,13 +250,12 @@ static void leon3_generic_hw_init(MachineState *machine) exit(1); } - memory_region_allocate_system_memory(ram, NULL, "leon3.ram", ram_size); - memory_region_add_subregion(address_space_mem, LEON3_RAM_OFFSET, ram); + memory_region_add_subregion(address_space_mem, LEON3_RAM_OFFSET, + machine->ram); /* Allocate BIOS */ prom_size = 8 * MiB; - memory_region_init_ram(prom, NULL, "Leon3.bios", prom_size, &error_fatal); - memory_region_set_readonly(prom, true); + memory_region_init_rom(prom, NULL, "Leon3.bios", prom_size, &error_fatal); memory_region_add_subregion(address_space_mem, LEON3_PROM_OFFSET, prom); /* Load boot prom */ @@ -290,7 +295,7 @@ static void leon3_generic_hw_init(MachineState *machine) uint64_t entry; kernel_size = load_elf(kernel_filename, NULL, NULL, NULL, - &entry, NULL, NULL, + &entry, NULL, NULL, NULL, 1 /* big endian */, EM_SPARC, 0, 0); if (kernel_size < 0) { kernel_size = load_uimage(kernel_filename, NULL, &entry, @@ -351,6 +356,7 @@ static void leon3_generic_machine_init(MachineClass *mc) mc->desc = "Leon-3 generic"; mc->init = leon3_generic_hw_init; mc->default_cpu_type = SPARC_CPU_TYPE_NAME("LEON3"); + mc->default_ram_id = "leon3.ram"; } DEFINE_MACHINE("leon3_generic", leon3_generic_machine_init) diff --git a/hw/sparc/sun4m.c b/hw/sparc/sun4m.c index 2aaa5bf1ae..36ee1a0a3d 100644 --- a/hw/sparc/sun4m.c +++ b/hw/sparc/sun4m.c @@ -270,7 +270,7 @@ static unsigned long sun4m_load_kernel(const char *kernel_filename, #endif kernel_size = load_elf(kernel_filename, NULL, translate_kernel_address, NULL, - NULL, NULL, NULL, 1, EM_SPARC, 0, 0); + NULL, NULL, NULL, NULL, 1, EM_SPARC, 0, 0); if (kernel_size < 0) kernel_size = load_aout(kernel_filename, KERNEL_LOAD_ADDR, RAM_size - KERNEL_LOAD_ADDR, bswap_needed, @@ -721,7 +721,7 @@ static void prom_init(hwaddr addr, const char *bios_name) if (filename) { ret = load_elf(filename, NULL, translate_prom_address, &addr, NULL, - NULL, NULL, 1, EM_SPARC, 0, 0); + NULL, NULL, NULL, 1, EM_SPARC, 0, 0); if (ret < 0 || ret > PROM_SIZE_MAX) { ret = load_image_targphys(filename, addr, PROM_SIZE_MAX); } @@ -761,7 +761,7 @@ static void prom_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->props = prom_properties; + device_class_set_props(dc, prom_properties); dc->realize = prom_realize; } @@ -777,63 +777,42 @@ static const TypeInfo prom_info = { typedef struct RamDevice { SysBusDevice parent_obj; - - MemoryRegion ram; - uint64_t size; + HostMemoryBackend *memdev; } RamDevice; /* System RAM */ static void ram_realize(DeviceState *dev, Error **errp) { RamDevice *d = SUN4M_RAM(dev); - SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + MemoryRegion *ram = host_memory_backend_get_memory(d->memdev); - memory_region_allocate_system_memory(&d->ram, OBJECT(d), "sun4m.ram", - d->size); - sysbus_init_mmio(sbd, &d->ram); + sysbus_init_mmio(SYS_BUS_DEVICE(dev), ram); } -static void ram_init(hwaddr addr, ram_addr_t RAM_size, - uint64_t max_mem) +static void ram_initfn(Object *obj) { - DeviceState *dev; - SysBusDevice *s; - RamDevice *d; - - /* allocate RAM */ - if ((uint64_t)RAM_size > max_mem) { - error_report("Too much memory for this machine: %" PRId64 "," - " maximum %" PRId64, - RAM_size / MiB, max_mem / MiB); - exit(1); - } - dev = qdev_create(NULL, "memory"); - s = SYS_BUS_DEVICE(dev); - - d = SUN4M_RAM(dev); - d->size = RAM_size; - qdev_init_nofail(dev); - - sysbus_mmio_map(s, 0, addr); + RamDevice *d = SUN4M_RAM(obj); + object_property_add_link(obj, "memdev", TYPE_MEMORY_BACKEND, + (Object **)&d->memdev, + object_property_allow_set_link, + OBJ_PROP_LINK_STRONG, &error_abort); + object_property_set_description(obj, "memdev", "Set RAM backend" + "Valid value is ID of a hostmem backend", + &error_abort); } -static Property ram_properties[] = { - DEFINE_PROP_UINT64("size", RamDevice, size, 0), - DEFINE_PROP_END_OF_LIST(), -}; - static void ram_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = ram_realize; - dc->props = ram_properties; } static const TypeInfo ram_info = { .name = TYPE_SUN4M_MEMORY, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(RamDevice), + .instance_init = ram_initfn, .class_init = ram_class_init, }; @@ -879,6 +858,15 @@ static void sun4m_hw_init(const struct sun4m_hwdef *hwdef, SysBusDevice *s; unsigned int smp_cpus = machine->smp.cpus; unsigned int max_cpus = machine->smp.max_cpus; + Object *ram_memdev = object_resolve_path_type(machine->ram_memdev_id, + TYPE_MEMORY_BACKEND, NULL); + + if (machine->ram_size > hwdef->max_mem) { + error_report("Too much memory for this machine: %" PRId64 "," + " maximum %" PRId64, + machine->ram_size / MiB, hwdef->max_mem / MiB); + exit(1); + } /* init CPUs */ for(i = 0; i < smp_cpus; i++) { @@ -888,9 +876,12 @@ static void sun4m_hw_init(const struct sun4m_hwdef *hwdef, for (i = smp_cpus; i < MAX_CPUS; i++) cpu_irqs[i] = qemu_allocate_irqs(dummy_cpu_set_irq, NULL, MAX_PILS); + /* Create and map RAM frontend */ + dev = qdev_create(NULL, "memory"); + object_property_set_link(OBJECT(dev), ram_memdev, "memdev", &error_fatal); + qdev_init_nofail(dev); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0); - /* set up devices */ - ram_init(0, machine->ram_size, hwdef->max_mem); /* models without ECC don't trap when missing ram is accessed */ if (!hwdef->ecc_base) { empty_slot_init(machine->ram_size, hwdef->max_mem - machine->ram_size); @@ -1078,7 +1069,7 @@ static void sun4m_hw_init(const struct sun4m_hwdef *hwdef, fw_cfg_add_i16(fw_cfg, FW_CFG_NB_CPUS, (uint16_t)smp_cpus); fw_cfg_add_i16(fw_cfg, FW_CFG_MAX_CPUS, (uint16_t)max_cpus); - fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)ram_size); + fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)machine->ram_size); fw_cfg_add_i16(fw_cfg, FW_CFG_MACHINE_ID, hwdef->machine_id); fw_cfg_add_i16(fw_cfg, FW_CFG_SUN4M_DEPTH, graphic_depth); fw_cfg_add_i16(fw_cfg, FW_CFG_SUN4M_WIDTH, graphic_width); @@ -1411,10 +1402,11 @@ static void ss5_class_init(ObjectClass *oc, void *data) mc->desc = "Sun4m platform, SPARCstation 5"; mc->init = ss5_init; mc->block_default_type = IF_SCSI; - mc->is_default = 1; + mc->is_default = true; mc->default_boot_order = "c"; mc->default_cpu_type = SPARC_CPU_TYPE_NAME("Fujitsu-MB86904"); mc->default_display = "tcx"; + mc->default_ram_id = "sun4m.ram"; } static const TypeInfo ss5_type = { @@ -1434,6 +1426,7 @@ static void ss10_class_init(ObjectClass *oc, void *data) mc->default_boot_order = "c"; mc->default_cpu_type = SPARC_CPU_TYPE_NAME("TI-SuperSparc-II"); mc->default_display = "tcx"; + mc->default_ram_id = "sun4m.ram"; } static const TypeInfo ss10_type = { @@ -1453,6 +1446,7 @@ static void ss600mp_class_init(ObjectClass *oc, void *data) mc->default_boot_order = "c"; mc->default_cpu_type = SPARC_CPU_TYPE_NAME("TI-SuperSparc-II"); mc->default_display = "tcx"; + mc->default_ram_id = "sun4m.ram"; } static const TypeInfo ss600mp_type = { @@ -1472,6 +1466,7 @@ static void ss20_class_init(ObjectClass *oc, void *data) mc->default_boot_order = "c"; mc->default_cpu_type = SPARC_CPU_TYPE_NAME("TI-SuperSparc-II"); mc->default_display = "tcx"; + mc->default_ram_id = "sun4m.ram"; } static const TypeInfo ss20_type = { @@ -1490,6 +1485,7 @@ static void voyager_class_init(ObjectClass *oc, void *data) mc->default_boot_order = "c"; mc->default_cpu_type = SPARC_CPU_TYPE_NAME("Fujitsu-MB86904"); mc->default_display = "tcx"; + mc->default_ram_id = "sun4m.ram"; } static const TypeInfo voyager_type = { @@ -1508,6 +1504,7 @@ static void ss_lx_class_init(ObjectClass *oc, void *data) mc->default_boot_order = "c"; mc->default_cpu_type = SPARC_CPU_TYPE_NAME("TI-MicroSparc-I"); mc->default_display = "tcx"; + mc->default_ram_id = "sun4m.ram"; } static const TypeInfo ss_lx_type = { @@ -1526,6 +1523,7 @@ static void ss4_class_init(ObjectClass *oc, void *data) mc->default_boot_order = "c"; mc->default_cpu_type = SPARC_CPU_TYPE_NAME("Fujitsu-MB86904"); mc->default_display = "tcx"; + mc->default_ram_id = "sun4m.ram"; } static const TypeInfo ss4_type = { @@ -1544,6 +1542,7 @@ static void scls_class_init(ObjectClass *oc, void *data) mc->default_boot_order = "c"; mc->default_cpu_type = SPARC_CPU_TYPE_NAME("TI-MicroSparc-I"); mc->default_display = "tcx"; + mc->default_ram_id = "sun4m.ram"; } static const TypeInfo scls_type = { @@ -1562,6 +1561,7 @@ static void sbook_class_init(ObjectClass *oc, void *data) mc->default_boot_order = "c"; mc->default_cpu_type = SPARC_CPU_TYPE_NAME("TI-MicroSparc-I"); mc->default_display = "tcx"; + mc->default_ram_id = "sun4m.ram"; } static const TypeInfo sbook_type = { diff --git a/hw/sparc/sun4m_iommu.c b/hw/sparc/sun4m_iommu.c index ccf46a5147..71f5465249 100644 --- a/hw/sparc/sun4m_iommu.c +++ b/hw/sparc/sun4m_iommu.c @@ -379,7 +379,7 @@ static void iommu_class_init(ObjectClass *klass, void *data) dc->reset = iommu_reset; dc->vmsd = &vmstate_iommu; - dc->props = iommu_properties; + device_class_set_props(dc, iommu_properties); } static const TypeInfo iommu_info = { diff --git a/hw/sparc64/Kconfig b/hw/sparc64/Kconfig index f9f8b0f73a..980a201bb7 100644 --- a/hw/sparc64/Kconfig +++ b/hw/sparc64/Kconfig @@ -12,6 +12,7 @@ config SUN4U select IDE_CMD646 select PCKBD select SIMBA + select CHRP_NVRAM config NIAGARA bool diff --git a/hw/sparc64/niagara.c b/hw/sparc64/niagara.c index 5eb2d097b9..ab5ef8c5b3 100644 --- a/hw/sparc64/niagara.c +++ b/hw/sparc64/niagara.c @@ -40,7 +40,6 @@ typedef struct NiagaraBoardState { MemoryRegion hv_ram; - MemoryRegion partition_ram; MemoryRegion nvram; MemoryRegion md_rom; MemoryRegion hv_rom; @@ -111,11 +110,8 @@ static void niagara_init(MachineState *machine) NIAGARA_HV_RAM_SIZE, &error_fatal); memory_region_add_subregion(sysmem, NIAGARA_HV_RAM_BASE, &s->hv_ram); - memory_region_allocate_system_memory(&s->partition_ram, NULL, - "sun4v-partition.ram", - machine->ram_size); memory_region_add_subregion(sysmem, NIAGARA_PARTITION_RAM_BASE, - &s->partition_ram); + machine->ram); memory_region_init_ram(&s->nvram, NULL, "sun4v.nvram", NIAGARA_NVRAM_SIZE, &error_fatal); @@ -173,6 +169,7 @@ static void niagara_class_init(ObjectClass *oc, void *data) mc->max_cpus = 1; /* XXX for now */ mc->default_boot_order = "c"; mc->default_cpu_type = SPARC_CPU_TYPE_NAME("Sun-UltraSparc-T1"); + mc->default_ram_id = "sun4v-partition.ram"; } static const TypeInfo niagara_type = { diff --git a/hw/sparc64/sun4u.c b/hw/sparc64/sun4u.c index 955082773b..6abfcb30f8 100644 --- a/hw/sparc64/sun4u.c +++ b/hw/sparc64/sun4u.c @@ -50,7 +50,6 @@ #include "hw/sparc/sparc64.h" #include "hw/nvram/fw_cfg.h" #include "hw/sysbus.h" -#include "hw/ide.h" #include "hw/ide/pci.h" #include "hw/loader.h" #include "hw/fw-path-provider.h" @@ -175,7 +174,8 @@ static uint64_t sun4u_load_kernel(const char *kernel_filename, bswap_needed = 0; #endif kernel_size = load_elf(kernel_filename, NULL, NULL, NULL, kernel_entry, - kernel_addr, &kernel_top, 1, EM_SPARCV9, 0, 0); + kernel_addr, &kernel_top, NULL, 1, EM_SPARCV9, 0, + 0); if (kernel_size < 0) { *kernel_addr = KERNEL_LOAD_ADDR; *kernel_entry = KERNEL_LOAD_ADDR; @@ -389,7 +389,7 @@ static void ebus_class_init(ObjectClass *klass, void *data) k->device_id = PCI_DEVICE_ID_SUN_EBUS; k->revision = 0x01; k->class_id = PCI_CLASS_BRIDGE_OTHER; - dc->props = ebus_properties; + device_class_set_props(dc, ebus_properties); } static const TypeInfo ebus_info = { @@ -439,7 +439,7 @@ static void prom_init(hwaddr addr, const char *bios_name) filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); if (filename) { ret = load_elf(filename, NULL, translate_prom_address, &addr, - NULL, NULL, NULL, 1, EM_SPARCV9, 0, 0); + NULL, NULL, NULL, NULL, 1, EM_SPARCV9, 0, 0); if (ret < 0 || ret > PROM_SIZE_MAX) { ret = load_image_targphys(filename, addr, PROM_SIZE_MAX); } @@ -479,7 +479,7 @@ static void prom_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->props = prom_properties; + device_class_set_props(dc, prom_properties); dc->realize = prom_realize; } @@ -540,7 +540,7 @@ static void ram_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = ram_realize; - dc->props = ram_properties; + device_class_set_props(dc, ram_properties); } static const TypeInfo ram_info = { @@ -562,7 +562,6 @@ static void sun4uv_init(MemoryRegion *address_space_mem, PCIBus *pci_bus, *pci_busA, *pci_busB; PCIDevice *ebus, *pci_dev; SysBusDevice *s; - DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; DeviceState *iommu, *dev; FWCfgState *fw_cfg; NICInfo *nd; @@ -662,12 +661,10 @@ static void sun4uv_init(MemoryRegion *address_space_mem, qemu_macaddr_default_if_unset(&macaddr); } - ide_drive_get(hd, ARRAY_SIZE(hd)); - pci_dev = pci_create(pci_busA, PCI_DEVFN(3, 0), "cmd646-ide"); qdev_prop_set_uint32(&pci_dev->qdev, "secondary", 1); qdev_init_nofail(&pci_dev->qdev); - pci_ide_create_devs(pci_dev, hd); + pci_ide_create_devs(pci_dev); /* Map NVRAM into I/O (ebus) space */ nvram = m48t59_init(NULL, 0, 0, NVRAM_SIZE, 1968, 59); @@ -815,7 +812,7 @@ static void sun4u_class_init(ObjectClass *oc, void *data) mc->init = sun4u_init; mc->block_default_type = IF_IDE; mc->max_cpus = 1; /* XXX for now */ - mc->is_default = 1; + mc->is_default = true; mc->default_boot_order = "c"; mc->default_cpu_type = SPARC_CPU_TYPE_NAME("TI-UltraSparc-IIi"); mc->ignore_boot_device_suffixes = true; diff --git a/hw/ssi/Makefile.objs b/hw/ssi/Makefile.objs index f5bcc65fe7..07a85f1967 100644 --- a/hw/ssi/Makefile.objs +++ b/hw/ssi/Makefile.objs @@ -6,5 +6,5 @@ common-obj-$(CONFIG_ASPEED_SOC) += aspeed_smc.o common-obj-$(CONFIG_STM32F2XX_SPI) += stm32f2xx_spi.o common-obj-$(CONFIG_MSF2) += mss-spi.o -obj-$(CONFIG_OMAP) += omap_spi.o -obj-$(CONFIG_IMX) += imx_spi.o +common-obj-$(CONFIG_OMAP) += omap_spi.o +common-obj-$(CONFIG_IMX) += imx_spi.o diff --git a/hw/ssi/aspeed_smc.c b/hw/ssi/aspeed_smc.c index f0c7bbbad3..2edccef2d5 100644 --- a/hw/ssi/aspeed_smc.c +++ b/hw/ssi/aspeed_smc.c @@ -31,6 +31,7 @@ #include "qapi/error.h" #include "exec/address-spaces.h" #include "qemu/units.h" +#include "trace.h" #include "hw/irq.h" #include "hw/qdev-properties.h" @@ -137,7 +138,7 @@ /* Checksum Calculation Result */ #define R_DMA_CHECKSUM (0x90 / 4) -/* Misc Control Register #2 */ +/* Read Timing Compensation Register */ #define R_TIMINGS (0x94 / 4) /* SPI controller registers and bits (AST2400) */ @@ -256,6 +257,7 @@ static const AspeedSMCController controllers[] = { .r_ce_ctrl = R_CE_CTRL, .r_ctrl0 = R_CTRL0, .r_timings = R_TIMINGS, + .nregs_timings = 1, .conf_enable_w0 = CONF_ENABLE_W0, .max_slaves = 5, .segments = aspeed_segments_legacy, @@ -271,6 +273,7 @@ static const AspeedSMCController controllers[] = { .r_ce_ctrl = R_CE_CTRL, .r_ctrl0 = R_CTRL0, .r_timings = R_TIMINGS, + .nregs_timings = 1, .conf_enable_w0 = CONF_ENABLE_W0, .max_slaves = 5, .segments = aspeed_segments_fmc, @@ -288,6 +291,7 @@ static const AspeedSMCController controllers[] = { .r_ce_ctrl = 0xff, .r_ctrl0 = R_SPI_CTRL0, .r_timings = R_SPI_TIMINGS, + .nregs_timings = 1, .conf_enable_w0 = SPI_CONF_ENABLE_W0, .max_slaves = 1, .segments = aspeed_segments_spi, @@ -303,6 +307,7 @@ static const AspeedSMCController controllers[] = { .r_ce_ctrl = R_CE_CTRL, .r_ctrl0 = R_CTRL0, .r_timings = R_TIMINGS, + .nregs_timings = 1, .conf_enable_w0 = CONF_ENABLE_W0, .max_slaves = 3, .segments = aspeed_segments_ast2500_fmc, @@ -320,6 +325,7 @@ static const AspeedSMCController controllers[] = { .r_ce_ctrl = R_CE_CTRL, .r_ctrl0 = R_CTRL0, .r_timings = R_TIMINGS, + .nregs_timings = 1, .conf_enable_w0 = CONF_ENABLE_W0, .max_slaves = 2, .segments = aspeed_segments_ast2500_spi1, @@ -335,6 +341,7 @@ static const AspeedSMCController controllers[] = { .r_ce_ctrl = R_CE_CTRL, .r_ctrl0 = R_CTRL0, .r_timings = R_TIMINGS, + .nregs_timings = 1, .conf_enable_w0 = CONF_ENABLE_W0, .max_slaves = 2, .segments = aspeed_segments_ast2500_spi2, @@ -350,12 +357,15 @@ static const AspeedSMCController controllers[] = { .r_ce_ctrl = R_CE_CTRL, .r_ctrl0 = R_CTRL0, .r_timings = R_TIMINGS, + .nregs_timings = 1, .conf_enable_w0 = CONF_ENABLE_W0, .max_slaves = 3, .segments = aspeed_segments_ast2600_fmc, .flash_window_base = ASPEED26_SOC_FMC_FLASH_BASE, .flash_window_size = 0x10000000, .has_dma = true, + .dma_flash_mask = 0x0FFFFFFC, + .dma_dram_mask = 0x3FFFFFFC, .nregs = ASPEED_SMC_R_MAX, .segment_to_reg = aspeed_2600_smc_segment_to_reg, .reg_to_segment = aspeed_2600_smc_reg_to_segment, @@ -365,12 +375,15 @@ static const AspeedSMCController controllers[] = { .r_ce_ctrl = R_CE_CTRL, .r_ctrl0 = R_CTRL0, .r_timings = R_TIMINGS, + .nregs_timings = 2, .conf_enable_w0 = CONF_ENABLE_W0, .max_slaves = 2, .segments = aspeed_segments_ast2600_spi1, .flash_window_base = ASPEED26_SOC_SPI_FLASH_BASE, .flash_window_size = 0x10000000, - .has_dma = false, + .has_dma = true, + .dma_flash_mask = 0x0FFFFFFC, + .dma_dram_mask = 0x3FFFFFFC, .nregs = ASPEED_SMC_R_MAX, .segment_to_reg = aspeed_2600_smc_segment_to_reg, .reg_to_segment = aspeed_2600_smc_reg_to_segment, @@ -380,12 +393,15 @@ static const AspeedSMCController controllers[] = { .r_ce_ctrl = R_CE_CTRL, .r_ctrl0 = R_CTRL0, .r_timings = R_TIMINGS, + .nregs_timings = 3, .conf_enable_w0 = CONF_ENABLE_W0, .max_slaves = 3, .segments = aspeed_segments_ast2600_spi2, .flash_window_base = ASPEED26_SOC_SPI2_FLASH_BASE, .flash_window_size = 0x10000000, - .has_dma = false, + .has_dma = true, + .dma_flash_mask = 0x0FFFFFFC, + .dma_dram_mask = 0x3FFFFFFC, .nregs = ASPEED_SMC_R_MAX, .segment_to_reg = aspeed_2600_smc_segment_to_reg, .reg_to_segment = aspeed_2600_smc_reg_to_segment, @@ -444,8 +460,13 @@ static void aspeed_2600_smc_reg_to_segment(const AspeedSMCState *s, uint32_t start_offset = (reg << 16) & AST2600_SEG_ADDR_MASK; uint32_t end_offset = reg & AST2600_SEG_ADDR_MASK; - seg->addr = s->ctrl->flash_window_base + start_offset; - seg->size = end_offset + MiB - start_offset; + if (reg) { + seg->addr = s->ctrl->flash_window_base + start_offset; + seg->size = end_offset + MiB - start_offset; + } else { + seg->addr = s->ctrl->flash_window_base; + seg->size = 0; + } } static bool aspeed_smc_flash_overlap(const AspeedSMCState *s, @@ -475,14 +496,32 @@ static bool aspeed_smc_flash_overlap(const AspeedSMCState *s, return false; } -static void aspeed_smc_flash_set_segment(AspeedSMCState *s, int cs, - uint64_t new) +static void aspeed_smc_flash_set_segment_region(AspeedSMCState *s, int cs, + uint64_t regval) { AspeedSMCFlash *fl = &s->flashes[cs]; AspeedSegments seg; + s->ctrl->reg_to_segment(s, regval, &seg); + + memory_region_transaction_begin(); + memory_region_set_size(&fl->mmio, seg.size); + memory_region_set_address(&fl->mmio, seg.addr - s->ctrl->flash_window_base); + memory_region_set_enabled(&fl->mmio, !!seg.size); + memory_region_transaction_commit(); + + s->regs[R_SEG_ADDR0 + cs] = regval; +} + +static void aspeed_smc_flash_set_segment(AspeedSMCState *s, int cs, + uint64_t new) +{ + AspeedSegments seg; + s->ctrl->reg_to_segment(s, new, &seg); + trace_aspeed_smc_flash_set_segment(cs, new, seg.addr, seg.addr + seg.size); + /* The start address of CS0 is read-only */ if (cs == 0 && seg.addr != s->ctrl->flash_window_base) { qemu_log_mask(LOG_GUEST_ERROR, @@ -510,8 +549,9 @@ static void aspeed_smc_flash_set_segment(AspeedSMCState *s, int cs, } /* Keep the segment in the overall flash window */ - if (seg.addr + seg.size <= s->ctrl->flash_window_base || - seg.addr > s->ctrl->flash_window_base + s->ctrl->flash_window_size) { + if (seg.size && + (seg.addr + seg.size <= s->ctrl->flash_window_base || + seg.addr > s->ctrl->flash_window_base + s->ctrl->flash_window_size)) { qemu_log_mask(LOG_GUEST_ERROR, "%s: new segment for CS%d is invalid : " "[ 0x%"HWADDR_PRIx" - 0x%"HWADDR_PRIx" ]\n", s->ctrl->name, cs, seg.addr, seg.addr + seg.size); @@ -529,13 +569,7 @@ static void aspeed_smc_flash_set_segment(AspeedSMCState *s, int cs, aspeed_smc_flash_overlap(s, &seg, cs); /* All should be fine now to move the region */ - memory_region_transaction_begin(); - memory_region_set_size(&fl->mmio, seg.size); - memory_region_set_address(&fl->mmio, seg.addr - s->ctrl->flash_window_base); - memory_region_set_enabled(&fl->mmio, true); - memory_region_transaction_commit(); - - s->regs[R_SEG_ADDR0 + cs] = new; + aspeed_smc_flash_set_segment_region(s, cs, new); } static uint64_t aspeed_smc_flash_default_read(void *opaque, hwaddr addr, @@ -611,27 +645,23 @@ static inline int aspeed_smc_flash_is_4byte(const AspeedSMCFlash *fl) } } -static inline bool aspeed_smc_is_ce_stop_active(const AspeedSMCFlash *fl) +static void aspeed_smc_flash_do_select(AspeedSMCFlash *fl, bool unselect) { - const AspeedSMCState *s = fl->controller; + AspeedSMCState *s = fl->controller; - return s->regs[s->r_ctrl0 + fl->id] & CTRL_CE_STOP_ACTIVE; + trace_aspeed_smc_flash_select(fl->id, unselect ? "un" : ""); + + qemu_set_irq(s->cs_lines[fl->id], unselect); } static void aspeed_smc_flash_select(AspeedSMCFlash *fl) { - AspeedSMCState *s = fl->controller; - - s->regs[s->r_ctrl0 + fl->id] &= ~CTRL_CE_STOP_ACTIVE; - qemu_set_irq(s->cs_lines[fl->id], aspeed_smc_is_ce_stop_active(fl)); + aspeed_smc_flash_do_select(fl, false); } static void aspeed_smc_flash_unselect(AspeedSMCFlash *fl) { - AspeedSMCState *s = fl->controller; - - s->regs[s->r_ctrl0 + fl->id] |= CTRL_CE_STOP_ACTIVE; - qemu_set_irq(s->cs_lines[fl->id], aspeed_smc_is_ce_stop_active(fl)); + aspeed_smc_flash_do_select(fl, true); } static uint32_t aspeed_smc_check_segment_addr(const AspeedSMCFlash *fl, @@ -728,6 +758,8 @@ static uint64_t aspeed_smc_flash_read(void *opaque, hwaddr addr, unsigned size) __func__, aspeed_smc_flash_mode(fl)); } + trace_aspeed_smc_flash_read(fl->id, addr, size, ret, + aspeed_smc_flash_mode(fl)); return ret; } @@ -762,11 +794,11 @@ static int aspeed_smc_num_dummies(uint8_t command) case FAST_READ: case DOR: case QOR: + case FAST_READ_4: case DOR_4: case QOR_4: return 1; case DIOR: - case FAST_READ_4: case DIOR_4: return 2; case QIOR: @@ -783,6 +815,9 @@ static bool aspeed_smc_do_snoop(AspeedSMCFlash *fl, uint64_t data, AspeedSMCState *s = fl->controller; uint8_t addr_width = aspeed_smc_flash_is_4byte(fl) ? 4 : 3; + trace_aspeed_smc_do_snoop(fl->id, s->snoop_index, s->snoop_dummies, + (uint8_t) data & 0xff); + if (s->snoop_index == SNOOP_OFF) { return false; /* Do nothing */ @@ -833,6 +868,9 @@ static void aspeed_smc_flash_write(void *opaque, hwaddr addr, uint64_t data, AspeedSMCState *s = fl->controller; int i; + trace_aspeed_smc_flash_write(fl->id, addr, size, data, + aspeed_smc_flash_mode(fl)); + if (!aspeed_smc_is_writable(fl)) { qemu_log_mask(LOG_GUEST_ERROR, "%s: flash is not writable at 0x%" HWADDR_PRIx "\n", __func__, addr); @@ -875,13 +913,25 @@ static const MemoryRegionOps aspeed_smc_flash_ops = { }, }; -static void aspeed_smc_flash_update_cs(AspeedSMCFlash *fl) +static void aspeed_smc_flash_update_ctrl(AspeedSMCFlash *fl, uint32_t value) { AspeedSMCState *s = fl->controller; + bool unselect; - s->snoop_index = aspeed_smc_is_ce_stop_active(fl) ? SNOOP_OFF : SNOOP_START; + /* User mode selects the CS, other modes unselect */ + unselect = (value & CTRL_CMD_MODE_MASK) != CTRL_USERMODE; - qemu_set_irq(s->cs_lines[fl->id], aspeed_smc_is_ce_stop_active(fl)); + /* A change of CTRL_CE_STOP_ACTIVE from 0 to 1, unselects the CS */ + if (!(s->regs[s->r_ctrl0 + fl->id] & CTRL_CE_STOP_ACTIVE) && + value & CTRL_CE_STOP_ACTIVE) { + unselect = true; + } + + s->regs[s->r_ctrl0 + fl->id] = value; + + s->snoop_index = unselect ? SNOOP_OFF : SNOOP_START; + + aspeed_smc_flash_do_select(fl, unselect); } static void aspeed_smc_reset(DeviceState *d) @@ -897,10 +947,10 @@ static void aspeed_smc_reset(DeviceState *d) qemu_set_irq(s->cs_lines[i], true); } - /* setup default segment register values for all */ + /* setup the default segment register values and regions for all */ for (i = 0; i < s->ctrl->max_slaves; ++i) { - s->regs[R_SEG_ADDR0 + i] = - s->ctrl->segment_to_reg(s, &s->ctrl->segments[i]); + aspeed_smc_flash_set_segment_region(s, i, + s->ctrl->segment_to_reg(s, &s->ctrl->segments[i])); } /* HW strapping flash type for the AST2600 controllers */ @@ -935,7 +985,8 @@ static uint64_t aspeed_smc_read(void *opaque, hwaddr addr, unsigned int size) addr >>= 2; if (addr == s->r_conf || - addr == s->r_timings || + (addr >= s->r_timings && + addr < s->r_timings + s->ctrl->nregs_timings) || addr == s->r_ce_ctrl || addr == R_INTR_CTRL || addr == R_DUMMY_DATA || @@ -946,6 +997,9 @@ static uint64_t aspeed_smc_read(void *opaque, hwaddr addr, unsigned int size) (s->ctrl->has_dma && addr == R_DMA_CHECKSUM) || (addr >= R_SEG_ADDR0 && addr < R_SEG_ADDR0 + s->ctrl->max_slaves) || (addr >= s->r_ctrl0 && addr < s->r_ctrl0 + s->ctrl->max_slaves)) { + + trace_aspeed_smc_read(addr, size, s->regs[addr]); + return s->regs[addr]; } else { qemu_log_mask(LOG_UNIMP, "%s: not implemented: 0x%" HWADDR_PRIx "\n", @@ -1065,6 +1119,7 @@ static void aspeed_smc_dma_checksum(AspeedSMCState *s) __func__, s->regs[R_DMA_FLASH_ADDR]); return; } + trace_aspeed_smc_dma_checksum(s->regs[R_DMA_FLASH_ADDR], data); /* * When the DMA is on-going, the DMA registers are updated @@ -1086,6 +1141,11 @@ static void aspeed_smc_dma_rw(AspeedSMCState *s) MemTxResult result; uint32_t data; + trace_aspeed_smc_dma_rw(s->regs[R_DMA_CTRL] & DMA_CTRL_WRITE ? + "write" : "read", + s->regs[R_DMA_FLASH_ADDR], + s->regs[R_DMA_DRAM_ADDR], + s->regs[R_DMA_LEN]); while (s->regs[R_DMA_LEN]) { if (s->regs[R_DMA_CTRL] & DMA_CTRL_WRITE) { data = address_space_ldl_le(&s->dram_as, s->regs[R_DMA_DRAM_ADDR], @@ -1199,14 +1259,16 @@ static void aspeed_smc_write(void *opaque, hwaddr addr, uint64_t data, addr >>= 2; + trace_aspeed_smc_write(addr, size, data); + if (addr == s->r_conf || - addr == s->r_timings || + (addr >= s->r_timings && + addr < s->r_timings + s->ctrl->nregs_timings) || addr == s->r_ce_ctrl) { s->regs[addr] = value; } else if (addr >= s->r_ctrl0 && addr < s->r_ctrl0 + s->num_cs) { int cs = addr - s->r_ctrl0; - s->regs[addr] = value; - aspeed_smc_flash_update_cs(&s->flashes[cs]); + aspeed_smc_flash_update_ctrl(&s->flashes[cs], value); } else if (addr >= R_SEG_ADDR0 && addr < R_SEG_ADDR0 + s->ctrl->max_slaves) { int cs = addr - R_SEG_ADDR0; @@ -1374,7 +1436,7 @@ static void aspeed_smc_class_init(ObjectClass *klass, void *data) dc->realize = aspeed_smc_realize; dc->reset = aspeed_smc_reset; - dc->props = aspeed_smc_properties; + device_class_set_props(dc, aspeed_smc_properties); dc->vmsd = &vmstate_aspeed_smc; mc->ctrl = data; } diff --git a/hw/ssi/trace-events b/hw/ssi/trace-events new file mode 100644 index 0000000000..0ea498de91 --- /dev/null +++ b/hw/ssi/trace-events @@ -0,0 +1,11 @@ +# aspeed_smc.c + +aspeed_smc_flash_set_segment(int cs, uint64_t reg, uint64_t start, uint64_t end) "CS%d segreg=0x%"PRIx64" [ 0x%"PRIx64" - 0x%"PRIx64" ]" +aspeed_smc_flash_read(int cs, uint64_t addr, uint32_t size, uint64_t data, int mode) "CS%d @0x%" PRIx64 " size %u: 0x%" PRIx64" mode:%d" +aspeed_smc_do_snoop(int cs, int index, int dummies, int data) "CS%d index:0x%x dummies:%d data:0x%x" +aspeed_smc_flash_write(int cs, uint64_t addr, uint32_t size, uint64_t data, int mode) "CS%d @0x%" PRIx64 " size %u: 0x%" PRIx64" mode:%d" +aspeed_smc_read(uint64_t addr, uint32_t size, uint64_t data) "@0x%" PRIx64 " size %u: 0x%" PRIx64 +aspeed_smc_dma_checksum(uint32_t addr, uint32_t data) "0x%08x: 0x%08x" +aspeed_smc_dma_rw(const char *dir, uint32_t flash_addr, uint32_t dram_addr, uint32_t size) "%s flash:@0x%08x dram:@0x%08x size:0x%08x" +aspeed_smc_write(uint64_t addr, uint32_t size, uint64_t data) "@0x%" PRIx64 " size %u: 0x%" PRIx64 +aspeed_smc_flash_select(int cs, const char *prefix) "CS%d %sselect" diff --git a/hw/ssi/xilinx_spi.c b/hw/ssi/xilinx_spi.c index a1be0de039..eba7ccd46a 100644 --- a/hw/ssi/xilinx_spi.c +++ b/hw/ssi/xilinx_spi.c @@ -372,7 +372,7 @@ static void xilinx_spi_class_init(ObjectClass *klass, void *data) dc->realize = xilinx_spi_realize; dc->reset = xlx_spi_reset; - dc->props = xilinx_spi_properties; + device_class_set_props(dc, xilinx_spi_properties); dc->vmsd = &vmstate_xilinx_spi; } diff --git a/hw/ssi/xilinx_spips.c b/hw/ssi/xilinx_spips.c index 0d6c2e1a61..c57850a505 100644 --- a/hw/ssi/xilinx_spips.c +++ b/hw/ssi/xilinx_spips.c @@ -576,11 +576,11 @@ static int xilinx_spips_num_dummies(XilinxQSPIPS *qs, uint8_t command) case FAST_READ: case DOR: case QOR: + case FAST_READ_4: case DOR_4: case QOR_4: return 1; case DIOR: - case FAST_READ_4: case DIOR_4: return 2; case QIOR: @@ -1455,7 +1455,7 @@ static void xilinx_spips_class_init(ObjectClass *klass, void *data) dc->realize = xilinx_spips_realize; dc->reset = xilinx_spips_reset; - dc->props = xilinx_spips_properties; + device_class_set_props(dc, xilinx_spips_properties); dc->vmsd = &vmstate_xilinx_spips; xsc->reg_ops = &spips_ops; @@ -1471,7 +1471,7 @@ static void xlnx_zynqmp_qspips_class_init(ObjectClass *klass, void * data) dc->realize = xlnx_zynqmp_qspips_realize; dc->reset = xlnx_zynqmp_qspips_reset; dc->vmsd = &vmstate_xlnx_zynqmp_qspips; - dc->props = xilinx_zynqmp_qspips_properties; + device_class_set_props(dc, xilinx_zynqmp_qspips_properties); xsc->reg_ops = &xlnx_zynqmp_qspips_ops; xsc->rx_fifo_size = RXFF_A_Q; xsc->tx_fifo_size = TXFF_A_Q; diff --git a/hw/timer/Kconfig b/hw/timer/Kconfig index a990f9fe35..59b3f44d69 100644 --- a/hw/timer/Kconfig +++ b/hw/timer/Kconfig @@ -15,6 +15,7 @@ config HPET config I8254 bool + depends on ISA_BUS config ALTERA_TIMER bool diff --git a/hw/timer/a9gtimer.c b/hw/timer/a9gtimer.c index 4c634c83b1..7233068a37 100644 --- a/hw/timer/a9gtimer.c +++ b/hw/timer/a9gtimer.c @@ -359,7 +359,7 @@ static void a9_gtimer_class_init(ObjectClass *klass, void *data) dc->realize = a9_gtimer_realize; dc->vmsd = &vmstate_a9_gtimer; dc->reset = a9_gtimer_reset; - dc->props = a9_gtimer_properties; + device_class_set_props(dc, a9_gtimer_properties); } static const TypeInfo a9_gtimer_info = { diff --git a/hw/timer/allwinner-a10-pit.c b/hw/timer/allwinner-a10-pit.c index aae880f5b3..f84fc0ea25 100644 --- a/hw/timer/allwinner-a10-pit.c +++ b/hw/timer/allwinner-a10-pit.c @@ -284,7 +284,7 @@ static void a10_pit_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->reset = a10_pit_reset; - dc->props = a10_pit_properties; + device_class_set_props(dc, a10_pit_properties); dc->desc = "allwinner a10 timer"; dc->vmsd = &vmstate_a10_pit; } diff --git a/hw/timer/altera_timer.c b/hw/timer/altera_timer.c index 79fc381252..be81b7a518 100644 --- a/hw/timer/altera_timer.c +++ b/hw/timer/altera_timer.c @@ -224,7 +224,7 @@ static void altera_timer_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = altera_timer_realize; - dc->props = altera_timer_properties; + device_class_set_props(dc, altera_timer_properties); dc->reset = altera_timer_reset; } diff --git a/hw/timer/arm_mptimer.c b/hw/timer/arm_mptimer.c index 2bf11f788c..cdfca3000b 100644 --- a/hw/timer/arm_mptimer.c +++ b/hw/timer/arm_mptimer.c @@ -312,7 +312,7 @@ static void arm_mptimer_class_init(ObjectClass *klass, void *data) dc->realize = arm_mptimer_realize; dc->vmsd = &vmstate_arm_mptimer; dc->reset = arm_mptimer_reset; - dc->props = arm_mptimer_properties; + device_class_set_props(dc, arm_mptimer_properties); } static const TypeInfo arm_mptimer_info = { diff --git a/hw/timer/arm_timer.c b/hw/timer/arm_timer.c index af524fabf7..9607366d78 100644 --- a/hw/timer/arm_timer.c +++ b/hw/timer/arm_timer.c @@ -180,7 +180,7 @@ static arm_timer_state *arm_timer_init(uint32_t freq) s->control = TIMER_CTRL_IE; s->timer = ptimer_init(arm_timer_tick, s, PTIMER_POLICY_DEFAULT); - vmstate_register(NULL, -1, &vmstate_arm_timer, s); + vmstate_register(NULL, VMSTATE_INSTANCE_ID_ANY, &vmstate_arm_timer, s); return s; } @@ -397,7 +397,7 @@ static void sp804_class_init(ObjectClass *klass, void *data) DeviceClass *k = DEVICE_CLASS(klass); k->realize = sp804_realize; - k->props = sp804_properties; + device_class_set_props(k, sp804_properties); k->vmsd = &vmstate_sp804; } 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/aspeed_timer.c b/hw/timer/aspeed_timer.c index bcce2192a9..42c47d2ce6 100644 --- a/hw/timer/aspeed_timer.c +++ b/hw/timer/aspeed_timer.c @@ -19,6 +19,7 @@ #include "qemu/timer.h" #include "qemu/log.h" #include "qemu/module.h" +#include "hw/qdev-properties.h" #include "trace.h" #define TIMER_NR_REGS 4 @@ -282,7 +283,7 @@ static void aspeed_timer_set_value(AspeedTimerCtrlState *s, int timer, int reg, if (old_reload || !t->reload) { break; } - + /* fall through to re-enable */ case TIMER_REG_STATUS: if (timer_enabled(t)) { uint64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); @@ -603,15 +604,8 @@ static void aspeed_timer_realize(DeviceState *dev, Error **errp) int i; SysBusDevice *sbd = SYS_BUS_DEVICE(dev); AspeedTimerCtrlState *s = ASPEED_TIMER(dev); - Object *obj; - Error *err = NULL; - obj = object_property_get_link(OBJECT(dev), "scu", &err); - if (!obj) { - error_propagate_prepend(errp, err, "required link 'scu' not found: "); - return; - } - s->scu = ASPEED_SCU(obj); + assert(s->scu); for (i = 0; i < ASPEED_TIMER_NR_TIMERS; i++) { aspeed_init_one_timer(s, i); @@ -677,6 +671,12 @@ static const VMStateDescription vmstate_aspeed_timer_state = { } }; +static Property aspeed_timer_properties[] = { + DEFINE_PROP_LINK("scu", AspeedTimerCtrlState, scu, TYPE_ASPEED_SCU, + AspeedSCUState *), + DEFINE_PROP_END_OF_LIST(), +}; + static void timer_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -685,6 +685,7 @@ static void timer_class_init(ObjectClass *klass, void *data) dc->reset = aspeed_timer_reset; dc->desc = "ASPEED Timer"; dc->vmsd = &vmstate_aspeed_timer_state; + device_class_set_props(dc, aspeed_timer_properties); } static const TypeInfo aspeed_timer_info = { diff --git a/hw/timer/cadence_ttc.c b/hw/timer/cadence_ttc.c index 5e3128c1e3..b0ba6b2bba 100644 --- a/hw/timer/cadence_ttc.c +++ b/hw/timer/cadence_ttc.c @@ -412,18 +412,23 @@ static void cadence_timer_init(uint32_t freq, CadenceTimerState *s) static void cadence_ttc_init(Object *obj) { CadenceTTCState *s = CADENCE_TTC(obj); - int i; - - for (i = 0; i < 3; ++i) { - cadence_timer_init(133000000, &s->timer[i]); - sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->timer[i].irq); - } memory_region_init_io(&s->iomem, obj, &cadence_ttc_ops, s, "timer", 0x1000); sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem); } +static void cadence_ttc_realize(DeviceState *dev, Error **errp) +{ + CadenceTTCState *s = CADENCE_TTC(dev); + int i; + + for (i = 0; i < 3; ++i) { + cadence_timer_init(133000000, &s->timer[i]); + sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->timer[i].irq); + } +} + static int cadence_timer_pre_save(void *opaque) { cadence_timer_sync((CadenceTimerState *)opaque); @@ -479,6 +484,7 @@ static void cadence_ttc_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->vmsd = &vmstate_cadence_ttc; + dc->realize = cadence_ttc_realize; } static const TypeInfo cadence_ttc_info = { diff --git a/hw/timer/cmsdk-apb-dualtimer.c b/hw/timer/cmsdk-apb-dualtimer.c index e28ba9c90a..f6534241b9 100644 --- a/hw/timer/cmsdk-apb-dualtimer.c +++ b/hw/timer/cmsdk-apb-dualtimer.c @@ -510,7 +510,7 @@ static void cmsdk_apb_dualtimer_class_init(ObjectClass *klass, void *data) dc->realize = cmsdk_apb_dualtimer_realize; dc->vmsd = &cmsdk_apb_dualtimer_vmstate; dc->reset = cmsdk_apb_dualtimer_reset; - dc->props = cmsdk_apb_dualtimer_properties; + device_class_set_props(dc, cmsdk_apb_dualtimer_properties); } static const TypeInfo cmsdk_apb_dualtimer_info = { diff --git a/hw/timer/cmsdk-apb-timer.c b/hw/timer/cmsdk-apb-timer.c index 40728e85e2..f85f1309f3 100644 --- a/hw/timer/cmsdk-apb-timer.c +++ b/hw/timer/cmsdk-apb-timer.c @@ -260,7 +260,7 @@ static void cmsdk_apb_timer_class_init(ObjectClass *klass, void *data) dc->realize = cmsdk_apb_timer_realize; dc->vmsd = &cmsdk_apb_timer_vmstate; dc->reset = cmsdk_apb_timer_reset; - dc->props = cmsdk_apb_timer_properties; + device_class_set_props(dc, cmsdk_apb_timer_properties); } static const TypeInfo cmsdk_apb_timer_info = { diff --git a/hw/timer/grlib_gptimer.c b/hw/timer/grlib_gptimer.c index 7a9371c0e3..eff0ee3491 100644 --- a/hw/timer/grlib_gptimer.c +++ b/hw/timer/grlib_gptimer.c @@ -415,7 +415,7 @@ static void grlib_gptimer_class_init(ObjectClass *klass, void *data) dc->realize = grlib_gptimer_realize; dc->reset = grlib_gptimer_reset; - dc->props = grlib_gptimer_properties; + device_class_set_props(dc, grlib_gptimer_properties); } static const TypeInfo grlib_gptimer_info = { diff --git a/hw/timer/hpet.c b/hw/timer/hpet.c index 9f17aaa278..380acfa7c8 100644 --- a/hw/timer/hpet.c +++ b/hw/timer/hpet.c @@ -27,7 +27,6 @@ #include "qemu/osdep.h" #include "hw/i386/pc.h" #include "hw/irq.h" -#include "ui/console.h" #include "qapi/error.h" #include "qemu/error-report.h" #include "qemu/timer.h" @@ -37,6 +36,7 @@ #include "hw/rtc/mc146818rtc_regs.h" #include "migration/vmstate.h" #include "hw/timer/i8254.h" +#include "exec/address-spaces.h" //#define HPET_DEBUG #ifdef HPET_DEBUG @@ -800,7 +800,7 @@ static void hpet_device_class_init(ObjectClass *klass, void *data) dc->realize = hpet_realize; dc->reset = hpet_reset; dc->vmsd = &vmstate_hpet; - dc->props = hpet_device_properties; + device_class_set_props(dc, hpet_device_properties); } static const TypeInfo hpet_device_info = { diff --git a/hw/timer/i8254.c b/hw/timer/i8254.c index a4be7330ef..29f62e5356 100644 --- a/hw/timer/i8254.c +++ b/hw/timer/i8254.c @@ -364,7 +364,7 @@ static void pit_class_initfn(ObjectClass *klass, void *data) k->get_channel_info = pit_get_channel_info_common; k->post_load = pit_post_load; dc->reset = pit_reset; - dc->props = pit_properties; + device_class_set_props(dc, pit_properties); } static const TypeInfo pit_info = { diff --git a/hw/timer/lm32_timer.c b/hw/timer/lm32_timer.c index 3fdecd09fe..f703f407f5 100644 --- a/hw/timer/lm32_timer.c +++ b/hw/timer/lm32_timer.c @@ -230,7 +230,7 @@ static void lm32_timer_class_init(ObjectClass *klass, void *data) dc->realize = lm32_timer_realize; dc->reset = timer_reset; dc->vmsd = &vmstate_lm32_timer; - dc->props = lm32_timer_properties; + device_class_set_props(dc, lm32_timer_properties); } static const TypeInfo lm32_timer_info = { diff --git a/hw/timer/milkymist-sysctl.c b/hw/timer/milkymist-sysctl.c index 7a62e212c3..94389820b2 100644 --- a/hw/timer/milkymist-sysctl.c +++ b/hw/timer/milkymist-sysctl.c @@ -343,7 +343,7 @@ static void milkymist_sysctl_class_init(ObjectClass *klass, void *data) dc->realize = milkymist_sysctl_realize; dc->reset = milkymist_sysctl_reset; dc->vmsd = &vmstate_milkymist_sysctl; - dc->props = milkymist_sysctl_properties; + device_class_set_props(dc, milkymist_sysctl_properties); } static const TypeInfo milkymist_sysctl_info = { diff --git a/hw/timer/mss-timer.c b/hw/timer/mss-timer.c index b1c9a80501..29943fd744 100644 --- a/hw/timer/mss-timer.c +++ b/hw/timer/mss-timer.c @@ -278,7 +278,7 @@ static void mss_timer_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->props = mss_timer_properties; + device_class_set_props(dc, mss_timer_properties); dc->vmsd = &vmstate_mss_timer; } diff --git a/hw/timer/pxa2xx_timer.c b/hw/timer/pxa2xx_timer.c index 311cd38aac..cd172cc1e9 100644 --- a/hw/timer/pxa2xx_timer.c +++ b/hw/timer/pxa2xx_timer.c @@ -562,7 +562,7 @@ static void pxa25x_timer_dev_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->desc = "PXA25x timer"; - dc->props = pxa25x_timer_dev_properties; + device_class_set_props(dc, pxa25x_timer_dev_properties); } static const TypeInfo pxa25x_timer_dev_info = { @@ -584,7 +584,7 @@ static void pxa27x_timer_dev_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->desc = "PXA27x timer"; - dc->props = pxa27x_timer_dev_properties; + device_class_set_props(dc, pxa27x_timer_dev_properties); } static const TypeInfo pxa27x_timer_dev_info = { diff --git a/hw/timer/slavio_timer.c b/hw/timer/slavio_timer.c index c55e8d0bf4..4c5d65e391 100644 --- a/hw/timer/slavio_timer.c +++ b/hw/timer/slavio_timer.c @@ -427,7 +427,7 @@ static void slavio_timer_class_init(ObjectClass *klass, void *data) dc->reset = slavio_timer_reset; dc->vmsd = &vmstate_slavio_timer; - dc->props = slavio_timer_properties; + device_class_set_props(dc, slavio_timer_properties); } static const TypeInfo slavio_timer_info = { diff --git a/hw/timer/stm32f2xx_timer.c b/hw/timer/stm32f2xx_timer.c index edc557a4b9..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); } @@ -323,8 +327,9 @@ static void stm32f2xx_timer_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->reset = stm32f2xx_timer_reset; - dc->props = stm32f2xx_timer_properties; + 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/timer/xilinx_timer.c b/hw/timer/xilinx_timer.c index 7191ea54f5..0190aa47d0 100644 --- a/hw/timer/xilinx_timer.c +++ b/hw/timer/xilinx_timer.c @@ -253,7 +253,7 @@ static void xilinx_timer_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = xilinx_timer_realize; - dc->props = xilinx_timer_properties; + device_class_set_props(dc, xilinx_timer_properties); } static const TypeInfo xilinx_timer_info = { diff --git a/hw/tpm/Kconfig b/hw/tpm/Kconfig index 4c8ee87d67..4794e7fe28 100644 --- a/hw/tpm/Kconfig +++ b/hw/tpm/Kconfig @@ -2,9 +2,19 @@ config TPMDEV bool depends on TPM -config TPM_TIS +config TPM_TIS_ISA bool depends on TPM && ISA_BUS + select TPM_TIS + +config TPM_TIS_SYSBUS + bool + depends on TPM + select TPM_TIS + +config TPM_TIS + bool + depends on TPM select TPMDEV config TPM_CRB @@ -22,3 +32,9 @@ config TPM_EMULATOR bool default y depends on TPMDEV + +config TPM_SPAPR + bool + default y + depends on TPM && PSERIES + select TPMDEV diff --git a/hw/tpm/Makefile.objs b/hw/tpm/Makefile.objs index de0b85d02a..f1ec4beb95 100644 --- a/hw/tpm/Makefile.objs +++ b/hw/tpm/Makefile.objs @@ -1,6 +1,9 @@ common-obj-$(CONFIG_TPM) += tpm_util.o obj-$(call lor,$(CONFIG_TPM_TIS),$(CONFIG_TPM_CRB)) += tpm_ppi.o -common-obj-$(CONFIG_TPM_TIS) += tpm_tis.o +common-obj-$(CONFIG_TPM_TIS_ISA) += tpm_tis_isa.o +common-obj-$(CONFIG_TPM_TIS_SYSBUS) += tpm_tis_sysbus.o +common-obj-$(CONFIG_TPM_TIS) += tpm_tis_common.o common-obj-$(CONFIG_TPM_CRB) += tpm_crb.o common-obj-$(CONFIG_TPM_PASSTHROUGH) += tpm_passthrough.o common-obj-$(CONFIG_TPM_EMULATOR) += tpm_emulator.o +obj-$(CONFIG_TPM_SPAPR) += tpm_spapr.o diff --git a/hw/tpm/tpm_crb.c b/hw/tpm/tpm_crb.c index faae733120..cd004e7f8e 100644 --- a/hw/tpm/tpm_crb.c +++ b/hw/tpm/tpm_crb.c @@ -313,7 +313,7 @@ static void tpm_crb_class_init(ObjectClass *klass, void *data) TPMIfClass *tc = TPM_IF_CLASS(klass); dc->realize = tpm_crb_realize; - dc->props = tpm_crb_properties; + device_class_set_props(dc, tpm_crb_properties); dc->vmsd = &vmstate_tpm_crb; dc->user_creatable = true; tc->model = TPM_MODEL_TPM_CRB; diff --git a/hw/tpm/tpm_emulator.c b/hw/tpm/tpm_emulator.c index 22f9113432..3a0fc442f3 100644 --- a/hw/tpm/tpm_emulator.c +++ b/hw/tpm/tpm_emulator.c @@ -155,7 +155,7 @@ static int tpm_emulator_unix_tx_bufs(TPMEmulator *tpm_emu, const uint8_t *in, uint32_t in_len, uint8_t *out, uint32_t out_len, bool *selftest_done, - Error **err) + Error **errp) { ssize_t ret; bool is_selftest = false; @@ -165,20 +165,20 @@ static int tpm_emulator_unix_tx_bufs(TPMEmulator *tpm_emu, is_selftest = tpm_util_is_selftest(in, in_len); } - ret = qio_channel_write_all(tpm_emu->data_ioc, (char *)in, in_len, err); + ret = qio_channel_write_all(tpm_emu->data_ioc, (char *)in, in_len, errp); if (ret != 0) { return -1; } ret = qio_channel_read_all(tpm_emu->data_ioc, (char *)out, - sizeof(struct tpm_resp_hdr), err); + sizeof(struct tpm_resp_hdr), errp); if (ret != 0) { return -1; } ret = qio_channel_read_all(tpm_emu->data_ioc, (char *)out + sizeof(struct tpm_resp_hdr), - tpm_cmd_get_size(out) - sizeof(struct tpm_resp_hdr), err); + tpm_cmd_get_size(out) - sizeof(struct tpm_resp_hdr), errp); if (ret != 0) { return -1; } @@ -914,7 +914,8 @@ static void tpm_emulator_inst_init(Object *obj) tpm_emu->cur_locty_number = ~0; qemu_mutex_init(&tpm_emu->mutex); - vmstate_register(NULL, -1, &vmstate_tpm_emulator, obj); + vmstate_register(NULL, VMSTATE_INSTANCE_ID_ANY, + &vmstate_tpm_emulator, obj); } /* diff --git a/hw/tpm/tpm_ppi.c b/hw/tpm/tpm_ppi.c index ff314592b4..6d9c1a3e40 100644 --- a/hw/tpm/tpm_ppi.c +++ b/hw/tpm/tpm_ppi.c @@ -43,7 +43,8 @@ void tpm_ppi_reset(TPMPPI *tpmppi) void tpm_ppi_init(TPMPPI *tpmppi, struct MemoryRegion *m, hwaddr addr, Object *obj) { - tpmppi->buf = g_malloc0(HOST_PAGE_ALIGN(TPM_PPI_ADDR_SIZE)); + tpmppi->buf = qemu_memalign(qemu_real_host_page_size, + HOST_PAGE_ALIGN(TPM_PPI_ADDR_SIZE)); memory_region_init_ram_device_ptr(&tpmppi->ram, obj, "tpm-ppi", TPM_PPI_ADDR_SIZE, tpmppi->buf); vmstate_register_ram(&tpmppi->ram, DEVICE(obj)); diff --git a/hw/tpm/tpm_spapr.c b/hw/tpm/tpm_spapr.c new file mode 100644 index 0000000000..ce65eb2e45 --- /dev/null +++ b/hw/tpm/tpm_spapr.c @@ -0,0 +1,429 @@ +/* + * QEMU PowerPC pSeries Logical Partition (aka sPAPR) hardware System Emulator + * + * PAPR Virtual TPM + * + * Copyright (c) 2015, 2017, 2019 IBM Corporation. + * + * Authors: + * Stefan Berger + * + * This code is licensed under the GPL version 2 or later. See the + * COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "qapi/error.h" +#include "hw/qdev-properties.h" +#include "migration/vmstate.h" + +#include "sysemu/tpm_backend.h" +#include "tpm_int.h" +#include "tpm_util.h" + +#include "hw/ppc/spapr.h" +#include "hw/ppc/spapr_vio.h" +#include "trace.h" + +#define DEBUG_SPAPR 0 + +#define VIO_SPAPR_VTPM(obj) \ + OBJECT_CHECK(SpaprTpmState, (obj), TYPE_TPM_SPAPR) + +typedef struct TpmCrq { + uint8_t valid; /* 0x80: cmd; 0xc0: init crq */ + /* 0x81-0x83: CRQ message response */ + uint8_t msg; /* see below */ + uint16_t len; /* len of TPM request; len of TPM response */ + uint32_t data; /* rtce_dma_handle when sending TPM request */ + uint64_t reserved; +} TpmCrq; + +#define SPAPR_VTPM_VALID_INIT_CRQ_COMMAND 0xC0 +#define SPAPR_VTPM_VALID_COMMAND 0x80 +#define SPAPR_VTPM_MSG_RESULT 0x80 + +/* msg types for valid = SPAPR_VTPM_VALID_INIT_CRQ */ +#define SPAPR_VTPM_INIT_CRQ_RESULT 0x1 +#define SPAPR_VTPM_INIT_CRQ_COMPLETE_RESULT 0x2 + +/* msg types for valid = SPAPR_VTPM_VALID_CMD */ +#define SPAPR_VTPM_GET_VERSION 0x1 +#define SPAPR_VTPM_TPM_COMMAND 0x2 +#define SPAPR_VTPM_GET_RTCE_BUFFER_SIZE 0x3 +#define SPAPR_VTPM_PREPARE_TO_SUSPEND 0x4 + +/* response error messages */ +#define SPAPR_VTPM_VTPM_ERROR 0xff + +/* error codes */ +#define SPAPR_VTPM_ERR_COPY_IN_FAILED 0x3 +#define SPAPR_VTPM_ERR_COPY_OUT_FAILED 0x4 + +#define TPM_SPAPR_BUFFER_MAX 4096 + +typedef struct { + SpaprVioDevice vdev; + + TpmCrq crq; /* track single TPM command */ + + uint8_t state; +#define SPAPR_VTPM_STATE_NONE 0 +#define SPAPR_VTPM_STATE_EXECUTION 1 +#define SPAPR_VTPM_STATE_COMPLETION 2 + + unsigned char *buffer; + + uint32_t numbytes; /* number of bytes to deliver on resume */ + + TPMBackendCmd cmd; + + TPMBackend *be_driver; + TPMVersion be_tpm_version; + + size_t be_buffer_size; +} SpaprTpmState; + +/* + * Send a request to the TPM. + */ +static void tpm_spapr_tpm_send(SpaprTpmState *s) +{ + if (trace_event_get_state_backends(TRACE_TPM_SPAPR_SHOW_BUFFER)) { + tpm_util_show_buffer(s->buffer, s->be_buffer_size, "To TPM"); + } + + s->state = SPAPR_VTPM_STATE_EXECUTION; + s->cmd = (TPMBackendCmd) { + .locty = 0, + .in = s->buffer, + .in_len = MIN(tpm_cmd_get_size(s->buffer), s->be_buffer_size), + .out = s->buffer, + .out_len = s->be_buffer_size, + }; + + tpm_backend_deliver_request(s->be_driver, &s->cmd); +} + +static int tpm_spapr_process_cmd(SpaprTpmState *s, uint64_t dataptr) +{ + long rc; + + /* a max. of be_buffer_size bytes can be transported */ + rc = spapr_vio_dma_read(&s->vdev, dataptr, + s->buffer, s->be_buffer_size); + if (rc) { + error_report("tpm_spapr_got_payload: DMA read failure"); + } + /* let vTPM handle any malformed request */ + tpm_spapr_tpm_send(s); + + return rc; +} + +static inline int spapr_tpm_send_crq(struct SpaprVioDevice *dev, TpmCrq *crq) +{ + return spapr_vio_send_crq(dev, (uint8_t *)crq); +} + +static int tpm_spapr_do_crq(struct SpaprVioDevice *dev, uint8_t *crq_data) +{ + SpaprTpmState *s = VIO_SPAPR_VTPM(dev); + TpmCrq local_crq; + TpmCrq *crq = &s->crq; /* requests only */ + int rc; + uint8_t valid = crq_data[0]; + uint8_t msg = crq_data[1]; + + trace_tpm_spapr_do_crq(valid, msg); + + switch (valid) { + case SPAPR_VTPM_VALID_INIT_CRQ_COMMAND: /* Init command/response */ + + /* Respond to initialization request */ + switch (msg) { + case SPAPR_VTPM_INIT_CRQ_RESULT: + trace_tpm_spapr_do_crq_crq_result(); + memset(&local_crq, 0, sizeof(local_crq)); + local_crq.valid = SPAPR_VTPM_VALID_INIT_CRQ_COMMAND; + local_crq.msg = SPAPR_VTPM_INIT_CRQ_RESULT; + spapr_tpm_send_crq(dev, &local_crq); + break; + + case SPAPR_VTPM_INIT_CRQ_COMPLETE_RESULT: + trace_tpm_spapr_do_crq_crq_complete_result(); + memset(&local_crq, 0, sizeof(local_crq)); + local_crq.valid = SPAPR_VTPM_VALID_INIT_CRQ_COMMAND; + local_crq.msg = SPAPR_VTPM_INIT_CRQ_COMPLETE_RESULT; + spapr_tpm_send_crq(dev, &local_crq); + break; + } + + break; + case SPAPR_VTPM_VALID_COMMAND: /* Payloads */ + switch (msg) { + case SPAPR_VTPM_TPM_COMMAND: + trace_tpm_spapr_do_crq_tpm_command(); + if (s->state == SPAPR_VTPM_STATE_EXECUTION) { + return H_BUSY; + } + memcpy(crq, crq_data, sizeof(*crq)); + + rc = tpm_spapr_process_cmd(s, be32_to_cpu(crq->data)); + + if (rc == H_SUCCESS) { + crq->valid = be16_to_cpu(0); + } else { + local_crq.valid = SPAPR_VTPM_MSG_RESULT; + local_crq.msg = SPAPR_VTPM_VTPM_ERROR; + local_crq.len = cpu_to_be16(0); + local_crq.data = cpu_to_be32(SPAPR_VTPM_ERR_COPY_IN_FAILED); + spapr_tpm_send_crq(dev, &local_crq); + } + break; + + case SPAPR_VTPM_GET_RTCE_BUFFER_SIZE: + trace_tpm_spapr_do_crq_tpm_get_rtce_buffer_size(s->be_buffer_size); + local_crq.valid = SPAPR_VTPM_VALID_COMMAND; + local_crq.msg = SPAPR_VTPM_GET_RTCE_BUFFER_SIZE | + SPAPR_VTPM_MSG_RESULT; + local_crq.len = cpu_to_be16(s->be_buffer_size); + spapr_tpm_send_crq(dev, &local_crq); + break; + + case SPAPR_VTPM_GET_VERSION: + local_crq.valid = SPAPR_VTPM_VALID_COMMAND; + local_crq.msg = SPAPR_VTPM_GET_VERSION | SPAPR_VTPM_MSG_RESULT; + local_crq.len = cpu_to_be16(0); + switch (s->be_tpm_version) { + case TPM_VERSION_1_2: + local_crq.data = cpu_to_be32(1); + break; + case TPM_VERSION_2_0: + local_crq.data = cpu_to_be32(2); + break; + default: + g_assert_not_reached(); + break; + } + trace_tpm_spapr_do_crq_get_version(be32_to_cpu(local_crq.data)); + spapr_tpm_send_crq(dev, &local_crq); + break; + + case SPAPR_VTPM_PREPARE_TO_SUSPEND: + trace_tpm_spapr_do_crq_prepare_to_suspend(); + local_crq.valid = SPAPR_VTPM_VALID_COMMAND; + local_crq.msg = SPAPR_VTPM_PREPARE_TO_SUSPEND | + SPAPR_VTPM_MSG_RESULT; + spapr_tpm_send_crq(dev, &local_crq); + break; + + default: + trace_tpm_spapr_do_crq_unknown_msg_type(crq->msg); + } + break; + default: + trace_tpm_spapr_do_crq_unknown_crq(valid, msg); + }; + + return H_SUCCESS; +} + +static void tpm_spapr_request_completed(TPMIf *ti, int ret) +{ + SpaprTpmState *s = VIO_SPAPR_VTPM(ti); + TpmCrq *crq = &s->crq; + uint32_t len; + int rc; + + s->state = SPAPR_VTPM_STATE_COMPLETION; + + /* a max. of be_buffer_size bytes can be transported */ + len = MIN(tpm_cmd_get_size(s->buffer), s->be_buffer_size); + + if (runstate_check(RUN_STATE_FINISH_MIGRATE)) { + trace_tpm_spapr_caught_response(len); + /* defer delivery of response until .post_load */ + s->numbytes = len; + return; + } + + rc = spapr_vio_dma_write(&s->vdev, be32_to_cpu(crq->data), + s->buffer, len); + + if (trace_event_get_state_backends(TRACE_TPM_SPAPR_SHOW_BUFFER)) { + tpm_util_show_buffer(s->buffer, len, "From TPM"); + } + + crq->valid = SPAPR_VTPM_MSG_RESULT; + if (rc == H_SUCCESS) { + crq->msg = SPAPR_VTPM_TPM_COMMAND | SPAPR_VTPM_MSG_RESULT; + crq->len = cpu_to_be16(len); + } else { + error_report("%s: DMA write failure", __func__); + crq->msg = SPAPR_VTPM_VTPM_ERROR; + crq->len = cpu_to_be16(0); + crq->data = cpu_to_be32(SPAPR_VTPM_ERR_COPY_OUT_FAILED); + } + + rc = spapr_tpm_send_crq(&s->vdev, crq); + if (rc) { + error_report("%s: Error sending response", __func__); + } +} + +static int tpm_spapr_do_startup_tpm(SpaprTpmState *s, size_t buffersize) +{ + return tpm_backend_startup_tpm(s->be_driver, buffersize); +} + +static const char *tpm_spapr_get_dt_compatible(SpaprVioDevice *dev) +{ + SpaprTpmState *s = VIO_SPAPR_VTPM(dev); + + switch (s->be_tpm_version) { + case TPM_VERSION_1_2: + return "IBM,vtpm"; + case TPM_VERSION_2_0: + return "IBM,vtpm20"; + default: + g_assert_not_reached(); + } +} + +static void tpm_spapr_reset(SpaprVioDevice *dev) +{ + SpaprTpmState *s = VIO_SPAPR_VTPM(dev); + + s->state = SPAPR_VTPM_STATE_NONE; + s->numbytes = 0; + + s->be_tpm_version = tpm_backend_get_tpm_version(s->be_driver); + + s->be_buffer_size = MIN(tpm_backend_get_buffer_size(s->be_driver), + TPM_SPAPR_BUFFER_MAX); + + tpm_backend_reset(s->be_driver); + tpm_spapr_do_startup_tpm(s, s->be_buffer_size); +} + +static enum TPMVersion tpm_spapr_get_version(TPMIf *ti) +{ + SpaprTpmState *s = VIO_SPAPR_VTPM(ti); + + if (tpm_backend_had_startup_error(s->be_driver)) { + return TPM_VERSION_UNSPEC; + } + + return tpm_backend_get_tpm_version(s->be_driver); +} + +/* persistent state handling */ + +static int tpm_spapr_pre_save(void *opaque) +{ + SpaprTpmState *s = opaque; + + tpm_backend_finish_sync(s->be_driver); + /* + * we cannot deliver the results to the VM since DMA would touch VM memory + */ + + return 0; +} + +static int tpm_spapr_post_load(void *opaque, int version_id) +{ + SpaprTpmState *s = opaque; + + if (s->numbytes) { + trace_tpm_spapr_post_load(); + /* deliver the results to the VM via DMA */ + tpm_spapr_request_completed(TPM_IF(s), 0); + s->numbytes = 0; + } + + return 0; +} + +static const VMStateDescription vmstate_spapr_vtpm = { + .name = "tpm-spapr", + .pre_save = tpm_spapr_pre_save, + .post_load = tpm_spapr_post_load, + .fields = (VMStateField[]) { + VMSTATE_SPAPR_VIO(vdev, SpaprTpmState), + + VMSTATE_UINT8(state, SpaprTpmState), + VMSTATE_UINT32(numbytes, SpaprTpmState), + VMSTATE_VBUFFER_UINT32(buffer, SpaprTpmState, 0, NULL, numbytes), + /* remember DMA address */ + VMSTATE_UINT32(crq.data, SpaprTpmState), + VMSTATE_END_OF_LIST(), + } +}; + +static Property tpm_spapr_properties[] = { + DEFINE_SPAPR_PROPERTIES(SpaprTpmState, vdev), + DEFINE_PROP_TPMBE("tpmdev", SpaprTpmState, be_driver), + DEFINE_PROP_END_OF_LIST(), +}; + +static void tpm_spapr_realizefn(SpaprVioDevice *dev, Error **errp) +{ + SpaprTpmState *s = VIO_SPAPR_VTPM(dev); + + if (!tpm_find()) { + error_setg(errp, "at most one TPM device is permitted"); + return; + } + + dev->crq.SendFunc = tpm_spapr_do_crq; + + if (!s->be_driver) { + error_setg(errp, "'tpmdev' property is required"); + return; + } + s->buffer = g_malloc(TPM_SPAPR_BUFFER_MAX); +} + +static void tpm_spapr_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SpaprVioDeviceClass *k = VIO_SPAPR_DEVICE_CLASS(klass); + TPMIfClass *tc = TPM_IF_CLASS(klass); + + k->realize = tpm_spapr_realizefn; + k->reset = tpm_spapr_reset; + k->dt_name = "vtpm"; + k->dt_type = "IBM,vtpm"; + k->get_dt_compatible = tpm_spapr_get_dt_compatible; + k->signal_mask = 0x00000001; + set_bit(DEVICE_CATEGORY_MISC, dc->categories); + device_class_set_props(dc, tpm_spapr_properties); + k->rtce_window_size = 0x10000000; + dc->vmsd = &vmstate_spapr_vtpm; + + tc->model = TPM_MODEL_TPM_SPAPR; + tc->get_version = tpm_spapr_get_version; + tc->request_completed = tpm_spapr_request_completed; +} + +static const TypeInfo tpm_spapr_info = { + .name = TYPE_TPM_SPAPR, + .parent = TYPE_VIO_SPAPR_DEVICE, + .instance_size = sizeof(SpaprTpmState), + .class_init = tpm_spapr_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_TPM_IF }, + { } + } +}; + +static void tpm_spapr_register_types(void) +{ + type_register_static(&tpm_spapr_info); +} + +type_init(tpm_spapr_register_types) diff --git a/hw/tpm/tpm_tis.h b/hw/tpm/tpm_tis.h new file mode 100644 index 0000000000..5554989395 --- /dev/null +++ b/hw/tpm/tpm_tis.h @@ -0,0 +1,91 @@ +/* + * tpm_tis.h - QEMU's TPM TIS common header + * + * Copyright (C) 2006,2010-2013 IBM Corporation + * + * Authors: + * Stefan Berger + * David Safford + * + * Xen 4 support: Andrease Niederl + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + * Implementation of the TIS interface according to specs found at + * http://www.trustedcomputinggroup.org. This implementation currently + * supports version 1.3, 21 March 2013 + * In the developers menu choose the PC Client section then find the TIS + * specification. + * + * TPM TIS for TPM 2 implementation following TCG PC Client Platform + * TPM Profile (PTP) Specification, Familiy 2.0, Revision 00.43 + */ +#ifndef TPM_TPM_TIS_H +#define TPM_TPM_TIS_H + +#include "qemu/osdep.h" +#include "sysemu/tpm_backend.h" +#include "tpm_ppi.h" + +#define TPM_TIS_NUM_LOCALITIES 5 /* per spec */ +#define TPM_TIS_LOCALITY_SHIFT 12 +#define TPM_TIS_NO_LOCALITY 0xff + +#define TPM_TIS_IS_VALID_LOCTY(x) ((x) < TPM_TIS_NUM_LOCALITIES) + +#define TPM_TIS_BUFFER_MAX 4096 + +typedef enum { + TPM_TIS_STATE_IDLE = 0, + TPM_TIS_STATE_READY, + TPM_TIS_STATE_COMPLETION, + TPM_TIS_STATE_EXECUTION, + TPM_TIS_STATE_RECEPTION, +} TPMTISState; + +/* locality data -- all fields are persisted */ +typedef struct TPMLocality { + TPMTISState state; + uint8_t access; + uint32_t sts; + uint32_t iface_id; + uint32_t inte; + uint32_t ints; +} TPMLocality; + +typedef struct TPMState { + MemoryRegion mmio; + + unsigned char buffer[TPM_TIS_BUFFER_MAX]; + uint16_t rw_offset; + + uint8_t active_locty; + uint8_t aborting_locty; + uint8_t next_locty; + + TPMLocality loc[TPM_TIS_NUM_LOCALITIES]; + + qemu_irq irq; + uint32_t irq_num; + + TPMBackendCmd cmd; + + TPMBackend *be_driver; + TPMVersion be_tpm_version; + + size_t be_buffer_size; + + bool ppi_enabled; + TPMPPI ppi; +} TPMState; + +extern const VMStateDescription vmstate_locty; +extern const MemoryRegionOps tpm_tis_memory_ops; + +int tpm_tis_pre_save(TPMState *s); +void tpm_tis_reset(TPMState *s); +enum TPMVersion tpm_tis_get_tpm_version(TPMState *s); +void tpm_tis_request_completed(TPMState *s, int ret); + +#endif /* TPM_TPM_TIS_H */ diff --git a/hw/tpm/tpm_tis.c b/hw/tpm/tpm_tis_common.c similarity index 82% rename from hw/tpm/tpm_tis.c rename to hw/tpm/tpm_tis_common.c index 7aaf9b946d..9ce64d4836 100644 --- a/hw/tpm/tpm_tis.c +++ b/hw/tpm/tpm_tis_common.c @@ -1,5 +1,6 @@ /* - * tpm_tis.c - QEMU's TPM TIS interface emulator + * tpm_tis_common.c - QEMU's TPM TIS interface emulator + * device agnostic functions * * Copyright (C) 2006,2010-2013 IBM Corporation * @@ -21,7 +22,6 @@ * TPM TIS for TPM 2 implementation following TCG PC Client Platform * TPM Profile (PTP) Specification, Familiy 2.0, Revision 00.43 */ - #include "qemu/osdep.h" #include "hw/irq.h" #include "hw/isa/isa.h" @@ -38,60 +38,7 @@ #include "tpm_ppi.h" #include "trace.h" -#define TPM_TIS_NUM_LOCALITIES 5 /* per spec */ -#define TPM_TIS_LOCALITY_SHIFT 12 -#define TPM_TIS_NO_LOCALITY 0xff - -#define TPM_TIS_IS_VALID_LOCTY(x) ((x) < TPM_TIS_NUM_LOCALITIES) - -#define TPM_TIS_BUFFER_MAX 4096 - -typedef enum { - TPM_TIS_STATE_IDLE = 0, - TPM_TIS_STATE_READY, - TPM_TIS_STATE_COMPLETION, - TPM_TIS_STATE_EXECUTION, - TPM_TIS_STATE_RECEPTION, -} TPMTISState; - -/* locality data -- all fields are persisted */ -typedef struct TPMLocality { - TPMTISState state; - uint8_t access; - uint32_t sts; - uint32_t iface_id; - uint32_t inte; - uint32_t ints; -} TPMLocality; - -typedef struct TPMState { - ISADevice busdev; - MemoryRegion mmio; - - unsigned char buffer[TPM_TIS_BUFFER_MAX]; - uint16_t rw_offset; - - uint8_t active_locty; - uint8_t aborting_locty; - uint8_t next_locty; - - TPMLocality loc[TPM_TIS_NUM_LOCALITIES]; - - qemu_irq irq; - uint32_t irq_num; - - TPMBackendCmd cmd; - - TPMBackend *be_driver; - TPMVersion be_tpm_version; - - size_t be_buffer_size; - - bool ppi_enabled; - TPMPPI ppi; -} TPMState; - -#define TPM(obj) OBJECT_CHECK(TPMState, (obj), TYPE_TPM_TIS) +#include "tpm_tis.h" #define DEBUG_TIS 0 @@ -107,30 +54,6 @@ static uint8_t tpm_tis_locality_from_addr(hwaddr addr) return (uint8_t)((addr >> TPM_TIS_LOCALITY_SHIFT) & 0x7); } -static void tpm_tis_show_buffer(const unsigned char *buffer, - size_t buffer_size, const char *string) -{ - size_t len, i; - char *line_buffer, *p; - - len = MIN(tpm_cmd_get_size(buffer), buffer_size); - - /* - * allocate enough room for 3 chars per buffer entry plus a - * newline after every 16 chars and a final null terminator. - */ - line_buffer = g_malloc(len * 3 + (len / 16) + 1); - - for (i = 0, p = line_buffer; i < len; i++) { - if (i && !(i % 16)) { - p += sprintf(p, "\n"); - } - p += sprintf(p, "%.2X ", buffer[i]); - } - trace_tpm_tis_show_buffer(string, len, line_buffer); - - g_free(line_buffer); -} /* * Set the given flags in the STS register by clearing the register but @@ -156,8 +79,8 @@ static void tpm_tis_sts_set(TPMLocality *l, uint32_t flags) */ static void tpm_tis_tpm_send(TPMState *s, uint8_t locty) { - if (trace_event_get_state_backends(TRACE_TPM_TIS_SHOW_BUFFER)) { - tpm_tis_show_buffer(s->buffer, s->be_buffer_size, "To TPM"); + if (trace_event_get_state_backends(TRACE_TPM_UTIL_SHOW_BUFFER)) { + tpm_util_show_buffer(s->buffer, s->be_buffer_size, "To TPM"); } /* @@ -305,9 +228,8 @@ static void tpm_tis_prep_abort(TPMState *s, uint8_t locty, uint8_t newlocty) /* * Callback from the TPM to indicate that the response was received. */ -static void tpm_tis_request_completed(TPMIf *ti, int ret) +void tpm_tis_request_completed(TPMState *s, int ret) { - TPMState *s = TPM(ti); uint8_t locty = s->cmd.locty; uint8_t l; @@ -325,8 +247,8 @@ static void tpm_tis_request_completed(TPMIf *ti, int ret) s->loc[locty].state = TPM_TIS_STATE_COMPLETION; s->rw_offset = 0; - if (trace_event_get_state_backends(TRACE_TPM_TIS_SHOW_BUFFER)) { - tpm_tis_show_buffer(s->buffer, s->be_buffer_size, "From TPM"); + if (trace_event_get_state_backends(TRACE_TPM_UTIL_SHOW_BUFFER)) { + tpm_util_show_buffer(s->buffer, s->be_buffer_size, "From TPM"); } if (TPM_TIS_IS_VALID_LOCTY(s->next_locty)) { @@ -362,7 +284,7 @@ static uint32_t tpm_tis_data_read(TPMState *s, uint8_t locty) } #ifdef DEBUG_TIS -static void tpm_tis_dump_state(void *opaque, hwaddr addr) +static void tpm_tis_dump_state(TPMState *s, hwaddr addr) { static const unsigned regs[] = { TPM_TIS_REG_ACCESS, @@ -377,7 +299,6 @@ static void tpm_tis_dump_state(void *opaque, hwaddr addr) int idx; uint8_t locty = tpm_tis_locality_from_addr(addr); hwaddr base = addr & ~0xfff; - TPMState *s = opaque; printf("tpm_tis: active locality : %d\n" "tpm_tis: state of locality %d : %d\n" @@ -387,7 +308,7 @@ static void tpm_tis_dump_state(void *opaque, hwaddr addr) for (idx = 0; regs[idx] != 0xfff; idx++) { printf("tpm_tis: 0x%04x : 0x%08x\n", regs[idx], - (int)tpm_tis_mmio_read(opaque, base + regs[idx], 4)); + (int)tpm_tis_mmio_read(s, base + regs[idx], 4)); } printf("tpm_tis: r/w offset : %d\n" @@ -512,7 +433,7 @@ static uint64_t tpm_tis_mmio_read(void *opaque, hwaddr addr, break; #ifdef DEBUG_TIS case TPM_TIS_REG_DEBUG: - tpm_tis_dump_state(opaque, addr); + tpm_tis_dump_state(s, addr); break; #endif } @@ -846,7 +767,7 @@ static void tpm_tis_mmio_write(void *opaque, hwaddr addr, } } -static const MemoryRegionOps tpm_tis_memory_ops = { +const MemoryRegionOps tpm_tis_memory_ops = { .read = tpm_tis_mmio_read, .write = tpm_tis_mmio_write, .endianness = DEVICE_LITTLE_ENDIAN, @@ -859,10 +780,8 @@ static const MemoryRegionOps tpm_tis_memory_ops = { /* * Get the TPMVersion of the backend device being used */ -static enum TPMVersion tpm_tis_get_tpm_version(TPMIf *ti) +enum TPMVersion tpm_tis_get_tpm_version(TPMState *s) { - TPMState *s = TPM(ti); - if (tpm_backend_had_startup_error(s->be_driver)) { return TPM_VERSION_UNSPEC; } @@ -874,9 +793,8 @@ static enum TPMVersion tpm_tis_get_tpm_version(TPMIf *ti) * This function is called when the machine starts, resets or due to * S3 resume. */ -static void tpm_tis_reset(DeviceState *dev) +void tpm_tis_reset(TPMState *s) { - TPMState *s = TPM(dev); int c; s->be_tpm_version = tpm_backend_get_tpm_version(s->be_driver); @@ -920,15 +838,14 @@ static void tpm_tis_reset(DeviceState *dev) /* persistent state handling */ -static int tpm_tis_pre_save(void *opaque) +int tpm_tis_pre_save(TPMState *s) { - TPMState *s = opaque; uint8_t locty = s->active_locty; trace_tpm_tis_pre_save(locty, s->rw_offset); if (DEBUG_TIS) { - tpm_tis_dump_state(opaque, 0); + tpm_tis_dump_state(s, 0); } /* @@ -939,7 +856,7 @@ static int tpm_tis_pre_save(void *opaque) return 0; } -static const VMStateDescription vmstate_locty = { +const VMStateDescription vmstate_locty = { .name = "tpm-tis/locty", .version_id = 0, .fields = (VMStateField[]) { @@ -953,99 +870,3 @@ static const VMStateDescription vmstate_locty = { } }; -static const VMStateDescription vmstate_tpm_tis = { - .name = "tpm-tis", - .version_id = 0, - .pre_save = tpm_tis_pre_save, - .fields = (VMStateField[]) { - VMSTATE_BUFFER(buffer, TPMState), - VMSTATE_UINT16(rw_offset, TPMState), - VMSTATE_UINT8(active_locty, TPMState), - VMSTATE_UINT8(aborting_locty, TPMState), - VMSTATE_UINT8(next_locty, TPMState), - - VMSTATE_STRUCT_ARRAY(loc, TPMState, TPM_TIS_NUM_LOCALITIES, 0, - vmstate_locty, TPMLocality), - - VMSTATE_END_OF_LIST() - } -}; - -static Property tpm_tis_properties[] = { - DEFINE_PROP_UINT32("irq", TPMState, irq_num, TPM_TIS_IRQ), - DEFINE_PROP_TPMBE("tpmdev", TPMState, be_driver), - DEFINE_PROP_BOOL("ppi", TPMState, ppi_enabled, true), - DEFINE_PROP_END_OF_LIST(), -}; - -static void tpm_tis_realizefn(DeviceState *dev, Error **errp) -{ - TPMState *s = TPM(dev); - - if (!tpm_find()) { - error_setg(errp, "at most one TPM device is permitted"); - return; - } - - if (!s->be_driver) { - error_setg(errp, "'tpmdev' property is required"); - return; - } - if (s->irq_num > 15) { - error_setg(errp, "IRQ %d is outside valid range of 0 to 15", - s->irq_num); - return; - } - - isa_init_irq(&s->busdev, &s->irq, s->irq_num); - - memory_region_add_subregion(isa_address_space(ISA_DEVICE(dev)), - TPM_TIS_ADDR_BASE, &s->mmio); - - if (s->ppi_enabled) { - tpm_ppi_init(&s->ppi, isa_address_space(ISA_DEVICE(dev)), - TPM_PPI_ADDR_BASE, OBJECT(s)); - } -} - -static void tpm_tis_initfn(Object *obj) -{ - TPMState *s = TPM(obj); - - memory_region_init_io(&s->mmio, OBJECT(s), &tpm_tis_memory_ops, - s, "tpm-tis-mmio", - TPM_TIS_NUM_LOCALITIES << TPM_TIS_LOCALITY_SHIFT); -} - -static void tpm_tis_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - TPMIfClass *tc = TPM_IF_CLASS(klass); - - dc->realize = tpm_tis_realizefn; - dc->props = tpm_tis_properties; - dc->reset = tpm_tis_reset; - dc->vmsd = &vmstate_tpm_tis; - tc->model = TPM_MODEL_TPM_TIS; - tc->get_version = tpm_tis_get_tpm_version; - tc->request_completed = tpm_tis_request_completed; -} - -static const TypeInfo tpm_tis_info = { - .name = TYPE_TPM_TIS, - .parent = TYPE_ISA_DEVICE, - .instance_size = sizeof(TPMState), - .instance_init = tpm_tis_initfn, - .class_init = tpm_tis_class_init, - .interfaces = (InterfaceInfo[]) { - { TYPE_TPM_IF }, - { } - } -}; - -static void tpm_tis_register(void) -{ - type_register_static(&tpm_tis_info); -} - -type_init(tpm_tis_register) diff --git a/hw/tpm/tpm_tis_isa.c b/hw/tpm/tpm_tis_isa.c new file mode 100644 index 0000000000..30ba37079d --- /dev/null +++ b/hw/tpm/tpm_tis_isa.c @@ -0,0 +1,170 @@ +/* + * tpm_tis_isa.c - QEMU's TPM TIS ISA Device + * + * Copyright (C) 2006,2010-2013 IBM Corporation + * + * Authors: + * Stefan Berger + * David Safford + * + * Xen 4 support: Andrease Niederl + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + * Implementation of the TIS interface according to specs found at + * http://www.trustedcomputinggroup.org. This implementation currently + * supports version 1.3, 21 March 2013 + * In the developers menu choose the PC Client section then find the TIS + * specification. + * + * TPM TIS for TPM 2 implementation following TCG PC Client Platform + * TPM Profile (PTP) Specification, Familiy 2.0, Revision 00.43 + */ + +#include "qemu/osdep.h" +#include "hw/isa/isa.h" +#include "hw/qdev-properties.h" +#include "migration/vmstate.h" +#include "tpm_util.h" +#include "tpm_tis.h" + +typedef struct TPMStateISA { + /*< private >*/ + ISADevice parent_obj; + + /*< public >*/ + TPMState state; /* not a QOM object */ +} TPMStateISA; + +#define TPM_TIS_ISA(obj) OBJECT_CHECK(TPMStateISA, (obj), TYPE_TPM_TIS_ISA) + +static int tpm_tis_pre_save_isa(void *opaque) +{ + TPMStateISA *isadev = opaque; + + return tpm_tis_pre_save(&isadev->state); +} + +static const VMStateDescription vmstate_tpm_tis_isa = { + .name = "tpm-tis", + .version_id = 0, + .pre_save = tpm_tis_pre_save_isa, + .fields = (VMStateField[]) { + VMSTATE_BUFFER(state.buffer, TPMStateISA), + VMSTATE_UINT16(state.rw_offset, TPMStateISA), + VMSTATE_UINT8(state.active_locty, TPMStateISA), + VMSTATE_UINT8(state.aborting_locty, TPMStateISA), + VMSTATE_UINT8(state.next_locty, TPMStateISA), + + VMSTATE_STRUCT_ARRAY(state.loc, TPMStateISA, TPM_TIS_NUM_LOCALITIES, 0, + vmstate_locty, TPMLocality), + + VMSTATE_END_OF_LIST() + } +}; + +static void tpm_tis_isa_request_completed(TPMIf *ti, int ret) +{ + TPMStateISA *isadev = TPM_TIS_ISA(ti); + TPMState *s = &isadev->state; + + tpm_tis_request_completed(s, ret); +} + +static enum TPMVersion tpm_tis_isa_get_tpm_version(TPMIf *ti) +{ + TPMStateISA *isadev = TPM_TIS_ISA(ti); + TPMState *s = &isadev->state; + + return tpm_tis_get_tpm_version(s); +} + +static void tpm_tis_isa_reset(DeviceState *dev) +{ + TPMStateISA *isadev = TPM_TIS_ISA(dev); + TPMState *s = &isadev->state; + + return tpm_tis_reset(s); +} + +static Property tpm_tis_isa_properties[] = { + DEFINE_PROP_UINT32("irq", TPMStateISA, state.irq_num, TPM_TIS_IRQ), + DEFINE_PROP_TPMBE("tpmdev", TPMStateISA, state.be_driver), + DEFINE_PROP_BOOL("ppi", TPMStateISA, state.ppi_enabled, true), + DEFINE_PROP_END_OF_LIST(), +}; + +static void tpm_tis_isa_initfn(Object *obj) +{ + TPMStateISA *isadev = TPM_TIS_ISA(obj); + TPMState *s = &isadev->state; + + memory_region_init_io(&s->mmio, obj, &tpm_tis_memory_ops, + s, "tpm-tis-mmio", + TPM_TIS_NUM_LOCALITIES << TPM_TIS_LOCALITY_SHIFT); +} + +static void tpm_tis_isa_realizefn(DeviceState *dev, Error **errp) +{ + TPMStateISA *isadev = TPM_TIS_ISA(dev); + TPMState *s = &isadev->state; + + if (!tpm_find()) { + error_setg(errp, "at most one TPM device is permitted"); + return; + } + + if (!s->be_driver) { + error_setg(errp, "'tpmdev' property is required"); + return; + } + if (s->irq_num > 15) { + error_setg(errp, "IRQ %d is outside valid range of 0 to 15", + s->irq_num); + return; + } + + isa_init_irq(ISA_DEVICE(dev), &s->irq, s->irq_num); + + memory_region_add_subregion(isa_address_space(ISA_DEVICE(dev)), + TPM_TIS_ADDR_BASE, &s->mmio); + + if (s->ppi_enabled) { + tpm_ppi_init(&s->ppi, isa_address_space(ISA_DEVICE(dev)), + TPM_PPI_ADDR_BASE, OBJECT(dev)); + } +} + +static void tpm_tis_isa_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + TPMIfClass *tc = TPM_IF_CLASS(klass); + + device_class_set_props(dc, tpm_tis_isa_properties); + dc->vmsd = &vmstate_tpm_tis_isa; + tc->model = TPM_MODEL_TPM_TIS; + dc->realize = tpm_tis_isa_realizefn; + dc->reset = tpm_tis_isa_reset; + tc->request_completed = tpm_tis_isa_request_completed; + tc->get_version = tpm_tis_isa_get_tpm_version; +} + +static const TypeInfo tpm_tis_isa_info = { + .name = TYPE_TPM_TIS_ISA, + .parent = TYPE_ISA_DEVICE, + .instance_size = sizeof(TPMStateISA), + .instance_init = tpm_tis_isa_initfn, + .class_init = tpm_tis_isa_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_TPM_IF }, + { } + } +}; + +static void tpm_tis_isa_register(void) +{ + type_register_static(&tpm_tis_isa_info); +} + +type_init(tpm_tis_isa_register) diff --git a/hw/tpm/tpm_tis_sysbus.c b/hw/tpm/tpm_tis_sysbus.c new file mode 100644 index 0000000000..18c02aed67 --- /dev/null +++ b/hw/tpm/tpm_tis_sysbus.c @@ -0,0 +1,159 @@ +/* + * tpm_tis_sysbus.c - QEMU's TPM TIS SYSBUS Device + * + * Copyright (C) 2006,2010-2013 IBM Corporation + * + * Authors: + * Stefan Berger + * David Safford + * + * Xen 4 support: Andrease Niederl + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + * Implementation of the TIS interface according to specs found at + * http://www.trustedcomputinggroup.org. This implementation currently + * supports version 1.3, 21 March 2013 + * In the developers menu choose the PC Client section then find the TIS + * specification. + * + * TPM TIS for TPM 2 implementation following TCG PC Client Platform + * TPM Profile (PTP) Specification, Familiy 2.0, Revision 00.43 + */ + +#include "qemu/osdep.h" +#include "hw/qdev-properties.h" +#include "migration/vmstate.h" +#include "tpm_util.h" +#include "hw/sysbus.h" +#include "tpm_tis.h" + +typedef struct TPMStateSysBus { + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + TPMState state; /* not a QOM object */ +} TPMStateSysBus; + +#define TPM_TIS_SYSBUS(obj) OBJECT_CHECK(TPMStateSysBus, (obj), TYPE_TPM_TIS_SYSBUS) + +static int tpm_tis_pre_save_sysbus(void *opaque) +{ + TPMStateSysBus *sbdev = opaque; + + return tpm_tis_pre_save(&sbdev->state); +} + +static const VMStateDescription vmstate_tpm_tis_sysbus = { + .name = "tpm-tis", + .version_id = 0, + .pre_save = tpm_tis_pre_save_sysbus, + .fields = (VMStateField[]) { + VMSTATE_BUFFER(state.buffer, TPMStateSysBus), + VMSTATE_UINT16(state.rw_offset, TPMStateSysBus), + VMSTATE_UINT8(state.active_locty, TPMStateSysBus), + VMSTATE_UINT8(state.aborting_locty, TPMStateSysBus), + VMSTATE_UINT8(state.next_locty, TPMStateSysBus), + + VMSTATE_STRUCT_ARRAY(state.loc, TPMStateSysBus, TPM_TIS_NUM_LOCALITIES, + 0, vmstate_locty, TPMLocality), + + VMSTATE_END_OF_LIST() + } +}; + +static void tpm_tis_sysbus_request_completed(TPMIf *ti, int ret) +{ + TPMStateSysBus *sbdev = TPM_TIS_SYSBUS(ti); + TPMState *s = &sbdev->state; + + tpm_tis_request_completed(s, ret); +} + +static enum TPMVersion tpm_tis_sysbus_get_tpm_version(TPMIf *ti) +{ + TPMStateSysBus *sbdev = TPM_TIS_SYSBUS(ti); + TPMState *s = &sbdev->state; + + return tpm_tis_get_tpm_version(s); +} + +static void tpm_tis_sysbus_reset(DeviceState *dev) +{ + TPMStateSysBus *sbdev = TPM_TIS_SYSBUS(dev); + TPMState *s = &sbdev->state; + + return tpm_tis_reset(s); +} + +static Property tpm_tis_sysbus_properties[] = { + DEFINE_PROP_UINT32("irq", TPMStateSysBus, state.irq_num, TPM_TIS_IRQ), + DEFINE_PROP_TPMBE("tpmdev", TPMStateSysBus, state.be_driver), + DEFINE_PROP_BOOL("ppi", TPMStateSysBus, state.ppi_enabled, true), + DEFINE_PROP_END_OF_LIST(), +}; + +static void tpm_tis_sysbus_initfn(Object *obj) +{ + TPMStateSysBus *sbdev = TPM_TIS_SYSBUS(obj); + TPMState *s = &sbdev->state; + + memory_region_init_io(&s->mmio, obj, &tpm_tis_memory_ops, + s, "tpm-tis-mmio", + TPM_TIS_NUM_LOCALITIES << TPM_TIS_LOCALITY_SHIFT); + + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); + sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq); +} + +static void tpm_tis_sysbus_realizefn(DeviceState *dev, Error **errp) +{ + TPMStateSysBus *sbdev = TPM_TIS_SYSBUS(dev); + TPMState *s = &sbdev->state; + + if (!tpm_find()) { + error_setg(errp, "at most one TPM device is permitted"); + return; + } + + if (!s->be_driver) { + error_setg(errp, "'tpmdev' property is required"); + return; + } +} + +static void tpm_tis_sysbus_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + TPMIfClass *tc = TPM_IF_CLASS(klass); + + device_class_set_props(dc, tpm_tis_sysbus_properties); + dc->vmsd = &vmstate_tpm_tis_sysbus; + tc->model = TPM_MODEL_TPM_TIS; + dc->realize = tpm_tis_sysbus_realizefn; + dc->user_creatable = true; + dc->reset = tpm_tis_sysbus_reset; + tc->request_completed = tpm_tis_sysbus_request_completed; + tc->get_version = tpm_tis_sysbus_get_tpm_version; +} + +static const TypeInfo tpm_tis_sysbus_info = { + .name = TYPE_TPM_TIS_SYSBUS, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(TPMStateSysBus), + .instance_init = tpm_tis_sysbus_initfn, + .class_init = tpm_tis_sysbus_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_TPM_IF }, + { } + } +}; + +static void tpm_tis_sysbus_register(void) +{ + type_register_static(&tpm_tis_sysbus_info); +} + +type_init(tpm_tis_sysbus_register) diff --git a/hw/tpm/tpm_util.c b/hw/tpm/tpm_util.c index 62b091f0c0..c0a0f3d71f 100644 --- a/hw/tpm/tpm_util.c +++ b/hw/tpm/tpm_util.c @@ -350,3 +350,28 @@ void tpm_sized_buffer_reset(TPMSizedBuffer *tsb) tsb->buffer = NULL; tsb->size = 0; } + +void tpm_util_show_buffer(const unsigned char *buffer, + size_t buffer_size, const char *string) +{ + size_t len, i; + char *line_buffer, *p; + + len = MIN(tpm_cmd_get_size(buffer), buffer_size); + + /* + * allocate enough room for 3 chars per buffer entry plus a + * newline after every 16 chars and a final null terminator. + */ + line_buffer = g_malloc(len * 3 + (len / 16) + 1); + + for (i = 0, p = line_buffer; i < len; i++) { + if (i && !(i % 16)) { + p += sprintf(p, "\n"); + } + p += sprintf(p, "%.2X ", buffer[i]); + } + trace_tpm_util_show_buffer(string, len, line_buffer); + + g_free(line_buffer); +} diff --git a/hw/tpm/tpm_util.h b/hw/tpm/tpm_util.h index f397ac21b8..7889081fba 100644 --- a/hw/tpm/tpm_util.h +++ b/hw/tpm/tpm_util.h @@ -79,4 +79,7 @@ typedef struct TPMSizedBuffer { void tpm_sized_buffer_reset(TPMSizedBuffer *tsb); +void tpm_util_show_buffer(const unsigned char *buffer, + size_t buffer_size, const char *string); + #endif /* TPM_TPM_UTIL_H */ diff --git a/hw/tpm/trace-events b/hw/tpm/trace-events index 89804bcd64..439e514787 100644 --- a/hw/tpm/trace-events +++ b/hw/tpm/trace-events @@ -14,6 +14,7 @@ tpm_util_get_buffer_size_len(uint32_t len, size_t expected) "tpm_resp->len = %u, tpm_util_get_buffer_size_hdr_len2(uint32_t len, size_t expected) "tpm2_resp->hdr.len = %u, expected = %zu" tpm_util_get_buffer_size_len2(uint32_t len, size_t expected) "tpm2_resp->len = %u, expected = %zu" tpm_util_get_buffer_size(size_t len) "buffersize of device: %zu" +tpm_util_show_buffer(const char *direction, size_t len, const char *buf) "direction: %s len: %zu\n%s" # tpm_emulator.c tpm_emulator_set_locality(uint8_t locty) "setting locality to %d" @@ -36,7 +37,6 @@ tpm_emulator_pre_save(void) "" tpm_emulator_inst_init(void) "" # tpm_tis.c -tpm_tis_show_buffer(const char *direction, size_t len, const char *buf) "direction: %s len: %zu\nbuf: %s" tpm_tis_raise_irq(uint32_t irqmask) "Raising IRQ for flag 0x%08x" tpm_tis_new_active_locality(uint8_t locty) "Active locality is now %d" tpm_tis_abort(uint8_t locty) "New active locality is %d" @@ -55,3 +55,17 @@ tpm_tis_pre_save(uint8_t locty, uint32_t rw_offset) "locty: %d, rw_offset = %u" # tpm_ppi.c tpm_ppi_memset(uint8_t *ptr, size_t size) "memset: %p %zu" + +# hw/tpm/tpm_spapr.c +tpm_spapr_show_buffer(const char *direction, size_t len, const char *buf) "direction: %s len: %zu\n%s" +tpm_spapr_do_crq(uint8_t raw1, uint8_t raw2) "1st 2 bytes in CRQ: 0x%02x 0x%02x" +tpm_spapr_do_crq_crq_result(void) "SPAPR_VTPM_INIT_CRQ_RESULT" +tpm_spapr_do_crq_crq_complete_result(void) "SPAPR_VTPM_INIT_CRQ_COMP_RESULT" +tpm_spapr_do_crq_tpm_command(void) "got TPM command payload" +tpm_spapr_do_crq_tpm_get_rtce_buffer_size(size_t buffersize) "response: buffer size is %zu" +tpm_spapr_do_crq_get_version(uint32_t version) "response: version %u" +tpm_spapr_do_crq_prepare_to_suspend(void) "response: preparing to suspend" +tpm_spapr_do_crq_unknown_msg_type(uint8_t type) "Unknown message type 0x%02x" +tpm_spapr_do_crq_unknown_crq(uint8_t raw1, uint8_t raw2) "unknown CRQ 0x%02x 0x%02x ..." +tpm_spapr_post_load(void) "Delivering TPM response after resume" +tpm_spapr_caught_response(uint32_t v) "Caught response to deliver after resume: %u bytes" diff --git a/hw/tricore/tricore_testboard.c b/hw/tricore/tricore_testboard.c index aef3289f8c..8ec2b5bddd 100644 --- a/hw/tricore/tricore_testboard.c +++ b/hw/tricore/tricore_testboard.c @@ -42,7 +42,7 @@ static void tricore_load_kernel(CPUTriCoreState *env) kernel_size = load_elf(tricoretb_binfo.kernel_filename, NULL, NULL, NULL, &entry, NULL, - NULL, 0, + NULL, NULL, 0, EM_TRICORE, 1, 0); if (kernel_size <= 0) { error_report("no kernel file '%s'", @@ -105,7 +105,6 @@ static void ttb_machine_init(MachineClass *mc) { mc->desc = "a minimal TriCore board"; mc->init = tricoreboard_init; - mc->is_default = 0; mc->default_cpu_type = TRICORE_CPU_TYPE_NAME("tc1796"); } diff --git a/hw/unicore32/puv3.c b/hw/unicore32/puv3.c index 7e933de228..7f9c0238fe 100644 --- a/hw/unicore32/puv3.c +++ b/hw/unicore32/puv3.c @@ -140,7 +140,7 @@ static void puv3_machine_init(MachineClass *mc) { mc->desc = "PKUnity Version-3 based on UniCore32"; mc->init = puv3_init; - mc->is_default = 1; + mc->is_default = true; mc->default_cpu_type = UNICORE32_CPU_TYPE_NAME("UniCore-II"); } diff --git a/hw/usb/Kconfig b/hw/usb/Kconfig index 555e09377b..464348ba14 100644 --- a/hw/usb/Kconfig +++ b/hw/usb/Kconfig @@ -82,11 +82,6 @@ config USB_NETWORK default y depends on USB -config USB_BLUETOOTH - bool - default y - depends on USB - config USB_SMARTCARD bool default y @@ -96,3 +91,8 @@ config USB_STORAGE_MTP bool default y depends on USB + +config IMX_USBPHY + bool + default y + depends on USB diff --git a/hw/usb/Makefile.objs b/hw/usb/Makefile.objs index 303ac084a0..66835e5bf7 100644 --- a/hw/usb/Makefile.objs +++ b/hw/usb/Makefile.objs @@ -13,8 +13,8 @@ common-obj-$(CONFIG_USB_XHCI) += hcd-xhci.o common-obj-$(CONFIG_USB_XHCI_NEC) += hcd-xhci-nec.o common-obj-$(CONFIG_USB_MUSB) += hcd-musb.o -obj-$(CONFIG_TUSB6010) += tusb6010.o -obj-$(CONFIG_IMX) += chipidea.o +common-obj-$(CONFIG_TUSB6010) += tusb6010.o +common-obj-$(CONFIG_IMX) += chipidea.o # emulated usb devices common-obj-$(CONFIG_USB) += dev-hub.o @@ -25,7 +25,6 @@ common-obj-$(CONFIG_USB_STORAGE_UAS) += dev-uas.o common-obj-$(CONFIG_USB_AUDIO) += dev-audio.o common-obj-$(CONFIG_USB_SERIAL) += dev-serial.o common-obj-$(CONFIG_USB_NETWORK) += dev-network.o -common-obj-$(CONFIG_USB_BLUETOOTH) += dev-bluetooth.o ifeq ($(CONFIG_USB_SMARTCARD),y) common-obj-y += dev-smartcard-reader.o @@ -40,9 +39,11 @@ common-obj-$(CONFIG_USB_STORAGE_MTP) += dev-mtp.o endif # usb redirection +ifeq ($(CONFIG_USB),y) common-obj-$(CONFIG_USB_REDIR) += redirect.o quirks.o redirect.o-cflags = $(USB_REDIR_CFLAGS) redirect.o-libs = $(USB_REDIR_LIBS) +endif # usb pass-through ifeq ($(CONFIG_USB_LIBUSB)$(CONFIG_USB),yy) @@ -60,3 +61,5 @@ common-obj-$(CONFIG_XEN) += xen-usb.o xen-usb.o-cflags := $(LIBUSB_CFLAGS) xen-usb.o-libs := $(LIBUSB_LIBS) endif + +common-obj-$(CONFIG_IMX_USBPHY) += imx-usb-phy.o diff --git a/hw/usb/bus.c b/hw/usb/bus.c index a6522f5429..c9a390063f 100644 --- a/hw/usb/bus.c +++ b/hw/usb/bus.c @@ -768,7 +768,7 @@ static void usb_device_class_init(ObjectClass *klass, void *data) k->bus_type = TYPE_USB_BUS; k->realize = usb_qdev_realize; k->unrealize = usb_qdev_unrealize; - k->props = usb_props; + device_class_set_props(k, usb_props); } static const TypeInfo usb_device_type_info = { diff --git a/hw/usb/ccid-card-emulated.c b/hw/usb/ccid-card-emulated.c index 291e41db8a..3083124556 100644 --- a/hw/usb/ccid-card-emulated.c +++ b/hw/usb/ccid-card-emulated.c @@ -603,7 +603,7 @@ static void emulated_class_initfn(ObjectClass *klass, void *data) cc->apdu_from_guest = emulated_apdu_from_guest; set_bit(DEVICE_CATEGORY_INPUT, dc->categories); dc->desc = "emulated smartcard"; - dc->props = emulated_card_properties; + device_class_set_props(dc, emulated_card_properties); } static const TypeInfo emulated_card_info = { diff --git a/hw/usb/ccid-card-passthru.c b/hw/usb/ccid-card-passthru.c index 267ed9a8a0..bb325dbc4a 100644 --- a/hw/usb/ccid-card-passthru.c +++ b/hw/usb/ccid-card-passthru.c @@ -307,7 +307,7 @@ static void ccid_card_vscard_read(void *opaque, const uint8_t *buf, int size) } } -static void ccid_card_vscard_event(void *opaque, int event) +static void ccid_card_vscard_event(void *opaque, QEMUChrEvent event) { PassthruState *card = opaque; @@ -318,6 +318,11 @@ static void ccid_card_vscard_event(void *opaque, int event) case CHR_EVENT_OPENED: DPRINTF(card, D_INFO, "%s: CHR_EVENT_OPENED\n", __func__); break; + case CHR_EVENT_MUX_IN: + case CHR_EVENT_MUX_OUT: + case CHR_EVENT_CLOSED: + /* Ignore */ + break; } } @@ -398,7 +403,7 @@ static void passthru_class_initfn(ObjectClass *klass, void *data) set_bit(DEVICE_CATEGORY_INPUT, dc->categories); dc->desc = "passthrough smartcard"; dc->vmsd = &passthru_vmstate; - dc->props = passthru_card_properties; + device_class_set_props(dc, passthru_card_properties); } static const TypeInfo passthru_card_info = { diff --git a/hw/usb/dev-audio.c b/hw/usb/dev-audio.c index ea604bbb8e..6fa213fad5 100644 --- a/hw/usb/dev-audio.c +++ b/hw/usb/dev-audio.c @@ -1002,7 +1002,7 @@ static void usb_audio_class_init(ObjectClass *klass, void *data) USBDeviceClass *k = USB_DEVICE_CLASS(klass); dc->vmsd = &vmstate_usb_audio; - dc->props = usb_audio_properties; + device_class_set_props(dc, usb_audio_properties); set_bit(DEVICE_CATEGORY_SOUND, dc->categories); k->product_desc = "QEMU USB Audio Interface"; k->realize = usb_audio_realize; diff --git a/hw/usb/dev-bluetooth.c b/hw/usb/dev-bluetooth.c deleted file mode 100644 index f6944fa74f..0000000000 --- a/hw/usb/dev-bluetooth.c +++ /dev/null @@ -1,581 +0,0 @@ -/* - * QEMU Bluetooth HCI USB Transport Layer v1.0 - * - * Copyright (C) 2007 OpenMoko, Inc. - * Copyright (C) 2008 Andrzej Zaborowski - * - * 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 or - * (at your option) version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#include "qemu/osdep.h" -#include "qemu/error-report.h" -#include "qemu/module.h" -#include "hw/usb.h" -#include "migration/vmstate.h" -#include "desc.h" -#include "sysemu/bt.h" -#include "hw/bt.h" - -struct USBBtState { - USBDevice dev; - struct HCIInfo *hci; - USBEndpoint *intr; - - int config; - -#define CFIFO_LEN_MASK 255 -#define DFIFO_LEN_MASK 4095 - struct usb_hci_in_fifo_s { - uint8_t data[(DFIFO_LEN_MASK + 1) * 2]; - struct { - uint8_t *data; - int len; - } fifo[CFIFO_LEN_MASK + 1]; - int dstart, dlen, dsize, start, len; - } evt, acl, sco; - - struct usb_hci_out_fifo_s { - uint8_t data[4096]; - int len; - } outcmd, outacl, outsco; -}; - -#define TYPE_USB_BT "usb-bt-dongle" -#define USB_BT(obj) OBJECT_CHECK(struct USBBtState, (obj), TYPE_USB_BT) - -#define USB_EVT_EP 1 -#define USB_ACL_EP 2 -#define USB_SCO_EP 3 - -enum { - STR_MANUFACTURER = 1, - STR_SERIALNUMBER, -}; - -static const USBDescStrings desc_strings = { - [STR_MANUFACTURER] = "QEMU", - [STR_SERIALNUMBER] = "1", -}; - -static const USBDescIface desc_iface_bluetooth[] = { - { - .bInterfaceNumber = 0, - .bNumEndpoints = 3, - .bInterfaceClass = 0xe0, /* Wireless */ - .bInterfaceSubClass = 0x01, /* Radio Frequency */ - .bInterfaceProtocol = 0x01, /* Bluetooth */ - .eps = (USBDescEndpoint[]) { - { - .bEndpointAddress = USB_DIR_IN | USB_EVT_EP, - .bmAttributes = USB_ENDPOINT_XFER_INT, - .wMaxPacketSize = 0x10, - .bInterval = 0x02, - }, - { - .bEndpointAddress = USB_DIR_OUT | USB_ACL_EP, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = 0x40, - .bInterval = 0x0a, - }, - { - .bEndpointAddress = USB_DIR_IN | USB_ACL_EP, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = 0x40, - .bInterval = 0x0a, - }, - }, - },{ - .bInterfaceNumber = 1, - .bAlternateSetting = 0, - .bNumEndpoints = 2, - .bInterfaceClass = 0xe0, /* Wireless */ - .bInterfaceSubClass = 0x01, /* Radio Frequency */ - .bInterfaceProtocol = 0x01, /* Bluetooth */ - .eps = (USBDescEndpoint[]) { - { - .bEndpointAddress = USB_DIR_OUT | USB_SCO_EP, - .bmAttributes = USB_ENDPOINT_XFER_ISOC, - .wMaxPacketSize = 0, - .bInterval = 0x01, - }, - { - .bEndpointAddress = USB_DIR_IN | USB_SCO_EP, - .bmAttributes = USB_ENDPOINT_XFER_ISOC, - .wMaxPacketSize = 0, - .bInterval = 0x01, - }, - }, - },{ - .bInterfaceNumber = 1, - .bAlternateSetting = 1, - .bNumEndpoints = 2, - .bInterfaceClass = 0xe0, /* Wireless */ - .bInterfaceSubClass = 0x01, /* Radio Frequency */ - .bInterfaceProtocol = 0x01, /* Bluetooth */ - .eps = (USBDescEndpoint[]) { - { - .bEndpointAddress = USB_DIR_OUT | USB_SCO_EP, - .bmAttributes = USB_ENDPOINT_XFER_ISOC, - .wMaxPacketSize = 0x09, - .bInterval = 0x01, - }, - { - .bEndpointAddress = USB_DIR_IN | USB_SCO_EP, - .bmAttributes = USB_ENDPOINT_XFER_ISOC, - .wMaxPacketSize = 0x09, - .bInterval = 0x01, - }, - }, - },{ - .bInterfaceNumber = 1, - .bAlternateSetting = 2, - .bNumEndpoints = 2, - .bInterfaceClass = 0xe0, /* Wireless */ - .bInterfaceSubClass = 0x01, /* Radio Frequency */ - .bInterfaceProtocol = 0x01, /* Bluetooth */ - .eps = (USBDescEndpoint[]) { - { - .bEndpointAddress = USB_DIR_OUT | USB_SCO_EP, - .bmAttributes = USB_ENDPOINT_XFER_ISOC, - .wMaxPacketSize = 0x11, - .bInterval = 0x01, - }, - { - .bEndpointAddress = USB_DIR_IN | USB_SCO_EP, - .bmAttributes = USB_ENDPOINT_XFER_ISOC, - .wMaxPacketSize = 0x11, - .bInterval = 0x01, - }, - }, - },{ - .bInterfaceNumber = 1, - .bAlternateSetting = 3, - .bNumEndpoints = 2, - .bInterfaceClass = 0xe0, /* Wireless */ - .bInterfaceSubClass = 0x01, /* Radio Frequency */ - .bInterfaceProtocol = 0x01, /* Bluetooth */ - .eps = (USBDescEndpoint[]) { - { - .bEndpointAddress = USB_DIR_OUT | USB_SCO_EP, - .bmAttributes = USB_ENDPOINT_XFER_ISOC, - .wMaxPacketSize = 0x19, - .bInterval = 0x01, - }, - { - .bEndpointAddress = USB_DIR_IN | USB_SCO_EP, - .bmAttributes = USB_ENDPOINT_XFER_ISOC, - .wMaxPacketSize = 0x19, - .bInterval = 0x01, - }, - }, - },{ - .bInterfaceNumber = 1, - .bAlternateSetting = 4, - .bNumEndpoints = 2, - .bInterfaceClass = 0xe0, /* Wireless */ - .bInterfaceSubClass = 0x01, /* Radio Frequency */ - .bInterfaceProtocol = 0x01, /* Bluetooth */ - .eps = (USBDescEndpoint[]) { - { - .bEndpointAddress = USB_DIR_OUT | USB_SCO_EP, - .bmAttributes = USB_ENDPOINT_XFER_ISOC, - .wMaxPacketSize = 0x21, - .bInterval = 0x01, - }, - { - .bEndpointAddress = USB_DIR_IN | USB_SCO_EP, - .bmAttributes = USB_ENDPOINT_XFER_ISOC, - .wMaxPacketSize = 0x21, - .bInterval = 0x01, - }, - }, - },{ - .bInterfaceNumber = 1, - .bAlternateSetting = 5, - .bNumEndpoints = 2, - .bInterfaceClass = 0xe0, /* Wireless */ - .bInterfaceSubClass = 0x01, /* Radio Frequency */ - .bInterfaceProtocol = 0x01, /* Bluetooth */ - .eps = (USBDescEndpoint[]) { - { - .bEndpointAddress = USB_DIR_OUT | USB_SCO_EP, - .bmAttributes = USB_ENDPOINT_XFER_ISOC, - .wMaxPacketSize = 0x31, - .bInterval = 0x01, - }, - { - .bEndpointAddress = USB_DIR_IN | USB_SCO_EP, - .bmAttributes = USB_ENDPOINT_XFER_ISOC, - .wMaxPacketSize = 0x31, - .bInterval = 0x01, - }, - }, - } -}; - -static const USBDescDevice desc_device_bluetooth = { - .bcdUSB = 0x0110, - .bDeviceClass = 0xe0, /* Wireless */ - .bDeviceSubClass = 0x01, /* Radio Frequency */ - .bDeviceProtocol = 0x01, /* Bluetooth */ - .bMaxPacketSize0 = 64, - .bNumConfigurations = 1, - .confs = (USBDescConfig[]) { - { - .bNumInterfaces = 2, - .bConfigurationValue = 1, - .bmAttributes = USB_CFG_ATT_ONE | USB_CFG_ATT_SELFPOWER, - .bMaxPower = 0, - .nif = ARRAY_SIZE(desc_iface_bluetooth), - .ifs = desc_iface_bluetooth, - }, - }, -}; - -static const USBDesc desc_bluetooth = { - .id = { - .idVendor = 0x0a12, - .idProduct = 0x0001, - .bcdDevice = 0x1958, - .iManufacturer = STR_MANUFACTURER, - .iProduct = 0, - .iSerialNumber = STR_SERIALNUMBER, - }, - .full = &desc_device_bluetooth, - .str = desc_strings, -}; - -static void usb_bt_fifo_reset(struct usb_hci_in_fifo_s *fifo) -{ - fifo->dstart = 0; - fifo->dlen = 0; - fifo->dsize = DFIFO_LEN_MASK + 1; - fifo->start = 0; - fifo->len = 0; -} - -static void usb_bt_fifo_enqueue(struct usb_hci_in_fifo_s *fifo, - const uint8_t *data, int len) -{ - int off = fifo->dstart + fifo->dlen; - uint8_t *buf; - - fifo->dlen += len; - if (off <= DFIFO_LEN_MASK) { - if (off + len > DFIFO_LEN_MASK + 1 && - (fifo->dsize = off + len) > (DFIFO_LEN_MASK + 1) * 2) { - fprintf(stderr, "%s: can't alloc %i bytes\n", __func__, len); - exit(-1); - } - buf = fifo->data + off; - } else { - if (fifo->dlen > fifo->dsize) { - fprintf(stderr, "%s: can't alloc %i bytes\n", __func__, len); - exit(-1); - } - buf = fifo->data + off - fifo->dsize; - } - - off = (fifo->start + fifo->len ++) & CFIFO_LEN_MASK; - fifo->fifo[off].data = memcpy(buf, data, len); - fifo->fifo[off].len = len; -} - -static inline void usb_bt_fifo_dequeue(struct usb_hci_in_fifo_s *fifo, - USBPacket *p) -{ - int len; - - assert(fifo->len != 0); - - len = MIN(p->iov.size, fifo->fifo[fifo->start].len); - usb_packet_copy(p, fifo->fifo[fifo->start].data, len); - if (len == p->iov.size) { - fifo->fifo[fifo->start].len -= len; - fifo->fifo[fifo->start].data += len; - } else { - fifo->start ++; - fifo->start &= CFIFO_LEN_MASK; - fifo->len --; - } - - fifo->dstart += len; - fifo->dlen -= len; - if (fifo->dstart >= fifo->dsize) { - fifo->dstart = 0; - fifo->dsize = DFIFO_LEN_MASK + 1; - } -} - -static inline void usb_bt_fifo_out_enqueue(struct USBBtState *s, - struct usb_hci_out_fifo_s *fifo, - void (*send)(struct HCIInfo *, const uint8_t *, int), - int (*complete)(const uint8_t *, int), - USBPacket *p) -{ - usb_packet_copy(p, fifo->data + fifo->len, p->iov.size); - fifo->len += p->iov.size; - if (complete(fifo->data, fifo->len)) { - send(s->hci, fifo->data, fifo->len); - fifo->len = 0; - } - - /* TODO: do we need to loop? */ -} - -static int usb_bt_hci_cmd_complete(const uint8_t *data, int len) -{ - len -= HCI_COMMAND_HDR_SIZE; - return len >= 0 && - len >= ((struct hci_command_hdr *) data)->plen; -} - -static int usb_bt_hci_acl_complete(const uint8_t *data, int len) -{ - len -= HCI_ACL_HDR_SIZE; - return len >= 0 && - len >= le16_to_cpu(((struct hci_acl_hdr *) data)->dlen); -} - -static int usb_bt_hci_sco_complete(const uint8_t *data, int len) -{ - len -= HCI_SCO_HDR_SIZE; - return len >= 0 && - len >= ((struct hci_sco_hdr *) data)->dlen; -} - -static void usb_bt_handle_reset(USBDevice *dev) -{ - struct USBBtState *s = (struct USBBtState *) dev->opaque; - - usb_bt_fifo_reset(&s->evt); - usb_bt_fifo_reset(&s->acl); - usb_bt_fifo_reset(&s->sco); - s->outcmd.len = 0; - s->outacl.len = 0; - s->outsco.len = 0; -} - -static void usb_bt_handle_control(USBDevice *dev, USBPacket *p, - int request, int value, int index, int length, uint8_t *data) -{ - struct USBBtState *s = (struct USBBtState *) dev->opaque; - int ret; - - ret = usb_desc_handle_control(dev, p, request, value, index, length, data); - if (ret >= 0) { - switch (request) { - case DeviceRequest | USB_REQ_GET_CONFIGURATION: - s->config = 0; - break; - case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: - s->config = 1; - usb_bt_fifo_reset(&s->evt); - usb_bt_fifo_reset(&s->acl); - usb_bt_fifo_reset(&s->sco); - break; - } - return; - } - - switch (request) { - case InterfaceRequest | USB_REQ_GET_STATUS: - case EndpointRequest | USB_REQ_GET_STATUS: - data[0] = 0x00; - data[1] = 0x00; - p->actual_length = 2; - break; - case InterfaceOutRequest | USB_REQ_CLEAR_FEATURE: - case EndpointOutRequest | USB_REQ_CLEAR_FEATURE: - goto fail; - case InterfaceOutRequest | USB_REQ_SET_FEATURE: - case EndpointOutRequest | USB_REQ_SET_FEATURE: - goto fail; - break; - case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_DEVICE) << 8): - if (s->config) - usb_bt_fifo_out_enqueue(s, &s->outcmd, s->hci->cmd_send, - usb_bt_hci_cmd_complete, p); - break; - default: - fail: - p->status = USB_RET_STALL; - break; - } -} - -static void usb_bt_handle_data(USBDevice *dev, USBPacket *p) -{ - struct USBBtState *s = (struct USBBtState *) dev->opaque; - - if (!s->config) - goto fail; - - switch (p->pid) { - case USB_TOKEN_IN: - switch (p->ep->nr) { - case USB_EVT_EP: - if (s->evt.len == 0) { - p->status = USB_RET_NAK; - break; - } - usb_bt_fifo_dequeue(&s->evt, p); - break; - - case USB_ACL_EP: - if (s->evt.len == 0) { - p->status = USB_RET_STALL; - break; - } - usb_bt_fifo_dequeue(&s->acl, p); - break; - - case USB_SCO_EP: - if (s->evt.len == 0) { - p->status = USB_RET_STALL; - break; - } - usb_bt_fifo_dequeue(&s->sco, p); - break; - - default: - goto fail; - } - break; - - case USB_TOKEN_OUT: - switch (p->ep->nr) { - case USB_ACL_EP: - usb_bt_fifo_out_enqueue(s, &s->outacl, s->hci->acl_send, - usb_bt_hci_acl_complete, p); - break; - - case USB_SCO_EP: - usb_bt_fifo_out_enqueue(s, &s->outsco, s->hci->sco_send, - usb_bt_hci_sco_complete, p); - break; - - default: - goto fail; - } - break; - - default: - fail: - p->status = USB_RET_STALL; - break; - } -} - -static void usb_bt_out_hci_packet_event(void *opaque, - const uint8_t *data, int len) -{ - struct USBBtState *s = (struct USBBtState *) opaque; - - if (s->evt.len == 0) { - usb_wakeup(s->intr, 0); - } - usb_bt_fifo_enqueue(&s->evt, data, len); -} - -static void usb_bt_out_hci_packet_acl(void *opaque, - const uint8_t *data, int len) -{ - struct USBBtState *s = (struct USBBtState *) opaque; - - usb_bt_fifo_enqueue(&s->acl, data, len); -} - -static void usb_bt_unrealize(USBDevice *dev, Error **errp) -{ - struct USBBtState *s = (struct USBBtState *) dev->opaque; - - s->hci->opaque = NULL; - s->hci->evt_recv = NULL; - s->hci->acl_recv = NULL; -} - -static void usb_bt_realize(USBDevice *dev, Error **errp) -{ - struct USBBtState *s = USB_BT(dev); - - usb_desc_create_serial(dev); - usb_desc_init(dev); - s->dev.opaque = s; - if (!s->hci) { - s->hci = bt_new_hci(qemu_find_bt_vlan(0)); - } - s->hci->opaque = s; - s->hci->evt_recv = usb_bt_out_hci_packet_event; - s->hci->acl_recv = usb_bt_out_hci_packet_acl; - usb_bt_handle_reset(&s->dev); - s->intr = usb_ep_get(dev, USB_TOKEN_IN, USB_EVT_EP); -} - -static USBDevice *usb_bt_init(USBBus *bus, const char *cmdline) -{ - USBDevice *dev; - struct USBBtState *s; - HCIInfo *hci; - const char *name = TYPE_USB_BT; - - if (*cmdline) { - hci = hci_init(cmdline); - } else { - hci = bt_new_hci(qemu_find_bt_vlan(0)); - } - if (!hci) - return NULL; - - dev = usb_create(bus, name); - s = USB_BT(dev); - s->hci = hci; - return dev; -} - -static const VMStateDescription vmstate_usb_bt = { - .name = "usb-bt", - .unmigratable = 1, -}; - -static void usb_bt_class_initfn(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - USBDeviceClass *uc = USB_DEVICE_CLASS(klass); - - uc->realize = usb_bt_realize; - uc->product_desc = "QEMU BT dongle"; - uc->usb_desc = &desc_bluetooth; - uc->handle_reset = usb_bt_handle_reset; - uc->handle_control = usb_bt_handle_control; - uc->handle_data = usb_bt_handle_data; - uc->unrealize = usb_bt_unrealize; - dc->vmsd = &vmstate_usb_bt; - set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); -} - -static const TypeInfo bt_info = { - .name = TYPE_USB_BT, - .parent = TYPE_USB_DEVICE, - .instance_size = sizeof(struct USBBtState), - .class_init = usb_bt_class_initfn, -}; - -static void usb_bt_register_types(void) -{ - type_register_static(&bt_info); - usb_legacy_register(TYPE_USB_BT, "bt", usb_bt_init); -} - -type_init(usb_bt_register_types) diff --git a/hw/usb/dev-hid.c b/hw/usb/dev-hid.c index 88492f26e6..67ec8b69ec 100644 --- a/hw/usb/dev-hid.c +++ b/hw/usb/dev-hid.c @@ -819,7 +819,7 @@ static void usb_tablet_class_initfn(ObjectClass *klass, void *data) uc->realize = usb_tablet_realize; uc->product_desc = "QEMU USB Tablet"; dc->vmsd = &vmstate_usb_ptr; - dc->props = usb_tablet_properties; + device_class_set_props(dc, usb_tablet_properties); set_bit(DEVICE_CATEGORY_INPUT, dc->categories); } @@ -842,7 +842,7 @@ static void usb_mouse_class_initfn(ObjectClass *klass, void *data) uc->realize = usb_mouse_realize; uc->product_desc = "QEMU USB Mouse"; dc->vmsd = &vmstate_usb_ptr; - dc->props = usb_mouse_properties; + device_class_set_props(dc, usb_mouse_properties); set_bit(DEVICE_CATEGORY_INPUT, dc->categories); } @@ -866,7 +866,7 @@ static void usb_keyboard_class_initfn(ObjectClass *klass, void *data) uc->realize = usb_keyboard_realize; uc->product_desc = "QEMU USB Keyboard"; dc->vmsd = &vmstate_usb_kbd; - dc->props = usb_keyboard_properties; + device_class_set_props(dc, usb_keyboard_properties); set_bit(DEVICE_CATEGORY_INPUT, dc->categories); } diff --git a/hw/usb/dev-hub.c b/hw/usb/dev-hub.c index c5043a831e..4ca86bb8fe 100644 --- a/hw/usb/dev-hub.c +++ b/hw/usb/dev-hub.c @@ -693,7 +693,7 @@ static void usb_hub_class_initfn(ObjectClass *klass, void *data) set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); dc->fw_name = "hub"; dc->vmsd = &vmstate_usb_hub; - dc->props = usb_hub_properties; + device_class_set_props(dc, usb_hub_properties); } static const TypeInfo hub_info = { diff --git a/hw/usb/dev-mtp.c b/hw/usb/dev-mtp.c index 7c07295519..20717f026b 100644 --- a/hw/usb/dev-mtp.c +++ b/hw/usb/dev-mtp.c @@ -2105,7 +2105,7 @@ static void usb_mtp_class_initfn(ObjectClass *klass, void *data) dc->desc = "USB Media Transfer Protocol device"; dc->fw_name = "mtp"; dc->vmsd = &vmstate_usb_mtp; - dc->props = mtp_properties; + device_class_set_props(dc, mtp_properties); } static TypeInfo mtp_info = { diff --git a/hw/usb/dev-network.c b/hw/usb/dev-network.c index 5de6213cc4..6210427544 100644 --- a/hw/usb/dev-network.c +++ b/hw/usb/dev-network.c @@ -626,7 +626,7 @@ static const uint32_t oid_supported_list[] = struct rndis_response { QTAILQ_ENTRY(rndis_response) entries; uint32_t length; - uint8_t buf[0]; + uint8_t buf[]; }; typedef struct USBNetState { @@ -1342,7 +1342,7 @@ static NetClientInfo net_usbnet_info = { .cleanup = usbnet_cleanup, }; -static void usb_net_realize(USBDevice *dev, Error **errrp) +static void usb_net_realize(USBDevice *dev, Error **errp) { USBNetState *s = USB_NET(dev); @@ -1409,7 +1409,7 @@ static void usb_net_class_initfn(ObjectClass *klass, void *data) set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); dc->fw_name = "network"; dc->vmsd = &vmstate_usb_net; - dc->props = net_properties; + device_class_set_props(dc, net_properties); } static const TypeInfo net_info = { diff --git a/hw/usb/dev-serial.c b/hw/usb/dev-serial.c index 45cc74128a..d2c03681b7 100644 --- a/hw/usb/dev-serial.c +++ b/hw/usb/dev-serial.c @@ -29,7 +29,7 @@ do { printf("usb-serial: " fmt , ## __VA_ARGS__); } while (0) #define DPRINTF(fmt, ...) do {} while(0) #endif -#define RECV_BUF 384 +#define RECV_BUF (512 - (2 * 8)) /* Commands */ #define FTDI_RESET 0 @@ -98,6 +98,7 @@ do { printf("usb-serial: " fmt , ## __VA_ARGS__); } while (0) typedef struct { USBDevice dev; + USBEndpoint *intr; uint8_t recv_buf[RECV_BUF]; uint16_t recv_ptr; uint16_t recv_used; @@ -153,7 +154,7 @@ static const USBDescDevice desc_device = { { .bNumInterfaces = 1, .bConfigurationValue = 1, - .bmAttributes = USB_CFG_ATT_ONE, + .bmAttributes = USB_CFG_ATT_ONE | USB_CFG_ATT_WAKEUP, .bMaxPower = 50, .nif = 1, .ifs = &desc_iface0, @@ -331,7 +332,7 @@ static void usb_serial_handle_control(USBDevice *dev, USBPacket *p, break; case DeviceInVendor | FTDI_GET_MDM_ST: data[0] = usb_get_modem_lines(s) | 1; - data[1] = 0; + data[1] = FTDI_THRE | FTDI_TEMT; p->actual_length = 2; break; case DeviceOutVendor | FTDI_SET_EVENT_CHR: @@ -357,13 +358,67 @@ static void usb_serial_handle_control(USBDevice *dev, USBPacket *p, } } +static void usb_serial_token_in(USBSerialState *s, USBPacket *p) +{ + const int max_packet_size = desc_iface0.eps[0].wMaxPacketSize; + int packet_len; + uint8_t header[2]; + + packet_len = p->iov.size; + if (packet_len <= 2) { + p->status = USB_RET_NAK; + return; + } + + header[0] = usb_get_modem_lines(s) | 1; + /* We do not have the uart details */ + /* handle serial break */ + if (s->event_trigger && s->event_trigger & FTDI_BI) { + s->event_trigger &= ~FTDI_BI; + header[1] = FTDI_BI; + usb_packet_copy(p, header, 2); + return; + } else { + header[1] = 0; + } + + if (!s->recv_used) { + p->status = USB_RET_NAK; + return; + } + + while (s->recv_used && packet_len > 2) { + int first_len, len; + + len = MIN(packet_len, max_packet_size); + len -= 2; + if (len > s->recv_used) { + len = s->recv_used; + } + + first_len = RECV_BUF - s->recv_ptr; + if (first_len > len) { + first_len = len; + } + usb_packet_copy(p, header, 2); + usb_packet_copy(p, s->recv_buf + s->recv_ptr, first_len); + if (len > first_len) { + usb_packet_copy(p, s->recv_buf, len - first_len); + } + s->recv_used -= len; + s->recv_ptr = (s->recv_ptr + len) % RECV_BUF; + packet_len -= len + 2; + } + + return; +} + static void usb_serial_handle_data(USBDevice *dev, USBPacket *p) { USBSerialState *s = (USBSerialState *)dev; uint8_t devep = p->ep->nr; struct iovec *iov; - uint8_t header[2]; - int i, first_len, len; + int i; switch (p->pid) { case USB_TOKEN_OUT: @@ -381,38 +436,7 @@ static void usb_serial_handle_data(USBDevice *dev, USBPacket *p) case USB_TOKEN_IN: if (devep != 1) goto fail; - first_len = RECV_BUF - s->recv_ptr; - len = p->iov.size; - if (len <= 2) { - p->status = USB_RET_NAK; - break; - } - header[0] = usb_get_modem_lines(s) | 1; - /* We do not have the uart details */ - /* handle serial break */ - if (s->event_trigger && s->event_trigger & FTDI_BI) { - s->event_trigger &= ~FTDI_BI; - header[1] = FTDI_BI; - usb_packet_copy(p, header, 2); - break; - } else { - header[1] = 0; - } - len -= 2; - if (len > s->recv_used) - len = s->recv_used; - if (!len) { - p->status = USB_RET_NAK; - break; - } - if (first_len > len) - first_len = len; - usb_packet_copy(p, header, 2); - usb_packet_copy(p, s->recv_buf + s->recv_ptr, first_len); - if (len > first_len) - usb_packet_copy(p, s->recv_buf, len - first_len); - s->recv_used -= len; - s->recv_ptr = (s->recv_ptr + len) % RECV_BUF; + usb_serial_token_in(s, p); break; default: @@ -459,9 +483,11 @@ static void usb_serial_read(void *opaque, const uint8_t *buf, int size) memcpy(s->recv_buf + start, buf, size); } s->recv_used += size; + + usb_wakeup(s->intr, 0); } -static void usb_serial_event(void *opaque, int event) +static void usb_serial_event(void *opaque, QEMUChrEvent event) { USBSerialState *s = opaque; @@ -479,6 +505,10 @@ static void usb_serial_event(void *opaque, int event) usb_device_detach(&s->dev); } break; + case CHR_EVENT_MUX_IN: + case CHR_EVENT_MUX_OUT: + /* Ignore */ + break; } } @@ -509,6 +539,7 @@ static void usb_serial_realize(USBDevice *dev, Error **errp) if (qemu_chr_fe_backend_open(&s->cs) && !dev->attached) { usb_device_attach(dev, &error_abort); } + s->intr = usb_ep_get(dev, USB_TOKEN_IN, 1); } static USBDevice *usb_braille_init(USBBus *bus, const char *unused) @@ -563,7 +594,7 @@ static void usb_serial_class_initfn(ObjectClass *klass, void *data) uc->product_desc = "QEMU USB Serial"; uc->usb_desc = &desc_serial; - dc->props = serial_properties; + device_class_set_props(dc, serial_properties); } static const TypeInfo serial_info = { @@ -584,7 +615,7 @@ static void usb_braille_class_initfn(ObjectClass *klass, void *data) uc->product_desc = "QEMU USB Braille"; uc->usb_desc = &desc_braille; - dc->props = braille_properties; + device_class_set_props(dc, braille_properties); } static const TypeInfo braille_info = { diff --git a/hw/usb/dev-smartcard-reader.c b/hw/usb/dev-smartcard-reader.c index 4568db2568..ef72738ced 100644 --- a/hw/usb/dev-smartcard-reader.c +++ b/hw/usb/dev-smartcard-reader.c @@ -227,7 +227,7 @@ typedef struct QEMU_PACKED CCID_Parameter { typedef struct QEMU_PACKED CCID_DataBlock { CCID_BULK_IN b; uint8_t bChainParameter; - uint8_t abData[0]; + uint8_t abData[]; } CCID_DataBlock; /* 6.1.4 PC_to_RDR_XfrBlock */ @@ -235,7 +235,7 @@ typedef struct QEMU_PACKED CCID_XferBlock { CCID_Header hdr; uint8_t bBWI; /* Block Waiting Timeout */ uint16_t wLevelParameter; /* XXX currently unused */ - uint8_t abData[0]; + uint8_t abData[]; } CCID_XferBlock; typedef struct QEMU_PACKED CCID_IccPowerOn { @@ -1456,7 +1456,7 @@ static void ccid_class_initfn(ObjectClass *klass, void *data) uc->unrealize = ccid_unrealize; dc->desc = "CCID Rev 1.1 smartcard reader"; dc->vmsd = &ccid_vmstate; - dc->props = ccid_properties; + device_class_set_props(dc, ccid_properties); set_bit(DEVICE_CATEGORY_INPUT, dc->categories); hc->unplug = qdev_simple_device_unplug_cb; } @@ -1478,7 +1478,7 @@ static void ccid_card_class_init(ObjectClass *klass, void *data) k->bus_type = TYPE_CCID_BUS; k->realize = ccid_card_realize; k->unrealize = ccid_card_unrealize; - k->props = ccid_props; + device_class_set_props(k, ccid_props); } static const TypeInfo ccid_card_type_info = { diff --git a/hw/usb/dev-storage.c b/hw/usb/dev-storage.c index 8545193488..5c4b57b06b 100644 --- a/hw/usb/dev-storage.c +++ b/hw/usb/dev-storage.c @@ -17,9 +17,7 @@ #include "desc.h" #include "hw/qdev-properties.h" #include "hw/scsi/scsi.h" -#include "ui/console.h" #include "migration/vmstate.h" -#include "monitor/monitor.h" #include "sysemu/sysemu.h" #include "sysemu/block-backend.h" #include "qapi/visitor.h" @@ -702,7 +700,7 @@ static void usb_msd_class_storage_initfn(ObjectClass *klass, void *data) USBDeviceClass *uc = USB_DEVICE_CLASS(klass); uc->realize = usb_msd_storage_realize; - dc->props = msd_properties; + device_class_set_props(dc, msd_properties); } static void usb_msd_get_bootindex(Object *obj, Visitor *v, const char *name, diff --git a/hw/usb/dev-uas.c b/hw/usb/dev-uas.c index 6d6d1073b9..11a8684cc2 100644 --- a/hw/usb/dev-uas.c +++ b/hw/usb/dev-uas.c @@ -303,7 +303,7 @@ static const USBDescDevice desc_device_high = { static const USBDescDevice desc_device_super = { .bcdUSB = 0x0300, - .bMaxPacketSize0 = 64, + .bMaxPacketSize0 = 9, .bNumConfigurations = 1, .confs = (USBDescConfig[]) { { @@ -952,7 +952,7 @@ static void usb_uas_class_initfn(ObjectClass *klass, void *data) set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); dc->fw_name = "storage"; dc->vmsd = &vmstate_usb_uas; - dc->props = uas_properties; + device_class_set_props(dc, uas_properties); } static const TypeInfo uas_info = { diff --git a/hw/usb/hcd-ehci-pci.c b/hw/usb/hcd-ehci-pci.c index 5b9a8d3dc1..fc73a054c6 100644 --- a/hw/usb/hcd-ehci-pci.c +++ b/hw/usb/hcd-ehci-pci.c @@ -161,7 +161,7 @@ static void ehci_class_init(ObjectClass *klass, void *data) k->class_id = PCI_CLASS_SERIAL_USB; k->config_write = usb_ehci_pci_write_config; dc->vmsd = &vmstate_ehci_pci; - dc->props = ehci_pci_properties; + device_class_set_props(dc, ehci_pci_properties); dc->reset = usb_ehci_pci_reset; } diff --git a/hw/usb/hcd-ehci-sysbus.c b/hw/usb/hcd-ehci-sysbus.c index 020211fd10..3730736540 100644 --- a/hw/usb/hcd-ehci-sysbus.c +++ b/hw/usb/hcd-ehci-sysbus.c @@ -33,6 +33,8 @@ static const VMStateDescription vmstate_ehci_sysbus = { static Property ehci_sysbus_properties[] = { DEFINE_PROP_UINT32("maxframes", EHCISysBusState, ehci.maxframes, 128), + DEFINE_PROP_BOOL("companion-enable", EHCISysBusState, ehci.companion_enable, + false), DEFINE_PROP_END_OF_LIST(), }; @@ -82,7 +84,7 @@ static void ehci_sysbus_class_init(ObjectClass *klass, void *data) dc->realize = usb_ehci_sysbus_realize; dc->vmsd = &vmstate_ehci_sysbus; - dc->props = ehci_sysbus_properties; + device_class_set_props(dc, ehci_sysbus_properties); dc->reset = usb_ehci_sysbus_reset; set_bit(DEVICE_CATEGORY_USB, dc->categories); } @@ -113,22 +115,6 @@ static const TypeInfo ehci_platform_type_info = { .class_init = ehci_platform_class_init, }; -static void ehci_xlnx_class_init(ObjectClass *oc, void *data) -{ - SysBusEHCIClass *sec = SYS_BUS_EHCI_CLASS(oc); - DeviceClass *dc = DEVICE_CLASS(oc); - - set_bit(DEVICE_CATEGORY_USB, dc->categories); - sec->capsbase = 0x100; - sec->opregbase = 0x140; -} - -static const TypeInfo ehci_xlnx_type_info = { - .name = "xlnx,ps7-usb", - .parent = TYPE_SYS_BUS_EHCI, - .class_init = ehci_xlnx_class_init, -}; - static void ehci_exynos4210_class_init(ObjectClass *oc, void *data) { SysBusEHCIClass *sec = SYS_BUS_EHCI_CLASS(oc); @@ -145,6 +131,22 @@ static const TypeInfo ehci_exynos4210_type_info = { .class_init = ehci_exynos4210_class_init, }; +static void ehci_aw_h3_class_init(ObjectClass *oc, void *data) +{ + SysBusEHCIClass *sec = SYS_BUS_EHCI_CLASS(oc); + DeviceClass *dc = DEVICE_CLASS(oc); + + sec->capsbase = 0x0; + sec->opregbase = 0x10; + set_bit(DEVICE_CATEGORY_USB, dc->categories); +} + +static const TypeInfo ehci_aw_h3_type_info = { + .name = TYPE_AW_H3_EHCI, + .parent = TYPE_SYS_BUS_EHCI, + .class_init = ehci_aw_h3_class_init, +}; + static void ehci_tegra2_class_init(ObjectClass *oc, void *data) { SysBusEHCIClass *sec = SYS_BUS_EHCI_CLASS(oc); @@ -265,8 +267,8 @@ static void ehci_sysbus_register_types(void) { type_register_static(&ehci_type_info); type_register_static(&ehci_platform_type_info); - type_register_static(&ehci_xlnx_type_info); type_register_static(&ehci_exynos4210_type_info); + type_register_static(&ehci_aw_h3_type_info); type_register_static(&ehci_tegra2_type_info); type_register_static(&ehci_ppc4xx_type_info); type_register_static(&ehci_fusbh200_type_info); diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index 56ab2f457f..29d49c2d7e 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -1301,7 +1301,6 @@ static void ehci_execute_complete(EHCIQueue *q) /* should not be triggerable */ fprintf(stderr, "USB invalid response %d\n", p->packet.status); g_assert_not_reached(); - break; } /* TODO check 4.12 for splits */ @@ -2105,9 +2104,7 @@ static void ehci_advance_state(EHCIState *ehci, int async) default: fprintf(stderr, "Bad state!\n"); - again = -1; g_assert_not_reached(); - break; } if (again < 0 || itd_count > 16) { diff --git a/hw/usb/hcd-ehci.h b/hw/usb/hcd-ehci.h index 0298238f0b..edb59311c4 100644 --- a/hw/usb/hcd-ehci.h +++ b/hw/usb/hcd-ehci.h @@ -342,6 +342,7 @@ typedef struct EHCIPCIState { #define TYPE_SYS_BUS_EHCI "sysbus-ehci-usb" #define TYPE_PLATFORM_EHCI "platform-ehci-usb" #define TYPE_EXYNOS4210_EHCI "exynos4210-ehci-usb" +#define TYPE_AW_H3_EHCI "aw-h3-ehci-usb" #define TYPE_TEGRA2_EHCI "tegra2-ehci-usb" #define TYPE_PPC4xx_EHCI "ppc4xx-ehci-usb" #define TYPE_FUSBH200_EHCI "fusbh200-ehci-usb" diff --git a/hw/usb/hcd-ohci-pci.c b/hw/usb/hcd-ohci-pci.c index c052f10521..a7fb1666af 100644 --- a/hw/usb/hcd-ohci-pci.c +++ b/hw/usb/hcd-ohci-pci.c @@ -139,7 +139,7 @@ static void ohci_pci_class_init(ObjectClass *klass, void *data) k->class_id = PCI_CLASS_SERIAL_USB; set_bit(DEVICE_CATEGORY_USB, dc->categories); dc->desc = "Apple USB Controller"; - dc->props = ohci_pci_properties; + device_class_set_props(dc, ohci_pci_properties); dc->hotpluggable = false; dc->vmsd = &vmstate_ohci; dc->reset = usb_ohci_reset_pci; diff --git a/hw/usb/hcd-ohci.c b/hw/usb/hcd-ohci.c index 29383db0b3..c787a79776 100644 --- a/hw/usb/hcd-ohci.c +++ b/hw/usb/hcd-ohci.c @@ -1874,21 +1874,6 @@ void ohci_sysbus_die(struct OHCIState *ohci) ohci_bus_stop(ohci); } -#define TYPE_SYSBUS_OHCI "sysbus-ohci" -#define SYSBUS_OHCI(obj) OBJECT_CHECK(OHCISysBusState, (obj), TYPE_SYSBUS_OHCI) - -typedef struct { - /*< private >*/ - SysBusDevice parent_obj; - /*< public >*/ - - OHCIState ohci; - char *masterbus; - uint32_t num_ports; - uint32_t firstport; - dma_addr_t dma_offset; -} OHCISysBusState; - static void ohci_realize_pxa(DeviceState *dev, Error **errp) { OHCISysBusState *s = SYSBUS_OHCI(dev); @@ -2004,7 +1989,7 @@ static void ohci_sysbus_class_init(ObjectClass *klass, void *data) dc->realize = ohci_realize_pxa; set_bit(DEVICE_CATEGORY_USB, dc->categories); dc->desc = "OHCI USB Controller"; - dc->props = ohci_sysbus_properties; + device_class_set_props(dc, ohci_sysbus_properties); dc->reset = usb_ohci_reset_sysbus; } diff --git a/hw/usb/hcd-ohci.h b/hw/usb/hcd-ohci.h index 16e3f1e13a..5c8819aedf 100644 --- a/hw/usb/hcd-ohci.h +++ b/hw/usb/hcd-ohci.h @@ -22,6 +22,7 @@ #define HCD_OHCI_H #include "sysemu/dma.h" +#include "hw/usb.h" /* Number of Downstream Ports on the root hub: */ #define OHCI_MAX_PORTS 15 @@ -90,6 +91,21 @@ typedef struct OHCIState { void (*ohci_die)(struct OHCIState *ohci); } OHCIState; +#define TYPE_SYSBUS_OHCI "sysbus-ohci" +#define SYSBUS_OHCI(obj) OBJECT_CHECK(OHCISysBusState, (obj), TYPE_SYSBUS_OHCI) + +typedef struct { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + + OHCIState ohci; + char *masterbus; + uint32_t num_ports; + uint32_t firstport; + dma_addr_t dma_offset; +} OHCISysBusState; + extern const VMStateDescription vmstate_ohci_state; void usb_ohci_init(OHCIState *ohci, DeviceState *dev, uint32_t num_ports, diff --git a/hw/usb/hcd-uhci.c b/hw/usb/hcd-uhci.c index 23507ad3b5..37f7beb3fa 100644 --- a/hw/usb/hcd-uhci.c +++ b/hw/usb/hcd-uhci.c @@ -1349,9 +1349,9 @@ static void uhci_data_class_init(ObjectClass *klass, void *data) if (!info->unplug) { /* uhci controllers in companion setups can't be hotplugged */ dc->hotpluggable = false; - dc->props = uhci_properties_companion; + device_class_set_props(dc, uhci_properties_companion); } else { - dc->props = uhci_properties_standalone; + device_class_set_props(dc, uhci_properties_standalone); } u->info = *info; } diff --git a/hw/usb/hcd-xhci-nec.c b/hw/usb/hcd-xhci-nec.c index 05fb3f54b3..e6a5a22b6d 100644 --- a/hw/usb/hcd-xhci-nec.c +++ b/hw/usb/hcd-xhci-nec.c @@ -44,7 +44,7 @@ static void nec_xhci_class_init(ObjectClass *klass, void *data) PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); - dc->props = nec_xhci_properties; + device_class_set_props(dc, nec_xhci_properties); k->vendor_id = PCI_VENDOR_ID_NEC; k->device_id = PCI_DEVICE_ID_NEC_UPD720200; k->revision = 0x03; diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index 80988bb305..b330e36fe6 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -1861,6 +1861,13 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, xhci_kick_epctx(epctx, streamid); } +static bool xhci_slot_ok(XHCIState *xhci, int slotid) +{ + return (xhci->slots[slotid - 1].uport && + xhci->slots[slotid - 1].uport->dev && + xhci->slots[slotid - 1].uport->dev->attached); +} + static void xhci_kick_epctx(XHCIEPContext *epctx, unsigned int streamid) { XHCIState *xhci = epctx->xhci; @@ -1878,9 +1885,7 @@ static void xhci_kick_epctx(XHCIEPContext *epctx, unsigned int streamid) /* If the device has been detached, but the guest has not noticed this yet the 2 above checks will succeed, but we must NOT continue */ - if (!xhci->slots[epctx->slotid - 1].uport || - !xhci->slots[epctx->slotid - 1].uport->dev || - !xhci->slots[epctx->slotid - 1].uport->dev->attached) { + if (!xhci_slot_ok(xhci, epctx->slotid)) { return; } @@ -1987,6 +1992,10 @@ static void xhci_kick_epctx(XHCIEPContext *epctx, unsigned int streamid) } else { xhci_fire_transfer(xhci, xfer, epctx); } + if (!xhci_slot_ok(xhci, epctx->slotid)) { + /* surprise removal -> stop processing */ + break; + } if (xfer->complete) { /* update ring dequeue ptr */ xhci_set_ep_state(xhci, epctx, stctx, epctx->state); @@ -2000,6 +2009,7 @@ static void xhci_kick_epctx(XHCIEPContext *epctx, unsigned int streamid) if (xfer != NULL && xfer->running_retry) { DPRINTF("xhci: xfer nacked, stopping schedule\n"); epctx->retry = xfer; + xhci_xfer_unmap(xfer); break; } if (count++ > TRANSFER_LIMIT) { @@ -3697,7 +3707,7 @@ static void xhci_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->vmsd = &vmstate_xhci; - dc->props = xhci_properties; + device_class_set_props(dc, xhci_properties); dc->reset = xhci_reset; set_bit(DEVICE_CATEGORY_USB, dc->categories); k->realize = usb_xhci_realize; diff --git a/hw/usb/host-libusb.c b/hw/usb/host-libusb.c index fcf48c0193..2ac7a936fb 100644 --- a/hw/usb/host-libusb.c +++ b/hw/usb/host-libusb.c @@ -88,6 +88,7 @@ struct USBHostDevice { bool needs_autoscan; bool allow_one_guest_reset; bool allow_all_guest_resets; + bool suppress_remote_wake; /* state */ QTAILQ_ENTRY(USBHostDevice) next; @@ -353,9 +354,7 @@ static USBHostRequest *usb_host_req_alloc(USBHostDevice *s, USBPacket *p, static void usb_host_req_free(USBHostRequest *r) { - if (r->host) { - QTAILQ_REMOVE(&r->host->requests, r, next); - } + QTAILQ_REMOVE(&r->host->requests, r, next); libusb_free_transfer(r->xfer); g_free(r->buffer); g_free(r); @@ -386,6 +385,8 @@ static void LIBUSB_CALL usb_host_req_complete_ctrl(struct libusb_transfer *xfer) r->p->status = status_map[xfer->status]; r->p->actual_length = xfer->actual_length; if (r->in && xfer->actual_length) { + USBDevice *udev = USB_DEVICE(s); + struct libusb_config_descriptor *conf = (void *)r->cbuf; memcpy(r->cbuf, r->buffer + 8, xfer->actual_length); /* Fix up USB-3 ep0 maxpacket size to allow superspeed connected devices @@ -394,6 +395,21 @@ static void LIBUSB_CALL usb_host_req_complete_ctrl(struct libusb_transfer *xfer) r->cbuf[7] == 9) { r->cbuf[7] = 64; } + /* + *If this is GET_DESCRIPTOR request for configuration descriptor, + * remove 'remote wakeup' flag from it to prevent idle power down + * in Windows guest + */ + if (s->suppress_remote_wake && + udev->setup_buf[0] == USB_DIR_IN && + udev->setup_buf[1] == USB_REQ_GET_DESCRIPTOR && + udev->setup_buf[3] == USB_DT_CONFIG && udev->setup_buf[2] == 0 && + xfer->actual_length > + offsetof(struct libusb_config_descriptor, bmAttributes) && + (conf->bmAttributes & USB_CFG_ATT_WAKEUP)) { + trace_usb_host_remote_wakeup_removed(s->bus_num, s->addr); + conf->bmAttributes &= ~USB_CFG_ATT_WAKEUP; + } } trace_usb_host_req_complete(s->bus_num, s->addr, r->p, r->p->status, r->p->actual_length); @@ -450,12 +466,7 @@ static void usb_host_req_abort(USBHostRequest *r) usb_packet_complete(USB_DEVICE(s), r->p); } r->p = NULL; - } - QTAILQ_REMOVE(&r->host->requests, r, next); - r->host = NULL; - - if (inflight) { libusb_cancel_transfer(r->xfer); } } @@ -944,6 +955,13 @@ static void usb_host_abort_xfers(USBHostDevice *s) QTAILQ_FOREACH_SAFE(r, &s->requests, next, rtmp) { usb_host_req_abort(r); } + + while (QTAILQ_FIRST(&s->requests) != NULL) { + struct timeval tv; + memset(&tv, 0, sizeof(tv)); + tv.tv_usec = 2500; + libusb_handle_events_timeout(ctx, &tv); + } } static int usb_host_close(USBHostDevice *s) @@ -993,6 +1011,7 @@ static void usb_host_exit_notifier(struct Notifier *n, void *data) USBHostDevice *s = container_of(n, USBHostDevice, exit); if (s->dh) { + usb_host_abort_xfers(s); usb_host_release_interfaces(s); libusb_reset_device(s->dh); usb_host_attach_kernel(s); @@ -1596,6 +1615,8 @@ static Property usb_host_dev_properties[] = { LIBUSB_LOG_LEVEL_WARNING), DEFINE_PROP_BIT("pipeline", USBHostDevice, options, USB_HOST_OPT_PIPELINE, true), + DEFINE_PROP_BOOL("suppress-remote-wake", USBHostDevice, + suppress_remote_wake, true), DEFINE_PROP_END_OF_LIST(), }; @@ -1615,7 +1636,7 @@ static void usb_host_class_initfn(ObjectClass *klass, void *data) uc->alloc_streams = usb_host_alloc_streams; uc->free_streams = usb_host_free_streams; dc->vmsd = &vmstate_usb_host; - dc->props = usb_host_dev_properties; + device_class_set_props(dc, usb_host_dev_properties); set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); } diff --git a/hw/usb/imx-usb-phy.c b/hw/usb/imx-usb-phy.c new file mode 100644 index 0000000000..e705a03a1f --- /dev/null +++ b/hw/usb/imx-usb-phy.c @@ -0,0 +1,225 @@ +/* + * i.MX USB PHY + * + * Copyright (c) 2020 Guenter Roeck + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + * We need to implement basic reset control in the PHY control register. + * For everything else, it is sufficient to set whatever is written. + */ + +#include "qemu/osdep.h" +#include "hw/usb/imx-usb-phy.h" +#include "migration/vmstate.h" +#include "qemu/log.h" +#include "qemu/module.h" + +static const VMStateDescription vmstate_imx_usbphy = { + .name = TYPE_IMX_USBPHY, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(usbphy, IMXUSBPHYState, USBPHY_MAX), + VMSTATE_END_OF_LIST() + }, +}; + +static void imx_usbphy_softreset(IMXUSBPHYState *s) +{ + s->usbphy[USBPHY_PWD] = 0x001e1c00; + s->usbphy[USBPHY_TX] = 0x10060607; + s->usbphy[USBPHY_RX] = 0x00000000; + s->usbphy[USBPHY_CTRL] = 0xc0200000; +} + +static void imx_usbphy_reset(DeviceState *dev) +{ + IMXUSBPHYState *s = IMX_USBPHY(dev); + + s->usbphy[USBPHY_STATUS] = 0x00000000; + s->usbphy[USBPHY_DEBUG] = 0x7f180000; + s->usbphy[USBPHY_DEBUG0_STATUS] = 0x00000000; + s->usbphy[USBPHY_DEBUG1] = 0x00001000; + s->usbphy[USBPHY_VERSION] = 0x04020000; + + imx_usbphy_softreset(s); +} + +static uint64_t imx_usbphy_read(void *opaque, hwaddr offset, unsigned size) +{ + IMXUSBPHYState *s = (IMXUSBPHYState *)opaque; + uint32_t index = offset >> 2; + uint32_t value; + + switch (index) { + case USBPHY_PWD_SET: + case USBPHY_TX_SET: + case USBPHY_RX_SET: + case USBPHY_CTRL_SET: + case USBPHY_DEBUG_SET: + case USBPHY_DEBUG1_SET: + /* + * All REG_NAME_SET register access are in fact targeting the + * REG_NAME register. + */ + value = s->usbphy[index - 1]; + break; + case USBPHY_PWD_CLR: + case USBPHY_TX_CLR: + case USBPHY_RX_CLR: + case USBPHY_CTRL_CLR: + case USBPHY_DEBUG_CLR: + case USBPHY_DEBUG1_CLR: + /* + * All REG_NAME_CLR register access are in fact targeting the + * REG_NAME register. + */ + value = s->usbphy[index - 2]; + break; + case USBPHY_PWD_TOG: + case USBPHY_TX_TOG: + case USBPHY_RX_TOG: + case USBPHY_CTRL_TOG: + case USBPHY_DEBUG_TOG: + case USBPHY_DEBUG1_TOG: + /* + * All REG_NAME_TOG register access are in fact targeting the + * REG_NAME register. + */ + value = s->usbphy[index - 3]; + break; + default: + value = s->usbphy[index]; + break; + } + return (uint64_t)value; +} + +static void imx_usbphy_write(void *opaque, hwaddr offset, uint64_t value, + unsigned size) +{ + IMXUSBPHYState *s = (IMXUSBPHYState *)opaque; + uint32_t index = offset >> 2; + + switch (index) { + case USBPHY_CTRL: + s->usbphy[index] = value; + if (value & USBPHY_CTRL_SFTRST) { + imx_usbphy_softreset(s); + } + break; + case USBPHY_PWD: + case USBPHY_TX: + case USBPHY_RX: + case USBPHY_STATUS: + case USBPHY_DEBUG: + case USBPHY_DEBUG1: + s->usbphy[index] = value; + break; + case USBPHY_CTRL_SET: + s->usbphy[index - 1] |= value; + if (value & USBPHY_CTRL_SFTRST) { + imx_usbphy_softreset(s); + } + break; + case USBPHY_PWD_SET: + case USBPHY_TX_SET: + case USBPHY_RX_SET: + case USBPHY_DEBUG_SET: + case USBPHY_DEBUG1_SET: + /* + * All REG_NAME_SET register access are in fact targeting the + * REG_NAME register. So we change the value of the REG_NAME + * register, setting bits passed in the value. + */ + s->usbphy[index - 1] |= value; + break; + case USBPHY_PWD_CLR: + case USBPHY_TX_CLR: + case USBPHY_RX_CLR: + case USBPHY_CTRL_CLR: + case USBPHY_DEBUG_CLR: + case USBPHY_DEBUG1_CLR: + /* + * All REG_NAME_CLR register access are in fact targeting the + * REG_NAME register. So we change the value of the REG_NAME + * register, unsetting bits passed in the value. + */ + s->usbphy[index - 2] &= ~value; + break; + case USBPHY_CTRL_TOG: + s->usbphy[index - 3] ^= value; + if ((value & USBPHY_CTRL_SFTRST) && + (s->usbphy[index - 3] & USBPHY_CTRL_SFTRST)) { + imx_usbphy_softreset(s); + } + break; + case USBPHY_PWD_TOG: + case USBPHY_TX_TOG: + case USBPHY_RX_TOG: + case USBPHY_DEBUG_TOG: + case USBPHY_DEBUG1_TOG: + /* + * All REG_NAME_TOG register access are in fact targeting the + * REG_NAME register. So we change the value of the REG_NAME + * register, toggling bits passed in the value. + */ + s->usbphy[index - 3] ^= value; + break; + default: + /* Other registers are read-only */ + break; + } +} + +static const struct MemoryRegionOps imx_usbphy_ops = { + .read = imx_usbphy_read, + .write = imx_usbphy_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + /* + * Our device would not work correctly if the guest was doing + * unaligned access. This might not be a limitation on the real + * device but in practice there is no reason for a guest to access + * this device unaligned. + */ + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, +}; + +static void imx_usbphy_realize(DeviceState *dev, Error **errp) +{ + IMXUSBPHYState *s = IMX_USBPHY(dev); + + memory_region_init_io(&s->iomem, OBJECT(s), &imx_usbphy_ops, s, + "imx-usbphy", 0x1000); + sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem); +} + +static void imx_usbphy_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = imx_usbphy_reset; + dc->vmsd = &vmstate_imx_usbphy; + dc->desc = "i.MX USB PHY Module"; + dc->realize = imx_usbphy_realize; +} + +static const TypeInfo imx_usbphy_info = { + .name = TYPE_IMX_USBPHY, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(IMXUSBPHYState), + .class_init = imx_usbphy_class_init, +}; + +static void imx_usbphy_register_types(void) +{ + type_register_static(&imx_usbphy_info); +} + +type_init(imx_usbphy_register_types) diff --git a/hw/usb/quirks.c b/hw/usb/quirks.c index 38a9c5634a..23ea7a23ea 100644 --- a/hw/usb/quirks.c +++ b/hw/usb/quirks.c @@ -22,10 +22,10 @@ static bool usb_id_match(const struct usb_device_id *ids, uint8_t interface_protocol) { int i; - for (i = 0; ids[i].vendor_id != -1; i++) { + for (i = 0; ids[i].terminating_entry == 0; i++) { if (ids[i].vendor_id == vendor_id && ids[i].product_id == product_id && - (ids[i].interface_class == -1 || + (ids[i].interface_protocol_used == 0 || (ids[i].interface_class == interface_class && ids[i].interface_subclass == interface_subclass && ids[i].interface_protocol == interface_protocol))) { diff --git a/hw/usb/quirks.h b/hw/usb/quirks.h index 89480befd7..50ef2f9c2e 100644 --- a/hw/usb/quirks.h +++ b/hw/usb/quirks.h @@ -21,19 +21,23 @@ #include "quirks-pl2303-ids.h" struct usb_device_id { - int vendor_id; - int product_id; - int interface_class; - int interface_subclass; - int interface_protocol; + uint16_t vendor_id; + uint16_t product_id; + uint8_t interface_class; + uint8_t interface_subclass; + uint8_t interface_protocol; + uint8_t interface_protocol_used:1, + terminating_entry:1, + reserved:6; }; #define USB_DEVICE(vendor, product) \ - .vendor_id = vendor, .product_id = product, .interface_class = -1, + .vendor_id = vendor, .product_id = product, .interface_protocol_used = 0, #define USB_DEVICE_AND_INTERFACE_INFO(vend, prod, iclass, isubclass, iproto) \ .vendor_id = vend, .product_id = prod, .interface_class = iclass, \ - .interface_subclass = isubclass, .interface_protocol = iproto + .interface_subclass = isubclass, .interface_protocol = iproto, \ + .interface_protocol_used = 1 static const struct usb_device_id usbredir_raw_serial_ids[] = { /* @@ -206,7 +210,7 @@ static const struct usb_device_id usbredir_raw_serial_ids[] = { { USB_DEVICE(ADLINK_VENDOR_ID, ADLINK_ND6530_PRODUCT_ID) }, { USB_DEVICE(SMART_VENDOR_ID, SMART_PRODUCT_ID) }, - { USB_DEVICE(-1, -1) } /* Terminating Entry */ + { .terminating_entry = 1 } /* Terminating Entry */ }; static const struct usb_device_id usbredir_ftdi_serial_ids[] = { @@ -906,7 +910,7 @@ static const struct usb_device_id usbredir_ftdi_serial_ids[] = { { USB_DEVICE(FTDI_VID, FTDI_DISTORTEC_JTAG_LOCK_PICK_PID) }, { USB_DEVICE(FTDI_VID, FTDI_LUMEL_PD12_PID) }, - { USB_DEVICE(-1, -1) } /* Terminating Entry */ + { .terminating_entry = 1 } /* Terminating Entry */ }; #undef USB_DEVICE diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c index e0f5ca6f81..45d8b76218 100644 --- a/hw/usb/redirect.c +++ b/hw/usb/redirect.c @@ -113,6 +113,8 @@ struct USBRedirDevice { /* Properties */ CharBackend cs; bool enable_streams; + bool suppress_remote_wake; + bool in_write; uint8_t debug; int32_t bootindex; char *filter_str; @@ -290,6 +292,13 @@ static int usbredir_write(void *priv, uint8_t *data, int count) return 0; } + /* Recursion check */ + if (dev->in_write) { + DPRINTF("usbredir_write recursion\n"); + return 0; + } + dev->in_write = true; + r = qemu_chr_fe_write(&dev->cs, data, count); if (r < count) { if (!dev->watch) { @@ -300,6 +309,7 @@ static int usbredir_write(void *priv, uint8_t *data, int count) r = 0; } } + dev->in_write = false; return r; } @@ -1354,7 +1364,7 @@ static void usbredir_chardev_read(void *opaque, const uint8_t *buf, int size) usbredirparser_do_write(dev->parser); } -static void usbredir_chardev_event(void *opaque, int event) +static void usbredir_chardev_event(void *opaque, QEMUChrEvent event) { USBRedirDevice *dev = opaque; @@ -1370,6 +1380,11 @@ static void usbredir_chardev_event(void *opaque, int event) DPRINTF("chardev close\n"); qemu_bh_schedule(dev->chardev_close_bh); break; + case CHR_EVENT_BREAK: + case CHR_EVENT_MUX_IN: + case CHR_EVENT_MUX_OUT: + /* Ignore */ + break; } } @@ -1989,6 +2004,23 @@ static void usbredir_control_packet(void *priv, uint64_t id, memcpy(dev->dev.data_buf, data, data_len); } p->actual_length = len; + /* + * If this is GET_DESCRIPTOR request for configuration descriptor, + * remove 'remote wakeup' flag from it to prevent idle power down + * in Windows guest + */ + if (dev->suppress_remote_wake && + control_packet->requesttype == USB_DIR_IN && + control_packet->request == USB_REQ_GET_DESCRIPTOR && + control_packet->value == (USB_DT_CONFIG << 8) && + control_packet->index == 0 && + /* bmAttributes field of config descriptor */ + len > 7 && (dev->dev.data_buf[7] & USB_CFG_ATT_WAKEUP)) { + DPRINTF("Removed remote wake %04X:%04X\n", + dev->device_info.vendor_id, + dev->device_info.product_id); + dev->dev.data_buf[7] &= ~USB_CFG_ATT_WAKEUP; + } usb_generic_async_ctrl_complete(&dev->dev, p); } free(data); @@ -2530,6 +2562,8 @@ static Property usbredir_properties[] = { DEFINE_PROP_UINT8("debug", USBRedirDevice, debug, usbredirparser_warning), DEFINE_PROP_STRING("filter", USBRedirDevice, filter_str), DEFINE_PROP_BOOL("streams", USBRedirDevice, enable_streams, true), + DEFINE_PROP_BOOL("suppress-remote-wake", USBRedirDevice, + suppress_remote_wake, true), DEFINE_PROP_END_OF_LIST(), }; @@ -2550,7 +2584,7 @@ static void usbredir_class_initfn(ObjectClass *klass, void *data) uc->alloc_streams = usbredir_alloc_streams; uc->free_streams = usbredir_free_streams; dc->vmsd = &usbredir_vmstate; - dc->props = usbredir_properties; + device_class_set_props(dc, usbredir_properties); set_bit(DEVICE_CATEGORY_MISC, dc->categories); } diff --git a/hw/usb/trace-events b/hw/usb/trace-events index 2d3713351c..1c24d82c09 100644 --- a/hw/usb/trace-events +++ b/hw/usb/trace-events @@ -266,3 +266,4 @@ usb_host_parse_config(int bus, int addr, int value, int active) "dev %d:%d, valu usb_host_parse_interface(int bus, int addr, int num, int alt, int active) "dev %d:%d, num %d, alt %d, active %d" usb_host_parse_endpoint(int bus, int addr, int ep, const char *dir, const char *type, int active) "dev %d:%d, ep %d, %s, %s, active %d" usb_host_parse_error(int bus, int addr, const char *errmsg) "dev %d:%d, msg %s" +usb_host_remote_wakeup_removed(int bus, int addr) "dev %d:%d" diff --git a/hw/usb/xen-usb.c b/hw/usb/xen-usb.c index 1fc2f32ce9..961190d0f7 100644 --- a/hw/usb/xen-usb.c +++ b/hw/usb/xen-usb.c @@ -347,13 +347,11 @@ static int32_t usbback_xlat_status(int status) return -ESHUTDOWN; } -static void usbback_packet_complete(USBPacket *packet) +static void usbback_packet_complete(struct usbback_req *usbback_req) { - struct usbback_req *usbback_req; + USBPacket *packet = &usbback_req->packet; int32_t status; - usbback_req = container_of(packet, struct usbback_req, packet); - QTAILQ_REMOVE(&usbback_req->stub->submit_q, usbback_req, q); status = usbback_xlat_status(packet->status); @@ -566,7 +564,7 @@ static void usbback_dispatch(struct usbback_req *usbback_req) usb_handle_packet(usbback_req->stub->dev, &usbback_req->packet); if (usbback_req->packet.status != USB_RET_ASYNC) { - usbback_packet_complete(&usbback_req->packet); + usbback_packet_complete(usbback_req); } return; @@ -993,7 +991,7 @@ static void xen_bus_complete(USBPort *port, USBPacket *packet) usbif = usbback_req->usbif; TR_REQ(&usbif->xendev, "\n"); - usbback_packet_complete(packet); + usbback_packet_complete(usbback_req); } static USBPortOps xen_usb_port_ops = { 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/ap.c b/hw/vfio/ap.c index da6a20669d..8649ac15f9 100644 --- a/hw/vfio/ap.c +++ b/hw/vfio/ap.c @@ -89,14 +89,13 @@ static void vfio_ap_realize(DeviceState *dev, Error **errp) { int ret; char *mdevid; - Error *local_err = NULL; VFIOGroup *vfio_group; APDevice *apdev = AP_DEVICE(dev); VFIOAPDevice *vapdev = VFIO_AP_DEVICE(apdev); - vfio_group = vfio_ap_get_group(vapdev, &local_err); + vfio_group = vfio_ap_get_group(vapdev, errp); if (!vfio_group) { - goto out_err; + return; } vapdev->vdev.ops = &vfio_ap_ops; @@ -113,7 +112,7 @@ static void vfio_ap_realize(DeviceState *dev, Error **errp) */ vapdev->vdev.balloon_allowed = true; - ret = vfio_get_device(vfio_group, mdevid, &vapdev->vdev, &local_err); + ret = vfio_get_device(vfio_group, mdevid, &vapdev->vdev, errp); if (ret) { goto out_get_dev_err; } @@ -123,8 +122,6 @@ static void vfio_ap_realize(DeviceState *dev, Error **errp) out_get_dev_err: vfio_ap_put_device(vapdev); vfio_put_group(vfio_group); -out_err: - error_propagate(errp, local_err); } static void vfio_ap_unrealize(DeviceState *dev, Error **errp) @@ -164,7 +161,7 @@ static void vfio_ap_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->props = vfio_ap_properties; + device_class_set_props(dc, vfio_ap_properties); dc->vmsd = &vfio_ap_vmstate; dc->desc = "VFIO-based AP device assignment"; set_bit(DEVICE_CATEGORY_MISC, dc->categories); diff --git a/hw/vfio/ccw.c b/hw/vfio/ccw.c index 6863f6c69f..50cc2ec75c 100644 --- a/hw/vfio/ccw.c +++ b/hw/vfio/ccw.c @@ -102,7 +102,7 @@ again: if (errno == EAGAIN) { goto again; } - error_report("vfio-ccw: wirte I/O region failed with errno=%d", errno); + error_report("vfio-ccw: write I/O region failed with errno=%d", errno); ret = -errno; } else { ret = region->ret_code; @@ -561,7 +561,7 @@ static void vfio_ccw_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); S390CCWDeviceClass *cdc = S390_CCW_DEVICE_CLASS(klass); - dc->props = vfio_ccw_properties; + device_class_set_props(dc, vfio_ccw_properties); dc->vmsd = &vfio_ccw_vmstate; dc->desc = "VFIO-based subchannel assignment"; set_bit(DEVICE_CATEGORY_MISC, dc->categories); diff --git a/hw/vfio/common.c b/hw/vfio/common.c index 5ca11488d6..0b3593b3c0 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -319,7 +319,7 @@ static int vfio_dma_unmap(VFIOContainer *container, unmap.size -= 1ULL << ctz64(container->pgsizes); continue; } - error_report("VFIO_UNMAP_DMA: %d", -errno); + error_report("VFIO_UNMAP_DMA failed: %s", strerror(errno)); return -errno; } @@ -352,7 +352,7 @@ static int vfio_dma_map(VFIOContainer *container, hwaddr iova, return 0; } - error_report("VFIO_MAP_DMA: %d", -errno); + error_report("VFIO_MAP_DMA failed: %s", strerror(errno)); return -errno; } diff --git a/hw/vfio/display.c b/hw/vfio/display.c index a5a608c5b2..f4977c66e1 100644 --- a/hw/vfio/display.c +++ b/hw/vfio/display.c @@ -287,7 +287,7 @@ static void vfio_display_dmabuf_update(void *opaque) VFIOPCIDevice *vdev = opaque; VFIODisplay *dpy = vdev->dpy; VFIODMABuf *primary, *cursor; - bool free_bufs = false, new_cursor = false;; + bool free_bufs = false, new_cursor = false; primary = vfio_display_get_dmabuf(vdev, DRM_PLANE_TYPE_PRIMARY); if (primary == NULL) { 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 + * + * 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 */ /* @@ -1298,519 +1201,6 @@ int vfio_pci_igd_opregion_init(VFIOPCIDevice *vdev, return 0; } -/* - * 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. */ @@ -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.c b/hw/vfio/pci.c index 2d40b396f2..5e75a95129 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -3076,7 +3076,9 @@ static void vfio_exitfn(PCIDevice *pdev) vfio_unregister_req_notifier(vdev); vfio_unregister_err_notifier(vdev); pci_device_set_intx_routing_notifier(&vdev->pdev, NULL); - kvm_irqchip_remove_change_notifier(&vdev->irqchip_change_notifier); + if (vdev->irqchip_change_notifier.notify) { + kvm_irqchip_remove_change_notifier(&vdev->irqchip_change_notifier); + } vfio_disable_interrupts(vdev); if (vdev->intx.mmap_timer) { timer_free(vdev->intx.mmap_timer); @@ -3197,7 +3199,7 @@ static void vfio_pci_dev_class_init(ObjectClass *klass, void *data) PCIDeviceClass *pdc = PCI_DEVICE_CLASS(klass); dc->reset = vfio_pci_reset; - dc->props = vfio_pci_dev_properties; + device_class_set_props(dc, vfio_pci_dev_properties); dc->desc = "VFIO-based PCI device assignment"; set_bit(DEVICE_CATEGORY_MISC, dc->categories); pdc->realize = vfio_realize; @@ -3229,7 +3231,7 @@ static void vfio_pci_nohotplug_dev_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->props = vfio_pci_dev_nohotplug_properties; + device_class_set_props(dc, vfio_pci_dev_nohotplug_properties); dc->hotpluggable = false; } 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/hw/vfio/platform.c b/hw/vfio/platform.c index d7598c6152..6b2952c034 100644 --- a/hw/vfio/platform.c +++ b/hw/vfio/platform.c @@ -699,7 +699,7 @@ static void vfio_platform_class_init(ObjectClass *klass, void *data) SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass); dc->realize = vfio_platform_realize; - dc->props = vfio_platform_dev_properties; + device_class_set_props(dc, vfio_platform_dev_properties); dc->vmsd = &vfio_platform_vmstate; dc->desc = "VFIO-based platform device assignment"; sbc->connect_irq_notifier = vfio_start_irqfd_injection; diff --git a/hw/vfio/spapr.c b/hw/vfio/spapr.c index 33692fc86f..2900bd1941 100644 --- a/hw/vfio/spapr.c +++ b/hw/vfio/spapr.c @@ -147,7 +147,7 @@ int vfio_spapr_create_window(VFIOContainer *container, { int ret = 0; IOMMUMemoryRegion *iommu_mr = IOMMU_MEMORY_REGION(section->mr); - uint64_t pagesize = memory_region_iommu_get_min_page_size(iommu_mr); + uint64_t pagesize = memory_region_iommu_get_min_page_size(iommu_mr), pgmask; unsigned entries, bits_total, bits_per_level, max_levels; struct vfio_iommu_spapr_tce_create create = { .argsz = sizeof(create) }; long rampagesize = qemu_minrampagesize(); @@ -159,8 +159,8 @@ int vfio_spapr_create_window(VFIOContainer *container, if (pagesize > rampagesize) { pagesize = rampagesize; } - pagesize = 1ULL << (63 - clz64(container->pgsizes & - (pagesize | (pagesize - 1)))); + pgmask = container->pgsizes & (pagesize | (pagesize - 1)); + pagesize = pgmask ? (1ULL << (63 - clz64(pgmask))) : 0; if (!pagesize) { error_report("Host doesn't support page size 0x%"PRIx64 ", the supported mask is 0x%lx", diff --git a/hw/virtio/Kconfig b/hw/virtio/Kconfig index 3724ff8bac..83122424fa 100644 --- a/hw/virtio/Kconfig +++ b/hw/virtio/Kconfig @@ -1,3 +1,6 @@ +config VHOST + bool + config VIRTIO bool @@ -6,6 +9,11 @@ config VIRTIO_RNG default y depends on VIRTIO +config VIRTIO_IOMMU + bool + default y + depends on PCI && VIRTIO + config VIRTIO_PCI bool default y if PCI_DEVICES diff --git a/hw/virtio/Makefile.objs b/hw/virtio/Makefile.objs index e2f70fbb89..4e4d39a0a4 100644 --- a/hw/virtio/Makefile.objs +++ b/hw/virtio/Makefile.objs @@ -2,8 +2,8 @@ ifeq ($(CONFIG_VIRTIO),y) common-obj-y += virtio-bus.o obj-y += virtio.o -obj-$(call lor,$(CONFIG_VHOST_USER),$(CONFIG_VHOST_KERNEL)) += vhost.o vhost-backend.o -common-obj-$(call lnot,$(call lor,$(CONFIG_VHOST_USER),$(CONFIG_VHOST_KERNEL))) += vhost-stub.o +obj-$(CONFIG_VHOST) += vhost.o vhost-backend.o +common-obj-$(call lnot,$(CONFIG_VHOST)) += vhost-stub.o obj-$(CONFIG_VHOST_USER) += vhost-user.o common-obj-$(CONFIG_VIRTIO_RNG) += virtio-rng.o @@ -16,6 +16,7 @@ obj-$(call land,$(CONFIG_VIRTIO_CRYPTO),$(CONFIG_VIRTIO_PCI)) += virtio-crypto-p obj-$(CONFIG_VIRTIO_PMEM) += virtio-pmem.o common-obj-$(call land,$(CONFIG_VIRTIO_PMEM),$(CONFIG_VIRTIO_PCI)) += virtio-pmem-pci.o obj-$(call land,$(CONFIG_VHOST_USER_FS),$(CONFIG_VIRTIO_PCI)) += vhost-user-fs-pci.o +obj-$(CONFIG_VIRTIO_IOMMU) += virtio-iommu.o obj-$(CONFIG_VHOST_VSOCK) += vhost-vsock.o ifeq ($(CONFIG_VIRTIO_PCI),y) @@ -28,6 +29,7 @@ obj-$(CONFIG_VIRTIO_INPUT_HOST) += virtio-input-host-pci.o obj-$(CONFIG_VIRTIO_INPUT) += virtio-input-pci.o obj-$(CONFIG_VIRTIO_RNG) += virtio-rng-pci.o obj-$(CONFIG_VIRTIO_BALLOON) += virtio-balloon-pci.o +obj-$(CONFIG_VIRTIO_IOMMU) += virtio-iommu-pci.o obj-$(CONFIG_VIRTIO_9P) += virtio-9p-pci.o obj-$(CONFIG_VIRTIO_SCSI) += virtio-scsi-pci.o obj-$(CONFIG_VIRTIO_BLK) += virtio-blk-pci.o diff --git a/hw/virtio/trace-events b/hw/virtio/trace-events index e28ba48da6..e83500bee9 100644 --- a/hw/virtio/trace-events +++ b/hw/virtio/trace-events @@ -53,3 +53,23 @@ virtio_mmio_write_offset(uint64_t offset, uint64_t value) "virtio_mmio_write off virtio_mmio_guest_page(uint64_t size, int shift) "guest page size 0x%" PRIx64 " shift %d" virtio_mmio_queue_write(uint64_t value, int max_size) "mmio_queue write 0x%" PRIx64 " max %d" virtio_mmio_setting_irq(int level) "virtio_mmio setting IRQ %d" + +# hw/virtio/virtio-iommu.c +virtio_iommu_device_reset(void) "reset!" +virtio_iommu_get_features(uint64_t features) "device supports features=0x%"PRIx64 +virtio_iommu_device_status(uint8_t status) "driver status = %d" +virtio_iommu_get_config(uint64_t page_size_mask, uint64_t start, uint64_t end, uint32_t domain_range, uint32_t probe_size) "page_size_mask=0x%"PRIx64" start=0x%"PRIx64" end=0x%"PRIx64" domain_range=%d probe_size=0x%x" +virtio_iommu_set_config(uint64_t page_size_mask, uint64_t start, uint64_t end, uint32_t domain_range, uint32_t probe_size) "page_size_mask=0x%"PRIx64" start=0x%"PRIx64" end=0x%"PRIx64" domain_bits=%d probe_size=0x%x" +virtio_iommu_attach(uint32_t domain_id, uint32_t ep_id) "domain=%d endpoint=%d" +virtio_iommu_detach(uint32_t domain_id, uint32_t ep_id) "domain=%d endpoint=%d" +virtio_iommu_map(uint32_t domain_id, uint64_t virt_start, uint64_t virt_end, uint64_t phys_start, uint32_t flags) "domain=%d virt_start=0x%"PRIx64" virt_end=0x%"PRIx64 " phys_start=0x%"PRIx64" flags=%d" +virtio_iommu_unmap(uint32_t domain_id, uint64_t virt_start, uint64_t virt_end) "domain=%d virt_start=0x%"PRIx64" virt_end=0x%"PRIx64 +virtio_iommu_unmap_done(uint32_t domain_id, uint64_t virt_start, uint64_t virt_end) "domain=%d virt_start=0x%"PRIx64" virt_end=0x%"PRIx64 +virtio_iommu_translate(const char *name, uint32_t rid, uint64_t iova, int flag) "mr=%s rid=%d addr=0x%"PRIx64" flag=%d" +virtio_iommu_init_iommu_mr(char *iommu_mr) "init %s" +virtio_iommu_get_endpoint(uint32_t ep_id) "Alloc endpoint=%d" +virtio_iommu_put_endpoint(uint32_t ep_id) "Free endpoint=%d" +virtio_iommu_get_domain(uint32_t domain_id) "Alloc domain=%d" +virtio_iommu_put_domain(uint32_t domain_id) "Free domain=%d" +virtio_iommu_translate_out(uint64_t virt_addr, uint64_t phys_addr, uint32_t sid) "0x%"PRIx64" -> 0x%"PRIx64 " for sid=%d" +virtio_iommu_report_fault(uint8_t reason, uint32_t flags, uint32_t endpoint, uint64_t addr) "FAULT reason=%d flags=%d endpoint=%d address =0x%"PRIx64 diff --git a/hw/virtio/vhost-scsi-pci.c b/hw/virtio/vhost-scsi-pci.c index e8dfbfc60f..5dce640eaf 100644 --- a/hw/virtio/vhost-scsi-pci.c +++ b/hw/virtio/vhost-scsi-pci.c @@ -64,7 +64,7 @@ static void vhost_scsi_pci_class_init(ObjectClass *klass, void *data) PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass); k->realize = vhost_scsi_pci_realize; set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); - dc->props = vhost_scsi_pci_properties; + device_class_set_props(dc, vhost_scsi_pci_properties); pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_SCSI; pcidev_k->revision = 0x00; diff --git a/hw/virtio/vhost-user-blk-pci.c b/hw/virtio/vhost-user-blk-pci.c index 1dc834a3ff..8d3d766427 100644 --- a/hw/virtio/vhost-user-blk-pci.c +++ b/hw/virtio/vhost-user-blk-pci.c @@ -69,7 +69,7 @@ static void vhost_user_blk_pci_class_init(ObjectClass *klass, void *data) PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass); set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); - dc->props = vhost_user_blk_pci_properties; + device_class_set_props(dc, vhost_user_blk_pci_properties); k->realize = vhost_user_blk_pci_realize; pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_BLOCK; diff --git a/hw/virtio/vhost-user-fs-pci.c b/hw/virtio/vhost-user-fs-pci.c index 933a3f265b..ae36f1172d 100644 --- a/hw/virtio/vhost-user-fs-pci.c +++ b/hw/virtio/vhost-user-fs-pci.c @@ -40,7 +40,8 @@ static void vhost_user_fs_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) DeviceState *vdev = DEVICE(&dev->vdev); if (vpci_dev->nvectors == DEV_NVECTORS_UNSPECIFIED) { - vpci_dev->nvectors = dev->vdev.conf.num_request_queues + 1; + /* Also reserve config change and hiprio queue vectors */ + vpci_dev->nvectors = dev->vdev.conf.num_request_queues + 2; } qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus)); @@ -54,7 +55,7 @@ static void vhost_user_fs_pci_class_init(ObjectClass *klass, void *data) PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass); k->realize = vhost_user_fs_pci_realize; set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); - dc->props = vhost_user_fs_pci_properties; + device_class_set_props(dc, vhost_user_fs_pci_properties); pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; pcidev_k->device_id = 0; /* Set by virtio-pci based on virtio id */ pcidev_k->revision = 0x00; diff --git a/hw/virtio/vhost-user-fs.c b/hw/virtio/vhost-user-fs.c index f0df7f4746..6136768875 100644 --- a/hw/virtio/vhost-user-fs.c +++ b/hw/virtio/vhost-user-fs.c @@ -209,11 +209,12 @@ static void vuf_device_realize(DeviceState *dev, Error **errp) sizeof(struct virtio_fs_config)); /* Hiprio queue */ - virtio_add_queue(vdev, fs->conf.queue_size, vuf_handle_output); + fs->hiprio_vq = virtio_add_queue(vdev, fs->conf.queue_size, vuf_handle_output); /* Request queues */ + fs->req_vqs = g_new(VirtQueue *, fs->conf.num_request_queues); for (i = 0; i < fs->conf.num_request_queues; i++) { - virtio_add_queue(vdev, fs->conf.queue_size, vuf_handle_output); + fs->req_vqs[i] = virtio_add_queue(vdev, fs->conf.queue_size, vuf_handle_output); } /* 1 high prio queue, plus the number configured */ @@ -230,6 +231,11 @@ static void vuf_device_realize(DeviceState *dev, Error **errp) err_virtio: vhost_user_cleanup(&fs->vhost_user); + virtio_delete_queue(fs->hiprio_vq); + for (i = 0; i < fs->conf.num_request_queues; i++) { + virtio_delete_queue(fs->req_vqs[i]); + } + g_free(fs->req_vqs); virtio_cleanup(vdev); g_free(fs->vhost_dev.vqs); return; @@ -239,6 +245,7 @@ static void vuf_device_unrealize(DeviceState *dev, Error **errp) { VirtIODevice *vdev = VIRTIO_DEVICE(dev); VHostUserFS *fs = VHOST_USER_FS(dev); + int i; /* This will stop vhost backend if appropriate. */ vuf_set_status(vdev, 0); @@ -247,6 +254,11 @@ static void vuf_device_unrealize(DeviceState *dev, Error **errp) vhost_user_cleanup(&fs->vhost_user); + virtio_delete_queue(fs->hiprio_vq); + for (i = 0; i < fs->conf.num_request_queues; i++) { + virtio_delete_queue(fs->req_vqs[i]); + } + g_free(fs->req_vqs); virtio_cleanup(vdev); g_free(fs->vhost_dev.vqs); fs->vhost_dev.vqs = NULL; @@ -263,7 +275,6 @@ static Property vuf_properties[] = { DEFINE_PROP_UINT16("num-request-queues", VHostUserFS, conf.num_request_queues, 1), DEFINE_PROP_UINT16("queue-size", VHostUserFS, conf.queue_size, 128), - DEFINE_PROP_STRING("vhostfd", VHostUserFS, conf.vhostfd), DEFINE_PROP_END_OF_LIST(), }; @@ -272,7 +283,7 @@ static void vuf_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); - dc->props = vuf_properties; + device_class_set_props(dc, vuf_properties); dc->vmsd = &vuf_vmstate; set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); vdc->realize = vuf_device_realize; diff --git a/hw/virtio/vhost-user-scsi-pci.c b/hw/virtio/vhost-user-scsi-pci.c index ff13af7030..32febb2daa 100644 --- a/hw/virtio/vhost-user-scsi-pci.c +++ b/hw/virtio/vhost-user-scsi-pci.c @@ -70,7 +70,7 @@ static void vhost_user_scsi_pci_class_init(ObjectClass *klass, void *data) PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass); k->realize = vhost_user_scsi_pci_realize; set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); - dc->props = vhost_user_scsi_pci_properties; + device_class_set_props(dc, vhost_user_scsi_pci_properties); pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_SCSI; pcidev_k->revision = 0x00; diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c index 02a9b25199..08e7e63790 100644 --- a/hw/virtio/vhost-user.c +++ b/hw/virtio/vhost-user.c @@ -58,6 +58,7 @@ enum VhostUserProtocolFeature { VHOST_USER_PROTOCOL_F_SLAVE_SEND_FD = 10, VHOST_USER_PROTOCOL_F_HOST_NOTIFIER = 11, VHOST_USER_PROTOCOL_F_INFLIGHT_SHMFD = 12, + VHOST_USER_PROTOCOL_F_RESET_DEVICE = 13, VHOST_USER_PROTOCOL_F_MAX }; @@ -98,6 +99,7 @@ typedef enum VhostUserRequest { VHOST_USER_GET_INFLIGHT_FD = 31, VHOST_USER_SET_INFLIGHT_FD = 32, VHOST_USER_GPU_SET_SOCKET = 33, + VHOST_USER_RESET_DEVICE = 34, VHOST_USER_MAX } VhostUserRequest; @@ -441,6 +443,7 @@ static int vhost_user_set_mem_table_postcopy(struct vhost_dev *dev, &offset); fd = memory_region_get_fd(mr); if (fd > 0) { + assert(fd_num < VHOST_MEMORY_MAX_NREGIONS); trace_vhost_user_set_mem_table_withfd(fd_num, mr->name, reg->memory_size, reg->guest_phys_addr, @@ -453,7 +456,6 @@ static int vhost_user_set_mem_table_postcopy(struct vhost_dev *dev, msg.payload.memory.regions[fd_num].guest_phys_addr = reg->guest_phys_addr; msg.payload.memory.regions[fd_num].mmap_offset = offset; - assert(fd_num < VHOST_MEMORY_MAX_NREGIONS); fds[fd_num++] = fd; } else { u->region_rb_offset[i] = 0; @@ -890,10 +892,14 @@ static int vhost_user_set_owner(struct vhost_dev *dev) static int vhost_user_reset_device(struct vhost_dev *dev) { VhostUserMsg msg = { - .hdr.request = VHOST_USER_RESET_OWNER, .hdr.flags = VHOST_USER_VERSION, }; + msg.hdr.request = virtio_has_feature(dev->protocol_features, + VHOST_USER_PROTOCOL_F_RESET_DEVICE) + ? VHOST_USER_RESET_DEVICE + : VHOST_USER_RESET_OWNER; + if (vhost_user_write(dev, &msg, NULL, 0) < 0) { return -1; } @@ -1055,7 +1061,7 @@ static void slave_read(void *opaque) fd[0]); break; default: - error_report("Received unexpected msg type."); + error_report("Received unexpected msg type: %d.", hdr.request); ret = -EINVAL; } @@ -1452,9 +1458,11 @@ static int vhost_user_backend_init(struct vhost_dev *dev, void *opaque) "VHOST_USER_PROTOCOL_F_LOG_SHMFD feature."); } - err = vhost_setup_slave_channel(dev); - if (err < 0) { - return err; + if (dev->vq_index == 0) { + err = vhost_setup_slave_channel(dev); + if (err < 0) { + return err; + } } u->postcopy_notifier.notify = vhost_user_postcopy_notifier; diff --git a/hw/virtio/vhost-vsock-pci.c b/hw/virtio/vhost-vsock-pci.c index 4ca097ffff..01effe3d52 100644 --- a/hw/virtio/vhost-vsock-pci.c +++ b/hw/virtio/vhost-vsock-pci.c @@ -55,7 +55,7 @@ static void vhost_vsock_pci_class_init(ObjectClass *klass, void *data) PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass); k->realize = vhost_vsock_pci_realize; set_bit(DEVICE_CATEGORY_MISC, dc->categories); - dc->props = vhost_vsock_pci_properties; + device_class_set_props(dc, vhost_vsock_pci_properties); pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_VSOCK; pcidev_k->revision = 0x00; diff --git a/hw/virtio/vhost-vsock.c b/hw/virtio/vhost-vsock.c index f5744363a8..09b6b07f94 100644 --- a/hw/virtio/vhost-vsock.c +++ b/hw/virtio/vhost-vsock.c @@ -325,7 +325,7 @@ static void vhost_vsock_device_realize(DeviceState *dev, Error **errp) } else { vhostfd = open("/dev/vhost-vsock", O_RDWR); if (vhostfd < 0) { - error_setg_errno(errp, -errno, + error_setg_errno(errp, errno, "vhost-vsock: failed to open vhost device"); return; } @@ -335,8 +335,10 @@ static void vhost_vsock_device_realize(DeviceState *dev, Error **errp) sizeof(struct virtio_vsock_config)); /* Receive and transmit queues belong to vhost */ - virtio_add_queue(vdev, VHOST_VSOCK_QUEUE_SIZE, vhost_vsock_handle_output); - virtio_add_queue(vdev, VHOST_VSOCK_QUEUE_SIZE, vhost_vsock_handle_output); + vsock->recv_vq = virtio_add_queue(vdev, VHOST_VSOCK_QUEUE_SIZE, + vhost_vsock_handle_output); + vsock->trans_vq = virtio_add_queue(vdev, VHOST_VSOCK_QUEUE_SIZE, + vhost_vsock_handle_output); /* The event queue belongs to QEMU */ vsock->event_vq = virtio_add_queue(vdev, VHOST_VSOCK_QUEUE_SIZE, @@ -362,9 +364,16 @@ static void vhost_vsock_device_realize(DeviceState *dev, Error **errp) err_vhost_dev: vhost_dev_cleanup(&vsock->vhost_dev); + /* vhost_dev_cleanup() closes the vhostfd passed to vhost_dev_init() */ + vhostfd = -1; err_virtio: + virtio_delete_queue(vsock->recv_vq); + virtio_delete_queue(vsock->trans_vq); + virtio_delete_queue(vsock->event_vq); virtio_cleanup(vdev); - close(vhostfd); + if (vhostfd >= 0) { + close(vhostfd); + } return; } @@ -379,6 +388,9 @@ static void vhost_vsock_device_unrealize(DeviceState *dev, Error **errp) vhost_vsock_set_status(vdev, 0); vhost_dev_cleanup(&vsock->vhost_dev); + virtio_delete_queue(vsock->recv_vq); + virtio_delete_queue(vsock->trans_vq); + virtio_delete_queue(vsock->event_vq); virtio_cleanup(vdev); } @@ -393,7 +405,7 @@ static void vhost_vsock_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); - dc->props = vhost_vsock_properties; + device_class_set_props(dc, vhost_vsock_properties); dc->vmsd = &vmstate_virtio_vhost_vsock; set_bit(DEVICE_CATEGORY_MISC, dc->categories); vdc->realize = vhost_vsock_device_realize; diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c index 4da0d5a6c5..01ebe12f28 100644 --- a/hw/virtio/vhost.c +++ b/hw/virtio/vhost.c @@ -290,11 +290,18 @@ static int vhost_dev_has_iommu(struct vhost_dev *dev) { VirtIODevice *vdev = dev->vdev; - return virtio_host_has_feature(vdev, VIRTIO_F_IOMMU_PLATFORM); + /* + * For vhost, VIRTIO_F_IOMMU_PLATFORM means the backend support + * incremental memory mapping API via IOTLB API. For platform that + * does not have IOMMU, there's no need to enable this feature + * which may cause unnecessary IOTLB miss/update trnasactions. + */ + return vdev->dma_as != &address_space_memory && + virtio_host_has_feature(vdev, VIRTIO_F_IOMMU_PLATFORM); } static void *vhost_memory_map(struct vhost_dev *dev, hwaddr addr, - hwaddr *plen, int is_write) + hwaddr *plen, bool is_write) { if (!vhost_dev_has_iommu(dev)) { return cpu_physical_memory_map(addr, plen, is_write); @@ -547,26 +554,28 @@ static void vhost_region_add_section(struct vhost_dev *dev, uintptr_t mrs_host = (uintptr_t)memory_region_get_ram_ptr(section->mr) + section->offset_within_region; RAMBlock *mrs_rb = section->mr->ram_block; - size_t mrs_page = qemu_ram_pagesize(mrs_rb); trace_vhost_region_add_section(section->mr->name, mrs_gpa, mrs_size, mrs_host); - /* Round the section to it's page size */ - /* First align the start down to a page boundary */ - uint64_t alignage = mrs_host & (mrs_page - 1); - if (alignage) { - mrs_host -= alignage; - mrs_size += alignage; - mrs_gpa -= alignage; + if (dev->vhost_ops->backend_type == VHOST_BACKEND_TYPE_USER) { + /* Round the section to it's page size */ + /* First align the start down to a page boundary */ + size_t mrs_page = qemu_ram_pagesize(mrs_rb); + uint64_t alignage = mrs_host & (mrs_page - 1); + if (alignage) { + mrs_host -= alignage; + mrs_size += alignage; + mrs_gpa -= alignage; + } + /* Now align the size up to a page boundary */ + alignage = mrs_size & (mrs_page - 1); + if (alignage) { + mrs_size += mrs_page - alignage; + } + trace_vhost_region_add_section_aligned(section->mr->name, mrs_gpa, + mrs_size, mrs_host); } - /* Now align the size up to a page boundary */ - alignage = mrs_size & (mrs_page - 1); - if (alignage) { - mrs_size += mrs_page - alignage; - } - trace_vhost_region_add_section_aligned(section->mr->name, mrs_gpa, mrs_size, - mrs_host); if (dev->n_tmp_sections) { /* Since we already have at least one section, lets see if @@ -590,9 +599,10 @@ static void vhost_region_add_section(struct vhost_dev *dev, * match up in the same RAMBlock if they do. */ if (mrs_gpa < prev_gpa_start) { - error_report("%s:Section rounded to %"PRIx64 - " prior to previous %"PRIx64, - __func__, mrs_gpa, prev_gpa_start); + error_report("%s:Section '%s' rounded to %"PRIx64 + " prior to previous '%s' %"PRIx64, + __func__, section->mr->name, mrs_gpa, + prev_sec->mr->name, prev_gpa_start); /* A way to cleanly fail here would be better */ return; } @@ -762,6 +772,9 @@ static int vhost_dev_set_features(struct vhost_dev *dev, if (enable_log) { features |= 0x1ULL << VHOST_F_LOG_ALL; } + if (!vhost_dev_has_iommu(dev)) { + features &= ~(0x1ULL << VIRTIO_F_IOMMU_PLATFORM); + } r = dev->vhost_ops->vhost_set_features(dev, features); if (r < 0) { VHOST_OPS_DEBUG("vhost_set_features failed"); @@ -1009,21 +1022,21 @@ static int vhost_virtqueue_start(struct vhost_dev *dev, vq->desc_size = s = l = virtio_queue_get_desc_size(vdev, idx); vq->desc_phys = a; - vq->desc = vhost_memory_map(dev, a, &l, 0); + vq->desc = vhost_memory_map(dev, a, &l, false); if (!vq->desc || l != s) { r = -ENOMEM; goto fail_alloc_desc; } vq->avail_size = s = l = virtio_queue_get_avail_size(vdev, idx); vq->avail_phys = a = virtio_queue_get_avail_addr(vdev, idx); - vq->avail = vhost_memory_map(dev, a, &l, 0); + vq->avail = vhost_memory_map(dev, a, &l, false); if (!vq->avail || l != s) { r = -ENOMEM; goto fail_alloc_avail; } vq->used_size = s = l = virtio_queue_get_used_size(vdev, idx); vq->used_phys = a = virtio_queue_get_used_addr(vdev, idx); - vq->used = vhost_memory_map(dev, a, &l, 1); + vq->used = vhost_memory_map(dev, a, &l, true); if (!vq->used || l != s) { r = -ENOMEM; goto fail_alloc_used; diff --git a/hw/virtio/virtio-9p-pci.c b/hw/virtio/virtio-9p-pci.c index 22a183cca7..6507ce340b 100644 --- a/hw/virtio/virtio-9p-pci.c +++ b/hw/virtio/virtio-9p-pci.c @@ -61,7 +61,7 @@ static void virtio_9p_pci_class_init(ObjectClass *klass, void *data) pcidev_k->revision = VIRTIO_PCI_ABI_VERSION; pcidev_k->class_id = 0x2; set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); - dc->props = virtio_9p_pci_properties; + device_class_set_props(dc, virtio_9p_pci_properties); } static void virtio_9p_pci_instance_init(Object *obj) diff --git a/hw/virtio/virtio-balloon-pci.c b/hw/virtio/virtio-balloon-pci.c index 69ca057911..56962aeb43 100644 --- a/hw/virtio/virtio-balloon-pci.c +++ b/hw/virtio/virtio-balloon-pci.c @@ -59,7 +59,7 @@ static void virtio_balloon_pci_class_init(ObjectClass *klass, void *data) PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass); k->realize = virtio_balloon_pci_realize; set_bit(DEVICE_CATEGORY_MISC, dc->categories); - dc->props = virtio_balloon_pci_properties; + device_class_set_props(dc, virtio_balloon_pci_properties); pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_BALLOON; pcidev_k->revision = VIRTIO_PCI_ABI_VERSION; diff --git a/hw/virtio/virtio-balloon.c b/hw/virtio/virtio-balloon.c index 40b04f5180..a4729f7fc9 100644 --- a/hw/virtio/virtio-balloon.c +++ b/hw/virtio/virtio-balloon.c @@ -831,6 +831,13 @@ static void virtio_balloon_device_unrealize(DeviceState *dev, Error **errp) } balloon_stats_destroy_timer(s); qemu_remove_balloon_handler(s); + + virtio_delete_queue(s->ivq); + virtio_delete_queue(s->dvq); + virtio_delete_queue(s->svq); + if (s->free_page_vq) { + virtio_delete_queue(s->free_page_vq); + } virtio_cleanup(vdev); } @@ -925,7 +932,7 @@ static void virtio_balloon_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); - dc->props = virtio_balloon_properties; + device_class_set_props(dc, virtio_balloon_properties); dc->vmsd = &vmstate_virtio_balloon; set_bit(DEVICE_CATEGORY_MISC, dc->categories); vdc->realize = virtio_balloon_device_realize; diff --git a/hw/virtio/virtio-blk-pci.c b/hw/virtio/virtio-blk-pci.c index d9b69a5af3..efb2c22a1d 100644 --- a/hw/virtio/virtio-blk-pci.c +++ b/hw/virtio/virtio-blk-pci.c @@ -66,7 +66,7 @@ static void virtio_blk_pci_class_init(ObjectClass *klass, void *data) PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass); set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); - dc->props = virtio_blk_pci_properties; + device_class_set_props(dc, virtio_blk_pci_properties); k->realize = virtio_blk_pci_realize; pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_BLOCK; diff --git a/hw/virtio/virtio-crypto-pci.c b/hw/virtio/virtio-crypto-pci.c index d853dc460c..0bebe0149d 100644 --- a/hw/virtio/virtio-crypto-pci.c +++ b/hw/virtio/virtio-crypto-pci.c @@ -69,7 +69,7 @@ static void virtio_crypto_pci_class_init(ObjectClass *klass, void *data) k->realize = virtio_crypto_pci_realize; set_bit(DEVICE_CATEGORY_MISC, dc->categories); - dc->props = virtio_crypto_pci_properties; + device_class_set_props(dc, virtio_crypto_pci_properties); pcidev_k->class_id = PCI_CLASS_OTHERS; } diff --git a/hw/virtio/virtio-crypto.c b/hw/virtio/virtio-crypto.c index 6d42f95a89..4c65114de5 100644 --- a/hw/virtio/virtio-crypto.c +++ b/hw/virtio/virtio-crypto.c @@ -831,12 +831,13 @@ static void virtio_crypto_device_unrealize(DeviceState *dev, Error **errp) max_queues = vcrypto->multiqueue ? vcrypto->max_queues : 1; for (i = 0; i < max_queues; i++) { - virtio_del_queue(vdev, i); + virtio_delete_queue(vcrypto->vqs[i].dataq); q = &vcrypto->vqs[i]; qemu_bh_delete(q->dataq_bh); } g_free(vcrypto->vqs); + virtio_delete_queue(vcrypto->ctrl_vq); virtio_cleanup(vdev); cryptodev_backend_set_used(vcrypto->cryptodev, false); @@ -955,7 +956,7 @@ static void virtio_crypto_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); - dc->props = virtio_crypto_properties; + device_class_set_props(dc, virtio_crypto_properties); dc->vmsd = &vmstate_virtio_crypto; set_bit(DEVICE_CATEGORY_MISC, dc->categories); vdc->realize = virtio_crypto_device_realize; diff --git a/hw/virtio/virtio-input-pci.c b/hw/virtio/virtio-input-pci.c index 80b1172c90..5a965408df 100644 --- a/hw/virtio/virtio-input-pci.c +++ b/hw/virtio/virtio-input-pci.c @@ -60,7 +60,7 @@ static void virtio_input_pci_class_init(ObjectClass *klass, void *data) VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass); - dc->props = virtio_input_pci_properties; + device_class_set_props(dc, virtio_input_pci_properties); k->realize = virtio_input_pci_realize; set_bit(DEVICE_CATEGORY_INPUT, dc->categories); diff --git a/hw/virtio/virtio-iommu-pci.c b/hw/virtio/virtio-iommu-pci.c new file mode 100644 index 0000000000..3dfbf55b47 --- /dev/null +++ b/hw/virtio/virtio-iommu-pci.c @@ -0,0 +1,104 @@ +/* + * Virtio IOMMU PCI Bindings + * + * Copyright (c) 2019 Red Hat, Inc. + * Written by Eric Auger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 or + * (at your option) any later version. + */ + +#include "qemu/osdep.h" + +#include "virtio-pci.h" +#include "hw/virtio/virtio-iommu.h" +#include "hw/qdev-properties.h" +#include "qapi/error.h" +#include "hw/boards.h" + +typedef struct VirtIOIOMMUPCI VirtIOIOMMUPCI; + +/* + * virtio-iommu-pci: This extends VirtioPCIProxy. + * + */ +#define VIRTIO_IOMMU_PCI(obj) \ + OBJECT_CHECK(VirtIOIOMMUPCI, (obj), TYPE_VIRTIO_IOMMU_PCI) + +struct VirtIOIOMMUPCI { + VirtIOPCIProxy parent_obj; + VirtIOIOMMU vdev; +}; + +static Property virtio_iommu_pci_properties[] = { + DEFINE_PROP_UINT32("class", VirtIOPCIProxy, class_code, 0), + DEFINE_PROP_END_OF_LIST(), +}; + +static void virtio_iommu_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) +{ + VirtIOIOMMUPCI *dev = VIRTIO_IOMMU_PCI(vpci_dev); + DeviceState *vdev = DEVICE(&dev->vdev); + + if (!qdev_get_machine_hotplug_handler(DEVICE(vpci_dev))) { + MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine()); + + error_setg(errp, + "%s machine fails to create iommu-map device tree bindings", + mc->name); + error_append_hint(errp, + "Check you machine implements a hotplug handler " + "for the virtio-iommu-pci device\n"); + error_append_hint(errp, "Check the guest is booted without FW or with " + "-no-acpi\n"); + return; + } + qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus)); + object_property_set_link(OBJECT(dev), + OBJECT(pci_get_bus(&vpci_dev->pci_dev)), + "primary-bus", errp); + object_property_set_bool(OBJECT(vdev), true, "realized", errp); +} + +static void virtio_iommu_pci_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); + PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass); + k->realize = virtio_iommu_pci_realize; + set_bit(DEVICE_CATEGORY_MISC, dc->categories); + device_class_set_props(dc, virtio_iommu_pci_properties); + pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; + pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_IOMMU; + pcidev_k->revision = VIRTIO_PCI_ABI_VERSION; + pcidev_k->class_id = PCI_CLASS_OTHERS; + dc->hotpluggable = false; +} + +static void virtio_iommu_pci_instance_init(Object *obj) +{ + VirtIOIOMMUPCI *dev = VIRTIO_IOMMU_PCI(obj); + + virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), + TYPE_VIRTIO_IOMMU); +} + +static const VirtioPCIDeviceTypeInfo virtio_iommu_pci_info = { + .base_name = TYPE_VIRTIO_IOMMU_PCI, + .generic_name = "virtio-iommu-pci", + .transitional_name = "virtio-iommu-pci-transitional", + .non_transitional_name = "virtio-iommu-pci-non-transitional", + .instance_size = sizeof(VirtIOIOMMUPCI), + .instance_init = virtio_iommu_pci_instance_init, + .class_init = virtio_iommu_pci_class_init, +}; + +static void virtio_iommu_pci_register(void) +{ + virtio_pci_types_register(&virtio_iommu_pci_info); +} + +type_init(virtio_iommu_pci_register) + + diff --git a/hw/virtio/virtio-iommu.c b/hw/virtio/virtio-iommu.c new file mode 100644 index 0000000000..22ba8848c2 --- /dev/null +++ b/hw/virtio/virtio-iommu.c @@ -0,0 +1,893 @@ +/* + * virtio-iommu device + * + * Copyright (c) 2020 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qemu/iov.h" +#include "qemu-common.h" +#include "hw/qdev-properties.h" +#include "hw/virtio/virtio.h" +#include "sysemu/kvm.h" +#include "qapi/error.h" +#include "qemu/error-report.h" +#include "trace.h" + +#include "standard-headers/linux/virtio_ids.h" + +#include "hw/virtio/virtio-bus.h" +#include "hw/virtio/virtio-access.h" +#include "hw/virtio/virtio-iommu.h" +#include "hw/pci/pci_bus.h" +#include "hw/pci/pci.h" + +/* Max size */ +#define VIOMMU_DEFAULT_QUEUE_SIZE 256 + +typedef struct VirtIOIOMMUDomain { + uint32_t id; + GTree *mappings; + QLIST_HEAD(, VirtIOIOMMUEndpoint) endpoint_list; +} VirtIOIOMMUDomain; + +typedef struct VirtIOIOMMUEndpoint { + uint32_t id; + VirtIOIOMMUDomain *domain; + QLIST_ENTRY(VirtIOIOMMUEndpoint) next; +} VirtIOIOMMUEndpoint; + +typedef struct VirtIOIOMMUInterval { + uint64_t low; + uint64_t high; +} VirtIOIOMMUInterval; + +typedef struct VirtIOIOMMUMapping { + uint64_t phys_addr; + uint32_t flags; +} VirtIOIOMMUMapping; + +static inline uint16_t virtio_iommu_get_bdf(IOMMUDevice *dev) +{ + return PCI_BUILD_BDF(pci_bus_num(dev->bus), dev->devfn); +} + +/** + * The bus number is used for lookup when SID based operations occur. + * In that case we lazily populate the IOMMUPciBus array from the bus hash + * table. At the time the IOMMUPciBus is created (iommu_find_add_as), the bus + * numbers may not be always initialized yet. + */ +static IOMMUPciBus *iommu_find_iommu_pcibus(VirtIOIOMMU *s, uint8_t bus_num) +{ + IOMMUPciBus *iommu_pci_bus = s->iommu_pcibus_by_bus_num[bus_num]; + + if (!iommu_pci_bus) { + GHashTableIter iter; + + g_hash_table_iter_init(&iter, s->as_by_busptr); + while (g_hash_table_iter_next(&iter, NULL, (void **)&iommu_pci_bus)) { + if (pci_bus_num(iommu_pci_bus->bus) == bus_num) { + s->iommu_pcibus_by_bus_num[bus_num] = iommu_pci_bus; + return iommu_pci_bus; + } + } + return NULL; + } + return iommu_pci_bus; +} + +static IOMMUMemoryRegion *virtio_iommu_mr(VirtIOIOMMU *s, uint32_t sid) +{ + uint8_t bus_n, devfn; + IOMMUPciBus *iommu_pci_bus; + IOMMUDevice *dev; + + bus_n = PCI_BUS_NUM(sid); + iommu_pci_bus = iommu_find_iommu_pcibus(s, bus_n); + if (iommu_pci_bus) { + devfn = sid & PCI_DEVFN_MAX; + dev = iommu_pci_bus->pbdev[devfn]; + if (dev) { + return &dev->iommu_mr; + } + } + return NULL; +} + +static gint interval_cmp(gconstpointer a, gconstpointer b, gpointer user_data) +{ + VirtIOIOMMUInterval *inta = (VirtIOIOMMUInterval *)a; + VirtIOIOMMUInterval *intb = (VirtIOIOMMUInterval *)b; + + if (inta->high < intb->low) { + return -1; + } else if (intb->high < inta->low) { + return 1; + } else { + return 0; + } +} + +static void virtio_iommu_detach_endpoint_from_domain(VirtIOIOMMUEndpoint *ep) +{ + if (!ep->domain) { + return; + } + QLIST_REMOVE(ep, next); + ep->domain = NULL; +} + +static VirtIOIOMMUEndpoint *virtio_iommu_get_endpoint(VirtIOIOMMU *s, + uint32_t ep_id) +{ + VirtIOIOMMUEndpoint *ep; + + ep = g_tree_lookup(s->endpoints, GUINT_TO_POINTER(ep_id)); + if (ep) { + return ep; + } + if (!virtio_iommu_mr(s, ep_id)) { + return NULL; + } + ep = g_malloc0(sizeof(*ep)); + ep->id = ep_id; + trace_virtio_iommu_get_endpoint(ep_id); + g_tree_insert(s->endpoints, GUINT_TO_POINTER(ep_id), ep); + return ep; +} + +static void virtio_iommu_put_endpoint(gpointer data) +{ + VirtIOIOMMUEndpoint *ep = (VirtIOIOMMUEndpoint *)data; + + if (ep->domain) { + virtio_iommu_detach_endpoint_from_domain(ep); + } + + trace_virtio_iommu_put_endpoint(ep->id); + g_free(ep); +} + +static VirtIOIOMMUDomain *virtio_iommu_get_domain(VirtIOIOMMU *s, + uint32_t domain_id) +{ + VirtIOIOMMUDomain *domain; + + domain = g_tree_lookup(s->domains, GUINT_TO_POINTER(domain_id)); + if (domain) { + return domain; + } + domain = g_malloc0(sizeof(*domain)); + domain->id = domain_id; + domain->mappings = g_tree_new_full((GCompareDataFunc)interval_cmp, + NULL, (GDestroyNotify)g_free, + (GDestroyNotify)g_free); + g_tree_insert(s->domains, GUINT_TO_POINTER(domain_id), domain); + QLIST_INIT(&domain->endpoint_list); + trace_virtio_iommu_get_domain(domain_id); + return domain; +} + +static void virtio_iommu_put_domain(gpointer data) +{ + VirtIOIOMMUDomain *domain = (VirtIOIOMMUDomain *)data; + VirtIOIOMMUEndpoint *iter, *tmp; + + QLIST_FOREACH_SAFE(iter, &domain->endpoint_list, next, tmp) { + virtio_iommu_detach_endpoint_from_domain(iter); + } + g_tree_destroy(domain->mappings); + trace_virtio_iommu_put_domain(domain->id); + g_free(domain); +} + +static AddressSpace *virtio_iommu_find_add_as(PCIBus *bus, void *opaque, + int devfn) +{ + VirtIOIOMMU *s = opaque; + IOMMUPciBus *sbus = g_hash_table_lookup(s->as_by_busptr, bus); + static uint32_t mr_index; + IOMMUDevice *sdev; + + if (!sbus) { + sbus = g_malloc0(sizeof(IOMMUPciBus) + + sizeof(IOMMUDevice *) * PCI_DEVFN_MAX); + sbus->bus = bus; + g_hash_table_insert(s->as_by_busptr, bus, sbus); + } + + sdev = sbus->pbdev[devfn]; + if (!sdev) { + char *name = g_strdup_printf("%s-%d-%d", + TYPE_VIRTIO_IOMMU_MEMORY_REGION, + mr_index++, devfn); + sdev = sbus->pbdev[devfn] = g_malloc0(sizeof(IOMMUDevice)); + + sdev->viommu = s; + sdev->bus = bus; + sdev->devfn = devfn; + + trace_virtio_iommu_init_iommu_mr(name); + + memory_region_init_iommu(&sdev->iommu_mr, sizeof(sdev->iommu_mr), + TYPE_VIRTIO_IOMMU_MEMORY_REGION, + OBJECT(s), name, + UINT64_MAX); + address_space_init(&sdev->as, + MEMORY_REGION(&sdev->iommu_mr), TYPE_VIRTIO_IOMMU); + g_free(name); + } + return &sdev->as; +} + +static int virtio_iommu_attach(VirtIOIOMMU *s, + struct virtio_iommu_req_attach *req) +{ + uint32_t domain_id = le32_to_cpu(req->domain); + uint32_t ep_id = le32_to_cpu(req->endpoint); + VirtIOIOMMUDomain *domain; + VirtIOIOMMUEndpoint *ep; + + trace_virtio_iommu_attach(domain_id, ep_id); + + ep = virtio_iommu_get_endpoint(s, ep_id); + if (!ep) { + return VIRTIO_IOMMU_S_NOENT; + } + + if (ep->domain) { + VirtIOIOMMUDomain *previous_domain = ep->domain; + /* + * the device is already attached to a domain, + * detach it first + */ + virtio_iommu_detach_endpoint_from_domain(ep); + if (QLIST_EMPTY(&previous_domain->endpoint_list)) { + g_tree_remove(s->domains, GUINT_TO_POINTER(previous_domain->id)); + } + } + + domain = virtio_iommu_get_domain(s, domain_id); + QLIST_INSERT_HEAD(&domain->endpoint_list, ep, next); + + ep->domain = domain; + + return VIRTIO_IOMMU_S_OK; +} + +static int virtio_iommu_detach(VirtIOIOMMU *s, + struct virtio_iommu_req_detach *req) +{ + uint32_t domain_id = le32_to_cpu(req->domain); + uint32_t ep_id = le32_to_cpu(req->endpoint); + VirtIOIOMMUDomain *domain; + VirtIOIOMMUEndpoint *ep; + + trace_virtio_iommu_detach(domain_id, ep_id); + + ep = g_tree_lookup(s->endpoints, GUINT_TO_POINTER(ep_id)); + if (!ep) { + return VIRTIO_IOMMU_S_NOENT; + } + + domain = ep->domain; + + if (!domain || domain->id != domain_id) { + return VIRTIO_IOMMU_S_INVAL; + } + + virtio_iommu_detach_endpoint_from_domain(ep); + + if (QLIST_EMPTY(&domain->endpoint_list)) { + g_tree_remove(s->domains, GUINT_TO_POINTER(domain->id)); + } + return VIRTIO_IOMMU_S_OK; +} + +static int virtio_iommu_map(VirtIOIOMMU *s, + struct virtio_iommu_req_map *req) +{ + uint32_t domain_id = le32_to_cpu(req->domain); + uint64_t phys_start = le64_to_cpu(req->phys_start); + uint64_t virt_start = le64_to_cpu(req->virt_start); + uint64_t virt_end = le64_to_cpu(req->virt_end); + uint32_t flags = le32_to_cpu(req->flags); + VirtIOIOMMUDomain *domain; + VirtIOIOMMUInterval *interval; + VirtIOIOMMUMapping *mapping; + + if (flags & ~VIRTIO_IOMMU_MAP_F_MASK) { + return VIRTIO_IOMMU_S_INVAL; + } + + domain = g_tree_lookup(s->domains, GUINT_TO_POINTER(domain_id)); + if (!domain) { + return VIRTIO_IOMMU_S_NOENT; + } + + interval = g_malloc0(sizeof(*interval)); + + interval->low = virt_start; + interval->high = virt_end; + + mapping = g_tree_lookup(domain->mappings, (gpointer)interval); + if (mapping) { + g_free(interval); + return VIRTIO_IOMMU_S_INVAL; + } + + trace_virtio_iommu_map(domain_id, virt_start, virt_end, phys_start, flags); + + mapping = g_malloc0(sizeof(*mapping)); + mapping->phys_addr = phys_start; + mapping->flags = flags; + + g_tree_insert(domain->mappings, interval, mapping); + + return VIRTIO_IOMMU_S_OK; +} + +static int virtio_iommu_unmap(VirtIOIOMMU *s, + struct virtio_iommu_req_unmap *req) +{ + uint32_t domain_id = le32_to_cpu(req->domain); + uint64_t virt_start = le64_to_cpu(req->virt_start); + uint64_t virt_end = le64_to_cpu(req->virt_end); + VirtIOIOMMUMapping *iter_val; + VirtIOIOMMUInterval interval, *iter_key; + VirtIOIOMMUDomain *domain; + int ret = VIRTIO_IOMMU_S_OK; + + trace_virtio_iommu_unmap(domain_id, virt_start, virt_end); + + domain = g_tree_lookup(s->domains, GUINT_TO_POINTER(domain_id)); + if (!domain) { + return VIRTIO_IOMMU_S_NOENT; + } + interval.low = virt_start; + interval.high = virt_end; + + while (g_tree_lookup_extended(domain->mappings, &interval, + (void **)&iter_key, (void**)&iter_val)) { + uint64_t current_low = iter_key->low; + uint64_t current_high = iter_key->high; + + if (interval.low <= current_low && interval.high >= current_high) { + g_tree_remove(domain->mappings, iter_key); + trace_virtio_iommu_unmap_done(domain_id, current_low, current_high); + } else { + ret = VIRTIO_IOMMU_S_RANGE; + break; + } + } + return ret; +} + +static int virtio_iommu_iov_to_req(struct iovec *iov, + unsigned int iov_cnt, + void *req, size_t req_sz) +{ + size_t sz, payload_sz = req_sz - sizeof(struct virtio_iommu_req_tail); + + sz = iov_to_buf(iov, iov_cnt, 0, req, payload_sz); + if (unlikely(sz != payload_sz)) { + return VIRTIO_IOMMU_S_INVAL; + } + return 0; +} + +#define virtio_iommu_handle_req(__req) \ +static int virtio_iommu_handle_ ## __req(VirtIOIOMMU *s, \ + struct iovec *iov, \ + unsigned int iov_cnt) \ +{ \ + struct virtio_iommu_req_ ## __req req; \ + int ret = virtio_iommu_iov_to_req(iov, iov_cnt, &req, sizeof(req)); \ + \ + return ret ? ret : virtio_iommu_ ## __req(s, &req); \ +} + +virtio_iommu_handle_req(attach) +virtio_iommu_handle_req(detach) +virtio_iommu_handle_req(map) +virtio_iommu_handle_req(unmap) + +static void virtio_iommu_handle_command(VirtIODevice *vdev, VirtQueue *vq) +{ + VirtIOIOMMU *s = VIRTIO_IOMMU(vdev); + struct virtio_iommu_req_head head; + struct virtio_iommu_req_tail tail = {}; + VirtQueueElement *elem; + unsigned int iov_cnt; + struct iovec *iov; + size_t sz; + + for (;;) { + elem = virtqueue_pop(vq, sizeof(VirtQueueElement)); + if (!elem) { + return; + } + + if (iov_size(elem->in_sg, elem->in_num) < sizeof(tail) || + iov_size(elem->out_sg, elem->out_num) < sizeof(head)) { + virtio_error(vdev, "virtio-iommu bad head/tail size"); + virtqueue_detach_element(vq, elem, 0); + g_free(elem); + break; + } + + iov_cnt = elem->out_num; + iov = elem->out_sg; + sz = iov_to_buf(iov, iov_cnt, 0, &head, sizeof(head)); + if (unlikely(sz != sizeof(head))) { + tail.status = VIRTIO_IOMMU_S_DEVERR; + goto out; + } + qemu_mutex_lock(&s->mutex); + switch (head.type) { + case VIRTIO_IOMMU_T_ATTACH: + tail.status = virtio_iommu_handle_attach(s, iov, iov_cnt); + break; + case VIRTIO_IOMMU_T_DETACH: + tail.status = virtio_iommu_handle_detach(s, iov, iov_cnt); + break; + case VIRTIO_IOMMU_T_MAP: + tail.status = virtio_iommu_handle_map(s, iov, iov_cnt); + break; + case VIRTIO_IOMMU_T_UNMAP: + tail.status = virtio_iommu_handle_unmap(s, iov, iov_cnt); + break; + default: + tail.status = VIRTIO_IOMMU_S_UNSUPP; + } + qemu_mutex_unlock(&s->mutex); + +out: + sz = iov_from_buf(elem->in_sg, elem->in_num, 0, + &tail, sizeof(tail)); + assert(sz == sizeof(tail)); + + virtqueue_push(vq, elem, sizeof(tail)); + virtio_notify(vdev, vq); + g_free(elem); + } +} + +static void virtio_iommu_report_fault(VirtIOIOMMU *viommu, uint8_t reason, + int flags, uint32_t endpoint, + uint64_t address) +{ + VirtIODevice *vdev = &viommu->parent_obj; + VirtQueue *vq = viommu->event_vq; + struct virtio_iommu_fault fault; + VirtQueueElement *elem; + size_t sz; + + memset(&fault, 0, sizeof(fault)); + fault.reason = reason; + fault.flags = cpu_to_le32(flags); + fault.endpoint = cpu_to_le32(endpoint); + fault.address = cpu_to_le64(address); + + elem = virtqueue_pop(vq, sizeof(VirtQueueElement)); + + if (!elem) { + error_report_once( + "no buffer available in event queue to report event"); + return; + } + + if (iov_size(elem->in_sg, elem->in_num) < sizeof(fault)) { + virtio_error(vdev, "error buffer of wrong size"); + virtqueue_detach_element(vq, elem, 0); + g_free(elem); + return; + } + + sz = iov_from_buf(elem->in_sg, elem->in_num, 0, + &fault, sizeof(fault)); + assert(sz == sizeof(fault)); + + trace_virtio_iommu_report_fault(reason, flags, endpoint, address); + virtqueue_push(vq, elem, sz); + virtio_notify(vdev, vq); + g_free(elem); + +} + +static IOMMUTLBEntry virtio_iommu_translate(IOMMUMemoryRegion *mr, hwaddr addr, + IOMMUAccessFlags flag, + int iommu_idx) +{ + IOMMUDevice *sdev = container_of(mr, IOMMUDevice, iommu_mr); + VirtIOIOMMUInterval interval, *mapping_key; + VirtIOIOMMUMapping *mapping_value; + VirtIOIOMMU *s = sdev->viommu; + bool read_fault, write_fault; + VirtIOIOMMUEndpoint *ep; + uint32_t sid, flags; + bool bypass_allowed; + bool found; + + interval.low = addr; + interval.high = addr + 1; + + IOMMUTLBEntry entry = { + .target_as = &address_space_memory, + .iova = addr, + .translated_addr = addr, + .addr_mask = (1 << ctz32(s->config.page_size_mask)) - 1, + .perm = IOMMU_NONE, + }; + + bypass_allowed = virtio_vdev_has_feature(&s->parent_obj, + VIRTIO_IOMMU_F_BYPASS); + + sid = virtio_iommu_get_bdf(sdev); + + trace_virtio_iommu_translate(mr->parent_obj.name, sid, addr, flag); + qemu_mutex_lock(&s->mutex); + + ep = g_tree_lookup(s->endpoints, GUINT_TO_POINTER(sid)); + if (!ep) { + if (!bypass_allowed) { + error_report_once("%s sid=%d is not known!!", __func__, sid); + virtio_iommu_report_fault(s, VIRTIO_IOMMU_FAULT_R_UNKNOWN, + VIRTIO_IOMMU_FAULT_F_ADDRESS, + sid, addr); + } else { + entry.perm = flag; + } + goto unlock; + } + + if (!ep->domain) { + if (!bypass_allowed) { + error_report_once("%s %02x:%02x.%01x not attached to any domain", + __func__, PCI_BUS_NUM(sid), + PCI_SLOT(sid), PCI_FUNC(sid)); + virtio_iommu_report_fault(s, VIRTIO_IOMMU_FAULT_R_DOMAIN, + VIRTIO_IOMMU_FAULT_F_ADDRESS, + sid, addr); + } else { + entry.perm = flag; + } + goto unlock; + } + + found = g_tree_lookup_extended(ep->domain->mappings, (gpointer)(&interval), + (void **)&mapping_key, + (void **)&mapping_value); + if (!found) { + error_report_once("%s no mapping for 0x%"PRIx64" for sid=%d", + __func__, addr, sid); + virtio_iommu_report_fault(s, VIRTIO_IOMMU_FAULT_R_MAPPING, + VIRTIO_IOMMU_FAULT_F_ADDRESS, + sid, addr); + goto unlock; + } + + read_fault = (flag & IOMMU_RO) && + !(mapping_value->flags & VIRTIO_IOMMU_MAP_F_READ); + write_fault = (flag & IOMMU_WO) && + !(mapping_value->flags & VIRTIO_IOMMU_MAP_F_WRITE); + + flags = read_fault ? VIRTIO_IOMMU_FAULT_F_READ : 0; + flags |= write_fault ? VIRTIO_IOMMU_FAULT_F_WRITE : 0; + if (flags) { + error_report_once("%s permission error on 0x%"PRIx64"(%d): allowed=%d", + __func__, addr, flag, mapping_value->flags); + flags |= VIRTIO_IOMMU_FAULT_F_ADDRESS; + virtio_iommu_report_fault(s, VIRTIO_IOMMU_FAULT_R_MAPPING, + flags | VIRTIO_IOMMU_FAULT_F_ADDRESS, + sid, addr); + goto unlock; + } + entry.translated_addr = addr - mapping_key->low + mapping_value->phys_addr; + entry.perm = flag; + trace_virtio_iommu_translate_out(addr, entry.translated_addr, sid); + +unlock: + qemu_mutex_unlock(&s->mutex); + return entry; +} + +static void virtio_iommu_get_config(VirtIODevice *vdev, uint8_t *config_data) +{ + VirtIOIOMMU *dev = VIRTIO_IOMMU(vdev); + struct virtio_iommu_config *config = &dev->config; + + trace_virtio_iommu_get_config(config->page_size_mask, + config->input_range.start, + config->input_range.end, + config->domain_range.end, + config->probe_size); + memcpy(config_data, &dev->config, sizeof(struct virtio_iommu_config)); +} + +static void virtio_iommu_set_config(VirtIODevice *vdev, + const uint8_t *config_data) +{ + struct virtio_iommu_config config; + + memcpy(&config, config_data, sizeof(struct virtio_iommu_config)); + trace_virtio_iommu_set_config(config.page_size_mask, + config.input_range.start, + config.input_range.end, + config.domain_range.end, + config.probe_size); +} + +static uint64_t virtio_iommu_get_features(VirtIODevice *vdev, uint64_t f, + Error **errp) +{ + VirtIOIOMMU *dev = VIRTIO_IOMMU(vdev); + + f |= dev->features; + trace_virtio_iommu_get_features(f); + return f; +} + +static gint int_cmp(gconstpointer a, gconstpointer b, gpointer user_data) +{ + guint ua = GPOINTER_TO_UINT(a); + guint ub = GPOINTER_TO_UINT(b); + return (ua > ub) - (ua < ub); +} + +static void virtio_iommu_device_realize(DeviceState *dev, Error **errp) +{ + VirtIODevice *vdev = VIRTIO_DEVICE(dev); + VirtIOIOMMU *s = VIRTIO_IOMMU(dev); + + virtio_init(vdev, "virtio-iommu", VIRTIO_ID_IOMMU, + sizeof(struct virtio_iommu_config)); + + memset(s->iommu_pcibus_by_bus_num, 0, sizeof(s->iommu_pcibus_by_bus_num)); + + s->req_vq = virtio_add_queue(vdev, VIOMMU_DEFAULT_QUEUE_SIZE, + virtio_iommu_handle_command); + s->event_vq = virtio_add_queue(vdev, VIOMMU_DEFAULT_QUEUE_SIZE, NULL); + + s->config.page_size_mask = TARGET_PAGE_MASK; + s->config.input_range.end = -1UL; + s->config.domain_range.end = 32; + + virtio_add_feature(&s->features, VIRTIO_RING_F_EVENT_IDX); + virtio_add_feature(&s->features, VIRTIO_RING_F_INDIRECT_DESC); + virtio_add_feature(&s->features, VIRTIO_F_VERSION_1); + virtio_add_feature(&s->features, VIRTIO_IOMMU_F_INPUT_RANGE); + virtio_add_feature(&s->features, VIRTIO_IOMMU_F_DOMAIN_RANGE); + virtio_add_feature(&s->features, VIRTIO_IOMMU_F_MAP_UNMAP); + virtio_add_feature(&s->features, VIRTIO_IOMMU_F_BYPASS); + virtio_add_feature(&s->features, VIRTIO_IOMMU_F_MMIO); + + qemu_mutex_init(&s->mutex); + + s->as_by_busptr = g_hash_table_new_full(NULL, NULL, NULL, g_free); + + if (s->primary_bus) { + pci_setup_iommu(s->primary_bus, virtio_iommu_find_add_as, s); + } else { + error_setg(errp, "VIRTIO-IOMMU is not attached to any PCI bus!"); + } +} + +static void virtio_iommu_device_unrealize(DeviceState *dev, Error **errp) +{ + VirtIODevice *vdev = VIRTIO_DEVICE(dev); + VirtIOIOMMU *s = VIRTIO_IOMMU(dev); + + g_hash_table_destroy(s->as_by_busptr); + g_tree_destroy(s->domains); + g_tree_destroy(s->endpoints); + + virtio_delete_queue(s->req_vq); + virtio_delete_queue(s->event_vq); + virtio_cleanup(vdev); +} + +static void virtio_iommu_device_reset(VirtIODevice *vdev) +{ + VirtIOIOMMU *s = VIRTIO_IOMMU(vdev); + + trace_virtio_iommu_device_reset(); + + if (s->domains) { + g_tree_destroy(s->domains); + } + if (s->endpoints) { + g_tree_destroy(s->endpoints); + } + s->domains = g_tree_new_full((GCompareDataFunc)int_cmp, + NULL, NULL, virtio_iommu_put_domain); + s->endpoints = g_tree_new_full((GCompareDataFunc)int_cmp, + NULL, NULL, virtio_iommu_put_endpoint); +} + +static void virtio_iommu_set_status(VirtIODevice *vdev, uint8_t status) +{ + trace_virtio_iommu_device_status(status); +} + +static void virtio_iommu_instance_init(Object *obj) +{ +} + +#define VMSTATE_INTERVAL \ +{ \ + .name = "interval", \ + .version_id = 1, \ + .minimum_version_id = 1, \ + .fields = (VMStateField[]) { \ + VMSTATE_UINT64(low, VirtIOIOMMUInterval), \ + VMSTATE_UINT64(high, VirtIOIOMMUInterval), \ + VMSTATE_END_OF_LIST() \ + } \ +} + +#define VMSTATE_MAPPING \ +{ \ + .name = "mapping", \ + .version_id = 1, \ + .minimum_version_id = 1, \ + .fields = (VMStateField[]) { \ + VMSTATE_UINT64(phys_addr, VirtIOIOMMUMapping),\ + VMSTATE_UINT32(flags, VirtIOIOMMUMapping), \ + VMSTATE_END_OF_LIST() \ + }, \ +} + +static const VMStateDescription vmstate_interval_mapping[2] = { + VMSTATE_MAPPING, /* value */ + VMSTATE_INTERVAL /* key */ +}; + +static int domain_preload(void *opaque) +{ + VirtIOIOMMUDomain *domain = opaque; + + domain->mappings = g_tree_new_full((GCompareDataFunc)interval_cmp, + NULL, g_free, g_free); + return 0; +} + +static const VMStateDescription vmstate_endpoint = { + .name = "endpoint", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(id, VirtIOIOMMUEndpoint), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_domain = { + .name = "domain", + .version_id = 1, + .minimum_version_id = 1, + .pre_load = domain_preload, + .fields = (VMStateField[]) { + VMSTATE_UINT32(id, VirtIOIOMMUDomain), + VMSTATE_GTREE_V(mappings, VirtIOIOMMUDomain, 1, + vmstate_interval_mapping, + VirtIOIOMMUInterval, VirtIOIOMMUMapping), + VMSTATE_QLIST_V(endpoint_list, VirtIOIOMMUDomain, 1, + vmstate_endpoint, VirtIOIOMMUEndpoint, next), + VMSTATE_END_OF_LIST() + } +}; + +static gboolean reconstruct_endpoints(gpointer key, gpointer value, + gpointer data) +{ + VirtIOIOMMU *s = (VirtIOIOMMU *)data; + VirtIOIOMMUDomain *d = (VirtIOIOMMUDomain *)value; + VirtIOIOMMUEndpoint *iter; + + QLIST_FOREACH(iter, &d->endpoint_list, next) { + iter->domain = d; + g_tree_insert(s->endpoints, GUINT_TO_POINTER(iter->id), iter); + } + return false; /* continue the domain traversal */ +} + +static int iommu_post_load(void *opaque, int version_id) +{ + VirtIOIOMMU *s = opaque; + + g_tree_foreach(s->domains, reconstruct_endpoints, s); + return 0; +} + +static const VMStateDescription vmstate_virtio_iommu_device = { + .name = "virtio-iommu-device", + .minimum_version_id = 1, + .version_id = 1, + .post_load = iommu_post_load, + .fields = (VMStateField[]) { + VMSTATE_GTREE_DIRECT_KEY_V(domains, VirtIOIOMMU, 1, + &vmstate_domain, VirtIOIOMMUDomain), + VMSTATE_END_OF_LIST() + }, +}; + +static const VMStateDescription vmstate_virtio_iommu = { + .name = "virtio-iommu", + .minimum_version_id = 1, + .priority = MIG_PRI_IOMMU, + .version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_VIRTIO_DEVICE, + VMSTATE_END_OF_LIST() + }, +}; + +static Property virtio_iommu_properties[] = { + DEFINE_PROP_LINK("primary-bus", VirtIOIOMMU, primary_bus, "PCI", PCIBus *), + DEFINE_PROP_END_OF_LIST(), +}; + +static void virtio_iommu_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); + + device_class_set_props(dc, virtio_iommu_properties); + dc->vmsd = &vmstate_virtio_iommu; + + set_bit(DEVICE_CATEGORY_MISC, dc->categories); + vdc->realize = virtio_iommu_device_realize; + vdc->unrealize = virtio_iommu_device_unrealize; + vdc->reset = virtio_iommu_device_reset; + vdc->get_config = virtio_iommu_get_config; + vdc->set_config = virtio_iommu_set_config; + vdc->get_features = virtio_iommu_get_features; + vdc->set_status = virtio_iommu_set_status; + vdc->vmsd = &vmstate_virtio_iommu_device; +} + +static void virtio_iommu_memory_region_class_init(ObjectClass *klass, + void *data) +{ + IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass); + + imrc->translate = virtio_iommu_translate; +} + +static const TypeInfo virtio_iommu_info = { + .name = TYPE_VIRTIO_IOMMU, + .parent = TYPE_VIRTIO_DEVICE, + .instance_size = sizeof(VirtIOIOMMU), + .instance_init = virtio_iommu_instance_init, + .class_init = virtio_iommu_class_init, +}; + +static const TypeInfo virtio_iommu_memory_region_info = { + .parent = TYPE_IOMMU_MEMORY_REGION, + .name = TYPE_VIRTIO_IOMMU_MEMORY_REGION, + .class_init = virtio_iommu_memory_region_class_init, +}; + +static void virtio_register_types(void) +{ + type_register_static(&virtio_iommu_info); + type_register_static(&virtio_iommu_memory_region_info); +} + +type_init(virtio_register_types) diff --git a/hw/virtio/virtio-mmio.c b/hw/virtio/virtio-mmio.c index 94d934c44b..f12d1595aa 100644 --- a/hw/virtio/virtio-mmio.c +++ b/hw/virtio/virtio-mmio.c @@ -65,6 +65,19 @@ static void virtio_mmio_stop_ioeventfd(VirtIOMMIOProxy *proxy) virtio_bus_stop_ioeventfd(&proxy->bus); } +static void virtio_mmio_soft_reset(VirtIOMMIOProxy *proxy) +{ + int i; + + if (proxy->legacy) { + return; + } + + for (i = 0; i < VIRTIO_QUEUE_MAX; i++) { + proxy->vqs[i].enabled = 0; + } +} + static uint64_t virtio_mmio_read(void *opaque, hwaddr offset, unsigned size) { VirtIOMMIOProxy *proxy = (VirtIOMMIOProxy *)opaque; @@ -295,8 +308,9 @@ static void virtio_mmio_write(void *opaque, hwaddr offset, uint64_t value, break; case VIRTIO_MMIO_QUEUE_NUM: trace_virtio_mmio_queue_write(value, VIRTQUEUE_MAX_SIZE); + virtio_queue_set_num(vdev, vdev->queue_sel, value); + if (proxy->legacy) { - virtio_queue_set_num(vdev, vdev->queue_sel, value); virtio_queue_update_rings(vdev, vdev->queue_sel); } else { proxy->vqs[vdev->queue_sel].num = value; @@ -378,6 +392,7 @@ static void virtio_mmio_write(void *opaque, hwaddr offset, uint64_t value, if (vdev->status == 0) { virtio_reset(vdev); + virtio_mmio_soft_reset(proxy); } break; case VIRTIO_MMIO_QUEUE_DESC_LOW: @@ -697,7 +712,7 @@ static void virtio_mmio_class_init(ObjectClass *klass, void *data) dc->realize = virtio_mmio_realizefn; dc->reset = virtio_mmio_reset; set_bit(DEVICE_CATEGORY_MISC, dc->categories); - dc->props = virtio_mmio_properties; + device_class_set_props(dc, virtio_mmio_properties); } static const TypeInfo virtio_mmio_info = { diff --git a/hw/virtio/virtio-net-pci.c b/hw/virtio/virtio-net-pci.c index f670aed0a7..5ca71d4c34 100644 --- a/hw/virtio/virtio-net-pci.c +++ b/hw/virtio/virtio-net-pci.c @@ -68,7 +68,7 @@ static void virtio_net_pci_class_init(ObjectClass *klass, void *data) k->revision = VIRTIO_PCI_ABI_VERSION; k->class_id = PCI_CLASS_NETWORK_ETHERNET; set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); - dc->props = virtio_net_properties; + device_class_set_props(dc, virtio_net_properties); vpciklass->realize = virtio_net_pci_realize; } diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index c6b47a9c73..4cb784389c 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -608,10 +608,14 @@ static void virtio_write_config(PCIDevice *pci_dev, uint32_t address, pcie_cap_flr_write_config(pci_dev, address, val, len); } - if (range_covers_byte(address, len, PCI_COMMAND) && - !(pci_dev->config[PCI_COMMAND] & PCI_COMMAND_MASTER)) { - virtio_pci_stop_ioeventfd(proxy); - virtio_set_status(vdev, vdev->status & ~VIRTIO_CONFIG_S_DRIVER_OK); + if (range_covers_byte(address, len, PCI_COMMAND)) { + if (!(pci_dev->config[PCI_COMMAND] & PCI_COMMAND_MASTER)) { + virtio_set_disabled(vdev, true); + virtio_pci_stop_ioeventfd(proxy); + virtio_set_status(vdev, vdev->status & ~VIRTIO_CONFIG_S_DRIVER_OK); + } else { + virtio_set_disabled(vdev, false); + } } if (proxy->config_cap && @@ -1256,6 +1260,8 @@ static void virtio_pci_common_write(void *opaque, hwaddr addr, break; case VIRTIO_PCI_COMMON_Q_SIZE: proxy->vqs[vdev->queue_sel].num = val; + virtio_queue_set_num(vdev, vdev->queue_sel, + proxy->vqs[vdev->queue_sel].num); break; case VIRTIO_PCI_COMMON_Q_MSIX: msix_vector_unuse(&proxy->pci_dev, @@ -1880,7 +1886,7 @@ static void virtio_pci_class_init(ObjectClass *klass, void *data) PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); VirtioPCIClass *vpciklass = VIRTIO_PCI_CLASS(klass); - dc->props = virtio_pci_properties; + device_class_set_props(dc, virtio_pci_properties); k->realize = virtio_pci_realize; k->exit = virtio_pci_exit; k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; @@ -1919,7 +1925,7 @@ static void virtio_pci_generic_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->props = virtio_pci_generic_properties; + device_class_set_props(dc, virtio_pci_generic_properties); } static void virtio_pci_transitional_instance_init(Object *obj) diff --git a/hw/virtio/virtio-pmem.c b/hw/virtio/virtio-pmem.c index c0c9395e55..43399522f5 100644 --- a/hw/virtio/virtio-pmem.c +++ b/hw/virtio/virtio-pmem.c @@ -130,6 +130,7 @@ static void virtio_pmem_unrealize(DeviceState *dev, Error **errp) VirtIOPMEM *pmem = VIRTIO_PMEM(dev); host_memory_backend_set_mapped(pmem->memdev, false); + virtio_delete_queue(pmem->rq_vq); virtio_cleanup(vdev); } @@ -165,7 +166,7 @@ static void virtio_pmem_class_init(ObjectClass *klass, void *data) VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); VirtIOPMEMClass *vpc = VIRTIO_PMEM_CLASS(klass); - dc->props = virtio_pmem_properties; + device_class_set_props(dc, virtio_pmem_properties); vdc->realize = virtio_pmem_realize; vdc->unrealize = virtio_pmem_unrealize; diff --git a/hw/virtio/virtio-rng.c b/hw/virtio/virtio-rng.c index b498a20332..1e363ad07b 100644 --- a/hw/virtio/virtio-rng.c +++ b/hw/virtio/virtio-rng.c @@ -269,7 +269,7 @@ static void virtio_rng_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); - dc->props = virtio_rng_properties; + device_class_set_props(dc, virtio_rng_properties); dc->vmsd = &vmstate_virtio_rng; set_bit(DEVICE_CATEGORY_MISC, dc->categories); vdc->realize = virtio_rng_device_realize; diff --git a/hw/virtio/virtio-scsi-pci.c b/hw/virtio/virtio-scsi-pci.c index 3c55dc19a1..e82e7e5680 100644 --- a/hw/virtio/virtio-scsi-pci.c +++ b/hw/virtio/virtio-scsi-pci.c @@ -76,7 +76,7 @@ static void virtio_scsi_pci_class_init(ObjectClass *klass, void *data) k->realize = virtio_scsi_pci_realize; set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); - dc->props = virtio_scsi_pci_properties; + device_class_set_props(dc, virtio_scsi_pci_properties); pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_SCSI; pcidev_k->revision = 0x00; diff --git a/hw/virtio/virtio-serial-pci.c b/hw/virtio/virtio-serial-pci.c index 953abbd13a..22ab4d8562 100644 --- a/hw/virtio/virtio-serial-pci.c +++ b/hw/virtio/virtio-serial-pci.c @@ -84,7 +84,7 @@ static void virtio_serial_pci_class_init(ObjectClass *klass, void *data) PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass); k->realize = virtio_serial_pci_realize; set_bit(DEVICE_CATEGORY_INPUT, dc->categories); - dc->props = virtio_serial_pci_properties; + device_class_set_props(dc, virtio_serial_pci_properties); pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_CONSOLE; pcidev_k->revision = VIRTIO_PCI_ABI_VERSION; diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index 04716b5f6c..b6c8ef5bc0 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -54,7 +54,7 @@ typedef struct VRingAvail { uint16_t flags; uint16_t idx; - uint16_t ring[0]; + uint16_t ring[]; } VRingAvail; typedef struct VRingUsedElem @@ -67,7 +67,7 @@ typedef struct VRingUsed { uint16_t flags; uint16_t idx; - VRingUsedElem ring[0]; + VRingUsedElem ring[]; } VRingUsed; typedef struct VRingMemoryRegionCaches { @@ -282,15 +282,19 @@ static void vring_packed_flags_write(VirtIODevice *vdev, /* Called within rcu_read_lock(). */ static VRingMemoryRegionCaches *vring_get_region_caches(struct VirtQueue *vq) { - VRingMemoryRegionCaches *caches = atomic_rcu_read(&vq->vring.caches); - assert(caches != NULL); - return caches; + return atomic_rcu_read(&vq->vring.caches); } + /* Called within rcu_read_lock(). */ static inline uint16_t vring_avail_flags(VirtQueue *vq) { VRingMemoryRegionCaches *caches = vring_get_region_caches(vq); hwaddr pa = offsetof(VRingAvail, flags); + + if (!caches) { + return 0; + } + return virtio_lduw_phys_cached(vq->vdev, &caches->avail, pa); } @@ -299,6 +303,11 @@ static inline uint16_t vring_avail_idx(VirtQueue *vq) { VRingMemoryRegionCaches *caches = vring_get_region_caches(vq); hwaddr pa = offsetof(VRingAvail, idx); + + if (!caches) { + return 0; + } + vq->shadow_avail_idx = virtio_lduw_phys_cached(vq->vdev, &caches->avail, pa); return vq->shadow_avail_idx; } @@ -308,6 +317,11 @@ static inline uint16_t vring_avail_ring(VirtQueue *vq, int i) { VRingMemoryRegionCaches *caches = vring_get_region_caches(vq); hwaddr pa = offsetof(VRingAvail, ring[i]); + + if (!caches) { + return 0; + } + return virtio_lduw_phys_cached(vq->vdev, &caches->avail, pa); } @@ -323,6 +337,11 @@ static inline void vring_used_write(VirtQueue *vq, VRingUsedElem *uelem, { VRingMemoryRegionCaches *caches = vring_get_region_caches(vq); hwaddr pa = offsetof(VRingUsed, ring[i]); + + if (!caches) { + return; + } + virtio_tswap32s(vq->vdev, &uelem->id); virtio_tswap32s(vq->vdev, &uelem->len); address_space_write_cached(&caches->used, pa, uelem, sizeof(VRingUsedElem)); @@ -334,6 +353,11 @@ static uint16_t vring_used_idx(VirtQueue *vq) { VRingMemoryRegionCaches *caches = vring_get_region_caches(vq); hwaddr pa = offsetof(VRingUsed, idx); + + if (!caches) { + return 0; + } + return virtio_lduw_phys_cached(vq->vdev, &caches->used, pa); } @@ -342,8 +366,12 @@ static inline void vring_used_idx_set(VirtQueue *vq, uint16_t val) { VRingMemoryRegionCaches *caches = vring_get_region_caches(vq); hwaddr pa = offsetof(VRingUsed, idx); - virtio_stw_phys_cached(vq->vdev, &caches->used, pa, val); - address_space_cache_invalidate(&caches->used, pa, sizeof(val)); + + if (caches) { + virtio_stw_phys_cached(vq->vdev, &caches->used, pa, val); + address_space_cache_invalidate(&caches->used, pa, sizeof(val)); + } + vq->used_idx = val; } @@ -353,8 +381,13 @@ static inline void vring_used_flags_set_bit(VirtQueue *vq, int mask) VRingMemoryRegionCaches *caches = vring_get_region_caches(vq); VirtIODevice *vdev = vq->vdev; hwaddr pa = offsetof(VRingUsed, flags); - uint16_t flags = virtio_lduw_phys_cached(vq->vdev, &caches->used, pa); + uint16_t flags; + if (!caches) { + return; + } + + flags = virtio_lduw_phys_cached(vq->vdev, &caches->used, pa); virtio_stw_phys_cached(vdev, &caches->used, pa, flags | mask); address_space_cache_invalidate(&caches->used, pa, sizeof(flags)); } @@ -365,8 +398,13 @@ static inline void vring_used_flags_unset_bit(VirtQueue *vq, int mask) VRingMemoryRegionCaches *caches = vring_get_region_caches(vq); VirtIODevice *vdev = vq->vdev; hwaddr pa = offsetof(VRingUsed, flags); - uint16_t flags = virtio_lduw_phys_cached(vq->vdev, &caches->used, pa); + uint16_t flags; + if (!caches) { + return; + } + + flags = virtio_lduw_phys_cached(vq->vdev, &caches->used, pa); virtio_stw_phys_cached(vdev, &caches->used, pa, flags & ~mask); address_space_cache_invalidate(&caches->used, pa, sizeof(flags)); } @@ -381,6 +419,10 @@ static inline void vring_set_avail_event(VirtQueue *vq, uint16_t val) } caches = vring_get_region_caches(vq); + if (!caches) { + return; + } + pa = offsetof(VRingUsed, ring[vq->vring.num]); virtio_stw_phys_cached(vq->vdev, &caches->used, pa, val); address_space_cache_invalidate(&caches->used, pa, sizeof(val)); @@ -410,7 +452,11 @@ static void virtio_queue_packed_set_notification(VirtQueue *vq, int enable) VRingMemoryRegionCaches *caches; RCU_READ_LOCK_GUARD(); - caches = vring_get_region_caches(vq); + caches = vring_get_region_caches(vq); + if (!caches) { + return; + } + vring_packed_event_read(vq->vdev, &caches->used, &e); if (!enable) { @@ -432,6 +478,11 @@ static void virtio_queue_packed_set_notification(VirtQueue *vq, int enable) } } +bool virtio_queue_get_notification(VirtQueue *vq) +{ + return vq->notification; +} + void virtio_queue_set_notification(VirtQueue *vq, int enable) { vq->notification = enable; @@ -546,7 +597,7 @@ static inline bool is_desc_avail(uint16_t flags, bool wrap_counter) * Called within rcu_read_lock(). */ static int virtio_queue_empty_rcu(VirtQueue *vq) { - if (unlikely(vq->vdev->broken)) { + if (virtio_device_disabled(vq->vdev)) { return 1; } @@ -565,7 +616,7 @@ static int virtio_queue_split_empty(VirtQueue *vq) { bool empty; - if (unlikely(vq->vdev->broken)) { + if (virtio_device_disabled(vq->vdev)) { return 1; } @@ -592,6 +643,10 @@ static int virtio_queue_packed_empty_rcu(VirtQueue *vq) } cache = vring_get_region_caches(vq); + if (!cache) { + return 1; + } + vring_packed_desc_read_flags(vq->vdev, &desc.flags, &cache->desc, vq->last_avail_idx); @@ -772,6 +827,10 @@ static void virtqueue_packed_fill_desc(VirtQueue *vq, } caches = vring_get_region_caches(vq); + if (!caches) { + return; + } + vring_packed_desc_write(vq->vdev, &desc, &caches->desc, head, strict_order); } @@ -783,7 +842,7 @@ void virtqueue_fill(VirtQueue *vq, const VirtQueueElement *elem, virtqueue_unmap_sg(vq, elem, len); - if (unlikely(vq->vdev->broken)) { + if (virtio_device_disabled(vq->vdev)) { return; } @@ -839,7 +898,7 @@ static void virtqueue_packed_flush(VirtQueue *vq, unsigned int count) void virtqueue_flush(VirtQueue *vq, unsigned int count) { - if (unlikely(vq->vdev->broken)) { + if (virtio_device_disabled(vq->vdev)) { vq->inuse -= count; return; } @@ -944,6 +1003,10 @@ static void virtqueue_split_get_avail_bytes(VirtQueue *vq, max = vq->vring.num; caches = vring_get_region_caches(vq); + if (!caches) { + goto err; + } + while ((rc = virtqueue_num_heads(vq, idx)) > 0) { MemoryRegionCache *desc_cache = &caches->desc; unsigned int num_bufs; @@ -1084,6 +1147,9 @@ static void virtqueue_packed_get_avail_bytes(VirtQueue *vq, max = vq->vring.num; caches = vring_get_region_caches(vq); + if (!caches) { + goto err; + } for (;;) { unsigned int num_bufs = total_bufs; @@ -1189,6 +1255,10 @@ void virtqueue_get_avail_bytes(VirtQueue *vq, unsigned int *in_bytes, } caches = vring_get_region_caches(vq); + if (!caches) { + goto err; + } + desc_size = virtio_vdev_has_feature(vq->vdev, VIRTIO_F_RING_PACKED) ? sizeof(VRingPackedDesc) : sizeof(VRingDesc); if (caches->desc.len < vq->vring.num * desc_size) { @@ -1288,7 +1358,7 @@ static void virtqueue_undo_map_desc(unsigned int out_num, unsigned int in_num, static void virtqueue_map_iovec(VirtIODevice *vdev, struct iovec *sg, hwaddr *addr, unsigned int num_sg, - int is_write) + bool is_write) { unsigned int i; hwaddr len; @@ -1312,8 +1382,9 @@ static void virtqueue_map_iovec(VirtIODevice *vdev, struct iovec *sg, void virtqueue_map(VirtIODevice *vdev, VirtQueueElement *elem) { - virtqueue_map_iovec(vdev, elem->in_sg, elem->in_addr, elem->in_num, 1); - virtqueue_map_iovec(vdev, elem->out_sg, elem->out_addr, elem->out_num, 0); + virtqueue_map_iovec(vdev, elem->in_sg, elem->in_addr, elem->in_num, true); + virtqueue_map_iovec(vdev, elem->out_sg, elem->out_addr, elem->out_num, + false); } static void *virtqueue_alloc_element(size_t sz, unsigned out_num, unsigned in_num) @@ -1382,6 +1453,11 @@ static void *virtqueue_split_pop(VirtQueue *vq, size_t sz) i = head; caches = vring_get_region_caches(vq); + if (!caches) { + virtio_error(vdev, "Region caches not initialized"); + goto done; + } + if (caches->desc.len < max * sizeof(VRingDesc)) { virtio_error(vdev, "Cannot map descriptor ring"); goto done; @@ -1504,6 +1580,11 @@ static void *virtqueue_packed_pop(VirtQueue *vq, size_t sz) i = vq->last_avail_idx; caches = vring_get_region_caches(vq); + if (!caches) { + virtio_error(vdev, "Region caches not initialized"); + goto done; + } + if (caches->desc.len < max * sizeof(VRingDesc)) { virtio_error(vdev, "Cannot map descriptor ring"); goto done; @@ -1602,7 +1683,7 @@ err_undo_map: void *virtqueue_pop(VirtQueue *vq, size_t sz) { - if (unlikely(vq->vdev->broken)) { + if (virtio_device_disabled(vq->vdev)) { return NULL; } @@ -1623,6 +1704,10 @@ static unsigned int virtqueue_packed_drop_all(VirtQueue *vq) VRingPackedDesc desc; caches = vring_get_region_caches(vq); + if (!caches) { + return 0; + } + desc_cache = &caches->desc; virtio_queue_set_notification(vq, 0); @@ -1698,7 +1783,7 @@ unsigned int virtqueue_drop_all(VirtQueue *vq) { struct VirtIODevice *vdev = vq->vdev; - if (unlikely(vdev->broken)) { + if (virtio_device_disabled(vq->vdev)) { return 0; } @@ -1816,7 +1901,7 @@ static void virtio_notify_vector(VirtIODevice *vdev, uint16_t vector) BusState *qbus = qdev_get_parent_bus(DEVICE(vdev)); VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); - if (unlikely(vdev->broken)) { + if (virtio_device_disabled(vdev)) { return; } @@ -1920,6 +2005,7 @@ void virtio_reset(void *opaque) vdev->guest_features = 0; vdev->queue_sel = 0; vdev->status = 0; + vdev->disabled = false; atomic_set(&vdev->isr, 0); vdev->config_vector = VIRTIO_NO_VECTOR; virtio_notify_vector(vdev, vdev->config_vector); @@ -2330,17 +2416,24 @@ VirtQueue *virtio_add_queue(VirtIODevice *vdev, int queue_size, return &vdev->vq[i]; } +void virtio_delete_queue(VirtQueue *vq) +{ + vq->vring.num = 0; + vq->vring.num_default = 0; + vq->handle_output = NULL; + vq->handle_aio_output = NULL; + g_free(vq->used_elems); + vq->used_elems = NULL; + virtio_virtqueue_reset_region_cache(vq); +} + void virtio_del_queue(VirtIODevice *vdev, int n) { if (n < 0 || n >= VIRTIO_QUEUE_MAX) { abort(); } - vdev->vq[n].vring.num = 0; - vdev->vq[n].vring.num_default = 0; - vdev->vq[n].handle_output = NULL; - vdev->vq[n].handle_aio_output = NULL; - g_free(vdev->vq[n].used_elems); + virtio_delete_queue(&vdev->vq[n]); } static void virtio_set_isr(VirtIODevice *vdev, int value) @@ -2399,6 +2492,10 @@ static bool virtio_packed_should_notify(VirtIODevice *vdev, VirtQueue *vq) VRingMemoryRegionCaches *caches; caches = vring_get_region_caches(vq); + if (!caches) { + return false; + } + vring_packed_event_read(vdev, &caches->avail, &e); old = vq->signalled_used; @@ -2553,6 +2650,13 @@ static bool virtio_started_needed(void *opaque) return vdev->started; } +static bool virtio_disabled_needed(void *opaque) +{ + VirtIODevice *vdev = opaque; + + return vdev->disabled; +} + static const VMStateDescription vmstate_virtqueue = { .name = "virtqueue_state", .version_id = 1, @@ -2718,6 +2822,17 @@ static const VMStateDescription vmstate_virtio_started = { } }; +static const VMStateDescription vmstate_virtio_disabled = { + .name = "virtio/disabled", + .version_id = 1, + .minimum_version_id = 1, + .needed = &virtio_disabled_needed, + .fields = (VMStateField[]) { + VMSTATE_BOOL(disabled, VirtIODevice), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription vmstate_virtio = { .name = "virtio", .version_id = 1, @@ -2735,6 +2850,7 @@ static const VMStateDescription vmstate_virtio = { &vmstate_virtio_extra_state, &vmstate_virtio_started, &vmstate_virtio_packed_virtqueues, + &vmstate_virtio_disabled, NULL } }; @@ -3384,17 +3500,12 @@ static bool virtio_queue_host_notifier_aio_poll(void *opaque) { EventNotifier *n = opaque; VirtQueue *vq = container_of(n, VirtQueue, host_notifier); - bool progress; if (!vq->vring.desc || virtio_queue_empty(vq)) { return false; } - progress = virtio_queue_notify_aio_vq(vq); - - /* In case the handler function re-enabled notifications */ - virtio_queue_set_notification(vq, 0); - return progress; + return virtio_queue_notify_aio_vq(vq); } static void virtio_queue_host_notifier_aio_poll_end(EventNotifier *n) @@ -3569,6 +3680,7 @@ static void virtio_device_instance_finalize(Object *obj) static Property virtio_properties[] = { DEFINE_VIRTIO_COMMON_FEATURES(VirtIODevice, host_features), DEFINE_PROP_BOOL("use-started", VirtIODevice, use_started, true), + DEFINE_PROP_BOOL("use-disabled-flag", VirtIODevice, use_disabled_flag, true), DEFINE_PROP_END_OF_LIST(), }; @@ -3685,7 +3797,7 @@ static void virtio_device_class_init(ObjectClass *klass, void *data) dc->realize = virtio_device_realize; dc->unrealize = virtio_device_unrealize; dc->bus_type = TYPE_VIRTIO_BUS; - dc->props = virtio_properties; + device_class_set_props(dc, virtio_properties); vdc->start_ioeventfd = virtio_device_start_ioeventfd_impl; vdc->stop_ioeventfd = virtio_device_stop_ioeventfd_impl; diff --git a/hw/watchdog/cmsdk-apb-watchdog.c b/hw/watchdog/cmsdk-apb-watchdog.c index e6f3b93c44..1541365914 100644 --- a/hw/watchdog/cmsdk-apb-watchdog.c +++ b/hw/watchdog/cmsdk-apb-watchdog.c @@ -371,7 +371,7 @@ static void cmsdk_apb_watchdog_class_init(ObjectClass *klass, void *data) dc->realize = cmsdk_apb_watchdog_realize; dc->vmsd = &cmsdk_apb_watchdog_vmstate; dc->reset = cmsdk_apb_watchdog_reset; - dc->props = cmsdk_apb_watchdog_properties; + device_class_set_props(dc, cmsdk_apb_watchdog_properties); } static const TypeInfo cmsdk_apb_watchdog_info = { diff --git a/hw/watchdog/wdt_aspeed.c b/hw/watchdog/wdt_aspeed.c index 145be6f99c..6352ba1b0e 100644 --- a/hw/watchdog/wdt_aspeed.c +++ b/hw/watchdog/wdt_aspeed.c @@ -93,11 +93,11 @@ static uint64_t aspeed_wdt_read(void *opaque, hwaddr offset, unsigned size) } -static void aspeed_wdt_reload(AspeedWDTState *s, bool pclk) +static void aspeed_wdt_reload(AspeedWDTState *s) { uint64_t reload; - if (pclk) { + if (!(s->regs[WDT_CTRL] & WDT_CTRL_1MHZ_CLK)) { reload = muldiv64(s->regs[WDT_RELOAD_VALUE], NANOSECONDS_PER_SECOND, s->pclk_freq); } else { @@ -109,6 +109,16 @@ static void aspeed_wdt_reload(AspeedWDTState *s, bool pclk) } } +static void aspeed_wdt_reload_1mhz(AspeedWDTState *s) +{ + uint64_t reload = s->regs[WDT_RELOAD_VALUE] * 1000ULL; + + if (aspeed_wdt_is_enabled(s)) { + timer_mod(s->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + reload); + } +} + + static void aspeed_wdt_write(void *opaque, hwaddr offset, uint64_t data, unsigned size) { @@ -130,13 +140,13 @@ static void aspeed_wdt_write(void *opaque, hwaddr offset, uint64_t data, case WDT_RESTART: if ((data & 0xFFFF) == WDT_RESTART_MAGIC) { s->regs[WDT_STATUS] = s->regs[WDT_RELOAD_VALUE]; - aspeed_wdt_reload(s, !(s->regs[WDT_CTRL] & WDT_CTRL_1MHZ_CLK)); + awc->wdt_reload(s); } break; case WDT_CTRL: if (enable && !aspeed_wdt_is_enabled(s)) { s->regs[WDT_CTRL] = data; - aspeed_wdt_reload(s, !(data & WDT_CTRL_1MHZ_CLK)); + awc->wdt_reload(s); } else if (!enable && aspeed_wdt_is_enabled(s)) { s->regs[WDT_CTRL] = data; timer_del(s->timer); @@ -219,7 +229,8 @@ static void aspeed_wdt_timer_expired(void *dev) return; } - qemu_log_mask(CPU_LOG_RESET, "Watchdog timer expired.\n"); + qemu_log_mask(CPU_LOG_RESET, "Watchdog timer %" HWADDR_PRIx " expired.\n", + s->iomem.addr); watchdog_perform_action(); timer_del(s->timer); } @@ -230,16 +241,8 @@ static void aspeed_wdt_realize(DeviceState *dev, Error **errp) { SysBusDevice *sbd = SYS_BUS_DEVICE(dev); AspeedWDTState *s = ASPEED_WDT(dev); - Error *err = NULL; - Object *obj; - obj = object_property_get_link(OBJECT(dev), "scu", &err); - if (!obj) { - error_propagate(errp, err); - error_prepend(errp, "required link 'scu' not found: "); - return; - } - s->scu = ASPEED_SCU(obj); + assert(s->scu); s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, aspeed_wdt_timer_expired, dev); @@ -253,6 +256,12 @@ static void aspeed_wdt_realize(DeviceState *dev, Error **errp) sysbus_init_mmio(sbd, &s->iomem); } +static Property aspeed_wdt_properties[] = { + DEFINE_PROP_LINK("scu", AspeedWDTState, scu, TYPE_ASPEED_SCU, + AspeedSCUState *), + DEFINE_PROP_END_OF_LIST(), +}; + static void aspeed_wdt_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -262,6 +271,7 @@ static void aspeed_wdt_class_init(ObjectClass *klass, void *data) dc->reset = aspeed_wdt_reset; set_bit(DEVICE_CATEGORY_MISC, dc->categories); dc->vmsd = &vmstate_aspeed_wdt; + device_class_set_props(dc, aspeed_wdt_properties); } static const TypeInfo aspeed_wdt_info = { @@ -282,6 +292,7 @@ static void aspeed_2400_wdt_class_init(ObjectClass *klass, void *data) awc->offset = 0x20; awc->ext_pulse_width_mask = 0xff; awc->reset_ctrl_reg = SCU_RESET_CONTROL1; + awc->wdt_reload = aspeed_wdt_reload; } static const TypeInfo aspeed_2400_wdt_info = { @@ -316,6 +327,7 @@ static void aspeed_2500_wdt_class_init(ObjectClass *klass, void *data) awc->ext_pulse_width_mask = 0xfffff; awc->reset_ctrl_reg = SCU_RESET_CONTROL1; awc->reset_pulse = aspeed_2500_wdt_reset_pulse; + awc->wdt_reload = aspeed_wdt_reload_1mhz; } static const TypeInfo aspeed_2500_wdt_info = { @@ -335,6 +347,7 @@ static void aspeed_2600_wdt_class_init(ObjectClass *klass, void *data) awc->ext_pulse_width_mask = 0xfffff; /* TODO */ awc->reset_ctrl_reg = AST2600_SCU_RESET_CONTROL1; awc->reset_pulse = aspeed_2500_wdt_reset_pulse; + awc->wdt_reload = aspeed_wdt_reload_1mhz; } static const TypeInfo aspeed_2600_wdt_info = { diff --git a/hw/xen/xen-bus.c b/hw/xen/xen-bus.c index c2ad22a42d..18237b34ea 100644 --- a/hw/xen/xen-bus.c +++ b/hw/xen/xen-bus.c @@ -1089,8 +1089,26 @@ static void xen_device_event(void *opaque) } } +void xen_device_set_event_channel_context(XenDevice *xendev, + XenEventChannel *channel, + AioContext *ctx, + Error **errp) +{ + if (!channel) { + error_setg(errp, "bad channel"); + return; + } + + if (channel->ctx) + aio_set_fd_handler(channel->ctx, xenevtchn_fd(channel->xeh), true, + NULL, NULL, NULL, NULL); + + channel->ctx = ctx; + aio_set_fd_handler(channel->ctx, xenevtchn_fd(channel->xeh), true, + xen_device_event, NULL, xen_device_poll, channel); +} + XenEventChannel *xen_device_bind_event_channel(XenDevice *xendev, - AioContext *ctx, unsigned int port, XenEventHandler handler, void *opaque, Error **errp) @@ -1116,9 +1134,10 @@ XenEventChannel *xen_device_bind_event_channel(XenDevice *xendev, channel->handler = handler; channel->opaque = opaque; - channel->ctx = ctx; - aio_set_fd_handler(channel->ctx, xenevtchn_fd(channel->xeh), true, - xen_device_event, NULL, xen_device_poll, channel); + /* Only reason for failure is a NULL channel */ + xen_device_set_event_channel_context(xendev, channel, + qemu_get_aio_context(), + &error_abort); QLIST_INSERT_HEAD(&xendev->event_channels, channel, list); @@ -1332,7 +1351,7 @@ static void xen_device_class_init(ObjectClass *class, void *data) dev_class->realize = xen_device_realize; dev_class->unrealize = xen_device_unrealize; - dev_class->props = xen_device_props; + device_class_set_props(dev_class, xen_device_props); dev_class->bus_type = TYPE_XEN_BUS; } diff --git a/hw/xen/xen-common.c b/hw/xen/xen-common.c index 5284b0dec1..a15070f7f6 100644 --- a/hw/xen/xen-common.c +++ b/hw/xen/xen-common.c @@ -11,12 +11,15 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" #include "qemu/module.h" +#include "qapi/error.h" #include "hw/xen/xen-legacy-backend.h" +#include "hw/xen/xen_pt.h" #include "chardev/char.h" #include "sysemu/accel.h" #include "sysemu/runstate.h" #include "migration/misc.h" #include "migration/global_state.h" +#include "hw/boards.h" //#define DEBUG_XEN @@ -124,6 +127,16 @@ static void xen_change_state_handler(void *opaque, int running, } } +static bool xen_get_igd_gfx_passthru(Object *obj, Error **errp) +{ + return has_igd_gfx_passthru; +} + +static void xen_set_igd_gfx_passthru(Object *obj, bool value, Error **errp) +{ + has_igd_gfx_passthru = value; +} + static void xen_setup_post(MachineState *ms, AccelState *accel) { int rc; @@ -139,6 +152,8 @@ static void xen_setup_post(MachineState *ms, AccelState *accel) static int xen_init(MachineState *ms) { + MachineClass *mc = MACHINE_GET_CLASS(ms); + xen_xc = xc_interface_open(0, 0, 0); if (xen_xc == NULL) { xen_pv_printf(NULL, 0, "can't open xen interface\n"); @@ -158,6 +173,10 @@ static int xen_init(MachineState *ms) return -1; } qemu_add_vm_change_state_handler(xen_change_state_handler, NULL); + /* + * opt out of system RAM being allocated by generic code + */ + mc->default_ram_id = NULL; return 0; } @@ -177,6 +196,12 @@ static void xen_accel_class_init(ObjectClass *oc, void *data) ac->compat_props = g_ptr_array_new(); compat_props_add(ac->compat_props, compat, G_N_ELEMENTS(compat)); + + object_class_property_add_bool(oc, "igd-passthru", + xen_get_igd_gfx_passthru, xen_set_igd_gfx_passthru, + &error_abort); + object_class_property_set_description(oc, "igd-passthru", + "Set on/off to enable/disable igd passthrou", &error_abort); } #define TYPE_XEN_ACCEL ACCEL_CLASS_NAME("xen") diff --git a/hw/xen/xen-legacy-backend.c b/hw/xen/xen-legacy-backend.c index 4412d7aa76..4a373b2373 100644 --- a/hw/xen/xen-legacy-backend.c +++ b/hw/xen/xen-legacy-backend.c @@ -786,7 +786,7 @@ static void xendev_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->props = xendev_properties; + device_class_set_props(dc, xendev_properties); set_bit(DEVICE_CATEGORY_MISC, dc->categories); /* xen-backend devices can be plugged/unplugged dynamically */ dc->user_creatable = true; @@ -824,7 +824,7 @@ static void xen_sysdev_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->props = xen_sysdev_properties; + device_class_set_props(dc, xen_sysdev_properties); dc->bus_type = TYPE_XENSYSBUS; } diff --git a/hw/xen/xen_pt.c b/hw/xen/xen_pt.c index 8fbaf2eae9..b91082cb8b 100644 --- a/hw/xen/xen_pt.c +++ b/hw/xen/xen_pt.c @@ -65,6 +65,8 @@ #include "qemu/range.h" #include "exec/address-spaces.h" +bool has_igd_gfx_passthru; + #define XEN_PT_NR_IRQS (256) static uint8_t xen_pt_mapped_machine_irq[XEN_PT_NR_IRQS] = {0}; @@ -962,7 +964,7 @@ static void xen_pci_passthrough_class_init(ObjectClass *klass, void *data) k->config_write = xen_pt_pci_write_config; set_bit(DEVICE_CATEGORY_MISC, dc->categories); dc->desc = "Assign an host PCI device with Xen"; - dc->props = xen_pci_passthrough_properties; + device_class_set_props(dc, xen_pci_passthrough_properties); }; static void xen_pci_passthrough_finalize(Object *obj) diff --git a/hw/xen/xen_pt.h b/hw/xen/xen_pt.h index 9167bbaf6d..179775db7b 100644 --- a/hw/xen/xen_pt.h +++ b/hw/xen/xen_pt.h @@ -203,7 +203,7 @@ typedef struct XenPTMSIX { uint64_t mmio_base_addr; MemoryRegion mmio; void *phys_iomem_base; - XenPTMSIXEntry msix_entry[0]; + XenPTMSIXEntry msix_entry[]; } XenPTMSIX; struct XenPCIPassthroughState { diff --git a/hw/xen/xen_pt_graphics.c b/hw/xen/xen_pt_graphics.c index b69732729b..a3bc7e3921 100644 --- a/hw/xen/xen_pt_graphics.c +++ b/hw/xen/xen_pt_graphics.c @@ -222,7 +222,7 @@ void xen_pt_setup_vga(XenPCIPassthroughState *s, XenHostPCIDevice *dev, } /* Currently we fixed this address as a primary for legacy BIOS. */ - cpu_physical_memory_rw(0xc0000, bios, bios_size, 1); + cpu_physical_memory_write(0xc0000, bios, bios_size); } uint32_t igd_read_opregion(XenPCIPassthroughState *s) diff --git a/hw/xen/xen_pt_load_rom.c b/hw/xen/xen_pt_load_rom.c index 307a5c93e2..a50a80837e 100644 --- a/hw/xen/xen_pt_load_rom.c +++ b/hw/xen/xen_pt_load_rom.c @@ -3,12 +3,8 @@ */ #include "qemu/osdep.h" #include "qapi/error.h" -#include "hw/i386/pc.h" #include "qemu/error-report.h" -#include "ui/console.h" #include "hw/loader.h" -#include "monitor/monitor.h" -#include "qemu/range.h" #include "hw/pci/pci.h" #include "xen_pt.h" diff --git a/hw/xenpv/xen_machine_pv.c b/hw/xenpv/xen_machine_pv.c index 3a8af1a1e0..8df575a457 100644 --- a/hw/xenpv/xen_machine_pv.c +++ b/hw/xenpv/xen_machine_pv.c @@ -60,7 +60,7 @@ static void xen_init_pv(MachineState *machine) xen_be_register("qnic", &xen_netdev_ops); /* configure framebuffer */ - if (xenfb_enabled) { + if (vga_interface_type == VGA_XENFB) { xen_config_dev_vfb(0, "vnc"); xen_config_dev_vkbd(0); } diff --git a/hw/xtensa/sim.c b/hw/xtensa/sim.c index a22743a3d6..aeb46d86f5 100644 --- a/hw/xtensa/sim.c +++ b/hw/xtensa/sim.c @@ -108,7 +108,7 @@ void xtensa_sim_load_kernel(XtensaCPU *cpu, MachineState *machine) uint64_t elf_entry; uint64_t elf_lowaddr; int success = load_elf(kernel_filename, NULL, translate_phys_addr, cpu, - &elf_entry, &elf_lowaddr, NULL, big_endian, + &elf_entry, &elf_lowaddr, NULL, NULL, big_endian, EM_XTENSA, 0, 0); if (success > 0) { diff --git a/hw/xtensa/xtfpga.c b/hw/xtensa/xtfpga.c index 8220c7a379..60ccc74f5f 100644 --- a/hw/xtensa/xtfpga.c +++ b/hw/xtensa/xtfpga.c @@ -380,6 +380,7 @@ static void xtfpga_init(const XtfpgaBoardDesc *board, MachineState *machine) cur_tagptr = put_tag(cur_tagptr, BP_TAG_FDT, sizeof(dtb_addr), &dtb_addr); cur_lowmem = QEMU_ALIGN_UP(cur_lowmem + fdt_size, 4 * KiB); + g_free(fdt); } #else if (dtb_filename) { @@ -415,7 +416,7 @@ static void xtfpga_init(const XtfpgaBoardDesc *board, MachineState *machine) uint64_t elf_entry; uint64_t elf_lowaddr; int success = load_elf(kernel_filename, NULL, translate_phys_addr, cpu, - &elf_entry, &elf_lowaddr, NULL, be, EM_XTENSA, 0, 0); + &elf_entry, &elf_lowaddr, NULL, NULL, be, EM_XTENSA, 0, 0); if (success > 0) { entry_point = elf_entry; } else { diff --git a/include/block/aio-wait.h b/include/block/aio-wait.h index afeeb18f95..716d2639df 100644 --- a/include/block/aio-wait.h +++ b/include/block/aio-wait.h @@ -26,6 +26,7 @@ #define QEMU_AIO_WAIT_H #include "block/aio.h" +#include "qemu/main-loop.h" /** * AioWait: @@ -124,4 +125,25 @@ void aio_wait_kick(void); */ void aio_wait_bh_oneshot(AioContext *ctx, QEMUBHFunc *cb, void *opaque); +/** + * in_aio_context_home_thread: + * @ctx: the aio context + * + * Return whether we are running in the thread that normally runs @ctx. Note + * that acquiring/releasing ctx does not affect the outcome, each AioContext + * still only has one home thread that is responsible for running it. + */ +static inline bool in_aio_context_home_thread(AioContext *ctx) +{ + if (ctx == qemu_get_current_aio_context()) { + return true; + } + + if (ctx == qemu_get_aio_context()) { + return qemu_mutex_iothread_locked(); + } else { + return false; + } +} + #endif /* QEMU_AIO_WAIT_H */ diff --git a/include/block/aio.h b/include/block/aio.h index 6b0d52f732..62ed954344 100644 --- a/include/block/aio.h +++ b/include/block/aio.h @@ -14,6 +14,9 @@ #ifndef QEMU_AIO_H #define QEMU_AIO_H +#ifdef CONFIG_LINUX_IO_URING +#include +#endif #include "qemu/queue.h" #include "qemu/event_notifier.h" #include "qemu/thread.h" @@ -42,6 +45,7 @@ void qemu_aio_unref(void *p); void qemu_aio_ref(void *p); typedef struct AioHandler AioHandler; +typedef QLIST_HEAD(, AioHandler) AioHandlerList; typedef void QEMUBHFunc(void *opaque); typedef bool AioPollFn(void *opaque); typedef void IOHandler(void *opaque); @@ -49,6 +53,72 @@ typedef void IOHandler(void *opaque); struct Coroutine; struct ThreadPool; struct LinuxAioState; +struct LuringState; + +/* Is polling disabled? */ +bool aio_poll_disabled(AioContext *ctx); + +/* Callbacks for file descriptor monitoring implementations */ +typedef struct { + /* + * update: + * @ctx: the AioContext + * @old_node: the existing handler or NULL if this file descriptor is being + * monitored for the first time + * @new_node: the new handler or NULL if this file descriptor is being + * removed + * + * Add/remove/modify a monitored file descriptor. + * + * Called with ctx->list_lock acquired. + */ + void (*update)(AioContext *ctx, AioHandler *old_node, AioHandler *new_node); + + /* + * wait: + * @ctx: the AioContext + * @ready_list: list for handlers that become ready + * @timeout: maximum duration to wait, in nanoseconds + * + * Wait for file descriptors to become ready and place them on ready_list. + * + * Called with ctx->list_lock incremented but not locked. + * + * Returns: number of ready file descriptors. + */ + int (*wait)(AioContext *ctx, AioHandlerList *ready_list, int64_t timeout); + + /* + * need_wait: + * @ctx: the AioContext + * + * Tell aio_poll() when to stop userspace polling early because ->wait() + * has fds ready. + * + * File descriptor monitoring implementations that cannot poll fd readiness + * from userspace should use aio_poll_disabled() here. This ensures that + * file descriptors are not starved by handlers that frequently make + * progress via userspace polling. + * + * Returns: true if ->wait() should be called, false otherwise. + */ + bool (*need_wait)(AioContext *ctx); +} FDMonOps; + +/* + * Each aio_bh_poll() call carves off a slice of the BH list, so that newly + * scheduled BHs are not processed until the next aio_bh_poll() call. All + * active aio_bh_poll() calls chain their slices together in a list, so that + * nested aio_bh_poll() calls process all scheduled bottom halves. + */ +typedef QSLIST_HEAD(, QEMUBH) BHList; +typedef struct BHListSlice BHListSlice; +struct BHListSlice { + BHList bh_list; + QSIMPLEQ_ENTRY(BHListSlice) next; +}; + +typedef QSLIST_HEAD(, AioHandler) AioHandlerSList; struct AioContext { GSource source; @@ -57,15 +127,22 @@ struct AioContext { QemuRecMutex lock; /* The list of registered AIO handlers. Protected by ctx->list_lock. */ - QLIST_HEAD(, AioHandler) aio_handlers; + AioHandlerList aio_handlers; + + /* The list of AIO handlers to be deleted. Protected by ctx->list_lock. */ + AioHandlerList deleted_aio_handlers; /* Used to avoid unnecessary event_notifier_set calls in aio_notify; - * accessed with atomic primitives. If this field is 0, everything - * (file descriptors, bottom halves, timers) will be re-evaluated - * before the next blocking poll(), thus the event_notifier_set call - * can be skipped. If it is non-zero, you may need to wake up a - * concurrent aio_poll or the glib main event loop, making - * event_notifier_set necessary. + * only written from the AioContext home thread, or under the BQL in + * the case of the main AioContext. However, it is read from any + * thread so it is still accessed with atomic primitives. + * + * If this field is 0, everything (file descriptors, bottom halves, + * timers) will be re-evaluated before the next blocking poll() or + * io_uring wait; therefore, the event_notifier_set call can be + * skipped. If it is non-zero, you may need to wake up a concurrent + * aio_poll or the glib main event loop, making event_notifier_set + * necessary. * * Bit 0 is reserved for GSource usage of the AioContext, and is 1 * between a call to aio_ctx_prepare and the next call to aio_ctx_check. @@ -90,8 +167,11 @@ struct AioContext { */ QemuLockCnt list_lock; - /* Anchor of the list of Bottom Halves belonging to the context */ - struct QEMUBH *first_bh; + /* Bottom Halves pending aio_bh_poll() processing */ + BHList bh_list; + + /* Chained BH list slices for each nested aio_bh_poll() call */ + QSIMPLEQ_HEAD(, BHListSlice) bh_slice_list; /* Used by aio_notify. * @@ -117,11 +197,23 @@ struct AioContext { struct ThreadPool *thread_pool; #ifdef CONFIG_LINUX_AIO - /* State for native Linux AIO. Uses aio_context_acquire/release for + /* + * State for native Linux AIO. Uses aio_context_acquire/release for * locking. */ struct LinuxAioState *linux_aio; #endif +#ifdef CONFIG_LINUX_IO_URING + /* + * State for Linux io_uring. Uses aio_context_acquire/release for + * locking. + */ + struct LuringState *linux_io_uring; + + /* State for file descriptor monitoring using Linux io_uring */ + struct io_uring fdmon_io_uring; + AioHandlerSList submit_list; +#endif /* TimerLists for calling timers - one per clock type. Has its own * locking. @@ -139,13 +231,21 @@ struct AioContext { int64_t poll_grow; /* polling time growth factor */ int64_t poll_shrink; /* polling time shrink factor */ + /* + * List of handlers participating in userspace polling. Protected by + * ctx->list_lock. Iterated and modified mostly by the event loop thread + * from aio_poll() with ctx->list_lock incremented. aio_set_fd_handler() + * only touches the list to delete nodes if ctx->list_lock's count is zero. + */ + AioHandlerList poll_aio_handlers; + /* Are we in polling mode or monitoring file descriptors? */ bool poll_started; /* epoll(7) state used when built with CONFIG_EPOLL */ int epollfd; - bool epoll_enabled; - bool epoll_available; + + const FDMonOps *fdmon_ops; }; /** @@ -386,6 +486,11 @@ struct LinuxAioState *aio_setup_linux_aio(AioContext *ctx, Error **errp); /* Return the LinuxAioState bound to this AioContext */ struct LinuxAioState *aio_get_linux_aio(AioContext *ctx); +/* Setup the LuringState bound to this AioContext */ +struct LuringState *aio_setup_linux_io_uring(AioContext *ctx, Error **errp); + +/* Return the LuringState bound to this AioContext */ +struct LuringState *aio_get_linux_io_uring(AioContext *ctx); /** * aio_timer_new_with_attrs: * @ctx: the aio context @@ -580,19 +685,6 @@ void aio_co_enter(AioContext *ctx, struct Coroutine *co); */ AioContext *qemu_get_current_aio_context(void); -/** - * in_aio_context_home_thread: - * @ctx: the aio context - * - * Return whether we are running in the thread that normally runs @ctx. Note - * that acquiring/releasing ctx does not affect the outcome, each AioContext - * still only has one home thread that is responsible for running it. - */ -static inline bool in_aio_context_home_thread(AioContext *ctx) -{ - return ctx == qemu_get_current_aio_context(); -} - /** * aio_context_setup: * @ctx: the aio context diff --git a/include/block/block-copy.h b/include/block/block-copy.h index 0a161724d7..aac85e1488 100644 --- a/include/block/block-copy.h +++ b/include/block/block-copy.h @@ -18,79 +18,30 @@ #include "block/block.h" #include "qemu/co-shared-resource.h" -typedef struct BlockCopyInFlightReq { - int64_t start_byte; - int64_t end_byte; - QLIST_ENTRY(BlockCopyInFlightReq) list; - CoQueue wait_queue; /* coroutines blocked on this request */ -} BlockCopyInFlightReq; - typedef void (*ProgressBytesCallbackFunc)(int64_t bytes, void *opaque); -typedef void (*ProgressResetCallbackFunc)(void *opaque); -typedef struct BlockCopyState { - /* - * BdrvChild objects are not owned or managed by block-copy. They are - * provided by block-copy user and user is responsible for appropriate - * permissions on these children. - */ - BdrvChild *source; - BdrvChild *target; - BdrvDirtyBitmap *copy_bitmap; - int64_t cluster_size; - bool use_copy_range; - int64_t copy_size; - uint64_t len; - QLIST_HEAD(, BlockCopyInFlightReq) inflight_reqs; - - BdrvRequestFlags write_flags; - - /* - * skip_unallocated: - * - * Used by sync=top jobs, which first scan the source node for unallocated - * areas and clear them in the copy_bitmap. During this process, the bitmap - * is thus not fully initialized: It may still have bits set for areas that - * are unallocated and should actually not be copied. - * - * This is indicated by skip_unallocated. - * - * In this case, block_copy() will query the source’s allocation status, - * skip unallocated regions, clear them in the copy_bitmap, and invoke - * block_copy_reset_unallocated() every time it does. - */ - bool skip_unallocated; - - /* progress_bytes_callback: called when some copying progress is done. */ - ProgressBytesCallbackFunc progress_bytes_callback; - - /* - * progress_reset_callback: called when some bytes reset from copy_bitmap - * (see @skip_unallocated above). The callee is assumed to recalculate how - * many bytes remain based on the dirty bit count of copy_bitmap. - */ - ProgressResetCallbackFunc progress_reset_callback; - void *progress_opaque; - - SharedResource *mem; -} BlockCopyState; +typedef struct BlockCopyState BlockCopyState; BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target, int64_t cluster_size, BdrvRequestFlags write_flags, Error **errp); -void block_copy_set_callbacks( +void block_copy_set_progress_callback( BlockCopyState *s, ProgressBytesCallbackFunc progress_bytes_callback, - ProgressResetCallbackFunc progress_reset_callback, void *progress_opaque); +void block_copy_set_progress_meter(BlockCopyState *s, ProgressMeter *pm); + void block_copy_state_free(BlockCopyState *s); int64_t block_copy_reset_unallocated(BlockCopyState *s, int64_t offset, int64_t *count); -int coroutine_fn block_copy(BlockCopyState *s, int64_t start, uint64_t bytes, +int coroutine_fn block_copy(BlockCopyState *s, int64_t offset, int64_t bytes, bool *error_is_read); +BdrvDirtyBitmap *block_copy_dirty_bitmap(BlockCopyState *s); +void block_copy_set_skip_unallocated(BlockCopyState *s, bool skip); + #endif /* BLOCK_COPY_H */ diff --git a/include/block/block-hmp-cmds.h b/include/block/block-hmp-cmds.h new file mode 100644 index 0000000000..3412e108ca --- /dev/null +++ b/include/block/block-hmp-cmds.h @@ -0,0 +1,54 @@ +/* + * HMP commands related to the block layer + * + * Copyright (c) 2003-2008 Fabrice Bellard + * Copyright (c) 2020 Red Hat, Inc. + * Copyright IBM, Corp. 2011 + * + * Authors: + * Anthony Liguori + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + */ + +#ifndef BLOCK_HMP_COMMANDS_H +#define BLOCK_HMP_COMMANDS_H + +void hmp_drive_add(Monitor *mon, const QDict *qdict); + +void hmp_commit(Monitor *mon, const QDict *qdict); +void hmp_drive_del(Monitor *mon, const QDict *qdict); + +void hmp_drive_mirror(Monitor *mon, const QDict *qdict); +void hmp_drive_backup(Monitor *mon, const QDict *qdict); + +void hmp_block_job_set_speed(Monitor *mon, const QDict *qdict); +void hmp_block_job_cancel(Monitor *mon, const QDict *qdict); +void hmp_block_job_pause(Monitor *mon, const QDict *qdict); +void hmp_block_job_resume(Monitor *mon, const QDict *qdict); +void hmp_block_job_complete(Monitor *mon, const QDict *qdict); + +void hmp_snapshot_blkdev(Monitor *mon, const QDict *qdict); +void hmp_snapshot_blkdev_internal(Monitor *mon, const QDict *qdict); +void hmp_snapshot_delete_blkdev_internal(Monitor *mon, const QDict *qdict); + +void hmp_nbd_server_start(Monitor *mon, const QDict *qdict); +void hmp_nbd_server_add(Monitor *mon, const QDict *qdict); +void hmp_nbd_server_remove(Monitor *mon, const QDict *qdict); +void hmp_nbd_server_stop(Monitor *mon, const QDict *qdict); + +void hmp_block_resize(Monitor *mon, const QDict *qdict); +void hmp_block_stream(Monitor *mon, const QDict *qdict); +void hmp_block_passwd(Monitor *mon, const QDict *qdict); +void hmp_block_set_io_throttle(Monitor *mon, const QDict *qdict); +void hmp_eject(Monitor *mon, const QDict *qdict); + +void hmp_qemu_io(Monitor *mon, const QDict *qdict); + +void hmp_info_block(Monitor *mon, const QDict *qdict); +void hmp_info_blockstats(Monitor *mon, const QDict *qdict); +void hmp_info_block_jobs(Monitor *mon, const QDict *qdict); +void hmp_info_snapshots(Monitor *mon, const QDict *qdict); + +#endif diff --git a/include/block/block.h b/include/block/block.h index 1df9848e74..b05995fe9c 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -51,18 +51,6 @@ typedef enum { */ BDRV_REQ_MAY_UNMAP = 0x4, - /* - * The BDRV_REQ_NO_SERIALISING flag is only valid for reads and means that - * we don't want wait_serialising_requests() during the read operation. - * - * This flag is used for backup copy-on-write operations, when we need to - * read old data before write (write notifier triggered). It is okay since - * we already waited for other serializing requests in the initiating write - * (see bdrv_aligned_pwritev), and it is necessary if the initiating write - * is already serializing (without the flag, the read would deadlock - * waiting for the serialising write to complete). - */ - BDRV_REQ_NO_SERIALISING = 0x8, BDRV_REQ_FUA = 0x10, BDRV_REQ_WRITE_COMPRESSED = 0x20, @@ -126,6 +114,7 @@ typedef struct HDGeometry { ignoring the format layer */ #define BDRV_O_NO_IO 0x10000 /* don't initialize for I/O */ #define BDRV_O_AUTO_RDONLY 0x20000 /* degrade to read-only if opening read-write fails */ +#define BDRV_O_IO_URING 0x40000 /* use io_uring instead of the thread pool */ #define BDRV_O_CACHE_MASK (BDRV_O_NOCACHE | BDRV_O_NO_FLUSH) @@ -280,6 +269,7 @@ enum { }; char *bdrv_perm_names(uint64_t perm); +uint64_t bdrv_qapi_perm_to_blk_perm(BlockPermission qapi_perm); /* disk I/O throttling */ void bdrv_init(void); @@ -293,12 +283,14 @@ BlockDriver *bdrv_find_format(const char *format_name); int bdrv_create(BlockDriver *drv, const char* filename, QemuOpts *opts, Error **errp); int bdrv_create_file(const char *filename, QemuOpts *opts, Error **errp); + BlockDriverState *bdrv_new(void); void bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top, Error **errp); void bdrv_replace_node(BlockDriverState *from, BlockDriverState *to, Error **errp); +int bdrv_parse_aio(const char *mode, int *flags); int bdrv_parse_cache_mode(const char *mode, int *flags, bool *writethrough); int bdrv_parse_discard_flags(const char *mode, int *flags); BdrvChild *bdrv_open_child(const char *filename, @@ -372,6 +364,7 @@ bool bdrv_is_backing_chain_frozen(BlockDriverState *bs, BlockDriverState *base, int bdrv_freeze_backing_chain(BlockDriverState *bs, BlockDriverState *base, Error **errp); void bdrv_unfreeze_backing_chain(BlockDriverState *bs, BlockDriverState *base); +int coroutine_fn bdrv_co_delete_file(BlockDriverState *bs, Error **errp); typedef struct BdrvCheckResult { @@ -400,11 +393,6 @@ int bdrv_amend_options(BlockDriverState *bs_new, QemuOpts *opts, BlockDriverAmendStatusCB *status_cb, void *cb_opaque, Error **errp); -/* external snapshots */ -bool bdrv_recurse_is_first_non_filter(BlockDriverState *bs, - BlockDriverState *candidate); -bool bdrv_is_first_non_filter(BlockDriverState *candidate); - /* check if a named node can be replaced when doing drive-mirror */ BlockDriverState *check_to_replace_node(BlockDriverState *parent_bs, const char *node_name, Error **errp); @@ -468,7 +456,7 @@ void bdrv_lock_medium(BlockDriverState *bs, bool locked); void bdrv_eject(BlockDriverState *bs, bool eject_flag); const char *bdrv_get_format_name(BlockDriverState *bs); BlockDriverState *bdrv_find_node(const char *node_name); -BlockDeviceInfoList *bdrv_named_nodes_list(Error **errp); +BlockDeviceInfoList *bdrv_named_nodes_list(bool flat, Error **errp); XDbgBlockGraph *bdrv_get_xdbg_block_graph(Error **errp); BlockDriverState *bdrv_lookup_bs(const char *device, const char *node_name, diff --git a/include/block/block_int.h b/include/block/block_int.h index dd033d0b37..4c3587ea19 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -94,14 +94,13 @@ struct BlockDriver { * must implement them and return -ENOTSUP. */ bool is_filter; - /* for snapshots block filter like Quorum can implement the - * following recursive callback. - * It's purpose is to recurse on the filter children while calling - * bdrv_recurse_is_first_non_filter on them. - * For a sample implementation look in the future Quorum block filter. + /* + * Return true if @to_replace can be replaced by a BDS with the + * same data as @bs without it affecting @bs's behavior (that is, + * without it being visible to @bs's parents). */ - bool (*bdrv_recurse_is_first_non_filter)(BlockDriverState *bs, - BlockDriverState *candidate); + bool (*bdrv_recurse_can_replace)(BlockDriverState *bs, + BlockDriverState *to_replace); int (*bdrv_probe)(const uint8_t *buf, int buf_size, const char *filename); int (*bdrv_probe_device)(const char *filename); @@ -123,6 +122,7 @@ struct BlockDriver { int (*bdrv_reopen_prepare)(BDRVReopenState *reopen_state, BlockReopenQueue *queue, Error **errp); void (*bdrv_reopen_commit)(BDRVReopenState *reopen_state); + void (*bdrv_reopen_commit_post)(BDRVReopenState *reopen_state); void (*bdrv_reopen_abort)(BDRVReopenState *reopen_state); void (*bdrv_join_options)(QDict *options, QDict *old_options); @@ -135,7 +135,8 @@ struct BlockDriver { void (*bdrv_close)(BlockDriverState *bs); int coroutine_fn (*bdrv_co_create)(BlockdevCreateOptions *opts, Error **errp); - int coroutine_fn (*bdrv_co_create_opts)(const char *filename, + int coroutine_fn (*bdrv_co_create_opts)(BlockDriver *drv, + const char *filename, QemuOpts *opts, Error **errp); int (*bdrv_make_empty)(BlockDriverState *bs); @@ -314,6 +315,10 @@ struct BlockDriver { */ int coroutine_fn (*bdrv_co_flush)(BlockDriverState *bs); + /* Delete a created file. */ + int coroutine_fn (*bdrv_co_delete_file)(BlockDriverState *bs, + Error **errp); + /* * Flushes all data that was already written to the OS all the way down to * the disk (for example file-posix.c calls fsync()). @@ -999,8 +1004,7 @@ extern unsigned int bdrv_drain_all_count; void bdrv_apply_subtree_drain(BdrvChild *child, BlockDriverState *new_parent); void bdrv_unapply_subtree_drain(BdrvChild *child, BlockDriverState *old_parent); -bool coroutine_fn bdrv_wait_serialising_requests(BdrvTrackedRequest *self); -void bdrv_mark_request_serialising(BdrvTrackedRequest *req, uint64_t align); +bool coroutine_fn bdrv_mark_request_serialising(BdrvTrackedRequest *req, uint64_t align); BdrvTrackedRequest *coroutine_fn bdrv_co_get_self_request(BlockDriverState *bs); int get_tmp_filename(char *filename, int size); @@ -1217,8 +1221,6 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, BlockCompletionFunc *cb, void *opaque, JobTxn *txn, Error **errp); -void hmp_drive_add_node(Monitor *mon, const char *optstr); - BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs, const char *child_name, const BdrvChildRole *child_role, @@ -1227,6 +1229,9 @@ BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs, void *opaque, Error **errp); void bdrv_root_unref_child(BdrvChild *child); +void bdrv_get_cumulative_perm(BlockDriverState *bs, uint64_t *perm, + uint64_t *shared_perm); + /** * Sets a BdrvChild's permissions. Avoid if the parent is a BDS; use * bdrv_child_refresh_perms() instead and make the parent's @@ -1264,6 +1269,9 @@ void bdrv_format_default_perms(BlockDriverState *bs, BdrvChild *c, uint64_t perm, uint64_t shared, uint64_t *nperm, uint64_t *nshared); +bool bdrv_recurse_can_replace(BlockDriverState *bs, + BlockDriverState *to_replace); + /* * Default implementation for drivers to pass bdrv_co_block_status() to * their file. @@ -1320,4 +1328,18 @@ int coroutine_fn bdrv_co_copy_range_to(BdrvChild *src, uint64_t src_offset, int refresh_total_sectors(BlockDriverState *bs, int64_t hint); +void bdrv_set_monitor_owned(BlockDriverState *bs); +BlockDriverState *bds_tree_init(QDict *bs_opts, Error **errp); + +/** + * Simple implementation of bdrv_co_create_opts for protocol drivers + * which only support creation via opening a file + * (usually existing raw storage device) + */ +int coroutine_fn bdrv_co_create_opts_simple(BlockDriver *drv, + const char *filename, + QemuOpts *opts, + Error **errp); +extern QemuOptsList bdrv_create_opts_simple; + #endif /* BLOCK_INT_H */ diff --git a/include/block/dirty-bitmap.h b/include/block/dirty-bitmap.h index e2b20ecab9..8a10029418 100644 --- a/include/block/dirty-bitmap.h +++ b/include/block/dirty-bitmap.h @@ -105,10 +105,13 @@ for (bitmap = bdrv_dirty_bitmap_first(bs); bitmap; \ bitmap = bdrv_dirty_bitmap_next(bitmap)) char *bdrv_dirty_bitmap_sha256(const BdrvDirtyBitmap *bitmap, Error **errp); -int64_t bdrv_dirty_bitmap_next_zero(BdrvDirtyBitmap *bitmap, uint64_t offset, - uint64_t bytes); +int64_t bdrv_dirty_bitmap_next_dirty(BdrvDirtyBitmap *bitmap, int64_t offset, + int64_t bytes); +int64_t bdrv_dirty_bitmap_next_zero(BdrvDirtyBitmap *bitmap, int64_t offset, + int64_t bytes); bool bdrv_dirty_bitmap_next_dirty_area(BdrvDirtyBitmap *bitmap, - uint64_t *offset, uint64_t *bytes); + int64_t start, int64_t end, int64_t max_dirty_count, + int64_t *dirty_start, int64_t *dirty_count); BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap, Error **errp); diff --git a/include/block/nbd.h b/include/block/nbd.h index 7f46932d80..20363280ae 100644 --- a/include/block/nbd.h +++ b/include/block/nbd.h @@ -353,6 +353,7 @@ void nbd_client_put(NBDClient *client); void nbd_server_start(SocketAddress *addr, const char *tls_creds, const char *tls_authz, Error **errp); +void nbd_server_start_options(NbdServerOptions *arg, Error **errp); /* nbd_read * Reads @size bytes from @ioc. Returns 0 on success. diff --git a/include/block/qapi.h b/include/block/qapi.h index cd9410dee3..22c7807c89 100644 --- a/include/block/qapi.h +++ b/include/block/qapi.h @@ -29,7 +29,9 @@ #include "block/snapshot.h" BlockDeviceInfo *bdrv_block_device_info(BlockBackend *blk, - BlockDriverState *bs, Error **errp); + BlockDriverState *bs, + bool flat, + Error **errp); int bdrv_query_snapshot_info_list(BlockDriverState *bs, SnapshotInfoList **p_list, Error **errp); diff --git a/include/block/raw-aio.h b/include/block/raw-aio.h index 4629f24d08..251b10d273 100644 --- a/include/block/raw-aio.h +++ b/include/block/raw-aio.h @@ -57,6 +57,18 @@ void laio_attach_aio_context(LinuxAioState *s, AioContext *new_context); void laio_io_plug(BlockDriverState *bs, LinuxAioState *s); void laio_io_unplug(BlockDriverState *bs, LinuxAioState *s); #endif +/* io_uring.c - Linux io_uring implementation */ +#ifdef CONFIG_LINUX_IO_URING +typedef struct LuringState LuringState; +LuringState *luring_init(Error **errp); +void luring_cleanup(LuringState *s); +int coroutine_fn luring_co_submit(BlockDriverState *bs, LuringState *s, int fd, + uint64_t offset, QEMUIOVector *qiov, int type); +void luring_detach_aio_context(LuringState *s, AioContext *old_context); +void luring_attach_aio_context(LuringState *s, AioContext *new_context); +void luring_io_plug(BlockDriverState *bs, LuringState *s); +void luring_io_unplug(BlockDriverState *bs, LuringState *s); +#endif #ifdef _WIN32 typedef struct QEMUWin32AIOState QEMUWin32AIOState; diff --git a/include/block/snapshot.h b/include/block/snapshot.h index b5d5084a12..2bfcd57578 100644 --- a/include/block/snapshot.h +++ b/include/block/snapshot.h @@ -78,7 +78,7 @@ int bdrv_snapshot_load_tmp_by_id_or_name(BlockDriverState *bs, bool bdrv_all_can_snapshot(BlockDriverState **first_bad_bs); int bdrv_all_delete_snapshot(const char *name, BlockDriverState **first_bsd_bs, - Error **err); + Error **errp); int bdrv_all_goto_snapshot(const char *name, BlockDriverState **first_bad_bs, Error **errp); int bdrv_all_find_snapshot(const char *name, BlockDriverState **first_bad_bs); diff --git a/include/chardev/char-fe.h b/include/chardev/char-fe.h index 67601dc9a4..a553843364 100644 --- a/include/chardev/char-fe.h +++ b/include/chardev/char-fe.h @@ -4,7 +4,7 @@ #include "chardev/char.h" #include "qemu/main-loop.h" -typedef void IOEventHandler(void *opaque, int event); +typedef void IOEventHandler(void *opaque, QEMUChrEvent event); typedef int BackendChangeHandler(void *opaque); /* This is the backend as seen by frontend, the actual backend is diff --git a/include/chardev/char-mux.h b/include/chardev/char-mux.h index 572cefd517..417fe32eed 100644 --- a/include/chardev/char-mux.h +++ b/include/chardev/char-mux.h @@ -56,6 +56,6 @@ typedef struct MuxChardev { object_dynamic_cast(OBJECT(chr), TYPE_CHARDEV_MUX) void mux_set_focus(Chardev *chr, int focus); -void mux_chr_send_all_event(Chardev *chr, int event); +void mux_chr_send_all_event(Chardev *chr, QEMUChrEvent event); #endif /* CHAR_MUX_H */ diff --git a/include/chardev/char.h b/include/chardev/char.h index 087b202b62..00589a6025 100644 --- a/include/chardev/char.h +++ b/include/chardev/char.h @@ -210,7 +210,7 @@ void qemu_chr_be_update_read_handlers(Chardev *s, * * Send an event from the back end to the front end. */ -void qemu_chr_be_event(Chardev *s, int event); +void qemu_chr_be_event(Chardev *s, QEMUChrEvent event); int qemu_chr_add_client(Chardev *s, int fd); Chardev *qemu_chr_find(const char *name); @@ -273,7 +273,7 @@ typedef struct ChardevClass { void (*chr_accept_input)(Chardev *chr); void (*chr_set_echo)(Chardev *chr, bool echo); void (*chr_set_fe_open)(Chardev *chr, int fe_open); - void (*chr_be_event)(Chardev *s, int event); + void (*chr_be_event)(Chardev *s, QEMUChrEvent event); /* Return 0 if succeeded, 1 if failed */ int (*chr_machine_done)(Chardev *chr); } ChardevClass; diff --git a/include/crypto/block.h b/include/crypto/block.h index d49d2c2da9..c77ccaf9c0 100644 --- a/include/crypto/block.h +++ b/include/crypto/block.h @@ -145,6 +145,26 @@ QCryptoBlock *qcrypto_block_create(QCryptoBlockCreateOptions *options, Error **errp); +/** + * qcrypto_block_calculate_payload_offset: + * @create_opts: the encryption options + * @optprefix: name prefix for options + * @len: output for number of header bytes before payload + * @errp: pointer to a NULL-initialized error object + * + * Calculate the number of header bytes before the payload in an encrypted + * storage volume. The header is an area before the payload that is reserved + * for encryption metadata. + * + * Returns: true on success, false on error + */ +bool +qcrypto_block_calculate_payload_offset(QCryptoBlockCreateOptions *create_opts, + const char *optprefix, + size_t *len, + Error **errp); + + /** * qcrypto_block_get_info: * @block: the block encryption object @@ -269,5 +289,7 @@ uint64_t qcrypto_block_get_sector_size(QCryptoBlock *block); void qcrypto_block_free(QCryptoBlock *block); G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoBlock, qcrypto_block_free) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoBlockCreateOptions, + qapi_free_QCryptoBlockCreateOptions) #endif /* QCRYPTO_BLOCK_H */ diff --git a/include/crypto/tlssession.h b/include/crypto/tlssession.h index e01e1a9dc2..15b9cef086 100644 --- a/include/crypto/tlssession.h +++ b/include/crypto/tlssession.h @@ -56,7 +56,7 @@ * * static int mysock_run_tls(int sockfd, * QCryptoTLSCreds *creds, - * Error *errp) + * Error **errp) * { * QCryptoTLSSession *sess; * diff --git a/include/disas/dis-asm.h b/include/disas/dis-asm.h index e9c7dd8eb4..c5f9fa08ab 100644 --- a/include/disas/dis-asm.h +++ b/include/disas/dis-asm.h @@ -226,6 +226,10 @@ enum bfd_architecture #define bfd_mach_nios2r2 2 bfd_arch_lm32, /* Lattice Mico32 */ #define bfd_mach_lm32 1 + bfd_arch_rx, /* Renesas RX */ +#define bfd_mach_rx 0x75 +#define bfd_mach_rx_v2 0x76 +#define bfd_mach_rx_v3 0x77 bfd_arch_last }; #define bfd_mach_s390_31 31 @@ -372,6 +376,9 @@ typedef struct disassemble_info { /* Command line options specific to the target disassembler. */ char * disassembler_options; + /* Field intended to be used by targets in any way they deem suitable. */ + int64_t target_info; + /* Options for Capstone disassembly. */ int cap_arch; int cap_mode; @@ -433,6 +440,7 @@ int print_insn_little_nios2 (bfd_vma, disassemble_info*); int print_insn_xtensa (bfd_vma, disassemble_info*); int print_insn_riscv32 (bfd_vma, disassemble_info*); int print_insn_riscv64 (bfd_vma, disassemble_info*); +int print_insn_rx(bfd_vma, disassemble_info *); #if 0 /* Fetch the disassembler for a given BFD, if that support is available. */ diff --git a/include/elf.h b/include/elf.h index 3501e0c8d0..8fbfe60e09 100644 --- a/include/elf.h +++ b/include/elf.h @@ -1650,6 +1650,7 @@ typedef struct elf64_shdr { #define NT_ARM_HW_BREAK 0x402 /* ARM hardware breakpoint registers */ #define NT_ARM_HW_WATCH 0x403 /* ARM hardware watchpoint registers */ #define NT_ARM_SYSTEM_CALL 0x404 /* ARM system call number */ +#define NT_ARM_SVE 0x405 /* ARM Scalable Vector Extension regs */ /* * Physical entry point into the kernel. diff --git a/include/exec/cpu-all.h b/include/exec/cpu-all.h index e96781a455..49384bb66a 100644 --- a/include/exec/cpu-all.h +++ b/include/exec/cpu-all.h @@ -388,7 +388,7 @@ void dump_opcount_info(void); #endif /* !CONFIG_USER_ONLY */ int cpu_memory_rw_debug(CPUState *cpu, target_ulong addr, - uint8_t *buf, target_ulong len, int is_write); + void *ptr, target_ulong len, bool is_write); int cpu_exec(CPUState *cpu); diff --git a/include/exec/cpu-common.h b/include/exec/cpu-common.h index 81753bbb34..b47e5630e7 100644 --- a/include/exec/cpu-common.h +++ b/include/exec/cpu-common.h @@ -69,23 +69,23 @@ void qemu_ram_unset_migratable(RAMBlock *rb); size_t qemu_ram_pagesize(RAMBlock *block); size_t qemu_ram_pagesize_largest(void); -void cpu_physical_memory_rw(hwaddr addr, uint8_t *buf, - hwaddr len, int is_write); +void cpu_physical_memory_rw(hwaddr addr, void *buf, + hwaddr len, bool is_write); static inline void cpu_physical_memory_read(hwaddr addr, void *buf, hwaddr len) { - cpu_physical_memory_rw(addr, buf, len, 0); + cpu_physical_memory_rw(addr, buf, len, false); } static inline void cpu_physical_memory_write(hwaddr addr, const void *buf, hwaddr len) { - cpu_physical_memory_rw(addr, (void *)buf, len, 1); + cpu_physical_memory_rw(addr, (void *)buf, len, true); } void *cpu_physical_memory_map(hwaddr addr, hwaddr *plen, - int is_write); + bool is_write); void cpu_physical_memory_unmap(void *buffer, hwaddr len, - int is_write, hwaddr access_len); + bool is_write, hwaddr access_len); void cpu_register_map_client(QEMUBH *bh); void cpu_unregister_map_client(QEMUBH *bh); diff --git a/include/exec/cpu_ldst.h b/include/exec/cpu_ldst.h index fd499f7e2f..53de19753a 100644 --- a/include/exec/cpu_ldst.h +++ b/include/exec/cpu_ldst.h @@ -25,9 +25,13 @@ * * The syntax for the accessors is: * - * load: cpu_ld{sign}{size}_{mmusuffix}(env, ptr) + * load: cpu_ld{sign}{size}_{mmusuffix}(env, ptr) + * cpu_ld{sign}{size}_{mmusuffix}_ra(env, ptr, retaddr) + * cpu_ld{sign}{size}_mmuidx_ra(env, ptr, mmu_idx, retaddr) * - * store: cpu_st{sign}{size}_{mmusuffix}(env, ptr, val) + * store: cpu_st{size}_{mmusuffix}(env, ptr, val) + * cpu_st{size}_{mmusuffix}_ra(env, ptr, val, retaddr) + * cpu_st{size}_mmuidx_ra(env, ptr, val, mmu_idx, retaddr) * * sign is: * (empty): for 32 and 64 bit sizes @@ -40,9 +44,10 @@ * l: 32 bits * q: 64 bits * - * mmusuffix is one of the generic suffixes "data" or "code", or - * (for softmmu configs) a target-specific MMU mode suffix as defined - * in target cpu.h. + * mmusuffix is one of the generic suffixes "data" or "code", or "mmuidx". + * The "mmuidx" suffix carries an extra mmu_idx argument that specifies + * the index to use; the "data" and "code" suffixes take the index from + * cpu_mmu_index(). */ #ifndef CPU_LDST_H #define CPU_LDST_H @@ -89,6 +94,34 @@ typedef target_ulong abi_ptr; #define TARGET_ABI_FMT_ptr TARGET_ABI_FMT_lx #endif +uint32_t cpu_ldub_data(CPUArchState *env, abi_ptr ptr); +uint32_t cpu_lduw_data(CPUArchState *env, abi_ptr ptr); +uint32_t cpu_ldl_data(CPUArchState *env, abi_ptr ptr); +uint64_t cpu_ldq_data(CPUArchState *env, abi_ptr ptr); +int cpu_ldsb_data(CPUArchState *env, abi_ptr ptr); +int cpu_ldsw_data(CPUArchState *env, abi_ptr ptr); + +uint32_t cpu_ldub_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t retaddr); +uint32_t cpu_lduw_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t retaddr); +uint32_t cpu_ldl_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t retaddr); +uint64_t cpu_ldq_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t retaddr); +int cpu_ldsb_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t retaddr); +int cpu_ldsw_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t retaddr); + +void cpu_stb_data(CPUArchState *env, abi_ptr ptr, uint32_t val); +void cpu_stw_data(CPUArchState *env, abi_ptr ptr, uint32_t val); +void cpu_stl_data(CPUArchState *env, abi_ptr ptr, uint32_t val); +void cpu_stq_data(CPUArchState *env, abi_ptr ptr, uint64_t val); + +void cpu_stb_data_ra(CPUArchState *env, abi_ptr ptr, + uint32_t val, uintptr_t retaddr); +void cpu_stw_data_ra(CPUArchState *env, abi_ptr ptr, + uint32_t val, uintptr_t retaddr); +void cpu_stl_data_ra(CPUArchState *env, abi_ptr ptr, + uint32_t val, uintptr_t retaddr); +void cpu_stq_data_ra(CPUArchState *env, abi_ptr ptr, + uint64_t val, uintptr_t retaddr); + #if defined(CONFIG_USER_ONLY) extern __thread uintptr_t helper_retaddr; @@ -113,47 +146,75 @@ static inline void clear_helper_retaddr(void) helper_retaddr = 0; } -/* In user-only mode we provide only the _code and _data accessors. */ - -#define MEMSUFFIX _data -#define DATA_SIZE 1 -#include "exec/cpu_ldst_useronly_template.h" - -#define DATA_SIZE 2 -#include "exec/cpu_ldst_useronly_template.h" - -#define DATA_SIZE 4 -#include "exec/cpu_ldst_useronly_template.h" - -#define DATA_SIZE 8 -#include "exec/cpu_ldst_useronly_template.h" -#undef MEMSUFFIX - /* - * Code access is deprecated in favour of translator_ld* functions - * (see translator.h). However there are still users that need to - * converted so for now these stay. + * Provide the same *_mmuidx_ra interface as for softmmu. + * The mmu_idx argument is ignored. */ -#define MEMSUFFIX _code -#define CODE_ACCESS -#define DATA_SIZE 1 -#include "exec/cpu_ldst_useronly_template.h" -#define DATA_SIZE 2 -#include "exec/cpu_ldst_useronly_template.h" +static inline uint32_t cpu_ldub_mmuidx_ra(CPUArchState *env, abi_ptr addr, + int mmu_idx, uintptr_t ra) +{ + return cpu_ldub_data_ra(env, addr, ra); +} -#define DATA_SIZE 4 -#include "exec/cpu_ldst_useronly_template.h" +static inline uint32_t cpu_lduw_mmuidx_ra(CPUArchState *env, abi_ptr addr, + int mmu_idx, uintptr_t ra) +{ + return cpu_lduw_data_ra(env, addr, ra); +} -#define DATA_SIZE 8 -#include "exec/cpu_ldst_useronly_template.h" -#undef MEMSUFFIX -#undef CODE_ACCESS +static inline uint32_t cpu_ldl_mmuidx_ra(CPUArchState *env, abi_ptr addr, + int mmu_idx, uintptr_t ra) +{ + return cpu_ldl_data_ra(env, addr, ra); +} + +static inline uint64_t cpu_ldq_mmuidx_ra(CPUArchState *env, abi_ptr addr, + int mmu_idx, uintptr_t ra) +{ + return cpu_ldq_data_ra(env, addr, ra); +} + +static inline int cpu_ldsb_mmuidx_ra(CPUArchState *env, abi_ptr addr, + int mmu_idx, uintptr_t ra) +{ + return cpu_ldsb_data_ra(env, addr, ra); +} + +static inline int cpu_ldsw_mmuidx_ra(CPUArchState *env, abi_ptr addr, + int mmu_idx, uintptr_t ra) +{ + return cpu_ldsw_data_ra(env, addr, ra); +} + +static inline void cpu_stb_mmuidx_ra(CPUArchState *env, abi_ptr addr, + uint32_t val, int mmu_idx, uintptr_t ra) +{ + cpu_stb_data_ra(env, addr, val, ra); +} + +static inline void cpu_stw_mmuidx_ra(CPUArchState *env, abi_ptr addr, + uint32_t val, int mmu_idx, uintptr_t ra) +{ + cpu_stw_data_ra(env, addr, val, ra); +} + +static inline void cpu_stl_mmuidx_ra(CPUArchState *env, abi_ptr addr, + uint32_t val, int mmu_idx, uintptr_t ra) +{ + cpu_stl_data_ra(env, addr, val, ra); +} + +static inline void cpu_stq_mmuidx_ra(CPUArchState *env, abi_ptr addr, + uint64_t val, int mmu_idx, uintptr_t ra) +{ + cpu_stq_data_ra(env, addr, val, ra); +} #else -/* The memory helpers for tcg-generated code need tcg_target_long etc. */ -#include "tcg.h" +/* Needed for TCG_OVERSIZED_GUEST */ +#include "tcg/tcg.h" static inline target_ulong tlb_addr_write(const CPUTLBEntry *entry) { @@ -173,11 +234,6 @@ static inline uintptr_t tlb_index(CPUArchState *env, uintptr_t mmu_idx, return (addr >> TARGET_PAGE_BITS) & size_mask; } -static inline size_t tlb_n_entries(CPUArchState *env, uintptr_t mmu_idx) -{ - return (env_tlb(env)->f[mmu_idx].mask >> CPU_TLB_ENTRY_BITS) + 1; -} - /* Find the TLB entry corresponding to the mmu_idx + address pair. */ static inline CPUTLBEntry *tlb_entry(CPUArchState *env, uintptr_t mmu_idx, target_ulong addr) @@ -185,281 +241,46 @@ static inline CPUTLBEntry *tlb_entry(CPUArchState *env, uintptr_t mmu_idx, return &env_tlb(env)->f[mmu_idx].table[tlb_index(env, mmu_idx, addr)]; } -#ifdef MMU_MODE0_SUFFIX -#define CPU_MMU_INDEX 0 -#define MEMSUFFIX MMU_MODE0_SUFFIX -#define DATA_SIZE 1 -#include "exec/cpu_ldst_template.h" - -#define DATA_SIZE 2 -#include "exec/cpu_ldst_template.h" - -#define DATA_SIZE 4 -#include "exec/cpu_ldst_template.h" - -#define DATA_SIZE 8 -#include "exec/cpu_ldst_template.h" -#undef CPU_MMU_INDEX -#undef MEMSUFFIX -#endif - -#if (NB_MMU_MODES >= 2) && defined(MMU_MODE1_SUFFIX) -#define CPU_MMU_INDEX 1 -#define MEMSUFFIX MMU_MODE1_SUFFIX -#define DATA_SIZE 1 -#include "exec/cpu_ldst_template.h" - -#define DATA_SIZE 2 -#include "exec/cpu_ldst_template.h" - -#define DATA_SIZE 4 -#include "exec/cpu_ldst_template.h" - -#define DATA_SIZE 8 -#include "exec/cpu_ldst_template.h" -#undef CPU_MMU_INDEX -#undef MEMSUFFIX -#endif - -#if (NB_MMU_MODES >= 3) && defined(MMU_MODE2_SUFFIX) - -#define CPU_MMU_INDEX 2 -#define MEMSUFFIX MMU_MODE2_SUFFIX -#define DATA_SIZE 1 -#include "exec/cpu_ldst_template.h" - -#define DATA_SIZE 2 -#include "exec/cpu_ldst_template.h" - -#define DATA_SIZE 4 -#include "exec/cpu_ldst_template.h" - -#define DATA_SIZE 8 -#include "exec/cpu_ldst_template.h" -#undef CPU_MMU_INDEX -#undef MEMSUFFIX -#endif /* (NB_MMU_MODES >= 3) */ - -#if (NB_MMU_MODES >= 4) && defined(MMU_MODE3_SUFFIX) - -#define CPU_MMU_INDEX 3 -#define MEMSUFFIX MMU_MODE3_SUFFIX -#define DATA_SIZE 1 -#include "exec/cpu_ldst_template.h" - -#define DATA_SIZE 2 -#include "exec/cpu_ldst_template.h" - -#define DATA_SIZE 4 -#include "exec/cpu_ldst_template.h" - -#define DATA_SIZE 8 -#include "exec/cpu_ldst_template.h" -#undef CPU_MMU_INDEX -#undef MEMSUFFIX -#endif /* (NB_MMU_MODES >= 4) */ - -#if (NB_MMU_MODES >= 5) && defined(MMU_MODE4_SUFFIX) - -#define CPU_MMU_INDEX 4 -#define MEMSUFFIX MMU_MODE4_SUFFIX -#define DATA_SIZE 1 -#include "exec/cpu_ldst_template.h" - -#define DATA_SIZE 2 -#include "exec/cpu_ldst_template.h" - -#define DATA_SIZE 4 -#include "exec/cpu_ldst_template.h" - -#define DATA_SIZE 8 -#include "exec/cpu_ldst_template.h" -#undef CPU_MMU_INDEX -#undef MEMSUFFIX -#endif /* (NB_MMU_MODES >= 5) */ - -#if (NB_MMU_MODES >= 6) && defined(MMU_MODE5_SUFFIX) - -#define CPU_MMU_INDEX 5 -#define MEMSUFFIX MMU_MODE5_SUFFIX -#define DATA_SIZE 1 -#include "exec/cpu_ldst_template.h" - -#define DATA_SIZE 2 -#include "exec/cpu_ldst_template.h" - -#define DATA_SIZE 4 -#include "exec/cpu_ldst_template.h" - -#define DATA_SIZE 8 -#include "exec/cpu_ldst_template.h" -#undef CPU_MMU_INDEX -#undef MEMSUFFIX -#endif /* (NB_MMU_MODES >= 6) */ - -#if (NB_MMU_MODES >= 7) && defined(MMU_MODE6_SUFFIX) - -#define CPU_MMU_INDEX 6 -#define MEMSUFFIX MMU_MODE6_SUFFIX -#define DATA_SIZE 1 -#include "exec/cpu_ldst_template.h" - -#define DATA_SIZE 2 -#include "exec/cpu_ldst_template.h" - -#define DATA_SIZE 4 -#include "exec/cpu_ldst_template.h" - -#define DATA_SIZE 8 -#include "exec/cpu_ldst_template.h" -#undef CPU_MMU_INDEX -#undef MEMSUFFIX -#endif /* (NB_MMU_MODES >= 7) */ - -#if (NB_MMU_MODES >= 8) && defined(MMU_MODE7_SUFFIX) - -#define CPU_MMU_INDEX 7 -#define MEMSUFFIX MMU_MODE7_SUFFIX -#define DATA_SIZE 1 -#include "exec/cpu_ldst_template.h" - -#define DATA_SIZE 2 -#include "exec/cpu_ldst_template.h" - -#define DATA_SIZE 4 -#include "exec/cpu_ldst_template.h" - -#define DATA_SIZE 8 -#include "exec/cpu_ldst_template.h" -#undef CPU_MMU_INDEX -#undef MEMSUFFIX -#endif /* (NB_MMU_MODES >= 8) */ - -#if (NB_MMU_MODES >= 9) && defined(MMU_MODE8_SUFFIX) - -#define CPU_MMU_INDEX 8 -#define MEMSUFFIX MMU_MODE8_SUFFIX -#define DATA_SIZE 1 -#include "exec/cpu_ldst_template.h" - -#define DATA_SIZE 2 -#include "exec/cpu_ldst_template.h" - -#define DATA_SIZE 4 -#include "exec/cpu_ldst_template.h" - -#define DATA_SIZE 8 -#include "exec/cpu_ldst_template.h" -#undef CPU_MMU_INDEX -#undef MEMSUFFIX -#endif /* (NB_MMU_MODES >= 9) */ - -#if (NB_MMU_MODES >= 10) && defined(MMU_MODE9_SUFFIX) - -#define CPU_MMU_INDEX 9 -#define MEMSUFFIX MMU_MODE9_SUFFIX -#define DATA_SIZE 1 -#include "exec/cpu_ldst_template.h" - -#define DATA_SIZE 2 -#include "exec/cpu_ldst_template.h" - -#define DATA_SIZE 4 -#include "exec/cpu_ldst_template.h" - -#define DATA_SIZE 8 -#include "exec/cpu_ldst_template.h" -#undef CPU_MMU_INDEX -#undef MEMSUFFIX -#endif /* (NB_MMU_MODES >= 10) */ - -#if (NB_MMU_MODES >= 11) && defined(MMU_MODE10_SUFFIX) - -#define CPU_MMU_INDEX 10 -#define MEMSUFFIX MMU_MODE10_SUFFIX -#define DATA_SIZE 1 -#include "exec/cpu_ldst_template.h" - -#define DATA_SIZE 2 -#include "exec/cpu_ldst_template.h" - -#define DATA_SIZE 4 -#include "exec/cpu_ldst_template.h" - -#define DATA_SIZE 8 -#include "exec/cpu_ldst_template.h" -#undef CPU_MMU_INDEX -#undef MEMSUFFIX -#endif /* (NB_MMU_MODES >= 11) */ - -#if (NB_MMU_MODES >= 12) && defined(MMU_MODE11_SUFFIX) - -#define CPU_MMU_INDEX 11 -#define MEMSUFFIX MMU_MODE11_SUFFIX -#define DATA_SIZE 1 -#include "exec/cpu_ldst_template.h" - -#define DATA_SIZE 2 -#include "exec/cpu_ldst_template.h" - -#define DATA_SIZE 4 -#include "exec/cpu_ldst_template.h" - -#define DATA_SIZE 8 -#include "exec/cpu_ldst_template.h" -#undef CPU_MMU_INDEX -#undef MEMSUFFIX -#endif /* (NB_MMU_MODES >= 12) */ - -#if (NB_MMU_MODES > 12) -#error "NB_MMU_MODES > 12 is not supported for now" -#endif /* (NB_MMU_MODES > 12) */ - -/* these access are slower, they must be as rare as possible */ -#define CPU_MMU_INDEX (cpu_mmu_index(env, false)) -#define MEMSUFFIX _data -#define DATA_SIZE 1 -#include "exec/cpu_ldst_template.h" - -#define DATA_SIZE 2 -#include "exec/cpu_ldst_template.h" - -#define DATA_SIZE 4 -#include "exec/cpu_ldst_template.h" - -#define DATA_SIZE 8 -#include "exec/cpu_ldst_template.h" -#undef CPU_MMU_INDEX -#undef MEMSUFFIX - -/* - * Code access is deprecated in favour of translator_ld* functions - * (see translator.h). However there are still users that need to - * converted so for now these stay. - */ - -#define CPU_MMU_INDEX (cpu_mmu_index(env, true)) -#define MEMSUFFIX _code -#define SOFTMMU_CODE_ACCESS - -#define DATA_SIZE 1 -#include "exec/cpu_ldst_template.h" - -#define DATA_SIZE 2 -#include "exec/cpu_ldst_template.h" - -#define DATA_SIZE 4 -#include "exec/cpu_ldst_template.h" - -#define DATA_SIZE 8 -#include "exec/cpu_ldst_template.h" - -#undef CPU_MMU_INDEX -#undef MEMSUFFIX -#undef SOFTMMU_CODE_ACCESS +uint32_t cpu_ldub_mmuidx_ra(CPUArchState *env, abi_ptr addr, + int mmu_idx, uintptr_t ra); +uint32_t cpu_lduw_mmuidx_ra(CPUArchState *env, abi_ptr addr, + int mmu_idx, uintptr_t ra); +uint32_t cpu_ldl_mmuidx_ra(CPUArchState *env, abi_ptr addr, + int mmu_idx, uintptr_t ra); +uint64_t cpu_ldq_mmuidx_ra(CPUArchState *env, abi_ptr addr, + int mmu_idx, uintptr_t ra); + +int cpu_ldsb_mmuidx_ra(CPUArchState *env, abi_ptr addr, + int mmu_idx, uintptr_t ra); +int cpu_ldsw_mmuidx_ra(CPUArchState *env, abi_ptr addr, + int mmu_idx, uintptr_t ra); + +void cpu_stb_mmuidx_ra(CPUArchState *env, abi_ptr addr, uint32_t val, + int mmu_idx, uintptr_t retaddr); +void cpu_stw_mmuidx_ra(CPUArchState *env, abi_ptr addr, uint32_t val, + int mmu_idx, uintptr_t retaddr); +void cpu_stl_mmuidx_ra(CPUArchState *env, abi_ptr addr, uint32_t val, + int mmu_idx, uintptr_t retaddr); +void cpu_stq_mmuidx_ra(CPUArchState *env, abi_ptr addr, uint64_t val, + int mmu_idx, uintptr_t retaddr); #endif /* defined(CONFIG_USER_ONLY) */ +uint32_t cpu_ldub_code(CPUArchState *env, abi_ptr addr); +uint32_t cpu_lduw_code(CPUArchState *env, abi_ptr addr); +uint32_t cpu_ldl_code(CPUArchState *env, abi_ptr addr); +uint64_t cpu_ldq_code(CPUArchState *env, abi_ptr addr); + +static inline int cpu_ldsb_code(CPUArchState *env, abi_ptr addr) +{ + return (int8_t)cpu_ldub_code(env, addr); +} + +static inline int cpu_ldsw_code(CPUArchState *env, abi_ptr addr) +{ + return (int16_t)cpu_lduw_code(env, addr); +} + /** * tlb_vaddr_to_host: * @env: CPUArchState diff --git a/include/exec/cpu_ldst_template.h b/include/exec/cpu_ldst_template.h deleted file mode 100644 index 54b5e858ce..0000000000 --- a/include/exec/cpu_ldst_template.h +++ /dev/null @@ -1,211 +0,0 @@ -/* - * Software MMU support - * - * Generate inline load/store functions for one MMU mode and data - * size. - * - * Generate a store function as well as signed and unsigned loads. - * - * Not used directly but included from cpu_ldst.h. - * - * Copyright (c) 2003 Fabrice Bellard - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - */ - -#if !defined(SOFTMMU_CODE_ACCESS) -#include "trace-root.h" -#endif - -#include "qemu/plugin.h" -#include "trace/mem.h" - -#if DATA_SIZE == 8 -#define SUFFIX q -#define USUFFIX q -#define DATA_TYPE uint64_t -#define SHIFT 3 -#elif DATA_SIZE == 4 -#define SUFFIX l -#define USUFFIX l -#define DATA_TYPE uint32_t -#define SHIFT 2 -#elif DATA_SIZE == 2 -#define SUFFIX w -#define USUFFIX uw -#define DATA_TYPE uint16_t -#define DATA_STYPE int16_t -#define SHIFT 1 -#elif DATA_SIZE == 1 -#define SUFFIX b -#define USUFFIX ub -#define DATA_TYPE uint8_t -#define DATA_STYPE int8_t -#define SHIFT 0 -#else -#error unsupported data size -#endif - -#if DATA_SIZE == 8 -#define RES_TYPE uint64_t -#else -#define RES_TYPE uint32_t -#endif - -#ifdef SOFTMMU_CODE_ACCESS -#define ADDR_READ addr_code -#define MMUSUFFIX _cmmu -#define URETSUFFIX USUFFIX -#define SRETSUFFIX glue(s, SUFFIX) -#else -#define ADDR_READ addr_read -#define MMUSUFFIX _mmu -#define URETSUFFIX USUFFIX -#define SRETSUFFIX glue(s, SUFFIX) -#endif - -/* generic load/store macros */ - -static inline RES_TYPE -glue(glue(glue(cpu_ld, USUFFIX), MEMSUFFIX), _ra)(CPUArchState *env, - target_ulong ptr, - uintptr_t retaddr) -{ - CPUTLBEntry *entry; - RES_TYPE res; - target_ulong addr; - int mmu_idx = CPU_MMU_INDEX; - TCGMemOpIdx oi; -#if !defined(SOFTMMU_CODE_ACCESS) - uint16_t meminfo = trace_mem_build_info(SHIFT, false, MO_TE, false, mmu_idx); - trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo); -#endif - - addr = ptr; - entry = tlb_entry(env, mmu_idx, addr); - if (unlikely(entry->ADDR_READ != - (addr & (TARGET_PAGE_MASK | (DATA_SIZE - 1))))) { - oi = make_memop_idx(SHIFT, mmu_idx); - res = glue(glue(helper_ret_ld, URETSUFFIX), MMUSUFFIX)(env, addr, - oi, retaddr); - } else { - uintptr_t hostaddr = addr + entry->addend; - res = glue(glue(ld, USUFFIX), _p)((uint8_t *)hostaddr); - } -#ifndef SOFTMMU_CODE_ACCESS - qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo); -#endif - return res; -} - -static inline RES_TYPE -glue(glue(cpu_ld, USUFFIX), MEMSUFFIX)(CPUArchState *env, target_ulong ptr) -{ - return glue(glue(glue(cpu_ld, USUFFIX), MEMSUFFIX), _ra)(env, ptr, 0); -} - -#if DATA_SIZE <= 2 -static inline int -glue(glue(glue(cpu_lds, SUFFIX), MEMSUFFIX), _ra)(CPUArchState *env, - target_ulong ptr, - uintptr_t retaddr) -{ - CPUTLBEntry *entry; - int res; - target_ulong addr; - int mmu_idx = CPU_MMU_INDEX; - TCGMemOpIdx oi; -#if !defined(SOFTMMU_CODE_ACCESS) - uint16_t meminfo = trace_mem_build_info(SHIFT, true, MO_TE, false, mmu_idx); - trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo); -#endif - - addr = ptr; - entry = tlb_entry(env, mmu_idx, addr); - if (unlikely(entry->ADDR_READ != - (addr & (TARGET_PAGE_MASK | (DATA_SIZE - 1))))) { - oi = make_memop_idx(SHIFT, mmu_idx); - res = (DATA_STYPE)glue(glue(helper_ret_ld, SRETSUFFIX), - MMUSUFFIX)(env, addr, oi, retaddr); - } else { - uintptr_t hostaddr = addr + entry->addend; - res = glue(glue(lds, SUFFIX), _p)((uint8_t *)hostaddr); - } -#ifndef SOFTMMU_CODE_ACCESS - qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo); -#endif - return res; -} - -static inline int -glue(glue(cpu_lds, SUFFIX), MEMSUFFIX)(CPUArchState *env, target_ulong ptr) -{ - return glue(glue(glue(cpu_lds, SUFFIX), MEMSUFFIX), _ra)(env, ptr, 0); -} -#endif - -#ifndef SOFTMMU_CODE_ACCESS - -/* generic store macro */ - -static inline void -glue(glue(glue(cpu_st, SUFFIX), MEMSUFFIX), _ra)(CPUArchState *env, - target_ulong ptr, - RES_TYPE v, uintptr_t retaddr) -{ - CPUTLBEntry *entry; - target_ulong addr; - int mmu_idx = CPU_MMU_INDEX; - TCGMemOpIdx oi; -#if !defined(SOFTMMU_CODE_ACCESS) - uint16_t meminfo = trace_mem_build_info(SHIFT, false, MO_TE, true, mmu_idx); - trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo); -#endif - - addr = ptr; - entry = tlb_entry(env, mmu_idx, addr); - if (unlikely(tlb_addr_write(entry) != - (addr & (TARGET_PAGE_MASK | (DATA_SIZE - 1))))) { - oi = make_memop_idx(SHIFT, mmu_idx); - glue(glue(helper_ret_st, SUFFIX), MMUSUFFIX)(env, addr, v, oi, - retaddr); - } else { - uintptr_t hostaddr = addr + entry->addend; - glue(glue(st, SUFFIX), _p)((uint8_t *)hostaddr, v); - } -#ifndef SOFTMMU_CODE_ACCESS - qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo); -#endif -} - -static inline void -glue(glue(cpu_st, SUFFIX), MEMSUFFIX)(CPUArchState *env, target_ulong ptr, - RES_TYPE v) -{ - glue(glue(glue(cpu_st, SUFFIX), MEMSUFFIX), _ra)(env, ptr, v, 0); -} - -#endif /* !SOFTMMU_CODE_ACCESS */ - -#undef RES_TYPE -#undef DATA_TYPE -#undef DATA_STYPE -#undef SUFFIX -#undef USUFFIX -#undef DATA_SIZE -#undef MMUSUFFIX -#undef ADDR_READ -#undef URETSUFFIX -#undef SRETSUFFIX -#undef SHIFT diff --git a/include/exec/cpu_ldst_useronly_template.h b/include/exec/cpu_ldst_useronly_template.h deleted file mode 100644 index dbdc7a845d..0000000000 --- a/include/exec/cpu_ldst_useronly_template.h +++ /dev/null @@ -1,159 +0,0 @@ -/* - * User-only accessor function support - * - * Generate inline load/store functions for one data size. - * - * Generate a store function as well as signed and unsigned loads. - * - * Not used directly but included from cpu_ldst.h. - * - * Copyright (c) 2015 Linaro Limited - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - */ - -#if !defined(CODE_ACCESS) -#include "trace-root.h" -#endif - -#include "trace/mem.h" - -#if DATA_SIZE == 8 -#define SUFFIX q -#define USUFFIX q -#define DATA_TYPE uint64_t -#define SHIFT 3 -#elif DATA_SIZE == 4 -#define SUFFIX l -#define USUFFIX l -#define DATA_TYPE uint32_t -#define SHIFT 2 -#elif DATA_SIZE == 2 -#define SUFFIX w -#define USUFFIX uw -#define DATA_TYPE uint16_t -#define DATA_STYPE int16_t -#define SHIFT 1 -#elif DATA_SIZE == 1 -#define SUFFIX b -#define USUFFIX ub -#define DATA_TYPE uint8_t -#define DATA_STYPE int8_t -#define SHIFT 0 -#else -#error unsupported data size -#endif - -#if DATA_SIZE == 8 -#define RES_TYPE uint64_t -#else -#define RES_TYPE uint32_t -#endif - -static inline RES_TYPE -glue(glue(cpu_ld, USUFFIX), MEMSUFFIX)(CPUArchState *env, abi_ptr ptr) -{ - RES_TYPE ret; -#ifdef CODE_ACCESS - set_helper_retaddr(1); - ret = glue(glue(ld, USUFFIX), _p)(g2h(ptr)); - clear_helper_retaddr(); -#else - uint16_t meminfo = trace_mem_build_info(SHIFT, false, MO_TE, false, - MMU_USER_IDX); - trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo); - ret = glue(glue(ld, USUFFIX), _p)(g2h(ptr)); -#endif - return ret; -} - -#ifndef CODE_ACCESS -static inline RES_TYPE -glue(glue(glue(cpu_ld, USUFFIX), MEMSUFFIX), _ra)(CPUArchState *env, - abi_ptr ptr, - uintptr_t retaddr) -{ - RES_TYPE ret; - set_helper_retaddr(retaddr); - ret = glue(glue(cpu_ld, USUFFIX), MEMSUFFIX)(env, ptr); - clear_helper_retaddr(); - return ret; -} -#endif - -#if DATA_SIZE <= 2 -static inline int -glue(glue(cpu_lds, SUFFIX), MEMSUFFIX)(CPUArchState *env, abi_ptr ptr) -{ - int ret; -#ifdef CODE_ACCESS - set_helper_retaddr(1); - ret = glue(glue(lds, SUFFIX), _p)(g2h(ptr)); - clear_helper_retaddr(); -#else - uint16_t meminfo = trace_mem_build_info(SHIFT, true, MO_TE, false, - MMU_USER_IDX); - trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo); - ret = glue(glue(lds, SUFFIX), _p)(g2h(ptr)); - qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo); -#endif - return ret; -} - -#ifndef CODE_ACCESS -static inline int -glue(glue(glue(cpu_lds, SUFFIX), MEMSUFFIX), _ra)(CPUArchState *env, - abi_ptr ptr, - uintptr_t retaddr) -{ - int ret; - set_helper_retaddr(retaddr); - ret = glue(glue(cpu_lds, SUFFIX), MEMSUFFIX)(env, ptr); - clear_helper_retaddr(); - return ret; -} -#endif /* CODE_ACCESS */ -#endif /* DATA_SIZE <= 2 */ - -#ifndef CODE_ACCESS -static inline void -glue(glue(cpu_st, SUFFIX), MEMSUFFIX)(CPUArchState *env, abi_ptr ptr, - RES_TYPE v) -{ - uint16_t meminfo = trace_mem_build_info(SHIFT, false, MO_TE, true, - MMU_USER_IDX); - trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo); - glue(glue(st, SUFFIX), _p)(g2h(ptr), v); - qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo); -} - -static inline void -glue(glue(glue(cpu_st, SUFFIX), MEMSUFFIX), _ra)(CPUArchState *env, - abi_ptr ptr, - RES_TYPE v, - uintptr_t retaddr) -{ - set_helper_retaddr(retaddr); - glue(glue(cpu_st, SUFFIX), MEMSUFFIX)(env, ptr, v); - clear_helper_retaddr(); -} -#endif - -#undef RES_TYPE -#undef DATA_TYPE -#undef DATA_STYPE -#undef SUFFIX -#undef USUFFIX -#undef DATA_SIZE -#undef SHIFT diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h index d85e610e85..350c4b451b 100644 --- a/include/exec/exec-all.h +++ b/include/exec/exec-all.h @@ -339,6 +339,12 @@ static inline void *probe_write(CPUArchState *env, target_ulong addr, int size, return probe_access(env, addr, size, MMU_DATA_STORE, mmu_idx, retaddr); } +static inline void *probe_read(CPUArchState *env, target_ulong addr, int size, + int mmu_idx, uintptr_t retaddr) +{ + return probe_access(env, addr, size, MMU_DATA_LOAD, mmu_idx, retaddr); +} + #define CODE_GEN_ALIGN 16 /* must be >= of the size of a icache line */ /* Estimated block size for TB allocation. */ diff --git a/include/exec/gdbstub.h b/include/exec/gdbstub.h index 08363969c1..20e1072692 100644 --- a/include/exec/gdbstub.h +++ b/include/exec/gdbstub.h @@ -68,40 +68,94 @@ void gdb_signalled(CPUArchState *, int); void gdbserver_fork(CPUState *); #endif /* Get or set a register. Returns the size of the register. */ -typedef int (*gdb_reg_cb)(CPUArchState *env, uint8_t *buf, int reg); +typedef int (*gdb_get_reg_cb)(CPUArchState *env, GByteArray *buf, int reg); +typedef int (*gdb_set_reg_cb)(CPUArchState *env, uint8_t *buf, int reg); void gdb_register_coprocessor(CPUState *cpu, - gdb_reg_cb get_reg, gdb_reg_cb set_reg, + gdb_get_reg_cb get_reg, gdb_set_reg_cb set_reg, int num_regs, const char *xml, int g_pos); -/* The GDB remote protocol transfers values in target byte order. This means - * we can use the raw memory access routines to access the value buffer. - * Conveniently, these also handle the case where the buffer is mis-aligned. +/* + * The GDB remote protocol transfers values in target byte order. As + * the gdbstub may be batching up several register values we always + * append to the array. */ -static inline int gdb_get_reg8(uint8_t *mem_buf, uint8_t val) +static inline int gdb_get_reg8(GByteArray *buf, uint8_t val) { - stb_p(mem_buf, val); + g_byte_array_append(buf, &val, 1); return 1; } -static inline int gdb_get_reg16(uint8_t *mem_buf, uint16_t val) +static inline int gdb_get_reg16(GByteArray *buf, uint16_t val) { - stw_p(mem_buf, val); + uint16_t to_word = tswap16(val); + g_byte_array_append(buf, (uint8_t *) &to_word, 2); return 2; } -static inline int gdb_get_reg32(uint8_t *mem_buf, uint32_t val) +static inline int gdb_get_reg32(GByteArray *buf, uint32_t val) { - stl_p(mem_buf, val); + uint32_t to_long = tswap32(val); + g_byte_array_append(buf, (uint8_t *) &to_long, 4); return 4; } -static inline int gdb_get_reg64(uint8_t *mem_buf, uint64_t val) +static inline int gdb_get_reg64(GByteArray *buf, uint64_t val) { - stq_p(mem_buf, val); + uint64_t to_quad = tswap64(val); + g_byte_array_append(buf, (uint8_t *) &to_quad, 8); return 8; } +static inline int gdb_get_reg128(GByteArray *buf, uint64_t val_hi, + uint64_t val_lo) +{ + uint64_t to_quad; +#ifdef TARGET_WORDS_BIGENDIAN + to_quad = tswap64(val_hi); + g_byte_array_append(buf, (uint8_t *) &to_quad, 8); + to_quad = tswap64(val_lo); + g_byte_array_append(buf, (uint8_t *) &to_quad, 8); +#else + to_quad = tswap64(val_lo); + g_byte_array_append(buf, (uint8_t *) &to_quad, 8); + to_quad = tswap64(val_hi); + g_byte_array_append(buf, (uint8_t *) &to_quad, 8); +#endif + return 16; +} + +static inline int gdb_get_float32(GByteArray *array, float32 val) +{ + uint8_t buf[sizeof(CPU_FloatU)]; + + stfl_p(buf, val); + g_byte_array_append(array, buf, sizeof(buf)); + + return sizeof(buf); +} +static inline int gdb_get_zeroes(GByteArray *array, size_t len) +{ + guint oldlen = array->len; + g_byte_array_set_size(array, oldlen + len); + memset(array->data + oldlen, 0, len); + + return len; +} + +/** + * gdb_get_reg_ptr: get pointer to start of last element + * @len: length of element + * + * This is a helper function to extract the pointer to the last + * element for additional processing. Some front-ends do additional + * dynamic swapping of the elements based on CPU state. + */ +static inline uint8_t * gdb_get_reg_ptr(GByteArray *buf, int len) +{ + return buf->data + buf->len - len; +} + #if TARGET_LONG_BITS == 64 #define gdb_get_regl(buf, val) gdb_get_reg64(buf, val) #define ldtul_p(addr) ldq_p(addr) diff --git a/include/exec/helper-gen.h b/include/exec/helper-gen.h index 236ff40524..29c02f85dc 100644 --- a/include/exec/helper-gen.h +++ b/include/exec/helper-gen.h @@ -66,6 +66,18 @@ static inline void glue(gen_helper_, name)(dh_retvar_decl(ret) \ tcg_gen_callN(HELPER(name), dh_retvar(ret), 6, args); \ } +#define DEF_HELPER_FLAGS_7(name, flags, ret, t1, t2, t3, t4, t5, t6, t7)\ +static inline void glue(gen_helper_, name)(dh_retvar_decl(ret) \ + dh_arg_decl(t1, 1), dh_arg_decl(t2, 2), dh_arg_decl(t3, 3), \ + dh_arg_decl(t4, 4), dh_arg_decl(t5, 5), dh_arg_decl(t6, 6), \ + dh_arg_decl(t7, 7)) \ +{ \ + TCGTemp *args[7] = { dh_arg(t1, 1), dh_arg(t2, 2), dh_arg(t3, 3), \ + dh_arg(t4, 4), dh_arg(t5, 5), dh_arg(t6, 6), \ + dh_arg(t7, 7) }; \ + tcg_gen_callN(HELPER(name), dh_retvar(ret), 7, args); \ +} + #include "helper.h" #include "trace/generated-helpers.h" #include "trace/generated-helpers-wrappers.h" @@ -79,6 +91,7 @@ static inline void glue(gen_helper_, name)(dh_retvar_decl(ret) \ #undef DEF_HELPER_FLAGS_4 #undef DEF_HELPER_FLAGS_5 #undef DEF_HELPER_FLAGS_6 +#undef DEF_HELPER_FLAGS_7 #undef GEN_HELPER #endif /* HELPER_GEN_H */ diff --git a/include/exec/helper-head.h b/include/exec/helper-head.h index f2519c9741..3094c7946d 100644 --- a/include/exec/helper-head.h +++ b/include/exec/helper-head.h @@ -148,6 +148,8 @@ DEF_HELPER_FLAGS_5(name, 0, ret, t1, t2, t3, t4, t5) #define DEF_HELPER_6(name, ret, t1, t2, t3, t4, t5, t6) \ DEF_HELPER_FLAGS_6(name, 0, ret, t1, t2, t3, t4, t5, t6) +#define DEF_HELPER_7(name, ret, t1, t2, t3, t4, t5, t6, t7) \ + DEF_HELPER_FLAGS_7(name, 0, ret, t1, t2, t3, t4, t5, t6, t7) /* MAX_OPC_PARAM_IARGS must be set to n if last entry is DEF_HELPER_FLAGS_n. */ diff --git a/include/exec/helper-proto.h b/include/exec/helper-proto.h index 1c4ba9bc78..a0a8d9aa46 100644 --- a/include/exec/helper-proto.h +++ b/include/exec/helper-proto.h @@ -30,6 +30,11 @@ dh_ctype(ret) HELPER(name) (dh_ctype(t1), dh_ctype(t2), dh_ctype(t3), \ dh_ctype(ret) HELPER(name) (dh_ctype(t1), dh_ctype(t2), dh_ctype(t3), \ dh_ctype(t4), dh_ctype(t5), dh_ctype(t6)); +#define DEF_HELPER_FLAGS_7(name, flags, ret, t1, t2, t3, t4, t5, t6, t7) \ +dh_ctype(ret) HELPER(name) (dh_ctype(t1), dh_ctype(t2), dh_ctype(t3), \ + dh_ctype(t4), dh_ctype(t5), dh_ctype(t6), \ + dh_ctype(t7)); + #include "helper.h" #include "trace/generated-helpers.h" #include "tcg-runtime.h" @@ -42,5 +47,6 @@ dh_ctype(ret) HELPER(name) (dh_ctype(t1), dh_ctype(t2), dh_ctype(t3), \ #undef DEF_HELPER_FLAGS_4 #undef DEF_HELPER_FLAGS_5 #undef DEF_HELPER_FLAGS_6 +#undef DEF_HELPER_FLAGS_7 #endif /* HELPER_PROTO_H */ diff --git a/include/exec/helper-tcg.h b/include/exec/helper-tcg.h index 573c2ce2e9..27870509a2 100644 --- a/include/exec/helper-tcg.h +++ b/include/exec/helper-tcg.h @@ -52,6 +52,12 @@ | dh_sizemask(t2, 2) | dh_sizemask(t3, 3) | dh_sizemask(t4, 4) \ | dh_sizemask(t5, 5) | dh_sizemask(t6, 6) }, +#define DEF_HELPER_FLAGS_7(NAME, FLAGS, ret, t1, t2, t3, t4, t5, t6, t7) \ + { .func = HELPER(NAME), .name = str(NAME), .flags = FLAGS, \ + .sizemask = dh_sizemask(ret, 0) | dh_sizemask(t1, 1) \ + | dh_sizemask(t2, 2) | dh_sizemask(t3, 3) | dh_sizemask(t4, 4) \ + | dh_sizemask(t5, 5) | dh_sizemask(t6, 6) | dh_sizemask(t7, 7) }, + #include "helper.h" #include "trace/generated-helpers.h" #include "tcg-runtime.h" @@ -65,5 +71,6 @@ #undef DEF_HELPER_FLAGS_4 #undef DEF_HELPER_FLAGS_5 #undef DEF_HELPER_FLAGS_6 +#undef DEF_HELPER_FLAGS_7 #endif /* HELPER_TCG_H */ diff --git a/include/exec/log.h b/include/exec/log.h index e2cfd436e6..fcc7b9e00b 100644 --- a/include/exec/log.h +++ b/include/exec/log.h @@ -15,8 +15,15 @@ */ static inline void log_cpu_state(CPUState *cpu, int flags) { + QemuLogFile *logfile; + if (qemu_log_enabled()) { - cpu_dump_state(cpu, qemu_logfile, flags); + rcu_read_lock(); + logfile = atomic_rcu_read(&qemu_logfile); + if (logfile) { + cpu_dump_state(cpu, logfile->fd, flags); + } + rcu_read_unlock(); } } @@ -40,19 +47,36 @@ static inline void log_cpu_state_mask(int mask, CPUState *cpu, int flags) static inline void log_target_disas(CPUState *cpu, target_ulong start, target_ulong len) { - target_disas(qemu_logfile, cpu, start, len); + QemuLogFile *logfile; + rcu_read_lock(); + logfile = atomic_rcu_read(&qemu_logfile); + if (logfile) { + target_disas(logfile->fd, cpu, start, len); + } + rcu_read_unlock(); } static inline void log_disas(void *code, unsigned long size) { - disas(qemu_logfile, code, size); + QemuLogFile *logfile; + rcu_read_lock(); + logfile = atomic_rcu_read(&qemu_logfile); + if (logfile) { + disas(logfile->fd, code, size); + } + rcu_read_unlock(); } #if defined(CONFIG_USER_ONLY) /* page_dump() output to the log file: */ -static inline void log_page_dump(void) +static inline void log_page_dump(const char *operation) { - page_dump(qemu_logfile); + FILE *logfile = qemu_log_lock(); + if (logfile) { + qemu_log("page layout changed following %s\n", operation); + page_dump(logfile); + } + qemu_log_unlock(logfile); } #endif #endif diff --git a/include/exec/memory.h b/include/exec/memory.h index 704811445a..112df4107e 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -360,10 +360,14 @@ typedef struct IOMMUMemoryRegionClass { typedef struct CoalescedMemoryRange CoalescedMemoryRange; typedef struct MemoryRegionIoeventfd MemoryRegionIoeventfd; +/** MemoryRegion: + * + * A struct representing a memory region. + */ struct MemoryRegion { Object parent_obj; - /* All fields are private - violators will be prosecuted */ + /* private: */ /* The following fields should fit in a cache line */ bool romd_mode; @@ -419,30 +423,232 @@ struct IOMMUMemoryRegion { * Use with memory_listener_register() and memory_listener_unregister(). */ struct MemoryListener { + /** + * @begin: + * + * Called at the beginning of an address space update transaction. + * Followed by calls to #MemoryListener.region_add(), + * #MemoryListener.region_del(), #MemoryListener.region_nop(), + * #MemoryListener.log_start() and #MemoryListener.log_stop() in + * increasing address order. + * + * @listener: The #MemoryListener. + */ void (*begin)(MemoryListener *listener); + + /** + * @commit: + * + * Called at the end of an address space update transaction, + * after the last call to #MemoryListener.region_add(), + * #MemoryListener.region_del() or #MemoryListener.region_nop(), + * #MemoryListener.log_start() and #MemoryListener.log_stop(). + * + * @listener: The #MemoryListener. + */ void (*commit)(MemoryListener *listener); + + /** + * @region_add: + * + * Called during an address space update transaction, + * for a section of the address space that is new in this address space + * space since the last transaction. + * + * @listener: The #MemoryListener. + * @section: The new #MemoryRegionSection. + */ void (*region_add)(MemoryListener *listener, MemoryRegionSection *section); + + /** + * @region_del: + * + * Called during an address space update transaction, + * for a section of the address space that has disappeared in the address + * space since the last transaction. + * + * @listener: The #MemoryListener. + * @section: The old #MemoryRegionSection. + */ void (*region_del)(MemoryListener *listener, MemoryRegionSection *section); + + /** + * @region_nop: + * + * Called during an address space update transaction, + * for a section of the address space that is in the same place in the address + * space as in the last transaction. + * + * @listener: The #MemoryListener. + * @section: The #MemoryRegionSection. + */ void (*region_nop)(MemoryListener *listener, MemoryRegionSection *section); + + /** + * @log_start: + * + * Called during an address space update transaction, after + * one of #MemoryListener.region_add(),#MemoryListener.region_del() or + * #MemoryListener.region_nop(), if dirty memory logging clients have + * become active since the last transaction. + * + * @listener: The #MemoryListener. + * @section: The #MemoryRegionSection. + * @old: A bitmap of dirty memory logging clients that were active in + * the previous transaction. + * @new: A bitmap of dirty memory logging clients that are active in + * the current transaction. + */ void (*log_start)(MemoryListener *listener, MemoryRegionSection *section, int old, int new); + + /** + * @log_stop: + * + * Called during an address space update transaction, after + * one of #MemoryListener.region_add(), #MemoryListener.region_del() or + * #MemoryListener.region_nop() and possibly after + * #MemoryListener.log_start(), if dirty memory logging clients have + * become inactive since the last transaction. + * + * @listener: The #MemoryListener. + * @section: The #MemoryRegionSection. + * @old: A bitmap of dirty memory logging clients that were active in + * the previous transaction. + * @new: A bitmap of dirty memory logging clients that are active in + * the current transaction. + */ void (*log_stop)(MemoryListener *listener, MemoryRegionSection *section, int old, int new); + + /** + * @log_sync: + * + * Called by memory_region_snapshot_and_clear_dirty() and + * memory_global_dirty_log_sync(), before accessing QEMU's "official" + * copy of the dirty memory bitmap for a #MemoryRegionSection. + * + * @listener: The #MemoryListener. + * @section: The #MemoryRegionSection. + */ void (*log_sync)(MemoryListener *listener, MemoryRegionSection *section); + + /** + * @log_clear: + * + * Called before reading the dirty memory bitmap for a + * #MemoryRegionSection. + * + * @listener: The #MemoryListener. + * @section: The #MemoryRegionSection. + */ void (*log_clear)(MemoryListener *listener, MemoryRegionSection *section); + + /** + * @log_global_start: + * + * Called by memory_global_dirty_log_start(), which + * enables the %DIRTY_LOG_MIGRATION client on all memory regions in + * the address space. #MemoryListener.log_global_start() is also + * called when a #MemoryListener is added, if global dirty logging is + * active at that time. + * + * @listener: The #MemoryListener. + */ void (*log_global_start)(MemoryListener *listener); + + /** + * @log_global_stop: + * + * Called by memory_global_dirty_log_stop(), which + * disables the %DIRTY_LOG_MIGRATION client on all memory regions in + * the address space. + * + * @listener: The #MemoryListener. + */ void (*log_global_stop)(MemoryListener *listener); + + /** + * @log_global_after_sync: + * + * Called after reading the dirty memory bitmap + * for any #MemoryRegionSection. + * + * @listener: The #MemoryListener. + */ void (*log_global_after_sync)(MemoryListener *listener); + + /** + * @eventfd_add: + * + * Called during an address space update transaction, + * for a section of the address space that has had a new ioeventfd + * registration since the last transaction. + * + * @listener: The #MemoryListener. + * @section: The new #MemoryRegionSection. + * @match_data: The @match_data parameter for the new ioeventfd. + * @data: The @data parameter for the new ioeventfd. + * @e: The #EventNotifier parameter for the new ioeventfd. + */ void (*eventfd_add)(MemoryListener *listener, MemoryRegionSection *section, bool match_data, uint64_t data, EventNotifier *e); + + /** + * @eventfd_del: + * + * Called during an address space update transaction, + * for a section of the address space that has dropped an ioeventfd + * registration since the last transaction. + * + * @listener: The #MemoryListener. + * @section: The new #MemoryRegionSection. + * @match_data: The @match_data parameter for the dropped ioeventfd. + * @data: The @data parameter for the dropped ioeventfd. + * @e: The #EventNotifier parameter for the dropped ioeventfd. + */ void (*eventfd_del)(MemoryListener *listener, MemoryRegionSection *section, bool match_data, uint64_t data, EventNotifier *e); + + /** + * @coalesced_io_add: + * + * Called during an address space update transaction, + * for a section of the address space that has had a new coalesced + * MMIO range registration since the last transaction. + * + * @listener: The #MemoryListener. + * @section: The new #MemoryRegionSection. + * @addr: The starting address for the coalesced MMIO range. + * @len: The length of the coalesced MMIO range. + */ void (*coalesced_io_add)(MemoryListener *listener, MemoryRegionSection *section, hwaddr addr, hwaddr len); + + /** + * @coalesced_io_del: + * + * Called during an address space update transaction, + * for a section of the address space that has dropped a coalesced + * MMIO range since the last transaction. + * + * @listener: The #MemoryListener. + * @section: The new #MemoryRegionSection. + * @addr: The starting address for the coalesced MMIO range. + * @len: The length of the coalesced MMIO range. + */ void (*coalesced_io_del)(MemoryListener *listener, MemoryRegionSection *section, hwaddr addr, hwaddr len); - /* Lower = earlier (during add), later (during del) */ + /** + * @priority: + * + * Govern the order in which memory listeners are invoked. Lower priorities + * are invoked earlier for "add" or "start" callbacks, and later for "delete" + * or "stop" callbacks. + */ unsigned priority; + + /* private: */ AddressSpace *address_space; QTAILQ_ENTRY(MemoryListener) link; QTAILQ_ENTRY(MemoryListener) link_as; @@ -452,7 +658,7 @@ struct MemoryListener { * AddressSpace: describes a mapping of addresses to #MemoryRegion objects */ struct AddressSpace { - /* All fields are private. */ + /* private: */ struct rcu_head rcu; char *name; MemoryRegion *root; @@ -936,6 +1142,7 @@ void memory_region_init_rom(MemoryRegion *mr, * @mr: the #MemoryRegion to be initialized. * @owner: the object that tracks the region's reference count * @ops: callbacks for write access handling (must not be NULL). + * @opaque: passed to the read and write callbacks of the @ops structure. * @name: Region name, becomes part of RAMBlock name used in migration stream * must be unique within any device * @size: size of the region. @@ -1024,7 +1231,7 @@ static inline IOMMUMemoryRegion *memory_region_get_iommu(MemoryRegion *mr) * Returns pointer to IOMMUMemoryRegionClass if a memory region is an iommu, * otherwise NULL. This is fast path avoiding QOM checking, use with caution. * - * @mr: the memory region being queried + * @iommu_mr: the memory region being queried */ static inline IOMMUMemoryRegionClass *memory_region_get_iommu_class_nocheck( IOMMUMemoryRegion *iommu_mr) @@ -1094,6 +1301,7 @@ void memory_region_notify_one(IOMMUNotifier *notifier, * @n: the IOMMUNotifier to be added; the notify callback receives a * pointer to an #IOMMUTLBEntry as the opaque value; the pointer * ceases to be valid on exit from the notifier. + * @errp: pointer to Error*, to store an error if it happens. */ int memory_region_register_iommu_notifier(MemoryRegion *mr, IOMMUNotifier *n, Error **errp); @@ -1265,6 +1473,15 @@ void *memory_region_get_ram_ptr(MemoryRegion *mr); */ void memory_region_ram_resize(MemoryRegion *mr, ram_addr_t newsize, Error **errp); +/** + * memory_region_do_writeback: Trigger cache writeback or msync for + * selected address range + * + * @mr: the memory region to be updated + * @addr: the initial address of the range to be written back + * @size: the size of the range to be written back + */ +void memory_region_do_writeback(MemoryRegion *mr, hwaddr addr, hwaddr size); /** * memory_region_set_log: Turn dirty logging on or off for a region. @@ -1600,6 +1817,8 @@ void memory_region_add_subregion_overlap(MemoryRegion *mr, /** * memory_region_get_ram_addr: Get the ram address associated with a memory * region + * + * @mr: the region to be queried */ ram_addr_t memory_region_get_ram_addr(MemoryRegion *mr); @@ -1692,8 +1911,8 @@ bool memory_region_is_mapped(MemoryRegion *mr); * * Returns a #MemoryRegionSection that describes a contiguous overlap. * It will have the following characteristics: - * .@size = 0 iff no overlap was found - * .@mr is non-%NULL iff an overlap was found + * - @size = 0 iff no overlap was found + * - @mr is non-%NULL iff an overlap was found * * Remember that in the return value the @offset_within_region is * relative to the returned region (in the .@mr field), not to the @@ -1704,8 +1923,8 @@ bool memory_region_is_mapped(MemoryRegion *mr); * returned one. However, in the special case where the @mr argument * has no container (and thus is the root of the address space), the * following will hold: - * .@offset_within_address_space >= @addr - * .@offset_within_address_space + .@size <= @addr + @size + * - @offset_within_address_space >= @addr + * - @offset_within_address_space + .@size <= @addr + @size * * @mr: a MemoryRegion within which @addr is a relative address * @addr: start of the area within @as to be searched @@ -1852,7 +2071,7 @@ void address_space_remove_listeners(AddressSpace *as); * @is_write: indicates the transfer direction */ MemTxResult address_space_rw(AddressSpace *as, hwaddr addr, - MemTxAttrs attrs, uint8_t *buf, + MemTxAttrs attrs, void *buf, hwaddr len, bool is_write); /** @@ -1870,7 +2089,7 @@ MemTxResult address_space_rw(AddressSpace *as, hwaddr addr, */ MemTxResult address_space_write(AddressSpace *as, hwaddr addr, MemTxAttrs attrs, - const uint8_t *buf, hwaddr len); + const void *buf, hwaddr len); /** * address_space_write_rom: write to address space, including ROM. @@ -1896,7 +2115,7 @@ MemTxResult address_space_write(AddressSpace *as, hwaddr addr, */ MemTxResult address_space_write_rom(AddressSpace *as, hwaddr addr, MemTxAttrs attrs, - const uint8_t *buf, hwaddr len); + const void *buf, hwaddr len); /* address_space_ld*: load from an address space * address_space_st*: store to an address space @@ -2129,14 +2348,14 @@ void *address_space_map(AddressSpace *as, hwaddr addr, * @is_write: indicates the transfer direction */ void address_space_unmap(AddressSpace *as, void *buffer, hwaddr len, - int is_write, hwaddr access_len); + bool is_write, hwaddr access_len); /* Internal functions, part of the implementation of address_space_read. */ MemTxResult address_space_read_full(AddressSpace *as, hwaddr addr, - MemTxAttrs attrs, uint8_t *buf, hwaddr len); + MemTxAttrs attrs, void *buf, hwaddr len); MemTxResult flatview_read_continue(FlatView *fv, hwaddr addr, - MemTxAttrs attrs, uint8_t *buf, + MemTxAttrs attrs, void *buf, hwaddr len, hwaddr addr1, hwaddr l, MemoryRegion *mr); void *qemu_map_ram_ptr(RAMBlock *ram_block, ram_addr_t addr); @@ -2151,8 +2370,8 @@ void address_space_write_cached_slow(MemoryRegionCache *cache, static inline bool memory_access_is_direct(MemoryRegion *mr, bool is_write) { if (is_write) { - return memory_region_is_ram(mr) && - !mr->readonly && !memory_region_is_ram_device(mr); + return memory_region_is_ram(mr) && !mr->readonly && + !mr->rom_device && !memory_region_is_ram_device(mr); } else { return (memory_region_is_ram(mr) && !memory_region_is_ram_device(mr)) || memory_region_is_romd(mr); @@ -2170,10 +2389,11 @@ static inline bool memory_access_is_direct(MemoryRegion *mr, bool is_write) * @addr: address within that address space * @attrs: memory transaction attributes * @buf: buffer with the data transferred + * @len: length of the data transferred */ static inline __attribute__((__always_inline__)) MemTxResult address_space_read(AddressSpace *as, hwaddr addr, - MemTxAttrs attrs, uint8_t *buf, + MemTxAttrs attrs, void *buf, hwaddr len) { MemTxResult result = MEMTX_OK; @@ -2184,7 +2404,7 @@ MemTxResult address_space_read(AddressSpace *as, hwaddr addr, if (__builtin_constant_p(len)) { if (len) { - rcu_read_lock(); + RCU_READ_LOCK_GUARD(); fv = address_space_to_flatview(as); l = len; mr = flatview_translate(fv, addr, &addr1, &l, false, attrs); @@ -2195,7 +2415,6 @@ MemTxResult address_space_read(AddressSpace *as, hwaddr addr, result = flatview_read_continue(fv, addr, attrs, buf, len, addr1, l, mr); } - rcu_read_unlock(); } } else { result = address_space_read_full(as, addr, attrs, buf, len); @@ -2233,7 +2452,7 @@ address_space_read_cached(MemoryRegionCache *cache, hwaddr addr, */ static inline void address_space_write_cached(MemoryRegionCache *cache, hwaddr addr, - void *buf, hwaddr len) + const void *buf, hwaddr len) { assert(addr < cache->len && len <= cache->len - addr); if (likely(cache->ptr)) { diff --git a/include/exec/poison.h b/include/exec/poison.h index 955eb863ab..7b9ac361dc 100644 --- a/include/exec/poison.h +++ b/include/exec/poison.h @@ -26,6 +26,7 @@ #pragma GCC poison TARGET_PPC #pragma GCC poison TARGET_PPC64 #pragma GCC poison TARGET_ABI32 +#pragma GCC poison TARGET_RX #pragma GCC poison TARGET_S390X #pragma GCC poison TARGET_SH4 #pragma GCC poison TARGET_SPARC diff --git a/include/exec/ram_addr.h b/include/exec/ram_addr.h index fa3c922c9d..628bccee69 100644 --- a/include/exec/ram_addr.h +++ b/include/exec/ram_addr.h @@ -24,45 +24,7 @@ #include "hw/xen/xen.h" #include "sysemu/tcg.h" #include "exec/ramlist.h" - -struct RAMBlock { - struct rcu_head rcu; - struct MemoryRegion *mr; - uint8_t *host; - uint8_t *colo_cache; /* For colo, VM's ram cache */ - ram_addr_t offset; - ram_addr_t used_length; - ram_addr_t max_length; - void (*resized)(const char*, uint64_t length, void *host); - uint32_t flags; - /* Protected by iothread lock. */ - char idstr[256]; - /* RCU-enabled, writes protected by the ramlist lock */ - QLIST_ENTRY(RAMBlock) next; - QLIST_HEAD(, RAMBlockNotifier) ramblock_notifiers; - int fd; - size_t page_size; - /* dirty bitmap used during migration */ - unsigned long *bmap; - /* bitmap of already received pages in postcopy */ - unsigned long *receivedmap; - - /* - * bitmap to track already cleared dirty bitmap. When the bit is - * set, it means the corresponding memory chunk needs a log-clear. - * Set this up to non-NULL to enable the capability to postpone - * and split clearing of dirty bitmap on the remote node (e.g., - * KVM). The bitmap will be set only when doing global sync. - * - * NOTE: this bitmap is different comparing to the other bitmaps - * in that one bit can represent multiple guest pages (which is - * decided by the `clear_bmap_shift' variable below). On - * destination side, this should always be NULL, and the variable - * `clear_bmap_shift' is meaningless. - */ - unsigned long *clear_bmap; - uint8_t clear_bmap_shift; -}; +#include "exec/ramblock.h" /** * clear_bmap_size: calculate clear bitmap size @@ -174,6 +136,14 @@ void qemu_ram_free(RAMBlock *block); int qemu_ram_resize(RAMBlock *block, ram_addr_t newsize, Error **errp); +void qemu_ram_writeback(RAMBlock *block, ram_addr_t start, ram_addr_t length); + +/* Clear whole block of mem */ +static inline void qemu_ram_block_writeback(RAMBlock *block) +{ + qemu_ram_writeback(block, 0, block->used_length); +} + #define DIRTY_CLIENTS_ALL ((1 << DIRTY_MEMORY_NUM) - 1) #define DIRTY_CLIENTS_NOCODE (DIRTY_CLIENTS_ALL & ~(1 << DIRTY_MEMORY_CODE)) diff --git a/include/exec/ramblock.h b/include/exec/ramblock.h new file mode 100644 index 0000000000..07d50864d8 --- /dev/null +++ b/include/exec/ramblock.h @@ -0,0 +1,64 @@ +/* + * Declarations for cpu physical memory functions + * + * Copyright 2011 Red Hat, Inc. and/or its affiliates + * + * Authors: + * Avi Kivity + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * later. See the COPYING file in the top-level directory. + * + */ + +/* + * This header is for use by exec.c and memory.c ONLY. Do not include it. + * The functions declared here will be removed soon. + */ + +#ifndef QEMU_EXEC_RAMBLOCK_H +#define QEMU_EXEC_RAMBLOCK_H + +#ifndef CONFIG_USER_ONLY +#include "cpu-common.h" + +struct RAMBlock { + struct rcu_head rcu; + struct MemoryRegion *mr; + uint8_t *host; + uint8_t *colo_cache; /* For colo, VM's ram cache */ + ram_addr_t offset; + ram_addr_t used_length; + ram_addr_t max_length; + void (*resized)(const char*, uint64_t length, void *host); + uint32_t flags; + /* Protected by iothread lock. */ + char idstr[256]; + /* RCU-enabled, writes protected by the ramlist lock */ + QLIST_ENTRY(RAMBlock) next; + QLIST_HEAD(, RAMBlockNotifier) ramblock_notifiers; + int fd; + size_t page_size; + /* dirty bitmap used during migration */ + unsigned long *bmap; + /* bitmap of already received pages in postcopy */ + unsigned long *receivedmap; + + /* + * bitmap to track already cleared dirty bitmap. When the bit is + * set, it means the corresponding memory chunk needs a log-clear. + * Set this up to non-NULL to enable the capability to postpone + * and split clearing of dirty bitmap on the remote node (e.g., + * KVM). The bitmap will be set only when doing global sync. + * + * NOTE: this bitmap is different comparing to the other bitmaps + * in that one bit can represent multiple guest pages (which is + * decided by the `clear_bmap_shift' variable below). On + * destination side, this should always be NULL, and the variable + * `clear_bmap_shift' is meaningless. + */ + unsigned long *clear_bmap; + uint8_t clear_bmap_shift; +}; +#endif +#endif diff --git a/include/exec/translator.h b/include/exec/translator.h index 459dd72aab..638e1529c5 100644 --- a/include/exec/translator.h +++ b/include/exec/translator.h @@ -148,41 +148,19 @@ void translator_loop_temp_check(DisasContextBase *db); /* * Translator Load Functions * - * These are intended to replace the old cpu_ld*_code functions and - * are mandatory for front-ends that have been migrated to the common - * translator_loop. These functions are only intended to be called - * from the translation stage and should not be called from helper - * functions. Those functions should be converted to encode the - * relevant information at translation time. + * These are intended to replace the direct usage of the cpu_ld*_code + * functions and are mandatory for front-ends that have been migrated + * to the common translator_loop. These functions are only intended + * to be called from the translation stage and should not be called + * from helper functions. Those functions should be converted to encode + * the relevant information at translation time. */ -#ifdef CONFIG_USER_ONLY - -#define DO_LOAD(type, name, shift) \ - do { \ - set_helper_retaddr(1); \ - ret = name ## _p(g2h(pc)); \ - clear_helper_retaddr(); \ - } while (0) - -#else - -#define DO_LOAD(type, name, shift) \ - do { \ - int mmu_idx = cpu_mmu_index(env, true); \ - TCGMemOpIdx oi = make_memop_idx(shift, mmu_idx); \ - ret = helper_ret_ ## name ## _cmmu(env, pc, oi, 0); \ - } while (0) - -#endif - -#define GEN_TRANSLATOR_LD(fullname, name, type, shift, swap_fn) \ +#define GEN_TRANSLATOR_LD(fullname, type, load_fn, swap_fn) \ static inline type \ fullname ## _swap(CPUArchState *env, abi_ptr pc, bool do_swap) \ { \ - type ret; \ - DO_LOAD(type, name, shift); \ - \ + type ret = load_fn(env, pc); \ if (do_swap) { \ ret = swap_fn(ret); \ } \ @@ -195,11 +173,11 @@ void translator_loop_temp_check(DisasContextBase *db); return fullname ## _swap(env, pc, false); \ } -GEN_TRANSLATOR_LD(translator_ldub, ldub, uint8_t, 0, /* no swap */ ) -GEN_TRANSLATOR_LD(translator_ldsw, ldsw, int16_t, 1, bswap16) -GEN_TRANSLATOR_LD(translator_lduw, lduw, uint16_t, 1, bswap16) -GEN_TRANSLATOR_LD(translator_ldl, ldl, uint32_t, 2, bswap32) -GEN_TRANSLATOR_LD(translator_ldq, ldq, uint64_t, 3, bswap64) +GEN_TRANSLATOR_LD(translator_ldub, uint8_t, cpu_ldub_code, /* no swap */) +GEN_TRANSLATOR_LD(translator_ldsw, int16_t, cpu_ldsw_code, bswap16) +GEN_TRANSLATOR_LD(translator_lduw, uint16_t, cpu_lduw_code, bswap16) +GEN_TRANSLATOR_LD(translator_ldl, uint32_t, cpu_ldl_code, bswap32) +GEN_TRANSLATOR_LD(translator_ldq, uint64_t, cpu_ldq_code, bswap64) #undef GEN_TRANSLATOR_LD #endif /* EXEC__TRANSLATOR_H */ diff --git a/include/hw/acpi/acpi-defs.h b/include/hw/acpi/acpi-defs.h index 57a3f58b0c..c13327fa78 100644 --- a/include/hw/acpi/acpi-defs.h +++ b/include/hw/acpi/acpi-defs.h @@ -152,7 +152,7 @@ typedef struct AcpiSerialPortConsoleRedirection */ struct AcpiRsdtDescriptorRev1 { ACPI_TABLE_HEADER_DEF /* ACPI common table header */ - uint32_t table_offset_entry[0]; /* Array of pointers to other */ + uint32_t table_offset_entry[]; /* Array of pointers to other */ /* ACPI tables */ } QEMU_PACKED; typedef struct AcpiRsdtDescriptorRev1 AcpiRsdtDescriptorRev1; @@ -162,7 +162,7 @@ typedef struct AcpiRsdtDescriptorRev1 AcpiRsdtDescriptorRev1; */ struct AcpiXsdtDescriptorRev2 { ACPI_TABLE_HEADER_DEF /* ACPI common table header */ - uint64_t table_offset_entry[0]; /* Array of pointers to other */ + uint64_t table_offset_entry[]; /* Array of pointers to other */ /* ACPI tables */ } QEMU_PACKED; typedef struct AcpiXsdtDescriptorRev2 AcpiXsdtDescriptorRev2; @@ -518,7 +518,7 @@ struct AcpiDmarDeviceScope { struct { uint8_t device; uint8_t function; - } path[0]; + } path[]; } QEMU_PACKED; typedef struct AcpiDmarDeviceScope AcpiDmarDeviceScope; @@ -530,7 +530,7 @@ struct AcpiDmarHardwareUnit { uint8_t reserved; uint16_t pci_segment; /* The PCI Segment associated with this unit */ uint64_t address; /* Base address of remapping hardware register-set */ - AcpiDmarDeviceScope scope[0]; + AcpiDmarDeviceScope scope[]; } QEMU_PACKED; typedef struct AcpiDmarHardwareUnit AcpiDmarHardwareUnit; @@ -541,7 +541,7 @@ struct AcpiDmarRootPortATS { uint8_t flags; uint8_t reserved; uint16_t pci_segment; - AcpiDmarDeviceScope scope[0]; + AcpiDmarDeviceScope scope[]; } QEMU_PACKED; typedef struct AcpiDmarRootPortATS AcpiDmarRootPortATS; @@ -604,7 +604,7 @@ typedef struct AcpiIortMemoryAccess AcpiIortMemoryAccess; struct AcpiIortItsGroup { ACPI_IORT_NODE_HEADER_DEF uint32_t its_count; - uint32_t identifiers[0]; + uint32_t identifiers[]; } QEMU_PACKED; typedef struct AcpiIortItsGroup AcpiIortItsGroup; @@ -621,7 +621,7 @@ struct AcpiIortSmmu3 { uint32_t pri_gsiv; uint32_t gerr_gsiv; uint32_t sync_gsiv; - AcpiIortIdMapping id_mapping_array[0]; + AcpiIortIdMapping id_mapping_array[]; } QEMU_PACKED; typedef struct AcpiIortSmmu3 AcpiIortSmmu3; @@ -630,7 +630,7 @@ struct AcpiIortRC { AcpiIortMemoryAccess memory_properties; uint32_t ats_attribute; uint32_t pci_segment_number; - AcpiIortIdMapping id_mapping_array[0]; + AcpiIortIdMapping id_mapping_array[]; } QEMU_PACKED; typedef struct AcpiIortRC AcpiIortRC; diff --git a/include/hw/acpi/acpi.h b/include/hw/acpi/acpi.h index 2fc08d825f..36c7678ec0 100644 --- a/include/hw/acpi/acpi.h +++ b/include/hw/acpi/acpi.h @@ -185,7 +185,6 @@ void acpi_send_gpe_event(ACPIREGS *ar, qemu_irq irq, void acpi_update_sci(ACPIREGS *acpi_regs, qemu_irq irq); /* acpi.c */ -extern int acpi_enabled; extern char unsigned *acpi_tables; extern size_t acpi_tables_len; diff --git a/include/hw/acpi/aml-build.h b/include/hw/acpi/aml-build.h index de4a406568..0f4ed53d7f 100644 --- a/include/hw/acpi/aml-build.h +++ b/include/hw/acpi/aml-build.h @@ -13,6 +13,7 @@ #define ACPI_BUILD_TABLE_FILE "etc/acpi/tables" #define ACPI_BUILD_RSDP_FILE "etc/acpi/rsdp" #define ACPI_BUILD_TPMLOG_FILE "etc/tpm/log" +#define ACPI_BUILD_LOADER_FILE "etc/table-loader" #define AML_NOTIFY_METHOD "NTFY" diff --git a/include/hw/arm/allwinner-a10.h b/include/hw/arm/allwinner-a10.h index 7d2d215630..77c82a9982 100644 --- a/include/hw/arm/allwinner-a10.h +++ b/include/hw/arm/allwinner-a10.h @@ -7,19 +7,19 @@ #include "hw/timer/allwinner-a10-pit.h" #include "hw/intc/allwinner-a10-pic.h" #include "hw/net/allwinner_emac.h" +#include "hw/sd/allwinner-sdhost.h" #include "hw/ide/ahci.h" +#include "hw/usb/hcd-ohci.h" +#include "hw/usb/hcd-ehci.h" +#include "hw/rtc/allwinner-rtc.h" #include "target/arm/cpu.h" -#define AW_A10_PIC_REG_BASE 0x01c20400 -#define AW_A10_PIT_REG_BASE 0x01c20c00 -#define AW_A10_UART0_REG_BASE 0x01c28000 -#define AW_A10_EMAC_BASE 0x01c0b000 -#define AW_A10_SATA_BASE 0x01c18000 - #define AW_A10_SDRAM_BASE 0x40000000 +#define AW_A10_NUM_USB 2 + #define TYPE_AW_A10 "allwinner-a10" #define AW_A10(obj) OBJECT_CHECK(AwA10State, (obj), TYPE_AW_A10) @@ -29,12 +29,15 @@ typedef struct AwA10State { /*< public >*/ ARMCPU cpu; - qemu_irq irq[AW_A10_PIC_INT_NR]; AwA10PITState timer; AwA10PICState intc; AwEmacState emac; AllwinnerAHCIState sata; + AwSdHostState mmc0; + AwRtcState rtc; MemoryRegion sram_a; + EHCISysBusState ehci[AW_A10_NUM_USB]; + OHCISysBusState ohci[AW_A10_NUM_USB]; } AwA10State; #endif diff --git a/include/hw/arm/allwinner-h3.h b/include/hw/arm/allwinner-h3.h new file mode 100644 index 0000000000..82e4e59216 --- /dev/null +++ b/include/hw/arm/allwinner-h3.h @@ -0,0 +1,161 @@ +/* + * Allwinner H3 System on Chip emulation + * + * Copyright (C) 2019 Niek Linnenbank + * + * 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 . + */ + +/* + * The Allwinner H3 is a System on Chip containing four ARM Cortex A7 + * processor cores. Features and specifications include DDR2/DDR3 memory, + * SD/MMC storage cards, 10/100/1000Mbit Ethernet, USB 2.0, HDMI and + * various I/O modules. + * + * This implementation is based on the following datasheet: + * + * https://linux-sunxi.org/File:Allwinner_H3_Datasheet_V1.2.pdf + * + * The latest datasheet and more info can be found on the Linux Sunxi wiki: + * + * https://linux-sunxi.org/H3 + */ + +#ifndef HW_ARM_ALLWINNER_H3_H +#define HW_ARM_ALLWINNER_H3_H + +#include "qom/object.h" +#include "hw/arm/boot.h" +#include "hw/timer/allwinner-a10-pit.h" +#include "hw/intc/arm_gic.h" +#include "hw/misc/allwinner-h3-ccu.h" +#include "hw/misc/allwinner-cpucfg.h" +#include "hw/misc/allwinner-h3-dramc.h" +#include "hw/misc/allwinner-h3-sysctrl.h" +#include "hw/misc/allwinner-sid.h" +#include "hw/sd/allwinner-sdhost.h" +#include "hw/net/allwinner-sun8i-emac.h" +#include "hw/rtc/allwinner-rtc.h" +#include "target/arm/cpu.h" +#include "sysemu/block-backend.h" + +/** + * Allwinner H3 device list + * + * This enumeration is can be used refer to a particular device in the + * Allwinner H3 SoC. For example, the physical memory base address for + * each device can be found in the AwH3State object in the memmap member + * using the device enum value as index. + * + * @see AwH3State + */ +enum { + AW_H3_SRAM_A1, + AW_H3_SRAM_A2, + AW_H3_SRAM_C, + AW_H3_SYSCTRL, + AW_H3_MMC0, + AW_H3_SID, + AW_H3_EHCI0, + AW_H3_OHCI0, + AW_H3_EHCI1, + AW_H3_OHCI1, + AW_H3_EHCI2, + AW_H3_OHCI2, + AW_H3_EHCI3, + AW_H3_OHCI3, + AW_H3_CCU, + AW_H3_PIT, + AW_H3_UART0, + AW_H3_UART1, + AW_H3_UART2, + AW_H3_UART3, + AW_H3_EMAC, + AW_H3_DRAMCOM, + AW_H3_DRAMCTL, + AW_H3_DRAMPHY, + AW_H3_GIC_DIST, + AW_H3_GIC_CPU, + AW_H3_GIC_HYP, + AW_H3_GIC_VCPU, + AW_H3_RTC, + AW_H3_CPUCFG, + AW_H3_SDRAM +}; + +/** Total number of CPU cores in the H3 SoC */ +#define AW_H3_NUM_CPUS (4) + +/** + * Allwinner H3 object model + * @{ + */ + +/** Object type for the Allwinner H3 SoC */ +#define TYPE_AW_H3 "allwinner-h3" + +/** Convert input object to Allwinner H3 state object */ +#define AW_H3(obj) OBJECT_CHECK(AwH3State, (obj), TYPE_AW_H3) + +/** @} */ + +/** + * Allwinner H3 object + * + * This struct contains the state of all the devices + * which are currently emulated by the H3 SoC code. + */ +typedef struct AwH3State { + /*< private >*/ + DeviceState parent_obj; + /*< public >*/ + + ARMCPU cpus[AW_H3_NUM_CPUS]; + const hwaddr *memmap; + AwA10PITState timer; + AwH3ClockCtlState ccu; + AwCpuCfgState cpucfg; + AwH3DramCtlState dramc; + AwH3SysCtrlState sysctrl; + AwSidState sid; + AwSdHostState mmc0; + AwSun8iEmacState emac; + AwRtcState rtc; + GICState gic; + MemoryRegion sram_a1; + MemoryRegion sram_a2; + MemoryRegion sram_c; +} AwH3State; + +/** + * Emulate Boot ROM firmware setup functionality. + * + * A real Allwinner H3 SoC contains a Boot ROM + * which is the first code that runs right after + * the SoC is powered on. The Boot ROM is responsible + * for loading user code (e.g. a bootloader) from any + * of the supported external devices and writing the + * downloaded code to internal SRAM. After loading the SoC + * begins executing the code written to SRAM. + * + * This function emulates the Boot ROM by copying 32 KiB + * of data from the given block device and writes it to + * the start of the first internal SRAM memory. + * + * @s: Allwinner H3 state object pointer + * @blk: Block backend device object pointer + */ +void allwinner_h3_bootrom_setup(AwH3State *s, BlockBackend *blk); + +#endif /* HW_ARM_ALLWINNER_H3_H */ diff --git a/include/hw/arm/aspeed.h b/include/hw/arm/aspeed.h index f49bc7081e..18521484b9 100644 --- a/include/hw/arm/aspeed.h +++ b/include/hw/arm/aspeed.h @@ -13,25 +13,14 @@ typedef struct AspeedBoardState AspeedBoardState; -typedef struct AspeedBoardConfig { - const char *name; - const char *desc; - const char *soc_name; - uint32_t hw_strap1; - uint32_t hw_strap2; - const char *fmc_model; - const char *spi_model; - uint32_t num_cs; - void (*i2c_init)(AspeedBoardState *bmc); - uint32_t ram; -} AspeedBoardConfig; - #define TYPE_ASPEED_MACHINE MACHINE_TYPE_NAME("aspeed") #define ASPEED_MACHINE(obj) \ OBJECT_CHECK(AspeedMachine, (obj), TYPE_ASPEED_MACHINE) typedef struct AspeedMachine { MachineState parent_obj; + + bool mmio_exec; } AspeedMachine; #define ASPEED_MACHINE_CLASS(klass) \ @@ -41,7 +30,16 @@ typedef struct AspeedMachine { typedef struct AspeedMachineClass { MachineClass parent_obj; - const AspeedBoardConfig *board; + + const char *name; + const char *desc; + const char *soc_name; + uint32_t hw_strap1; + uint32_t hw_strap2; + const char *fmc_model; + const char *spi_model; + uint32_t num_cs; + void (*i2c_init)(AspeedBoardState *bmc); } AspeedMachineClass; diff --git a/include/hw/arm/aspeed_soc.h b/include/hw/arm/aspeed_soc.h index 495c08be1b..78b9f6ae53 100644 --- a/include/hw/arm/aspeed_soc.h +++ b/include/hw/arm/aspeed_soc.h @@ -26,8 +26,10 @@ #include "target/arm/cpu.h" #include "hw/gpio/aspeed_gpio.h" #include "hw/sd/aspeed_sdhci.h" +#include "hw/usb/hcd-ehci.h" #define ASPEED_SPIS_NUM 2 +#define ASPEED_EHCIS_NUM 2 #define ASPEED_WDTS_NUM 4 #define ASPEED_CPUS_NUM 2 #define ASPEED_MACS_NUM 4 @@ -40,6 +42,7 @@ typedef struct AspeedSoCState { ARMCPU cpu[ASPEED_CPUS_NUM]; uint32_t num_cpus; A15MPPrivState a7mpcore; + MemoryRegion *dram_mr; MemoryRegion sram; AspeedVICState vic; AspeedRtcState rtc; @@ -49,6 +52,7 @@ typedef struct AspeedSoCState { AspeedXDMAState xdma; AspeedSMCState fmc; AspeedSMCState spi[ASPEED_SPIS_NUM]; + EHCISysBusState ehci[ASPEED_EHCIS_NUM]; AspeedSDMCState sdmc; AspeedWDTState wdt[ASPEED_WDTS_NUM]; FTGMAC100State ftgmac100[ASPEED_MACS_NUM]; @@ -56,6 +60,7 @@ typedef struct AspeedSoCState { AspeedGPIOState gpio; AspeedGPIOState gpio_1_8v; AspeedSDHCIState sdhci; + AspeedSDHCIState emmc; } AspeedSoCState; #define TYPE_ASPEED_SOC "aspeed-soc" @@ -69,6 +74,7 @@ typedef struct AspeedSoCClass { uint32_t silicon_rev; uint64_t sram_size; int spis_num; + int ehcis_num; int wdts_num; int macs_num; const int *irqmap; @@ -92,6 +98,8 @@ enum { ASPEED_FMC, ASPEED_SPI1, ASPEED_SPI2, + ASPEED_EHCI1, + ASPEED_EHCI2, ASPEED_VIC, ASPEED_SDMC, ASPEED_SCU, @@ -125,6 +133,7 @@ enum { ASPEED_MII4, ASPEED_SDRAM, ASPEED_XDMA, + ASPEED_EMMC, }; #endif /* ASPEED_SOC_H */ diff --git a/include/hw/arm/bcm2835_peripherals.h b/include/hw/arm/bcm2835_peripherals.h index 7859281e11..2e8655a7c2 100644 --- a/include/hw/arm/bcm2835_peripherals.h +++ b/include/hw/arm/bcm2835_peripherals.h @@ -5,7 +5,8 @@ * Rasperry Pi 2 emulation and refactoring Copyright (c) 2015, Microsoft * Written by Andrew Baumann * - * This code is licensed under the GNU GPLv2 and later. + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. */ #ifndef BCM2835_PERIPHERALS_H diff --git a/include/hw/arm/bcm2836.h b/include/hw/arm/bcm2836.h index 92a6544816..024af8aae4 100644 --- a/include/hw/arm/bcm2836.h +++ b/include/hw/arm/bcm2836.h @@ -5,7 +5,8 @@ * Rasperry Pi 2 emulation and refactoring Copyright (c) 2015, Microsoft * Written by Andrew Baumann * - * This code is licensed under the GNU GPLv2 and later. + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. */ #ifndef BCM2836_H diff --git a/include/hw/arm/exynos4210.h b/include/hw/arm/exynos4210.h index f0f23b0e9b..55260394af 100644 --- a/include/hw/arm/exynos4210.h +++ b/include/hw/arm/exynos4210.h @@ -24,6 +24,7 @@ #ifndef EXYNOS4210_H #define EXYNOS4210_H +#include "hw/or-irq.h" #include "hw/sysbus.h" #include "target/arm/cpu-qom.h" @@ -74,6 +75,8 @@ #define EXYNOS4210_I2C_NUMBER 9 +#define EXYNOS4210_NUM_DMA 3 + typedef struct Exynos4210Irq { qemu_irq int_combiner_irq[EXYNOS4210_MAX_INT_COMBINER_IN_IRQ]; qemu_irq ext_combiner_irq[EXYNOS4210_MAX_EXT_COMBINER_IN_IRQ]; @@ -97,6 +100,7 @@ typedef struct Exynos4210State { MemoryRegion boot_secondary; MemoryRegion bootreg_mem; I2CBus *i2c_if[EXYNOS4210_I2C_NUMBER]; + qemu_or_irq pl330_irq_orgate[EXYNOS4210_NUM_DMA]; } Exynos4210State; #define TYPE_EXYNOS4210_SOC "exynos4210" diff --git a/include/hw/arm/fsl-imx25.h b/include/hw/arm/fsl-imx25.h index 241efb52ae..5e196bbf05 100644 --- a/include/hw/arm/fsl-imx25.h +++ b/include/hw/arm/fsl-imx25.h @@ -24,8 +24,11 @@ #include "hw/timer/imx_gpt.h" #include "hw/timer/imx_epit.h" #include "hw/net/imx_fec.h" +#include "hw/misc/imx_rngc.h" #include "hw/i2c/imx_i2c.h" #include "hw/gpio/imx_gpio.h" +#include "hw/sd/sdhci.h" +#include "hw/usb/chipidea.h" #include "exec/memory.h" #include "target/arm/cpu.h" @@ -37,6 +40,8 @@ #define FSL_IMX25_NUM_EPITS 2 #define FSL_IMX25_NUM_I2CS 3 #define FSL_IMX25_NUM_GPIOS 4 +#define FSL_IMX25_NUM_ESDHCS 2 +#define FSL_IMX25_NUM_USBS 2 typedef struct FslIMX25State { /*< private >*/ @@ -50,8 +55,11 @@ typedef struct FslIMX25State { IMXGPTState gpt[FSL_IMX25_NUM_GPTS]; IMXEPITState epit[FSL_IMX25_NUM_EPITS]; IMXFECState fec; + IMXRNGCState rngc; IMXI2CState i2c[FSL_IMX25_NUM_I2CS]; IMXGPIOState gpio[FSL_IMX25_NUM_GPIOS]; + SDHCIState esdhc[FSL_IMX25_NUM_ESDHCS]; + ChipideaState usb[FSL_IMX25_NUM_USBS]; MemoryRegion rom[2]; MemoryRegion iram; MemoryRegion iram_alias; @@ -211,10 +219,20 @@ typedef struct FslIMX25State { #define FSL_IMX25_GPIO4_SIZE 0x4000 #define FSL_IMX25_GPIO3_ADDR 0x53FA4000 #define FSL_IMX25_GPIO3_SIZE 0x4000 +#define FSL_IMX25_RNGC_ADDR 0x53FB0000 +#define FSL_IMX25_RNGC_SIZE 0x4000 +#define FSL_IMX25_ESDHC1_ADDR 0x53FB4000 +#define FSL_IMX25_ESDHC1_SIZE 0x4000 +#define FSL_IMX25_ESDHC2_ADDR 0x53FB8000 +#define FSL_IMX25_ESDHC2_SIZE 0x4000 #define FSL_IMX25_GPIO1_ADDR 0x53FCC000 #define FSL_IMX25_GPIO1_SIZE 0x4000 #define FSL_IMX25_GPIO2_ADDR 0x53FD0000 #define FSL_IMX25_GPIO2_SIZE 0x4000 +#define FSL_IMX25_USB1_ADDR 0x53FF4000 +#define FSL_IMX25_USB1_SIZE 0x0200 +#define FSL_IMX25_USB2_ADDR 0x53FF4400 +#define FSL_IMX25_USB2_SIZE 0x0200 #define FSL_IMX25_AVIC_ADDR 0x68000000 #define FSL_IMX25_AVIC_SIZE 0x4000 #define FSL_IMX25_IRAM_ADDR 0x78000000 @@ -238,6 +256,7 @@ typedef struct FslIMX25State { #define FSL_IMX25_EPIT1_IRQ 28 #define FSL_IMX25_EPIT2_IRQ 27 #define FSL_IMX25_FEC_IRQ 57 +#define FSL_IMX25_RNGC_IRQ 22 #define FSL_IMX25_I2C1_IRQ 3 #define FSL_IMX25_I2C2_IRQ 4 #define FSL_IMX25_I2C3_IRQ 10 @@ -245,5 +264,9 @@ typedef struct FslIMX25State { #define FSL_IMX25_GPIO2_IRQ 51 #define FSL_IMX25_GPIO3_IRQ 16 #define FSL_IMX25_GPIO4_IRQ 23 +#define FSL_IMX25_ESDHC1_IRQ 9 +#define FSL_IMX25_ESDHC2_IRQ 8 +#define FSL_IMX25_USB1_IRQ 37 +#define FSL_IMX25_USB2_IRQ 35 #endif /* FSL_IMX25_H */ diff --git a/include/hw/arm/fsl-imx6.h b/include/hw/arm/fsl-imx6.h index 1265a55c3b..973bcb72f7 100644 --- a/include/hw/arm/fsl-imx6.h +++ b/include/hw/arm/fsl-imx6.h @@ -21,6 +21,7 @@ #include "hw/cpu/a9mpcore.h" #include "hw/misc/imx6_ccm.h" #include "hw/misc/imx6_src.h" +#include "hw/misc/imx2_wdt.h" #include "hw/char/imx_serial.h" #include "hw/timer/imx_gpt.h" #include "hw/timer/imx_epit.h" @@ -29,6 +30,8 @@ #include "hw/sd/sdhci.h" #include "hw/ssi/imx_spi.h" #include "hw/net/imx_fec.h" +#include "hw/usb/chipidea.h" +#include "hw/usb/imx-usb-phy.h" #include "exec/memory.h" #include "cpu.h" @@ -42,6 +45,9 @@ #define FSL_IMX6_NUM_GPIOS 7 #define FSL_IMX6_NUM_ESDHCS 4 #define FSL_IMX6_NUM_ECSPIS 5 +#define FSL_IMX6_NUM_WDTS 2 +#define FSL_IMX6_NUM_USB_PHYS 2 +#define FSL_IMX6_NUM_USBS 4 typedef struct FslIMX6State { /*< private >*/ @@ -59,6 +65,9 @@ typedef struct FslIMX6State { IMXGPIOState gpio[FSL_IMX6_NUM_GPIOS]; SDHCIState esdhc[FSL_IMX6_NUM_ESDHCS]; IMXSPIState spi[FSL_IMX6_NUM_ECSPIS]; + IMX2WdtState wdt[FSL_IMX6_NUM_WDTS]; + IMXUSBPHYState usbphy[FSL_IMX6_NUM_USB_PHYS]; + ChipideaState usb[FSL_IMX6_NUM_USBS]; IMXFECState eth; MemoryRegion rom; MemoryRegion caam; diff --git a/include/hw/arm/fsl-imx6ul.h b/include/hw/arm/fsl-imx6ul.h index eda389aec7..1a0bab8daa 100644 --- a/include/hw/arm/fsl-imx6ul.h +++ b/include/hw/arm/fsl-imx6ul.h @@ -34,6 +34,8 @@ #include "hw/sd/sdhci.h" #include "hw/ssi/imx_spi.h" #include "hw/net/imx_fec.h" +#include "hw/usb/chipidea.h" +#include "hw/usb/imx-usb-phy.h" #include "exec/memory.h" #include "cpu.h" @@ -54,6 +56,8 @@ enum FslIMX6ULConfiguration { FSL_IMX6UL_NUM_I2CS = 4, FSL_IMX6UL_NUM_ECSPIS = 4, FSL_IMX6UL_NUM_ADCS = 2, + FSL_IMX6UL_NUM_USB_PHYS = 2, + FSL_IMX6UL_NUM_USBS = 2, }; typedef struct FslIMX6ULState { @@ -77,6 +81,8 @@ typedef struct FslIMX6ULState { IMXFECState eth[FSL_IMX6UL_NUM_ETHS]; SDHCIState usdhc[FSL_IMX6UL_NUM_USDHCS]; IMX2WdtState wdt[FSL_IMX6UL_NUM_WDTS]; + IMXUSBPHYState usbphy[FSL_IMX6UL_NUM_USB_PHYS]; + ChipideaState usb[FSL_IMX6UL_NUM_USBS]; MemoryRegion rom; MemoryRegion caam; MemoryRegion ocram; @@ -145,6 +151,10 @@ enum FslIMX6ULMemoryMap { FSL_IMX6UL_EPIT2_ADDR = 0x020D4000, FSL_IMX6UL_EPIT1_ADDR = 0x020D0000, FSL_IMX6UL_SNVS_HP_ADDR = 0x020CC000, + FSL_IMX6UL_USBPHY2_ADDR = 0x020CA000, + FSL_IMX6UL_USBPHY2_SIZE = (4 * 1024), + FSL_IMX6UL_USBPHY1_ADDR = 0x020C9000, + FSL_IMX6UL_USBPHY1_SIZE = (4 * 1024), FSL_IMX6UL_ANALOG_ADDR = 0x020C8000, FSL_IMX6UL_CCM_ADDR = 0x020C4000, FSL_IMX6UL_WDOG2_ADDR = 0x020C0000, @@ -241,10 +251,10 @@ enum FslIMX6ULIRQs { FSL_IMX6UL_UART7_IRQ = 39, FSL_IMX6UL_UART8_IRQ = 40, - FSL_IMX6UL_USB1_IRQ = 42, - FSL_IMX6UL_USB2_IRQ = 43, + FSL_IMX6UL_USB1_IRQ = 43, + FSL_IMX6UL_USB2_IRQ = 42, FSL_IMX6UL_USB_PHY1_IRQ = 44, - FSL_IMX6UL_USB_PHY2_IRQ = 44, + FSL_IMX6UL_USB_PHY2_IRQ = 45, FSL_IMX6UL_CAAM_JQ2_IRQ = 46, FSL_IMX6UL_CAAM_ERR_IRQ = 47, diff --git a/include/hw/arm/omap.h b/include/hw/arm/omap.h index f3aa670036..6be386d0e2 100644 --- a/include/hw/arm/omap.h +++ b/include/hw/arm/omap.h @@ -67,6 +67,58 @@ void omap_clk_setrate(omap_clk clk, int divide, int multiply); int64_t omap_clk_getrate(omap_clk clk); void omap_clk_reparent(omap_clk clk, omap_clk parent); +/* omap_intc.c */ +#define TYPE_OMAP_INTC "common-omap-intc" +#define OMAP_INTC(obj) \ + OBJECT_CHECK(omap_intr_handler, (obj), TYPE_OMAP_INTC) + +typedef struct omap_intr_handler_s omap_intr_handler; + +/* + * TODO: Ideally we should have a clock framework that + * let us wire these clocks up with QOM properties or links. + * + * qdev should support a generic means of defining a 'port' with + * an arbitrary interface for connecting two devices. Then we + * could reframe the omap clock API in terms of clock ports, + * and get some type safety. For now the best qdev provides is + * passing an arbitrary pointer. + * (It's not possible to pass in the string which is the clock + * name, because this device does not have the necessary information + * (ie the struct omap_mpu_state_s*) to do the clockname to pointer + * translation.) + */ +void omap_intc_set_iclk(omap_intr_handler *intc, omap_clk clk); +void omap_intc_set_fclk(omap_intr_handler *intc, omap_clk clk); + +/* omap_i2c.c */ +#define TYPE_OMAP_I2C "omap_i2c" +#define OMAP_I2C(obj) OBJECT_CHECK(OMAPI2CState, (obj), TYPE_OMAP_I2C) + +typedef struct OMAPI2CState OMAPI2CState; + +/* TODO: clock framework (see above) */ +void omap_i2c_set_iclk(OMAPI2CState *i2c, omap_clk clk); +void omap_i2c_set_fclk(OMAPI2CState *i2c, omap_clk clk); + +/* omap_gpio.c */ +#define TYPE_OMAP1_GPIO "omap-gpio" +#define OMAP1_GPIO(obj) \ + OBJECT_CHECK(struct omap_gpif_s, (obj), TYPE_OMAP1_GPIO) + +#define TYPE_OMAP2_GPIO "omap2-gpio" +#define OMAP2_GPIO(obj) \ + OBJECT_CHECK(struct omap2_gpif_s, (obj), TYPE_OMAP2_GPIO) + +typedef struct omap_gpif_s omap_gpif; +typedef struct omap2_gpif_s omap2_gpif; + +/* TODO: clock framework (see above) */ +void omap_gpio_set_clk(omap_gpif *gpio, omap_clk clk); + +void omap2_gpio_set_iclk(omap2_gpif *gpio, omap_clk clk); +void omap2_gpio_set_fclk(omap2_gpif *gpio, uint8_t i, omap_clk clk); + /* OMAP2 l4 Interconnect */ struct omap_l4_s; struct omap_l4_region_s { diff --git a/include/hw/arm/smmu-common.h b/include/hw/arm/smmu-common.h index 1f37844e5c..ca4a4b1ad1 100644 --- a/include/hw/arm/smmu-common.h +++ b/include/hw/arm/smmu-common.h @@ -85,7 +85,7 @@ typedef struct SMMUDevice { typedef struct SMMUPciBus { PCIBus *bus; - SMMUDevice *pbdev[0]; /* Parent array is sparse, so dynamically alloc */ + SMMUDevice *pbdev[]; /* Parent array is sparse, so dynamically alloc */ } SMMUPciBus; typedef struct SMMUIOTLBKey { diff --git a/include/hw/arm/stm32f405_soc.h b/include/hw/arm/stm32f405_soc.h new file mode 100644 index 0000000000..1fe97f8c3a --- /dev/null +++ b/include/hw/arm/stm32f405_soc.h @@ -0,0 +1,73 @@ +/* + * STM32F405 SoC + * + * Copyright (c) 2014 Alistair Francis + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef HW_ARM_STM32F405_SOC_H +#define HW_ARM_STM32F405_SOC_H + +#include "hw/misc/stm32f4xx_syscfg.h" +#include "hw/timer/stm32f2xx_timer.h" +#include "hw/char/stm32f2xx_usart.h" +#include "hw/adc/stm32f2xx_adc.h" +#include "hw/misc/stm32f4xx_exti.h" +#include "hw/or-irq.h" +#include "hw/ssi/stm32f2xx_spi.h" +#include "hw/arm/armv7m.h" + +#define TYPE_STM32F405_SOC "stm32f405-soc" +#define STM32F405_SOC(obj) \ + OBJECT_CHECK(STM32F405State, (obj), TYPE_STM32F405_SOC) + +#define STM_NUM_USARTS 7 +#define STM_NUM_TIMERS 4 +#define STM_NUM_ADCS 6 +#define STM_NUM_SPIS 6 + +#define FLASH_BASE_ADDRESS 0x08000000 +#define FLASH_SIZE (1024 * 1024) +#define SRAM_BASE_ADDRESS 0x20000000 +#define SRAM_SIZE (192 * 1024) + +typedef struct STM32F405State { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + + char *cpu_type; + + ARMv7MState armv7m; + + STM32F4xxSyscfgState syscfg; + STM32F4xxExtiState exti; + STM32F2XXUsartState usart[STM_NUM_USARTS]; + STM32F2XXTimerState timer[STM_NUM_TIMERS]; + qemu_or_irq adc_irqs; + STM32F2XXADCState adc[STM_NUM_ADCS]; + STM32F2XXSPIState spi[STM_NUM_SPIS]; + + MemoryRegion sram; + MemoryRegion flash; + MemoryRegion flash_alias; +} STM32F405State; + +#endif diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h index 0b41083e9d..60b2f521eb 100644 --- a/include/hw/arm/virt.h +++ b/include/hw/arm/virt.h @@ -95,6 +95,14 @@ typedef enum VirtIOMMUType { VIRT_IOMMU_VIRTIO, } VirtIOMMUType; +typedef enum VirtGICType { + VIRT_GIC_VERSION_MAX, + VIRT_GIC_VERSION_HOST, + VIRT_GIC_VERSION_2, + VIRT_GIC_VERSION_3, + VIRT_GIC_VERSION_NOSEL, +} VirtGICType; + typedef struct MemMapEntry { hwaddr base; hwaddr size; @@ -109,6 +117,7 @@ typedef struct { bool smbios_old_sys_ver; bool no_highmem_ecam; bool no_ged; /* Machines < 4.2 has no support for ACPI GED device */ + bool kvm_no_adjvtime; } VirtMachineClass; typedef struct { @@ -122,10 +131,13 @@ typedef struct { bool highmem_ecam; bool its; bool virt; - int32_t gic_version; + OnOffAuto acpi; + VirtGICType gic_version; VirtIOMMUType iommu; + uint16_t virtio_iommu_bdf; struct arm_boot_info bootinfo; MemMapEntry *memmap; + char *pciehb_nodename; const int *irqmap; int smp_cpus; void *fdt; @@ -136,6 +148,7 @@ typedef struct { uint32_t iommu_phandle; int psci_conduit; hwaddr highest_gpa; + DeviceState *gic; DeviceState *acpi_dev; Notifier powerdown_notifier; } VirtMachineState; @@ -151,6 +164,7 @@ typedef struct { OBJECT_CLASS_CHECK(VirtMachineClass, klass, TYPE_VIRT_MACHINE) void virt_acpi_setup(VirtMachineState *vms); +bool virt_is_acpi_enabled(VirtMachineState *vms); /* Return the number of used redistributor regions */ static inline int virt_gicv3_redist_region_count(VirtMachineState *vms) @@ -158,7 +172,7 @@ static inline int virt_gicv3_redist_region_count(VirtMachineState *vms) uint32_t redist0_capacity = vms->memmap[VIRT_GIC_REDIST].size / GICV3_REDIST_SIZE; - assert(vms->gic_version == 3); + assert(vms->gic_version == VIRT_GIC_VERSION_3); return vms->smp_cpus > redist0_capacity ? 2 : 1; } diff --git a/include/hw/arm/xlnx-versal.h b/include/hw/arm/xlnx-versal.h index d844c4ffe4..6c0a692b2f 100644 --- a/include/hw/arm/xlnx-versal.h +++ b/include/hw/arm/xlnx-versal.h @@ -22,6 +22,7 @@ #define XLNX_VERSAL_NR_ACPUS 2 #define XLNX_VERSAL_NR_UARTS 2 #define XLNX_VERSAL_NR_GEMS 2 +#define XLNX_VERSAL_NR_ADMAS 8 #define XLNX_VERSAL_NR_IRQS 192 typedef struct Versal { @@ -50,6 +51,7 @@ typedef struct Versal { struct { SysBusDevice *uart[XLNX_VERSAL_NR_UARTS]; SysBusDevice *gem[XLNX_VERSAL_NR_GEMS]; + SysBusDevice *adma[XLNX_VERSAL_NR_ADMAS]; } iou; } lpd; @@ -74,6 +76,7 @@ typedef struct Versal { #define VERSAL_GEM0_WAKE_IRQ_0 57 #define VERSAL_GEM1_IRQ_0 58 #define VERSAL_GEM1_WAKE_IRQ_0 59 +#define VERSAL_ADMA_IRQ_0 60 /* Architecturally reserved IRQs suitable for virtualization. */ #define VERSAL_RSVD_IRQ_FIRST 111 @@ -96,6 +99,9 @@ typedef struct Versal { #define MM_GEM1 0xff0d0000U #define MM_GEM1_SIZE 0x10000 +#define MM_ADMA_CH0 0xffa80000U +#define MM_ADMA_CH0_SIZE 0x10000 + #define MM_OCM 0xfffc0000U #define MM_OCM_SIZE 0x40000 diff --git a/include/hw/boards.h b/include/hw/boards.h index de45087f34..fd4d62b501 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -4,6 +4,7 @@ #define HW_BOARDS_H #include "exec/memory.h" +#include "sysemu/hostmem.h" #include "sysemu/blockdev.h" #include "sysemu/accel.h" #include "qapi/qapi-types-machine.h" @@ -11,38 +12,6 @@ #include "qom/object.h" #include "hw/core/cpu.h" -/** - * memory_region_allocate_system_memory - Allocate a board's main memory - * @mr: the #MemoryRegion to be initialized - * @owner: the object that tracks the region's reference count - * @name: name of the memory region - * @ram_size: size of the region in bytes - * - * This function allocates the main memory for a board model, and - * initializes @mr appropriately. It also arranges for the memory - * to be migrated (by calling vmstate_register_ram_global()). - * - * Memory allocated via this function will be backed with the memory - * backend the user provided using "-mem-path" or "-numa node,memdev=..." - * if appropriate; this is typically used to cause host huge pages to be - * used. This function should therefore be called by a board exactly once, - * for the primary or largest RAM area it implements. - * - * For boards where the major RAM is split into two parts in the memory - * map, you can deal with this by calling memory_region_allocate_system_memory() - * once to get a MemoryRegion with enough RAM for both parts, and then - * creating alias MemoryRegions via memory_region_init_alias() which - * alias into different parts of the RAM MemoryRegion and can be mapped - * into the memory map in the appropriate places. - * - * Smaller pieces of memory (display RAM, static RAMs, etc) don't need - * to be backed via the -mem-path memory backend and can simply - * be created via memory_region_init_ram(). - */ -void memory_region_allocate_system_memory(MemoryRegion *mr, Object *owner, - const char *name, - uint64_t ram_size); - #define TYPE_MACHINE_SUFFIX "-machine" /* Machine class name that needs to be used for class-name-based machine @@ -63,10 +32,6 @@ extern MachineState *current_machine; void machine_run_board_init(MachineState *machine); bool machine_usb(MachineState *machine); -bool machine_kernel_irqchip_allowed(MachineState *machine); -bool machine_kernel_irqchip_required(MachineState *machine); -bool machine_kernel_irqchip_split(MachineState *machine); -int machine_kvm_shadow_mem(MachineState *machine); int machine_phandle_start(MachineState *machine); bool machine_dump_guest_core(MachineState *machine); bool machine_mem_merge(MachineState *machine); @@ -76,7 +41,12 @@ void machine_set_cpu_numa_node(MachineState *machine, Error **errp); void machine_class_allow_dynamic_sysbus_dev(MachineClass *mc, const char *type); - +/* + * Checks that backend isn't used, preps it for exclusive usage and + * returns migratable MemoryRegion provided by backend. + */ +MemoryRegion *machine_consume_memdev(MachineState *machine, + HostMemoryBackend *backend); /** * CPUArchId: @@ -101,7 +71,7 @@ typedef struct CPUArchId { */ typedef struct { int len; - CPUArchId cpus[0]; + CPUArchId cpus[]; } CPUArchIdList; /** @@ -111,6 +81,8 @@ typedef struct { * @max_cpus: maximum number of CPUs supported. Default: 1 * @min_cpus: minimum number of CPUs supported. Default: 1 * @default_cpus: number of CPUs instantiated if none are specified. Default: 1 + * @is_default: + * If true QEMU will use this machine by default if no '-M' option is given. * @get_hotplug_handler: this function is called during bus-less * device hotplug. If defined it returns pointer to an instance * of HotplugHandler object, which handles hotplug operation @@ -173,6 +145,19 @@ typedef struct { * false is returned, an error must be set to show the reason of * the rejection. If the hook is not provided, all hotplug will be * allowed. + * @default_ram_id: + * Specifies inital RAM MemoryRegion name to be used for default backend + * creation if user explicitly hasn't specified backend with "memory-backend" + * property. + * It also will be used as a way to optin into "-m" option support. + * If it's not set by board, '-m' will be ignored and generic code will + * not create default RAM MemoryRegion. + * @fixup_ram_size: + * Amends user provided ram size (with -m option) using machine + * specific algorithm. To be used by old machine types for compat + * purposes only. + * Applies only to default memory backend, i.e., explicit memory backend + * wasn't used. */ struct MachineClass { /*< private >*/ @@ -204,7 +189,7 @@ struct MachineClass { no_sdcard:1, pci_allow_0_address:1, legacy_fw_cfg_order:1; - int is_default; + bool is_default; const char *default_machine_opts; const char *default_boot_order; const char *default_display; @@ -229,6 +214,7 @@ struct MachineClass { bool nvdimm_supported; bool numa_mem_supported; bool auto_enable_numa; + const char *default_ram_id; HotplugHandler *(*get_hotplug_handler)(MachineState *machine, DeviceState *dev); @@ -238,6 +224,7 @@ struct MachineClass { unsigned cpu_index); const CPUArchIdList *(*possible_cpu_arch_ids)(MachineState *machine); int64_t (*get_default_cpu_node_id)(const MachineState *ms, int idx); + ram_addr_t (*fixup_ram_size)(ram_addr_t size); }; /** @@ -256,12 +243,14 @@ typedef struct DeviceMemoryState { * @cpus: the number of present logical processors on the machine * @cores: the number of cores in one package * @threads: the number of threads in one core + * @sockets: the number of sockets on the machine * @max_cpus: the maximum number of logical processors on the machine */ typedef struct CpuTopology { unsigned int cpus; unsigned int cores; unsigned int threads; + unsigned int sockets; unsigned int max_cpus; } CpuTopology; @@ -275,11 +264,6 @@ struct MachineState { /*< public >*/ - char *accel; - bool kernel_irqchip_allowed; - bool kernel_irqchip_required; - bool kernel_irqchip_split; - int kvm_shadow_mem; char *dtb; char *dumpdtb; int phandle_start; @@ -288,13 +272,18 @@ struct MachineState { bool mem_merge; bool usb; bool usb_disabled; - bool igd_gfx_passthru; char *firmware; bool iommu; bool suppress_vmdesc; bool enforce_config_section; bool enable_graphics; char *memory_encryption; + char *ram_memdev_id; + /* + * convenience alias to ram_memdev_id backend memory region + * or to numa container memory region + */ + MemoryRegion *ram; DeviceMemoryState *device_memory; ram_addr_t ram_size; @@ -329,6 +318,9 @@ struct MachineState { } \ type_init(machine_initfn##_register_types) +extern GlobalProperty hw_compat_4_2[]; +extern const size_t hw_compat_4_2_len; + extern GlobalProperty hw_compat_4_1[]; extern const size_t hw_compat_4_1_len; diff --git a/include/hw/bt.h b/include/hw/bt.h deleted file mode 100644 index d9ee2fc29a..0000000000 --- a/include/hw/bt.h +++ /dev/null @@ -1,2177 +0,0 @@ -/* - * QEMU Bluetooth HCI helpers. - * - * Copyright (C) 2007 OpenMoko, Inc. - * Written by Andrzej Zaborowski - * - * Useful definitions taken from BlueZ project's headers. - * Copyright (C) 2000-2001 Qualcomm Incorporated - * Copyright (C) 2002-2003 Maxim Krasnyansky - * Copyright (C) 2002-2006 Marcel Holtmann - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - */ - -#ifndef HW_BT_H -#define HW_BT_H - - -/* BD Address */ -typedef struct { - uint8_t b[6]; -} QEMU_PACKED bdaddr_t; - -#define BDADDR_ANY (&(bdaddr_t) {{0, 0, 0, 0, 0, 0}}) -#define BDADDR_ALL (&(bdaddr_t) {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}) -#define BDADDR_LOCAL (&(bdaddr_t) {{0, 0, 0, 0xff, 0xff, 0xff}}) - -/* Copy, swap, convert BD Address */ -static inline int bacmp(const bdaddr_t *ba1, const bdaddr_t *ba2) -{ - return memcmp(ba1, ba2, sizeof(bdaddr_t)); -} -static inline void bacpy(bdaddr_t *dst, const bdaddr_t *src) -{ - memcpy(dst, src, sizeof(bdaddr_t)); -} - -#define BAINIT(orig) { .b = { \ - (orig)->b[0], (orig)->b[1], (orig)->b[2], \ - (orig)->b[3], (orig)->b[4], (orig)->b[5], \ -}, } - -/* The twisted structures of a bluetooth environment */ -struct bt_device_s; -struct bt_scatternet_s; -struct bt_piconet_s; -struct bt_link_s; - -struct bt_scatternet_s { - struct bt_device_s *slave; -}; - -struct bt_link_s { - struct bt_device_s *slave, *host; - uint16_t handle; /* Master (host) side handle */ - uint16_t acl_interval; - enum { - acl_active, - acl_hold, - acl_sniff, - acl_parked, - } acl_mode; -}; - -struct bt_device_s { - int lt_addr; - bdaddr_t bd_addr; - int mtu; - int setup; - struct bt_scatternet_s *net; - - uint8_t key[16]; - int key_present; - uint8_t class[3]; - - uint8_t reject_reason; - - uint64_t lmp_caps; - const char *lmp_name; - void (*lmp_connection_request)(struct bt_link_s *link); - void (*lmp_connection_complete)(struct bt_link_s *link); - void (*lmp_disconnect_master)(struct bt_link_s *link); - void (*lmp_disconnect_slave)(struct bt_link_s *link); - void (*lmp_acl_data)(struct bt_link_s *link, const uint8_t *data, - int start, int len); - void (*lmp_acl_resp)(struct bt_link_s *link, const uint8_t *data, - int start, int len); - void (*lmp_mode_change)(struct bt_link_s *link); - - void (*handle_destroy)(struct bt_device_s *device); - struct bt_device_s *next; /* Next in the piconet/scatternet */ - - int inquiry_scan; - int page_scan; - - uint16_t clkoff; /* Note: Always little-endian */ -}; - -extern struct HCIInfo null_hci; -/* bt.c */ -void bt_device_init(struct bt_device_s *dev, struct bt_scatternet_s *net); -void bt_device_done(struct bt_device_s *dev); -struct bt_scatternet_s *qemu_find_bt_vlan(int id); - -/* bt-hci.c */ -struct HCIInfo *bt_new_hci(struct bt_scatternet_s *net); -struct HCIInfo *hci_init(const char *str); - -/* bt-vhci.c */ -void bt_vhci_init(struct HCIInfo *info); - -/* bt-hci-csr.c */ -enum { - csrhci_pin_reset, - csrhci_pin_wakeup, - __csrhci_pins, -}; -qemu_irq *csrhci_pins_get(Chardev *chr); -Chardev *uart_hci_init(void); - -/* bt-l2cap.c */ -struct bt_l2cap_device_s; -struct bt_l2cap_conn_params_s; -struct bt_l2cap_psm_s; -void bt_l2cap_device_init(struct bt_l2cap_device_s *dev, - struct bt_scatternet_s *net); -void bt_l2cap_device_done(struct bt_l2cap_device_s *dev); -void bt_l2cap_psm_register(struct bt_l2cap_device_s *dev, int psm, - int min_mtu, int (*new_channel)(struct bt_l2cap_device_s *dev, - struct bt_l2cap_conn_params_s *params)); - -struct bt_l2cap_device_s { - struct bt_device_s device; - struct bt_l2cap_psm_s *first_psm; -}; - -struct bt_l2cap_conn_params_s { - /* Input */ - uint8_t *(*sdu_out)(struct bt_l2cap_conn_params_s *chan, int len); - void (*sdu_submit)(struct bt_l2cap_conn_params_s *chan); - int remote_mtu; - /* Output */ - void *opaque; - void (*sdu_in)(void *opaque, const uint8_t *data, int len); - void (*close)(void *opaque); -}; - -enum bt_l2cap_psm_predef { - BT_PSM_SDP = 0x0001, - BT_PSM_RFCOMM = 0x0003, - BT_PSM_TELEPHONY = 0x0005, - BT_PSM_TCS = 0x0007, - BT_PSM_BNEP = 0x000f, - BT_PSM_HID_CTRL = 0x0011, - BT_PSM_HID_INTR = 0x0013, - BT_PSM_UPNP = 0x0015, - BT_PSM_AVCTP = 0x0017, - BT_PSM_AVDTP = 0x0019, -}; - -/* bt-sdp.c */ -void bt_l2cap_sdp_init(struct bt_l2cap_device_s *dev); - -/* bt-hid.c */ -struct bt_device_s *bt_keyboard_init(struct bt_scatternet_s *net); - -/* Link Management Protocol layer defines */ - -#define LLID_ACLU_CONT 0x1 -#define LLID_ACLU_START 0x2 -#define LLID_ACLC 0x3 - -enum lmp_pdu_type { - LMP_NAME_REQ = 0x0001, - LMP_NAME_RES = 0x0002, - LMP_ACCEPTED = 0x0003, - LMP_NOT_ACCEPTED = 0x0004, - LMP_CLKOFFSET_REQ = 0x0005, - LMP_CLKOFFSET_RES = 0x0006, - LMP_DETACH = 0x0007, - LMP_IN_RAND = 0x0008, - LMP_COMB_KEY = 0x0009, - LMP_UNIT_KEY = 0x000a, - LMP_AU_RAND = 0x000b, - LMP_SRES = 0x000c, - LMP_TEMP_RAND = 0x000d, - LMP_TEMP_KEY = 0x000e, - LMP_CRYPT_MODE_REQ = 0x000f, - LMP_CRYPT_KEY_SIZE_REQ = 0x0010, - LMP_START_ENCRYPT_REQ = 0x0011, - LMP_STOP_ENCRYPT_REQ = 0x0012, - LMP_SWITCH_REQ = 0x0013, - LMP_HOLD = 0x0014, - LMP_HOLD_REQ = 0x0015, - LMP_SNIFF_REQ = 0x0017, - LMP_UNSNIFF_REQ = 0x0018, - LMP_LMP_PARK_REQ = 0x0019, - LMP_SET_BCAST_SCAN_WND = 0x001b, - LMP_MODIFY_BEACON = 0x001c, - LMP_UNPARK_BD_ADDR_REQ = 0x001d, - LMP_UNPARK_PM_ADDR_REQ = 0x001e, - LMP_INCR_POWER_REQ = 0x001f, - LMP_DECR_POWER_REQ = 0x0020, - LMP_MAX_POWER = 0x0021, - LMP_MIN_POWER = 0x0022, - LMP_AUTO_RATE = 0x0023, - LMP_PREFERRED_RATE = 0x0024, - LMP_VERSION_REQ = 0x0025, - LMP_VERSION_RES = 0x0026, - LMP_FEATURES_REQ = 0x0027, - LMP_FEATURES_RES = 0x0028, - LMP_QUALITY_OF_SERVICE = 0x0029, - LMP_QOS_REQ = 0x002a, - LMP_RM_SCO_LINK_REQ = 0x002b, - LMP_SCO_LINK_REQ = 0x002c, - LMP_MAX_SLOT = 0x002d, - LMP_MAX_SLOT_REQ = 0x002e, - LMP_TIMING_ACCURACY_REQ = 0x002f, - LMP_TIMING_ACCURACY_RES = 0x0030, - LMP_SETUP_COMPLETE = 0x0031, - LMP_USE_SEMIPERM_KEY = 0x0032, - LMP_HOST_CONNECTION_REQ = 0x0033, - LMP_SLOT_OFFSET = 0x0034, - LMP_PAGE_MODE_REQ = 0x0035, - LMP_PAGE_SCAN_MODE_REQ = 0x0036, - LMP_SUPERVISION_TIMEOUT = 0x0037, - LMP_TEST_ACTIVATE = 0x0038, - LMP_TEST_CONTROL = 0x0039, - LMP_CRYPT_KEY_MASK_REQ = 0x003a, - LMP_CRYPT_KEY_MASK_RES = 0x003b, - LMP_SET_AFH = 0x003c, - LMP_ACCEPTED_EXT = 0x7f01, - LMP_NOT_ACCEPTED_EXT = 0x7f02, - LMP_FEATURES_REQ_EXT = 0x7f03, - LMP_FEATURES_RES_EXT = 0x7f04, - LMP_PACKET_TYPE_TBL_REQ = 0x7f0b, - LMP_ESCO_LINK_REQ = 0x7f0c, - LMP_RM_ESCO_LINK_REQ = 0x7f0d, - LMP_CHANNEL_CLASS_REQ = 0x7f10, - LMP_CHANNEL_CLASS = 0x7f11, -}; - -/* Host Controller Interface layer defines */ - -enum hci_packet_type { - HCI_COMMAND_PKT = 0x01, - HCI_ACLDATA_PKT = 0x02, - HCI_SCODATA_PKT = 0x03, - HCI_EVENT_PKT = 0x04, - HCI_VENDOR_PKT = 0xff, -}; - -enum bt_packet_type { - HCI_2DH1 = 1 << 1, - HCI_3DH1 = 1 << 2, - HCI_DM1 = 1 << 3, - HCI_DH1 = 1 << 4, - HCI_2DH3 = 1 << 8, - HCI_3DH3 = 1 << 9, - HCI_DM3 = 1 << 10, - HCI_DH3 = 1 << 11, - HCI_2DH5 = 1 << 12, - HCI_3DH5 = 1 << 13, - HCI_DM5 = 1 << 14, - HCI_DH5 = 1 << 15, -}; - -enum sco_packet_type { - HCI_HV1 = 1 << 5, - HCI_HV2 = 1 << 6, - HCI_HV3 = 1 << 7, -}; - -enum ev_packet_type { - HCI_EV3 = 1 << 3, - HCI_EV4 = 1 << 4, - HCI_EV5 = 1 << 5, - HCI_2EV3 = 1 << 6, - HCI_3EV3 = 1 << 7, - HCI_2EV5 = 1 << 8, - HCI_3EV5 = 1 << 9, -}; - -enum hci_error_code { - HCI_SUCCESS = 0x00, - HCI_UNKNOWN_COMMAND = 0x01, - HCI_NO_CONNECTION = 0x02, - HCI_HARDWARE_FAILURE = 0x03, - HCI_PAGE_TIMEOUT = 0x04, - HCI_AUTHENTICATION_FAILURE = 0x05, - HCI_PIN_OR_KEY_MISSING = 0x06, - HCI_MEMORY_FULL = 0x07, - HCI_CONNECTION_TIMEOUT = 0x08, - HCI_MAX_NUMBER_OF_CONNECTIONS = 0x09, - HCI_MAX_NUMBER_OF_SCO_CONNECTIONS = 0x0a, - HCI_ACL_CONNECTION_EXISTS = 0x0b, - HCI_COMMAND_DISALLOWED = 0x0c, - HCI_REJECTED_LIMITED_RESOURCES = 0x0d, - HCI_REJECTED_SECURITY = 0x0e, - HCI_REJECTED_PERSONAL = 0x0f, - HCI_HOST_TIMEOUT = 0x10, - HCI_UNSUPPORTED_FEATURE = 0x11, - HCI_INVALID_PARAMETERS = 0x12, - HCI_OE_USER_ENDED_CONNECTION = 0x13, - HCI_OE_LOW_RESOURCES = 0x14, - HCI_OE_POWER_OFF = 0x15, - HCI_CONNECTION_TERMINATED = 0x16, - HCI_REPEATED_ATTEMPTS = 0x17, - HCI_PAIRING_NOT_ALLOWED = 0x18, - HCI_UNKNOWN_LMP_PDU = 0x19, - HCI_UNSUPPORTED_REMOTE_FEATURE = 0x1a, - HCI_SCO_OFFSET_REJECTED = 0x1b, - HCI_SCO_INTERVAL_REJECTED = 0x1c, - HCI_AIR_MODE_REJECTED = 0x1d, - HCI_INVALID_LMP_PARAMETERS = 0x1e, - HCI_UNSPECIFIED_ERROR = 0x1f, - HCI_UNSUPPORTED_LMP_PARAMETER_VALUE = 0x20, - HCI_ROLE_CHANGE_NOT_ALLOWED = 0x21, - HCI_LMP_RESPONSE_TIMEOUT = 0x22, - HCI_LMP_ERROR_TRANSACTION_COLLISION = 0x23, - HCI_LMP_PDU_NOT_ALLOWED = 0x24, - HCI_ENCRYPTION_MODE_NOT_ACCEPTED = 0x25, - HCI_UNIT_LINK_KEY_USED = 0x26, - HCI_QOS_NOT_SUPPORTED = 0x27, - HCI_INSTANT_PASSED = 0x28, - HCI_PAIRING_NOT_SUPPORTED = 0x29, - HCI_TRANSACTION_COLLISION = 0x2a, - HCI_QOS_UNACCEPTABLE_PARAMETER = 0x2c, - HCI_QOS_REJECTED = 0x2d, - HCI_CLASSIFICATION_NOT_SUPPORTED = 0x2e, - HCI_INSUFFICIENT_SECURITY = 0x2f, - HCI_PARAMETER_OUT_OF_RANGE = 0x30, - HCI_ROLE_SWITCH_PENDING = 0x32, - HCI_SLOT_VIOLATION = 0x34, - HCI_ROLE_SWITCH_FAILED = 0x35, -}; - -enum acl_flag_bits { - ACL_CONT = 1 << 0, - ACL_START = 1 << 1, - ACL_ACTIVE_BCAST = 1 << 2, - ACL_PICO_BCAST = 1 << 3, -}; - -enum baseband_link_type { - SCO_LINK = 0x00, - ACL_LINK = 0x01, -}; - -enum lmp_feature_bits0 { - LMP_3SLOT = 1 << 0, - LMP_5SLOT = 1 << 1, - LMP_ENCRYPT = 1 << 2, - LMP_SOFFSET = 1 << 3, - LMP_TACCURACY = 1 << 4, - LMP_RSWITCH = 1 << 5, - LMP_HOLD_MODE = 1 << 6, - LMP_SNIFF_MODE = 1 << 7, -}; - -enum lmp_feature_bits1 { - LMP_PARK = 1 << 0, - LMP_RSSI = 1 << 1, - LMP_QUALITY = 1 << 2, - LMP_SCO = 1 << 3, - LMP_HV2 = 1 << 4, - LMP_HV3 = 1 << 5, - LMP_ULAW = 1 << 6, - LMP_ALAW = 1 << 7, -}; - -enum lmp_feature_bits2 { - LMP_CVSD = 1 << 0, - LMP_PSCHEME = 1 << 1, - LMP_PCONTROL = 1 << 2, - LMP_TRSP_SCO = 1 << 3, - LMP_BCAST_ENC = 1 << 7, -}; - -enum lmp_feature_bits3 { - LMP_EDR_ACL_2M = 1 << 1, - LMP_EDR_ACL_3M = 1 << 2, - LMP_ENH_ISCAN = 1 << 3, - LMP_ILACE_ISCAN = 1 << 4, - LMP_ILACE_PSCAN = 1 << 5, - LMP_RSSI_INQ = 1 << 6, - LMP_ESCO = 1 << 7, -}; - -enum lmp_feature_bits4 { - LMP_EV4 = 1 << 0, - LMP_EV5 = 1 << 1, - LMP_AFH_CAP_SLV = 1 << 3, - LMP_AFH_CLS_SLV = 1 << 4, - LMP_EDR_3SLOT = 1 << 7, -}; - -enum lmp_feature_bits5 { - LMP_EDR_5SLOT = 1 << 0, - LMP_SNIFF_SUBR = 1 << 1, - LMP_AFH_CAP_MST = 1 << 3, - LMP_AFH_CLS_MST = 1 << 4, - LMP_EDR_ESCO_2M = 1 << 5, - LMP_EDR_ESCO_3M = 1 << 6, - LMP_EDR_3S_ESCO = 1 << 7, -}; - -enum lmp_feature_bits6 { - LMP_EXT_INQ = 1 << 0, -}; - -enum lmp_feature_bits7 { - LMP_EXT_FEAT = 1 << 7, -}; - -enum hci_link_policy { - HCI_LP_RSWITCH = 1 << 0, - HCI_LP_HOLD = 1 << 1, - HCI_LP_SNIFF = 1 << 2, - HCI_LP_PARK = 1 << 3, -}; - -enum hci_link_mode { - HCI_LM_ACCEPT = 1 << 15, - HCI_LM_MASTER = 1 << 0, - HCI_LM_AUTH = 1 << 1, - HCI_LM_ENCRYPT = 1 << 2, - HCI_LM_TRUSTED = 1 << 3, - HCI_LM_RELIABLE = 1 << 4, - HCI_LM_SECURE = 1 << 5, -}; - -/* HCI Commands */ - -/* Link Control */ -#define OGF_LINK_CTL 0x01 - -#define OCF_INQUIRY 0x0001 -typedef struct { - uint8_t lap[3]; - uint8_t length; /* 1.28s units */ - uint8_t num_rsp; -} QEMU_PACKED inquiry_cp; -#define INQUIRY_CP_SIZE 5 - -typedef struct { - uint8_t status; - bdaddr_t bdaddr; -} QEMU_PACKED status_bdaddr_rp; -#define STATUS_BDADDR_RP_SIZE 7 - -#define OCF_INQUIRY_CANCEL 0x0002 - -#define OCF_PERIODIC_INQUIRY 0x0003 -typedef struct { - uint16_t max_period; /* 1.28s units */ - uint16_t min_period; /* 1.28s units */ - uint8_t lap[3]; - uint8_t length; /* 1.28s units */ - uint8_t num_rsp; -} QEMU_PACKED periodic_inquiry_cp; -#define PERIODIC_INQUIRY_CP_SIZE 9 - -#define OCF_EXIT_PERIODIC_INQUIRY 0x0004 - -#define OCF_CREATE_CONN 0x0005 -typedef struct { - bdaddr_t bdaddr; - uint16_t pkt_type; - uint8_t pscan_rep_mode; - uint8_t pscan_mode; - uint16_t clock_offset; - uint8_t role_switch; -} QEMU_PACKED create_conn_cp; -#define CREATE_CONN_CP_SIZE 13 - -#define OCF_DISCONNECT 0x0006 -typedef struct { - uint16_t handle; - uint8_t reason; -} QEMU_PACKED disconnect_cp; -#define DISCONNECT_CP_SIZE 3 - -#define OCF_ADD_SCO 0x0007 -typedef struct { - uint16_t handle; - uint16_t pkt_type; -} QEMU_PACKED add_sco_cp; -#define ADD_SCO_CP_SIZE 4 - -#define OCF_CREATE_CONN_CANCEL 0x0008 -typedef struct { - bdaddr_t bdaddr; -} QEMU_PACKED create_conn_cancel_cp; -#define CREATE_CONN_CANCEL_CP_SIZE 6 - -typedef struct { - uint8_t status; - bdaddr_t bdaddr; -} QEMU_PACKED create_conn_cancel_rp; -#define CREATE_CONN_CANCEL_RP_SIZE 7 - -#define OCF_ACCEPT_CONN_REQ 0x0009 -typedef struct { - bdaddr_t bdaddr; - uint8_t role; -} QEMU_PACKED accept_conn_req_cp; -#define ACCEPT_CONN_REQ_CP_SIZE 7 - -#define OCF_REJECT_CONN_REQ 0x000A -typedef struct { - bdaddr_t bdaddr; - uint8_t reason; -} QEMU_PACKED reject_conn_req_cp; -#define REJECT_CONN_REQ_CP_SIZE 7 - -#define OCF_LINK_KEY_REPLY 0x000B -typedef struct { - bdaddr_t bdaddr; - uint8_t link_key[16]; -} QEMU_PACKED link_key_reply_cp; -#define LINK_KEY_REPLY_CP_SIZE 22 - -#define OCF_LINK_KEY_NEG_REPLY 0x000C - -#define OCF_PIN_CODE_REPLY 0x000D -typedef struct { - bdaddr_t bdaddr; - uint8_t pin_len; - uint8_t pin_code[16]; -} QEMU_PACKED pin_code_reply_cp; -#define PIN_CODE_REPLY_CP_SIZE 23 - -#define OCF_PIN_CODE_NEG_REPLY 0x000E - -#define OCF_SET_CONN_PTYPE 0x000F -typedef struct { - uint16_t handle; - uint16_t pkt_type; -} QEMU_PACKED set_conn_ptype_cp; -#define SET_CONN_PTYPE_CP_SIZE 4 - -#define OCF_AUTH_REQUESTED 0x0011 -typedef struct { - uint16_t handle; -} QEMU_PACKED auth_requested_cp; -#define AUTH_REQUESTED_CP_SIZE 2 - -#define OCF_SET_CONN_ENCRYPT 0x0013 -typedef struct { - uint16_t handle; - uint8_t encrypt; -} QEMU_PACKED set_conn_encrypt_cp; -#define SET_CONN_ENCRYPT_CP_SIZE 3 - -#define OCF_CHANGE_CONN_LINK_KEY 0x0015 -typedef struct { - uint16_t handle; -} QEMU_PACKED change_conn_link_key_cp; -#define CHANGE_CONN_LINK_KEY_CP_SIZE 2 - -#define OCF_MASTER_LINK_KEY 0x0017 -typedef struct { - uint8_t key_flag; -} QEMU_PACKED master_link_key_cp; -#define MASTER_LINK_KEY_CP_SIZE 1 - -#define OCF_REMOTE_NAME_REQ 0x0019 -typedef struct { - bdaddr_t bdaddr; - uint8_t pscan_rep_mode; - uint8_t pscan_mode; - uint16_t clock_offset; -} QEMU_PACKED remote_name_req_cp; -#define REMOTE_NAME_REQ_CP_SIZE 10 - -#define OCF_REMOTE_NAME_REQ_CANCEL 0x001A -typedef struct { - bdaddr_t bdaddr; -} QEMU_PACKED remote_name_req_cancel_cp; -#define REMOTE_NAME_REQ_CANCEL_CP_SIZE 6 - -typedef struct { - uint8_t status; - bdaddr_t bdaddr; -} QEMU_PACKED remote_name_req_cancel_rp; -#define REMOTE_NAME_REQ_CANCEL_RP_SIZE 7 - -#define OCF_READ_REMOTE_FEATURES 0x001B -typedef struct { - uint16_t handle; -} QEMU_PACKED read_remote_features_cp; -#define READ_REMOTE_FEATURES_CP_SIZE 2 - -#define OCF_READ_REMOTE_EXT_FEATURES 0x001C -typedef struct { - uint16_t handle; - uint8_t page_num; -} QEMU_PACKED read_remote_ext_features_cp; -#define READ_REMOTE_EXT_FEATURES_CP_SIZE 3 - -#define OCF_READ_REMOTE_VERSION 0x001D -typedef struct { - uint16_t handle; -} QEMU_PACKED read_remote_version_cp; -#define READ_REMOTE_VERSION_CP_SIZE 2 - -#define OCF_READ_CLOCK_OFFSET 0x001F -typedef struct { - uint16_t handle; -} QEMU_PACKED read_clock_offset_cp; -#define READ_CLOCK_OFFSET_CP_SIZE 2 - -#define OCF_READ_LMP_HANDLE 0x0020 -typedef struct { - uint16_t handle; -} QEMU_PACKED read_lmp_handle_cp; -#define READ_LMP_HANDLE_CP_SIZE 2 - -typedef struct { - uint8_t status; - uint16_t handle; - uint8_t lmp_handle; - uint32_t reserved; -} QEMU_PACKED read_lmp_handle_rp; -#define READ_LMP_HANDLE_RP_SIZE 8 - -#define OCF_SETUP_SYNC_CONN 0x0028 -typedef struct { - uint16_t handle; - uint32_t tx_bandwidth; - uint32_t rx_bandwidth; - uint16_t max_latency; - uint16_t voice_setting; - uint8_t retrans_effort; - uint16_t pkt_type; -} QEMU_PACKED setup_sync_conn_cp; -#define SETUP_SYNC_CONN_CP_SIZE 17 - -#define OCF_ACCEPT_SYNC_CONN_REQ 0x0029 -typedef struct { - bdaddr_t bdaddr; - uint32_t tx_bandwidth; - uint32_t rx_bandwidth; - uint16_t max_latency; - uint16_t voice_setting; - uint8_t retrans_effort; - uint16_t pkt_type; -} QEMU_PACKED accept_sync_conn_req_cp; -#define ACCEPT_SYNC_CONN_REQ_CP_SIZE 21 - -#define OCF_REJECT_SYNC_CONN_REQ 0x002A -typedef struct { - bdaddr_t bdaddr; - uint8_t reason; -} QEMU_PACKED reject_sync_conn_req_cp; -#define REJECT_SYNC_CONN_REQ_CP_SIZE 7 - -/* Link Policy */ -#define OGF_LINK_POLICY 0x02 - -#define OCF_HOLD_MODE 0x0001 -typedef struct { - uint16_t handle; - uint16_t max_interval; - uint16_t min_interval; -} QEMU_PACKED hold_mode_cp; -#define HOLD_MODE_CP_SIZE 6 - -#define OCF_SNIFF_MODE 0x0003 -typedef struct { - uint16_t handle; - uint16_t max_interval; - uint16_t min_interval; - uint16_t attempt; - uint16_t timeout; -} QEMU_PACKED sniff_mode_cp; -#define SNIFF_MODE_CP_SIZE 10 - -#define OCF_EXIT_SNIFF_MODE 0x0004 -typedef struct { - uint16_t handle; -} QEMU_PACKED exit_sniff_mode_cp; -#define EXIT_SNIFF_MODE_CP_SIZE 2 - -#define OCF_PARK_MODE 0x0005 -typedef struct { - uint16_t handle; - uint16_t max_interval; - uint16_t min_interval; -} QEMU_PACKED park_mode_cp; -#define PARK_MODE_CP_SIZE 6 - -#define OCF_EXIT_PARK_MODE 0x0006 -typedef struct { - uint16_t handle; -} QEMU_PACKED exit_park_mode_cp; -#define EXIT_PARK_MODE_CP_SIZE 2 - -#define OCF_QOS_SETUP 0x0007 -typedef struct { - uint8_t service_type; /* 1 = best effort */ - uint32_t token_rate; /* Byte per seconds */ - uint32_t peak_bandwidth; /* Byte per seconds */ - uint32_t latency; /* Microseconds */ - uint32_t delay_variation; /* Microseconds */ -} QEMU_PACKED hci_qos; -#define HCI_QOS_CP_SIZE 17 -typedef struct { - uint16_t handle; - uint8_t flags; /* Reserved */ - hci_qos qos; -} QEMU_PACKED qos_setup_cp; -#define QOS_SETUP_CP_SIZE (3 + HCI_QOS_CP_SIZE) - -#define OCF_ROLE_DISCOVERY 0x0009 -typedef struct { - uint16_t handle; -} QEMU_PACKED role_discovery_cp; -#define ROLE_DISCOVERY_CP_SIZE 2 -typedef struct { - uint8_t status; - uint16_t handle; - uint8_t role; -} QEMU_PACKED role_discovery_rp; -#define ROLE_DISCOVERY_RP_SIZE 4 - -#define OCF_SWITCH_ROLE 0x000B -typedef struct { - bdaddr_t bdaddr; - uint8_t role; -} QEMU_PACKED switch_role_cp; -#define SWITCH_ROLE_CP_SIZE 7 - -#define OCF_READ_LINK_POLICY 0x000C -typedef struct { - uint16_t handle; -} QEMU_PACKED read_link_policy_cp; -#define READ_LINK_POLICY_CP_SIZE 2 -typedef struct { - uint8_t status; - uint16_t handle; - uint16_t policy; -} QEMU_PACKED read_link_policy_rp; -#define READ_LINK_POLICY_RP_SIZE 5 - -#define OCF_WRITE_LINK_POLICY 0x000D -typedef struct { - uint16_t handle; - uint16_t policy; -} QEMU_PACKED write_link_policy_cp; -#define WRITE_LINK_POLICY_CP_SIZE 4 -typedef struct { - uint8_t status; - uint16_t handle; -} QEMU_PACKED write_link_policy_rp; -#define WRITE_LINK_POLICY_RP_SIZE 3 - -#define OCF_READ_DEFAULT_LINK_POLICY 0x000E - -#define OCF_WRITE_DEFAULT_LINK_POLICY 0x000F - -#define OCF_FLOW_SPECIFICATION 0x0010 - -#define OCF_SNIFF_SUBRATE 0x0011 -typedef struct { - uint16_t handle; - uint16_t max_remote_latency; - uint16_t max_local_latency; - uint16_t min_remote_timeout; - uint16_t min_local_timeout; -} QEMU_PACKED sniff_subrate_cp; -#define SNIFF_SUBRATE_CP_SIZE 10 - -/* Host Controller and Baseband */ -#define OGF_HOST_CTL 0x03 - -#define OCF_SET_EVENT_MASK 0x0001 -typedef struct { - uint8_t mask[8]; -} QEMU_PACKED set_event_mask_cp; -#define SET_EVENT_MASK_CP_SIZE 8 - -#define OCF_RESET 0x0003 - -#define OCF_SET_EVENT_FLT 0x0005 -typedef struct { - uint8_t flt_type; - uint8_t cond_type; - uint8_t condition[0]; -} QEMU_PACKED set_event_flt_cp; -#define SET_EVENT_FLT_CP_SIZE 2 - -enum bt_filter_type { - FLT_CLEAR_ALL = 0x00, - FLT_INQ_RESULT = 0x01, - FLT_CONN_SETUP = 0x02, -}; -enum inq_result_cond_type { - INQ_RESULT_RETURN_ALL = 0x00, - INQ_RESULT_RETURN_CLASS = 0x01, - INQ_RESULT_RETURN_BDADDR = 0x02, -}; -enum conn_setup_cond_type { - CONN_SETUP_ALLOW_ALL = 0x00, - CONN_SETUP_ALLOW_CLASS = 0x01, - CONN_SETUP_ALLOW_BDADDR = 0x02, -}; -enum conn_setup_cond { - CONN_SETUP_AUTO_OFF = 0x01, - CONN_SETUP_AUTO_ON = 0x02, -}; - -#define OCF_FLUSH 0x0008 -typedef struct { - uint16_t handle; -} QEMU_PACKED flush_cp; -#define FLUSH_CP_SIZE 2 - -typedef struct { - uint8_t status; - uint16_t handle; -} QEMU_PACKED flush_rp; -#define FLUSH_RP_SIZE 3 - -#define OCF_READ_PIN_TYPE 0x0009 -typedef struct { - uint8_t status; - uint8_t pin_type; -} QEMU_PACKED read_pin_type_rp; -#define READ_PIN_TYPE_RP_SIZE 2 - -#define OCF_WRITE_PIN_TYPE 0x000A -typedef struct { - uint8_t pin_type; -} QEMU_PACKED write_pin_type_cp; -#define WRITE_PIN_TYPE_CP_SIZE 1 - -#define OCF_CREATE_NEW_UNIT_KEY 0x000B - -#define OCF_READ_STORED_LINK_KEY 0x000D -typedef struct { - bdaddr_t bdaddr; - uint8_t read_all; -} QEMU_PACKED read_stored_link_key_cp; -#define READ_STORED_LINK_KEY_CP_SIZE 7 -typedef struct { - uint8_t status; - uint16_t max_keys; - uint16_t num_keys; -} QEMU_PACKED read_stored_link_key_rp; -#define READ_STORED_LINK_KEY_RP_SIZE 5 - -#define OCF_WRITE_STORED_LINK_KEY 0x0011 -typedef struct { - uint8_t num_keys; - /* variable length part */ -} QEMU_PACKED write_stored_link_key_cp; -#define WRITE_STORED_LINK_KEY_CP_SIZE 1 -typedef struct { - uint8_t status; - uint8_t num_keys; -} QEMU_PACKED write_stored_link_key_rp; -#define READ_WRITE_LINK_KEY_RP_SIZE 2 - -#define OCF_DELETE_STORED_LINK_KEY 0x0012 -typedef struct { - bdaddr_t bdaddr; - uint8_t delete_all; -} QEMU_PACKED delete_stored_link_key_cp; -#define DELETE_STORED_LINK_KEY_CP_SIZE 7 -typedef struct { - uint8_t status; - uint16_t num_keys; -} QEMU_PACKED delete_stored_link_key_rp; -#define DELETE_STORED_LINK_KEY_RP_SIZE 3 - -#define OCF_CHANGE_LOCAL_NAME 0x0013 -typedef struct { - char name[248]; -} QEMU_PACKED change_local_name_cp; -#define CHANGE_LOCAL_NAME_CP_SIZE 248 - -#define OCF_READ_LOCAL_NAME 0x0014 -typedef struct { - uint8_t status; - char name[248]; -} QEMU_PACKED read_local_name_rp; -#define READ_LOCAL_NAME_RP_SIZE 249 - -#define OCF_READ_CONN_ACCEPT_TIMEOUT 0x0015 -typedef struct { - uint8_t status; - uint16_t timeout; -} QEMU_PACKED read_conn_accept_timeout_rp; -#define READ_CONN_ACCEPT_TIMEOUT_RP_SIZE 3 - -#define OCF_WRITE_CONN_ACCEPT_TIMEOUT 0x0016 -typedef struct { - uint16_t timeout; -} QEMU_PACKED write_conn_accept_timeout_cp; -#define WRITE_CONN_ACCEPT_TIMEOUT_CP_SIZE 2 - -#define OCF_READ_PAGE_TIMEOUT 0x0017 -typedef struct { - uint8_t status; - uint16_t timeout; -} QEMU_PACKED read_page_timeout_rp; -#define READ_PAGE_TIMEOUT_RP_SIZE 3 - -#define OCF_WRITE_PAGE_TIMEOUT 0x0018 -typedef struct { - uint16_t timeout; -} QEMU_PACKED write_page_timeout_cp; -#define WRITE_PAGE_TIMEOUT_CP_SIZE 2 - -#define OCF_READ_SCAN_ENABLE 0x0019 -typedef struct { - uint8_t status; - uint8_t enable; -} QEMU_PACKED read_scan_enable_rp; -#define READ_SCAN_ENABLE_RP_SIZE 2 - -#define OCF_WRITE_SCAN_ENABLE 0x001A -typedef struct { - uint8_t scan_enable; -} QEMU_PACKED write_scan_enable_cp; -#define WRITE_SCAN_ENABLE_CP_SIZE 1 - -enum scan_enable_bits { - SCAN_DISABLED = 0, - SCAN_INQUIRY = 1 << 0, - SCAN_PAGE = 1 << 1, -}; - -#define OCF_READ_PAGE_ACTIVITY 0x001B -typedef struct { - uint8_t status; - uint16_t interval; - uint16_t window; -} QEMU_PACKED read_page_activity_rp; -#define READ_PAGE_ACTIVITY_RP_SIZE 5 - -#define OCF_WRITE_PAGE_ACTIVITY 0x001C -typedef struct { - uint16_t interval; - uint16_t window; -} QEMU_PACKED write_page_activity_cp; -#define WRITE_PAGE_ACTIVITY_CP_SIZE 4 - -#define OCF_READ_INQ_ACTIVITY 0x001D -typedef struct { - uint8_t status; - uint16_t interval; - uint16_t window; -} QEMU_PACKED read_inq_activity_rp; -#define READ_INQ_ACTIVITY_RP_SIZE 5 - -#define OCF_WRITE_INQ_ACTIVITY 0x001E -typedef struct { - uint16_t interval; - uint16_t window; -} QEMU_PACKED write_inq_activity_cp; -#define WRITE_INQ_ACTIVITY_CP_SIZE 4 - -#define OCF_READ_AUTH_ENABLE 0x001F - -#define OCF_WRITE_AUTH_ENABLE 0x0020 - -#define AUTH_DISABLED 0x00 -#define AUTH_ENABLED 0x01 - -#define OCF_READ_ENCRYPT_MODE 0x0021 - -#define OCF_WRITE_ENCRYPT_MODE 0x0022 - -#define ENCRYPT_DISABLED 0x00 -#define ENCRYPT_P2P 0x01 -#define ENCRYPT_BOTH 0x02 - -#define OCF_READ_CLASS_OF_DEV 0x0023 -typedef struct { - uint8_t status; - uint8_t dev_class[3]; -} QEMU_PACKED read_class_of_dev_rp; -#define READ_CLASS_OF_DEV_RP_SIZE 4 - -#define OCF_WRITE_CLASS_OF_DEV 0x0024 -typedef struct { - uint8_t dev_class[3]; -} QEMU_PACKED write_class_of_dev_cp; -#define WRITE_CLASS_OF_DEV_CP_SIZE 3 - -#define OCF_READ_VOICE_SETTING 0x0025 -typedef struct { - uint8_t status; - uint16_t voice_setting; -} QEMU_PACKED read_voice_setting_rp; -#define READ_VOICE_SETTING_RP_SIZE 3 - -#define OCF_WRITE_VOICE_SETTING 0x0026 -typedef struct { - uint16_t voice_setting; -} QEMU_PACKED write_voice_setting_cp; -#define WRITE_VOICE_SETTING_CP_SIZE 2 - -#define OCF_READ_AUTOMATIC_FLUSH_TIMEOUT 0x0027 - -#define OCF_WRITE_AUTOMATIC_FLUSH_TIMEOUT 0x0028 - -#define OCF_READ_NUM_BROADCAST_RETRANS 0x0029 - -#define OCF_WRITE_NUM_BROADCAST_RETRANS 0x002A - -#define OCF_READ_HOLD_MODE_ACTIVITY 0x002B - -#define OCF_WRITE_HOLD_MODE_ACTIVITY 0x002C - -#define OCF_READ_TRANSMIT_POWER_LEVEL 0x002D -typedef struct { - uint16_t handle; - uint8_t type; -} QEMU_PACKED read_transmit_power_level_cp; -#define READ_TRANSMIT_POWER_LEVEL_CP_SIZE 3 -typedef struct { - uint8_t status; - uint16_t handle; - int8_t level; -} QEMU_PACKED read_transmit_power_level_rp; -#define READ_TRANSMIT_POWER_LEVEL_RP_SIZE 4 - -#define OCF_HOST_BUFFER_SIZE 0x0033 -typedef struct { - uint16_t acl_mtu; - uint8_t sco_mtu; - uint16_t acl_max_pkt; - uint16_t sco_max_pkt; -} QEMU_PACKED host_buffer_size_cp; -#define HOST_BUFFER_SIZE_CP_SIZE 7 - -#define OCF_HOST_NUMBER_OF_COMPLETED_PACKETS 0x0035 - -#define OCF_READ_LINK_SUPERVISION_TIMEOUT 0x0036 -typedef struct { - uint8_t status; - uint16_t handle; - uint16_t link_sup_to; -} QEMU_PACKED read_link_supervision_timeout_rp; -#define READ_LINK_SUPERVISION_TIMEOUT_RP_SIZE 5 - -#define OCF_WRITE_LINK_SUPERVISION_TIMEOUT 0x0037 -typedef struct { - uint16_t handle; - uint16_t link_sup_to; -} QEMU_PACKED write_link_supervision_timeout_cp; -#define WRITE_LINK_SUPERVISION_TIMEOUT_CP_SIZE 4 -typedef struct { - uint8_t status; - uint16_t handle; -} QEMU_PACKED write_link_supervision_timeout_rp; -#define WRITE_LINK_SUPERVISION_TIMEOUT_RP_SIZE 3 - -#define OCF_READ_NUM_SUPPORTED_IAC 0x0038 - -#define MAX_IAC_LAP 0x40 -#define OCF_READ_CURRENT_IAC_LAP 0x0039 -typedef struct { - uint8_t status; - uint8_t num_current_iac; - uint8_t lap[MAX_IAC_LAP][3]; -} QEMU_PACKED read_current_iac_lap_rp; -#define READ_CURRENT_IAC_LAP_RP_SIZE 2+3*MAX_IAC_LAP - -#define OCF_WRITE_CURRENT_IAC_LAP 0x003A -typedef struct { - uint8_t num_current_iac; - uint8_t lap[MAX_IAC_LAP][3]; -} QEMU_PACKED write_current_iac_lap_cp; -#define WRITE_CURRENT_IAC_LAP_CP_SIZE 1+3*MAX_IAC_LAP - -#define OCF_READ_PAGE_SCAN_PERIOD_MODE 0x003B - -#define OCF_WRITE_PAGE_SCAN_PERIOD_MODE 0x003C - -#define OCF_READ_PAGE_SCAN_MODE 0x003D - -#define OCF_WRITE_PAGE_SCAN_MODE 0x003E - -#define OCF_SET_AFH_CLASSIFICATION 0x003F -typedef struct { - uint8_t map[10]; -} QEMU_PACKED set_afh_classification_cp; -#define SET_AFH_CLASSIFICATION_CP_SIZE 10 -typedef struct { - uint8_t status; -} QEMU_PACKED set_afh_classification_rp; -#define SET_AFH_CLASSIFICATION_RP_SIZE 1 - -#define OCF_READ_INQUIRY_SCAN_TYPE 0x0042 -typedef struct { - uint8_t status; - uint8_t type; -} QEMU_PACKED read_inquiry_scan_type_rp; -#define READ_INQUIRY_SCAN_TYPE_RP_SIZE 2 - -#define OCF_WRITE_INQUIRY_SCAN_TYPE 0x0043 -typedef struct { - uint8_t type; -} QEMU_PACKED write_inquiry_scan_type_cp; -#define WRITE_INQUIRY_SCAN_TYPE_CP_SIZE 1 -typedef struct { - uint8_t status; -} QEMU_PACKED write_inquiry_scan_type_rp; -#define WRITE_INQUIRY_SCAN_TYPE_RP_SIZE 1 - -#define OCF_READ_INQUIRY_MODE 0x0044 -typedef struct { - uint8_t status; - uint8_t mode; -} QEMU_PACKED read_inquiry_mode_rp; -#define READ_INQUIRY_MODE_RP_SIZE 2 - -#define OCF_WRITE_INQUIRY_MODE 0x0045 -typedef struct { - uint8_t mode; -} QEMU_PACKED write_inquiry_mode_cp; -#define WRITE_INQUIRY_MODE_CP_SIZE 1 -typedef struct { - uint8_t status; -} QEMU_PACKED write_inquiry_mode_rp; -#define WRITE_INQUIRY_MODE_RP_SIZE 1 - -#define OCF_READ_PAGE_SCAN_TYPE 0x0046 - -#define OCF_WRITE_PAGE_SCAN_TYPE 0x0047 - -#define OCF_READ_AFH_MODE 0x0048 -typedef struct { - uint8_t status; - uint8_t mode; -} QEMU_PACKED read_afh_mode_rp; -#define READ_AFH_MODE_RP_SIZE 2 - -#define OCF_WRITE_AFH_MODE 0x0049 -typedef struct { - uint8_t mode; -} QEMU_PACKED write_afh_mode_cp; -#define WRITE_AFH_MODE_CP_SIZE 1 -typedef struct { - uint8_t status; -} QEMU_PACKED write_afh_mode_rp; -#define WRITE_AFH_MODE_RP_SIZE 1 - -#define OCF_READ_EXT_INQUIRY_RESPONSE 0x0051 -typedef struct { - uint8_t status; - uint8_t fec; - uint8_t data[240]; -} QEMU_PACKED read_ext_inquiry_response_rp; -#define READ_EXT_INQUIRY_RESPONSE_RP_SIZE 242 - -#define OCF_WRITE_EXT_INQUIRY_RESPONSE 0x0052 -typedef struct { - uint8_t fec; - uint8_t data[240]; -} QEMU_PACKED write_ext_inquiry_response_cp; -#define WRITE_EXT_INQUIRY_RESPONSE_CP_SIZE 241 -typedef struct { - uint8_t status; -} QEMU_PACKED write_ext_inquiry_response_rp; -#define WRITE_EXT_INQUIRY_RESPONSE_RP_SIZE 1 - -/* Informational Parameters */ -#define OGF_INFO_PARAM 0x04 - -#define OCF_READ_LOCAL_VERSION 0x0001 -typedef struct { - uint8_t status; - uint8_t hci_ver; - uint16_t hci_rev; - uint8_t lmp_ver; - uint16_t manufacturer; - uint16_t lmp_subver; -} QEMU_PACKED read_local_version_rp; -#define READ_LOCAL_VERSION_RP_SIZE 9 - -#define OCF_READ_LOCAL_COMMANDS 0x0002 -typedef struct { - uint8_t status; - uint8_t commands[64]; -} QEMU_PACKED read_local_commands_rp; -#define READ_LOCAL_COMMANDS_RP_SIZE 65 - -#define OCF_READ_LOCAL_FEATURES 0x0003 -typedef struct { - uint8_t status; - uint8_t features[8]; -} QEMU_PACKED read_local_features_rp; -#define READ_LOCAL_FEATURES_RP_SIZE 9 - -#define OCF_READ_LOCAL_EXT_FEATURES 0x0004 -typedef struct { - uint8_t page_num; -} QEMU_PACKED read_local_ext_features_cp; -#define READ_LOCAL_EXT_FEATURES_CP_SIZE 1 -typedef struct { - uint8_t status; - uint8_t page_num; - uint8_t max_page_num; - uint8_t features[8]; -} QEMU_PACKED read_local_ext_features_rp; -#define READ_LOCAL_EXT_FEATURES_RP_SIZE 11 - -#define OCF_READ_BUFFER_SIZE 0x0005 -typedef struct { - uint8_t status; - uint16_t acl_mtu; - uint8_t sco_mtu; - uint16_t acl_max_pkt; - uint16_t sco_max_pkt; -} QEMU_PACKED read_buffer_size_rp; -#define READ_BUFFER_SIZE_RP_SIZE 8 - -#define OCF_READ_COUNTRY_CODE 0x0007 -typedef struct { - uint8_t status; - uint8_t country_code; -} QEMU_PACKED read_country_code_rp; -#define READ_COUNTRY_CODE_RP_SIZE 2 - -#define OCF_READ_BD_ADDR 0x0009 -typedef struct { - uint8_t status; - bdaddr_t bdaddr; -} QEMU_PACKED read_bd_addr_rp; -#define READ_BD_ADDR_RP_SIZE 7 - -/* Status params */ -#define OGF_STATUS_PARAM 0x05 - -#define OCF_READ_FAILED_CONTACT_COUNTER 0x0001 -typedef struct { - uint8_t status; - uint16_t handle; - uint8_t counter; -} QEMU_PACKED read_failed_contact_counter_rp; -#define READ_FAILED_CONTACT_COUNTER_RP_SIZE 4 - -#define OCF_RESET_FAILED_CONTACT_COUNTER 0x0002 -typedef struct { - uint8_t status; - uint16_t handle; -} QEMU_PACKED reset_failed_contact_counter_rp; -#define RESET_FAILED_CONTACT_COUNTER_RP_SIZE 3 - -#define OCF_READ_LINK_QUALITY 0x0003 -typedef struct { - uint16_t handle; -} QEMU_PACKED read_link_quality_cp; -#define READ_LINK_QUALITY_CP_SIZE 2 - -typedef struct { - uint8_t status; - uint16_t handle; - uint8_t link_quality; -} QEMU_PACKED read_link_quality_rp; -#define READ_LINK_QUALITY_RP_SIZE 4 - -#define OCF_READ_RSSI 0x0005 -typedef struct { - uint8_t status; - uint16_t handle; - int8_t rssi; -} QEMU_PACKED read_rssi_rp; -#define READ_RSSI_RP_SIZE 4 - -#define OCF_READ_AFH_MAP 0x0006 -typedef struct { - uint8_t status; - uint16_t handle; - uint8_t mode; - uint8_t map[10]; -} QEMU_PACKED read_afh_map_rp; -#define READ_AFH_MAP_RP_SIZE 14 - -#define OCF_READ_CLOCK 0x0007 -typedef struct { - uint16_t handle; - uint8_t which_clock; -} QEMU_PACKED read_clock_cp; -#define READ_CLOCK_CP_SIZE 3 -typedef struct { - uint8_t status; - uint16_t handle; - uint32_t clock; - uint16_t accuracy; -} QEMU_PACKED read_clock_rp; -#define READ_CLOCK_RP_SIZE 9 - -/* Testing commands */ -#define OGF_TESTING_CMD 0x3e - -/* Vendor specific commands */ -#define OGF_VENDOR_CMD 0x3f - -/* HCI Events */ - -#define EVT_INQUIRY_COMPLETE 0x01 - -#define EVT_INQUIRY_RESULT 0x02 -typedef struct { - uint8_t num_responses; - bdaddr_t bdaddr; - uint8_t pscan_rep_mode; - uint8_t pscan_period_mode; - uint8_t pscan_mode; - uint8_t dev_class[3]; - uint16_t clock_offset; -} QEMU_PACKED inquiry_info; -#define INQUIRY_INFO_SIZE 15 - -#define EVT_CONN_COMPLETE 0x03 -typedef struct { - uint8_t status; - uint16_t handle; - bdaddr_t bdaddr; - uint8_t link_type; - uint8_t encr_mode; -} QEMU_PACKED evt_conn_complete; -#define EVT_CONN_COMPLETE_SIZE 11 - -#define EVT_CONN_REQUEST 0x04 -typedef struct { - bdaddr_t bdaddr; - uint8_t dev_class[3]; - uint8_t link_type; -} QEMU_PACKED evt_conn_request; -#define EVT_CONN_REQUEST_SIZE 10 - -#define EVT_DISCONN_COMPLETE 0x05 -typedef struct { - uint8_t status; - uint16_t handle; - uint8_t reason; -} QEMU_PACKED evt_disconn_complete; -#define EVT_DISCONN_COMPLETE_SIZE 4 - -#define EVT_AUTH_COMPLETE 0x06 -typedef struct { - uint8_t status; - uint16_t handle; -} QEMU_PACKED evt_auth_complete; -#define EVT_AUTH_COMPLETE_SIZE 3 - -#define EVT_REMOTE_NAME_REQ_COMPLETE 0x07 -typedef struct { - uint8_t status; - bdaddr_t bdaddr; - char name[248]; -} QEMU_PACKED evt_remote_name_req_complete; -#define EVT_REMOTE_NAME_REQ_COMPLETE_SIZE 255 - -#define EVT_ENCRYPT_CHANGE 0x08 -typedef struct { - uint8_t status; - uint16_t handle; - uint8_t encrypt; -} QEMU_PACKED evt_encrypt_change; -#define EVT_ENCRYPT_CHANGE_SIZE 4 - -#define EVT_CHANGE_CONN_LINK_KEY_COMPLETE 0x09 -typedef struct { - uint8_t status; - uint16_t handle; -} QEMU_PACKED evt_change_conn_link_key_complete; -#define EVT_CHANGE_CONN_LINK_KEY_COMPLETE_SIZE 3 - -#define EVT_MASTER_LINK_KEY_COMPLETE 0x0A -typedef struct { - uint8_t status; - uint16_t handle; - uint8_t key_flag; -} QEMU_PACKED evt_master_link_key_complete; -#define EVT_MASTER_LINK_KEY_COMPLETE_SIZE 4 - -#define EVT_READ_REMOTE_FEATURES_COMPLETE 0x0B -typedef struct { - uint8_t status; - uint16_t handle; - uint8_t features[8]; -} QEMU_PACKED evt_read_remote_features_complete; -#define EVT_READ_REMOTE_FEATURES_COMPLETE_SIZE 11 - -#define EVT_READ_REMOTE_VERSION_COMPLETE 0x0C -typedef struct { - uint8_t status; - uint16_t handle; - uint8_t lmp_ver; - uint16_t manufacturer; - uint16_t lmp_subver; -} QEMU_PACKED evt_read_remote_version_complete; -#define EVT_READ_REMOTE_VERSION_COMPLETE_SIZE 8 - -#define EVT_QOS_SETUP_COMPLETE 0x0D -typedef struct { - uint8_t status; - uint16_t handle; - uint8_t flags; /* Reserved */ - hci_qos qos; -} QEMU_PACKED evt_qos_setup_complete; -#define EVT_QOS_SETUP_COMPLETE_SIZE (4 + HCI_QOS_CP_SIZE) - -#define EVT_CMD_COMPLETE 0x0E -typedef struct { - uint8_t ncmd; - uint16_t opcode; -} QEMU_PACKED evt_cmd_complete; -#define EVT_CMD_COMPLETE_SIZE 3 - -#define EVT_CMD_STATUS 0x0F -typedef struct { - uint8_t status; - uint8_t ncmd; - uint16_t opcode; -} QEMU_PACKED evt_cmd_status; -#define EVT_CMD_STATUS_SIZE 4 - -#define EVT_HARDWARE_ERROR 0x10 -typedef struct { - uint8_t code; -} QEMU_PACKED evt_hardware_error; -#define EVT_HARDWARE_ERROR_SIZE 1 - -#define EVT_FLUSH_OCCURRED 0x11 -typedef struct { - uint16_t handle; -} QEMU_PACKED evt_flush_occurred; -#define EVT_FLUSH_OCCURRED_SIZE 2 - -#define EVT_ROLE_CHANGE 0x12 -typedef struct { - uint8_t status; - bdaddr_t bdaddr; - uint8_t role; -} QEMU_PACKED evt_role_change; -#define EVT_ROLE_CHANGE_SIZE 8 - -#define EVT_NUM_COMP_PKTS 0x13 -typedef struct { - uint8_t num_hndl; - struct { - uint16_t handle; - uint16_t num_packets; - } connection[0]; -} QEMU_PACKED evt_num_comp_pkts; -#define EVT_NUM_COMP_PKTS_SIZE(num_hndl) (1 + 4 * (num_hndl)) - -#define EVT_MODE_CHANGE 0x14 -typedef struct { - uint8_t status; - uint16_t handle; - uint8_t mode; - uint16_t interval; -} QEMU_PACKED evt_mode_change; -#define EVT_MODE_CHANGE_SIZE 6 - -#define EVT_RETURN_LINK_KEYS 0x15 -typedef struct { - uint8_t num_keys; - /* variable length part */ -} QEMU_PACKED evt_return_link_keys; -#define EVT_RETURN_LINK_KEYS_SIZE 1 - -#define EVT_PIN_CODE_REQ 0x16 -typedef struct { - bdaddr_t bdaddr; -} QEMU_PACKED evt_pin_code_req; -#define EVT_PIN_CODE_REQ_SIZE 6 - -#define EVT_LINK_KEY_REQ 0x17 -typedef struct { - bdaddr_t bdaddr; -} QEMU_PACKED evt_link_key_req; -#define EVT_LINK_KEY_REQ_SIZE 6 - -#define EVT_LINK_KEY_NOTIFY 0x18 -typedef struct { - bdaddr_t bdaddr; - uint8_t link_key[16]; - uint8_t key_type; -} QEMU_PACKED evt_link_key_notify; -#define EVT_LINK_KEY_NOTIFY_SIZE 23 - -#define EVT_LOOPBACK_COMMAND 0x19 - -#define EVT_DATA_BUFFER_OVERFLOW 0x1A -typedef struct { - uint8_t link_type; -} QEMU_PACKED evt_data_buffer_overflow; -#define EVT_DATA_BUFFER_OVERFLOW_SIZE 1 - -#define EVT_MAX_SLOTS_CHANGE 0x1B -typedef struct { - uint16_t handle; - uint8_t max_slots; -} QEMU_PACKED evt_max_slots_change; -#define EVT_MAX_SLOTS_CHANGE_SIZE 3 - -#define EVT_READ_CLOCK_OFFSET_COMPLETE 0x1C -typedef struct { - uint8_t status; - uint16_t handle; - uint16_t clock_offset; -} QEMU_PACKED evt_read_clock_offset_complete; -#define EVT_READ_CLOCK_OFFSET_COMPLETE_SIZE 5 - -#define EVT_CONN_PTYPE_CHANGED 0x1D -typedef struct { - uint8_t status; - uint16_t handle; - uint16_t ptype; -} QEMU_PACKED evt_conn_ptype_changed; -#define EVT_CONN_PTYPE_CHANGED_SIZE 5 - -#define EVT_QOS_VIOLATION 0x1E -typedef struct { - uint16_t handle; -} QEMU_PACKED evt_qos_violation; -#define EVT_QOS_VIOLATION_SIZE 2 - -#define EVT_PSCAN_REP_MODE_CHANGE 0x20 -typedef struct { - bdaddr_t bdaddr; - uint8_t pscan_rep_mode; -} QEMU_PACKED evt_pscan_rep_mode_change; -#define EVT_PSCAN_REP_MODE_CHANGE_SIZE 7 - -#define EVT_FLOW_SPEC_COMPLETE 0x21 -typedef struct { - uint8_t status; - uint16_t handle; - uint8_t flags; - uint8_t direction; - hci_qos qos; -} QEMU_PACKED evt_flow_spec_complete; -#define EVT_FLOW_SPEC_COMPLETE_SIZE (5 + HCI_QOS_CP_SIZE) - -#define EVT_INQUIRY_RESULT_WITH_RSSI 0x22 -typedef struct { - uint8_t num_responses; - bdaddr_t bdaddr; - uint8_t pscan_rep_mode; - uint8_t pscan_period_mode; - uint8_t dev_class[3]; - uint16_t clock_offset; - int8_t rssi; -} QEMU_PACKED inquiry_info_with_rssi; -#define INQUIRY_INFO_WITH_RSSI_SIZE 15 -typedef struct { - uint8_t num_responses; - bdaddr_t bdaddr; - uint8_t pscan_rep_mode; - uint8_t pscan_period_mode; - uint8_t pscan_mode; - uint8_t dev_class[3]; - uint16_t clock_offset; - int8_t rssi; -} QEMU_PACKED inquiry_info_with_rssi_and_pscan_mode; -#define INQUIRY_INFO_WITH_RSSI_AND_PSCAN_MODE_SIZE 16 - -#define EVT_READ_REMOTE_EXT_FEATURES_COMPLETE 0x23 -typedef struct { - uint8_t status; - uint16_t handle; - uint8_t page_num; - uint8_t max_page_num; - uint8_t features[8]; -} QEMU_PACKED evt_read_remote_ext_features_complete; -#define EVT_READ_REMOTE_EXT_FEATURES_COMPLETE_SIZE 13 - -#define EVT_SYNC_CONN_COMPLETE 0x2C -typedef struct { - uint8_t status; - uint16_t handle; - bdaddr_t bdaddr; - uint8_t link_type; - uint8_t trans_interval; - uint8_t retrans_window; - uint16_t rx_pkt_len; - uint16_t tx_pkt_len; - uint8_t air_mode; -} QEMU_PACKED evt_sync_conn_complete; -#define EVT_SYNC_CONN_COMPLETE_SIZE 17 - -#define EVT_SYNC_CONN_CHANGED 0x2D -typedef struct { - uint8_t status; - uint16_t handle; - uint8_t trans_interval; - uint8_t retrans_window; - uint16_t rx_pkt_len; - uint16_t tx_pkt_len; -} QEMU_PACKED evt_sync_conn_changed; -#define EVT_SYNC_CONN_CHANGED_SIZE 9 - -#define EVT_SNIFF_SUBRATE 0x2E -typedef struct { - uint8_t status; - uint16_t handle; - uint16_t max_remote_latency; - uint16_t max_local_latency; - uint16_t min_remote_timeout; - uint16_t min_local_timeout; -} QEMU_PACKED evt_sniff_subrate; -#define EVT_SNIFF_SUBRATE_SIZE 11 - -#define EVT_TESTING 0xFE - -#define EVT_VENDOR 0xFF - -/* Command opcode pack/unpack */ -#define cmd_opcode_pack(ogf, ocf) (uint16_t)((ocf & 0x03ff)|(ogf << 10)) -#define cmd_opcode_ogf(op) (op >> 10) -#define cmd_opcode_ocf(op) (op & 0x03ff) - -/* ACL handle and flags pack/unpack */ -#define acl_handle_pack(h, f) (uint16_t)(((h) & 0x0fff)|((f) << 12)) -#define acl_handle(h) ((h) & 0x0fff) -#define acl_flags(h) ((h) >> 12) - -/* HCI Packet structures */ -#define HCI_COMMAND_HDR_SIZE 3 -#define HCI_EVENT_HDR_SIZE 2 -#define HCI_ACL_HDR_SIZE 4 -#define HCI_SCO_HDR_SIZE 3 - -struct hci_command_hdr { - uint16_t opcode; /* OCF & OGF */ - uint8_t plen; -} QEMU_PACKED; - -struct hci_event_hdr { - uint8_t evt; - uint8_t plen; -} QEMU_PACKED; - -struct hci_acl_hdr { - uint16_t handle; /* Handle & Flags(PB, BC) */ - uint16_t dlen; -} QEMU_PACKED; - -struct hci_sco_hdr { - uint16_t handle; - uint8_t dlen; -} QEMU_PACKED; - -/* L2CAP layer defines */ - -enum bt_l2cap_lm_bits { - L2CAP_LM_MASTER = 1 << 0, - L2CAP_LM_AUTH = 1 << 1, - L2CAP_LM_ENCRYPT = 1 << 2, - L2CAP_LM_TRUSTED = 1 << 3, - L2CAP_LM_RELIABLE = 1 << 4, - L2CAP_LM_SECURE = 1 << 5, -}; - -enum bt_l2cap_cid_predef { - L2CAP_CID_INVALID = 0x0000, - L2CAP_CID_SIGNALLING= 0x0001, - L2CAP_CID_GROUP = 0x0002, - L2CAP_CID_ALLOC = 0x0040, -}; - -/* L2CAP command codes */ -enum bt_l2cap_cmd { - L2CAP_COMMAND_REJ = 1, - L2CAP_CONN_REQ, - L2CAP_CONN_RSP, - L2CAP_CONF_REQ, - L2CAP_CONF_RSP, - L2CAP_DISCONN_REQ, - L2CAP_DISCONN_RSP, - L2CAP_ECHO_REQ, - L2CAP_ECHO_RSP, - L2CAP_INFO_REQ, - L2CAP_INFO_RSP, -}; - -enum bt_l2cap_sar_bits { - L2CAP_SAR_NO_SEG = 0, - L2CAP_SAR_START, - L2CAP_SAR_END, - L2CAP_SAR_CONT, -}; - -/* L2CAP structures */ -typedef struct { - uint16_t len; - uint16_t cid; - uint8_t data[0]; -} QEMU_PACKED l2cap_hdr; -#define L2CAP_HDR_SIZE 4 - -typedef struct { - uint8_t code; - uint8_t ident; - uint16_t len; -} QEMU_PACKED l2cap_cmd_hdr; -#define L2CAP_CMD_HDR_SIZE 4 - -typedef struct { - uint16_t reason; -} QEMU_PACKED l2cap_cmd_rej; -#define L2CAP_CMD_REJ_SIZE 2 - -typedef struct { - uint16_t dcid; - uint16_t scid; -} QEMU_PACKED l2cap_cmd_rej_cid; -#define L2CAP_CMD_REJ_CID_SIZE 4 - -/* reject reason */ -enum bt_l2cap_rej_reason { - L2CAP_REJ_CMD_NOT_UNDERSTOOD = 0, - L2CAP_REJ_SIG_TOOBIG, - L2CAP_REJ_CID_INVAL, -}; - -typedef struct { - uint16_t psm; - uint16_t scid; -} QEMU_PACKED l2cap_conn_req; -#define L2CAP_CONN_REQ_SIZE 4 - -typedef struct { - uint16_t dcid; - uint16_t scid; - uint16_t result; - uint16_t status; -} QEMU_PACKED l2cap_conn_rsp; -#define L2CAP_CONN_RSP_SIZE 8 - -/* connect result */ -enum bt_l2cap_conn_res { - L2CAP_CR_SUCCESS = 0, - L2CAP_CR_PEND, - L2CAP_CR_BAD_PSM, - L2CAP_CR_SEC_BLOCK, - L2CAP_CR_NO_MEM, -}; - -/* connect status */ -enum bt_l2cap_conn_stat { - L2CAP_CS_NO_INFO = 0, - L2CAP_CS_AUTHEN_PEND, - L2CAP_CS_AUTHOR_PEND, -}; - -typedef struct { - uint16_t dcid; - uint16_t flags; - uint8_t data[0]; -} QEMU_PACKED l2cap_conf_req; -#define L2CAP_CONF_REQ_SIZE(datalen) (4 + (datalen)) - -typedef struct { - uint16_t scid; - uint16_t flags; - uint16_t result; - uint8_t data[0]; -} QEMU_PACKED l2cap_conf_rsp; -#define L2CAP_CONF_RSP_SIZE(datalen) (6 + datalen) - -enum bt_l2cap_conf_res { - L2CAP_CONF_SUCCESS = 0, - L2CAP_CONF_UNACCEPT, - L2CAP_CONF_REJECT, - L2CAP_CONF_UNKNOWN, -}; - -typedef struct { - uint8_t type; - uint8_t len; - uint8_t val[0]; -} QEMU_PACKED l2cap_conf_opt; -#define L2CAP_CONF_OPT_SIZE 2 - -enum bt_l2cap_conf_val { - L2CAP_CONF_MTU = 1, - L2CAP_CONF_FLUSH_TO, - L2CAP_CONF_QOS, - L2CAP_CONF_RFC, - L2CAP_CONF_RFC_MODE = L2CAP_CONF_RFC, -}; - -typedef struct { - uint8_t flags; - uint8_t service_type; - uint32_t token_rate; - uint32_t token_bucket_size; - uint32_t peak_bandwidth; - uint32_t latency; - uint32_t delay_variation; -} QEMU_PACKED l2cap_conf_opt_qos; -#define L2CAP_CONF_OPT_QOS_SIZE 22 - -enum bt_l2cap_conf_opt_qos_st { - L2CAP_CONF_QOS_NO_TRAFFIC = 0x00, - L2CAP_CONF_QOS_BEST_EFFORT, - L2CAP_CONF_QOS_GUARANTEED, -}; - -#define L2CAP_CONF_QOS_WILDCARD 0xffffffff - -enum bt_l2cap_mode { - L2CAP_MODE_BASIC = 0, - L2CAP_MODE_RETRANS = 1, - L2CAP_MODE_FLOWCTL = 2, -}; - -typedef struct { - uint16_t dcid; - uint16_t scid; -} QEMU_PACKED l2cap_disconn_req; -#define L2CAP_DISCONN_REQ_SIZE 4 - -typedef struct { - uint16_t dcid; - uint16_t scid; -} QEMU_PACKED l2cap_disconn_rsp; -#define L2CAP_DISCONN_RSP_SIZE 4 - -typedef struct { - uint16_t type; -} QEMU_PACKED l2cap_info_req; -#define L2CAP_INFO_REQ_SIZE 2 - -typedef struct { - uint16_t type; - uint16_t result; - uint8_t data[0]; -} QEMU_PACKED l2cap_info_rsp; -#define L2CAP_INFO_RSP_SIZE 4 - -/* info type */ -enum bt_l2cap_info_type { - L2CAP_IT_CL_MTU = 1, - L2CAP_IT_FEAT_MASK, -}; - -/* info result */ -enum bt_l2cap_info_result { - L2CAP_IR_SUCCESS = 0, - L2CAP_IR_NOTSUPP, -}; - -/* Service Discovery Protocol defines */ -/* Note that all multibyte values in lower layer protocols (above in this file) - * are little-endian while SDP is big-endian. */ - -/* Protocol UUIDs */ -enum sdp_proto_uuid { - SDP_UUID = 0x0001, - UDP_UUID = 0x0002, - RFCOMM_UUID = 0x0003, - TCP_UUID = 0x0004, - TCS_BIN_UUID = 0x0005, - TCS_AT_UUID = 0x0006, - OBEX_UUID = 0x0008, - IP_UUID = 0x0009, - FTP_UUID = 0x000a, - HTTP_UUID = 0x000c, - WSP_UUID = 0x000e, - BNEP_UUID = 0x000f, - UPNP_UUID = 0x0010, - HIDP_UUID = 0x0011, - HCRP_CTRL_UUID = 0x0012, - HCRP_DATA_UUID = 0x0014, - HCRP_NOTE_UUID = 0x0016, - AVCTP_UUID = 0x0017, - AVDTP_UUID = 0x0019, - CMTP_UUID = 0x001b, - UDI_UUID = 0x001d, - MCAP_CTRL_UUID = 0x001e, - MCAP_DATA_UUID = 0x001f, - L2CAP_UUID = 0x0100, -}; - -/* - * Service class identifiers of standard services and service groups - */ -enum service_class_id { - SDP_SERVER_SVCLASS_ID = 0x1000, - BROWSE_GRP_DESC_SVCLASS_ID = 0x1001, - PUBLIC_BROWSE_GROUP = 0x1002, - SERIAL_PORT_SVCLASS_ID = 0x1101, - LAN_ACCESS_SVCLASS_ID = 0x1102, - DIALUP_NET_SVCLASS_ID = 0x1103, - IRMC_SYNC_SVCLASS_ID = 0x1104, - OBEX_OBJPUSH_SVCLASS_ID = 0x1105, - OBEX_FILETRANS_SVCLASS_ID = 0x1106, - IRMC_SYNC_CMD_SVCLASS_ID = 0x1107, - HEADSET_SVCLASS_ID = 0x1108, - CORDLESS_TELEPHONY_SVCLASS_ID = 0x1109, - AUDIO_SOURCE_SVCLASS_ID = 0x110a, - AUDIO_SINK_SVCLASS_ID = 0x110b, - AV_REMOTE_TARGET_SVCLASS_ID = 0x110c, - ADVANCED_AUDIO_SVCLASS_ID = 0x110d, - AV_REMOTE_SVCLASS_ID = 0x110e, - VIDEO_CONF_SVCLASS_ID = 0x110f, - INTERCOM_SVCLASS_ID = 0x1110, - FAX_SVCLASS_ID = 0x1111, - HEADSET_AGW_SVCLASS_ID = 0x1112, - WAP_SVCLASS_ID = 0x1113, - WAP_CLIENT_SVCLASS_ID = 0x1114, - PANU_SVCLASS_ID = 0x1115, - NAP_SVCLASS_ID = 0x1116, - GN_SVCLASS_ID = 0x1117, - DIRECT_PRINTING_SVCLASS_ID = 0x1118, - REFERENCE_PRINTING_SVCLASS_ID = 0x1119, - IMAGING_SVCLASS_ID = 0x111a, - IMAGING_RESPONDER_SVCLASS_ID = 0x111b, - IMAGING_ARCHIVE_SVCLASS_ID = 0x111c, - IMAGING_REFOBJS_SVCLASS_ID = 0x111d, - HANDSFREE_SVCLASS_ID = 0x111e, - HANDSFREE_AGW_SVCLASS_ID = 0x111f, - DIRECT_PRT_REFOBJS_SVCLASS_ID = 0x1120, - REFLECTED_UI_SVCLASS_ID = 0x1121, - BASIC_PRINTING_SVCLASS_ID = 0x1122, - PRINTING_STATUS_SVCLASS_ID = 0x1123, - HID_SVCLASS_ID = 0x1124, - HCR_SVCLASS_ID = 0x1125, - HCR_PRINT_SVCLASS_ID = 0x1126, - HCR_SCAN_SVCLASS_ID = 0x1127, - CIP_SVCLASS_ID = 0x1128, - VIDEO_CONF_GW_SVCLASS_ID = 0x1129, - UDI_MT_SVCLASS_ID = 0x112a, - UDI_TA_SVCLASS_ID = 0x112b, - AV_SVCLASS_ID = 0x112c, - SAP_SVCLASS_ID = 0x112d, - PBAP_PCE_SVCLASS_ID = 0x112e, - PBAP_PSE_SVCLASS_ID = 0x112f, - PBAP_SVCLASS_ID = 0x1130, - PNP_INFO_SVCLASS_ID = 0x1200, - GENERIC_NETWORKING_SVCLASS_ID = 0x1201, - GENERIC_FILETRANS_SVCLASS_ID = 0x1202, - GENERIC_AUDIO_SVCLASS_ID = 0x1203, - GENERIC_TELEPHONY_SVCLASS_ID = 0x1204, - UPNP_SVCLASS_ID = 0x1205, - UPNP_IP_SVCLASS_ID = 0x1206, - UPNP_PAN_SVCLASS_ID = 0x1300, - UPNP_LAP_SVCLASS_ID = 0x1301, - UPNP_L2CAP_SVCLASS_ID = 0x1302, - VIDEO_SOURCE_SVCLASS_ID = 0x1303, - VIDEO_SINK_SVCLASS_ID = 0x1304, - VIDEO_DISTRIBUTION_SVCLASS_ID = 0x1305, - MDP_SVCLASS_ID = 0x1400, - MDP_SOURCE_SVCLASS_ID = 0x1401, - MDP_SINK_SVCLASS_ID = 0x1402, - APPLE_AGENT_SVCLASS_ID = 0x2112, -}; - -/* - * Standard profile descriptor identifiers; note these - * may be identical to some of the service classes defined above - */ -#define SDP_SERVER_PROFILE_ID SDP_SERVER_SVCLASS_ID -#define BROWSE_GRP_DESC_PROFILE_ID BROWSE_GRP_DESC_SVCLASS_ID -#define SERIAL_PORT_PROFILE_ID SERIAL_PORT_SVCLASS_ID -#define LAN_ACCESS_PROFILE_ID LAN_ACCESS_SVCLASS_ID -#define DIALUP_NET_PROFILE_ID DIALUP_NET_SVCLASS_ID -#define IRMC_SYNC_PROFILE_ID IRMC_SYNC_SVCLASS_ID -#define OBEX_OBJPUSH_PROFILE_ID OBEX_OBJPUSH_SVCLASS_ID -#define OBEX_FILETRANS_PROFILE_ID OBEX_FILETRANS_SVCLASS_ID -#define IRMC_SYNC_CMD_PROFILE_ID IRMC_SYNC_CMD_SVCLASS_ID -#define HEADSET_PROFILE_ID HEADSET_SVCLASS_ID -#define CORDLESS_TELEPHONY_PROFILE_ID CORDLESS_TELEPHONY_SVCLASS_ID -#define AUDIO_SOURCE_PROFILE_ID AUDIO_SOURCE_SVCLASS_ID -#define AUDIO_SINK_PROFILE_ID AUDIO_SINK_SVCLASS_ID -#define AV_REMOTE_TARGET_PROFILE_ID AV_REMOTE_TARGET_SVCLASS_ID -#define ADVANCED_AUDIO_PROFILE_ID ADVANCED_AUDIO_SVCLASS_ID -#define AV_REMOTE_PROFILE_ID AV_REMOTE_SVCLASS_ID -#define VIDEO_CONF_PROFILE_ID VIDEO_CONF_SVCLASS_ID -#define INTERCOM_PROFILE_ID INTERCOM_SVCLASS_ID -#define FAX_PROFILE_ID FAX_SVCLASS_ID -#define HEADSET_AGW_PROFILE_ID HEADSET_AGW_SVCLASS_ID -#define WAP_PROFILE_ID WAP_SVCLASS_ID -#define WAP_CLIENT_PROFILE_ID WAP_CLIENT_SVCLASS_ID -#define PANU_PROFILE_ID PANU_SVCLASS_ID -#define NAP_PROFILE_ID NAP_SVCLASS_ID -#define GN_PROFILE_ID GN_SVCLASS_ID -#define DIRECT_PRINTING_PROFILE_ID DIRECT_PRINTING_SVCLASS_ID -#define REFERENCE_PRINTING_PROFILE_ID REFERENCE_PRINTING_SVCLASS_ID -#define IMAGING_PROFILE_ID IMAGING_SVCLASS_ID -#define IMAGING_RESPONDER_PROFILE_ID IMAGING_RESPONDER_SVCLASS_ID -#define IMAGING_ARCHIVE_PROFILE_ID IMAGING_ARCHIVE_SVCLASS_ID -#define IMAGING_REFOBJS_PROFILE_ID IMAGING_REFOBJS_SVCLASS_ID -#define HANDSFREE_PROFILE_ID HANDSFREE_SVCLASS_ID -#define HANDSFREE_AGW_PROFILE_ID HANDSFREE_AGW_SVCLASS_ID -#define DIRECT_PRT_REFOBJS_PROFILE_ID DIRECT_PRT_REFOBJS_SVCLASS_ID -#define REFLECTED_UI_PROFILE_ID REFLECTED_UI_SVCLASS_ID -#define BASIC_PRINTING_PROFILE_ID BASIC_PRINTING_SVCLASS_ID -#define PRINTING_STATUS_PROFILE_ID PRINTING_STATUS_SVCLASS_ID -#define HID_PROFILE_ID HID_SVCLASS_ID -#define HCR_PROFILE_ID HCR_SCAN_SVCLASS_ID -#define HCR_PRINT_PROFILE_ID HCR_PRINT_SVCLASS_ID -#define HCR_SCAN_PROFILE_ID HCR_SCAN_SVCLASS_ID -#define CIP_PROFILE_ID CIP_SVCLASS_ID -#define VIDEO_CONF_GW_PROFILE_ID VIDEO_CONF_GW_SVCLASS_ID -#define UDI_MT_PROFILE_ID UDI_MT_SVCLASS_ID -#define UDI_TA_PROFILE_ID UDI_TA_SVCLASS_ID -#define AV_PROFILE_ID AV_SVCLASS_ID -#define SAP_PROFILE_ID SAP_SVCLASS_ID -#define PBAP_PCE_PROFILE_ID PBAP_PCE_SVCLASS_ID -#define PBAP_PSE_PROFILE_ID PBAP_PSE_SVCLASS_ID -#define PBAP_PROFILE_ID PBAP_SVCLASS_ID -#define PNP_INFO_PROFILE_ID PNP_INFO_SVCLASS_ID -#define GENERIC_NETWORKING_PROFILE_ID GENERIC_NETWORKING_SVCLASS_ID -#define GENERIC_FILETRANS_PROFILE_ID GENERIC_FILETRANS_SVCLASS_ID -#define GENERIC_AUDIO_PROFILE_ID GENERIC_AUDIO_SVCLASS_ID -#define GENERIC_TELEPHONY_PROFILE_ID GENERIC_TELEPHONY_SVCLASS_ID -#define UPNP_PROFILE_ID UPNP_SVCLASS_ID -#define UPNP_IP_PROFILE_ID UPNP_IP_SVCLASS_ID -#define UPNP_PAN_PROFILE_ID UPNP_PAN_SVCLASS_ID -#define UPNP_LAP_PROFILE_ID UPNP_LAP_SVCLASS_ID -#define UPNP_L2CAP_PROFILE_ID UPNP_L2CAP_SVCLASS_ID -#define VIDEO_SOURCE_PROFILE_ID VIDEO_SOURCE_SVCLASS_ID -#define VIDEO_SINK_PROFILE_ID VIDEO_SINK_SVCLASS_ID -#define VIDEO_DISTRIBUTION_PROFILE_ID VIDEO_DISTRIBUTION_SVCLASS_ID -#define MDP_PROFILE_ID MDP_SVCLASS_ID -#define MDP_SOURCE_PROFILE_ID MDP_SROUCE_SVCLASS_ID -#define MDP_SINK_PROFILE_ID MDP_SINK_SVCLASS_ID -#define APPLE_AGENT_PROFILE_ID APPLE_AGENT_SVCLASS_ID - -/* Data Representation */ -enum bt_sdp_data_type { - SDP_DTYPE_NIL = 0 << 3, - SDP_DTYPE_UINT = 1 << 3, - SDP_DTYPE_SINT = 2 << 3, - SDP_DTYPE_UUID = 3 << 3, - SDP_DTYPE_STRING = 4 << 3, - SDP_DTYPE_BOOL = 5 << 3, - SDP_DTYPE_SEQ = 6 << 3, - SDP_DTYPE_ALT = 7 << 3, - SDP_DTYPE_URL = 8 << 3, -}; - -enum bt_sdp_data_size { - SDP_DSIZE_1 = 0, - SDP_DSIZE_2, - SDP_DSIZE_4, - SDP_DSIZE_8, - SDP_DSIZE_16, - SDP_DSIZE_NEXT1, - SDP_DSIZE_NEXT2, - SDP_DSIZE_NEXT4, - SDP_DSIZE_MASK = SDP_DSIZE_NEXT4, -}; - -enum bt_sdp_cmd { - SDP_ERROR_RSP = 0x01, - SDP_SVC_SEARCH_REQ = 0x02, - SDP_SVC_SEARCH_RSP = 0x03, - SDP_SVC_ATTR_REQ = 0x04, - SDP_SVC_ATTR_RSP = 0x05, - SDP_SVC_SEARCH_ATTR_REQ = 0x06, - SDP_SVC_SEARCH_ATTR_RSP = 0x07, -}; - -enum bt_sdp_errorcode { - SDP_INVALID_VERSION = 0x0001, - SDP_INVALID_RECORD_HANDLE = 0x0002, - SDP_INVALID_SYNTAX = 0x0003, - SDP_INVALID_PDU_SIZE = 0x0004, - SDP_INVALID_CSTATE = 0x0005, -}; - -/* - * String identifiers are based on the SDP spec stating that - * "base attribute id of the primary (universal) language must be 0x0100" - * - * Other languages should have their own offset; e.g.: - * #define XXXLangBase yyyy - * #define AttrServiceName_XXX 0x0000+XXXLangBase - */ -#define SDP_PRIMARY_LANG_BASE 0x0100 - -enum bt_sdp_attribute_id { - SDP_ATTR_RECORD_HANDLE = 0x0000, - SDP_ATTR_SVCLASS_ID_LIST = 0x0001, - SDP_ATTR_RECORD_STATE = 0x0002, - SDP_ATTR_SERVICE_ID = 0x0003, - SDP_ATTR_PROTO_DESC_LIST = 0x0004, - SDP_ATTR_BROWSE_GRP_LIST = 0x0005, - SDP_ATTR_LANG_BASE_ATTR_ID_LIST = 0x0006, - SDP_ATTR_SVCINFO_TTL = 0x0007, - SDP_ATTR_SERVICE_AVAILABILITY = 0x0008, - SDP_ATTR_PFILE_DESC_LIST = 0x0009, - SDP_ATTR_DOC_URL = 0x000a, - SDP_ATTR_CLNT_EXEC_URL = 0x000b, - SDP_ATTR_ICON_URL = 0x000c, - SDP_ATTR_ADD_PROTO_DESC_LIST = 0x000d, - - SDP_ATTR_SVCNAME_PRIMARY = SDP_PRIMARY_LANG_BASE + 0, - SDP_ATTR_SVCDESC_PRIMARY = SDP_PRIMARY_LANG_BASE + 1, - SDP_ATTR_SVCPROV_PRIMARY = SDP_PRIMARY_LANG_BASE + 2, - - SDP_ATTR_GROUP_ID = 0x0200, - SDP_ATTR_IP_SUBNET = 0x0200, - - /* SDP */ - SDP_ATTR_VERSION_NUM_LIST = 0x0200, - SDP_ATTR_SVCDB_STATE = 0x0201, - - SDP_ATTR_SERVICE_VERSION = 0x0300, - SDP_ATTR_EXTERNAL_NETWORK = 0x0301, - SDP_ATTR_SUPPORTED_DATA_STORES_LIST = 0x0301, - SDP_ATTR_FAX_CLASS1_SUPPORT = 0x0302, - SDP_ATTR_REMOTE_AUDIO_VOLUME_CONTROL = 0x0302, - SDP_ATTR_FAX_CLASS20_SUPPORT = 0x0303, - SDP_ATTR_SUPPORTED_FORMATS_LIST = 0x0303, - SDP_ATTR_FAX_CLASS2_SUPPORT = 0x0304, - SDP_ATTR_AUDIO_FEEDBACK_SUPPORT = 0x0305, - SDP_ATTR_NETWORK_ADDRESS = 0x0306, - SDP_ATTR_WAP_GATEWAY = 0x0307, - SDP_ATTR_HOMEPAGE_URL = 0x0308, - SDP_ATTR_WAP_STACK_TYPE = 0x0309, - SDP_ATTR_SECURITY_DESC = 0x030a, - SDP_ATTR_NET_ACCESS_TYPE = 0x030b, - SDP_ATTR_MAX_NET_ACCESSRATE = 0x030c, - SDP_ATTR_IP4_SUBNET = 0x030d, - SDP_ATTR_IP6_SUBNET = 0x030e, - SDP_ATTR_SUPPORTED_CAPABILITIES = 0x0310, - SDP_ATTR_SUPPORTED_FEATURES = 0x0311, - SDP_ATTR_SUPPORTED_FUNCTIONS = 0x0312, - SDP_ATTR_TOTAL_IMAGING_DATA_CAPACITY = 0x0313, - SDP_ATTR_SUPPORTED_REPOSITORIES = 0x0314, - - /* PnP Information */ - SDP_ATTR_SPECIFICATION_ID = 0x0200, - SDP_ATTR_VENDOR_ID = 0x0201, - SDP_ATTR_PRODUCT_ID = 0x0202, - SDP_ATTR_VERSION = 0x0203, - SDP_ATTR_PRIMARY_RECORD = 0x0204, - SDP_ATTR_VENDOR_ID_SOURCE = 0x0205, - - /* BT HID */ - SDP_ATTR_DEVICE_RELEASE_NUMBER = 0x0200, - SDP_ATTR_PARSER_VERSION = 0x0201, - SDP_ATTR_DEVICE_SUBCLASS = 0x0202, - SDP_ATTR_COUNTRY_CODE = 0x0203, - SDP_ATTR_VIRTUAL_CABLE = 0x0204, - SDP_ATTR_RECONNECT_INITIATE = 0x0205, - SDP_ATTR_DESCRIPTOR_LIST = 0x0206, - SDP_ATTR_LANG_ID_BASE_LIST = 0x0207, - SDP_ATTR_SDP_DISABLE = 0x0208, - SDP_ATTR_BATTERY_POWER = 0x0209, - SDP_ATTR_REMOTE_WAKEUP = 0x020a, - SDP_ATTR_PROFILE_VERSION = 0x020b, - SDP_ATTR_SUPERVISION_TIMEOUT = 0x020c, - SDP_ATTR_NORMALLY_CONNECTABLE = 0x020d, - SDP_ATTR_BOOT_DEVICE = 0x020e, -}; - -#endif diff --git a/include/hw/char/bcm2835_aux.h b/include/hw/char/bcm2835_aux.h index cdbf7e3e37..934acf9c81 100644 --- a/include/hw/char/bcm2835_aux.h +++ b/include/hw/char/bcm2835_aux.h @@ -2,7 +2,8 @@ * Rasperry Pi 2 emulation and refactoring Copyright (c) 2015, Microsoft * Written by Andrew Baumann * - * This code is licensed under the GNU GPLv2 and later. + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. */ #ifndef BCM2835_AUX_H diff --git a/include/hw/char/serial.h b/include/hw/char/serial.h index 8be3d8a4f9..535fa23a2b 100644 --- a/include/hw/char/serial.h +++ b/include/hw/char/serial.h @@ -30,10 +30,13 @@ #include "exec/memory.h" #include "qemu/fifo8.h" #include "chardev/char.h" +#include "hw/sysbus.h" #define UART_FIFO_LENGTH 16 /* 16550A Fifo Length */ typedef struct SerialState { + DeviceState parent; + uint16_t divider; uint8_t rbr; /* receive register */ uint8_t thr; /* transmit holding register */ @@ -54,8 +57,7 @@ typedef struct SerialState { qemu_irq irq; CharBackend chr; int last_break_enable; - int it_shift; - int baudbase; + uint32_t baudbase; uint32_t tsr_retry; guint watch_tag; uint32_t wakeup; @@ -77,20 +79,39 @@ typedef struct SerialState { MemoryRegion io; } SerialState; +typedef struct SerialMM { + SysBusDevice parent; + + SerialState serial; + + uint8_t regshift; + uint8_t endianness; +} SerialMM; + +typedef struct SerialIO { + SysBusDevice parent; + + SerialState serial; +} SerialIO; + extern const VMStateDescription vmstate_serial; extern const MemoryRegionOps serial_io_ops; -void serial_realize_core(SerialState *s, Error **errp); -void serial_exit_core(SerialState *s); void serial_set_frequency(SerialState *s, uint32_t frequency); -/* legacy pre qom */ -SerialState *serial_init(int base, qemu_irq irq, int baudbase, - Chardev *chr, MemoryRegion *system_io); -SerialState *serial_mm_init(MemoryRegion *address_space, - hwaddr base, int it_shift, - qemu_irq irq, int baudbase, - Chardev *chr, enum device_endian end); +#define TYPE_SERIAL "serial" +#define SERIAL(s) OBJECT_CHECK(SerialState, (s), TYPE_SERIAL) + +#define TYPE_SERIAL_MM "serial-mm" +#define SERIAL_MM(s) OBJECT_CHECK(SerialMM, (s), TYPE_SERIAL_MM) + +#define TYPE_SERIAL_IO "serial-io" +#define SERIAL_IO(s) OBJECT_CHECK(SerialIO, (s), TYPE_SERIAL_IO) + +SerialMM *serial_mm_init(MemoryRegion *address_space, + hwaddr base, int regshift, + qemu_irq irq, int baudbase, + Chardev *chr, enum device_endian end); /* serial-isa.c */ diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index 77c6f05299..5bf94d28cf 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -79,7 +79,6 @@ struct TranslationBlock; * @class_by_name: Callback to map -cpu command line model name to an * instantiatable CPU type. * @parse_features: Callback to parse command line arguments. - * @reset: Callback to reset the #CPUState to its initial state. * @reset_dump_flags: #CPUDumpFlags to use for reset logging. * @has_work: Callback for checking if there is work to do. * @do_interrupt: Callback for interrupt handling. @@ -165,7 +164,6 @@ typedef struct CPUClass { ObjectClass *(*class_by_name)(const char *cpu_model); void (*parse_features)(const char *typename, char *str, Error **errp); - void (*reset)(CPUState *cpu); int reset_dump_flags; bool (*has_work)(CPUState *cpu); void (*do_interrupt)(CPUState *cpu); @@ -195,7 +193,7 @@ typedef struct CPUClass { hwaddr (*get_phys_page_attrs_debug)(CPUState *cpu, vaddr addr, MemTxAttrs *attrs); int (*asidx_from_attrs)(CPUState *cpu, MemTxAttrs attrs); - int (*gdb_read_register)(CPUState *cpu, uint8_t *buf, int reg); + int (*gdb_read_register)(CPUState *cpu, GByteArray *buf, int reg); int (*gdb_write_register)(CPUState *cpu, uint8_t *buf, int reg); bool (*debug_check_watchpoint)(CPUState *cpu, CPUWatchpoint *wp); void (*debug_excp_handler)(CPUState *cpu); diff --git a/include/hw/cris/etraxfs.h b/include/hw/cris/etraxfs.h index aa146a2cd8..403e7f95e6 100644 --- a/include/hw/cris/etraxfs.h +++ b/include/hw/cris/etraxfs.h @@ -30,23 +30,9 @@ #include "hw/qdev-properties.h" #include "hw/sysbus.h" -/* Instantiate an ETRAXFS Ethernet MAC. */ -static inline DeviceState * -etraxfs_eth_init(NICInfo *nd, hwaddr base, int phyaddr, - void *dma_out, void *dma_in) -{ - DeviceState *dev; - qemu_check_nic_model(nd, "fseth"); - - dev = qdev_create(NULL, "etraxfs-eth"); - qdev_set_nic_properties(dev, nd); - qdev_prop_set_uint32(dev, "phyaddr", phyaddr); - qdev_prop_set_ptr(dev, "dma_out", dma_out); - qdev_prop_set_ptr(dev, "dma_in", dma_in); - qdev_init_nofail(dev); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); - return dev; -} +DeviceState *etraxfs_eth_init(NICInfo *nd, hwaddr base, int phyaddr, + struct etraxfs_dma_client *dma_out, + struct etraxfs_dma_client *dma_in); static inline DeviceState *etraxfs_ser_create(hwaddr addr, qemu_irq irq, diff --git a/include/hw/display/bcm2835_fb.h b/include/hw/display/bcm2835_fb.h index 228988ba05..2246be74d8 100644 --- a/include/hw/display/bcm2835_fb.h +++ b/include/hw/display/bcm2835_fb.h @@ -5,7 +5,8 @@ * Rasperry Pi 2 emulation and refactoring Copyright (c) 2015, Microsoft * Written by Andrew Baumann * - * This code is licensed under the GNU GPLv2 and later. + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. */ #ifndef BCM2835_FB_H diff --git a/include/hw/dma/bcm2835_dma.h b/include/hw/dma/bcm2835_dma.h index 91ed8d05d1..a6747842b7 100644 --- a/include/hw/dma/bcm2835_dma.h +++ b/include/hw/dma/bcm2835_dma.h @@ -1,6 +1,8 @@ /* * Raspberry Pi emulation (c) 2012 Gregory Estrade - * This code is licensed under the GNU GPLv2 and later. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. */ #ifndef BCM2835_DMA_H diff --git a/include/hw/elf_ops.h b/include/hw/elf_ops.h index e07d276df7..e0bb47bb67 100644 --- a/include/hw/elf_ops.h +++ b/include/hw/elf_ops.h @@ -104,19 +104,21 @@ static int glue(symcmp, SZ)(const void *s0, const void *s1) : ((sym0->st_value > sym1->st_value) ? 1 : 0); } -static int glue(load_symbols, SZ)(struct elfhdr *ehdr, int fd, int must_swab, - int clear_lsb, symbol_fn_t sym_cb) +static void glue(load_symbols, SZ)(struct elfhdr *ehdr, int fd, int must_swab, + int clear_lsb, symbol_fn_t sym_cb) { - struct elf_shdr *symtab, *strtab, *shdr_table = NULL; - struct elf_sym *syms = NULL; + struct elf_shdr *symtab, *strtab; + g_autofree struct elf_shdr *shdr_table = NULL; + g_autofree struct elf_sym *syms = NULL; + g_autofree char *str = NULL; struct syminfo *s; int nsyms, i; - char *str = NULL; shdr_table = load_at(fd, ehdr->e_shoff, sizeof(struct elf_shdr) * ehdr->e_shnum); - if (!shdr_table) - return -1; + if (!shdr_table) { + return ; + } if (must_swab) { for (i = 0; i < ehdr->e_shnum; i++) { @@ -125,23 +127,25 @@ static int glue(load_symbols, SZ)(struct elfhdr *ehdr, int fd, int must_swab, } symtab = glue(find_section, SZ)(shdr_table, ehdr->e_shnum, SHT_SYMTAB); - if (!symtab) - goto fail; + if (!symtab) { + return; + } syms = load_at(fd, symtab->sh_offset, symtab->sh_size); - if (!syms) - goto fail; + if (!syms) { + return; + } nsyms = symtab->sh_size / sizeof(struct elf_sym); /* String table */ if (symtab->sh_link >= ehdr->e_shnum) { - goto fail; + return; } strtab = &shdr_table[symtab->sh_link]; str = load_at(fd, strtab->sh_offset, strtab->sh_size); if (!str) { - goto fail; + return; } i = 0; @@ -170,8 +174,13 @@ static int glue(load_symbols, SZ)(struct elfhdr *ehdr, int fd, int must_swab, } i++; } - syms = g_realloc(syms, nsyms * sizeof(*syms)); + /* check we have symbols left */ + if (nsyms == 0) { + return; + } + + syms = g_realloc(syms, nsyms * sizeof(*syms)); qsort(syms, nsyms, sizeof(*syms), glue(symcmp, SZ)); for (i = 0; i < nsyms - 1; i++) { if (syms[i].st_size == 0) { @@ -182,18 +191,11 @@ static int glue(load_symbols, SZ)(struct elfhdr *ehdr, int fd, int must_swab, /* Commit */ s = g_malloc0(sizeof(*s)); s->lookup_symbol = glue(lookup_symbol, SZ); - glue(s->disas_symtab.elf, SZ) = syms; + glue(s->disas_symtab.elf, SZ) = g_steal_pointer(&syms); s->disas_num_syms = nsyms; - s->disas_strtab = str; + s->disas_strtab = g_steal_pointer(&str); s->next = syminfos; syminfos = s; - g_free(shdr_table); - return 0; - fail: - g_free(syms); - g_free(str); - g_free(shdr_table); - return -1; } static int glue(elf_reloc, SZ)(struct elfhdr *ehdr, int fd, int must_swab, @@ -316,7 +318,8 @@ static int glue(load_elf, SZ)(const char *name, int fd, void *translate_opaque, int must_swab, uint64_t *pentry, uint64_t *lowaddr, uint64_t *highaddr, - int elf_machine, int clear_lsb, int data_swab, + uint32_t *pflags, int elf_machine, + int clear_lsb, int data_swab, AddressSpace *as, bool load_rom, symbol_fn_t sym_cb) { @@ -389,6 +392,9 @@ static int glue(load_elf, SZ)(const char *name, int fd, } } + if (pflags) { + *pflags = (elf_word)ehdr.e_flags; + } if (pentry) *pentry = (uint64_t)(elf_sword)ehdr.e_entry; diff --git a/include/hw/i2c/aspeed_i2c.h b/include/hw/i2c/aspeed_i2c.h index 13e0105918..f1b9e5bf91 100644 --- a/include/hw/i2c/aspeed_i2c.h +++ b/include/hw/i2c/aspeed_i2c.h @@ -32,6 +32,7 @@ OBJECT_CHECK(AspeedI2CState, (obj), TYPE_ASPEED_I2C) #define ASPEED_I2C_NR_BUSSES 16 +#define ASPEED_I2C_MAX_POOL_SIZE 0x800 struct AspeedI2CState; @@ -50,6 +51,9 @@ typedef struct AspeedI2CBus { uint32_t intr_status; uint32_t cmd; uint32_t buf; + uint32_t pool_ctrl; + uint32_t dma_addr; + uint32_t dma_len; } AspeedI2CBus; typedef struct AspeedI2CState { @@ -59,8 +63,13 @@ typedef struct AspeedI2CState { qemu_irq irq; uint32_t intr_status; + uint32_t ctrl_global; + MemoryRegion pool_iomem; + uint8_t pool[ASPEED_I2C_MAX_POOL_SIZE]; AspeedI2CBus busses[ASPEED_I2C_NR_BUSSES]; + MemoryRegion *dram_mr; + AddressSpace dram_as; } AspeedI2CState; #define ASPEED_I2C_CLASS(klass) \ @@ -75,6 +84,13 @@ typedef struct AspeedI2CClass { uint8_t reg_size; uint8_t gap; qemu_irq (*bus_get_irq)(AspeedI2CBus *); + + uint64_t pool_size; + hwaddr pool_base; + uint8_t *(*bus_pool_base)(AspeedI2CBus *); + bool check_sram; + bool has_dma; + } AspeedI2CClass; I2CBus *aspeed_i2c_get_bus(DeviceState *dev, int busnr); diff --git a/include/hw/i386/ich9.h b/include/hw/i386/ich9.h index 72e803f6e2..a98d10b252 100644 --- a/include/hw/i386/ich9.h +++ b/include/hw/i386/ich9.h @@ -5,7 +5,6 @@ #include "hw/sysbus.h" #include "hw/i386/pc.h" #include "hw/isa/apm.h" -#include "hw/i386/ioapic.h" #include "hw/pci/pci.h" #include "hw/pci/pcie_host.h" #include "hw/pci/pci_bridge.h" diff --git a/include/hw/i386/intel_iommu.h b/include/hw/i386/intel_iommu.h index 66b931e526..3870052f5f 100644 --- a/include/hw/i386/intel_iommu.h +++ b/include/hw/i386/intel_iommu.h @@ -22,11 +22,7 @@ #ifndef INTEL_IOMMU_H #define INTEL_IOMMU_H -#include "sysemu/dma.h" #include "hw/i386/x86-iommu.h" -#include "hw/i386/ioapic.h" -#include "hw/pci/msi.h" -#include "hw/sysbus.h" #include "qemu/iova-tree.h" #define TYPE_INTEL_IOMMU_DEVICE "intel-iommu" @@ -118,7 +114,8 @@ struct VTDAddressSpace { struct VTDBus { PCIBus* bus; /* A reference to the bus to provide translation for */ - VTDAddressSpace *dev_as[0]; /* A table of VTDAddressSpace objects indexed by devfn */ + /* A table of VTDAddressSpace objects indexed by devfn */ + VTDAddressSpace *dev_as[]; }; struct VTDIOTLBEntry { diff --git a/include/hw/i386/ioapic_internal.h b/include/hw/i386/ioapic_internal.h index d46c87c510..fe06938bda 100644 --- a/include/hw/i386/ioapic_internal.h +++ b/include/hw/i386/ioapic_internal.h @@ -23,7 +23,6 @@ #define QEMU_IOAPIC_INTERNAL_H #include "exec/memory.h" -#include "hw/i386/ioapic.h" #include "hw/sysbus.h" #include "qemu/notify.h" diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index 1f86eba3f9..6ab6eda046 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -1,22 +1,15 @@ #ifndef HW_PC_H #define HW_PC_H -#include "exec/memory.h" +#include "qemu/notify.h" +#include "qapi/qapi-types-common.h" #include "hw/boards.h" -#include "hw/isa/isa.h" #include "hw/block/fdc.h" #include "hw/block/flash.h" -#include "net/net.h" -#include "hw/i386/ioapic.h" #include "hw/i386/x86.h" -#include "qemu/range.h" -#include "qemu/bitmap.h" -#include "qemu/module.h" -#include "hw/pci/pci.h" -#include "hw/mem/pc-dimm.h" -#include "hw/mem/nvdimm.h" #include "hw/acpi/acpi_dev_interface.h" +#include "hw/hotplug.h" #define HPET_INTCAP "hpet-intcap" @@ -43,7 +36,6 @@ struct PCMachineState { /* Configuration options: */ OnOffAuto vmport; - OnOffAuto smm; bool acpi_build_enabled; bool smbus_enabled; @@ -61,7 +53,6 @@ struct PCMachineState { #define PC_MACHINE_ACPI_DEVICE_PROP "acpi-device" #define PC_MACHINE_DEVMEM_REGION_SIZE "device-memory-region-size" #define PC_MACHINE_VMPORT "vmport" -#define PC_MACHINE_SMM "smm" #define PC_MACHINE_SMBUS "smbus" #define PC_MACHINE_SATA "sata" #define PC_MACHINE_PIT "pit" @@ -134,27 +125,8 @@ typedef struct PCMachineClass { #define PC_MACHINE_CLASS(klass) \ OBJECT_CLASS_CHECK(PCMachineClass, (klass), TYPE_PC_MACHINE) -/* i8259.c */ - -extern DeviceState *isa_pic; -qemu_irq *i8259_init(ISABus *bus, qemu_irq parent_irq); -qemu_irq *kvm_i8259_init(ISABus *bus); -int pic_read_irq(DeviceState *d); -int pic_get_output(DeviceState *d); - /* ioapic.c */ -/* Global System Interrupts */ - -#define GSI_NUM_PINS IOAPIC_NUM_PINS - -typedef struct GSIState { - qemu_irq i8259_irq[ISA_NUM_IRQS]; - qemu_irq ioapic_irq[IOAPIC_NUM_PINS]; -} GSIState; - -void gsi_handler(void *opaque, int n, int level); - GSIState *pc_gsi_create(qemu_irq **irqs, bool pci_enabled); /* vmport.c */ @@ -173,7 +145,6 @@ void vmmouse_set_data(const uint32_t *data); /* pc.c */ extern int fd_bootchk; -bool pc_machine_is_smm_enabled(PCMachineState *pcms); void pc_acpi_smi_interrupt(void *opaque, int irq, int level); void pc_hot_add_cpu(MachineState *ms, const int64_t id, Error **errp); @@ -199,7 +170,6 @@ void pc_memory_init(PCMachineState *pcms, MemoryRegion *rom_memory, MemoryRegion **ram_memory); uint64_t pc_pci_hole64_start(void); -qemu_irq pc_allocate_cpu_irq(void); DeviceState *pc_vga_init(ISABus *isa_bus, PCIBus *pci_bus); void pc_basic_device_init(ISABus *isa_bus, qemu_irq *gsi, ISADevice **rtc_state, @@ -217,17 +187,14 @@ void pc_pci_device_init(PCIBus *pci_bus); typedef void (*cpu_set_smm_t)(int smm, void *arg); void pc_i8259_create(ISABus *isa_bus, qemu_irq *i8259_irqs); -void ioapic_init_gsi(GSIState *gsi_state, const char *parent_name); ISADevice *pc_find_fdc0(void); int cmos_get_fd_drive_type(FloppyDriveType fd0); -#define FW_CFG_IO_BASE 0x510 - +/* port92.c */ #define PORT92_A20_LINE "a20" -/* hpet.c */ -extern int no_hpet; +#define TYPE_PORT92 "port92" /* pc_sysfw.c */ void pc_system_flash_create(PCMachineState *pcms); @@ -237,6 +204,9 @@ void pc_system_firmware_init(PCMachineState *pcms, MemoryRegion *rom_memory); void pc_madt_cpu_entry(AcpiDeviceIf *adev, int uid, const CPUArchIdList *apic_ids, GArray *entry); +extern GlobalProperty pc_compat_4_2[]; +extern const size_t pc_compat_4_2_len; + extern GlobalProperty pc_compat_4_1[]; extern const size_t pc_compat_4_1_len; diff --git a/include/hw/i386/topology.h b/include/hw/i386/topology.h index 4ff5b2da6c..07239f95f4 100644 --- a/include/hw/i386/topology.h +++ b/include/hw/i386/topology.h @@ -45,11 +45,19 @@ */ typedef uint32_t apic_id_t; -typedef struct X86CPUTopoInfo { +typedef struct X86CPUTopoIDs { unsigned pkg_id; + unsigned node_id; unsigned die_id; unsigned core_id; unsigned smt_id; +} X86CPUTopoIDs; + +typedef struct X86CPUTopoInfo { + unsigned nodes_per_pkg; + unsigned dies_per_pkg; + unsigned cores_per_die; + unsigned threads_per_core; } X86CPUTopoInfo; /* Return the bit width needed for 'count' IDs @@ -63,120 +71,201 @@ static unsigned apicid_bitwidth_for_count(unsigned count) /* Bit width of the SMT_ID (thread ID) field on the APIC ID */ -static inline unsigned apicid_smt_width(unsigned nr_dies, - unsigned nr_cores, - unsigned nr_threads) +static inline unsigned apicid_smt_width(X86CPUTopoInfo *topo_info) { - return apicid_bitwidth_for_count(nr_threads); + return apicid_bitwidth_for_count(topo_info->threads_per_core); } /* Bit width of the Core_ID field */ -static inline unsigned apicid_core_width(unsigned nr_dies, - unsigned nr_cores, - unsigned nr_threads) +static inline unsigned apicid_core_width(X86CPUTopoInfo *topo_info) { - return apicid_bitwidth_for_count(nr_cores); + return apicid_bitwidth_for_count(topo_info->cores_per_die); } /* Bit width of the Die_ID field */ -static inline unsigned apicid_die_width(unsigned nr_dies, - unsigned nr_cores, - unsigned nr_threads) +static inline unsigned apicid_die_width(X86CPUTopoInfo *topo_info) { - return apicid_bitwidth_for_count(nr_dies); + return apicid_bitwidth_for_count(topo_info->dies_per_pkg); } +/* Bit width of the node_id field per socket */ +static inline unsigned apicid_node_width_epyc(X86CPUTopoInfo *topo_info) +{ + return apicid_bitwidth_for_count(MAX(topo_info->nodes_per_pkg, 1)); +} /* Bit offset of the Core_ID field */ -static inline unsigned apicid_core_offset(unsigned nr_dies, - unsigned nr_cores, - unsigned nr_threads) +static inline unsigned apicid_core_offset(X86CPUTopoInfo *topo_info) { - return apicid_smt_width(nr_dies, nr_cores, nr_threads); + return apicid_smt_width(topo_info); } /* Bit offset of the Die_ID field */ -static inline unsigned apicid_die_offset(unsigned nr_dies, - unsigned nr_cores, - unsigned nr_threads) +static inline unsigned apicid_die_offset(X86CPUTopoInfo *topo_info) { - return apicid_core_offset(nr_dies, nr_cores, nr_threads) + - apicid_core_width(nr_dies, nr_cores, nr_threads); + return apicid_core_offset(topo_info) + apicid_core_width(topo_info); } /* Bit offset of the Pkg_ID (socket ID) field */ -static inline unsigned apicid_pkg_offset(unsigned nr_dies, - unsigned nr_cores, - unsigned nr_threads) +static inline unsigned apicid_pkg_offset(X86CPUTopoInfo *topo_info) { - return apicid_die_offset(nr_dies, nr_cores, nr_threads) + - apicid_die_width(nr_dies, nr_cores, nr_threads); + return apicid_die_offset(topo_info) + apicid_die_width(topo_info); } +#define NODE_ID_OFFSET 3 /* Minimum node_id offset if numa configured */ + +/* + * Bit offset of the node_id field + * + * Make sure nodes_per_pkg > 0 if numa configured else zero. + */ +static inline unsigned apicid_node_offset_epyc(X86CPUTopoInfo *topo_info) +{ + unsigned offset = apicid_die_offset(topo_info) + + apicid_die_width(topo_info); + + if (topo_info->nodes_per_pkg) { + return MAX(NODE_ID_OFFSET, offset); + } else { + return offset; + } +} + +/* Bit offset of the Pkg_ID (socket ID) field */ +static inline unsigned apicid_pkg_offset_epyc(X86CPUTopoInfo *topo_info) +{ + return apicid_node_offset_epyc(topo_info) + + apicid_node_width_epyc(topo_info); +} + +/* + * Make APIC ID for the CPU based on Pkg_ID, Core_ID, SMT_ID + * + * The caller must make sure core_id < nr_cores and smt_id < nr_threads. + */ +static inline apic_id_t +x86_apicid_from_topo_ids_epyc(X86CPUTopoInfo *topo_info, + const X86CPUTopoIDs *topo_ids) +{ + return (topo_ids->pkg_id << apicid_pkg_offset_epyc(topo_info)) | + (topo_ids->node_id << apicid_node_offset_epyc(topo_info)) | + (topo_ids->die_id << apicid_die_offset(topo_info)) | + (topo_ids->core_id << apicid_core_offset(topo_info)) | + topo_ids->smt_id; +} + +static inline void x86_topo_ids_from_idx_epyc(X86CPUTopoInfo *topo_info, + unsigned cpu_index, + X86CPUTopoIDs *topo_ids) +{ + unsigned nr_nodes = MAX(topo_info->nodes_per_pkg, 1); + unsigned nr_dies = topo_info->dies_per_pkg; + unsigned nr_cores = topo_info->cores_per_die; + unsigned nr_threads = topo_info->threads_per_core; + unsigned cores_per_node = DIV_ROUND_UP((nr_dies * nr_cores * nr_threads), + nr_nodes); + + topo_ids->pkg_id = cpu_index / (nr_dies * nr_cores * nr_threads); + topo_ids->node_id = (cpu_index / cores_per_node) % nr_nodes; + topo_ids->die_id = cpu_index / (nr_cores * nr_threads) % nr_dies; + topo_ids->core_id = cpu_index / nr_threads % nr_cores; + topo_ids->smt_id = cpu_index % nr_threads; +} + +/* + * Calculate thread/core/package IDs for a specific topology, + * based on APIC ID + */ +static inline void x86_topo_ids_from_apicid_epyc(apic_id_t apicid, + X86CPUTopoInfo *topo_info, + X86CPUTopoIDs *topo_ids) +{ + topo_ids->smt_id = apicid & + ~(0xFFFFFFFFUL << apicid_smt_width(topo_info)); + topo_ids->core_id = + (apicid >> apicid_core_offset(topo_info)) & + ~(0xFFFFFFFFUL << apicid_core_width(topo_info)); + topo_ids->die_id = + (apicid >> apicid_die_offset(topo_info)) & + ~(0xFFFFFFFFUL << apicid_die_width(topo_info)); + topo_ids->node_id = + (apicid >> apicid_node_offset_epyc(topo_info)) & + ~(0xFFFFFFFFUL << apicid_node_width_epyc(topo_info)); + topo_ids->pkg_id = apicid >> apicid_pkg_offset_epyc(topo_info); +} + +/* + * Make APIC ID for the CPU 'cpu_index' + * + * 'cpu_index' is a sequential, contiguous ID for the CPU. + */ +static inline apic_id_t x86_apicid_from_cpu_idx_epyc(X86CPUTopoInfo *topo_info, + unsigned cpu_index) +{ + X86CPUTopoIDs topo_ids; + x86_topo_ids_from_idx_epyc(topo_info, cpu_index, &topo_ids); + return x86_apicid_from_topo_ids_epyc(topo_info, &topo_ids); +} /* Make APIC ID for the CPU based on Pkg_ID, Core_ID, SMT_ID * * The caller must make sure core_id < nr_cores and smt_id < nr_threads. */ -static inline apic_id_t apicid_from_topo_ids(unsigned nr_dies, - unsigned nr_cores, - unsigned nr_threads, - const X86CPUTopoInfo *topo) +static inline apic_id_t x86_apicid_from_topo_ids(X86CPUTopoInfo *topo_info, + const X86CPUTopoIDs *topo_ids) { - return (topo->pkg_id << apicid_pkg_offset(nr_dies, nr_cores, nr_threads)) | - (topo->die_id << apicid_die_offset(nr_dies, nr_cores, nr_threads)) | - (topo->core_id << apicid_core_offset(nr_dies, nr_cores, nr_threads)) | - topo->smt_id; + return (topo_ids->pkg_id << apicid_pkg_offset(topo_info)) | + (topo_ids->die_id << apicid_die_offset(topo_info)) | + (topo_ids->core_id << apicid_core_offset(topo_info)) | + topo_ids->smt_id; } /* Calculate thread/core/package IDs for a specific topology, * based on (contiguous) CPU index */ -static inline void x86_topo_ids_from_idx(unsigned nr_dies, - unsigned nr_cores, - unsigned nr_threads, +static inline void x86_topo_ids_from_idx(X86CPUTopoInfo *topo_info, unsigned cpu_index, - X86CPUTopoInfo *topo) + X86CPUTopoIDs *topo_ids) { - topo->pkg_id = cpu_index / (nr_dies * nr_cores * nr_threads); - topo->die_id = cpu_index / (nr_cores * nr_threads) % nr_dies; - topo->core_id = cpu_index / nr_threads % nr_cores; - topo->smt_id = cpu_index % nr_threads; + unsigned nr_dies = topo_info->dies_per_pkg; + unsigned nr_cores = topo_info->cores_per_die; + unsigned nr_threads = topo_info->threads_per_core; + + topo_ids->pkg_id = cpu_index / (nr_dies * nr_cores * nr_threads); + topo_ids->die_id = cpu_index / (nr_cores * nr_threads) % nr_dies; + topo_ids->core_id = cpu_index / nr_threads % nr_cores; + topo_ids->smt_id = cpu_index % nr_threads; } /* Calculate thread/core/package IDs for a specific topology, * based on APIC ID */ static inline void x86_topo_ids_from_apicid(apic_id_t apicid, - unsigned nr_dies, - unsigned nr_cores, - unsigned nr_threads, - X86CPUTopoInfo *topo) + X86CPUTopoInfo *topo_info, + X86CPUTopoIDs *topo_ids) { - topo->smt_id = apicid & - ~(0xFFFFFFFFUL << apicid_smt_width(nr_dies, nr_cores, nr_threads)); - topo->core_id = - (apicid >> apicid_core_offset(nr_dies, nr_cores, nr_threads)) & - ~(0xFFFFFFFFUL << apicid_core_width(nr_dies, nr_cores, nr_threads)); - topo->die_id = - (apicid >> apicid_die_offset(nr_dies, nr_cores, nr_threads)) & - ~(0xFFFFFFFFUL << apicid_die_width(nr_dies, nr_cores, nr_threads)); - topo->pkg_id = apicid >> apicid_pkg_offset(nr_dies, nr_cores, nr_threads); + topo_ids->smt_id = apicid & + ~(0xFFFFFFFFUL << apicid_smt_width(topo_info)); + topo_ids->core_id = + (apicid >> apicid_core_offset(topo_info)) & + ~(0xFFFFFFFFUL << apicid_core_width(topo_info)); + topo_ids->die_id = + (apicid >> apicid_die_offset(topo_info)) & + ~(0xFFFFFFFFUL << apicid_die_width(topo_info)); + topo_ids->pkg_id = apicid >> apicid_pkg_offset(topo_info); } /* Make APIC ID for the CPU 'cpu_index' * * 'cpu_index' is a sequential, contiguous ID for the CPU. */ -static inline apic_id_t x86_apicid_from_cpu_idx(unsigned nr_dies, - unsigned nr_cores, - unsigned nr_threads, +static inline apic_id_t x86_apicid_from_cpu_idx(X86CPUTopoInfo *topo_info, unsigned cpu_index) { - X86CPUTopoInfo topo; - x86_topo_ids_from_idx(nr_dies, nr_cores, nr_threads, cpu_index, &topo); - return apicid_from_topo_ids(nr_dies, nr_cores, nr_threads, &topo); + X86CPUTopoIDs topo_ids; + x86_topo_ids_from_idx(topo_info, cpu_index, &topo_ids); + return x86_apicid_from_topo_ids(topo_info, &topo_ids); } #endif /* HW_I386_TOPOLOGY_H */ diff --git a/include/hw/i386/x86.h b/include/hw/i386/x86.h index 4b84917885..b522854816 100644 --- a/include/hw/i386/x86.h +++ b/include/hw/i386/x86.h @@ -21,8 +21,11 @@ #include "exec/hwaddr.h" #include "qemu/notify.h" +#include "hw/i386/topology.h" #include "hw/boards.h" #include "hw/nmi.h" +#include "hw/isa/isa.h" +#include "hw/i386/ioapic.h" typedef struct { /*< private >*/ @@ -60,6 +63,18 @@ typedef struct { uint16_t boot_cpus; unsigned smp_dies; + OnOffAuto smm; + OnOffAuto acpi; + + /* Apic id specific handlers */ + uint32_t (*apicid_from_cpu_idx)(X86CPUTopoInfo *topo_info, + unsigned cpu_index); + void (*topo_ids_from_apicid)(apic_id_t apicid, X86CPUTopoInfo *topo_info, + X86CPUTopoIDs *topo_ids); + apic_id_t (*apicid_from_topo_ids)(X86CPUTopoInfo *topo_info, + const X86CPUTopoIDs *topo_ids); + uint32_t (*apicid_pkg_offset)(X86CPUTopoInfo *topo_info); + /* * Address space used by IOAPIC device. All IOAPIC interrupts * will be translated to MSI messages in the address space. @@ -68,6 +83,8 @@ typedef struct { } X86MachineState; #define X86_MACHINE_MAX_RAM_BELOW_4G "max-ram-below-4g" +#define X86_MACHINE_SMM "smm" +#define X86_MACHINE_ACPI "acpi" #define TYPE_X86_MACHINE MACHINE_TYPE_NAME("x86") #define X86_MACHINE(obj) \ @@ -77,6 +94,8 @@ typedef struct { #define X86_MACHINE_CLASS(class) \ OBJECT_CLASS_CHECK(X86MachineClass, class, TYPE_X86_MACHINE) +void init_topo_info(X86CPUTopoInfo *topo_info, const X86MachineState *x86ms); + uint32_t x86_cpu_apic_id_from_index(X86MachineState *pcms, unsigned int cpu_index); @@ -95,4 +114,23 @@ void x86_load_linux(X86MachineState *x86ms, bool pvh_enabled, bool linuxboot_dma_enabled); +bool x86_machine_is_smm_enabled(X86MachineState *x86ms); +bool x86_machine_is_acpi_enabled(X86MachineState *x86ms); + +/* Global System Interrupts */ + +#define GSI_NUM_PINS IOAPIC_NUM_PINS + +typedef struct GSIState { + qemu_irq i8259_irq[ISA_NUM_IRQS]; + qemu_irq ioapic_irq[IOAPIC_NUM_PINS]; +} GSIState; + +qemu_irq x86_allocate_cpu_irq(void); +void gsi_handler(void *opaque, int n, int level); +void ioapic_init_gsi(GSIState *gsi_state, const char *parent_name); + +/* hpet.c */ +extern int no_hpet; + #endif diff --git a/include/hw/ide.h b/include/hw/ide.h index 28d8a06439..c5ce5da4f4 100644 --- a/include/hw/ide.h +++ b/include/hw/ide.h @@ -2,23 +2,14 @@ #define HW_IDE_H #include "hw/isa/isa.h" -#include "hw/pci/pci.h" #include "exec/memory.h" -#define MAX_IDE_DEVS 2 - /* ide-isa.c */ ISADevice *isa_ide_init(ISABus *bus, int iobase, int iobase2, int isairq, DriveInfo *hd0, DriveInfo *hd1); /* ide-pci.c */ -void pci_cmd646_ide_init(PCIBus *bus, DriveInfo **hd_table, - int secondary_ide_enabled); -PCIDevice *pci_piix3_xen_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn); -PCIDevice *pci_piix3_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn); -PCIDevice *pci_piix4_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn); int pci_piix3_xen_ide_unplug(DeviceState *dev, bool aux); -void via_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn); /* ide-mmio.c */ void mmio_ide_init_drives(DeviceState *dev, DriveInfo *hd0, DriveInfo *hd1); diff --git a/include/hw/ide/internal.h b/include/hw/ide/internal.h index 1cf804d31d..45c94ec4b1 100644 --- a/include/hw/ide/internal.h +++ b/include/hw/ide/internal.h @@ -27,6 +27,8 @@ typedef struct IDEDMAOps IDEDMAOps; #define TYPE_IDE_BUS "IDE" #define IDE_BUS(obj) OBJECT_CHECK(IDEBus, (obj), TYPE_IDE_BUS) +#define MAX_IDE_DEVS 2 + /* Bits of HD_STATUS */ #define ERR_STAT 0x01 #define INDEX_STAT 0x02 @@ -322,11 +324,10 @@ typedef void EndTransferFunc(IDEState *); typedef void DMAStartFunc(IDEDMA *, IDEState *, BlockCompletionFunc *); typedef void DMAVoidFunc(IDEDMA *); -typedef int DMAIntFunc(IDEDMA *, int); +typedef int DMAIntFunc(IDEDMA *, bool); typedef int32_t DMAInt32Func(IDEDMA *, int32_t len); typedef void DMAu32Func(IDEDMA *, uint32_t); typedef void DMAStopFunc(IDEDMA *, bool); -typedef void DMARestartFunc(void *, int, RunState); struct unreported_events { bool eject_request; diff --git a/include/hw/ide/pci.h b/include/hw/ide/pci.h index a9f2c33e68..dd504e5a0b 100644 --- a/include/hw/ide/pci.h +++ b/include/hw/ide/pci.h @@ -2,6 +2,7 @@ #define HW_IDE_PCI_H #include "hw/ide/internal.h" +#include "hw/pci/pci.h" #define BM_STATUS_DMAING 0x01 #define BM_STATUS_ERROR 0x02 @@ -62,7 +63,7 @@ static inline IDEState *bmdma_active_if(BMDMAState *bmdma) void bmdma_init(IDEBus *bus, BMDMAState *bm, PCIIDEState *d); void bmdma_cmd_writeb(BMDMAState *bm, uint32_t val); extern MemoryRegionOps bmdma_addr_ioport_ops; -void pci_ide_create_devs(PCIDevice *dev, DriveInfo **hd_table); +void pci_ide_create_devs(PCIDevice *dev); extern const VMStateDescription vmstate_ide_pci; extern const MemoryRegionOps pci_ide_cmd_le_ops; diff --git a/include/hw/input/i8042.h b/include/hw/input/i8042.h index 246e6f3335..8eaebf50ce 100644 --- a/include/hw/input/i8042.h +++ b/include/hw/input/i8042.h @@ -14,10 +14,12 @@ #define I8042_A20_LINE "a20" +typedef struct ISAKBDState ISAKBDState; + void i8042_mm_init(qemu_irq kbd_irq, qemu_irq mouse_irq, MemoryRegion *region, ram_addr_t size, hwaddr mask); -void i8042_isa_mouse_fake_event(void *opaque); +void i8042_isa_mouse_fake_event(ISAKBDState *isa); void i8042_setup_a20_line(ISADevice *dev, qemu_irq a20_out); #endif /* HW_INPUT_I8042_H */ diff --git a/include/hw/input/lasips2.h b/include/hw/input/lasips2.h new file mode 100644 index 0000000000..0cd7b59064 --- /dev/null +++ b/include/hw/input/lasips2.h @@ -0,0 +1,16 @@ +/* + * QEMU LASI PS/2 emulation + * + * Copyright (c) 2019 Sven Schnelle + * + */ +#ifndef HW_INPUT_LASIPS2_H +#define HW_INPUT_LASIPS2_H + +#include "exec/hwaddr.h" + +#define TYPE_LASIPS2 "lasips2" + +void lasips2_init(MemoryRegion *address_space, hwaddr base, qemu_irq irq); + +#endif /* HW_INPUT_LASIPS2_H */ diff --git a/include/hw/input/ps2.h b/include/hw/input/ps2.h index b60455d4f6..35d983897a 100644 --- a/include/hw/input/ps2.h +++ b/include/hw/input/ps2.h @@ -47,5 +47,6 @@ void ps2_queue_3(PS2State *s, int b1, int b2, int b3); void ps2_queue_4(PS2State *s, int b1, int b2, int b3, int b4); void ps2_keyboard_set_translation(void *opaque, int mode); void ps2_mouse_fake_event(void *opaque); +int ps2_queue_empty(PS2State *s); #endif /* HW_PS2_H */ diff --git a/include/hw/intc/arm_gic.h b/include/hw/intc/arm_gic.h index ed703a1720..303b9748cb 100644 --- a/include/hw/intc/arm_gic.h +++ b/include/hw/intc/arm_gic.h @@ -68,6 +68,8 @@ /* Number of SGI target-list bits */ #define GIC_TARGETLIST_BITS 8 +#define GIC_MAX_PRIORITY_BITS 8 +#define GIC_MIN_PRIORITY_BITS 4 #define TYPE_ARM_GIC "arm_gic" #define ARM_GIC(obj) \ diff --git a/include/hw/intc/arm_gic_common.h b/include/hw/intc/arm_gic_common.h index b5585fec45..6e0d6b8a88 100644 --- a/include/hw/intc/arm_gic_common.h +++ b/include/hw/intc/arm_gic_common.h @@ -96,6 +96,7 @@ typedef struct GICState { uint16_t priority_mask[GIC_NCPU_VCPU]; uint16_t running_priority[GIC_NCPU_VCPU]; uint16_t current_pending[GIC_NCPU_VCPU]; + uint32_t n_prio_bits; /* If we present the GICv2 without security extensions to a guest, * the guest can configure the GICC_CTLR to configure group 1 binary point diff --git a/include/hw/intc/bcm2835_ic.h b/include/hw/intc/bcm2835_ic.h index fb75fa0064..392ded1cb3 100644 --- a/include/hw/intc/bcm2835_ic.h +++ b/include/hw/intc/bcm2835_ic.h @@ -1,6 +1,8 @@ /* * Raspberry Pi emulation (c) 2012 Gregory Estrade - * This code is licensed under the GNU GPLv2 and later. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. */ #ifndef BCM2835_IC_H diff --git a/include/hw/intc/bcm2836_control.h b/include/hw/intc/bcm2836_control.h index de061b8929..2c22405686 100644 --- a/include/hw/intc/bcm2836_control.h +++ b/include/hw/intc/bcm2836_control.h @@ -8,7 +8,8 @@ * ARM Local Timer IRQ Copyright (c) 2019. Zoltán Baldaszti * Added basic IRQ_TIMER interrupt support * - * This code is licensed under the GNU GPLv2 and later. + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. */ #ifndef BCM2836_CONTROL_H diff --git a/include/hw/intc/i8259.h b/include/hw/intc/i8259.h new file mode 100644 index 0000000000..e2b1e8c59a --- /dev/null +++ b/include/hw/intc/i8259.h @@ -0,0 +1,12 @@ +#ifndef HW_I8259_H +#define HW_I8259_H + +/* i8259.c */ + +extern DeviceState *isa_pic; +qemu_irq *i8259_init(ISABus *bus, qemu_irq parent_irq); +qemu_irq *kvm_i8259_init(ISABus *bus); +int pic_get_output(DeviceState *d); +int pic_read_irq(DeviceState *d); + +#endif diff --git a/include/hw/ipmi/ipmi.h b/include/hw/ipmi/ipmi.h index 6f2413b39b..8a99d958bb 100644 --- a/include/hw/ipmi/ipmi.h +++ b/include/hw/ipmi/ipmi.h @@ -55,6 +55,7 @@ enum ipmi_op { #define IPMI_CC_COMMAND_NOT_SUPPORTED 0xd5 #define IPMI_NETFN_APP 0x06 +#define IPMI_NETFN_OEM 0x3a #define IPMI_DEBUG 1 @@ -265,4 +266,45 @@ int ipmi_bmc_sdr_find(IPMIBmc *b, uint16_t recid, const struct ipmi_sdr_compact **sdr, uint16_t *nextrec); void ipmi_bmc_gen_event(IPMIBmc *b, uint8_t *evt, bool log); +#define TYPE_IPMI_BMC_SIMULATOR "ipmi-bmc-sim" +#define IPMI_BMC_SIMULATOR(obj) OBJECT_CHECK(IPMIBmcSim, (obj), \ + TYPE_IPMI_BMC_SIMULATOR) + +typedef struct IPMIBmcSim IPMIBmcSim; + +typedef struct RspBuffer { + uint8_t buffer[MAX_IPMI_MSG_SIZE]; + unsigned int len; +} RspBuffer; + +static inline void rsp_buffer_set_error(RspBuffer *rsp, uint8_t byte) +{ + rsp->buffer[2] = byte; +} + +/* Add a byte to the response. */ +static inline void rsp_buffer_push(RspBuffer *rsp, uint8_t byte) +{ + if (rsp->len >= sizeof(rsp->buffer)) { + rsp_buffer_set_error(rsp, IPMI_CC_REQUEST_DATA_TRUNCATED); + return; + } + rsp->buffer[rsp->len++] = byte; +} + +typedef struct IPMICmdHandler { + void (*cmd_handler)(IPMIBmcSim *s, + uint8_t *cmd, unsigned int cmd_len, + RspBuffer *rsp); + unsigned int cmd_len_min; +} IPMICmdHandler; + +typedef struct IPMINetfn { + unsigned int cmd_nums; + const IPMICmdHandler *cmd_handlers; +} IPMINetfn; + +int ipmi_sim_register_netfn(IPMIBmcSim *s, unsigned int netfn, + const IPMINetfn *netfnd); + #endif diff --git a/include/hw/isa/i8259_internal.h b/include/hw/isa/i8259_internal.h index ee189e4a77..861d70d8f8 100644 --- a/include/hw/isa/i8259_internal.h +++ b/include/hw/isa/i8259_internal.h @@ -25,9 +25,9 @@ #ifndef QEMU_I8259_INTERNAL_H #define QEMU_I8259_INTERNAL_H -#include "hw/i386/pc.h" #include "hw/isa/isa.h" #include "hw/intc/intc.h" +#include "hw/intc/i8259.h" typedef struct PICCommonState PICCommonState; diff --git a/include/hw/isa/isa.h b/include/hw/isa/isa.h index 79f703fd6c..59a4d4b50a 100644 --- a/include/hw/isa/isa.h +++ b/include/hw/isa/isa.h @@ -56,7 +56,6 @@ typedef int (*IsaDmaTransferHandler)(void *opaque, int nchan, int pos, typedef struct IsaDmaClass { InterfaceClass parent; - IsaDmaTransferMode (*get_transfer_mode)(IsaDma *obj, int nchan); bool (*has_autoinitialization)(IsaDma *obj, int nchan); int (*read_memory)(IsaDma *obj, int nchan, void *buf, int pos, int len); int (*write_memory)(IsaDma *obj, int nchan, void *buf, int pos, int len); @@ -88,7 +87,7 @@ struct ISADevice { DeviceState parent_obj; /*< public >*/ - uint32_t isairq[2]; + int8_t isairq[2]; /* -1 = unassigned */ int nirqs; int ioport_id; }; @@ -96,9 +95,9 @@ struct ISADevice { ISABus *isa_bus_new(DeviceState *dev, MemoryRegion *address_space, MemoryRegion *address_space_io, Error **errp); void isa_bus_irqs(ISABus *bus, qemu_irq *irqs); -qemu_irq isa_get_irq(ISADevice *dev, int isairq); -void isa_init_irq(ISADevice *dev, qemu_irq *p, int isairq); -void isa_connect_gpio_out(ISADevice *isadev, int gpioirq, int isairq); +qemu_irq isa_get_irq(ISADevice *dev, unsigned isairq); +void isa_init_irq(ISADevice *dev, qemu_irq *p, unsigned isairq); +void isa_connect_gpio_out(ISADevice *isadev, int gpioirq, unsigned isairq); void isa_bus_dma(ISABus *bus, IsaDma *dma8, IsaDma *dma16); IsaDma *isa_get_dma(ISABus *bus, int nchan); MemoryRegion *isa_address_space(ISADevice *dev); diff --git a/include/hw/isa/superio.h b/include/hw/isa/superio.h index b151dcd753..147cc0a7b7 100644 --- a/include/hw/isa/superio.h +++ b/include/hw/isa/superio.h @@ -3,7 +3,7 @@ * * Copyright (c) 2018 Philippe Mathieu-Daudé * - * This code is licensed under the GNU GPLv2 and later. + * This work is licensed under the terms of the GNU GPL, version 2 or later. * See the COPYING file in the top-level directory. * SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/include/hw/loader.h b/include/hw/loader.h index 48a96cd559..a9eeea3952 100644 --- a/include/hw/loader.h +++ b/include/hw/loader.h @@ -101,6 +101,7 @@ const char *load_elf_strerror(int error); * @pentry: Populated with program entry point. Ignored if NULL. * @lowaddr: Populated with lowest loaded address. Ignored if NULL. * @highaddr: Populated with highest loaded address. Ignored if NULL. + * @pflags: Populated with ELF processor-specific flags. Ignore if NULL. * @bigendian: Expected ELF endianness. 0 for LE otherwise BE * @elf_machine: Expected ELF machine type * @clear_lsb: Set to mask off LSB of addresses (Some architectures use @@ -131,8 +132,9 @@ int load_elf_ram_sym(const char *filename, uint64_t (*elf_note_fn)(void *, void *, bool), uint64_t (*translate_fn)(void *, uint64_t), void *translate_opaque, uint64_t *pentry, - uint64_t *lowaddr, uint64_t *highaddr, int big_endian, - int elf_machine, int clear_lsb, int data_swab, + uint64_t *lowaddr, uint64_t *highaddr, uint32_t *pflags, + int big_endian, int elf_machine, + int clear_lsb, int data_swab, AddressSpace *as, bool load_rom, symbol_fn_t sym_cb); /** load_elf_ram: @@ -143,9 +145,9 @@ int load_elf_ram(const char *filename, uint64_t (*elf_note_fn)(void *, void *, bool), uint64_t (*translate_fn)(void *, uint64_t), void *translate_opaque, uint64_t *pentry, uint64_t *lowaddr, - uint64_t *highaddr, int big_endian, int elf_machine, - int clear_lsb, int data_swab, AddressSpace *as, - bool load_rom); + uint64_t *highaddr, uint32_t *pflags, int big_endian, + int elf_machine, int clear_lsb, int data_swab, + AddressSpace *as, bool load_rom); /** load_elf_as: * Same as load_elf_ram(), but always loads the elf as ROM @@ -154,8 +156,9 @@ int load_elf_as(const char *filename, uint64_t (*elf_note_fn)(void *, void *, bool), uint64_t (*translate_fn)(void *, uint64_t), void *translate_opaque, uint64_t *pentry, uint64_t *lowaddr, - uint64_t *highaddr, int big_endian, int elf_machine, - int clear_lsb, int data_swab, AddressSpace *as); + uint64_t *highaddr, uint32_t *pflags, int big_endian, + int elf_machine, int clear_lsb, int data_swab, + AddressSpace *as); /** load_elf: * Same as load_elf_as(), but doesn't allow the caller to specify an @@ -165,8 +168,8 @@ int load_elf(const char *filename, uint64_t (*elf_note_fn)(void *, void *, bool), uint64_t (*translate_fn)(void *, uint64_t), void *translate_opaque, uint64_t *pentry, uint64_t *lowaddr, - uint64_t *highaddr, int big_endian, int elf_machine, - int clear_lsb, int data_swab); + uint64_t *highaddr, uint32_t *pflags, int big_endian, + int elf_machine, int clear_lsb, int data_swab); /** load_elf_hdr: * @filename: Path of ELF file diff --git a/include/hw/mem/nvdimm.h b/include/hw/mem/nvdimm.h index 523a9b3d4a..4807ca615b 100644 --- a/include/hw/mem/nvdimm.h +++ b/include/hw/mem/nvdimm.h @@ -25,6 +25,7 @@ #include "hw/mem/pc-dimm.h" #include "hw/acpi/bios-linker-loader.h" +#include "qemu/uuid.h" #define NVDIMM_DEBUG 0 #define nvdimm_debug(fmt, ...) \ @@ -49,6 +50,7 @@ TYPE_NVDIMM) #define NVDIMM_LABEL_SIZE_PROP "label-size" +#define NVDIMM_UUID_PROP "uuid" #define NVDIMM_UNARMED_PROP "unarmed" struct NVDIMMDevice { @@ -83,6 +85,11 @@ struct NVDIMMDevice { * the guest write persistence. */ bool unarmed; + + /* + * The PPC64 - spapr requires each nvdimm device have a uuid. + */ + QemuUUID uuid; }; typedef struct NVDIMMDevice NVDIMMDevice; diff --git a/include/hw/misc/allwinner-cpucfg.h b/include/hw/misc/allwinner-cpucfg.h new file mode 100644 index 0000000000..2c3693a8be --- /dev/null +++ b/include/hw/misc/allwinner-cpucfg.h @@ -0,0 +1,52 @@ +/* + * Allwinner CPU Configuration Module emulation + * + * Copyright (C) 2019 Niek Linnenbank + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef HW_MISC_ALLWINNER_CPUCFG_H +#define HW_MISC_ALLWINNER_CPUCFG_H + +#include "qom/object.h" +#include "hw/sysbus.h" + +/** + * Object model + * @{ + */ + +#define TYPE_AW_CPUCFG "allwinner-cpucfg" +#define AW_CPUCFG(obj) \ + OBJECT_CHECK(AwCpuCfgState, (obj), TYPE_AW_CPUCFG) + +/** @} */ + +/** + * Allwinner CPU Configuration Module instance state + */ +typedef struct AwCpuCfgState { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + + MemoryRegion iomem; + uint32_t gen_ctrl; + uint32_t super_standby; + uint32_t entry_addr; + +} AwCpuCfgState; + +#endif /* HW_MISC_ALLWINNER_CPUCFG_H */ diff --git a/include/hw/misc/allwinner-h3-ccu.h b/include/hw/misc/allwinner-h3-ccu.h new file mode 100644 index 0000000000..eec59649f3 --- /dev/null +++ b/include/hw/misc/allwinner-h3-ccu.h @@ -0,0 +1,66 @@ +/* + * Allwinner H3 Clock Control Unit emulation + * + * Copyright (C) 2019 Niek Linnenbank + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef HW_MISC_ALLWINNER_H3_CCU_H +#define HW_MISC_ALLWINNER_H3_CCU_H + +#include "qom/object.h" +#include "hw/sysbus.h" + +/** + * @name Constants + * @{ + */ + +/** Size of register I/O address space used by CCU device */ +#define AW_H3_CCU_IOSIZE (0x400) + +/** Total number of known registers */ +#define AW_H3_CCU_REGS_NUM (AW_H3_CCU_IOSIZE / sizeof(uint32_t)) + +/** @} */ + +/** + * @name Object model + * @{ + */ + +#define TYPE_AW_H3_CCU "allwinner-h3-ccu" +#define AW_H3_CCU(obj) \ + OBJECT_CHECK(AwH3ClockCtlState, (obj), TYPE_AW_H3_CCU) + +/** @} */ + +/** + * Allwinner H3 CCU object instance state. + */ +typedef struct AwH3ClockCtlState { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + + /** Maps I/O registers in physical memory */ + MemoryRegion iomem; + + /** Array of hardware registers */ + uint32_t regs[AW_H3_CCU_REGS_NUM]; + +} AwH3ClockCtlState; + +#endif /* HW_MISC_ALLWINNER_H3_CCU_H */ diff --git a/include/hw/misc/allwinner-h3-dramc.h b/include/hw/misc/allwinner-h3-dramc.h new file mode 100644 index 0000000000..bacdf236b7 --- /dev/null +++ b/include/hw/misc/allwinner-h3-dramc.h @@ -0,0 +1,106 @@ +/* + * Allwinner H3 SDRAM Controller emulation + * + * Copyright (C) 2019 Niek Linnenbank + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef HW_MISC_ALLWINNER_H3_DRAMC_H +#define HW_MISC_ALLWINNER_H3_DRAMC_H + +#include "qom/object.h" +#include "hw/sysbus.h" +#include "exec/hwaddr.h" + +/** + * Constants + * @{ + */ + +/** Highest register address used by DRAMCOM module */ +#define AW_H3_DRAMCOM_REGS_MAXADDR (0x804) + +/** Total number of known DRAMCOM registers */ +#define AW_H3_DRAMCOM_REGS_NUM (AW_H3_DRAMCOM_REGS_MAXADDR / \ + sizeof(uint32_t)) + +/** Highest register address used by DRAMCTL module */ +#define AW_H3_DRAMCTL_REGS_MAXADDR (0x88c) + +/** Total number of known DRAMCTL registers */ +#define AW_H3_DRAMCTL_REGS_NUM (AW_H3_DRAMCTL_REGS_MAXADDR / \ + sizeof(uint32_t)) + +/** Highest register address used by DRAMPHY module */ +#define AW_H3_DRAMPHY_REGS_MAXADDR (0x4) + +/** Total number of known DRAMPHY registers */ +#define AW_H3_DRAMPHY_REGS_NUM (AW_H3_DRAMPHY_REGS_MAXADDR / \ + sizeof(uint32_t)) + +/** @} */ + +/** + * Object model + * @{ + */ + +#define TYPE_AW_H3_DRAMC "allwinner-h3-dramc" +#define AW_H3_DRAMC(obj) \ + OBJECT_CHECK(AwH3DramCtlState, (obj), TYPE_AW_H3_DRAMC) + +/** @} */ + +/** + * Allwinner H3 SDRAM Controller object instance state. + */ +typedef struct AwH3DramCtlState { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + + /** Physical base address for start of RAM */ + hwaddr ram_addr; + + /** Total RAM size in megabytes */ + uint32_t ram_size; + + /** + * @name Memory Regions + * @{ + */ + + MemoryRegion row_mirror; /**< Simulates rows for RAM size detection */ + MemoryRegion row_mirror_alias; /**< Alias of the row which is mirrored */ + MemoryRegion dramcom_iomem; /**< DRAMCOM module I/O registers */ + MemoryRegion dramctl_iomem; /**< DRAMCTL module I/O registers */ + MemoryRegion dramphy_iomem; /**< DRAMPHY module I/O registers */ + + /** @} */ + + /** + * @name Hardware Registers + * @{ + */ + + uint32_t dramcom[AW_H3_DRAMCOM_REGS_NUM]; /**< Array of DRAMCOM registers */ + uint32_t dramctl[AW_H3_DRAMCTL_REGS_NUM]; /**< Array of DRAMCTL registers */ + uint32_t dramphy[AW_H3_DRAMPHY_REGS_NUM] ;/**< Array of DRAMPHY registers */ + + /** @} */ + +} AwH3DramCtlState; + +#endif /* HW_MISC_ALLWINNER_H3_DRAMC_H */ diff --git a/include/hw/misc/allwinner-h3-sysctrl.h b/include/hw/misc/allwinner-h3-sysctrl.h new file mode 100644 index 0000000000..af4119e026 --- /dev/null +++ b/include/hw/misc/allwinner-h3-sysctrl.h @@ -0,0 +1,67 @@ +/* + * Allwinner H3 System Control emulation + * + * Copyright (C) 2019 Niek Linnenbank + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef HW_MISC_ALLWINNER_H3_SYSCTRL_H +#define HW_MISC_ALLWINNER_H3_SYSCTRL_H + +#include "qom/object.h" +#include "hw/sysbus.h" + +/** + * @name Constants + * @{ + */ + +/** Highest register address used by System Control device */ +#define AW_H3_SYSCTRL_REGS_MAXADDR (0x30) + +/** Total number of known registers */ +#define AW_H3_SYSCTRL_REGS_NUM ((AW_H3_SYSCTRL_REGS_MAXADDR / \ + sizeof(uint32_t)) + 1) + +/** @} */ + +/** + * @name Object model + * @{ + */ + +#define TYPE_AW_H3_SYSCTRL "allwinner-h3-sysctrl" +#define AW_H3_SYSCTRL(obj) \ + OBJECT_CHECK(AwH3SysCtrlState, (obj), TYPE_AW_H3_SYSCTRL) + +/** @} */ + +/** + * Allwinner H3 System Control object instance state + */ +typedef struct AwH3SysCtrlState { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + + /** Maps I/O registers in physical memory */ + MemoryRegion iomem; + + /** Array of hardware registers */ + uint32_t regs[AW_H3_SYSCTRL_REGS_NUM]; + +} AwH3SysCtrlState; + +#endif /* HW_MISC_ALLWINNER_H3_SYSCTRL_H */ diff --git a/include/hw/misc/allwinner-sid.h b/include/hw/misc/allwinner-sid.h new file mode 100644 index 0000000000..4c1fa4762b --- /dev/null +++ b/include/hw/misc/allwinner-sid.h @@ -0,0 +1,60 @@ +/* + * Allwinner Security ID emulation + * + * Copyright (C) 2019 Niek Linnenbank + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef HW_MISC_ALLWINNER_SID_H +#define HW_MISC_ALLWINNER_SID_H + +#include "qom/object.h" +#include "hw/sysbus.h" +#include "qemu/uuid.h" + +/** + * Object model + * @{ + */ + +#define TYPE_AW_SID "allwinner-sid" +#define AW_SID(obj) \ + OBJECT_CHECK(AwSidState, (obj), TYPE_AW_SID) + +/** @} */ + +/** + * Allwinner Security ID object instance state + */ +typedef struct AwSidState { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + + /** Maps I/O registers in physical memory */ + MemoryRegion iomem; + + /** Control register defines how and what to read */ + uint32_t control; + + /** RdKey register contains the data retrieved by the device */ + uint32_t rdkey; + + /** Stores the emulated device identifier */ + QemuUUID identifier; + +} AwSidState; + +#endif /* HW_MISC_ALLWINNER_SID_H */ diff --git a/include/hw/misc/aspeed_sdmc.h b/include/hw/misc/aspeed_sdmc.h index 5dbde59fe7..cea1e67fe3 100644 --- a/include/hw/misc/aspeed_sdmc.h +++ b/include/hw/misc/aspeed_sdmc.h @@ -40,6 +40,7 @@ typedef struct AspeedSDMCClass { SysBusDeviceClass parent_class; uint64_t max_ram_size; + const uint64_t *valid_ram_sizes; uint32_t (*compute_conf)(AspeedSDMCState *s, uint32_t data); void (*write)(AspeedSDMCState *s, uint32_t reg, uint32_t data); } AspeedSDMCClass; diff --git a/include/hw/misc/bcm2835_mbox.h b/include/hw/misc/bcm2835_mbox.h index 7e8f3ce86d..57f95cc35e 100644 --- a/include/hw/misc/bcm2835_mbox.h +++ b/include/hw/misc/bcm2835_mbox.h @@ -1,6 +1,8 @@ /* * Raspberry Pi emulation (c) 2012 Gregory Estrade - * This code is licensed under the GNU GPLv2 and later. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. */ #ifndef BCM2835_MBOX_H diff --git a/include/hw/misc/bcm2835_mbox_defs.h b/include/hw/misc/bcm2835_mbox_defs.h index a18e520b22..9670bf33a0 100644 --- a/include/hw/misc/bcm2835_mbox_defs.h +++ b/include/hw/misc/bcm2835_mbox_defs.h @@ -1,6 +1,8 @@ /* * Raspberry Pi emulation (c) 2012 Gregory Estrade - * This code is licensed under the GNU GPLv2 and later. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. */ #ifndef BCM2835_MBOX_DEFS_H diff --git a/include/hw/misc/bcm2835_property.h b/include/hw/misc/bcm2835_property.h index 11be0dbeac..b321f22499 100644 --- a/include/hw/misc/bcm2835_property.h +++ b/include/hw/misc/bcm2835_property.h @@ -1,6 +1,8 @@ /* * Raspberry Pi emulation (c) 2012 Gregory Estrade - * This code is licensed under the GNU GPLv2 and later. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. */ #ifndef BCM2835_PROPERTY_H diff --git a/include/hw/misc/imx_rngc.h b/include/hw/misc/imx_rngc.h new file mode 100644 index 0000000000..f0d2b44d4f --- /dev/null +++ b/include/hw/misc/imx_rngc.h @@ -0,0 +1,35 @@ +/* + * Freescale i.MX RNGC emulation + * + * Copyright (C) 2020 Martin Kaiser + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef IMX_RNGC_H +#define IMX_RNGC_H + +#include "hw/sysbus.h" + +#define TYPE_IMX_RNGC "imx.rngc" +#define IMX_RNGC(obj) OBJECT_CHECK(IMXRNGCState, (obj), TYPE_IMX_RNGC) + +typedef struct IMXRNGCState { + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + MemoryRegion iomem; + + uint8_t op_self_test; + uint8_t op_seed; + uint8_t mask; + bool auto_seed; + + QEMUBH *self_test_bh; + QEMUBH *seed_bh; + qemu_irq irq; +} IMXRNGCState; + +#endif /* IMX_RNGC_H */ diff --git a/include/hw/misc/mac_via.h b/include/hw/misc/mac_via.h index 3f86fcb7e1..e74f85be0f 100644 --- a/include/hw/misc/mac_via.h +++ b/include/hw/misc/mac_via.h @@ -81,6 +81,8 @@ typedef struct MOS6522Q800VIA2State { typedef struct MacVIAState { SysBusDevice busdev; + VMChangeStateEntry *vmstate; + /* MMIO */ MemoryRegion mmio; MemoryRegion via1mem; @@ -100,6 +102,7 @@ typedef struct MacVIAState { uint8_t cmd; int wprotect; int alt; + BlockBackend *blk; /* ADB */ ADBBusState adb_bus; diff --git a/include/hw/misc/macio/macio.h b/include/hw/misc/macio/macio.h index 070a694eb5..87335a991c 100644 --- a/include/hw/misc/macio/macio.h +++ b/include/hw/misc/macio/macio.h @@ -27,6 +27,7 @@ #define MACIO_H #include "hw/char/escc.h" +#include "hw/pci/pci.h" #include "hw/ide/internal.h" #include "hw/intc/heathrow_pic.h" #include "hw/misc/macio/cuda.h" diff --git a/include/hw/misc/mos6522.h b/include/hw/misc/mos6522.h index 493c907537..97384c6e02 100644 --- a/include/hw/misc/mos6522.h +++ b/include/hw/misc/mos6522.h @@ -115,7 +115,6 @@ typedef struct MOS6522State { uint8_t pcr; uint8_t ifr; uint8_t ier; - uint8_t anh; MOS6522Timer timers[2]; uint64_t frequency; diff --git a/include/hw/misc/stm32f4xx_exti.h b/include/hw/misc/stm32f4xx_exti.h new file mode 100644 index 0000000000..707036a41b --- /dev/null +++ b/include/hw/misc/stm32f4xx_exti.h @@ -0,0 +1,60 @@ +/* + * STM32F4XX EXTI + * + * Copyright (c) 2014 Alistair Francis + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef HW_STM_EXTI_H +#define HW_STM_EXTI_H + +#include "hw/sysbus.h" +#include "hw/hw.h" + +#define EXTI_IMR 0x00 +#define EXTI_EMR 0x04 +#define EXTI_RTSR 0x08 +#define EXTI_FTSR 0x0C +#define EXTI_SWIER 0x10 +#define EXTI_PR 0x14 + +#define TYPE_STM32F4XX_EXTI "stm32f4xx-exti" +#define STM32F4XX_EXTI(obj) \ + OBJECT_CHECK(STM32F4xxExtiState, (obj), TYPE_STM32F4XX_EXTI) + +#define NUM_GPIO_EVENT_IN_LINES 16 +#define NUM_INTERRUPT_OUT_LINES 16 + +typedef struct { + SysBusDevice parent_obj; + + MemoryRegion mmio; + + uint32_t exti_imr; + uint32_t exti_emr; + uint32_t exti_rtsr; + uint32_t exti_ftsr; + uint32_t exti_swier; + uint32_t exti_pr; + + qemu_irq irq[NUM_INTERRUPT_OUT_LINES]; +} STM32F4xxExtiState; + +#endif diff --git a/include/hw/misc/stm32f4xx_syscfg.h b/include/hw/misc/stm32f4xx_syscfg.h new file mode 100644 index 0000000000..c62c6629e5 --- /dev/null +++ b/include/hw/misc/stm32f4xx_syscfg.h @@ -0,0 +1,61 @@ +/* + * STM32F4xx SYSCFG + * + * Copyright (c) 2014 Alistair Francis + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef HW_STM_SYSCFG_H +#define HW_STM_SYSCFG_H + +#include "hw/sysbus.h" +#include "hw/hw.h" + +#define SYSCFG_MEMRMP 0x00 +#define SYSCFG_PMC 0x04 +#define SYSCFG_EXTICR1 0x08 +#define SYSCFG_EXTICR2 0x0C +#define SYSCFG_EXTICR3 0x10 +#define SYSCFG_EXTICR4 0x14 +#define SYSCFG_CMPCR 0x20 + +#define TYPE_STM32F4XX_SYSCFG "stm32f4xx-syscfg" +#define STM32F4XX_SYSCFG(obj) \ + OBJECT_CHECK(STM32F4xxSyscfgState, (obj), TYPE_STM32F4XX_SYSCFG) + +#define SYSCFG_NUM_EXTICR 4 + +typedef struct { + /* */ + SysBusDevice parent_obj; + + /* */ + MemoryRegion mmio; + + uint32_t syscfg_memrmp; + uint32_t syscfg_pmc; + uint32_t syscfg_exticr[SYSCFG_NUM_EXTICR]; + uint32_t syscfg_cmpcr; + + qemu_irq irq; + qemu_irq gpio_out[16]; +} STM32F4xxSyscfgState; + +#endif diff --git a/include/hw/net/allwinner-sun8i-emac.h b/include/hw/net/allwinner-sun8i-emac.h new file mode 100644 index 0000000000..eda034e96b --- /dev/null +++ b/include/hw/net/allwinner-sun8i-emac.h @@ -0,0 +1,99 @@ +/* + * Allwinner Sun8i Ethernet MAC emulation + * + * Copyright (C) 2019 Niek Linnenbank + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef HW_NET_ALLWINNER_SUN8I_EMAC_H +#define HW_NET_ALLWINNER_SUN8I_EMAC_H + +#include "qom/object.h" +#include "net/net.h" +#include "hw/sysbus.h" + +/** + * Object model + * @{ + */ + +#define TYPE_AW_SUN8I_EMAC "allwinner-sun8i-emac" +#define AW_SUN8I_EMAC(obj) \ + OBJECT_CHECK(AwSun8iEmacState, (obj), TYPE_AW_SUN8I_EMAC) + +/** @} */ + +/** + * Allwinner Sun8i EMAC object instance state + */ +typedef struct AwSun8iEmacState { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + + /** Maps I/O registers in physical memory */ + MemoryRegion iomem; + + /** Interrupt output signal to notify CPU */ + qemu_irq irq; + + /** Generic Network Interface Controller (NIC) for networking API */ + NICState *nic; + + /** Generic Network Interface Controller (NIC) configuration */ + NICConf conf; + + /** + * @name Media Independent Interface (MII) + * @{ + */ + + uint8_t mii_phy_addr; /**< PHY address */ + uint32_t mii_cr; /**< Control */ + uint32_t mii_st; /**< Status */ + uint32_t mii_adv; /**< Advertised Abilities */ + + /** @} */ + + /** + * @name Hardware Registers + * @{ + */ + + uint32_t basic_ctl0; /**< Basic Control 0 */ + uint32_t basic_ctl1; /**< Basic Control 1 */ + uint32_t int_en; /**< Interrupt Enable */ + uint32_t int_sta; /**< Interrupt Status */ + uint32_t frm_flt; /**< Receive Frame Filter */ + + uint32_t rx_ctl0; /**< Receive Control 0 */ + uint32_t rx_ctl1; /**< Receive Control 1 */ + uint32_t rx_desc_head; /**< Receive Descriptor List Address */ + uint32_t rx_desc_curr; /**< Current Receive Descriptor Address */ + + uint32_t tx_ctl0; /**< Transmit Control 0 */ + uint32_t tx_ctl1; /**< Transmit Control 1 */ + uint32_t tx_desc_head; /**< Transmit Descriptor List Address */ + uint32_t tx_desc_curr; /**< Current Transmit Descriptor Address */ + uint32_t tx_flowctl; /**< Transmit Flow Control */ + + uint32_t mii_cmd; /**< Management Interface Command */ + uint32_t mii_data; /**< Management Interface Data */ + + /** @} */ + +} AwSun8iEmacState; + +#endif /* HW_NET_ALLWINNER_SUN8I_H */ diff --git a/include/hw/net/lasi_82596.h b/include/hw/net/lasi_82596.h new file mode 100644 index 0000000000..e76ef8308e --- /dev/null +++ b/include/hw/net/lasi_82596.h @@ -0,0 +1,29 @@ +/* + * QEMU LASI i82596 device emulation + * + * Copyright (c) 201 Helge Deller + * + */ + +#ifndef LASI_82596_H +#define LASI_82596_H + +#include "net/net.h" +#include "hw/net/i82596.h" + +#define TYPE_LASI_82596 "lasi_82596" +#define SYSBUS_I82596(obj) \ + OBJECT_CHECK(SysBusI82596State, (obj), TYPE_LASI_82596) + +typedef struct { + SysBusDevice parent_obj; + + I82596State state; + uint16_t last_val; + int val_index:1; +} SysBusI82596State; + +SysBusI82596State *lasi_82596_init(MemoryRegion *addr_space, + hwaddr hpa, qemu_irq irq); + +#endif diff --git a/include/hw/nmi.h b/include/hw/nmi.h index a1e128724e..fe37ce3ad8 100644 --- a/include/hw/nmi.h +++ b/include/hw/nmi.h @@ -31,7 +31,7 @@ #define NMI_GET_CLASS(obj) \ OBJECT_GET_CLASS(NMIClass, (obj), TYPE_NMI) #define NMI(obj) \ - INTERFACE_CHECK(NMI, (obj), TYPE_NMI) + INTERFACE_CHECK(NMIState, (obj), TYPE_NMI) typedef struct NMIState NMIState; diff --git a/include/hw/nvram/fw_cfg.h b/include/hw/nvram/fw_cfg.h index b5291eefad..25d9307018 100644 --- a/include/hw/nvram/fw_cfg.h +++ b/include/hw/nvram/fw_cfg.h @@ -53,6 +53,12 @@ struct FWCfgState { dma_addr_t dma_addr; AddressSpace *dma_as; MemoryRegion dma_iomem; + + /* restore during migration */ + bool acpi_mr_restore; + uint64_t table_mr_size; + uint64_t linker_mr_size; + uint64_t rsdp_mr_size; }; struct FWCfgIoState { diff --git a/include/hw/or-irq.h b/include/hw/or-irq.h index 3a3230dd84..0038bfbe3d 100644 --- a/include/hw/or-irq.h +++ b/include/hw/or-irq.h @@ -33,7 +33,7 @@ /* This can safely be increased if necessary without breaking * migration compatibility (as long as it remains greater than 15). */ -#define MAX_OR_LINES 32 +#define MAX_OR_LINES 48 typedef struct OrIRQState qemu_or_irq; diff --git a/include/hw/pci-host/i440fx.h b/include/hw/pci-host/i440fx.h index f54e6466e4..cc58d82ed4 100644 --- a/include/hw/pci-host/i440fx.h +++ b/include/hw/pci-host/i440fx.h @@ -13,12 +13,27 @@ #include "hw/hw.h" #include "hw/pci/pci_bus.h" - -typedef struct PCII440FXState PCII440FXState; +#include "hw/pci-host/pam.h" #define TYPE_I440FX_PCI_HOST_BRIDGE "i440FX-pcihost" #define TYPE_I440FX_PCI_DEVICE "i440FX" +#define I440FX_PCI_DEVICE(obj) \ + OBJECT_CHECK(PCII440FXState, (obj), TYPE_I440FX_PCI_DEVICE) + +typedef struct PCII440FXState { + /*< private >*/ + PCIDevice parent_obj; + /*< public >*/ + + MemoryRegion *system_memory; + MemoryRegion *pci_address_space; + MemoryRegion *ram_memory; + PAMMemoryRegion pam_regions[13]; + MemoryRegion smram_region; + MemoryRegion smram, low_smram; +} PCII440FXState; + #define TYPE_IGD_PASSTHROUGH_I440FX_PCI_DEVICE "igd-passthrough-i440FX" PCIBus *i440fx_init(const char *host_type, const char *pci_type, diff --git a/include/hw/pci-host/pnv_phb3.h b/include/hw/pci-host/pnv_phb3.h new file mode 100644 index 0000000000..75b787867a --- /dev/null +++ b/include/hw/pci-host/pnv_phb3.h @@ -0,0 +1,164 @@ +/* + * QEMU PowerPC PowerNV (POWER8) PHB3 model + * + * Copyright (c) 2014-2020, IBM Corporation. + * + * This code is licensed under the GPL version 2 or later. See the + * COPYING file in the top-level directory. + */ + +#ifndef PCI_HOST_PNV_PHB3_H +#define PCI_HOST_PNV_PHB3_H + +#include "hw/pci/pcie_host.h" +#include "hw/pci/pcie_port.h" +#include "hw/ppc/xics.h" + +typedef struct PnvPHB3 PnvPHB3; + +/* + * PHB3 XICS Source for MSIs + */ +#define TYPE_PHB3_MSI "phb3-msi" +#define PHB3_MSI(obj) OBJECT_CHECK(Phb3MsiState, (obj), TYPE_PHB3_MSI) + +#define PHB3_MAX_MSI 2048 + +typedef struct Phb3MsiState { + ICSState ics; + qemu_irq *qirqs; + + PnvPHB3 *phb; + uint64_t rba[PHB3_MAX_MSI / 64]; + uint32_t rba_sum; +} Phb3MsiState; + +void pnv_phb3_msi_update_config(Phb3MsiState *msis, uint32_t base, + uint32_t count); +void pnv_phb3_msi_send(Phb3MsiState *msis, uint64_t addr, uint16_t data, + int32_t dev_pe); +void pnv_phb3_msi_ffi(Phb3MsiState *msis, uint64_t val); +void pnv_phb3_msi_pic_print_info(Phb3MsiState *msis, Monitor *mon); + + +/* + * We have one such address space wrapper per possible device under + * the PHB since they need to be assigned statically at qemu device + * creation time. The relationship to a PE is done later dynamically. + * This means we can potentially create a lot of these guys. Q35 + * stores them as some kind of radix tree but we never really need to + * do fast lookups so instead we simply keep a QLIST of them for now, + * we can add the radix if needed later on. + * + * We do cache the PE number to speed things up a bit though. + */ +typedef struct PnvPhb3DMASpace { + PCIBus *bus; + uint8_t devfn; + int pe_num; /* Cached PE number */ +#define PHB_INVALID_PE (-1) + PnvPHB3 *phb; + AddressSpace dma_as; + IOMMUMemoryRegion dma_mr; + MemoryRegion msi32_mr; + MemoryRegion msi64_mr; + QLIST_ENTRY(PnvPhb3DMASpace) list; +} PnvPhb3DMASpace; + +/* + * PHB3 Power Bus Common Queue + */ +#define TYPE_PNV_PBCQ "pnv-pbcq" +#define PNV_PBCQ(obj) OBJECT_CHECK(PnvPBCQState, (obj), TYPE_PNV_PBCQ) + +typedef struct PnvPBCQState { + DeviceState parent; + + uint32_t nest_xbase; + uint32_t spci_xbase; + uint32_t pci_xbase; +#define PBCQ_NEST_REGS_COUNT 0x46 +#define PBCQ_PCI_REGS_COUNT 0x15 +#define PBCQ_SPCI_REGS_COUNT 0x5 + + uint64_t nest_regs[PBCQ_NEST_REGS_COUNT]; + uint64_t spci_regs[PBCQ_SPCI_REGS_COUNT]; + uint64_t pci_regs[PBCQ_PCI_REGS_COUNT]; + MemoryRegion mmbar0; + MemoryRegion mmbar1; + MemoryRegion phbbar; + uint64_t mmio0_base; + uint64_t mmio0_size; + uint64_t mmio1_base; + uint64_t mmio1_size; + PnvPHB3 *phb; + + MemoryRegion xscom_nest_regs; + MemoryRegion xscom_pci_regs; + MemoryRegion xscom_spci_regs; +} PnvPBCQState; + +/* + * PHB3 PCIe Root port + */ +#define TYPE_PNV_PHB3_ROOT_BUS "pnv-phb3-root-bus" + +#define TYPE_PNV_PHB3_ROOT_PORT "pnv-phb3-root-port" + +typedef struct PnvPHB3RootPort { + PCIESlot parent_obj; +} PnvPHB3RootPort; + +/* + * PHB3 PCIe Host Bridge for PowerNV machines (POWER8) + */ +#define TYPE_PNV_PHB3 "pnv-phb3" +#define PNV_PHB3(obj) OBJECT_CHECK(PnvPHB3, (obj), TYPE_PNV_PHB3) + +#define PNV_PHB3_NUM_M64 16 +#define PNV_PHB3_NUM_REGS (0x1000 >> 3) +#define PNV_PHB3_NUM_LSI 8 +#define PNV_PHB3_NUM_PE 256 + +#define PCI_MMIO_TOTAL_SIZE (0x1ull << 60) + +struct PnvPHB3 { + PCIExpressHost parent_obj; + + uint32_t chip_id; + uint32_t phb_id; + char bus_path[8]; + + uint64_t regs[PNV_PHB3_NUM_REGS]; + MemoryRegion mr_regs; + + MemoryRegion mr_m32; + MemoryRegion mr_m64[PNV_PHB3_NUM_M64]; + MemoryRegion pci_mmio; + MemoryRegion pci_io; + + uint64_t ioda_LIST[8]; + uint64_t ioda_LXIVT[8]; + uint64_t ioda_TVT[512]; + uint64_t ioda_M64BT[16]; + uint64_t ioda_MDT[256]; + uint64_t ioda_PEEV[4]; + + uint32_t total_irq; + ICSState lsis; + qemu_irq *qirqs; + Phb3MsiState msis; + + PnvPBCQState pbcq; + + PnvPHB3RootPort root; + + QLIST_HEAD(, PnvPhb3DMASpace) dma_spaces; +}; + +uint64_t pnv_phb3_reg_read(void *opaque, hwaddr off, unsigned size); +void pnv_phb3_reg_write(void *opaque, hwaddr off, uint64_t val, unsigned size); +void pnv_phb3_update_regions(PnvPHB3 *phb); +void pnv_phb3_remap_irqs(PnvPHB3 *phb); + +#endif /* PCI_HOST_PNV_PHB3_H */ diff --git a/include/hw/pci-host/pnv_phb3_regs.h b/include/hw/pci-host/pnv_phb3_regs.h new file mode 100644 index 0000000000..a174ef1f70 --- /dev/null +++ b/include/hw/pci-host/pnv_phb3_regs.h @@ -0,0 +1,450 @@ +/* + * QEMU PowerPC PowerNV (POWER8) PHB3 model + * + * Copyright (c) 2013-2020, IBM Corporation. + * + * This code is licensed under the GPL version 2 or later. See the + * COPYING file in the top-level directory. + */ + +#ifndef PCI_HOST_PNV_PHB3_REGS_H +#define PCI_HOST_PNV_PHB3_REGS_H + +#include "qemu/host-utils.h" + +/* + * QEMU version of the GETFIELD/SETFIELD macros + * + * These are common with the PnvXive model. + */ +static inline uint64_t GETFIELD(uint64_t mask, uint64_t word) +{ + return (word & mask) >> ctz64(mask); +} + +static inline uint64_t SETFIELD(uint64_t mask, uint64_t word, + uint64_t value) +{ + return (word & ~mask) | ((value << ctz64(mask)) & mask); +} + +/* + * PBCQ XSCOM registers + */ + +#define PBCQ_NEST_IRSN_COMPARE 0x1a +#define PBCQ_NEST_IRSN_COMP PPC_BITMASK(0, 18) +#define PBCQ_NEST_IRSN_MASK 0x1b +#define PBCQ_NEST_LSI_SRC_ID 0x1f +#define PBCQ_NEST_LSI_SRC PPC_BITMASK(0, 7) +#define PBCQ_NEST_REGS_COUNT 0x46 +#define PBCQ_NEST_MMIO_BAR0 0x40 +#define PBCQ_NEST_MMIO_BAR1 0x41 +#define PBCQ_NEST_PHB_BAR 0x42 +#define PBCQ_NEST_MMIO_MASK0 0x43 +#define PBCQ_NEST_MMIO_MASK1 0x44 +#define PBCQ_NEST_BAR_EN 0x45 +#define PBCQ_NEST_BAR_EN_MMIO0 PPC_BIT(0) +#define PBCQ_NEST_BAR_EN_MMIO1 PPC_BIT(1) +#define PBCQ_NEST_BAR_EN_PHB PPC_BIT(2) +#define PBCQ_NEST_BAR_EN_IRSN_RX PPC_BIT(3) +#define PBCQ_NEST_BAR_EN_IRSN_TX PPC_BIT(4) + +#define PBCQ_PCI_REGS_COUNT 0x15 +#define PBCQ_PCI_BAR2 0x0b + +#define PBCQ_SPCI_REGS_COUNT 0x5 +#define PBCQ_SPCI_ASB_ADDR 0x0 +#define PBCQ_SPCI_ASB_STATUS 0x1 +#define PBCQ_SPCI_ASB_DATA 0x2 +#define PBCQ_SPCI_AIB_CAPP_EN 0x3 +#define PBCQ_SPCI_CAPP_SEC_TMR 0x4 + +/* + * PHB MMIO registers + */ + +/* PHB Fundamental register set A */ +#define PHB_LSI_SOURCE_ID 0x100 +#define PHB_LSI_SRC_ID PPC_BITMASK(5, 12) +#define PHB_DMA_CHAN_STATUS 0x110 +#define PHB_DMA_CHAN_ANY_ERR PPC_BIT(27) +#define PHB_DMA_CHAN_ANY_ERR1 PPC_BIT(28) +#define PHB_DMA_CHAN_ANY_FREEZE PPC_BIT(29) +#define PHB_CPU_LOADSTORE_STATUS 0x120 +#define PHB_CPU_LS_ANY_ERR PPC_BIT(27) +#define PHB_CPU_LS_ANY_ERR1 PPC_BIT(28) +#define PHB_CPU_LS_ANY_FREEZE PPC_BIT(29) +#define PHB_DMA_MSI_NODE_ID 0x128 +#define PHB_DMAMSI_NID_FIXED PPC_BIT(0) +#define PHB_DMAMSI_NID PPC_BITMASK(24, 31) +#define PHB_CONFIG_DATA 0x130 +#define PHB_LOCK0 0x138 +#define PHB_CONFIG_ADDRESS 0x140 +#define PHB_CA_ENABLE PPC_BIT(0) +#define PHB_CA_BUS PPC_BITMASK(4, 11) +#define PHB_CA_DEV PPC_BITMASK(12, 16) +#define PHB_CA_FUNC PPC_BITMASK(17, 19) +#define PHB_CA_REG PPC_BITMASK(20, 31) +#define PHB_CA_PE PPC_BITMASK(40, 47) +#define PHB_LOCK1 0x148 +#define PHB_IVT_BAR 0x150 +#define PHB_IVT_BAR_ENABLE PPC_BIT(0) +#define PHB_IVT_BASE_ADDRESS_MASK PPC_BITMASK(14, 48) +#define PHB_IVT_LENGTH_MASK PPC_BITMASK(52, 63) +#define PHB_RBA_BAR 0x158 +#define PHB_RBA_BAR_ENABLE PPC_BIT(0) +#define PHB_RBA_BASE_ADDRESS PPC_BITMASK(14, 55) +#define PHB_PHB3_CONFIG 0x160 +#define PHB_PHB3C_64B_TCE_EN PPC_BIT(2) +#define PHB_PHB3C_32BIT_MSI_EN PPC_BIT(8) +#define PHB_PHB3C_64BIT_MSI_EN PPC_BIT(14) +#define PHB_PHB3C_M32_EN PPC_BIT(16) +#define PHB_RTT_BAR 0x168 +#define PHB_RTT_BAR_ENABLE PPC_BIT(0) +#define PHB_RTT_BASE_ADDRESS_MASK PPC_BITMASK(14, 46) +#define PHB_PELTV_BAR 0x188 +#define PHB_PELTV_BAR_ENABLE PPC_BIT(0) +#define PHB_PELTV_BASE_ADDRESS PPC_BITMASK(14, 50) +#define PHB_M32_BASE_ADDR 0x190 +#define PHB_M32_BASE_MASK 0x198 +#define PHB_M32_START_ADDR 0x1a0 +#define PHB_PEST_BAR 0x1a8 +#define PHB_PEST_BAR_ENABLE PPC_BIT(0) +#define PHB_PEST_BASE_ADDRESS PPC_BITMASK(14, 51) +#define PHB_M64_UPPER_BITS 0x1f0 +#define PHB_INTREP_TIMER 0x1f8 +#define PHB_DMARD_SYNC 0x200 +#define PHB_DMARD_SYNC_START PPC_BIT(0) +#define PHB_DMARD_SYNC_COMPLETE PPC_BIT(1) +#define PHB_RTC_INVALIDATE 0x208 +#define PHB_RTC_INVALIDATE_ALL PPC_BIT(0) +#define PHB_RTC_INVALIDATE_RID PPC_BITMASK(16, 31) +#define PHB_TCE_KILL 0x210 +#define PHB_TCE_KILL_ALL PPC_BIT(0) +#define PHB_TCE_SPEC_CTL 0x218 +#define PHB_IODA_ADDR 0x220 +#define PHB_IODA_AD_AUTOINC PPC_BIT(0) +#define PHB_IODA_AD_TSEL PPC_BITMASK(11, 15) +#define PHB_IODA_AD_TADR PPC_BITMASK(55, 63) +#define PHB_IODA_DATA0 0x228 +#define PHB_FFI_REQUEST 0x238 +#define PHB_FFI_LOCK_CLEAR PPC_BIT(3) +#define PHB_FFI_REQUEST_ISN PPC_BITMASK(49, 59) +#define PHB_FFI_LOCK 0x240 +#define PHB_FFI_LOCK_STATE PPC_BIT(0) +#define PHB_XIVE_UPDATE 0x248 /* Broken in DD1 */ +#define PHB_PHB3_GEN_CAP 0x250 +#define PHB_PHB3_TCE_CAP 0x258 +#define PHB_PHB3_IRQ_CAP 0x260 +#define PHB_PHB3_EEH_CAP 0x268 +#define PHB_IVC_INVALIDATE 0x2a0 +#define PHB_IVC_INVALIDATE_ALL PPC_BIT(0) +#define PHB_IVC_INVALIDATE_SID PPC_BITMASK(16, 31) +#define PHB_IVC_UPDATE 0x2a8 +#define PHB_IVC_UPDATE_ENABLE_P PPC_BIT(0) +#define PHB_IVC_UPDATE_ENABLE_Q PPC_BIT(1) +#define PHB_IVC_UPDATE_ENABLE_SERVER PPC_BIT(2) +#define PHB_IVC_UPDATE_ENABLE_PRI PPC_BIT(3) +#define PHB_IVC_UPDATE_ENABLE_GEN PPC_BIT(4) +#define PHB_IVC_UPDATE_ENABLE_CON PPC_BIT(5) +#define PHB_IVC_UPDATE_GEN_MATCH PPC_BITMASK(6, 7) +#define PHB_IVC_UPDATE_SERVER PPC_BITMASK(8, 23) +#define PHB_IVC_UPDATE_PRI PPC_BITMASK(24, 31) +#define PHB_IVC_UPDATE_GEN PPC_BITMASK(32, 33) +#define PHB_IVC_UPDATE_P PPC_BITMASK(34, 34) +#define PHB_IVC_UPDATE_Q PPC_BITMASK(35, 35) +#define PHB_IVC_UPDATE_SID PPC_BITMASK(48, 63) +#define PHB_PAPR_ERR_INJ_CTL 0x2b0 +#define PHB_PAPR_ERR_INJ_CTL_INB PPC_BIT(0) +#define PHB_PAPR_ERR_INJ_CTL_OUTB PPC_BIT(1) +#define PHB_PAPR_ERR_INJ_CTL_STICKY PPC_BIT(2) +#define PHB_PAPR_ERR_INJ_CTL_CFG PPC_BIT(3) +#define PHB_PAPR_ERR_INJ_CTL_RD PPC_BIT(4) +#define PHB_PAPR_ERR_INJ_CTL_WR PPC_BIT(5) +#define PHB_PAPR_ERR_INJ_CTL_FREEZE PPC_BIT(6) +#define PHB_PAPR_ERR_INJ_ADDR 0x2b8 +#define PHB_PAPR_ERR_INJ_ADDR_MMIO PPC_BITMASK(16, 63) +#define PHB_PAPR_ERR_INJ_MASK 0x2c0 +#define PHB_PAPR_ERR_INJ_MASK_CFG PPC_BITMASK(4, 11) +#define PHB_PAPR_ERR_INJ_MASK_MMIO PPC_BITMASK(16, 63) +#define PHB_ETU_ERR_SUMMARY 0x2c8 + +/* UTL registers */ +#define UTL_SYS_BUS_CONTROL 0x400 +#define UTL_STATUS 0x408 +#define UTL_SYS_BUS_AGENT_STATUS 0x410 +#define UTL_SYS_BUS_AGENT_ERR_SEVERITY 0x418 +#define UTL_SYS_BUS_AGENT_IRQ_EN 0x420 +#define UTL_SYS_BUS_BURST_SZ_CONF 0x440 +#define UTL_REVISION_ID 0x448 +#define UTL_BCLK_DOMAIN_DBG1 0x460 +#define UTL_BCLK_DOMAIN_DBG2 0x468 +#define UTL_BCLK_DOMAIN_DBG3 0x470 +#define UTL_BCLK_DOMAIN_DBG4 0x478 +#define UTL_BCLK_DOMAIN_DBG5 0x480 +#define UTL_BCLK_DOMAIN_DBG6 0x488 +#define UTL_OUT_POST_HDR_BUF_ALLOC 0x4c0 +#define UTL_OUT_POST_DAT_BUF_ALLOC 0x4d0 +#define UTL_IN_POST_HDR_BUF_ALLOC 0x4e0 +#define UTL_IN_POST_DAT_BUF_ALLOC 0x4f0 +#define UTL_OUT_NP_BUF_ALLOC 0x500 +#define UTL_IN_NP_BUF_ALLOC 0x510 +#define UTL_PCIE_TAGS_ALLOC 0x520 +#define UTL_GBIF_READ_TAGS_ALLOC 0x530 +#define UTL_PCIE_PORT_CONTROL 0x540 +#define UTL_PCIE_PORT_STATUS 0x548 +#define UTL_PCIE_PORT_ERROR_SEV 0x550 +#define UTL_PCIE_PORT_IRQ_EN 0x558 +#define UTL_RC_STATUS 0x560 +#define UTL_RC_ERR_SEVERITY 0x568 +#define UTL_RC_IRQ_EN 0x570 +#define UTL_EP_STATUS 0x578 +#define UTL_EP_ERR_SEVERITY 0x580 +#define UTL_EP_ERR_IRQ_EN 0x588 +#define UTL_PCI_PM_CTRL1 0x590 +#define UTL_PCI_PM_CTRL2 0x598 +#define UTL_GP_CTL1 0x5a0 +#define UTL_GP_CTL2 0x5a8 +#define UTL_PCLK_DOMAIN_DBG1 0x5b0 +#define UTL_PCLK_DOMAIN_DBG2 0x5b8 +#define UTL_PCLK_DOMAIN_DBG3 0x5c0 +#define UTL_PCLK_DOMAIN_DBG4 0x5c8 + +/* PCI-E Stack registers */ +#define PHB_PCIE_SYSTEM_CONFIG 0x600 +#define PHB_PCIE_BUS_NUMBER 0x608 +#define PHB_PCIE_SYSTEM_TEST 0x618 +#define PHB_PCIE_LINK_MANAGEMENT 0x630 +#define PHB_PCIE_LM_LINK_ACTIVE PPC_BIT(8) +#define PHB_PCIE_DLP_TRAIN_CTL 0x640 +#define PHB_PCIE_DLP_TCTX_DISABLE PPC_BIT(1) +#define PHB_PCIE_DLP_TCRX_DISABLED PPC_BIT(16) +#define PHB_PCIE_DLP_INBAND_PRESENCE PPC_BIT(19) +#define PHB_PCIE_DLP_TC_DL_LINKUP PPC_BIT(21) +#define PHB_PCIE_DLP_TC_DL_PGRESET PPC_BIT(22) +#define PHB_PCIE_DLP_TC_DL_LINKACT PPC_BIT(23) +#define PHB_PCIE_SLOP_LOOPBACK_STATUS 0x648 +#define PHB_PCIE_SYS_LINK_INIT 0x668 +#define PHB_PCIE_UTL_CONFIG 0x670 +#define PHB_PCIE_DLP_CONTROL 0x678 +#define PHB_PCIE_UTL_ERRLOG1 0x680 +#define PHB_PCIE_UTL_ERRLOG2 0x688 +#define PHB_PCIE_UTL_ERRLOG3 0x690 +#define PHB_PCIE_UTL_ERRLOG4 0x698 +#define PHB_PCIE_DLP_ERRLOG1 0x6a0 +#define PHB_PCIE_DLP_ERRLOG2 0x6a8 +#define PHB_PCIE_DLP_ERR_STATUS 0x6b0 +#define PHB_PCIE_DLP_ERR_COUNTERS 0x6b8 +#define PHB_PCIE_UTL_ERR_INJECT 0x6c0 +#define PHB_PCIE_TLDLP_ERR_INJECT 0x6c8 +#define PHB_PCIE_LANE_EQ_CNTL0 0x6d0 +#define PHB_PCIE_LANE_EQ_CNTL1 0x6d8 +#define PHB_PCIE_LANE_EQ_CNTL2 0x6e0 +#define PHB_PCIE_LANE_EQ_CNTL3 0x6e8 +#define PHB_PCIE_STRAPPING 0x700 + +/* Fundamental register set B */ +#define PHB_VERSION 0x800 +#define PHB_RESET 0x808 +#define PHB_CONTROL 0x810 +#define PHB_CTRL_IVE_128_BYTES PPC_BIT(24) +#define PHB_AIB_RX_CRED_INIT_TIMER 0x818 +#define PHB_AIB_RX_CMD_CRED 0x820 +#define PHB_AIB_RX_DATA_CRED 0x828 +#define PHB_AIB_TX_CMD_CRED 0x830 +#define PHB_AIB_TX_DATA_CRED 0x838 +#define PHB_AIB_TX_CHAN_MAPPING 0x840 +#define PHB_AIB_TAG_ENABLE 0x858 +#define PHB_AIB_FENCE_CTRL 0x860 +#define PHB_TCE_TAG_ENABLE 0x868 +#define PHB_TCE_WATERMARK 0x870 +#define PHB_TIMEOUT_CTRL1 0x878 +#define PHB_TIMEOUT_CTRL2 0x880 +#define PHB_Q_DMA_R 0x888 +#define PHB_Q_DMA_R_QUIESCE_DMA PPC_BIT(0) +#define PHB_Q_DMA_R_AUTORESET PPC_BIT(1) +#define PHB_Q_DMA_R_DMA_RESP_STATUS PPC_BIT(4) +#define PHB_Q_DMA_R_MMIO_RESP_STATUS PPC_BIT(5) +#define PHB_Q_DMA_R_TCE_RESP_STATUS PPC_BIT(6) +#define PHB_AIB_TAG_STATUS 0x900 +#define PHB_TCE_TAG_STATUS 0x908 + +/* FIR & Error registers */ +#define PHB_LEM_FIR_ACCUM 0xc00 +#define PHB_LEM_FIR_AND_MASK 0xc08 +#define PHB_LEM_FIR_OR_MASK 0xc10 +#define PHB_LEM_ERROR_MASK 0xc18 +#define PHB_LEM_ERROR_AND_MASK 0xc20 +#define PHB_LEM_ERROR_OR_MASK 0xc28 +#define PHB_LEM_ACTION0 0xc30 +#define PHB_LEM_ACTION1 0xc38 +#define PHB_LEM_WOF 0xc40 +#define PHB_ERR_STATUS 0xc80 +#define PHB_ERR1_STATUS 0xc88 +#define PHB_ERR_INJECT 0xc90 +#define PHB_ERR_LEM_ENABLE 0xc98 +#define PHB_ERR_IRQ_ENABLE 0xca0 +#define PHB_ERR_FREEZE_ENABLE 0xca8 +#define PHB_ERR_AIB_FENCE_ENABLE 0xcb0 +#define PHB_ERR_LOG_0 0xcc0 +#define PHB_ERR_LOG_1 0xcc8 +#define PHB_ERR_STATUS_MASK 0xcd0 +#define PHB_ERR1_STATUS_MASK 0xcd8 + +#define PHB_OUT_ERR_STATUS 0xd00 +#define PHB_OUT_ERR1_STATUS 0xd08 +#define PHB_OUT_ERR_INJECT 0xd10 +#define PHB_OUT_ERR_LEM_ENABLE 0xd18 +#define PHB_OUT_ERR_IRQ_ENABLE 0xd20 +#define PHB_OUT_ERR_FREEZE_ENABLE 0xd28 +#define PHB_OUT_ERR_AIB_FENCE_ENABLE 0xd30 +#define PHB_OUT_ERR_LOG_0 0xd40 +#define PHB_OUT_ERR_LOG_1 0xd48 +#define PHB_OUT_ERR_STATUS_MASK 0xd50 +#define PHB_OUT_ERR1_STATUS_MASK 0xd58 + +#define PHB_INA_ERR_STATUS 0xd80 +#define PHB_INA_ERR1_STATUS 0xd88 +#define PHB_INA_ERR_INJECT 0xd90 +#define PHB_INA_ERR_LEM_ENABLE 0xd98 +#define PHB_INA_ERR_IRQ_ENABLE 0xda0 +#define PHB_INA_ERR_FREEZE_ENABLE 0xda8 +#define PHB_INA_ERR_AIB_FENCE_ENABLE 0xdb0 +#define PHB_INA_ERR_LOG_0 0xdc0 +#define PHB_INA_ERR_LOG_1 0xdc8 +#define PHB_INA_ERR_STATUS_MASK 0xdd0 +#define PHB_INA_ERR1_STATUS_MASK 0xdd8 + +#define PHB_INB_ERR_STATUS 0xe00 +#define PHB_INB_ERR1_STATUS 0xe08 +#define PHB_INB_ERR_INJECT 0xe10 +#define PHB_INB_ERR_LEM_ENABLE 0xe18 +#define PHB_INB_ERR_IRQ_ENABLE 0xe20 +#define PHB_INB_ERR_FREEZE_ENABLE 0xe28 +#define PHB_INB_ERR_AIB_FENCE_ENABLE 0xe30 +#define PHB_INB_ERR_LOG_0 0xe40 +#define PHB_INB_ERR_LOG_1 0xe48 +#define PHB_INB_ERR_STATUS_MASK 0xe50 +#define PHB_INB_ERR1_STATUS_MASK 0xe58 + +/* Performance monitor & Debug registers */ +#define PHB_TRACE_CONTROL 0xf80 +#define PHB_PERFMON_CONFIG 0xf88 +#define PHB_PERFMON_CTR0 0xf90 +#define PHB_PERFMON_CTR1 0xf98 +#define PHB_PERFMON_CTR2 0xfa0 +#define PHB_PERFMON_CTR3 0xfa8 +#define PHB_HOTPLUG_OVERRIDE 0xfb0 +#define PHB_HPOVR_FORCE_RESAMPLE PPC_BIT(9) +#define PHB_HPOVR_PRESENCE_A PPC_BIT(10) +#define PHB_HPOVR_PRESENCE_B PPC_BIT(11) +#define PHB_HPOVR_LINK_ACTIVE PPC_BIT(12) +#define PHB_HPOVR_LINK_BIFURCATED PPC_BIT(13) +#define PHB_HPOVR_LINK_LANE_SWAPPED PPC_BIT(14) + +/* + * IODA2 on-chip tables + */ + +#define IODA2_TBL_LIST 1 +#define IODA2_TBL_LXIVT 2 +#define IODA2_TBL_IVC_CAM 3 +#define IODA2_TBL_RBA 4 +#define IODA2_TBL_RCAM 5 +#define IODA2_TBL_MRT 6 +#define IODA2_TBL_PESTA 7 +#define IODA2_TBL_PESTB 8 +#define IODA2_TBL_TVT 9 +#define IODA2_TBL_TCAM 10 +#define IODA2_TBL_TDR 11 +#define IODA2_TBL_M64BT 16 +#define IODA2_TBL_M32DT 17 +#define IODA2_TBL_PEEV 20 + +/* LXIVT */ +#define IODA2_LXIVT_SERVER PPC_BITMASK(8, 23) +#define IODA2_LXIVT_PRIORITY PPC_BITMASK(24, 31) +#define IODA2_LXIVT_NODE_ID PPC_BITMASK(56, 63) + +/* IVT */ +#define IODA2_IVT_SERVER PPC_BITMASK(0, 23) +#define IODA2_IVT_PRIORITY PPC_BITMASK(24, 31) +#define IODA2_IVT_GEN PPC_BITMASK(37, 38) +#define IODA2_IVT_P PPC_BITMASK(39, 39) +#define IODA2_IVT_Q PPC_BITMASK(47, 47) +#define IODA2_IVT_PE PPC_BITMASK(48, 63) + +/* TVT */ +#define IODA2_TVT_TABLE_ADDR PPC_BITMASK(0, 47) +#define IODA2_TVT_NUM_LEVELS PPC_BITMASK(48, 50) +#define IODA2_TVE_1_LEVEL 0 +#define IODA2_TVE_2_LEVELS 1 +#define IODA2_TVE_3_LEVELS 2 +#define IODA2_TVE_4_LEVELS 3 +#define IODA2_TVE_5_LEVELS 4 +#define IODA2_TVT_TCE_TABLE_SIZE PPC_BITMASK(51, 55) +#define IODA2_TVT_IO_PSIZE PPC_BITMASK(59, 63) + +/* PESTA */ +#define IODA2_PESTA_MMIO_FROZEN PPC_BIT(0) + +/* PESTB */ +#define IODA2_PESTB_DMA_STOPPED PPC_BIT(0) + +/* M32DT */ +#define IODA2_M32DT_PE PPC_BITMASK(8, 15) + +/* M64BT */ +#define IODA2_M64BT_ENABLE PPC_BIT(0) +#define IODA2_M64BT_SINGLE_PE PPC_BIT(1) +#define IODA2_M64BT_BASE PPC_BITMASK(2, 31) +#define IODA2_M64BT_MASK PPC_BITMASK(34, 63) +#define IODA2_M64BT_SINGLE_BASE PPC_BITMASK(2, 26) +#define IODA2_M64BT_PE_HI PPC_BITMASK(27, 31) +#define IODA2_M64BT_SINGLE_MASK PPC_BITMASK(34, 58) +#define IODA2_M64BT_PE_LOW PPC_BITMASK(59, 63) + +/* + * IODA2 in-memory tables + */ + +/* + * PEST + * + * 2x8 bytes entries, PEST0 and PEST1 + */ + +#define IODA2_PEST0_MMIO_CAUSE PPC_BIT(2) +#define IODA2_PEST0_CFG_READ PPC_BIT(3) +#define IODA2_PEST0_CFG_WRITE PPC_BIT(4) +#define IODA2_PEST0_TTYPE PPC_BITMASK(5, 7) +#define PEST_TTYPE_DMA_WRITE 0 +#define PEST_TTYPE_MSI 1 +#define PEST_TTYPE_DMA_READ 2 +#define PEST_TTYPE_DMA_READ_RESP 3 +#define PEST_TTYPE_MMIO_LOAD 4 +#define PEST_TTYPE_MMIO_STORE 5 +#define PEST_TTYPE_OTHER 7 +#define IODA2_PEST0_CA_RETURN PPC_BIT(8) +#define IODA2_PEST0_UTL_RTOS_TIMEOUT PPC_BIT(8) /* Same bit as CA return */ +#define IODA2_PEST0_UR_RETURN PPC_BIT(9) +#define IODA2_PEST0_UTL_NONFATAL PPC_BIT(10) +#define IODA2_PEST0_UTL_FATAL PPC_BIT(11) +#define IODA2_PEST0_PARITY_UE PPC_BIT(13) +#define IODA2_PEST0_UTL_CORRECTABLE PPC_BIT(14) +#define IODA2_PEST0_UTL_INTERRUPT PPC_BIT(15) +#define IODA2_PEST0_MMIO_XLATE PPC_BIT(16) +#define IODA2_PEST0_IODA2_ERROR PPC_BIT(16) /* Same bit as MMIO xlate */ +#define IODA2_PEST0_TCE_PAGE_FAULT PPC_BIT(18) +#define IODA2_PEST0_TCE_ACCESS_FAULT PPC_BIT(19) +#define IODA2_PEST0_DMA_RESP_TIMEOUT PPC_BIT(20) +#define IODA2_PEST0_AIB_SIZE_INVALID PPC_BIT(21) +#define IODA2_PEST0_LEM_BIT PPC_BITMASK(26, 31) +#define IODA2_PEST0_RID PPC_BITMASK(32, 47) +#define IODA2_PEST0_MSI_DATA PPC_BITMASK(48, 63) + +#define IODA2_PEST1_FAIL_ADDR PPC_BITMASK(3, 63) + + +#endif /* PCI_HOST_PNV_PHB3_REGS_H */ diff --git a/include/hw/pci-host/pnv_phb4.h b/include/hw/pci-host/pnv_phb4.h new file mode 100644 index 0000000000..c882bfd0aa --- /dev/null +++ b/include/hw/pci-host/pnv_phb4.h @@ -0,0 +1,230 @@ +/* + * QEMU PowerPC PowerNV (POWER9) PHB4 model + * + * Copyright (c) 2018-2020, IBM Corporation. + * + * This code is licensed under the GPL version 2 or later. See the + * COPYING file in the top-level directory. + */ + +#ifndef PCI_HOST_PNV_PHB4_H +#define PCI_HOST_PNV_PHB4_H + +#include "hw/pci/pcie_host.h" +#include "hw/pci/pcie_port.h" +#include "hw/ppc/xive.h" + +typedef struct PnvPhb4PecState PnvPhb4PecState; +typedef struct PnvPhb4PecStack PnvPhb4PecStack; +typedef struct PnvPHB4 PnvPHB4; +typedef struct PnvChip PnvChip; + +/* + * We have one such address space wrapper per possible device under + * the PHB since they need to be assigned statically at qemu device + * creation time. The relationship to a PE is done later + * dynamically. This means we can potentially create a lot of these + * guys. Q35 stores them as some kind of radix tree but we never + * really need to do fast lookups so instead we simply keep a QLIST of + * them for now, we can add the radix if needed later on. + * + * We do cache the PE number to speed things up a bit though. + */ +typedef struct PnvPhb4DMASpace { + PCIBus *bus; + uint8_t devfn; + int pe_num; /* Cached PE number */ +#define PHB_INVALID_PE (-1) + PnvPHB4 *phb; + AddressSpace dma_as; + IOMMUMemoryRegion dma_mr; + MemoryRegion msi32_mr; + MemoryRegion msi64_mr; + QLIST_ENTRY(PnvPhb4DMASpace) list; +} PnvPhb4DMASpace; + +/* + * PHB4 PCIe Root port + */ +#define TYPE_PNV_PHB4_ROOT_BUS "pnv-phb4-root-bus" +#define TYPE_PNV_PHB4_ROOT_PORT "pnv-phb4-root-port" + +typedef struct PnvPHB4RootPort { + PCIESlot parent_obj; +} PnvPHB4RootPort; + +/* + * PHB4 PCIe Host Bridge for PowerNV machines (POWER9) + */ +#define TYPE_PNV_PHB4 "pnv-phb4" +#define PNV_PHB4(obj) OBJECT_CHECK(PnvPHB4, (obj), TYPE_PNV_PHB4) + +#define PNV_PHB4_MAX_LSIs 8 +#define PNV_PHB4_MAX_INTs 4096 +#define PNV_PHB4_MAX_MIST (PNV_PHB4_MAX_INTs >> 2) +#define PNV_PHB4_MAX_MMIO_WINDOWS 32 +#define PNV_PHB4_MIN_MMIO_WINDOWS 16 +#define PNV_PHB4_NUM_REGS (0x3000 >> 3) +#define PNV_PHB4_MAX_PEs 512 +#define PNV_PHB4_MAX_TVEs (PNV_PHB4_MAX_PEs * 2) +#define PNV_PHB4_MAX_PEEVs (PNV_PHB4_MAX_PEs / 64) +#define PNV_PHB4_MAX_MBEs (PNV_PHB4_MAX_MMIO_WINDOWS * 2) + +#define PNV_PHB4_VERSION 0x000000a400000002ull +#define PNV_PHB4_DEVICE_ID 0x04c1 + +#define PCI_MMIO_TOTAL_SIZE (0x1ull << 60) + +struct PnvPHB4 { + PCIExpressHost parent_obj; + + PnvPHB4RootPort root; + + uint32_t chip_id; + uint32_t phb_id; + + uint64_t version; + uint16_t device_id; + + char bus_path[8]; + + /* Main register images */ + uint64_t regs[PNV_PHB4_NUM_REGS]; + MemoryRegion mr_regs; + + /* Extra SCOM-only register */ + uint64_t scom_hv_ind_addr_reg; + + /* + * Geometry of the PHB. There are two types, small and big PHBs, a + * number of resources (number of PEs, windows etc...) are doubled + * for a big PHB + */ + bool big_phb; + + /* Memory regions for MMIO space */ + MemoryRegion mr_mmio[PNV_PHB4_MAX_MMIO_WINDOWS]; + + /* PCI side space */ + MemoryRegion pci_mmio; + MemoryRegion pci_io; + + /* On-chip IODA tables */ + uint64_t ioda_LIST[PNV_PHB4_MAX_LSIs]; + uint64_t ioda_MIST[PNV_PHB4_MAX_MIST]; + uint64_t ioda_TVT[PNV_PHB4_MAX_TVEs]; + uint64_t ioda_MBT[PNV_PHB4_MAX_MBEs]; + uint64_t ioda_MDT[PNV_PHB4_MAX_PEs]; + uint64_t ioda_PEEV[PNV_PHB4_MAX_PEEVs]; + + /* + * The internal PESTA/B is 2 bits per PE split into two tables, we + * store them in a single array here to avoid wasting space. + */ + uint8_t ioda_PEST_AB[PNV_PHB4_MAX_PEs]; + + /* P9 Interrupt generation */ + XiveSource xsrc; + qemu_irq *qirqs; + + PnvPhb4PecStack *stack; + + QLIST_HEAD(, PnvPhb4DMASpace) dma_spaces; +}; + +void pnv_phb4_pic_print_info(PnvPHB4 *phb, Monitor *mon); +void pnv_phb4_update_regions(PnvPhb4PecStack *stack); +extern const MemoryRegionOps pnv_phb4_xscom_ops; + +/* + * PHB4 PEC (PCI Express Controller) + */ +#define TYPE_PNV_PHB4_PEC "pnv-phb4-pec" +#define PNV_PHB4_PEC(obj) \ + OBJECT_CHECK(PnvPhb4PecState, (obj), TYPE_PNV_PHB4_PEC) + +#define TYPE_PNV_PHB4_PEC_STACK "pnv-phb4-pec-stack" +#define PNV_PHB4_PEC_STACK(obj) \ + OBJECT_CHECK(PnvPhb4PecStack, (obj), TYPE_PNV_PHB4_PEC_STACK) + +/* Per-stack data */ +struct PnvPhb4PecStack { + DeviceState parent; + + /* My own stack number */ + uint32_t stack_no; + + /* Nest registers */ +#define PHB4_PEC_NEST_STK_REGS_COUNT 0x17 + uint64_t nest_regs[PHB4_PEC_NEST_STK_REGS_COUNT]; + MemoryRegion nest_regs_mr; + + /* PCI registers (excluding pass-through) */ +#define PHB4_PEC_PCI_STK_REGS_COUNT 0xf + uint64_t pci_regs[PHB4_PEC_PCI_STK_REGS_COUNT]; + MemoryRegion pci_regs_mr; + + /* PHB pass-through XSCOM */ + MemoryRegion phb_regs_mr; + + /* Memory windows from PowerBus to PHB */ + MemoryRegion mmbar0; + MemoryRegion mmbar1; + MemoryRegion phbbar; + MemoryRegion intbar; + uint64_t mmio0_base; + uint64_t mmio0_size; + uint64_t mmio1_base; + uint64_t mmio1_size; + + /* The owner PEC */ + PnvPhb4PecState *pec; + + /* The actual PHB */ + PnvPHB4 phb; +}; + +struct PnvPhb4PecState { + DeviceState parent; + + /* PEC number in chip */ + uint32_t index; + uint32_t chip_id; + + MemoryRegion *system_memory; + + /* Nest registers, excuding per-stack */ +#define PHB4_PEC_NEST_REGS_COUNT 0xf + uint64_t nest_regs[PHB4_PEC_NEST_REGS_COUNT]; + MemoryRegion nest_regs_mr; + + /* PCI registers, excluding per-stack */ +#define PHB4_PEC_PCI_REGS_COUNT 0x2 + uint64_t pci_regs[PHB4_PEC_PCI_REGS_COUNT]; + MemoryRegion pci_regs_mr; + + /* Stacks */ + #define PHB4_PEC_MAX_STACKS 3 + uint32_t num_stacks; + PnvPhb4PecStack stacks[PHB4_PEC_MAX_STACKS]; +}; + +#define PNV_PHB4_PEC_CLASS(klass) \ + OBJECT_CLASS_CHECK(PnvPhb4PecClass, (klass), TYPE_PNV_PHB4_PEC) +#define PNV_PHB4_PEC_GET_CLASS(obj) \ + OBJECT_GET_CLASS(PnvPhb4PecClass, (obj), TYPE_PNV_PHB4_PEC) + +typedef struct PnvPhb4PecClass { + DeviceClass parent_class; + + uint32_t (*xscom_nest_base)(PnvPhb4PecState *pec); + uint32_t xscom_nest_size; + uint32_t (*xscom_pci_base)(PnvPhb4PecState *pec); + uint32_t xscom_pci_size; + const char *compat; + int compat_size; + const char *stk_compat; + int stk_compat_size; +} PnvPhb4PecClass; + +#endif /* PCI_HOST_PNV_PHB4_H */ diff --git a/include/hw/pci-host/pnv_phb4_regs.h b/include/hw/pci-host/pnv_phb4_regs.h new file mode 100644 index 0000000000..55df2c3e5e --- /dev/null +++ b/include/hw/pci-host/pnv_phb4_regs.h @@ -0,0 +1,553 @@ +/* + * QEMU PowerPC PowerNV (POWER9) PHB4 model + * + * Copyright (c) 2013-2020, IBM Corporation. + * + * This code is licensed under the GPL version 2 or later. See the + * COPYING file in the top-level directory. + */ + +#ifndef PCI_HOST_PNV_PHB4_REGS_H +#define PCI_HOST_PNV_PHB4_REGS_H + +/* + * PEC XSCOM registers + * + * There a 3 PECs in P9. Each PEC can have several PHBs. Each PEC has some + * "global" registers and some "per-stack" (per-PHB) registers. Those are + * organized in two XSCOM ranges, the "Nest" range and the "PCI" range, each + * range contains both some "PEC" registers and some "per-stack" registers. + * + * Finally the PCI range also contains an additional range per stack that + * passes through to some of the PHB own registers. + * + * PEC0 can contain 1 PHB (PHB0) + * PEC1 can contain 2 PHBs (PHB1 and PHB2) + * PEC2 can contain 3 PHBs (PHB3, PHB4 and PHB5) + */ + +/* + * This is the "stack" offset, it's the offset from a given range base + * to the first "per-stack" registers and also the stride between + * stacks, thus for PEC2, the global registers are at offset 0, the + * PHB3 registers at offset 0x40, the PHB4 at offset 0x80 etc.... + * + * It is *also* the offset to the pass-through SCOM region but in this case + * it is 0 based, ie PHB3 is at 0x100 PHB4 is a 0x140 etc.. + */ +#define PEC_STACK_OFFSET 0x40 + +/* XSCOM Nest global registers */ +#define PEC_NEST_PBCQ_HW_CONFIG 0x00 +#define PEC_NEST_DROP_PRIO_CTRL 0x01 +#define PEC_NEST_PBCQ_ERR_INJECT 0x02 +#define PEC_NEST_PCI_NEST_CLK_TRACE_CTL 0x03 +#define PEC_NEST_PBCQ_PMON_CTRL 0x04 +#define PEC_NEST_PBCQ_PBUS_ADDR_EXT 0x05 +#define PEC_NEST_PBCQ_PRED_VEC_TIMEOUT 0x06 +#define PEC_NEST_CAPP_CTRL 0x07 +#define PEC_NEST_PBCQ_READ_STK_OVR 0x08 +#define PEC_NEST_PBCQ_WRITE_STK_OVR 0x09 +#define PEC_NEST_PBCQ_STORE_STK_OVR 0x0a +#define PEC_NEST_PBCQ_RETRY_BKOFF_CTRL 0x0b + +/* XSCOM Nest per-stack registers */ +#define PEC_NEST_STK_PCI_NEST_FIR 0x00 +#define PEC_NEST_STK_PCI_NEST_FIR_CLR 0x01 +#define PEC_NEST_STK_PCI_NEST_FIR_SET 0x02 +#define PEC_NEST_STK_PCI_NEST_FIR_MSK 0x03 +#define PEC_NEST_STK_PCI_NEST_FIR_MSKC 0x04 +#define PEC_NEST_STK_PCI_NEST_FIR_MSKS 0x05 +#define PEC_NEST_STK_PCI_NEST_FIR_ACT0 0x06 +#define PEC_NEST_STK_PCI_NEST_FIR_ACT1 0x07 +#define PEC_NEST_STK_PCI_NEST_FIR_WOF 0x08 +#define PEC_NEST_STK_ERR_REPORT_0 0x0a +#define PEC_NEST_STK_ERR_REPORT_1 0x0b +#define PEC_NEST_STK_PBCQ_GNRL_STATUS 0x0c +#define PEC_NEST_STK_PBCQ_MODE 0x0d +#define PEC_NEST_STK_MMIO_BAR0 0x0e +#define PEC_NEST_STK_MMIO_BAR0_MASK 0x0f +#define PEC_NEST_STK_MMIO_BAR1 0x10 +#define PEC_NEST_STK_MMIO_BAR1_MASK 0x11 +#define PEC_NEST_STK_PHB_REGS_BAR 0x12 +#define PEC_NEST_STK_INT_BAR 0x13 +#define PEC_NEST_STK_BAR_EN 0x14 +#define PEC_NEST_STK_BAR_EN_MMIO0 PPC_BIT(0) +#define PEC_NEST_STK_BAR_EN_MMIO1 PPC_BIT(1) +#define PEC_NEST_STK_BAR_EN_PHB PPC_BIT(2) +#define PEC_NEST_STK_BAR_EN_INT PPC_BIT(3) +#define PEC_NEST_STK_DATA_FRZ_TYPE 0x15 +#define PEC_NEST_STK_PBCQ_TUN_BAR 0x16 + +/* XSCOM PCI global registers */ +#define PEC_PCI_PBAIB_HW_CONFIG 0x00 +#define PEC_PCI_PBAIB_READ_STK_OVR 0x02 + +/* XSCOM PCI per-stack registers */ +#define PEC_PCI_STK_PCI_FIR 0x00 +#define PEC_PCI_STK_PCI_FIR_CLR 0x01 +#define PEC_PCI_STK_PCI_FIR_SET 0x02 +#define PEC_PCI_STK_PCI_FIR_MSK 0x03 +#define PEC_PCI_STK_PCI_FIR_MSKC 0x04 +#define PEC_PCI_STK_PCI_FIR_MSKS 0x05 +#define PEC_PCI_STK_PCI_FIR_ACT0 0x06 +#define PEC_PCI_STK_PCI_FIR_ACT1 0x07 +#define PEC_PCI_STK_PCI_FIR_WOF 0x08 +#define PEC_PCI_STK_ETU_RESET 0x0a +#define PEC_PCI_STK_PBAIB_ERR_REPORT 0x0b +#define PEC_PCI_STK_PBAIB_TX_CMD_CRED 0x0d +#define PEC_PCI_STK_PBAIB_TX_DAT_CRED 0x0e + +/* + * PHB "SCOM" registers. This is accessed via the above window + * and provides a backdoor to the PHB when the AIB bus is not + * functional. Some of these directly map some of the PHB MMIO + * registers, some are specific and allow indirect access to a + * wider range of PHB registers + */ +#define PHB_SCOM_HV_IND_ADDR 0x00 +#define PHB_SCOM_HV_IND_ADDR_VALID PPC_BIT(0) +#define PHB_SCOM_HV_IND_ADDR_4B PPC_BIT(1) +#define PHB_SCOM_HV_IND_ADDR_AUTOINC PPC_BIT(2) +#define PHB_SCOM_HV_IND_ADDR_ADDR PPC_BITMASK(51, 63) +#define PHB_SCOM_HV_IND_DATA 0x01 +#define PHB_SCOM_ETU_LEM_FIR 0x08 +#define PHB_SCOM_ETU_LEM_FIR_AND 0x09 +#define PHB_SCOM_ETU_LEM_FIR_OR 0x0a +#define PHB_SCOM_ETU_LEM_FIR_MSK 0x0b +#define PHB_SCOM_ETU_LEM_ERR_MSK_AND 0x0c +#define PHB_SCOM_ETU_LEM_ERR_MSK_OR 0x0d +#define PHB_SCOM_ETU_LEM_ACT0 0x0e +#define PHB_SCOM_ETU_LEM_ACT1 0x0f +#define PHB_SCOM_ETU_LEM_WOF 0x10 +#define PHB_SCOM_ETU_PMON_CONFIG 0x17 +#define PHB_SCOM_ETU_PMON_CTR0 0x18 +#define PHB_SCOM_ETU_PMON_CTR1 0x19 +#define PHB_SCOM_ETU_PMON_CTR2 0x1a +#define PHB_SCOM_ETU_PMON_CTR3 0x1b + + +/* + * PHB MMIO registers + */ + +/* PHB Fundamental register set A */ +#define PHB_LSI_SOURCE_ID 0x100 +#define PHB_LSI_SRC_ID PPC_BITMASK(4, 12) +#define PHB_DMA_CHAN_STATUS 0x110 +#define PHB_DMA_CHAN_ANY_ERR PPC_BIT(27) +#define PHB_DMA_CHAN_ANY_ERR1 PPC_BIT(28) +#define PHB_DMA_CHAN_ANY_FREEZE PPC_BIT(29) +#define PHB_CPU_LOADSTORE_STATUS 0x120 +#define PHB_CPU_LS_ANY_ERR PPC_BIT(27) +#define PHB_CPU_LS_ANY_ERR1 PPC_BIT(28) +#define PHB_CPU_LS_ANY_FREEZE PPC_BIT(29) +#define PHB_CONFIG_DATA 0x130 +#define PHB_LOCK0 0x138 +#define PHB_CONFIG_ADDRESS 0x140 +#define PHB_CA_ENABLE PPC_BIT(0) +#define PHB_CA_STATUS PPC_BITMASK(1, 3) +#define PHB_CA_STATUS_GOOD 0 +#define PHB_CA_STATUS_UR 1 +#define PHB_CA_STATUS_CRS 2 +#define PHB_CA_STATUS_CA 4 +#define PHB_CA_BUS PPC_BITMASK(4, 11) +#define PHB_CA_DEV PPC_BITMASK(12, 16) +#define PHB_CA_FUNC PPC_BITMASK(17, 19) +#define PHB_CA_BDFN PPC_BITMASK(4, 19) /* bus,dev,func */ +#define PHB_CA_REG PPC_BITMASK(20, 31) +#define PHB_CA_PE PPC_BITMASK(39, 47) +#define PHB_LOCK1 0x148 +#define PHB_PHB4_CONFIG 0x160 +#define PHB_PHB4C_32BIT_MSI_EN PPC_BIT(8) +#define PHB_PHB4C_64BIT_MSI_EN PPC_BIT(14) +#define PHB_RTT_BAR 0x168 +#define PHB_RTT_BAR_ENABLE PPC_BIT(0) +#define PHB_RTT_BASE_ADDRESS_MASK PPC_BITMASK(8, 46) +#define PHB_PELTV_BAR 0x188 +#define PHB_PELTV_BAR_ENABLE PPC_BIT(0) +#define PHB_PELTV_BASE_ADDRESS PPC_BITMASK(8, 50) +#define PHB_M32_START_ADDR 0x1a0 +#define PHB_PEST_BAR 0x1a8 +#define PHB_PEST_BAR_ENABLE PPC_BIT(0) +#define PHB_PEST_BASE_ADDRESS PPC_BITMASK(8, 51) +#define PHB_ASN_CMPM 0x1C0 +#define PHB_ASN_CMPM_ENABLE PPC_BIT(63) +#define PHB_CAPI_CMPM 0x1C8 +#define PHB_CAPI_CMPM_ENABLE PPC_BIT(63) +#define PHB_M64_AOMASK 0x1d0 +#define PHB_M64_UPPER_BITS 0x1f0 +#define PHB_NXLATE_PREFIX 0x1f8 +#define PHB_DMARD_SYNC 0x200 +#define PHB_DMARD_SYNC_START PPC_BIT(0) +#define PHB_DMARD_SYNC_COMPLETE PPC_BIT(1) +#define PHB_RTC_INVALIDATE 0x208 +#define PHB_RTC_INVALIDATE_ALL PPC_BIT(0) +#define PHB_RTC_INVALIDATE_RID PPC_BITMASK(16, 31) +#define PHB_TCE_KILL 0x210 +#define PHB_TCE_KILL_ALL PPC_BIT(0) +#define PHB_TCE_KILL_PE PPC_BIT(1) +#define PHB_TCE_KILL_ONE PPC_BIT(2) +#define PHB_TCE_KILL_PSEL PPC_BIT(3) +#define PHB_TCE_KILL_64K 0x1000 /* Address override */ +#define PHB_TCE_KILL_2M 0x2000 /* Address override */ +#define PHB_TCE_KILL_1G 0x3000 /* Address override */ +#define PHB_TCE_KILL_PENUM PPC_BITMASK(55, 63) +#define PHB_TCE_SPEC_CTL 0x218 +#define PHB_IODA_ADDR 0x220 +#define PHB_IODA_AD_AUTOINC PPC_BIT(0) +#define PHB_IODA_AD_TSEL PPC_BITMASK(11, 15) +#define PHB_IODA_AD_MIST_PWV PPC_BITMASK(28, 31) +#define PHB_IODA_AD_TADR PPC_BITMASK(54, 63) +#define PHB_IODA_DATA0 0x228 +#define PHB_PHB4_GEN_CAP 0x250 +#define PHB_PHB4_TCE_CAP 0x258 +#define PHB_PHB4_IRQ_CAP 0x260 +#define PHB_PHB4_EEH_CAP 0x268 +#define PHB_PAPR_ERR_INJ_CTL 0x2b0 +#define PHB_PAPR_ERR_INJ_CTL_INB PPC_BIT(0) +#define PHB_PAPR_ERR_INJ_CTL_OUTB PPC_BIT(1) +#define PHB_PAPR_ERR_INJ_CTL_STICKY PPC_BIT(2) +#define PHB_PAPR_ERR_INJ_CTL_CFG PPC_BIT(3) +#define PHB_PAPR_ERR_INJ_CTL_RD PPC_BIT(4) +#define PHB_PAPR_ERR_INJ_CTL_WR PPC_BIT(5) +#define PHB_PAPR_ERR_INJ_CTL_FREEZE PPC_BIT(6) +#define PHB_PAPR_ERR_INJ_ADDR 0x2b8 +#define PHB_PAPR_ERR_INJ_ADDR_MMIO PPC_BITMASK(16, 63) +#define PHB_PAPR_ERR_INJ_MASK 0x2c0 +#define PHB_PAPR_ERR_INJ_MASK_CFG PPC_BITMASK(4, 11) +#define PHB_PAPR_ERR_INJ_MASK_CFG_ALL PPC_BITMASK(4, 19) +#define PHB_PAPR_ERR_INJ_MASK_MMIO PPC_BITMASK(16, 63) +#define PHB_ETU_ERR_SUMMARY 0x2c8 +#define PHB_INT_NOTIFY_ADDR 0x300 +#define PHB_INT_NOTIFY_INDEX 0x308 + +/* Fundamental register set B */ +#define PHB_VERSION 0x800 +#define PHB_CTRLR 0x810 +#define PHB_CTRLR_IRQ_PGSZ_64K PPC_BIT(11) +#define PHB_CTRLR_IRQ_STORE_EOI PPC_BIT(12) +#define PHB_CTRLR_MMIO_RD_STRICT PPC_BIT(13) +#define PHB_CTRLR_MMIO_EEH_DISABLE PPC_BIT(14) +#define PHB_CTRLR_CFG_EEH_BLOCK PPC_BIT(15) +#define PHB_CTRLR_FENCE_LNKILL_DIS PPC_BIT(16) +#define PHB_CTRLR_TVT_ADDR_SEL PPC_BITMASK(17, 19) +#define TVT_DD1_1_PER_PE 0 +#define TVT_DD1_2_PER_PE 1 +#define TVT_DD1_4_PER_PE 2 +#define TVT_DD1_8_PER_PE 3 +#define TVT_DD1_16_PER_PE 4 +#define TVT_2_PER_PE 0 +#define TVT_4_PER_PE 1 +#define TVT_8_PER_PE 2 +#define TVT_16_PER_PE 3 +#define PHB_CTRLR_DMA_RD_SPACING PPC_BITMASK(28, 31) +#define PHB_AIB_FENCE_CTRL 0x860 +#define PHB_TCE_TAG_ENABLE 0x868 +#define PHB_TCE_WATERMARK 0x870 +#define PHB_TIMEOUT_CTRL1 0x878 +#define PHB_TIMEOUT_CTRL2 0x880 +#define PHB_Q_DMA_R 0x888 +#define PHB_Q_DMA_R_QUIESCE_DMA PPC_BIT(0) +#define PHB_Q_DMA_R_AUTORESET PPC_BIT(1) +#define PHB_Q_DMA_R_DMA_RESP_STATUS PPC_BIT(4) +#define PHB_Q_DMA_R_MMIO_RESP_STATUS PPC_BIT(5) +#define PHB_Q_DMA_R_TCE_RESP_STATUS PPC_BIT(6) +#define PHB_Q_DMA_R_TCE_KILL_STATUS PPC_BIT(7) +#define PHB_TCE_TAG_STATUS 0x908 + +/* FIR & Error registers */ +#define PHB_LEM_FIR_ACCUM 0xc00 +#define PHB_LEM_FIR_AND_MASK 0xc08 +#define PHB_LEM_FIR_OR_MASK 0xc10 +#define PHB_LEM_ERROR_MASK 0xc18 +#define PHB_LEM_ERROR_AND_MASK 0xc20 +#define PHB_LEM_ERROR_OR_MASK 0xc28 +#define PHB_LEM_ACTION0 0xc30 +#define PHB_LEM_ACTION1 0xc38 +#define PHB_LEM_WOF 0xc40 +#define PHB_ERR_STATUS 0xc80 +#define PHB_ERR1_STATUS 0xc88 +#define PHB_ERR_INJECT 0xc90 +#define PHB_ERR_LEM_ENABLE 0xc98 +#define PHB_ERR_IRQ_ENABLE 0xca0 +#define PHB_ERR_FREEZE_ENABLE 0xca8 +#define PHB_ERR_AIB_FENCE_ENABLE 0xcb0 +#define PHB_ERR_LOG_0 0xcc0 +#define PHB_ERR_LOG_1 0xcc8 +#define PHB_ERR_STATUS_MASK 0xcd0 +#define PHB_ERR1_STATUS_MASK 0xcd8 + +#define PHB_TXE_ERR_STATUS 0xd00 +#define PHB_TXE_ERR1_STATUS 0xd08 +#define PHB_TXE_ERR_INJECT 0xd10 +#define PHB_TXE_ERR_LEM_ENABLE 0xd18 +#define PHB_TXE_ERR_IRQ_ENABLE 0xd20 +#define PHB_TXE_ERR_FREEZE_ENABLE 0xd28 +#define PHB_TXE_ERR_AIB_FENCE_ENABLE 0xd30 +#define PHB_TXE_ERR_LOG_0 0xd40 +#define PHB_TXE_ERR_LOG_1 0xd48 +#define PHB_TXE_ERR_STATUS_MASK 0xd50 +#define PHB_TXE_ERR1_STATUS_MASK 0xd58 + +#define PHB_RXE_ARB_ERR_STATUS 0xd80 +#define PHB_RXE_ARB_ERR1_STATUS 0xd88 +#define PHB_RXE_ARB_ERR_INJECT 0xd90 +#define PHB_RXE_ARB_ERR_LEM_ENABLE 0xd98 +#define PHB_RXE_ARB_ERR_IRQ_ENABLE 0xda0 +#define PHB_RXE_ARB_ERR_FREEZE_ENABLE 0xda8 +#define PHB_RXE_ARB_ERR_AIB_FENCE_ENABLE 0xdb0 +#define PHB_RXE_ARB_ERR_LOG_0 0xdc0 +#define PHB_RXE_ARB_ERR_LOG_1 0xdc8 +#define PHB_RXE_ARB_ERR_STATUS_MASK 0xdd0 +#define PHB_RXE_ARB_ERR1_STATUS_MASK 0xdd8 + +#define PHB_RXE_MRG_ERR_STATUS 0xe00 +#define PHB_RXE_MRG_ERR1_STATUS 0xe08 +#define PHB_RXE_MRG_ERR_INJECT 0xe10 +#define PHB_RXE_MRG_ERR_LEM_ENABLE 0xe18 +#define PHB_RXE_MRG_ERR_IRQ_ENABLE 0xe20 +#define PHB_RXE_MRG_ERR_FREEZE_ENABLE 0xe28 +#define PHB_RXE_MRG_ERR_AIB_FENCE_ENABLE 0xe30 +#define PHB_RXE_MRG_ERR_LOG_0 0xe40 +#define PHB_RXE_MRG_ERR_LOG_1 0xe48 +#define PHB_RXE_MRG_ERR_STATUS_MASK 0xe50 +#define PHB_RXE_MRG_ERR1_STATUS_MASK 0xe58 + +#define PHB_RXE_TCE_ERR_STATUS 0xe80 +#define PHB_RXE_TCE_ERR1_STATUS 0xe88 +#define PHB_RXE_TCE_ERR_INJECT 0xe90 +#define PHB_RXE_TCE_ERR_LEM_ENABLE 0xe98 +#define PHB_RXE_TCE_ERR_IRQ_ENABLE 0xea0 +#define PHB_RXE_TCE_ERR_FREEZE_ENABLE 0xea8 +#define PHB_RXE_TCE_ERR_AIB_FENCE_ENABLE 0xeb0 +#define PHB_RXE_TCE_ERR_LOG_0 0xec0 +#define PHB_RXE_TCE_ERR_LOG_1 0xec8 +#define PHB_RXE_TCE_ERR_STATUS_MASK 0xed0 +#define PHB_RXE_TCE_ERR1_STATUS_MASK 0xed8 + +/* Performance monitor & Debug registers */ +#define PHB_TRACE_CONTROL 0xf80 +#define PHB_PERFMON_CONFIG 0xf88 +#define PHB_PERFMON_CTR0 0xf90 +#define PHB_PERFMON_CTR1 0xf98 +#define PHB_PERFMON_CTR2 0xfa0 +#define PHB_PERFMON_CTR3 0xfa8 + +/* Root complex config space memory mapped */ +#define PHB_RC_CONFIG_BASE 0x1000 +#define PHB_RC_CONFIG_SIZE 0x800 + +/* PHB4 REGB registers */ + +/* PBL core */ +#define PHB_PBL_CONTROL 0x1800 +#define PHB_PBL_TIMEOUT_CTRL 0x1810 +#define PHB_PBL_NPTAG_ENABLE 0x1820 +#define PHB_PBL_NBW_CMP_MASK 0x1830 +#define PHB_PBL_NBW_MASK_ENABLE PPC_BIT(63) +#define PHB_PBL_SYS_LINK_INIT 0x1838 +#define PHB_PBL_BUF_STATUS 0x1840 +#define PHB_PBL_ERR_STATUS 0x1900 +#define PHB_PBL_ERR1_STATUS 0x1908 +#define PHB_PBL_ERR_INJECT 0x1910 +#define PHB_PBL_ERR_INF_ENABLE 0x1920 +#define PHB_PBL_ERR_ERC_ENABLE 0x1928 +#define PHB_PBL_ERR_FAT_ENABLE 0x1930 +#define PHB_PBL_ERR_LOG_0 0x1940 +#define PHB_PBL_ERR_LOG_1 0x1948 +#define PHB_PBL_ERR_STATUS_MASK 0x1950 +#define PHB_PBL_ERR1_STATUS_MASK 0x1958 + +/* PCI-E stack */ +#define PHB_PCIE_SCR 0x1A00 +#define PHB_PCIE_SCR_SLOT_CAP PPC_BIT(15) +#define PHB_PCIE_SCR_MAXLINKSPEED PPC_BITMASK(32, 35) + + +#define PHB_PCIE_CRESET 0x1A10 +#define PHB_PCIE_CRESET_CFG_CORE PPC_BIT(0) +#define PHB_PCIE_CRESET_TLDLP PPC_BIT(1) +#define PHB_PCIE_CRESET_PBL PPC_BIT(2) +#define PHB_PCIE_CRESET_PERST_N PPC_BIT(3) +#define PHB_PCIE_CRESET_PIPE_N PPC_BIT(4) + + +#define PHB_PCIE_HOTPLUG_STATUS 0x1A20 +#define PHB_PCIE_HPSTAT_PRESENCE PPC_BIT(10) + +#define PHB_PCIE_DLP_TRAIN_CTL 0x1A40 +#define PHB_PCIE_DLP_LINK_WIDTH PPC_BITMASK(30, 35) +#define PHB_PCIE_DLP_LINK_SPEED PPC_BITMASK(36, 39) +#define PHB_PCIE_DLP_LTSSM_TRC PPC_BITMASK(24, 27) +#define PHB_PCIE_DLP_LTSSM_RESET 0 +#define PHB_PCIE_DLP_LTSSM_DETECT 1 +#define PHB_PCIE_DLP_LTSSM_POLLING 2 +#define PHB_PCIE_DLP_LTSSM_CONFIG 3 +#define PHB_PCIE_DLP_LTSSM_L0 4 +#define PHB_PCIE_DLP_LTSSM_REC 5 +#define PHB_PCIE_DLP_LTSSM_L1 6 +#define PHB_PCIE_DLP_LTSSM_L2 7 +#define PHB_PCIE_DLP_LTSSM_HOTRESET 8 +#define PHB_PCIE_DLP_LTSSM_DISABLED 9 +#define PHB_PCIE_DLP_LTSSM_LOOPBACK 10 +#define PHB_PCIE_DLP_TL_LINKACT PPC_BIT(23) +#define PHB_PCIE_DLP_DL_PGRESET PPC_BIT(22) +#define PHB_PCIE_DLP_TRAINING PPC_BIT(20) +#define PHB_PCIE_DLP_INBAND_PRESENCE PPC_BIT(19) + +#define PHB_PCIE_DLP_CTL 0x1A78 +#define PHB_PCIE_DLP_CTL_BYPASS_PH2 PPC_BIT(4) +#define PHB_PCIE_DLP_CTL_BYPASS_PH3 PPC_BIT(5) + +#define PHB_PCIE_DLP_TRWCTL 0x1A80 +#define PHB_PCIE_DLP_TRWCTL_EN PPC_BIT(0) + +#define PHB_PCIE_DLP_ERRLOG1 0x1AA0 +#define PHB_PCIE_DLP_ERRLOG2 0x1AA8 +#define PHB_PCIE_DLP_ERR_STATUS 0x1AB0 +#define PHB_PCIE_DLP_ERR_COUNTERS 0x1AB8 + +#define PHB_PCIE_LANE_EQ_CNTL0 0x1AD0 +#define PHB_PCIE_LANE_EQ_CNTL1 0x1AD8 +#define PHB_PCIE_LANE_EQ_CNTL2 0x1AE0 +#define PHB_PCIE_LANE_EQ_CNTL3 0x1AE8 +#define PHB_PCIE_LANE_EQ_CNTL20 0x1AF0 +#define PHB_PCIE_LANE_EQ_CNTL21 0x1AF8 +#define PHB_PCIE_LANE_EQ_CNTL22 0x1B00 /* DD1 only */ +#define PHB_PCIE_LANE_EQ_CNTL23 0x1B08 /* DD1 only */ +#define PHB_PCIE_TRACE_CTRL 0x1B20 +#define PHB_PCIE_MISC_STRAP 0x1B30 + +/* Error */ +#define PHB_REGB_ERR_STATUS 0x1C00 +#define PHB_REGB_ERR1_STATUS 0x1C08 +#define PHB_REGB_ERR_INJECT 0x1C10 +#define PHB_REGB_ERR_INF_ENABLE 0x1C20 +#define PHB_REGB_ERR_ERC_ENABLE 0x1C28 +#define PHB_REGB_ERR_FAT_ENABLE 0x1C30 +#define PHB_REGB_ERR_LOG_0 0x1C40 +#define PHB_REGB_ERR_LOG_1 0x1C48 +#define PHB_REGB_ERR_STATUS_MASK 0x1C50 +#define PHB_REGB_ERR1_STATUS_MASK 0x1C58 + +/* + * IODA3 on-chip tables + */ + +#define IODA3_TBL_LIST 1 +#define IODA3_TBL_MIST 2 +#define IODA3_TBL_RCAM 5 +#define IODA3_TBL_MRT 6 +#define IODA3_TBL_PESTA 7 +#define IODA3_TBL_PESTB 8 +#define IODA3_TBL_TVT 9 +#define IODA3_TBL_TCR 10 +#define IODA3_TBL_TDR 11 +#define IODA3_TBL_MBT 16 +#define IODA3_TBL_MDT 17 +#define IODA3_TBL_PEEV 20 + +/* LIST */ +#define IODA3_LIST_P PPC_BIT(6) +#define IODA3_LIST_Q PPC_BIT(7) +#define IODA3_LIST_STATE PPC_BIT(14) + +/* MIST */ +#define IODA3_MIST_P3 PPC_BIT(48 + 0) +#define IODA3_MIST_Q3 PPC_BIT(48 + 1) +#define IODA3_MIST_PE3 PPC_BITMASK(48 + 4, 48 + 15) + +/* TVT */ +#define IODA3_TVT_TABLE_ADDR PPC_BITMASK(0, 47) +#define IODA3_TVT_NUM_LEVELS PPC_BITMASK(48, 50) +#define IODA3_TVE_1_LEVEL 0 +#define IODA3_TVE_2_LEVELS 1 +#define IODA3_TVE_3_LEVELS 2 +#define IODA3_TVE_4_LEVELS 3 +#define IODA3_TVE_5_LEVELS 4 +#define IODA3_TVT_TCE_TABLE_SIZE PPC_BITMASK(51, 55) +#define IODA3_TVT_NON_TRANSLATE_50 PPC_BIT(56) +#define IODA3_TVT_IO_PSIZE PPC_BITMASK(59, 63) + +/* PESTA */ +#define IODA3_PESTA_MMIO_FROZEN PPC_BIT(0) +#define IODA3_PESTA_TRANS_TYPE PPC_BITMASK(5, 7) +#define IODA3_PESTA_TRANS_TYPE_MMIOLOAD 0x4 +#define IODA3_PESTA_CA_CMPLT_TMT PPC_BIT(8) +#define IODA3_PESTA_UR PPC_BIT(9) + +/* PESTB */ +#define IODA3_PESTB_DMA_STOPPED PPC_BIT(0) + +/* MDT */ +/* FIXME: check this field with Eric and add a B, C and D */ +#define IODA3_MDT_PE_A PPC_BITMASK(0, 15) +#define IODA3_MDT_PE_B PPC_BITMASK(16, 31) +#define IODA3_MDT_PE_C PPC_BITMASK(32, 47) +#define IODA3_MDT_PE_D PPC_BITMASK(48, 63) + +/* MBT */ +#define IODA3_MBT0_ENABLE PPC_BIT(0) +#define IODA3_MBT0_TYPE PPC_BIT(1) +#define IODA3_MBT0_TYPE_M32 IODA3_MBT0_TYPE +#define IODA3_MBT0_TYPE_M64 0 +#define IODA3_MBT0_MODE PPC_BITMASK(2, 3) +#define IODA3_MBT0_MODE_PE_SEG 0 +#define IODA3_MBT0_MODE_MDT 1 +#define IODA3_MBT0_MODE_SINGLE_PE 2 +#define IODA3_MBT0_SEG_DIV PPC_BITMASK(4, 5) +#define IODA3_MBT0_SEG_DIV_MAX 0 +#define IODA3_MBT0_SEG_DIV_128 1 +#define IODA3_MBT0_SEG_DIV_64 2 +#define IODA3_MBT0_SEG_DIV_8 3 +#define IODA3_MBT0_MDT_COLUMN PPC_BITMASK(4, 5) +#define IODA3_MBT0_BASE_ADDR PPC_BITMASK(8, 51) + +#define IODA3_MBT1_ENABLE PPC_BIT(0) +#define IODA3_MBT1_MASK PPC_BITMASK(8, 51) +#define IODA3_MBT1_SEG_BASE PPC_BITMASK(55, 63) +#define IODA3_MBT1_SINGLE_PE_NUM PPC_BITMASK(55, 63) + +/* + * IODA3 in-memory tables + */ + +/* + * PEST + * + * 2x8 bytes entries, PEST0 and PEST1 + */ + +#define IODA3_PEST0_MMIO_CAUSE PPC_BIT(2) +#define IODA3_PEST0_CFG_READ PPC_BIT(3) +#define IODA3_PEST0_CFG_WRITE PPC_BIT(4) +#define IODA3_PEST0_TTYPE PPC_BITMASK(5, 7) +#define PEST_TTYPE_DMA_WRITE 0 +#define PEST_TTYPE_MSI 1 +#define PEST_TTYPE_DMA_READ 2 +#define PEST_TTYPE_DMA_READ_RESP 3 +#define PEST_TTYPE_MMIO_LOAD 4 +#define PEST_TTYPE_MMIO_STORE 5 +#define PEST_TTYPE_OTHER 7 +#define IODA3_PEST0_CA_RETURN PPC_BIT(8) +#define IODA3_PEST0_UR_RETURN PPC_BIT(9) +#define IODA3_PEST0_PCIE_NONFATAL PPC_BIT(10) +#define IODA3_PEST0_PCIE_FATAL PPC_BIT(11) +#define IODA3_PEST0_PARITY_UE PPC_BIT(13) +#define IODA3_PEST0_PCIE_CORRECTABLE PPC_BIT(14) +#define IODA3_PEST0_PCIE_INTERRUPT PPC_BIT(15) +#define IODA3_PEST0_MMIO_XLATE PPC_BIT(16) +#define IODA3_PEST0_IODA3_ERROR PPC_BIT(16) /* Same bit as MMIO xlate */ +#define IODA3_PEST0_TCE_PAGE_FAULT PPC_BIT(18) +#define IODA3_PEST0_TCE_ACCESS_FAULT PPC_BIT(19) +#define IODA3_PEST0_DMA_RESP_TIMEOUT PPC_BIT(20) +#define IODA3_PEST0_AIB_SIZE_INVALID PPC_BIT(21) +#define IODA3_PEST0_LEM_BIT PPC_BITMASK(26, 31) +#define IODA3_PEST0_RID PPC_BITMASK(32, 47) +#define IODA3_PEST0_MSI_DATA PPC_BITMASK(48, 63) + +#define IODA3_PEST1_FAIL_ADDR PPC_BITMASK(3, 63) + + +#endif /* PCI_HOST_PNV_PHB4_REGS_H */ diff --git a/include/hw/pci-host/q35.h b/include/hw/pci-host/q35.h index b3bcf2e632..070305f83d 100644 --- a/include/hw/pci-host/q35.h +++ b/include/hw/pci-host/q35.h @@ -22,16 +22,11 @@ #ifndef HW_Q35_H #define HW_Q35_H -#include "hw/isa/isa.h" -#include "hw/sysbus.h" -#include "hw/i386/pc.h" -#include "hw/isa/apm.h" #include "hw/pci/pci.h" #include "hw/pci/pcie_host.h" -#include "hw/acpi/acpi.h" -#include "hw/acpi/ich9.h" #include "hw/pci-host/pam.h" -#include "hw/i386/intel_iommu.h" +#include "qemu/units.h" +#include "qemu/range.h" #define TYPE_Q35_HOST_DEVICE "q35-pcihost" #define Q35_HOST_DEVICE(obj) \ @@ -54,6 +49,8 @@ typedef struct MCHPCIState { MemoryRegion smram_region, open_high_smram; MemoryRegion smram, low_smram, high_smram; MemoryRegion tseg_blackhole, tseg_window; + MemoryRegion smbase_blackhole, smbase_window; + bool has_smram_at_smbase; Range pci_hole; uint64_t below_4g_mem_size; uint64_t above_4g_mem_size; @@ -97,6 +94,13 @@ typedef struct Q35PCIHost { #define MCH_HOST_BRIDGE_EXT_TSEG_MBYTES_QUERY 0xffff #define MCH_HOST_BRIDGE_EXT_TSEG_MBYTES_MAX 0xfff +#define MCH_HOST_BRIDGE_SMBASE_SIZE (128 * KiB) +#define MCH_HOST_BRIDGE_SMBASE_ADDR 0x30000 +#define MCH_HOST_BRIDGE_F_SMBASE 0x9c +#define MCH_HOST_BRIDGE_F_SMBASE_QUERY 0xff +#define MCH_HOST_BRIDGE_F_SMBASE_IN_RAM 0x01 +#define MCH_HOST_BRIDGE_F_SMBASE_LCK 0x02 + #define MCH_HOST_BRIDGE_PCIEXBAR 0x60 /* 64bit register */ #define MCH_HOST_BRIDGE_PCIEXBAR_SIZE 8 /* 64bit register */ #define MCH_HOST_BRIDGE_PCIEXBAR_DEFAULT 0xb0000000 diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h index db75c6dfd0..cfedf5a995 100644 --- a/include/hw/pci/pci.h +++ b/include/hw/pci/pci.h @@ -86,6 +86,7 @@ extern bool pci_available; #define PCI_DEVICE_ID_VIRTIO_9P 0x1009 #define PCI_DEVICE_ID_VIRTIO_VSOCK 0x1012 #define PCI_DEVICE_ID_VIRTIO_PMEM 0x1013 +#define PCI_DEVICE_ID_VIRTIO_IOMMU 0x1014 #define PCI_VENDOR_ID_REDHAT 0x1b36 #define PCI_DEVICE_ID_REDHAT_BRIDGE 0x0001 @@ -174,7 +175,7 @@ enum { #define QEMU_PCI_CAP_MULTIFUNCTION_BITNR 3 QEMU_PCI_CAP_MULTIFUNCTION = (1 << QEMU_PCI_CAP_MULTIFUNCTION_BITNR), - /* command register SERR bit enabled */ + /* command register SERR bit enabled - unused since QEMU v5.0 */ #define QEMU_PCI_CAP_SERR_BITNR 4 QEMU_PCI_CAP_SERR = (1 << QEMU_PCI_CAP_SERR_BITNR), /* Standard hot plug controller. */ 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/hw/pci/pci_host.h b/include/hw/pci/pci_host.h index ba31595fc7..9ce088bd13 100644 --- a/include/hw/pci/pci_host.h +++ b/include/hw/pci/pci_host.h @@ -62,8 +62,8 @@ void pci_host_config_write_common(PCIDevice *pci_dev, uint32_t addr, uint32_t pci_host_config_read_common(PCIDevice *pci_dev, uint32_t addr, uint32_t limit, uint32_t len); -void pci_data_write(PCIBus *s, uint32_t addr, uint32_t val, int len); -uint32_t pci_data_read(PCIBus *s, uint32_t addr, int len); +void pci_data_write(PCIBus *s, uint32_t addr, uint32_t val, unsigned len); +uint32_t pci_data_read(PCIBus *s, uint32_t addr, unsigned len); extern const MemoryRegionOps pci_host_conf_le_ops; extern const MemoryRegionOps pci_host_conf_be_ops; diff --git a/include/hw/pci/pcie.h b/include/hw/pci/pcie.h index 7064875835..14c58ebdb6 100644 --- a/include/hw/pci/pcie.h +++ b/include/hw/pci/pcie.h @@ -104,7 +104,7 @@ void pcie_cap_deverr_reset(PCIDevice *dev); void pcie_cap_lnkctl_init(PCIDevice *dev); void pcie_cap_lnkctl_reset(PCIDevice *dev); -void pcie_cap_slot_init(PCIDevice *dev, uint16_t slot); +void pcie_cap_slot_init(PCIDevice *dev, PCIESlot *s); void pcie_cap_slot_reset(PCIDevice *dev); void pcie_cap_slot_get(PCIDevice *dev, uint16_t *slt_ctl, uint16_t *slt_sta); void pcie_cap_slot_write_config(PCIDevice *dev, diff --git a/include/hw/pci/pcie_port.h b/include/hw/pci/pcie_port.h index 7515430087..caae57573b 100644 --- a/include/hw/pci/pcie_port.h +++ b/include/hw/pci/pcie_port.h @@ -55,6 +55,9 @@ struct PCIESlot { /* Disable ACS (really for a pcie_root_port) */ bool disable_acs; + + /* Indicates whether hot-plug is enabled on the slot */ + bool hotplug; QLIST_ENTRY(PCIESlot) next; }; @@ -72,6 +75,7 @@ void pcie_chassis_del_slot(PCIESlot *s); typedef struct PCIERootPortClass { PCIDeviceClass parent_class; DeviceRealize parent_realize; + DeviceReset parent_reset; uint8_t (*aer_vector)(const PCIDevice *dev); int (*interrupts_init)(PCIDevice *dev, Error **errp); diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h index 0b4c722e6b..d4b0b0e2ff 100644 --- a/include/hw/ppc/pnv.h +++ b/include/hw/ppc/pnv.h @@ -24,11 +24,14 @@ #include "hw/sysbus.h" #include "hw/ipmi/ipmi.h" #include "hw/ppc/pnv_lpc.h" +#include "hw/ppc/pnv_pnor.h" #include "hw/ppc/pnv_psi.h" #include "hw/ppc/pnv_occ.h" #include "hw/ppc/pnv_homer.h" #include "hw/ppc/pnv_xive.h" #include "hw/ppc/pnv_core.h" +#include "hw/pci-host/pnv_phb3.h" +#include "hw/pci-host/pnv_phb4.h" #define TYPE_PNV_CHIP "pnv-chip" #define PNV_CHIP(obj) OBJECT_CHECK(PnvChip, (obj), TYPE_PNV_CHIP) @@ -37,13 +40,6 @@ #define PNV_CHIP_GET_CLASS(obj) \ OBJECT_GET_CLASS(PnvChipClass, (obj), TYPE_PNV_CHIP) -typedef enum PnvChipType { - PNV_CHIP_POWER8E, /* AKA Murano (default) */ - PNV_CHIP_POWER8, /* AKA Venice */ - PNV_CHIP_POWER8NVL, /* AKA Naples */ - PNV_CHIP_POWER9, /* AKA Nimbus */ -} PnvChipType; - typedef struct PnvChip { /*< private >*/ SysBusDevice parent_obj; @@ -54,8 +50,11 @@ typedef struct PnvChip { uint64_t ram_size; uint32_t nr_cores; + uint32_t nr_threads; uint64_t cores_mask; - void *cores; + PnvCore **cores; + + uint32_t num_phbs; MemoryRegion xscom_mmio; MemoryRegion xscom; @@ -78,6 +77,11 @@ typedef struct Pnv8Chip { Pnv8Psi psi; PnvOCC occ; PnvHomer homer; + +#define PNV8_CHIP_PHB3_MAX 4 + PnvPHB3 phbs[PNV8_CHIP_PHB3_MAX]; + + XICSFabric *xics; } Pnv8Chip; #define TYPE_PNV9_CHIP "pnv9-chip" @@ -96,16 +100,37 @@ typedef struct Pnv9Chip { uint32_t nr_quads; PnvQuad *quads; + +#define PNV9_CHIP_MAX_PEC 3 + PnvPhb4PecState pecs[PNV9_CHIP_MAX_PEC]; } Pnv9Chip; +/* + * A SMT8 fused core is a pair of SMT4 cores. + */ +#define PNV9_PIR2FUSEDCORE(pir) (((pir) >> 3) & 0xf) +#define PNV9_PIR2CHIP(pir) (((pir) >> 8) & 0x7f) + +#define TYPE_PNV10_CHIP "pnv10-chip" +#define PNV10_CHIP(obj) OBJECT_CHECK(Pnv10Chip, (obj), TYPE_PNV10_CHIP) + +typedef struct Pnv10Chip { + /*< private >*/ + PnvChip parent_obj; + + /*< public >*/ + Pnv9Psi psi; + PnvLpcController lpc; +} Pnv10Chip; + typedef struct PnvChipClass { /*< private >*/ SysBusDeviceClass parent_class; /*< public >*/ - PnvChipType chip_type; uint64_t chip_cfam_id; uint64_t cores_mask; + uint32_t num_phbs; DeviceRealize parent_realize; @@ -113,9 +138,12 @@ typedef struct PnvChipClass { void (*intc_create)(PnvChip *chip, PowerPCCPU *cpu, Error **errp); void (*intc_reset)(PnvChip *chip, PowerPCCPU *cpu); void (*intc_destroy)(PnvChip *chip, PowerPCCPU *cpu); + void (*intc_print_info)(PnvChip *chip, PowerPCCPU *cpu, Monitor *mon); ISABus *(*isa_create)(PnvChip *chip, Error **errp); void (*dt_populate)(PnvChip *chip, void *fdt); void (*pic_print_info)(PnvChip *chip, Monitor *mon); + uint64_t (*xscom_core_base)(PnvChip *chip, uint32_t core_id); + uint32_t (*xscom_pcba)(PnvChip *chip, uint64_t addr); } PnvChipClass; #define PNV_CHIP_TYPE_SUFFIX "-" TYPE_PNV_CHIP @@ -137,6 +165,10 @@ typedef struct PnvChipClass { #define PNV_CHIP_POWER9(obj) \ OBJECT_CHECK(PnvChip, (obj), TYPE_PNV_CHIP_POWER9) +#define TYPE_PNV_CHIP_POWER10 PNV_CHIP_TYPE_NAME("power10_v1.0") +#define PNV_CHIP_POWER10(obj) \ + OBJECT_CHECK(PnvChip, (obj), TYPE_PNV_CHIP_POWER10) + /* * This generates a HW chip id depending on an index, as found on a * two socket system with dual chip modules : @@ -156,11 +188,30 @@ typedef struct PnvChipClass { #define PNV_CHIP_INDEX(chip) \ (((chip)->chip_id >> 2) * 2 + ((chip)->chip_id & 0x3)) +PowerPCCPU *pnv_chip_find_cpu(PnvChip *chip, uint32_t pir); + #define TYPE_PNV_MACHINE MACHINE_TYPE_NAME("powernv") #define PNV_MACHINE(obj) \ OBJECT_CHECK(PnvMachineState, (obj), TYPE_PNV_MACHINE) +#define PNV_MACHINE_GET_CLASS(obj) \ + OBJECT_GET_CLASS(PnvMachineClass, obj, TYPE_PNV_MACHINE) +#define PNV_MACHINE_CLASS(klass) \ + OBJECT_CLASS_CHECK(PnvMachineClass, klass, TYPE_PNV_MACHINE) -typedef struct PnvMachineState { +typedef struct PnvMachineState PnvMachineState; + +typedef struct PnvMachineClass { + /*< private >*/ + MachineClass parent_class; + + /*< public >*/ + const char *compat; + int compat_size; + + void (*dt_power_mgt)(PnvMachineState *pnv, void *fdt); +} PnvMachineClass; + +struct PnvMachineState { /*< private >*/ MachineState parent_obj; @@ -175,17 +226,11 @@ typedef struct PnvMachineState { IPMIBmc *bmc; Notifier powerdown_notifier; -} PnvMachineState; -static inline bool pnv_chip_is_power9(const PnvChip *chip) -{ - return PNV_CHIP_GET_CLASS(chip)->chip_type == PNV_CHIP_POWER9; -} + PnvPnor *pnor; -static inline bool pnv_is_power9(PnvMachineState *pnv) -{ - return pnv_chip_is_power9(pnv->chips[0]); -} + hwaddr fw_load_addr; +}; #define PNV_FDT_ADDR 0x01000000 #define PNV_TIMEBASE_FREQ 512000000ULL @@ -195,6 +240,9 @@ static inline bool pnv_is_power9(PnvMachineState *pnv) */ void pnv_dt_bmc_sensors(IPMIBmc *bmc, void *fdt); void pnv_bmc_powerdown(IPMIBmc *bmc); +IPMIBmc *pnv_bmc_create(PnvPnor *pnor); +IPMIBmc *pnv_bmc_find(Error **errp); +void pnv_bmc_set_pnor(IPMIBmc *bmc, PnvPnor *pnor); /* * POWER8 MMIO base addresses @@ -203,12 +251,12 @@ void pnv_bmc_powerdown(IPMIBmc *bmc); #define PNV_XSCOM_BASE(chip) \ (0x0003fc0000000000ull + ((uint64_t)(chip)->chip_id) * PNV_XSCOM_SIZE) -#define PNV_OCC_COMMON_AREA_SIZE 0x0000000000700000ull -#define PNV_OCC_COMMON_AREA(chip) \ - (0x7fff800000ull + ((uint64_t)PNV_CHIP_INDEX(chip) * \ - PNV_OCC_COMMON_AREA_SIZE)) +#define PNV_OCC_COMMON_AREA_SIZE 0x0000000000800000ull +#define PNV_OCC_COMMON_AREA_BASE 0x7fff800000ull +#define PNV_OCC_SENSOR_BASE(chip) (PNV_OCC_COMMON_AREA_BASE + \ + PNV_OCC_SENSOR_DATA_BLOCK_BASE(PNV_CHIP_INDEX(chip))) -#define PNV_HOMER_SIZE 0x0000000000300000ull +#define PNV_HOMER_SIZE 0x0000000000400000ull #define PNV_HOMER_BASE(chip) \ (0x7ffd800000ull + ((uint64_t)PNV_CHIP_INDEX(chip)) * PNV_HOMER_SIZE) @@ -271,12 +319,31 @@ void pnv_bmc_powerdown(IPMIBmc *bmc); #define PNV9_XSCOM_SIZE 0x0000000400000000ull #define PNV9_XSCOM_BASE(chip) PNV9_CHIP_BASE(chip, 0x00603fc00000000ull) -#define PNV9_OCC_COMMON_AREA_SIZE 0x0000000000700000ull -#define PNV9_OCC_COMMON_AREA(chip) \ - (0x203fff800000ull + ((uint64_t)PNV_CHIP_INDEX(chip) * \ - PNV9_OCC_COMMON_AREA_SIZE)) +#define PNV9_OCC_COMMON_AREA_SIZE 0x0000000000800000ull +#define PNV9_OCC_COMMON_AREA_BASE 0x203fff800000ull +#define PNV9_OCC_SENSOR_BASE(chip) (PNV9_OCC_COMMON_AREA_BASE + \ + PNV_OCC_SENSOR_DATA_BLOCK_BASE(PNV_CHIP_INDEX(chip))) -#define PNV9_HOMER_SIZE 0x0000000000300000ull +#define PNV9_HOMER_SIZE 0x0000000000400000ull #define PNV9_HOMER_BASE(chip) \ (0x203ffd800000ull + ((uint64_t)PNV_CHIP_INDEX(chip)) * PNV9_HOMER_SIZE) + +/* + * POWER10 MMIO base addresses - 16TB stride per chip + */ +#define PNV10_CHIP_BASE(chip, base) \ + ((base) + ((uint64_t) (chip)->chip_id << 44)) + +#define PNV10_XSCOM_SIZE 0x0000000400000000ull +#define PNV10_XSCOM_BASE(chip) PNV10_CHIP_BASE(chip, 0x00603fc00000000ull) + +#define PNV10_LPCM_SIZE 0x0000000100000000ull +#define PNV10_LPCM_BASE(chip) PNV10_CHIP_BASE(chip, 0x0006030000000000ull) + +#define PNV10_PSIHB_ESB_SIZE 0x0000000000100000ull +#define PNV10_PSIHB_ESB_BASE(chip) PNV10_CHIP_BASE(chip, 0x0006030202000000ull) + +#define PNV10_PSIHB_SIZE 0x0000000000100000ull +#define PNV10_PSIHB_BASE(chip) PNV10_CHIP_BASE(chip, 0x0006030203000000ull) + #endif /* PPC_PNV_H */ diff --git a/include/hw/ppc/pnv_core.h b/include/hw/ppc/pnv_core.h index 55eee95104..113550eb7f 100644 --- a/include/hw/ppc/pnv_core.h +++ b/include/hw/ppc/pnv_core.h @@ -40,6 +40,7 @@ typedef struct PnvCore { /*< public >*/ PowerPCCPU **threads; uint32_t pir; + uint64_t hrmor; PnvChip *chip; MemoryRegion xscom_regs; diff --git a/include/hw/ppc/pnv_homer.h b/include/hw/ppc/pnv_homer.h index abaec43c2d..1e91c950f6 100644 --- a/include/hw/ppc/pnv_homer.h +++ b/include/hw/ppc/pnv_homer.h @@ -33,6 +33,7 @@ typedef struct PnvHomer { DeviceState parent; struct PnvChip *chip; + MemoryRegion pba_regs; MemoryRegion regs; } PnvHomer; @@ -44,6 +45,8 @@ typedef struct PnvHomer { typedef struct PnvHomerClass { DeviceClass parent_class; + int pba_size; + const MemoryRegionOps *pba_ops; int homer_size; const MemoryRegionOps *homer_ops; diff --git a/include/hw/ppc/pnv_lpc.h b/include/hw/ppc/pnv_lpc.h index f659410716..c1ec85d5e2 100644 --- a/include/hw/ppc/pnv_lpc.h +++ b/include/hw/ppc/pnv_lpc.h @@ -31,6 +31,9 @@ #define TYPE_PNV9_LPC TYPE_PNV_LPC "-POWER9" #define PNV9_LPC(obj) OBJECT_CHECK(PnvLpcController, (obj), TYPE_PNV9_LPC) +#define TYPE_PNV10_LPC TYPE_PNV_LPC "-POWER10" +#define PNV10_LPC(obj) OBJECT_CHECK(PnvLpcController, (obj), TYPE_PNV10_LPC) + typedef struct PnvLpcController { DeviceState parent; @@ -97,6 +100,7 @@ typedef struct PnvLpcClass { struct PnvChip; ISABus *pnv_lpc_isa_create(PnvLpcController *lpc, bool use_cpld, Error **errp); -int pnv_dt_lpc(struct PnvChip *chip, void *fdt, int root_offset); +int pnv_dt_lpc(struct PnvChip *chip, void *fdt, int root_offset, + uint64_t lpcm_addr, uint64_t lpcm_size); #endif /* PPC_PNV_LPC_H */ diff --git a/include/hw/ppc/pnv_occ.h b/include/hw/ppc/pnv_occ.h index 66b0989be6..f8d3061419 100644 --- a/include/hw/ppc/pnv_occ.h +++ b/include/hw/ppc/pnv_occ.h @@ -29,6 +29,9 @@ #define TYPE_PNV9_OCC TYPE_PNV_OCC "-POWER9" #define PNV9_OCC(obj) OBJECT_CHECK(PnvOCC, (obj), TYPE_PNV9_OCC) +#define PNV_OCC_SENSOR_DATA_BLOCK_OFFSET 0x00580000 +#define PNV_OCC_SENSOR_DATA_BLOCK_SIZE 0x00025800 + typedef struct PnvOCC { DeviceState xd; @@ -50,10 +53,11 @@ typedef struct PnvOCCClass { DeviceClass parent_class; int xscom_size; - int sram_size; const MemoryRegionOps *xscom_ops; - const MemoryRegionOps *sram_ops; int psi_irq; } PnvOCCClass; +#define PNV_OCC_SENSOR_DATA_BLOCK_BASE(i) \ + (PNV_OCC_SENSOR_DATA_BLOCK_OFFSET + (i) * PNV_OCC_SENSOR_DATA_BLOCK_SIZE) + #endif /* PPC_PNV_OCC_H */ diff --git a/include/hw/ppc/pnv_pnor.h b/include/hw/ppc/pnv_pnor.h new file mode 100644 index 0000000000..4f96abdfb4 --- /dev/null +++ b/include/hw/ppc/pnv_pnor.h @@ -0,0 +1,30 @@ +/* + * QEMU PowerNV PNOR simple model + * + * Copyright (c) 2019, IBM Corporation. + * + * This code is licensed under the GPL version 2 or later. See the + * COPYING file in the top-level directory. + */ +#ifndef _PPC_PNV_PNOR_H +#define _PPC_PNV_PNOR_H + +/* + * PNOR offset on the LPC FW address space + */ +#define PNOR_SPI_OFFSET 0x0c000000UL + +#define TYPE_PNV_PNOR "pnv-pnor" +#define PNV_PNOR(obj) OBJECT_CHECK(PnvPnor, (obj), TYPE_PNV_PNOR) + +typedef struct PnvPnor { + SysBusDevice parent_obj; + + BlockBackend *blk; + + uint8_t *storage; + int64_t size; + MemoryRegion mmio; +} PnvPnor; + +#endif /* _PPC_PNV_PNOR_H */ diff --git a/include/hw/ppc/pnv_psi.h b/include/hw/ppc/pnv_psi.h index e82df9709f..f0f5b55197 100644 --- a/include/hw/ppc/pnv_psi.h +++ b/include/hw/ppc/pnv_psi.h @@ -69,6 +69,8 @@ typedef struct Pnv9Psi { XiveSource source; } Pnv9Psi; +#define TYPE_PNV10_PSI TYPE_PNV_PSI "-POWER10" + #define PNV_PSI_CLASS(klass) \ OBJECT_CLASS_CHECK(PnvPsiClass, (klass), TYPE_PNV_PSI) #define PNV_PSI_GET_CLASS(obj) \ @@ -77,10 +79,11 @@ typedef struct Pnv9Psi { typedef struct PnvPsiClass { SysBusDeviceClass parent_class; - int chip_type; uint32_t xscom_pcba; uint32_t xscom_size; uint64_t bar_mask; + const char *compat; + int compat_size; void (*irq_set)(PnvPsi *psi, int, bool state); } PnvPsiClass; diff --git a/include/hw/ppc/pnv_xive.h b/include/hw/ppc/pnv_xive.h index 4fdaa9247d..76cf16f644 100644 --- a/include/hw/ppc/pnv_xive.h +++ b/include/hw/ppc/pnv_xive.h @@ -16,6 +16,10 @@ struct PnvChip; #define TYPE_PNV_XIVE "pnv-xive" #define PNV_XIVE(obj) OBJECT_CHECK(PnvXive, (obj), TYPE_PNV_XIVE) +#define PNV_XIVE_CLASS(klass) \ + OBJECT_CLASS_CHECK(PnvXiveClass, (klass), TYPE_PNV_XIVE) +#define PNV_XIVE_GET_CLASS(obj) \ + OBJECT_GET_CLASS(PnvXiveClass, (obj), TYPE_PNV_XIVE) #define XIVE_BLOCK_MAX 16 @@ -72,9 +76,6 @@ typedef struct PnvXive { /* Interrupt controller registers */ uint64_t regs[0x300]; - /* Can be configured by FW */ - uint32_t tctx_chipid; - /* * Virtual Structure Descriptor tables : EAT, SBE, ENDT, NVTT, IRQ * These are in a SRAM protected by ECC. @@ -88,6 +89,12 @@ typedef struct PnvXive { uint64_t edt[XIVE_TABLE_EDT_MAX]; } PnvXive; +typedef struct PnvXiveClass { + XiveRouterClass parent_class; + + DeviceRealize parent_realize; +} PnvXiveClass; + void pnv_xive_pic_print_info(PnvXive *xive, Monitor *mon); #endif /* PPC_PNV_XIVE_H */ diff --git a/include/hw/ppc/pnv_xscom.h b/include/hw/ppc/pnv_xscom.h index 67641ed278..09156a5a7a 100644 --- a/include/hw/ppc/pnv_xscom.h +++ b/include/hw/ppc/pnv_xscom.h @@ -22,13 +22,11 @@ #include "qom/object.h" -typedef struct PnvXScomInterface { - Object parent; -} PnvXScomInterface; +typedef struct PnvXScomInterface PnvXScomInterface; #define TYPE_PNV_XSCOM_INTERFACE "pnv-xscom-interface" #define PNV_XSCOM_INTERFACE(obj) \ - OBJECT_CHECK(PnvXScomInterface, (obj), TYPE_PNV_XSCOM_INTERFACE) + INTERFACE_CHECK(PnvXScomInterface, (obj), TYPE_PNV_XSCOM_INTERFACE) #define PNV_XSCOM_INTERFACE_CLASS(klass) \ OBJECT_CLASS_CHECK(PnvXScomInterfaceClass, (klass), \ TYPE_PNV_XSCOM_INTERFACE) @@ -70,6 +68,21 @@ typedef struct PnvXScomInterfaceClass { #define PNV_XSCOM_OCC_BASE 0x0066000 #define PNV_XSCOM_OCC_SIZE 0x6000 +#define PNV_XSCOM_PBA_BASE 0x2013f00 +#define PNV_XSCOM_PBA_SIZE 0x40 + +#define PNV_XSCOM_PBCQ_NEST_BASE 0x2012000 +#define PNV_XSCOM_PBCQ_NEST_SIZE 0x46 + +#define PNV_XSCOM_PBCQ_PCI_BASE 0x9012000 +#define PNV_XSCOM_PBCQ_PCI_SIZE 0x15 + +#define PNV_XSCOM_PBCQ_SPCI_BASE 0x9013c00 +#define PNV_XSCOM_PBCQ_SPCI_SIZE 0x5 + +/* + * Layout of the XSCOM PCB addresses (POWER 9) + */ #define PNV9_XSCOM_EC_BASE(core) \ ((uint64_t)(((core) & 0x1F) + 0x20) << 24) #define PNV9_XSCOM_EC_SIZE 0x100000 @@ -81,22 +94,57 @@ typedef struct PnvXScomInterfaceClass { #define PNV9_XSCOM_OCC_BASE PNV_XSCOM_OCC_BASE #define PNV9_XSCOM_OCC_SIZE 0x8000 +#define PNV9_XSCOM_PBA_BASE 0x5012b00 +#define PNV9_XSCOM_PBA_SIZE 0x40 + #define PNV9_XSCOM_PSIHB_BASE 0x5012900 #define PNV9_XSCOM_PSIHB_SIZE 0x100 #define PNV9_XSCOM_XIVE_BASE 0x5013000 #define PNV9_XSCOM_XIVE_SIZE 0x300 -extern void pnv_xscom_realize(PnvChip *chip, uint64_t size, Error **errp); -extern int pnv_dt_xscom(PnvChip *chip, void *fdt, int offset); +#define PNV9_XSCOM_PEC_NEST_BASE 0x4010c00 +#define PNV9_XSCOM_PEC_NEST_SIZE 0x100 -extern void pnv_xscom_add_subregion(PnvChip *chip, hwaddr offset, - MemoryRegion *mr); -extern void pnv_xscom_region_init(MemoryRegion *mr, - struct Object *owner, - const MemoryRegionOps *ops, - void *opaque, - const char *name, - uint64_t size); +#define PNV9_XSCOM_PEC_PCI_BASE 0xd010800 +#define PNV9_XSCOM_PEC_PCI_SIZE 0x200 + +/* XSCOM PCI "pass-through" window to PHB SCOM */ +#define PNV9_XSCOM_PEC_PCI_STK0 0x100 +#define PNV9_XSCOM_PEC_PCI_STK1 0x140 +#define PNV9_XSCOM_PEC_PCI_STK2 0x180 + +/* + * Layout of the XSCOM PCB addresses (POWER 10) + */ +#define PNV10_XSCOM_EQ_CHIPLET(core) (0x20 + ((core) >> 2)) +#define PNV10_XSCOM_EQ(chiplet) ((chiplet) << 24) +#define PNV10_XSCOM_EC(proc) \ + ((0x2 << 16) | ((1 << (3 - (proc))) << 12)) + +#define PNV10_XSCOM_EQ_BASE(core) \ + ((uint64_t) PNV10_XSCOM_EQ(PNV10_XSCOM_EQ_CHIPLET(core))) +#define PNV10_XSCOM_EQ_SIZE 0x100000 + +#define PNV10_XSCOM_EC_BASE(core) \ + ((uint64_t) PNV10_XSCOM_EQ_BASE(core) | PNV10_XSCOM_EC(core & 0x3)) +#define PNV10_XSCOM_EC_SIZE 0x100000 + +#define PNV10_XSCOM_PSIHB_BASE 0x3011D00 +#define PNV10_XSCOM_PSIHB_SIZE 0x100 + +void pnv_xscom_realize(PnvChip *chip, uint64_t size, Error **errp); +int pnv_dt_xscom(PnvChip *chip, void *fdt, int root_offset, + uint64_t xscom_base, uint64_t xscom_size, + const char *compat, int compat_size); + +void pnv_xscom_add_subregion(PnvChip *chip, hwaddr offset, + MemoryRegion *mr); +void pnv_xscom_region_init(MemoryRegion *mr, + struct Object *owner, + const MemoryRegionOps *ops, + void *opaque, + const char *name, + uint64_t size); #endif /* PPC_PNV_XSCOM_H */ diff --git a/include/hw/ppc/ppc.h b/include/hw/ppc/ppc.h index 4bdcb8bacd..93e614cffd 100644 --- a/include/hw/ppc/ppc.h +++ b/include/hw/ppc/ppc.h @@ -5,6 +5,7 @@ void ppc_set_irq(PowerPCCPU *cpu, int n_IRQ, int level); PowerPCCPU *ppc_get_vcpu_by_pir(int pir); +int ppc_cpu_pir(PowerPCCPU *cpu); /* PowerPC hardware exceptions management helpers */ typedef void (*clk_setup_cb)(void *opaque, uint32_t freq); @@ -23,6 +24,7 @@ struct ppc_tb_t { /* Time base management */ int64_t tb_offset; /* Compensation */ int64_t atb_offset; /* Compensation */ + int64_t vtb_offset; uint32_t tb_freq; /* TB frequency */ /* Decrementer management */ uint64_t decr_next; /* Tick for next decr interrupt */ @@ -31,8 +33,7 @@ struct ppc_tb_t { /* Hypervisor decrementer management */ uint64_t hdecr_next; /* Tick for next hdecr interrupt */ QEMUTimer *hdecr_timer; - uint64_t purr_load; - uint64_t purr_start; + int64_t purr_offset; void *opaque; uint32_t flags; }; @@ -67,7 +68,6 @@ clk_setup_cb ppc_40x_timers_init (CPUPPCState *env, uint32_t freq, void ppc40x_core_reset(PowerPCCPU *cpu); void ppc40x_chip_reset(PowerPCCPU *cpu); void ppc40x_system_reset(PowerPCCPU *cpu); -void PPC_debug_write (void *opaque, uint32_t addr, uint32_t val); #if defined(CONFIG_USER_ONLY) static inline void ppc40x_irq_init(PowerPCCPU *cpu) {} @@ -76,6 +76,7 @@ static inline void ppc970_irq_init(PowerPCCPU *cpu) {} static inline void ppcPOWER7_irq_init(PowerPCCPU *cpu) {} static inline void ppcPOWER9_irq_init(PowerPCCPU *cpu) {} static inline void ppce500_irq_init(PowerPCCPU *cpu) {} +static inline void ppc_irq_reset(PowerPCCPU *cpu) {} #else void ppc40x_irq_init(PowerPCCPU *cpu); void ppce500_irq_init(PowerPCCPU *cpu); @@ -83,6 +84,7 @@ void ppc6xx_irq_init(PowerPCCPU *cpu); void ppc970_irq_init(PowerPCCPU *cpu); void ppcPOWER7_irq_init(PowerPCCPU *cpu); void ppcPOWER9_irq_init(PowerPCCPU *cpu); +void ppc_irq_reset(PowerPCCPU *cpu); #endif /* PPC machines for OpenBIOS */ diff --git a/include/hw/ppc/ppc4xx.h b/include/hw/ppc/ppc4xx.h index 7d82259051..cc19c8da5b 100644 --- a/include/hw/ppc/ppc4xx.h +++ b/include/hw/ppc/ppc4xx.h @@ -42,11 +42,10 @@ enum { qemu_irq *ppcuic_init (CPUPPCState *env, qemu_irq *irqs, uint32_t dcr_base, int has_ssr, int has_vr); -ram_addr_t ppc4xx_sdram_adjust(ram_addr_t ram_size, int nr_banks, - MemoryRegion ram_memories[], - hwaddr ram_bases[], - hwaddr ram_sizes[], - const ram_addr_t sdram_bank_sizes[]); +void ppc4xx_sdram_banks(MemoryRegion *ram, int nr_banks, + MemoryRegion ram_memories[], + hwaddr ram_bases[], hwaddr ram_sizes[], + const ram_addr_t sdram_bank_sizes[]); void ppc4xx_sdram_init (CPUPPCState *env, qemu_irq irq, int nbanks, MemoryRegion ram_memories[], diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h index d5ab5ea7b2..42d64a0368 100644 --- a/include/hw/ppc/spapr.h +++ b/include/hw/ppc/spapr.h @@ -79,8 +79,10 @@ typedef enum { #define SPAPR_CAP_LARGE_DECREMENTER 0x08 /* Count Cache Flush Assist HW Instruction */ #define SPAPR_CAP_CCF_ASSIST 0x09 +/* Implements PAPR FWNMI option */ +#define SPAPR_CAP_FWNMI 0x0A /* Num Caps */ -#define SPAPR_CAP_NUM (SPAPR_CAP_CCF_ASSIST + 1) +#define SPAPR_CAP_NUM (SPAPR_CAP_FWNMI + 1) /* * Capability Values @@ -124,6 +126,7 @@ struct SpaprMachineClass { bool pre_4_1_migration; /* don't migrate hpt-max-page-size */ bool linux_pci_probe; bool smp_threads_vsmt; /* set VSMT to smp_threads by default */ + hwaddr rma_limit; /* clamp the RMA to this size */ void (*phb_placement)(SpaprMachineState *spapr, uint32_t index, uint64_t *buid, hwaddr *pio, @@ -154,12 +157,12 @@ struct SpaprMachineState { SpaprPendingHpt *pending_hpt; /* in-progress resize */ hwaddr rma_size; - int vrma_adjust; uint32_t fdt_size; uint32_t fdt_initial_size; void *fdt_blob; long kernel_size; bool kernel_le; + uint64_t kernel_addr; uint32_t initrd_base; long initrd_size; uint64_t rtc_offset; /* Now used only during incoming migration */ @@ -189,6 +192,23 @@ struct SpaprMachineState { * occurs during the unplug process. */ QTAILQ_HEAD(, SpaprDimmState) pending_dimm_unplugs; + /* State related to FWNMI option */ + + /* System Reset and Machine Check Notification Routine addresses + * registered by "ibm,nmi-register" RTAS call. + */ + target_ulong fwnmi_system_reset_addr; + target_ulong fwnmi_machine_check_addr; + + /* Machine Check FWNMI synchronization, fwnmi_machine_check_interlock is + * set to -1 if a FWNMI machine check is not in progress, else is set to + * the CPU that was delivered the machine check, and is set back to -1 + * when that CPU makes an "ibm,nmi-interlock" RTAS call. The cond is used + * to synchronize other CPUs. + */ + int fwnmi_machine_check_interlock; + QemuCond fwnmi_machine_check_interlock_cond; + /*< public >*/ char *kvm_type; char *host_model; @@ -207,6 +227,8 @@ struct SpaprMachineState { unsigned gpu_numa_id; SpaprTpmProxy *tpm_proxy; + + Error *fwnmi_migration_blocker; }; #define H_SUCCESS 0 @@ -287,6 +309,7 @@ struct SpaprMachineState { #define H_P7 -60 #define H_P8 -61 #define H_P9 -62 +#define H_OVERLAP -68 #define H_UNSUPPORTED_FLAG -256 #define H_MULTI_THREADS_ACTIVE -9005 @@ -494,8 +517,13 @@ struct SpaprMachineState { #define H_INT_ESB 0x3C8 #define H_INT_SYNC 0x3CC #define H_INT_RESET 0x3D0 +#define H_SCM_READ_METADATA 0x3E4 +#define H_SCM_WRITE_METADATA 0x3E8 +#define H_SCM_BIND_MEM 0x3EC +#define H_SCM_UNBIND_MEM 0x3F0 +#define H_SCM_UNBIND_ALL 0x3FC -#define MAX_HCALL_OPCODE H_INT_RESET +#define MAX_HCALL_OPCODE H_SCM_UNBIND_ALL /* The hcalls above are standardized in PAPR and implemented by pHyp * as well. @@ -645,8 +673,10 @@ target_ulong spapr_hypercall(PowerPCCPU *cpu, target_ulong opcode, #define RTAS_IBM_REMOVE_PE_DMA_WINDOW (RTAS_TOKEN_BASE + 0x28) #define RTAS_IBM_RESET_PE_DMA_WINDOW (RTAS_TOKEN_BASE + 0x29) #define RTAS_IBM_SUSPEND_ME (RTAS_TOKEN_BASE + 0x2A) +#define RTAS_IBM_NMI_REGISTER (RTAS_TOKEN_BASE + 0x2B) +#define RTAS_IBM_NMI_INTERLOCK (RTAS_TOKEN_BASE + 0x2C) -#define RTAS_TOKEN_MAX (RTAS_TOKEN_BASE + 0x2B) +#define RTAS_TOKEN_MAX (RTAS_TOKEN_BASE + 0x2D) /* RTAS ibm,get-system-parameter token values */ #define RTAS_SYSPARM_SPLPAR_CHARACTERISTICS 20 @@ -714,8 +744,12 @@ void spapr_load_rtas(SpaprMachineState *spapr, void *fdt, hwaddr addr); #define SPAPR_IS_PCI_LIOBN(liobn) (!!((liobn) & 0x80000000)) #define SPAPR_PCI_DMA_WINDOW_NUM(liobn) ((liobn) & 0xff) +#define RTAS_SIZE 2048 #define RTAS_ERROR_LOG_MAX 2048 +/* Offset from rtas-base where error log is placed */ +#define RTAS_ERROR_LOG_OFFSET 0x30 + #define RTAS_EVENT_SCAN_RATE 1 /* This helper should be used to encode interrupt specifiers when the related @@ -766,13 +800,11 @@ struct SpaprEventLogEntry { QTAILQ_ENTRY(SpaprEventLogEntry) next; }; +void *spapr_build_fdt(SpaprMachineState *spapr, bool reset, size_t space); void spapr_events_init(SpaprMachineState *sm); void spapr_dt_events(SpaprMachineState *sm, void *fdt); -int spapr_h_cas_compose_response(SpaprMachineState *sm, - target_ulong addr, target_ulong size, - SpaprOptionVector *ov5_updates); void close_htab_fd(SpaprMachineState *spapr); -void spapr_setup_hpt_and_vrma(SpaprMachineState *spapr); +void spapr_setup_hpt(SpaprMachineState *spapr); void spapr_free_hpt(SpaprMachineState *spapr); SpaprTceTable *spapr_tce_new_table(DeviceState *owner, uint32_t liobn); void spapr_tce_table_enable(SpaprTceTable *tcet, @@ -801,9 +833,11 @@ int spapr_hpt_shift_for_ramsize(uint64_t ramsize); void spapr_reallocate_hpt(SpaprMachineState *spapr, int shift, Error **errp); void spapr_clear_pending_events(SpaprMachineState *spapr); +void spapr_clear_pending_hotplug_events(SpaprMachineState *spapr); int spapr_max_server_number(SpaprMachineState *spapr); void spapr_store_hpte(PowerPCCPU *cpu, hwaddr ptex, uint64_t pte0, uint64_t pte1); +void spapr_mce_req_event(PowerPCCPU *cpu, bool recovered); /* DRC callbacks. */ void spapr_core_release(DeviceState *dev); @@ -871,6 +905,7 @@ extern const VMStateDescription vmstate_spapr_cap_hpt_maxpagesize; extern const VMStateDescription vmstate_spapr_cap_nested_kvm_hv; extern const VMStateDescription vmstate_spapr_cap_large_decr; extern const VMStateDescription vmstate_spapr_cap_ccf_assist; +extern const VMStateDescription vmstate_spapr_cap_fwnmi; static inline uint8_t spapr_get_cap(SpaprMachineState *spapr, int cap) { @@ -893,4 +928,5 @@ void spapr_check_pagesize(SpaprMachineState *spapr, hwaddr pagesize, #define SPAPR_OV5_XIVE_BOTH 0x80 /* Only to advertise on the platform */ void spapr_set_all_lpcrs(target_ulong value, target_ulong mask); +hwaddr spapr_get_rtas_addr(void); #endif /* HW_SPAPR_H */ diff --git a/include/hw/ppc/spapr_cpu_core.h b/include/hw/ppc/spapr_cpu_core.h index 1c4cc6559c..7aed8f555b 100644 --- a/include/hw/ppc/spapr_cpu_core.h +++ b/include/hw/ppc/spapr_cpu_core.h @@ -40,7 +40,9 @@ typedef struct SpaprCpuCoreClass { } SpaprCpuCoreClass; const char *spapr_get_cpu_core_type(const char *cpu_type); -void spapr_cpu_set_entry_state(PowerPCCPU *cpu, target_ulong nip, target_ulong r3); +void spapr_cpu_set_entry_state(PowerPCCPU *cpu, target_ulong nip, + target_ulong r1, target_ulong r3, + target_ulong r4); typedef struct SpaprCpuState { uint64_t vpa_addr; diff --git a/include/hw/ppc/spapr_drc.h b/include/hw/ppc/spapr_drc.h index 83f03cc577..21af8deac1 100644 --- a/include/hw/ppc/spapr_drc.h +++ b/include/hw/ppc/spapr_drc.h @@ -78,6 +78,13 @@ #define SPAPR_DRC_PHB(obj) OBJECT_CHECK(SpaprDrc, (obj), \ TYPE_SPAPR_DRC_PHB) +#define TYPE_SPAPR_DRC_PMEM "spapr-drc-pmem" +#define SPAPR_DRC_PMEM_GET_CLASS(obj) \ + OBJECT_GET_CLASS(SpaprDrcClass, obj, TYPE_SPAPR_DRC_PMEM) +#define SPAPR_DRC_PMEM_CLASS(klass) \ + OBJECT_CLASS_CHECK(SpaprDrcClass, klass, TYPE_SPAPR_DRC_PMEM) +#define SPAPR_DRC_PMEM(obj) OBJECT_CHECK(SpaprDrc, (obj), \ + TYPE_SPAPR_DRC_PMEM) /* * Various hotplug types managed by SpaprDrc * @@ -95,6 +102,7 @@ typedef enum { SPAPR_DR_CONNECTOR_TYPE_SHIFT_VIO = 3, SPAPR_DR_CONNECTOR_TYPE_SHIFT_PCI = 4, SPAPR_DR_CONNECTOR_TYPE_SHIFT_LMB = 8, + SPAPR_DR_CONNECTOR_TYPE_SHIFT_PMEM = 9, } SpaprDrcTypeShift; typedef enum { @@ -104,6 +112,7 @@ typedef enum { SPAPR_DR_CONNECTOR_TYPE_VIO = 1 << SPAPR_DR_CONNECTOR_TYPE_SHIFT_VIO, SPAPR_DR_CONNECTOR_TYPE_PCI = 1 << SPAPR_DR_CONNECTOR_TYPE_SHIFT_PCI, SPAPR_DR_CONNECTOR_TYPE_LMB = 1 << SPAPR_DR_CONNECTOR_TYPE_SHIFT_LMB, + SPAPR_DR_CONNECTOR_TYPE_PMEM = 1 << SPAPR_DR_CONNECTOR_TYPE_SHIFT_PMEM, } SpaprDrcType; /* @@ -269,7 +278,9 @@ int spapr_dt_drc(void *fdt, int offset, Object *owner, uint32_t drc_type_mask); void spapr_drc_attach(SpaprDrc *drc, DeviceState *d, Error **errp); void spapr_drc_detach(SpaprDrc *drc); -bool spapr_drc_needed(void *opaque); + +/* Returns true if a hot plug/unplug request is pending */ +bool spapr_drc_transient(SpaprDrc *drc); static inline bool spapr_drc_unplug_requested(SpaprDrc *drc) { diff --git a/include/hw/ppc/spapr_irq.h b/include/hw/ppc/spapr_irq.h index ff814d13de..ca8cb44213 100644 --- a/include/hw/ppc/spapr_irq.h +++ b/include/hw/ppc/spapr_irq.h @@ -43,7 +43,8 @@ typedef struct SpaprInterruptController SpaprInterruptController; typedef struct SpaprInterruptControllerClass { InterfaceClass parent; - int (*activate)(SpaprInterruptController *intc, Error **errp); + int (*activate)(SpaprInterruptController *intc, uint32_t nr_servers, + Error **errp); void (*deactivate)(SpaprInterruptController *intc); /* @@ -98,8 +99,13 @@ qemu_irq spapr_qirq(SpaprMachineState *spapr, int irq); int spapr_irq_post_load(SpaprMachineState *spapr, int version_id); void spapr_irq_reset(SpaprMachineState *spapr, Error **errp); int spapr_irq_get_phandle(SpaprMachineState *spapr, void *fdt, Error **errp); -int spapr_irq_init_kvm(int (*fn)(SpaprInterruptController *, Error **), + +typedef int (*SpaprInterruptControllerInitKvm)(SpaprInterruptController *, + uint32_t, Error **); + +int spapr_irq_init_kvm(SpaprInterruptControllerInitKvm fn, SpaprInterruptController *intc, + uint32_t nr_servers, Error **errp); /* diff --git a/include/hw/ppc/spapr_nvdimm.h b/include/hw/ppc/spapr_nvdimm.h new file mode 100644 index 0000000000..b3330cc485 --- /dev/null +++ b/include/hw/ppc/spapr_nvdimm.h @@ -0,0 +1,37 @@ +/* + * QEMU PowerPC PAPR SCM backend definitions + * + * Copyright (c) 2020, IBM Corporation. + * + * This code is licensed under the GPL version 2 or later. See the + * COPYING file in the top-level directory. + */ + +#ifndef HW_SPAPR_NVDIMM_H +#define HW_SPAPR_NVDIMM_H + +#include "hw/mem/nvdimm.h" +#include "hw/ppc/spapr.h" + +/* + * The nvdimm size should be aligned to SCM block size. + * The SCM block size should be aligned to SPAPR_MEMORY_BLOCK_SIZE + * inorder to have SCM regions not to overlap with dimm memory regions. + * The SCM devices can have variable block sizes. For now, fixing the + * block size to the minimum value. + */ +#define SPAPR_MINIMUM_SCM_BLOCK_SIZE SPAPR_MEMORY_BLOCK_SIZE + +/* Have an explicit check for alignment */ +QEMU_BUILD_BUG_ON(SPAPR_MINIMUM_SCM_BLOCK_SIZE % SPAPR_MEMORY_BLOCK_SIZE); + +int spapr_pmem_dt_populate(SpaprDrc *drc, SpaprMachineState *spapr, + void *fdt, int *fdt_start_offset, Error **errp); +int spapr_dt_nvdimm(void *fdt, int parent_offset, NVDIMMDevice *nvdimm); +void spapr_dt_persistent_memory(void *fdt); +void spapr_nvdimm_validate_opts(NVDIMMDevice *nvdimm, uint64_t size, + Error **errp); +void spapr_add_nvdimm(DeviceState *dev, uint64_t slot, Error **errp); +void spapr_create_nvdimm_dr_connectors(SpaprMachineState *spapr); + +#endif diff --git a/include/hw/ppc/spapr_ovec.h b/include/hw/ppc/spapr_ovec.h index 7891e9caac..d4dee9e06a 100644 --- a/include/hw/ppc/spapr_ovec.h +++ b/include/hw/ppc/spapr_ovec.h @@ -66,16 +66,14 @@ SpaprOptionVector *spapr_ovec_clone(SpaprOptionVector *ov_orig); void spapr_ovec_intersect(SpaprOptionVector *ov, SpaprOptionVector *ov1, SpaprOptionVector *ov2); -bool spapr_ovec_diff(SpaprOptionVector *ov, - SpaprOptionVector *ov_old, - SpaprOptionVector *ov_new); +bool spapr_ovec_subset(SpaprOptionVector *ov1, SpaprOptionVector *ov2); void spapr_ovec_cleanup(SpaprOptionVector *ov); void spapr_ovec_set(SpaprOptionVector *ov, long bitnr); void spapr_ovec_clear(SpaprOptionVector *ov, long bitnr); bool spapr_ovec_test(SpaprOptionVector *ov, long bitnr); SpaprOptionVector *spapr_ovec_parse_vector(target_ulong table_addr, int vector); -int spapr_ovec_populate_dt(void *fdt, int fdt_offset, - SpaprOptionVector *ov, const char *name); +int spapr_dt_ovec(void *fdt, int fdt_offset, + SpaprOptionVector *ov, const char *name); /* migration */ extern const VMStateDescription vmstate_spapr_ovec; diff --git a/include/hw/ppc/spapr_vio.h b/include/hw/ppc/spapr_vio.h index 72762ed16b..bed7df60e3 100644 --- a/include/hw/ppc/spapr_vio.h +++ b/include/hw/ppc/spapr_vio.h @@ -58,6 +58,7 @@ typedef struct SpaprVioDeviceClass { void (*realize)(SpaprVioDevice *dev, Error **errp); void (*reset)(SpaprVioDevice *dev); int (*devnode)(SpaprVioDevice *dev, void *fdt, int node_off); + const char *(*get_dt_compatible)(SpaprVioDevice *dev); } SpaprVioDeviceClass; struct SpaprVioDevice { @@ -80,10 +81,10 @@ struct SpaprVioBus { uint32_t next_reg; }; -extern SpaprVioBus *spapr_vio_bus_init(void); -extern SpaprVioDevice *spapr_vio_find_by_reg(SpaprVioBus *bus, uint32_t reg); +SpaprVioBus *spapr_vio_bus_init(void); +SpaprVioDevice *spapr_vio_find_by_reg(SpaprVioBus *bus, uint32_t reg); void spapr_dt_vdevice(SpaprVioBus *bus, void *fdt); -extern gchar *spapr_vio_stdout_path(SpaprVioBus *bus); +gchar *spapr_vio_stdout_path(SpaprVioBus *bus); static inline void spapr_vio_irq_pulse(SpaprVioDevice *dev) { diff --git a/include/hw/ppc/spapr_xive.h b/include/hw/ppc/spapr_xive.h index 742b7e834f..93d09d68de 100644 --- a/include/hw/ppc/spapr_xive.h +++ b/include/hw/ppc/spapr_xive.h @@ -15,6 +15,10 @@ #define TYPE_SPAPR_XIVE "spapr-xive" #define SPAPR_XIVE(obj) OBJECT_CHECK(SpaprXive, (obj), TYPE_SPAPR_XIVE) +#define SPAPR_XIVE_CLASS(klass) \ + OBJECT_CLASS_CHECK(SpaprXiveClass, (klass), TYPE_SPAPR_XIVE) +#define SPAPR_XIVE_GET_CLASS(obj) \ + OBJECT_GET_CLASS(SpaprXiveClass, (obj), TYPE_SPAPR_XIVE) typedef struct SpaprXive { XiveRouter parent; @@ -47,6 +51,12 @@ typedef struct SpaprXive { VMChangeStateEntry *change; } SpaprXive; +typedef struct SpaprXiveClass { + XiveRouterClass parent; + + DeviceRealize parent_realize; +} SpaprXiveClass; + /* * The sPAPR machine has a unique XIVE IC device. Assign a fixed value * to the controller block id value. It can nevertheless be changed @@ -66,7 +76,8 @@ int spapr_xive_end_to_target(uint8_t end_blk, uint32_t end_idx, /* * KVM XIVE device helpers */ -int kvmppc_xive_connect(SpaprInterruptController *intc, Error **errp); +int kvmppc_xive_connect(SpaprInterruptController *intc, uint32_t nr_servers, + Error **errp); void kvmppc_xive_disconnect(SpaprInterruptController *intc); void kvmppc_xive_reset(SpaprXive *xive, Error **errp); void kvmppc_xive_set_source_config(SpaprXive *xive, uint32_t lisn, XiveEAS *eas, diff --git a/include/hw/ppc/xics.h b/include/hw/ppc/xics.h index 48a75aa4ab..9ed58ec7e9 100644 --- a/include/hw/ppc/xics.h +++ b/include/hw/ppc/xics.h @@ -101,6 +101,10 @@ struct ICSStateClass { DeviceClass parent_class; DeviceRealize parent_realize; + DeviceReset parent_reset; + + void (*reject)(ICSState *s, uint32_t irq); + void (*resend)(ICSState *s); }; struct ICSState { @@ -161,6 +165,7 @@ void icp_set_mfrr(ICPState *icp, uint8_t mfrr); uint32_t icp_accept(ICPState *ss); uint32_t icp_ipoll(ICPState *ss, uint32_t *mfrr); void icp_eoi(ICPState *icp, uint32_t xirr); +void icp_irq(ICSState *ics, int server, int nr, uint8_t priority); void icp_reset(ICPState *icp); void ics_write_xive(ICSState *ics, int nr, int server, diff --git a/include/hw/ppc/xics_spapr.h b/include/hw/ppc/xics_spapr.h index 28b87038c8..1c65c96e3c 100644 --- a/include/hw/ppc/xics_spapr.h +++ b/include/hw/ppc/xics_spapr.h @@ -32,7 +32,8 @@ #define TYPE_ICS_SPAPR "ics-spapr" #define ICS_SPAPR(obj) OBJECT_CHECK(ICSState, (obj), TYPE_ICS_SPAPR) -int xics_kvm_connect(SpaprInterruptController *intc, Error **errp); +int xics_kvm_connect(SpaprInterruptController *intc, uint32_t nr_servers, + Error **errp); void xics_kvm_disconnect(SpaprInterruptController *intc); bool xics_kvm_has_broken_disconnect(SpaprMachineState *spapr); diff --git a/include/hw/ppc/xive.h b/include/hw/ppc/xive.h index 8fd439ec9b..705cf48176 100644 --- a/include/hw/ppc/xive.h +++ b/include/hw/ppc/xive.h @@ -311,6 +311,8 @@ void xive_source_set_irq(void *opaque, int srcno, int val); #define XIVE_TM_RING_COUNT 4 #define XIVE_TM_RING_SIZE 0x10 +typedef struct XivePresenter XivePresenter; + typedef struct XiveTCTX { DeviceState parent_obj; @@ -319,14 +321,19 @@ typedef struct XiveTCTX { qemu_irq os_output; uint8_t regs[XIVE_TM_RING_COUNT * XIVE_TM_RING_SIZE]; + + XivePresenter *xptr; } XiveTCTX; /* * XIVE Router */ +typedef struct XiveFabric XiveFabric; typedef struct XiveRouter { SysBusDevice parent; + + XiveFabric *xfb; } XiveRouter; #define TYPE_XIVE_ROUTER "xive-router" @@ -351,7 +358,7 @@ typedef struct XiveRouterClass { XiveNVT *nvt); int (*write_nvt)(XiveRouter *xrtr, uint8_t nvt_blk, uint32_t nvt_idx, XiveNVT *nvt, uint8_t word_number); - XiveTCTX *(*get_tctx)(XiveRouter *xrtr, CPUState *cs); + uint8_t (*get_block_id)(XiveRouter *xrtr); } XiveRouterClass; int xive_router_get_eas(XiveRouter *xrtr, uint8_t eas_blk, uint32_t eas_idx, @@ -364,9 +371,58 @@ int xive_router_get_nvt(XiveRouter *xrtr, uint8_t nvt_blk, uint32_t nvt_idx, XiveNVT *nvt); int xive_router_write_nvt(XiveRouter *xrtr, uint8_t nvt_blk, uint32_t nvt_idx, XiveNVT *nvt, uint8_t word_number); -XiveTCTX *xive_router_get_tctx(XiveRouter *xrtr, CPUState *cs); void xive_router_notify(XiveNotifier *xn, uint32_t lisn); +/* + * XIVE Presenter + */ + +typedef struct XiveTCTXMatch { + XiveTCTX *tctx; + uint8_t ring; +} XiveTCTXMatch; + +#define TYPE_XIVE_PRESENTER "xive-presenter" +#define XIVE_PRESENTER(obj) \ + INTERFACE_CHECK(XivePresenter, (obj), TYPE_XIVE_PRESENTER) +#define XIVE_PRESENTER_CLASS(klass) \ + OBJECT_CLASS_CHECK(XivePresenterClass, (klass), TYPE_XIVE_PRESENTER) +#define XIVE_PRESENTER_GET_CLASS(obj) \ + OBJECT_GET_CLASS(XivePresenterClass, (obj), TYPE_XIVE_PRESENTER) + +typedef struct XivePresenterClass { + InterfaceClass parent; + int (*match_nvt)(XivePresenter *xptr, uint8_t format, + uint8_t nvt_blk, uint32_t nvt_idx, + bool cam_ignore, uint8_t priority, + uint32_t logic_serv, XiveTCTXMatch *match); +} XivePresenterClass; + +int xive_presenter_tctx_match(XivePresenter *xptr, XiveTCTX *tctx, + uint8_t format, + uint8_t nvt_blk, uint32_t nvt_idx, + bool cam_ignore, uint32_t logic_serv); + +/* + * XIVE Fabric (Interface between Interrupt Controller and Machine) + */ + +#define TYPE_XIVE_FABRIC "xive-fabric" +#define XIVE_FABRIC(obj) \ + INTERFACE_CHECK(XiveFabric, (obj), TYPE_XIVE_FABRIC) +#define XIVE_FABRIC_CLASS(klass) \ + OBJECT_CLASS_CHECK(XiveFabricClass, (klass), TYPE_XIVE_FABRIC) +#define XIVE_FABRIC_GET_CLASS(obj) \ + OBJECT_GET_CLASS(XiveFabricClass, (obj), TYPE_XIVE_FABRIC) + +typedef struct XiveFabricClass { + InterfaceClass parent; + int (*match_nvt)(XiveFabric *xfb, uint8_t format, + uint8_t nvt_blk, uint32_t nvt_idx, + bool cam_ignore, uint8_t priority, + uint32_t logic_serv, XiveTCTXMatch *match); +} XiveFabricClass; + /* * XIVE END ESBs */ @@ -379,7 +435,6 @@ typedef struct XiveENDSource { DeviceState parent; uint32_t nr_ends; - uint8_t block_id; /* ESB memory region */ uint32_t esb_shift; @@ -408,20 +463,16 @@ typedef struct XiveENDSource { #define XIVE_TM_OS_PAGE 0x2 #define XIVE_TM_USER_PAGE 0x3 -extern const MemoryRegionOps xive_tm_ops; -void xive_tctx_tm_write(XiveTCTX *tctx, hwaddr offset, uint64_t value, - unsigned size); -uint64_t xive_tctx_tm_read(XiveTCTX *tctx, hwaddr offset, unsigned size); +void xive_tctx_tm_write(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, + uint64_t value, unsigned size); +uint64_t xive_tctx_tm_read(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, + unsigned size); void xive_tctx_pic_print_info(XiveTCTX *tctx, Monitor *mon); -Object *xive_tctx_create(Object *cpu, XiveRouter *xrtr, Error **errp); +Object *xive_tctx_create(Object *cpu, XivePresenter *xptr, Error **errp); void xive_tctx_reset(XiveTCTX *tctx); void xive_tctx_destroy(XiveTCTX *tctx); - -static inline uint32_t xive_nvt_cam_line(uint8_t nvt_blk, uint32_t nvt_idx) -{ - return (nvt_blk << 19) | nvt_idx; -} +void xive_tctx_ipb_update(XiveTCTX *tctx, uint8_t ring, uint8_t ipb); /* * KVM XIVE device helpers diff --git a/include/hw/ppc/xive_regs.h b/include/hw/ppc/xive_regs.h index 55307cd153..09f243600c 100644 --- a/include/hw/ppc/xive_regs.h +++ b/include/hw/ppc/xive_regs.h @@ -252,9 +252,12 @@ typedef struct XiveNVT { uint32_t w0; #define NVT_W0_VALID PPC_BIT32(0) uint32_t w1; +#define NVT_W1_EQ_BLOCK PPC_BITMASK32(0, 3) +#define NVT_W1_EQ_INDEX PPC_BITMASK32(4, 31) uint32_t w2; uint32_t w3; uint32_t w4; +#define NVT_W4_IPB PPC_BITMASK32(16, 23) uint32_t w5; uint32_t w6; uint32_t w7; @@ -271,4 +274,26 @@ typedef struct XiveNVT { #define xive_nvt_is_valid(nvt) (be32_to_cpu((nvt)->w0) & NVT_W0_VALID) +/* + * The VP number space in a block is defined by the END_W6_NVT_INDEX + * field of the XIVE END + */ +#define XIVE_NVT_SHIFT 19 +#define XIVE_NVT_COUNT (1 << XIVE_NVT_SHIFT) + +static inline uint32_t xive_nvt_cam_line(uint8_t nvt_blk, uint32_t nvt_idx) +{ + return (nvt_blk << XIVE_NVT_SHIFT) | nvt_idx; +} + +static inline uint32_t xive_nvt_idx(uint32_t cam_line) +{ + return cam_line & ((1 << XIVE_NVT_SHIFT) - 1); +} + +static inline uint32_t xive_nvt_blk(uint32_t cam_line) +{ + return (cam_line >> XIVE_NVT_SHIFT) & 0xf; +} + #endif /* PPC_XIVE_REGS_H */ diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h index 1518495b1e..1405b8a990 100644 --- a/include/hw/qdev-core.h +++ b/include/hw/qdev-core.h @@ -5,6 +5,7 @@ #include "qemu/bitmap.h" #include "qom/object.h" #include "hw/hotplug.h" +#include "hw/resettable.h" enum { DEV_NVECTORS_UNSPECIFIED = -1, @@ -100,7 +101,12 @@ typedef struct DeviceClass { DECLARE_BITMAP(categories, DEVICE_CATEGORY_MAX); const char *fw_name; const char *desc; - Property *props; + + /* + * The underscore at the end ensures a compile-time error if someone + * assigns to dc->props instead of using device_class_set_props. + */ + Property *props_; /* * Can this device be instantiated with -device / device_add? @@ -117,6 +123,11 @@ typedef struct DeviceClass { bool hotpluggable; /* callbacks */ + /* + * Reset method here is deprecated and replaced by methods in the + * resettable class interface to implement a multi-phase reset. + * TODO: remove once every reset callback is unused + */ DeviceReset reset; DeviceRealize realize; DeviceUnrealize unrealize; @@ -141,6 +152,7 @@ struct NamedGPIOList { /** * DeviceState: * @realized: Indicates whether the device has been fully constructed. + * @reset: ResettableState for the device; handled by Resettable interface. * * This structure should not be accessed directly. We declare it here * so that it can be embedded in individual device state structures. @@ -163,6 +175,7 @@ struct DeviceState { int num_child_bus; int instance_id_alias; int alias_required_for_version; + ResettableState reset; }; struct DeviceListener { @@ -215,6 +228,7 @@ typedef struct BusChild { /** * BusState: * @hotplug_handler: link to a hotplug handler associated with bus. + * @reset: ResettableState for the bus; handled by Resettable interface. */ struct BusState { Object obj; @@ -226,6 +240,7 @@ struct BusState { int num_children; QTAILQ_HEAD(, BusChild) children; QLIST_ENTRY(BusState) sibling; + ResettableState reset; }; /** @@ -258,8 +273,8 @@ struct PropertyInfo { const char *description; const QEnumLookup *enum_table; int (*print)(DeviceState *dev, Property *prop, char *dest, size_t len); - void (*set_default_value)(Object *obj, const Property *prop); - void (*create)(Object *obj, Property *prop, Error **errp); + void (*set_default_value)(ObjectProperty *op, const Property *prop); + void (*create)(ObjectClass *oc, Property *prop, Error **errp); ObjectPropertyAccessor *get; ObjectPropertyAccessor *set; ObjectPropertyRelease *release; @@ -396,6 +411,13 @@ int qdev_walk_children(DeviceState *dev, qdev_walkerfn *post_devfn, qbus_walkerfn *post_busfn, void *opaque); +/** + * @qdev_reset_all: + * Reset @dev. See @qbus_reset_all() for more details. + * + * Note: This function is deprecated and will be removed when it becomes unused. + * Please use device_cold_reset() now. + */ void qdev_reset_all(DeviceState *dev); void qdev_reset_all_fn(void *opaque); @@ -408,10 +430,40 @@ void qdev_reset_all_fn(void *opaque); * hard reset means that qbus_reset_all will reset all state of the device. * For PCI devices, for example, this will include the base address registers * or configuration space. + * + * Note: This function is deprecated and will be removed when it becomes unused. + * Please use bus_cold_reset() now. */ void qbus_reset_all(BusState *bus); void qbus_reset_all_fn(void *opaque); +/** + * device_cold_reset: + * Reset device @dev and perform a recursive processing using the resettable + * interface. It triggers a RESET_TYPE_COLD. + */ +void device_cold_reset(DeviceState *dev); + +/** + * bus_cold_reset: + * + * Reset bus @bus and perform a recursive processing using the resettable + * interface. It triggers a RESET_TYPE_COLD. + */ +void bus_cold_reset(BusState *bus); + +/** + * device_is_in_reset: + * Return true if the device @dev is currently being reset. + */ +bool device_is_in_reset(DeviceState *dev); + +/** + * bus_is_in_reset: + * Return true if the bus @bus is currently being reset. + */ +bool bus_is_in_reset(BusState *bus); + /* This should go away once we get rid of the NULL bus hack */ BusState *sysbus_get_default(void); @@ -427,12 +479,21 @@ char *qdev_get_own_fw_dev_path_from_handler(BusState *bus, DeviceState *dev); void qdev_machine_init(void); /** - * @device_reset + * device_legacy_reset: * * Reset a single device (by calling the reset method). + * Note: This function is deprecated and will be removed when it becomes unused. + * Please use device_cold_reset() now. */ -void device_reset(DeviceState *dev); +void device_legacy_reset(DeviceState *dev); +void device_class_set_props(DeviceClass *dc, Property *props); + +/** + * device_class_set_parent_reset: + * TODO: remove the function when DeviceClass's reset method + * is not used anymore. + */ void device_class_set_parent_reset(DeviceClass *dc, DeviceReset dev_reset, DeviceReset *parent_reset); @@ -457,8 +518,6 @@ extern bool qdev_hot_removed; char *qdev_get_dev_path(DeviceState *dev); -GSList *qdev_build_hotpluggable_device_list(Object *peripheral); - void qbus_set_hotplug_handler(BusState *bus, Object *handler, Error **errp); void qbus_set_bus_hotplug_handler(BusState *bus, Error **errp); diff --git a/include/hw/qdev-properties.h b/include/hw/qdev-properties.h index c6a8cb5516..f161604fb6 100644 --- a/include/hw/qdev-properties.h +++ b/include/hw/qdev-properties.h @@ -18,9 +18,9 @@ extern const PropertyInfo qdev_prop_size; extern const PropertyInfo qdev_prop_string; extern const PropertyInfo qdev_prop_chr; extern const PropertyInfo qdev_prop_tpm; -extern const PropertyInfo qdev_prop_ptr; extern const PropertyInfo qdev_prop_macaddr; extern const PropertyInfo qdev_prop_on_off_auto; +extern const PropertyInfo qdev_prop_multifd_compression; extern const PropertyInfo qdev_prop_losttickpolicy; extern const PropertyInfo qdev_prop_blockdev_on_error; extern const PropertyInfo qdev_prop_bios_chs_trans; @@ -171,25 +171,6 @@ extern const PropertyInfo qdev_prop_pcie_link_width; #define DEFINE_PROP_PCI_DEVFN(_n, _s, _f, _d) \ DEFINE_PROP_SIGNED(_n, _s, _f, _d, qdev_prop_pci_devfn, int32_t) -/* - * Please avoid pointer properties. If you must use them, you must - * cover them in their device's class init function as follows: - * - * - If the property must be set, the device cannot be used with - * device_add, so add code like this: - * |* Reason: pointer property "NAME-OF-YOUR-PROP" *| - * DeviceClass *dc = DEVICE_CLASS(class); - * dc->user_creatable = false; - * - * - If the property may safely remain null, document it like this: - * |* - * * Note: pointer property "interrupt_vector" may remain null, thus - * * no need for dc->user_creatable = false; - * *| - */ -#define DEFINE_PROP_PTR(_n, _s, _f) \ - DEFINE_PROP(_n, _s, _f, qdev_prop_ptr, void*) - #define DEFINE_PROP_CHR(_n, _s, _f) \ DEFINE_PROP(_n, _s, _f, qdev_prop_chr, CharBackend) #define DEFINE_PROP_STRING(_n, _s, _f) \ @@ -204,6 +185,9 @@ extern const PropertyInfo qdev_prop_pcie_link_width; DEFINE_PROP(_n, _s, _f, qdev_prop_macaddr, MACAddr) #define DEFINE_PROP_ON_OFF_AUTO(_n, _s, _f, _d) \ DEFINE_PROP_SIGNED(_n, _s, _f, _d, qdev_prop_on_off_auto, OnOffAuto) +#define DEFINE_PROP_MULTIFD_COMPRESSION(_n, _s, _f, _d) \ + DEFINE_PROP_SIGNED(_n, _s, _f, _d, qdev_prop_multifd_compression, \ + MultiFDCompression) #define DEFINE_PROP_LOSTTICKPOLICY(_n, _s, _f, _d) \ DEFINE_PROP_SIGNED(_n, _s, _f, _d, qdev_prop_losttickpolicy, \ LostTickPolicy) @@ -216,8 +200,6 @@ extern const PropertyInfo qdev_prop_pcie_link_width; DEFINE_PROP_UNSIGNED(_n, _s, _f, 0, qdev_prop_blocksize, uint16_t) #define DEFINE_PROP_PCI_HOST_DEVADDR(_n, _s, _f) \ DEFINE_PROP(_n, _s, _f, qdev_prop_pci_host_devaddr, PCIHostDeviceAddress) -#define DEFINE_PROP_MEMORY_REGION(_n, _s, _f) \ - DEFINE_PROP(_n, _s, _f, qdev_prop_ptr, MemoryRegion *) #define DEFINE_PROP_OFF_AUTO_PCIBAR(_n, _s, _f, _d) \ DEFINE_PROP_SIGNED(_n, _s, _f, _d, qdev_prop_off_auto_pcibar, \ OffAutoPCIBAR) @@ -264,8 +246,6 @@ void qdev_prop_set_drive(DeviceState *dev, const char *name, void qdev_prop_set_macaddr(DeviceState *dev, const char *name, const uint8_t *value); void qdev_prop_set_enum(DeviceState *dev, const char *name, int value); -/* FIXME: Remove opaque pointer properties. */ -void qdev_prop_set_ptr(DeviceState *dev, const char *name, void *value); void qdev_prop_register_global(GlobalProperty *prop); int qdev_prop_check_globals(void); @@ -277,13 +257,12 @@ void error_set_from_qdev_prop_error(Error **errp, int ret, DeviceState *dev, * qdev_property_add_static: * @dev: Device to add the property to. * @prop: The qdev property definition. - * @errp: location to store error information. * * Add a static QOM property to @dev for qdev property @prop. * On error, store error in @errp. Static properties access data in a struct. * The type of the QOM property is derived from prop->info. */ -void qdev_property_add_static(DeviceState *dev, Property *prop, Error **errp); +void qdev_property_add_static(DeviceState *dev, Property *prop); void qdev_alias_all_properties(DeviceState *target, Object *source); diff --git a/include/hw/registerfields.h b/include/hw/registerfields.h index 2659a58737..0407edb7ec 100644 --- a/include/hw/registerfields.h +++ b/include/hw/registerfields.h @@ -22,6 +22,14 @@ enum { A_ ## reg = (addr) }; \ enum { R_ ## reg = (addr) / 4 }; +#define REG8(reg, addr) \ + enum { A_ ## reg = (addr) }; \ + enum { R_ ## reg = (addr) }; + +#define REG16(reg, addr) \ + enum { A_ ## reg = (addr) }; \ + enum { R_ ## reg = (addr) / 2 }; + /* Define SHIFT, LENGTH and MASK constants for a field within a register */ /* This macro will define R_FOO_BAR_MASK, R_FOO_BAR_SHIFT and R_FOO_BAR_LENGTH @@ -34,6 +42,12 @@ MAKE_64BIT_MASK(shift, length)}; /* Extract a field from a register */ +#define FIELD_EX8(storage, reg, field) \ + extract8((storage), R_ ## reg ## _ ## field ## _SHIFT, \ + R_ ## reg ## _ ## field ## _LENGTH) +#define FIELD_EX16(storage, reg, field) \ + extract16((storage), R_ ## reg ## _ ## field ## _SHIFT, \ + R_ ## reg ## _ ## field ## _LENGTH) #define FIELD_EX32(storage, reg, field) \ extract32((storage), R_ ## reg ## _ ## field ## _SHIFT, \ R_ ## reg ## _ ## field ## _LENGTH) @@ -49,6 +63,22 @@ * Assigning values larger then the target field will result in * compilation warnings. */ +#define FIELD_DP8(storage, reg, field, val) ({ \ + struct { \ + unsigned int v:R_ ## reg ## _ ## field ## _LENGTH; \ + } v = { .v = val }; \ + uint8_t d; \ + d = deposit32((storage), R_ ## reg ## _ ## field ## _SHIFT, \ + R_ ## reg ## _ ## field ## _LENGTH, v.v); \ + d; }) +#define FIELD_DP16(storage, reg, field, val) ({ \ + struct { \ + unsigned int v:R_ ## reg ## _ ## field ## _LENGTH; \ + } v = { .v = val }; \ + uint16_t d; \ + d = deposit32((storage), R_ ## reg ## _ ## field ## _SHIFT, \ + R_ ## reg ## _ ## field ## _LENGTH, v.v); \ + d; }) #define FIELD_DP32(storage, reg, field, val) ({ \ struct { \ unsigned int v:R_ ## reg ## _ ## field ## _LENGTH; \ diff --git a/include/hw/resettable.h b/include/hw/resettable.h new file mode 100644 index 0000000000..f4c4bab0ef --- /dev/null +++ b/include/hw/resettable.h @@ -0,0 +1,247 @@ +/* + * Resettable interface header. + * + * Copyright (c) 2019 GreenSocs SAS + * + * Authors: + * Damien Hedde + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef HW_RESETTABLE_H +#define HW_RESETTABLE_H + +#include "qom/object.h" + +#define TYPE_RESETTABLE_INTERFACE "resettable" + +#define RESETTABLE_CLASS(class) \ + OBJECT_CLASS_CHECK(ResettableClass, (class), TYPE_RESETTABLE_INTERFACE) + +#define RESETTABLE_GET_CLASS(obj) \ + OBJECT_GET_CLASS(ResettableClass, (obj), TYPE_RESETTABLE_INTERFACE) + +typedef struct ResettableState ResettableState; + +/** + * ResetType: + * Types of reset. + * + * + Cold: reset resulting from a power cycle of the object. + * + * TODO: Support has to be added to handle more types. In particular, + * ResettableState structure needs to be expanded. + */ +typedef enum ResetType { + RESET_TYPE_COLD, +} ResetType; + +/* + * ResettableClass: + * Interface for resettable objects. + * + * See docs/devel/reset.rst for more detailed information about how QEMU models + * reset. This whole API must only be used when holding the iothread mutex. + * + * All objects which can be reset must implement this interface; + * it is usually provided by a base class such as DeviceClass or BusClass. + * Every Resettable object must maintain some state tracking the + * progress of a reset operation by providing a ResettableState structure. + * The functions defined in this module take care of updating the + * state of the reset. + * The base class implementation of the interface provides this + * state and implements the associated method: get_state. + * + * Concrete object implementations (typically specific devices + * such as a UART model) should provide the functions + * for the phases.enter, phases.hold and phases.exit methods, which + * they can set in their class init function, either directly or + * by calling resettable_class_set_parent_phases(). + * The phase methods are guaranteed to only only ever be called once + * for any reset event, in the order 'enter', 'hold', 'exit'. + * An object will always move quickly from 'enter' to 'hold' + * but might remain in 'hold' for an arbitrary period of time + * before eventually reset is deasserted and the 'exit' phase is called. + * Object implementations should be prepared for functions handling + * inbound connections from other devices (such as qemu_irq handler + * functions) to be called at any point during reset after their + * 'enter' method has been called. + * + * Users of a resettable object should not call these methods + * directly, but instead use the function resettable_reset(). + * + * @phases.enter: This phase is called when the object enters reset. It + * should reset local state of the object, but it must not do anything that + * has a side-effect on other objects, such as raising or lowering a qemu_irq + * line or reading or writing guest memory. It takes the reset's type as + * argument. + * + * @phases.hold: This phase is called for entry into reset, once every object + * in the system which is being reset has had its @phases.enter method called. + * At this point devices can do actions that affect other objects. + * + * @phases.exit: This phase is called when the object leaves the reset state. + * Actions affecting other objects are permitted. + * + * @get_state: Mandatory method which must return a pointer to a + * ResettableState. + * + * @get_transitional_function: transitional method to handle Resettable objects + * not yet fully moved to this interface. It will be removed as soon as it is + * not needed anymore. This method is optional and may return a pointer to a + * function to be used instead of the phases. If the method exists and returns + * a non-NULL function pointer then that function is executed as a replacement + * of the 'hold' phase method taking the object as argument. The two other phase + * methods are not executed. + * + * @child_foreach: Executes a given callback on every Resettable child. Child + * in this context means a child in the qbus tree, so the children of a qbus + * are the devices on it, and the children of a device are all the buses it + * owns. This is not the same as the QOM object hierarchy. The function takes + * additional opaque and ResetType arguments which must be passed unmodified to + * the callback. + */ +typedef void (*ResettableEnterPhase)(Object *obj, ResetType type); +typedef void (*ResettableHoldPhase)(Object *obj); +typedef void (*ResettableExitPhase)(Object *obj); +typedef ResettableState * (*ResettableGetState)(Object *obj); +typedef void (*ResettableTrFunction)(Object *obj); +typedef ResettableTrFunction (*ResettableGetTrFunction)(Object *obj); +typedef void (*ResettableChildCallback)(Object *, void *opaque, + ResetType type); +typedef void (*ResettableChildForeach)(Object *obj, + ResettableChildCallback cb, + void *opaque, ResetType type); +typedef struct ResettablePhases { + ResettableEnterPhase enter; + ResettableHoldPhase hold; + ResettableExitPhase exit; +} ResettablePhases; +typedef struct ResettableClass { + InterfaceClass parent_class; + + /* Phase methods */ + ResettablePhases phases; + + /* State access method */ + ResettableGetState get_state; + + /* Transitional method for legacy reset compatibility */ + ResettableGetTrFunction get_transitional_function; + + /* Hierarchy handling method */ + ResettableChildForeach child_foreach; +} ResettableClass; + +/** + * ResettableState: + * Structure holding reset related state. The fields should not be accessed + * directly; the definition is here to allow further inclusion into other + * objects. + * + * @count: Number of reset level the object is into. It is incremented when + * the reset operation starts and decremented when it finishes. + * @hold_phase_pending: flag which indicates that we need to invoke the 'hold' + * phase handler for this object. + * @exit_phase_in_progress: true if we are currently in the exit phase + */ +struct ResettableState { + unsigned count; + bool hold_phase_pending; + bool exit_phase_in_progress; +}; + +/** + * resettable_state_clear: + * Clear the state. It puts the state to the initial (zeroed) state required + * to reuse an object. Typically used in realize step of base classes + * implementing the interface. + */ +static inline void resettable_state_clear(ResettableState *state) +{ + memset(state, 0, sizeof(ResettableState)); +} + +/** + * resettable_reset: + * Trigger a reset on an object @obj of type @type. @obj must implement + * Resettable interface. + * + * Calling this function is equivalent to calling @resettable_assert_reset() + * then @resettable_release_reset(). + */ +void resettable_reset(Object *obj, ResetType type); + +/** + * resettable_assert_reset: + * Put an object @obj into reset. @obj must implement Resettable interface. + * + * @resettable_release_reset() must eventually be called after this call. + * There must be one call to @resettable_release_reset() per call of + * @resettable_assert_reset(), with the same type argument. + * + * NOTE: Until support for migration is added, the @resettable_release_reset() + * must not be delayed. It must occur just after @resettable_assert_reset() so + * that migration cannot be triggered in between. Prefer using + * @resettable_reset() for now. + */ +void resettable_assert_reset(Object *obj, ResetType type); + +/** + * resettable_release_reset: + * Release the object @obj from reset. @obj must implement Resettable interface. + * + * See @resettable_assert_reset() description for details. + */ +void resettable_release_reset(Object *obj, ResetType type); + +/** + * resettable_is_in_reset: + * Return true if @obj is under reset. + * + * @obj must implement Resettable interface. + */ +bool resettable_is_in_reset(Object *obj); + +/** + * resettable_change_parent: + * Indicate that the parent of Ressettable @obj is changing from @oldp to @newp. + * All 3 objects must implement resettable interface. @oldp or @newp may be + * NULL. + * + * This function will adapt the reset state of @obj so that it is coherent + * with the reset state of @newp. It may trigger @resettable_assert_reset() + * or @resettable_release_reset(). It will do such things only if the reset + * state of @newp and @oldp are different. + * + * When using this function during reset, it must only be called during + * a hold phase method. Calling this during enter or exit phase is an error. + */ +void resettable_change_parent(Object *obj, Object *newp, Object *oldp); + +/** + * resettable_cold_reset_fn: + * Helper to call resettable_reset((Object *) opaque, RESET_TYPE_COLD). + * + * This function is typically useful to register a reset handler with + * qemu_register_reset. + */ +void resettable_cold_reset_fn(void *opaque); + +/** + * resettable_class_set_parent_phases: + * + * Save @rc current reset phases into @parent_phases and override @rc phases + * by the given new methods (@enter, @hold and @exit). + * Each phase is overridden only if the new one is not NULL allowing to + * override a subset of phases. + */ +void resettable_class_set_parent_phases(ResettableClass *rc, + ResettableEnterPhase enter, + ResettableHoldPhase hold, + ResettableExitPhase exit, + ResettablePhases *parent_phases); + +#endif diff --git a/include/hw/riscv/sifive_clint.h b/include/hw/riscv/sifive_clint.h index ae8286c884..4a720bfece 100644 --- a/include/hw/riscv/sifive_clint.h +++ b/include/hw/riscv/sifive_clint.h @@ -41,7 +41,8 @@ typedef struct SiFiveCLINTState { } SiFiveCLINTState; DeviceState *sifive_clint_create(hwaddr addr, hwaddr size, uint32_t num_harts, - uint32_t sip_base, uint32_t timecmp_base, uint32_t time_base); + uint32_t sip_base, uint32_t timecmp_base, uint32_t time_base, + bool provide_rdtime); enum { SIFIVE_SIP_BASE = 0x0, diff --git a/include/hw/riscv/virt.h b/include/hw/riscv/virt.h index b17048a93a..e69355efaf 100644 --- a/include/hw/riscv/virt.h +++ b/include/hw/riscv/virt.h @@ -44,6 +44,7 @@ enum { VIRT_DEBUG, VIRT_MROM, VIRT_TEST, + VIRT_RTC, VIRT_CLINT, VIRT_PLIC, VIRT_UART0, @@ -57,6 +58,7 @@ enum { enum { UART0_IRQ = 10, + RTC_IRQ = 11, VIRTIO_IRQ = 1, /* 1 to 8 */ VIRTIO_COUNT = 8, PCIE_IRQ = 0x20, /* 32 to 35 */ diff --git a/include/hw/rtc/allwinner-rtc.h b/include/hw/rtc/allwinner-rtc.h new file mode 100644 index 0000000000..7893f74795 --- /dev/null +++ b/include/hw/rtc/allwinner-rtc.h @@ -0,0 +1,134 @@ +/* + * Allwinner Real Time Clock emulation + * + * Copyright (C) 2019 Niek Linnenbank + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef HW_MISC_ALLWINNER_RTC_H +#define HW_MISC_ALLWINNER_RTC_H + +#include "qom/object.h" +#include "hw/sysbus.h" + +/** + * Constants + * @{ + */ + +/** Highest register address used by RTC device */ +#define AW_RTC_REGS_MAXADDR (0x200) + +/** Total number of known registers */ +#define AW_RTC_REGS_NUM (AW_RTC_REGS_MAXADDR / sizeof(uint32_t)) + +/** @} */ + +/** + * Object model types + * @{ + */ + +/** Generic Allwinner RTC device (abstract) */ +#define TYPE_AW_RTC "allwinner-rtc" + +/** Allwinner RTC sun4i family (A10, A12) */ +#define TYPE_AW_RTC_SUN4I TYPE_AW_RTC "-sun4i" + +/** Allwinner RTC sun6i family and newer (A31, H2+, H3, etc) */ +#define TYPE_AW_RTC_SUN6I TYPE_AW_RTC "-sun6i" + +/** Allwinner RTC sun7i family (A20) */ +#define TYPE_AW_RTC_SUN7I TYPE_AW_RTC "-sun7i" + +/** @} */ + +/** + * Object model macros + * @{ + */ + +#define AW_RTC(obj) \ + OBJECT_CHECK(AwRtcState, (obj), TYPE_AW_RTC) +#define AW_RTC_CLASS(klass) \ + OBJECT_CLASS_CHECK(AwRtcClass, (klass), TYPE_AW_RTC) +#define AW_RTC_GET_CLASS(obj) \ + OBJECT_GET_CLASS(AwRtcClass, (obj), TYPE_AW_RTC) + +/** @} */ + +/** + * Allwinner RTC per-object instance state. + */ +typedef struct AwRtcState { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + + /** + * Actual year represented by the device when year counter is zero + * + * Can be overridden by the user using the corresponding 'base-year' + * property. The base year used by the target OS driver can vary, for + * example the Linux driver for sun6i uses 1970 while NetBSD uses 2000. + */ + int base_year; + + /** Maps I/O registers in physical memory */ + MemoryRegion iomem; + + /** Array of hardware registers */ + uint32_t regs[AW_RTC_REGS_NUM]; + +} AwRtcState; + +/** + * Allwinner RTC class-level struct. + * + * This struct is filled by each sunxi device specific code + * such that the generic code can use this struct to support + * all devices. + */ +typedef struct AwRtcClass { + /*< private >*/ + SysBusDeviceClass parent_class; + /*< public >*/ + + /** Defines device specific register map */ + const uint8_t *regmap; + + /** Size of the regmap in bytes */ + size_t regmap_size; + + /** + * Read device specific register + * + * @offset: register offset to read + * @return true if register read successful, false otherwise + */ + bool (*read)(AwRtcState *s, uint32_t offset); + + /** + * Write device specific register + * + * @offset: register offset to write + * @data: value to set in register + * @return true if register write successful, false otherwise + */ + bool (*write)(AwRtcState *s, uint32_t offset, uint32_t data); + +} AwRtcClass; + +#endif /* HW_MISC_ALLWINNER_RTC_H */ diff --git a/include/hw/rtc/goldfish_rtc.h b/include/hw/rtc/goldfish_rtc.h new file mode 100644 index 0000000000..16f9f9e29d --- /dev/null +++ b/include/hw/rtc/goldfish_rtc.h @@ -0,0 +1,46 @@ +/* + * Goldfish virtual platform RTC + * + * Copyright (C) 2019 Western Digital Corporation or its affiliates. + * + * For more details on Google Goldfish virtual platform refer: + * https://android.googlesource.com/platform/external/qemu/+/master/docs/GOLDFISH-VIRTUAL-HARDWARE.TXT + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#ifndef HW_RTC_GOLDFISH_RTC_H +#define HW_RTC_GOLDFISH_RTC_H + +#include "hw/sysbus.h" + +#define TYPE_GOLDFISH_RTC "goldfish_rtc" +#define GOLDFISH_RTC(obj) \ + OBJECT_CHECK(GoldfishRTCState, (obj), TYPE_GOLDFISH_RTC) + +typedef struct GoldfishRTCState { + SysBusDevice parent_obj; + + MemoryRegion iomem; + QEMUTimer *timer; + qemu_irq irq; + + uint64_t tick_offset; + uint64_t tick_offset_vmstate; + uint64_t alarm_next; + uint32_t alarm_running; + uint32_t irq_pending; + uint32_t irq_enabled; +} GoldfishRTCState; + +#endif diff --git a/include/hw/s390x/event-facility.h b/include/hw/s390x/event-facility.h index bdc32a3c09..700a610f33 100644 --- a/include/hw/s390x/event-facility.h +++ b/include/hw/s390x/event-facility.h @@ -122,7 +122,7 @@ typedef struct MDBO { typedef struct MDB { MdbHeader header; - MDBO mdbo[0]; + MDBO mdbo[]; } QEMU_PACKED MDB; typedef struct SclpMsg { diff --git a/include/hw/s390x/sclp.h b/include/hw/s390x/sclp.h index c54413b78c..cd7b24359f 100644 --- a/include/hw/s390x/sclp.h +++ b/include/hw/s390x/sclp.h @@ -132,7 +132,7 @@ typedef struct ReadInfo { uint16_t highest_cpu; uint8_t _reserved5[124 - 122]; /* 122-123 */ uint32_t hmfai; - struct CPUEntry entries[0]; + struct CPUEntry entries[]; } QEMU_PACKED ReadInfo; typedef struct ReadCpuInfo { @@ -142,7 +142,7 @@ typedef struct ReadCpuInfo { uint16_t nr_standby; /* 12-13 */ uint16_t offset_standby; /* 14-15 */ uint8_t reserved0[24-16]; /* 16-23 */ - struct CPUEntry entries[0]; + struct CPUEntry entries[]; } QEMU_PACKED ReadCpuInfo; typedef struct ReadStorageElementInfo { @@ -151,7 +151,7 @@ typedef struct ReadStorageElementInfo { uint16_t assigned; uint16_t standby; uint8_t _reserved0[16 - 14]; /* 14-15 */ - uint32_t entries[0]; + uint32_t entries[]; } QEMU_PACKED ReadStorageElementInfo; typedef struct AttachStorageElement { @@ -159,7 +159,7 @@ typedef struct AttachStorageElement { uint8_t _reserved0[10 - 8]; /* 8-9 */ uint16_t assigned; uint8_t _reserved1[16 - 12]; /* 12-15 */ - uint32_t entries[0]; + uint32_t entries[]; } QEMU_PACKED AttachStorageElement; typedef struct AssignStorage { diff --git a/include/hw/sd/allwinner-sdhost.h b/include/hw/sd/allwinner-sdhost.h new file mode 100644 index 0000000000..d94606a853 --- /dev/null +++ b/include/hw/sd/allwinner-sdhost.h @@ -0,0 +1,135 @@ +/* + * Allwinner (sun4i and above) SD Host Controller emulation + * + * Copyright (C) 2019 Niek Linnenbank + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef HW_SD_ALLWINNER_SDHOST_H +#define HW_SD_ALLWINNER_SDHOST_H + +#include "qom/object.h" +#include "hw/sysbus.h" +#include "hw/sd/sd.h" + +/** + * Object model types + * @{ + */ + +/** Generic Allwinner SD Host Controller (abstract) */ +#define TYPE_AW_SDHOST "allwinner-sdhost" + +/** Allwinner sun4i family (A10, A12) */ +#define TYPE_AW_SDHOST_SUN4I TYPE_AW_SDHOST "-sun4i" + +/** Allwinner sun5i family and newer (A13, H2+, H3, etc) */ +#define TYPE_AW_SDHOST_SUN5I TYPE_AW_SDHOST "-sun5i" + +/** @} */ + +/** + * Object model macros + * @{ + */ + +#define AW_SDHOST(obj) \ + OBJECT_CHECK(AwSdHostState, (obj), TYPE_AW_SDHOST) +#define AW_SDHOST_CLASS(klass) \ + OBJECT_CLASS_CHECK(AwSdHostClass, (klass), TYPE_AW_SDHOST) +#define AW_SDHOST_GET_CLASS(obj) \ + OBJECT_GET_CLASS(AwSdHostClass, (obj), TYPE_AW_SDHOST) + +/** @} */ + +/** + * Allwinner SD Host Controller object instance state. + */ +typedef struct AwSdHostState { + /*< private >*/ + SysBusDevice busdev; + /*< public >*/ + + /** Secure Digital (SD) bus, which connects to SD card (if present) */ + SDBus sdbus; + + /** Maps I/O registers in physical memory */ + MemoryRegion iomem; + + /** Interrupt output signal to notify CPU */ + qemu_irq irq; + + /** Number of bytes left in current DMA transfer */ + uint32_t transfer_cnt; + + /** + * @name Hardware Registers + * @{ + */ + + uint32_t global_ctl; /**< Global Control */ + uint32_t clock_ctl; /**< Clock Control */ + uint32_t timeout; /**< Timeout */ + uint32_t bus_width; /**< Bus Width */ + uint32_t block_size; /**< Block Size */ + uint32_t byte_count; /**< Byte Count */ + + uint32_t command; /**< Command */ + uint32_t command_arg; /**< Command Argument */ + uint32_t response[4]; /**< Command Response */ + + uint32_t irq_mask; /**< Interrupt Mask */ + uint32_t irq_status; /**< Raw Interrupt Status */ + uint32_t status; /**< Status */ + + uint32_t fifo_wlevel; /**< FIFO Water Level */ + uint32_t fifo_func_sel; /**< FIFO Function Select */ + uint32_t debug_enable; /**< Debug Enable */ + uint32_t auto12_arg; /**< Auto Command 12 Argument */ + uint32_t newtiming_set; /**< SD New Timing Set */ + uint32_t newtiming_debug; /**< SD New Timing Debug */ + uint32_t hardware_rst; /**< Hardware Reset */ + uint32_t dmac; /**< Internal DMA Controller Control */ + uint32_t desc_base; /**< Descriptor List Base Address */ + uint32_t dmac_status; /**< Internal DMA Controller Status */ + uint32_t dmac_irq; /**< Internal DMA Controller IRQ Enable */ + uint32_t card_threshold; /**< Card Threshold Control */ + uint32_t startbit_detect; /**< eMMC DDR Start Bit Detection Control */ + uint32_t response_crc; /**< Response CRC */ + uint32_t data_crc[8]; /**< Data CRC */ + uint32_t status_crc; /**< Status CRC */ + + /** @} */ + +} AwSdHostState; + +/** + * Allwinner SD Host Controller class-level struct. + * + * This struct is filled by each sunxi device specific code + * such that the generic code can use this struct to support + * all devices. + */ +typedef struct AwSdHostClass { + /*< private >*/ + SysBusDeviceClass parent_class; + /*< public >*/ + + /** Maximum buffer size in bytes per DMA descriptor */ + size_t max_desc_size; + +} AwSdHostClass; + +#endif /* HW_SD_ALLWINNER_SDHOST_H */ diff --git a/include/hw/sd/aspeed_sdhci.h b/include/hw/sd/aspeed_sdhci.h index dfdab43790..dffbb46946 100644 --- a/include/hw/sd/aspeed_sdhci.h +++ b/include/hw/sd/aspeed_sdhci.h @@ -24,6 +24,7 @@ typedef struct AspeedSDHCIState { SysBusDevice parent; SDHCIState slots[ASPEED_SDHCI_NUM_SLOTS]; + uint8_t num_slots; MemoryRegion iomem; qemu_irq irq; diff --git a/include/hw/semihosting/console.h b/include/hw/semihosting/console.h index 9be9754bcd..0238f540f4 100644 --- a/include/hw/semihosting/console.h +++ b/include/hw/semihosting/console.h @@ -37,6 +37,22 @@ int qemu_semihosting_console_outs(CPUArchState *env, target_ulong s); */ void qemu_semihosting_console_outc(CPUArchState *env, target_ulong c); +/** + * qemu_semihosting_console_inc: + * @env: CPUArchState + * + * Receive single character from debug console. This may be the remote + * gdb session if a softmmu guest is currently being debugged. As this + * call may block if no data is available we suspend the CPU and will + * re-execute the instruction when data is there. Therefore two + * conditions must be met: + * - CPUState is synchronized before calling this function + * - pc is only updated once the character is successfully returned + * + * Returns: character read OR cpu_loop_exit! + */ +target_ulong qemu_semihosting_console_inc(CPUArchState *env); + /** * qemu_semihosting_log_out: * @s: pointer to string diff --git a/include/hw/semihosting/semihost.h b/include/hw/semihosting/semihost.h index 60fc42d851..b8ce5117ae 100644 --- a/include/hw/semihosting/semihost.h +++ b/include/hw/semihosting/semihost.h @@ -56,6 +56,9 @@ static inline Chardev *semihosting_get_chardev(void) { return NULL; } +static inline void qemu_semihosting_console_init(void) +{ +} #else /* !CONFIG_USER_ONLY */ bool semihosting_enabled(void); SemihostingTarget semihosting_get_target(void); @@ -68,6 +71,7 @@ Chardev *semihosting_get_chardev(void); void qemu_semihosting_enable(void); int qemu_semihosting_config_options(const char *opt); void qemu_semihosting_connect_chardevs(void); +void qemu_semihosting_console_init(void); #endif /* CONFIG_USER_ONLY */ #endif /* SEMIHOST_H */ diff --git a/include/hw/southbridge/piix.h b/include/hw/southbridge/piix.h index 152628c6d9..02bd741209 100644 --- a/include/hw/southbridge/piix.h +++ b/include/hw/southbridge/piix.h @@ -68,7 +68,6 @@ extern PCIDevice *piix4_dev; PIIX3State *piix3_create(PCIBus *pci_bus, ISABus **isa_bus); -DeviceState *piix4_create(PCIBus *pci_bus, ISABus **isa_bus, - I2CBus **smbus, size_t ide_buses); +DeviceState *piix4_create(PCIBus *pci_bus, ISABus **isa_bus, I2CBus **smbus); #endif diff --git a/include/hw/ssi/aspeed_smc.h b/include/hw/ssi/aspeed_smc.h index 684d16e336..6fbbb238f1 100644 --- a/include/hw/ssi/aspeed_smc.h +++ b/include/hw/ssi/aspeed_smc.h @@ -40,6 +40,7 @@ typedef struct AspeedSMCController { uint8_t r_ce_ctrl; uint8_t r_ctrl0; uint8_t r_timings; + uint8_t nregs_timings; uint8_t conf_enable_w0; uint8_t max_slaves; const AspeedSegments *segments; diff --git a/include/hw/sysbus.h b/include/hw/sysbus.h index 27e80881da..c4a1c0adfa 100644 --- a/include/hw/sysbus.h +++ b/include/hw/sysbus.h @@ -24,10 +24,6 @@ typedef struct SysBusDevice SysBusDevice; /** * SysBusDeviceClass: - * @init: Callback function invoked when the #DeviceState.realized property - * is changed to %true. Deprecated, new types inheriting directly from - * TYPE_SYS_BUS_DEVICE should use #DeviceClass.realize instead, new leaf - * types should consult their respective parent type. * * SysBusDeviceClass is not overriding #DeviceClass.realize, so derived * classes overriding it are not required to invoke its implementation. @@ -117,8 +113,7 @@ void foreach_dynamic_sysbus_device(FindSysbusDeviceFunc *func, void *opaque); /* Legacy helper function for creating devices. */ DeviceState *sysbus_create_varargs(const char *name, hwaddr addr, ...); -DeviceState *sysbus_try_create_varargs(const char *name, - hwaddr addr, ...); + static inline DeviceState *sysbus_create_simple(const char *name, hwaddr addr, qemu_irq irq) @@ -126,11 +121,5 @@ static inline DeviceState *sysbus_create_simple(const char *name, return sysbus_create_varargs(name, addr, irq, NULL); } -static inline DeviceState *sysbus_try_create_simple(const char *name, - hwaddr addr, - qemu_irq irq) -{ - return sysbus_try_create_varargs(name, addr, irq, NULL); -} #endif /* HW_SYSBUS_H */ diff --git a/include/hw/usb/imx-usb-phy.h b/include/hw/usb/imx-usb-phy.h new file mode 100644 index 0000000000..07f0235d10 --- /dev/null +++ b/include/hw/usb/imx-usb-phy.h @@ -0,0 +1,53 @@ +#ifndef IMX_USB_PHY_H +#define IMX_USB_PHY_H + +#include "hw/sysbus.h" +#include "qemu/bitops.h" + +enum IMXUsbPhyRegisters { + USBPHY_PWD, + USBPHY_PWD_SET, + USBPHY_PWD_CLR, + USBPHY_PWD_TOG, + USBPHY_TX, + USBPHY_TX_SET, + USBPHY_TX_CLR, + USBPHY_TX_TOG, + USBPHY_RX, + USBPHY_RX_SET, + USBPHY_RX_CLR, + USBPHY_RX_TOG, + USBPHY_CTRL, + USBPHY_CTRL_SET, + USBPHY_CTRL_CLR, + USBPHY_CTRL_TOG, + USBPHY_STATUS, + USBPHY_DEBUG = 0x14, + USBPHY_DEBUG_SET, + USBPHY_DEBUG_CLR, + USBPHY_DEBUG_TOG, + USBPHY_DEBUG0_STATUS, + USBPHY_DEBUG1 = 0x1c, + USBPHY_DEBUG1_SET, + USBPHY_DEBUG1_CLR, + USBPHY_DEBUG1_TOG, + USBPHY_VERSION, + USBPHY_MAX +}; + +#define USBPHY_CTRL_SFTRST BIT(31) + +#define TYPE_IMX_USBPHY "imx.usbphy" +#define IMX_USBPHY(obj) OBJECT_CHECK(IMXUSBPHYState, (obj), TYPE_IMX_USBPHY) + +typedef struct IMXUSBPHYState { + /* */ + SysBusDevice parent_obj; + + /* */ + MemoryRegion iomem; + + uint32_t usbphy[USBPHY_MAX]; +} IMXUSBPHYState; + +#endif /* IMX_USB_PHY_H */ diff --git a/include/hw/virtio/vhost-user-blk.h b/include/hw/virtio/vhost-user-blk.h index 108bfadeeb..34ad6f0c0e 100644 --- a/include/hw/virtio/vhost-user-blk.h +++ b/include/hw/virtio/vhost-user-blk.h @@ -36,8 +36,8 @@ typedef struct VHostUserBlk { struct vhost_dev dev; struct vhost_inflight *inflight; VhostUserState vhost_user; - struct vhost_virtqueue *vqs; - guint watch; + struct vhost_virtqueue *vhost_vqs; + VirtQueue **virtqs; bool connected; } VHostUserBlk; diff --git a/include/hw/virtio/vhost-user-fs.h b/include/hw/virtio/vhost-user-fs.h index 539885b458..6f3030d288 100644 --- a/include/hw/virtio/vhost-user-fs.h +++ b/include/hw/virtio/vhost-user-fs.h @@ -28,7 +28,6 @@ typedef struct { char *tag; uint16_t num_request_queues; uint16_t queue_size; - char *vhostfd; } VHostUserFSConf; typedef struct { @@ -38,6 +37,8 @@ typedef struct { struct vhost_virtqueue *vhost_vqs; struct vhost_dev vhost_dev; VhostUserState vhost_user; + VirtQueue **req_vqs; + VirtQueue *hiprio_vq; /*< public >*/ } VHostUserFS; diff --git a/include/hw/virtio/vhost-vsock.h b/include/hw/virtio/vhost-vsock.h index d509d67c4a..bc5a988ee5 100644 --- a/include/hw/virtio/vhost-vsock.h +++ b/include/hw/virtio/vhost-vsock.h @@ -33,6 +33,8 @@ typedef struct { struct vhost_virtqueue vhost_vqs[2]; struct vhost_dev vhost_dev; VirtQueue *event_vq; + VirtQueue *recv_vq; + VirtQueue *trans_vq; QEMUTimer *post_load_timer; /*< public >*/ diff --git a/include/hw/virtio/virtio-blk.h b/include/hw/virtio/virtio-blk.h index cddcfbebe9..1e62f869b2 100644 --- a/include/hw/virtio/virtio-blk.h +++ b/include/hw/virtio/virtio-blk.h @@ -38,8 +38,10 @@ struct VirtIOBlkConf uint32_t request_merging; uint16_t num_queues; uint16_t queue_size; + bool seg_max_adjust; uint32_t max_discard_sectors; uint32_t max_write_zeroes_sectors; + bool x_enable_wce_if_config_wce; }; struct VirtIOBlockDataPlane; diff --git a/include/hw/virtio/virtio-iommu.h b/include/hw/virtio/virtio-iommu.h new file mode 100644 index 0000000000..e653004d7c --- /dev/null +++ b/include/hw/virtio/virtio-iommu.h @@ -0,0 +1,61 @@ +/* + * virtio-iommu device + * + * Copyright (c) 2020 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + */ + +#ifndef QEMU_VIRTIO_IOMMU_H +#define QEMU_VIRTIO_IOMMU_H + +#include "standard-headers/linux/virtio_iommu.h" +#include "hw/virtio/virtio.h" +#include "hw/pci/pci.h" + +#define TYPE_VIRTIO_IOMMU "virtio-iommu-device" +#define TYPE_VIRTIO_IOMMU_PCI "virtio-iommu-device-base" +#define VIRTIO_IOMMU(obj) \ + OBJECT_CHECK(VirtIOIOMMU, (obj), TYPE_VIRTIO_IOMMU) + +#define TYPE_VIRTIO_IOMMU_MEMORY_REGION "virtio-iommu-memory-region" + +typedef struct IOMMUDevice { + void *viommu; + PCIBus *bus; + int devfn; + IOMMUMemoryRegion iommu_mr; + AddressSpace as; +} IOMMUDevice; + +typedef struct IOMMUPciBus { + PCIBus *bus; + IOMMUDevice *pbdev[]; /* Parent array is sparse, so dynamically alloc */ +} IOMMUPciBus; + +typedef struct VirtIOIOMMU { + VirtIODevice parent_obj; + VirtQueue *req_vq; + VirtQueue *event_vq; + struct virtio_iommu_config config; + uint64_t features; + GHashTable *as_by_busptr; + IOMMUPciBus *iommu_pcibus_by_bus_num[PCI_BUS_MAX]; + PCIBus *primary_bus; + GTree *domains; + QemuMutex mutex; + GTree *endpoints; +} VirtIOIOMMU; + +#endif diff --git a/include/hw/virtio/virtio-scsi.h b/include/hw/virtio/virtio-scsi.h index 122f7c4b6f..24e768909d 100644 --- a/include/hw/virtio/virtio-scsi.h +++ b/include/hw/virtio/virtio-scsi.h @@ -48,6 +48,7 @@ typedef struct virtio_scsi_config VirtIOSCSIConfig; struct VirtIOSCSIConf { uint32_t num_queues; uint32_t virtqueue_size; + bool seg_max_adjust; uint32_t max_sectors; uint32_t cmd_per_lun; #ifdef CONFIG_VHOST_SCSI diff --git a/include/hw/virtio/virtio.h b/include/hw/virtio/virtio.h index c32a815303..b69d517496 100644 --- a/include/hw/virtio/virtio.h +++ b/include/hw/virtio/virtio.h @@ -100,6 +100,8 @@ struct VirtIODevice uint16_t device_id; bool vm_running; bool broken; /* device in invalid state, needs reset */ + bool use_disabled_flag; /* allow use of 'disable' flag when needed */ + bool disabled; /* device in temporarily disabled state */ bool use_started; bool started; bool start_on_kick; /* when virtio 1.0 feature has not been negotiated */ @@ -183,6 +185,8 @@ VirtQueue *virtio_add_queue(VirtIODevice *vdev, int queue_size, void virtio_del_queue(VirtIODevice *vdev, int n); +void virtio_delete_queue(VirtQueue *vq); + void virtqueue_push(VirtQueue *vq, const VirtQueueElement *elem, unsigned int len); void virtqueue_flush(VirtQueue *vq, unsigned int count); @@ -224,6 +228,7 @@ int virtio_load(VirtIODevice *vdev, QEMUFile *f, int version_id); void virtio_notify_config(VirtIODevice *vdev); +bool virtio_queue_get_notification(VirtQueue *vq); void virtio_queue_set_notification(VirtQueue *vq, int enable); int virtio_queue_ready(VirtQueue *vq); @@ -378,4 +383,17 @@ static inline void virtio_set_started(VirtIODevice *vdev, bool started) vdev->started = started; } } + +static inline void virtio_set_disabled(VirtIODevice *vdev, bool disable) +{ + if (vdev->use_disabled_flag) { + vdev->disabled = disable; + } +} + +static inline bool virtio_device_disabled(VirtIODevice *vdev) +{ + return unlikely(vdev->disabled || vdev->broken); +} + #endif diff --git a/include/hw/vmstate-if.h b/include/hw/vmstate-if.h new file mode 100644 index 0000000000..8ff7f0f292 --- /dev/null +++ b/include/hw/vmstate-if.h @@ -0,0 +1,40 @@ +/* + * VMState interface + * + * Copyright (c) 2009-2019 Red Hat Inc + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef VMSTATE_IF_H +#define VMSTATE_IF_H + +#include "qom/object.h" + +#define TYPE_VMSTATE_IF "vmstate-if" + +#define VMSTATE_IF_CLASS(klass) \ + OBJECT_CLASS_CHECK(VMStateIfClass, (klass), TYPE_VMSTATE_IF) +#define VMSTATE_IF_GET_CLASS(obj) \ + OBJECT_GET_CLASS(VMStateIfClass, (obj), TYPE_VMSTATE_IF) +#define VMSTATE_IF(obj) \ + INTERFACE_CHECK(VMStateIf, (obj), TYPE_VMSTATE_IF) + +typedef struct VMStateIf VMStateIf; + +typedef struct VMStateIfClass { + InterfaceClass parent_class; + + char * (*get_id)(VMStateIf *obj); +} VMStateIfClass; + +static inline char *vmstate_if_get_id(VMStateIf *vmif) +{ + if (!vmif) { + return NULL; + } + + return VMSTATE_IF_GET_CLASS(vmif)->get_id(vmif); +} + +#endif /* VMSTATE_IF_H */ diff --git a/include/hw/watchdog/wdt_aspeed.h b/include/hw/watchdog/wdt_aspeed.h index dfedd7662d..819c22993a 100644 --- a/include/hw/watchdog/wdt_aspeed.h +++ b/include/hw/watchdog/wdt_aspeed.h @@ -47,6 +47,7 @@ typedef struct AspeedWDTClass { uint32_t ext_pulse_width_mask; uint32_t reset_ctrl_reg; void (*reset_pulse)(AspeedWDTState *s, uint32_t property); + void (*wdt_reload)(AspeedWDTState *s); } AspeedWDTClass; #endif /* WDT_ASPEED_H */ diff --git a/include/hw/xen/xen-bus.h b/include/hw/xen/xen-bus.h index 3d5532258d..c18c1372af 100644 --- a/include/hw/xen/xen-bus.h +++ b/include/hw/xen/xen-bus.h @@ -128,10 +128,13 @@ void xen_device_copy_grant_refs(XenDevice *xendev, bool to_domain, typedef bool (*XenEventHandler)(void *opaque); XenEventChannel *xen_device_bind_event_channel(XenDevice *xendev, - AioContext *ctx, unsigned int port, XenEventHandler handler, void *opaque, Error **errp); +void xen_device_set_event_channel_context(XenDevice *xendev, + XenEventChannel *channel, + AioContext *ctx, + Error **errp); void xen_device_notify_event_channel(XenDevice *xendev, XenEventChannel *channel, Error **errp); diff --git a/include/io/task.h b/include/io/task.h index 5cb9faf9f2..1abbfb8b65 100644 --- a/include/io/task.h +++ b/include/io/task.h @@ -119,7 +119,7 @@ typedef void (*QIOTaskWorker)(QIOTask *task, * gboolean myobject_operation_timer(gpointer opaque) * { * QIOTask *task = QIO_TASK(opaque); - * Error *err;* + * Error *err = NULL; * * ...check something important... * if (err) { diff --git a/include/migration/register.h b/include/migration/register.h index a13359a08d..c1dcff0f90 100644 --- a/include/migration/register.h +++ b/include/migration/register.h @@ -14,6 +14,8 @@ #ifndef MIGRATION_REGISTER_H #define MIGRATION_REGISTER_H +#include "hw/vmstate-if.h" + typedef struct SaveVMHandlers { /* This runs inside the iothread lock. */ SaveStateHandler *save_state; @@ -69,11 +71,11 @@ typedef struct SaveVMHandlers { } SaveVMHandlers; int register_savevm_live(const char *idstr, - int instance_id, + uint32_t instance_id, int version_id, const SaveVMHandlers *ops, void *opaque); -void unregister_savevm(DeviceState *dev, const char *idstr, void *opaque); +void unregister_savevm(VMStateIf *obj, const char *idstr, void *opaque); #endif diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h index ac4f46a67d..30667631bc 100644 --- a/include/migration/vmstate.h +++ b/include/migration/vmstate.h @@ -27,6 +27,8 @@ #ifndef QEMU_VMSTATE_H #define QEMU_VMSTATE_H +#include "hw/vmstate-if.h" + typedef struct VMStateInfo VMStateInfo; typedef struct VMStateField VMStateField; @@ -227,6 +229,7 @@ extern const VMStateInfo vmstate_info_tmp; extern const VMStateInfo vmstate_info_bitmap; extern const VMStateInfo vmstate_info_qtailq; extern const VMStateInfo vmstate_info_gtree; +extern const VMStateInfo vmstate_info_qlist; #define type_check_2darray(t1,t2,n,m) ((t1(*)[n][m])0 - (t2*)0) /* @@ -796,6 +799,26 @@ extern const VMStateInfo vmstate_info_gtree; .offset = offsetof(_state, _field), \ } +/* + * For migrating a QLIST + * Target QLIST needs be properly initialized. + * _type: type of QLIST element + * _next: name of QLIST_ENTRY entry field in QLIST element + * _vmsd: VMSD for QLIST element + * size: size of QLIST element + * start: offset of QLIST_ENTRY in QTAILQ element + */ +#define VMSTATE_QLIST_V(_field, _state, _version, _vmsd, _type, _next) \ +{ \ + .name = (stringify(_field)), \ + .version_id = (_version), \ + .vmsd = &(_vmsd), \ + .size = sizeof(_type), \ + .info = &vmstate_info_qlist, \ + .offset = offsetof(_state, _field), \ + .start = offsetof(_type, _next), \ +} + /* _f : field name _f_n : num of elements field_name _n : num of elements @@ -1155,23 +1178,25 @@ int vmstate_save_state_v(QEMUFile *f, const VMStateDescription *vmsd, bool vmstate_save_needed(const VMStateDescription *vmsd, void *opaque); +#define VMSTATE_INSTANCE_ID_ANY -1 + /* Returns: 0 on success, -1 on failure */ -int vmstate_register_with_alias_id(DeviceState *dev, int instance_id, +int vmstate_register_with_alias_id(VMStateIf *obj, uint32_t instance_id, const VMStateDescription *vmsd, void *base, int alias_id, int required_for_version, Error **errp); /* Returns: 0 on success, -1 on failure */ -static inline int vmstate_register(DeviceState *dev, int instance_id, +static inline int vmstate_register(VMStateIf *obj, int instance_id, const VMStateDescription *vmsd, void *opaque) { - return vmstate_register_with_alias_id(dev, instance_id, vmsd, + return vmstate_register_with_alias_id(obj, instance_id, vmsd, opaque, -1, 0, NULL); } -void vmstate_unregister(DeviceState *dev, const VMStateDescription *vmsd, +void vmstate_unregister(VMStateIf *obj, const VMStateDescription *vmsd, void *opaque); struct MemoryRegion; diff --git a/include/monitor/hmp.h b/include/monitor/hmp.h index a0e9511440..e33ca5a911 100644 --- a/include/monitor/hmp.h +++ b/include/monitor/hmp.h @@ -16,7 +16,7 @@ #include "qemu/readline.h" -void hmp_handle_error(Monitor *mon, Error **errp); +void hmp_handle_error(Monitor *mon, Error *err); void hmp_info_name(Monitor *mon, const QDict *qdict); void hmp_info_version(Monitor *mon, const QDict *qdict); @@ -30,8 +30,6 @@ void hmp_info_migrate_capabilities(Monitor *mon, const QDict *qdict); void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict); void hmp_info_migrate_cache_size(Monitor *mon, const QDict *qdict); void hmp_info_cpus(Monitor *mon, const QDict *qdict); -void hmp_info_block(Monitor *mon, const QDict *qdict); -void hmp_info_blockstats(Monitor *mon, const QDict *qdict); void hmp_info_vnc(Monitor *mon, const QDict *qdict); void hmp_info_spice(Monitor *mon, const QDict *qdict); void hmp_info_balloon(Monitor *mon, const QDict *qdict); @@ -39,7 +37,6 @@ void hmp_info_irq(Monitor *mon, const QDict *qdict); void hmp_info_pic(Monitor *mon, const QDict *qdict); void hmp_info_rdma(Monitor *mon, const QDict *qdict); void hmp_info_pci(Monitor *mon, const QDict *qdict); -void hmp_info_block_jobs(Monitor *mon, const QDict *qdict); void hmp_info_tpm(Monitor *mon, const QDict *qdict); void hmp_info_iothreads(Monitor *mon, const QDict *qdict); void hmp_quit(Monitor *mon, const QDict *qdict); @@ -58,18 +55,10 @@ void hmp_cont(Monitor *mon, const QDict *qdict); void hmp_system_wakeup(Monitor *mon, const QDict *qdict); void hmp_nmi(Monitor *mon, const QDict *qdict); void hmp_set_link(Monitor *mon, const QDict *qdict); -void hmp_block_passwd(Monitor *mon, const QDict *qdict); void hmp_balloon(Monitor *mon, const QDict *qdict); -void hmp_block_resize(Monitor *mon, const QDict *qdict); -void hmp_snapshot_blkdev(Monitor *mon, const QDict *qdict); -void hmp_snapshot_blkdev_internal(Monitor *mon, const QDict *qdict); -void hmp_snapshot_delete_blkdev_internal(Monitor *mon, const QDict *qdict); -void hmp_drive_mirror(Monitor *mon, const QDict *qdict); -void hmp_drive_backup(Monitor *mon, const QDict *qdict); void hmp_loadvm(Monitor *mon, const QDict *qdict); void hmp_savevm(Monitor *mon, const QDict *qdict); void hmp_delvm(Monitor *mon, const QDict *qdict); -void hmp_info_snapshots(Monitor *mon, const QDict *qdict); void hmp_migrate_cancel(Monitor *mon, const QDict *qdict); void hmp_migrate_continue(Monitor *mon, const QDict *qdict); void hmp_migrate_incoming(Monitor *mon, const QDict *qdict); @@ -85,15 +74,7 @@ void hmp_migrate_start_postcopy(Monitor *mon, const QDict *qdict); void hmp_x_colo_lost_heartbeat(Monitor *mon, const QDict *qdict); void hmp_set_password(Monitor *mon, const QDict *qdict); void hmp_expire_password(Monitor *mon, const QDict *qdict); -void hmp_eject(Monitor *mon, const QDict *qdict); void hmp_change(Monitor *mon, const QDict *qdict); -void hmp_block_set_io_throttle(Monitor *mon, const QDict *qdict); -void hmp_block_stream(Monitor *mon, const QDict *qdict); -void hmp_block_job_set_speed(Monitor *mon, const QDict *qdict); -void hmp_block_job_cancel(Monitor *mon, const QDict *qdict); -void hmp_block_job_pause(Monitor *mon, const QDict *qdict); -void hmp_block_job_resume(Monitor *mon, const QDict *qdict); -void hmp_block_job_complete(Monitor *mon, const QDict *qdict); void hmp_migrate(Monitor *mon, const QDict *qdict); void hmp_device_add(Monitor *mon, const QDict *qdict); void hmp_device_del(Monitor *mon, const QDict *qdict); @@ -104,15 +85,10 @@ void hmp_getfd(Monitor *mon, const QDict *qdict); void hmp_closefd(Monitor *mon, const QDict *qdict); void hmp_sendkey(Monitor *mon, const QDict *qdict); void hmp_screendump(Monitor *mon, const QDict *qdict); -void hmp_nbd_server_start(Monitor *mon, const QDict *qdict); -void hmp_nbd_server_add(Monitor *mon, const QDict *qdict); -void hmp_nbd_server_remove(Monitor *mon, const QDict *qdict); -void hmp_nbd_server_stop(Monitor *mon, const QDict *qdict); void hmp_chardev_add(Monitor *mon, const QDict *qdict); void hmp_chardev_change(Monitor *mon, const QDict *qdict); void hmp_chardev_remove(Monitor *mon, const QDict *qdict); void hmp_chardev_send_break(Monitor *mon, const QDict *qdict); -void hmp_qemu_io(Monitor *mon, const QDict *qdict); void hmp_cpu_add(Monitor *mon, const QDict *qdict); void hmp_object_add(Monitor *mon, const QDict *qdict); void hmp_object_del(Monitor *mon, const QDict *qdict); diff --git a/include/monitor/monitor.h b/include/monitor/monitor.h index a81eeff5f8..1018d754a6 100644 --- a/include/monitor/monitor.h +++ b/include/monitor/monitor.h @@ -7,15 +7,20 @@ extern __thread Monitor *cur_mon; typedef struct MonitorHMP MonitorHMP; +typedef struct MonitorOptions MonitorOptions; #define QMP_REQ_QUEUE_LEN_MAX 8 +extern QemuOptsList qemu_mon_opts; + bool monitor_cur_is_qmp(void); void monitor_init_globals(void); void monitor_init_globals_core(void); -void monitor_init_qmp(Chardev *chr, bool pretty); -void monitor_init_hmp(Chardev *chr, bool use_readline); +void monitor_init_qmp(Chardev *chr, bool pretty, Error **errp); +void monitor_init_hmp(Chardev *chr, bool use_readline, Error **errp); +int monitor_init(MonitorOptions *opts, bool allow_hmp, Error **errp); +int monitor_init_opts(QemuOpts *opts, Error **errp); void monitor_cleanup(void); int monitor_suspend(Monitor *mon); diff --git a/include/net/can_emu.h b/include/net/can_emu.h index d4fc51b57d..fce9770928 100644 --- a/include/net/can_emu.h +++ b/include/net/can_emu.h @@ -83,7 +83,7 @@ typedef struct CanBusClientState CanBusClientState; typedef struct CanBusState CanBusState; typedef struct CanBusClientInfo { - int (*can_receive)(CanBusClientState *); + bool (*can_receive)(CanBusClientState *); ssize_t (*receive)(CanBusClientState *, const struct qemu_can_frame *frames, size_t frames_cnt); } CanBusClientInfo; diff --git a/include/net/filter.h b/include/net/filter.h index e8fb6259db..9393c59192 100644 --- a/include/net/filter.h +++ b/include/net/filter.h @@ -62,6 +62,8 @@ struct NetFilterState { NetClientState *netdev; NetFilterDirection direction; bool on; + char *position; + bool insert_before_flag; QTAILQ_ENTRY(NetFilterState) next; }; diff --git a/include/net/net.h b/include/net/net.h index e175ba9677..39085d9444 100644 --- a/include/net/net.h +++ b/include/net/net.h @@ -42,7 +42,7 @@ typedef struct NICConf { /* Net clients */ typedef void (NetPoll)(NetClientState *, bool enable); -typedef int (NetCanReceive)(NetClientState *); +typedef bool (NetCanReceive)(NetClientState *); typedef ssize_t (NetReceive)(NetClientState *, const uint8_t *, size_t); typedef ssize_t (NetReceiveIOV)(NetClientState *, const struct iovec *, int); typedef void (NetCleanup) (NetClientState *); @@ -98,6 +98,7 @@ struct NetClientState { unsigned rxfilter_notify_enabled:1; int vring_enable; int vnet_hdr_len; + bool is_netdev; QTAILQ_HEAD(, NetFilterState) filters; }; @@ -203,7 +204,6 @@ void net_cleanup(void); void hmp_host_net_add(Monitor *mon, const QDict *qdict); void hmp_host_net_remove(Monitor *mon, const QDict *qdict); void netdev_add(QemuOpts *opts, Error **errp); -void qmp_netdev_add(QDict *qdict, QObject **ret, Error **errp); int net_hub_id_for_client(NetClientState *nc, int *id); NetClientState *net_hub_port_find(int hub_id); diff --git a/include/qapi/error.h b/include/qapi/error.h index 3f95141a01..ad5b6e896d 100644 --- a/include/qapi/error.h +++ b/include/qapi/error.h @@ -233,13 +233,13 @@ void error_propagate_prepend(Error **dst_errp, Error *local_err, * Prepend some text to @errp's human-readable error message. * The text is made by formatting @fmt, @ap like vprintf(). */ -void error_vprepend(Error **errp, const char *fmt, va_list ap); +void error_vprepend(Error *const *errp, const char *fmt, va_list ap); /* * Prepend some text to @errp's human-readable error message. * The text is made by formatting @fmt, ... like printf(). */ -void error_prepend(Error **errp, const char *fmt, ...) +void error_prepend(Error *const *errp, const char *fmt, ...) GCC_FMT_ATTR(2, 3); /* @@ -256,7 +256,7 @@ void error_prepend(Error **errp, const char *fmt, ...) * May be called multiple times. The resulting hint should end with a * newline. */ -void error_append_hint(Error **errp, const char *fmt, ...) +void error_append_hint(Error *const *errp, const char *fmt, ...) GCC_FMT_ATTR(2, 3); /* diff --git a/include/qapi/qmp/dispatch.h b/include/qapi/qmp/dispatch.h index 9aa426a398..5a9cf82472 100644 --- a/include/qapi/qmp/dispatch.h +++ b/include/qapi/qmp/dispatch.h @@ -39,7 +39,8 @@ typedef QTAILQ_HEAD(QmpCommandList, QmpCommand) QmpCommandList; void qmp_register_command(QmpCommandList *cmds, const char *name, QmpCommandFunc *fn, QmpCommandOptions options); -QmpCommand *qmp_find_command(QmpCommandList *cmds, const char *name); +const QmpCommand *qmp_find_command(const QmpCommandList *cmds, + const char *name); void qmp_disable_command(QmpCommandList *cmds, const char *name); void qmp_enable_command(QmpCommandList *cmds, const char *name); @@ -47,13 +48,13 @@ bool qmp_command_is_enabled(const QmpCommand *cmd); const char *qmp_command_name(const QmpCommand *cmd); bool qmp_has_success_response(const QmpCommand *cmd); QDict *qmp_error_response(Error *err); -QDict *qmp_dispatch(QmpCommandList *cmds, QObject *request, +QDict *qmp_dispatch(const QmpCommandList *cmds, QObject *request, bool allow_oob); bool qmp_is_oob(const QDict *dict); -typedef void (*qmp_cmd_callback_fn)(QmpCommand *cmd, void *opaque); +typedef void (*qmp_cmd_callback_fn)(const QmpCommand *cmd, void *opaque); -void qmp_for_each_command(QmpCommandList *cmds, qmp_cmd_callback_fn fn, +void qmp_for_each_command(const QmpCommandList *cmds, qmp_cmd_callback_fn fn, void *opaque); #endif diff --git a/include/qapi/qmp/qstring.h b/include/qapi/qmp/qstring.h index 3e83e3a95d..e2e356e5e7 100644 --- a/include/qapi/qmp/qstring.h +++ b/include/qapi/qmp/qstring.h @@ -33,6 +33,7 @@ void qstring_append_int(QString *qstring, int64_t value); void qstring_append(QString *qstring, const char *str); void qstring_append_chr(QString *qstring, int c); bool qstring_is_equal(const QObject *x, const QObject *y); +char *qstring_free(QString *qstring, bool return_str); void qstring_destroy_obj(QObject *obj); #endif /* QSTRING_H */ diff --git a/include/qemu-common.h b/include/qemu-common.h index 082da59e85..d0142f29ac 100644 --- a/include/qemu-common.h +++ b/include/qemu-common.h @@ -13,7 +13,7 @@ #define TFR(expr) do { if ((expr) != -1) break; } while (errno == EINTR) /* Copyright string for -version arguments, About dialogs, etc */ -#define QEMU_COPYRIGHT "Copyright (c) 2003-2019 " \ +#define QEMU_COPYRIGHT "Copyright (c) 2003-2020 " \ "Fabrice Bellard and the QEMU Project developers" /* Bug reporting information for --help arguments, About dialogs, etc */ diff --git a/include/qemu/atomic.h b/include/qemu/atomic.h index f9cd24c899..ff72db5115 100644 --- a/include/qemu/atomic.h +++ b/include/qemu/atomic.h @@ -208,11 +208,14 @@ /* Provide shorter names for GCC atomic builtins, return old value */ #define atomic_fetch_inc(ptr) __atomic_fetch_add(ptr, 1, __ATOMIC_SEQ_CST) #define atomic_fetch_dec(ptr) __atomic_fetch_sub(ptr, 1, __ATOMIC_SEQ_CST) + +#ifndef atomic_fetch_add #define atomic_fetch_add(ptr, n) __atomic_fetch_add(ptr, n, __ATOMIC_SEQ_CST) #define atomic_fetch_sub(ptr, n) __atomic_fetch_sub(ptr, n, __ATOMIC_SEQ_CST) #define atomic_fetch_and(ptr, n) __atomic_fetch_and(ptr, n, __ATOMIC_SEQ_CST) #define atomic_fetch_or(ptr, n) __atomic_fetch_or(ptr, n, __ATOMIC_SEQ_CST) #define atomic_fetch_xor(ptr, n) __atomic_fetch_xor(ptr, n, __ATOMIC_SEQ_CST) +#endif #define atomic_inc_fetch(ptr) __atomic_add_fetch(ptr, 1, __ATOMIC_SEQ_CST) #define atomic_dec_fetch(ptr) __atomic_sub_fetch(ptr, 1, __ATOMIC_SEQ_CST) @@ -392,11 +395,14 @@ /* Provide shorter names for GCC atomic builtins. */ #define atomic_fetch_inc(ptr) __sync_fetch_and_add(ptr, 1) #define atomic_fetch_dec(ptr) __sync_fetch_and_add(ptr, -1) + +#ifndef atomic_fetch_add #define atomic_fetch_add(ptr, n) __sync_fetch_and_add(ptr, n) #define atomic_fetch_sub(ptr, n) __sync_fetch_and_sub(ptr, n) #define atomic_fetch_and(ptr, n) __sync_fetch_and_and(ptr, n) #define atomic_fetch_or(ptr, n) __sync_fetch_and_or(ptr, n) #define atomic_fetch_xor(ptr, n) __sync_fetch_and_xor(ptr, n) +#endif #define atomic_inc_fetch(ptr) __sync_add_and_fetch(ptr, 1) #define atomic_dec_fetch(ptr) __sync_add_and_fetch(ptr, -1) diff --git a/include/qemu/bitops.h b/include/qemu/bitops.h index ee76552c06..f55ce8b320 100644 --- a/include/qemu/bitops.h +++ b/include/qemu/bitops.h @@ -301,6 +301,44 @@ static inline uint32_t extract32(uint32_t value, int start, int length) return (value >> start) & (~0U >> (32 - length)); } +/** + * extract8: + * @value: the value to extract the bit field from + * @start: the lowest bit in the bit field (numbered from 0) + * @length: the length of the bit field + * + * Extract from the 8 bit input @value the bit field specified by the + * @start and @length parameters, and return it. The bit field must + * lie entirely within the 8 bit word. It is valid to request that + * all 8 bits are returned (ie @length 8 and @start 0). + * + * Returns: the value of the bit field extracted from the input value. + */ +static inline uint8_t extract8(uint8_t value, int start, int length) +{ + assert(start >= 0 && length > 0 && length <= 8 - start); + return extract32(value, start, length); +} + +/** + * extract16: + * @value: the value to extract the bit field from + * @start: the lowest bit in the bit field (numbered from 0) + * @length: the length of the bit field + * + * Extract from the 16 bit input @value the bit field specified by the + * @start and @length parameters, and return it. The bit field must + * lie entirely within the 16 bit word. It is valid to request that + * all 16 bits are returned (ie @length 16 and @start 0). + * + * Returns: the value of the bit field extracted from the input value. + */ +static inline uint16_t extract16(uint16_t value, int start, int length) +{ + assert(start >= 0 && length > 0 && length <= 16 - start); + return extract32(value, start, length); +} + /** * extract64: * @value: the value to extract the bit field from @@ -424,13 +462,16 @@ static inline uint64_t deposit64(uint64_t value, int start, int length, /** * half_shuffle32: - * @value: 32-bit value (of which only the bottom 16 bits are of interest) + * @x: 32-bit value (of which only the bottom 16 bits are of interest) + * + * Given an input value:: + * + * xxxx xxxx xxxx xxxx ABCD EFGH IJKL MNOP * - * Given an input value: - * xxxx xxxx xxxx xxxx ABCD EFGH IJKL MNOP * return the value where the bottom 16 bits are spread out into - * the odd bits in the word, and the even bits are zeroed: - * 0A0B 0C0D 0E0F 0G0H 0I0J 0K0L 0M0N 0O0P + * the odd bits in the word, and the even bits are zeroed:: + * + * 0A0B 0C0D 0E0F 0G0H 0I0J 0K0L 0M0N 0O0P * * Any bits set in the top half of the input are ignored. * @@ -450,13 +491,16 @@ static inline uint32_t half_shuffle32(uint32_t x) /** * half_shuffle64: - * @value: 64-bit value (of which only the bottom 32 bits are of interest) + * @x: 64-bit value (of which only the bottom 32 bits are of interest) + * + * Given an input value:: + * + * xxxx xxxx xxxx .... xxxx xxxx ABCD EFGH IJKL MNOP QRST UVWX YZab cdef * - * Given an input value: - * xxxx xxxx xxxx .... xxxx xxxx ABCD EFGH IJKL MNOP QRST UVWX YZab cdef * return the value where the bottom 32 bits are spread out into - * the odd bits in the word, and the even bits are zeroed: - * 0A0B 0C0D 0E0F 0G0H 0I0J 0K0L 0M0N .... 0U0V 0W0X 0Y0Z 0a0b 0c0d 0e0f + * the odd bits in the word, and the even bits are zeroed:: + * + * 0A0B 0C0D 0E0F 0G0H 0I0J 0K0L 0M0N .... 0U0V 0W0X 0Y0Z 0a0b 0c0d 0e0f * * Any bits set in the top half of the input are ignored. * @@ -477,13 +521,16 @@ static inline uint64_t half_shuffle64(uint64_t x) /** * half_unshuffle32: - * @value: 32-bit value (of which only the odd bits are of interest) + * @x: 32-bit value (of which only the odd bits are of interest) + * + * Given an input value:: + * + * xAxB xCxD xExF xGxH xIxJ xKxL xMxN xOxP * - * Given an input value: - * xAxB xCxD xExF xGxH xIxJ xKxL xMxN xOxP * return the value where all the odd bits are compressed down - * into the low half of the word, and the high half is zeroed: - * 0000 0000 0000 0000 ABCD EFGH IJKL MNOP + * into the low half of the word, and the high half is zeroed:: + * + * 0000 0000 0000 0000 ABCD EFGH IJKL MNOP * * Any even bits set in the input are ignored. * @@ -504,13 +551,16 @@ static inline uint32_t half_unshuffle32(uint32_t x) /** * half_unshuffle64: - * @value: 64-bit value (of which only the odd bits are of interest) + * @x: 64-bit value (of which only the odd bits are of interest) + * + * Given an input value:: + * + * xAxB xCxD xExF xGxH xIxJ xKxL xMxN .... xUxV xWxX xYxZ xaxb xcxd xexf * - * Given an input value: - * xAxB xCxD xExF xGxH xIxJ xKxL xMxN .... xUxV xWxX xYxZ xaxb xcxd xexf * return the value where all the odd bits are compressed down - * into the low half of the word, and the high half is zeroed: - * 0000 0000 0000 .... 0000 0000 ABCD EFGH IJKL MNOP QRST UVWX YZab cdef + * into the low half of the word, and the high half is zeroed:: + * + * 0000 0000 0000 .... 0000 0000 ABCD EFGH IJKL MNOP QRST UVWX YZab cdef * * Any even bits set in the input are ignored. * diff --git a/include/qemu/compiler.h b/include/qemu/compiler.h index 85c02c16d3..c76281f354 100644 --- a/include/qemu/compiler.h +++ b/include/qemu/compiler.h @@ -236,7 +236,7 @@ * supports QEMU_ERROR, this will be reported at compile time; otherwise * this will be reported at link time due to the missing symbol. */ -#ifdef __OPTIMIZE__ +#if defined(__OPTIMIZE__) && !defined(__NO_INLINE__) extern void QEMU_NORETURN QEMU_ERROR("code path is reachable") qemu_build_not_reached(void); #else diff --git a/include/qemu/cpuid.h b/include/qemu/cpuid.h index 69301700bd..09fc245b91 100644 --- a/include/qemu/cpuid.h +++ b/include/qemu/cpuid.h @@ -45,6 +45,9 @@ #ifndef bit_AVX2 #define bit_AVX2 (1 << 5) #endif +#ifndef bit_AVX512F +#define bit_AVX512F (1 << 16) +#endif #ifndef bit_BMI2 #define bit_BMI2 (1 << 8) #endif diff --git a/include/qemu/cutils.h b/include/qemu/cutils.h index b54c847e0f..eb59852dfd 100644 --- a/include/qemu/cutils.h +++ b/include/qemu/cutils.h @@ -130,6 +130,7 @@ const char *qemu_strchrnul(const char *s, int c); #endif time_t mktimegm(struct tm *tm); int qemu_fdatasync(int fd); +int qemu_msync(void *addr, size_t length, int fd); int fcntl_setfl(int fd, int flag); int qemu_parse_fd(const char *param); int qemu_strtoi(const char *nptr, const char **endptr, int base, diff --git a/include/qemu/dbus.h b/include/qemu/dbus.h new file mode 100644 index 0000000000..9d591f9ee4 --- /dev/null +++ b/include/qemu/dbus.h @@ -0,0 +1,19 @@ +/* + * Helpers for using D-Bus + * + * Copyright (C) 2019 Red Hat, Inc. + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + */ + +#ifndef DBUS_H +#define DBUS_H + +#include + +GStrv qemu_dbus_get_queued_owners(GDBusConnection *connection, + const char *name, + Error **errp); + +#endif /* DBUS_H */ diff --git a/include/qemu/error-report.h b/include/qemu/error-report.h index 00d069b20f..87532d8596 100644 --- a/include/qemu/error-report.h +++ b/include/qemu/error-report.h @@ -73,6 +73,7 @@ void error_init(const char *argv0); }) const char *error_get_progname(void); -extern bool enable_timestamp_msg; + +extern bool error_with_timestamp; #endif diff --git a/include/qemu/hbitmap.h b/include/qemu/hbitmap.h index 1bf944ca3d..5e71b6d6f7 100644 --- a/include/qemu/hbitmap.h +++ b/include/qemu/hbitmap.h @@ -297,12 +297,18 @@ void hbitmap_free(HBitmap *hb); */ void hbitmap_iter_init(HBitmapIter *hbi, const HBitmap *hb, uint64_t first); -/* hbitmap_iter_skip_words: - * @hbi: HBitmapIter to operate on. +/* + * hbitmap_next_dirty: * - * Internal function used by hbitmap_iter_next and hbitmap_iter_next_word. + * Find next dirty bit within selected range. If not found, return -1. + * + * @hb: The HBitmap to operate on + * @start: The bit to start from. + * @count: Number of bits to proceed. If @start+@count > bitmap size, the whole + * bitmap is looked through. You can use INT64_MAX as @count to search up to + * the bitmap end. */ -unsigned long hbitmap_iter_skip_words(HBitmapIter *hbi); +int64_t hbitmap_next_dirty(const HBitmap *hb, int64_t start, int64_t count); /* hbitmap_next_zero: * @@ -311,47 +317,28 @@ unsigned long hbitmap_iter_skip_words(HBitmapIter *hbi); * @hb: The HBitmap to operate on * @start: The bit to start from. * @count: Number of bits to proceed. If @start+@count > bitmap size, the whole - * bitmap is looked through. You can use UINT64_MAX as @count to search up to + * bitmap is looked through. You can use INT64_MAX as @count to search up to * the bitmap end. */ -int64_t hbitmap_next_zero(const HBitmap *hb, uint64_t start, uint64_t count); +int64_t hbitmap_next_zero(const HBitmap *hb, int64_t start, int64_t count); /* hbitmap_next_dirty_area: * @hb: The HBitmap to operate on - * @start: in-out parameter. - * in: the offset to start from - * out: (if area found) start of found area - * @count: in-out parameter. - * in: length of requested region - * out: length of found area + * @start: the offset to start from + * @end: end of requested area + * @max_dirty_count: limit for out parameter dirty_count + * @dirty_start: on success: start of found area + * @dirty_count: on success: length of found area * - * If dirty area found within [@start, @start + @count), returns true and sets - * @offset and @bytes appropriately. Otherwise returns false and leaves @offset - * and @bytes unchanged. + * If dirty area found within [@start, @end), returns true and sets + * @dirty_start and @dirty_count appropriately. @dirty_count will not exceed + * @max_dirty_count. + * If dirty area was not found, returns false and leaves @dirty_start and + * @dirty_count unchanged. */ -bool hbitmap_next_dirty_area(const HBitmap *hb, uint64_t *start, - uint64_t *count); - -/* hbitmap_create_meta: - * Create a "meta" hbitmap to track dirtiness of the bits in this HBitmap. - * The caller owns the created bitmap and must call hbitmap_free_meta(hb) to - * free it. - * - * Currently, we only guarantee that if a bit in the hbitmap is changed it - * will be reflected in the meta bitmap, but we do not yet guarantee the - * opposite. - * - * @hb: The HBitmap to operate on. - * @chunk_size: How many bits in @hb does one bit in the meta track. - */ -HBitmap *hbitmap_create_meta(HBitmap *hb, int chunk_size); - -/* hbitmap_free_meta: - * Free the meta bitmap of @hb. - * - * @hb: The HBitmap whose meta bitmap should be freed. - */ -void hbitmap_free_meta(HBitmap *hb); +bool hbitmap_next_dirty_area(const HBitmap *hb, int64_t start, int64_t end, + int64_t max_dirty_count, + int64_t *dirty_start, int64_t *dirty_count); /** * hbitmap_iter_next: @@ -362,34 +349,4 @@ void hbitmap_free_meta(HBitmap *hb); */ int64_t hbitmap_iter_next(HBitmapIter *hbi); -/** - * hbitmap_iter_next_word: - * @hbi: HBitmapIter to operate on. - * @p_cur: Location where to store the next non-zero word. - * - * Return the index of the next nonzero word that is set in @hbi's - * associated HBitmap, and set *p_cur to the content of that word - * (bits before the index that was passed to hbitmap_iter_init are - * trimmed on the first call). Return -1, and set *p_cur to zero, - * if all remaining words are zero. - */ -static inline size_t hbitmap_iter_next_word(HBitmapIter *hbi, unsigned long *p_cur) -{ - unsigned long cur = hbi->cur[HBITMAP_LEVELS - 1]; - - if (cur == 0) { - cur = hbitmap_iter_skip_words(hbi); - if (cur == 0) { - *p_cur = 0; - return -1; - } - } - - /* The next call will resume work from the next word. */ - hbi->cur[HBITMAP_LEVELS - 1] = 0; - *p_cur = cur; - return hbi->pos; -} - - #endif diff --git a/include/qemu/id.h b/include/qemu/id.h index 40c70103e4..b55c406e69 100644 --- a/include/qemu/id.h +++ b/include/qemu/id.h @@ -4,6 +4,7 @@ typedef enum IdSubSystems { ID_QDEV, ID_BLOCK, + ID_CHR, ID_MAX /* last element, used as array size */ } IdSubSystems; diff --git a/include/qemu/job.h b/include/qemu/job.h index bd59cd8944..32aabb1c60 100644 --- a/include/qemu/job.h +++ b/include/qemu/job.h @@ -28,6 +28,7 @@ #include "qapi/qapi-types-job.h" #include "qemu/queue.h" +#include "qemu/progress_meter.h" #include "qemu/coroutine.h" #include "block/aio.h" @@ -117,15 +118,7 @@ typedef struct Job { /** True if this job should automatically dismiss itself */ bool auto_dismiss; - /** - * Current progress. The unit is arbitrary as long as the ratio between - * progress_current and progress_total represents the estimated percentage - * of work already done. - */ - int64_t progress_current; - - /** Estimated progress_current value at the completion of the job */ - int64_t progress_total; + ProgressMeter progress; /** * Return code from @run and/or @prepare callback(s). diff --git a/include/qemu/lockable.h b/include/qemu/lockable.h index 84ea794bcf..1aeb2cb1a6 100644 --- a/include/qemu/lockable.h +++ b/include/qemu/lockable.h @@ -50,6 +50,7 @@ qemu_make_lockable(void *x, QemuLockable *lockable) #define QEMU_LOCK_FUNC(x) ((QemuLockUnlockFunc *) \ QEMU_GENERIC(x, \ (QemuMutex *, qemu_mutex_lock), \ + (QemuRecMutex *, qemu_rec_mutex_lock), \ (CoMutex *, qemu_co_mutex_lock), \ (QemuSpin *, qemu_spin_lock), \ unknown_lock_type)) @@ -57,6 +58,7 @@ qemu_make_lockable(void *x, QemuLockable *lockable) #define QEMU_UNLOCK_FUNC(x) ((QemuLockUnlockFunc *) \ QEMU_GENERIC(x, \ (QemuMutex *, qemu_mutex_unlock), \ + (QemuRecMutex *, qemu_rec_mutex_unlock), \ (CoMutex *, qemu_co_mutex_unlock), \ (QemuSpin *, qemu_spin_unlock), \ unknown_lock_type)) @@ -65,7 +67,7 @@ qemu_make_lockable(void *x, QemuLockable *lockable) * In C++ it would be different, but then C++ wouldn't need QemuLockable * either... */ -#define QEMU_MAKE_LOCKABLE_(x) qemu_make_lockable((x), &(QemuLockable) { \ +#define QEMU_MAKE_LOCKABLE_(x) (&(QemuLockable) { \ .object = (x), \ .lock = QEMU_LOCK_FUNC(x), \ .unlock = QEMU_UNLOCK_FUNC(x), \ @@ -73,12 +75,25 @@ qemu_make_lockable(void *x, QemuLockable *lockable) /* QEMU_MAKE_LOCKABLE - Make a polymorphic QemuLockable * - * @x: a lock object (currently one of QemuMutex, CoMutex, QemuSpin). + * @x: a lock object (currently one of QemuMutex, QemuRecMutex, CoMutex, QemuSpin). + * + * Returns a QemuLockable object that can be passed around + * to a function that can operate with locks of any kind, or + * NULL if @x is %NULL. + */ +#define QEMU_MAKE_LOCKABLE(x) \ + QEMU_GENERIC(x, \ + (QemuLockable *, (x)), \ + qemu_make_lockable((x), QEMU_MAKE_LOCKABLE_(x))) + +/* QEMU_MAKE_LOCKABLE_NONNULL - Make a polymorphic QemuLockable + * + * @x: a lock object (currently one of QemuMutex, QemuRecMutex, CoMutex, QemuSpin). * * Returns a QemuLockable object that can be passed around * to a function that can operate with locks of any kind. */ -#define QEMU_MAKE_LOCKABLE(x) \ +#define QEMU_MAKE_LOCKABLE_NONNULL(x) \ QEMU_GENERIC(x, \ (QemuLockable *, (x)), \ QEMU_MAKE_LOCKABLE_(x)) @@ -93,4 +108,69 @@ static inline void qemu_lockable_unlock(QemuLockable *x) x->unlock(x->object); } +static inline QemuLockable *qemu_lockable_auto_lock(QemuLockable *x) +{ + qemu_lockable_lock(x); + return x; +} + +static inline void qemu_lockable_auto_unlock(QemuLockable *x) +{ + if (x) { + qemu_lockable_unlock(x); + } +} + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(QemuLockable, qemu_lockable_auto_unlock) + +#define WITH_QEMU_LOCK_GUARD_(x, var) \ + for (g_autoptr(QemuLockable) var = \ + qemu_lockable_auto_lock(QEMU_MAKE_LOCKABLE_NONNULL((x))); \ + var; \ + qemu_lockable_auto_unlock(var), var = NULL) + +/** + * WITH_QEMU_LOCK_GUARD - Lock a lock object for scope + * + * @x: a lock object (currently one of QemuMutex, CoMutex, QemuSpin). + * + * This macro defines a lock scope such that entering the scope takes the lock + * and leaving the scope releases the lock. Return statements are allowed + * within the scope and release the lock. Break and continue statements leave + * the scope early and release the lock. + * + * WITH_QEMU_LOCK_GUARD(&mutex) { + * ... + * if (error) { + * return; <-- mutex is automatically unlocked + * } + * + * if (early_exit) { + * break; <-- leave this scope early + * } + * ... + * } + */ +#define WITH_QEMU_LOCK_GUARD(x) \ + WITH_QEMU_LOCK_GUARD_((x), qemu_lockable_auto##__COUNTER__) + +/** + * QEMU_LOCK_GUARD - Lock an object until the end of the scope + * + * @x: a lock object (currently one of QemuMutex, CoMutex, QemuSpin). + * + * This macro takes a lock until the end of the scope. Return statements + * release the lock. + * + * ... <-- mutex not locked + * QEMU_LOCK_GUARD(&mutex); <-- mutex locked from here onwards + * ... + * if (error) { + * return; <-- mutex is automatically unlocked + * } + */ +#define QEMU_LOCK_GUARD(x) \ + g_autoptr(QemuLockable) qemu_lockable_auto##__COUNTER__ = \ + qemu_lockable_auto_lock(QEMU_MAKE_LOCKABLE((x))) + #endif diff --git a/include/qemu/log.h b/include/qemu/log.h index a91105b2ad..f4724f7330 100644 --- a/include/qemu/log.h +++ b/include/qemu/log.h @@ -3,9 +3,16 @@ /* A small part of this API is split into its own header */ #include "qemu/log-for-trace.h" +#include "qemu/rcu.h" + +typedef struct QemuLogFile { + struct rcu_head rcu; + FILE *fd; +} QemuLogFile; /* Private global variable, don't use */ -extern FILE *qemu_logfile; +extern QemuLogFile *qemu_logfile; + /* * The new API: @@ -25,7 +32,16 @@ static inline bool qemu_log_enabled(void) */ static inline bool qemu_log_separate(void) { - return qemu_logfile != NULL && qemu_logfile != stderr; + QemuLogFile *logfile; + bool res = false; + + rcu_read_lock(); + logfile = atomic_rcu_read(&qemu_logfile); + if (logfile && logfile->fd != stderr) { + res = true; + } + rcu_read_unlock(); + return res; } #define CPU_LOG_TB_OUT_ASM (1 << 0) @@ -46,6 +62,8 @@ static inline bool qemu_log_separate(void) #define CPU_LOG_TB_OP_IND (1 << 16) #define CPU_LOG_TB_FPU (1 << 17) #define CPU_LOG_PLUGIN (1 << 18) +/* LOG_STRACE is used for user-mode strace logging. */ +#define LOG_STRACE (1 << 19) /* Lock output for a series of related logs. Since this is not needed * for a single qemu_log / qemu_log_mask / qemu_log_mask_and_addr, we @@ -53,14 +71,25 @@ static inline bool qemu_log_separate(void) * qemu_loglevel is never set when qemu_logfile is unset. */ -static inline void qemu_log_lock(void) +static inline FILE *qemu_log_lock(void) { - qemu_flockfile(qemu_logfile); + QemuLogFile *logfile; + rcu_read_lock(); + logfile = atomic_rcu_read(&qemu_logfile); + if (logfile) { + qemu_flockfile(logfile->fd); + return logfile->fd; + } else { + return NULL; + } } -static inline void qemu_log_unlock(void) +static inline void qemu_log_unlock(FILE *fd) { - qemu_funlockfile(qemu_logfile); + if (fd) { + qemu_funlockfile(fd); + } + rcu_read_unlock(); } /* Logging functions: */ @@ -70,9 +99,14 @@ static inline void qemu_log_unlock(void) static inline void GCC_FMT_ATTR(1, 0) qemu_log_vprintf(const char *fmt, va_list va) { - if (qemu_logfile) { - vfprintf(qemu_logfile, fmt, va); + QemuLogFile *logfile; + + rcu_read_lock(); + logfile = atomic_rcu_read(&qemu_logfile); + if (logfile) { + vfprintf(logfile->fd, fmt, va); } + rcu_read_unlock(); } /* log only if a bit is set on the current loglevel mask: diff --git a/include/qemu/main-loop.h b/include/qemu/main-loop.h index f6ba78ea73..a6d20b0719 100644 --- a/include/qemu/main-loop.h +++ b/include/qemu/main-loop.h @@ -295,6 +295,14 @@ void qemu_mutex_lock_iothread_impl(const char *file, int line); */ void qemu_mutex_unlock_iothread(void); +/* + * qemu_cond_wait_iothread: Wait on condition for the main loop mutex + * + * This function atomically releases the main loop mutex and causes + * the calling thread to block on the condition. + */ +void qemu_cond_wait_iothread(QemuCond *cond); + /* internal interfaces */ void qemu_fd_register(int fd); diff --git a/include/qemu/module.h b/include/qemu/module.h index 65ba596e46..011ae1ae76 100644 --- a/include/qemu/module.h +++ b/include/qemu/module.h @@ -40,12 +40,14 @@ static void __attribute__((constructor)) do_qemu_init_ ## function(void) \ #endif typedef enum { + MODULE_INIT_MIGRATION, MODULE_INIT_BLOCK, MODULE_INIT_OPTS, MODULE_INIT_QOM, MODULE_INIT_TRACE, MODULE_INIT_XEN_BACKEND, MODULE_INIT_LIBQOS, + MODULE_INIT_FUZZ_TARGET, MODULE_INIT_MAX } module_init_type; @@ -56,7 +58,9 @@ typedef enum { #define xen_backend_init(function) module_init(function, \ MODULE_INIT_XEN_BACKEND) #define libqos_init(function) module_init(function, MODULE_INIT_LIBQOS) - +#define fuzz_target_init(function) module_init(function, \ + MODULE_INIT_FUZZ_TARGET) +#define migration_init(function) module_init(function, MODULE_INIT_MIGRATION) #define block_module_load_one(lib) module_load_one("block-", lib) #define ui_module_load_one(lib) module_load_one("ui-", lib) #define audio_module_load_one(lib) module_load_one("audio-", lib) diff --git a/include/qemu/nvdimm-utils.h b/include/qemu/nvdimm-utils.h new file mode 100644 index 0000000000..4b8b198ba7 --- /dev/null +++ b/include/qemu/nvdimm-utils.h @@ -0,0 +1,7 @@ +#ifndef NVDIMM_UTILS_H +#define NVDIMM_UTILS_H + +#include "qemu/osdep.h" + +GSList *nvdimm_get_device_list(void); +#endif diff --git a/include/qemu/osdep.h b/include/qemu/osdep.h index 0f97d68586..20f5c5f197 100644 --- a/include/qemu/osdep.h +++ b/include/qemu/osdep.h @@ -33,20 +33,6 @@ #else #include "exec/poison.h" #endif -#ifdef __COVERITY__ -/* Coverity does not like the new _Float* types that are used by - * recent glibc, and croaks on every single file that includes - * stdlib.h. These typedefs are enough to please it. - * - * Note that these fix parse errors so they cannot be placed in - * scripts/coverity-model.c. - */ -typedef float _Float32; -typedef double _Float32x; -typedef double _Float64; -typedef __float80 _Float64x; -typedef __float128 _Float128; -#endif #include "qemu/compiler.h" @@ -462,6 +448,7 @@ int qemu_mprotect_none(void *addr, size_t size); int qemu_open(const char *name, int flags, ...); int qemu_close(int fd); +int qemu_unlink(const char *name); #ifndef _WIN32 int qemu_dup(int fd); #endif diff --git a/include/qemu/progress_meter.h b/include/qemu/progress_meter.h new file mode 100644 index 0000000000..9a23ff071c --- /dev/null +++ b/include/qemu/progress_meter.h @@ -0,0 +1,58 @@ +/* + * Helper functionality for some process progress tracking. + * + * Copyright (c) 2011 IBM Corp. + * Copyright (c) 2012, 2018 Red Hat, Inc. + * Copyright (c) 2020 Virtuozzo International GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef QEMU_PROGRESS_METER_H +#define QEMU_PROGRESS_METER_H + +typedef struct ProgressMeter { + /** + * Current progress. The unit is arbitrary as long as the ratio between + * current and total represents the estimated percentage + * of work already done. + */ + uint64_t current; + + /** Estimated current value at the completion of the process */ + uint64_t total; +} ProgressMeter; + +static inline void progress_work_done(ProgressMeter *pm, uint64_t done) +{ + pm->current += done; +} + +static inline void progress_set_remaining(ProgressMeter *pm, uint64_t remaining) +{ + pm->total = pm->current + remaining; +} + +static inline void progress_increase_remaining(ProgressMeter *pm, + uint64_t delta) +{ + pm->total += delta; +} + +#endif /* QEMU_PROGRESS_METER_H */ diff --git a/include/qemu/queue.h b/include/qemu/queue.h index 4764d93ea3..456a5b01ee 100644 --- a/include/qemu/queue.h +++ b/include/qemu/queue.h @@ -142,8 +142,27 @@ struct { \ (elm)->field.le_next->field.le_prev = \ (elm)->field.le_prev; \ *(elm)->field.le_prev = (elm)->field.le_next; \ + (elm)->field.le_next = NULL; \ + (elm)->field.le_prev = NULL; \ } while (/*CONSTCOND*/0) +/* + * Like QLIST_REMOVE() but safe to call when elm is not in a list + */ +#define QLIST_SAFE_REMOVE(elm, field) do { \ + if ((elm)->field.le_prev != NULL) { \ + if ((elm)->field.le_next != NULL) \ + (elm)->field.le_next->field.le_prev = \ + (elm)->field.le_prev; \ + *(elm)->field.le_prev = (elm)->field.le_next; \ + (elm)->field.le_next = NULL; \ + (elm)->field.le_prev = NULL; \ + } \ +} while (/*CONSTCOND*/0) + +/* Is elm in a list? */ +#define QLIST_IS_INSERTED(elm, field) ((elm)->field.le_prev != NULL) + #define QLIST_FOREACH(var, head, field) \ for ((var) = ((head)->lh_first); \ (var); \ @@ -208,12 +227,27 @@ struct { \ } while (/*CONSTCOND*/0) #define QSLIST_REMOVE_HEAD(head, field) do { \ - (head)->slh_first = (head)->slh_first->field.sle_next; \ + typeof((head)->slh_first) elm = (head)->slh_first; \ + (head)->slh_first = elm->field.sle_next; \ + elm->field.sle_next = NULL; \ } while (/*CONSTCOND*/0) -#define QSLIST_REMOVE_AFTER(slistelm, field) do { \ - (slistelm)->field.sle_next = \ - QSLIST_NEXT(QSLIST_NEXT((slistelm), field), field); \ +#define QSLIST_REMOVE_AFTER(slistelm, field) do { \ + typeof(slistelm) next = (slistelm)->field.sle_next; \ + (slistelm)->field.sle_next = next->field.sle_next; \ + next->field.sle_next = NULL; \ +} while (/*CONSTCOND*/0) + +#define QSLIST_REMOVE(head, elm, type, field) do { \ + if ((head)->slh_first == (elm)) { \ + QSLIST_REMOVE_HEAD((head), field); \ + } else { \ + struct type *curelm = (head)->slh_first; \ + while (curelm->field.sle_next != (elm)) \ + curelm = curelm->field.sle_next; \ + curelm->field.sle_next = curelm->field.sle_next->field.sle_next; \ + (elm)->field.sle_next = NULL; \ + } \ } while (/*CONSTCOND*/0) #define QSLIST_FOREACH(var, head, field) \ @@ -276,8 +310,10 @@ struct { \ } while (/*CONSTCOND*/0) #define QSIMPLEQ_REMOVE_HEAD(head, field) do { \ - if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL)\ + typeof((head)->sqh_first) elm = (head)->sqh_first; \ + if (((head)->sqh_first = elm->field.sqe_next) == NULL) \ (head)->sqh_last = &(head)->sqh_first; \ + elm->field.sqe_next = NULL; \ } while (/*CONSTCOND*/0) #define QSIMPLEQ_SPLIT_AFTER(head, elm, field, removed) do { \ @@ -301,6 +337,7 @@ struct { \ if ((curelm->field.sqe_next = \ curelm->field.sqe_next->field.sqe_next) == NULL) \ (head)->sqh_last = &(curelm)->field.sqe_next; \ + (elm)->field.sqe_next = NULL; \ } \ } while (/*CONSTCOND*/0) @@ -418,6 +455,8 @@ union { \ (head)->tqh_circ.tql_prev = (elm)->field.tqe_circ.tql_prev; \ (elm)->field.tqe_circ.tql_prev->tql_next = (elm)->field.tqe_next; \ (elm)->field.tqe_circ.tql_prev = NULL; \ + (elm)->field.tqe_circ.tql_next = NULL; \ + (elm)->field.tqe_next = NULL; \ } while (/*CONSTCOND*/0) /* remove @left, @right and all elements in between from @head */ @@ -501,4 +540,36 @@ union { \ QTAILQ_RAW_TQH_CIRC(head)->tql_prev = QTAILQ_RAW_TQE_CIRC(elm, entry); \ } while (/*CONSTCOND*/0) +#define QLIST_RAW_FIRST(head) \ + field_at_offset(head, 0, void *) + +#define QLIST_RAW_NEXT(elm, entry) \ + field_at_offset(elm, entry, void *) + +#define QLIST_RAW_PREVIOUS(elm, entry) \ + field_at_offset(elm, entry + sizeof(void *), void *) + +#define QLIST_RAW_FOREACH(elm, head, entry) \ + for ((elm) = *QLIST_RAW_FIRST(head); \ + (elm); \ + (elm) = *QLIST_RAW_NEXT(elm, entry)) + +#define QLIST_RAW_INSERT_AFTER(head, prev, elem, entry) do { \ + *QLIST_RAW_NEXT(prev, entry) = elem; \ + *QLIST_RAW_PREVIOUS(elem, entry) = QLIST_RAW_NEXT(prev, entry); \ + *QLIST_RAW_NEXT(elem, entry) = NULL; \ +} while (0) + +#define QLIST_RAW_INSERT_HEAD(head, elm, entry) do { \ + void *first = *QLIST_RAW_FIRST(head); \ + *QLIST_RAW_FIRST(head) = elm; \ + *QLIST_RAW_PREVIOUS(elm, entry) = QLIST_RAW_FIRST(head); \ + if (first) { \ + *QLIST_RAW_NEXT(elm, entry) = first; \ + *QLIST_RAW_PREVIOUS(first, entry) = QLIST_RAW_NEXT(elm, entry); \ + } else { \ + *QLIST_RAW_NEXT(elm, entry) = NULL; \ + } \ +} while (0) + #endif /* QEMU_SYS_QUEUE_H */ diff --git a/include/qemu/rcu_queue.h b/include/qemu/rcu_queue.h index 2d386f303e..558961cc27 100644 --- a/include/qemu/rcu_queue.h +++ b/include/qemu/rcu_queue.h @@ -262,6 +262,53 @@ extern "C" { (var) && ((next) = atomic_rcu_read(&(var)->field.tqe_next), 1); \ (var) = (next)) +/* + * RCU singly-linked list + */ + +/* Singly-linked list access methods */ +#define QSLIST_EMPTY_RCU(head) (atomic_read(&(head)->slh_first) == NULL) +#define QSLIST_FIRST_RCU(head) atomic_rcu_read(&(head)->slh_first) +#define QSLIST_NEXT_RCU(elm, field) atomic_rcu_read(&(elm)->field.sle_next) + +/* Singly-linked list functions */ +#define QSLIST_INSERT_HEAD_RCU(head, elm, field) do { \ + (elm)->field.sle_next = (head)->slh_first; \ + atomic_rcu_set(&(head)->slh_first, (elm)); \ +} while (/*CONSTCOND*/0) + +#define QSLIST_INSERT_AFTER_RCU(head, listelm, elm, field) do { \ + (elm)->field.sle_next = (listelm)->field.sle_next; \ + atomic_rcu_set(&(listelm)->field.sle_next, (elm)); \ +} while (/*CONSTCOND*/0) + +#define QSLIST_REMOVE_HEAD_RCU(head, field) do { \ + atomic_set(&(head)->slh_first, (head)->slh_first->field.sle_next); \ +} while (/*CONSTCOND*/0) + +#define QSLIST_REMOVE_RCU(head, elm, type, field) do { \ + if ((head)->slh_first == (elm)) { \ + QSLIST_REMOVE_HEAD_RCU((head), field); \ + } else { \ + struct type *curr = (head)->slh_first; \ + while (curr->field.sle_next != (elm)) { \ + curr = curr->field.sle_next; \ + } \ + atomic_set(&curr->field.sle_next, \ + curr->field.sle_next->field.sle_next); \ + } \ +} while (/*CONSTCOND*/0) + +#define QSLIST_FOREACH_RCU(var, head, field) \ + for ((var) = atomic_rcu_read(&(head)->slh_first); \ + (var); \ + (var) = atomic_rcu_read(&(var)->field.sle_next)) + +#define QSLIST_FOREACH_SAFE_RCU(var, head, field, next) \ + for ((var) = atomic_rcu_read(&(head)->slh_first); \ + (var) && ((next) = atomic_rcu_read(&(var)->field.sle_next), 1); \ + (var) = (next)) + #ifdef __cplusplus } #endif diff --git a/include/qemu/selfmap.h b/include/qemu/selfmap.h new file mode 100644 index 0000000000..8382c4c779 --- /dev/null +++ b/include/qemu/selfmap.h @@ -0,0 +1,44 @@ +/* + * Utility functions to read our own memory map + * + * Copyright (c) 2020 Linaro Ltd + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef _SELFMAP_H_ +#define _SELFMAP_H_ + +typedef struct { + unsigned long start; + unsigned long end; + + /* flags */ + bool is_read; + bool is_write; + bool is_exec; + bool is_priv; + + unsigned long offset; + gchar *dev; + uint64_t inode; + gchar *path; +} MapInfo; + + +/** + * read_self_maps: + * + * Read /proc/self/maps and return a list of MapInfo structures. + */ +GSList *read_self_maps(void); + +/** + * free_self_maps: + * @info: a GSlist + * + * Free a list of MapInfo structures. + */ +void free_self_maps(GSList *info); + +#endif /* _SELFMAP_H_ */ diff --git a/include/qemu/seqlock.h b/include/qemu/seqlock.h index fd408b7ec5..8b6b4ee4bb 100644 --- a/include/qemu/seqlock.h +++ b/include/qemu/seqlock.h @@ -55,11 +55,11 @@ static inline void seqlock_write_lock_impl(QemuSeqLock *sl, QemuLockable *lock) #define seqlock_write_lock(sl, lock) \ seqlock_write_lock_impl(sl, QEMU_MAKE_LOCKABLE(lock)) -/* Lock out other writers and update the count. */ +/* Update the count and release the lock. */ static inline void seqlock_write_unlock_impl(QemuSeqLock *sl, QemuLockable *lock) { + seqlock_write_end(sl); qemu_lockable_unlock(lock); - seqlock_write_begin(sl); } #define seqlock_write_unlock(sl, lock) \ seqlock_write_unlock_impl(sl, QEMU_MAKE_LOCKABLE(lock)) diff --git a/include/qemu/thread.h b/include/qemu/thread.h index 047db0307e..d22848138e 100644 --- a/include/qemu/thread.h +++ b/include/qemu/thread.h @@ -57,17 +57,17 @@ extern QemuCondTimedWaitFunc qemu_cond_timedwait_func; * hide them. */ #define qemu_mutex_lock(m) \ - qemu_mutex_lock_impl(m, __FILE__, __LINE__); + qemu_mutex_lock_impl(m, __FILE__, __LINE__) #define qemu_mutex_trylock(m) \ - qemu_mutex_trylock_impl(m, __FILE__, __LINE__); + qemu_mutex_trylock_impl(m, __FILE__, __LINE__) #define qemu_rec_mutex_lock(m) \ - qemu_rec_mutex_lock_impl(m, __FILE__, __LINE__); + qemu_rec_mutex_lock_impl(m, __FILE__, __LINE__) #define qemu_rec_mutex_trylock(m) \ - qemu_rec_mutex_trylock_impl(m, __FILE__, __LINE__); + qemu_rec_mutex_trylock_impl(m, __FILE__, __LINE__) #define qemu_cond_wait(c, m) \ - qemu_cond_wait_impl(c, m, __FILE__, __LINE__); + qemu_cond_wait_impl(c, m, __FILE__, __LINE__) #define qemu_cond_timedwait(c, m, ms) \ - qemu_cond_wait_impl(c, m, ms, __FILE__, __LINE__); + qemu_cond_timedwait_impl(c, m, ms, __FILE__, __LINE__) #else #define qemu_mutex_lock(m) ({ \ QemuMutexLockFunc _f = atomic_read(&qemu_mutex_lock_func); \ diff --git a/include/qemu/timer.h b/include/qemu/timer.h index 85bc6eb00b..6a8b48b5a9 100644 --- a/include/qemu/timer.h +++ b/include/qemu/timer.h @@ -838,14 +838,11 @@ extern int use_rt_clock; static inline int64_t get_clock(void) { -#ifdef CLOCK_MONOTONIC if (use_rt_clock) { struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); return ts.tv_sec * 1000000000LL + ts.tv_nsec; - } else -#endif - { + } else { /* XXX: using gettimeofday leads to problems if the date changes, so it should be avoided. */ return get_clock_realtime(); diff --git a/include/qom/object.h b/include/qom/object.h index 128d00c77f..784c97c0e1 100644 --- a/include/qom/object.h +++ b/include/qom/object.h @@ -200,8 +200,14 @@ typedef struct InterfaceInfo InterfaceInfo; * * Interfaces allow a limited form of multiple inheritance. Instances are * similar to normal types except for the fact that are only defined by - * their classes and never carry any state. You can dynamically cast an object - * to one of its #Interface types and vice versa. + * their classes and never carry any state. As a consequence, a pointer to + * an interface instance should always be of incomplete type in order to be + * sure it cannot be dereferenced. That is, you should define the + * 'typedef struct SomethingIf SomethingIf' so that you can pass around + * 'SomethingIf *si' arguments, but not define a 'struct SomethingIf { ... }'. + * The only things you can validly do with a 'SomethingIf *' are to pass it as + * an argument to a method on its corresponding SomethingIfClass, or to + * dynamically cast it to an object that implements the interface. * * # Methods # * @@ -303,6 +309,8 @@ typedef struct InterfaceInfo InterfaceInfo; */ +typedef struct ObjectProperty ObjectProperty; + /** * ObjectPropertyAccessor: * @obj: the object that owns the property @@ -350,7 +358,16 @@ typedef void (ObjectPropertyRelease)(Object *obj, const char *name, void *opaque); -typedef struct ObjectProperty +/** + * ObjectPropertyInit: + * @obj: the object that owns the property + * @prop: the property to set + * + * Called when a property is initialized. + */ +typedef void (ObjectPropertyInit)(Object *obj, ObjectProperty *prop); + +struct ObjectProperty { gchar *name; gchar *type; @@ -359,8 +376,10 @@ typedef struct ObjectProperty ObjectPropertyAccessor *set; ObjectPropertyResolve *resolve; ObjectPropertyRelease *release; + ObjectPropertyInit *init; void *opaque; -} ObjectProperty; + QObject *defval; +}; /** * ObjectUnparent: @@ -592,6 +611,18 @@ struct InterfaceClass ((interface *)object_dynamic_cast_assert(OBJECT((obj)), (name), \ __FILE__, __LINE__, __func__)) +/** + * object_new_with_class: + * @klass: The class to instantiate. + * + * This function will initialize a new object using heap allocated memory. + * The returned object has a reference count of 1, and will be freed when + * the last reference is dropped. + * + * Returns: The newly allocated and instantiated object. + */ +Object *object_new_with_class(ObjectClass *klass); + /** * object_new: * @typename: The name of the type of the object to instantiate. @@ -679,6 +710,7 @@ void object_apply_global_props(Object *obj, const GPtrArray *props, Error **errp); void object_set_machine_compat_props(GPtrArray *compat_props); void object_set_accelerator_compat_props(GPtrArray *compat_props); +void object_register_sugar_prop(const char *driver, const char *prop, const char *value); void object_apply_compat_props(Object *obj); /** @@ -973,8 +1005,9 @@ GSList *object_class_get_list_sorted(const char *implements_type, * * Increase the reference count of a object. A object cannot be freed as long * as its reference count is greater than zero. + * Returns: @obj */ -void object_ref(Object *obj); +Object *object_ref(Object *obj); /** * object_unref: @@ -1024,6 +1057,42 @@ ObjectProperty *object_class_property_add(ObjectClass *klass, const char *name, ObjectPropertyRelease *release, void *opaque, Error **errp); +/** + * object_property_set_default_bool: + * @prop: the property to set + * @value: the value to be written to the property + * + * Set the property default value. + */ +void object_property_set_default_bool(ObjectProperty *prop, bool value); + +/** + * object_property_set_default_str: + * @prop: the property to set + * @value: the value to be written to the property + * + * Set the property default value. + */ +void object_property_set_default_str(ObjectProperty *prop, const char *value); + +/** + * object_property_set_default_int: + * @prop: the property to set + * @value: the value to be written to the property + * + * Set the property default value. + */ +void object_property_set_default_int(ObjectProperty *prop, int64_t value); + +/** + * object_property_set_default_uint: + * @prop: the property to set + * @value: the value to be written to the property + * + * Set the property default value. + */ +void object_property_set_default_uint(ObjectProperty *prop, uint64_t value); + /** * object_property_find: * @obj: the object @@ -1443,6 +1512,10 @@ void object_property_add_child(Object *obj, const char *name, typedef enum { /* Unref the link pointer when the property is deleted */ OBJ_PROP_LINK_STRONG = 0x1, + + /* private */ + OBJ_PROP_LINK_DIRECT = 0x2, + OBJ_PROP_LINK_CLASS = 0x4, } ObjectPropertyLinkFlags; /** @@ -1460,7 +1533,7 @@ void object_property_allow_set_link(const Object *, const char *, * @obj: the object to add a property to * @name: the name of the property * @type: the qobj type of the link - * @child: a pointer to where the link object reference is stored + * @targetp: a pointer to where the link object reference is stored * @check: callback to veto setting or NULL if the property is read-only * @flags: additional options for the link * @errp: if an error occurs, a pointer to an area to store the error @@ -1485,7 +1558,15 @@ void object_property_allow_set_link(const Object *, const char *, * modified. */ void object_property_add_link(Object *obj, const char *name, - const char *type, Object **child, + const char *type, Object **targetp, + void (*check)(const Object *obj, const char *name, + Object *val, Error **errp), + ObjectPropertyLinkFlags flags, + Error **errp); + +ObjectProperty *object_class_property_add_link(ObjectClass *oc, + const char *name, + const char *type, ptrdiff_t offset, void (*check)(const Object *obj, const char *name, Object *val, Error **errp), ObjectPropertyLinkFlags flags, @@ -1508,7 +1589,8 @@ void object_property_add_str(Object *obj, const char *name, void (*set)(Object *, const char *, Error **), Error **errp); -void object_class_property_add_str(ObjectClass *klass, const char *name, +ObjectProperty *object_class_property_add_str(ObjectClass *klass, + const char *name, char *(*get)(Object *, Error **), void (*set)(Object *, const char *, Error **), @@ -1530,7 +1612,8 @@ void object_property_add_bool(Object *obj, const char *name, void (*set)(Object *, bool, Error **), Error **errp); -void object_class_property_add_bool(ObjectClass *klass, const char *name, +ObjectProperty *object_class_property_add_bool(ObjectClass *klass, + const char *name, bool (*get)(Object *, Error **), void (*set)(Object *, bool, Error **), Error **errp); @@ -1554,7 +1637,8 @@ void object_property_add_enum(Object *obj, const char *name, void (*set)(Object *, int, Error **), Error **errp); -void object_class_property_add_enum(ObjectClass *klass, const char *name, +ObjectProperty *object_class_property_add_enum(ObjectClass *klass, + const char *name, const char *typename, const QEnumLookup *lookup, int (*get)(Object *, Error **), @@ -1575,69 +1659,106 @@ void object_property_add_tm(Object *obj, const char *name, void (*get)(Object *, struct tm *, Error **), Error **errp); -void object_class_property_add_tm(ObjectClass *klass, const char *name, +ObjectProperty *object_class_property_add_tm(ObjectClass *klass, + const char *name, void (*get)(Object *, struct tm *, Error **), Error **errp); +typedef enum { + /* Automatically add a getter to the property */ + OBJ_PROP_FLAG_READ = 1 << 0, + /* Automatically add a setter to the property */ + OBJ_PROP_FLAG_WRITE = 1 << 1, + /* Automatically add a getter and a setter to the property */ + OBJ_PROP_FLAG_READWRITE = (OBJ_PROP_FLAG_READ | OBJ_PROP_FLAG_WRITE), +} ObjectPropertyFlags; + /** * object_property_add_uint8_ptr: * @obj: the object to add a property to * @name: the name of the property * @v: pointer to value + * @flags: bitwise-or'd ObjectPropertyFlags * @errp: if an error occurs, a pointer to an area to store the error * * Add an integer property in memory. This function will add a * property of type 'uint8'. */ void object_property_add_uint8_ptr(Object *obj, const char *name, - const uint8_t *v, Error **errp); -void object_class_property_add_uint8_ptr(ObjectClass *klass, const char *name, - const uint8_t *v, Error **errp); + const uint8_t *v, ObjectPropertyFlags flags, + Error **errp); + +ObjectProperty *object_class_property_add_uint8_ptr(ObjectClass *klass, + const char *name, + const uint8_t *v, + ObjectPropertyFlags flags, + Error **errp); /** * object_property_add_uint16_ptr: * @obj: the object to add a property to * @name: the name of the property * @v: pointer to value + * @flags: bitwise-or'd ObjectPropertyFlags * @errp: if an error occurs, a pointer to an area to store the error * * Add an integer property in memory. This function will add a * property of type 'uint16'. */ void object_property_add_uint16_ptr(Object *obj, const char *name, - const uint16_t *v, Error **errp); -void object_class_property_add_uint16_ptr(ObjectClass *klass, const char *name, - const uint16_t *v, Error **errp); + const uint16_t *v, + ObjectPropertyFlags flags, + Error **errp); + +ObjectProperty *object_class_property_add_uint16_ptr(ObjectClass *klass, + const char *name, + const uint16_t *v, + ObjectPropertyFlags flags, + Error **errp); /** * object_property_add_uint32_ptr: * @obj: the object to add a property to * @name: the name of the property * @v: pointer to value + * @flags: bitwise-or'd ObjectPropertyFlags * @errp: if an error occurs, a pointer to an area to store the error * * Add an integer property in memory. This function will add a * property of type 'uint32'. */ void object_property_add_uint32_ptr(Object *obj, const char *name, - const uint32_t *v, Error **errp); -void object_class_property_add_uint32_ptr(ObjectClass *klass, const char *name, - const uint32_t *v, Error **errp); + const uint32_t *v, + ObjectPropertyFlags flags, + Error **errp); + +ObjectProperty *object_class_property_add_uint32_ptr(ObjectClass *klass, + const char *name, + const uint32_t *v, + ObjectPropertyFlags flags, + Error **errp); /** * object_property_add_uint64_ptr: * @obj: the object to add a property to * @name: the name of the property * @v: pointer to value + * @flags: bitwise-or'd ObjectPropertyFlags * @errp: if an error occurs, a pointer to an area to store the error * * Add an integer property in memory. This function will add a * property of type 'uint64'. */ void object_property_add_uint64_ptr(Object *obj, const char *name, - const uint64_t *v, Error **Errp); -void object_class_property_add_uint64_ptr(ObjectClass *klass, const char *name, - const uint64_t *v, Error **Errp); + const uint64_t *v, + ObjectPropertyFlags flags, + Error **Errp); + +ObjectProperty *object_class_property_add_uint64_ptr(ObjectClass *klass, + const char *name, + const uint64_t *v, + ObjectPropertyFlags flags, + Error **Errp); /** * object_property_add_alias: @@ -1747,4 +1868,20 @@ Object *container_get(Object *root, const char *path); * Returns the instance_size of the given @typename. */ size_t object_type_get_instance_size(const char *typename); + +/** + * object_property_help: + * @name: the name of the property + * @type: the type of the property + * @defval: the default value + * @description: description of the property + * + * Returns: a user-friendly formatted string describing the property + * for help purposes. + */ +char *object_property_help(const char *name, const char *type, + QObject *defval, const char *description); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(Object, object_unref) + #endif diff --git a/include/qom/object_interfaces.h b/include/qom/object_interfaces.h index 3e4e1d928b..6f92f3cebb 100644 --- a/include/qom/object_interfaces.h +++ b/include/qom/object_interfaces.h @@ -162,4 +162,11 @@ void user_creatable_del(const char *id, Error **errp); */ void user_creatable_cleanup(void); +/** + * qmp_object_add: + * + * QMP command handler for object-add. See the QAPI schema for documentation. + */ +void qmp_object_add(QDict *qdict, QObject **ret_data, Error **errp); + #endif diff --git a/include/standard-headers/asm-x86/bootparam.h b/include/standard-headers/asm-x86/bootparam.h index a6f7cf535e..072e2ed546 100644 --- a/include/standard-headers/asm-x86/bootparam.h +++ b/include/standard-headers/asm-x86/bootparam.h @@ -2,7 +2,7 @@ #ifndef _ASM_X86_BOOTPARAM_H #define _ASM_X86_BOOTPARAM_H -/* setup_data types */ +/* setup_data/setup_indirect types */ #define SETUP_NONE 0 #define SETUP_E820_EXT 1 #define SETUP_DTB 2 @@ -11,6 +11,11 @@ #define SETUP_APPLE_PROPERTIES 5 #define SETUP_JAILHOUSE 6 +#define SETUP_INDIRECT (1<<31) + +/* SETUP_INDIRECT | max(SETUP_*) */ +#define SETUP_TYPE_MAX (SETUP_INDIRECT | SETUP_JAILHOUSE) + /* ram_size flags */ #define RAMDISK_IMAGE_START_MASK 0x07FF #define RAMDISK_PROMPT_FLAG 0x8000 diff --git a/include/standard-headers/drivers/infiniband/hw/vmw_pvrdma/pvrdma_dev_api.h b/include/standard-headers/drivers/infiniband/hw/vmw_pvrdma/pvrdma_dev_api.h index d019872608..a5a1c8234e 100644 --- a/include/standard-headers/drivers/infiniband/hw/vmw_pvrdma/pvrdma_dev_api.h +++ b/include/standard-headers/drivers/infiniband/hw/vmw_pvrdma/pvrdma_dev_api.h @@ -58,7 +58,8 @@ #define PVRDMA_ROCEV1_VERSION 17 #define PVRDMA_ROCEV2_VERSION 18 #define PVRDMA_PPN64_VERSION 19 -#define PVRDMA_VERSION PVRDMA_PPN64_VERSION +#define PVRDMA_QPHANDLE_VERSION 20 +#define PVRDMA_VERSION PVRDMA_QPHANDLE_VERSION #define PVRDMA_BOARD_ID 1 #define PVRDMA_REV_ID 1 @@ -581,6 +582,17 @@ struct pvrdma_cmd_create_qp_resp { uint32_t max_inline_data; }; +struct pvrdma_cmd_create_qp_resp_v2 { + struct pvrdma_cmd_resp_hdr hdr; + uint32_t qpn; + uint32_t qp_handle; + uint32_t max_send_wr; + uint32_t max_recv_wr; + uint32_t max_send_sge; + uint32_t max_recv_sge; + uint32_t max_inline_data; +}; + struct pvrdma_cmd_modify_qp { struct pvrdma_cmd_hdr hdr; uint32_t qp_handle; @@ -663,6 +675,7 @@ union pvrdma_cmd_resp { struct pvrdma_cmd_create_cq_resp create_cq_resp; struct pvrdma_cmd_resize_cq_resp resize_cq_resp; struct pvrdma_cmd_create_qp_resp create_qp_resp; + struct pvrdma_cmd_create_qp_resp_v2 create_qp_resp_v2; struct pvrdma_cmd_query_qp_resp query_qp_resp; struct pvrdma_cmd_destroy_qp_resp destroy_qp_resp; struct pvrdma_cmd_create_srq_resp create_srq_resp; diff --git a/include/standard-headers/drm/drm_fourcc.h b/include/standard-headers/drm/drm_fourcc.h index a308c91b4f..66e838074c 100644 --- a/include/standard-headers/drm/drm_fourcc.h +++ b/include/standard-headers/drm/drm_fourcc.h @@ -68,7 +68,7 @@ extern "C" { #define fourcc_code(a, b, c, d) ((uint32_t)(a) | ((uint32_t)(b) << 8) | \ ((uint32_t)(c) << 16) | ((uint32_t)(d) << 24)) -#define DRM_FORMAT_BIG_ENDIAN (1<<31) /* format is big endian instead of little endian */ +#define DRM_FORMAT_BIG_ENDIAN (1U<<31) /* format is big endian instead of little endian */ /* Reserve 0 for the invalid format specifier */ #define DRM_FORMAT_INVALID 0 @@ -409,6 +409,30 @@ extern "C" { #define I915_FORMAT_MOD_Y_TILED_CCS fourcc_mod_code(INTEL, 4) #define I915_FORMAT_MOD_Yf_TILED_CCS fourcc_mod_code(INTEL, 5) +/* + * Intel color control surfaces (CCS) for Gen-12 render compression. + * + * The main surface is Y-tiled and at plane index 0, the CCS is linear and + * at index 1. A 64B CCS cache line corresponds to an area of 4x1 tiles in + * main surface. In other words, 4 bits in CCS map to a main surface cache + * line pair. The main surface pitch is required to be a multiple of four + * Y-tile widths. + */ +#define I915_FORMAT_MOD_Y_TILED_GEN12_RC_CCS fourcc_mod_code(INTEL, 6) + +/* + * Intel color control surfaces (CCS) for Gen-12 media compression + * + * The main surface is Y-tiled and at plane index 0, the CCS is linear and + * at index 1. A 64B CCS cache line corresponds to an area of 4x1 tiles in + * main surface. In other words, 4 bits in CCS map to a main surface cache + * line pair. The main surface pitch is required to be a multiple of four + * Y-tile widths. For semi-planar formats like NV12, CCS planes follow the + * Y and UV planes i.e., planes 0 and 1 are used for Y and UV surfaces, + * planes 2 and 3 for the respective CCS. + */ +#define I915_FORMAT_MOD_Y_TILED_GEN12_MC_CCS fourcc_mod_code(INTEL, 7) + /* * Tiled, NV12MT, grouped in 64 (pixels) x 32 (lines) -sized macroblocks * @@ -647,7 +671,21 @@ extern "C" { * Further information on the use of AFBC modifiers can be found in * Documentation/gpu/afbc.rst */ -#define DRM_FORMAT_MOD_ARM_AFBC(__afbc_mode) fourcc_mod_code(ARM, __afbc_mode) + +/* + * The top 4 bits (out of the 56 bits alloted for specifying vendor specific + * modifiers) denote the category for modifiers. Currently we have only two + * categories of modifiers ie AFBC and MISC. We can have a maximum of sixteen + * different categories. + */ +#define DRM_FORMAT_MOD_ARM_CODE(__type, __val) \ + fourcc_mod_code(ARM, ((uint64_t)(__type) << 52) | ((__val) & 0x000fffffffffffffULL)) + +#define DRM_FORMAT_MOD_ARM_TYPE_AFBC 0x00 +#define DRM_FORMAT_MOD_ARM_TYPE_MISC 0x01 + +#define DRM_FORMAT_MOD_ARM_AFBC(__afbc_mode) \ + DRM_FORMAT_MOD_ARM_CODE(DRM_FORMAT_MOD_ARM_TYPE_AFBC, __afbc_mode) /* * AFBC superblock size @@ -741,6 +779,16 @@ extern "C" { */ #define AFBC_FORMAT_MOD_BCH (1ULL << 11) +/* + * Arm 16x16 Block U-Interleaved modifier + * + * This is used by Arm Mali Utgard and Midgard GPUs. It divides the image + * into 16x16 pixel blocks. Blocks are stored linearly in order, but pixels + * in the block are reordered. + */ +#define DRM_FORMAT_MOD_ARM_16X16_BLOCK_U_INTERLEAVED \ + DRM_FORMAT_MOD_ARM_CODE(DRM_FORMAT_MOD_ARM_TYPE_MISC, 1ULL) + /* * Allwinner tiled modifier * diff --git a/include/standard-headers/linux/ethtool.h b/include/standard-headers/linux/ethtool.h index 4ff422b635..8adf3b018b 100644 --- a/include/standard-headers/linux/ethtool.h +++ b/include/standard-headers/linux/ethtool.h @@ -593,6 +593,9 @@ struct ethtool_pauseparam { * @ETH_SS_RSS_HASH_FUNCS: RSS hush function names * @ETH_SS_PHY_STATS: Statistic names, for use with %ETHTOOL_GPHYSTATS * @ETH_SS_PHY_TUNABLES: PHY tunable names + * @ETH_SS_LINK_MODES: link mode names + * @ETH_SS_MSG_CLASSES: debug message class names + * @ETH_SS_WOL_MODES: wake-on-lan modes */ enum ethtool_stringset { ETH_SS_TEST = 0, @@ -604,6 +607,12 @@ enum ethtool_stringset { ETH_SS_TUNABLES, ETH_SS_PHY_STATS, ETH_SS_PHY_TUNABLES, + ETH_SS_LINK_MODES, + ETH_SS_MSG_CLASSES, + ETH_SS_WOL_MODES, + + /* add new constants above here */ + ETH_SS_COUNT }; /** @@ -1507,6 +1516,11 @@ enum ethtool_link_mode_bit_indices { ETHTOOL_LINK_MODE_200000baseCR4_Full_BIT = 66, ETHTOOL_LINK_MODE_100baseT1_Full_BIT = 67, ETHTOOL_LINK_MODE_1000baseT1_Full_BIT = 68, + ETHTOOL_LINK_MODE_400000baseKR8_Full_BIT = 69, + ETHTOOL_LINK_MODE_400000baseSR8_Full_BIT = 70, + ETHTOOL_LINK_MODE_400000baseLR8_ER8_FR8_Full_BIT = 71, + ETHTOOL_LINK_MODE_400000baseDR8_Full_BIT = 72, + ETHTOOL_LINK_MODE_400000baseCR8_Full_BIT = 73, /* must be last entry */ __ETHTOOL_LINK_MODE_MASK_NBITS @@ -1618,6 +1632,7 @@ enum ethtool_link_mode_bit_indices { #define SPEED_56000 56000 #define SPEED_100000 100000 #define SPEED_200000 200000 +#define SPEED_400000 400000 #define SPEED_UNKNOWN -1 @@ -1682,6 +1697,8 @@ static inline int ethtool_validate_duplex(uint8_t duplex) #define WAKE_MAGICSECURE (1 << 6) /* only meaningful if WAKE_MAGIC */ #define WAKE_FILTER (1 << 7) +#define WOL_MODE_COUNT 8 + /* L2-L4 network traffic flow types */ #define TCP_V4_FLOW 0x01 /* hash or spec (tcp_ip4_spec) */ #define UDP_V4_FLOW 0x02 /* hash or spec (udp_ip4_spec) */ diff --git a/include/standard-headers/linux/fuse.h b/include/standard-headers/linux/fuse.h new file mode 100644 index 0000000000..f4df0a40f6 --- /dev/null +++ b/include/standard-headers/linux/fuse.h @@ -0,0 +1,891 @@ +/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-2-Clause) */ +/* + This file defines the kernel interface of FUSE + Copyright (C) 2001-2008 Miklos Szeredi + + This program can be distributed under the terms of the GNU GPL. + See the file COPYING. + + This -- and only this -- header file may also be distributed under + the terms of the BSD Licence as follows: + + Copyright (C) 2001-2007 Miklos Szeredi. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. +*/ + +/* + * This file defines the kernel interface of FUSE + * + * Protocol changelog: + * + * 7.1: + * - add the following messages: + * FUSE_SETATTR, FUSE_SYMLINK, FUSE_MKNOD, FUSE_MKDIR, FUSE_UNLINK, + * FUSE_RMDIR, FUSE_RENAME, FUSE_LINK, FUSE_OPEN, FUSE_READ, FUSE_WRITE, + * FUSE_RELEASE, FUSE_FSYNC, FUSE_FLUSH, FUSE_SETXATTR, FUSE_GETXATTR, + * FUSE_LISTXATTR, FUSE_REMOVEXATTR, FUSE_OPENDIR, FUSE_READDIR, + * FUSE_RELEASEDIR + * - add padding to messages to accommodate 32-bit servers on 64-bit kernels + * + * 7.2: + * - add FOPEN_DIRECT_IO and FOPEN_KEEP_CACHE flags + * - add FUSE_FSYNCDIR message + * + * 7.3: + * - add FUSE_ACCESS message + * - add FUSE_CREATE message + * - add filehandle to fuse_setattr_in + * + * 7.4: + * - add frsize to fuse_kstatfs + * - clean up request size limit checking + * + * 7.5: + * - add flags and max_write to fuse_init_out + * + * 7.6: + * - add max_readahead to fuse_init_in and fuse_init_out + * + * 7.7: + * - add FUSE_INTERRUPT message + * - add POSIX file lock support + * + * 7.8: + * - add lock_owner and flags fields to fuse_release_in + * - add FUSE_BMAP message + * - add FUSE_DESTROY message + * + * 7.9: + * - new fuse_getattr_in input argument of GETATTR + * - add lk_flags in fuse_lk_in + * - add lock_owner field to fuse_setattr_in, fuse_read_in and fuse_write_in + * - add blksize field to fuse_attr + * - add file flags field to fuse_read_in and fuse_write_in + * - Add ATIME_NOW and MTIME_NOW flags to fuse_setattr_in + * + * 7.10 + * - add nonseekable open flag + * + * 7.11 + * - add IOCTL message + * - add unsolicited notification support + * - add POLL message and NOTIFY_POLL notification + * + * 7.12 + * - add umask flag to input argument of create, mknod and mkdir + * - add notification messages for invalidation of inodes and + * directory entries + * + * 7.13 + * - make max number of background requests and congestion threshold + * tunables + * + * 7.14 + * - add splice support to fuse device + * + * 7.15 + * - add store notify + * - add retrieve notify + * + * 7.16 + * - add BATCH_FORGET request + * - FUSE_IOCTL_UNRESTRICTED shall now return with array of 'struct + * fuse_ioctl_iovec' instead of ambiguous 'struct iovec' + * - add FUSE_IOCTL_32BIT flag + * + * 7.17 + * - add FUSE_FLOCK_LOCKS and FUSE_RELEASE_FLOCK_UNLOCK + * + * 7.18 + * - add FUSE_IOCTL_DIR flag + * - add FUSE_NOTIFY_DELETE + * + * 7.19 + * - add FUSE_FALLOCATE + * + * 7.20 + * - add FUSE_AUTO_INVAL_DATA + * + * 7.21 + * - add FUSE_READDIRPLUS + * - send the requested events in POLL request + * + * 7.22 + * - add FUSE_ASYNC_DIO + * + * 7.23 + * - add FUSE_WRITEBACK_CACHE + * - add time_gran to fuse_init_out + * - add reserved space to fuse_init_out + * - add FATTR_CTIME + * - add ctime and ctimensec to fuse_setattr_in + * - add FUSE_RENAME2 request + * - add FUSE_NO_OPEN_SUPPORT flag + * + * 7.24 + * - add FUSE_LSEEK for SEEK_HOLE and SEEK_DATA support + * + * 7.25 + * - add FUSE_PARALLEL_DIROPS + * + * 7.26 + * - add FUSE_HANDLE_KILLPRIV + * - add FUSE_POSIX_ACL + * + * 7.27 + * - add FUSE_ABORT_ERROR + * + * 7.28 + * - add FUSE_COPY_FILE_RANGE + * - add FOPEN_CACHE_DIR + * - add FUSE_MAX_PAGES, add max_pages to init_out + * - add FUSE_CACHE_SYMLINKS + * + * 7.29 + * - add FUSE_NO_OPENDIR_SUPPORT flag + * + * 7.30 + * - add FUSE_EXPLICIT_INVAL_DATA + * - add FUSE_IOCTL_COMPAT_X32 + * + * 7.31 + * - add FUSE_WRITE_KILL_PRIV flag + * - add FUSE_SETUPMAPPING and FUSE_REMOVEMAPPING + * - add map_alignment to fuse_init_out, add FUSE_MAP_ALIGNMENT flag + */ + +#ifndef _LINUX_FUSE_H +#define _LINUX_FUSE_H + +#include + +/* + * Version negotiation: + * + * Both the kernel and userspace send the version they support in the + * INIT request and reply respectively. + * + * If the major versions match then both shall use the smallest + * of the two minor versions for communication. + * + * If the kernel supports a larger major version, then userspace shall + * reply with the major version it supports, ignore the rest of the + * INIT message and expect a new INIT message from the kernel with a + * matching major version. + * + * If the library supports a larger major version, then it shall fall + * back to the major protocol version sent by the kernel for + * communication and reply with that major version (and an arbitrary + * supported minor version). + */ + +/** Version number of this interface */ +#define FUSE_KERNEL_VERSION 7 + +/** Minor version number of this interface */ +#define FUSE_KERNEL_MINOR_VERSION 31 + +/** The node ID of the root inode */ +#define FUSE_ROOT_ID 1 + +/* Make sure all structures are padded to 64bit boundary, so 32bit + userspace works under 64bit kernels */ + +struct fuse_attr { + uint64_t ino; + uint64_t size; + uint64_t blocks; + uint64_t atime; + uint64_t mtime; + uint64_t ctime; + uint32_t atimensec; + uint32_t mtimensec; + uint32_t ctimensec; + uint32_t mode; + uint32_t nlink; + uint32_t uid; + uint32_t gid; + uint32_t rdev; + uint32_t blksize; + uint32_t padding; +}; + +struct fuse_kstatfs { + uint64_t blocks; + uint64_t bfree; + uint64_t bavail; + uint64_t files; + uint64_t ffree; + uint32_t bsize; + uint32_t namelen; + uint32_t frsize; + uint32_t padding; + uint32_t spare[6]; +}; + +struct fuse_file_lock { + uint64_t start; + uint64_t end; + uint32_t type; + uint32_t pid; /* tgid */ +}; + +/** + * Bitmasks for fuse_setattr_in.valid + */ +#define FATTR_MODE (1 << 0) +#define FATTR_UID (1 << 1) +#define FATTR_GID (1 << 2) +#define FATTR_SIZE (1 << 3) +#define FATTR_ATIME (1 << 4) +#define FATTR_MTIME (1 << 5) +#define FATTR_FH (1 << 6) +#define FATTR_ATIME_NOW (1 << 7) +#define FATTR_MTIME_NOW (1 << 8) +#define FATTR_LOCKOWNER (1 << 9) +#define FATTR_CTIME (1 << 10) + +/** + * Flags returned by the OPEN request + * + * FOPEN_DIRECT_IO: bypass page cache for this open file + * FOPEN_KEEP_CACHE: don't invalidate the data cache on open + * FOPEN_NONSEEKABLE: the file is not seekable + * FOPEN_CACHE_DIR: allow caching this directory + * FOPEN_STREAM: the file is stream-like (no file position at all) + */ +#define FOPEN_DIRECT_IO (1 << 0) +#define FOPEN_KEEP_CACHE (1 << 1) +#define FOPEN_NONSEEKABLE (1 << 2) +#define FOPEN_CACHE_DIR (1 << 3) +#define FOPEN_STREAM (1 << 4) + +/** + * INIT request/reply flags + * + * FUSE_ASYNC_READ: asynchronous read requests + * FUSE_POSIX_LOCKS: remote locking for POSIX file locks + * FUSE_FILE_OPS: kernel sends file handle for fstat, etc... (not yet supported) + * FUSE_ATOMIC_O_TRUNC: handles the O_TRUNC open flag in the filesystem + * FUSE_EXPORT_SUPPORT: filesystem handles lookups of "." and ".." + * FUSE_BIG_WRITES: filesystem can handle write size larger than 4kB + * FUSE_DONT_MASK: don't apply umask to file mode on create operations + * FUSE_SPLICE_WRITE: kernel supports splice write on the device + * FUSE_SPLICE_MOVE: kernel supports splice move on the device + * FUSE_SPLICE_READ: kernel supports splice read on the device + * FUSE_FLOCK_LOCKS: remote locking for BSD style file locks + * FUSE_HAS_IOCTL_DIR: kernel supports ioctl on directories + * FUSE_AUTO_INVAL_DATA: automatically invalidate cached pages + * FUSE_DO_READDIRPLUS: do READDIRPLUS (READDIR+LOOKUP in one) + * FUSE_READDIRPLUS_AUTO: adaptive readdirplus + * FUSE_ASYNC_DIO: asynchronous direct I/O submission + * FUSE_WRITEBACK_CACHE: use writeback cache for buffered writes + * FUSE_NO_OPEN_SUPPORT: kernel supports zero-message opens + * FUSE_PARALLEL_DIROPS: allow parallel lookups and readdir + * FUSE_HANDLE_KILLPRIV: fs handles killing suid/sgid/cap on write/chown/trunc + * FUSE_POSIX_ACL: filesystem supports posix acls + * FUSE_ABORT_ERROR: reading the device after abort returns ECONNABORTED + * FUSE_MAX_PAGES: init_out.max_pages contains the max number of req pages + * FUSE_CACHE_SYMLINKS: cache READLINK responses + * FUSE_NO_OPENDIR_SUPPORT: kernel supports zero-message opendir + * FUSE_EXPLICIT_INVAL_DATA: only invalidate cached pages on explicit request + * FUSE_MAP_ALIGNMENT: map_alignment field is valid + */ +#define FUSE_ASYNC_READ (1 << 0) +#define FUSE_POSIX_LOCKS (1 << 1) +#define FUSE_FILE_OPS (1 << 2) +#define FUSE_ATOMIC_O_TRUNC (1 << 3) +#define FUSE_EXPORT_SUPPORT (1 << 4) +#define FUSE_BIG_WRITES (1 << 5) +#define FUSE_DONT_MASK (1 << 6) +#define FUSE_SPLICE_WRITE (1 << 7) +#define FUSE_SPLICE_MOVE (1 << 8) +#define FUSE_SPLICE_READ (1 << 9) +#define FUSE_FLOCK_LOCKS (1 << 10) +#define FUSE_HAS_IOCTL_DIR (1 << 11) +#define FUSE_AUTO_INVAL_DATA (1 << 12) +#define FUSE_DO_READDIRPLUS (1 << 13) +#define FUSE_READDIRPLUS_AUTO (1 << 14) +#define FUSE_ASYNC_DIO (1 << 15) +#define FUSE_WRITEBACK_CACHE (1 << 16) +#define FUSE_NO_OPEN_SUPPORT (1 << 17) +#define FUSE_PARALLEL_DIROPS (1 << 18) +#define FUSE_HANDLE_KILLPRIV (1 << 19) +#define FUSE_POSIX_ACL (1 << 20) +#define FUSE_ABORT_ERROR (1 << 21) +#define FUSE_MAX_PAGES (1 << 22) +#define FUSE_CACHE_SYMLINKS (1 << 23) +#define FUSE_NO_OPENDIR_SUPPORT (1 << 24) +#define FUSE_EXPLICIT_INVAL_DATA (1 << 25) +#define FUSE_MAP_ALIGNMENT (1 << 26) + +/** + * CUSE INIT request/reply flags + * + * CUSE_UNRESTRICTED_IOCTL: use unrestricted ioctl + */ +#define CUSE_UNRESTRICTED_IOCTL (1 << 0) + +/** + * Release flags + */ +#define FUSE_RELEASE_FLUSH (1 << 0) +#define FUSE_RELEASE_FLOCK_UNLOCK (1 << 1) + +/** + * Getattr flags + */ +#define FUSE_GETATTR_FH (1 << 0) + +/** + * Lock flags + */ +#define FUSE_LK_FLOCK (1 << 0) + +/** + * WRITE flags + * + * FUSE_WRITE_CACHE: delayed write from page cache, file handle is guessed + * FUSE_WRITE_LOCKOWNER: lock_owner field is valid + * FUSE_WRITE_KILL_PRIV: kill suid and sgid bits + */ +#define FUSE_WRITE_CACHE (1 << 0) +#define FUSE_WRITE_LOCKOWNER (1 << 1) +#define FUSE_WRITE_KILL_PRIV (1 << 2) + +/** + * Read flags + */ +#define FUSE_READ_LOCKOWNER (1 << 1) + +/** + * Ioctl flags + * + * FUSE_IOCTL_COMPAT: 32bit compat ioctl on 64bit machine + * FUSE_IOCTL_UNRESTRICTED: not restricted to well-formed ioctls, retry allowed + * FUSE_IOCTL_RETRY: retry with new iovecs + * FUSE_IOCTL_32BIT: 32bit ioctl + * FUSE_IOCTL_DIR: is a directory + * FUSE_IOCTL_COMPAT_X32: x32 compat ioctl on 64bit machine (64bit time_t) + * + * FUSE_IOCTL_MAX_IOV: maximum of in_iovecs + out_iovecs + */ +#define FUSE_IOCTL_COMPAT (1 << 0) +#define FUSE_IOCTL_UNRESTRICTED (1 << 1) +#define FUSE_IOCTL_RETRY (1 << 2) +#define FUSE_IOCTL_32BIT (1 << 3) +#define FUSE_IOCTL_DIR (1 << 4) +#define FUSE_IOCTL_COMPAT_X32 (1 << 5) + +#define FUSE_IOCTL_MAX_IOV 256 + +/** + * Poll flags + * + * FUSE_POLL_SCHEDULE_NOTIFY: request poll notify + */ +#define FUSE_POLL_SCHEDULE_NOTIFY (1 << 0) + +/** + * Fsync flags + * + * FUSE_FSYNC_FDATASYNC: Sync data only, not metadata + */ +#define FUSE_FSYNC_FDATASYNC (1 << 0) + +enum fuse_opcode { + FUSE_LOOKUP = 1, + FUSE_FORGET = 2, /* no reply */ + FUSE_GETATTR = 3, + FUSE_SETATTR = 4, + FUSE_READLINK = 5, + FUSE_SYMLINK = 6, + FUSE_MKNOD = 8, + FUSE_MKDIR = 9, + FUSE_UNLINK = 10, + FUSE_RMDIR = 11, + FUSE_RENAME = 12, + FUSE_LINK = 13, + FUSE_OPEN = 14, + FUSE_READ = 15, + FUSE_WRITE = 16, + FUSE_STATFS = 17, + FUSE_RELEASE = 18, + FUSE_FSYNC = 20, + FUSE_SETXATTR = 21, + FUSE_GETXATTR = 22, + FUSE_LISTXATTR = 23, + FUSE_REMOVEXATTR = 24, + FUSE_FLUSH = 25, + FUSE_INIT = 26, + FUSE_OPENDIR = 27, + FUSE_READDIR = 28, + FUSE_RELEASEDIR = 29, + FUSE_FSYNCDIR = 30, + FUSE_GETLK = 31, + FUSE_SETLK = 32, + FUSE_SETLKW = 33, + FUSE_ACCESS = 34, + FUSE_CREATE = 35, + FUSE_INTERRUPT = 36, + FUSE_BMAP = 37, + FUSE_DESTROY = 38, + FUSE_IOCTL = 39, + FUSE_POLL = 40, + FUSE_NOTIFY_REPLY = 41, + FUSE_BATCH_FORGET = 42, + FUSE_FALLOCATE = 43, + FUSE_READDIRPLUS = 44, + FUSE_RENAME2 = 45, + FUSE_LSEEK = 46, + FUSE_COPY_FILE_RANGE = 47, + FUSE_SETUPMAPPING = 48, + FUSE_REMOVEMAPPING = 49, + + /* CUSE specific operations */ + CUSE_INIT = 4096, + + /* Reserved opcodes: helpful to detect structure endian-ness */ + CUSE_INIT_BSWAP_RESERVED = 1048576, /* CUSE_INIT << 8 */ + FUSE_INIT_BSWAP_RESERVED = 436207616, /* FUSE_INIT << 24 */ +}; + +enum fuse_notify_code { + FUSE_NOTIFY_POLL = 1, + FUSE_NOTIFY_INVAL_INODE = 2, + FUSE_NOTIFY_INVAL_ENTRY = 3, + FUSE_NOTIFY_STORE = 4, + FUSE_NOTIFY_RETRIEVE = 5, + FUSE_NOTIFY_DELETE = 6, + FUSE_NOTIFY_CODE_MAX, +}; + +/* The read buffer is required to be at least 8k, but may be much larger */ +#define FUSE_MIN_READ_BUFFER 8192 + +#define FUSE_COMPAT_ENTRY_OUT_SIZE 120 + +struct fuse_entry_out { + uint64_t nodeid; /* Inode ID */ + uint64_t generation; /* Inode generation: nodeid:gen must + be unique for the fs's lifetime */ + uint64_t entry_valid; /* Cache timeout for the name */ + uint64_t attr_valid; /* Cache timeout for the attributes */ + uint32_t entry_valid_nsec; + uint32_t attr_valid_nsec; + struct fuse_attr attr; +}; + +struct fuse_forget_in { + uint64_t nlookup; +}; + +struct fuse_forget_one { + uint64_t nodeid; + uint64_t nlookup; +}; + +struct fuse_batch_forget_in { + uint32_t count; + uint32_t dummy; +}; + +struct fuse_getattr_in { + uint32_t getattr_flags; + uint32_t dummy; + uint64_t fh; +}; + +#define FUSE_COMPAT_ATTR_OUT_SIZE 96 + +struct fuse_attr_out { + uint64_t attr_valid; /* Cache timeout for the attributes */ + uint32_t attr_valid_nsec; + uint32_t dummy; + struct fuse_attr attr; +}; + +#define FUSE_COMPAT_MKNOD_IN_SIZE 8 + +struct fuse_mknod_in { + uint32_t mode; + uint32_t rdev; + uint32_t umask; + uint32_t padding; +}; + +struct fuse_mkdir_in { + uint32_t mode; + uint32_t umask; +}; + +struct fuse_rename_in { + uint64_t newdir; +}; + +struct fuse_rename2_in { + uint64_t newdir; + uint32_t flags; + uint32_t padding; +}; + +struct fuse_link_in { + uint64_t oldnodeid; +}; + +struct fuse_setattr_in { + uint32_t valid; + uint32_t padding; + uint64_t fh; + uint64_t size; + uint64_t lock_owner; + uint64_t atime; + uint64_t mtime; + uint64_t ctime; + uint32_t atimensec; + uint32_t mtimensec; + uint32_t ctimensec; + uint32_t mode; + uint32_t unused4; + uint32_t uid; + uint32_t gid; + uint32_t unused5; +}; + +struct fuse_open_in { + uint32_t flags; + uint32_t unused; +}; + +struct fuse_create_in { + uint32_t flags; + uint32_t mode; + uint32_t umask; + uint32_t padding; +}; + +struct fuse_open_out { + uint64_t fh; + uint32_t open_flags; + uint32_t padding; +}; + +struct fuse_release_in { + uint64_t fh; + uint32_t flags; + uint32_t release_flags; + uint64_t lock_owner; +}; + +struct fuse_flush_in { + uint64_t fh; + uint32_t unused; + uint32_t padding; + uint64_t lock_owner; +}; + +struct fuse_read_in { + uint64_t fh; + uint64_t offset; + uint32_t size; + uint32_t read_flags; + uint64_t lock_owner; + uint32_t flags; + uint32_t padding; +}; + +#define FUSE_COMPAT_WRITE_IN_SIZE 24 + +struct fuse_write_in { + uint64_t fh; + uint64_t offset; + uint32_t size; + uint32_t write_flags; + uint64_t lock_owner; + uint32_t flags; + uint32_t padding; +}; + +struct fuse_write_out { + uint32_t size; + uint32_t padding; +}; + +#define FUSE_COMPAT_STATFS_SIZE 48 + +struct fuse_statfs_out { + struct fuse_kstatfs st; +}; + +struct fuse_fsync_in { + uint64_t fh; + uint32_t fsync_flags; + uint32_t padding; +}; + +struct fuse_setxattr_in { + uint32_t size; + uint32_t flags; +}; + +struct fuse_getxattr_in { + uint32_t size; + uint32_t padding; +}; + +struct fuse_getxattr_out { + uint32_t size; + uint32_t padding; +}; + +struct fuse_lk_in { + uint64_t fh; + uint64_t owner; + struct fuse_file_lock lk; + uint32_t lk_flags; + uint32_t padding; +}; + +struct fuse_lk_out { + struct fuse_file_lock lk; +}; + +struct fuse_access_in { + uint32_t mask; + uint32_t padding; +}; + +struct fuse_init_in { + uint32_t major; + uint32_t minor; + uint32_t max_readahead; + uint32_t flags; +}; + +#define FUSE_COMPAT_INIT_OUT_SIZE 8 +#define FUSE_COMPAT_22_INIT_OUT_SIZE 24 + +struct fuse_init_out { + uint32_t major; + uint32_t minor; + uint32_t max_readahead; + uint32_t flags; + uint16_t max_background; + uint16_t congestion_threshold; + uint32_t max_write; + uint32_t time_gran; + uint16_t max_pages; + uint16_t map_alignment; + uint32_t unused[8]; +}; + +#define CUSE_INIT_INFO_MAX 4096 + +struct cuse_init_in { + uint32_t major; + uint32_t minor; + uint32_t unused; + uint32_t flags; +}; + +struct cuse_init_out { + uint32_t major; + uint32_t minor; + uint32_t unused; + uint32_t flags; + uint32_t max_read; + uint32_t max_write; + uint32_t dev_major; /* chardev major */ + uint32_t dev_minor; /* chardev minor */ + uint32_t spare[10]; +}; + +struct fuse_interrupt_in { + uint64_t unique; +}; + +struct fuse_bmap_in { + uint64_t block; + uint32_t blocksize; + uint32_t padding; +}; + +struct fuse_bmap_out { + uint64_t block; +}; + +struct fuse_ioctl_in { + uint64_t fh; + uint32_t flags; + uint32_t cmd; + uint64_t arg; + uint32_t in_size; + uint32_t out_size; +}; + +struct fuse_ioctl_iovec { + uint64_t base; + uint64_t len; +}; + +struct fuse_ioctl_out { + int32_t result; + uint32_t flags; + uint32_t in_iovs; + uint32_t out_iovs; +}; + +struct fuse_poll_in { + uint64_t fh; + uint64_t kh; + uint32_t flags; + uint32_t events; +}; + +struct fuse_poll_out { + uint32_t revents; + uint32_t padding; +}; + +struct fuse_notify_poll_wakeup_out { + uint64_t kh; +}; + +struct fuse_fallocate_in { + uint64_t fh; + uint64_t offset; + uint64_t length; + uint32_t mode; + uint32_t padding; +}; + +struct fuse_in_header { + uint32_t len; + uint32_t opcode; + uint64_t unique; + uint64_t nodeid; + uint32_t uid; + uint32_t gid; + uint32_t pid; + uint32_t padding; +}; + +struct fuse_out_header { + uint32_t len; + int32_t error; + uint64_t unique; +}; + +struct fuse_dirent { + uint64_t ino; + uint64_t off; + uint32_t namelen; + uint32_t type; + char name[]; +}; + +#define FUSE_NAME_OFFSET offsetof(struct fuse_dirent, name) +#define FUSE_DIRENT_ALIGN(x) \ + (((x) + sizeof(uint64_t) - 1) & ~(sizeof(uint64_t) - 1)) +#define FUSE_DIRENT_SIZE(d) \ + FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET + (d)->namelen) + +struct fuse_direntplus { + struct fuse_entry_out entry_out; + struct fuse_dirent dirent; +}; + +#define FUSE_NAME_OFFSET_DIRENTPLUS \ + offsetof(struct fuse_direntplus, dirent.name) +#define FUSE_DIRENTPLUS_SIZE(d) \ + FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET_DIRENTPLUS + (d)->dirent.namelen) + +struct fuse_notify_inval_inode_out { + uint64_t ino; + int64_t off; + int64_t len; +}; + +struct fuse_notify_inval_entry_out { + uint64_t parent; + uint32_t namelen; + uint32_t padding; +}; + +struct fuse_notify_delete_out { + uint64_t parent; + uint64_t child; + uint32_t namelen; + uint32_t padding; +}; + +struct fuse_notify_store_out { + uint64_t nodeid; + uint64_t offset; + uint32_t size; + uint32_t padding; +}; + +struct fuse_notify_retrieve_out { + uint64_t notify_unique; + uint64_t nodeid; + uint64_t offset; + uint32_t size; + uint32_t padding; +}; + +/* Matches the size of fuse_write_in */ +struct fuse_notify_retrieve_in { + uint64_t dummy1; + uint64_t offset; + uint32_t size; + uint32_t dummy2; + uint64_t dummy3; + uint64_t dummy4; +}; + +/* Device ioctls: */ +#define FUSE_DEV_IOC_CLONE _IOR(229, 0, uint32_t) + +struct fuse_lseek_in { + uint64_t fh; + uint64_t offset; + uint32_t whence; + uint32_t padding; +}; + +struct fuse_lseek_out { + uint64_t offset; +}; + +struct fuse_copy_file_range_in { + uint64_t fh_in; + uint64_t off_in; + uint64_t nodeid_out; + uint64_t fh_out; + uint64_t off_out; + uint64_t len; + uint64_t flags; +}; + +#endif /* _LINUX_FUSE_H */ diff --git a/include/standard-headers/linux/input-event-codes.h b/include/standard-headers/linux/input-event-codes.h index eb08cb8598..b484c25289 100644 --- a/include/standard-headers/linux/input-event-codes.h +++ b/include/standard-headers/linux/input-event-codes.h @@ -649,6 +649,83 @@ */ #define KEY_DATA 0x277 #define KEY_ONSCREEN_KEYBOARD 0x278 +/* Electronic privacy screen control */ +#define KEY_PRIVACY_SCREEN_TOGGLE 0x279 + +/* + * Some keyboards have keys which do not have a defined meaning, these keys + * are intended to be programmed / bound to macros by the user. For most + * keyboards with these macro-keys the key-sequence to inject, or action to + * take, is all handled by software on the host side. So from the kernel's + * point of view these are just normal keys. + * + * The KEY_MACRO# codes below are intended for such keys, which may be labeled + * e.g. G1-G18, or S1 - S30. The KEY_MACRO# codes MUST NOT be used for keys + * where the marking on the key does indicate a defined meaning / purpose. + * + * The KEY_MACRO# codes MUST also NOT be used as fallback for when no existing + * KEY_FOO define matches the marking / purpose. In this case a new KEY_FOO + * define MUST be added. + */ +#define KEY_MACRO1 0x290 +#define KEY_MACRO2 0x291 +#define KEY_MACRO3 0x292 +#define KEY_MACRO4 0x293 +#define KEY_MACRO5 0x294 +#define KEY_MACRO6 0x295 +#define KEY_MACRO7 0x296 +#define KEY_MACRO8 0x297 +#define KEY_MACRO9 0x298 +#define KEY_MACRO10 0x299 +#define KEY_MACRO11 0x29a +#define KEY_MACRO12 0x29b +#define KEY_MACRO13 0x29c +#define KEY_MACRO14 0x29d +#define KEY_MACRO15 0x29e +#define KEY_MACRO16 0x29f +#define KEY_MACRO17 0x2a0 +#define KEY_MACRO18 0x2a1 +#define KEY_MACRO19 0x2a2 +#define KEY_MACRO20 0x2a3 +#define KEY_MACRO21 0x2a4 +#define KEY_MACRO22 0x2a5 +#define KEY_MACRO23 0x2a6 +#define KEY_MACRO24 0x2a7 +#define KEY_MACRO25 0x2a8 +#define KEY_MACRO26 0x2a9 +#define KEY_MACRO27 0x2aa +#define KEY_MACRO28 0x2ab +#define KEY_MACRO29 0x2ac +#define KEY_MACRO30 0x2ad + +/* + * Some keyboards with the macro-keys described above have some extra keys + * for controlling the host-side software responsible for the macro handling: + * -A macro recording start/stop key. Note that not all keyboards which emit + * KEY_MACRO_RECORD_START will also emit KEY_MACRO_RECORD_STOP if + * KEY_MACRO_RECORD_STOP is not advertised, then KEY_MACRO_RECORD_START + * should be interpreted as a recording start/stop toggle; + * -Keys for switching between different macro (pre)sets, either a key for + * cycling through the configured presets or keys to directly select a preset. + */ +#define KEY_MACRO_RECORD_START 0x2b0 +#define KEY_MACRO_RECORD_STOP 0x2b1 +#define KEY_MACRO_PRESET_CYCLE 0x2b2 +#define KEY_MACRO_PRESET1 0x2b3 +#define KEY_MACRO_PRESET2 0x2b4 +#define KEY_MACRO_PRESET3 0x2b5 + +/* + * Some keyboards have a buildin LCD panel where the contents are controlled + * by the host. Often these have a number of keys directly below the LCD + * intended for controlling a menu shown on the LCD. These keys often don't + * have any labeling so we just name them KEY_KBD_LCD_MENU# + */ +#define KEY_KBD_LCD_MENU1 0x2b8 +#define KEY_KBD_LCD_MENU2 0x2b9 +#define KEY_KBD_LCD_MENU3 0x2ba +#define KEY_KBD_LCD_MENU4 0x2bb +#define KEY_KBD_LCD_MENU5 0x2bc #define BTN_TRIGGER_HAPPY 0x2c0 #define BTN_TRIGGER_HAPPY1 0x2c0 diff --git a/include/standard-headers/linux/input.h b/include/standard-headers/linux/input.h index d8914f25a5..f89c986190 100644 --- a/include/standard-headers/linux/input.h +++ b/include/standard-headers/linux/input.h @@ -31,6 +31,7 @@ struct input_event { unsigned long __sec; #if defined(__sparc__) && defined(__arch64__) unsigned int __usec; + unsigned int __pad; #else unsigned long __usec; #endif diff --git a/include/standard-headers/linux/pci_regs.h b/include/standard-headers/linux/pci_regs.h index 29d6e93fd1..5437690483 100644 --- a/include/standard-headers/linux/pci_regs.h +++ b/include/standard-headers/linux/pci_regs.h @@ -34,6 +34,7 @@ * of which the first 64 bytes are standardized as follows: */ #define PCI_STD_HEADER_SIZEOF 64 +#define PCI_STD_NUM_BARS 6 /* Number of standard BARs */ #define PCI_VENDOR_ID 0x00 /* 16 bits */ #define PCI_DEVICE_ID 0x02 /* 16 bits */ #define PCI_COMMAND 0x04 /* 16 bits */ @@ -673,6 +674,9 @@ #define PCI_EXP_LNKCTL2_TLS_8_0GT 0x0003 /* Supported Speed 8GT/s */ #define PCI_EXP_LNKCTL2_TLS_16_0GT 0x0004 /* Supported Speed 16GT/s */ #define PCI_EXP_LNKCTL2_TLS_32_0GT 0x0005 /* Supported Speed 32GT/s */ +#define PCI_EXP_LNKCTL2_ENTER_COMP 0x0010 /* Enter Compliance */ +#define PCI_EXP_LNKCTL2_TX_MARGIN 0x0380 /* Transmit Margin */ +#define PCI_EXP_LNKCTL2_HASD 0x0020 /* HW Autonomous Speed Disable */ #define PCI_EXP_LNKSTA2 50 /* Link Status 2 */ #define PCI_CAP_EXP_ENDPOINT_SIZEOF_V2 52 /* v2 endpoints with link end here */ #define PCI_EXP_SLTCAP2 52 /* Slot Capabilities 2 */ diff --git a/include/standard-headers/linux/virtio_ring.h b/include/standard-headers/linux/virtio_ring.h index 306cd41147..f230fed479 100644 --- a/include/standard-headers/linux/virtio_ring.h +++ b/include/standard-headers/linux/virtio_ring.h @@ -167,7 +167,7 @@ static inline void vring_init(struct vring *vr, unsigned int num, void *p, { vr->num = num; vr->desc = p; - vr->avail = p + num*sizeof(struct vring_desc); + vr->avail = (struct vring_avail *)((char *)p + num * sizeof(struct vring_desc)); vr->used = (void *)(((uintptr_t)&vr->avail->ring[num] + sizeof(__virtio16) + align-1) & ~(align - 1)); } diff --git a/include/standard-headers/rdma/vmw_pvrdma-abi.h b/include/standard-headers/rdma/vmw_pvrdma-abi.h index 336a8d596f..0989426a3f 100644 --- a/include/standard-headers/rdma/vmw_pvrdma-abi.h +++ b/include/standard-headers/rdma/vmw_pvrdma-abi.h @@ -179,6 +179,11 @@ struct pvrdma_create_qp { uint64_t __attribute__((aligned(8))) qp_addr; }; +struct pvrdma_create_qp_resp { + uint32_t qpn; + uint32_t qp_handle; +}; + /* PVRDMA masked atomic compare and swap */ struct pvrdma_ex_cmp_swap { uint64_t __attribute__((aligned(8))) swap_val; diff --git a/include/sysemu/accel.h b/include/sysemu/accel.h index 8eb60b870b..47e5788530 100644 --- a/include/sysemu/accel.h +++ b/include/sysemu/accel.h @@ -64,10 +64,12 @@ typedef struct AccelClass { #define ACCEL_GET_CLASS(obj) \ OBJECT_GET_CLASS(AccelClass, (obj), TYPE_ACCEL) -extern unsigned long tcg_tb_size; +AccelClass *accel_find(const char *opt_name); +int accel_init_machine(AccelState *accel, MachineState *ms); -void configure_accelerator(MachineState *ms, const char *progname); /* Called just before os_setup_post (ie just before drop OS privs) */ void accel_setup_post(MachineState *ms); +AccelState *current_accel(void); + #endif diff --git a/include/sysemu/arch_init.h b/include/sysemu/arch_init.h index 62c6fe4cf1..71a7a285ee 100644 --- a/include/sysemu/arch_init.h +++ b/include/sysemu/arch_init.h @@ -24,6 +24,9 @@ enum { QEMU_ARCH_NIOS2 = (1 << 17), QEMU_ARCH_HPPA = (1 << 18), QEMU_ARCH_RISCV = (1 << 19), + QEMU_ARCH_RX = (1 << 20), + + QEMU_ARCH_NONE = (1 << 31), }; extern const uint32_t arch_type; diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h index b198deca0b..9bbdbd63d7 100644 --- a/include/sysemu/block-backend.h +++ b/include/sysemu/block-backend.h @@ -171,7 +171,6 @@ BlockAIOCB *blk_aio_pdiscard(BlockBackend *blk, int64_t offset, int bytes, BlockCompletionFunc *cb, void *opaque); void blk_aio_cancel(BlockAIOCB *acb); void blk_aio_cancel_async(BlockAIOCB *acb); -int blk_co_ioctl(BlockBackend *blk, unsigned long int req, void *buf); int blk_ioctl(BlockBackend *blk, unsigned long int req, void *buf); BlockAIOCB *blk_aio_ioctl(BlockBackend *blk, unsigned long int req, void *buf, BlockCompletionFunc *cb, void *opaque); diff --git a/include/sysemu/blockdev.h b/include/sysemu/blockdev.h index 900c0de956..193d396e63 100644 --- a/include/sysemu/blockdev.h +++ b/include/sysemu/blockdev.h @@ -58,8 +58,4 @@ QemuOpts *drive_add(BlockInterfaceType type, int index, const char *file, DriveInfo *drive_new(QemuOpts *arg, BlockInterfaceType block_default_type, Error **errp); -/* device-hotplug */ - -void hmp_commit(Monitor *mon, const QDict *qdict); -void hmp_drive_del(Monitor *mon, const QDict *qdict); #endif diff --git a/include/sysemu/bt.h b/include/sysemu/bt.h deleted file mode 100644 index 2fd8c0f14b..0000000000 --- a/include/sysemu/bt.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef SYSEMU_BT_H -#define SYSEMU_BT_H - -/* BT HCI info */ - -typedef struct HCIInfo { - int (*bdaddr_set)(struct HCIInfo *hci, const uint8_t *bd_addr); - void (*cmd_send)(struct HCIInfo *hci, const uint8_t *data, int len); - void (*sco_send)(struct HCIInfo *hci, const uint8_t *data, int len); - void (*acl_send)(struct HCIInfo *hci, const uint8_t *data, int len); - void *opaque; - void (*evt_recv)(void *opaque, const uint8_t *data, int len); - void (*acl_recv)(void *opaque, const uint8_t *data, int len); -} HCIInfo; - -/* bt-host.c */ -struct HCIInfo *bt_host_hci(const char *id); -struct HCIInfo *qemu_next_hci(void); - -#endif diff --git a/include/sysemu/cpus.h b/include/sysemu/cpus.h index 32c05f27e7..3c1da6a018 100644 --- a/include/sysemu/cpus.h +++ b/include/sysemu/cpus.h @@ -40,6 +40,4 @@ extern int smp_threads; void list_cpus(const char *optarg); -void qemu_tcg_configure(QemuOpts *opts, Error **errp); - #endif diff --git a/include/sysemu/cryptodev.h b/include/sysemu/cryptodev.h index a9afb7e5b5..35eab06d0e 100644 --- a/include/sysemu/cryptodev.h +++ b/include/sysemu/cryptodev.h @@ -143,7 +143,7 @@ typedef struct CryptoDevBackendSymOpInfo { uint8_t *dst; uint8_t *aad_data; uint8_t *digest_result; - uint8_t data[0]; + uint8_t data[]; } CryptoDevBackendSymOpInfo; typedef struct CryptoDevBackendClass { diff --git a/include/sysemu/hostmem.h b/include/sysemu/hostmem.h index 4dbdadd39e..8276e53683 100644 --- a/include/sysemu/hostmem.h +++ b/include/sysemu/hostmem.h @@ -27,6 +27,22 @@ #define MEMORY_BACKEND_CLASS(klass) \ OBJECT_CLASS_CHECK(HostMemoryBackendClass, (klass), TYPE_MEMORY_BACKEND) +/* hostmem-ram.c */ +/** + * @TYPE_MEMORY_BACKEND_RAM: + * name of backend that uses mmap on the anonymous RAM + */ + +#define TYPE_MEMORY_BACKEND_RAM "memory-backend-ram" + +/* hostmem-file.c */ +/** + * @TYPE_MEMORY_BACKEND_FILE: + * name of backend that uses mmap on a file descriptor + */ +#define TYPE_MEMORY_BACKEND_FILE "memory-backend-file" + +typedef struct HostMemoryBackend HostMemoryBackend; typedef struct HostMemoryBackendClass HostMemoryBackendClass; /** @@ -45,6 +61,7 @@ struct HostMemoryBackendClass { * @parent: opaque parent object container * @size: amount of memory backend provides * @mr: MemoryRegion representing host memory belonging to backend + * @prealloc_threads: number of threads to be used for preallocatining RAM */ struct HostMemoryBackend { /* private */ @@ -53,7 +70,8 @@ struct HostMemoryBackend { /* protected */ uint64_t size; bool merge, dump, use_canonical_path; - bool prealloc, force_prealloc, is_mapped, share; + bool prealloc, is_mapped, share; + uint32_t prealloc_threads; DECLARE_BITMAP(host_nodes, MAX_NODES + 1); HostMemPolicy policy; diff --git a/include/sysemu/kvm.h b/include/sysemu/kvm.h index 9fe233b9bf..141342de98 100644 --- a/include/sysemu/kvm.h +++ b/include/sysemu/kvm.h @@ -515,14 +515,16 @@ int kvm_irqchip_add_irqfd_notifier(KVMState *s, EventNotifier *n, int kvm_irqchip_remove_irqfd_notifier(KVMState *s, EventNotifier *n, qemu_irq irq); void kvm_irqchip_set_qemuirq_gsi(KVMState *s, qemu_irq irq, int gsi); -void kvm_pc_gsi_handler(void *opaque, int n, int level); void kvm_pc_setup_irq_routing(bool pci_enabled); void kvm_init_irq_routing(KVMState *s); +bool kvm_kernel_irqchip_allowed(void); +bool kvm_kernel_irqchip_required(void); +bool kvm_kernel_irqchip_split(void); + /** * kvm_arch_irqchip_create: * @KVMState: The KVMState pointer - * @MachineState: The MachineState pointer * * Allow architectures to create an in-kernel irq chip themselves. * @@ -530,7 +532,7 @@ void kvm_init_irq_routing(KVMState *s); * 0: irq chip was not created * > 0: irq chip was created */ -int kvm_arch_irqchip_create(MachineState *ms, KVMState *s); +int kvm_arch_irqchip_create(KVMState *s); /** * kvm_set_one_reg - set a register value in KVM via KVM_SET_ONE_REG ioctl diff --git a/include/sysemu/numa.h b/include/sysemu/numa.h index ae9c41d02b..ad58ee88f7 100644 --- a/include/sysemu/numa.h +++ b/include/sysemu/numa.h @@ -14,10 +14,35 @@ struct CPUArchId; #define NUMA_DISTANCE_MAX 254 #define NUMA_DISTANCE_UNREACHABLE 255 +/* the value of AcpiHmatLBInfo flags */ +enum { + HMAT_LB_MEM_MEMORY = 0, + HMAT_LB_MEM_CACHE_1ST_LEVEL = 1, + HMAT_LB_MEM_CACHE_2ND_LEVEL = 2, + HMAT_LB_MEM_CACHE_3RD_LEVEL = 3, + HMAT_LB_LEVELS /* must be the last entry */ +}; + +/* the value of AcpiHmatLBInfo data type */ +enum { + HMAT_LB_DATA_ACCESS_LATENCY = 0, + HMAT_LB_DATA_READ_LATENCY = 1, + HMAT_LB_DATA_WRITE_LATENCY = 2, + HMAT_LB_DATA_ACCESS_BANDWIDTH = 3, + HMAT_LB_DATA_READ_BANDWIDTH = 4, + HMAT_LB_DATA_WRITE_BANDWIDTH = 5, + HMAT_LB_TYPES /* must be the last entry */ +}; + +#define UINT16_BITS 16 + struct NodeInfo { uint64_t node_mem; struct HostMemoryBackend *node_memdev; bool present; + bool has_cpu; + uint8_t lb_info_provided; + uint16_t initiator; uint8_t distance[MAX_NODES]; }; @@ -26,6 +51,31 @@ struct NumaNodeMem { uint64_t node_plugged_mem; }; +struct HMAT_LB_Data { + uint8_t initiator; + uint8_t target; + uint64_t data; +}; +typedef struct HMAT_LB_Data HMAT_LB_Data; + +struct HMAT_LB_Info { + /* Indicates it's memory or the specified level memory side cache. */ + uint8_t hierarchy; + + /* Present the type of data, access/read/write latency or bandwidth. */ + uint8_t data_type; + + /* The range bitmap of bandwidth for calculating common base */ + uint64_t range_bitmap; + + /* The common base unit for latencies or bandwidths */ + uint64_t base; + + /* Array to store the latencies or bandwidths */ + GArray *list; +}; +typedef struct HMAT_LB_Info HMAT_LB_Info; + struct NumaState { /* Number of NUMA nodes */ int num_nodes; @@ -33,13 +83,26 @@ struct NumaState { /* Allow setting NUMA distance for different NUMA nodes */ bool have_numa_distance; + /* Detect if HMAT support is enabled. */ + bool hmat_enabled; + /* NUMA nodes information */ NodeInfo nodes[MAX_NODES]; + + /* NUMA nodes HMAT Locality Latency and Bandwidth Information */ + HMAT_LB_Info *hmat_lb[HMAT_LB_LEVELS][HMAT_LB_TYPES]; + + /* Memory Side Cache Information Structure */ + NumaHmatCacheOptions *hmat_cache[MAX_NODES][HMAT_LB_LEVELS]; }; typedef struct NumaState NumaState; void set_numa_options(MachineState *ms, NumaOptions *object, Error **errp); void parse_numa_opts(MachineState *ms); +void parse_numa_hmat_lb(NumaState *numa_state, NumaHmatLBOptions *node, + Error **errp); +void parse_numa_hmat_cache(MachineState *ms, NumaHmatCacheOptions *node, + Error **errp); void numa_complete_configuration(MachineState *ms); void query_numa_node_mem(NumaNodeMem node_mem[], MachineState *ms); extern QemuOptsList qemu_numa_opts; @@ -49,5 +112,6 @@ void numa_default_auto_assign_ram(MachineClass *mc, NodeInfo *nodes, int nb_nodes, ram_addr_t size); void numa_cpu_pre_plug(const struct CPUArchId *slot, DeviceState *dev, Error **errp); +bool numa_uses_legacy_mem(void); #endif diff --git a/include/sysemu/os-win32.h b/include/sysemu/os-win32.h index ff18b23db1..d8978e28c0 100644 --- a/include/sysemu/os-win32.h +++ b/include/sysemu/os-win32.h @@ -67,8 +67,6 @@ int getpagesize(void); # define EPROTONOSUPPORT EINVAL #endif -int setenv(const char *name, const char *value, int overwrite); - typedef struct { long tv_sec; long tv_usec; diff --git a/include/sysemu/qtest.h b/include/sysemu/qtest.h index 5ed09c80b1..eedd3664f0 100644 --- a/include/sysemu/qtest.h +++ b/include/sysemu/qtest.h @@ -26,4 +26,8 @@ bool qtest_driver(void); void qtest_server_init(const char *qtest_chrdev, const char *qtest_log, Error **errp); +void qtest_server_set_send_handler(void (*send)(void *, const char *), + void *opaque); +void qtest_server_inproc_recv(void *opaque, const char *buf); + #endif diff --git a/include/sysemu/replay.h b/include/sysemu/replay.h index 8df517298c..5471bb514d 100644 --- a/include/sysemu/replay.h +++ b/include/sysemu/replay.h @@ -117,6 +117,13 @@ int64_t replay_read_clock(ReplayClockKind kind); ? replay_save_clock((clock), (value), cpu_get_icount_raw_locked()) \ : (value)) +/* Processing data from random generators */ + +/* Saves the values from the random number generator */ +void replay_save_random(int ret, void *buf, size_t len); +/* Loads the saved values for the random number generator */ +int replay_read_random(void *buf, size_t len); + /* Events */ /*! Called when qemu shutdown is requested. */ diff --git a/include/sysemu/runstate.h b/include/sysemu/runstate.h index 0b41555609..f760094858 100644 --- a/include/sysemu/runstate.h +++ b/include/sysemu/runstate.h @@ -63,6 +63,7 @@ ShutdownCause qemu_reset_requested_get(void); void qemu_system_killed(int signal, pid_t pid); void qemu_system_reset(ShutdownCause reason); void qemu_system_guest_panicked(GuestPanicInformation *info); +void qemu_system_guest_crashloaded(GuestPanicInformation *info); #endif diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h index 80c57fdc4e..ef81302e1a 100644 --- a/include/sysemu/sysemu.h +++ b/include/sysemu/sysemu.h @@ -32,7 +32,6 @@ typedef enum { } VGAInterfaceType; extern int vga_interface_type; -#define xenfb_enabled (vga_interface_type == VGA_XENFB) extern int graphic_width; extern int graphic_height; @@ -42,9 +41,7 @@ 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_quit; extern int no_shutdown; extern int old_param; extern int boot_menu; @@ -53,8 +50,6 @@ extern uint8_t *boot_splash_filedata; extern bool enable_mlock; extern bool enable_cpu_pm; extern QEMUClockType rtc_clock; -extern const char *mem_path; -extern int mem_prealloc; #define MAX_OPTION_ROMS 16 typedef struct QEMUOptionRom { @@ -68,9 +63,6 @@ extern int nb_option_roms; extern const char *prom_envs[MAX_PROM_ENVS]; extern unsigned int nb_prom_envs; -/* generic hotplug */ -void hmp_drive_add(Monitor *mon, const QDict *qdict); - /* pcie aer error injection */ void hmp_pcie_aer_inject_error(Monitor *mon, const QDict *qdict); @@ -118,6 +110,10 @@ QemuOpts *qemu_get_machine_opts(void); bool defaults_enabled(void); +void qemu_init(int argc, char **argv, char **envp); +void qemu_main_loop(void); +void qemu_cleanup(void); + extern QemuOptsList qemu_legacy_drive_opts; extern QemuOptsList qemu_common_drive_opts; extern QemuOptsList qemu_drive_opts; @@ -128,7 +124,6 @@ extern QemuOptsList qemu_netdev_opts; extern QemuOptsList qemu_nic_opts; extern QemuOptsList qemu_net_opts; extern QemuOptsList qemu_global_opts; -extern QemuOptsList qemu_mon_opts; extern QemuOptsList qemu_semihosting_config_opts; #endif diff --git a/include/sysemu/tpm.h b/include/sysemu/tpm.h index 5b541a71c8..f37851b1aa 100644 --- a/include/sysemu/tpm.h +++ b/include/sysemu/tpm.h @@ -43,13 +43,17 @@ typedef struct TPMIfClass { enum TPMVersion (*get_version)(TPMIf *obj); } TPMIfClass; -#define TYPE_TPM_TIS "tpm-tis" +#define TYPE_TPM_TIS_ISA "tpm-tis" +#define TYPE_TPM_TIS_SYSBUS "tpm-tis-device" #define TYPE_TPM_CRB "tpm-crb" +#define TYPE_TPM_SPAPR "tpm-spapr" -#define TPM_IS_TIS(chr) \ - object_dynamic_cast(OBJECT(chr), TYPE_TPM_TIS) +#define TPM_IS_TIS_ISA(chr) \ + object_dynamic_cast(OBJECT(chr), TYPE_TPM_TIS_ISA) #define TPM_IS_CRB(chr) \ object_dynamic_cast(OBJECT(chr), TYPE_TPM_CRB) +#define TPM_IS_SPAPR(chr) \ + object_dynamic_cast(OBJECT(chr), TYPE_TPM_SPAPR) /* returns NULL unless there is exactly one TPM device */ static inline TPMIf *tpm_find(void) diff --git a/include/sysemu/whpx.h b/include/sysemu/whpx.h index 4794e8effe..a84b49e749 100644 --- a/include/sysemu/whpx.h +++ b/include/sysemu/whpx.h @@ -35,4 +35,11 @@ int whpx_enabled(void); #endif /* CONFIG_WHPX */ +/* state subset only touched by the VCPU itself during runtime */ +#define WHPX_SET_RUNTIME_STATE 1 +/* state subset modified during VCPU reset */ +#define WHPX_SET_RESET_STATE 2 +/* full state set, modified during initialization or on vmload */ +#define WHPX_SET_FULL_STATE 3 + #endif /* QEMU_WHPX_H */ diff --git a/tcg/tcg-gvec-desc.h b/include/tcg/tcg-gvec-desc.h similarity index 100% rename from tcg/tcg-gvec-desc.h rename to include/tcg/tcg-gvec-desc.h diff --git a/tcg/tcg-mo.h b/include/tcg/tcg-mo.h similarity index 100% rename from tcg/tcg-mo.h rename to include/tcg/tcg-mo.h diff --git a/tcg/tcg-op-gvec.h b/include/tcg/tcg-op-gvec.h similarity index 97% rename from tcg/tcg-op-gvec.h rename to include/tcg/tcg-op-gvec.h index 830d68f697..74534e2480 100644 --- a/tcg/tcg-op-gvec.h +++ b/include/tcg/tcg-op-gvec.h @@ -83,6 +83,13 @@ void tcg_gen_gvec_4_ptr(uint32_t dofs, uint32_t aofs, uint32_t bofs, uint32_t maxsz, int32_t data, gen_helper_gvec_4_ptr *fn); +typedef void gen_helper_gvec_5_ptr(TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_ptr, + TCGv_ptr, TCGv_ptr, TCGv_i32); +void tcg_gen_gvec_5_ptr(uint32_t dofs, uint32_t aofs, uint32_t bofs, + uint32_t cofs, uint32_t eofs, TCGv_ptr ptr, + uint32_t oprsz, uint32_t maxsz, int32_t data, + gen_helper_gvec_5_ptr *fn); + /* Expand a gvec operation. Either inline or out-of-line depending on the actual vector size and the operations supported by the host. */ typedef struct { diff --git a/tcg/tcg-op.h b/include/tcg/tcg-op.h similarity index 99% rename from tcg/tcg-op.h rename to include/tcg/tcg-op.h index 4af272daa5..230db6e022 100644 --- a/tcg/tcg-op.h +++ b/include/tcg/tcg-op.h @@ -25,7 +25,7 @@ #ifndef TCG_TCG_OP_H #define TCG_TCG_OP_H -#include "tcg.h" +#include "tcg/tcg.h" #include "exec/helper-proto.h" #include "exec/helper-gen.h" diff --git a/tcg/tcg-opc.h b/include/tcg/tcg-opc.h similarity index 100% rename from tcg/tcg-opc.h rename to include/tcg/tcg-opc.h diff --git a/tcg/tcg.h b/include/tcg/tcg.h similarity index 96% rename from tcg/tcg.h rename to include/tcg/tcg.h index 92ca10dffc..c48bd76b0a 100644 --- a/tcg/tcg.h +++ b/include/tcg/tcg.h @@ -31,7 +31,7 @@ #include "qemu/bitops.h" #include "qemu/plugin.h" #include "qemu/queue.h" -#include "tcg-mo.h" +#include "tcg/tcg-mo.h" #include "tcg-target.h" #include "qemu/int128.h" @@ -211,7 +211,7 @@ typedef uint64_t TCGRegSet; typedef enum TCGOpcode { #define DEF(name, oargs, iargs, cargs, flags) INDEX_op_ ## name, -#include "tcg-opc.h" +#include "tcg/tcg-opc.h" #undef DEF NB_OPS, } TCGOpcode; @@ -267,7 +267,7 @@ struct TCGLabel { typedef struct TCGPool { struct TCGPool *next; int size; - uint8_t data[0] __attribute__ ((aligned)); + uint8_t data[] __attribute__ ((aligned)); } TCGPool; #define TCG_POOL_CHUNK_SIZE 32768 @@ -1290,27 +1290,6 @@ void helper_be_stl_mmu(CPUArchState *env, target_ulong addr, uint32_t val, void helper_be_stq_mmu(CPUArchState *env, target_ulong addr, uint64_t val, TCGMemOpIdx oi, uintptr_t retaddr); -uint8_t helper_ret_ldub_cmmu(CPUArchState *env, target_ulong addr, - TCGMemOpIdx oi, uintptr_t retaddr); -int8_t helper_ret_ldsb_cmmu(CPUArchState *env, target_ulong addr, - TCGMemOpIdx oi, uintptr_t retaddr); -uint16_t helper_le_lduw_cmmu(CPUArchState *env, target_ulong addr, - TCGMemOpIdx oi, uintptr_t retaddr); -int16_t helper_le_ldsw_cmmu(CPUArchState *env, target_ulong addr, - TCGMemOpIdx oi, uintptr_t retaddr); -uint32_t helper_le_ldl_cmmu(CPUArchState *env, target_ulong addr, - TCGMemOpIdx oi, uintptr_t retaddr); -uint64_t helper_le_ldq_cmmu(CPUArchState *env, target_ulong addr, - TCGMemOpIdx oi, uintptr_t retaddr); -uint16_t helper_be_lduw_cmmu(CPUArchState *env, target_ulong addr, - TCGMemOpIdx oi, uintptr_t retaddr); -int16_t helper_be_ldsw_cmmu(CPUArchState *env, target_ulong addr, - TCGMemOpIdx oi, uintptr_t retaddr); -uint32_t helper_be_ldl_cmmu(CPUArchState *env, target_ulong addr, - TCGMemOpIdx oi, uintptr_t retaddr); -uint64_t helper_be_ldq_cmmu(CPUArchState *env, target_ulong addr, - TCGMemOpIdx oi, uintptr_t retaddr); - /* Temporary aliases until backends are converted. */ #ifdef TARGET_WORDS_BIGENDIAN # define helper_ret_ldsw_mmu helper_be_ldsw_mmu @@ -1322,10 +1301,6 @@ uint64_t helper_be_ldq_cmmu(CPUArchState *env, target_ulong addr, # define helper_ret_stw_mmu helper_be_stw_mmu # define helper_ret_stl_mmu helper_be_stl_mmu # define helper_ret_stq_mmu helper_be_stq_mmu -# define helper_ret_lduw_cmmu helper_be_lduw_cmmu -# define helper_ret_ldsw_cmmu helper_be_ldsw_cmmu -# define helper_ret_ldl_cmmu helper_be_ldl_cmmu -# define helper_ret_ldq_cmmu helper_be_ldq_cmmu #else # define helper_ret_ldsw_mmu helper_le_ldsw_mmu # define helper_ret_lduw_mmu helper_le_lduw_mmu @@ -1336,10 +1311,6 @@ uint64_t helper_be_ldq_cmmu(CPUArchState *env, target_ulong addr, # define helper_ret_stw_mmu helper_le_stw_mmu # define helper_ret_stl_mmu helper_le_stl_mmu # define helper_ret_stq_mmu helper_le_stq_mmu -# define helper_ret_lduw_cmmu helper_le_lduw_cmmu -# define helper_ret_ldsw_cmmu helper_le_ldsw_cmmu -# define helper_ret_ldl_cmmu helper_le_ldl_cmmu -# define helper_ret_ldq_cmmu helper_le_ldq_cmmu #endif uint32_t helper_atomic_cmpxchgb_mmu(CPUArchState *env, target_ulong addr, diff --git a/include/ui/console.h b/include/ui/console.h index 7c86c289b0..5e507baea4 100644 --- a/include/ui/console.h +++ b/include/ui/console.h @@ -365,6 +365,7 @@ static inline void console_write_ch(console_ch_t *dest, uint32_t ch) typedef struct GraphicHwOps { void (*invalidate)(void *opaque); void (*gfx_update)(void *opaque); + bool gfx_update_async; /* if true, calls graphic_hw_update_done() */ void (*text_update)(void *opaque, console_ch_t *text); void (*update_interval)(void *opaque, uint64_t interval); int (*ui_info)(void *opaque, uint32_t head, QemuUIInfo *info); @@ -380,6 +381,7 @@ void graphic_console_set_hwops(QemuConsole *con, void graphic_console_close(QemuConsole *con); void graphic_hw_update(QemuConsole *con); +void graphic_hw_update_done(QemuConsole *con); void graphic_hw_invalidate(QemuConsole *con); void graphic_hw_text_update(QemuConsole *con, console_ch_t *chardata); void graphic_hw_gl_block(QemuConsole *con, bool block); @@ -440,6 +442,7 @@ void qemu_display_register(QemuDisplay *ui); bool qemu_display_find_default(DisplayOptions *opts); void qemu_display_early_init(DisplayOptions *opts); void qemu_display_init(DisplayState *ds, DisplayOptions *opts); +void qemu_display_help(void); /* vnc.c */ void vnc_display_init(const char *id, Error **errp); diff --git a/include/ui/gtk.h b/include/ui/gtk.h index d9eedad976..d1b230848a 100644 --- a/include/ui/gtk.h +++ b/include/ui/gtk.h @@ -28,6 +28,8 @@ #include "ui/egl-context.h" #endif +#define MILLISEC_PER_SEC 1000000 + typedef struct GtkDisplayState GtkDisplayState; typedef struct VirtualGfxConsole { diff --git a/include/ui/qemu-pixman.h b/include/ui/qemu-pixman.h index 0668109305..3b7cf70157 100644 --- a/include/ui/qemu-pixman.h +++ b/include/ui/qemu-pixman.h @@ -90,4 +90,6 @@ void qemu_pixman_glyph_render(pixman_image_t *glyph, pixman_color_t *bgcol, int x, int y, int cw, int ch); +G_DEFINE_AUTOPTR_CLEANUP_FUNC(pixman_image_t, qemu_pixman_image_unref) + #endif /* QEMU_PIXMAN_H */ diff --git a/include/user/syscall-trace.h b/include/user/syscall-trace.h index 9e60473643..79fd3e5aa9 100644 --- a/include/user/syscall-trace.h +++ b/include/user/syscall-trace.h @@ -10,6 +10,8 @@ #ifndef _SYSCALL_TRACE_H_ #define _SYSCALL_TRACE_H_ +#include "trace-root.h" + /* * These helpers just provide a common place for the various * subsystems that want to track syscalls to put their hooks in. We 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/job-qmp.c b/job-qmp.c index fbfed25a00..f9a58832e1 100644 --- a/job-qmp.c +++ b/job-qmp.c @@ -114,7 +114,16 @@ void qmp_job_finalize(const char *id, Error **errp) } trace_qmp_job_finalize(job); + job_ref(job); job_finalize(job, errp); + + /* + * Job's context might have changed via job_finalize (and job_txn_apply + * automatically acquires the new one), so make sure we release the correct + * one. + */ + aio_context = job->aio_context; + job_unref(job); aio_context_release(aio_context); } @@ -143,8 +152,8 @@ static JobInfo *job_query_single(Job *job, Error **errp) .id = g_strdup(job->id), .type = job_type(job), .status = job->status, - .current_progress = job->progress_current, - .total_progress = job->progress_total, + .current_progress = job->progress.current, + .total_progress = job->progress.total, .has_error = !!job->err, .error = job->err ? \ g_strdup(error_get_pretty(job->err)) : NULL, diff --git a/job.c b/job.c index 04409b40aa..53be57a3a0 100644 --- a/job.c +++ b/job.c @@ -136,17 +136,38 @@ static void job_txn_del_job(Job *job) } } -static int job_txn_apply(JobTxn *txn, int fn(Job *)) +static int job_txn_apply(Job *job, int fn(Job *)) { - Job *job, *next; + AioContext *inner_ctx; + Job *other_job, *next; + JobTxn *txn = job->txn; int rc = 0; - QLIST_FOREACH_SAFE(job, &txn->jobs, txn_list, next) { - rc = fn(job); + /* + * Similar to job_completed_txn_abort, we take each job's lock before + * applying fn, but since we assume that outer_ctx is held by the caller, + * we need to release it here to avoid holding the lock twice - which would + * break AIO_WAIT_WHILE from within fn. + */ + job_ref(job); + aio_context_release(job->aio_context); + + QLIST_FOREACH_SAFE(other_job, &txn->jobs, txn_list, next) { + inner_ctx = other_job->aio_context; + aio_context_acquire(inner_ctx); + rc = fn(other_job); + aio_context_release(inner_ctx); if (rc) { break; } } + + /* + * Note that job->aio_context might have been changed by calling fn, so we + * can't use a local variable to cache it. + */ + aio_context_acquire(job->aio_context); + job_unref(job); return rc; } @@ -369,17 +390,17 @@ void job_unref(Job *job) void job_progress_update(Job *job, uint64_t done) { - job->progress_current += done; + progress_work_done(&job->progress, done); } void job_progress_set_remaining(Job *job, uint64_t remaining) { - job->progress_total = job->progress_current + remaining; + progress_set_remaining(&job->progress, remaining); } void job_progress_increase_remaining(Job *job, uint64_t delta) { - job->progress_total += delta; + progress_increase_remaining(&job->progress, delta); } void job_event_cancelled(Job *job) @@ -774,11 +795,11 @@ static void job_do_finalize(Job *job) assert(job && job->txn); /* prepare the transaction to complete */ - rc = job_txn_apply(job->txn, job_prepare); + rc = job_txn_apply(job, job_prepare); if (rc) { job_completed_txn_abort(job); } else { - job_txn_apply(job->txn, job_finalize_single); + job_txn_apply(job, job_finalize_single); } } @@ -824,10 +845,10 @@ static void job_completed_txn_success(Job *job) assert(other_job->ret == 0); } - job_txn_apply(txn, job_transition_to_pending); + job_txn_apply(job, job_transition_to_pending); /* If no jobs need manual finalization, automatically do so */ - if (job_txn_apply(txn, job_needs_finalize) == 0) { + if (job_txn_apply(job, job_needs_finalize) == 0) { job_do_finalize(job); } } @@ -849,9 +870,10 @@ static void job_completed(Job *job) static void job_exit(void *opaque) { Job *job = (Job *)opaque; - AioContext *ctx = job->aio_context; + AioContext *ctx; - aio_context_acquire(ctx); + job_ref(job); + aio_context_acquire(job->aio_context); /* This is a lie, we're not quiescent, but still doing the completion * callbacks. However, completion callbacks tend to involve operations that @@ -862,6 +884,14 @@ static void job_exit(void *opaque) job_completed(job); + /* + * Note that calling job_completed can move the job to a different + * aio_context, so we cannot cache from above. job_txn_apply takes care of + * acquiring the new lock, and we ref/unref to avoid job_completed freeing + * the job underneath us. + */ + ctx = job->aio_context; + job_unref(job); aio_context_release(ctx); } diff --git a/linux-headers/asm-arm/kvm.h b/linux-headers/asm-arm/kvm.h index 9d379d3372..0db5644e27 100644 --- a/linux-headers/asm-arm/kvm.h +++ b/linux-headers/asm-arm/kvm.h @@ -131,8 +131,9 @@ struct kvm_vcpu_events { struct { __u8 serror_pending; __u8 serror_has_esr; + __u8 ext_dabt_pending; /* Align it to 8 bytes */ - __u8 pad[6]; + __u8 pad[5]; __u64 serror_esr; } exception; __u32 reserved[12]; diff --git a/linux-headers/asm-arm/unistd-common.h b/linux-headers/asm-arm/unistd-common.h index eb5d361b11..23de64e44c 100644 --- a/linux-headers/asm-arm/unistd-common.h +++ b/linux-headers/asm-arm/unistd-common.h @@ -390,5 +390,7 @@ #define __NR_fspick (__NR_SYSCALL_BASE + 433) #define __NR_pidfd_open (__NR_SYSCALL_BASE + 434) #define __NR_clone3 (__NR_SYSCALL_BASE + 435) +#define __NR_openat2 (__NR_SYSCALL_BASE + 437) +#define __NR_pidfd_getfd (__NR_SYSCALL_BASE + 438) #endif /* _ASM_ARM_UNISTD_COMMON_H */ diff --git a/linux-headers/asm-arm64/kvm.h b/linux-headers/asm-arm64/kvm.h index 0ce6e49f3a..9e34f0f875 100644 --- a/linux-headers/asm-arm64/kvm.h +++ b/linux-headers/asm-arm64/kvm.h @@ -164,8 +164,9 @@ struct kvm_vcpu_events { struct { __u8 serror_pending; __u8 serror_has_esr; + __u8 ext_dabt_pending; /* Align it to 8 bytes */ - __u8 pad[6]; + __u8 pad[5]; __u64 serror_esr; } exception; __u32 reserved[12]; @@ -219,10 +220,18 @@ struct kvm_vcpu_events { #define KVM_REG_ARM_PTIMER_CVAL ARM64_SYS_REG(3, 3, 14, 2, 2) #define KVM_REG_ARM_PTIMER_CNT ARM64_SYS_REG(3, 3, 14, 0, 1) -/* EL0 Virtual Timer Registers */ +/* + * EL0 Virtual Timer Registers + * + * WARNING: + * KVM_REG_ARM_TIMER_CVAL and KVM_REG_ARM_TIMER_CNT are not defined + * with the appropriate register encodings. Their values have been + * accidentally swapped. As this is set API, the definitions here + * must be used, rather than ones derived from the encodings. + */ #define KVM_REG_ARM_TIMER_CTL ARM64_SYS_REG(3, 3, 14, 3, 1) -#define KVM_REG_ARM_TIMER_CNT ARM64_SYS_REG(3, 3, 14, 3, 2) #define KVM_REG_ARM_TIMER_CVAL ARM64_SYS_REG(3, 3, 14, 0, 2) +#define KVM_REG_ARM_TIMER_CNT ARM64_SYS_REG(3, 3, 14, 3, 2) /* KVM-as-firmware specific pseudo-registers */ #define KVM_REG_ARM_FW (0x0014 << KVM_REG_ARM_COPROC_SHIFT) @@ -323,6 +332,8 @@ struct kvm_vcpu_events { #define KVM_ARM_VCPU_TIMER_CTRL 1 #define KVM_ARM_VCPU_TIMER_IRQ_VTIMER 0 #define KVM_ARM_VCPU_TIMER_IRQ_PTIMER 1 +#define KVM_ARM_VCPU_PVTIME_CTRL 2 +#define KVM_ARM_VCPU_PVTIME_IPA 0 /* KVM_IRQ_LINE irq field index values */ #define KVM_ARM_IRQ_VCPU2_SHIFT 28 diff --git a/linux-headers/asm-arm64/unistd.h b/linux-headers/asm-arm64/unistd.h index 4703d21866..f83a70e07d 100644 --- a/linux-headers/asm-arm64/unistd.h +++ b/linux-headers/asm-arm64/unistd.h @@ -19,5 +19,6 @@ #define __ARCH_WANT_NEW_STAT #define __ARCH_WANT_SET_GET_RLIMIT #define __ARCH_WANT_TIME32_SYSCALLS +#define __ARCH_WANT_SYS_CLONE3 #include diff --git a/linux-headers/asm-generic/mman-common.h b/linux-headers/asm-generic/mman-common.h index c160a5354e..f94f65d429 100644 --- a/linux-headers/asm-generic/mman-common.h +++ b/linux-headers/asm-generic/mman-common.h @@ -11,6 +11,8 @@ #define PROT_WRITE 0x2 /* page can be written */ #define PROT_EXEC 0x4 /* page can be executed */ #define PROT_SEM 0x8 /* page may be used for atomic ops */ +/* 0x10 reserved for arch-specific use */ +/* 0x20 reserved for arch-specific use */ #define PROT_NONE 0x0 /* page can not be accessed */ #define PROT_GROWSDOWN 0x01000000 /* mprotect flag: extend change to start of growsdown vma */ #define PROT_GROWSUP 0x02000000 /* mprotect flag: extend change to end of growsup vma */ diff --git a/linux-headers/asm-generic/unistd.h b/linux-headers/asm-generic/unistd.h index 1fc8faa6e9..3a3201e461 100644 --- a/linux-headers/asm-generic/unistd.h +++ b/linux-headers/asm-generic/unistd.h @@ -851,8 +851,13 @@ __SYSCALL(__NR_pidfd_open, sys_pidfd_open) __SYSCALL(__NR_clone3, sys_clone3) #endif +#define __NR_openat2 437 +__SYSCALL(__NR_openat2, sys_openat2) +#define __NR_pidfd_getfd 438 +__SYSCALL(__NR_pidfd_getfd, sys_pidfd_getfd) + #undef __NR_syscalls -#define __NR_syscalls 436 +#define __NR_syscalls 439 /* * 32 bit systems traditionally used different diff --git a/linux-headers/asm-mips/unistd_n32.h b/linux-headers/asm-mips/unistd_n32.h index 7dffe8e34e..aec9f6081a 100644 --- a/linux-headers/asm-mips/unistd_n32.h +++ b/linux-headers/asm-mips/unistd_n32.h @@ -364,6 +364,9 @@ #define __NR_fsmount (__NR_Linux + 432) #define __NR_fspick (__NR_Linux + 433) #define __NR_pidfd_open (__NR_Linux + 434) +#define __NR_clone3 (__NR_Linux + 435) +#define __NR_openat2 (__NR_Linux + 437) +#define __NR_pidfd_getfd (__NR_Linux + 438) #endif /* _ASM_MIPS_UNISTD_N32_H */ diff --git a/linux-headers/asm-mips/unistd_n64.h b/linux-headers/asm-mips/unistd_n64.h index f4592d6fc5..1c75d83df5 100644 --- a/linux-headers/asm-mips/unistd_n64.h +++ b/linux-headers/asm-mips/unistd_n64.h @@ -340,6 +340,9 @@ #define __NR_fsmount (__NR_Linux + 432) #define __NR_fspick (__NR_Linux + 433) #define __NR_pidfd_open (__NR_Linux + 434) +#define __NR_clone3 (__NR_Linux + 435) +#define __NR_openat2 (__NR_Linux + 437) +#define __NR_pidfd_getfd (__NR_Linux + 438) #endif /* _ASM_MIPS_UNISTD_N64_H */ diff --git a/linux-headers/asm-mips/unistd_o32.h b/linux-headers/asm-mips/unistd_o32.h index 04c6728352..660716e240 100644 --- a/linux-headers/asm-mips/unistd_o32.h +++ b/linux-headers/asm-mips/unistd_o32.h @@ -410,6 +410,9 @@ #define __NR_fsmount (__NR_Linux + 432) #define __NR_fspick (__NR_Linux + 433) #define __NR_pidfd_open (__NR_Linux + 434) +#define __NR_clone3 (__NR_Linux + 435) +#define __NR_openat2 (__NR_Linux + 437) +#define __NR_pidfd_getfd (__NR_Linux + 438) #endif /* _ASM_MIPS_UNISTD_O32_H */ diff --git a/linux-headers/asm-powerpc/kvm.h b/linux-headers/asm-powerpc/kvm.h index b0f72dea8b..264e266a85 100644 --- a/linux-headers/asm-powerpc/kvm.h +++ b/linux-headers/asm-powerpc/kvm.h @@ -667,6 +667,8 @@ struct kvm_ppc_cpu_char { /* PPC64 eXternal Interrupt Controller Specification */ #define KVM_DEV_XICS_GRP_SOURCES 1 /* 64-bit source attributes */ +#define KVM_DEV_XICS_GRP_CTRL 2 +#define KVM_DEV_XICS_NR_SERVERS 1 /* Layout of 64-bit source attribute values */ #define KVM_XICS_DESTINATION_SHIFT 0 @@ -683,6 +685,7 @@ struct kvm_ppc_cpu_char { #define KVM_DEV_XIVE_GRP_CTRL 1 #define KVM_DEV_XIVE_RESET 1 #define KVM_DEV_XIVE_EQ_SYNC 2 +#define KVM_DEV_XIVE_NR_SERVERS 3 #define KVM_DEV_XIVE_GRP_SOURCE 2 /* 64-bit source identifier */ #define KVM_DEV_XIVE_GRP_SOURCE_CONFIG 3 /* 64-bit source identifier */ #define KVM_DEV_XIVE_GRP_EQ_CONFIG 4 /* 64-bit EQ identifier */ diff --git a/linux-headers/asm-powerpc/unistd_32.h b/linux-headers/asm-powerpc/unistd_32.h index 5584cc1b4f..4ba8e32f73 100644 --- a/linux-headers/asm-powerpc/unistd_32.h +++ b/linux-headers/asm-powerpc/unistd_32.h @@ -418,6 +418,8 @@ #define __NR_fspick 433 #define __NR_pidfd_open 434 #define __NR_clone3 435 +#define __NR_openat2 437 +#define __NR_pidfd_getfd 438 #endif /* _ASM_POWERPC_UNISTD_32_H */ diff --git a/linux-headers/asm-powerpc/unistd_64.h b/linux-headers/asm-powerpc/unistd_64.h index 251bcff77e..ac20bb4f95 100644 --- a/linux-headers/asm-powerpc/unistd_64.h +++ b/linux-headers/asm-powerpc/unistd_64.h @@ -390,6 +390,8 @@ #define __NR_fspick 433 #define __NR_pidfd_open 434 #define __NR_clone3 435 +#define __NR_openat2 437 +#define __NR_pidfd_getfd 438 #endif /* _ASM_POWERPC_UNISTD_64_H */ diff --git a/linux-headers/asm-s390/unistd_32.h b/linux-headers/asm-s390/unistd_32.h index 7cce3ee296..e4a6b654f1 100644 --- a/linux-headers/asm-s390/unistd_32.h +++ b/linux-headers/asm-s390/unistd_32.h @@ -408,5 +408,7 @@ #define __NR_fspick 433 #define __NR_pidfd_open 434 #define __NR_clone3 435 +#define __NR_openat2 437 +#define __NR_pidfd_getfd 438 #endif /* _ASM_S390_UNISTD_32_H */ diff --git a/linux-headers/asm-s390/unistd_64.h b/linux-headers/asm-s390/unistd_64.h index 2371ff1e7a..472f732956 100644 --- a/linux-headers/asm-s390/unistd_64.h +++ b/linux-headers/asm-s390/unistd_64.h @@ -356,5 +356,7 @@ #define __NR_fspick 433 #define __NR_pidfd_open 434 #define __NR_clone3 435 +#define __NR_openat2 437 +#define __NR_pidfd_getfd 438 #endif /* _ASM_S390_UNISTD_64_H */ diff --git a/linux-headers/asm-x86/unistd_32.h b/linux-headers/asm-x86/unistd_32.h index e8ebec1cdc..f6e06fcfbd 100644 --- a/linux-headers/asm-x86/unistd_32.h +++ b/linux-headers/asm-x86/unistd_32.h @@ -426,5 +426,7 @@ #define __NR_fspick 433 #define __NR_pidfd_open 434 #define __NR_clone3 435 +#define __NR_openat2 437 +#define __NR_pidfd_getfd 438 #endif /* _ASM_X86_UNISTD_32_H */ diff --git a/linux-headers/asm-x86/unistd_64.h b/linux-headers/asm-x86/unistd_64.h index a2f863d549..924f826d2d 100644 --- a/linux-headers/asm-x86/unistd_64.h +++ b/linux-headers/asm-x86/unistd_64.h @@ -348,5 +348,7 @@ #define __NR_fspick 433 #define __NR_pidfd_open 434 #define __NR_clone3 435 +#define __NR_openat2 437 +#define __NR_pidfd_getfd 438 #endif /* _ASM_X86_UNISTD_64_H */ diff --git a/linux-headers/asm-x86/unistd_x32.h b/linux-headers/asm-x86/unistd_x32.h index 4cdc67d848..010307757b 100644 --- a/linux-headers/asm-x86/unistd_x32.h +++ b/linux-headers/asm-x86/unistd_x32.h @@ -301,6 +301,8 @@ #define __NR_fspick (__X32_SYSCALL_BIT + 433) #define __NR_pidfd_open (__X32_SYSCALL_BIT + 434) #define __NR_clone3 (__X32_SYSCALL_BIT + 435) +#define __NR_openat2 (__X32_SYSCALL_BIT + 437) +#define __NR_pidfd_getfd (__X32_SYSCALL_BIT + 438) #define __NR_rt_sigaction (__X32_SYSCALL_BIT + 512) #define __NR_rt_sigreturn (__X32_SYSCALL_BIT + 513) #define __NR_ioctl (__X32_SYSCALL_BIT + 514) diff --git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h index 3d9b18f7f8..265099100e 100644 --- a/linux-headers/linux/kvm.h +++ b/linux-headers/linux/kvm.h @@ -235,6 +235,7 @@ struct kvm_hyperv_exit { #define KVM_EXIT_S390_STSI 25 #define KVM_EXIT_IOAPIC_EOI 26 #define KVM_EXIT_HYPERV 27 +#define KVM_EXIT_ARM_NISV 28 /* For KVM_EXIT_INTERNAL_ERROR */ /* Emulate instruction failed. */ @@ -394,6 +395,11 @@ struct kvm_run { } eoi; /* KVM_EXIT_HYPERV */ struct kvm_hyperv_exit hyperv; + /* KVM_EXIT_ARM_NISV */ + struct { + __u64 esr_iss; + __u64 fault_ipa; + } arm_nisv; /* Fix the size of the union. */ char padding[256]; }; @@ -1000,6 +1006,10 @@ struct kvm_ppc_resize_hpt { #define KVM_CAP_PMU_EVENT_FILTER 173 #define KVM_CAP_ARM_IRQ_LINE_LAYOUT_2 174 #define KVM_CAP_HYPERV_DIRECT_TLBFLUSH 175 +#define KVM_CAP_PPC_GUEST_DEBUG_SSTEP 176 +#define KVM_CAP_ARM_NISV_TO_USER 177 +#define KVM_CAP_ARM_INJECT_EXT_DABT 178 +#define KVM_CAP_S390_VCPU_RESETS 179 #ifdef KVM_CAP_IRQ_ROUTING @@ -1227,6 +1237,8 @@ enum kvm_device_type { #define KVM_DEV_TYPE_ARM_VGIC_ITS KVM_DEV_TYPE_ARM_VGIC_ITS KVM_DEV_TYPE_XIVE, #define KVM_DEV_TYPE_XIVE KVM_DEV_TYPE_XIVE + KVM_DEV_TYPE_ARM_PV_TIME, +#define KVM_DEV_TYPE_ARM_PV_TIME KVM_DEV_TYPE_ARM_PV_TIME KVM_DEV_TYPE_MAX, }; @@ -1337,6 +1349,7 @@ struct kvm_s390_ucas_mapping { #define KVM_PPC_GET_CPU_CHAR _IOR(KVMIO, 0xb1, struct kvm_ppc_cpu_char) /* Available with KVM_CAP_PMU_EVENT_FILTER */ #define KVM_SET_PMU_EVENT_FILTER _IOW(KVMIO, 0xb2, struct kvm_pmu_event_filter) +#define KVM_PPC_SVM_OFF _IO(KVMIO, 0xb3) /* ioctl for vm fd */ #define KVM_CREATE_DEVICE _IOWR(KVMIO, 0xe0, struct kvm_create_device) @@ -1461,6 +1474,10 @@ struct kvm_enc_region { /* Available with KVM_CAP_ARM_SVE */ #define KVM_ARM_VCPU_FINALIZE _IOW(KVMIO, 0xc2, int) +/* Available with KVM_CAP_S390_VCPU_RESETS */ +#define KVM_S390_NORMAL_RESET _IO(KVMIO, 0xc3) +#define KVM_S390_CLEAR_RESET _IO(KVMIO, 0xc4) + /* Secure Encrypted Virtualization command */ enum sev_cmd_id { /* Guest initialization commands */ diff --git a/linux-headers/linux/psp-sev.h b/linux-headers/linux/psp-sev.h index 34c39690c0..31f971e896 100644 --- a/linux-headers/linux/psp-sev.h +++ b/linux-headers/linux/psp-sev.h @@ -58,6 +58,9 @@ typedef enum { SEV_RET_HWSEV_RET_PLATFORM, SEV_RET_HWSEV_RET_UNSAFE, SEV_RET_UNSUPPORTED, + SEV_RET_INVALID_PARAM, + SEV_RET_RESOURCE_LIMIT, + SEV_RET_SECURE_DATA_INVALID, SEV_RET_MAX, } sev_ret_code; diff --git a/linux-user/Makefile.objs b/linux-user/Makefile.objs index d2f33beb5e..1940910a73 100644 --- a/linux-user/Makefile.objs +++ b/linux-user/Makefile.objs @@ -5,6 +5,21 @@ obj-y = main.o syscall.o strace.o mmap.o signal.o \ obj-$(TARGET_HAS_BFLT) += flatload.o obj-$(TARGET_I386) += vm86.o -obj-$(TARGET_ARM) += arm/nwfpe/ -obj-$(TARGET_ARM) += arm/semihost.o obj-$(TARGET_AARCH64) += arm/semihost.o + +obj-$(TARGET_ALPHA) += alpha/ +obj-$(TARGET_ARM) += arm/ +obj-$(TARGET_HPPA) += hppa/ +obj-$(TARGET_I386) += i386/ +obj-$(TARGET_M68K) += m68k/ +obj-$(TARGET_MICROBLAZE) += microblaze/ +obj-$(TARGET_MIPS) += mips/ +obj-$(TARGET_MIPS64) += mips64/ +obj-$(TARGET_PPC) += ppc/ +obj-$(TARGET_PPC64) += ppc/ +obj-$(TARGET_S390X) += s390x/ +obj-$(TARGET_SH4) += sh4/ +obj-$(TARGET_SPARC) += sparc/ +obj-$(TARGET_SPARC64) += $(TARGET_ABI_DIR)/ +obj-$(TARGET_X86_64) += x86_64/ +obj-$(TARGET_XTENSA) += xtensa/ diff --git a/linux-user/aarch64/cpu_loop.c b/linux-user/aarch64/cpu_loop.c index 31c845a70d..bbe9fefca8 100644 --- a/linux-user/aarch64/cpu_loop.c +++ b/linux-user/aarch64/cpu_loop.c @@ -130,6 +130,7 @@ void cpu_loop(CPUARMState *env) break; case EXCP_SEMIHOST: env->xregs[0] = do_arm_semihosting(env); + env->pc += 4; break; case EXCP_YIELD: /* nothing to do here for user-mode, just resume guest code */ diff --git a/linux-user/aarch64/syscall_nr.h b/linux-user/aarch64/syscall_nr.h index f00ffd7fb8..85de000b24 100644 --- a/linux-user/aarch64/syscall_nr.h +++ b/linux-user/aarch64/syscall_nr.h @@ -1,7 +1,8 @@ /* * This file contains the system call numbers. + * Do not modify. + * This file is generated by scripts/gensyscalls.sh */ - #ifndef LINUX_USER_AARCH64_SYSCALL_NR_H #define LINUX_USER_AARCH64_SYSCALL_NR_H @@ -84,7 +85,7 @@ #define TARGET_NR_splice 76 #define TARGET_NR_tee 77 #define TARGET_NR_readlinkat 78 -#define TARGET_NR_fstatat64 79 +#define TARGET_NR_newfstatat 79 #define TARGET_NR_fstat 80 #define TARGET_NR_sync 81 #define TARGET_NR_fsync 82 @@ -254,8 +255,8 @@ #define TARGET_NR_prlimit64 261 #define TARGET_NR_fanotify_init 262 #define TARGET_NR_fanotify_mark 263 -#define TARGET_NR_name_to_handle_at 264 -#define TARGET_NR_open_by_handle_at 265 +#define TARGET_NR_name_to_handle_at 264 +#define TARGET_NR_open_by_handle_at 265 #define TARGET_NR_clock_adjtime 266 #define TARGET_NR_syncfs 267 #define TARGET_NR_setns 268 @@ -276,5 +277,28 @@ #define TARGET_NR_membarrier 283 #define TARGET_NR_mlock2 284 #define TARGET_NR_copy_file_range 285 +#define TARGET_NR_preadv2 286 +#define TARGET_NR_pwritev2 287 +#define TARGET_NR_pkey_mprotect 288 +#define TARGET_NR_pkey_alloc 289 +#define TARGET_NR_pkey_free 290 +#define TARGET_NR_statx 291 +#define TARGET_NR_io_pgetevents 292 +#define TARGET_NR_rseq 293 +#define TARGET_NR_kexec_file_load 294 +#define TARGET_NR_pidfd_send_signal 424 +#define TARGET_NR_io_uring_setup 425 +#define TARGET_NR_io_uring_enter 426 +#define TARGET_NR_io_uring_register 427 +#define TARGET_NR_open_tree 428 +#define TARGET_NR_move_mount 429 +#define TARGET_NR_fsopen 430 +#define TARGET_NR_fsconfig 431 +#define TARGET_NR_fsmount 432 +#define TARGET_NR_fspick 433 +#define TARGET_NR_pidfd_open 434 +#define TARGET_NR_clone3 435 +#define TARGET_NR_syscalls 436 + +#endif /* LINUX_USER_AARCH64_SYSCALL_NR_H */ -#endif diff --git a/linux-user/alpha/Makefile.objs b/linux-user/alpha/Makefile.objs new file mode 100644 index 0000000000..d6397a70ab --- /dev/null +++ b/linux-user/alpha/Makefile.objs @@ -0,0 +1,5 @@ +generated-files-y += linux-user/alpha/syscall_nr.h + +syshdr := $(SRC_PATH)/linux-user/alpha/syscallhdr.sh +%/syscall_nr.h: $(SRC_PATH)/linux-user/alpha/syscall.tbl $(syshdr) + $(call quiet-command, sh $(syshdr) $< $@ $(TARGET_SYSTBL_ABI),"GEN","$@") diff --git a/linux-user/alpha/syscall.tbl b/linux-user/alpha/syscall.tbl new file mode 100644 index 0000000000..36d42da746 --- /dev/null +++ b/linux-user/alpha/syscall.tbl @@ -0,0 +1,479 @@ +# SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note +# +# system call numbers and entry vectors for alpha +# +# The format is: +# +# +# The is always "common" for this file +# +0 common osf_syscall alpha_syscall_zero +1 common exit sys_exit +2 common fork alpha_fork +3 common read sys_read +4 common write sys_write +5 common osf_old_open sys_ni_syscall +6 common close sys_close +7 common osf_wait4 sys_osf_wait4 +8 common osf_old_creat sys_ni_syscall +9 common link sys_link +10 common unlink sys_unlink +11 common osf_execve sys_ni_syscall +12 common chdir sys_chdir +13 common fchdir sys_fchdir +14 common mknod sys_mknod +15 common chmod sys_chmod +16 common chown sys_chown +17 common brk sys_osf_brk +18 common osf_getfsstat sys_ni_syscall +19 common lseek sys_lseek +20 common getxpid sys_getxpid +21 common osf_mount sys_osf_mount +22 common umount2 sys_umount +23 common setuid sys_setuid +24 common getxuid sys_getxuid +25 common exec_with_loader sys_ni_syscall +26 common ptrace sys_ptrace +27 common osf_nrecvmsg sys_ni_syscall +28 common osf_nsendmsg sys_ni_syscall +29 common osf_nrecvfrom sys_ni_syscall +30 common osf_naccept sys_ni_syscall +31 common osf_ngetpeername sys_ni_syscall +32 common osf_ngetsockname sys_ni_syscall +33 common access sys_access +34 common osf_chflags sys_ni_syscall +35 common osf_fchflags sys_ni_syscall +36 common sync sys_sync +37 common kill sys_kill +38 common osf_old_stat sys_ni_syscall +39 common setpgid sys_setpgid +40 common osf_old_lstat sys_ni_syscall +41 common dup sys_dup +42 common pipe sys_alpha_pipe +43 common osf_set_program_attributes sys_osf_set_program_attributes +44 common osf_profil sys_ni_syscall +45 common open sys_open +46 common osf_old_sigaction sys_ni_syscall +47 common getxgid sys_getxgid +48 common osf_sigprocmask sys_osf_sigprocmask +49 common osf_getlogin sys_ni_syscall +50 common osf_setlogin sys_ni_syscall +51 common acct sys_acct +52 common sigpending sys_sigpending +54 common ioctl sys_ioctl +55 common osf_reboot sys_ni_syscall +56 common osf_revoke sys_ni_syscall +57 common symlink sys_symlink +58 common readlink sys_readlink +59 common execve sys_execve +60 common umask sys_umask +61 common chroot sys_chroot +62 common osf_old_fstat sys_ni_syscall +63 common getpgrp sys_getpgrp +64 common getpagesize sys_getpagesize +65 common osf_mremap sys_ni_syscall +66 common vfork alpha_vfork +67 common stat sys_newstat +68 common lstat sys_newlstat +69 common osf_sbrk sys_ni_syscall +70 common osf_sstk sys_ni_syscall +71 common mmap sys_osf_mmap +72 common osf_old_vadvise sys_ni_syscall +73 common munmap sys_munmap +74 common mprotect sys_mprotect +75 common madvise sys_madvise +76 common vhangup sys_vhangup +77 common osf_kmodcall sys_ni_syscall +78 common osf_mincore sys_ni_syscall +79 common getgroups sys_getgroups +80 common setgroups sys_setgroups +81 common osf_old_getpgrp sys_ni_syscall +82 common setpgrp sys_setpgid +83 common osf_setitimer compat_sys_setitimer +84 common osf_old_wait sys_ni_syscall +85 common osf_table sys_ni_syscall +86 common osf_getitimer compat_sys_getitimer +87 common gethostname sys_gethostname +88 common sethostname sys_sethostname +89 common getdtablesize sys_getdtablesize +90 common dup2 sys_dup2 +91 common fstat sys_newfstat +92 common fcntl sys_fcntl +93 common osf_select sys_osf_select +94 common poll sys_poll +95 common fsync sys_fsync +96 common setpriority sys_setpriority +97 common socket sys_socket +98 common connect sys_connect +99 common accept sys_accept +100 common getpriority sys_osf_getpriority +101 common send sys_send +102 common recv sys_recv +103 common sigreturn sys_sigreturn +104 common bind sys_bind +105 common setsockopt sys_setsockopt +106 common listen sys_listen +107 common osf_plock sys_ni_syscall +108 common osf_old_sigvec sys_ni_syscall +109 common osf_old_sigblock sys_ni_syscall +110 common osf_old_sigsetmask sys_ni_syscall +111 common sigsuspend sys_sigsuspend +112 common osf_sigstack sys_osf_sigstack +113 common recvmsg sys_recvmsg +114 common sendmsg sys_sendmsg +115 common osf_old_vtrace sys_ni_syscall +116 common osf_gettimeofday sys_osf_gettimeofday +117 common osf_getrusage sys_osf_getrusage +118 common getsockopt sys_getsockopt +120 common readv sys_osf_readv +121 common writev sys_osf_writev +122 common osf_settimeofday sys_osf_settimeofday +123 common fchown sys_fchown +124 common fchmod sys_fchmod +125 common recvfrom sys_recvfrom +126 common setreuid sys_setreuid +127 common setregid sys_setregid +128 common rename sys_rename +129 common truncate sys_truncate +130 common ftruncate sys_ftruncate +131 common flock sys_flock +132 common setgid sys_setgid +133 common sendto sys_sendto +134 common shutdown sys_shutdown +135 common socketpair sys_socketpair +136 common mkdir sys_mkdir +137 common rmdir sys_rmdir +138 common osf_utimes sys_osf_utimes +139 common osf_old_sigreturn sys_ni_syscall +140 common osf_adjtime sys_ni_syscall +141 common getpeername sys_getpeername +142 common osf_gethostid sys_ni_syscall +143 common osf_sethostid sys_ni_syscall +144 common getrlimit sys_getrlimit +145 common setrlimit sys_setrlimit +146 common osf_old_killpg sys_ni_syscall +147 common setsid sys_setsid +148 common quotactl sys_quotactl +149 common osf_oldquota sys_ni_syscall +150 common getsockname sys_getsockname +153 common osf_pid_block sys_ni_syscall +154 common osf_pid_unblock sys_ni_syscall +156 common sigaction sys_osf_sigaction +157 common osf_sigwaitprim sys_ni_syscall +158 common osf_nfssvc sys_ni_syscall +159 common osf_getdirentries sys_osf_getdirentries +160 common osf_statfs sys_osf_statfs +161 common osf_fstatfs sys_osf_fstatfs +163 common osf_asynch_daemon sys_ni_syscall +164 common osf_getfh sys_ni_syscall +165 common osf_getdomainname sys_osf_getdomainname +166 common setdomainname sys_setdomainname +169 common osf_exportfs sys_ni_syscall +181 common osf_alt_plock sys_ni_syscall +184 common osf_getmnt sys_ni_syscall +187 common osf_alt_sigpending sys_ni_syscall +188 common osf_alt_setsid sys_ni_syscall +199 common osf_swapon sys_swapon +200 common msgctl sys_old_msgctl +201 common msgget sys_msgget +202 common msgrcv sys_msgrcv +203 common msgsnd sys_msgsnd +204 common semctl sys_old_semctl +205 common semget sys_semget +206 common semop sys_semop +207 common osf_utsname sys_osf_utsname +208 common lchown sys_lchown +209 common shmat sys_shmat +210 common shmctl sys_old_shmctl +211 common shmdt sys_shmdt +212 common shmget sys_shmget +213 common osf_mvalid sys_ni_syscall +214 common osf_getaddressconf sys_ni_syscall +215 common osf_msleep sys_ni_syscall +216 common osf_mwakeup sys_ni_syscall +217 common msync sys_msync +218 common osf_signal sys_ni_syscall +219 common osf_utc_gettime sys_ni_syscall +220 common osf_utc_adjtime sys_ni_syscall +222 common osf_security sys_ni_syscall +223 common osf_kloadcall sys_ni_syscall +224 common osf_stat sys_osf_stat +225 common osf_lstat sys_osf_lstat +226 common osf_fstat sys_osf_fstat +227 common osf_statfs64 sys_osf_statfs64 +228 common osf_fstatfs64 sys_osf_fstatfs64 +233 common getpgid sys_getpgid +234 common getsid sys_getsid +235 common sigaltstack sys_sigaltstack +236 common osf_waitid sys_ni_syscall +237 common osf_priocntlset sys_ni_syscall +238 common osf_sigsendset sys_ni_syscall +239 common osf_set_speculative sys_ni_syscall +240 common osf_msfs_syscall sys_ni_syscall +241 common osf_sysinfo sys_osf_sysinfo +242 common osf_uadmin sys_ni_syscall +243 common osf_fuser sys_ni_syscall +244 common osf_proplist_syscall sys_osf_proplist_syscall +245 common osf_ntp_adjtime sys_ni_syscall +246 common osf_ntp_gettime sys_ni_syscall +247 common osf_pathconf sys_ni_syscall +248 common osf_fpathconf sys_ni_syscall +250 common osf_uswitch sys_ni_syscall +251 common osf_usleep_thread sys_osf_usleep_thread +252 common osf_audcntl sys_ni_syscall +253 common osf_audgen sys_ni_syscall +254 common sysfs sys_sysfs +255 common osf_subsys_info sys_ni_syscall +256 common osf_getsysinfo sys_osf_getsysinfo +257 common osf_setsysinfo sys_osf_setsysinfo +258 common osf_afs_syscall sys_ni_syscall +259 common osf_swapctl sys_ni_syscall +260 common osf_memcntl sys_ni_syscall +261 common osf_fdatasync sys_ni_syscall +300 common bdflush sys_bdflush +301 common sethae sys_sethae +302 common mount sys_mount +303 common old_adjtimex sys_old_adjtimex +304 common swapoff sys_swapoff +305 common getdents sys_getdents +306 common create_module sys_ni_syscall +307 common init_module sys_init_module +308 common delete_module sys_delete_module +309 common get_kernel_syms sys_ni_syscall +310 common syslog sys_syslog +311 common reboot sys_reboot +312 common clone alpha_clone +313 common uselib sys_uselib +314 common mlock sys_mlock +315 common munlock sys_munlock +316 common mlockall sys_mlockall +317 common munlockall sys_munlockall +318 common sysinfo sys_sysinfo +319 common _sysctl sys_sysctl +# 320 was sys_idle +321 common oldumount sys_oldumount +322 common swapon sys_swapon +323 common times sys_times +324 common personality sys_personality +325 common setfsuid sys_setfsuid +326 common setfsgid sys_setfsgid +327 common ustat sys_ustat +328 common statfs sys_statfs +329 common fstatfs sys_fstatfs +330 common sched_setparam sys_sched_setparam +331 common sched_getparam sys_sched_getparam +332 common sched_setscheduler sys_sched_setscheduler +333 common sched_getscheduler sys_sched_getscheduler +334 common sched_yield sys_sched_yield +335 common sched_get_priority_max sys_sched_get_priority_max +336 common sched_get_priority_min sys_sched_get_priority_min +337 common sched_rr_get_interval sys_sched_rr_get_interval +338 common afs_syscall sys_ni_syscall +339 common uname sys_newuname +340 common nanosleep sys_nanosleep +341 common mremap sys_mremap +342 common nfsservctl sys_ni_syscall +343 common setresuid sys_setresuid +344 common getresuid sys_getresuid +345 common pciconfig_read sys_pciconfig_read +346 common pciconfig_write sys_pciconfig_write +347 common query_module sys_ni_syscall +348 common prctl sys_prctl +349 common pread64 sys_pread64 +350 common pwrite64 sys_pwrite64 +351 common rt_sigreturn sys_rt_sigreturn +352 common rt_sigaction sys_rt_sigaction +353 common rt_sigprocmask sys_rt_sigprocmask +354 common rt_sigpending sys_rt_sigpending +355 common rt_sigtimedwait sys_rt_sigtimedwait +356 common rt_sigqueueinfo sys_rt_sigqueueinfo +357 common rt_sigsuspend sys_rt_sigsuspend +358 common select sys_select +359 common gettimeofday sys_gettimeofday +360 common settimeofday sys_settimeofday +361 common getitimer sys_getitimer +362 common setitimer sys_setitimer +363 common utimes sys_utimes +364 common getrusage sys_getrusage +365 common wait4 sys_wait4 +366 common adjtimex sys_adjtimex +367 common getcwd sys_getcwd +368 common capget sys_capget +369 common capset sys_capset +370 common sendfile sys_sendfile64 +371 common setresgid sys_setresgid +372 common getresgid sys_getresgid +373 common dipc sys_ni_syscall +374 common pivot_root sys_pivot_root +375 common mincore sys_mincore +376 common pciconfig_iobase sys_pciconfig_iobase +377 common getdents64 sys_getdents64 +378 common gettid sys_gettid +379 common readahead sys_readahead +# 380 is unused +381 common tkill sys_tkill +382 common setxattr sys_setxattr +383 common lsetxattr sys_lsetxattr +384 common fsetxattr sys_fsetxattr +385 common getxattr sys_getxattr +386 common lgetxattr sys_lgetxattr +387 common fgetxattr sys_fgetxattr +388 common listxattr sys_listxattr +389 common llistxattr sys_llistxattr +390 common flistxattr sys_flistxattr +391 common removexattr sys_removexattr +392 common lremovexattr sys_lremovexattr +393 common fremovexattr sys_fremovexattr +394 common futex sys_futex +395 common sched_setaffinity sys_sched_setaffinity +396 common sched_getaffinity sys_sched_getaffinity +397 common tuxcall sys_ni_syscall +398 common io_setup sys_io_setup +399 common io_destroy sys_io_destroy +400 common io_getevents sys_io_getevents +401 common io_submit sys_io_submit +402 common io_cancel sys_io_cancel +405 common exit_group sys_exit_group +406 common lookup_dcookie sys_lookup_dcookie +407 common epoll_create sys_epoll_create +408 common epoll_ctl sys_epoll_ctl +409 common epoll_wait sys_epoll_wait +410 common remap_file_pages sys_remap_file_pages +411 common set_tid_address sys_set_tid_address +412 common restart_syscall sys_restart_syscall +413 common fadvise64 sys_fadvise64 +414 common timer_create sys_timer_create +415 common timer_settime sys_timer_settime +416 common timer_gettime sys_timer_gettime +417 common timer_getoverrun sys_timer_getoverrun +418 common timer_delete sys_timer_delete +419 common clock_settime sys_clock_settime +420 common clock_gettime sys_clock_gettime +421 common clock_getres sys_clock_getres +422 common clock_nanosleep sys_clock_nanosleep +423 common semtimedop sys_semtimedop +424 common tgkill sys_tgkill +425 common stat64 sys_stat64 +426 common lstat64 sys_lstat64 +427 common fstat64 sys_fstat64 +428 common vserver sys_ni_syscall +429 common mbind sys_ni_syscall +430 common get_mempolicy sys_ni_syscall +431 common set_mempolicy sys_ni_syscall +432 common mq_open sys_mq_open +433 common mq_unlink sys_mq_unlink +434 common mq_timedsend sys_mq_timedsend +435 common mq_timedreceive sys_mq_timedreceive +436 common mq_notify sys_mq_notify +437 common mq_getsetattr sys_mq_getsetattr +438 common waitid sys_waitid +439 common add_key sys_add_key +440 common request_key sys_request_key +441 common keyctl sys_keyctl +442 common ioprio_set sys_ioprio_set +443 common ioprio_get sys_ioprio_get +444 common inotify_init sys_inotify_init +445 common inotify_add_watch sys_inotify_add_watch +446 common inotify_rm_watch sys_inotify_rm_watch +447 common fdatasync sys_fdatasync +448 common kexec_load sys_kexec_load +449 common migrate_pages sys_migrate_pages +450 common openat sys_openat +451 common mkdirat sys_mkdirat +452 common mknodat sys_mknodat +453 common fchownat sys_fchownat +454 common futimesat sys_futimesat +455 common fstatat64 sys_fstatat64 +456 common unlinkat sys_unlinkat +457 common renameat sys_renameat +458 common linkat sys_linkat +459 common symlinkat sys_symlinkat +460 common readlinkat sys_readlinkat +461 common fchmodat sys_fchmodat +462 common faccessat sys_faccessat +463 common pselect6 sys_pselect6 +464 common ppoll sys_ppoll +465 common unshare sys_unshare +466 common set_robust_list sys_set_robust_list +467 common get_robust_list sys_get_robust_list +468 common splice sys_splice +469 common sync_file_range sys_sync_file_range +470 common tee sys_tee +471 common vmsplice sys_vmsplice +472 common move_pages sys_move_pages +473 common getcpu sys_getcpu +474 common epoll_pwait sys_epoll_pwait +475 common utimensat sys_utimensat +476 common signalfd sys_signalfd +477 common timerfd sys_ni_syscall +478 common eventfd sys_eventfd +479 common recvmmsg sys_recvmmsg +480 common fallocate sys_fallocate +481 common timerfd_create sys_timerfd_create +482 common timerfd_settime sys_timerfd_settime +483 common timerfd_gettime sys_timerfd_gettime +484 common signalfd4 sys_signalfd4 +485 common eventfd2 sys_eventfd2 +486 common epoll_create1 sys_epoll_create1 +487 common dup3 sys_dup3 +488 common pipe2 sys_pipe2 +489 common inotify_init1 sys_inotify_init1 +490 common preadv sys_preadv +491 common pwritev sys_pwritev +492 common rt_tgsigqueueinfo sys_rt_tgsigqueueinfo +493 common perf_event_open sys_perf_event_open +494 common fanotify_init sys_fanotify_init +495 common fanotify_mark sys_fanotify_mark +496 common prlimit64 sys_prlimit64 +497 common name_to_handle_at sys_name_to_handle_at +498 common open_by_handle_at sys_open_by_handle_at +499 common clock_adjtime sys_clock_adjtime +500 common syncfs sys_syncfs +501 common setns sys_setns +502 common accept4 sys_accept4 +503 common sendmmsg sys_sendmmsg +504 common process_vm_readv sys_process_vm_readv +505 common process_vm_writev sys_process_vm_writev +506 common kcmp sys_kcmp +507 common finit_module sys_finit_module +508 common sched_setattr sys_sched_setattr +509 common sched_getattr sys_sched_getattr +510 common renameat2 sys_renameat2 +511 common getrandom sys_getrandom +512 common memfd_create sys_memfd_create +513 common execveat sys_execveat +514 common seccomp sys_seccomp +515 common bpf sys_bpf +516 common userfaultfd sys_userfaultfd +517 common membarrier sys_membarrier +518 common mlock2 sys_mlock2 +519 common copy_file_range sys_copy_file_range +520 common preadv2 sys_preadv2 +521 common pwritev2 sys_pwritev2 +522 common statx sys_statx +523 common io_pgetevents sys_io_pgetevents +524 common pkey_mprotect sys_pkey_mprotect +525 common pkey_alloc sys_pkey_alloc +526 common pkey_free sys_pkey_free +527 common rseq sys_rseq +528 common statfs64 sys_statfs64 +529 common fstatfs64 sys_fstatfs64 +530 common getegid sys_getegid +531 common geteuid sys_geteuid +532 common getppid sys_getppid +# all other architectures have common numbers for new syscall, alpha +# is the exception. +534 common pidfd_send_signal sys_pidfd_send_signal +535 common io_uring_setup sys_io_uring_setup +536 common io_uring_enter sys_io_uring_enter +537 common io_uring_register sys_io_uring_register +538 common open_tree sys_open_tree +539 common move_mount sys_move_mount +540 common fsopen sys_fsopen +541 common fsconfig sys_fsconfig +542 common fsmount sys_fsmount +543 common fspick sys_fspick +544 common pidfd_open sys_pidfd_open +# 545 reserved for clone3 +547 common openat2 sys_openat2 +548 common pidfd_getfd sys_pidfd_getfd diff --git a/linux-user/alpha/syscall_nr.h b/linux-user/alpha/syscall_nr.h deleted file mode 100644 index 2e5541bbf9..0000000000 --- a/linux-user/alpha/syscall_nr.h +++ /dev/null @@ -1,457 +0,0 @@ -#ifndef LINUX_USER_ALPHA_SYSCALL_NR_H -#define LINUX_USER_ALPHA_SYSCALL_NR_H - -#define TARGET_NR_osf_syscall 0 /* not implemented */ -#define TARGET_NR_exit 1 -#define TARGET_NR_fork 2 -#define TARGET_NR_read 3 -#define TARGET_NR_write 4 -#define TARGET_NR_osf_old_open 5 /* not implemented */ -#define TARGET_NR_close 6 -#define TARGET_NR_osf_wait4 7 -#define TARGET_NR_osf_old_creat 8 /* not implemented */ -#define TARGET_NR_link 9 -#define TARGET_NR_unlink 10 -#define TARGET_NR_osf_execve 11 /* not implemented */ -#define TARGET_NR_chdir 12 -#define TARGET_NR_fchdir 13 -#define TARGET_NR_mknod 14 -#define TARGET_NR_chmod 15 -#define TARGET_NR_chown 16 -#define TARGET_NR_brk 17 -#define TARGET_NR_osf_getfsstat 18 /* not implemented */ -#define TARGET_NR_lseek 19 -#define TARGET_NR_getxpid 20 -#define TARGET_NR_osf_mount 21 -#define TARGET_NR_umount2 22 -#define TARGET_NR_setuid 23 -#define TARGET_NR_getxuid 24 -#define TARGET_NR_exec_with_loader 25 /* not implemented */ -#define TARGET_NR_ptrace 26 -#define TARGET_NR_osf_nrecvmsg 27 /* not implemented */ -#define TARGET_NR_osf_nsendmsg 28 /* not implemented */ -#define TARGET_NR_osf_nrecvfrom 29 /* not implemented */ -#define TARGET_NR_osf_naccept 30 /* not implemented */ -#define TARGET_NR_osf_ngetpeername 31 /* not implemented */ -#define TARGET_NR_osf_ngetsockname 32 /* not implemented */ -#define TARGET_NR_access 33 -#define TARGET_NR_osf_chflags 34 /* not implemented */ -#define TARGET_NR_osf_fchflags 35 /* not implemented */ -#define TARGET_NR_sync 36 -#define TARGET_NR_kill 37 -#define TARGET_NR_osf_old_stat 38 /* not implemented */ -#define TARGET_NR_setpgid 39 -#define TARGET_NR_osf_old_lstat 40 /* not implemented */ -#define TARGET_NR_dup 41 -#define TARGET_NR_pipe 42 -#define TARGET_NR_osf_set_program_attributes 43 -#define TARGET_NR_osf_profil 44 /* not implemented */ -#define TARGET_NR_open 45 -#define TARGET_NR_osf_old_sigaction 46 /* not implemented */ -#define TARGET_NR_getxgid 47 -#define TARGET_NR_sigprocmask 48 -#define TARGET_NR_osf_getlogin 49 /* not implemented */ -#define TARGET_NR_osf_setlogin 50 /* not implemented */ -#define TARGET_NR_acct 51 -#define TARGET_NR_sigpending 52 - -#define TARGET_NR_ioctl 54 -#define TARGET_NR_osf_reboot 55 /* not implemented */ -#define TARGET_NR_osf_revoke 56 /* not implemented */ -#define TARGET_NR_symlink 57 -#define TARGET_NR_readlink 58 -#define TARGET_NR_execve 59 -#define TARGET_NR_umask 60 -#define TARGET_NR_chroot 61 -#define TARGET_NR_osf_old_fstat 62 /* not implemented */ -#define TARGET_NR_getpgrp 63 -#define TARGET_NR_getpagesize 64 -#define TARGET_NR_osf_mremap 65 /* not implemented */ -#define TARGET_NR_vfork 66 -#define TARGET_NR_stat 67 -#define TARGET_NR_lstat 68 -#define TARGET_NR_osf_sbrk 69 /* not implemented */ -#define TARGET_NR_osf_sstk 70 /* not implemented */ -#define TARGET_NR_mmap 71 /* OSF/1 mmap is superset of Linux */ -#define TARGET_NR_osf_old_vadvise 72 /* not implemented */ -#define TARGET_NR_munmap 73 -#define TARGET_NR_mprotect 74 -#define TARGET_NR_madvise 75 -#define TARGET_NR_vhangup 76 -#define TARGET_NR_osf_kmodcall 77 /* not implemented */ -#define TARGET_NR_osf_mincore 78 /* not implemented */ -#define TARGET_NR_getgroups 79 -#define TARGET_NR_setgroups 80 -#define TARGET_NR_osf_old_getpgrp 81 /* not implemented */ -#define TARGET_NR_setpgrp 82 /* BSD alias for setpgid */ -#define TARGET_NR_osf_setitimer 83 -#define TARGET_NR_osf_old_wait 84 /* not implemented */ -#define TARGET_NR_osf_table 85 /* not implemented */ -#define TARGET_NR_osf_getitimer 86 -#define TARGET_NR_gethostname 87 -#define TARGET_NR_sethostname 88 -#define TARGET_NR_getdtablesize 89 -#define TARGET_NR_dup2 90 -#define TARGET_NR_fstat 91 -#define TARGET_NR_fcntl 92 -#define TARGET_NR_osf_select 93 -#define TARGET_NR_poll 94 -#define TARGET_NR_fsync 95 -#define TARGET_NR_setpriority 96 -#define TARGET_NR_socket 97 -#define TARGET_NR_connect 98 -#define TARGET_NR_accept 99 -#define TARGET_NR_getpriority 100 -#define TARGET_NR_send 101 -#define TARGET_NR_recv 102 -#define TARGET_NR_sigreturn 103 -#define TARGET_NR_bind 104 -#define TARGET_NR_setsockopt 105 -#define TARGET_NR_listen 106 -#define TARGET_NR_osf_plock 107 /* not implemented */ -#define TARGET_NR_osf_old_sigvec 108 /* not implemented */ -#define TARGET_NR_osf_old_sigblock 109 /* not implemented */ -#define TARGET_NR_osf_old_sigsetmask 110 /* not implemented */ -#define TARGET_NR_sigsuspend 111 -#define TARGET_NR_osf_sigstack 112 -#define TARGET_NR_recvmsg 113 -#define TARGET_NR_sendmsg 114 -#define TARGET_NR_osf_old_vtrace 115 /* not implemented */ -#define TARGET_NR_osf_gettimeofday 116 -#define TARGET_NR_osf_getrusage 117 -#define TARGET_NR_getsockopt 118 - -#define TARGET_NR_readv 120 -#define TARGET_NR_writev 121 -#define TARGET_NR_osf_settimeofday 122 -#define TARGET_NR_fchown 123 -#define TARGET_NR_fchmod 124 -#define TARGET_NR_recvfrom 125 -#define TARGET_NR_setreuid 126 -#define TARGET_NR_setregid 127 -#define TARGET_NR_rename 128 -#define TARGET_NR_truncate 129 -#define TARGET_NR_ftruncate 130 -#define TARGET_NR_flock 131 -#define TARGET_NR_setgid 132 -#define TARGET_NR_sendto 133 -#define TARGET_NR_shutdown 134 -#define TARGET_NR_socketpair 135 -#define TARGET_NR_mkdir 136 -#define TARGET_NR_rmdir 137 -#define TARGET_NR_osf_utimes 138 -#define TARGET_NR_osf_old_sigreturn 139 /* not implemented */ -#define TARGET_NR_osf_adjtime 140 /* not implemented */ -#define TARGET_NR_getpeername 141 -#define TARGET_NR_osf_gethostid 142 /* not implemented */ -#define TARGET_NR_osf_sethostid 143 /* not implemented */ -#define TARGET_NR_getrlimit 144 -#define TARGET_NR_setrlimit 145 -#define TARGET_NR_osf_old_killpg 146 /* not implemented */ -#define TARGET_NR_setsid 147 -#define TARGET_NR_quotactl 148 -#define TARGET_NR_osf_oldquota 149 /* not implemented */ -#define TARGET_NR_getsockname 150 - -#define TARGET_NR_osf_pid_block 153 /* not implemented */ -#define TARGET_NR_osf_pid_unblock 154 /* not implemented */ - -#define TARGET_NR_sigaction 156 -#define TARGET_NR_osf_sigwaitprim 157 /* not implemented */ -#define TARGET_NR_osf_nfssvc 158 /* not implemented */ -#define TARGET_NR_osf_getdirentries 159 -#define TARGET_NR_osf_statfs 160 -#define TARGET_NR_osf_fstatfs 161 - -#define TARGET_NR_osf_asynch_daemon 163 /* not implemented */ -#define TARGET_NR_osf_getfh 164 /* not implemented */ -#define TARGET_NR_osf_getdomainname 165 -#define TARGET_NR_setdomainname 166 - -#define TARGET_NR_osf_exportfs 169 /* not implemented */ - -#define TARGET_NR_osf_alt_plock 181 /* not implemented */ - -#define TARGET_NR_osf_getmnt 184 /* not implemented */ - -#define TARGET_NR_osf_alt_sigpending 187 /* not implemented */ -#define TARGET_NR_osf_alt_setsid 188 /* not implemented */ - -#define TARGET_NR_osf_swapon 199 -#define TARGET_NR_msgctl 200 -#define TARGET_NR_msgget 201 -#define TARGET_NR_msgrcv 202 -#define TARGET_NR_msgsnd 203 -#define TARGET_NR_semctl 204 -#define TARGET_NR_semget 205 -#define TARGET_NR_semop 206 -#define TARGET_NR_osf_utsname 207 -#define TARGET_NR_lchown 208 -#define TARGET_NR_osf_shmat 209 -/* this has the usual shmat semantics so give it the name syscall.c expects - * so that our support for it is enabled. - */ -#define TARGET_NR_shmat TARGET_NR_osf_shmat -#define TARGET_NR_shmctl 210 -#define TARGET_NR_shmdt 211 -#define TARGET_NR_shmget 212 -#define TARGET_NR_osf_mvalid 213 /* not implemented */ -#define TARGET_NR_osf_getaddressconf 214 /* not implemented */ -#define TARGET_NR_osf_msleep 215 /* not implemented */ -#define TARGET_NR_osf_mwakeup 216 /* not implemented */ -#define TARGET_NR_msync 217 -#define TARGET_NR_osf_signal 218 /* not implemented */ -#define TARGET_NR_osf_utc_gettime 219 /* not implemented */ -#define TARGET_NR_osf_utc_adjtime 220 /* not implemented */ - -#define TARGET_NR_osf_security 222 /* not implemented */ -#define TARGET_NR_osf_kloadcall 223 /* not implemented */ - -#define TARGET_NR_getpgid 233 -#define TARGET_NR_getsid 234 -#define TARGET_NR_sigaltstack 235 -#define TARGET_NR_osf_waitid 236 /* not implemented */ -#define TARGET_NR_osf_priocntlset 237 /* not implemented */ -#define TARGET_NR_osf_sigsendset 238 /* not implemented */ -#define TARGET_NR_osf_set_speculative 239 /* not implemented */ -#define TARGET_NR_osf_msfs_syscall 240 /* not implemented */ -#define TARGET_NR_osf_sysinfo 241 -#define TARGET_NR_osf_uadmin 242 /* not implemented */ -#define TARGET_NR_osf_fuser 243 /* not implemented */ -#define TARGET_NR_osf_proplist_syscall 244 -#define TARGET_NR_osf_ntp_adjtime 245 /* not implemented */ -#define TARGET_NR_osf_ntp_gettime 246 /* not implemented */ -#define TARGET_NR_osf_pathconf 247 /* not implemented */ -#define TARGET_NR_osf_fpathconf 248 /* not implemented */ - -#define TARGET_NR_osf_uswitch 250 /* not implemented */ -#define TARGET_NR_osf_usleep_thread 251 -#define TARGET_NR_osf_audcntl 252 /* not implemented */ -#define TARGET_NR_osf_audgen 253 /* not implemented */ -#define TARGET_NR_sysfs 254 -#define TARGET_NR_osf_subsys_info 255 /* not implemented */ -#define TARGET_NR_osf_getsysinfo 256 -#define TARGET_NR_osf_setsysinfo 257 -#define TARGET_NR_osf_afs_syscall 258 /* not implemented */ -#define TARGET_NR_osf_swapctl 259 /* not implemented */ -#define TARGET_NR_osf_memcntl 260 /* not implemented */ -#define TARGET_NR_osf_fdatasync 261 /* not implemented */ - - -/* - * Linux-specific system calls begin at 300 - */ -#define TARGET_NR_bdflush 300 -#define TARGET_NR_sethae 301 -#define TARGET_NR_mount 302 -#define TARGET_NR_old_adjtimex 303 -#define TARGET_NR_swapoff 304 -#define TARGET_NR_getdents 305 -#define TARGET_NR_create_module 306 -#define TARGET_NR_init_module 307 -#define TARGET_NR_delete_module 308 -#define TARGET_NR_get_kernel_syms 309 -#define TARGET_NR_syslog 310 -#define TARGET_NR_reboot 311 -#define TARGET_NR_clone 312 -#define TARGET_NR_uselib 313 -#define TARGET_NR_mlock 314 -#define TARGET_NR_munlock 315 -#define TARGET_NR_mlockall 316 -#define TARGET_NR_munlockall 317 -#define TARGET_NR_sysinfo 318 -#define TARGET_NR__sysctl 319 -/* 320 was sys_idle. */ -#define TARGET_NR_umount 321 -#define TARGET_NR_swapon 322 -#define TARGET_NR_times 323 -#define TARGET_NR_personality 324 -#define TARGET_NR_setfsuid 325 -#define TARGET_NR_setfsgid 326 -#define TARGET_NR_ustat 327 -#define TARGET_NR_statfs 328 -#define TARGET_NR_fstatfs 329 -#define TARGET_NR_sched_setparam 330 -#define TARGET_NR_sched_getparam 331 -#define TARGET_NR_sched_setscheduler 332 -#define TARGET_NR_sched_getscheduler 333 -#define TARGET_NR_sched_yield 334 -#define TARGET_NR_sched_get_priority_max 335 -#define TARGET_NR_sched_get_priority_min 336 -#define TARGET_NR_sched_rr_get_interval 337 -#define TARGET_NR_afs_syscall 338 -#define TARGET_NR_uname 339 -#define TARGET_NR_nanosleep 340 -#define TARGET_NR_mremap 341 -#define TARGET_NR_nfsservctl 342 -#define TARGET_NR_setresuid 343 -#define TARGET_NR_getresuid 344 -#define TARGET_NR_pciconfig_read 345 -#define TARGET_NR_pciconfig_write 346 -#define TARGET_NR_query_module 347 -#define TARGET_NR_prctl 348 -#define TARGET_NR_pread64 349 -#define TARGET_NR_pwrite64 350 -#define TARGET_NR_rt_sigreturn 351 -#define TARGET_NR_rt_sigaction 352 -#define TARGET_NR_rt_sigprocmask 353 -#define TARGET_NR_rt_sigpending 354 -#define TARGET_NR_rt_sigtimedwait 355 -#define TARGET_NR_rt_sigqueueinfo 356 -#define TARGET_NR_rt_sigsuspend 357 -#define TARGET_NR_select 358 -#define TARGET_NR_gettimeofday 359 -#define TARGET_NR_settimeofday 360 -#define TARGET_NR_getitimer 361 -#define TARGET_NR_setitimer 362 -#define TARGET_NR_utimes 363 -#define TARGET_NR_getrusage 364 -#define TARGET_NR_wait4 365 -#define TARGET_NR_adjtimex 366 -#define TARGET_NR_getcwd 367 -#define TARGET_NR_capget 368 -#define TARGET_NR_capset 369 -#define TARGET_NR_sendfile 370 -#define TARGET_NR_setresgid 371 -#define TARGET_NR_getresgid 372 -#define TARGET_NR_dipc 373 -#define TARGET_NR_pivot_root 374 -#define TARGET_NR_mincore 375 -#define TARGET_NR_pciconfig_iobase 376 -#define TARGET_NR_getdents64 377 -#define TARGET_NR_gettid 378 -#define TARGET_NR_readahead 379 -/* 380 is unused */ -#define TARGET_NR_tkill 381 -#define TARGET_NR_setxattr 382 -#define TARGET_NR_lsetxattr 383 -#define TARGET_NR_fsetxattr 384 -#define TARGET_NR_getxattr 385 -#define TARGET_NR_lgetxattr 386 -#define TARGET_NR_fgetxattr 387 -#define TARGET_NR_listxattr 388 -#define TARGET_NR_llistxattr 389 -#define TARGET_NR_flistxattr 390 -#define TARGET_NR_removexattr 391 -#define TARGET_NR_lremovexattr 392 -#define TARGET_NR_fremovexattr 393 -#define TARGET_NR_futex 394 -#define TARGET_NR_sched_setaffinity 395 -#define TARGET_NR_sched_getaffinity 396 -#define TARGET_NR_tuxcall 397 -#define TARGET_NR_io_setup 398 -#define TARGET_NR_io_destroy 399 -#define TARGET_NR_io_getevents 400 -#define TARGET_NR_io_submit 401 -#define TARGET_NR_io_cancel 402 -#define TARGET_NR_exit_group 405 -#define TARGET_NR_lookup_dcookie 406 -#define TARGET_NR_epoll_create 407 -#define TARGET_NR_epoll_ctl 408 -#define TARGET_NR_epoll_wait 409 -#define TARGET_NR_remap_file_pages 410 -#define TARGET_NR_set_tid_address 411 -#define TARGET_NR_restart_syscall 412 -#define TARGET_NR_fadvise64 413 -#define TARGET_NR_timer_create 414 -#define TARGET_NR_timer_settime 415 -#define TARGET_NR_timer_gettime 416 -#define TARGET_NR_timer_getoverrun 417 -#define TARGET_NR_timer_delete 418 -#define TARGET_NR_clock_settime 419 -#define TARGET_NR_clock_gettime 420 -#define TARGET_NR_clock_getres 421 -#define TARGET_NR_clock_nanosleep 422 -#define TARGET_NR_semtimedop 423 -#define TARGET_NR_tgkill 424 -#define TARGET_NR_stat64 425 -#define TARGET_NR_lstat64 426 -#define TARGET_NR_fstat64 427 -#define TARGET_NR_vserver 428 -#define TARGET_NR_mbind 429 -#define TARGET_NR_get_mempolicy 430 -#define TARGET_NR_set_mempolicy 431 -#define TARGET_NR_mq_open 432 -#define TARGET_NR_mq_unlink 433 -#define TARGET_NR_mq_timedsend 434 -#define TARGET_NR_mq_timedreceive 435 -#define TARGET_NR_mq_notify 436 -#define TARGET_NR_mq_getsetattr 437 -#define TARGET_NR_waitid 438 -#define TARGET_NR_add_key 439 -#define TARGET_NR_request_key 440 -#define TARGET_NR_keyctl 441 -#define TARGET_NR_ioprio_set 442 -#define TARGET_NR_ioprio_get 443 -#define TARGET_NR_inotify_init 444 -#define TARGET_NR_inotify_add_watch 445 -#define TARGET_NR_inotify_rm_watch 446 -#define TARGET_NR_fdatasync 447 -#define TARGET_NR_kexec_load 448 -#define TARGET_NR_migrate_pages 449 -#define TARGET_NR_openat 450 -#define TARGET_NR_mkdirat 451 -#define TARGET_NR_mknodat 452 -#define TARGET_NR_fchownat 453 -#define TARGET_NR_futimesat 454 -#define TARGET_NR_fstatat64 455 -#define TARGET_NR_unlinkat 456 -#define TARGET_NR_renameat 457 -#define TARGET_NR_linkat 458 -#define TARGET_NR_symlinkat 459 -#define TARGET_NR_readlinkat 460 -#define TARGET_NR_fchmodat 461 -#define TARGET_NR_faccessat 462 -#define TARGET_NR_pselect6 463 -#define TARGET_NR_ppoll 464 -#define TARGET_NR_unshare 465 -#define TARGET_NR_set_robust_list 466 -#define TARGET_NR_get_robust_list 467 -#define TARGET_NR_splice 468 -#define TARGET_NR_sync_file_range 469 -#define TARGET_NR_tee 470 -#define TARGET_NR_vmsplice 471 -#define TARGET_NR_move_pages 472 -#define TARGET_NR_getcpu 473 -#define TARGET_NR_epoll_pwait 474 -#define TARGET_NR_utimensat 475 -#define TARGET_NR_signalfd 476 -#define TARGET_NR_timerfd 477 -#define TARGET_NR_eventfd 478 -#define TARGET_NR_recvmmsg 479 -#define TARGET_NR_fallocate 480 -#define TARGET_NR_timerfd_create 481 -#define TARGET_NR_timerfd_settime 482 -#define TARGET_NR_timerfd_gettime 483 -#define TARGET_NR_signalfd4 484 -#define TARGET_NR_eventfd2 485 -#define TARGET_NR_epoll_create1 486 -#define TARGET_NR_dup3 487 -#define TARGET_NR_pipe2 488 -#define TARGET_NR_inotify_init1 489 -#define TARGET_NR_preadv 490 -#define TARGET_NR_pwritev 491 -#define TARGET_NR_rt_tgsigqueueinfo 492 -#define TARGET_NR_perf_event_open 493 -#define TARGET_NR_fanotify_init 494 -#define TARGET_NR_fanotify_mark 495 -#define TARGET_NR_prlimit64 496 -#define TARGET_NR_name_to_handle_at 497 -#define TARGET_NR_open_by_handle_at 498 -#define TARGET_NR_clock_adjtime 499 -#define TARGET_NR_syncfs 500 -#define TARGET_NR_setns 501 -#define TARGET_NR_accept4 502 -#define TARGET_NR_sendmmsg 503 -#define TARGET_NR_process_vm_readv 504 -#define TARGET_NR_process_vm_writev 505 -#define TARGET_NR_kcmp 506 -#define TARGET_NR_finit_module 507 -#define TARGET_NR_sched_setattr 508 -#define TARGET_NR_sched_getattr 509 -#define TARGET_NR_renameat2 510 -#define TARGET_NR_getrandom 511 -#define TARGET_NR_memfd_create 512 -#define TARGET_NR_execveat 513 - -#endif diff --git a/linux-user/alpha/syscallhdr.sh b/linux-user/alpha/syscallhdr.sh new file mode 100644 index 0000000000..55cafe6abf --- /dev/null +++ b/linux-user/alpha/syscallhdr.sh @@ -0,0 +1,32 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 + +in="$1" +out="$2" +my_abis=`echo "($3)" | tr ',' '|'` +prefix="$4" +offset="$5" + +fileguard=LINUX_USER_ALPHA_`basename "$out" | sed \ + -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/' \ + -e 's/[^A-Z0-9_]/_/g' -e 's/__/_/g'` +grep -E "^[0-9A-Fa-fXx]+[[:space:]]+${my_abis}" "$in" | sort -n | ( + printf "#ifndef %s\n" "${fileguard}" + printf "#define %s\n" "${fileguard}" + printf "\n" + + nxt=0 + while read nr abi name entry ; do + if [ -z "$offset" ]; then + printf "#define TARGET_NR_%s%s\t%s\n" \ + "${prefix}" "${name}" "${nr}" + else + printf "#define TARGET_NR_%s%s\t(%s + %s)\n" \ + "${prefix}" "${name}" "${offset}" "${nr}" + fi + nxt=$((nr+1)) + done + + printf "\n" + printf "#endif /* %s */" "${fileguard}" +) > "$out" diff --git a/linux-user/arm/Makefile.objs b/linux-user/arm/Makefile.objs new file mode 100644 index 0000000000..c7eb94dcba --- /dev/null +++ b/linux-user/arm/Makefile.objs @@ -0,0 +1,8 @@ +obj-$(TARGET_ARM) += nwfpe/ +obj-$(TARGET_ARM) += semihost.o + +generated-files-y += linux-user/arm/syscall_nr.h + +syshdr := $(SRC_PATH)/linux-user/arm/syscallhdr.sh +%/syscall_nr.h: $(SRC_PATH)/linux-user/arm/syscall.tbl $(syshdr) + $(call quiet-command, sh $(syshdr) $< $@ $(TARGET_SYSTBL_ABI),"GEN","$@") diff --git a/linux-user/arm/cpu_loop.c b/linux-user/arm/cpu_loop.c index 7be4071751..cf618daa1c 100644 --- a/linux-user/arm/cpu_loop.c +++ b/linux-user/arm/cpu_loop.c @@ -349,8 +349,9 @@ void cpu_loop(CPUARMState *env) env->regs[0] = cpu_get_tls(env); break; default: - gemu_log("qemu: Unsupported ARM syscall: 0x%x\n", - n); + qemu_log_mask(LOG_UNIMP, + "qemu: Unsupported ARM syscall: 0x%x\n", + n); env->regs[0] = -TARGET_ENOSYS; break; } @@ -377,6 +378,7 @@ void cpu_loop(CPUARMState *env) break; case EXCP_SEMIHOST: env->regs[0] = do_arm_semihosting(env); + env->regs[15] += env->thumb ? 2 : 4; break; case EXCP_INTERRUPT: /* just indicate that signals should be handled asap */ diff --git a/linux-user/arm/semihost.c b/linux-user/arm/semihost.c index a16b525eec..a1f0f6050e 100644 --- a/linux-user/arm/semihost.c +++ b/linux-user/arm/semihost.c @@ -14,6 +14,7 @@ #include "cpu.h" #include "hw/semihosting/console.h" #include "qemu.h" +#include int qemu_semihosting_console_outs(CPUArchState *env, target_ulong addr) { @@ -47,3 +48,29 @@ void qemu_semihosting_console_outc(CPUArchState *env, target_ulong addr) } } } + +/* + * For linux-user we can safely block. However as we want to return as + * soon as a character is read we need to tweak the termio to disable + * line buffering. We restore the old mode afterwards in case the + * program is expecting more normal behaviour. This is slow but + * nothing using semihosting console reading is expecting to be fast. + */ +target_ulong qemu_semihosting_console_inc(CPUArchState *env) +{ + uint8_t c; + struct termios old_tio, new_tio; + + /* Disable line-buffering and echo */ + tcgetattr(STDIN_FILENO, &old_tio); + new_tio = old_tio; + new_tio.c_lflag &= (~ICANON & ~ECHO); + tcsetattr(STDIN_FILENO, TCSANOW, &new_tio); + + c = getchar(); + + /* restore config */ + tcsetattr(STDIN_FILENO, TCSANOW, &old_tio); + + return (target_ulong) c; +} diff --git a/linux-user/arm/signal.c b/linux-user/arm/signal.c index b0e753801b..d96fc27ce1 100644 --- a/linux-user/arm/signal.c +++ b/linux-user/arm/signal.c @@ -346,7 +346,7 @@ static void setup_sigframe_v2(struct target_ucontext_v2 *uc, setup_sigcontext(&uc->tuc_mcontext, env, set->sig[0]); /* Save coprocessor signal frame. */ regspace = uc->tuc_regspace; - if (arm_feature(env, ARM_FEATURE_VFP)) { + if (cpu_isar_feature(aa32_vfp_simd, env_archcpu(env))) { regspace = setup_sigframe_v2_vfp(regspace, env); } if (arm_feature(env, ARM_FEATURE_IWMMXT)) { @@ -671,7 +671,7 @@ static int do_sigframe_return_v2(CPUARMState *env, /* Restore coprocessor signal frame */ regspace = uc->tuc_regspace; - if (arm_feature(env, ARM_FEATURE_VFP)) { + if (cpu_isar_feature(aa32_vfp_simd, env_archcpu(env))) { regspace = restore_sigframe_v2_vfp(env, regspace); if (!regspace) { return 1; diff --git a/linux-user/arm/syscall.tbl b/linux-user/arm/syscall.tbl new file mode 100644 index 0000000000..4d1cf74a2c --- /dev/null +++ b/linux-user/arm/syscall.tbl @@ -0,0 +1,453 @@ +# +# Linux system call numbers and entry vectors +# +# The format is: +# [ []] +# +# Where abi is: +# common - for system calls shared between oabi and eabi (may have compat) +# oabi - for oabi-only system calls (may have compat) +# eabi - for eabi-only system calls +# +# For each syscall number, "common" is mutually exclusive with oabi and eabi +# +0 common restart_syscall sys_restart_syscall +1 common exit sys_exit +2 common fork sys_fork +3 common read sys_read +4 common write sys_write +5 common open sys_open +6 common close sys_close +# 7 was sys_waitpid +8 common creat sys_creat +9 common link sys_link +10 common unlink sys_unlink +11 common execve sys_execve +12 common chdir sys_chdir +13 oabi time sys_time32 +14 common mknod sys_mknod +15 common chmod sys_chmod +16 common lchown sys_lchown16 +# 17 was sys_break +# 18 was sys_stat +19 common lseek sys_lseek +20 common getpid sys_getpid +21 common mount sys_mount +22 oabi umount sys_oldumount +23 common setuid sys_setuid16 +24 common getuid sys_getuid16 +25 oabi stime sys_stime32 +26 common ptrace sys_ptrace +27 oabi alarm sys_alarm +# 28 was sys_fstat +29 common pause sys_pause +30 oabi utime sys_utime32 +# 31 was sys_stty +# 32 was sys_gtty +33 common access sys_access +34 common nice sys_nice +# 35 was sys_ftime +36 common sync sys_sync +37 common kill sys_kill +38 common rename sys_rename +39 common mkdir sys_mkdir +40 common rmdir sys_rmdir +41 common dup sys_dup +42 common pipe sys_pipe +43 common times sys_times +# 44 was sys_prof +45 common brk sys_brk +46 common setgid sys_setgid16 +47 common getgid sys_getgid16 +# 48 was sys_signal +49 common geteuid sys_geteuid16 +50 common getegid sys_getegid16 +51 common acct sys_acct +52 common umount2 sys_umount +# 53 was sys_lock +54 common ioctl sys_ioctl +55 common fcntl sys_fcntl +# 56 was sys_mpx +57 common setpgid sys_setpgid +# 58 was sys_ulimit +# 59 was sys_olduname +60 common umask sys_umask +61 common chroot sys_chroot +62 common ustat sys_ustat +63 common dup2 sys_dup2 +64 common getppid sys_getppid +65 common getpgrp sys_getpgrp +66 common setsid sys_setsid +67 common sigaction sys_sigaction +# 68 was sys_sgetmask +# 69 was sys_ssetmask +70 common setreuid sys_setreuid16 +71 common setregid sys_setregid16 +72 common sigsuspend sys_sigsuspend +73 common sigpending sys_sigpending +74 common sethostname sys_sethostname +75 common setrlimit sys_setrlimit +# Back compat 2GB limited rlimit +76 oabi getrlimit sys_old_getrlimit +77 common getrusage sys_getrusage +78 common gettimeofday sys_gettimeofday +79 common settimeofday sys_settimeofday +80 common getgroups sys_getgroups16 +81 common setgroups sys_setgroups16 +82 oabi select sys_old_select +83 common symlink sys_symlink +# 84 was sys_lstat +85 common readlink sys_readlink +86 common uselib sys_uselib +87 common swapon sys_swapon +88 common reboot sys_reboot +89 oabi readdir sys_old_readdir +90 oabi mmap sys_old_mmap +91 common munmap sys_munmap +92 common truncate sys_truncate +93 common ftruncate sys_ftruncate +94 common fchmod sys_fchmod +95 common fchown sys_fchown16 +96 common getpriority sys_getpriority +97 common setpriority sys_setpriority +# 98 was sys_profil +99 common statfs sys_statfs +100 common fstatfs sys_fstatfs +# 101 was sys_ioperm +102 oabi socketcall sys_socketcall sys_oabi_socketcall +103 common syslog sys_syslog +104 common setitimer sys_setitimer +105 common getitimer sys_getitimer +106 common stat sys_newstat +107 common lstat sys_newlstat +108 common fstat sys_newfstat +# 109 was sys_uname +# 110 was sys_iopl +111 common vhangup sys_vhangup +# 112 was sys_idle +# syscall to call a syscall! +113 oabi syscall sys_syscall +114 common wait4 sys_wait4 +115 common swapoff sys_swapoff +116 common sysinfo sys_sysinfo +117 oabi ipc sys_ipc sys_oabi_ipc +118 common fsync sys_fsync +119 common sigreturn sys_sigreturn_wrapper +120 common clone sys_clone +121 common setdomainname sys_setdomainname +122 common uname sys_newuname +# 123 was sys_modify_ldt +124 common adjtimex sys_adjtimex_time32 +125 common mprotect sys_mprotect +126 common sigprocmask sys_sigprocmask +# 127 was sys_create_module +128 common init_module sys_init_module +129 common delete_module sys_delete_module +# 130 was sys_get_kernel_syms +131 common quotactl sys_quotactl +132 common getpgid sys_getpgid +133 common fchdir sys_fchdir +134 common bdflush sys_bdflush +135 common sysfs sys_sysfs +136 common personality sys_personality +# 137 was sys_afs_syscall +138 common setfsuid sys_setfsuid16 +139 common setfsgid sys_setfsgid16 +140 common _llseek sys_llseek +141 common getdents sys_getdents +142 common _newselect sys_select +143 common flock sys_flock +144 common msync sys_msync +145 common readv sys_readv +146 common writev sys_writev +147 common getsid sys_getsid +148 common fdatasync sys_fdatasync +149 common _sysctl sys_sysctl +150 common mlock sys_mlock +151 common munlock sys_munlock +152 common mlockall sys_mlockall +153 common munlockall sys_munlockall +154 common sched_setparam sys_sched_setparam +155 common sched_getparam sys_sched_getparam +156 common sched_setscheduler sys_sched_setscheduler +157 common sched_getscheduler sys_sched_getscheduler +158 common sched_yield sys_sched_yield +159 common sched_get_priority_max sys_sched_get_priority_max +160 common sched_get_priority_min sys_sched_get_priority_min +161 common sched_rr_get_interval sys_sched_rr_get_interval_time32 +162 common nanosleep sys_nanosleep_time32 +163 common mremap sys_mremap +164 common setresuid sys_setresuid16 +165 common getresuid sys_getresuid16 +# 166 was sys_vm86 +# 167 was sys_query_module +168 common poll sys_poll +169 common nfsservctl +170 common setresgid sys_setresgid16 +171 common getresgid sys_getresgid16 +172 common prctl sys_prctl +173 common rt_sigreturn sys_rt_sigreturn_wrapper +174 common rt_sigaction sys_rt_sigaction +175 common rt_sigprocmask sys_rt_sigprocmask +176 common rt_sigpending sys_rt_sigpending +177 common rt_sigtimedwait sys_rt_sigtimedwait_time32 +178 common rt_sigqueueinfo sys_rt_sigqueueinfo +179 common rt_sigsuspend sys_rt_sigsuspend +180 common pread64 sys_pread64 sys_oabi_pread64 +181 common pwrite64 sys_pwrite64 sys_oabi_pwrite64 +182 common chown sys_chown16 +183 common getcwd sys_getcwd +184 common capget sys_capget +185 common capset sys_capset +186 common sigaltstack sys_sigaltstack +187 common sendfile sys_sendfile +# 188 reserved +# 189 reserved +190 common vfork sys_vfork +# SuS compliant getrlimit +191 common ugetrlimit sys_getrlimit +192 common mmap2 sys_mmap2 +193 common truncate64 sys_truncate64 sys_oabi_truncate64 +194 common ftruncate64 sys_ftruncate64 sys_oabi_ftruncate64 +195 common stat64 sys_stat64 sys_oabi_stat64 +196 common lstat64 sys_lstat64 sys_oabi_lstat64 +197 common fstat64 sys_fstat64 sys_oabi_fstat64 +198 common lchown32 sys_lchown +199 common getuid32 sys_getuid +200 common getgid32 sys_getgid +201 common geteuid32 sys_geteuid +202 common getegid32 sys_getegid +203 common setreuid32 sys_setreuid +204 common setregid32 sys_setregid +205 common getgroups32 sys_getgroups +206 common setgroups32 sys_setgroups +207 common fchown32 sys_fchown +208 common setresuid32 sys_setresuid +209 common getresuid32 sys_getresuid +210 common setresgid32 sys_setresgid +211 common getresgid32 sys_getresgid +212 common chown32 sys_chown +213 common setuid32 sys_setuid +214 common setgid32 sys_setgid +215 common setfsuid32 sys_setfsuid +216 common setfsgid32 sys_setfsgid +217 common getdents64 sys_getdents64 +218 common pivot_root sys_pivot_root +219 common mincore sys_mincore +220 common madvise sys_madvise +221 common fcntl64 sys_fcntl64 sys_oabi_fcntl64 +# 222 for tux +# 223 is unused +224 common gettid sys_gettid +225 common readahead sys_readahead sys_oabi_readahead +226 common setxattr sys_setxattr +227 common lsetxattr sys_lsetxattr +228 common fsetxattr sys_fsetxattr +229 common getxattr sys_getxattr +230 common lgetxattr sys_lgetxattr +231 common fgetxattr sys_fgetxattr +232 common listxattr sys_listxattr +233 common llistxattr sys_llistxattr +234 common flistxattr sys_flistxattr +235 common removexattr sys_removexattr +236 common lremovexattr sys_lremovexattr +237 common fremovexattr sys_fremovexattr +238 common tkill sys_tkill +239 common sendfile64 sys_sendfile64 +240 common futex sys_futex_time32 +241 common sched_setaffinity sys_sched_setaffinity +242 common sched_getaffinity sys_sched_getaffinity +243 common io_setup sys_io_setup +244 common io_destroy sys_io_destroy +245 common io_getevents sys_io_getevents_time32 +246 common io_submit sys_io_submit +247 common io_cancel sys_io_cancel +248 common exit_group sys_exit_group +249 common lookup_dcookie sys_lookup_dcookie +250 common epoll_create sys_epoll_create +251 common epoll_ctl sys_epoll_ctl sys_oabi_epoll_ctl +252 common epoll_wait sys_epoll_wait sys_oabi_epoll_wait +253 common remap_file_pages sys_remap_file_pages +# 254 for set_thread_area +# 255 for get_thread_area +256 common set_tid_address sys_set_tid_address +257 common timer_create sys_timer_create +258 common timer_settime sys_timer_settime32 +259 common timer_gettime sys_timer_gettime32 +260 common timer_getoverrun sys_timer_getoverrun +261 common timer_delete sys_timer_delete +262 common clock_settime sys_clock_settime32 +263 common clock_gettime sys_clock_gettime32 +264 common clock_getres sys_clock_getres_time32 +265 common clock_nanosleep sys_clock_nanosleep_time32 +266 common statfs64 sys_statfs64_wrapper +267 common fstatfs64 sys_fstatfs64_wrapper +268 common tgkill sys_tgkill +269 common utimes sys_utimes_time32 +270 common arm_fadvise64_64 sys_arm_fadvise64_64 +271 common pciconfig_iobase sys_pciconfig_iobase +272 common pciconfig_read sys_pciconfig_read +273 common pciconfig_write sys_pciconfig_write +274 common mq_open sys_mq_open +275 common mq_unlink sys_mq_unlink +276 common mq_timedsend sys_mq_timedsend_time32 +277 common mq_timedreceive sys_mq_timedreceive_time32 +278 common mq_notify sys_mq_notify +279 common mq_getsetattr sys_mq_getsetattr +280 common waitid sys_waitid +281 common socket sys_socket +282 common bind sys_bind sys_oabi_bind +283 common connect sys_connect sys_oabi_connect +284 common listen sys_listen +285 common accept sys_accept +286 common getsockname sys_getsockname +287 common getpeername sys_getpeername +288 common socketpair sys_socketpair +289 common send sys_send +290 common sendto sys_sendto sys_oabi_sendto +291 common recv sys_recv +292 common recvfrom sys_recvfrom +293 common shutdown sys_shutdown +294 common setsockopt sys_setsockopt +295 common getsockopt sys_getsockopt +296 common sendmsg sys_sendmsg sys_oabi_sendmsg +297 common recvmsg sys_recvmsg +298 common semop sys_semop sys_oabi_semop +299 common semget sys_semget +300 common semctl sys_old_semctl +301 common msgsnd sys_msgsnd +302 common msgrcv sys_msgrcv +303 common msgget sys_msgget +304 common msgctl sys_old_msgctl +305 common shmat sys_shmat +306 common shmdt sys_shmdt +307 common shmget sys_shmget +308 common shmctl sys_old_shmctl +309 common add_key sys_add_key +310 common request_key sys_request_key +311 common keyctl sys_keyctl +312 common semtimedop sys_semtimedop_time32 sys_oabi_semtimedop +313 common vserver +314 common ioprio_set sys_ioprio_set +315 common ioprio_get sys_ioprio_get +316 common inotify_init sys_inotify_init +317 common inotify_add_watch sys_inotify_add_watch +318 common inotify_rm_watch sys_inotify_rm_watch +319 common mbind sys_mbind +320 common get_mempolicy sys_get_mempolicy +321 common set_mempolicy sys_set_mempolicy +322 common openat sys_openat +323 common mkdirat sys_mkdirat +324 common mknodat sys_mknodat +325 common fchownat sys_fchownat +326 common futimesat sys_futimesat_time32 +327 common fstatat64 sys_fstatat64 sys_oabi_fstatat64 +328 common unlinkat sys_unlinkat +329 common renameat sys_renameat +330 common linkat sys_linkat +331 common symlinkat sys_symlinkat +332 common readlinkat sys_readlinkat +333 common fchmodat sys_fchmodat +334 common faccessat sys_faccessat +335 common pselect6 sys_pselect6_time32 +336 common ppoll sys_ppoll_time32 +337 common unshare sys_unshare +338 common set_robust_list sys_set_robust_list +339 common get_robust_list sys_get_robust_list +340 common splice sys_splice +341 common arm_sync_file_range sys_sync_file_range2 +342 common tee sys_tee +343 common vmsplice sys_vmsplice +344 common move_pages sys_move_pages +345 common getcpu sys_getcpu +346 common epoll_pwait sys_epoll_pwait +347 common kexec_load sys_kexec_load +348 common utimensat sys_utimensat_time32 +349 common signalfd sys_signalfd +350 common timerfd_create sys_timerfd_create +351 common eventfd sys_eventfd +352 common fallocate sys_fallocate +353 common timerfd_settime sys_timerfd_settime32 +354 common timerfd_gettime sys_timerfd_gettime32 +355 common signalfd4 sys_signalfd4 +356 common eventfd2 sys_eventfd2 +357 common epoll_create1 sys_epoll_create1 +358 common dup3 sys_dup3 +359 common pipe2 sys_pipe2 +360 common inotify_init1 sys_inotify_init1 +361 common preadv sys_preadv +362 common pwritev sys_pwritev +363 common rt_tgsigqueueinfo sys_rt_tgsigqueueinfo +364 common perf_event_open sys_perf_event_open +365 common recvmmsg sys_recvmmsg_time32 +366 common accept4 sys_accept4 +367 common fanotify_init sys_fanotify_init +368 common fanotify_mark sys_fanotify_mark +369 common prlimit64 sys_prlimit64 +370 common name_to_handle_at sys_name_to_handle_at +371 common open_by_handle_at sys_open_by_handle_at +372 common clock_adjtime sys_clock_adjtime32 +373 common syncfs sys_syncfs +374 common sendmmsg sys_sendmmsg +375 common setns sys_setns +376 common process_vm_readv sys_process_vm_readv +377 common process_vm_writev sys_process_vm_writev +378 common kcmp sys_kcmp +379 common finit_module sys_finit_module +380 common sched_setattr sys_sched_setattr +381 common sched_getattr sys_sched_getattr +382 common renameat2 sys_renameat2 +383 common seccomp sys_seccomp +384 common getrandom sys_getrandom +385 common memfd_create sys_memfd_create +386 common bpf sys_bpf +387 common execveat sys_execveat +388 common userfaultfd sys_userfaultfd +389 common membarrier sys_membarrier +390 common mlock2 sys_mlock2 +391 common copy_file_range sys_copy_file_range +392 common preadv2 sys_preadv2 +393 common pwritev2 sys_pwritev2 +394 common pkey_mprotect sys_pkey_mprotect +395 common pkey_alloc sys_pkey_alloc +396 common pkey_free sys_pkey_free +397 common statx sys_statx +398 common rseq sys_rseq +399 common io_pgetevents sys_io_pgetevents_time32 +400 common migrate_pages sys_migrate_pages +401 common kexec_file_load sys_kexec_file_load +# 402 is unused +403 common clock_gettime64 sys_clock_gettime +404 common clock_settime64 sys_clock_settime +405 common clock_adjtime64 sys_clock_adjtime +406 common clock_getres_time64 sys_clock_getres +407 common clock_nanosleep_time64 sys_clock_nanosleep +408 common timer_gettime64 sys_timer_gettime +409 common timer_settime64 sys_timer_settime +410 common timerfd_gettime64 sys_timerfd_gettime +411 common timerfd_settime64 sys_timerfd_settime +412 common utimensat_time64 sys_utimensat +413 common pselect6_time64 sys_pselect6 +414 common ppoll_time64 sys_ppoll +416 common io_pgetevents_time64 sys_io_pgetevents +417 common recvmmsg_time64 sys_recvmmsg +418 common mq_timedsend_time64 sys_mq_timedsend +419 common mq_timedreceive_time64 sys_mq_timedreceive +420 common semtimedop_time64 sys_semtimedop +421 common rt_sigtimedwait_time64 sys_rt_sigtimedwait +422 common futex_time64 sys_futex +423 common sched_rr_get_interval_time64 sys_sched_rr_get_interval +424 common pidfd_send_signal sys_pidfd_send_signal +425 common io_uring_setup sys_io_uring_setup +426 common io_uring_enter sys_io_uring_enter +427 common io_uring_register sys_io_uring_register +428 common open_tree sys_open_tree +429 common move_mount sys_move_mount +430 common fsopen sys_fsopen +431 common fsconfig sys_fsconfig +432 common fsmount sys_fsmount +433 common fspick sys_fspick +434 common pidfd_open sys_pidfd_open +435 common clone3 sys_clone3 +437 common openat2 sys_openat2 +438 common pidfd_getfd sys_pidfd_getfd diff --git a/linux-user/arm/syscall_nr.h b/linux-user/arm/syscall_nr.h deleted file mode 100644 index e7eda0d766..0000000000 --- a/linux-user/arm/syscall_nr.h +++ /dev/null @@ -1,403 +0,0 @@ -/* - * This file contains the system call numbers. - */ - -#ifndef LINUX_USER_ARM_SYSCALL_NR_H -#define LINUX_USER_ARM_SYSCALL_NR_H - -#define TARGET_NR_restart_syscall ( 0) -#define TARGET_NR_exit ( 1) -#define TARGET_NR_fork ( 2) -#define TARGET_NR_read ( 3) -#define TARGET_NR_write ( 4) -#define TARGET_NR_open ( 5) -#define TARGET_NR_close ( 6) -#define TARGET_NR_waitpid ( 7) /* removed */ -#define TARGET_NR_creat ( 8) -#define TARGET_NR_link ( 9) -#define TARGET_NR_unlink ( 10) -#define TARGET_NR_execve ( 11) -#define TARGET_NR_chdir ( 12) -#define TARGET_NR_time ( 13) -#define TARGET_NR_mknod ( 14) -#define TARGET_NR_chmod ( 15) -#define TARGET_NR_lchown ( 16) -#define TARGET_NR_break ( 17) /* removed */ - /* 18 was sys_stat */ -#define TARGET_NR_lseek ( 19) -#define TARGET_NR_getpid ( 20) -#define TARGET_NR_mount ( 21) -#define TARGET_NR_umount ( 22) -#define TARGET_NR_setuid ( 23) -#define TARGET_NR_getuid ( 24) -#define TARGET_NR_stime ( 25) -#define TARGET_NR_ptrace ( 26) -#define TARGET_NR_alarm ( 27) - -#define TARGET_NR_pause ( 29) -#define TARGET_NR_utime ( 30) -#define TARGET_NR_stty ( 31) /* removed */ -#define TARGET_NR_gtty ( 32) /* removed */ -#define TARGET_NR_access ( 33) -#define TARGET_NR_nice ( 34) -#define TARGET_NR_ftime ( 35) /* removed */ -#define TARGET_NR_sync ( 36) -#define TARGET_NR_kill ( 37) -#define TARGET_NR_rename ( 38) -#define TARGET_NR_mkdir ( 39) -#define TARGET_NR_rmdir ( 40) -#define TARGET_NR_dup ( 41) -#define TARGET_NR_pipe ( 42) -#define TARGET_NR_times ( 43) -#define TARGET_NR_prof ( 44) /* removed */ -#define TARGET_NR_brk ( 45) -#define TARGET_NR_setgid ( 46) -#define TARGET_NR_getgid ( 47) -#define TARGET_NR_signal ( 48) /* removed */ -#define TARGET_NR_geteuid ( 49) -#define TARGET_NR_getegid ( 50) -#define TARGET_NR_acct ( 51) -#define TARGET_NR_umount2 ( 52) -#define TARGET_NR_lock ( 53) /* removed */ -#define TARGET_NR_ioctl ( 54) -#define TARGET_NR_fcntl ( 55) -#define TARGET_NR_mpx ( 56) /* removed */ -#define TARGET_NR_setpgid ( 57) -#define TARGET_NR_ulimit ( 58) /* removed */ - /* 59 was sys_olduname */ -#define TARGET_NR_umask ( 60) -#define TARGET_NR_chroot ( 61) -#define TARGET_NR_ustat ( 62) -#define TARGET_NR_dup2 ( 63) -#define TARGET_NR_getppid ( 64) -#define TARGET_NR_getpgrp ( 65) -#define TARGET_NR_setsid ( 66) -#define TARGET_NR_sigaction ( 67) -#define TARGET_NR_sgetmask ( 68) /* removed */ -#define TARGET_NR_ssetmask ( 69) /* removed */ -#define TARGET_NR_setreuid ( 70) -#define TARGET_NR_setregid ( 71) -#define TARGET_NR_sigsuspend ( 72) -#define TARGET_NR_sigpending ( 73) -#define TARGET_NR_sethostname ( 74) -#define TARGET_NR_setrlimit ( 75) -#define TARGET_NR_getrlimit ( 76) /* Back compat 2GB limited rlimit */ -#define TARGET_NR_getrusage ( 77) -#define TARGET_NR_gettimeofday ( 78) -#define TARGET_NR_settimeofday ( 79) -#define TARGET_NR_getgroups ( 80) -#define TARGET_NR_setgroups ( 81) -#define TARGET_NR_select ( 82) -#define TARGET_NR_symlink ( 83) - /* 84 was sys_lstat */ -#define TARGET_NR_readlink ( 85) -#define TARGET_NR_uselib ( 86) -#define TARGET_NR_swapon ( 87) -#define TARGET_NR_reboot ( 88) -#define TARGET_NR_readdir ( 89) -#define TARGET_NR_mmap ( 90) -#define TARGET_NR_munmap ( 91) -#define TARGET_NR_truncate ( 92) -#define TARGET_NR_ftruncate ( 93) -#define TARGET_NR_fchmod ( 94) -#define TARGET_NR_fchown ( 95) -#define TARGET_NR_getpriority ( 96) -#define TARGET_NR_setpriority ( 97) -#define TARGET_NR_profil ( 98) /* removed */ -#define TARGET_NR_statfs ( 99) -#define TARGET_NR_fstatfs (100) -#define TARGET_NR_ioperm (101) -#define TARGET_NR_socketcall (102) -#define TARGET_NR_syslog (103) -#define TARGET_NR_setitimer (104) -#define TARGET_NR_getitimer (105) -#define TARGET_NR_stat (106) -#define TARGET_NR_lstat (107) -#define TARGET_NR_fstat (108) - /* 109 was sys_uname */ - /* 110 was sys_iopl */ -#define TARGET_NR_vhangup (111) -#define TARGET_NR_idle (112) -#define TARGET_NR_syscall (113) /* syscall to call a syscall! */ -#define TARGET_NR_wait4 (114) -#define TARGET_NR_swapoff (115) -#define TARGET_NR_sysinfo (116) -#define TARGET_NR_ipc (117) -#define TARGET_NR_fsync (118) -#define TARGET_NR_sigreturn (119) -#define TARGET_NR_clone (120) -#define TARGET_NR_setdomainname (121) -#define TARGET_NR_uname (122) -#define TARGET_NR_modify_ldt (123) -#define TARGET_NR_adjtimex (124) -#define TARGET_NR_mprotect (125) -#define TARGET_NR_sigprocmask (126) -#define TARGET_NR_create_module (127) /* removed */ -#define TARGET_NR_init_module (128) -#define TARGET_NR_delete_module (129) -#define TARGET_NR_get_kernel_syms (130) /* removed */ -#define TARGET_NR_quotactl (131) -#define TARGET_NR_getpgid (132) -#define TARGET_NR_fchdir (133) -#define TARGET_NR_bdflush (134) -#define TARGET_NR_sysfs (135) -#define TARGET_NR_personality (136) -#define TARGET_NR_afs_syscall (137) /* Syscall for Andrew File System */ -#define TARGET_NR_setfsuid (138) -#define TARGET_NR_setfsgid (139) -#define TARGET_NR__llseek (140) -#define TARGET_NR_getdents (141) -#define TARGET_NR__newselect (142) -#define TARGET_NR_flock (143) -#define TARGET_NR_msync (144) -#define TARGET_NR_readv (145) -#define TARGET_NR_writev (146) -#define TARGET_NR_getsid (147) -#define TARGET_NR_fdatasync (148) -#define TARGET_NR__sysctl (149) -#define TARGET_NR_mlock (150) -#define TARGET_NR_munlock (151) -#define TARGET_NR_mlockall (152) -#define TARGET_NR_munlockall (153) -#define TARGET_NR_sched_setparam (154) -#define TARGET_NR_sched_getparam (155) -#define TARGET_NR_sched_setscheduler (156) -#define TARGET_NR_sched_getscheduler (157) -#define TARGET_NR_sched_yield (158) -#define TARGET_NR_sched_get_priority_max (159) -#define TARGET_NR_sched_get_priority_min (160) -#define TARGET_NR_sched_rr_get_interval (161) -#define TARGET_NR_nanosleep (162) -#define TARGET_NR_mremap (163) -#define TARGET_NR_setresuid (164) -#define TARGET_NR_getresuid (165) -#define TARGET_NR_vm86 (166) /* removed */ -#define TARGET_NR_query_module (167) /* removed */ -#define TARGET_NR_poll (168) -#define TARGET_NR_nfsservctl (169) -#define TARGET_NR_setresgid (170) -#define TARGET_NR_getresgid (171) -#define TARGET_NR_prctl (172) -#define TARGET_NR_rt_sigreturn (173) -#define TARGET_NR_rt_sigaction (174) -#define TARGET_NR_rt_sigprocmask (175) -#define TARGET_NR_rt_sigpending (176) -#define TARGET_NR_rt_sigtimedwait (177) -#define TARGET_NR_rt_sigqueueinfo (178) -#define TARGET_NR_rt_sigsuspend (179) -#define TARGET_NR_pread64 (180) -#define TARGET_NR_pwrite64 (181) -#define TARGET_NR_chown (182) -#define TARGET_NR_getcwd (183) -#define TARGET_NR_capget (184) -#define TARGET_NR_capset (185) -#define TARGET_NR_sigaltstack (186) -#define TARGET_NR_sendfile (187) - /* 188 reserved */ - /* 189 reserved */ -#define TARGET_NR_vfork (190) -#define TARGET_NR_ugetrlimit (191) /* SuS compliant getrlimit */ -#define TARGET_NR_mmap2 (192) -#define TARGET_NR_truncate64 (193) -#define TARGET_NR_ftruncate64 (194) -#define TARGET_NR_stat64 (195) -#define TARGET_NR_lstat64 (196) -#define TARGET_NR_fstat64 (197) -#define TARGET_NR_lchown32 (198) -#define TARGET_NR_getuid32 (199) -#define TARGET_NR_getgid32 (200) -#define TARGET_NR_geteuid32 (201) -#define TARGET_NR_getegid32 (202) -#define TARGET_NR_setreuid32 (203) -#define TARGET_NR_setregid32 (204) -#define TARGET_NR_getgroups32 (205) -#define TARGET_NR_setgroups32 (206) -#define TARGET_NR_fchown32 (207) -#define TARGET_NR_setresuid32 (208) -#define TARGET_NR_getresuid32 (209) -#define TARGET_NR_setresgid32 (210) -#define TARGET_NR_getresgid32 (211) -#define TARGET_NR_chown32 (212) -#define TARGET_NR_setuid32 (213) -#define TARGET_NR_setgid32 (214) -#define TARGET_NR_setfsuid32 (215) -#define TARGET_NR_setfsgid32 (216) -#define TARGET_NR_getdents64 (217) -#define TARGET_NR_pivot_root (218) -#define TARGET_NR_mincore (219) -#define TARGET_NR_madvise (220) -#define TARGET_NR_fcntl64 (221) - /* 222 for tux */ - /* 223 is unused */ -#define TARGET_NR_gettid (224) -#define TARGET_NR_readahead (225) -#define TARGET_NR_setxattr (226) -#define TARGET_NR_lsetxattr (227) -#define TARGET_NR_fsetxattr (228) -#define TARGET_NR_getxattr (229) -#define TARGET_NR_lgetxattr (230) -#define TARGET_NR_fgetxattr (231) -#define TARGET_NR_listxattr (232) -#define TARGET_NR_llistxattr (233) -#define TARGET_NR_flistxattr (234) -#define TARGET_NR_removexattr (235) -#define TARGET_NR_lremovexattr (236) -#define TARGET_NR_fremovexattr (237) -#define TARGET_NR_tkill (238) -#define TARGET_NR_sendfile64 (239) -#define TARGET_NR_futex (240) -#define TARGET_NR_sched_setaffinity (241) -#define TARGET_NR_sched_getaffinity (242) -#define TARGET_NR_io_setup (243) -#define TARGET_NR_io_destroy (244) -#define TARGET_NR_io_getevents (245) -#define TARGET_NR_io_submit (246) -#define TARGET_NR_io_cancel (247) -#define TARGET_NR_exit_group (248) -#define TARGET_NR_lookup_dcookie (249) -#define TARGET_NR_epoll_create (250) -#define TARGET_NR_epoll_ctl (251) -#define TARGET_NR_epoll_wait (252) -#define TARGET_NR_remap_file_pages (253) - /* 254 for set_thread_area */ - /* 255 for get_thread_area */ - /* 256 for set_tid_address */ -#define TARGET_NR_set_tid_address 256 -#define TARGET_NR_timer_create 257 -#define TARGET_NR_timer_settime 258 -#define TARGET_NR_timer_gettime 259 -#define TARGET_NR_timer_getoverrun 260 -#define TARGET_NR_timer_delete 261 -#define TARGET_NR_clock_settime 262 -#define TARGET_NR_clock_gettime 263 -#define TARGET_NR_clock_getres 264 -#define TARGET_NR_clock_nanosleep 265 -#define TARGET_NR_statfs64 266 -#define TARGET_NR_fstatfs64 267 -#define TARGET_NR_tgkill 268 -#define TARGET_NR_utimes 269 -#define TARGET_NR_arm_fadvise64_64 270 -#define TARGET_NR_pciconfig_iobase 271 -#define TARGET_NR_pciconfig_read 272 -#define TARGET_NR_pciconfig_write 273 -#define TARGET_NR_mq_open 274 -#define TARGET_NR_mq_unlink 275 -#define TARGET_NR_mq_timedsend 276 -#define TARGET_NR_mq_timedreceive 277 -#define TARGET_NR_mq_notify 278 -#define TARGET_NR_mq_getsetattr 279 -#define TARGET_NR_waitid 280 -#define TARGET_NR_socket 281 -#define TARGET_NR_bind 282 -#define TARGET_NR_connect 283 -#define TARGET_NR_listen 284 -#define TARGET_NR_accept 285 -#define TARGET_NR_getsockname 286 -#define TARGET_NR_getpeername 287 -#define TARGET_NR_socketpair 288 -#define TARGET_NR_send 289 -#define TARGET_NR_sendto 290 -#define TARGET_NR_recv 291 -#define TARGET_NR_recvfrom 292 -#define TARGET_NR_shutdown 293 -#define TARGET_NR_setsockopt 294 -#define TARGET_NR_getsockopt 295 -#define TARGET_NR_sendmsg 296 -#define TARGET_NR_recvmsg 297 -#define TARGET_NR_semop 298 -#define TARGET_NR_semget 299 -#define TARGET_NR_semctl 300 -#define TARGET_NR_msgsnd 301 -#define TARGET_NR_msgrcv 302 -#define TARGET_NR_msgget 303 -#define TARGET_NR_msgctl 304 -#define TARGET_NR_shmat 305 -#define TARGET_NR_shmdt 306 -#define TARGET_NR_shmget 307 -#define TARGET_NR_shmctl 308 -#define TARGET_NR_add_key 309 -#define TARGET_NR_request_key 310 -#define TARGET_NR_keyctl 311 -#define TARGET_NR_semtimedop 312 -#define TARGET_NR_vserver 313 -#define TARGET_NR_ioprio_set 314 -#define TARGET_NR_ioprio_get 315 -#define TARGET_NR_inotify_init 316 -#define TARGET_NR_inotify_add_watch 317 -#define TARGET_NR_inotify_rm_watch 318 -#define TARGET_NR_mbind 319 -#define TARGET_NR_get_mempolicy 320 -#define TARGET_NR_set_mempolicy 321 -#define TARGET_NR_openat (322) -#define TARGET_NR_mkdirat (323) -#define TARGET_NR_mknodat (324) -#define TARGET_NR_fchownat (325) -#define TARGET_NR_futimesat (326) -#define TARGET_NR_fstatat64 (327) -#define TARGET_NR_unlinkat (328) -#define TARGET_NR_renameat (329) -#define TARGET_NR_linkat (330) -#define TARGET_NR_symlinkat (331) -#define TARGET_NR_readlinkat (332) -#define TARGET_NR_fchmodat (333) -#define TARGET_NR_faccessat (334) -#define TARGET_NR_pselect6 (335) -#define TARGET_NR_ppoll (336) -#define TARGET_NR_unshare (337) -#define TARGET_NR_set_robust_list (338) -#define TARGET_NR_get_robust_list (339) -#define TARGET_NR_splice (340) -#define TARGET_NR_arm_sync_file_range (341) -#define TARGET_NR_sync_file_range2 TARGET_NR_arm_sync_file_range -#define TARGET_NR_tee (342) -#define TARGET_NR_vmsplice (343) -#define TARGET_NR_move_pages (344) -#define TARGET_NR_getcpu (345) -#define TARGET_NR_epoll_pwait (346) -#define TARGET_NR_kexec_load (347) -#define TARGET_NR_utimensat (348) -#define TARGET_NR_signalfd (349) -#define TARGET_NR_timerfd_create (350) -#define TARGET_NR_eventfd (351) -#define TARGET_NR_fallocate (352) -#define TARGET_NR_timerfd_settime (353) -#define TARGET_NR_timerfd_gettime (354) -#define TARGET_NR_signalfd4 (355) -#define TARGET_NR_eventfd2 (356) -#define TARGET_NR_epoll_create1 (357) -#define TARGET_NR_dup3 (358) -#define TARGET_NR_pipe2 (359) -#define TARGET_NR_inotify_init1 (360) -#define TARGET_NR_preadv (361) -#define TARGET_NR_pwritev (362) -#define TARGET_NR_rt_tgsigqueueinfo (363) -#define TARGET_NR_perf_event_open (364) -#define TARGET_NR_recvmmsg (365) -#define TARGET_NR_accept4 (366) -#define TARGET_NR_fanotify_init (367) -#define TARGET_NR_fanotify_mark (368) -#define TARGET_NR_prlimit64 (369) -#define TARGET_NR_name_to_handle_at (370) -#define TARGET_NR_open_by_handle_at (371) -#define TARGET_NR_clock_adjtime (372) -#define TARGET_NR_syncfs (373) -#define TARGET_NR_sendmmsg (374) -#define TARGET_NR_setns (375) -#define TARGET_NR_process_vm_readv (376) -#define TARGET_NR_process_vm_writev (377) -#define TARGET_NR_kcmp (378) -#define TARGET_NR_finit_module (379) - -#define TARGET_NR_sched_setattr (380) -#define TARGET_NR_sched_getattr (381) -#define TARGET_NR_renameat2 (382) -#define TARGET_NR_seccomp (383) -#define TARGET_NR_getrandom (384) -#define TARGET_NR_memfd_create (385) -#define TARGET_NR_bpf (386) -#define TARGET_NR_execveat (387) -#define TARGET_NR_userfaultfd (388) -#define TARGET_NR_membarrier (389) -#define TARGET_NR_mlock2 (390) - -#endif diff --git a/linux-user/arm/syscallhdr.sh b/linux-user/arm/syscallhdr.sh new file mode 100644 index 0000000000..4c952b2cfb --- /dev/null +++ b/linux-user/arm/syscallhdr.sh @@ -0,0 +1,31 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 + +in="$1" +out="$2" +my_abis=`echo "($3)" | tr ',' '|'` +prefix="$4" +offset="$5" + +fileguard=LINUX_USER_ARM_`basename "$out" | sed \ + -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/' \ + -e 's/[^A-Z0-9_]/_/g' -e 's/__/_/g'` +if echo $out | grep -q uapi; then + fileguard="_UAPI$fileguard" +fi +grep -E "^[0-9A-Fa-fXx]+[[:space:]]+${my_abis}" "$in" | sort -n | ( + echo "#ifndef ${fileguard}" + echo "#define ${fileguard} 1" + echo "" + + while read nr abi name entry ; do + if [ -z "$offset" ]; then + echo "#define TARGET_NR_${prefix}${name} $nr" + else + echo "#define TARGET_NR_${prefix}${name} ($offset + $nr)" + fi + done + + echo "" + echo "#endif /* ${fileguard} */" +) > "$out" diff --git a/linux-user/elfload.c b/linux-user/elfload.c index f6693e5760..619c054cc4 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -10,6 +10,7 @@ #include "qemu/path.h" #include "qemu/queue.h" #include "qemu/guest-random.h" +#include "qemu/units.h" #ifdef _ARCH_PPC64 #undef ARCH_DLINFO @@ -467,22 +468,25 @@ static uint32_t get_elf_hwcap(void) /* EDSP is in v5TE and above, but all our v5 CPUs are v5TE */ GET_FEATURE(ARM_FEATURE_V5, ARM_HWCAP_ARM_EDSP); - GET_FEATURE(ARM_FEATURE_VFP, ARM_HWCAP_ARM_VFP); GET_FEATURE(ARM_FEATURE_IWMMXT, ARM_HWCAP_ARM_IWMMXT); GET_FEATURE(ARM_FEATURE_THUMB2EE, ARM_HWCAP_ARM_THUMBEE); GET_FEATURE(ARM_FEATURE_NEON, ARM_HWCAP_ARM_NEON); - GET_FEATURE(ARM_FEATURE_VFP3, ARM_HWCAP_ARM_VFPv3); GET_FEATURE(ARM_FEATURE_V6K, ARM_HWCAP_ARM_TLS); - GET_FEATURE(ARM_FEATURE_VFP4, ARM_HWCAP_ARM_VFPv4); - GET_FEATURE_ID(arm_div, ARM_HWCAP_ARM_IDIVA); - GET_FEATURE_ID(thumb_div, ARM_HWCAP_ARM_IDIVT); - /* All QEMU's VFPv3 CPUs have 32 registers, see VFP_DREG in translate.c. - * Note that the ARM_HWCAP_ARM_VFPv3D16 bit is always the inverse of - * ARM_HWCAP_ARM_VFPD32 (and so always clear for QEMU); it is unrelated - * to our VFP_FP16 feature bit. - */ - GET_FEATURE(ARM_FEATURE_VFP3, ARM_HWCAP_ARM_VFPD32); GET_FEATURE(ARM_FEATURE_LPAE, ARM_HWCAP_ARM_LPAE); + GET_FEATURE_ID(aa32_arm_div, ARM_HWCAP_ARM_IDIVA); + GET_FEATURE_ID(aa32_thumb_div, ARM_HWCAP_ARM_IDIVT); + GET_FEATURE_ID(aa32_vfp, ARM_HWCAP_ARM_VFP); + + if (cpu_isar_feature(aa32_fpsp_v3, cpu) || + cpu_isar_feature(aa32_fpdp_v3, cpu)) { + hwcaps |= ARM_HWCAP_ARM_VFPv3; + if (cpu_isar_feature(aa32_simd_r32, cpu)) { + hwcaps |= ARM_HWCAP_ARM_VFPD32; + } else { + hwcaps |= ARM_HWCAP_ARM_VFPv3D16; + } + } + GET_FEATURE_ID(aa32_simdfmac, ARM_HWCAP_ARM_VFPv4); return hwcaps; } @@ -656,6 +660,9 @@ static uint32_t get_elf_hwcap(void) GET_FEATURE_ID(aa64_jscvt, ARM_HWCAP_A64_JSCVT); GET_FEATURE_ID(aa64_sb, ARM_HWCAP_A64_SB); GET_FEATURE_ID(aa64_condm_4, ARM_HWCAP_A64_FLAGM); + GET_FEATURE_ID(aa64_dcpop, ARM_HWCAP_A64_DCPOP); + GET_FEATURE_ID(aa64_rcpc_8_3, ARM_HWCAP_A64_LRCPC); + GET_FEATURE_ID(aa64_rcpc_8_4, ARM_HWCAP_A64_ILRCPC); return hwcaps; } @@ -665,6 +672,7 @@ static uint32_t get_elf_hwcap2(void) ARMCPU *cpu = ARM_CPU(thread_cpu); uint32_t hwcaps = 0; + GET_FEATURE_ID(aa64_dcpodp, ARM_HWCAP2_A64_DCPODP); GET_FEATURE_ID(aa64_condm_5, ARM_HWCAP2_A64_FLAGM2); GET_FEATURE_ID(aa64_frint, ARM_HWCAP2_A64_FRINT); @@ -1565,7 +1573,7 @@ struct exec ~(abi_ulong)(TARGET_ELF_EXEC_PAGESIZE-1)) #define TARGET_ELF_PAGEOFFSET(_v) ((_v) & (TARGET_ELF_EXEC_PAGESIZE-1)) -#define DLINFO_ITEMS 15 +#define DLINFO_ITEMS 16 static inline void memcpy_fromfs(void * to, const void * from, unsigned long n) { @@ -2029,6 +2037,7 @@ static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc, NEW_AUX_ENT(AT_CLKTCK, (abi_ulong) sysconf(_SC_CLK_TCK)); NEW_AUX_ENT(AT_RANDOM, (abi_ulong) u_rand_bytes); NEW_AUX_ENT(AT_SECURE, (abi_ulong) qemu_getauxval(AT_SECURE)); + NEW_AUX_ENT(AT_EXECFN, info->file_string); #ifdef ELF_HWCAP2 NEW_AUX_ENT(AT_HWCAP2, (abi_ulong) ELF_HWCAP2); @@ -2163,6 +2172,8 @@ unsigned long init_guest_space(unsigned long host_start, /* Check to see if the address is valid. */ if (host_start && real_start != current_start) { + qemu_log_mask(CPU_LOG_PAGE, "invalid %lx && %lx != %lx\n", + host_start, real_start, current_start); goto try_again; } @@ -2189,7 +2200,7 @@ unsigned long init_guest_space(unsigned long host_start, * to where we need to put the commpage. */ munmap((void *)real_start, host_size); - real_size = aligned_size + qemu_host_page_size; + real_size = aligned_size + align; real_start = (unsigned long) mmap((void *)real_start, real_size, PROT_NONE, flags, -1, 0); if (real_start == (unsigned long)-1) { @@ -2231,7 +2242,11 @@ unsigned long init_guest_space(unsigned long host_start, * probably a bad strategy if not, which means we got here * because of trouble with ARM commpage setup. */ - munmap((void *)real_start, real_size); + if (munmap((void *)real_start, real_size) != 0) { + error_report("%s: failed to unmap %lx:%lx (%s)", __func__, + real_start, real_size, strerror(errno)); + abort(); + } current_start += align; if (host_start == current_start) { /* Theoretically possible if host doesn't have any suitably @@ -2362,24 +2377,51 @@ static void load_elf_image(const char *image_name, int image_fd, } } - load_addr = loaddr; - if (ehdr->e_type == ET_DYN) { - /* The image indicates that it can be loaded anywhere. Find a - location that can hold the memory space required. If the - image is pre-linked, LOADDR will be non-zero. Since we do - not supply MAP_FIXED here we'll use that address if and - only if it remains available. */ - load_addr = target_mmap(loaddr, hiaddr - loaddr, PROT_NONE, - MAP_PRIVATE | MAP_ANON | MAP_NORESERVE, - -1, 0); - if (load_addr == -1) { - goto exit_perror; + if (pinterp_name != NULL) { + /* + * This is the main executable. + * + * Reserve extra space for brk. + * We hold on to this space while placing the interpreter + * and the stack, lest they be placed immediately after + * the data segment and block allocation from the brk. + * + * 16MB is chosen as "large enough" without being so large + * as to allow the result to not fit with a 32-bit guest on + * a 32-bit host. + */ + info->reserve_brk = 16 * MiB; + hiaddr += info->reserve_brk; + + if (ehdr->e_type == ET_EXEC) { + /* + * Make sure that the low address does not conflict with + * MMAP_MIN_ADDR or the QEMU application itself. + */ + probe_guest_base(image_name, loaddr, hiaddr); } - } else if (pinterp_name != NULL) { - /* This is the main executable. Make sure that the low - address does not conflict with MMAP_MIN_ADDR or the - QEMU application itself. */ - probe_guest_base(image_name, loaddr, hiaddr); + } + + /* + * Reserve address space for all of this. + * + * In the case of ET_EXEC, we supply MAP_FIXED so that we get + * exactly the address range that is required. + * + * Otherwise this is ET_DYN, and we are searching for a location + * that can hold the memory space required. If the image is + * pre-linked, LOADDR will be non-zero, and the kernel should + * honor that address if it happens to be free. + * + * In both cases, we will overwrite pages in this range with mappings + * from the executable. + */ + load_addr = target_mmap(loaddr, hiaddr - loaddr, PROT_NONE, + MAP_PRIVATE | MAP_ANON | MAP_NORESERVE | + (ehdr->e_type == ET_EXEC ? MAP_FIXED : 0), + -1, 0); + if (load_addr == -1) { + goto exit_perror; } load_bias = load_addr - loaddr; @@ -2858,6 +2900,17 @@ int load_elf_binary(struct linux_binprm *bprm, struct image_info *info) bprm->core_dump = &elf_core_dump; #endif + /* + * If we reserved extra space for brk, release it now. + * The implementation of do_brk in syscalls.c expects to be able + * to mmap pages in this space. + */ + if (info->reserve_brk) { + abi_ulong start_brk = HOST_PAGE_ALIGN(info->brk); + abi_ulong end_brk = HOST_PAGE_ALIGN(info->brk + info->reserve_brk); + target_munmap(start_brk, end_brk - start_brk); + } + return 0; } diff --git a/linux-user/exit.c b/linux-user/exit.c index a362ef67d2..1594015444 100644 --- a/linux-user/exit.c +++ b/linux-user/exit.c @@ -18,7 +18,7 @@ */ #include "qemu/osdep.h" #include "qemu.h" -#ifdef TARGET_GPROF +#ifdef CONFIG_GPROF #include #endif @@ -28,7 +28,7 @@ extern void __gcov_dump(void); void preexit_cleanup(CPUArchState *env, int code) { -#ifdef TARGET_GPROF +#ifdef CONFIG_GPROF _mcleanup(); #endif #ifdef CONFIG_GCOV diff --git a/linux-user/fd-trans.c b/linux-user/fd-trans.c index 9b92386abf..c0687c52e6 100644 --- a/linux-user/fd-trans.c +++ b/linux-user/fd-trans.c @@ -514,7 +514,8 @@ static abi_long host_to_target_data_bridge_nlattr(struct nlattr *nlattr, u32[1] = tswap32(u32[1]); /* optmask */ break; default: - gemu_log("Unknown QEMU_IFLA_BR type %d\n", nlattr->nla_type); + qemu_log_mask(LOG_UNIMP, "Unknown QEMU_IFLA_BR type %d\n", + nlattr->nla_type); break; } return 0; @@ -577,7 +578,8 @@ static abi_long host_to_target_slave_data_bridge_nlattr(struct nlattr *nlattr, case QEMU_IFLA_BRPORT_BRIDGE_ID: break; default: - gemu_log("Unknown QEMU_IFLA_BRPORT type %d\n", nlattr->nla_type); + qemu_log_mask(LOG_UNIMP, "Unknown QEMU_IFLA_BRPORT type %d\n", + nlattr->nla_type); break; } return 0; @@ -605,7 +607,8 @@ static abi_long host_to_target_data_tun_nlattr(struct nlattr *nlattr, *u32 = tswap32(*u32); break; default: - gemu_log("Unknown QEMU_IFLA_TUN type %d\n", nlattr->nla_type); + qemu_log_mask(LOG_UNIMP, "Unknown QEMU_IFLA_TUN type %d\n", + nlattr->nla_type); break; } return 0; @@ -652,7 +655,8 @@ static abi_long host_to_target_data_linkinfo_nlattr(struct nlattr *nlattr, NULL, host_to_target_data_tun_nlattr); } else { - gemu_log("Unknown QEMU_IFLA_INFO_KIND %s\n", li_context->name); + qemu_log_mask(LOG_UNIMP, "Unknown QEMU_IFLA_INFO_KIND %s\n", + li_context->name); } break; case QEMU_IFLA_INFO_SLAVE_DATA: @@ -663,12 +667,13 @@ static abi_long host_to_target_data_linkinfo_nlattr(struct nlattr *nlattr, NULL, host_to_target_slave_data_bridge_nlattr); } else { - gemu_log("Unknown QEMU_IFLA_INFO_SLAVE_KIND %s\n", + qemu_log_mask(LOG_UNIMP, "Unknown QEMU_IFLA_INFO_SLAVE_KIND %s\n", li_context->slave_name); } break; default: - gemu_log("Unknown host QEMU_IFLA_INFO type: %d\n", nlattr->nla_type); + qemu_log_mask(LOG_UNIMP, "Unknown host QEMU_IFLA_INFO type: %d\n", + nlattr->nla_type); break; } @@ -690,7 +695,8 @@ static abi_long host_to_target_data_inet_nlattr(struct nlattr *nlattr, } break; default: - gemu_log("Unknown host AF_INET type: %d\n", nlattr->nla_type); + qemu_log_mask(LOG_UNIMP, "Unknown host AF_INET type: %d\n", + nlattr->nla_type); } return 0; } @@ -741,7 +747,8 @@ static abi_long host_to_target_data_inet6_nlattr(struct nlattr *nlattr, } break; default: - gemu_log("Unknown host AF_INET6 type: %d\n", nlattr->nla_type); + qemu_log_mask(LOG_UNIMP, "Unknown host AF_INET6 type: %d\n", + nlattr->nla_type); } return 0; } @@ -759,7 +766,8 @@ static abi_long host_to_target_data_spec_nlattr(struct nlattr *nlattr, NULL, host_to_target_data_inet6_nlattr); default: - gemu_log("Unknown host AF_SPEC type: %d\n", nlattr->nla_type); + qemu_log_mask(LOG_UNIMP, "Unknown host AF_SPEC type: %d\n", + nlattr->nla_type); break; } return 0; @@ -780,7 +788,8 @@ static abi_long host_to_target_data_xdp_nlattr(struct nlattr *nlattr, *u32 = tswap32(*u32); break; default: - gemu_log("Unknown host XDP type: %d\n", nlattr->nla_type); + qemu_log_mask( + LOG_UNIMP, "Unknown host XDP type: %d\n", nlattr->nla_type); break; } return 0; @@ -920,7 +929,8 @@ static abi_long host_to_target_data_link_rtattr(struct rtattr *rtattr) NULL, host_to_target_data_xdp_nlattr); default: - gemu_log("Unknown host QEMU_IFLA type: %d\n", rtattr->rta_type); + qemu_log_mask(LOG_UNIMP, "Unknown host QEMU_IFLA type: %d\n", + rtattr->rta_type); break; } return 0; @@ -954,7 +964,8 @@ static abi_long host_to_target_data_addr_rtattr(struct rtattr *rtattr) ci->tstamp = tswap32(ci->tstamp); break; default: - gemu_log("Unknown host IFA type: %d\n", rtattr->rta_type); + qemu_log_mask( + LOG_UNIMP, "Unknown host IFA type: %d\n", rtattr->rta_type); break; } return 0; @@ -996,7 +1007,8 @@ static abi_long host_to_target_data_route_rtattr(struct rtattr *rtattr) #endif break; default: - gemu_log("Unknown host RTA type: %d\n", rtattr->rta_type); + qemu_log_mask( + LOG_UNIMP, "Unknown host RTA type: %d\n", rtattr->rta_type); break; } return 0; @@ -1111,7 +1123,8 @@ static abi_long target_to_host_data_link_rtattr(struct rtattr *rtattr) { switch (rtattr->rta_type) { default: - gemu_log("Unknown target QEMU_IFLA type: %d\n", rtattr->rta_type); + qemu_log_mask(LOG_UNIMP, "Unknown target QEMU_IFLA type: %d\n", + rtattr->rta_type); break; } return 0; @@ -1125,7 +1138,8 @@ static abi_long target_to_host_data_addr_rtattr(struct rtattr *rtattr) case IFA_ADDRESS: break; default: - gemu_log("Unknown target IFA type: %d\n", rtattr->rta_type); + qemu_log_mask(LOG_UNIMP, "Unknown target IFA type: %d\n", + rtattr->rta_type); break; } return 0; @@ -1147,7 +1161,8 @@ static abi_long target_to_host_data_route_rtattr(struct rtattr *rtattr) *u32 = tswap32(*u32); break; default: - gemu_log("Unknown target RTA type: %d\n", rtattr->rta_type); + qemu_log_mask(LOG_UNIMP, "Unknown target RTA type: %d\n", + rtattr->rta_type); break; } return 0; @@ -1232,8 +1247,8 @@ static abi_long host_to_target_data_audit(struct nlmsghdr *nlh) { switch (nlh->nlmsg_type) { default: - gemu_log("Unknown host audit message type %d\n", - nlh->nlmsg_type); + qemu_log_mask(LOG_UNIMP, "Unknown host audit message type %d\n", + nlh->nlmsg_type); return -TARGET_EINVAL; } return 0; @@ -1253,8 +1268,8 @@ static abi_long target_to_host_data_audit(struct nlmsghdr *nlh) case AUDIT_FIRST_USER_MSG2 ... AUDIT_LAST_USER_MSG2: break; default: - gemu_log("Unknown target audit message type %d\n", - nlh->nlmsg_type); + qemu_log_mask(LOG_UNIMP, "Unknown target audit message type %d\n", + nlh->nlmsg_type); return -TARGET_EINVAL; } diff --git a/linux-user/flatload.c b/linux-user/flatload.c index 0122ab3afe..66901f39cc 100644 --- a/linux-user/flatload.c +++ b/linux-user/flatload.c @@ -37,7 +37,7 @@ #include "qemu.h" #include "flat.h" -#include +#include "target_flat.h" //#define DEBUG diff --git a/linux-user/hppa/Makefile.objs b/linux-user/hppa/Makefile.objs new file mode 100644 index 0000000000..f8368be6f3 --- /dev/null +++ b/linux-user/hppa/Makefile.objs @@ -0,0 +1,5 @@ +generated-files-y += linux-user/hppa/syscall_nr.h + +syshdr := $(SRC_PATH)/linux-user/hppa/syscallhdr.sh +%/syscall_nr.h: $(SRC_PATH)/linux-user/hppa/syscall.tbl $(syshdr) + $(call quiet-command, sh $(syshdr) $< $@ $(TARGET_SYSTBL_ABI),"GEN","$@") diff --git a/linux-user/hppa/syscall.tbl b/linux-user/hppa/syscall.tbl new file mode 100644 index 0000000000..52a15f5cd1 --- /dev/null +++ b/linux-user/hppa/syscall.tbl @@ -0,0 +1,437 @@ +# SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note +# +# system call numbers and entry vectors for parisc +# +# The format is: +# +# +# The can be common, 64, or 32 for this file. +# +0 common restart_syscall sys_restart_syscall +1 common exit sys_exit +2 common fork sys_fork_wrapper +3 common read sys_read +4 common write sys_write +5 common open sys_open compat_sys_open +6 common close sys_close +7 common waitpid sys_waitpid +8 common creat sys_creat +9 common link sys_link +10 common unlink sys_unlink +11 common execve sys_execve compat_sys_execve +12 common chdir sys_chdir +13 32 time sys_time32 +13 64 time sys_time +14 common mknod sys_mknod +15 common chmod sys_chmod +16 common lchown sys_lchown +17 common socket sys_socket +18 common stat sys_newstat compat_sys_newstat +19 common lseek sys_lseek compat_sys_lseek +20 common getpid sys_getpid +21 common mount sys_mount compat_sys_mount +22 common bind sys_bind +23 common setuid sys_setuid +24 common getuid sys_getuid +25 32 stime sys_stime32 +25 64 stime sys_stime +26 common ptrace sys_ptrace compat_sys_ptrace +27 common alarm sys_alarm +28 common fstat sys_newfstat compat_sys_newfstat +29 common pause sys_pause +30 32 utime sys_utime32 +30 64 utime sys_utime +31 common connect sys_connect +32 common listen sys_listen +33 common access sys_access +34 common nice sys_nice +35 common accept sys_accept +36 common sync sys_sync +37 common kill sys_kill +38 common rename sys_rename +39 common mkdir sys_mkdir +40 common rmdir sys_rmdir +41 common dup sys_dup +42 common pipe sys_pipe +43 common times sys_times compat_sys_times +44 common getsockname sys_getsockname +45 common brk sys_brk +46 common setgid sys_setgid +47 common getgid sys_getgid +48 common signal sys_signal +49 common geteuid sys_geteuid +50 common getegid sys_getegid +51 common acct sys_acct +52 common umount2 sys_umount +53 common getpeername sys_getpeername +54 common ioctl sys_ioctl compat_sys_ioctl +55 common fcntl sys_fcntl compat_sys_fcntl +56 common socketpair sys_socketpair +57 common setpgid sys_setpgid +58 common send sys_send +59 common uname sys_newuname +60 common umask sys_umask +61 common chroot sys_chroot +62 common ustat sys_ustat compat_sys_ustat +63 common dup2 sys_dup2 +64 common getppid sys_getppid +65 common getpgrp sys_getpgrp +66 common setsid sys_setsid +67 common pivot_root sys_pivot_root +68 common sgetmask sys_sgetmask sys32_unimplemented +69 common ssetmask sys_ssetmask sys32_unimplemented +70 common setreuid sys_setreuid +71 common setregid sys_setregid +72 common mincore sys_mincore +73 common sigpending sys_sigpending compat_sys_sigpending +74 common sethostname sys_sethostname +75 common setrlimit sys_setrlimit compat_sys_setrlimit +76 common getrlimit sys_getrlimit compat_sys_getrlimit +77 common getrusage sys_getrusage compat_sys_getrusage +78 common gettimeofday sys_gettimeofday compat_sys_gettimeofday +79 common settimeofday sys_settimeofday compat_sys_settimeofday +80 common getgroups sys_getgroups +81 common setgroups sys_setgroups +82 common sendto sys_sendto +83 common symlink sys_symlink +84 common lstat sys_newlstat compat_sys_newlstat +85 common readlink sys_readlink +86 common uselib sys_ni_syscall +87 common swapon sys_swapon +88 common reboot sys_reboot +89 common mmap2 sys_mmap2 +90 common mmap sys_mmap +91 common munmap sys_munmap +92 common truncate sys_truncate compat_sys_truncate +93 common ftruncate sys_ftruncate compat_sys_ftruncate +94 common fchmod sys_fchmod +95 common fchown sys_fchown +96 common getpriority sys_getpriority +97 common setpriority sys_setpriority +98 common recv sys_recv +99 common statfs sys_statfs compat_sys_statfs +100 common fstatfs sys_fstatfs compat_sys_fstatfs +101 common stat64 sys_stat64 +# 102 was socketcall +103 common syslog sys_syslog +104 common setitimer sys_setitimer compat_sys_setitimer +105 common getitimer sys_getitimer compat_sys_getitimer +106 common capget sys_capget +107 common capset sys_capset +108 32 pread64 parisc_pread64 +108 64 pread64 sys_pread64 +109 32 pwrite64 parisc_pwrite64 +109 64 pwrite64 sys_pwrite64 +110 common getcwd sys_getcwd +111 common vhangup sys_vhangup +112 common fstat64 sys_fstat64 +113 common vfork sys_vfork_wrapper +114 common wait4 sys_wait4 compat_sys_wait4 +115 common swapoff sys_swapoff +116 common sysinfo sys_sysinfo compat_sys_sysinfo +117 common shutdown sys_shutdown +118 common fsync sys_fsync +119 common madvise sys_madvise +120 common clone sys_clone_wrapper +121 common setdomainname sys_setdomainname +122 common sendfile sys_sendfile compat_sys_sendfile +123 common recvfrom sys_recvfrom +124 32 adjtimex sys_adjtimex_time32 +124 64 adjtimex sys_adjtimex +125 common mprotect sys_mprotect +126 common sigprocmask sys_sigprocmask compat_sys_sigprocmask +# 127 was create_module +128 common init_module sys_init_module +129 common delete_module sys_delete_module +# 130 was get_kernel_syms +131 common quotactl sys_quotactl +132 common getpgid sys_getpgid +133 common fchdir sys_fchdir +134 common bdflush sys_bdflush +135 common sysfs sys_sysfs +136 32 personality parisc_personality +136 64 personality sys_personality +# 137 was afs_syscall +138 common setfsuid sys_setfsuid +139 common setfsgid sys_setfsgid +140 common _llseek sys_llseek +141 common getdents sys_getdents compat_sys_getdents +142 common _newselect sys_select compat_sys_select +143 common flock sys_flock +144 common msync sys_msync +145 common readv sys_readv compat_sys_readv +146 common writev sys_writev compat_sys_writev +147 common getsid sys_getsid +148 common fdatasync sys_fdatasync +149 common _sysctl sys_sysctl compat_sys_sysctl +150 common mlock sys_mlock +151 common munlock sys_munlock +152 common mlockall sys_mlockall +153 common munlockall sys_munlockall +154 common sched_setparam sys_sched_setparam +155 common sched_getparam sys_sched_getparam +156 common sched_setscheduler sys_sched_setscheduler +157 common sched_getscheduler sys_sched_getscheduler +158 common sched_yield sys_sched_yield +159 common sched_get_priority_max sys_sched_get_priority_max +160 common sched_get_priority_min sys_sched_get_priority_min +161 32 sched_rr_get_interval sys_sched_rr_get_interval_time32 +161 64 sched_rr_get_interval sys_sched_rr_get_interval +162 32 nanosleep sys_nanosleep_time32 +162 64 nanosleep sys_nanosleep +163 common mremap sys_mremap +164 common setresuid sys_setresuid +165 common getresuid sys_getresuid +166 common sigaltstack sys_sigaltstack compat_sys_sigaltstack +# 167 was query_module +168 common poll sys_poll +# 169 was nfsservctl +170 common setresgid sys_setresgid +171 common getresgid sys_getresgid +172 common prctl sys_prctl +173 common rt_sigreturn sys_rt_sigreturn_wrapper +174 common rt_sigaction sys_rt_sigaction compat_sys_rt_sigaction +175 common rt_sigprocmask sys_rt_sigprocmask compat_sys_rt_sigprocmask +176 common rt_sigpending sys_rt_sigpending compat_sys_rt_sigpending +177 32 rt_sigtimedwait sys_rt_sigtimedwait_time32 compat_sys_rt_sigtimedwait_time32 +177 64 rt_sigtimedwait sys_rt_sigtimedwait +178 common rt_sigqueueinfo sys_rt_sigqueueinfo compat_sys_rt_sigqueueinfo +179 common rt_sigsuspend sys_rt_sigsuspend compat_sys_rt_sigsuspend +180 common chown sys_chown +181 common setsockopt sys_setsockopt compat_sys_setsockopt +182 common getsockopt sys_getsockopt compat_sys_getsockopt +183 common sendmsg sys_sendmsg compat_sys_sendmsg +184 common recvmsg sys_recvmsg compat_sys_recvmsg +185 common semop sys_semop +186 common semget sys_semget +187 common semctl sys_semctl compat_sys_semctl +188 common msgsnd sys_msgsnd compat_sys_msgsnd +189 common msgrcv sys_msgrcv compat_sys_msgrcv +190 common msgget sys_msgget +191 common msgctl sys_msgctl compat_sys_msgctl +192 common shmat sys_shmat compat_sys_shmat +193 common shmdt sys_shmdt +194 common shmget sys_shmget +195 common shmctl sys_shmctl compat_sys_shmctl +# 196 was getpmsg +# 197 was putpmsg +198 common lstat64 sys_lstat64 +199 32 truncate64 parisc_truncate64 +199 64 truncate64 sys_truncate64 +200 32 ftruncate64 parisc_ftruncate64 +200 64 ftruncate64 sys_ftruncate64 +201 common getdents64 sys_getdents64 +202 common fcntl64 sys_fcntl64 compat_sys_fcntl64 +# 203 was attrctl +# 204 was acl_get +# 205 was acl_set +206 common gettid sys_gettid +207 32 readahead parisc_readahead +207 64 readahead sys_readahead +208 common tkill sys_tkill +209 common sendfile64 sys_sendfile64 compat_sys_sendfile64 +210 32 futex sys_futex_time32 +210 64 futex sys_futex +211 common sched_setaffinity sys_sched_setaffinity compat_sys_sched_setaffinity +212 common sched_getaffinity sys_sched_getaffinity compat_sys_sched_getaffinity +# 213 was set_thread_area +# 214 was get_thread_area +215 common io_setup sys_io_setup compat_sys_io_setup +216 common io_destroy sys_io_destroy +217 32 io_getevents sys_io_getevents_time32 +217 64 io_getevents sys_io_getevents +218 common io_submit sys_io_submit compat_sys_io_submit +219 common io_cancel sys_io_cancel +# 220 was alloc_hugepages +# 221 was free_hugepages +222 common exit_group sys_exit_group +223 common lookup_dcookie sys_lookup_dcookie compat_sys_lookup_dcookie +224 common epoll_create sys_epoll_create +225 common epoll_ctl sys_epoll_ctl +226 common epoll_wait sys_epoll_wait +227 common remap_file_pages sys_remap_file_pages +228 32 semtimedop sys_semtimedop_time32 +228 64 semtimedop sys_semtimedop +229 common mq_open sys_mq_open compat_sys_mq_open +230 common mq_unlink sys_mq_unlink +231 32 mq_timedsend sys_mq_timedsend_time32 +231 64 mq_timedsend sys_mq_timedsend +232 32 mq_timedreceive sys_mq_timedreceive_time32 +232 64 mq_timedreceive sys_mq_timedreceive +233 common mq_notify sys_mq_notify compat_sys_mq_notify +234 common mq_getsetattr sys_mq_getsetattr compat_sys_mq_getsetattr +235 common waitid sys_waitid compat_sys_waitid +236 32 fadvise64_64 parisc_fadvise64_64 +236 64 fadvise64_64 sys_fadvise64_64 +237 common set_tid_address sys_set_tid_address +238 common setxattr sys_setxattr +239 common lsetxattr sys_lsetxattr +240 common fsetxattr sys_fsetxattr +241 common getxattr sys_getxattr +242 common lgetxattr sys_lgetxattr +243 common fgetxattr sys_fgetxattr +244 common listxattr sys_listxattr +245 common llistxattr sys_llistxattr +246 common flistxattr sys_flistxattr +247 common removexattr sys_removexattr +248 common lremovexattr sys_lremovexattr +249 common fremovexattr sys_fremovexattr +250 common timer_create sys_timer_create compat_sys_timer_create +251 32 timer_settime sys_timer_settime32 +251 64 timer_settime sys_timer_settime +252 32 timer_gettime sys_timer_gettime32 +252 64 timer_gettime sys_timer_gettime +253 common timer_getoverrun sys_timer_getoverrun +254 common timer_delete sys_timer_delete +255 32 clock_settime sys_clock_settime32 +255 64 clock_settime sys_clock_settime +256 32 clock_gettime sys_clock_gettime32 +256 64 clock_gettime sys_clock_gettime +257 32 clock_getres sys_clock_getres_time32 +257 64 clock_getres sys_clock_getres +258 32 clock_nanosleep sys_clock_nanosleep_time32 +258 64 clock_nanosleep sys_clock_nanosleep +259 common tgkill sys_tgkill +260 common mbind sys_mbind compat_sys_mbind +261 common get_mempolicy sys_get_mempolicy compat_sys_get_mempolicy +262 common set_mempolicy sys_set_mempolicy compat_sys_set_mempolicy +# 263 was vserver +264 common add_key sys_add_key +265 common request_key sys_request_key +266 common keyctl sys_keyctl compat_sys_keyctl +267 common ioprio_set sys_ioprio_set +268 common ioprio_get sys_ioprio_get +269 common inotify_init sys_inotify_init +270 common inotify_add_watch sys_inotify_add_watch +271 common inotify_rm_watch sys_inotify_rm_watch +272 common migrate_pages sys_migrate_pages +273 32 pselect6 sys_pselect6_time32 compat_sys_pselect6_time32 +273 64 pselect6 sys_pselect6 +274 32 ppoll sys_ppoll_time32 compat_sys_ppoll_time32 +274 64 ppoll sys_ppoll +275 common openat sys_openat compat_sys_openat +276 common mkdirat sys_mkdirat +277 common mknodat sys_mknodat +278 common fchownat sys_fchownat +279 32 futimesat sys_futimesat_time32 +279 64 futimesat sys_futimesat +280 common fstatat64 sys_fstatat64 +281 common unlinkat sys_unlinkat +282 common renameat sys_renameat +283 common linkat sys_linkat +284 common symlinkat sys_symlinkat +285 common readlinkat sys_readlinkat +286 common fchmodat sys_fchmodat +287 common faccessat sys_faccessat +288 common unshare sys_unshare +289 common set_robust_list sys_set_robust_list compat_sys_set_robust_list +290 common get_robust_list sys_get_robust_list compat_sys_get_robust_list +291 common splice sys_splice +292 32 sync_file_range parisc_sync_file_range +292 64 sync_file_range sys_sync_file_range +293 common tee sys_tee +294 common vmsplice sys_vmsplice compat_sys_vmsplice +295 common move_pages sys_move_pages compat_sys_move_pages +296 common getcpu sys_getcpu +297 common epoll_pwait sys_epoll_pwait compat_sys_epoll_pwait +298 common statfs64 sys_statfs64 compat_sys_statfs64 +299 common fstatfs64 sys_fstatfs64 compat_sys_fstatfs64 +300 common kexec_load sys_kexec_load compat_sys_kexec_load +301 32 utimensat sys_utimensat_time32 +301 64 utimensat sys_utimensat +302 common signalfd sys_signalfd compat_sys_signalfd +# 303 was timerfd +304 common eventfd sys_eventfd +305 32 fallocate parisc_fallocate +305 64 fallocate sys_fallocate +306 common timerfd_create sys_timerfd_create +307 32 timerfd_settime sys_timerfd_settime32 +307 64 timerfd_settime sys_timerfd_settime +308 32 timerfd_gettime sys_timerfd_gettime32 +308 64 timerfd_gettime sys_timerfd_gettime +309 common signalfd4 sys_signalfd4 compat_sys_signalfd4 +310 common eventfd2 sys_eventfd2 +311 common epoll_create1 sys_epoll_create1 +312 common dup3 sys_dup3 +313 common pipe2 sys_pipe2 +314 common inotify_init1 sys_inotify_init1 +315 common preadv sys_preadv compat_sys_preadv +316 common pwritev sys_pwritev compat_sys_pwritev +317 common rt_tgsigqueueinfo sys_rt_tgsigqueueinfo compat_sys_rt_tgsigqueueinfo +318 common perf_event_open sys_perf_event_open +319 32 recvmmsg sys_recvmmsg_time32 compat_sys_recvmmsg_time32 +319 64 recvmmsg sys_recvmmsg +320 common accept4 sys_accept4 +321 common prlimit64 sys_prlimit64 +322 common fanotify_init sys_fanotify_init +323 common fanotify_mark sys_fanotify_mark sys32_fanotify_mark +324 32 clock_adjtime sys_clock_adjtime32 +324 64 clock_adjtime sys_clock_adjtime +325 common name_to_handle_at sys_name_to_handle_at +326 common open_by_handle_at sys_open_by_handle_at compat_sys_open_by_handle_at +327 common syncfs sys_syncfs +328 common setns sys_setns +329 common sendmmsg sys_sendmmsg compat_sys_sendmmsg +330 common process_vm_readv sys_process_vm_readv compat_sys_process_vm_readv +331 common process_vm_writev sys_process_vm_writev compat_sys_process_vm_writev +332 common kcmp sys_kcmp +333 common finit_module sys_finit_module +334 common sched_setattr sys_sched_setattr +335 common sched_getattr sys_sched_getattr +336 32 utimes sys_utimes_time32 +336 64 utimes sys_utimes +337 common renameat2 sys_renameat2 +338 common seccomp sys_seccomp +339 common getrandom sys_getrandom +340 common memfd_create sys_memfd_create +341 common bpf sys_bpf +342 common execveat sys_execveat compat_sys_execveat +343 common membarrier sys_membarrier +344 common userfaultfd sys_userfaultfd +345 common mlock2 sys_mlock2 +346 common copy_file_range sys_copy_file_range +347 common preadv2 sys_preadv2 compat_sys_preadv2 +348 common pwritev2 sys_pwritev2 compat_sys_pwritev2 +349 common statx sys_statx +350 32 io_pgetevents sys_io_pgetevents_time32 compat_sys_io_pgetevents +350 64 io_pgetevents sys_io_pgetevents +351 common pkey_mprotect sys_pkey_mprotect +352 common pkey_alloc sys_pkey_alloc +353 common pkey_free sys_pkey_free +354 common rseq sys_rseq +355 common kexec_file_load sys_kexec_file_load sys_kexec_file_load +# up to 402 is unassigned and reserved for arch specific syscalls +403 32 clock_gettime64 sys_clock_gettime sys_clock_gettime +404 32 clock_settime64 sys_clock_settime sys_clock_settime +405 32 clock_adjtime64 sys_clock_adjtime sys_clock_adjtime +406 32 clock_getres_time64 sys_clock_getres sys_clock_getres +407 32 clock_nanosleep_time64 sys_clock_nanosleep sys_clock_nanosleep +408 32 timer_gettime64 sys_timer_gettime sys_timer_gettime +409 32 timer_settime64 sys_timer_settime sys_timer_settime +410 32 timerfd_gettime64 sys_timerfd_gettime sys_timerfd_gettime +411 32 timerfd_settime64 sys_timerfd_settime sys_timerfd_settime +412 32 utimensat_time64 sys_utimensat sys_utimensat +413 32 pselect6_time64 sys_pselect6 compat_sys_pselect6_time64 +414 32 ppoll_time64 sys_ppoll compat_sys_ppoll_time64 +416 32 io_pgetevents_time64 sys_io_pgetevents sys_io_pgetevents +417 32 recvmmsg_time64 sys_recvmmsg compat_sys_recvmmsg_time64 +418 32 mq_timedsend_time64 sys_mq_timedsend sys_mq_timedsend +419 32 mq_timedreceive_time64 sys_mq_timedreceive sys_mq_timedreceive +420 32 semtimedop_time64 sys_semtimedop sys_semtimedop +421 32 rt_sigtimedwait_time64 sys_rt_sigtimedwait compat_sys_rt_sigtimedwait_time64 +422 32 futex_time64 sys_futex sys_futex +423 32 sched_rr_get_interval_time64 sys_sched_rr_get_interval sys_sched_rr_get_interval +424 common pidfd_send_signal sys_pidfd_send_signal +425 common io_uring_setup sys_io_uring_setup +426 common io_uring_enter sys_io_uring_enter +427 common io_uring_register sys_io_uring_register +428 common open_tree sys_open_tree +429 common move_mount sys_move_mount +430 common fsopen sys_fsopen +431 common fsconfig sys_fsconfig +432 common fsmount sys_fsmount +433 common fspick sys_fspick +434 common pidfd_open sys_pidfd_open +435 common clone3 sys_clone3_wrapper +437 common openat2 sys_openat2 +438 common pidfd_getfd sys_pidfd_getfd diff --git a/linux-user/hppa/syscall_nr.h b/linux-user/hppa/syscall_nr.h deleted file mode 100644 index ae41e94321..0000000000 --- a/linux-user/hppa/syscall_nr.h +++ /dev/null @@ -1,358 +0,0 @@ -/* - * This file contains the system call numbers. - */ - -#ifndef LINUX_USER_HPPA_SYSCALL_NR_H -#define LINUX_USER_HPPA_SYSCALL_NR_H - -#define TARGET_NR_restart_syscall 0 -#define TARGET_NR_exit 1 -#define TARGET_NR_fork 2 -#define TARGET_NR_read 3 -#define TARGET_NR_write 4 -#define TARGET_NR_open 5 -#define TARGET_NR_close 6 -#define TARGET_NR_waitpid 7 -#define TARGET_NR_creat 8 -#define TARGET_NR_link 9 -#define TARGET_NR_unlink 10 -#define TARGET_NR_execve 11 -#define TARGET_NR_chdir 12 -#define TARGET_NR_time 13 -#define TARGET_NR_mknod 14 -#define TARGET_NR_chmod 15 -#define TARGET_NR_lchown 16 -#define TARGET_NR_socket 17 -#define TARGET_NR_stat 18 -#define TARGET_NR_lseek 19 -#define TARGET_NR_getpid 20 -#define TARGET_NR_mount 21 -#define TARGET_NR_bind 22 -#define TARGET_NR_setuid 23 -#define TARGET_NR_getuid 24 -#define TARGET_NR_stime 25 -#define TARGET_NR_ptrace 26 -#define TARGET_NR_alarm 27 -#define TARGET_NR_fstat 28 -#define TARGET_NR_pause 29 -#define TARGET_NR_utime 30 -#define TARGET_NR_connect 31 -#define TARGET_NR_listen 32 -#define TARGET_NR_access 33 -#define TARGET_NR_nice 34 -#define TARGET_NR_accept 35 -#define TARGET_NR_sync 36 -#define TARGET_NR_kill 37 -#define TARGET_NR_rename 38 -#define TARGET_NR_mkdir 39 -#define TARGET_NR_rmdir 40 -#define TARGET_NR_dup 41 -#define TARGET_NR_pipe 42 -#define TARGET_NR_times 43 -#define TARGET_NR_getsockname 44 -#define TARGET_NR_brk 45 -#define TARGET_NR_setgid 46 -#define TARGET_NR_getgid 47 -#define TARGET_NR_signal 48 -#define TARGET_NR_geteuid 49 -#define TARGET_NR_getegid 50 -#define TARGET_NR_acct 51 -#define TARGET_NR_umount2 52 -#define TARGET_NR_getpeername 53 -#define TARGET_NR_ioctl 54 -#define TARGET_NR_fcntl 55 -#define TARGET_NR_socketpair 56 -#define TARGET_NR_setpgid 57 -#define TARGET_NR_send 58 -#define TARGET_NR_uname 59 -#define TARGET_NR_umask 60 -#define TARGET_NR_chroot 61 -#define TARGET_NR_ustat 62 -#define TARGET_NR_dup2 63 -#define TARGET_NR_getppid 64 -#define TARGET_NR_getpgrp 65 -#define TARGET_NR_setsid 66 -#define TARGET_NR_pivot_root 67 -#define TARGET_NR_sgetmask 68 -#define TARGET_NR_ssetmask 69 -#define TARGET_NR_setreuid 70 -#define TARGET_NR_setregid 71 -#define TARGET_NR_mincore 72 -#define TARGET_NR_sigpending 73 -#define TARGET_NR_sethostname 74 -#define TARGET_NR_setrlimit 75 -#define TARGET_NR_getrlimit 76 -#define TARGET_NR_getrusage 77 -#define TARGET_NR_gettimeofday 78 -#define TARGET_NR_settimeofday 79 -#define TARGET_NR_getgroups 80 -#define TARGET_NR_setgroups 81 -#define TARGET_NR_sendto 82 -#define TARGET_NR_symlink 83 -#define TARGET_NR_lstat 84 -#define TARGET_NR_readlink 85 -#define TARGET_NR_uselib 86 -#define TARGET_NR_swapon 87 -#define TARGET_NR_reboot 88 -#define TARGET_NR_mmap2 89 -#define TARGET_NR_mmap 90 -#define TARGET_NR_munmap 91 -#define TARGET_NR_truncate 92 -#define TARGET_NR_ftruncate 93 -#define TARGET_NR_fchmod 94 -#define TARGET_NR_fchown 95 -#define TARGET_NR_getpriority 96 -#define TARGET_NR_setpriority 97 -#define TARGET_NR_recv 98 -#define TARGET_NR_statfs 99 -#define TARGET_NR_fstatfs 100 -#define TARGET_NR_stat64 101 -#define TARGET_NR_socketcall 102 -#define TARGET_NR_syslog 103 -#define TARGET_NR_setitimer 104 -#define TARGET_NR_getitimer 105 -#define TARGET_NR_capget 106 -#define TARGET_NR_capset 107 -#define TARGET_NR_pread64 108 -#define TARGET_NR_pwrite64 109 -#define TARGET_NR_getcwd 110 -#define TARGET_NR_vhangup 111 -#define TARGET_NR_fstat64 112 -#define TARGET_NR_vfork 113 -#define TARGET_NR_wait4 114 -#define TARGET_NR_swapoff 115 -#define TARGET_NR_sysinfo 116 -#define TARGET_NR_shutdown 117 -#define TARGET_NR_fsync 118 -#define TARGET_NR_madvise 119 -#define TARGET_NR_clone 120 -#define TARGET_NR_setdomainname 121 -#define TARGET_NR_sendfile 122 -#define TARGET_NR_recvfrom 123 -#define TARGET_NR_adjtimex 124 -#define TARGET_NR_mprotect 125 -#define TARGET_NR_sigprocmask 126 -#define TARGET_NR_create_module 127 -#define TARGET_NR_init_module 128 -#define TARGET_NR_delete_module 129 -#define TARGET_NR_get_kernel_syms 130 -#define TARGET_NR_quotactl 131 -#define TARGET_NR_getpgid 132 -#define TARGET_NR_fchdir 133 -#define TARGET_NR_bdflush 134 -#define TARGET_NR_sysfs 135 -#define TARGET_NR_personality 136 -#define TARGET_NR_afs_syscall 137 /* Syscall for Andrew File System */ -#define TARGET_NR_setfsuid 138 -#define TARGET_NR_setfsgid 139 -#define TARGET_NR__llseek 140 -#define TARGET_NR_getdents 141 -#define TARGET_NR__newselect 142 -#define TARGET_NR_flock 143 -#define TARGET_NR_msync 144 -#define TARGET_NR_readv 145 -#define TARGET_NR_writev 146 -#define TARGET_NR_getsid 147 -#define TARGET_NR_fdatasync 148 -#define TARGET_NR__sysctl 149 -#define TARGET_NR_mlock 150 -#define TARGET_NR_munlock 151 -#define TARGET_NR_mlockall 152 -#define TARGET_NR_munlockall 153 -#define TARGET_NR_sched_setparam 154 -#define TARGET_NR_sched_getparam 155 -#define TARGET_NR_sched_setscheduler 156 -#define TARGET_NR_sched_getscheduler 157 -#define TARGET_NR_sched_yield 158 -#define TARGET_NR_sched_get_priority_max 159 -#define TARGET_NR_sched_get_priority_min 160 -#define TARGET_NR_sched_rr_get_interval 161 -#define TARGET_NR_nanosleep 162 -#define TARGET_NR_mremap 163 -#define TARGET_NR_setresuid 164 -#define TARGET_NR_getresuid 165 -#define TARGET_NR_sigaltstack 166 -#define TARGET_NR_query_module 167 -#define TARGET_NR_poll 168 -#define TARGET_NR_nfsservctl 169 -#define TARGET_NR_setresgid 170 -#define TARGET_NR_getresgid 171 -#define TARGET_NR_prctl 172 -#define TARGET_NR_rt_sigreturn 173 -#define TARGET_NR_rt_sigaction 174 -#define TARGET_NR_rt_sigprocmask 175 -#define TARGET_NR_rt_sigpending 176 -#define TARGET_NR_rt_sigtimedwait 177 -#define TARGET_NR_rt_sigqueueinfo 178 -#define TARGET_NR_rt_sigsuspend 179 -#define TARGET_NR_chown 180 -#define TARGET_NR_setsockopt 181 -#define TARGET_NR_getsockopt 182 -#define TARGET_NR_sendmsg 183 -#define TARGET_NR_recvmsg 184 -#define TARGET_NR_semop 185 -#define TARGET_NR_semget 186 -#define TARGET_NR_semctl 187 -#define TARGET_NR_msgsnd 188 -#define TARGET_NR_msgrcv 189 -#define TARGET_NR_msgget 190 -#define TARGET_NR_msgctl 191 -#define TARGET_NR_shmat 192 -#define TARGET_NR_shmdt 193 -#define TARGET_NR_shmget 194 -#define TARGET_NR_shmctl 195 -#define TARGET_NR_getpmsg 196 -#define TARGET_NR_putpmsg 197 -#define TARGET_NR_lstat64 198 -#define TARGET_NR_truncate64 199 -#define TARGET_NR_ftruncate64 200 -#define TARGET_NR_getdents64 201 -#define TARGET_NR_fcntl64 202 -#define TARGET_NR_attrctl 203 -#define TARGET_NR_acl_get 204 -#define TARGET_NR_acl_set 205 -#define TARGET_NR_gettid 206 -#define TARGET_NR_readahead 207 -#define TARGET_NR_tkill 208 -#define TARGET_NR_sendfile64 209 -#define TARGET_NR_futex 210 -#define TARGET_NR_sched_setaffinity 211 -#define TARGET_NR_sched_getaffinity 212 -#define TARGET_NR_set_thread_area 213 -#define TARGET_NR_get_thread_area 214 -#define TARGET_NR_io_setup 215 -#define TARGET_NR_io_destroy 216 -#define TARGET_NR_io_getevents 217 -#define TARGET_NR_io_submit 218 -#define TARGET_NR_io_cancel 219 -#define TARGET_NR_alloc_hugepages 220 -#define TARGET_NR_free_hugepages 221 -#define TARGET_NR_exit_group 222 -#define TARGET_NR_lookup_dcookie 223 -#define TARGET_NR_epoll_create 224 -#define TARGET_NR_epoll_ctl 225 -#define TARGET_NR_epoll_wait 226 -#define TARGET_NR_remap_file_pages 227 -#define TARGET_NR_semtimedop 228 -#define TARGET_NR_mq_open 229 -#define TARGET_NR_mq_unlink 230 -#define TARGET_NR_mq_timedsend 231 -#define TARGET_NR_mq_timedreceive 232 -#define TARGET_NR_mq_notify 233 -#define TARGET_NR_mq_getsetattr 234 -#define TARGET_NR_waitid 235 -#define TARGET_NR_fadvise64_64 236 -#define TARGET_NR_set_tid_address 237 -#define TARGET_NR_setxattr 238 -#define TARGET_NR_lsetxattr 239 -#define TARGET_NR_fsetxattr 240 -#define TARGET_NR_getxattr 241 -#define TARGET_NR_lgetxattr 242 -#define TARGET_NR_fgetxattr 243 -#define TARGET_NR_listxattr 244 -#define TARGET_NR_llistxattr 245 -#define TARGET_NR_flistxattr 246 -#define TARGET_NR_removexattr 247 -#define TARGET_NR_lremovexattr 248 -#define TARGET_NR_fremovexattr 249 -#define TARGET_NR_timer_create 250 -#define TARGET_NR_timer_settime 251 -#define TARGET_NR_timer_gettime 252 -#define TARGET_NR_timer_getoverrun 253 -#define TARGET_NR_timer_delete 254 -#define TARGET_NR_clock_settime 255 -#define TARGET_NR_clock_gettime 256 -#define TARGET_NR_clock_getres 257 -#define TARGET_NR_clock_nanosleep 258 -#define TARGET_NR_tgkill 259 -#define TARGET_NR_mbind 260 -#define TARGET_NR_get_mempolicy 261 -#define TARGET_NR_set_mempolicy 262 -#define TARGET_NR_vserver 263 -#define TARGET_NR_add_key 264 -#define TARGET_NR_request_key 265 -#define TARGET_NR_keyctl 266 -#define TARGET_NR_ioprio_set 267 -#define TARGET_NR_ioprio_get 268 -#define TARGET_NR_inotify_init 269 -#define TARGET_NR_inotify_add_watch 270 -#define TARGET_NR_inotify_rm_watch 271 -#define TARGET_NR_migrate_pages 272 -#define TARGET_NR_pselect6 273 -#define TARGET_NR_ppoll 274 -#define TARGET_NR_openat 275 -#define TARGET_NR_mkdirat 276 -#define TARGET_NR_mknodat 277 -#define TARGET_NR_fchownat 278 -#define TARGET_NR_futimesat 279 -#define TARGET_NR_fstatat64 280 -#define TARGET_NR_unlinkat 281 -#define TARGET_NR_renameat 282 -#define TARGET_NR_linkat 283 -#define TARGET_NR_symlinkat 284 -#define TARGET_NR_readlinkat 285 -#define TARGET_NR_fchmodat 286 -#define TARGET_NR_faccessat 287 -#define TARGET_NR_unshare 288 -#define TARGET_NR_set_robust_list 289 -#define TARGET_NR_get_robust_list 290 -#define TARGET_NR_splice 291 -#define TARGET_NR_sync_file_range 292 -#define TARGET_NR_tee 293 -#define TARGET_NR_vmsplice 294 -#define TARGET_NR_move_pages 295 -#define TARGET_NR_getcpu 296 -#define TARGET_NR_epoll_pwait 297 -#define TARGET_NR_statfs64 298 -#define TARGET_NR_fstatfs64 299 -#define TARGET_NR_kexec_load 300 -#define TARGET_NR_utimensat 301 -#define TARGET_NR_signalfd 302 -#define TARGET_NR_timerfd 303 -#define TARGET_NR_eventfd 304 -#define TARGET_NR_fallocate 305 -#define TARGET_NR_timerfd_create 306 -#define TARGET_NR_timerfd_settime 307 -#define TARGET_NR_timerfd_gettime 308 -#define TARGET_NR_signalfd4 309 -#define TARGET_NR_eventfd2 310 -#define TARGET_NR_epoll_create1 311 -#define TARGET_NR_dup3 312 -#define TARGET_NR_pipe2 313 -#define TARGET_NR_inotify_init1 314 -#define TARGET_NR_preadv 315 -#define TARGET_NR_pwritev 316 -#define TARGET_NR_rt_tgsigqueueinfo 317 -#define TARGET_NR_perf_event_open 318 -#define TARGET_NR_recvmmsg 319 -#define TARGET_NR_accept4 320 -#define TARGET_NR_prlimit64 321 -#define TARGET_NR_fanotify_init 322 -#define TARGET_NR_fanotify_mark 323 -#define TARGET_NR_clock_adjtime 324 -#define TARGET_NR_name_to_handle_at 325 -#define TARGET_NR_open_by_handle_at 326 -#define TARGET_NR_syncfs 327 -#define TARGET_NR_setns 328 -#define TARGET_NR_sendmmsg 329 -#define TARGET_NR_process_vm_readv 330 -#define TARGET_NR_process_vm_writev 331 -#define TARGET_NR_kcmp 332 -#define TARGET_NR_finit_module 333 -#define TARGET_NR_sched_setattr 334 -#define TARGET_NR_sched_getattr 335 -#define TARGET_NR_utimes 336 -#define TARGET_NR_renameat2 337 -#define TARGET_NR_seccomp 338 -#define TARGET_NR_getrandom 339 -#define TARGET_NR_memfd_create 340 -#define TARGET_NR_bpf 341 -#define TARGET_NR_execveat 342 -#define TARGET_NR_membarrier 343 -#define TARGET_NR_userfaultfd 344 -#define TARGET_NR_mlock2 345 -#define TARGET_NR_copy_file_range 346 -#define TARGET_NR_preadv2 347 -#define TARGET_NR_pwritev2 348 - -#endif diff --git a/linux-user/hppa/syscallhdr.sh b/linux-user/hppa/syscallhdr.sh new file mode 100644 index 0000000000..ac91a95762 --- /dev/null +++ b/linux-user/hppa/syscallhdr.sh @@ -0,0 +1,32 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 + +in="$1" +out="$2" +my_abis=`echo "($3)" | tr ',' '|'` +prefix="$4" +offset="$5" + +fileguard=LINUX_USER_HPPA_`basename "$out" | sed \ + -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/' \ + -e 's/[^A-Z0-9_]/_/g' -e 's/__/_/g'` +grep -E "^[0-9A-Fa-fXx]+[[:space:]]+${my_abis}" "$in" | sort -n | ( + printf "#ifndef %s\n" "${fileguard}" + printf "#define %s\n" "${fileguard}" + printf "\n" + + nxt=0 + while read nr abi name entry compat ; do + if [ -z "$offset" ]; then + printf "#define TARGET_NR_%s%s\t%s\n" \ + "${prefix}" "${name}" "${nr}" + else + printf "#define TARGET_NR_%s%s\t(%s + %s)\n" \ + "${prefix}" "${name}" "${offset}" "${nr}" + fi + nxt=$((nr+1)) + done + + printf "\n" + printf "#endif /* %s */" "${fileguard}" +) > "$out" diff --git a/linux-user/hppa/target_signal.h b/linux-user/hppa/target_signal.h index ba159ff8d0..c2a0102ed7 100644 --- a/linux-user/hppa/target_signal.h +++ b/linux-user/hppa/target_signal.h @@ -34,6 +34,7 @@ #define TARGET_SIGURG 29 #define TARGET_SIGXFSZ 30 #define TARGET_SIGSYS 31 +#define TARGET_SIGRTMIN 32 #define TARGET_SIG_BLOCK 0 #define TARGET_SIG_UNBLOCK 1 diff --git a/linux-user/i386/Makefile.objs b/linux-user/i386/Makefile.objs new file mode 100644 index 0000000000..c25cf17bfb --- /dev/null +++ b/linux-user/i386/Makefile.objs @@ -0,0 +1,5 @@ +generated-files-y += linux-user/i386/syscall_nr.h + +syshdr := $(SRC_PATH)/linux-user/i386/syscallhdr.sh +%/syscall_nr.h: $(SRC_PATH)/linux-user/i386/syscall_32.tbl $(syshdr) + $(call quiet-command, sh $(syshdr) $< $@ $(TARGET_SYSTBL_ABI),"GEN","$@") diff --git a/linux-user/i386/cpu_loop.c b/linux-user/i386/cpu_loop.c index 024b6f4d58..70cde417e6 100644 --- a/linux-user/i386/cpu_loop.c +++ b/linux-user/i386/cpu_loop.c @@ -81,13 +81,126 @@ static void set_idt(int n, unsigned int dpl) } #endif +static void gen_signal(CPUX86State *env, int sig, int code, abi_ptr addr) +{ + target_siginfo_t info = { + .si_signo = sig, + .si_code = code, + ._sifields._sigfault._addr = addr + }; + + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); +} + +#ifdef TARGET_X86_64 +static bool write_ok_or_segv(CPUX86State *env, abi_ptr addr, size_t len) +{ + /* + * For all the vsyscalls, NULL means "don't write anything" not + * "write it at address 0". + */ + if (addr == 0 || access_ok(VERIFY_WRITE, addr, len)) { + return true; + } + + env->error_code = PG_ERROR_W_MASK | PG_ERROR_U_MASK; + gen_signal(env, TARGET_SIGSEGV, TARGET_SEGV_MAPERR, addr); + return false; +} + +/* + * Since v3.1, the kernel traps and emulates the vsyscall page. + * Entry points other than the official generate SIGSEGV. + */ +static void emulate_vsyscall(CPUX86State *env) +{ + int syscall; + abi_ulong ret; + uint64_t caller; + + /* + * Validate the entry point. We have already validated the page + * during translation to get here; now verify the offset. + */ + switch (env->eip & ~TARGET_PAGE_MASK) { + case 0x000: + syscall = TARGET_NR_gettimeofday; + break; + case 0x400: + syscall = TARGET_NR_time; + break; + case 0x800: + syscall = TARGET_NR_getcpu; + break; + default: + goto sigsegv; + } + + /* + * Validate the return address. + * Note that the kernel treats this the same as an invalid entry point. + */ + if (get_user_u64(caller, env->regs[R_ESP])) { + goto sigsegv; + } + + /* + * Validate the the pointer arguments. + */ + switch (syscall) { + case TARGET_NR_gettimeofday: + if (!write_ok_or_segv(env, env->regs[R_EDI], + sizeof(struct target_timeval)) || + !write_ok_or_segv(env, env->regs[R_ESI], + sizeof(struct target_timezone))) { + return; + } + break; + case TARGET_NR_time: + if (!write_ok_or_segv(env, env->regs[R_EDI], sizeof(abi_long))) { + return; + } + break; + case TARGET_NR_getcpu: + if (!write_ok_or_segv(env, env->regs[R_EDI], sizeof(uint32_t)) || + !write_ok_or_segv(env, env->regs[R_ESI], sizeof(uint32_t))) { + return; + } + break; + default: + g_assert_not_reached(); + } + + /* + * Perform the syscall. None of the vsyscalls should need restarting. + */ + ret = do_syscall(env, syscall, env->regs[R_EDI], env->regs[R_ESI], + env->regs[R_EDX], env->regs[10], env->regs[8], + env->regs[9], 0, 0); + g_assert(ret != -TARGET_ERESTARTSYS); + g_assert(ret != -TARGET_QEMU_ESIGRETURN); + if (ret == -TARGET_EFAULT) { + goto sigsegv; + } + env->regs[R_EAX] = ret; + + /* Emulate a ret instruction to leave the vsyscall page. */ + env->eip = caller; + env->regs[R_ESP] += 8; + return; + + sigsegv: + /* Like force_sig(SIGSEGV). */ + gen_signal(env, TARGET_SIGSEGV, TARGET_SI_KERNEL, 0); +} +#endif + void cpu_loop(CPUX86State *env) { CPUState *cs = env_cpu(env); int trapnr; abi_ulong pc; abi_ulong ret; - target_siginfo_t info; for(;;) { cpu_exec_start(cs); @@ -131,73 +244,53 @@ void cpu_loop(CPUX86State *env) env->regs[R_EAX] = ret; } break; +#endif +#ifdef TARGET_X86_64 + case EXCP_VSYSCALL: + emulate_vsyscall(env); + break; #endif case EXCP0B_NOSEG: case EXCP0C_STACK: - info.si_signo = TARGET_SIGBUS; - info.si_errno = 0; - info.si_code = TARGET_SI_KERNEL; - info._sifields._sigfault._addr = 0; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + gen_signal(env, TARGET_SIGBUS, TARGET_SI_KERNEL, 0); break; case EXCP0D_GPF: /* XXX: potential problem if ABI32 */ #ifndef TARGET_X86_64 if (env->eflags & VM_MASK) { handle_vm86_fault(env); - } else -#endif - { - info.si_signo = TARGET_SIGSEGV; - info.si_errno = 0; - info.si_code = TARGET_SI_KERNEL; - info._sifields._sigfault._addr = 0; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; } +#endif + gen_signal(env, TARGET_SIGSEGV, TARGET_SI_KERNEL, 0); break; case EXCP0E_PAGE: - info.si_signo = TARGET_SIGSEGV; - info.si_errno = 0; - if (!(env->error_code & 1)) - info.si_code = TARGET_SEGV_MAPERR; - else - info.si_code = TARGET_SEGV_ACCERR; - info._sifields._sigfault._addr = env->cr[2]; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + gen_signal(env, TARGET_SIGSEGV, + (env->error_code & 1 ? + TARGET_SEGV_ACCERR : TARGET_SEGV_MAPERR), + env->cr[2]); break; case EXCP00_DIVZ: #ifndef TARGET_X86_64 if (env->eflags & VM_MASK) { handle_vm86_trap(env, trapnr); - } else -#endif - { - /* division by zero */ - info.si_signo = TARGET_SIGFPE; - info.si_errno = 0; - info.si_code = TARGET_FPE_INTDIV; - info._sifields._sigfault._addr = env->eip; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; } +#endif + gen_signal(env, TARGET_SIGFPE, TARGET_FPE_INTDIV, env->eip); break; case EXCP01_DB: case EXCP03_INT3: #ifndef TARGET_X86_64 if (env->eflags & VM_MASK) { handle_vm86_trap(env, trapnr); - } else + break; + } #endif - { - info.si_signo = TARGET_SIGTRAP; - info.si_errno = 0; - if (trapnr == EXCP01_DB) { - info.si_code = TARGET_TRAP_BRKPT; - info._sifields._sigfault._addr = env->eip; - } else { - info.si_code = TARGET_SI_KERNEL; - info._sifields._sigfault._addr = 0; - } - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + if (trapnr == EXCP01_DB) { + gen_signal(env, TARGET_SIGTRAP, TARGET_TRAP_BRKPT, env->eip); + } else { + gen_signal(env, TARGET_SIGTRAP, TARGET_SI_KERNEL, 0); } break; case EXCP04_INTO: @@ -205,31 +298,19 @@ void cpu_loop(CPUX86State *env) #ifndef TARGET_X86_64 if (env->eflags & VM_MASK) { handle_vm86_trap(env, trapnr); - } else -#endif - { - info.si_signo = TARGET_SIGSEGV; - info.si_errno = 0; - info.si_code = TARGET_SI_KERNEL; - info._sifields._sigfault._addr = 0; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; } +#endif + gen_signal(env, TARGET_SIGSEGV, TARGET_SI_KERNEL, 0); break; case EXCP06_ILLOP: - info.si_signo = TARGET_SIGILL; - info.si_errno = 0; - info.si_code = TARGET_ILL_ILLOPN; - info._sifields._sigfault._addr = env->eip; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + gen_signal(env, TARGET_SIGILL, TARGET_ILL_ILLOPN, env->eip); break; case EXCP_INTERRUPT: /* just indicate that signals should be handled asap */ break; case EXCP_DEBUG: - info.si_signo = TARGET_SIGTRAP; - info.si_errno = 0; - info.si_code = TARGET_TRAP_BRKPT; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + gen_signal(env, TARGET_SIGTRAP, TARGET_TRAP_BRKPT, 0); break; case EXCP_ATOMIC: cpu_exec_step_atomic(cs); diff --git a/linux-user/i386/syscall_32.tbl b/linux-user/i386/syscall_32.tbl new file mode 100644 index 0000000000..c17cb77eb1 --- /dev/null +++ b/linux-user/i386/syscall_32.tbl @@ -0,0 +1,444 @@ +# +# 32-bit system call numbers and entry vectors +# +# The format is: +# +# +# The __ia32_sys and __ia32_compat_sys stubs are created on-the-fly for +# sys_*() system calls and compat_sys_*() compat system calls if +# IA32_EMULATION is defined, and expect struct pt_regs *regs as their only +# parameter. +# +# The abi is always "i386" for this file. +# +0 i386 restart_syscall sys_restart_syscall __ia32_sys_restart_syscall +1 i386 exit sys_exit __ia32_sys_exit +2 i386 fork sys_fork __ia32_sys_fork +3 i386 read sys_read __ia32_sys_read +4 i386 write sys_write __ia32_sys_write +5 i386 open sys_open __ia32_compat_sys_open +6 i386 close sys_close __ia32_sys_close +7 i386 waitpid sys_waitpid __ia32_sys_waitpid +8 i386 creat sys_creat __ia32_sys_creat +9 i386 link sys_link __ia32_sys_link +10 i386 unlink sys_unlink __ia32_sys_unlink +11 i386 execve sys_execve __ia32_compat_sys_execve +12 i386 chdir sys_chdir __ia32_sys_chdir +13 i386 time sys_time32 __ia32_sys_time32 +14 i386 mknod sys_mknod __ia32_sys_mknod +15 i386 chmod sys_chmod __ia32_sys_chmod +16 i386 lchown sys_lchown16 __ia32_sys_lchown16 +17 i386 break +18 i386 oldstat sys_stat __ia32_sys_stat +19 i386 lseek sys_lseek __ia32_compat_sys_lseek +20 i386 getpid sys_getpid __ia32_sys_getpid +21 i386 mount sys_mount __ia32_compat_sys_mount +22 i386 umount sys_oldumount __ia32_sys_oldumount +23 i386 setuid sys_setuid16 __ia32_sys_setuid16 +24 i386 getuid sys_getuid16 __ia32_sys_getuid16 +25 i386 stime sys_stime32 __ia32_sys_stime32 +26 i386 ptrace sys_ptrace __ia32_compat_sys_ptrace +27 i386 alarm sys_alarm __ia32_sys_alarm +28 i386 oldfstat sys_fstat __ia32_sys_fstat +29 i386 pause sys_pause __ia32_sys_pause +30 i386 utime sys_utime32 __ia32_sys_utime32 +31 i386 stty +32 i386 gtty +33 i386 access sys_access __ia32_sys_access +34 i386 nice sys_nice __ia32_sys_nice +35 i386 ftime +36 i386 sync sys_sync __ia32_sys_sync +37 i386 kill sys_kill __ia32_sys_kill +38 i386 rename sys_rename __ia32_sys_rename +39 i386 mkdir sys_mkdir __ia32_sys_mkdir +40 i386 rmdir sys_rmdir __ia32_sys_rmdir +41 i386 dup sys_dup __ia32_sys_dup +42 i386 pipe sys_pipe __ia32_sys_pipe +43 i386 times sys_times __ia32_compat_sys_times +44 i386 prof +45 i386 brk sys_brk __ia32_sys_brk +46 i386 setgid sys_setgid16 __ia32_sys_setgid16 +47 i386 getgid sys_getgid16 __ia32_sys_getgid16 +48 i386 signal sys_signal __ia32_sys_signal +49 i386 geteuid sys_geteuid16 __ia32_sys_geteuid16 +50 i386 getegid sys_getegid16 __ia32_sys_getegid16 +51 i386 acct sys_acct __ia32_sys_acct +52 i386 umount2 sys_umount __ia32_sys_umount +53 i386 lock +54 i386 ioctl sys_ioctl __ia32_compat_sys_ioctl +55 i386 fcntl sys_fcntl __ia32_compat_sys_fcntl64 +56 i386 mpx +57 i386 setpgid sys_setpgid __ia32_sys_setpgid +58 i386 ulimit +59 i386 oldolduname sys_olduname __ia32_sys_olduname +60 i386 umask sys_umask __ia32_sys_umask +61 i386 chroot sys_chroot __ia32_sys_chroot +62 i386 ustat sys_ustat __ia32_compat_sys_ustat +63 i386 dup2 sys_dup2 __ia32_sys_dup2 +64 i386 getppid sys_getppid __ia32_sys_getppid +65 i386 getpgrp sys_getpgrp __ia32_sys_getpgrp +66 i386 setsid sys_setsid __ia32_sys_setsid +67 i386 sigaction sys_sigaction __ia32_compat_sys_sigaction +68 i386 sgetmask sys_sgetmask __ia32_sys_sgetmask +69 i386 ssetmask sys_ssetmask __ia32_sys_ssetmask +70 i386 setreuid sys_setreuid16 __ia32_sys_setreuid16 +71 i386 setregid sys_setregid16 __ia32_sys_setregid16 +72 i386 sigsuspend sys_sigsuspend __ia32_sys_sigsuspend +73 i386 sigpending sys_sigpending __ia32_compat_sys_sigpending +74 i386 sethostname sys_sethostname __ia32_sys_sethostname +75 i386 setrlimit sys_setrlimit __ia32_compat_sys_setrlimit +76 i386 getrlimit sys_old_getrlimit __ia32_compat_sys_old_getrlimit +77 i386 getrusage sys_getrusage __ia32_compat_sys_getrusage +78 i386 gettimeofday sys_gettimeofday __ia32_compat_sys_gettimeofday +79 i386 settimeofday sys_settimeofday __ia32_compat_sys_settimeofday +80 i386 getgroups sys_getgroups16 __ia32_sys_getgroups16 +81 i386 setgroups sys_setgroups16 __ia32_sys_setgroups16 +82 i386 select sys_old_select __ia32_compat_sys_old_select +83 i386 symlink sys_symlink __ia32_sys_symlink +84 i386 oldlstat sys_lstat __ia32_sys_lstat +85 i386 readlink sys_readlink __ia32_sys_readlink +86 i386 uselib sys_uselib __ia32_sys_uselib +87 i386 swapon sys_swapon __ia32_sys_swapon +88 i386 reboot sys_reboot __ia32_sys_reboot +89 i386 readdir sys_old_readdir __ia32_compat_sys_old_readdir +90 i386 mmap sys_old_mmap __ia32_compat_sys_x86_mmap +91 i386 munmap sys_munmap __ia32_sys_munmap +92 i386 truncate sys_truncate __ia32_compat_sys_truncate +93 i386 ftruncate sys_ftruncate __ia32_compat_sys_ftruncate +94 i386 fchmod sys_fchmod __ia32_sys_fchmod +95 i386 fchown sys_fchown16 __ia32_sys_fchown16 +96 i386 getpriority sys_getpriority __ia32_sys_getpriority +97 i386 setpriority sys_setpriority __ia32_sys_setpriority +98 i386 profil +99 i386 statfs sys_statfs __ia32_compat_sys_statfs +100 i386 fstatfs sys_fstatfs __ia32_compat_sys_fstatfs +101 i386 ioperm sys_ioperm __ia32_sys_ioperm +102 i386 socketcall sys_socketcall __ia32_compat_sys_socketcall +103 i386 syslog sys_syslog __ia32_sys_syslog +104 i386 setitimer sys_setitimer __ia32_compat_sys_setitimer +105 i386 getitimer sys_getitimer __ia32_compat_sys_getitimer +106 i386 stat sys_newstat __ia32_compat_sys_newstat +107 i386 lstat sys_newlstat __ia32_compat_sys_newlstat +108 i386 fstat sys_newfstat __ia32_compat_sys_newfstat +109 i386 olduname sys_uname __ia32_sys_uname +110 i386 iopl sys_iopl __ia32_sys_iopl +111 i386 vhangup sys_vhangup __ia32_sys_vhangup +112 i386 idle +113 i386 vm86old sys_vm86old __ia32_sys_ni_syscall +114 i386 wait4 sys_wait4 __ia32_compat_sys_wait4 +115 i386 swapoff sys_swapoff __ia32_sys_swapoff +116 i386 sysinfo sys_sysinfo __ia32_compat_sys_sysinfo +117 i386 ipc sys_ipc __ia32_compat_sys_ipc +118 i386 fsync sys_fsync __ia32_sys_fsync +119 i386 sigreturn sys_sigreturn __ia32_compat_sys_sigreturn +120 i386 clone sys_clone __ia32_compat_sys_x86_clone +121 i386 setdomainname sys_setdomainname __ia32_sys_setdomainname +122 i386 uname sys_newuname __ia32_sys_newuname +123 i386 modify_ldt sys_modify_ldt __ia32_sys_modify_ldt +124 i386 adjtimex sys_adjtimex_time32 __ia32_sys_adjtimex_time32 +125 i386 mprotect sys_mprotect __ia32_sys_mprotect +126 i386 sigprocmask sys_sigprocmask __ia32_compat_sys_sigprocmask +127 i386 create_module +128 i386 init_module sys_init_module __ia32_sys_init_module +129 i386 delete_module sys_delete_module __ia32_sys_delete_module +130 i386 get_kernel_syms +131 i386 quotactl sys_quotactl __ia32_compat_sys_quotactl32 +132 i386 getpgid sys_getpgid __ia32_sys_getpgid +133 i386 fchdir sys_fchdir __ia32_sys_fchdir +134 i386 bdflush sys_bdflush __ia32_sys_bdflush +135 i386 sysfs sys_sysfs __ia32_sys_sysfs +136 i386 personality sys_personality __ia32_sys_personality +137 i386 afs_syscall +138 i386 setfsuid sys_setfsuid16 __ia32_sys_setfsuid16 +139 i386 setfsgid sys_setfsgid16 __ia32_sys_setfsgid16 +140 i386 _llseek sys_llseek __ia32_sys_llseek +141 i386 getdents sys_getdents __ia32_compat_sys_getdents +142 i386 _newselect sys_select __ia32_compat_sys_select +143 i386 flock sys_flock __ia32_sys_flock +144 i386 msync sys_msync __ia32_sys_msync +145 i386 readv sys_readv __ia32_compat_sys_readv +146 i386 writev sys_writev __ia32_compat_sys_writev +147 i386 getsid sys_getsid __ia32_sys_getsid +148 i386 fdatasync sys_fdatasync __ia32_sys_fdatasync +149 i386 _sysctl sys_sysctl __ia32_compat_sys_sysctl +150 i386 mlock sys_mlock __ia32_sys_mlock +151 i386 munlock sys_munlock __ia32_sys_munlock +152 i386 mlockall sys_mlockall __ia32_sys_mlockall +153 i386 munlockall sys_munlockall __ia32_sys_munlockall +154 i386 sched_setparam sys_sched_setparam __ia32_sys_sched_setparam +155 i386 sched_getparam sys_sched_getparam __ia32_sys_sched_getparam +156 i386 sched_setscheduler sys_sched_setscheduler __ia32_sys_sched_setscheduler +157 i386 sched_getscheduler sys_sched_getscheduler __ia32_sys_sched_getscheduler +158 i386 sched_yield sys_sched_yield __ia32_sys_sched_yield +159 i386 sched_get_priority_max sys_sched_get_priority_max __ia32_sys_sched_get_priority_max +160 i386 sched_get_priority_min sys_sched_get_priority_min __ia32_sys_sched_get_priority_min +161 i386 sched_rr_get_interval sys_sched_rr_get_interval_time32 __ia32_sys_sched_rr_get_interval_time32 +162 i386 nanosleep sys_nanosleep_time32 __ia32_sys_nanosleep_time32 +163 i386 mremap sys_mremap __ia32_sys_mremap +164 i386 setresuid sys_setresuid16 __ia32_sys_setresuid16 +165 i386 getresuid sys_getresuid16 __ia32_sys_getresuid16 +166 i386 vm86 sys_vm86 __ia32_sys_ni_syscall +167 i386 query_module +168 i386 poll sys_poll __ia32_sys_poll +169 i386 nfsservctl +170 i386 setresgid sys_setresgid16 __ia32_sys_setresgid16 +171 i386 getresgid sys_getresgid16 __ia32_sys_getresgid16 +172 i386 prctl sys_prctl __ia32_sys_prctl +173 i386 rt_sigreturn sys_rt_sigreturn __ia32_compat_sys_rt_sigreturn +174 i386 rt_sigaction sys_rt_sigaction __ia32_compat_sys_rt_sigaction +175 i386 rt_sigprocmask sys_rt_sigprocmask __ia32_compat_sys_rt_sigprocmask +176 i386 rt_sigpending sys_rt_sigpending __ia32_compat_sys_rt_sigpending +177 i386 rt_sigtimedwait sys_rt_sigtimedwait_time32 __ia32_compat_sys_rt_sigtimedwait_time32 +178 i386 rt_sigqueueinfo sys_rt_sigqueueinfo __ia32_compat_sys_rt_sigqueueinfo +179 i386 rt_sigsuspend sys_rt_sigsuspend __ia32_compat_sys_rt_sigsuspend +180 i386 pread64 sys_pread64 __ia32_compat_sys_x86_pread +181 i386 pwrite64 sys_pwrite64 __ia32_compat_sys_x86_pwrite +182 i386 chown sys_chown16 __ia32_sys_chown16 +183 i386 getcwd sys_getcwd __ia32_sys_getcwd +184 i386 capget sys_capget __ia32_sys_capget +185 i386 capset sys_capset __ia32_sys_capset +186 i386 sigaltstack sys_sigaltstack __ia32_compat_sys_sigaltstack +187 i386 sendfile sys_sendfile __ia32_compat_sys_sendfile +188 i386 getpmsg +189 i386 putpmsg +190 i386 vfork sys_vfork __ia32_sys_vfork +191 i386 ugetrlimit sys_getrlimit __ia32_compat_sys_getrlimit +192 i386 mmap2 sys_mmap_pgoff __ia32_sys_mmap_pgoff +193 i386 truncate64 sys_truncate64 __ia32_compat_sys_x86_truncate64 +194 i386 ftruncate64 sys_ftruncate64 __ia32_compat_sys_x86_ftruncate64 +195 i386 stat64 sys_stat64 __ia32_compat_sys_x86_stat64 +196 i386 lstat64 sys_lstat64 __ia32_compat_sys_x86_lstat64 +197 i386 fstat64 sys_fstat64 __ia32_compat_sys_x86_fstat64 +198 i386 lchown32 sys_lchown __ia32_sys_lchown +199 i386 getuid32 sys_getuid __ia32_sys_getuid +200 i386 getgid32 sys_getgid __ia32_sys_getgid +201 i386 geteuid32 sys_geteuid __ia32_sys_geteuid +202 i386 getegid32 sys_getegid __ia32_sys_getegid +203 i386 setreuid32 sys_setreuid __ia32_sys_setreuid +204 i386 setregid32 sys_setregid __ia32_sys_setregid +205 i386 getgroups32 sys_getgroups __ia32_sys_getgroups +206 i386 setgroups32 sys_setgroups __ia32_sys_setgroups +207 i386 fchown32 sys_fchown __ia32_sys_fchown +208 i386 setresuid32 sys_setresuid __ia32_sys_setresuid +209 i386 getresuid32 sys_getresuid __ia32_sys_getresuid +210 i386 setresgid32 sys_setresgid __ia32_sys_setresgid +211 i386 getresgid32 sys_getresgid __ia32_sys_getresgid +212 i386 chown32 sys_chown __ia32_sys_chown +213 i386 setuid32 sys_setuid __ia32_sys_setuid +214 i386 setgid32 sys_setgid __ia32_sys_setgid +215 i386 setfsuid32 sys_setfsuid __ia32_sys_setfsuid +216 i386 setfsgid32 sys_setfsgid __ia32_sys_setfsgid +217 i386 pivot_root sys_pivot_root __ia32_sys_pivot_root +218 i386 mincore sys_mincore __ia32_sys_mincore +219 i386 madvise sys_madvise __ia32_sys_madvise +220 i386 getdents64 sys_getdents64 __ia32_sys_getdents64 +221 i386 fcntl64 sys_fcntl64 __ia32_compat_sys_fcntl64 +# 222 is unused +# 223 is unused +224 i386 gettid sys_gettid __ia32_sys_gettid +225 i386 readahead sys_readahead __ia32_compat_sys_x86_readahead +226 i386 setxattr sys_setxattr __ia32_sys_setxattr +227 i386 lsetxattr sys_lsetxattr __ia32_sys_lsetxattr +228 i386 fsetxattr sys_fsetxattr __ia32_sys_fsetxattr +229 i386 getxattr sys_getxattr __ia32_sys_getxattr +230 i386 lgetxattr sys_lgetxattr __ia32_sys_lgetxattr +231 i386 fgetxattr sys_fgetxattr __ia32_sys_fgetxattr +232 i386 listxattr sys_listxattr __ia32_sys_listxattr +233 i386 llistxattr sys_llistxattr __ia32_sys_llistxattr +234 i386 flistxattr sys_flistxattr __ia32_sys_flistxattr +235 i386 removexattr sys_removexattr __ia32_sys_removexattr +236 i386 lremovexattr sys_lremovexattr __ia32_sys_lremovexattr +237 i386 fremovexattr sys_fremovexattr __ia32_sys_fremovexattr +238 i386 tkill sys_tkill __ia32_sys_tkill +239 i386 sendfile64 sys_sendfile64 __ia32_sys_sendfile64 +240 i386 futex sys_futex_time32 __ia32_sys_futex_time32 +241 i386 sched_setaffinity sys_sched_setaffinity __ia32_compat_sys_sched_setaffinity +242 i386 sched_getaffinity sys_sched_getaffinity __ia32_compat_sys_sched_getaffinity +243 i386 set_thread_area sys_set_thread_area __ia32_sys_set_thread_area +244 i386 get_thread_area sys_get_thread_area __ia32_sys_get_thread_area +245 i386 io_setup sys_io_setup __ia32_compat_sys_io_setup +246 i386 io_destroy sys_io_destroy __ia32_sys_io_destroy +247 i386 io_getevents sys_io_getevents_time32 __ia32_sys_io_getevents_time32 +248 i386 io_submit sys_io_submit __ia32_compat_sys_io_submit +249 i386 io_cancel sys_io_cancel __ia32_sys_io_cancel +250 i386 fadvise64 sys_fadvise64 __ia32_compat_sys_x86_fadvise64 +# 251 is available for reuse (was briefly sys_set_zone_reclaim) +252 i386 exit_group sys_exit_group __ia32_sys_exit_group +253 i386 lookup_dcookie sys_lookup_dcookie __ia32_compat_sys_lookup_dcookie +254 i386 epoll_create sys_epoll_create __ia32_sys_epoll_create +255 i386 epoll_ctl sys_epoll_ctl __ia32_sys_epoll_ctl +256 i386 epoll_wait sys_epoll_wait __ia32_sys_epoll_wait +257 i386 remap_file_pages sys_remap_file_pages __ia32_sys_remap_file_pages +258 i386 set_tid_address sys_set_tid_address __ia32_sys_set_tid_address +259 i386 timer_create sys_timer_create __ia32_compat_sys_timer_create +260 i386 timer_settime sys_timer_settime32 __ia32_sys_timer_settime32 +261 i386 timer_gettime sys_timer_gettime32 __ia32_sys_timer_gettime32 +262 i386 timer_getoverrun sys_timer_getoverrun __ia32_sys_timer_getoverrun +263 i386 timer_delete sys_timer_delete __ia32_sys_timer_delete +264 i386 clock_settime sys_clock_settime32 __ia32_sys_clock_settime32 +265 i386 clock_gettime sys_clock_gettime32 __ia32_sys_clock_gettime32 +266 i386 clock_getres sys_clock_getres_time32 __ia32_sys_clock_getres_time32 +267 i386 clock_nanosleep sys_clock_nanosleep_time32 __ia32_sys_clock_nanosleep_time32 +268 i386 statfs64 sys_statfs64 __ia32_compat_sys_statfs64 +269 i386 fstatfs64 sys_fstatfs64 __ia32_compat_sys_fstatfs64 +270 i386 tgkill sys_tgkill __ia32_sys_tgkill +271 i386 utimes sys_utimes_time32 __ia32_sys_utimes_time32 +272 i386 fadvise64_64 sys_fadvise64_64 __ia32_compat_sys_x86_fadvise64_64 +273 i386 vserver +274 i386 mbind sys_mbind __ia32_sys_mbind +275 i386 get_mempolicy sys_get_mempolicy __ia32_compat_sys_get_mempolicy +276 i386 set_mempolicy sys_set_mempolicy __ia32_sys_set_mempolicy +277 i386 mq_open sys_mq_open __ia32_compat_sys_mq_open +278 i386 mq_unlink sys_mq_unlink __ia32_sys_mq_unlink +279 i386 mq_timedsend sys_mq_timedsend_time32 __ia32_sys_mq_timedsend_time32 +280 i386 mq_timedreceive sys_mq_timedreceive_time32 __ia32_sys_mq_timedreceive_time32 +281 i386 mq_notify sys_mq_notify __ia32_compat_sys_mq_notify +282 i386 mq_getsetattr sys_mq_getsetattr __ia32_compat_sys_mq_getsetattr +283 i386 kexec_load sys_kexec_load __ia32_compat_sys_kexec_load +284 i386 waitid sys_waitid __ia32_compat_sys_waitid +# 285 sys_setaltroot +286 i386 add_key sys_add_key __ia32_sys_add_key +287 i386 request_key sys_request_key __ia32_sys_request_key +288 i386 keyctl sys_keyctl __ia32_compat_sys_keyctl +289 i386 ioprio_set sys_ioprio_set __ia32_sys_ioprio_set +290 i386 ioprio_get sys_ioprio_get __ia32_sys_ioprio_get +291 i386 inotify_init sys_inotify_init __ia32_sys_inotify_init +292 i386 inotify_add_watch sys_inotify_add_watch __ia32_sys_inotify_add_watch +293 i386 inotify_rm_watch sys_inotify_rm_watch __ia32_sys_inotify_rm_watch +294 i386 migrate_pages sys_migrate_pages __ia32_sys_migrate_pages +295 i386 openat sys_openat __ia32_compat_sys_openat +296 i386 mkdirat sys_mkdirat __ia32_sys_mkdirat +297 i386 mknodat sys_mknodat __ia32_sys_mknodat +298 i386 fchownat sys_fchownat __ia32_sys_fchownat +299 i386 futimesat sys_futimesat_time32 __ia32_sys_futimesat_time32 +300 i386 fstatat64 sys_fstatat64 __ia32_compat_sys_x86_fstatat +301 i386 unlinkat sys_unlinkat __ia32_sys_unlinkat +302 i386 renameat sys_renameat __ia32_sys_renameat +303 i386 linkat sys_linkat __ia32_sys_linkat +304 i386 symlinkat sys_symlinkat __ia32_sys_symlinkat +305 i386 readlinkat sys_readlinkat __ia32_sys_readlinkat +306 i386 fchmodat sys_fchmodat __ia32_sys_fchmodat +307 i386 faccessat sys_faccessat __ia32_sys_faccessat +308 i386 pselect6 sys_pselect6_time32 __ia32_compat_sys_pselect6_time32 +309 i386 ppoll sys_ppoll_time32 __ia32_compat_sys_ppoll_time32 +310 i386 unshare sys_unshare __ia32_sys_unshare +311 i386 set_robust_list sys_set_robust_list __ia32_compat_sys_set_robust_list +312 i386 get_robust_list sys_get_robust_list __ia32_compat_sys_get_robust_list +313 i386 splice sys_splice __ia32_sys_splice +314 i386 sync_file_range sys_sync_file_range __ia32_compat_sys_x86_sync_file_range +315 i386 tee sys_tee __ia32_sys_tee +316 i386 vmsplice sys_vmsplice __ia32_compat_sys_vmsplice +317 i386 move_pages sys_move_pages __ia32_compat_sys_move_pages +318 i386 getcpu sys_getcpu __ia32_sys_getcpu +319 i386 epoll_pwait sys_epoll_pwait __ia32_sys_epoll_pwait +320 i386 utimensat sys_utimensat_time32 __ia32_sys_utimensat_time32 +321 i386 signalfd sys_signalfd __ia32_compat_sys_signalfd +322 i386 timerfd_create sys_timerfd_create __ia32_sys_timerfd_create +323 i386 eventfd sys_eventfd __ia32_sys_eventfd +324 i386 fallocate sys_fallocate __ia32_compat_sys_x86_fallocate +325 i386 timerfd_settime sys_timerfd_settime32 __ia32_sys_timerfd_settime32 +326 i386 timerfd_gettime sys_timerfd_gettime32 __ia32_sys_timerfd_gettime32 +327 i386 signalfd4 sys_signalfd4 __ia32_compat_sys_signalfd4 +328 i386 eventfd2 sys_eventfd2 __ia32_sys_eventfd2 +329 i386 epoll_create1 sys_epoll_create1 __ia32_sys_epoll_create1 +330 i386 dup3 sys_dup3 __ia32_sys_dup3 +331 i386 pipe2 sys_pipe2 __ia32_sys_pipe2 +332 i386 inotify_init1 sys_inotify_init1 __ia32_sys_inotify_init1 +333 i386 preadv sys_preadv __ia32_compat_sys_preadv +334 i386 pwritev sys_pwritev __ia32_compat_sys_pwritev +335 i386 rt_tgsigqueueinfo sys_rt_tgsigqueueinfo __ia32_compat_sys_rt_tgsigqueueinfo +336 i386 perf_event_open sys_perf_event_open __ia32_sys_perf_event_open +337 i386 recvmmsg sys_recvmmsg_time32 __ia32_compat_sys_recvmmsg_time32 +338 i386 fanotify_init sys_fanotify_init __ia32_sys_fanotify_init +339 i386 fanotify_mark sys_fanotify_mark __ia32_compat_sys_fanotify_mark +340 i386 prlimit64 sys_prlimit64 __ia32_sys_prlimit64 +341 i386 name_to_handle_at sys_name_to_handle_at __ia32_sys_name_to_handle_at +342 i386 open_by_handle_at sys_open_by_handle_at __ia32_compat_sys_open_by_handle_at +343 i386 clock_adjtime sys_clock_adjtime32 __ia32_sys_clock_adjtime32 +344 i386 syncfs sys_syncfs __ia32_sys_syncfs +345 i386 sendmmsg sys_sendmmsg __ia32_compat_sys_sendmmsg +346 i386 setns sys_setns __ia32_sys_setns +347 i386 process_vm_readv sys_process_vm_readv __ia32_compat_sys_process_vm_readv +348 i386 process_vm_writev sys_process_vm_writev __ia32_compat_sys_process_vm_writev +349 i386 kcmp sys_kcmp __ia32_sys_kcmp +350 i386 finit_module sys_finit_module __ia32_sys_finit_module +351 i386 sched_setattr sys_sched_setattr __ia32_sys_sched_setattr +352 i386 sched_getattr sys_sched_getattr __ia32_sys_sched_getattr +353 i386 renameat2 sys_renameat2 __ia32_sys_renameat2 +354 i386 seccomp sys_seccomp __ia32_sys_seccomp +355 i386 getrandom sys_getrandom __ia32_sys_getrandom +356 i386 memfd_create sys_memfd_create __ia32_sys_memfd_create +357 i386 bpf sys_bpf __ia32_sys_bpf +358 i386 execveat sys_execveat __ia32_compat_sys_execveat +359 i386 socket sys_socket __ia32_sys_socket +360 i386 socketpair sys_socketpair __ia32_sys_socketpair +361 i386 bind sys_bind __ia32_sys_bind +362 i386 connect sys_connect __ia32_sys_connect +363 i386 listen sys_listen __ia32_sys_listen +364 i386 accept4 sys_accept4 __ia32_sys_accept4 +365 i386 getsockopt sys_getsockopt __ia32_compat_sys_getsockopt +366 i386 setsockopt sys_setsockopt __ia32_compat_sys_setsockopt +367 i386 getsockname sys_getsockname __ia32_sys_getsockname +368 i386 getpeername sys_getpeername __ia32_sys_getpeername +369 i386 sendto sys_sendto __ia32_sys_sendto +370 i386 sendmsg sys_sendmsg __ia32_compat_sys_sendmsg +371 i386 recvfrom sys_recvfrom __ia32_compat_sys_recvfrom +372 i386 recvmsg sys_recvmsg __ia32_compat_sys_recvmsg +373 i386 shutdown sys_shutdown __ia32_sys_shutdown +374 i386 userfaultfd sys_userfaultfd __ia32_sys_userfaultfd +375 i386 membarrier sys_membarrier __ia32_sys_membarrier +376 i386 mlock2 sys_mlock2 __ia32_sys_mlock2 +377 i386 copy_file_range sys_copy_file_range __ia32_sys_copy_file_range +378 i386 preadv2 sys_preadv2 __ia32_compat_sys_preadv2 +379 i386 pwritev2 sys_pwritev2 __ia32_compat_sys_pwritev2 +380 i386 pkey_mprotect sys_pkey_mprotect __ia32_sys_pkey_mprotect +381 i386 pkey_alloc sys_pkey_alloc __ia32_sys_pkey_alloc +382 i386 pkey_free sys_pkey_free __ia32_sys_pkey_free +383 i386 statx sys_statx __ia32_sys_statx +384 i386 arch_prctl sys_arch_prctl __ia32_compat_sys_arch_prctl +385 i386 io_pgetevents sys_io_pgetevents_time32 __ia32_compat_sys_io_pgetevents +386 i386 rseq sys_rseq __ia32_sys_rseq +393 i386 semget sys_semget __ia32_sys_semget +394 i386 semctl sys_semctl __ia32_compat_sys_semctl +395 i386 shmget sys_shmget __ia32_sys_shmget +396 i386 shmctl sys_shmctl __ia32_compat_sys_shmctl +397 i386 shmat sys_shmat __ia32_compat_sys_shmat +398 i386 shmdt sys_shmdt __ia32_sys_shmdt +399 i386 msgget sys_msgget __ia32_sys_msgget +400 i386 msgsnd sys_msgsnd __ia32_compat_sys_msgsnd +401 i386 msgrcv sys_msgrcv __ia32_compat_sys_msgrcv +402 i386 msgctl sys_msgctl __ia32_compat_sys_msgctl +403 i386 clock_gettime64 sys_clock_gettime __ia32_sys_clock_gettime +404 i386 clock_settime64 sys_clock_settime __ia32_sys_clock_settime +405 i386 clock_adjtime64 sys_clock_adjtime __ia32_sys_clock_adjtime +406 i386 clock_getres_time64 sys_clock_getres __ia32_sys_clock_getres +407 i386 clock_nanosleep_time64 sys_clock_nanosleep __ia32_sys_clock_nanosleep +408 i386 timer_gettime64 sys_timer_gettime __ia32_sys_timer_gettime +409 i386 timer_settime64 sys_timer_settime __ia32_sys_timer_settime +410 i386 timerfd_gettime64 sys_timerfd_gettime __ia32_sys_timerfd_gettime +411 i386 timerfd_settime64 sys_timerfd_settime __ia32_sys_timerfd_settime +412 i386 utimensat_time64 sys_utimensat __ia32_sys_utimensat +413 i386 pselect6_time64 sys_pselect6 __ia32_compat_sys_pselect6_time64 +414 i386 ppoll_time64 sys_ppoll __ia32_compat_sys_ppoll_time64 +416 i386 io_pgetevents_time64 sys_io_pgetevents __ia32_sys_io_pgetevents +417 i386 recvmmsg_time64 sys_recvmmsg __ia32_compat_sys_recvmmsg_time64 +418 i386 mq_timedsend_time64 sys_mq_timedsend __ia32_sys_mq_timedsend +419 i386 mq_timedreceive_time64 sys_mq_timedreceive __ia32_sys_mq_timedreceive +420 i386 semtimedop_time64 sys_semtimedop __ia32_sys_semtimedop +421 i386 rt_sigtimedwait_time64 sys_rt_sigtimedwait __ia32_compat_sys_rt_sigtimedwait_time64 +422 i386 futex_time64 sys_futex __ia32_sys_futex +423 i386 sched_rr_get_interval_time64 sys_sched_rr_get_interval __ia32_sys_sched_rr_get_interval +424 i386 pidfd_send_signal sys_pidfd_send_signal __ia32_sys_pidfd_send_signal +425 i386 io_uring_setup sys_io_uring_setup __ia32_sys_io_uring_setup +426 i386 io_uring_enter sys_io_uring_enter __ia32_sys_io_uring_enter +427 i386 io_uring_register sys_io_uring_register __ia32_sys_io_uring_register +428 i386 open_tree sys_open_tree __ia32_sys_open_tree +429 i386 move_mount sys_move_mount __ia32_sys_move_mount +430 i386 fsopen sys_fsopen __ia32_sys_fsopen +431 i386 fsconfig sys_fsconfig __ia32_sys_fsconfig +432 i386 fsmount sys_fsmount __ia32_sys_fsmount +433 i386 fspick sys_fspick __ia32_sys_fspick +434 i386 pidfd_open sys_pidfd_open __ia32_sys_pidfd_open +435 i386 clone3 sys_clone3 __ia32_sys_clone3 +437 i386 openat2 sys_openat2 __ia32_sys_openat2 +438 i386 pidfd_getfd sys_pidfd_getfd __ia32_sys_pidfd_getfd diff --git a/linux-user/i386/syscall_nr.h b/linux-user/i386/syscall_nr.h deleted file mode 100644 index 3234ec21c6..0000000000 --- a/linux-user/i386/syscall_nr.h +++ /dev/null @@ -1,387 +0,0 @@ -/* - * This file contains the system call numbers. - */ - -#ifndef LINUX_USER_I386_SYSCALL_NR_H -#define LINUX_USER_I386_SYSCALL_NR_H - -#define TARGET_NR_restart_syscall 0 -#define TARGET_NR_exit 1 -#define TARGET_NR_fork 2 -#define TARGET_NR_read 3 -#define TARGET_NR_write 4 -#define TARGET_NR_open 5 -#define TARGET_NR_close 6 -#define TARGET_NR_waitpid 7 -#define TARGET_NR_creat 8 -#define TARGET_NR_link 9 -#define TARGET_NR_unlink 10 -#define TARGET_NR_execve 11 -#define TARGET_NR_chdir 12 -#define TARGET_NR_time 13 -#define TARGET_NR_mknod 14 -#define TARGET_NR_chmod 15 -#define TARGET_NR_lchown 16 -#define TARGET_NR_break 17 -#define TARGET_NR_oldstat 18 -#define TARGET_NR_lseek 19 -#define TARGET_NR_getpid 20 -#define TARGET_NR_mount 21 -#define TARGET_NR_umount 22 -#define TARGET_NR_setuid 23 -#define TARGET_NR_getuid 24 -#define TARGET_NR_stime 25 -#define TARGET_NR_ptrace 26 -#define TARGET_NR_alarm 27 -#define TARGET_NR_oldfstat 28 -#define TARGET_NR_pause 29 -#define TARGET_NR_utime 30 -#define TARGET_NR_stty 31 -#define TARGET_NR_gtty 32 -#define TARGET_NR_access 33 -#define TARGET_NR_nice 34 -#define TARGET_NR_ftime 35 -#define TARGET_NR_sync 36 -#define TARGET_NR_kill 37 -#define TARGET_NR_rename 38 -#define TARGET_NR_mkdir 39 -#define TARGET_NR_rmdir 40 -#define TARGET_NR_dup 41 -#define TARGET_NR_pipe 42 -#define TARGET_NR_times 43 -#define TARGET_NR_prof 44 -#define TARGET_NR_brk 45 -#define TARGET_NR_setgid 46 -#define TARGET_NR_getgid 47 -#define TARGET_NR_signal 48 -#define TARGET_NR_geteuid 49 -#define TARGET_NR_getegid 50 -#define TARGET_NR_acct 51 -#define TARGET_NR_umount2 52 -#define TARGET_NR_lock 53 -#define TARGET_NR_ioctl 54 -#define TARGET_NR_fcntl 55 -#define TARGET_NR_mpx 56 -#define TARGET_NR_setpgid 57 -#define TARGET_NR_ulimit 58 -#define TARGET_NR_oldolduname 59 -#define TARGET_NR_umask 60 -#define TARGET_NR_chroot 61 -#define TARGET_NR_ustat 62 -#define TARGET_NR_dup2 63 -#define TARGET_NR_getppid 64 -#define TARGET_NR_getpgrp 65 -#define TARGET_NR_setsid 66 -#define TARGET_NR_sigaction 67 -#define TARGET_NR_sgetmask 68 -#define TARGET_NR_ssetmask 69 -#define TARGET_NR_setreuid 70 -#define TARGET_NR_setregid 71 -#define TARGET_NR_sigsuspend 72 -#define TARGET_NR_sigpending 73 -#define TARGET_NR_sethostname 74 -#define TARGET_NR_setrlimit 75 -#define TARGET_NR_getrlimit 76 /* Back compatible 2Gig limited rlimit */ -#define TARGET_NR_getrusage 77 -#define TARGET_NR_gettimeofday 78 -#define TARGET_NR_settimeofday 79 -#define TARGET_NR_getgroups 80 -#define TARGET_NR_setgroups 81 -#define TARGET_NR_select 82 -#define TARGET_NR_symlink 83 -#define TARGET_NR_oldlstat 84 -#define TARGET_NR_readlink 85 -#define TARGET_NR_uselib 86 -#define TARGET_NR_swapon 87 -#define TARGET_NR_reboot 88 -#define TARGET_NR_readdir 89 -#define TARGET_NR_mmap 90 -#define TARGET_NR_munmap 91 -#define TARGET_NR_truncate 92 -#define TARGET_NR_ftruncate 93 -#define TARGET_NR_fchmod 94 -#define TARGET_NR_fchown 95 -#define TARGET_NR_getpriority 96 -#define TARGET_NR_setpriority 97 -#define TARGET_NR_profil 98 -#define TARGET_NR_statfs 99 -#define TARGET_NR_fstatfs 100 -#define TARGET_NR_ioperm 101 -#define TARGET_NR_socketcall 102 -#define TARGET_NR_syslog 103 -#define TARGET_NR_setitimer 104 -#define TARGET_NR_getitimer 105 -#define TARGET_NR_stat 106 -#define TARGET_NR_lstat 107 -#define TARGET_NR_fstat 108 -#define TARGET_NR_olduname 109 -#define TARGET_NR_iopl 110 -#define TARGET_NR_vhangup 111 -#define TARGET_NR_idle 112 -#define TARGET_NR_vm86old 113 -#define TARGET_NR_wait4 114 -#define TARGET_NR_swapoff 115 -#define TARGET_NR_sysinfo 116 -#define TARGET_NR_ipc 117 -#define TARGET_NR_fsync 118 -#define TARGET_NR_sigreturn 119 -#define TARGET_NR_clone 120 -#define TARGET_NR_setdomainname 121 -#define TARGET_NR_uname 122 -#define TARGET_NR_modify_ldt 123 -#define TARGET_NR_adjtimex 124 -#define TARGET_NR_mprotect 125 -#define TARGET_NR_sigprocmask 126 -#define TARGET_NR_create_module 127 -#define TARGET_NR_init_module 128 -#define TARGET_NR_delete_module 129 -#define TARGET_NR_get_kernel_syms 130 -#define TARGET_NR_quotactl 131 -#define TARGET_NR_getpgid 132 -#define TARGET_NR_fchdir 133 -#define TARGET_NR_bdflush 134 -#define TARGET_NR_sysfs 135 -#define TARGET_NR_personality 136 -#define TARGET_NR_afs_syscall 137 /* Syscall for Andrew File System */ -#define TARGET_NR_setfsuid 138 -#define TARGET_NR_setfsgid 139 -#define TARGET_NR__llseek 140 -#define TARGET_NR_getdents 141 -#define TARGET_NR__newselect 142 -#define TARGET_NR_flock 143 -#define TARGET_NR_msync 144 -#define TARGET_NR_readv 145 -#define TARGET_NR_writev 146 -#define TARGET_NR_getsid 147 -#define TARGET_NR_fdatasync 148 -#define TARGET_NR__sysctl 149 -#define TARGET_NR_mlock 150 -#define TARGET_NR_munlock 151 -#define TARGET_NR_mlockall 152 -#define TARGET_NR_munlockall 153 -#define TARGET_NR_sched_setparam 154 -#define TARGET_NR_sched_getparam 155 -#define TARGET_NR_sched_setscheduler 156 -#define TARGET_NR_sched_getscheduler 157 -#define TARGET_NR_sched_yield 158 -#define TARGET_NR_sched_get_priority_max 159 -#define TARGET_NR_sched_get_priority_min 160 -#define TARGET_NR_sched_rr_get_interval 161 -#define TARGET_NR_nanosleep 162 -#define TARGET_NR_mremap 163 -#define TARGET_NR_setresuid 164 -#define TARGET_NR_getresuid 165 -#define TARGET_NR_vm86 166 -#define TARGET_NR_query_module 167 -#define TARGET_NR_poll 168 -#define TARGET_NR_nfsservctl 169 -#define TARGET_NR_setresgid 170 -#define TARGET_NR_getresgid 171 -#define TARGET_NR_prctl 172 -#define TARGET_NR_rt_sigreturn 173 -#define TARGET_NR_rt_sigaction 174 -#define TARGET_NR_rt_sigprocmask 175 -#define TARGET_NR_rt_sigpending 176 -#define TARGET_NR_rt_sigtimedwait 177 -#define TARGET_NR_rt_sigqueueinfo 178 -#define TARGET_NR_rt_sigsuspend 179 -#define TARGET_NR_pread64 180 -#define TARGET_NR_pwrite64 181 -#define TARGET_NR_chown 182 -#define TARGET_NR_getcwd 183 -#define TARGET_NR_capget 184 -#define TARGET_NR_capset 185 -#define TARGET_NR_sigaltstack 186 -#define TARGET_NR_sendfile 187 -#define TARGET_NR_getpmsg 188 /* some people actually want streams */ -#define TARGET_NR_putpmsg 189 /* some people actually want streams */ -#define TARGET_NR_vfork 190 -#define TARGET_NR_ugetrlimit 191 /* SuS compliant getrlimit */ -#define TARGET_NR_mmap2 192 -#define TARGET_NR_truncate64 193 -#define TARGET_NR_ftruncate64 194 -#define TARGET_NR_stat64 195 -#define TARGET_NR_lstat64 196 -#define TARGET_NR_fstat64 197 -#define TARGET_NR_lchown32 198 -#define TARGET_NR_getuid32 199 -#define TARGET_NR_getgid32 200 -#define TARGET_NR_geteuid32 201 -#define TARGET_NR_getegid32 202 -#define TARGET_NR_setreuid32 203 -#define TARGET_NR_setregid32 204 -#define TARGET_NR_getgroups32 205 -#define TARGET_NR_setgroups32 206 -#define TARGET_NR_fchown32 207 -#define TARGET_NR_setresuid32 208 -#define TARGET_NR_getresuid32 209 -#define TARGET_NR_setresgid32 210 -#define TARGET_NR_getresgid32 211 -#define TARGET_NR_chown32 212 -#define TARGET_NR_setuid32 213 -#define TARGET_NR_setgid32 214 -#define TARGET_NR_setfsuid32 215 -#define TARGET_NR_setfsgid32 216 -#define TARGET_NR_pivot_root 217 -#define TARGET_NR_mincore 218 -#define TARGET_NR_madvise 219 -#define TARGET_NR_madvise1 219 /* delete when C lib stub is removed */ -#define TARGET_NR_getdents64 220 -#define TARGET_NR_fcntl64 221 -/* 223 is unused */ -#define TARGET_NR_gettid 224 -#define TARGET_NR_readahead 225 -#define TARGET_NR_setxattr 226 -#define TARGET_NR_lsetxattr 227 -#define TARGET_NR_fsetxattr 228 -#define TARGET_NR_getxattr 229 -#define TARGET_NR_lgetxattr 230 -#define TARGET_NR_fgetxattr 231 -#define TARGET_NR_listxattr 232 -#define TARGET_NR_llistxattr 233 -#define TARGET_NR_flistxattr 234 -#define TARGET_NR_removexattr 235 -#define TARGET_NR_lremovexattr 236 -#define TARGET_NR_fremovexattr 237 -#define TARGET_NR_tkill 238 -#define TARGET_NR_sendfile64 239 -#define TARGET_NR_futex 240 -#define TARGET_NR_sched_setaffinity 241 -#define TARGET_NR_sched_getaffinity 242 -#define TARGET_NR_set_thread_area 243 -#define TARGET_NR_get_thread_area 244 -#define TARGET_NR_io_setup 245 -#define TARGET_NR_io_destroy 246 -#define TARGET_NR_io_getevents 247 -#define TARGET_NR_io_submit 248 -#define TARGET_NR_io_cancel 249 -#define TARGET_NR_fadvise64 250 -/* 251 is available for reuse (was briefly sys_set_zone_reclaim) */ -#define TARGET_NR_exit_group 252 -#define TARGET_NR_lookup_dcookie 253 -#define TARGET_NR_epoll_create 254 -#define TARGET_NR_epoll_ctl 255 -#define TARGET_NR_epoll_wait 256 -#define TARGET_NR_remap_file_pages 257 -#define TARGET_NR_set_tid_address 258 -#define TARGET_NR_timer_create 259 -#define TARGET_NR_timer_settime (TARGET_NR_timer_create+1) -#define TARGET_NR_timer_gettime (TARGET_NR_timer_create+2) -#define TARGET_NR_timer_getoverrun (TARGET_NR_timer_create+3) -#define TARGET_NR_timer_delete (TARGET_NR_timer_create+4) -#define TARGET_NR_clock_settime (TARGET_NR_timer_create+5) -#define TARGET_NR_clock_gettime (TARGET_NR_timer_create+6) -#define TARGET_NR_clock_getres (TARGET_NR_timer_create+7) -#define TARGET_NR_clock_nanosleep (TARGET_NR_timer_create+8) -#define TARGET_NR_statfs64 268 -#define TARGET_NR_fstatfs64 269 -#define TARGET_NR_tgkill 270 -#define TARGET_NR_utimes 271 -#define TARGET_NR_fadvise64_64 272 -#define TARGET_NR_vserver 273 -#define TARGET_NR_mbind 274 -#define TARGET_NR_get_mempolicy 275 -#define TARGET_NR_set_mempolicy 276 -#define TARGET_NR_mq_open 277 -#define TARGET_NR_mq_unlink (TARGET_NR_mq_open+1) -#define TARGET_NR_mq_timedsend (TARGET_NR_mq_open+2) -#define TARGET_NR_mq_timedreceive (TARGET_NR_mq_open+3) -#define TARGET_NR_mq_notify (TARGET_NR_mq_open+4) -#define TARGET_NR_mq_getsetattr (TARGET_NR_mq_open+5) -#define TARGET_NR_kexec_load 283 -#define TARGET_NR_waitid 284 -/* #define TARGET_NR_sys_setaltroot 285 */ -#define TARGET_NR_add_key 286 -#define TARGET_NR_request_key 287 -#define TARGET_NR_keyctl 288 -#define TARGET_NR_ioprio_set 289 -#define TARGET_NR_ioprio_get 290 -#define TARGET_NR_inotify_init 291 -#define TARGET_NR_inotify_add_watch 292 -#define TARGET_NR_inotify_rm_watch 293 -#define TARGET_NR_migrate_pages 294 -#define TARGET_NR_openat 295 -#define TARGET_NR_mkdirat 296 -#define TARGET_NR_mknodat 297 -#define TARGET_NR_fchownat 298 -#define TARGET_NR_futimesat 299 -#define TARGET_NR_fstatat64 300 -#define TARGET_NR_unlinkat 301 -#define TARGET_NR_renameat 302 -#define TARGET_NR_linkat 303 -#define TARGET_NR_symlinkat 304 -#define TARGET_NR_readlinkat 305 -#define TARGET_NR_fchmodat 306 -#define TARGET_NR_faccessat 307 -#define TARGET_NR_pselect6 308 -#define TARGET_NR_ppoll 309 -#define TARGET_NR_unshare 310 -#define TARGET_NR_set_robust_list 311 -#define TARGET_NR_get_robust_list 312 -#define TARGET_NR_splice 313 -#define TARGET_NR_sync_file_range 314 -#define TARGET_NR_tee 315 -#define TARGET_NR_vmsplice 316 -#define TARGET_NR_move_pages 317 -#define TARGET_NR_getcpu 318 -#define TARGET_NR_epoll_pwait 319 -#define TARGET_NR_utimensat 320 -#define TARGET_NR_signalfd 321 -#define TARGET_NR_timerfd_create 322 -#define TARGET_NR_eventfd 323 -#define TARGET_NR_fallocate 324 -#define TARGET_NR_timerfd_settime 325 -#define TARGET_NR_timerfd_gettime 326 -#define TARGET_NR_signalfd4 327 -#define TARGET_NR_eventfd2 328 -#define TARGET_NR_epoll_create1 329 -#define TARGET_NR_dup3 330 -#define TARGET_NR_pipe2 331 -#define TARGET_NR_inotify_init1 332 -#define TARGET_NR_preadv 333 -#define TARGET_NR_pwritev 334 -#define TARGET_NR_rt_tgsigqueueinfo 335 -#define TARGET_NR_perf_event_open 336 -#define TARGET_NR_recvmmsg 337 -#define TARGET_NR_fanotify_init 338 -#define TARGET_NR_fanotify_mark 339 -#define TARGET_NR_prlimit64 340 -#define TARGET_NR_name_to_handle_at 341 -#define TARGET_NR_open_by_handle_at 342 -#define TARGET_NR_clock_adjtime 343 -#define TARGET_NR_syncfs 344 -#define TARGET_NR_sendmmsg 345 -#define TARGET_NR_setns 346 -#define TARGET_NR_process_vm_readv 347 -#define TARGET_NR_process_vm_writev 348 -#define TARGET_NR_kcmp 349 -#define TARGET_NR_finit_module 350 -#define TARGET_NR_sched_setattr 351 -#define TARGET_NR_sched_getattr 352 -#define TARGET_NR_renameat2 353 -#define TARGET_NR_seccomp 354 -#define TARGET_NR_getrandom 355 -#define TARGET_NR_memfd_create 356 -#define TARGET_NR_bpf 357 -#define TARGET_NR_execveat 358 -#define TARGET_NR_socket 359 -#define TARGET_NR_socketpair 360 -#define TARGET_NR_bind 361 -#define TARGET_NR_connect 362 -#define TARGET_NR_listen 363 -#define TARGET_NR_accept4 364 -#define TARGET_NR_getsockopt 365 -#define TARGET_NR_setsockopt 366 -#define TARGET_NR_getsockname 367 -#define TARGET_NR_getpeername 368 -#define TARGET_NR_sendto 369 -#define TARGET_NR_sendmsg 370 -#define TARGET_NR_recvfrom 371 -#define TARGET_NR_recvmsg 372 -#define TARGET_NR_shutdown 373 -#define TARGET_NR_userfaultfd 374 -#define TARGET_NR_membarrier 375 -#define TARGET_NR_mlock2 376 -#define TARGET_NR_copy_file_range 377 - -#endif diff --git a/linux-user/i386/syscallhdr.sh b/linux-user/i386/syscallhdr.sh new file mode 100644 index 0000000000..b2eca96db7 --- /dev/null +++ b/linux-user/i386/syscallhdr.sh @@ -0,0 +1,28 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 + +in="$1" +out="$2" +my_abis=`echo "($3)" | tr ',' '|'` +prefix="$4" +offset="$5" + +fileguard=LINUX_USER_I386_`basename "$out" | sed \ + -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/' \ + -e 's/[^A-Z0-9_]/_/g' -e 's/__/_/g'` +grep -E "^[0-9A-Fa-fXx]+[[:space:]]+${my_abis}" "$in" | sort -n | ( + echo "#ifndef ${fileguard}" + echo "#define ${fileguard} 1" + echo "" + + while read nr abi name entry ; do + if [ -z "$offset" ]; then + echo "#define TARGET_NR_${prefix}${name} $nr" + else + echo "#define TARGET_NR_${prefix}${name} ($offset + $nr)" + fi + done + + echo "" + echo "#endif /* ${fileguard} */" +) > "$out" diff --git a/linux-user/i386/target_cpu.h b/linux-user/i386/target_cpu.h index 0b44530854..2207d24c2e 100644 --- a/linux-user/i386/target_cpu.h +++ b/linux-user/i386/target_cpu.h @@ -33,6 +33,8 @@ static inline void cpu_clone_regs_parent(CPUX86State *env, unsigned flags) { } +abi_long do_arch_prctl(CPUX86State *env, int code, abi_ulong addr); + #if defined(TARGET_ABI32) abi_long do_set_thread_area(CPUX86State *env, abi_ulong ptr); @@ -42,8 +44,6 @@ static inline void cpu_set_tls(CPUX86State *env, target_ulong newtls) cpu_x86_load_seg(env, R_GS, env->segs[R_GS].selector); } #else -abi_long do_arch_prctl(CPUX86State *env, int code, abi_ulong addr); - static inline void cpu_set_tls(CPUX86State *env, target_ulong newtls) { do_arch_prctl(env, TARGET_ARCH_SET_FS, newtls); diff --git a/linux-user/ioctls.h b/linux-user/ioctls.h index c6b9d6ad66..0defa1d8c1 100644 --- a/linux-user/ioctls.h +++ b/linux-user/ioctls.h @@ -69,6 +69,29 @@ IOCTL(KDSETLED, 0, TYPE_INT) IOCTL_SPECIAL(KDSIGACCEPT, 0, do_ioctl_kdsigaccept, TYPE_INT) + IOCTL(RTC_AIE_ON, 0, TYPE_NULL) + IOCTL(RTC_AIE_OFF, 0, TYPE_NULL) + IOCTL(RTC_UIE_ON, 0, TYPE_NULL) + IOCTL(RTC_UIE_OFF, 0, TYPE_NULL) + IOCTL(RTC_PIE_ON, 0, TYPE_NULL) + IOCTL(RTC_PIE_OFF, 0, TYPE_NULL) + IOCTL(RTC_WIE_ON, 0, TYPE_NULL) + IOCTL(RTC_WIE_OFF, 0, TYPE_NULL) + IOCTL(RTC_ALM_READ, IOC_R, MK_PTR(MK_STRUCT(STRUCT_rtc_time))) + IOCTL(RTC_ALM_SET, IOC_W, MK_PTR(MK_STRUCT(STRUCT_rtc_time))) + IOCTL(RTC_RD_TIME, IOC_R, MK_PTR(MK_STRUCT(STRUCT_rtc_time))) + IOCTL(RTC_SET_TIME, IOC_W, MK_PTR(MK_STRUCT(STRUCT_rtc_time))) + IOCTL(RTC_IRQP_READ, IOC_R, MK_PTR(TYPE_ULONG)) + IOCTL(RTC_IRQP_SET, IOC_W, TYPE_ULONG) + IOCTL(RTC_EPOCH_READ, IOC_R, MK_PTR(TYPE_ULONG)) + IOCTL(RTC_EPOCH_SET, IOC_W, TYPE_ULONG) + IOCTL(RTC_WKALM_RD, IOC_R, MK_PTR(MK_STRUCT(STRUCT_rtc_wkalrm))) + IOCTL(RTC_WKALM_SET, IOC_W, MK_PTR(MK_STRUCT(STRUCT_rtc_wkalrm))) + IOCTL(RTC_PLL_GET, IOC_R, MK_PTR(MK_STRUCT(STRUCT_rtc_pll_info))) + IOCTL(RTC_PLL_SET, IOC_W, MK_PTR(MK_STRUCT(STRUCT_rtc_pll_info))) + IOCTL(RTC_VL_READ, IOC_R, MK_PTR(TYPE_INT)) + IOCTL(RTC_VL_CLR, 0, TYPE_NULL) + IOCTL(BLKROSET, IOC_W, MK_PTR(TYPE_INT)) IOCTL(BLKROGET, IOC_R, MK_PTR(TYPE_INT)) IOCTL(BLKRRPART, 0, TYPE_NULL) @@ -114,7 +137,13 @@ IOCTL(FDMSGON, 0, TYPE_NULL) IOCTL(FDMSGOFF, 0, TYPE_NULL) + IOCTL(FDSETEMSGTRESH, 0, TYPE_NULL) + IOCTL(FDFMTBEG, 0, TYPE_NULL) + IOCTL(FDFMTTRK, IOC_W, MK_PTR(MK_STRUCT(STRUCT_format_descr))) + IOCTL(FDFMTEND, 0, TYPE_NULL) IOCTL(FDFLUSH, 0, TYPE_NULL) + IOCTL(FDSETMAXERRS, IOC_W, MK_PTR(MK_STRUCT(STRUCT_floppy_max_errors))) + IOCTL(FDGETMAXERRS, IOC_R, MK_PTR(MK_STRUCT(STRUCT_floppy_max_errors))) IOCTL(FDRESET, 0, TYPE_NULL) IOCTL(FDRAWCMD, 0, TYPE_NULL) IOCTL(FDTWADDLE, 0, TYPE_NULL) @@ -138,6 +167,12 @@ IOCTL(FS_IOC_GETFLAGS, IOC_R, MK_PTR(TYPE_INT)) IOCTL(FS_IOC_SETFLAGS, IOC_W, MK_PTR(TYPE_INT)) + IOCTL(FS_IOC_GETVERSION, IOC_R, MK_PTR(TYPE_INT)) + IOCTL(FS_IOC_SETVERSION, IOC_W, MK_PTR(TYPE_INT)) + IOCTL(FS_IOC32_GETFLAGS, IOC_R, MK_PTR(TYPE_INT)) + IOCTL(FS_IOC32_SETFLAGS, IOC_W, MK_PTR(TYPE_INT)) + IOCTL(FS_IOC32_GETVERSION, IOC_R, MK_PTR(TYPE_INT)) + IOCTL(FS_IOC32_SETVERSION, IOC_W, MK_PTR(TYPE_INT)) #ifdef CONFIG_USBFS /* USB ioctls */ @@ -426,6 +461,27 @@ IOCTL(SOUND_MIXER_WRITE_LOUD, IOC_W, MK_PTR(TYPE_INT)) IOCTL(SOUND_MIXER_WRITE_RECSRC, IOC_W, MK_PTR(TYPE_INT)) + IOCTL(SNDRV_TIMER_IOCTL_PVERSION, IOC_R, MK_PTR(TYPE_INT)) + IOCTL(SNDRV_TIMER_IOCTL_NEXT_DEVICE, IOC_RW, + MK_PTR(MK_STRUCT(STRUCT_snd_timer_id))) + IOCTL(SNDRV_TIMER_IOCTL_GINFO, IOC_RW, + MK_PTR(MK_STRUCT(STRUCT_snd_timer_ginfo))) + IOCTL(SNDRV_TIMER_IOCTL_GPARAMS, IOC_W, + MK_PTR(MK_STRUCT(STRUCT_snd_timer_gparams))) + IOCTL(SNDRV_TIMER_IOCTL_GSTATUS, IOC_RW, + MK_PTR(MK_STRUCT(STRUCT_snd_timer_gstatus))) + IOCTL(SNDRV_TIMER_IOCTL_SELECT, IOC_W, + MK_PTR(MK_STRUCT(STRUCT_snd_timer_select))) + IOCTL(SNDRV_TIMER_IOCTL_INFO, IOC_R, MK_PTR(MK_STRUCT(STRUCT_snd_timer_info))) + IOCTL(SNDRV_TIMER_IOCTL_PARAMS, IOC_W, + MK_PTR(MK_STRUCT(STRUCT_snd_timer_params))) + IOCTL(SNDRV_TIMER_IOCTL_STATUS, IOC_R, + MK_PTR(MK_STRUCT(STRUCT_snd_timer_status))) + IOCTL(SNDRV_TIMER_IOCTL_START, 0, TYPE_NULL) + IOCTL(SNDRV_TIMER_IOCTL_STOP, 0, TYPE_NULL) + IOCTL(SNDRV_TIMER_IOCTL_CONTINUE, 0, TYPE_NULL) + IOCTL(SNDRV_TIMER_IOCTL_PAUSE, 0, TYPE_NULL) + IOCTL(HDIO_GETGEO, IOC_R, MK_PTR(MK_STRUCT(STRUCT_hd_geometry))) IOCTL(HDIO_GET_UNMASKINTR, IOC_R, MK_PTR(TYPE_INT)) IOCTL(HDIO_GET_MULTCOUNT, IOC_R, MK_PTR(TYPE_INT)) @@ -522,3 +578,9 @@ IOCTL_IGNORE(TIOCSTART) IOCTL_IGNORE(TIOCSTOP) #endif + +#ifdef CONFIG_KCOV + IOCTL(KCOV_ENABLE, 0, TYPE_NULL) + IOCTL(KCOV_DISABLE, 0, TYPE_NULL) + IOCTL(KCOV_INIT_TRACE, IOC_R, TYPE_ULONG) +#endif diff --git a/linux-user/m68k/Makefile.objs b/linux-user/m68k/Makefile.objs new file mode 100644 index 0000000000..961bd05c23 --- /dev/null +++ b/linux-user/m68k/Makefile.objs @@ -0,0 +1,5 @@ +generated-files-y += linux-user/m68k/syscall_nr.h + +syshdr := $(SRC_PATH)/linux-user/m68k/syscallhdr.sh +%/syscall_nr.h: $(SRC_PATH)/linux-user/m68k/syscall.tbl $(syshdr) + $(call quiet-command, sh $(syshdr) $< $@ $(TARGET_SYSTBL_ABI),"GEN","$@") diff --git a/linux-user/m68k/syscall.tbl b/linux-user/m68k/syscall.tbl new file mode 100644 index 0000000000..f4f49fcb76 --- /dev/null +++ b/linux-user/m68k/syscall.tbl @@ -0,0 +1,439 @@ +# SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note +# +# system call numbers and entry vectors for m68k +# +# The format is: +# +# +# The is always "common" for this file +# +0 common restart_syscall sys_restart_syscall +1 common exit sys_exit +2 common fork __sys_fork +3 common read sys_read +4 common write sys_write +5 common open sys_open +6 common close sys_close +7 common waitpid sys_waitpid +8 common creat sys_creat +9 common link sys_link +10 common unlink sys_unlink +11 common execve sys_execve +12 common chdir sys_chdir +13 common time sys_time32 +14 common mknod sys_mknod +15 common chmod sys_chmod +16 common chown sys_chown16 +# 17 was break +18 common oldstat sys_stat +19 common lseek sys_lseek +20 common getpid sys_getpid +21 common mount sys_mount +22 common umount sys_oldumount +23 common setuid sys_setuid16 +24 common getuid sys_getuid16 +25 common stime sys_stime32 +26 common ptrace sys_ptrace +27 common alarm sys_alarm +28 common oldfstat sys_fstat +29 common pause sys_pause +30 common utime sys_utime32 +# 31 was stty +# 32 was gtty +33 common access sys_access +34 common nice sys_nice +# 35 was ftime +36 common sync sys_sync +37 common kill sys_kill +38 common rename sys_rename +39 common mkdir sys_mkdir +40 common rmdir sys_rmdir +41 common dup sys_dup +42 common pipe sys_pipe +43 common times sys_times +# 44 was prof +45 common brk sys_brk +46 common setgid sys_setgid16 +47 common getgid sys_getgid16 +48 common signal sys_signal +49 common geteuid sys_geteuid16 +50 common getegid sys_getegid16 +51 common acct sys_acct +52 common umount2 sys_umount +# 53 was lock +54 common ioctl sys_ioctl +55 common fcntl sys_fcntl +# 56 was mpx +57 common setpgid sys_setpgid +# 58 was ulimit +# 59 was oldolduname +60 common umask sys_umask +61 common chroot sys_chroot +62 common ustat sys_ustat +63 common dup2 sys_dup2 +64 common getppid sys_getppid +65 common getpgrp sys_getpgrp +66 common setsid sys_setsid +67 common sigaction sys_sigaction +68 common sgetmask sys_sgetmask +69 common ssetmask sys_ssetmask +70 common setreuid sys_setreuid16 +71 common setregid sys_setregid16 +72 common sigsuspend sys_sigsuspend +73 common sigpending sys_sigpending +74 common sethostname sys_sethostname +75 common setrlimit sys_setrlimit +76 common getrlimit sys_old_getrlimit +77 common getrusage sys_getrusage +78 common gettimeofday sys_gettimeofday +79 common settimeofday sys_settimeofday +80 common getgroups sys_getgroups16 +81 common setgroups sys_setgroups16 +82 common select sys_old_select +83 common symlink sys_symlink +84 common oldlstat sys_lstat +85 common readlink sys_readlink +86 common uselib sys_uselib +87 common swapon sys_swapon +88 common reboot sys_reboot +89 common readdir sys_old_readdir +90 common mmap sys_old_mmap +91 common munmap sys_munmap +92 common truncate sys_truncate +93 common ftruncate sys_ftruncate +94 common fchmod sys_fchmod +95 common fchown sys_fchown16 +96 common getpriority sys_getpriority +97 common setpriority sys_setpriority +# 98 was profil +99 common statfs sys_statfs +100 common fstatfs sys_fstatfs +# 101 was ioperm +102 common socketcall sys_socketcall +103 common syslog sys_syslog +104 common setitimer sys_setitimer +105 common getitimer sys_getitimer +106 common stat sys_newstat +107 common lstat sys_newlstat +108 common fstat sys_newfstat +# 109 was olduname +# 110 was iopl +111 common vhangup sys_vhangup +# 112 was idle +# 113 was vm86 +114 common wait4 sys_wait4 +115 common swapoff sys_swapoff +116 common sysinfo sys_sysinfo +117 common ipc sys_ipc +118 common fsync sys_fsync +119 common sigreturn sys_sigreturn +120 common clone __sys_clone +121 common setdomainname sys_setdomainname +122 common uname sys_newuname +123 common cacheflush sys_cacheflush +124 common adjtimex sys_adjtimex_time32 +125 common mprotect sys_mprotect +126 common sigprocmask sys_sigprocmask +127 common create_module sys_ni_syscall +128 common init_module sys_init_module +129 common delete_module sys_delete_module +130 common get_kernel_syms sys_ni_syscall +131 common quotactl sys_quotactl +132 common getpgid sys_getpgid +133 common fchdir sys_fchdir +134 common bdflush sys_bdflush +135 common sysfs sys_sysfs +136 common personality sys_personality +# 137 was afs_syscall +138 common setfsuid sys_setfsuid16 +139 common setfsgid sys_setfsgid16 +140 common _llseek sys_llseek +141 common getdents sys_getdents +142 common _newselect sys_select +143 common flock sys_flock +144 common msync sys_msync +145 common readv sys_readv +146 common writev sys_writev +147 common getsid sys_getsid +148 common fdatasync sys_fdatasync +149 common _sysctl sys_sysctl +150 common mlock sys_mlock +151 common munlock sys_munlock +152 common mlockall sys_mlockall +153 common munlockall sys_munlockall +154 common sched_setparam sys_sched_setparam +155 common sched_getparam sys_sched_getparam +156 common sched_setscheduler sys_sched_setscheduler +157 common sched_getscheduler sys_sched_getscheduler +158 common sched_yield sys_sched_yield +159 common sched_get_priority_max sys_sched_get_priority_max +160 common sched_get_priority_min sys_sched_get_priority_min +161 common sched_rr_get_interval sys_sched_rr_get_interval_time32 +162 common nanosleep sys_nanosleep_time32 +163 common mremap sys_mremap +164 common setresuid sys_setresuid16 +165 common getresuid sys_getresuid16 +166 common getpagesize sys_getpagesize +167 common query_module sys_ni_syscall +168 common poll sys_poll +169 common nfsservctl sys_ni_syscall +170 common setresgid sys_setresgid16 +171 common getresgid sys_getresgid16 +172 common prctl sys_prctl +173 common rt_sigreturn sys_rt_sigreturn +174 common rt_sigaction sys_rt_sigaction +175 common rt_sigprocmask sys_rt_sigprocmask +176 common rt_sigpending sys_rt_sigpending +177 common rt_sigtimedwait sys_rt_sigtimedwait_time32 +178 common rt_sigqueueinfo sys_rt_sigqueueinfo +179 common rt_sigsuspend sys_rt_sigsuspend +180 common pread64 sys_pread64 +181 common pwrite64 sys_pwrite64 +182 common lchown sys_lchown16 +183 common getcwd sys_getcwd +184 common capget sys_capget +185 common capset sys_capset +186 common sigaltstack sys_sigaltstack +187 common sendfile sys_sendfile +188 common getpmsg sys_ni_syscall +189 common putpmsg sys_ni_syscall +190 common vfork __sys_vfork +191 common ugetrlimit sys_getrlimit +192 common mmap2 sys_mmap2 +193 common truncate64 sys_truncate64 +194 common ftruncate64 sys_ftruncate64 +195 common stat64 sys_stat64 +196 common lstat64 sys_lstat64 +197 common fstat64 sys_fstat64 +198 common chown32 sys_chown +199 common getuid32 sys_getuid +200 common getgid32 sys_getgid +201 common geteuid32 sys_geteuid +202 common getegid32 sys_getegid +203 common setreuid32 sys_setreuid +204 common setregid32 sys_setregid +205 common getgroups32 sys_getgroups +206 common setgroups32 sys_setgroups +207 common fchown32 sys_fchown +208 common setresuid32 sys_setresuid +209 common getresuid32 sys_getresuid +210 common setresgid32 sys_setresgid +211 common getresgid32 sys_getresgid +212 common lchown32 sys_lchown +213 common setuid32 sys_setuid +214 common setgid32 sys_setgid +215 common setfsuid32 sys_setfsuid +216 common setfsgid32 sys_setfsgid +217 common pivot_root sys_pivot_root +# 218 is reserved +# 219 is reserved +220 common getdents64 sys_getdents64 +221 common gettid sys_gettid +222 common tkill sys_tkill +223 common setxattr sys_setxattr +224 common lsetxattr sys_lsetxattr +225 common fsetxattr sys_fsetxattr +226 common getxattr sys_getxattr +227 common lgetxattr sys_lgetxattr +228 common fgetxattr sys_fgetxattr +229 common listxattr sys_listxattr +230 common llistxattr sys_llistxattr +231 common flistxattr sys_flistxattr +232 common removexattr sys_removexattr +233 common lremovexattr sys_lremovexattr +234 common fremovexattr sys_fremovexattr +235 common futex sys_futex_time32 +236 common sendfile64 sys_sendfile64 +237 common mincore sys_mincore +238 common madvise sys_madvise +239 common fcntl64 sys_fcntl64 +240 common readahead sys_readahead +241 common io_setup sys_io_setup +242 common io_destroy sys_io_destroy +243 common io_getevents sys_io_getevents_time32 +244 common io_submit sys_io_submit +245 common io_cancel sys_io_cancel +246 common fadvise64 sys_fadvise64 +247 common exit_group sys_exit_group +248 common lookup_dcookie sys_lookup_dcookie +249 common epoll_create sys_epoll_create +250 common epoll_ctl sys_epoll_ctl +251 common epoll_wait sys_epoll_wait +252 common remap_file_pages sys_remap_file_pages +253 common set_tid_address sys_set_tid_address +254 common timer_create sys_timer_create +255 common timer_settime sys_timer_settime32 +256 common timer_gettime sys_timer_gettime32 +257 common timer_getoverrun sys_timer_getoverrun +258 common timer_delete sys_timer_delete +259 common clock_settime sys_clock_settime32 +260 common clock_gettime sys_clock_gettime32 +261 common clock_getres sys_clock_getres_time32 +262 common clock_nanosleep sys_clock_nanosleep_time32 +263 common statfs64 sys_statfs64 +264 common fstatfs64 sys_fstatfs64 +265 common tgkill sys_tgkill +266 common utimes sys_utimes_time32 +267 common fadvise64_64 sys_fadvise64_64 +268 common mbind sys_mbind +269 common get_mempolicy sys_get_mempolicy +270 common set_mempolicy sys_set_mempolicy +271 common mq_open sys_mq_open +272 common mq_unlink sys_mq_unlink +273 common mq_timedsend sys_mq_timedsend_time32 +274 common mq_timedreceive sys_mq_timedreceive_time32 +275 common mq_notify sys_mq_notify +276 common mq_getsetattr sys_mq_getsetattr +277 common waitid sys_waitid +# 278 was vserver +279 common add_key sys_add_key +280 common request_key sys_request_key +281 common keyctl sys_keyctl +282 common ioprio_set sys_ioprio_set +283 common ioprio_get sys_ioprio_get +284 common inotify_init sys_inotify_init +285 common inotify_add_watch sys_inotify_add_watch +286 common inotify_rm_watch sys_inotify_rm_watch +287 common migrate_pages sys_migrate_pages +288 common openat sys_openat +289 common mkdirat sys_mkdirat +290 common mknodat sys_mknodat +291 common fchownat sys_fchownat +292 common futimesat sys_futimesat_time32 +293 common fstatat64 sys_fstatat64 +294 common unlinkat sys_unlinkat +295 common renameat sys_renameat +296 common linkat sys_linkat +297 common symlinkat sys_symlinkat +298 common readlinkat sys_readlinkat +299 common fchmodat sys_fchmodat +300 common faccessat sys_faccessat +301 common pselect6 sys_pselect6_time32 +302 common ppoll sys_ppoll_time32 +303 common unshare sys_unshare +304 common set_robust_list sys_set_robust_list +305 common get_robust_list sys_get_robust_list +306 common splice sys_splice +307 common sync_file_range sys_sync_file_range +308 common tee sys_tee +309 common vmsplice sys_vmsplice +310 common move_pages sys_move_pages +311 common sched_setaffinity sys_sched_setaffinity +312 common sched_getaffinity sys_sched_getaffinity +313 common kexec_load sys_kexec_load +314 common getcpu sys_getcpu +315 common epoll_pwait sys_epoll_pwait +316 common utimensat sys_utimensat_time32 +317 common signalfd sys_signalfd +318 common timerfd_create sys_timerfd_create +319 common eventfd sys_eventfd +320 common fallocate sys_fallocate +321 common timerfd_settime sys_timerfd_settime32 +322 common timerfd_gettime sys_timerfd_gettime32 +323 common signalfd4 sys_signalfd4 +324 common eventfd2 sys_eventfd2 +325 common epoll_create1 sys_epoll_create1 +326 common dup3 sys_dup3 +327 common pipe2 sys_pipe2 +328 common inotify_init1 sys_inotify_init1 +329 common preadv sys_preadv +330 common pwritev sys_pwritev +331 common rt_tgsigqueueinfo sys_rt_tgsigqueueinfo +332 common perf_event_open sys_perf_event_open +333 common get_thread_area sys_get_thread_area +334 common set_thread_area sys_set_thread_area +335 common atomic_cmpxchg_32 sys_atomic_cmpxchg_32 +336 common atomic_barrier sys_atomic_barrier +337 common fanotify_init sys_fanotify_init +338 common fanotify_mark sys_fanotify_mark +339 common prlimit64 sys_prlimit64 +340 common name_to_handle_at sys_name_to_handle_at +341 common open_by_handle_at sys_open_by_handle_at +342 common clock_adjtime sys_clock_adjtime32 +343 common syncfs sys_syncfs +344 common setns sys_setns +345 common process_vm_readv sys_process_vm_readv +346 common process_vm_writev sys_process_vm_writev +347 common kcmp sys_kcmp +348 common finit_module sys_finit_module +349 common sched_setattr sys_sched_setattr +350 common sched_getattr sys_sched_getattr +351 common renameat2 sys_renameat2 +352 common getrandom sys_getrandom +353 common memfd_create sys_memfd_create +354 common bpf sys_bpf +355 common execveat sys_execveat +356 common socket sys_socket +357 common socketpair sys_socketpair +358 common bind sys_bind +359 common connect sys_connect +360 common listen sys_listen +361 common accept4 sys_accept4 +362 common getsockopt sys_getsockopt +363 common setsockopt sys_setsockopt +364 common getsockname sys_getsockname +365 common getpeername sys_getpeername +366 common sendto sys_sendto +367 common sendmsg sys_sendmsg +368 common recvfrom sys_recvfrom +369 common recvmsg sys_recvmsg +370 common shutdown sys_shutdown +371 common recvmmsg sys_recvmmsg_time32 +372 common sendmmsg sys_sendmmsg +373 common userfaultfd sys_userfaultfd +374 common membarrier sys_membarrier +375 common mlock2 sys_mlock2 +376 common copy_file_range sys_copy_file_range +377 common preadv2 sys_preadv2 +378 common pwritev2 sys_pwritev2 +379 common statx sys_statx +380 common seccomp sys_seccomp +381 common pkey_mprotect sys_pkey_mprotect +382 common pkey_alloc sys_pkey_alloc +383 common pkey_free sys_pkey_free +384 common rseq sys_rseq +# room for arch specific calls +393 common semget sys_semget +394 common semctl sys_semctl +395 common shmget sys_shmget +396 common shmctl sys_shmctl +397 common shmat sys_shmat +398 common shmdt sys_shmdt +399 common msgget sys_msgget +400 common msgsnd sys_msgsnd +401 common msgrcv sys_msgrcv +402 common msgctl sys_msgctl +403 common clock_gettime64 sys_clock_gettime +404 common clock_settime64 sys_clock_settime +405 common clock_adjtime64 sys_clock_adjtime +406 common clock_getres_time64 sys_clock_getres +407 common clock_nanosleep_time64 sys_clock_nanosleep +408 common timer_gettime64 sys_timer_gettime +409 common timer_settime64 sys_timer_settime +410 common timerfd_gettime64 sys_timerfd_gettime +411 common timerfd_settime64 sys_timerfd_settime +412 common utimensat_time64 sys_utimensat +413 common pselect6_time64 sys_pselect6 +414 common ppoll_time64 sys_ppoll +416 common io_pgetevents_time64 sys_io_pgetevents +417 common recvmmsg_time64 sys_recvmmsg +418 common mq_timedsend_time64 sys_mq_timedsend +419 common mq_timedreceive_time64 sys_mq_timedreceive +420 common semtimedop_time64 sys_semtimedop +421 common rt_sigtimedwait_time64 sys_rt_sigtimedwait +422 common futex_time64 sys_futex +423 common sched_rr_get_interval_time64 sys_sched_rr_get_interval +424 common pidfd_send_signal sys_pidfd_send_signal +425 common io_uring_setup sys_io_uring_setup +426 common io_uring_enter sys_io_uring_enter +427 common io_uring_register sys_io_uring_register +428 common open_tree sys_open_tree +429 common move_mount sys_move_mount +430 common fsopen sys_fsopen +431 common fsconfig sys_fsconfig +432 common fsmount sys_fsmount +433 common fspick sys_fspick +434 common pidfd_open sys_pidfd_open +435 common clone3 __sys_clone3 +437 common openat2 sys_openat2 +438 common pidfd_getfd sys_pidfd_getfd diff --git a/linux-user/m68k/syscall_nr.h b/linux-user/m68k/syscall_nr.h deleted file mode 100644 index d33d8e98a7..0000000000 --- a/linux-user/m68k/syscall_nr.h +++ /dev/null @@ -1,386 +0,0 @@ -/* - * This file contains the system call numbers. - */ - -#ifndef LINUX_USER_M68K_SYSCALL_NR_H -#define LINUX_USER_M68K_SYSCALL_NR_H - -#define TARGET_NR_exit 1 -#define TARGET_NR_fork 2 -#define TARGET_NR_read 3 -#define TARGET_NR_write 4 -#define TARGET_NR_open 5 -#define TARGET_NR_close 6 -#define TARGET_NR_waitpid 7 -#define TARGET_NR_creat 8 -#define TARGET_NR_link 9 -#define TARGET_NR_unlink 10 -#define TARGET_NR_execve 11 -#define TARGET_NR_chdir 12 -#define TARGET_NR_time 13 -#define TARGET_NR_mknod 14 -#define TARGET_NR_chmod 15 -#define TARGET_NR_chown 16 -#define TARGET_NR_break 17 -#define TARGET_NR_oldstat 18 -#define TARGET_NR_lseek 19 -#define TARGET_NR_getpid 20 -#define TARGET_NR_mount 21 -#define TARGET_NR_umount 22 -#define TARGET_NR_setuid 23 -#define TARGET_NR_getuid 24 -#define TARGET_NR_stime 25 -#define TARGET_NR_ptrace 26 -#define TARGET_NR_alarm 27 -#define TARGET_NR_oldfstat 28 -#define TARGET_NR_pause 29 -#define TARGET_NR_utime 30 -#define TARGET_NR_stty 31 -#define TARGET_NR_gtty 32 -#define TARGET_NR_access 33 -#define TARGET_NR_nice 34 -#define TARGET_NR_ftime 35 -#define TARGET_NR_sync 36 -#define TARGET_NR_kill 37 -#define TARGET_NR_rename 38 -#define TARGET_NR_mkdir 39 -#define TARGET_NR_rmdir 40 -#define TARGET_NR_dup 41 -#define TARGET_NR_pipe 42 -#define TARGET_NR_times 43 -#define TARGET_NR_prof 44 -#define TARGET_NR_brk 45 -#define TARGET_NR_setgid 46 -#define TARGET_NR_getgid 47 -#define TARGET_NR_signal 48 -#define TARGET_NR_geteuid 49 -#define TARGET_NR_getegid 50 -#define TARGET_NR_acct 51 -#define TARGET_NR_umount2 52 -#define TARGET_NR_lock 53 -#define TARGET_NR_ioctl 54 -#define TARGET_NR_fcntl 55 -#define TARGET_NR_mpx 56 -#define TARGET_NR_setpgid 57 -#define TARGET_NR_ulimit 58 -#define TARGET_NR_oldolduname 59 -#define TARGET_NR_umask 60 -#define TARGET_NR_chroot 61 -#define TARGET_NR_ustat 62 -#define TARGET_NR_dup2 63 -#define TARGET_NR_getppid 64 -#define TARGET_NR_getpgrp 65 -#define TARGET_NR_setsid 66 -#define TARGET_NR_sigaction 67 -#define TARGET_NR_sgetmask 68 -#define TARGET_NR_ssetmask 69 -#define TARGET_NR_setreuid 70 -#define TARGET_NR_setregid 71 -#define TARGET_NR_sigsuspend 72 -#define TARGET_NR_sigpending 73 -#define TARGET_NR_sethostname 74 -#define TARGET_NR_setrlimit 75 -#define TARGET_NR_getrlimit 76 -#define TARGET_NR_getrusage 77 -#define TARGET_NR_gettimeofday 78 -#define TARGET_NR_settimeofday 79 -#define TARGET_NR_getgroups 80 -#define TARGET_NR_setgroups 81 -#define TARGET_NR_select 82 -#define TARGET_NR_symlink 83 -#define TARGET_NR_oldlstat 84 -#define TARGET_NR_readlink 85 -#define TARGET_NR_uselib 86 -#define TARGET_NR_swapon 87 -#define TARGET_NR_reboot 88 -#define TARGET_NR_readdir 89 -#define TARGET_NR_mmap 90 -#define TARGET_NR_munmap 91 -#define TARGET_NR_truncate 92 -#define TARGET_NR_ftruncate 93 -#define TARGET_NR_fchmod 94 -#define TARGET_NR_fchown 95 -#define TARGET_NR_getpriority 96 -#define TARGET_NR_setpriority 97 -#define TARGET_NR_profil 98 -#define TARGET_NR_statfs 99 -#define TARGET_NR_fstatfs 100 -#define TARGET_NR_ioperm 101 -#define TARGET_NR_socketcall 102 -#define TARGET_NR_syslog 103 -#define TARGET_NR_setitimer 104 -#define TARGET_NR_getitimer 105 -#define TARGET_NR_stat 106 -#define TARGET_NR_lstat 107 -#define TARGET_NR_fstat 108 -#define TARGET_NR_olduname 109 -//#define TARGET_NR_iopl /* 110 */ not supported -#define TARGET_NR_vhangup 111 -//#define TARGET_NR_idle /* 112 */ Obsolete -//#define TARGET_NR_vm86 /* 113 */ not supported -#define TARGET_NR_wait4 114 -#define TARGET_NR_swapoff 115 -#define TARGET_NR_sysinfo 116 -#define TARGET_NR_ipc 117 -#define TARGET_NR_fsync 118 -#define TARGET_NR_sigreturn 119 -#define TARGET_NR_clone 120 -#define TARGET_NR_setdomainname 121 -#define TARGET_NR_uname 122 -#define TARGET_NR_cacheflush 123 -#define TARGET_NR_adjtimex 124 -#define TARGET_NR_mprotect 125 -#define TARGET_NR_sigprocmask 126 -#define TARGET_NR_create_module 127 -#define TARGET_NR_init_module 128 -#define TARGET_NR_delete_module 129 -#define TARGET_NR_get_kernel_syms 130 -#define TARGET_NR_quotactl 131 -#define TARGET_NR_getpgid 132 -#define TARGET_NR_fchdir 133 -#define TARGET_NR_bdflush 134 -#define TARGET_NR_sysfs 135 -#define TARGET_NR_personality 136 -#define TARGET_NR_afs_syscall 137 /* Syscall for Andrew File System */ -#define TARGET_NR_setfsuid 138 -#define TARGET_NR_setfsgid 139 -#define TARGET_NR__llseek 140 -#define TARGET_NR_getdents 141 -#define TARGET_NR__newselect 142 -#define TARGET_NR_flock 143 -#define TARGET_NR_msync 144 -#define TARGET_NR_readv 145 -#define TARGET_NR_writev 146 -#define TARGET_NR_getsid 147 -#define TARGET_NR_fdatasync 148 -#define TARGET_NR__sysctl 149 -#define TARGET_NR_mlock 150 -#define TARGET_NR_munlock 151 -#define TARGET_NR_mlockall 152 -#define TARGET_NR_munlockall 153 -#define TARGET_NR_sched_setparam 154 -#define TARGET_NR_sched_getparam 155 -#define TARGET_NR_sched_setscheduler 156 -#define TARGET_NR_sched_getscheduler 157 -#define TARGET_NR_sched_yield 158 -#define TARGET_NR_sched_get_priority_max 159 -#define TARGET_NR_sched_get_priority_min 160 -#define TARGET_NR_sched_rr_get_interval 161 -#define TARGET_NR_nanosleep 162 -#define TARGET_NR_mremap 163 -#define TARGET_NR_setresuid 164 -#define TARGET_NR_getresuid 165 -#define TARGET_NR_getpagesize 166 -#define TARGET_NR_query_module 167 -#define TARGET_NR_poll 168 -#define TARGET_NR_nfsservctl 169 -#define TARGET_NR_setresgid 170 -#define TARGET_NR_getresgid 171 -#define TARGET_NR_prctl 172 -#define TARGET_NR_rt_sigreturn 173 -#define TARGET_NR_rt_sigaction 174 -#define TARGET_NR_rt_sigprocmask 175 -#define TARGET_NR_rt_sigpending 176 -#define TARGET_NR_rt_sigtimedwait 177 -#define TARGET_NR_rt_sigqueueinfo 178 -#define TARGET_NR_rt_sigsuspend 179 -#define TARGET_NR_pread64 180 -#define TARGET_NR_pwrite64 181 -#define TARGET_NR_lchown 182 -#define TARGET_NR_getcwd 183 -#define TARGET_NR_capget 184 -#define TARGET_NR_capset 185 -#define TARGET_NR_sigaltstack 186 -#define TARGET_NR_sendfile 187 -#define TARGET_NR_getpmsg 188 /* some people actually want streams */ -#define TARGET_NR_putpmsg 189 /* some people actually want streams */ -#define TARGET_NR_vfork 190 -#define TARGET_NR_ugetrlimit 191 -#define TARGET_NR_mmap2 192 -#define TARGET_NR_truncate64 193 -#define TARGET_NR_ftruncate64 194 -#define TARGET_NR_stat64 195 -#define TARGET_NR_lstat64 196 -#define TARGET_NR_fstat64 197 -#define TARGET_NR_chown32 198 -#define TARGET_NR_getuid32 199 -#define TARGET_NR_getgid32 200 -#define TARGET_NR_geteuid32 201 -#define TARGET_NR_getegid32 202 -#define TARGET_NR_setreuid32 203 -#define TARGET_NR_setregid32 204 -#define TARGET_NR_getgroups32 205 -#define TARGET_NR_setgroups32 206 -#define TARGET_NR_fchown32 207 -#define TARGET_NR_setresuid32 208 -#define TARGET_NR_getresuid32 209 -#define TARGET_NR_setresgid32 210 -#define TARGET_NR_getresgid32 211 -#define TARGET_NR_lchown32 212 -#define TARGET_NR_setuid32 213 -#define TARGET_NR_setgid32 214 -#define TARGET_NR_setfsuid32 215 -#define TARGET_NR_setfsgid32 216 -#define TARGET_NR_pivot_root 217 -#define TARGET_NR_getdents64 220 -#define TARGET_NR_gettid 221 -#define TARGET_NR_tkill 222 -#define TARGET_NR_setxattr 223 -#define TARGET_NR_lsetxattr 224 -#define TARGET_NR_fsetxattr 225 -#define TARGET_NR_getxattr 226 -#define TARGET_NR_lgetxattr 227 -#define TARGET_NR_fgetxattr 228 -#define TARGET_NR_listxattr 229 -#define TARGET_NR_llistxattr 230 -#define TARGET_NR_flistxattr 231 -#define TARGET_NR_removexattr 232 -#define TARGET_NR_lremovexattr 233 -#define TARGET_NR_fremovexattr 234 -#define TARGET_NR_futex 235 -#define TARGET_NR_sendfile64 236 -#define TARGET_NR_mincore 237 -#define TARGET_NR_madvise 238 -#define TARGET_NR_fcntl64 239 -#define TARGET_NR_readahead 240 -#define TARGET_NR_io_setup 241 -#define TARGET_NR_io_destroy 242 -#define TARGET_NR_io_getevents 243 -#define TARGET_NR_io_submit 244 -#define TARGET_NR_io_cancel 245 -#define TARGET_NR_fadvise64 246 -#define TARGET_NR_exit_group 247 -#define TARGET_NR_lookup_dcookie 248 -#define TARGET_NR_epoll_create 249 -#define TARGET_NR_epoll_ctl 250 -#define TARGET_NR_epoll_wait 251 -#define TARGET_NR_remap_file_pages 252 -#define TARGET_NR_set_tid_address 253 -#define TARGET_NR_timer_create 254 -#define TARGET_NR_timer_settime 255 -#define TARGET_NR_timer_gettime 256 -#define TARGET_NR_timer_getoverrun 257 -#define TARGET_NR_timer_delete 258 -#define TARGET_NR_clock_settime 259 -#define TARGET_NR_clock_gettime 260 -#define TARGET_NR_clock_getres 261 -#define TARGET_NR_clock_nanosleep 262 -#define TARGET_NR_statfs64 263 -#define TARGET_NR_fstatfs64 264 -#define TARGET_NR_tgkill 265 -#define TARGET_NR_utimes 266 -#define TARGET_NR_fadvise64_64 267 -#define TARGET_NR_mbind 268 -#define TARGET_NR_get_mempolicy 269 -#define TARGET_NR_set_mempolicy 270 -#define TARGET_NR_mq_open 271 -#define TARGET_NR_mq_unlink 272 -#define TARGET_NR_mq_timedsend 273 -#define TARGET_NR_mq_timedreceive 274 -#define TARGET_NR_mq_notify 275 -#define TARGET_NR_mq_getsetattr 276 -#define TARGET_NR_waitid 277 -#define TARGET_NR_vserver 278 -#define TARGET_NR_add_key 279 -#define TARGET_NR_request_key 280 -#define TARGET_NR_keyctl 281 -#define TARGET_NR_ioprio_set 282 -#define TARGET_NR_ioprio_get 283 -#define TARGET_NR_inotify_init 284 -#define TARGET_NR_inotify_add_watch 285 -#define TARGET_NR_inotify_rm_watch 286 -#define TARGET_NR_migrate_pages 287 -#define TARGET_NR_openat 288 -#define TARGET_NR_mkdirat 289 -#define TARGET_NR_mknodat 290 -#define TARGET_NR_fchownat 291 -#define TARGET_NR_futimesat 292 -#define TARGET_NR_fstatat64 293 -#define TARGET_NR_unlinkat 294 -#define TARGET_NR_renameat 295 -#define TARGET_NR_linkat 296 -#define TARGET_NR_symlinkat 297 -#define TARGET_NR_readlinkat 298 -#define TARGET_NR_fchmodat 299 -#define TARGET_NR_faccessat 300 -#define TARGET_NR_pselect6 301 -#define TARGET_NR_ppoll 302 -#define TARGET_NR_unshare 303 -#define TARGET_NR_set_robust_list 304 -#define TARGET_NR_get_robust_list 305 -#define TARGET_NR_splice 306 -#define TARGET_NR_sync_file_range 307 -#define TARGET_NR_tee 308 -#define TARGET_NR_vmsplice 309 -#define TARGET_NR_move_pages 310 -#define TARGET_NR_sched_setaffinity 311 -#define TARGET_NR_sched_getaffinity 312 -#define TARGET_NR_kexec_load 313 -#define TARGET_NR_getcpu 314 -#define TARGET_NR_epoll_pwait 315 -#define TARGET_NR_utimensat 316 -#define TARGET_NR_signalfd 317 -#define TARGET_NR_timerfd_create 318 -#define TARGET_NR_eventfd 319 -#define TARGET_NR_fallocate 320 -#define TARGET_NR_timerfd_settime 321 -#define TARGET_NR_timerfd_gettime 322 -#define TARGET_NR_signalfd4 323 -#define TARGET_NR_eventfd2 324 -#define TARGET_NR_epoll_create1 325 -#define TARGET_NR_dup3 326 -#define TARGET_NR_pipe2 327 -#define TARGET_NR_inotify_init1 328 -#define TARGET_NR_inotify_init1 328 -#define TARGET_NR_preadv 329 -#define TARGET_NR_pwritev 330 -#define TARGET_NR_rt_tgsigqueueinfo 331 -#define TARGET_NR_perf_event_open 332 -#define TARGET_NR_get_thread_area 333 -#define TARGET_NR_set_thread_area 334 -#define TARGET_NR_atomic_cmpxchg_32 335 -#define TARGET_NR_atomic_barrier 336 -#define TARGET_NR_fanotify_init 337 -#define TARGET_NR_fanotify_mark 338 -#define TARGET_NR_prlimit64 339 -#define TARGET_NR_name_to_handle_at 340 -#define TARGET_NR_open_by_handle_at 341 -#define TARGET_NR_clock_adjtime 342 -#define TARGET_NR_syncfs 343 -#define TARGET_NR_setns 344 -#define TARGET_NR_process_vm_readv 345 -#define TARGET_NR_process_vm_writev 346 -#define TARGET_NR_kcmp 347 -#define TARGET_NR_finit_module 348 -#define TARGET_NR_sched_setattr 349 -#define TARGET_NR_sched_getattr 350 -#define TARGET_NR_renameat2 351 -#define TARGET_NR_getrandom 352 -#define TARGET_NR_memfd_create 353 -#define TARGET_NR_bpf 354 -#define TARGET_NR_execveat 355 -#define TARGET_NR_socket 356 -#define TARGET_NR_socketpair 357 -#define TARGET_NR_bind 358 -#define TARGET_NR_connect 359 -#define TARGET_NR_listen 360 -#define TARGET_NR_accept4 361 -#define TARGET_NR_getsockopt 362 -#define TARGET_NR_setsockopt 363 -#define TARGET_NR_getsockname 364 -#define TARGET_NR_getpeername 365 -#define TARGET_NR_sendto 366 -#define TARGET_NR_sendmsg 367 -#define TARGET_NR_recvfrom 368 -#define TARGET_NR_recvmsg 369 -#define TARGET_NR_shutdown 370 -#define TARGET_NR_recvmmsg 371 -#define TARGET_NR_sendmmsg 372 -#define TARGET_NR_userfaultfd 373 -#define TARGET_NR_membarrier 374 -#define TARGET_NR_mlock2 375 -#define TARGET_NR_copy_file_range 376 -#define TARGET_NR_preadv2 377 -#define TARGET_NR_pwritev2 378 - -#endif diff --git a/linux-user/m68k/syscallhdr.sh b/linux-user/m68k/syscallhdr.sh new file mode 100644 index 0000000000..eeb4d01d34 --- /dev/null +++ b/linux-user/m68k/syscallhdr.sh @@ -0,0 +1,32 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 + +in="$1" +out="$2" +my_abis=`echo "($3)" | tr ',' '|'` +prefix="$4" +offset="$5" + +fileguard=LINUX_USER_M68K_`basename "$out" | sed \ + -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/' \ + -e 's/[^A-Z0-9_]/_/g' -e 's/__/_/g'` +grep -E "^[0-9A-Fa-fXx]+[[:space:]]+${my_abis}" "$in" | sort -n | ( + printf "#ifndef %s\n" "${fileguard}" + printf "#define %s\n" "${fileguard}" + printf "\n" + + nxt=0 + while read nr abi name entry ; do + if [ -z "$offset" ]; then + printf "#define TARGET_NR_%s%s\t%s\n" \ + "${prefix}" "${name}" "${nr}" + else + printf "#define TARGET_NR_%s%s\t(%s + %s)\n" \ + "${prefix}" "${name}" "${offset}" "${nr}" + fi + nxt=$((nr+1)) + done + + printf "\n" + printf "#endif /* %s */\n" "${fileguard}" +) > "$out" diff --git a/linux-user/main.c b/linux-user/main.c index 6ff7851e86..22578b1633 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -37,7 +37,7 @@ #include "qemu/plugin.h" #include "cpu.h" #include "exec/exec-all.h" -#include "tcg.h" +#include "tcg/tcg.h" #include "qemu/timer.h" #include "qemu/envlist.h" #include "qemu/guest-random.h" @@ -60,6 +60,19 @@ unsigned long mmap_min_addr; unsigned long guest_base; int have_guest_base; +/* + * Used to implement backwards-compatibility for the `-strace`, and + * QEMU_STRACE options. Without this, the QEMU_LOG can be overwritten by + * -strace, or vice versa. + */ +static bool enable_strace; + +/* + * The last log mask given by the user in an environment variable or argument. + * Used to support command line arguments overriding environment variables. + */ +static int last_log_mask; + /* * When running 32-on-64 we should make sure we can fit all of the possible * guest address space into a contiguous chunk of virtual host memory. @@ -99,15 +112,6 @@ const char *qemu_uname_release; by remapping the process stack directly at the right place */ unsigned long guest_stack_size = 8 * 1024 * 1024UL; -void gemu_log(const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - vfprintf(stderr, fmt, ap); - va_end(ap); -} - #if defined(TARGET_I386) int cpu_get_pic_interrupt(CPUX86State *env) { @@ -223,15 +227,11 @@ static void handle_arg_help(const char *arg) static void handle_arg_log(const char *arg) { - int mask; - - mask = qemu_str_to_log_mask(arg); - if (!mask) { + last_log_mask = qemu_str_to_log_mask(arg); + if (!last_log_mask) { qemu_print_log_usage(stdout); exit(EXIT_FAILURE); } - qemu_log_needs_buffers(); - qemu_set_log(mask); } static void handle_arg_dfilter(const char *arg) @@ -375,7 +375,7 @@ static void handle_arg_singlestep(const char *arg) static void handle_arg_strace(const char *arg) { - do_strace = 1; + enable_strace = true; } static void handle_arg_version(const char *arg) @@ -629,6 +629,7 @@ int main(int argc, char **argv, char **envp) int i; int ret; int execfd; + int log_mask; unsigned long max_reserved_va; error_init(argv[0]); @@ -661,6 +662,12 @@ int main(int argc, char **argv, char **envp) optind = parse_args(argc, argv); + log_mask = last_log_mask | (enable_strace ? LOG_STRACE : 0); + if (log_mask) { + qemu_log_needs_buffers(); + qemu_set_log(log_mask); + } + if (!trace_init_backends()) { exit(1); } @@ -826,7 +833,7 @@ int main(int argc, char **argv, char **envp) if (qemu_loglevel_mask(CPU_LOG_PAGE)) { qemu_log("guest_base 0x%lx\n", guest_base); - log_page_dump(); + log_page_dump("binary load"); qemu_log("start_brk 0x" TARGET_ABI_FMT_lx "\n", info->start_brk); qemu_log("end_code 0x" TARGET_ABI_FMT_lx "\n", info->end_code); diff --git a/linux-user/microblaze/Makefile.objs b/linux-user/microblaze/Makefile.objs new file mode 100644 index 0000000000..bb8b318dda --- /dev/null +++ b/linux-user/microblaze/Makefile.objs @@ -0,0 +1,5 @@ +generated-files-y += linux-user/microblaze/syscall_nr.h + +syshdr := $(SRC_PATH)/linux-user/microblaze/syscallhdr.sh +%/syscall_nr.h: $(SRC_PATH)/linux-user/microblaze/syscall.tbl $(syshdr) + $(call quiet-command, sh $(syshdr) $< $@ $(TARGET_SYSTBL_ABI),"GEN","$@") diff --git a/linux-user/microblaze/syscall.tbl b/linux-user/microblaze/syscall.tbl new file mode 100644 index 0000000000..4c67b11f9c --- /dev/null +++ b/linux-user/microblaze/syscall.tbl @@ -0,0 +1,445 @@ +# SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note +# +# system call numbers and entry vectors for microblaze +# +# The format is: +# +# +# The is always "common" for this file +# +0 common restart_syscall sys_restart_syscall +1 common exit sys_exit +2 common fork sys_fork +3 common read sys_read +4 common write sys_write +5 common open sys_open +6 common close sys_close +7 common waitpid sys_waitpid +8 common creat sys_creat +9 common link sys_link +10 common unlink sys_unlink +11 common execve sys_execve +12 common chdir sys_chdir +13 common time sys_time32 +14 common mknod sys_mknod +15 common chmod sys_chmod +16 common lchown sys_lchown +17 common break sys_ni_syscall +18 common oldstat sys_ni_syscall +19 common lseek sys_lseek +20 common getpid sys_getpid +21 common mount sys_mount +22 common umount sys_oldumount +23 common setuid sys_setuid +24 common getuid sys_getuid +25 common stime sys_stime32 +26 common ptrace sys_ptrace +27 common alarm sys_alarm +28 common oldfstat sys_ni_syscall +29 common pause sys_pause +30 common utime sys_utime32 +31 common stty sys_ni_syscall +32 common gtty sys_ni_syscall +33 common access sys_access +34 common nice sys_nice +35 common ftime sys_ni_syscall +36 common sync sys_sync +37 common kill sys_kill +38 common rename sys_rename +39 common mkdir sys_mkdir +40 common rmdir sys_rmdir +41 common dup sys_dup +42 common pipe sys_pipe +43 common times sys_times +44 common prof sys_ni_syscall +45 common brk sys_brk +46 common setgid sys_setgid +47 common getgid sys_getgid +48 common signal sys_signal +49 common geteuid sys_geteuid +50 common getegid sys_getegid +51 common acct sys_acct +52 common umount2 sys_umount +53 common lock sys_ni_syscall +54 common ioctl sys_ioctl +55 common fcntl sys_fcntl +56 common mpx sys_ni_syscall +57 common setpgid sys_setpgid +58 common ulimit sys_ni_syscall +59 common oldolduname sys_ni_syscall +60 common umask sys_umask +61 common chroot sys_chroot +62 common ustat sys_ustat +63 common dup2 sys_dup2 +64 common getppid sys_getppid +65 common getpgrp sys_getpgrp +66 common setsid sys_setsid +67 common sigaction sys_ni_syscall +68 common sgetmask sys_sgetmask +69 common ssetmask sys_ssetmask +70 common setreuid sys_setreuid +71 common setregid sys_setregid +72 common sigsuspend sys_ni_syscall +73 common sigpending sys_sigpending +74 common sethostname sys_sethostname +75 common setrlimit sys_setrlimit +76 common getrlimit sys_ni_syscall +77 common getrusage sys_getrusage +78 common gettimeofday sys_gettimeofday +79 common settimeofday sys_settimeofday +80 common getgroups sys_getgroups +81 common setgroups sys_setgroups +82 common select sys_ni_syscall +83 common symlink sys_symlink +84 common oldlstat sys_ni_syscall +85 common readlink sys_readlink +86 common uselib sys_uselib +87 common swapon sys_swapon +88 common reboot sys_reboot +89 common readdir sys_ni_syscall +90 common mmap sys_mmap +91 common munmap sys_munmap +92 common truncate sys_truncate +93 common ftruncate sys_ftruncate +94 common fchmod sys_fchmod +95 common fchown sys_fchown +96 common getpriority sys_getpriority +97 common setpriority sys_setpriority +98 common profil sys_ni_syscall +99 common statfs sys_statfs +100 common fstatfs sys_fstatfs +101 common ioperm sys_ni_syscall +102 common socketcall sys_socketcall +103 common syslog sys_syslog +104 common setitimer sys_setitimer +105 common getitimer sys_getitimer +106 common stat sys_newstat +107 common lstat sys_newlstat +108 common fstat sys_newfstat +109 common olduname sys_ni_syscall +110 common iopl sys_ni_syscall +111 common vhangup sys_vhangup +112 common idle sys_ni_syscall +113 common vm86old sys_ni_syscall +114 common wait4 sys_wait4 +115 common swapoff sys_swapoff +116 common sysinfo sys_sysinfo +117 common ipc sys_ni_syscall +118 common fsync sys_fsync +119 common sigreturn sys_ni_syscall +120 common clone sys_clone +121 common setdomainname sys_setdomainname +122 common uname sys_newuname +123 common modify_ldt sys_ni_syscall +124 common adjtimex sys_adjtimex_time32 +125 common mprotect sys_mprotect +126 common sigprocmask sys_sigprocmask +127 common create_module sys_ni_syscall +128 common init_module sys_init_module +129 common delete_module sys_delete_module +130 common get_kernel_syms sys_ni_syscall +131 common quotactl sys_quotactl +132 common getpgid sys_getpgid +133 common fchdir sys_fchdir +134 common bdflush sys_bdflush +135 common sysfs sys_sysfs +136 common personality sys_personality +137 common afs_syscall sys_ni_syscall +138 common setfsuid sys_setfsuid +139 common setfsgid sys_setfsgid +140 common _llseek sys_llseek +141 common getdents sys_getdents +142 common _newselect sys_select +143 common flock sys_flock +144 common msync sys_msync +145 common readv sys_readv +146 common writev sys_writev +147 common getsid sys_getsid +148 common fdatasync sys_fdatasync +149 common _sysctl sys_sysctl +150 common mlock sys_mlock +151 common munlock sys_munlock +152 common mlockall sys_mlockall +153 common munlockall sys_munlockall +154 common sched_setparam sys_sched_setparam +155 common sched_getparam sys_sched_getparam +156 common sched_setscheduler sys_sched_setscheduler +157 common sched_getscheduler sys_sched_getscheduler +158 common sched_yield sys_sched_yield +159 common sched_get_priority_max sys_sched_get_priority_max +160 common sched_get_priority_min sys_sched_get_priority_min +161 common sched_rr_get_interval sys_sched_rr_get_interval_time32 +162 common nanosleep sys_nanosleep_time32 +163 common mremap sys_mremap +164 common setresuid sys_setresuid +165 common getresuid sys_getresuid +166 common vm86 sys_ni_syscall +167 common query_module sys_ni_syscall +168 common poll sys_poll +169 common nfsservctl sys_ni_syscall +170 common setresgid sys_setresgid +171 common getresgid sys_getresgid +172 common prctl sys_prctl +173 common rt_sigreturn sys_rt_sigreturn_wrapper +174 common rt_sigaction sys_rt_sigaction +175 common rt_sigprocmask sys_rt_sigprocmask +176 common rt_sigpending sys_rt_sigpending +177 common rt_sigtimedwait sys_rt_sigtimedwait_time32 +178 common rt_sigqueueinfo sys_rt_sigqueueinfo +179 common rt_sigsuspend sys_rt_sigsuspend +180 common pread64 sys_pread64 +181 common pwrite64 sys_pwrite64 +182 common chown sys_chown +183 common getcwd sys_getcwd +184 common capget sys_capget +185 common capset sys_capset +186 common sigaltstack sys_ni_syscall +187 common sendfile sys_sendfile +188 common getpmsg sys_ni_syscall +189 common putpmsg sys_ni_syscall +190 common vfork sys_vfork +191 common ugetrlimit sys_getrlimit +192 common mmap2 sys_mmap2 +193 common truncate64 sys_truncate64 +194 common ftruncate64 sys_ftruncate64 +195 common stat64 sys_stat64 +196 common lstat64 sys_lstat64 +197 common fstat64 sys_fstat64 +198 common lchown32 sys_lchown +199 common getuid32 sys_getuid +200 common getgid32 sys_getgid +201 common geteuid32 sys_geteuid +202 common getegid32 sys_getegid +203 common setreuid32 sys_setreuid +204 common setregid32 sys_setregid +205 common getgroups32 sys_getgroups +206 common setgroups32 sys_setgroups +207 common fchown32 sys_fchown +208 common setresuid32 sys_setresuid +209 common getresuid32 sys_getresuid +210 common setresgid32 sys_setresgid +211 common getresgid32 sys_getresgid +212 common chown32 sys_chown +213 common setuid32 sys_setuid +214 common setgid32 sys_setgid +215 common setfsuid32 sys_setfsuid +216 common setfsgid32 sys_setfsgid +217 common pivot_root sys_pivot_root +218 common mincore sys_mincore +219 common madvise sys_madvise +220 common getdents64 sys_getdents64 +221 common fcntl64 sys_fcntl64 +# 222 is reserved for TUX +# 223 is unused +224 common gettid sys_gettid +225 common readahead sys_readahead +226 common setxattr sys_setxattr +227 common lsetxattr sys_lsetxattr +228 common fsetxattr sys_fsetxattr +229 common getxattr sys_getxattr +230 common lgetxattr sys_lgetxattr +231 common fgetxattr sys_fgetxattr +232 common listxattr sys_listxattr +233 common llistxattr sys_llistxattr +234 common flistxattr sys_flistxattr +235 common removexattr sys_removexattr +236 common lremovexattr sys_lremovexattr +237 common fremovexattr sys_fremovexattr +238 common tkill sys_tkill +239 common sendfile64 sys_sendfile64 +240 common futex sys_futex_time32 +241 common sched_setaffinity sys_sched_setaffinity +242 common sched_getaffinity sys_sched_getaffinity +243 common set_thread_area sys_ni_syscall +244 common get_thread_area sys_ni_syscall +245 common io_setup sys_io_setup +246 common io_destroy sys_io_destroy +247 common io_getevents sys_io_getevents_time32 +248 common io_submit sys_io_submit +249 common io_cancel sys_io_cancel +250 common fadvise64 sys_fadvise64 +# 251 is available for reuse (was briefly sys_set_zone_reclaim) +252 common exit_group sys_exit_group +253 common lookup_dcookie sys_lookup_dcookie +254 common epoll_create sys_epoll_create +255 common epoll_ctl sys_epoll_ctl +256 common epoll_wait sys_epoll_wait +257 common remap_file_pages sys_remap_file_pages +258 common set_tid_address sys_set_tid_address +259 common timer_create sys_timer_create +260 common timer_settime sys_timer_settime32 +261 common timer_gettime sys_timer_gettime32 +262 common timer_getoverrun sys_timer_getoverrun +263 common timer_delete sys_timer_delete +264 common clock_settime sys_clock_settime32 +265 common clock_gettime sys_clock_gettime32 +266 common clock_getres sys_clock_getres_time32 +267 common clock_nanosleep sys_clock_nanosleep_time32 +268 common statfs64 sys_statfs64 +269 common fstatfs64 sys_fstatfs64 +270 common tgkill sys_tgkill +271 common utimes sys_utimes_time32 +272 common fadvise64_64 sys_fadvise64_64 +273 common vserver sys_ni_syscall +274 common mbind sys_mbind +275 common get_mempolicy sys_get_mempolicy +276 common set_mempolicy sys_set_mempolicy +277 common mq_open sys_mq_open +278 common mq_unlink sys_mq_unlink +279 common mq_timedsend sys_mq_timedsend_time32 +280 common mq_timedreceive sys_mq_timedreceive_time32 +281 common mq_notify sys_mq_notify +282 common mq_getsetattr sys_mq_getsetattr +283 common kexec_load sys_kexec_load +284 common waitid sys_waitid +# 285 was setaltroot +286 common add_key sys_add_key +287 common request_key sys_request_key +288 common keyctl sys_keyctl +289 common ioprio_set sys_ioprio_set +290 common ioprio_get sys_ioprio_get +291 common inotify_init sys_inotify_init +292 common inotify_add_watch sys_inotify_add_watch +293 common inotify_rm_watch sys_inotify_rm_watch +294 common migrate_pages sys_ni_syscall +295 common openat sys_openat +296 common mkdirat sys_mkdirat +297 common mknodat sys_mknodat +298 common fchownat sys_fchownat +299 common futimesat sys_futimesat_time32 +300 common fstatat64 sys_fstatat64 +301 common unlinkat sys_unlinkat +302 common renameat sys_renameat +303 common linkat sys_linkat +304 common symlinkat sys_symlinkat +305 common readlinkat sys_readlinkat +306 common fchmodat sys_fchmodat +307 common faccessat sys_faccessat +308 common pselect6 sys_pselect6_time32 +309 common ppoll sys_ppoll_time32 +310 common unshare sys_unshare +311 common set_robust_list sys_set_robust_list +312 common get_robust_list sys_get_robust_list +313 common splice sys_splice +314 common sync_file_range sys_sync_file_range +315 common tee sys_tee +316 common vmsplice sys_vmsplice +317 common move_pages sys_move_pages +318 common getcpu sys_getcpu +319 common epoll_pwait sys_epoll_pwait +320 common utimensat sys_utimensat_time32 +321 common signalfd sys_signalfd +322 common timerfd_create sys_timerfd_create +323 common eventfd sys_eventfd +324 common fallocate sys_fallocate +325 common semtimedop sys_semtimedop_time32 +326 common timerfd_settime sys_timerfd_settime32 +327 common timerfd_gettime sys_timerfd_gettime32 +328 common semctl sys_old_semctl +329 common semget sys_semget +330 common semop sys_semop +331 common msgctl sys_old_msgctl +332 common msgget sys_msgget +333 common msgrcv sys_msgrcv +334 common msgsnd sys_msgsnd +335 common shmat sys_shmat +336 common shmctl sys_old_shmctl +337 common shmdt sys_shmdt +338 common shmget sys_shmget +339 common signalfd4 sys_signalfd4 +340 common eventfd2 sys_eventfd2 +341 common epoll_create1 sys_epoll_create1 +342 common dup3 sys_dup3 +343 common pipe2 sys_pipe2 +344 common inotify_init1 sys_inotify_init1 +345 common socket sys_socket +346 common socketpair sys_socketpair +347 common bind sys_bind +348 common listen sys_listen +349 common accept sys_accept +350 common connect sys_connect +351 common getsockname sys_getsockname +352 common getpeername sys_getpeername +353 common sendto sys_sendto +354 common send sys_send +355 common recvfrom sys_recvfrom +356 common recv sys_recv +357 common setsockopt sys_setsockopt +358 common getsockopt sys_getsockopt +359 common shutdown sys_shutdown +360 common sendmsg sys_sendmsg +361 common recvmsg sys_recvmsg +362 common accept4 sys_accept4 +363 common preadv sys_preadv +364 common pwritev sys_pwritev +365 common rt_tgsigqueueinfo sys_rt_tgsigqueueinfo +366 common perf_event_open sys_perf_event_open +367 common recvmmsg sys_recvmmsg_time32 +368 common fanotify_init sys_fanotify_init +369 common fanotify_mark sys_fanotify_mark +370 common prlimit64 sys_prlimit64 +371 common name_to_handle_at sys_name_to_handle_at +372 common open_by_handle_at sys_open_by_handle_at +373 common clock_adjtime sys_clock_adjtime32 +374 common syncfs sys_syncfs +375 common setns sys_setns +376 common sendmmsg sys_sendmmsg +377 common process_vm_readv sys_process_vm_readv +378 common process_vm_writev sys_process_vm_writev +379 common kcmp sys_kcmp +380 common finit_module sys_finit_module +381 common sched_setattr sys_sched_setattr +382 common sched_getattr sys_sched_getattr +383 common renameat2 sys_renameat2 +384 common seccomp sys_seccomp +385 common getrandom sys_getrandom +386 common memfd_create sys_memfd_create +387 common bpf sys_bpf +388 common execveat sys_execveat +389 common userfaultfd sys_userfaultfd +390 common membarrier sys_membarrier +391 common mlock2 sys_mlock2 +392 common copy_file_range sys_copy_file_range +393 common preadv2 sys_preadv2 +394 common pwritev2 sys_pwritev2 +395 common pkey_mprotect sys_pkey_mprotect +396 common pkey_alloc sys_pkey_alloc +397 common pkey_free sys_pkey_free +398 common statx sys_statx +399 common io_pgetevents sys_io_pgetevents_time32 +400 common rseq sys_rseq +# 401 and 402 are unused +403 common clock_gettime64 sys_clock_gettime +404 common clock_settime64 sys_clock_settime +405 common clock_adjtime64 sys_clock_adjtime +406 common clock_getres_time64 sys_clock_getres +407 common clock_nanosleep_time64 sys_clock_nanosleep +408 common timer_gettime64 sys_timer_gettime +409 common timer_settime64 sys_timer_settime +410 common timerfd_gettime64 sys_timerfd_gettime +411 common timerfd_settime64 sys_timerfd_settime +412 common utimensat_time64 sys_utimensat +413 common pselect6_time64 sys_pselect6 +414 common ppoll_time64 sys_ppoll +416 common io_pgetevents_time64 sys_io_pgetevents +417 common recvmmsg_time64 sys_recvmmsg +418 common mq_timedsend_time64 sys_mq_timedsend +419 common mq_timedreceive_time64 sys_mq_timedreceive +420 common semtimedop_time64 sys_semtimedop +421 common rt_sigtimedwait_time64 sys_rt_sigtimedwait +422 common futex_time64 sys_futex +423 common sched_rr_get_interval_time64 sys_sched_rr_get_interval +424 common pidfd_send_signal sys_pidfd_send_signal +425 common io_uring_setup sys_io_uring_setup +426 common io_uring_enter sys_io_uring_enter +427 common io_uring_register sys_io_uring_register +428 common open_tree sys_open_tree +429 common move_mount sys_move_mount +430 common fsopen sys_fsopen +431 common fsconfig sys_fsconfig +432 common fsmount sys_fsmount +433 common fspick sys_fspick +434 common pidfd_open sys_pidfd_open +435 common clone3 sys_clone3 +437 common openat2 sys_openat2 +438 common pidfd_getfd sys_pidfd_getfd diff --git a/linux-user/microblaze/syscall_nr.h b/linux-user/microblaze/syscall_nr.h deleted file mode 100644 index aa2eb93881..0000000000 --- a/linux-user/microblaze/syscall_nr.h +++ /dev/null @@ -1,397 +0,0 @@ -#ifndef LINUX_USER_MICROBLAZE_SYSCALL_NR_H -#define LINUX_USER_MICROBLAZE_SYSCALL_NR_H - -#define TARGET_NR_restart_syscall 0 /* ok */ -#define TARGET_NR_exit 1 /* ok */ -#define TARGET_NR_fork 2 /* not for no MMU - weird */ -#define TARGET_NR_read 3 /* ok */ -#define TARGET_NR_write 4 /* ok */ -#define TARGET_NR_open 5 /* openat */ -#define TARGET_NR_close 6 /* ok */ -#define TARGET_NR_waitpid 7 /* waitid */ -#define TARGET_NR_creat 8 /* openat */ -#define TARGET_NR_link 9 /* linkat */ -#define TARGET_NR_unlink 10 /* unlinkat */ -#define TARGET_NR_execve 11 /* ok */ -#define TARGET_NR_chdir 12 /* ok */ -#define TARGET_NR_time 13 /* obsolete -> sys_gettimeofday */ -#define TARGET_NR_mknod 14 /* mknodat */ -#define TARGET_NR_chmod 15 /* fchmodat */ -#define TARGET_NR_lchown 16 /* ok */ -#define TARGET_NR_break 17 /* don't know */ -#define TARGET_NR_oldstat 18 /* remove */ -#define TARGET_NR_lseek 19 /* ok */ -#define TARGET_NR_getpid 20 /* ok */ -#define TARGET_NR_mount 21 /* ok */ -#define TARGET_NR_umount 22 /* ok */ /* use only umount2 */ -#define TARGET_NR_setuid 23 /* ok */ -#define TARGET_NR_getuid 24 /* ok */ -#define TARGET_NR_stime 25 /* obsolete -> sys_settimeofday */ -#define TARGET_NR_ptrace 26 /* ok */ -#define TARGET_NR_alarm 27 /* obsolete -> sys_setitimer */ -#define TARGET_NR_oldfstat 28 /* remove */ -#define TARGET_NR_pause 29 /* obsolete -> sys_rt_sigtimedwait */ -#define TARGET_NR_utime 30 /* obsolete -> sys_utimesat */ -#define TARGET_NR_stty 31 /* remove */ -#define TARGET_NR_gtty 32 /* remove */ -#define TARGET_NR_access 33 /* faccessat */ -#define TARGET_NR_nice 34 /* can be implemented by sys_setpriority */ -#define TARGET_NR_ftime 35 /* remove */ -#define TARGET_NR_sync 36 /* ok */ -#define TARGET_NR_kill 37 /* ok */ -#define TARGET_NR_rename 38 /* renameat */ -#define TARGET_NR_mkdir 39 /* mkdirat */ -#define TARGET_NR_rmdir 40 /* unlinkat */ -#define TARGET_NR_dup 41 /* ok */ -#define TARGET_NR_pipe 42 /* ok */ -#define TARGET_NR_times 43 /* ok */ -#define TARGET_NR_prof 44 /* remove */ -#define TARGET_NR_brk 45 /* ok -mmu, nommu specific */ -#define TARGET_NR_setgid 46 /* ok */ -#define TARGET_NR_getgid 47 /* ok */ -#define TARGET_NR_signal 48 /* obsolete -> sys_rt_sigaction */ -#define TARGET_NR_geteuid 49 /* ok */ -#define TARGET_NR_getegid 50 /* ok */ -#define TARGET_NR_acct 51 /* add it and then I can disable it */ -#define TARGET_NR_umount2 52 /* remove */ -#define TARGET_NR_lock 53 /* remove */ -#define TARGET_NR_ioctl 54 /* ok */ -#define TARGET_NR_fcntl 55 /* ok -> 64bit version*/ -#define TARGET_NR_mpx 56 /* remove */ -#define TARGET_NR_setpgid 57 /* ok */ -#define TARGET_NR_ulimit 58 /* remove */ -#define TARGET_NR_oldolduname 59 /* remove */ -#define TARGET_NR_umask 60 /* ok */ -#define TARGET_NR_chroot 61 /* ok */ -#define TARGET_NR_ustat 62 /* obsolete -> statfs64 */ -#define TARGET_NR_dup2 63 /* ok */ -#define TARGET_NR_getppid 64 /* ok */ -#define TARGET_NR_getpgrp 65 /* obsolete -> sys_getpgid */ -#define TARGET_NR_setsid 66 /* ok */ -#define TARGET_NR_sigaction 67 /* obsolete -> rt_sigaction */ -#define TARGET_NR_sgetmask 68 /* obsolete -> sys_rt_sigprocmask */ -#define TARGET_NR_ssetmask 69 /* obsolete ->sys_rt_sigprocmask */ -#define TARGET_NR_setreuid 70 /* ok */ -#define TARGET_NR_setregid 71 /* ok */ -#define TARGET_NR_sigsuspend 72 /* obsolete -> rt_sigsuspend */ -#define TARGET_NR_sigpending 73 /* obsolete -> sys_rt_sigpending */ -#define TARGET_NR_sethostname 74 /* ok */ -#define TARGET_NR_setrlimit 75 /* ok */ -#define TARGET_NR_getrlimit 76 /* ok Back compatible 2Gig limited rlimit */ -#define TARGET_NR_getrusage 77 /* ok */ -#define TARGET_NR_gettimeofday 78 /* ok */ -#define TARGET_NR_settimeofday 79 /* ok */ -#define TARGET_NR_getgroups 80 /* ok */ -#define TARGET_NR_setgroups 81 /* ok */ -#define TARGET_NR_select 82 /* obsolete -> sys_pselect7 */ -#define TARGET_NR_symlink 83 /* symlinkat */ -#define TARGET_NR_oldlstat 84 /* remove */ -#define TARGET_NR_readlink 85 /* obsolete -> sys_readlinkat */ -#define TARGET_NR_uselib 86 /* remove */ -#define TARGET_NR_swapon 87 /* ok */ -#define TARGET_NR_reboot 88 /* ok */ -#define TARGET_NR_readdir 89 /* remove ? */ -#define TARGET_NR_mmap 90 /* obsolete -> sys_mmap2 */ -#define TARGET_NR_munmap 91 /* ok - mmu and nommu */ -#define TARGET_NR_truncate 92 /* ok or truncate64 */ -#define TARGET_NR_ftruncate 93 /* ok or ftruncate64 */ -#define TARGET_NR_fchmod 94 /* ok */ -#define TARGET_NR_fchown 95 /* ok */ -#define TARGET_NR_getpriority 96 /* ok */ -#define TARGET_NR_setpriority 97 /* ok */ -#define TARGET_NR_profil 98 /* remove */ -#define TARGET_NR_statfs 99 /* ok or statfs64 */ -#define TARGET_NR_fstatfs 100 /* ok or fstatfs64 */ -#define TARGET_NR_ioperm 101 /* remove */ -#define TARGET_NR_socketcall 102 /* remove */ -#define TARGET_NR_syslog 103 /* ok */ -#define TARGET_NR_setitimer 104 /* ok */ -#define TARGET_NR_getitimer 105 /* ok */ -#define TARGET_NR_stat 106 /* remove */ -#define TARGET_NR_lstat 107 /* remove */ -#define TARGET_NR_fstat 108 /* remove */ -#define TARGET_NR_olduname 109 /* remove */ -#define TARGET_NR_iopl 110 /* remove */ -#define TARGET_NR_vhangup 111 /* ok */ -#define TARGET_NR_idle 112 /* remove */ -#define TARGET_NR_vm86old 113 /* remove */ -#define TARGET_NR_wait4 114 /* obsolete -> waitid */ -#define TARGET_NR_swapoff 115 /* ok */ -#define TARGET_NR_sysinfo 116 /* ok */ -#define TARGET_NR_ipc 117 /* remove - direct call */ -#define TARGET_NR_fsync 118 /* ok */ -#define TARGET_NR_sigreturn 119 /* obsolete -> sys_rt_sigreturn */ -#define TARGET_NR_clone 120 /* ok */ -#define TARGET_NR_setdomainname 121 /* ok */ -#define TARGET_NR_uname 122 /* remove */ -#define TARGET_NR_modify_ldt 123 /* remove */ -#define TARGET_NR_adjtimex 124 /* ok */ -#define TARGET_NR_mprotect 125 /* remove */ -#define TARGET_NR_sigprocmask 126 /* obsolete -> sys_rt_sigprocmask */ -#define TARGET_NR_create_module 127 /* remove */ -#define TARGET_NR_init_module 128 /* ok */ -#define TARGET_NR_delete_module 129 /* ok */ -#define TARGET_NR_get_kernel_syms 130 /* remove */ -#define TARGET_NR_quotactl 131 /* ok */ -#define TARGET_NR_getpgid 132 /* ok */ -#define TARGET_NR_fchdir 133 /* ok */ -#define TARGET_NR_bdflush 134 /* remove */ -#define TARGET_NR_sysfs 135 /* needed for busybox */ -#define TARGET_NR_personality 136 /* ok */ -#define TARGET_NR_afs_syscall 137 /* Syscall for Andrew File System */ -#define TARGET_NR_setfsuid 138 /* ok */ -#define TARGET_NR_setfsgid 139 /* ok */ -#define TARGET_NR__llseek 140 /* remove only lseek */ -#define TARGET_NR_getdents 141 /* ok or getdents64 */ -#define TARGET_NR__newselect 142 /* remove */ -#define TARGET_NR_flock 143 /* ok */ -#define TARGET_NR_msync 144 /* remove */ -#define TARGET_NR_readv 145 /* ok */ -#define TARGET_NR_writev 146 /* ok */ -#define TARGET_NR_getsid 147 /* ok */ -#define TARGET_NR_fdatasync 148 /* ok */ -#define TARGET_NR__sysctl 149 /* remove */ -#define TARGET_NR_mlock 150 /* ok - nommu or mmu */ -#define TARGET_NR_munlock 151 /* ok - nommu or mmu */ -#define TARGET_NR_mlockall 152 /* ok - nommu or mmu */ -#define TARGET_NR_munlockall 153 /* ok - nommu or mmu */ -#define TARGET_NR_sched_setparam 154 /* ok */ -#define TARGET_NR_sched_getparam 155 /* ok */ -#define TARGET_NR_sched_setscheduler 156 /* ok */ -#define TARGET_NR_sched_getscheduler 157 /* ok */ -#define TARGET_NR_sched_yield 158 /* ok */ -#define TARGET_NR_sched_get_priority_max 159 /* ok */ -#define TARGET_NR_sched_get_priority_min 160 /* ok */ -#define TARGET_NR_sched_rr_get_interval 161 /* ok */ -#define TARGET_NR_nanosleep 162 /* ok */ -#define TARGET_NR_mremap 163 /* ok - nommu or mmu */ -#define TARGET_NR_setresuid 164 /* ok */ -#define TARGET_NR_getresuid 165 /* ok */ -#define TARGET_NR_vm86 166 /* remove */ -#define TARGET_NR_query_module 167 /* ok */ -#define TARGET_NR_poll 168 /* obsolete -> sys_ppoll */ -#define TARGET_NR_nfsservctl 169 /* ok */ -#define TARGET_NR_setresgid 170 /* ok */ -#define TARGET_NR_getresgid 171 /* ok */ -#define TARGET_NR_prctl 172 /* ok */ -#define TARGET_NR_rt_sigreturn 173 /* ok */ -#define TARGET_NR_rt_sigaction 174 /* ok */ -#define TARGET_NR_rt_sigprocmask 175 /* ok */ -#define TARGET_NR_rt_sigpending 176 /* ok */ -#define TARGET_NR_rt_sigtimedwait 177 /* ok */ -#define TARGET_NR_rt_sigqueueinfo 178 /* ok */ -#define TARGET_NR_rt_sigsuspend 179 /* ok */ -#define TARGET_NR_pread64 180 /* ok */ -#define TARGET_NR_pwrite64 181 /* ok */ -#define TARGET_NR_chown 182 /* obsolete -> fchownat */ -#define TARGET_NR_getcwd 183 /* ok */ -#define TARGET_NR_capget 184 /* ok */ -#define TARGET_NR_capset 185 /* ok */ -#define TARGET_NR_sigaltstack 186 /* remove */ -#define TARGET_NR_sendfile 187 /* ok -> exist 64bit version*/ -#define TARGET_NR_getpmsg 188 /* remove - some people actually want streams */ -#define TARGET_NR_putpmsg 189 /* remove - some people actually want streams */ -#define TARGET_NR_vfork 190 /* for noMMU - group with clone -> maybe remove */ -#define TARGET_NR_ugetrlimit 191 /* remove - SuS compliant getrlimit */ -#define TARGET_NR_mmap2 192 /* ok */ -#define TARGET_NR_truncate64 193 /* ok */ -#define TARGET_NR_ftruncate64 194 /* ok */ -#define TARGET_NR_stat64 195 /* remove _ARCH_WANT_STAT64 */ -#define TARGET_NR_lstat64 196 /* remove _ARCH_WANT_STAT64 */ -#define TARGET_NR_fstat64 197 /* remove _ARCH_WANT_STAT64 */ -#define TARGET_NR_lchown32 198 /* ok - without 32 */ -#define TARGET_NR_getuid32 199 /* ok - without 32 */ -#define TARGET_NR_getgid32 200 /* ok - without 32 */ -#define TARGET_NR_geteuid32 201 /* ok - without 32 */ -#define TARGET_NR_getegid32 202 /* ok - without 32 */ -#define TARGET_NR_setreuid32 203 /* ok - without 32 */ -#define TARGET_NR_setregid32 204 /* ok - without 32 */ -#define TARGET_NR_getgroups32 205 /* ok - without 32 */ -#define TARGET_NR_setgroups32 206 /* ok - without 32 */ -#define TARGET_NR_fchown32 207 /* ok - without 32 */ -#define TARGET_NR_setresuid32 208 /* ok - without 32 */ -#define TARGET_NR_getresuid32 209 /* ok - without 32 */ -#define TARGET_NR_setresgid32 210 /* ok - without 32 */ -#define TARGET_NR_getresgid32 211 /* ok - without 32 */ -#define TARGET_NR_chown32 212 /* ok - without 32 -obsolete -> fchownat */ -#define TARGET_NR_setuid32 213 /* ok - without 32 */ -#define TARGET_NR_setgid32 214 /* ok - without 32 */ -#define TARGET_NR_setfsuid32 215 /* ok - without 32 */ -#define TARGET_NR_setfsgid32 216 /* ok - without 32 */ -#define TARGET_NR_pivot_root 217 /* ok */ -#define TARGET_NR_mincore 218 /* ok */ -#define TARGET_NR_madvise 219 /* ok */ -//#define TARGET_NR_madvise1 219 /* remove delete when C lib stub is removed */ -#define TARGET_NR_getdents64 220 /* ok */ -#define TARGET_NR_fcntl64 221 /* ok */ -/* 223 is unused */ -#define TARGET_NR_gettid 224 /* ok */ -#define TARGET_NR_readahead 225 /* ok */ -#define TARGET_NR_setxattr 226 /* ok */ -#define TARGET_NR_lsetxattr 227 /* ok */ -#define TARGET_NR_fsetxattr 228 /* ok */ -#define TARGET_NR_getxattr 229 /* ok */ -#define TARGET_NR_lgetxattr 230 /* ok */ -#define TARGET_NR_fgetxattr 231 /* ok */ -#define TARGET_NR_listxattr 232 /* ok */ -#define TARGET_NR_llistxattr 233 /* ok */ -#define TARGET_NR_flistxattr 234 /* ok */ -#define TARGET_NR_removexattr 235 /* ok */ -#define TARGET_NR_lremovexattr 236 /* ok */ -#define TARGET_NR_fremovexattr 237 /* ok */ -#define TARGET_NR_tkill 238 /* ok */ -#define TARGET_NR_sendfile64 239 /* ok */ -#define TARGET_NR_futex 240 /* ok */ -#define TARGET_NR_sched_setaffinity 241 /* ok */ -#define TARGET_NR_sched_getaffinity 242 /* ok */ -#define TARGET_NR_set_thread_area 243 /* remove */ -#define TARGET_NR_get_thread_area 244 /* remove */ -#define TARGET_NR_io_setup 245 /* ok */ -#define TARGET_NR_io_destroy 246 /* ok */ -#define TARGET_NR_io_getevents 247 /* ok */ -#define TARGET_NR_io_submit 248 /* ok */ -#define TARGET_NR_io_cancel 249 /* ok */ -#define TARGET_NR_fadvise64 250 /* remove -> sys_fadvise64_64 */ -/* 251 is available for reuse (was briefly sys_set_zone_reclaim) */ -#define TARGET_NR_exit_group 252 /* ok */ -#define TARGET_NR_lookup_dcookie 253 /* ok */ -#define TARGET_NR_epoll_create 254 /* ok */ -#define TARGET_NR_epoll_ctl 255 /* ok */ -#define TARGET_NR_epoll_wait 256 /* obsolete -> sys_epoll_pwait */ -#define TARGET_NR_remap_file_pages 257 /* only for mmu */ -#define TARGET_NR_set_tid_address 258 /* ok */ -#define TARGET_NR_timer_create 259 /* ok */ -#define TARGET_NR_timer_settime (TARGET_NR_timer_create+1) /* 260 */ /* ok */ -#define TARGET_NR_timer_gettime (TARGET_NR_timer_create+2) /* 261 */ /* ok */ -#define TARGET_NR_timer_getoverrun (TARGET_NR_timer_create+3) /* 262 */ /* ok */ -#define TARGET_NR_timer_delete (TARGET_NR_timer_create+4) /* 263 */ /* ok */ -#define TARGET_NR_clock_settime (TARGET_NR_timer_create+5) /* 264 */ /* ok */ -#define TARGET_NR_clock_gettime (TARGET_NR_timer_create+6) /* 265 */ /* ok */ -#define TARGET_NR_clock_getres (TARGET_NR_timer_create+7) /* 266 */ /* ok */ -#define TARGET_NR_clock_nanosleep (TARGET_NR_timer_create+8) /* 267 */ /* ok */ -#define TARGET_NR_statfs64 268 /* ok */ -#define TARGET_NR_fstatfs64 269 /* ok */ -#define TARGET_NR_tgkill 270 /* ok */ -#define TARGET_NR_utimes 271 /* obsolete -> sys_futimesat */ -#define TARGET_NR_fadvise64_64 272 /* ok */ -#define TARGET_NR_vserver 273 /* ok */ -#define TARGET_NR_mbind 274 /* only for mmu */ -#define TARGET_NR_get_mempolicy 275 /* only for mmu */ -#define TARGET_NR_set_mempolicy 276 /* only for mmu */ -#define TARGET_NR_mq_open 277 /* ok */ -#define TARGET_NR_mq_unlink (TARGET_NR_mq_open+1) /* 278 */ /* ok */ -#define TARGET_NR_mq_timedsend (TARGET_NR_mq_open+2) /* 279 */ /* ok */ -#define TARGET_NR_mq_timedreceive (TARGET_NR_mq_open+3) /* 280 */ /* ok */ -#define TARGET_NR_mq_notify (TARGET_NR_mq_open+4) /* 281 */ /* ok */ -#define TARGET_NR_mq_getsetattr (TARGET_NR_mq_open+5) /* 282 */ /* ok */ -#define TARGET_NR_kexec_load 283 /* ok */ -#define TARGET_NR_waitid 284 /* ok */ -/* #define TARGET_NR_sys_setaltroot 285 */ -#define TARGET_NR_add_key 286 /* ok */ -#define TARGET_NR_request_key 287 /* ok */ -#define TARGET_NR_keyctl 288 /* ok */ -#define TARGET_NR_ioprio_set 289 /* ok */ -#define TARGET_NR_ioprio_get 290 /* ok */ -#define TARGET_NR_inotify_init 291 /* ok */ -#define TARGET_NR_inotify_add_watch 292 /* ok */ -#define TARGET_NR_inotify_rm_watch 293 /* ok */ -#define TARGET_NR_migrate_pages 294 /* mmu */ -#define TARGET_NR_openat 295 /* ok */ -#define TARGET_NR_mkdirat 296 /* ok */ -#define TARGET_NR_mknodat 297 /* ok */ -#define TARGET_NR_fchownat 298 /* ok */ -#define TARGET_NR_futimesat 299 /* obsolete -> sys_utimesat */ -#define TARGET_NR_fstatat64 300 /* stat64 */ -#define TARGET_NR_unlinkat 301 /* ok */ -#define TARGET_NR_renameat 302 /* ok */ -#define TARGET_NR_linkat 303 /* ok */ -#define TARGET_NR_symlinkat 304 /* ok */ -#define TARGET_NR_readlinkat 305 /* ok */ -#define TARGET_NR_fchmodat 306 /* ok */ -#define TARGET_NR_faccessat 307 /* ok */ -#define TARGET_NR_pselect6 308 /* obsolete -> sys_pselect7 */ -#define TARGET_NR_ppoll 309 /* ok */ -#define TARGET_NR_unshare 310 /* ok */ -#define TARGET_NR_set_robust_list 311 /* ok */ -#define TARGET_NR_get_robust_list 312 /* ok */ -#define TARGET_NR_splice 313 /* ok */ -#define TARGET_NR_sync_file_range 314 /* ok */ -#define TARGET_NR_tee 315 /* ok */ -#define TARGET_NR_vmsplice 316 /* ok */ -#define TARGET_NR_move_pages 317 /* mmu */ -#define TARGET_NR_getcpu 318 /* ok */ -#define TARGET_NR_epoll_pwait 319 /* ok */ -#define TARGET_NR_utimensat 320 /* ok */ -#define TARGET_NR_signalfd 321 /* ok */ -#define TARGET_NR_timerfd_create 322 /* ok */ -#define TARGET_NR_eventfd 323 /* ok */ -#define TARGET_NR_fallocate 324 /* ok */ -#define TARGET_NR_semtimedop 325 /* ok - semaphore group */ -#define TARGET_NR_timerfd_settime 326 /* ok */ -#define TARGET_NR_timerfd_gettime 327 /* ok */ -/* sysv ipc syscalls */ -#define TARGET_NR_semctl 328 /* ok */ -#define TARGET_NR_semget 329 /* ok */ -#define TARGET_NR_semop 330 /* ok */ -#define TARGET_NR_msgctl 331 /* ok */ -#define TARGET_NR_msgget 332 /* ok */ -#define TARGET_NR_msgrcv 333 /* ok */ -#define TARGET_NR_msgsnd 334 /* ok */ -#define TARGET_NR_shmat 335 /* ok */ -#define TARGET_NR_shmctl 336 /* ok */ -#define TARGET_NR_shmdt 337 /* ok */ -#define TARGET_NR_shmget 338 /* ok */ - - -#define TARGET_NR_signalfd4 339 /* new */ -#define TARGET_NR_eventfd2 340 /* new */ -#define TARGET_NR_epoll_create1 341 /* new */ -#define TARGET_NR_dup3 342 /* new */ -#define TARGET_NR_pipe2 343 /* new */ -#define TARGET_NR_inotify_init1 344 /* new */ -#define TARGET_NR_socket 345 /* new */ -#define TARGET_NR_socketpair 346 /* new */ -#define TARGET_NR_bind 347 /* new */ -#define TARGET_NR_listen 348 /* new */ -#define TARGET_NR_accept 349 /* new */ -#define TARGET_NR_connect 350 /* new */ -#define TARGET_NR_getsockname 351 /* new */ -#define TARGET_NR_getpeername 352 /* new */ -#define TARGET_NR_sendto 353 /* new */ -#define TARGET_NR_send 354 /* new */ -#define TARGET_NR_recvfrom 355 /* new */ -#define TARGET_NR_recv 356 /* new */ -#define TARGET_NR_setsockopt 357 /* new */ -#define TARGET_NR_getsockopt 358 /* new */ -#define TARGET_NR_shutdown 359 /* new */ -#define TARGET_NR_sendmsg 360 /* new */ -#define TARGET_NR_recvmsg 361 /* new */ -#define TARGET_NR_accept4 362 /* new */ -#define TARGET_NR_preadv 363 /* new */ -#define TARGET_NR_pwritev 364 /* new */ -#define TARGET_NR_rt_tgsigqueueinfo 365 /* new */ -#define TARGET_NR_perf_event_open 366 /* new */ -#define TARGET_NR_recvmmsg 367 /* new */ -#define TARGET_NR_fanotify_init 368 -#define TARGET_NR_fanotify_mark 369 -#define TARGET_NR_prlimit64 370 -#define TARGET_NR_name_to_handle_at 371 -#define TARGET_NR_open_by_handle_at 372 -#define TARGET_NR_clock_adjtime 373 -#define TARGET_NR_syncfs 374 -#define TARGET_NR_setns 375 -#define TARGET_NR_sendmmsg 376 -#define TARGET_NR_process_vm_readv 377 -#define TARGET_NR_process_vm_writev 378 -#define TARGET_NR_kcmp 379 -#define TARGET_NR_finit_module 380 -#define TARGET_NR_sched_setattr 381 -#define TARGET_NR_sched_getattr 382 -#define TARGET_NR_renameat2 383 -#define TARGET_NR_seccomp 384 -#define TARGET_NR_getrandom 385 -#define TARGET_NR_memfd_create 386 -#define TARGET_NR_bpf 387 -#define TARGET_NR_execveat 388 - -#endif diff --git a/linux-user/microblaze/syscallhdr.sh b/linux-user/microblaze/syscallhdr.sh new file mode 100644 index 0000000000..f55dce8a62 --- /dev/null +++ b/linux-user/microblaze/syscallhdr.sh @@ -0,0 +1,32 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 + +in="$1" +out="$2" +my_abis=`echo "($3)" | tr ',' '|'` +prefix="$4" +offset="$5" + +fileguard=LINUX_USER_MICROBLAZE_`basename "$out" | sed \ + -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/' \ + -e 's/[^A-Z0-9_]/_/g' -e 's/__/_/g'` +grep -E "^[0-9A-Fa-fXx]+[[:space:]]+${my_abis}" "$in" | sort -n | ( + printf "#ifndef %s\n" "${fileguard}" + printf "#define %s\n" "${fileguard}" + printf "\n" + + nxt=0 + while read nr abi name entry ; do + if [ -z "$offset" ]; then + printf "#define TARGET_NR_%s%s\t%s\n" \ + "${prefix}" "${name}" "${nr}" + else + printf "#define TARGET_NR_%s%s\t(%s + %s)\n" \ + "${prefix}" "${name}" "${offset}" "${nr}" + fi + nxt=$((nr+1)) + done + + printf "\n" + printf "#endif /* %s */" "${fileguard}" +) > "$out" diff --git a/linux-user/mips/Makefile.objs b/linux-user/mips/Makefile.objs new file mode 100644 index 0000000000..9be4de07d9 --- /dev/null +++ b/linux-user/mips/Makefile.objs @@ -0,0 +1,5 @@ +generated-files-y += linux-user/mips/syscall_nr.h + +syshdr := $(SRC_PATH)/linux-user/mips/syscallhdr.sh +%/syscall_nr.h: $(SRC_PATH)/linux-user/mips/syscall_o32.tbl $(syshdr) + $(call quiet-command, sh $(syshdr) $< $@ $(TARGET_SYSTBL_ABI) "" 4000,"GEN","$@") diff --git a/linux-user/mips/cpu_loop.c b/linux-user/mips/cpu_loop.c index 39915b3fde..553e8ca7f5 100644 --- a/linux-user/mips/cpu_loop.c +++ b/linux-user/mips/cpu_loop.c @@ -25,373 +25,10 @@ #include "internal.h" # ifdef TARGET_ABI_MIPSO32 -# define MIPS_SYS(name, args) args, -static const uint8_t mips_syscall_args[] = { - MIPS_SYS(sys_syscall , 8) /* 4000 */ - MIPS_SYS(sys_exit , 1) - MIPS_SYS(sys_fork , 0) - MIPS_SYS(sys_read , 3) - MIPS_SYS(sys_write , 3) - MIPS_SYS(sys_open , 3) /* 4005 */ - MIPS_SYS(sys_close , 1) - MIPS_SYS(sys_waitpid , 3) - MIPS_SYS(sys_creat , 2) - MIPS_SYS(sys_link , 2) - MIPS_SYS(sys_unlink , 1) /* 4010 */ - MIPS_SYS(sys_execve , 0) - MIPS_SYS(sys_chdir , 1) - MIPS_SYS(sys_time , 1) - MIPS_SYS(sys_mknod , 3) - MIPS_SYS(sys_chmod , 2) /* 4015 */ - MIPS_SYS(sys_lchown , 3) - MIPS_SYS(sys_ni_syscall , 0) - MIPS_SYS(sys_ni_syscall , 0) /* was sys_stat */ - MIPS_SYS(sys_lseek , 3) - MIPS_SYS(sys_getpid , 0) /* 4020 */ - MIPS_SYS(sys_mount , 5) - MIPS_SYS(sys_umount , 1) - MIPS_SYS(sys_setuid , 1) - MIPS_SYS(sys_getuid , 0) - MIPS_SYS(sys_stime , 1) /* 4025 */ - MIPS_SYS(sys_ptrace , 4) - MIPS_SYS(sys_alarm , 1) - MIPS_SYS(sys_ni_syscall , 0) /* was sys_fstat */ - MIPS_SYS(sys_pause , 0) - MIPS_SYS(sys_utime , 2) /* 4030 */ - MIPS_SYS(sys_ni_syscall , 0) - MIPS_SYS(sys_ni_syscall , 0) - MIPS_SYS(sys_access , 2) - MIPS_SYS(sys_nice , 1) - MIPS_SYS(sys_ni_syscall , 0) /* 4035 */ - MIPS_SYS(sys_sync , 0) - MIPS_SYS(sys_kill , 2) - MIPS_SYS(sys_rename , 2) - MIPS_SYS(sys_mkdir , 2) - MIPS_SYS(sys_rmdir , 1) /* 4040 */ - MIPS_SYS(sys_dup , 1) - MIPS_SYS(sys_pipe , 0) - MIPS_SYS(sys_times , 1) - MIPS_SYS(sys_ni_syscall , 0) - MIPS_SYS(sys_brk , 1) /* 4045 */ - MIPS_SYS(sys_setgid , 1) - MIPS_SYS(sys_getgid , 0) - MIPS_SYS(sys_ni_syscall , 0) /* was signal(2) */ - MIPS_SYS(sys_geteuid , 0) - MIPS_SYS(sys_getegid , 0) /* 4050 */ - MIPS_SYS(sys_acct , 0) - MIPS_SYS(sys_umount2 , 2) - MIPS_SYS(sys_ni_syscall , 0) - MIPS_SYS(sys_ioctl , 3) - MIPS_SYS(sys_fcntl , 3) /* 4055 */ - MIPS_SYS(sys_ni_syscall , 2) - MIPS_SYS(sys_setpgid , 2) - MIPS_SYS(sys_ni_syscall , 0) - MIPS_SYS(sys_olduname , 1) - MIPS_SYS(sys_umask , 1) /* 4060 */ - MIPS_SYS(sys_chroot , 1) - MIPS_SYS(sys_ustat , 2) - MIPS_SYS(sys_dup2 , 2) - MIPS_SYS(sys_getppid , 0) - MIPS_SYS(sys_getpgrp , 0) /* 4065 */ - MIPS_SYS(sys_setsid , 0) - MIPS_SYS(sys_sigaction , 3) - MIPS_SYS(sys_sgetmask , 0) - MIPS_SYS(sys_ssetmask , 1) - MIPS_SYS(sys_setreuid , 2) /* 4070 */ - MIPS_SYS(sys_setregid , 2) - MIPS_SYS(sys_sigsuspend , 0) - MIPS_SYS(sys_sigpending , 1) - MIPS_SYS(sys_sethostname , 2) - MIPS_SYS(sys_setrlimit , 2) /* 4075 */ - MIPS_SYS(sys_getrlimit , 2) - MIPS_SYS(sys_getrusage , 2) - MIPS_SYS(sys_gettimeofday, 2) - MIPS_SYS(sys_settimeofday, 2) - MIPS_SYS(sys_getgroups , 2) /* 4080 */ - MIPS_SYS(sys_setgroups , 2) - MIPS_SYS(sys_ni_syscall , 0) /* old_select */ - MIPS_SYS(sys_symlink , 2) - MIPS_SYS(sys_ni_syscall , 0) /* was sys_lstat */ - MIPS_SYS(sys_readlink , 3) /* 4085 */ - MIPS_SYS(sys_uselib , 1) - MIPS_SYS(sys_swapon , 2) - MIPS_SYS(sys_reboot , 3) - MIPS_SYS(old_readdir , 3) - MIPS_SYS(old_mmap , 6) /* 4090 */ - MIPS_SYS(sys_munmap , 2) - MIPS_SYS(sys_truncate , 2) - MIPS_SYS(sys_ftruncate , 2) - MIPS_SYS(sys_fchmod , 2) - MIPS_SYS(sys_fchown , 3) /* 4095 */ - MIPS_SYS(sys_getpriority , 2) - MIPS_SYS(sys_setpriority , 3) - MIPS_SYS(sys_ni_syscall , 0) - MIPS_SYS(sys_statfs , 2) - MIPS_SYS(sys_fstatfs , 2) /* 4100 */ - MIPS_SYS(sys_ni_syscall , 0) /* was ioperm(2) */ - MIPS_SYS(sys_socketcall , 2) - MIPS_SYS(sys_syslog , 3) - MIPS_SYS(sys_setitimer , 3) - MIPS_SYS(sys_getitimer , 2) /* 4105 */ - MIPS_SYS(sys_newstat , 2) - MIPS_SYS(sys_newlstat , 2) - MIPS_SYS(sys_newfstat , 2) - MIPS_SYS(sys_uname , 1) - MIPS_SYS(sys_ni_syscall , 0) /* 4110 was iopl(2) */ - MIPS_SYS(sys_vhangup , 0) - MIPS_SYS(sys_ni_syscall , 0) /* was sys_idle() */ - MIPS_SYS(sys_ni_syscall , 0) /* was sys_vm86 */ - MIPS_SYS(sys_wait4 , 4) - MIPS_SYS(sys_swapoff , 1) /* 4115 */ - MIPS_SYS(sys_sysinfo , 1) - MIPS_SYS(sys_ipc , 6) - MIPS_SYS(sys_fsync , 1) - MIPS_SYS(sys_sigreturn , 0) - MIPS_SYS(sys_clone , 6) /* 4120 */ - MIPS_SYS(sys_setdomainname, 2) - MIPS_SYS(sys_newuname , 1) - MIPS_SYS(sys_ni_syscall , 0) /* sys_modify_ldt */ - MIPS_SYS(sys_adjtimex , 1) - MIPS_SYS(sys_mprotect , 3) /* 4125 */ - MIPS_SYS(sys_sigprocmask , 3) - MIPS_SYS(sys_ni_syscall , 0) /* was create_module */ - MIPS_SYS(sys_init_module , 5) - MIPS_SYS(sys_delete_module, 1) - MIPS_SYS(sys_ni_syscall , 0) /* 4130 was get_kernel_syms */ - MIPS_SYS(sys_quotactl , 0) - MIPS_SYS(sys_getpgid , 1) - MIPS_SYS(sys_fchdir , 1) - MIPS_SYS(sys_bdflush , 2) - MIPS_SYS(sys_sysfs , 3) /* 4135 */ - MIPS_SYS(sys_personality , 1) - MIPS_SYS(sys_ni_syscall , 0) /* for afs_syscall */ - MIPS_SYS(sys_setfsuid , 1) - MIPS_SYS(sys_setfsgid , 1) - MIPS_SYS(sys_llseek , 5) /* 4140 */ - MIPS_SYS(sys_getdents , 3) - MIPS_SYS(sys_select , 5) - MIPS_SYS(sys_flock , 2) - MIPS_SYS(sys_msync , 3) - MIPS_SYS(sys_readv , 3) /* 4145 */ - MIPS_SYS(sys_writev , 3) - MIPS_SYS(sys_cacheflush , 3) - MIPS_SYS(sys_cachectl , 3) - MIPS_SYS(sys_sysmips , 4) - MIPS_SYS(sys_ni_syscall , 0) /* 4150 */ - MIPS_SYS(sys_getsid , 1) - MIPS_SYS(sys_fdatasync , 0) - MIPS_SYS(sys_sysctl , 1) - MIPS_SYS(sys_mlock , 2) - MIPS_SYS(sys_munlock , 2) /* 4155 */ - MIPS_SYS(sys_mlockall , 1) - MIPS_SYS(sys_munlockall , 0) - MIPS_SYS(sys_sched_setparam, 2) - MIPS_SYS(sys_sched_getparam, 2) - MIPS_SYS(sys_sched_setscheduler, 3) /* 4160 */ - MIPS_SYS(sys_sched_getscheduler, 1) - MIPS_SYS(sys_sched_yield , 0) - MIPS_SYS(sys_sched_get_priority_max, 1) - MIPS_SYS(sys_sched_get_priority_min, 1) - MIPS_SYS(sys_sched_rr_get_interval, 2) /* 4165 */ - MIPS_SYS(sys_nanosleep, 2) - MIPS_SYS(sys_mremap , 5) - MIPS_SYS(sys_accept , 3) - MIPS_SYS(sys_bind , 3) - MIPS_SYS(sys_connect , 3) /* 4170 */ - MIPS_SYS(sys_getpeername , 3) - MIPS_SYS(sys_getsockname , 3) - MIPS_SYS(sys_getsockopt , 5) - MIPS_SYS(sys_listen , 2) - MIPS_SYS(sys_recv , 4) /* 4175 */ - MIPS_SYS(sys_recvfrom , 6) - MIPS_SYS(sys_recvmsg , 3) - MIPS_SYS(sys_send , 4) - MIPS_SYS(sys_sendmsg , 3) - MIPS_SYS(sys_sendto , 6) /* 4180 */ - MIPS_SYS(sys_setsockopt , 5) - MIPS_SYS(sys_shutdown , 2) - MIPS_SYS(sys_socket , 3) - MIPS_SYS(sys_socketpair , 4) - MIPS_SYS(sys_setresuid , 3) /* 4185 */ - MIPS_SYS(sys_getresuid , 3) - MIPS_SYS(sys_ni_syscall , 0) /* was sys_query_module */ - MIPS_SYS(sys_poll , 3) - MIPS_SYS(sys_nfsservctl , 3) - MIPS_SYS(sys_setresgid , 3) /* 4190 */ - MIPS_SYS(sys_getresgid , 3) - MIPS_SYS(sys_prctl , 5) - MIPS_SYS(sys_rt_sigreturn, 0) - MIPS_SYS(sys_rt_sigaction, 4) - MIPS_SYS(sys_rt_sigprocmask, 4) /* 4195 */ - MIPS_SYS(sys_rt_sigpending, 2) - MIPS_SYS(sys_rt_sigtimedwait, 4) - MIPS_SYS(sys_rt_sigqueueinfo, 3) - MIPS_SYS(sys_rt_sigsuspend, 0) - MIPS_SYS(sys_pread64 , 6) /* 4200 */ - MIPS_SYS(sys_pwrite64 , 6) - MIPS_SYS(sys_chown , 3) - MIPS_SYS(sys_getcwd , 2) - MIPS_SYS(sys_capget , 2) - MIPS_SYS(sys_capset , 2) /* 4205 */ - MIPS_SYS(sys_sigaltstack , 2) - MIPS_SYS(sys_sendfile , 4) - MIPS_SYS(sys_ni_syscall , 0) - MIPS_SYS(sys_ni_syscall , 0) - MIPS_SYS(sys_mmap2 , 6) /* 4210 */ - MIPS_SYS(sys_truncate64 , 4) - MIPS_SYS(sys_ftruncate64 , 4) - MIPS_SYS(sys_stat64 , 2) - MIPS_SYS(sys_lstat64 , 2) - MIPS_SYS(sys_fstat64 , 2) /* 4215 */ - MIPS_SYS(sys_pivot_root , 2) - MIPS_SYS(sys_mincore , 3) - MIPS_SYS(sys_madvise , 3) - MIPS_SYS(sys_getdents64 , 3) - MIPS_SYS(sys_fcntl64 , 3) /* 4220 */ - MIPS_SYS(sys_ni_syscall , 0) - MIPS_SYS(sys_gettid , 0) - MIPS_SYS(sys_readahead , 5) - MIPS_SYS(sys_setxattr , 5) - MIPS_SYS(sys_lsetxattr , 5) /* 4225 */ - MIPS_SYS(sys_fsetxattr , 5) - MIPS_SYS(sys_getxattr , 4) - MIPS_SYS(sys_lgetxattr , 4) - MIPS_SYS(sys_fgetxattr , 4) - MIPS_SYS(sys_listxattr , 3) /* 4230 */ - MIPS_SYS(sys_llistxattr , 3) - MIPS_SYS(sys_flistxattr , 3) - MIPS_SYS(sys_removexattr , 2) - MIPS_SYS(sys_lremovexattr, 2) - MIPS_SYS(sys_fremovexattr, 2) /* 4235 */ - MIPS_SYS(sys_tkill , 2) - MIPS_SYS(sys_sendfile64 , 5) - MIPS_SYS(sys_futex , 6) - MIPS_SYS(sys_sched_setaffinity, 3) - MIPS_SYS(sys_sched_getaffinity, 3) /* 4240 */ - MIPS_SYS(sys_io_setup , 2) - MIPS_SYS(sys_io_destroy , 1) - MIPS_SYS(sys_io_getevents, 5) - MIPS_SYS(sys_io_submit , 3) - MIPS_SYS(sys_io_cancel , 3) /* 4245 */ - MIPS_SYS(sys_exit_group , 1) - MIPS_SYS(sys_lookup_dcookie, 3) - MIPS_SYS(sys_epoll_create, 1) - MIPS_SYS(sys_epoll_ctl , 4) - MIPS_SYS(sys_epoll_wait , 3) /* 4250 */ - MIPS_SYS(sys_remap_file_pages, 5) - MIPS_SYS(sys_set_tid_address, 1) - MIPS_SYS(sys_restart_syscall, 0) - MIPS_SYS(sys_fadvise64_64, 7) - MIPS_SYS(sys_statfs64 , 3) /* 4255 */ - MIPS_SYS(sys_fstatfs64 , 2) - MIPS_SYS(sys_timer_create, 3) - MIPS_SYS(sys_timer_settime, 4) - MIPS_SYS(sys_timer_gettime, 2) - MIPS_SYS(sys_timer_getoverrun, 1) /* 4260 */ - MIPS_SYS(sys_timer_delete, 1) - MIPS_SYS(sys_clock_settime, 2) - MIPS_SYS(sys_clock_gettime, 2) - MIPS_SYS(sys_clock_getres, 2) - MIPS_SYS(sys_clock_nanosleep, 4) /* 4265 */ - MIPS_SYS(sys_tgkill , 3) - MIPS_SYS(sys_utimes , 2) - MIPS_SYS(sys_mbind , 4) - MIPS_SYS(sys_ni_syscall , 0) /* sys_get_mempolicy */ - MIPS_SYS(sys_ni_syscall , 0) /* 4270 sys_set_mempolicy */ - MIPS_SYS(sys_mq_open , 4) - MIPS_SYS(sys_mq_unlink , 1) - MIPS_SYS(sys_mq_timedsend, 5) - MIPS_SYS(sys_mq_timedreceive, 5) - MIPS_SYS(sys_mq_notify , 2) /* 4275 */ - MIPS_SYS(sys_mq_getsetattr, 3) - MIPS_SYS(sys_ni_syscall , 0) /* sys_vserver */ - MIPS_SYS(sys_waitid , 4) - MIPS_SYS(sys_ni_syscall , 0) /* available, was setaltroot */ - MIPS_SYS(sys_add_key , 5) - MIPS_SYS(sys_request_key, 4) - MIPS_SYS(sys_keyctl , 5) - MIPS_SYS(sys_set_thread_area, 1) - MIPS_SYS(sys_inotify_init, 0) - MIPS_SYS(sys_inotify_add_watch, 3) /* 4285 */ - MIPS_SYS(sys_inotify_rm_watch, 2) - MIPS_SYS(sys_migrate_pages, 4) - MIPS_SYS(sys_openat, 4) - MIPS_SYS(sys_mkdirat, 3) - MIPS_SYS(sys_mknodat, 4) /* 4290 */ - MIPS_SYS(sys_fchownat, 5) - MIPS_SYS(sys_futimesat, 3) - MIPS_SYS(sys_fstatat64, 4) - MIPS_SYS(sys_unlinkat, 3) - MIPS_SYS(sys_renameat, 4) /* 4295 */ - MIPS_SYS(sys_linkat, 5) - MIPS_SYS(sys_symlinkat, 3) - MIPS_SYS(sys_readlinkat, 4) - MIPS_SYS(sys_fchmodat, 3) - MIPS_SYS(sys_faccessat, 3) /* 4300 */ - MIPS_SYS(sys_pselect6, 6) - MIPS_SYS(sys_ppoll, 5) - MIPS_SYS(sys_unshare, 1) - MIPS_SYS(sys_splice, 6) - MIPS_SYS(sys_sync_file_range, 7) /* 4305 */ - MIPS_SYS(sys_tee, 4) - MIPS_SYS(sys_vmsplice, 4) - MIPS_SYS(sys_move_pages, 6) - MIPS_SYS(sys_set_robust_list, 2) - MIPS_SYS(sys_get_robust_list, 3) /* 4310 */ - MIPS_SYS(sys_kexec_load, 4) - MIPS_SYS(sys_getcpu, 3) - MIPS_SYS(sys_epoll_pwait, 6) - MIPS_SYS(sys_ioprio_set, 3) - MIPS_SYS(sys_ioprio_get, 2) - MIPS_SYS(sys_utimensat, 4) - MIPS_SYS(sys_signalfd, 3) - MIPS_SYS(sys_ni_syscall, 0) /* was timerfd */ - MIPS_SYS(sys_eventfd, 1) - MIPS_SYS(sys_fallocate, 6) /* 4320 */ - MIPS_SYS(sys_timerfd_create, 2) - MIPS_SYS(sys_timerfd_gettime, 2) - MIPS_SYS(sys_timerfd_settime, 4) - MIPS_SYS(sys_signalfd4, 4) - MIPS_SYS(sys_eventfd2, 2) /* 4325 */ - MIPS_SYS(sys_epoll_create1, 1) - MIPS_SYS(sys_dup3, 3) - MIPS_SYS(sys_pipe2, 2) - MIPS_SYS(sys_inotify_init1, 1) - MIPS_SYS(sys_preadv, 5) /* 4330 */ - MIPS_SYS(sys_pwritev, 5) - MIPS_SYS(sys_rt_tgsigqueueinfo, 4) - MIPS_SYS(sys_perf_event_open, 5) - MIPS_SYS(sys_accept4, 4) - MIPS_SYS(sys_recvmmsg, 5) /* 4335 */ - MIPS_SYS(sys_fanotify_init, 2) - MIPS_SYS(sys_fanotify_mark, 6) - MIPS_SYS(sys_prlimit64, 4) - MIPS_SYS(sys_name_to_handle_at, 5) - MIPS_SYS(sys_open_by_handle_at, 3) /* 4340 */ - MIPS_SYS(sys_clock_adjtime, 2) - MIPS_SYS(sys_syncfs, 1) - MIPS_SYS(sys_sendmmsg, 4) - MIPS_SYS(sys_setns, 2) - MIPS_SYS(sys_process_vm_readv, 6) /* 345 */ - MIPS_SYS(sys_process_vm_writev, 6) - MIPS_SYS(sys_kcmp, 5) - MIPS_SYS(sys_finit_module, 3) - MIPS_SYS(sys_sched_setattr, 2) - MIPS_SYS(sys_sched_getattr, 3) /* 350 */ - MIPS_SYS(sys_renameat2, 5) - MIPS_SYS(sys_seccomp, 3) - MIPS_SYS(sys_getrandom, 3) - MIPS_SYS(sys_memfd_create, 2) - MIPS_SYS(sys_bpf, 3) /* 355 */ - MIPS_SYS(sys_execveat, 5) - MIPS_SYS(sys_userfaultfd, 1) - MIPS_SYS(sys_membarrier, 2) - MIPS_SYS(sys_mlock2, 3) - MIPS_SYS(sys_copy_file_range, 6) /* 360 */ - MIPS_SYS(sys_preadv2, 6) - MIPS_SYS(sys_pwritev2, 6) +# define MIPS_SYSCALL_NUMBER_UNUSED -1 +static const int8_t mips_syscall_args[] = { +#include "syscall-args-o32.c.inc" }; -# undef MIPS_SYS # endif /* O32 */ /* Break codes */ @@ -447,8 +84,14 @@ void cpu_loop(CPUMIPSState *env) # ifdef TARGET_ABI_MIPSO32 syscall_num = env->active_tc.gpr[2] - 4000; if (syscall_num >= sizeof(mips_syscall_args)) { + /* syscall_num is larger that any defined for MIPS O32 */ + ret = -TARGET_ENOSYS; + } else if (mips_syscall_args[syscall_num] == + MIPS_SYSCALL_NUMBER_UNUSED) { + /* syscall_num belongs to the range not defined for MIPS O32 */ ret = -TARGET_ENOSYS; } else { + /* syscall_num is valid */ int nb_args; abi_ulong sp_reg; abi_ulong arg5 = 0, arg6 = 0, arg7 = 0, arg8 = 0; diff --git a/linux-user/mips/syscall-args-o32.c.inc b/linux-user/mips/syscall-args-o32.c.inc new file mode 100644 index 0000000000..0ad35857b4 --- /dev/null +++ b/linux-user/mips/syscall-args-o32.c.inc @@ -0,0 +1,436 @@ + [ 0] = 7, /* syscall */ + [ 1] = 1, /* exit */ + [ 2] = 0, /* fork */ + [ 3] = 3, /* read */ + [ 4] = 3, /* write */ + [ 5] = 3, /* open */ + [ 6] = 1, /* close */ + [ 7] = 3, /* waitpid */ + [ 8] = 2, /* creat */ + [ 9] = 2, /* link */ + [ 10] = 1, /* unlink */ + [ 11] = 3, /* execve */ + [ 12] = 1, /* chdir */ + [ 13] = 1, /* time */ + [ 14] = 3, /* mknod */ + [ 15] = 2, /* chmod */ + [ 16] = 3, /* lchown */ + [ 17] = 0, /* break */ + [ 18] = 2, /* oldstat */ + [ 19] = 3, /* lseek */ + [ 20] = 0, /* getpid */ + [ 21] = 5, /* mount */ + [ 22] = 1, /* umount */ + [ 23] = 1, /* setuid */ + [ 24] = 0, /* getuid */ + [ 25] = 1, /* stime */ + [ 26] = 4, /* ptrace */ + [ 27] = 1, /* alarm */ + [ 28] = 2, /* oldfstat */ + [ 29] = 0, /* pause */ + [ 30] = 2, /* utime */ + [ 31] = 0, /* stty */ + [ 32] = 0, /* gtty */ + [ 33] = 2, /* access */ + [ 34] = 1, /* nice */ + [ 35] = 1, /* ftime */ + [ 36] = 0, /* sync */ + [ 37] = 2, /* kill */ + [ 38] = 2, /* rename */ + [ 39] = 2, /* mkdir */ + [ 40] = 1, /* rmdir */ + [ 41] = 1, /* dup */ + [ 42] = 0, /* pipe */ + [ 43] = 1, /* times */ + [ 44] = 0, /* prof */ + [ 45] = 1, /* brk */ + [ 46] = 1, /* setgid */ + [ 47] = 0, /* getgid */ + [ 48] = 2, /* signal */ + [ 49] = 0, /* geteuid */ + [ 50] = 0, /* getegid */ + [ 51] = 1, /* acct */ + [ 52] = 2, /* umount2 */ + [ 53] = 0, /* lock */ + [ 54] = 3, /* ioctl */ + [ 55] = 3, /* fcntl */ + [ 56] = 0, /* mpx */ + [ 57] = 2, /* setpgid */ + [ 58] = 0, /* ulimit */ + [ 59] = 1, /* oldolduname */ + [ 60] = 1, /* umask */ + [ 61] = 1, /* chroot */ + [ 62] = 2, /* ustat */ + [ 63] = 2, /* dup2 */ + [ 64] = 0, /* getppid */ + [ 65] = 0, /* getpgrp */ + [ 66] = 0, /* setsid */ + [ 67] = 3, /* sigaction */ + [ 68] = 0, /* sgetmask */ + [ 69] = 1, /* ssetmask */ + [ 70] = 2, /* setreuid */ + [ 71] = 2, /* setregid */ + [ 72] = 1, /* sigsuspend */ + [ 73] = 1, /* sigpending */ + [ 74] = 2, /* sethostname */ + [ 75] = 2, /* setrlimit */ + [ 76] = 2, /* getrlimit */ + [ 77] = 2, /* getrusage */ + [ 78] = 2, /* gettimeofday */ + [ 79] = 2, /* settimeofday */ + [ 80] = 2, /* getgroups */ + [ 81] = 2, /* setgroups */ + [ 82] = 0, /* reserved82 */ + [ 83] = 2, /* symlink */ + [ 84] = 2, /* oldlstat */ + [ 85] = 3, /* readlink */ + [ 86] = 1, /* uselib */ + [ 87] = 2, /* swapon */ + [ 88] = 4, /* reboot */ + [ 89] = 3, /* readdir */ + [ 90] = 6, /* mmap */ + [ 91] = 2, /* munmap */ + [ 92] = 2, /* truncate */ + [ 93] = 2, /* ftruncate */ + [ 94] = 2, /* fchmod */ + [ 95] = 3, /* fchown */ + [ 96] = 2, /* getpriority */ + [ 97] = 3, /* setpriority */ + [ 98] = 0, /* profil */ + [ 99] = 2, /* statfs */ + [ 100] = 2, /* fstatfs */ + [ 101] = 3, /* ioperm */ + [ 102] = 2, /* socketcall */ + [ 103] = 3, /* syslog */ + [ 104] = 3, /* setitimer */ + [ 105] = 2, /* getitimer */ + [ 106] = 2, /* stat */ + [ 107] = 2, /* lstat */ + [ 108] = 2, /* fstat */ + [ 109] = 1, /* olduname */ + [ 110] = 1, /* iopl */ + [ 111] = 0, /* vhangup */ + [ 112] = 0, /* idle */ + [ 113] = 5, /* vm86 */ + [ 114] = 4, /* wait4 */ + [ 115] = 1, /* swapoff */ + [ 116] = 1, /* sysinfo */ + [ 117] = 6, /* ipc */ + [ 118] = 1, /* fsync */ + [ 119] = 0, /* sigreturn */ + [ 120] = 5, /* clone */ + [ 121] = 2, /* setdomainname */ + [ 122] = 1, /* uname */ + [ 123] = 0, /* modify_ldt */ + [ 124] = 1, /* adjtimex */ + [ 125] = 3, /* mprotect */ + [ 126] = 3, /* sigprocmask */ + [ 127] = 2, /* create_module */ + [ 128] = 3, /* init_module */ + [ 129] = 2, /* delete_module */ + [ 130] = 1, /* get_kernel_syms */ + [ 131] = 4, /* quotactl */ + [ 132] = 1, /* getpgid */ + [ 133] = 1, /* fchdir */ + [ 134] = 2, /* bdflush */ + [ 135] = 3, /* sysfs */ + [ 136] = 1, /* personality */ + [ 137] = 0, /* afs_syscall */ + [ 138] = 1, /* setfsuid */ + [ 139] = 1, /* setfsgid */ + [ 140] = 5, /* _llseek */ + [ 141] = 3, /* getdents */ + [ 142] = 5, /* _newselect */ + [ 143] = 2, /* flock */ + [ 144] = 3, /* msync */ + [ 145] = 3, /* readv */ + [ 146] = 3, /* writev */ + [ 147] = 3, /* cacheflush */ + [ 148] = 3, /* cachectl */ + [ 149] = 4, /* sysmips */ + [ 150] = 0, /* setup */ + [ 151] = 1, /* getsid */ + [ 152] = 1, /* fdatasync */ + [ 153] = 1, /* _sysctl */ + [ 154] = 2, /* mlock */ + [ 155] = 2, /* munlock */ + [ 156] = 1, /* mlockall */ + [ 157] = 0, /* munlockall */ + [ 158] = 2, /* sched_setparam */ + [ 159] = 2, /* sched_getparam */ + [ 160] = 3, /* sched_setscheduler */ + [ 161] = 1, /* sched_getscheduler */ + [ 162] = 0, /* sched_yield */ + [ 163] = 1, /* sched_get_priority_max */ + [ 164] = 1, /* sched_get_priority_min */ + [ 165] = 2, /* sched_rr_get_interval */ + [ 166] = 2, /* nanosleep */ + [ 167] = 5, /* mremap */ + [ 168] = 3, /* accept */ + [ 169] = 3, /* bind */ + [ 170] = 3, /* connect */ + [ 171] = 3, /* getpeername */ + [ 172] = 3, /* getsockname */ + [ 173] = 5, /* getsockopt */ + [ 174] = 2, /* listen */ + [ 175] = 4, /* recv */ + [ 176] = 6, /* recvfrom */ + [ 177] = 3, /* recvmsg */ + [ 178] = 4, /* send */ + [ 179] = 3, /* sendmsg */ + [ 180] = 6, /* sendto */ + [ 181] = 5, /* setsockopt */ + [ 182] = 2, /* shutdown */ + [ 183] = 3, /* socket */ + [ 184] = 4, /* socketpair */ + [ 185] = 3, /* setresuid */ + [ 186] = 3, /* getresuid */ + [ 187] = 5, /* query_module */ + [ 188] = 3, /* poll */ + [ 189] = 3, /* nfsservctl */ + [ 190] = 3, /* setresgid */ + [ 191] = 3, /* getresgid */ + [ 192] = 5, /* prctl */ + [ 193] = 0, /* rt_sigreturn */ + [ 194] = 4, /* rt_sigaction */ + [ 195] = 4, /* rt_sigprocmask */ + [ 196] = 2, /* rt_sigpending */ + [ 197] = 4, /* rt_sigtimedwait */ + [ 198] = 3, /* rt_sigqueueinfo */ + [ 199] = 2, /* rt_sigsuspend */ + [ 200] = 6, /* pread64 */ + [ 201] = 6, /* pwrite64 */ + [ 202] = 3, /* chown */ + [ 203] = 2, /* getcwd */ + [ 204] = 2, /* capget */ + [ 205] = 2, /* capset */ + [ 206] = 2, /* sigaltstack */ + [ 207] = 4, /* sendfile */ + [ 208] = 5, /* getpmsg */ + [ 209] = 5, /* putpmsg */ + [ 210] = 6, /* mmap2 */ + [ 211] = 4, /* truncate64 */ + [ 212] = 4, /* ftruncate64 */ + [ 213] = 2, /* stat64 */ + [ 214] = 2, /* lstat64 */ + [ 215] = 2, /* fstat64 */ + [ 216] = 2, /* pivot_root */ + [ 217] = 3, /* mincore */ + [ 218] = 3, /* madvise */ + [ 219] = 3, /* getdents64 */ + [ 220] = 3, /* fcntl64 */ + [ 221] = 0, /* reserved221 */ + [ 222] = 0, /* gettid */ + [ 223] = 5, /* readahead */ + [ 224] = 5, /* setxattr */ + [ 225] = 5, /* lsetxattr */ + [ 226] = 5, /* fsetxattr */ + [ 227] = 4, /* getxattr */ + [ 228] = 4, /* lgetxattr */ + [ 229] = 4, /* fgetxattr */ + [ 230] = 3, /* listxattr */ + [ 231] = 3, /* llistxattr */ + [ 232] = 3, /* flistxattr */ + [ 233] = 2, /* removexattr */ + [ 234] = 2, /* lremovexattr */ + [ 235] = 2, /* fremovexattr */ + [ 236] = 2, /* tkill */ + [ 237] = 4, /* sendfile64 */ + [ 238] = 6, /* futex */ + [ 239] = 3, /* sched_setaffinity */ + [ 240] = 3, /* sched_getaffinity */ + [ 241] = 2, /* io_setup */ + [ 242] = 1, /* io_destroy */ + [ 243] = 5, /* io_getevents */ + [ 244] = 3, /* io_submit */ + [ 245] = 3, /* io_cancel */ + [ 246] = 1, /* exit_group */ + [ 247] = 4, /* lookup_dcookie */ + [ 248] = 1, /* epoll_create */ + [ 249] = 4, /* epoll_ctl */ + [ 250] = 4, /* epoll_wait */ + [ 251] = 5, /* remap_file_pages */ + [ 252] = 1, /* set_tid_address */ + [ 253] = 0, /* restart_syscall */ + [ 254] = 7, /* fadvise64 */ + [ 255] = 3, /* statfs64 */ + [ 256] = 3, /* fstatfs64 */ + [ 257] = 3, /* timer_create */ + [ 258] = 4, /* timer_settime */ + [ 259] = 2, /* timer_gettime */ + [ 260] = 1, /* timer_getoverrun */ + [ 261] = 1, /* timer_delete */ + [ 262] = 2, /* clock_settime */ + [ 263] = 2, /* clock_gettime */ + [ 264] = 2, /* clock_getres */ + [ 265] = 4, /* clock_nanosleep */ + [ 266] = 3, /* tgkill */ + [ 267] = 2, /* utimes */ + [ 268] = 6, /* mbind */ + [ 269] = 5, /* get_mempolicy */ + [ 270] = 3, /* set_mempolicy */ + [ 271] = 4, /* mq_open */ + [ 272] = 1, /* mq_unlink */ + [ 273] = 5, /* mq_timedsend */ + [ 274] = 5, /* mq_timedreceive */ + [ 275] = 2, /* mq_notify */ + [ 276] = 3, /* mq_getsetattr */ + [ 277] = 5, /* vserver */ + [ 278] = 5, /* waitid */ + [ 279] = MIPS_SYSCALL_NUMBER_UNUSED, + [ 280] = 5, /* add_key */ + [ 281] = 4, /* request_key */ + [ 282] = 5, /* keyctl */ + [ 283] = 1, /* set_thread_area */ + [ 284] = 0, /* inotify_init */ + [ 285] = 3, /* inotify_add_watch */ + [ 286] = 2, /* inotify_rm_watch */ + [ 287] = 4, /* migrate_pages */ + [ 288] = 4, /* openat */ + [ 289] = 3, /* mkdirat */ + [ 290] = 4, /* mknodat */ + [ 291] = 5, /* fchownat */ + [ 292] = 3, /* futimesat */ + [ 293] = 4, /* fstatat64 */ + [ 294] = 3, /* unlinkat */ + [ 295] = 4, /* renameat */ + [ 296] = 5, /* linkat */ + [ 297] = 3, /* symlinkat */ + [ 298] = 4, /* readlinkat */ + [ 299] = 3, /* fchmodat */ + [ 300] = 3, /* faccessat */ + [ 301] = 6, /* pselect6 */ + [ 302] = 5, /* ppoll */ + [ 303] = 1, /* unshare */ + [ 304] = 6, /* splice */ + [ 305] = 7, /* sync_file_range */ + [ 306] = 4, /* tee */ + [ 307] = 4, /* vmsplice */ + [ 308] = 6, /* move_pages */ + [ 309] = 2, /* set_robust_list */ + [ 310] = 3, /* get_robust_list */ + [ 311] = 4, /* kexec_load */ + [ 312] = 3, /* getcpu */ + [ 313] = 6, /* epoll_pwait */ + [ 314] = 3, /* ioprio_set */ + [ 315] = 2, /* ioprio_get */ + [ 316] = 4, /* utimensat */ + [ 317] = 3, /* signalfd */ + [ 318] = 4, /* timerfd */ + [ 319] = 1, /* eventfd */ + [ 320] = 6, /* fallocate */ + [ 321] = 2, /* timerfd_create */ + [ 322] = 2, /* timerfd_gettime */ + [ 323] = 4, /* timerfd_settime */ + [ 324] = 4, /* signalfd4 */ + [ 325] = 2, /* eventfd2 */ + [ 326] = 1, /* epoll_create1 */ + [ 327] = 3, /* dup3 */ + [ 328] = 2, /* pipe2 */ + [ 329] = 1, /* inotify_init1 */ + [ 330] = 5, /* preadv */ + [ 331] = 5, /* pwritev */ + [ 332] = 4, /* rt_tgsigqueueinfo */ + [ 333] = 5, /* perf_event_open */ + [ 334] = 4, /* accept4 */ + [ 335] = 5, /* recvmmsg */ + [ 336] = 2, /* fanotify_init */ + [ 337] = 6, /* fanotify_mark */ + [ 338] = 4, /* prlimit64 */ + [ 339] = 5, /* name_to_handle_at */ + [ 340] = 3, /* open_by_handle_at */ + [ 341] = 2, /* clock_adjtime */ + [ 342] = 1, /* syncfs */ + [ 343] = 4, /* sendmmsg */ + [ 344] = 2, /* setns */ + [ 345] = 6, /* process_vm_readv */ + [ 346] = 6, /* process_vm_writev */ + [ 347] = 5, /* kcmp */ + [ 348] = 3, /* finit_module */ + [ 349] = 3, /* sched_setattr */ + [ 350] = 4, /* sched_getattr */ + [ 351] = 5, /* renameat2 */ + [ 352] = 3, /* seccomp */ + [ 353] = 3, /* getrandom */ + [ 354] = 2, /* memfd_create */ + [ 355] = 3, /* bpf */ + [ 356] = 5, /* execveat */ + [ 357] = 1, /* userfaultfd */ + [ 358] = 2, /* membarrier */ + [ 359] = 3, /* mlock2 */ + [ 360] = 6, /* copy_file_range */ + [ 361] = 6, /* preadv2 */ + [ 362] = 6, /* pwritev2 */ + [ 363] = 4, /* pkey_mprotect */ + [ 364] = 2, /* pkey_alloc */ + [ 365] = 1, /* pkey_free */ + [ 366] = 5, /* statx */ + [ 367] = 4, /* rseq */ + [ 368] = 6, /* io_pgetevents */ + [ 369] = MIPS_SYSCALL_NUMBER_UNUSED, + [ 370] = MIPS_SYSCALL_NUMBER_UNUSED, + [ 371] = MIPS_SYSCALL_NUMBER_UNUSED, + [ 372] = MIPS_SYSCALL_NUMBER_UNUSED, + [ 373] = MIPS_SYSCALL_NUMBER_UNUSED, + [ 374] = MIPS_SYSCALL_NUMBER_UNUSED, + [ 375] = MIPS_SYSCALL_NUMBER_UNUSED, + [ 376] = MIPS_SYSCALL_NUMBER_UNUSED, + [ 377] = MIPS_SYSCALL_NUMBER_UNUSED, + [ 378] = MIPS_SYSCALL_NUMBER_UNUSED, + [ 379] = MIPS_SYSCALL_NUMBER_UNUSED, + [ 380] = MIPS_SYSCALL_NUMBER_UNUSED, + [ 381] = MIPS_SYSCALL_NUMBER_UNUSED, + [ 382] = MIPS_SYSCALL_NUMBER_UNUSED, + [ 383] = MIPS_SYSCALL_NUMBER_UNUSED, + [ 384] = MIPS_SYSCALL_NUMBER_UNUSED, + [ 385] = MIPS_SYSCALL_NUMBER_UNUSED, + [ 386] = MIPS_SYSCALL_NUMBER_UNUSED, + [ 387] = MIPS_SYSCALL_NUMBER_UNUSED, + [ 388] = MIPS_SYSCALL_NUMBER_UNUSED, + [ 389] = MIPS_SYSCALL_NUMBER_UNUSED, + [ 390] = MIPS_SYSCALL_NUMBER_UNUSED, + [ 391] = MIPS_SYSCALL_NUMBER_UNUSED, + [ 392] = MIPS_SYSCALL_NUMBER_UNUSED, + [ 393] = 3, /* semget */ + [ 394] = 4, /* semctl */ + [ 395] = 3, /* shmget */ + [ 396] = 3, /* shmctl */ + [ 397] = 3, /* shmat */ + [ 398] = 1, /* shmdt */ + [ 399] = 2, /* msgget */ + [ 400] = 4, /* msgsnd */ + [ 401] = 5, /* msgrcv */ + [ 402] = 3, /* msgctl */ + [ 403] = 2, /* clock_gettime64 */ + [ 404] = 2, /* clock_settime64 */ + [ 405] = 2, /* clock_adjtime64 */ + [ 406] = 2, /* clock_getres_time64 */ + [ 407] = 4, /* clock_nanosleep_time64 */ + [ 408] = 2, /* timer_gettime64 */ + [ 409] = 4, /* timer_settime64 */ + [ 410] = 2, /* timerfd_gettime64 */ + [ 411] = 4, /* timerfd_settime64 */ + [ 412] = 4, /* utimensat_time64 */ + [ 413] = 6, /* pselect6_time64 */ + [ 414] = 5, /* ppoll_time64 */ + [ 415] = MIPS_SYSCALL_NUMBER_UNUSED, + [ 416] = 6, /* io_pgetevents_time64 */ + [ 417] = 5, /* recvmmsg_time64 */ + [ 418] = 5, /* mq_timedsend_time64 */ + [ 419] = 5, /* mq_timedreceive_time64 */ + [ 420] = 4, /* semtimedop_time64 */ + [ 421] = 4, /* rt_sigtimedwait_time64 */ + [ 422] = 6, /* futex_time64 */ + [ 423] = 2, /* sched_rr_get_interval_time64 */ + [ 424] = 4, /* pidfd_send_signal */ + [ 425] = 2, /* io_uring_setup */ + [ 426] = 6, /* io_uring_enter */ + [ 427] = 4, /* io_uring_register */ + [ 428] = 3, /* open_tree */ + [ 429] = 5, /* move_mount */ + [ 430] = 2, /* fsopen */ + [ 431] = 5, /* fsconfig */ + [ 432] = 3, /* fsmount */ + [ 433] = 3, /* fspick */ + [ 434] = 2, /* pidfd_open */ + [ 435] = 2, /* clone3 */ diff --git a/linux-user/mips/syscall_nr.h b/linux-user/mips/syscall_nr.h deleted file mode 100644 index 7fa7fa5a86..0000000000 --- a/linux-user/mips/syscall_nr.h +++ /dev/null @@ -1,380 +0,0 @@ -/* - * Linux o32 style syscalls are in the range from 4000 to 4999. - */ - -#ifndef LINUX_USER_MIPS_SYSCALL_NR_H -#define LINUX_USER_MIPS_SYSCALL_NR_H - -#define TARGET_NR_Linux 4000 -#define TARGET_NR_syscall (TARGET_NR_Linux + 0) -#define TARGET_NR_exit (TARGET_NR_Linux + 1) -#define TARGET_NR_fork (TARGET_NR_Linux + 2) -#define TARGET_NR_read (TARGET_NR_Linux + 3) -#define TARGET_NR_write (TARGET_NR_Linux + 4) -#define TARGET_NR_open (TARGET_NR_Linux + 5) -#define TARGET_NR_close (TARGET_NR_Linux + 6) -#define TARGET_NR_waitpid (TARGET_NR_Linux + 7) -#define TARGET_NR_creat (TARGET_NR_Linux + 8) -#define TARGET_NR_link (TARGET_NR_Linux + 9) -#define TARGET_NR_unlink (TARGET_NR_Linux + 10) -#define TARGET_NR_execve (TARGET_NR_Linux + 11) -#define TARGET_NR_chdir (TARGET_NR_Linux + 12) -#define TARGET_NR_time (TARGET_NR_Linux + 13) -#define TARGET_NR_mknod (TARGET_NR_Linux + 14) -#define TARGET_NR_chmod (TARGET_NR_Linux + 15) -#define TARGET_NR_lchown (TARGET_NR_Linux + 16) -#define TARGET_NR_break (TARGET_NR_Linux + 17) -#define TARGET_NR_unused18 (TARGET_NR_Linux + 18) -#define TARGET_NR_lseek (TARGET_NR_Linux + 19) -#define TARGET_NR_getpid (TARGET_NR_Linux + 20) -#define TARGET_NR_mount (TARGET_NR_Linux + 21) -#define TARGET_NR_umount (TARGET_NR_Linux + 22) -#define TARGET_NR_setuid (TARGET_NR_Linux + 23) -#define TARGET_NR_getuid (TARGET_NR_Linux + 24) -#define TARGET_NR_stime (TARGET_NR_Linux + 25) -#define TARGET_NR_ptrace (TARGET_NR_Linux + 26) -#define TARGET_NR_alarm (TARGET_NR_Linux + 27) -#define TARGET_NR_unused28 (TARGET_NR_Linux + 28) -#define TARGET_NR_pause (TARGET_NR_Linux + 29) -#define TARGET_NR_utime (TARGET_NR_Linux + 30) -#define TARGET_NR_stty (TARGET_NR_Linux + 31) -#define TARGET_NR_gtty (TARGET_NR_Linux + 32) -#define TARGET_NR_access (TARGET_NR_Linux + 33) -#define TARGET_NR_nice (TARGET_NR_Linux + 34) -#define TARGET_NR_ftime (TARGET_NR_Linux + 35) -#define TARGET_NR_sync (TARGET_NR_Linux + 36) -#define TARGET_NR_kill (TARGET_NR_Linux + 37) -#define TARGET_NR_rename (TARGET_NR_Linux + 38) -#define TARGET_NR_mkdir (TARGET_NR_Linux + 39) -#define TARGET_NR_rmdir (TARGET_NR_Linux + 40) -#define TARGET_NR_dup (TARGET_NR_Linux + 41) -#define TARGET_NR_pipe (TARGET_NR_Linux + 42) -#define TARGET_NR_times (TARGET_NR_Linux + 43) -#define TARGET_NR_prof (TARGET_NR_Linux + 44) -#define TARGET_NR_brk (TARGET_NR_Linux + 45) -#define TARGET_NR_setgid (TARGET_NR_Linux + 46) -#define TARGET_NR_getgid (TARGET_NR_Linux + 47) -#define TARGET_NR_signal (TARGET_NR_Linux + 48) -#define TARGET_NR_geteuid (TARGET_NR_Linux + 49) -#define TARGET_NR_getegid (TARGET_NR_Linux + 50) -#define TARGET_NR_acct (TARGET_NR_Linux + 51) -#define TARGET_NR_umount2 (TARGET_NR_Linux + 52) -#define TARGET_NR_lock (TARGET_NR_Linux + 53) -#define TARGET_NR_ioctl (TARGET_NR_Linux + 54) -#define TARGET_NR_fcntl (TARGET_NR_Linux + 55) -#define TARGET_NR_mpx (TARGET_NR_Linux + 56) -#define TARGET_NR_setpgid (TARGET_NR_Linux + 57) -#define TARGET_NR_ulimit (TARGET_NR_Linux + 58) -#define TARGET_NR_unused59 (TARGET_NR_Linux + 59) -#define TARGET_NR_umask (TARGET_NR_Linux + 60) -#define TARGET_NR_chroot (TARGET_NR_Linux + 61) -#define TARGET_NR_ustat (TARGET_NR_Linux + 62) -#define TARGET_NR_dup2 (TARGET_NR_Linux + 63) -#define TARGET_NR_getppid (TARGET_NR_Linux + 64) -#define TARGET_NR_getpgrp (TARGET_NR_Linux + 65) -#define TARGET_NR_setsid (TARGET_NR_Linux + 66) -#define TARGET_NR_sigaction (TARGET_NR_Linux + 67) -#define TARGET_NR_sgetmask (TARGET_NR_Linux + 68) -#define TARGET_NR_ssetmask (TARGET_NR_Linux + 69) -#define TARGET_NR_setreuid (TARGET_NR_Linux + 70) -#define TARGET_NR_setregid (TARGET_NR_Linux + 71) -#define TARGET_NR_sigsuspend (TARGET_NR_Linux + 72) -#define TARGET_NR_sigpending (TARGET_NR_Linux + 73) -#define TARGET_NR_sethostname (TARGET_NR_Linux + 74) -#define TARGET_NR_setrlimit (TARGET_NR_Linux + 75) -#define TARGET_NR_getrlimit (TARGET_NR_Linux + 76) -#define TARGET_NR_getrusage (TARGET_NR_Linux + 77) -#define TARGET_NR_gettimeofday (TARGET_NR_Linux + 78) -#define TARGET_NR_settimeofday (TARGET_NR_Linux + 79) -#define TARGET_NR_getgroups (TARGET_NR_Linux + 80) -#define TARGET_NR_setgroups (TARGET_NR_Linux + 81) -#define TARGET_NR_reserved82 (TARGET_NR_Linux + 82) -#define TARGET_NR_symlink (TARGET_NR_Linux + 83) -#define TARGET_NR_unused84 (TARGET_NR_Linux + 84) -#define TARGET_NR_readlink (TARGET_NR_Linux + 85) -#define TARGET_NR_uselib (TARGET_NR_Linux + 86) -#define TARGET_NR_swapon (TARGET_NR_Linux + 87) -#define TARGET_NR_reboot (TARGET_NR_Linux + 88) -#define TARGET_NR_readdir (TARGET_NR_Linux + 89) -#define TARGET_NR_mmap (TARGET_NR_Linux + 90) -#define TARGET_NR_munmap (TARGET_NR_Linux + 91) -#define TARGET_NR_truncate (TARGET_NR_Linux + 92) -#define TARGET_NR_ftruncate (TARGET_NR_Linux + 93) -#define TARGET_NR_fchmod (TARGET_NR_Linux + 94) -#define TARGET_NR_fchown (TARGET_NR_Linux + 95) -#define TARGET_NR_getpriority (TARGET_NR_Linux + 96) -#define TARGET_NR_setpriority (TARGET_NR_Linux + 97) -#define TARGET_NR_profil (TARGET_NR_Linux + 98) -#define TARGET_NR_statfs (TARGET_NR_Linux + 99) -#define TARGET_NR_fstatfs (TARGET_NR_Linux + 100) -#define TARGET_NR_ioperm (TARGET_NR_Linux + 101) -#define TARGET_NR_socketcall (TARGET_NR_Linux + 102) -#define TARGET_NR_syslog (TARGET_NR_Linux + 103) -#define TARGET_NR_setitimer (TARGET_NR_Linux + 104) -#define TARGET_NR_getitimer (TARGET_NR_Linux + 105) -#define TARGET_NR_stat (TARGET_NR_Linux + 106) -#define TARGET_NR_lstat (TARGET_NR_Linux + 107) -#define TARGET_NR_fstat (TARGET_NR_Linux + 108) -#define TARGET_NR_unused109 (TARGET_NR_Linux + 109) -#define TARGET_NR_iopl (TARGET_NR_Linux + 110) -#define TARGET_NR_vhangup (TARGET_NR_Linux + 111) -#define TARGET_NR_idle (TARGET_NR_Linux + 112) -#define TARGET_NR_vm86 (TARGET_NR_Linux + 113) -#define TARGET_NR_wait4 (TARGET_NR_Linux + 114) -#define TARGET_NR_swapoff (TARGET_NR_Linux + 115) -#define TARGET_NR_sysinfo (TARGET_NR_Linux + 116) -#define TARGET_NR_ipc (TARGET_NR_Linux + 117) -#define TARGET_NR_fsync (TARGET_NR_Linux + 118) -#define TARGET_NR_sigreturn (TARGET_NR_Linux + 119) -#define TARGET_NR_clone (TARGET_NR_Linux + 120) -#define TARGET_NR_setdomainname (TARGET_NR_Linux + 121) -#define TARGET_NR_uname (TARGET_NR_Linux + 122) -#define TARGET_NR_modify_ldt (TARGET_NR_Linux + 123) -#define TARGET_NR_adjtimex (TARGET_NR_Linux + 124) -#define TARGET_NR_mprotect (TARGET_NR_Linux + 125) -#define TARGET_NR_sigprocmask (TARGET_NR_Linux + 126) -#define TARGET_NR_create_module (TARGET_NR_Linux + 127) -#define TARGET_NR_init_module (TARGET_NR_Linux + 128) -#define TARGET_NR_delete_module (TARGET_NR_Linux + 129) -#define TARGET_NR_get_kernel_syms (TARGET_NR_Linux + 130) -#define TARGET_NR_quotactl (TARGET_NR_Linux + 131) -#define TARGET_NR_getpgid (TARGET_NR_Linux + 132) -#define TARGET_NR_fchdir (TARGET_NR_Linux + 133) -#define TARGET_NR_bdflush (TARGET_NR_Linux + 134) -#define TARGET_NR_sysfs (TARGET_NR_Linux + 135) -#define TARGET_NR_personality (TARGET_NR_Linux + 136) -#define TARGET_NR_afs_syscall (TARGET_NR_Linux + 137) /* Syscall for Andrew File System */ -#define TARGET_NR_setfsuid (TARGET_NR_Linux + 138) -#define TARGET_NR_setfsgid (TARGET_NR_Linux + 139) -#define TARGET_NR__llseek (TARGET_NR_Linux + 140) -#define TARGET_NR_getdents (TARGET_NR_Linux + 141) -#define TARGET_NR__newselect (TARGET_NR_Linux + 142) -#define TARGET_NR_flock (TARGET_NR_Linux + 143) -#define TARGET_NR_msync (TARGET_NR_Linux + 144) -#define TARGET_NR_readv (TARGET_NR_Linux + 145) -#define TARGET_NR_writev (TARGET_NR_Linux + 146) -#define TARGET_NR_cacheflush (TARGET_NR_Linux + 147) -#define TARGET_NR_cachectl (TARGET_NR_Linux + 148) -#define TARGET_NR_sysmips (TARGET_NR_Linux + 149) -#define TARGET_NR_unused150 (TARGET_NR_Linux + 150) -#define TARGET_NR_getsid (TARGET_NR_Linux + 151) -#define TARGET_NR_fdatasync (TARGET_NR_Linux + 152) -#define TARGET_NR__sysctl (TARGET_NR_Linux + 153) -#define TARGET_NR_mlock (TARGET_NR_Linux + 154) -#define TARGET_NR_munlock (TARGET_NR_Linux + 155) -#define TARGET_NR_mlockall (TARGET_NR_Linux + 156) -#define TARGET_NR_munlockall (TARGET_NR_Linux + 157) -#define TARGET_NR_sched_setparam (TARGET_NR_Linux + 158) -#define TARGET_NR_sched_getparam (TARGET_NR_Linux + 159) -#define TARGET_NR_sched_setscheduler (TARGET_NR_Linux + 160) -#define TARGET_NR_sched_getscheduler (TARGET_NR_Linux + 161) -#define TARGET_NR_sched_yield (TARGET_NR_Linux + 162) -#define TARGET_NR_sched_get_priority_max (TARGET_NR_Linux + 163) -#define TARGET_NR_sched_get_priority_min (TARGET_NR_Linux + 164) -#define TARGET_NR_sched_rr_get_interval (TARGET_NR_Linux + 165) -#define TARGET_NR_nanosleep (TARGET_NR_Linux + 166) -#define TARGET_NR_mremap (TARGET_NR_Linux + 167) -#define TARGET_NR_accept (TARGET_NR_Linux + 168) -#define TARGET_NR_bind (TARGET_NR_Linux + 169) -#define TARGET_NR_connect (TARGET_NR_Linux + 170) -#define TARGET_NR_getpeername (TARGET_NR_Linux + 171) -#define TARGET_NR_getsockname (TARGET_NR_Linux + 172) -#define TARGET_NR_getsockopt (TARGET_NR_Linux + 173) -#define TARGET_NR_listen (TARGET_NR_Linux + 174) -#define TARGET_NR_recv (TARGET_NR_Linux + 175) -#define TARGET_NR_recvfrom (TARGET_NR_Linux + 176) -#define TARGET_NR_recvmsg (TARGET_NR_Linux + 177) -#define TARGET_NR_send (TARGET_NR_Linux + 178) -#define TARGET_NR_sendmsg (TARGET_NR_Linux + 179) -#define TARGET_NR_sendto (TARGET_NR_Linux + 180) -#define TARGET_NR_setsockopt (TARGET_NR_Linux + 181) -#define TARGET_NR_shutdown (TARGET_NR_Linux + 182) -#define TARGET_NR_socket (TARGET_NR_Linux + 183) -#define TARGET_NR_socketpair (TARGET_NR_Linux + 184) -#define TARGET_NR_setresuid (TARGET_NR_Linux + 185) -#define TARGET_NR_getresuid (TARGET_NR_Linux + 186) -#define TARGET_NR_query_module (TARGET_NR_Linux + 187) -#define TARGET_NR_poll (TARGET_NR_Linux + 188) -#define TARGET_NR_nfsservctl (TARGET_NR_Linux + 189) -#define TARGET_NR_setresgid (TARGET_NR_Linux + 190) -#define TARGET_NR_getresgid (TARGET_NR_Linux + 191) -#define TARGET_NR_prctl (TARGET_NR_Linux + 192) -#define TARGET_NR_rt_sigreturn (TARGET_NR_Linux + 193) -#define TARGET_NR_rt_sigaction (TARGET_NR_Linux + 194) -#define TARGET_NR_rt_sigprocmask (TARGET_NR_Linux + 195) -#define TARGET_NR_rt_sigpending (TARGET_NR_Linux + 196) -#define TARGET_NR_rt_sigtimedwait (TARGET_NR_Linux + 197) -#define TARGET_NR_rt_sigqueueinfo (TARGET_NR_Linux + 198) -#define TARGET_NR_rt_sigsuspend (TARGET_NR_Linux + 199) -#define TARGET_NR_pread64 (TARGET_NR_Linux + 200) -#define TARGET_NR_pwrite64 (TARGET_NR_Linux + 201) -#define TARGET_NR_chown (TARGET_NR_Linux + 202) -#define TARGET_NR_getcwd (TARGET_NR_Linux + 203) -#define TARGET_NR_capget (TARGET_NR_Linux + 204) -#define TARGET_NR_capset (TARGET_NR_Linux + 205) -#define TARGET_NR_sigaltstack (TARGET_NR_Linux + 206) -#define TARGET_NR_sendfile (TARGET_NR_Linux + 207) -#define TARGET_NR_getpmsg (TARGET_NR_Linux + 208) -#define TARGET_NR_putpmsg (TARGET_NR_Linux + 209) -#define TARGET_NR_mmap2 (TARGET_NR_Linux + 210) -#define TARGET_NR_truncate64 (TARGET_NR_Linux + 211) -#define TARGET_NR_ftruncate64 (TARGET_NR_Linux + 212) -#define TARGET_NR_stat64 (TARGET_NR_Linux + 213) -#define TARGET_NR_lstat64 (TARGET_NR_Linux + 214) -#define TARGET_NR_fstat64 (TARGET_NR_Linux + 215) -#define TARGET_NR_pivot_root (TARGET_NR_Linux + 216) -#define TARGET_NR_mincore (TARGET_NR_Linux + 217) -#define TARGET_NR_madvise (TARGET_NR_Linux + 218) -#define TARGET_NR_getdents64 (TARGET_NR_Linux + 219) -#define TARGET_NR_fcntl64 (TARGET_NR_Linux + 220) -#define TARGET_NR_reserved221 (TARGET_NR_Linux + 221) -#define TARGET_NR_gettid (TARGET_NR_Linux + 222) -#define TARGET_NR_readahead (TARGET_NR_Linux + 223) -#define TARGET_NR_setxattr (TARGET_NR_Linux + 224) -#define TARGET_NR_lsetxattr (TARGET_NR_Linux + 225) -#define TARGET_NR_fsetxattr (TARGET_NR_Linux + 226) -#define TARGET_NR_getxattr (TARGET_NR_Linux + 227) -#define TARGET_NR_lgetxattr (TARGET_NR_Linux + 228) -#define TARGET_NR_fgetxattr (TARGET_NR_Linux + 229) -#define TARGET_NR_listxattr (TARGET_NR_Linux + 230) -#define TARGET_NR_llistxattr (TARGET_NR_Linux + 231) -#define TARGET_NR_flistxattr (TARGET_NR_Linux + 232) -#define TARGET_NR_removexattr (TARGET_NR_Linux + 233) -#define TARGET_NR_lremovexattr (TARGET_NR_Linux + 234) -#define TARGET_NR_fremovexattr (TARGET_NR_Linux + 235) -#define TARGET_NR_tkill (TARGET_NR_Linux + 236) -#define TARGET_NR_sendfile64 (TARGET_NR_Linux + 237) -#define TARGET_NR_futex (TARGET_NR_Linux + 238) -#define TARGET_NR_sched_setaffinity (TARGET_NR_Linux + 239) -#define TARGET_NR_sched_getaffinity (TARGET_NR_Linux + 240) -#define TARGET_NR_io_setup (TARGET_NR_Linux + 241) -#define TARGET_NR_io_destroy (TARGET_NR_Linux + 242) -#define TARGET_NR_io_getevents (TARGET_NR_Linux + 243) -#define TARGET_NR_io_submit (TARGET_NR_Linux + 244) -#define TARGET_NR_io_cancel (TARGET_NR_Linux + 245) -#define TARGET_NR_exit_group (TARGET_NR_Linux + 246) -#define TARGET_NR_lookup_dcookie (TARGET_NR_Linux + 247) -#define TARGET_NR_epoll_create (TARGET_NR_Linux + 248) -#define TARGET_NR_epoll_ctl (TARGET_NR_Linux + 249) -#define TARGET_NR_epoll_wait (TARGET_NR_Linux + 250) -#define TARGET_NR_remap_file_pages (TARGET_NR_Linux + 251) -#define TARGET_NR_set_tid_address (TARGET_NR_Linux + 252) -#define TARGET_NR_restart_syscall (TARGET_NR_Linux + 253) -#define TARGET_NR_fadvise64_64 (TARGET_NR_Linux + 254) -#define TARGET_NR_statfs64 (TARGET_NR_Linux + 255) -#define TARGET_NR_fstatfs64 (TARGET_NR_Linux + 256) -#define TARGET_NR_timer_create (TARGET_NR_Linux + 257) -#define TARGET_NR_timer_settime (TARGET_NR_Linux + 258) -#define TARGET_NR_timer_gettime (TARGET_NR_Linux + 259) -#define TARGET_NR_timer_getoverrun (TARGET_NR_Linux + 260) -#define TARGET_NR_timer_delete (TARGET_NR_Linux + 261) -#define TARGET_NR_clock_settime (TARGET_NR_Linux + 262) -#define TARGET_NR_clock_gettime (TARGET_NR_Linux + 263) -#define TARGET_NR_clock_getres (TARGET_NR_Linux + 264) -#define TARGET_NR_clock_nanosleep (TARGET_NR_Linux + 265) -#define TARGET_NR_tgkill (TARGET_NR_Linux + 266) -#define TARGET_NR_utimes (TARGET_NR_Linux + 267) -#define TARGET_NR_mbind (TARGET_NR_Linux + 268) -#define TARGET_NR_get_mempolicy (TARGET_NR_Linux + 269) -#define TARGET_NR_set_mempolicy (TARGET_NR_Linux + 270) -#define TARGET_NR_mq_open (TARGET_NR_Linux + 271) -#define TARGET_NR_mq_unlink (TARGET_NR_Linux + 272) -#define TARGET_NR_mq_timedsend (TARGET_NR_Linux + 273) -#define TARGET_NR_mq_timedreceive (TARGET_NR_Linux + 274) -#define TARGET_NR_mq_notify (TARGET_NR_Linux + 275) -#define TARGET_NR_mq_getsetattr (TARGET_NR_Linux + 276) -#define TARGET_NR_vserver (TARGET_NR_Linux + 277) -#define TARGET_NR_waitid (TARGET_NR_Linux + 278) -/* #define TARGET_NR_sys_setaltroot (TARGET_NR_Linux + 279) */ -#define TARGET_NR_add_key (TARGET_NR_Linux + 280) -#define TARGET_NR_request_key (TARGET_NR_Linux + 281) -#define TARGET_NR_keyctl (TARGET_NR_Linux + 282) -#define TARGET_NR_set_thread_area (TARGET_NR_Linux + 283) -#define TARGET_NR_inotify_init (TARGET_NR_Linux + 284) -#define TARGET_NR_inotify_add_watch (TARGET_NR_Linux + 285) -#define TARGET_NR_inotify_rm_watch (TARGET_NR_Linux + 286) -#define TARGET_NR_migrate_pages (TARGET_NR_Linux + 287) -#define TARGET_NR_openat (TARGET_NR_Linux + 288) -#define TARGET_NR_mkdirat (TARGET_NR_Linux + 289) -#define TARGET_NR_mknodat (TARGET_NR_Linux + 290) -#define TARGET_NR_fchownat (TARGET_NR_Linux + 291) -#define TARGET_NR_futimesat (TARGET_NR_Linux + 292) -#define TARGET_NR_fstatat64 (TARGET_NR_Linux + 293) -#define TARGET_NR_unlinkat (TARGET_NR_Linux + 294) -#define TARGET_NR_renameat (TARGET_NR_Linux + 295) -#define TARGET_NR_linkat (TARGET_NR_Linux + 296) -#define TARGET_NR_symlinkat (TARGET_NR_Linux + 297) -#define TARGET_NR_readlinkat (TARGET_NR_Linux + 298) -#define TARGET_NR_fchmodat (TARGET_NR_Linux + 299) -#define TARGET_NR_faccessat (TARGET_NR_Linux + 300) -#define TARGET_NR_pselect6 (TARGET_NR_Linux + 301) -#define TARGET_NR_ppoll (TARGET_NR_Linux + 302) -#define TARGET_NR_unshare (TARGET_NR_Linux + 303) -#define TARGET_NR_splice (TARGET_NR_Linux + 304) -#define TARGET_NR_sync_file_range (TARGET_NR_Linux + 305) -#define TARGET_NR_tee (TARGET_NR_Linux + 306) -#define TARGET_NR_vmsplice (TARGET_NR_Linux + 307) -#define TARGET_NR_move_pages (TARGET_NR_Linux + 308) -#define TARGET_NR_set_robust_list (TARGET_NR_Linux + 309) -#define TARGET_NR_get_robust_list (TARGET_NR_Linux + 310) -#define TARGET_NR_kexec_load (TARGET_NR_Linux + 311) -#define TARGET_NR_getcpu (TARGET_NR_Linux + 312) -#define TARGET_NR_epoll_pwait (TARGET_NR_Linux + 313) -#define TARGET_NR_ioprio_set (TARGET_NR_Linux + 314) -#define TARGET_NR_ioprio_get (TARGET_NR_Linux + 315) -#define TARGET_NR_utimensat (TARGET_NR_Linux + 316) -#define TARGET_NR_signalfd (TARGET_NR_Linux + 317) -#define TARGET_NR_timerfd (TARGET_NR_Linux + 318) -#define TARGET_NR_eventfd (TARGET_NR_Linux + 319) -#define TARGET_NR_fallocate (TARGET_NR_Linux + 320) -#define TARGET_NR_timerfd_create (TARGET_NR_Linux + 321) -#define TARGET_NR_timerfd_gettime (TARGET_NR_Linux + 322) -#define TARGET_NR_timerfd_settime (TARGET_NR_Linux + 323) -#define TARGET_NR_signalfd4 (TARGET_NR_Linux + 324) -#define TARGET_NR_eventfd2 (TARGET_NR_Linux + 325) -#define TARGET_NR_epoll_create1 (TARGET_NR_Linux + 326) -#define TARGET_NR_dup3 (TARGET_NR_Linux + 327) -#define TARGET_NR_pipe2 (TARGET_NR_Linux + 328) -#define TARGET_NR_inotify_init1 (TARGET_NR_Linux + 329) -#define TARGET_NR_preadv (TARGET_NR_Linux + 330) -#define TARGET_NR_pwritev (TARGET_NR_Linux + 331) -#define TARGET_NR_rt_tgsigqueueinfo (TARGET_NR_Linux + 332) -#define TARGET_NR_perf_event_open (TARGET_NR_Linux + 333) -#define TARGET_NR_accept4 (TARGET_NR_Linux + 334) -#define TARGET_NR_recvmmsg (TARGET_NR_Linux + 335) -#define TARGET_NR_fanotify_init (TARGET_NR_Linux + 336) -#define TARGET_NR_fanotify_mark (TARGET_NR_Linux + 337) -#define TARGET_NR_prlimit64 (TARGET_NR_Linux + 338) -#define TARGET_NR_name_to_handle_at (TARGET_NR_Linux + 339) -#define TARGET_NR_open_by_handle_at (TARGET_NR_Linux + 340) -#define TARGET_NR_clock_adjtime (TARGET_NR_Linux + 341) -#define TARGET_NR_syncfs (TARGET_NR_Linux + 342) -#define TARGET_NR_sendmmsg (TARGET_NR_Linux + 343) -#define TARGET_NR_setns (TARGET_NR_Linux + 344) -#define TARGET_NR_process_vm_readv (TARGET_NR_Linux + 345) -#define TARGET_NR_process_vm_writev (TARGET_NR_Linux + 346) -#define TARGET_NR_kcmp (TARGET_NR_Linux + 347) -#define TARGET_NR_finit_module (TARGET_NR_Linux + 348) - -#define TARGET_NR_sched_setattr (TARGET_NR_Linux + 349) -#define TARGET_NR_sched_getattr (TARGET_NR_Linux + 350) -#define TARGET_NR_renameat2 (TARGET_NR_Linux + 351) -#define TARGET_NR_seccomp (TARGET_NR_Linux + 352) -#define TARGET_NR_getrandom (TARGET_NR_Linux + 353) -#define TARGET_NR_memfd_create (TARGET_NR_Linux + 354) -#define TARGET_NR_bpf (TARGET_NR_Linux + 355) -#define TARGET_NR_execveat (TARGET_NR_Linux + 356) -#define TARGET_NR_userfaultfd (TARGET_NR_Linux + 357) -#define TARGET_NR_membarrier (TARGET_NR_Linux + 358) -#define TARGET_NR_mlock2 (TARGET_NR_Linux + 359) -#define TARGET_NR_copy_file_range (TARGET_NR_Linux + 360) -#define TARGET_NR_preadv2 (TARGET_NR_Linux + 361) -#define TARGET_NR_pwritev2 (TARGET_NR_Linux + 362) -#define TARGET_NR_pkey_mprotect (TARGET_NR_Linux + 363) -#define TARGET_NR_pkey_alloc (TARGET_NR_Linux + 364) -#define TARGET_NR_pkey_free (TARGET_NR_Linux + 365) -#define TARGET_NR_statx (TARGET_NR_Linux + 366) -#define TARGET_NR_rseq (TARGET_NR_Linux + 367) -#define TARGET_NR_io_pgetevents (TARGET_NR_Linux + 368) - -#endif diff --git a/linux-user/mips/syscall_o32.tbl b/linux-user/mips/syscall_o32.tbl new file mode 100644 index 0000000000..ac586774c9 --- /dev/null +++ b/linux-user/mips/syscall_o32.tbl @@ -0,0 +1,427 @@ +# SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note +# +# system call numbers and entry vectors for mips +# +# The format is: +# +# +# The is always "o32" for this file. +# +0 o32 syscall sys_syscall sys32_syscall +1 o32 exit sys_exit +2 o32 fork __sys_fork +3 o32 read sys_read +4 o32 write sys_write +5 o32 open sys_open compat_sys_open +6 o32 close sys_close +7 o32 waitpid sys_waitpid +8 o32 creat sys_creat +9 o32 link sys_link +10 o32 unlink sys_unlink +11 o32 execve sys_execve compat_sys_execve +12 o32 chdir sys_chdir +13 o32 time sys_time32 +14 o32 mknod sys_mknod +15 o32 chmod sys_chmod +16 o32 lchown sys_lchown +17 o32 break sys_ni_syscall +# 18 was sys_stat +18 o32 unused18 sys_ni_syscall +19 o32 lseek sys_lseek +20 o32 getpid sys_getpid +21 o32 mount sys_mount compat_sys_mount +22 o32 umount sys_oldumount +23 o32 setuid sys_setuid +24 o32 getuid sys_getuid +25 o32 stime sys_stime32 +26 o32 ptrace sys_ptrace compat_sys_ptrace +27 o32 alarm sys_alarm +# 28 was sys_fstat +28 o32 unused28 sys_ni_syscall +29 o32 pause sys_pause +30 o32 utime sys_utime32 +31 o32 stty sys_ni_syscall +32 o32 gtty sys_ni_syscall +33 o32 access sys_access +34 o32 nice sys_nice +35 o32 ftime sys_ni_syscall +36 o32 sync sys_sync +37 o32 kill sys_kill +38 o32 rename sys_rename +39 o32 mkdir sys_mkdir +40 o32 rmdir sys_rmdir +41 o32 dup sys_dup +42 o32 pipe sysm_pipe +43 o32 times sys_times compat_sys_times +44 o32 prof sys_ni_syscall +45 o32 brk sys_brk +46 o32 setgid sys_setgid +47 o32 getgid sys_getgid +48 o32 signal sys_ni_syscall +49 o32 geteuid sys_geteuid +50 o32 getegid sys_getegid +51 o32 acct sys_acct +52 o32 umount2 sys_umount +53 o32 lock sys_ni_syscall +54 o32 ioctl sys_ioctl compat_sys_ioctl +55 o32 fcntl sys_fcntl compat_sys_fcntl +56 o32 mpx sys_ni_syscall +57 o32 setpgid sys_setpgid +58 o32 ulimit sys_ni_syscall +59 o32 unused59 sys_olduname +60 o32 umask sys_umask +61 o32 chroot sys_chroot +62 o32 ustat sys_ustat compat_sys_ustat +63 o32 dup2 sys_dup2 +64 o32 getppid sys_getppid +65 o32 getpgrp sys_getpgrp +66 o32 setsid sys_setsid +67 o32 sigaction sys_sigaction sys_32_sigaction +68 o32 sgetmask sys_sgetmask +69 o32 ssetmask sys_ssetmask +70 o32 setreuid sys_setreuid +71 o32 setregid sys_setregid +72 o32 sigsuspend sys_sigsuspend sys32_sigsuspend +73 o32 sigpending sys_sigpending compat_sys_sigpending +74 o32 sethostname sys_sethostname +75 o32 setrlimit sys_setrlimit compat_sys_setrlimit +76 o32 getrlimit sys_getrlimit compat_sys_getrlimit +77 o32 getrusage sys_getrusage compat_sys_getrusage +78 o32 gettimeofday sys_gettimeofday compat_sys_gettimeofday +79 o32 settimeofday sys_settimeofday compat_sys_settimeofday +80 o32 getgroups sys_getgroups +81 o32 setgroups sys_setgroups +# 82 was old_select +82 o32 reserved82 sys_ni_syscall +83 o32 symlink sys_symlink +# 84 was sys_lstat +84 o32 unused84 sys_ni_syscall +85 o32 readlink sys_readlink +86 o32 uselib sys_uselib +87 o32 swapon sys_swapon +88 o32 reboot sys_reboot +89 o32 readdir sys_old_readdir compat_sys_old_readdir +90 o32 mmap sys_mips_mmap +91 o32 munmap sys_munmap +92 o32 truncate sys_truncate compat_sys_truncate +93 o32 ftruncate sys_ftruncate compat_sys_ftruncate +94 o32 fchmod sys_fchmod +95 o32 fchown sys_fchown +96 o32 getpriority sys_getpriority +97 o32 setpriority sys_setpriority +98 o32 profil sys_ni_syscall +99 o32 statfs sys_statfs compat_sys_statfs +100 o32 fstatfs sys_fstatfs compat_sys_fstatfs +101 o32 ioperm sys_ni_syscall +102 o32 socketcall sys_socketcall compat_sys_socketcall +103 o32 syslog sys_syslog +104 o32 setitimer sys_setitimer compat_sys_setitimer +105 o32 getitimer sys_getitimer compat_sys_getitimer +106 o32 stat sys_newstat compat_sys_newstat +107 o32 lstat sys_newlstat compat_sys_newlstat +108 o32 fstat sys_newfstat compat_sys_newfstat +109 o32 unused109 sys_uname +110 o32 iopl sys_ni_syscall +111 o32 vhangup sys_vhangup +112 o32 idle sys_ni_syscall +113 o32 vm86 sys_ni_syscall +114 o32 wait4 sys_wait4 compat_sys_wait4 +115 o32 swapoff sys_swapoff +116 o32 sysinfo sys_sysinfo compat_sys_sysinfo +117 o32 ipc sys_ipc compat_sys_ipc +118 o32 fsync sys_fsync +119 o32 sigreturn sys_sigreturn sys32_sigreturn +120 o32 clone __sys_clone +121 o32 setdomainname sys_setdomainname +122 o32 uname sys_newuname +123 o32 modify_ldt sys_ni_syscall +124 o32 adjtimex sys_adjtimex_time32 +125 o32 mprotect sys_mprotect +126 o32 sigprocmask sys_sigprocmask compat_sys_sigprocmask +127 o32 create_module sys_ni_syscall +128 o32 init_module sys_init_module +129 o32 delete_module sys_delete_module +130 o32 get_kernel_syms sys_ni_syscall +131 o32 quotactl sys_quotactl +132 o32 getpgid sys_getpgid +133 o32 fchdir sys_fchdir +134 o32 bdflush sys_bdflush +135 o32 sysfs sys_sysfs +136 o32 personality sys_personality sys_32_personality +137 o32 afs_syscall sys_ni_syscall +138 o32 setfsuid sys_setfsuid +139 o32 setfsgid sys_setfsgid +140 o32 _llseek sys_llseek sys_32_llseek +141 o32 getdents sys_getdents compat_sys_getdents +142 o32 _newselect sys_select compat_sys_select +143 o32 flock sys_flock +144 o32 msync sys_msync +145 o32 readv sys_readv compat_sys_readv +146 o32 writev sys_writev compat_sys_writev +147 o32 cacheflush sys_cacheflush +148 o32 cachectl sys_cachectl +149 o32 sysmips __sys_sysmips +150 o32 unused150 sys_ni_syscall +151 o32 getsid sys_getsid +152 o32 fdatasync sys_fdatasync +153 o32 _sysctl sys_sysctl compat_sys_sysctl +154 o32 mlock sys_mlock +155 o32 munlock sys_munlock +156 o32 mlockall sys_mlockall +157 o32 munlockall sys_munlockall +158 o32 sched_setparam sys_sched_setparam +159 o32 sched_getparam sys_sched_getparam +160 o32 sched_setscheduler sys_sched_setscheduler +161 o32 sched_getscheduler sys_sched_getscheduler +162 o32 sched_yield sys_sched_yield +163 o32 sched_get_priority_max sys_sched_get_priority_max +164 o32 sched_get_priority_min sys_sched_get_priority_min +165 o32 sched_rr_get_interval sys_sched_rr_get_interval_time32 +166 o32 nanosleep sys_nanosleep_time32 +167 o32 mremap sys_mremap +168 o32 accept sys_accept +169 o32 bind sys_bind +170 o32 connect sys_connect +171 o32 getpeername sys_getpeername +172 o32 getsockname sys_getsockname +173 o32 getsockopt sys_getsockopt compat_sys_getsockopt +174 o32 listen sys_listen +175 o32 recv sys_recv compat_sys_recv +176 o32 recvfrom sys_recvfrom compat_sys_recvfrom +177 o32 recvmsg sys_recvmsg compat_sys_recvmsg +178 o32 send sys_send +179 o32 sendmsg sys_sendmsg compat_sys_sendmsg +180 o32 sendto sys_sendto +181 o32 setsockopt sys_setsockopt compat_sys_setsockopt +182 o32 shutdown sys_shutdown +183 o32 socket sys_socket +184 o32 socketpair sys_socketpair +185 o32 setresuid sys_setresuid +186 o32 getresuid sys_getresuid +187 o32 query_module sys_ni_syscall +188 o32 poll sys_poll +189 o32 nfsservctl sys_ni_syscall +190 o32 setresgid sys_setresgid +191 o32 getresgid sys_getresgid +192 o32 prctl sys_prctl +193 o32 rt_sigreturn sys_rt_sigreturn sys32_rt_sigreturn +194 o32 rt_sigaction sys_rt_sigaction compat_sys_rt_sigaction +195 o32 rt_sigprocmask sys_rt_sigprocmask compat_sys_rt_sigprocmask +196 o32 rt_sigpending sys_rt_sigpending compat_sys_rt_sigpending +197 o32 rt_sigtimedwait sys_rt_sigtimedwait_time32 compat_sys_rt_sigtimedwait_time32 +198 o32 rt_sigqueueinfo sys_rt_sigqueueinfo compat_sys_rt_sigqueueinfo +199 o32 rt_sigsuspend sys_rt_sigsuspend compat_sys_rt_sigsuspend +200 o32 pread64 sys_pread64 sys_32_pread +201 o32 pwrite64 sys_pwrite64 sys_32_pwrite +202 o32 chown sys_chown +203 o32 getcwd sys_getcwd +204 o32 capget sys_capget +205 o32 capset sys_capset +206 o32 sigaltstack sys_sigaltstack compat_sys_sigaltstack +207 o32 sendfile sys_sendfile compat_sys_sendfile +208 o32 getpmsg sys_ni_syscall +209 o32 putpmsg sys_ni_syscall +210 o32 mmap2 sys_mips_mmap2 +211 o32 truncate64 sys_truncate64 sys_32_truncate64 +212 o32 ftruncate64 sys_ftruncate64 sys_32_ftruncate64 +213 o32 stat64 sys_stat64 sys_newstat +214 o32 lstat64 sys_lstat64 sys_newlstat +215 o32 fstat64 sys_fstat64 sys_newfstat +216 o32 pivot_root sys_pivot_root +217 o32 mincore sys_mincore +218 o32 madvise sys_madvise +219 o32 getdents64 sys_getdents64 +220 o32 fcntl64 sys_fcntl64 compat_sys_fcntl64 +221 o32 reserved221 sys_ni_syscall +222 o32 gettid sys_gettid +223 o32 readahead sys_readahead sys32_readahead +224 o32 setxattr sys_setxattr +225 o32 lsetxattr sys_lsetxattr +226 o32 fsetxattr sys_fsetxattr +227 o32 getxattr sys_getxattr +228 o32 lgetxattr sys_lgetxattr +229 o32 fgetxattr sys_fgetxattr +230 o32 listxattr sys_listxattr +231 o32 llistxattr sys_llistxattr +232 o32 flistxattr sys_flistxattr +233 o32 removexattr sys_removexattr +234 o32 lremovexattr sys_lremovexattr +235 o32 fremovexattr sys_fremovexattr +236 o32 tkill sys_tkill +237 o32 sendfile64 sys_sendfile64 +238 o32 futex sys_futex_time32 +239 o32 sched_setaffinity sys_sched_setaffinity compat_sys_sched_setaffinity +240 o32 sched_getaffinity sys_sched_getaffinity compat_sys_sched_getaffinity +241 o32 io_setup sys_io_setup compat_sys_io_setup +242 o32 io_destroy sys_io_destroy +243 o32 io_getevents sys_io_getevents_time32 +244 o32 io_submit sys_io_submit compat_sys_io_submit +245 o32 io_cancel sys_io_cancel +246 o32 exit_group sys_exit_group +247 o32 lookup_dcookie sys_lookup_dcookie compat_sys_lookup_dcookie +248 o32 epoll_create sys_epoll_create +249 o32 epoll_ctl sys_epoll_ctl +250 o32 epoll_wait sys_epoll_wait +251 o32 remap_file_pages sys_remap_file_pages +252 o32 set_tid_address sys_set_tid_address +253 o32 restart_syscall sys_restart_syscall +254 o32 fadvise64 sys_fadvise64_64 sys32_fadvise64_64 +255 o32 statfs64 sys_statfs64 compat_sys_statfs64 +256 o32 fstatfs64 sys_fstatfs64 compat_sys_fstatfs64 +257 o32 timer_create sys_timer_create compat_sys_timer_create +258 o32 timer_settime sys_timer_settime32 +259 o32 timer_gettime sys_timer_gettime32 +260 o32 timer_getoverrun sys_timer_getoverrun +261 o32 timer_delete sys_timer_delete +262 o32 clock_settime sys_clock_settime32 +263 o32 clock_gettime sys_clock_gettime32 +264 o32 clock_getres sys_clock_getres_time32 +265 o32 clock_nanosleep sys_clock_nanosleep_time32 +266 o32 tgkill sys_tgkill +267 o32 utimes sys_utimes_time32 +268 o32 mbind sys_mbind compat_sys_mbind +269 o32 get_mempolicy sys_get_mempolicy compat_sys_get_mempolicy +270 o32 set_mempolicy sys_set_mempolicy compat_sys_set_mempolicy +271 o32 mq_open sys_mq_open compat_sys_mq_open +272 o32 mq_unlink sys_mq_unlink +273 o32 mq_timedsend sys_mq_timedsend_time32 +274 o32 mq_timedreceive sys_mq_timedreceive_time32 +275 o32 mq_notify sys_mq_notify compat_sys_mq_notify +276 o32 mq_getsetattr sys_mq_getsetattr compat_sys_mq_getsetattr +277 o32 vserver sys_ni_syscall +278 o32 waitid sys_waitid compat_sys_waitid +# 279 was sys_setaltroot +280 o32 add_key sys_add_key +281 o32 request_key sys_request_key +282 o32 keyctl sys_keyctl compat_sys_keyctl +283 o32 set_thread_area sys_set_thread_area +284 o32 inotify_init sys_inotify_init +285 o32 inotify_add_watch sys_inotify_add_watch +286 o32 inotify_rm_watch sys_inotify_rm_watch +287 o32 migrate_pages sys_migrate_pages compat_sys_migrate_pages +288 o32 openat sys_openat compat_sys_openat +289 o32 mkdirat sys_mkdirat +290 o32 mknodat sys_mknodat +291 o32 fchownat sys_fchownat +292 o32 futimesat sys_futimesat_time32 +293 o32 fstatat64 sys_fstatat64 sys_newfstatat +294 o32 unlinkat sys_unlinkat +295 o32 renameat sys_renameat +296 o32 linkat sys_linkat +297 o32 symlinkat sys_symlinkat +298 o32 readlinkat sys_readlinkat +299 o32 fchmodat sys_fchmodat +300 o32 faccessat sys_faccessat +301 o32 pselect6 sys_pselect6_time32 compat_sys_pselect6_time32 +302 o32 ppoll sys_ppoll_time32 compat_sys_ppoll_time32 +303 o32 unshare sys_unshare +304 o32 splice sys_splice +305 o32 sync_file_range sys_sync_file_range sys32_sync_file_range +306 o32 tee sys_tee +307 o32 vmsplice sys_vmsplice compat_sys_vmsplice +308 o32 move_pages sys_move_pages compat_sys_move_pages +309 o32 set_robust_list sys_set_robust_list compat_sys_set_robust_list +310 o32 get_robust_list sys_get_robust_list compat_sys_get_robust_list +311 o32 kexec_load sys_kexec_load compat_sys_kexec_load +312 o32 getcpu sys_getcpu +313 o32 epoll_pwait sys_epoll_pwait compat_sys_epoll_pwait +314 o32 ioprio_set sys_ioprio_set +315 o32 ioprio_get sys_ioprio_get +316 o32 utimensat sys_utimensat_time32 +317 o32 signalfd sys_signalfd compat_sys_signalfd +318 o32 timerfd sys_ni_syscall +319 o32 eventfd sys_eventfd +320 o32 fallocate sys_fallocate sys32_fallocate +321 o32 timerfd_create sys_timerfd_create +322 o32 timerfd_gettime sys_timerfd_gettime32 +323 o32 timerfd_settime sys_timerfd_settime32 +324 o32 signalfd4 sys_signalfd4 compat_sys_signalfd4 +325 o32 eventfd2 sys_eventfd2 +326 o32 epoll_create1 sys_epoll_create1 +327 o32 dup3 sys_dup3 +328 o32 pipe2 sys_pipe2 +329 o32 inotify_init1 sys_inotify_init1 +330 o32 preadv sys_preadv compat_sys_preadv +331 o32 pwritev sys_pwritev compat_sys_pwritev +332 o32 rt_tgsigqueueinfo sys_rt_tgsigqueueinfo compat_sys_rt_tgsigqueueinfo +333 o32 perf_event_open sys_perf_event_open +334 o32 accept4 sys_accept4 +335 o32 recvmmsg sys_recvmmsg_time32 compat_sys_recvmmsg_time32 +336 o32 fanotify_init sys_fanotify_init +337 o32 fanotify_mark sys_fanotify_mark compat_sys_fanotify_mark +338 o32 prlimit64 sys_prlimit64 +339 o32 name_to_handle_at sys_name_to_handle_at +340 o32 open_by_handle_at sys_open_by_handle_at compat_sys_open_by_handle_at +341 o32 clock_adjtime sys_clock_adjtime32 +342 o32 syncfs sys_syncfs +343 o32 sendmmsg sys_sendmmsg compat_sys_sendmmsg +344 o32 setns sys_setns +345 o32 process_vm_readv sys_process_vm_readv compat_sys_process_vm_readv +346 o32 process_vm_writev sys_process_vm_writev compat_sys_process_vm_writev +347 o32 kcmp sys_kcmp +348 o32 finit_module sys_finit_module +349 o32 sched_setattr sys_sched_setattr +350 o32 sched_getattr sys_sched_getattr +351 o32 renameat2 sys_renameat2 +352 o32 seccomp sys_seccomp +353 o32 getrandom sys_getrandom +354 o32 memfd_create sys_memfd_create +355 o32 bpf sys_bpf +356 o32 execveat sys_execveat compat_sys_execveat +357 o32 userfaultfd sys_userfaultfd +358 o32 membarrier sys_membarrier +359 o32 mlock2 sys_mlock2 +360 o32 copy_file_range sys_copy_file_range +361 o32 preadv2 sys_preadv2 compat_sys_preadv2 +362 o32 pwritev2 sys_pwritev2 compat_sys_pwritev2 +363 o32 pkey_mprotect sys_pkey_mprotect +364 o32 pkey_alloc sys_pkey_alloc +365 o32 pkey_free sys_pkey_free +366 o32 statx sys_statx +367 o32 rseq sys_rseq +368 o32 io_pgetevents sys_io_pgetevents_time32 compat_sys_io_pgetevents +# room for arch specific calls +393 o32 semget sys_semget +394 o32 semctl sys_semctl compat_sys_semctl +395 o32 shmget sys_shmget +396 o32 shmctl sys_shmctl compat_sys_shmctl +397 o32 shmat sys_shmat compat_sys_shmat +398 o32 shmdt sys_shmdt +399 o32 msgget sys_msgget +400 o32 msgsnd sys_msgsnd compat_sys_msgsnd +401 o32 msgrcv sys_msgrcv compat_sys_msgrcv +402 o32 msgctl sys_msgctl compat_sys_msgctl +403 o32 clock_gettime64 sys_clock_gettime sys_clock_gettime +404 o32 clock_settime64 sys_clock_settime sys_clock_settime +405 o32 clock_adjtime64 sys_clock_adjtime sys_clock_adjtime +406 o32 clock_getres_time64 sys_clock_getres sys_clock_getres +407 o32 clock_nanosleep_time64 sys_clock_nanosleep sys_clock_nanosleep +408 o32 timer_gettime64 sys_timer_gettime sys_timer_gettime +409 o32 timer_settime64 sys_timer_settime sys_timer_settime +410 o32 timerfd_gettime64 sys_timerfd_gettime sys_timerfd_gettime +411 o32 timerfd_settime64 sys_timerfd_settime sys_timerfd_settime +412 o32 utimensat_time64 sys_utimensat sys_utimensat +413 o32 pselect6_time64 sys_pselect6 compat_sys_pselect6_time64 +414 o32 ppoll_time64 sys_ppoll compat_sys_ppoll_time64 +416 o32 io_pgetevents_time64 sys_io_pgetevents sys_io_pgetevents +417 o32 recvmmsg_time64 sys_recvmmsg compat_sys_recvmmsg_time64 +418 o32 mq_timedsend_time64 sys_mq_timedsend sys_mq_timedsend +419 o32 mq_timedreceive_time64 sys_mq_timedreceive sys_mq_timedreceive +420 o32 semtimedop_time64 sys_semtimedop sys_semtimedop +421 o32 rt_sigtimedwait_time64 sys_rt_sigtimedwait compat_sys_rt_sigtimedwait_time64 +422 o32 futex_time64 sys_futex sys_futex +423 o32 sched_rr_get_interval_time64 sys_sched_rr_get_interval sys_sched_rr_get_interval +424 o32 pidfd_send_signal sys_pidfd_send_signal +425 o32 io_uring_setup sys_io_uring_setup +426 o32 io_uring_enter sys_io_uring_enter +427 o32 io_uring_register sys_io_uring_register +428 o32 open_tree sys_open_tree +429 o32 move_mount sys_move_mount +430 o32 fsopen sys_fsopen +431 o32 fsconfig sys_fsconfig +432 o32 fsmount sys_fsmount +433 o32 fspick sys_fspick +434 o32 pidfd_open sys_pidfd_open +435 o32 clone3 __sys_clone3 +437 o32 openat2 sys_openat2 +438 o32 pidfd_getfd sys_pidfd_getfd diff --git a/linux-user/mips/syscallhdr.sh b/linux-user/mips/syscallhdr.sh new file mode 100644 index 0000000000..761e3e47dd --- /dev/null +++ b/linux-user/mips/syscallhdr.sh @@ -0,0 +1,36 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 + +in="$1" +out="$2" +my_abis=`echo "($3)" | tr ',' '|'` +prefix="$4" +offset="$5" + +fileguard=LINUX_USER_MIPS_`basename "$out" | sed \ + -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/' \ + -e 's/[^A-Z0-9_]/_/g' -e 's/__/_/g'` +grep -E "^[0-9A-Fa-fXx]+[[:space:]]+${my_abis}" "$in" | sort -n | ( + printf "#ifndef %s\n" "${fileguard}" + printf "#define %s\n" "${fileguard}" + printf "\n" + + nxt=0 + while read nr abi name entry compat ; do + if [ "$name" = "fadvise64" ] ; then + name="fadvise64_64" + fi + if [ -z "$offset" ]; then + printf "#define TARGET_NR_%s%s\t%s\n" \ + "${prefix}" "${name}" "${nr}" + else + printf "#define TARGET_NR_%s%s\t(%s + %s)\n" \ + "${prefix}" "${name}" "${offset}" "${nr}" + fi + nxt=$((nr+1)) + done + + printf "\n" + printf "#endif /* %s */" "${fileguard}" + printf "\n" +) > "$out" diff --git a/linux-user/mips64/Makefile.objs b/linux-user/mips64/Makefile.objs new file mode 100644 index 0000000000..573448f956 --- /dev/null +++ b/linux-user/mips64/Makefile.objs @@ -0,0 +1,12 @@ +generated-files-y += linux-user/$(TARGET_ABI_DIR)/syscall_nr.h + +syshdr := $(SRC_PATH)/linux-user/$(TARGET_ABI_DIR)/syscallhdr.sh + +ifeq ($(TARGET_SYSTBL_ABI),n32) +%/syscall_nr.h: $(SRC_PATH)/linux-user/$(TARGET_ABI_DIR)/syscall_n32.tbl $(syshdr) + $(call quiet-command, sh $(syshdr) $< $@ n32 "" 6000,"GEN","$@") +endif +ifeq ($(TARGET_SYSTBL_ABI),n64) +%/syscall_nr.h: $(SRC_PATH)/linux-user/$(TARGET_ABI_DIR)/syscall_n64.tbl $(syshdr) + $(call quiet-command, sh $(syshdr) $< $@ n64 "" 5000,"GEN","$@") +endif diff --git a/linux-user/mips64/syscall_n32.tbl b/linux-user/mips64/syscall_n32.tbl new file mode 100644 index 0000000000..1f9e8ad636 --- /dev/null +++ b/linux-user/mips64/syscall_n32.tbl @@ -0,0 +1,378 @@ +# SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note +# +# system call numbers and entry vectors for mips +# +# The format is: +# +# +# The is always "n32" for this file. +# +0 n32 read sys_read +1 n32 write sys_write +2 n32 open sys_open +3 n32 close sys_close +4 n32 stat sys_newstat +5 n32 fstat sys_newfstat +6 n32 lstat sys_newlstat +7 n32 poll sys_poll +8 n32 lseek sys_lseek +9 n32 mmap sys_mips_mmap +10 n32 mprotect sys_mprotect +11 n32 munmap sys_munmap +12 n32 brk sys_brk +13 n32 rt_sigaction compat_sys_rt_sigaction +14 n32 rt_sigprocmask compat_sys_rt_sigprocmask +15 n32 ioctl compat_sys_ioctl +16 n32 pread64 sys_pread64 +17 n32 pwrite64 sys_pwrite64 +18 n32 readv compat_sys_readv +19 n32 writev compat_sys_writev +20 n32 access sys_access +21 n32 pipe sysm_pipe +22 n32 _newselect compat_sys_select +23 n32 sched_yield sys_sched_yield +24 n32 mremap sys_mremap +25 n32 msync sys_msync +26 n32 mincore sys_mincore +27 n32 madvise sys_madvise +28 n32 shmget sys_shmget +29 n32 shmat sys_shmat +30 n32 shmctl compat_sys_old_shmctl +31 n32 dup sys_dup +32 n32 dup2 sys_dup2 +33 n32 pause sys_pause +34 n32 nanosleep sys_nanosleep_time32 +35 n32 getitimer compat_sys_getitimer +36 n32 setitimer compat_sys_setitimer +37 n32 alarm sys_alarm +38 n32 getpid sys_getpid +39 n32 sendfile compat_sys_sendfile +40 n32 socket sys_socket +41 n32 connect sys_connect +42 n32 accept sys_accept +43 n32 sendto sys_sendto +44 n32 recvfrom compat_sys_recvfrom +45 n32 sendmsg compat_sys_sendmsg +46 n32 recvmsg compat_sys_recvmsg +47 n32 shutdown sys_shutdown +48 n32 bind sys_bind +49 n32 listen sys_listen +50 n32 getsockname sys_getsockname +51 n32 getpeername sys_getpeername +52 n32 socketpair sys_socketpair +53 n32 setsockopt compat_sys_setsockopt +54 n32 getsockopt compat_sys_getsockopt +55 n32 clone __sys_clone +56 n32 fork __sys_fork +57 n32 execve compat_sys_execve +58 n32 exit sys_exit +59 n32 wait4 compat_sys_wait4 +60 n32 kill sys_kill +61 n32 uname sys_newuname +62 n32 semget sys_semget +63 n32 semop sys_semop +64 n32 semctl compat_sys_old_semctl +65 n32 shmdt sys_shmdt +66 n32 msgget sys_msgget +67 n32 msgsnd compat_sys_msgsnd +68 n32 msgrcv compat_sys_msgrcv +69 n32 msgctl compat_sys_old_msgctl +70 n32 fcntl compat_sys_fcntl +71 n32 flock sys_flock +72 n32 fsync sys_fsync +73 n32 fdatasync sys_fdatasync +74 n32 truncate sys_truncate +75 n32 ftruncate sys_ftruncate +76 n32 getdents compat_sys_getdents +77 n32 getcwd sys_getcwd +78 n32 chdir sys_chdir +79 n32 fchdir sys_fchdir +80 n32 rename sys_rename +81 n32 mkdir sys_mkdir +82 n32 rmdir sys_rmdir +83 n32 creat sys_creat +84 n32 link sys_link +85 n32 unlink sys_unlink +86 n32 symlink sys_symlink +87 n32 readlink sys_readlink +88 n32 chmod sys_chmod +89 n32 fchmod sys_fchmod +90 n32 chown sys_chown +91 n32 fchown sys_fchown +92 n32 lchown sys_lchown +93 n32 umask sys_umask +94 n32 gettimeofday compat_sys_gettimeofday +95 n32 getrlimit compat_sys_getrlimit +96 n32 getrusage compat_sys_getrusage +97 n32 sysinfo compat_sys_sysinfo +98 n32 times compat_sys_times +99 n32 ptrace compat_sys_ptrace +100 n32 getuid sys_getuid +101 n32 syslog sys_syslog +102 n32 getgid sys_getgid +103 n32 setuid sys_setuid +104 n32 setgid sys_setgid +105 n32 geteuid sys_geteuid +106 n32 getegid sys_getegid +107 n32 setpgid sys_setpgid +108 n32 getppid sys_getppid +109 n32 getpgrp sys_getpgrp +110 n32 setsid sys_setsid +111 n32 setreuid sys_setreuid +112 n32 setregid sys_setregid +113 n32 getgroups sys_getgroups +114 n32 setgroups sys_setgroups +115 n32 setresuid sys_setresuid +116 n32 getresuid sys_getresuid +117 n32 setresgid sys_setresgid +118 n32 getresgid sys_getresgid +119 n32 getpgid sys_getpgid +120 n32 setfsuid sys_setfsuid +121 n32 setfsgid sys_setfsgid +122 n32 getsid sys_getsid +123 n32 capget sys_capget +124 n32 capset sys_capset +125 n32 rt_sigpending compat_sys_rt_sigpending +126 n32 rt_sigtimedwait compat_sys_rt_sigtimedwait_time32 +127 n32 rt_sigqueueinfo compat_sys_rt_sigqueueinfo +128 n32 rt_sigsuspend compat_sys_rt_sigsuspend +129 n32 sigaltstack compat_sys_sigaltstack +130 n32 utime sys_utime32 +131 n32 mknod sys_mknod +132 n32 personality sys_32_personality +133 n32 ustat compat_sys_ustat +134 n32 statfs compat_sys_statfs +135 n32 fstatfs compat_sys_fstatfs +136 n32 sysfs sys_sysfs +137 n32 getpriority sys_getpriority +138 n32 setpriority sys_setpriority +139 n32 sched_setparam sys_sched_setparam +140 n32 sched_getparam sys_sched_getparam +141 n32 sched_setscheduler sys_sched_setscheduler +142 n32 sched_getscheduler sys_sched_getscheduler +143 n32 sched_get_priority_max sys_sched_get_priority_max +144 n32 sched_get_priority_min sys_sched_get_priority_min +145 n32 sched_rr_get_interval sys_sched_rr_get_interval_time32 +146 n32 mlock sys_mlock +147 n32 munlock sys_munlock +148 n32 mlockall sys_mlockall +149 n32 munlockall sys_munlockall +150 n32 vhangup sys_vhangup +151 n32 pivot_root sys_pivot_root +152 n32 _sysctl compat_sys_sysctl +153 n32 prctl sys_prctl +154 n32 adjtimex sys_adjtimex_time32 +155 n32 setrlimit compat_sys_setrlimit +156 n32 chroot sys_chroot +157 n32 sync sys_sync +158 n32 acct sys_acct +159 n32 settimeofday compat_sys_settimeofday +160 n32 mount compat_sys_mount +161 n32 umount2 sys_umount +162 n32 swapon sys_swapon +163 n32 swapoff sys_swapoff +164 n32 reboot sys_reboot +165 n32 sethostname sys_sethostname +166 n32 setdomainname sys_setdomainname +167 n32 create_module sys_ni_syscall +168 n32 init_module sys_init_module +169 n32 delete_module sys_delete_module +170 n32 get_kernel_syms sys_ni_syscall +171 n32 query_module sys_ni_syscall +172 n32 quotactl sys_quotactl +173 n32 nfsservctl sys_ni_syscall +174 n32 getpmsg sys_ni_syscall +175 n32 putpmsg sys_ni_syscall +176 n32 afs_syscall sys_ni_syscall +# 177 reserved for security +177 n32 reserved177 sys_ni_syscall +178 n32 gettid sys_gettid +179 n32 readahead sys_readahead +180 n32 setxattr sys_setxattr +181 n32 lsetxattr sys_lsetxattr +182 n32 fsetxattr sys_fsetxattr +183 n32 getxattr sys_getxattr +184 n32 lgetxattr sys_lgetxattr +185 n32 fgetxattr sys_fgetxattr +186 n32 listxattr sys_listxattr +187 n32 llistxattr sys_llistxattr +188 n32 flistxattr sys_flistxattr +189 n32 removexattr sys_removexattr +190 n32 lremovexattr sys_lremovexattr +191 n32 fremovexattr sys_fremovexattr +192 n32 tkill sys_tkill +193 n32 reserved193 sys_ni_syscall +194 n32 futex sys_futex_time32 +195 n32 sched_setaffinity compat_sys_sched_setaffinity +196 n32 sched_getaffinity compat_sys_sched_getaffinity +197 n32 cacheflush sys_cacheflush +198 n32 cachectl sys_cachectl +199 n32 sysmips __sys_sysmips +200 n32 io_setup compat_sys_io_setup +201 n32 io_destroy sys_io_destroy +202 n32 io_getevents sys_io_getevents_time32 +203 n32 io_submit compat_sys_io_submit +204 n32 io_cancel sys_io_cancel +205 n32 exit_group sys_exit_group +206 n32 lookup_dcookie sys_lookup_dcookie +207 n32 epoll_create sys_epoll_create +208 n32 epoll_ctl sys_epoll_ctl +209 n32 epoll_wait sys_epoll_wait +210 n32 remap_file_pages sys_remap_file_pages +211 n32 rt_sigreturn sysn32_rt_sigreturn +212 n32 fcntl64 compat_sys_fcntl64 +213 n32 set_tid_address sys_set_tid_address +214 n32 restart_syscall sys_restart_syscall +215 n32 semtimedop sys_semtimedop_time32 +216 n32 fadvise64 sys_fadvise64_64 +217 n32 statfs64 compat_sys_statfs64 +218 n32 fstatfs64 compat_sys_fstatfs64 +219 n32 sendfile64 sys_sendfile64 +220 n32 timer_create compat_sys_timer_create +221 n32 timer_settime sys_timer_settime32 +222 n32 timer_gettime sys_timer_gettime32 +223 n32 timer_getoverrun sys_timer_getoverrun +224 n32 timer_delete sys_timer_delete +225 n32 clock_settime sys_clock_settime32 +226 n32 clock_gettime sys_clock_gettime32 +227 n32 clock_getres sys_clock_getres_time32 +228 n32 clock_nanosleep sys_clock_nanosleep_time32 +229 n32 tgkill sys_tgkill +230 n32 utimes sys_utimes_time32 +231 n32 mbind compat_sys_mbind +232 n32 get_mempolicy compat_sys_get_mempolicy +233 n32 set_mempolicy compat_sys_set_mempolicy +234 n32 mq_open compat_sys_mq_open +235 n32 mq_unlink sys_mq_unlink +236 n32 mq_timedsend sys_mq_timedsend_time32 +237 n32 mq_timedreceive sys_mq_timedreceive_time32 +238 n32 mq_notify compat_sys_mq_notify +239 n32 mq_getsetattr compat_sys_mq_getsetattr +240 n32 vserver sys_ni_syscall +241 n32 waitid compat_sys_waitid +# 242 was sys_setaltroot +243 n32 add_key sys_add_key +244 n32 request_key sys_request_key +245 n32 keyctl compat_sys_keyctl +246 n32 set_thread_area sys_set_thread_area +247 n32 inotify_init sys_inotify_init +248 n32 inotify_add_watch sys_inotify_add_watch +249 n32 inotify_rm_watch sys_inotify_rm_watch +250 n32 migrate_pages compat_sys_migrate_pages +251 n32 openat sys_openat +252 n32 mkdirat sys_mkdirat +253 n32 mknodat sys_mknodat +254 n32 fchownat sys_fchownat +255 n32 futimesat sys_futimesat_time32 +256 n32 newfstatat sys_newfstatat +257 n32 unlinkat sys_unlinkat +258 n32 renameat sys_renameat +259 n32 linkat sys_linkat +260 n32 symlinkat sys_symlinkat +261 n32 readlinkat sys_readlinkat +262 n32 fchmodat sys_fchmodat +263 n32 faccessat sys_faccessat +264 n32 pselect6 compat_sys_pselect6_time32 +265 n32 ppoll compat_sys_ppoll_time32 +266 n32 unshare sys_unshare +267 n32 splice sys_splice +268 n32 sync_file_range sys_sync_file_range +269 n32 tee sys_tee +270 n32 vmsplice compat_sys_vmsplice +271 n32 move_pages compat_sys_move_pages +272 n32 set_robust_list compat_sys_set_robust_list +273 n32 get_robust_list compat_sys_get_robust_list +274 n32 kexec_load compat_sys_kexec_load +275 n32 getcpu sys_getcpu +276 n32 epoll_pwait compat_sys_epoll_pwait +277 n32 ioprio_set sys_ioprio_set +278 n32 ioprio_get sys_ioprio_get +279 n32 utimensat sys_utimensat_time32 +280 n32 signalfd compat_sys_signalfd +281 n32 timerfd sys_ni_syscall +282 n32 eventfd sys_eventfd +283 n32 fallocate sys_fallocate +284 n32 timerfd_create sys_timerfd_create +285 n32 timerfd_gettime sys_timerfd_gettime32 +286 n32 timerfd_settime sys_timerfd_settime32 +287 n32 signalfd4 compat_sys_signalfd4 +288 n32 eventfd2 sys_eventfd2 +289 n32 epoll_create1 sys_epoll_create1 +290 n32 dup3 sys_dup3 +291 n32 pipe2 sys_pipe2 +292 n32 inotify_init1 sys_inotify_init1 +293 n32 preadv compat_sys_preadv +294 n32 pwritev compat_sys_pwritev +295 n32 rt_tgsigqueueinfo compat_sys_rt_tgsigqueueinfo +296 n32 perf_event_open sys_perf_event_open +297 n32 accept4 sys_accept4 +298 n32 recvmmsg compat_sys_recvmmsg_time32 +299 n32 getdents64 sys_getdents64 +300 n32 fanotify_init sys_fanotify_init +301 n32 fanotify_mark sys_fanotify_mark +302 n32 prlimit64 sys_prlimit64 +303 n32 name_to_handle_at sys_name_to_handle_at +304 n32 open_by_handle_at sys_open_by_handle_at +305 n32 clock_adjtime sys_clock_adjtime32 +306 n32 syncfs sys_syncfs +307 n32 sendmmsg compat_sys_sendmmsg +308 n32 setns sys_setns +309 n32 process_vm_readv compat_sys_process_vm_readv +310 n32 process_vm_writev compat_sys_process_vm_writev +311 n32 kcmp sys_kcmp +312 n32 finit_module sys_finit_module +313 n32 sched_setattr sys_sched_setattr +314 n32 sched_getattr sys_sched_getattr +315 n32 renameat2 sys_renameat2 +316 n32 seccomp sys_seccomp +317 n32 getrandom sys_getrandom +318 n32 memfd_create sys_memfd_create +319 n32 bpf sys_bpf +320 n32 execveat compat_sys_execveat +321 n32 userfaultfd sys_userfaultfd +322 n32 membarrier sys_membarrier +323 n32 mlock2 sys_mlock2 +324 n32 copy_file_range sys_copy_file_range +325 n32 preadv2 compat_sys_preadv2 +326 n32 pwritev2 compat_sys_pwritev2 +327 n32 pkey_mprotect sys_pkey_mprotect +328 n32 pkey_alloc sys_pkey_alloc +329 n32 pkey_free sys_pkey_free +330 n32 statx sys_statx +331 n32 rseq sys_rseq +332 n32 io_pgetevents compat_sys_io_pgetevents +# 333 through 402 are unassigned to sync up with generic numbers +403 n32 clock_gettime64 sys_clock_gettime +404 n32 clock_settime64 sys_clock_settime +405 n32 clock_adjtime64 sys_clock_adjtime +406 n32 clock_getres_time64 sys_clock_getres +407 n32 clock_nanosleep_time64 sys_clock_nanosleep +408 n32 timer_gettime64 sys_timer_gettime +409 n32 timer_settime64 sys_timer_settime +410 n32 timerfd_gettime64 sys_timerfd_gettime +411 n32 timerfd_settime64 sys_timerfd_settime +412 n32 utimensat_time64 sys_utimensat +413 n32 pselect6_time64 compat_sys_pselect6_time64 +414 n32 ppoll_time64 compat_sys_ppoll_time64 +416 n32 io_pgetevents_time64 sys_io_pgetevents +417 n32 recvmmsg_time64 compat_sys_recvmmsg_time64 +418 n32 mq_timedsend_time64 sys_mq_timedsend +419 n32 mq_timedreceive_time64 sys_mq_timedreceive +420 n32 semtimedop_time64 sys_semtimedop +421 n32 rt_sigtimedwait_time64 compat_sys_rt_sigtimedwait_time64 +422 n32 futex_time64 sys_futex +423 n32 sched_rr_get_interval_time64 sys_sched_rr_get_interval +424 n32 pidfd_send_signal sys_pidfd_send_signal +425 n32 io_uring_setup sys_io_uring_setup +426 n32 io_uring_enter sys_io_uring_enter +427 n32 io_uring_register sys_io_uring_register +428 n32 open_tree sys_open_tree +429 n32 move_mount sys_move_mount +430 n32 fsopen sys_fsopen +431 n32 fsconfig sys_fsconfig +432 n32 fsmount sys_fsmount +433 n32 fspick sys_fspick +434 n32 pidfd_open sys_pidfd_open +435 n32 clone3 __sys_clone3 +437 n32 openat2 sys_openat2 +438 n32 pidfd_getfd sys_pidfd_getfd diff --git a/linux-user/mips64/syscall_n64.tbl b/linux-user/mips64/syscall_n64.tbl new file mode 100644 index 0000000000..c0b9d802db --- /dev/null +++ b/linux-user/mips64/syscall_n64.tbl @@ -0,0 +1,354 @@ +# SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note +# +# system call numbers and entry vectors for mips +# +# The format is: +# +# +# The is always "n64" for this file. +# +0 n64 read sys_read +1 n64 write sys_write +2 n64 open sys_open +3 n64 close sys_close +4 n64 stat sys_newstat +5 n64 fstat sys_newfstat +6 n64 lstat sys_newlstat +7 n64 poll sys_poll +8 n64 lseek sys_lseek +9 n64 mmap sys_mips_mmap +10 n64 mprotect sys_mprotect +11 n64 munmap sys_munmap +12 n64 brk sys_brk +13 n64 rt_sigaction sys_rt_sigaction +14 n64 rt_sigprocmask sys_rt_sigprocmask +15 n64 ioctl sys_ioctl +16 n64 pread64 sys_pread64 +17 n64 pwrite64 sys_pwrite64 +18 n64 readv sys_readv +19 n64 writev sys_writev +20 n64 access sys_access +21 n64 pipe sysm_pipe +22 n64 _newselect sys_select +23 n64 sched_yield sys_sched_yield +24 n64 mremap sys_mremap +25 n64 msync sys_msync +26 n64 mincore sys_mincore +27 n64 madvise sys_madvise +28 n64 shmget sys_shmget +29 n64 shmat sys_shmat +30 n64 shmctl sys_old_shmctl +31 n64 dup sys_dup +32 n64 dup2 sys_dup2 +33 n64 pause sys_pause +34 n64 nanosleep sys_nanosleep +35 n64 getitimer sys_getitimer +36 n64 setitimer sys_setitimer +37 n64 alarm sys_alarm +38 n64 getpid sys_getpid +39 n64 sendfile sys_sendfile64 +40 n64 socket sys_socket +41 n64 connect sys_connect +42 n64 accept sys_accept +43 n64 sendto sys_sendto +44 n64 recvfrom sys_recvfrom +45 n64 sendmsg sys_sendmsg +46 n64 recvmsg sys_recvmsg +47 n64 shutdown sys_shutdown +48 n64 bind sys_bind +49 n64 listen sys_listen +50 n64 getsockname sys_getsockname +51 n64 getpeername sys_getpeername +52 n64 socketpair sys_socketpair +53 n64 setsockopt sys_setsockopt +54 n64 getsockopt sys_getsockopt +55 n64 clone __sys_clone +56 n64 fork __sys_fork +57 n64 execve sys_execve +58 n64 exit sys_exit +59 n64 wait4 sys_wait4 +60 n64 kill sys_kill +61 n64 uname sys_newuname +62 n64 semget sys_semget +63 n64 semop sys_semop +64 n64 semctl sys_old_semctl +65 n64 shmdt sys_shmdt +66 n64 msgget sys_msgget +67 n64 msgsnd sys_msgsnd +68 n64 msgrcv sys_msgrcv +69 n64 msgctl sys_old_msgctl +70 n64 fcntl sys_fcntl +71 n64 flock sys_flock +72 n64 fsync sys_fsync +73 n64 fdatasync sys_fdatasync +74 n64 truncate sys_truncate +75 n64 ftruncate sys_ftruncate +76 n64 getdents sys_getdents +77 n64 getcwd sys_getcwd +78 n64 chdir sys_chdir +79 n64 fchdir sys_fchdir +80 n64 rename sys_rename +81 n64 mkdir sys_mkdir +82 n64 rmdir sys_rmdir +83 n64 creat sys_creat +84 n64 link sys_link +85 n64 unlink sys_unlink +86 n64 symlink sys_symlink +87 n64 readlink sys_readlink +88 n64 chmod sys_chmod +89 n64 fchmod sys_fchmod +90 n64 chown sys_chown +91 n64 fchown sys_fchown +92 n64 lchown sys_lchown +93 n64 umask sys_umask +94 n64 gettimeofday sys_gettimeofday +95 n64 getrlimit sys_getrlimit +96 n64 getrusage sys_getrusage +97 n64 sysinfo sys_sysinfo +98 n64 times sys_times +99 n64 ptrace sys_ptrace +100 n64 getuid sys_getuid +101 n64 syslog sys_syslog +102 n64 getgid sys_getgid +103 n64 setuid sys_setuid +104 n64 setgid sys_setgid +105 n64 geteuid sys_geteuid +106 n64 getegid sys_getegid +107 n64 setpgid sys_setpgid +108 n64 getppid sys_getppid +109 n64 getpgrp sys_getpgrp +110 n64 setsid sys_setsid +111 n64 setreuid sys_setreuid +112 n64 setregid sys_setregid +113 n64 getgroups sys_getgroups +114 n64 setgroups sys_setgroups +115 n64 setresuid sys_setresuid +116 n64 getresuid sys_getresuid +117 n64 setresgid sys_setresgid +118 n64 getresgid sys_getresgid +119 n64 getpgid sys_getpgid +120 n64 setfsuid sys_setfsuid +121 n64 setfsgid sys_setfsgid +122 n64 getsid sys_getsid +123 n64 capget sys_capget +124 n64 capset sys_capset +125 n64 rt_sigpending sys_rt_sigpending +126 n64 rt_sigtimedwait sys_rt_sigtimedwait +127 n64 rt_sigqueueinfo sys_rt_sigqueueinfo +128 n64 rt_sigsuspend sys_rt_sigsuspend +129 n64 sigaltstack sys_sigaltstack +130 n64 utime sys_utime +131 n64 mknod sys_mknod +132 n64 personality sys_personality +133 n64 ustat sys_ustat +134 n64 statfs sys_statfs +135 n64 fstatfs sys_fstatfs +136 n64 sysfs sys_sysfs +137 n64 getpriority sys_getpriority +138 n64 setpriority sys_setpriority +139 n64 sched_setparam sys_sched_setparam +140 n64 sched_getparam sys_sched_getparam +141 n64 sched_setscheduler sys_sched_setscheduler +142 n64 sched_getscheduler sys_sched_getscheduler +143 n64 sched_get_priority_max sys_sched_get_priority_max +144 n64 sched_get_priority_min sys_sched_get_priority_min +145 n64 sched_rr_get_interval sys_sched_rr_get_interval +146 n64 mlock sys_mlock +147 n64 munlock sys_munlock +148 n64 mlockall sys_mlockall +149 n64 munlockall sys_munlockall +150 n64 vhangup sys_vhangup +151 n64 pivot_root sys_pivot_root +152 n64 _sysctl sys_sysctl +153 n64 prctl sys_prctl +154 n64 adjtimex sys_adjtimex +155 n64 setrlimit sys_setrlimit +156 n64 chroot sys_chroot +157 n64 sync sys_sync +158 n64 acct sys_acct +159 n64 settimeofday sys_settimeofday +160 n64 mount sys_mount +161 n64 umount2 sys_umount +162 n64 swapon sys_swapon +163 n64 swapoff sys_swapoff +164 n64 reboot sys_reboot +165 n64 sethostname sys_sethostname +166 n64 setdomainname sys_setdomainname +167 n64 create_module sys_ni_syscall +168 n64 init_module sys_init_module +169 n64 delete_module sys_delete_module +170 n64 get_kernel_syms sys_ni_syscall +171 n64 query_module sys_ni_syscall +172 n64 quotactl sys_quotactl +173 n64 nfsservctl sys_ni_syscall +174 n64 getpmsg sys_ni_syscall +175 n64 putpmsg sys_ni_syscall +176 n64 afs_syscall sys_ni_syscall +# 177 reserved for security +177 n64 reserved177 sys_ni_syscall +178 n64 gettid sys_gettid +179 n64 readahead sys_readahead +180 n64 setxattr sys_setxattr +181 n64 lsetxattr sys_lsetxattr +182 n64 fsetxattr sys_fsetxattr +183 n64 getxattr sys_getxattr +184 n64 lgetxattr sys_lgetxattr +185 n64 fgetxattr sys_fgetxattr +186 n64 listxattr sys_listxattr +187 n64 llistxattr sys_llistxattr +188 n64 flistxattr sys_flistxattr +189 n64 removexattr sys_removexattr +190 n64 lremovexattr sys_lremovexattr +191 n64 fremovexattr sys_fremovexattr +192 n64 tkill sys_tkill +193 n64 reserved193 sys_ni_syscall +194 n64 futex sys_futex +195 n64 sched_setaffinity sys_sched_setaffinity +196 n64 sched_getaffinity sys_sched_getaffinity +197 n64 cacheflush sys_cacheflush +198 n64 cachectl sys_cachectl +199 n64 sysmips __sys_sysmips +200 n64 io_setup sys_io_setup +201 n64 io_destroy sys_io_destroy +202 n64 io_getevents sys_io_getevents +203 n64 io_submit sys_io_submit +204 n64 io_cancel sys_io_cancel +205 n64 exit_group sys_exit_group +206 n64 lookup_dcookie sys_lookup_dcookie +207 n64 epoll_create sys_epoll_create +208 n64 epoll_ctl sys_epoll_ctl +209 n64 epoll_wait sys_epoll_wait +210 n64 remap_file_pages sys_remap_file_pages +211 n64 rt_sigreturn sys_rt_sigreturn +212 n64 set_tid_address sys_set_tid_address +213 n64 restart_syscall sys_restart_syscall +214 n64 semtimedop sys_semtimedop +215 n64 fadvise64 sys_fadvise64_64 +216 n64 timer_create sys_timer_create +217 n64 timer_settime sys_timer_settime +218 n64 timer_gettime sys_timer_gettime +219 n64 timer_getoverrun sys_timer_getoverrun +220 n64 timer_delete sys_timer_delete +221 n64 clock_settime sys_clock_settime +222 n64 clock_gettime sys_clock_gettime +223 n64 clock_getres sys_clock_getres +224 n64 clock_nanosleep sys_clock_nanosleep +225 n64 tgkill sys_tgkill +226 n64 utimes sys_utimes +227 n64 mbind sys_mbind +228 n64 get_mempolicy sys_get_mempolicy +229 n64 set_mempolicy sys_set_mempolicy +230 n64 mq_open sys_mq_open +231 n64 mq_unlink sys_mq_unlink +232 n64 mq_timedsend sys_mq_timedsend +233 n64 mq_timedreceive sys_mq_timedreceive +234 n64 mq_notify sys_mq_notify +235 n64 mq_getsetattr sys_mq_getsetattr +236 n64 vserver sys_ni_syscall +237 n64 waitid sys_waitid +# 238 was sys_setaltroot +239 n64 add_key sys_add_key +240 n64 request_key sys_request_key +241 n64 keyctl sys_keyctl +242 n64 set_thread_area sys_set_thread_area +243 n64 inotify_init sys_inotify_init +244 n64 inotify_add_watch sys_inotify_add_watch +245 n64 inotify_rm_watch sys_inotify_rm_watch +246 n64 migrate_pages sys_migrate_pages +247 n64 openat sys_openat +248 n64 mkdirat sys_mkdirat +249 n64 mknodat sys_mknodat +250 n64 fchownat sys_fchownat +251 n64 futimesat sys_futimesat +252 n64 newfstatat sys_newfstatat +253 n64 unlinkat sys_unlinkat +254 n64 renameat sys_renameat +255 n64 linkat sys_linkat +256 n64 symlinkat sys_symlinkat +257 n64 readlinkat sys_readlinkat +258 n64 fchmodat sys_fchmodat +259 n64 faccessat sys_faccessat +260 n64 pselect6 sys_pselect6 +261 n64 ppoll sys_ppoll +262 n64 unshare sys_unshare +263 n64 splice sys_splice +264 n64 sync_file_range sys_sync_file_range +265 n64 tee sys_tee +266 n64 vmsplice sys_vmsplice +267 n64 move_pages sys_move_pages +268 n64 set_robust_list sys_set_robust_list +269 n64 get_robust_list sys_get_robust_list +270 n64 kexec_load sys_kexec_load +271 n64 getcpu sys_getcpu +272 n64 epoll_pwait sys_epoll_pwait +273 n64 ioprio_set sys_ioprio_set +274 n64 ioprio_get sys_ioprio_get +275 n64 utimensat sys_utimensat +276 n64 signalfd sys_signalfd +277 n64 timerfd sys_ni_syscall +278 n64 eventfd sys_eventfd +279 n64 fallocate sys_fallocate +280 n64 timerfd_create sys_timerfd_create +281 n64 timerfd_gettime sys_timerfd_gettime +282 n64 timerfd_settime sys_timerfd_settime +283 n64 signalfd4 sys_signalfd4 +284 n64 eventfd2 sys_eventfd2 +285 n64 epoll_create1 sys_epoll_create1 +286 n64 dup3 sys_dup3 +287 n64 pipe2 sys_pipe2 +288 n64 inotify_init1 sys_inotify_init1 +289 n64 preadv sys_preadv +290 n64 pwritev sys_pwritev +291 n64 rt_tgsigqueueinfo sys_rt_tgsigqueueinfo +292 n64 perf_event_open sys_perf_event_open +293 n64 accept4 sys_accept4 +294 n64 recvmmsg sys_recvmmsg +295 n64 fanotify_init sys_fanotify_init +296 n64 fanotify_mark sys_fanotify_mark +297 n64 prlimit64 sys_prlimit64 +298 n64 name_to_handle_at sys_name_to_handle_at +299 n64 open_by_handle_at sys_open_by_handle_at +300 n64 clock_adjtime sys_clock_adjtime +301 n64 syncfs sys_syncfs +302 n64 sendmmsg sys_sendmmsg +303 n64 setns sys_setns +304 n64 process_vm_readv sys_process_vm_readv +305 n64 process_vm_writev sys_process_vm_writev +306 n64 kcmp sys_kcmp +307 n64 finit_module sys_finit_module +308 n64 getdents64 sys_getdents64 +309 n64 sched_setattr sys_sched_setattr +310 n64 sched_getattr sys_sched_getattr +311 n64 renameat2 sys_renameat2 +312 n64 seccomp sys_seccomp +313 n64 getrandom sys_getrandom +314 n64 memfd_create sys_memfd_create +315 n64 bpf sys_bpf +316 n64 execveat sys_execveat +317 n64 userfaultfd sys_userfaultfd +318 n64 membarrier sys_membarrier +319 n64 mlock2 sys_mlock2 +320 n64 copy_file_range sys_copy_file_range +321 n64 preadv2 sys_preadv2 +322 n64 pwritev2 sys_pwritev2 +323 n64 pkey_mprotect sys_pkey_mprotect +324 n64 pkey_alloc sys_pkey_alloc +325 n64 pkey_free sys_pkey_free +326 n64 statx sys_statx +327 n64 rseq sys_rseq +328 n64 io_pgetevents sys_io_pgetevents +# 329 through 423 are reserved to sync up with other architectures +424 n64 pidfd_send_signal sys_pidfd_send_signal +425 n64 io_uring_setup sys_io_uring_setup +426 n64 io_uring_enter sys_io_uring_enter +427 n64 io_uring_register sys_io_uring_register +428 n64 open_tree sys_open_tree +429 n64 move_mount sys_move_mount +430 n64 fsopen sys_fsopen +431 n64 fsconfig sys_fsconfig +432 n64 fsmount sys_fsmount +433 n64 fspick sys_fspick +434 n64 pidfd_open sys_pidfd_open +435 n64 clone3 __sys_clone3 +437 n64 openat2 sys_openat2 +438 n64 pidfd_getfd sys_pidfd_getfd diff --git a/linux-user/mips64/syscall_nr.h b/linux-user/mips64/syscall_nr.h deleted file mode 100644 index db40f69ca2..0000000000 --- a/linux-user/mips64/syscall_nr.h +++ /dev/null @@ -1,679 +0,0 @@ -#ifndef LINUX_USER_MIPS64_SYSCALL_NR_H -#define LINUX_USER_MIPS64_SYSCALL_NR_H - -#ifdef TARGET_ABI32 -/* - * Linux N32 syscalls are in the range from 6000 to 6999. - */ -#define TARGET_NR_Linux 6000 -#define TARGET_NR_read (TARGET_NR_Linux + 0) -#define TARGET_NR_write (TARGET_NR_Linux + 1) -#define TARGET_NR_open (TARGET_NR_Linux + 2) -#define TARGET_NR_close (TARGET_NR_Linux + 3) -#define TARGET_NR_stat (TARGET_NR_Linux + 4) -#define TARGET_NR_fstat (TARGET_NR_Linux + 5) -#define TARGET_NR_lstat (TARGET_NR_Linux + 6) -#define TARGET_NR_poll (TARGET_NR_Linux + 7) -#define TARGET_NR_lseek (TARGET_NR_Linux + 8) -#define TARGET_NR_mmap (TARGET_NR_Linux + 9) -#define TARGET_NR_mprotect (TARGET_NR_Linux + 10) -#define TARGET_NR_munmap (TARGET_NR_Linux + 11) -#define TARGET_NR_brk (TARGET_NR_Linux + 12) -#define TARGET_NR_rt_sigaction (TARGET_NR_Linux + 13) -#define TARGET_NR_rt_sigprocmask (TARGET_NR_Linux + 14) -#define TARGET_NR_ioctl (TARGET_NR_Linux + 15) -#define TARGET_NR_pread64 (TARGET_NR_Linux + 16) -#define TARGET_NR_pwrite64 (TARGET_NR_Linux + 17) -#define TARGET_NR_readv (TARGET_NR_Linux + 18) -#define TARGET_NR_writev (TARGET_NR_Linux + 19) -#define TARGET_NR_access (TARGET_NR_Linux + 20) -#define TARGET_NR_pipe (TARGET_NR_Linux + 21) -#define TARGET_NR__newselect (TARGET_NR_Linux + 22) -#define TARGET_NR_sched_yield (TARGET_NR_Linux + 23) -#define TARGET_NR_mremap (TARGET_NR_Linux + 24) -#define TARGET_NR_msync (TARGET_NR_Linux + 25) -#define TARGET_NR_mincore (TARGET_NR_Linux + 26) -#define TARGET_NR_madvise (TARGET_NR_Linux + 27) -#define TARGET_NR_shmget (TARGET_NR_Linux + 28) -#define TARGET_NR_shmat (TARGET_NR_Linux + 29) -#define TARGET_NR_shmctl (TARGET_NR_Linux + 30) -#define TARGET_NR_dup (TARGET_NR_Linux + 31) -#define TARGET_NR_dup2 (TARGET_NR_Linux + 32) -#define TARGET_NR_pause (TARGET_NR_Linux + 33) -#define TARGET_NR_nanosleep (TARGET_NR_Linux + 34) -#define TARGET_NR_getitimer (TARGET_NR_Linux + 35) -#define TARGET_NR_setitimer (TARGET_NR_Linux + 36) -#define TARGET_NR_alarm (TARGET_NR_Linux + 37) -#define TARGET_NR_getpid (TARGET_NR_Linux + 38) -#define TARGET_NR_sendfile (TARGET_NR_Linux + 39) -#define TARGET_NR_socket (TARGET_NR_Linux + 40) -#define TARGET_NR_connect (TARGET_NR_Linux + 41) -#define TARGET_NR_accept (TARGET_NR_Linux + 42) -#define TARGET_NR_sendto (TARGET_NR_Linux + 43) -#define TARGET_NR_recvfrom (TARGET_NR_Linux + 44) -#define TARGET_NR_sendmsg (TARGET_NR_Linux + 45) -#define TARGET_NR_recvmsg (TARGET_NR_Linux + 46) -#define TARGET_NR_shutdown (TARGET_NR_Linux + 47) -#define TARGET_NR_bind (TARGET_NR_Linux + 48) -#define TARGET_NR_listen (TARGET_NR_Linux + 49) -#define TARGET_NR_getsockname (TARGET_NR_Linux + 50) -#define TARGET_NR_getpeername (TARGET_NR_Linux + 51) -#define TARGET_NR_socketpair (TARGET_NR_Linux + 52) -#define TARGET_NR_setsockopt (TARGET_NR_Linux + 53) -#define TARGET_NR_getsockopt (TARGET_NR_Linux + 54) -#define TARGET_NR_clone (TARGET_NR_Linux + 55) -#define TARGET_NR_fork (TARGET_NR_Linux + 56) -#define TARGET_NR_execve (TARGET_NR_Linux + 57) -#define TARGET_NR_exit (TARGET_NR_Linux + 58) -#define TARGET_NR_wait4 (TARGET_NR_Linux + 59) -#define TARGET_NR_kill (TARGET_NR_Linux + 60) -#define TARGET_NR_uname (TARGET_NR_Linux + 61) -#define TARGET_NR_semget (TARGET_NR_Linux + 62) -#define TARGET_NR_semop (TARGET_NR_Linux + 63) -#define TARGET_NR_semctl (TARGET_NR_Linux + 64) -#define TARGET_NR_shmdt (TARGET_NR_Linux + 65) -#define TARGET_NR_msgget (TARGET_NR_Linux + 66) -#define TARGET_NR_msgsnd (TARGET_NR_Linux + 67) -#define TARGET_NR_msgrcv (TARGET_NR_Linux + 68) -#define TARGET_NR_msgctl (TARGET_NR_Linux + 69) -#define TARGET_NR_fcntl (TARGET_NR_Linux + 70) -#define TARGET_NR_flock (TARGET_NR_Linux + 71) -#define TARGET_NR_fsync (TARGET_NR_Linux + 72) -#define TARGET_NR_fdatasync (TARGET_NR_Linux + 73) -#define TARGET_NR_truncate (TARGET_NR_Linux + 74) -#define TARGET_NR_ftruncate (TARGET_NR_Linux + 75) -#define TARGET_NR_getdents (TARGET_NR_Linux + 76) -#define TARGET_NR_getcwd (TARGET_NR_Linux + 77) -#define TARGET_NR_chdir (TARGET_NR_Linux + 78) -#define TARGET_NR_fchdir (TARGET_NR_Linux + 79) -#define TARGET_NR_rename (TARGET_NR_Linux + 80) -#define TARGET_NR_mkdir (TARGET_NR_Linux + 81) -#define TARGET_NR_rmdir (TARGET_NR_Linux + 82) -#define TARGET_NR_creat (TARGET_NR_Linux + 83) -#define TARGET_NR_link (TARGET_NR_Linux + 84) -#define TARGET_NR_unlink (TARGET_NR_Linux + 85) -#define TARGET_NR_symlink (TARGET_NR_Linux + 86) -#define TARGET_NR_readlink (TARGET_NR_Linux + 87) -#define TARGET_NR_chmod (TARGET_NR_Linux + 88) -#define TARGET_NR_fchmod (TARGET_NR_Linux + 89) -#define TARGET_NR_chown (TARGET_NR_Linux + 90) -#define TARGET_NR_fchown (TARGET_NR_Linux + 91) -#define TARGET_NR_lchown (TARGET_NR_Linux + 92) -#define TARGET_NR_umask (TARGET_NR_Linux + 93) -#define TARGET_NR_gettimeofday (TARGET_NR_Linux + 94) -#define TARGET_NR_getrlimit (TARGET_NR_Linux + 95) -#define TARGET_NR_getrusage (TARGET_NR_Linux + 96) -#define TARGET_NR_sysinfo (TARGET_NR_Linux + 97) -#define TARGET_NR_times (TARGET_NR_Linux + 98) -#define TARGET_NR_ptrace (TARGET_NR_Linux + 99) -#define TARGET_NR_getuid (TARGET_NR_Linux + 100) -#define TARGET_NR_syslog (TARGET_NR_Linux + 101) -#define TARGET_NR_getgid (TARGET_NR_Linux + 102) -#define TARGET_NR_setuid (TARGET_NR_Linux + 103) -#define TARGET_NR_setgid (TARGET_NR_Linux + 104) -#define TARGET_NR_geteuid (TARGET_NR_Linux + 105) -#define TARGET_NR_getegid (TARGET_NR_Linux + 106) -#define TARGET_NR_setpgid (TARGET_NR_Linux + 107) -#define TARGET_NR_getppid (TARGET_NR_Linux + 108) -#define TARGET_NR_getpgrp (TARGET_NR_Linux + 109) -#define TARGET_NR_setsid (TARGET_NR_Linux + 110) -#define TARGET_NR_setreuid (TARGET_NR_Linux + 111) -#define TARGET_NR_setregid (TARGET_NR_Linux + 112) -#define TARGET_NR_getgroups (TARGET_NR_Linux + 113) -#define TARGET_NR_setgroups (TARGET_NR_Linux + 114) -#define TARGET_NR_setresuid (TARGET_NR_Linux + 115) -#define TARGET_NR_getresuid (TARGET_NR_Linux + 116) -#define TARGET_NR_setresgid (TARGET_NR_Linux + 117) -#define TARGET_NR_getresgid (TARGET_NR_Linux + 118) -#define TARGET_NR_getpgid (TARGET_NR_Linux + 119) -#define TARGET_NR_setfsuid (TARGET_NR_Linux + 120) -#define TARGET_NR_setfsgid (TARGET_NR_Linux + 121) -#define TARGET_NR_getsid (TARGET_NR_Linux + 122) -#define TARGET_NR_capget (TARGET_NR_Linux + 123) -#define TARGET_NR_capset (TARGET_NR_Linux + 124) -#define TARGET_NR_rt_sigpending (TARGET_NR_Linux + 125) -#define TARGET_NR_rt_sigtimedwait (TARGET_NR_Linux + 126) -#define TARGET_NR_rt_sigqueueinfo (TARGET_NR_Linux + 127) -#define TARGET_NR_rt_sigsuspend (TARGET_NR_Linux + 128) -#define TARGET_NR_sigaltstack (TARGET_NR_Linux + 129) -#define TARGET_NR_utime (TARGET_NR_Linux + 130) -#define TARGET_NR_mknod (TARGET_NR_Linux + 131) -#define TARGET_NR_personality (TARGET_NR_Linux + 132) -#define TARGET_NR_ustat (TARGET_NR_Linux + 133) -#define TARGET_NR_statfs (TARGET_NR_Linux + 134) -#define TARGET_NR_fstatfs (TARGET_NR_Linux + 135) -#define TARGET_NR_sysfs (TARGET_NR_Linux + 136) -#define TARGET_NR_getpriority (TARGET_NR_Linux + 137) -#define TARGET_NR_setpriority (TARGET_NR_Linux + 138) -#define TARGET_NR_sched_setparam (TARGET_NR_Linux + 139) -#define TARGET_NR_sched_getparam (TARGET_NR_Linux + 140) -#define TARGET_NR_sched_setscheduler (TARGET_NR_Linux + 141) -#define TARGET_NR_sched_getscheduler (TARGET_NR_Linux + 142) -#define TARGET_NR_sched_get_priority_max (TARGET_NR_Linux + 143) -#define TARGET_NR_sched_get_priority_min (TARGET_NR_Linux + 144) -#define TARGET_NR_sched_rr_get_interval (TARGET_NR_Linux + 145) -#define TARGET_NR_mlock (TARGET_NR_Linux + 146) -#define TARGET_NR_munlock (TARGET_NR_Linux + 147) -#define TARGET_NR_mlockall (TARGET_NR_Linux + 148) -#define TARGET_NR_munlockall (TARGET_NR_Linux + 149) -#define TARGET_NR_vhangup (TARGET_NR_Linux + 150) -#define TARGET_NR_pivot_root (TARGET_NR_Linux + 151) -#define TARGET_NR__sysctl (TARGET_NR_Linux + 152) -#define TARGET_NR_prctl (TARGET_NR_Linux + 153) -#define TARGET_NR_adjtimex (TARGET_NR_Linux + 154) -#define TARGET_NR_setrlimit (TARGET_NR_Linux + 155) -#define TARGET_NR_chroot (TARGET_NR_Linux + 156) -#define TARGET_NR_sync (TARGET_NR_Linux + 157) -#define TARGET_NR_acct (TARGET_NR_Linux + 158) -#define TARGET_NR_settimeofday (TARGET_NR_Linux + 159) -#define TARGET_NR_mount (TARGET_NR_Linux + 160) -#define TARGET_NR_umount2 (TARGET_NR_Linux + 161) -#define TARGET_NR_swapon (TARGET_NR_Linux + 162) -#define TARGET_NR_swapoff (TARGET_NR_Linux + 163) -#define TARGET_NR_reboot (TARGET_NR_Linux + 164) -#define TARGET_NR_sethostname (TARGET_NR_Linux + 165) -#define TARGET_NR_setdomainname (TARGET_NR_Linux + 166) -#define TARGET_NR_create_module (TARGET_NR_Linux + 167) -#define TARGET_NR_init_module (TARGET_NR_Linux + 168) -#define TARGET_NR_delete_module (TARGET_NR_Linux + 169) -#define TARGET_NR_get_kernel_syms (TARGET_NR_Linux + 170) -#define TARGET_NR_query_module (TARGET_NR_Linux + 171) -#define TARGET_NR_quotactl (TARGET_NR_Linux + 172) -#define TARGET_NR_nfsservctl (TARGET_NR_Linux + 173) -#define TARGET_NR_getpmsg (TARGET_NR_Linux + 174) -#define TARGET_NR_putpmsg (TARGET_NR_Linux + 175) -#define TARGET_NR_afs_syscall (TARGET_NR_Linux + 176) -#define TARGET_NR_reserved177 (TARGET_NR_Linux + 177) -#define TARGET_NR_gettid (TARGET_NR_Linux + 178) -#define TARGET_NR_readahead (TARGET_NR_Linux + 179) -#define TARGET_NR_setxattr (TARGET_NR_Linux + 180) -#define TARGET_NR_lsetxattr (TARGET_NR_Linux + 181) -#define TARGET_NR_fsetxattr (TARGET_NR_Linux + 182) -#define TARGET_NR_getxattr (TARGET_NR_Linux + 183) -#define TARGET_NR_lgetxattr (TARGET_NR_Linux + 184) -#define TARGET_NR_fgetxattr (TARGET_NR_Linux + 185) -#define TARGET_NR_listxattr (TARGET_NR_Linux + 186) -#define TARGET_NR_llistxattr (TARGET_NR_Linux + 187) -#define TARGET_NR_flistxattr (TARGET_NR_Linux + 188) -#define TARGET_NR_removexattr (TARGET_NR_Linux + 189) -#define TARGET_NR_lremovexattr (TARGET_NR_Linux + 190) -#define TARGET_NR_fremovexattr (TARGET_NR_Linux + 191) -#define TARGET_NR_tkill (TARGET_NR_Linux + 192) -#define TARGET_NR_reserved193 (TARGET_NR_Linux + 193) -#define TARGET_NR_futex (TARGET_NR_Linux + 194) -#define TARGET_NR_sched_setaffinity (TARGET_NR_Linux + 195) -#define TARGET_NR_sched_getaffinity (TARGET_NR_Linux + 196) -#define TARGET_NR_cacheflush (TARGET_NR_Linux + 197) -#define TARGET_NR_cachectl (TARGET_NR_Linux + 198) -#define TARGET_NR_sysmips (TARGET_NR_Linux + 199) -#define TARGET_NR_io_setup (TARGET_NR_Linux + 200) -#define TARGET_NR_io_destroy (TARGET_NR_Linux + 201) -#define TARGET_NR_io_getevents (TARGET_NR_Linux + 202) -#define TARGET_NR_io_submit (TARGET_NR_Linux + 203) -#define TARGET_NR_io_cancel (TARGET_NR_Linux + 204) -#define TARGET_NR_exit_group (TARGET_NR_Linux + 205) -#define TARGET_NR_lookup_dcookie (TARGET_NR_Linux + 206) -#define TARGET_NR_epoll_create (TARGET_NR_Linux + 207) -#define TARGET_NR_epoll_ctl (TARGET_NR_Linux + 208) -#define TARGET_NR_epoll_wait (TARGET_NR_Linux + 209) -#define TARGET_NR_remap_file_pages (TARGET_NR_Linux + 210) -#define TARGET_NR_rt_sigreturn (TARGET_NR_Linux + 211) -#define TARGET_NR_fcntl64 (TARGET_NR_Linux + 212) -#define TARGET_NR_set_tid_address (TARGET_NR_Linux + 213) -#define TARGET_NR_restart_syscall (TARGET_NR_Linux + 214) -#define TARGET_NR_semtimedop (TARGET_NR_Linux + 215) -#define TARGET_NR_fadvise64 (TARGET_NR_Linux + 216) -#define TARGET_NR_statfs64 (TARGET_NR_Linux + 217) -#define TARGET_NR_fstatfs64 (TARGET_NR_Linux + 218) -#define TARGET_NR_sendfile64 (TARGET_NR_Linux + 219) -#define TARGET_NR_timer_create (TARGET_NR_Linux + 220) -#define TARGET_NR_timer_settime (TARGET_NR_Linux + 221) -#define TARGET_NR_timer_gettime (TARGET_NR_Linux + 222) -#define TARGET_NR_timer_getoverrun (TARGET_NR_Linux + 223) -#define TARGET_NR_timer_delete (TARGET_NR_Linux + 224) -#define TARGET_NR_clock_settime (TARGET_NR_Linux + 225) -#define TARGET_NR_clock_gettime (TARGET_NR_Linux + 226) -#define TARGET_NR_clock_getres (TARGET_NR_Linux + 227) -#define TARGET_NR_clock_nanosleep (TARGET_NR_Linux + 228) -#define TARGET_NR_tgkill (TARGET_NR_Linux + 229) -#define TARGET_NR_utimes (TARGET_NR_Linux + 230) -#define TARGET_NR_mbind (TARGET_NR_Linux + 231) -#define TARGET_NR_get_mempolicy (TARGET_NR_Linux + 232) -#define TARGET_NR_set_mempolicy (TARGET_NR_Linux + 233) -#define TARGET_NR_mq_open (TARGET_NR_Linux + 234) -#define TARGET_NR_mq_unlink (TARGET_NR_Linux + 235) -#define TARGET_NR_mq_timedsend (TARGET_NR_Linux + 236) -#define TARGET_NR_mq_timedreceive (TARGET_NR_Linux + 237) -#define TARGET_NR_mq_notify (TARGET_NR_Linux + 238) -#define TARGET_NR_mq_getsetattr (TARGET_NR_Linux + 239) -#define TARGET_NR_vserver (TARGET_NR_Linux + 240) -#define TARGET_NR_waitid (TARGET_NR_Linux + 241) -/* #define TARGET_NR_sys_setaltroot (TARGET_NR_Linux + 242) */ -#define TARGET_NR_add_key (TARGET_NR_Linux + 243) -#define TARGET_NR_request_key (TARGET_NR_Linux + 244) -#define TARGET_NR_keyctl (TARGET_NR_Linux + 245) -#define TARGET_NR_set_thread_area (TARGET_NR_Linux + 246) -#define TARGET_NR_inotify_init (TARGET_NR_Linux + 247) -#define TARGET_NR_inotify_add_watch (TARGET_NR_Linux + 248) -#define TARGET_NR_inotify_rm_watch (TARGET_NR_Linux + 249) -#define TARGET_NR_migrate_pages (TARGET_NR_Linux + 250) -#define TARGET_NR_openat (TARGET_NR_Linux + 251) -#define TARGET_NR_mkdirat (TARGET_NR_Linux + 252) -#define TARGET_NR_mknodat (TARGET_NR_Linux + 253) -#define TARGET_NR_fchownat (TARGET_NR_Linux + 254) -#define TARGET_NR_futimesat (TARGET_NR_Linux + 255) -#define TARGET_NR_newfstatat (TARGET_NR_Linux + 256) -#define TARGET_NR_unlinkat (TARGET_NR_Linux + 257) -#define TARGET_NR_renameat (TARGET_NR_Linux + 258) -#define TARGET_NR_linkat (TARGET_NR_Linux + 259) -#define TARGET_NR_symlinkat (TARGET_NR_Linux + 260) -#define TARGET_NR_readlinkat (TARGET_NR_Linux + 261) -#define TARGET_NR_fchmodat (TARGET_NR_Linux + 262) -#define TARGET_NR_faccessat (TARGET_NR_Linux + 263) -#define TARGET_NR_pselect6 (TARGET_NR_Linux + 264) -#define TARGET_NR_ppoll (TARGET_NR_Linux + 265) -#define TARGET_NR_unshare (TARGET_NR_Linux + 266) -#define TARGET_NR_splice (TARGET_NR_Linux + 267) -#define TARGET_NR_sync_file_range (TARGET_NR_Linux + 268) -#define TARGET_NR_tee (TARGET_NR_Linux + 269) -#define TARGET_NR_vmsplice (TARGET_NR_Linux + 270) -#define TARGET_NR_move_pages (TARGET_NR_Linux + 271) -#define TARGET_NR_set_robust_list (TARGET_NR_Linux + 272) -#define TARGET_NR_get_robust_list (TARGET_NR_Linux + 273) -#define TARGET_NR_kexec_load (TARGET_NR_Linux + 274) -#define TARGET_NR_getcpu (TARGET_NR_Linux + 275) -#define TARGET_NR_epoll_pwait (TARGET_NR_Linux + 276) -#define TARGET_NR_ioprio_set (TARGET_NR_Linux + 277) -#define TARGET_NR_ioprio_get (TARGET_NR_Linux + 278) -#define TARGET_NR_utimensat (TARGET_NR_Linux + 279) -#define TARGET_NR_signalfd (TARGET_NR_Linux + 280) -#define TARGET_NR_timerfd (TARGET_NR_Linux + 281) -#define TARGET_NR_eventfd (TARGET_NR_Linux + 282) -#define TARGET_NR_fallocate (TARGET_NR_Linux + 283) -#define TARGET_NR_timerfd_create (TARGET_NR_Linux + 284) -#define TARGET_NR_timerfd_gettime (TARGET_NR_Linux + 285) -#define TARGET_NR_timerfd_settime (TARGET_NR_Linux + 286) -#define TARGET_NR_signalfd4 (TARGET_NR_Linux + 287) -#define TARGET_NR_eventfd2 (TARGET_NR_Linux + 288) -#define TARGET_NR_epoll_create1 (TARGET_NR_Linux + 289) -#define TARGET_NR_dup3 (TARGET_NR_Linux + 290) -#define TARGET_NR_pipe2 (TARGET_NR_Linux + 291) -#define TARGET_NR_inotify_init1 (TARGET_NR_Linux + 292) -#define TARGET_NR_preadv (TARGET_NR_Linux + 293) -#define TARGET_NR_pwritev (TARGET_NR_Linux + 294) -#define TARGET_NR_rt_tgsigqueueinfo (TARGET_NR_Linux + 295) -#define TARGET_NR_perf_event_open (TARGET_NR_Linux + 296) -#define TARGET_NR_accept4 (TARGET_NR_Linux + 297) -#define TARGET_NR_recvmmsg (TARGET_NR_Linux + 298) -#define TARGET_NR_getdents64 (TARGET_NR_Linux + 299) -#define TARGET_NR_fanotify_init (TARGET_NR_Linux + 300) -#define TARGET_NR_fanotify_mark (TARGET_NR_Linux + 301) -#define TARGET_NR_prlimit64 (TARGET_NR_Linux + 302) -#define TARGET_NR_name_to_handle_at (TARGET_NR_Linux + 303) -#define TARGET_NR_open_by_handle_at (TARGET_NR_Linux + 304) -#define TARGET_NR_clock_adjtime (TARGET_NR_Linux + 305) -#define TARGET_NR_syncfs (TARGET_NR_Linux + 306) -#define TARGET_NR_sendmmsg (TARGET_NR_Linux + 307) -#define TARGET_NR_setns (TARGET_NR_Linux + 308) -#define TARGET_NR_process_vm_readv (TARGET_NR_Linux + 309) -#define TARGET_NR_process_vm_writev (TARGET_NR_Linux + 310) -#define TARGET_NR_kcmp (TARGET_NR_Linux + 311) -#define TARGET_NR_finit_module (TARGET_NR_Linux + 312) -#define TARGET_NR_sched_setattr (TARGET_NR_Linux + 313) -#define TARGET_NR_sched_getattr (TARGET_NR_Linux + 314) -#define TARGET_NR_renameat2 (TARGET_NR_Linux + 315) -#define TARGET_NR_seccomp (TARGET_NR_Linux + 316) -#define TARGET_NR_getrandom (TARGET_NR_Linux + 317) -#define TARGET_NR_memfd_create (TARGET_NR_Linux + 318) -#define TARGET_NR_bpf (TARGET_NR_Linux + 319) -#define TARGET_NR_execveat (TARGET_NR_Linux + 320) -#define TARGET_NR_userfaultfd (TARGET_NR_Linux + 321) -#define TARGET_NR_membarrier (TARGET_NR_Linux + 322) -#define TARGET_NR_mlock2 (TARGET_NR_Linux + 323) -#define TARGET_NR_copy_file_range (TARGET_NR_Linux + 324) -#define TARGET_NR_preadv2 (TARGET_NR_Linux + 325) -#define TARGET_NR_pwritev2 (TARGET_NR_Linux + 326) -#define TARGET_NR_pkey_mprotect (TARGET_NR_Linux + 327) -#define TARGET_NR_pkey_alloc (TARGET_NR_Linux + 328) -#define TARGET_NR_pkey_free (TARGET_NR_Linux + 329) -#define TARGET_NR_statx (TARGET_NR_Linux + 330) -#define TARGET_NR_rseq (TARGET_NR_Linux + 331) -#define TARGET_NR_io_pgetevents (TARGET_NR_Linux + 332) - -#else -/* - * Linux 64-bit syscalls are in the range from 5000 to 5999. - */ -#define TARGET_NR_Linux 5000 -#define TARGET_NR_read (TARGET_NR_Linux + 0) -#define TARGET_NR_write (TARGET_NR_Linux + 1) -#define TARGET_NR_open (TARGET_NR_Linux + 2) -#define TARGET_NR_close (TARGET_NR_Linux + 3) -#define TARGET_NR_stat (TARGET_NR_Linux + 4) -#define TARGET_NR_fstat (TARGET_NR_Linux + 5) -#define TARGET_NR_lstat (TARGET_NR_Linux + 6) -#define TARGET_NR_poll (TARGET_NR_Linux + 7) -#define TARGET_NR_lseek (TARGET_NR_Linux + 8) -#define TARGET_NR_mmap (TARGET_NR_Linux + 9) -#define TARGET_NR_mprotect (TARGET_NR_Linux + 10) -#define TARGET_NR_munmap (TARGET_NR_Linux + 11) -#define TARGET_NR_brk (TARGET_NR_Linux + 12) -#define TARGET_NR_rt_sigaction (TARGET_NR_Linux + 13) -#define TARGET_NR_rt_sigprocmask (TARGET_NR_Linux + 14) -#define TARGET_NR_ioctl (TARGET_NR_Linux + 15) -#define TARGET_NR_pread64 (TARGET_NR_Linux + 16) -#define TARGET_NR_pwrite64 (TARGET_NR_Linux + 17) -#define TARGET_NR_readv (TARGET_NR_Linux + 18) -#define TARGET_NR_writev (TARGET_NR_Linux + 19) -#define TARGET_NR_access (TARGET_NR_Linux + 20) -#define TARGET_NR_pipe (TARGET_NR_Linux + 21) -#define TARGET_NR__newselect (TARGET_NR_Linux + 22) -#define TARGET_NR_sched_yield (TARGET_NR_Linux + 23) -#define TARGET_NR_mremap (TARGET_NR_Linux + 24) -#define TARGET_NR_msync (TARGET_NR_Linux + 25) -#define TARGET_NR_mincore (TARGET_NR_Linux + 26) -#define TARGET_NR_madvise (TARGET_NR_Linux + 27) -#define TARGET_NR_shmget (TARGET_NR_Linux + 28) -#define TARGET_NR_shmat (TARGET_NR_Linux + 29) -#define TARGET_NR_shmctl (TARGET_NR_Linux + 30) -#define TARGET_NR_dup (TARGET_NR_Linux + 31) -#define TARGET_NR_dup2 (TARGET_NR_Linux + 32) -#define TARGET_NR_pause (TARGET_NR_Linux + 33) -#define TARGET_NR_nanosleep (TARGET_NR_Linux + 34) -#define TARGET_NR_getitimer (TARGET_NR_Linux + 35) -#define TARGET_NR_setitimer (TARGET_NR_Linux + 36) -#define TARGET_NR_alarm (TARGET_NR_Linux + 37) -#define TARGET_NR_getpid (TARGET_NR_Linux + 38) -#define TARGET_NR_sendfile (TARGET_NR_Linux + 39) -#define TARGET_NR_socket (TARGET_NR_Linux + 40) -#define TARGET_NR_connect (TARGET_NR_Linux + 41) -#define TARGET_NR_accept (TARGET_NR_Linux + 42) -#define TARGET_NR_sendto (TARGET_NR_Linux + 43) -#define TARGET_NR_recvfrom (TARGET_NR_Linux + 44) -#define TARGET_NR_sendmsg (TARGET_NR_Linux + 45) -#define TARGET_NR_recvmsg (TARGET_NR_Linux + 46) -#define TARGET_NR_shutdown (TARGET_NR_Linux + 47) -#define TARGET_NR_bind (TARGET_NR_Linux + 48) -#define TARGET_NR_listen (TARGET_NR_Linux + 49) -#define TARGET_NR_getsockname (TARGET_NR_Linux + 50) -#define TARGET_NR_getpeername (TARGET_NR_Linux + 51) -#define TARGET_NR_socketpair (TARGET_NR_Linux + 52) -#define TARGET_NR_setsockopt (TARGET_NR_Linux + 53) -#define TARGET_NR_getsockopt (TARGET_NR_Linux + 54) -#define TARGET_NR_clone (TARGET_NR_Linux + 55) -#define TARGET_NR_fork (TARGET_NR_Linux + 56) -#define TARGET_NR_execve (TARGET_NR_Linux + 57) -#define TARGET_NR_exit (TARGET_NR_Linux + 58) -#define TARGET_NR_wait4 (TARGET_NR_Linux + 59) -#define TARGET_NR_kill (TARGET_NR_Linux + 60) -#define TARGET_NR_uname (TARGET_NR_Linux + 61) -#define TARGET_NR_semget (TARGET_NR_Linux + 62) -#define TARGET_NR_semop (TARGET_NR_Linux + 63) -#define TARGET_NR_semctl (TARGET_NR_Linux + 64) -#define TARGET_NR_shmdt (TARGET_NR_Linux + 65) -#define TARGET_NR_msgget (TARGET_NR_Linux + 66) -#define TARGET_NR_msgsnd (TARGET_NR_Linux + 67) -#define TARGET_NR_msgrcv (TARGET_NR_Linux + 68) -#define TARGET_NR_msgctl (TARGET_NR_Linux + 69) -#define TARGET_NR_fcntl (TARGET_NR_Linux + 70) -#define TARGET_NR_flock (TARGET_NR_Linux + 71) -#define TARGET_NR_fsync (TARGET_NR_Linux + 72) -#define TARGET_NR_fdatasync (TARGET_NR_Linux + 73) -#define TARGET_NR_truncate (TARGET_NR_Linux + 74) -#define TARGET_NR_ftruncate (TARGET_NR_Linux + 75) -#define TARGET_NR_getdents (TARGET_NR_Linux + 76) -#define TARGET_NR_getcwd (TARGET_NR_Linux + 77) -#define TARGET_NR_chdir (TARGET_NR_Linux + 78) -#define TARGET_NR_fchdir (TARGET_NR_Linux + 79) -#define TARGET_NR_rename (TARGET_NR_Linux + 80) -#define TARGET_NR_mkdir (TARGET_NR_Linux + 81) -#define TARGET_NR_rmdir (TARGET_NR_Linux + 82) -#define TARGET_NR_creat (TARGET_NR_Linux + 83) -#define TARGET_NR_link (TARGET_NR_Linux + 84) -#define TARGET_NR_unlink (TARGET_NR_Linux + 85) -#define TARGET_NR_symlink (TARGET_NR_Linux + 86) -#define TARGET_NR_readlink (TARGET_NR_Linux + 87) -#define TARGET_NR_chmod (TARGET_NR_Linux + 88) -#define TARGET_NR_fchmod (TARGET_NR_Linux + 89) -#define TARGET_NR_chown (TARGET_NR_Linux + 90) -#define TARGET_NR_fchown (TARGET_NR_Linux + 91) -#define TARGET_NR_lchown (TARGET_NR_Linux + 92) -#define TARGET_NR_umask (TARGET_NR_Linux + 93) -#define TARGET_NR_gettimeofday (TARGET_NR_Linux + 94) -#define TARGET_NR_getrlimit (TARGET_NR_Linux + 95) -#define TARGET_NR_getrusage (TARGET_NR_Linux + 96) -#define TARGET_NR_sysinfo (TARGET_NR_Linux + 97) -#define TARGET_NR_times (TARGET_NR_Linux + 98) -#define TARGET_NR_ptrace (TARGET_NR_Linux + 99) -#define TARGET_NR_getuid (TARGET_NR_Linux + 100) -#define TARGET_NR_syslog (TARGET_NR_Linux + 101) -#define TARGET_NR_getgid (TARGET_NR_Linux + 102) -#define TARGET_NR_setuid (TARGET_NR_Linux + 103) -#define TARGET_NR_setgid (TARGET_NR_Linux + 104) -#define TARGET_NR_geteuid (TARGET_NR_Linux + 105) -#define TARGET_NR_getegid (TARGET_NR_Linux + 106) -#define TARGET_NR_setpgid (TARGET_NR_Linux + 107) -#define TARGET_NR_getppid (TARGET_NR_Linux + 108) -#define TARGET_NR_getpgrp (TARGET_NR_Linux + 109) -#define TARGET_NR_setsid (TARGET_NR_Linux + 110) -#define TARGET_NR_setreuid (TARGET_NR_Linux + 111) -#define TARGET_NR_setregid (TARGET_NR_Linux + 112) -#define TARGET_NR_getgroups (TARGET_NR_Linux + 113) -#define TARGET_NR_setgroups (TARGET_NR_Linux + 114) -#define TARGET_NR_setresuid (TARGET_NR_Linux + 115) -#define TARGET_NR_getresuid (TARGET_NR_Linux + 116) -#define TARGET_NR_setresgid (TARGET_NR_Linux + 117) -#define TARGET_NR_getresgid (TARGET_NR_Linux + 118) -#define TARGET_NR_getpgid (TARGET_NR_Linux + 119) -#define TARGET_NR_setfsuid (TARGET_NR_Linux + 120) -#define TARGET_NR_setfsgid (TARGET_NR_Linux + 121) -#define TARGET_NR_getsid (TARGET_NR_Linux + 122) -#define TARGET_NR_capget (TARGET_NR_Linux + 123) -#define TARGET_NR_capset (TARGET_NR_Linux + 124) -#define TARGET_NR_rt_sigpending (TARGET_NR_Linux + 125) -#define TARGET_NR_rt_sigtimedwait (TARGET_NR_Linux + 126) -#define TARGET_NR_rt_sigqueueinfo (TARGET_NR_Linux + 127) -#define TARGET_NR_rt_sigsuspend (TARGET_NR_Linux + 128) -#define TARGET_NR_sigaltstack (TARGET_NR_Linux + 129) -#define TARGET_NR_utime (TARGET_NR_Linux + 130) -#define TARGET_NR_mknod (TARGET_NR_Linux + 131) -#define TARGET_NR_personality (TARGET_NR_Linux + 132) -#define TARGET_NR_ustat (TARGET_NR_Linux + 133) -#define TARGET_NR_statfs (TARGET_NR_Linux + 134) -#define TARGET_NR_fstatfs (TARGET_NR_Linux + 135) -#define TARGET_NR_sysfs (TARGET_NR_Linux + 136) -#define TARGET_NR_getpriority (TARGET_NR_Linux + 137) -#define TARGET_NR_setpriority (TARGET_NR_Linux + 138) -#define TARGET_NR_sched_setparam (TARGET_NR_Linux + 139) -#define TARGET_NR_sched_getparam (TARGET_NR_Linux + 140) -#define TARGET_NR_sched_setscheduler (TARGET_NR_Linux + 141) -#define TARGET_NR_sched_getscheduler (TARGET_NR_Linux + 142) -#define TARGET_NR_sched_get_priority_max (TARGET_NR_Linux + 143) -#define TARGET_NR_sched_get_priority_min (TARGET_NR_Linux + 144) -#define TARGET_NR_sched_rr_get_interval (TARGET_NR_Linux + 145) -#define TARGET_NR_mlock (TARGET_NR_Linux + 146) -#define TARGET_NR_munlock (TARGET_NR_Linux + 147) -#define TARGET_NR_mlockall (TARGET_NR_Linux + 148) -#define TARGET_NR_munlockall (TARGET_NR_Linux + 149) -#define TARGET_NR_vhangup (TARGET_NR_Linux + 150) -#define TARGET_NR_pivot_root (TARGET_NR_Linux + 151) -#define TARGET_NR__sysctl (TARGET_NR_Linux + 152) -#define TARGET_NR_prctl (TARGET_NR_Linux + 153) -#define TARGET_NR_adjtimex (TARGET_NR_Linux + 154) -#define TARGET_NR_setrlimit (TARGET_NR_Linux + 155) -#define TARGET_NR_chroot (TARGET_NR_Linux + 156) -#define TARGET_NR_sync (TARGET_NR_Linux + 157) -#define TARGET_NR_acct (TARGET_NR_Linux + 158) -#define TARGET_NR_settimeofday (TARGET_NR_Linux + 159) -#define TARGET_NR_mount (TARGET_NR_Linux + 160) -#define TARGET_NR_umount2 (TARGET_NR_Linux + 161) -#define TARGET_NR_swapon (TARGET_NR_Linux + 162) -#define TARGET_NR_swapoff (TARGET_NR_Linux + 163) -#define TARGET_NR_reboot (TARGET_NR_Linux + 164) -#define TARGET_NR_sethostname (TARGET_NR_Linux + 165) -#define TARGET_NR_setdomainname (TARGET_NR_Linux + 166) -#define TARGET_NR_create_module (TARGET_NR_Linux + 167) -#define TARGET_NR_init_module (TARGET_NR_Linux + 168) -#define TARGET_NR_delete_module (TARGET_NR_Linux + 169) -#define TARGET_NR_get_kernel_syms (TARGET_NR_Linux + 170) -#define TARGET_NR_query_module (TARGET_NR_Linux + 171) -#define TARGET_NR_quotactl (TARGET_NR_Linux + 172) -#define TARGET_NR_nfsservctl (TARGET_NR_Linux + 173) -#define TARGET_NR_getpmsg (TARGET_NR_Linux + 174) -#define TARGET_NR_putpmsg (TARGET_NR_Linux + 175) -#define TARGET_NR_afs_syscall (TARGET_NR_Linux + 176) -#define TARGET_NR_reserved177 (TARGET_NR_Linux + 177) -#define TARGET_NR_gettid (TARGET_NR_Linux + 178) -#define TARGET_NR_readahead (TARGET_NR_Linux + 179) -#define TARGET_NR_setxattr (TARGET_NR_Linux + 180) -#define TARGET_NR_lsetxattr (TARGET_NR_Linux + 181) -#define TARGET_NR_fsetxattr (TARGET_NR_Linux + 182) -#define TARGET_NR_getxattr (TARGET_NR_Linux + 183) -#define TARGET_NR_lgetxattr (TARGET_NR_Linux + 184) -#define TARGET_NR_fgetxattr (TARGET_NR_Linux + 185) -#define TARGET_NR_listxattr (TARGET_NR_Linux + 186) -#define TARGET_NR_llistxattr (TARGET_NR_Linux + 187) -#define TARGET_NR_flistxattr (TARGET_NR_Linux + 188) -#define TARGET_NR_removexattr (TARGET_NR_Linux + 189) -#define TARGET_NR_lremovexattr (TARGET_NR_Linux + 190) -#define TARGET_NR_fremovexattr (TARGET_NR_Linux + 191) -#define TARGET_NR_tkill (TARGET_NR_Linux + 192) -#define TARGET_NR_reserved193 (TARGET_NR_Linux + 193) -#define TARGET_NR_futex (TARGET_NR_Linux + 194) -#define TARGET_NR_sched_setaffinity (TARGET_NR_Linux + 195) -#define TARGET_NR_sched_getaffinity (TARGET_NR_Linux + 196) -#define TARGET_NR_cacheflush (TARGET_NR_Linux + 197) -#define TARGET_NR_cachectl (TARGET_NR_Linux + 198) -#define TARGET_NR_sysmips (TARGET_NR_Linux + 199) -#define TARGET_NR_io_setup (TARGET_NR_Linux + 200) -#define TARGET_NR_io_destroy (TARGET_NR_Linux + 201) -#define TARGET_NR_io_getevents (TARGET_NR_Linux + 202) -#define TARGET_NR_io_submit (TARGET_NR_Linux + 203) -#define TARGET_NR_io_cancel (TARGET_NR_Linux + 204) -#define TARGET_NR_exit_group (TARGET_NR_Linux + 205) -#define TARGET_NR_lookup_dcookie (TARGET_NR_Linux + 206) -#define TARGET_NR_epoll_create (TARGET_NR_Linux + 207) -#define TARGET_NR_epoll_ctl (TARGET_NR_Linux + 208) -#define TARGET_NR_epoll_wait (TARGET_NR_Linux + 209) -#define TARGET_NR_remap_file_pages (TARGET_NR_Linux + 210) -#define TARGET_NR_rt_sigreturn (TARGET_NR_Linux + 211) -#define TARGET_NR_set_tid_address (TARGET_NR_Linux + 212) -#define TARGET_NR_restart_syscall (TARGET_NR_Linux + 213) -#define TARGET_NR_semtimedop (TARGET_NR_Linux + 214) -#define TARGET_NR_fadvise64 (TARGET_NR_Linux + 215) -#define TARGET_NR_timer_create (TARGET_NR_Linux + 216) -#define TARGET_NR_timer_settime (TARGET_NR_Linux + 217) -#define TARGET_NR_timer_gettime (TARGET_NR_Linux + 218) -#define TARGET_NR_timer_getoverrun (TARGET_NR_Linux + 219) -#define TARGET_NR_timer_delete (TARGET_NR_Linux + 220) -#define TARGET_NR_clock_settime (TARGET_NR_Linux + 221) -#define TARGET_NR_clock_gettime (TARGET_NR_Linux + 222) -#define TARGET_NR_clock_getres (TARGET_NR_Linux + 223) -#define TARGET_NR_clock_nanosleep (TARGET_NR_Linux + 224) -#define TARGET_NR_tgkill (TARGET_NR_Linux + 225) -#define TARGET_NR_utimes (TARGET_NR_Linux + 226) -#define TARGET_NR_mbind (TARGET_NR_Linux + 227) -#define TARGET_NR_get_mempolicy (TARGET_NR_Linux + 228) -#define TARGET_NR_set_mempolicy (TARGET_NR_Linux + 229) -#define TARGET_NR_mq_open (TARGET_NR_Linux + 230) -#define TARGET_NR_mq_unlink (TARGET_NR_Linux + 231) -#define TARGET_NR_mq_timedsend (TARGET_NR_Linux + 232) -#define TARGET_NR_mq_timedreceive (TARGET_NR_Linux + 233) -#define TARGET_NR_mq_notify (TARGET_NR_Linux + 234) -#define TARGET_NR_mq_getsetattr (TARGET_NR_Linux + 235) -#define TARGET_NR_vserver (TARGET_NR_Linux + 236) -#define TARGET_NR_waitid (TARGET_NR_Linux + 237) -/* #define TARGET_NR_sys_setaltroot (TARGET_NR_Linux + 238) */ -#define TARGET_NR_add_key (TARGET_NR_Linux + 239) -#define TARGET_NR_request_key (TARGET_NR_Linux + 240) -#define TARGET_NR_keyctl (TARGET_NR_Linux + 241) -#define TARGET_NR_set_thread_area (TARGET_NR_Linux + 242) -#define TARGET_NR_inotify_init (TARGET_NR_Linux + 243) -#define TARGET_NR_inotify_add_watch (TARGET_NR_Linux + 244) -#define TARGET_NR_inotify_rm_watch (TARGET_NR_Linux + 245) -#define TARGET_NR_migrate_pages (TARGET_NR_Linux + 246) -#define TARGET_NR_openat (TARGET_NR_Linux + 247) -#define TARGET_NR_mkdirat (TARGET_NR_Linux + 248) -#define TARGET_NR_mknodat (TARGET_NR_Linux + 249) -#define TARGET_NR_fchownat (TARGET_NR_Linux + 250) -#define TARGET_NR_futimesat (TARGET_NR_Linux + 251) -#define TARGET_NR_newfstatat (TARGET_NR_Linux + 252) -#define TARGET_NR_unlinkat (TARGET_NR_Linux + 253) -#define TARGET_NR_renameat (TARGET_NR_Linux + 254) -#define TARGET_NR_linkat (TARGET_NR_Linux + 255) -#define TARGET_NR_symlinkat (TARGET_NR_Linux + 256) -#define TARGET_NR_readlinkat (TARGET_NR_Linux + 257) -#define TARGET_NR_fchmodat (TARGET_NR_Linux + 258) -#define TARGET_NR_faccessat (TARGET_NR_Linux + 259) -#define TARGET_NR_pselect6 (TARGET_NR_Linux + 260) -#define TARGET_NR_ppoll (TARGET_NR_Linux + 261) -#define TARGET_NR_unshare (TARGET_NR_Linux + 262) -#define TARGET_NR_splice (TARGET_NR_Linux + 263) -#define TARGET_NR_sync_file_range (TARGET_NR_Linux + 264) -#define TARGET_NR_tee (TARGET_NR_Linux + 265) -#define TARGET_NR_vmsplice (TARGET_NR_Linux + 266) -#define TARGET_NR_move_pages (TARGET_NR_Linux + 267) -#define TARGET_NR_set_robust_list (TARGET_NR_Linux + 268) -#define TARGET_NR_get_robust_list (TARGET_NR_Linux + 269) -#define TARGET_NR_kexec_load (TARGET_NR_Linux + 270) -#define TARGET_NR_getcpu (TARGET_NR_Linux + 271) -#define TARGET_NR_epoll_pwait (TARGET_NR_Linux + 272) -#define TARGET_NR_ioprio_set (TARGET_NR_Linux + 273) -#define TARGET_NR_ioprio_get (TARGET_NR_Linux + 274) -#define TARGET_NR_utimensat (TARGET_NR_Linux + 275) -#define TARGET_NR_signalfd (TARGET_NR_Linux + 276) -#define TARGET_NR_timerfd (TARGET_NR_Linux + 277) -#define TARGET_NR_eventfd (TARGET_NR_Linux + 278) -#define TARGET_NR_fallocate (TARGET_NR_Linux + 279) -#define TARGET_NR_timerfd_create (TARGET_NR_Linux + 280) -#define TARGET_NR_timerfd_gettime (TARGET_NR_Linux + 281) -#define TARGET_NR_timerfd_settime (TARGET_NR_Linux + 282) -#define TARGET_NR_signalfd4 (TARGET_NR_Linux + 283) -#define TARGET_NR_eventfd2 (TARGET_NR_Linux + 284) -#define TARGET_NR_epoll_create1 (TARGET_NR_Linux + 285) -#define TARGET_NR_dup3 (TARGET_NR_Linux + 286) -#define TARGET_NR_pipe2 (TARGET_NR_Linux + 287) -#define TARGET_NR_inotify_init1 (TARGET_NR_Linux + 288) -#define TARGET_NR_preadv (TARGET_NR_Linux + 289) -#define TARGET_NR_pwritev (TARGET_NR_Linux + 290) -#define TARGET_NR_rt_tgsigqueueinfo (TARGET_NR_Linux + 291) -#define TARGET_NR_perf_event_open (TARGET_NR_Linux + 292) -#define TARGET_NR_accept4 (TARGET_NR_Linux + 293) -#define TARGET_NR_recvmmsg (TARGET_NR_Linux + 294) -#define TARGET_NR_fanotify_init (TARGET_NR_Linux + 295) -#define TARGET_NR_fanotify_mark (TARGET_NR_Linux + 296) -#define TARGET_NR_prlimit64 (TARGET_NR_Linux + 297) -#define TARGET_NR_name_to_handle_at (TARGET_NR_Linux + 298) -#define TARGET_NR_open_by_handle_at (TARGET_NR_Linux + 299) -#define TARGET_NR_clock_adjtime (TARGET_NR_Linux + 300) -#define TARGET_NR_syncfs (TARGET_NR_Linux + 301) -#define TARGET_NR_sendmmsg (TARGET_NR_Linux + 302) -#define TARGET_NR_setns (TARGET_NR_Linux + 303) -#define TARGET_NR_process_vm_readv (TARGET_NR_Linux + 304) -#define TARGET_NR_process_vm_writev (TARGET_NR_Linux + 305) -#define TARGET_NR_kcmp (TARGET_NR_Linux + 306) -#define TARGET_NR_finit_module (TARGET_NR_Linux + 307) -#define TARGET_NR_getdents64 (TARGET_NR_Linux + 308) -#define TARGET_NR_sched_setattr (TARGET_NR_Linux + 309) -#define TARGET_NR_sched_getattr (TARGET_NR_Linux + 310) -#define TARGET_NR_renameat2 (TARGET_NR_Linux + 311) -#define TARGET_NR_seccomp (TARGET_NR_Linux + 312) -#define TARGET_NR_getrandom (TARGET_NR_Linux + 313) -#define TARGET_NR_memfd_create (TARGET_NR_Linux + 314) -#define TARGET_NR_bpf (TARGET_NR_Linux + 315) -#define TARGET_NR_execveat (TARGET_NR_Linux + 316) -#define TARGET_NR_userfaultfd (TARGET_NR_Linux + 317) -#define TARGET_NR_membarrier (TARGET_NR_Linux + 318) -#define TARGET_NR_mlock2 (TARGET_NR_Linux + 319) -#define TARGET_NR_copy_file_range (TARGET_NR_Linux + 320) -#define TARGET_NR_preadv2 (TARGET_NR_Linux + 321) -#define TARGET_NR_pwritev2 (TARGET_NR_Linux + 322) -#define TARGET_NR_pkey_mprotect (TARGET_NR_Linux + 323) -#define TARGET_NR_pkey_alloc (TARGET_NR_Linux + 324) -#define TARGET_NR_pkey_free (TARGET_NR_Linux + 325) -#define TARGET_NR_statx (TARGET_NR_Linux + 326) -#define TARGET_NR_rseq (TARGET_NR_Linux + 327) -#define TARGET_NR_io_pgetevents (TARGET_NR_Linux + 328) -#endif - -#endif diff --git a/linux-user/mips64/syscallhdr.sh b/linux-user/mips64/syscallhdr.sh new file mode 100644 index 0000000000..ed5a45165a --- /dev/null +++ b/linux-user/mips64/syscallhdr.sh @@ -0,0 +1,33 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 + +in="$1" +out="$2" +my_abis=`echo "($3)" | tr ',' '|'` +prefix="$4" +offset="$5" + +fileguard=LINUX_USER_MIPS64_`basename "$out" | sed \ + -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/' \ + -e 's/[^A-Z0-9_]/_/g' -e 's/__/_/g'` +grep -E "^[0-9A-Fa-fXx]+[[:space:]]+${my_abis}" "$in" | sort -n | ( + printf "#ifndef %s\n" "${fileguard}" + printf "#define %s\n" "${fileguard}" + printf "\n" + + nxt=0 + while read nr abi name entry compat ; do + if [ -z "$offset" ]; then + printf "#define TARGET_NR_%s%s\t%s\n" \ + "${prefix}" "${name}" "${nr}" + else + printf "#define TARGET_NR_%s%s\t(%s + %s)\n" \ + "${prefix}" "${name}" "${offset}" "${nr}" + fi + nxt=$((nr+1)) + done + + printf "\n" + printf "#endif /* %s */" "${fileguard}" + printf "\n" +) > "$out" diff --git a/linux-user/mmap.c b/linux-user/mmap.c index 46a6e3a761..e378033797 100644 --- a/linux-user/mmap.c +++ b/linux-user/mmap.c @@ -17,11 +17,10 @@ * along with this program; if not, see . */ #include "qemu/osdep.h" - +#include "trace.h" +#include "exec/log.h" #include "qemu.h" -//#define DEBUG_MMAP - static pthread_mutex_t mmap_mutex = PTHREAD_MUTEX_INITIALIZER; static __thread int mmap_lock_count; @@ -66,13 +65,7 @@ int target_mprotect(abi_ulong start, abi_ulong len, int prot) abi_ulong end, host_start, host_end, addr; int prot1, ret; -#ifdef DEBUG_MMAP - printf("mprotect: start=0x" TARGET_ABI_FMT_lx - "len=0x" TARGET_ABI_FMT_lx " prot=%c%c%c\n", start, len, - prot & PROT_READ ? 'r' : '-', - prot & PROT_WRITE ? 'w' : '-', - prot & PROT_EXEC ? 'x' : '-'); -#endif + trace_target_mprotect(start, len, prot); if ((start & ~TARGET_PAGE_MASK) != 0) return -TARGET_EINVAL; @@ -191,7 +184,11 @@ static int mmap_frag(abi_ulong real_start, } #if HOST_LONG_BITS == 64 && TARGET_ABI_BITS == 64 +#ifdef TARGET_AARCH64 +# define TASK_UNMAPPED_BASE 0x5500000000 +#else # define TASK_UNMAPPED_BASE (1ul << 38) +#endif #else # define TASK_UNMAPPED_BASE 0x40000000 #endif @@ -369,32 +366,7 @@ abi_long target_mmap(abi_ulong start, abi_ulong len, int prot, abi_ulong ret, end, real_start, real_end, retaddr, host_offset, host_len; mmap_lock(); -#ifdef DEBUG_MMAP - { - printf("mmap: start=0x" TARGET_ABI_FMT_lx - " len=0x" TARGET_ABI_FMT_lx " prot=%c%c%c flags=", - start, len, - prot & PROT_READ ? 'r' : '-', - prot & PROT_WRITE ? 'w' : '-', - prot & PROT_EXEC ? 'x' : '-'); - if (flags & MAP_FIXED) - printf("MAP_FIXED "); - if (flags & MAP_ANONYMOUS) - printf("MAP_ANON "); - switch(flags & MAP_TYPE) { - case MAP_PRIVATE: - printf("MAP_PRIVATE "); - break; - case MAP_SHARED: - printf("MAP_SHARED "); - break; - default: - printf("[MAP_TYPE=0x%x] ", flags & MAP_TYPE); - break; - } - printf("fd=%d offset=" TARGET_ABI_FMT_lx "\n", fd, offset); - } -#endif + trace_target_mmap(start, len, prot, flags, fd, offset); if (!len) { errno = EINVAL; @@ -569,11 +541,10 @@ abi_long target_mmap(abi_ulong start, abi_ulong len, int prot, the_end1: page_set_flags(start, start + len, prot | PAGE_VALID); the_end: -#ifdef DEBUG_MMAP - printf("ret=0x" TARGET_ABI_FMT_lx "\n", start); - page_dump(stdout); - printf("\n"); -#endif + trace_target_mmap_complete(start); + if (qemu_loglevel_mask(CPU_LOG_PAGE)) { + log_page_dump(__func__); + } tb_invalidate_phys_range(start, start + len); mmap_unlock(); return start; @@ -628,11 +599,8 @@ int target_munmap(abi_ulong start, abi_ulong len) abi_ulong end, real_start, real_end, addr; int prot, ret; -#ifdef DEBUG_MMAP - printf("munmap: start=0x" TARGET_ABI_FMT_lx " len=0x" - TARGET_ABI_FMT_lx "\n", - start, len); -#endif + trace_target_munmap(start, len); + if (start & ~TARGET_PAGE_MASK) return -TARGET_EINVAL; len = TARGET_PAGE_ALIGN(len); diff --git a/linux-user/nios2/syscall_nr.h b/linux-user/nios2/syscall_nr.h index 8fb87864ca..32d485dc9a 100644 --- a/linux-user/nios2/syscall_nr.h +++ b/linux-user/nios2/syscall_nr.h @@ -1,334 +1,324 @@ +/* + * This file contains the system call numbers. + * Do not modify. + * This file is generated by scripts/gensyscalls.sh + */ #ifndef LINUX_USER_NIOS2_SYSCALL_NR_H #define LINUX_USER_NIOS2_SYSCALL_NR_H -#define TARGET_NR_io_setup 0 -#define TARGET_NR_io_destroy 1 -#define TARGET_NR_io_submit 2 -#define TARGET_NR_io_cancel 3 -#define TARGET_NR_io_getevents 4 -#define TARGET_NR_setxattr 5 -#define TARGET_NR_lsetxattr 6 -#define TARGET_NR_fsetxattr 7 -#define TARGET_NR_getxattr 8 -#define TARGET_NR_lgetxattr 9 -#define TARGET_NR_fgetxattr 10 -#define TARGET_NR_listxattr 11 -#define TARGET_NR_llistxattr 12 -#define TARGET_NR_flistxattr 13 -#define TARGET_NR_removexattr 14 -#define TARGET_NR_lremovexattr 15 -#define TARGET_NR_fremovexattr 16 -#define TARGET_NR_getcwd 17 -#define TARGET_NR_lookup_dcookie 18 -#define TARGET_NR_eventfd2 19 -#define TARGET_NR_epoll_create1 20 -#define TARGET_NR_epoll_ctl 21 -#define TARGET_NR_epoll_pwait 22 -#define TARGET_NR_dup 23 -#define TARGET_NR_dup3 24 -#define TARGET_NR_fcntl64 25 -#define TARGET_NR_inotify_init1 26 -#define TARGET_NR_inotify_add_watch 27 -#define TARGET_NR_inotify_rm_watch 28 -#define TARGET_NR_ioctl 29 -#define TARGET_NR_ioprio_set 30 -#define TARGET_NR_ioprio_get 31 -#define TARGET_NR_flock 32 -#define TARGET_NR_mknodat 33 -#define TARGET_NR_mkdirat 34 -#define TARGET_NR_unlinkat 35 -#define TARGET_NR_symlinkat 36 -#define TARGET_NR_linkat 37 -#define TARGET_NR_renameat 38 -#define TARGET_NR_umount2 39 -#define TARGET_NR_mount 40 -#define TARGET_NR_pivot_root 41 -#define TARGET_NR_nfsservctl 42 -#define TARGET_NR_statfs64 43 -#define TARGET_NR_fstatfs64 44 -#define TARGET_NR_truncate64 45 -#define TARGET_NR_ftruncate64 46 -#define TARGET_NR_fallocate 47 -#define TARGET_NR_faccessat 48 -#define TARGET_NR_chdir 49 -#define TARGET_NR_fchdir 50 -#define TARGET_NR_chroot 51 -#define TARGET_NR_fchmod 52 -#define TARGET_NR_fchmodat 53 -#define TARGET_NR_fchownat 54 -#define TARGET_NR_fchown 55 -#define TARGET_NR_openat 56 -#define TARGET_NR_close 57 -#define TARGET_NR_vhangup 58 -#define TARGET_NR_pipe2 59 -#define TARGET_NR_quotactl 60 -#define TARGET_NR_getdents64 61 -#define TARGET_NR_read 63 -#define TARGET_NR_write 64 -#define TARGET_NR_readv 65 -#define TARGET_NR_writev 66 -#define TARGET_NR_pread64 67 -#define TARGET_NR_pwrite64 68 -#define TARGET_NR_preadv 69 -#define TARGET_NR_pwritev 70 -#define TARGET_NR_sendfile64 71 -#define TARGET_NR_pselect6 72 -#define TARGET_NR_ppoll 73 -#define TARGET_NR_signalfd4 74 -#define TARGET_NR_vmsplice 75 -#define TARGET_NR_splice 76 -#define TARGET_NR_tee 77 -#define TARGET_NR_readlinkat 78 -#define TARGET_NR_fstatat64 79 -#define TARGET_NR_fstat64 80 -#define TARGET_NR_sync 81 -#define TARGET_NR_fsync 82 -#define TARGET_NR_fdatasync 83 -#define TARGET_NR_sync_file_range 84 -#define TARGET_NR_timerfd_create 85 -#define TARGET_NR_timerfd_settime 86 -#define TARGET_NR_timerfd_gettime 87 -#define TARGET_NR_utimensat 88 -#define TARGET_NR_acct 89 -#define TARGET_NR_capget 90 -#define TARGET_NR_capset 91 -#define TARGET_NR_personality 92 -#define TARGET_NR_exit 93 -#define TARGET_NR_exit_group 94 -#define TARGET_NR_waitid 95 -#define TARGET_NR_set_tid_address 96 -#define TARGET_NR_unshare 97 -#define TARGET_NR_futex 98 -#define TARGET_NR_set_robust_list 99 -#define TARGET_NR_get_robust_list 100 -#define TARGET_NR_nanosleep 101 -#define TARGET_NR_getitimer 102 -#define TARGET_NR_setitimer 103 -#define TARGET_NR_kexec_load 104 -#define TARGET_NR_init_module 105 -#define TARGET_NR_delete_module 106 -#define TARGET_NR_timer_create 107 -#define TARGET_NR_timer_gettime 108 -#define TARGET_NR_timer_getoverrun 109 -#define TARGET_NR_timer_settime 110 -#define TARGET_NR_timer_delete 111 -#define TARGET_NR_clock_settime 112 -#define TARGET_NR_clock_gettime 113 -#define TARGET_NR_clock_getres 114 -#define TARGET_NR_clock_nanosleep 115 -#define TARGET_NR_syslog 116 -#define TARGET_NR_ptrace 117 -#define TARGET_NR_sched_setparam 118 -#define TARGET_NR_sched_setscheduler 119 -#define TARGET_NR_sched_getscheduler 120 -#define TARGET_NR_sched_getparam 121 -#define TARGET_NR_sched_setaffinity 122 -#define TARGET_NR_sched_getaffinity 123 -#define TARGET_NR_sched_yield 124 -#define TARGET_NR_sched_get_priority_max 125 -#define TARGET_NR_sched_get_priority_min 126 -#define TARGET_NR_sched_rr_get_interval 127 -#define TARGET_NR_restart_syscall 128 -#define TARGET_NR_kill 129 -#define TARGET_NR_tkill 130 -#define TARGET_NR_tgkill 131 -#define TARGET_NR_sigaltstack 132 -#define TARGET_NR_rt_sigsuspend 133 -#define TARGET_NR_rt_sigaction 134 -#define TARGET_NR_rt_sigprocmask 135 -#define TARGET_NR_rt_sigpending 136 -#define TARGET_NR_rt_sigtimedwait 137 -#define TARGET_NR_rt_sigqueueinfo 138 -#define TARGET_NR_rt_sigreturn 139 -#define TARGET_NR_setpriority 140 -#define TARGET_NR_getpriority 141 -#define TARGET_NR_reboot 142 -#define TARGET_NR_setregid 143 -#define TARGET_NR_setgid 144 -#define TARGET_NR_setreuid 145 -#define TARGET_NR_setuid 146 -#define TARGET_NR_setresuid 147 -#define TARGET_NR_getresuid 148 -#define TARGET_NR_setresgid 149 -#define TARGET_NR_getresgid 150 -#define TARGET_NR_setfsuid 151 -#define TARGET_NR_setfsgid 152 -#define TARGET_NR_times 153 -#define TARGET_NR_setpgid 154 -#define TARGET_NR_getpgid 155 -#define TARGET_NR_getsid 156 -#define TARGET_NR_setsid 157 -#define TARGET_NR_getgroups 158 -#define TARGET_NR_setgroups 159 -#define TARGET_NR_uname 160 -#define TARGET_NR_sethostname 161 -#define TARGET_NR_setdomainname 162 -#define TARGET_NR_getrlimit 163 -#define TARGET_NR_setrlimit 164 -#define TARGET_NR_getrusage 165 -#define TARGET_NR_umask 166 -#define TARGET_NR_prctl 167 -#define TARGET_NR_getcpu 168 -#define TARGET_NR_gettimeofday 169 -#define TARGET_NR_settimeofday 170 -#define TARGET_NR_adjtimex 171 -#define TARGET_NR_getpid 172 -#define TARGET_NR_getppid 173 -#define TARGET_NR_getuid 174 -#define TARGET_NR_geteuid 175 -#define TARGET_NR_getgid 176 -#define TARGET_NR_getegid 177 -#define TARGET_NR_gettid 178 -#define TARGET_NR_sysinfo 179 -#define TARGET_NR_mq_open 180 -#define TARGET_NR_mq_unlink 181 -#define TARGET_NR_mq_timedsend 182 -#define TARGET_NR_mq_timedreceive 183 -#define TARGET_NR_mq_notify 184 -#define TARGET_NR_mq_getsetattr 185 -#define TARGET_NR_msgget 186 -#define TARGET_NR_msgctl 187 -#define TARGET_NR_msgrcv 188 -#define TARGET_NR_msgsnd 189 -#define TARGET_NR_semget 190 -#define TARGET_NR_semctl 191 -#define TARGET_NR_semtimedop 192 -#define TARGET_NR_semop 193 -#define TARGET_NR_shmget 194 -#define TARGET_NR_shmctl 195 -#define TARGET_NR_shmat 196 -#define TARGET_NR_shmdt 197 -#define TARGET_NR_socket 198 -#define TARGET_NR_socketpair 199 -#define TARGET_NR_bind 200 -#define TARGET_NR_listen 201 -#define TARGET_NR_accept 202 -#define TARGET_NR_connect 203 -#define TARGET_NR_getsockname 204 -#define TARGET_NR_getpeername 205 -#define TARGET_NR_sendto 206 -#define TARGET_NR_recvfrom 207 -#define TARGET_NR_setsockopt 208 -#define TARGET_NR_getsockopt 209 -#define TARGET_NR_shutdown 210 -#define TARGET_NR_sendmsg 211 -#define TARGET_NR_recvmsg 212 -#define TARGET_NR_readahead 213 -#define TARGET_NR_brk 214 -#define TARGET_NR_munmap 215 -#define TARGET_NR_mremap 216 -#define TARGET_NR_add_key 217 -#define TARGET_NR_request_key 218 -#define TARGET_NR_keyctl 219 -#define TARGET_NR_clone 220 -#define TARGET_NR_execve 221 -#define TARGET_NR_mmap2 222 -#define TARGET_NR_fadvise64_64 223 -#define TARGET_NR_swapon 224 -#define TARGET_NR_swapoff 225 -#define TARGET_NR_mprotect 226 -#define TARGET_NR_msync 227 -#define TARGET_NR_mlock 228 -#define TARGET_NR_munlock 229 -#define TARGET_NR_mlockall 230 -#define TARGET_NR_munlockall 231 -#define TARGET_NR_mincore 232 -#define TARGET_NR_madvise 233 -#define TARGET_NR_remap_file_pages 234 -#define TARGET_NR_mbind 235 -#define TARGET_NR_get_mempolicy 236 -#define TARGET_NR_set_mempolicy 237 -#define TARGET_NR_migrate_pages 238 -#define TARGET_NR_move_pages 239 -#define TARGET_NR_rt_tgsigqueueinfo 240 -#define TARGET_NR_perf_event_open 241 -#define TARGET_NR_accept4 242 -#define TARGET_NR_recvmmsg 243 -#define TARGET_NR_cacheflush 244 -#define TARGET_NR_arch_specific_syscall 244 -#define TARGET_NR_wait4 260 -#define TARGET_NR_prlimit64 261 -#define TARGET_NR_fanotify_init 262 -#define TARGET_NR_fanotify_mark 263 -#define TARGET_NR_name_to_handle_at 264 -#define TARGET_NR_open_by_handle_at 265 -#define TARGET_NR_clock_adjtime 266 -#define TARGET_NR_syncfs 267 -#define TARGET_NR_setns 268 -#define TARGET_NR_sendmmsg 269 -#define TARGET_NR_process_vm_readv 270 -#define TARGET_NR_process_vm_writev 271 -#define TARGET_NR_kcmp 272 -#define TARGET_NR_finit_module 273 -#define TARGET_NR_sched_setattr 274 -#define TARGET_NR_sched_getattr 275 -#define TARGET_NR_renameat2 276 -#define TARGET_NR_seccomp 277 -#define TARGET_NR_getrandom 278 -#define TARGET_NR_memfd_create 279 -#define TARGET_NR_bpf 280 -#define TARGET_NR_execveat 281 -#define TARGET_NR_userfaultfd 282 -#define TARGET_NR_membarrier 283 -#define TARGET_NR_mlock2 284 -#define TARGET_NR_copy_file_range 285 -#define TARGET_NR_preadv2 286 -#define TARGET_NR_pwritev2 287 -#define TARGET_NR_open 1024 -#define TARGET_NR_link 1025 -#define TARGET_NR_unlink 1026 -#define TARGET_NR_mknod 1027 -#define TARGET_NR_chmod 1028 -#define TARGET_NR_chown 1029 -#define TARGET_NR_mkdir 1030 -#define TARGET_NR_rmdir 1031 -#define TARGET_NR_lchown 1032 -#define TARGET_NR_access 1033 -#define TARGET_NR_rename 1034 -#define TARGET_NR_readlink 1035 -#define TARGET_NR_symlink 1036 -#define TARGET_NR_utimes 1037 -#define TARGET_NR_3264_stat 1038 -#define TARGET_NR_3264_lstat 1039 -#define TARGET_NR_pipe 1040 -#define TARGET_NR_dup2 1041 -#define TARGET_NR_epoll_create 1042 -#define TARGET_NR_inotify_init 1043 -#define TARGET_NR_eventfd 1044 -#define TARGET_NR_signalfd 1045 -#define TARGET_NR_sendfile 1046 -#define TARGET_NR_ftruncate 1047 -#define TARGET_NR_truncate 1048 -#define TARGET_NR_stat 1049 -#define TARGET_NR_lstat 1050 -#define TARGET_NR_fstat 1051 -#define TARGET_NR_fcntl 1052 -#define TARGET_NR_fadvise64 1053 -#define TARGET_NR_newfstatat 1054 -#define TARGET_NR_fstatfs 1055 -#define TARGET_NR_statfs 1056 -#define TARGET_NR_lseek 1057 -#define TARGET_NR_mmap 1058 -#define TARGET_NR_alarm 1059 -#define TARGET_NR_getpgrp 1060 -#define TARGET_NR_pause 1061 -#define TARGET_NR_time 1062 -#define TARGET_NR_utime 1063 -#define TARGET_NR_creat 1064 -#define TARGET_NR_getdents 1065 -#define TARGET_NR_futimesat 1066 -#define TARGET_NR_select 1067 -#define TARGET_NR_poll 1068 -#define TARGET_NR_epoll_wait 1069 -#define TARGET_NR_ustat 1070 -#define TARGET_NR_vfork 1071 -#define TARGET_NR_oldwait4 1072 -#define TARGET_NR_recv 1073 -#define TARGET_NR_send 1074 -#define TARGET_NR_bdflush 1075 -#define TARGET_NR_umount 1076 -#define TARGET_NR_uselib 1077 -#define TARGET_NR__sysctl 1078 -#define TARGET_NR_fork 1079 +#define TARGET_NR_cacheflush (TARGET_NR_arch_specific_syscall) +#define TARGET_NR_io_setup 0 +#define TARGET_NR_io_destroy 1 +#define TARGET_NR_io_submit 2 +#define TARGET_NR_io_cancel 3 +#define TARGET_NR_io_getevents 4 +#define TARGET_NR_setxattr 5 +#define TARGET_NR_lsetxattr 6 +#define TARGET_NR_fsetxattr 7 +#define TARGET_NR_getxattr 8 +#define TARGET_NR_lgetxattr 9 +#define TARGET_NR_fgetxattr 10 +#define TARGET_NR_listxattr 11 +#define TARGET_NR_llistxattr 12 +#define TARGET_NR_flistxattr 13 +#define TARGET_NR_removexattr 14 +#define TARGET_NR_lremovexattr 15 +#define TARGET_NR_fremovexattr 16 +#define TARGET_NR_getcwd 17 +#define TARGET_NR_lookup_dcookie 18 +#define TARGET_NR_eventfd2 19 +#define TARGET_NR_epoll_create1 20 +#define TARGET_NR_epoll_ctl 21 +#define TARGET_NR_epoll_pwait 22 +#define TARGET_NR_dup 23 +#define TARGET_NR_dup3 24 +#define TARGET_NR_fcntl64 25 +#define TARGET_NR_inotify_init1 26 +#define TARGET_NR_inotify_add_watch 27 +#define TARGET_NR_inotify_rm_watch 28 +#define TARGET_NR_ioctl 29 +#define TARGET_NR_ioprio_set 30 +#define TARGET_NR_ioprio_get 31 +#define TARGET_NR_flock 32 +#define TARGET_NR_mknodat 33 +#define TARGET_NR_mkdirat 34 +#define TARGET_NR_unlinkat 35 +#define TARGET_NR_symlinkat 36 +#define TARGET_NR_linkat 37 +#define TARGET_NR_renameat 38 +#define TARGET_NR_umount2 39 +#define TARGET_NR_mount 40 +#define TARGET_NR_pivot_root 41 +#define TARGET_NR_nfsservctl 42 +#define TARGET_NR_statfs64 43 +#define TARGET_NR_fstatfs64 44 +#define TARGET_NR_truncate64 45 +#define TARGET_NR_ftruncate64 46 +#define TARGET_NR_fallocate 47 +#define TARGET_NR_faccessat 48 +#define TARGET_NR_chdir 49 +#define TARGET_NR_fchdir 50 +#define TARGET_NR_chroot 51 +#define TARGET_NR_fchmod 52 +#define TARGET_NR_fchmodat 53 +#define TARGET_NR_fchownat 54 +#define TARGET_NR_fchown 55 +#define TARGET_NR_openat 56 +#define TARGET_NR_close 57 +#define TARGET_NR_vhangup 58 +#define TARGET_NR_pipe2 59 +#define TARGET_NR_quotactl 60 +#define TARGET_NR_getdents64 61 +#define TARGET_NR_llseek 62 +#define TARGET_NR_read 63 +#define TARGET_NR_write 64 +#define TARGET_NR_readv 65 +#define TARGET_NR_writev 66 +#define TARGET_NR_pread64 67 +#define TARGET_NR_pwrite64 68 +#define TARGET_NR_preadv 69 +#define TARGET_NR_pwritev 70 +#define TARGET_NR_sendfile64 71 +#define TARGET_NR_pselect6 72 +#define TARGET_NR_ppoll 73 +#define TARGET_NR_signalfd4 74 +#define TARGET_NR_vmsplice 75 +#define TARGET_NR_splice 76 +#define TARGET_NR_tee 77 +#define TARGET_NR_readlinkat 78 +#define TARGET_NR_fstatat64 79 +#define TARGET_NR_fstat64 80 +#define TARGET_NR_sync 81 +#define TARGET_NR_fsync 82 +#define TARGET_NR_fdatasync 83 +#define TARGET_NR_sync_file_range 84 +#define TARGET_NR_timerfd_create 85 +#define TARGET_NR_timerfd_settime 86 +#define TARGET_NR_timerfd_gettime 87 +#define TARGET_NR_utimensat 88 +#define TARGET_NR_acct 89 +#define TARGET_NR_capget 90 +#define TARGET_NR_capset 91 +#define TARGET_NR_personality 92 +#define TARGET_NR_exit 93 +#define TARGET_NR_exit_group 94 +#define TARGET_NR_waitid 95 +#define TARGET_NR_set_tid_address 96 +#define TARGET_NR_unshare 97 +#define TARGET_NR_futex 98 +#define TARGET_NR_set_robust_list 99 +#define TARGET_NR_get_robust_list 100 +#define TARGET_NR_nanosleep 101 +#define TARGET_NR_getitimer 102 +#define TARGET_NR_setitimer 103 +#define TARGET_NR_kexec_load 104 +#define TARGET_NR_init_module 105 +#define TARGET_NR_delete_module 106 +#define TARGET_NR_timer_create 107 +#define TARGET_NR_timer_gettime 108 +#define TARGET_NR_timer_getoverrun 109 +#define TARGET_NR_timer_settime 110 +#define TARGET_NR_timer_delete 111 +#define TARGET_NR_clock_settime 112 +#define TARGET_NR_clock_gettime 113 +#define TARGET_NR_clock_getres 114 +#define TARGET_NR_clock_nanosleep 115 +#define TARGET_NR_syslog 116 +#define TARGET_NR_ptrace 117 +#define TARGET_NR_sched_setparam 118 +#define TARGET_NR_sched_setscheduler 119 +#define TARGET_NR_sched_getscheduler 120 +#define TARGET_NR_sched_getparam 121 +#define TARGET_NR_sched_setaffinity 122 +#define TARGET_NR_sched_getaffinity 123 +#define TARGET_NR_sched_yield 124 +#define TARGET_NR_sched_get_priority_max 125 +#define TARGET_NR_sched_get_priority_min 126 +#define TARGET_NR_sched_rr_get_interval 127 +#define TARGET_NR_restart_syscall 128 +#define TARGET_NR_kill 129 +#define TARGET_NR_tkill 130 +#define TARGET_NR_tgkill 131 +#define TARGET_NR_sigaltstack 132 +#define TARGET_NR_rt_sigsuspend 133 +#define TARGET_NR_rt_sigaction 134 +#define TARGET_NR_rt_sigprocmask 135 +#define TARGET_NR_rt_sigpending 136 +#define TARGET_NR_rt_sigtimedwait 137 +#define TARGET_NR_rt_sigqueueinfo 138 +#define TARGET_NR_rt_sigreturn 139 +#define TARGET_NR_setpriority 140 +#define TARGET_NR_getpriority 141 +#define TARGET_NR_reboot 142 +#define TARGET_NR_setregid 143 +#define TARGET_NR_setgid 144 +#define TARGET_NR_setreuid 145 +#define TARGET_NR_setuid 146 +#define TARGET_NR_setresuid 147 +#define TARGET_NR_getresuid 148 +#define TARGET_NR_setresgid 149 +#define TARGET_NR_getresgid 150 +#define TARGET_NR_setfsuid 151 +#define TARGET_NR_setfsgid 152 +#define TARGET_NR_times 153 +#define TARGET_NR_setpgid 154 +#define TARGET_NR_getpgid 155 +#define TARGET_NR_getsid 156 +#define TARGET_NR_setsid 157 +#define TARGET_NR_getgroups 158 +#define TARGET_NR_setgroups 159 +#define TARGET_NR_uname 160 +#define TARGET_NR_sethostname 161 +#define TARGET_NR_setdomainname 162 +#define TARGET_NR_getrlimit 163 +#define TARGET_NR_setrlimit 164 +#define TARGET_NR_getrusage 165 +#define TARGET_NR_umask 166 +#define TARGET_NR_prctl 167 +#define TARGET_NR_getcpu 168 +#define TARGET_NR_gettimeofday 169 +#define TARGET_NR_settimeofday 170 +#define TARGET_NR_adjtimex 171 +#define TARGET_NR_getpid 172 +#define TARGET_NR_getppid 173 +#define TARGET_NR_getuid 174 +#define TARGET_NR_geteuid 175 +#define TARGET_NR_getgid 176 +#define TARGET_NR_getegid 177 +#define TARGET_NR_gettid 178 +#define TARGET_NR_sysinfo 179 +#define TARGET_NR_mq_open 180 +#define TARGET_NR_mq_unlink 181 +#define TARGET_NR_mq_timedsend 182 +#define TARGET_NR_mq_timedreceive 183 +#define TARGET_NR_mq_notify 184 +#define TARGET_NR_mq_getsetattr 185 +#define TARGET_NR_msgget 186 +#define TARGET_NR_msgctl 187 +#define TARGET_NR_msgrcv 188 +#define TARGET_NR_msgsnd 189 +#define TARGET_NR_semget 190 +#define TARGET_NR_semctl 191 +#define TARGET_NR_semtimedop 192 +#define TARGET_NR_semop 193 +#define TARGET_NR_shmget 194 +#define TARGET_NR_shmctl 195 +#define TARGET_NR_shmat 196 +#define TARGET_NR_shmdt 197 +#define TARGET_NR_socket 198 +#define TARGET_NR_socketpair 199 +#define TARGET_NR_bind 200 +#define TARGET_NR_listen 201 +#define TARGET_NR_accept 202 +#define TARGET_NR_connect 203 +#define TARGET_NR_getsockname 204 +#define TARGET_NR_getpeername 205 +#define TARGET_NR_sendto 206 +#define TARGET_NR_recvfrom 207 +#define TARGET_NR_setsockopt 208 +#define TARGET_NR_getsockopt 209 +#define TARGET_NR_shutdown 210 +#define TARGET_NR_sendmsg 211 +#define TARGET_NR_recvmsg 212 +#define TARGET_NR_readahead 213 +#define TARGET_NR_brk 214 +#define TARGET_NR_munmap 215 +#define TARGET_NR_mremap 216 +#define TARGET_NR_add_key 217 +#define TARGET_NR_request_key 218 +#define TARGET_NR_keyctl 219 +#define TARGET_NR_clone 220 +#define TARGET_NR_execve 221 +#define TARGET_NR_mmap2 222 +#define TARGET_NR_fadvise64_64 223 +#define TARGET_NR_swapon 224 +#define TARGET_NR_swapoff 225 +#define TARGET_NR_mprotect 226 +#define TARGET_NR_msync 227 +#define TARGET_NR_mlock 228 +#define TARGET_NR_munlock 229 +#define TARGET_NR_mlockall 230 +#define TARGET_NR_munlockall 231 +#define TARGET_NR_mincore 232 +#define TARGET_NR_madvise 233 +#define TARGET_NR_remap_file_pages 234 +#define TARGET_NR_mbind 235 +#define TARGET_NR_get_mempolicy 236 +#define TARGET_NR_set_mempolicy 237 +#define TARGET_NR_migrate_pages 238 +#define TARGET_NR_move_pages 239 +#define TARGET_NR_rt_tgsigqueueinfo 240 +#define TARGET_NR_perf_event_open 241 +#define TARGET_NR_accept4 242 +#define TARGET_NR_recvmmsg 243 +#define TARGET_NR_arch_specific_syscall 244 +#define TARGET_NR_wait4 260 +#define TARGET_NR_prlimit64 261 +#define TARGET_NR_fanotify_init 262 +#define TARGET_NR_fanotify_mark 263 +#define TARGET_NR_name_to_handle_at 264 +#define TARGET_NR_open_by_handle_at 265 +#define TARGET_NR_clock_adjtime 266 +#define TARGET_NR_syncfs 267 +#define TARGET_NR_setns 268 +#define TARGET_NR_sendmmsg 269 +#define TARGET_NR_process_vm_readv 270 +#define TARGET_NR_process_vm_writev 271 +#define TARGET_NR_kcmp 272 +#define TARGET_NR_finit_module 273 +#define TARGET_NR_sched_setattr 274 +#define TARGET_NR_sched_getattr 275 +#define TARGET_NR_renameat2 276 +#define TARGET_NR_seccomp 277 +#define TARGET_NR_getrandom 278 +#define TARGET_NR_memfd_create 279 +#define TARGET_NR_bpf 280 +#define TARGET_NR_execveat 281 +#define TARGET_NR_userfaultfd 282 +#define TARGET_NR_membarrier 283 +#define TARGET_NR_mlock2 284 +#define TARGET_NR_copy_file_range 285 +#define TARGET_NR_preadv2 286 +#define TARGET_NR_pwritev2 287 +#define TARGET_NR_pkey_mprotect 288 +#define TARGET_NR_pkey_alloc 289 +#define TARGET_NR_pkey_free 290 +#define TARGET_NR_statx 291 +#define TARGET_NR_io_pgetevents 292 +#define TARGET_NR_rseq 293 +#define TARGET_NR_kexec_file_load 294 +#define TARGET_NR_clock_gettime64 403 +#define TARGET_NR_clock_settime64 404 +#define TARGET_NR_clock_adjtime64 405 +#define TARGET_NR_clock_getres_time64 406 +#define TARGET_NR_clock_nanosleep_time64 407 +#define TARGET_NR_timer_gettime64 408 +#define TARGET_NR_timer_settime64 409 +#define TARGET_NR_timerfd_gettime64 410 +#define TARGET_NR_timerfd_settime64 411 +#define TARGET_NR_utimensat_time64 412 +#define TARGET_NR_pselect6_time64 413 +#define TARGET_NR_ppoll_time64 414 +#define TARGET_NR_io_pgetevents_time64 416 +#define TARGET_NR_recvmmsg_time64 417 +#define TARGET_NR_mq_timedsend_time64 418 +#define TARGET_NR_mq_timedreceive_time64 419 +#define TARGET_NR_semtimedop_time64 420 +#define TARGET_NR_rt_sigtimedwait_time64 421 +#define TARGET_NR_futex_time64 422 +#define TARGET_NR_sched_rr_get_interval_time64 423 +#define TARGET_NR_pidfd_send_signal 424 +#define TARGET_NR_io_uring_setup 425 +#define TARGET_NR_io_uring_enter 426 +#define TARGET_NR_io_uring_register 427 +#define TARGET_NR_open_tree 428 +#define TARGET_NR_move_mount 429 +#define TARGET_NR_fsopen 430 +#define TARGET_NR_fsconfig 431 +#define TARGET_NR_fsmount 432 +#define TARGET_NR_fspick 433 +#define TARGET_NR_pidfd_open 434 +#define TARGET_NR_syscalls 436 + +#endif /* LINUX_USER_NIOS2_SYSCALL_NR_H */ -#endif diff --git a/linux-user/openrisc/syscall_nr.h b/linux-user/openrisc/syscall_nr.h index 7763dbcfd8..340383beb2 100644 --- a/linux-user/openrisc/syscall_nr.h +++ b/linux-user/openrisc/syscall_nr.h @@ -1,13 +1,17 @@ +/* + * This file contains the system call numbers. + * Do not modify. + * This file is generated by scripts/gensyscalls.sh + */ #ifndef LINUX_USER_OPENRISC_SYSCALL_NR_H #define LINUX_USER_OPENRISC_SYSCALL_NR_H #define TARGET_NR_io_setup 0 +#define TARGET_NR_or1k_atomic TARGET_NR_arch_specific_syscall #define TARGET_NR_io_destroy 1 #define TARGET_NR_io_submit 2 #define TARGET_NR_io_cancel 3 #define TARGET_NR_io_getevents 4 - -/* fs/xattr.c */ #define TARGET_NR_setxattr 5 #define TARGET_NR_lsetxattr 6 #define TARGET_NR_fsetxattr 7 @@ -20,63 +24,36 @@ #define TARGET_NR_removexattr 14 #define TARGET_NR_lremovexattr 15 #define TARGET_NR_fremovexattr 16 - -/* fs/dcache.c */ #define TARGET_NR_getcwd 17 - -/* fs/cookies.c */ #define TARGET_NR_lookup_dcookie 18 - -/* fs/eventfd.c */ #define TARGET_NR_eventfd2 19 - -/* fs/eventpoll.c */ #define TARGET_NR_epoll_create1 20 #define TARGET_NR_epoll_ctl 21 #define TARGET_NR_epoll_pwait 22 - -/* fs/fcntl.c */ #define TARGET_NR_dup 23 #define TARGET_NR_dup3 24 -#define TARGET_NR_3264_fcntl 25 - -/* fs/inotify_user.c */ +#define TARGET_NR_fcntl64 25 #define TARGET_NR_inotify_init1 26 #define TARGET_NR_inotify_add_watch 27 #define TARGET_NR_inotify_rm_watch 28 - -/* fs/ioctl.c */ #define TARGET_NR_ioctl 29 - -/* fs/ioprio.c */ #define TARGET_NR_ioprio_set 30 #define TARGET_NR_ioprio_get 31 - -/* fs/locks.c */ #define TARGET_NR_flock 32 - -/* fs/namei.c */ #define TARGET_NR_mknodat 33 #define TARGET_NR_mkdirat 34 #define TARGET_NR_unlinkat 35 #define TARGET_NR_symlinkat 36 #define TARGET_NR_linkat 37 #define TARGET_NR_renameat 38 - -/* fs/namespace.c */ #define TARGET_NR_umount2 39 #define TARGET_NR_mount 40 #define TARGET_NR_pivot_root 41 - -/* fs/nfsctl.c */ #define TARGET_NR_nfsservctl 42 - -/* fs/open.c */ -#define TARGET_NR_3264_statfs 43 -#define TARGET_NR_3264_fstatfs 44 -#define TARGET_NR_3264_truncate 45 -#define TARGET_NR_3264_ftruncate 46 - +#define TARGET_NR_statfs64 43 +#define TARGET_NR_fstatfs64 44 +#define TARGET_NR_truncate64 45 +#define TARGET_NR_ftruncate64 46 #define TARGET_NR_fallocate 47 #define TARGET_NR_faccessat 48 #define TARGET_NR_chdir 49 @@ -89,18 +66,10 @@ #define TARGET_NR_openat 56 #define TARGET_NR_close 57 #define TARGET_NR_vhangup 58 - -/* fs/pipe.c */ #define TARGET_NR_pipe2 59 - -/* fs/quota.c */ #define TARGET_NR_quotactl 60 - -/* fs/readdir.c */ #define TARGET_NR_getdents64 61 - -/* fs/read_write.c */ -#define TARGET_NR_3264_lseek 62 +#define TARGET_NR_llseek 62 #define TARGET_NR_read 63 #define TARGET_NR_write 64 #define TARGET_NR_readv 65 @@ -109,85 +78,42 @@ #define TARGET_NR_pwrite64 68 #define TARGET_NR_preadv 69 #define TARGET_NR_pwritev 70 - -/* fs/sendfile.c */ -#define TARGET_NR_3264_sendfile 71 - -/* fs/select.c */ +#define TARGET_NR_sendfile64 71 #define TARGET_NR_pselect6 72 #define TARGET_NR_ppoll 73 - -/* fs/signalfd.c */ #define TARGET_NR_signalfd4 74 - -/* fs/splice.c */ #define TARGET_NR_vmsplice 75 #define TARGET_NR_splice 76 #define TARGET_NR_tee 77 - -/* fs/stat.c */ #define TARGET_NR_readlinkat 78 -#define TARGET_NR_3264_fstatat 79 -#define TARGET_NR_3264_fstat 80 - -/* fs/sync.c */ +#define TARGET_NR_fstatat64 79 +#define TARGET_NR_fstat64 80 #define TARGET_NR_sync 81 #define TARGET_NR_fsync 82 #define TARGET_NR_fdatasync 83 - -#ifdef __ARCH_WANT_SYNC_FILE_RANGE2 -#define TARGET_NR_sync_file_range2 84 -#else #define TARGET_NR_sync_file_range 84 -#endif - -/* fs/timerfd.c */ #define TARGET_NR_timerfd_create 85 #define TARGET_NR_timerfd_settime 86 #define TARGET_NR_timerfd_gettime 87 - -/* fs/utimes.c */ #define TARGET_NR_utimensat 88 - -/* kernel/acct.c */ #define TARGET_NR_acct 89 - -/* kernel/capability.c */ #define TARGET_NR_capget 90 #define TARGET_NR_capset 91 - -/* kernel/exec_domain.c */ #define TARGET_NR_personality 92 - -/* kernel/exit.c */ #define TARGET_NR_exit 93 #define TARGET_NR_exit_group 94 #define TARGET_NR_waitid 95 - -/* kernel/fork.c */ #define TARGET_NR_set_tid_address 96 #define TARGET_NR_unshare 97 - -/* kernel/futex.c */ #define TARGET_NR_futex 98 #define TARGET_NR_set_robust_list 99 #define TARGET_NR_get_robust_list 100 - -/* kernel/hrtimer.c */ #define TARGET_NR_nanosleep 101 - -/* kernel/itimer.c */ #define TARGET_NR_getitimer 102 #define TARGET_NR_setitimer 103 - -/* kernel/kexec.c */ #define TARGET_NR_kexec_load 104 - -/* kernel/module.c */ #define TARGET_NR_init_module 105 #define TARGET_NR_delete_module 106 - -/* kernel/posix-timers.c */ #define TARGET_NR_timer_create 107 #define TARGET_NR_timer_gettime 108 #define TARGET_NR_timer_getoverrun 109 @@ -197,14 +123,8 @@ #define TARGET_NR_clock_gettime 113 #define TARGET_NR_clock_getres 114 #define TARGET_NR_clock_nanosleep 115 - -/* kernel/printk.c */ #define TARGET_NR_syslog 116 - -/* kernel/ptrace.c */ #define TARGET_NR_ptrace 117 - -/* kernel/sched.c */ #define TARGET_NR_sched_setparam 118 #define TARGET_NR_sched_setscheduler 119 #define TARGET_NR_sched_getscheduler 120 @@ -215,8 +135,6 @@ #define TARGET_NR_sched_get_priority_max 125 #define TARGET_NR_sched_get_priority_min 126 #define TARGET_NR_sched_rr_get_interval 127 - -/* kernel/signal.c */ #define TARGET_NR_restart_syscall 128 #define TARGET_NR_kill 129 #define TARGET_NR_tkill 130 @@ -229,8 +147,6 @@ #define TARGET_NR_rt_sigtimedwait 137 #define TARGET_NR_rt_sigqueueinfo 138 #define TARGET_NR_rt_sigreturn 139 - -/* kernel/sys.c */ #define TARGET_NR_setpriority 140 #define TARGET_NR_getpriority 141 #define TARGET_NR_reboot 142 @@ -260,13 +176,9 @@ #define TARGET_NR_umask 166 #define TARGET_NR_prctl 167 #define TARGET_NR_getcpu 168 - -/* kernel/time.c */ #define TARGET_NR_gettimeofday 169 #define TARGET_NR_settimeofday 170 #define TARGET_NR_adjtimex 171 - -/* kernel/timer.c */ #define TARGET_NR_getpid 172 #define TARGET_NR_getppid 173 #define TARGET_NR_getuid 174 @@ -275,34 +187,24 @@ #define TARGET_NR_getegid 177 #define TARGET_NR_gettid 178 #define TARGET_NR_sysinfo 179 - -/* ipc/mqueue.c */ #define TARGET_NR_mq_open 180 #define TARGET_NR_mq_unlink 181 #define TARGET_NR_mq_timedsend 182 #define TARGET_NR_mq_timedreceive 183 #define TARGET_NR_mq_notify 184 #define TARGET_NR_mq_getsetattr 185 - -/* ipc/msg.c */ #define TARGET_NR_msgget 186 #define TARGET_NR_msgctl 187 #define TARGET_NR_msgrcv 188 #define TARGET_NR_msgsnd 189 - -/* ipc/sem.c */ #define TARGET_NR_semget 190 #define TARGET_NR_semctl 191 #define TARGET_NR_semtimedop 192 #define TARGET_NR_semop 193 - -/* ipc/shm.c */ #define TARGET_NR_shmget 194 #define TARGET_NR_shmctl 195 #define TARGET_NR_shmat 196 #define TARGET_NR_shmdt 197 - -/* net/socket.c */ #define TARGET_NR_socket 198 #define TARGET_NR_socketpair 199 #define TARGET_NR_bind 200 @@ -318,30 +220,17 @@ #define TARGET_NR_shutdown 210 #define TARGET_NR_sendmsg 211 #define TARGET_NR_recvmsg 212 - -/* mm/filemap.c */ #define TARGET_NR_readahead 213 - -/* mm/nommu.c, also with MMU */ #define TARGET_NR_brk 214 #define TARGET_NR_munmap 215 #define TARGET_NR_mremap 216 - -/* security/keys/keyctl.c */ #define TARGET_NR_add_key 217 #define TARGET_NR_request_key 218 #define TARGET_NR_keyctl 219 - -/* arch/example/kernel/sys_example.c */ #define TARGET_NR_clone 220 #define TARGET_NR_execve 221 - -#define TARGET_NR_3264_mmap 222 -/* mm/fadvise.c */ -#define TARGET_NR_3264_fadvise64 223 - -/* mm/, CONFIG_MMU only */ -#ifndef __ARCH_NOMMU +#define TARGET_NR_mmap2 222 +#define TARGET_NR_fadvise64_64 223 #define TARGET_NR_swapon 224 #define TARGET_NR_swapoff 225 #define TARGET_NR_mprotect 226 @@ -358,25 +247,17 @@ #define TARGET_NR_set_mempolicy 237 #define TARGET_NR_migrate_pages 238 #define TARGET_NR_move_pages 239 -#endif - #define TARGET_NR_rt_tgsigqueueinfo 240 #define TARGET_NR_perf_event_open 241 #define TARGET_NR_accept4 242 #define TARGET_NR_recvmmsg 243 - -/* - * Architectures may provide up to 16 syscalls of their own - * starting with this value. - */ #define TARGET_NR_arch_specific_syscall 244 - #define TARGET_NR_wait4 260 #define TARGET_NR_prlimit64 261 #define TARGET_NR_fanotify_init 262 #define TARGET_NR_fanotify_mark 263 -#define TARGET_NR_name_to_handle_at 264 -#define TARGET_NR_open_by_handle_at 265 +#define TARGET_NR_name_to_handle_at 264 +#define TARGET_NR_open_by_handle_at 265 #define TARGET_NR_clock_adjtime 266 #define TARGET_NR_syncfs 267 #define TARGET_NR_setns 268 @@ -397,113 +278,47 @@ #define TARGET_NR_membarrier 283 #define TARGET_NR_mlock2 284 #define TARGET_NR_copy_file_range 285 +#define TARGET_NR_preadv2 286 +#define TARGET_NR_pwritev2 287 +#define TARGET_NR_pkey_mprotect 288 +#define TARGET_NR_pkey_alloc 289 +#define TARGET_NR_pkey_free 290 +#define TARGET_NR_statx 291 +#define TARGET_NR_io_pgetevents 292 +#define TARGET_NR_rseq 293 +#define TARGET_NR_kexec_file_load 294 +#define TARGET_NR_clock_gettime64 403 +#define TARGET_NR_clock_settime64 404 +#define TARGET_NR_clock_adjtime64 405 +#define TARGET_NR_clock_getres_time64 406 +#define TARGET_NR_clock_nanosleep_time64 407 +#define TARGET_NR_timer_gettime64 408 +#define TARGET_NR_timer_settime64 409 +#define TARGET_NR_timerfd_gettime64 410 +#define TARGET_NR_timerfd_settime64 411 +#define TARGET_NR_utimensat_time64 412 +#define TARGET_NR_pselect6_time64 413 +#define TARGET_NR_ppoll_time64 414 +#define TARGET_NR_io_pgetevents_time64 416 +#define TARGET_NR_recvmmsg_time64 417 +#define TARGET_NR_mq_timedsend_time64 418 +#define TARGET_NR_mq_timedreceive_time64 419 +#define TARGET_NR_semtimedop_time64 420 +#define TARGET_NR_rt_sigtimedwait_time64 421 +#define TARGET_NR_futex_time64 422 +#define TARGET_NR_sched_rr_get_interval_time64 423 +#define TARGET_NR_pidfd_send_signal 424 +#define TARGET_NR_io_uring_setup 425 +#define TARGET_NR_io_uring_enter 426 +#define TARGET_NR_io_uring_register 427 +#define TARGET_NR_open_tree 428 +#define TARGET_NR_move_mount 429 +#define TARGET_NR_fsopen 430 +#define TARGET_NR_fsconfig 431 +#define TARGET_NR_fsmount 432 +#define TARGET_NR_fspick 433 +#define TARGET_NR_pidfd_open 434 +#define TARGET_NR_syscalls 436 -/* - * All syscalls below here should go away really, - * these are provided for both review and as a porting - * help for the C library version. -* - * Last chance: are any of these important enough to - * enable by default? - */ -#define TARGET_NR_open 1024 -#define TARGET_NR_link 1025 -#define TARGET_NR_unlink 1026 -#define TARGET_NR_mknod 1027 -#define TARGET_NR_chmod 1028 -#define TARGET_NR_chown 1029 -#define TARGET_NR_mkdir 1030 -#define TARGET_NR_rmdir 1031 -#define TARGET_NR_lchown 1032 -#define TARGET_NR_access 1033 -#define TARGET_NR_rename 1034 -#define TARGET_NR_readlink 1035 -#define TARGET_NR_symlink 1036 -#define TARGET_NR_utimes 1037 -#define TARGET_NR_3264_stat 1038 -#define TARGET_NR_3264_lstat 1039 +#endif /* LINUX_USER_OPENRISC_SYSCALL_NR_H */ -#define TARGET_NR_pipe 1040 -#define TARGET_NR_dup2 1041 -#define TARGET_NR_epoll_create 1042 -#define TARGET_NR_inotify_init 1043 -#define TARGET_NR_eventfd 1044 -#define TARGET_NR_signalfd 1045 - -#define TARGET_NR_sendfile 1046 -#define TARGET_NR_ftruncate 1047 -#define TARGET_NR_truncate 1048 -#define TARGET_NR_stat 1049 -#define TARGET_NR_lstat 1050 -#define TARGET_NR_fstat 1051 -#define TARGET_NR_fcntl 1052 -#define TARGET_NR_fadvise64 1053 -#define __ARCH_WANT_SYS_FADVISE64 -#define TARGET_NR_newfstatat 1054 -#define __ARCH_WANT_SYS_NEWFSTATAT -#define TARGET_NR_fstatfs 1055 -#define TARGET_NR_statfs 1056 -#define TARGET_NR_lseek 1057 -#define TARGET_NR_mmap 1058 - -#define TARGET_NR_alarm 1059 -#define __ARCH_WANT_SYS_ALARM -#define TARGET_NR_getpgrp 1060 -#define __ARCH_WANT_SYS_GETPGRP -#define TARGET_NR_pause 1061 -#define __ARCH_WANT_SYS_PAUSE -#define TARGET_NR_time 1062 -#define __ARCH_WANT_SYS_TIME -#define __ARCH_WANT_COMPAT_SYS_TIME -#define TARGET_NR_utime 1063 -#define __ARCH_WANT_SYS_UTIME - -#define TARGET_NR_creat 1064 -#define TARGET_NR_getdents 1065 -#define __ARCH_WANT_SYS_GETDENTS -#define TARGET_NR_futimesat 1066 -#define TARGET_NR_poll 1068 -#define TARGET_NR_epoll_wait 1069 -#define TARGET_NR_ustat 1070 -#define TARGET_NR_vfork 1071 -#define TARGET_NR_oldwait4 1072 -#define TARGET_NR_recv 1073 -#define TARGET_NR_send 1074 -#define TARGET_NR_bdflush 1075 -#define TARGET_NR_umount 1076 -#define __ARCH_WANT_SYS_OLDUMOUNT -#define TARGET_NR_uselib 1077 -#define TARGET_NR__sysctl 1078 - -#define TARGET_NR_fork 1079 - - -/* - * 32 bit systems traditionally used different - * syscalls for off_t and loff_t arguments, while - * 64 bit systems only need the off_t version. - * For new 32 bit platforms, there is no need to - * implement the old 32 bit off_t syscalls, so - * they take different names. - * Here we map the numbers so that both versions - * use the same syscall table layout. - */ - -#define TARGET_NR_fcntl64 TARGET_NR_3264_fcntl -#define TARGET_NR_statfs64 TARGET_NR_3264_statfs -#define TARGET_NR_fstatfs64 TARGET_NR_3264_fstatfs -#define TARGET_NR_truncate64 TARGET_NR_3264_truncate -#define TARGET_NR_ftruncate64 TARGET_NR_3264_ftruncate -#define TARGET_NR_llseek TARGET_NR_3264_lseek -#define TARGET_NR_sendfile64 TARGET_NR_3264_sendfile -#define TARGET_NR_fstatat64 TARGET_NR_3264_fstatat -#define TARGET_NR_fstat64 TARGET_NR_3264_fstat -#define TARGET_NR_mmap2 TARGET_NR_3264_mmap -#define TARGET_NR_fadvise64_64 TARGET_NR_3264_fadvise64 - -#ifdef TARGET_NR_3264_stat -#define TARGET_NR_stat64 TARGET_NR_3264_stat -#define TARGET_NR_lstat64 TARGET_NR_3264_lstat -#endif - -#endif diff --git a/linux-user/ppc/Makefile.objs b/linux-user/ppc/Makefile.objs new file mode 100644 index 0000000000..be92e67eb1 --- /dev/null +++ b/linux-user/ppc/Makefile.objs @@ -0,0 +1,6 @@ +generated-files-y += linux-user/$(TARGET_ABI_DIR)/syscall_nr.h + +syshdr := $(SRC_PATH)/linux-user/$(TARGET_ABI_DIR)/syscallhdr.sh + +%/syscall_nr.h: $(SRC_PATH)/linux-user/$(TARGET_ABI_DIR)/syscall.tbl $(syshdr) + $(call quiet-command, sh $(syshdr) $< $@ $(TARGET_SYSTBL_ABI),"GEN","$@") diff --git a/linux-user/ppc/cpu_loop.c b/linux-user/ppc/cpu_loop.c index d5704def29..5b27f8603e 100644 --- a/linux-user/ppc/cpu_loop.c +++ b/linux-user/ppc/cpu_loop.c @@ -47,6 +47,11 @@ uint32_t cpu_ppc_load_atbu(CPUPPCState *env) return cpu_ppc_get_tb(env) >> 32; } +uint64_t cpu_ppc_load_vtb(CPUPPCState *env) +{ + return cpu_ppc_get_tb(env); +} + uint32_t cpu_ppc601_load_rtcu(CPUPPCState *env) __attribute__ (( alias ("cpu_ppc_load_tbu") )); diff --git a/linux-user/ppc/signal.c b/linux-user/ppc/signal.c index 5b82af6cb6..20a02c197c 100644 --- a/linux-user/ppc/signal.c +++ b/linux-user/ppc/signal.c @@ -35,12 +35,26 @@ struct target_mcontext { target_ulong mc_gregs[48]; /* Includes fpscr. */ uint64_t mc_fregs[33]; + #if defined(TARGET_PPC64) /* Pointer to the vector regs */ target_ulong v_regs; + /* + * On ppc64, this mcontext structure is naturally *unaligned*, + * or rather it is aligned on a 8 bytes boundary but not on + * a 16 byte boundary. This pad fixes it up. This is why we + * cannot use ppc_avr_t, which would force alignment. This is + * also why the vector regs are referenced in the ABI by the + * v_regs pointer above so any amount of padding can be added here. + */ + target_ulong pad; + /* VSCR and VRSAVE are saved separately. Also reserve space for VSX. */ + struct { + uint64_t altivec[34 + 16][2]; + } mc_vregs; #else target_ulong mc_pad[2]; -#endif + /* We need to handle Altivec and SPE at the same time, which no kernel needs to do. Fortunately, the kernel defines this bit to be Altivec-register-large all the time, rather than trying to @@ -48,32 +62,14 @@ struct target_mcontext { union { /* SPE vector registers. One extra for SPEFSCR. */ uint32_t spe[33]; - /* Altivec vector registers. The packing of VSCR and VRSAVE - varies depending on whether we're PPC64 or not: PPC64 splits - them apart; PPC32 stuffs them together. - We also need to account for the VSX registers on PPC64 - */ -#if defined(TARGET_PPC64) -#define QEMU_NVRREG (34 + 16) - /* On ppc64, this mcontext structure is naturally *unaligned*, - * or rather it is aligned on a 8 bytes boundary but not on - * a 16 bytes one. This pad fixes it up. This is also why the - * vector regs are referenced by the v_regs pointer above so - * any amount of padding can be added here + /* + * Altivec vector registers. One extra for VRSAVE. + * On ppc32, we are already aligned to 16 bytes. We could + * use ppc_avr_t, but choose to share the same type as ppc64. */ - target_ulong pad; -#else - /* On ppc32, we are already aligned to 16 bytes */ -#define QEMU_NVRREG 33 -#endif - /* We cannot use ppc_avr_t here as we do *not* want the implied - * 16-bytes alignment that would result from it. This would have - * the effect of making the whole struct target_mcontext aligned - * which breaks the layout of struct target_ucontext on ppc64. - */ - uint64_t altivec[QEMU_NVRREG][2]; -#undef QEMU_NVRREG + uint64_t altivec[33][2]; } mc_vregs; +#endif }; /* See arch/powerpc/include/asm/sigcontext.h. */ @@ -278,6 +274,7 @@ static void save_user_regs(CPUPPCState *env, struct target_mcontext *frame) __put_user((uint32_t)env->spr[SPR_VRSAVE], vrsave); } +#if defined(TARGET_PPC64) /* Save VSX second halves */ if (env->insns_flags2 & PPC2_VSX) { uint64_t *vsregs = (uint64_t *)&frame->mc_vregs.altivec[34]; @@ -286,6 +283,7 @@ static void save_user_regs(CPUPPCState *env, struct target_mcontext *frame) __put_user(*vsrl, &vsregs[i]); } } +#endif /* Save floating point registers. */ if (env->insns_flags & PPC_FLOAT) { @@ -296,22 +294,18 @@ static void save_user_regs(CPUPPCState *env, struct target_mcontext *frame) __put_user((uint64_t) env->fpscr, &frame->mc_fregs[32]); } +#if !defined(TARGET_PPC64) /* Save SPE registers. The kernel only saves the high half. */ if (env->insns_flags & PPC_SPE) { -#if defined(TARGET_PPC64) - for (i = 0; i < ARRAY_SIZE(env->gpr); i++) { - __put_user(env->gpr[i] >> 32, &frame->mc_vregs.spe[i]); - } -#else for (i = 0; i < ARRAY_SIZE(env->gprh); i++) { __put_user(env->gprh[i], &frame->mc_vregs.spe[i]); } -#endif /* Set MSR_SPE in the saved MSR value to indicate that frame->mc_vregs contains valid data. */ msr |= MSR_SPE; __put_user(env->spe_fscr, &frame->mc_vregs.spe[32]); } +#endif /* Store MSR. */ __put_user(msr, &frame->mc_gregs[TARGET_PT_MSR]); @@ -392,6 +386,7 @@ static void restore_user_regs(CPUPPCState *env, __get_user(env->spr[SPR_VRSAVE], vrsave); } +#if defined(TARGET_PPC64) /* Restore VSX second halves */ if (env->insns_flags2 & PPC2_VSX) { uint64_t *vsregs = (uint64_t *)&frame->mc_vregs.altivec[34]; @@ -400,6 +395,7 @@ static void restore_user_regs(CPUPPCState *env, __get_user(*vsrl, &vsregs[i]); } } +#endif /* Restore floating point registers. */ if (env->insns_flags & PPC_FLOAT) { @@ -412,22 +408,15 @@ static void restore_user_regs(CPUPPCState *env, env->fpscr = (uint32_t) fpscr; } +#if !defined(TARGET_PPC64) /* Save SPE registers. The kernel only saves the high half. */ if (env->insns_flags & PPC_SPE) { -#if defined(TARGET_PPC64) - for (i = 0; i < ARRAY_SIZE(env->gpr); i++) { - uint32_t hi; - - __get_user(hi, &frame->mc_vregs.spe[i]); - env->gpr[i] = ((uint64_t)hi << 32) | ((uint32_t) env->gpr[i]); - } -#else for (i = 0; i < ARRAY_SIZE(env->gprh); i++) { __get_user(env->gprh[i], &frame->mc_vregs.spe[i]); } -#endif __get_user(env->spe_fscr, &frame->mc_vregs.spe[32]); } +#endif } #if !defined(TARGET_PPC64) @@ -567,10 +556,8 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, env->nip = tswapl(handler->entry); env->gpr[2] = tswapl(handler->toc); } else { - /* ELFv2 PPC64 function pointers are entry points, but R12 - * must also be set */ - env->nip = tswapl((target_ulong) ka->_sa_handler); - env->gpr[12] = env->nip; + /* ELFv2 PPC64 function pointers are entry points. R12 must also be set. */ + env->gpr[12] = env->nip = ka->_sa_handler; } #else env->nip = (target_ulong) ka->_sa_handler; @@ -588,7 +575,7 @@ sigsegv: } -#if !defined(TARGET_PPC64) +#if !defined(TARGET_PPC64) || defined(TARGET_ABI32) long do_sigreturn(CPUPPCState *env) { struct target_sigcontext *sc = NULL; diff --git a/linux-user/ppc/syscall.tbl b/linux-user/ppc/syscall.tbl new file mode 100644 index 0000000000..35b61bfc1b --- /dev/null +++ b/linux-user/ppc/syscall.tbl @@ -0,0 +1,521 @@ +# SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note +# +# system call numbers and entry vectors for powerpc +# +# The format is: +# +# +# The can be common, spu, nospu, 64, or 32 for this file. +# +0 nospu restart_syscall sys_restart_syscall +1 nospu exit sys_exit +2 nospu fork ppc_fork +3 common read sys_read +4 common write sys_write +5 common open sys_open compat_sys_open +6 common close sys_close +7 common waitpid sys_waitpid +8 common creat sys_creat +9 common link sys_link +10 common unlink sys_unlink +11 nospu execve sys_execve compat_sys_execve +12 common chdir sys_chdir +13 32 time sys_time32 +13 64 time sys_time +13 spu time sys_time +14 common mknod sys_mknod +15 common chmod sys_chmod +16 common lchown sys_lchown +17 common break sys_ni_syscall +18 32 oldstat sys_stat sys_ni_syscall +18 64 oldstat sys_ni_syscall +18 spu oldstat sys_ni_syscall +19 common lseek sys_lseek compat_sys_lseek +20 common getpid sys_getpid +21 nospu mount sys_mount compat_sys_mount +22 32 umount sys_oldumount +22 64 umount sys_ni_syscall +22 spu umount sys_ni_syscall +23 common setuid sys_setuid +24 common getuid sys_getuid +25 32 stime sys_stime32 +25 64 stime sys_stime +25 spu stime sys_stime +26 nospu ptrace sys_ptrace compat_sys_ptrace +27 common alarm sys_alarm +28 32 oldfstat sys_fstat sys_ni_syscall +28 64 oldfstat sys_ni_syscall +28 spu oldfstat sys_ni_syscall +29 nospu pause sys_pause +30 32 utime sys_utime32 +30 64 utime sys_utime +31 common stty sys_ni_syscall +32 common gtty sys_ni_syscall +33 common access sys_access +34 common nice sys_nice +35 common ftime sys_ni_syscall +36 common sync sys_sync +37 common kill sys_kill +38 common rename sys_rename +39 common mkdir sys_mkdir +40 common rmdir sys_rmdir +41 common dup sys_dup +42 common pipe sys_pipe +43 common times sys_times compat_sys_times +44 common prof sys_ni_syscall +45 common brk sys_brk +46 common setgid sys_setgid +47 common getgid sys_getgid +48 nospu signal sys_signal +49 common geteuid sys_geteuid +50 common getegid sys_getegid +51 nospu acct sys_acct +52 nospu umount2 sys_umount +53 common lock sys_ni_syscall +54 common ioctl sys_ioctl compat_sys_ioctl +55 common fcntl sys_fcntl compat_sys_fcntl +56 common mpx sys_ni_syscall +57 common setpgid sys_setpgid +58 common ulimit sys_ni_syscall +59 32 oldolduname sys_olduname +59 64 oldolduname sys_ni_syscall +59 spu oldolduname sys_ni_syscall +60 common umask sys_umask +61 common chroot sys_chroot +62 nospu ustat sys_ustat compat_sys_ustat +63 common dup2 sys_dup2 +64 common getppid sys_getppid +65 common getpgrp sys_getpgrp +66 common setsid sys_setsid +67 32 sigaction sys_sigaction compat_sys_sigaction +67 64 sigaction sys_ni_syscall +67 spu sigaction sys_ni_syscall +68 common sgetmask sys_sgetmask +69 common ssetmask sys_ssetmask +70 common setreuid sys_setreuid +71 common setregid sys_setregid +72 32 sigsuspend sys_sigsuspend +72 64 sigsuspend sys_ni_syscall +72 spu sigsuspend sys_ni_syscall +73 32 sigpending sys_sigpending compat_sys_sigpending +73 64 sigpending sys_ni_syscall +73 spu sigpending sys_ni_syscall +74 common sethostname sys_sethostname +75 common setrlimit sys_setrlimit compat_sys_setrlimit +76 32 getrlimit sys_old_getrlimit compat_sys_old_getrlimit +76 64 getrlimit sys_ni_syscall +76 spu getrlimit sys_ni_syscall +77 common getrusage sys_getrusage compat_sys_getrusage +78 common gettimeofday sys_gettimeofday compat_sys_gettimeofday +79 common settimeofday sys_settimeofday compat_sys_settimeofday +80 common getgroups sys_getgroups +81 common setgroups sys_setgroups +82 32 select ppc_select sys_ni_syscall +82 64 select sys_ni_syscall +82 spu select sys_ni_syscall +83 common symlink sys_symlink +84 32 oldlstat sys_lstat sys_ni_syscall +84 64 oldlstat sys_ni_syscall +84 spu oldlstat sys_ni_syscall +85 common readlink sys_readlink +86 nospu uselib sys_uselib +87 nospu swapon sys_swapon +88 nospu reboot sys_reboot +89 32 readdir sys_old_readdir compat_sys_old_readdir +89 64 readdir sys_ni_syscall +89 spu readdir sys_ni_syscall +90 common mmap sys_mmap +91 common munmap sys_munmap +92 common truncate sys_truncate compat_sys_truncate +93 common ftruncate sys_ftruncate compat_sys_ftruncate +94 common fchmod sys_fchmod +95 common fchown sys_fchown +96 common getpriority sys_getpriority +97 common setpriority sys_setpriority +98 common profil sys_ni_syscall +99 nospu statfs sys_statfs compat_sys_statfs +100 nospu fstatfs sys_fstatfs compat_sys_fstatfs +101 common ioperm sys_ni_syscall +102 common socketcall sys_socketcall compat_sys_socketcall +103 common syslog sys_syslog +104 common setitimer sys_setitimer compat_sys_setitimer +105 common getitimer sys_getitimer compat_sys_getitimer +106 common stat sys_newstat compat_sys_newstat +107 common lstat sys_newlstat compat_sys_newlstat +108 common fstat sys_newfstat compat_sys_newfstat +109 32 olduname sys_uname +109 64 olduname sys_ni_syscall +109 spu olduname sys_ni_syscall +110 common iopl sys_ni_syscall +111 common vhangup sys_vhangup +112 common idle sys_ni_syscall +113 common vm86 sys_ni_syscall +114 common wait4 sys_wait4 compat_sys_wait4 +115 nospu swapoff sys_swapoff +116 common sysinfo sys_sysinfo compat_sys_sysinfo +117 nospu ipc sys_ipc compat_sys_ipc +118 common fsync sys_fsync +119 32 sigreturn sys_sigreturn compat_sys_sigreturn +119 64 sigreturn sys_ni_syscall +119 spu sigreturn sys_ni_syscall +120 nospu clone ppc_clone +121 common setdomainname sys_setdomainname +122 common uname sys_newuname +123 common modify_ldt sys_ni_syscall +124 32 adjtimex sys_adjtimex_time32 +124 64 adjtimex sys_adjtimex +124 spu adjtimex sys_adjtimex +125 common mprotect sys_mprotect +126 32 sigprocmask sys_sigprocmask compat_sys_sigprocmask +126 64 sigprocmask sys_ni_syscall +126 spu sigprocmask sys_ni_syscall +127 common create_module sys_ni_syscall +128 nospu init_module sys_init_module +129 nospu delete_module sys_delete_module +130 common get_kernel_syms sys_ni_syscall +131 nospu quotactl sys_quotactl +132 common getpgid sys_getpgid +133 common fchdir sys_fchdir +134 common bdflush sys_bdflush +135 common sysfs sys_sysfs +136 32 personality sys_personality ppc64_personality +136 64 personality ppc64_personality +136 spu personality ppc64_personality +137 common afs_syscall sys_ni_syscall +138 common setfsuid sys_setfsuid +139 common setfsgid sys_setfsgid +140 common _llseek sys_llseek +141 common getdents sys_getdents compat_sys_getdents +142 common _newselect sys_select compat_sys_select +143 common flock sys_flock +144 common msync sys_msync +145 common readv sys_readv compat_sys_readv +146 common writev sys_writev compat_sys_writev +147 common getsid sys_getsid +148 common fdatasync sys_fdatasync +149 nospu _sysctl sys_sysctl compat_sys_sysctl +150 common mlock sys_mlock +151 common munlock sys_munlock +152 common mlockall sys_mlockall +153 common munlockall sys_munlockall +154 common sched_setparam sys_sched_setparam +155 common sched_getparam sys_sched_getparam +156 common sched_setscheduler sys_sched_setscheduler +157 common sched_getscheduler sys_sched_getscheduler +158 common sched_yield sys_sched_yield +159 common sched_get_priority_max sys_sched_get_priority_max +160 common sched_get_priority_min sys_sched_get_priority_min +161 32 sched_rr_get_interval sys_sched_rr_get_interval_time32 +161 64 sched_rr_get_interval sys_sched_rr_get_interval +161 spu sched_rr_get_interval sys_sched_rr_get_interval +162 32 nanosleep sys_nanosleep_time32 +162 64 nanosleep sys_nanosleep +162 spu nanosleep sys_nanosleep +163 common mremap sys_mremap +164 common setresuid sys_setresuid +165 common getresuid sys_getresuid +166 common query_module sys_ni_syscall +167 common poll sys_poll +168 common nfsservctl sys_ni_syscall +169 common setresgid sys_setresgid +170 common getresgid sys_getresgid +171 common prctl sys_prctl +172 nospu rt_sigreturn sys_rt_sigreturn compat_sys_rt_sigreturn +173 nospu rt_sigaction sys_rt_sigaction compat_sys_rt_sigaction +174 nospu rt_sigprocmask sys_rt_sigprocmask compat_sys_rt_sigprocmask +175 nospu rt_sigpending sys_rt_sigpending compat_sys_rt_sigpending +176 32 rt_sigtimedwait sys_rt_sigtimedwait_time32 compat_sys_rt_sigtimedwait_time32 +176 64 rt_sigtimedwait sys_rt_sigtimedwait +177 nospu rt_sigqueueinfo sys_rt_sigqueueinfo compat_sys_rt_sigqueueinfo +178 nospu rt_sigsuspend sys_rt_sigsuspend compat_sys_rt_sigsuspend +179 common pread64 sys_pread64 compat_sys_pread64 +180 common pwrite64 sys_pwrite64 compat_sys_pwrite64 +181 common chown sys_chown +182 common getcwd sys_getcwd +183 common capget sys_capget +184 common capset sys_capset +185 nospu sigaltstack sys_sigaltstack compat_sys_sigaltstack +186 32 sendfile sys_sendfile compat_sys_sendfile +186 64 sendfile sys_sendfile64 +186 spu sendfile sys_sendfile64 +187 common getpmsg sys_ni_syscall +188 common putpmsg sys_ni_syscall +189 nospu vfork ppc_vfork +190 common ugetrlimit sys_getrlimit compat_sys_getrlimit +191 common readahead sys_readahead compat_sys_readahead +192 32 mmap2 sys_mmap2 compat_sys_mmap2 +193 32 truncate64 sys_truncate64 compat_sys_truncate64 +194 32 ftruncate64 sys_ftruncate64 compat_sys_ftruncate64 +195 32 stat64 sys_stat64 +196 32 lstat64 sys_lstat64 +197 32 fstat64 sys_fstat64 +198 nospu pciconfig_read sys_pciconfig_read +199 nospu pciconfig_write sys_pciconfig_write +200 nospu pciconfig_iobase sys_pciconfig_iobase +201 common multiplexer sys_ni_syscall +202 common getdents64 sys_getdents64 +203 common pivot_root sys_pivot_root +204 32 fcntl64 sys_fcntl64 compat_sys_fcntl64 +205 common madvise sys_madvise +206 common mincore sys_mincore +207 common gettid sys_gettid +208 common tkill sys_tkill +209 common setxattr sys_setxattr +210 common lsetxattr sys_lsetxattr +211 common fsetxattr sys_fsetxattr +212 common getxattr sys_getxattr +213 common lgetxattr sys_lgetxattr +214 common fgetxattr sys_fgetxattr +215 common listxattr sys_listxattr +216 common llistxattr sys_llistxattr +217 common flistxattr sys_flistxattr +218 common removexattr sys_removexattr +219 common lremovexattr sys_lremovexattr +220 common fremovexattr sys_fremovexattr +221 32 futex sys_futex_time32 +221 64 futex sys_futex +221 spu futex sys_futex +222 common sched_setaffinity sys_sched_setaffinity compat_sys_sched_setaffinity +223 common sched_getaffinity sys_sched_getaffinity compat_sys_sched_getaffinity +# 224 unused +225 common tuxcall sys_ni_syscall +226 32 sendfile64 sys_sendfile64 compat_sys_sendfile64 +227 common io_setup sys_io_setup compat_sys_io_setup +228 common io_destroy sys_io_destroy +229 32 io_getevents sys_io_getevents_time32 +229 64 io_getevents sys_io_getevents +229 spu io_getevents sys_io_getevents +230 common io_submit sys_io_submit compat_sys_io_submit +231 common io_cancel sys_io_cancel +232 nospu set_tid_address sys_set_tid_address +233 common fadvise64 sys_fadvise64 ppc32_fadvise64 +234 nospu exit_group sys_exit_group +235 nospu lookup_dcookie sys_lookup_dcookie compat_sys_lookup_dcookie +236 common epoll_create sys_epoll_create +237 common epoll_ctl sys_epoll_ctl +238 common epoll_wait sys_epoll_wait +239 common remap_file_pages sys_remap_file_pages +240 common timer_create sys_timer_create compat_sys_timer_create +241 32 timer_settime sys_timer_settime32 +241 64 timer_settime sys_timer_settime +241 spu timer_settime sys_timer_settime +242 32 timer_gettime sys_timer_gettime32 +242 64 timer_gettime sys_timer_gettime +242 spu timer_gettime sys_timer_gettime +243 common timer_getoverrun sys_timer_getoverrun +244 common timer_delete sys_timer_delete +245 32 clock_settime sys_clock_settime32 +245 64 clock_settime sys_clock_settime +245 spu clock_settime sys_clock_settime +246 32 clock_gettime sys_clock_gettime32 +246 64 clock_gettime sys_clock_gettime +246 spu clock_gettime sys_clock_gettime +247 32 clock_getres sys_clock_getres_time32 +247 64 clock_getres sys_clock_getres +247 spu clock_getres sys_clock_getres +248 32 clock_nanosleep sys_clock_nanosleep_time32 +248 64 clock_nanosleep sys_clock_nanosleep +248 spu clock_nanosleep sys_clock_nanosleep +249 32 swapcontext ppc_swapcontext ppc32_swapcontext +249 64 swapcontext ppc64_swapcontext +249 spu swapcontext sys_ni_syscall +250 common tgkill sys_tgkill +251 32 utimes sys_utimes_time32 +251 64 utimes sys_utimes +251 spu utimes sys_utimes +252 common statfs64 sys_statfs64 compat_sys_statfs64 +253 common fstatfs64 sys_fstatfs64 compat_sys_fstatfs64 +254 32 fadvise64_64 ppc_fadvise64_64 +254 spu fadvise64_64 sys_ni_syscall +255 common rtas sys_rtas +256 32 sys_debug_setcontext sys_debug_setcontext sys_ni_syscall +256 64 sys_debug_setcontext sys_ni_syscall +256 spu sys_debug_setcontext sys_ni_syscall +# 257 reserved for vserver +258 nospu migrate_pages sys_migrate_pages compat_sys_migrate_pages +259 nospu mbind sys_mbind compat_sys_mbind +260 nospu get_mempolicy sys_get_mempolicy compat_sys_get_mempolicy +261 nospu set_mempolicy sys_set_mempolicy compat_sys_set_mempolicy +262 nospu mq_open sys_mq_open compat_sys_mq_open +263 nospu mq_unlink sys_mq_unlink +264 32 mq_timedsend sys_mq_timedsend_time32 +264 64 mq_timedsend sys_mq_timedsend +265 32 mq_timedreceive sys_mq_timedreceive_time32 +265 64 mq_timedreceive sys_mq_timedreceive +266 nospu mq_notify sys_mq_notify compat_sys_mq_notify +267 nospu mq_getsetattr sys_mq_getsetattr compat_sys_mq_getsetattr +268 nospu kexec_load sys_kexec_load compat_sys_kexec_load +269 nospu add_key sys_add_key +270 nospu request_key sys_request_key +271 nospu keyctl sys_keyctl compat_sys_keyctl +272 nospu waitid sys_waitid compat_sys_waitid +273 nospu ioprio_set sys_ioprio_set +274 nospu ioprio_get sys_ioprio_get +275 nospu inotify_init sys_inotify_init +276 nospu inotify_add_watch sys_inotify_add_watch +277 nospu inotify_rm_watch sys_inotify_rm_watch +278 nospu spu_run sys_spu_run +279 nospu spu_create sys_spu_create +280 32 pselect6 sys_pselect6_time32 compat_sys_pselect6_time32 +280 64 pselect6 sys_pselect6 +281 32 ppoll sys_ppoll_time32 compat_sys_ppoll_time32 +281 64 ppoll sys_ppoll +282 common unshare sys_unshare +283 common splice sys_splice +284 common tee sys_tee +285 common vmsplice sys_vmsplice compat_sys_vmsplice +286 common openat sys_openat compat_sys_openat +287 common mkdirat sys_mkdirat +288 common mknodat sys_mknodat +289 common fchownat sys_fchownat +290 32 futimesat sys_futimesat_time32 +290 64 futimesat sys_futimesat +290 spu utimesat sys_futimesat +291 32 fstatat64 sys_fstatat64 +291 64 newfstatat sys_newfstatat +291 spu newfstatat sys_newfstatat +292 common unlinkat sys_unlinkat +293 common renameat sys_renameat +294 common linkat sys_linkat +295 common symlinkat sys_symlinkat +296 common readlinkat sys_readlinkat +297 common fchmodat sys_fchmodat +298 common faccessat sys_faccessat +299 common get_robust_list sys_get_robust_list compat_sys_get_robust_list +300 common set_robust_list sys_set_robust_list compat_sys_set_robust_list +301 common move_pages sys_move_pages compat_sys_move_pages +302 common getcpu sys_getcpu +303 nospu epoll_pwait sys_epoll_pwait compat_sys_epoll_pwait +304 32 utimensat sys_utimensat_time32 +304 64 utimensat sys_utimensat +304 spu utimensat sys_utimensat +305 common signalfd sys_signalfd compat_sys_signalfd +306 common timerfd_create sys_timerfd_create +307 common eventfd sys_eventfd +308 common sync_file_range2 sys_sync_file_range2 compat_sys_sync_file_range2 +309 nospu fallocate sys_fallocate compat_sys_fallocate +310 nospu subpage_prot sys_subpage_prot +311 32 timerfd_settime sys_timerfd_settime32 +311 64 timerfd_settime sys_timerfd_settime +311 spu timerfd_settime sys_timerfd_settime +312 32 timerfd_gettime sys_timerfd_gettime32 +312 64 timerfd_gettime sys_timerfd_gettime +312 spu timerfd_gettime sys_timerfd_gettime +313 common signalfd4 sys_signalfd4 compat_sys_signalfd4 +314 common eventfd2 sys_eventfd2 +315 common epoll_create1 sys_epoll_create1 +316 common dup3 sys_dup3 +317 common pipe2 sys_pipe2 +318 nospu inotify_init1 sys_inotify_init1 +319 common perf_event_open sys_perf_event_open +320 common preadv sys_preadv compat_sys_preadv +321 common pwritev sys_pwritev compat_sys_pwritev +322 nospu rt_tgsigqueueinfo sys_rt_tgsigqueueinfo compat_sys_rt_tgsigqueueinfo +323 nospu fanotify_init sys_fanotify_init +324 nospu fanotify_mark sys_fanotify_mark compat_sys_fanotify_mark +325 common prlimit64 sys_prlimit64 +326 common socket sys_socket +327 common bind sys_bind +328 common connect sys_connect +329 common listen sys_listen +330 common accept sys_accept +331 common getsockname sys_getsockname +332 common getpeername sys_getpeername +333 common socketpair sys_socketpair +334 common send sys_send +335 common sendto sys_sendto +336 common recv sys_recv compat_sys_recv +337 common recvfrom sys_recvfrom compat_sys_recvfrom +338 common shutdown sys_shutdown +339 common setsockopt sys_setsockopt compat_sys_setsockopt +340 common getsockopt sys_getsockopt compat_sys_getsockopt +341 common sendmsg sys_sendmsg compat_sys_sendmsg +342 common recvmsg sys_recvmsg compat_sys_recvmsg +343 32 recvmmsg sys_recvmmsg_time32 compat_sys_recvmmsg_time32 +343 64 recvmmsg sys_recvmmsg +343 spu recvmmsg sys_recvmmsg +344 common accept4 sys_accept4 +345 common name_to_handle_at sys_name_to_handle_at +346 common open_by_handle_at sys_open_by_handle_at compat_sys_open_by_handle_at +347 32 clock_adjtime sys_clock_adjtime32 +347 64 clock_adjtime sys_clock_adjtime +347 spu clock_adjtime sys_clock_adjtime +348 common syncfs sys_syncfs +349 common sendmmsg sys_sendmmsg compat_sys_sendmmsg +350 common setns sys_setns +351 nospu process_vm_readv sys_process_vm_readv compat_sys_process_vm_readv +352 nospu process_vm_writev sys_process_vm_writev compat_sys_process_vm_writev +353 nospu finit_module sys_finit_module +354 nospu kcmp sys_kcmp +355 common sched_setattr sys_sched_setattr +356 common sched_getattr sys_sched_getattr +357 common renameat2 sys_renameat2 +358 common seccomp sys_seccomp +359 common getrandom sys_getrandom +360 common memfd_create sys_memfd_create +361 common bpf sys_bpf +362 nospu execveat sys_execveat compat_sys_execveat +363 32 switch_endian sys_ni_syscall +363 64 switch_endian ppc_switch_endian +363 spu switch_endian sys_ni_syscall +364 common userfaultfd sys_userfaultfd +365 common membarrier sys_membarrier +# 366-377 originally left for IPC, now unused +378 nospu mlock2 sys_mlock2 +379 nospu copy_file_range sys_copy_file_range +380 common preadv2 sys_preadv2 compat_sys_preadv2 +381 common pwritev2 sys_pwritev2 compat_sys_pwritev2 +382 nospu kexec_file_load sys_kexec_file_load +383 nospu statx sys_statx +384 nospu pkey_alloc sys_pkey_alloc +385 nospu pkey_free sys_pkey_free +386 nospu pkey_mprotect sys_pkey_mprotect +387 nospu rseq sys_rseq +388 32 io_pgetevents sys_io_pgetevents_time32 compat_sys_io_pgetevents +388 64 io_pgetevents sys_io_pgetevents +# room for arch specific syscalls +392 64 semtimedop sys_semtimedop +393 common semget sys_semget +394 common semctl sys_semctl compat_sys_semctl +395 common shmget sys_shmget +396 common shmctl sys_shmctl compat_sys_shmctl +397 common shmat sys_shmat compat_sys_shmat +398 common shmdt sys_shmdt +399 common msgget sys_msgget +400 common msgsnd sys_msgsnd compat_sys_msgsnd +401 common msgrcv sys_msgrcv compat_sys_msgrcv +402 common msgctl sys_msgctl compat_sys_msgctl +403 32 clock_gettime64 sys_clock_gettime sys_clock_gettime +404 32 clock_settime64 sys_clock_settime sys_clock_settime +405 32 clock_adjtime64 sys_clock_adjtime sys_clock_adjtime +406 32 clock_getres_time64 sys_clock_getres sys_clock_getres +407 32 clock_nanosleep_time64 sys_clock_nanosleep sys_clock_nanosleep +408 32 timer_gettime64 sys_timer_gettime sys_timer_gettime +409 32 timer_settime64 sys_timer_settime sys_timer_settime +410 32 timerfd_gettime64 sys_timerfd_gettime sys_timerfd_gettime +411 32 timerfd_settime64 sys_timerfd_settime sys_timerfd_settime +412 32 utimensat_time64 sys_utimensat sys_utimensat +413 32 pselect6_time64 sys_pselect6 compat_sys_pselect6_time64 +414 32 ppoll_time64 sys_ppoll compat_sys_ppoll_time64 +416 32 io_pgetevents_time64 sys_io_pgetevents sys_io_pgetevents +417 32 recvmmsg_time64 sys_recvmmsg compat_sys_recvmmsg_time64 +418 32 mq_timedsend_time64 sys_mq_timedsend sys_mq_timedsend +419 32 mq_timedreceive_time64 sys_mq_timedreceive sys_mq_timedreceive +420 32 semtimedop_time64 sys_semtimedop sys_semtimedop +421 32 rt_sigtimedwait_time64 sys_rt_sigtimedwait compat_sys_rt_sigtimedwait_time64 +422 32 futex_time64 sys_futex sys_futex +423 32 sched_rr_get_interval_time64 sys_sched_rr_get_interval sys_sched_rr_get_interval +424 common pidfd_send_signal sys_pidfd_send_signal +425 common io_uring_setup sys_io_uring_setup +426 common io_uring_enter sys_io_uring_enter +427 common io_uring_register sys_io_uring_register +428 common open_tree sys_open_tree +429 common move_mount sys_move_mount +430 common fsopen sys_fsopen +431 common fsconfig sys_fsconfig +432 common fsmount sys_fsmount +433 common fspick sys_fspick +434 common pidfd_open sys_pidfd_open +435 nospu clone3 ppc_clone3 +437 common openat2 sys_openat2 +438 common pidfd_getfd sys_pidfd_getfd diff --git a/linux-user/ppc/syscall_nr.h b/linux-user/ppc/syscall_nr.h deleted file mode 100644 index b57a07b931..0000000000 --- a/linux-user/ppc/syscall_nr.h +++ /dev/null @@ -1,402 +0,0 @@ -/* - * This file contains the system call numbers. - */ - -#ifndef LINUX_USER_PPC_SYSCALL_NR_H -#define LINUX_USER_PPC_SYSCALL_NR_H - -#define TARGET_NR_restart_syscall 0 -#define TARGET_NR_exit 1 -#define TARGET_NR_fork 2 -#define TARGET_NR_read 3 -#define TARGET_NR_write 4 -#define TARGET_NR_open 5 -#define TARGET_NR_close 6 -#define TARGET_NR_waitpid 7 -#define TARGET_NR_creat 8 -#define TARGET_NR_link 9 -#define TARGET_NR_unlink 10 -#define TARGET_NR_execve 11 -#define TARGET_NR_chdir 12 -#define TARGET_NR_time 13 -#define TARGET_NR_mknod 14 -#define TARGET_NR_chmod 15 -#define TARGET_NR_lchown 16 -#define TARGET_NR_break 17 -#define TARGET_NR_oldstat 18 -#define TARGET_NR_lseek 19 -#define TARGET_NR_getpid 20 -#define TARGET_NR_mount 21 -#define TARGET_NR_umount 22 -#define TARGET_NR_setuid 23 -#define TARGET_NR_getuid 24 -#define TARGET_NR_stime 25 -#define TARGET_NR_ptrace 26 -#define TARGET_NR_alarm 27 -#define TARGET_NR_oldfstat 28 -#define TARGET_NR_pause 29 -#define TARGET_NR_utime 30 -#define TARGET_NR_stty 31 -#define TARGET_NR_gtty 32 -#define TARGET_NR_access 33 -#define TARGET_NR_nice 34 -#define TARGET_NR_ftime 35 -#define TARGET_NR_sync 36 -#define TARGET_NR_kill 37 -#define TARGET_NR_rename 38 -#define TARGET_NR_mkdir 39 -#define TARGET_NR_rmdir 40 -#define TARGET_NR_dup 41 -#define TARGET_NR_pipe 42 -#define TARGET_NR_times 43 -#define TARGET_NR_prof 44 -#define TARGET_NR_brk 45 -#define TARGET_NR_setgid 46 -#define TARGET_NR_getgid 47 -#define TARGET_NR_signal 48 -#define TARGET_NR_geteuid 49 -#define TARGET_NR_getegid 50 -#define TARGET_NR_acct 51 -#define TARGET_NR_umount2 52 -#define TARGET_NR_lock 53 -#define TARGET_NR_ioctl 54 -#define TARGET_NR_fcntl 55 -#define TARGET_NR_mpx 56 -#define TARGET_NR_setpgid 57 -#define TARGET_NR_ulimit 58 -#define TARGET_NR_oldolduname 59 -#define TARGET_NR_umask 60 -#define TARGET_NR_chroot 61 -#define TARGET_NR_ustat 62 -#define TARGET_NR_dup2 63 -#define TARGET_NR_getppid 64 -#define TARGET_NR_getpgrp 65 -#define TARGET_NR_setsid 66 -#define TARGET_NR_sigaction 67 -#define TARGET_NR_sgetmask 68 -#define TARGET_NR_ssetmask 69 -#define TARGET_NR_setreuid 70 -#define TARGET_NR_setregid 71 -#define TARGET_NR_sigsuspend 72 -#define TARGET_NR_sigpending 73 -#define TARGET_NR_sethostname 74 -#define TARGET_NR_setrlimit 75 -#define TARGET_NR_getrlimit 76 -#define TARGET_NR_getrusage 77 -#define TARGET_NR_gettimeofday 78 -#define TARGET_NR_settimeofday 79 -#define TARGET_NR_getgroups 80 -#define TARGET_NR_setgroups 81 -#define TARGET_NR_select 82 -#define TARGET_NR_symlink 83 -#define TARGET_NR_oldlstat 84 -#define TARGET_NR_readlink 85 -#define TARGET_NR_uselib 86 -#define TARGET_NR_swapon 87 -#define TARGET_NR_reboot 88 -#define TARGET_NR_readdir 89 -#define TARGET_NR_mmap 90 -#define TARGET_NR_munmap 91 -#define TARGET_NR_truncate 92 -#define TARGET_NR_ftruncate 93 -#define TARGET_NR_fchmod 94 -#define TARGET_NR_fchown 95 -#define TARGET_NR_getpriority 96 -#define TARGET_NR_setpriority 97 -#define TARGET_NR_profil 98 -#define TARGET_NR_statfs 99 -#define TARGET_NR_fstatfs 100 -#define TARGET_NR_ioperm 101 -#define TARGET_NR_socketcall 102 -#define TARGET_NR_syslog 103 -#define TARGET_NR_setitimer 104 -#define TARGET_NR_getitimer 105 -#define TARGET_NR_stat 106 -#define TARGET_NR_lstat 107 -#define TARGET_NR_fstat 108 -#define TARGET_NR_olduname 109 -#define TARGET_NR_iopl 110 -#define TARGET_NR_vhangup 111 -#define TARGET_NR_idle 112 -#define TARGET_NR_vm86 113 -#define TARGET_NR_wait4 114 -#define TARGET_NR_swapoff 115 -#define TARGET_NR_sysinfo 116 -#define TARGET_NR_ipc 117 -#define TARGET_NR_fsync 118 -#if !defined(TARGET_PPC64) -#define TARGET_NR_sigreturn 119 -#endif -#define TARGET_NR_clone 120 -#define TARGET_NR_setdomainname 121 -#define TARGET_NR_uname 122 -#define TARGET_NR_modify_ldt 123 -#define TARGET_NR_adjtimex 124 -#define TARGET_NR_mprotect 125 -#define TARGET_NR_sigprocmask 126 -#define TARGET_NR_create_module 127 -#define TARGET_NR_init_module 128 -#define TARGET_NR_delete_module 129 -#define TARGET_NR_get_kernel_syms 130 -#define TARGET_NR_quotactl 131 -#define TARGET_NR_getpgid 132 -#define TARGET_NR_fchdir 133 -#define TARGET_NR_bdflush 134 -#define TARGET_NR_sysfs 135 -#define TARGET_NR_personality 136 -#define TARGET_NR_afs_syscall 137 /* Syscall for Andrew File System */ -#define TARGET_NR_setfsuid 138 -#define TARGET_NR_setfsgid 139 -#define TARGET_NR__llseek 140 -#define TARGET_NR_getdents 141 -#define TARGET_NR__newselect 142 -#define TARGET_NR_flock 143 -#define TARGET_NR_msync 144 -#define TARGET_NR_readv 145 -#define TARGET_NR_writev 146 -#define TARGET_NR_getsid 147 -#define TARGET_NR_fdatasync 148 -#define TARGET_NR__sysctl 149 -#define TARGET_NR_mlock 150 -#define TARGET_NR_munlock 151 -#define TARGET_NR_mlockall 152 -#define TARGET_NR_munlockall 153 -#define TARGET_NR_sched_setparam 154 -#define TARGET_NR_sched_getparam 155 -#define TARGET_NR_sched_setscheduler 156 -#define TARGET_NR_sched_getscheduler 157 -#define TARGET_NR_sched_yield 158 -#define TARGET_NR_sched_get_priority_max 159 -#define TARGET_NR_sched_get_priority_min 160 -#define TARGET_NR_sched_rr_get_interval 161 -#define TARGET_NR_nanosleep 162 -#define TARGET_NR_mremap 163 -#define TARGET_NR_setresuid32 164 -#define TARGET_NR_getresuid32 165 -#define TARGET_NR_query_module 166 -#define TARGET_NR_poll 167 -#define TARGET_NR_nfsservctl 168 -#define TARGET_NR_setresgid32 169 -#define TARGET_NR_getresgid32 170 -#define TARGET_NR_prctl 171 -#define TARGET_NR_rt_sigreturn 172 -#define TARGET_NR_rt_sigaction 173 -#define TARGET_NR_rt_sigprocmask 174 -#define TARGET_NR_rt_sigpending 175 -#define TARGET_NR_rt_sigtimedwait 176 -#define TARGET_NR_rt_sigqueueinfo 177 -#define TARGET_NR_rt_sigsuspend 178 -#define TARGET_NR_pread64 179 -#define TARGET_NR_pwrite64 180 -#define TARGET_NR_chown 181 -#define TARGET_NR_getcwd 182 -#define TARGET_NR_capget 183 -#define TARGET_NR_capset 184 -#define TARGET_NR_sigaltstack 185 -#define TARGET_NR_sendfile 186 -#define TARGET_NR_getpmsg 187 /* some people actually want streams */ -#define TARGET_NR_putpmsg 188 /* some people actually want streams */ -#define TARGET_NR_vfork 189 -#define TARGET_NR_ugetrlimit 190 /* SuS compliant getrlimit */ -#define TARGET_NR_readahead 191 -#if !defined(TARGET_PPC64) || defined(TARGET_ABI32) -#define TARGET_NR_mmap2 192 -#define TARGET_NR_truncate64 193 -#define TARGET_NR_ftruncate64 194 -#define TARGET_NR_stat64 195 -#define TARGET_NR_lstat64 196 -#define TARGET_NR_fstat64 197 -#endif -#define TARGET_NR_pciconfig_read 198 -#define TARGET_NR_pciconfig_write 199 -#define TARGET_NR_pciconfig_iobase 200 -#define TARGET_NR_multiplexer 201 -#define TARGET_NR_getdents64 202 -#define TARGET_NR_pivot_root 203 -#if !defined(TARGET_PPC64) || defined(TARGET_ABI32) -#define TARGET_NR_fcntl64 204 -#endif -#define TARGET_NR_madvise 205 -#define TARGET_NR_mincore 206 -#define TARGET_NR_gettid 207 -#define TARGET_NR_tkill 208 -#define TARGET_NR_setxattr 209 -#define TARGET_NR_lsetxattr 210 -#define TARGET_NR_fsetxattr 211 -#define TARGET_NR_getxattr 212 -#define TARGET_NR_lgetxattr 213 -#define TARGET_NR_fgetxattr 214 -#define TARGET_NR_listxattr 215 -#define TARGET_NR_llistxattr 216 -#define TARGET_NR_flistxattr 217 -#define TARGET_NR_removexattr 218 -#define TARGET_NR_lremovexattr 219 -#define TARGET_NR_fremovexattr 220 -#define TARGET_NR_futex 221 -#define TARGET_NR_sched_setaffinity 222 -#define TARGET_NR_sched_getaffinity 223 -/* 224 currently unused */ -#define TARGET_NR_tuxcall 225 -#if !defined(TARGET_PPC64) || defined(TARGET_ABI32) -#define TARGET_NR_sendfile64 226 -#endif -#define TARGET_NR_io_setup 227 -#define TARGET_NR_io_destroy 228 -#define TARGET_NR_io_getevents 229 -#define TARGET_NR_io_submit 230 -#define TARGET_NR_io_cancel 231 -#define TARGET_NR_set_tid_address 232 -#define TARGET_NR_fadvise64 233 -#define TARGET_NR_exit_group 234 -#define TARGET_NR_lookup_dcookie 235 -#define TARGET_NR_epoll_create 236 -#define TARGET_NR_epoll_ctl 237 -#define TARGET_NR_epoll_wait 238 -#define TARGET_NR_remap_file_pages 239 -#define TARGET_NR_timer_create 240 -#define TARGET_NR_timer_settime 241 -#define TARGET_NR_timer_gettime 242 -#define TARGET_NR_timer_getoverrun 243 -#define TARGET_NR_timer_delete 244 -#define TARGET_NR_clock_settime 245 -#define TARGET_NR_clock_gettime 246 -#define TARGET_NR_clock_getres 247 -#define TARGET_NR_clock_nanosleep 248 -#define TARGET_NR_swapcontext 249 -#define TARGET_NR_tgkill 250 -#define TARGET_NR_utimes 251 -#define TARGET_NR_statfs64 252 -#define TARGET_NR_fstatfs64 253 -#if !defined(TARGET_PPC64) || defined(TARGET_ABI32) -#define TARGET_NR_fadvise64_64 254 -#endif -#define TARGET_NR_rtas 255 -#define TARGET_NR_sys_debug_setcontext 256 -/* Number 257 is reserved for vserver */ -#define TARGET_NR_migrate_pages 258 -#define TARGET_NR_mbind 259 -#define TARGET_NR_get_mempolicy 260 -#define TARGET_NR_set_mempolicy 261 -#define TARGET_NR_mq_open 262 -#define TARGET_NR_mq_unlink 263 -#define TARGET_NR_mq_timedsend 264 -#define TARGET_NR_mq_timedreceive 265 -#define TARGET_NR_mq_notify 266 -#define TARGET_NR_mq_getsetattr 267 -#define TARGET_NR_kexec_load 268 -#define TARGET_NR_add_key 269 -#define TARGET_NR_request_key 270 -#define TARGET_NR_keyctl 271 -#define TARGET_NR_waitid 272 -#define TARGET_NR_ioprio_set 273 -#define TARGET_NR_ioprio_get 274 -#define TARGET_NR_inotify_init 275 -#define TARGET_NR_inotify_add_watch 276 -#define TARGET_NR_inotify_rm_watch 277 -#define TARGET_NR_spu_run 278 -#define TARGET_NR_spu_create 279 -#define TARGET_NR_pselect6 280 -#define TARGET_NR_ppoll 281 -#define TARGET_NR_unshare 282 -#define TARGET_NR_splice 283 -#define TARGET_NR_tee 284 -#define TARGET_NR_vmsplice 285 -#define TARGET_NR_openat 286 -#define TARGET_NR_mkdirat 287 -#define TARGET_NR_mknodat 288 -#define TARGET_NR_fchownat 289 -#define TARGET_NR_futimesat 290 -#if defined(TARGET_PPC64) && !defined(TARGET_ABI32) -#define TARGET_NR_newfstatat 291 -#else -#define TARGET_NR_fstatat64 291 -#endif -#define TARGET_NR_unlinkat 292 -#define TARGET_NR_renameat 293 -#define TARGET_NR_linkat 294 -#define TARGET_NR_symlinkat 295 -#define TARGET_NR_readlinkat 296 -#define TARGET_NR_fchmodat 297 -#define TARGET_NR_faccessat 298 -#define TARGET_NR_get_robust_list 299 -#define TARGET_NR_set_robust_list 300 -#define TARGET_NR_move_pages 301 -#define TARGET_NR_getcpu 302 -#define TARGET_NR_epoll_pwait 303 -#define TARGET_NR_utimensat 304 -#define TARGET_NR_signalfd 305 -#define TARGET_NR_timerfd_create 306 -#define TARGET_NR_eventfd 307 -#define TARGET_NR_sync_file_range2 308 -#define TARGET_NR_fallocate 309 -#define TARGET_NR_subpage_prot 310 -#define TARGET_NR_timerfd_settime 311 -#define TARGET_NR_timerfd_gettime 312 -#define TARGET_NR_signalfd4 313 -#define TARGET_NR_eventfd2 314 -#define TARGET_NR_epoll_create1 315 -#define TARGET_NR_dup3 316 -#define TARGET_NR_pipe2 317 -#define TARGET_NR_inotify_init1 318 -#define TARGET_NR_perf_event_open 319 -#define TARGET_NR_preadv 320 -#define TARGET_NR_pwritev 321 -#define TARGET_NR_rt_tgsigqueueinfo 322 -#define TARGET_NR_fanotify_init 323 -#define TARGET_NR_fanotify_mark 324 -#define TARGET_NR_prlimit64 325 -#define TARGET_NR_socket 326 -#define TARGET_NR_bind 327 -#define TARGET_NR_connect 328 -#define TARGET_NR_listen 329 -#define TARGET_NR_accept 330 -#define TARGET_NR_getsockname 331 -#define TARGET_NR_getpeername 332 -#define TARGET_NR_socketpair 333 -#define TARGET_NR_send 334 -#define TARGET_NR_sendto 335 -#define TARGET_NR_recv 336 -#define TARGET_NR_recvfrom 337 -#define TARGET_NR_shutdown 338 -#define TARGET_NR_setsockopt 339 -#define TARGET_NR_getsockopt 340 -#define TARGET_NR_sendmsg 341 -#define TARGET_NR_recvmsg 342 -#define TARGET_NR_recvmmsg 343 -#define TARGET_NR_accept4 344 -#define TARGET_NR_name_to_handle_at 345 -#define TARGET_NR_open_by_handle_at 346 -#define TARGET_NR_clock_adjtime 347 -#define TARGET_NR_syncfs 348 -#define TARGET_NR_sendmmsg 349 -#define TARGET_NR_setns 350 -#define TARGET_NR_process_vm_readv 351 -#define TARGET_NR_process_vm_writev 352 -#define TARGET_NR_finit_module 353 -#define TARGET_NR_kcmp 354 -#define TARGET_NR_sched_setattr 355 -#define TARGET_NR_sched_getattr 356 -#define TARGET_NR_renameat2 357 -#define TARGET_NR_seccomp 358 -#define TARGET_NR_getrandom 359 -#define TARGET_NR_memfd_create 360 -#define TARGET_NR_bpf 361 -#define TARGET_NR_execveat 362 -#define TARGET_NR_switch_endian 363 -#define TARGET_NR_userfaultfd 364 -#define TARGET_NR_membarrier 365 -#define TARGET_NR_semop 366 -#define TARGET_NR_semget 367 -#define TARGET_NR_semctl 368 -#define TARGET_NR_semtimedop 369 -#define TARGET_NR_msgsnd 370 -#define TARGET_NR_msgrcv 371 -#define TARGET_NR_msgget 372 -#define TARGET_NR_msgctl 373 -#define TARGET_NR_shmat 374 -#define TARGET_NR_shmdt 375 -#define TARGET_NR_shmget 376 -#define TARGET_NR_shmctl 377 -#define TARGET_NR_mlock2 378 - -#endif diff --git a/linux-user/ppc/syscallhdr.sh b/linux-user/ppc/syscallhdr.sh new file mode 100644 index 0000000000..6c44e0eaad --- /dev/null +++ b/linux-user/ppc/syscallhdr.sh @@ -0,0 +1,34 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 + +in="$1" +out="$2" +my_abis=`echo "($3)" | tr ',' '|'` +prefix="$4" +offset="$5" + +fileguard=LINUX_USER_PPC_`basename "$out" | sed \ + -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/' \ + -e 's/[^A-Z0-9_]/_/g' -e 's/__/_/g'` +grep -E "^[0-9A-Fa-fXx]+[[:space:]]+${my_abis}" "$in" | sort -n | ( + printf "#ifndef %s\n" "${fileguard}" + printf "#define %s\n" "${fileguard}" + printf "\n" + + while read nr abi name entry compat ; do + if [ "$entry" = "sys_ni_syscall" ] ; then + continue + fi + if [ -z "$offset" ]; then + printf "#define TARGET_NR_%s%s\t%s\n" \ + "${prefix}" "${name}" "${nr}" + else + printf "#define TARGET_NR_%s%s\t(%s + %s)\n" \ + "${prefix}" "${name}" "${offset}" "${nr}" + fi + done + + printf "\n" + printf "#endif /* %s */" "${fileguard}" + printf "\n" +) > "$out" diff --git a/linux-user/qemu.h b/linux-user/qemu.h index f6f5fe5fbb..792c74290f 100644 --- a/linux-user/qemu.h +++ b/linux-user/qemu.h @@ -35,6 +35,7 @@ struct image_info { abi_ulong end_data; abi_ulong start_brk; abi_ulong brk; + abi_ulong reserve_brk; abi_ulong start_mmap; abi_ulong start_stack; abi_ulong stack_limit; @@ -210,7 +211,6 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, abi_long arg2, abi_long arg3, abi_long arg4, abi_long arg5, abi_long arg6, abi_long arg7, abi_long arg8); -void gemu_log(const char *fmt, ...) GCC_FMT_ATTR(1, 2); extern __thread CPUState *thread_cpu; void cpu_loop(CPUArchState *env); const char *target_strerror(int err); @@ -385,7 +385,6 @@ void print_syscall_ret(int num, abi_long arg1); * --- SIGSEGV {si_signo=SIGSEGV, si_code=SI_KERNEL, si_addr=0} --- */ void print_taken_signal(int target_signum, const target_siginfo_t *tinfo); -extern int do_strace; /* signal.c */ void process_pending_signals(CPUArchState *cpu_env); diff --git a/linux-user/riscv/syscall32_nr.h b/linux-user/riscv/syscall32_nr.h new file mode 100644 index 0000000000..4fef73e954 --- /dev/null +++ b/linux-user/riscv/syscall32_nr.h @@ -0,0 +1,295 @@ +/* + * This file contains the system call numbers. + */ +#ifndef LINUX_USER_RISCV_SYSCALL32_NR_H +#define LINUX_USER_RISCV_SYSCALL32_NR_H + +#define TARGET_NR_io_setup 0 +#define TARGET_NR_io_destroy 1 +#define TARGET_NR_io_submit 2 +#define TARGET_NR_io_cancel 3 +#define TARGET_NR_setxattr 5 +#define TARGET_NR_lsetxattr 6 +#define TARGET_NR_fsetxattr 7 +#define TARGET_NR_getxattr 8 +#define TARGET_NR_lgetxattr 9 +#define TARGET_NR_fgetxattr 10 +#define TARGET_NR_listxattr 11 +#define TARGET_NR_llistxattr 12 +#define TARGET_NR_flistxattr 13 +#define TARGET_NR_removexattr 14 +#define TARGET_NR_lremovexattr 15 +#define TARGET_NR_fremovexattr 16 +#define TARGET_NR_getcwd 17 +#define TARGET_NR_lookup_dcookie 18 +#define TARGET_NR_eventfd2 19 +#define TARGET_NR_epoll_create1 20 +#define TARGET_NR_epoll_ctl 21 +#define TARGET_NR_epoll_pwait 22 +#define TARGET_NR_dup 23 +#define TARGET_NR_dup3 24 +#define TARGET_NR_fcntl64 25 +#define TARGET_NR_inotify_init1 26 +#define TARGET_NR_inotify_add_watch 27 +#define TARGET_NR_inotify_rm_watch 28 +#define TARGET_NR_ioctl 29 +#define TARGET_NR_ioprio_set 30 +#define TARGET_NR_ioprio_get 31 +#define TARGET_NR_flock 32 +#define TARGET_NR_mknodat 33 +#define TARGET_NR_mkdirat 34 +#define TARGET_NR_unlinkat 35 +#define TARGET_NR_symlinkat 36 +#define TARGET_NR_linkat 37 +#define TARGET_NR_umount2 39 +#define TARGET_NR_mount 40 +#define TARGET_NR_pivot_root 41 +#define TARGET_NR_nfsservctl 42 +#define TARGET_NR_statfs64 43 +#define TARGET_NR_fstatfs64 44 +#define TARGET_NR_truncate64 45 +#define TARGET_NR_ftruncate64 46 +#define TARGET_NR_fallocate 47 +#define TARGET_NR_faccessat 48 +#define TARGET_NR_chdir 49 +#define TARGET_NR_fchdir 50 +#define TARGET_NR_chroot 51 +#define TARGET_NR_fchmod 52 +#define TARGET_NR_fchmodat 53 +#define TARGET_NR_fchownat 54 +#define TARGET_NR_fchown 55 +#define TARGET_NR_openat 56 +#define TARGET_NR_close 57 +#define TARGET_NR_vhangup 58 +#define TARGET_NR_pipe2 59 +#define TARGET_NR_quotactl 60 +#define TARGET_NR_getdents64 61 +#define TARGET_NR_llseek 62 +#define TARGET_NR_read 63 +#define TARGET_NR_write 64 +#define TARGET_NR_readv 65 +#define TARGET_NR_writev 66 +#define TARGET_NR_pread64 67 +#define TARGET_NR_pwrite64 68 +#define TARGET_NR_preadv 69 +#define TARGET_NR_pwritev 70 +#define TARGET_NR_sendfile64 71 +#define TARGET_NR_signalfd4 74 +#define TARGET_NR_vmsplice 75 +#define TARGET_NR_splice 76 +#define TARGET_NR_tee 77 +#define TARGET_NR_readlinkat 78 +#define TARGET_NR_fstatat64 79 +#define TARGET_NR_fstat64 80 +#define TARGET_NR_sync 81 +#define TARGET_NR_fsync 82 +#define TARGET_NR_fdatasync 83 +#define TARGET_NR_sync_file_range 84 +#define TARGET_NR_timerfd_create 85 +#define TARGET_NR_acct 89 +#define TARGET_NR_capget 90 +#define TARGET_NR_capset 91 +#define TARGET_NR_personality 92 +#define TARGET_NR_exit 93 +#define TARGET_NR_exit_group 94 +#define TARGET_NR_waitid 95 +#define TARGET_NR_set_tid_address 96 +#define TARGET_NR_unshare 97 +#define TARGET_NR_set_robust_list 99 +#define TARGET_NR_get_robust_list 100 +#define TARGET_NR_getitimer 102 +#define TARGET_NR_setitimer 103 +#define TARGET_NR_kexec_load 104 +#define TARGET_NR_init_module 105 +#define TARGET_NR_delete_module 106 +#define TARGET_NR_timer_create 107 +#define TARGET_NR_timer_getoverrun 109 +#define TARGET_NR_timer_delete 111 +#define TARGET_NR_syslog 116 +#define TARGET_NR_ptrace 117 +#define TARGET_NR_sched_setparam 118 +#define TARGET_NR_sched_setscheduler 119 +#define TARGET_NR_sched_getscheduler 120 +#define TARGET_NR_sched_getparam 121 +#define TARGET_NR_sched_setaffinity 122 +#define TARGET_NR_sched_getaffinity 123 +#define TARGET_NR_sched_yield 124 +#define TARGET_NR_sched_get_priority_max 125 +#define TARGET_NR_sched_get_priority_min 126 +#define TARGET_NR_restart_syscall 128 +#define TARGET_NR_kill 129 +#define TARGET_NR_tkill 130 +#define TARGET_NR_tgkill 131 +#define TARGET_NR_sigaltstack 132 +#define TARGET_NR_rt_sigsuspend 133 +#define TARGET_NR_rt_sigaction 134 +#define TARGET_NR_rt_sigprocmask 135 +#define TARGET_NR_rt_sigpending 136 +#define TARGET_NR_rt_sigqueueinfo 138 +#define TARGET_NR_rt_sigreturn 139 +#define TARGET_NR_setpriority 140 +#define TARGET_NR_getpriority 141 +#define TARGET_NR_reboot 142 +#define TARGET_NR_setregid 143 +#define TARGET_NR_setgid 144 +#define TARGET_NR_setreuid 145 +#define TARGET_NR_setuid 146 +#define TARGET_NR_setresuid 147 +#define TARGET_NR_getresuid 148 +#define TARGET_NR_setresgid 149 +#define TARGET_NR_getresgid 150 +#define TARGET_NR_setfsuid 151 +#define TARGET_NR_setfsgid 152 +#define TARGET_NR_times 153 +#define TARGET_NR_setpgid 154 +#define TARGET_NR_getpgid 155 +#define TARGET_NR_getsid 156 +#define TARGET_NR_setsid 157 +#define TARGET_NR_getgroups 158 +#define TARGET_NR_setgroups 159 +#define TARGET_NR_uname 160 +#define TARGET_NR_sethostname 161 +#define TARGET_NR_setdomainname 162 +#define TARGET_NR_getrlimit 163 +#define TARGET_NR_setrlimit 164 +#define TARGET_NR_getrusage 165 +#define TARGET_NR_umask 166 +#define TARGET_NR_prctl 167 +#define TARGET_NR_getcpu 168 +#define TARGET_NR_getpid 172 +#define TARGET_NR_getppid 173 +#define TARGET_NR_getuid 174 +#define TARGET_NR_geteuid 175 +#define TARGET_NR_getgid 176 +#define TARGET_NR_getegid 177 +#define TARGET_NR_gettid 178 +#define TARGET_NR_sysinfo 179 +#define TARGET_NR_mq_open 180 +#define TARGET_NR_mq_unlink 181 +#define TARGET_NR_mq_notify 184 +#define TARGET_NR_mq_getsetattr 185 +#define TARGET_NR_msgget 186 +#define TARGET_NR_msgctl 187 +#define TARGET_NR_msgrcv 188 +#define TARGET_NR_msgsnd 189 +#define TARGET_NR_semget 190 +#define TARGET_NR_semctl 191 +#define TARGET_NR_semop 193 +#define TARGET_NR_shmget 194 +#define TARGET_NR_shmctl 195 +#define TARGET_NR_shmat 196 +#define TARGET_NR_shmdt 197 +#define TARGET_NR_socket 198 +#define TARGET_NR_socketpair 199 +#define TARGET_NR_bind 200 +#define TARGET_NR_listen 201 +#define TARGET_NR_accept 202 +#define TARGET_NR_connect 203 +#define TARGET_NR_getsockname 204 +#define TARGET_NR_getpeername 205 +#define TARGET_NR_sendto 206 +#define TARGET_NR_recvfrom 207 +#define TARGET_NR_setsockopt 208 +#define TARGET_NR_getsockopt 209 +#define TARGET_NR_shutdown 210 +#define TARGET_NR_sendmsg 211 +#define TARGET_NR_recvmsg 212 +#define TARGET_NR_readahead 213 +#define TARGET_NR_brk 214 +#define TARGET_NR_munmap 215 +#define TARGET_NR_mremap 216 +#define TARGET_NR_add_key 217 +#define TARGET_NR_request_key 218 +#define TARGET_NR_keyctl 219 +#define TARGET_NR_clone 220 +#define TARGET_NR_execve 221 +#define TARGET_NR_mmap2 222 +#define TARGET_NR_fadvise64_64 223 +#define TARGET_NR_swapon 224 +#define TARGET_NR_swapoff 225 +#define TARGET_NR_mprotect 226 +#define TARGET_NR_msync 227 +#define TARGET_NR_mlock 228 +#define TARGET_NR_munlock 229 +#define TARGET_NR_mlockall 230 +#define TARGET_NR_munlockall 231 +#define TARGET_NR_mincore 232 +#define TARGET_NR_madvise 233 +#define TARGET_NR_remap_file_pages 234 +#define TARGET_NR_mbind 235 +#define TARGET_NR_get_mempolicy 236 +#define TARGET_NR_set_mempolicy 237 +#define TARGET_NR_migrate_pages 238 +#define TARGET_NR_move_pages 239 +#define TARGET_NR_rt_tgsigqueueinfo 240 +#define TARGET_NR_perf_event_open 241 +#define TARGET_NR_accept4 242 +#define TARGET_NR_arch_specific_syscall 244 +#define TARGET_NR_riscv_flush_icache (TARGET_NR_arch_specific_syscall + 15) +#define TARGET_NR_prlimit64 261 +#define TARGET_NR_fanotify_init 262 +#define TARGET_NR_fanotify_mark 263 +#define TARGET_NR_name_to_handle_at 264 +#define TARGET_NR_open_by_handle_at 265 +#define TARGET_NR_syncfs 267 +#define TARGET_NR_setns 268 +#define TARGET_NR_sendmmsg 269 +#define TARGET_NR_process_vm_readv 270 +#define TARGET_NR_process_vm_writev 271 +#define TARGET_NR_kcmp 272 +#define TARGET_NR_finit_module 273 +#define TARGET_NR_sched_setattr 274 +#define TARGET_NR_sched_getattr 275 +#define TARGET_NR_renameat2 276 +#define TARGET_NR_seccomp 277 +#define TARGET_NR_getrandom 278 +#define TARGET_NR_memfd_create 279 +#define TARGET_NR_bpf 280 +#define TARGET_NR_execveat 281 +#define TARGET_NR_userfaultfd 282 +#define TARGET_NR_membarrier 283 +#define TARGET_NR_mlock2 284 +#define TARGET_NR_copy_file_range 285 +#define TARGET_NR_preadv2 286 +#define TARGET_NR_pwritev2 287 +#define TARGET_NR_pkey_mprotect 288 +#define TARGET_NR_pkey_alloc 289 +#define TARGET_NR_pkey_free 290 +#define TARGET_NR_statx 291 +#define TARGET_NR_rseq 293 +#define TARGET_NR_kexec_file_load 294 +#define TARGET_NR_clock_gettime64 403 +#define TARGET_NR_clock_settime64 404 +#define TARGET_NR_clock_adjtime64 405 +#define TARGET_NR_clock_getres_time64 406 +#define TARGET_NR_clock_nanosleep_time64 407 +#define TARGET_NR_timer_gettime64 408 +#define TARGET_NR_timer_settime64 409 +#define TARGET_NR_timerfd_gettime64 410 +#define TARGET_NR_timerfd_settime64 411 +#define TARGET_NR_utimensat_time64 412 +#define TARGET_NR_pselect6_time64 413 +#define TARGET_NR_ppoll_time64 414 +#define TARGET_NR_io_pgetevents_time64 416 +#define TARGET_NR_recvmmsg_time64 417 +#define TARGET_NR_mq_timedsend_time64 418 +#define TARGET_NR_mq_timedreceive_time64 419 +#define TARGET_NR_semtimedop_time64 420 +#define TARGET_NR_rt_sigtimedwait_time64 421 +#define TARGET_NR_futex_time64 422 +#define TARGET_NR_sched_rr_get_interval_time64 423 +#define TARGET_NR_pidfd_send_signal 424 +#define TARGET_NR_io_uring_setup 425 +#define TARGET_NR_io_uring_enter 426 +#define TARGET_NR_io_uring_register 427 +#define TARGET_NR_open_tree 428 +#define TARGET_NR_move_mount 429 +#define TARGET_NR_fsopen 430 +#define TARGET_NR_fsconfig 431 +#define TARGET_NR_fsmount 432 +#define TARGET_NR_fspick 433 +#define TARGET_NR_pidfd_open 434 +#define TARGET_NR_clone3 435 +#define TARGET_NR_syscalls 436 + +#endif /* LINUX_USER_RISCV_SYSCALL32_NR_H */ diff --git a/linux-user/riscv/syscall64_nr.h b/linux-user/riscv/syscall64_nr.h new file mode 100644 index 0000000000..cc82f3244f --- /dev/null +++ b/linux-user/riscv/syscall64_nr.h @@ -0,0 +1,301 @@ +/* + * This file contains the system call numbers. + */ +#ifndef LINUX_USER_RISCV_SYSCALL64_NR_H +#define LINUX_USER_RISCV_SYSCALL64_NR_H + +#define TARGET_NR_io_setup 0 +#define TARGET_NR_io_destroy 1 +#define TARGET_NR_io_submit 2 +#define TARGET_NR_io_cancel 3 +#define TARGET_NR_io_getevents 4 +#define TARGET_NR_setxattr 5 +#define TARGET_NR_lsetxattr 6 +#define TARGET_NR_fsetxattr 7 +#define TARGET_NR_getxattr 8 +#define TARGET_NR_lgetxattr 9 +#define TARGET_NR_fgetxattr 10 +#define TARGET_NR_listxattr 11 +#define TARGET_NR_llistxattr 12 +#define TARGET_NR_flistxattr 13 +#define TARGET_NR_removexattr 14 +#define TARGET_NR_lremovexattr 15 +#define TARGET_NR_fremovexattr 16 +#define TARGET_NR_getcwd 17 +#define TARGET_NR_lookup_dcookie 18 +#define TARGET_NR_eventfd2 19 +#define TARGET_NR_epoll_create1 20 +#define TARGET_NR_epoll_ctl 21 +#define TARGET_NR_epoll_pwait 22 +#define TARGET_NR_dup 23 +#define TARGET_NR_dup3 24 +#define TARGET_NR_fcntl 25 +#define TARGET_NR_inotify_init1 26 +#define TARGET_NR_inotify_add_watch 27 +#define TARGET_NR_inotify_rm_watch 28 +#define TARGET_NR_ioctl 29 +#define TARGET_NR_ioprio_set 30 +#define TARGET_NR_ioprio_get 31 +#define TARGET_NR_flock 32 +#define TARGET_NR_mknodat 33 +#define TARGET_NR_mkdirat 34 +#define TARGET_NR_unlinkat 35 +#define TARGET_NR_symlinkat 36 +#define TARGET_NR_linkat 37 +#define TARGET_NR_umount2 39 +#define TARGET_NR_mount 40 +#define TARGET_NR_pivot_root 41 +#define TARGET_NR_nfsservctl 42 +#define TARGET_NR_statfs 43 +#define TARGET_NR_fstatfs 44 +#define TARGET_NR_truncate 45 +#define TARGET_NR_ftruncate 46 +#define TARGET_NR_fallocate 47 +#define TARGET_NR_faccessat 48 +#define TARGET_NR_chdir 49 +#define TARGET_NR_fchdir 50 +#define TARGET_NR_chroot 51 +#define TARGET_NR_fchmod 52 +#define TARGET_NR_fchmodat 53 +#define TARGET_NR_fchownat 54 +#define TARGET_NR_fchown 55 +#define TARGET_NR_openat 56 +#define TARGET_NR_close 57 +#define TARGET_NR_vhangup 58 +#define TARGET_NR_pipe2 59 +#define TARGET_NR_quotactl 60 +#define TARGET_NR_getdents64 61 +#define TARGET_NR_lseek 62 +#define TARGET_NR_read 63 +#define TARGET_NR_write 64 +#define TARGET_NR_readv 65 +#define TARGET_NR_writev 66 +#define TARGET_NR_pread64 67 +#define TARGET_NR_pwrite64 68 +#define TARGET_NR_preadv 69 +#define TARGET_NR_pwritev 70 +#define TARGET_NR_sendfile 71 +#define TARGET_NR_pselect6 72 +#define TARGET_NR_ppoll 73 +#define TARGET_NR_signalfd4 74 +#define TARGET_NR_vmsplice 75 +#define TARGET_NR_splice 76 +#define TARGET_NR_tee 77 +#define TARGET_NR_readlinkat 78 +#define TARGET_NR_newfstatat 79 +#define TARGET_NR_fstat 80 +#define TARGET_NR_sync 81 +#define TARGET_NR_fsync 82 +#define TARGET_NR_fdatasync 83 +#define TARGET_NR_sync_file_range 84 +#define TARGET_NR_timerfd_create 85 +#define TARGET_NR_timerfd_settime 86 +#define TARGET_NR_timerfd_gettime 87 +#define TARGET_NR_utimensat 88 +#define TARGET_NR_acct 89 +#define TARGET_NR_capget 90 +#define TARGET_NR_capset 91 +#define TARGET_NR_personality 92 +#define TARGET_NR_exit 93 +#define TARGET_NR_exit_group 94 +#define TARGET_NR_waitid 95 +#define TARGET_NR_set_tid_address 96 +#define TARGET_NR_unshare 97 +#define TARGET_NR_futex 98 +#define TARGET_NR_set_robust_list 99 +#define TARGET_NR_get_robust_list 100 +#define TARGET_NR_nanosleep 101 +#define TARGET_NR_getitimer 102 +#define TARGET_NR_setitimer 103 +#define TARGET_NR_kexec_load 104 +#define TARGET_NR_init_module 105 +#define TARGET_NR_delete_module 106 +#define TARGET_NR_timer_create 107 +#define TARGET_NR_timer_gettime 108 +#define TARGET_NR_timer_getoverrun 109 +#define TARGET_NR_timer_settime 110 +#define TARGET_NR_timer_delete 111 +#define TARGET_NR_clock_settime 112 +#define TARGET_NR_clock_gettime 113 +#define TARGET_NR_clock_getres 114 +#define TARGET_NR_clock_nanosleep 115 +#define TARGET_NR_syslog 116 +#define TARGET_NR_ptrace 117 +#define TARGET_NR_sched_setparam 118 +#define TARGET_NR_sched_setscheduler 119 +#define TARGET_NR_sched_getscheduler 120 +#define TARGET_NR_sched_getparam 121 +#define TARGET_NR_sched_setaffinity 122 +#define TARGET_NR_sched_getaffinity 123 +#define TARGET_NR_sched_yield 124 +#define TARGET_NR_sched_get_priority_max 125 +#define TARGET_NR_sched_get_priority_min 126 +#define TARGET_NR_sched_rr_get_interval 127 +#define TARGET_NR_restart_syscall 128 +#define TARGET_NR_kill 129 +#define TARGET_NR_tkill 130 +#define TARGET_NR_tgkill 131 +#define TARGET_NR_sigaltstack 132 +#define TARGET_NR_rt_sigsuspend 133 +#define TARGET_NR_rt_sigaction 134 +#define TARGET_NR_rt_sigprocmask 135 +#define TARGET_NR_rt_sigpending 136 +#define TARGET_NR_rt_sigtimedwait 137 +#define TARGET_NR_rt_sigqueueinfo 138 +#define TARGET_NR_rt_sigreturn 139 +#define TARGET_NR_setpriority 140 +#define TARGET_NR_getpriority 141 +#define TARGET_NR_reboot 142 +#define TARGET_NR_setregid 143 +#define TARGET_NR_setgid 144 +#define TARGET_NR_setreuid 145 +#define TARGET_NR_setuid 146 +#define TARGET_NR_setresuid 147 +#define TARGET_NR_getresuid 148 +#define TARGET_NR_setresgid 149 +#define TARGET_NR_getresgid 150 +#define TARGET_NR_setfsuid 151 +#define TARGET_NR_setfsgid 152 +#define TARGET_NR_times 153 +#define TARGET_NR_setpgid 154 +#define TARGET_NR_getpgid 155 +#define TARGET_NR_getsid 156 +#define TARGET_NR_setsid 157 +#define TARGET_NR_getgroups 158 +#define TARGET_NR_setgroups 159 +#define TARGET_NR_uname 160 +#define TARGET_NR_sethostname 161 +#define TARGET_NR_setdomainname 162 +#define TARGET_NR_getrlimit 163 +#define TARGET_NR_setrlimit 164 +#define TARGET_NR_getrusage 165 +#define TARGET_NR_umask 166 +#define TARGET_NR_prctl 167 +#define TARGET_NR_getcpu 168 +#define TARGET_NR_gettimeofday 169 +#define TARGET_NR_settimeofday 170 +#define TARGET_NR_adjtimex 171 +#define TARGET_NR_getpid 172 +#define TARGET_NR_getppid 173 +#define TARGET_NR_getuid 174 +#define TARGET_NR_geteuid 175 +#define TARGET_NR_getgid 176 +#define TARGET_NR_getegid 177 +#define TARGET_NR_gettid 178 +#define TARGET_NR_sysinfo 179 +#define TARGET_NR_mq_open 180 +#define TARGET_NR_mq_unlink 181 +#define TARGET_NR_mq_timedsend 182 +#define TARGET_NR_mq_timedreceive 183 +#define TARGET_NR_mq_notify 184 +#define TARGET_NR_mq_getsetattr 185 +#define TARGET_NR_msgget 186 +#define TARGET_NR_msgctl 187 +#define TARGET_NR_msgrcv 188 +#define TARGET_NR_msgsnd 189 +#define TARGET_NR_semget 190 +#define TARGET_NR_semctl 191 +#define TARGET_NR_semtimedop 192 +#define TARGET_NR_semop 193 +#define TARGET_NR_shmget 194 +#define TARGET_NR_shmctl 195 +#define TARGET_NR_shmat 196 +#define TARGET_NR_shmdt 197 +#define TARGET_NR_socket 198 +#define TARGET_NR_socketpair 199 +#define TARGET_NR_bind 200 +#define TARGET_NR_listen 201 +#define TARGET_NR_accept 202 +#define TARGET_NR_connect 203 +#define TARGET_NR_getsockname 204 +#define TARGET_NR_getpeername 205 +#define TARGET_NR_sendto 206 +#define TARGET_NR_recvfrom 207 +#define TARGET_NR_setsockopt 208 +#define TARGET_NR_getsockopt 209 +#define TARGET_NR_shutdown 210 +#define TARGET_NR_sendmsg 211 +#define TARGET_NR_recvmsg 212 +#define TARGET_NR_readahead 213 +#define TARGET_NR_brk 214 +#define TARGET_NR_munmap 215 +#define TARGET_NR_mremap 216 +#define TARGET_NR_add_key 217 +#define TARGET_NR_request_key 218 +#define TARGET_NR_keyctl 219 +#define TARGET_NR_clone 220 +#define TARGET_NR_execve 221 +#define TARGET_NR_mmap 222 +#define TARGET_NR_fadvise64 223 +#define TARGET_NR_swapon 224 +#define TARGET_NR_swapoff 225 +#define TARGET_NR_mprotect 226 +#define TARGET_NR_msync 227 +#define TARGET_NR_mlock 228 +#define TARGET_NR_munlock 229 +#define TARGET_NR_mlockall 230 +#define TARGET_NR_munlockall 231 +#define TARGET_NR_mincore 232 +#define TARGET_NR_madvise 233 +#define TARGET_NR_remap_file_pages 234 +#define TARGET_NR_mbind 235 +#define TARGET_NR_get_mempolicy 236 +#define TARGET_NR_set_mempolicy 237 +#define TARGET_NR_migrate_pages 238 +#define TARGET_NR_move_pages 239 +#define TARGET_NR_rt_tgsigqueueinfo 240 +#define TARGET_NR_perf_event_open 241 +#define TARGET_NR_accept4 242 +#define TARGET_NR_recvmmsg 243 +#define TARGET_NR_arch_specific_syscall 244 +#define TARGET_NR_riscv_flush_icache (TARGET_NR_arch_specific_syscall + 15) +#define TARGET_NR_wait4 260 +#define TARGET_NR_prlimit64 261 +#define TARGET_NR_fanotify_init 262 +#define TARGET_NR_fanotify_mark 263 +#define TARGET_NR_name_to_handle_at 264 +#define TARGET_NR_open_by_handle_at 265 +#define TARGET_NR_clock_adjtime 266 +#define TARGET_NR_syncfs 267 +#define TARGET_NR_setns 268 +#define TARGET_NR_sendmmsg 269 +#define TARGET_NR_process_vm_readv 270 +#define TARGET_NR_process_vm_writev 271 +#define TARGET_NR_kcmp 272 +#define TARGET_NR_finit_module 273 +#define TARGET_NR_sched_setattr 274 +#define TARGET_NR_sched_getattr 275 +#define TARGET_NR_renameat2 276 +#define TARGET_NR_seccomp 277 +#define TARGET_NR_getrandom 278 +#define TARGET_NR_memfd_create 279 +#define TARGET_NR_bpf 280 +#define TARGET_NR_execveat 281 +#define TARGET_NR_userfaultfd 282 +#define TARGET_NR_membarrier 283 +#define TARGET_NR_mlock2 284 +#define TARGET_NR_copy_file_range 285 +#define TARGET_NR_preadv2 286 +#define TARGET_NR_pwritev2 287 +#define TARGET_NR_pkey_mprotect 288 +#define TARGET_NR_pkey_alloc 289 +#define TARGET_NR_pkey_free 290 +#define TARGET_NR_statx 291 +#define TARGET_NR_io_pgetevents 292 +#define TARGET_NR_rseq 293 +#define TARGET_NR_kexec_file_load 294 +#define TARGET_NR_pidfd_send_signal 424 +#define TARGET_NR_io_uring_setup 425 +#define TARGET_NR_io_uring_enter 426 +#define TARGET_NR_io_uring_register 427 +#define TARGET_NR_open_tree 428 +#define TARGET_NR_move_mount 429 +#define TARGET_NR_fsopen 430 +#define TARGET_NR_fsconfig 431 +#define TARGET_NR_fsmount 432 +#define TARGET_NR_fspick 433 +#define TARGET_NR_pidfd_open 434 +#define TARGET_NR_clone3 435 +#define TARGET_NR_syscalls 436 + +#endif /* LINUX_USER_RISCV_SYSCALL64_NR_H */ diff --git a/linux-user/riscv/syscall_nr.h b/linux-user/riscv/syscall_nr.h index 5c87282209..0a5a2f2fb1 100644 --- a/linux-user/riscv/syscall_nr.h +++ b/linux-user/riscv/syscall_nr.h @@ -6,300 +6,10 @@ #ifndef LINUX_USER_RISCV_SYSCALL_NR_H #define LINUX_USER_RISCV_SYSCALL_NR_H -#define TARGET_NR_io_setup 0 -#define TARGET_NR_io_destroy 1 -#define TARGET_NR_io_submit 2 -#define TARGET_NR_io_cancel 3 -#define TARGET_NR_io_getevents 4 -#define TARGET_NR_setxattr 5 -#define TARGET_NR_lsetxattr 6 -#define TARGET_NR_fsetxattr 7 -#define TARGET_NR_getxattr 8 -#define TARGET_NR_lgetxattr 9 -#define TARGET_NR_fgetxattr 10 -#define TARGET_NR_listxattr 11 -#define TARGET_NR_llistxattr 12 -#define TARGET_NR_flistxattr 13 -#define TARGET_NR_removexattr 14 -#define TARGET_NR_lremovexattr 15 -#define TARGET_NR_fremovexattr 16 -#define TARGET_NR_getcwd 17 -#define TARGET_NR_lookup_dcookie 18 -#define TARGET_NR_eventfd2 19 -#define TARGET_NR_epoll_create1 20 -#define TARGET_NR_epoll_ctl 21 -#define TARGET_NR_epoll_pwait 22 -#define TARGET_NR_dup 23 -#define TARGET_NR_dup3 24 #ifdef TARGET_RISCV32 -#define TARGET_NR_fcntl64 25 +# include "syscall32_nr.h" #else -#define TARGET_NR_fcntl 25 +# include "syscall64_nr.h" #endif -#define TARGET_NR_inotify_init1 26 -#define TARGET_NR_inotify_add_watch 27 -#define TARGET_NR_inotify_rm_watch 28 -#define TARGET_NR_ioctl 29 -#define TARGET_NR_ioprio_set 30 -#define TARGET_NR_ioprio_get 31 -#define TARGET_NR_flock 32 -#define TARGET_NR_mknodat 33 -#define TARGET_NR_mkdirat 34 -#define TARGET_NR_unlinkat 35 -#define TARGET_NR_symlinkat 36 -#define TARGET_NR_linkat 37 -#define TARGET_NR_renameat 38 -#define TARGET_NR_umount2 39 -#define TARGET_NR_mount 40 -#define TARGET_NR_pivot_root 41 -#define TARGET_NR_nfsservctl 42 -#define TARGET_NR_statfs 43 -#define TARGET_NR_fstatfs 44 -#define TARGET_NR_truncate 45 -#define TARGET_NR_ftruncate 46 -#define TARGET_NR_fallocate 47 -#define TARGET_NR_faccessat 48 -#define TARGET_NR_chdir 49 -#define TARGET_NR_fchdir 50 -#define TARGET_NR_chroot 51 -#define TARGET_NR_fchmod 52 -#define TARGET_NR_fchmodat 53 -#define TARGET_NR_fchownat 54 -#define TARGET_NR_fchown 55 -#define TARGET_NR_openat 56 -#define TARGET_NR_close 57 -#define TARGET_NR_vhangup 58 -#define TARGET_NR_pipe2 59 -#define TARGET_NR_quotactl 60 -#define TARGET_NR_getdents64 61 -#ifdef TARGET_RISCV32 -#define TARGET_NR__llseek 62 -#else -#define TARGET_NR_lseek 62 -#endif -#define TARGET_NR_read 63 -#define TARGET_NR_write 64 -#define TARGET_NR_readv 65 -#define TARGET_NR_writev 66 -#define TARGET_NR_pread64 67 -#define TARGET_NR_pwrite64 68 -#define TARGET_NR_preadv 69 -#define TARGET_NR_pwritev 70 -#define TARGET_NR_sendfile 71 -#define TARGET_NR_pselect6 72 -#define TARGET_NR_ppoll 73 -#define TARGET_NR_signalfd4 74 -#define TARGET_NR_vmsplice 75 -#define TARGET_NR_splice 76 -#define TARGET_NR_tee 77 -#define TARGET_NR_readlinkat 78 -#define TARGET_NR_newfstatat 79 -#define TARGET_NR_fstat 80 -#define TARGET_NR_sync 81 -#define TARGET_NR_fsync 82 -#define TARGET_NR_fdatasync 83 -#define TARGET_NR_sync_file_range 84 -#define TARGET_NR_timerfd_create 85 -#define TARGET_NR_timerfd_settime 86 -#define TARGET_NR_timerfd_gettime 87 -#define TARGET_NR_utimensat 88 -#define TARGET_NR_acct 89 -#define TARGET_NR_capget 90 -#define TARGET_NR_capset 91 -#define TARGET_NR_personality 92 -#define TARGET_NR_exit 93 -#define TARGET_NR_exit_group 94 -#define TARGET_NR_waitid 95 -#define TARGET_NR_set_tid_address 96 -#define TARGET_NR_unshare 97 -#define TARGET_NR_futex 98 -#define TARGET_NR_set_robust_list 99 -#define TARGET_NR_get_robust_list 100 -#define TARGET_NR_nanosleep 101 -#define TARGET_NR_getitimer 102 -#define TARGET_NR_setitimer 103 -#define TARGET_NR_kexec_load 104 -#define TARGET_NR_init_module 105 -#define TARGET_NR_delete_module 106 -#define TARGET_NR_timer_create 107 -#define TARGET_NR_timer_gettime 108 -#define TARGET_NR_timer_getoverrun 109 -#define TARGET_NR_timer_settime 110 -#define TARGET_NR_timer_delete 111 -#define TARGET_NR_clock_settime 112 -#define TARGET_NR_clock_gettime 113 -#define TARGET_NR_clock_getres 114 -#define TARGET_NR_clock_nanosleep 115 -#define TARGET_NR_syslog 116 -#define TARGET_NR_ptrace 117 -#define TARGET_NR_sched_setparam 118 -#define TARGET_NR_sched_setscheduler 119 -#define TARGET_NR_sched_getscheduler 120 -#define TARGET_NR_sched_getparam 121 -#define TARGET_NR_sched_setaffinity 122 -#define TARGET_NR_sched_getaffinity 123 -#define TARGET_NR_sched_yield 124 -#define TARGET_NR_sched_get_priority_max 125 -#define TARGET_NR_sched_get_priority_min 126 -#define TARGET_NR_sched_rr_get_interval 127 -#define TARGET_NR_restart_syscall 128 -#define TARGET_NR_kill 129 -#define TARGET_NR_tkill 130 -#define TARGET_NR_tgkill 131 -#define TARGET_NR_sigaltstack 132 -#define TARGET_NR_rt_sigsuspend 133 -#define TARGET_NR_rt_sigaction 134 -#define TARGET_NR_rt_sigprocmask 135 -#define TARGET_NR_rt_sigpending 136 -#define TARGET_NR_rt_sigtimedwait 137 -#define TARGET_NR_rt_sigqueueinfo 138 -#define TARGET_NR_rt_sigreturn 139 -#define TARGET_NR_setpriority 140 -#define TARGET_NR_getpriority 141 -#define TARGET_NR_reboot 142 -#define TARGET_NR_setregid 143 -#define TARGET_NR_setgid 144 -#define TARGET_NR_setreuid 145 -#define TARGET_NR_setuid 146 -#define TARGET_NR_setresuid 147 -#define TARGET_NR_getresuid 148 -#define TARGET_NR_setresgid 149 -#define TARGET_NR_getresgid 150 -#define TARGET_NR_setfsuid 151 -#define TARGET_NR_setfsgid 152 -#define TARGET_NR_times 153 -#define TARGET_NR_setpgid 154 -#define TARGET_NR_getpgid 155 -#define TARGET_NR_getsid 156 -#define TARGET_NR_setsid 157 -#define TARGET_NR_getgroups 158 -#define TARGET_NR_setgroups 159 -#define TARGET_NR_uname 160 -#define TARGET_NR_sethostname 161 -#define TARGET_NR_setdomainname 162 -#define TARGET_NR_getrlimit 163 -#define TARGET_NR_setrlimit 164 -#define TARGET_NR_getrusage 165 -#define TARGET_NR_umask 166 -#define TARGET_NR_prctl 167 -#define TARGET_NR_getcpu 168 -#define TARGET_NR_gettimeofday 169 -#define TARGET_NR_settimeofday 170 -#define TARGET_NR_adjtimex 171 -#define TARGET_NR_getpid 172 -#define TARGET_NR_getppid 173 -#define TARGET_NR_getuid 174 -#define TARGET_NR_geteuid 175 -#define TARGET_NR_getgid 176 -#define TARGET_NR_getegid 177 -#define TARGET_NR_gettid 178 -#define TARGET_NR_sysinfo 179 -#define TARGET_NR_mq_open 180 -#define TARGET_NR_mq_unlink 181 -#define TARGET_NR_mq_timedsend 182 -#define TARGET_NR_mq_timedreceive 183 -#define TARGET_NR_mq_notify 184 -#define TARGET_NR_mq_getsetattr 185 -#define TARGET_NR_msgget 186 -#define TARGET_NR_msgctl 187 -#define TARGET_NR_msgrcv 188 -#define TARGET_NR_msgsnd 189 -#define TARGET_NR_semget 190 -#define TARGET_NR_semctl 191 -#define TARGET_NR_semtimedop 192 -#define TARGET_NR_semop 193 -#define TARGET_NR_shmget 194 -#define TARGET_NR_shmctl 195 -#define TARGET_NR_shmat 196 -#define TARGET_NR_shmdt 197 -#define TARGET_NR_socket 198 -#define TARGET_NR_socketpair 199 -#define TARGET_NR_bind 200 -#define TARGET_NR_listen 201 -#define TARGET_NR_accept 202 -#define TARGET_NR_connect 203 -#define TARGET_NR_getsockname 204 -#define TARGET_NR_getpeername 205 -#define TARGET_NR_sendto 206 -#define TARGET_NR_recvfrom 207 -#define TARGET_NR_setsockopt 208 -#define TARGET_NR_getsockopt 209 -#define TARGET_NR_shutdown 210 -#define TARGET_NR_sendmsg 211 -#define TARGET_NR_recvmsg 212 -#define TARGET_NR_readahead 213 -#define TARGET_NR_brk 214 -#define TARGET_NR_munmap 215 -#define TARGET_NR_mremap 216 -#define TARGET_NR_add_key 217 -#define TARGET_NR_request_key 218 -#define TARGET_NR_keyctl 219 -#define TARGET_NR_clone 220 -#define TARGET_NR_execve 221 -#ifdef TARGET_RISCV32 -#define TARGET_NR_mmap2 222 -#define TARGET_NR_fadvise64_64 223 -#else -#define TARGET_NR_mmap 222 -#define TARGET_NR_fadvise64 223 -#endif -#define TARGET_NR_swapon 224 -#define TARGET_NR_swapoff 225 -#define TARGET_NR_mprotect 226 -#define TARGET_NR_msync 227 -#define TARGET_NR_mlock 228 -#define TARGET_NR_munlock 229 -#define TARGET_NR_mlockall 230 -#define TARGET_NR_munlockall 231 -#define TARGET_NR_mincore 232 -#define TARGET_NR_madvise 233 -#define TARGET_NR_remap_file_pages 234 -#define TARGET_NR_mbind 235 -#define TARGET_NR_get_mempolicy 236 -#define TARGET_NR_set_mempolicy 237 -#define TARGET_NR_migrate_pages 238 -#define TARGET_NR_move_pages 239 -#define TARGET_NR_rt_tgsigqueueinfo 240 -#define TARGET_NR_perf_event_open 241 -#define TARGET_NR_accept4 242 -#define TARGET_NR_recvmmsg 243 -#define TARGET_NR_arch_specific_syscall 244 -#define TARGET_NR_wait4 260 -#define TARGET_NR_prlimit64 261 -#define TARGET_NR_fanotify_init 262 -#define TARGET_NR_fanotify_mark 263 -#define TARGET_NR_name_to_handle_at 264 -#define TARGET_NR_open_by_handle_at 265 -#define TARGET_NR_clock_adjtime 266 -#define TARGET_NR_syncfs 267 -#define TARGET_NR_setns 268 -#define TARGET_NR_sendmmsg 269 -#define TARGET_NR_process_vm_readv 270 -#define TARGET_NR_process_vm_writev 271 -#define TARGET_NR_kcmp 272 -#define TARGET_NR_finit_module 273 -#define TARGET_NR_sched_setattr 274 -#define TARGET_NR_sched_getattr 275 -#define TARGET_NR_renameat2 276 -#define TARGET_NR_seccomp 277 -#define TARGET_NR_getrandom 278 -#define TARGET_NR_memfd_create 279 -#define TARGET_NR_bpf 280 -#define TARGET_NR_execveat 281 -#define TARGET_NR_userfaultfd 282 -#define TARGET_NR_membarrier 283 -#define TARGET_NR_mlock2 284 -#define TARGET_NR_copy_file_range 285 -#define TARGET_NR_preadv2 286 -#define TARGET_NR_pwritev2 287 -#define TARGET_NR_pkey_mprotect 288 -#define TARGET_NR_pkey_alloc 289 -#define TARGET_NR_pkey_free 290 -#define TARGET_NR_statx 291 -#define TARGET_NR_io_pgetevents 292 -#define TARGET_NR_rseq 293 -#define TARGET_NR_kexec_file_load 294 - -#define TARGET_NR_syscalls (TARGET_NR_kexec_file_load + 1) #endif diff --git a/linux-user/s390x/Makefile.objs b/linux-user/s390x/Makefile.objs new file mode 100644 index 0000000000..f30f1625cc --- /dev/null +++ b/linux-user/s390x/Makefile.objs @@ -0,0 +1,5 @@ +generated-files-y += linux-user/s390x/syscall_nr.h + +syshdr := $(SRC_PATH)/linux-user/s390x/syscallhdr.sh +%/syscall_nr.h: $(SRC_PATH)/linux-user/s390x/syscall.tbl $(syshdr) + $(call quiet-command, sh $(syshdr) $< $@ $(TARGET_SYSTBL_ABI),"GEN","$@") diff --git a/linux-user/s390x/syscall.tbl b/linux-user/s390x/syscall.tbl new file mode 100644 index 0000000000..bd7bd3581a --- /dev/null +++ b/linux-user/s390x/syscall.tbl @@ -0,0 +1,442 @@ +# SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note +# +# System call table for s390 +# +# Format: +# +# +# +# where can be common, 64, or 32 + +1 common exit sys_exit sys_exit +2 common fork sys_fork sys_fork +3 common read sys_read compat_sys_s390_read +4 common write sys_write compat_sys_s390_write +5 common open sys_open compat_sys_open +6 common close sys_close sys_close +7 common restart_syscall sys_restart_syscall sys_restart_syscall +8 common creat sys_creat sys_creat +9 common link sys_link sys_link +10 common unlink sys_unlink sys_unlink +11 common execve sys_execve compat_sys_execve +12 common chdir sys_chdir sys_chdir +13 32 time - sys_time32 +14 common mknod sys_mknod sys_mknod +15 common chmod sys_chmod sys_chmod +16 32 lchown - sys_lchown16 +19 common lseek sys_lseek compat_sys_lseek +20 common getpid sys_getpid sys_getpid +21 common mount sys_mount compat_sys_mount +22 common umount sys_oldumount sys_oldumount +23 32 setuid - sys_setuid16 +24 32 getuid - sys_getuid16 +25 32 stime - sys_stime32 +26 common ptrace sys_ptrace compat_sys_ptrace +27 common alarm sys_alarm sys_alarm +29 common pause sys_pause sys_pause +30 common utime sys_utime sys_utime32 +33 common access sys_access sys_access +34 common nice sys_nice sys_nice +36 common sync sys_sync sys_sync +37 common kill sys_kill sys_kill +38 common rename sys_rename sys_rename +39 common mkdir sys_mkdir sys_mkdir +40 common rmdir sys_rmdir sys_rmdir +41 common dup sys_dup sys_dup +42 common pipe sys_pipe sys_pipe +43 common times sys_times compat_sys_times +45 common brk sys_brk sys_brk +46 32 setgid - sys_setgid16 +47 32 getgid - sys_getgid16 +48 common signal sys_signal sys_signal +49 32 geteuid - sys_geteuid16 +50 32 getegid - sys_getegid16 +51 common acct sys_acct sys_acct +52 common umount2 sys_umount sys_umount +54 common ioctl sys_ioctl compat_sys_ioctl +55 common fcntl sys_fcntl compat_sys_fcntl +57 common setpgid sys_setpgid sys_setpgid +60 common umask sys_umask sys_umask +61 common chroot sys_chroot sys_chroot +62 common ustat sys_ustat compat_sys_ustat +63 common dup2 sys_dup2 sys_dup2 +64 common getppid sys_getppid sys_getppid +65 common getpgrp sys_getpgrp sys_getpgrp +66 common setsid sys_setsid sys_setsid +67 common sigaction sys_sigaction compat_sys_sigaction +70 32 setreuid - sys_setreuid16 +71 32 setregid - sys_setregid16 +72 common sigsuspend sys_sigsuspend sys_sigsuspend +73 common sigpending sys_sigpending compat_sys_sigpending +74 common sethostname sys_sethostname sys_sethostname +75 common setrlimit sys_setrlimit compat_sys_setrlimit +76 32 getrlimit - compat_sys_old_getrlimit +77 common getrusage sys_getrusage compat_sys_getrusage +78 common gettimeofday sys_gettimeofday compat_sys_gettimeofday +79 common settimeofday sys_settimeofday compat_sys_settimeofday +80 32 getgroups - sys_getgroups16 +81 32 setgroups - sys_setgroups16 +83 common symlink sys_symlink sys_symlink +85 common readlink sys_readlink sys_readlink +86 common uselib sys_uselib sys_uselib +87 common swapon sys_swapon sys_swapon +88 common reboot sys_reboot sys_reboot +89 common readdir - compat_sys_old_readdir +90 common mmap sys_old_mmap compat_sys_s390_old_mmap +91 common munmap sys_munmap sys_munmap +92 common truncate sys_truncate compat_sys_truncate +93 common ftruncate sys_ftruncate compat_sys_ftruncate +94 common fchmod sys_fchmod sys_fchmod +95 32 fchown - sys_fchown16 +96 common getpriority sys_getpriority sys_getpriority +97 common setpriority sys_setpriority sys_setpriority +99 common statfs sys_statfs compat_sys_statfs +100 common fstatfs sys_fstatfs compat_sys_fstatfs +101 32 ioperm - - +102 common socketcall sys_socketcall compat_sys_socketcall +103 common syslog sys_syslog sys_syslog +104 common setitimer sys_setitimer compat_sys_setitimer +105 common getitimer sys_getitimer compat_sys_getitimer +106 common stat sys_newstat compat_sys_newstat +107 common lstat sys_newlstat compat_sys_newlstat +108 common fstat sys_newfstat compat_sys_newfstat +110 common lookup_dcookie sys_lookup_dcookie compat_sys_lookup_dcookie +111 common vhangup sys_vhangup sys_vhangup +112 common idle - - +114 common wait4 sys_wait4 compat_sys_wait4 +115 common swapoff sys_swapoff sys_swapoff +116 common sysinfo sys_sysinfo compat_sys_sysinfo +117 common ipc sys_s390_ipc compat_sys_s390_ipc +118 common fsync sys_fsync sys_fsync +119 common sigreturn sys_sigreturn compat_sys_sigreturn +120 common clone sys_clone sys_clone +121 common setdomainname sys_setdomainname sys_setdomainname +122 common uname sys_newuname sys_newuname +124 common adjtimex sys_adjtimex sys_adjtimex_time32 +125 common mprotect sys_mprotect sys_mprotect +126 common sigprocmask sys_sigprocmask compat_sys_sigprocmask +127 common create_module - - +128 common init_module sys_init_module sys_init_module +129 common delete_module sys_delete_module sys_delete_module +130 common get_kernel_syms - - +131 common quotactl sys_quotactl sys_quotactl +132 common getpgid sys_getpgid sys_getpgid +133 common fchdir sys_fchdir sys_fchdir +134 common bdflush sys_bdflush sys_bdflush +135 common sysfs sys_sysfs sys_sysfs +136 common personality sys_s390_personality sys_s390_personality +137 common afs_syscall - - +138 32 setfsuid - sys_setfsuid16 +139 32 setfsgid - sys_setfsgid16 +140 32 _llseek - sys_llseek +141 common getdents sys_getdents compat_sys_getdents +142 32 _newselect - compat_sys_select +142 64 select sys_select - +143 common flock sys_flock sys_flock +144 common msync sys_msync sys_msync +145 common readv sys_readv compat_sys_readv +146 common writev sys_writev compat_sys_writev +147 common getsid sys_getsid sys_getsid +148 common fdatasync sys_fdatasync sys_fdatasync +149 common _sysctl sys_sysctl compat_sys_sysctl +150 common mlock sys_mlock sys_mlock +151 common munlock sys_munlock sys_munlock +152 common mlockall sys_mlockall sys_mlockall +153 common munlockall sys_munlockall sys_munlockall +154 common sched_setparam sys_sched_setparam sys_sched_setparam +155 common sched_getparam sys_sched_getparam sys_sched_getparam +156 common sched_setscheduler sys_sched_setscheduler sys_sched_setscheduler +157 common sched_getscheduler sys_sched_getscheduler sys_sched_getscheduler +158 common sched_yield sys_sched_yield sys_sched_yield +159 common sched_get_priority_max sys_sched_get_priority_max sys_sched_get_priority_max +160 common sched_get_priority_min sys_sched_get_priority_min sys_sched_get_priority_min +161 common sched_rr_get_interval sys_sched_rr_get_interval sys_sched_rr_get_interval_time32 +162 common nanosleep sys_nanosleep sys_nanosleep_time32 +163 common mremap sys_mremap sys_mremap +164 32 setresuid - sys_setresuid16 +165 32 getresuid - sys_getresuid16 +167 common query_module - - +168 common poll sys_poll sys_poll +169 common nfsservctl - - +170 32 setresgid - sys_setresgid16 +171 32 getresgid - sys_getresgid16 +172 common prctl sys_prctl sys_prctl +173 common rt_sigreturn sys_rt_sigreturn compat_sys_rt_sigreturn +174 common rt_sigaction sys_rt_sigaction compat_sys_rt_sigaction +175 common rt_sigprocmask sys_rt_sigprocmask compat_sys_rt_sigprocmask +176 common rt_sigpending sys_rt_sigpending compat_sys_rt_sigpending +177 common rt_sigtimedwait sys_rt_sigtimedwait compat_sys_rt_sigtimedwait_time32 +178 common rt_sigqueueinfo sys_rt_sigqueueinfo compat_sys_rt_sigqueueinfo +179 common rt_sigsuspend sys_rt_sigsuspend compat_sys_rt_sigsuspend +180 common pread64 sys_pread64 compat_sys_s390_pread64 +181 common pwrite64 sys_pwrite64 compat_sys_s390_pwrite64 +182 32 chown - sys_chown16 +183 common getcwd sys_getcwd sys_getcwd +184 common capget sys_capget sys_capget +185 common capset sys_capset sys_capset +186 common sigaltstack sys_sigaltstack compat_sys_sigaltstack +187 common sendfile sys_sendfile64 compat_sys_sendfile +188 common getpmsg - - +189 common putpmsg - - +190 common vfork sys_vfork sys_vfork +191 32 ugetrlimit - compat_sys_getrlimit +191 64 getrlimit sys_getrlimit - +192 32 mmap2 - compat_sys_s390_mmap2 +193 32 truncate64 - compat_sys_s390_truncate64 +194 32 ftruncate64 - compat_sys_s390_ftruncate64 +195 32 stat64 - compat_sys_s390_stat64 +196 32 lstat64 - compat_sys_s390_lstat64 +197 32 fstat64 - compat_sys_s390_fstat64 +198 32 lchown32 - sys_lchown +198 64 lchown sys_lchown - +199 32 getuid32 - sys_getuid +199 64 getuid sys_getuid - +200 32 getgid32 - sys_getgid +200 64 getgid sys_getgid - +201 32 geteuid32 - sys_geteuid +201 64 geteuid sys_geteuid - +202 32 getegid32 - sys_getegid +202 64 getegid sys_getegid - +203 32 setreuid32 - sys_setreuid +203 64 setreuid sys_setreuid - +204 32 setregid32 - sys_setregid +204 64 setregid sys_setregid - +205 32 getgroups32 - sys_getgroups +205 64 getgroups sys_getgroups - +206 32 setgroups32 - sys_setgroups +206 64 setgroups sys_setgroups - +207 32 fchown32 - sys_fchown +207 64 fchown sys_fchown - +208 32 setresuid32 - sys_setresuid +208 64 setresuid sys_setresuid - +209 32 getresuid32 - sys_getresuid +209 64 getresuid sys_getresuid - +210 32 setresgid32 - sys_setresgid +210 64 setresgid sys_setresgid - +211 32 getresgid32 - sys_getresgid +211 64 getresgid sys_getresgid - +212 32 chown32 - sys_chown +212 64 chown sys_chown - +213 32 setuid32 - sys_setuid +213 64 setuid sys_setuid - +214 32 setgid32 - sys_setgid +214 64 setgid sys_setgid - +215 32 setfsuid32 - sys_setfsuid +215 64 setfsuid sys_setfsuid - +216 32 setfsgid32 - sys_setfsgid +216 64 setfsgid sys_setfsgid - +217 common pivot_root sys_pivot_root sys_pivot_root +218 common mincore sys_mincore sys_mincore +219 common madvise sys_madvise sys_madvise +220 common getdents64 sys_getdents64 sys_getdents64 +221 32 fcntl64 - compat_sys_fcntl64 +222 common readahead sys_readahead compat_sys_s390_readahead +223 32 sendfile64 - compat_sys_sendfile64 +224 common setxattr sys_setxattr sys_setxattr +225 common lsetxattr sys_lsetxattr sys_lsetxattr +226 common fsetxattr sys_fsetxattr sys_fsetxattr +227 common getxattr sys_getxattr sys_getxattr +228 common lgetxattr sys_lgetxattr sys_lgetxattr +229 common fgetxattr sys_fgetxattr sys_fgetxattr +230 common listxattr sys_listxattr sys_listxattr +231 common llistxattr sys_llistxattr sys_llistxattr +232 common flistxattr sys_flistxattr sys_flistxattr +233 common removexattr sys_removexattr sys_removexattr +234 common lremovexattr sys_lremovexattr sys_lremovexattr +235 common fremovexattr sys_fremovexattr sys_fremovexattr +236 common gettid sys_gettid sys_gettid +237 common tkill sys_tkill sys_tkill +238 common futex sys_futex sys_futex_time32 +239 common sched_setaffinity sys_sched_setaffinity compat_sys_sched_setaffinity +240 common sched_getaffinity sys_sched_getaffinity compat_sys_sched_getaffinity +241 common tgkill sys_tgkill sys_tgkill +243 common io_setup sys_io_setup compat_sys_io_setup +244 common io_destroy sys_io_destroy sys_io_destroy +245 common io_getevents sys_io_getevents sys_io_getevents_time32 +246 common io_submit sys_io_submit compat_sys_io_submit +247 common io_cancel sys_io_cancel sys_io_cancel +248 common exit_group sys_exit_group sys_exit_group +249 common epoll_create sys_epoll_create sys_epoll_create +250 common epoll_ctl sys_epoll_ctl sys_epoll_ctl +251 common epoll_wait sys_epoll_wait sys_epoll_wait +252 common set_tid_address sys_set_tid_address sys_set_tid_address +253 common fadvise64 sys_fadvise64_64 compat_sys_s390_fadvise64 +254 common timer_create sys_timer_create compat_sys_timer_create +255 common timer_settime sys_timer_settime sys_timer_settime32 +256 common timer_gettime sys_timer_gettime sys_timer_gettime32 +257 common timer_getoverrun sys_timer_getoverrun sys_timer_getoverrun +258 common timer_delete sys_timer_delete sys_timer_delete +259 common clock_settime sys_clock_settime sys_clock_settime32 +260 common clock_gettime sys_clock_gettime sys_clock_gettime32 +261 common clock_getres sys_clock_getres sys_clock_getres_time32 +262 common clock_nanosleep sys_clock_nanosleep sys_clock_nanosleep_time32 +264 32 fadvise64_64 - compat_sys_s390_fadvise64_64 +265 common statfs64 sys_statfs64 compat_sys_statfs64 +266 common fstatfs64 sys_fstatfs64 compat_sys_fstatfs64 +267 common remap_file_pages sys_remap_file_pages sys_remap_file_pages +268 common mbind sys_mbind compat_sys_mbind +269 common get_mempolicy sys_get_mempolicy compat_sys_get_mempolicy +270 common set_mempolicy sys_set_mempolicy compat_sys_set_mempolicy +271 common mq_open sys_mq_open compat_sys_mq_open +272 common mq_unlink sys_mq_unlink sys_mq_unlink +273 common mq_timedsend sys_mq_timedsend sys_mq_timedsend_time32 +274 common mq_timedreceive sys_mq_timedreceive sys_mq_timedreceive_time32 +275 common mq_notify sys_mq_notify compat_sys_mq_notify +276 common mq_getsetattr sys_mq_getsetattr compat_sys_mq_getsetattr +277 common kexec_load sys_kexec_load compat_sys_kexec_load +278 common add_key sys_add_key sys_add_key +279 common request_key sys_request_key sys_request_key +280 common keyctl sys_keyctl compat_sys_keyctl +281 common waitid sys_waitid compat_sys_waitid +282 common ioprio_set sys_ioprio_set sys_ioprio_set +283 common ioprio_get sys_ioprio_get sys_ioprio_get +284 common inotify_init sys_inotify_init sys_inotify_init +285 common inotify_add_watch sys_inotify_add_watch sys_inotify_add_watch +286 common inotify_rm_watch sys_inotify_rm_watch sys_inotify_rm_watch +287 common migrate_pages sys_migrate_pages compat_sys_migrate_pages +288 common openat sys_openat compat_sys_openat +289 common mkdirat sys_mkdirat sys_mkdirat +290 common mknodat sys_mknodat sys_mknodat +291 common fchownat sys_fchownat sys_fchownat +292 common futimesat sys_futimesat sys_futimesat_time32 +293 32 fstatat64 - compat_sys_s390_fstatat64 +293 64 newfstatat sys_newfstatat - +294 common unlinkat sys_unlinkat sys_unlinkat +295 common renameat sys_renameat sys_renameat +296 common linkat sys_linkat sys_linkat +297 common symlinkat sys_symlinkat sys_symlinkat +298 common readlinkat sys_readlinkat sys_readlinkat +299 common fchmodat sys_fchmodat sys_fchmodat +300 common faccessat sys_faccessat sys_faccessat +301 common pselect6 sys_pselect6 compat_sys_pselect6_time32 +302 common ppoll sys_ppoll compat_sys_ppoll_time32 +303 common unshare sys_unshare sys_unshare +304 common set_robust_list sys_set_robust_list compat_sys_set_robust_list +305 common get_robust_list sys_get_robust_list compat_sys_get_robust_list +306 common splice sys_splice sys_splice +307 common sync_file_range sys_sync_file_range compat_sys_s390_sync_file_range +308 common tee sys_tee sys_tee +309 common vmsplice sys_vmsplice compat_sys_vmsplice +310 common move_pages sys_move_pages compat_sys_move_pages +311 common getcpu sys_getcpu sys_getcpu +312 common epoll_pwait sys_epoll_pwait compat_sys_epoll_pwait +313 common utimes sys_utimes sys_utimes_time32 +314 common fallocate sys_fallocate compat_sys_s390_fallocate +315 common utimensat sys_utimensat sys_utimensat_time32 +316 common signalfd sys_signalfd compat_sys_signalfd +317 common timerfd - - +318 common eventfd sys_eventfd sys_eventfd +319 common timerfd_create sys_timerfd_create sys_timerfd_create +320 common timerfd_settime sys_timerfd_settime sys_timerfd_settime32 +321 common timerfd_gettime sys_timerfd_gettime sys_timerfd_gettime32 +322 common signalfd4 sys_signalfd4 compat_sys_signalfd4 +323 common eventfd2 sys_eventfd2 sys_eventfd2 +324 common inotify_init1 sys_inotify_init1 sys_inotify_init1 +325 common pipe2 sys_pipe2 sys_pipe2 +326 common dup3 sys_dup3 sys_dup3 +327 common epoll_create1 sys_epoll_create1 sys_epoll_create1 +328 common preadv sys_preadv compat_sys_preadv +329 common pwritev sys_pwritev compat_sys_pwritev +330 common rt_tgsigqueueinfo sys_rt_tgsigqueueinfo compat_sys_rt_tgsigqueueinfo +331 common perf_event_open sys_perf_event_open sys_perf_event_open +332 common fanotify_init sys_fanotify_init sys_fanotify_init +333 common fanotify_mark sys_fanotify_mark compat_sys_fanotify_mark +334 common prlimit64 sys_prlimit64 sys_prlimit64 +335 common name_to_handle_at sys_name_to_handle_at sys_name_to_handle_at +336 common open_by_handle_at sys_open_by_handle_at compat_sys_open_by_handle_at +337 common clock_adjtime sys_clock_adjtime sys_clock_adjtime32 +338 common syncfs sys_syncfs sys_syncfs +339 common setns sys_setns sys_setns +340 common process_vm_readv sys_process_vm_readv compat_sys_process_vm_readv +341 common process_vm_writev sys_process_vm_writev compat_sys_process_vm_writev +342 common s390_runtime_instr sys_s390_runtime_instr sys_s390_runtime_instr +343 common kcmp sys_kcmp sys_kcmp +344 common finit_module sys_finit_module sys_finit_module +345 common sched_setattr sys_sched_setattr sys_sched_setattr +346 common sched_getattr sys_sched_getattr sys_sched_getattr +347 common renameat2 sys_renameat2 sys_renameat2 +348 common seccomp sys_seccomp sys_seccomp +349 common getrandom sys_getrandom sys_getrandom +350 common memfd_create sys_memfd_create sys_memfd_create +351 common bpf sys_bpf sys_bpf +352 common s390_pci_mmio_write sys_s390_pci_mmio_write sys_s390_pci_mmio_write +353 common s390_pci_mmio_read sys_s390_pci_mmio_read sys_s390_pci_mmio_read +354 common execveat sys_execveat compat_sys_execveat +355 common userfaultfd sys_userfaultfd sys_userfaultfd +356 common membarrier sys_membarrier sys_membarrier +357 common recvmmsg sys_recvmmsg compat_sys_recvmmsg_time32 +358 common sendmmsg sys_sendmmsg compat_sys_sendmmsg +359 common socket sys_socket sys_socket +360 common socketpair sys_socketpair sys_socketpair +361 common bind sys_bind sys_bind +362 common connect sys_connect sys_connect +363 common listen sys_listen sys_listen +364 common accept4 sys_accept4 sys_accept4 +365 common getsockopt sys_getsockopt compat_sys_getsockopt +366 common setsockopt sys_setsockopt compat_sys_setsockopt +367 common getsockname sys_getsockname sys_getsockname +368 common getpeername sys_getpeername sys_getpeername +369 common sendto sys_sendto sys_sendto +370 common sendmsg sys_sendmsg compat_sys_sendmsg +371 common recvfrom sys_recvfrom compat_sys_recvfrom +372 common recvmsg sys_recvmsg compat_sys_recvmsg +373 common shutdown sys_shutdown sys_shutdown +374 common mlock2 sys_mlock2 sys_mlock2 +375 common copy_file_range sys_copy_file_range sys_copy_file_range +376 common preadv2 sys_preadv2 compat_sys_preadv2 +377 common pwritev2 sys_pwritev2 compat_sys_pwritev2 +378 common s390_guarded_storage sys_s390_guarded_storage sys_s390_guarded_storage +379 common statx sys_statx sys_statx +380 common s390_sthyi sys_s390_sthyi sys_s390_sthyi +381 common kexec_file_load sys_kexec_file_load sys_kexec_file_load +382 common io_pgetevents sys_io_pgetevents compat_sys_io_pgetevents +383 common rseq sys_rseq sys_rseq +384 common pkey_mprotect sys_pkey_mprotect sys_pkey_mprotect +385 common pkey_alloc sys_pkey_alloc sys_pkey_alloc +386 common pkey_free sys_pkey_free sys_pkey_free +# room for arch specific syscalls +392 64 semtimedop sys_semtimedop - +393 common semget sys_semget sys_semget +394 common semctl sys_semctl compat_sys_semctl +395 common shmget sys_shmget sys_shmget +396 common shmctl sys_shmctl compat_sys_shmctl +397 common shmat sys_shmat compat_sys_shmat +398 common shmdt sys_shmdt sys_shmdt +399 common msgget sys_msgget sys_msgget +400 common msgsnd sys_msgsnd compat_sys_msgsnd +401 common msgrcv sys_msgrcv compat_sys_msgrcv +402 common msgctl sys_msgctl compat_sys_msgctl +403 32 clock_gettime64 - sys_clock_gettime +404 32 clock_settime64 - sys_clock_settime +405 32 clock_adjtime64 - sys_clock_adjtime +406 32 clock_getres_time64 - sys_clock_getres +407 32 clock_nanosleep_time64 - sys_clock_nanosleep +408 32 timer_gettime64 - sys_timer_gettime +409 32 timer_settime64 - sys_timer_settime +410 32 timerfd_gettime64 - sys_timerfd_gettime +411 32 timerfd_settime64 - sys_timerfd_settime +412 32 utimensat_time64 - sys_utimensat +413 32 pselect6_time64 - compat_sys_pselect6_time64 +414 32 ppoll_time64 - compat_sys_ppoll_time64 +416 32 io_pgetevents_time64 - sys_io_pgetevents +417 32 recvmmsg_time64 - compat_sys_recvmmsg_time64 +418 32 mq_timedsend_time64 - sys_mq_timedsend +419 32 mq_timedreceive_time64 - sys_mq_timedreceive +420 32 semtimedop_time64 - sys_semtimedop +421 32 rt_sigtimedwait_time64 - compat_sys_rt_sigtimedwait_time64 +422 32 futex_time64 - sys_futex +423 32 sched_rr_get_interval_time64 - sys_sched_rr_get_interval +424 common pidfd_send_signal sys_pidfd_send_signal sys_pidfd_send_signal +425 common io_uring_setup sys_io_uring_setup sys_io_uring_setup +426 common io_uring_enter sys_io_uring_enter sys_io_uring_enter +427 common io_uring_register sys_io_uring_register sys_io_uring_register +428 common open_tree sys_open_tree sys_open_tree +429 common move_mount sys_move_mount sys_move_mount +430 common fsopen sys_fsopen sys_fsopen +431 common fsconfig sys_fsconfig sys_fsconfig +432 common fsmount sys_fsmount sys_fsmount +433 common fspick sys_fspick sys_fspick +434 common pidfd_open sys_pidfd_open sys_pidfd_open +435 common clone3 sys_clone3 sys_clone3 +437 common openat2 sys_openat2 sys_openat2 +438 common pidfd_getfd sys_pidfd_getfd sys_pidfd_getfd diff --git a/linux-user/s390x/syscall_nr.h b/linux-user/s390x/syscall_nr.h deleted file mode 100644 index b1553a0810..0000000000 --- a/linux-user/s390x/syscall_nr.h +++ /dev/null @@ -1,398 +0,0 @@ -/* - * This file contains the system call numbers. - */ - -#ifndef LINUX_USER_S390X_SYSCALL_NR_H -#define LINUX_USER_S390X_SYSCALL_NR_H - -#define TARGET_NR_exit 1 -#define TARGET_NR_fork 2 -#define TARGET_NR_read 3 -#define TARGET_NR_write 4 -#define TARGET_NR_open 5 -#define TARGET_NR_close 6 -#define TARGET_NR_restart_syscall 7 -#define TARGET_NR_creat 8 -#define TARGET_NR_link 9 -#define TARGET_NR_unlink 10 -#define TARGET_NR_execve 11 -#define TARGET_NR_chdir 12 -#define TARGET_NR_mknod 14 -#define TARGET_NR_chmod 15 -#define TARGET_NR_lseek 19 -#define TARGET_NR_getpid 20 -#define TARGET_NR_mount 21 -#define TARGET_NR_umount 22 -#define TARGET_NR_ptrace 26 -#define TARGET_NR_alarm 27 -#define TARGET_NR_pause 29 -#define TARGET_NR_utime 30 -#define TARGET_NR_access 33 -#define TARGET_NR_nice 34 -#define TARGET_NR_sync 36 -#define TARGET_NR_kill 37 -#define TARGET_NR_rename 38 -#define TARGET_NR_mkdir 39 -#define TARGET_NR_rmdir 40 -#define TARGET_NR_dup 41 -#define TARGET_NR_pipe 42 -#define TARGET_NR_times 43 -#define TARGET_NR_brk 45 -#define TARGET_NR_signal 48 -#define TARGET_NR_acct 51 -#define TARGET_NR_umount2 52 -#define TARGET_NR_ioctl 54 -#define TARGET_NR_fcntl 55 -#define TARGET_NR_setpgid 57 -#define TARGET_NR_umask 60 -#define TARGET_NR_chroot 61 -#define TARGET_NR_ustat 62 -#define TARGET_NR_dup2 63 -#define TARGET_NR_getppid 64 -#define TARGET_NR_getpgrp 65 -#define TARGET_NR_setsid 66 -#define TARGET_NR_sigaction 67 -#define TARGET_NR_sigsuspend 72 -#define TARGET_NR_sigpending 73 -#define TARGET_NR_sethostname 74 -#define TARGET_NR_setrlimit 75 -#define TARGET_NR_getrusage 77 -#define TARGET_NR_gettimeofday 78 -#define TARGET_NR_settimeofday 79 -#define TARGET_NR_symlink 83 -#define TARGET_NR_readlink 85 -#define TARGET_NR_uselib 86 -#define TARGET_NR_swapon 87 -#define TARGET_NR_reboot 88 -#define TARGET_NR_readdir 89 -#define TARGET_NR_mmap 90 -#define TARGET_NR_munmap 91 -#define TARGET_NR_truncate 92 -#define TARGET_NR_ftruncate 93 -#define TARGET_NR_fchmod 94 -#define TARGET_NR_getpriority 96 -#define TARGET_NR_setpriority 97 -#define TARGET_NR_statfs 99 -#define TARGET_NR_fstatfs 100 -#define TARGET_NR_socketcall 102 -#define TARGET_NR_syslog 103 -#define TARGET_NR_setitimer 104 -#define TARGET_NR_getitimer 105 -#define TARGET_NR_stat 106 -#define TARGET_NR_lstat 107 -#define TARGET_NR_fstat 108 -#define TARGET_NR_lookup_dcookie 110 -#define TARGET_NR_vhangup 111 -#define TARGET_NR_idle 112 -#define TARGET_NR_wait4 114 -#define TARGET_NR_swapoff 115 -#define TARGET_NR_sysinfo 116 -#define TARGET_NR_ipc 117 -#define TARGET_NR_fsync 118 -#define TARGET_NR_sigreturn 119 -#define TARGET_NR_clone 120 -#define TARGET_NR_setdomainname 121 -#define TARGET_NR_uname 122 -#define TARGET_NR_adjtimex 124 -#define TARGET_NR_mprotect 125 -#define TARGET_NR_sigprocmask 126 -#define TARGET_NR_create_module 127 -#define TARGET_NR_init_module 128 -#define TARGET_NR_delete_module 129 -#define TARGET_NR_get_kernel_syms 130 -#define TARGET_NR_quotactl 131 -#define TARGET_NR_getpgid 132 -#define TARGET_NR_fchdir 133 -#define TARGET_NR_bdflush 134 -#define TARGET_NR_sysfs 135 -#define TARGET_NR_personality 136 -#define TARGET_NR_afs_syscall 137 /* Syscall for Andrew File System */ -#define TARGET_NR_getdents 141 -#define TARGET_NR_flock 143 -#define TARGET_NR_msync 144 -#define TARGET_NR_readv 145 -#define TARGET_NR_writev 146 -#define TARGET_NR_getsid 147 -#define TARGET_NR_fdatasync 148 -#define TARGET_NR__sysctl 149 -#define TARGET_NR_mlock 150 -#define TARGET_NR_munlock 151 -#define TARGET_NR_mlockall 152 -#define TARGET_NR_munlockall 153 -#define TARGET_NR_sched_setparam 154 -#define TARGET_NR_sched_getparam 155 -#define TARGET_NR_sched_setscheduler 156 -#define TARGET_NR_sched_getscheduler 157 -#define TARGET_NR_sched_yield 158 -#define TARGET_NR_sched_get_priority_max 159 -#define TARGET_NR_sched_get_priority_min 160 -#define TARGET_NR_sched_rr_get_interval 161 -#define TARGET_NR_nanosleep 162 -#define TARGET_NR_mremap 163 -#define TARGET_NR_query_module 167 -#define TARGET_NR_poll 168 -#define TARGET_NR_nfsservctl 169 -#define TARGET_NR_prctl 172 -#define TARGET_NR_rt_sigreturn 173 -#define TARGET_NR_rt_sigaction 174 -#define TARGET_NR_rt_sigprocmask 175 -#define TARGET_NR_rt_sigpending 176 -#define TARGET_NR_rt_sigtimedwait 177 -#define TARGET_NR_rt_sigqueueinfo 178 -#define TARGET_NR_rt_sigsuspend 179 -#define TARGET_NR_pread64 180 -#define TARGET_NR_pwrite64 181 -#define TARGET_NR_getcwd 183 -#define TARGET_NR_capget 184 -#define TARGET_NR_capset 185 -#define TARGET_NR_sigaltstack 186 -#define TARGET_NR_sendfile 187 -#define TARGET_NR_getpmsg 188 -#define TARGET_NR_putpmsg 189 -#define TARGET_NR_vfork 190 -#define TARGET_NR_pivot_root 217 -#define TARGET_NR_mincore 218 -#define TARGET_NR_madvise 219 -#define TARGET_NR_getdents64 220 -#define TARGET_NR_readahead 222 -#define TARGET_NR_setxattr 224 -#define TARGET_NR_lsetxattr 225 -#define TARGET_NR_fsetxattr 226 -#define TARGET_NR_getxattr 227 -#define TARGET_NR_lgetxattr 228 -#define TARGET_NR_fgetxattr 229 -#define TARGET_NR_listxattr 230 -#define TARGET_NR_llistxattr 231 -#define TARGET_NR_flistxattr 232 -#define TARGET_NR_removexattr 233 -#define TARGET_NR_lremovexattr 234 -#define TARGET_NR_fremovexattr 235 -#define TARGET_NR_gettid 236 -#define TARGET_NR_tkill 237 -#define TARGET_NR_futex 238 -#define TARGET_NR_sched_setaffinity 239 -#define TARGET_NR_sched_getaffinity 240 -#define TARGET_NR_tgkill 241 -/* Number 242 is reserved for tux */ -#define TARGET_NR_io_setup 243 -#define TARGET_NR_io_destroy 244 -#define TARGET_NR_io_getevents 245 -#define TARGET_NR_io_submit 246 -#define TARGET_NR_io_cancel 247 -#define TARGET_NR_exit_group 248 -#define TARGET_NR_epoll_create 249 -#define TARGET_NR_epoll_ctl 250 -#define TARGET_NR_epoll_wait 251 -#define TARGET_NR_set_tid_address 252 -#define TARGET_NR_fadvise64 253 -#define TARGET_NR_timer_create 254 -#define TARGET_NR_timer_settime (TARGET_NR_timer_create+1) -#define TARGET_NR_timer_gettime (TARGET_NR_timer_create+2) -#define TARGET_NR_timer_getoverrun (TARGET_NR_timer_create+3) -#define TARGET_NR_timer_delete (TARGET_NR_timer_create+4) -#define TARGET_NR_clock_settime (TARGET_NR_timer_create+5) -#define TARGET_NR_clock_gettime (TARGET_NR_timer_create+6) -#define TARGET_NR_clock_getres (TARGET_NR_timer_create+7) -#define TARGET_NR_clock_nanosleep (TARGET_NR_timer_create+8) -/* Number 263 is reserved for vserver */ -#define TARGET_NR_statfs64 265 -#define TARGET_NR_fstatfs64 266 -#define TARGET_NR_remap_file_pages 267 -/* Number 268 is reserved for new sys_mbind */ -/* Number 269 is reserved for new sys_get_mempolicy */ -/* Number 270 is reserved for new sys_set_mempolicy */ -#define TARGET_NR_mq_open 271 -#define TARGET_NR_mq_unlink 272 -#define TARGET_NR_mq_timedsend 273 -#define TARGET_NR_mq_timedreceive 274 -#define TARGET_NR_mq_notify 275 -#define TARGET_NR_mq_getsetattr 276 -#define TARGET_NR_kexec_load 277 -#define TARGET_NR_add_key 278 -#define TARGET_NR_request_key 279 -#define TARGET_NR_keyctl 280 -#define TARGET_NR_waitid 281 -#define TARGET_NR_ioprio_set 282 -#define TARGET_NR_ioprio_get 283 -#define TARGET_NR_inotify_init 284 -#define TARGET_NR_inotify_add_watch 285 -#define TARGET_NR_inotify_rm_watch 286 -/* Number 287 is reserved for new sys_migrate_pages */ -#define TARGET_NR_openat 288 -#define TARGET_NR_mkdirat 289 -#define TARGET_NR_mknodat 290 -#define TARGET_NR_fchownat 291 -#define TARGET_NR_futimesat 292 -#define TARGET_NR_unlinkat 294 -#define TARGET_NR_renameat 295 -#define TARGET_NR_linkat 296 -#define TARGET_NR_symlinkat 297 -#define TARGET_NR_readlinkat 298 -#define TARGET_NR_fchmodat 299 -#define TARGET_NR_faccessat 300 -#define TARGET_NR_pselect6 301 -#define TARGET_NR_ppoll 302 -#define TARGET_NR_unshare 303 -#define TARGET_NR_set_robust_list 304 -#define TARGET_NR_get_robust_list 305 -#define TARGET_NR_splice 306 -#define TARGET_NR_sync_file_range 307 -#define TARGET_NR_tee 308 -#define TARGET_NR_vmsplice 309 -/* Number 310 is reserved for new sys_move_pages */ -#define TARGET_NR_getcpu 311 -#define TARGET_NR_epoll_pwait 312 -#define TARGET_NR_utimes 313 -#define TARGET_NR_fallocate 314 -#define TARGET_NR_utimensat 315 -#define TARGET_NR_signalfd 316 -#define TARGET_NR_timerfd 317 -#define TARGET_NR_eventfd 318 -#define TARGET_NR_timerfd_create 319 -#define TARGET_NR_timerfd_settime 320 -#define TARGET_NR_timerfd_gettime 321 -#define TARGET_NR_signalfd4 322 -#define TARGET_NR_eventfd2 323 -#define TARGET_NR_inotify_init1 324 -#define TARGET_NR_pipe2 325 -#define TARGET_NR_dup3 326 -#define TARGET_NR_epoll_create1 327 -#define TARGET_NR_preadv 328 -#define TARGET_NR_pwritev 329 -#define TARGET_NR_rt_tgsigqueueinfo 330 -#define TARGET_NR_perf_event_open 331 -#define TARGET_NR_fanotify_init 332 -#define TARGET_NR_fanotify_mark 333 -#define TARGET_NR_prlimit64 334 -#define TARGET_NR_name_to_handle_at 335 -#define TARGET_NR_open_by_handle_at 336 -#define TARGET_NR_clock_adjtime 337 -#define TARGET_NR_syncfs 338 -#define TARGET_NR_setns 339 -#define TARGET_NR_process_vm_readv 340 -#define TARGET_NR_process_vm_writev 341 -#define TARGET_NR_s390_runtime_instr 342 -#define TARGET_NR_kcmp 343 -#define TARGET_NR_finit_module 344 -#define TARGET_NR_sched_setattr 345 -#define TARGET_NR_sched_getattr 346 -#define TARGET_NR_renameat2 347 -#define TARGET_NR_seccomp 348 -#define TARGET_NR_getrandom 349 -#define TARGET_NR_memfd_create 350 -#define TARGET_NR_bpf 351 -#define TARGET_NR_s390_pci_mmio_write 352 -#define TARGET_NR_s390_pci_mmio_read 353 -#define TARGET_NR_execveat 354 -#define TARGET_NR_userfaultfd 355 -#define TARGET_NR_membarrier 356 -#define TARGET_NR_recvmmsg 357 -#define TARGET_NR_sendmmsg 358 -#define TARGET_NR_socket 359 -#define TARGET_NR_socketpair 360 -#define TARGET_NR_bind 361 -#define TARGET_NR_connect 362 -#define TARGET_NR_listen 363 -#define TARGET_NR_accept4 364 -#define TARGET_NR_getsockopt 365 -#define TARGET_NR_setsockopt 366 -#define TARGET_NR_getsockname 367 -#define TARGET_NR_getpeername 368 -#define TARGET_NR_sendto 369 -#define TARGET_NR_sendmsg 370 -#define TARGET_NR_recvfrom 371 -#define TARGET_NR_recvmsg 372 -#define TARGET_NR_shutdown 373 -#define TARGET_NR_mlock2 374 - -/* - * There are some system calls that are not present on 64 bit, some - * have a different name although they do the same (e.g. TARGET_NR_chown32 - * is TARGET_NR_chown on 64 bit). - */ -#ifndef TARGET_S390X - -#define TARGET_NR_time 13 -#define TARGET_NR_lchown 16 -#define TARGET_NR_setuid 23 -#define TARGET_NR_getuid 24 -#define TARGET_NR_stime 25 -#define TARGET_NR_setgid 46 -#define TARGET_NR_getgid 47 -#define TARGET_NR_geteuid 49 -#define TARGET_NR_getegid 50 -#define TARGET_NR_setreuid 70 -#define TARGET_NR_setregid 71 -#define TARGET_NR_getrlimit 76 -#define TARGET_NR_getgroups 80 -#define TARGET_NR_setgroups 81 -#define TARGET_NR_fchown 95 -#define TARGET_NR_ioperm 101 -#define TARGET_NR_setfsuid 138 -#define TARGET_NR_setfsgid 139 -#define TARGET_NR__llseek 140 -#define TARGET_NR__newselect 142 -#define TARGET_NR_setresuid 164 -#define TARGET_NR_getresuid 165 -#define TARGET_NR_setresgid 170 -#define TARGET_NR_getresgid 171 -#define TARGET_NR_chown 182 -#define TARGET_NR_ugetrlimit 191 /* SuS compliant getrlimit */ -#define TARGET_NR_mmap2 192 -#define TARGET_NR_truncate64 193 -#define TARGET_NR_ftruncate64 194 -#define TARGET_NR_stat64 195 -#define TARGET_NR_lstat64 196 -#define TARGET_NR_fstat64 197 -#define TARGET_NR_lchown32 198 -#define TARGET_NR_getuid32 199 -#define TARGET_NR_getgid32 200 -#define TARGET_NR_geteuid32 201 -#define TARGET_NR_getegid32 202 -#define TARGET_NR_setreuid32 203 -#define TARGET_NR_setregid32 204 -#define TARGET_NR_getgroups32 205 -#define TARGET_NR_setgroups32 206 -#define TARGET_NR_fchown32 207 -#define TARGET_NR_setresuid32 208 -#define TARGET_NR_getresuid32 209 -#define TARGET_NR_setresgid32 210 -#define TARGET_NR_getresgid32 211 -#define TARGET_NR_chown32 212 -#define TARGET_NR_setuid32 213 -#define TARGET_NR_setgid32 214 -#define TARGET_NR_setfsuid32 215 -#define TARGET_NR_setfsgid32 216 -#define TARGET_NR_fcntl64 221 -#define TARGET_NR_sendfile64 223 -#define TARGET_NR_fadvise64_64 264 -#define TARGET_NR_fstatat64 293 - -#else - -#define TARGET_NR_select 142 -#define TARGET_NR_getrlimit 191 /* SuS compliant getrlimit */ -#define TARGET_NR_lchown 198 -#define TARGET_NR_getuid 199 -#define TARGET_NR_getgid 200 -#define TARGET_NR_geteuid 201 -#define TARGET_NR_getegid 202 -#define TARGET_NR_setreuid 203 -#define TARGET_NR_setregid 204 -#define TARGET_NR_getgroups 205 -#define TARGET_NR_setgroups 206 -#define TARGET_NR_fchown 207 -#define TARGET_NR_setresuid 208 -#define TARGET_NR_getresuid 209 -#define TARGET_NR_setresgid 210 -#define TARGET_NR_getresgid 211 -#define TARGET_NR_chown 212 -#define TARGET_NR_setuid 213 -#define TARGET_NR_setgid 214 -#define TARGET_NR_setfsuid 215 -#define TARGET_NR_setfsgid 216 -#define TARGET_NR_newfstatat 293 - -#endif - -#endif diff --git a/linux-user/s390x/syscallhdr.sh b/linux-user/s390x/syscallhdr.sh new file mode 100755 index 0000000000..85a99c48de --- /dev/null +++ b/linux-user/s390x/syscallhdr.sh @@ -0,0 +1,32 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 + +in="$1" +out="$2" +my_abis=`echo "($3)" | tr ',' '|'` +prefix="$4" +offset="$5" + +fileguard=LINUX_USER_S390X_`basename "$out" | sed \ + -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/' \ + -e 's/[^A-Z0-9_]/_/g' -e 's/__/_/g'` +grep -E "^[0-9A-Fa-fXx]+[[:space:]]+${my_abis}" "$in" | sort -n | ( + printf "#ifndef %s\n" "${fileguard}" + printf "#define %s\n" "${fileguard}" + printf "\n" + + nxt=0 + while read nr abi name entry ; do + if [ -z "$offset" ]; then + printf "#define TARGET_NR_%s%s\t%s\n" \ + "${prefix}" "${name}" "${nr}" + else + printf "#define TARGET_NR_%s%s\t(%s + %s)\n" \ + "${prefix}" "${name}" "${offset}" "${nr}" + fi + nxt=$((nr+1)) + done + + printf "\n" + printf "#endif /* %s */\n" "${fileguard}" +) > "$out" diff --git a/linux-user/sh4/Makefile.objs b/linux-user/sh4/Makefile.objs new file mode 100644 index 0000000000..83fc939570 --- /dev/null +++ b/linux-user/sh4/Makefile.objs @@ -0,0 +1,5 @@ +generated-files-y += linux-user/sh4/syscall_nr.h + +syshdr := $(SRC_PATH)/linux-user/sh4/syscallhdr.sh +%/syscall_nr.h: $(SRC_PATH)/linux-user/sh4/syscall.tbl $(syshdr) + $(call quiet-command, sh $(syshdr) $< $@ $(TARGET_SYSTBL_ABI),"GEN","$@") diff --git a/linux-user/sh4/syscall.tbl b/linux-user/sh4/syscall.tbl new file mode 100644 index 0000000000..c7a30fcd13 --- /dev/null +++ b/linux-user/sh4/syscall.tbl @@ -0,0 +1,442 @@ +# SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note +# +# system call numbers and entry vectors for sh +# +# The format is: +# +# +# The is always "common" for this file +# +0 common restart_syscall sys_restart_syscall +1 common exit sys_exit +2 common fork sys_fork +3 common read sys_read +4 common write sys_write +5 common open sys_open +6 common close sys_close +7 common waitpid sys_waitpid +8 common creat sys_creat +9 common link sys_link +10 common unlink sys_unlink +11 common execve sys_execve +12 common chdir sys_chdir +13 common time sys_time32 +14 common mknod sys_mknod +15 common chmod sys_chmod +16 common lchown sys_lchown16 +# 17 was break +18 common oldstat sys_stat +19 common lseek sys_lseek +20 common getpid sys_getpid +21 common mount sys_mount +22 common umount sys_oldumount +23 common setuid sys_setuid16 +24 common getuid sys_getuid16 +25 common stime sys_stime32 +26 common ptrace sys_ptrace +27 common alarm sys_alarm +28 common oldfstat sys_fstat +29 common pause sys_pause +30 common utime sys_utime32 +# 31 was stty +# 32 was gtty +33 common access sys_access +34 common nice sys_nice +# 35 was ftime +36 common sync sys_sync +37 common kill sys_kill +38 common rename sys_rename +39 common mkdir sys_mkdir +40 common rmdir sys_rmdir +41 common dup sys_dup +42 common pipe sys_sh_pipe +43 common times sys_times +# 44 was prof +45 common brk sys_brk +46 common setgid sys_setgid16 +47 common getgid sys_getgid16 +48 common signal sys_signal +49 common geteuid sys_geteuid16 +50 common getegid sys_getegid16 +51 common acct sys_acct +52 common umount2 sys_umount +# 53 was lock +54 common ioctl sys_ioctl +55 common fcntl sys_fcntl +# 56 was mpx +57 common setpgid sys_setpgid +# 58 was ulimit +# 59 was olduname +60 common umask sys_umask +61 common chroot sys_chroot +62 common ustat sys_ustat +63 common dup2 sys_dup2 +64 common getppid sys_getppid +65 common getpgrp sys_getpgrp +66 common setsid sys_setsid +67 common sigaction sys_sigaction +68 common sgetmask sys_sgetmask +69 common ssetmask sys_ssetmask +70 common setreuid sys_setreuid16 +71 common setregid sys_setregid16 +72 common sigsuspend sys_sigsuspend +73 common sigpending sys_sigpending +74 common sethostname sys_sethostname +75 common setrlimit sys_setrlimit +76 common getrlimit sys_old_getrlimit +77 common getrusage sys_getrusage +78 common gettimeofday sys_gettimeofday +79 common settimeofday sys_settimeofday +80 common getgroups sys_getgroups16 +81 common setgroups sys_setgroups16 +# 82 was select +83 common symlink sys_symlink +84 common oldlstat sys_lstat +85 common readlink sys_readlink +86 common uselib sys_uselib +87 common swapon sys_swapon +88 common reboot sys_reboot +89 common readdir sys_old_readdir +90 common mmap old_mmap +91 common munmap sys_munmap +92 common truncate sys_truncate +93 common ftruncate sys_ftruncate +94 common fchmod sys_fchmod +95 common fchown sys_fchown16 +96 common getpriority sys_getpriority +97 common setpriority sys_setpriority +# 98 was profil +99 common statfs sys_statfs +100 common fstatfs sys_fstatfs +# 101 was ioperm +102 common socketcall sys_socketcall +103 common syslog sys_syslog +104 common setitimer sys_setitimer +105 common getitimer sys_getitimer +106 common stat sys_newstat +107 common lstat sys_newlstat +108 common fstat sys_newfstat +109 common olduname sys_uname +# 110 was iopl +111 common vhangup sys_vhangup +# 112 was idle +# 113 was vm86old +114 common wait4 sys_wait4 +115 common swapoff sys_swapoff +116 common sysinfo sys_sysinfo +117 common ipc sys_ipc +118 common fsync sys_fsync +119 common sigreturn sys_sigreturn +120 common clone sys_clone +121 common setdomainname sys_setdomainname +122 common uname sys_newuname +123 common cacheflush sys_cacheflush +124 common adjtimex sys_adjtimex_time32 +125 common mprotect sys_mprotect +126 common sigprocmask sys_sigprocmask +# 127 was create_module +128 common init_module sys_init_module +129 common delete_module sys_delete_module +# 130 was get_kernel_syms +131 common quotactl sys_quotactl +132 common getpgid sys_getpgid +133 common fchdir sys_fchdir +134 common bdflush sys_bdflush +135 common sysfs sys_sysfs +136 common personality sys_personality +# 137 was afs_syscall +138 common setfsuid sys_setfsuid16 +139 common setfsgid sys_setfsgid16 +140 common _llseek sys_llseek +141 common getdents sys_getdents +142 common _newselect sys_select +143 common flock sys_flock +144 common msync sys_msync +145 common readv sys_readv +146 common writev sys_writev +147 common getsid sys_getsid +148 common fdatasync sys_fdatasync +149 common _sysctl sys_sysctl +150 common mlock sys_mlock +151 common munlock sys_munlock +152 common mlockall sys_mlockall +153 common munlockall sys_munlockall +154 common sched_setparam sys_sched_setparam +155 common sched_getparam sys_sched_getparam +156 common sched_setscheduler sys_sched_setscheduler +157 common sched_getscheduler sys_sched_getscheduler +158 common sched_yield sys_sched_yield +159 common sched_get_priority_max sys_sched_get_priority_max +160 common sched_get_priority_min sys_sched_get_priority_min +161 common sched_rr_get_interval sys_sched_rr_get_interval_time32 +162 common nanosleep sys_nanosleep_time32 +163 common mremap sys_mremap +164 common setresuid sys_setresuid16 +165 common getresuid sys_getresuid16 +# 166 was vm86 +# 167 was query_module +168 common poll sys_poll +169 common nfsservctl sys_ni_syscall +170 common setresgid sys_setresgid16 +171 common getresgid sys_getresgid16 +172 common prctl sys_prctl +173 common rt_sigreturn sys_rt_sigreturn +174 common rt_sigaction sys_rt_sigaction +175 common rt_sigprocmask sys_rt_sigprocmask +176 common rt_sigpending sys_rt_sigpending +177 common rt_sigtimedwait sys_rt_sigtimedwait_time32 +178 common rt_sigqueueinfo sys_rt_sigqueueinfo +179 common rt_sigsuspend sys_rt_sigsuspend +180 common pread64 sys_pread_wrapper +181 common pwrite64 sys_pwrite_wrapper +182 common chown sys_chown16 +183 common getcwd sys_getcwd +184 common capget sys_capget +185 common capset sys_capset +186 common sigaltstack sys_sigaltstack +187 common sendfile sys_sendfile +# 188 is reserved for getpmsg +# 189 is reserved for putpmsg +190 common vfork sys_vfork +191 common ugetrlimit sys_getrlimit +192 common mmap2 sys_mmap2 +193 common truncate64 sys_truncate64 +194 common ftruncate64 sys_ftruncate64 +195 common stat64 sys_stat64 +196 common lstat64 sys_lstat64 +197 common fstat64 sys_fstat64 +198 common lchown32 sys_lchown +199 common getuid32 sys_getuid +200 common getgid32 sys_getgid +201 common geteuid32 sys_geteuid +202 common getegid32 sys_getegid +203 common setreuid32 sys_setreuid +204 common setregid32 sys_setregid +205 common getgroups32 sys_getgroups +206 common setgroups32 sys_setgroups +207 common fchown32 sys_fchown +208 common setresuid32 sys_setresuid +209 common getresuid32 sys_getresuid +210 common setresgid32 sys_setresgid +211 common getresgid32 sys_getresgid +212 common chown32 sys_chown +213 common setuid32 sys_setuid +214 common setgid32 sys_setgid +215 common setfsuid32 sys_setfsuid +216 common setfsgid32 sys_setfsgid +217 common pivot_root sys_pivot_root +218 common mincore sys_mincore +219 common madvise sys_madvise +220 common getdents64 sys_getdents64 +221 common fcntl64 sys_fcntl64 +# 222 is reserved for tux +# 223 is unused +224 common gettid sys_gettid +225 common readahead sys_readahead +226 common setxattr sys_setxattr +227 common lsetxattr sys_lsetxattr +228 common fsetxattr sys_fsetxattr +229 common getxattr sys_getxattr +230 common lgetxattr sys_lgetxattr +231 common fgetxattr sys_fgetxattr +232 common listxattr sys_listxattr +233 common llistxattr sys_llistxattr +234 common flistxattr sys_flistxattr +235 common removexattr sys_removexattr +236 common lremovexattr sys_lremovexattr +237 common fremovexattr sys_fremovexattr +238 common tkill sys_tkill +239 common sendfile64 sys_sendfile64 +240 common futex sys_futex_time32 +241 common sched_setaffinity sys_sched_setaffinity +242 common sched_getaffinity sys_sched_getaffinity +# 243 is reserved for set_thread_area +# 244 is reserved for get_thread_area +245 common io_setup sys_io_setup +246 common io_destroy sys_io_destroy +247 common io_getevents sys_io_getevents_time32 +248 common io_submit sys_io_submit +249 common io_cancel sys_io_cancel +250 common fadvise64 sys_fadvise64 +# 251 is unused +252 common exit_group sys_exit_group +253 common lookup_dcookie sys_lookup_dcookie +254 common epoll_create sys_epoll_create +255 common epoll_ctl sys_epoll_ctl +256 common epoll_wait sys_epoll_wait +257 common remap_file_pages sys_remap_file_pages +258 common set_tid_address sys_set_tid_address +259 common timer_create sys_timer_create +260 common timer_settime sys_timer_settime32 +261 common timer_gettime sys_timer_gettime32 +262 common timer_getoverrun sys_timer_getoverrun +263 common timer_delete sys_timer_delete +264 common clock_settime sys_clock_settime32 +265 common clock_gettime sys_clock_gettime32 +266 common clock_getres sys_clock_getres_time32 +267 common clock_nanosleep sys_clock_nanosleep_time32 +268 common statfs64 sys_statfs64 +269 common fstatfs64 sys_fstatfs64 +270 common tgkill sys_tgkill +271 common utimes sys_utimes_time32 +272 common fadvise64_64 sys_fadvise64_64_wrapper +# 273 is reserved for vserver +274 common mbind sys_mbind +275 common get_mempolicy sys_get_mempolicy +276 common set_mempolicy sys_set_mempolicy +277 common mq_open sys_mq_open +278 common mq_unlink sys_mq_unlink +279 common mq_timedsend sys_mq_timedsend_time32 +280 common mq_timedreceive sys_mq_timedreceive_time32 +281 common mq_notify sys_mq_notify +282 common mq_getsetattr sys_mq_getsetattr +283 common kexec_load sys_kexec_load +284 common waitid sys_waitid +285 common add_key sys_add_key +286 common request_key sys_request_key +287 common keyctl sys_keyctl +288 common ioprio_set sys_ioprio_set +289 common ioprio_get sys_ioprio_get +290 common inotify_init sys_inotify_init +291 common inotify_add_watch sys_inotify_add_watch +292 common inotify_rm_watch sys_inotify_rm_watch +# 293 is unused +294 common migrate_pages sys_migrate_pages +295 common openat sys_openat +296 common mkdirat sys_mkdirat +297 common mknodat sys_mknodat +298 common fchownat sys_fchownat +299 common futimesat sys_futimesat_time32 +300 common fstatat64 sys_fstatat64 +301 common unlinkat sys_unlinkat +302 common renameat sys_renameat +303 common linkat sys_linkat +304 common symlinkat sys_symlinkat +305 common readlinkat sys_readlinkat +306 common fchmodat sys_fchmodat +307 common faccessat sys_faccessat +308 common pselect6 sys_pselect6_time32 +309 common ppoll sys_ppoll_time32 +310 common unshare sys_unshare +311 common set_robust_list sys_set_robust_list +312 common get_robust_list sys_get_robust_list +313 common splice sys_splice +314 common sync_file_range sys_sync_file_range +315 common tee sys_tee +316 common vmsplice sys_vmsplice +317 common move_pages sys_move_pages +318 common getcpu sys_getcpu +319 common epoll_pwait sys_epoll_pwait +320 common utimensat sys_utimensat_time32 +321 common signalfd sys_signalfd +322 common timerfd_create sys_timerfd_create +323 common eventfd sys_eventfd +324 common fallocate sys_fallocate +325 common timerfd_settime sys_timerfd_settime32 +326 common timerfd_gettime sys_timerfd_gettime32 +327 common signalfd4 sys_signalfd4 +328 common eventfd2 sys_eventfd2 +329 common epoll_create1 sys_epoll_create1 +330 common dup3 sys_dup3 +331 common pipe2 sys_pipe2 +332 common inotify_init1 sys_inotify_init1 +333 common preadv sys_preadv +334 common pwritev sys_pwritev +335 common rt_tgsigqueueinfo sys_rt_tgsigqueueinfo +336 common perf_event_open sys_perf_event_open +337 common fanotify_init sys_fanotify_init +338 common fanotify_mark sys_fanotify_mark +339 common prlimit64 sys_prlimit64 +340 common socket sys_socket +341 common bind sys_bind +342 common connect sys_connect +343 common listen sys_listen +344 common accept sys_accept +345 common getsockname sys_getsockname +346 common getpeername sys_getpeername +347 common socketpair sys_socketpair +348 common send sys_send +349 common sendto sys_sendto +350 common recv sys_recv +351 common recvfrom sys_recvfrom +352 common shutdown sys_shutdown +353 common setsockopt sys_setsockopt +354 common getsockopt sys_getsockopt +355 common sendmsg sys_sendmsg +356 common recvmsg sys_recvmsg +357 common recvmmsg sys_recvmmsg_time32 +358 common accept4 sys_accept4 +359 common name_to_handle_at sys_name_to_handle_at +360 common open_by_handle_at sys_open_by_handle_at +361 common clock_adjtime sys_clock_adjtime32 +362 common syncfs sys_syncfs +363 common sendmmsg sys_sendmmsg +364 common setns sys_setns +365 common process_vm_readv sys_process_vm_readv +366 common process_vm_writev sys_process_vm_writev +367 common kcmp sys_kcmp +368 common finit_module sys_finit_module +369 common sched_getattr sys_sched_getattr +370 common sched_setattr sys_sched_setattr +371 common renameat2 sys_renameat2 +372 common seccomp sys_seccomp +373 common getrandom sys_getrandom +374 common memfd_create sys_memfd_create +375 common bpf sys_bpf +376 common execveat sys_execveat +377 common userfaultfd sys_userfaultfd +378 common membarrier sys_membarrier +379 common mlock2 sys_mlock2 +380 common copy_file_range sys_copy_file_range +381 common preadv2 sys_preadv2 +382 common pwritev2 sys_pwritev2 +383 common statx sys_statx +384 common pkey_mprotect sys_pkey_mprotect +385 common pkey_alloc sys_pkey_alloc +386 common pkey_free sys_pkey_free +387 common rseq sys_rseq +# room for arch specific syscalls +393 common semget sys_semget +394 common semctl sys_semctl +395 common shmget sys_shmget +396 common shmctl sys_shmctl +397 common shmat sys_shmat +398 common shmdt sys_shmdt +399 common msgget sys_msgget +400 common msgsnd sys_msgsnd +401 common msgrcv sys_msgrcv +402 common msgctl sys_msgctl +403 common clock_gettime64 sys_clock_gettime +404 common clock_settime64 sys_clock_settime +405 common clock_adjtime64 sys_clock_adjtime +406 common clock_getres_time64 sys_clock_getres +407 common clock_nanosleep_time64 sys_clock_nanosleep +408 common timer_gettime64 sys_timer_gettime +409 common timer_settime64 sys_timer_settime +410 common timerfd_gettime64 sys_timerfd_gettime +411 common timerfd_settime64 sys_timerfd_settime +412 common utimensat_time64 sys_utimensat +413 common pselect6_time64 sys_pselect6 +414 common ppoll_time64 sys_ppoll +416 common io_pgetevents_time64 sys_io_pgetevents +417 common recvmmsg_time64 sys_recvmmsg +418 common mq_timedsend_time64 sys_mq_timedsend +419 common mq_timedreceive_time64 sys_mq_timedreceive +420 common semtimedop_time64 sys_semtimedop +421 common rt_sigtimedwait_time64 sys_rt_sigtimedwait +422 common futex_time64 sys_futex +423 common sched_rr_get_interval_time64 sys_sched_rr_get_interval +424 common pidfd_send_signal sys_pidfd_send_signal +425 common io_uring_setup sys_io_uring_setup +426 common io_uring_enter sys_io_uring_enter +427 common io_uring_register sys_io_uring_register +428 common open_tree sys_open_tree +429 common move_mount sys_move_mount +430 common fsopen sys_fsopen +431 common fsconfig sys_fsconfig +432 common fsmount sys_fsmount +433 common fspick sys_fspick +434 common pidfd_open sys_pidfd_open +# 435 reserved for clone3 +437 common openat2 sys_openat2 +438 common pidfd_getfd sys_pidfd_getfd diff --git a/linux-user/sh4/syscall_nr.h b/linux-user/sh4/syscall_nr.h deleted file mode 100644 index d53a2a07dd..0000000000 --- a/linux-user/sh4/syscall_nr.h +++ /dev/null @@ -1,393 +0,0 @@ -/* - * This file contains the system call numbers. - */ - -#ifndef LINUX_USER_SH4_SYSCALL_NR_H -#define LINUX_USER_SH4_SYSCALL_NR_H - -#define TARGET_NR_restart_syscall 0 -#define TARGET_NR_exit 1 -#define TARGET_NR_fork 2 -#define TARGET_NR_read 3 -#define TARGET_NR_write 4 -#define TARGET_NR_open 5 -#define TARGET_NR_close 6 -#define TARGET_NR_waitpid 7 -#define TARGET_NR_creat 8 -#define TARGET_NR_link 9 -#define TARGET_NR_unlink 10 -#define TARGET_NR_execve 11 -#define TARGET_NR_chdir 12 -#define TARGET_NR_time 13 -#define TARGET_NR_mknod 14 -#define TARGET_NR_chmod 15 -#define TARGET_NR_lchown 16 -#define TARGET_NR_break 17 -#define TARGET_NR_oldstat 18 -#define TARGET_NR_lseek 19 -#define TARGET_NR_getpid 20 -#define TARGET_NR_mount 21 -#define TARGET_NR_umount 22 -#define TARGET_NR_setuid 23 -#define TARGET_NR_getuid 24 -#define TARGET_NR_stime 25 -#define TARGET_NR_ptrace 26 -#define TARGET_NR_alarm 27 -#define TARGET_NR_oldfstat 28 -#define TARGET_NR_pause 29 -#define TARGET_NR_utime 30 -#define TARGET_NR_stty 31 -#define TARGET_NR_gtty 32 -#define TARGET_NR_access 33 -#define TARGET_NR_nice 34 -#define TARGET_NR_ftime 35 -#define TARGET_NR_sync 36 -#define TARGET_NR_kill 37 -#define TARGET_NR_rename 38 -#define TARGET_NR_mkdir 39 -#define TARGET_NR_rmdir 40 -#define TARGET_NR_dup 41 -#define TARGET_NR_pipe 42 -#define TARGET_NR_times 43 -#define TARGET_NR_prof 44 -#define TARGET_NR_brk 45 -#define TARGET_NR_setgid 46 -#define TARGET_NR_getgid 47 -#define TARGET_NR_signal 48 -#define TARGET_NR_geteuid 49 -#define TARGET_NR_getegid 50 -#define TARGET_NR_acct 51 -#define TARGET_NR_umount2 52 -#define TARGET_NR_lock 53 -#define TARGET_NR_ioctl 54 -#define TARGET_NR_fcntl 55 -#define TARGET_NR_mpx 56 -#define TARGET_NR_setpgid 57 -#define TARGET_NR_ulimit 58 -#define TARGET_NR_oldolduname 59 -#define TARGET_NR_umask 60 -#define TARGET_NR_chroot 61 -#define TARGET_NR_ustat 62 -#define TARGET_NR_dup2 63 -#define TARGET_NR_getppid 64 -#define TARGET_NR_getpgrp 65 -#define TARGET_NR_setsid 66 -#define TARGET_NR_sigaction 67 -#define TARGET_NR_sgetmask 68 -#define TARGET_NR_ssetmask 69 -#define TARGET_NR_setreuid 70 -#define TARGET_NR_setregid 71 -#define TARGET_NR_sigsuspend 72 -#define TARGET_NR_sigpending 73 -#define TARGET_NR_sethostname 74 -#define TARGET_NR_setrlimit 75 -#define TARGET_NR_getrlimit 76 /* Back compatible 2Gig limited rlimit */ -#define TARGET_NR_getrusage 77 -#define TARGET_NR_gettimeofday 78 -#define TARGET_NR_settimeofday 79 -#define TARGET_NR_getgroups 80 -#define TARGET_NR_setgroups 81 - /* 82 was sys_oldselect */ -#define TARGET_NR_symlink 83 -#define TARGET_NR_oldlstat 84 -#define TARGET_NR_readlink 85 -#define TARGET_NR_uselib 86 -#define TARGET_NR_swapon 87 -#define TARGET_NR_reboot 88 -#define TARGET_NR_readdir 89 -#define TARGET_NR_mmap 90 -#define TARGET_NR_munmap 91 -#define TARGET_NR_truncate 92 -#define TARGET_NR_ftruncate 93 -#define TARGET_NR_fchmod 94 -#define TARGET_NR_fchown 95 -#define TARGET_NR_getpriority 96 -#define TARGET_NR_setpriority 97 -#define TARGET_NR_profil 98 -#define TARGET_NR_statfs 99 -#define TARGET_NR_fstatfs 100 -#define TARGET_NR_ioperm 101 -#define TARGET_NR_socketcall 102 -#define TARGET_NR_syslog 103 -#define TARGET_NR_setitimer 104 -#define TARGET_NR_getitimer 105 -#define TARGET_NR_stat 106 -#define TARGET_NR_lstat 107 -#define TARGET_NR_fstat 108 -#define TARGET_NR_olduname 109 -#define TARGET_NR_iopl 110 -#define TARGET_NR_vhangup 111 -#define TARGET_NR_idle 112 -#define TARGET_NR_vm86old 113 -#define TARGET_NR_wait4 114 -#define TARGET_NR_swapoff 115 -#define TARGET_NR_sysinfo 116 -#define TARGET_NR_ipc 117 -#define TARGET_NR_fsync 118 -#define TARGET_NR_sigreturn 119 -#define TARGET_NR_clone 120 -#define TARGET_NR_setdomainname 121 -#define TARGET_NR_uname 122 -#define TARGET_NR_cacheflush 123 -#define TARGET_NR_adjtimex 124 -#define TARGET_NR_mprotect 125 -#define TARGET_NR_sigprocmask 126 -#define TARGET_NR_create_module 127 -#define TARGET_NR_init_module 128 -#define TARGET_NR_delete_module 129 -#define TARGET_NR_get_kernel_syms 130 -#define TARGET_NR_quotactl 131 -#define TARGET_NR_getpgid 132 -#define TARGET_NR_fchdir 133 -#define TARGET_NR_bdflush 134 -#define TARGET_NR_sysfs 135 -#define TARGET_NR_personality 136 -#define TARGET_NR_afs_syscall 137 /* Syscall for Andrew File System */ -#define TARGET_NR_setfsuid 138 -#define TARGET_NR_setfsgid 139 -#define TARGET_NR__llseek 140 -#define TARGET_NR_getdents 141 -#define TARGET_NR__newselect 142 -#define TARGET_NR_flock 143 -#define TARGET_NR_msync 144 -#define TARGET_NR_readv 145 -#define TARGET_NR_writev 146 -#define TARGET_NR_getsid 147 -#define TARGET_NR_fdatasync 148 -#define TARGET_NR__sysctl 149 -#define TARGET_NR_mlock 150 -#define TARGET_NR_munlock 151 -#define TARGET_NR_mlockall 152 -#define TARGET_NR_munlockall 153 -#define TARGET_NR_sched_setparam 154 -#define TARGET_NR_sched_getparam 155 -#define TARGET_NR_sched_setscheduler 156 -#define TARGET_NR_sched_getscheduler 157 -#define TARGET_NR_sched_yield 158 -#define TARGET_NR_sched_get_priority_max 159 -#define TARGET_NR_sched_get_priority_min 160 -#define TARGET_NR_sched_rr_get_interval 161 -#define TARGET_NR_nanosleep 162 -#define TARGET_NR_mremap 163 -#define TARGET_NR_setresuid 164 -#define TARGET_NR_getresuid 165 -#define TARGET_NR_vm86 166 -#define TARGET_NR_query_module 167 -#define TARGET_NR_poll 168 -#define TARGET_NR_nfsservctl 169 -#define TARGET_NR_setresgid 170 -#define TARGET_NR_getresgid 171 -#define TARGET_NR_prctl 172 -#define TARGET_NR_rt_sigreturn 173 -#define TARGET_NR_rt_sigaction 174 -#define TARGET_NR_rt_sigprocmask 175 -#define TARGET_NR_rt_sigpending 176 -#define TARGET_NR_rt_sigtimedwait 177 -#define TARGET_NR_rt_sigqueueinfo 178 -#define TARGET_NR_rt_sigsuspend 179 -#define TARGET_NR_pread64 180 -#define TARGET_NR_pwrite64 181 -#define TARGET_NR_chown 182 -#define TARGET_NR_getcwd 183 -#define TARGET_NR_capget 184 -#define TARGET_NR_capset 185 -#define TARGET_NR_sigaltstack 186 -#define TARGET_NR_sendfile 187 -#define TARGET_NR_streams1 188 /* some people actually want it */ -#define TARGET_NR_streams2 189 /* some people actually want it */ -#define TARGET_NR_vfork 190 -#define TARGET_NR_ugetrlimit 191 /* SuS compliant getrlimit */ -#define TARGET_NR_mmap2 192 -#define TARGET_NR_truncate64 193 -#define TARGET_NR_ftruncate64 194 -#define TARGET_NR_stat64 195 -#define TARGET_NR_lstat64 196 -#define TARGET_NR_fstat64 197 -#define TARGET_NR_lchown32 198 -#define TARGET_NR_getuid32 199 -#define TARGET_NR_getgid32 200 -#define TARGET_NR_geteuid32 201 -#define TARGET_NR_getegid32 202 -#define TARGET_NR_setreuid32 203 -#define TARGET_NR_setregid32 204 -#define TARGET_NR_getgroups32 205 -#define TARGET_NR_setgroups32 206 -#define TARGET_NR_fchown32 207 -#define TARGET_NR_setresuid32 208 -#define TARGET_NR_getresuid32 209 -#define TARGET_NR_setresgid32 210 -#define TARGET_NR_getresgid32 211 -#define TARGET_NR_chown32 212 -#define TARGET_NR_setuid32 213 -#define TARGET_NR_setgid32 214 -#define TARGET_NR_setfsuid32 215 -#define TARGET_NR_setfsgid32 216 -#define TARGET_NR_pivot_root 217 -#define TARGET_NR_mincore 218 -#define TARGET_NR_madvise 219 -#define TARGET_NR_getdents64 220 -#define TARGET_NR_fcntl64 221 -/* 223 is unused */ -#define TARGET_NR_gettid 224 -#define TARGET_NR_readahead 225 -#define TARGET_NR_setxattr 226 -#define TARGET_NR_lsetxattr 227 -#define TARGET_NR_fsetxattr 228 -#define TARGET_NR_getxattr 229 -#define TARGET_NR_lgetxattr 230 -#define TARGET_NR_fgetxattr 231 -#define TARGET_NR_listxattr 232 -#define TARGET_NR_llistxattr 233 -#define TARGET_NR_flistxattr 234 -#define TARGET_NR_removexattr 235 -#define TARGET_NR_lremovexattr 236 -#define TARGET_NR_fremovexattr 237 -#define TARGET_NR_tkill 238 -#define TARGET_NR_sendfile64 239 -#define TARGET_NR_futex 240 -#define TARGET_NR_sched_setaffinity 241 -#define TARGET_NR_sched_getaffinity 242 -#define TARGET_NR_set_thread_area 243 -#define TARGET_NR_get_thread_area 244 -#define TARGET_NR_io_setup 245 -#define TARGET_NR_io_destroy 246 -#define TARGET_NR_io_getevents 247 -#define TARGET_NR_io_submit 248 -#define TARGET_NR_io_cancel 249 -#define TARGET_NR_fadvise64 250 - -#define TARGET_NR_exit_group 252 -#define TARGET_NR_lookup_dcookie 253 -#define TARGET_NR_epoll_create 254 -#define TARGET_NR_epoll_ctl 255 -#define TARGET_NR_epoll_wait 256 -#define TARGET_NR_remap_file_pages 257 -#define TARGET_NR_set_tid_address 258 -#define TARGET_NR_timer_create 259 -#define TARGET_NR_timer_settime (TARGET_NR_timer_create+1) -#define TARGET_NR_timer_gettime (TARGET_NR_timer_create+2) -#define TARGET_NR_timer_getoverrun (TARGET_NR_timer_create+3) -#define TARGET_NR_timer_delete (TARGET_NR_timer_create+4) -#define TARGET_NR_clock_settime (TARGET_NR_timer_create+5) -#define TARGET_NR_clock_gettime (TARGET_NR_timer_create+6) -#define TARGET_NR_clock_getres (TARGET_NR_timer_create+7) -#define TARGET_NR_clock_nanosleep (TARGET_NR_timer_create+8) -#define TARGET_NR_statfs64 268 -#define TARGET_NR_fstatfs64 269 -#define TARGET_NR_tgkill 270 -#define TARGET_NR_utimes 271 -#define TARGET_NR_fadvise64_64 272 -#define TARGET_NR_vserver 273 -#define TARGET_NR_mbind 274 -#define TARGET_NR_get_mempolicy 275 -#define TARGET_NR_set_mempolicy 276 -#define TARGET_NR_mq_open 277 -#define TARGET_NR_mq_unlink (TARGET_NR_mq_open+1) -#define TARGET_NR_mq_timedsend (TARGET_NR_mq_open+2) -#define TARGET_NR_mq_timedreceive (TARGET_NR_mq_open+3) -#define TARGET_NR_mq_notify (TARGET_NR_mq_open+4) -#define TARGET_NR_mq_getsetattr (TARGET_NR_mq_open+5) -#define TARGET_NR_sys_kexec_load 283 -#define TARGET_NR_waitid 284 -#define TARGET_NR_add_key 285 -#define TARGET_NR_request_key 286 -#define TARGET_NR_keyctl 287 -#define TARGET_NR_ioprio_set 288 -#define TARGET_NR_ioprio_get 289 -#define TARGET_NR_inotify_init 290 -#define TARGET_NR_inotify_add_watch 291 -#define TARGET_NR_inotify_rm_watch 292 -/* 293 is unused */ -#define TARGET_NR_migrate_pages 294 -#define TARGET_NR_openat 295 -#define TARGET_NR_mkdirat 296 -#define TARGET_NR_mknodat 297 -#define TARGET_NR_fchownat 298 -#define TARGET_NR_futimesat 299 -#define TARGET_NR_fstatat64 300 -#define TARGET_NR_unlinkat 301 -#define TARGET_NR_renameat 302 -#define TARGET_NR_linkat 303 -#define TARGET_NR_symlinkat 304 -#define TARGET_NR_readlinkat 305 -#define TARGET_NR_fchmodat 306 -#define TARGET_NR_faccessat 307 -#define TARGET_NR_pselect6 308 -#define TARGET_NR_ppoll 309 -#define TARGET_NR_unshare 310 -#define TARGET_NR_set_robust_list 311 -#define TARGET_NR_get_robust_list 312 -#define TARGET_NR_splice 313 -#define TARGET_NR_sync_file_range 314 -#define TARGET_NR_tee 315 -#define TARGET_NR_vmsplice 316 -#define TARGET_NR_move_pages 317 -#define TARGET_NR_getcpu 318 -#define TARGET_NR_epoll_pwait 319 -#define TARGET_NR_utimensat 320 -#define TARGET_NR_signalfd 321 -#define TARGET_NR_timerfd_create 322 -#define TARGET_NR_eventfd 323 -#define TARGET_NR_fallocate 324 -#define TARGET_NR_timerfd_settime 325 -#define TARGET_NR_timerfd_gettime 326 -#define TARGET_NR_signalfd4 327 -#define TARGET_NR_eventfd2 328 -#define TARGET_NR_epoll_create1 329 -#define TARGET_NR_dup3 330 -#define TARGET_NR_pipe2 331 -#define TARGET_NR_inotify_init1 332 -#define TARGET_NR_preadv 333 -#define TARGET_NR_pwritev 334 -#define TARGET_NR_rt_tgsigqueueinfo 335 -#define TARGET_NR_perf_event_open 336 -#define TARGET_NR_fanotify_init 337 -#define TARGET_NR_fanotify_mark 338 -#define TARGET_NR_prlimit64 339 - -/* Non-multiplexed socket family */ -#define TARGET_NR_socket 340 -#define TARGET_NR_bind 341 -#define TARGET_NR_connect 342 -#define TARGET_NR_listen 343 -#define TARGET_NR_accept 344 -#define TARGET_NR_getsockname 345 -#define TARGET_NR_getpeername 346 -#define TARGET_NR_socketpair 347 -#define TARGET_NR_send 348 -#define TARGET_NR_sendto 349 -#define TARGET_NR_recv 350 -#define TARGET_NR_recvfrom 351 -#define TARGET_NR_shutdown 352 -#define TARGET_NR_setsockopt 353 -#define TARGET_NR_getsockopt 354 -#define TARGET_NR_sendmsg 355 -#define TARGET_NR_recvmsg 356 -#define TARGET_NR_recvmmsg 357 -#define TARGET_NR_accept4 358 -#define TARGET_NR_name_to_handle_at 359 -#define TARGET_NR_open_by_handle_at 360 -#define TARGET_NR_clock_adjtime 361 -#define TARGET_NR_syncfs 362 -#define TARGET_NR_sendmmsg 363 -#define TARGET_NR_setns 364 -#define TARGET_NR_process_vm_readv 365 -#define TARGET_NR_process_vm_writev 366 -#define TARGET_NR_kcmp 367 -#define TARGET_NR_finit_module 368 -#define TARGET_NR_sched_getattr 369 -#define TARGET_NR_sched_setattr 370 -#define TARGET_NR_renameat2 371 -#define TARGET_NR_seccomp 372 -#define TARGET_NR_getrandom 373 -#define TARGET_NR_memfd_create 374 -#define TARGET_NR_bpf 375 -#define TARGET_NR_execveat 376 -#define TARGET_NR_userfaultfd 377 -#define TARGET_NR_membarrier 378 -#define TARGET_NR_mlock2 379 -#define TARGET_NR_copy_file_range 380 -#define TARGET_NR_preadv2 381 -#define TARGET_NR_pwritev2 382 - -#endif diff --git a/linux-user/sh4/syscallhdr.sh b/linux-user/sh4/syscallhdr.sh new file mode 100644 index 0000000000..080790556a --- /dev/null +++ b/linux-user/sh4/syscallhdr.sh @@ -0,0 +1,32 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 + +in="$1" +out="$2" +my_abis=`echo "($3)" | tr ',' '|'` +prefix="$4" +offset="$5" + +fileguard=LINUX_USER_SH4_`basename "$out" | sed \ + -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/' \ + -e 's/[^A-Z0-9_]/_/g' -e 's/__/_/g'` +grep -E "^[0-9A-Fa-fXx]+[[:space:]]+${my_abis}" "$in" | sort -n | ( + printf "#ifndef %s\n" "${fileguard}" + printf "#define %s\n" "${fileguard}" + printf "\n" + + nxt=0 + while read nr abi name entry ; do + if [ -z "$offset" ]; then + printf "#define TARGET_NR_%s%s\t%s\n" \ + "${prefix}" "${name}" "${nr}" + else + printf "#define TARGET_NR_%s%s\t(%s + %s)\n" \ + "${prefix}" "${name}" "${offset}" "${nr}" + fi + nxt=$((nr+1)) + done + + printf "\n" + printf "#endif /* %s */" "${fileguard}" +) > "$out" diff --git a/linux-user/signal.c b/linux-user/signal.c index 5ca6d62b15..8cf51ffecd 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -30,6 +30,15 @@ static struct target_sigaction sigact_table[TARGET_NSIG]; static void host_signal_handler(int host_signum, siginfo_t *info, void *puc); + +/* + * System includes define _NSIG as SIGRTMAX + 1, + * but qemu (like the kernel) defines TARGET_NSIG as TARGET_SIGRTMAX + * and the first signal is SIGHUP defined as 1 + * Signal number 0 is reserved for use as kill(pid, 0), to test whether + * a process exists without sending it a signal. + */ +QEMU_BUILD_BUG_ON(__SIGRTMAX + 1 != _NSIG); static uint8_t host_to_target_signal_table[_NSIG] = { [SIGHUP] = TARGET_SIGHUP, [SIGINT] = TARGET_SIGINT, @@ -66,26 +75,25 @@ static uint8_t host_to_target_signal_table[_NSIG] = { [SIGPWR] = TARGET_SIGPWR, [SIGSYS] = TARGET_SIGSYS, /* next signals stay the same */ - /* Nasty hack: Reverse SIGRTMIN and SIGRTMAX to avoid overlap with - host libpthread signals. This assumes no one actually uses SIGRTMAX :-/ - To fix this properly we need to do manual signal delivery multiplexed - over a single host signal. */ - [__SIGRTMIN] = __SIGRTMAX, - [__SIGRTMAX] = __SIGRTMIN, }; -static uint8_t target_to_host_signal_table[_NSIG]; +static uint8_t target_to_host_signal_table[TARGET_NSIG + 1]; + +/* valid sig is between 1 and _NSIG - 1 */ int host_to_target_signal(int sig) { - if (sig < 0 || sig >= _NSIG) + if (sig < 1 || sig >= _NSIG) { return sig; + } return host_to_target_signal_table[sig]; } +/* valid sig is between 1 and TARGET_NSIG */ int target_to_host_signal(int sig) { - if (sig < 0 || sig >= _NSIG) + if (sig < 1 || sig > TARGET_NSIG) { return sig; + } return target_to_host_signal_table[sig]; } @@ -106,11 +114,15 @@ static inline int target_sigismember(const target_sigset_t *set, int signum) void host_to_target_sigset_internal(target_sigset_t *d, const sigset_t *s) { - int i; + int host_sig, target_sig; target_sigemptyset(d); - for (i = 1; i <= TARGET_NSIG; i++) { - if (sigismember(s, i)) { - target_sigaddset(d, host_to_target_signal(i)); + for (host_sig = 1; host_sig < _NSIG; host_sig++) { + target_sig = host_to_target_signal(host_sig); + if (target_sig < 1 || target_sig > TARGET_NSIG) { + continue; + } + if (sigismember(s, host_sig)) { + target_sigaddset(d, target_sig); } } } @@ -128,11 +140,15 @@ void host_to_target_sigset(target_sigset_t *d, const sigset_t *s) void target_to_host_sigset_internal(sigset_t *d, const target_sigset_t *s) { - int i; + int host_sig, target_sig; sigemptyset(d); - for (i = 1; i <= TARGET_NSIG; i++) { - if (target_sigismember(s, i)) { - sigaddset(d, target_to_host_signal(i)); + for (target_sig = 1; target_sig <= TARGET_NSIG; target_sig++) { + host_sig = target_to_host_signal(target_sig); + if (host_sig < 1 || host_sig >= _NSIG) { + continue; + } + if (target_sigismember(s, target_sig)) { + sigaddset(d, host_sig); } } } @@ -480,37 +496,72 @@ static int core_dump_signal(int sig) } } +static void signal_table_init(void) +{ + int host_sig, target_sig, count; + + /* + * Signals are supported starting from TARGET_SIGRTMIN and going up + * until we run out of host realtime signals. + * glibc at least uses only the lower 2 rt signals and probably + * nobody's using the upper ones. + * it's why SIGRTMIN (34) is generally greater than __SIGRTMIN (32) + * To fix this properly we need to do manual signal delivery multiplexed + * over a single host signal. + * Attempts for configure "missing" signals via sigaction will be + * silently ignored. + */ + for (host_sig = SIGRTMIN; host_sig <= SIGRTMAX; host_sig++) { + target_sig = host_sig - SIGRTMIN + TARGET_SIGRTMIN; + if (target_sig <= TARGET_NSIG) { + host_to_target_signal_table[host_sig] = target_sig; + } + } + + /* generate signal conversion tables */ + for (target_sig = 1; target_sig <= TARGET_NSIG; target_sig++) { + target_to_host_signal_table[target_sig] = _NSIG; /* poison */ + } + for (host_sig = 1; host_sig < _NSIG; host_sig++) { + if (host_to_target_signal_table[host_sig] == 0) { + host_to_target_signal_table[host_sig] = host_sig; + } + target_sig = host_to_target_signal_table[host_sig]; + if (target_sig <= TARGET_NSIG) { + target_to_host_signal_table[target_sig] = host_sig; + } + } + + if (trace_event_get_state_backends(TRACE_SIGNAL_TABLE_INIT)) { + for (target_sig = 1, count = 0; target_sig <= TARGET_NSIG; target_sig++) { + if (target_to_host_signal_table[target_sig] == _NSIG) { + count++; + } + } + trace_signal_table_init(count); + } +} + void signal_init(void) { TaskState *ts = (TaskState *)thread_cpu->opaque; struct sigaction act; struct sigaction oact; - int i, j; + int i; int host_sig; - /* generate signal conversion tables */ - for(i = 1; i < _NSIG; i++) { - if (host_to_target_signal_table[i] == 0) - host_to_target_signal_table[i] = i; - } - for(i = 1; i < _NSIG; i++) { - j = host_to_target_signal_table[i]; - target_to_host_signal_table[j] = i; - } + /* initialize signal conversion tables */ + signal_table_init(); /* Set the signal mask from the host mask. */ sigprocmask(0, 0, &ts->signal_mask); - /* set all host signal handlers. ALL signals are blocked during - the handlers to serialize them. */ - memset(sigact_table, 0, sizeof(sigact_table)); - sigfillset(&act.sa_mask); act.sa_flags = SA_SIGINFO; act.sa_sigaction = host_signal_handler; for(i = 1; i <= TARGET_NSIG; i++) { -#ifdef TARGET_GPROF - if (i == SIGPROF) { +#ifdef CONFIG_GPROF + if (i == TARGET_SIGPROF) { continue; } #endif @@ -787,6 +838,8 @@ int do_sigaction(int sig, const struct target_sigaction *act, int host_sig; int ret = 0; + trace_signal_do_sigaction_guest(sig, TARGET_NSIG); + if (sig < 1 || sig > TARGET_NSIG || sig == TARGET_SIGKILL || sig == TARGET_SIGSTOP) { return -TARGET_EINVAL; } @@ -817,6 +870,23 @@ int do_sigaction(int sig, const struct target_sigaction *act, /* we update the host linux signal state */ host_sig = target_to_host_signal(sig); + trace_signal_do_sigaction_host(host_sig, TARGET_NSIG); + if (host_sig > SIGRTMAX) { + /* we don't have enough host signals to map all target signals */ + qemu_log_mask(LOG_UNIMP, "Unsupported target signal #%d, ignored\n", + sig); + /* + * we don't return an error here because some programs try to + * register an handler for all possible rt signals even if they + * don't need it. + * An error here can abort them whereas there can be no problem + * to not have the signal available later. + * This is the case for golang, + * See https://github.com/golang/go/issues/33746 + * So we silently ignore the error. + */ + return 0; + } if (host_sig != SIGSEGV && host_sig != SIGBUS) { sigfillset(&act1.sa_mask); act1.sa_flags = SA_SIGINFO; @@ -864,7 +934,7 @@ static void handle_pending_signal(CPUArchState *cpu_env, int sig, handler = sa->_sa_handler; } - if (do_strace) { + if (unlikely(qemu_loglevel_mask(LOG_STRACE))) { print_taken_signal(sig, &k->info); } diff --git a/linux-user/sparc/Makefile.objs b/linux-user/sparc/Makefile.objs new file mode 100644 index 0000000000..29d3f066cb --- /dev/null +++ b/linux-user/sparc/Makefile.objs @@ -0,0 +1,5 @@ +generated-files-y += linux-user/sparc/syscall_nr.h + +syshdr := $(SRC_PATH)/linux-user/sparc/syscallhdr.sh +%/syscall_nr.h: $(SRC_PATH)/linux-user/sparc/syscall.tbl $(syshdr) + $(call quiet-command, sh $(syshdr) $< $@ $(TARGET_SYSTBL_ABI),"GEN","$@") diff --git a/linux-user/sparc/syscall.tbl b/linux-user/sparc/syscall.tbl new file mode 100644 index 0000000000..f13615ecde --- /dev/null +++ b/linux-user/sparc/syscall.tbl @@ -0,0 +1,485 @@ +# SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note +# +# system call numbers and entry vectors for sparc +# +# The format is: +# +# +# The can be common, 64, or 32 for this file. +# +0 common restart_syscall sys_restart_syscall +1 32 exit sys_exit sparc_exit +1 64 exit sparc_exit +2 common fork sys_fork +3 common read sys_read +4 common write sys_write +5 common open sys_open compat_sys_open +6 common close sys_close +7 common wait4 sys_wait4 compat_sys_wait4 +8 common creat sys_creat +9 common link sys_link +10 common unlink sys_unlink +11 32 execv sunos_execv +11 64 execv sys_nis_syscall +12 common chdir sys_chdir +13 32 chown sys_chown16 +13 64 chown sys_chown +14 common mknod sys_mknod +15 common chmod sys_chmod +16 32 lchown sys_lchown16 +16 64 lchown sys_lchown +17 common brk sys_brk +18 common perfctr sys_nis_syscall +19 common lseek sys_lseek compat_sys_lseek +20 common getpid sys_getpid +21 common capget sys_capget +22 common capset sys_capset +23 32 setuid sys_setuid16 +23 64 setuid sys_setuid +24 32 getuid sys_getuid16 +24 64 getuid sys_getuid +25 common vmsplice sys_vmsplice compat_sys_vmsplice +26 common ptrace sys_ptrace compat_sys_ptrace +27 common alarm sys_alarm +28 common sigaltstack sys_sigaltstack compat_sys_sigaltstack +29 32 pause sys_pause +29 64 pause sys_nis_syscall +30 32 utime sys_utime32 +30 64 utime sys_utime +31 32 lchown32 sys_lchown +32 32 fchown32 sys_fchown +33 common access sys_access +34 common nice sys_nice +35 32 chown32 sys_chown +36 common sync sys_sync +37 common kill sys_kill +38 common stat sys_newstat compat_sys_newstat +39 32 sendfile sys_sendfile compat_sys_sendfile +39 64 sendfile sys_sendfile64 +40 common lstat sys_newlstat compat_sys_newlstat +41 common dup sys_dup +42 common pipe sys_sparc_pipe +43 common times sys_times compat_sys_times +44 32 getuid32 sys_getuid +45 common umount2 sys_umount +46 32 setgid sys_setgid16 +46 64 setgid sys_setgid +47 32 getgid sys_getgid16 +47 64 getgid sys_getgid +48 common signal sys_signal +49 32 geteuid sys_geteuid16 +49 64 geteuid sys_geteuid +50 32 getegid sys_getegid16 +50 64 getegid sys_getegid +51 common acct sys_acct +52 64 memory_ordering sys_memory_ordering +53 32 getgid32 sys_getgid +54 common ioctl sys_ioctl compat_sys_ioctl +55 common reboot sys_reboot +56 32 mmap2 sys_mmap2 sys32_mmap2 +57 common symlink sys_symlink +58 common readlink sys_readlink +59 32 execve sys_execve sys32_execve +59 64 execve sys64_execve +60 common umask sys_umask +61 common chroot sys_chroot +62 common fstat sys_newfstat compat_sys_newfstat +63 common fstat64 sys_fstat64 compat_sys_fstat64 +64 common getpagesize sys_getpagesize +65 common msync sys_msync +66 common vfork sys_vfork +67 common pread64 sys_pread64 compat_sys_pread64 +68 common pwrite64 sys_pwrite64 compat_sys_pwrite64 +69 32 geteuid32 sys_geteuid +70 32 getegid32 sys_getegid +71 common mmap sys_mmap +72 32 setreuid32 sys_setreuid +73 32 munmap sys_munmap +73 64 munmap sys_64_munmap +74 common mprotect sys_mprotect +75 common madvise sys_madvise +76 common vhangup sys_vhangup +77 32 truncate64 sys_truncate64 compat_sys_truncate64 +78 common mincore sys_mincore +79 32 getgroups sys_getgroups16 +79 64 getgroups sys_getgroups +80 32 setgroups sys_setgroups16 +80 64 setgroups sys_setgroups +81 common getpgrp sys_getpgrp +82 32 setgroups32 sys_setgroups +83 common setitimer sys_setitimer compat_sys_setitimer +84 32 ftruncate64 sys_ftruncate64 compat_sys_ftruncate64 +85 common swapon sys_swapon +86 common getitimer sys_getitimer compat_sys_getitimer +87 32 setuid32 sys_setuid +88 common sethostname sys_sethostname +89 32 setgid32 sys_setgid +90 common dup2 sys_dup2 +91 32 setfsuid32 sys_setfsuid +92 common fcntl sys_fcntl compat_sys_fcntl +93 common select sys_select +94 32 setfsgid32 sys_setfsgid +95 common fsync sys_fsync +96 common setpriority sys_setpriority +97 common socket sys_socket +98 common connect sys_connect +99 common accept sys_accept +100 common getpriority sys_getpriority +101 common rt_sigreturn sys_rt_sigreturn sys32_rt_sigreturn +102 common rt_sigaction sys_rt_sigaction compat_sys_rt_sigaction +103 common rt_sigprocmask sys_rt_sigprocmask compat_sys_rt_sigprocmask +104 common rt_sigpending sys_rt_sigpending compat_sys_rt_sigpending +105 32 rt_sigtimedwait sys_rt_sigtimedwait_time32 compat_sys_rt_sigtimedwait_time32 +105 64 rt_sigtimedwait sys_rt_sigtimedwait +106 common rt_sigqueueinfo sys_rt_sigqueueinfo compat_sys_rt_sigqueueinfo +107 common rt_sigsuspend sys_rt_sigsuspend compat_sys_rt_sigsuspend +108 32 setresuid32 sys_setresuid +108 64 setresuid sys_setresuid +109 32 getresuid32 sys_getresuid +109 64 getresuid sys_getresuid +110 32 setresgid32 sys_setresgid +110 64 setresgid sys_setresgid +111 32 getresgid32 sys_getresgid +111 64 getresgid sys_getresgid +112 32 setregid32 sys_setregid +113 common recvmsg sys_recvmsg compat_sys_recvmsg +114 common sendmsg sys_sendmsg compat_sys_sendmsg +115 32 getgroups32 sys_getgroups +116 common gettimeofday sys_gettimeofday compat_sys_gettimeofday +117 common getrusage sys_getrusage compat_sys_getrusage +118 common getsockopt sys_getsockopt compat_sys_getsockopt +119 common getcwd sys_getcwd +120 common readv sys_readv compat_sys_readv +121 common writev sys_writev compat_sys_writev +122 common settimeofday sys_settimeofday compat_sys_settimeofday +123 32 fchown sys_fchown16 +123 64 fchown sys_fchown +124 common fchmod sys_fchmod +125 common recvfrom sys_recvfrom +126 32 setreuid sys_setreuid16 +126 64 setreuid sys_setreuid +127 32 setregid sys_setregid16 +127 64 setregid sys_setregid +128 common rename sys_rename +129 common truncate sys_truncate compat_sys_truncate +130 common ftruncate sys_ftruncate compat_sys_ftruncate +131 common flock sys_flock +132 common lstat64 sys_lstat64 compat_sys_lstat64 +133 common sendto sys_sendto +134 common shutdown sys_shutdown +135 common socketpair sys_socketpair +136 common mkdir sys_mkdir +137 common rmdir sys_rmdir +138 32 utimes sys_utimes_time32 +138 64 utimes sys_utimes +139 common stat64 sys_stat64 compat_sys_stat64 +140 common sendfile64 sys_sendfile64 +141 common getpeername sys_getpeername +142 32 futex sys_futex_time32 +142 64 futex sys_futex +143 common gettid sys_gettid +144 common getrlimit sys_getrlimit compat_sys_getrlimit +145 common setrlimit sys_setrlimit compat_sys_setrlimit +146 common pivot_root sys_pivot_root +147 common prctl sys_prctl +148 common pciconfig_read sys_pciconfig_read +149 common pciconfig_write sys_pciconfig_write +150 common getsockname sys_getsockname +151 common inotify_init sys_inotify_init +152 common inotify_add_watch sys_inotify_add_watch +153 common poll sys_poll +154 common getdents64 sys_getdents64 +155 32 fcntl64 sys_fcntl64 compat_sys_fcntl64 +156 common inotify_rm_watch sys_inotify_rm_watch +157 common statfs sys_statfs compat_sys_statfs +158 common fstatfs sys_fstatfs compat_sys_fstatfs +159 common umount sys_oldumount +160 common sched_set_affinity sys_sched_setaffinity compat_sys_sched_setaffinity +161 common sched_get_affinity sys_sched_getaffinity compat_sys_sched_getaffinity +162 common getdomainname sys_getdomainname +163 common setdomainname sys_setdomainname +164 64 utrap_install sys_utrap_install +165 common quotactl sys_quotactl +166 common set_tid_address sys_set_tid_address +167 common mount sys_mount compat_sys_mount +168 common ustat sys_ustat compat_sys_ustat +169 common setxattr sys_setxattr +170 common lsetxattr sys_lsetxattr +171 common fsetxattr sys_fsetxattr +172 common getxattr sys_getxattr +173 common lgetxattr sys_lgetxattr +174 common getdents sys_getdents compat_sys_getdents +175 common setsid sys_setsid +176 common fchdir sys_fchdir +177 common fgetxattr sys_fgetxattr +178 common listxattr sys_listxattr +179 common llistxattr sys_llistxattr +180 common flistxattr sys_flistxattr +181 common removexattr sys_removexattr +182 common lremovexattr sys_lremovexattr +183 32 sigpending sys_sigpending compat_sys_sigpending +183 64 sigpending sys_nis_syscall +184 common query_module sys_ni_syscall +185 common setpgid sys_setpgid +186 common fremovexattr sys_fremovexattr +187 common tkill sys_tkill +188 32 exit_group sys_exit_group sparc_exit_group +188 64 exit_group sparc_exit_group +189 common uname sys_newuname +190 common init_module sys_init_module +191 32 personality sys_personality sys_sparc64_personality +191 64 personality sys_sparc64_personality +192 32 remap_file_pages sys_sparc_remap_file_pages sys_remap_file_pages +192 64 remap_file_pages sys_remap_file_pages +193 common epoll_create sys_epoll_create +194 common epoll_ctl sys_epoll_ctl +195 common epoll_wait sys_epoll_wait +196 common ioprio_set sys_ioprio_set +197 common getppid sys_getppid +198 32 sigaction sys_sparc_sigaction compat_sys_sparc_sigaction +198 64 sigaction sys_nis_syscall +199 common sgetmask sys_sgetmask +200 common ssetmask sys_ssetmask +201 32 sigsuspend sys_sigsuspend +201 64 sigsuspend sys_nis_syscall +202 common oldlstat sys_newlstat compat_sys_newlstat +203 common uselib sys_uselib +204 32 readdir sys_old_readdir compat_sys_old_readdir +204 64 readdir sys_nis_syscall +205 common readahead sys_readahead compat_sys_readahead +206 common socketcall sys_socketcall sys32_socketcall +207 common syslog sys_syslog +208 common lookup_dcookie sys_lookup_dcookie compat_sys_lookup_dcookie +209 common fadvise64 sys_fadvise64 compat_sys_fadvise64 +210 common fadvise64_64 sys_fadvise64_64 compat_sys_fadvise64_64 +211 common tgkill sys_tgkill +212 common waitpid sys_waitpid +213 common swapoff sys_swapoff +214 common sysinfo sys_sysinfo compat_sys_sysinfo +215 32 ipc sys_ipc compat_sys_ipc +215 64 ipc sys_sparc_ipc +216 32 sigreturn sys_sigreturn sys32_sigreturn +216 64 sigreturn sys_nis_syscall +217 common clone sys_clone +218 common ioprio_get sys_ioprio_get +219 32 adjtimex sys_adjtimex_time32 +219 64 adjtimex sys_sparc_adjtimex +220 32 sigprocmask sys_sigprocmask compat_sys_sigprocmask +220 64 sigprocmask sys_nis_syscall +221 common create_module sys_ni_syscall +222 common delete_module sys_delete_module +223 common get_kernel_syms sys_ni_syscall +224 common getpgid sys_getpgid +225 common bdflush sys_bdflush +226 common sysfs sys_sysfs +227 common afs_syscall sys_nis_syscall +228 common setfsuid sys_setfsuid16 +229 common setfsgid sys_setfsgid16 +230 common _newselect sys_select compat_sys_select +231 32 time sys_time32 +232 common splice sys_splice +233 32 stime sys_stime32 +233 64 stime sys_stime +234 common statfs64 sys_statfs64 compat_sys_statfs64 +235 common fstatfs64 sys_fstatfs64 compat_sys_fstatfs64 +236 common _llseek sys_llseek +237 common mlock sys_mlock +238 common munlock sys_munlock +239 common mlockall sys_mlockall +240 common munlockall sys_munlockall +241 common sched_setparam sys_sched_setparam +242 common sched_getparam sys_sched_getparam +243 common sched_setscheduler sys_sched_setscheduler +244 common sched_getscheduler sys_sched_getscheduler +245 common sched_yield sys_sched_yield +246 common sched_get_priority_max sys_sched_get_priority_max +247 common sched_get_priority_min sys_sched_get_priority_min +248 32 sched_rr_get_interval sys_sched_rr_get_interval_time32 +248 64 sched_rr_get_interval sys_sched_rr_get_interval +249 32 nanosleep sys_nanosleep_time32 +249 64 nanosleep sys_nanosleep +250 32 mremap sys_mremap +250 64 mremap sys_64_mremap +251 common _sysctl sys_sysctl compat_sys_sysctl +252 common getsid sys_getsid +253 common fdatasync sys_fdatasync +254 32 nfsservctl sys_ni_syscall sys_nis_syscall +254 64 nfsservctl sys_nis_syscall +255 common sync_file_range sys_sync_file_range compat_sys_sync_file_range +256 32 clock_settime sys_clock_settime32 +256 64 clock_settime sys_clock_settime +257 32 clock_gettime sys_clock_gettime32 +257 64 clock_gettime sys_clock_gettime +258 32 clock_getres sys_clock_getres_time32 +258 64 clock_getres sys_clock_getres +259 32 clock_nanosleep sys_clock_nanosleep_time32 +259 64 clock_nanosleep sys_clock_nanosleep +260 common sched_getaffinity sys_sched_getaffinity compat_sys_sched_getaffinity +261 common sched_setaffinity sys_sched_setaffinity compat_sys_sched_setaffinity +262 32 timer_settime sys_timer_settime32 +262 64 timer_settime sys_timer_settime +263 32 timer_gettime sys_timer_gettime32 +263 64 timer_gettime sys_timer_gettime +264 common timer_getoverrun sys_timer_getoverrun +265 common timer_delete sys_timer_delete +266 common timer_create sys_timer_create compat_sys_timer_create +# 267 was vserver +267 common vserver sys_nis_syscall +268 common io_setup sys_io_setup compat_sys_io_setup +269 common io_destroy sys_io_destroy +270 common io_submit sys_io_submit compat_sys_io_submit +271 common io_cancel sys_io_cancel +272 32 io_getevents sys_io_getevents_time32 +272 64 io_getevents sys_io_getevents +273 common mq_open sys_mq_open compat_sys_mq_open +274 common mq_unlink sys_mq_unlink +275 32 mq_timedsend sys_mq_timedsend_time32 +275 64 mq_timedsend sys_mq_timedsend +276 32 mq_timedreceive sys_mq_timedreceive_time32 +276 64 mq_timedreceive sys_mq_timedreceive +277 common mq_notify sys_mq_notify compat_sys_mq_notify +278 common mq_getsetattr sys_mq_getsetattr compat_sys_mq_getsetattr +279 common waitid sys_waitid compat_sys_waitid +280 common tee sys_tee +281 common add_key sys_add_key +282 common request_key sys_request_key +283 common keyctl sys_keyctl compat_sys_keyctl +284 common openat sys_openat compat_sys_openat +285 common mkdirat sys_mkdirat +286 common mknodat sys_mknodat +287 common fchownat sys_fchownat +288 32 futimesat sys_futimesat_time32 +288 64 futimesat sys_futimesat +289 common fstatat64 sys_fstatat64 compat_sys_fstatat64 +290 common unlinkat sys_unlinkat +291 common renameat sys_renameat +292 common linkat sys_linkat +293 common symlinkat sys_symlinkat +294 common readlinkat sys_readlinkat +295 common fchmodat sys_fchmodat +296 common faccessat sys_faccessat +297 32 pselect6 sys_pselect6_time32 compat_sys_pselect6_time32 +297 64 pselect6 sys_pselect6 +298 32 ppoll sys_ppoll_time32 compat_sys_ppoll_time32 +298 64 ppoll sys_ppoll +299 common unshare sys_unshare +300 common set_robust_list sys_set_robust_list compat_sys_set_robust_list +301 common get_robust_list sys_get_robust_list compat_sys_get_robust_list +302 common migrate_pages sys_migrate_pages compat_sys_migrate_pages +303 common mbind sys_mbind compat_sys_mbind +304 common get_mempolicy sys_get_mempolicy compat_sys_get_mempolicy +305 common set_mempolicy sys_set_mempolicy compat_sys_set_mempolicy +306 common kexec_load sys_kexec_load compat_sys_kexec_load +307 common move_pages sys_move_pages compat_sys_move_pages +308 common getcpu sys_getcpu +309 common epoll_pwait sys_epoll_pwait compat_sys_epoll_pwait +310 32 utimensat sys_utimensat_time32 +310 64 utimensat sys_utimensat +311 common signalfd sys_signalfd compat_sys_signalfd +312 common timerfd_create sys_timerfd_create +313 common eventfd sys_eventfd +314 common fallocate sys_fallocate compat_sys_fallocate +315 32 timerfd_settime sys_timerfd_settime32 +315 64 timerfd_settime sys_timerfd_settime +316 32 timerfd_gettime sys_timerfd_gettime32 +316 64 timerfd_gettime sys_timerfd_gettime +317 common signalfd4 sys_signalfd4 compat_sys_signalfd4 +318 common eventfd2 sys_eventfd2 +319 common epoll_create1 sys_epoll_create1 +320 common dup3 sys_dup3 +321 common pipe2 sys_pipe2 +322 common inotify_init1 sys_inotify_init1 +323 common accept4 sys_accept4 +324 common preadv sys_preadv compat_sys_preadv +325 common pwritev sys_pwritev compat_sys_pwritev +326 common rt_tgsigqueueinfo sys_rt_tgsigqueueinfo compat_sys_rt_tgsigqueueinfo +327 common perf_event_open sys_perf_event_open +328 32 recvmmsg sys_recvmmsg_time32 compat_sys_recvmmsg_time32 +328 64 recvmmsg sys_recvmmsg +329 common fanotify_init sys_fanotify_init +330 common fanotify_mark sys_fanotify_mark compat_sys_fanotify_mark +331 common prlimit64 sys_prlimit64 +332 common name_to_handle_at sys_name_to_handle_at +333 common open_by_handle_at sys_open_by_handle_at compat_sys_open_by_handle_at +334 32 clock_adjtime sys_clock_adjtime32 +334 64 clock_adjtime sys_sparc_clock_adjtime +335 common syncfs sys_syncfs +336 common sendmmsg sys_sendmmsg compat_sys_sendmmsg +337 common setns sys_setns +338 common process_vm_readv sys_process_vm_readv compat_sys_process_vm_readv +339 common process_vm_writev sys_process_vm_writev compat_sys_process_vm_writev +340 32 kern_features sys_ni_syscall sys_kern_features +340 64 kern_features sys_kern_features +341 common kcmp sys_kcmp +342 common finit_module sys_finit_module +343 common sched_setattr sys_sched_setattr +344 common sched_getattr sys_sched_getattr +345 common renameat2 sys_renameat2 +346 common seccomp sys_seccomp +347 common getrandom sys_getrandom +348 common memfd_create sys_memfd_create +349 common bpf sys_bpf +350 32 execveat sys_execveat sys32_execveat +350 64 execveat sys64_execveat +351 common membarrier sys_membarrier +352 common userfaultfd sys_userfaultfd +353 common bind sys_bind +354 common listen sys_listen +355 common setsockopt sys_setsockopt compat_sys_setsockopt +356 common mlock2 sys_mlock2 +357 common copy_file_range sys_copy_file_range +358 common preadv2 sys_preadv2 compat_sys_preadv2 +359 common pwritev2 sys_pwritev2 compat_sys_pwritev2 +360 common statx sys_statx +361 32 io_pgetevents sys_io_pgetevents_time32 compat_sys_io_pgetevents +361 64 io_pgetevents sys_io_pgetevents +362 common pkey_mprotect sys_pkey_mprotect +363 common pkey_alloc sys_pkey_alloc +364 common pkey_free sys_pkey_free +365 common rseq sys_rseq +# room for arch specific syscalls +392 64 semtimedop sys_semtimedop +393 common semget sys_semget +394 common semctl sys_semctl compat_sys_semctl +395 common shmget sys_shmget +396 common shmctl sys_shmctl compat_sys_shmctl +397 common shmat sys_shmat compat_sys_shmat +398 common shmdt sys_shmdt +399 common msgget sys_msgget +400 common msgsnd sys_msgsnd compat_sys_msgsnd +401 common msgrcv sys_msgrcv compat_sys_msgrcv +402 common msgctl sys_msgctl compat_sys_msgctl +403 32 clock_gettime64 sys_clock_gettime sys_clock_gettime +404 32 clock_settime64 sys_clock_settime sys_clock_settime +405 32 clock_adjtime64 sys_clock_adjtime sys_clock_adjtime +406 32 clock_getres_time64 sys_clock_getres sys_clock_getres +407 32 clock_nanosleep_time64 sys_clock_nanosleep sys_clock_nanosleep +408 32 timer_gettime64 sys_timer_gettime sys_timer_gettime +409 32 timer_settime64 sys_timer_settime sys_timer_settime +410 32 timerfd_gettime64 sys_timerfd_gettime sys_timerfd_gettime +411 32 timerfd_settime64 sys_timerfd_settime sys_timerfd_settime +412 32 utimensat_time64 sys_utimensat sys_utimensat +413 32 pselect6_time64 sys_pselect6 compat_sys_pselect6_time64 +414 32 ppoll_time64 sys_ppoll compat_sys_ppoll_time64 +416 32 io_pgetevents_time64 sys_io_pgetevents sys_io_pgetevents +417 32 recvmmsg_time64 sys_recvmmsg compat_sys_recvmmsg_time64 +418 32 mq_timedsend_time64 sys_mq_timedsend sys_mq_timedsend +419 32 mq_timedreceive_time64 sys_mq_timedreceive sys_mq_timedreceive +420 32 semtimedop_time64 sys_semtimedop sys_semtimedop +421 32 rt_sigtimedwait_time64 sys_rt_sigtimedwait compat_sys_rt_sigtimedwait_time64 +422 32 futex_time64 sys_futex sys_futex +423 32 sched_rr_get_interval_time64 sys_sched_rr_get_interval sys_sched_rr_get_interval +424 common pidfd_send_signal sys_pidfd_send_signal +425 common io_uring_setup sys_io_uring_setup +426 common io_uring_enter sys_io_uring_enter +427 common io_uring_register sys_io_uring_register +428 common open_tree sys_open_tree +429 common move_mount sys_move_mount +430 common fsopen sys_fsopen +431 common fsconfig sys_fsconfig +432 common fsmount sys_fsmount +433 common fspick sys_fspick +434 common pidfd_open sys_pidfd_open +# 435 reserved for clone3 +437 common openat2 sys_openat2 +438 common pidfd_getfd sys_pidfd_getfd diff --git a/linux-user/sparc/syscall_nr.h b/linux-user/sparc/syscall_nr.h deleted file mode 100644 index 162099f9ce..0000000000 --- a/linux-user/sparc/syscall_nr.h +++ /dev/null @@ -1,363 +0,0 @@ -#ifndef LINUX_USER_SPARC_SYSCALL_NR_H -#define LINUX_USER_SPARC_SYSCALL_NR_H - -#define TARGET_NR_exit 1 /* Common */ -#define TARGET_NR_fork 2 /* Common */ -#define TARGET_NR_read 3 /* Common */ -#define TARGET_NR_write 4 /* Common */ -#define TARGET_NR_open 5 /* Common */ -#define TARGET_NR_close 6 /* Common */ -#define TARGET_NR_wait4 7 /* Common */ -#define TARGET_NR_creat 8 /* Common */ -#define TARGET_NR_link 9 /* Common */ -#define TARGET_NR_unlink 10 /* Common */ -#define TARGET_NR_execv 11 /* SunOS Specific */ -#define TARGET_NR_chdir 12 /* Common */ -#define TARGET_NR_chown 13 /* Common */ -#define TARGET_NR_mknod 14 /* Common */ -#define TARGET_NR_chmod 15 /* Common */ -#define TARGET_NR_lchown 16 /* Common */ -#define TARGET_NR_brk 17 /* Common */ -#define TARGET_NR_perfctr 18 /* Performance counter operations */ -#define TARGET_NR_lseek 19 /* Common */ -#define TARGET_NR_getpid 20 /* Common */ -#define TARGET_NR_capget 21 /* Linux Specific */ -#define TARGET_NR_capset 22 /* Linux Specific */ -#define TARGET_NR_setuid 23 /* Implemented via setreuid in SunOS */ -#define TARGET_NR_getuid 24 /* Common */ -#define TARGET_NR_vmsplice 25 -#define TARGET_NR_ptrace 26 /* Common */ -#define TARGET_NR_alarm 27 /* Implemented via setitimer in SunOS */ -#define TARGET_NR_sigaltstack 28 /* Common */ -#define TARGET_NR_pause 29 /* Is sigblock(0)->sigpause() in SunOS */ -#define TARGET_NR_utime 30 /* Implemented via utimes() under SunOS */ -#define TARGET_NR_lchown32 31 /* Linux sparc32 specific */ -#define TARGET_NR_fchown32 32 /* Linux sparc32 specific */ -#define TARGET_NR_access 33 /* Common */ -#define TARGET_NR_nice 34 /* Implemented via get/setpriority() in SunOS */ -#define TARGET_NR_chown32 35 /* Linux sparc32 specific */ -#define TARGET_NR_sync 36 /* Common */ -#define TARGET_NR_kill 37 /* Common */ -#define TARGET_NR_stat 38 /* Common */ -#define TARGET_NR_sendfile 39 /* Linux Specific */ -#define TARGET_NR_lstat 40 /* Common */ -#define TARGET_NR_dup 41 /* Common */ -#define TARGET_NR_pipe 42 /* Common */ -#define TARGET_NR_times 43 /* Implemented via getrusage() in SunOS */ -#define TARGET_NR_getuid32 44 /* Linux sparc32 specific */ -#define TARGET_NR_umount2 45 /* Linux Specific */ -#define TARGET_NR_setgid 46 /* Implemented via setregid() in SunOS */ -#define TARGET_NR_getgid 47 /* Common */ -#define TARGET_NR_signal 48 /* Implemented via sigvec() in SunOS */ -#define TARGET_NR_geteuid 49 /* SunOS calls getuid() */ -#define TARGET_NR_getegid 50 /* SunOS calls getgid() */ -#define TARGET_NR_acct 51 /* Common */ -#define TARGET_NR_getgid32 53 /* Linux sparc32 specific */ -#define TARGET_NR_ioctl 54 /* Common */ -#define TARGET_NR_reboot 55 /* Common */ -#define TARGET_NR_mmap2 56 /* Linux sparc32 Specific */ -#define TARGET_NR_symlink 57 /* Common */ -#define TARGET_NR_readlink 58 /* Common */ -#define TARGET_NR_execve 59 /* Common */ -#define TARGET_NR_umask 60 /* Common */ -#define TARGET_NR_chroot 61 /* Common */ -#define TARGET_NR_fstat 62 /* Common */ -#define TARGET_NR_fstat64 63 /* Linux sparc32 Specific */ -#define TARGET_NR_getpagesize 64 /* Common */ -#define TARGET_NR_msync 65 /* Common in newer 1.3.x revs... */ -#define TARGET_NR_vfork 66 /* Common */ -#define TARGET_NR_pread64 67 /* Linux Specific */ -#define TARGET_NR_pwrite64 68 /* Linux Specific */ -#define TARGET_NR_geteuid32 69 /* Linux sparc32, sbrk under SunOS */ -#define TARGET_NR_getegid32 70 /* Linux sparc32, sstk under SunOS */ -#define TARGET_NR_mmap 71 /* Common */ -#define TARGET_NR_setreuid32 72 /* Linux sparc32, vadvise under SunOS */ -#define TARGET_NR_munmap 73 /* Common */ -#define TARGET_NR_mprotect 74 /* Common */ -#define TARGET_NR_madvise 75 /* Common */ -#define TARGET_NR_vhangup 76 /* Common */ -#define TARGET_NR_truncate64 77 /* Linux sparc32 Specific */ -#define TARGET_NR_mincore 78 /* Common */ -#define TARGET_NR_getgroups 79 /* Common */ -#define TARGET_NR_setgroups 80 /* Common */ -#define TARGET_NR_getpgrp 81 /* Common */ -#define TARGET_NR_setgroups32 82 /* Linux sparc32, setpgrp under SunOS */ -#define TARGET_NR_setitimer 83 /* Common */ -#define TARGET_NR_ftruncate64 84 /* Linux sparc32 Specific */ -#define TARGET_NR_swapon 85 /* Common */ -#define TARGET_NR_getitimer 86 /* Common */ -#define TARGET_NR_setuid32 87 /* Linux sparc32, gethostname under SunOS */ -#define TARGET_NR_sethostname 88 /* Common */ -#define TARGET_NR_setgid32 89 /* Linux sparc32, getdtablesize under SunOS */ -#define TARGET_NR_dup2 90 /* Common */ -#define TARGET_NR_setfsuid32 91 /* Linux sparc32, getdopt under SunOS */ -#define TARGET_NR_fcntl 92 /* Common */ -#define TARGET_NR_select 93 /* Common */ -#define TARGET_NR_setfsgid32 94 /* Linux sparc32, setdopt under SunOS */ -#define TARGET_NR_fsync 95 /* Common */ -#define TARGET_NR_setpriority 96 /* Common */ -#define TARGET_NR_socket 97 /* Common */ -#define TARGET_NR_connect 98 /* Common */ -#define TARGET_NR_accept 99 /* Common */ -#define TARGET_NR_getpriority 100 /* Common */ -#define TARGET_NR_rt_sigreturn 101 /* Linux Specific */ -#define TARGET_NR_rt_sigaction 102 /* Linux Specific */ -#define TARGET_NR_rt_sigprocmask 103 /* Linux Specific */ -#define TARGET_NR_rt_sigpending 104 /* Linux Specific */ -#define TARGET_NR_rt_sigtimedwait 105 /* Linux Specific */ -#define TARGET_NR_rt_sigqueueinfo 106 /* Linux Specific */ -#define TARGET_NR_rt_sigsuspend 107 /* Linux Specific */ -#define TARGET_NR_setresuid32 108 /* Linux Specific, sigvec under SunOS */ -#define TARGET_NR_getresuid32 109 /* Linux Specific, sigblock under SunOS */ -#define TARGET_NR_setresgid32 110 /* Linux Specific, sigsetmask under SunOS */ -#define TARGET_NR_getresgid32 111 /* Linux Specific, sigpause under SunOS */ -#define TARGET_NR_setregid32 112 /* Linux sparc32, sigstack under SunOS */ -#define TARGET_NR_recvmsg 113 /* Common */ -#define TARGET_NR_sendmsg 114 /* Common */ -#define TARGET_NR_getgroups32 115 /* Linux sparc32, vtrace under SunOS */ -#define TARGET_NR_gettimeofday 116 /* Common */ -#define TARGET_NR_getrusage 117 /* Common */ -#define TARGET_NR_getsockopt 118 /* Common */ -#define TARGET_NR_getcwd 119 /* Linux Specific */ -#define TARGET_NR_readv 120 /* Common */ -#define TARGET_NR_writev 121 /* Common */ -#define TARGET_NR_settimeofday 122 /* Common */ -#define TARGET_NR_fchown 123 /* Common */ -#define TARGET_NR_fchmod 124 /* Common */ -#define TARGET_NR_recvfrom 125 /* Common */ -#define TARGET_NR_setreuid 126 /* Common */ -#define TARGET_NR_setregid 127 /* Common */ -#define TARGET_NR_rename 128 /* Common */ -#define TARGET_NR_truncate 129 /* Common */ -#define TARGET_NR_ftruncate 130 /* Common */ -#define TARGET_NR_flock 131 /* Common */ -#define TARGET_NR_lstat64 132 /* Linux sparc32 Specific */ -#define TARGET_NR_sendto 133 /* Common */ -#define TARGET_NR_shutdown 134 /* Common */ -#define TARGET_NR_socketpair 135 /* Common */ -#define TARGET_NR_mkdir 136 /* Common */ -#define TARGET_NR_rmdir 137 /* Common */ -#define TARGET_NR_utimes 138 /* SunOS Specific */ -#define TARGET_NR_stat64 139 /* Linux sparc32 Specific */ -#define TARGET_NR_sendfile64 140 -#define TARGET_NR_getpeername 141 /* Common */ -#define TARGET_NR_futex 142 /* gethostid under SunOS */ -#define TARGET_NR_gettid 143 /* ENOSYS under SunOS */ -#define TARGET_NR_getrlimit 144 /* Common */ -#define TARGET_NR_setrlimit 145 /* Common */ -#define TARGET_NR_pivot_root 146 /* Linux Specific, killpg under SunOS */ -#define TARGET_NR_prctl 147 /* ENOSYS under SunOS */ -#define TARGET_NR_pciconfig_read 148 /* ENOSYS under SunOS */ -#define TARGET_NR_pciconfig_write 149 /* ENOSYS under SunOS */ -#define TARGET_NR_getsockname 150 /* Common */ -#define TARGET_NR_inotify_init 151 -#define TARGET_NR_inotify_add_watch 152 -#define TARGET_NR_poll 153 /* Common */ -#define TARGET_NR_getdents64 154 /* Linux specific */ -#define TARGET_NR_fcntl64 155 /* Linux sparc32 Specific */ -#define TARGET_NR_inotify_rm_watch 156 -#define TARGET_NR_statfs 157 /* Common */ -#define TARGET_NR_fstatfs 158 /* Common */ -#define TARGET_NR_umount 159 /* Common */ -#define TARGET_NR_sched_set_affinity 160 -#define TARGET_NR_sched_get_affinity 161 -#define TARGET_NR_getdomainname 162 /* SunOS Specific */ -#define TARGET_NR_setdomainname 163 /* Common */ -#define TARGET_NR_quotactl 165 /* Common */ -#define TARGET_NR_set_tid_address 166 /* Linux specific, exportfs under SunOS */ -#define TARGET_NR_mount 167 /* Common */ -#define TARGET_NR_ustat 168 /* Common */ -#define TARGET_NR_setxattr 169 -#define TARGET_NR_lsetxattr 170 -#define TARGET_NR_fsetxattr 171 -#define TARGET_NR_getxattr 172 -#define TARGET_NR_lgetxattr 173 -#define TARGET_NR_getdents 174 /* Common */ -#define TARGET_NR_setsid 175 /* Common */ -#define TARGET_NR_fchdir 176 /* Common */ -#define TARGET_NR_fgetxattr 177 -#define TARGET_NR_listxattr 178 -#define TARGET_NR_llistxattr 179 -#define TARGET_NR_flistxattr 180 -#define TARGET_NR_removexattr 181 -#define TARGET_NR_lremovexattr 182 -#define TARGET_NR_sigpending 183 /* Common */ -#define TARGET_NR_query_module 184 /* Linux Specific */ -#define TARGET_NR_setpgid 185 /* Common */ -#define TARGET_NR_fremovexattr 186 -#define TARGET_NR_tkill 187 /* SunOS: fpathconf */ -#define TARGET_NR_exit_group 188 /* Linux specific, sysconf undef SunOS */ -#define TARGET_NR_uname 189 /* Linux Specific */ -#define TARGET_NR_init_module 190 /* Linux Specific */ -#define TARGET_NR_personality 191 /* Linux Specific */ -#define TARGET_NR_remap_file_pages 192 -#define TARGET_NR_epoll_create 193 -#define TARGET_NR_epoll_ctl 194 -#define TARGET_NR_epoll_wait 195 -#define TARGET_NR_ioprio_set 196 -#define TARGET_NR_getppid 197 /* Linux Specific */ -#define TARGET_NR_sigaction 198 /* Linux Specific */ -#define TARGET_NR_sgetmask 199 /* Linux Specific */ -#define TARGET_NR_ssetmask 200 /* Linux Specific */ -#define TARGET_NR_sigsuspend 201 /* Linux Specific */ -#define TARGET_NR_oldlstat 202 /* Linux Specific */ -#define TARGET_NR_uselib 203 /* Linux Specific */ -#define TARGET_NR_readdir 204 /* Linux Specific */ -#define TARGET_NR_readahead 205 /* Linux Specific */ -#define TARGET_NR_socketcall 206 /* Linux Specific */ -#define TARGET_NR_syslog 207 /* Linux Specific */ -#define TARGET_NR_lookup_dcookie 208 /* Linux Specific */ -#define TARGET_NR_fadvise64 209 /* Linux Specific */ -#define TARGET_NR_fadvise64_64 210 /* Linux Specific */ -#define TARGET_NR_tgkill 211 /* Linux Specific */ -#define TARGET_NR_waitpid 212 /* Linux Specific */ -#define TARGET_NR_swapoff 213 /* Linux Specific */ -#define TARGET_NR_sysinfo 214 /* Linux Specific */ -#define TARGET_NR_ipc 215 /* Linux Specific */ -#define TARGET_NR_sigreturn 216 /* Linux Specific */ -#define TARGET_NR_clone 217 /* Linux Specific */ -#define TARGET_NR_ioprio_get 218 -#define TARGET_NR_adjtimex 219 /* Linux Specific */ -#define TARGET_NR_sigprocmask 220 /* Linux Specific */ -#define TARGET_NR_create_module 221 /* Linux Specific */ -#define TARGET_NR_delete_module 222 /* Linux Specific */ -#define TARGET_NR_get_kernel_syms 223 /* Linux Specific */ -#define TARGET_NR_getpgid 224 /* Linux Specific */ -#define TARGET_NR_bdflush 225 /* Linux Specific */ -#define TARGET_NR_sysfs 226 /* Linux Specific */ -#define TARGET_NR_afs_syscall 227 /* Linux Specific */ -#define TARGET_NR_setfsuid 228 /* Linux Specific */ -#define TARGET_NR_setfsgid 229 /* Linux Specific */ -#define TARGET_NR__newselect 230 /* Linux Specific */ -#define TARGET_NR_time 231 /* Linux Specific */ -#define TARGET_NR_splice 232 -#define TARGET_NR_stime 233 /* Linux Specific */ -#define TARGET_NR_statfs64 234 /* Linux Specific */ -#define TARGET_NR_fstatfs64 235 /* Linux Specific */ -#define TARGET_NR__llseek 236 /* Linux Specific */ -#define TARGET_NR_mlock 237 -#define TARGET_NR_munlock 238 -#define TARGET_NR_mlockall 239 -#define TARGET_NR_munlockall 240 -#define TARGET_NR_sched_setparam 241 -#define TARGET_NR_sched_getparam 242 -#define TARGET_NR_sched_setscheduler 243 -#define TARGET_NR_sched_getscheduler 244 -#define TARGET_NR_sched_yield 245 -#define TARGET_NR_sched_get_priority_max 246 -#define TARGET_NR_sched_get_priority_min 247 -#define TARGET_NR_sched_rr_get_interval 248 -#define TARGET_NR_nanosleep 249 -#define TARGET_NR_mremap 250 -#define TARGET_NR__sysctl 251 -#define TARGET_NR_getsid 252 -#define TARGET_NR_fdatasync 253 -#define TARGET_NR_nfsservctl 254 -#define TARGET_NR_sync_file_range 255 -#define TARGET_NR_clock_settime 256 -#define TARGET_NR_clock_gettime 257 -#define TARGET_NR_clock_getres 258 -#define TARGET_NR_clock_nanosleep 259 -#define TARGET_NR_sched_getaffinity 260 -#define TARGET_NR_sched_setaffinity 261 -#define TARGET_NR_timer_settime 262 -#define TARGET_NR_timer_gettime 263 -#define TARGET_NR_timer_getoverrun 264 -#define TARGET_NR_timer_delete 265 -#define TARGET_NR_timer_create 266 -/* #define TARGET_NR_vserver 267 Reserved for VSERVER */ -#define TARGET_NR_io_setup 268 -#define TARGET_NR_io_destroy 269 -#define TARGET_NR_io_submit 270 -#define TARGET_NR_io_cancel 271 -#define TARGET_NR_io_getevents 272 -#define TARGET_NR_mq_open 273 -#define TARGET_NR_mq_unlink 274 -#define TARGET_NR_mq_timedsend 275 -#define TARGET_NR_mq_timedreceive 276 -#define TARGET_NR_mq_notify 277 -#define TARGET_NR_mq_getsetattr 278 -#define TARGET_NR_waitid 279 -#define TARGET_NR_tee 280 -#define TARGET_NR_add_key 281 -#define TARGET_NR_request_key 282 -#define TARGET_NR_keyctl 283 -#define TARGET_NR_openat 284 -#define TARGET_NR_mkdirat 285 -#define TARGET_NR_mknodat 286 -#define TARGET_NR_fchownat 287 -#define TARGET_NR_futimesat 288 -#define TARGET_NR_fstatat64 289 -#define TARGET_NR_unlinkat 290 -#define TARGET_NR_renameat 291 -#define TARGET_NR_linkat 292 -#define TARGET_NR_symlinkat 293 -#define TARGET_NR_readlinkat 294 -#define TARGET_NR_fchmodat 295 -#define TARGET_NR_faccessat 296 -#define TARGET_NR_pselect6 297 -#define TARGET_NR_ppoll 298 -#define TARGET_NR_unshare 299 -#define TARGET_NR_set_robust_list 300 -#define TARGET_NR_get_robust_list 301 -#define TARGET_NR_migrate_pages 302 -#define TARGET_NR_mbind 303 -#define TARGET_NR_get_mempolicy 304 -#define TARGET_NR_set_mempolicy 305 -#define TARGET_NR_kexec_load 306 -#define TARGET_NR_move_pages 307 -#define TARGET_NR_getcpu 308 -#define TARGET_NR_epoll_pwait 309 -#define TARGET_NR_utimensat 310 -#define TARGET_NR_signalfd 311 -#define TARGET_NR_timerfd_create 312 -#define TARGET_NR_eventfd 313 -#define TARGET_NR_fallocate 314 -#define TARGET_NR_timerfd_settime 315 -#define TARGET_NR_timerfd_gettime 316 -#define TARGET_NR_signalfd4 317 -#define TARGET_NR_eventfd2 318 -#define TARGET_NR_epoll_create1 319 -#define TARGET_NR_dup3 320 -#define TARGET_NR_pipe2 321 -#define TARGET_NR_inotify_init1 322 -#define TARGET_NR_accept4 323 -#define TARGET_NR_preadv 324 -#define TARGET_NR_pwritev 325 -#define TARGET_NR_rt_tgsigqueueinfo 326 -#define TARGET_NR_perf_event_open 327 -#define TARGET_NR_recvmmsg 328 -#define TARGET_NR_fanotify_init 329 -#define TARGET_NR_fanotify_mark 330 -#define TARGET_NR_prlimit64 331 -#define TARGET_NR_name_to_handle_at 332 -#define TARGET_NR_open_by_handle_at 333 -#define TARGET_NR_clock_adjtime 334 -#define TARGET_NR_syncfs 335 -#define TARGET_NR_sendmmsg 336 -#define TARGET_NR_setns 337 -#define TARGET_NR_process_vm_readv 338 -#define TARGET_NR_process_vm_writev 339 -#define TARGET_NR_kern_features 340 -#define TARGET_NR_kcmp 341 -#define TARGET_NR_finit_module 342 -#define TARGET_NR_sched_setattr 343 -#define TARGET_NR_sched_getattr 344 -#define TARGET_NR_renameat2 345 -#define TARGET_NR_seccomp 346 -#define TARGET_NR_getrandom 347 -#define TARGET_NR_memfd_create 348 -#define TARGET_NR_bpf 349 -#define TARGET_NR_execveat 350 -#define TARGET_NR_membarrier 351 -#define TARGET_NR_userfaultfd 352 -#define TARGET_NR_bind 353 -#define TARGET_NR_listen 354 -#define TARGET_NR_setsockopt 355 -#define TARGET_NR_mlock2 356 -#define TARGET_NR_copy_file_range 357 -#define TARGET_NR_preadv2 358 -#define TARGET_NR_pwritev2 359 -#define TARGET_NR_statx 360 - -#endif diff --git a/linux-user/sparc/syscallhdr.sh b/linux-user/sparc/syscallhdr.sh new file mode 100644 index 0000000000..34a99dc832 --- /dev/null +++ b/linux-user/sparc/syscallhdr.sh @@ -0,0 +1,32 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 + +in="$1" +out="$2" +my_abis=`echo "($3)" | tr ',' '|'` +prefix="$4" +offset="$5" + +fileguard=LINUX_USER_SPARC_`basename "$out" | sed \ + -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/' \ + -e 's/[^A-Z0-9_]/_/g' -e 's/__/_/g'` +grep -E "^[0-9A-Fa-fXx]+[[:space:]]+${my_abis}" "$in" | sort -n | ( + printf "#ifndef %s\n" "${fileguard}" + printf "#define %s\n" "${fileguard}" + printf "\n" + + nxt=0 + while read nr abi name entry compat ; do + if [ -z "$offset" ]; then + printf "#define TARGET_NR_%s%s\t%s\n" \ + "${prefix}" "${name}" "${nr}" + else + printf "#define TARGET_NR_%s%s\t(%s + %s)\n" \ + "${prefix}" "${name}" "${offset}" "${nr}" + fi + nxt=$((nr+1)) + done + + printf "\n" + printf "#endif /* %s */" "${fileguard}" +) > "$out" diff --git a/linux-user/sparc64/Makefile.objs b/linux-user/sparc64/Makefile.objs new file mode 100644 index 0000000000..afcd535bc4 --- /dev/null +++ b/linux-user/sparc64/Makefile.objs @@ -0,0 +1,5 @@ +generated-files-y += linux-user/sparc64/syscall_nr.h + +syshdr := $(SRC_PATH)/linux-user/sparc64/syscallhdr.sh +%/syscall_nr.h: $(SRC_PATH)/linux-user/sparc/syscall.tbl $(syshdr) + $(call quiet-command, sh $(syshdr) $< $@ $(TARGET_SYSTBL_ABI),"GEN","$@") diff --git a/linux-user/sparc64/syscall.tbl b/linux-user/sparc64/syscall.tbl new file mode 100644 index 0000000000..f13615ecde --- /dev/null +++ b/linux-user/sparc64/syscall.tbl @@ -0,0 +1,485 @@ +# SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note +# +# system call numbers and entry vectors for sparc +# +# The format is: +# +# +# The can be common, 64, or 32 for this file. +# +0 common restart_syscall sys_restart_syscall +1 32 exit sys_exit sparc_exit +1 64 exit sparc_exit +2 common fork sys_fork +3 common read sys_read +4 common write sys_write +5 common open sys_open compat_sys_open +6 common close sys_close +7 common wait4 sys_wait4 compat_sys_wait4 +8 common creat sys_creat +9 common link sys_link +10 common unlink sys_unlink +11 32 execv sunos_execv +11 64 execv sys_nis_syscall +12 common chdir sys_chdir +13 32 chown sys_chown16 +13 64 chown sys_chown +14 common mknod sys_mknod +15 common chmod sys_chmod +16 32 lchown sys_lchown16 +16 64 lchown sys_lchown +17 common brk sys_brk +18 common perfctr sys_nis_syscall +19 common lseek sys_lseek compat_sys_lseek +20 common getpid sys_getpid +21 common capget sys_capget +22 common capset sys_capset +23 32 setuid sys_setuid16 +23 64 setuid sys_setuid +24 32 getuid sys_getuid16 +24 64 getuid sys_getuid +25 common vmsplice sys_vmsplice compat_sys_vmsplice +26 common ptrace sys_ptrace compat_sys_ptrace +27 common alarm sys_alarm +28 common sigaltstack sys_sigaltstack compat_sys_sigaltstack +29 32 pause sys_pause +29 64 pause sys_nis_syscall +30 32 utime sys_utime32 +30 64 utime sys_utime +31 32 lchown32 sys_lchown +32 32 fchown32 sys_fchown +33 common access sys_access +34 common nice sys_nice +35 32 chown32 sys_chown +36 common sync sys_sync +37 common kill sys_kill +38 common stat sys_newstat compat_sys_newstat +39 32 sendfile sys_sendfile compat_sys_sendfile +39 64 sendfile sys_sendfile64 +40 common lstat sys_newlstat compat_sys_newlstat +41 common dup sys_dup +42 common pipe sys_sparc_pipe +43 common times sys_times compat_sys_times +44 32 getuid32 sys_getuid +45 common umount2 sys_umount +46 32 setgid sys_setgid16 +46 64 setgid sys_setgid +47 32 getgid sys_getgid16 +47 64 getgid sys_getgid +48 common signal sys_signal +49 32 geteuid sys_geteuid16 +49 64 geteuid sys_geteuid +50 32 getegid sys_getegid16 +50 64 getegid sys_getegid +51 common acct sys_acct +52 64 memory_ordering sys_memory_ordering +53 32 getgid32 sys_getgid +54 common ioctl sys_ioctl compat_sys_ioctl +55 common reboot sys_reboot +56 32 mmap2 sys_mmap2 sys32_mmap2 +57 common symlink sys_symlink +58 common readlink sys_readlink +59 32 execve sys_execve sys32_execve +59 64 execve sys64_execve +60 common umask sys_umask +61 common chroot sys_chroot +62 common fstat sys_newfstat compat_sys_newfstat +63 common fstat64 sys_fstat64 compat_sys_fstat64 +64 common getpagesize sys_getpagesize +65 common msync sys_msync +66 common vfork sys_vfork +67 common pread64 sys_pread64 compat_sys_pread64 +68 common pwrite64 sys_pwrite64 compat_sys_pwrite64 +69 32 geteuid32 sys_geteuid +70 32 getegid32 sys_getegid +71 common mmap sys_mmap +72 32 setreuid32 sys_setreuid +73 32 munmap sys_munmap +73 64 munmap sys_64_munmap +74 common mprotect sys_mprotect +75 common madvise sys_madvise +76 common vhangup sys_vhangup +77 32 truncate64 sys_truncate64 compat_sys_truncate64 +78 common mincore sys_mincore +79 32 getgroups sys_getgroups16 +79 64 getgroups sys_getgroups +80 32 setgroups sys_setgroups16 +80 64 setgroups sys_setgroups +81 common getpgrp sys_getpgrp +82 32 setgroups32 sys_setgroups +83 common setitimer sys_setitimer compat_sys_setitimer +84 32 ftruncate64 sys_ftruncate64 compat_sys_ftruncate64 +85 common swapon sys_swapon +86 common getitimer sys_getitimer compat_sys_getitimer +87 32 setuid32 sys_setuid +88 common sethostname sys_sethostname +89 32 setgid32 sys_setgid +90 common dup2 sys_dup2 +91 32 setfsuid32 sys_setfsuid +92 common fcntl sys_fcntl compat_sys_fcntl +93 common select sys_select +94 32 setfsgid32 sys_setfsgid +95 common fsync sys_fsync +96 common setpriority sys_setpriority +97 common socket sys_socket +98 common connect sys_connect +99 common accept sys_accept +100 common getpriority sys_getpriority +101 common rt_sigreturn sys_rt_sigreturn sys32_rt_sigreturn +102 common rt_sigaction sys_rt_sigaction compat_sys_rt_sigaction +103 common rt_sigprocmask sys_rt_sigprocmask compat_sys_rt_sigprocmask +104 common rt_sigpending sys_rt_sigpending compat_sys_rt_sigpending +105 32 rt_sigtimedwait sys_rt_sigtimedwait_time32 compat_sys_rt_sigtimedwait_time32 +105 64 rt_sigtimedwait sys_rt_sigtimedwait +106 common rt_sigqueueinfo sys_rt_sigqueueinfo compat_sys_rt_sigqueueinfo +107 common rt_sigsuspend sys_rt_sigsuspend compat_sys_rt_sigsuspend +108 32 setresuid32 sys_setresuid +108 64 setresuid sys_setresuid +109 32 getresuid32 sys_getresuid +109 64 getresuid sys_getresuid +110 32 setresgid32 sys_setresgid +110 64 setresgid sys_setresgid +111 32 getresgid32 sys_getresgid +111 64 getresgid sys_getresgid +112 32 setregid32 sys_setregid +113 common recvmsg sys_recvmsg compat_sys_recvmsg +114 common sendmsg sys_sendmsg compat_sys_sendmsg +115 32 getgroups32 sys_getgroups +116 common gettimeofday sys_gettimeofday compat_sys_gettimeofday +117 common getrusage sys_getrusage compat_sys_getrusage +118 common getsockopt sys_getsockopt compat_sys_getsockopt +119 common getcwd sys_getcwd +120 common readv sys_readv compat_sys_readv +121 common writev sys_writev compat_sys_writev +122 common settimeofday sys_settimeofday compat_sys_settimeofday +123 32 fchown sys_fchown16 +123 64 fchown sys_fchown +124 common fchmod sys_fchmod +125 common recvfrom sys_recvfrom +126 32 setreuid sys_setreuid16 +126 64 setreuid sys_setreuid +127 32 setregid sys_setregid16 +127 64 setregid sys_setregid +128 common rename sys_rename +129 common truncate sys_truncate compat_sys_truncate +130 common ftruncate sys_ftruncate compat_sys_ftruncate +131 common flock sys_flock +132 common lstat64 sys_lstat64 compat_sys_lstat64 +133 common sendto sys_sendto +134 common shutdown sys_shutdown +135 common socketpair sys_socketpair +136 common mkdir sys_mkdir +137 common rmdir sys_rmdir +138 32 utimes sys_utimes_time32 +138 64 utimes sys_utimes +139 common stat64 sys_stat64 compat_sys_stat64 +140 common sendfile64 sys_sendfile64 +141 common getpeername sys_getpeername +142 32 futex sys_futex_time32 +142 64 futex sys_futex +143 common gettid sys_gettid +144 common getrlimit sys_getrlimit compat_sys_getrlimit +145 common setrlimit sys_setrlimit compat_sys_setrlimit +146 common pivot_root sys_pivot_root +147 common prctl sys_prctl +148 common pciconfig_read sys_pciconfig_read +149 common pciconfig_write sys_pciconfig_write +150 common getsockname sys_getsockname +151 common inotify_init sys_inotify_init +152 common inotify_add_watch sys_inotify_add_watch +153 common poll sys_poll +154 common getdents64 sys_getdents64 +155 32 fcntl64 sys_fcntl64 compat_sys_fcntl64 +156 common inotify_rm_watch sys_inotify_rm_watch +157 common statfs sys_statfs compat_sys_statfs +158 common fstatfs sys_fstatfs compat_sys_fstatfs +159 common umount sys_oldumount +160 common sched_set_affinity sys_sched_setaffinity compat_sys_sched_setaffinity +161 common sched_get_affinity sys_sched_getaffinity compat_sys_sched_getaffinity +162 common getdomainname sys_getdomainname +163 common setdomainname sys_setdomainname +164 64 utrap_install sys_utrap_install +165 common quotactl sys_quotactl +166 common set_tid_address sys_set_tid_address +167 common mount sys_mount compat_sys_mount +168 common ustat sys_ustat compat_sys_ustat +169 common setxattr sys_setxattr +170 common lsetxattr sys_lsetxattr +171 common fsetxattr sys_fsetxattr +172 common getxattr sys_getxattr +173 common lgetxattr sys_lgetxattr +174 common getdents sys_getdents compat_sys_getdents +175 common setsid sys_setsid +176 common fchdir sys_fchdir +177 common fgetxattr sys_fgetxattr +178 common listxattr sys_listxattr +179 common llistxattr sys_llistxattr +180 common flistxattr sys_flistxattr +181 common removexattr sys_removexattr +182 common lremovexattr sys_lremovexattr +183 32 sigpending sys_sigpending compat_sys_sigpending +183 64 sigpending sys_nis_syscall +184 common query_module sys_ni_syscall +185 common setpgid sys_setpgid +186 common fremovexattr sys_fremovexattr +187 common tkill sys_tkill +188 32 exit_group sys_exit_group sparc_exit_group +188 64 exit_group sparc_exit_group +189 common uname sys_newuname +190 common init_module sys_init_module +191 32 personality sys_personality sys_sparc64_personality +191 64 personality sys_sparc64_personality +192 32 remap_file_pages sys_sparc_remap_file_pages sys_remap_file_pages +192 64 remap_file_pages sys_remap_file_pages +193 common epoll_create sys_epoll_create +194 common epoll_ctl sys_epoll_ctl +195 common epoll_wait sys_epoll_wait +196 common ioprio_set sys_ioprio_set +197 common getppid sys_getppid +198 32 sigaction sys_sparc_sigaction compat_sys_sparc_sigaction +198 64 sigaction sys_nis_syscall +199 common sgetmask sys_sgetmask +200 common ssetmask sys_ssetmask +201 32 sigsuspend sys_sigsuspend +201 64 sigsuspend sys_nis_syscall +202 common oldlstat sys_newlstat compat_sys_newlstat +203 common uselib sys_uselib +204 32 readdir sys_old_readdir compat_sys_old_readdir +204 64 readdir sys_nis_syscall +205 common readahead sys_readahead compat_sys_readahead +206 common socketcall sys_socketcall sys32_socketcall +207 common syslog sys_syslog +208 common lookup_dcookie sys_lookup_dcookie compat_sys_lookup_dcookie +209 common fadvise64 sys_fadvise64 compat_sys_fadvise64 +210 common fadvise64_64 sys_fadvise64_64 compat_sys_fadvise64_64 +211 common tgkill sys_tgkill +212 common waitpid sys_waitpid +213 common swapoff sys_swapoff +214 common sysinfo sys_sysinfo compat_sys_sysinfo +215 32 ipc sys_ipc compat_sys_ipc +215 64 ipc sys_sparc_ipc +216 32 sigreturn sys_sigreturn sys32_sigreturn +216 64 sigreturn sys_nis_syscall +217 common clone sys_clone +218 common ioprio_get sys_ioprio_get +219 32 adjtimex sys_adjtimex_time32 +219 64 adjtimex sys_sparc_adjtimex +220 32 sigprocmask sys_sigprocmask compat_sys_sigprocmask +220 64 sigprocmask sys_nis_syscall +221 common create_module sys_ni_syscall +222 common delete_module sys_delete_module +223 common get_kernel_syms sys_ni_syscall +224 common getpgid sys_getpgid +225 common bdflush sys_bdflush +226 common sysfs sys_sysfs +227 common afs_syscall sys_nis_syscall +228 common setfsuid sys_setfsuid16 +229 common setfsgid sys_setfsgid16 +230 common _newselect sys_select compat_sys_select +231 32 time sys_time32 +232 common splice sys_splice +233 32 stime sys_stime32 +233 64 stime sys_stime +234 common statfs64 sys_statfs64 compat_sys_statfs64 +235 common fstatfs64 sys_fstatfs64 compat_sys_fstatfs64 +236 common _llseek sys_llseek +237 common mlock sys_mlock +238 common munlock sys_munlock +239 common mlockall sys_mlockall +240 common munlockall sys_munlockall +241 common sched_setparam sys_sched_setparam +242 common sched_getparam sys_sched_getparam +243 common sched_setscheduler sys_sched_setscheduler +244 common sched_getscheduler sys_sched_getscheduler +245 common sched_yield sys_sched_yield +246 common sched_get_priority_max sys_sched_get_priority_max +247 common sched_get_priority_min sys_sched_get_priority_min +248 32 sched_rr_get_interval sys_sched_rr_get_interval_time32 +248 64 sched_rr_get_interval sys_sched_rr_get_interval +249 32 nanosleep sys_nanosleep_time32 +249 64 nanosleep sys_nanosleep +250 32 mremap sys_mremap +250 64 mremap sys_64_mremap +251 common _sysctl sys_sysctl compat_sys_sysctl +252 common getsid sys_getsid +253 common fdatasync sys_fdatasync +254 32 nfsservctl sys_ni_syscall sys_nis_syscall +254 64 nfsservctl sys_nis_syscall +255 common sync_file_range sys_sync_file_range compat_sys_sync_file_range +256 32 clock_settime sys_clock_settime32 +256 64 clock_settime sys_clock_settime +257 32 clock_gettime sys_clock_gettime32 +257 64 clock_gettime sys_clock_gettime +258 32 clock_getres sys_clock_getres_time32 +258 64 clock_getres sys_clock_getres +259 32 clock_nanosleep sys_clock_nanosleep_time32 +259 64 clock_nanosleep sys_clock_nanosleep +260 common sched_getaffinity sys_sched_getaffinity compat_sys_sched_getaffinity +261 common sched_setaffinity sys_sched_setaffinity compat_sys_sched_setaffinity +262 32 timer_settime sys_timer_settime32 +262 64 timer_settime sys_timer_settime +263 32 timer_gettime sys_timer_gettime32 +263 64 timer_gettime sys_timer_gettime +264 common timer_getoverrun sys_timer_getoverrun +265 common timer_delete sys_timer_delete +266 common timer_create sys_timer_create compat_sys_timer_create +# 267 was vserver +267 common vserver sys_nis_syscall +268 common io_setup sys_io_setup compat_sys_io_setup +269 common io_destroy sys_io_destroy +270 common io_submit sys_io_submit compat_sys_io_submit +271 common io_cancel sys_io_cancel +272 32 io_getevents sys_io_getevents_time32 +272 64 io_getevents sys_io_getevents +273 common mq_open sys_mq_open compat_sys_mq_open +274 common mq_unlink sys_mq_unlink +275 32 mq_timedsend sys_mq_timedsend_time32 +275 64 mq_timedsend sys_mq_timedsend +276 32 mq_timedreceive sys_mq_timedreceive_time32 +276 64 mq_timedreceive sys_mq_timedreceive +277 common mq_notify sys_mq_notify compat_sys_mq_notify +278 common mq_getsetattr sys_mq_getsetattr compat_sys_mq_getsetattr +279 common waitid sys_waitid compat_sys_waitid +280 common tee sys_tee +281 common add_key sys_add_key +282 common request_key sys_request_key +283 common keyctl sys_keyctl compat_sys_keyctl +284 common openat sys_openat compat_sys_openat +285 common mkdirat sys_mkdirat +286 common mknodat sys_mknodat +287 common fchownat sys_fchownat +288 32 futimesat sys_futimesat_time32 +288 64 futimesat sys_futimesat +289 common fstatat64 sys_fstatat64 compat_sys_fstatat64 +290 common unlinkat sys_unlinkat +291 common renameat sys_renameat +292 common linkat sys_linkat +293 common symlinkat sys_symlinkat +294 common readlinkat sys_readlinkat +295 common fchmodat sys_fchmodat +296 common faccessat sys_faccessat +297 32 pselect6 sys_pselect6_time32 compat_sys_pselect6_time32 +297 64 pselect6 sys_pselect6 +298 32 ppoll sys_ppoll_time32 compat_sys_ppoll_time32 +298 64 ppoll sys_ppoll +299 common unshare sys_unshare +300 common set_robust_list sys_set_robust_list compat_sys_set_robust_list +301 common get_robust_list sys_get_robust_list compat_sys_get_robust_list +302 common migrate_pages sys_migrate_pages compat_sys_migrate_pages +303 common mbind sys_mbind compat_sys_mbind +304 common get_mempolicy sys_get_mempolicy compat_sys_get_mempolicy +305 common set_mempolicy sys_set_mempolicy compat_sys_set_mempolicy +306 common kexec_load sys_kexec_load compat_sys_kexec_load +307 common move_pages sys_move_pages compat_sys_move_pages +308 common getcpu sys_getcpu +309 common epoll_pwait sys_epoll_pwait compat_sys_epoll_pwait +310 32 utimensat sys_utimensat_time32 +310 64 utimensat sys_utimensat +311 common signalfd sys_signalfd compat_sys_signalfd +312 common timerfd_create sys_timerfd_create +313 common eventfd sys_eventfd +314 common fallocate sys_fallocate compat_sys_fallocate +315 32 timerfd_settime sys_timerfd_settime32 +315 64 timerfd_settime sys_timerfd_settime +316 32 timerfd_gettime sys_timerfd_gettime32 +316 64 timerfd_gettime sys_timerfd_gettime +317 common signalfd4 sys_signalfd4 compat_sys_signalfd4 +318 common eventfd2 sys_eventfd2 +319 common epoll_create1 sys_epoll_create1 +320 common dup3 sys_dup3 +321 common pipe2 sys_pipe2 +322 common inotify_init1 sys_inotify_init1 +323 common accept4 sys_accept4 +324 common preadv sys_preadv compat_sys_preadv +325 common pwritev sys_pwritev compat_sys_pwritev +326 common rt_tgsigqueueinfo sys_rt_tgsigqueueinfo compat_sys_rt_tgsigqueueinfo +327 common perf_event_open sys_perf_event_open +328 32 recvmmsg sys_recvmmsg_time32 compat_sys_recvmmsg_time32 +328 64 recvmmsg sys_recvmmsg +329 common fanotify_init sys_fanotify_init +330 common fanotify_mark sys_fanotify_mark compat_sys_fanotify_mark +331 common prlimit64 sys_prlimit64 +332 common name_to_handle_at sys_name_to_handle_at +333 common open_by_handle_at sys_open_by_handle_at compat_sys_open_by_handle_at +334 32 clock_adjtime sys_clock_adjtime32 +334 64 clock_adjtime sys_sparc_clock_adjtime +335 common syncfs sys_syncfs +336 common sendmmsg sys_sendmmsg compat_sys_sendmmsg +337 common setns sys_setns +338 common process_vm_readv sys_process_vm_readv compat_sys_process_vm_readv +339 common process_vm_writev sys_process_vm_writev compat_sys_process_vm_writev +340 32 kern_features sys_ni_syscall sys_kern_features +340 64 kern_features sys_kern_features +341 common kcmp sys_kcmp +342 common finit_module sys_finit_module +343 common sched_setattr sys_sched_setattr +344 common sched_getattr sys_sched_getattr +345 common renameat2 sys_renameat2 +346 common seccomp sys_seccomp +347 common getrandom sys_getrandom +348 common memfd_create sys_memfd_create +349 common bpf sys_bpf +350 32 execveat sys_execveat sys32_execveat +350 64 execveat sys64_execveat +351 common membarrier sys_membarrier +352 common userfaultfd sys_userfaultfd +353 common bind sys_bind +354 common listen sys_listen +355 common setsockopt sys_setsockopt compat_sys_setsockopt +356 common mlock2 sys_mlock2 +357 common copy_file_range sys_copy_file_range +358 common preadv2 sys_preadv2 compat_sys_preadv2 +359 common pwritev2 sys_pwritev2 compat_sys_pwritev2 +360 common statx sys_statx +361 32 io_pgetevents sys_io_pgetevents_time32 compat_sys_io_pgetevents +361 64 io_pgetevents sys_io_pgetevents +362 common pkey_mprotect sys_pkey_mprotect +363 common pkey_alloc sys_pkey_alloc +364 common pkey_free sys_pkey_free +365 common rseq sys_rseq +# room for arch specific syscalls +392 64 semtimedop sys_semtimedop +393 common semget sys_semget +394 common semctl sys_semctl compat_sys_semctl +395 common shmget sys_shmget +396 common shmctl sys_shmctl compat_sys_shmctl +397 common shmat sys_shmat compat_sys_shmat +398 common shmdt sys_shmdt +399 common msgget sys_msgget +400 common msgsnd sys_msgsnd compat_sys_msgsnd +401 common msgrcv sys_msgrcv compat_sys_msgrcv +402 common msgctl sys_msgctl compat_sys_msgctl +403 32 clock_gettime64 sys_clock_gettime sys_clock_gettime +404 32 clock_settime64 sys_clock_settime sys_clock_settime +405 32 clock_adjtime64 sys_clock_adjtime sys_clock_adjtime +406 32 clock_getres_time64 sys_clock_getres sys_clock_getres +407 32 clock_nanosleep_time64 sys_clock_nanosleep sys_clock_nanosleep +408 32 timer_gettime64 sys_timer_gettime sys_timer_gettime +409 32 timer_settime64 sys_timer_settime sys_timer_settime +410 32 timerfd_gettime64 sys_timerfd_gettime sys_timerfd_gettime +411 32 timerfd_settime64 sys_timerfd_settime sys_timerfd_settime +412 32 utimensat_time64 sys_utimensat sys_utimensat +413 32 pselect6_time64 sys_pselect6 compat_sys_pselect6_time64 +414 32 ppoll_time64 sys_ppoll compat_sys_ppoll_time64 +416 32 io_pgetevents_time64 sys_io_pgetevents sys_io_pgetevents +417 32 recvmmsg_time64 sys_recvmmsg compat_sys_recvmmsg_time64 +418 32 mq_timedsend_time64 sys_mq_timedsend sys_mq_timedsend +419 32 mq_timedreceive_time64 sys_mq_timedreceive sys_mq_timedreceive +420 32 semtimedop_time64 sys_semtimedop sys_semtimedop +421 32 rt_sigtimedwait_time64 sys_rt_sigtimedwait compat_sys_rt_sigtimedwait_time64 +422 32 futex_time64 sys_futex sys_futex +423 32 sched_rr_get_interval_time64 sys_sched_rr_get_interval sys_sched_rr_get_interval +424 common pidfd_send_signal sys_pidfd_send_signal +425 common io_uring_setup sys_io_uring_setup +426 common io_uring_enter sys_io_uring_enter +427 common io_uring_register sys_io_uring_register +428 common open_tree sys_open_tree +429 common move_mount sys_move_mount +430 common fsopen sys_fsopen +431 common fsconfig sys_fsconfig +432 common fsmount sys_fsmount +433 common fspick sys_fspick +434 common pidfd_open sys_pidfd_open +# 435 reserved for clone3 +437 common openat2 sys_openat2 +438 common pidfd_getfd sys_pidfd_getfd diff --git a/linux-user/sparc64/syscall_nr.h b/linux-user/sparc64/syscall_nr.h deleted file mode 100644 index 6b088c9862..0000000000 --- a/linux-user/sparc64/syscall_nr.h +++ /dev/null @@ -1,366 +0,0 @@ -#ifndef LINUX_USER_SPARC64_SYSCALL_NR_H -#define LINUX_USER_SPARC64_SYSCALL_NR_H - -#define TARGET_NR_restart_syscall 0 /* Linux Specific */ -#define TARGET_NR_exit 1 /* Common */ -#define TARGET_NR_fork 2 /* Common */ -#define TARGET_NR_read 3 /* Common */ -#define TARGET_NR_write 4 /* Common */ -#define TARGET_NR_open 5 /* Common */ -#define TARGET_NR_close 6 /* Common */ -#define TARGET_NR_wait4 7 /* Common */ -#define TARGET_NR_creat 8 /* Common */ -#define TARGET_NR_link 9 /* Common */ -#define TARGET_NR_unlink 10 /* Common */ -#define TARGET_NR_execv 11 /* SunOS Specific */ -#define TARGET_NR_chdir 12 /* Common */ -#define TARGET_NR_chown 13 /* Common */ -#define TARGET_NR_mknod 14 /* Common */ -#define TARGET_NR_chmod 15 /* Common */ -#define TARGET_NR_lchown 16 /* Common */ -#define TARGET_NR_brk 17 /* Common */ -#define TARGET_NR_perfctr 18 /* Performance counter operations */ -#define TARGET_NR_lseek 19 /* Common */ -#define TARGET_NR_getpid 20 /* Common */ -#define TARGET_NR_capget 21 /* Linux Specific */ -#define TARGET_NR_capset 22 /* Linux Specific */ -#define TARGET_NR_setuid 23 /* Implemented via setreuid in SunOS */ -#define TARGET_NR_getuid 24 /* Common */ -#define TARGET_NR_vmsplice 25 -#define TARGET_NR_ptrace 26 /* Common */ -#define TARGET_NR_alarm 27 /* Implemented via setitimer in SunOS */ -#define TARGET_NR_sigaltstack 28 /* Common */ -#define TARGET_NR_pause 29 /* Is sigblock(0)->sigpause() in SunOS */ -#define TARGET_NR_utime 30 /* Implemented via utimes() under SunOS */ -#define TARGET_NR_lchown32 31 /* Linux sparc32 specific */ -#define TARGET_NR_fchown32 32 /* Linux sparc32 specific */ -#define TARGET_NR_access 33 /* Common */ -#define TARGET_NR_nice 34 /* Implemented via get/setpriority() in SunOS */ -#define TARGET_NR_chown32 35 /* Linux sparc32 specific */ -#define TARGET_NR_sync 36 /* Common */ -#define TARGET_NR_kill 37 /* Common */ -#define TARGET_NR_stat 38 /* Common */ -#define TARGET_NR_sendfile 39 /* Linux Specific */ -#define TARGET_NR_lstat 40 /* Common */ -#define TARGET_NR_dup 41 /* Common */ -#define TARGET_NR_pipe 42 /* Common */ -#define TARGET_NR_times 43 /* Implemented via getrusage() in SunOS */ -#define TARGET_NR_getuid32 44 /* Linux sparc32 specific */ -#define TARGET_NR_umount2 45 /* Linux Specific */ -#define TARGET_NR_setgid 46 /* Implemented via setregid() in SunOS */ -#define TARGET_NR_getgid 47 /* Common */ -#define TARGET_NR_signal 48 /* Implemented via sigvec() in SunOS */ -#define TARGET_NR_geteuid 49 /* SunOS calls getuid() */ -#define TARGET_NR_getegid 50 /* SunOS calls getgid() */ -#define TARGET_NR_acct 51 /* Common */ -#define TARGET_NR_memory_ordering 52 /* Linux Specific */ -#define TARGET_NR_getgid32 53 /* Linux sparc32 specific */ -#define TARGET_NR_ioctl 54 /* Common */ -#define TARGET_NR_reboot 55 /* Common */ -#define TARGET_NR_mmap2 56 /* Linux sparc32 Specific */ -#define TARGET_NR_symlink 57 /* Common */ -#define TARGET_NR_readlink 58 /* Common */ -#define TARGET_NR_execve 59 /* Common */ -#define TARGET_NR_umask 60 /* Common */ -#define TARGET_NR_chroot 61 /* Common */ -#define TARGET_NR_fstat 62 /* Common */ -#define TARGET_NR_fstat64 63 /* Linux sparc32 Specific */ -#define TARGET_NR_getpagesize 64 /* Common */ -#define TARGET_NR_msync 65 /* Common in newer 1.3.x revs... */ -#define TARGET_NR_vfork 66 /* Common */ -#define TARGET_NR_pread64 67 /* Linux Specific */ -#define TARGET_NR_pwrite64 68 /* Linux Specific */ -#define TARGET_NR_geteuid32 69 /* Linux sparc32, sbrk under SunOS */ -#define TARGET_NR_getegid32 70 /* Linux sparc32, sstk under SunOS */ -#define TARGET_NR_mmap 71 /* Common */ -#define TARGET_NR_setreuid32 72 /* Linux sparc32, vadvise under SunOS */ -#define TARGET_NR_munmap 73 /* Common */ -#define TARGET_NR_mprotect 74 /* Common */ -#define TARGET_NR_madvise 75 /* Common */ -#define TARGET_NR_vhangup 76 /* Common */ -#define TARGET_NR_truncate64 77 /* Linux sparc32 Specific */ -#define TARGET_NR_mincore 78 /* Common */ -#define TARGET_NR_getgroups 79 /* Common */ -#define TARGET_NR_setgroups 80 /* Common */ -#define TARGET_NR_getpgrp 81 /* Common */ -#define TARGET_NR_setgroups32 82 /* Linux sparc32, setpgrp under SunOS */ -#define TARGET_NR_setitimer 83 /* Common */ -#define TARGET_NR_ftruncate64 84 /* Linux sparc32 Specific */ -#define TARGET_NR_swapon 85 /* Common */ -#define TARGET_NR_getitimer 86 /* Common */ -#define TARGET_NR_setuid32 87 /* Linux sparc32, gethostname under SunOS */ -#define TARGET_NR_sethostname 88 /* Common */ -#define TARGET_NR_setgid32 89 /* Linux sparc32, getdtablesize under SunOS */ -#define TARGET_NR_dup2 90 /* Common */ -#define TARGET_NR_setfsuid32 91 /* Linux sparc32, getdopt under SunOS */ -#define TARGET_NR_fcntl 92 /* Common */ -#define TARGET_NR_select 93 /* Common */ -#define TARGET_NR_setfsgid32 94 /* Linux sparc32, setdopt under SunOS */ -#define TARGET_NR_fsync 95 /* Common */ -#define TARGET_NR_setpriority 96 /* Common */ -#define TARGET_NR_socket 97 /* Common */ -#define TARGET_NR_connect 98 /* Common */ -#define TARGET_NR_accept 99 /* Common */ -#define TARGET_NR_getpriority 100 /* Common */ -#define TARGET_NR_rt_sigreturn 101 /* Linux Specific */ -#define TARGET_NR_rt_sigaction 102 /* Linux Specific */ -#define TARGET_NR_rt_sigprocmask 103 /* Linux Specific */ -#define TARGET_NR_rt_sigpending 104 /* Linux Specific */ -#define TARGET_NR_rt_sigtimedwait 105 /* Linux Specific */ -#define TARGET_NR_rt_sigqueueinfo 106 /* Linux Specific */ -#define TARGET_NR_rt_sigsuspend 107 /* Linux Specific */ -#define TARGET_NR_setresuid 108 /* Linux Specific, sigvec under SunOS */ -#define TARGET_NR_getresuid 109 /* Linux Specific, sigblock under SunOS */ -#define TARGET_NR_setresgid 110 /* Linux Specific, sigsetmask under SunOS */ -#define TARGET_NR_getresgid 111 /* Linux Specific, sigpause under SunOS */ -/* #define TARGET_NR_setregid32 75 Linux sparc32, sigstack under SunOS */ -#define TARGET_NR_recvmsg 113 /* Common */ -#define TARGET_NR_sendmsg 114 /* Common */ -#define TARGET_NR_getgroups32 115 /* Linux sparc32, vtrace under SunOS */ -#define TARGET_NR_gettimeofday 116 /* Common */ -#define TARGET_NR_getrusage 117 /* Common */ -#define TARGET_NR_getsockopt 118 /* Common */ -#define TARGET_NR_getcwd 119 /* Linux Specific */ -#define TARGET_NR_readv 120 /* Common */ -#define TARGET_NR_writev 121 /* Common */ -#define TARGET_NR_settimeofday 122 /* Common */ -#define TARGET_NR_fchown 123 /* Common */ -#define TARGET_NR_fchmod 124 /* Common */ -#define TARGET_NR_recvfrom 125 /* Common */ -#define TARGET_NR_setreuid 126 /* Common */ -#define TARGET_NR_setregid 127 /* Common */ -#define TARGET_NR_rename 128 /* Common */ -#define TARGET_NR_truncate 129 /* Common */ -#define TARGET_NR_ftruncate 130 /* Common */ -#define TARGET_NR_flock 131 /* Common */ -#define TARGET_NR_lstat64 132 /* Linux sparc32 Specific */ -#define TARGET_NR_sendto 133 /* Common */ -#define TARGET_NR_shutdown 134 /* Common */ -#define TARGET_NR_socketpair 135 /* Common */ -#define TARGET_NR_mkdir 136 /* Common */ -#define TARGET_NR_rmdir 137 /* Common */ -#define TARGET_NR_utimes 138 /* SunOS Specific */ -#define TARGET_NR_stat64 139 /* Linux sparc32 Specific */ -#define TARGET_NR_sendfile64 140 /* adjtime under SunOS */ -#define TARGET_NR_getpeername 141 /* Common */ -#define TARGET_NR_futex 142 /* gethostid under SunOS */ -#define TARGET_NR_gettid 143 /* ENOSYS under SunOS */ -#define TARGET_NR_getrlimit 144 /* Common */ -#define TARGET_NR_setrlimit 145 /* Common */ -#define TARGET_NR_pivot_root 146 /* Linux Specific, killpg under SunOS */ -#define TARGET_NR_prctl 147 /* ENOSYS under SunOS */ -#define TARGET_NR_pciconfig_read 148 /* ENOSYS under SunOS */ -#define TARGET_NR_pciconfig_write 149 /* ENOSYS under SunOS */ -#define TARGET_NR_getsockname 150 /* Common */ -#define TARGET_NR_inotify_init 151 -#define TARGET_NR_inotify_add_watch 152 -#define TARGET_NR_poll 153 /* Common */ -#define TARGET_NR_getdents64 154 /* Linux specific */ -#define TARGET_NR_fcntl64 155 /* Linux sparc32 Specific */ -#define TARGET_NR_inotify_rm_watch 156 /* Linux specific */ -#define TARGET_NR_statfs 157 /* Common */ -#define TARGET_NR_fstatfs 158 /* Common */ -#define TARGET_NR_umount 159 /* Common */ -#define TARGET_NR_sched_set_affinity 160 /* Linux specific, async_daemon under SunOS */ -#define TARGET_NR_sched_get_affinity 161 /* Linux specific, getfh under SunOS */ -#define TARGET_NR_getdomainname 162 /* SunOS Specific */ -#define TARGET_NR_setdomainname 163 /* Common */ -#define TARGET_NR_utrap_install 164 /* SYSV ABI/v9 required */ -#define TARGET_NR_quotactl 165 /* Common */ -#define TARGET_NR_set_tid_address 166 /* Linux specific, exportfs under SunOS */ -#define TARGET_NR_mount 167 /* Common */ -#define TARGET_NR_ustat 168 /* Common */ -#define TARGET_NR_setxattr 169 /* SunOS: semsys */ -#define TARGET_NR_lsetxattr 170 /* SunOS: msgsys */ -#define TARGET_NR_fsetxattr 171 /* SunOS: shmsys */ -#define TARGET_NR_getxattr 172 /* SunOS: auditsys */ -#define TARGET_NR_lgetxattr 173 /* SunOS: rfssys */ -#define TARGET_NR_getdents 174 /* Common */ -#define TARGET_NR_setsid 175 /* Common */ -#define TARGET_NR_fchdir 176 /* Common */ -#define TARGET_NR_fgetxattr 177 /* SunOS: fchroot */ -#define TARGET_NR_listxattr 178 /* SunOS: vpixsys */ -#define TARGET_NR_llistxattr 179 /* SunOS: aioread */ -#define TARGET_NR_flistxattr 180 /* SunOS: aiowrite */ -#define TARGET_NR_removexattr 181 /* SunOS: aiowait */ -#define TARGET_NR_lremovexattr 182 /* SunOS: aiocancel */ -#define TARGET_NR_sigpending 183 /* Common */ -#define TARGET_NR_query_module 184 /* Linux Specific */ -#define TARGET_NR_setpgid 185 /* Common */ -#define TARGET_NR_fremovexattr 186 /* SunOS: pathconf */ -#define TARGET_NR_tkill 187 /* SunOS: fpathconf */ -#define TARGET_NR_exit_group 188 /* Linux specific, sysconf undef SunOS */ -#define TARGET_NR_uname 189 /* Linux Specific */ -#define TARGET_NR_init_module 190 /* Linux Specific */ -#define TARGET_NR_personality 191 /* Linux Specific */ -#define TARGET_NR_remap_file_pages 192 /* Linux Specific */ -#define TARGET_NR_epoll_create 193 /* Linux Specific */ -#define TARGET_NR_epoll_ctl 194 /* Linux Specific */ -#define TARGET_NR_epoll_wait 195 /* Linux Specific */ -#define TARGET_NR_ioprio_set 196 -#define TARGET_NR_getppid 197 /* Linux Specific */ -#define TARGET_NR_sigaction 198 /* Linux Specific */ -#define TARGET_NR_sgetmask 199 /* Linux Specific */ -#define TARGET_NR_ssetmask 200 /* Linux Specific */ -#define TARGET_NR_sigsuspend 201 /* Linux Specific */ -#define TARGET_NR_oldlstat 202 /* Linux Specific */ -#define TARGET_NR_uselib 203 /* Linux Specific */ -#define TARGET_NR_readdir 204 /* Linux Specific */ -#define TARGET_NR_readahead 205 /* Linux Specific */ -#define TARGET_NR_socketcall 206 /* Linux Specific */ -#define TARGET_NR_syslog 207 /* Linux Specific */ -#define TARGET_NR_lookup_dcookie 208 /* Linux Specific */ -#define TARGET_NR_fadvise64 209 /* Linux Specific */ -#define TARGET_NR_fadvise64_64 210 /* Linux Specific */ -#define TARGET_NR_tgkill 211 /* Linux Specific */ -#define TARGET_NR_waitpid 212 /* Linux Specific */ -#define TARGET_NR_swapoff 213 /* Linux Specific */ -#define TARGET_NR_sysinfo 214 /* Linux Specific */ -#define TARGET_NR_ipc 215 /* Linux Specific */ -#define TARGET_NR_sigreturn 216 /* Linux Specific */ -#define TARGET_NR_clone 217 /* Linux Specific */ -#define TARGET_NR_ioprio_get 218 -#define TARGET_NR_adjtimex 219 /* Linux Specific */ -#define TARGET_NR_sigprocmask 220 /* Linux Specific */ -#define TARGET_NR_create_module 221 /* Linux Specific */ -#define TARGET_NR_delete_module 222 /* Linux Specific */ -#define TARGET_NR_get_kernel_syms 223 /* Linux Specific */ -#define TARGET_NR_getpgid 224 /* Linux Specific */ -#define TARGET_NR_bdflush 225 /* Linux Specific */ -#define TARGET_NR_sysfs 226 /* Linux Specific */ -#define TARGET_NR_afs_syscall 227 /* Linux Specific */ -#define TARGET_NR_setfsuid 228 /* Linux Specific */ -#define TARGET_NR_setfsgid 229 /* Linux Specific */ -#define TARGET_NR__newselect 230 /* Linux Specific */ -#define TARGET_NR_time 231 /* Linux sparc32 */ -#define TARGET_NR_splice 232 -#define TARGET_NR_stime 233 /* Linux Specific */ -#define TARGET_NR_statfs64 234 /* Linux Specific */ -#define TARGET_NR_fstatfs64 235 /* Linux Specific */ -#define TARGET_NR__llseek 236 /* Linux Specific */ -#define TARGET_NR_mlock 237 -#define TARGET_NR_munlock 238 -#define TARGET_NR_mlockall 239 -#define TARGET_NR_munlockall 240 -#define TARGET_NR_sched_setparam 241 -#define TARGET_NR_sched_getparam 242 -#define TARGET_NR_sched_setscheduler 243 -#define TARGET_NR_sched_getscheduler 244 -#define TARGET_NR_sched_yield 245 -#define TARGET_NR_sched_get_priority_max 246 -#define TARGET_NR_sched_get_priority_min 247 -#define TARGET_NR_sched_rr_get_interval 248 -#define TARGET_NR_nanosleep 249 -#define TARGET_NR_mremap 250 -#define TARGET_NR__sysctl 251 -#define TARGET_NR_getsid 252 -#define TARGET_NR_fdatasync 253 -#define TARGET_NR_nfsservctl 254 -#define TARGET_NR_sync_file_range 255 -#define TARGET_NR_clock_settime 256 -#define TARGET_NR_clock_gettime 257 -#define TARGET_NR_clock_getres 258 -#define TARGET_NR_clock_nanosleep 259 -#define TARGET_NR_sched_getaffinity 260 -#define TARGET_NR_sched_setaffinity 261 -#define TARGET_NR_timer_settime 262 -#define TARGET_NR_timer_gettime 263 -#define TARGET_NR_timer_getoverrun 264 -#define TARGET_NR_timer_delete 265 -#define TARGET_NR_timer_create 266 -/* #define TARGET_NR_vserver 267 Reserved for VSERVER */ -#define TARGET_NR_io_setup 268 -#define TARGET_NR_io_destroy 269 -#define TARGET_NR_io_submit 270 -#define TARGET_NR_io_cancel 271 -#define TARGET_NR_io_getevents 272 -#define TARGET_NR_mq_open 273 -#define TARGET_NR_mq_unlink 274 -#define TARGET_NR_mq_timedsend 275 -#define TARGET_NR_mq_timedreceive 276 -#define TARGET_NR_mq_notify 277 -#define TARGET_NR_mq_getsetattr 278 -#define TARGET_NR_waitid 279 -#define TARGET_NR_tee 280 -#define TARGET_NR_add_key 281 -#define TARGET_NR_request_key 282 -#define TARGET_NR_keyctl 283 -#define TARGET_NR_openat 284 -#define TARGET_NR_mkdirat 285 -#define TARGET_NR_mknodat 286 -#define TARGET_NR_fchownat 287 -#define TARGET_NR_futimesat 288 -#define TARGET_NR_fstatat64 289 -#define TARGET_NR_unlinkat 290 -#define TARGET_NR_renameat 291 -#define TARGET_NR_linkat 292 -#define TARGET_NR_symlinkat 293 -#define TARGET_NR_readlinkat 294 -#define TARGET_NR_fchmodat 295 -#define TARGET_NR_faccessat 296 -#define TARGET_NR_pselect6 297 -#define TARGET_NR_ppoll 298 -#define TARGET_NR_unshare 299 -#define TARGET_NR_set_robust_list 300 -#define TARGET_NR_get_robust_list 301 -#define TARGET_NR_migrate_pages 302 -#define TARGET_NR_mbind 303 -#define TARGET_NR_get_mempolicy 304 -#define TARGET_NR_set_mempolicy 305 -#define TARGET_NR_kexec_load 306 -#define TARGET_NR_move_pages 307 -#define TARGET_NR_getcpu 308 -#define TARGET_NR_epoll_pwait 309 -#define TARGET_NR_utimensat 310 -#define TARGET_NR_signalfd 311 -#define TARGET_NR_timerfd_create 312 -#define TARGET_NR_eventfd 313 -#define TARGET_NR_fallocate 314 -#define TARGET_NR_timerfd_settime 315 -#define TARGET_NR_timerfd_gettime 316 -#define TARGET_NR_signalfd4 317 -#define TARGET_NR_eventfd2 318 -#define TARGET_NR_epoll_create1 319 -#define TARGET_NR_dup3 320 -#define TARGET_NR_pipe2 321 -#define TARGET_NR_inotify_init1 322 -#define TARGET_NR_accept4 323 -#define TARGET_NR_preadv 324 -#define TARGET_NR_pwritev 325 -#define TARGET_NR_rt_tgsigqueueinfo 326 -#define TARGET_NR_perf_event_open 327 -#define TARGET_NR_recvmmsg 328 -#define TARGET_NR_fanotify_init 329 -#define TARGET_NR_fanotify_mark 330 -#define TARGET_NR_prlimit64 331 -#define TARGET_NR_name_to_handle_at 332 -#define TARGET_NR_open_by_handle_at 333 -#define TARGET_NR_clock_adjtime 334 -#define TARGET_NR_syncfs 335 -#define TARGET_NR_sendmmsg 336 -#define TARGET_NR_setns 337 -#define TARGET_NR_process_vm_readv 338 -#define TARGET_NR_process_vm_writev 339 -#define TARGET_NR_kern_features 340 -#define TARGET_NR_kcmp 341 -#define TARGET_NR_finit_module 342 -#define TARGET_NR_sched_setattr 343 -#define TARGET_NR_sched_getattr 344 -#define TARGET_NR_renameat2 345 -#define TARGET_NR_seccomp 346 -#define TARGET_NR_getrandom 347 -#define TARGET_NR_memfd_create 348 -#define TARGET_NR_bpf 349 -#define TARGET_NR_execveat 350 -#define TARGET_NR_membarrier 351 -#define TARGET_NR_userfaultfd 352 -#define TARGET_NR_bind 353 -#define TARGET_NR_listen 354 -#define TARGET_NR_setsockopt 355 -#define TARGET_NR_mlock2 356 -#define TARGET_NR_copy_file_range 357 -#define TARGET_NR_preadv2 358 -#define TARGET_NR_pwritev2 359 -#define TARGET_NR_statx 360 - -#endif diff --git a/linux-user/sparc64/syscallhdr.sh b/linux-user/sparc64/syscallhdr.sh new file mode 100644 index 0000000000..08c7e39bb3 --- /dev/null +++ b/linux-user/sparc64/syscallhdr.sh @@ -0,0 +1,32 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 + +in="$1" +out="$2" +my_abis=`echo "($3)" | tr ',' '|'` +prefix="$4" +offset="$5" + +fileguard=LINUX_USER_SPARC64_`basename "$out" | sed \ + -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/' \ + -e 's/[^A-Z0-9_]/_/g' -e 's/__/_/g'` +grep -E "^[0-9A-Fa-fXx]+[[:space:]]+${my_abis}" "$in" | sort -n | ( + printf "#ifndef %s\n" "${fileguard}" + printf "#define %s\n" "${fileguard}" + printf "\n" + + nxt=0 + while read nr abi name entry compat ; do + if [ -z "$offset" ]; then + printf "#define TARGET_NR_%s%s\t%s\n" \ + "${prefix}" "${name}" "${nr}" + else + printf "#define TARGET_NR_%s%s\t(%s + %s)\n" \ + "${prefix}" "${name}" "${offset}" "${nr}" + fi + nxt=$((nr+1)) + done + + printf "\n" + printf "#endif /* %s */" "${fileguard}" +) > "$out" diff --git a/linux-user/strace.c b/linux-user/strace.c index 3d4d684450..0d9095c674 100644 --- a/linux-user/strace.c +++ b/linux-user/strace.c @@ -12,8 +12,6 @@ #include #include "qemu.h" -int do_strace=0; - struct syscallname { int nr; const char *name; @@ -80,7 +78,7 @@ print_ipc_cmd(int cmd) { #define output_cmd(val) \ if( cmd == val ) { \ - gemu_log(#val); \ + qemu_log(#val); \ return; \ } @@ -120,7 +118,7 @@ if( cmd == val ) { \ output_cmd( IPC_RMID ); /* Some value we don't recognize */ - gemu_log("%d",cmd); + qemu_log("%d", cmd); } static void @@ -151,7 +149,7 @@ print_signal(abi_ulong arg, int last) print_raw_param("%ld", arg, last); return; } - gemu_log("%s%s", signal_name, get_comma(last)); + qemu_log("%s%s", signal_name, get_comma(last)); } static void print_si_code(int arg) @@ -184,10 +182,10 @@ static void print_si_code(int arg) codename = "SI_TKILL"; break; default: - gemu_log("%d", arg); + qemu_log("%d", arg); return; } - gemu_log("%s", codename); + qemu_log("%s", codename); } static void get_target_siginfo(target_siginfo_t *tinfo, @@ -288,33 +286,33 @@ static void print_siginfo(const target_siginfo_t *tinfo) int si_type = extract32(tinfo->si_code, 16, 16); int si_code = sextract32(tinfo->si_code, 0, 16); - gemu_log("{si_signo="); + qemu_log("{si_signo="); print_signal(tinfo->si_signo, 1); - gemu_log(", si_code="); + qemu_log(", si_code="); print_si_code(si_code); switch (si_type) { case QEMU_SI_KILL: - gemu_log(", si_pid=%u, si_uid=%u", + qemu_log(", si_pid=%u, si_uid=%u", (unsigned int)tinfo->_sifields._kill._pid, (unsigned int)tinfo->_sifields._kill._uid); break; case QEMU_SI_TIMER: - gemu_log(", si_timer1=%u, si_timer2=%u", + qemu_log(", si_timer1=%u, si_timer2=%u", tinfo->_sifields._timer._timer1, tinfo->_sifields._timer._timer2); break; case QEMU_SI_POLL: - gemu_log(", si_band=%d, si_fd=%d", + qemu_log(", si_band=%d, si_fd=%d", tinfo->_sifields._sigpoll._band, tinfo->_sifields._sigpoll._fd); break; case QEMU_SI_FAULT: - gemu_log(", si_addr="); + qemu_log(", si_addr="); print_pointer(tinfo->_sifields._sigfault._addr, 1); break; case QEMU_SI_CHLD: - gemu_log(", si_pid=%u, si_uid=%u, si_status=%d" + qemu_log(", si_pid=%u, si_uid=%u, si_status=%d" ", si_utime=" TARGET_ABI_FMT_ld ", si_stime=" TARGET_ABI_FMT_ld, (unsigned int)(tinfo->_sifields._sigchld._pid), @@ -324,7 +322,7 @@ static void print_siginfo(const target_siginfo_t *tinfo) tinfo->_sifields._sigchld._stime); break; case QEMU_SI_RT: - gemu_log(", si_pid=%u, si_uid=%u, si_sigval=" TARGET_ABI_FMT_ld, + qemu_log(", si_pid=%u, si_uid=%u, si_sigval=" TARGET_ABI_FMT_ld, (unsigned int)tinfo->_sifields._rt._pid, (unsigned int)tinfo->_sifields._rt._uid, tinfo->_sifields._rt._sigval.sival_ptr); @@ -332,7 +330,7 @@ static void print_siginfo(const target_siginfo_t *tinfo) default: g_assert_not_reached(); } - gemu_log("}"); + qemu_log("}"); } static void @@ -349,76 +347,76 @@ print_sockaddr(abi_ulong addr, abi_long addrlen, int last) case AF_UNIX: { struct target_sockaddr_un *un = (struct target_sockaddr_un *)sa; int i; - gemu_log("{sun_family=AF_UNIX,sun_path=\""); + qemu_log("{sun_family=AF_UNIX,sun_path=\""); for (i = 0; i < addrlen - offsetof(struct target_sockaddr_un, sun_path) && un->sun_path[i]; i++) { - gemu_log("%c", un->sun_path[i]); + qemu_log("%c", un->sun_path[i]); } - gemu_log("\"}"); + qemu_log("\"}"); break; } case AF_INET: { struct target_sockaddr_in *in = (struct target_sockaddr_in *)sa; uint8_t *c = (uint8_t *)&in->sin_addr.s_addr; - gemu_log("{sin_family=AF_INET,sin_port=htons(%d),", + qemu_log("{sin_family=AF_INET,sin_port=htons(%d),", ntohs(in->sin_port)); - gemu_log("sin_addr=inet_addr(\"%d.%d.%d.%d\")", + qemu_log("sin_addr=inet_addr(\"%d.%d.%d.%d\")", c[0], c[1], c[2], c[3]); - gemu_log("}"); + qemu_log("}"); break; } case AF_PACKET: { struct target_sockaddr_ll *ll = (struct target_sockaddr_ll *)sa; uint8_t *c = (uint8_t *)&ll->sll_addr; - gemu_log("{sll_family=AF_PACKET," + qemu_log("{sll_family=AF_PACKET," "sll_protocol=htons(0x%04x),if%d,pkttype=", ntohs(ll->sll_protocol), ll->sll_ifindex); switch (ll->sll_pkttype) { case PACKET_HOST: - gemu_log("PACKET_HOST"); + qemu_log("PACKET_HOST"); break; case PACKET_BROADCAST: - gemu_log("PACKET_BROADCAST"); + qemu_log("PACKET_BROADCAST"); break; case PACKET_MULTICAST: - gemu_log("PACKET_MULTICAST"); + qemu_log("PACKET_MULTICAST"); break; case PACKET_OTHERHOST: - gemu_log("PACKET_OTHERHOST"); + qemu_log("PACKET_OTHERHOST"); break; case PACKET_OUTGOING: - gemu_log("PACKET_OUTGOING"); + qemu_log("PACKET_OUTGOING"); break; default: - gemu_log("%d", ll->sll_pkttype); + qemu_log("%d", ll->sll_pkttype); break; } - gemu_log(",sll_addr=%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", + qemu_log(",sll_addr=%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", c[0], c[1], c[2], c[3], c[4], c[5], c[6], c[7]); - gemu_log("}"); + qemu_log("}"); break; } case AF_NETLINK: { struct target_sockaddr_nl *nl = (struct target_sockaddr_nl *)sa; - gemu_log("{nl_family=AF_NETLINK,nl_pid=%u,nl_groups=%u}", + qemu_log("{nl_family=AF_NETLINK,nl_pid=%u,nl_groups=%u}", tswap32(nl->nl_pid), tswap32(nl->nl_groups)); break; } default: - gemu_log("{sa_family=%d, sa_data={", sa->sa_family); + qemu_log("{sa_family=%d, sa_data={", sa->sa_family); for (i = 0; i < 13; i++) { - gemu_log("%02x, ", sa->sa_data[i]); + qemu_log("%02x, ", sa->sa_data[i]); } - gemu_log("%02x}", sa->sa_data[i]); - gemu_log("}"); + qemu_log("%02x}", sa->sa_data[i]); + qemu_log("}"); break; } unlock_user(sa, addr, 0); } else { print_raw_param("0x"TARGET_ABI_FMT_lx, addr, 0); } - gemu_log(", "TARGET_ABI_FMT_ld"%s", addrlen, get_comma(last)); + qemu_log(", "TARGET_ABI_FMT_ld"%s", addrlen, get_comma(last)); } static void @@ -426,19 +424,19 @@ print_socket_domain(int domain) { switch (domain) { case PF_UNIX: - gemu_log("PF_UNIX"); + qemu_log("PF_UNIX"); break; case PF_INET: - gemu_log("PF_INET"); + qemu_log("PF_INET"); break; case PF_NETLINK: - gemu_log("PF_NETLINK"); + qemu_log("PF_NETLINK"); break; case PF_PACKET: - gemu_log("PF_PACKET"); + qemu_log("PF_PACKET"); break; default: - gemu_log("%d", domain); + qemu_log("%d", domain); break; } } @@ -446,26 +444,32 @@ print_socket_domain(int domain) static void print_socket_type(int type) { - switch (type) { + switch (type & TARGET_SOCK_TYPE_MASK) { case TARGET_SOCK_DGRAM: - gemu_log("SOCK_DGRAM"); + qemu_log("SOCK_DGRAM"); break; case TARGET_SOCK_STREAM: - gemu_log("SOCK_STREAM"); + qemu_log("SOCK_STREAM"); break; case TARGET_SOCK_RAW: - gemu_log("SOCK_RAW"); + qemu_log("SOCK_RAW"); break; case TARGET_SOCK_RDM: - gemu_log("SOCK_RDM"); + qemu_log("SOCK_RDM"); break; case TARGET_SOCK_SEQPACKET: - gemu_log("SOCK_SEQPACKET"); + qemu_log("SOCK_SEQPACKET"); break; case TARGET_SOCK_PACKET: - gemu_log("SOCK_PACKET"); + qemu_log("SOCK_PACKET"); break; } + if (type & TARGET_SOCK_CLOEXEC) { + qemu_log("|SOCK_CLOEXEC"); + } + if (type & TARGET_SOCK_NONBLOCK) { + qemu_log("|SOCK_NONBLOCK"); + } } static void @@ -475,10 +479,10 @@ print_socket_protocol(int domain, int type, int protocol) (domain == AF_INET && type == TARGET_SOCK_PACKET)) { switch (protocol) { case 0x0003: - gemu_log("ETH_P_ALL"); + qemu_log("ETH_P_ALL"); break; default: - gemu_log("%d", protocol); + qemu_log("%d", protocol); } return; } @@ -486,25 +490,25 @@ print_socket_protocol(int domain, int type, int protocol) if (domain == PF_NETLINK) { switch (protocol) { case NETLINK_ROUTE: - gemu_log("NETLINK_ROUTE"); + qemu_log("NETLINK_ROUTE"); break; case NETLINK_AUDIT: - gemu_log("NETLINK_AUDIT"); + qemu_log("NETLINK_AUDIT"); break; case NETLINK_NETFILTER: - gemu_log("NETLINK_NETFILTER"); + qemu_log("NETLINK_NETFILTER"); break; case NETLINK_KOBJECT_UEVENT: - gemu_log("NETLINK_KOBJECT_UEVENT"); + qemu_log("NETLINK_KOBJECT_UEVENT"); break; case NETLINK_RDMA: - gemu_log("NETLINK_RDMA"); + qemu_log("NETLINK_RDMA"); break; case NETLINK_CRYPTO: - gemu_log("NETLINK_CRYPTO"); + qemu_log("NETLINK_CRYPTO"); break; default: - gemu_log("%d", protocol); + qemu_log("%d", protocol); break; } return; @@ -512,19 +516,19 @@ print_socket_protocol(int domain, int type, int protocol) switch (protocol) { case IPPROTO_IP: - gemu_log("IPPROTO_IP"); + qemu_log("IPPROTO_IP"); break; case IPPROTO_TCP: - gemu_log("IPPROTO_TCP"); + qemu_log("IPPROTO_TCP"); break; case IPPROTO_UDP: - gemu_log("IPPROTO_UDP"); + qemu_log("IPPROTO_UDP"); break; case IPPROTO_RAW: - gemu_log("IPPROTO_RAW"); + qemu_log("IPPROTO_RAW"); break; default: - gemu_log("%d", protocol); + qemu_log("%d", protocol); break; } } @@ -536,7 +540,7 @@ print_fdset(int n, abi_ulong target_fds_addr) { int i; - gemu_log("["); + qemu_log("["); if( target_fds_addr ) { abi_long *target_fds; @@ -550,11 +554,11 @@ print_fdset(int n, abi_ulong target_fds_addr) for (i=n; i>=0; i--) { if ((tswapal(target_fds[i / TARGET_ABI_BITS]) >> (i & (TARGET_ABI_BITS - 1))) & 1) - gemu_log("%d,", i ); + qemu_log("%d,", i); } unlock_user(target_fds, target_fds_addr, 0); } - gemu_log("]"); + qemu_log("]"); } #endif @@ -578,46 +582,46 @@ print_clockid(int clockid, int last) { switch (clockid) { case TARGET_CLOCK_REALTIME: - gemu_log("CLOCK_REALTIME"); + qemu_log("CLOCK_REALTIME"); break; case TARGET_CLOCK_MONOTONIC: - gemu_log("CLOCK_MONOTONIC"); + qemu_log("CLOCK_MONOTONIC"); break; case TARGET_CLOCK_PROCESS_CPUTIME_ID: - gemu_log("CLOCK_PROCESS_CPUTIME_ID"); + qemu_log("CLOCK_PROCESS_CPUTIME_ID"); break; case TARGET_CLOCK_THREAD_CPUTIME_ID: - gemu_log("CLOCK_THREAD_CPUTIME_ID"); + qemu_log("CLOCK_THREAD_CPUTIME_ID"); break; case TARGET_CLOCK_MONOTONIC_RAW: - gemu_log("CLOCK_MONOTONIC_RAW"); + qemu_log("CLOCK_MONOTONIC_RAW"); break; case TARGET_CLOCK_REALTIME_COARSE: - gemu_log("CLOCK_REALTIME_COARSE"); + qemu_log("CLOCK_REALTIME_COARSE"); break; case TARGET_CLOCK_MONOTONIC_COARSE: - gemu_log("CLOCK_MONOTONIC_COARSE"); + qemu_log("CLOCK_MONOTONIC_COARSE"); break; case TARGET_CLOCK_BOOTTIME: - gemu_log("CLOCK_BOOTTIME"); + qemu_log("CLOCK_BOOTTIME"); break; case TARGET_CLOCK_REALTIME_ALARM: - gemu_log("CLOCK_REALTIME_ALARM"); + qemu_log("CLOCK_REALTIME_ALARM"); break; case TARGET_CLOCK_BOOTTIME_ALARM: - gemu_log("CLOCK_BOOTTIME_ALARM"); + qemu_log("CLOCK_BOOTTIME_ALARM"); break; case TARGET_CLOCK_SGI_CYCLE: - gemu_log("CLOCK_SGI_CYCLE"); + qemu_log("CLOCK_SGI_CYCLE"); break; case TARGET_CLOCK_TAI: - gemu_log("CLOCK_TAI"); + qemu_log("CLOCK_TAI"); break; default: - gemu_log("%d", clockid); + qemu_log("%d", clockid); break; } - gemu_log("%s", get_comma(last)); + qemu_log("%s", get_comma(last)); } #endif @@ -638,15 +642,15 @@ print_newselect(const struct syscallname *name, abi_long arg1, abi_long arg2, abi_long arg3, abi_long arg4, abi_long arg5, abi_long arg6) { - gemu_log("%s(" TARGET_ABI_FMT_ld ",", name->name, arg1); + qemu_log("%s(" TARGET_ABI_FMT_ld ",", name->name, arg1); print_fdset(arg1, arg2); - gemu_log(","); + qemu_log(","); print_fdset(arg1, arg3); - gemu_log(","); + qemu_log(","); print_fdset(arg1, arg4); - gemu_log(","); + qemu_log(","); print_timeval(arg5, 1); - gemu_log(")"); + qemu_log(")"); /* save for use in the return output function below */ newselect_arg1=arg1; @@ -663,9 +667,10 @@ print_semctl(const struct syscallname *name, abi_long arg1, abi_long arg2, abi_long arg3, abi_long arg4, abi_long arg5, abi_long arg6) { - gemu_log("%s(" TARGET_ABI_FMT_ld "," TARGET_ABI_FMT_ld ",", name->name, arg1, arg2); + qemu_log("%s(" TARGET_ABI_FMT_ld "," TARGET_ABI_FMT_ld ",", + name->name, arg1, arg2); print_ipc_cmd(arg3); - gemu_log(",0x" TARGET_ABI_FMT_lx ")", arg4); + qemu_log(",0x" TARGET_ABI_FMT_lx ")", arg4); } #endif @@ -679,7 +684,7 @@ print_execve(const struct syscallname *name, if (!(s = lock_user_string(arg1))) return; - gemu_log("%s(\"%s\",{", name->name, s); + qemu_log("%s(\"%s\",{", name->name, s); unlock_user(s, arg1, 0); for (arg_ptr_addr = arg2; ; arg_ptr_addr += sizeof(abi_ulong)) { @@ -693,12 +698,12 @@ print_execve(const struct syscallname *name, if (!arg_addr) break; if ((s = lock_user_string(arg_addr))) { - gemu_log("\"%s\",", s); + qemu_log("\"%s\",", s); unlock_user(s, arg_addr, 0); } } - gemu_log("NULL})"); + qemu_log("NULL})"); } #ifdef TARGET_NR_ipc @@ -709,12 +714,18 @@ print_ipc(const struct syscallname *name, { switch(arg1) { case IPCOP_semctl: - gemu_log("semctl(" TARGET_ABI_FMT_ld "," TARGET_ABI_FMT_ld ",", arg1, arg2); + qemu_log("semctl(" TARGET_ABI_FMT_ld "," TARGET_ABI_FMT_ld ",", + arg1, arg2); print_ipc_cmd(arg3); - gemu_log(",0x" TARGET_ABI_FMT_lx ")", arg4); + qemu_log(",0x" TARGET_ABI_FMT_lx ")", arg4); break; default: - gemu_log("%s(" TARGET_ABI_FMT_ld "," TARGET_ABI_FMT_ld "," TARGET_ABI_FMT_ld "," TARGET_ABI_FMT_ld ")", + qemu_log(("%s(" + TARGET_ABI_FMT_ld "," + TARGET_ABI_FMT_ld "," + TARGET_ABI_FMT_ld "," + TARGET_ABI_FMT_ld + ")"), name->name, arg1, arg2, arg3, arg4); } } @@ -733,9 +744,9 @@ print_syscall_ret_addr(const struct syscallname *name, abi_long ret) errstr = target_strerror(-ret); } if (errstr) { - gemu_log(" = -1 errno=%d (%s)\n", (int)-ret, errstr); + qemu_log(" = -1 errno=%d (%s)\n", (int)-ret, errstr); } else { - gemu_log(" = 0x" TARGET_ABI_FMT_lx "\n", ret); + qemu_log(" = 0x" TARGET_ABI_FMT_lx "\n", ret); } } @@ -743,7 +754,7 @@ print_syscall_ret_addr(const struct syscallname *name, abi_long ret) static void print_syscall_ret_raw(struct syscallname *name, abi_long ret) { - gemu_log(" = 0x" TARGET_ABI_FMT_lx "\n", ret); + qemu_log(" = 0x" TARGET_ABI_FMT_lx "\n", ret); } #endif @@ -751,15 +762,15 @@ print_syscall_ret_raw(struct syscallname *name, abi_long ret) static void print_syscall_ret_newselect(const struct syscallname *name, abi_long ret) { - gemu_log(" = 0x" TARGET_ABI_FMT_lx " (", ret); + qemu_log(" = 0x" TARGET_ABI_FMT_lx " (", ret); print_fdset(newselect_arg1,newselect_arg2); - gemu_log(","); + qemu_log(","); print_fdset(newselect_arg1,newselect_arg3); - gemu_log(","); + qemu_log(","); print_fdset(newselect_arg1,newselect_arg4); - gemu_log(","); + qemu_log(","); print_timeval(newselect_arg5, 1); - gemu_log(")\n"); + qemu_log(")\n"); } #endif @@ -770,44 +781,46 @@ print_syscall_ret_newselect(const struct syscallname *name, abi_long ret) #define TARGET_TIME_OOP 3 /* leap second in progress */ #define TARGET_TIME_WAIT 4 /* leap second has occurred */ #define TARGET_TIME_ERROR 5 /* clock not synchronized */ +#ifdef TARGET_NR_adjtimex static void print_syscall_ret_adjtimex(const struct syscallname *name, abi_long ret) { const char *errstr = NULL; - gemu_log(" = "); + qemu_log(" = "); if (ret < 0) { - gemu_log("-1 errno=%d", errno); + qemu_log("-1 errno=%d", errno); errstr = target_strerror(-ret); if (errstr) { - gemu_log(" (%s)", errstr); + qemu_log(" (%s)", errstr); } } else { - gemu_log(TARGET_ABI_FMT_ld, ret); + qemu_log(TARGET_ABI_FMT_ld, ret); switch (ret) { case TARGET_TIME_OK: - gemu_log(" TIME_OK (clock synchronized, no leap second)"); + qemu_log(" TIME_OK (clock synchronized, no leap second)"); break; case TARGET_TIME_INS: - gemu_log(" TIME_INS (insert leap second)"); + qemu_log(" TIME_INS (insert leap second)"); break; case TARGET_TIME_DEL: - gemu_log(" TIME_DEL (delete leap second)"); + qemu_log(" TIME_DEL (delete leap second)"); break; case TARGET_TIME_OOP: - gemu_log(" TIME_OOP (leap second in progress)"); + qemu_log(" TIME_OOP (leap second in progress)"); break; case TARGET_TIME_WAIT: - gemu_log(" TIME_WAIT (leap second has occurred)"); + qemu_log(" TIME_WAIT (leap second has occurred)"); break; case TARGET_TIME_ERROR: - gemu_log(" TIME_ERROR (clock not synchronized)"); + qemu_log(" TIME_ERROR (clock not synchronized)"); break; } } - gemu_log("\n"); + qemu_log("\n"); } +#endif UNUSED static struct flags access_flags[] = { FLAG_GENERIC(F_OK), @@ -1104,12 +1117,12 @@ print_flags(const struct flags *f, abi_long flags, int last) int n; if ((flags == 0) && (f->f_value == 0)) { - gemu_log("%s%s", f->f_string, get_comma(last)); + qemu_log("%s%s", f->f_string, get_comma(last)); return; } for (n = 0; f->f_string != NULL; f++) { if ((f->f_value != 0) && ((flags & f->f_value) == f->f_value)) { - gemu_log("%s%s", sep, f->f_string); + qemu_log("%s%s", sep, f->f_string); flags &= ~f->f_value; sep = "|"; n++; @@ -1119,13 +1132,13 @@ print_flags(const struct flags *f, abi_long flags, int last) if (n > 0) { /* print rest of the flags as numeric */ if (flags != 0) { - gemu_log("%s%#x%s", sep, (unsigned int)flags, get_comma(last)); + qemu_log("%s%#x%s", sep, (unsigned int)flags, get_comma(last)); } else { - gemu_log("%s", get_comma(last)); + qemu_log("%s", get_comma(last)); } } else { /* no string version of flags found, print them in hex then */ - gemu_log("%#x%s", (unsigned int)flags, get_comma(last)); + qemu_log("%#x%s", (unsigned int)flags, get_comma(last)); } } @@ -1134,11 +1147,11 @@ print_at_dirfd(abi_long dirfd, int last) { #ifdef AT_FDCWD if (dirfd == AT_FDCWD) { - gemu_log("AT_FDCWD%s", get_comma(last)); + qemu_log("AT_FDCWD%s", get_comma(last)); return; } #endif - gemu_log("%d%s", (int)dirfd, get_comma(last)); + qemu_log("%d%s", (int)dirfd, get_comma(last)); } static void @@ -1149,7 +1162,7 @@ print_file_mode(abi_long mode, int last) for (m = &mode_flags[0]; m->f_string != NULL; m++) { if ((m->f_value & mode) == m->f_value) { - gemu_log("%s%s", m->f_string, sep); + qemu_log("%s%s", m->f_string, sep); sep = "|"; mode &= ~m->f_value; break; @@ -1159,9 +1172,9 @@ print_file_mode(abi_long mode, int last) mode &= ~S_IFMT; /* print rest of the mode as octal */ if (mode != 0) - gemu_log("%s%#o", sep, (unsigned int)mode); + qemu_log("%s%#o", sep, (unsigned int)mode); - gemu_log("%s", get_comma(last)); + qemu_log("%s", get_comma(last)); } static void @@ -1170,17 +1183,17 @@ print_open_flags(abi_long flags, int last) print_flags(open_access_flags, flags & TARGET_O_ACCMODE, 1); flags &= ~TARGET_O_ACCMODE; if (flags == 0) { - gemu_log("%s", get_comma(last)); + qemu_log("%s", get_comma(last)); return; } - gemu_log("|"); + qemu_log("|"); print_flags(open_flags, flags, last); } static void print_syscall_prologue(const struct syscallname *sc) { - gemu_log("%s(", sc->name); + qemu_log("%s(", sc->name); } /*ARGSUSED*/ @@ -1188,7 +1201,7 @@ static void print_syscall_epilogue(const struct syscallname *sc) { (void)sc; - gemu_log(")"); + qemu_log(")"); } static void @@ -1197,7 +1210,7 @@ print_string(abi_long addr, int last) char *s; if ((s = lock_user_string(addr)) != NULL) { - gemu_log("\"%s\"%s", s, get_comma(last)); + qemu_log("\"%s\"%s", s, get_comma(last)); unlock_user(s, addr, 0); } else { /* can't get string out of it, so print it as pointer */ @@ -1214,20 +1227,20 @@ print_buf(abi_long addr, abi_long len, int last) s = lock_user(VERIFY_READ, addr, len, 1); if (s) { - gemu_log("\""); + qemu_log("\""); for (i = 0; i < MAX_PRINT_BUF && i < len; i++) { if (isprint(s[i])) { - gemu_log("%c", s[i]); + qemu_log("%c", s[i]); } else { - gemu_log("\\%o", s[i]); + qemu_log("\\%o", s[i]); } } - gemu_log("\""); + qemu_log("\""); if (i != len) { - gemu_log("..."); + qemu_log("..."); } if (!last) { - gemu_log(","); + qemu_log(","); } unlock_user(s, addr, 0); } else { @@ -1245,16 +1258,16 @@ print_raw_param(const char *fmt, abi_long param, int last) char format[64]; (void) snprintf(format, sizeof (format), "%s%s", fmt, get_comma(last)); - gemu_log(format, param); + qemu_log(format, param); } static void print_pointer(abi_long p, int last) { if (p == 0) - gemu_log("NULL%s", get_comma(last)); + qemu_log("NULL%s", get_comma(last)); else - gemu_log("0x" TARGET_ABI_FMT_lx "%s", p, get_comma(last)); + qemu_log("0x" TARGET_ABI_FMT_lx "%s", p, get_comma(last)); } /* @@ -1265,12 +1278,12 @@ static void print_number(abi_long addr, int last) { if (addr == 0) { - gemu_log("NULL%s", get_comma(last)); + qemu_log("NULL%s", get_comma(last)); } else { int num; get_user_s32(num, addr); - gemu_log("[%d]%s", num, get_comma(last)); + qemu_log("[%d]%s", num, get_comma(last)); } } @@ -1285,11 +1298,11 @@ print_timeval(abi_ulong tv_addr, int last) print_pointer(tv_addr, last); return; } - gemu_log("{" TARGET_ABI_FMT_ld "," TARGET_ABI_FMT_ld "}%s", + qemu_log("{" TARGET_ABI_FMT_ld "," TARGET_ABI_FMT_ld "}%s", tswapal(tv->tv_sec), tswapal(tv->tv_usec), get_comma(last)); unlock_user(tv, tv_addr, 0); } else - gemu_log("NULL%s", get_comma(last)); + qemu_log("NULL%s", get_comma(last)); } static void @@ -1303,11 +1316,11 @@ print_timezone(abi_ulong tz_addr, int last) print_pointer(tz_addr, last); return; } - gemu_log("{%d,%d}%s", tswap32(tz->tz_minuteswest), + qemu_log("{%d,%d}%s", tswap32(tz->tz_minuteswest), tswap32(tz->tz_dsttime), get_comma(last)); unlock_user(tz, tz_addr, 0); } else { - gemu_log("NULL%s", get_comma(last)); + qemu_log("NULL%s", get_comma(last)); } } @@ -1515,83 +1528,83 @@ print_fcntl(const struct syscallname *name, print_raw_param("%d", arg0, 0); switch(arg1) { case TARGET_F_DUPFD: - gemu_log("F_DUPFD,"); + qemu_log("F_DUPFD,"); print_raw_param(TARGET_ABI_FMT_ld, arg2, 1); break; case TARGET_F_GETFD: - gemu_log("F_GETFD"); + qemu_log("F_GETFD"); break; case TARGET_F_SETFD: - gemu_log("F_SETFD,"); + qemu_log("F_SETFD,"); print_raw_param(TARGET_ABI_FMT_ld, arg2, 1); break; case TARGET_F_GETFL: - gemu_log("F_GETFL"); + qemu_log("F_GETFL"); break; case TARGET_F_SETFL: - gemu_log("F_SETFL,"); + qemu_log("F_SETFL,"); print_open_flags(arg2, 1); break; case TARGET_F_GETLK: - gemu_log("F_GETLK,"); + qemu_log("F_GETLK,"); print_pointer(arg2, 1); break; case TARGET_F_SETLK: - gemu_log("F_SETLK,"); + qemu_log("F_SETLK,"); print_pointer(arg2, 1); break; case TARGET_F_SETLKW: - gemu_log("F_SETLKW,"); + qemu_log("F_SETLKW,"); print_pointer(arg2, 1); break; case TARGET_F_GETOWN: - gemu_log("F_GETOWN"); + qemu_log("F_GETOWN"); break; case TARGET_F_SETOWN: - gemu_log("F_SETOWN,"); + qemu_log("F_SETOWN,"); print_raw_param(TARGET_ABI_FMT_ld, arg2, 0); break; case TARGET_F_GETSIG: - gemu_log("F_GETSIG"); + qemu_log("F_GETSIG"); break; case TARGET_F_SETSIG: - gemu_log("F_SETSIG,"); + qemu_log("F_SETSIG,"); print_raw_param(TARGET_ABI_FMT_ld, arg2, 0); break; #if TARGET_ABI_BITS == 32 case TARGET_F_GETLK64: - gemu_log("F_GETLK64,"); + qemu_log("F_GETLK64,"); print_pointer(arg2, 1); break; case TARGET_F_SETLK64: - gemu_log("F_SETLK64,"); + qemu_log("F_SETLK64,"); print_pointer(arg2, 1); break; case TARGET_F_SETLKW64: - gemu_log("F_SETLKW64,"); + qemu_log("F_SETLKW64,"); print_pointer(arg2, 1); break; #endif case TARGET_F_SETLEASE: - gemu_log("F_SETLEASE,"); + qemu_log("F_SETLEASE,"); print_raw_param(TARGET_ABI_FMT_ld, arg2, 0); break; case TARGET_F_GETLEASE: - gemu_log("F_GETLEASE"); + qemu_log("F_GETLEASE"); break; case TARGET_F_SETPIPE_SZ: - gemu_log("F_SETPIPE_SZ,"); + qemu_log("F_SETPIPE_SZ,"); print_raw_param(TARGET_ABI_FMT_ld, arg2, 1); break; case TARGET_F_GETPIPE_SZ: - gemu_log("F_GETPIPE_SZ"); + qemu_log("F_GETPIPE_SZ"); break; case TARGET_F_DUPFD_CLOEXEC: - gemu_log("F_DUPFD_CLOEXEC,"); + qemu_log("F_DUPFD_CLOEXEC,"); print_raw_param(TARGET_ABI_FMT_ld, arg2, 1); break; case TARGET_F_NOTIFY: - gemu_log("F_NOTIFY,"); + qemu_log("F_NOTIFY,"); print_raw_param(TARGET_ABI_FMT_ld, arg2, 0); break; default: @@ -1679,7 +1692,7 @@ print__llseek(const struct syscallname *name, case SEEK_CUR: whence = "SEEK_CUR"; break; case SEEK_END: whence = "SEEK_END"; break; } - gemu_log("%s",whence); + qemu_log("%s", whence); print_syscall_epilogue(name); } #endif @@ -1694,9 +1707,9 @@ print_socket(const struct syscallname *name, print_syscall_prologue(name); print_socket_domain(domain); - gemu_log(","); + qemu_log(","); print_socket_type(type); - gemu_log(","); + qemu_log(","); if (domain == AF_PACKET || (domain == AF_INET && type == TARGET_SOCK_PACKET)) { protocol = tswap16(protocol); @@ -1728,17 +1741,17 @@ static void do_print_socket(const char *name, abi_long arg1) get_user_ualx(domain, arg1, 0); get_user_ualx(type, arg1, 1); get_user_ualx(protocol, arg1, 2); - gemu_log("%s(", name); + qemu_log("%s(", name); print_socket_domain(domain); - gemu_log(","); + qemu_log(","); print_socket_type(type); - gemu_log(","); + qemu_log(","); if (domain == AF_PACKET || (domain == AF_INET && type == TARGET_SOCK_PACKET)) { protocol = tswap16(protocol); } print_socket_protocol(domain, type, protocol); - gemu_log(")"); + qemu_log(")"); } static void do_print_sockaddr(const char *name, abi_long arg1) @@ -1749,10 +1762,10 @@ static void do_print_sockaddr(const char *name, abi_long arg1) get_user_ualx(addr, arg1, 1); get_user_ualx(addrlen, arg1, 2); - gemu_log("%s(", name); + qemu_log("%s(", name); print_sockfd(sockfd, 0); print_sockaddr(addr, addrlen, 0); - gemu_log(")"); + qemu_log(")"); } static void do_print_listen(const char *name, abi_long arg1) @@ -1762,10 +1775,10 @@ static void do_print_listen(const char *name, abi_long arg1) get_user_ualx(sockfd, arg1, 0); get_user_ualx(backlog, arg1, 1); - gemu_log("%s(", name); + qemu_log("%s(", name); print_sockfd(sockfd, 0); print_raw_param(TARGET_ABI_FMT_ld, backlog, 1); - gemu_log(")"); + qemu_log(")"); } static void do_print_socketpair(const char *name, abi_long arg1) @@ -1777,15 +1790,15 @@ static void do_print_socketpair(const char *name, abi_long arg1) get_user_ualx(protocol, arg1, 2); get_user_ualx(tab, arg1, 3); - gemu_log("%s(", name); + qemu_log("%s(", name); print_socket_domain(domain); - gemu_log(","); + qemu_log(","); print_socket_type(type); - gemu_log(","); + qemu_log(","); print_socket_protocol(domain, type, protocol); - gemu_log(","); + qemu_log(","); print_raw_param(TARGET_ABI_FMT_lx, tab, 1); - gemu_log(")"); + qemu_log(")"); } static void do_print_sendrecv(const char *name, abi_long arg1) @@ -1797,12 +1810,12 @@ static void do_print_sendrecv(const char *name, abi_long arg1) get_user_ualx(len, arg1, 2); get_user_ualx(flags, arg1, 3); - gemu_log("%s(", name); + qemu_log("%s(", name); print_sockfd(sockfd, 0); print_buf(msg, len, 0); print_raw_param(TARGET_ABI_FMT_ld, len, 0); print_flags(msg_flags, flags, 1); - gemu_log(")"); + qemu_log(")"); } static void do_print_msgaddr(const char *name, abi_long arg1) @@ -1816,13 +1829,13 @@ static void do_print_msgaddr(const char *name, abi_long arg1) get_user_ualx(addr, arg1, 4); get_user_ualx(addrlen, arg1, 5); - gemu_log("%s(", name); + qemu_log("%s(", name); print_sockfd(sockfd, 0); print_buf(msg, len, 0); print_raw_param(TARGET_ABI_FMT_ld, len, 0); print_flags(msg_flags, flags, 0); print_sockaddr(addr, addrlen, 0); - gemu_log(")"); + qemu_log(")"); } static void do_print_shutdown(const char *name, abi_long arg1) @@ -1832,23 +1845,23 @@ static void do_print_shutdown(const char *name, abi_long arg1) get_user_ualx(sockfd, arg1, 0); get_user_ualx(how, arg1, 1); - gemu_log("shutdown("); + qemu_log("shutdown("); print_sockfd(sockfd, 0); switch (how) { case SHUT_RD: - gemu_log("SHUT_RD"); + qemu_log("SHUT_RD"); break; case SHUT_WR: - gemu_log("SHUT_WR"); + qemu_log("SHUT_WR"); break; case SHUT_RDWR: - gemu_log("SHUT_RDWR"); + qemu_log("SHUT_RDWR"); break; default: print_raw_param(TARGET_ABI_FMT_ld, how, 1); break; } - gemu_log(")"); + qemu_log(")"); } static void do_print_msg(const char *name, abi_long arg1) @@ -1859,11 +1872,11 @@ static void do_print_msg(const char *name, abi_long arg1) get_user_ualx(msg, arg1, 1); get_user_ualx(flags, arg1, 2); - gemu_log("%s(", name); + qemu_log("%s(", name); print_sockfd(sockfd, 0); print_pointer(msg, 0); print_flags(msg_flags, flags, 1); - gemu_log(")"); + qemu_log(")"); } static void do_print_sockopt(const char *name, abi_long arg1) @@ -1876,113 +1889,113 @@ static void do_print_sockopt(const char *name, abi_long arg1) get_user_ualx(optval, arg1, 3); get_user_ualx(optlen, arg1, 4); - gemu_log("%s(", name); + qemu_log("%s(", name); print_sockfd(sockfd, 0); switch (level) { case SOL_TCP: - gemu_log("SOL_TCP,"); + qemu_log("SOL_TCP,"); print_raw_param(TARGET_ABI_FMT_ld, optname, 0); print_pointer(optval, 0); break; case SOL_IP: - gemu_log("SOL_IP,"); + qemu_log("SOL_IP,"); print_raw_param(TARGET_ABI_FMT_ld, optname, 0); print_pointer(optval, 0); break; case SOL_RAW: - gemu_log("SOL_RAW,"); + qemu_log("SOL_RAW,"); print_raw_param(TARGET_ABI_FMT_ld, optname, 0); print_pointer(optval, 0); break; case TARGET_SOL_SOCKET: - gemu_log("SOL_SOCKET,"); + qemu_log("SOL_SOCKET,"); switch (optname) { case TARGET_SO_DEBUG: - gemu_log("SO_DEBUG,"); + qemu_log("SO_DEBUG,"); print_optint: print_number(optval, 0); break; case TARGET_SO_REUSEADDR: - gemu_log("SO_REUSEADDR,"); + qemu_log("SO_REUSEADDR,"); goto print_optint; case TARGET_SO_REUSEPORT: - gemu_log("SO_REUSEPORT,"); + qemu_log("SO_REUSEPORT,"); goto print_optint; case TARGET_SO_TYPE: - gemu_log("SO_TYPE,"); + qemu_log("SO_TYPE,"); goto print_optint; case TARGET_SO_ERROR: - gemu_log("SO_ERROR,"); + qemu_log("SO_ERROR,"); goto print_optint; case TARGET_SO_DONTROUTE: - gemu_log("SO_DONTROUTE,"); + qemu_log("SO_DONTROUTE,"); goto print_optint; case TARGET_SO_BROADCAST: - gemu_log("SO_BROADCAST,"); + qemu_log("SO_BROADCAST,"); goto print_optint; case TARGET_SO_SNDBUF: - gemu_log("SO_SNDBUF,"); + qemu_log("SO_SNDBUF,"); goto print_optint; case TARGET_SO_RCVBUF: - gemu_log("SO_RCVBUF,"); + qemu_log("SO_RCVBUF,"); goto print_optint; case TARGET_SO_KEEPALIVE: - gemu_log("SO_KEEPALIVE,"); + qemu_log("SO_KEEPALIVE,"); goto print_optint; case TARGET_SO_OOBINLINE: - gemu_log("SO_OOBINLINE,"); + qemu_log("SO_OOBINLINE,"); goto print_optint; case TARGET_SO_NO_CHECK: - gemu_log("SO_NO_CHECK,"); + qemu_log("SO_NO_CHECK,"); goto print_optint; case TARGET_SO_PRIORITY: - gemu_log("SO_PRIORITY,"); + qemu_log("SO_PRIORITY,"); goto print_optint; case TARGET_SO_BSDCOMPAT: - gemu_log("SO_BSDCOMPAT,"); + qemu_log("SO_BSDCOMPAT,"); goto print_optint; case TARGET_SO_PASSCRED: - gemu_log("SO_PASSCRED,"); + qemu_log("SO_PASSCRED,"); goto print_optint; case TARGET_SO_TIMESTAMP: - gemu_log("SO_TIMESTAMP,"); + qemu_log("SO_TIMESTAMP,"); goto print_optint; case TARGET_SO_RCVLOWAT: - gemu_log("SO_RCVLOWAT,"); + qemu_log("SO_RCVLOWAT,"); goto print_optint; case TARGET_SO_RCVTIMEO: - gemu_log("SO_RCVTIMEO,"); + qemu_log("SO_RCVTIMEO,"); print_timeval(optval, 0); break; case TARGET_SO_SNDTIMEO: - gemu_log("SO_SNDTIMEO,"); + qemu_log("SO_SNDTIMEO,"); print_timeval(optval, 0); break; case TARGET_SO_ATTACH_FILTER: { struct target_sock_fprog *fprog; - gemu_log("SO_ATTACH_FILTER,"); + qemu_log("SO_ATTACH_FILTER,"); if (lock_user_struct(VERIFY_READ, fprog, optval, 0)) { struct target_sock_filter *filter; - gemu_log("{"); + qemu_log("{"); if (lock_user_struct(VERIFY_READ, filter, tswapal(fprog->filter), 0)) { int i; for (i = 0; i < tswap16(fprog->len) - 1; i++) { - gemu_log("[%d]{0x%x,%d,%d,0x%x},", + qemu_log("[%d]{0x%x,%d,%d,0x%x},", i, tswap16(filter[i].code), filter[i].jt, filter[i].jf, tswap32(filter[i].k)); } - gemu_log("[%d]{0x%x,%d,%d,0x%x}", + qemu_log("[%d]{0x%x,%d,%d,0x%x}", i, tswap16(filter[i].code), filter[i].jt, filter[i].jf, tswap32(filter[i].k)); } else { - gemu_log(TARGET_ABI_FMT_lx, tswapal(fprog->filter)); + qemu_log(TARGET_ABI_FMT_lx, tswapal(fprog->filter)); } - gemu_log(",%d},", tswap16(fprog->len)); + qemu_log(",%d},", tswap16(fprog->len)); unlock_user(fprog, optval, 0); } else { print_pointer(optval, 0); @@ -2002,7 +2015,7 @@ print_optint: break; } print_raw_param(TARGET_ABI_FMT_ld, optlen, 1); - gemu_log(")"); + qemu_log(")"); } #define PRINT_SOCKOP(name, func) \ @@ -2164,7 +2177,7 @@ print_rt_sigprocmask(const struct syscallname *name, case TARGET_SIG_UNBLOCK: how = "SIG_UNBLOCK"; break; case TARGET_SIG_SETMASK: how = "SIG_SETMASK"; break; } - gemu_log("%s,",how); + qemu_log("%s,", how); print_pointer(arg1, 0); print_pointer(arg2, 1); print_syscall_epilogue(name); @@ -2278,7 +2291,7 @@ print_syslog_action(abi_ulong arg, int last) return; } } - gemu_log("%s%s", type, get_comma(last)); + qemu_log("%s%s", type, get_comma(last)); } static void @@ -2683,20 +2696,20 @@ static void print_futex_op(abi_long tflag, int last) { #define print_op(val) \ if( cmd == val ) { \ - gemu_log(#val); \ + qemu_log(#val); \ return; \ } int cmd = (int)tflag; #ifdef FUTEX_PRIVATE_FLAG if (cmd & FUTEX_PRIVATE_FLAG) { - gemu_log("FUTEX_PRIVATE_FLAG|"); + qemu_log("FUTEX_PRIVATE_FLAG|"); cmd &= ~FUTEX_PRIVATE_FLAG; } #endif #ifdef FUTEX_CLOCK_REALTIME if (cmd & FUTEX_CLOCK_REALTIME) { - gemu_log("FUTEX_CLOCK_REALTIME|"); + qemu_log("FUTEX_CLOCK_REALTIME|"); cmd &= ~FUTEX_CLOCK_REALTIME; } #endif @@ -2716,7 +2729,7 @@ if( cmd == val ) { \ print_op(FUTEX_WAKE_BITSET) #endif /* unknown values */ - gemu_log("%d",cmd); + qemu_log("%d", cmd); } static void @@ -2812,22 +2825,24 @@ print_syscall(int num, int i; const char *format="%s(" TARGET_ABI_FMT_ld "," TARGET_ABI_FMT_ld "," TARGET_ABI_FMT_ld "," TARGET_ABI_FMT_ld "," TARGET_ABI_FMT_ld "," TARGET_ABI_FMT_ld ")"; - gemu_log("%d ", getpid() ); + qemu_log("%d ", getpid()); for(i=0;i #endif +#ifdef CONFIG_KCOV +#include +#endif #define termios host_termios #define winsize host_winsize @@ -107,14 +110,18 @@ #include #include #include +#include +#include #include "linux_loop.h" #include "uname.h" #include "qemu.h" #include "qemu/guest-random.h" +#include "qemu/selfmap.h" #include "user/syscall-trace.h" #include "qapi/error.h" #include "fd-trans.h" +#include "tcg/tcg.h" #ifndef CLONE_IO #define CLONE_IO 0x80000000 /* Clone io context */ @@ -239,7 +246,12 @@ static type name (type1 arg1,type2 arg2,type3 arg3,type4 arg4,type5 arg5, \ #define __NR_sys_rt_sigqueueinfo __NR_rt_sigqueueinfo #define __NR_sys_rt_tgsigqueueinfo __NR_rt_tgsigqueueinfo #define __NR_sys_syslog __NR_syslog -#define __NR_sys_futex __NR_futex +#if defined(__NR_futex) +# define __NR_sys_futex __NR_futex +#endif +#if defined(__NR_futex_time64) +# define __NR_sys_futex_time64 __NR_futex_time64 +#endif #define __NR_sys_inotify_init __NR_inotify_init #define __NR_sys_inotify_add_watch __NR_inotify_add_watch #define __NR_sys_inotify_rm_watch __NR_inotify_rm_watch @@ -289,10 +301,14 @@ _syscall1(int,exit_group,int,error_code) #if defined(TARGET_NR_set_tid_address) && defined(__NR_set_tid_address) _syscall1(int,set_tid_address,int *,tidptr) #endif -#if defined(TARGET_NR_futex) && defined(__NR_futex) +#if defined(__NR_futex) _syscall6(int,sys_futex,int *,uaddr,int,op,int,val, const struct timespec *,timeout,int *,uaddr2,int,val3) #endif +#if defined(__NR_futex_time64) +_syscall6(int,sys_futex_time64,int *,uaddr,int,op,int,val, + const struct timespec *,timeout,int *,uaddr2,int,val3) +#endif #define __NR_sys_sched_getaffinity __NR_sched_getaffinity _syscall3(int, sys_sched_getaffinity, pid_t, pid, unsigned int, len, unsigned long *, user_mask_ptr); @@ -329,6 +345,9 @@ _syscall5(int, kcmp, pid_t, pid1, pid_t, pid2, int, type, _syscall5(int, sys_statx, int, dirfd, const char *, pathname, int, flags, unsigned int, mask, struct target_statx *, statxbuf) #endif +#if defined(TARGET_NR_membarrier) && defined(__NR_membarrier) +_syscall2(int, membarrier, int, cmd, int, flags) +#endif static bitmask_transtbl fcntl_flags_tbl[] = { { TARGET_O_ACCMODE, TARGET_O_WRONLY, O_ACCMODE, O_WRONLY, }, @@ -733,21 +752,34 @@ safe_syscall3(ssize_t, read, int, fd, void *, buff, size_t, count) safe_syscall3(ssize_t, write, int, fd, const void *, buff, size_t, count) safe_syscall4(int, openat, int, dirfd, const char *, pathname, \ int, flags, mode_t, mode) +#if defined(TARGET_NR_wait4) || defined(TARGET_NR_waitpid) safe_syscall4(pid_t, wait4, pid_t, pid, int *, status, int, options, \ struct rusage *, rusage) +#endif safe_syscall5(int, waitid, idtype_t, idtype, id_t, id, siginfo_t *, infop, \ int, options, struct rusage *, rusage) safe_syscall3(int, execve, const char *, filename, char **, argv, char **, envp) +#if defined(TARGET_NR_select) || defined(TARGET_NR__newselect) || \ + defined(TARGET_NR_pselect6) safe_syscall6(int, pselect6, int, nfds, fd_set *, readfds, fd_set *, writefds, \ fd_set *, exceptfds, struct timespec *, timeout, void *, sig) +#endif +#if defined(TARGET_NR_ppoll) || defined(TARGET_NR_poll) safe_syscall5(int, ppoll, struct pollfd *, ufds, unsigned int, nfds, struct timespec *, tsp, const sigset_t *, sigmask, size_t, sigsetsize) +#endif safe_syscall6(int, epoll_pwait, int, epfd, struct epoll_event *, events, int, maxevents, int, timeout, const sigset_t *, sigmask, size_t, sigsetsize) +#if defined(__NR_futex) safe_syscall6(int,futex,int *,uaddr,int,op,int,val, \ const struct timespec *,timeout,int *,uaddr2,int,val3) +#endif +#if defined(__NR_futex_time64) +safe_syscall6(int,futex_time64,int *,uaddr,int,op,int,val, \ + const struct timespec *,timeout,int *,uaddr2,int,val3) +#endif safe_syscall2(int, rt_sigsuspend, sigset_t *, newset, size_t, sigsetsize) safe_syscall2(int, kill, pid_t, pid, int, sig) safe_syscall2(int, tkill, int, tid, int, sig) @@ -767,12 +799,16 @@ safe_syscall6(ssize_t, recvfrom, int, fd, void *, buf, size_t, len, safe_syscall3(ssize_t, sendmsg, int, fd, const struct msghdr *, msg, int, flags) safe_syscall3(ssize_t, recvmsg, int, fd, struct msghdr *, msg, int, flags) safe_syscall2(int, flock, int, fd, int, operation) +#ifdef TARGET_NR_rt_sigtimedwait safe_syscall4(int, rt_sigtimedwait, const sigset_t *, these, siginfo_t *, uinfo, const struct timespec *, uts, size_t, sigsetsize) +#endif safe_syscall4(int, accept4, int, fd, struct sockaddr *, addr, socklen_t *, len, int, flags) +#if defined(TARGET_NR_nanosleep) safe_syscall2(int, nanosleep, const struct timespec *, req, struct timespec *, rem) +#endif #ifdef TARGET_NR_clock_nanosleep safe_syscall4(int, clock_nanosleep, const clockid_t, clock, int, flags, const struct timespec *, req, struct timespec *, rem) @@ -793,9 +829,11 @@ safe_syscall5(int, msgrcv, int, msgid, void *, msgp, size_t, sz, safe_syscall4(int, semtimedop, int, semid, struct sembuf *, tsops, unsigned, nsops, const struct timespec *, timeout) #endif -#if defined(TARGET_NR_mq_open) && defined(__NR_mq_open) +#ifdef TARGET_NR_mq_timedsend safe_syscall5(int, mq_timedsend, int, mqdes, const char *, msg_ptr, size_t, len, unsigned, prio, const struct timespec *, timeout) +#endif +#ifdef TARGET_NR_mq_timedreceive safe_syscall5(int, mq_timedreceive, int, mqdes, char *, msg_ptr, size_t, len, unsigned *, prio, const struct timespec *, timeout) #endif @@ -937,6 +975,8 @@ abi_long do_brk(abi_ulong new_brk) return target_brk; } +#if defined(TARGET_NR_select) || defined(TARGET_NR__newselect) || \ + defined(TARGET_NR_pselect6) static inline abi_long copy_from_user_fdset(fd_set *fds, abi_ulong target_fds_addr, int n) @@ -1012,6 +1052,7 @@ static inline abi_long copy_to_user_fdset(abi_ulong target_fds_addr, return 0; } +#endif #if defined(__alpha__) #define HOST_HZ 1024 @@ -1058,6 +1099,7 @@ static inline abi_long host_to_target_rusage(abi_ulong target_addr, return 0; } +#ifdef TARGET_NR_setrlimit static inline rlim_t target_to_host_rlim(abi_ulong target_rlim) { abi_ulong target_rlim_swap; @@ -1073,7 +1115,9 @@ static inline rlim_t target_to_host_rlim(abi_ulong target_rlim) return result; } +#endif +#if defined(TARGET_NR_getrlimit) || defined(TARGET_NR_ugetrlimit) static inline abi_ulong host_to_target_rlim(rlim_t rlim) { abi_ulong target_rlim_swap; @@ -1087,6 +1131,7 @@ static inline abi_ulong host_to_target_rlim(rlim_t rlim) return result; } +#endif static inline int target_to_host_resource(int code) { @@ -1177,6 +1222,12 @@ static inline abi_long copy_to_user_timeval64(abi_ulong target_tv_addr, return 0; } +#if defined(TARGET_NR_futex) || \ + defined(TARGET_NR_rt_sigtimedwait) || \ + defined(TARGET_NR_pselect6) || defined(TARGET_NR_pselect6) || \ + defined(TARGET_NR_nanosleep) || defined(TARGET_NR_clock_settime) || \ + defined(TARGET_NR_utimensat) || defined(TARGET_NR_mq_timedsend) || \ + defined(TARGET_NR_mq_timedreceive) static inline abi_long target_to_host_timespec(struct timespec *host_ts, abi_ulong target_addr) { @@ -1190,6 +1241,23 @@ static inline abi_long target_to_host_timespec(struct timespec *host_ts, unlock_user_struct(target_ts, target_addr, 0); return 0; } +#endif + +#if defined(TARGET_NR_clock_settime64) || defined(TARGET_NR_futex_time64) +static inline abi_long target_to_host_timespec64(struct timespec *host_ts, + abi_ulong target_addr) +{ + struct target__kernel_timespec *target_ts; + + if (!lock_user_struct(VERIFY_READ, target_ts, target_addr, 1)) { + return -TARGET_EFAULT; + } + __get_user(host_ts->tv_sec, &target_ts->tv_sec); + __get_user(host_ts->tv_nsec, &target_ts->tv_nsec); + unlock_user_struct(target_ts, target_addr, 0); + return 0; +} +#endif static inline abi_long host_to_target_timespec(abi_ulong target_addr, struct timespec *host_ts) @@ -1219,6 +1287,26 @@ static inline abi_long host_to_target_timespec64(abi_ulong target_addr, return 0; } +#if defined(TARGET_NR_gettimeofday) +static inline abi_long copy_to_user_timezone(abi_ulong target_tz_addr, + struct timezone *tz) +{ + struct target_timezone *target_tz; + + if (!lock_user_struct(VERIFY_WRITE, target_tz, target_tz_addr, 1)) { + return -TARGET_EFAULT; + } + + __put_user(tz->tz_minuteswest, &target_tz->tz_minuteswest); + __put_user(tz->tz_dsttime, &target_tz->tz_dsttime); + + unlock_user_struct(target_tz, target_tz_addr, 1); + + return 0; +} +#endif + +#if defined(TARGET_NR_settimeofday) static inline abi_long copy_from_user_timezone(struct timezone *tz, abi_ulong target_tz_addr) { @@ -1235,6 +1323,7 @@ static inline abi_long copy_from_user_timezone(struct timezone *tz, return 0; } +#endif #if defined(TARGET_NR_mq_open) && defined(__NR_mq_open) #include @@ -1555,7 +1644,11 @@ static inline abi_long target_to_host_cmsg(struct msghdr *msgh, * something more intelligent than "twice the size of the * target buffer we're reading from". */ - gemu_log("Host cmsg overflow\n"); + qemu_log_mask(LOG_UNIMP, + ("Unsupported ancillary data %d/%d: " + "unhandled msg size\n"), + tswap32(target_cmsg->cmsg_level), + tswap32(target_cmsg->cmsg_type)); break; } @@ -1585,8 +1678,8 @@ static inline abi_long target_to_host_cmsg(struct msghdr *msgh, __get_user(cred->uid, &target_cred->uid); __get_user(cred->gid, &target_cred->gid); } else { - gemu_log("Unsupported ancillary data: %d/%d\n", - cmsg->cmsg_level, cmsg->cmsg_type); + qemu_log_mask(LOG_UNIMP, "Unsupported ancillary data: %d/%d\n", + cmsg->cmsg_level, cmsg->cmsg_type); memcpy(data, target_data, len); } @@ -1807,8 +1900,8 @@ static inline abi_long host_to_target_cmsg(struct target_msghdr *target_msgh, default: unimplemented: - gemu_log("Unsupported ancillary data: %d/%d\n", - cmsg->cmsg_level, cmsg->cmsg_type); + qemu_log_mask(LOG_UNIMP, "Unsupported ancillary data: %d/%d\n", + cmsg->cmsg_level, cmsg->cmsg_type); memcpy(target_data, data, MIN(len, tgt_len)); if (tgt_len > len) { memset(target_data + len, 0, tgt_len - len); @@ -2283,7 +2376,8 @@ set_timeout: #endif /* SOL_NETLINK */ default: unimplemented: - gemu_log("Unsupported setsockopt level=%d optname=%d\n", level, optname); + qemu_log_mask(LOG_UNIMP, "Unsupported setsockopt level=%d optname=%d\n", + level, optname); ret = -TARGET_ENOPROTOOPT; } return ret; @@ -2302,10 +2396,42 @@ static abi_long do_getsockopt(int sockfd, int level, int optname, level = SOL_SOCKET; switch (optname) { /* These don't just return a single integer */ - case TARGET_SO_RCVTIMEO: - case TARGET_SO_SNDTIMEO: case TARGET_SO_PEERNAME: goto unimplemented; + case TARGET_SO_RCVTIMEO: { + struct timeval tv; + socklen_t tvlen; + + optname = SO_RCVTIMEO; + +get_timeout: + if (get_user_u32(len, optlen)) { + return -TARGET_EFAULT; + } + if (len < 0) { + return -TARGET_EINVAL; + } + + tvlen = sizeof(tv); + ret = get_errno(getsockopt(sockfd, level, optname, + &tv, &tvlen)); + if (ret < 0) { + return ret; + } + if (len > sizeof(struct target_timeval)) { + len = sizeof(struct target_timeval); + } + if (copy_to_user_timeval(optval_addr, &tv)) { + return -TARGET_EFAULT; + } + if (put_user_u32(len, optlen)) { + return -TARGET_EFAULT; + } + break; + } + case TARGET_SO_SNDTIMEO: + optname = SO_SNDTIMEO; + goto get_timeout; case TARGET_SO_PEERCRED: { struct ucred cr; socklen_t crlen; @@ -2339,6 +2465,28 @@ static abi_long do_getsockopt(int sockfd, int level, int optname, } break; } + case TARGET_SO_PEERSEC: { + char *name; + + if (get_user_u32(len, optlen)) { + return -TARGET_EFAULT; + } + if (len < 0) { + return -TARGET_EINVAL; + } + name = lock_user(VERIFY_WRITE, optval_addr, len, 0); + if (!name) { + return -TARGET_EFAULT; + } + lv = len; + ret = get_errno(getsockopt(sockfd, level, SO_PEERSEC, + name, &lv)); + if (put_user_u32(lv, optlen)) { + ret = -TARGET_EFAULT; + } + unlock_user(name, optval_addr, lv); + break; + } case TARGET_SO_LINGER: { struct linger lg; @@ -2636,8 +2784,9 @@ static abi_long do_getsockopt(int sockfd, int level, int optname, #endif /* SOL_NETLINK */ default: unimplemented: - gemu_log("getsockopt level=%d optname=%d not yet supported\n", - level, optname); + qemu_log_mask(LOG_UNIMP, + "getsockopt level=%d optname=%d not yet supported\n", + level, optname); ret = -TARGET_EOPNOTSUPP; break; } @@ -3392,7 +3541,7 @@ static abi_long do_socketcall(int num, abi_ulong vptr) case TARGET_SYS_SENDMMSG: /* sockfd, msgvec, vlen, flags */ return do_sendrecvmmsg(a[0], a[1], a[2], a[3], 1); default: - gemu_log("Unsupported socketcall: %d\n", num); + qemu_log_mask(LOG_UNIMP, "Unsupported socketcall: %d\n", num); return -TARGET_EINVAL; } } @@ -4303,7 +4452,8 @@ static abi_long do_ipc(CPUArchState *cpu_env, ret = do_shmctl(first, second, ptr); break; default: - gemu_log("Unsupported ipc call: %d (version %d)\n", call, version); + qemu_log_mask(LOG_UNIMP, "Unsupported ipc call: %d (version %d)\n", + call, version); ret = -TARGET_ENOSYS; break; } @@ -5151,7 +5301,8 @@ static abi_long do_ioctl(int fd, int cmd, abi_long arg) ie = ioctl_entries; for(;;) { if (ie->target_cmd == 0) { - gemu_log("Unsupported ioctl: cmd=0x%04lx\n", (long)cmd); + qemu_log_mask( + LOG_UNIMP, "Unsupported ioctl: cmd=0x%04lx\n", (long)cmd); return -TARGET_ENOSYS; } if (ie->target_cmd == cmd) @@ -5174,6 +5325,8 @@ static abi_long do_ioctl(int fd, int cmd, abi_long arg) break; case TYPE_PTRVOID: case TYPE_INT: + case TYPE_LONG: + case TYPE_ULONG: ret = get_errno(safe_ioctl(fd, ie->host_cmd, arg)); break; case TYPE_PTR: @@ -5217,8 +5370,9 @@ static abi_long do_ioctl(int fd, int cmd, abi_long arg) } break; default: - gemu_log("Unsupported ioctl type: cmd=0x%04lx type=%d\n", - (long)cmd, arg_type[0]); + qemu_log_mask(LOG_UNIMP, + "Unsupported ioctl type: cmd=0x%04lx type=%d\n", + (long)cmd, arg_type[0]); ret = -TARGET_ENOSYS; break; } @@ -5424,6 +5578,10 @@ static bitmask_transtbl mmap_flags_tbl[] = { { 0, 0, 0, 0 } }; +/* + * NOTE: TARGET_ABI32 is defined for TARGET_I386 (but not for TARGET_X86_64) + * TARGET_I386 is defined if TARGET_X86_64 is defined + */ #if defined(TARGET_I386) /* NOTE: there is really one LDT for all the threads */ @@ -5563,7 +5721,7 @@ static abi_long do_modify_ldt(CPUX86State *env, int func, abi_ulong ptr, return ret; } -#if defined(TARGET_I386) && defined(TARGET_ABI32) +#if defined(TARGET_ABI32) abi_long do_set_thread_area(CPUX86State *env, abi_ulong ptr) { uint64_t *gdt_table = g2h(env->gdt.base); @@ -5695,9 +5853,12 @@ static abi_long do_get_thread_area(CPUX86State *env, abi_ulong ptr) unlock_user_struct(target_ldt_info, ptr, 1); return 0; } -#endif /* TARGET_I386 && TARGET_ABI32 */ -#ifndef TARGET_ABI32 +abi_long do_arch_prctl(CPUX86State *env, int code, abi_ulong addr) +{ + return -ENOSYS; +} +#else abi_long do_arch_prctl(CPUX86State *env, int code, abi_ulong addr) { abi_long ret = 0; @@ -5730,7 +5891,7 @@ abi_long do_arch_prctl(CPUX86State *env, int code, abi_ulong addr) } return ret; } -#endif +#endif /* defined(TARGET_ABI32 */ #endif /* defined(TARGET_I386) */ @@ -6491,6 +6652,8 @@ static inline abi_long target_ftruncate64(void *cpu_env, abi_long arg1, } #endif +#if defined(TARGET_NR_timer_settime) || \ + (defined(TARGET_NR_timerfd_settime) && defined(CONFIG_TIMERFD)) static inline abi_long target_to_host_itimerspec(struct itimerspec *host_itspec, abi_ulong target_addr) { @@ -6510,7 +6673,11 @@ static inline abi_long target_to_host_itimerspec(struct itimerspec *host_itspec, unlock_user_struct(target_itspec, target_addr, 1); return 0; } +#endif +#if ((defined(TARGET_NR_timerfd_gettime) || \ + defined(TARGET_NR_timerfd_settime)) && defined(CONFIG_TIMERFD)) || \ + defined(TARGET_NR_timer_gettime) || defined(TARGET_NR_timer_settime) static inline abi_long host_to_target_itimerspec(abi_ulong target_addr, struct itimerspec *host_its) { @@ -6529,7 +6696,10 @@ static inline abi_long host_to_target_itimerspec(abi_ulong target_addr, unlock_user_struct(target_itspec, target_addr, 0); return 0; } +#endif +#if defined(TARGET_NR_adjtimex) || \ + (defined(TARGET_NR_clock_adjtime) && defined(CONFIG_CLOCK_ADJTIME)) static inline abi_long target_to_host_timex(struct timex *host_tx, abi_long target_addr) { @@ -6599,7 +6769,7 @@ static inline abi_long host_to_target_timex(abi_long target_addr, unlock_user_struct(target_tx, target_addr, 1); return 0; } - +#endif static inline abi_long target_to_host_sigevent(struct sigevent *host_sevp, abi_ulong target_addr) @@ -6760,12 +6930,62 @@ static inline abi_long host_to_target_statx(struct target_statx *host_stx, } #endif +static int do_sys_futex(int *uaddr, int op, int val, + const struct timespec *timeout, int *uaddr2, + int val3) +{ +#if HOST_LONG_BITS == 64 +#if defined(__NR_futex) + /* always a 64-bit time_t, it doesn't define _time64 version */ + return sys_futex(uaddr, op, val, timeout, uaddr2, val3); + +#endif +#else /* HOST_LONG_BITS == 64 */ +#if defined(__NR_futex_time64) + if (sizeof(timeout->tv_sec) == 8) { + /* _time64 function on 32bit arch */ + return sys_futex_time64(uaddr, op, val, timeout, uaddr2, val3); + } +#endif +#if defined(__NR_futex) + /* old function on 32bit arch */ + return sys_futex(uaddr, op, val, timeout, uaddr2, val3); +#endif +#endif /* HOST_LONG_BITS == 64 */ + g_assert_not_reached(); +} + +static int do_safe_futex(int *uaddr, int op, int val, + const struct timespec *timeout, int *uaddr2, + int val3) +{ +#if HOST_LONG_BITS == 64 +#if defined(__NR_futex) + /* always a 64-bit time_t, it doesn't define _time64 version */ + return get_errno(safe_futex(uaddr, op, val, timeout, uaddr2, val3)); +#endif +#else /* HOST_LONG_BITS == 64 */ +#if defined(__NR_futex_time64) + if (sizeof(timeout->tv_sec) == 8) { + /* _time64 function on 32bit arch */ + return get_errno(safe_futex_time64(uaddr, op, val, timeout, uaddr2, + val3)); + } +#endif +#if defined(__NR_futex) + /* old function on 32bit arch */ + return get_errno(safe_futex(uaddr, op, val, timeout, uaddr2, val3)); +#endif +#endif /* HOST_LONG_BITS == 64 */ + return -TARGET_ENOSYS; +} /* ??? Using host futex calls even when target atomic operations are not really atomic probably breaks things. However implementing futexes locally would make futexes shared between multiple processes tricky. However they're probably useless because guest atomic operations won't work either. */ +#if defined(TARGET_NR_futex) static int do_futex(target_ulong uaddr, int op, int val, target_ulong timeout, target_ulong uaddr2, int val3) { @@ -6788,12 +7008,11 @@ static int do_futex(target_ulong uaddr, int op, int val, target_ulong timeout, } else { pts = NULL; } - return get_errno(safe_futex(g2h(uaddr), op, tswap32(val), - pts, NULL, val3)); + return do_safe_futex(g2h(uaddr), op, tswap32(val), pts, NULL, val3); case FUTEX_WAKE: - return get_errno(safe_futex(g2h(uaddr), op, val, NULL, NULL, 0)); + return do_safe_futex(g2h(uaddr), op, val, NULL, NULL, 0); case FUTEX_FD: - return get_errno(safe_futex(g2h(uaddr), op, val, NULL, NULL, 0)); + return do_safe_futex(g2h(uaddr), op, val, NULL, NULL, 0); case FUTEX_REQUEUE: case FUTEX_CMP_REQUEUE: case FUTEX_WAKE_OP: @@ -6803,15 +7022,63 @@ static int do_futex(target_ulong uaddr, int op, int val, target_ulong timeout, to satisfy the compiler. We do not need to tswap TIMEOUT since it's not compared to guest memory. */ pts = (struct timespec *)(uintptr_t) timeout; - return get_errno(safe_futex(g2h(uaddr), op, val, pts, - g2h(uaddr2), - (base_op == FUTEX_CMP_REQUEUE - ? tswap32(val3) - : val3))); + return do_safe_futex(g2h(uaddr), op, val, pts, g2h(uaddr2), + (base_op == FUTEX_CMP_REQUEUE + ? tswap32(val3) + : val3)); default: return -TARGET_ENOSYS; } } +#endif + +#if defined(TARGET_NR_futex_time64) +static int do_futex_time64(target_ulong uaddr, int op, int val, target_ulong timeout, + target_ulong uaddr2, int val3) +{ + struct timespec ts, *pts; + int base_op; + + /* ??? We assume FUTEX_* constants are the same on both host + and target. */ +#ifdef FUTEX_CMD_MASK + base_op = op & FUTEX_CMD_MASK; +#else + base_op = op; +#endif + switch (base_op) { + case FUTEX_WAIT: + case FUTEX_WAIT_BITSET: + if (timeout) { + pts = &ts; + target_to_host_timespec64(pts, timeout); + } else { + pts = NULL; + } + return do_safe_futex(g2h(uaddr), op, tswap32(val), pts, NULL, val3); + case FUTEX_WAKE: + return do_safe_futex(g2h(uaddr), op, val, NULL, NULL, 0); + case FUTEX_FD: + return do_safe_futex(g2h(uaddr), op, val, NULL, NULL, 0); + case FUTEX_REQUEUE: + case FUTEX_CMP_REQUEUE: + case FUTEX_WAKE_OP: + /* For FUTEX_REQUEUE, FUTEX_CMP_REQUEUE, and FUTEX_WAKE_OP, the + TIMEOUT parameter is interpreted as a uint32_t by the kernel. + But the prototype takes a `struct timespec *'; insert casts + to satisfy the compiler. We do not need to tswap TIMEOUT + since it's not compared to guest memory. */ + pts = (struct timespec *)(uintptr_t) timeout; + return do_safe_futex(g2h(uaddr), op, val, pts, g2h(uaddr2), + (base_op == FUTEX_CMP_REQUEUE + ? tswap32(val3) + : val3)); + default: + return -TARGET_ENOSYS; + } +} +#endif + #if defined(TARGET_NR_name_to_handle_at) && defined(CONFIG_OPEN_BY_HANDLE) static abi_long do_name_to_handle_at(abi_long dirfd, abi_long pathname, abi_long handle, abi_long mount_id, @@ -6966,47 +7233,60 @@ static int open_self_maps(void *cpu_env, int fd) { CPUState *cpu = env_cpu((CPUArchState *)cpu_env); TaskState *ts = cpu->opaque; - FILE *fp; - char *line = NULL; - size_t len = 0; - ssize_t read; + GSList *map_info = read_self_maps(); + GSList *s; + int count; - fp = fopen("/proc/self/maps", "r"); - if (fp == NULL) { - return -1; - } + for (s = map_info; s; s = g_slist_next(s)) { + MapInfo *e = (MapInfo *) s->data; - while ((read = getline(&line, &len, fp)) != -1) { - int fields, dev_maj, dev_min, inode; - uint64_t min, max, offset; - char flag_r, flag_w, flag_x, flag_p; - char path[512] = ""; - fields = sscanf(line, "%"PRIx64"-%"PRIx64" %c%c%c%c %"PRIx64" %x:%x %d" - " %512s", &min, &max, &flag_r, &flag_w, &flag_x, - &flag_p, &offset, &dev_maj, &dev_min, &inode, path); - - if ((fields < 10) || (fields > 11)) { - continue; - } - if (h2g_valid(min)) { + if (h2g_valid(e->start)) { + unsigned long min = e->start; + unsigned long max = e->end; int flags = page_get_flags(h2g(min)); - max = h2g_valid(max - 1) ? max : (uintptr_t)g2h(GUEST_ADDR_MAX) + 1; + const char *path; + + max = h2g_valid(max - 1) ? + max : (uintptr_t) g2h(GUEST_ADDR_MAX) + 1; + if (page_check_range(h2g(min), max - min, flags) == -1) { continue; } + if (h2g(min) == ts->info->stack_limit) { - pstrcpy(path, sizeof(path), " [stack]"); + path = "[stack]"; + } else { + path = e->path; + } + + count = dprintf(fd, TARGET_ABI_FMT_ptr "-" TARGET_ABI_FMT_ptr + " %c%c%c%c %08" PRIx64 " %s %"PRId64, + h2g(min), h2g(max - 1) + 1, + e->is_read ? 'r' : '-', + e->is_write ? 'w' : '-', + e->is_exec ? 'x' : '-', + e->is_priv ? 'p' : '-', + (uint64_t) e->offset, e->dev, e->inode); + if (path) { + dprintf(fd, "%*s%s\n", 73 - count, "", path); + } else { + dprintf(fd, "\n"); } - dprintf(fd, TARGET_ABI_FMT_ptr "-" TARGET_ABI_FMT_ptr - " %c%c%c%c %08" PRIx64 " %02x:%02x %d %s%s\n", - h2g(min), h2g(max - 1) + 1, flag_r, flag_w, - flag_x, flag_p, offset, dev_maj, dev_min, inode, - path[0] ? " " : "", path); } } - free(line); - fclose(fp); + free_self_maps(map_info); + +#ifdef TARGET_VSYSCALL_PAGE + /* + * We only support execution from the vsyscall page. + * This is as if CONFIG_LEGACY_VSYSCALL_XONLY=y from v5.3. + */ + count = dprintf(fd, TARGET_FMT_lx "-" TARGET_FMT_lx + " --xp 00000000 00:00 0", + TARGET_VSYSCALL_PAGE, TARGET_VSYSCALL_PAGE + TARGET_PAGE_SIZE); + dprintf(fd, "%*s%s\n", 73 - count, "", "[vsyscall]"); +#endif return 0; } @@ -7015,34 +7295,29 @@ static int open_self_stat(void *cpu_env, int fd) { CPUState *cpu = env_cpu((CPUArchState *)cpu_env); TaskState *ts = cpu->opaque; - abi_ulong start_stack = ts->info->start_stack; + g_autoptr(GString) buf = g_string_new(NULL); int i; for (i = 0; i < 44; i++) { - char buf[128]; - int len; - uint64_t val = 0; + if (i == 0) { + /* pid */ + g_string_printf(buf, FMT_pid " ", getpid()); + } else if (i == 1) { + /* app name */ + gchar *bin = g_strrstr(ts->bprm->argv[0], "/"); + bin = bin ? bin + 1 : ts->bprm->argv[0]; + g_string_printf(buf, "(%.15s) ", bin); + } else if (i == 27) { + /* stack bottom */ + g_string_printf(buf, TARGET_ABI_FMT_ld " ", ts->info->start_stack); + } else { + /* for the rest, there is MasterCard */ + g_string_printf(buf, "0%c", i == 43 ? '\n' : ' '); + } - if (i == 0) { - /* pid */ - val = getpid(); - snprintf(buf, sizeof(buf), "%"PRId64 " ", val); - } else if (i == 1) { - /* app name */ - snprintf(buf, sizeof(buf), "(%s) ", ts->bprm->argv[0]); - } else if (i == 27) { - /* stack bottom */ - val = start_stack; - snprintf(buf, sizeof(buf), "%"PRId64 " ", val); - } else { - /* for the rest, there is MasterCard */ - snprintf(buf, sizeof(buf), "0%c", i == 43 ? '\n' : ' '); - } - - len = strlen(buf); - if (write(fd, buf, len) != len) { - return -1; - } + if (write(fd, buf->str, buf->len) != buf->len) { + return -1; + } } return 0; @@ -7373,7 +7648,7 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1, ts = cpu->opaque; if (ts->child_tidptr) { put_user_u32(0, ts->child_tidptr); - sys_futex(g2h(ts->child_tidptr), FUTEX_WAKE, INT_MAX, + do_sys_futex(g2h(ts->child_tidptr), FUTEX_WAKE, INT_MAX, NULL, NULL, 0); } thread_cpu = NULL; @@ -8420,6 +8695,7 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1, } } return ret; +#ifdef TARGET_NR_rt_sigtimedwait case TARGET_NR_rt_sigtimedwait: { sigset_t set; @@ -8456,6 +8732,7 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1, } } return ret; +#endif case TARGET_NR_rt_sigqueueinfo: { siginfo_t uinfo; @@ -8555,16 +8832,25 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1, } } return ret; +#if defined(TARGET_NR_gettimeofday) case TARGET_NR_gettimeofday: { struct timeval tv; - ret = get_errno(gettimeofday(&tv, NULL)); + struct timezone tz; + + ret = get_errno(gettimeofday(&tv, &tz)); if (!is_error(ret)) { - if (copy_to_user_timeval(arg1, &tv)) + if (arg1 && copy_to_user_timeval(arg1, &tv)) { return -TARGET_EFAULT; + } + if (arg2 && copy_to_user_timezone(arg2, &tz)) { + return -TARGET_EFAULT; + } } } return ret; +#endif +#if defined(TARGET_NR_settimeofday) case TARGET_NR_settimeofday: { struct timeval tv, *ptv = NULL; @@ -8586,6 +8872,7 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1, return get_errno(settimeofday(ptv, ptz)); } +#endif #if defined(TARGET_NR_select) case TARGET_NR_select: #if defined(TARGET_WANT_NI_OLD_SELECT) @@ -9057,6 +9344,8 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1, #ifdef TARGET_NR_sendmmsg case TARGET_NR_sendmmsg: return do_sendrecvmmsg(arg1, arg2, arg3, arg4, 1); +#endif +#ifdef TARGET_NR_recvmmsg case TARGET_NR_recvmmsg: return do_sendrecvmmsg(arg1, arg2, arg3, arg4, 0); #endif @@ -9231,6 +9520,7 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1, return do_syscall(cpu_env, arg1 & 0xffff, arg2, arg3, arg4, arg5, arg6, arg7, arg8, 0); #endif +#if defined(TARGET_NR_wait4) case TARGET_NR_wait4: { int status; @@ -9258,6 +9548,7 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1, } } return ret; +#endif #ifdef TARGET_NR_swapoff case TARGET_NR_swapoff: if (!(p = lock_user_string(arg1))) @@ -9402,6 +9693,7 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1, return do_vm86(cpu_env, arg1, arg2); #endif #endif +#if defined(TARGET_NR_adjtimex) case TARGET_NR_adjtimex: { struct timex host_buf; @@ -9417,6 +9709,7 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1, } } return ret; +#endif #if defined(TARGET_NR_clock_adjtime) && defined(CONFIG_CLOCK_ADJTIME) case TARGET_NR_clock_adjtime: { @@ -9933,6 +10226,7 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1, return get_errno(sched_get_priority_max(arg1)); case TARGET_NR_sched_get_priority_min: return get_errno(sched_get_priority_min(arg1)); +#ifdef TARGET_NR_sched_rr_get_interval case TARGET_NR_sched_rr_get_interval: { struct timespec ts; @@ -9942,6 +10236,8 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1, } } return ret; +#endif +#if defined(TARGET_NR_nanosleep) case TARGET_NR_nanosleep: { struct timespec req, rem; @@ -9952,6 +10248,7 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1, } } return ret; +#endif case TARGET_NR_prctl: switch (arg1) { case PR_GET_PDEATHSIG: @@ -10173,11 +10470,7 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1, break; #ifdef TARGET_NR_arch_prctl case TARGET_NR_arch_prctl: -#if defined(TARGET_I386) && !defined(TARGET_ABI32) return do_arch_prctl(cpu_env, arg1, arg2); -#else -#error unreachable -#endif #endif #ifdef TARGET_NR_pread64 case TARGET_NR_pread64: @@ -11037,11 +11330,11 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1, This is a hint, so ignoring and returning success is ok. */ return 0; #endif -#if TARGET_ABI_BITS == 32 +#ifdef TARGET_NR_fcntl64 case TARGET_NR_fcntl64: { - int cmd; - struct flock64 fl; + int cmd; + struct flock64 fl; from_flock64_fn *copyfrom = copy_from_user_flock64; to_flock64_fn *copyto = copy_to_user_flock64; @@ -11052,7 +11345,7 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1, } #endif - cmd = target_to_host_fcntl_cmd(arg2); + cmd = target_to_host_fcntl_cmd(arg2); if (cmd == -TARGET_EINVAL) { return cmd; } @@ -11326,6 +11619,18 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1, return ret; } #endif +#ifdef TARGET_NR_clock_settime64 + case TARGET_NR_clock_settime64: + { + struct timespec ts; + + ret = target_to_host_timespec64(&ts, arg2); + if (!is_error(ret)) { + ret = get_errno(clock_settime(arg1, &ts)); + } + return ret; + } +#endif #ifdef TARGET_NR_clock_gettime case TARGET_NR_clock_gettime: { @@ -11337,6 +11642,17 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1, return ret; } #endif +#ifdef TARGET_NR_clock_gettime64 + case TARGET_NR_clock_gettime64: + { + struct timespec ts; + ret = get_errno(clock_gettime(arg1, &ts)); + if (!is_error(ret)) { + ret = host_to_target_timespec64(arg2, &ts); + } + return ret; + } +#endif #ifdef TARGET_NR_clock_getres case TARGET_NR_clock_getres: { @@ -11422,8 +11738,14 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1, } return ret; #endif +#ifdef TARGET_NR_futex case TARGET_NR_futex: return do_futex(arg1, arg2, arg3, arg4, arg5, arg6); +#endif +#ifdef TARGET_NR_futex_time64 + case TARGET_NR_futex_time64: + return do_futex_time64(arg1, arg2, arg3, arg4, arg5, arg6); +#endif #if defined(TARGET_NR_inotify_init) && defined(__NR_inotify_init) case TARGET_NR_inotify_init: ret = get_errno(sys_inotify_init()); @@ -11488,6 +11810,7 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1, unlock_user (p, arg1, 0); return ret; +#ifdef TARGET_NR_mq_timedsend case TARGET_NR_mq_timedsend: { struct timespec ts; @@ -11503,7 +11826,9 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1, unlock_user (p, arg2, arg3); } return ret; +#endif +#ifdef TARGET_NR_mq_timedreceive case TARGET_NR_mq_timedreceive: { struct timespec ts; @@ -11524,6 +11849,7 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1, put_user_u32(prio, arg4); } return ret; +#endif /* Not implemented for now... */ /* case TARGET_NR_mq_notify: */ @@ -11653,8 +11979,14 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1, #endif return ret; #endif +#if defined(TARGET_NR_sync_file_range2) || \ + defined(TARGET_NR_arm_sync_file_range) #if defined(TARGET_NR_sync_file_range2) case TARGET_NR_sync_file_range2: +#endif +#if defined(TARGET_NR_arm_sync_file_range) + case TARGET_NR_arm_sync_file_range: +#endif /* This is like sync_file_range but the arguments are reordered */ #if TARGET_ABI_BITS == 32 ret = get_errno(sync_file_range(arg1, target_offset64(arg3, arg4), @@ -11680,7 +12012,7 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1, #endif #if defined(TARGET_NR_epoll_create1) && defined(CONFIG_EPOLL_CREATE1) case TARGET_NR_epoll_create1: - return get_errno(epoll_create1(arg1)); + return get_errno(epoll_create1(target_to_host_bitmask(arg1, fcntl_flags_tbl))); #endif #if defined(TARGET_NR_epoll_ctl) case TARGET_NR_epoll_ctl: @@ -11797,7 +12129,10 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1, struct target_rlimit64 *target_rnew, *target_rold; struct host_rlimit64 rnew, rold, *rnewp = 0; int resource = target_to_host_resource(arg2); - if (arg3) { + + if (arg3 && (resource != RLIMIT_AS && + resource != RLIMIT_DATA && + resource != RLIMIT_STACK)) { if (!lock_user_struct(VERIFY_READ, target_rnew, arg3, 1)) { return -TARGET_EFAULT; } @@ -12061,6 +12396,10 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1, unlock_user(p, arg1, 0); return ret; #endif +#if defined TARGET_NR_membarrier && defined __NR_membarrier + case TARGET_NR_membarrier: + return get_errno(membarrier(arg1, arg2)); +#endif default: qemu_log_mask(LOG_UNIMP, "Unsupported syscall: %d\n", num); @@ -12094,14 +12433,15 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, record_syscall_start(cpu, num, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); - if (unlikely(do_strace)) { + if (unlikely(qemu_loglevel_mask(LOG_STRACE))) { print_syscall(num, arg1, arg2, arg3, arg4, arg5, arg6); - ret = do_syscall1(cpu_env, num, arg1, arg2, arg3, arg4, - arg5, arg6, arg7, arg8); + } + + ret = do_syscall1(cpu_env, num, arg1, arg2, arg3, arg4, + arg5, arg6, arg7, arg8); + + if (unlikely(qemu_loglevel_mask(LOG_STRACE))) { print_syscall_ret(num, ret); - } else { - ret = do_syscall1(cpu_env, num, arg1, arg2, arg3, arg4, - arg5, arg6, arg7, arg8); } record_syscall_return(cpu, num, ret); diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index 98c2119de9..152ec637cb 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -763,6 +763,42 @@ struct target_pollfd { #define TARGET_KDSETLED 0x4B32 /* set led state [lights, not flags] */ #define TARGET_KDSIGACCEPT 0x4B4E +struct target_rtc_pll_info { + int pll_ctrl; + int pll_value; + int pll_max; + int pll_min; + int pll_posmult; + int pll_negmult; + abi_long pll_clock; +}; + +/* real time clock ioctls */ +#define TARGET_RTC_AIE_ON TARGET_IO('p', 0x01) +#define TARGET_RTC_AIE_OFF TARGET_IO('p', 0x02) +#define TARGET_RTC_UIE_ON TARGET_IO('p', 0x03) +#define TARGET_RTC_UIE_OFF TARGET_IO('p', 0x04) +#define TARGET_RTC_PIE_ON TARGET_IO('p', 0x05) +#define TARGET_RTC_PIE_OFF TARGET_IO('p', 0x06) +#define TARGET_RTC_WIE_ON TARGET_IO('p', 0x0f) +#define TARGET_RTC_WIE_OFF TARGET_IO('p', 0x10) +#define TARGET_RTC_ALM_READ TARGET_IOR('p', 0x08, struct rtc_time) +#define TARGET_RTC_ALM_SET TARGET_IOW('p', 0x07, struct rtc_time) +#define TARGET_RTC_RD_TIME TARGET_IOR('p', 0x09, struct rtc_time) +#define TARGET_RTC_SET_TIME TARGET_IOW('p', 0x0a, struct rtc_time) +#define TARGET_RTC_IRQP_READ TARGET_IOR('p', 0x0b, abi_ulong) +#define TARGET_RTC_IRQP_SET TARGET_IOW('p', 0x0c, abi_ulong) +#define TARGET_RTC_EPOCH_READ TARGET_IOR('p', 0x0d, abi_ulong) +#define TARGET_RTC_EPOCH_SET TARGET_IOW('p', 0x0e, abi_ulong) +#define TARGET_RTC_WKALM_RD TARGET_IOR('p', 0x10, struct rtc_wkalrm) +#define TARGET_RTC_WKALM_SET TARGET_IOW('p', 0x0f, struct rtc_wkalrm) +#define TARGET_RTC_PLL_GET TARGET_IOR('p', 0x11, \ + struct target_rtc_pll_info) +#define TARGET_RTC_PLL_SET TARGET_IOW('p', 0x12, \ + struct target_rtc_pll_info) +#define TARGET_RTC_VL_READ TARGET_IOR('p', 0x13, int) +#define TARGET_RTC_VL_CLR TARGET_IO('p', 0x14) + #if defined(TARGET_ALPHA) || defined(TARGET_MIPS) || defined(TARGET_SH4) || \ defined(TARGET_XTENSA) #define TARGET_FIOGETOWN TARGET_IOR('f', 123, int) @@ -899,7 +935,13 @@ struct target_pollfd { #define TARGET_FDMSGON TARGET_IO(2, 0x45) #define TARGET_FDMSGOFF TARGET_IO(2, 0x46) +#define TARGET_FDFMTBEG TARGET_IO(2, 0x47) +#define TARGET_FDFMTTRK TARGET_IOW(2, 0x48, struct format_descr) +#define TARGET_FDFMTEND TARGET_IO(2, 0x49) +#define TARGET_FDSETEMSGTRESH TARGET_IO(2, 0x4a) #define TARGET_FDFLUSH TARGET_IO(2, 0x4b) +#define TARGET_FDSETMAXERRS TARGET_IOW(2, 0x4c, struct floppy_max_errors) +#define TARGET_FDGETMAXERRS TARGET_IOR(2, 0x0e, struct floppy_max_errors) #define TARGET_FDRESET TARGET_IO(2, 0x54) #define TARGET_FDRAWCMD TARGET_IO(2, 0x58) #define TARGET_FDTWADDLE TARGET_IO(2, 0x59) @@ -911,13 +953,19 @@ struct target_pollfd { #define TARGET_FICLONE TARGET_IOW(0x94, 9, int) #define TARGET_FICLONERANGE TARGET_IOW(0x94, 13, struct file_clone_range) -/* Note that the ioctl numbers claim type "long" but the actual type - * used by the kernel is "int". +/* + * Note that the ioctl numbers for FS_IOC_ + * claim type "long" but the actual type used by the kernel is "int". */ #define TARGET_FS_IOC_GETFLAGS TARGET_IOR('f', 1, abi_long) #define TARGET_FS_IOC_SETFLAGS TARGET_IOW('f', 2, abi_long) - +#define TARGET_FS_IOC_GETVERSION TARGET_IOR('v', 1, abi_long) +#define TARGET_FS_IOC_SETVERSION TARGET_IOW('v', 2, abi_long) #define TARGET_FS_IOC_FIEMAP TARGET_IOWR('f',11,struct fiemap) +#define TARGET_FS_IOC32_GETFLAGS TARGET_IOR('f', 1, int) +#define TARGET_FS_IOC32_SETFLAGS TARGET_IOW('f', 2, int) +#define TARGET_FS_IOC32_GETVERSION TARGET_IOR('v', 1, int) +#define TARGET_FS_IOC32_SETVERSION TARGET_IOW('v', 2, int) /* usb ioctls */ #define TARGET_USBDEVFS_CONTROL TARGET_IOWRU('U', 0) @@ -2389,6 +2437,90 @@ struct target_statfs64 { #define TARGET_SOUND_MIXER_WRITE_RECSRC TARGET_MIXER_WRITE(SOUND_MIXER_RECSRC) +struct target_snd_timer_id { + int dev_class; + int dev_sclass; + int card; + int device; + int subdevice; +}; + +struct target_snd_timer_ginfo { + struct target_snd_timer_id tid; + unsigned int flags; + int card; + unsigned char id[64]; + unsigned char name[80]; + abi_ulong reserved0; + abi_ulong resolution; + abi_ulong resolution_min; + abi_ulong resolution_max; + unsigned int clients; + unsigned char reserved[32]; +}; + +struct target_snd_timer_gparams { + struct target_snd_timer_id tid; + abi_ulong period_num; + abi_ulong period_den; + unsigned char reserved[32]; +}; + +struct target_snd_timer_gstatus { + struct target_snd_timer_id tid; + abi_ulong resolution; + abi_ulong resolution_num; + abi_ulong resolution_den; + unsigned char reserved[32]; +}; + +struct target_snd_timer_select { + struct target_snd_timer_id id; + unsigned char reserved[32]; +}; + +struct target_snd_timer_info { + unsigned int flags; + int card; + unsigned char id[64]; + unsigned char name[80]; + abi_ulong reserved0; + abi_ulong resolution; + unsigned char reserved[64]; +}; + +struct target_snd_timer_status { + struct target_timespec tstamp; + unsigned int resolution; + unsigned int lost; + unsigned int overrun; + unsigned int queue; + unsigned char reserved[64]; +}; + +/* alsa timer ioctls */ +#define TARGET_SNDRV_TIMER_IOCTL_PVERSION TARGET_IOR('T', 0x00, int) +#define TARGET_SNDRV_TIMER_IOCTL_NEXT_DEVICE TARGET_IOWR('T', 0x01, \ + struct snd_timer_id) +#define TARGET_SNDRV_TIMER_IOCTL_GINFO TARGET_IOWR('T', 0x03, \ + struct target_snd_timer_ginfo) +#define TARGET_SNDRV_TIMER_IOCTL_GPARAMS TARGET_IOW('T', 0x04, \ + struct target_snd_timer_gparams) +#define TARGET_SNDRV_TIMER_IOCTL_GSTATUS TARGET_IOWR('T', 0x05, \ + struct target_snd_timer_gstatus) +#define TARGET_SNDRV_TIMER_IOCTL_SELECT TARGET_IOW('T', 0x10, \ + struct target_snd_timer_select) +#define TARGET_SNDRV_TIMER_IOCTL_INFO TARGET_IOR('T', 0x11, \ + struct target_snd_timer_info) +#define TARGET_SNDRV_TIMER_IOCTL_PARAMS TARGET_IOW('T', 0x12, \ + struct snd_timer_params) +#define TARGET_SNDRV_TIMER_IOCTL_STATUS TARGET_IOR('T', 0x14, \ + struct target_snd_timer_status) +#define TARGET_SNDRV_TIMER_IOCTL_START TARGET_IO('T', 0xa0) +#define TARGET_SNDRV_TIMER_IOCTL_STOP TARGET_IO('T', 0xa1) +#define TARGET_SNDRV_TIMER_IOCTL_CONTINUE TARGET_IO('T', 0xa2) +#define TARGET_SNDRV_TIMER_IOCTL_PAUSE TARGET_IO('T', 0xa3) + /* vfat ioctls */ #define TARGET_VFAT_IOCTL_READDIR_BOTH TARGET_IORU('r', 1) #define TARGET_VFAT_IOCTL_READDIR_SHORT TARGET_IORU('r', 2) @@ -2422,6 +2554,11 @@ struct target_mtpos { #define TARGET_MTIOCGET TARGET_IOR('m', 2, struct target_mtget) #define TARGET_MTIOCPOS TARGET_IOR('m', 3, struct target_mtpos) +/* kcov ioctls */ +#define TARGET_KCOV_ENABLE TARGET_IO('c', 100) +#define TARGET_KCOV_DISABLE TARGET_IO('c', 101) +#define TARGET_KCOV_INIT_TRACE TARGET_IOR('c', 1, abi_ulong) + struct target_sysinfo { abi_long uptime; /* Seconds since boot */ abi_ulong loads[3]; /* 1, 5, and 15 minute load averages */ diff --git a/linux-user/syscall_types.h b/linux-user/syscall_types.h index 4e36983826..4e12c1661e 100644 --- a/linux-user/syscall_types.h +++ b/linux-user/syscall_types.h @@ -83,6 +83,72 @@ STRUCT(buffmem_desc, STRUCT(mixer_info, MK_ARRAY(TYPE_CHAR, 16), MK_ARRAY(TYPE_CHAR, 32), TYPE_INT, MK_ARRAY(TYPE_INT, 10)) +STRUCT(snd_timer_id, + TYPE_INT, /* dev_class */ + TYPE_INT, /* dev_sclass */ + TYPE_INT, /* card */ + TYPE_INT, /* device */ + TYPE_INT) /* subdevice */ + +STRUCT(snd_timer_ginfo, + MK_STRUCT(STRUCT_snd_timer_id), /* tid */ + TYPE_INT, /* flags */ + TYPE_INT, /* card */ + MK_ARRAY(TYPE_CHAR, 64), /* id */ + MK_ARRAY(TYPE_CHAR, 80), /* name */ + TYPE_ULONG, /* reserved0 */ + TYPE_ULONG, /* resolution */ + TYPE_ULONG, /* resolution_min */ + TYPE_ULONG, /* resolution_max */ + TYPE_INT, /* clients */ + MK_ARRAY(TYPE_CHAR, 32)) /* reserved */ + +STRUCT(snd_timer_gparams, + MK_STRUCT(STRUCT_snd_timer_id), /* tid */ + TYPE_ULONG, /* period_num */ + TYPE_ULONG, /* period_den */ + MK_ARRAY(TYPE_CHAR, 32)) /* reserved */ + +STRUCT(snd_timer_gstatus, + MK_STRUCT(STRUCT_snd_timer_id), /* tid */ + TYPE_ULONG, /* resolution */ + TYPE_ULONG, /* resolution_num */ + TYPE_ULONG, /* resolution_den */ + MK_ARRAY(TYPE_CHAR, 32)) /* reserved */ + +STRUCT(snd_timer_select, + MK_STRUCT(STRUCT_snd_timer_id), /* id */ + MK_ARRAY(TYPE_CHAR, 32)) /* reserved */ + +STRUCT(snd_timer_info, + TYPE_INT, /* flags */ + TYPE_INT, /* card */ + MK_ARRAY(TYPE_CHAR, 64), /* id */ + MK_ARRAY(TYPE_CHAR, 80), /* name */ + TYPE_ULONG, /* reserved0 */ + TYPE_ULONG, /* resolution */ + MK_ARRAY(TYPE_CHAR, 64)) /* reserved */ + +STRUCT(snd_timer_params, + TYPE_INT, /* flags */ + TYPE_INT, /* ticks */ + TYPE_INT, /* queue_size */ + TYPE_INT, /* reserved0 */ + TYPE_INT, /* filter */ + MK_ARRAY(TYPE_CHAR, 60)) /* reserved */ + +STRUCT(timespec, + TYPE_LONG, /* tv_sec */ + TYPE_LONG) /* tv_nsec */ + +STRUCT(snd_timer_status, + MK_STRUCT(STRUCT_timespec), /* tstamp */ + TYPE_INT, /* resolution */ + TYPE_INT, /* lost */ + TYPE_INT, /* overrun */ + TYPE_INT, /* queue */ + MK_ARRAY(TYPE_CHAR, 64)) /* reserved */ + /* loop device ioctls */ STRUCT(loop_info, TYPE_INT, /* lo_number */ @@ -255,12 +321,49 @@ STRUCT(blkpg_partition, MK_ARRAY(TYPE_CHAR, BLKPG_DEVNAMELTH), /* devname */ MK_ARRAY(TYPE_CHAR, BLKPG_VOLNAMELTH)) /* volname */ +STRUCT(rtc_time, + TYPE_INT, /* tm_sec */ + TYPE_INT, /* tm_min */ + TYPE_INT, /* tm_hour */ + TYPE_INT, /* tm_mday */ + TYPE_INT, /* tm_mon */ + TYPE_INT, /* tm_year */ + TYPE_INT, /* tm_wday */ + TYPE_INT, /* tm_yday */ + TYPE_INT) /* tm_isdst */ + +STRUCT(rtc_wkalrm, + TYPE_CHAR, /* enabled */ + TYPE_CHAR, /* pending */ + MK_STRUCT(STRUCT_rtc_time)) /* time */ + +STRUCT(rtc_pll_info, + TYPE_INT, /* pll_ctrl */ + TYPE_INT, /* pll_value */ + TYPE_INT, /* pll_max */ + TYPE_INT, /* pll_min */ + TYPE_INT, /* pll_posmult */ + TYPE_INT, /* pll_negmult */ + TYPE_LONG) /* pll_clock */ + STRUCT(blkpg_ioctl_arg, TYPE_INT, /* op */ TYPE_INT, /* flags */ TYPE_INT, /* datalen */ TYPE_PTRVOID) /* data */ +STRUCT(format_descr, + TYPE_INT, /* device */ + TYPE_INT, /* head */ + TYPE_INT) /* track */ + +STRUCT(floppy_max_errors, + TYPE_INT, /* abort */ + TYPE_INT, /* read_track */ + TYPE_INT, /* reset */ + TYPE_INT, /* recal */ + TYPE_INT) /* reporting */ + #if defined(CONFIG_USBFS) /* usb device ioctls */ STRUCT(usbdevfs_ctrltransfer, diff --git a/linux-user/trace-events b/linux-user/trace-events index 6df234bbb6..0296133dae 100644 --- a/linux-user/trace-events +++ b/linux-user/trace-events @@ -1,6 +1,9 @@ # See docs/devel/tracing.txt for syntax documentation. # signal.c +signal_table_init(int i) "number of unavailable signals: %d" +signal_do_sigaction_guest(int sig, int max) "target signal %d (MAX %d)" +signal_do_sigaction_host(int sig, int max) "host signal %d (MAX %d)" # */signal.c user_setup_frame(void *env, uint64_t frame_addr) "env=%p frame_addr=0x%"PRIx64 user_setup_rt_frame(void *env, uint64_t frame_addr) "env=%p frame_addr=0x%"PRIx64 @@ -11,3 +14,9 @@ user_handle_signal(void *env, int target_sig) "env=%p signal %d" user_host_signal(void *env, int host_sig, int target_sig) "env=%p signal %d (target %d(" user_queue_signal(void *env, int target_sig) "env=%p signal %d" user_s390x_restore_sigregs(void *env, uint64_t sc_psw_addr, uint64_t env_psw_addr) "env=%p frame psw.addr 0x%"PRIx64 " current psw.addr 0x%"PRIx64 + +# mmap.c +target_mprotect(uint64_t start, uint64_t len, int flags) "start=0x%"PRIx64 " len=0x%"PRIx64 " prot=0x%x" +target_mmap(uint64_t start, uint64_t len, int pflags, int mflags, int fd, uint64_t offset) "start=0x%"PRIx64 " len=0x%"PRIx64 " prot=0x%x flags=0x%x fd=%d offset=0x%"PRIx64 +target_mmap_complete(uint64_t retaddr) "retaddr=0x%"PRIx64 +target_munmap(uint64_t start, uint64_t len) "start=0x%"PRIx64" len=0x%"PRIx64 diff --git a/linux-user/vm86.c b/linux-user/vm86.c index 2fa7a89edc..4412522c4c 100644 --- a/linux-user/vm86.c +++ b/linux-user/vm86.c @@ -402,7 +402,8 @@ int do_vm86(CPUX86State *env, long subfunction, abi_ulong vm86_addr) case TARGET_VM86_FREE_IRQ: case TARGET_VM86_GET_IRQ_BITS: case TARGET_VM86_GET_AND_RESET_IRQ: - gemu_log("qemu: unsupported vm86 subfunction (%ld)\n", subfunction); + qemu_log_mask(LOG_UNIMP, "qemu: unsupported vm86 subfunction (%ld)\n", + subfunction); ret = -TARGET_EINVAL; goto out; case TARGET_VM86_PLUS_INSTALL_CHECK: diff --git a/linux-user/x86_64/Makefile.objs b/linux-user/x86_64/Makefile.objs new file mode 100644 index 0000000000..2cef1d48be --- /dev/null +++ b/linux-user/x86_64/Makefile.objs @@ -0,0 +1,5 @@ +generated-files-y += linux-user/x86_64/syscall_nr.h + +syshdr := $(SRC_PATH)/linux-user/x86_64/syscallhdr.sh +%/syscall_nr.h: $(SRC_PATH)/linux-user/x86_64/syscall_64.tbl $(syshdr) + $(call quiet-command, sh $(syshdr) $< $@ $(TARGET_SYSTBL_ABI),"GEN","$@") diff --git a/linux-user/x86_64/syscall_64.tbl b/linux-user/x86_64/syscall_64.tbl new file mode 100644 index 0000000000..44d510bc9b --- /dev/null +++ b/linux-user/x86_64/syscall_64.tbl @@ -0,0 +1,404 @@ +# +# 64-bit system call numbers and entry vectors +# +# The format is: +# +# +# The __x64_sys_*() stubs are created on-the-fly for sys_*() system calls +# +# The abi is "common", "64" or "x32" for this file. +# +0 common read __x64_sys_read +1 common write __x64_sys_write +2 common open __x64_sys_open +3 common close __x64_sys_close +4 common stat __x64_sys_newstat +5 common fstat __x64_sys_newfstat +6 common lstat __x64_sys_newlstat +7 common poll __x64_sys_poll +8 common lseek __x64_sys_lseek +9 common mmap __x64_sys_mmap +10 common mprotect __x64_sys_mprotect +11 common munmap __x64_sys_munmap +12 common brk __x64_sys_brk +13 64 rt_sigaction __x64_sys_rt_sigaction +14 common rt_sigprocmask __x64_sys_rt_sigprocmask +15 64 rt_sigreturn __x64_sys_rt_sigreturn/ptregs +16 64 ioctl __x64_sys_ioctl +17 common pread64 __x64_sys_pread64 +18 common pwrite64 __x64_sys_pwrite64 +19 64 readv __x64_sys_readv +20 64 writev __x64_sys_writev +21 common access __x64_sys_access +22 common pipe __x64_sys_pipe +23 common select __x64_sys_select +24 common sched_yield __x64_sys_sched_yield +25 common mremap __x64_sys_mremap +26 common msync __x64_sys_msync +27 common mincore __x64_sys_mincore +28 common madvise __x64_sys_madvise +29 common shmget __x64_sys_shmget +30 common shmat __x64_sys_shmat +31 common shmctl __x64_sys_shmctl +32 common dup __x64_sys_dup +33 common dup2 __x64_sys_dup2 +34 common pause __x64_sys_pause +35 common nanosleep __x64_sys_nanosleep +36 common getitimer __x64_sys_getitimer +37 common alarm __x64_sys_alarm +38 common setitimer __x64_sys_setitimer +39 common getpid __x64_sys_getpid +40 common sendfile __x64_sys_sendfile64 +41 common socket __x64_sys_socket +42 common connect __x64_sys_connect +43 common accept __x64_sys_accept +44 common sendto __x64_sys_sendto +45 64 recvfrom __x64_sys_recvfrom +46 64 sendmsg __x64_sys_sendmsg +47 64 recvmsg __x64_sys_recvmsg +48 common shutdown __x64_sys_shutdown +49 common bind __x64_sys_bind +50 common listen __x64_sys_listen +51 common getsockname __x64_sys_getsockname +52 common getpeername __x64_sys_getpeername +53 common socketpair __x64_sys_socketpair +54 64 setsockopt __x64_sys_setsockopt +55 64 getsockopt __x64_sys_getsockopt +56 common clone __x64_sys_clone/ptregs +57 common fork __x64_sys_fork/ptregs +58 common vfork __x64_sys_vfork/ptregs +59 64 execve __x64_sys_execve/ptregs +60 common exit __x64_sys_exit +61 common wait4 __x64_sys_wait4 +62 common kill __x64_sys_kill +63 common uname __x64_sys_newuname +64 common semget __x64_sys_semget +65 common semop __x64_sys_semop +66 common semctl __x64_sys_semctl +67 common shmdt __x64_sys_shmdt +68 common msgget __x64_sys_msgget +69 common msgsnd __x64_sys_msgsnd +70 common msgrcv __x64_sys_msgrcv +71 common msgctl __x64_sys_msgctl +72 common fcntl __x64_sys_fcntl +73 common flock __x64_sys_flock +74 common fsync __x64_sys_fsync +75 common fdatasync __x64_sys_fdatasync +76 common truncate __x64_sys_truncate +77 common ftruncate __x64_sys_ftruncate +78 common getdents __x64_sys_getdents +79 common getcwd __x64_sys_getcwd +80 common chdir __x64_sys_chdir +81 common fchdir __x64_sys_fchdir +82 common rename __x64_sys_rename +83 common mkdir __x64_sys_mkdir +84 common rmdir __x64_sys_rmdir +85 common creat __x64_sys_creat +86 common link __x64_sys_link +87 common unlink __x64_sys_unlink +88 common symlink __x64_sys_symlink +89 common readlink __x64_sys_readlink +90 common chmod __x64_sys_chmod +91 common fchmod __x64_sys_fchmod +92 common chown __x64_sys_chown +93 common fchown __x64_sys_fchown +94 common lchown __x64_sys_lchown +95 common umask __x64_sys_umask +96 common gettimeofday __x64_sys_gettimeofday +97 common getrlimit __x64_sys_getrlimit +98 common getrusage __x64_sys_getrusage +99 common sysinfo __x64_sys_sysinfo +100 common times __x64_sys_times +101 64 ptrace __x64_sys_ptrace +102 common getuid __x64_sys_getuid +103 common syslog __x64_sys_syslog +104 common getgid __x64_sys_getgid +105 common setuid __x64_sys_setuid +106 common setgid __x64_sys_setgid +107 common geteuid __x64_sys_geteuid +108 common getegid __x64_sys_getegid +109 common setpgid __x64_sys_setpgid +110 common getppid __x64_sys_getppid +111 common getpgrp __x64_sys_getpgrp +112 common setsid __x64_sys_setsid +113 common setreuid __x64_sys_setreuid +114 common setregid __x64_sys_setregid +115 common getgroups __x64_sys_getgroups +116 common setgroups __x64_sys_setgroups +117 common setresuid __x64_sys_setresuid +118 common getresuid __x64_sys_getresuid +119 common setresgid __x64_sys_setresgid +120 common getresgid __x64_sys_getresgid +121 common getpgid __x64_sys_getpgid +122 common setfsuid __x64_sys_setfsuid +123 common setfsgid __x64_sys_setfsgid +124 common getsid __x64_sys_getsid +125 common capget __x64_sys_capget +126 common capset __x64_sys_capset +127 64 rt_sigpending __x64_sys_rt_sigpending +128 64 rt_sigtimedwait __x64_sys_rt_sigtimedwait +129 64 rt_sigqueueinfo __x64_sys_rt_sigqueueinfo +130 common rt_sigsuspend __x64_sys_rt_sigsuspend +131 64 sigaltstack __x64_sys_sigaltstack +132 common utime __x64_sys_utime +133 common mknod __x64_sys_mknod +134 64 uselib +135 common personality __x64_sys_personality +136 common ustat __x64_sys_ustat +137 common statfs __x64_sys_statfs +138 common fstatfs __x64_sys_fstatfs +139 common sysfs __x64_sys_sysfs +140 common getpriority __x64_sys_getpriority +141 common setpriority __x64_sys_setpriority +142 common sched_setparam __x64_sys_sched_setparam +143 common sched_getparam __x64_sys_sched_getparam +144 common sched_setscheduler __x64_sys_sched_setscheduler +145 common sched_getscheduler __x64_sys_sched_getscheduler +146 common sched_get_priority_max __x64_sys_sched_get_priority_max +147 common sched_get_priority_min __x64_sys_sched_get_priority_min +148 common sched_rr_get_interval __x64_sys_sched_rr_get_interval +149 common mlock __x64_sys_mlock +150 common munlock __x64_sys_munlock +151 common mlockall __x64_sys_mlockall +152 common munlockall __x64_sys_munlockall +153 common vhangup __x64_sys_vhangup +154 common modify_ldt __x64_sys_modify_ldt +155 common pivot_root __x64_sys_pivot_root +156 64 _sysctl __x64_sys_sysctl +157 common prctl __x64_sys_prctl +158 common arch_prctl __x64_sys_arch_prctl +159 common adjtimex __x64_sys_adjtimex +160 common setrlimit __x64_sys_setrlimit +161 common chroot __x64_sys_chroot +162 common sync __x64_sys_sync +163 common acct __x64_sys_acct +164 common settimeofday __x64_sys_settimeofday +165 common mount __x64_sys_mount +166 common umount2 __x64_sys_umount +167 common swapon __x64_sys_swapon +168 common swapoff __x64_sys_swapoff +169 common reboot __x64_sys_reboot +170 common sethostname __x64_sys_sethostname +171 common setdomainname __x64_sys_setdomainname +172 common iopl __x64_sys_iopl/ptregs +173 common ioperm __x64_sys_ioperm +174 64 create_module +175 common init_module __x64_sys_init_module +176 common delete_module __x64_sys_delete_module +177 64 get_kernel_syms +178 64 query_module +179 common quotactl __x64_sys_quotactl +180 64 nfsservctl +181 common getpmsg +182 common putpmsg +183 common afs_syscall +184 common tuxcall +185 common security +186 common gettid __x64_sys_gettid +187 common readahead __x64_sys_readahead +188 common setxattr __x64_sys_setxattr +189 common lsetxattr __x64_sys_lsetxattr +190 common fsetxattr __x64_sys_fsetxattr +191 common getxattr __x64_sys_getxattr +192 common lgetxattr __x64_sys_lgetxattr +193 common fgetxattr __x64_sys_fgetxattr +194 common listxattr __x64_sys_listxattr +195 common llistxattr __x64_sys_llistxattr +196 common flistxattr __x64_sys_flistxattr +197 common removexattr __x64_sys_removexattr +198 common lremovexattr __x64_sys_lremovexattr +199 common fremovexattr __x64_sys_fremovexattr +200 common tkill __x64_sys_tkill +201 common time __x64_sys_time +202 common futex __x64_sys_futex +203 common sched_setaffinity __x64_sys_sched_setaffinity +204 common sched_getaffinity __x64_sys_sched_getaffinity +205 64 set_thread_area +206 64 io_setup __x64_sys_io_setup +207 common io_destroy __x64_sys_io_destroy +208 common io_getevents __x64_sys_io_getevents +209 64 io_submit __x64_sys_io_submit +210 common io_cancel __x64_sys_io_cancel +211 64 get_thread_area +212 common lookup_dcookie __x64_sys_lookup_dcookie +213 common epoll_create __x64_sys_epoll_create +214 64 epoll_ctl_old +215 64 epoll_wait_old +216 common remap_file_pages __x64_sys_remap_file_pages +217 common getdents64 __x64_sys_getdents64 +218 common set_tid_address __x64_sys_set_tid_address +219 common restart_syscall __x64_sys_restart_syscall +220 common semtimedop __x64_sys_semtimedop +221 common fadvise64 __x64_sys_fadvise64 +222 64 timer_create __x64_sys_timer_create +223 common timer_settime __x64_sys_timer_settime +224 common timer_gettime __x64_sys_timer_gettime +225 common timer_getoverrun __x64_sys_timer_getoverrun +226 common timer_delete __x64_sys_timer_delete +227 common clock_settime __x64_sys_clock_settime +228 common clock_gettime __x64_sys_clock_gettime +229 common clock_getres __x64_sys_clock_getres +230 common clock_nanosleep __x64_sys_clock_nanosleep +231 common exit_group __x64_sys_exit_group +232 common epoll_wait __x64_sys_epoll_wait +233 common epoll_ctl __x64_sys_epoll_ctl +234 common tgkill __x64_sys_tgkill +235 common utimes __x64_sys_utimes +236 64 vserver +237 common mbind __x64_sys_mbind +238 common set_mempolicy __x64_sys_set_mempolicy +239 common get_mempolicy __x64_sys_get_mempolicy +240 common mq_open __x64_sys_mq_open +241 common mq_unlink __x64_sys_mq_unlink +242 common mq_timedsend __x64_sys_mq_timedsend +243 common mq_timedreceive __x64_sys_mq_timedreceive +244 64 mq_notify __x64_sys_mq_notify +245 common mq_getsetattr __x64_sys_mq_getsetattr +246 64 kexec_load __x64_sys_kexec_load +247 64 waitid __x64_sys_waitid +248 common add_key __x64_sys_add_key +249 common request_key __x64_sys_request_key +250 common keyctl __x64_sys_keyctl +251 common ioprio_set __x64_sys_ioprio_set +252 common ioprio_get __x64_sys_ioprio_get +253 common inotify_init __x64_sys_inotify_init +254 common inotify_add_watch __x64_sys_inotify_add_watch +255 common inotify_rm_watch __x64_sys_inotify_rm_watch +256 common migrate_pages __x64_sys_migrate_pages +257 common openat __x64_sys_openat +258 common mkdirat __x64_sys_mkdirat +259 common mknodat __x64_sys_mknodat +260 common fchownat __x64_sys_fchownat +261 common futimesat __x64_sys_futimesat +262 common newfstatat __x64_sys_newfstatat +263 common unlinkat __x64_sys_unlinkat +264 common renameat __x64_sys_renameat +265 common linkat __x64_sys_linkat +266 common symlinkat __x64_sys_symlinkat +267 common readlinkat __x64_sys_readlinkat +268 common fchmodat __x64_sys_fchmodat +269 common faccessat __x64_sys_faccessat +270 common pselect6 __x64_sys_pselect6 +271 common ppoll __x64_sys_ppoll +272 common unshare __x64_sys_unshare +273 64 set_robust_list __x64_sys_set_robust_list +274 64 get_robust_list __x64_sys_get_robust_list +275 common splice __x64_sys_splice +276 common tee __x64_sys_tee +277 common sync_file_range __x64_sys_sync_file_range +278 64 vmsplice __x64_sys_vmsplice +279 64 move_pages __x64_sys_move_pages +280 common utimensat __x64_sys_utimensat +281 common epoll_pwait __x64_sys_epoll_pwait +282 common signalfd __x64_sys_signalfd +283 common timerfd_create __x64_sys_timerfd_create +284 common eventfd __x64_sys_eventfd +285 common fallocate __x64_sys_fallocate +286 common timerfd_settime __x64_sys_timerfd_settime +287 common timerfd_gettime __x64_sys_timerfd_gettime +288 common accept4 __x64_sys_accept4 +289 common signalfd4 __x64_sys_signalfd4 +290 common eventfd2 __x64_sys_eventfd2 +291 common epoll_create1 __x64_sys_epoll_create1 +292 common dup3 __x64_sys_dup3 +293 common pipe2 __x64_sys_pipe2 +294 common inotify_init1 __x64_sys_inotify_init1 +295 64 preadv __x64_sys_preadv +296 64 pwritev __x64_sys_pwritev +297 64 rt_tgsigqueueinfo __x64_sys_rt_tgsigqueueinfo +298 common perf_event_open __x64_sys_perf_event_open +299 64 recvmmsg __x64_sys_recvmmsg +300 common fanotify_init __x64_sys_fanotify_init +301 common fanotify_mark __x64_sys_fanotify_mark +302 common prlimit64 __x64_sys_prlimit64 +303 common name_to_handle_at __x64_sys_name_to_handle_at +304 common open_by_handle_at __x64_sys_open_by_handle_at +305 common clock_adjtime __x64_sys_clock_adjtime +306 common syncfs __x64_sys_syncfs +307 64 sendmmsg __x64_sys_sendmmsg +308 common setns __x64_sys_setns +309 common getcpu __x64_sys_getcpu +310 64 process_vm_readv __x64_sys_process_vm_readv +311 64 process_vm_writev __x64_sys_process_vm_writev +312 common kcmp __x64_sys_kcmp +313 common finit_module __x64_sys_finit_module +314 common sched_setattr __x64_sys_sched_setattr +315 common sched_getattr __x64_sys_sched_getattr +316 common renameat2 __x64_sys_renameat2 +317 common seccomp __x64_sys_seccomp +318 common getrandom __x64_sys_getrandom +319 common memfd_create __x64_sys_memfd_create +320 common kexec_file_load __x64_sys_kexec_file_load +321 common bpf __x64_sys_bpf +322 64 execveat __x64_sys_execveat/ptregs +323 common userfaultfd __x64_sys_userfaultfd +324 common membarrier __x64_sys_membarrier +325 common mlock2 __x64_sys_mlock2 +326 common copy_file_range __x64_sys_copy_file_range +327 64 preadv2 __x64_sys_preadv2 +328 64 pwritev2 __x64_sys_pwritev2 +329 common pkey_mprotect __x64_sys_pkey_mprotect +330 common pkey_alloc __x64_sys_pkey_alloc +331 common pkey_free __x64_sys_pkey_free +332 common statx __x64_sys_statx +333 common io_pgetevents __x64_sys_io_pgetevents +334 common rseq __x64_sys_rseq +# don't use numbers 387 through 423, add new calls after the last +# 'common' entry +424 common pidfd_send_signal __x64_sys_pidfd_send_signal +425 common io_uring_setup __x64_sys_io_uring_setup +426 common io_uring_enter __x64_sys_io_uring_enter +427 common io_uring_register __x64_sys_io_uring_register +428 common open_tree __x64_sys_open_tree +429 common move_mount __x64_sys_move_mount +430 common fsopen __x64_sys_fsopen +431 common fsconfig __x64_sys_fsconfig +432 common fsmount __x64_sys_fsmount +433 common fspick __x64_sys_fspick +434 common pidfd_open __x64_sys_pidfd_open +435 common clone3 __x64_sys_clone3/ptregs +437 common openat2 __x64_sys_openat2 +438 common pidfd_getfd __x64_sys_pidfd_getfd + +# +# x32-specific system call numbers start at 512 to avoid cache impact +# for native 64-bit operation. The __x32_compat_sys stubs are created +# on-the-fly for compat_sys_*() compatibility system calls if X86_X32 +# is defined. +# +512 x32 rt_sigaction __x32_compat_sys_rt_sigaction +513 x32 rt_sigreturn sys32_x32_rt_sigreturn +514 x32 ioctl __x32_compat_sys_ioctl +515 x32 readv __x32_compat_sys_readv +516 x32 writev __x32_compat_sys_writev +517 x32 recvfrom __x32_compat_sys_recvfrom +518 x32 sendmsg __x32_compat_sys_sendmsg +519 x32 recvmsg __x32_compat_sys_recvmsg +520 x32 execve __x32_compat_sys_execve/ptregs +521 x32 ptrace __x32_compat_sys_ptrace +522 x32 rt_sigpending __x32_compat_sys_rt_sigpending +523 x32 rt_sigtimedwait __x32_compat_sys_rt_sigtimedwait_time64 +524 x32 rt_sigqueueinfo __x32_compat_sys_rt_sigqueueinfo +525 x32 sigaltstack __x32_compat_sys_sigaltstack +526 x32 timer_create __x32_compat_sys_timer_create +527 x32 mq_notify __x32_compat_sys_mq_notify +528 x32 kexec_load __x32_compat_sys_kexec_load +529 x32 waitid __x32_compat_sys_waitid +530 x32 set_robust_list __x32_compat_sys_set_robust_list +531 x32 get_robust_list __x32_compat_sys_get_robust_list +532 x32 vmsplice __x32_compat_sys_vmsplice +533 x32 move_pages __x32_compat_sys_move_pages +534 x32 preadv __x32_compat_sys_preadv64 +535 x32 pwritev __x32_compat_sys_pwritev64 +536 x32 rt_tgsigqueueinfo __x32_compat_sys_rt_tgsigqueueinfo +537 x32 recvmmsg __x32_compat_sys_recvmmsg_time64 +538 x32 sendmmsg __x32_compat_sys_sendmmsg +539 x32 process_vm_readv __x32_compat_sys_process_vm_readv +540 x32 process_vm_writev __x32_compat_sys_process_vm_writev +541 x32 setsockopt __x32_compat_sys_setsockopt +542 x32 getsockopt __x32_compat_sys_getsockopt +543 x32 io_setup __x32_compat_sys_io_setup +544 x32 io_submit __x32_compat_sys_io_submit +545 x32 execveat __x32_compat_sys_execveat/ptregs +546 x32 preadv2 __x32_compat_sys_preadv64v2 +547 x32 pwritev2 __x32_compat_sys_pwritev64v2 diff --git a/linux-user/x86_64/syscall_nr.h b/linux-user/x86_64/syscall_nr.h deleted file mode 100644 index 9b6981e74c..0000000000 --- a/linux-user/x86_64/syscall_nr.h +++ /dev/null @@ -1,332 +0,0 @@ -#ifndef LINUX_USER_X86_64_SYSCALL_NR_H -#define LINUX_USER_X86_64_SYSCALL_NR_H - -#define TARGET_NR_read 0 -#define TARGET_NR_write 1 -#define TARGET_NR_open 2 -#define TARGET_NR_close 3 -#define TARGET_NR_stat 4 -#define TARGET_NR_fstat 5 -#define TARGET_NR_lstat 6 -#define TARGET_NR_poll 7 -#define TARGET_NR_lseek 8 -#define TARGET_NR_mmap 9 -#define TARGET_NR_mprotect 10 -#define TARGET_NR_munmap 11 -#define TARGET_NR_brk 12 -#define TARGET_NR_rt_sigaction 13 -#define TARGET_NR_rt_sigprocmask 14 -#define TARGET_NR_rt_sigreturn 15 -#define TARGET_NR_ioctl 16 -#define TARGET_NR_pread64 17 -#define TARGET_NR_pwrite64 18 -#define TARGET_NR_readv 19 -#define TARGET_NR_writev 20 -#define TARGET_NR_access 21 -#define TARGET_NR_pipe 22 -#define TARGET_NR_select 23 -#define TARGET_NR_sched_yield 24 -#define TARGET_NR_mremap 25 -#define TARGET_NR_msync 26 -#define TARGET_NR_mincore 27 -#define TARGET_NR_madvise 28 -#define TARGET_NR_shmget 29 -#define TARGET_NR_shmat 30 -#define TARGET_NR_shmctl 31 -#define TARGET_NR_dup 32 -#define TARGET_NR_dup2 33 -#define TARGET_NR_pause 34 -#define TARGET_NR_nanosleep 35 -#define TARGET_NR_getitimer 36 -#define TARGET_NR_alarm 37 -#define TARGET_NR_setitimer 38 -#define TARGET_NR_getpid 39 -#define TARGET_NR_sendfile 40 -#define TARGET_NR_socket 41 -#define TARGET_NR_connect 42 -#define TARGET_NR_accept 43 -#define TARGET_NR_sendto 44 -#define TARGET_NR_recvfrom 45 -#define TARGET_NR_sendmsg 46 -#define TARGET_NR_recvmsg 47 -#define TARGET_NR_shutdown 48 -#define TARGET_NR_bind 49 -#define TARGET_NR_listen 50 -#define TARGET_NR_getsockname 51 -#define TARGET_NR_getpeername 52 -#define TARGET_NR_socketpair 53 -#define TARGET_NR_setsockopt 54 -#define TARGET_NR_getsockopt 55 -#define TARGET_NR_clone 56 -#define TARGET_NR_fork 57 -#define TARGET_NR_vfork 58 -#define TARGET_NR_execve 59 -#define TARGET_NR_exit 60 -#define TARGET_NR_wait4 61 -#define TARGET_NR_kill 62 -#define TARGET_NR_uname 63 -#define TARGET_NR_semget 64 -#define TARGET_NR_semop 65 -#define TARGET_NR_semctl 66 -#define TARGET_NR_shmdt 67 -#define TARGET_NR_msgget 68 -#define TARGET_NR_msgsnd 69 -#define TARGET_NR_msgrcv 70 -#define TARGET_NR_msgctl 71 -#define TARGET_NR_fcntl 72 -#define TARGET_NR_flock 73 -#define TARGET_NR_fsync 74 -#define TARGET_NR_fdatasync 75 -#define TARGET_NR_truncate 76 -#define TARGET_NR_ftruncate 77 -#define TARGET_NR_getdents 78 -#define TARGET_NR_getcwd 79 -#define TARGET_NR_chdir 80 -#define TARGET_NR_fchdir 81 -#define TARGET_NR_rename 82 -#define TARGET_NR_mkdir 83 -#define TARGET_NR_rmdir 84 -#define TARGET_NR_creat 85 -#define TARGET_NR_link 86 -#define TARGET_NR_unlink 87 -#define TARGET_NR_symlink 88 -#define TARGET_NR_readlink 89 -#define TARGET_NR_chmod 90 -#define TARGET_NR_fchmod 91 -#define TARGET_NR_chown 92 -#define TARGET_NR_fchown 93 -#define TARGET_NR_lchown 94 -#define TARGET_NR_umask 95 -#define TARGET_NR_gettimeofday 96 -#define TARGET_NR_getrlimit 97 -#define TARGET_NR_getrusage 98 -#define TARGET_NR_sysinfo 99 -#define TARGET_NR_times 100 -#define TARGET_NR_ptrace 101 -#define TARGET_NR_getuid 102 -#define TARGET_NR_syslog 103 -#define TARGET_NR_getgid 104 -#define TARGET_NR_setuid 105 -#define TARGET_NR_setgid 106 -#define TARGET_NR_geteuid 107 -#define TARGET_NR_getegid 108 -#define TARGET_NR_setpgid 109 -#define TARGET_NR_getppid 110 -#define TARGET_NR_getpgrp 111 -#define TARGET_NR_setsid 112 -#define TARGET_NR_setreuid 113 -#define TARGET_NR_setregid 114 -#define TARGET_NR_getgroups 115 -#define TARGET_NR_setgroups 116 -#define TARGET_NR_setresuid 117 -#define TARGET_NR_getresuid 118 -#define TARGET_NR_setresgid 119 -#define TARGET_NR_getresgid 120 -#define TARGET_NR_getpgid 121 -#define TARGET_NR_setfsuid 122 -#define TARGET_NR_setfsgid 123 -#define TARGET_NR_getsid 124 -#define TARGET_NR_capget 125 -#define TARGET_NR_capset 126 -#define TARGET_NR_rt_sigpending 127 -#define TARGET_NR_rt_sigtimedwait 128 -#define TARGET_NR_rt_sigqueueinfo 129 -#define TARGET_NR_rt_sigsuspend 130 -#define TARGET_NR_sigaltstack 131 -#define TARGET_NR_utime 132 -#define TARGET_NR_mknod 133 -#define TARGET_NR_uselib 134 -#define TARGET_NR_personality 135 -#define TARGET_NR_ustat 136 -#define TARGET_NR_statfs 137 -#define TARGET_NR_fstatfs 138 -#define TARGET_NR_sysfs 139 -#define TARGET_NR_getpriority 140 -#define TARGET_NR_setpriority 141 -#define TARGET_NR_sched_setparam 142 -#define TARGET_NR_sched_getparam 143 -#define TARGET_NR_sched_setscheduler 144 -#define TARGET_NR_sched_getscheduler 145 -#define TARGET_NR_sched_get_priority_max 146 -#define TARGET_NR_sched_get_priority_min 147 -#define TARGET_NR_sched_rr_get_interval 148 -#define TARGET_NR_mlock 149 -#define TARGET_NR_munlock 150 -#define TARGET_NR_mlockall 151 -#define TARGET_NR_munlockall 152 -#define TARGET_NR_vhangup 153 -#define TARGET_NR_modify_ldt 154 -#define TARGET_NR_pivot_root 155 -#define TARGET_NR__sysctl 156 -#define TARGET_NR_prctl 157 -#define TARGET_NR_arch_prctl 158 -#define TARGET_NR_adjtimex 159 -#define TARGET_NR_setrlimit 160 -#define TARGET_NR_chroot 161 -#define TARGET_NR_sync 162 -#define TARGET_NR_acct 163 -#define TARGET_NR_settimeofday 164 -#define TARGET_NR_mount 165 -#define TARGET_NR_umount2 166 -#define TARGET_NR_swapon 167 -#define TARGET_NR_swapoff 168 -#define TARGET_NR_reboot 169 -#define TARGET_NR_sethostname 170 -#define TARGET_NR_setdomainname 171 -#define TARGET_NR_iopl 172 -#define TARGET_NR_ioperm 173 -#define TARGET_NR_create_module 174 -#define TARGET_NR_init_module 175 -#define TARGET_NR_delete_module 176 -#define TARGET_NR_get_kernel_syms 177 -#define TARGET_NR_query_module 178 -#define TARGET_NR_quotactl 179 -#define TARGET_NR_nfsservctl 180 -#define TARGET_NR_getpmsg 181 /* reserved for LiS/STREAMS */ -#define TARGET_NR_putpmsg 182 /* reserved for LiS/STREAMS */ -#define TARGET_NR_afs_syscall 183 /* reserved for AFS */ -#define TARGET_NR_tuxcall 184 /* reserved for tux */ -#define TARGET_NR_security 185 -#define TARGET_NR_gettid 186 -#define TARGET_NR_readahead 187 -#define TARGET_NR_setxattr 188 -#define TARGET_NR_lsetxattr 189 -#define TARGET_NR_fsetxattr 190 -#define TARGET_NR_getxattr 191 -#define TARGET_NR_lgetxattr 192 -#define TARGET_NR_fgetxattr 193 -#define TARGET_NR_listxattr 194 -#define TARGET_NR_llistxattr 195 -#define TARGET_NR_flistxattr 196 -#define TARGET_NR_removexattr 197 -#define TARGET_NR_lremovexattr 198 -#define TARGET_NR_fremovexattr 199 -#define TARGET_NR_tkill 200 -#define TARGET_NR_time 201 -#define TARGET_NR_futex 202 -#define TARGET_NR_sched_setaffinity 203 -#define TARGET_NR_sched_getaffinity 204 -#define TARGET_NR_set_thread_area 205 -#define TARGET_NR_io_setup 206 -#define TARGET_NR_io_destroy 207 -#define TARGET_NR_io_getevents 208 -#define TARGET_NR_io_submit 209 -#define TARGET_NR_io_cancel 210 -#define TARGET_NR_get_thread_area 211 -#define TARGET_NR_lookup_dcookie 212 -#define TARGET_NR_epoll_create 213 -#define TARGET_NR_epoll_ctl_old 214 -#define TARGET_NR_epoll_wait_old 215 -#define TARGET_NR_remap_file_pages 216 -#define TARGET_NR_getdents64 217 -#define TARGET_NR_set_tid_address 218 -#define TARGET_NR_restart_syscall 219 -#define TARGET_NR_semtimedop 220 -#define TARGET_NR_fadvise64 221 -#define TARGET_NR_timer_create 222 -#define TARGET_NR_timer_settime 223 -#define TARGET_NR_timer_gettime 224 -#define TARGET_NR_timer_getoverrun 225 -#define TARGET_NR_timer_delete 226 -#define TARGET_NR_clock_settime 227 -#define TARGET_NR_clock_gettime 228 -#define TARGET_NR_clock_getres 229 -#define TARGET_NR_clock_nanosleep 230 -#define TARGET_NR_exit_group 231 -#define TARGET_NR_epoll_wait 232 -#define TARGET_NR_epoll_ctl 233 -#define TARGET_NR_tgkill 234 -#define TARGET_NR_utimes 235 -#define TARGET_NR_vserver 236 -#define TARGET_NR_mbind 237 -#define TARGET_NR_set_mempolicy 238 -#define TARGET_NR_get_mempolicy 239 -#define TARGET_NR_mq_open 240 -#define TARGET_NR_mq_unlink 241 -#define TARGET_NR_mq_timedsend 242 -#define TARGET_NR_mq_timedreceive 243 -#define TARGET_NR_mq_notify 244 -#define TARGET_NR_mq_getsetattr 245 -#define TARGET_NR_kexec_load 246 -#define TARGET_NR_waitid 247 -#define TARGET_NR_add_key 248 -#define TARGET_NR_request_key 249 -#define TARGET_NR_keyctl 250 -#define TARGET_NR_ioprio_set 251 -#define TARGET_NR_ioprio_get 252 -#define TARGET_NR_inotify_init 253 -#define TARGET_NR_inotify_add_watch 254 -#define TARGET_NR_inotify_rm_watch 255 -#define TARGET_NR_migrate_pages 256 -#define TARGET_NR_openat 257 -#define TARGET_NR_mkdirat 258 -#define TARGET_NR_mknodat 259 -#define TARGET_NR_fchownat 260 -#define TARGET_NR_futimesat 261 -#define TARGET_NR_newfstatat 262 -#define TARGET_NR_unlinkat 263 -#define TARGET_NR_renameat 264 -#define TARGET_NR_linkat 265 -#define TARGET_NR_symlinkat 266 -#define TARGET_NR_readlinkat 267 -#define TARGET_NR_fchmodat 268 -#define TARGET_NR_faccessat 269 -#define TARGET_NR_pselect6 270 -#define TARGET_NR_ppoll 271 -#define TARGET_NR_unshare 272 -#define TARGET_NR_set_robust_list 273 -#define TARGET_NR_get_robust_list 274 -#define TARGET_NR_splice 275 -#define TARGET_NR_tee 276 -#define TARGET_NR_sync_file_range 277 -#define TARGET_NR_vmsplice 278 -#define TARGET_NR_move_pages 279 -#define TARGET_NR_utimensat 280 -#define TARGET_NR_epoll_pwait 281 -#define TARGET_NR_signalfd 282 -#define TARGET_NR_timerfd_create 283 -#define TARGET_NR_eventfd 284 -#define TARGET_NR_fallocate 285 -#define TARGET_NR_timerfd_settime 286 -#define TARGET_NR_timerfd_gettime 287 -#define TARGET_NR_accept4 288 -#define TARGET_NR_signalfd4 289 -#define TARGET_NR_eventfd2 290 -#define TARGET_NR_epoll_create1 291 -#define TARGET_NR_dup3 292 -#define TARGET_NR_pipe2 293 -#define TARGET_NR_inotify_init1 294 -#define TARGET_NR_preadv 295 -#define TARGET_NR_pwritev 296 -#define TARGET_NR_rt_tgsigqueueinfo 297 -#define TARGET_NR_perf_event_open 298 -#define TARGET_NR_recvmmsg 299 -#define TARGET_NR_fanotify_init 300 -#define TARGET_NR_fanotify_mark 301 -#define TARGET_NR_prlimit64 302 -#define TARGET_NR_name_to_handle_at 303 -#define TARGET_NR_open_by_handle_at 304 -#define TARGET_NR_clock_adjtime 305 -#define TARGET_NR_syncfs 306 -#define TARGET_NR_sendmmsg 307 -#define TARGET_NR_setns 308 -#define TARGET_NR_getcpu 309 -#define TARGET_NR_process_vm_readv 310 -#define TARGET_NR_process_vm_writev 311 -#define TARGET_NR_kcmp 312 -#define TARGET_NR_finit_module 313 -#define TARGET_NR_sched_setattr 314 -#define TARGET_NR_sched_getattr 315 -#define TARGET_NR_renameat2 316 -#define TARGET_NR_seccomp 317 -#define TARGET_NR_getrandom 318 -#define TARGET_NR_memfd_create 319 -#define TARGET_NR_kexec_file_load 320 -#define TARGET_NR_bpf 321 -#define TARGET_NR_execveat 322 -#define TARGET_NR_userfaultfd 323 -#define TARGET_NR_membarrier 324 -#define TARGET_NR_mlock2 325 -#define TARGET_NR_copy_file_range 326 - -#endif diff --git a/linux-user/x86_64/syscallhdr.sh b/linux-user/x86_64/syscallhdr.sh new file mode 100644 index 0000000000..182be52a74 --- /dev/null +++ b/linux-user/x86_64/syscallhdr.sh @@ -0,0 +1,28 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 + +in="$1" +out="$2" +my_abis=`echo "($3)" | tr ',' '|'` +prefix="$4" +offset="$5" + +fileguard=LINUX_USER_X86_64_`basename "$out" | sed \ + -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/' \ + -e 's/[^A-Z0-9_]/_/g' -e 's/__/_/g'` +grep -E "^[0-9A-Fa-fXx]+[[:space:]]+${my_abis}" "$in" | sort -n | ( + echo "#ifndef ${fileguard}" + echo "#define ${fileguard} 1" + echo "" + + while read nr abi name entry ; do + if [ -z "$offset" ]; then + echo "#define TARGET_NR_${prefix}${name} $nr" + else + echo "#define TARGET_NR_${prefix}${name} ($offset + $nr)" + fi + done + + echo "" + echo "#endif /* ${fileguard} */" +) > "$out" diff --git a/linux-user/xtensa/Makefile.objs b/linux-user/xtensa/Makefile.objs new file mode 100644 index 0000000000..d4be1b7455 --- /dev/null +++ b/linux-user/xtensa/Makefile.objs @@ -0,0 +1,5 @@ +generated-files-y += linux-user/xtensa/syscall_nr.h + +syshdr := $(SRC_PATH)/linux-user/xtensa/syscallhdr.sh +%/syscall_nr.h: $(SRC_PATH)/linux-user/xtensa/syscall.tbl $(syshdr) + $(call quiet-command, sh $(syshdr) $< $@ $(TARGET_SYSTBL_ABI),"GEN","$@") diff --git a/linux-user/xtensa/syscall.tbl b/linux-user/xtensa/syscall.tbl new file mode 100644 index 0000000000..85a9ab1bc0 --- /dev/null +++ b/linux-user/xtensa/syscall.tbl @@ -0,0 +1,410 @@ +# SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note +# +# system call numbers and entry vectors for xtensa +# +# The format is: +# +# +# The is always "common" for this file +# +0 common spill sys_ni_syscall +1 common xtensa sys_ni_syscall +2 common available4 sys_ni_syscall +3 common available5 sys_ni_syscall +4 common available6 sys_ni_syscall +5 common available7 sys_ni_syscall +6 common available8 sys_ni_syscall +7 common available9 sys_ni_syscall +# File Operations +8 common open sys_open +9 common close sys_close +10 common dup sys_dup +11 common dup2 sys_dup2 +12 common read sys_read +13 common write sys_write +14 common select sys_select +15 common lseek sys_lseek +16 common poll sys_poll +17 common _llseek sys_llseek +18 common epoll_wait sys_epoll_wait +19 common epoll_ctl sys_epoll_ctl +20 common epoll_create sys_epoll_create +21 common creat sys_creat +22 common truncate sys_truncate +23 common ftruncate sys_ftruncate +24 common readv sys_readv +25 common writev sys_writev +26 common fsync sys_fsync +27 common fdatasync sys_fdatasync +28 common truncate64 sys_truncate64 +29 common ftruncate64 sys_ftruncate64 +30 common pread64 sys_pread64 +31 common pwrite64 sys_pwrite64 +32 common link sys_link +33 common rename sys_rename +34 common symlink sys_symlink +35 common readlink sys_readlink +36 common mknod sys_mknod +37 common pipe sys_pipe +38 common unlink sys_unlink +39 common rmdir sys_rmdir +40 common mkdir sys_mkdir +41 common chdir sys_chdir +42 common fchdir sys_fchdir +43 common getcwd sys_getcwd +44 common chmod sys_chmod +45 common chown sys_chown +46 common stat sys_newstat +47 common stat64 sys_stat64 +48 common lchown sys_lchown +49 common lstat sys_newlstat +50 common lstat64 sys_lstat64 +51 common available51 sys_ni_syscall +52 common fchmod sys_fchmod +53 common fchown sys_fchown +54 common fstat sys_newfstat +55 common fstat64 sys_fstat64 +56 common flock sys_flock +57 common access sys_access +58 common umask sys_umask +59 common getdents sys_getdents +60 common getdents64 sys_getdents64 +61 common fcntl64 sys_fcntl64 +62 common fallocate sys_fallocate +63 common fadvise64_64 xtensa_fadvise64_64 +64 common utime sys_utime32 +65 common utimes sys_utimes_time32 +66 common ioctl sys_ioctl +67 common fcntl sys_fcntl +68 common setxattr sys_setxattr +69 common getxattr sys_getxattr +70 common listxattr sys_listxattr +71 common removexattr sys_removexattr +72 common lsetxattr sys_lsetxattr +73 common lgetxattr sys_lgetxattr +74 common llistxattr sys_llistxattr +75 common lremovexattr sys_lremovexattr +76 common fsetxattr sys_fsetxattr +77 common fgetxattr sys_fgetxattr +78 common flistxattr sys_flistxattr +79 common fremovexattr sys_fremovexattr +# File Map / Shared Memory Operations +80 common mmap2 sys_mmap_pgoff +81 common munmap sys_munmap +82 common mprotect sys_mprotect +83 common brk sys_brk +84 common mlock sys_mlock +85 common munlock sys_munlock +86 common mlockall sys_mlockall +87 common munlockall sys_munlockall +88 common mremap sys_mremap +89 common msync sys_msync +90 common mincore sys_mincore +91 common madvise sys_madvise +92 common shmget sys_shmget +93 common shmat xtensa_shmat +94 common shmctl sys_old_shmctl +95 common shmdt sys_shmdt +# Socket Operations +96 common socket sys_socket +97 common setsockopt sys_setsockopt +98 common getsockopt sys_getsockopt +99 common shutdown sys_shutdown +100 common bind sys_bind +101 common connect sys_connect +102 common listen sys_listen +103 common accept sys_accept +104 common getsockname sys_getsockname +105 common getpeername sys_getpeername +106 common sendmsg sys_sendmsg +107 common recvmsg sys_recvmsg +108 common send sys_send +109 common recv sys_recv +110 common sendto sys_sendto +111 common recvfrom sys_recvfrom +112 common socketpair sys_socketpair +113 common sendfile sys_sendfile +114 common sendfile64 sys_sendfile64 +115 common sendmmsg sys_sendmmsg +# Process Operations +116 common clone sys_clone +117 common execve sys_execve +118 common exit sys_exit +119 common exit_group sys_exit_group +120 common getpid sys_getpid +121 common wait4 sys_wait4 +122 common waitid sys_waitid +123 common kill sys_kill +124 common tkill sys_tkill +125 common tgkill sys_tgkill +126 common set_tid_address sys_set_tid_address +127 common gettid sys_gettid +128 common setsid sys_setsid +129 common getsid sys_getsid +130 common prctl sys_prctl +131 common personality sys_personality +132 common getpriority sys_getpriority +133 common setpriority sys_setpriority +134 common setitimer sys_setitimer +135 common getitimer sys_getitimer +136 common setuid sys_setuid +137 common getuid sys_getuid +138 common setgid sys_setgid +139 common getgid sys_getgid +140 common geteuid sys_geteuid +141 common getegid sys_getegid +142 common setreuid sys_setreuid +143 common setregid sys_setregid +144 common setresuid sys_setresuid +145 common getresuid sys_getresuid +146 common setresgid sys_setresgid +147 common getresgid sys_getresgid +148 common setpgid sys_setpgid +149 common getpgid sys_getpgid +150 common getppid sys_getppid +151 common getpgrp sys_getpgrp +# 152 was set_thread_area +152 common reserved152 sys_ni_syscall +# 153 was get_thread_area +153 common reserved153 sys_ni_syscall +154 common times sys_times +155 common acct sys_acct +156 common sched_setaffinity sys_sched_setaffinity +157 common sched_getaffinity sys_sched_getaffinity +158 common capget sys_capget +159 common capset sys_capset +160 common ptrace sys_ptrace +161 common semtimedop sys_semtimedop_time32 +162 common semget sys_semget +163 common semop sys_semop +164 common semctl sys_old_semctl +165 common available165 sys_ni_syscall +166 common msgget sys_msgget +167 common msgsnd sys_msgsnd +168 common msgrcv sys_msgrcv +169 common msgctl sys_old_msgctl +170 common available170 sys_ni_syscall +# File System +171 common umount2 sys_umount +172 common mount sys_mount +173 common swapon sys_swapon +174 common chroot sys_chroot +175 common pivot_root sys_pivot_root +176 common umount sys_oldumount +177 common swapoff sys_swapoff +178 common sync sys_sync +179 common syncfs sys_syncfs +180 common setfsuid sys_setfsuid +181 common setfsgid sys_setfsgid +182 common sysfs sys_sysfs +183 common ustat sys_ustat +184 common statfs sys_statfs +185 common fstatfs sys_fstatfs +186 common statfs64 sys_statfs64 +187 common fstatfs64 sys_fstatfs64 +# System +188 common setrlimit sys_setrlimit +189 common getrlimit sys_getrlimit +190 common getrusage sys_getrusage +191 common futex sys_futex_time32 +192 common gettimeofday sys_gettimeofday +193 common settimeofday sys_settimeofday +194 common adjtimex sys_adjtimex_time32 +195 common nanosleep sys_nanosleep_time32 +196 common getgroups sys_getgroups +197 common setgroups sys_setgroups +198 common sethostname sys_sethostname +199 common setdomainname sys_setdomainname +200 common syslog sys_syslog +201 common vhangup sys_vhangup +202 common uselib sys_uselib +203 common reboot sys_reboot +204 common quotactl sys_quotactl +# 205 was old nfsservctl +205 common nfsservctl sys_ni_syscall +206 common _sysctl sys_sysctl +207 common bdflush sys_bdflush +208 common uname sys_newuname +209 common sysinfo sys_sysinfo +210 common init_module sys_init_module +211 common delete_module sys_delete_module +212 common sched_setparam sys_sched_setparam +213 common sched_getparam sys_sched_getparam +214 common sched_setscheduler sys_sched_setscheduler +215 common sched_getscheduler sys_sched_getscheduler +216 common sched_get_priority_max sys_sched_get_priority_max +217 common sched_get_priority_min sys_sched_get_priority_min +218 common sched_rr_get_interval sys_sched_rr_get_interval_time32 +219 common sched_yield sys_sched_yield +222 common available222 sys_ni_syscall +# Signal Handling +223 common restart_syscall sys_restart_syscall +224 common sigaltstack sys_sigaltstack +225 common rt_sigreturn xtensa_rt_sigreturn +226 common rt_sigaction sys_rt_sigaction +227 common rt_sigprocmask sys_rt_sigprocmask +228 common rt_sigpending sys_rt_sigpending +229 common rt_sigtimedwait sys_rt_sigtimedwait_time32 +230 common rt_sigqueueinfo sys_rt_sigqueueinfo +231 common rt_sigsuspend sys_rt_sigsuspend +# Message +232 common mq_open sys_mq_open +233 common mq_unlink sys_mq_unlink +234 common mq_timedsend sys_mq_timedsend_time32 +235 common mq_timedreceive sys_mq_timedreceive_time32 +236 common mq_notify sys_mq_notify +237 common mq_getsetattr sys_mq_getsetattr +238 common available238 sys_ni_syscall +239 common io_setup sys_io_setup +# IO +240 common io_destroy sys_io_destroy +241 common io_submit sys_io_submit +242 common io_getevents sys_io_getevents_time32 +243 common io_cancel sys_io_cancel +244 common clock_settime sys_clock_settime32 +245 common clock_gettime sys_clock_gettime32 +246 common clock_getres sys_clock_getres_time32 +247 common clock_nanosleep sys_clock_nanosleep_time32 +# Timer +248 common timer_create sys_timer_create +249 common timer_delete sys_timer_delete +250 common timer_settime sys_timer_settime32 +251 common timer_gettime sys_timer_gettime32 +252 common timer_getoverrun sys_timer_getoverrun +# System +253 common reserved253 sys_ni_syscall +254 common lookup_dcookie sys_lookup_dcookie +255 common available255 sys_ni_syscall +256 common add_key sys_add_key +257 common request_key sys_request_key +258 common keyctl sys_keyctl +259 common available259 sys_ni_syscall +260 common readahead sys_readahead +261 common remap_file_pages sys_remap_file_pages +262 common migrate_pages sys_migrate_pages +263 common mbind sys_mbind +264 common get_mempolicy sys_get_mempolicy +265 common set_mempolicy sys_set_mempolicy +266 common unshare sys_unshare +267 common move_pages sys_move_pages +268 common splice sys_splice +269 common tee sys_tee +270 common vmsplice sys_vmsplice +271 common available271 sys_ni_syscall +272 common pselect6 sys_pselect6_time32 +273 common ppoll sys_ppoll_time32 +274 common epoll_pwait sys_epoll_pwait +275 common epoll_create1 sys_epoll_create1 +276 common inotify_init sys_inotify_init +277 common inotify_add_watch sys_inotify_add_watch +278 common inotify_rm_watch sys_inotify_rm_watch +279 common inotify_init1 sys_inotify_init1 +280 common getcpu sys_getcpu +281 common kexec_load sys_ni_syscall +282 common ioprio_set sys_ioprio_set +283 common ioprio_get sys_ioprio_get +284 common set_robust_list sys_set_robust_list +285 common get_robust_list sys_get_robust_list +286 common available286 sys_ni_syscall +287 common available287 sys_ni_syscall +# Relative File Operations +288 common openat sys_openat +289 common mkdirat sys_mkdirat +290 common mknodat sys_mknodat +291 common unlinkat sys_unlinkat +292 common renameat sys_renameat +293 common linkat sys_linkat +294 common symlinkat sys_symlinkat +295 common readlinkat sys_readlinkat +296 common utimensat sys_utimensat_time32 +297 common fchownat sys_fchownat +298 common futimesat sys_futimesat_time32 +299 common fstatat64 sys_fstatat64 +300 common fchmodat sys_fchmodat +301 common faccessat sys_faccessat +302 common available302 sys_ni_syscall +303 common available303 sys_ni_syscall +304 common signalfd sys_signalfd +# 305 was timerfd +306 common eventfd sys_eventfd +307 common recvmmsg sys_recvmmsg_time32 +308 common setns sys_setns +309 common signalfd4 sys_signalfd4 +310 common dup3 sys_dup3 +311 common pipe2 sys_pipe2 +312 common timerfd_create sys_timerfd_create +313 common timerfd_settime sys_timerfd_settime32 +314 common timerfd_gettime sys_timerfd_gettime32 +315 common available315 sys_ni_syscall +316 common eventfd2 sys_eventfd2 +317 common preadv sys_preadv +318 common pwritev sys_pwritev +319 common available319 sys_ni_syscall +320 common fanotify_init sys_fanotify_init +321 common fanotify_mark sys_fanotify_mark +322 common process_vm_readv sys_process_vm_readv +323 common process_vm_writev sys_process_vm_writev +324 common name_to_handle_at sys_name_to_handle_at +325 common open_by_handle_at sys_open_by_handle_at +326 common sync_file_range2 sys_sync_file_range2 +327 common perf_event_open sys_perf_event_open +328 common rt_tgsigqueueinfo sys_rt_tgsigqueueinfo +329 common clock_adjtime sys_clock_adjtime32 +330 common prlimit64 sys_prlimit64 +331 common kcmp sys_kcmp +332 common finit_module sys_finit_module +333 common accept4 sys_accept4 +334 common sched_setattr sys_sched_setattr +335 common sched_getattr sys_sched_getattr +336 common renameat2 sys_renameat2 +337 common seccomp sys_seccomp +338 common getrandom sys_getrandom +339 common memfd_create sys_memfd_create +340 common bpf sys_bpf +341 common execveat sys_execveat +342 common userfaultfd sys_userfaultfd +343 common membarrier sys_membarrier +344 common mlock2 sys_mlock2 +345 common copy_file_range sys_copy_file_range +346 common preadv2 sys_preadv2 +347 common pwritev2 sys_pwritev2 +348 common pkey_mprotect sys_pkey_mprotect +349 common pkey_alloc sys_pkey_alloc +350 common pkey_free sys_pkey_free +351 common statx sys_statx +352 common rseq sys_rseq +# 353 through 402 are unassigned to sync up with generic numbers +403 common clock_gettime64 sys_clock_gettime +404 common clock_settime64 sys_clock_settime +405 common clock_adjtime64 sys_clock_adjtime +406 common clock_getres_time64 sys_clock_getres +407 common clock_nanosleep_time64 sys_clock_nanosleep +408 common timer_gettime64 sys_timer_gettime +409 common timer_settime64 sys_timer_settime +410 common timerfd_gettime64 sys_timerfd_gettime +411 common timerfd_settime64 sys_timerfd_settime +412 common utimensat_time64 sys_utimensat +413 common pselect6_time64 sys_pselect6 +414 common ppoll_time64 sys_ppoll +416 common io_pgetevents_time64 sys_io_pgetevents +417 common recvmmsg_time64 sys_recvmmsg +418 common mq_timedsend_time64 sys_mq_timedsend +419 common mq_timedreceive_time64 sys_mq_timedreceive +420 common semtimedop_time64 sys_semtimedop +421 common rt_sigtimedwait_time64 sys_rt_sigtimedwait +422 common futex_time64 sys_futex +423 common sched_rr_get_interval_time64 sys_sched_rr_get_interval +424 common pidfd_send_signal sys_pidfd_send_signal +425 common io_uring_setup sys_io_uring_setup +426 common io_uring_enter sys_io_uring_enter +427 common io_uring_register sys_io_uring_register +428 common open_tree sys_open_tree +429 common move_mount sys_move_mount +430 common fsopen sys_fsopen +431 common fsconfig sys_fsconfig +432 common fsmount sys_fsmount +433 common fspick sys_fspick +434 common pidfd_open sys_pidfd_open +435 common clone3 sys_clone3 +437 common openat2 sys_openat2 +438 common pidfd_getfd sys_pidfd_getfd diff --git a/linux-user/xtensa/syscall_nr.h b/linux-user/xtensa/syscall_nr.h deleted file mode 100644 index 27645bea47..0000000000 --- a/linux-user/xtensa/syscall_nr.h +++ /dev/null @@ -1,437 +0,0 @@ -/* - * include/asm-xtensa/unistd.h - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - * - * Copyright (C) 2001 - 2009 Tensilica Inc. - */ - -#ifndef XTENSA_SYSCALL_NR_H -#define XTENSA_SYSCALL_NR_H - -#define TARGET_NR_spill 0 -#define TARGET_NR_xtensa 1 -#define TARGET_NR_available4 2 -#define TARGET_NR_available5 3 -#define TARGET_NR_available6 4 -#define TARGET_NR_available7 5 -#define TARGET_NR_available8 6 -#define TARGET_NR_available9 7 - -/* File Operations */ - -#define TARGET_NR_open 8 -#define TARGET_NR_close 9 -#define TARGET_NR_dup 10 -#define TARGET_NR_dup2 11 -#define TARGET_NR_read 12 -#define TARGET_NR_write 13 -#define TARGET_NR_select 14 -#define TARGET_NR_lseek 15 -#define TARGET_NR_poll 16 -#define TARGET_NR__llseek 17 -#define TARGET_NR_epoll_wait 18 -#define TARGET_NR_epoll_ctl 19 -#define TARGET_NR_epoll_create 20 -#define TARGET_NR_creat 21 -#define TARGET_NR_truncate 22 -#define TARGET_NR_ftruncate 23 -#define TARGET_NR_readv 24 -#define TARGET_NR_writev 25 -#define TARGET_NR_fsync 26 -#define TARGET_NR_fdatasync 27 -#define TARGET_NR_truncate64 28 -#define TARGET_NR_ftruncate64 29 -#define TARGET_NR_pread64 30 -#define TARGET_NR_pwrite64 31 - -#define TARGET_NR_link 32 -#define TARGET_NR_rename 33 -#define TARGET_NR_symlink 34 -#define TARGET_NR_readlink 35 -#define TARGET_NR_mknod 36 -#define TARGET_NR_pipe 37 -#define TARGET_NR_unlink 38 -#define TARGET_NR_rmdir 39 - -#define TARGET_NR_mkdir 40 -#define TARGET_NR_chdir 41 -#define TARGET_NR_fchdir 42 -#define TARGET_NR_getcwd 43 - -#define TARGET_NR_chmod 44 -#define TARGET_NR_chown 45 -#define TARGET_NR_stat 46 -#define TARGET_NR_stat64 47 - -#define TARGET_NR_lchown 48 -#define TARGET_NR_lstat 49 -#define TARGET_NR_lstat64 50 -#define TARGET_NR_available51 51 - -#define TARGET_NR_fchmod 52 -#define TARGET_NR_fchown 53 -#define TARGET_NR_fstat 54 -#define TARGET_NR_fstat64 55 - -#define TARGET_NR_flock 56 -#define TARGET_NR_access 57 -#define TARGET_NR_umask 58 -#define TARGET_NR_getdents 59 -#define TARGET_NR_getdents64 60 -#define TARGET_NR_fcntl64 61 -#define TARGET_NR_fallocate 62 -#define TARGET_NR_fadvise64_64 63 -#define TARGET_NR_utime 64 /* glibc 2.3.3 ?? */ -#define TARGET_NR_utimes 65 -#define TARGET_NR_ioctl 66 -#define TARGET_NR_fcntl 67 - -#define TARGET_NR_setxattr 68 -#define TARGET_NR_getxattr 69 -#define TARGET_NR_listxattr 70 -#define TARGET_NR_removexattr 71 -#define TARGET_NR_lsetxattr 72 -#define TARGET_NR_lgetxattr 73 -#define TARGET_NR_llistxattr 74 -#define TARGET_NR_lremovexattr 75 -#define TARGET_NR_fsetxattr 76 -#define TARGET_NR_fgetxattr 77 -#define TARGET_NR_flistxattr 78 -#define TARGET_NR_fremovexattr 79 - -/* File Map / Shared Memory Operations */ - -#define TARGET_NR_mmap2 80 -#define TARGET_NR_munmap 81 -#define TARGET_NR_mprotect 82 -#define TARGET_NR_brk 83 -#define TARGET_NR_mlock 84 -#define TARGET_NR_munlock 85 -#define TARGET_NR_mlockall 86 -#define TARGET_NR_munlockall 87 -#define TARGET_NR_mremap 88 -#define TARGET_NR_msync 89 -#define TARGET_NR_mincore 90 -#define TARGET_NR_madvise 91 -#define TARGET_NR_shmget 92 -#define TARGET_NR_shmat 93 -#define TARGET_NR_shmctl 94 -#define TARGET_NR_shmdt 95 - -/* Socket Operations */ - -#define TARGET_NR_socket 96 -#define TARGET_NR_setsockopt 97 -#define TARGET_NR_getsockopt 98 -#define TARGET_NR_shutdown 99 - -#define TARGET_NR_bind 100 -#define TARGET_NR_connect 101 -#define TARGET_NR_listen 102 -#define TARGET_NR_accept 103 - -#define TARGET_NR_getsockname 104 -#define TARGET_NR_getpeername 105 -#define TARGET_NR_sendmsg 106 -#define TARGET_NR_recvmsg 107 -#define TARGET_NR_send 108 -#define TARGET_NR_recv 109 -#define TARGET_NR_sendto 110 -#define TARGET_NR_recvfrom 111 - -#define TARGET_NR_socketpair 112 -#define TARGET_NR_sendfile 113 -#define TARGET_NR_sendfile64 114 -#define TARGET_NR_sendmmsg 115 - -/* Process Operations */ - -#define TARGET_NR_clone 116 -#define TARGET_NR_execve 117 -#define TARGET_NR_exit 118 -#define TARGET_NR_exit_group 119 -#define TARGET_NR_getpid 120 -#define TARGET_NR_wait4 121 -#define TARGET_NR_waitid 122 -#define TARGET_NR_kill 123 -#define TARGET_NR_tkill 124 -#define TARGET_NR_tgkill 125 -#define TARGET_NR_set_tid_address 126 -#define TARGET_NR_gettid 127 -#define TARGET_NR_setsid 128 -#define TARGET_NR_getsid 129 -#define TARGET_NR_prctl 130 -#define TARGET_NR_personality 131 -#define TARGET_NR_getpriority 132 -#define TARGET_NR_setpriority 133 -#define TARGET_NR_setitimer 134 -#define TARGET_NR_getitimer 135 -#define TARGET_NR_setuid 136 -#define TARGET_NR_getuid 137 -#define TARGET_NR_setgid 138 -#define TARGET_NR_getgid 139 -#define TARGET_NR_geteuid 140 -#define TARGET_NR_getegid 141 -#define TARGET_NR_setreuid 142 -#define TARGET_NR_setregid 143 -#define TARGET_NR_setresuid 144 -#define TARGET_NR_getresuid 145 -#define TARGET_NR_setresgid 146 -#define TARGET_NR_getresgid 147 -#define TARGET_NR_setpgid 148 -#define TARGET_NR_getpgid 149 -#define TARGET_NR_getppid 150 -#define TARGET_NR_getpgrp 151 - -#define TARGET_NR_reserved152 152 /* set_thread_area */ -#define TARGET_NR_reserved153 153 /* get_thread_area */ -#define TARGET_NR_times 154 -#define TARGET_NR_acct 155 -#define TARGET_NR_sched_setaffinity 156 -#define TARGET_NR_sched_getaffinity 157 -#define TARGET_NR_capget 158 -#define TARGET_NR_capset 159 -#define TARGET_NR_ptrace 160 -#define TARGET_NR_semtimedop 161 -#define TARGET_NR_semget 162 -#define TARGET_NR_semop 163 -#define TARGET_NR_semctl 164 -#define TARGET_NR_available165 165 -#define TARGET_NR_msgget 166 -#define TARGET_NR_msgsnd 167 -#define TARGET_NR_msgrcv 168 -#define TARGET_NR_msgctl 169 -#define TARGET_NR_available170 170 - -/* File System */ - -#define TARGET_NR_umount2 171 -#define TARGET_NR_mount 172 -#define TARGET_NR_swapon 173 -#define TARGET_NR_chroot 174 -#define TARGET_NR_pivot_root 175 -#define TARGET_NR_umount 176 -#define TARGET_NR_swapoff 177 -#define TARGET_NR_sync 178 -#define TARGET_NR_syncfs 179 -#define TARGET_NR_setfsuid 180 -#define TARGET_NR_setfsgid 181 -#define TARGET_NR_sysfs 182 -#define TARGET_NR_ustat 183 -#define TARGET_NR_statfs 184 -#define TARGET_NR_fstatfs 185 -#define TARGET_NR_statfs64 186 -#define TARGET_NR_fstatfs64 187 - -/* System */ - -#define TARGET_NR_setrlimit 188 -#define TARGET_NR_getrlimit 189 -#define TARGET_NR_getrusage 190 -#define TARGET_NR_futex 191 -#define TARGET_NR_gettimeofday 192 -#define TARGET_NR_settimeofday 193 -#define TARGET_NR_adjtimex 194 -#define TARGET_NR_nanosleep 195 -#define TARGET_NR_getgroups 196 -#define TARGET_NR_setgroups 197 -#define TARGET_NR_sethostname 198 -#define TARGET_NR_setdomainname 199 -#define TARGET_NR_syslog 200 -#define TARGET_NR_vhangup 201 -#define TARGET_NR_uselib 202 -#define TARGET_NR_reboot 203 -#define TARGET_NR_quotactl 204 -#define TARGET_NR_nfsservctl 205 -#define TARGET_NR__sysctl 206 -#define TARGET_NR_bdflush 207 -#define TARGET_NR_uname 208 -#define TARGET_NR_sysinfo 209 -#define TARGET_NR_init_module 210 -#define TARGET_NR_delete_module 211 - -#define TARGET_NR_sched_setparam 212 -#define TARGET_NR_sched_getparam 213 -#define TARGET_NR_sched_setscheduler 214 -#define TARGET_NR_sched_getscheduler 215 -#define TARGET_NR_sched_get_priority_max 216 -#define TARGET_NR_sched_get_priority_min 217 -#define TARGET_NR_sched_rr_get_interval 218 -#define TARGET_NR_sched_yield 219 -#define TARGET_NR_available222 222 - -/* Signal Handling */ - -#define TARGET_NR_restart_syscall 223 -#define TARGET_NR_sigaltstack 224 -#define TARGET_NR_rt_sigreturn 225 -#define TARGET_NR_rt_sigaction 226 -#define TARGET_NR_rt_sigprocmask 227 -#define TARGET_NR_rt_sigpending 228 -#define TARGET_NR_rt_sigtimedwait 229 -#define TARGET_NR_rt_sigqueueinfo 230 -#define TARGET_NR_rt_sigsuspend 231 - -/* Message */ - -#define TARGET_NR_mq_open 232 -#define TARGET_NR_mq_unlink 233 -#define TARGET_NR_mq_timedsend 234 -#define TARGET_NR_mq_timedreceive 235 -#define TARGET_NR_mq_notify 236 -#define TARGET_NR_mq_getsetattr 237 -#define TARGET_NR_available238 238 - -/* IO */ - -#define TARGET_NR_io_setup 239 -#define TARGET_NR_io_destroy 240 -#define TARGET_NR_io_submit 241 -#define TARGET_NR_io_getevents 242 -#define TARGET_NR_io_cancel 243 -#define TARGET_NR_clock_settime 244 -#define TARGET_NR_clock_gettime 245 -#define TARGET_NR_clock_getres 246 -#define TARGET_NR_clock_nanosleep 247 - -/* Timer */ - -#define TARGET_NR_timer_create 248 -#define TARGET_NR_timer_delete 249 -#define TARGET_NR_timer_settime 250 -#define TARGET_NR_timer_gettime 251 -#define TARGET_NR_timer_getoverrun 252 - -/* System */ - -#define TARGET_NR_reserved253 253 -#define TARGET_NR_lookup_dcookie 254 -#define TARGET_NR_available255 255 -#define TARGET_NR_add_key 256 -#define TARGET_NR_request_key 257 -#define TARGET_NR_keyctl 258 -#define TARGET_NR_available259 259 - - -#define TARGET_NR_readahead 260 -#define TARGET_NR_remap_file_pages 261 -#define TARGET_NR_migrate_pages 262 -#define TARGET_NR_mbind 263 -#define TARGET_NR_get_mempolicy 264 -#define TARGET_NR_set_mempolicy 265 -#define TARGET_NR_unshare 266 -#define TARGET_NR_move_pages 267 -#define TARGET_NR_splice 268 -#define TARGET_NR_tee 269 -#define TARGET_NR_vmsplice 270 -#define TARGET_NR_available271 271 - -#define TARGET_NR_pselect6 272 -#define TARGET_NR_ppoll 273 -#define TARGET_NR_epoll_pwait 274 -#define TARGET_NR_epoll_create1 275 - -#define TARGET_NR_inotify_init 276 -#define TARGET_NR_inotify_add_watch 277 -#define TARGET_NR_inotify_rm_watch 278 -#define TARGET_NR_inotify_init1 279 - -#define TARGET_NR_getcpu 280 -#define TARGET_NR_kexec_load 281 - -#define TARGET_NR_ioprio_set 282 -#define TARGET_NR_ioprio_get 283 - -#define TARGET_NR_set_robust_list 284 -#define TARGET_NR_get_robust_list 285 -#define TARGET_NR_available286 286 -#define TARGET_NR_available287 287 - -/* Relative File Operations */ - -#define TARGET_NR_openat 288 -#define TARGET_NR_mkdirat 289 -#define TARGET_NR_mknodat 290 -#define TARGET_NR_unlinkat 291 -#define TARGET_NR_renameat 292 -#define TARGET_NR_linkat 293 -#define TARGET_NR_symlinkat 294 -#define TARGET_NR_readlinkat 295 -#define TARGET_NR_utimensat 296 -#define TARGET_NR_fchownat 297 -#define TARGET_NR_futimesat 298 -#define TARGET_NR_fstatat64 299 -#define TARGET_NR_fchmodat 300 -#define TARGET_NR_faccessat 301 -#define TARGET_NR_available302 302 -#define TARGET_NR_available303 303 - -#define TARGET_NR_signalfd 304 -/* 305 was TARGET_NR_timerfd */ -#define TARGET_NR_eventfd 306 -#define TARGET_NR_recvmmsg 307 - -#define TARGET_NR_setns 308 -#define TARGET_NR_signalfd4 309 -#define TARGET_NR_dup3 310 -#define TARGET_NR_pipe2 311 - -#define TARGET_NR_timerfd_create 312 -#define TARGET_NR_timerfd_settime 313 -#define TARGET_NR_timerfd_gettime 314 -#define TARGET_NR_available315 315 - -#define TARGET_NR_eventfd2 316 -#define TARGET_NR_preadv 317 -#define TARGET_NR_pwritev 318 -#define TARGET_NR_available319 319 - -#define TARGET_NR_fanotify_init 320 -#define TARGET_NR_fanotify_mark 321 -#define TARGET_NR_process_vm_readv 322 -#define TARGET_NR_process_vm_writev 323 - -#define TARGET_NR_name_to_handle_at 324 -#define TARGET_NR_open_by_handle_at 325 -#define TARGET_NR_sync_file_range2 326 -#define TARGET_NR_perf_event_open 327 - -#define TARGET_NR_rt_tgsigqueueinfo 328 -#define TARGET_NR_clock_adjtime 329 -#define TARGET_NR_prlimit64 330 -#define TARGET_NR_kcmp 331 - -#define TARGET_NR_finit_module 332 - -#define TARGET_NR_accept4 333 - -#define TARGET_NR_sched_setattr 334 -#define TARGET_NR_sched_getattr 335 - -#define TARGET_NR_renameat2 336 - -#define TARGET_NR_seccomp 337 -#define TARGET_NR_getrandom 338 -#define TARGET_NR_memfd_create 339 -#define TARGET_NR_bpf 340 -#define TARGET_NR_execveat 341 - -#define TARGET_NR_userfaultfd 342 -#define TARGET_NR_membarrier 343 -#define TARGET_NR_mlock2 344 -#define TARGET_NR_copy_file_range 345 -#define TARGET_NR_preadv2 346 -#define TARGET_NR_pwritev2 347 - -#define TARGET_NR_pkey_mprotect 348 -#define TARGET_NR_pkey_alloc 349 -#define TARGET_NR_pkey_free 350 - -#define TARGET_NR_statx 351 - -#define TARGET_NR_syscall_count 352 - -#endif /* XTENSA_SYSCALL_NR_H */ diff --git a/linux-user/xtensa/syscallhdr.sh b/linux-user/xtensa/syscallhdr.sh new file mode 100644 index 0000000000..eef0644c94 --- /dev/null +++ b/linux-user/xtensa/syscallhdr.sh @@ -0,0 +1,32 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 + +in="$1" +out="$2" +my_abis=`echo "($3)" | tr ',' '|'` +prefix="$4" +offset="$5" + +fileguard=LINUX_USER_XTENSA_`basename "$out" | sed \ + -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/' \ + -e 's/[^A-Z0-9_]/_/g' -e 's/__/_/g'` +grep -E "^[0-9A-Fa-fXx]+[[:space:]]+${my_abis}" "$in" | sort -n | ( + printf "#ifndef %s\n" "${fileguard}" + printf "#define %s\n" "${fileguard}" + printf "\n" + + nxt=0 + while read nr abi name entry ; do + if [ -z "$offset" ]; then + printf "#define TARGET_NR_%s%s\t%s\n" \ + "${prefix}" "${name}" "${nr}" + else + printf "#define TARGET_NR_%s%s\t(%s + %s)\n" \ + "${prefix}" "${name}" "${offset}" "${nr}" + fi + nxt=$((nr+1)) + done + + printf "\n" + printf "#endif /* %s */" "${fileguard}" +) > "$out" diff --git a/memory.c b/memory.c index 148a7da565..2a2abb343a 100644 --- a/memory.c +++ b/memory.c @@ -434,7 +434,7 @@ static MemTxResult memory_region_read_accessor(MemoryRegion *mr, tmp = mr->ops->read(mr->opaque, addr, size); if (mr->subpage) { trace_memory_region_subpage_read(get_cpu_index(), mr, addr, tmp, size); - } else if (TRACE_MEMORY_REGION_OPS_READ_ENABLED) { + } else if (trace_event_get_state_backends(TRACE_MEMORY_REGION_OPS_READ)) { hwaddr abs_addr = memory_region_to_absolute_addr(mr, addr); trace_memory_region_ops_read(get_cpu_index(), mr, abs_addr, tmp, size); } @@ -456,7 +456,7 @@ static MemTxResult memory_region_read_with_attrs_accessor(MemoryRegion *mr, r = mr->ops->read_with_attrs(mr->opaque, addr, &tmp, size, attrs); if (mr->subpage) { trace_memory_region_subpage_read(get_cpu_index(), mr, addr, tmp, size); - } else if (TRACE_MEMORY_REGION_OPS_READ_ENABLED) { + } else if (trace_event_get_state_backends(TRACE_MEMORY_REGION_OPS_READ)) { hwaddr abs_addr = memory_region_to_absolute_addr(mr, addr); trace_memory_region_ops_read(get_cpu_index(), mr, abs_addr, tmp, size); } @@ -476,7 +476,7 @@ static MemTxResult memory_region_write_accessor(MemoryRegion *mr, if (mr->subpage) { trace_memory_region_subpage_write(get_cpu_index(), mr, addr, tmp, size); - } else if (TRACE_MEMORY_REGION_OPS_WRITE_ENABLED) { + } else if (trace_event_get_state_backends(TRACE_MEMORY_REGION_OPS_WRITE)) { hwaddr abs_addr = memory_region_to_absolute_addr(mr, addr); trace_memory_region_ops_write(get_cpu_index(), mr, abs_addr, tmp, size); } @@ -496,7 +496,7 @@ static MemTxResult memory_region_write_with_attrs_accessor(MemoryRegion *mr, if (mr->subpage) { trace_memory_region_subpage_write(get_cpu_index(), mr, addr, tmp, size); - } else if (TRACE_MEMORY_REGION_OPS_WRITE_ENABLED) { + } else if (trace_event_get_state_backends(TRACE_MEMORY_REGION_OPS_WRITE)) { hwaddr abs_addr = memory_region_to_absolute_addr(mr, addr); trace_memory_region_ops_write(get_cpu_index(), mr, abs_addr, tmp, size); } @@ -794,10 +794,19 @@ static void address_space_update_ioeventfds(AddressSpace *as) FlatView *view; FlatRange *fr; unsigned ioeventfd_nb = 0; - MemoryRegionIoeventfd *ioeventfds = NULL; + unsigned ioeventfd_max; + MemoryRegionIoeventfd *ioeventfds; AddrRange tmp; unsigned i; + /* + * It is likely that the number of ioeventfds hasn't changed much, so use + * the previous size as the starting value, with some headroom to avoid + * gratuitous reallocations. + */ + ioeventfd_max = QEMU_ALIGN_UP(as->ioeventfd_nb, 4); + ioeventfds = g_new(MemoryRegionIoeventfd, ioeventfd_max); + view = address_space_get_flatview(as); FOR_EACH_FLAT_RANGE(fr, view) { for (i = 0; i < fr->mr->ioeventfd_nb; ++i) { @@ -806,8 +815,11 @@ static void address_space_update_ioeventfds(AddressSpace *as) int128_make64(fr->offset_in_region))); if (addrrange_intersects(fr->addr, tmp)) { ++ioeventfd_nb; - ioeventfds = g_realloc(ioeventfds, - ioeventfd_nb * sizeof(*ioeventfds)); + if (ioeventfd_nb > ioeventfd_max) { + ioeventfd_max = MAX(ioeventfd_max * 2, 4); + ioeventfds = g_realloc(ioeventfds, + ioeventfd_max * sizeof(*ioeventfds)); + } ioeventfds[ioeventfd_nb-1] = fr->mr->ioeventfds[i]; ioeventfds[ioeventfd_nb-1].addr = tmp; } @@ -1158,15 +1170,6 @@ void memory_region_init(MemoryRegion *mr, memory_region_do_init(mr, owner, name, size); } -static void memory_region_get_addr(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - MemoryRegion *mr = MEMORY_REGION(obj); - uint64_t value = mr->addr; - - visit_type_uint64(v, name, &value, errp); -} - static void memory_region_get_container(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) @@ -1230,10 +1233,8 @@ static void memory_region_initfn(Object *obj) NULL, NULL, &error_abort); op->resolve = memory_region_resolve_container; - object_property_add(OBJECT(mr), "addr", "uint64", - memory_region_get_addr, - NULL, /* memory_region_set_addr */ - NULL, NULL, &error_abort); + object_property_add_uint64_ptr(OBJECT(mr), "addr", + &mr->addr, OBJ_PROP_FLAG_READ, &error_abort); object_property_add(OBJECT(mr), "priority", "uint32", memory_region_get_priority, NULL, /* memory_region_set_priority */ @@ -1659,19 +1660,8 @@ void memory_region_init_rom_nomigrate(MemoryRegion *mr, uint64_t size, Error **errp) { - Error *err = NULL; - memory_region_init(mr, owner, name, size); - mr->ram = true; + memory_region_init_ram_shared_nomigrate(mr, owner, name, size, false, errp); mr->readonly = true; - mr->terminates = true; - mr->destructor = memory_region_destructor_ram; - mr->ram_block = qemu_ram_alloc(size, false, mr, &err); - mr->dirty_log_mask = tcg_enabled() ? (1 << DIRTY_MEMORY_CODE) : 0; - if (err) { - mr->size = int128_zero(); - object_unparent(OBJECT(mr)); - error_propagate(errp, err); - } } void memory_region_init_rom_device_nomigrate(MemoryRegion *mr, @@ -2270,6 +2260,18 @@ void memory_region_ram_resize(MemoryRegion *mr, ram_addr_t newsize, Error **errp qemu_ram_resize(mr->ram_block, newsize, errp); } + +void memory_region_do_writeback(MemoryRegion *mr, hwaddr addr, hwaddr size) +{ + /* + * Might be extended case needed to cover + * different types of memory regions + */ + if (mr->ram_block && mr->dirty_log_mask) { + qemu_ram_writeback(mr->ram_block, addr, size); + } +} + /* * Call proper memory listeners about the change on the newly * added/removed CoalescedMemoryRange. @@ -2869,6 +2871,9 @@ void address_space_destroy(AddressSpace *as) static const char *memory_region_type(MemoryRegion *mr) { + if (mr->alias) { + return memory_region_type(mr->alias); + } if (memory_region_is_ram_device(mr)) { return "ramd"; } else if (memory_region_is_romd(mr)) { @@ -3042,7 +3047,6 @@ struct FlatViewInfo { bool dispatch_tree; bool owner; AccelClass *ac; - const char *ac_name; }; static void mtree_print_flatview(gpointer key, gpointer value, @@ -3112,7 +3116,7 @@ static void mtree_print_flatview(gpointer key, gpointer value, if (fvi->ac->has_memory(current_machine, as, int128_get64(range->addr.start), MR_SIZE(range->addr.size) + 1)) { - qemu_printf(" %s", fvi->ac_name); + qemu_printf(" %s", fvi->ac->name); } } } @@ -3156,12 +3160,10 @@ void mtree_info(bool flatview, bool dispatch_tree, bool owner) }; GArray *fv_address_spaces; GHashTable *views = g_hash_table_new(g_direct_hash, g_direct_equal); - AccelClass *ac = ACCEL_GET_CLASS(current_machine->accelerator); + AccelClass *ac = ACCEL_GET_CLASS(current_accel()); if (ac->has_memory) { fvi.ac = ac; - fvi.ac_name = current_machine->accel ? current_machine->accel : - object_class_get_name(OBJECT_CLASS(ac)); } /* Gather all FVs in one table */ diff --git a/migration/Makefile.objs b/migration/Makefile.objs index a4f3bafd86..0fc619e380 100644 --- a/migration/Makefile.objs +++ b/migration/Makefile.objs @@ -7,6 +7,9 @@ common-obj-y += qemu-file-channel.o common-obj-y += xbzrle.o postcopy-ram.o common-obj-y += qjson.o common-obj-y += block-dirty-bitmap.o +common-obj-y += multifd.o +common-obj-y += multifd-zlib.o +common-obj-$(CONFIG_ZSTD) += multifd-zstd.o common-obj-$(CONFIG_RDMA) += rdma.o diff --git a/migration/block.c b/migration/block.c index c90288ed29..737b6499f9 100644 --- a/migration/block.c +++ b/migration/block.c @@ -27,8 +27,8 @@ #include "migration/vmstate.h" #include "sysemu/block-backend.h" -#define BLOCK_SIZE (1 << 20) -#define BDRV_SECTORS_PER_DIRTY_CHUNK (BLOCK_SIZE >> BDRV_SECTOR_BITS) +#define BLK_MIG_BLOCK_SIZE (1 << 20) +#define BDRV_SECTORS_PER_DIRTY_CHUNK (BLK_MIG_BLOCK_SIZE >> BDRV_SECTOR_BITS) #define BLK_MIG_FLAG_DEVICE_BLOCK 0x01 #define BLK_MIG_FLAG_EOS 0x02 @@ -133,7 +133,7 @@ static void blk_send(QEMUFile *f, BlkMigBlock * blk) uint64_t flags = BLK_MIG_FLAG_DEVICE_BLOCK; if (block_mig_state.zero_blocks && - buffer_is_zero(blk->buf, BLOCK_SIZE)) { + buffer_is_zero(blk->buf, BLK_MIG_BLOCK_SIZE)) { flags |= BLK_MIG_FLAG_ZERO_BLOCK; } @@ -154,7 +154,7 @@ static void blk_send(QEMUFile *f, BlkMigBlock * blk) return; } - qemu_put_buffer(f, blk->buf, BLOCK_SIZE); + qemu_put_buffer(f, blk->buf, BLK_MIG_BLOCK_SIZE); } int blk_mig_active(void) @@ -309,7 +309,7 @@ static int mig_save_device_bulk(QEMUFile *f, BlkMigDevState *bmds) } blk = g_new(BlkMigBlock, 1); - blk->buf = g_malloc(BLOCK_SIZE); + blk->buf = g_malloc(BLK_MIG_BLOCK_SIZE); blk->bmds = bmds; blk->sector = cur_sector; blk->nr_sectors = nr_sectors; @@ -350,7 +350,8 @@ static int set_dirty_tracking(void) QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) { bmds->dirty_bitmap = bdrv_create_dirty_bitmap(blk_bs(bmds->blk), - BLOCK_SIZE, NULL, NULL); + BLK_MIG_BLOCK_SIZE, + NULL, NULL); if (!bmds->dirty_bitmap) { ret = -errno; goto fail; @@ -548,7 +549,7 @@ static int mig_save_device_dirty(QEMUFile *f, BlkMigDevState *bmds, bdrv_dirty_bitmap_unlock(bmds->dirty_bitmap); blk = g_new(BlkMigBlock, 1); - blk->buf = g_malloc(BLOCK_SIZE); + blk->buf = g_malloc(BLK_MIG_BLOCK_SIZE); blk->bmds = bmds; blk->sector = sector; blk->nr_sectors = nr_sectors; @@ -770,7 +771,7 @@ static int block_save_iterate(QEMUFile *f, void *opaque) /* control the rate of transfer */ blk_mig_lock(); - while (block_mig_state.read_done * BLOCK_SIZE < + while (block_mig_state.read_done * BLK_MIG_BLOCK_SIZE < qemu_file_get_rate_limit(f) && block_mig_state.submitted < MAX_PARALLEL_IO && (block_mig_state.submitted + block_mig_state.read_done) < @@ -874,13 +875,13 @@ static void block_save_pending(QEMUFile *f, void *opaque, uint64_t max_size, qemu_mutex_unlock_iothread(); blk_mig_lock(); - pending += block_mig_state.submitted * BLOCK_SIZE + - block_mig_state.read_done * BLOCK_SIZE; + pending += block_mig_state.submitted * BLK_MIG_BLOCK_SIZE + + block_mig_state.read_done * BLK_MIG_BLOCK_SIZE; blk_mig_unlock(); /* Report at least one block pending during bulk phase */ if (pending <= max_size && !block_mig_state.bulk_completed) { - pending = max_size + BLOCK_SIZE; + pending = max_size + BLK_MIG_BLOCK_SIZE; } DPRINTF("Enter save live pending %" PRIu64 "\n", pending); @@ -901,7 +902,7 @@ static int block_load(QEMUFile *f, void *opaque, int version_id) int nr_sectors; int ret; BlockDriverInfo bdi; - int cluster_size = BLOCK_SIZE; + int cluster_size = BLK_MIG_BLOCK_SIZE; do { addr = qemu_get_be64(f); @@ -939,11 +940,11 @@ static int block_load(QEMUFile *f, void *opaque, int version_id) ret = bdrv_get_info(blk_bs(blk), &bdi); if (ret == 0 && bdi.cluster_size > 0 && - bdi.cluster_size <= BLOCK_SIZE && - BLOCK_SIZE % bdi.cluster_size == 0) { + bdi.cluster_size <= BLK_MIG_BLOCK_SIZE && + BLK_MIG_BLOCK_SIZE % bdi.cluster_size == 0) { cluster_size = bdi.cluster_size; } else { - cluster_size = BLOCK_SIZE; + cluster_size = BLK_MIG_BLOCK_SIZE; } } @@ -962,14 +963,14 @@ static int block_load(QEMUFile *f, void *opaque, int version_id) int64_t cur_addr; uint8_t *cur_buf; - buf = g_malloc(BLOCK_SIZE); - qemu_get_buffer(f, buf, BLOCK_SIZE); - for (i = 0; i < BLOCK_SIZE / cluster_size; i++) { + buf = g_malloc(BLK_MIG_BLOCK_SIZE); + qemu_get_buffer(f, buf, BLK_MIG_BLOCK_SIZE); + for (i = 0; i < BLK_MIG_BLOCK_SIZE / cluster_size; i++) { cur_addr = addr * BDRV_SECTOR_SIZE + i * cluster_size; cur_buf = buf + i * cluster_size; if ((!block_mig_state.zero_blocks || - cluster_size < BLOCK_SIZE) && + cluster_size < BLK_MIG_BLOCK_SIZE) && buffer_is_zero(cur_buf, cluster_size)) { ret = blk_pwrite_zeroes(blk, cur_addr, cluster_size, diff --git a/migration/colo.c b/migration/colo.c index 2c88aa57a2..a54ac84f41 100644 --- a/migration/colo.c +++ b/migration/colo.c @@ -26,6 +26,7 @@ #include "qemu/main-loop.h" #include "qemu/rcu.h" #include "migration/failover.h" +#include "migration/ram.h" #ifdef CONFIG_REPLICATION #include "replication.h" #endif @@ -92,6 +93,7 @@ static void secondary_vm_do_failover(void) replication_stop_all(true, &local_err); if (local_err) { error_report_err(local_err); + local_err = NULL; } /* Notify all filters of all NIC to do checkpoint */ @@ -664,13 +666,138 @@ void migrate_start_colo_process(MigrationState *s) qemu_mutex_lock_iothread(); } -static void colo_wait_handle_message(QEMUFile *f, int *checkpoint_request, - Error **errp) +static void colo_incoming_process_checkpoint(MigrationIncomingState *mis, + QEMUFile *fb, QIOChannelBuffer *bioc, Error **errp) +{ + uint64_t total_size; + uint64_t value; + Error *local_err = NULL; + int ret; + + qemu_mutex_lock_iothread(); + vm_stop_force_state(RUN_STATE_COLO); + trace_colo_vm_state_change("run", "stop"); + qemu_mutex_unlock_iothread(); + + /* FIXME: This is unnecessary for periodic checkpoint mode */ + colo_send_message(mis->to_src_file, COLO_MESSAGE_CHECKPOINT_REPLY, + &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + colo_receive_check_message(mis->from_src_file, + COLO_MESSAGE_VMSTATE_SEND, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + qemu_mutex_lock_iothread(); + cpu_synchronize_all_pre_loadvm(); + ret = qemu_loadvm_state_main(mis->from_src_file, mis); + qemu_mutex_unlock_iothread(); + + if (ret < 0) { + error_setg(errp, "Load VM's live state (ram) error"); + return; + } + + value = colo_receive_message_value(mis->from_src_file, + COLO_MESSAGE_VMSTATE_SIZE, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + /* + * Read VM device state data into channel buffer, + * It's better to re-use the memory allocated. + * Here we need to handle the channel buffer directly. + */ + if (value > bioc->capacity) { + bioc->capacity = value; + bioc->data = g_realloc(bioc->data, bioc->capacity); + } + total_size = qemu_get_buffer(mis->from_src_file, bioc->data, value); + if (total_size != value) { + error_setg(errp, "Got %" PRIu64 " VMState data, less than expected" + " %" PRIu64, total_size, value); + return; + } + bioc->usage = total_size; + qio_channel_io_seek(QIO_CHANNEL(bioc), 0, 0, NULL); + + colo_send_message(mis->to_src_file, COLO_MESSAGE_VMSTATE_RECEIVED, + &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + qemu_mutex_lock_iothread(); + vmstate_loading = true; + ret = qemu_load_device_state(fb); + if (ret < 0) { + error_setg(errp, "COLO: load device state failed"); + qemu_mutex_unlock_iothread(); + return; + } + +#ifdef CONFIG_REPLICATION + replication_get_error_all(&local_err); + if (local_err) { + error_propagate(errp, local_err); + qemu_mutex_unlock_iothread(); + return; + } + + /* discard colo disk buffer */ + replication_do_checkpoint_all(&local_err); + if (local_err) { + error_propagate(errp, local_err); + qemu_mutex_unlock_iothread(); + return; + } +#else + abort(); +#endif + /* Notify all filters of all NIC to do checkpoint */ + colo_notify_filters_event(COLO_EVENT_CHECKPOINT, &local_err); + + if (local_err) { + error_propagate(errp, local_err); + qemu_mutex_unlock_iothread(); + return; + } + + vmstate_loading = false; + vm_start(); + trace_colo_vm_state_change("stop", "run"); + qemu_mutex_unlock_iothread(); + + if (failover_get_state() == FAILOVER_STATUS_RELAUNCH) { + failover_set_state(FAILOVER_STATUS_RELAUNCH, + FAILOVER_STATUS_NONE); + failover_request_active(NULL); + return; + } + + colo_send_message(mis->to_src_file, COLO_MESSAGE_VMSTATE_LOADED, + &local_err); + if (local_err) { + error_propagate(errp, local_err); + } +} + +static void colo_wait_handle_message(MigrationIncomingState *mis, + QEMUFile *fb, QIOChannelBuffer *bioc, Error **errp) { COLOMessage msg; Error *local_err = NULL; - msg = colo_receive_message(f, &local_err); + msg = colo_receive_message(mis->from_src_file, &local_err); if (local_err) { error_propagate(errp, local_err); return; @@ -678,10 +805,9 @@ static void colo_wait_handle_message(QEMUFile *f, int *checkpoint_request, switch (msg) { case COLO_MESSAGE_CHECKPOINT_REQUEST: - *checkpoint_request = 1; + colo_incoming_process_checkpoint(mis, fb, bioc, errp); break; default: - *checkpoint_request = 0; error_setg(errp, "Got unknown COLO message: %d", msg); break; } @@ -692,10 +818,7 @@ void *colo_process_incoming_thread(void *opaque) MigrationIncomingState *mis = opaque; QEMUFile *fb = NULL; QIOChannelBuffer *bioc = NULL; /* Cache incoming device state */ - uint64_t total_size; - uint64_t value; Error *local_err = NULL; - int ret; rcu_register_thread(); qemu_sem_init(&mis->colo_incoming_sem, 0); @@ -724,6 +847,8 @@ void *colo_process_incoming_thread(void *opaque) */ qemu_file_set_blocking(mis->from_src_file, true); + colo_incoming_start_dirty_log(); + bioc = qio_channel_buffer_new(COLO_BUFFER_BASE_SIZE); fb = qemu_fopen_channel_input(QIO_CHANNEL(bioc)); object_unref(OBJECT(bioc)); @@ -749,134 +874,19 @@ void *colo_process_incoming_thread(void *opaque) } while (mis->state == MIGRATION_STATUS_COLO) { - int request = 0; - - colo_wait_handle_message(mis->from_src_file, &request, &local_err); + colo_wait_handle_message(mis, fb, bioc, &local_err); if (local_err) { - goto out; + error_report_err(local_err); + break; } - assert(request); if (failover_get_state() != FAILOVER_STATUS_NONE) { error_report("failover request"); - goto out; - } - - qemu_mutex_lock_iothread(); - vm_stop_force_state(RUN_STATE_COLO); - trace_colo_vm_state_change("run", "stop"); - qemu_mutex_unlock_iothread(); - - /* FIXME: This is unnecessary for periodic checkpoint mode */ - colo_send_message(mis->to_src_file, COLO_MESSAGE_CHECKPOINT_REPLY, - &local_err); - if (local_err) { - goto out; - } - - colo_receive_check_message(mis->from_src_file, - COLO_MESSAGE_VMSTATE_SEND, &local_err); - if (local_err) { - goto out; - } - - qemu_mutex_lock_iothread(); - cpu_synchronize_all_pre_loadvm(); - ret = qemu_loadvm_state_main(mis->from_src_file, mis); - qemu_mutex_unlock_iothread(); - - if (ret < 0) { - error_report("Load VM's live state (ram) error"); - goto out; - } - - value = colo_receive_message_value(mis->from_src_file, - COLO_MESSAGE_VMSTATE_SIZE, &local_err); - if (local_err) { - goto out; - } - - /* - * Read VM device state data into channel buffer, - * It's better to re-use the memory allocated. - * Here we need to handle the channel buffer directly. - */ - if (value > bioc->capacity) { - bioc->capacity = value; - bioc->data = g_realloc(bioc->data, bioc->capacity); - } - total_size = qemu_get_buffer(mis->from_src_file, bioc->data, value); - if (total_size != value) { - error_report("Got %" PRIu64 " VMState data, less than expected" - " %" PRIu64, total_size, value); - goto out; - } - bioc->usage = total_size; - qio_channel_io_seek(QIO_CHANNEL(bioc), 0, 0, NULL); - - colo_send_message(mis->to_src_file, COLO_MESSAGE_VMSTATE_RECEIVED, - &local_err); - if (local_err) { - goto out; - } - - qemu_mutex_lock_iothread(); - vmstate_loading = true; - ret = qemu_load_device_state(fb); - if (ret < 0) { - error_report("COLO: load device state failed"); - qemu_mutex_unlock_iothread(); - goto out; - } - -#ifdef CONFIG_REPLICATION - replication_get_error_all(&local_err); - if (local_err) { - qemu_mutex_unlock_iothread(); - goto out; - } - - /* discard colo disk buffer */ - replication_do_checkpoint_all(&local_err); - if (local_err) { - qemu_mutex_unlock_iothread(); - goto out; - } -#else - abort(); -#endif - /* Notify all filters of all NIC to do checkpoint */ - colo_notify_filters_event(COLO_EVENT_CHECKPOINT, &local_err); - - if (local_err) { - qemu_mutex_unlock_iothread(); - goto out; - } - - vmstate_loading = false; - vm_start(); - trace_colo_vm_state_change("stop", "run"); - qemu_mutex_unlock_iothread(); - - if (failover_get_state() == FAILOVER_STATUS_RELAUNCH) { - failover_set_state(FAILOVER_STATUS_RELAUNCH, - FAILOVER_STATUS_NONE); - failover_request_active(NULL); - goto out; - } - - colo_send_message(mis->to_src_file, COLO_MESSAGE_VMSTATE_LOADED, - &local_err); - if (local_err) { - goto out; + break; } } out: vmstate_loading = false; - /* Throw the unreported error message after exited from loop */ - if (local_err) { - error_report_err(local_err); - } /* * There are only two reasons we can get here, some error happened diff --git a/migration/migration.c b/migration/migration.c index 354ad072fa..187ac0410c 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -53,6 +53,7 @@ #include "monitor/monitor.h" #include "net/announce.h" #include "qemu/queue.h" +#include "multifd.h" #define MAX_THROTTLE (32 << 20) /* Migration transfer speed throttling */ @@ -77,6 +78,7 @@ /*0: means nocompress, 1: best speed, ... 9: best compress ratio */ #define DEFAULT_MIGRATE_COMPRESS_LEVEL 1 /* Define default autoconverge cpu throttle migration parameters */ +#define DEFAULT_MIGRATE_THROTTLE_TRIGGER_THRESHOLD 50 #define DEFAULT_MIGRATE_CPU_THROTTLE_INITIAL 20 #define DEFAULT_MIGRATE_CPU_THROTTLE_INCREMENT 10 #define DEFAULT_MIGRATE_MAX_CPU_THROTTLE 99 @@ -87,6 +89,11 @@ /* The delay time (in ms) between two COLO checkpoints */ #define DEFAULT_MIGRATE_X_CHECKPOINT_DELAY (200 * 100) #define DEFAULT_MIGRATE_MULTIFD_CHANNELS 2 +#define DEFAULT_MIGRATE_MULTIFD_COMPRESSION MULTIFD_COMPRESSION_NONE +/* 0: means nocompress, 1: best speed, ... 9: best compress ratio */ +#define DEFAULT_MIGRATE_MULTIFD_ZLIB_LEVEL 1 +/* 0: means nocompress, 1: best speed, ... 20: best compress ratio */ +#define DEFAULT_MIGRATE_MULTIFD_ZSTD_LEVEL 1 /* Background transfer rate for postcopy, 0 means unlimited, note * that page requests can still exceed this limit. @@ -483,11 +490,6 @@ static void process_incoming_migration_co(void *opaque) goto fail; } - if (colo_init_ram_cache() < 0) { - error_report("Init ram cache failed"); - goto fail; - } - qemu_thread_create(&mis->colo_incoming_thread, "COLO incoming", colo_process_incoming_thread, mis, QEMU_THREAD_JOINABLE); mis->have_colo_incoming_thread = true; @@ -518,13 +520,23 @@ fail: exit(EXIT_FAILURE); } -static void migration_incoming_setup(QEMUFile *f) +/** + * @migration_incoming_setup: Setup incoming migration + * + * Returns 0 for no error or 1 for error + * + * @f: file for main migration channel + * @errp: where to put errors + */ +static int migration_incoming_setup(QEMUFile *f, Error **errp) { MigrationIncomingState *mis = migration_incoming_get_current(); + Error *local_err = NULL; - if (multifd_load_setup() != 0) { + if (multifd_load_setup(&local_err) != 0) { /* We haven't been able to create multifd threads nothing better to do */ + error_report_err(local_err); exit(EXIT_FAILURE); } @@ -532,6 +544,7 @@ static void migration_incoming_setup(QEMUFile *f) mis->from_src_file = f; } qemu_file_set_blocking(f, false); + return 0; } void migration_incoming_process(void) @@ -572,19 +585,27 @@ static bool postcopy_try_recover(QEMUFile *f) return false; } -void migration_fd_process_incoming(QEMUFile *f) +void migration_fd_process_incoming(QEMUFile *f, Error **errp) { + Error *local_err = NULL; + if (postcopy_try_recover(f)) { return; } - migration_incoming_setup(f); + if (migration_incoming_setup(f, &local_err)) { + if (local_err) { + error_propagate(errp, local_err); + } + return; + } migration_incoming_process(); } void migration_ioc_process_incoming(QIOChannel *ioc, Error **errp) { MigrationIncomingState *mis = migration_incoming_get_current(); + Error *local_err = NULL; bool start_migration; if (!mis->from_src_file) { @@ -596,7 +617,12 @@ void migration_ioc_process_incoming(QIOChannel *ioc, Error **errp) return; } - migration_incoming_setup(f); + if (migration_incoming_setup(f, &local_err)) { + if (local_err) { + error_propagate(errp, local_err); + } + return; + } /* * Common migration only needs one channel, so we can start @@ -604,7 +630,6 @@ void migration_ioc_process_incoming(QIOChannel *ioc, Error **errp) */ start_migration = !migrate_use_multifd(); } else { - Error *local_err = NULL; /* Multiple connections */ assert(migrate_use_multifd()); start_migration = multifd_recv_new_channel(ioc, &local_err); @@ -754,6 +779,8 @@ MigrationParameters *qmp_query_migrate_parameters(Error **errp) params->compress_wait_thread = s->parameters.compress_wait_thread; params->has_decompress_threads = true; params->decompress_threads = s->parameters.decompress_threads; + params->has_throttle_trigger_threshold = true; + params->throttle_trigger_threshold = s->parameters.throttle_trigger_threshold; params->has_cpu_throttle_initial = true; params->cpu_throttle_initial = s->parameters.cpu_throttle_initial; params->has_cpu_throttle_increment = true; @@ -763,7 +790,8 @@ MigrationParameters *qmp_query_migrate_parameters(Error **errp) params->has_tls_hostname = true; params->tls_hostname = g_strdup(s->parameters.tls_hostname); params->has_tls_authz = true; - params->tls_authz = g_strdup(s->parameters.tls_authz); + params->tls_authz = g_strdup(s->parameters.tls_authz ? + s->parameters.tls_authz : ""); params->has_max_bandwidth = true; params->max_bandwidth = s->parameters.max_bandwidth; params->has_downtime_limit = true; @@ -774,6 +802,12 @@ MigrationParameters *qmp_query_migrate_parameters(Error **errp) params->block_incremental = s->parameters.block_incremental; params->has_multifd_channels = true; params->multifd_channels = s->parameters.multifd_channels; + params->has_multifd_compression = true; + params->multifd_compression = s->parameters.multifd_compression; + params->has_multifd_zlib_level = true; + params->multifd_zlib_level = s->parameters.multifd_zlib_level; + params->has_multifd_zstd_level = true; + params->multifd_zstd_level = s->parameters.multifd_zstd_level; params->has_xbzrle_cache_size = true; params->xbzrle_cache_size = s->parameters.xbzrle_cache_size; params->has_max_postcopy_bandwidth = true; @@ -821,6 +855,27 @@ bool migration_is_setup_or_active(int state) case MIGRATION_STATUS_PRE_SWITCHOVER: case MIGRATION_STATUS_DEVICE: case MIGRATION_STATUS_WAIT_UNPLUG: + case MIGRATION_STATUS_COLO: + return true; + + default: + return false; + + } +} + +bool migration_is_running(int state) +{ + switch (state) { + case MIGRATION_STATUS_ACTIVE: + case MIGRATION_STATUS_POSTCOPY_ACTIVE: + case MIGRATION_STATUS_POSTCOPY_PAUSED: + case MIGRATION_STATUS_POSTCOPY_RECOVER: + case MIGRATION_STATUS_SETUP: + case MIGRATION_STATUS_PRE_SWITCHOVER: + case MIGRATION_STATUS_DEVICE: + case MIGRATION_STATUS_WAIT_UNPLUG: + case MIGRATION_STATUS_CANCELLING: return true; default: @@ -1005,17 +1060,6 @@ static bool migrate_caps_check(bool *cap_list, #endif if (cap_list[MIGRATION_CAPABILITY_POSTCOPY_RAM]) { - if (cap_list[MIGRATION_CAPABILITY_COMPRESS]) { - /* The decompression threads asynchronously write into RAM - * rather than use the atomic copies needed to avoid - * userfaulting. It should be possible to fix the decompression - * threads for compatibility in future. - */ - error_setg(errp, "Postcopy is not currently compatible " - "with compression"); - return false; - } - /* This check is reasonably expensive, so only when it's being * set the first time, also it's only the destination that needs * special support. @@ -1088,7 +1132,7 @@ void qmp_migrate_set_capabilities(MigrationCapabilityStatusList *params, MigrationCapabilityStatusList *cap; bool cap_list[MIGRATION_CAPABILITY__MAX]; - if (migration_is_setup_or_active(s->state)) { + if (migration_is_running(s->state)) { error_setg(errp, QERR_MIGRATION_ACTIVE); return; } @@ -1130,6 +1174,15 @@ static bool migrate_params_check(MigrationParameters *params, Error **errp) return false; } + if (params->has_throttle_trigger_threshold && + (params->throttle_trigger_threshold < 1 || + params->throttle_trigger_threshold > 100)) { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, + "throttle_trigger_threshold", + "an integer in the range of 1 to 100"); + return false; + } + if (params->has_cpu_throttle_initial && (params->cpu_throttle_initial < 1 || params->cpu_throttle_initial > 99)) { @@ -1171,13 +1224,27 @@ static bool migrate_params_check(MigrationParameters *params, Error **errp) return false; } + if (params->has_multifd_zlib_level && + (params->multifd_zlib_level > 9)) { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "multifd_zlib_level", + "is invalid, it should be in the range of 0 to 9"); + return false; + } + + if (params->has_multifd_zstd_level && + (params->multifd_zstd_level > 20)) { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "multifd_zstd_level", + "is invalid, it should be in the range of 0 to 20"); + return false; + } + if (params->has_xbzrle_cache_size && (params->xbzrle_cache_size < qemu_target_page_size() || !is_power_of_2(params->xbzrle_cache_size))) { error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "xbzrle_cache_size", "is invalid, it should be bigger than target page size" - " and a power of two"); + " and a power of 2"); return false; } @@ -1245,6 +1312,10 @@ static void migrate_params_test_apply(MigrateSetParameters *params, dest->decompress_threads = params->decompress_threads; } + if (params->has_throttle_trigger_threshold) { + dest->throttle_trigger_threshold = params->throttle_trigger_threshold; + } + if (params->has_cpu_throttle_initial) { dest->cpu_throttle_initial = params->cpu_throttle_initial; } @@ -1281,6 +1352,9 @@ static void migrate_params_test_apply(MigrateSetParameters *params, if (params->has_multifd_channels) { dest->multifd_channels = params->multifd_channels; } + if (params->has_multifd_compression) { + dest->multifd_compression = params->multifd_compression; + } if (params->has_xbzrle_cache_size) { dest->xbzrle_cache_size = params->xbzrle_cache_size; } @@ -1326,6 +1400,10 @@ static void migrate_params_apply(MigrateSetParameters *params, Error **errp) s->parameters.decompress_threads = params->decompress_threads; } + if (params->has_throttle_trigger_threshold) { + s->parameters.throttle_trigger_threshold = params->throttle_trigger_threshold; + } + if (params->has_cpu_throttle_initial) { s->parameters.cpu_throttle_initial = params->cpu_throttle_initial; } @@ -1377,6 +1455,9 @@ static void migrate_params_apply(MigrateSetParameters *params, Error **errp) if (params->has_multifd_channels) { s->parameters.multifd_channels = params->multifd_channels; } + if (params->has_multifd_compression) { + s->parameters.multifd_compression = params->multifd_compression; + } if (params->has_xbzrle_cache_size) { s->parameters.xbzrle_cache_size = params->xbzrle_cache_size; xbzrle_cache_resize(params->xbzrle_cache_size, errp); @@ -1601,7 +1682,7 @@ static void migrate_fd_cancel(MigrationState *s) do { old_state = s->state; - if (!migration_is_setup_or_active(old_state)) { + if (!migration_is_running(old_state)) { break; } /* If the migration is paused, kick it out of the pause */ @@ -1784,6 +1865,7 @@ void qmp_migrate_incoming(const char *uri, Error **errp) } if (!once) { error_setg(errp, "The incoming migration has already been started"); + return; } qemu_start_incoming_migration(uri, &local_err); @@ -1898,9 +1980,7 @@ static bool migrate_prepare(MigrationState *s, bool blk, bool blk_inc, return true; } - if (migration_is_setup_or_active(s->state) || - s->state == MIGRATION_STATUS_CANCELLING || - s->state == MIGRATION_STATUS_COLO) { + if (migration_is_running(s->state)) { error_setg(errp, QERR_MIGRATION_ACTIVE); return false; } @@ -2035,11 +2115,10 @@ void qmp_migrate_set_downtime(double value, Error **errp) } value *= 1000; /* Convert to milliseconds */ - value = MAX(0, MIN(INT64_MAX, value)); MigrateSetParameters p = { .has_downtime_limit = true, - .downtime_limit = value, + .downtime_limit = (int64_t)value, }; qmp_migrate_set_parameters(&p, errp); @@ -2204,6 +2283,33 @@ int migrate_multifd_channels(void) return s->parameters.multifd_channels; } +MultiFDCompression migrate_multifd_compression(void) +{ + MigrationState *s; + + s = migrate_get_current(); + + return s->parameters.multifd_compression; +} + +int migrate_multifd_zlib_level(void) +{ + MigrationState *s; + + s = migrate_get_current(); + + return s->parameters.multifd_zlib_level; +} + +int migrate_multifd_zstd_level(void) +{ + MigrationState *s; + + s = migrate_get_current(); + + return s->parameters.multifd_zstd_level; +} + int migrate_use_xbzrle(void) { MigrationState *s; @@ -2765,14 +2871,22 @@ static int migration_maybe_pause(MigrationState *s, /* This block intentionally left blank */ } - qemu_mutex_unlock_iothread(); - migrate_set_state(&s->state, *current_active_state, - MIGRATION_STATUS_PRE_SWITCHOVER); - qemu_sem_wait(&s->pause_sem); - migrate_set_state(&s->state, MIGRATION_STATUS_PRE_SWITCHOVER, - new_state); - *current_active_state = new_state; - qemu_mutex_lock_iothread(); + /* + * If the migration is cancelled when it is in the completion phase, + * the migration state is set to MIGRATION_STATUS_CANCELLING. + * So we don't need to wait a semaphore, otherwise we would always + * wait for the 'pause_sem' semaphore. + */ + if (s->state != MIGRATION_STATUS_CANCELLING) { + qemu_mutex_unlock_iothread(); + migrate_set_state(&s->state, *current_active_state, + MIGRATION_STATUS_PRE_SWITCHOVER); + qemu_sem_wait(&s->pause_sem); + migrate_set_state(&s->state, MIGRATION_STATUS_PRE_SWITCHOVER, + new_state); + *current_active_state = new_state; + qemu_mutex_lock_iothread(); + } return s->state == new_state ? 0 : -EINVAL; } @@ -3224,6 +3338,37 @@ void migration_consume_urgent_request(void) qemu_sem_wait(&migrate_get_current()->rate_limit_sem); } +/* Returns true if the rate limiting was broken by an urgent request */ +bool migration_rate_limit(void) +{ + int64_t now = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); + MigrationState *s = migrate_get_current(); + + bool urgent = false; + migration_update_counters(s, now); + if (qemu_file_rate_limit(s->to_dst_file)) { + /* + * Wait for a delay to do rate limiting OR + * something urgent to post the semaphore. + */ + int ms = s->iteration_start_time + BUFFER_DELAY - now; + trace_migration_rate_limit_pre(ms); + if (qemu_sem_timedwait(&s->rate_limit_sem, ms) == 0) { + /* + * We were woken by one or more urgent things but + * the timedwait will have consumed one of them. + * The service routine for the urgent wake will dec + * the semaphore itself for each item it consumes, + * so add this one we just eat back. + */ + qemu_sem_post(&s->rate_limit_sem); + urgent = true; + } + trace_migration_rate_limit_post(urgent); + } + return urgent; +} + /* * Master migration thread on the source VM. * It drives the migration and pumps the data down the outgoing channel. @@ -3270,7 +3415,7 @@ static void *migration_thread(void *opaque) qemu_savevm_state_setup(s->to_dst_file); - if (qemu_savevm_nr_failover_devices()) { + if (qemu_savevm_state_guest_unplug_pending()) { migrate_set_state(&s->state, MIGRATION_STATUS_SETUP, MIGRATION_STATUS_WAIT_UNPLUG); @@ -3290,8 +3435,6 @@ static void *migration_thread(void *opaque) trace_migration_thread_setup_complete(); while (migration_is_active(s)) { - int64_t current_time; - if (urgent || !qemu_file_rate_limit(s->to_dst_file)) { MigIterateState iter_state = migration_iteration_run(s); if (iter_state == MIG_ITERATE_SKIP) { @@ -3318,29 +3461,7 @@ static void *migration_thread(void *opaque) update_iteration_initial_status(s); } - current_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); - - migration_update_counters(s, current_time); - - urgent = false; - if (qemu_file_rate_limit(s->to_dst_file)) { - /* Wait for a delay to do rate limiting OR - * something urgent to post the semaphore. - */ - int ms = s->iteration_start_time + BUFFER_DELAY - current_time; - trace_migration_thread_ratelimit_pre(ms); - if (qemu_sem_timedwait(&s->rate_limit_sem, ms) == 0) { - /* We were worken by one or more urgent things but - * the timedwait will have consumed one of them. - * The service routine for the urgent wake will dec - * the semaphore itself for each item it consumes, - * so add this one we just eat back. - */ - qemu_sem_post(&s->rate_limit_sem); - urgent = true; - } - trace_migration_thread_ratelimit_post(urgent); - } + urgent = migration_rate_limit(); } trace_migration_thread_after_loop(); @@ -3352,11 +3473,17 @@ static void *migration_thread(void *opaque) void migrate_fd_connect(MigrationState *s, Error *error_in) { + Error *local_err = NULL; int64_t rate_limit; bool resume = s->state == MIGRATION_STATUS_POSTCOPY_PAUSED; s->expected_downtime = s->parameters.downtime_limit; - s->cleanup_bh = qemu_bh_new(migrate_fd_cleanup_bh, s); + if (resume) { + assert(s->cleanup_bh); + } else { + assert(!s->cleanup_bh); + s->cleanup_bh = qemu_bh_new(migrate_fd_cleanup_bh, s); + } if (error_in) { migrate_fd_error(s, error_in); migrate_fd_cleanup(s); @@ -3400,7 +3527,8 @@ void migrate_fd_connect(MigrationState *s, Error *error_in) return; } - if (multifd_save_setup() != 0) { + if (multifd_save_setup(&local_err) != 0) { + error_report_err(local_err); migrate_set_state(&s->state, MIGRATION_STATUS_SETUP, MIGRATION_STATUS_FAILED); migrate_fd_cleanup(s); @@ -3457,6 +3585,9 @@ static Property migration_properties[] = { DEFINE_PROP_UINT8("x-decompress-threads", MigrationState, parameters.decompress_threads, DEFAULT_MIGRATE_DECOMPRESS_THREAD_COUNT), + DEFINE_PROP_UINT8("x-throttle-trigger-threshold", MigrationState, + parameters.throttle_trigger_threshold, + DEFAULT_MIGRATE_THROTTLE_TRIGGER_THRESHOLD), DEFINE_PROP_UINT8("x-cpu-throttle-initial", MigrationState, parameters.cpu_throttle_initial, DEFAULT_MIGRATE_CPU_THROTTLE_INITIAL), @@ -3474,6 +3605,15 @@ static Property migration_properties[] = { DEFINE_PROP_UINT8("multifd-channels", MigrationState, parameters.multifd_channels, DEFAULT_MIGRATE_MULTIFD_CHANNELS), + DEFINE_PROP_MULTIFD_COMPRESSION("multifd-compression", MigrationState, + parameters.multifd_compression, + DEFAULT_MIGRATE_MULTIFD_COMPRESSION), + DEFINE_PROP_UINT8("multifd-zlib-level", MigrationState, + parameters.multifd_zlib_level, + DEFAULT_MIGRATE_MULTIFD_ZLIB_LEVEL), + DEFINE_PROP_UINT8("multifd-zstd-level", MigrationState, + parameters.multifd_zstd_level, + DEFAULT_MIGRATE_MULTIFD_ZSTD_LEVEL), DEFINE_PROP_SIZE("xbzrle-cache-size", MigrationState, parameters.xbzrle_cache_size, DEFAULT_MIGRATE_XBZRLE_CACHE_SIZE), @@ -3518,7 +3658,7 @@ static void migration_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->user_creatable = false; - dc->props = migration_properties; + device_class_set_props(dc, migration_properties); } static void migration_instance_finalize(Object *obj) @@ -3557,6 +3697,7 @@ static void migration_instance_init(Object *obj) params->has_compress_level = true; params->has_compress_threads = true; params->has_decompress_threads = true; + params->has_throttle_trigger_threshold = true; params->has_cpu_throttle_initial = true; params->has_cpu_throttle_increment = true; params->has_max_bandwidth = true; @@ -3564,6 +3705,9 @@ static void migration_instance_init(Object *obj) params->has_x_checkpoint_delay = true; params->has_block_incremental = true; params->has_multifd_channels = true; + params->has_multifd_compression = true; + params->has_multifd_zlib_level = true; + params->has_multifd_zstd_level = true; params->has_xbzrle_cache_size = true; params->has_max_postcopy_bandwidth = true; params->has_max_cpu_throttle = true; diff --git a/migration/migration.h b/migration/migration.h index 79b3dda146..507284e563 100644 --- a/migration/migration.h +++ b/migration/migration.h @@ -265,7 +265,7 @@ struct MigrationState void migrate_set_state(int *state, int old_state, int new_state); -void migration_fd_process_incoming(QEMUFile *f); +void migration_fd_process_incoming(QEMUFile *f, Error **errp); void migration_ioc_process_incoming(QIOChannel *ioc, Error **errp); void migration_incoming_process(void); @@ -279,6 +279,7 @@ void migrate_fd_error(MigrationState *s, const Error *error); void migrate_fd_connect(MigrationState *s, Error *error_in); bool migration_is_setup_or_active(int state); +bool migration_is_running(int state); void migrate_init(MigrationState *s); bool migration_is_blocked(Error **errp); @@ -299,6 +300,9 @@ bool migrate_auto_converge(void); bool migrate_use_multifd(void); bool migrate_pause_before_switchover(void); int migrate_multifd_channels(void); +MultiFDCompression migrate_multifd_compression(void); +int migrate_multifd_zlib_level(void); +int migrate_multifd_zstd_level(void); int migrate_use_xbzrle(void); int64_t migrate_xbzrle_cache_size(void); @@ -341,5 +345,6 @@ int foreach_not_ignored_block(RAMBlockIterFunc func, void *opaque); void migration_make_urgent_request(void); void migration_consume_urgent_request(void); +bool migration_rate_limit(void); #endif diff --git a/migration/multifd-zlib.c b/migration/multifd-zlib.c new file mode 100644 index 0000000000..ab4ba75d75 --- /dev/null +++ b/migration/multifd-zlib.c @@ -0,0 +1,325 @@ +/* + * Multifd zlib compression implementation + * + * Copyright (c) 2020 Red Hat Inc + * + * Authors: + * Juan Quintela + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include +#include "qemu/rcu.h" +#include "exec/target_page.h" +#include "qapi/error.h" +#include "migration.h" +#include "trace.h" +#include "multifd.h" + +struct zlib_data { + /* stream for compression */ + z_stream zs; + /* compressed buffer */ + uint8_t *zbuff; + /* size of compressed buffer */ + uint32_t zbuff_len; +}; + +/* Multifd zlib compression */ + +/** + * zlib_send_setup: setup send side + * + * Setup each channel with zlib compression. + * + * Returns 0 for success or -1 for error + * + * @p: Params for the channel that we are using + * @errp: pointer to an error + */ +static int zlib_send_setup(MultiFDSendParams *p, Error **errp) +{ + uint32_t page_count = MULTIFD_PACKET_SIZE / qemu_target_page_size(); + struct zlib_data *z = g_malloc0(sizeof(struct zlib_data)); + z_stream *zs = &z->zs; + + zs->zalloc = Z_NULL; + zs->zfree = Z_NULL; + zs->opaque = Z_NULL; + if (deflateInit(zs, migrate_multifd_zlib_level()) != Z_OK) { + g_free(z); + error_setg(errp, "multifd %d: deflate init failed", p->id); + return -1; + } + /* We will never have more than page_count pages */ + z->zbuff_len = page_count * qemu_target_page_size(); + z->zbuff_len *= 2; + z->zbuff = g_try_malloc(z->zbuff_len); + if (!z->zbuff) { + deflateEnd(&z->zs); + g_free(z); + error_setg(errp, "multifd %d: out of memory for zbuff", p->id); + return -1; + } + p->data = z; + return 0; +} + +/** + * zlib_send_cleanup: cleanup send side + * + * Close the channel and return memory. + * + * @p: Params for the channel that we are using + */ +static void zlib_send_cleanup(MultiFDSendParams *p, Error **errp) +{ + struct zlib_data *z = p->data; + + deflateEnd(&z->zs); + g_free(z->zbuff); + z->zbuff = NULL; + g_free(p->data); + p->data = NULL; +} + +/** + * zlib_send_prepare: prepare date to be able to send + * + * Create a compressed buffer with all the pages that we are going to + * send. + * + * Returns 0 for success or -1 for error + * + * @p: Params for the channel that we are using + * @used: number of pages used + */ +static int zlib_send_prepare(MultiFDSendParams *p, uint32_t used, Error **errp) +{ + struct iovec *iov = p->pages->iov; + struct zlib_data *z = p->data; + z_stream *zs = &z->zs; + uint32_t out_size = 0; + int ret; + uint32_t i; + + for (i = 0; i < used; i++) { + uint32_t available = z->zbuff_len - out_size; + int flush = Z_NO_FLUSH; + + if (i == used - 1) { + flush = Z_SYNC_FLUSH; + } + + zs->avail_in = iov[i].iov_len; + zs->next_in = iov[i].iov_base; + + zs->avail_out = available; + zs->next_out = z->zbuff + out_size; + + /* + * Welcome to deflate semantics + * + * We need to loop while: + * - return is Z_OK + * - there are stuff to be compressed + * - there are output space free + */ + do { + ret = deflate(zs, flush); + } while (ret == Z_OK && zs->avail_in && zs->avail_out); + if (ret == Z_OK && zs->avail_in) { + error_setg(errp, "multifd %d: deflate failed to compress all input", + p->id); + return -1; + } + if (ret != Z_OK) { + error_setg(errp, "multifd %d: deflate returned %d instead of Z_OK", + p->id, ret); + return -1; + } + out_size += available - zs->avail_out; + } + p->next_packet_size = out_size; + p->flags |= MULTIFD_FLAG_ZLIB; + + return 0; +} + +/** + * zlib_send_write: do the actual write of the data + * + * Do the actual write of the comprresed buffer. + * + * Returns 0 for success or -1 for error + * + * @p: Params for the channel that we are using + * @used: number of pages used + * @errp: pointer to an error + */ +static int zlib_send_write(MultiFDSendParams *p, uint32_t used, Error **errp) +{ + struct zlib_data *z = p->data; + + return qio_channel_write_all(p->c, (void *)z->zbuff, p->next_packet_size, + errp); +} + +/** + * zlib_recv_setup: setup receive side + * + * Create the compressed channel and buffer. + * + * Returns 0 for success or -1 for error + * + * @p: Params for the channel that we are using + * @errp: pointer to an error + */ +static int zlib_recv_setup(MultiFDRecvParams *p, Error **errp) +{ + uint32_t page_count = MULTIFD_PACKET_SIZE / qemu_target_page_size(); + struct zlib_data *z = g_malloc0(sizeof(struct zlib_data)); + z_stream *zs = &z->zs; + + p->data = z; + zs->zalloc = Z_NULL; + zs->zfree = Z_NULL; + zs->opaque = Z_NULL; + zs->avail_in = 0; + zs->next_in = Z_NULL; + if (inflateInit(zs) != Z_OK) { + error_setg(errp, "multifd %d: inflate init failed", p->id); + return -1; + } + /* We will never have more than page_count pages */ + z->zbuff_len = page_count * qemu_target_page_size(); + /* We know compression "could" use more space */ + z->zbuff_len *= 2; + z->zbuff = g_try_malloc(z->zbuff_len); + if (!z->zbuff) { + inflateEnd(zs); + error_setg(errp, "multifd %d: out of memory for zbuff", p->id); + return -1; + } + return 0; +} + +/** + * zlib_recv_cleanup: setup receive side + * + * For no compression this function does nothing. + * + * @p: Params for the channel that we are using + */ +static void zlib_recv_cleanup(MultiFDRecvParams *p) +{ + struct zlib_data *z = p->data; + + inflateEnd(&z->zs); + g_free(z->zbuff); + z->zbuff = NULL; + g_free(p->data); + p->data = NULL; +} + +/** + * zlib_recv_pages: read the data from the channel into actual pages + * + * Read the compressed buffer, and uncompress it into the actual + * pages. + * + * Returns 0 for success or -1 for error + * + * @p: Params for the channel that we are using + * @used: number of pages used + * @errp: pointer to an error + */ +static int zlib_recv_pages(MultiFDRecvParams *p, uint32_t used, Error **errp) +{ + struct zlib_data *z = p->data; + z_stream *zs = &z->zs; + uint32_t in_size = p->next_packet_size; + /* we measure the change of total_out */ + uint32_t out_size = zs->total_out; + uint32_t expected_size = used * qemu_target_page_size(); + uint32_t flags = p->flags & MULTIFD_FLAG_COMPRESSION_MASK; + int ret; + int i; + + if (flags != MULTIFD_FLAG_ZLIB) { + error_setg(errp, "multifd %d: flags received %x flags expected %x", + p->id, flags, MULTIFD_FLAG_ZLIB); + return -1; + } + ret = qio_channel_read_all(p->c, (void *)z->zbuff, in_size, errp); + + if (ret != 0) { + return ret; + } + + zs->avail_in = in_size; + zs->next_in = z->zbuff; + + for (i = 0; i < used; i++) { + struct iovec *iov = &p->pages->iov[i]; + int flush = Z_NO_FLUSH; + unsigned long start = zs->total_out; + + if (i == used - 1) { + flush = Z_SYNC_FLUSH; + } + + zs->avail_out = iov->iov_len; + zs->next_out = iov->iov_base; + + /* + * Welcome to inflate semantics + * + * We need to loop while: + * - return is Z_OK + * - there are input available + * - we haven't completed a full page + */ + do { + ret = inflate(zs, flush); + } while (ret == Z_OK && zs->avail_in + && (zs->total_out - start) < iov->iov_len); + if (ret == Z_OK && (zs->total_out - start) < iov->iov_len) { + error_setg(errp, "multifd %d: inflate generated too few output", + p->id); + return -1; + } + if (ret != Z_OK) { + error_setg(errp, "multifd %d: inflate returned %d instead of Z_OK", + p->id, ret); + return -1; + } + } + out_size = zs->total_out - out_size; + if (out_size != expected_size) { + error_setg(errp, "multifd %d: packet size received %d size expected %d", + p->id, out_size, expected_size); + return -1; + } + return 0; +} + +static MultiFDMethods multifd_zlib_ops = { + .send_setup = zlib_send_setup, + .send_cleanup = zlib_send_cleanup, + .send_prepare = zlib_send_prepare, + .send_write = zlib_send_write, + .recv_setup = zlib_recv_setup, + .recv_cleanup = zlib_recv_cleanup, + .recv_pages = zlib_recv_pages +}; + +static void multifd_zlib_register(void) +{ + multifd_register_ops(MULTIFD_COMPRESSION_ZLIB, &multifd_zlib_ops); +} + +migration_init(multifd_zlib_register); diff --git a/migration/multifd-zstd.c b/migration/multifd-zstd.c new file mode 100644 index 0000000000..693bddf8c9 --- /dev/null +++ b/migration/multifd-zstd.c @@ -0,0 +1,339 @@ +/* + * Multifd zlib compression implementation + * + * Copyright (c) 2020 Red Hat Inc + * + * Authors: + * Juan Quintela + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include +#include "qemu/rcu.h" +#include "exec/target_page.h" +#include "qapi/error.h" +#include "migration.h" +#include "trace.h" +#include "multifd.h" + +struct zstd_data { + /* stream for compression */ + ZSTD_CStream *zcs; + /* stream for decompression */ + ZSTD_DStream *zds; + /* buffers */ + ZSTD_inBuffer in; + ZSTD_outBuffer out; + /* compressed buffer */ + uint8_t *zbuff; + /* size of compressed buffer */ + uint32_t zbuff_len; +}; + +/* Multifd zstd compression */ + +/** + * zstd_send_setup: setup send side + * + * Setup each channel with zstd compression. + * + * Returns 0 for success or -1 for error + * + * @p: Params for the channel that we are using + * @errp: pointer to an error + */ +static int zstd_send_setup(MultiFDSendParams *p, Error **errp) +{ + uint32_t page_count = MULTIFD_PACKET_SIZE / qemu_target_page_size(); + struct zstd_data *z = g_new0(struct zstd_data, 1); + int res; + + p->data = z; + z->zcs = ZSTD_createCStream(); + if (!z->zcs) { + g_free(z); + error_setg(errp, "multifd %d: zstd createCStream failed", p->id); + return -1; + } + + res = ZSTD_initCStream(z->zcs, migrate_multifd_zstd_level()); + if (ZSTD_isError(res)) { + ZSTD_freeCStream(z->zcs); + g_free(z); + error_setg(errp, "multifd %d: initCStream failed with error %s", + p->id, ZSTD_getErrorName(res)); + return -1; + } + /* We will never have more than page_count pages */ + z->zbuff_len = page_count * qemu_target_page_size(); + z->zbuff_len *= 2; + z->zbuff = g_try_malloc(z->zbuff_len); + if (!z->zbuff) { + ZSTD_freeCStream(z->zcs); + g_free(z); + error_setg(errp, "multifd %d: out of memory for zbuff", p->id); + return -1; + } + return 0; +} + +/** + * zstd_send_cleanup: cleanup send side + * + * Close the channel and return memory. + * + * @p: Params for the channel that we are using + */ +static void zstd_send_cleanup(MultiFDSendParams *p, Error **errp) +{ + struct zstd_data *z = p->data; + + ZSTD_freeCStream(z->zcs); + z->zcs = NULL; + g_free(z->zbuff); + z->zbuff = NULL; + g_free(p->data); + p->data = NULL; +} + +/** + * zstd_send_prepare: prepare date to be able to send + * + * Create a compressed buffer with all the pages that we are going to + * send. + * + * Returns 0 for success or -1 for error + * + * @p: Params for the channel that we are using + * @used: number of pages used + */ +static int zstd_send_prepare(MultiFDSendParams *p, uint32_t used, Error **errp) +{ + struct iovec *iov = p->pages->iov; + struct zstd_data *z = p->data; + int ret; + uint32_t i; + + z->out.dst = z->zbuff; + z->out.size = z->zbuff_len; + z->out.pos = 0; + + for (i = 0; i < used; i++) { + ZSTD_EndDirective flush = ZSTD_e_continue; + + if (i == used - 1) { + flush = ZSTD_e_flush; + } + z->in.src = iov[i].iov_base; + z->in.size = iov[i].iov_len; + z->in.pos = 0; + + /* + * Welcome to compressStream2 semantics + * + * We need to loop while: + * - return is > 0 + * - there is input available + * - there is output space free + */ + do { + ret = ZSTD_compressStream2(z->zcs, &z->out, &z->in, flush); + } while (ret > 0 && (z->in.size - z->in.pos > 0) + && (z->out.size - z->out.pos > 0)); + if (ret > 0 && (z->in.size - z->in.pos > 0)) { + error_setg(errp, "multifd %d: compressStream buffer too small", + p->id); + return -1; + } + if (ZSTD_isError(ret)) { + error_setg(errp, "multifd %d: compressStream error %s", + p->id, ZSTD_getErrorName(ret)); + return -1; + } + } + p->next_packet_size = z->out.pos; + p->flags |= MULTIFD_FLAG_ZSTD; + + return 0; +} + +/** + * zstd_send_write: do the actual write of the data + * + * Do the actual write of the comprresed buffer. + * + * Returns 0 for success or -1 for error + * + * @p: Params for the channel that we are using + * @used: number of pages used + * @errp: pointer to an error + */ +static int zstd_send_write(MultiFDSendParams *p, uint32_t used, Error **errp) +{ + struct zstd_data *z = p->data; + + return qio_channel_write_all(p->c, (void *)z->zbuff, p->next_packet_size, + errp); +} + +/** + * zstd_recv_setup: setup receive side + * + * Create the compressed channel and buffer. + * + * Returns 0 for success or -1 for error + * + * @p: Params for the channel that we are using + * @errp: pointer to an error + */ +static int zstd_recv_setup(MultiFDRecvParams *p, Error **errp) +{ + uint32_t page_count = MULTIFD_PACKET_SIZE / qemu_target_page_size(); + struct zstd_data *z = g_new0(struct zstd_data, 1); + int ret; + + p->data = z; + z->zds = ZSTD_createDStream(); + if (!z->zds) { + g_free(z); + error_setg(errp, "multifd %d: zstd createDStream failed", p->id); + return -1; + } + + ret = ZSTD_initDStream(z->zds); + if (ZSTD_isError(ret)) { + ZSTD_freeDStream(z->zds); + g_free(z); + error_setg(errp, "multifd %d: initDStream failed with error %s", + p->id, ZSTD_getErrorName(ret)); + return -1; + } + + /* We will never have more than page_count pages */ + z->zbuff_len = page_count * qemu_target_page_size(); + /* We know compression "could" use more space */ + z->zbuff_len *= 2; + z->zbuff = g_try_malloc(z->zbuff_len); + if (!z->zbuff) { + ZSTD_freeDStream(z->zds); + g_free(z); + error_setg(errp, "multifd %d: out of memory for zbuff", p->id); + return -1; + } + return 0; +} + +/** + * zstd_recv_cleanup: setup receive side + * + * For no compression this function does nothing. + * + * @p: Params for the channel that we are using + */ +static void zstd_recv_cleanup(MultiFDRecvParams *p) +{ + struct zstd_data *z = p->data; + + ZSTD_freeDStream(z->zds); + z->zds = NULL; + g_free(z->zbuff); + z->zbuff = NULL; + g_free(p->data); + p->data = NULL; +} + +/** + * zstd_recv_pages: read the data from the channel into actual pages + * + * Read the compressed buffer, and uncompress it into the actual + * pages. + * + * Returns 0 for success or -1 for error + * + * @p: Params for the channel that we are using + * @used: number of pages used + * @errp: pointer to an error + */ +static int zstd_recv_pages(MultiFDRecvParams *p, uint32_t used, Error **errp) +{ + uint32_t in_size = p->next_packet_size; + uint32_t out_size = 0; + uint32_t expected_size = used * qemu_target_page_size(); + uint32_t flags = p->flags & MULTIFD_FLAG_COMPRESSION_MASK; + struct zstd_data *z = p->data; + int ret; + int i; + + if (flags != MULTIFD_FLAG_ZSTD) { + error_setg(errp, "multifd %d: flags received %x flags expected %x", + p->id, flags, MULTIFD_FLAG_ZSTD); + return -1; + } + ret = qio_channel_read_all(p->c, (void *)z->zbuff, in_size, errp); + + if (ret != 0) { + return ret; + } + + z->in.src = z->zbuff; + z->in.size = in_size; + z->in.pos = 0; + + for (i = 0; i < used; i++) { + struct iovec *iov = &p->pages->iov[i]; + + z->out.dst = iov->iov_base; + z->out.size = iov->iov_len; + z->out.pos = 0; + + /* + * Welcome to decompressStream semantics + * + * We need to loop while: + * - return is > 0 + * - there is input available + * - we haven't put out a full page + */ + do { + ret = ZSTD_decompressStream(z->zds, &z->out, &z->in); + } while (ret > 0 && (z->in.size - z->in.pos > 0) + && (z->out.pos < iov->iov_len)); + if (ret > 0 && (z->out.pos < iov->iov_len)) { + error_setg(errp, "multifd %d: decompressStream buffer too small", + p->id); + return -1; + } + if (ZSTD_isError(ret)) { + error_setg(errp, "multifd %d: decompressStream returned %s", + p->id, ZSTD_getErrorName(ret)); + return ret; + } + out_size += z->out.pos; + } + if (out_size != expected_size) { + error_setg(errp, "multifd %d: packet size received %d size expected %d", + p->id, out_size, expected_size); + return -1; + } + return 0; +} + +static MultiFDMethods multifd_zstd_ops = { + .send_setup = zstd_send_setup, + .send_cleanup = zstd_send_cleanup, + .send_prepare = zstd_send_prepare, + .send_write = zstd_send_write, + .recv_setup = zstd_recv_setup, + .recv_cleanup = zstd_recv_cleanup, + .recv_pages = zstd_recv_pages +}; + +static void multifd_zstd_register(void) +{ + multifd_register_ops(MULTIFD_COMPRESSION_ZSTD, &multifd_zstd_ops); +} + +migration_init(multifd_zstd_register); diff --git a/migration/multifd.c b/migration/multifd.c new file mode 100644 index 0000000000..cb6a4a3ab8 --- /dev/null +++ b/migration/multifd.c @@ -0,0 +1,1074 @@ +/* + * Multifd common code + * + * Copyright (c) 2019-2020 Red Hat Inc + * + * Authors: + * Juan Quintela + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qemu/rcu.h" +#include "exec/target_page.h" +#include "sysemu/sysemu.h" +#include "exec/ramblock.h" +#include "qemu/error-report.h" +#include "qapi/error.h" +#include "ram.h" +#include "migration.h" +#include "socket.h" +#include "qemu-file.h" +#include "trace.h" +#include "multifd.h" + +/* Multiple fd's */ + +#define MULTIFD_MAGIC 0x11223344U +#define MULTIFD_VERSION 1 + +typedef struct { + uint32_t magic; + uint32_t version; + unsigned char uuid[16]; /* QemuUUID */ + uint8_t id; + uint8_t unused1[7]; /* Reserved for future use */ + uint64_t unused2[4]; /* Reserved for future use */ +} __attribute__((packed)) MultiFDInit_t; + +/* Multifd without compression */ + +/** + * nocomp_send_setup: setup send side + * + * For no compression this function does nothing. + * + * Returns 0 for success or -1 for error + * + * @p: Params for the channel that we are using + * @errp: pointer to an error + */ +static int nocomp_send_setup(MultiFDSendParams *p, Error **errp) +{ + return 0; +} + +/** + * nocomp_send_cleanup: cleanup send side + * + * For no compression this function does nothing. + * + * @p: Params for the channel that we are using + */ +static void nocomp_send_cleanup(MultiFDSendParams *p, Error **errp) +{ + return; +} + +/** + * nocomp_send_prepare: prepare date to be able to send + * + * For no compression we just have to calculate the size of the + * packet. + * + * Returns 0 for success or -1 for error + * + * @p: Params for the channel that we are using + * @used: number of pages used + * @errp: pointer to an error + */ +static int nocomp_send_prepare(MultiFDSendParams *p, uint32_t used, + Error **errp) +{ + p->next_packet_size = used * qemu_target_page_size(); + p->flags |= MULTIFD_FLAG_NOCOMP; + return 0; +} + +/** + * nocomp_send_write: do the actual write of the data + * + * For no compression we just have to write the data. + * + * Returns 0 for success or -1 for error + * + * @p: Params for the channel that we are using + * @used: number of pages used + * @errp: pointer to an error + */ +static int nocomp_send_write(MultiFDSendParams *p, uint32_t used, Error **errp) +{ + return qio_channel_writev_all(p->c, p->pages->iov, used, errp); +} + +/** + * nocomp_recv_setup: setup receive side + * + * For no compression this function does nothing. + * + * Returns 0 for success or -1 for error + * + * @p: Params for the channel that we are using + * @errp: pointer to an error + */ +static int nocomp_recv_setup(MultiFDRecvParams *p, Error **errp) +{ + return 0; +} + +/** + * nocomp_recv_cleanup: setup receive side + * + * For no compression this function does nothing. + * + * @p: Params for the channel that we are using + */ +static void nocomp_recv_cleanup(MultiFDRecvParams *p) +{ +} + +/** + * nocomp_recv_pages: read the data from the channel into actual pages + * + * For no compression we just need to read things into the correct place. + * + * Returns 0 for success or -1 for error + * + * @p: Params for the channel that we are using + * @used: number of pages used + * @errp: pointer to an error + */ +static int nocomp_recv_pages(MultiFDRecvParams *p, uint32_t used, Error **errp) +{ + uint32_t flags = p->flags & MULTIFD_FLAG_COMPRESSION_MASK; + + if (flags != MULTIFD_FLAG_NOCOMP) { + error_setg(errp, "multifd %d: flags received %x flags expected %x", + p->id, flags, MULTIFD_FLAG_NOCOMP); + return -1; + } + return qio_channel_readv_all(p->c, p->pages->iov, used, errp); +} + +static MultiFDMethods multifd_nocomp_ops = { + .send_setup = nocomp_send_setup, + .send_cleanup = nocomp_send_cleanup, + .send_prepare = nocomp_send_prepare, + .send_write = nocomp_send_write, + .recv_setup = nocomp_recv_setup, + .recv_cleanup = nocomp_recv_cleanup, + .recv_pages = nocomp_recv_pages +}; + +static MultiFDMethods *multifd_ops[MULTIFD_COMPRESSION__MAX] = { + [MULTIFD_COMPRESSION_NONE] = &multifd_nocomp_ops, +}; + +void multifd_register_ops(int method, MultiFDMethods *ops) +{ + assert(0 < method && method < MULTIFD_COMPRESSION__MAX); + multifd_ops[method] = ops; +} + +static int multifd_send_initial_packet(MultiFDSendParams *p, Error **errp) +{ + MultiFDInit_t msg = {}; + int ret; + + msg.magic = cpu_to_be32(MULTIFD_MAGIC); + msg.version = cpu_to_be32(MULTIFD_VERSION); + msg.id = p->id; + memcpy(msg.uuid, &qemu_uuid.data, sizeof(msg.uuid)); + + ret = qio_channel_write_all(p->c, (char *)&msg, sizeof(msg), errp); + if (ret != 0) { + return -1; + } + return 0; +} + +static int multifd_recv_initial_packet(QIOChannel *c, Error **errp) +{ + MultiFDInit_t msg; + int ret; + + ret = qio_channel_read_all(c, (char *)&msg, sizeof(msg), errp); + if (ret != 0) { + return -1; + } + + msg.magic = be32_to_cpu(msg.magic); + msg.version = be32_to_cpu(msg.version); + + if (msg.magic != MULTIFD_MAGIC) { + error_setg(errp, "multifd: received packet magic %x " + "expected %x", msg.magic, MULTIFD_MAGIC); + return -1; + } + + if (msg.version != MULTIFD_VERSION) { + error_setg(errp, "multifd: received packet version %d " + "expected %d", msg.version, MULTIFD_VERSION); + return -1; + } + + if (memcmp(msg.uuid, &qemu_uuid, sizeof(qemu_uuid))) { + char *uuid = qemu_uuid_unparse_strdup(&qemu_uuid); + char *msg_uuid = qemu_uuid_unparse_strdup((const QemuUUID *)msg.uuid); + + error_setg(errp, "multifd: received uuid '%s' and expected " + "uuid '%s' for channel %hhd", msg_uuid, uuid, msg.id); + g_free(uuid); + g_free(msg_uuid); + return -1; + } + + if (msg.id > migrate_multifd_channels()) { + error_setg(errp, "multifd: received channel version %d " + "expected %d", msg.version, MULTIFD_VERSION); + return -1; + } + + return msg.id; +} + +static MultiFDPages_t *multifd_pages_init(size_t size) +{ + MultiFDPages_t *pages = g_new0(MultiFDPages_t, 1); + + pages->allocated = size; + pages->iov = g_new0(struct iovec, size); + pages->offset = g_new0(ram_addr_t, size); + + return pages; +} + +static void multifd_pages_clear(MultiFDPages_t *pages) +{ + pages->used = 0; + pages->allocated = 0; + pages->packet_num = 0; + pages->block = NULL; + g_free(pages->iov); + pages->iov = NULL; + g_free(pages->offset); + pages->offset = NULL; + g_free(pages); +} + +static void multifd_send_fill_packet(MultiFDSendParams *p) +{ + MultiFDPacket_t *packet = p->packet; + int i; + + packet->flags = cpu_to_be32(p->flags); + packet->pages_alloc = cpu_to_be32(p->pages->allocated); + packet->pages_used = cpu_to_be32(p->pages->used); + packet->next_packet_size = cpu_to_be32(p->next_packet_size); + packet->packet_num = cpu_to_be64(p->packet_num); + + if (p->pages->block) { + strncpy(packet->ramblock, p->pages->block->idstr, 256); + } + + for (i = 0; i < p->pages->used; i++) { + /* there are architectures where ram_addr_t is 32 bit */ + uint64_t temp = p->pages->offset[i]; + + packet->offset[i] = cpu_to_be64(temp); + } +} + +static int multifd_recv_unfill_packet(MultiFDRecvParams *p, Error **errp) +{ + MultiFDPacket_t *packet = p->packet; + uint32_t pages_max = MULTIFD_PACKET_SIZE / qemu_target_page_size(); + RAMBlock *block; + int i; + + packet->magic = be32_to_cpu(packet->magic); + if (packet->magic != MULTIFD_MAGIC) { + error_setg(errp, "multifd: received packet " + "magic %x and expected magic %x", + packet->magic, MULTIFD_MAGIC); + return -1; + } + + packet->version = be32_to_cpu(packet->version); + if (packet->version != MULTIFD_VERSION) { + error_setg(errp, "multifd: received packet " + "version %d and expected version %d", + packet->version, MULTIFD_VERSION); + return -1; + } + + p->flags = be32_to_cpu(packet->flags); + + packet->pages_alloc = be32_to_cpu(packet->pages_alloc); + /* + * If we received a packet that is 100 times bigger than expected + * just stop migration. It is a magic number. + */ + if (packet->pages_alloc > pages_max * 100) { + error_setg(errp, "multifd: received packet " + "with size %d and expected a maximum size of %d", + packet->pages_alloc, pages_max * 100) ; + return -1; + } + /* + * We received a packet that is bigger than expected but inside + * reasonable limits (see previous comment). Just reallocate. + */ + if (packet->pages_alloc > p->pages->allocated) { + multifd_pages_clear(p->pages); + p->pages = multifd_pages_init(packet->pages_alloc); + } + + p->pages->used = be32_to_cpu(packet->pages_used); + if (p->pages->used > packet->pages_alloc) { + error_setg(errp, "multifd: received packet " + "with %d pages and expected maximum pages are %d", + p->pages->used, packet->pages_alloc) ; + return -1; + } + + p->next_packet_size = be32_to_cpu(packet->next_packet_size); + p->packet_num = be64_to_cpu(packet->packet_num); + + if (p->pages->used == 0) { + return 0; + } + + /* make sure that ramblock is 0 terminated */ + packet->ramblock[255] = 0; + block = qemu_ram_block_by_name(packet->ramblock); + if (!block) { + error_setg(errp, "multifd: unknown ram block %s", + packet->ramblock); + return -1; + } + + for (i = 0; i < p->pages->used; i++) { + uint64_t offset = be64_to_cpu(packet->offset[i]); + + if (offset > (block->used_length - qemu_target_page_size())) { + error_setg(errp, "multifd: offset too long %" PRIu64 + " (max " RAM_ADDR_FMT ")", + offset, block->max_length); + return -1; + } + p->pages->iov[i].iov_base = block->host + offset; + p->pages->iov[i].iov_len = qemu_target_page_size(); + } + + return 0; +} + +struct { + MultiFDSendParams *params; + /* array of pages to sent */ + MultiFDPages_t *pages; + /* global number of generated multifd packets */ + uint64_t packet_num; + /* send channels ready */ + QemuSemaphore channels_ready; + /* + * Have we already run terminate threads. There is a race when it + * happens that we got one error while we are exiting. + * We will use atomic operations. Only valid values are 0 and 1. + */ + int exiting; + /* multifd ops */ + MultiFDMethods *ops; +} *multifd_send_state; + +/* + * How we use multifd_send_state->pages and channel->pages? + * + * We create a pages for each channel, and a main one. Each time that + * we need to send a batch of pages we interchange the ones between + * multifd_send_state and the channel that is sending it. There are + * two reasons for that: + * - to not have to do so many mallocs during migration + * - to make easier to know what to free at the end of migration + * + * This way we always know who is the owner of each "pages" struct, + * and we don't need any locking. It belongs to the migration thread + * or to the channel thread. Switching is safe because the migration + * thread is using the channel mutex when changing it, and the channel + * have to had finish with its own, otherwise pending_job can't be + * false. + */ + +static int multifd_send_pages(QEMUFile *f) +{ + int i; + static int next_channel; + MultiFDSendParams *p = NULL; /* make happy gcc */ + MultiFDPages_t *pages = multifd_send_state->pages; + uint64_t transferred; + + if (atomic_read(&multifd_send_state->exiting)) { + return -1; + } + + qemu_sem_wait(&multifd_send_state->channels_ready); + for (i = next_channel;; i = (i + 1) % migrate_multifd_channels()) { + p = &multifd_send_state->params[i]; + + qemu_mutex_lock(&p->mutex); + if (p->quit) { + error_report("%s: channel %d has already quit!", __func__, i); + qemu_mutex_unlock(&p->mutex); + return -1; + } + if (!p->pending_job) { + p->pending_job++; + next_channel = (i + 1) % migrate_multifd_channels(); + break; + } + qemu_mutex_unlock(&p->mutex); + } + assert(!p->pages->used); + assert(!p->pages->block); + + p->packet_num = multifd_send_state->packet_num++; + multifd_send_state->pages = p->pages; + p->pages = pages; + transferred = ((uint64_t) pages->used) * qemu_target_page_size() + + p->packet_len; + qemu_file_update_transfer(f, transferred); + ram_counters.multifd_bytes += transferred; + ram_counters.transferred += transferred;; + qemu_mutex_unlock(&p->mutex); + qemu_sem_post(&p->sem); + + return 1; +} + +int multifd_queue_page(QEMUFile *f, RAMBlock *block, ram_addr_t offset) +{ + MultiFDPages_t *pages = multifd_send_state->pages; + + if (!pages->block) { + pages->block = block; + } + + if (pages->block == block) { + pages->offset[pages->used] = offset; + pages->iov[pages->used].iov_base = block->host + offset; + pages->iov[pages->used].iov_len = qemu_target_page_size(); + pages->used++; + + if (pages->used < pages->allocated) { + return 1; + } + } + + if (multifd_send_pages(f) < 0) { + return -1; + } + + if (pages->block != block) { + return multifd_queue_page(f, block, offset); + } + + return 1; +} + +static void multifd_send_terminate_threads(Error *err) +{ + int i; + + trace_multifd_send_terminate_threads(err != NULL); + + if (err) { + MigrationState *s = migrate_get_current(); + migrate_set_error(s, err); + if (s->state == MIGRATION_STATUS_SETUP || + s->state == MIGRATION_STATUS_PRE_SWITCHOVER || + s->state == MIGRATION_STATUS_DEVICE || + s->state == MIGRATION_STATUS_ACTIVE) { + migrate_set_state(&s->state, s->state, + MIGRATION_STATUS_FAILED); + } + } + + /* + * We don't want to exit each threads twice. Depending on where + * we get the error, or if there are two independent errors in two + * threads at the same time, we can end calling this function + * twice. + */ + if (atomic_xchg(&multifd_send_state->exiting, 1)) { + return; + } + + for (i = 0; i < migrate_multifd_channels(); i++) { + MultiFDSendParams *p = &multifd_send_state->params[i]; + + qemu_mutex_lock(&p->mutex); + p->quit = true; + qemu_sem_post(&p->sem); + qemu_mutex_unlock(&p->mutex); + } +} + +void multifd_save_cleanup(void) +{ + int i; + + if (!migrate_use_multifd()) { + return; + } + multifd_send_terminate_threads(NULL); + for (i = 0; i < migrate_multifd_channels(); i++) { + MultiFDSendParams *p = &multifd_send_state->params[i]; + + if (p->running) { + qemu_thread_join(&p->thread); + } + } + for (i = 0; i < migrate_multifd_channels(); i++) { + MultiFDSendParams *p = &multifd_send_state->params[i]; + Error *local_err = NULL; + + socket_send_channel_destroy(p->c); + p->c = NULL; + qemu_mutex_destroy(&p->mutex); + qemu_sem_destroy(&p->sem); + qemu_sem_destroy(&p->sem_sync); + g_free(p->name); + p->name = NULL; + multifd_pages_clear(p->pages); + p->pages = NULL; + p->packet_len = 0; + g_free(p->packet); + p->packet = NULL; + multifd_send_state->ops->send_cleanup(p, &local_err); + if (local_err) { + migrate_set_error(migrate_get_current(), local_err); + } + } + qemu_sem_destroy(&multifd_send_state->channels_ready); + g_free(multifd_send_state->params); + multifd_send_state->params = NULL; + multifd_pages_clear(multifd_send_state->pages); + multifd_send_state->pages = NULL; + g_free(multifd_send_state); + multifd_send_state = NULL; +} + +void multifd_send_sync_main(QEMUFile *f) +{ + int i; + + if (!migrate_use_multifd()) { + return; + } + if (multifd_send_state->pages->used) { + if (multifd_send_pages(f) < 0) { + error_report("%s: multifd_send_pages fail", __func__); + return; + } + } + for (i = 0; i < migrate_multifd_channels(); i++) { + MultiFDSendParams *p = &multifd_send_state->params[i]; + + trace_multifd_send_sync_main_signal(p->id); + + qemu_mutex_lock(&p->mutex); + + if (p->quit) { + error_report("%s: channel %d has already quit", __func__, i); + qemu_mutex_unlock(&p->mutex); + return; + } + + p->packet_num = multifd_send_state->packet_num++; + p->flags |= MULTIFD_FLAG_SYNC; + p->pending_job++; + qemu_file_update_transfer(f, p->packet_len); + ram_counters.multifd_bytes += p->packet_len; + ram_counters.transferred += p->packet_len; + qemu_mutex_unlock(&p->mutex); + qemu_sem_post(&p->sem); + } + for (i = 0; i < migrate_multifd_channels(); i++) { + MultiFDSendParams *p = &multifd_send_state->params[i]; + + trace_multifd_send_sync_main_wait(p->id); + qemu_sem_wait(&p->sem_sync); + } + trace_multifd_send_sync_main(multifd_send_state->packet_num); +} + +static void *multifd_send_thread(void *opaque) +{ + MultiFDSendParams *p = opaque; + Error *local_err = NULL; + int ret = 0; + uint32_t flags = 0; + + trace_multifd_send_thread_start(p->id); + rcu_register_thread(); + + if (multifd_send_initial_packet(p, &local_err) < 0) { + ret = -1; + goto out; + } + /* initial packet */ + p->num_packets = 1; + + while (true) { + qemu_sem_wait(&p->sem); + + if (atomic_read(&multifd_send_state->exiting)) { + break; + } + qemu_mutex_lock(&p->mutex); + + if (p->pending_job) { + uint32_t used = p->pages->used; + uint64_t packet_num = p->packet_num; + flags = p->flags; + + if (used) { + ret = multifd_send_state->ops->send_prepare(p, used, + &local_err); + if (ret != 0) { + qemu_mutex_unlock(&p->mutex); + break; + } + } + multifd_send_fill_packet(p); + p->flags = 0; + p->num_packets++; + p->num_pages += used; + p->pages->used = 0; + p->pages->block = NULL; + qemu_mutex_unlock(&p->mutex); + + trace_multifd_send(p->id, packet_num, used, flags, + p->next_packet_size); + + ret = qio_channel_write_all(p->c, (void *)p->packet, + p->packet_len, &local_err); + if (ret != 0) { + break; + } + + if (used) { + ret = multifd_send_state->ops->send_write(p, used, &local_err); + if (ret != 0) { + break; + } + } + + qemu_mutex_lock(&p->mutex); + p->pending_job--; + qemu_mutex_unlock(&p->mutex); + + if (flags & MULTIFD_FLAG_SYNC) { + qemu_sem_post(&p->sem_sync); + } + qemu_sem_post(&multifd_send_state->channels_ready); + } else if (p->quit) { + qemu_mutex_unlock(&p->mutex); + break; + } else { + qemu_mutex_unlock(&p->mutex); + /* sometimes there are spurious wakeups */ + } + } + +out: + if (local_err) { + trace_multifd_send_error(p->id); + multifd_send_terminate_threads(local_err); + } + + /* + * Error happen, I will exit, but I can't just leave, tell + * who pay attention to me. + */ + if (ret != 0) { + qemu_sem_post(&p->sem_sync); + qemu_sem_post(&multifd_send_state->channels_ready); + } + + qemu_mutex_lock(&p->mutex); + p->running = false; + qemu_mutex_unlock(&p->mutex); + + rcu_unregister_thread(); + trace_multifd_send_thread_end(p->id, p->num_packets, p->num_pages); + + return NULL; +} + +static void multifd_new_send_channel_async(QIOTask *task, gpointer opaque) +{ + MultiFDSendParams *p = opaque; + QIOChannel *sioc = QIO_CHANNEL(qio_task_get_source(task)); + Error *local_err = NULL; + + trace_multifd_new_send_channel_async(p->id); + if (qio_task_propagate_error(task, &local_err)) { + migrate_set_error(migrate_get_current(), local_err); + /* Error happen, we need to tell who pay attention to me */ + qemu_sem_post(&multifd_send_state->channels_ready); + qemu_sem_post(&p->sem_sync); + /* + * Although multifd_send_thread is not created, but main migration + * thread neet to judge whether it is running, so we need to mark + * its status. + */ + p->quit = true; + } else { + p->c = QIO_CHANNEL(sioc); + qio_channel_set_delay(p->c, false); + p->running = true; + qemu_thread_create(&p->thread, p->name, multifd_send_thread, p, + QEMU_THREAD_JOINABLE); + } +} + +int multifd_save_setup(Error **errp) +{ + int thread_count; + uint32_t page_count = MULTIFD_PACKET_SIZE / qemu_target_page_size(); + uint8_t i; + + if (!migrate_use_multifd()) { + return 0; + } + thread_count = migrate_multifd_channels(); + multifd_send_state = g_malloc0(sizeof(*multifd_send_state)); + multifd_send_state->params = g_new0(MultiFDSendParams, thread_count); + multifd_send_state->pages = multifd_pages_init(page_count); + qemu_sem_init(&multifd_send_state->channels_ready, 0); + atomic_set(&multifd_send_state->exiting, 0); + multifd_send_state->ops = multifd_ops[migrate_multifd_compression()]; + + for (i = 0; i < thread_count; i++) { + MultiFDSendParams *p = &multifd_send_state->params[i]; + + qemu_mutex_init(&p->mutex); + qemu_sem_init(&p->sem, 0); + qemu_sem_init(&p->sem_sync, 0); + p->quit = false; + p->pending_job = 0; + p->id = i; + p->pages = multifd_pages_init(page_count); + p->packet_len = sizeof(MultiFDPacket_t) + + sizeof(uint64_t) * page_count; + p->packet = g_malloc0(p->packet_len); + p->packet->magic = cpu_to_be32(MULTIFD_MAGIC); + p->packet->version = cpu_to_be32(MULTIFD_VERSION); + p->name = g_strdup_printf("multifdsend_%d", i); + socket_send_channel_create(multifd_new_send_channel_async, p); + } + + for (i = 0; i < thread_count; i++) { + MultiFDSendParams *p = &multifd_send_state->params[i]; + Error *local_err = NULL; + int ret; + + ret = multifd_send_state->ops->send_setup(p, &local_err); + if (ret) { + error_propagate(errp, local_err); + return ret; + } + } + return 0; +} + +struct { + MultiFDRecvParams *params; + /* number of created threads */ + int count; + /* syncs main thread and channels */ + QemuSemaphore sem_sync; + /* global number of generated multifd packets */ + uint64_t packet_num; + /* multifd ops */ + MultiFDMethods *ops; +} *multifd_recv_state; + +static void multifd_recv_terminate_threads(Error *err) +{ + int i; + + trace_multifd_recv_terminate_threads(err != NULL); + + if (err) { + MigrationState *s = migrate_get_current(); + migrate_set_error(s, err); + if (s->state == MIGRATION_STATUS_SETUP || + s->state == MIGRATION_STATUS_ACTIVE) { + migrate_set_state(&s->state, s->state, + MIGRATION_STATUS_FAILED); + } + } + + for (i = 0; i < migrate_multifd_channels(); i++) { + MultiFDRecvParams *p = &multifd_recv_state->params[i]; + + qemu_mutex_lock(&p->mutex); + p->quit = true; + /* + * We could arrive here for two reasons: + * - normal quit, i.e. everything went fine, just finished + * - error quit: We close the channels so the channel threads + * finish the qio_channel_read_all_eof() + */ + if (p->c) { + qio_channel_shutdown(p->c, QIO_CHANNEL_SHUTDOWN_BOTH, NULL); + } + qemu_mutex_unlock(&p->mutex); + } +} + +int multifd_load_cleanup(Error **errp) +{ + int i; + + if (!migrate_use_multifd()) { + return 0; + } + multifd_recv_terminate_threads(NULL); + for (i = 0; i < migrate_multifd_channels(); i++) { + MultiFDRecvParams *p = &multifd_recv_state->params[i]; + + if (p->running) { + p->quit = true; + /* + * multifd_recv_thread may hung at MULTIFD_FLAG_SYNC handle code, + * however try to wakeup it without harm in cleanup phase. + */ + qemu_sem_post(&p->sem_sync); + qemu_thread_join(&p->thread); + } + } + for (i = 0; i < migrate_multifd_channels(); i++) { + MultiFDRecvParams *p = &multifd_recv_state->params[i]; + + object_unref(OBJECT(p->c)); + p->c = NULL; + qemu_mutex_destroy(&p->mutex); + qemu_sem_destroy(&p->sem_sync); + g_free(p->name); + p->name = NULL; + multifd_pages_clear(p->pages); + p->pages = NULL; + p->packet_len = 0; + g_free(p->packet); + p->packet = NULL; + multifd_recv_state->ops->recv_cleanup(p); + } + qemu_sem_destroy(&multifd_recv_state->sem_sync); + g_free(multifd_recv_state->params); + multifd_recv_state->params = NULL; + g_free(multifd_recv_state); + multifd_recv_state = NULL; + + return 0; +} + +void multifd_recv_sync_main(void) +{ + int i; + + if (!migrate_use_multifd()) { + return; + } + for (i = 0; i < migrate_multifd_channels(); i++) { + MultiFDRecvParams *p = &multifd_recv_state->params[i]; + + trace_multifd_recv_sync_main_wait(p->id); + qemu_sem_wait(&multifd_recv_state->sem_sync); + } + for (i = 0; i < migrate_multifd_channels(); i++) { + MultiFDRecvParams *p = &multifd_recv_state->params[i]; + + qemu_mutex_lock(&p->mutex); + if (multifd_recv_state->packet_num < p->packet_num) { + multifd_recv_state->packet_num = p->packet_num; + } + qemu_mutex_unlock(&p->mutex); + trace_multifd_recv_sync_main_signal(p->id); + qemu_sem_post(&p->sem_sync); + } + trace_multifd_recv_sync_main(multifd_recv_state->packet_num); +} + +static void *multifd_recv_thread(void *opaque) +{ + MultiFDRecvParams *p = opaque; + Error *local_err = NULL; + int ret; + + trace_multifd_recv_thread_start(p->id); + rcu_register_thread(); + + while (true) { + uint32_t used; + uint32_t flags; + + if (p->quit) { + break; + } + + ret = qio_channel_read_all_eof(p->c, (void *)p->packet, + p->packet_len, &local_err); + if (ret == 0) { /* EOF */ + break; + } + if (ret == -1) { /* Error */ + break; + } + + qemu_mutex_lock(&p->mutex); + ret = multifd_recv_unfill_packet(p, &local_err); + if (ret) { + qemu_mutex_unlock(&p->mutex); + break; + } + + used = p->pages->used; + flags = p->flags; + /* recv methods don't know how to handle the SYNC flag */ + p->flags &= ~MULTIFD_FLAG_SYNC; + trace_multifd_recv(p->id, p->packet_num, used, flags, + p->next_packet_size); + p->num_packets++; + p->num_pages += used; + qemu_mutex_unlock(&p->mutex); + + if (used) { + ret = multifd_recv_state->ops->recv_pages(p, used, &local_err); + if (ret != 0) { + break; + } + } + + if (flags & MULTIFD_FLAG_SYNC) { + qemu_sem_post(&multifd_recv_state->sem_sync); + qemu_sem_wait(&p->sem_sync); + } + } + + if (local_err) { + multifd_recv_terminate_threads(local_err); + } + qemu_mutex_lock(&p->mutex); + p->running = false; + qemu_mutex_unlock(&p->mutex); + + rcu_unregister_thread(); + trace_multifd_recv_thread_end(p->id, p->num_packets, p->num_pages); + + return NULL; +} + +int multifd_load_setup(Error **errp) +{ + int thread_count; + uint32_t page_count = MULTIFD_PACKET_SIZE / qemu_target_page_size(); + uint8_t i; + + if (!migrate_use_multifd()) { + return 0; + } + thread_count = migrate_multifd_channels(); + multifd_recv_state = g_malloc0(sizeof(*multifd_recv_state)); + multifd_recv_state->params = g_new0(MultiFDRecvParams, thread_count); + atomic_set(&multifd_recv_state->count, 0); + qemu_sem_init(&multifd_recv_state->sem_sync, 0); + multifd_recv_state->ops = multifd_ops[migrate_multifd_compression()]; + + for (i = 0; i < thread_count; i++) { + MultiFDRecvParams *p = &multifd_recv_state->params[i]; + + qemu_mutex_init(&p->mutex); + qemu_sem_init(&p->sem_sync, 0); + p->quit = false; + p->id = i; + p->pages = multifd_pages_init(page_count); + p->packet_len = sizeof(MultiFDPacket_t) + + sizeof(uint64_t) * page_count; + p->packet = g_malloc0(p->packet_len); + p->name = g_strdup_printf("multifdrecv_%d", i); + } + + for (i = 0; i < thread_count; i++) { + MultiFDRecvParams *p = &multifd_recv_state->params[i]; + Error *local_err = NULL; + int ret; + + ret = multifd_recv_state->ops->recv_setup(p, &local_err); + if (ret) { + error_propagate(errp, local_err); + return ret; + } + } + return 0; +} + +bool multifd_recv_all_channels_created(void) +{ + int thread_count = migrate_multifd_channels(); + + if (!migrate_use_multifd()) { + return true; + } + + return thread_count == atomic_read(&multifd_recv_state->count); +} + +/* + * Try to receive all multifd channels to get ready for the migration. + * - Return true and do not set @errp when correctly receving all channels; + * - Return false and do not set @errp when correctly receiving the current one; + * - Return false and set @errp when failing to receive the current channel. + */ +bool multifd_recv_new_channel(QIOChannel *ioc, Error **errp) +{ + MultiFDRecvParams *p; + Error *local_err = NULL; + int id; + + id = multifd_recv_initial_packet(ioc, &local_err); + if (id < 0) { + multifd_recv_terminate_threads(local_err); + error_propagate_prepend(errp, local_err, + "failed to receive packet" + " via multifd channel %d: ", + atomic_read(&multifd_recv_state->count)); + return false; + } + trace_multifd_recv_new_channel(id); + + p = &multifd_recv_state->params[id]; + if (p->c != NULL) { + error_setg(&local_err, "multifd: received id '%d' already setup'", + id); + multifd_recv_terminate_threads(local_err); + error_propagate(errp, local_err); + return false; + } + p->c = ioc; + object_ref(OBJECT(ioc)); + /* initial packet */ + p->num_packets = 1; + + p->running = true; + qemu_thread_create(&p->thread, p->name, multifd_recv_thread, p, + QEMU_THREAD_JOINABLE); + atomic_inc(&multifd_recv_state->count); + return atomic_read(&multifd_recv_state->count) == + migrate_multifd_channels(); +} diff --git a/migration/multifd.h b/migration/multifd.h new file mode 100644 index 0000000000..448a03d89a --- /dev/null +++ b/migration/multifd.h @@ -0,0 +1,170 @@ +/* + * Multifd common functions + * + * Copyright (c) 2019-2020 Red Hat Inc + * + * Authors: + * Juan Quintela + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef QEMU_MIGRATION_MULTIFD_H +#define QEMU_MIGRATION_MULTIFD_H + +int multifd_save_setup(Error **errp); +void multifd_save_cleanup(void); +int multifd_load_setup(Error **errp); +int multifd_load_cleanup(Error **errp); +bool multifd_recv_all_channels_created(void); +bool multifd_recv_new_channel(QIOChannel *ioc, Error **errp); +void multifd_recv_sync_main(void); +void multifd_send_sync_main(QEMUFile *f); +int multifd_queue_page(QEMUFile *f, RAMBlock *block, ram_addr_t offset); + +/* Multifd Compression flags */ +#define MULTIFD_FLAG_SYNC (1 << 0) + +/* We reserve 3 bits for compression methods */ +#define MULTIFD_FLAG_COMPRESSION_MASK (7 << 1) +/* we need to be compatible. Before compression value was 0 */ +#define MULTIFD_FLAG_NOCOMP (0 << 1) +#define MULTIFD_FLAG_ZLIB (1 << 1) +#define MULTIFD_FLAG_ZSTD (2 << 1) + +/* This value needs to be a multiple of qemu_target_page_size() */ +#define MULTIFD_PACKET_SIZE (512 * 1024) + +typedef struct { + uint32_t magic; + uint32_t version; + uint32_t flags; + /* maximum number of allocated pages */ + uint32_t pages_alloc; + uint32_t pages_used; + /* size of the next packet that contains pages */ + uint32_t next_packet_size; + uint64_t packet_num; + uint64_t unused[4]; /* Reserved for future use */ + char ramblock[256]; + uint64_t offset[]; +} __attribute__((packed)) MultiFDPacket_t; + +typedef struct { + /* number of used pages */ + uint32_t used; + /* number of allocated pages */ + uint32_t allocated; + /* global number of generated multifd packets */ + uint64_t packet_num; + /* offset of each page */ + ram_addr_t *offset; + /* pointer to each page */ + struct iovec *iov; + RAMBlock *block; +} MultiFDPages_t; + +typedef struct { + /* this fields are not changed once the thread is created */ + /* channel number */ + uint8_t id; + /* channel thread name */ + char *name; + /* channel thread id */ + QemuThread thread; + /* communication channel */ + QIOChannel *c; + /* sem where to wait for more work */ + QemuSemaphore sem; + /* this mutex protects the following parameters */ + QemuMutex mutex; + /* is this channel thread running */ + bool running; + /* should this thread finish */ + bool quit; + /* thread has work to do */ + int pending_job; + /* array of pages to sent */ + MultiFDPages_t *pages; + /* packet allocated len */ + uint32_t packet_len; + /* pointer to the packet */ + MultiFDPacket_t *packet; + /* multifd flags for each packet */ + uint32_t flags; + /* size of the next packet that contains pages */ + uint32_t next_packet_size; + /* global number of generated multifd packets */ + uint64_t packet_num; + /* thread local variables */ + /* packets sent through this channel */ + uint64_t num_packets; + /* pages sent through this channel */ + uint64_t num_pages; + /* syncs main thread and channels */ + QemuSemaphore sem_sync; + /* used for compression methods */ + void *data; +} MultiFDSendParams; + +typedef struct { + /* this fields are not changed once the thread is created */ + /* channel number */ + uint8_t id; + /* channel thread name */ + char *name; + /* channel thread id */ + QemuThread thread; + /* communication channel */ + QIOChannel *c; + /* this mutex protects the following parameters */ + QemuMutex mutex; + /* is this channel thread running */ + bool running; + /* should this thread finish */ + bool quit; + /* array of pages to receive */ + MultiFDPages_t *pages; + /* packet allocated len */ + uint32_t packet_len; + /* pointer to the packet */ + MultiFDPacket_t *packet; + /* multifd flags for each packet */ + uint32_t flags; + /* global number of generated multifd packets */ + uint64_t packet_num; + /* thread local variables */ + /* size of the next packet that contains pages */ + uint32_t next_packet_size; + /* packets sent through this channel */ + uint64_t num_packets; + /* pages sent through this channel */ + uint64_t num_pages; + /* syncs main thread and channels */ + QemuSemaphore sem_sync; + /* used for de-compression methods */ + void *data; +} MultiFDRecvParams; + +typedef struct { + /* Setup for sending side */ + int (*send_setup)(MultiFDSendParams *p, Error **errp); + /* Cleanup for sending side */ + void (*send_cleanup)(MultiFDSendParams *p, Error **errp); + /* Prepare the send packet */ + int (*send_prepare)(MultiFDSendParams *p, uint32_t used, Error **errp); + /* Write the send packet */ + int (*send_write)(MultiFDSendParams *p, uint32_t used, Error **errp); + /* Setup for receiving side */ + int (*recv_setup)(MultiFDRecvParams *p, Error **errp); + /* Cleanup for receiving side */ + void (*recv_cleanup)(MultiFDRecvParams *p); + /* Read all pages */ + int (*recv_pages)(MultiFDRecvParams *p, uint32_t used, Error **errp); +} MultiFDMethods; + +void multifd_register_ops(int method, MultiFDMethods *ops); + +#endif + diff --git a/migration/qemu-file.c b/migration/qemu-file.c index 26fb25ddc1..1c3a358a14 100644 --- a/migration/qemu-file.c +++ b/migration/qemu-file.c @@ -53,6 +53,8 @@ struct QEMUFile { int last_error; Error *last_error_obj; + /* has the file has been shutdown */ + bool shutdown; }; /* @@ -61,10 +63,18 @@ struct QEMUFile { */ int qemu_file_shutdown(QEMUFile *f) { + int ret; + + f->shutdown = true; if (!f->ops->shut_down) { return -ENOSYS; } - return f->ops->shut_down(f->opaque, true, true, NULL); + ret = f->ops->shut_down(f->opaque, true, true, NULL); + + if (!f->last_error) { + qemu_file_set_error(f, -EIO); + } + return ret; } /* @@ -214,6 +224,9 @@ void qemu_fflush(QEMUFile *f) return; } + if (f->shutdown) { + return; + } if (f->iovcnt > 0) { expect = iov_size(f->iov, f->iovcnt); ret = f->ops->writev_buffer(f->opaque, f->iov, f->iovcnt, f->pos, @@ -328,6 +341,10 @@ static ssize_t qemu_fill_buffer(QEMUFile *f) f->buf_index = 0; f->buf_size = pending; + if (f->shutdown) { + return 0; + } + len = f->ops->get_buffer(f->opaque, f->buf + pending, f->pos, IO_BUF_SIZE - pending, &local_error); if (len > 0) { @@ -642,6 +659,9 @@ int64_t qemu_ftell(QEMUFile *f) int qemu_file_rate_limit(QEMUFile *f) { + if (f->shutdown) { + return 1; + } if (qemu_file_get_error(f)) { return 1; } @@ -744,11 +764,8 @@ static int qemu_compress_data(z_stream *stream, uint8_t *dest, size_t dest_len, /* Compress size bytes of data start at p and store the compressed * data to the buffer of f. * - * When f is not writable, return -1 if f has no space to save the - * compressed data. - * When f is wirtable and it has no space to save the compressed data, - * do fflush first, if f still has no space to save the compressed - * data, return -1. + * Since the file is dummy file with empty_ops, return -1 if f has no space to + * save the compressed data. */ ssize_t qemu_put_compression_data(QEMUFile *f, z_stream *stream, const uint8_t *p, size_t size) @@ -756,14 +773,7 @@ ssize_t qemu_put_compression_data(QEMUFile *f, z_stream *stream, ssize_t blen = IO_BUF_SIZE - f->buf_index - sizeof(int32_t); if (blen < compressBound(size)) { - if (!qemu_file_is_writable(f)) { - return -1; - } - qemu_fflush(f); - blen = IO_BUF_SIZE - sizeof(int32_t); - if (blen < compressBound(size)) { - return -1; - } + return -1; } blen = qemu_compress_data(stream, f->buf + f->buf_index + sizeof(int32_t), diff --git a/migration/ram.c b/migration/ram.c index 5078f94490..04f13feb2e 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -28,16 +28,13 @@ #include "qemu/osdep.h" #include "cpu.h" -#include #include "qemu/cutils.h" #include "qemu/bitops.h" #include "qemu/bitmap.h" #include "qemu/main-loop.h" -#include "qemu/pmem.h" #include "xbzrle.h" #include "ram.h" #include "migration.h" -#include "socket.h" #include "migration/register.h" #include "migration/misc.h" #include "qemu-file.h" @@ -45,6 +42,7 @@ #include "page_cache.h" #include "qemu/error-report.h" #include "qapi/error.h" +#include "qapi/qapi-types-migration.h" #include "qapi/qapi-events-migration.h" #include "qapi/qmp/qerror.h" #include "trace.h" @@ -54,9 +52,9 @@ #include "migration/colo.h" #include "block.h" #include "sysemu/sysemu.h" -#include "qemu/uuid.h" #include "savevm.h" #include "qemu/iov.h" +#include "multifd.h" /***********************************************************/ /* ram save/restore */ @@ -576,938 +574,6 @@ exit: return -1; } -/* Multiple fd's */ - -#define MULTIFD_MAGIC 0x11223344U -#define MULTIFD_VERSION 1 - -#define MULTIFD_FLAG_SYNC (1 << 0) - -/* This value needs to be a multiple of qemu_target_page_size() */ -#define MULTIFD_PACKET_SIZE (512 * 1024) - -typedef struct { - uint32_t magic; - uint32_t version; - unsigned char uuid[16]; /* QemuUUID */ - uint8_t id; - uint8_t unused1[7]; /* Reserved for future use */ - uint64_t unused2[4]; /* Reserved for future use */ -} __attribute__((packed)) MultiFDInit_t; - -typedef struct { - uint32_t magic; - uint32_t version; - uint32_t flags; - /* maximum number of allocated pages */ - uint32_t pages_alloc; - uint32_t pages_used; - /* size of the next packet that contains pages */ - uint32_t next_packet_size; - uint64_t packet_num; - uint64_t unused[4]; /* Reserved for future use */ - char ramblock[256]; - uint64_t offset[]; -} __attribute__((packed)) MultiFDPacket_t; - -typedef struct { - /* number of used pages */ - uint32_t used; - /* number of allocated pages */ - uint32_t allocated; - /* global number of generated multifd packets */ - uint64_t packet_num; - /* offset of each page */ - ram_addr_t *offset; - /* pointer to each page */ - struct iovec *iov; - RAMBlock *block; -} MultiFDPages_t; - -typedef struct { - /* this fields are not changed once the thread is created */ - /* channel number */ - uint8_t id; - /* channel thread name */ - char *name; - /* channel thread id */ - QemuThread thread; - /* communication channel */ - QIOChannel *c; - /* sem where to wait for more work */ - QemuSemaphore sem; - /* this mutex protects the following parameters */ - QemuMutex mutex; - /* is this channel thread running */ - bool running; - /* should this thread finish */ - bool quit; - /* thread has work to do */ - int pending_job; - /* array of pages to sent */ - MultiFDPages_t *pages; - /* packet allocated len */ - uint32_t packet_len; - /* pointer to the packet */ - MultiFDPacket_t *packet; - /* multifd flags for each packet */ - uint32_t flags; - /* size of the next packet that contains pages */ - uint32_t next_packet_size; - /* global number of generated multifd packets */ - uint64_t packet_num; - /* thread local variables */ - /* packets sent through this channel */ - uint64_t num_packets; - /* pages sent through this channel */ - uint64_t num_pages; - /* syncs main thread and channels */ - QemuSemaphore sem_sync; -} MultiFDSendParams; - -typedef struct { - /* this fields are not changed once the thread is created */ - /* channel number */ - uint8_t id; - /* channel thread name */ - char *name; - /* channel thread id */ - QemuThread thread; - /* communication channel */ - QIOChannel *c; - /* this mutex protects the following parameters */ - QemuMutex mutex; - /* is this channel thread running */ - bool running; - /* should this thread finish */ - bool quit; - /* array of pages to receive */ - MultiFDPages_t *pages; - /* packet allocated len */ - uint32_t packet_len; - /* pointer to the packet */ - MultiFDPacket_t *packet; - /* multifd flags for each packet */ - uint32_t flags; - /* global number of generated multifd packets */ - uint64_t packet_num; - /* thread local variables */ - /* size of the next packet that contains pages */ - uint32_t next_packet_size; - /* packets sent through this channel */ - uint64_t num_packets; - /* pages sent through this channel */ - uint64_t num_pages; - /* syncs main thread and channels */ - QemuSemaphore sem_sync; -} MultiFDRecvParams; - -static int multifd_send_initial_packet(MultiFDSendParams *p, Error **errp) -{ - MultiFDInit_t msg; - int ret; - - msg.magic = cpu_to_be32(MULTIFD_MAGIC); - msg.version = cpu_to_be32(MULTIFD_VERSION); - msg.id = p->id; - memcpy(msg.uuid, &qemu_uuid.data, sizeof(msg.uuid)); - - ret = qio_channel_write_all(p->c, (char *)&msg, sizeof(msg), errp); - if (ret != 0) { - return -1; - } - return 0; -} - -static int multifd_recv_initial_packet(QIOChannel *c, Error **errp) -{ - MultiFDInit_t msg; - int ret; - - ret = qio_channel_read_all(c, (char *)&msg, sizeof(msg), errp); - if (ret != 0) { - return -1; - } - - msg.magic = be32_to_cpu(msg.magic); - msg.version = be32_to_cpu(msg.version); - - if (msg.magic != MULTIFD_MAGIC) { - error_setg(errp, "multifd: received packet magic %x " - "expected %x", msg.magic, MULTIFD_MAGIC); - return -1; - } - - if (msg.version != MULTIFD_VERSION) { - error_setg(errp, "multifd: received packet version %d " - "expected %d", msg.version, MULTIFD_VERSION); - return -1; - } - - if (memcmp(msg.uuid, &qemu_uuid, sizeof(qemu_uuid))) { - char *uuid = qemu_uuid_unparse_strdup(&qemu_uuid); - char *msg_uuid = qemu_uuid_unparse_strdup((const QemuUUID *)msg.uuid); - - error_setg(errp, "multifd: received uuid '%s' and expected " - "uuid '%s' for channel %hhd", msg_uuid, uuid, msg.id); - g_free(uuid); - g_free(msg_uuid); - return -1; - } - - if (msg.id > migrate_multifd_channels()) { - error_setg(errp, "multifd: received channel version %d " - "expected %d", msg.version, MULTIFD_VERSION); - return -1; - } - - return msg.id; -} - -static MultiFDPages_t *multifd_pages_init(size_t size) -{ - MultiFDPages_t *pages = g_new0(MultiFDPages_t, 1); - - pages->allocated = size; - pages->iov = g_new0(struct iovec, size); - pages->offset = g_new0(ram_addr_t, size); - - return pages; -} - -static void multifd_pages_clear(MultiFDPages_t *pages) -{ - pages->used = 0; - pages->allocated = 0; - pages->packet_num = 0; - pages->block = NULL; - g_free(pages->iov); - pages->iov = NULL; - g_free(pages->offset); - pages->offset = NULL; - g_free(pages); -} - -static void multifd_send_fill_packet(MultiFDSendParams *p) -{ - MultiFDPacket_t *packet = p->packet; - int i; - - packet->flags = cpu_to_be32(p->flags); - packet->pages_alloc = cpu_to_be32(p->pages->allocated); - packet->pages_used = cpu_to_be32(p->pages->used); - packet->next_packet_size = cpu_to_be32(p->next_packet_size); - packet->packet_num = cpu_to_be64(p->packet_num); - - if (p->pages->block) { - strncpy(packet->ramblock, p->pages->block->idstr, 256); - } - - for (i = 0; i < p->pages->used; i++) { - packet->offset[i] = cpu_to_be64(p->pages->offset[i]); - } -} - -static int multifd_recv_unfill_packet(MultiFDRecvParams *p, Error **errp) -{ - MultiFDPacket_t *packet = p->packet; - uint32_t pages_max = MULTIFD_PACKET_SIZE / qemu_target_page_size(); - RAMBlock *block; - int i; - - packet->magic = be32_to_cpu(packet->magic); - if (packet->magic != MULTIFD_MAGIC) { - error_setg(errp, "multifd: received packet " - "magic %x and expected magic %x", - packet->magic, MULTIFD_MAGIC); - return -1; - } - - packet->version = be32_to_cpu(packet->version); - if (packet->version != MULTIFD_VERSION) { - error_setg(errp, "multifd: received packet " - "version %d and expected version %d", - packet->version, MULTIFD_VERSION); - return -1; - } - - p->flags = be32_to_cpu(packet->flags); - - packet->pages_alloc = be32_to_cpu(packet->pages_alloc); - /* - * If we received a packet that is 100 times bigger than expected - * just stop migration. It is a magic number. - */ - if (packet->pages_alloc > pages_max * 100) { - error_setg(errp, "multifd: received packet " - "with size %d and expected a maximum size of %d", - packet->pages_alloc, pages_max * 100) ; - return -1; - } - /* - * We received a packet that is bigger than expected but inside - * reasonable limits (see previous comment). Just reallocate. - */ - if (packet->pages_alloc > p->pages->allocated) { - multifd_pages_clear(p->pages); - p->pages = multifd_pages_init(packet->pages_alloc); - } - - p->pages->used = be32_to_cpu(packet->pages_used); - if (p->pages->used > packet->pages_alloc) { - error_setg(errp, "multifd: received packet " - "with %d pages and expected maximum pages are %d", - p->pages->used, packet->pages_alloc) ; - return -1; - } - - p->next_packet_size = be32_to_cpu(packet->next_packet_size); - p->packet_num = be64_to_cpu(packet->packet_num); - - if (p->pages->used) { - /* make sure that ramblock is 0 terminated */ - packet->ramblock[255] = 0; - block = qemu_ram_block_by_name(packet->ramblock); - if (!block) { - error_setg(errp, "multifd: unknown ram block %s", - packet->ramblock); - return -1; - } - } - - for (i = 0; i < p->pages->used; i++) { - ram_addr_t offset = be64_to_cpu(packet->offset[i]); - - if (offset > (block->used_length - TARGET_PAGE_SIZE)) { - error_setg(errp, "multifd: offset too long " RAM_ADDR_FMT - " (max " RAM_ADDR_FMT ")", - offset, block->max_length); - return -1; - } - p->pages->iov[i].iov_base = block->host + offset; - p->pages->iov[i].iov_len = TARGET_PAGE_SIZE; - } - - return 0; -} - -struct { - MultiFDSendParams *params; - /* array of pages to sent */ - MultiFDPages_t *pages; - /* global number of generated multifd packets */ - uint64_t packet_num; - /* send channels ready */ - QemuSemaphore channels_ready; -} *multifd_send_state; - -/* - * How we use multifd_send_state->pages and channel->pages? - * - * We create a pages for each channel, and a main one. Each time that - * we need to send a batch of pages we interchange the ones between - * multifd_send_state and the channel that is sending it. There are - * two reasons for that: - * - to not have to do so many mallocs during migration - * - to make easier to know what to free at the end of migration - * - * This way we always know who is the owner of each "pages" struct, - * and we don't need any locking. It belongs to the migration thread - * or to the channel thread. Switching is safe because the migration - * thread is using the channel mutex when changing it, and the channel - * have to had finish with its own, otherwise pending_job can't be - * false. - */ - -static int multifd_send_pages(RAMState *rs) -{ - int i; - static int next_channel; - MultiFDSendParams *p = NULL; /* make happy gcc */ - MultiFDPages_t *pages = multifd_send_state->pages; - uint64_t transferred; - - qemu_sem_wait(&multifd_send_state->channels_ready); - for (i = next_channel;; i = (i + 1) % migrate_multifd_channels()) { - p = &multifd_send_state->params[i]; - - qemu_mutex_lock(&p->mutex); - if (p->quit) { - error_report("%s: channel %d has already quit!", __func__, i); - qemu_mutex_unlock(&p->mutex); - return -1; - } - if (!p->pending_job) { - p->pending_job++; - next_channel = (i + 1) % migrate_multifd_channels(); - break; - } - qemu_mutex_unlock(&p->mutex); - } - p->pages->used = 0; - - p->packet_num = multifd_send_state->packet_num++; - p->pages->block = NULL; - multifd_send_state->pages = p->pages; - p->pages = pages; - transferred = ((uint64_t) pages->used) * TARGET_PAGE_SIZE + p->packet_len; - qemu_file_update_transfer(rs->f, transferred); - ram_counters.multifd_bytes += transferred; - ram_counters.transferred += transferred;; - qemu_mutex_unlock(&p->mutex); - qemu_sem_post(&p->sem); - - return 1; -} - -static int multifd_queue_page(RAMState *rs, RAMBlock *block, ram_addr_t offset) -{ - MultiFDPages_t *pages = multifd_send_state->pages; - - if (!pages->block) { - pages->block = block; - } - - if (pages->block == block) { - pages->offset[pages->used] = offset; - pages->iov[pages->used].iov_base = block->host + offset; - pages->iov[pages->used].iov_len = TARGET_PAGE_SIZE; - pages->used++; - - if (pages->used < pages->allocated) { - return 1; - } - } - - if (multifd_send_pages(rs) < 0) { - return -1; - } - - if (pages->block != block) { - return multifd_queue_page(rs, block, offset); - } - - return 1; -} - -static void multifd_send_terminate_threads(Error *err) -{ - int i; - - trace_multifd_send_terminate_threads(err != NULL); - - if (err) { - MigrationState *s = migrate_get_current(); - migrate_set_error(s, err); - if (s->state == MIGRATION_STATUS_SETUP || - s->state == MIGRATION_STATUS_PRE_SWITCHOVER || - s->state == MIGRATION_STATUS_DEVICE || - s->state == MIGRATION_STATUS_ACTIVE) { - migrate_set_state(&s->state, s->state, - MIGRATION_STATUS_FAILED); - } - } - - for (i = 0; i < migrate_multifd_channels(); i++) { - MultiFDSendParams *p = &multifd_send_state->params[i]; - - qemu_mutex_lock(&p->mutex); - p->quit = true; - qemu_sem_post(&p->sem); - qemu_mutex_unlock(&p->mutex); - } -} - -void multifd_save_cleanup(void) -{ - int i; - - if (!migrate_use_multifd()) { - return; - } - multifd_send_terminate_threads(NULL); - for (i = 0; i < migrate_multifd_channels(); i++) { - MultiFDSendParams *p = &multifd_send_state->params[i]; - - if (p->running) { - qemu_thread_join(&p->thread); - } - socket_send_channel_destroy(p->c); - p->c = NULL; - qemu_mutex_destroy(&p->mutex); - qemu_sem_destroy(&p->sem); - qemu_sem_destroy(&p->sem_sync); - g_free(p->name); - p->name = NULL; - multifd_pages_clear(p->pages); - p->pages = NULL; - p->packet_len = 0; - g_free(p->packet); - p->packet = NULL; - } - qemu_sem_destroy(&multifd_send_state->channels_ready); - g_free(multifd_send_state->params); - multifd_send_state->params = NULL; - multifd_pages_clear(multifd_send_state->pages); - multifd_send_state->pages = NULL; - g_free(multifd_send_state); - multifd_send_state = NULL; -} - -static void multifd_send_sync_main(RAMState *rs) -{ - int i; - - if (!migrate_use_multifd()) { - return; - } - if (multifd_send_state->pages->used) { - if (multifd_send_pages(rs) < 0) { - error_report("%s: multifd_send_pages fail", __func__); - return; - } - } - for (i = 0; i < migrate_multifd_channels(); i++) { - MultiFDSendParams *p = &multifd_send_state->params[i]; - - trace_multifd_send_sync_main_signal(p->id); - - qemu_mutex_lock(&p->mutex); - - if (p->quit) { - error_report("%s: channel %d has already quit", __func__, i); - qemu_mutex_unlock(&p->mutex); - return; - } - - p->packet_num = multifd_send_state->packet_num++; - p->flags |= MULTIFD_FLAG_SYNC; - p->pending_job++; - qemu_file_update_transfer(rs->f, p->packet_len); - ram_counters.multifd_bytes += p->packet_len; - ram_counters.transferred += p->packet_len; - qemu_mutex_unlock(&p->mutex); - qemu_sem_post(&p->sem); - } - for (i = 0; i < migrate_multifd_channels(); i++) { - MultiFDSendParams *p = &multifd_send_state->params[i]; - - trace_multifd_send_sync_main_wait(p->id); - qemu_sem_wait(&p->sem_sync); - } - trace_multifd_send_sync_main(multifd_send_state->packet_num); -} - -static void *multifd_send_thread(void *opaque) -{ - MultiFDSendParams *p = opaque; - Error *local_err = NULL; - int ret = 0; - uint32_t flags = 0; - - trace_multifd_send_thread_start(p->id); - rcu_register_thread(); - - if (multifd_send_initial_packet(p, &local_err) < 0) { - ret = -1; - goto out; - } - /* initial packet */ - p->num_packets = 1; - - while (true) { - qemu_sem_wait(&p->sem); - qemu_mutex_lock(&p->mutex); - - if (p->pending_job) { - uint32_t used = p->pages->used; - uint64_t packet_num = p->packet_num; - flags = p->flags; - - p->next_packet_size = used * qemu_target_page_size(); - multifd_send_fill_packet(p); - p->flags = 0; - p->num_packets++; - p->num_pages += used; - qemu_mutex_unlock(&p->mutex); - - trace_multifd_send(p->id, packet_num, used, flags, - p->next_packet_size); - - ret = qio_channel_write_all(p->c, (void *)p->packet, - p->packet_len, &local_err); - if (ret != 0) { - break; - } - - if (used) { - ret = qio_channel_writev_all(p->c, p->pages->iov, - used, &local_err); - if (ret != 0) { - break; - } - } - - qemu_mutex_lock(&p->mutex); - p->pending_job--; - qemu_mutex_unlock(&p->mutex); - - if (flags & MULTIFD_FLAG_SYNC) { - qemu_sem_post(&p->sem_sync); - } - qemu_sem_post(&multifd_send_state->channels_ready); - } else if (p->quit) { - qemu_mutex_unlock(&p->mutex); - break; - } else { - qemu_mutex_unlock(&p->mutex); - /* sometimes there are spurious wakeups */ - } - } - -out: - if (local_err) { - trace_multifd_send_error(p->id); - multifd_send_terminate_threads(local_err); - } - - /* - * Error happen, I will exit, but I can't just leave, tell - * who pay attention to me. - */ - if (ret != 0) { - qemu_sem_post(&p->sem_sync); - qemu_sem_post(&multifd_send_state->channels_ready); - } - - qemu_mutex_lock(&p->mutex); - p->running = false; - qemu_mutex_unlock(&p->mutex); - - rcu_unregister_thread(); - trace_multifd_send_thread_end(p->id, p->num_packets, p->num_pages); - - return NULL; -} - -static void multifd_new_send_channel_async(QIOTask *task, gpointer opaque) -{ - MultiFDSendParams *p = opaque; - QIOChannel *sioc = QIO_CHANNEL(qio_task_get_source(task)); - Error *local_err = NULL; - - trace_multifd_new_send_channel_async(p->id); - if (qio_task_propagate_error(task, &local_err)) { - migrate_set_error(migrate_get_current(), local_err); - multifd_save_cleanup(); - } else { - p->c = QIO_CHANNEL(sioc); - qio_channel_set_delay(p->c, false); - p->running = true; - qemu_thread_create(&p->thread, p->name, multifd_send_thread, p, - QEMU_THREAD_JOINABLE); - } -} - -int multifd_save_setup(void) -{ - int thread_count; - uint32_t page_count = MULTIFD_PACKET_SIZE / qemu_target_page_size(); - uint8_t i; - - if (!migrate_use_multifd()) { - return 0; - } - thread_count = migrate_multifd_channels(); - multifd_send_state = g_malloc0(sizeof(*multifd_send_state)); - multifd_send_state->params = g_new0(MultiFDSendParams, thread_count); - multifd_send_state->pages = multifd_pages_init(page_count); - qemu_sem_init(&multifd_send_state->channels_ready, 0); - - for (i = 0; i < thread_count; i++) { - MultiFDSendParams *p = &multifd_send_state->params[i]; - - qemu_mutex_init(&p->mutex); - qemu_sem_init(&p->sem, 0); - qemu_sem_init(&p->sem_sync, 0); - p->quit = false; - p->pending_job = 0; - p->id = i; - p->pages = multifd_pages_init(page_count); - p->packet_len = sizeof(MultiFDPacket_t) - + sizeof(ram_addr_t) * page_count; - p->packet = g_malloc0(p->packet_len); - p->packet->magic = cpu_to_be32(MULTIFD_MAGIC); - p->packet->version = cpu_to_be32(MULTIFD_VERSION); - p->name = g_strdup_printf("multifdsend_%d", i); - socket_send_channel_create(multifd_new_send_channel_async, p); - } - return 0; -} - -struct { - MultiFDRecvParams *params; - /* number of created threads */ - int count; - /* syncs main thread and channels */ - QemuSemaphore sem_sync; - /* global number of generated multifd packets */ - uint64_t packet_num; -} *multifd_recv_state; - -static void multifd_recv_terminate_threads(Error *err) -{ - int i; - - trace_multifd_recv_terminate_threads(err != NULL); - - if (err) { - MigrationState *s = migrate_get_current(); - migrate_set_error(s, err); - if (s->state == MIGRATION_STATUS_SETUP || - s->state == MIGRATION_STATUS_ACTIVE) { - migrate_set_state(&s->state, s->state, - MIGRATION_STATUS_FAILED); - } - } - - for (i = 0; i < migrate_multifd_channels(); i++) { - MultiFDRecvParams *p = &multifd_recv_state->params[i]; - - qemu_mutex_lock(&p->mutex); - p->quit = true; - /* We could arrive here for two reasons: - - normal quit, i.e. everything went fine, just finished - - error quit: We close the channels so the channel threads - finish the qio_channel_read_all_eof() */ - qio_channel_shutdown(p->c, QIO_CHANNEL_SHUTDOWN_BOTH, NULL); - qemu_mutex_unlock(&p->mutex); - } -} - -int multifd_load_cleanup(Error **errp) -{ - int i; - int ret = 0; - - if (!migrate_use_multifd()) { - return 0; - } - multifd_recv_terminate_threads(NULL); - for (i = 0; i < migrate_multifd_channels(); i++) { - MultiFDRecvParams *p = &multifd_recv_state->params[i]; - - if (p->running) { - p->quit = true; - /* - * multifd_recv_thread may hung at MULTIFD_FLAG_SYNC handle code, - * however try to wakeup it without harm in cleanup phase. - */ - qemu_sem_post(&p->sem_sync); - qemu_thread_join(&p->thread); - } - object_unref(OBJECT(p->c)); - p->c = NULL; - qemu_mutex_destroy(&p->mutex); - qemu_sem_destroy(&p->sem_sync); - g_free(p->name); - p->name = NULL; - multifd_pages_clear(p->pages); - p->pages = NULL; - p->packet_len = 0; - g_free(p->packet); - p->packet = NULL; - } - qemu_sem_destroy(&multifd_recv_state->sem_sync); - g_free(multifd_recv_state->params); - multifd_recv_state->params = NULL; - g_free(multifd_recv_state); - multifd_recv_state = NULL; - - return ret; -} - -static void multifd_recv_sync_main(void) -{ - int i; - - if (!migrate_use_multifd()) { - return; - } - for (i = 0; i < migrate_multifd_channels(); i++) { - MultiFDRecvParams *p = &multifd_recv_state->params[i]; - - trace_multifd_recv_sync_main_wait(p->id); - qemu_sem_wait(&multifd_recv_state->sem_sync); - } - for (i = 0; i < migrate_multifd_channels(); i++) { - MultiFDRecvParams *p = &multifd_recv_state->params[i]; - - qemu_mutex_lock(&p->mutex); - if (multifd_recv_state->packet_num < p->packet_num) { - multifd_recv_state->packet_num = p->packet_num; - } - qemu_mutex_unlock(&p->mutex); - trace_multifd_recv_sync_main_signal(p->id); - qemu_sem_post(&p->sem_sync); - } - trace_multifd_recv_sync_main(multifd_recv_state->packet_num); -} - -static void *multifd_recv_thread(void *opaque) -{ - MultiFDRecvParams *p = opaque; - Error *local_err = NULL; - int ret; - - trace_multifd_recv_thread_start(p->id); - rcu_register_thread(); - - while (true) { - uint32_t used; - uint32_t flags; - - if (p->quit) { - break; - } - - ret = qio_channel_read_all_eof(p->c, (void *)p->packet, - p->packet_len, &local_err); - if (ret == 0) { /* EOF */ - break; - } - if (ret == -1) { /* Error */ - break; - } - - qemu_mutex_lock(&p->mutex); - ret = multifd_recv_unfill_packet(p, &local_err); - if (ret) { - qemu_mutex_unlock(&p->mutex); - break; - } - - used = p->pages->used; - flags = p->flags; - trace_multifd_recv(p->id, p->packet_num, used, flags, - p->next_packet_size); - p->num_packets++; - p->num_pages += used; - qemu_mutex_unlock(&p->mutex); - - if (used) { - ret = qio_channel_readv_all(p->c, p->pages->iov, - used, &local_err); - if (ret != 0) { - break; - } - } - - if (flags & MULTIFD_FLAG_SYNC) { - qemu_sem_post(&multifd_recv_state->sem_sync); - qemu_sem_wait(&p->sem_sync); - } - } - - if (local_err) { - multifd_recv_terminate_threads(local_err); - } - qemu_mutex_lock(&p->mutex); - p->running = false; - qemu_mutex_unlock(&p->mutex); - - rcu_unregister_thread(); - trace_multifd_recv_thread_end(p->id, p->num_packets, p->num_pages); - - return NULL; -} - -int multifd_load_setup(void) -{ - int thread_count; - uint32_t page_count = MULTIFD_PACKET_SIZE / qemu_target_page_size(); - uint8_t i; - - if (!migrate_use_multifd()) { - return 0; - } - thread_count = migrate_multifd_channels(); - multifd_recv_state = g_malloc0(sizeof(*multifd_recv_state)); - multifd_recv_state->params = g_new0(MultiFDRecvParams, thread_count); - atomic_set(&multifd_recv_state->count, 0); - qemu_sem_init(&multifd_recv_state->sem_sync, 0); - - for (i = 0; i < thread_count; i++) { - MultiFDRecvParams *p = &multifd_recv_state->params[i]; - - qemu_mutex_init(&p->mutex); - qemu_sem_init(&p->sem_sync, 0); - p->quit = false; - p->id = i; - p->pages = multifd_pages_init(page_count); - p->packet_len = sizeof(MultiFDPacket_t) - + sizeof(ram_addr_t) * page_count; - p->packet = g_malloc0(p->packet_len); - p->name = g_strdup_printf("multifdrecv_%d", i); - } - return 0; -} - -bool multifd_recv_all_channels_created(void) -{ - int thread_count = migrate_multifd_channels(); - - if (!migrate_use_multifd()) { - return true; - } - - return thread_count == atomic_read(&multifd_recv_state->count); -} - -/* - * Try to receive all multifd channels to get ready for the migration. - * - Return true and do not set @errp when correctly receving all channels; - * - Return false and do not set @errp when correctly receiving the current one; - * - Return false and set @errp when failing to receive the current channel. - */ -bool multifd_recv_new_channel(QIOChannel *ioc, Error **errp) -{ - MultiFDRecvParams *p; - Error *local_err = NULL; - int id; - - id = multifd_recv_initial_packet(ioc, &local_err); - if (id < 0) { - multifd_recv_terminate_threads(local_err); - error_propagate_prepend(errp, local_err, - "failed to receive packet" - " via multifd channel %d: ", - atomic_read(&multifd_recv_state->count)); - return false; - } - trace_multifd_recv_new_channel(id); - - p = &multifd_recv_state->params[id]; - if (p->c != NULL) { - error_setg(&local_err, "multifd: received id '%d' already setup'", - id); - multifd_recv_terminate_threads(local_err); - error_propagate(errp, local_err); - return false; - } - p->c = ioc; - object_ref(OBJECT(ioc)); - /* initial packet */ - p->num_packets = 1; - - p->running = true; - qemu_thread_create(&p->thread, p->name, multifd_recv_thread, p, - QEMU_THREAD_JOINABLE); - atomic_inc(&multifd_recv_state->count); - return atomic_read(&multifd_recv_state->count) == - migrate_multifd_channels(); -} - /** * save_page_header: write page header to wire * @@ -1730,7 +796,7 @@ static inline bool migration_bitmap_clear_dirty(RAMState *rs, if (rb->clear_bmap && clear_bmap_test_and_clear(rb, page)) { uint8_t shift = rb->clear_bmap_shift; hwaddr size = 1ULL << (TARGET_PAGE_BITS + shift); - hwaddr start = (page << TARGET_PAGE_BITS) & (-size); + hwaddr start = (((ram_addr_t)page) << TARGET_PAGE_BITS) & (-size); /* * CLEAR_BITMAP_SHIFT_MIN should always guarantee this... this @@ -1830,11 +896,38 @@ static void migration_update_rates(RAMState *rs, int64_t end_time) } } +static void migration_trigger_throttle(RAMState *rs) +{ + MigrationState *s = migrate_get_current(); + uint64_t threshold = s->parameters.throttle_trigger_threshold; + + uint64_t bytes_xfer_period = ram_counters.transferred - rs->bytes_xfer_prev; + uint64_t bytes_dirty_period = rs->num_dirty_pages_period * TARGET_PAGE_SIZE; + uint64_t bytes_dirty_threshold = bytes_xfer_period * threshold / 100; + + /* During block migration the auto-converge logic incorrectly detects + * that ram migration makes no progress. Avoid this by disabling the + * throttling logic during the bulk phase of block migration. */ + if (migrate_auto_converge() && !blk_mig_bulk_active()) { + /* The following detection logic can be refined later. For now: + Check to see if the ratio between dirtied bytes and the approx. + amount of bytes that just got transferred since the last time + we were in this routine reaches the threshold. If that happens + twice, start or increase throttling. */ + + if ((bytes_dirty_period > bytes_dirty_threshold) && + (++rs->dirty_rate_high_cnt >= 2)) { + trace_migration_throttle(); + rs->dirty_rate_high_cnt = 0; + mig_throttle_guest_down(); + } + } +} + static void migration_bitmap_sync(RAMState *rs) { RAMBlock *block; int64_t end_time; - uint64_t bytes_xfer_now; ram_counters.dirty_sync_count++; @@ -1861,26 +954,7 @@ static void migration_bitmap_sync(RAMState *rs) /* more than 1 second = 1000 millisecons */ if (end_time > rs->time_last_bitmap_sync + 1000) { - bytes_xfer_now = ram_counters.transferred; - - /* During block migration the auto-converge logic incorrectly detects - * that ram migration makes no progress. Avoid this by disabling the - * throttling logic during the bulk phase of block migration. */ - if (migrate_auto_converge() && !blk_mig_bulk_active()) { - /* The following detection logic can be refined later. For now: - Check to see if the dirtied bytes is 50% more than the approx. - amount of bytes that just got transferred since the last time we - were in this routine. If that happens twice, start or increase - throttling */ - - if ((rs->num_dirty_pages_period * TARGET_PAGE_SIZE > - (bytes_xfer_now - rs->bytes_xfer_prev) / 2) && - (++rs->dirty_rate_high_cnt >= 2)) { - trace_migration_throttle(); - rs->dirty_rate_high_cnt = 0; - mig_throttle_guest_down(); - } - } + migration_trigger_throttle(rs); migration_update_rates(rs, end_time); @@ -1889,7 +963,7 @@ static void migration_bitmap_sync(RAMState *rs) /* reset period counters */ rs->time_last_bitmap_sync = end_time; rs->num_dirty_pages_period = 0; - rs->bytes_xfer_prev = bytes_xfer_now; + rs->bytes_xfer_prev = ram_counters.transferred; } if (migrate_use_events()) { qapi_event_send_migration_pass(ram_counters.dirty_sync_count); @@ -1906,6 +980,7 @@ static void migration_bitmap_sync_precopy(RAMState *rs) */ if (precopy_notify(PRECOPY_NOTIFY_BEFORE_BITMAP_SYNC, &local_err)) { error_report_err(local_err); + local_err = NULL; } migration_bitmap_sync(rs); @@ -1967,7 +1042,7 @@ static void ram_release_pages(const char *rbname, uint64_t offset, int pages) return; } - ram_discard_range(rbname, offset, pages << TARGET_PAGE_BITS); + ram_discard_range(rbname, offset, ((ram_addr_t)pages) << TARGET_PAGE_BITS); } /* @@ -2055,7 +1130,7 @@ static int ram_save_page(RAMState *rs, PageSearchStatus *pss, bool last_stage) uint8_t *p; bool send_async = true; RAMBlock *block = pss->block; - ram_addr_t offset = pss->page << TARGET_PAGE_BITS; + ram_addr_t offset = ((ram_addr_t)pss->page) << TARGET_PAGE_BITS; ram_addr_t current_addr = block->offset + offset; p = block->host + offset; @@ -2087,7 +1162,7 @@ static int ram_save_page(RAMState *rs, PageSearchStatus *pss, bool last_stage) static int ram_save_multifd_page(RAMState *rs, RAMBlock *block, ram_addr_t offset) { - if (multifd_queue_page(rs, block, offset) < 0) { + if (multifd_queue_page(rs->f, block, offset) < 0) { return -1; } ram_counters.normal++; @@ -2242,7 +1317,8 @@ static bool find_dirty_block(RAMState *rs, PageSearchStatus *pss, bool *again) *again = false; return false; } - if ((pss->page << TARGET_PAGE_BITS) >= pss->block->used_length) { + if ((((ram_addr_t)pss->page) << TARGET_PAGE_BITS) + >= pss->block->used_length) { /* Didn't find anything in this RAM Block */ pss->page = 0; pss->block = QLIST_NEXT_RCU(pss->block, next); @@ -2433,7 +1509,7 @@ int ram_save_queue_pages(const char *rbname, ram_addr_t start, ram_addr_t len) * it's the 1st request. */ error_report("ram_save_queue_pages no previous block"); - goto err; + return -1; } } else { ramblock = qemu_ram_block_by_name(rbname); @@ -2441,7 +1517,7 @@ int ram_save_queue_pages(const char *rbname, ram_addr_t start, ram_addr_t len) if (!ramblock) { /* We shouldn't be asked for a non-existent RAMBlock */ error_report("ram_save_queue_pages no block '%s'", rbname); - goto err; + return -1; } rs->last_req_rb = ramblock; } @@ -2450,7 +1526,7 @@ int ram_save_queue_pages(const char *rbname, ram_addr_t start, ram_addr_t len) error_report("%s request overrun start=" RAM_ADDR_FMT " len=" RAM_ADDR_FMT " blocklen=" RAM_ADDR_FMT, __func__, start, len, ramblock->used_length); - goto err; + return -1; } struct RAMSrcPageRequest *new_entry = @@ -2466,9 +1542,6 @@ int ram_save_queue_pages(const char *rbname, ram_addr_t start, ram_addr_t len) qemu_mutex_unlock(&rs->src_page_req_mutex); return 0; - -err: - return -1; } static bool save_page_use_compression(RAMState *rs) @@ -2536,7 +1609,7 @@ static int ram_save_target_page(RAMState *rs, PageSearchStatus *pss, bool last_stage) { RAMBlock *block = pss->block; - ram_addr_t offset = pss->page << TARGET_PAGE_BITS; + ram_addr_t offset = ((ram_addr_t)pss->page) << TARGET_PAGE_BITS; int res; if (control_save_page(rs, block, offset, &res)) { @@ -2562,10 +1635,13 @@ static int ram_save_target_page(RAMState *rs, PageSearchStatus *pss, } /* - * do not use multifd for compression as the first page in the new - * block should be posted out before sending the compressed page + * Do not use multifd for: + * 1. Compression as the first page in the new block should be posted out + * before sending the compressed page + * 2. In postcopy as one whole host page should be placed */ - if (!save_page_use_compression(rs) && migrate_use_multifd()) { + if (!save_page_use_compression(rs) && migrate_use_multifd() + && !migration_in_postcopy()) { return ram_save_multifd_page(rs, block, offset); } @@ -2616,8 +1692,11 @@ static int ram_save_host_page(RAMState *rs, PageSearchStatus *pss, pages += tmppages; pss->page++; + /* Allow rate limiting to happen in the middle of huge pages */ + migration_rate_limit(); } while ((pss->page & (pagesize_bits - 1)) && - offset_in_ramblock(pss->block, pss->page << TARGET_PAGE_BITS)); + offset_in_ramblock(pss->block, + ((ram_addr_t)pss->page) << TARGET_PAGE_BITS)); /* The offset we leave with is the last one we looked at */ pss->page--; @@ -2834,8 +1913,10 @@ void ram_postcopy_migrated_memory_release(MigrationState *ms) while (run_start < range) { unsigned long run_end = find_next_bit(bitmap, range, run_start + 1); - ram_discard_range(block->idstr, run_start << TARGET_PAGE_BITS, - (run_end - run_start) << TARGET_PAGE_BITS); + ram_discard_range(block->idstr, + ((ram_addr_t)run_start) << TARGET_PAGE_BITS, + ((ram_addr_t)(run_end - run_start)) + << TARGET_PAGE_BITS); run_start = find_next_zero_bit(bitmap, range, run_end + 1); } } @@ -3071,8 +2152,6 @@ int ram_postcopy_send_discard_bitmap(MigrationState *ms) */ int ram_discard_range(const char *rbname, uint64_t start, size_t length) { - int ret = -1; - trace_ram_discard_range(rbname, start, length); RCU_READ_LOCK_GUARD(); @@ -3080,7 +2159,7 @@ int ram_discard_range(const char *rbname, uint64_t start, size_t length) if (!rb) { error_report("ram_discard_range: Failed to find block '%s'", rbname); - goto err; + return -1; } /* @@ -3092,10 +2171,7 @@ int ram_discard_range(const char *rbname, uint64_t start, size_t length) length >> qemu_target_page_bits()); } - ret = ram_block_discard_range(rb, start, length); - -err: - return ret; + return ram_block_discard_range(rb, start, length); } /* @@ -3384,7 +2460,7 @@ static int ram_save_setup(QEMUFile *f, void *opaque) ram_control_before_iterate(f, RAM_CONTROL_SETUP); ram_control_after_iterate(f, RAM_CONTROL_SETUP); - multifd_send_sync_main(*rsp); + multifd_send_sync_main(f); qemu_put_be64(f, RAM_SAVE_FLAG_EOS); qemu_fflush(f); @@ -3403,7 +2479,7 @@ static int ram_save_iterate(QEMUFile *f, void *opaque) { RAMState **temp = opaque; RAMState *rs = *temp; - int ret; + int ret = 0; int i; int64_t t0; int done = 0; @@ -3449,6 +2525,14 @@ static int ram_save_iterate(QEMUFile *f, void *opaque) rs->target_page_count += pages; + /* + * During postcopy, it is necessary to make sure one whole host + * page is sent in one chunk. + */ + if (migrate_postcopy_ram()) { + flush_compressed_data(rs); + } + /* * we want to check in the 1st loop, just in case it was the 1st * time and we had to sync the dirty bitmap. @@ -3474,12 +2558,15 @@ static int ram_save_iterate(QEMUFile *f, void *opaque) ram_control_after_iterate(f, RAM_CONTROL_ROUND); out: - multifd_send_sync_main(rs); - qemu_put_be64(f, RAM_SAVE_FLAG_EOS); - qemu_fflush(f); - ram_counters.transferred += 8; + if (ret >= 0 + && migration_is_setup_or_active(migrate_get_current()->state)) { + multifd_send_sync_main(rs->f); + qemu_put_be64(f, RAM_SAVE_FLAG_EOS); + qemu_fflush(f); + ram_counters.transferred += 8; - ret = qemu_file_get_error(f); + ret = qemu_file_get_error(f); + } if (ret < 0) { return ret; } @@ -3531,9 +2618,11 @@ static int ram_save_complete(QEMUFile *f, void *opaque) ram_control_after_iterate(f, RAM_CONTROL_FINISH); } - multifd_send_sync_main(rs); - qemu_put_be64(f, RAM_SAVE_FLAG_EOS); - qemu_fflush(f); + if (ret >= 0) { + multifd_send_sync_main(rs->f); + qemu_put_be64(f, RAM_SAVE_FLAG_EOS); + qemu_fflush(f); + } return ret; } @@ -3654,7 +2743,7 @@ static inline void *host_from_ram_block_offset(RAMBlock *block, } static inline void *colo_cache_from_block_offset(RAMBlock *block, - ram_addr_t offset) + ram_addr_t offset, bool record_bitmap) { if (!offset_in_ramblock(block, offset)) { return NULL; @@ -3670,7 +2759,8 @@ static inline void *colo_cache_from_block_offset(RAMBlock *block, * It help us to decide which pages in ram cache should be flushed * into VM's RAM later. */ - if (!test_and_set_bit(offset >> TARGET_PAGE_BITS, block->bmap)) { + if (record_bitmap && + !test_and_set_bit(offset >> TARGET_PAGE_BITS, block->bmap)) { ram_state->migration_dirty_pages++; } return block->colo_cache + offset; @@ -3889,26 +2979,26 @@ int colo_init_ram_cache(void) { RAMBlock *block; - rcu_read_lock(); - RAMBLOCK_FOREACH_NOT_IGNORED(block) { - block->colo_cache = qemu_anon_ram_alloc(block->used_length, - NULL, - false); - if (!block->colo_cache) { - error_report("%s: Can't alloc memory for COLO cache of block %s," - "size 0x" RAM_ADDR_FMT, __func__, block->idstr, - block->used_length); - RAMBLOCK_FOREACH_NOT_IGNORED(block) { - if (block->colo_cache) { - qemu_anon_ram_free(block->colo_cache, block->used_length); - block->colo_cache = NULL; + WITH_RCU_READ_LOCK_GUARD() { + RAMBLOCK_FOREACH_NOT_IGNORED(block) { + block->colo_cache = qemu_anon_ram_alloc(block->used_length, + NULL, + false); + if (!block->colo_cache) { + error_report("%s: Can't alloc memory for COLO cache of block %s," + "size 0x" RAM_ADDR_FMT, __func__, block->idstr, + block->used_length); + RAMBLOCK_FOREACH_NOT_IGNORED(block) { + if (block->colo_cache) { + qemu_anon_ram_free(block->colo_cache, block->used_length); + block->colo_cache = NULL; + } } + return -errno; } - return -errno; } - memcpy(block->colo_cache, block->host, block->used_length); } - rcu_read_unlock(); + /* * Record the dirty pages that sent by PVM, we use this dirty bitmap together * with to decide which page in cache should be flushed into SVM's RAM. Here @@ -3919,19 +3009,36 @@ int colo_init_ram_cache(void) RAMBLOCK_FOREACH_NOT_IGNORED(block) { unsigned long pages = block->max_length >> TARGET_PAGE_BITS; - block->bmap = bitmap_new(pages); - bitmap_set(block->bmap, 0, pages); } } - ram_state = g_new0(RAMState, 1); - ram_state->migration_dirty_pages = 0; - qemu_mutex_init(&ram_state->bitmap_mutex); - memory_global_dirty_log_start(); + ram_state_init(&ram_state); return 0; } +/* TODO: duplicated with ram_init_bitmaps */ +void colo_incoming_start_dirty_log(void) +{ + RAMBlock *block = NULL; + /* For memory_global_dirty_log_start below. */ + qemu_mutex_lock_iothread(); + qemu_mutex_lock_ramlist(); + + memory_global_dirty_log_sync(); + WITH_RCU_READ_LOCK_GUARD() { + RAMBLOCK_FOREACH_NOT_IGNORED(block) { + ramblock_sync_dirty_bitmap(ram_state, block); + /* Discard this dirty bitmap record */ + bitmap_zero(block->bmap, block->max_length >> TARGET_PAGE_BITS); + } + memory_global_dirty_log_start(); + } + ram_state->migration_dirty_pages = 0; + qemu_mutex_unlock_ramlist(); + qemu_mutex_unlock_iothread(); +} + /* It is need to hold the global lock to call this helper */ void colo_release_ram_cache(void) { @@ -3951,9 +3058,7 @@ void colo_release_ram_cache(void) } } } - qemu_mutex_destroy(&ram_state->bitmap_mutex); - g_free(ram_state); - ram_state = NULL; + ram_state_cleanup(&ram_state); } /** @@ -3981,9 +3086,7 @@ static int ram_load_cleanup(void *opaque) RAMBlock *rb; RAMBLOCK_FOREACH_NOT_IGNORED(rb) { - if (ramblock_is_pmem(rb)) { - pmem_persist(rb->host, rb->used_length); - } + qemu_ram_block_writeback(rb); } xbzrle_load_cleanup(); @@ -4031,8 +3134,9 @@ static int ram_load_postcopy(QEMUFile *f) MigrationIncomingState *mis = migration_incoming_get_current(); /* Temporary page that is later 'placed' */ void *postcopy_host_page = mis->postcopy_tmp_page; - void *last_host = NULL; + void *this_host = NULL; bool all_zero = false; + int target_pages = 0; while (!ret && !(flags & RAM_SAVE_FLAG_EOS)) { ram_addr_t addr; @@ -4041,6 +3145,7 @@ static int ram_load_postcopy(QEMUFile *f) void *place_source = NULL; RAMBlock *block = NULL; uint8_t ch; + int len; addr = qemu_get_be64(f); @@ -4058,7 +3163,8 @@ static int ram_load_postcopy(QEMUFile *f) trace_ram_load_postcopy_loop((uint64_t)addr, flags); place_needed = false; - if (flags & (RAM_SAVE_FLAG_ZERO | RAM_SAVE_FLAG_PAGE)) { + if (flags & (RAM_SAVE_FLAG_ZERO | RAM_SAVE_FLAG_PAGE | + RAM_SAVE_FLAG_COMPRESS_PAGE)) { block = ram_block_from_stream(f, flags); host = host_from_ram_block_offset(block, addr); @@ -4067,6 +3173,7 @@ static int ram_load_postcopy(QEMUFile *f) ret = -EINVAL; break; } + target_pages++; matches_target_page_size = block->page_size == TARGET_PAGE_SIZE; /* * Postcopy requires that we place whole host pages atomically; @@ -4076,38 +3183,47 @@ static int ram_load_postcopy(QEMUFile *f) * that's moved into place later. * The migration protocol uses, possibly smaller, target-pages * however the source ensures it always sends all the components - * of a host page in order. + * of a host page in one chunk. */ page_buffer = postcopy_host_page + ((uintptr_t)host & (block->page_size - 1)); /* If all TP are zero then we can optimise the place */ - if (!((uintptr_t)host & (block->page_size - 1))) { + if (target_pages == 1) { all_zero = true; + this_host = (void *)QEMU_ALIGN_DOWN((uintptr_t)host, + block->page_size); } else { /* not the 1st TP within the HP */ - if (host != (last_host + TARGET_PAGE_SIZE)) { - error_report("Non-sequential target page %p/%p", - host, last_host); + if (QEMU_ALIGN_DOWN((uintptr_t)host, block->page_size) != + (uintptr_t)this_host) { + error_report("Non-same host page %p/%p", + host, this_host); ret = -EINVAL; break; } } - /* * If it's the last part of a host page then we place the host * page */ - place_needed = (((uintptr_t)host + TARGET_PAGE_SIZE) & - (block->page_size - 1)) == 0; + if (target_pages == (block->page_size / TARGET_PAGE_SIZE)) { + place_needed = true; + target_pages = 0; + } place_source = postcopy_host_page; } - last_host = host; switch (flags & ~RAM_SAVE_FLAG_CONTINUE) { case RAM_SAVE_FLAG_ZERO: ch = qemu_get_byte(f); - memset(page_buffer, ch, TARGET_PAGE_SIZE); + /* + * Can skip to set page_buffer when + * this is a zero page and (block->page_size == TARGET_PAGE_SIZE). + */ + if (ch || !matches_target_page_size) { + memset(page_buffer, ch, TARGET_PAGE_SIZE); + } if (ch) { all_zero = false; } @@ -4131,6 +3247,17 @@ static int ram_load_postcopy(QEMUFile *f) TARGET_PAGE_SIZE); } break; + case RAM_SAVE_FLAG_COMPRESS_PAGE: + all_zero = false; + len = qemu_get_be32(f); + if (len < 0 || len > compressBound(TARGET_PAGE_SIZE)) { + error_report("Invalid compressed data length: %d", len); + ret = -EINVAL; + break; + } + decompress_data_with_multi_threads(f, page_buffer, len); + break; + case RAM_SAVE_FLAG_EOS: /* normal exit */ multifd_recv_sync_main(); @@ -4142,6 +3269,11 @@ static int ram_load_postcopy(QEMUFile *f) break; } + /* Got the whole host page, wait for decompress before placing. */ + if (place_needed) { + ret |= wait_for_decompress_done(); + } + /* Detect for any possible file errors */ if (!ret && qemu_file_get_error(f)) { ret = qemu_file_get_error(f); @@ -4149,7 +3281,8 @@ static int ram_load_postcopy(QEMUFile *f) if (!ret && place_needed) { /* This gets called at the last target page in the host page */ - void *place_dest = host + TARGET_PAGE_SIZE - block->page_size; + void *place_dest = (void *)QEMU_ALIGN_DOWN((uintptr_t)host, + block->page_size); if (all_zero) { ret = postcopy_place_page_zero(mis, place_dest, @@ -4201,13 +3334,16 @@ static void colo_flush_ram_cache(void) while (block) { offset = migration_bitmap_find_dirty(ram_state, block, offset); - if (offset << TARGET_PAGE_BITS >= block->used_length) { + if (((ram_addr_t)offset) << TARGET_PAGE_BITS + >= block->used_length) { offset = 0; block = QLIST_NEXT_RCU(block, next); } else { migration_bitmap_clear_dirty(ram_state, block, offset); - dst_host = block->host + (offset << TARGET_PAGE_BITS); - src_host = block->colo_cache + (offset << TARGET_PAGE_BITS); + dst_host = block->host + + (((ram_addr_t)offset) << TARGET_PAGE_BITS); + src_host = block->colo_cache + + (((ram_addr_t)offset) << TARGET_PAGE_BITS); memcpy(dst_host, src_host, TARGET_PAGE_SIZE); } } @@ -4227,7 +3363,7 @@ static void colo_flush_ram_cache(void) */ static int ram_load_precopy(QEMUFile *f) { - int flags = 0, ret = 0, invalid_flags = 0, len = 0; + int flags = 0, ret = 0, invalid_flags = 0, len = 0, i = 0; /* ADVISE is earlier, it shows the source has the postcopy capability on */ bool postcopy_advised = postcopy_is_advised(); if (!migrate_use_compression()) { @@ -4236,9 +3372,20 @@ static int ram_load_precopy(QEMUFile *f) while (!ret && !(flags & RAM_SAVE_FLAG_EOS)) { ram_addr_t addr, total_ram_bytes; - void *host = NULL; + void *host = NULL, *host_bak = NULL; uint8_t ch; + /* + * Yield periodically to let main loop run, but an iteration of + * the main loop is expensive, so do it each some iterations + */ + if ((i & 32767) == 0 && qemu_in_coroutine()) { + aio_co_schedule(qemu_get_current_aio_context(), + qemu_coroutine_self()); + qemu_coroutine_yield(); + } + i++; + addr = qemu_get_be64(f); flags = addr & ~TARGET_PAGE_MASK; addr &= TARGET_PAGE_MASK; @@ -4256,20 +3403,35 @@ static int ram_load_precopy(QEMUFile *f) RAM_SAVE_FLAG_COMPRESS_PAGE | RAM_SAVE_FLAG_XBZRLE)) { RAMBlock *block = ram_block_from_stream(f, flags); + host = host_from_ram_block_offset(block, addr); /* - * After going into COLO, we should load the Page into colo_cache. + * After going into COLO stage, we should not load the page + * into SVM's memory directly, we put them into colo_cache firstly. + * NOTE: We need to keep a copy of SVM's ram in colo_cache. + * Previously, we copied all these memory in preparing stage of COLO + * while we need to stop VM, which is a time-consuming process. + * Here we optimize it by a trick, back-up every page while in + * migration process while COLO is enabled, though it affects the + * speed of the migration, but it obviously reduce the downtime of + * back-up all SVM'S memory in COLO preparing stage. */ - if (migration_incoming_in_colo_state()) { - host = colo_cache_from_block_offset(block, addr); - } else { - host = host_from_ram_block_offset(block, addr); + if (migration_incoming_colo_enabled()) { + if (migration_incoming_in_colo_state()) { + /* In COLO stage, put all pages into cache temporarily */ + host = colo_cache_from_block_offset(block, addr, true); + } else { + /* + * In migration stage but before COLO stage, + * Put all pages into both cache and SVM's memory. + */ + host_bak = colo_cache_from_block_offset(block, addr, false); + } } if (!host) { error_report("Illegal RAM offset " RAM_ADDR_FMT, addr); ret = -EINVAL; break; } - if (!migration_incoming_in_colo_state()) { ramblock_recv_bitmap_set(block, host); } @@ -4383,8 +3545,12 @@ static int ram_load_precopy(QEMUFile *f) if (!ret) { ret = qemu_file_get_error(f); } + if (!ret && host_bak) { + memcpy(host_bak, host, TARGET_PAGE_SIZE); + } } + ret |= wait_for_decompress_done(); return ret; } @@ -4416,8 +3582,6 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id) } else { ret = ram_load_precopy(f); } - - ret |= wait_for_decompress_done(); } trace_ram_load_complete(ret, seq_iter); diff --git a/migration/ram.h b/migration/ram.h index bd0eee79b6..5ceaff7cb4 100644 --- a/migration/ram.h +++ b/migration/ram.h @@ -41,13 +41,6 @@ int xbzrle_cache_resize(int64_t new_size, Error **errp); uint64_t ram_bytes_remaining(void); uint64_t ram_bytes_total(void); -int multifd_save_setup(void); -void multifd_save_cleanup(void); -int multifd_load_setup(void); -int multifd_load_cleanup(Error **errp); -bool multifd_recv_all_channels_created(void); -bool multifd_recv_new_channel(QIOChannel *ioc, Error **errp); - uint64_t ram_pagesize_summary(void); int ram_save_queue_pages(const char *rbname, ram_addr_t start, ram_addr_t len); void acct_update_position(QEMUFile *f, size_t size, bool zero); @@ -73,5 +66,6 @@ int ram_dirty_bitmap_reload(MigrationState *s, RAMBlock *rb); /* ram cache */ int colo_init_ram_cache(void); void colo_release_ram_cache(void); +void colo_incoming_start_dirty_log(void); #endif diff --git a/migration/rdma.c b/migration/rdma.c index e241dcb992..f61587891b 100644 --- a/migration/rdma.c +++ b/migration/rdma.c @@ -3980,13 +3980,13 @@ static void rdma_accept_incoming_migration(void *opaque) RDMAContext *rdma = opaque; int ret; QEMUFile *f; - Error *local_err = NULL, **errp = &local_err; + Error *local_err = NULL; trace_qemu_rdma_accept_incoming_migration(); ret = qemu_rdma_accept(rdma); if (ret) { - ERROR(errp, "RDMA Migration initialization failed!"); + fprintf(stderr, "RDMA ERROR: Migration initialization failed\n"); return; } @@ -3998,13 +3998,16 @@ static void rdma_accept_incoming_migration(void *opaque) f = qemu_fopen_rdma(rdma, "rb"); if (f == NULL) { - ERROR(errp, "could not qemu_fopen_rdma!"); + fprintf(stderr, "RDMA ERROR: could not qemu_fopen_rdma\n"); qemu_rdma_cleanup(rdma); return; } rdma->migration_started_on_destination = 1; - migration_fd_process_incoming(f); + migration_fd_process_incoming(f, &local_err); + if (local_err) { + error_reportf_err(local_err, "RDMA ERROR:"); + } } void rdma_start_incoming_migration(const char *host_port, Error **errp) diff --git a/migration/savevm.c b/migration/savevm.c index a71b930b91..c00a6807d9 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -233,7 +233,7 @@ typedef struct CompatEntry { typedef struct SaveStateEntry { QTAILQ_ENTRY(SaveStateEntry) entry; char idstr[256]; - int instance_id; + uint32_t instance_id; int alias_id; int version_id; /* version id read from the stream */ @@ -250,6 +250,7 @@ typedef struct SaveStateEntry { typedef struct SaveState { QTAILQ_HEAD(, SaveStateEntry) handlers; + SaveStateEntry *handler_pri_head[MIG_PRI_MAX + 1]; int global_section_id; uint32_t len; const char *name; @@ -261,6 +262,7 @@ typedef struct SaveState { static SaveState savevm_state = { .handlers = QTAILQ_HEAD_INITIALIZER(savevm_state.handlers), + .handler_pri_head = { [MIG_PRI_DEFAULT ... MIG_PRI_MAX] = NULL }, .global_section_id = 0, }; @@ -663,12 +665,13 @@ void dump_vmstate_json_to_file(FILE *out_file) } fprintf(out_file, "\n}\n"); fclose(out_file); + g_slist_free(list); } -static int calculate_new_instance_id(const char *idstr) +static uint32_t calculate_new_instance_id(const char *idstr) { SaveStateEntry *se; - int instance_id = 0; + uint32_t instance_id = 0; QTAILQ_FOREACH(se, &savevm_state.handlers, entry) { if (strcmp(idstr, se->idstr) == 0 @@ -676,6 +679,8 @@ static int calculate_new_instance_id(const char *idstr) instance_id = se->instance_id + 1; } } + /* Make sure we never loop over without being noticed */ + assert(instance_id != VMSTATE_INSTANCE_ID_ANY); return instance_id; } @@ -709,20 +714,43 @@ static void savevm_state_handler_insert(SaveStateEntry *nse) { MigrationPriority priority = save_state_priority(nse); SaveStateEntry *se; + int i; assert(priority <= MIG_PRI_MAX); - QTAILQ_FOREACH(se, &savevm_state.handlers, entry) { - if (save_state_priority(se) < priority) { + for (i = priority - 1; i >= 0; i--) { + se = savevm_state.handler_pri_head[i]; + if (se != NULL) { + assert(save_state_priority(se) < priority); break; } } - if (se) { + if (i >= 0) { QTAILQ_INSERT_BEFORE(se, nse, entry); } else { QTAILQ_INSERT_TAIL(&savevm_state.handlers, nse, entry); } + + if (savevm_state.handler_pri_head[priority] == NULL) { + savevm_state.handler_pri_head[priority] = nse; + } +} + +static void savevm_state_handler_remove(SaveStateEntry *se) +{ + SaveStateEntry *next; + MigrationPriority priority = save_state_priority(se); + + if (se == savevm_state.handler_pri_head[priority]) { + next = QTAILQ_NEXT(se, entry); + if (next != NULL && save_state_priority(next) == priority) { + savevm_state.handler_pri_head[priority] = next; + } else { + savevm_state.handler_pri_head[priority] = NULL; + } + } + QTAILQ_REMOVE(&savevm_state.handlers, se, entry); } /* TODO: Individual devices generally have very little idea about the rest @@ -730,7 +758,7 @@ static void savevm_state_handler_insert(SaveStateEntry *nse) Meanwhile pass -1 as instance_id if you do not already have a clearly distinguishing id for all instances of your device class. */ int register_savevm_live(const char *idstr, - int instance_id, + uint32_t instance_id, int version_id, const SaveVMHandlers *ops, void *opaque) @@ -750,7 +778,7 @@ int register_savevm_live(const char *idstr, pstrcat(se->idstr, sizeof(se->idstr), idstr); - if (instance_id == -1) { + if (instance_id == VMSTATE_INSTANCE_ID_ANY) { se->instance_id = calculate_new_instance_id(se->idstr); } else { se->instance_id = instance_id; @@ -760,31 +788,31 @@ int register_savevm_live(const char *idstr, return 0; } -void unregister_savevm(DeviceState *dev, const char *idstr, void *opaque) +void unregister_savevm(VMStateIf *obj, const char *idstr, void *opaque) { SaveStateEntry *se, *new_se; char id[256] = ""; - if (dev) { - char *path = qdev_get_dev_path(dev); - if (path) { - pstrcpy(id, sizeof(id), path); + if (obj) { + char *oid = vmstate_if_get_id(obj); + if (oid) { + pstrcpy(id, sizeof(id), oid); pstrcat(id, sizeof(id), "/"); - g_free(path); + g_free(oid); } } pstrcat(id, sizeof(id), idstr); QTAILQ_FOREACH_SAFE(se, &savevm_state.handlers, entry, new_se) { if (strcmp(se->idstr, id) == 0 && se->opaque == opaque) { - QTAILQ_REMOVE(&savevm_state.handlers, se, entry); + savevm_state_handler_remove(se); g_free(se->compat); g_free(se); } } } -int vmstate_register_with_alias_id(DeviceState *dev, int instance_id, +int vmstate_register_with_alias_id(VMStateIf *obj, uint32_t instance_id, const VMStateDescription *vmsd, void *opaque, int alias_id, int required_for_version, @@ -802,8 +830,8 @@ int vmstate_register_with_alias_id(DeviceState *dev, int instance_id, se->vmsd = vmsd; se->alias_id = alias_id; - if (dev) { - char *id = qdev_get_dev_path(dev); + if (obj) { + char *id = vmstate_if_get_id(obj); if (id) { if (snprintf(se->idstr, sizeof(se->idstr), "%s/", id) >= sizeof(se->idstr)) { @@ -817,14 +845,14 @@ int vmstate_register_with_alias_id(DeviceState *dev, int instance_id, se->compat = g_new0(CompatEntry, 1); pstrcpy(se->compat->idstr, sizeof(se->compat->idstr), vmsd->name); - se->compat->instance_id = instance_id == -1 ? + se->compat->instance_id = instance_id == VMSTATE_INSTANCE_ID_ANY ? calculate_compat_instance_id(vmsd->name) : instance_id; - instance_id = -1; + instance_id = VMSTATE_INSTANCE_ID_ANY; } } pstrcat(se->idstr, sizeof(se->idstr), vmsd->name); - if (instance_id == -1) { + if (instance_id == VMSTATE_INSTANCE_ID_ANY) { se->instance_id = calculate_new_instance_id(se->idstr); } else { se->instance_id = instance_id; @@ -834,14 +862,14 @@ int vmstate_register_with_alias_id(DeviceState *dev, int instance_id, return 0; } -void vmstate_unregister(DeviceState *dev, const VMStateDescription *vmsd, +void vmstate_unregister(VMStateIf *obj, const VMStateDescription *vmsd, void *opaque) { SaveStateEntry *se, *new_se; QTAILQ_FOREACH_SAFE(se, &savevm_state.handlers, entry, new_se) { if (se->vmsd == vmsd && se->opaque == opaque) { - QTAILQ_REMOVE(&savevm_state.handlers, se, entry); + savevm_state_handler_remove(se); g_free(se->compat); g_free(se); } @@ -1113,36 +1141,18 @@ void qemu_savevm_state_header(QEMUFile *f) } } -int qemu_savevm_nr_failover_devices(void) +bool qemu_savevm_state_guest_unplug_pending(void) { SaveStateEntry *se; - int n = 0; QTAILQ_FOREACH(se, &savevm_state.handlers, entry) { if (se->vmsd && se->vmsd->dev_unplug_pending && se->vmsd->dev_unplug_pending(se->opaque)) { - n++; + return true; } } - return n; -} - -bool qemu_savevm_state_guest_unplug_pending(void) -{ - SaveStateEntry *se; - int n = 0; - - QTAILQ_FOREACH(se, &savevm_state.handlers, entry) { - if (!se->vmsd || !se->vmsd->dev_unplug_pending) { - continue; - } - if (se->vmsd->dev_unplug_pending(se->opaque)) { - n++; - } - } - - return n > 0; + return false; } void qemu_savevm_state_setup(QEMUFile *f) @@ -1504,9 +1514,7 @@ static int qemu_savevm_state(QEMUFile *f, Error **errp) MigrationState *ms = migrate_get_current(); MigrationStatus status; - if (migration_is_setup_or_active(ms->state) || - ms->state == MIGRATION_STATUS_CANCELLING || - ms->state == MIGRATION_STATUS_COLO) { + if (migration_is_running(ms->state)) { error_setg(errp, QERR_MIGRATION_ACTIVE); return -EINVAL; } @@ -1600,7 +1608,7 @@ int qemu_save_device_state(QEMUFile *f) return qemu_file_get_error(f); } -static SaveStateEntry *find_se(const char *idstr, int instance_id) +static SaveStateEntry *find_se(const char *idstr, uint32_t instance_id) { SaveStateEntry *se; @@ -2267,7 +2275,7 @@ qemu_loadvm_section_start_full(QEMUFile *f, MigrationIncomingState *mis) /* Find savevm section */ se = find_se(idstr, instance_id); if (se == NULL) { - error_report("Unknown savevm section or instance '%s' %d. " + error_report("Unknown savevm section or instance '%s' %"PRIu32". " "Make sure that your current VM setup matches your " "saved VM setup, including any hotplugged devices", idstr, instance_id); @@ -2291,7 +2299,7 @@ qemu_loadvm_section_start_full(QEMUFile *f, MigrationIncomingState *mis) ret = vmstate_load(f, se); if (ret < 0) { - error_report("error while loading state for instance 0x%x of" + error_report("error while loading state for instance 0x%"PRIx32" of" " device '%s'", instance_id, idstr); return ret; } diff --git a/migration/savevm.h b/migration/savevm.h index c42b9c80ee..ba64a7e271 100644 --- a/migration/savevm.h +++ b/migration/savevm.h @@ -31,7 +31,6 @@ bool qemu_savevm_state_blocked(Error **errp); void qemu_savevm_state_setup(QEMUFile *f); -int qemu_savevm_nr_failover_devices(void); bool qemu_savevm_state_guest_unplug_pending(void); int qemu_savevm_state_resume_prepare(MigrationState *s); void qemu_savevm_state_header(QEMUFile *f); diff --git a/migration/trace-events b/migration/trace-events index 6dee7b5389..4ab0a503d2 100644 --- a/migration/trace-events +++ b/migration/trace-events @@ -76,6 +76,11 @@ get_gtree_end(const char *field_name, const char *key_vmsd_name, const char *val put_gtree(const char *field_name, const char *key_vmsd_name, const char *val_vmsd_name, uint32_t nnodes) "%s(%s/%s) nnodes=%d" put_gtree_end(const char *field_name, const char *key_vmsd_name, const char *val_vmsd_name, int ret) "%s(%s/%s) %d" +get_qlist(const char *field_name, const char *vmsd_name, int version_id) "%s(%s v%d)" +get_qlist_end(const char *field_name, const char *vmsd_name) "%s(%s)" +put_qlist(const char *field_name, const char *vmsd_name, int version_id) "%s(%s v%d)" +put_qlist_end(const char *field_name, const char *vmsd_name) "%s(%s)" + # qemu-file.c qemu_file_fclose(void) "" @@ -138,12 +143,12 @@ migrate_send_rp_recv_bitmap(char *name, int64_t size) "block '%s' size 0x%"PRIi6 migration_completion_file_err(void) "" migration_completion_postcopy_end(void) "" migration_completion_postcopy_end_after_complete(void) "" +migration_rate_limit_pre(int ms) "%d ms" +migration_rate_limit_post(int urgent) "urgent: %d" migration_return_path_end_before(void) "" migration_return_path_end_after(int rp_error) "%d" migration_thread_after_loop(void) "" migration_thread_file_err(void) "" -migration_thread_ratelimit_pre(int ms) "%d ms" -migration_thread_ratelimit_post(int urgent) "urgent: %d" migration_thread_setup_complete(void) "" open_return_path_on_source(void) "" open_return_path_on_source_continue(void) "" diff --git a/migration/vmstate-types.c b/migration/vmstate-types.c index 7236cf92bc..35e784c9d9 100644 --- a/migration/vmstate-types.c +++ b/migration/vmstate-types.c @@ -843,3 +843,77 @@ const VMStateInfo vmstate_info_gtree = { .get = get_gtree, .put = put_gtree, }; + +static int put_qlist(QEMUFile *f, void *pv, size_t unused_size, + const VMStateField *field, QJSON *vmdesc) +{ + const VMStateDescription *vmsd = field->vmsd; + /* offset of the QTAILQ entry in a QTAILQ element*/ + size_t entry_offset = field->start; + void *elm; + int ret; + + trace_put_qlist(field->name, vmsd->name, vmsd->version_id); + QLIST_RAW_FOREACH(elm, pv, entry_offset) { + qemu_put_byte(f, true); + ret = vmstate_save_state(f, vmsd, elm, vmdesc); + if (ret) { + error_report("%s: failed to save %s (%d)", field->name, + vmsd->name, ret); + return ret; + } + } + qemu_put_byte(f, false); + trace_put_qlist_end(field->name, vmsd->name); + + return 0; +} + +static int get_qlist(QEMUFile *f, void *pv, size_t unused_size, + const VMStateField *field) +{ + int ret = 0; + const VMStateDescription *vmsd = field->vmsd; + /* size of a QLIST element */ + size_t size = field->size; + /* offset of the QLIST entry in a QLIST element */ + size_t entry_offset = field->start; + int version_id = field->version_id; + void *elm, *prev = NULL; + + trace_get_qlist(field->name, vmsd->name, vmsd->version_id); + if (version_id > vmsd->version_id) { + error_report("%s %s", vmsd->name, "too new"); + return -EINVAL; + } + if (version_id < vmsd->minimum_version_id) { + error_report("%s %s", vmsd->name, "too old"); + return -EINVAL; + } + + while (qemu_get_byte(f)) { + elm = g_malloc(size); + ret = vmstate_load_state(f, vmsd, elm, version_id); + if (ret) { + error_report("%s: failed to load %s (%d)", field->name, + vmsd->name, ret); + g_free(elm); + return ret; + } + if (!prev) { + QLIST_RAW_INSERT_HEAD(pv, elm, entry_offset); + } else { + QLIST_RAW_INSERT_AFTER(pv, prev, elm, entry_offset); + } + prev = elm; + } + trace_get_qlist_end(field->name, vmsd->name); + + return ret; +} + +const VMStateInfo vmstate_info_qlist = { + .name = "qlist", + .get = get_qlist, + .put = put_qlist, +}; diff --git a/migration/vmstate.c b/migration/vmstate.c index 7dd8ef66c6..bafa890384 100644 --- a/migration/vmstate.c +++ b/migration/vmstate.c @@ -362,7 +362,6 @@ int vmstate_save_state_v(QEMUFile *f, const VMStateDescription *vmsd, } for (i = 0; i < n_elems; i++) { void *curr_elem = first_elem + size * i; - ret = 0; vmsd_desc_field_start(vmsd, vmdesc_loop, field, i, n_elems); old_offset = qemu_ftell_fast(f); diff --git a/monitor/Makefile.objs b/monitor/Makefile.objs index e91a8581cd..a8533c9dd7 100644 --- a/monitor/Makefile.objs +++ b/monitor/Makefile.objs @@ -1,3 +1,6 @@ obj-y += misc.o common-obj-y += monitor.o qmp.o hmp.o -common-obj-y += qmp-cmds.o hmp-cmds.o +common-obj-y += qmp-cmds.o qmp-cmds-control.o +common-obj-y += hmp-cmds.o + +storage-daemon-obj-y += monitor.o qmp.o qmp-cmds-control.o diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c index b2551c16d1..9b94e67879 100644 --- a/monitor/hmp-cmds.c +++ b/monitor/hmp-cmds.c @@ -31,6 +31,7 @@ #include "qapi/qapi-builtin-visit.h" #include "qapi/qapi-commands-block.h" #include "qapi/qapi-commands-char.h" +#include "qapi/qapi-commands-control.h" #include "qapi/qapi-commands-migration.h" #include "qapi/qapi-commands-misc.h" #include "qapi/qapi-commands-net.h" @@ -39,15 +40,13 @@ #include "qapi/qapi-commands-tpm.h" #include "qapi/qapi-commands-ui.h" #include "qapi/qapi-visit-net.h" +#include "qapi/qapi-visit-migration.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qerror.h" #include "qapi/string-input-visitor.h" #include "qapi/string-output-visitor.h" #include "qom/object_interfaces.h" #include "ui/console.h" -#include "block/nbd.h" -#include "block/qapi.h" -#include "qemu-io.h" #include "qemu/cutils.h" #include "qemu/error-report.h" #include "exec/ramlist.h" @@ -60,11 +59,10 @@ #include #endif -void hmp_handle_error(Monitor *mon, Error **errp) +void hmp_handle_error(Monitor *mon, Error *err) { - assert(errp); - if (*errp) { - error_reportf_err(*errp, "Error: "); + if (err) { + error_reportf_err(err, "Error: "); } } @@ -305,7 +303,7 @@ void hmp_info_migrate(Monitor *mon, const QDict *qdict) info->xbzrle_cache->cache_miss); monitor_printf(mon, "xbzrle cache miss rate: %0.2f\n", info->xbzrle_cache->cache_miss_rate); - monitor_printf(mon, "xbzrle overflow : %" PRIu64 "\n", + monitor_printf(mon, "xbzrle overflow: %" PRIu64 "\n", info->xbzrle_cache->overflow); } @@ -409,6 +407,10 @@ void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict) monitor_printf(mon, "%s: %u\n", MigrationParameter_str(MIGRATION_PARAMETER_DECOMPRESS_THREADS), params->decompress_threads); + assert(params->has_throttle_trigger_threshold); + monitor_printf(mon, "%s: %u\n", + MigrationParameter_str(MIGRATION_PARAMETER_THROTTLE_TRIGGER_THRESHOLD), + params->throttle_trigger_threshold); assert(params->has_cpu_throttle_initial); monitor_printf(mon, "%s: %u\n", MigrationParameter_str(MIGRATION_PARAMETER_CPU_THROTTLE_INITIAL), @@ -448,15 +450,18 @@ void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict) monitor_printf(mon, "%s: %u\n", MigrationParameter_str(MIGRATION_PARAMETER_MULTIFD_CHANNELS), params->multifd_channels); + monitor_printf(mon, "%s: %s\n", + MigrationParameter_str(MIGRATION_PARAMETER_MULTIFD_COMPRESSION), + MultiFDCompression_str(params->multifd_compression)); monitor_printf(mon, "%s: %" PRIu64 "\n", MigrationParameter_str(MIGRATION_PARAMETER_XBZRLE_CACHE_SIZE), params->xbzrle_cache_size); monitor_printf(mon, "%s: %" PRIu64 "\n", MigrationParameter_str(MIGRATION_PARAMETER_MAX_POSTCOPY_BANDWIDTH), params->max_postcopy_bandwidth); - monitor_printf(mon, " %s: '%s'\n", + monitor_printf(mon, "%s: '%s'\n", MigrationParameter_str(MIGRATION_PARAMETER_TLS_AUTHZ), - params->has_tls_authz ? params->tls_authz : ""); + params->tls_authz); } qapi_free_MigrationParameters(params); @@ -468,213 +473,6 @@ void hmp_info_migrate_cache_size(Monitor *mon, const QDict *qdict) qmp_query_migrate_cache_size(NULL) >> 10); } -static void print_block_info(Monitor *mon, BlockInfo *info, - BlockDeviceInfo *inserted, bool verbose) -{ - ImageInfo *image_info; - - assert(!info || !info->has_inserted || info->inserted == inserted); - - if (info && *info->device) { - monitor_printf(mon, "%s", info->device); - if (inserted && inserted->has_node_name) { - monitor_printf(mon, " (%s)", inserted->node_name); - } - } else { - assert(info || inserted); - monitor_printf(mon, "%s", - inserted && inserted->has_node_name ? inserted->node_name - : info && info->has_qdev ? info->qdev - : ""); - } - - if (inserted) { - monitor_printf(mon, ": %s (%s%s%s)\n", - inserted->file, - inserted->drv, - inserted->ro ? ", read-only" : "", - inserted->encrypted ? ", encrypted" : ""); - } else { - monitor_printf(mon, ": [not inserted]\n"); - } - - if (info) { - if (info->has_qdev) { - monitor_printf(mon, " Attached to: %s\n", info->qdev); - } - if (info->has_io_status && info->io_status != BLOCK_DEVICE_IO_STATUS_OK) { - monitor_printf(mon, " I/O status: %s\n", - BlockDeviceIoStatus_str(info->io_status)); - } - - if (info->removable) { - monitor_printf(mon, " Removable device: %slocked, tray %s\n", - info->locked ? "" : "not ", - info->tray_open ? "open" : "closed"); - } - } - - - if (!inserted) { - return; - } - - monitor_printf(mon, " Cache mode: %s%s%s\n", - inserted->cache->writeback ? "writeback" : "writethrough", - inserted->cache->direct ? ", direct" : "", - inserted->cache->no_flush ? ", ignore flushes" : ""); - - if (inserted->has_backing_file) { - monitor_printf(mon, - " Backing file: %s " - "(chain depth: %" PRId64 ")\n", - inserted->backing_file, - inserted->backing_file_depth); - } - - if (inserted->detect_zeroes != BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF) { - monitor_printf(mon, " Detect zeroes: %s\n", - BlockdevDetectZeroesOptions_str(inserted->detect_zeroes)); - } - - if (inserted->bps || inserted->bps_rd || inserted->bps_wr || - inserted->iops || inserted->iops_rd || inserted->iops_wr) - { - monitor_printf(mon, " I/O throttling: bps=%" PRId64 - " bps_rd=%" PRId64 " bps_wr=%" PRId64 - " bps_max=%" PRId64 - " bps_rd_max=%" PRId64 - " bps_wr_max=%" PRId64 - " iops=%" PRId64 " iops_rd=%" PRId64 - " iops_wr=%" PRId64 - " iops_max=%" PRId64 - " iops_rd_max=%" PRId64 - " iops_wr_max=%" PRId64 - " iops_size=%" PRId64 - " group=%s\n", - inserted->bps, - inserted->bps_rd, - inserted->bps_wr, - inserted->bps_max, - inserted->bps_rd_max, - inserted->bps_wr_max, - inserted->iops, - inserted->iops_rd, - inserted->iops_wr, - inserted->iops_max, - inserted->iops_rd_max, - inserted->iops_wr_max, - inserted->iops_size, - inserted->group); - } - - if (verbose) { - monitor_printf(mon, "\nImages:\n"); - image_info = inserted->image; - while (1) { - bdrv_image_info_dump(image_info); - if (image_info->has_backing_image) { - image_info = image_info->backing_image; - } else { - break; - } - } - } -} - -void hmp_info_block(Monitor *mon, const QDict *qdict) -{ - BlockInfoList *block_list, *info; - BlockDeviceInfoList *blockdev_list, *blockdev; - const char *device = qdict_get_try_str(qdict, "device"); - bool verbose = qdict_get_try_bool(qdict, "verbose", false); - bool nodes = qdict_get_try_bool(qdict, "nodes", false); - bool printed = false; - - /* Print BlockBackend information */ - if (!nodes) { - block_list = qmp_query_block(NULL); - } else { - block_list = NULL; - } - - for (info = block_list; info; info = info->next) { - if (device && strcmp(device, info->value->device)) { - continue; - } - - if (info != block_list) { - monitor_printf(mon, "\n"); - } - - print_block_info(mon, info->value, info->value->has_inserted - ? info->value->inserted : NULL, - verbose); - printed = true; - } - - qapi_free_BlockInfoList(block_list); - - if ((!device && !nodes) || printed) { - return; - } - - /* Print node information */ - blockdev_list = qmp_query_named_block_nodes(NULL); - for (blockdev = blockdev_list; blockdev; blockdev = blockdev->next) { - assert(blockdev->value->has_node_name); - if (device && strcmp(device, blockdev->value->node_name)) { - continue; - } - - if (blockdev != blockdev_list) { - monitor_printf(mon, "\n"); - } - - print_block_info(mon, NULL, blockdev->value, verbose); - } - qapi_free_BlockDeviceInfoList(blockdev_list); -} - -void hmp_info_blockstats(Monitor *mon, const QDict *qdict) -{ - BlockStatsList *stats_list, *stats; - - stats_list = qmp_query_blockstats(false, false, NULL); - - for (stats = stats_list; stats; stats = stats->next) { - if (!stats->value->has_device) { - continue; - } - - monitor_printf(mon, "%s:", stats->value->device); - monitor_printf(mon, " rd_bytes=%" PRId64 - " wr_bytes=%" PRId64 - " rd_operations=%" PRId64 - " wr_operations=%" PRId64 - " flush_operations=%" PRId64 - " wr_total_time_ns=%" PRId64 - " rd_total_time_ns=%" PRId64 - " flush_total_time_ns=%" PRId64 - " rd_merged=%" PRId64 - " wr_merged=%" PRId64 - " idle_time_ns=%" PRId64 - "\n", - stats->value->stats->rd_bytes, - stats->value->stats->wr_bytes, - stats->value->stats->rd_operations, - stats->value->stats->wr_operations, - stats->value->stats->flush_operations, - stats->value->stats->wr_total_time_ns, - stats->value->stats->rd_total_time_ns, - stats->value->stats->flush_total_time_ns, - stats->value->stats->rd_merged, - stats->value->stats->wr_merged, - stats->value->stats->idle_time_ns); - } - - qapi_free_BlockStatsList(stats_list); -} #ifdef CONFIG_VNC /* Helper for hmp_info_vnc_clients, _servers */ @@ -729,12 +527,13 @@ static void hmp_info_vnc_servers(Monitor *mon, VncServerInfo2List *server) void hmp_info_vnc(Monitor *mon, const QDict *qdict) { - VncInfo2List *info2l; + VncInfo2List *info2l, *info2l_head; Error *err = NULL; info2l = qmp_query_vnc_servers(&err); + info2l_head = info2l; if (err) { - hmp_handle_error(mon, &err); + hmp_handle_error(mon, err); return; } if (!info2l) { @@ -761,7 +560,7 @@ void hmp_info_vnc(Monitor *mon, const QDict *qdict) info2l = info2l->next; } - qapi_free_VncInfo2List(info2l); + qapi_free_VncInfo2List(info2l_head); } #endif @@ -850,7 +649,7 @@ void hmp_info_balloon(Monitor *mon, const QDict *qdict) info = qmp_query_balloon(&err); if (err) { - hmp_handle_error(mon, &err); + hmp_handle_error(mon, err); return; } @@ -1054,44 +853,6 @@ void hmp_info_pci(Monitor *mon, const QDict *qdict) qapi_free_PciInfoList(info_list); } -void hmp_info_block_jobs(Monitor *mon, const QDict *qdict) -{ - BlockJobInfoList *list; - Error *err = NULL; - - list = qmp_query_block_jobs(&err); - assert(!err); - - if (!list) { - monitor_printf(mon, "No active jobs\n"); - return; - } - - while (list) { - if (strcmp(list->value->type, "stream") == 0) { - monitor_printf(mon, "Streaming device %s: Completed %" PRId64 - " of %" PRId64 " bytes, speed limit %" PRId64 - " bytes/s\n", - list->value->device, - list->value->offset, - list->value->len, - list->value->speed); - } else { - monitor_printf(mon, "Type %s, device %s: Completed %" PRId64 - " of %" PRId64 " bytes, speed limit %" PRId64 - " bytes/s\n", - list->value->type, - list->value->device, - list->value->offset, - list->value->len, - list->value->speed); - } - list = list->next; - } - - qapi_free_BlockJobInfoList(list); -} - void hmp_info_tpm(Monitor *mon, const QDict *qdict) { TPMInfoList *info_list, *info; @@ -1172,7 +933,7 @@ void hmp_sync_profile(Monitor *mon, const QDict *qdict) Error *err = NULL; error_setg(&err, QERR_INVALID_PARAMETER, op); - hmp_handle_error(mon, &err); + hmp_handle_error(mon, err); } } @@ -1191,7 +952,7 @@ void hmp_exit_preconfig(Monitor *mon, const QDict *qdict) Error *err = NULL; qmp_x_exit_preconfig(&err); - hmp_handle_error(mon, &err); + hmp_handle_error(mon, err); } void hmp_cpu(Monitor *mon, const QDict *qdict) @@ -1220,7 +981,7 @@ void hmp_memsave(Monitor *mon, const QDict *qdict) } qmp_memsave(addr, size, filename, true, cpu_index, &err); - hmp_handle_error(mon, &err); + hmp_handle_error(mon, err); } void hmp_pmemsave(Monitor *mon, const QDict *qdict) @@ -1231,7 +992,7 @@ void hmp_pmemsave(Monitor *mon, const QDict *qdict) Error *err = NULL; qmp_pmemsave(addr, size, filename, &err); - hmp_handle_error(mon, &err); + hmp_handle_error(mon, err); } void hmp_ringbuf_write(Monitor *mon, const QDict *qdict) @@ -1242,7 +1003,7 @@ void hmp_ringbuf_write(Monitor *mon, const QDict *qdict) qmp_ringbuf_write(chardev, data, false, 0, &err); - hmp_handle_error(mon, &err); + hmp_handle_error(mon, err); } void hmp_ringbuf_read(Monitor *mon, const QDict *qdict) @@ -1255,7 +1016,7 @@ void hmp_ringbuf_read(Monitor *mon, const QDict *qdict) data = qmp_ringbuf_read(chardev, size, false, 0, &err); if (err) { - hmp_handle_error(mon, &err); + hmp_handle_error(mon, err); return; } @@ -1280,7 +1041,7 @@ void hmp_cont(Monitor *mon, const QDict *qdict) Error *err = NULL; qmp_cont(&err); - hmp_handle_error(mon, &err); + hmp_handle_error(mon, err); } void hmp_system_wakeup(Monitor *mon, const QDict *qdict) @@ -1288,7 +1049,7 @@ void hmp_system_wakeup(Monitor *mon, const QDict *qdict) Error *err = NULL; qmp_system_wakeup(&err); - hmp_handle_error(mon, &err); + hmp_handle_error(mon, err); } void hmp_nmi(Monitor *mon, const QDict *qdict) @@ -1296,7 +1057,7 @@ void hmp_nmi(Monitor *mon, const QDict *qdict) Error *err = NULL; qmp_inject_nmi(&err); - hmp_handle_error(mon, &err); + hmp_handle_error(mon, err); } void hmp_set_link(Monitor *mon, const QDict *qdict) @@ -1306,17 +1067,7 @@ void hmp_set_link(Monitor *mon, const QDict *qdict) Error *err = NULL; qmp_set_link(name, up, &err); - hmp_handle_error(mon, &err); -} - -void hmp_block_passwd(Monitor *mon, const QDict *qdict) -{ - const char *device = qdict_get_str(qdict, "device"); - const char *password = qdict_get_str(qdict, "password"); - Error *err = NULL; - - qmp_block_passwd(true, device, false, NULL, password, &err); - hmp_handle_error(mon, &err); + hmp_handle_error(mon, err); } void hmp_balloon(Monitor *mon, const QDict *qdict) @@ -1325,122 +1076,7 @@ void hmp_balloon(Monitor *mon, const QDict *qdict) Error *err = NULL; qmp_balloon(value, &err); - hmp_handle_error(mon, &err); -} - -void hmp_block_resize(Monitor *mon, const QDict *qdict) -{ - const char *device = qdict_get_str(qdict, "device"); - int64_t size = qdict_get_int(qdict, "size"); - Error *err = NULL; - - qmp_block_resize(true, device, false, NULL, size, &err); - hmp_handle_error(mon, &err); -} - -void hmp_drive_mirror(Monitor *mon, const QDict *qdict) -{ - const char *filename = qdict_get_str(qdict, "target"); - const char *format = qdict_get_try_str(qdict, "format"); - bool reuse = qdict_get_try_bool(qdict, "reuse", false); - bool full = qdict_get_try_bool(qdict, "full", false); - Error *err = NULL; - DriveMirror mirror = { - .device = (char *)qdict_get_str(qdict, "device"), - .target = (char *)filename, - .has_format = !!format, - .format = (char *)format, - .sync = full ? MIRROR_SYNC_MODE_FULL : MIRROR_SYNC_MODE_TOP, - .has_mode = true, - .mode = reuse ? NEW_IMAGE_MODE_EXISTING : NEW_IMAGE_MODE_ABSOLUTE_PATHS, - .unmap = true, - }; - - if (!filename) { - error_setg(&err, QERR_MISSING_PARAMETER, "target"); - hmp_handle_error(mon, &err); - return; - } - qmp_drive_mirror(&mirror, &err); - hmp_handle_error(mon, &err); -} - -void hmp_drive_backup(Monitor *mon, const QDict *qdict) -{ - const char *device = qdict_get_str(qdict, "device"); - const char *filename = qdict_get_str(qdict, "target"); - const char *format = qdict_get_try_str(qdict, "format"); - bool reuse = qdict_get_try_bool(qdict, "reuse", false); - bool full = qdict_get_try_bool(qdict, "full", false); - bool compress = qdict_get_try_bool(qdict, "compress", false); - Error *err = NULL; - DriveBackup backup = { - .device = (char *)device, - .target = (char *)filename, - .has_format = !!format, - .format = (char *)format, - .sync = full ? MIRROR_SYNC_MODE_FULL : MIRROR_SYNC_MODE_TOP, - .has_mode = true, - .mode = reuse ? NEW_IMAGE_MODE_EXISTING : NEW_IMAGE_MODE_ABSOLUTE_PATHS, - .has_compress = !!compress, - .compress = compress, - }; - - if (!filename) { - error_setg(&err, QERR_MISSING_PARAMETER, "target"); - hmp_handle_error(mon, &err); - return; - } - - qmp_drive_backup(&backup, &err); - hmp_handle_error(mon, &err); -} - -void hmp_snapshot_blkdev(Monitor *mon, const QDict *qdict) -{ - const char *device = qdict_get_str(qdict, "device"); - const char *filename = qdict_get_try_str(qdict, "snapshot-file"); - const char *format = qdict_get_try_str(qdict, "format"); - bool reuse = qdict_get_try_bool(qdict, "reuse", false); - enum NewImageMode mode; - Error *err = NULL; - - if (!filename) { - /* In the future, if 'snapshot-file' is not specified, the snapshot - will be taken internally. Today it's actually required. */ - error_setg(&err, QERR_MISSING_PARAMETER, "snapshot-file"); - hmp_handle_error(mon, &err); - return; - } - - mode = reuse ? NEW_IMAGE_MODE_EXISTING : NEW_IMAGE_MODE_ABSOLUTE_PATHS; - qmp_blockdev_snapshot_sync(true, device, false, NULL, - filename, false, NULL, - !!format, format, - true, mode, &err); - hmp_handle_error(mon, &err); -} - -void hmp_snapshot_blkdev_internal(Monitor *mon, const QDict *qdict) -{ - const char *device = qdict_get_str(qdict, "device"); - const char *name = qdict_get_str(qdict, "name"); - Error *err = NULL; - - qmp_blockdev_snapshot_internal_sync(device, name, &err); - hmp_handle_error(mon, &err); -} - -void hmp_snapshot_delete_blkdev_internal(Monitor *mon, const QDict *qdict) -{ - const char *device = qdict_get_str(qdict, "device"); - const char *name = qdict_get_str(qdict, "name"); - const char *id = qdict_get_try_str(qdict, "id"); - Error *err = NULL; - - qmp_blockdev_snapshot_delete_internal_sync(device, !!id, id, - true, name, &err); - hmp_handle_error(mon, &err); + hmp_handle_error(mon, err); } void hmp_loadvm(Monitor *mon, const QDict *qdict) @@ -1454,7 +1090,7 @@ void hmp_loadvm(Monitor *mon, const QDict *qdict) if (load_snapshot(name, &err) == 0 && saved_vm_running) { vm_start(); } - hmp_handle_error(mon, &err); + hmp_handle_error(mon, err); } void hmp_savevm(Monitor *mon, const QDict *qdict) @@ -1462,7 +1098,7 @@ void hmp_savevm(Monitor *mon, const QDict *qdict) Error *err = NULL; save_snapshot(qdict_get_try_str(qdict, "name"), &err); - hmp_handle_error(mon, &err); + hmp_handle_error(mon, err); } void hmp_delvm(Monitor *mon, const QDict *qdict) @@ -1476,149 +1112,7 @@ void hmp_delvm(Monitor *mon, const QDict *qdict) "deleting snapshot on device '%s': ", bdrv_get_device_name(bs)); } - hmp_handle_error(mon, &err); -} - -void hmp_info_snapshots(Monitor *mon, const QDict *qdict) -{ - BlockDriverState *bs, *bs1; - BdrvNextIterator it1; - QEMUSnapshotInfo *sn_tab, *sn; - bool no_snapshot = true; - int nb_sns, i; - int total; - int *global_snapshots; - AioContext *aio_context; - - typedef struct SnapshotEntry { - QEMUSnapshotInfo sn; - QTAILQ_ENTRY(SnapshotEntry) next; - } SnapshotEntry; - - typedef struct ImageEntry { - const char *imagename; - QTAILQ_ENTRY(ImageEntry) next; - QTAILQ_HEAD(, SnapshotEntry) snapshots; - } ImageEntry; - - QTAILQ_HEAD(, ImageEntry) image_list = - QTAILQ_HEAD_INITIALIZER(image_list); - - ImageEntry *image_entry, *next_ie; - SnapshotEntry *snapshot_entry; - - bs = bdrv_all_find_vmstate_bs(); - if (!bs) { - monitor_printf(mon, "No available block device supports snapshots\n"); - return; - } - aio_context = bdrv_get_aio_context(bs); - - aio_context_acquire(aio_context); - nb_sns = bdrv_snapshot_list(bs, &sn_tab); - aio_context_release(aio_context); - - if (nb_sns < 0) { - monitor_printf(mon, "bdrv_snapshot_list: error %d\n", nb_sns); - return; - } - - for (bs1 = bdrv_first(&it1); bs1; bs1 = bdrv_next(&it1)) { - int bs1_nb_sns = 0; - ImageEntry *ie; - SnapshotEntry *se; - AioContext *ctx = bdrv_get_aio_context(bs1); - - aio_context_acquire(ctx); - if (bdrv_can_snapshot(bs1)) { - sn = NULL; - bs1_nb_sns = bdrv_snapshot_list(bs1, &sn); - if (bs1_nb_sns > 0) { - no_snapshot = false; - ie = g_new0(ImageEntry, 1); - ie->imagename = bdrv_get_device_name(bs1); - QTAILQ_INIT(&ie->snapshots); - QTAILQ_INSERT_TAIL(&image_list, ie, next); - for (i = 0; i < bs1_nb_sns; i++) { - se = g_new0(SnapshotEntry, 1); - se->sn = sn[i]; - QTAILQ_INSERT_TAIL(&ie->snapshots, se, next); - } - } - g_free(sn); - } - aio_context_release(ctx); - } - - if (no_snapshot) { - monitor_printf(mon, "There is no snapshot available.\n"); - return; - } - - global_snapshots = g_new0(int, nb_sns); - total = 0; - for (i = 0; i < nb_sns; i++) { - SnapshotEntry *next_sn; - if (bdrv_all_find_snapshot(sn_tab[i].name, &bs1) == 0) { - global_snapshots[total] = i; - total++; - QTAILQ_FOREACH(image_entry, &image_list, next) { - QTAILQ_FOREACH_SAFE(snapshot_entry, &image_entry->snapshots, - next, next_sn) { - if (!strcmp(sn_tab[i].name, snapshot_entry->sn.name)) { - QTAILQ_REMOVE(&image_entry->snapshots, snapshot_entry, - next); - g_free(snapshot_entry); - } - } - } - } - } - - monitor_printf(mon, "List of snapshots present on all disks:\n"); - - if (total > 0) { - bdrv_snapshot_dump(NULL); - monitor_printf(mon, "\n"); - for (i = 0; i < total; i++) { - sn = &sn_tab[global_snapshots[i]]; - /* The ID is not guaranteed to be the same on all images, so - * overwrite it. - */ - pstrcpy(sn->id_str, sizeof(sn->id_str), "--"); - bdrv_snapshot_dump(sn); - monitor_printf(mon, "\n"); - } - } else { - monitor_printf(mon, "None\n"); - } - - QTAILQ_FOREACH(image_entry, &image_list, next) { - if (QTAILQ_EMPTY(&image_entry->snapshots)) { - continue; - } - monitor_printf(mon, - "\nList of partial (non-loadable) snapshots on '%s':\n", - image_entry->imagename); - bdrv_snapshot_dump(NULL); - monitor_printf(mon, "\n"); - QTAILQ_FOREACH(snapshot_entry, &image_entry->snapshots, next) { - bdrv_snapshot_dump(&snapshot_entry->sn); - monitor_printf(mon, "\n"); - } - } - - QTAILQ_FOREACH_SAFE(image_entry, &image_list, next, next_ie) { - SnapshotEntry *next_sn; - QTAILQ_FOREACH_SAFE(snapshot_entry, &image_entry->snapshots, next, - next_sn) { - g_free(snapshot_entry); - } - g_free(image_entry); - } - g_free(sn_tab); - g_free(global_snapshots); - + hmp_handle_error(mon, err); } void hmp_announce_self(Monitor *mon, const QDict *qdict) @@ -1652,7 +1146,7 @@ void hmp_migrate_continue(Monitor *mon, const QDict *qdict) qmp_migrate_continue(val, &err); } - hmp_handle_error(mon, &err); + hmp_handle_error(mon, err); } void hmp_migrate_incoming(Monitor *mon, const QDict *qdict) @@ -1662,7 +1156,7 @@ void hmp_migrate_incoming(Monitor *mon, const QDict *qdict) qmp_migrate_incoming(uri, &err); - hmp_handle_error(mon, &err); + hmp_handle_error(mon, err); } void hmp_migrate_recover(Monitor *mon, const QDict *qdict) @@ -1672,7 +1166,7 @@ void hmp_migrate_recover(Monitor *mon, const QDict *qdict) qmp_migrate_recover(uri, &err); - hmp_handle_error(mon, &err); + hmp_handle_error(mon, err); } void hmp_migrate_pause(Monitor *mon, const QDict *qdict) @@ -1681,7 +1175,7 @@ void hmp_migrate_pause(Monitor *mon, const QDict *qdict) qmp_migrate_pause(&err); - hmp_handle_error(mon, &err); + hmp_handle_error(mon, err); } /* Kept for backwards compatibility */ @@ -1697,7 +1191,7 @@ void hmp_migrate_set_cache_size(Monitor *mon, const QDict *qdict) Error *err = NULL; qmp_migrate_set_cache_size(value, &err); - hmp_handle_error(mon, &err); + hmp_handle_error(mon, err); } /* Kept for backwards compatibility */ @@ -1728,7 +1222,7 @@ void hmp_migrate_set_capability(Monitor *mon, const QDict *qdict) end: qapi_free_MigrationCapabilityStatusList(caps); - hmp_handle_error(mon, &err); + hmp_handle_error(mon, err); } void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict) @@ -1739,6 +1233,7 @@ void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict) MigrateSetParameters *p = g_new0(MigrateSetParameters, 1); uint64_t valuebw = 0; uint64_t cache_size; + MultiFDCompression compress_type; Error *err = NULL; int val, ret; @@ -1764,6 +1259,10 @@ void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict) p->has_decompress_threads = true; visit_type_int(v, param, &p->decompress_threads, &err); break; + case MIGRATION_PARAMETER_THROTTLE_TRIGGER_THRESHOLD: + p->has_throttle_trigger_threshold = true; + visit_type_int(v, param, &p->throttle_trigger_threshold, &err); + break; case MIGRATION_PARAMETER_CPU_THROTTLE_INITIAL: p->has_cpu_throttle_initial = true; visit_type_int(v, param, &p->cpu_throttle_initial, &err); @@ -1824,6 +1323,22 @@ void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict) p->has_multifd_channels = true; visit_type_int(v, param, &p->multifd_channels, &err); break; + case MIGRATION_PARAMETER_MULTIFD_COMPRESSION: + p->has_multifd_compression = true; + visit_type_MultiFDCompression(v, param, &compress_type, &err); + if (err) { + break; + } + p->multifd_compression = compress_type; + break; + case MIGRATION_PARAMETER_MULTIFD_ZLIB_LEVEL: + p->has_multifd_zlib_level = true; + visit_type_int(v, param, &p->multifd_zlib_level, &err); + break; + case MIGRATION_PARAMETER_MULTIFD_ZSTD_LEVEL: + p->has_multifd_zstd_level = true; + visit_type_int(v, param, &p->multifd_zstd_level, &err); + break; case MIGRATION_PARAMETER_XBZRLE_CACHE_SIZE: p->has_xbzrle_cache_size = true; visit_type_size(v, param, &cache_size, &err); @@ -1869,7 +1384,7 @@ void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict) cleanup: qapi_free_MigrateSetParameters(p); visit_free(v); - hmp_handle_error(mon, &err); + hmp_handle_error(mon, err); } void hmp_client_migrate_info(Monitor *mon, const QDict *qdict) @@ -1886,14 +1401,14 @@ void hmp_client_migrate_info(Monitor *mon, const QDict *qdict) qmp_client_migrate_info(protocol, hostname, has_port, port, has_tls_port, tls_port, !!cert_subject, cert_subject, &err); - hmp_handle_error(mon, &err); + hmp_handle_error(mon, err); } void hmp_migrate_start_postcopy(Monitor *mon, const QDict *qdict) { Error *err = NULL; qmp_migrate_start_postcopy(&err); - hmp_handle_error(mon, &err); + hmp_handle_error(mon, err); } void hmp_x_colo_lost_heartbeat(Monitor *mon, const QDict *qdict) @@ -1901,7 +1416,7 @@ void hmp_x_colo_lost_heartbeat(Monitor *mon, const QDict *qdict) Error *err = NULL; qmp_x_colo_lost_heartbeat(&err); - hmp_handle_error(mon, &err); + hmp_handle_error(mon, err); } void hmp_set_password(Monitor *mon, const QDict *qdict) @@ -1912,7 +1427,7 @@ void hmp_set_password(Monitor *mon, const QDict *qdict) Error *err = NULL; qmp_set_password(protocol, password, !!connected, connected, &err); - hmp_handle_error(mon, &err); + hmp_handle_error(mon, err); } void hmp_expire_password(Monitor *mon, const QDict *qdict) @@ -1922,18 +1437,9 @@ void hmp_expire_password(Monitor *mon, const QDict *qdict) Error *err = NULL; qmp_expire_password(protocol, whenstr, &err); - hmp_handle_error(mon, &err); + hmp_handle_error(mon, err); } -void hmp_eject(Monitor *mon, const QDict *qdict) -{ - bool force = qdict_get_try_bool(qdict, "force", false); - const char *device = qdict_get_str(qdict, "device"); - Error *err = NULL; - - qmp_eject(true, device, false, NULL, true, force, &err); - hmp_handle_error(mon, &err); -} #ifdef CONFIG_VNC static void hmp_change_read_arg(void *opaque, const char *password, @@ -1978,7 +1484,7 @@ void hmp_change(Monitor *mon, const QDict *qdict) read_only, BLOCKDEV_CHANGE_READ_ONLY_MODE_RETAIN, &err); if (err) { - hmp_handle_error(mon, &err); + hmp_handle_error(mon, err); return; } } @@ -1988,102 +1494,7 @@ void hmp_change(Monitor *mon, const QDict *qdict) &err); } - hmp_handle_error(mon, &err); -} - -void hmp_block_set_io_throttle(Monitor *mon, const QDict *qdict) -{ - Error *err = NULL; - char *device = (char *) qdict_get_str(qdict, "device"); - BlockIOThrottle throttle = { - .bps = qdict_get_int(qdict, "bps"), - .bps_rd = qdict_get_int(qdict, "bps_rd"), - .bps_wr = qdict_get_int(qdict, "bps_wr"), - .iops = qdict_get_int(qdict, "iops"), - .iops_rd = qdict_get_int(qdict, "iops_rd"), - .iops_wr = qdict_get_int(qdict, "iops_wr"), - }; - - /* qmp_block_set_io_throttle has separate parameters for the - * (deprecated) block device name and the qdev ID but the HMP - * version has only one, so we must decide which one to pass. */ - if (blk_by_name(device)) { - throttle.has_device = true; - throttle.device = device; - } else { - throttle.has_id = true; - throttle.id = device; - } - - qmp_block_set_io_throttle(&throttle, &err); - hmp_handle_error(mon, &err); -} - -void hmp_block_stream(Monitor *mon, const QDict *qdict) -{ - Error *error = NULL; - const char *device = qdict_get_str(qdict, "device"); - const char *base = qdict_get_try_str(qdict, "base"); - int64_t speed = qdict_get_try_int(qdict, "speed", 0); - - qmp_block_stream(true, device, device, base != NULL, base, false, NULL, - false, NULL, qdict_haskey(qdict, "speed"), speed, true, - BLOCKDEV_ON_ERROR_REPORT, false, false, false, false, - &error); - - hmp_handle_error(mon, &error); -} - -void hmp_block_job_set_speed(Monitor *mon, const QDict *qdict) -{ - Error *error = NULL; - const char *device = qdict_get_str(qdict, "device"); - int64_t value = qdict_get_int(qdict, "speed"); - - qmp_block_job_set_speed(device, value, &error); - - hmp_handle_error(mon, &error); -} - -void hmp_block_job_cancel(Monitor *mon, const QDict *qdict) -{ - Error *error = NULL; - const char *device = qdict_get_str(qdict, "device"); - bool force = qdict_get_try_bool(qdict, "force", false); - - qmp_block_job_cancel(device, true, force, &error); - - hmp_handle_error(mon, &error); -} - -void hmp_block_job_pause(Monitor *mon, const QDict *qdict) -{ - Error *error = NULL; - const char *device = qdict_get_str(qdict, "device"); - - qmp_block_job_pause(device, &error); - - hmp_handle_error(mon, &error); -} - -void hmp_block_job_resume(Monitor *mon, const QDict *qdict) -{ - Error *error = NULL; - const char *device = qdict_get_str(qdict, "device"); - - qmp_block_job_resume(device, &error); - - hmp_handle_error(mon, &error); -} - -void hmp_block_job_complete(Monitor *mon, const QDict *qdict) -{ - Error *error = NULL; - const char *device = qdict_get_str(qdict, "device"); - - qmp_block_job_complete(device, &error); - - hmp_handle_error(mon, &error); + hmp_handle_error(mon, err); } typedef struct HMPMigrationStatus @@ -2143,7 +1554,7 @@ void hmp_migrate(Monitor *mon, const QDict *qdict) qmp_migrate(uri, !!blk, blk, !!inc, inc, false, false, true, resume, &err); if (err) { - hmp_handle_error(mon, &err); + hmp_handle_error(mon, err); return; } @@ -2181,7 +1592,7 @@ void hmp_netdev_add(Monitor *mon, const QDict *qdict) } out: - hmp_handle_error(mon, &err); + hmp_handle_error(mon, err); } void hmp_netdev_del(Monitor *mon, const QDict *qdict) @@ -2190,7 +1601,7 @@ void hmp_netdev_del(Monitor *mon, const QDict *qdict) Error *err = NULL; qmp_netdev_del(id, &err); - hmp_handle_error(mon, &err); + hmp_handle_error(mon, err); } void hmp_object_add(Monitor *mon, const QDict *qdict) @@ -2201,7 +1612,7 @@ void hmp_object_add(Monitor *mon, const QDict *qdict) opts = qemu_opts_from_qdict(qemu_find_opts("object"), qdict, &err); if (err) { - hmp_handle_error(mon, &err); + hmp_handle_error(mon, err); return; } @@ -2209,7 +1620,7 @@ void hmp_object_add(Monitor *mon, const QDict *qdict) qemu_opts_del(opts); if (err) { - hmp_handle_error(mon, &err); + hmp_handle_error(mon, err); } if (obj) { object_unref(obj); @@ -2222,7 +1633,7 @@ void hmp_getfd(Monitor *mon, const QDict *qdict) Error *err = NULL; qmp_getfd(fdname, &err); - hmp_handle_error(mon, &err); + hmp_handle_error(mon, err); } void hmp_closefd(Monitor *mon, const QDict *qdict) @@ -2231,7 +1642,7 @@ void hmp_closefd(Monitor *mon, const QDict *qdict) Error *err = NULL; qmp_closefd(fdname, &err); - hmp_handle_error(mon, &err); + hmp_handle_error(mon, err); } void hmp_sendkey(Monitor *mon, const QDict *qdict) @@ -2290,7 +1701,7 @@ void hmp_sendkey(Monitor *mon, const QDict *qdict) } qmp_send_key(head, has_hold_time, hold_time, &err); - hmp_handle_error(mon, &err); + hmp_handle_error(mon, err); out: qapi_free_KeyValueList(head); @@ -2309,93 +1720,7 @@ void hmp_screendump(Monitor *mon, const QDict *qdict) Error *err = NULL; qmp_screendump(filename, id != NULL, id, id != NULL, head, &err); - hmp_handle_error(mon, &err); -} - -void hmp_nbd_server_start(Monitor *mon, const QDict *qdict) -{ - const char *uri = qdict_get_str(qdict, "uri"); - bool writable = qdict_get_try_bool(qdict, "writable", false); - bool all = qdict_get_try_bool(qdict, "all", false); - Error *local_err = NULL; - BlockInfoList *block_list, *info; - SocketAddress *addr; - - if (writable && !all) { - error_setg(&local_err, "-w only valid together with -a"); - goto exit; - } - - /* First check if the address is valid and start the server. */ - addr = socket_parse(uri, &local_err); - if (local_err != NULL) { - goto exit; - } - - nbd_server_start(addr, NULL, NULL, &local_err); - qapi_free_SocketAddress(addr); - if (local_err != NULL) { - goto exit; - } - - if (!all) { - return; - } - - /* Then try adding all block devices. If one fails, close all and - * exit. - */ - block_list = qmp_query_block(NULL); - - for (info = block_list; info; info = info->next) { - if (!info->value->has_inserted) { - continue; - } - - qmp_nbd_server_add(info->value->device, false, NULL, - true, writable, false, NULL, &local_err); - - if (local_err != NULL) { - qmp_nbd_server_stop(NULL); - break; - } - } - - qapi_free_BlockInfoList(block_list); - -exit: - hmp_handle_error(mon, &local_err); -} - -void hmp_nbd_server_add(Monitor *mon, const QDict *qdict) -{ - const char *device = qdict_get_str(qdict, "device"); - const char *name = qdict_get_try_str(qdict, "name"); - bool writable = qdict_get_try_bool(qdict, "writable", false); - Error *local_err = NULL; - - qmp_nbd_server_add(device, !!name, name, true, writable, - false, NULL, &local_err); - hmp_handle_error(mon, &local_err); -} - -void hmp_nbd_server_remove(Monitor *mon, const QDict *qdict) -{ - const char *name = qdict_get_str(qdict, "name"); - bool force = qdict_get_try_bool(qdict, "force", false); - Error *err = NULL; - - /* Rely on NBD_SERVER_REMOVE_MODE_SAFE being the default */ - qmp_nbd_server_remove(name, force, NBD_SERVER_REMOVE_MODE_HARD, &err); - hmp_handle_error(mon, &err); -} - -void hmp_nbd_server_stop(Monitor *mon, const QDict *qdict) -{ - Error *err = NULL; - - qmp_nbd_server_stop(&err); - hmp_handle_error(mon, &err); + hmp_handle_error(mon, err); } void hmp_chardev_add(Monitor *mon, const QDict *qdict) @@ -2411,7 +1736,7 @@ void hmp_chardev_add(Monitor *mon, const QDict *qdict) qemu_chr_new_from_opts(opts, NULL, &err); qemu_opts_del(opts); } - hmp_handle_error(mon, &err); + hmp_handle_error(mon, err); } void hmp_chardev_change(Monitor *mon, const QDict *qdict) @@ -2445,7 +1770,7 @@ end: qapi_free_ChardevReturn(ret); qapi_free_ChardevBackend(backend); qemu_opts_del(opts); - hmp_handle_error(mon, &err); + hmp_handle_error(mon, err); } void hmp_chardev_remove(Monitor *mon, const QDict *qdict) @@ -2453,7 +1778,7 @@ void hmp_chardev_remove(Monitor *mon, const QDict *qdict) Error *local_err = NULL; qmp_chardev_remove(qdict_get_str(qdict, "id"), &local_err); - hmp_handle_error(mon, &local_err); + hmp_handle_error(mon, local_err); } void hmp_chardev_send_break(Monitor *mon, const QDict *qdict) @@ -2461,63 +1786,7 @@ void hmp_chardev_send_break(Monitor *mon, const QDict *qdict) Error *local_err = NULL; qmp_chardev_send_break(qdict_get_str(qdict, "id"), &local_err); - hmp_handle_error(mon, &local_err); -} - -void hmp_qemu_io(Monitor *mon, const QDict *qdict) -{ - BlockBackend *blk; - BlockBackend *local_blk = NULL; - const char* device = qdict_get_str(qdict, "device"); - const char* command = qdict_get_str(qdict, "command"); - Error *err = NULL; - int ret; - - blk = blk_by_name(device); - if (!blk) { - BlockDriverState *bs = bdrv_lookup_bs(NULL, device, &err); - if (bs) { - blk = local_blk = blk_new(bdrv_get_aio_context(bs), - 0, BLK_PERM_ALL); - ret = blk_insert_bs(blk, bs, &err); - if (ret < 0) { - goto fail; - } - } else { - goto fail; - } - } - - /* - * Notably absent: Proper permission management. This is sad, but it seems - * almost impossible to achieve without changing the semantics and thereby - * limiting the use cases of the qemu-io HMP command. - * - * In an ideal world we would unconditionally create a new BlockBackend for - * qemuio_command(), but we have commands like 'reopen' and want them to - * take effect on the exact BlockBackend whose name the user passed instead - * of just on a temporary copy of it. - * - * Another problem is that deleting the temporary BlockBackend involves - * draining all requests on it first, but some qemu-iotests cases want to - * issue multiple aio_read/write requests and expect them to complete in - * the background while the monitor has already returned. - * - * This is also what prevents us from saving the original permissions and - * restoring them later: We can't revoke permissions until all requests - * have completed, and we don't know when that is nor can we really let - * anything else run before we have revoken them to avoid race conditions. - * - * What happens now is that command() in qemu-io-cmds.c can extend the - * permissions if necessary for the qemu-io command. And they simply stay - * extended, possibly resulting in a read-only guest device keeping write - * permissions. Ugly, but it appears to be the lesser evil. - */ - qemuio_command(blk, command); - -fail: - blk_unref(local_blk); - hmp_handle_error(mon, &err); + hmp_handle_error(mon, local_err); } void hmp_object_del(Monitor *mon, const QDict *qdict) @@ -2526,7 +1795,7 @@ void hmp_object_del(Monitor *mon, const QDict *qdict) Error *err = NULL; user_creatable_del(id, &err); - hmp_handle_error(mon, &err); + hmp_handle_error(mon, err); } void hmp_info_memory_devices(Monitor *mon, const QDict *qdict) @@ -2576,7 +1845,7 @@ void hmp_info_memory_devices(Monitor *mon, const QDict *qdict) } qapi_free_MemoryDeviceInfoList(info_list); - hmp_handle_error(mon, &err); + hmp_handle_error(mon, err); } void hmp_info_iothreads(Monitor *mon, const QDict *qdict) @@ -2605,7 +1874,7 @@ void hmp_rocker(Monitor *mon, const QDict *qdict) rocker = qmp_query_rocker(name, &err); if (err != NULL) { - hmp_handle_error(mon, &err); + hmp_handle_error(mon, err); return; } @@ -2624,7 +1893,7 @@ void hmp_rocker_ports(Monitor *mon, const QDict *qdict) list = qmp_query_rocker_ports(name, &err); if (err != NULL) { - hmp_handle_error(mon, &err); + hmp_handle_error(mon, err); return; } @@ -2653,7 +1922,7 @@ void hmp_rocker_of_dpa_flows(Monitor *mon, const QDict *qdict) list = qmp_query_rocker_of_dpa_flows(name, tbl_id != -1, tbl_id, &err); if (err != NULL) { - hmp_handle_error(mon, &err); + hmp_handle_error(mon, err); return; } @@ -2800,11 +2069,10 @@ void hmp_rocker_of_dpa_groups(Monitor *mon, const QDict *qdict) const char *name = qdict_get_str(qdict, "name"); uint8_t type = qdict_get_try_int(qdict, "type", 9); Error *err = NULL; - bool set = false; list = qmp_query_rocker_of_dpa_groups(name, type != 9, type, &err); if (err != NULL) { - hmp_handle_error(mon, &err); + hmp_handle_error(mon, err); return; } @@ -2812,6 +2080,7 @@ void hmp_rocker_of_dpa_groups(Monitor *mon, const QDict *qdict) for (g = list; g; g = g->next) { RockerOfDpaGroup *group = g->value; + bool set = false; monitor_printf(mon, "0x%08x", group->id); @@ -2856,14 +2125,11 @@ void hmp_rocker_of_dpa_groups(Monitor *mon, const QDict *qdict) if (group->has_set_eth_dst) { if (!set) { - set = true; monitor_printf(mon, " set"); } monitor_printf(mon, " dst %s", group->set_eth_dst); } - set = false; - if (group->has_ttl_check && group->ttl_check) { monitor_printf(mon, " check TTL"); } @@ -2911,7 +2177,7 @@ void hmp_info_vm_generation_id(Monitor *mon, const QDict *qdict) if (info) { monitor_printf(mon, "%s\n", info->guid); } - hmp_handle_error(mon, &err); + hmp_handle_error(mon, err); qapi_free_GuidInfo(info); } @@ -2930,5 +2196,5 @@ void hmp_info_memory_size_summary(Monitor *mon, const QDict *qdict) qapi_free_MemoryInfo(info); } - hmp_handle_error(mon, &err); + hmp_handle_error(mon, err); } diff --git a/monitor/hmp.c b/monitor/hmp.c index 8942e28933..d598dd02bb 100644 --- a/monitor/hmp.c +++ b/monitor/hmp.c @@ -1322,7 +1322,7 @@ static void monitor_read(void *opaque, const uint8_t *buf, int size) cur_mon = old_mon; } -static void monitor_event(void *opaque, int event) +static void monitor_event(void *opaque, QEMUChrEvent event) { Monitor *mon = opaque; MonitorHMP *hmp_mon = container_of(mon, MonitorHMP, common); @@ -1371,6 +1371,10 @@ static void monitor_event(void *opaque, int event) mon_refcount--; monitor_fdsets_cleanup(); break; + + case CHR_EVENT_BREAK: + /* Ignored */ + break; } } @@ -1395,12 +1399,16 @@ static void monitor_readline_flush(void *opaque) monitor_flush(&mon->common); } -void monitor_init_hmp(Chardev *chr, bool use_readline) +void monitor_init_hmp(Chardev *chr, bool use_readline, Error **errp) { MonitorHMP *mon = g_new0(MonitorHMP, 1); + if (!qemu_chr_fe_init(&mon->common.chr, chr, errp)) { + g_free(mon); + return; + } + monitor_data_init(&mon->common, false, false, false); - qemu_chr_fe_init(&mon->common.chr, chr, &error_abort); mon->use_readline = use_readline; if (mon->use_readline) { diff --git a/monitor/misc.c b/monitor/misc.c index 3baa15f3bf..6c45fa490f 100644 --- a/monitor/misc.c +++ b/monitor/misc.c @@ -66,11 +66,16 @@ #include "qemu/option.h" #include "qemu/thread.h" #include "block/qapi.h" -#include "qapi/qapi-commands.h" -#include "qapi/qapi-emit-events.h" +#include "block/block-hmp-cmds.h" +#include "qapi/qapi-commands-char.h" +#include "qapi/qapi-commands-control.h" +#include "qapi/qapi-commands-migration.h" +#include "qapi/qapi-commands-misc.h" +#include "qapi/qapi-commands-qom.h" +#include "qapi/qapi-commands-trace.h" +#include "qapi/qapi-init-commands.h" #include "qapi/error.h" #include "qapi/qmp-event.h" -#include "qapi/qapi-introspect.h" #include "sysemu/cpus.h" #include "qemu/cutils.h" #include "tcg/tcg.h" @@ -106,9 +111,6 @@ struct MonFdset { QLIST_ENTRY(MonFdset) next; }; -/* QMP checker flags */ -#define QMP_ACCEPT_UNKNOWNS 1 - /* Protects mon_fdsets */ static QemuMutex mon_fdsets_lock; static QLIST_HEAD(, MonFdset) mon_fdsets; @@ -230,73 +232,6 @@ static void hmp_info_help(Monitor *mon, const QDict *qdict) help_cmd(mon, "info"); } -static void query_commands_cb(QmpCommand *cmd, void *opaque) -{ - CommandInfoList *info, **list = opaque; - - if (!cmd->enabled) { - return; - } - - info = g_malloc0(sizeof(*info)); - info->value = g_malloc0(sizeof(*info->value)); - info->value->name = g_strdup(cmd->name); - info->next = *list; - *list = info; -} - -CommandInfoList *qmp_query_commands(Error **errp) -{ - CommandInfoList *list = NULL; - MonitorQMP *mon; - - assert(monitor_is_qmp(cur_mon)); - mon = container_of(cur_mon, MonitorQMP, common); - - qmp_for_each_command(mon->commands, query_commands_cb, &list); - - return list; -} - -EventInfoList *qmp_query_events(Error **errp) -{ - /* - * TODO This deprecated command is the only user of - * QAPIEvent_str() and QAPIEvent_lookup[]. When the command goes, - * they should go, too. - */ - EventInfoList *info, *ev_list = NULL; - QAPIEvent e; - - for (e = 0 ; e < QAPI_EVENT__MAX ; e++) { - const char *event_name = QAPIEvent_str(e); - assert(event_name != NULL); - info = g_malloc0(sizeof(*info)); - info->value = g_malloc0(sizeof(*info->value)); - info->value->name = g_strdup(event_name); - - info->next = ev_list; - ev_list = info; - } - - return ev_list; -} - -/* - * Minor hack: generated marshalling suppressed for this command - * ('gen': false in the schema) so we can parse the JSON string - * directly into QObject instead of first parsing it with - * visit_type_SchemaInfoList() into a SchemaInfoList, then marshal it - * to QObject with generated output marshallers, every time. Instead, - * we do it in test-qobject-input-visitor.c, just to make sure - * qapi-gen.py's output actually conforms to the schema. - */ -static void qmp_query_qmp_schema(QDict *qdict, QObject **ret_data, - Error **errp) -{ - *ret_data = qobject_from_qlit(&qmp_schema_qlit); -} - static void monitor_init_qmp_commands(void) { /* @@ -312,7 +247,7 @@ static void monitor_init_qmp_commands(void) qmp_query_qmp_schema, QCO_ALLOW_PRECONFIG); qmp_register_command(&qmp_commands, "device_add", qmp_device_add, QCO_NO_OPTIONS); - qmp_register_command(&qmp_commands, "netdev_add", qmp_netdev_add, + qmp_register_command(&qmp_commands, "object-add", qmp_object_add, QCO_NO_OPTIONS); QTAILQ_INIT(&qmp_cap_negotiation_commands); @@ -320,63 +255,6 @@ static void monitor_init_qmp_commands(void) qmp_marshal_qmp_capabilities, QCO_ALLOW_PRECONFIG); } -/* - * Accept QMP capabilities in @list for @mon. - * On success, set mon->qmp.capab[], and return true. - * On error, set @errp, and return false. - */ -static bool qmp_caps_accept(MonitorQMP *mon, QMPCapabilityList *list, - Error **errp) -{ - GString *unavailable = NULL; - bool capab[QMP_CAPABILITY__MAX]; - - memset(capab, 0, sizeof(capab)); - - for (; list; list = list->next) { - if (!mon->capab_offered[list->value]) { - if (!unavailable) { - unavailable = g_string_new(QMPCapability_str(list->value)); - } else { - g_string_append_printf(unavailable, ", %s", - QMPCapability_str(list->value)); - } - } - capab[list->value] = true; - } - - if (unavailable) { - error_setg(errp, "Capability %s not available", unavailable->str); - g_string_free(unavailable, true); - return false; - } - - memcpy(mon->capab, capab, sizeof(capab)); - return true; -} - -void qmp_qmp_capabilities(bool has_enable, QMPCapabilityList *enable, - Error **errp) -{ - MonitorQMP *mon; - - assert(monitor_is_qmp(cur_mon)); - mon = container_of(cur_mon, MonitorQMP, common); - - if (mon->commands == &qmp_commands) { - error_set(errp, ERROR_CLASS_COMMAND_NOT_FOUND, - "Capabilities negotiation is already complete, command " - "ignored"); - return; - } - - if (!qmp_caps_accept(mon, enable, errp)) { - return; - } - - mon->commands = &qmp_commands; -} - /* Set the current CPU defined by the user. Callers must hold BQL. */ int monitor_set_cpu(int cpu_index) { @@ -394,7 +272,7 @@ int monitor_set_cpu(int cpu_index) /* Callers must hold BQL. */ static CPUState *mon_get_cpu_sync(bool synchronize) { - CPUState *cpu; + CPUState *cpu = NULL; if (cur_mon->mon_cpu_path) { cpu = (CPUState *) object_resolve_path_type(cur_mon->mon_cpu_path, @@ -411,6 +289,7 @@ static CPUState *mon_get_cpu_sync(bool synchronize) monitor_set_cpu(first_cpu->cpu_index); cpu = first_cpu; } + assert(cpu != NULL); if (synchronize) { cpu_synchronize_state(cpu); } @@ -1746,6 +1625,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); } @@ -1956,6 +1836,32 @@ void object_add_completion(ReadLineState *rs, int nb_args, const char *str) g_slist_free(list); } +static int qdev_add_hotpluggable_device(Object *obj, void *opaque) +{ + GSList **list = opaque; + DeviceState *dev = (DeviceState *)object_dynamic_cast(OBJECT(obj), + TYPE_DEVICE); + + if (dev == NULL) { + return 0; + } + + if (dev->realized && object_property_get_bool(obj, "hotpluggable", NULL)) { + *list = g_slist_append(*list, dev); + } + + return 0; +} + +static GSList *qdev_build_hotpluggable_device_list(Object *peripheral) +{ + GSList *list = NULL; + + object_child_foreach(peripheral, qdev_add_hotpluggable_device, &list); + + return list; +} + static void peripheral_device_del_completion(ReadLineState *rs, const char *str, size_t len) { @@ -2129,13 +2035,11 @@ void netdev_del_completion(ReadLineState *rs, int nb_args, const char *str) count = qemu_find_net_clients_except(NULL, ncs, NET_CLIENT_DRIVER_NIC, MAX_QUEUE_NUM); for (i = 0; i < MIN(count, MAX_QUEUE_NUM); i++) { - QemuOpts *opts; const char *name = ncs[i]->name; if (strncmp(str, name, len)) { continue; } - opts = qemu_opts_find(qemu_find_opts_err("netdev", NULL), name); - if (opts) { + if (ncs[i]->is_netdev) { readline_add_completion(rs, name); } } diff --git a/monitor/monitor-internal.h b/monitor/monitor-internal.h index d78f5ca190..8f60ccc70a 100644 --- a/monitor/monitor-internal.h +++ b/monitor/monitor-internal.h @@ -27,6 +27,7 @@ #include "chardev/char-fe.h" #include "monitor/monitor.h" +#include "qapi/qapi-types-control.h" #include "qapi/qmp/dispatch.h" #include "qapi/qmp/json-parser.h" #include "qemu/readline.h" @@ -132,7 +133,7 @@ typedef struct { * qmp_capabilities succeeds, we go into command mode, and * @command becomes &qmp_commands. */ - QmpCommandList *commands; + const QmpCommandList *commands; bool capab_offered[QMP_CAPABILITY__MAX]; /* capabilities offered */ bool capab[QMP_CAPABILITY__MAX]; /* offered and accepted */ /* @@ -179,4 +180,7 @@ void help_cmd(Monitor *mon, const char *name); void handle_hmp_command(MonitorHMP *mon, const char *cmdline); int hmp_compare_cmd(const char *name, const char *list); +void qmp_query_qmp_schema(QDict *qdict, QObject **ret_data, + Error **errp); + #endif diff --git a/monitor/monitor.c b/monitor/monitor.c index 12898b6448..125494410a 100644 --- a/monitor/monitor.c +++ b/monitor/monitor.c @@ -25,7 +25,9 @@ #include "qemu/osdep.h" #include "monitor-internal.h" #include "qapi/error.h" +#include "qapi/opts-visitor.h" #include "qapi/qapi-emit-events.h" +#include "qapi/qapi-visit-control.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qstring.h" #include "qemu/error-report.h" @@ -609,6 +611,72 @@ void monitor_init_globals_core(void) NULL); } +int monitor_init(MonitorOptions *opts, bool allow_hmp, Error **errp) +{ + Chardev *chr; + Error *local_err = NULL; + + chr = qemu_chr_find(opts->chardev); + if (chr == NULL) { + error_setg(errp, "chardev \"%s\" not found", opts->chardev); + return -1; + } + + if (!opts->has_mode) { + opts->mode = allow_hmp ? MONITOR_MODE_READLINE : MONITOR_MODE_CONTROL; + } + + switch (opts->mode) { + case MONITOR_MODE_CONTROL: + monitor_init_qmp(chr, opts->pretty, &local_err); + break; + case MONITOR_MODE_READLINE: + if (!allow_hmp) { + error_setg(errp, "Only QMP is supported"); + return -1; + } + if (opts->pretty) { + warn_report("'pretty' is deprecated for HMP monitors, it has no " + "effect and will be removed in future versions"); + } + monitor_init_hmp(chr, true, &local_err); + break; + default: + g_assert_not_reached(); + } + + if (local_err) { + error_propagate(errp, local_err); + return -1; + } + return 0; +} + +int monitor_init_opts(QemuOpts *opts, Error **errp) +{ + Visitor *v; + MonitorOptions *options; + Error *local_err = NULL; + + v = opts_visitor_new(opts); + visit_type_MonitorOptions(v, NULL, &options, &local_err); + visit_free(v); + + if (local_err) { + goto out; + } + + monitor_init(options, true, &local_err); + qapi_free_MonitorOptions(options); + +out: + if (local_err) { + error_propagate(errp, local_err); + return -1; + } + return 0; +} + QemuOptsList qemu_mon_opts = { .name = "mon", .implied_opt_name = "chardev", diff --git a/monitor/qmp-cmds-control.c b/monitor/qmp-cmds-control.c new file mode 100644 index 0000000000..8f04cfa6e6 --- /dev/null +++ b/monitor/qmp-cmds-control.c @@ -0,0 +1,169 @@ +/* + * QMP commands related to the monitor (common to sysemu and tools) + * + * Copyright (c) 2003-2004 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" + +#include "monitor-internal.h" +#include "qemu-version.h" +#include "qapi/error.h" +#include "qapi/qapi-commands-control.h" +#include "qapi/qapi-emit-events.h" +#include "qapi/qapi-introspect.h" + +/* + * Accept QMP capabilities in @list for @mon. + * On success, set mon->qmp.capab[], and return true. + * On error, set @errp, and return false. + */ +static bool qmp_caps_accept(MonitorQMP *mon, QMPCapabilityList *list, + Error **errp) +{ + GString *unavailable = NULL; + bool capab[QMP_CAPABILITY__MAX]; + + memset(capab, 0, sizeof(capab)); + + for (; list; list = list->next) { + if (!mon->capab_offered[list->value]) { + if (!unavailable) { + unavailable = g_string_new(QMPCapability_str(list->value)); + } else { + g_string_append_printf(unavailable, ", %s", + QMPCapability_str(list->value)); + } + } + capab[list->value] = true; + } + + if (unavailable) { + error_setg(errp, "Capability %s not available", unavailable->str); + g_string_free(unavailable, true); + return false; + } + + memcpy(mon->capab, capab, sizeof(capab)); + return true; +} + +void qmp_qmp_capabilities(bool has_enable, QMPCapabilityList *enable, + Error **errp) +{ + MonitorQMP *mon; + + assert(monitor_is_qmp(cur_mon)); + mon = container_of(cur_mon, MonitorQMP, common); + + if (mon->commands == &qmp_commands) { + error_set(errp, ERROR_CLASS_COMMAND_NOT_FOUND, + "Capabilities negotiation is already complete, command " + "ignored"); + return; + } + + if (!qmp_caps_accept(mon, enable, errp)) { + return; + } + + mon->commands = &qmp_commands; +} + +VersionInfo *qmp_query_version(Error **errp) +{ + VersionInfo *info = g_new0(VersionInfo, 1); + + info->qemu = g_new0(VersionTriple, 1); + info->qemu->major = QEMU_VERSION_MAJOR; + info->qemu->minor = QEMU_VERSION_MINOR; + info->qemu->micro = QEMU_VERSION_MICRO; + info->package = g_strdup(QEMU_PKGVERSION); + + return info; +} + +static void query_commands_cb(const QmpCommand *cmd, void *opaque) +{ + CommandInfoList *info, **list = opaque; + + if (!cmd->enabled) { + return; + } + + info = g_malloc0(sizeof(*info)); + info->value = g_malloc0(sizeof(*info->value)); + info->value->name = g_strdup(cmd->name); + info->next = *list; + *list = info; +} + +CommandInfoList *qmp_query_commands(Error **errp) +{ + CommandInfoList *list = NULL; + MonitorQMP *mon; + + assert(monitor_is_qmp(cur_mon)); + mon = container_of(cur_mon, MonitorQMP, common); + + qmp_for_each_command(mon->commands, query_commands_cb, &list); + + return list; +} + +EventInfoList *qmp_query_events(Error **errp) +{ + /* + * TODO This deprecated command is the only user of + * QAPIEvent_str() and QAPIEvent_lookup[]. When the command goes, + * they should go, too. + */ + EventInfoList *info, *ev_list = NULL; + QAPIEvent e; + + for (e = 0 ; e < QAPI_EVENT__MAX ; e++) { + const char *event_name = QAPIEvent_str(e); + assert(event_name != NULL); + info = g_malloc0(sizeof(*info)); + info->value = g_malloc0(sizeof(*info->value)); + info->value->name = g_strdup(event_name); + + info->next = ev_list; + ev_list = info; + } + + return ev_list; +} + +/* + * Minor hack: generated marshalling suppressed for this command + * ('gen': false in the schema) so we can parse the JSON string + * directly into QObject instead of first parsing it with + * visit_type_SchemaInfoList() into a SchemaInfoList, then marshal it + * to QObject with generated output marshallers, every time. Instead, + * we do it in test-qobject-input-visitor.c, just to make sure + * qapi-gen.py's output actually conforms to the schema. + */ +void qmp_query_qmp_schema(QDict *qdict, QObject **ret_data, + Error **errp) +{ + *ret_data = qobject_from_qlit(&qmp_schema_qlit); +} diff --git a/monitor/qmp-cmds.c b/monitor/qmp-cmds.c index 0880341a2d..864cbfa32e 100644 --- a/monitor/qmp-cmds.c +++ b/monitor/qmp-cmds.c @@ -15,7 +15,6 @@ #include "qemu/osdep.h" #include "qemu-common.h" -#include "qemu-version.h" #include "qemu/cutils.h" #include "qemu/option.h" #include "monitor/monitor.h" @@ -31,7 +30,8 @@ #include "sysemu/blockdev.h" #include "sysemu/block-backend.h" #include "qapi/error.h" -#include "qapi/qapi-commands-block-core.h" +#include "qapi/qapi-commands-block.h" +#include "qapi/qapi-commands-control.h" #include "qapi/qapi-commands-machine.h" #include "qapi/qapi-commands-misc.h" #include "qapi/qapi-commands-ui.h" @@ -51,19 +51,6 @@ NameInfo *qmp_query_name(Error **errp) return info; } -VersionInfo *qmp_query_version(Error **errp) -{ - VersionInfo *info = g_new0(VersionInfo, 1); - - info->qemu = g_new0(VersionTriple, 1); - info->qemu->major = QEMU_VERSION_MAJOR; - info->qemu->minor = QEMU_VERSION_MINOR; - info->qemu->micro = QEMU_VERSION_MICRO; - info->package = g_strdup(QEMU_PKGVERSION); - - return info; -} - KvmInfo *qmp_query_kvm(Error **errp) { KvmInfo *info = g_malloc0(sizeof(*info)); @@ -109,7 +96,7 @@ void qmp_system_reset(Error **errp) qemu_system_reset_request(SHUTDOWN_CAUSE_HOST_QMP_SYSTEM_RESET); } -void qmp_system_powerdown(Error **erp) +void qmp_system_powerdown(Error **errp) { qemu_system_powerdown_request(); } diff --git a/monitor/qmp.c b/monitor/qmp.c index b67a8e7d1f..f89e7daf27 100644 --- a/monitor/qmp.c +++ b/monitor/qmp.c @@ -27,7 +27,7 @@ #include "chardev/char-io.h" #include "monitor-internal.h" #include "qapi/error.h" -#include "qapi/qapi-commands-misc.h" +#include "qapi/qapi-commands-control.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qjson.h" #include "qapi/qmp/qlist.h" @@ -337,7 +337,7 @@ static QDict *qmp_greeting(MonitorQMP *mon) ver, cap_list); } -static void monitor_qmp_event(void *opaque, int event) +static void monitor_qmp_event(void *opaque, QEMUChrEvent event) { QDict *data; MonitorQMP *mon = opaque; @@ -365,6 +365,11 @@ static void monitor_qmp_event(void *opaque, int event) mon_refcount--; monitor_fdsets_cleanup(); break; + case CHR_EVENT_BREAK: + case CHR_EVENT_MUX_IN: + case CHR_EVENT_MUX_OUT: + /* Ignore */ + break; } } @@ -390,10 +395,16 @@ static void monitor_qmp_setup_handlers_bh(void *opaque) monitor_list_append(&mon->common); } -void monitor_init_qmp(Chardev *chr, bool pretty) +void monitor_init_qmp(Chardev *chr, bool pretty, Error **errp) { MonitorQMP *mon = g_new0(MonitorQMP, 1); + if (!qemu_chr_fe_init(&mon->common.chr, chr, errp)) { + g_free(mon); + return; + } + qemu_chr_fe_set_echo(&mon->common.chr, true); + /* Note: we run QMP monitor in I/O thread when @chr supports that */ monitor_data_init(&mon->common, true, false, qemu_chr_has_feature(chr, QEMU_CHAR_FEATURE_GCONTEXT)); @@ -403,9 +414,6 @@ void monitor_init_qmp(Chardev *chr, bool pretty) qemu_mutex_init(&mon->qmp_queue_lock); mon->qmp_requests = g_queue_new(); - qemu_chr_fe_init(&mon->common.chr, chr, &error_abort); - qemu_chr_fe_set_echo(&mon->common.chr, true); - json_message_parser_init(&mon->parser, handle_qmp_command, mon, NULL); if (mon->common.use_io_thread) { /* diff --git a/nbd/server.c b/nbd/server.c index 24ebc1a805..02b1ed0801 100644 --- a/nbd/server.c +++ b/nbd/server.c @@ -1909,27 +1909,98 @@ static int coroutine_fn nbd_co_send_sparse_read(NBDClient *client, return ret; } -/* - * Populate @extents from block status. Update @bytes to be the actual - * length encoded (which may be smaller than the original), and update - * @nb_extents to the number of extents used. - * - * Returns zero on success and -errno on bdrv_block_status_above failure. - */ -static int blockstatus_to_extents(BlockDriverState *bs, uint64_t offset, - uint64_t *bytes, NBDExtent *extents, - unsigned int *nb_extents) -{ - uint64_t remaining_bytes = *bytes; - NBDExtent *extent = extents, *extents_end = extents + *nb_extents; - bool first_extent = true; +typedef struct NBDExtentArray { + NBDExtent *extents; + unsigned int nb_alloc; + unsigned int count; + uint64_t total_length; + bool can_add; + bool converted_to_be; +} NBDExtentArray; - assert(*nb_extents); - while (remaining_bytes) { +static NBDExtentArray *nbd_extent_array_new(unsigned int nb_alloc) +{ + NBDExtentArray *ea = g_new0(NBDExtentArray, 1); + + ea->nb_alloc = nb_alloc; + ea->extents = g_new(NBDExtent, nb_alloc); + ea->can_add = true; + + return ea; +} + +static void nbd_extent_array_free(NBDExtentArray *ea) +{ + g_free(ea->extents); + g_free(ea); +} +G_DEFINE_AUTOPTR_CLEANUP_FUNC(NBDExtentArray, nbd_extent_array_free); + +/* Further modifications of the array after conversion are abandoned */ +static void nbd_extent_array_convert_to_be(NBDExtentArray *ea) +{ + int i; + + assert(!ea->converted_to_be); + ea->can_add = false; + ea->converted_to_be = true; + + for (i = 0; i < ea->count; i++) { + ea->extents[i].flags = cpu_to_be32(ea->extents[i].flags); + ea->extents[i].length = cpu_to_be32(ea->extents[i].length); + } +} + +/* + * Add extent to NBDExtentArray. If extent can't be added (no available space), + * return -1. + * For safety, when returning -1 for the first time, .can_add is set to false, + * further call to nbd_extent_array_add() will crash. + * (to avoid the situation, when after failing to add an extent (returned -1), + * user miss this failure and add another extent, which is successfully added + * (array is full, but new extent may be squashed into the last one), then we + * have invalid array with skipped extent) + */ +static int nbd_extent_array_add(NBDExtentArray *ea, + uint32_t length, uint32_t flags) +{ + assert(ea->can_add); + + if (!length) { + return 0; + } + + /* Extend previous extent if flags are the same */ + if (ea->count > 0 && flags == ea->extents[ea->count - 1].flags) { + uint64_t sum = (uint64_t)length + ea->extents[ea->count - 1].length; + + if (sum <= UINT32_MAX) { + ea->extents[ea->count - 1].length = sum; + ea->total_length += length; + return 0; + } + } + + if (ea->count >= ea->nb_alloc) { + ea->can_add = false; + return -1; + } + + ea->total_length += length; + ea->extents[ea->count] = (NBDExtent) {.length = length, .flags = flags}; + ea->count++; + + return 0; +} + +static int blockstatus_to_extents(BlockDriverState *bs, uint64_t offset, + uint64_t bytes, NBDExtentArray *ea) +{ + while (bytes) { uint32_t flags; int64_t num; - int ret = bdrv_block_status_above(bs, NULL, offset, remaining_bytes, - &num, NULL, NULL); + int ret = bdrv_block_status_above(bs, NULL, offset, bytes, &num, + NULL, NULL); if (ret < 0) { return ret; @@ -1938,60 +2009,37 @@ static int blockstatus_to_extents(BlockDriverState *bs, uint64_t offset, flags = (ret & BDRV_BLOCK_ALLOCATED ? 0 : NBD_STATE_HOLE) | (ret & BDRV_BLOCK_ZERO ? NBD_STATE_ZERO : 0); - if (first_extent) { - extent->flags = flags; - extent->length = num; - first_extent = false; - } else if (flags == extent->flags) { - /* extend current extent */ - extent->length += num; - } else { - if (extent + 1 == extents_end) { - break; - } - - /* start new extent */ - extent++; - extent->flags = flags; - extent->length = num; + if (nbd_extent_array_add(ea, num, flags) < 0) { + return 0; } + offset += num; - remaining_bytes -= num; + bytes -= num; } - extents_end = extent + 1; - - for (extent = extents; extent < extents_end; extent++) { - extent->flags = cpu_to_be32(extent->flags); - extent->length = cpu_to_be32(extent->length); - } - - *bytes -= remaining_bytes; - *nb_extents = extents_end - extents; - return 0; } -/* nbd_co_send_extents +/* + * nbd_co_send_extents * - * @length is only for tracing purposes (and may be smaller or larger - * than the client's original request). @last controls whether - * NBD_REPLY_FLAG_DONE is sent. @extents should already be in - * big-endian format. + * @ea is converted to BE by the function + * @last controls whether NBD_REPLY_FLAG_DONE is sent. */ static int nbd_co_send_extents(NBDClient *client, uint64_t handle, - NBDExtent *extents, unsigned int nb_extents, - uint64_t length, bool last, - uint32_t context_id, Error **errp) + NBDExtentArray *ea, + bool last, uint32_t context_id, Error **errp) { NBDStructuredMeta chunk; - struct iovec iov[] = { {.iov_base = &chunk, .iov_len = sizeof(chunk)}, - {.iov_base = extents, .iov_len = nb_extents * sizeof(extents[0])} + {.iov_base = ea->extents, .iov_len = ea->count * sizeof(ea->extents[0])} }; - trace_nbd_co_send_extents(handle, nb_extents, context_id, length, last); + nbd_extent_array_convert_to_be(ea); + + trace_nbd_co_send_extents(handle, ea->count, context_id, ea->total_length, + last); set_be_chunk(&chunk.h, last ? NBD_REPLY_FLAG_DONE : 0, NBD_REPLY_TYPE_BLOCK_STATUS, handle, sizeof(chunk) - sizeof(chunk.h) + iov[1].iov_len); @@ -2009,82 +2057,47 @@ static int nbd_co_send_block_status(NBDClient *client, uint64_t handle, { int ret; unsigned int nb_extents = dont_fragment ? 1 : NBD_MAX_BLOCK_STATUS_EXTENTS; - NBDExtent *extents = g_new(NBDExtent, nb_extents); - uint64_t final_length = length; + g_autoptr(NBDExtentArray) ea = nbd_extent_array_new(nb_extents); - ret = blockstatus_to_extents(bs, offset, &final_length, extents, - &nb_extents); + ret = blockstatus_to_extents(bs, offset, length, ea); if (ret < 0) { - g_free(extents); return nbd_co_send_structured_error( client, handle, -ret, "can't get block status", errp); } - ret = nbd_co_send_extents(client, handle, extents, nb_extents, - final_length, last, context_id, errp); - - g_free(extents); - - return ret; + return nbd_co_send_extents(client, handle, ea, last, context_id, errp); } -/* - * Populate @extents from a dirty bitmap. Unless @dont_fragment, the - * final extent may exceed the original @length. Store in @length the - * byte length encoded (which may be smaller or larger than the - * original), and return the number of extents used. - */ -static unsigned int bitmap_to_extents(BdrvDirtyBitmap *bitmap, uint64_t offset, - uint64_t *length, NBDExtent *extents, - unsigned int nb_extents, - bool dont_fragment) +/* Populate @ea from a dirty bitmap. */ +static void bitmap_to_extents(BdrvDirtyBitmap *bitmap, + uint64_t offset, uint64_t length, + NBDExtentArray *es) { - uint64_t begin = offset, end = offset; - uint64_t overall_end = offset + *length; - unsigned int i = 0; - BdrvDirtyBitmapIter *it; - bool dirty; + int64_t start, dirty_start, dirty_count; + int64_t end = offset + length; + bool full = false; bdrv_dirty_bitmap_lock(bitmap); - it = bdrv_dirty_iter_new(bitmap); - dirty = bdrv_dirty_bitmap_get_locked(bitmap, offset); - - assert(begin < overall_end && nb_extents); - while (begin < overall_end && i < nb_extents) { - bool next_dirty = !dirty; - - if (dirty) { - end = bdrv_dirty_bitmap_next_zero(bitmap, begin, UINT64_MAX); - } else { - bdrv_set_dirty_iter(it, begin); - end = bdrv_dirty_iter_next(it); + for (start = offset; + bdrv_dirty_bitmap_next_dirty_area(bitmap, start, end, INT32_MAX, + &dirty_start, &dirty_count); + start = dirty_start + dirty_count) + { + if ((nbd_extent_array_add(es, dirty_start - start, 0) < 0) || + (nbd_extent_array_add(es, dirty_count, NBD_STATE_DIRTY) < 0)) + { + full = true; + break; } - if (end == -1 || end - begin > UINT32_MAX) { - /* Cap to an aligned value < 4G beyond begin. */ - end = MIN(bdrv_dirty_bitmap_size(bitmap), - begin + UINT32_MAX + 1 - - bdrv_dirty_bitmap_granularity(bitmap)); - next_dirty = dirty; - } - if (dont_fragment && end > overall_end) { - end = overall_end; - } - - extents[i].length = cpu_to_be32(end - begin); - extents[i].flags = cpu_to_be32(dirty ? NBD_STATE_DIRTY : 0); - i++; - begin = end; - dirty = next_dirty; } - bdrv_dirty_iter_free(it); + if (!full) { + /* last non dirty extent */ + nbd_extent_array_add(es, end - start, 0); + } bdrv_dirty_bitmap_unlock(bitmap); - - assert(offset < end); - *length = end - offset; - return i; } static int nbd_co_send_bitmap(NBDClient *client, uint64_t handle, @@ -2092,20 +2105,12 @@ static int nbd_co_send_bitmap(NBDClient *client, uint64_t handle, uint32_t length, bool dont_fragment, bool last, uint32_t context_id, Error **errp) { - int ret; unsigned int nb_extents = dont_fragment ? 1 : NBD_MAX_BLOCK_STATUS_EXTENTS; - NBDExtent *extents = g_new(NBDExtent, nb_extents); - uint64_t final_length = length; + g_autoptr(NBDExtentArray) ea = nbd_extent_array_new(nb_extents); - nb_extents = bitmap_to_extents(bitmap, offset, &final_length, extents, - nb_extents, dont_fragment); + bitmap_to_extents(bitmap, offset, length, ea); - ret = nbd_co_send_extents(client, handle, extents, nb_extents, - final_length, last, context_id, errp); - - g_free(extents); - - return ret; + return nbd_co_send_extents(client, handle, ea, last, context_id, errp); } /* nbd_co_receive_request @@ -2400,7 +2405,7 @@ static coroutine_fn int nbd_handle_request(NBDClient *client, } } - return ret; + return 0; } else { return nbd_send_generic_reply(client, request->handle, -EINVAL, "CMD_BLOCK_STATUS not negotiated", diff --git a/net/can/can_socketcan.c b/net/can/can_socketcan.c index 8a6ffad40c..807f31fcde 100644 --- a/net/can/can_socketcan.c +++ b/net/can/can_socketcan.c @@ -76,8 +76,7 @@ QEMU_BUILD_BUG_ON(offsetof(qemu_can_frame, data) static void can_host_socketcan_display_msg(struct qemu_can_frame *msg) { int i; - - qemu_log_lock(); + FILE *logfile = qemu_log_lock(); qemu_log("[cansocketcan]: %03X [%01d] %s %s", msg->can_id & QEMU_CAN_EFF_MASK, msg->can_dlc, @@ -89,7 +88,7 @@ static void can_host_socketcan_display_msg(struct qemu_can_frame *msg) } qemu_log("\n"); qemu_log_flush(); - qemu_log_unlock(); + qemu_log_unlock(logfile); } static void can_host_socketcan_read(void *opaque) @@ -111,9 +110,9 @@ static void can_host_socketcan_read(void *opaque) } } -static int can_host_socketcan_can_receive(CanBusClientState *client) +static bool can_host_socketcan_can_receive(CanBusClientState *client) { - return 1; + return true; } static ssize_t can_host_socketcan_receive(CanBusClientState *client, diff --git a/net/colo-compare.c b/net/colo-compare.c index 7ee17f2cf8..10c0239f9d 100644 --- a/net/colo-compare.c +++ b/net/colo-compare.c @@ -48,8 +48,8 @@ static NotifierList colo_compare_notifiers = #define COLO_COMPARE_FREE_PRIMARY 0x01 #define COLO_COMPARE_FREE_SECONDARY 0x02 -/* TODO: Should be configurable */ #define REGULAR_PACKET_CHECK_MS 3000 +#define DEFAULT_TIME_OUT_MS 3000 static QemuMutex event_mtx; static QemuCond event_complete_cond; @@ -92,6 +92,8 @@ typedef struct CompareState { SocketReadState sec_rs; SocketReadState notify_rs; bool vnet_hdr; + uint32_t compare_timeout; + uint32_t expired_scan_cycle; /* * Record the connection that through the NIC @@ -607,10 +609,9 @@ static int colo_old_packet_check_one_conn(Connection *conn, CompareState *s) { GList *result = NULL; - int64_t check_time = REGULAR_PACKET_CHECK_MS; result = g_queue_find_custom(&conn->primary_list, - &check_time, + &s->compare_timeout, (GCompareFunc)colo_old_packet_check_one); if (result) { @@ -822,7 +823,7 @@ static void check_old_packet_regular(void *opaque) /* if have old packet we will notify checkpoint */ colo_old_packet_check(s); timer_mod(s->packet_check_timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + - REGULAR_PACKET_CHECK_MS); + s->expired_scan_cycle); } /* Public API, Used for COLO frame to notify compare event */ @@ -852,7 +853,7 @@ static void colo_compare_timer_init(CompareState *s) SCALE_MS, check_old_packet_regular, s); timer_mod(s->packet_check_timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + - REGULAR_PACKET_CHECK_MS); + s->expired_scan_cycle); } static void colo_compare_timer_del(CompareState *s) @@ -984,6 +985,72 @@ static void compare_set_notify_dev(Object *obj, const char *value, Error **errp) s->notify_dev = g_strdup(value); } +static void compare_get_timeout(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + CompareState *s = COLO_COMPARE(obj); + uint32_t value = s->compare_timeout; + + visit_type_uint32(v, name, &value, errp); +} + +static void compare_set_timeout(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + CompareState *s = COLO_COMPARE(obj); + Error *local_err = NULL; + uint32_t value; + + visit_type_uint32(v, name, &value, &local_err); + if (local_err) { + goto out; + } + if (!value) { + error_setg(&local_err, "Property '%s.%s' requires a positive value", + object_get_typename(obj), name); + goto out; + } + s->compare_timeout = value; + +out: + error_propagate(errp, local_err); +} + +static void compare_get_expired_scan_cycle(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + CompareState *s = COLO_COMPARE(obj); + uint32_t value = s->expired_scan_cycle; + + visit_type_uint32(v, name, &value, errp); +} + +static void compare_set_expired_scan_cycle(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + CompareState *s = COLO_COMPARE(obj); + Error *local_err = NULL; + uint32_t value; + + visit_type_uint32(v, name, &value, &local_err); + if (local_err) { + goto out; + } + if (!value) { + error_setg(&local_err, "Property '%s.%s' requires a positive value", + object_get_typename(obj), name); + goto out; + } + s->expired_scan_cycle = value; + +out: + error_propagate(errp, local_err); +} + static void compare_pri_rs_finalize(SocketReadState *pri_rs) { CompareState *s = container_of(pri_rs, CompareState, pri_rs); @@ -1090,6 +1157,16 @@ static void colo_compare_complete(UserCreatable *uc, Error **errp) return; } + if (!s->compare_timeout) { + /* Set default value to 3000 MS */ + s->compare_timeout = DEFAULT_TIME_OUT_MS; + } + + if (!s->expired_scan_cycle) { + /* Set default value to 3000 MS */ + s->expired_scan_cycle = REGULAR_PACKET_CHECK_MS; + } + if (find_and_check_chardev(&chr, s->pri_indev, errp) || !qemu_chr_fe_init(&s->chr_pri_in, chr, errp)) { return; @@ -1185,6 +1262,14 @@ static void colo_compare_init(Object *obj) compare_get_notify_dev, compare_set_notify_dev, NULL); + object_property_add(obj, "compare_timeout", "uint32", + compare_get_timeout, + compare_set_timeout, NULL, NULL, NULL); + + object_property_add(obj, "expired_scan_cycle", "uint32", + compare_get_expired_scan_cycle, + compare_set_expired_scan_cycle, NULL, NULL, NULL); + s->vnet_hdr = false; object_property_add_bool(obj, "vnet_hdr_support", compare_get_vnet_hdr, compare_set_vnet_hdr, NULL); diff --git a/net/filter-buffer.c b/net/filter-buffer.c index 88da78f821..12e0254287 100644 --- a/net/filter-buffer.c +++ b/net/filter-buffer.c @@ -74,7 +74,7 @@ static ssize_t filter_buffer_receive_iov(NetFilterState *nf, * the filter can still accept packets until its internal queue is full. * For example: * For some reason, receiver could not receive more packets - * (.can_receive() returns zero). Without a filter, at most one packet + * (.can_receive() returns false). Without a filter, at most one packet * will be queued in incoming queue and sender's poll will be disabled * unit its sent_cb() was called. With a filter, it will keep receiving * the packets without caring about the receiver. This is suboptimal. diff --git a/net/filter-mirror.c b/net/filter-mirror.c index 8d36009c53..d83e815545 100644 --- a/net/filter-mirror.c +++ b/net/filter-mirror.c @@ -132,7 +132,7 @@ static void redirector_chr_read(void *opaque, const uint8_t *buf, int size) } } -static void redirector_chr_event(void *opaque, int event) +static void redirector_chr_event(void *opaque, QEMUChrEvent event) { NetFilterState *nf = opaque; MirrorState *s = FILTER_REDIRECTOR(nf); diff --git a/net/filter.c b/net/filter.c index 4b932e79f9..8221666263 100644 --- a/net/filter.c +++ b/net/filter.c @@ -171,11 +171,47 @@ static void netfilter_set_status(Object *obj, const char *str, Error **errp) } } +static char *netfilter_get_position(Object *obj, Error **errp) +{ + NetFilterState *nf = NETFILTER(obj); + + return g_strdup(nf->position); +} + +static void netfilter_set_position(Object *obj, const char *str, Error **errp) +{ + NetFilterState *nf = NETFILTER(obj); + + nf->position = g_strdup(str); +} + +static char *netfilter_get_insert(Object *obj, Error **errp) +{ + NetFilterState *nf = NETFILTER(obj); + + return nf->insert_before_flag ? g_strdup("before") : g_strdup("behind"); +} + +static void netfilter_set_insert(Object *obj, const char *str, Error **errp) +{ + NetFilterState *nf = NETFILTER(obj); + + if (strcmp(str, "before") && strcmp(str, "behind")) { + error_setg(errp, "Invalid value for netfilter insert, " + "should be 'before' or 'behind'"); + return; + } + + nf->insert_before_flag = !strcmp(str, "before"); +} + static void netfilter_init(Object *obj) { NetFilterState *nf = NETFILTER(obj); nf->on = true; + nf->insert_before_flag = false; + nf->position = g_strdup("tail"); object_property_add_str(obj, "netdev", netfilter_get_netdev_id, netfilter_set_netdev_id, @@ -187,11 +223,18 @@ static void netfilter_init(Object *obj) object_property_add_str(obj, "status", netfilter_get_status, netfilter_set_status, NULL); + object_property_add_str(obj, "position", + netfilter_get_position, netfilter_set_position, + NULL); + object_property_add_str(obj, "insert", + netfilter_get_insert, netfilter_set_insert, + NULL); } static void netfilter_complete(UserCreatable *uc, Error **errp) { NetFilterState *nf = NETFILTER(uc); + NetFilterState *position = NULL; NetClientState *ncs[MAX_QUEUE_NUM]; NetFilterClass *nfc = NETFILTER_GET_CLASS(uc); int queues; @@ -219,6 +262,41 @@ static void netfilter_complete(UserCreatable *uc, Error **errp) return; } + if (strcmp(nf->position, "head") && strcmp(nf->position, "tail")) { + Object *container; + Object *obj; + char *position_id; + + if (!g_str_has_prefix(nf->position, "id=")) { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "position", + "'head', 'tail' or 'id='"); + return; + } + + /* get the id from the string */ + position_id = g_strndup(nf->position + 3, strlen(nf->position) - 3); + + /* Search for the position to insert before/behind */ + container = object_get_objects_root(); + obj = object_resolve_path_component(container, position_id); + if (!obj) { + error_setg(errp, "filter '%s' not found", position_id); + g_free(position_id); + return; + } + + position = NETFILTER(obj); + + if (position->netdev != ncs[0]) { + error_setg(errp, "filter '%s' belongs to a different netdev", + position_id); + g_free(position_id); + return; + } + + g_free(position_id); + } + nf->netdev = ncs[0]; if (nfc->setup) { @@ -228,7 +306,18 @@ static void netfilter_complete(UserCreatable *uc, Error **errp) return; } } - QTAILQ_INSERT_TAIL(&nf->netdev->filters, nf, next); + + if (position) { + if (nf->insert_before_flag) { + QTAILQ_INSERT_BEFORE(position, nf, next); + } else { + QTAILQ_INSERT_AFTER(&nf->netdev->filters, position, nf, next); + } + } else if (!strcmp(nf->position, "head")) { + QTAILQ_INSERT_HEAD(&nf->netdev->filters, nf, next); + } else if (!strcmp(nf->position, "tail")) { + QTAILQ_INSERT_TAIL(&nf->netdev->filters, nf, next); + } } static void netfilter_finalize(Object *obj) @@ -245,6 +334,7 @@ static void netfilter_finalize(Object *obj) QTAILQ_REMOVE(&nf->netdev->filters, nf, next); } g_free(nf->netdev_id); + g_free(nf->position); } static void default_handle_event(NetFilterState *nf, int event, Error **errp) diff --git a/net/hub.c b/net/hub.c index 5795a678ed..1375738bf1 100644 --- a/net/hub.c +++ b/net/hub.c @@ -90,7 +90,7 @@ static NetHub *net_hub_new(int id) return hub; } -static int net_hub_port_can_receive(NetClientState *nc) +static bool net_hub_port_can_receive(NetClientState *nc) { NetHubPort *port; NetHubPort *src_port = DO_UPCAST(NetHubPort, nc, nc); @@ -102,11 +102,11 @@ static int net_hub_port_can_receive(NetClientState *nc) } if (qemu_can_send_packet(&port->nc)) { - return 1; + return true; } } - return 0; + return false; } static ssize_t net_hub_port_receive(NetClientState *nc, @@ -193,29 +193,6 @@ NetClientState *net_hub_add_port(int hub_id, const char *name, return &port->nc; } -/** - * Find a specific client on a hub - */ -NetClientState *net_hub_find_client_by_name(int hub_id, const char *name) -{ - NetHub *hub; - NetHubPort *port; - NetClientState *peer; - - QLIST_FOREACH(hub, &hubs, next) { - if (hub->id == hub_id) { - QLIST_FOREACH(port, &hub->ports, next) { - peer = port->nc.peer; - - if (peer && strcmp(peer->name, name) == 0) { - return peer; - } - } - } - } - return NULL; -} - /** * Find a available port on a hub; otherwise create one new port */ diff --git a/net/hub.h b/net/hub.h index 66d3322fac..ce45f7b399 100644 --- a/net/hub.h +++ b/net/hub.h @@ -15,10 +15,8 @@ #ifndef NET_HUB_H #define NET_HUB_H - NetClientState *net_hub_add_port(int hub_id, const char *name, NetClientState *hubpeer); -NetClientState *net_hub_find_client_by_name(int hub_id, const char *name); void net_hub_info(Monitor *mon); void net_hub_check_clients(void); bool net_hub_flush(NetClientState *nc); diff --git a/net/net.c b/net/net.c index 84aa6d8d00..38778e831d 100644 --- a/net/net.c +++ b/net/net.c @@ -1060,6 +1060,15 @@ static int net_client_init1(const void *object, bool is_netdev, Error **errp) } return -1; } + + if (is_netdev) { + NetClientState *nc; + + nc = qemu_find_netdev(netdev->id); + assert(nc); + nc->is_netdev = true; + } + return 0; } @@ -1126,16 +1135,13 @@ static int net_client_init(QemuOpts *opts, bool is_netdev, Error **errp) prefix_addr = substrings[0]; - if (substrings[1]) { - /* User-specified prefix length. */ - int err; - - err = qemu_strtoul(substrings[1], NULL, 10, &prefix_len); - if (err) { - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, - "ipv6-prefixlen", "a number"); - goto out; - } + /* Handle user-specified prefix length. */ + if (substrings[1] && + qemu_strtoul(substrings[1], NULL, 10, &prefix_len)) + { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, + "ipv6-prefixlen", "a number"); + goto out; } qemu_opt_set(opts, "ipv6-prefix", prefix_addr, &error_abort); @@ -1173,36 +1179,14 @@ void netdev_add(QemuOpts *opts, Error **errp) net_client_init(opts, true, errp); } -void qmp_netdev_add(QDict *qdict, QObject **ret, Error **errp) +void qmp_netdev_add(Netdev *netdev, Error **errp) { - Error *local_err = NULL; - QemuOptsList *opts_list; - QemuOpts *opts; - - opts_list = qemu_find_opts_err("netdev", &local_err); - if (local_err) { - goto out; - } - - opts = qemu_opts_from_qdict(opts_list, qdict, &local_err); - if (local_err) { - goto out; - } - - netdev_add(opts, &local_err); - if (local_err) { - qemu_opts_del(opts); - goto out; - } - -out: - error_propagate(errp, local_err); + net_client_init1(netdev, true, errp); } void qmp_netdev_del(const char *id, Error **errp) { NetClientState *nc; - QemuOpts *opts; nc = qemu_find_netdev(id); if (!nc) { @@ -1211,14 +1195,12 @@ void qmp_netdev_del(const char *id, Error **errp) return; } - opts = qemu_opts_find(qemu_find_opts_err("netdev", NULL), id); - if (!opts) { + if (!nc->is_netdev) { error_setg(errp, "Device '%s' is not a netdev", id); return; } qemu_del_net_client(nc); - qemu_opts_del(opts); } static void netfilter_print_info(Monitor *mon, NetFilterState *nf) diff --git a/net/queue.c b/net/queue.c index 61276ca4be..0164727e39 100644 --- a/net/queue.c +++ b/net/queue.c @@ -46,7 +46,7 @@ struct NetPacket { unsigned flags; int size; NetPacketSent *sent_cb; - uint8_t data[0]; + uint8_t data[]; }; struct NetQueue { diff --git a/net/slirp.c b/net/slirp.c index c4334ee876..77042e6df7 100644 --- a/net/slirp.c +++ b/net/slirp.c @@ -610,25 +610,13 @@ error: return -1; } -static SlirpState *slirp_lookup(Monitor *mon, const char *hub_id, - const char *name) +static SlirpState *slirp_lookup(Monitor *mon, const char *id) { - if (name) { - NetClientState *nc; - if (hub_id) { - nc = net_hub_find_client_by_name(strtol(hub_id, NULL, 0), name); - if (!nc) { - monitor_printf(mon, "unrecognized (hub-id, stackname) pair\n"); - return NULL; - } - warn_report("Using 'hub-id' is deprecated, specify the netdev id " - "directly instead"); - } else { - nc = qemu_find_netdev(name); - if (!nc) { - monitor_printf(mon, "unrecognized netdev id '%s'\n", name); - return NULL; - } + if (id) { + NetClientState *nc = qemu_find_netdev(id); + if (!nc) { + monitor_printf(mon, "unrecognized netdev id '%s'\n", id); + return NULL; } if (strcmp(nc->model, "user")) { monitor_printf(mon, "invalid device specified\n"); @@ -655,16 +643,12 @@ void hmp_hostfwd_remove(Monitor *mon, const QDict *qdict) int err; const char *arg1 = qdict_get_str(qdict, "arg1"); const char *arg2 = qdict_get_try_str(qdict, "arg2"); - const char *arg3 = qdict_get_try_str(qdict, "arg3"); - if (arg3) { - s = slirp_lookup(mon, arg1, arg2); - src_str = arg3; - } else if (arg2) { - s = slirp_lookup(mon, NULL, arg1); + if (arg2) { + s = slirp_lookup(mon, arg1); src_str = arg2; } else { - s = slirp_lookup(mon, NULL, NULL); + s = slirp_lookup(mon, NULL); src_str = arg1; } if (!s) { @@ -784,16 +768,12 @@ void hmp_hostfwd_add(Monitor *mon, const QDict *qdict) SlirpState *s; const char *arg1 = qdict_get_str(qdict, "arg1"); const char *arg2 = qdict_get_try_str(qdict, "arg2"); - const char *arg3 = qdict_get_try_str(qdict, "arg3"); - if (arg3) { - s = slirp_lookup(mon, arg1, arg2); - redir_str = arg3; - } else if (arg2) { - s = slirp_lookup(mon, NULL, arg1); + if (arg2) { + s = slirp_lookup(mon, arg1); redir_str = arg2; } else { - s = slirp_lookup(mon, NULL, NULL); + s = slirp_lookup(mon, NULL); redir_str = arg1; } if (s) { diff --git a/net/vhost-user.c b/net/vhost-user.c index 014199d600..17532daaf3 100644 --- a/net/vhost-user.c +++ b/net/vhost-user.c @@ -218,7 +218,7 @@ static gboolean net_vhost_user_watch(GIOChannel *chan, GIOCondition cond, return TRUE; } -static void net_vhost_user_event(void *opaque, int event); +static void net_vhost_user_event(void *opaque, QEMUChrEvent event); static void chr_closed_bh(void *opaque) { @@ -249,7 +249,7 @@ static void chr_closed_bh(void *opaque) } } -static void net_vhost_user_event(void *opaque, int event) +static void net_vhost_user_event(void *opaque, QEMUChrEvent event) { const char *name = opaque; NetClientState *ncs[MAX_QUEUE_NUM]; @@ -294,6 +294,11 @@ static void net_vhost_user_event(void *opaque, int event) aio_bh_schedule_oneshot(ctx, chr_closed_bh, opaque); } break; + case CHR_EVENT_BREAK: + case CHR_EVENT_MUX_IN: + case CHR_EVENT_MUX_OUT: + /* Ignore */ + break; } if (err) { diff --git a/os-posix.c b/os-posix.c index 86cffd2c7d..3cd52e1e70 100644 --- a/os-posix.c +++ b/os-posix.c @@ -80,41 +80,26 @@ void os_setup_signal_handling(void) sigaction(SIGTERM, &act, NULL); } -/* Find a likely location for support files using the location of the binary. - For installed binaries this will be "$bindir/../share/qemu". When - running from the build tree this will be "$bindir/../pc-bios". */ -#define SHARE_SUFFIX "/share/qemu" -#define BUILD_SUFFIX "/pc-bios" +/* + * Find a likely location for support files using the location of the binary. + * When running from the build tree this will be "$bindir/../pc-bios". + * Otherwise, this is CONFIG_QEMU_DATADIR. + */ char *os_find_datadir(void) { - char *dir, *exec_dir; - char *res; - size_t max_len; + g_autofree char *exec_dir = NULL; + g_autofree char *dir = NULL; exec_dir = qemu_get_exec_dir(); - if (exec_dir == NULL) { - return NULL; - } - dir = g_path_get_dirname(exec_dir); + g_return_val_if_fail(exec_dir != NULL, NULL); - max_len = strlen(dir) + - MAX(strlen(SHARE_SUFFIX), strlen(BUILD_SUFFIX)) + 1; - res = g_malloc0(max_len); - snprintf(res, max_len, "%s%s", dir, SHARE_SUFFIX); - if (access(res, R_OK)) { - snprintf(res, max_len, "%s%s", dir, BUILD_SUFFIX); - if (access(res, R_OK)) { - g_free(res); - res = NULL; - } + dir = g_build_filename(exec_dir, "..", "pc-bios", NULL); + if (g_file_test(dir, G_FILE_TEST_IS_DIR)) { + return g_steal_pointer(&dir); } - g_free(dir); - g_free(exec_dir); - return res; + return g_strdup(CONFIG_QEMU_DATADIR); } -#undef SHARE_SUFFIX -#undef BUILD_SUFFIX void os_set_proc_name(const char *s) { diff --git a/os-win32.c b/os-win32.c index 1336cd34f0..6b86e022f0 100644 --- a/os-win32.c +++ b/os-win32.c @@ -30,28 +30,6 @@ #include "qemu-options.h" #include "sysemu/runstate.h" -/***********************************************************/ -/* Functions missing in mingw */ - -int setenv(const char *name, const char *value, int overwrite) -{ - int result = 0; - if (overwrite || !getenv(name)) { - size_t length = strlen(name) + strlen(value) + 2; - char *string = g_malloc(length); - snprintf(string, length, "%s=%s", name, value); - result = putenv(string); - - /* Windows takes a copy and does not continue to use our string. - * Therefore it can be safely freed on this platform. POSIX code - * typically has to leak the string because according to the spec it - * becomes part of the environment. - */ - g_free(string); - } - return result; -} - static BOOL WINAPI qemu_ctrl_handler(DWORD type) { qemu_system_shutdown_request(SHUTDOWN_CAUSE_HOST_SIGNAL); diff --git a/pc-bios/README b/pc-bios/README index 91218c69e9..a5a770f066 100644 --- a/pc-bios/README +++ b/pc-bios/README @@ -4,9 +4,6 @@ - The VGA BIOS and the Cirrus VGA BIOS come from the LGPL VGA bios project (http://www.nongnu.org/vgabios/). -- The PowerPC Open Hack'Ware Open Firmware Compatible BIOS is - available at https://repo.or.cz/openhackware.git. - - OpenBIOS (http://www.openbios.org/) is a free (GPL v2) portable firmware implementation. The goal is to implement a 100% IEEE 1275-1994 (referred to as Open Firmware) compliant firmware. @@ -17,7 +14,7 @@ - SLOF (Slimline Open Firmware) is a free IEEE 1275 Open Firmware implementation for certain IBM POWER hardware. The sources are at https://github.com/aik/SLOF, and the image currently in qemu is - built from git tag qemu-slof-20191209. + built from git tag qemu-slof-20200327. - sgabios (the Serial Graphics Adapter option ROM) provides a means for legacy x86 software to communicate with an attached serial console as diff --git a/pc-bios/bios-256k.bin b/pc-bios/bios-256k.bin index 40e7e67a9b..4af7f7d5a9 100644 Binary files a/pc-bios/bios-256k.bin and b/pc-bios/bios-256k.bin differ diff --git a/pc-bios/bios.bin b/pc-bios/bios.bin index 97b1e239aa..67874c31b8 100644 Binary files a/pc-bios/bios.bin and b/pc-bios/bios.bin differ diff --git a/pc-bios/hppa-firmware.img b/pc-bios/hppa-firmware.img index c79e1e923c..82d98b1353 100644 Binary files a/pc-bios/hppa-firmware.img and b/pc-bios/hppa-firmware.img differ diff --git a/pc-bios/opensbi-riscv32-sifive_u-fw_jump.bin b/pc-bios/opensbi-riscv32-sifive_u-fw_jump.bin new file mode 100644 index 0000000000..bab13f597a Binary files /dev/null and b/pc-bios/opensbi-riscv32-sifive_u-fw_jump.bin differ diff --git a/pc-bios/opensbi-riscv32-virt-fw_jump.bin b/pc-bios/opensbi-riscv32-virt-fw_jump.bin index 6c5b7b89f6..c9654e7047 100644 Binary files a/pc-bios/opensbi-riscv32-virt-fw_jump.bin and b/pc-bios/opensbi-riscv32-virt-fw_jump.bin differ diff --git a/pc-bios/opensbi-riscv64-sifive_u-fw_jump.bin b/pc-bios/opensbi-riscv64-sifive_u-fw_jump.bin index 971f2be405..77f4dc8f83 100644 Binary files a/pc-bios/opensbi-riscv64-sifive_u-fw_jump.bin and b/pc-bios/opensbi-riscv64-sifive_u-fw_jump.bin differ diff --git a/pc-bios/opensbi-riscv64-virt-fw_jump.bin b/pc-bios/opensbi-riscv64-virt-fw_jump.bin index 45a5aed1ce..31e74d12ea 100644 Binary files a/pc-bios/opensbi-riscv64-virt-fw_jump.bin and b/pc-bios/opensbi-riscv64-virt-fw_jump.bin differ diff --git a/pc-bios/optionrom/pvh_main.c b/pc-bios/optionrom/pvh_main.c index a015e1bf22..28e79d7fc4 100644 --- a/pc-bios/optionrom/pvh_main.c +++ b/pc-bios/optionrom/pvh_main.c @@ -29,7 +29,7 @@ asm (".code32"); /* this code will be executed in protected mode */ #define RSDP_SIGNATURE 0x2052545020445352LL /* "RSD PTR " */ #define RSDP_AREA_ADDR 0x000E0000 -#define RSDP_AREA_SIZE 2048 +#define RSDP_AREA_SIZE 0x00020000 #define EBDA_BASE_ADDR 0x0000040E #define EBDA_SIZE 1024 diff --git a/pc-bios/ppc_rom.bin b/pc-bios/ppc_rom.bin deleted file mode 100644 index 174a24744b..0000000000 Binary files a/pc-bios/ppc_rom.bin and /dev/null differ diff --git a/pc-bios/s390-ccw.img b/pc-bios/s390-ccw.img index c84ae93561..b9da9d8ecb 100644 Binary files a/pc-bios/s390-ccw.img and b/pc-bios/s390-ccw.img differ diff --git a/pc-bios/s390-ccw/bootmap.h b/pc-bios/s390-ccw/bootmap.h index 94f53a5f1e..12a0166aae 100644 --- a/pc-bios/s390-ccw/bootmap.h +++ b/pc-bios/s390-ccw/bootmap.h @@ -136,7 +136,7 @@ typedef struct BootMapScriptHeader { typedef struct BootMapScript { BootMapScriptHeader header; - BootMapScriptEntry entry[0]; + BootMapScriptEntry entry[]; } __attribute__ ((packed)) BootMapScript; /* diff --git a/pc-bios/s390-ccw/jump2ipl.c b/pc-bios/s390-ccw/jump2ipl.c index 266f1502b9..4eba2510b0 100644 --- a/pc-bios/s390-ccw/jump2ipl.c +++ b/pc-bios/s390-ccw/jump2ipl.c @@ -12,11 +12,11 @@ #define KERN_IMAGE_START 0x010000UL #define PSW_MASK_64 0x0000000100000000ULL #define PSW_MASK_32 0x0000000080000000ULL -#define IPL_PSW_MASK (PSW_MASK_32 | PSW_MASK_64) +#define PSW_MASK_SHORTPSW 0x0008000000000000ULL +#define RESET_PSW_MASK (PSW_MASK_SHORTPSW | PSW_MASK_32 | PSW_MASK_64) typedef struct ResetInfo { - uint32_t ipl_mask; - uint32_t ipl_addr; + uint64_t ipl_psw; uint32_t ipl_continue; } ResetInfo; @@ -35,6 +35,7 @@ void jump_to_IPL_code(uint64_t address) { /* store the subsystem information _after_ the bootmap was loaded */ write_subsystem_identification(); + write_iplb_location(); /* prevent unknown IPL types in the guest */ if (iplb.pbt == S390_IPL_TYPE_QEMU_SCSI) { @@ -50,7 +51,9 @@ void jump_to_IPL_code(uint64_t address) ResetInfo *current = 0; save = *current; - current->ipl_addr = (uint32_t) (uint64_t) &jump_to_IPL_2; + + current->ipl_psw = (uint64_t) &jump_to_IPL_2; + current->ipl_psw |= RESET_PSW_MASK; current->ipl_continue = address & 0x7fffffff; debug_print_int("set IPL addr to", current->ipl_continue); @@ -82,7 +85,7 @@ void jump_to_low_kernel(void) } /* Trying to get PSW at zero address */ - if (*((uint64_t *)0) & IPL_PSW_MASK) { + if (*((uint64_t *)0) & RESET_PSW_MASK) { jump_to_IPL_code((*((uint64_t *)0)) & 0x7fffffff); } diff --git a/pc-bios/s390-ccw/main.c b/pc-bios/s390-ccw/main.c index a21b386280..4e65b411e1 100644 --- a/pc-bios/s390-ccw/main.c +++ b/pc-bios/s390-ccw/main.c @@ -9,6 +9,7 @@ */ #include "libc.h" +#include "helper.h" #include "s390-arch.h" #include "s390-ccw.h" #include "cio.h" @@ -22,7 +23,7 @@ QemuIplParameters qipl; IplParameterBlock iplb __attribute__((__aligned__(PAGE_SIZE))); static bool have_iplb; static uint16_t cutype; -LowCore const *lowcore; /* Yes, this *is* a pointer to address 0 */ +LowCore *lowcore; /* Yes, this *is* a pointer to address 0 */ #define LOADPARM_PROMPT "PROMPT " #define LOADPARM_EMPTY " " @@ -42,6 +43,11 @@ void write_subsystem_identification(void) *zeroes = 0; } +void write_iplb_location(void) +{ + lowcore->ptr_iplb = ptr2u32(&iplb); +} + void panic(const char *string) { sclp_print(string); diff --git a/pc-bios/s390-ccw/netmain.c b/pc-bios/s390-ccw/netmain.c index f2dcc01e27..309ffa30d9 100644 --- a/pc-bios/s390-ccw/netmain.c +++ b/pc-bios/s390-ccw/netmain.c @@ -40,6 +40,7 @@ #define DEFAULT_TFTP_RETRIES 20 extern char _start[]; +void write_iplb_location(void) {} #define KERNEL_ADDR ((void *)0L) #define KERNEL_MAX_SIZE ((long)_start) diff --git a/pc-bios/s390-ccw/s390-arch.h b/pc-bios/s390-ccw/s390-arch.h index 504fc7c2f0..5f36361c02 100644 --- a/pc-bios/s390-ccw/s390-arch.h +++ b/pc-bios/s390-ccw/s390-arch.h @@ -36,7 +36,13 @@ typedef struct LowCore { /* prefix area: defined by architecture */ PSWLegacy ipl_psw; /* 0x000 */ uint32_t ccw1[2]; /* 0x008 */ - uint32_t ccw2[2]; /* 0x010 */ + union { + uint32_t ccw2[2]; /* 0x010 */ + struct { + uint32_t reserved10; + uint32_t ptr_iplb; + }; + }; uint8_t pad1[0x80 - 0x18]; /* 0x018 */ uint32_t ext_params; /* 0x080 */ uint16_t cpu_addr; /* 0x084 */ @@ -85,7 +91,7 @@ typedef struct LowCore { PSW io_new_psw; /* 0x1f0 */ } __attribute__((packed, aligned(8192))) LowCore; -extern LowCore const *lowcore; +extern LowCore *lowcore; static inline void set_prefix(uint32_t address) { diff --git a/pc-bios/s390-ccw/s390-ccw.h b/pc-bios/s390-ccw/s390-ccw.h index 11bce7d73c..21f27e7990 100644 --- a/pc-bios/s390-ccw/s390-ccw.h +++ b/pc-bios/s390-ccw/s390-ccw.h @@ -57,6 +57,7 @@ void consume_io_int(void); /* main.c */ void panic(const char *string); void write_subsystem_identification(void); +void write_iplb_location(void); extern char stack[PAGE_SIZE * 8] __attribute__((__aligned__(PAGE_SIZE))); unsigned int get_loadparm_index(void); diff --git a/pc-bios/s390-ccw/sclp.h b/pc-bios/s390-ccw/sclp.h index 8450161ba7..64b53cad29 100644 --- a/pc-bios/s390-ccw/sclp.h +++ b/pc-bios/s390-ccw/sclp.h @@ -95,7 +95,7 @@ typedef struct EventBufferHeader { typedef struct WriteEventData { SCCBHeader h; EventBufferHeader ebh; - char data[0]; + char data[]; } __attribute__((packed)) WriteEventData; typedef struct ReadEventData { diff --git a/pc-bios/s390-netboot.img b/pc-bios/s390-netboot.img index f9ef28ef1a..6bb5c86eb0 100644 Binary files a/pc-bios/s390-netboot.img and b/pc-bios/s390-netboot.img differ diff --git a/pc-bios/slof.bin b/pc-bios/slof.bin index 50fc9b1e1c..80bbf91a18 100644 Binary files a/pc-bios/slof.bin and b/pc-bios/slof.bin differ diff --git a/pc-bios/vgabios-ati.bin b/pc-bios/vgabios-ati.bin index b5314fb66c..6202cf73e2 100644 Binary files a/pc-bios/vgabios-ati.bin and b/pc-bios/vgabios-ati.bin differ diff --git a/pc-bios/vgabios-bochs-display.bin b/pc-bios/vgabios-bochs-display.bin index 5033cc8433..b675f86bc6 100644 Binary files a/pc-bios/vgabios-bochs-display.bin and b/pc-bios/vgabios-bochs-display.bin differ diff --git a/pc-bios/vgabios-cirrus.bin b/pc-bios/vgabios-cirrus.bin index 4a3643903f..63fde14fea 100644 Binary files a/pc-bios/vgabios-cirrus.bin and b/pc-bios/vgabios-cirrus.bin differ diff --git a/pc-bios/vgabios-qxl.bin b/pc-bios/vgabios-qxl.bin index f6811bcc8b..80168826e2 100644 Binary files a/pc-bios/vgabios-qxl.bin and b/pc-bios/vgabios-qxl.bin differ diff --git a/pc-bios/vgabios-ramfb.bin b/pc-bios/vgabios-ramfb.bin index 79986b26cf..1dc413dcd2 100644 Binary files a/pc-bios/vgabios-ramfb.bin and b/pc-bios/vgabios-ramfb.bin differ diff --git a/pc-bios/vgabios-stdvga.bin b/pc-bios/vgabios-stdvga.bin index d709ec60a0..f92b9a664d 100644 Binary files a/pc-bios/vgabios-stdvga.bin and b/pc-bios/vgabios-stdvga.bin differ diff --git a/pc-bios/vgabios-virtio.bin b/pc-bios/vgabios-virtio.bin index b99faf4ccc..b0d2f70670 100644 Binary files a/pc-bios/vgabios-virtio.bin and b/pc-bios/vgabios-virtio.bin differ diff --git a/pc-bios/vgabios-vmware.bin b/pc-bios/vgabios-vmware.bin index 8c92ab76f7..1c2776e8cf 100644 Binary files a/pc-bios/vgabios-vmware.bin and b/pc-bios/vgabios-vmware.bin differ diff --git a/pc-bios/vgabios.bin b/pc-bios/vgabios.bin index aeae0364ad..c5aad8f3a9 100644 Binary files a/pc-bios/vgabios.bin and b/pc-bios/vgabios.bin differ diff --git a/plugins/api.c b/plugins/api.c index fa1d9f276d..53c8a73582 100644 --- a/plugins/api.c +++ b/plugins/api.c @@ -46,6 +46,7 @@ #include "qemu/plugin-memory.h" #include "hw/boards.h" #endif +#include "trace/mem.h" /* Uninstall and Reset handlers */ diff --git a/plugins/core.c b/plugins/core.c index 9e1b9e7a91..51bfc94787 100644 --- a/plugins/core.c +++ b/plugins/core.c @@ -15,6 +15,7 @@ #include "qemu/error-report.h" #include "qemu/config-file.h" #include "qapi/error.h" +#include "qemu/lockable.h" #include "qemu/option.h" #include "qemu/rcu_queue.h" #include "qemu/xxhash.h" @@ -150,11 +151,11 @@ do_plugin_register_cb(qemu_plugin_id_t id, enum qemu_plugin_event ev, { struct qemu_plugin_ctx *ctx; - qemu_rec_mutex_lock(&plugin.lock); + QEMU_LOCK_GUARD(&plugin.lock); ctx = plugin_id_to_ctx_locked(id); /* if the plugin is on its way out, ignore this request */ if (unlikely(ctx->uninstalling)) { - goto out_unlock; + return; } if (func) { struct qemu_plugin_cb *cb = ctx->callbacks[ev]; @@ -178,8 +179,6 @@ do_plugin_register_cb(qemu_plugin_id_t id, enum qemu_plugin_event ev, } else { plugin_unregister_cb__locked(ctx, ev); } - out_unlock: - qemu_rec_mutex_unlock(&plugin.lock); } void plugin_register_cb(qemu_plugin_id_t id, enum qemu_plugin_event ev, @@ -286,6 +285,7 @@ static inline uint32_t cb_to_tcg_flags(enum qemu_plugin_cb_flags flags) switch (flags) { case QEMU_PLUGIN_CB_RW_REGS: ret = 0; + break; case QEMU_PLUGIN_CB_R_REGS: ret = TCG_CALL_NO_WG; break; diff --git a/plugins/loader.c b/plugins/loader.c index 15fc7e5515..685d334e1a 100644 --- a/plugins/loader.c +++ b/plugins/loader.c @@ -19,6 +19,7 @@ #include "qemu/error-report.h" #include "qemu/config-file.h" #include "qapi/error.h" +#include "qemu/lockable.h" #include "qemu/option.h" #include "qemu/rcu_queue.h" #include "qemu/qht.h" @@ -367,15 +368,14 @@ void plugin_reset_uninstall(qemu_plugin_id_t id, struct qemu_plugin_reset_data *data; struct qemu_plugin_ctx *ctx; - qemu_rec_mutex_lock(&plugin.lock); - ctx = plugin_id_to_ctx_locked(id); - if (ctx->uninstalling || (reset && ctx->resetting)) { - qemu_rec_mutex_unlock(&plugin.lock); - return; + WITH_QEMU_LOCK_GUARD(&plugin.lock) { + ctx = plugin_id_to_ctx_locked(id); + if (ctx->uninstalling || (reset && ctx->resetting)) { + return; + } + ctx->resetting = reset; + ctx->uninstalling = !reset; } - ctx->resetting = reset; - ctx->uninstalling = !reset; - qemu_rec_mutex_unlock(&plugin.lock); data = g_new(struct qemu_plugin_reset_data, 1); data->ctx = ctx; diff --git a/python/qemu/__init__.py b/python/qemu/__init__.py index 6c919a3d56..4ca06c34a4 100644 --- a/python/qemu/__init__.py +++ b/python/qemu/__init__.py @@ -9,27 +9,3 @@ # This work is licensed under the terms of the GNU GPL, version 2. See # the COPYING file in the top-level directory. # -# Based on qmp.py. -# - -import logging -import os - -from . import qmp -from . import machine - -LOG = logging.getLogger(__name__) - -# Mapping host architecture to any additional architectures it can -# support which often includes its 32 bit cousin. -ADDITIONAL_ARCHES = { - "x86_64" : "i386", - "aarch64" : "armhf" -} - -def kvm_available(target_arch=None): - host_arch = os.uname()[4] - if target_arch and target_arch != host_arch: - if target_arch != ADDITIONAL_ARCHES.get(host_arch): - return False - return os.access("/dev/kvm", os.R_OK | os.W_OK) diff --git a/python/qemu/accel.py b/python/qemu/accel.py new file mode 100644 index 0000000000..36ae85791e --- /dev/null +++ b/python/qemu/accel.py @@ -0,0 +1,78 @@ +""" +QEMU accel module: + +This module provides utilities for discover and check the availability of +accelerators. +""" +# Copyright (C) 2015-2016 Red Hat Inc. +# Copyright (C) 2012 IBM Corp. +# +# Authors: +# Fam Zheng +# +# This work is licensed under the terms of the GNU GPL, version 2. See +# the COPYING file in the top-level directory. +# + +import logging +import os +import subprocess + +LOG = logging.getLogger(__name__) + +# Mapping host architecture to any additional architectures it can +# support which often includes its 32 bit cousin. +ADDITIONAL_ARCHES = { + "x86_64" : "i386", + "aarch64" : "armhf", + "ppc64le" : "ppc64", +} + +def list_accel(qemu_bin): + """ + List accelerators enabled in the QEMU binary. + + @param qemu_bin (str): path to the QEMU binary. + @raise Exception: if failed to run `qemu -accel help` + @return a list of accelerator names. + """ + if not qemu_bin: + return [] + try: + out = subprocess.check_output([qemu_bin, '-accel', 'help'], + universal_newlines=True) + except: + LOG.debug("Failed to get the list of accelerators in %s", qemu_bin) + raise + # Skip the first line which is the header. + return [acc.strip() for acc in out.splitlines()[1:]] + +def kvm_available(target_arch=None, qemu_bin=None): + """ + Check if KVM is available using the following heuristic: + - Kernel module is present in the host; + - Target and host arches don't mismatch; + - KVM is enabled in the QEMU binary. + + @param target_arch (str): target architecture + @param qemu_bin (str): path to the QEMU binary + @return True if kvm is available, otherwise False. + """ + if not os.access("/dev/kvm", os.R_OK | os.W_OK): + return False + if target_arch: + host_arch = os.uname()[4] + if target_arch != host_arch: + if target_arch != ADDITIONAL_ARCHES.get(host_arch): + return False + if qemu_bin and "kvm" not in list_accel(qemu_bin): + return False + return True + +def tcg_available(qemu_bin): + """ + Check if TCG is available. + + @param qemu_bin (str): path to the QEMU binary + """ + return 'tcg' in list_accel(qemu_bin) diff --git a/python/qemu/machine.py b/python/qemu/machine.py index a4631d6934..b9a98e2c86 100644 --- a/python/qemu/machine.py +++ b/python/qemu/machine.py @@ -104,6 +104,7 @@ class QEMUMachine(object): self._events = [] self._iolog = None self._socket_scm_helper = socket_scm_helper + self._qmp_set = True # Enable QMP monitor by default. self._qmp = None self._qemu_full_args = None self._test_dir = test_dir @@ -111,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 @@ -228,17 +230,20 @@ class QEMUMachine(object): self._iolog = iolog.read() def _base_args(self): - if isinstance(self._monitor_address, tuple): - moncdev = "socket,id=mon,host=%s,port=%s" % ( - self._monitor_address[0], - self._monitor_address[1]) - else: - moncdev = 'socket,id=mon,path=%s' % self._vm_monitor - args = ['-chardev', moncdev, - '-mon', 'chardev=mon,mode=control', - '-display', 'none', '-vga', 'none'] + args = ['-display', 'none', '-vga', 'none'] + if self._qmp_set: + if isinstance(self._monitor_address, tuple): + moncdev = "socket,id=mon,host=%s,port=%s" % ( + self._monitor_address[0], + self._monitor_address[1]) + else: + moncdev = 'socket,id=mon,path=%s' % self._vm_monitor + args.extend(['-chardev', moncdev, '-mon', + '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") @@ -255,20 +260,22 @@ class QEMUMachine(object): def _pre_launch(self): self._temp_dir = tempfile.mkdtemp(dir=self._test_dir) - if self._monitor_address is not None: - self._vm_monitor = self._monitor_address - else: - self._vm_monitor = os.path.join(self._sock_dir, - self._name + "-monitor.sock") - self._remove_files.append(self._vm_monitor) self._qemu_log_path = os.path.join(self._temp_dir, self._name + ".log") self._qemu_log_file = open(self._qemu_log_path, 'wb') - self._qmp = qmp.QEMUMonitorProtocol(self._vm_monitor, - server=True) + if self._qmp_set: + if self._monitor_address is not None: + self._vm_monitor = self._monitor_address + else: + self._vm_monitor = os.path.join(self._sock_dir, + self._name + "-monitor.sock") + self._remove_files.append(self._vm_monitor) + self._qmp = qmp.QEMUMonitorProtocol(self._vm_monitor, server=True, + nickname=self._name) def _post_launch(self): - self._qmp.accept() + if self._qmp: + self._qmp.accept() def _post_shutdown(self): if self._qemu_log_file is not None: @@ -330,7 +337,8 @@ class QEMUMachine(object): Wait for the VM to power off """ self._popen.wait() - self._qmp.close() + if self._qmp: + self._qmp.close() self._load_io_log() self._post_shutdown() @@ -346,12 +354,14 @@ class QEMUMachine(object): self._console_socket = None if self.is_running(): - try: - if not has_quit: - self._qmp.cmd('quit') - self._qmp.close() - except: - self._popen.kill() + if self._qmp: + try: + if not has_quit: + self._qmp.cmd('quit') + self._qmp.close() + self._popen.wait(timeout=3) + except: + self._popen.kill() self._popen.wait() self._load_io_log() @@ -368,6 +378,21 @@ class QEMUMachine(object): self._launched = False + def set_qmp_monitor(self, enabled=True): + """ + Set the QMP monitor. + + @param enabled: if False, qmp monitor options will be removed from + the base arguments of the resulting QEMU command + line. Default is True. + @note: call this function before launch(). + """ + if enabled: + self._qmp_set = True + else: + self._qmp_set = False + self._qmp = None + def qmp(self, cmd, conv_keys=True, **args): """ Invoke a QMP command and return the response dict @@ -507,7 +532,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 @@ -528,9 +553,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..d6c9b2f4b1 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,31 +14,39 @@ 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): + def __init__(self, address, server=False, nickname=None): """ Create a QEMUMonitorProtocol class. @@ -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 """ @@ -55,6 +62,9 @@ class QEMUMonitorProtocol(object): self.__address = address self.__sock = self.__get_sock() self.__sockfile = None + self._nickname = nickname + if self._nickname: + self.logger = logging.getLogger('QMP').getChild(self._nickname) if server: self.__sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.__sock.bind(self.__address) @@ -73,7 +83,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 +91,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 +117,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 +138,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 +160,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 +194,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 +270,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/Makefile.objs b/qapi/Makefile.objs index dd3f5e6f94..4673ab7490 100644 --- a/qapi/Makefile.objs +++ b/qapi/Makefile.objs @@ -5,9 +5,9 @@ util-obj-y += opts-visitor.o qapi-clone-visitor.o util-obj-y += qmp-event.o util-obj-y += qapi-util.o -QAPI_COMMON_MODULES = audio authz block-core block char common crypto -QAPI_COMMON_MODULES += dump error introspect job machine migration misc net -QAPI_COMMON_MODULES += qdev qom rdma rocker run-state sockets tpm +QAPI_COMMON_MODULES = audio authz block-core block char common control crypto +QAPI_COMMON_MODULES += dump error introspect job machine migration misc +QAPI_COMMON_MODULES += net pragma qdev qom rdma rocker run-state sockets tpm QAPI_COMMON_MODULES += trace transaction ui QAPI_TARGET_MODULES = machine-target misc-target QAPI_MODULES = $(QAPI_COMMON_MODULES) $(QAPI_TARGET_MODULES) @@ -30,3 +30,9 @@ obj-y += $(QAPI_TARGET_MODULES:%=qapi-events-%.o) obj-y += qapi-events.o obj-y += $(QAPI_TARGET_MODULES:%=qapi-commands-%.o) obj-y += qapi-commands.o +obj-y += qapi-init-commands.o + +QAPI_MODULES_STORAGE_DAEMON = block-core char common control crypto +QAPI_MODULES_STORAGE_DAEMON += introspect job qom sockets pragma transaction + +storage-daemon-obj-y += $(QAPI_MODULES_STORAGE_DAEMON:%=qapi-commands-%.o) diff --git a/qapi/audio.json b/qapi/audio.json index 83312b2339..c31251f45b 100644 --- a/qapi/audio.json +++ b/qapi/audio.json @@ -273,10 +273,24 @@ # # An enumeration of possible audio formats. # +# @u8: unsigned 8 bit integer +# +# @s8: signed 8 bit integer +# +# @u16: unsigned 16 bit integer +# +# @s16: signed 16 bit integer +# +# @u32: unsigned 32 bit integer +# +# @s32: signed 32 bit integer +# +# @f32: single precision floating-point (since 5.0) +# # 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-core.json b/qapi/block-core.json index 0cf68fea14..943df1926a 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -162,7 +162,7 @@ # @backing-image: info of the backing image (since 1.6) # # @format-specific: structure supplying additional format-specific -# information (since 1.7) +# information (since 1.7) # # Since: 1.3 # @@ -297,7 +297,7 @@ # # @encrypted: true if the backing device is encrypted # -# @encryption_key_missing: Deprecated; always false +# @encryption_key_missing: always false # # @detect_zeroes: detect and optimize zero writes (Since 2.1) # @@ -363,13 +363,19 @@ # @dirty-bitmaps: dirty bitmaps information (only present if node # has one or more dirty bitmaps) (Since 4.2) # +# Features: +# @deprecated: Member @encryption_key_missing is deprecated. It is +# always false. +# # Since: 0.14.0 # ## { 'struct': 'BlockDeviceInfo', 'data': { 'file': 'str', '*node-name': 'str', 'ro': 'bool', 'drv': 'str', '*backing_file': 'str', 'backing_file_depth': 'int', - 'encrypted': 'bool', 'encryption_key_missing': 'bool', + 'encrypted': 'bool', + 'encryption_key_missing': { 'type': 'bool', + 'features': [ 'deprecated' ] }, 'detect_zeroes': 'BlockdevDetectZeroesOptions', 'bps': 'int', 'bps_rd': 'int', 'bps_wr': 'int', 'iops': 'int', 'iops_rd': 'int', 'iops_wr': 'int', @@ -475,7 +481,7 @@ # # @granularity: granularity of the dirty bitmap in bytes (since 1.4) # -# @status: Deprecated in favor of @recording and @locked. (since 2.4) +# @status: current status of the dirty bitmap (since 2.4) # # @recording: true if the bitmap is recording new writes from the guest. # Replaces `active` and `disabled` statuses. (since 4.0) @@ -492,11 +498,17 @@ # @busy to be false. This bitmap cannot be used. To remove # it, use @block-dirty-bitmap-remove. (Since 4.0) # +# Features: +# @deprecated: Member @status is deprecated. Use @recording and +# @locked instead. +# # Since: 1.3 ## { 'struct': 'BlockDirtyInfo', 'data': {'*name': 'str', 'count': 'int', 'granularity': 'uint32', - 'recording': 'bool', 'busy': 'bool', 'status': 'DirtyBitmapStatus', + 'recording': 'bool', 'busy': 'bool', + 'status': { 'type': 'DirtyBitmapStatus', + 'features': [ 'deprecated' ] }, 'persistent': 'bool', '*inconsistent': 'bool' } } ## @@ -550,91 +562,19 @@ # For the example above, @bins may be something like [3, 1, 5, 2], # and corresponding histogram looks like: # -# 5| * -# 4| * -# 3| * * -# 2| * * * -# 1| * * * * -# +------------------ -# 10 50 100 +# | 5| * +# | 4| * +# | 3| * * +# | 2| * * * +# | 1| * * * * +# | +------------------ +# | 10 50 100 # # Since: 4.0 ## { 'struct': 'BlockLatencyHistogramInfo', 'data': {'boundaries': ['uint64'], 'bins': ['uint64'] } } -## -# @block-latency-histogram-set: -# -# Manage read, write and flush latency histograms for the device. -# -# If only @id parameter is specified, remove all present latency histograms -# for the device. Otherwise, add/reset some of (or all) latency histograms. -# -# @id: The name or QOM path of the guest device. -# -# @boundaries: list of interval boundary values (see description in -# BlockLatencyHistogramInfo definition). If specified, all -# latency histograms are removed, and empty ones created for all -# io types with intervals corresponding to @boundaries (except for -# io types, for which specific boundaries are set through the -# following parameters). -# -# @boundaries-read: list of interval boundary values for read latency -# histogram. If specified, old read latency histogram is -# removed, and empty one created with intervals -# corresponding to @boundaries-read. The parameter has higher -# priority then @boundaries. -# -# @boundaries-write: list of interval boundary values for write latency -# histogram. -# -# @boundaries-flush: list of interval boundary values for flush latency -# histogram. -# -# Returns: error if device is not found or any boundary arrays are invalid. -# -# Since: 4.0 -# -# Example: set new histograms for all io types with intervals -# [0, 10), [10, 50), [50, 100), [100, +inf): -# -# -> { "execute": "block-latency-histogram-set", -# "arguments": { "id": "drive0", -# "boundaries": [10, 50, 100] } } -# <- { "return": {} } -# -# Example: set new histogram only for write, other histograms will remain -# not changed (or not created): -# -# -> { "execute": "block-latency-histogram-set", -# "arguments": { "id": "drive0", -# "boundaries-write": [10, 50, 100] } } -# <- { "return": {} } -# -# Example: set new histograms with the following intervals: -# read, flush: [0, 10), [10, 50), [50, 100), [100, +inf) -# write: [0, 1000), [1000, 5000), [5000, +inf) -# -# -> { "execute": "block-latency-histogram-set", -# "arguments": { "id": "drive0", -# "boundaries": [10, 50, 100], -# "boundaries-write": [1000, 5000] } } -# <- { "return": {} } -# -# Example: remove all latency histograms: -# -# -> { "execute": "block-latency-histogram-set", -# "arguments": { "id": "drive0" } } -# <- { "return": {} } -## -{ 'command': 'block-latency-histogram-set', - 'data': {'id': 'str', - '*boundaries': ['uint64'], - '*boundaries-read': ['uint64'], - '*boundaries-write': ['uint64'], - '*boundaries-flush': ['uint64'] } } - ## # @BlockInfo: # @@ -659,7 +599,6 @@ # # @dirty-bitmaps: dirty bitmaps information (only present if the # driver has one or more dirty bitmaps) (Since 2.0) -# Deprecated in 4.2; see BlockDeviceInfo instead. # # @io-status: @BlockDeviceIoStatus. Only present if the device # supports it and the VM is configured to stop on errors @@ -669,13 +608,18 @@ # @inserted: @BlockDeviceInfo describing the device if media is # present # +# Features: +# @deprecated: Member @dirty-bitmaps is deprecated. Use @inserted +# member @dirty-bitmaps instead. +# # Since: 0.14.0 ## { 'struct': 'BlockInfo', 'data': {'device': 'str', '*qdev': 'str', 'type': 'str', 'removable': 'bool', 'locked': 'bool', '*inserted': 'BlockDeviceInfo', '*tray_open': 'bool', '*io-status': 'BlockDeviceIoStatus', - '*dirty-bitmaps': ['BlockDirtyInfo'] } } + '*dirty-bitmaps': { 'type': ['BlockDirtyInfo'], + 'features': [ 'deprecated' ] } } } ## # @BlockMeasureInfo: @@ -708,7 +652,7 @@ # Get a list of BlockInfo for all virtual block devices. # # Returns: a list of @BlockInfo describing each virtual block device. Filter -# nodes that were created implicitly are skipped over. +# nodes that were created implicitly are skipped over. # # Since: 0.14.0 # @@ -1164,7 +1108,10 @@ # for jobs, cancel the job # # @ignore: ignore the error, only report a QMP event (BLOCK_IO_ERROR -# or BLOCK_JOB_ERROR) +# or BLOCK_JOB_ERROR). The backup, mirror and commit block jobs retry +# the failing request later and may still complete successfully. The +# stream block job continues to stream and will complete with an +# error. # # @enospc: same as @stop on ENOSPC, same as @report otherwise. # @@ -1326,8 +1273,8 @@ # # @size: new image size in bytes # -# Returns: nothing on success -# If @device is not a valid block device, DeviceNotFound +# Returns: - nothing on success +# - If @device is not a valid block device, DeviceNotFound # # Since: 0.14.0 # @@ -1352,8 +1299,8 @@ # @existing: QEMU should look for an existing image file. # # @absolute-paths: QEMU should create a new image with absolute paths -# for the backing file. If there is no backing file available, the new -# image will not be backed either. +# for the backing file. If there is no backing file available, the new +# image will not be backed either. # # Since: 1.1 ## @@ -1370,8 +1317,8 @@ # @node-name: graph node name to generate the snapshot from (Since 2.0) # # @snapshot-file: the target of the new overlay image. If the file -# exists, or if it is a device, the overlay will be created in the -# existing file/device. Otherwise, a new file will be created. +# exists, or if it is a device, the overlay will be created in the +# existing file/device. Otherwise, a new file will be created. # # @snapshot-node-name: the graph node name of the new image (Since 2.0) # @@ -1456,8 +1403,8 @@ # a node name is autogenerated. (Since: 4.2) # # Note: @on-source-error and @on-target-error only affect background -# I/O. If an error occurs during a guest write request, the device's -# rerror/werror actions will be used. +# I/O. If an error occurs during a guest write request, the device's +# rerror/werror actions will be used. # # Since: 4.2 ## @@ -1510,8 +1457,8 @@ # # For the arguments, see the documentation of BlockdevSnapshotSync. # -# Returns: nothing on success -# If @device is not a valid block device, DeviceNotFound +# Returns: - nothing on success +# - If @device is not a valid block device, DeviceNotFound # # Since: 0.14.0 # @@ -1541,6 +1488,12 @@ # # For the arguments, see the documentation of BlockdevSnapshot. # +# Features: +# @allow-write-only-overlay: If present, the check whether this operation is safe +# was relaxed so that it can be used to change +# backing file of a destination of a blockdev-mirror. +# (since 5.0) +# # Since: 2.5 # # Example: @@ -1561,7 +1514,8 @@ # ## { 'command': 'blockdev-snapshot', - 'data': 'BlockdevSnapshot' } + 'data': 'BlockdevSnapshot', + 'features': [ 'allow-write-only-overlay' ] } ## # @change-backing-file: @@ -1578,17 +1532,16 @@ # to verify "image-node-name" is in the chain # described by "device". # -# @device: The device name or node-name of the root node that owns -# image-node-name. +# @device: The device name or node-name of the root node that owns +# image-node-name. # -# @backing-file: The string to write as the backing file. This -# string is not validated, so care should be taken -# when specifying the string or the image chain may -# not be able to be reopened again. +# @backing-file: The string to write as the backing file. This +# string is not validated, so care should be taken +# when specifying the string or the image chain may +# not be able to be reopened again. # -# Returns: Nothing on success -# -# If "device" does not exist or cannot be determined, DeviceNotFound +# Returns: - Nothing on success +# - If "device" does not exist or cannot be determined, DeviceNotFound # # Since: 2.1 ## @@ -1605,7 +1558,7 @@ # @job-id: identifier for the newly-created block job. If # omitted, the device name will be used. (Since 2.7) # -# @device: the device name or node-name of a root node +# @device: the device name or node-name of a root node # # @base-node: The node name of the backing image to write data into. # If not specified, this is the deepest backing image. @@ -1614,7 +1567,7 @@ # @base: Same as @base-node, except that it is a file name rather than a node # name. This must be the exact filename string that was used to open the # node; other strings, even if addressing the same file, are not -# accepted (deprecated, use @base-node instead) +# accepted # # @top-node: The node name of the backing image within the image chain # which contains the topmost data to be committed down. If @@ -1623,38 +1576,41 @@ # @top: Same as @top-node, except that it is a file name rather than a node # name. This must be the exact filename string that was used to open the # node; other strings, even if addressing the same file, are not -# accepted (deprecated, use @base-node instead) +# accepted # -# @backing-file: The backing file string to write into the overlay -# image of 'top'. If 'top' is the active layer, -# specifying a backing file string is an error. This -# filename is not validated. +# @backing-file: The backing file string to write into the overlay +# image of 'top'. If 'top' is the active layer, +# specifying a backing file string is an error. This +# filename is not validated. # -# If a pathname string is such that it cannot be -# resolved by QEMU, that means that subsequent QMP or -# HMP commands must use node-names for the image in -# question, as filename lookup methods will fail. +# If a pathname string is such that it cannot be +# resolved by QEMU, that means that subsequent QMP or +# HMP commands must use node-names for the image in +# question, as filename lookup methods will fail. # -# If not specified, QEMU will automatically determine -# the backing file string to use, or error out if -# there is no obvious choice. Care should be taken -# when specifying the string, to specify a valid -# filename or protocol. -# (Since 2.1) +# If not specified, QEMU will automatically determine +# the backing file string to use, or error out if +# there is no obvious choice. Care should be taken +# when specifying the string, to specify a valid +# filename or protocol. +# (Since 2.1) # -# If top == base, that is an error. -# If top == active, the job will not be completed by itself, -# user needs to complete the job with the block-job-complete -# command after getting the ready event. (Since 2.0) +# If top == base, that is an error. +# If top == active, the job will not be completed by itself, +# user needs to complete the job with the block-job-complete +# command after getting the ready event. (Since 2.0) # -# If the base image is smaller than top, then the base image -# will be resized to be the same size as top. If top is -# smaller than the base image, the base will not be -# truncated. If you want the base image size to match the -# size of the smaller top, you can safely truncate it -# yourself once the commit operation successfully completes. +# If the base image is smaller than top, then the base image +# will be resized to be the same size as top. If top is +# smaller than the base image, the base will not be +# truncated. If you want the base image size to match the +# size of the smaller top, you can safely truncate it +# yourself once the commit operation successfully completes. # -# @speed: the maximum speed, in bytes per second +# @speed: the maximum speed, in bytes per second +# +# @on-error: the action to take on an error. 'ignore' means that the request +# should be retried. (default: report; Since: 5.0) # # @filter-node-name: the node name that should be assigned to the # filter driver that the commit job inserts into the graph @@ -1674,9 +1630,13 @@ # list without user intervention. # Defaults to true. (Since 3.1) # -# Returns: Nothing on success -# If @device does not exist, DeviceNotFound -# Any other error returns a GenericError. +# Features: +# @deprecated: Members @base and @top are deprecated. Use @base-node +# and @top-node instead. +# +# Returns: - Nothing on success +# - If @device does not exist, DeviceNotFound +# - Any other error returns a GenericError. # # Since: 1.3 # @@ -1690,8 +1650,11 @@ ## { 'command': 'block-commit', 'data': { '*job-id': 'str', 'device': 'str', '*base-node': 'str', - '*base': 'str', '*top-node': 'str', '*top': 'str', + '*base': { 'type': 'str', 'features': [ 'deprecated' ] }, + '*top-node': 'str', + '*top': { 'type': 'str', 'features': [ 'deprecated' ] }, '*backing-file': 'str', '*speed': 'int', + '*on-error': 'BlockdevOnError', '*filter-node-name': 'str', '*auto-finalize': 'bool', '*auto-dismiss': 'bool' } } @@ -1704,8 +1667,8 @@ # The operation can be stopped before it has completed using the # block-job-cancel command. # -# Returns: nothing on success -# If @device is not a valid block device, GenericError +# Returns: - nothing on success +# - If @device is not a valid block device, GenericError # # Since: 1.6 # @@ -1730,8 +1693,8 @@ # The operation can be stopped before it has completed using the # block-job-cancel command. # -# Returns: nothing on success -# If @device is not a valid block device, DeviceNotFound +# Returns: - nothing on success +# - If @device is not a valid block device, DeviceNotFound # # Since: 2.3 # @@ -1752,6 +1715,9 @@ # # Get the named block driver list # +# @flat: Omit the nested data about backing image ("backing-image" key) if true. +# Default is false (Since 5.0) +# # Returns: the list of BlockDeviceInfo # # Since: 2.0 @@ -1805,7 +1771,9 @@ # } } ] } # ## -{ 'command': 'query-named-block-nodes', 'returns': [ 'BlockDeviceInfo' ] } +{ 'command': 'query-named-block-nodes', + 'returns': [ 'BlockDeviceInfo' ], + 'data': { '*flat': 'bool' } } ## # @XDbgBlockGraphNodeType: @@ -1925,8 +1893,8 @@ # format of the mirror image, default is to probe if mode='existing', # else the format of the source. # -# Returns: nothing on success -# If @device is not a valid block device, GenericError +# Returns: - nothing on success +# - If @device is not a valid block device, GenericError # # Since: 1.3 # @@ -2097,9 +2065,9 @@ # # Create a dirty bitmap with a name on the node, and start tracking the writes. # -# Returns: nothing on success -# If @node is not a valid block device or node, DeviceNotFound -# If @name is already taken, GenericError with an explanation +# Returns: - nothing on success +# - If @node is not a valid block device or node, DeviceNotFound +# - If @name is already taken, GenericError with an explanation # # Since: 2.4 # @@ -2120,10 +2088,10 @@ # with block-dirty-bitmap-add. If the bitmap is persistent, remove it from its # storage too. # -# Returns: nothing on success -# If @node is not a valid block device or node, DeviceNotFound -# If @name is not found, GenericError with an explanation -# if @name is frozen by an operation, GenericError +# Returns: - nothing on success +# - If @node is not a valid block device or node, DeviceNotFound +# - If @name is not found, GenericError with an explanation +# - if @name is frozen by an operation, GenericError # # Since: 2.4 # @@ -2144,9 +2112,9 @@ # backup from this point in time forward will only backup clusters # modified after this clear operation. # -# Returns: nothing on success -# If @node is not a valid block device, DeviceNotFound -# If @name is not found, GenericError with an explanation +# Returns: - nothing on success +# - If @node is not a valid block device, DeviceNotFound +# - If @name is not found, GenericError with an explanation # # Since: 2.4 # @@ -2165,9 +2133,9 @@ # # Enables a dirty bitmap so that it will begin tracking disk changes. # -# Returns: nothing on success -# If @node is not a valid block device, DeviceNotFound -# If @name is not found, GenericError with an explanation +# Returns: - nothing on success +# - If @node is not a valid block device, DeviceNotFound +# - If @name is not found, GenericError with an explanation # # Since: 4.0 # @@ -2186,9 +2154,9 @@ # # Disables a dirty bitmap so that it will stop tracking disk changes. # -# Returns: nothing on success -# If @node is not a valid block device, DeviceNotFound -# If @name is not found, GenericError with an explanation +# Returns: - nothing on success +# - If @node is not a valid block device, DeviceNotFound +# - If @name is not found, GenericError with an explanation # # Since: 4.0 # @@ -2215,11 +2183,11 @@ # of the source bitmaps. This can be used to achieve backup checkpoints, or in # simpler usages, to copy bitmaps. # -# Returns: nothing on success -# If @node is not a valid block device, DeviceNotFound -# If any bitmap in @bitmaps or @target is not found, GenericError -# If any of the bitmaps have different sizes or granularities, -# GenericError +# Returns: - nothing on success +# - If @node is not a valid block device, DeviceNotFound +# - If any bitmap in @bitmaps or @target is not found, GenericError +# - If any of the bitmaps have different sizes or granularities, +# GenericError # # Since: 4.0 # @@ -2251,10 +2219,10 @@ # # Get bitmap SHA256. # -# Returns: BlockDirtyBitmapSha256 on success -# If @node is not a valid block device, DeviceNotFound -# If @name is not found or if hashing has failed, GenericError with an -# explanation +# Returns: - BlockDirtyBitmapSha256 on success +# - If @node is not a valid block device, DeviceNotFound +# - If @name is not found or if hashing has failed, GenericError with an +# explanation # # Since: 2.10 ## @@ -2345,84 +2313,12 @@ '*copy-mode': 'MirrorCopyMode', '*auto-finalize': 'bool', '*auto-dismiss': 'bool' } } -## -# @block_set_io_throttle: -# -# Change I/O throttle limits for a block drive. -# -# Since QEMU 2.4, each device with I/O limits is member of a throttle -# group. -# -# If two or more devices are members of the same group, the limits -# will apply to the combined I/O of the whole group in a round-robin -# fashion. Therefore, setting new I/O limits to a device will affect -# the whole group. -# -# The name of the group can be specified using the 'group' parameter. -# If the parameter is unset, it is assumed to be the current group of -# that device. If it's not in any group yet, the name of the device -# will be used as the name for its group. -# -# The 'group' parameter can also be used to move a device to a -# different group. In this case the limits specified in the parameters -# will be applied to the new group only. -# -# I/O limits can be disabled by setting all of them to 0. In this case -# the device will be removed from its group and the rest of its -# members will not be affected. The 'group' parameter is ignored. -# -# Returns: Nothing on success -# If @device is not a valid block device, DeviceNotFound -# -# Since: 1.1 -# -# Example: -# -# -> { "execute": "block_set_io_throttle", -# "arguments": { "id": "virtio-blk-pci0/virtio-backend", -# "bps": 0, -# "bps_rd": 0, -# "bps_wr": 0, -# "iops": 512, -# "iops_rd": 0, -# "iops_wr": 0, -# "bps_max": 0, -# "bps_rd_max": 0, -# "bps_wr_max": 0, -# "iops_max": 0, -# "iops_rd_max": 0, -# "iops_wr_max": 0, -# "bps_max_length": 0, -# "iops_size": 0 } } -# <- { "return": {} } -# -# -> { "execute": "block_set_io_throttle", -# "arguments": { "id": "ide0-1-0", -# "bps": 1000000, -# "bps_rd": 0, -# "bps_wr": 0, -# "iops": 0, -# "iops_rd": 0, -# "iops_wr": 0, -# "bps_max": 8000000, -# "bps_rd_max": 0, -# "bps_wr_max": 0, -# "iops_max": 0, -# "iops_rd_max": 0, -# "iops_wr_max": 0, -# "bps_max_length": 60, -# "iops_size": 0 } } -# <- { "return": {} } -## -{ 'command': 'block_set_io_throttle', 'boxed': true, - 'data': 'BlockIOThrottle' } - ## # @BlockIOThrottle: # # A set of parameters describing block throttling. # -# @device: Block device name (deprecated, use @id instead) +# @device: Block device name # # @id: The name or QOM path of the guest device (since: 2.8) # @@ -2439,61 +2335,65 @@ # @iops_wr: write I/O operations per second # # @bps_max: total throughput limit during bursts, -# in bytes (Since 1.7) +# in bytes (Since 1.7) # # @bps_rd_max: read throughput limit during bursts, -# in bytes (Since 1.7) +# in bytes (Since 1.7) # # @bps_wr_max: write throughput limit during bursts, -# in bytes (Since 1.7) +# in bytes (Since 1.7) # # @iops_max: total I/O operations per second during bursts, -# in bytes (Since 1.7) +# in bytes (Since 1.7) # # @iops_rd_max: read I/O operations per second during bursts, -# in bytes (Since 1.7) +# in bytes (Since 1.7) # # @iops_wr_max: write I/O operations per second during bursts, -# in bytes (Since 1.7) +# in bytes (Since 1.7) # # @bps_max_length: maximum length of the @bps_max burst -# period, in seconds. It must only -# be set if @bps_max is set as well. -# Defaults to 1. (Since 2.6) +# period, in seconds. It must only +# be set if @bps_max is set as well. +# Defaults to 1. (Since 2.6) # # @bps_rd_max_length: maximum length of the @bps_rd_max -# burst period, in seconds. It must only -# be set if @bps_rd_max is set as well. -# Defaults to 1. (Since 2.6) +# burst period, in seconds. It must only +# be set if @bps_rd_max is set as well. +# Defaults to 1. (Since 2.6) # # @bps_wr_max_length: maximum length of the @bps_wr_max -# burst period, in seconds. It must only -# be set if @bps_wr_max is set as well. -# Defaults to 1. (Since 2.6) +# burst period, in seconds. It must only +# be set if @bps_wr_max is set as well. +# Defaults to 1. (Since 2.6) # # @iops_max_length: maximum length of the @iops burst -# period, in seconds. It must only -# be set if @iops_max is set as well. -# Defaults to 1. (Since 2.6) +# period, in seconds. It must only +# be set if @iops_max is set as well. +# Defaults to 1. (Since 2.6) # # @iops_rd_max_length: maximum length of the @iops_rd_max -# burst period, in seconds. It must only -# be set if @iops_rd_max is set as well. -# Defaults to 1. (Since 2.6) +# burst period, in seconds. It must only +# be set if @iops_rd_max is set as well. +# Defaults to 1. (Since 2.6) # # @iops_wr_max_length: maximum length of the @iops_wr_max -# burst period, in seconds. It must only -# be set if @iops_wr_max is set as well. -# Defaults to 1. (Since 2.6) +# burst period, in seconds. It must only +# be set if @iops_wr_max is set as well. +# Defaults to 1. (Since 2.6) # # @iops_size: an I/O size in bytes (Since 1.7) # # @group: throttle group name (Since 2.4) # +# Features: +# @deprecated: Member @device is deprecated. Use @id instead. +# # Since: 1.1 ## { 'struct': 'BlockIOThrottle', - 'data': { '*device': 'str', '*id': 'str', 'bps': 'int', 'bps_rd': 'int', + 'data': { '*device': { 'type': 'str', 'features': [ 'deprecated' ] }, + '*id': 'str', 'bps': 'int', 'bps_rd': 'int', 'bps_wr': 'int', 'iops': 'int', 'iops_rd': 'int', 'iops_wr': 'int', '*bps_max': 'int', '*bps_rd_max': 'int', '*bps_wr_max': 'int', '*iops_max': 'int', @@ -2511,31 +2411,31 @@ # transaction. All fields are optional. When setting limits, if a field is # missing the current value is not changed. # -# @iops-total: limit total I/O operations per second -# @iops-total-max: I/O operations burst -# @iops-total-max-length: length of the iops-total-max burst period, in seconds -# It must only be set if @iops-total-max is set as well. -# @iops-read: limit read operations per second -# @iops-read-max: I/O operations read burst -# @iops-read-max-length: length of the iops-read-max burst period, in seconds -# It must only be set if @iops-read-max is set as well. -# @iops-write: limit write operations per second -# @iops-write-max: I/O operations write burst -# @iops-write-max-length: length of the iops-write-max burst period, in seconds -# It must only be set if @iops-write-max is set as well. -# @bps-total: limit total bytes per second -# @bps-total-max: total bytes burst -# @bps-total-max-length: length of the bps-total-max burst period, in seconds. -# It must only be set if @bps-total-max is set as well. -# @bps-read: limit read bytes per second -# @bps-read-max: total bytes read burst -# @bps-read-max-length: length of the bps-read-max burst period, in seconds -# It must only be set if @bps-read-max is set as well. -# @bps-write: limit write bytes per second -# @bps-write-max: total bytes write burst -# @bps-write-max-length: length of the bps-write-max burst period, in seconds -# It must only be set if @bps-write-max is set as well. -# @iops-size: when limiting by iops max size of an I/O in bytes +# @iops-total: limit total I/O operations per second +# @iops-total-max: I/O operations burst +# @iops-total-max-length: length of the iops-total-max burst period, in seconds +# It must only be set if @iops-total-max is set as well. +# @iops-read: limit read operations per second +# @iops-read-max: I/O operations read burst +# @iops-read-max-length: length of the iops-read-max burst period, in seconds +# It must only be set if @iops-read-max is set as well. +# @iops-write: limit write operations per second +# @iops-write-max: I/O operations write burst +# @iops-write-max-length: length of the iops-write-max burst period, in seconds +# It must only be set if @iops-write-max is set as well. +# @bps-total: limit total bytes per second +# @bps-total-max: total bytes burst +# @bps-total-max-length: length of the bps-total-max burst period, in seconds. +# It must only be set if @bps-total-max is set as well. +# @bps-read: limit read bytes per second +# @bps-read-max: total bytes read burst +# @bps-read-max-length: length of the bps-read-max burst period, in seconds +# It must only be set if @bps-read-max is set as well. +# @bps-write: limit write bytes per second +# @bps-write-max: total bytes write burst +# @bps-write-max-length: length of the bps-write-max burst period, in seconds +# It must only be set if @bps-write-max is set as well. +# @iops-size: when limiting by iops max size of an I/O in bytes # # Since: 2.11 ## @@ -2582,28 +2482,28 @@ # # @device: the device or node name of the top image # -# @base: the common backing file name. -# It cannot be set if @base-node is also set. +# @base: the common backing file name. +# It cannot be set if @base-node is also set. # # @base-node: the node name of the backing file. -# It cannot be set if @base is also set. (Since 2.8) +# It cannot be set if @base is also set. (Since 2.8) # # @backing-file: The backing file string to write into the top -# image. This filename is not validated. +# image. This filename is not validated. # -# If a pathname string is such that it cannot be -# resolved by QEMU, that means that subsequent QMP or -# HMP commands must use node-names for the image in -# question, as filename lookup methods will fail. +# If a pathname string is such that it cannot be +# resolved by QEMU, that means that subsequent QMP or +# HMP commands must use node-names for the image in +# question, as filename lookup methods will fail. # -# If not specified, QEMU will automatically determine -# the backing file string to use, or error out if there -# is no obvious choice. Care should be taken when -# specifying the string, to specify a valid filename or -# protocol. -# (Since 2.1) +# If not specified, QEMU will automatically determine +# the backing file string to use, or error out if there +# is no obvious choice. Care should be taken when +# specifying the string, to specify a valid filename or +# protocol. +# (Since 2.1) # -# @speed: the maximum speed, in bytes per second +# @speed: the maximum speed, in bytes per second # # @on-error: the action to take on an error (default report). # 'stop' and 'enospc' can only be used if the block device @@ -2622,7 +2522,8 @@ # list without user intervention. # Defaults to true. (Since 3.1) # -# Returns: Nothing on success. If @device does not exist, DeviceNotFound. +# Returns: - Nothing on success. +# - If @device does not exist, DeviceNotFound. # # Since: 1.1 # @@ -2653,11 +2554,11 @@ # the name of the parameter), but since QEMU 2.7 it can have # other values. # -# @speed: the maximum speed, in bytes per second, or 0 for unlimited. -# Defaults to 0. +# @speed: the maximum speed, in bytes per second, or 0 for unlimited. +# Defaults to 0. # -# Returns: Nothing on success -# If no background operation is active on this device, DeviceNotActive +# Returns: - Nothing on success +# - If no background operation is active on this device, DeviceNotActive # # Since: 1.1 ## @@ -2696,8 +2597,8 @@ # abandon the job immediately (even if it is paused) instead of waiting # for the destination to complete its final synchronization (since 1.3) # -# Returns: Nothing on success -# If no background operation is active on this device, DeviceNotActive +# Returns: - Nothing on success +# - If no background operation is active on this device, DeviceNotActive # # Since: 1.1 ## @@ -2720,8 +2621,8 @@ # the name of the parameter), but since QEMU 2.7 it can have # other values. # -# Returns: Nothing on success -# If no background operation is active on this device, DeviceNotActive +# Returns: - Nothing on success +# - If no background operation is active on this device, DeviceNotActive # # Since: 1.3 ## @@ -2742,8 +2643,8 @@ # the name of the parameter), but since QEMU 2.7 it can have # other values. # -# Returns: Nothing on success -# If no background operation is active on this device, DeviceNotActive +# Returns: - Nothing on success +# - If no background operation is active on this device, DeviceNotActive # # Since: 1.3 ## @@ -2770,8 +2671,8 @@ # the name of the parameter), but since QEMU 2.7 it can have # other values. # -# Returns: Nothing on success -# If no background operation is active on this device, DeviceNotActive +# Returns: - Nothing on success +# - If no background operation is active on this device, DeviceNotActive # # Since: 1.3 ## @@ -2820,8 +2721,8 @@ # # Determines how to handle discard requests. # -# @ignore: Ignore the request -# @unmap: Forward as an unmap request +# @ignore: Ignore the request +# @unmap: Forward as an unmap request # # Since: 2.9 ## @@ -2834,10 +2735,10 @@ # Describes the operation mode for the automatic conversion of plain # zero writes by the OS to driver specific optimized zero write commands. # -# @off: Disabled (default) -# @on: Enabled -# @unmap: Enabled and even try to unmap blocks if possible. This requires -# also that @BlockdevDiscardOptions is set to unmap for this device. +# @off: Disabled (default) +# @on: Enabled +# @unmap: Enabled and even try to unmap blocks if possible. This requires +# also that @BlockdevDiscardOptions is set to unmap for this device. # # Since: 2.1 ## @@ -2849,23 +2750,25 @@ # # Selects the AIO backend to handle I/O requests # -# @threads: Use qemu's thread pool -# @native: Use native AIO backend (only Linux and Windows) +# @threads: Use qemu's thread pool +# @native: Use native AIO backend (only Linux and Windows) +# @io_uring: Use linux io_uring (since 5.0) # # Since: 2.9 ## { 'enum': 'BlockdevAioOptions', - 'data': [ 'threads', 'native' ] } + 'data': [ 'threads', 'native', + { 'name': 'io_uring', 'if': 'defined(CONFIG_LINUX_IO_URING)' } ] } ## # @BlockdevCacheOptions: # # Includes cache-related options for block devices # -# @direct: enables use of O_DIRECT (bypass the host page cache; -# default: false) -# @no-flush: ignore any flush requests for the device (default: -# false) +# @direct: enables use of O_DIRECT (bypass the host page cache; +# default: false) +# @no-flush: ignore any flush requests for the device (default: +# false) # # Since: 2.9 ## @@ -2884,15 +2787,16 @@ # @copy-on-read: Since 3.0 # @blklogwrites: Since 3.0 # @blkreplay: Since 4.2 +# @compress: Since 5.0 # # Since: 2.9 ## { 'enum': 'BlockdevDriver', 'data': [ 'blkdebug', 'blklogwrites', 'blkreplay', 'blkverify', 'bochs', - 'cloop', 'copy-on-read', 'dmg', 'file', 'ftp', 'ftps', 'gluster', - 'host_cdrom', 'host_device', 'http', 'https', 'iscsi', 'luks', - 'nbd', 'nfs', 'null-aio', 'null-co', 'nvme', 'parallels', 'qcow', - 'qcow2', 'qed', 'quorum', 'raw', 'rbd', + 'cloop', 'compress', 'copy-on-read', 'dmg', 'file', 'ftp', 'ftps', + 'gluster', 'host_cdrom', 'host_device', 'http', 'https', 'iscsi', + 'luks', 'nbd', 'nfs', 'null-aio', 'null-co', 'nvme', 'parallels', + 'qcow', 'qcow2', 'qed', 'quorum', 'raw', 'rbd', { 'name': 'replication', 'if': 'defined(CONFIG_REPLICATION)' }, 'sheepdog', 'ssh', 'throttle', 'vdi', 'vhdx', 'vmdk', 'vpc', 'vvfat', 'vxhs' ] } @@ -2902,18 +2806,18 @@ # # Driver specific block device options for the file backend. # -# @filename: path to the image file -# @pr-manager: the id for the object that will handle persistent reservations -# for this device (default: none, forward the commands via SG_IO; -# since 2.11) -# @aio: AIO backend (default: threads) (since: 2.8) -# @locking: whether to enable file locking. If set to 'auto', only enable -# when Open File Descriptor (OFD) locking API is available -# (default: auto, since 2.10) -# @drop-cache: invalidate page cache during live migration. This prevents -# stale data on the migration destination with cache.direct=off. -# Currently only supported on Linux hosts. -# (default: on, since: 4.0) +# @filename: path to the image file +# @pr-manager: the id for the object that will handle persistent reservations +# for this device (default: none, forward the commands via SG_IO; +# since 2.11) +# @aio: AIO backend (default: threads) (since: 2.8) +# @locking: whether to enable file locking. If set to 'auto', only enable +# when Open File Descriptor (OFD) locking API is available +# (default: auto, since 2.10) +# @drop-cache: invalidate page cache during live migration. This prevents +# stale data on the migration destination with cache.direct=off. +# Currently only supported on Linux hosts. +# (default: on, since: 4.0) # @x-check-cache-dropped: whether to check that page cache was dropped on live # migration. May cause noticeable delays if the image # file is large, do not use in production. @@ -2935,8 +2839,8 @@ '*pr-manager': 'str', '*locking': 'OnOffAuto', '*aio': 'BlockdevAioOptions', - '*drop-cache': {'type': 'bool', - 'if': 'defined(CONFIG_LINUX)'}, + '*drop-cache': {'type': 'bool', + 'if': 'defined(CONFIG_LINUX)'}, '*x-check-cache-dropped': 'bool' }, 'features': [ { 'name': 'dynamic-auto-read-only', 'if': 'defined(CONFIG_POSIX)' } ] } @@ -2946,7 +2850,7 @@ # # Driver specific block device options for the null backend. # -# @size: size of the device in bytes. +# @size: size of the device in bytes. # @latency-ns: emulated latency (in nanoseconds) in processing # requests. Default to zero which completes requests immediately. # (Since 2.4) @@ -2963,9 +2867,13 @@ # # Driver specific block device options for the NVMe backend. # -# @device: controller address of the NVMe device. +# @device: PCI controller address of the NVMe device in +# format hhhh:bb:ss.f (host:bus:slot.function) # @namespace: namespace number of the device, starting from 1. # +# Note that the PCI @device must have been unbound from any host +# kernel driver before instructing QEMU to add the blockdev. +# # Since: 2.12 ## { 'struct': 'BlockdevOptionsNVMe', @@ -2976,15 +2884,15 @@ # # Driver specific block device options for the vvfat protocol. # -# @dir: directory to be exported as FAT image -# @fat-type: FAT type: 12, 16 or 32 -# @floppy: whether to export a floppy image (true) or -# partitioned hard disk (false; default) -# @label: set the volume label, limited to 11 bytes. FAT16 and -# FAT32 traditionally have some restrictions on labels, which are -# ignored by most operating systems. Defaults to "QEMU VVFAT". -# (since 2.4) -# @rw: whether to allow write operations (default: false) +# @dir: directory to be exported as FAT image +# @fat-type: FAT type: 12, 16 or 32 +# @floppy: whether to export a floppy image (true) or +# partitioned hard disk (false; default) +# @label: set the volume label, limited to 11 bytes. FAT16 and +# FAT32 traditionally have some restrictions on labels, which are +# ignored by most operating systems. Defaults to "QEMU VVFAT". +# (since 2.4) +# @rw: whether to allow write operations (default: false) # # Since: 2.9 ## @@ -2998,7 +2906,7 @@ # Driver specific block device options for image format that have no option # besides their data source. # -# @file: reference to or definition of the data source block device +# @file: reference to or definition of the data source block device # # Since: 2.9 ## @@ -3027,9 +2935,9 @@ # Driver specific block device options for image format that have no option # besides their data source and an optional backing file. # -# @backing: reference to or definition of the backing file block -# device, null disables the backing file entirely. -# Defaults to the backing file stored the image file. +# @backing: reference to or definition of the backing file block +# device, null disables the backing file entirely. +# Defaults to the backing file stored the image file. # # Since: 2.9 ## @@ -3042,15 +2950,15 @@ # # General overlap check modes. # -# @none: Do not perform any checks +# @none: Do not perform any checks # -# @constant: Perform only checks which can be done in constant time and -# without reading anything from disk +# @constant: Perform only checks which can be done in constant time and +# without reading anything from disk # -# @cached: Perform only checks which can be done without reading anything -# from disk +# @cached: Perform only checks which can be done without reading anything +# from disk # -# @all: Perform all available overlap checks +# @all: Perform all available overlap checks # # Since: 2.9 ## @@ -3089,10 +2997,10 @@ # Specifies which metadata structures should be guarded against unintended # overwriting. # -# @flags: set of flags for separate specification of each metadata structure -# type +# @flags: set of flags for separate specification of each metadata structure +# type # -# @mode: named mode which chooses a specific set of flags +# @mode: named mode which chooses a specific set of flags # # Since: 2.9 ## @@ -3125,9 +3033,9 @@ # # Driver specific block device options for qcow. # -# @encrypt: Image decryption options. Mandatory for -# encrypted images, except when doing a metadata-only -# probe of the image. +# @encrypt: Image decryption options. Mandatory for +# encrypted images, except when doing a metadata-only +# probe of the image. # # Since: 2.10 ## @@ -3162,51 +3070,51 @@ # # Driver specific block device options for qcow2. # -# @lazy-refcounts: whether to enable the lazy refcounts -# feature (default is taken from the image file) +# @lazy-refcounts: whether to enable the lazy refcounts +# feature (default is taken from the image file) # -# @pass-discard-request: whether discard requests to the qcow2 -# device should be forwarded to the data source +# @pass-discard-request: whether discard requests to the qcow2 +# device should be forwarded to the data source # # @pass-discard-snapshot: whether discard requests for the data source # should be issued when a snapshot operation (e.g. # deleting a snapshot) frees clusters in the qcow2 file # -# @pass-discard-other: whether discard requests for the data source -# should be issued on other occasions where a cluster -# gets freed +# @pass-discard-other: whether discard requests for the data source +# should be issued on other occasions where a cluster +# gets freed # -# @overlap-check: which overlap checks to perform for writes -# to the image, defaults to 'cached' (since 2.2) +# @overlap-check: which overlap checks to perform for writes +# to the image, defaults to 'cached' (since 2.2) # -# @cache-size: the maximum total size of the L2 table and -# refcount block caches in bytes (since 2.2) +# @cache-size: the maximum total size of the L2 table and +# refcount block caches in bytes (since 2.2) # -# @l2-cache-size: the maximum size of the L2 table cache in -# bytes (since 2.2) +# @l2-cache-size: the maximum size of the L2 table cache in +# bytes (since 2.2) # -# @l2-cache-entry-size: the size of each entry in the L2 cache in -# bytes. It must be a power of two between 512 -# and the cluster size. The default value is -# the cluster size (since 2.12) +# @l2-cache-entry-size: the size of each entry in the L2 cache in +# bytes. It must be a power of two between 512 +# and the cluster size. The default value is +# the cluster size (since 2.12) # -# @refcount-cache-size: the maximum size of the refcount block cache -# in bytes (since 2.2) +# @refcount-cache-size: the maximum size of the refcount block cache +# in bytes (since 2.2) # -# @cache-clean-interval: clean unused entries in the L2 and refcount -# caches. The interval is in seconds. The default value -# is 600 on supporting platforms, and 0 on other -# platforms. 0 disables this feature. (since 2.5) +# @cache-clean-interval: clean unused entries in the L2 and refcount +# caches. The interval is in seconds. The default value +# is 600 on supporting platforms, and 0 on other +# platforms. 0 disables this feature. (since 2.5) # -# @encrypt: Image decryption options. Mandatory for -# encrypted images, except when doing a metadata-only -# probe of the image. (since 2.10) +# @encrypt: Image decryption options. Mandatory for +# encrypted images, except when doing a metadata-only +# probe of the image. (since 2.10) # -# @data-file: reference to or definition of the external data file. -# This may only be specified for images that require an -# external data file. If it is not specified for such -# an image, the data file name is loaded from the image -# file. (since 4.0) +# @data-file: reference to or definition of the external data file. +# This may only be specified for images that require an +# external data file. If it is not specified for such +# an image, the data file name is loaded from the image +# file. (since 4.0) # # Since: 2.9 ## @@ -3228,9 +3136,9 @@ ## # @SshHostKeyCheckMode: # -# @none Don't check the host key at all -# @hash Compare the host key with a given hash -# @known_hosts Check the host key against the known_hosts file +# @none: Don't check the host key at all +# @hash: Compare the host key with a given hash +# @known_hosts: Check the host key against the known_hosts file # # Since: 2.12 ## @@ -3240,8 +3148,8 @@ ## # @SshHostKeyCheckHashType: # -# @md5 The given hash is an md5 hash -# @sha1 The given hash is an sha1 hash +# @md5: The given hash is an md5 hash +# @sha1: The given hash is an sha1 hash # # Since: 2.12 ## @@ -3251,8 +3159,8 @@ ## # @SshHostKeyHash: # -# @type The hash algorithm used for the hash -# @hash The expected hash value +# @type: The hash algorithm used for the hash +# @hash: The expected hash value # # Since: 2.12 ## @@ -3297,8 +3205,8 @@ # # Trigger events supported by blkdebug. # -# @l1_shrink_write_table: write zeros to the l1 table to shrink image. -# (since 2.11) +# @l1_shrink_write_table: write zeros to the l1 table to shrink image. +# (since 2.11) # # @l1_shrink_free_l2_clusters: discard the l2 tables. (since 2.11) # @@ -3356,25 +3264,25 @@ # # Describes a single error injection for blkdebug. # -# @event: trigger event +# @event: trigger event # -# @state: the state identifier blkdebug needs to be in to -# actually trigger the event; defaults to "any" +# @state: the state identifier blkdebug needs to be in to +# actually trigger the event; defaults to "any" # -# @iotype: the type of I/O operations on which this error should -# be injected; defaults to "all read, write, -# write-zeroes, discard, and flush operations" -# (since: 4.1) +# @iotype: the type of I/O operations on which this error should +# be injected; defaults to "all read, write, +# write-zeroes, discard, and flush operations" +# (since: 4.1) # -# @errno: error identifier (errno) to be returned; defaults to -# EIO +# @errno: error identifier (errno) to be returned; defaults to +# EIO # -# @sector: specifies the sector index which has to be affected -# in order to actually trigger the event; defaults to "any -# sector" +# @sector: specifies the sector index which has to be affected +# in order to actually trigger the event; defaults to "any +# sector" # -# @once: disables further events after this one has been -# triggered; defaults to false +# @once: disables further events after this one has been +# triggered; defaults to false # # @immediately: fail immediately; defaults to false # @@ -3394,13 +3302,13 @@ # # Describes a single state-change event for blkdebug. # -# @event: trigger event +# @event: trigger event # -# @state: the current state identifier blkdebug needs to be in; -# defaults to "any" +# @state: the current state identifier blkdebug needs to be in; +# defaults to "any" # -# @new_state: the state identifier blkdebug is supposed to assume if -# this event is triggered +# @new_state: the state identifier blkdebug is supposed to assume if +# this event is triggered # # Since: 2.9 ## @@ -3414,41 +3322,51 @@ # # Driver specific block device options for blkdebug. # -# @image: underlying raw block device (or image file) +# @image: underlying raw block device (or image file) # -# @config: filename of the configuration file +# @config: filename of the configuration file # -# @align: required alignment for requests in bytes, must be -# positive power of 2, or 0 for default +# @align: required alignment for requests in bytes, must be +# positive power of 2, or 0 for default # -# @max-transfer: maximum size for I/O transfers in bytes, must be -# positive multiple of @align and of the underlying -# file's request alignment (but need not be a power of -# 2), or 0 for default (since 2.10) +# @max-transfer: maximum size for I/O transfers in bytes, must be +# positive multiple of @align and of the underlying +# file's request alignment (but need not be a power of +# 2), or 0 for default (since 2.10) # -# @opt-write-zero: preferred alignment for write zero requests in bytes, -# must be positive multiple of @align and of the -# underlying file's request alignment (but need not be a -# power of 2), or 0 for default (since 2.10) +# @opt-write-zero: preferred alignment for write zero requests in bytes, +# must be positive multiple of @align and of the +# underlying file's request alignment (but need not be a +# power of 2), or 0 for default (since 2.10) # -# @max-write-zero: maximum size for write zero requests in bytes, must be -# positive multiple of @align, of @opt-write-zero, and of -# the underlying file's request alignment (but need not -# be a power of 2), or 0 for default (since 2.10) +# @max-write-zero: maximum size for write zero requests in bytes, must be +# positive multiple of @align, of @opt-write-zero, and of +# the underlying file's request alignment (but need not +# be a power of 2), or 0 for default (since 2.10) # -# @opt-discard: preferred alignment for discard requests in bytes, must -# be positive multiple of @align and of the underlying -# file's request alignment (but need not be a power of -# 2), or 0 for default (since 2.10) +# @opt-discard: preferred alignment for discard requests in bytes, must +# be positive multiple of @align and of the underlying +# file's request alignment (but need not be a power of +# 2), or 0 for default (since 2.10) # -# @max-discard: maximum size for discard requests in bytes, must be -# positive multiple of @align, of @opt-discard, and of -# the underlying file's request alignment (but need not -# be a power of 2), or 0 for default (since 2.10) +# @max-discard: maximum size for discard requests in bytes, must be +# positive multiple of @align, of @opt-discard, and of +# the underlying file's request alignment (but need not +# be a power of 2), or 0 for default (since 2.10) # -# @inject-error: array of error injection descriptions +# @inject-error: array of error injection descriptions # -# @set-state: array of state-change descriptions +# @set-state: array of state-change descriptions +# +# @take-child-perms: Permissions to take on @image in addition to what +# is necessary anyway (which depends on how the +# blkdebug node is used). Defaults to none. +# (since 5.0) +# +# @unshare-child-perms: Permissions not to share on @image in addition +# to what cannot be shared anyway (which depends +# on how the blkdebug node is used). Defaults +# to none. (since 5.0) # # Since: 2.9 ## @@ -3459,21 +3377,23 @@ '*opt-write-zero': 'int32', '*max-write-zero': 'int32', '*opt-discard': 'int32', '*max-discard': 'int32', '*inject-error': ['BlkdebugInjectErrorOptions'], - '*set-state': ['BlkdebugSetStateOptions'] } } + '*set-state': ['BlkdebugSetStateOptions'], + '*take-child-perms': ['BlockPermission'], + '*unshare-child-perms': ['BlockPermission'] } } ## # @BlockdevOptionsBlklogwrites: # # Driver specific block device options for blklogwrites. # -# @file: block device +# @file: block device # -# @log: block device used to log writes to @file +# @log: block device used to log writes to @file # # @log-sector-size: sector size used in logging writes to @file, determines # granularity of offsets and sizes of writes (default: 512) # -# @log-append: append to an existing log (default: false) +# @log-append: append to an existing log (default: false) # # @log-super-update-interval: interval of write requests after which the log # super block is updated to disk (default: 4096) @@ -3492,9 +3412,9 @@ # # Driver specific block device options for blkverify. # -# @test: block device to be tested +# @test: block device to be tested # -# @raw: raw image used for verification +# @raw: raw image used for verification # # Since: 2.9 ## @@ -3507,7 +3427,7 @@ # # Driver specific block device options for blkreplay. # -# @image: disk image which should be controlled with blkreplay +# @image: disk image which should be controlled with blkreplay # # Since: 4.2 ## @@ -3532,10 +3452,10 @@ # # Driver specific block device options for Quorum # -# @blkverify: true if the driver must print content mismatch +# @blkverify: true if the driver must print content mismatch # set to false by default # -# @children: the children block devices to use +# @children: the children block devices to use # # @vote-threshold: the vote limit under which a read will fail # @@ -3559,16 +3479,16 @@ # # Driver specific block device options for Gluster # -# @volume: name of gluster volume where VM image resides +# @volume: name of gluster volume where VM image resides # -# @path: absolute path to image file in gluster volume +# @path: absolute path to image file in gluster volume # -# @server: gluster servers description +# @server: gluster servers description # -# @debug: libgfapi log level (default '4' which is Error) -# (Since 2.8) +# @debug: libgfapi log level (default '4' which is Error) +# (Since 2.8) # -# @logfile: libgfapi log file (default /dev/stderr) (Since 2.8) +# @logfile: libgfapi log file (default /dev/stderr) (Since 2.8) # # Since: 2.9 ## @@ -3603,30 +3523,30 @@ ## # @BlockdevOptionsIscsi: # -# @transport: The iscsi transport type +# @transport: The iscsi transport type # -# @portal: The address of the iscsi portal +# @portal: The address of the iscsi portal # -# @target: The target iqn name +# @target: The target iqn name # -# @lun: LUN to connect to. Defaults to 0. +# @lun: LUN to connect to. Defaults to 0. # -# @user: User name to log in with. If omitted, no CHAP -# authentication is performed. +# @user: User name to log in with. If omitted, no CHAP +# authentication is performed. # # @password-secret: The ID of a QCryptoSecret object providing # the password for the login. This option is required if # @user is specified. # -# @initiator-name: The iqn name we want to identify to the target -# as. If this option is not specified, an initiator name is -# generated automatically. +# @initiator-name: The iqn name we want to identify to the target +# as. If this option is not specified, an initiator name is +# generated automatically. # -# @header-digest: The desired header digest. Defaults to -# none-crc32c. +# @header-digest: The desired header digest. Defaults to +# none-crc32c. # -# @timeout: Timeout in seconds after which a request will -# timeout. 0 means no timeout and is the default. +# @timeout: Timeout in seconds after which a request will +# timeout. 0 means no timeout and is the default. # # Driver specific block device options for iscsi # @@ -3655,34 +3575,37 @@ ## # @BlockdevOptionsRbd: # -# @pool: Ceph pool name. +# @pool: Ceph pool name. # -# @image: Image name in the Ceph pool. +# @namespace: Rados namespace name in the Ceph pool. (Since 5.0) # -# @conf: path to Ceph configuration file. Values -# in the configuration file will be overridden by -# options specified via QAPI. +# @image: Image name in the Ceph pool. # -# @snapshot: Ceph snapshot name. +# @conf: path to Ceph configuration file. Values +# in the configuration file will be overridden by +# options specified via QAPI. # -# @user: Ceph id name. +# @snapshot: Ceph snapshot name. +# +# @user: Ceph id name. # # @auth-client-required: Acceptable authentication modes. -# This maps to Ceph configuration option -# "auth_client_required". (Since 3.0) +# This maps to Ceph configuration option +# "auth_client_required". (Since 3.0) # -# @key-secret: ID of a QCryptoSecret object providing a key -# for cephx authentication. -# This maps to Ceph configuration option -# "key". (Since 3.0) +# @key-secret: ID of a QCryptoSecret object providing a key +# for cephx authentication. +# This maps to Ceph configuration option +# "key". (Since 3.0) # -# @server: Monitor host address and port. This maps -# to the "mon_host" Ceph option. +# @server: Monitor host address and port. This maps +# to the "mon_host" Ceph option. # # Since: 2.9 ## { 'struct': 'BlockdevOptionsRbd', 'data': { 'pool': 'str', + '*namespace': 'str', 'image': 'str', '*conf': 'str', '*snapshot': 'str', @@ -3696,10 +3619,10 @@ # # Driver specific block device options for sheepdog # -# @vdi: Virtual disk image name -# @server: The Sheepdog server to connect to -# @snap-id: Snapshot ID -# @tag: Snapshot tag name +# @vdi: Virtual disk image name +# @server: The Sheepdog server to connect to +# @snap-id: Snapshot ID +# @tag: Snapshot tag name # # Only one of @snap-id and @tag may be present. # @@ -3749,7 +3672,7 @@ # # An enumeration of NFS transport types # -# @inet: TCP transport +# @inet: TCP transport # # Since: 2.9 ## @@ -3761,9 +3684,9 @@ # # Captures the address of the socket # -# @type: transport type used for NFS (only TCP supported) +# @type: transport type used for NFS (only TCP supported) # -# @host: host address for NFS server +# @host: host address for NFS server # # Since: 2.9 ## @@ -3776,29 +3699,29 @@ # # Driver specific block device option for NFS # -# @server: host address +# @server: host address # -# @path: path of the image on the host +# @path: path of the image on the host # -# @user: UID value to use when talking to the -# server (defaults to 65534 on Windows and getuid() -# on unix) +# @user: UID value to use when talking to the +# server (defaults to 65534 on Windows and getuid() +# on unix) # -# @group: GID value to use when talking to the -# server (defaults to 65534 on Windows and getgid() -# in unix) +# @group: GID value to use when talking to the +# server (defaults to 65534 on Windows and getgid() +# in unix) # -# @tcp-syn-count: number of SYNs during the session -# establishment (defaults to libnfs default) +# @tcp-syn-count: number of SYNs during the session +# establishment (defaults to libnfs default) # -# @readahead-size: set the readahead size in bytes (defaults -# to libnfs default) +# @readahead-size: set the readahead size in bytes (defaults +# to libnfs default) # -# @page-cache-size: set the pagecache size in bytes (defaults -# to libnfs default) +# @page-cache-size: set the pagecache size in bytes (defaults +# to libnfs default) # -# @debug: set the NFS debug level (max 2) (defaults -# to libnfs default) +# @debug: set the NFS debug level (max 2) (defaults +# to libnfs default) # # Since: 2.9 ## @@ -3818,22 +3741,22 @@ # Driver specific block device options shared by all protocols supported by the # curl backend. # -# @url: URL of the image file +# @url: URL of the image file # -# @readahead: Size of the read-ahead cache; must be a multiple of -# 512 (defaults to 256 kB) +# @readahead: Size of the read-ahead cache; must be a multiple of +# 512 (defaults to 256 kB) # -# @timeout: Timeout for connections, in seconds (defaults to 5) +# @timeout: Timeout for connections, in seconds (defaults to 5) # -# @username: Username for authentication (defaults to none) +# @username: Username for authentication (defaults to none) # -# @password-secret: ID of a QCryptoSecret object providing a password -# for authentication (defaults to no password) +# @password-secret: ID of a QCryptoSecret object providing a password +# for authentication (defaults to no password) # -# @proxy-username: Username for proxy authentication (defaults to none) +# @proxy-username: Username for proxy authentication (defaults to none) # -# @proxy-password-secret: ID of a QCryptoSecret object providing a password -# for proxy authentication (defaults to no password) +# @proxy-password-secret: ID of a QCryptoSecret object providing a password +# for proxy authentication (defaults to no password) # # Since: 2.9 ## @@ -3852,9 +3775,9 @@ # Driver specific block device options for HTTP connections over the curl # backend. URLs must start with "http://". # -# @cookie: List of cookies to set; format is -# "name1=content1; name2=content2;" as explained by -# CURLOPT_COOKIE(3). Defaults to no cookies. +# @cookie: List of cookies to set; format is +# "name1=content1; name2=content2;" as explained by +# CURLOPT_COOKIE(3). Defaults to no cookies. # # @cookie-secret: ID of a QCryptoSecret object providing the cookie data in a # secure way. See @cookie for the format. (since 2.10) @@ -3872,12 +3795,12 @@ # Driver specific block device options for HTTPS connections over the curl # backend. URLs must start with "https://". # -# @cookie: List of cookies to set; format is -# "name1=content1; name2=content2;" as explained by -# CURLOPT_COOKIE(3). Defaults to no cookies. +# @cookie: List of cookies to set; format is +# "name1=content1; name2=content2;" as explained by +# CURLOPT_COOKIE(3). Defaults to no cookies. # -# @sslverify: Whether to verify the SSL certificate's validity (defaults to -# true) +# @sslverify: Whether to verify the SSL certificate's validity (defaults to +# true) # # @cookie-secret: ID of a QCryptoSecret object providing the cookie data in a # secure way. See @cookie for the format. (since 2.10) @@ -3908,8 +3831,8 @@ # Driver specific block device options for FTPS connections over the curl # backend. URLs must start with "ftps://". # -# @sslverify: Whether to verify the SSL certificate's validity (defaults to -# true) +# @sslverify: Whether to verify the SSL certificate's validity (defaults to +# true) # # Since: 2.9 ## @@ -3922,11 +3845,11 @@ # # Driver specific block device options for NBD. # -# @server: NBD server address +# @server: NBD server address # -# @export: export name +# @export: export name # -# @tls-creds: TLS credentials ID +# @tls-creds: TLS credentials ID # # @x-dirty-bitmap: A "qemu:dirty-bitmap:NAME" string to query in place of # traditional "base:allocation" block status (see @@ -3954,8 +3877,8 @@ # # Driver specific block device options for the raw driver. # -# @offset: position where the block device starts -# @size: the assumed size of the device +# @offset: position where the block device starts +# @size: the assumed size of the device # # Since: 2.9 ## @@ -3968,9 +3891,9 @@ # # Driver specific block device options for VxHS # -# @vdisk-id: UUID of VxHS volume -# @server: vxhs server IP, port -# @tls-creds: TLS credentials ID +# @vdisk-id: UUID of VxHS volume +# @server: vxhs server IP, port +# @tls-creds: TLS credentials ID # # Since: 2.10 ## @@ -3984,9 +3907,9 @@ # # Driver specific block device options for the throttle driver # -# @throttle-group: the name of the throttle-group object to use. It -# must already exist. -# @file: reference to or definition of the data source block device +# @throttle-group: the name of the throttle-group object to use. It +# must already exist. +# @file: reference to or definition of the data source block device # Since: 2.11 ## { 'struct': 'BlockdevOptionsThrottle', @@ -3999,19 +3922,19 @@ # Options for creating a block device. Many options are available for all # block devices, independent of the block driver: # -# @driver: block driver name -# @node-name: the node name of the new node (Since 2.0). -# This option is required on the top level of blockdev-add. -# Valid node names start with an alphabetic character and may -# contain only alphanumeric characters, '-', '.' and '_'. Their -# maximum length is 31 characters. -# @discard: discard-related options (default: ignore) -# @cache: cache-related options -# @read-only: whether the block device should be read-only (default: false). -# Note that some block drivers support only read-only access, -# either generally or in certain configurations. In this case, -# the default value does not work and the option must be -# specified explicitly. +# @driver: block driver name +# @node-name: the node name of the new node (Since 2.0). +# This option is required on the top level of blockdev-add. +# Valid node names start with an alphabetic character and may +# contain only alphanumeric characters, '-', '.' and '_'. Their +# maximum length is 31 characters. +# @discard: discard-related options (default: ignore) +# @cache: cache-related options +# @read-only: whether the block device should be read-only (default: false). +# Note that some block drivers support only read-only access, +# either generally or in certain configurations. In this case, +# the default value does not work and the option must be +# specified explicitly. # @auto-read-only: if true and @read-only is false, QEMU may automatically # decide not to open the image read-write as requested, but # fall back to read-only instead (and switch between the modes @@ -4020,8 +3943,8 @@ # (default: false, since 3.1) # @detect-zeroes: detect and optimize zero writes (Since 2.1) # (default: off) -# @force-share: force share all permission on added nodes. -# Requires read-only=true. (Since 2.10) +# @force-share: force share all permission on added nodes. +# Requires read-only=true. (Since 2.10) # # Remaining options are determined by the block driver. # @@ -4044,6 +3967,7 @@ 'blkreplay': 'BlockdevOptionsBlkreplay', 'bochs': 'BlockdevOptionsGenericFormat', 'cloop': 'BlockdevOptionsGenericFormat', + 'compress': 'BlockdevOptionsGenericFormat', 'copy-on-read':'BlockdevOptionsGenericFormat', 'dmg': 'BlockdevOptionsGenericFormat', 'file': 'BlockdevOptionsFile', @@ -4086,8 +4010,8 @@ # # Reference to a block device. # -# @definition: defines a new block device inline -# @reference: references the ID of an existing block device +# @definition: defines a new block device inline +# @reference: references the ID of an existing block device # # Since: 2.9 ## @@ -4100,11 +4024,11 @@ # # Reference to a block device. # -# @definition: defines a new block device inline -# @reference: references the ID of an existing block device. -# An empty string means that no block device should -# be referenced. Deprecated; use null instead. -# @null: No block device should be referenced (since 2.10) +# @definition: defines a new block device inline +# @reference: references the ID of an existing block device. +# An empty string means that no block device should +# be referenced. Deprecated; use null instead. +# @null: No block device should be referenced (since 2.10) # # Since: 2.9 ## @@ -4245,13 +4169,13 @@ # # Driver specific image creation options for file. # -# @filename Filename for the new image file -# @size Size of the virtual disk in bytes -# @preallocation Preallocation mode for the new image (default: off; -# allowed values: off, -# falloc (if defined CONFIG_POSIX_FALLOCATE), -# full (if defined CONFIG_POSIX)) -# @nocow Turn off copy-on-write (valid only on btrfs; default: off) +# @filename: Filename for the new image file +# @size: Size of the virtual disk in bytes +# @preallocation: Preallocation mode for the new image (default: off; +# allowed values: off, +# falloc (if defined CONFIG_POSIX_FALLOCATE), +# full (if defined CONFIG_POSIX)) +# @nocow: Turn off copy-on-write (valid only on btrfs; default: off) # # Since: 2.12 ## @@ -4266,12 +4190,12 @@ # # Driver specific image creation options for gluster. # -# @location Where to store the new image file -# @size Size of the virtual disk in bytes -# @preallocation Preallocation mode for the new image (default: off; -# allowed values: off, -# falloc (if defined CONFIG_GLUSTERFS_FALLOCATE), -# full (if defined CONFIG_GLUSTERFS_ZEROFILL)) +# @location: Where to store the new image file +# @size: Size of the virtual disk in bytes +# @preallocation: Preallocation mode for the new image (default: off; +# allowed values: off, +# falloc (if defined CONFIG_GLUSTERFS_FALLOCATE), +# full (if defined CONFIG_GLUSTERFS_ZEROFILL)) # # Since: 2.12 ## @@ -4285,11 +4209,11 @@ # # Driver specific image creation options for LUKS. # -# @file Node to create the image format on -# @size Size of the virtual disk in bytes -# @preallocation Preallocation mode for the new image -# (since: 4.2) -# (default: off; allowed values: off, metadata, falloc, full) +# @file: Node to create the image format on +# @size: Size of the virtual disk in bytes +# @preallocation: Preallocation mode for the new image +# (since: 4.2) +# (default: off; allowed values: off, metadata, falloc, full) # # Since: 2.12 ## @@ -4304,8 +4228,8 @@ # # Driver specific image creation options for NFS. # -# @location Where to store the new image file -# @size Size of the virtual disk in bytes +# @location: Where to store the new image file +# @size: Size of the virtual disk in bytes # # Since: 2.12 ## @@ -4318,9 +4242,9 @@ # # Driver specific image creation options for parallels. # -# @file Node to create the image format on -# @size Size of the virtual disk in bytes -# @cluster-size Cluster size in bytes (default: 1 MB) +# @file: Node to create the image format on +# @size: Size of the virtual disk in bytes +# @cluster-size: Cluster size in bytes (default: 1 MB) # # Since: 2.12 ## @@ -4334,11 +4258,11 @@ # # Driver specific image creation options for qcow. # -# @file Node to create the image format on -# @size Size of the virtual disk in bytes -# @backing-file File name of the backing file if a backing file -# should be used -# @encrypt Encryption options if the image should be encrypted +# @file: Node to create the image format on +# @size: Size of the virtual disk in bytes +# @backing-file: File name of the backing file if a backing file +# should be used +# @encrypt: Encryption options if the image should be encrypted # # Since: 2.12 ## @@ -4365,24 +4289,24 @@ # # Driver specific image creation options for qcow2. # -# @file Node to create the image format on -# @data-file Node to use as an external data file in which all guest -# data is stored so that only metadata remains in the qcow2 -# file (since: 4.0) -# @data-file-raw True if the external data file must stay valid as a -# standalone (read-only) raw image without looking at qcow2 -# metadata (default: false; since: 4.0) -# @size Size of the virtual disk in bytes -# @version Compatibility level (default: v3) -# @backing-file File name of the backing file if a backing file -# should be used -# @backing-fmt Name of the block driver to use for the backing file -# @encrypt Encryption options if the image should be encrypted -# @cluster-size qcow2 cluster size in bytes (default: 65536) -# @preallocation Preallocation mode for the new image (default: off; -# allowed values: off, falloc, full, metadata) -# @lazy-refcounts True if refcounts may be updated lazily (default: off) -# @refcount-bits Width of reference counts in bits (default: 16) +# @file: Node to create the image format on +# @data-file: Node to use as an external data file in which all guest +# data is stored so that only metadata remains in the qcow2 +# file (since: 4.0) +# @data-file-raw: True if the external data file must stay valid as a +# standalone (read-only) raw image without looking at qcow2 +# metadata (default: false; since: 4.0) +# @size: Size of the virtual disk in bytes +# @version: Compatibility level (default: v3) +# @backing-file: File name of the backing file if a backing file +# should be used +# @backing-fmt: Name of the block driver to use for the backing file +# @encrypt: Encryption options if the image should be encrypted +# @cluster-size: qcow2 cluster size in bytes (default: 65536) +# @preallocation: Preallocation mode for the new image (default: off; +# allowed values: off, falloc, full, metadata) +# @lazy-refcounts: True if refcounts may be updated lazily (default: off) +# @refcount-bits: Width of reference counts in bits (default: 16) # # Since: 2.12 ## @@ -4405,13 +4329,13 @@ # # Driver specific image creation options for qed. # -# @file Node to create the image format on -# @size Size of the virtual disk in bytes -# @backing-file File name of the backing file if a backing file -# should be used -# @backing-fmt Name of the block driver to use for the backing file -# @cluster-size Cluster size in bytes (default: 65536) -# @table-size L1/L2 table size (in clusters) +# @file: Node to create the image format on +# @size: Size of the virtual disk in bytes +# @backing-file: File name of the backing file if a backing file +# should be used +# @backing-fmt: Name of the block driver to use for the backing file +# @cluster-size: Cluster size in bytes (default: 65536) +# @table-size: L1/L2 table size (in clusters) # # Since: 2.12 ## @@ -4428,10 +4352,10 @@ # # Driver specific image creation options for rbd/Ceph. # -# @location Where to store the new image file. This location cannot -# point to a snapshot. -# @size Size of the virtual disk in bytes -# @cluster-size RBD object size +# @location: Where to store the new image file. This location cannot +# point to a snapshot. +# @size: Size of the virtual disk in bytes +# @cluster-size: RBD object size # # Since: 2.12 ## @@ -4479,23 +4403,23 @@ # # Driver specific image creation options for VMDK. # -# @file Where to store the new image file. This refers to the image -# file for monolithcSparse and streamOptimized format, or the -# descriptor file for other formats. -# @size Size of the virtual disk in bytes -# @extents Where to store the data extents. Required for monolithcFlat, -# twoGbMaxExtentSparse and twoGbMaxExtentFlat formats. For -# monolithicFlat, only one entry is required; for -# twoGbMaxExtent* formats, the number of entries required is -# calculated as extent_number = virtual_size / 2GB. Providing -# more extents than will be used is an error. -# @subformat The subformat of the VMDK image. Default: "monolithicSparse". -# @backing-file The path of backing file. Default: no backing file is used. -# @adapter-type The adapter type used to fill in the descriptor. Default: ide. -# @hwversion Hardware version. The meaningful options are "4" or "6". -# Default: "4". -# @zeroed-grain Whether to enable zeroed-grain feature for sparse subformats. -# Default: false. +# @file: Where to store the new image file. This refers to the image +# file for monolithcSparse and streamOptimized format, or the +# descriptor file for other formats. +# @size: Size of the virtual disk in bytes +# @extents: Where to store the data extents. Required for monolithcFlat, +# twoGbMaxExtentSparse and twoGbMaxExtentFlat formats. For +# monolithicFlat, only one entry is required; for +# twoGbMaxExtent* formats, the number of entries required is +# calculated as extent_number = virtual_size / 2GB. Providing +# more extents than will be used is an error. +# @subformat: The subformat of the VMDK image. Default: "monolithicSparse". +# @backing-file: The path of backing file. Default: no backing file is used. +# @adapter-type: The adapter type used to fill in the descriptor. Default: ide. +# @hwversion: Hardware version. The meaningful options are "4" or "6". +# Default: "4". +# @zeroed-grain: Whether to enable zeroed-grain feature for sparse subformats. +# Default: false. # # Since: 4.0 ## @@ -4513,9 +4437,9 @@ ## # @SheepdogRedundancyType: # -# @full Create a fully replicated vdi with x copies -# @erasure-coded Create an erasure coded vdi with x data strips and -# y parity strips +# @full: Create a fully replicated vdi with x copies +# @erasure-coded: Create an erasure coded vdi with x data strips and +# y parity strips # # Since: 2.12 ## @@ -4525,7 +4449,7 @@ ## # @SheepdogRedundancyFull: # -# @copies Number of copies to use (between 1 and 31) +# @copies: Number of copies to use (between 1 and 31) # # Since: 2.12 ## @@ -4535,8 +4459,8 @@ ## # @SheepdogRedundancyErasureCoded: # -# @data-strips Number of data strips to use (one of {2,4,8,16}) -# @parity-strips Number of parity strips to use (between 1 and 15) +# @data-strips: Number of data strips to use (one of {2,4,8,16}) +# @parity-strips: Number of parity strips to use (between 1 and 15) # # Since: 2.12 ## @@ -4560,13 +4484,13 @@ # # Driver specific image creation options for Sheepdog. # -# @location Where to store the new image file -# @size Size of the virtual disk in bytes -# @backing-file File name of a base image -# @preallocation Preallocation mode for the new image (default: off; -# allowed values: off, full) -# @redundancy Redundancy of the image -# @object-size Object size of the image +# @location: Where to store the new image file +# @size: Size of the virtual disk in bytes +# @backing-file: File name of a base image +# @preallocation: Preallocation mode for the new image (default: off; +# allowed values: off, full) +# @redundancy: Redundancy of the image +# @object-size: Object size of the image # # Since: 2.12 ## @@ -4583,8 +4507,8 @@ # # Driver specific image creation options for SSH. # -# @location Where to store the new image file -# @size Size of the virtual disk in bytes +# @location: Where to store the new image file +# @size: Size of the virtual disk in bytes # # Since: 2.12 ## @@ -4597,10 +4521,10 @@ # # Driver specific image creation options for VDI. # -# @file Node to create the image format on -# @size Size of the virtual disk in bytes -# @preallocation Preallocation mode for the new image (default: off; -# allowed values: off, metadata) +# @file: Node to create the image format on +# @size: Size of the virtual disk in bytes +# @preallocation: Preallocation mode for the new image (default: off; +# allowed values: off, metadata) # # Since: 2.12 ## @@ -4625,17 +4549,17 @@ # # Driver specific image creation options for vhdx. # -# @file Node to create the image format on -# @size Size of the virtual disk in bytes -# @log-size Log size in bytes, must be a multiple of 1 MB -# (default: 1 MB) -# @block-size Block size in bytes, must be a multiple of 1 MB and not -# larger than 256 MB (default: automatically choose a block -# size depending on the image size) -# @subformat vhdx subformat (default: dynamic) -# @block-state-zero Force use of payload blocks of type 'ZERO'. Non-standard, -# but default. Do not set to 'off' when using 'qemu-img -# convert' with subformat=dynamic. +# @file: Node to create the image format on +# @size: Size of the virtual disk in bytes +# @log-size: Log size in bytes, must be a multiple of 1 MB +# (default: 1 MB) +# @block-size: Block size in bytes, must be a multiple of 1 MB and not +# larger than 256 MB (default: automatically choose a block +# size depending on the image size) +# @subformat: vhdx subformat (default: dynamic) +# @block-state-zero: Force use of payload blocks of type 'ZERO'. Non-standard, +# but default. Do not set to 'off' when using 'qemu-img +# convert' with subformat=dynamic. # # Since: 2.12 ## @@ -4663,12 +4587,12 @@ # # Driver specific image creation options for vpc (VHD). # -# @file Node to create the image format on -# @size Size of the virtual disk in bytes -# @subformat vhdx subformat (default: dynamic) -# @force-size Force use of the exact byte size instead of rounding to the -# next size that can be represented in CHS geometry -# (default: false) +# @file: Node to create the image format on +# @size: Size of the virtual disk in bytes +# @subformat: vhdx subformat (default: dynamic) +# @force-size: Force use of the exact byte size instead of rounding to the +# next size that can be represented in CHS geometry +# (default: false) # # Since: 2.12 ## @@ -4683,7 +4607,7 @@ # # Options for creating an image format on a given node. # -# @driver block driver to create the image format +# @driver: block driver to create the image format # # Since: 2.12 ## @@ -4725,247 +4649,6 @@ 'data': { 'job-id': 'str', 'options': 'BlockdevCreateOptions' } } -## -# @blockdev-open-tray: -# -# Opens a block device's tray. If there is a block driver state tree inserted as -# a medium, it will become inaccessible to the guest (but it will remain -# associated to the block device, so closing the tray will make it accessible -# again). -# -# If the tray was already open before, this will be a no-op. -# -# Once the tray opens, a DEVICE_TRAY_MOVED event is emitted. There are cases in -# which no such event will be generated, these include: -# - if the guest has locked the tray, @force is false and the guest does not -# respond to the eject request -# - if the BlockBackend denoted by @device does not have a guest device attached -# to it -# - if the guest device does not have an actual tray -# -# @device: Block device name (deprecated, use @id instead) -# -# @id: The name or QOM path of the guest device (since: 2.8) -# -# @force: if false (the default), an eject request will be sent to -# the guest if it has locked the tray (and the tray will not be opened -# immediately); if true, the tray will be opened regardless of whether -# it is locked -# -# Since: 2.5 -# -# Example: -# -# -> { "execute": "blockdev-open-tray", -# "arguments": { "id": "ide0-1-0" } } -# -# <- { "timestamp": { "seconds": 1418751016, -# "microseconds": 716996 }, -# "event": "DEVICE_TRAY_MOVED", -# "data": { "device": "ide1-cd0", -# "id": "ide0-1-0", -# "tray-open": true } } -# -# <- { "return": {} } -# -## -{ 'command': 'blockdev-open-tray', - 'data': { '*device': 'str', - '*id': 'str', - '*force': 'bool' } } - -## -# @blockdev-close-tray: -# -# Closes a block device's tray. If there is a block driver state tree associated -# with the block device (which is currently ejected), that tree will be loaded -# as the medium. -# -# If the tray was already closed before, this will be a no-op. -# -# @device: Block device name (deprecated, use @id instead) -# -# @id: The name or QOM path of the guest device (since: 2.8) -# -# Since: 2.5 -# -# Example: -# -# -> { "execute": "blockdev-close-tray", -# "arguments": { "id": "ide0-1-0" } } -# -# <- { "timestamp": { "seconds": 1418751345, -# "microseconds": 272147 }, -# "event": "DEVICE_TRAY_MOVED", -# "data": { "device": "ide1-cd0", -# "id": "ide0-1-0", -# "tray-open": false } } -# -# <- { "return": {} } -# -## -{ 'command': 'blockdev-close-tray', - 'data': { '*device': 'str', - '*id': 'str' } } - -## -# @blockdev-remove-medium: -# -# Removes a medium (a block driver state tree) from a block device. That block -# device's tray must currently be open (unless there is no attached guest -# device). -# -# If the tray is open and there is no medium inserted, this will be a no-op. -# -# @id: The name or QOM path of the guest device -# -# Since: 2.12 -# -# Example: -# -# -> { "execute": "blockdev-remove-medium", -# "arguments": { "id": "ide0-1-0" } } -# -# <- { "error": { "class": "GenericError", -# "desc": "Tray of device 'ide0-1-0' is not open" } } -# -# -> { "execute": "blockdev-open-tray", -# "arguments": { "id": "ide0-1-0" } } -# -# <- { "timestamp": { "seconds": 1418751627, -# "microseconds": 549958 }, -# "event": "DEVICE_TRAY_MOVED", -# "data": { "device": "ide1-cd0", -# "id": "ide0-1-0", -# "tray-open": true } } -# -# <- { "return": {} } -# -# -> { "execute": "blockdev-remove-medium", -# "arguments": { "id": "ide0-1-0" } } -# -# <- { "return": {} } -# -## -{ 'command': 'blockdev-remove-medium', - 'data': { 'id': 'str' } } - -## -# @blockdev-insert-medium: -# -# Inserts a medium (a block driver state tree) into a block device. That block -# device's tray must currently be open (unless there is no attached guest -# device) and there must be no medium inserted already. -# -# @id: The name or QOM path of the guest device -# -# @node-name: name of a node in the block driver state graph -# -# Since: 2.12 -# -# Example: -# -# -> { "execute": "blockdev-add", -# "arguments": { -# "node-name": "node0", -# "driver": "raw", -# "file": { "driver": "file", -# "filename": "fedora.iso" } } } -# <- { "return": {} } -# -# -> { "execute": "blockdev-insert-medium", -# "arguments": { "id": "ide0-1-0", -# "node-name": "node0" } } -# -# <- { "return": {} } -# -## -{ 'command': 'blockdev-insert-medium', - 'data': { 'id': 'str', - 'node-name': 'str'} } - - -## -# @BlockdevChangeReadOnlyMode: -# -# Specifies the new read-only mode of a block device subject to the -# @blockdev-change-medium command. -# -# @retain: Retains the current read-only mode -# -# @read-only: Makes the device read-only -# -# @read-write: Makes the device writable -# -# Since: 2.3 -# -## -{ 'enum': 'BlockdevChangeReadOnlyMode', - 'data': ['retain', 'read-only', 'read-write'] } - - -## -# @blockdev-change-medium: -# -# Changes the medium inserted into a block device by ejecting the current medium -# and loading a new image file which is inserted as the new medium (this command -# combines blockdev-open-tray, blockdev-remove-medium, blockdev-insert-medium -# and blockdev-close-tray). -# -# @device: Block device name (deprecated, use @id instead) -# -# @id: The name or QOM path of the guest device -# (since: 2.8) -# -# @filename: filename of the new image to be loaded -# -# @format: format to open the new image with (defaults to -# the probed format) -# -# @read-only-mode: change the read-only mode of the device; defaults -# to 'retain' -# -# Since: 2.5 -# -# Examples: -# -# 1. Change a removable medium -# -# -> { "execute": "blockdev-change-medium", -# "arguments": { "id": "ide0-1-0", -# "filename": "/srv/images/Fedora-12-x86_64-DVD.iso", -# "format": "raw" } } -# <- { "return": {} } -# -# 2. Load a read-only medium into a writable drive -# -# -> { "execute": "blockdev-change-medium", -# "arguments": { "id": "floppyA", -# "filename": "/srv/images/ro.img", -# "format": "raw", -# "read-only-mode": "retain" } } -# -# <- { "error": -# { "class": "GenericError", -# "desc": "Could not open '/srv/images/ro.img': Permission denied" } } -# -# -> { "execute": "blockdev-change-medium", -# "arguments": { "id": "floppyA", -# "filename": "/srv/images/ro.img", -# "format": "raw", -# "read-only-mode": "read-only" } } -# -# <- { "return": {} } -# -## -{ 'command': 'blockdev-change-medium', - 'data': { '*device': 'str', - '*id': 'str', - 'filename': 'str', - '*format': 'str', - '*read-only-mode': 'BlockdevChangeReadOnlyMode' } } - - ## # @BlockErrorAction: # @@ -5008,8 +4691,8 @@ # the access size # # @fatal: if set, the image is marked corrupt and therefore unusable after this -# event and must be repaired (Since 2.2; before, every -# BLOCK_IMAGE_CORRUPTED event was fatal) +# event and must be repaired (Since 2.2; before, every +# BLOCK_IMAGE_CORRUPTED event was fatal) # # Note: If action is "stop", a STOP event will eventually follow the # BLOCK_IO_ERROR event. @@ -5057,10 +4740,10 @@ # # @reason: human readable string describing the error cause. # (This field is a debugging aid for humans, it should not -# be parsed by applications) (since: 2.2) +# be parsed by applications) (since: 2.2) # # Note: If action is "stop", a STOP event will eventually follow the -# BLOCK_IO_ERROR event +# BLOCK_IO_ERROR event # # Since: 0.13.0 # @@ -5202,7 +4885,7 @@ # @speed: rate limit, bytes per second # # Note: The "ready to complete" status is always reset by a @BLOCK_JOB_ERROR -# event +# event # # Since: 1.3 # @@ -5336,15 +5019,15 @@ # @node: the name of the node that will be added. # # Note: this command is experimental, and its API is not stable. It -# does not support all kinds of operations, all kinds of children, nor -# all block drivers. +# does not support all kinds of operations, all kinds of children, nor +# all block drivers. # -# FIXME Removing children from a quorum node means introducing gaps in the -# child indices. This cannot be represented in the 'children' list of -# BlockdevOptionsQuorum, as returned by .bdrv_refresh_filename(). +# FIXME Removing children from a quorum node means introducing gaps in the +# child indices. This cannot be represented in the 'children' list of +# BlockdevOptionsQuorum, as returned by .bdrv_refresh_filename(). # -# Warning: The data in a new quorum child MUST be consistent with that of -# the rest of the array. +# Warning: The data in a new quorum child MUST be consistent with that of +# the rest of the array. # # Since: 2.7 # @@ -5391,7 +5074,7 @@ # is already attached # # Note: this command is experimental and intended for test cases that need -# control over IOThreads only. +# control over IOThreads only. # # Since: 2.12 # @@ -5414,3 +5097,347 @@ 'data' : { 'node-name': 'str', 'iothread': 'StrOrNull', '*force': 'bool' } } + +## +# @NbdServerOptions: +# +# @addr: Address on which to listen. +# @tls-creds: ID of the TLS credentials object (since 2.6). +# @tls-authz: ID of the QAuthZ authorization object used to validate +# the client's x509 distinguished name. This object is +# is only resolved at time of use, so can be deleted and +# recreated on the fly while the NBD server is active. +# If missing, it will default to denying access (since 4.0). +# +# Keep this type consistent with the nbd-server-start arguments. The only +# intended difference is using SocketAddress instead of SocketAddressLegacy. +# +# Since: 4.2 +## +{ 'struct': 'NbdServerOptions', + 'data': { 'addr': 'SocketAddress', + '*tls-creds': 'str', + '*tls-authz': 'str'} } + +## +# @nbd-server-start: +# +# Start an NBD server listening on the given host and port. Block +# devices can then be exported using @nbd-server-add. The NBD +# server will present them as named exports; for example, another +# QEMU instance could refer to them as "nbd:HOST:PORT:exportname=NAME". +# +# @addr: Address on which to listen. +# @tls-creds: ID of the TLS credentials object (since 2.6). +# @tls-authz: ID of the QAuthZ authorization object used to validate +# the client's x509 distinguished name. This object is +# is only resolved at time of use, so can be deleted and +# recreated on the fly while the NBD server is active. +# If missing, it will default to denying access (since 4.0). +# +# Returns: error if the server is already running. +# +# Keep this type consistent with the NbdServerOptions type. The only intended +# difference is using SocketAddressLegacy instead of SocketAddress. +# +# Since: 1.3.0 +## +{ 'command': 'nbd-server-start', + 'data': { 'addr': 'SocketAddressLegacy', + '*tls-creds': 'str', + '*tls-authz': 'str'} } + +## +# @BlockExportNbd: +# +# An NBD block export. +# +# @device: The device name or node name of the node to be exported +# +# @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) +# +# Since: 5.0 +## +{ 'struct': 'BlockExportNbd', + 'data': {'device': 'str', '*name': 'str', '*description': 'str', + '*writable': 'bool', '*bitmap': 'str' } } + +## +# @nbd-server-add: +# +# Export a block node to QEMU's embedded NBD server. +# +# Returns: error if the server is not running, or export with the same name +# already exists. +# +# Since: 1.3.0 +## +{ 'command': 'nbd-server-add', + 'data': 'BlockExportNbd', 'boxed': true } + +## +# @NbdServerRemoveMode: +# +# Mode for removing an NBD export. +# +# @safe: Remove export if there are no existing connections, fail otherwise. +# +# @hard: Drop all connections immediately and remove export. +# +# Potential additional modes to be added in the future: +# +# hide: Just hide export from new clients, leave existing connections as is. +# Remove export after all clients are disconnected. +# +# soft: Hide export from new clients, answer with ESHUTDOWN for all further +# requests from existing clients. +# +# Since: 2.12 +## +{'enum': 'NbdServerRemoveMode', 'data': ['safe', 'hard']} + +## +# @nbd-server-remove: +# +# Remove NBD export by name. +# +# @name: Export name. +# +# @mode: Mode of command operation. See @NbdServerRemoveMode description. +# Default is 'safe'. +# +# Returns: error if +# - the server is not running +# - export is not found +# - mode is 'safe' and there are existing connections +# +# Since: 2.12 +## +{ 'command': 'nbd-server-remove', + 'data': {'name': 'str', '*mode': 'NbdServerRemoveMode'} } + +## +# @nbd-server-stop: +# +# Stop QEMU's embedded NBD server, and unregister all devices previously +# added via @nbd-server-add. +# +# Since: 1.3.0 +## +{ 'command': 'nbd-server-stop' } + +## +# @BlockExportType: +# +# An enumeration of block export types +# +# @nbd: NBD export +# +# Since: 4.2 +## +{ 'enum': 'BlockExportType', + 'data': [ 'nbd' ] } + +## +# @BlockExport: +# +# Describes a block export, i.e. how single node should be exported on an +# external interface. +# +# Since: 4.2 +## +{ 'union': 'BlockExport', + 'base': { 'type': 'BlockExportType' }, + 'discriminator': 'type', + 'data': { + 'nbd': 'BlockExportNbd' + } } + +## +# @QuorumOpType: +# +# An enumeration of the quorum operation types +# +# @read: read operation +# +# @write: write operation +# +# @flush: flush operation +# +# Since: 2.6 +## +{ 'enum': 'QuorumOpType', + 'data': [ 'read', 'write', 'flush' ] } + +## +# @QUORUM_FAILURE: +# +# Emitted by the Quorum block driver if it fails to establish a quorum +# +# @reference: device name if defined else node name +# +# @sector-num: number of the first sector of the failed read operation +# +# @sectors-count: failed read operation sector count +# +# Note: This event is rate-limited. +# +# Since: 2.0 +# +# Example: +# +# <- { "event": "QUORUM_FAILURE", +# "data": { "reference": "usr1", "sector-num": 345435, "sectors-count": 5 }, +# "timestamp": { "seconds": 1344522075, "microseconds": 745528 } } +# +## +{ 'event': 'QUORUM_FAILURE', + 'data': { 'reference': 'str', 'sector-num': 'int', 'sectors-count': 'int' } } + +## +# @QUORUM_REPORT_BAD: +# +# Emitted to report a corruption of a Quorum file +# +# @type: quorum operation type (Since 2.6) +# +# @error: error message. Only present on failure. This field +# contains a human-readable error message. There are no semantics other +# than that the block layer reported an error and clients should not +# try to interpret the error string. +# +# @node-name: the graph node name of the block driver state +# +# @sector-num: number of the first sector of the failed read operation +# +# @sectors-count: failed read operation sector count +# +# Note: This event is rate-limited. +# +# Since: 2.0 +# +# Example: +# +# 1. Read operation +# +# { "event": "QUORUM_REPORT_BAD", +# "data": { "node-name": "node0", "sector-num": 345435, "sectors-count": 5, +# "type": "read" }, +# "timestamp": { "seconds": 1344522075, "microseconds": 745528 } } +# +# 2. Flush operation +# +# { "event": "QUORUM_REPORT_BAD", +# "data": { "node-name": "node0", "sector-num": 0, "sectors-count": 2097120, +# "type": "flush", "error": "Broken pipe" }, +# "timestamp": { "seconds": 1456406829, "microseconds": 291763 } } +# +## +{ 'event': 'QUORUM_REPORT_BAD', + 'data': { 'type': 'QuorumOpType', '*error': 'str', 'node-name': 'str', + 'sector-num': 'int', 'sectors-count': 'int' } } + +## +# @BlockdevSnapshotInternal: +# +# @device: the device name or node-name of a root node to generate the snapshot +# from +# +# @name: the name of the internal snapshot to be created +# +# Notes: In transaction, if @name is empty, or any snapshot matching @name +# exists, the operation will fail. Only some image formats support it, +# for example, qcow2, rbd, and sheepdog. +# +# Since: 1.7 +## +{ 'struct': 'BlockdevSnapshotInternal', + 'data': { 'device': 'str', 'name': 'str' } } + +## +# @blockdev-snapshot-internal-sync: +# +# Synchronously take an internal snapshot of a block device, when the +# format of the image used supports it. If the name is an empty +# string, or a snapshot with name already exists, the operation will +# fail. +# +# For the arguments, see the documentation of BlockdevSnapshotInternal. +# +# Returns: - nothing on success +# - If @device is not a valid block device, GenericError +# - If any snapshot matching @name exists, or @name is empty, +# GenericError +# - If the format of the image used does not support it, +# BlockFormatFeatureNotSupported +# +# Since: 1.7 +# +# Example: +# +# -> { "execute": "blockdev-snapshot-internal-sync", +# "arguments": { "device": "ide-hd0", +# "name": "snapshot0" } +# } +# <- { "return": {} } +# +## +{ 'command': 'blockdev-snapshot-internal-sync', + 'data': 'BlockdevSnapshotInternal' } + +## +# @blockdev-snapshot-delete-internal-sync: +# +# Synchronously delete an internal snapshot of a block device, when the format +# of the image used support it. The snapshot is identified by name or id or +# both. One of the name or id is required. Return SnapshotInfo for the +# successfully deleted snapshot. +# +# @device: the device name or node-name of a root node to delete the snapshot +# from +# +# @id: optional the snapshot's ID to be deleted +# +# @name: optional the snapshot's name to be deleted +# +# Returns: - SnapshotInfo on success +# - If @device is not a valid block device, GenericError +# - If snapshot not found, GenericError +# - If the format of the image used does not support it, +# BlockFormatFeatureNotSupported +# - If @id and @name are both not specified, GenericError +# +# Since: 1.7 +# +# Example: +# +# -> { "execute": "blockdev-snapshot-delete-internal-sync", +# "arguments": { "device": "ide-hd0", +# "name": "snapshot0" } +# } +# <- { "return": { +# "id": "1", +# "name": "snapshot0", +# "vm-state-size": 0, +# "date-sec": 1000012, +# "date-nsec": 10, +# "vm-clock-sec": 100, +# "vm-clock-nsec": 20 +# } +# } +# +## +{ 'command': 'blockdev-snapshot-delete-internal-sync', + 'data': { 'device': 'str', '*id': 'str', '*name': 'str'}, + 'returns': 'SnapshotInfo' } diff --git a/qapi/block.json b/qapi/block.json index 145c268bb6..2ddbfa8306 100644 --- a/qapi/block.json +++ b/qapi/block.json @@ -60,23 +60,6 @@ { 'enum': 'FloppyDriveType', 'data': ['144', '288', '120', 'none', 'auto']} -## -# @BlockdevSnapshotInternal: -# -# @device: the device name or node-name of a root node to generate the snapshot -# from -# -# @name: the name of the internal snapshot to be created -# -# Notes: In transaction, if @name is empty, or any snapshot matching @name -# exists, the operation will fail. Only some image formats support it, -# for example, qcow2, rbd, and sheepdog. -# -# Since: 1.7 -## -{ 'struct': 'BlockdevSnapshotInternal', - 'data': { 'device': 'str', 'name': 'str' } } - ## # @PRManagerInfo: # @@ -104,103 +87,23 @@ { 'command': 'query-pr-managers', 'returns': ['PRManagerInfo'], 'allow-preconfig': true } - -## -# @blockdev-snapshot-internal-sync: -# -# Synchronously take an internal snapshot of a block device, when the -# format of the image used supports it. If the name is an empty -# string, or a snapshot with name already exists, the operation will -# fail. -# -# For the arguments, see the documentation of BlockdevSnapshotInternal. -# -# Returns: nothing on success -# -# If @device is not a valid block device, GenericError -# -# If any snapshot matching @name exists, or @name is empty, -# GenericError -# -# If the format of the image used does not support it, -# BlockFormatFeatureNotSupported -# -# Since: 1.7 -# -# Example: -# -# -> { "execute": "blockdev-snapshot-internal-sync", -# "arguments": { "device": "ide-hd0", -# "name": "snapshot0" } -# } -# <- { "return": {} } -# -## -{ 'command': 'blockdev-snapshot-internal-sync', - 'data': 'BlockdevSnapshotInternal' } - -## -# @blockdev-snapshot-delete-internal-sync: -# -# Synchronously delete an internal snapshot of a block device, when the format -# of the image used support it. The snapshot is identified by name or id or -# both. One of the name or id is required. Return SnapshotInfo for the -# successfully deleted snapshot. -# -# @device: the device name or node-name of a root node to delete the snapshot -# from -# -# @id: optional the snapshot's ID to be deleted -# -# @name: optional the snapshot's name to be deleted -# -# Returns: SnapshotInfo on success -# If @device is not a valid block device, GenericError -# If snapshot not found, GenericError -# If the format of the image used does not support it, -# BlockFormatFeatureNotSupported -# If @id and @name are both not specified, GenericError -# -# Since: 1.7 -# -# Example: -# -# -> { "execute": "blockdev-snapshot-delete-internal-sync", -# "arguments": { "device": "ide-hd0", -# "name": "snapshot0" } -# } -# <- { "return": { -# "id": "1", -# "name": "snapshot0", -# "vm-state-size": 0, -# "date-sec": 1000012, -# "date-nsec": 10, -# "vm-clock-sec": 100, -# "vm-clock-nsec": 20 -# } -# } -# -## -{ 'command': 'blockdev-snapshot-delete-internal-sync', - 'data': { 'device': 'str', '*id': 'str', '*name': 'str'}, - 'returns': 'SnapshotInfo' } - ## # @eject: # -# Ejects a device from a removable drive. +# Ejects the medium from a removable drive. # -# @device: Block device name (deprecated, use @id instead) +# @device: Block device name # -# @id: The name or QOM path of the guest device (since: 2.8) +# @id: The name or QOM path of the guest device (since: 2.8) # -# @force: If true, eject regardless of whether the drive is locked. -# If not specified, the default value is false. +# @force: If true, eject regardless of whether the drive is locked. +# If not specified, the default value is false. # -# Returns: Nothing on success -# -# If @device is not a valid block device, DeviceNotFound +# Features: +# @deprecated: Member @device is deprecated. Use @id instead. # +# Returns: - Nothing on success +# - If @device is not a valid block device, DeviceNotFound # Notes: Ejecting a device with no media results in success # # Since: 0.14.0 @@ -211,111 +114,260 @@ # <- { "return": {} } ## { 'command': 'eject', - 'data': { '*device': 'str', + 'data': { '*device': { 'type': 'str', 'features': [ 'deprecated' ] }, '*id': 'str', '*force': 'bool' } } ## -# @nbd-server-start: +# @blockdev-open-tray: # -# Start an NBD server listening on the given host and port. Block -# devices can then be exported using @nbd-server-add. The NBD -# server will present them as named exports; for example, another -# QEMU instance could refer to them as "nbd:HOST:PORT:exportname=NAME". +# Opens a block device's tray. If there is a block driver state tree inserted as +# a medium, it will become inaccessible to the guest (but it will remain +# associated to the block device, so closing the tray will make it accessible +# again). # -# @addr: Address on which to listen. -# @tls-creds: ID of the TLS credentials object (since 2.6). -# @tls-authz: ID of the QAuthZ authorization object used to validate -# the client's x509 distinguished name. This object is -# is only resolved at time of use, so can be deleted and -# recreated on the fly while the NBD server is active. -# If missing, it will default to denying access (since 4.0). +# If the tray was already open before, this will be a no-op. # -# Returns: error if the server is already running. +# Once the tray opens, a DEVICE_TRAY_MOVED event is emitted. There are cases in +# which no such event will be generated, these include: +# +# - if the guest has locked the tray, @force is false and the guest does not +# respond to the eject request +# - if the BlockBackend denoted by @device does not have a guest device attached +# to it +# - if the guest device does not have an actual tray +# +# @device: Block device name +# +# @id: The name or QOM path of the guest device (since: 2.8) +# +# @force: if false (the default), an eject request will be sent to +# the guest if it has locked the tray (and the tray will not be opened +# immediately); if true, the tray will be opened regardless of whether +# it is locked +# +# Features: +# @deprecated: Member @device is deprecated. Use @id instead. +# +# Since: 2.5 +# +# Example: +# +# -> { "execute": "blockdev-open-tray", +# "arguments": { "id": "ide0-1-0" } } +# +# <- { "timestamp": { "seconds": 1418751016, +# "microseconds": 716996 }, +# "event": "DEVICE_TRAY_MOVED", +# "data": { "device": "ide1-cd0", +# "id": "ide0-1-0", +# "tray-open": true } } +# +# <- { "return": {} } # -# Since: 1.3.0 ## -{ 'command': 'nbd-server-start', - 'data': { 'addr': 'SocketAddressLegacy', - '*tls-creds': 'str', - '*tls-authz': 'str'} } +{ 'command': 'blockdev-open-tray', + 'data': { '*device': { 'type': 'str', 'features': [ 'deprecated' ] }, + '*id': 'str', + '*force': 'bool' } } ## -# @nbd-server-add: +# @blockdev-close-tray: # -# Export a block node to QEMU's embedded NBD server. +# Closes a block device's tray. If there is a block driver state tree associated +# with the block device (which is currently ejected), that tree will be loaded +# as the medium. # -# @device: The device name or node name of the node to be exported +# If the tray was already closed before, this will be a no-op. # -# @name: Export name. If unspecified, the @device parameter is used as the -# export name. (Since 2.12) +# @device: Block device name # -# @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) +# @id: The name or QOM path of the guest device (since: 2.8) # -# Returns: error if the server is not running, or export with the same name -# already exists. +# Features: +# @deprecated: Member @device is deprecated. Use @id instead. +# +# Since: 2.5 +# +# Example: +# +# -> { "execute": "blockdev-close-tray", +# "arguments": { "id": "ide0-1-0" } } +# +# <- { "timestamp": { "seconds": 1418751345, +# "microseconds": 272147 }, +# "event": "DEVICE_TRAY_MOVED", +# "data": { "device": "ide1-cd0", +# "id": "ide0-1-0", +# "tray-open": false } } +# +# <- { "return": {} } # -# Since: 1.3.0 ## -{ 'command': 'nbd-server-add', - 'data': {'device': 'str', '*name': 'str', '*writable': 'bool', - '*bitmap': 'str' } } +{ 'command': 'blockdev-close-tray', + 'data': { '*device': { 'type': 'str', 'features': [ 'deprecated' ] }, + '*id': 'str' } } ## -# @NbdServerRemoveMode: +# @blockdev-remove-medium: # -# Mode for removing an NBD export. +# Removes a medium (a block driver state tree) from a block device. That block +# device's tray must currently be open (unless there is no attached guest +# device). # -# @safe: Remove export if there are no existing connections, fail otherwise. +# If the tray is open and there is no medium inserted, this will be a no-op. # -# @hard: Drop all connections immediately and remove export. -# -# Potential additional modes to be added in the future: -# -# hide: Just hide export from new clients, leave existing connections as is. -# Remove export after all clients are disconnected. -# -# soft: Hide export from new clients, answer with ESHUTDOWN for all further -# requests from existing clients. +# @id: The name or QOM path of the guest device # # Since: 2.12 +# +# Example: +# +# -> { "execute": "blockdev-remove-medium", +# "arguments": { "id": "ide0-1-0" } } +# +# <- { "error": { "class": "GenericError", +# "desc": "Tray of device 'ide0-1-0' is not open" } } +# +# -> { "execute": "blockdev-open-tray", +# "arguments": { "id": "ide0-1-0" } } +# +# <- { "timestamp": { "seconds": 1418751627, +# "microseconds": 549958 }, +# "event": "DEVICE_TRAY_MOVED", +# "data": { "device": "ide1-cd0", +# "id": "ide0-1-0", +# "tray-open": true } } +# +# <- { "return": {} } +# +# -> { "execute": "blockdev-remove-medium", +# "arguments": { "id": "ide0-1-0" } } +# +# <- { "return": {} } +# ## -{'enum': 'NbdServerRemoveMode', 'data': ['safe', 'hard']} +{ 'command': 'blockdev-remove-medium', + 'data': { 'id': 'str' } } ## -# @nbd-server-remove: +# @blockdev-insert-medium: # -# Remove NBD export by name. +# Inserts a medium (a block driver state tree) into a block device. That block +# device's tray must currently be open (unless there is no attached guest +# device) and there must be no medium inserted already. # -# @name: Export name. +# @id: The name or QOM path of the guest device # -# @mode: Mode of command operation. See @NbdServerRemoveMode description. -# Default is 'safe'. -# -# Returns: error if -# - the server is not running -# - export is not found -# - mode is 'safe' and there are existing connections +# @node-name: name of a node in the block driver state graph # # Since: 2.12 +# +# Example: +# +# -> { "execute": "blockdev-add", +# "arguments": { +# "node-name": "node0", +# "driver": "raw", +# "file": { "driver": "file", +# "filename": "fedora.iso" } } } +# <- { "return": {} } +# +# -> { "execute": "blockdev-insert-medium", +# "arguments": { "id": "ide0-1-0", +# "node-name": "node0" } } +# +# <- { "return": {} } +# ## -{ 'command': 'nbd-server-remove', - 'data': {'name': 'str', '*mode': 'NbdServerRemoveMode'} } +{ 'command': 'blockdev-insert-medium', + 'data': { 'id': 'str', + 'node-name': 'str'} } + ## -# @nbd-server-stop: +# @BlockdevChangeReadOnlyMode: # -# Stop QEMU's embedded NBD server, and unregister all devices previously -# added via @nbd-server-add. +# Specifies the new read-only mode of a block device subject to the +# @blockdev-change-medium command. +# +# @retain: Retains the current read-only mode +# +# @read-only: Makes the device read-only +# +# @read-write: Makes the device writable +# +# Since: 2.3 # -# Since: 1.3.0 ## -{ 'command': 'nbd-server-stop' } +{ 'enum': 'BlockdevChangeReadOnlyMode', + 'data': ['retain', 'read-only', 'read-write'] } + + +## +# @blockdev-change-medium: +# +# Changes the medium inserted into a block device by ejecting the current medium +# and loading a new image file which is inserted as the new medium (this command +# combines blockdev-open-tray, blockdev-remove-medium, blockdev-insert-medium +# and blockdev-close-tray). +# +# @device: Block device name +# +# @id: The name or QOM path of the guest device +# (since: 2.8) +# +# @filename: filename of the new image to be loaded +# +# @format: format to open the new image with (defaults to +# the probed format) +# +# @read-only-mode: change the read-only mode of the device; defaults +# to 'retain' +# +# Features: +# @deprecated: Member @device is deprecated. Use @id instead. +# +# Since: 2.5 +# +# Examples: +# +# 1. Change a removable medium +# +# -> { "execute": "blockdev-change-medium", +# "arguments": { "id": "ide0-1-0", +# "filename": "/srv/images/Fedora-12-x86_64-DVD.iso", +# "format": "raw" } } +# <- { "return": {} } +# +# 2. Load a read-only medium into a writable drive +# +# -> { "execute": "blockdev-change-medium", +# "arguments": { "id": "floppyA", +# "filename": "/srv/images/ro.img", +# "format": "raw", +# "read-only-mode": "retain" } } +# +# <- { "error": +# { "class": "GenericError", +# "desc": "Could not open '/srv/images/ro.img': Permission denied" } } +# +# -> { "execute": "blockdev-change-medium", +# "arguments": { "id": "floppyA", +# "filename": "/srv/images/ro.img", +# "format": "raw", +# "read-only-mode": "read-only" } } +# +# <- { "return": {} } +# +## +{ 'command': 'blockdev-change-medium', + 'data': { '*device': { 'type': 'str', 'features': [ 'deprecated' ] }, + '*id': 'str', + 'filename': 'str', + '*format': 'str', + '*read-only-mode': 'BlockdevChangeReadOnlyMode' } } + ## # @DEVICE_TRAY_MOVED: @@ -371,85 +423,145 @@ 'data': { 'id': 'str', 'connected': 'bool' } } ## -# @QuorumOpType: +# @block_set_io_throttle: # -# An enumeration of the quorum operation types +# Change I/O throttle limits for a block drive. # -# @read: read operation +# Since QEMU 2.4, each device with I/O limits is member of a throttle +# group. # -# @write: write operation +# If two or more devices are members of the same group, the limits +# will apply to the combined I/O of the whole group in a round-robin +# fashion. Therefore, setting new I/O limits to a device will affect +# the whole group. # -# @flush: flush operation +# The name of the group can be specified using the 'group' parameter. +# If the parameter is unset, it is assumed to be the current group of +# that device. If it's not in any group yet, the name of the device +# will be used as the name for its group. # -# Since: 2.6 -## -{ 'enum': 'QuorumOpType', - 'data': [ 'read', 'write', 'flush' ] } - -## -# @QUORUM_FAILURE: +# The 'group' parameter can also be used to move a device to a +# different group. In this case the limits specified in the parameters +# will be applied to the new group only. # -# Emitted by the Quorum block driver if it fails to establish a quorum +# I/O limits can be disabled by setting all of them to 0. In this case +# the device will be removed from its group and the rest of its +# members will not be affected. The 'group' parameter is ignored. # -# @reference: device name if defined else node name +# Returns: - Nothing on success +# - If @device is not a valid block device, DeviceNotFound # -# @sector-num: number of the first sector of the failed read operation -# -# @sectors-count: failed read operation sector count -# -# Note: This event is rate-limited. -# -# Since: 2.0 +# Since: 1.1 # # Example: # -# <- { "event": "QUORUM_FAILURE", -# "data": { "reference": "usr1", "sector-num": 345435, "sectors-count": 5 }, -# "timestamp": { "seconds": 1344522075, "microseconds": 745528 } } +# -> { "execute": "block_set_io_throttle", +# "arguments": { "id": "virtio-blk-pci0/virtio-backend", +# "bps": 0, +# "bps_rd": 0, +# "bps_wr": 0, +# "iops": 512, +# "iops_rd": 0, +# "iops_wr": 0, +# "bps_max": 0, +# "bps_rd_max": 0, +# "bps_wr_max": 0, +# "iops_max": 0, +# "iops_rd_max": 0, +# "iops_wr_max": 0, +# "bps_max_length": 0, +# "iops_size": 0 } } +# <- { "return": {} } # +# -> { "execute": "block_set_io_throttle", +# "arguments": { "id": "ide0-1-0", +# "bps": 1000000, +# "bps_rd": 0, +# "bps_wr": 0, +# "iops": 0, +# "iops_rd": 0, +# "iops_wr": 0, +# "bps_max": 8000000, +# "bps_rd_max": 0, +# "bps_wr_max": 0, +# "iops_max": 0, +# "iops_rd_max": 0, +# "iops_wr_max": 0, +# "bps_max_length": 60, +# "iops_size": 0 } } +# <- { "return": {} } ## -{ 'event': 'QUORUM_FAILURE', - 'data': { 'reference': 'str', 'sector-num': 'int', 'sectors-count': 'int' } } +{ 'command': 'block_set_io_throttle', 'boxed': true, + 'data': 'BlockIOThrottle' } ## -# @QUORUM_REPORT_BAD: +# @block-latency-histogram-set: # -# Emitted to report a corruption of a Quorum file +# Manage read, write and flush latency histograms for the device. # -# @type: quorum operation type (Since 2.6) +# If only @id parameter is specified, remove all present latency histograms +# for the device. Otherwise, add/reset some of (or all) latency histograms. # -# @error: error message. Only present on failure. This field -# contains a human-readable error message. There are no semantics other -# than that the block layer reported an error and clients should not -# try to interpret the error string. +# @id: The name or QOM path of the guest device. # -# @node-name: the graph node name of the block driver state +# @boundaries: list of interval boundary values (see description in +# BlockLatencyHistogramInfo definition). If specified, all +# latency histograms are removed, and empty ones created for all +# io types with intervals corresponding to @boundaries (except for +# io types, for which specific boundaries are set through the +# following parameters). # -# @sector-num: number of the first sector of the failed read operation +# @boundaries-read: list of interval boundary values for read latency +# histogram. If specified, old read latency histogram is +# removed, and empty one created with intervals +# corresponding to @boundaries-read. The parameter has higher +# priority then @boundaries. # -# @sectors-count: failed read operation sector count +# @boundaries-write: list of interval boundary values for write latency +# histogram. # -# Note: This event is rate-limited. +# @boundaries-flush: list of interval boundary values for flush latency +# histogram. # -# Since: 2.0 +# Returns: error if device is not found or any boundary arrays are invalid. # -# Example: +# Since: 4.0 # -# 1. Read operation +# Example: set new histograms for all io types with intervals +# [0, 10), [10, 50), [50, 100), [100, +inf): # -# { "event": "QUORUM_REPORT_BAD", -# "data": { "node-name": "node0", "sector-num": 345435, "sectors-count": 5, -# "type": "read" }, -# "timestamp": { "seconds": 1344522075, "microseconds": 745528 } } +# -> { "execute": "block-latency-histogram-set", +# "arguments": { "id": "drive0", +# "boundaries": [10, 50, 100] } } +# <- { "return": {} } # -# 2. Flush operation +# Example: set new histogram only for write, other histograms will remain +# not changed (or not created): # -# { "event": "QUORUM_REPORT_BAD", -# "data": { "node-name": "node0", "sector-num": 0, "sectors-count": 2097120, -# "type": "flush", "error": "Broken pipe" }, -# "timestamp": { "seconds": 1456406829, "microseconds": 291763 } } +# -> { "execute": "block-latency-histogram-set", +# "arguments": { "id": "drive0", +# "boundaries-write": [10, 50, 100] } } +# <- { "return": {} } # +# Example: set new histograms with the following intervals: +# read, flush: [0, 10), [10, 50), [50, 100), [100, +inf) +# write: [0, 1000), [1000, 5000), [5000, +inf) +# +# -> { "execute": "block-latency-histogram-set", +# "arguments": { "id": "drive0", +# "boundaries": [10, 50, 100], +# "boundaries-write": [1000, 5000] } } +# <- { "return": {} } +# +# Example: remove all latency histograms: +# +# -> { "execute": "block-latency-histogram-set", +# "arguments": { "id": "drive0" } } +# <- { "return": {} } ## -{ 'event': 'QUORUM_REPORT_BAD', - 'data': { 'type': 'QuorumOpType', '*error': 'str', 'node-name': 'str', - 'sector-num': 'int', 'sectors-count': 'int' } } +{ 'command': 'block-latency-histogram-set', + 'data': {'id': 'str', + '*boundaries': ['uint64'], + '*boundaries-read': ['uint64'], + '*boundaries-write': ['uint64'], + '*boundaries-flush': ['uint64'] } } diff --git a/qapi/char.json b/qapi/char.json index a6e81ac7bc..daceb20f84 100644 --- a/qapi/char.json +++ b/qapi/char.json @@ -133,6 +133,7 @@ # @data: data to write # # @format: data encoding (default 'utf8'). +# # - base64: data must be base64 encoded text. Its binary # decoding gets written. # - utf8: data's UTF-8 encoding is written @@ -167,6 +168,7 @@ # @size: how many bytes to read at most # # @format: data encoding (default 'utf8'). +# # - base64: the data read is returned in base64 encoding. # - utf8: the data read is interpreted as UTF-8. # Bug: can screw up when the buffer contains invalid UTF-8 @@ -256,17 +258,18 @@ # @server: create server socket (default: true) # @wait: wait for incoming connection on server # sockets (default: false). +# Silently ignored with server: false. This use is deprecated. # @nodelay: set TCP_NODELAY socket option (default: false) # @telnet: enable telnet protocol on server # sockets (default: false) # @tn3270: enable tn3270 protocol on server # sockets (default: false) (Since: 2.10) # @websocket: enable websocket protocol on server -# sockets (default: false) (Since: 3.1) +# sockets (default: false) (Since: 3.1) # @reconnect: For a client socket, if a socket is disconnected, -# then attempt a reconnect after the given number of seconds. -# Setting this to zero disables this function. (default: 0) -# (Since: 2.2) +# then attempt a reconnect after the given number of seconds. +# Setting this to zero disables this function. (default: 0) +# (Since: 2.2) # # Since: 1.4 ## diff --git a/qapi/control.json b/qapi/control.json new file mode 100644 index 0000000000..6b816bb61f --- /dev/null +++ b/qapi/control.json @@ -0,0 +1,258 @@ +# -*- Mode: Python -*- +# + +## +# = QMP monitor control +## + +## +# @qmp_capabilities: +# +# Enable QMP capabilities. +# +# Arguments: +# +# @enable: An optional list of QMPCapability values to enable. The +# client must not enable any capability that is not +# mentioned in the QMP greeting message. If the field is not +# provided, it means no QMP capabilities will be enabled. +# (since 2.12) +# +# Example: +# +# -> { "execute": "qmp_capabilities", +# "arguments": { "enable": [ "oob" ] } } +# <- { "return": {} } +# +# Notes: This command is valid exactly when first connecting: it must be +# issued before any other command will be accepted, and will fail once the +# monitor is accepting other commands. (see qemu docs/interop/qmp-spec.txt) +# +# The QMP client needs to explicitly enable QMP capabilities, otherwise +# all the QMP capabilities will be turned off by default. +# +# Since: 0.13 +# +## +{ 'command': 'qmp_capabilities', + 'data': { '*enable': [ 'QMPCapability' ] }, + 'allow-preconfig': true } + +## +# @QMPCapability: +# +# Enumeration of capabilities to be advertised during initial client +# connection, used for agreeing on particular QMP extension behaviors. +# +# @oob: QMP ability to support out-of-band requests. +# (Please refer to qmp-spec.txt for more information on OOB) +# +# Since: 2.12 +# +## +{ 'enum': 'QMPCapability', + 'data': [ 'oob' ] } + +## +# @VersionTriple: +# +# A three-part version number. +# +# @major: The major version number. +# +# @minor: The minor version number. +# +# @micro: The micro version number. +# +# Since: 2.4 +## +{ 'struct': 'VersionTriple', + 'data': {'major': 'int', 'minor': 'int', 'micro': 'int'} } + + +## +# @VersionInfo: +# +# A description of QEMU's version. +# +# @qemu: The version of QEMU. By current convention, a micro +# version of 50 signifies a development branch. A micro version +# greater than or equal to 90 signifies a release candidate for +# the next minor version. A micro version of less than 50 +# signifies a stable release. +# +# @package: QEMU will always set this field to an empty string. Downstream +# versions of QEMU should set this to a non-empty string. The +# exact format depends on the downstream however it highly +# recommended that a unique name is used. +# +# Since: 0.14.0 +## +{ 'struct': 'VersionInfo', + 'data': {'qemu': 'VersionTriple', 'package': 'str'} } + +## +# @query-version: +# +# Returns the current version of QEMU. +# +# Returns: A @VersionInfo object describing the current version of QEMU. +# +# Since: 0.14.0 +# +# Example: +# +# -> { "execute": "query-version" } +# <- { +# "return":{ +# "qemu":{ +# "major":0, +# "minor":11, +# "micro":5 +# }, +# "package":"" +# } +# } +# +## +{ 'command': 'query-version', 'returns': 'VersionInfo', + 'allow-preconfig': true } + +## +# @CommandInfo: +# +# Information about a QMP command +# +# @name: The command name +# +# Since: 0.14.0 +## +{ 'struct': 'CommandInfo', 'data': {'name': 'str'} } + +## +# @query-commands: +# +# Return a list of supported QMP commands by this server +# +# Returns: A list of @CommandInfo for all supported commands +# +# Since: 0.14.0 +# +# Example: +# +# -> { "execute": "query-commands" } +# <- { +# "return":[ +# { +# "name":"query-balloon" +# }, +# { +# "name":"system_powerdown" +# } +# ] +# } +# +# Note: This example has been shortened as the real response is too long. +# +## +{ 'command': 'query-commands', 'returns': ['CommandInfo'], + 'allow-preconfig': true } + +## +# @EventInfo: +# +# Information about a QMP event +# +# @name: The event name +# +# Since: 1.2.0 +## +{ 'struct': 'EventInfo', 'data': {'name': 'str'} } + +## +# @query-events: +# +# Return information on QMP events. +# +# Features: +# @deprecated: This command is deprecated, because its output doesn't +# reflect compile-time configuration. Use 'query-qmp-schema' +# instead. +# +# Returns: A list of @EventInfo. +# +# Since: 1.2.0 +# +# Example: +# +# -> { "execute": "query-events" } +# <- { +# "return": [ +# { +# "name":"SHUTDOWN" +# }, +# { +# "name":"RESET" +# } +# ] +# } +# +# Note: This example has been shortened as the real response is too long. +# +## +{ 'command': 'query-events', 'returns': ['EventInfo'], + 'features': [ 'deprecated' ] } + +## +# @quit: +# +# This command will cause the QEMU process to exit gracefully. While every +# attempt is made to send the QMP response before terminating, this is not +# guaranteed. When using this interface, a premature EOF would not be +# unexpected. +# +# Since: 0.14.0 +# +# Example: +# +# -> { "execute": "quit" } +# <- { "return": {} } +## +{ 'command': 'quit' } + +## +# @MonitorMode: +# +# An enumeration of monitor modes. +# +# @readline: HMP monitor (human-oriented command line interface) +# +# @control: QMP monitor (JSON-based machine interface) +# +# Since: 5.0 +## +{ 'enum': 'MonitorMode', 'data': [ 'readline', 'control' ] } + +## +# @MonitorOptions: +# +# Options to be used for adding a new monitor. +# +# @id: Name of the monitor +# +# @mode: Selects the monitor mode (default: readline in the system +# emulator, control in qemu-storage-daemon) +# +# @pretty: Enables pretty printing (QMP only) +# +# @chardev: Name of a character device to expose the monitor on +# +# Since: 5.0 +## +{ 'struct': 'MonitorOptions', + 'data': { + '*id': 'str', + '*mode': 'MonitorMode', + '*pretty': 'bool', + 'chardev': 'str' + } } diff --git a/qapi/dump.json b/qapi/dump.json index 2b35409a7b..a1eed7b15c 100644 --- a/qapi/dump.json +++ b/qapi/dump.json @@ -38,8 +38,8 @@ # using gdb to process the core file. # # IMPORTANT: this option can make QEMU allocate several gigabytes -# of RAM. This can happen for a large guest, or a -# malicious guest pretending to be large. +# of RAM. This can happen for a large guest, or a +# malicious guest pretending to be large. # # Also, paging=true has the following limitations: # diff --git a/qapi/introspect.json b/qapi/introspect.json index 031a954fa9..b1aabd4cfd 100644 --- a/qapi/introspect.json +++ b/qapi/introspect.json @@ -34,15 +34,15 @@ # alternate that includes the original type alongside something else. # # Returns: array of @SchemaInfo, where each element describes an -# entity in the ABI: command, event, type, ... +# entity in the ABI: command, event, type, ... # -# The order of the various SchemaInfo is unspecified; however, all -# names are guaranteed to be unique (no name will be duplicated with -# different meta-types). +# The order of the various SchemaInfo is unspecified; however, all +# names are guaranteed to be unique (no name will be duplicated with +# different meta-types). # # Note: the QAPI schema is also used to help define *internal* -# interfaces, by defining QAPI types. These are not part of the QMP -# wire ABI, and therefore not returned by this command. +# interfaces, by defining QAPI types. These are not part of the QMP +# wire ABI, and therefore not returned by this command. # # Since: 2.5 ## @@ -89,12 +89,18 @@ # # @meta-type: the entity's meta type, inherited from @base. # +# @features: names of features associated with the entity, in no +# particular order. +# (since 4.1 for object types, 4.2 for commands, 5.0 for +# the rest) +# # Additional members depend on the value of @meta-type. # # Since: 2.5 ## { 'union': 'SchemaInfo', - 'base': { 'name': 'str', 'meta-type': 'SchemaMetaType' }, + 'base': { 'name': 'str', 'meta-type': 'SchemaMetaType', + '*features': [ 'str' ] }, 'discriminator': 'meta-type', 'data': { 'builtin': 'SchemaInfoBuiltin', @@ -174,9 +180,6 @@ # and may even differ from the order of the values of the # enum type of the @tag. # -# @features: names of features associated with the type, in no particular -# order. (since: 4.1) -# # Values of this type are JSON object on the wire. # # Since: 2.5 @@ -184,8 +187,7 @@ { 'struct': 'SchemaInfoObject', 'data': { 'members': [ 'SchemaInfoObjectMember' ], '*tag': 'str', - '*variants': [ 'SchemaInfoObjectVariant' ], - '*features': [ 'str' ] } } + '*variants': [ 'SchemaInfoObjectVariant' ] } } ## # @SchemaInfoObjectMember: @@ -204,11 +206,15 @@ # Future extension: if present and non-null, the parameter # is optional, and defaults to this value. # +# @features: names of features associated with the member, in no +# particular order. (since 5.0) +# # Since: 2.5 ## { 'struct': 'SchemaInfoObjectMember', - 'data': { 'name': 'str', 'type': 'str', '*default': 'any' } } + 'data': { 'name': 'str', 'type': 'str', '*default': 'any', # @default's type must be null or match @type + '*features': [ 'str' ] } } ## # @SchemaInfoObjectVariant: @@ -266,17 +272,13 @@ # @allow-oob: whether the command allows out-of-band execution, # defaults to false (Since: 2.12) # -# @features: names of features associated with the command, in no particular -# order. (since 4.2) -# # TODO: @success-response (currently irrelevant, because it's QGA, not QMP) # # Since: 2.5 ## { 'struct': 'SchemaInfoCommand', 'data': { 'arg-type': 'str', 'ret-type': 'str', - '*allow-oob': 'bool', - '*features': [ 'str' ] } } + '*allow-oob': 'bool' } } ## # @SchemaInfoEvent: diff --git a/qapi/job.json b/qapi/job.json index a121b615fb..5e658281f5 100644 --- a/qapi/job.json +++ b/qapi/job.json @@ -214,28 +214,28 @@ # # Information about a job. # -# @id: The job identifier +# @id: The job identifier # -# @type: The kind of job that is being performed +# @type: The kind of job that is being performed # -# @status: Current job state/status +# @status: Current job state/status # -# @current-progress: Progress made until now. The unit is arbitrary and the -# value can only meaningfully be used for the ratio of -# @current-progress to @total-progress. The value is -# monotonically increasing. +# @current-progress: Progress made until now. The unit is arbitrary and the +# value can only meaningfully be used for the ratio of +# @current-progress to @total-progress. The value is +# monotonically increasing. # -# @total-progress: Estimated @current-progress value at the completion of -# the job. This value can arbitrarily change while the -# job is running, in both directions. +# @total-progress: Estimated @current-progress value at the completion of +# the job. This value can arbitrarily change while the +# job is running, in both directions. # -# @error: If this field is present, the job failed; if it is -# still missing in the CONCLUDED state, this indicates -# successful completion. +# @error: If this field is present, the job failed; if it is +# still missing in the CONCLUDED state, this indicates +# successful completion. # -# The value is a human-readable error message to describe -# the reason for the job failure. It should not be parsed -# by applications. +# The value is a human-readable error message to describe +# the reason for the job failure. It should not be parsed +# by applications. # # Since: 3.0 ## diff --git a/qapi/machine-target.json b/qapi/machine-target.json index 0462322472..f2c82949d8 100644 --- a/qapi/machine-target.json +++ b/qapi/machine-target.json @@ -40,13 +40,13 @@ # model details. # # Note: When a non-migration-safe CPU model is expanded in static mode, some -# features enabled by the CPU model may be omitted, because they can't be -# implemented by a static CPU model definition (e.g. cache info passthrough and -# PMU passthrough in x86). If you need an accurate representation of the -# features enabled by a non-migration-safe CPU model, use @full. If you need a -# static representation that will keep ABI compatibility even when changing QEMU -# version or machine-type, use @static (but keep in mind that some features may -# be omitted). +# features enabled by the CPU model may be omitted, because they can't be +# implemented by a static CPU model definition (e.g. cache info passthrough and +# PMU passthrough in x86). If you need an accurate representation of the +# features enabled by a non-migration-safe CPU model, use @full. If you need a +# static representation that will keep ABI compatibility even when changing QEMU +# version or machine-type, use @static (but keep in mind that some features may +# be omitted). # # Since: 2.8.0 ## @@ -148,7 +148,7 @@ # with wrong types. # # Note: this command isn't specific to s390x, but is only implemented -# on this architecture currently. +# on this architecture currently. # # Since: 2.8.0 ## @@ -191,7 +191,7 @@ # with wrong types. # # Note: this command isn't specific to s390x, but is only implemented -# on this architecture currently. +# on this architecture currently. # # Since: 2.8.0 ## diff --git a/qapi/machine.json b/qapi/machine.json index ca26779f1a..ff7b5032e3 100644 --- a/qapi/machine.json +++ b/qapi/machine.json @@ -16,19 +16,19 @@ # individual target constants are not documented here, for the time # being. # +# @rx: since 5.0 +# # Notes: The resulting QMP strings can be appended to the "qemu-system-" # prefix to produce the corresponding QEMU executable name. This # is true even for "qemu-system-x86_64". # -# ppcemb: dropped in 3.1 -# # Since: 3.0 ## { 'enum' : 'SysEmuTarget', 'data' : [ 'aarch64', 'alpha', 'arm', 'cris', 'hppa', 'i386', 'lm32', 'm68k', 'microblaze', 'microblazeel', 'mips', 'mips64', 'mips64el', 'mipsel', 'moxie', 'nios2', 'or1k', 'ppc', - 'ppc64', 'riscv32', 'riscv64', 's390x', 'sh4', + 'ppc64', 'riscv32', 'riscv64', 'rx', 's390x', 'sh4', 'sh4eb', 'sparc', 'sparc64', 'tricore', 'unicore32', 'x86_64', 'xtensa', 'xtensaeb' ] } @@ -186,8 +186,11 @@ # This command causes vCPU threads to exit to userspace, which causes # a small interruption to guest CPU execution. This will have a negative # impact on realtime guests and other latency sensitive guest workloads. -# It is recommended to use @query-cpus-fast instead of this command to -# avoid the vCPU interruption. +# +# Features: +# @deprecated: This command is deprecated, because it interferes with +# the guest. Use 'query-cpus-fast' instead to avoid the vCPU +# interruption. # # Returns: a list of @CpuInfo for each virtual CPU # @@ -218,12 +221,9 @@ # ] # } # -# Notes: This interface is deprecated (since 2.12.0), and it is strongly -# recommended that you avoid using it. Use @query-cpus-fast to -# obtain information about virtual CPUs. -# ## -{ 'command': 'query-cpus', 'returns': ['CpuInfo'] } +{ 'command': 'query-cpus', 'returns': ['CpuInfo'], + 'features': [ 'deprecated' ] } ## # @CpuInfoFast: @@ -239,12 +239,14 @@ # @props: properties describing to which node/socket/core/thread # virtual CPU belongs to, provided if supported by board # -# @arch: base architecture of the cpu; deprecated since 3.0.0 in favor -# of @target +# @arch: base architecture of the cpu # # @target: the QEMU system emulation target, which determines which # additional fields will be listed (since 3.0) # +# Features: +# @deprecated: Member @arch is deprecated. Use @target instead. +# # Since: 2.12 # ## @@ -253,7 +255,8 @@ 'qom-path' : 'str', 'thread-id' : 'int', '*props' : 'CpuInstanceProperties', - 'arch' : 'CpuInfoArch', + 'arch' : { 'type': 'CpuInfoArch', + 'features': [ 'deprecated' ] }, 'target' : 'SysEmuTarget' }, 'discriminator' : 'target', 'data' : { 's390x' : 'CpuInfoS390' } } @@ -309,21 +312,22 @@ # # @id: ID of CPU to be created, valid values [0..max_cpus) # +# Features: +# @deprecated: This command is deprecated. Use `device_add` instead. +# See the `query-hotpluggable-cpus` command for details. +# # Returns: Nothing on success # # Since: 1.5 # -# Note: This command is deprecated. The `device_add` command should be -# used instead. See the `query-hotpluggable-cpus` command for -# details. -# # Example: # # -> { "execute": "cpu-add", "arguments": { "id": 2 } } # <- { "return": {} } # ## -{ 'command': 'cpu-add', 'data': {'id': 'int'} } +{ 'command': 'cpu-add', 'data': {'id': 'int'}, + 'features': [ 'deprecated' ] } ## # @MachineInfo: @@ -426,10 +430,14 @@ # # @cpu: property based CPU(s) to node mapping (Since: 2.10) # +# @hmat-lb: memory latency and bandwidth information (Since: 5.0) +# +# @hmat-cache: memory side cache information (Since: 5.0) +# # Since: 2.1 ## { 'enum': 'NumaOptionsType', - 'data': [ 'node', 'dist', 'cpu' ] } + 'data': [ 'node', 'dist', 'cpu', 'hmat-lb', 'hmat-cache' ] } ## # @NumaOptions: @@ -444,7 +452,9 @@ 'data': { 'node': 'NumaNodeOptions', 'dist': 'NumaDistOptions', - 'cpu': 'NumaCpuOptions' }} + 'cpu': 'NumaCpuOptions', + 'hmat-lb': 'NumaHmatLBOptions', + 'hmat-cache': 'NumaHmatCacheOptions' }} ## # @NumaNodeOptions: @@ -463,6 +473,13 @@ # @memdev: memory backend object. If specified for one node, # it must be specified for all nodes. # +# @initiator: defined in ACPI 6.3 Chapter 5.2.27.3 Table 5-145, +# points to the nodeid which has the memory controller +# responsible for this NUMA node. This field provides +# additional information as to the initiator node that +# is closest (as in directly attached) to this node, and +# therefore has the best performance (since 5.0) +# # Since: 2.1 ## { 'struct': 'NumaNodeOptions', @@ -470,7 +487,8 @@ '*nodeid': 'uint16', '*cpus': ['uint16'], '*mem': 'size', - '*memdev': 'str' }} + '*memdev': 'str', + '*initiator': 'uint16' }} ## # @NumaDistOptions: @@ -549,6 +567,166 @@ 'base': 'CpuInstanceProperties', 'data' : {} } +## +# @HmatLBMemoryHierarchy: +# +# The memory hierarchy in the System Locality Latency and Bandwidth +# Information Structure of HMAT (Heterogeneous Memory Attribute Table) +# +# For more information about @HmatLBMemoryHierarchy, see chapter +# 5.2.27.4: Table 5-146: Field "Flags" of ACPI 6.3 spec. +# +# @memory: the structure represents the memory performance +# +# @first-level: first level of memory side cache +# +# @second-level: second level of memory side cache +# +# @third-level: third level of memory side cache +# +# Since: 5.0 +## +{ 'enum': 'HmatLBMemoryHierarchy', + 'data': [ 'memory', 'first-level', 'second-level', 'third-level' ] } + +## +# @HmatLBDataType: +# +# Data type in the System Locality Latency and Bandwidth +# Information Structure of HMAT (Heterogeneous Memory Attribute Table) +# +# For more information about @HmatLBDataType, see chapter +# 5.2.27.4: Table 5-146: Field "Data Type" of ACPI 6.3 spec. +# +# @access-latency: access latency (nanoseconds) +# +# @read-latency: read latency (nanoseconds) +# +# @write-latency: write latency (nanoseconds) +# +# @access-bandwidth: access bandwidth (Bytes per second) +# +# @read-bandwidth: read bandwidth (Bytes per second) +# +# @write-bandwidth: write bandwidth (Bytes per second) +# +# Since: 5.0 +## +{ 'enum': 'HmatLBDataType', + 'data': [ 'access-latency', 'read-latency', 'write-latency', + 'access-bandwidth', 'read-bandwidth', 'write-bandwidth' ] } + +## +# @NumaHmatLBOptions: +# +# Set the system locality latency and bandwidth information +# between Initiator and Target proximity Domains. +# +# For more information about @NumaHmatLBOptions, see chapter +# 5.2.27.4: Table 5-146 of ACPI 6.3 spec. +# +# @initiator: the Initiator Proximity Domain. +# +# @target: the Target Proximity Domain. +# +# @hierarchy: the Memory Hierarchy. Indicates the performance +# of memory or side cache. +# +# @data-type: presents the type of data, access/read/write +# latency or hit latency. +# +# @latency: the value of latency from @initiator to @target +# proximity domain, the latency unit is "ns(nanosecond)". +# +# @bandwidth: the value of bandwidth between @initiator and @target +# proximity domain, the bandwidth unit is +# "Bytes per second". +# +# Since: 5.0 +## +{ 'struct': 'NumaHmatLBOptions', + 'data': { + 'initiator': 'uint16', + 'target': 'uint16', + 'hierarchy': 'HmatLBMemoryHierarchy', + 'data-type': 'HmatLBDataType', + '*latency': 'uint64', + '*bandwidth': 'size' }} + +## +# @HmatCacheAssociativity: +# +# Cache associativity in the Memory Side Cache Information Structure +# of HMAT +# +# For more information of @HmatCacheAssociativity, see chapter +# 5.2.27.5: Table 5-147 of ACPI 6.3 spec. +# +# @none: None (no memory side cache in this proximity domain, +# or cache associativity unknown) +# +# @direct: Direct Mapped +# +# @complex: Complex Cache Indexing (implementation specific) +# +# Since: 5.0 +## +{ 'enum': 'HmatCacheAssociativity', + 'data': [ 'none', 'direct', 'complex' ] } + +## +# @HmatCacheWritePolicy: +# +# Cache write policy in the Memory Side Cache Information Structure +# of HMAT +# +# For more information of @HmatCacheWritePolicy, see chapter +# 5.2.27.5: Table 5-147: Field "Cache Attributes" of ACPI 6.3 spec. +# +# @none: None (no memory side cache in this proximity domain, +# or cache write policy unknown) +# +# @write-back: Write Back (WB) +# +# @write-through: Write Through (WT) +# +# Since: 5.0 +## +{ 'enum': 'HmatCacheWritePolicy', + 'data': [ 'none', 'write-back', 'write-through' ] } + +## +# @NumaHmatCacheOptions: +# +# Set the memory side cache information for a given memory domain. +# +# For more information of @NumaHmatCacheOptions, see chapter +# 5.2.27.5: Table 5-147: Field "Cache Attributes" of ACPI 6.3 spec. +# +# @node-id: the memory proximity domain to which the memory belongs. +# +# @size: the size of memory side cache in bytes. +# +# @level: the cache level described in this structure. +# +# @associativity: the cache associativity, +# none/direct-mapped/complex(complex cache indexing). +# +# @policy: the write policy, none/write-back/write-through. +# +# @line: the cache Line size in bytes. +# +# Since: 5.0 +## +{ 'struct': 'NumaHmatCacheOptions', + 'data': { + 'node-id': 'uint32', + 'size': 'size', + 'level': 'uint8', + 'associativity': 'HmatCacheAssociativity', + 'policy': 'HmatCacheWritePolicy', + 'line': 'uint16' }} + ## # @HostMemPolicy: # @@ -649,10 +827,10 @@ # @core-id: core number within die the CPU belongs to# @thread-id: thread number within core the CPU belongs to # # Note: currently there are 5 properties that could be present -# but management should be prepared to pass through other -# properties with device_add command to allow for future -# interface extension. This also requires the filed names to be kept in -# sync with the properties passed to -device/device_add. +# but management should be prepared to pass through other +# properties with device_add command to allow for future +# interface extension. This also requires the filed names to be kept in +# sync with the properties passed to -device/device_add. # # Since: 2.7 ## diff --git a/qapi/migration.json b/qapi/migration.json index b7348d0c8b..eca2981d0a 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -28,22 +28,22 @@ # @normal-bytes: number of normal bytes sent (since 1.2) # # @dirty-pages-rate: number of pages dirtied by second by the -# guest (since 1.3) +# guest (since 1.3) # # @mbps: throughput in megabits/sec. (since 1.6) # # @dirty-sync-count: number of times that dirty ram was synchronized (since 2.1) # # @postcopy-requests: The number of page requests received from the destination -# (since 2.7) +# (since 2.7) # # @page-size: The number of bytes per page for the various page-based -# statistics (since 2.10) +# statistics (since 2.10) # # @multifd-bytes: The number of bytes sent through multifd (since 3.0) # # @pages-per-second: the number of memory pages transferred per second -# (Since 4.0) +# (Since 4.0) # # Since: 0.14.0 ## @@ -98,7 +98,7 @@ ## { 'struct': 'CompressionStats', 'data': {'pages': 'int', 'busy': 'int', 'busy-rate': 'number', - 'compressed-size': 'int', 'compression-rate': 'number' } } + 'compressed-size': 'int', 'compression-rate': 'number' } } ## # @MigrationStatus: @@ -131,7 +131,7 @@ # @pre-switchover: Paused before device serialisation. (since 2.11) # # @device: During device serialisation when pause-before-switchover is enabled -# (since 2.11) +# (since 2.11) # # @wait-unplug: wait for device unplug request by guest OS to be completed. # (since 4.2) @@ -167,41 +167,41 @@ # status is 'active' or 'completed' (since 1.2) # # @total-time: total amount of milliseconds since migration started. -# If migration has ended, it returns the total migration -# time. (since 1.2) +# If migration has ended, it returns the total migration +# time. (since 1.2) # # @downtime: only present when migration finishes correctly -# total downtime in milliseconds for the guest. -# (since 1.3) +# total downtime in milliseconds for the guest. +# (since 1.3) # # @expected-downtime: only present while migration is active -# expected downtime in milliseconds for the guest in last walk -# of the dirty bitmap. (since 1.3) +# expected downtime in milliseconds for the guest in last walk +# of the dirty bitmap. (since 1.3) # -# @setup-time: amount of setup time in milliseconds _before_ the -# iterations begin but _after_ the QMP command is issued. This is designed -# to provide an accounting of any activities (such as RDMA pinning) which -# may be expensive, but do not actually occur during the iterative -# migration rounds themselves. (since 1.6) +# @setup-time: amount of setup time in milliseconds *before* the +# iterations begin but *after* the QMP command is issued. This is designed +# to provide an accounting of any activities (such as RDMA pinning) which +# may be expensive, but do not actually occur during the iterative +# migration rounds themselves. (since 1.6) # # @cpu-throttle-percentage: percentage of time guest cpus are being -# throttled during auto-converge. This is only present when auto-converge -# has started throttling guest cpus. (Since 2.7) +# throttled during auto-converge. This is only present when auto-converge +# has started throttling guest cpus. (Since 2.7) # # @error-desc: the human readable error description string, when # @status is 'failed'. Clients should not attempt to parse the # error strings. (Since 2.7) # # @postcopy-blocktime: total time when all vCPU were blocked during postcopy -# live migration. This is only present when the postcopy-blocktime -# migration capability is enabled. (Since 3.0) +# live migration. This is only present when the postcopy-blocktime +# migration capability is enabled. (Since 3.0) # # @postcopy-vcpu-blocktime: list of the postcopy blocktime per vCPU. This is -# only present when the postcopy-blocktime migration capability -# is enabled. (Since 3.0) +# only present when the postcopy-blocktime migration capability +# is enabled. (Since 3.0) # # @compression: migration compression statistics, only returned if compression -# feature is on and status is 'active' or 'completed' (Since 3.1) +# feature is on and status is 'active' or 'completed' (Since 3.1) # # @socket-address: Only used for tcp, to know what the real port is (Since 4.0) # @@ -355,54 +355,54 @@ # loads, by sending compressed difference of the pages # # @rdma-pin-all: Controls whether or not the entire VM memory footprint is -# mlock()'d on demand or all at once. Refer to docs/rdma.txt for usage. -# Disabled by default. (since 2.0) +# mlock()'d on demand or all at once. Refer to docs/rdma.txt for usage. +# Disabled by default. (since 2.0) # # @zero-blocks: During storage migration encode blocks of zeroes efficiently. This -# essentially saves 1MB of zeroes per block on the wire. Enabling requires -# source and target VM to support this feature. To enable it is sufficient -# to enable the capability on the source VM. The feature is disabled by -# default. (since 1.6) +# essentially saves 1MB of zeroes per block on the wire. Enabling requires +# source and target VM to support this feature. To enable it is sufficient +# to enable the capability on the source VM. The feature is disabled by +# default. (since 1.6) # # @compress: Use multiple compression threads to accelerate live migration. -# This feature can help to reduce the migration traffic, by sending -# compressed pages. Please note that if compress and xbzrle are both -# on, compress only takes effect in the ram bulk stage, after that, -# it will be disabled and only xbzrle takes effect, this can help to -# minimize migration traffic. The feature is disabled by default. -# (since 2.4 ) +# This feature can help to reduce the migration traffic, by sending +# compressed pages. Please note that if compress and xbzrle are both +# on, compress only takes effect in the ram bulk stage, after that, +# it will be disabled and only xbzrle takes effect, this can help to +# minimize migration traffic. The feature is disabled by default. +# (since 2.4 ) # # @events: generate events for each migration state change # (since 2.4 ) # # @auto-converge: If enabled, QEMU will automatically throttle down the guest -# to speed up convergence of RAM migration. (since 1.6) +# to speed up convergence of RAM migration. (since 1.6) # # @postcopy-ram: Start executing on the migration target before all of RAM has -# been migrated, pulling the remaining pages along as needed. The -# capacity must have the same setting on both source and target -# or migration will not even start. NOTE: If the migration fails during -# postcopy the VM will fail. (since 2.6) +# been migrated, pulling the remaining pages along as needed. The +# capacity must have the same setting on both source and target +# or migration will not even start. NOTE: If the migration fails during +# postcopy the VM will fail. (since 2.6) # # @x-colo: If enabled, migration will never end, and the state of the VM on the -# primary side will be migrated continuously to the VM on secondary -# side, this process is called COarse-Grain LOck Stepping (COLO) for -# Non-stop Service. (since 2.8) +# primary side will be migrated continuously to the VM on secondary +# side, this process is called COarse-Grain LOck Stepping (COLO) for +# Non-stop Service. (since 2.8) # # @release-ram: if enabled, qemu will free the migrated ram pages on the source -# during postcopy-ram migration. (since 2.9) +# during postcopy-ram migration. (since 2.9) # # @block: If enabled, QEMU will also migrate the contents of all block -# devices. Default is disabled. A possible alternative uses -# mirror jobs to a builtin NBD server on the destination, which -# offers more flexibility. -# (Since 2.10) +# devices. Default is disabled. A possible alternative uses +# mirror jobs to a builtin NBD server on the destination, which +# offers more flexibility. +# (Since 2.10) # # @return-path: If enabled, migration will use the return path even # for precopy. (since 2.10) # # @pause-before-switchover: Pause outgoing migration before serialising device -# state and before disabling block IO (since 2.11) +# state and before disabling block IO (since 2.11) # # @multifd: Use more than one fd for migration (since 4.0) # @@ -410,11 +410,11 @@ # (since 2.12) # # @postcopy-blocktime: Calculate downtime for postcopy live migration -# (since 3.0) +# (since 3.0) # # @late-block-activate: If enabled, the destination will not activate block -# devices (and thus take locks) immediately at the end of migration. -# (since 3.0) +# devices (and thus take locks) immediately at the end of migration. +# (since 3.0) # # @x-ignore-shared: If enabled, QEMU will not migrate shared memory (since 4.0) # @@ -488,30 +488,46 @@ ## { 'command': 'query-migrate-capabilities', 'returns': ['MigrationCapabilityStatus']} +## +# @MultiFDCompression: +# +# An enumeration of multifd compression methods. +# +# @none: no compression. +# @zlib: use zlib compression method. +# @zstd: use zstd compression method. +# +# Since: 5.0 +# +## +{ 'enum': 'MultiFDCompression', + 'data': [ 'none', 'zlib', + { 'name': 'zstd', 'if': 'defined(CONFIG_ZSTD)' } ] } + ## # @MigrationParameter: # # Migration parameters enumeration # # @announce-initial: Initial delay (in milliseconds) before sending the first -# announce (Since 4.0) +# announce (Since 4.0) # # @announce-max: Maximum delay (in milliseconds) between packets in the -# announcement (Since 4.0) +# announcement (Since 4.0) # # @announce-rounds: Number of self-announce packets sent after migration -# (Since 4.0) +# (Since 4.0) # # @announce-step: Increase in delay (in milliseconds) between subsequent -# packets in the announcement (Since 4.0) +# packets in the announcement (Since 4.0) # # @compress-level: Set the compression level to be used in live migration, -# the compression level is an integer between 0 and 9, where 0 means -# no compression, 1 means the best compression speed, and 9 means best -# compression ratio which will consume more CPU. +# the compression level is an integer between 0 and 9, where 0 means +# no compression, 1 means the best compression speed, and 9 means best +# compression ratio which will consume more CPU. # # @compress-threads: Set compression thread count to be used in live migration, -# the compression thread count is an integer between 1 and 255. +# the compression thread count is an integer between 1 and 255. # # @compress-wait-thread: Controls behavior when all compression threads are # currently busy. If true (default), wait for a free @@ -519,10 +535,14 @@ # send the page uncompressed. (Since 3.1) # # @decompress-threads: Set decompression thread count to be used in live -# migration, the decompression thread count is an integer between 1 -# and 255. Usually, decompression is at least 4 times as fast as -# compression, so set the decompress-threads to the number about 1/4 -# of compress-threads is adequate. +# migration, the decompression thread count is an integer between 1 +# and 255. Usually, decompression is at least 4 times as fast as +# compression, so set the decompress-threads to the number about 1/4 +# of compress-threads is adequate. +# +# @throttle-trigger-threshold: The ratio of bytes_dirty_period and bytes_xfer_period +# to trigger throttling. It is expressed as percentage. +# The default value is 50. (Since 5.0) # # @cpu-throttle-initial: Initial percentage of time guest cpus are throttled # when migration auto-converge is activated. The @@ -560,14 +580,14 @@ # downtime in milliseconds (Since 2.8) # # @x-checkpoint-delay: The delay time (in ms) between two COLO checkpoints in -# periodic mode. (Since 2.8) +# periodic mode. (Since 2.8) # # @block-incremental: Affects how much storage is migrated when the -# block migration capability is enabled. When false, the entire -# storage backing chain is migrated into a flattened image at -# the destination; when true, only the active qcow2 layer is -# migrated and the destination must already have access to the -# same backing chain as was used on the source. (since 2.10) +# block migration capability is enabled. When false, the entire +# storage backing chain is migrated into a flattened image at +# the destination; when true, only the active qcow2 layer is +# migrated and the destination must already have access to the +# same backing chain as was used on the source. (since 2.10) # # @multifd-channels: Number of channels used to migrate data in # parallel. This is the same number that the @@ -580,40 +600,58 @@ # (Since 2.11) # # @max-postcopy-bandwidth: Background transfer bandwidth during postcopy. -# Defaults to 0 (unlimited). In bytes per second. -# (Since 3.0) +# Defaults to 0 (unlimited). In bytes per second. +# (Since 3.0) # # @max-cpu-throttle: maximum cpu throttle percentage. # Defaults to 99. (Since 3.1) # +# @multifd-compression: Which compression method to use. +# Defaults to none. (Since 5.0) +# +# @multifd-zlib-level: Set the compression level to be used in live +# migration, the compression level is an integer between 0 +# and 9, where 0 means no compression, 1 means the best +# compression speed, and 9 means best compression ratio which +# will consume more CPU. +# Defaults to 1. (Since 5.0) +# +# @multifd-zstd-level: Set the compression level to be used in live +# migration, the compression level is an integer between 0 +# and 20, where 0 means no compression, 1 means the best +# compression speed, and 20 means best compression ratio which +# will consume more CPU. +# Defaults to 1. (Since 5.0) +# # Since: 2.4 ## { 'enum': 'MigrationParameter', 'data': ['announce-initial', 'announce-max', 'announce-rounds', 'announce-step', 'compress-level', 'compress-threads', 'decompress-threads', - 'compress-wait-thread', + 'compress-wait-thread', 'throttle-trigger-threshold', 'cpu-throttle-initial', 'cpu-throttle-increment', 'tls-creds', 'tls-hostname', 'tls-authz', 'max-bandwidth', 'downtime-limit', 'x-checkpoint-delay', 'block-incremental', 'multifd-channels', 'xbzrle-cache-size', 'max-postcopy-bandwidth', - 'max-cpu-throttle' ] } + 'max-cpu-throttle', 'multifd-compression', + 'multifd-zlib-level' ,'multifd-zstd-level' ] } ## # @MigrateSetParameters: # # @announce-initial: Initial delay (in milliseconds) before sending the first -# announce (Since 4.0) +# announce (Since 4.0) # # @announce-max: Maximum delay (in milliseconds) between packets in the -# announcement (Since 4.0) +# announcement (Since 4.0) # # @announce-rounds: Number of self-announce packets sent after migration -# (Since 4.0) +# (Since 4.0) # # @announce-step: Increase in delay (in milliseconds) between subsequent -# packets in the announcement (Since 4.0) +# packets in the announcement (Since 4.0) # # @compress-level: compression level # @@ -626,6 +664,10 @@ # # @decompress-threads: decompression thread count # +# @throttle-trigger-threshold: The ratio of bytes_dirty_period and bytes_xfer_period +# to trigger throttling. It is expressed as percentage. +# The default value is 50. (Since 5.0) +# # @cpu-throttle-initial: Initial percentage of time guest cpus are # throttled when migration auto-converge is activated. # The default value is 20. (Since 2.7) @@ -665,11 +707,11 @@ # @x-checkpoint-delay: the delay time between two COLO checkpoints. (Since 2.8) # # @block-incremental: Affects how much storage is migrated when the -# block migration capability is enabled. When false, the entire -# storage backing chain is migrated into a flattened image at -# the destination; when true, only the active qcow2 layer is -# migrated and the destination must already have access to the -# same backing chain as was used on the source. (since 2.10) +# block migration capability is enabled. When false, the entire +# storage backing chain is migrated into a flattened image at +# the destination; when true, only the active qcow2 layer is +# migrated and the destination must already have access to the +# same backing chain as was used on the source. (since 2.10) # # @multifd-channels: Number of channels used to migrate data in # parallel. This is the same number that the @@ -682,12 +724,29 @@ # (Since 2.11) # # @max-postcopy-bandwidth: Background transfer bandwidth during postcopy. -# Defaults to 0 (unlimited). In bytes per second. -# (Since 3.0) +# Defaults to 0 (unlimited). In bytes per second. +# (Since 3.0) # # @max-cpu-throttle: maximum cpu throttle percentage. # The default value is 99. (Since 3.1) # +# @multifd-compression: Which compression method to use. +# Defaults to none. (Since 5.0) +# +# @multifd-zlib-level: Set the compression level to be used in live +# migration, the compression level is an integer between 0 +# and 9, where 0 means no compression, 1 means the best +# compression speed, and 9 means best compression ratio which +# will consume more CPU. +# Defaults to 1. (Since 5.0) +# +# @multifd-zstd-level: Set the compression level to be used in live +# migration, the compression level is an integer between 0 +# and 20, where 0 means no compression, 1 means the best +# compression speed, and 20 means best compression ratio which +# will consume more CPU. +# Defaults to 1. (Since 5.0) +# # Since: 2.4 ## # TODO either fuse back into MigrationParameters, or make @@ -701,6 +760,7 @@ '*compress-threads': 'int', '*compress-wait-thread': 'bool', '*decompress-threads': 'int', + '*throttle-trigger-threshold': 'int', '*cpu-throttle-initial': 'int', '*cpu-throttle-increment': 'int', '*tls-creds': 'StrOrNull', @@ -713,7 +773,10 @@ '*multifd-channels': 'int', '*xbzrle-cache-size': 'size', '*max-postcopy-bandwidth': 'size', - '*max-cpu-throttle': 'int' } } + '*max-cpu-throttle': 'int', + '*multifd-compression': 'MultiFDCompression', + '*multifd-zlib-level': 'int', + '*multifd-zstd-level': 'int' } } ## # @migrate-set-parameters: @@ -737,16 +800,16 @@ # The optional members aren't actually optional. # # @announce-initial: Initial delay (in milliseconds) before sending the -# first announce (Since 4.0) +# first announce (Since 4.0) # # @announce-max: Maximum delay (in milliseconds) between packets in the -# announcement (Since 4.0) +# announcement (Since 4.0) # # @announce-rounds: Number of self-announce packets sent after migration -# (Since 4.0) +# (Since 4.0) # # @announce-step: Increase in delay (in milliseconds) between subsequent -# packets in the announcement (Since 4.0) +# packets in the announcement (Since 4.0) # # @compress-level: compression level # @@ -759,6 +822,10 @@ # # @decompress-threads: decompression thread count # +# @throttle-trigger-threshold: The ratio of bytes_dirty_period and bytes_xfer_period +# to trigger throttling. It is expressed as percentage. +# The default value is 50. (Since 5.0) +# # @cpu-throttle-initial: Initial percentage of time guest cpus are # throttled when migration auto-converge is activated. # (Since 2.7) @@ -799,11 +866,11 @@ # @x-checkpoint-delay: the delay time between two COLO checkpoints. (Since 2.8) # # @block-incremental: Affects how much storage is migrated when the -# block migration capability is enabled. When false, the entire -# storage backing chain is migrated into a flattened image at -# the destination; when true, only the active qcow2 layer is -# migrated and the destination must already have access to the -# same backing chain as was used on the source. (since 2.10) +# block migration capability is enabled. When false, the entire +# storage backing chain is migrated into a flattened image at +# the destination; when true, only the active qcow2 layer is +# migrated and the destination must already have access to the +# same backing chain as was used on the source. (since 2.10) # # @multifd-channels: Number of channels used to migrate data in # parallel. This is the same number that the @@ -816,12 +883,29 @@ # (Since 2.11) # # @max-postcopy-bandwidth: Background transfer bandwidth during postcopy. -# Defaults to 0 (unlimited). In bytes per second. -# (Since 3.0) +# Defaults to 0 (unlimited). In bytes per second. +# (Since 3.0) # # @max-cpu-throttle: maximum cpu throttle percentage. # Defaults to 99. -# (Since 3.1) +# (Since 3.1) +# +# @multifd-compression: Which compression method to use. +# Defaults to none. (Since 5.0) +# +# @multifd-zlib-level: Set the compression level to be used in live +# migration, the compression level is an integer between 0 +# and 9, where 0 means no compression, 1 means the best +# compression speed, and 9 means best compression ratio which +# will consume more CPU. +# Defaults to 1. (Since 5.0) +# +# @multifd-zstd-level: Set the compression level to be used in live +# migration, the compression level is an integer between 0 +# and 20, where 0 means no compression, 1 means the best +# compression speed, and 20 means best compression ratio which +# will consume more CPU. +# Defaults to 1. (Since 5.0) # # Since: 2.4 ## @@ -834,6 +918,7 @@ '*compress-threads': 'uint8', '*compress-wait-thread': 'bool', '*decompress-threads': 'uint8', + '*throttle-trigger-threshold': 'uint8', '*cpu-throttle-initial': 'uint8', '*cpu-throttle-increment': 'uint8', '*tls-creds': 'str', @@ -845,8 +930,11 @@ '*block-incremental': 'bool' , '*multifd-channels': 'uint8', '*xbzrle-cache-size': 'size', - '*max-postcopy-bandwidth': 'size', - '*max-cpu-throttle':'uint8'} } + '*max-postcopy-bandwidth': 'size', + '*max-cpu-throttle': 'uint8', + '*multifd-compression': 'MultiFDCompression', + '*multifd-zlib-level': 'uint8', + '*multifd-zstd-level': 'uint8' } } ## # @query-migrate-parameters: @@ -1047,8 +1135,8 @@ # The reason for a COLO exit. # # @none: failover has never happened. This state does not occur -# in the COLO_EXIT event, and is only visible in the result of -# query-colo-status. +# in the COLO_EXIT event, and is only visible in the result of +# query-colo-status. # # @request: COLO exit is due to an external request. # @@ -1122,9 +1210,11 @@ # # @value: maximum downtime in seconds # -# Returns: nothing on success +# Features: +# @deprecated: This command is deprecated. Use +# 'migrate-set-parameters' instead. # -# Notes: This command is deprecated in favor of 'migrate-set-parameters' +# Returns: nothing on success # # Since: 0.14.0 # @@ -1134,7 +1224,8 @@ # <- { "return": {} } # ## -{ 'command': 'migrate_set_downtime', 'data': {'value': 'number'} } +{ 'command': 'migrate_set_downtime', 'data': {'value': 'number'}, + 'features': [ 'deprecated' ] } ## # @migrate_set_speed: @@ -1143,9 +1234,11 @@ # # @value: maximum speed in bytes per second. # -# Returns: nothing on success +# Features: +# @deprecated: This command is deprecated. Use +# 'migrate-set-parameters' instead. # -# Notes: This command is deprecated in favor of 'migrate-set-parameters' +# Returns: nothing on success # # Since: 0.14.0 # @@ -1155,7 +1248,8 @@ # <- { "return": {} } # ## -{ 'command': 'migrate_set_speed', 'data': {'value': 'int'} } +{ 'command': 'migrate_set_speed', 'data': {'value': 'int'}, + 'features': [ 'deprecated' ] } ## # @migrate-set-cache-size: @@ -1164,13 +1258,15 @@ # # @value: cache size in bytes # +# Features: +# @deprecated: This command is deprecated. Use +# 'migrate-set-parameters' instead. +# # The size will be rounded down to the nearest power of 2. # The cache size can be modified before and during ongoing migration # # Returns: nothing on success # -# Notes: This command is deprecated in favor of 'migrate-set-parameters' -# # Since: 1.2 # # Example: @@ -1180,16 +1276,19 @@ # <- { "return": {} } # ## -{ 'command': 'migrate-set-cache-size', 'data': {'value': 'int'} } +{ 'command': 'migrate-set-cache-size', 'data': {'value': 'int'}, + 'features': [ 'deprecated' ] } ## # @query-migrate-cache-size: # # Query migration XBZRLE cache size # -# Returns: XBZRLE cache size in bytes +# Features: +# @deprecated: This command is deprecated. Use +# 'query-migrate-parameters' instead. # -# Notes: This command is deprecated in favor of 'query-migrate-parameters' +# Returns: XBZRLE cache size in bytes # # Since: 1.2 # @@ -1199,7 +1298,8 @@ # <- { "return": 67108864 } # ## -{ 'command': 'query-migrate-cache-size', 'returns': 'int' } +{ 'command': 'query-migrate-cache-size', 'returns': 'int', + 'features': [ 'deprecated' ] } ## # @migrate: @@ -1281,11 +1381,11 @@ # of the VM are not saved by this command. # # @filename: the file to save the state of the devices to as binary -# data. See xen-save-devices-state.txt for a description of the binary -# format. +# data. See xen-save-devices-state.txt for a description of the binary +# format. # # @live: Optional argument to ask QEMU to treat this command as part of a live -# migration. Default to true. (since 2.11) +# migration. Default to true. (since 2.11) # # Returns: Nothing on success # diff --git a/qapi/misc-target.json b/qapi/misc-target.json index a00fd821eb..dee3b45930 100644 --- a/qapi/misc-target.json +++ b/qapi/misc-target.json @@ -230,14 +230,14 @@ # QEMU/KVM software version, but also decided by the hardware that # the program is running upon. # -# @version: version of GIC to be described. Currently, only 2 and 3 -# are supported. +# @version: version of GIC to be described. Currently, only 2 and 3 +# are supported. # # @emulated: whether current QEMU/hardware supports emulated GIC # device in user space. # -# @kernel: whether current QEMU/hardware supports hardware -# accelerated GIC device in kernel. +# @kernel: whether current QEMU/hardware supports hardware +# accelerated GIC device in kernel. # # Since: 2.6 ## diff --git a/qapi/misc.json b/qapi/misc.json index 33b94e3589..99b90ac80b 100644 --- a/qapi/misc.json +++ b/qapi/misc.json @@ -7,173 +7,32 @@ { 'include': 'common.json' } -## -# @qmp_capabilities: -# -# Enable QMP capabilities. -# -# Arguments: -# -# @enable: An optional list of QMPCapability values to enable. The -# client must not enable any capability that is not -# mentioned in the QMP greeting message. If the field is not -# provided, it means no QMP capabilities will be enabled. -# (since 2.12) -# -# Example: -# -# -> { "execute": "qmp_capabilities", -# "arguments": { "enable": [ "oob" ] } } -# <- { "return": {} } -# -# Notes: This command is valid exactly when first connecting: it must be -# issued before any other command will be accepted, and will fail once the -# monitor is accepting other commands. (see qemu docs/interop/qmp-spec.txt) -# -# The QMP client needs to explicitly enable QMP capabilities, otherwise -# all the QMP capabilities will be turned off by default. -# -# Since: 0.13 -# -## -{ 'command': 'qmp_capabilities', - 'data': { '*enable': [ 'QMPCapability' ] }, - 'allow-preconfig': true } - -## -# @QMPCapability: -# -# Enumeration of capabilities to be advertised during initial client -# connection, used for agreeing on particular QMP extension behaviors. -# -# @oob: QMP ability to support out-of-band requests. -# (Please refer to qmp-spec.txt for more information on OOB) -# -# Since: 2.12 -# -## -{ 'enum': 'QMPCapability', - 'data': [ 'oob' ] } - -## -# @VersionTriple: -# -# A three-part version number. -# -# @major: The major version number. -# -# @minor: The minor version number. -# -# @micro: The micro version number. -# -# Since: 2.4 -## -{ 'struct': 'VersionTriple', - 'data': {'major': 'int', 'minor': 'int', 'micro': 'int'} } - - -## -# @VersionInfo: -# -# A description of QEMU's version. -# -# @qemu: The version of QEMU. By current convention, a micro -# version of 50 signifies a development branch. A micro version -# greater than or equal to 90 signifies a release candidate for -# the next minor version. A micro version of less than 50 -# signifies a stable release. -# -# @package: QEMU will always set this field to an empty string. Downstream -# versions of QEMU should set this to a non-empty string. The -# exact format depends on the downstream however it highly -# recommended that a unique name is used. -# -# Since: 0.14.0 -## -{ 'struct': 'VersionInfo', - 'data': {'qemu': 'VersionTriple', 'package': 'str'} } - -## -# @query-version: -# -# Returns the current version of QEMU. -# -# Returns: A @VersionInfo object describing the current version of QEMU. -# -# Since: 0.14.0 -# -# Example: -# -# -> { "execute": "query-version" } -# <- { -# "return":{ -# "qemu":{ -# "major":0, -# "minor":11, -# "micro":5 -# }, -# "package":"" -# } -# } -# -## -{ 'command': 'query-version', 'returns': 'VersionInfo', - 'allow-preconfig': true } - -## -# @CommandInfo: -# -# Information about a QMP command -# -# @name: The command name -# -# Since: 0.14.0 -## -{ 'struct': 'CommandInfo', 'data': {'name': 'str'} } - -## -# @query-commands: -# -# Return a list of supported QMP commands by this server -# -# Returns: A list of @CommandInfo for all supported commands -# -# Since: 0.14.0 -# -# Example: -# -# -> { "execute": "query-commands" } -# <- { -# "return":[ -# { -# "name":"query-balloon" -# }, -# { -# "name":"system_powerdown" -# } -# ] -# } -# -# Note: This example has been shortened as the real response is too long. -# -## -{ 'command': 'query-commands', 'returns': ['CommandInfo'], - 'allow-preconfig': true } - ## # @LostTickPolicy: # -# Policy for handling lost ticks in timer devices. +# Policy for handling lost ticks in timer devices. Ticks end up getting +# lost when, for example, the guest is paused. # -# @discard: throw away the missed tick(s) and continue with future injection -# normally. Guest time may be delayed, unless the OS has explicit -# handling of lost ticks +# @discard: throw away the missed ticks and continue with future injection +# normally. The guest OS will see the timer jump ahead by a +# potentially quite significant amount all at once, as if the +# intervening chunk of time had simply not existed; needless to +# say, such a sudden jump can easily confuse a guest OS which is +# not specifically prepared to deal with it. Assuming the guest +# OS can deal correctly with the time jump, the time in the guest +# and in the host should now match. # -# @delay: continue to deliver ticks at the normal rate. Guest time will be -# delayed due to the late tick +# @delay: continue to deliver ticks at the normal rate. The guest OS will +# not notice anything is amiss, as from its point of view time will +# have continued to flow normally. The time in the guest should now +# be behind the time in the host by exactly the amount of time during +# which ticks have been missed. # -# @slew: deliver ticks at a higher rate to catch up with the missed tick. The -# guest time should not be delayed once catchup is complete. +# @slew: deliver ticks at a higher rate to catch up with the missed ticks. +# The guest OS will not notice anything is amiss, as from its point +# of view time will have continued to flow normally. Once the timer +# has managed to catch up with all the missing ticks, the time in +# the guest and in the host should match. # # Since: 2.0 ## @@ -300,48 +159,6 @@ ## { 'command': 'query-uuid', 'returns': 'UuidInfo', 'allow-preconfig': true } -## -# @EventInfo: -# -# Information about a QMP event -# -# @name: The event name -# -# Since: 1.2.0 -## -{ 'struct': 'EventInfo', 'data': {'name': 'str'} } - -## -# @query-events: -# -# Return information on QMP events. -# -# Returns: A list of @EventInfo. -# -# Since: 1.2.0 -# -# Note: This command is deprecated, because its output doesn't reflect -# compile-time configuration. Use query-qmp-schema instead. -# -# Example: -# -# -> { "execute": "query-events" } -# <- { -# "return": [ -# { -# "name":"SHUTDOWN" -# }, -# { -# "name":"RESET" -# } -# ] -# } -# -# Note: This example has been shortened as the real response is too long. -# -## -{ 'command': 'query-events', 'returns': ['EventInfo'] } - ## # @IOThreadInfo: # @@ -375,8 +192,8 @@ # Returns a list of information about each iothread. # # Note: this list excludes the QEMU main loop thread, which is not declared -# using the -object iothread command-line option. It is always the main thread -# of the process. +# using the -object iothread command-line option. It is always the main thread +# of the process. # # Returns: a list of @IOThreadInfo for each iothread # @@ -418,12 +235,10 @@ # # Return information about the balloon device. # -# Returns: @BalloonInfo on success -# -# If the balloon driver is enabled but not functional because the KVM -# kernel module cannot support it, KvmMissingCap -# -# If no balloon device is present, DeviceNotActive +# Returns: - @BalloonInfo on success +# - If the balloon driver is enabled but not functional because the KVM +# kernel module cannot support it, KvmMissingCap +# - If no balloon device is present, DeviceNotActive # # Since: 0.14.0 # @@ -480,8 +295,8 @@ # # @bar: the index of the Base Address Register for this region # -# @type: 'io' if the region is a PIO region -# 'memory' if the region is a MMIO region +# @type: - 'io' if the region is a PIO region +# - 'memory' if the region is a MMIO region # # @size: memory size # @@ -624,9 +439,9 @@ # Return information about the PCI bus topology of the guest. # # Returns: a list of @PciInfo for each PCI bus. Each bus is -# represented by a json-object, which has a key with a json-array of -# all PCI devices attached to it. Each device is represented by a -# json-object. +# represented by a json-object, which has a key with a json-array of +# all PCI devices attached to it. Each device is represented by a +# json-object. # # Since: 0.14.0 # @@ -764,23 +579,6 @@ ## { 'command': 'query-pci', 'returns': ['PciInfo'] } -## -# @quit: -# -# This command will cause the QEMU process to exit gracefully. While every -# attempt is made to send the QMP response before terminating, this is not -# guaranteed. When using this interface, a premature EOF would not be -# unexpected. -# -# Since: 0.14.0 -# -# Example: -# -# -> { "execute": "quit" } -# <- { "return": {} } -## -{ 'command': 'quit' } - ## # @stop: # @@ -788,10 +586,10 @@ # # Since: 0.14.0 # -# Notes: This function will succeed even if the guest is already in the stopped -# state. In "inmigrate" state, it will ensure that the guest -# remains paused once migration finishes, as if the -S option was -# passed on the command line. +# Notes: This function will succeed even if the guest is already in the stopped +# state. In "inmigrate" state, it will ensure that the guest +# remains paused once migration finishes, as if the -S option was +# passed on the command line. # # Example: # @@ -847,7 +645,7 @@ # @filename: the file to save the memory to as binary data # # @cpu-index: the index of the virtual CPU to use for translating the -# virtual address (defaults to CPU 0) +# virtual address (defaults to CPU 0) # # Returns: Nothing on success # @@ -905,11 +703,11 @@ # # Returns: If successful, nothing # -# Notes: This command will succeed if the guest is currently running. It -# will also succeed if the guest is in the "inmigrate" state; in -# this case, the effect of the command is to make sure the guest -# starts once migration finishes, removing the effect of the -S -# command line option if it was passed. +# Notes: This command will succeed if the guest is currently running. It +# will also succeed if the guest is in the "inmigrate" state; in +# this case, the effect of the command is to make sure the guest +# starts once migration finishes, removing the effect of the -S +# command line option if it was passed. # # Example: # @@ -955,7 +753,7 @@ # Returns: nothing. # # Note: prior to 4.0, this command does nothing in case the guest -# isn't suspended. +# isn't suspended. # # Example: # @@ -992,10 +790,10 @@ # # @value: the target size of the balloon in bytes # -# Returns: Nothing on success -# If the balloon driver is enabled but not functional because the KVM +# Returns: - Nothing on success +# - If the balloon driver is enabled but not functional because the KVM # kernel module cannot support it, KvmMissingCap -# If no balloon device is present, DeviceNotActive +# - If no balloon device is present, DeviceNotActive # # Notes: This command just issues a request to the guest. When it returns, # the balloon size may not have changed. A guest can change the balloon @@ -1069,18 +867,18 @@ # change password command. Otherwise, this specifies a new server URI # address to listen to for VNC connections. # -# @arg: If @device is a block device, then this is an optional format to open -# the device with. -# If @device is 'vnc' and @target is 'password', this is the new VNC -# password to set. See change-vnc-password for additional notes. +# @arg: If @device is a block device, then this is an optional format to open +# the device with. +# If @device is 'vnc' and @target is 'password', this is the new VNC +# password to set. See change-vnc-password for additional notes. # -# Returns: Nothing on success. -# If @device is not a valid block device, DeviceNotFound +# Features: +# @deprecated: This command is deprecated. For changing block +# devices, use 'blockdev-change-medium' instead; for changing VNC +# parameters, use 'change-vnc-password' instead. # -# Notes: This interface is deprecated, and it is strongly recommended that you -# avoid using it. For changing block devices, use -# blockdev-change-medium; for changing VNC parameters, use -# change-vnc-password. +# Returns: - Nothing on success. +# - If @device is not a valid block device, DeviceNotFound # # Since: 0.14.0 # @@ -1102,7 +900,8 @@ # ## { 'command': 'change', - 'data': {'device': 'str', 'target': 'str', '*arg': 'str'} } + 'data': {'device': 'str', 'target': 'str', '*arg': 'str'}, + 'features': [ 'deprecated' ] } ## # @xen-set-global-dirty-log: @@ -1225,11 +1024,9 @@ # # @opaque: A free-form string that can be used to describe the fd. # -# Returns: @AddfdInfo on success -# -# If file descriptor was not received, FdNotSupplied -# -# If @fdset-id is a negative value, InvalidParameterValue +# Returns: - @AddfdInfo on success +# - If file descriptor was not received, FdNotSupplied +# - If @fdset-id is a negative value, InvalidParameterValue # # Notes: The list of fd sets is shared by all monitor connections. # @@ -1257,8 +1054,8 @@ # # @fd: The file descriptor that is to be removed. # -# Returns: Nothing on success -# If @fdset-id or @fd is not found, FdNotFound +# Returns: - Nothing on success +# - If @fdset-id or @fd is not found, FdNotFound # # Since: 1.2.0 # @@ -1719,8 +1516,8 @@ # of the VM are not loaded by this command. # # @filename: the file to load the state of the devices from as binary -# data. See xen-save-devices-state.txt for a description of the binary -# format. +# data. See xen-save-devices-state.txt for a description of the binary +# format. # # Since: 2.7 # diff --git a/qapi/net.json b/qapi/net.json index 335295be50..cebb1b52e3 100644 --- a/qapi/net.json +++ b/qapi/net.json @@ -39,18 +39,8 @@ # # Add a network backend. # -# @type: the type of network backend. Possible values are listed in -# NetClientDriver (excluding 'none' and 'nic') -# -# @id: the name of the new network backend -# # Additional arguments depend on the type. # -# TODO: This command effectively bypasses QAPI completely due to its -# "additional arguments" business. It shouldn't have been added to -# the schema in this form. It should be qapified properly, or -# replaced by a properly qapified command. -# # Since: 0.14.0 # # Returns: Nothing on success @@ -64,9 +54,7 @@ # <- { "return": {} } # ## -{ 'command': 'netdev_add', - 'data': {'type': 'str', 'id': 'str'}, - 'gen': false } # so we can get the additional arguments +{ 'command': 'netdev_add', 'data': 'Netdev', 'boxed': true } ## # @netdev_del: @@ -213,7 +201,7 @@ # @fd: file descriptor of an already opened tap # # @fds: multiple file descriptors of already opened multiqueue capable -# tap +# tap # # @script: script to initialize the interface # @@ -232,14 +220,14 @@ # @vhostfd: file descriptor of an already opened vhost net device # # @vhostfds: file descriptors of multiple already opened vhost net -# devices +# devices # # @vhostforce: vhost on for non-MSIX virtio guests # # @queues: number of queues to be created for multiqueue capable tap # # @poll-us: maximum number of microseconds that could -# be spent on busy polling for tap (since 2.7) +# be spent on busy polling for tap (since 2.7) # # Since: 1.2 ## @@ -446,8 +434,6 @@ # Available netdev drivers. # # Since: 2.7 -# -# 'dump': dropped in 2.12 ## { 'enum': 'NetClientDriver', 'data': [ 'none', 'nic', 'user', 'tap', 'l2tpv3', 'socket', 'vde', @@ -464,7 +450,7 @@ # # Since: 1.2 # -# 'l2tpv3' - since 2.1 +# 'l2tpv3' - since 2.1 ## { 'union': 'Netdev', 'base': { 'id': 'str', 'type': 'NetClientDriver' }, @@ -493,8 +479,6 @@ # @opts: device type specific properties (legacy) # # Since: 1.2 -# -# 'vlan': dropped in 3.0 ## { 'struct': 'NetLegacy', 'data': { @@ -691,7 +675,7 @@ # Parameters for self-announce timers # # @initial: Initial delay (in ms) before sending the first GARP/RARP -# announcement +# announcement # # @max: Maximum delay (in ms) between GARP/RARP announcement packets # @@ -700,11 +684,11 @@ # @step: Delay increase (in ms) after each self-announcement attempt # # @interfaces: An optional list of interface names, which restricts the -# announcement to the listed interfaces. (Since 4.1) +# announcement to the listed interfaces. (Since 4.1) # # @id: A name to be used to identify an instance of announce-timers -# and to allow it to modified later. Not for use as -# part of the migration parameters. (Since 4.1) +# and to allow it to modified later. Not for use as +# part of the migration parameters. (Since 4.1) # # Since: 4.0 ## diff --git a/qapi/pragma.json b/qapi/pragma.json new file mode 100644 index 0000000000..cffae27666 --- /dev/null +++ b/qapi/pragma.json @@ -0,0 +1,24 @@ +{ 'pragma': { 'doc-required': true } } + +# Whitelists to permit QAPI rule violations; think twice before you +# add to them! +{ 'pragma': { + # Commands allowed to return a non-dictionary: + 'returns-whitelist': [ + 'human-monitor-command', + 'qom-get', + 'query-migrate-cache-size', + 'query-tpm-models', + 'query-tpm-types', + 'ringbuf-read' ], + 'name-case-whitelist': [ + 'ACPISlotType', # DIMM, visible through query-acpi-ospm-status + 'CpuInfoMIPS', # PC, visible through query-cpu + 'CpuInfoTricore', # PC, visible through query-cpu + 'BlockdevVmdkSubformat', # all members, to match VMDK spec spellings + 'BlockdevVmdkAdapterType', # legacyESX, to match VMDK spec spellings + 'QapiErrorClass', # all members, visible through errors + 'UuidInfo', # UUID, visible through query-uuid + 'X86CPURegister32', # all members, visible indirectly through qom-get + 'CpuInfo' # CPU, visible through query-cpu + ] } } diff --git a/qapi/qapi-schema.json b/qapi/qapi-schema.json index 9751b11f8f..43b0ba0dea 100644 --- a/qapi/qapi-schema.json +++ b/qapi/qapi-schema.json @@ -49,30 +49,7 @@ # ## -{ 'pragma': { 'doc-required': true } } - -# Whitelists to permit QAPI rule violations; think twice before you -# add to them! -{ 'pragma': { - # Commands allowed to return a non-dictionary: - 'returns-whitelist': [ - 'human-monitor-command', - 'qom-get', - 'query-migrate-cache-size', - 'query-tpm-models', - 'query-tpm-types', - 'ringbuf-read' ], - 'name-case-whitelist': [ - 'ACPISlotType', # DIMM, visible through query-acpi-ospm-status - 'CpuInfoMIPS', # PC, visible through query-cpu - 'CpuInfoTricore', # PC, visible through query-cpu - 'BlockdevVmdkSubformat', # all members, to match VMDK spec spellings - 'BlockdevVmdkAdapterType', # legacyESX, to match VMDK spec spellings - 'QapiErrorClass', # all members, visible through errors - 'UuidInfo', # UUID, visible through query-uuid - 'X86CPURegister32', # all members, visible indirectly through qom-get - 'CpuInfo' # CPU, visible through query-cpu - ] } } +{ 'include': 'pragma.json' } # Documentation generated with qapi-gen.py is in source order, with # included sub-schemas inserted at the first include directive @@ -98,6 +75,7 @@ { 'include': 'migration.json' } { 'include': 'transaction.json' } { 'include': 'trace.json' } +{ 'include': 'control.json' } { 'include': 'introspect.json' } { 'include': 'qom.json' } { 'include': 'qdev.json' } diff --git a/qapi/qdev.json b/qapi/qdev.json index c6d05032f4..f4ed9735c4 100644 --- a/qapi/qdev.json +++ b/qapi/qdev.json @@ -19,8 +19,8 @@ # Returns: a list of ObjectPropertyInfo describing a devices properties # # Note: objects can create properties at runtime, for example to describe -# links between different devices and/or objects. These properties -# are not included in the output of this command. +# links between different devices and/or objects. These properties +# are not included in the output of this command. # # Since: 1.2 ## @@ -58,9 +58,9 @@ # <- { "return": {} } # # TODO: This command effectively bypasses QAPI completely due to its -# "additional arguments" business. It shouldn't have been added to -# the schema in this form. It should be qapified properly, or -# replaced by a properly qapified command. +# "additional arguments" business. It shouldn't have been added to +# the schema in this form. It should be qapified properly, or +# replaced by a properly qapified command. # # Since: 0.13 ## diff --git a/qapi/qmp-dispatch.c b/qapi/qmp-dispatch.c index bc264b3c9b..79347e0864 100644 --- a/qapi/qmp-dispatch.c +++ b/qapi/qmp-dispatch.c @@ -19,20 +19,13 @@ #include "sysemu/runstate.h" #include "qapi/qmp/qbool.h" -static QDict *qmp_dispatch_check_obj(const QObject *request, bool allow_oob, +static QDict *qmp_dispatch_check_obj(QDict *dict, bool allow_oob, Error **errp) { const char *exec_key = NULL; const QDictEntry *ent; const char *arg_name; const QObject *arg_obj; - QDict *dict; - - dict = qobject_to(QDict, request); - if (!dict) { - error_setg(errp, "QMP input must be a JSON object"); - return NULL; - } for (ent = qdict_first(dict); ent; ent = qdict_next(dict, ent)) { @@ -75,75 +68,6 @@ static QDict *qmp_dispatch_check_obj(const QObject *request, bool allow_oob, return dict; } -static QObject *do_qmp_dispatch(QmpCommandList *cmds, QObject *request, - bool allow_oob, Error **errp) -{ - Error *local_err = NULL; - bool oob; - const char *command; - QDict *args, *dict; - QmpCommand *cmd; - QObject *ret = NULL; - - dict = qmp_dispatch_check_obj(request, allow_oob, errp); - if (!dict) { - return NULL; - } - - command = qdict_get_try_str(dict, "execute"); - oob = false; - if (!command) { - assert(allow_oob); - command = qdict_get_str(dict, "exec-oob"); - oob = true; - } - cmd = qmp_find_command(cmds, command); - if (cmd == NULL) { - error_set(errp, ERROR_CLASS_COMMAND_NOT_FOUND, - "The command %s has not been found", command); - return NULL; - } - if (!cmd->enabled) { - error_set(errp, ERROR_CLASS_COMMAND_NOT_FOUND, - "The command %s has been disabled for this instance", - command); - return NULL; - } - if (oob && !(cmd->options & QCO_ALLOW_OOB)) { - error_setg(errp, "The command %s does not support OOB", - command); - return NULL; - } - - if (runstate_check(RUN_STATE_PRECONFIG) && - !(cmd->options & QCO_ALLOW_PRECONFIG)) { - error_setg(errp, "The command '%s' isn't permitted in '%s' state", - cmd->name, RunState_str(RUN_STATE_PRECONFIG)); - return NULL; - } - - if (!qdict_haskey(dict, "arguments")) { - args = qdict_new(); - } else { - args = qdict_get_qdict(dict, "arguments"); - qobject_ref(args); - } - - cmd->fn(args, &ret, &local_err); - if (local_err) { - error_propagate(errp, local_err); - } else if (cmd->options & QCO_NO_SUCCESS_RESP) { - g_assert(!ret); - } else if (!ret) { - /* TODO turn into assertion */ - ret = QOBJECT(qdict_new()); - } - - qobject_unref(args); - - return ret; -} - QDict *qmp_error_response(Error *err) { QDict *rsp; @@ -164,26 +88,102 @@ bool qmp_is_oob(const QDict *dict) && !qdict_haskey(dict, "execute"); } -QDict *qmp_dispatch(QmpCommandList *cmds, QObject *request, +QDict *qmp_dispatch(const QmpCommandList *cmds, QObject *request, bool allow_oob) { Error *err = NULL; - QDict *dict = qobject_to(QDict, request); - QObject *ret, *id = dict ? qdict_get(dict, "id") : NULL; - QDict *rsp; + bool oob; + const char *command; + QDict *args; + const QmpCommand *cmd; + QDict *dict; + QObject *id; + QObject *ret = NULL; + QDict *rsp = NULL; - ret = do_qmp_dispatch(cmds, request, allow_oob, &err); - if (err) { - rsp = qmp_error_response(err); - } else if (ret) { - rsp = qdict_new(); - qdict_put_obj(rsp, "return", ret); - } else { - /* Can only happen for commands with QCO_NO_SUCCESS_RESP */ - rsp = NULL; + dict = qobject_to(QDict, request); + if (!dict) { + id = NULL; + error_setg(&err, "QMP input must be a JSON object"); + goto out; } - if (rsp && id) { + id = qdict_get(dict, "id"); + + if (!qmp_dispatch_check_obj(dict, allow_oob, &err)) { + goto out; + } + + command = qdict_get_try_str(dict, "execute"); + oob = false; + if (!command) { + assert(allow_oob); + command = qdict_get_str(dict, "exec-oob"); + oob = true; + } + cmd = qmp_find_command(cmds, command); + if (cmd == NULL) { + error_set(&err, ERROR_CLASS_COMMAND_NOT_FOUND, + "The command %s has not been found", command); + goto out; + } + if (!cmd->enabled) { + error_set(&err, ERROR_CLASS_COMMAND_NOT_FOUND, + "The command %s has been disabled for this instance", + command); + goto out; + } + if (oob && !(cmd->options & QCO_ALLOW_OOB)) { + error_setg(&err, "The command %s does not support OOB", + command); + goto out; + } + + if (runstate_check(RUN_STATE_PRECONFIG) && + !(cmd->options & QCO_ALLOW_PRECONFIG)) { + error_setg(&err, "The command '%s' isn't permitted in '%s' state", + cmd->name, RunState_str(RUN_STATE_PRECONFIG)); + goto out; + } + + if (!qdict_haskey(dict, "arguments")) { + args = qdict_new(); + } else { + args = qdict_get_qdict(dict, "arguments"); + qobject_ref(args); + } + cmd->fn(args, &ret, &err); + qobject_unref(args); + if (err) { + /* or assert(!ret) after reviewing all handlers: */ + qobject_unref(ret); + goto out; + } + + if (cmd->options & QCO_NO_SUCCESS_RESP) { + g_assert(!ret); + return NULL; + } else if (!ret) { + /* + * When the command's schema has no 'returns', cmd->fn() + * leaves @ret null. The QMP spec calls for an empty object + * then; supply it. + */ + ret = QOBJECT(qdict_new()); + } + + rsp = qdict_new(); + qdict_put_obj(rsp, "return", ret); + +out: + if (err) { + assert(!rsp); + rsp = qmp_error_response(err); + } + + assert(rsp); + + if (id) { qdict_put_obj(rsp, "id", qobject_ref(id)); } diff --git a/qapi/qmp-registry.c b/qapi/qmp-registry.c index ca00f74795..d0f9a1d3e3 100644 --- a/qapi/qmp-registry.c +++ b/qapi/qmp-registry.c @@ -27,7 +27,7 @@ void qmp_register_command(QmpCommandList *cmds, const char *name, QTAILQ_INSERT_TAIL(cmds, cmd, node); } -QmpCommand *qmp_find_command(QmpCommandList *cmds, const char *name) +const QmpCommand *qmp_find_command(const QmpCommandList *cmds, const char *name) { QmpCommand *cmd; @@ -77,10 +77,10 @@ bool qmp_has_success_response(const QmpCommand *cmd) return !(cmd->options & QCO_NO_SUCCESS_RESP); } -void qmp_for_each_command(QmpCommandList *cmds, qmp_cmd_callback_fn fn, +void qmp_for_each_command(const QmpCommandList *cmds, qmp_cmd_callback_fn fn, void *opaque) { - QmpCommand *cmd; + const QmpCommand *cmd; QTAILQ_FOREACH(cmd, cmds, node) { fn(cmd, opaque); diff --git a/qapi/qom.json b/qapi/qom.json index 32db96ffc4..8abe998962 100644 --- a/qapi/qom.json +++ b/qapi/qom.json @@ -26,10 +26,15 @@ # # @description: if specified, the description of the property. # +# @default-value: the default value, if any (since 5.0) +# # Since: 1.2 ## { 'struct': 'ObjectPropertyInfo', - 'data': { 'name': 'str', 'type': 'str', '*description': 'str' } } + 'data': { 'name': 'str', + 'type': 'str', + '*description': 'str', + '*default-value': 'any' } } ## # @qom-list: @@ -184,8 +189,8 @@ # @typename: the type name of an object # # Note: objects can create properties at runtime, for example to describe -# links between different devices and/or objects. These properties -# are not included in the output of this command. +# links between different devices and/or objects. These properties +# are not included in the output of this command. # # Returns: a list of ObjectPropertyInfo describing object properties # @@ -205,7 +210,12 @@ # # @id: the name of the new object # -# @props: a dictionary of properties to be passed to the backend +# @props: a dictionary of properties to be passed to the backend. Deprecated +# since 5.0, specify the properties on the top level instead. It is an +# error to specify the same option both on the top level and in @props. +# +# Additional arguments depend on qom-type and are passed to the backend +# unchanged. # # Returns: Nothing on success # Error if @qom-type is not a valid class name @@ -216,12 +226,13 @@ # # -> { "execute": "object-add", # "arguments": { "qom-type": "rng-random", "id": "rng1", -# "props": { "filename": "/dev/hwrng" } } } +# "filename": "/dev/hwrng" } } # <- { "return": {} } # ## { 'command': 'object-add', - 'data': {'qom-type': 'str', 'id': 'str', '*props': 'any'} } + 'data': {'qom-type': 'str', 'id': 'str', '*props': 'any'}, + 'gen': false } # so we can get the additional arguments ## # @object-del: diff --git a/qapi/rocker.json b/qapi/rocker.json index 3587661161..52597db491 100644 --- a/qapi/rocker.json +++ b/qapi/rocker.json @@ -140,7 +140,7 @@ # @ip-dst: IP header destination address # # Note: optional members may or may not appear in the flow key -# depending if they're relevant to the flow key. +# depending if they're relevant to the flow key. # # Since: 2.4 ## @@ -170,7 +170,7 @@ # @ip-tos: IP header TOS field # # Note: optional members may or may not appear in the flow mask -# depending if they're relevant to the flow mask. +# depending if they're relevant to the flow mask. # # Since: 2.4 ## @@ -197,7 +197,7 @@ # @out-pport: physical output port # # Note: optional members may or may not appear in the flow action -# depending if they're relevant to the flow action. +# depending if they're relevant to the flow action. # # Since: 2.4 ## @@ -235,7 +235,7 @@ # @name: switch name # # @tbl-id: flow table ID. If tbl-id is not specified, returns -# flow information for all tables. +# flow information for all tables. # # Returns: rocker OF-DPA flow information # @@ -291,7 +291,7 @@ # @ttl-check: perform TTL check # # Note: optional members may or may not appear in the group depending -# if they're relevant to the group type. +# if they're relevant to the group type. # # Since: 2.4 ## @@ -311,7 +311,7 @@ # @name: switch name # # @type: group type. If type is not specified, returns -# group information for all group types. +# group information for all group types. # # Returns: rocker OF-DPA group information # diff --git a/qapi/run-state.json b/qapi/run-state.json index d7477cd715..2e22907740 100644 --- a/qapi/run-state.json +++ b/qapi/run-state.json @@ -15,16 +15,16 @@ # @finish-migrate: guest is paused to finish the migration process # # @inmigrate: guest is paused waiting for an incoming migration. Note -# that this state does not tell whether the machine will start at the -# end of the migration. This depends on the command-line -S option and -# any invocation of 'stop' or 'cont' that has happened since QEMU was -# started. +# that this state does not tell whether the machine will start at the +# end of the migration. This depends on the command-line -S option and +# any invocation of 'stop' or 'cont' that has happened since QEMU was +# started. # # @internal-error: An internal error that prevents further guest execution -# has occurred +# has occurred # # @io-error: the last IOP has failed and the device is configured to pause -# on I/O errors +# on I/O errors # # @paused: guest has been paused via the 'stop' command # @@ -85,8 +85,8 @@ # @guest-panic: Guest panicked, and command line turns that into a shutdown # # @subsystem-reset: Partial guest reset that does not trigger QMP events and -# ignores --no-reboot. This is useful for sanitizing -# hypercalls on s390 that are used during kexec/kdump/boot +# ignores --no-reboot. This is useful for sanitizing +# hypercalls on s390 that are used during kexec/kdump/boot # ## { 'enum': 'ShutdownCause', @@ -140,13 +140,13 @@ # about to exit. # # @guest: If true, the shutdown was triggered by a guest request (such as -# a guest-initiated ACPI shutdown request or other hardware-specific action) -# rather than a host request (such as sending qemu a SIGINT). (since 2.10) +# a guest-initiated ACPI shutdown request or other hardware-specific action) +# rather than a host request (such as sending qemu a SIGINT). (since 2.10) # # @reason: The @ShutdownCause which resulted in the SHUTDOWN. (since 4.0) # # Note: If the command-line option "-no-shutdown" has been specified, qemu will -# not exit, and a STOP event will eventually follow the SHUTDOWN event +# not exit, and a STOP event will eventually follow the SHUTDOWN event # # Since: 0.12.0 # @@ -180,9 +180,9 @@ # Emitted when the virtual machine is reset # # @guest: If true, the reset was triggered by a guest request (such as -# a guest-initiated ACPI reboot request or other hardware-specific action) -# rather than a host request (such as the QMP command system_reset). -# (since 2.10) +# a guest-initiated ACPI reboot request or other hardware-specific action) +# rather than a host request (such as the QMP command system_reset). +# (since 2.10) # # @reason: The @ShutdownCause of the RESET. (since 4.0) # @@ -283,7 +283,7 @@ # @action: action that has been taken # # Note: If action is "reset", "shutdown", or "pause" the WATCHDOG event is -# followed respectively by the RESET, SHUTDOWN, or STOP events +# followed respectively by the RESET, SHUTDOWN, or STOP events # # Note: This event is rate-limited. # @@ -356,6 +356,26 @@ { 'event': 'GUEST_PANICKED', 'data': { 'action': 'GuestPanicAction', '*info': 'GuestPanicInformation' } } +## +# @GUEST_CRASHLOADED: +# +# Emitted when guest OS crash loaded is detected +# +# @action: action that has been taken, currently always "run" +# +# @info: information about a panic +# +# Since: 5.0 +# +# Example: +# +# <- { "event": "GUEST_CRASHLOADED", +# "data": { "action": "run" } } +# +## +{ 'event': 'GUEST_CRASHLOADED', + 'data': { 'action': 'GuestPanicAction', '*info': 'GuestPanicInformation' } } + ## # @GuestPanicAction: # @@ -363,10 +383,10 @@ # # @pause: system pauses # -# Since: 2.1 (poweroff since 2.8) +# Since: 2.1 (poweroff since 2.8, run since 5.0) ## { 'enum': 'GuestPanicAction', - 'data': [ 'pause', 'poweroff' ] } + 'data': [ 'pause', 'poweroff', 'run' ] } ## # @GuestPanicInformationType: @@ -421,12 +441,12 @@ # @disabled-wait: the CPU has entered a disabled wait state # # @extint-loop: clock comparator or cpu timer interrupt with new PSW enabled -# for external interrupts +# for external interrupts # # @pgmint-loop: program interrupt with BAD new PSW # # @opint-loop: operation exception interrupt with invalid code at the program -# interrupt new PSW +# interrupt new PSW # # Since: 2.12 ## diff --git a/qapi/sockets.json b/qapi/sockets.json index 32375f3a36..ea933ed4b2 100644 --- a/qapi/sockets.json +++ b/qapi/sockets.json @@ -89,7 +89,7 @@ # @port: port # # Note: string types are used to allow for possible future hostname or -# service resolution support. +# service resolution support. # # Since: 2.8 ## @@ -104,9 +104,9 @@ # Captures the address of a socket, which could also be a named file descriptor # # Note: This type is deprecated in favor of SocketAddress. The -# difference between SocketAddressLegacy and SocketAddress is that the -# latter is a flat union rather than a simple union. Flat is nicer -# because it avoids nesting on the wire, i.e. that form has fewer {}. +# difference between SocketAddressLegacy and SocketAddress is that the +# latter is a flat union rather than a simple union. Flat is nicer +# because it avoids nesting on the wire, i.e. that form has fewer {}. # # Since: 1.3 diff --git a/qapi/tpm.json b/qapi/tpm.json index b30323bb6b..dc1f081739 100644 --- a/qapi/tpm.json +++ b/qapi/tpm.json @@ -12,11 +12,11 @@ # # @tpm-tis: TPM TIS model # @tpm-crb: TPM CRB model (since 2.12) +# @tpm-spapr: TPM SPAPR model (since 5.0) # # Since: 1.5 ## -{ 'enum': 'TpmModel', 'data': [ 'tpm-tis', 'tpm-crb' ] } - +{ 'enum': 'TpmModel', 'data': [ 'tpm-tis', 'tpm-crb', 'tpm-spapr' ] } ## # @query-tpm-models: # @@ -29,7 +29,7 @@ # Example: # # -> { "execute": "query-tpm-models" } -# <- { "return": [ "tpm-tis", "tpm-crb" ] } +# <- { "return": [ "tpm-tis", "tpm-crb", "tpm-spapr" ] } # ## { 'command': 'query-tpm-models', 'returns': ['TpmModel'] } @@ -96,8 +96,8 @@ # # A union referencing different TPM backend types' configuration options # -# @type: 'passthrough' The configuration options for the TPM passthrough type -# 'emulator' The configuration options for TPM emulator backend type +# @type: - 'passthrough' The configuration options for the TPM passthrough type +# - 'emulator' The configuration options for TPM emulator backend type # # Since: 1.5 ## diff --git a/qapi/trace.json b/qapi/trace.json index 799b254a18..47c68f04da 100644 --- a/qapi/trace.json +++ b/qapi/trace.json @@ -52,14 +52,15 @@ # # Returns: a list of @TraceEventInfo for the matching events # -# An event is returned if: -# - its name matches the @name pattern, and -# - if @vcpu is given, the event has the "vcpu" property. +# An event is returned if: # -# Therefore, if @vcpu is given, the operation will only match per-vCPU events, -# returning their state on the specified vCPU. Special case: if @name is an -# exact match, @vcpu is given and the event does not have the "vcpu" property, -# an error is returned. +# - its name matches the @name pattern, and +# - if @vcpu is given, the event has the "vcpu" property. +# +# Therefore, if @vcpu is given, the operation will only match per-vCPU events, +# returning their state on the specified vCPU. Special case: if @name is an +# exact match, @vcpu is given and the event does not have the "vcpu" property, +# an error is returned. # # Since: 2.2 # diff --git a/qapi/transaction.json b/qapi/transaction.json index 0590dbcd1a..b6c11158f0 100644 --- a/qapi/transaction.json +++ b/qapi/transaction.json @@ -5,7 +5,7 @@ # = Transactions ## -{ 'include': 'block.json' } +{ 'include': 'block-core.json' } ## # @Abort: @@ -132,8 +132,8 @@ # Errors depend on the operations of the transaction # # Note: The transaction aborts on the first failure. Therefore, there will be -# information on only one failed operation returned in an error condition, and -# subsequent actions will not have been attempted. +# information on only one failed operation returned in an error condition, and +# subsequent actions will not have been attempted. # # Since: 1.1 # diff --git a/qapi/ui.json b/qapi/ui.json index e04525d8b4..e16e98a060 100644 --- a/qapi/ui.json +++ b/qapi/ui.json @@ -12,19 +12,19 @@ # # Sets the password of a remote display session. # -# @protocol: `vnc' to modify the VNC server password -# `spice' to modify the Spice server password +# @protocol: - 'vnc' to modify the VNC server password +# - 'spice' to modify the Spice server password # # @password: the new password # # @connected: how to handle existing clients when changing the -# password. If nothing is specified, defaults to `keep' -# `fail' to fail the command if clients are connected -# `disconnect' to disconnect existing clients -# `keep' to maintain existing clients +# password. If nothing is specified, defaults to 'keep' +# 'fail' to fail the command if clients are connected +# 'disconnect' to disconnect existing clients +# 'keep' to maintain existing clients # -# Returns: Nothing on success -# If Spice is not enabled, DeviceNotFound +# Returns: - Nothing on success +# - If Spice is not enabled, DeviceNotFound # # Since: 0.14.0 # @@ -43,16 +43,17 @@ # # Expire the password of a remote display server. # -# @protocol: the name of the remote display protocol `vnc' or `spice' +# @protocol: the name of the remote display protocol 'vnc' or 'spice' # # @time: when to expire the password. -# `now' to expire the password immediately -# `never' to cancel password expiration -# `+INT' where INT is the number of seconds from now (integer) -# `INT' where INT is the absolute time in seconds # -# Returns: Nothing on success -# If @protocol is `spice' and Spice is not active, DeviceNotFound +# - 'now' to expire the password immediately +# - 'never' to cancel password expiration +# - '+INT' where INT is the number of seconds from now (integer) +# - 'INT' where INT is the absolute time in seconds +# +# Returns: - Nothing on success +# - If @protocol is 'spice' and Spice is not active, DeviceNotFound # # Since: 0.14.0 # @@ -201,9 +202,10 @@ # @tls-port: The SPICE server's TLS port number. # # @auth: the current authentication type used by the server -# 'none' if no authentication is being used -# 'spice' uses SASL or direct TLS authentication, depending on command -# line options +# +# - 'none' if no authentication is being used +# - 'spice' uses SASL or direct TLS authentication, depending on command +# line options # # @mouse-mode: The mode in which the mouse cursor is displayed currently. Can # be determined by the client or the server, or unknown if spice @@ -433,27 +435,28 @@ # @host: The hostname the VNC server is bound to. This depends on # the name resolution on the host and may be an IP address. # -# @family: 'ipv6' if the host is listening for IPv6 connections -# 'ipv4' if the host is listening for IPv4 connections -# 'unix' if the host is listening on a unix domain socket -# 'unknown' otherwise +# @family: - 'ipv6' if the host is listening for IPv6 connections +# - 'ipv4' if the host is listening for IPv4 connections +# - 'unix' if the host is listening on a unix domain socket +# - 'unknown' otherwise # # @service: The service name of the server's port. This may depends # on the host system's service database so symbolic names should not # be relied on. # # @auth: the current authentication type used by the server -# 'none' if no authentication is being used -# 'vnc' if VNC authentication is being used -# 'vencrypt+plain' if VEncrypt is used with plain text authentication -# 'vencrypt+tls+none' if VEncrypt is used with TLS and no authentication -# 'vencrypt+tls+vnc' if VEncrypt is used with TLS and VNC authentication -# 'vencrypt+tls+plain' if VEncrypt is used with TLS and plain text auth -# 'vencrypt+x509+none' if VEncrypt is used with x509 and no auth -# 'vencrypt+x509+vnc' if VEncrypt is used with x509 and VNC auth -# 'vencrypt+x509+plain' if VEncrypt is used with x509 and plain text auth -# 'vencrypt+tls+sasl' if VEncrypt is used with TLS and SASL auth -# 'vencrypt+x509+sasl' if VEncrypt is used with x509 and SASL auth +# +# - 'none' if no authentication is being used +# - 'vnc' if VNC authentication is being used +# - 'vencrypt+plain' if VEncrypt is used with plain text authentication +# - 'vencrypt+tls+none' if VEncrypt is used with TLS and no authentication +# - 'vencrypt+tls+vnc' if VEncrypt is used with TLS and VNC authentication +# - 'vencrypt+tls+plain' if VEncrypt is used with TLS and plain text auth +# - 'vencrypt+x509+none' if VEncrypt is used with x509 and no auth +# - 'vencrypt+x509+vnc' if VEncrypt is used with x509 and VNC auth +# - 'vencrypt+x509+plain' if VEncrypt is used with x509 and plain text auth +# - 'vencrypt+tls+sasl' if VEncrypt is used with TLS and SASL auth +# - 'vencrypt+x509+sasl' if VEncrypt is used with x509 and SASL auth # # @clients: a list of @VncClientInfo of all currently connected clients # @@ -591,12 +594,12 @@ # # Change the VNC server password. # -# @password: the new password to use with VNC authentication +# @password: the new password to use with VNC authentication # # Since: 1.1 # -# Notes: An empty password in this command will set the password to the empty -# string. Existing clients are unaffected by executing this command. +# Notes: An empty password in this command will set the password to the empty +# string. Existing clients are unaffected by executing this command. ## { 'command': 'change-vnc-password', 'data': { 'password': 'str' }, @@ -612,7 +615,7 @@ # @client: client information # # Note: This event is emitted before any authentication takes place, thus -# the authentication ID is not provided +# the authentication ID is not provided # # Since: 0.13.0 # @@ -776,7 +779,6 @@ # @ac_forward: since 2.10 # @ac_refresh: since 2.10 # @ac_bookmarks: since 2.10 -# altgr, altgr_r: dropped in 2.10 # # @muhenkan: since 2.12 # @katakanahiragana: since 2.12 @@ -840,8 +842,8 @@ # @hold-time: time to delay key up events, milliseconds. Defaults # to 100 # -# Returns: Nothing on success -# If key is unknown or redundant, InvalidParameter +# Returns: - Nothing on success +# - If key is unknown or redundant, InvalidParameter # # Since: 1.3.0 # @@ -915,9 +917,9 @@ # # Pointer motion input event. # -# @axis: Which axis is referenced by @value. -# @value: Pointer position. For absolute coordinates the -# valid range is 0 -> 0x7ffff +# @axis: Which axis is referenced by @value. +# @value: Pointer position. For absolute coordinates the +# valid range is 0 -> 0x7ffff # # Since: 2.0 ## @@ -931,10 +933,11 @@ # Input event union. # # @type: the input type, one of: -# - 'key': Input event of Keyboard -# - 'btn': Input event of pointer buttons -# - 'rel': Input event of relative pointer motion -# - 'abs': Input event of absolute pointer motion +# +# - 'key': Input event of Keyboard +# - 'btn': Input event of pointer buttons +# - 'rel': Input event of relative pointer motion +# - 'abs': Input event of absolute pointer motion # # Since: 2.0 ## @@ -949,13 +952,6 @@ # # Send input event(s) to guest. # -# @device: display device to send event(s) to. -# @head: head to send event(s) to, in case the -# display device supports multiple scanouts. -# @events: List of InputEvent union. -# -# Returns: Nothing on success. -# # The @device and @head parameters can be used to send the input event # to specific input devices in case (a) multiple input devices of the # same kind are added to the virtual machine and (b) you have @@ -967,12 +963,19 @@ # are admissible, but devices with input routing config take # precedence. # +# @device: display device to send event(s) to. +# @head: head to send event(s) to, in case the +# display device supports multiple scanouts. +# @events: List of InputEvent union. +# +# Returns: Nothing on success. +# # Since: 2.6 # # Note: The consoles are visible in the qom tree, under -# /backend/console[$index]. They have a device link and head property, -# so it is possible to map which console belongs to which device and -# display. +# /backend/console[$index]. They have a device link and head property, +# so it is possible to map which console belongs to which device and +# display. # # Example: # @@ -1144,6 +1147,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 +1158,7 @@ 'base' : { 'type' : 'DisplayType', '*full-screen' : 'bool', '*window-close' : 'bool', + '*show-cursor' : 'bool', '*gl' : 'DisplayGLMode' }, 'discriminator' : 'type', 'data' : { 'gtk' : 'DisplayGTK', diff --git a/qdev-monitor.c b/qdev-monitor.c index e6b112eb0a..9833b33549 100644 --- a/qdev-monitor.c +++ b/qdev-monitor.c @@ -37,6 +37,7 @@ #include "sysemu/sysemu.h" #include "migration/misc.h" #include "migration/migration.h" +#include "qemu/cutils.h" /* * Aliases were a bad idea from the start. Let's keep them @@ -66,6 +67,7 @@ static const QDevAlias qdev_alias_table[] = { { "virtio-input-host-ccw", "virtio-input-host", QEMU_ARCH_S390X }, { "virtio-input-host-pci", "virtio-input-host", QEMU_ARCH_ALL & ~QEMU_ARCH_S390X }, + { "virtio-iommu-pci", "virtio-iommu", QEMU_ARCH_ALL & ~QEMU_ARCH_S390X }, { "virtio-keyboard-ccw", "virtio-keyboard", QEMU_ARCH_S390X }, { "virtio-keyboard-pci", "virtio-keyboard", QEMU_ARCH_ALL & ~QEMU_ARCH_S390X }, @@ -256,6 +258,8 @@ int qdev_device_help(QemuOpts *opts) const char *driver; ObjectPropertyInfoList *prop_list; ObjectPropertyInfoList *prop; + GPtrArray *array; + int i; driver = qemu_opt_get(opts, "driver"); if (driver && is_help_option(driver)) { @@ -285,19 +289,20 @@ int qdev_device_help(QemuOpts *opts) } else { qemu_printf("There are no options for %s.\n", driver); } + array = g_ptr_array_new(); for (prop = prop_list; prop; prop = prop->next) { - int len; - qemu_printf(" %s=<%s>%n", prop->value->name, prop->value->type, &len); - if (prop->value->has_description) { - if (len < 24) { - qemu_printf("%*s", 24 - len, ""); - } - qemu_printf(" - %s\n", prop->value->description); - } else { - qemu_printf("\n"); - } + g_ptr_array_add(array, + object_property_help(prop->value->name, + prop->value->type, + prop->value->default_value, + prop->value->description)); } - + g_ptr_array_sort(array, (GCompareFunc)qemu_pstrcmp0); + for (i = 0; i < array->len; i++) { + printf("%s\n", (char *)array->pdata[i]); + } + g_ptr_array_set_free_func(array, g_free); + g_ptr_array_free(array, true); qapi_free_ObjectPropertyInfoList(prop_list); return 1; @@ -328,7 +333,8 @@ static Object *qdev_get_peripheral_anon(void) return dev; } -static void qbus_list_bus(DeviceState *dev, Error **errp) +static void qbus_error_append_bus_list_hint(DeviceState *dev, + Error *const *errp) { BusState *child; const char *sep = " "; @@ -342,7 +348,8 @@ static void qbus_list_bus(DeviceState *dev, Error **errp) error_append_hint(errp, "\n"); } -static void qbus_list_dev(BusState *bus, Error **errp) +static void qbus_error_append_dev_list_hint(BusState *bus, + Error *const *errp) { BusChild *kid; const char *sep = " "; @@ -500,7 +507,7 @@ static BusState *qbus_find(const char *path, Error **errp) if (!dev) { error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, "Device '%s' not found", elem); - qbus_list_dev(bus, errp); + qbus_error_append_dev_list_hint(bus, errp); return NULL; } @@ -518,7 +525,7 @@ static BusState *qbus_find(const char *path, Error **errp) if (dev->num_child_bus) { error_setg(errp, "Device '%s' has multiple child buses", elem); - qbus_list_bus(dev, errp); + qbus_error_append_bus_list_hint(dev, errp); } else { error_setg(errp, "Device '%s' has no child bus", elem); } @@ -534,7 +541,7 @@ static BusState *qbus_find(const char *path, Error **errp) bus = qbus_find_bus(dev, elem); if (!bus) { error_setg(errp, "Bus '%s' not found", elem); - qbus_list_bus(dev, errp); + qbus_error_append_bus_list_hint(dev, errp); return NULL; } } @@ -746,7 +753,7 @@ static void qdev_print(Monitor *mon, DeviceState *dev, int indent) } class = object_get_class(OBJECT(dev)); do { - qdev_print_props(mon, dev, DEVICE_CLASS(class)->props, indent); + qdev_print_props(mon, dev, DEVICE_CLASS(class)->props_, indent); class = object_class_get_parent(class); } while (class != object_class_by_name(TYPE_DEVICE)); bus_print_dev(dev->parent_bus, mon, dev, indent); @@ -881,6 +888,12 @@ void qmp_device_del(const char *id, Error **errp) { DeviceState *dev = find_device_state(id, errp); if (dev != NULL) { + if (dev->pending_deleted_event) { + error_setg(errp, "Device %s is already in the " + "process of unplug", id); + return; + } + qdev_unplug(dev, errp); } } @@ -890,7 +903,7 @@ void hmp_device_add(Monitor *mon, const QDict *qdict) Error *err = NULL; qmp_device_add((QDict *)qdict, NULL, &err); - hmp_handle_error(mon, &err); + hmp_handle_error(mon, err); } void hmp_device_del(Monitor *mon, const QDict *qdict) @@ -899,7 +912,7 @@ void hmp_device_del(Monitor *mon, const QDict *qdict) Error *err = NULL; qmp_device_del(id, &err); - hmp_handle_error(mon, &err); + hmp_handle_error(mon, err); } BlockBackend *blk_by_qdev_id(const char *id, Error **errp) diff --git a/qemu-bridge-helper.c b/qemu-bridge-helper.c index 3d50ec094c..88b26747fc 100644 --- a/qemu-bridge-helper.c +++ b/qemu-bridge-helper.c @@ -43,7 +43,7 @@ #include "net/tap-linux.h" -#ifdef CONFIG_LIBCAP +#ifdef CONFIG_LIBCAP_NG #include #endif @@ -207,7 +207,7 @@ static int send_fd(int c, int fd) return sendmsg(c, &msg, 0); } -#ifdef CONFIG_LIBCAP +#ifdef CONFIG_LIBCAP_NG static int drop_privileges(void) { /* clear all capabilities */ @@ -246,7 +246,7 @@ int main(int argc, char **argv) int access_allowed, access_denied; int ret = EXIT_SUCCESS; -#ifdef CONFIG_LIBCAP +#ifdef CONFIG_LIBCAP_NG /* if we're run from an suid binary, immediately drop privileges preserving * cap_net_admin */ if (geteuid() == 0 && getuid() != geteuid()) { diff --git a/qemu-deprecated.texi b/qemu-deprecated.texi deleted file mode 100644 index 4b4b7425ac..0000000000 --- a/qemu-deprecated.texi +++ /dev/null @@ -1,390 +0,0 @@ -@node Deprecated features -@appendix Deprecated features - -In general features are intended to be supported indefinitely once -introduced into QEMU. In the event that a feature needs to be removed, -it will be listed in this appendix. The feature will remain functional -for 2 releases prior to actual removal. Deprecated features may also -generate warnings on the console when QEMU starts up, or if activated -via a monitor command, however, this is not a mandatory requirement. - -Prior to the 2.10.0 release there was no official policy on how -long features would be deprecated prior to their removal, nor -any documented list of which features were deprecated. Thus -any features deprecated prior to 2.10.0 will be treated as if -they were first deprecated in the 2.10.0 release. - -What follows is a list of all features currently marked as -deprecated. - -@section System emulator command line arguments - -@subsection -machine enforce-config-section=on|off (since 3.1) - -The @option{enforce-config-section} parameter is replaced by the -@option{-global migration.send-configuration=@var{on|off}} option. - -@subsection -no-kvm (since 1.3.0) - -The ``-no-kvm'' argument is now a synonym for setting ``-accel tcg''. - -@subsection -usbdevice (since 2.10.0) - -The ``-usbdevice DEV'' argument is now a synonym for setting -the ``-device usb-DEV'' argument instead. The deprecated syntax -would automatically enable USB support on the machine type. -If using the new syntax, USB support must be explicitly -enabled via the ``-machine usb=on'' argument. - -@subsection -drive file=json:@{...@{'driver':'file'@}@} (since 3.0) - -The 'file' driver for drives is no longer appropriate for character or host -devices and will only accept regular files (S_IFREG). The correct driver -for these file types is 'host_cdrom' or 'host_device' as appropriate. - -@subsection -net ...,name=@var{name} (since 3.1) - -The @option{name} parameter of the @option{-net} option is a synonym -for the @option{id} parameter, which should now be used instead. - -@subsection -smp (invalid topologies) (since 3.1) - -CPU topology properties should describe whole machine topology including -possible CPUs. - -However, historically it was possible to start QEMU with an incorrect topology -where @math{@var{n} <= @var{sockets} * @var{cores} * @var{threads} < @var{maxcpus}}, -which could lead to an incorrect topology enumeration by the guest. -Support for invalid topologies will be removed, the user must ensure -topologies described with -smp include all possible cpus, i.e. - @math{@var{sockets} * @var{cores} * @var{threads} = @var{maxcpus}}. - -@subsection -vnc acl (since 4.0.0) - -The @code{acl} option to the @code{-vnc} argument has been replaced -by the @code{tls-authz} and @code{sasl-authz} options. - -@subsection QEMU_AUDIO_ environment variables and -audio-help (since 4.0) - -The ``-audiodev'' argument is now the preferred way to specify audio -backend settings instead of environment variables. To ease migration to -the new format, the ``-audiodev-help'' option can be used to convert -the current values of the environment variables to ``-audiodev'' options. - -@subsection Creating sound card devices and vnc without audiodev= property (since 4.2) - -When not using the deprecated legacy audio config, each sound card -should specify an @code{audiodev=} property. Additionally, when using -vnc, you should specify an @code{audiodev=} propery if you plan to -transmit audio through the VNC protocol. - -@subsection -mon ...,control=readline,pretty=on|off (since 4.1) - -The @code{pretty=on|off} switch has no effect for HMP monitors, but is -silently ignored. Using the switch with HMP monitors will become an -error in the future. - -@subsection -realtime (since 4.1) - -The @code{-realtime mlock=on|off} argument has been replaced by the -@code{-overcommit mem-lock=on|off} argument. - -@subsection -virtfs_synth (since 4.1) - -The ``-virtfs_synth'' argument is now deprecated. Please use ``-fsdev synth'' -and ``-device virtio-9p-...'' instead. - -@subsection -numa node,mem=@var{size} (since 4.1) - -The parameter @option{mem} of @option{-numa node} is used to assign a part of -guest RAM to a NUMA node. But when using it, it's impossible to manage specified -RAM chunk on the host side (like bind it to a host node, setting bind policy, ...), -so guest end-ups with the fake NUMA configuration with suboptiomal performance. -However since 2014 there is an alternative way to assign RAM to a NUMA node -using parameter @option{memdev}, which does the same as @option{mem} and adds -means to actualy manage node RAM on the host side. Use parameter @option{memdev} -with @var{memory-backend-ram} backend as an replacement for parameter @option{mem} -to achieve the same fake NUMA effect or a properly configured -@var{memory-backend-file} backend to actually benefit from NUMA configuration. -In future new machine versions will not accept the option but it will still -work with old machine types. User can check QAPI schema to see if the legacy -option is supported by looking at MachineInfo::numa-mem-supported property. - -@subsection -numa node (without memory specified) (since 4.1) - -Splitting RAM by default between NUMA nodes has the same issues as @option{mem} -parameter described above with the difference that the role of the user plays -QEMU using implicit generic or board specific splitting rule. -Use @option{memdev} with @var{memory-backend-ram} backend or @option{mem} (if -it's supported by used machine type) to define mapping explictly instead. - -@subsection -mem-path fallback to RAM (since 4.1) -Currently if guest RAM allocation from file pointed by @option{mem-path} -fails, QEMU falls back to allocating from RAM, which might result -in unpredictable behavior since the backing file specified by the user -is ignored. In the future, users will be responsible for making sure -the backing storage specified with @option{-mem-path} can actually provide -the guest RAM configured with @option{-m} and QEMU will fail to start up if -RAM allocation is unsuccessful. - -@subsection RISC-V -bios (since 4.1) - -QEMU 4.1 introduced support for the -bios option in QEMU for RISC-V for the -RISC-V virt machine and sifive_u machine. - -QEMU 4.1 has no changes to the default behaviour to avoid breakages. This -default will change in a future QEMU release, so please prepare now. All users -of the virt or sifive_u machine must change their command line usage. - -QEMU 4.1 has three options, please migrate to one of these three: - 1. ``-bios none`` - This is the current default behavior if no -bios option - is included. QEMU will not automatically load any firmware. It is up - to the user to load all the images they need. - 2. ``-bios default`` - In a future QEMU release this will become the default - behaviour if no -bios option is specified. This option will load the - default OpenSBI firmware automatically. The firmware is included with - the QEMU release and no user interaction is required. All a user needs - to do is specify the kernel they want to boot with the -kernel option - 3. ``-bios `` - Tells QEMU to load the specified file as the firmwrae. - -@section QEMU Machine Protocol (QMP) commands - -@subsection change (since 2.5.0) - -Use ``blockdev-change-medium'' or ``change-vnc-password'' instead. - -@subsection migrate_set_downtime and migrate_set_speed (since 2.8.0) - -Use ``migrate-set-parameters'' instead. - -@subsection migrate-set-cache-size and query-migrate-cache-size (since 2.11.0) - -Use ``migrate-set-parameters'' and ``query-migrate-parameters'' instead. - -@subsection query-block result field dirty-bitmaps[i].status (since 4.0) - -The ``status'' field of the ``BlockDirtyInfo'' structure, returned by -the query-block command is deprecated. Two new boolean fields, -``recording'' and ``busy'' effectively replace it. - -@subsection query-block result field dirty-bitmaps (Since 4.2) - -The ``dirty-bitmaps`` field of the ``BlockInfo`` structure, returned by -the query-block command is itself now deprecated. The ``dirty-bitmaps`` -field of the ``BlockDeviceInfo`` struct should be used instead, which is the -type of the ``inserted`` field in query-block replies, as well as the -type of array items in query-named-block-nodes. - -Since the ``dirty-bitmaps`` field is optionally present in both the old and -new locations, clients must use introspection to learn where to anticipate -the field if/when it does appear in command output. - -@subsection query-cpus (since 2.12.0) - -The ``query-cpus'' command is replaced by the ``query-cpus-fast'' command. - -@subsection query-cpus-fast "arch" output member (since 3.0.0) - -The ``arch'' output member of the ``query-cpus-fast'' command is -replaced by the ``target'' output member. - -@subsection cpu-add (since 4.0) - -Use ``device_add'' for hotplugging vCPUs instead of ``cpu-add''. See -documentation of ``query-hotpluggable-cpus'' for additional -details. - -@subsection query-events (since 4.0) - -The ``query-events'' command has been superseded by the more powerful -and accurate ``query-qmp-schema'' command. - -@subsection chardev client socket with 'wait' option (since 4.0) - -Character devices creating sockets in client mode should not specify -the 'wait' field, which is only applicable to sockets in server mode - -@section Human Monitor Protocol (HMP) commands - -@subsection The hub_id parameter of 'hostfwd_add' / 'hostfwd_remove' (since 3.1) - -The @option{[hub_id name]} parameter tuple of the 'hostfwd_add' and -'hostfwd_remove' HMP commands has been replaced by @option{netdev_id}. - -@subsection cpu-add (since 4.0) - -Use ``device_add'' for hotplugging vCPUs instead of ``cpu-add''. See -documentation of ``query-hotpluggable-cpus'' for additional details. - -@subsection acl_show, acl_reset, acl_policy, acl_add, acl_remove (since 4.0.0) - -The ``acl_show'', ``acl_reset'', ``acl_policy'', ``acl_add'', and -``acl_remove'' commands are deprecated with no replacement. Authorization -for VNC should be performed using the pluggable QAuthZ objects. - -@section Guest Emulator ISAs - -@subsection RISC-V ISA privledge specification version 1.09.1 (since 4.1) - -The RISC-V ISA privledge specification version 1.09.1 has been deprecated. -QEMU supports both the newer version 1.10.0 and the ratified version 1.11.0, these -should be used instead of the 1.09.1 version. - -@section System emulator CPUS - -@subsection RISC-V ISA CPUs (since 4.1) - -The RISC-V cpus with the ISA version in the CPU name have been depcreated. The -four CPUs are: ``rv32gcsu-v1.9.1``, ``rv32gcsu-v1.10.0``, ``rv64gcsu-v1.9.1`` and -``rv64gcsu-v1.10.0``. Instead the version can be specified via the CPU ``priv_spec`` -option when using the ``rv32`` or ``rv64`` CPUs. - -@subsection RISC-V ISA CPUs (since 4.1) - -The RISC-V no MMU cpus have been depcreated. The two CPUs: ``rv32imacu-nommu`` and -``rv64imacu-nommu`` should no longer be used. Instead the MMU status can be specified -via the CPU ``mmu`` option when using the ``rv32`` or ``rv64`` CPUs. - -@section System emulator devices - -@subsection bluetooth (since 3.1) - -The bluetooth subsystem is unmaintained since many years and likely bitrotten -quite a bit. It will be removed without replacement unless some users speaks -up at the @email{qemu-devel@@nongnu.org} mailing list with information about -their usecases. - -@subsection ide-drive (since 4.2) - -The 'ide-drive' device is deprecated. Users should use 'ide-hd' or -'ide-cd' as appropriate to get an IDE hard disk or CD-ROM as needed. - -@subsection scsi-disk (since 4.2) - -The 'scsi-disk' device is deprecated. Users should use 'scsi-hd' or -'scsi-cd' as appropriate to get a SCSI hard disk or CD-ROM as needed. - -@section System emulator machines - -@subsection pc-0.12, pc-0.13, pc-0.14 and pc-0.15 (since 4.0) - -These machine types are very old and likely can not be used for live migration -from old QEMU versions anymore. A newer machine type should be used instead. - -@subsection prep (PowerPC) (since 3.1) - -This machine type uses an unmaintained firmware, broken in lots of ways, -and unable to start post-2004 operating systems. 40p machine type should be -used instead. - -@subsection spike_v1.9.1 and spike_v1.10 (since 4.1) - -The version specific Spike machines have been deprecated in favour of the -generic ``spike`` machine. If you need to specify an older version of the RISC-V -spec you can use the ``-cpu rv64gcsu,priv_spec=v1.9.1`` command line argument. - -@section Device options - -@subsection Block device options - -@subsubsection "backing": "" (since 2.12.0) - -In order to prevent QEMU from automatically opening an image's backing -chain, use ``"backing": null'' instead. - -@subsubsection rbd keyvalue pair encoded filenames: "" (since 3.1.0) - -Options for ``rbd'' should be specified according to its runtime options, -like other block drivers. Legacy parsing of keyvalue pair encoded -filenames is useful to open images with the old format for backing files; -These image files should be updated to use the current format. - -Example of legacy encoding: - -@code{json:@{"file.driver":"rbd", "file.filename":"rbd:rbd/name"@}} - -The above, converted to the current supported format: - -@code{json:@{"file.driver":"rbd", "file.pool":"rbd", "file.image":"name"@}} - -@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 -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) - -Previous versions of QEMU never changed existing CPU models in -ways that introduced additional host software or hardware -requirements to the VM. This allowed management software to -safely change the machine type of an existing VM without -introducing new requirements ("runnability guarantee"). This -prevented CPU models from being updated to include CPU -vulnerability mitigations, leaving guests vulnerable in the -default configuration. - -The CPU model runnability guarantee won't apply anymore to -existing CPU models. Management software that needs runnability -guarantees must resolve the CPU model aliases using te -``alias-of'' field returned by the ``query-cpu-definitions'' QMP -command. - - -@node Recently removed features -@appendix Recently removed features - -What follows is a record of recently removed, formerly deprecated -features that serves as a record for users who have encountered -trouble after a recent upgrade. - -@section QEMU Machine Protocol (QMP) commands - -@subsection block-dirty-bitmap-add "autoload" parameter (since 4.2.0) - -The "autoload" parameter has been ignored since 2.12.0. All bitmaps -are automatically loaded from qcow2 images. diff --git a/qemu-doc.texi b/qemu-doc.texi deleted file mode 100644 index 3ddf5c0a68..0000000000 --- a/qemu-doc.texi +++ /dev/null @@ -1,3011 +0,0 @@ -\input texinfo @c -*- texinfo -*- -@c %**start of header -@setfilename qemu-doc.info -@include version.texi - -@documentlanguage en -@documentencoding UTF-8 - -@settitle QEMU version @value{VERSION} User Documentation -@exampleindent 0 -@paragraphindent 0 -@c %**end of header - -@set qemu_system qemu-system-x86_64 -@set qemu_system_x86 qemu-system-x86_64 - -@ifinfo -@direntry -* QEMU: (qemu-doc). The QEMU Emulator User Documentation. -@end direntry -@end ifinfo - -@iftex -@titlepage -@sp 7 -@center @titlefont{QEMU version @value{VERSION}} -@sp 1 -@center @titlefont{User Documentation} -@sp 3 -@end titlepage -@end iftex - -@ifnottex -@node Top -@top - -@menu -* Introduction:: -* QEMU PC System emulator:: -* QEMU System emulator for non PC targets:: -* QEMU Guest Agent:: -* QEMU User space emulator:: -* System requirements:: -* Security:: -* Implementation notes:: -* Deprecated features:: -* Recently removed features:: -* Supported build platforms:: -* License:: -* Index:: -@end menu -@end ifnottex - -@contents - -@node Introduction -@chapter Introduction - -@menu -* intro_features:: Features -@end menu - -@node intro_features -@section Features - -QEMU is a FAST! processor emulator using dynamic translation to -achieve good emulation speed. - -@cindex operating modes -QEMU has two operating modes: - -@itemize -@cindex system emulation -@item Full system emulation. In this mode, QEMU emulates a full system (for -example a PC), including one or several processors and various -peripherals. It can be used to launch different Operating Systems -without rebooting the PC or to debug system code. - -@cindex user mode emulation -@item User mode emulation. In this mode, QEMU can launch -processes compiled for one CPU on another CPU. It can be used to -launch the Wine Windows API emulator (@url{https://www.winehq.org}) or -to ease cross-compilation and cross-debugging. - -@end itemize - -QEMU has the following features: - -@itemize -@item QEMU can run without a host kernel driver and yet gives acceptable -performance. It uses dynamic translation to native code for reasonable speed, -with support for self-modifying code and precise exceptions. - -@item It is portable to several operating systems (GNU/Linux, *BSD, Mac OS X, -Windows) and architectures. - -@item It performs accurate software emulation of the FPU. -@end itemize - -QEMU user mode emulation has the following features: -@itemize -@item Generic Linux system call converter, including most ioctls. - -@item clone() emulation using native CPU clone() to use Linux scheduler for threads. - -@item Accurate signal handling by remapping host signals to target signals. -@end itemize - -QEMU full system emulation has the following features: -@itemize -@item -QEMU uses a full software MMU for maximum portability. - -@item -QEMU can optionally use an in-kernel accelerator, like kvm. The accelerators -execute most of the guest code natively, while -continuing to emulate the rest of the machine. - -@item -Various hardware devices can be emulated and in some cases, host -devices (e.g. serial and parallel ports, USB, drives) can be used -transparently by the guest Operating System. Host device passthrough -can be used for talking to external physical peripherals (e.g. a -webcam, modem or tape drive). - -@item -Symmetric multiprocessing (SMP) support. Currently, an in-kernel -accelerator is required to use more than one host CPU for emulation. - -@end itemize - - -@node QEMU PC System emulator -@chapter QEMU PC System emulator -@cindex system emulation (PC) - -@menu -* pcsys_introduction:: Introduction -* pcsys_quickstart:: Quick Start -* sec_invocation:: Invocation -* pcsys_keys:: Keys in the graphical frontends -* mux_keys:: Keys in the character backend multiplexer -* pcsys_monitor:: QEMU Monitor -* cpu_models:: CPU models -* disk_images:: Disk Images -* pcsys_network:: Network emulation -* pcsys_other_devs:: Other Devices -* direct_linux_boot:: Direct Linux Boot -* pcsys_usb:: USB emulation -* vnc_security:: VNC security -* network_tls:: TLS setup for network services -* gdb_usage:: GDB usage -* pcsys_os_specific:: Target OS specific information -@end menu - -@node pcsys_introduction -@section Introduction - -@c man begin DESCRIPTION - -The QEMU PC System emulator simulates the -following peripherals: - -@itemize @minus -@item -i440FX host PCI bridge and PIIX3 PCI to ISA bridge -@item -Cirrus CLGD 5446 PCI VGA card or dummy VGA card with Bochs VESA -extensions (hardware level, including all non standard modes). -@item -PS/2 mouse and keyboard -@item -2 PCI IDE interfaces with hard disk and CD-ROM support -@item -Floppy disk -@item -PCI and ISA network adapters -@item -Serial ports -@item -IPMI BMC, either and internal or external one -@item -Creative SoundBlaster 16 sound card -@item -ENSONIQ AudioPCI ES1370 sound card -@item -Intel 82801AA AC97 Audio compatible sound card -@item -Intel HD Audio Controller and HDA codec -@item -Adlib (OPL2) - Yamaha YM3812 compatible chip -@item -Gravis Ultrasound GF1 sound card -@item -CS4231A compatible sound card -@item -PCI UHCI, OHCI, EHCI or XHCI USB controller and a virtual USB-1.1 hub. -@end itemize - -SMP is supported with up to 255 CPUs. - -QEMU uses the PC BIOS from the Seabios project and the Plex86/Bochs LGPL -VGA BIOS. - -QEMU uses YM3812 emulation by Tatsuyuki Satoh. - -QEMU uses GUS emulation (GUSEMU32 @url{http://www.deinmeister.de/gusemu/}) -by Tibor "TS" Schütz. - -Note that, by default, GUS shares IRQ(7) with parallel ports and so -QEMU must be told to not have parallel ports to have working GUS. - -@example -@value{qemu_system_x86} dos.img -soundhw gus -parallel none -@end example - -Alternatively: -@example -@value{qemu_system_x86} dos.img -device gus,irq=5 -@end example - -Or some other unclaimed IRQ. - -CS4231A is the chip used in Windows Sound System and GUSMAX products - -@c man end - -@node pcsys_quickstart -@section Quick Start -@cindex quick start - -Download and uncompress a hard disk image with Linux installed (e.g. -@file{linux.img}) and type: - -@example -@value{qemu_system} linux.img -@end example - -Linux should boot and give you a prompt. - -@node sec_invocation -@section Invocation - -@example -@c man begin SYNOPSIS -@command{@value{qemu_system}} [@var{options}] [@var{disk_image}] -@c man end -@end example - -@c man begin OPTIONS -@var{disk_image} is a raw hard disk image for IDE hard disk 0. Some -targets do not need a disk image. - -@include qemu-options.texi - -@c man end - -@subsection Device URL Syntax -@c TODO merge this with section Disk Images - -@c man begin NOTES - -In addition to using normal file images for the emulated storage devices, -QEMU can also use networked resources such as iSCSI devices. These are -specified using a special URL syntax. - -@table @option -@item iSCSI -iSCSI support allows QEMU to access iSCSI resources directly and use as -images for the guest storage. Both disk and cdrom images are supported. - -Syntax for specifying iSCSI LUNs is -``iscsi://[:]//'' - -By default qemu will use the iSCSI initiator-name -'iqn.2008-11.org.linux-kvm[:]' but this can also be set from the command -line or a configuration file. - -Since version Qemu 2.4 it is possible to specify a iSCSI request timeout to detect -stalled requests and force a reestablishment of the session. The timeout -is specified in seconds. The default is 0 which means no timeout. Libiscsi -1.15.0 or greater is required for this feature. - -Example (without authentication): -@example -@value{qemu_system} -iscsi initiator-name=iqn.2001-04.com.example:my-initiator \ - -cdrom iscsi://192.0.2.1/iqn.2001-04.com.example/2 \ - -drive file=iscsi://192.0.2.1/iqn.2001-04.com.example/1 -@end example - -Example (CHAP username/password via URL): -@example -@value{qemu_system} -drive file=iscsi://user%password@@192.0.2.1/iqn.2001-04.com.example/1 -@end example - -Example (CHAP username/password via environment variables): -@example -LIBISCSI_CHAP_USERNAME="user" \ -LIBISCSI_CHAP_PASSWORD="password" \ -@value{qemu_system} -drive file=iscsi://192.0.2.1/iqn.2001-04.com.example/1 -@end example - -@item NBD -QEMU supports NBD (Network Block Devices) both using TCP protocol as well -as Unix Domain Sockets. With TCP, the default port is 10809. - -Syntax for specifying a NBD device using TCP, in preferred URI form: -``nbd://[:]/[]'' - -Syntax for specifying a NBD device using Unix Domain Sockets; remember -that '?' is a shell glob character and may need quoting: -``nbd+unix:///[]?socket='' - -Older syntax that is also recognized: -``nbd::[:exportname=]'' - -Syntax for specifying a NBD device using Unix Domain Sockets -``nbd:unix:[:exportname=]'' - -Example for TCP -@example -@value{qemu_system} --drive file=nbd:192.0.2.1:30000 -@end example - -Example for Unix Domain Sockets -@example -@value{qemu_system} --drive file=nbd:unix:/tmp/nbd-socket -@end example - -@item SSH -QEMU supports SSH (Secure Shell) access to remote disks. - -Examples: -@example -@value{qemu_system} -drive file=ssh://user@@host/path/to/disk.img -@value{qemu_system} -drive file.driver=ssh,file.user=user,file.host=host,file.port=22,file.path=/path/to/disk.img -@end example - -Currently authentication must be done using ssh-agent. Other -authentication methods may be supported in future. - -@item Sheepdog -Sheepdog is a distributed storage system for QEMU. -QEMU supports using either local sheepdog devices or remote networked -devices. - -Syntax for specifying a sheepdog device -@example -sheepdog[+tcp|+unix]://[host:port]/vdiname[?socket=path][#snapid|#tag] -@end example - -Example -@example -@value{qemu_system} --drive file=sheepdog://192.0.2.1:30000/MyVirtualMachine -@end example - -See also @url{https://sheepdog.github.io/sheepdog/}. - -@item GlusterFS -GlusterFS is a user space distributed file system. -QEMU supports the use of GlusterFS volumes for hosting VM disk images using -TCP, Unix Domain Sockets and RDMA transport protocols. - -Syntax for specifying a VM disk image on GlusterFS volume is -@example - -URI: -gluster[+type]://[host[:port]]/volume/path[?socket=...][,debug=N][,logfile=...] - -JSON: -'json:@{"driver":"qcow2","file":@{"driver":"gluster","volume":"testvol","path":"a.img","debug":N,"logfile":"...", -@ "server":[@{"type":"tcp","host":"...","port":"..."@}, -@ @{"type":"unix","socket":"..."@}]@}@}' -@end example - - -Example -@example -URI: -@value{qemu_system} --drive file=gluster://192.0.2.1/testvol/a.img, -@ file.debug=9,file.logfile=/var/log/qemu-gluster.log - -JSON: -@value{qemu_system} 'json:@{"driver":"qcow2", -@ "file":@{"driver":"gluster", -@ "volume":"testvol","path":"a.img", -@ "debug":9,"logfile":"/var/log/qemu-gluster.log", -@ "server":[@{"type":"tcp","host":"1.2.3.4","port":24007@}, -@ @{"type":"unix","socket":"/var/run/glusterd.socket"@}]@}@}' -@value{qemu_system} -drive driver=qcow2,file.driver=gluster,file.volume=testvol,file.path=/path/a.img, -@ file.debug=9,file.logfile=/var/log/qemu-gluster.log, -@ file.server.0.type=tcp,file.server.0.host=1.2.3.4,file.server.0.port=24007, -@ file.server.1.type=unix,file.server.1.socket=/var/run/glusterd.socket -@end example - -See also @url{http://www.gluster.org}. - -@item HTTP/HTTPS/FTP/FTPS -QEMU supports read-only access to files accessed over http(s) and ftp(s). - -Syntax using a single filename: -@example -://[[:]@@]/ -@end example - -where: -@table @option -@item protocol -'http', 'https', 'ftp', or 'ftps'. - -@item username -Optional username for authentication to the remote server. - -@item password -Optional password for authentication to the remote server. - -@item host -Address of the remote server. - -@item path -Path on the remote server, including any query string. -@end table - -The following options are also supported: -@table @option -@item url -The full URL when passing options to the driver explicitly. - -@item readahead -The amount of data to read ahead with each range request to the remote server. -This value may optionally have the suffix 'T', 'G', 'M', 'K', 'k' or 'b'. If it -does not have a suffix, it will be assumed to be in bytes. The value must be a -multiple of 512 bytes. It defaults to 256k. - -@item sslverify -Whether to verify the remote server's certificate when connecting over SSL. It -can have the value 'on' or 'off'. It defaults to 'on'. - -@item cookie -Send this cookie (it can also be a list of cookies separated by ';') with -each outgoing request. Only supported when using protocols such as HTTP -which support cookies, otherwise ignored. - -@item timeout -Set the timeout in seconds of the CURL connection. This timeout is the time -that CURL waits for a response from the remote server to get the size of the -image to be downloaded. If not set, the default timeout of 5 seconds is used. -@end table - -Note that when passing options to qemu explicitly, @option{driver} is the value -of . - -Example: boot from a remote Fedora 20 live ISO image -@example -@value{qemu_system_x86} --drive media=cdrom,file=https://archives.fedoraproject.org/pub/archive/fedora/linux/releases/20/Live/x86_64/Fedora-Live-Desktop-x86_64-20-1.iso,readonly - -@value{qemu_system_x86} --drive media=cdrom,file.driver=http,file.url=http://archives.fedoraproject.org/pub/fedora/linux/releases/20/Live/x86_64/Fedora-Live-Desktop-x86_64-20-1.iso,readonly -@end example - -Example: boot from a remote Fedora 20 cloud image using a local overlay for -writes, copy-on-read, and a readahead of 64k -@example -qemu-img create -f qcow2 -o backing_file='json:@{"file.driver":"http",, "file.url":"http://archives.fedoraproject.org/pub/archive/fedora/linux/releases/20/Images/x86_64/Fedora-x86_64-20-20131211.1-sda.qcow2",, "file.readahead":"64k"@}' /tmp/Fedora-x86_64-20-20131211.1-sda.qcow2 - -@value{qemu_system_x86} -drive file=/tmp/Fedora-x86_64-20-20131211.1-sda.qcow2,copy-on-read=on -@end example - -Example: boot from an image stored on a VMware vSphere server with a self-signed -certificate using a local overlay for writes, a readahead of 64k and a timeout -of 10 seconds. -@example -qemu-img create -f qcow2 -o backing_file='json:@{"file.driver":"https",, "file.url":"https://user:password@@vsphere.example.com/folder/test/test-flat.vmdk?dcPath=Datacenter&dsName=datastore1",, "file.sslverify":"off",, "file.readahead":"64k",, "file.timeout":10@}' /tmp/test.qcow2 - -@value{qemu_system_x86} -drive file=/tmp/test.qcow2 -@end example - -@end table - -@c man end - -@node pcsys_keys -@section Keys in the graphical frontends - -@c man begin OPTIONS - -During the graphical emulation, you can use special key combinations to change -modes. The default key mappings are shown below, but if you use @code{-alt-grab} -then the modifier is Ctrl-Alt-Shift (instead of Ctrl-Alt) and if you use -@code{-ctrl-grab} then the modifier is the right Ctrl key (instead of Ctrl-Alt): - -@table @key -@item Ctrl-Alt-f -@kindex Ctrl-Alt-f -Toggle full screen - -@item Ctrl-Alt-+ -@kindex Ctrl-Alt-+ -Enlarge the screen - -@item Ctrl-Alt-- -@kindex Ctrl-Alt-- -Shrink the screen - -@item Ctrl-Alt-u -@kindex Ctrl-Alt-u -Restore the screen's un-scaled dimensions - -@item Ctrl-Alt-n -@kindex Ctrl-Alt-n -Switch to virtual console 'n'. Standard console mappings are: -@table @emph -@item 1 -Target system display -@item 2 -Monitor -@item 3 -Serial port -@end table - -@item Ctrl-Alt -@kindex Ctrl-Alt -Toggle mouse and keyboard grab. -@end table - -@kindex Ctrl-Up -@kindex Ctrl-Down -@kindex Ctrl-PageUp -@kindex Ctrl-PageDown -In the virtual consoles, you can use @key{Ctrl-Up}, @key{Ctrl-Down}, -@key{Ctrl-PageUp} and @key{Ctrl-PageDown} to move in the back log. - -@c man end - -@node mux_keys -@section Keys in the character backend multiplexer - -@c man begin OPTIONS - -During emulation, if you are using a character backend multiplexer -(which is the default if you are using @option{-nographic}) then -several commands are available via an escape sequence. These -key sequences all start with an escape character, which is @key{Ctrl-a} -by default, but can be changed with @option{-echr}. The list below assumes -you're using the default. - -@table @key -@item Ctrl-a h -@kindex Ctrl-a h -Print this help -@item Ctrl-a x -@kindex Ctrl-a x -Exit emulator -@item Ctrl-a s -@kindex Ctrl-a s -Save disk data back to file (if -snapshot) -@item Ctrl-a t -@kindex Ctrl-a t -Toggle console timestamps -@item Ctrl-a b -@kindex Ctrl-a b -Send break (magic sysrq in Linux) -@item Ctrl-a c -@kindex Ctrl-a c -Rotate between the frontends connected to the multiplexer (usually -this switches between the monitor and the console) -@item Ctrl-a Ctrl-a -@kindex Ctrl-a Ctrl-a -Send the escape character to the frontend -@end table -@c man end - -@ignore - -@c man begin SEEALSO -The HTML documentation of QEMU for more precise information and Linux -user mode emulator invocation. -@c man end - -@c man begin AUTHOR -Fabrice Bellard -@c man end - -@end ignore - -@node pcsys_monitor -@section QEMU Monitor -@cindex QEMU monitor - -The QEMU monitor is used to give complex commands to the QEMU -emulator. You can use it to: - -@itemize @minus - -@item -Remove or insert removable media images -(such as CD-ROM or floppies). - -@item -Freeze/unfreeze the Virtual Machine (VM) and save or restore its state -from a disk file. - -@item Inspect the VM state without an external debugger. - -@end itemize - -@subsection Commands - -The following commands are available: - -@include qemu-monitor.texi - -@include qemu-monitor-info.texi - -@subsection Integer expressions - -The monitor understands integers expressions for every integer -argument. You can use register names to get the value of specifics -CPU registers by prefixing them with @emph{$}. - -@node cpu_models -@section CPU models - -@include docs/qemu-cpu-models.texi - -@node disk_images -@section Disk Images - -QEMU supports many disk image formats, including growable disk images -(their size increase as non empty sectors are written), compressed and -encrypted disk images. - -@menu -* disk_images_quickstart:: Quick start for disk image creation -* disk_images_snapshot_mode:: Snapshot mode -* vm_snapshots:: VM snapshots -* qemu_img_invocation:: qemu-img Invocation -* qemu_nbd_invocation:: qemu-nbd Invocation -* disk_images_formats:: Disk image file formats -* host_drives:: Using host drives -* disk_images_fat_images:: Virtual FAT disk images -* disk_images_nbd:: NBD access -* disk_images_sheepdog:: Sheepdog disk images -* disk_images_iscsi:: iSCSI LUNs -* disk_images_gluster:: GlusterFS disk images -* disk_images_ssh:: Secure Shell (ssh) disk images -* disk_images_nvme:: NVMe userspace driver -* disk_image_locking:: Disk image file locking -@end menu - -@node disk_images_quickstart -@subsection Quick start for disk image creation - -You can create a disk image with the command: -@example -qemu-img create myimage.img mysize -@end example -where @var{myimage.img} is the disk image filename and @var{mysize} is its -size in kilobytes. You can add an @code{M} suffix to give the size in -megabytes and a @code{G} suffix for gigabytes. - -See @ref{qemu_img_invocation} for more information. - -@node disk_images_snapshot_mode -@subsection Snapshot mode - -If you use the option @option{-snapshot}, all disk images are -considered as read only. When sectors in written, they are written in -a temporary file created in @file{/tmp}. You can however force the -write back to the raw disk images by using the @code{commit} monitor -command (or @key{C-a s} in the serial console). - -@node vm_snapshots -@subsection VM snapshots - -VM snapshots are snapshots of the complete virtual machine including -CPU state, RAM, device state and the content of all the writable -disks. In order to use VM snapshots, you must have at least one non -removable and writable block device using the @code{qcow2} disk image -format. Normally this device is the first virtual hard drive. - -Use the monitor command @code{savevm} to create a new VM snapshot or -replace an existing one. A human readable name can be assigned to each -snapshot in addition to its numerical ID. - -Use @code{loadvm} to restore a VM snapshot and @code{delvm} to remove -a VM snapshot. @code{info snapshots} lists the available snapshots -with their associated information: - -@example -(qemu) info snapshots -Snapshot devices: hda -Snapshot list (from hda): -ID TAG VM SIZE DATE VM CLOCK -1 start 41M 2006-08-06 12:38:02 00:00:14.954 -2 40M 2006-08-06 12:43:29 00:00:18.633 -3 msys 40M 2006-08-06 12:44:04 00:00:23.514 -@end example - -A VM snapshot is made of a VM state info (its size is shown in -@code{info snapshots}) and a snapshot of every writable disk image. -The VM state info is stored in the first @code{qcow2} non removable -and writable block device. The disk image snapshots are stored in -every disk image. The size of a snapshot in a disk image is difficult -to evaluate and is not shown by @code{info snapshots} because the -associated disk sectors are shared among all the snapshots to save -disk space (otherwise each snapshot would need a full copy of all the -disk images). - -When using the (unrelated) @code{-snapshot} option -(@ref{disk_images_snapshot_mode}), you can always make VM snapshots, -but they are deleted as soon as you exit QEMU. - -VM snapshots currently have the following known limitations: -@itemize -@item -They cannot cope with removable devices if they are removed or -inserted after a snapshot is done. -@item -A few device drivers still have incomplete snapshot support so their -state is not saved or restored properly (in particular USB). -@end itemize - -@node qemu_img_invocation -@subsection @code{qemu-img} Invocation - -@include qemu-img.texi - -@node qemu_nbd_invocation -@subsection @code{qemu-nbd} Invocation - -@include qemu-nbd.texi - -@include docs/qemu-block-drivers.texi - -@node pcsys_network -@section Network emulation - -QEMU can simulate several network cards (e.g. PCI or ISA cards on the PC -target) and can connect them to a network backend on the host or an emulated -hub. The various host network backends can either be used to connect the NIC of -the guest to a real network (e.g. by using a TAP devices or the non-privileged -user mode network stack), or to other guest instances running in another QEMU -process (e.g. by using the socket host network backend). - -@subsection Using TAP network interfaces - -This is the standard way to connect QEMU to a real network. QEMU adds -a virtual network device on your host (called @code{tapN}), and you -can then configure it as if it was a real ethernet card. - -@subsubsection Linux host - -As an example, you can download the @file{linux-test-xxx.tar.gz} -archive and copy the script @file{qemu-ifup} in @file{/etc} and -configure properly @code{sudo} so that the command @code{ifconfig} -contained in @file{qemu-ifup} can be executed as root. You must verify -that your host kernel supports the TAP network interfaces: the -device @file{/dev/net/tun} must be present. - -See @ref{sec_invocation} to have examples of command lines using the -TAP network interfaces. - -@subsubsection Windows host - -There is a virtual ethernet driver for Windows 2000/XP systems, called -TAP-Win32. But it is not included in standard QEMU for Windows, -so you will need to get it separately. It is part of OpenVPN package, -so download OpenVPN from : @url{https://openvpn.net/}. - -@subsection Using the user mode network stack - -By using the option @option{-net user} (default configuration if no -@option{-net} option is specified), QEMU uses a completely user mode -network stack (you don't need root privilege to use the virtual -network). The virtual network configuration is the following: - -@example - - guest (10.0.2.15) <------> Firewall/DHCP server <-----> Internet - | (10.0.2.2) - | - ----> DNS server (10.0.2.3) - | - ----> SMB server (10.0.2.4) -@end example - -The QEMU VM behaves as if it was behind a firewall which blocks all -incoming connections. You can use a DHCP client to automatically -configure the network in the QEMU VM. The DHCP server assign addresses -to the hosts starting from 10.0.2.15. - -In order to check that the user mode network is working, you can ping -the address 10.0.2.2 and verify that you got an address in the range -10.0.2.x from the QEMU virtual DHCP server. - -Note that ICMP traffic in general does not work with user mode networking. -@code{ping}, aka. ICMP echo, to the local router (10.0.2.2) shall work, -however. If you're using QEMU on Linux >= 3.0, it can use unprivileged ICMP -ping sockets to allow @code{ping} to the Internet. The host admin has to set -the ping_group_range in order to grant access to those sockets. To allow ping -for GID 100 (usually users group): - -@example -echo 100 100 > /proc/sys/net/ipv4/ping_group_range -@end example - -When using the built-in TFTP server, the router is also the TFTP -server. - -When using the @option{'-netdev user,hostfwd=...'} option, TCP or UDP -connections can be redirected from the host to the guest. It allows for -example to redirect X11, telnet or SSH connections. - -@subsection Hubs - -QEMU can simulate several hubs. A hub can be thought of as a virtual connection -between several network devices. These devices can be for example QEMU virtual -ethernet cards or virtual Host ethernet devices (TAP devices). You can connect -guest NICs or host network backends to such a hub using the @option{-netdev -hubport} or @option{-nic hubport} options. The legacy @option{-net} option -also connects the given device to the emulated hub with ID 0 (i.e. the default -hub) unless you specify a netdev with @option{-net nic,netdev=xxx} here. - -@subsection Connecting emulated networks between QEMU instances - -Using the @option{-netdev socket} (or @option{-nic socket} or -@option{-net socket}) option, it is possible to create emulated -networks that span several QEMU instances. -See the description of the @option{-netdev socket} option in the -@ref{sec_invocation,,Invocation chapter} to have a basic example. - -@node pcsys_other_devs -@section Other Devices - -@subsection Inter-VM Shared Memory device - -On Linux hosts, a shared memory device is available. The basic syntax -is: - -@example -@value{qemu_system_x86} -device ivshmem-plain,memdev=@var{hostmem} -@end example - -where @var{hostmem} names a host memory backend. For a POSIX shared -memory backend, use something like - -@example --object memory-backend-file,size=1M,share,mem-path=/dev/shm/ivshmem,id=@var{hostmem} -@end example - -If desired, interrupts can be sent between guest VMs accessing the same shared -memory region. Interrupt support requires using a shared memory server and -using a chardev socket to connect to it. The code for the shared memory server -is qemu.git/contrib/ivshmem-server. An example syntax when using the shared -memory server is: - -@example -# First start the ivshmem server once and for all -ivshmem-server -p @var{pidfile} -S @var{path} -m @var{shm-name} -l @var{shm-size} -n @var{vectors} - -# Then start your qemu instances with matching arguments -@value{qemu_system_x86} -device ivshmem-doorbell,vectors=@var{vectors},chardev=@var{id} - -chardev socket,path=@var{path},id=@var{id} -@end example - -When using the server, the guest will be assigned a VM ID (>=0) that allows guests -using the same server to communicate via interrupts. Guests can read their -VM ID from a device register (see ivshmem-spec.txt). - -@subsubsection Migration with ivshmem - -With device property @option{master=on}, the guest will copy the shared -memory on migration to the destination host. With @option{master=off}, -the guest will not be able to migrate with the device attached. In the -latter case, the device should be detached and then reattached after -migration using the PCI hotplug support. - -At most one of the devices sharing the same memory can be master. The -master must complete migration before you plug back the other devices. - -@subsubsection ivshmem and hugepages - -Instead of specifying the using POSIX shm, you may specify -a memory backend that has hugepage support: - -@example -@value{qemu_system_x86} -object memory-backend-file,size=1G,mem-path=/dev/hugepages/my-shmem-file,share,id=mb1 - -device ivshmem-plain,memdev=mb1 -@end example - -ivshmem-server also supports hugepages mount points with the -@option{-m} memory path argument. - -@node direct_linux_boot -@section Direct Linux Boot - -This section explains how to launch a Linux kernel inside QEMU without -having to make a full bootable image. It is very useful for fast Linux -kernel testing. - -The syntax is: -@example -@value{qemu_system} -kernel bzImage -hda rootdisk.img -append "root=/dev/hda" -@end example - -Use @option{-kernel} to provide the Linux kernel image and -@option{-append} to give the kernel command line arguments. The -@option{-initrd} option can be used to provide an INITRD image. - -If you do not need graphical output, you can disable it and redirect -the virtual serial port and the QEMU monitor to the console with the -@option{-nographic} option. The typical command line is: -@example -@value{qemu_system} -kernel bzImage -hda rootdisk.img \ - -append "root=/dev/hda console=ttyS0" -nographic -@end example - -Use @key{Ctrl-a c} to switch between the serial console and the -monitor (@pxref{pcsys_keys}). - -@node pcsys_usb -@section USB emulation - -QEMU can emulate a PCI UHCI, OHCI, EHCI or XHCI USB controller. You can -plug virtual USB devices or real host USB devices (only works with certain -host operating systems). QEMU will automatically create and connect virtual -USB hubs as necessary to connect multiple USB devices. - -@menu -* usb_devices:: -* host_usb_devices:: -@end menu -@node usb_devices -@subsection Connecting USB devices - -USB devices can be connected with the @option{-device usb-...} command line -option or the @code{device_add} monitor command. Available devices are: - -@table @code -@item usb-mouse -Virtual Mouse. This will override the PS/2 mouse emulation when activated. -@item usb-tablet -Pointer device that uses absolute coordinates (like a touchscreen). -This means QEMU is able to report the mouse position without having -to grab the mouse. Also overrides the PS/2 mouse emulation when activated. -@item usb-storage,drive=@var{drive_id} -Mass storage device backed by @var{drive_id} (@pxref{disk_images}) -@item usb-uas -USB attached SCSI device, see -@url{https://git.qemu.org/?p=qemu.git;a=blob_plain;f=docs/usb-storage.txt,usb-storage.txt} -for details -@item usb-bot -Bulk-only transport storage device, see -@url{https://git.qemu.org/?p=qemu.git;a=blob_plain;f=docs/usb-storage.txt,usb-storage.txt} -for details here, too -@item usb-mtp,rootdir=@var{dir} -Media transfer protocol device, using @var{dir} as root of the file tree -that is presented to the guest. -@item usb-host,hostbus=@var{bus},hostaddr=@var{addr} -Pass through the host device identified by @var{bus} and @var{addr} -@item usb-host,vendorid=@var{vendor},productid=@var{product} -Pass through the host device identified by @var{vendor} and @var{product} ID -@item usb-wacom-tablet -Virtual Wacom PenPartner tablet. This device is similar to the @code{tablet} -above but it can be used with the tslib library because in addition to touch -coordinates it reports touch pressure. -@item usb-kbd -Standard USB keyboard. Will override the PS/2 keyboard (if present). -@item usb-serial,chardev=@var{id} -Serial converter. This emulates an FTDI FT232BM chip connected to host character -device @var{id}. -@item usb-braille,chardev=@var{id} -Braille device. This will use BrlAPI to display the braille output on a real -or fake device referenced by @var{id}. -@item usb-net[,netdev=@var{id}] -Network adapter that supports CDC ethernet and RNDIS protocols. @var{id} -specifies a netdev defined with @code{-netdev @dots{},id=@var{id}}. -For instance, user-mode networking can be used with -@example -@value{qemu_system} [...] -netdev user,id=net0 -device usb-net,netdev=net0 -@end example -@item usb-ccid -Smartcard reader device -@item usb-audio -USB audio device -@item usb-bt-dongle -Bluetooth dongle for the transport layer of HCI. It is connected to HCI -scatternet 0 by default (corresponds to @code{-bt hci,vlan=0}). -Note that the syntax for the @code{-device usb-bt-dongle} option is not as -useful yet as it was with the legacy @code{-usbdevice} option. So to -configure an USB bluetooth device, you might need to use -"@code{-usbdevice bt}[:@var{hci-type}]" instead. This configures a -bluetooth dongle whose type is specified in the same format as with -the @option{-bt hci} option, @pxref{bt-hcis,,allowed HCI types}. If -no type is given, the HCI logic corresponds to @code{-bt hci,vlan=0}. -This USB device implements the USB Transport Layer of HCI. Example -usage: -@example -@command{@value{qemu_system}} [...@var{OPTIONS}...] @option{-usbdevice} bt:hci,vlan=3 @option{-bt} device:keyboard,vlan=3 -@end example -@end table - -@node host_usb_devices -@subsection Using host USB devices on a Linux host - -WARNING: this is an experimental feature. QEMU will slow down when -using it. USB devices requiring real time streaming (i.e. USB Video -Cameras) are not supported yet. - -@enumerate -@item If you use an early Linux 2.4 kernel, verify that no Linux driver -is actually using the USB device. A simple way to do that is simply to -disable the corresponding kernel module by renaming it from @file{mydriver.o} -to @file{mydriver.o.disabled}. - -@item Verify that @file{/proc/bus/usb} is working (most Linux distributions should enable it by default). You should see something like that: -@example -ls /proc/bus/usb -001 devices drivers -@end example - -@item Since only root can access to the USB devices directly, you can either launch QEMU as root or change the permissions of the USB devices you want to use. For testing, the following suffices: -@example -chown -R myuid /proc/bus/usb -@end example - -@item Launch QEMU and do in the monitor: -@example -info usbhost - Device 1.2, speed 480 Mb/s - Class 00: USB device 1234:5678, USB DISK -@end example -You should see the list of the devices you can use (Never try to use -hubs, it won't work). - -@item Add the device in QEMU by using: -@example -device_add usb-host,vendorid=0x1234,productid=0x5678 -@end example - -Normally the guest OS should report that a new USB device is plugged. -You can use the option @option{-device usb-host,...} to do the same. - -@item Now you can try to use the host USB device in QEMU. - -@end enumerate - -When relaunching QEMU, you may have to unplug and plug again the USB -device to make it work again (this is a bug). - -@node vnc_security -@section VNC security - -The VNC server capability provides access to the graphical console -of the guest VM across the network. This has a number of security -considerations depending on the deployment scenarios. - -@menu -* vnc_sec_none:: -* vnc_sec_password:: -* vnc_sec_certificate:: -* vnc_sec_certificate_verify:: -* vnc_sec_certificate_pw:: -* vnc_sec_sasl:: -* vnc_sec_certificate_sasl:: -* vnc_setup_sasl:: -@end menu -@node vnc_sec_none -@subsection Without passwords - -The simplest VNC server setup does not include any form of authentication. -For this setup it is recommended to restrict it to listen on a UNIX domain -socket only. For example - -@example -@value{qemu_system} [...OPTIONS...] -vnc unix:/home/joebloggs/.qemu-myvm-vnc -@end example - -This ensures that only users on local box with read/write access to that -path can access the VNC server. To securely access the VNC server from a -remote machine, a combination of netcat+ssh can be used to provide a secure -tunnel. - -@node vnc_sec_password -@subsection With passwords - -The VNC protocol has limited support for password based authentication. Since -the protocol limits passwords to 8 characters it should not be considered -to provide high security. The password can be fairly easily brute-forced by -a client making repeat connections. For this reason, a VNC server using password -authentication should be restricted to only listen on the loopback interface -or UNIX domain sockets. Password authentication is not supported when operating -in FIPS 140-2 compliance mode as it requires the use of the DES cipher. Password -authentication is requested with the @code{password} option, and then once QEMU -is running the password is set with the monitor. Until the monitor is used to -set the password all clients will be rejected. - -@example -@value{qemu_system} [...OPTIONS...] -vnc :1,password -monitor stdio -(qemu) change vnc password -Password: ******** -(qemu) -@end example - -@node vnc_sec_certificate -@subsection With x509 certificates - -The QEMU VNC server also implements the VeNCrypt extension allowing use of -TLS for encryption of the session, and x509 certificates for authentication. -The use of x509 certificates is strongly recommended, because TLS on its -own is susceptible to man-in-the-middle attacks. Basic x509 certificate -support provides a secure session, but no authentication. This allows any -client to connect, and provides an encrypted session. - -@example -@value{qemu_system} [...OPTIONS...] \ - -object tls-creds-x509,id=tls0,dir=/etc/pki/qemu,endpoint=server,verify-peer=no \ - -vnc :1,tls-creds=tls0 -monitor stdio -@end example - -In the above example @code{/etc/pki/qemu} should contain at least three files, -@code{ca-cert.pem}, @code{server-cert.pem} and @code{server-key.pem}. Unprivileged -users will want to use a private directory, for example @code{$HOME/.pki/qemu}. -NB the @code{server-key.pem} file should be protected with file mode 0600 to -only be readable by the user owning it. - -@node vnc_sec_certificate_verify -@subsection With x509 certificates and client verification - -Certificates can also provide a means to authenticate the client connecting. -The server will request that the client provide a certificate, which it will -then validate against the CA certificate. This is a good choice if deploying -in an environment with a private internal certificate authority. It uses the -same syntax as previously, but with @code{verify-peer} set to @code{yes} -instead. - -@example -@value{qemu_system} [...OPTIONS...] \ - -object tls-creds-x509,id=tls0,dir=/etc/pki/qemu,endpoint=server,verify-peer=yes \ - -vnc :1,tls-creds=tls0 -monitor stdio -@end example - - -@node vnc_sec_certificate_pw -@subsection With x509 certificates, client verification and passwords - -Finally, the previous method can be combined with VNC password authentication -to provide two layers of authentication for clients. - -@example -@value{qemu_system} [...OPTIONS...] \ - -object tls-creds-x509,id=tls0,dir=/etc/pki/qemu,endpoint=server,verify-peer=yes \ - -vnc :1,tls-creds=tls0,password -monitor stdio -(qemu) change vnc password -Password: ******** -(qemu) -@end example - - -@node vnc_sec_sasl -@subsection With SASL authentication - -The SASL authentication method is a VNC extension, that provides an -easily extendable, pluggable authentication method. This allows for -integration with a wide range of authentication mechanisms, such as -PAM, GSSAPI/Kerberos, LDAP, SQL databases, one-time keys and more. -The strength of the authentication depends on the exact mechanism -configured. If the chosen mechanism also provides a SSF layer, then -it will encrypt the datastream as well. - -Refer to the later docs on how to choose the exact SASL mechanism -used for authentication, but assuming use of one supporting SSF, -then QEMU can be launched with: - -@example -@value{qemu_system} [...OPTIONS...] -vnc :1,sasl -monitor stdio -@end example - -@node vnc_sec_certificate_sasl -@subsection With x509 certificates and SASL authentication - -If the desired SASL authentication mechanism does not supported -SSF layers, then it is strongly advised to run it in combination -with TLS and x509 certificates. This provides securely encrypted -data stream, avoiding risk of compromising of the security -credentials. This can be enabled, by combining the 'sasl' option -with the aforementioned TLS + x509 options: - -@example -@value{qemu_system} [...OPTIONS...] \ - -object tls-creds-x509,id=tls0,dir=/etc/pki/qemu,endpoint=server,verify-peer=yes \ - -vnc :1,tls-creds=tls0,sasl -monitor stdio -@end example - -@node vnc_setup_sasl - -@subsection Configuring SASL mechanisms - -The following documentation assumes use of the Cyrus SASL implementation on a -Linux host, but the principles should apply to any other SASL implementation -or host. When SASL is enabled, the mechanism configuration will be loaded from -system default SASL service config /etc/sasl2/qemu.conf. If running QEMU as an -unprivileged user, an environment variable SASL_CONF_PATH can be used to make -it search alternate locations for the service config file. - -If the TLS option is enabled for VNC, then it will provide session encryption, -otherwise the SASL mechanism will have to provide encryption. In the latter -case the list of possible plugins that can be used is drastically reduced. In -fact only the GSSAPI SASL mechanism provides an acceptable level of security -by modern standards. Previous versions of QEMU referred to the DIGEST-MD5 -mechanism, however, it has multiple serious flaws described in detail in -RFC 6331 and thus should never be used any more. The SCRAM-SHA-1 mechanism -provides a simple username/password auth facility similar to DIGEST-MD5, but -does not support session encryption, so can only be used in combination with -TLS. - -When not using TLS the recommended configuration is - -@example -mech_list: gssapi -keytab: /etc/qemu/krb5.tab -@end example - -This says to use the 'GSSAPI' mechanism with the Kerberos v5 protocol, with -the server principal stored in /etc/qemu/krb5.tab. For this to work the -administrator of your KDC must generate a Kerberos principal for the server, -with a name of 'qemu/somehost.example.com@@EXAMPLE.COM' replacing -'somehost.example.com' with the fully qualified host name of the machine -running QEMU, and 'EXAMPLE.COM' with the Kerberos Realm. - -When using TLS, if username+password authentication is desired, then a -reasonable configuration is - -@example -mech_list: scram-sha-1 -sasldb_path: /etc/qemu/passwd.db -@end example - -The @code{saslpasswd2} program can be used to populate the @code{passwd.db} -file with accounts. - -Other SASL configurations will be left as an exercise for the reader. Note that -all mechanisms, except GSSAPI, should be combined with use of TLS to ensure a -secure data channel. - - -@node network_tls -@section TLS setup for network services - -Almost all network services in QEMU have the ability to use TLS for -session data encryption, along with x509 certificates for simple -client authentication. What follows is a description of how to -generate certificates suitable for usage with QEMU, and applies to -the VNC server, character devices with the TCP backend, NBD server -and client, and migration server and client. - -At a high level, QEMU requires certificates and private keys to be -provided in PEM format. Aside from the core fields, the certificates -should include various extension data sets, including v3 basic -constraints data, key purpose, key usage and subject alt name. - -The GnuTLS package includes a command called @code{certtool} which can -be used to easily generate certificates and keys in the required format -with expected data present. Alternatively a certificate management -service may be used. - -At a minimum it is necessary to setup a certificate authority, and -issue certificates to each server. If using x509 certificates for -authentication, then each client will also need to be issued a -certificate. - -Assuming that the QEMU network services will only ever be exposed to -clients on a private intranet, there is no need to use a commercial -certificate authority to create certificates. A self-signed CA is -sufficient, and in fact likely to be more secure since it removes -the ability of malicious 3rd parties to trick the CA into mis-issuing -certs for impersonating your services. The only likely exception -where a commercial CA might be desirable is if enabling the VNC -websockets server and exposing it directly to remote browser clients. -In such a case it might be useful to use a commercial CA to avoid -needing to install custom CA certs in the web browsers. - -The recommendation is for the server to keep its certificates in either -@code{/etc/pki/qemu} or for unprivileged users in @code{$HOME/.pki/qemu}. - -@menu -* tls_generate_ca:: -* tls_generate_server:: -* tls_generate_client:: -* tls_creds_setup:: -* tls_psk:: -@end menu -@node tls_generate_ca -@subsection Setup the Certificate Authority - -This step only needs to be performed once per organization / organizational -unit. First the CA needs a private key. This key must be kept VERY secret -and secure. If this key is compromised the entire trust chain of the certificates -issued with it is lost. - -@example -# certtool --generate-privkey > ca-key.pem -@end example - -To generate a self-signed certificate requires one core piece of information, -the name of the organization. A template file @code{ca.info} should be -populated with the desired data to avoid having to deal with interactive -prompts from certtool: -@example -# cat > ca.info < server-hostNNN.info < server-hostNNN-key.pem -# certtool --generate-certificate \ - --load-ca-certificate ca-cert.pem \ - --load-ca-privkey ca-key.pem \ - --load-privkey server-hostNNN-key.pem \ - --template server-hostNNN.info \ - --outfile server-hostNNN-cert.pem -@end example - -The @code{dns_name} and @code{ip_address} fields in the template are setting -the subject alt name extension data. The @code{tls_www_server} keyword is the -key purpose extension to indicate this certificate is intended for usage in -a web server. Although QEMU network services are not in fact HTTP servers -(except for VNC websockets), setting this key purpose is still recommended. -The @code{encryption_key} and @code{signing_key} keyword is the key usage -extension to indicate this certificate is intended for usage in the data -session. - -The @code{server-hostNNN-key.pem} and @code{server-hostNNN-cert.pem} files -should now be securely copied to the server for which they were generated, -and renamed to @code{server-key.pem} and @code{server-cert.pem} when added -to the @code{/etc/pki/qemu} directory on the target host. The @code{server-key.pem} -file is security sensitive and should be kept protected with file mode 0600 -to prevent disclosure. - -@node tls_generate_client -@subsection Issuing client certificates - -The QEMU x509 TLS credential setup defaults to enabling client verification -using certificates, providing a simple authentication mechanism. If this -default is used, each client also needs to be issued a certificate. The client -certificate contains enough metadata to uniquely identify the client with the -scope of the certificate authority. The client certificate would typically -include fields for organization, state, city, building, etc. - -Once again on the host holding the CA, create template files containing the -information for each client, and use it to issue client certificates. - - -@example -# cat > client-hostNNN.info < client-hostNNN-key.pem -# certtool --generate-certificate \ - --load-ca-certificate ca-cert.pem \ - --load-ca-privkey ca-key.pem \ - --load-privkey client-hostNNN-key.pem \ - --template client-hostNNN.info \ - --outfile client-hostNNN-cert.pem -@end example - -The subject alt name extension data is not required for clients, so the -the @code{dns_name} and @code{ip_address} fields are not included. -The @code{tls_www_client} keyword is the key purpose extension to indicate -this certificate is intended for usage in a web client. Although QEMU -network clients are not in fact HTTP clients, setting this key purpose is -still recommended. The @code{encryption_key} and @code{signing_key} keyword -is the key usage extension to indicate this certificate is intended for -usage in the data session. - -The @code{client-hostNNN-key.pem} and @code{client-hostNNN-cert.pem} files -should now be securely copied to the client for which they were generated, -and renamed to @code{client-key.pem} and @code{client-cert.pem} when added -to the @code{/etc/pki/qemu} directory on the target host. The @code{client-key.pem} -file is security sensitive and should be kept protected with file mode 0600 -to prevent disclosure. - -If a single host is going to be using TLS in both a client and server -role, it is possible to create a single certificate to cover both roles. -This would be quite common for the migration and NBD services, where a -QEMU process will be started by accepting a TLS protected incoming migration, -and later itself be migrated out to another host. To generate a single -certificate, simply include the template data from both the client and server -instructions in one. - -@example -# cat > both-hostNNN.info < both-hostNNN-key.pem -# certtool --generate-certificate \ - --load-ca-certificate ca-cert.pem \ - --load-ca-privkey ca-key.pem \ - --load-privkey both-hostNNN-key.pem \ - --template both-hostNNN.info \ - --outfile both-hostNNN-cert.pem -@end example - -When copying the PEM files to the target host, save them twice, -once as @code{server-cert.pem} and @code{server-key.pem}, and -again as @code{client-cert.pem} and @code{client-key.pem}. - -@node tls_creds_setup -@subsection TLS x509 credential configuration - -QEMU has a standard mechanism for loading x509 credentials that will be -used for network services and clients. It requires specifying the -@code{tls-creds-x509} class name to the @code{--object} command line -argument for the system emulators. Each set of credentials loaded should -be given a unique string identifier via the @code{id} parameter. A single -set of TLS credentials can be used for multiple network backends, so VNC, -migration, NBD, character devices can all share the same credentials. Note, -however, that credentials for use in a client endpoint must be loaded -separately from those used in a server endpoint. - -When specifying the object, the @code{dir} parameters specifies which -directory contains the credential files. This directory is expected to -contain files with the names mentioned previously, @code{ca-cert.pem}, -@code{server-key.pem}, @code{server-cert.pem}, @code{client-key.pem} -and @code{client-cert.pem} as appropriate. It is also possible to -include a set of pre-generated Diffie-Hellman (DH) parameters in a file -@code{dh-params.pem}, which can be created using the -@code{certtool --generate-dh-params} command. If omitted, QEMU will -dynamically generate DH parameters when loading the credentials. - -The @code{endpoint} parameter indicates whether the credentials will -be used for a network client or server, and determines which PEM -files are loaded. - -The @code{verify} parameter determines whether x509 certificate -validation should be performed. This defaults to enabled, meaning -clients will always validate the server hostname against the -certificate subject alt name fields and/or CN field. It also -means that servers will request that clients provide a certificate -and validate them. Verification should never be turned off for -client endpoints, however, it may be turned off for server endpoints -if an alternative mechanism is used to authenticate clients. For -example, the VNC server can use SASL to authenticate clients -instead. - -To load server credentials with client certificate validation -enabled - -@example -@value{qemu_system} -object tls-creds-x509,id=tls0,dir=/etc/pki/qemu,endpoint=server -@end example - -while to load client credentials use - -@example -@value{qemu_system} -object tls-creds-x509,id=tls0,dir=/etc/pki/qemu,endpoint=client -@end example - -Network services which support TLS will all have a @code{tls-creds} -parameter which expects the ID of the TLS credentials object. For -example with VNC: - -@example -@value{qemu_system} -vnc 0.0.0.0:0,tls-creds=tls0 -@end example - -@node tls_psk -@subsection TLS Pre-Shared Keys (PSK) - -Instead of using certificates, you may also use TLS Pre-Shared Keys -(TLS-PSK). This can be simpler to set up than certificates but is -less scalable. - -Use the GnuTLS @code{psktool} program to generate a @code{keys.psk} -file containing one or more usernames and random keys: - -@example -mkdir -m 0700 /tmp/keys -psktool -u rich -p /tmp/keys/keys.psk -@end example - -TLS-enabled servers such as qemu-nbd can use this directory like so: - -@example -qemu-nbd \ - -t -x / \ - --object tls-creds-psk,id=tls0,endpoint=server,dir=/tmp/keys \ - --tls-creds tls0 \ - image.qcow2 -@end example - -When connecting from a qemu-based client you must specify the -directory containing @code{keys.psk} and an optional @var{username} -(defaults to ``qemu''): - -@example -qemu-img info \ - --object tls-creds-psk,id=tls0,dir=/tmp/keys,username=rich,endpoint=client \ - --image-opts \ - file.driver=nbd,file.host=localhost,file.port=10809,file.tls-creds=tls0,file.export=/ -@end example - -@node gdb_usage -@section GDB usage - -QEMU has a primitive support to work with gdb, so that you can do -'Ctrl-C' while the virtual machine is running and inspect its state. - -In order to use gdb, launch QEMU with the '-s' option. It will wait for a -gdb connection: -@example -@value{qemu_system} -s -kernel bzImage -hda rootdisk.img -append "root=/dev/hda" -Connected to host network interface: tun0 -Waiting gdb connection on port 1234 -@end example - -Then launch gdb on the 'vmlinux' executable: -@example -> gdb vmlinux -@end example - -In gdb, connect to QEMU: -@example -(gdb) target remote localhost:1234 -@end example - -Then you can use gdb normally. For example, type 'c' to launch the kernel: -@example -(gdb) c -@end example - -Here are some useful tips in order to use gdb on system code: - -@enumerate -@item -Use @code{info reg} to display all the CPU registers. -@item -Use @code{x/10i $eip} to display the code at the PC position. -@item -Use @code{set architecture i8086} to dump 16 bit code. Then use -@code{x/10i $cs*16+$eip} to dump the code at the PC position. -@end enumerate - -Advanced debugging options: - -The default single stepping behavior is step with the IRQs and timer service routines off. It is set this way because when gdb executes a single step it expects to advance beyond the current instruction. With the IRQs and timer service routines on, a single step might jump into the one of the interrupt or exception vectors instead of executing the current instruction. This means you may hit the same breakpoint a number of times before executing the instruction gdb wants to have executed. Because there are rare circumstances where you want to single step into an interrupt vector the behavior can be controlled from GDB. There are three commands you can query and set the single step behavior: -@table @code -@item maintenance packet qqemu.sstepbits - -This will display the MASK bits used to control the single stepping IE: -@example -(gdb) maintenance packet qqemu.sstepbits -sending: "qqemu.sstepbits" -received: "ENABLE=1,NOIRQ=2,NOTIMER=4" -@end example -@item maintenance packet qqemu.sstep - -This will display the current value of the mask used when single stepping IE: -@example -(gdb) maintenance packet qqemu.sstep -sending: "qqemu.sstep" -received: "0x7" -@end example -@item maintenance packet Qqemu.sstep=HEX_VALUE - -This will change the single step mask, so if wanted to enable IRQs on the single step, but not timers, you would use: -@example -(gdb) maintenance packet Qqemu.sstep=0x5 -sending: "qemu.sstep=0x5" -received: "OK" -@end example -@end table - -@node pcsys_os_specific -@section Target OS specific information - -@subsection Linux - -To have access to SVGA graphic modes under X11, use the @code{vesa} or -the @code{cirrus} X11 driver. For optimal performances, use 16 bit -color depth in the guest and the host OS. - -When using a 2.6 guest Linux kernel, you should add the option -@code{clock=pit} on the kernel command line because the 2.6 Linux -kernels make very strict real time clock checks by default that QEMU -cannot simulate exactly. - -When using a 2.6 guest Linux kernel, verify that the 4G/4G patch is -not activated because QEMU is slower with this patch. The QEMU -Accelerator Module is also much slower in this case. Earlier Fedora -Core 3 Linux kernel (< 2.6.9-1.724_FC3) were known to incorporate this -patch by default. Newer kernels don't have it. - -@subsection Windows - -If you have a slow host, using Windows 95 is better as it gives the -best speed. Windows 2000 is also a good choice. - -@subsubsection SVGA graphic modes support - -QEMU emulates a Cirrus Logic GD5446 Video -card. All Windows versions starting from Windows 95 should recognize -and use this graphic card. For optimal performances, use 16 bit color -depth in the guest and the host OS. - -If you are using Windows XP as guest OS and if you want to use high -resolution modes which the Cirrus Logic BIOS does not support (i.e. >= -1280x1024x16), then you should use the VESA VBE virtual graphic card -(option @option{-std-vga}). - -@subsubsection CPU usage reduction - -Windows 9x does not correctly use the CPU HLT -instruction. The result is that it takes host CPU cycles even when -idle. You can install the utility from -@url{https://web.archive.org/web/20060212132151/http://www.user.cityline.ru/~maxamn/amnhltm.zip} -to solve this problem. Note that no such tool is needed for NT, 2000 or XP. - -@subsubsection Windows 2000 disk full problem - -Windows 2000 has a bug which gives a disk full problem during its -installation. When installing it, use the @option{-win2k-hack} QEMU -option to enable a specific workaround. After Windows 2000 is -installed, you no longer need this option (this option slows down the -IDE transfers). - -@subsubsection Windows 2000 shutdown - -Windows 2000 cannot automatically shutdown in QEMU although Windows 98 -can. It comes from the fact that Windows 2000 does not automatically -use the APM driver provided by the BIOS. - -In order to correct that, do the following (thanks to Struan -Bartlett): go to the Control Panel => Add/Remove Hardware & Next => -Add/Troubleshoot a device => Add a new device & Next => No, select the -hardware from a list & Next => NT Apm/Legacy Support & Next => Next -(again) a few times. Now the driver is installed and Windows 2000 now -correctly instructs QEMU to shutdown at the appropriate moment. - -@subsubsection Share a directory between Unix and Windows - -See @ref{sec_invocation} about the help of the option -@option{'-netdev user,smb=...'}. - -@subsubsection Windows XP security problem - -Some releases of Windows XP install correctly but give a security -error when booting: -@example -A problem is preventing Windows from accurately checking the -license for this computer. Error code: 0x800703e6. -@end example - -The workaround is to install a service pack for XP after a boot in safe -mode. Then reboot, and the problem should go away. Since there is no -network while in safe mode, its recommended to download the full -installation of SP1 or SP2 and transfer that via an ISO or using the -vvfat block device ("-hdb fat:directory_which_holds_the_SP"). - -@subsection MS-DOS and FreeDOS - -@subsubsection CPU usage reduction - -DOS does not correctly use the CPU HLT instruction. The result is that -it takes host CPU cycles even when idle. You can install the utility from -@url{https://web.archive.org/web/20051222085335/http://www.vmware.com/software/dosidle210.zip} -to solve this problem. - -@node QEMU System emulator for non PC targets -@chapter QEMU System emulator for non PC targets - -QEMU is a generic emulator and it emulates many non PC -machines. Most of the options are similar to the PC emulator. The -differences are mentioned in the following sections. - -@menu -* PowerPC System emulator:: -* Sparc32 System emulator:: -* Sparc64 System emulator:: -* MIPS System emulator:: -* ARM System emulator:: -* ColdFire System emulator:: -* Cris System emulator:: -* Microblaze System emulator:: -* SH4 System emulator:: -* Xtensa System emulator:: -@end menu - -@node PowerPC System emulator -@section PowerPC System emulator -@cindex system emulation (PowerPC) - -Use the executable @file{qemu-system-ppc} to simulate a complete PREP -or PowerMac PowerPC system. - -QEMU emulates the following PowerMac peripherals: - -@itemize @minus -@item -UniNorth or Grackle PCI Bridge -@item -PCI VGA compatible card with VESA Bochs Extensions -@item -2 PMAC IDE interfaces with hard disk and CD-ROM support -@item -NE2000 PCI adapters -@item -Non Volatile RAM -@item -VIA-CUDA with ADB keyboard and mouse. -@end itemize - -QEMU emulates the following PREP peripherals: - -@itemize @minus -@item -PCI Bridge -@item -PCI VGA compatible card with VESA Bochs Extensions -@item -2 IDE interfaces with hard disk and CD-ROM support -@item -Floppy disk -@item -NE2000 network adapters -@item -Serial port -@item -PREP Non Volatile RAM -@item -PC compatible keyboard and mouse. -@end itemize - -QEMU uses the Open Hack'Ware Open Firmware Compatible BIOS available at -@url{http://perso.magic.fr/l_indien/OpenHackWare/index.htm}. - -Since version 0.9.1, QEMU uses OpenBIOS @url{https://www.openbios.org/} -for the g3beige and mac99 PowerMac machines. OpenBIOS is a free (GPL -v2) portable firmware implementation. The goal is to implement a 100% -IEEE 1275-1994 (referred to as Open Firmware) compliant firmware. - -@c man begin OPTIONS - -The following options are specific to the PowerPC emulation: - -@table @option - -@item -g @var{W}x@var{H}[x@var{DEPTH}] - -Set the initial VGA graphic mode. The default is 800x600x32. - -@item -prom-env @var{string} - -Set OpenBIOS variables in NVRAM, for example: - -@example -qemu-system-ppc -prom-env 'auto-boot?=false' \ - -prom-env 'boot-device=hd:2,\yaboot' \ - -prom-env 'boot-args=conf=hd:2,\yaboot.conf' -@end example - -These variables are not used by Open Hack'Ware. - -@end table - -@c man end - - -More information is available at -@url{http://perso.magic.fr/l_indien/qemu-ppc/}. - -@node Sparc32 System emulator -@section Sparc32 System emulator -@cindex system emulation (Sparc32) - -Use the executable @file{qemu-system-sparc} to simulate the following -Sun4m architecture machines: -@itemize @minus -@item -SPARCstation 4 -@item -SPARCstation 5 -@item -SPARCstation 10 -@item -SPARCstation 20 -@item -SPARCserver 600MP -@item -SPARCstation LX -@item -SPARCstation Voyager -@item -SPARCclassic -@item -SPARCbook -@end itemize - -The emulation is somewhat complete. SMP up to 16 CPUs is supported, -but Linux limits the number of usable CPUs to 4. - -QEMU emulates the following sun4m peripherals: - -@itemize @minus -@item -IOMMU -@item -TCX or cgthree Frame buffer -@item -Lance (Am7990) Ethernet -@item -Non Volatile RAM M48T02/M48T08 -@item -Slave I/O: timers, interrupt controllers, Zilog serial ports, keyboard -and power/reset logic -@item -ESP SCSI controller with hard disk and CD-ROM support -@item -Floppy drive (not on SS-600MP) -@item -CS4231 sound device (only on SS-5, not working yet) -@end itemize - -The number of peripherals is fixed in the architecture. Maximum -memory size depends on the machine type, for SS-5 it is 256MB and for -others 2047MB. - -Since version 0.8.2, QEMU uses OpenBIOS -@url{https://www.openbios.org/}. OpenBIOS is a free (GPL v2) portable -firmware implementation. The goal is to implement a 100% IEEE -1275-1994 (referred to as Open Firmware) compliant firmware. - -A sample Linux 2.6 series kernel and ram disk image are available on -the QEMU web site. There are still issues with NetBSD and OpenBSD, but -most kernel versions work. Please note that currently older Solaris kernels -don't work probably due to interface issues between OpenBIOS and -Solaris. - -@c man begin OPTIONS - -The following options are specific to the Sparc32 emulation: - -@table @option - -@item -g @var{W}x@var{H}x[x@var{DEPTH}] - -Set the initial graphics mode. For TCX, the default is 1024x768x8 with the -option of 1024x768x24. For cgthree, the default is 1024x768x8 with the option -of 1152x900x8 for people who wish to use OBP. - -@item -prom-env @var{string} - -Set OpenBIOS variables in NVRAM, for example: - -@example -qemu-system-sparc -prom-env 'auto-boot?=false' \ - -prom-env 'boot-device=sd(0,2,0):d' -prom-env 'boot-args=linux single' -@end example - -@item -M [SS-4|SS-5|SS-10|SS-20|SS-600MP|LX|Voyager|SPARCClassic] [|SPARCbook] - -Set the emulated machine type. Default is SS-5. - -@end table - -@c man end - -@node Sparc64 System emulator -@section Sparc64 System emulator -@cindex system emulation (Sparc64) - -Use the executable @file{qemu-system-sparc64} to simulate a Sun4u -(UltraSPARC PC-like machine), Sun4v (T1 PC-like machine), or generic -Niagara (T1) machine. The Sun4u emulator is mostly complete, being -able to run Linux, NetBSD and OpenBSD in headless (-nographic) mode. The -Sun4v emulator is still a work in progress. - -The Niagara T1 emulator makes use of firmware and OS binaries supplied in the S10image/ directory -of the OpenSPARC T1 project @url{http://download.oracle.com/technetwork/systems/opensparc/OpenSPARCT1_Arch.1.5.tar.bz2} -and is able to boot the disk.s10hw2 Solaris image. -@example -qemu-system-sparc64 -M niagara -L /path-to/S10image/ \ - -nographic -m 256 \ - -drive if=pflash,readonly=on,file=/S10image/disk.s10hw2 -@end example - - -QEMU emulates the following peripherals: - -@itemize @minus -@item -UltraSparc IIi APB PCI Bridge -@item -PCI VGA compatible card with VESA Bochs Extensions -@item -PS/2 mouse and keyboard -@item -Non Volatile RAM M48T59 -@item -PC-compatible serial ports -@item -2 PCI IDE interfaces with hard disk and CD-ROM support -@item -Floppy disk -@end itemize - -@c man begin OPTIONS - -The following options are specific to the Sparc64 emulation: - -@table @option - -@item -prom-env @var{string} - -Set OpenBIOS variables in NVRAM, for example: - -@example -qemu-system-sparc64 -prom-env 'auto-boot?=false' -@end example - -@item -M [sun4u|sun4v|niagara] - -Set the emulated machine type. The default is sun4u. - -@end table - -@c man end - -@node MIPS System emulator -@section MIPS System emulator -@cindex system emulation (MIPS) - -@menu -* nanoMIPS System emulator :: -@end menu - -Four executables cover simulation of 32 and 64-bit MIPS systems in -both endian options, @file{qemu-system-mips}, @file{qemu-system-mipsel} -@file{qemu-system-mips64} and @file{qemu-system-mips64el}. -Five different machine types are emulated: - -@itemize @minus -@item -A generic ISA PC-like machine "mips" -@item -The MIPS Malta prototype board "malta" -@item -An ACER Pica "pica61". This machine needs the 64-bit emulator. -@item -MIPS emulator pseudo board "mipssim" -@item -A MIPS Magnum R4000 machine "magnum". This machine needs the 64-bit emulator. -@end itemize - -The generic emulation is supported by Debian 'Etch' and is able to -install Debian into a virtual disk image. The following devices are -emulated: - -@itemize @minus -@item -A range of MIPS CPUs, default is the 24Kf -@item -PC style serial port -@item -PC style IDE disk -@item -NE2000 network card -@end itemize - -The Malta emulation supports the following devices: - -@itemize @minus -@item -Core board with MIPS 24Kf CPU and Galileo system controller -@item -PIIX4 PCI/USB/SMbus controller -@item -The Multi-I/O chip's serial device -@item -PCI network cards (PCnet32 and others) -@item -Malta FPGA serial device -@item -Cirrus (default) or any other PCI VGA graphics card -@end itemize - -The Boston board emulation supports the following devices: - -@itemize @minus -@item -Xilinx FPGA, which includes a PCIe root port and an UART -@item -Intel EG20T PCH connects the I/O peripherals, but only the SATA bus is emulated -@end itemize - -The ACER Pica emulation supports: - -@itemize @minus -@item -MIPS R4000 CPU -@item -PC-style IRQ and DMA controllers -@item -PC Keyboard -@item -IDE controller -@end itemize - -The MIPS Magnum R4000 emulation supports: - -@itemize @minus -@item -MIPS R4000 CPU -@item -PC-style IRQ controller -@item -PC Keyboard -@item -SCSI controller -@item -G364 framebuffer -@end itemize - -The Fulong 2E emulation supports: - -@itemize @minus -@item -Loongson 2E CPU -@item -Bonito64 system controller as North Bridge -@item -VT82C686 chipset as South Bridge -@item -RTL8139D as a network card chipset -@end itemize - -The mipssim pseudo board emulation provides an environment similar -to what the proprietary MIPS emulator uses for running Linux. -It supports: - -@itemize @minus -@item -A range of MIPS CPUs, default is the 24Kf -@item -PC style serial port -@item -MIPSnet network emulation -@end itemize - -@node nanoMIPS System emulator -@subsection nanoMIPS System emulator -@cindex system emulation (nanoMIPS) - -Executable @file{qemu-system-mipsel} also covers simulation of -32-bit nanoMIPS system in little endian mode: - -@itemize @minus -@item -nanoMIPS I7200 CPU -@end itemize - -Example of @file{qemu-system-mipsel} usage for nanoMIPS is shown below: - -Download @code{} from @url{https://mipsdistros.mips.com/LinuxDistro/nanomips/buildroot/index.html}. - -Download @code{} from @url{https://mipsdistros.mips.com/LinuxDistro/nanomips/kernels/v4.15.18-432-gb2eb9a8b07a1-20180627102142/index.html}. - -Start system emulation of Malta board with nanoMIPS I7200 CPU: -@example -qemu-system-mipsel -cpu I7200 -kernel @code{} \ - -M malta -serial stdio -m @code{} -hda @code{} \ - -append "mem=256m@@0x0 rw console=ttyS0 vga=cirrus vesa=0x111 root=/dev/sda" -@end example - - -@node ARM System emulator -@section ARM System emulator -@cindex system emulation (ARM) - -Use the executable @file{qemu-system-arm} to simulate a ARM -machine. The ARM Integrator/CP board is emulated with the following -devices: - -@itemize @minus -@item -ARM926E, ARM1026E, ARM946E, ARM1136 or Cortex-A8 CPU -@item -Two PL011 UARTs -@item -SMC 91c111 Ethernet adapter -@item -PL110 LCD controller -@item -PL050 KMI with PS/2 keyboard and mouse. -@item -PL181 MultiMedia Card Interface with SD card. -@end itemize - -The ARM Versatile baseboard is emulated with the following devices: - -@itemize @minus -@item -ARM926E, ARM1136 or Cortex-A8 CPU -@item -PL190 Vectored Interrupt Controller -@item -Four PL011 UARTs -@item -SMC 91c111 Ethernet adapter -@item -PL110 LCD controller -@item -PL050 KMI with PS/2 keyboard and mouse. -@item -PCI host bridge. Note the emulated PCI bridge only provides access to -PCI memory space. It does not provide access to PCI IO space. -This means some devices (eg. ne2k_pci NIC) are not usable, and others -(eg. rtl8139 NIC) are only usable when the guest drivers use the memory -mapped control registers. -@item -PCI OHCI USB controller. -@item -LSI53C895A PCI SCSI Host Bus Adapter with hard disk and CD-ROM devices. -@item -PL181 MultiMedia Card Interface with SD card. -@end itemize - -Several variants of the ARM RealView baseboard are emulated, -including the EB, PB-A8 and PBX-A9. Due to interactions with the -bootloader, only certain Linux kernel configurations work out -of the box on these boards. - -Kernels for the PB-A8 board should have CONFIG_REALVIEW_HIGH_PHYS_OFFSET -enabled in the kernel, and expect 512M RAM. Kernels for The PBX-A9 board -should have CONFIG_SPARSEMEM enabled, CONFIG_REALVIEW_HIGH_PHYS_OFFSET -disabled and expect 1024M RAM. - -The following devices are emulated: - -@itemize @minus -@item -ARM926E, ARM1136, ARM11MPCore, Cortex-A8 or Cortex-A9 MPCore CPU -@item -ARM AMBA Generic/Distributed Interrupt Controller -@item -Four PL011 UARTs -@item -SMC 91c111 or SMSC LAN9118 Ethernet adapter -@item -PL110 LCD controller -@item -PL050 KMI with PS/2 keyboard and mouse -@item -PCI host bridge -@item -PCI OHCI USB controller -@item -LSI53C895A PCI SCSI Host Bus Adapter with hard disk and CD-ROM devices -@item -PL181 MultiMedia Card Interface with SD card. -@end itemize - -The XScale-based clamshell PDA models ("Spitz", "Akita", "Borzoi" -and "Terrier") emulation includes the following peripherals: - -@itemize @minus -@item -Intel PXA270 System-on-chip (ARM V5TE core) -@item -NAND Flash memory -@item -IBM/Hitachi DSCM microdrive in a PXA PCMCIA slot - not in "Akita" -@item -On-chip OHCI USB controller -@item -On-chip LCD controller -@item -On-chip Real Time Clock -@item -TI ADS7846 touchscreen controller on SSP bus -@item -Maxim MAX1111 analog-digital converter on I@math{^2}C bus -@item -GPIO-connected keyboard controller and LEDs -@item -Secure Digital card connected to PXA MMC/SD host -@item -Three on-chip UARTs -@item -WM8750 audio CODEC on I@math{^2}C and I@math{^2}S busses -@end itemize - -The Palm Tungsten|E PDA (codename "Cheetah") emulation includes the -following elements: - -@itemize @minus -@item -Texas Instruments OMAP310 System-on-chip (ARM 925T core) -@item -ROM and RAM memories (ROM firmware image can be loaded with -option-rom) -@item -On-chip LCD controller -@item -On-chip Real Time Clock -@item -TI TSC2102i touchscreen controller / analog-digital converter / Audio -CODEC, connected through MicroWire and I@math{^2}S busses -@item -GPIO-connected matrix keypad -@item -Secure Digital card connected to OMAP MMC/SD host -@item -Three on-chip UARTs -@end itemize - -Nokia N800 and N810 internet tablets (known also as RX-34 and RX-44 / 48) -emulation supports the following elements: - -@itemize @minus -@item -Texas Instruments OMAP2420 System-on-chip (ARM 1136 core) -@item -RAM and non-volatile OneNAND Flash memories -@item -Display connected to EPSON remote framebuffer chip and OMAP on-chip -display controller and a LS041y3 MIPI DBI-C controller -@item -TI TSC2301 (in N800) and TI TSC2005 (in N810) touchscreen controllers -driven through SPI bus -@item -National Semiconductor LM8323-controlled qwerty keyboard driven -through I@math{^2}C bus -@item -Secure Digital card connected to OMAP MMC/SD host -@item -Three OMAP on-chip UARTs and on-chip STI debugging console -@item -A Bluetooth(R) transceiver and HCI connected to an UART -@item -Mentor Graphics "Inventra" dual-role USB controller embedded in a TI -TUSB6010 chip - only USB host mode is supported -@item -TI TMP105 temperature sensor driven through I@math{^2}C bus -@item -TI TWL92230C power management companion with an RTC on I@math{^2}C bus -@item -Nokia RETU and TAHVO multi-purpose chips with an RTC, connected -through CBUS -@end itemize - -The Luminary Micro Stellaris LM3S811EVB emulation includes the following -devices: - -@itemize @minus -@item -Cortex-M3 CPU core. -@item -64k Flash and 8k SRAM. -@item -Timers, UARTs, ADC and I@math{^2}C interface. -@item -OSRAM Pictiva 96x16 OLED with SSD0303 controller on I@math{^2}C bus. -@end itemize - -The Luminary Micro Stellaris LM3S6965EVB emulation includes the following -devices: - -@itemize @minus -@item -Cortex-M3 CPU core. -@item -256k Flash and 64k SRAM. -@item -Timers, UARTs, ADC, I@math{^2}C and SSI interfaces. -@item -OSRAM Pictiva 128x64 OLED with SSD0323 controller connected via SSI. -@end itemize - -The Freecom MusicPal internet radio emulation includes the following -elements: - -@itemize @minus -@item -Marvell MV88W8618 ARM core. -@item -32 MB RAM, 256 KB SRAM, 8 MB flash. -@item -Up to 2 16550 UARTs -@item -MV88W8xx8 Ethernet controller -@item -MV88W8618 audio controller, WM8750 CODEC and mixer -@item -128×64 display with brightness control -@item -2 buttons, 2 navigation wheels with button function -@end itemize - -The Siemens SX1 models v1 and v2 (default) basic emulation. -The emulation includes the following elements: - -@itemize @minus -@item -Texas Instruments OMAP310 System-on-chip (ARM 925T core) -@item -ROM and RAM memories (ROM firmware image can be loaded with -pflash) -V1 -1 Flash of 16MB and 1 Flash of 8MB -V2 -1 Flash of 32MB -@item -On-chip LCD controller -@item -On-chip Real Time Clock -@item -Secure Digital card connected to OMAP MMC/SD host -@item -Three on-chip UARTs -@end itemize - -A Linux 2.6 test image is available on the QEMU web site. More -information is available in the QEMU mailing-list archive. - -@c man begin OPTIONS - -The following options are specific to the ARM emulation: - -@table @option - -@item -semihosting -Enable semihosting syscall emulation. - -On ARM this implements the "Angel" interface. - -Note that this allows guest direct access to the host filesystem, -so should only be used with trusted guest OS. - -@end table - -@c man end - -@node ColdFire System emulator -@section ColdFire System emulator -@cindex system emulation (ColdFire) -@cindex system emulation (M68K) - -Use the executable @file{qemu-system-m68k} to simulate a ColdFire machine. -The emulator is able to boot a uClinux kernel. - -The M5208EVB emulation includes the following devices: - -@itemize @minus -@item -MCF5208 ColdFire V2 Microprocessor (ISA A+ with EMAC). -@item -Three Two on-chip UARTs. -@item -Fast Ethernet Controller (FEC) -@end itemize - -The AN5206 emulation includes the following devices: - -@itemize @minus -@item -MCF5206 ColdFire V2 Microprocessor. -@item -Two on-chip UARTs. -@end itemize - -@c man begin OPTIONS - -The following options are specific to the ColdFire emulation: - -@table @option - -@item -semihosting -Enable semihosting syscall emulation. - -On M68K this implements the "ColdFire GDB" interface used by libgloss. - -Note that this allows guest direct access to the host filesystem, -so should only be used with trusted guest OS. - -@end table - -@c man end - -@node Cris System emulator -@section Cris System emulator -@cindex system emulation (Cris) - -TODO - -@node Microblaze System emulator -@section Microblaze System emulator -@cindex system emulation (Microblaze) - -TODO - -@node SH4 System emulator -@section SH4 System emulator -@cindex system emulation (SH4) - -TODO - -@node Xtensa System emulator -@section Xtensa System emulator -@cindex system emulation (Xtensa) - -Two executables cover simulation of both Xtensa endian options, -@file{qemu-system-xtensa} and @file{qemu-system-xtensaeb}. -Two different machine types are emulated: - -@itemize @minus -@item -Xtensa emulator pseudo board "sim" -@item -Avnet LX60/LX110/LX200 board -@end itemize - -The sim pseudo board emulation provides an environment similar -to one provided by the proprietary Tensilica ISS. -It supports: - -@itemize @minus -@item -A range of Xtensa CPUs, default is the DC232B -@item -Console and filesystem access via semihosting calls -@end itemize - -The Avnet LX60/LX110/LX200 emulation supports: - -@itemize @minus -@item -A range of Xtensa CPUs, default is the DC232B -@item -16550 UART -@item -OpenCores 10/100 Mbps Ethernet MAC -@end itemize - -@c man begin OPTIONS - -The following options are specific to the Xtensa emulation: - -@table @option - -@item -semihosting -Enable semihosting syscall emulation. - -Xtensa semihosting provides basic file IO calls, such as open/read/write/seek/select. -Tensilica baremetal libc for ISS and linux platform "sim" use this interface. - -Note that this allows guest direct access to the host filesystem, -so should only be used with trusted guest OS. - -@end table - -@c man end - -@node QEMU User space emulator -@chapter QEMU User space emulator - -@menu -* Supported Operating Systems :: -* Features:: -* Linux User space emulator:: -* BSD User space emulator :: -@end menu - -@node Supported Operating Systems -@section Supported Operating Systems - -The following OS are supported in user space emulation: - -@itemize @minus -@item -Linux (referred as qemu-linux-user) -@item -BSD (referred as qemu-bsd-user) -@end itemize - -@node Features -@section Features - -QEMU user space emulation has the following notable features: - -@table @strong -@item System call translation: -QEMU includes a generic system call translator. This means that -the parameters of the system calls can be converted to fix -endianness and 32/64-bit mismatches between hosts and targets. -IOCTLs can be converted too. - -@item POSIX signal handling: -QEMU can redirect to the running program all signals coming from -the host (such as @code{SIGALRM}), as well as synthesize signals from -virtual CPU exceptions (for example @code{SIGFPE} when the program -executes a division by zero). - -QEMU relies on the host kernel to emulate most signal system -calls, for example to emulate the signal mask. On Linux, QEMU -supports both normal and real-time signals. - -@item Threading: -On Linux, QEMU can emulate the @code{clone} syscall and create a real -host thread (with a separate virtual CPU) for each emulated thread. -Note that not all targets currently emulate atomic operations correctly. -x86 and ARM use a global lock in order to preserve their semantics. -@end table - -QEMU was conceived so that ultimately it can emulate itself. Although -it is not very useful, it is an important test to show the power of the -emulator. - -@node Linux User space emulator -@section Linux User space emulator - -@menu -* Quick Start:: -* Wine launch:: -* Command line options:: -* Other binaries:: -@end menu - -@node Quick Start -@subsection Quick Start - -In order to launch a Linux process, QEMU needs the process executable -itself and all the target (x86) dynamic libraries used by it. - -@itemize - -@item On x86, you can just try to launch any process by using the native -libraries: - -@example -qemu-i386 -L / /bin/ls -@end example - -@code{-L /} tells that the x86 dynamic linker must be searched with a -@file{/} prefix. - -@item Since QEMU is also a linux process, you can launch QEMU with -QEMU (NOTE: you can only do that if you compiled QEMU from the sources): - -@example -qemu-i386 -L / qemu-i386 -L / /bin/ls -@end example - -@item On non x86 CPUs, you need first to download at least an x86 glibc -(@file{qemu-runtime-i386-XXX-.tar.gz} on the QEMU web page). Ensure that -@code{LD_LIBRARY_PATH} is not set: - -@example -unset LD_LIBRARY_PATH -@end example - -Then you can launch the precompiled @file{ls} x86 executable: - -@example -qemu-i386 tests/i386/ls -@end example -You can look at @file{scripts/qemu-binfmt-conf.sh} so that -QEMU is automatically launched by the Linux kernel when you try to -launch x86 executables. It requires the @code{binfmt_misc} module in the -Linux kernel. - -@item The x86 version of QEMU is also included. You can try weird things such as: -@example -qemu-i386 /usr/local/qemu-i386/bin/qemu-i386 \ - /usr/local/qemu-i386/bin/ls-i386 -@end example - -@end itemize - -@node Wine launch -@subsection Wine launch - -@itemize - -@item Ensure that you have a working QEMU with the x86 glibc -distribution (see previous section). In order to verify it, you must be -able to do: - -@example -qemu-i386 /usr/local/qemu-i386/bin/ls-i386 -@end example - -@item Download the binary x86 Wine install -(@file{qemu-XXX-i386-wine.tar.gz} on the QEMU web page). - -@item Configure Wine on your account. Look at the provided script -@file{/usr/local/qemu-i386/@/bin/wine-conf.sh}. Your previous -@code{$@{HOME@}/.wine} directory is saved to @code{$@{HOME@}/.wine.org}. - -@item Then you can try the example @file{putty.exe}: - -@example -qemu-i386 /usr/local/qemu-i386/wine/bin/wine \ - /usr/local/qemu-i386/wine/c/Program\ Files/putty.exe -@end example - -@end itemize - -@node Command line options -@subsection Command line options - -@example -@command{qemu-i386} [@option{-h]} [@option{-d]} [@option{-L} @var{path}] [@option{-s} @var{size}] [@option{-cpu} @var{model}] [@option{-g} @var{port}] [@option{-B} @var{offset}] [@option{-R} @var{size}] @var{program} [@var{arguments}...] -@end example - -@table @option -@item -h -Print the help -@item -L path -Set the x86 elf interpreter prefix (default=/usr/local/qemu-i386) -@item -s size -Set the x86 stack size in bytes (default=524288) -@item -cpu model -Select CPU model (-cpu help for list and additional feature selection) -@item -E @var{var}=@var{value} -Set environment @var{var} to @var{value}. -@item -U @var{var} -Remove @var{var} from the environment. -@item -B offset -Offset guest address by the specified number of bytes. This is useful when -the address region required by guest applications is reserved on the host. -This option is currently only supported on some hosts. -@item -R size -Pre-allocate a guest virtual address space of the given size (in bytes). -"G", "M", and "k" suffixes may be used when specifying the size. -@end table - -Debug options: - -@table @option -@item -d item1,... -Activate logging of the specified items (use '-d help' for a list of log items) -@item -p pagesize -Act as if the host page size was 'pagesize' bytes -@item -g port -Wait gdb connection to port -@item -singlestep -Run the emulation in single step mode. -@end table - -Environment variables: - -@table @env -@item QEMU_STRACE -Print system calls and arguments similar to the 'strace' program -(NOTE: the actual 'strace' program will not work because the user -space emulator hasn't implemented ptrace). At the moment this is -incomplete. All system calls that don't have a specific argument -format are printed with information for six arguments. Many -flag-style arguments don't have decoders and will show up as numbers. -@end table - -@node Other binaries -@subsection Other binaries - -@cindex user mode (Alpha) -@command{qemu-alpha} TODO. - -@cindex user mode (ARM) -@command{qemu-armeb} TODO. - -@cindex user mode (ARM) -@command{qemu-arm} is also capable of running ARM "Angel" semihosted ELF -binaries (as implemented by the arm-elf and arm-eabi Newlib/GDB -configurations), and arm-uclinux bFLT format binaries. - -@cindex user mode (ColdFire) -@cindex user mode (M68K) -@command{qemu-m68k} is capable of running semihosted binaries using the BDM -(m5xxx-ram-hosted.ld) or m68k-sim (sim.ld) syscall interfaces, and -coldfire uClinux bFLT format binaries. - -The binary format is detected automatically. - -@cindex user mode (Cris) -@command{qemu-cris} TODO. - -@cindex user mode (i386) -@command{qemu-i386} TODO. -@command{qemu-x86_64} TODO. - -@cindex user mode (Microblaze) -@command{qemu-microblaze} TODO. - -@cindex user mode (MIPS) -@command{qemu-mips} executes 32-bit big endian MIPS binaries (MIPS O32 ABI). - -@command{qemu-mipsel} executes 32-bit little endian MIPS binaries (MIPS O32 ABI). - -@command{qemu-mips64} executes 64-bit big endian MIPS binaries (MIPS N64 ABI). - -@command{qemu-mips64el} executes 64-bit little endian MIPS binaries (MIPS N64 ABI). - -@command{qemu-mipsn32} executes 32-bit big endian MIPS binaries (MIPS N32 ABI). - -@command{qemu-mipsn32el} executes 32-bit little endian MIPS binaries (MIPS N32 ABI). - -@cindex user mode (NiosII) -@command{qemu-nios2} TODO. - -@cindex user mode (PowerPC) -@command{qemu-ppc64abi32} TODO. -@command{qemu-ppc64} TODO. -@command{qemu-ppc} TODO. - -@cindex user mode (SH4) -@command{qemu-sh4eb} TODO. -@command{qemu-sh4} TODO. - -@cindex user mode (SPARC) -@command{qemu-sparc} can execute Sparc32 binaries (Sparc32 CPU, 32 bit ABI). - -@command{qemu-sparc32plus} can execute Sparc32 and SPARC32PLUS binaries -(Sparc64 CPU, 32 bit ABI). - -@command{qemu-sparc64} can execute some Sparc64 (Sparc64 CPU, 64 bit ABI) and -SPARC32PLUS binaries (Sparc64 CPU, 32 bit ABI). - -@node BSD User space emulator -@section BSD User space emulator - -@menu -* BSD Status:: -* BSD Quick Start:: -* BSD Command line options:: -@end menu - -@node BSD Status -@subsection BSD Status - -@itemize @minus -@item -target Sparc64 on Sparc64: Some trivial programs work. -@end itemize - -@node BSD Quick Start -@subsection Quick Start - -In order to launch a BSD process, QEMU needs the process executable -itself and all the target dynamic libraries used by it. - -@itemize - -@item On Sparc64, you can just try to launch any process by using the native -libraries: - -@example -qemu-sparc64 /bin/ls -@end example - -@end itemize - -@node BSD Command line options -@subsection Command line options - -@example -@command{qemu-sparc64} [@option{-h]} [@option{-d]} [@option{-L} @var{path}] [@option{-s} @var{size}] [@option{-bsd} @var{type}] @var{program} [@var{arguments}...] -@end example - -@table @option -@item -h -Print the help -@item -L path -Set the library root path (default=/) -@item -s size -Set the stack size in bytes (default=524288) -@item -ignore-environment -Start with an empty environment. Without this option, -the initial environment is a copy of the caller's environment. -@item -E @var{var}=@var{value} -Set environment @var{var} to @var{value}. -@item -U @var{var} -Remove @var{var} from the environment. -@item -bsd type -Set the type of the emulated BSD Operating system. Valid values are -FreeBSD, NetBSD and OpenBSD (default). -@end table - -Debug options: - -@table @option -@item -d item1,... -Activate logging of the specified items (use '-d help' for a list of log items) -@item -p pagesize -Act as if the host page size was 'pagesize' bytes -@item -singlestep -Run the emulation in single step mode. -@end table - -@node System requirements -@chapter System requirements - -@section KVM kernel module - -On x86_64 hosts, the default set of CPU features enabled by the KVM accelerator -require the host to be running Linux v4.5 or newer. - -The OpteronG[345] CPU models require KVM support for RDTSCP, which was -added with Linux 4.5 which is supported by the major distros. And even -if RHEL7 has kernel 3.10, KVM there has the required functionality there -to make it close to a 4.5 or newer kernel. - -@include docs/security.texi - -@include qemu-tech.texi - -@include qemu-deprecated.texi - -@node Supported build platforms -@appendix Supported build platforms - -QEMU aims to support building and executing on multiple host OS platforms. -This appendix outlines which platforms are the major build targets. These -platforms are used as the basis for deciding upon the minimum required -versions of 3rd party software QEMU depends on. The supported platforms -are the targets for automated testing performed by the project when patches -are submitted for review, and tested before and after merge. - -If a platform is not listed here, it does not imply that QEMU won't work. -If an unlisted platform has comparable software versions to a listed platform, -there is every expectation that it will work. Bug reports are welcome for -problems encountered on unlisted platforms unless they are clearly older -vintage than what is described here. - -Note that when considering software versions shipped in distros as support -targets, QEMU considers only the version number, and assumes the features in -that distro match the upstream release with the same version. In other words, -if a distro backports extra features to the software in their distro, QEMU -upstream code will not add explicit support for those backports, unless the -feature is auto-detectable in a manner that works for the upstream releases -too. - -The Repology site @url{https://repology.org} is a useful resource to identify -currently shipped versions of software in various operating systems, though -it does not cover all distros listed below. - -@section Linux OS - -For distributions with frequent, short-lifetime releases, the project will -aim to support all versions that are not end of life by their respective -vendors. For the purposes of identifying supported software versions, the -project will look at Fedora, Ubuntu, and openSUSE distros. Other short- -lifetime distros will be assumed to ship similar software versions. - -For distributions with long-lifetime releases, the project will aim to support -the most recent major version at all times. Support for the previous major -version will be dropped 2 years after the new major version is released. For -the purposes of identifying supported software versions, the project will look -at RHEL, Debian, Ubuntu LTS, and SLES distros. Other long-lifetime distros will -be assumed to ship similar software versions. - -@section Windows - -The project supports building with current versions of the MinGW toolchain, -hosted on Linux. - -@section macOS - -The project supports building with the two most recent versions of macOS, with -the current homebrew package set available. - -@section FreeBSD - -The project aims to support the all the versions which are not end of life. - -@section NetBSD - -The project aims to support the most recent major version at all times. Support -for the previous major version will be dropped 2 years after the new major -version is released. - -@section OpenBSD - -The project aims to support the all the versions which are not end of life. - -@node License -@appendix License - -QEMU is a trademark of Fabrice Bellard. - -QEMU is released under the -@url{https://www.gnu.org/licenses/gpl-2.0.txt,GNU General Public License}, -version 2. Parts of QEMU have specific licenses, see file -@url{https://git.qemu.org/?p=qemu.git;a=blob_plain;f=LICENSE,LICENSE}. - -@node Index -@appendix Index -@menu -* Concept Index:: -* Function Index:: -* Keystroke Index:: -* Program Index:: -* Data Type Index:: -* Variable Index:: -@end menu - -@node Concept Index -@section Concept Index -This is the main index. Should we combine all keywords in one index? TODO -@printindex cp - -@node Function Index -@section Function Index -This index could be used for command line options and monitor functions. -@printindex fn - -@node Keystroke Index -@section Keystroke Index - -This is a list of all keystrokes which have a special function -in system emulation. - -@printindex ky - -@node Program Index -@section Program Index -@printindex pg - -@node Data Type Index -@section Data Type Index - -This index could be used for qdev device names and options. - -@printindex tp - -@node Variable Index -@section Variable Index -@printindex vr - -@bye diff --git a/qemu-img-cmds.hx b/qemu-img-cmds.hx index 1c93e6d185..c9c54de1df 100644 --- a/qemu-img-cmds.hx +++ b/qemu-img-cmds.hx @@ -1,102 +1,93 @@ HXCOMM Keep the list of subcommands sorted by name. HXCOMM Use DEFHEADING() to define headings in both help text and texi -HXCOMM Text between STEXI and ETEXI are copied to texi version and +HXCOMM Text between SRST and ERST are copied to rST version and HXCOMM discarded from C version HXCOMM DEF(command, callback, arg_string) is used to construct HXCOMM command structures and help message. -HXCOMM HXCOMM can be used for comments, discarded from both texi and C +HXCOMM HXCOMM can be used for comments, discarded from both rST and C -HXCOMM When amending the TEXI sections, please remember to copy the usage +HXCOMM When amending the rST sections, please remember to copy the usage HXCOMM over to the per-command sections in qemu-img.texi. -STEXI -@table @option -ETEXI - DEF("amend", img_amend, "amend [--object objectdef] [--image-opts] [-p] [-q] [-f fmt] [-t cache] -o options filename") -STEXI -@item amend [--object @var{objectdef}] [--image-opts] [-p] [-q] [-f @var{fmt}] [-t @var{cache}] -o @var{options} @var{filename} -ETEXI +SRST +.. option:: amend [--object OBJECTDEF] [--image-opts] [-p] [-q] [-f FMT] [-t CACHE] -o OPTIONS FILENAME +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] [-w] [-U] filename") -STEXI -@item bench [-c @var{count}] [-d @var{depth}] [-f @var{fmt}] [--flush-interval=@var{flush_interval}] [-n] [--no-drain] [-o @var{offset}] [--pattern=@var{pattern}] [-q] [-s @var{buffer_size}] [-S @var{step_size}] [-t @var{cache}] [-w] [-U] @var{filename} -ETEXI - + "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] [-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") -STEXI -@item check [--object @var{objectdef}] [--image-opts] [-q] [-f @var{fmt}] [--output=@var{ofmt}] [-r [leaks | all]] [-T @var{src_cache}] [-U] @var{filename} -ETEXI +SRST +.. option:: check [--object OBJECTDEF] [--image-opts] [-q] [-f FMT] [--output=OFMT] [-r [leaks | all]] [-T SRC_CACHE] [-U] FILENAME +ERST DEF("commit", img_commit, "commit [--object objectdef] [--image-opts] [-q] [-f fmt] [-t cache] [-b base] [-d] [-p] filename") -STEXI -@item commit [--object @var{objectdef}] [--image-opts] [-q] [-f @var{fmt}] [-t @var{cache}] [-b @var{base}] [-d] [-p] @var{filename} -ETEXI +SRST +.. option:: commit [--object OBJECTDEF] [--image-opts] [-q] [-f FMT] [-t CACHE] [-b BASE] [-d] [-p] FILENAME +ERST DEF("compare", img_compare, "compare [--object objectdef] [--image-opts] [-f fmt] [-F fmt] [-T src_cache] [-p] [-q] [-s] [-U] filename1 filename2") -STEXI -@item compare [--object @var{objectdef}] [--image-opts] [-f @var{fmt}] [-F @var{fmt}] [-T @var{src_cache}] [-p] [-q] [-s] [-U] @var{filename1} @var{filename2} -ETEXI +SRST +.. option:: compare [--object OBJECTDEF] [--image-opts] [-f FMT] [-F FMT] [-T SRC_CACHE] [-p] [-q] [-s] [-U] FILENAME1 FILENAME2 +ERST DEF("convert", img_convert, - "convert [--object objectdef] [--image-opts] [--target-image-opts] [-U] [-C] [-c] [-p] [-q] [-n] [-f fmt] [-t cache] [-T src_cache] [-O output_fmt] [-B backing_file] [-o options] [-l snapshot_param] [-S sparse_size] [-m num_coroutines] [-W] [--salvage] filename [filename2 [...]] output_filename") -STEXI -@item convert [--object @var{objectdef}] [--image-opts] [--target-image-opts] [-U] [-C] [-c] [-p] [-q] [-n] [-f @var{fmt}] [-t @var{cache}] [-T @var{src_cache}] [-O @var{output_fmt}] [-B @var{backing_file}] [-o @var{options}] [-l @var{snapshot_param}] [-S @var{sparse_size}] [-m @var{num_coroutines}] [-W] [--salvage] @var{filename} [@var{filename2} [...]] @var{output_filename} -ETEXI + "convert [--object objectdef] [--image-opts] [--target-image-opts] [--target-is-zero] [-U] [-C] [-c] [-p] [-q] [-n] [-f fmt] [-t cache] [-T src_cache] [-O output_fmt] [-B backing_file] [-o options] [-l snapshot_param] [-S sparse_size] [-m num_coroutines] [-W] [--salvage] filename [filename2 [...]] output_filename") +SRST +.. option:: convert [--object OBJECTDEF] [--image-opts] [--target-image-opts] [--target-is-zero] [-U] [-C] [-c] [-p] [-q] [-n] [-f FMT] [-t CACHE] [-T SRC_CACHE] [-O OUTPUT_FMT] [-B BACKING_FILE] [-o OPTIONS] [-l SNAPSHOT_PARAM] [-S SPARSE_SIZE] [-m NUM_COROUTINES] [-W] [--salvage] FILENAME [FILENAME2 [...]] OUTPUT_FILENAME +ERST DEF("create", img_create, "create [--object objectdef] [-q] [-f fmt] [-b backing_file] [-F backing_fmt] [-u] [-o options] filename [size]") -STEXI -@item create [--object @var{objectdef}] [-q] [-f @var{fmt}] [-b @var{backing_file}] [-F @var{backing_fmt}] [-u] [-o @var{options}] @var{filename} [@var{size}] -ETEXI +SRST +.. option:: create [--object OBJECTDEF] [-q] [-f FMT] [-b BACKING_FILE] [-F BACKING_FMT] [-u] [-o OPTIONS] FILENAME [SIZE] +ERST DEF("dd", img_dd, "dd [--image-opts] [-U] [-f fmt] [-O output_fmt] [bs=block_size] [count=blocks] [skip=blocks] if=input of=output") -STEXI -@item dd [--image-opts] [-U] [-f @var{fmt}] [-O @var{output_fmt}] [bs=@var{block_size}] [count=@var{blocks}] [skip=@var{blocks}] if=@var{input} of=@var{output} -ETEXI +SRST +.. option:: dd [--image-opts] [-U] [-f FMT] [-O OUTPUT_FMT] [bs=BLOCK_SIZE] [count=BLOCKS] [skip=BLOCKS] if=INPUT of=OUTPUT +ERST DEF("info", img_info, "info [--object objectdef] [--image-opts] [-f fmt] [--output=ofmt] [--backing-chain] [-U] filename") -STEXI -@item info [--object @var{objectdef}] [--image-opts] [-f @var{fmt}] [--output=@var{ofmt}] [--backing-chain] [-U] @var{filename} -ETEXI +SRST +.. option:: info [--object OBJECTDEF] [--image-opts] [-f FMT] [--output=OFMT] [--backing-chain] [-U] FILENAME +ERST DEF("map", img_map, "map [--object objectdef] [--image-opts] [-f fmt] [--output=ofmt] [-U] filename") -STEXI -@item map [--object @var{objectdef}] [--image-opts] [-f @var{fmt}] [--output=@var{ofmt}] [-U] @var{filename} -ETEXI +SRST +.. option:: map [--object OBJECTDEF] [--image-opts] [-f FMT] [--output=OFMT] [-U] FILENAME +ERST DEF("measure", img_measure, "measure [--output=ofmt] [-O output_fmt] [-o options] [--size N | [--object objectdef] [--image-opts] [-f fmt] [-l snapshot_param] filename]") -STEXI -@item measure [--output=@var{ofmt}] [-O @var{output_fmt}] [-o @var{options}] [--size @var{N} | [--object @var{objectdef}] [--image-opts] [-f @var{fmt}] [-l @var{snapshot_param}] @var{filename}] -ETEXI +SRST +.. option:: measure [--output=OFMT] [-O OUTPUT_FMT] [-o OPTIONS] [--size N | [--object OBJECTDEF] [--image-opts] [-f FMT] [-l SNAPSHOT_PARAM] FILENAME] +ERST DEF("snapshot", img_snapshot, "snapshot [--object objectdef] [--image-opts] [-U] [-q] [-l | -a snapshot | -c snapshot | -d snapshot] filename") -STEXI -@item snapshot [--object @var{objectdef}] [--image-opts] [-U] [-q] [-l | -a @var{snapshot} | -c @var{snapshot} | -d @var{snapshot}] @var{filename} -ETEXI +SRST +.. option:: snapshot [--object OBJECTDEF] [--image-opts] [-U] [-q] [-l | -a SNAPSHOT | -c SNAPSHOT | -d SNAPSHOT] FILENAME +ERST DEF("rebase", img_rebase, "rebase [--object objectdef] [--image-opts] [-U] [-q] [-f fmt] [-t cache] [-T src_cache] [-p] [-u] -b backing_file [-F backing_fmt] filename") -STEXI -@item rebase [--object @var{objectdef}] [--image-opts] [-U] [-q] [-f @var{fmt}] [-t @var{cache}] [-T @var{src_cache}] [-p] [-u] -b @var{backing_file} [-F @var{backing_fmt}] @var{filename} -ETEXI +SRST +.. option:: rebase [--object OBJECTDEF] [--image-opts] [-U] [-q] [-f FMT] [-t CACHE] [-T SRC_CACHE] [-p] [-u] -b BACKING_FILE [-F BACKING_FMT] FILENAME +ERST DEF("resize", img_resize, "resize [--object objectdef] [--image-opts] [-f fmt] [--preallocation=prealloc] [-q] [--shrink] filename [+ | -]size") -STEXI -@item resize [--object @var{objectdef}] [--image-opts] [-f @var{fmt}] [--preallocation=@var{prealloc}] [-q] [--shrink] @var{filename} [+ | -]@var{size} -ETEXI - -STEXI -@end table -ETEXI +SRST +.. option:: resize [--object OBJECTDEF] [--image-opts] [-f FMT] [--preallocation=PREALLOC] [-q] [--shrink] FILENAME [+ | -]SIZE +ERST diff --git a/qemu-img.c b/qemu-img.c index 95a24b9762..821cbf610e 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -70,6 +70,7 @@ enum { OPTION_PREALLOCATION = 265, OPTION_SHRINK = 266, OPTION_SALVAGE = 267, + OPTION_TARGET_IS_ZERO = 268, }; typedef enum OutputFormat { @@ -646,9 +647,9 @@ static int collect_image_check(BlockDriverState *bs, check->leaks = result.leaks; check->has_leaks = result.leaks != 0; check->corruptions_fixed = result.corruptions_fixed; - check->has_corruptions_fixed = result.corruptions != 0; + check->has_corruptions_fixed = result.corruptions_fixed != 0; check->leaks_fixed = result.leaks_fixed; - check->has_leaks_fixed = result.leaks != 0; + check->has_leaks_fixed = result.leaks_fixed != 0; check->image_end_offset = result.image_end_offset; check->has_image_end_offset = result.image_end_offset != 0; check->total_clusters = result.bfi.total_clusters; @@ -802,9 +803,12 @@ static int img_check(int argc, char **argv) if (check->corruptions_fixed || check->leaks_fixed) { int corruptions_fixed, leaks_fixed; + bool has_leaks_fixed, has_corruptions_fixed; leaks_fixed = check->leaks_fixed; + has_leaks_fixed = check->has_leaks_fixed; corruptions_fixed = check->corruptions_fixed; + has_corruptions_fixed = check->has_corruptions_fixed; if (output_format == OFORMAT_HUMAN) { qprintf(quiet, @@ -816,10 +820,14 @@ static int img_check(int argc, char **argv) check->corruptions_fixed); } + qapi_free_ImageCheck(check); + check = g_new0(ImageCheck, 1); ret = collect_image_check(bs, check, filename, fmt, 0); check->leaks_fixed = leaks_fixed; + check->has_leaks_fixed = has_leaks_fixed; check->corruptions_fixed = corruptions_fixed; + check->has_corruptions_fixed = has_corruptions_fixed; } if (!ret) { @@ -881,9 +889,9 @@ static void run_block_job(BlockJob *job, Error **errp) do { float progress = 0.0f; aio_poll(aio_context, true); - if (job->job.progress_total) { - progress = (float)job->job.progress_current / - job->job.progress_total * 100.f; + if (job->job.progress.total) { + progress = (float)job->job.progress.current / + job->job.progress.total * 100.f; } qemu_progress_print(progress, 0); } while (!job_is_ready(&job->job) && !job_is_completed(&job->job)); @@ -1916,8 +1924,8 @@ retry: if (status == BLK_DATA && !copy_range) { ret = convert_co_read(s, sector_num, n, buf); if (ret < 0) { - error_report("error while reading sector %" PRId64 - ": %s", sector_num, strerror(-ret)); + error_report("error while reading at byte %lld: %s", + sector_num * BDRV_SECTOR_SIZE, strerror(-ret)); s->ret = ret; } } else if (!s->min_sparse && status == BLK_ZERO) { @@ -1945,8 +1953,8 @@ retry: ret = convert_co_write(s, sector_num, n, buf, status); } if (ret < 0) { - error_report("error while writing sector %" PRId64 - ": %s", sector_num, strerror(-ret)); + error_report("error while writing at byte %lld: %s", + sector_num * BDRV_SECTOR_SIZE, strerror(-ret)); s->ret = ret; } } @@ -1984,10 +1992,9 @@ static int convert_do_copy(ImgConvertState *s) int64_t sector_num = 0; /* Check whether we have zero initialisation or can get it efficiently */ - if (s->target_is_new && s->min_sparse && !s->target_has_backing) { + if (!s->has_zero_init && s->target_is_new && s->min_sparse && + !s->target_has_backing) { s->has_zero_init = bdrv_has_zero_init(blk_bs(s->target)); - } else { - s->has_zero_init = false; } if (!s->has_zero_init && !s->target_has_backing && @@ -2086,6 +2093,7 @@ static int img_convert(int argc, char **argv) {"force-share", no_argument, 0, 'U'}, {"target-image-opts", no_argument, 0, OPTION_TARGET_IMAGE_OPTS}, {"salvage", no_argument, 0, OPTION_SALVAGE}, + {"target-is-zero", no_argument, 0, OPTION_TARGET_IS_ZERO}, {0, 0, 0, 0} }; c = getopt_long(argc, argv, ":hf:O:B:Cco:l:S:pt:T:qnm:WU", @@ -2209,6 +2217,14 @@ static int img_convert(int argc, char **argv) case OPTION_TARGET_IMAGE_OPTS: tgt_image_opts = true; break; + case OPTION_TARGET_IS_ZERO: + /* + * The user asserting that the target is blank has the + * same effect as the target driver supporting zero + * initialisation. + */ + s.has_zero_init = true; + break; } } @@ -2247,6 +2263,11 @@ static int img_convert(int argc, char **argv) warn_report("This will become an error in future QEMU versions."); } + if (s.has_zero_init && !skip_create) { + error_report("--target-is-zero requires use of -n flag"); + goto fail_getopt; + } + s.src_num = argc - optind - 1; out_filename = s.src_num >= 1 ? argv[argc - 1] : NULL; @@ -2380,6 +2401,12 @@ static int img_convert(int argc, char **argv) } s.target_has_backing = (bool) out_baseimg; + if (s.has_zero_init && s.target_has_backing) { + error_report("Cannot use --target-is-zero when the destination " + "image has a backing file"); + goto out; + } + if (s.src_num > 1 && out_baseimg) { error_report("Having a backing file for the target makes no sense when " "concatenating multiple input images"); @@ -2503,7 +2530,7 @@ static int img_convert(int argc, char **argv) } } - if (s.target_has_backing) { + if (s.target_has_backing && s.target_is_new) { /* Errors are treated as "backing length unknown" (which means * s.target_backing_sectors has to be negative, which it will * be automatically). The backing file length is used only @@ -2680,7 +2707,10 @@ static ImageInfoList *collect_image_info_list(bool image_opts, blk_unref(blk); + /* Clear parameters that only apply to the topmost image */ filename = fmt = NULL; + image_opts = false; + if (chain) { if (info->has_full_backing_filename) { filename = info->full_backing_filename; @@ -4184,7 +4214,8 @@ static int img_bench(int argc, char **argv) {"force-share", no_argument, 0, 'U'}, {0, 0, 0, 0} }; - c = getopt_long(argc, argv, ":hc:d:f:no:qs:S:t:wU", long_options, NULL); + c = getopt_long(argc, argv, ":hc:d:f:ni:o:qs:S:t:wU", long_options, + NULL); if (c == -1) { break; } @@ -4227,6 +4258,14 @@ static int img_bench(int argc, char **argv) case 'n': flags |= BDRV_O_NATIVE_AIO; break; + case 'i': + ret = bdrv_parse_aio(optarg, &flags); + if (ret < 0) { + error_report("Invalid aio option: %s", optarg); + ret = -1; + goto out; + } + break; case 'o': { offset = cvtnum(optarg); @@ -4900,10 +4939,8 @@ static int img_measure(int argc, char **argv) filename = argv[optind]; } - if (!filename && - (object_opts || image_opts || fmt || snapshot_name || sn_opts)) { - error_report("--object, --image-opts, -f, and -l " - "require a filename argument."); + if (!filename && (image_opts || fmt || snapshot_name || sn_opts)) { + error_report("--image-opts, -f, and -l require a filename argument."); goto out; } if (filename && img_size != UINT64_MAX) { diff --git a/qemu-img.texi b/qemu-img.texi deleted file mode 100644 index b5156d6316..0000000000 --- a/qemu-img.texi +++ /dev/null @@ -1,795 +0,0 @@ -@example -@c man begin SYNOPSIS -@command{qemu-img} [@var{standard} @var{options}] @var{command} [@var{command} @var{options}] -@c man end -@end example - -@c man begin DESCRIPTION -qemu-img allows you to create, convert and modify images offline. It can handle -all image formats supported by QEMU. - -@b{Warning:} Never use qemu-img to modify images in use by a running virtual -machine or any other process; this may destroy the image. Also, be aware that -querying an image that is being modified by another process may encounter -inconsistent state. -@c man end - -@c man begin OPTIONS - -Standard options: -@table @option -@item -h, --help -Display this help and exit -@item -V, --version -Display version information and exit -@item -T, --trace [[enable=]@var{pattern}][,events=@var{file}][,file=@var{file}] -@findex --trace -@include qemu-option-trace.texi -@end table - -The following commands are supported: - -@include qemu-img-cmds.texi - -Command parameters: -@table @var - -@item filename -is a disk image filename - -@item fmt -is the disk image format. It is guessed automatically in most cases. See below -for a description of the supported disk formats. - -@item size -is the disk image size in bytes. Optional suffixes @code{k} or @code{K} -(kilobyte, 1024) @code{M} (megabyte, 1024k) and @code{G} (gigabyte, 1024M) -and T (terabyte, 1024G) are supported. @code{b} is ignored. - -@item output_filename -is the destination disk image filename - -@item output_fmt -is the destination format - -@item options -is a comma separated list of format specific options in a -name=value format. Use @code{-o ?} for an overview of the options supported -by the used format or see the format descriptions below for details. - -@item snapshot_param -is param used for internal snapshot, format is -'snapshot.id=[ID],snapshot.name=[NAME]' or '[ID_OR_NAME]' - -@end table - -@table @option - -@item --object @var{objectdef} -is a QEMU user creatable object definition. See the @code{qemu(1)} manual -page for a description of the object properties. The most common object -type is a @code{secret}, which is used to supply passwords and/or encryption -keys. - -@item --image-opts -Indicates that the source @var{filename} parameter is to be interpreted as a -full option string, not a plain filename. This parameter is mutually -exclusive with the @var{-f} parameter. - -@item --target-image-opts -Indicates that the @var{output_filename} parameter(s) are to be interpreted as -a full option string, not a plain filename. This parameter is mutually -exclusive with the @var{-O} parameters. It is currently required to also use -the @var{-n} parameter to skip image creation. This restriction may be relaxed -in a future release. - -@item --force-share (-U) -If specified, @code{qemu-img} will open the image in shared mode, allowing -other QEMU processes to open it in write mode. For example, this can be used to -get the image information (with 'info' subcommand) when the image is used by a -running guest. Note that this could produce inconsistent results because of -concurrent metadata changes, etc. This option is only allowed when opening -images in read-only mode. - -@item --backing-chain -will enumerate information about backing files in a disk image chain. Refer -below for further description. - -@item -c -indicates that target image must be compressed (qcow format only) - -@item -h -with or without a command shows help and lists the supported formats - -@item -p -display progress bar (compare, convert and rebase commands only). -If the @var{-p} option is not used for a command that supports it, the -progress is reported when the process receives a @code{SIGUSR1} or -@code{SIGINFO} signal. - -@item -q -Quiet mode - do not print any output (except errors). There's no progress bar -in case both @var{-q} and @var{-p} options are used. - -@item -S @var{size} -indicates the consecutive number of bytes that must contain only zeros -for qemu-img to create a sparse image during conversion. This value is rounded -down to the nearest 512 bytes. You may use the common size suffixes like -@code{k} for kilobytes. - -@item -t @var{cache} -specifies the cache mode that should be used with the (destination) file. See -the documentation of the emulator's @code{-drive cache=...} option for allowed -values. - -@item -T @var{src_cache} -specifies the cache mode that should be used with the source file(s). See -the documentation of the emulator's @code{-drive cache=...} option for allowed -values. - -@end table - -Parameters to snapshot subcommand: - -@table @option - -@item snapshot -is the name of the snapshot to create, apply or delete -@item -a -applies a snapshot (revert disk to saved state) -@item -c -creates a snapshot -@item -d -deletes a snapshot -@item -l -lists all snapshots in the given image -@end table - -Parameters to compare subcommand: - -@table @option - -@item -f -First image format -@item -F -Second image format -@item -s -Strict mode - fail on different image size or sector allocation -@end table - -Parameters to convert subcommand: - -@table @option - -@item -n -Skip the creation of the target volume -@item -m -Number of parallel coroutines for the convert process -@item -W -Allow out-of-order writes to the destination. This option improves performance, -but is only recommended for preallocated devices like host devices or other -raw block devices. -@item -C -Try to use copy offloading to move data from source image to target. This may -improve performance if the data is remote, such as with NFS or iSCSI backends, -but will not automatically sparsify zero sectors, and may result in a fully -allocated target image depending on the host support for getting allocation -information. -@item --salvage -Try to ignore I/O errors when reading. Unless in quiet mode (@code{-q}), errors -will still be printed. Areas that cannot be read from the source will be -treated as containing only zeroes. -@end table - -Parameters to dd subcommand: - -@table @option - -@item bs=@var{block_size} -defines the block size -@item count=@var{blocks} -sets the number of input blocks to copy -@item if=@var{input} -sets the input file -@item of=@var{output} -sets the output file -@item skip=@var{blocks} -sets the number of input blocks to skip -@end table - -Command description: - -@table @option - -@item amend [--object @var{objectdef}] [--image-opts] [-p] [-q] [-f @var{fmt}] [-t @var{cache}] -o @var{options} @var{filename} - -Amends the image format specific @var{options} for the image file -@var{filename}. Not all file formats support this operation. - -@item bench [-c @var{count}] [-d @var{depth}] [-f @var{fmt}] [--flush-interval=@var{flush_interval}] [-n] [--no-drain] [-o @var{offset}] [--pattern=@var{pattern}] [-q] [-s @var{buffer_size}] [-S @var{step_size}] [-t @var{cache}] [-w] [-U] @var{filename} - -Run a simple sequential I/O benchmark on the specified image. If @code{-w} is -specified, a write test is performed, otherwise a read test is performed. - -A total number of @var{count} I/O requests is performed, each @var{buffer_size} -bytes in size, and with @var{depth} requests in parallel. The first request -starts at the position given by @var{offset}, each following request increases -the current position by @var{step_size}. If @var{step_size} is not given, -@var{buffer_size} is used for its value. - -If @var{flush_interval} is specified for a write test, the request queue is -drained and a flush is issued before new writes are made whenever the number of -remaining requests is a multiple of @var{flush_interval}. If additionally -@code{--no-drain} is specified, a flush is issued without draining the request -queue first. - -If @code{-n} is specified, the native AIO backend is used if possible. On -Linux, this option only works if @code{-t none} or @code{-t directsync} is -specified as well. - -For write tests, by default a buffer filled with zeros is written. This can be -overridden with a pattern byte specified by @var{pattern}. - -@item check [--object @var{objectdef}] [--image-opts] [-q] [-f @var{fmt}] [--output=@var{ofmt}] [-r [leaks | all]] [-T @var{src_cache}] [-U] @var{filename} - -Perform a consistency check on the disk image @var{filename}. The command can -output in the format @var{ofmt} which is either @code{human} or @code{json}. -The JSON output is an object of QAPI type @code{ImageCheck}. - -If @code{-r} is specified, qemu-img tries to repair any inconsistencies found -during the check. @code{-r leaks} repairs only cluster leaks, whereas -@code{-r all} fixes all kinds of errors, with a higher risk of choosing the -wrong fix or hiding corruption that has already occurred. - -Only the formats @code{qcow2}, @code{qed} and @code{vdi} support -consistency checks. - -In case the image does not have any inconsistencies, check exits with @code{0}. -Other exit codes indicate the kind of inconsistency found or if another error -occurred. The following table summarizes all exit codes of the check subcommand: - -@table @option - -@item 0 -Check completed, the image is (now) consistent -@item 1 -Check not completed because of internal errors -@item 2 -Check completed, image is corrupted -@item 3 -Check completed, image has leaked clusters, but is not corrupted -@item 63 -Checks are not supported by the image format - -@end table - -If @code{-r} is specified, exit codes representing the image state refer to the -state after (the attempt at) repairing it. That is, a successful @code{-r all} -will yield the exit code 0, independently of the image state before. - -@item commit [--object @var{objectdef}] [--image-opts] [-q] [-f @var{fmt}] [-t @var{cache}] [-b @var{base}] [-d] [-p] @var{filename} - -Commit the changes recorded in @var{filename} in its base image or backing file. -If the backing file is smaller than the snapshot, then the backing file will be -resized to be the same size as the snapshot. If the snapshot is smaller than -the backing file, the backing file will not be truncated. If you want the -backing file to match the size of the smaller snapshot, you can safely truncate -it yourself once the commit operation successfully completes. - -The image @var{filename} is emptied after the operation has succeeded. If you do -not need @var{filename} afterwards and intend to drop it, you may skip emptying -@var{filename} by specifying the @code{-d} flag. - -If the backing chain of the given image file @var{filename} has more than one -layer, the backing file into which the changes will be committed may be -specified as @var{base} (which has to be part of @var{filename}'s backing -chain). If @var{base} is not specified, the immediate backing file of the top -image (which is @var{filename}) will be used. Note that after a commit operation -all images between @var{base} and the top image will be invalid and may return -garbage data when read. For this reason, @code{-b} implies @code{-d} (so that -the top image stays valid). - -@item compare [--object @var{objectdef}] [--image-opts] [-f @var{fmt}] [-F @var{fmt}] [-T @var{src_cache}] [-p] [-q] [-s] [-U] @var{filename1} @var{filename2} - -Check if two images have the same content. You can compare images with -different format or settings. - -The format is probed unless you specify it by @var{-f} (used for -@var{filename1}) and/or @var{-F} (used for @var{filename2}) option. - -By default, images with different size are considered identical if the larger -image contains only unallocated and/or zeroed sectors in the area after the end -of the other image. In addition, if any sector is not allocated in one image -and contains only zero bytes in the second one, it is evaluated as equal. You -can use Strict mode by specifying the @var{-s} option. When compare runs in -Strict mode, it fails in case image size differs or a sector is allocated in -one image and is not allocated in the second one. - -By default, compare prints out a result message. This message displays -information that both images are same or the position of the first different -byte. In addition, result message can report different image size in case -Strict mode is used. - -Compare exits with @code{0} in case the images are equal and with @code{1} -in case the images differ. Other exit codes mean an error occurred during -execution and standard error output should contain an error message. -The following table sumarizes all exit codes of the compare subcommand: - -@table @option - -@item 0 -Images are identical -@item 1 -Images differ -@item 2 -Error on opening an image -@item 3 -Error on checking a sector allocation -@item 4 -Error on reading data - -@end table - -@item convert [--object @var{objectdef}] [--image-opts] [--target-image-opts] [-U] [-C] [-c] [-p] [-q] [-n] [-f @var{fmt}] [-t @var{cache}] [-T @var{src_cache}] [-O @var{output_fmt}] [-B @var{backing_file}] [-o @var{options}] [-l @var{snapshot_param}] [-S @var{sparse_size}] [-m @var{num_coroutines}] [-W] @var{filename} [@var{filename2} [...]] @var{output_filename} - -Convert the disk image @var{filename} or a snapshot @var{snapshot_param} -to disk image @var{output_filename} using format @var{output_fmt}. It can be optionally compressed (@code{-c} -option) or use any format specific options like encryption (@code{-o} option). - -Only the formats @code{qcow} and @code{qcow2} support compression. The -compression is read-only. It means that if a compressed sector is -rewritten, then it is rewritten as uncompressed data. - -Image conversion is also useful to get smaller image when using a -growable format such as @code{qcow}: the empty sectors are detected and -suppressed from the destination image. - -@var{sparse_size} indicates the consecutive number of bytes (defaults to 4k) -that must contain only zeros for qemu-img to create a sparse image during -conversion. If @var{sparse_size} is 0, the source will not be scanned for -unallocated or zero sectors, and the destination image will always be -fully allocated. - -You can use the @var{backing_file} option to force the output image to be -created as a copy on write image of the specified base image; the -@var{backing_file} should have the same content as the input's base image, -however the path, image format, etc may differ. - -If a relative path name is given, the backing file is looked up relative to -the directory containing @var{output_filename}. - -If the @code{-n} option is specified, the target volume creation will be -skipped. This is useful for formats such as @code{rbd} if the target -volume has already been created with site specific options that cannot -be supplied through qemu-img. - -Out of order writes can be enabled with @code{-W} to improve performance. -This is only recommended for preallocated devices like host devices or other -raw block devices. Out of order write does not work in combination with -creating compressed images. - -@var{num_coroutines} specifies how many coroutines work in parallel during -the convert process (defaults to 8). - -@item create [--object @var{objectdef}] [-q] [-f @var{fmt}] [-b @var{backing_file}] [-F @var{backing_fmt}] [-u] [-o @var{options}] @var{filename} [@var{size}] - -Create the new disk image @var{filename} of size @var{size} and format -@var{fmt}. Depending on the file format, you can add one or more @var{options} -that enable additional features of this format. - -If the option @var{backing_file} is specified, then the image will record -only the differences from @var{backing_file}. No size needs to be specified in -this case. @var{backing_file} will never be modified unless you use the -@code{commit} monitor command (or qemu-img commit). - -If a relative path name is given, the backing file is looked up relative to -the directory containing @var{filename}. - -Note that a given backing file will be opened to check that it is valid. Use -the @code{-u} option to enable unsafe backing file mode, which means that the -image will be created even if the associated backing file cannot be opened. A -matching backing file must be created or additional options be used to make the -backing file specification valid when you want to use an image created this -way. - -The size can also be specified using the @var{size} option with @code{-o}, -it doesn't need to be specified separately in this case. - -@item dd [--image-opts] [-U] [-f @var{fmt}] [-O @var{output_fmt}] [bs=@var{block_size}] [count=@var{blocks}] [skip=@var{blocks}] if=@var{input} of=@var{output} - -Dd copies from @var{input} file to @var{output} file converting it from -@var{fmt} format to @var{output_fmt} format. - -The data is by default read and written using blocks of 512 bytes but can be -modified by specifying @var{block_size}. If count=@var{blocks} is specified -dd will stop reading input after reading @var{blocks} input blocks. - -The size syntax is similar to dd(1)'s size syntax. - -@item info [--object @var{objectdef}] [--image-opts] [-f @var{fmt}] [--output=@var{ofmt}] [--backing-chain] [-U] @var{filename} - -Give information about the disk image @var{filename}. Use it in -particular to know the size reserved on disk which can be different -from the displayed size. If VM snapshots are stored in the disk image, -they are displayed too. - -If a disk image has a backing file chain, information about each disk image in -the chain can be recursively enumerated by using the option @code{--backing-chain}. - -For instance, if you have an image chain like: - -@example -base.qcow2 <- snap1.qcow2 <- snap2.qcow2 -@end example - -To enumerate information about each disk image in the above chain, starting from top to base, do: - -@example -qemu-img info --backing-chain snap2.qcow2 -@end example - -The command can output in the format @var{ofmt} which is either @code{human} or -@code{json}. The JSON output is an object of QAPI type @code{ImageInfo}; with -@code{--backing-chain}, it is an array of @code{ImageInfo} objects. - -@code{--output=human} reports the following information (for every image in the -chain): -@table @var -@item image -The image file name - -@item file format -The image format - -@item virtual size -The size of the guest disk - -@item disk size -How much space the image file occupies on the host file system (may be shown as -0 if this information is unavailable, e.g. because there is no file system) - -@item cluster_size -Cluster size of the image format, if applicable - -@item encrypted -Whether the image is encrypted (only present if so) - -@item cleanly shut down -This is shown as @code{no} if the image is dirty and will have to be -auto-repaired the next time it is opened in qemu. - -@item backing file -The backing file name, if present - -@item backing file format -The format of the backing file, if the image enforces it - -@item Snapshot list -A list of all internal snapshots - -@item Format specific information -Further information whose structure depends on the image format. This section -is a textual representation of the respective @code{ImageInfoSpecific*} QAPI -object (e.g. @code{ImageInfoSpecificQCow2} for qcow2 images). -@end table - -@item map [--object @var{objectdef}] [--image-opts] [-f @var{fmt}] [--output=@var{ofmt}] [-U] @var{filename} - -Dump the metadata of image @var{filename} and its backing file chain. -In particular, this commands dumps the allocation state of every sector -of @var{filename}, together with the topmost file that allocates it in -the backing file chain. - -Two option formats are possible. The default format (@code{human}) -only dumps known-nonzero areas of the file. Known-zero parts of the -file are omitted altogether, and likewise for parts that are not allocated -throughout the chain. @command{qemu-img} output will identify a file -from where the data can be read, and the offset in the file. Each line -will include four fields, the first three of which are hexadecimal -numbers. For example the first line of: -@example -Offset Length Mapped to File -0 0x20000 0x50000 /tmp/overlay.qcow2 -0x100000 0x10000 0x95380000 /tmp/backing.qcow2 -@end example -@noindent -means that 0x20000 (131072) bytes starting at offset 0 in the image are -available in /tmp/overlay.qcow2 (opened in @code{raw} format) starting -at offset 0x50000 (327680). Data that is compressed, encrypted, or -otherwise not available in raw format will cause an error if @code{human} -format is in use. Note that file names can include newlines, thus it is -not safe to parse this output format in scripts. - -The alternative format @code{json} will return an array of dictionaries -in JSON format. It will include similar information in -the @code{start}, @code{length}, @code{offset} fields; -it will also include other more specific information: -@itemize @minus -@item -whether the sectors contain actual data or not (boolean field @code{data}; -if false, the sectors are either unallocated or stored as optimized -all-zero clusters); - -@item -whether the data is known to read as zero (boolean field @code{zero}); - -@item -in order to make the output shorter, the target file is expressed as -a @code{depth}; for example, a depth of 2 refers to the backing file -of the backing file of @var{filename}. -@end itemize - -In JSON format, the @code{offset} field is optional; it is absent in -cases where @code{human} format would omit the entry or exit with an error. -If @code{data} is false and the @code{offset} field is present, the -corresponding sectors in the file are not yet in use, but they are -preallocated. - -For more information, consult @file{include/block/block.h} in QEMU's -source code. - -@item measure [--output=@var{ofmt}] [-O @var{output_fmt}] [-o @var{options}] [--size @var{N} | [--object @var{objectdef}] [--image-opts] [-f @var{fmt}] [-l @var{snapshot_param}] @var{filename}] - -Calculate the file size required for a new image. This information can be used -to size logical volumes or SAN LUNs appropriately for the image that will be -placed in them. The values reported are guaranteed to be large enough to fit -the image. The command can output in the format @var{ofmt} which is either -@code{human} or @code{json}. The JSON output is an object of QAPI type -@code{BlockMeasureInfo}. - -If the size @var{N} is given then act as if creating a new empty image file -using @command{qemu-img create}. If @var{filename} is given then act as if -converting an existing image file using @command{qemu-img convert}. The format -of the new file is given by @var{output_fmt} while the format of an existing -file is given by @var{fmt}. - -A snapshot in an existing image can be specified using @var{snapshot_param}. - -The following fields are reported: -@example -required size: 524288 -fully allocated size: 1074069504 -@end example - -The @code{required size} is the file size of the new image. It may be smaller -than the virtual disk size if the image format supports compact representation. - -The @code{fully allocated size} is the file size of the new image once data has -been written to all sectors. This is the maximum size that the image file can -occupy with the exception of internal snapshots, dirty bitmaps, vmstate data, -and other advanced image format features. - -@item snapshot [--object @var{objectdef}] [--image-opts] [-U] [-q] [-l | -a @var{snapshot} | -c @var{snapshot} | -d @var{snapshot}] @var{filename} - -List, apply, create or delete snapshots in image @var{filename}. - -@item rebase [--object @var{objectdef}] [--image-opts] [-U] [-q] [-f @var{fmt}] [-t @var{cache}] [-T @var{src_cache}] [-p] [-u] -b @var{backing_file} [-F @var{backing_fmt}] @var{filename} - -Changes the backing file of an image. Only the formats @code{qcow2} and -@code{qed} support changing the backing file. - -The backing file is changed to @var{backing_file} and (if the image format of -@var{filename} supports this) the backing file format is changed to -@var{backing_fmt}. If @var{backing_file} is specified as ``'' (the empty -string), then the image is rebased onto no backing file (i.e. it will exist -independently of any backing file). - -If a relative path name is given, the backing file is looked up relative to -the directory containing @var{filename}. - -@var{cache} specifies the cache mode to be used for @var{filename}, whereas -@var{src_cache} specifies the cache mode for reading backing files. - -There are two different modes in which @code{rebase} can operate: -@table @option -@item Safe mode -This is the default mode and performs a real rebase operation. The new backing -file may differ from the old one and qemu-img rebase will take care of keeping -the guest-visible content of @var{filename} unchanged. - -In order to achieve this, any clusters that differ between @var{backing_file} -and the old backing file of @var{filename} are merged into @var{filename} -before actually changing the backing file. - -Note that the safe mode is an expensive operation, comparable to converting -an image. It only works if the old backing file still exists. - -@item Unsafe mode -qemu-img uses the unsafe mode if @code{-u} is specified. In this mode, only the -backing file name and format of @var{filename} is changed without any checks -on the file contents. The user must take care of specifying the correct new -backing file, or the guest-visible content of the image will be corrupted. - -This mode is useful for renaming or moving the backing file to somewhere else. -It can be used without an accessible old backing file, i.e. you can use it to -fix an image whose backing file has already been moved/renamed. -@end table - -You can use @code{rebase} to perform a ``diff'' operation on two -disk images. This can be useful when you have copied or cloned -a guest, and you want to get back to a thin image on top of a -template or base image. - -Say that @code{base.img} has been cloned as @code{modified.img} by -copying it, and that the @code{modified.img} guest has run so there -are now some changes compared to @code{base.img}. To construct a thin -image called @code{diff.qcow2} that contains just the differences, do: - -@example -qemu-img create -f qcow2 -b modified.img diff.qcow2 -qemu-img rebase -b base.img diff.qcow2 -@end example - -At this point, @code{modified.img} can be discarded, since -@code{base.img + diff.qcow2} contains the same information. - -@item resize [--object @var{objectdef}] [--image-opts] [-f @var{fmt}] [--preallocation=@var{prealloc}] [-q] [--shrink] @var{filename} [+ | -]@var{size} - -Change the disk image as if it had been created with @var{size}. - -Before using this command to shrink a disk image, you MUST use file system and -partitioning tools inside the VM to reduce allocated file systems and partition -sizes accordingly. Failure to do so will result in data loss! - -When shrinking images, the @code{--shrink} option must be given. This informs -qemu-img that the user acknowledges all loss of data beyond the truncated -image's end. - -After using this command to grow a disk image, you must use file system and -partitioning tools inside the VM to actually begin using the new space on the -device. - -When growing an image, the @code{--preallocation} option may be used to specify -how the additional image area should be allocated on the host. See the format -description in the @code{NOTES} section which values are allowed. Using this -option may result in slightly more data being allocated than necessary. - -@end table -@c man end - -@ignore -@c man begin NOTES -Supported image file formats: - -@table @option -@item raw - -Raw disk image format (default). This format has the advantage of -being simple and easily exportable to all other emulators. If your -file system supports @emph{holes} (for example in ext2 or ext3 on -Linux or NTFS on Windows), then only the written sectors will reserve -space. Use @code{qemu-img info} to know the real size used by the -image or @code{ls -ls} on Unix/Linux. - -Supported options: -@table @code -@item preallocation -Preallocation mode (allowed values: @code{off}, @code{falloc}, @code{full}). -@code{falloc} mode preallocates space for image by calling posix_fallocate(). -@code{full} mode preallocates space for image by writing data to underlying -storage. This data may or may not be zero, depending on the storage location. -@end table - -@item qcow2 -QEMU image format, the most versatile format. Use it to have smaller -images (useful if your filesystem does not supports holes, for example -on Windows), optional AES encryption, zlib based compression and -support of multiple VM snapshots. - -Supported options: -@table @code -@item compat -Determines the qcow2 version to use. @code{compat=0.10} uses the -traditional image format that can be read by any QEMU since 0.10. -@code{compat=1.1} enables image format extensions that only QEMU 1.1 and -newer understand (this is the default). Amongst others, this includes zero -clusters, which allow efficient copy-on-read for sparse images. - -@item backing_file -File name of a base image (see @option{create} subcommand) -@item backing_fmt -Image format of the base image -@item encryption -If this option is set to @code{on}, the image is encrypted with 128-bit AES-CBC. - -The use of encryption in qcow and qcow2 images is considered to be flawed by -modern cryptography standards, suffering from a number of design problems: - -@itemize @minus -@item -The AES-CBC cipher is used with predictable initialization vectors based -on the sector number. This makes it vulnerable to chosen plaintext attacks -which can reveal the existence of encrypted data. -@item -The user passphrase is directly used as the encryption key. A poorly -chosen or short passphrase will compromise the security of the encryption. -@item -In the event of the passphrase being compromised there is no way to -change the passphrase to protect data in any qcow images. The files must -be cloned, using a different encryption passphrase in the new file. The -original file must then be securely erased using a program like shred, -though even this is ineffective with many modern storage technologies. -@item -Initialization vectors used to encrypt sectors are based on the -guest virtual sector number, instead of the host physical sector. When -a disk image has multiple internal snapshots this means that data in -multiple physical sectors is encrypted with the same initialization -vector. With the CBC mode, this opens the possibility of watermarking -attacks if the attack can collect multiple sectors encrypted with the -same IV and some predictable data. Having multiple qcow2 images with -the same passphrase also exposes this weakness since the passphrase -is directly used as the key. -@end itemize - -Use of qcow / qcow2 encryption is thus strongly discouraged. Users are -recommended to use an alternative encryption technology such as the -Linux dm-crypt / LUKS system. - -@item cluster_size -Changes the qcow2 cluster size (must be between 512 and 2M). Smaller cluster -sizes can improve the image file size whereas larger cluster sizes generally -provide better performance. - -@item preallocation -Preallocation mode (allowed values: @code{off}, @code{metadata}, @code{falloc}, -@code{full}). An image with preallocated metadata is initially larger but can -improve performance when the image needs to grow. @code{falloc} and @code{full} -preallocations are like the same options of @code{raw} format, but sets up -metadata also. - -@item lazy_refcounts -If this option is set to @code{on}, reference count updates are postponed with -the goal of avoiding metadata I/O and improving performance. This is -particularly interesting with @option{cache=writethrough} which doesn't batch -metadata updates. The tradeoff is that after a host crash, the reference count -tables must be rebuilt, i.e. on the next open an (automatic) @code{qemu-img -check -r all} is required, which may take some time. - -This option can only be enabled if @code{compat=1.1} is specified. - -@item nocow -If this option is set to @code{on}, it will turn off COW of the file. It's only -valid on btrfs, no effect on other file systems. - -Btrfs has low performance when hosting a VM image file, even more when the guest -on the VM also using btrfs as file system. Turning off COW is a way to mitigate -this bad performance. Generally there are two ways to turn off COW on btrfs: -a) Disable it by mounting with nodatacow, then all newly created files will be -NOCOW. b) For an empty file, add the NOCOW file attribute. That's what this option -does. - -Note: this option is only valid to new or empty files. If there is an existing -file which is COW and has data blocks already, it couldn't be changed to NOCOW -by setting @code{nocow=on}. One can issue @code{lsattr filename} to check if -the NOCOW flag is set or not (Capital 'C' is NOCOW flag). - -@end table - -@item Other -QEMU also supports various other image file formats for compatibility with -older QEMU versions or other hypervisors, including VMDK, VDI, VHD (vpc), VHDX, -qcow1 and QED. For a full list of supported formats see @code{qemu-img --help}. -For a more detailed description of these formats, see the QEMU Emulation User -Documentation. - -The main purpose of the block drivers for these formats is image conversion. -For running VMs, it is recommended to convert the disk images to either raw or -qcow2 in order to achieve good performance. -@end table - - -@c man end - -@setfilename qemu-img -@settitle QEMU disk image utility - -@c man begin SEEALSO -The HTML documentation of QEMU for more precise information and Linux -user mode emulator invocation. -@c man end - -@c man begin AUTHOR -Fabrice Bellard -@c man end - -@end ignore diff --git a/qemu-io.c b/qemu-io.c index 91e3276592..3adc5a7d0d 100644 --- a/qemu-io.c +++ b/qemu-io.c @@ -130,7 +130,8 @@ static void open_help(void) " -C, -- use copy-on-read\n" " -n, -- disable host cache, short for -t none\n" " -U, -- force shared permissions\n" -" -k, -- use kernel AIO implementation (on Linux only)\n" +" -k, -- use kernel AIO implementation (Linux only, prefer use of -i)\n" +" -i, -- use AIO mode (threads, native or io_uring)\n" " -t, -- use the given cache mode for the image\n" " -d, -- use the given discard mode for the image\n" " -o, -- options to be given to the block driver" @@ -172,7 +173,7 @@ static int open_f(BlockBackend *blk, int argc, char **argv) QDict *opts; bool force_share = false; - while ((c = getopt(argc, argv, "snCro:kt:d:U")) != -1) { + while ((c = getopt(argc, argv, "snCro:ki:t:d:U")) != -1) { switch (c) { case 's': flags |= BDRV_O_SNAPSHOT; @@ -204,6 +205,13 @@ static int open_f(BlockBackend *blk, int argc, char **argv) return -EINVAL; } break; + case 'i': + if (bdrv_parse_aio(optarg, &flags) < 0) { + error_report("Invalid aio option: %s", optarg); + qemu_opts_reset(&empty_opts); + return -EINVAL; + } + break; case 'o': if (imageOpts) { printf("--image-opts and 'open -o' are mutually exclusive\n"); @@ -291,7 +299,9 @@ static void usage(const char *name) " -n, --nocache disable host cache, short for -t none\n" " -C, --copy-on-read enable copy-on-read\n" " -m, --misalign misalign allocations for O_DIRECT\n" -" -k, --native-aio use kernel AIO implementation (on Linux only)\n" +" -k, --native-aio use kernel AIO implementation\n" +" (Linux only, prefer use of -i)\n" +" -i, --aio=MODE use AIO mode (threads, native or io_uring)\n" " -t, --cache=MODE use the given cache mode for the image\n" " -d, --discard=MODE use the given discard mode for the image\n" " -T, --trace [[enable=]][,events=][,file=]\n" @@ -496,7 +506,7 @@ static QemuOptsList file_opts = { int main(int argc, char **argv) { int readonly = 0; - const char *sopt = "hVc:d:f:rsnCmkt:T:U"; + const char *sopt = "hVc:d:f:rsnCmki:t:T:U"; const struct option lopt[] = { { "help", no_argument, NULL, 'h' }, { "version", no_argument, NULL, 'V' }, @@ -508,6 +518,7 @@ int main(int argc, char **argv) { "copy-on-read", no_argument, NULL, 'C' }, { "misalign", no_argument, NULL, 'm' }, { "native-aio", no_argument, NULL, 'k' }, + { "aio", required_argument, NULL, 'i' }, { "discard", required_argument, NULL, 'd' }, { "cache", required_argument, NULL, 't' }, { "trace", required_argument, NULL, 'T' }, @@ -575,6 +586,12 @@ int main(int argc, char **argv) case 'k': flags |= BDRV_O_NATIVE_AIO; break; + case 'i': + if (bdrv_parse_aio(optarg, &flags) < 0) { + error_report("Invalid aio option: %s", optarg); + exit(1); + } + break; case 't': if (bdrv_parse_cache_mode(optarg, &flags, &writethrough) < 0) { error_report("Invalid cache option: %s", optarg); diff --git a/qemu-nbd.c b/qemu-nbd.c index 108a51f7eb..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" @@ -135,7 +134,7 @@ static void usage(const char *name) " '[ID_OR_NAME]'\n" " -n, --nocache disable host cache\n" " --cache=MODE set cache mode (none, writeback, ...)\n" -" --aio=MODE set AIO mode (native or threads)\n" +" --aio=MODE set AIO mode (native, io_uring or threads)\n" " --discard=MODE set discard mode (ignore, unmap)\n" " --detect-zeroes=MODE set detect-zeroes mode (off, on, unmap)\n" " --image-opts treat FILE as a full set of image 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; @@ -726,13 +633,9 @@ int main(int argc, char **argv) exit(EXIT_FAILURE); } seen_aio = true; - if (!strcmp(optarg, "native")) { - flags |= BDRV_O_NATIVE_AIO; - } else if (!strcmp(optarg, "threads")) { - /* this is the default */ - } else { - error_report("invalid aio mode `%s'", optarg); - exit(EXIT_FAILURE); + if (bdrv_parse_aio(optarg, &flags) < 0) { + error_report("Invalid aio mode '%s'", optarg); + exit(EXIT_FAILURE); } break; case QEMU_NBD_OPT_DISCARD: @@ -793,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; @@ -898,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"); @@ -1162,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-nbd.texi b/qemu-nbd.texi deleted file mode 100644 index 7f55657722..0000000000 --- a/qemu-nbd.texi +++ /dev/null @@ -1,214 +0,0 @@ -@example -@c man begin SYNOPSIS -@command{qemu-nbd} [OPTION]... @var{filename} - -@command{qemu-nbd} @option{-L} [OPTION]... - -@command{qemu-nbd} @option{-d} @var{dev} -@c man end -@end example - -@c man begin DESCRIPTION - -Export a QEMU disk image using the NBD protocol. - -Other uses: -@itemize -@item -Bind a /dev/nbdX block device to a QEMU server (on Linux). -@item -As a client to query exports of a remote NBD server. -@end itemize - -@c man end - -@c man begin OPTIONS -@var{filename} is a disk image filename, or a set of block -driver options if @option{--image-opts} is specified. - -@var{dev} is an NBD device. - -@table @option -@item --object type,id=@var{id},...props... -Define a new instance of the @var{type} object class identified by @var{id}. -See the @code{qemu(1)} manual page for full details of the properties -supported. The common object types that it makes sense to define are the -@code{secret} object, which is used to supply passwords and/or encryption -keys, and the @code{tls-creds} object, which is used to supply TLS -credentials for the qemu-nbd server or client. -@item -p, --port=@var{port} -The TCP port to listen on as a server, or connect to as a client -(default @samp{10809}). -@item -o, --offset=@var{offset} -The offset into the image. -@item -b, --bind=@var{iface} -The interface to bind to as a server, or connect to as a client -(default @samp{0.0.0.0}). -@item -k, --socket=@var{path} -Use a unix socket with path @var{path}. -@item --image-opts -Treat @var{filename} as a set of image options, instead of a plain -filename. If this flag is specified, the @var{-f} flag should -not be used, instead the '@code{format=}' option should be set. -@item -f, --format=@var{fmt} -Force the use of the block driver for format @var{fmt} instead of -auto-detecting. -@item -r, --read-only -Export the disk as read-only. -@item -P, --partition=@var{num} -Deprecated: Only expose MBR partition @var{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. -@item -B, --bitmap=@var{name} -If @var{filename} has a qcow2 persistent bitmap @var{name}, expose -that bitmap via the ``qemu:dirty-bitmap:@var{name}'' context -accessible through NBD_OPT_SET_META_CONTEXT. -@item -s, --snapshot -Use @var{filename} as an external snapshot, create a temporary -file with backing_file=@var{filename}, redirect the write to -the temporary one. -@item -l, --load-snapshot=@var{snapshot_param} -Load an internal snapshot inside @var{filename} and export it -as an read-only device, @var{snapshot_param} format is -'snapshot.id=[ID],snapshot.name=[NAME]' or '[ID_OR_NAME]' -@item -n, --nocache -@itemx --cache=@var{cache} -The cache mode to be used with the file. See the documentation of -the emulator's @code{-drive cache=...} option for allowed values. -@item --aio=@var{aio} -Set the asynchronous I/O mode between @samp{threads} (the default) -and @samp{native} (Linux only). -@item --discard=@var{discard} -Control whether @dfn{discard} (also known as @dfn{trim} or @dfn{unmap}) -requests are ignored or passed to the filesystem. @var{discard} is one of -@samp{ignore} (or @samp{off}), @samp{unmap} (or @samp{on}). The default is -@samp{ignore}. -@item --detect-zeroes=@var{detect-zeroes} -Control the automatic conversion of plain zero writes by the OS to -driver-specific optimized zero write commands. @var{detect-zeroes} is one of -@samp{off}, @samp{on} or @samp{unmap}. @samp{unmap} -converts a zero write to an unmap operation and can only be used if -@var{discard} is set to @samp{unmap}. The default is @samp{off}. -@item -c, --connect=@var{dev} -Connect @var{filename} to NBD device @var{dev} (Linux only). -@item -d, --disconnect -Disconnect the device @var{dev} (Linux only). -@item -e, --shared=@var{num} -Allow up to @var{num} clients to share the device (default -@samp{1}). Safe for readers, but for now, consistency is not -guaranteed between multiple writers. -@item -t, --persistent -Don't exit on the last connection. -@item -x, --export-name=@var{name} -Set the NBD volume export name (default of a zero-length string). -@item -D, --description=@var{description} -Set the NBD volume export description, as a human-readable -string. -@item -L, --list -Connect as a client and list all details about the exports exposed by -a remote NBD server. This enables list mode, and is incompatible -with options that change behavior related to a specific export (such as -@option{--export-name}, @option{--offset}, ...). -@item --tls-creds=ID -Enable mandatory TLS encryption for the server by setting the ID -of the TLS credentials object previously created with the --object -option; or provide the credentials needed for connecting as a client -in list mode. -@item --fork -Fork off the server process and exit the parent once the server is running. -@item --pid-file=PATH -Store the server's process ID in the given file. -@item --tls-authz=ID -Specify the ID of a qauthz object previously created with the ---object option. This will be used to authorize connecting users -against their x509 distinguished name. -@item -v, --verbose -Display extra debugging information. -@item -h, --help -Display this help and exit. -@item -V, --version -Display version information and exit. -@item -T, --trace [[enable=]@var{pattern}][,events=@var{file}][,file=@var{file}] -@findex --trace -@include qemu-option-trace.texi -@end table - -@c man end - -@c man begin EXAMPLES -Start a server listening on port 10809 that exposes only the -guest-visible contents of a qcow2 file, with no TLS encryption, and -with the default export name (an empty string). The command is -one-shot, and will block until the first successful client -disconnects: - -@example -qemu-nbd -f qcow2 file.qcow2 -@end example - -Start a long-running server listening with encryption on port 10810, -and whitelist clients with a specific X.509 certificate to connect to -a 1 megabyte subset of a raw file, using the export name 'subset': - -@example -qemu-nbd \ - --object tls-creds-x509,id=tls0,endpoint=server,dir=/path/to/qemutls \ - --object 'authz-simple,id=auth0,identity=CN=laptop.example.com,,\ - O=Example Org,,L=London,,ST=London,,C=GB' \ - --tls-creds tls0 --tls-authz auth0 \ - -t -x subset -p 10810 \ - --image-opts driver=raw,offset=1M,size=1M,file.driver=file,file.filename=file.raw -@end example - -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: - -@example -qemu-nbd --fork --persistent --shared=5 --socket=/path/to/sock \ - --partition=1 --read-only --format=qcow2 file.qcow2 -@end example - -Expose the guest-visible contents of a qcow2 file via a block device -/dev/nbd0 (and possibly creating /dev/nbd0p1 and friends for -partitions found within), then disconnect the device when done. -Access to bind qemu-nbd to an /dev/nbd device generally requires root -privileges, and may also require the execution of @code{modprobe nbd} -to enable the kernel NBD client module. @emph{CAUTION}: Do not use -this method to mount filesystems from an untrusted guest image - a -malicious guest may have prepared the image to attempt to trigger -kernel bugs in partition probing or file system mounting. - -@example -qemu-nbd -c /dev/nbd0 -f qcow2 file.qcow2 -qemu-nbd -d /dev/nbd0 -@end example - -Query a remote server to see details about what export(s) it is -serving on port 10809, and authenticating via PSK: - -@example -qemu-nbd \ - --object tls-creds-psk,id=tls0,dir=/tmp/keys,username=eblake,endpoint=client \ - --tls-creds tls0 -L -b remote.example.com -@end example - -@c man end - -@ignore - -@setfilename qemu-nbd -@settitle QEMU Disk Network Block Device Server - -@c man begin AUTHOR -Copyright (C) 2006 Anthony Liguori . -This is free software; see the source for copying conditions. There is NO -warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -@c man end - -@c man begin SEEALSO -qemu(1), qemu-img(1) -@c man end - -@end ignore diff --git a/qemu-option-trace.texi b/qemu-option-trace.texi deleted file mode 100644 index 7d1b7f05c5..0000000000 --- a/qemu-option-trace.texi +++ /dev/null @@ -1,24 +0,0 @@ -Specify tracing options. - -@table @option -@item [enable=]@var{pattern} -Immediately enable events matching @var{pattern} -(either event name or a globbing pattern). This option is only -available if QEMU has been compiled with the @var{simple}, @var{log} -or @var{ftrace} tracing backend. To specify multiple events or patterns, -specify the @option{-trace} option multiple times. - -Use @code{-trace help} to print a list of names of trace points. - -@item events=@var{file} -Immediately enable events listed in @var{file}. -The file must contain one event name (as listed in the @file{trace-events-all} -file) per line; globbing patterns are accepted too. This option is only -available if QEMU has been compiled with the @var{simple}, @var{log} or -@var{ftrace} tracing backend. - -@item file=@var{file} -Log output traces to @var{file}. -This option is only available if QEMU has been compiled with -the @var{simple} tracing backend. -@end table diff --git a/qemu-options.hx b/qemu-options.hx index 65c9473b73..292d4e7c0c 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -1,141 +1,164 @@ -HXCOMM Use DEFHEADING() to define headings in both help text and texi -HXCOMM Text between STEXI and ETEXI are copied to texi version and -HXCOMM discarded from C version +HXCOMM Use DEFHEADING() to define headings in both help text and rST. +HXCOMM Text between SRST and ERST is copied to the rST version and +HXCOMM discarded from C version. HXCOMM DEF(option, HAS_ARG/0, opt_enum, opt_help, arch_mask) is used to HXCOMM construct option structures, enums and help message for specified HXCOMM architectures. -HXCOMM HXCOMM can be used for comments, discarded from both texi and C +HXCOMM HXCOMM can be used for comments, discarded from both rST and C. DEFHEADING(Standard options:) -STEXI -@table @option -ETEXI DEF("help", 0, QEMU_OPTION_h, "-h or -help display this help and exit\n", QEMU_ARCH_ALL) -STEXI -@item -h -@findex -h -Display help and exit -ETEXI +SRST +``-h`` + Display help and exit +ERST DEF("version", 0, QEMU_OPTION_version, "-version display version information and exit\n", QEMU_ARCH_ALL) -STEXI -@item -version -@findex -version -Display version information and exit -ETEXI +SRST +``-version`` + Display version information and exit +ERST DEF("machine", HAS_ARG, QEMU_OPTION_machine, \ "-machine [type=]name[,prop[=value][,...]]\n" " selects emulated machine ('-machine help' for list)\n" " property accel=accel1[:accel2[:...]] selects accelerator\n" " supported accelerators are kvm, xen, hax, hvf, whpx or tcg (default: tcg)\n" - " kernel_irqchip=on|off|split controls accelerated irqchip support (default=off)\n" " vmport=on|off|auto controls emulation of vmport (default: auto)\n" - " kvm_shadow_mem=size of KVM shadow MMU in bytes\n" " dump-guest-core=on|off include guest memory in a core dump (default=on)\n" " mem-merge=on|off controls memory merge support (default: on)\n" - " igd-passthru=on|off controls IGD GFX passthrough support (default=off)\n" " aes-key-wrap=on|off controls support for AES key wrapping (default=on)\n" " dea-key-wrap=on|off controls support for DEA key wrapping (default=on)\n" " suppress-vmdesc=on|off disables self-describing migration (default=off)\n" " nvdimm=on|off controls NVDIMM support (default=off)\n" " enforce-config-section=on|off enforce configuration section migration (default=off)\n" - " memory-encryption=@var{} memory encryption object to use (default=none)\n", + " memory-encryption=@var{} memory encryption object to use (default=none)\n" + " hmat=on|off controls ACPI HMAT support (default=off)\n", QEMU_ARCH_ALL) -STEXI -@item -machine [type=]@var{name}[,prop=@var{value}[,...]] -@findex -machine -Select the emulated machine by @var{name}. Use @code{-machine help} to list -available machines. +SRST +``-machine [type=]name[,prop=value[,...]]`` + Select the emulated machine by name. Use ``-machine help`` to list + available machines. -For architectures which aim to support live migration compatibility -across releases, each release will introduce a new versioned machine -type. For example, the 2.8.0 release introduced machine types -``pc-i440fx-2.8'' and ``pc-q35-2.8'' for the x86_64/i686 architectures. + For architectures which aim to support live migration compatibility + across releases, each release will introduce a new versioned machine + type. For example, the 2.8.0 release introduced machine types + "pc-i440fx-2.8" and "pc-q35-2.8" for the x86\_64/i686 architectures. -To allow live migration of guests from QEMU version 2.8.0, to QEMU -version 2.9.0, the 2.9.0 version must support the ``pc-i440fx-2.8'' -and ``pc-q35-2.8'' machines too. To allow users live migrating VMs -to skip multiple intermediate releases when upgrading, new releases -of QEMU will support machine types from many previous versions. + To allow live migration of guests from QEMU version 2.8.0, to QEMU + version 2.9.0, the 2.9.0 version must support the "pc-i440fx-2.8" + and "pc-q35-2.8" machines too. To allow users live migrating VMs to + skip multiple intermediate releases when upgrading, new releases of + QEMU will support machine types from many previous versions. -Supported machine properties are: -@table @option -@item accel=@var{accels1}[:@var{accels2}[:...]] -This is used to enable an accelerator. Depending on the target architecture, -kvm, xen, hax, hvf, whpx or tcg can be available. By default, tcg is used. If there is -more than one accelerator specified, the next one is used if the previous one -fails to initialize. -@item kernel_irqchip=on|off -Controls in-kernel irqchip support for the chosen accelerator when available. -@item gfx_passthru=on|off -Enables IGD GFX passthrough support for the chosen machine when available. -@item vmport=on|off|auto -Enables emulation of VMWare IO port, for vmmouse etc. auto says to select the -value based on accel. For accel=xen the default is off otherwise the default -is on. -@item kvm_shadow_mem=size -Defines the size of the KVM shadow MMU. -@item dump-guest-core=on|off -Include guest memory in a core dump. The default is on. -@item mem-merge=on|off -Enables or disables memory merge support. This feature, when supported by -the host, de-duplicates identical memory pages among VMs instances -(enabled by default). -@item aes-key-wrap=on|off -Enables or disables AES key wrapping support on s390-ccw hosts. This feature -controls whether AES wrapping keys will be created to allow -execution of AES cryptographic functions. The default is on. -@item dea-key-wrap=on|off -Enables or disables DEA key wrapping support on s390-ccw hosts. This feature -controls whether DEA wrapping keys will be created to allow -execution of DEA cryptographic functions. The default is on. -@item nvdimm=on|off -Enables or disables NVDIMM support. The default is off. -@item enforce-config-section=on|off -If @option{enforce-config-section} is set to @var{on}, force migration -code to send configuration section even if the machine-type sets the -@option{migration.send-configuration} property to @var{off}. -NOTE: this parameter is deprecated. Please use @option{-global} -@option{migration.send-configuration}=@var{on|off} instead. -@item memory-encryption=@var{} -Memory encryption object to use. The default is none. -@end table -ETEXI + Supported machine properties are: + + ``accel=accels1[:accels2[:...]]`` + This is used to enable an accelerator. Depending on the target + architecture, kvm, xen, hax, hvf, whpx or tcg can be available. + By default, tcg is used. If there is more than one accelerator + specified, the next one is used if the previous one fails to + initialize. + + ``vmport=on|off|auto`` + Enables emulation of VMWare IO port, for vmmouse etc. auto says + to select the value based on accel. For accel=xen the default is + off otherwise the default is on. + + ``dump-guest-core=on|off`` + Include guest memory in a core dump. The default is on. + + ``mem-merge=on|off`` + Enables or disables memory merge support. This feature, when + supported by the host, de-duplicates identical memory pages + among VMs instances (enabled by default). + + ``aes-key-wrap=on|off`` + Enables or disables AES key wrapping support on s390-ccw hosts. + This feature controls whether AES wrapping keys will be created + to allow execution of AES cryptographic functions. The default + is on. + + ``dea-key-wrap=on|off`` + Enables or disables DEA key wrapping support on s390-ccw hosts. + This feature controls whether DEA wrapping keys will be created + to allow execution of DEA cryptographic functions. The default + is on. + + ``nvdimm=on|off`` + Enables or disables NVDIMM support. The default is off. + + ``enforce-config-section=on|off`` + If ``enforce-config-section`` is set to on, force migration code + to send configuration section even if the machine-type sets the + ``migration.send-configuration`` property to off. NOTE: this + parameter is deprecated. Please use ``-global`` + ``migration.send-configuration``\ =on\|off instead. + + ``memory-encryption=`` + Memory encryption object to use. The default is none. + + ``hmat=on|off`` + Enables or disables ACPI Heterogeneous Memory Attribute Table + (HMAT) support. The default is off. +ERST HXCOMM Deprecated by -machine DEF("M", HAS_ARG, QEMU_OPTION_M, "", QEMU_ARCH_ALL) DEF("cpu", HAS_ARG, QEMU_OPTION_cpu, "-cpu cpu select CPU ('-cpu help' for list)\n", QEMU_ARCH_ALL) -STEXI -@item -cpu @var{model} -@findex -cpu -Select CPU model (@code{-cpu help} for list and additional feature selection) -ETEXI +SRST +``-cpu model`` + Select CPU model (``-cpu help`` for list and additional feature + selection) +ERST DEF("accel", HAS_ARG, QEMU_OPTION_accel, - "-accel [accel=]accelerator[,thread=single|multi]\n" + "-accel [accel=]accelerator[,prop[=value][,...]]\n" " select accelerator (kvm, xen, hax, hvf, whpx or tcg; use 'help' for a list)\n" + " igd-passthru=on|off (enable Xen integrated Intel graphics passthrough, default=off)\n" + " kernel-irqchip=on|off|split controls accelerated irqchip support (default=on)\n" + " kvm-shadow-mem=size of KVM shadow MMU in bytes\n" + " tb-size=n (TCG translation block cache size)\n" " thread=single|multi (enable multi-threaded TCG)\n", QEMU_ARCH_ALL) -STEXI -@item -accel @var{name}[,prop=@var{value}[,...]] -@findex -accel -This is used to enable an accelerator. Depending on the target architecture, -kvm, xen, hax, hvf, whpx or tcg can be available. By default, tcg is used. If there is -more than one accelerator specified, the next one is used if the previous one -fails to initialize. -@table @option -@item thread=single|multi -Controls number of TCG threads. When the TCG is multi-threaded there will be one -thread per vCPU therefor taking advantage of additional host cores. The default -is to enable multi-threading where both the back-end and front-ends support it and -no incompatible TCG features have been enabled (e.g. icount/replay). -@end table -ETEXI +SRST +``-accel name[,prop=value[,...]]`` + This is used to enable an accelerator. Depending on the target + architecture, kvm, xen, hax, hvf, whpx or tcg can be available. By + default, tcg is used. If there is more than one accelerator + specified, the next one is used if the previous one fails to + initialize. + + ``igd-passthru=on|off`` + When Xen is in use, this option controls whether Intel + integrated graphics devices can be passed through to the guest + (default=off) + + ``kernel-irqchip=on|off|split`` + Controls KVM in-kernel irqchip support. The default is full + acceleration of the interrupt controllers. On x86, split irqchip + reduces the kernel attack surface, at a performance cost for + non-MSI interrupts. Disabling the in-kernel irqchip completely + is not recommended except for debugging purposes. + + ``kvm-shadow-mem=size`` + Defines the size of the KVM shadow MMU. + + ``tb-size=n`` + Controls the size (in MiB) of the TCG translation block cache. + + ``thread=single|multi`` + Controls number of TCG threads. When the TCG is multi-threaded + there will be one thread per vCPU therefor taking advantage of + additional host cores. The default is to enable multi-threading + where both the back-end and front-ends support it and no + incompatible TCG features have been enabled (e.g. + icount/replay). +ERST DEF("smp", HAS_ARG, QEMU_OPTION_smp, "-smp [cpus=]n[,maxcpus=cpus][,cores=cores][,threads=threads][,dies=dies][,sockets=sockets]\n" @@ -147,153 +170,247 @@ DEF("smp", HAS_ARG, QEMU_OPTION_smp, " dies= number of CPU dies on one socket (for PC only)\n" " sockets= number of discrete sockets in the system\n", QEMU_ARCH_ALL) -STEXI -@item -smp [cpus=]@var{n}[,cores=@var{cores}][,threads=@var{threads}][,dies=dies][,sockets=@var{sockets}][,maxcpus=@var{maxcpus}] -@findex -smp -Simulate an SMP system with @var{n} CPUs. On the PC target, up to 255 -CPUs are supported. On Sparc32 target, Linux limits the number of usable CPUs -to 4. -For the PC target, the number of @var{cores} per die, the number of @var{threads} -per cores, the number of @var{dies} per packages and the total number of -@var{sockets} can be specified. Missing values will be computed. -If any on the three values is given, the total number of CPUs @var{n} can be omitted. -@var{maxcpus} specifies the maximum number of hotpluggable CPUs. -ETEXI +SRST +``-smp [cpus=]n[,cores=cores][,threads=threads][,dies=dies][,sockets=sockets][,maxcpus=maxcpus]`` + Simulate an SMP system with n CPUs. On the PC target, up to 255 CPUs + are supported. On Sparc32 target, Linux limits the number of usable + CPUs to 4. For the PC target, the number of cores per die, the + number of threads per cores, the number of dies per packages and the + total number of sockets can be specified. Missing values will be + computed. If any on the three values is given, the total number of + CPUs n can be omitted. maxcpus specifies the maximum number of + hotpluggable CPUs. +ERST DEF("numa", HAS_ARG, QEMU_OPTION_numa, - "-numa node[,mem=size][,cpus=firstcpu[-lastcpu]][,nodeid=node]\n" - "-numa node[,memdev=id][,cpus=firstcpu[-lastcpu]][,nodeid=node]\n" + "-numa node[,mem=size][,cpus=firstcpu[-lastcpu]][,nodeid=node][,initiator=node]\n" + "-numa node[,memdev=id][,cpus=firstcpu[-lastcpu]][,nodeid=node][,initiator=node]\n" "-numa dist,src=source,dst=destination,val=distance\n" - "-numa cpu,node-id=node[,socket-id=x][,core-id=y][,thread-id=z]\n", + "-numa cpu,node-id=node[,socket-id=x][,core-id=y][,thread-id=z]\n" + "-numa hmat-lb,initiator=node,target=node,hierarchy=memory|first-level|second-level|third-level,data-type=access-latency|read-latency|write-latency[,latency=lat][,bandwidth=bw]\n" + "-numa hmat-cache,node-id=node,size=size,level=level[,associativity=none|direct|complex][,policy=none|write-back|write-through][,line=size]\n", QEMU_ARCH_ALL) -STEXI -@item -numa node[,mem=@var{size}][,cpus=@var{firstcpu}[-@var{lastcpu}]][,nodeid=@var{node}] -@itemx -numa node[,memdev=@var{id}][,cpus=@var{firstcpu}[-@var{lastcpu}]][,nodeid=@var{node}] -@itemx -numa dist,src=@var{source},dst=@var{destination},val=@var{distance} -@itemx -numa cpu,node-id=@var{node}[,socket-id=@var{x}][,core-id=@var{y}][,thread-id=@var{z}] -@findex -numa -Define a NUMA node and assign RAM and VCPUs to it. -Set the NUMA distance from a source node to a destination node. +SRST +``-numa node[,mem=size][,cpus=firstcpu[-lastcpu]][,nodeid=node][,initiator=initiator]`` + \ +``-numa node[,memdev=id][,cpus=firstcpu[-lastcpu]][,nodeid=node][,initiator=initiator]`` + \ +``-numa dist,src=source,dst=destination,val=distance`` + \ +``-numa cpu,node-id=node[,socket-id=x][,core-id=y][,thread-id=z]`` + \ +``-numa hmat-lb,initiator=node,target=node,hierarchy=hierarchy,data-type=tpye[,latency=lat][,bandwidth=bw]`` + \ +``-numa hmat-cache,node-id=node,size=size,level=level[,associativity=str][,policy=str][,line=size]`` + Define a NUMA node and assign RAM and VCPUs to it. Set the NUMA + distance from a source node to a destination node. Set the ACPI + Heterogeneous Memory Attributes for the given nodes. -Legacy VCPU assignment uses @samp{cpus} option where -@var{firstcpu} and @var{lastcpu} are CPU indexes. Each -@samp{cpus} option represent a contiguous range of CPU indexes -(or a single VCPU if @var{lastcpu} is omitted). A non-contiguous -set of VCPUs can be represented by providing multiple @samp{cpus} -options. If @samp{cpus} is omitted on all nodes, VCPUs are automatically -split between them. + Legacy VCPU assignment uses '\ ``cpus``\ ' option where firstcpu and + lastcpu are CPU indexes. Each '\ ``cpus``\ ' option represent a + contiguous range of CPU indexes (or a single VCPU if lastcpu is + omitted). A non-contiguous set of VCPUs can be represented by + providing multiple '\ ``cpus``\ ' options. If '\ ``cpus``\ ' is + omitted on all nodes, VCPUs are automatically split between them. -For example, the following option assigns VCPUs 0, 1, 2 and 5 to -a NUMA node: -@example --numa node,cpus=0-2,cpus=5 -@end example + For example, the following option assigns VCPUs 0, 1, 2 and 5 to a + NUMA node: -@samp{cpu} option is a new alternative to @samp{cpus} option -which uses @samp{socket-id|core-id|thread-id} properties to assign -CPU objects to a @var{node} using topology layout properties of CPU. -The set of properties is machine specific, and depends on used -machine type/@samp{smp} options. It could be queried with -@samp{hotpluggable-cpus} monitor command. -@samp{node-id} property specifies @var{node} to which CPU object -will be assigned, it's required for @var{node} to be declared -with @samp{node} option before it's used with @samp{cpu} option. + :: -For example: -@example --M pc \ --smp 1,sockets=2,maxcpus=2 \ --numa node,nodeid=0 -numa node,nodeid=1 \ --numa cpu,node-id=0,socket-id=0 -numa cpu,node-id=1,socket-id=1 -@end example + -numa node,cpus=0-2,cpus=5 -@samp{mem} assigns a given RAM amount to a node. @samp{memdev} -assigns RAM from a given memory backend device to a node. If -@samp{mem} and @samp{memdev} are omitted in all nodes, RAM is -split equally between them. + '\ ``cpu``\ ' option is a new alternative to '\ ``cpus``\ ' option + which uses '\ ``socket-id|core-id|thread-id``\ ' properties to + assign CPU objects to a node using topology layout properties of + CPU. The set of properties is machine specific, and depends on used + machine type/'\ ``smp``\ ' options. It could be queried with + '\ ``hotpluggable-cpus``\ ' monitor command. '\ ``node-id``\ ' + property specifies node to which CPU object will be assigned, it's + required for node to be declared with '\ ``node``\ ' option before + it's used with '\ ``cpu``\ ' option. -@samp{mem} and @samp{memdev} are mutually exclusive. Furthermore, -if one node uses @samp{memdev}, all of them have to use it. + For example: -@var{source} and @var{destination} are NUMA node IDs. -@var{distance} is the NUMA distance from @var{source} to @var{destination}. -The distance from a node to itself is always 10. If any pair of nodes is -given a distance, then all pairs must be given distances. Although, when -distances are only given in one direction for each pair of nodes, then -the distances in the opposite directions are assumed to be the same. If, -however, an asymmetrical pair of distances is given for even one node -pair, then all node pairs must be provided distance values for both -directions, even when they are symmetrical. When a node is unreachable -from another node, set the pair's distance to 255. + :: -Note that the -@option{numa} option doesn't allocate any of the -specified resources, it just assigns existing resources to NUMA -nodes. This means that one still has to use the @option{-m}, -@option{-smp} options to allocate RAM and VCPUs respectively. + -M pc \ + -smp 1,sockets=2,maxcpus=2 \ + -numa node,nodeid=0 -numa node,nodeid=1 \ + -numa cpu,node-id=0,socket-id=0 -numa cpu,node-id=1,socket-id=1 -ETEXI + '\ ``mem``\ ' assigns a given RAM amount to a node. '\ ``memdev``\ ' + assigns RAM from a given memory backend device to a node. If + '\ ``mem``\ ' and '\ ``memdev``\ ' are omitted in all nodes, RAM is + split equally between them. + + '\ ``mem``\ ' and '\ ``memdev``\ ' are mutually exclusive. + Furthermore, if one node uses '\ ``memdev``\ ', all of them have to + use it. + + '\ ``initiator``\ ' is an additional option that points to an + initiator NUMA node that has best performance (the lowest latency or + largest bandwidth) to this NUMA node. Note that this option can be + set only when the machine property 'hmat' is set to 'on'. + + Following example creates a machine with 2 NUMA nodes, node 0 has + CPU. node 1 has only memory, and its initiator is node 0. Note that + because node 0 has CPU, by default the initiator of node 0 is itself + and must be itself. + + :: + + -machine hmat=on \ + -m 2G,slots=2,maxmem=4G \ + -object memory-backend-ram,size=1G,id=m0 \ + -object memory-backend-ram,size=1G,id=m1 \ + -numa node,nodeid=0,memdev=m0 \ + -numa node,nodeid=1,memdev=m1,initiator=0 \ + -smp 2,sockets=2,maxcpus=2 \ + -numa cpu,node-id=0,socket-id=0 \ + -numa cpu,node-id=0,socket-id=1 + + source and destination are NUMA node IDs. distance is the NUMA + distance from source to destination. The distance from a node to + itself is always 10. If any pair of nodes is given a distance, then + all pairs must be given distances. Although, when distances are only + given in one direction for each pair of nodes, then the distances in + the opposite directions are assumed to be the same. If, however, an + asymmetrical pair of distances is given for even one node pair, then + all node pairs must be provided distance values for both directions, + even when they are symmetrical. When a node is unreachable from + another node, set the pair's distance to 255. + + Note that the -``numa`` option doesn't allocate any of the specified + resources, it just assigns existing resources to NUMA nodes. This + means that one still has to use the ``-m``, ``-smp`` options to + allocate RAM and VCPUs respectively. + + Use '\ ``hmat-lb``\ ' to set System Locality Latency and Bandwidth + Information between initiator and target NUMA nodes in ACPI + Heterogeneous Attribute Memory Table (HMAT). Initiator NUMA node can + create memory requests, usually it has one or more processors. + Target NUMA node contains addressable memory. + + In '\ ``hmat-lb``\ ' option, node are NUMA node IDs. hierarchy is + the memory hierarchy of the target NUMA node: if hierarchy is + 'memory', the structure represents the memory performance; if + hierarchy is 'first-level\|second-level\|third-level', this + structure represents aggregated performance of memory side caches + for each domain. type of 'data-type' is type of data represented by + this structure instance: if 'hierarchy' is 'memory', 'data-type' is + 'access\|read\|write' latency or 'access\|read\|write' bandwidth of + the target memory; if 'hierarchy' is + 'first-level\|second-level\|third-level', 'data-type' is + 'access\|read\|write' hit latency or 'access\|read\|write' hit + bandwidth of the target memory side cache. + + lat is latency value in nanoseconds. bw is bandwidth value, the + possible value and units are NUM[M\|G\|T], mean that the bandwidth + value are NUM byte per second (or MB/s, GB/s or TB/s depending on + used suffix). Note that if latency or bandwidth value is 0, means + the corresponding latency or bandwidth information is not provided. + + In '\ ``hmat-cache``\ ' option, node-id is the NUMA-id of the memory + belongs. size is the size of memory side cache in bytes. level is + the cache level described in this structure, note that the cache + level 0 should not be used with '\ ``hmat-cache``\ ' option. + associativity is the cache associativity, the possible value is + 'none/direct(direct-mapped)/complex(complex cache indexing)'. policy + is the write policy. line is the cache Line size in bytes. + + For example, the following options describe 2 NUMA nodes. Node 0 has + 2 cpus and a ram, node 1 has only a ram. The processors in node 0 + access memory in node 0 with access-latency 5 nanoseconds, + access-bandwidth is 200 MB/s; The processors in NUMA node 0 access + memory in NUMA node 1 with access-latency 10 nanoseconds, + access-bandwidth is 100 MB/s. And for memory side cache information, + NUMA node 0 and 1 both have 1 level memory cache, size is 10KB, + policy is write-back, the cache Line size is 8 bytes: + + :: + + -machine hmat=on \ + -m 2G \ + -object memory-backend-ram,size=1G,id=m0 \ + -object memory-backend-ram,size=1G,id=m1 \ + -smp 2 \ + -numa node,nodeid=0,memdev=m0 \ + -numa node,nodeid=1,memdev=m1,initiator=0 \ + -numa cpu,node-id=0,socket-id=0 \ + -numa cpu,node-id=0,socket-id=1 \ + -numa hmat-lb,initiator=0,target=0,hierarchy=memory,data-type=access-latency,latency=5 \ + -numa hmat-lb,initiator=0,target=0,hierarchy=memory,data-type=access-bandwidth,bandwidth=200M \ + -numa hmat-lb,initiator=0,target=1,hierarchy=memory,data-type=access-latency,latency=10 \ + -numa hmat-lb,initiator=0,target=1,hierarchy=memory,data-type=access-bandwidth,bandwidth=100M \ + -numa hmat-cache,node-id=0,size=10K,level=1,associativity=direct,policy=write-back,line=8 \ + -numa hmat-cache,node-id=1,size=10K,level=1,associativity=direct,policy=write-back,line=8 +ERST DEF("add-fd", HAS_ARG, QEMU_OPTION_add_fd, "-add-fd fd=fd,set=set[,opaque=opaque]\n" " Add 'fd' to fd 'set'\n", QEMU_ARCH_ALL) -STEXI -@item -add-fd fd=@var{fd},set=@var{set}[,opaque=@var{opaque}] -@findex -add-fd +SRST +``-add-fd fd=fd,set=set[,opaque=opaque]`` + Add a file descriptor to an fd set. Valid options are: -Add a file descriptor to an fd set. Valid options are: + ``fd=fd`` + This option defines the file descriptor of which a duplicate is + added to fd set. The file descriptor cannot be stdin, stdout, or + stderr. -@table @option -@item fd=@var{fd} -This option defines the file descriptor of which a duplicate is added to fd set. -The file descriptor cannot be stdin, stdout, or stderr. -@item set=@var{set} -This option defines the ID of the fd set to add the file descriptor to. -@item opaque=@var{opaque} -This option defines a free-form string that can be used to describe @var{fd}. -@end table + ``set=set`` + This option defines the ID of the fd set to add the file + descriptor to. -You can open an image using pre-opened file descriptors from an fd set: -@example -@value{qemu_system} \ - -add-fd fd=3,set=2,opaque="rdwr:/path/to/file" \ - -add-fd fd=4,set=2,opaque="rdonly:/path/to/file" \ - -drive file=/dev/fdset/2,index=0,media=disk -@end example -ETEXI + ``opaque=opaque`` + This option defines a free-form string that can be used to + describe fd. + + You can open an image using pre-opened file descriptors from an fd + set: + + .. parsed-literal:: + + |qemu_system| \ + -add-fd fd=3,set=2,opaque="rdwr:/path/to/file" \ + -add-fd fd=4,set=2,opaque="rdonly:/path/to/file" \ + -drive file=/dev/fdset/2,index=0,media=disk +ERST DEF("set", HAS_ARG, QEMU_OPTION_set, "-set group.id.arg=value\n" " set parameter for item of type \n" " i.e. -set drive.$id.file=/path/to/image\n", QEMU_ARCH_ALL) -STEXI -@item -set @var{group}.@var{id}.@var{arg}=@var{value} -@findex -set -Set parameter @var{arg} for item @var{id} of type @var{group} -ETEXI +SRST +``-set group.id.arg=value`` + Set parameter arg for item id of type group +ERST DEF("global", HAS_ARG, QEMU_OPTION_global, "-global driver.property=value\n" "-global driver=driver,property=property,value=value\n" " set a global default for a driver property\n", QEMU_ARCH_ALL) -STEXI -@item -global @var{driver}.@var{prop}=@var{value} -@itemx -global driver=@var{driver},property=@var{property},value=@var{value} -@findex -global -Set default value of @var{driver}'s property @var{prop} to @var{value}, e.g.: +SRST +``-global driver.prop=value`` + \ +``-global driver=driver,property=property,value=value`` + Set default value of driver's property prop to value, e.g.: -@example -@value{qemu_system_x86} -global ide-hd.physical_block_size=4096 disk-image.img -@end example + .. parsed-literal:: -In particular, you can use this to set driver properties for devices which are -created automatically by the machine model. To create a device which is not -created automatically and set properties on it, use -@option{device}. + |qemu_system_x86| -global ide-hd.physical_block_size=4096 disk-image.img --global @var{driver}.@var{prop}=@var{value} is shorthand for -global -driver=@var{driver},property=@var{prop},value=@var{value}. The -longhand syntax works even when @var{driver} contains a dot. -ETEXI + In particular, you can use this to set driver properties for devices + which are created automatically by the machine model. To create a + device which is not created automatically and set properties on it, + use -``device``. + + -global driver.prop=value is shorthand for -global + driver=driver,property=prop,value=value. The longhand syntax works + even when driver contains a dot. +ERST DEF("boot", HAS_ARG, QEMU_OPTION_boot, "-boot [order=drives][,once=drives][,menu=on|off]\n" @@ -303,50 +420,50 @@ DEF("boot", HAS_ARG, QEMU_OPTION_boot, " 'sp_time': the period that splash picture last if menu=on, unit is ms\n" " 'rb_timeout': the timeout before guest reboot when boot failed, unit is ms\n", QEMU_ARCH_ALL) -STEXI -@item -boot [order=@var{drives}][,once=@var{drives}][,menu=on|off][,splash=@var{sp_name}][,splash-time=@var{sp_time}][,reboot-timeout=@var{rb_timeout}][,strict=on|off] -@findex -boot -Specify boot order @var{drives} as a string of drive letters. Valid -drive letters depend on the target architecture. The x86 PC uses: a, b -(floppy 1 and 2), c (first hard disk), d (first CD-ROM), n-p (Etherboot -from network adapter 1-4), hard disk boot is the default. To apply a -particular boot order only on the first startup, specify it via -@option{once}. Note that the @option{order} or @option{once} parameter -should not be used together with the @option{bootindex} property of -devices, since the firmware implementations normally do not support both -at the same time. +SRST +``-boot [order=drives][,once=drives][,menu=on|off][,splash=sp_name][,splash-time=sp_time][,reboot-timeout=rb_timeout][,strict=on|off]`` + Specify boot order drives as a string of drive letters. Valid drive + letters depend on the target architecture. The x86 PC uses: a, b + (floppy 1 and 2), c (first hard disk), d (first CD-ROM), n-p + (Etherboot from network adapter 1-4), hard disk boot is the default. + To apply a particular boot order only on the first startup, specify + it via ``once``. Note that the ``order`` or ``once`` parameter + should not be used together with the ``bootindex`` property of + devices, since the firmware implementations normally do not support + both at the same time. -Interactive boot menus/prompts can be enabled via @option{menu=on} as far -as firmware/BIOS supports them. The default is non-interactive boot. + Interactive boot menus/prompts can be enabled via ``menu=on`` as far + as firmware/BIOS supports them. The default is non-interactive boot. -A splash picture could be passed to bios, enabling user to show it as logo, -when option splash=@var{sp_name} is given and menu=on, If firmware/BIOS -supports them. Currently Seabios for X86 system support it. -limitation: The splash file could be a jpeg file or a BMP file in 24 BPP -format(true color). The resolution should be supported by the SVGA mode, so -the recommended is 320x240, 640x480, 800x640. + A splash picture could be passed to bios, enabling user to show it + as logo, when option splash=sp\_name is given and menu=on, If + firmware/BIOS supports them. Currently Seabios for X86 system + support it. limitation: The splash file could be a jpeg file or a + BMP file in 24 BPP format(true color). The resolution should be + supported by the SVGA mode, so the recommended is 320x240, 640x480, + 800x640. -A timeout could be passed to bios, guest will pause for @var{rb_timeout} ms -when boot failed, then reboot. If @option{reboot-timeout} is not set, -guest will not reboot by default. Currently Seabios for X86 -system support it. + A timeout could be passed to bios, guest will pause for rb\_timeout + ms when boot failed, then reboot. If rb\_timeout is '-1', guest will + not reboot, qemu passes '-1' to bios by default. Currently Seabios + for X86 system support it. -Do strict boot via @option{strict=on} as far as firmware/BIOS -supports it. This only effects when boot priority is changed by -bootindex options. The default is non-strict boot. + Do strict boot via ``strict=on`` as far as firmware/BIOS supports + it. This only effects when boot priority is changed by bootindex + options. The default is non-strict boot. -@example -# try to boot from network first, then from hard disk -@value{qemu_system_x86} -boot order=nc -# boot from CD-ROM first, switch back to default order after reboot -@value{qemu_system_x86} -boot once=d -# boot with a splash picture for 5 seconds. -@value{qemu_system_x86} -boot menu=on,splash=/root/boot.bmp,splash-time=5000 -@end example + .. parsed-literal:: -Note: The legacy format '-boot @var{drives}' is still supported but its -use is discouraged as it may be removed from future versions. -ETEXI + # try to boot from network first, then from hard disk + |qemu_system_x86| -boot order=nc + # boot from CD-ROM first, switch back to default order after reboot + |qemu_system_x86| -boot once=d + # boot with a splash picture for 5 seconds. + |qemu_system_x86| -boot menu=on,splash=/root/boot.bmp,splash-time=5000 + + Note: The legacy format '-boot drives' is still supported but its + use is discouraged as it may be removed from future versions. +ERST DEF("m", HAS_ARG, QEMU_OPTION_m, "-m [size=]megs[,slots=n,maxmem=size]\n" @@ -356,77 +473,73 @@ DEF("m", HAS_ARG, QEMU_OPTION_m, " maxmem: maximum amount of guest memory (default: none)\n" "NOTE: Some architectures might enforce a specific granularity\n", QEMU_ARCH_ALL) -STEXI -@item -m [size=]@var{megs}[,slots=n,maxmem=size] -@findex -m -Sets guest startup RAM size to @var{megs} megabytes. Default is 128 MiB. -Optionally, a suffix of ``M'' or ``G'' can be used to signify a value in -megabytes or gigabytes respectively. Optional pair @var{slots}, @var{maxmem} -could be used to set amount of hotpluggable memory slots and maximum amount of -memory. Note that @var{maxmem} must be aligned to the page size. +SRST +``-m [size=]megs[,slots=n,maxmem=size]`` + Sets guest startup RAM size to megs megabytes. Default is 128 MiB. + Optionally, a suffix of "M" or "G" can be used to signify a value in + megabytes or gigabytes respectively. Optional pair slots, maxmem + could be used to set amount of hotpluggable memory slots and maximum + amount of memory. Note that maxmem must be aligned to the page size. -For example, the following command-line sets the guest startup RAM size to -1GB, creates 3 slots to hotplug additional memory and sets the maximum -memory the guest can reach to 4GB: + For example, the following command-line sets the guest startup RAM + size to 1GB, creates 3 slots to hotplug additional memory and sets + the maximum memory the guest can reach to 4GB: -@example -@value{qemu_system} -m 1G,slots=3,maxmem=4G -@end example + .. parsed-literal:: -If @var{slots} and @var{maxmem} are not specified, memory hotplug won't -be enabled and the guest startup RAM will never increase. -ETEXI + |qemu_system| -m 1G,slots=3,maxmem=4G + + If slots and maxmem are not specified, memory hotplug won't be + enabled and the guest startup RAM will never increase. +ERST DEF("mem-path", HAS_ARG, QEMU_OPTION_mempath, "-mem-path FILE provide backing storage for guest RAM\n", QEMU_ARCH_ALL) -STEXI -@item -mem-path @var{path} -@findex -mem-path -Allocate guest RAM from a temporarily created file in @var{path}. -ETEXI +SRST +``-mem-path path`` + Allocate guest RAM from a temporarily created file in path. +ERST DEF("mem-prealloc", 0, QEMU_OPTION_mem_prealloc, "-mem-prealloc preallocate guest memory (use with -mem-path)\n", QEMU_ARCH_ALL) -STEXI -@item -mem-prealloc -@findex -mem-prealloc -Preallocate memory when using -mem-path. -ETEXI +SRST +``-mem-prealloc`` + Preallocate memory when using -mem-path. +ERST DEF("k", HAS_ARG, QEMU_OPTION_k, "-k language use keyboard layout (for example 'fr' for French)\n", QEMU_ARCH_ALL) -STEXI -@item -k @var{language} -@findex -k -Use keyboard layout @var{language} (for example @code{fr} for -French). This option is only needed where it is not easy to get raw PC -keycodes (e.g. on Macs, with some X11 servers or with a VNC or curses -display). You don't normally need to use it on PC/Linux or PC/Windows -hosts. +SRST +``-k language`` + Use keyboard layout language (for example ``fr`` for French). This + option is only needed where it is not easy to get raw PC keycodes + (e.g. on Macs, with some X11 servers or with a VNC or curses + display). You don't normally need to use it on PC/Linux or + PC/Windows hosts. -The available layouts are: -@example -ar de-ch es fo fr-ca hu ja mk no pt-br sv -da en-gb et fr fr-ch is lt nl pl ru th -de en-us fi fr-be hr it lv nl-be pt sl tr -@end example + The available layouts are: -The default is @code{en-us}. -ETEXI + :: + + ar de-ch es fo fr-ca hu ja mk no pt-br sv + da en-gb et fr fr-ch is lt nl pl ru th + de en-us fi fr-be hr it lv nl-be pt sl tr + + The default is ``en-us``. +ERST HXCOMM Deprecated by -audiodev DEF("audio-help", 0, QEMU_OPTION_audio_help, "-audio-help show -audiodev equivalent of the currently specified audio settings\n", QEMU_ARCH_ALL) -STEXI -@item -audio-help -@findex -audio-help -Will show the -audiodev equivalent of the currently specified -(deprecated) environment variables. -ETEXI +SRST +``-audio-help`` + Will show the -audiodev equivalent of the currently specified + (deprecated) environment variables. +ERST DEF("audiodev", HAS_ARG, QEMU_OPTION_audiodev, "-audiodev [driver=]driver,id=id[,prop[=value][,...]]\n" @@ -438,7 +551,7 @@ DEF("audiodev", HAS_ARG, QEMU_OPTION_audiodev, " in|out.frequency= frequency to use with fixed settings\n" " in|out.channels= number of channels to use with fixed settings\n" " in|out.format= sample format to use with fixed settings\n" - " valid values: s8, s16, s32, u8, u16, u32\n" + " valid values: s8, s16, s32, u8, u16, u32, f32\n" " in|out.voices= number of voices to use\n" " in|out.buffer-length= length of buffer in microseconds\n" "-audiodev none,id=id,[,prop[=value][,...]]\n" @@ -482,228 +595,200 @@ DEF("audiodev", HAS_ARG, QEMU_OPTION_audiodev, "-audiodev wav,id=id[,prop[=value][,...]]\n" " path= path of wav file to record\n", QEMU_ARCH_ALL) -STEXI -@item -audiodev [driver=]@var{driver},id=@var{id}[,@var{prop}[=@var{value}][,...]] -@findex -audiodev -Adds a new audio backend @var{driver} identified by @var{id}. There are -global and driver specific properties. Some values can be set -differently for input and output, they're marked with @code{in|out.}. -You can set the input's property with @code{in.@var{prop}} and the -output's property with @code{out.@var{prop}}. For example: -@example --audiodev alsa,id=example,in.frequency=44110,out.frequency=8000 --audiodev alsa,id=example,out.channels=1 # leaves in.channels unspecified -@end example +SRST +``-audiodev [driver=]driver,id=id[,prop[=value][,...]]`` + Adds a new audio backend driver identified by id. There are global + and driver specific properties. Some values can be set differently + for input and output, they're marked with ``in|out.``. You can set + the input's property with ``in.prop`` and the output's property with + ``out.prop``. For example: -NOTE: parameter validation is known to be incomplete, in many cases -specifying an invalid option causes QEMU to print an error message and -continue emulation without sound. + :: -Valid global options are: + -audiodev alsa,id=example,in.frequency=44110,out.frequency=8000 + -audiodev alsa,id=example,out.channels=1 # leaves in.channels unspecified -@table @option -@item id=@var{identifier} -Identifies the audio backend. + NOTE: parameter validation is known to be incomplete, in many cases + specifying an invalid option causes QEMU to print an error message + and continue emulation without sound. -@item timer-period=@var{period} -Sets the timer @var{period} used by the audio subsystem in microseconds. -Default is 10000 (10 ms). + Valid global options are: -@item in|out.mixing-engine=on|off -Use QEMU's mixing engine to mix all streams inside QEMU and convert -audio formats when not supported by the backend. When off, -@var{fixed-settings} must be off too. Note that disabling this option -means that the selected backend must support multiple streams and the -audio formats used by the virtual cards, otherwise you'll get no sound. -It's not recommended to disable this option unless you want to use 5.1 -or 7.1 audio, as mixing engine only supports mono and stereo audio. -Default is on. + ``id=identifier`` + Identifies the audio backend. -@item in|out.fixed-settings=on|off -Use fixed settings for host audio. When off, it will change based on -how the guest opens the sound card. In this case you must not specify -@var{frequency}, @var{channels} or @var{format}. Default is on. + ``timer-period=period`` + Sets the timer period used by the audio subsystem in + microseconds. Default is 10000 (10 ms). -@item in|out.frequency=@var{frequency} -Specify the @var{frequency} to use when using @var{fixed-settings}. -Default is 44100Hz. + ``in|out.mixing-engine=on|off`` + Use QEMU's mixing engine to mix all streams inside QEMU and + convert audio formats when not supported by the backend. When + off, fixed-settings must be off too. Note that disabling this + option means that the selected backend must support multiple + streams and the audio formats used by the virtual cards, + otherwise you'll get no sound. It's not recommended to disable + this option unless you want to use 5.1 or 7.1 audio, as mixing + engine only supports mono and stereo audio. Default is on. -@item in|out.channels=@var{channels} -Specify the number of @var{channels} to use when using -@var{fixed-settings}. Default is 2 (stereo). + ``in|out.fixed-settings=on|off`` + Use fixed settings for host audio. When off, it will change + based on how the guest opens the sound card. In this case you + must not specify frequency, channels or format. Default is on. -@item in|out.format=@var{format} -Specify the sample @var{format} to use when using @var{fixed-settings}. -Valid values are: @code{s8}, @code{s16}, @code{s32}, @code{u8}, -@code{u16}, @code{u32}. Default is @code{s16}. + ``in|out.frequency=frequency`` + Specify the frequency to use when using fixed-settings. Default + is 44100Hz. -@item in|out.voices=@var{voices} -Specify the number of @var{voices} to use. Default is 1. + ``in|out.channels=channels`` + Specify the number of channels to use when using fixed-settings. + Default is 2 (stereo). -@item in|out.buffer-length=@var{usecs} -Sets the size of the buffer in microseconds. + ``in|out.format=format`` + Specify the sample format to use when using fixed-settings. + Valid values are: ``s8``, ``s16``, ``s32``, ``u8``, ``u16``, + ``u32``, ``f32``. Default is ``s16``. -@end table + ``in|out.voices=voices`` + Specify the number of voices to use. Default is 1. -@item -audiodev none,id=@var{id}[,@var{prop}[=@var{value}][,...]] -Creates a dummy backend that discards all outputs. This backend has no -backend specific properties. + ``in|out.buffer-length=usecs`` + Sets the size of the buffer in microseconds. -@item -audiodev alsa,id=@var{id}[,@var{prop}[=@var{value}][,...]] -Creates backend using the ALSA. This backend is only available on -Linux. +``-audiodev none,id=id[,prop[=value][,...]]`` + Creates a dummy backend that discards all outputs. This backend has + no backend specific properties. -ALSA specific options are: +``-audiodev alsa,id=id[,prop[=value][,...]]`` + Creates backend using the ALSA. This backend is only available on + Linux. -@table @option + ALSA specific options are: -@item in|out.dev=@var{device} -Specify the ALSA @var{device} to use for input and/or output. Default -is @code{default}. + ``in|out.dev=device`` + Specify the ALSA device to use for input and/or output. Default + is ``default``. -@item in|out.period-length=@var{usecs} -Sets the period length in microseconds. + ``in|out.period-length=usecs`` + Sets the period length in microseconds. -@item in|out.try-poll=on|off -Attempt to use poll mode with the device. Default is on. + ``in|out.try-poll=on|off`` + Attempt to use poll mode with the device. Default is on. -@item threshold=@var{threshold} -Threshold (in microseconds) when playback starts. Default is 0. + ``threshold=threshold`` + Threshold (in microseconds) when playback starts. Default is 0. -@end table +``-audiodev coreaudio,id=id[,prop[=value][,...]]`` + Creates a backend using Apple's Core Audio. This backend is only + available on Mac OS and only supports playback. -@item -audiodev coreaudio,id=@var{id}[,@var{prop}[=@var{value}][,...]] -Creates a backend using Apple's Core Audio. This backend is only -available on Mac OS and only supports playback. + Core Audio specific options are: -Core Audio specific options are: + ``in|out.buffer-count=count`` + Sets the count of the buffers. -@table @option +``-audiodev dsound,id=id[,prop[=value][,...]]`` + Creates a backend using Microsoft's DirectSound. This backend is + only available on Windows and only supports playback. -@item in|out.buffer-count=@var{count} -Sets the @var{count} of the buffers. + DirectSound specific options are: -@end table + ``latency=usecs`` + Add extra usecs microseconds latency to playback. Default is + 10000 (10 ms). -@item -audiodev dsound,id=@var{id}[,@var{prop}[=@var{value}][,...]] -Creates a backend using Microsoft's DirectSound. This backend is only -available on Windows and only supports playback. +``-audiodev oss,id=id[,prop[=value][,...]]`` + Creates a backend using OSS. This backend is available on most + Unix-like systems. -DirectSound specific options are: + OSS specific options are: -@table @option + ``in|out.dev=device`` + Specify the file name of the OSS device to use. Default is + ``/dev/dsp``. -@item latency=@var{usecs} -Add extra @var{usecs} microseconds latency to playback. Default is -10000 (10 ms). + ``in|out.buffer-count=count`` + Sets the count of the buffers. -@end table + ``in|out.try-poll=on|of`` + Attempt to use poll mode with the device. Default is on. -@item -audiodev oss,id=@var{id}[,@var{prop}[=@var{value}][,...]] -Creates a backend using OSS. This backend is available on most -Unix-like systems. + ``try-mmap=on|off`` + Try using memory mapped device access. Default is off. -OSS specific options are: + ``exclusive=on|off`` + Open the device in exclusive mode (vmix won't work in this + case). Default is off. -@table @option + ``dsp-policy=policy`` + Sets the timing policy (between 0 and 10, where smaller number + means smaller latency but higher CPU usage). Use -1 to use + buffer sizes specified by ``buffer`` and ``buffer-count``. This + option is ignored if you do not have OSS 4. Default is 5. -@item in|out.dev=@var{device} -Specify the file name of the OSS @var{device} to use. Default is -@code{/dev/dsp}. +``-audiodev pa,id=id[,prop[=value][,...]]`` + Creates a backend using PulseAudio. This backend is available on + most systems. -@item in|out.buffer-count=@var{count} -Sets the @var{count} of the buffers. + PulseAudio specific options are: -@item in|out.try-poll=on|of -Attempt to use poll mode with the device. Default is on. + ``server=server`` + Sets the PulseAudio server to connect to. -@item try-mmap=on|off -Try using memory mapped device access. Default is off. + ``in|out.name=sink`` + Use the specified source/sink for recording/playback. -@item exclusive=on|off -Open the device in exclusive mode (vmix won't work in this case). -Default is off. + ``in|out.latency=usecs`` + Desired latency in microseconds. The PulseAudio server will try + to honor this value but actual latencies may be lower or higher. -@item dsp-policy=@var{policy} -Sets the timing policy (between 0 and 10, where smaller number means -smaller latency but higher CPU usage). Use -1 to use buffer sizes -specified by @code{buffer} and @code{buffer-count}. This option is -ignored if you do not have OSS 4. Default is 5. +``-audiodev sdl,id=id[,prop[=value][,...]]`` + Creates a backend using SDL. This backend is available on most + systems, but you should use your platform's native backend if + possible. This backend has no backend specific properties. -@end table +``-audiodev spice,id=id[,prop[=value][,...]]`` + Creates a backend that sends audio through SPICE. This backend + requires ``-spice`` and automatically selected in that case, so + usually you can ignore this option. This backend has no backend + specific properties. -@item -audiodev pa,id=@var{id}[,@var{prop}[=@var{value}][,...]] -Creates a backend using PulseAudio. This backend is available on most -systems. +``-audiodev wav,id=id[,prop[=value][,...]]`` + Creates a backend that writes audio to a WAV file. -PulseAudio specific options are: + Backend specific options are: -@table @option - -@item server=@var{server} -Sets the PulseAudio @var{server} to connect to. - -@item in|out.name=@var{sink} -Use the specified source/sink for recording/playback. - -@item in|out.latency=@var{usecs} -Desired latency in microseconds. The PulseAudio server will try to honor this -value but actual latencies may be lower or higher. - -@end table - -@item -audiodev sdl,id=@var{id}[,@var{prop}[=@var{value}][,...]] -Creates a backend using SDL. This backend is available on most systems, -but you should use your platform's native backend if possible. This -backend has no backend specific properties. - -@item -audiodev spice,id=@var{id}[,@var{prop}[=@var{value}][,...]] -Creates a backend that sends audio through SPICE. This backend requires -@code{-spice} and automatically selected in that case, so usually you -can ignore this option. This backend has no backend specific -properties. - -@item -audiodev wav,id=@var{id}[,@var{prop}[=@var{value}][,...]] -Creates a backend that writes audio to a WAV file. - -Backend specific options are: - -@table @option - -@item path=@var{path} -Write recorded audio into the specified file. Default is -@code{qemu.wav}. - -@end table -ETEXI + ``path=path`` + Write recorded audio into the specified file. Default is + ``qemu.wav``. +ERST DEF("soundhw", HAS_ARG, QEMU_OPTION_soundhw, "-soundhw c1,... enable audio support\n" " and only specified sound cards (comma separated list)\n" " use '-soundhw help' to get the list of supported cards\n" " use '-soundhw all' to enable all of them\n", QEMU_ARCH_ALL) -STEXI -@item -soundhw @var{card1}[,@var{card2},...] or -soundhw all -@findex -soundhw -Enable audio and selected sound hardware. Use 'help' to print all -available sound hardware. For example: +SRST +``-soundhw card1[,card2,...] or -soundhw all`` + Enable audio and selected sound hardware. Use 'help' to print all + available sound hardware. For example: -@example -@value{qemu_system_x86} -soundhw sb16,adlib disk.img -@value{qemu_system_x86} -soundhw es1370 disk.img -@value{qemu_system_x86} -soundhw ac97 disk.img -@value{qemu_system_x86} -soundhw hda disk.img -@value{qemu_system_x86} -soundhw all disk.img -@value{qemu_system_x86} -soundhw help -@end example + .. parsed-literal:: -Note that Linux's i810_audio OSS kernel (for AC97) module might -require manually specifying clocking. + |qemu_system_x86| -soundhw sb16,adlib disk.img + |qemu_system_x86| -soundhw es1370 disk.img + |qemu_system_x86| -soundhw ac97 disk.img + |qemu_system_x86| -soundhw hda disk.img + |qemu_system_x86| -soundhw all disk.img + |qemu_system_x86| -soundhw help -@example -modprobe i810_audio clocking=48000 -@end example -ETEXI + Note that Linux's i810\_audio OSS kernel (for AC97) module might + require manually specifying clocking. + + :: + + modprobe i810_audio clocking=48000 +ERST DEF("device", HAS_ARG, QEMU_OPTION_device, "-device driver[,prop[=value][,...]]\n" @@ -712,83 +797,85 @@ DEF("device", HAS_ARG, QEMU_OPTION_device, " use '-device help' to print all possible drivers\n" " use '-device driver,help' to print all possible properties\n", QEMU_ARCH_ALL) -STEXI -@item -device @var{driver}[,@var{prop}[=@var{value}][,...]] -@findex -device -Add device @var{driver}. @var{prop}=@var{value} sets driver -properties. Valid properties depend on the driver. To get help on -possible drivers and properties, use @code{-device help} and -@code{-device @var{driver},help}. +SRST +``-device driver[,prop[=value][,...]]`` + Add device driver. prop=value sets driver properties. Valid + properties depend on the driver. To get help on possible drivers and + properties, use ``-device help`` and ``-device driver,help``. -Some drivers are: -@item -device ipmi-bmc-sim,id=@var{id}[,slave_addr=@var{val}][,sdrfile=@var{file}][,furareasize=@var{val}][,furdatafile=@var{file}][,guid=@var{uuid}] + Some drivers are: -Add an IPMI BMC. This is a simulation of a hardware management -interface processor that normally sits on a system. It provides -a watchdog and the ability to reset and power control the system. -You need to connect this to an IPMI interface to make it useful +``-device ipmi-bmc-sim,id=id[,slave_addr=val][,sdrfile=file][,furareasize=val][,furdatafile=file][,guid=uuid]`` + Add an IPMI BMC. This is a simulation of a hardware management + interface processor that normally sits on a system. It provides a + watchdog and the ability to reset and power control the system. You + need to connect this to an IPMI interface to make it useful -The IPMI slave address to use for the BMC. The default is 0x20. -This address is the BMC's address on the I2C network of management -controllers. If you don't know what this means, it is safe to ignore -it. + The IPMI slave address to use for the BMC. The default is 0x20. This + address is the BMC's address on the I2C network of management + controllers. If you don't know what this means, it is safe to ignore + it. -@table @option -@item id=@var{id} -The BMC id for interfaces to use this device. -@item slave_addr=@var{val} -Define slave address to use for the BMC. The default is 0x20. -@item sdrfile=@var{file} -file containing raw Sensor Data Records (SDR) data. The default is none. -@item fruareasize=@var{val} -size of a Field Replaceable Unit (FRU) area. The default is 1024. -@item frudatafile=@var{file} -file containing raw Field Replaceable Unit (FRU) inventory data. The default is none. -@item guid=@var{uuid} -value for the GUID for the BMC, in standard UUID format. If this is set, -get "Get GUID" command to the BMC will return it. Otherwise "Get GUID" -will return an error. -@end table + ``id=id`` + The BMC id for interfaces to use this device. -@item -device ipmi-bmc-extern,id=@var{id},chardev=@var{id}[,slave_addr=@var{val}] + ``slave_addr=val`` + Define slave address to use for the BMC. The default is 0x20. -Add a connection to an external IPMI BMC simulator. Instead of -locally emulating the BMC like the above item, instead connect -to an external entity that provides the IPMI services. + ``sdrfile=file`` + file containing raw Sensor Data Records (SDR) data. The default + is none. -A connection is made to an external BMC simulator. If you do this, it -is strongly recommended that you use the "reconnect=" chardev option -to reconnect to the simulator if the connection is lost. Note that if -this is not used carefully, it can be a security issue, as the -interface has the ability to send resets, NMIs, and power off the VM. -It's best if QEMU makes a connection to an external simulator running -on a secure port on localhost, so neither the simulator nor QEMU is -exposed to any outside network. + ``fruareasize=val`` + size of a Field Replaceable Unit (FRU) area. The default is + 1024. -See the "lanserv/README.vm" file in the OpenIPMI library for more -details on the external interface. + ``frudatafile=file`` + file containing raw Field Replaceable Unit (FRU) inventory data. + The default is none. -@item -device isa-ipmi-kcs,bmc=@var{id}[,ioport=@var{val}][,irq=@var{val}] + ``guid=uuid`` + value for the GUID for the BMC, in standard UUID format. If this + is set, get "Get GUID" command to the BMC will return it. + Otherwise "Get GUID" will return an error. -Add a KCS IPMI interafce on the ISA bus. This also adds a -corresponding ACPI and SMBIOS entries, if appropriate. +``-device ipmi-bmc-extern,id=id,chardev=id[,slave_addr=val]`` + Add a connection to an external IPMI BMC simulator. Instead of + locally emulating the BMC like the above item, instead connect to an + external entity that provides the IPMI services. -@table @option -@item bmc=@var{id} -The BMC to connect to, one of ipmi-bmc-sim or ipmi-bmc-extern above. -@item ioport=@var{val} -Define the I/O address of the interface. The default is 0xca0 for KCS. -@item irq=@var{val} -Define the interrupt to use. The default is 5. To disable interrupts, -set this to 0. -@end table + A connection is made to an external BMC simulator. If you do this, + it is strongly recommended that you use the "reconnect=" chardev + option to reconnect to the simulator if the connection is lost. Note + that if this is not used carefully, it can be a security issue, as + the interface has the ability to send resets, NMIs, and power off + the VM. It's best if QEMU makes a connection to an external + simulator running on a secure port on localhost, so neither the + simulator nor QEMU is exposed to any outside network. -@item -device isa-ipmi-bt,bmc=@var{id}[,ioport=@var{val}][,irq=@var{val}] + See the "lanserv/README.vm" file in the OpenIPMI library for more + details on the external interface. -Like the KCS interface, but defines a BT interface. The default port is -0xe4 and the default interrupt is 5. +``-device isa-ipmi-kcs,bmc=id[,ioport=val][,irq=val]`` + Add a KCS IPMI interafce on the ISA bus. This also adds a + corresponding ACPI and SMBIOS entries, if appropriate. -ETEXI + ``bmc=id`` + The BMC to connect to, one of ipmi-bmc-sim or ipmi-bmc-extern + above. + + ``ioport=val`` + Define the I/O address of the interface. The default is 0xca0 + for KCS. + + ``irq=val`` + Define the interrupt to use. The default is 5. To disable + interrupts, set this to 0. + +``-device isa-ipmi-bt,bmc=id[,ioport=val][,irq=val]`` + Like the KCS interface, but defines a BT interface. The default port + is 0xe4 and the default interrupt is 5. +ERST DEF("name", HAS_ARG, QEMU_OPTION_name, "-name string1[,process=string2][,debug-threads=on|off]\n" @@ -797,45 +884,36 @@ DEF("name", HAS_ARG, QEMU_OPTION_name, " When debug-threads is enabled, individual threads are given a separate name\n" " NOTE: The thread names are for debugging and not a stable API.\n", QEMU_ARCH_ALL) -STEXI -@item -name @var{name} -@findex -name -Sets the @var{name} of the guest. -This name will be displayed in the SDL window caption. -The @var{name} will also be used for the VNC server. -Also optionally set the top visible process name in Linux. -Naming of individual threads can also be enabled on Linux to aid debugging. -ETEXI +SRST +``-name name`` + Sets the name of the guest. This name will be displayed in the SDL + window caption. The name will also be used for the VNC server. Also + optionally set the top visible process name in Linux. Naming of + individual threads can also be enabled on Linux to aid debugging. +ERST DEF("uuid", HAS_ARG, QEMU_OPTION_uuid, "-uuid %08x-%04x-%04x-%04x-%012x\n" " specify machine UUID\n", QEMU_ARCH_ALL) -STEXI -@item -uuid @var{uuid} -@findex -uuid -Set system UUID. -ETEXI +SRST +``-uuid uuid`` + Set system UUID. +ERST -STEXI -@end table -ETEXI DEFHEADING() DEFHEADING(Block device options:) -STEXI -@table @option -ETEXI DEF("fda", HAS_ARG, QEMU_OPTION_fda, "-fda/-fdb file use 'file' as floppy disk 0/1 image\n", QEMU_ARCH_ALL) DEF("fdb", HAS_ARG, QEMU_OPTION_fdb, "", QEMU_ARCH_ALL) -STEXI -@item -fda @var{file} -@itemx -fdb @var{file} -@findex -fda -@findex -fdb -Use @var{file} as floppy disk 0/1 image (@pxref{disk_images}). -ETEXI +SRST +``-fda file`` + \ +``-fdb file`` + Use file as floppy disk 0/1 image (see + :ref:`disk_005fimages`). +ERST DEF("hda", HAS_ARG, QEMU_OPTION_hda, "-hda/-hdb file use 'file' as IDE hard disk 0/1 image\n", QEMU_ARCH_ALL) @@ -843,28 +921,27 @@ DEF("hdb", HAS_ARG, QEMU_OPTION_hdb, "", QEMU_ARCH_ALL) DEF("hdc", HAS_ARG, QEMU_OPTION_hdc, "-hdc/-hdd file use 'file' as IDE hard disk 2/3 image\n", QEMU_ARCH_ALL) DEF("hdd", HAS_ARG, QEMU_OPTION_hdd, "", QEMU_ARCH_ALL) -STEXI -@item -hda @var{file} -@itemx -hdb @var{file} -@itemx -hdc @var{file} -@itemx -hdd @var{file} -@findex -hda -@findex -hdb -@findex -hdc -@findex -hdd -Use @var{file} as hard disk 0, 1, 2 or 3 image (@pxref{disk_images}). -ETEXI +SRST +``-hda file`` + \ +``-hdb file`` + \ +``-hdc file`` + \ +``-hdd file`` + Use file as hard disk 0, 1, 2 or 3 image (see + :ref:`disk_005fimages`). +ERST DEF("cdrom", HAS_ARG, QEMU_OPTION_cdrom, "-cdrom file use 'file' as IDE cdrom image (cdrom is ide1 master)\n", QEMU_ARCH_ALL) -STEXI -@item -cdrom @var{file} -@findex -cdrom -Use @var{file} as CD-ROM image (you cannot use @option{-hdc} and -@option{-cdrom} at the same time). You can use the host CD-ROM by -using @file{/dev/cdrom} as filename (@pxref{host_drives}). -ETEXI +SRST +``-cdrom file`` + Use file as CD-ROM image (you cannot use ``-hdc`` and ``-cdrom`` at + the same time). You can use the host CD-ROM by using ``/dev/cdrom`` + as filename. +ERST DEF("blockdev", HAS_ARG, QEMU_OPTION_blockdev, "-blockdev [driver=]driver[,node-name=N][,discard=ignore|unmap]\n" @@ -873,193 +950,216 @@ DEF("blockdev", HAS_ARG, QEMU_OPTION_blockdev, " [,force-share=on|off][,detect-zeroes=on|off|unmap]\n" " [,driver specific parameters...]\n" " configure a block backend\n", QEMU_ARCH_ALL) -STEXI -@item -blockdev @var{option}[,@var{option}[,@var{option}[,...]]] -@findex -blockdev +SRST +``-blockdev option[,option[,option[,...]]]`` + Define a new block driver node. Some of the options apply to all + block drivers, other options are only accepted for a specific block + driver. See below for a list of generic options and options for the + most common block drivers. -Define a new block driver node. Some of the options apply to all block drivers, -other options are only accepted for a specific block driver. See below for a -list of generic options and options for the most common block drivers. + Options that expect a reference to another node (e.g. ``file``) can + be given in two ways. Either you specify the node name of an already + existing node (file=node-name), or you define a new node inline, + adding options for the referenced node after a dot + (file.filename=path,file.aio=native). -Options that expect a reference to another node (e.g. @code{file}) can be -given in two ways. Either you specify the node name of an already existing node -(file=@var{node-name}), or you define a new node inline, adding options -for the referenced node after a dot (file.filename=@var{path},file.aio=native). + A block driver node created with ``-blockdev`` can be used for a + guest device by specifying its node name for the ``drive`` property + in a ``-device`` argument that defines a block device. -A block driver node created with @option{-blockdev} can be used for a guest -device by specifying its node name for the @code{drive} property in a -@option{-device} argument that defines a block device. + ``Valid options for any block driver node:`` + ``driver`` + Specifies the block driver to use for the given node. -@table @option -@item Valid options for any block driver node: + ``node-name`` + This defines the name of the block driver node by which it + will be referenced later. The name must be unique, i.e. it + must not match the name of a different block driver node, or + (if you use ``-drive`` as well) the ID of a drive. -@table @code -@item driver -Specifies the block driver to use for the given node. -@item node-name -This defines the name of the block driver node by which it will be referenced -later. The name must be unique, i.e. it must not match the name of a different -block driver node, or (if you use @option{-drive} as well) the ID of a drive. + If no node name is specified, it is automatically generated. + The generated node name is not intended to be predictable + and changes between QEMU invocations. For the top level, an + explicit node name must be specified. -If no node name is specified, it is automatically generated. The generated node -name is not intended to be predictable and changes between QEMU invocations. -For the top level, an explicit node name must be specified. -@item read-only -Open the node read-only. Guest write attempts will fail. + ``read-only`` + Open the node read-only. Guest write attempts will fail. -Note that some block drivers support only read-only access, either generally or -in certain configurations. In this case, the default value -@option{read-only=off} does not work and the option must be specified -explicitly. -@item auto-read-only -If @option{auto-read-only=on} is set, QEMU may fall back to read-only usage -even when @option{read-only=off} is requested, or even switch between modes as -needed, e.g. depending on whether the image file is writable or whether a -writing user is attached to the node. -@item force-share -Override the image locking system of QEMU by forcing the node to utilize -weaker shared access for permissions where it would normally request exclusive -access. When there is the potential for multiple instances to have the same -file open (whether this invocation of QEMU is the first or the second -instance), both instances must permit shared access for the second instance to -succeed at opening the file. + Note that some block drivers support only read-only access, + either generally or in certain configurations. In this case, + the default value ``read-only=off`` does not work and the + option must be specified explicitly. -Enabling @option{force-share=on} requires @option{read-only=on}. -@item cache.direct -The host page cache can be avoided with @option{cache.direct=on}. This will -attempt to do disk IO directly to the guest's memory. QEMU may still perform an -internal copy of the data. -@item cache.no-flush -In case you don't care about data integrity over host failures, you can use -@option{cache.no-flush=on}. This option tells QEMU that it never needs to write -any data to the disk but can instead keep things in cache. If anything goes -wrong, like your host losing power, the disk storage getting disconnected -accidentally, etc. your image will most probably be rendered unusable. -@item discard=@var{discard} -@var{discard} is one of "ignore" (or "off") or "unmap" (or "on") and controls -whether @code{discard} (also known as @code{trim} or @code{unmap}) requests are -ignored or passed to the filesystem. Some machine types may not support -discard requests. -@item detect-zeroes=@var{detect-zeroes} -@var{detect-zeroes} is "off", "on" or "unmap" and enables the automatic -conversion of plain zero writes by the OS to driver specific optimized -zero write commands. You may even choose "unmap" if @var{discard} is set -to "unmap" to allow a zero write to be converted to an @code{unmap} operation. -@end table + ``auto-read-only`` + If ``auto-read-only=on`` is set, QEMU may fall back to + read-only usage even when ``read-only=off`` is requested, or + even switch between modes as needed, e.g. depending on + whether the image file is writable or whether a writing user + is attached to the node. -@item Driver-specific options for @code{file} + ``force-share`` + Override the image locking system of QEMU by forcing the + node to utilize weaker shared access for permissions where + it would normally request exclusive access. When there is + the potential for multiple instances to have the same file + open (whether this invocation of QEMU is the first or the + second instance), both instances must permit shared access + for the second instance to succeed at opening the file. -This is the protocol-level block driver for accessing regular files. + Enabling ``force-share=on`` requires ``read-only=on``. -@table @code -@item filename -The path to the image file in the local filesystem -@item aio -Specifies the AIO backend (threads/native, default: threads) -@item locking -Specifies whether the image file is protected with Linux OFD / POSIX locks. The -default is to use the Linux Open File Descriptor API if available, otherwise no -lock is applied. (auto/on/off, default: auto) -@end table -Example: -@example --blockdev driver=file,node-name=disk,filename=disk.img -@end example + ``cache.direct`` + The host page cache can be avoided with ``cache.direct=on``. + This will attempt to do disk IO directly to the guest's + memory. QEMU may still perform an internal copy of the data. -@item Driver-specific options for @code{raw} + ``cache.no-flush`` + In case you don't care about data integrity over host + failures, you can use ``cache.no-flush=on``. This option + tells QEMU that it never needs to write any data to the disk + but can instead keep things in cache. If anything goes + wrong, like your host losing power, the disk storage getting + disconnected accidentally, etc. your image will most + probably be rendered unusable. -This is the image format block driver for raw images. It is usually -stacked on top of a protocol level block driver such as @code{file}. + ``discard=discard`` + discard is one of "ignore" (or "off") or "unmap" (or "on") + and controls whether ``discard`` (also known as ``trim`` or + ``unmap``) requests are ignored or passed to the filesystem. + Some machine types may not support discard requests. -@table @code -@item file -Reference to or definition of the data source block driver node -(e.g. a @code{file} driver node) -@end table -Example 1: -@example --blockdev driver=file,node-name=disk_file,filename=disk.img --blockdev driver=raw,node-name=disk,file=disk_file -@end example -Example 2: -@example --blockdev driver=raw,node-name=disk,file.driver=file,file.filename=disk.img -@end example + ``detect-zeroes=detect-zeroes`` + detect-zeroes is "off", "on" or "unmap" and enables the + automatic conversion of plain zero writes by the OS to + driver specific optimized zero write commands. You may even + choose "unmap" if discard is set to "unmap" to allow a zero + write to be converted to an ``unmap`` operation. -@item Driver-specific options for @code{qcow2} + ``Driver-specific options for file`` + This is the protocol-level block driver for accessing regular + files. -This is the image format block driver for qcow2 images. It is usually -stacked on top of a protocol level block driver such as @code{file}. + ``filename`` + The path to the image file in the local filesystem -@table @code -@item file -Reference to or definition of the data source block driver node -(e.g. a @code{file} driver node) + ``aio`` + Specifies the AIO backend (threads/native, default: threads) -@item backing -Reference to or definition of the backing file block device (default is taken -from the image file). It is allowed to pass @code{null} here in order to disable -the default backing file. + ``locking`` + Specifies whether the image file is protected with Linux OFD + / POSIX locks. The default is to use the Linux Open File + Descriptor API if available, otherwise no lock is applied. + (auto/on/off, default: auto) -@item lazy-refcounts -Whether to enable the lazy refcounts feature (on/off; default is taken from the -image file) + Example: -@item cache-size -The maximum total size of the L2 table and refcount block caches in bytes -(default: the sum of l2-cache-size and refcount-cache-size) + :: -@item l2-cache-size -The maximum size of the L2 table cache in bytes -(default: if cache-size is not specified - 32M on Linux platforms, and 8M on -non-Linux platforms; otherwise, as large as possible within the cache-size, -while permitting the requested or the minimal refcount cache size) + -blockdev driver=file,node-name=disk,filename=disk.img -@item refcount-cache-size -The maximum size of the refcount block cache in bytes -(default: 4 times the cluster size; or if cache-size is specified, the part of -it which is not used for the L2 cache) + ``Driver-specific options for raw`` + This is the image format block driver for raw images. It is + usually stacked on top of a protocol level block driver such as + ``file``. -@item cache-clean-interval -Clean unused entries in the L2 and refcount caches. The interval is in seconds. -The default value is 600 on supporting platforms, and 0 on other platforms. -Setting it to 0 disables this feature. + ``file`` + Reference to or definition of the data source block driver + node (e.g. a ``file`` driver node) -@item pass-discard-request -Whether discard requests to the qcow2 device should be forwarded to the data -source (on/off; default: on if discard=unmap is specified, off otherwise) + Example 1: -@item pass-discard-snapshot -Whether discard requests for the data source should be issued when a snapshot -operation (e.g. deleting a snapshot) frees clusters in the qcow2 file (on/off; -default: on) + :: -@item pass-discard-other -Whether discard requests for the data source should be issued on other -occasions where a cluster gets freed (on/off; default: off) + -blockdev driver=file,node-name=disk_file,filename=disk.img + -blockdev driver=raw,node-name=disk,file=disk_file -@item overlap-check -Which overlap checks to perform for writes to the image -(none/constant/cached/all; default: cached). For details or finer -granularity control refer to the QAPI documentation of @code{blockdev-add}. -@end table + Example 2: -Example 1: -@example --blockdev driver=file,node-name=my_file,filename=/tmp/disk.qcow2 --blockdev driver=qcow2,node-name=hda,file=my_file,overlap-check=none,cache-size=16777216 -@end example -Example 2: -@example --blockdev driver=qcow2,node-name=disk,file.driver=http,file.filename=http://example.com/image.qcow2 -@end example + :: -@item Driver-specific options for other drivers -Please refer to the QAPI documentation of the @code{blockdev-add} QMP command. + -blockdev driver=raw,node-name=disk,file.driver=file,file.filename=disk.img -@end table + ``Driver-specific options for qcow2`` + This is the image format block driver for qcow2 images. It is + usually stacked on top of a protocol level block driver such as + ``file``. -ETEXI + ``file`` + Reference to or definition of the data source block driver + node (e.g. a ``file`` driver node) + + ``backing`` + Reference to or definition of the backing file block device + (default is taken from the image file). It is allowed to + pass ``null`` here in order to disable the default backing + file. + + ``lazy-refcounts`` + Whether to enable the lazy refcounts feature (on/off; + default is taken from the image file) + + ``cache-size`` + The maximum total size of the L2 table and refcount block + caches in bytes (default: the sum of l2-cache-size and + refcount-cache-size) + + ``l2-cache-size`` + The maximum size of the L2 table cache in bytes (default: if + cache-size is not specified - 32M on Linux platforms, and 8M + on non-Linux platforms; otherwise, as large as possible + within the cache-size, while permitting the requested or the + minimal refcount cache size) + + ``refcount-cache-size`` + The maximum size of the refcount block cache in bytes + (default: 4 times the cluster size; or if cache-size is + specified, the part of it which is not used for the L2 + cache) + + ``cache-clean-interval`` + Clean unused entries in the L2 and refcount caches. The + interval is in seconds. The default value is 600 on + supporting platforms, and 0 on other platforms. Setting it + to 0 disables this feature. + + ``pass-discard-request`` + Whether discard requests to the qcow2 device should be + forwarded to the data source (on/off; default: on if + discard=unmap is specified, off otherwise) + + ``pass-discard-snapshot`` + Whether discard requests for the data source should be + issued when a snapshot operation (e.g. deleting a snapshot) + frees clusters in the qcow2 file (on/off; default: on) + + ``pass-discard-other`` + Whether discard requests for the data source should be + issued on other occasions where a cluster gets freed + (on/off; default: off) + + ``overlap-check`` + Which overlap checks to perform for writes to the image + (none/constant/cached/all; default: cached). For details or + finer granularity control refer to the QAPI documentation of + ``blockdev-add``. + + Example 1: + + :: + + -blockdev driver=file,node-name=my_file,filename=/tmp/disk.qcow2 + -blockdev driver=qcow2,node-name=hda,file=my_file,overlap-check=none,cache-size=16777216 + + Example 2: + + :: + + -blockdev driver=qcow2,node-name=disk,file.driver=http,file.filename=http://example.com/image.qcow2 + + ``Driver-specific options for other drivers`` + Please refer to the QAPI documentation of the ``blockdev-add`` + QMP command. +ERST DEF("drive", HAS_ARG, QEMU_OPTION_drive, "-drive [file=file][,if=type][,bus=n][,unit=m][,media=d][,index=i]\n" @@ -1075,206 +1175,230 @@ DEF("drive", HAS_ARG, QEMU_OPTION_drive, " [[,iops_size=is]]\n" " [[,group=g]]\n" " use 'file' as a drive image\n", QEMU_ARCH_ALL) -STEXI -@item -drive @var{option}[,@var{option}[,@var{option}[,...]]] -@findex -drive +SRST +``-drive option[,option[,option[,...]]]`` + Define a new drive. This includes creating a block driver node (the + backend) as well as a guest device, and is mostly a shortcut for + defining the corresponding ``-blockdev`` and ``-device`` options. -Define a new drive. This includes creating a block driver node (the backend) as -well as a guest device, and is mostly a shortcut for defining the corresponding -@option{-blockdev} and @option{-device} options. + ``-drive`` accepts all options that are accepted by ``-blockdev``. + In addition, it knows the following options: -@option{-drive} accepts all options that are accepted by @option{-blockdev}. In -addition, it knows the following options: + ``file=file`` + This option defines which disk image (see + :ref:`disk_005fimages`) to use with this drive. If + the filename contains comma, you must double it (for instance, + "file=my,,file" to use file "my,file"). -@table @option -@item file=@var{file} -This option defines which disk image (@pxref{disk_images}) to use with -this drive. If the filename contains comma, you must double it -(for instance, "file=my,,file" to use file "my,file"). + Special files such as iSCSI devices can be specified using + protocol specific URLs. See the section for "Device URL Syntax" + for more information. -Special files such as iSCSI devices can be specified using protocol -specific URLs. See the section for "Device URL Syntax" for more information. -@item if=@var{interface} -This option defines on which type on interface the drive is connected. -Available types are: ide, scsi, sd, mtd, floppy, pflash, virtio, none. -@item bus=@var{bus},unit=@var{unit} -These options define where is connected the drive by defining the bus number and -the unit id. -@item index=@var{index} -This option defines where is connected the drive by using an index in the list -of available connectors of a given interface type. -@item media=@var{media} -This option defines the type of the media: disk or cdrom. -@item snapshot=@var{snapshot} -@var{snapshot} is "on" or "off" and controls snapshot mode for the given drive -(see @option{-snapshot}). -@item cache=@var{cache} -@var{cache} is "none", "writeback", "unsafe", "directsync" or "writethrough" -and controls how the host cache is used to access block data. This is a -shortcut that sets the @option{cache.direct} and @option{cache.no-flush} -options (as in @option{-blockdev}), and additionally @option{cache.writeback}, -which provides a default for the @option{write-cache} option of block guest -devices (as in @option{-device}). The modes correspond to the following -settings: + ``if=interface`` + This option defines on which type on interface the drive is + connected. Available types are: ide, scsi, sd, mtd, floppy, + pflash, virtio, none. -@c Our texi2pod.pl script doesn't support @multitable, so fall back to using -@c plain ASCII art (well, UTF-8 art really). This looks okay both in the manpage -@c and the HTML output. -@example -@ │ cache.writeback cache.direct cache.no-flush -─────────────┼───────────────────────────────────────────────── -writeback │ on off off -none │ on on off -writethrough │ off off off -directsync │ off on off -unsafe │ on off on -@end example + ``bus=bus,unit=unit`` + These options define where is connected the drive by defining + the bus number and the unit id. -The default mode is @option{cache=writeback}. + ``index=index`` + This option defines where is connected the drive by using an + index in the list of available connectors of a given interface + type. -@item aio=@var{aio} -@var{aio} is "threads", or "native" and selects between pthread based disk I/O and native Linux AIO. -@item format=@var{format} -Specify which disk @var{format} will be used rather than detecting -the format. Can be used to specify format=raw to avoid interpreting -an untrusted format header. -@item werror=@var{action},rerror=@var{action} -Specify which @var{action} to take on write and read errors. Valid actions are: -"ignore" (ignore the error and try to continue), "stop" (pause QEMU), -"report" (report the error to the guest), "enospc" (pause QEMU only if the -host disk is full; report the error to the guest otherwise). -The default setting is @option{werror=enospc} and @option{rerror=report}. -@item copy-on-read=@var{copy-on-read} -@var{copy-on-read} is "on" or "off" and enables whether to copy read backing -file sectors into the image file. -@item bps=@var{b},bps_rd=@var{r},bps_wr=@var{w} -Specify bandwidth throttling limits in bytes per second, either for all request -types or for reads or writes only. Small values can lead to timeouts or hangs -inside the guest. A safe minimum for disks is 2 MB/s. -@item bps_max=@var{bm},bps_rd_max=@var{rm},bps_wr_max=@var{wm} -Specify bursts in bytes per second, either for all request types or for reads -or writes only. Bursts allow the guest I/O to spike above the limit -temporarily. -@item iops=@var{i},iops_rd=@var{r},iops_wr=@var{w} -Specify request rate limits in requests per second, either for all request -types or for reads or writes only. -@item iops_max=@var{bm},iops_rd_max=@var{rm},iops_wr_max=@var{wm} -Specify bursts in requests per second, either for all request types or for reads -or writes only. Bursts allow the guest I/O to spike above the limit -temporarily. -@item iops_size=@var{is} -Let every @var{is} bytes of a request count as a new request for iops -throttling purposes. Use this option to prevent guests from circumventing iops -limits by sending fewer but larger requests. -@item group=@var{g} -Join a throttling quota group with given name @var{g}. All drives that are -members of the same group are accounted for together. Use this option to -prevent guests from circumventing throttling limits by using many small disks -instead of a single larger disk. -@end table + ``media=media`` + This option defines the type of the media: disk or cdrom. -By default, the @option{cache.writeback=on} mode is used. It will report data -writes as completed as soon as the data is present in the host page cache. -This is safe as long as your guest OS makes sure to correctly flush disk caches -where needed. If your guest OS does not handle volatile disk write caches -correctly and your host crashes or loses power, then the guest may experience -data corruption. + ``snapshot=snapshot`` + snapshot is "on" or "off" and controls snapshot mode for the + given drive (see ``-snapshot``). -For such guests, you should consider using @option{cache.writeback=off}. This -means that the host page cache will be used to read and write data, but write -notification will be sent to the guest only after QEMU has made sure to flush -each write to the disk. Be aware that this has a major impact on performance. + ``cache=cache`` + cache is "none", "writeback", "unsafe", "directsync" or + "writethrough" and controls how the host cache is used to access + block data. This is a shortcut that sets the ``cache.direct`` + and ``cache.no-flush`` options (as in ``-blockdev``), and + additionally ``cache.writeback``, which provides a default for + the ``write-cache`` option of block guest devices (as in + ``-device``). The modes correspond to the following settings: -When using the @option{-snapshot} option, unsafe caching is always used. + ============= =============== ============ ============== + \ cache.writeback cache.direct cache.no-flush + ============= =============== ============ ============== + writeback on off off + none on on off + writethrough off off off + directsync off on off + unsafe on off on + ============= =============== ============ ============== -Copy-on-read avoids accessing the same backing file sectors repeatedly and is -useful when the backing file is over a slow network. By default copy-on-read -is off. + The default mode is ``cache=writeback``. -Instead of @option{-cdrom} you can use: -@example -@value{qemu_system} -drive file=file,index=2,media=cdrom -@end example + ``aio=aio`` + aio is "threads", or "native" and selects between pthread based + disk I/O and native Linux AIO. -Instead of @option{-hda}, @option{-hdb}, @option{-hdc}, @option{-hdd}, you can -use: -@example -@value{qemu_system} -drive file=file,index=0,media=disk -@value{qemu_system} -drive file=file,index=1,media=disk -@value{qemu_system} -drive file=file,index=2,media=disk -@value{qemu_system} -drive file=file,index=3,media=disk -@end example + ``format=format`` + Specify which disk format will be used rather than detecting the + format. Can be used to specify format=raw to avoid interpreting + an untrusted format header. -You can open an image using pre-opened file descriptors from an fd set: -@example -@value{qemu_system} \ - -add-fd fd=3,set=2,opaque="rdwr:/path/to/file" \ - -add-fd fd=4,set=2,opaque="rdonly:/path/to/file" \ - -drive file=/dev/fdset/2,index=0,media=disk -@end example + ``werror=action,rerror=action`` + Specify which action to take on write and read errors. Valid + actions are: "ignore" (ignore the error and try to continue), + "stop" (pause QEMU), "report" (report the error to the guest), + "enospc" (pause QEMU only if the host disk is full; report the + error to the guest otherwise). The default setting is + ``werror=enospc`` and ``rerror=report``. -You can connect a CDROM to the slave of ide0: -@example -@value{qemu_system_x86} -drive file=file,if=ide,index=1,media=cdrom -@end example + ``copy-on-read=copy-on-read`` + copy-on-read is "on" or "off" and enables whether to copy read + backing file sectors into the image file. -If you don't specify the "file=" argument, you define an empty drive: -@example -@value{qemu_system_x86} -drive if=ide,index=1,media=cdrom -@end example + ``bps=b,bps_rd=r,bps_wr=w`` + Specify bandwidth throttling limits in bytes per second, either + for all request types or for reads or writes only. Small values + can lead to timeouts or hangs inside the guest. A safe minimum + for disks is 2 MB/s. -Instead of @option{-fda}, @option{-fdb}, you can use: -@example -@value{qemu_system_x86} -drive file=file,index=0,if=floppy -@value{qemu_system_x86} -drive file=file,index=1,if=floppy -@end example + ``bps_max=bm,bps_rd_max=rm,bps_wr_max=wm`` + Specify bursts in bytes per second, either for all request types + or for reads or writes only. Bursts allow the guest I/O to spike + above the limit temporarily. -By default, @var{interface} is "ide" and @var{index} is automatically -incremented: -@example -@value{qemu_system_x86} -drive file=a -drive file=b" -@end example -is interpreted like: -@example -@value{qemu_system_x86} -hda a -hdb b -@end example -ETEXI + ``iops=i,iops_rd=r,iops_wr=w`` + Specify request rate limits in requests per second, either for + all request types or for reads or writes only. + + ``iops_max=bm,iops_rd_max=rm,iops_wr_max=wm`` + Specify bursts in requests per second, either for all request + types or for reads or writes only. Bursts allow the guest I/O to + spike above the limit temporarily. + + ``iops_size=is`` + Let every is bytes of a request count as a new request for iops + throttling purposes. Use this option to prevent guests from + circumventing iops limits by sending fewer but larger requests. + + ``group=g`` + Join a throttling quota group with given name g. All drives that + are members of the same group are accounted for together. Use + this option to prevent guests from circumventing throttling + limits by using many small disks instead of a single larger + disk. + + By default, the ``cache.writeback=on`` mode is used. It will report + data writes as completed as soon as the data is present in the host + page cache. This is safe as long as your guest OS makes sure to + correctly flush disk caches where needed. If your guest OS does not + handle volatile disk write caches correctly and your host crashes or + loses power, then the guest may experience data corruption. + + For such guests, you should consider using ``cache.writeback=off``. + This means that the host page cache will be used to read and write + data, but write notification will be sent to the guest only after + QEMU has made sure to flush each write to the disk. Be aware that + this has a major impact on performance. + + When using the ``-snapshot`` option, unsafe caching is always used. + + Copy-on-read avoids accessing the same backing file sectors + repeatedly and is useful when the backing file is over a slow + network. By default copy-on-read is off. + + Instead of ``-cdrom`` you can use: + + .. parsed-literal:: + + |qemu_system| -drive file=file,index=2,media=cdrom + + Instead of ``-hda``, ``-hdb``, ``-hdc``, ``-hdd``, you can use: + + .. parsed-literal:: + + |qemu_system| -drive file=file,index=0,media=disk + |qemu_system| -drive file=file,index=1,media=disk + |qemu_system| -drive file=file,index=2,media=disk + |qemu_system| -drive file=file,index=3,media=disk + + You can open an image using pre-opened file descriptors from an fd + set: + + .. parsed-literal:: + + |qemu_system| \ + -add-fd fd=3,set=2,opaque="rdwr:/path/to/file" \ + -add-fd fd=4,set=2,opaque="rdonly:/path/to/file" \ + -drive file=/dev/fdset/2,index=0,media=disk + + You can connect a CDROM to the slave of ide0: + + .. parsed-literal:: + + |qemu_system_x86| -drive file=file,if=ide,index=1,media=cdrom + + If you don't specify the "file=" argument, you define an empty + drive: + + .. parsed-literal:: + + |qemu_system_x86| -drive if=ide,index=1,media=cdrom + + Instead of ``-fda``, ``-fdb``, you can use: + + .. parsed-literal:: + + |qemu_system_x86| -drive file=file,index=0,if=floppy + |qemu_system_x86| -drive file=file,index=1,if=floppy + + By default, interface is "ide" and index is automatically + incremented: + + .. parsed-literal:: + + |qemu_system_x86| -drive file=a -drive file=b" + + is interpreted like: + + .. parsed-literal:: + + |qemu_system_x86| -hda a -hdb b +ERST DEF("mtdblock", HAS_ARG, QEMU_OPTION_mtdblock, "-mtdblock file use 'file' as on-board Flash memory image\n", QEMU_ARCH_ALL) -STEXI -@item -mtdblock @var{file} -@findex -mtdblock -Use @var{file} as on-board Flash memory image. -ETEXI +SRST +``-mtdblock file`` + Use file as on-board Flash memory image. +ERST DEF("sd", HAS_ARG, QEMU_OPTION_sd, "-sd file use 'file' as SecureDigital card image\n", QEMU_ARCH_ALL) -STEXI -@item -sd @var{file} -@findex -sd -Use @var{file} as SecureDigital card image. -ETEXI +SRST +``-sd file`` + Use file as SecureDigital card image. +ERST DEF("pflash", HAS_ARG, QEMU_OPTION_pflash, "-pflash file use 'file' as a parallel flash image\n", QEMU_ARCH_ALL) -STEXI -@item -pflash @var{file} -@findex -pflash -Use @var{file} as a parallel flash image. -ETEXI +SRST +``-pflash file`` + Use file as a parallel flash image. +ERST DEF("snapshot", 0, QEMU_OPTION_snapshot, "-snapshot write to temporary files instead of disk image files\n", QEMU_ARCH_ALL) -STEXI -@item -snapshot -@findex -snapshot -Write to temporary files instead of disk image files. In this case, -the raw disk image you use is not written back. You can however force -the write back by pressing @key{C-a s} (@pxref{disk_images}). -ETEXI +SRST +``-snapshot`` + Write to temporary files instead of disk image files. In this case, + the raw disk image you use is not written back. You can however + force the write back by pressing C-a s (see + :ref:`disk_005fimages`). +ERST DEF("fsdev", HAS_ARG, QEMU_OPTION_fsdev, "-fsdev local,id=id,path=path,security_model=mapped-xattr|mapped-file|passthrough|none\n" @@ -1289,93 +1413,118 @@ DEF("fsdev", HAS_ARG, QEMU_OPTION_fsdev, "-fsdev synth,id=id\n", QEMU_ARCH_ALL) -STEXI +SRST +``-fsdev local,id=id,path=path,security_model=security_model [,writeout=writeout][,readonly][,fmode=fmode][,dmode=dmode] [,throttling.option=value[,throttling.option=value[,...]]]`` + \ +``-fsdev proxy,id=id,socket=socket[,writeout=writeout][,readonly]`` + \ +``-fsdev proxy,id=id,sock_fd=sock_fd[,writeout=writeout][,readonly]`` + \ +``-fsdev synth,id=id[,readonly]`` + Define a new file system device. Valid options are: -@item -fsdev local,id=@var{id},path=@var{path},security_model=@var{security_model} [,writeout=@var{writeout}][,readonly][,fmode=@var{fmode}][,dmode=@var{dmode}] [,throttling.@var{option}=@var{value}[,throttling.@var{option}=@var{value}[,...]]] -@itemx -fsdev proxy,id=@var{id},socket=@var{socket}[,writeout=@var{writeout}][,readonly] -@itemx -fsdev proxy,id=@var{id},sock_fd=@var{sock_fd}[,writeout=@var{writeout}][,readonly] -@itemx -fsdev synth,id=@var{id}[,readonly] -@findex -fsdev -Define a new file system device. Valid options are: -@table @option -@item local -Accesses to the filesystem are done by QEMU. -@item proxy -Accesses to the filesystem are done by virtfs-proxy-helper(1). -@item synth -Synthetic filesystem, only used by QTests. -@item id=@var{id} -Specifies identifier for this device. -@item path=@var{path} -Specifies the export path for the file system device. Files under -this path will be available to the 9p client on the guest. -@item security_model=@var{security_model} -Specifies the security model to be used for this export path. -Supported security models are "passthrough", "mapped-xattr", "mapped-file" and "none". -In "passthrough" security model, files are stored using the same -credentials as they are created on the guest. This requires QEMU -to run as root. In "mapped-xattr" security model, some of the file -attributes like uid, gid, mode bits and link target are stored as -file attributes. For "mapped-file" these attributes are stored in the -hidden .virtfs_metadata directory. Directories exported by this security model cannot -interact with other unix tools. "none" security model is same as -passthrough except the sever won't report failures if it fails to -set file attributes like ownership. Security model is mandatory -only for local fsdriver. Other fsdrivers (like proxy) don't take -security model as a parameter. -@item writeout=@var{writeout} -This is an optional argument. The only supported value is "immediate". -This means that host page cache will be used to read and write data but -write notification will be sent to the guest only when the data has been -reported as written by the storage subsystem. -@item readonly -Enables exporting 9p share as a readonly mount for guests. By default -read-write access is given. -@item socket=@var{socket} -Enables proxy filesystem driver to use passed socket file for communicating -with virtfs-proxy-helper(1). -@item sock_fd=@var{sock_fd} -Enables proxy filesystem driver to use passed socket descriptor for -communicating with virtfs-proxy-helper(1). Usually a helper like libvirt -will create socketpair and pass one of the fds as sock_fd. -@item fmode=@var{fmode} -Specifies the default mode for newly created files on the host. Works only -with security models "mapped-xattr" and "mapped-file". -@item dmode=@var{dmode} -Specifies the default mode for newly created directories on the host. Works -only with security models "mapped-xattr" and "mapped-file". -@item throttling.bps-total=@var{b},throttling.bps-read=@var{r},throttling.bps-write=@var{w} -Specify bandwidth throttling limits in bytes per second, either for all request -types or for reads or writes only. -@item throttling.bps-total-max=@var{bm},bps-read-max=@var{rm},bps-write-max=@var{wm} -Specify bursts in bytes per second, either for all request types or for reads -or writes only. Bursts allow the guest I/O to spike above the limit -temporarily. -@item throttling.iops-total=@var{i},throttling.iops-read=@var{r}, throttling.iops-write=@var{w} -Specify request rate limits in requests per second, either for all request -types or for reads or writes only. -@item throttling.iops-total-max=@var{im},throttling.iops-read-max=@var{irm}, throttling.iops-write-max=@var{iwm} -Specify bursts in requests per second, either for all request types or for reads -or writes only. Bursts allow the guest I/O to spike above the limit temporarily. -@item throttling.iops-size=@var{is} -Let every @var{is} bytes of a request count as a new request for iops -throttling purposes. -@end table + ``local`` + Accesses to the filesystem are done by QEMU. --fsdev option is used along with -device driver "virtio-9p-...". -@item -device virtio-9p-@var{type},fsdev=@var{id},mount_tag=@var{mount_tag} -Options for virtio-9p-... driver are: -@table @option -@item @var{type} -Specifies the variant to be used. Supported values are "pci", "ccw" or "device", -depending on the machine type. -@item fsdev=@var{id} -Specifies the id value specified along with -fsdev option. -@item mount_tag=@var{mount_tag} -Specifies the tag name to be used by the guest to mount this export point. -@end table + ``proxy`` + Accesses to the filesystem are done by virtfs-proxy-helper(1). -ETEXI + ``synth`` + Synthetic filesystem, only used by QTests. + + ``id=id`` + Specifies identifier for this device. + + ``path=path`` + Specifies the export path for the file system device. Files + under this path will be available to the 9p client on the guest. + + ``security_model=security_model`` + Specifies the security model to be used for this export path. + Supported security models are "passthrough", "mapped-xattr", + "mapped-file" and "none". In "passthrough" security model, files + are stored using the same credentials as they are created on the + guest. This requires QEMU to run as root. In "mapped-xattr" + security model, some of the file attributes like uid, gid, mode + bits and link target are stored as file attributes. For + "mapped-file" these attributes are stored in the hidden + .virtfs\_metadata directory. Directories exported by this + security model cannot interact with other unix tools. "none" + security model is same as passthrough except the sever won't + report failures if it fails to set file attributes like + ownership. Security model is mandatory only for local fsdriver. + Other fsdrivers (like proxy) don't take security model as a + parameter. + + ``writeout=writeout`` + This is an optional argument. The only supported value is + "immediate". This means that host page cache will be used to + read and write data but write notification will be sent to the + guest only when the data has been reported as written by the + storage subsystem. + + ``readonly`` + Enables exporting 9p share as a readonly mount for guests. By + default read-write access is given. + + ``socket=socket`` + Enables proxy filesystem driver to use passed socket file for + communicating with virtfs-proxy-helper(1). + + ``sock_fd=sock_fd`` + Enables proxy filesystem driver to use passed socket descriptor + for communicating with virtfs-proxy-helper(1). Usually a helper + like libvirt will create socketpair and pass one of the fds as + sock\_fd. + + ``fmode=fmode`` + Specifies the default mode for newly created files on the host. + Works only with security models "mapped-xattr" and + "mapped-file". + + ``dmode=dmode`` + Specifies the default mode for newly created directories on the + host. Works only with security models "mapped-xattr" and + "mapped-file". + + ``throttling.bps-total=b,throttling.bps-read=r,throttling.bps-write=w`` + Specify bandwidth throttling limits in bytes per second, either + for all request types or for reads or writes only. + + ``throttling.bps-total-max=bm,bps-read-max=rm,bps-write-max=wm`` + Specify bursts in bytes per second, either for all request types + or for reads or writes only. Bursts allow the guest I/O to spike + above the limit temporarily. + + ``throttling.iops-total=i,throttling.iops-read=r, throttling.iops-write=w`` + Specify request rate limits in requests per second, either for + all request types or for reads or writes only. + + ``throttling.iops-total-max=im,throttling.iops-read-max=irm, throttling.iops-write-max=iwm`` + Specify bursts in requests per second, either for all request + types or for reads or writes only. Bursts allow the guest I/O to + spike above the limit temporarily. + + ``throttling.iops-size=is`` + Let every is bytes of a request count as a new request for iops + throttling purposes. + + -fsdev option is used along with -device driver "virtio-9p-...". + +``-device virtio-9p-type,fsdev=id,mount_tag=mount_tag`` + Options for virtio-9p-... driver are: + + ``type`` + Specifies the variant to be used. Supported values are "pci", + "ccw" or "device", depending on the machine type. + + ``fsdev=id`` + Specifies the id value specified along with -fsdev option. + + ``mount_tag=mount_tag`` + Specifies the tag name to be used by the guest to mount this + export point. +ERST DEF("virtfs", HAS_ARG, QEMU_OPTION_virtfs, "-virtfs local,path=path,mount_tag=tag,security_model=mapped-xattr|mapped-file|passthrough|none\n" @@ -1385,98 +1534,113 @@ DEF("virtfs", HAS_ARG, QEMU_OPTION_virtfs, "-virtfs synth,mount_tag=tag[,id=id][,readonly]\n", QEMU_ARCH_ALL) -STEXI +SRST +``-virtfs local,path=path,mount_tag=mount_tag ,security_model=security_model[,writeout=writeout][,readonly] [,fmode=fmode][,dmode=dmode][,multidevs=multidevs]`` + \ +``-virtfs proxy,socket=socket,mount_tag=mount_tag [,writeout=writeout][,readonly]`` + \ +``-virtfs proxy,sock_fd=sock_fd,mount_tag=mount_tag [,writeout=writeout][,readonly]`` + \ +``-virtfs synth,mount_tag=mount_tag`` + Define a new filesystem device and expose it to the guest using a + virtio-9p-device. The general form of a Virtual File system + pass-through options are: -@item -virtfs local,path=@var{path},mount_tag=@var{mount_tag} ,security_model=@var{security_model}[,writeout=@var{writeout}][,readonly] [,fmode=@var{fmode}][,dmode=@var{dmode}][,multidevs=@var{multidevs}] -@itemx -virtfs proxy,socket=@var{socket},mount_tag=@var{mount_tag} [,writeout=@var{writeout}][,readonly] -@itemx -virtfs proxy,sock_fd=@var{sock_fd},mount_tag=@var{mount_tag} [,writeout=@var{writeout}][,readonly] -@itemx -virtfs synth,mount_tag=@var{mount_tag} -@findex -virtfs + ``local`` + Accesses to the filesystem are done by QEMU. -Define a new filesystem device and expose it to the guest using a virtio-9p-device. The general form of a Virtual File system pass-through options are: -@table @option -@item local -Accesses to the filesystem are done by QEMU. -@item proxy -Accesses to the filesystem are done by virtfs-proxy-helper(1). -@item synth -Synthetic filesystem, only used by QTests. -@item id=@var{id} -Specifies identifier for the filesystem device -@item path=@var{path} -Specifies the export path for the file system device. Files under -this path will be available to the 9p client on the guest. -@item security_model=@var{security_model} -Specifies the security model to be used for this export path. -Supported security models are "passthrough", "mapped-xattr", "mapped-file" and "none". -In "passthrough" security model, files are stored using the same -credentials as they are created on the guest. This requires QEMU -to run as root. In "mapped-xattr" security model, some of the file -attributes like uid, gid, mode bits and link target are stored as -file attributes. For "mapped-file" these attributes are stored in the -hidden .virtfs_metadata directory. Directories exported by this security model cannot -interact with other unix tools. "none" security model is same as -passthrough except the sever won't report failures if it fails to -set file attributes like ownership. Security model is mandatory only -for local fsdriver. Other fsdrivers (like proxy) don't take security -model as a parameter. -@item writeout=@var{writeout} -This is an optional argument. The only supported value is "immediate". -This means that host page cache will be used to read and write data but -write notification will be sent to the guest only when the data has been -reported as written by the storage subsystem. -@item readonly -Enables exporting 9p share as a readonly mount for guests. By default -read-write access is given. -@item socket=@var{socket} -Enables proxy filesystem driver to use passed socket file for -communicating with virtfs-proxy-helper(1). Usually a helper like libvirt -will create socketpair and pass one of the fds as sock_fd. -@item sock_fd -Enables proxy filesystem driver to use passed 'sock_fd' as the socket -descriptor for interfacing with virtfs-proxy-helper(1). -@item fmode=@var{fmode} -Specifies the default mode for newly created files on the host. Works only -with security models "mapped-xattr" and "mapped-file". -@item dmode=@var{dmode} -Specifies the default mode for newly created directories on the host. Works -only with security models "mapped-xattr" and "mapped-file". -@item mount_tag=@var{mount_tag} -Specifies the tag name to be used by the guest to mount this export point. -@item multidevs=@var{multidevs} -Specifies how to deal with multiple devices being shared with a 9p export. -Supported behaviours are either "remap", "forbid" or "warn". The latter is -the default behaviour on which virtfs 9p expects only one device to be -shared with the same export, and if more than one device is shared and -accessed via the same 9p export then only a warning message is logged -(once) by qemu on host side. In order to avoid file ID collisions on guest -you should either create a separate virtfs export for each device to be -shared with guests (recommended way) or you might use "remap" instead which -allows you to share multiple devices with only one export instead, which is -achieved by remapping the original inode numbers from host to guest in a -way that would prevent such collisions. Remapping inodes in such use cases -is required because the original device IDs from host are never passed and -exposed on guest. Instead all files of an export shared with virtfs always -share the same device id on guest. So two files with identical inode -numbers but from actually different devices on host would otherwise cause a -file ID collision and hence potential misbehaviours on guest. "forbid" on -the other hand assumes like "warn" that only one device is shared by the -same export, however it will not only log a warning message but also -deny access to additional devices on guest. Note though that "forbid" does -currently not block all possible file access operations (e.g. readdir() -would still return entries from other devices). -@end table -ETEXI + ``proxy`` + Accesses to the filesystem are done by virtfs-proxy-helper(1). -DEF("virtfs_synth", 0, QEMU_OPTION_virtfs_synth, - "-virtfs_synth Create synthetic file system image\n", - QEMU_ARCH_ALL) -STEXI -@item -virtfs_synth -@findex -virtfs_synth -Create synthetic file system image. Note that this option is now deprecated. -Please use @code{-fsdev synth} and @code{-device virtio-9p-...} instead. -ETEXI + ``synth`` + Synthetic filesystem, only used by QTests. + + ``id=id`` + Specifies identifier for the filesystem device + + ``path=path`` + Specifies the export path for the file system device. Files + under this path will be available to the 9p client on the guest. + + ``security_model=security_model`` + Specifies the security model to be used for this export path. + Supported security models are "passthrough", "mapped-xattr", + "mapped-file" and "none". In "passthrough" security model, files + are stored using the same credentials as they are created on the + guest. This requires QEMU to run as root. In "mapped-xattr" + security model, some of the file attributes like uid, gid, mode + bits and link target are stored as file attributes. For + "mapped-file" these attributes are stored in the hidden + .virtfs\_metadata directory. Directories exported by this + security model cannot interact with other unix tools. "none" + security model is same as passthrough except the sever won't + report failures if it fails to set file attributes like + ownership. Security model is mandatory only for local fsdriver. + Other fsdrivers (like proxy) don't take security model as a + parameter. + + ``writeout=writeout`` + This is an optional argument. The only supported value is + "immediate". This means that host page cache will be used to + read and write data but write notification will be sent to the + guest only when the data has been reported as written by the + storage subsystem. + + ``readonly`` + Enables exporting 9p share as a readonly mount for guests. By + default read-write access is given. + + ``socket=socket`` + Enables proxy filesystem driver to use passed socket file for + communicating with virtfs-proxy-helper(1). Usually a helper like + libvirt will create socketpair and pass one of the fds as + sock\_fd. + + ``sock_fd`` + Enables proxy filesystem driver to use passed 'sock\_fd' as the + socket descriptor for interfacing with virtfs-proxy-helper(1). + + ``fmode=fmode`` + Specifies the default mode for newly created files on the host. + Works only with security models "mapped-xattr" and + "mapped-file". + + ``dmode=dmode`` + Specifies the default mode for newly created directories on the + host. Works only with security models "mapped-xattr" and + "mapped-file". + + ``mount_tag=mount_tag`` + Specifies the tag name to be used by the guest to mount this + export point. + + ``multidevs=multidevs`` + Specifies how to deal with multiple devices being shared with a + 9p export. Supported behaviours are either "remap", "forbid" or + "warn". The latter is the default behaviour on which virtfs 9p + expects only one device to be shared with the same export, and + if more than one device is shared and accessed via the same 9p + export then only a warning message is logged (once) by qemu on + host side. In order to avoid file ID collisions on guest you + should either create a separate virtfs export for each device to + be shared with guests (recommended way) or you might use "remap" + instead which allows you to share multiple devices with only one + export instead, which is achieved by remapping the original + inode numbers from host to guest in a way that would prevent + such collisions. Remapping inodes in such use cases is required + because the original device IDs from host are never passed and + exposed on guest. Instead all files of an export shared with + virtfs always share the same device id on guest. So two files + with identical inode numbers but from actually different devices + on host would otherwise cause a file ID collision and hence + potential misbehaviours on guest. "forbid" on the other hand + assumes like "warn" that only one device is shared by the same + export, however it will not only log a warning message but also + deny access to additional devices on guest. Note though that + "forbid" does currently not block all possible file access + operations (e.g. readdir() would still return entries from other + devices). +ERST DEF("iscsi", HAS_ARG, QEMU_OPTION_iscsi, "-iscsi [user=user][,password=password]\n" @@ -1485,70 +1649,53 @@ DEF("iscsi", HAS_ARG, QEMU_OPTION_iscsi, " [,timeout=timeout]\n" " iSCSI session parameters\n", QEMU_ARCH_ALL) -STEXI -@item -iscsi -@findex -iscsi -Configure iSCSI session parameters. -ETEXI +SRST +``-iscsi`` + Configure iSCSI session parameters. +ERST -STEXI -@end table -ETEXI DEFHEADING() DEFHEADING(USB options:) -STEXI -@table @option -ETEXI DEF("usb", 0, QEMU_OPTION_usb, "-usb enable on-board USB host controller (if not enabled by default)\n", QEMU_ARCH_ALL) -STEXI -@item -usb -@findex -usb -Enable USB emulation on machine types with an on-board USB host controller (if -not enabled by default). Note that on-board USB host controllers may not -support USB 3.0. In this case @option{-device qemu-xhci} can be used instead -on machines with PCI. -ETEXI +SRST +``-usb`` + Enable USB emulation on machine types with an on-board USB host + controller (if not enabled by default). Note that on-board USB host + controllers may not support USB 3.0. In this case + ``-device qemu-xhci`` can be used instead on machines with PCI. +ERST DEF("usbdevice", HAS_ARG, QEMU_OPTION_usbdevice, "-usbdevice name add the host or guest USB device 'name'\n", QEMU_ARCH_ALL) -STEXI +SRST +``-usbdevice devname`` + Add the USB device devname. Note that this option is deprecated, + please use ``-device usb-...`` instead. See + :ref:`usb_005fdevices`. -@item -usbdevice @var{devname} -@findex -usbdevice -Add the USB device @var{devname}. Note that this option is deprecated, -please use @code{-device usb-...} instead. @xref{usb_devices}. + ``mouse`` + Virtual Mouse. This will override the PS/2 mouse emulation when + activated. -@table @option + ``tablet`` + Pointer device that uses absolute coordinates (like a + touchscreen). This means QEMU is able to report the mouse + position without having to grab the mouse. Also overrides the + PS/2 mouse emulation when activated. -@item mouse -Virtual Mouse. This will override the PS/2 mouse emulation when activated. + ``braille`` + Braille device. This will use BrlAPI to display the braille + output on a real or fake device. +ERST -@item tablet -Pointer device that uses absolute coordinates (like a touchscreen). This -means QEMU is able to report the mouse position without having to grab the -mouse. Also overrides the PS/2 mouse emulation when activated. - -@item braille -Braille device. This will use BrlAPI to display the braille output on a real -or fake device. - -@end table -ETEXI - -STEXI -@end table -ETEXI DEFHEADING() DEFHEADING(Display options:) -STEXI -@table @option -ETEXI DEF("display", HAS_ARG, QEMU_OPTION_display, #if defined(CONFIG_SPICE) @@ -1585,110 +1732,114 @@ DEF("display", HAS_ARG, QEMU_OPTION_display, "\"-display none\"\n" #endif , QEMU_ARCH_ALL) -STEXI -@item -display @var{type} -@findex -display -Select type of display to use. This option is a replacement for the -old style -sdl/-curses/... options. Valid values for @var{type} are -@table @option -@item sdl -Display video output via SDL (usually in a separate graphics -window; see the SDL documentation for other possibilities). -@item curses -Display video output via curses. For graphics device models which -support a text mode, QEMU can display this output using a -curses/ncurses interface. Nothing is displayed when the graphics -device is in graphical mode or if the graphics device does not support -a text mode. Generally only the VGA device models support text mode. -The font charset used by the guest can be specified with the -@code{charset} option, for example @code{charset=CP850} for IBM CP850 -encoding. The default is @code{CP437}. -@item none -Do not display video output. The guest will still see an emulated -graphics card, but its output will not be displayed to the QEMU -user. This option differs from the -nographic option in that it -only affects what is done with video output; -nographic also changes -the destination of the serial and parallel port data. -@item gtk -Display video output in a GTK window. This interface provides drop-down -menus and other UI elements to configure and control the VM during -runtime. -@item vnc -Start a VNC server on display -@item egl-headless -Offload all OpenGL operations to a local DRI device. For any graphical display, -this display needs to be paired with either VNC or SPICE displays. -@item spice-app -Start QEMU as a Spice server and launch the default Spice client -application. The Spice server will redirect the serial consoles and -QEMU monitors. (Since 4.0) -@end table -ETEXI +SRST +``-display type`` + Select type of display to use. This option is a replacement for the + old style -sdl/-curses/... options. Use ``-display help`` to list + the available display types. Valid values for type are + + ``sdl`` + Display video output via SDL (usually in a separate graphics + window; see the SDL documentation for other possibilities). + + ``curses`` + Display video output via curses. For graphics device models + which support a text mode, QEMU can display this output using a + curses/ncurses interface. Nothing is displayed when the graphics + device is in graphical mode or if the graphics device does not + support a text mode. Generally only the VGA device models + support text mode. The font charset used by the guest can be + specified with the ``charset`` option, for example + ``charset=CP850`` for IBM CP850 encoding. The default is + ``CP437``. + + ``none`` + Do not display video output. The guest will still see an + emulated graphics card, but its output will not be displayed to + the QEMU user. This option differs from the -nographic option in + that it only affects what is done with video output; -nographic + also changes the destination of the serial and parallel port + data. + + ``gtk`` + Display video output in a GTK window. This interface provides + drop-down menus and other UI elements to configure and control + the VM during runtime. + + ``vnc`` + Start a VNC server on display + + ``egl-headless`` + Offload all OpenGL operations to a local DRI device. For any + graphical display, this display needs to be paired with either + VNC or SPICE displays. + + ``spice-app`` + Start QEMU as a Spice server and launch the default Spice client + application. The Spice server will redirect the serial consoles + and QEMU monitors. (Since 4.0) +ERST DEF("nographic", 0, QEMU_OPTION_nographic, "-nographic disable graphical output and redirect serial I/Os to console\n", QEMU_ARCH_ALL) -STEXI -@item -nographic -@findex -nographic -Normally, if QEMU is compiled with graphical window support, it displays -output such as guest graphics, guest console, and the QEMU monitor in a -window. With this option, you can totally disable graphical output so -that QEMU is a simple command line application. The emulated serial port -is redirected on the console and muxed with the monitor (unless -redirected elsewhere explicitly). Therefore, you can still use QEMU to -debug a Linux kernel with a serial console. Use @key{C-a h} for help on -switching between the console and monitor. -ETEXI +SRST +``-nographic`` + Normally, if QEMU is compiled with graphical window support, it + displays output such as guest graphics, guest console, and the QEMU + monitor in a window. With this option, you can totally disable + graphical output so that QEMU is a simple command line application. + The emulated serial port is redirected on the console and muxed with + the monitor (unless redirected elsewhere explicitly). Therefore, you + can still use QEMU to debug a Linux kernel with a serial console. + Use C-a h for help on switching between the console and monitor. +ERST DEF("curses", 0, QEMU_OPTION_curses, "-curses shorthand for -display curses\n", QEMU_ARCH_ALL) -STEXI -@item -curses -@findex -curses -Normally, if QEMU is compiled with graphical window support, it displays -output such as guest graphics, guest console, and the QEMU monitor in a -window. With this option, QEMU can display the VGA output when in text -mode using a curses/ncurses interface. Nothing is displayed in graphical -mode. -ETEXI +SRST +``-curses`` + Normally, if QEMU is compiled with graphical window support, it + displays output such as guest graphics, guest console, and the QEMU + monitor in a window. With this option, QEMU can display the VGA + output when in text mode using a curses/ncurses interface. Nothing + is displayed in graphical mode. +ERST DEF("alt-grab", 0, QEMU_OPTION_alt_grab, "-alt-grab use Ctrl-Alt-Shift to grab mouse (instead of Ctrl-Alt)\n", QEMU_ARCH_ALL) -STEXI -@item -alt-grab -@findex -alt-grab -Use Ctrl-Alt-Shift to grab mouse (instead of Ctrl-Alt). Note that this also -affects the special keys (for fullscreen, monitor-mode switching, etc). -ETEXI +SRST +``-alt-grab`` + Use Ctrl-Alt-Shift to grab mouse (instead of Ctrl-Alt). Note that + this also affects the special keys (for fullscreen, monitor-mode + switching, etc). +ERST DEF("ctrl-grab", 0, QEMU_OPTION_ctrl_grab, "-ctrl-grab use Right-Ctrl to grab mouse (instead of Ctrl-Alt)\n", QEMU_ARCH_ALL) -STEXI -@item -ctrl-grab -@findex -ctrl-grab -Use Right-Ctrl to grab mouse (instead of Ctrl-Alt). Note that this also -affects the special keys (for fullscreen, monitor-mode switching, etc). -ETEXI +SRST +``-ctrl-grab`` + Use Right-Ctrl to grab mouse (instead of Ctrl-Alt). Note that this + also affects the special keys (for fullscreen, monitor-mode + switching, etc). +ERST DEF("no-quit", 0, QEMU_OPTION_no_quit, "-no-quit disable SDL window close capability\n", QEMU_ARCH_ALL) -STEXI -@item -no-quit -@findex -no-quit -Disable SDL window close capability. -ETEXI +SRST +``-no-quit`` + Disable SDL window close capability. +ERST DEF("sdl", 0, QEMU_OPTION_sdl, "-sdl shorthand for -display sdl\n", QEMU_ARCH_ALL) -STEXI -@item -sdl -@findex -sdl -Enable SDL. -ETEXI +SRST +``-sdl`` + Enable SDL. +ERST DEF("spice", HAS_ARG, QEMU_OPTION_spice, "-spice [port=port][,tls-port=secured-port][,x509-dir=]\n" @@ -1709,431 +1860,405 @@ DEF("spice", HAS_ARG, QEMU_OPTION_spice, " enable spice\n" " at least one of {port, tls-port} is mandatory\n", QEMU_ARCH_ALL) -STEXI -@item -spice @var{option}[,@var{option}[,...]] -@findex -spice -Enable the spice remote desktop protocol. Valid options are +SRST +``-spice option[,option[,...]]`` + Enable the spice remote desktop protocol. Valid options are -@table @option + ``port=`` + Set the TCP port spice is listening on for plaintext channels. -@item port= -Set the TCP port spice is listening on for plaintext channels. + ``addr=`` + Set the IP address spice is listening on. Default is any + address. -@item addr= -Set the IP address spice is listening on. Default is any address. + ``ipv4``; \ ``ipv6``; \ ``unix`` + Force using the specified IP version. -@item ipv4 -@itemx ipv6 -@itemx unix -Force using the specified IP version. + ``password=`` + Set the password you need to authenticate. -@item password= -Set the password you need to authenticate. + ``sasl`` + Require that the client use SASL to authenticate with the spice. + The exact choice of authentication method used is controlled + from the system / user's SASL configuration file for the 'qemu' + service. This is typically found in /etc/sasl2/qemu.conf. If + running QEMU as an unprivileged user, an environment variable + SASL\_CONF\_PATH can be used to make it search alternate + locations for the service config. While some SASL auth methods + can also provide data encryption (eg GSSAPI), it is recommended + that SASL always be combined with the 'tls' and 'x509' settings + to enable use of SSL and server certificates. This ensures a + data encryption preventing compromise of authentication + credentials. -@item sasl -Require that the client use SASL to authenticate with the spice. -The exact choice of authentication method used is controlled from the -system / user's SASL configuration file for the 'qemu' service. This -is typically found in /etc/sasl2/qemu.conf. If running QEMU as an -unprivileged user, an environment variable SASL_CONF_PATH can be used -to make it search alternate locations for the service config. -While some SASL auth methods can also provide data encryption (eg GSSAPI), -it is recommended that SASL always be combined with the 'tls' and -'x509' settings to enable use of SSL and server certificates. This -ensures a data encryption preventing compromise of authentication -credentials. + ``disable-ticketing`` + Allow client connects without authentication. -@item disable-ticketing -Allow client connects without authentication. + ``disable-copy-paste`` + Disable copy paste between the client and the guest. -@item disable-copy-paste -Disable copy paste between the client and the guest. + ``disable-agent-file-xfer`` + Disable spice-vdagent based file-xfer between the client and the + guest. -@item disable-agent-file-xfer -Disable spice-vdagent based file-xfer between the client and the guest. + ``tls-port=`` + Set the TCP port spice is listening on for encrypted channels. -@item tls-port= -Set the TCP port spice is listening on for encrypted channels. + ``x509-dir=`` + Set the x509 file directory. Expects same filenames as -vnc + $display,x509=$dir -@item x509-dir= -Set the x509 file directory. Expects same filenames as -vnc $display,x509=$dir + ``x509-key-file=``; \ ``x509-key-password=``; \ ``x509-cert-file=``; \ ``x509-cacert-file=``; \ ``x509-dh-key-file=`` + The x509 file names can also be configured individually. -@item x509-key-file= -@itemx x509-key-password= -@itemx x509-cert-file= -@itemx x509-cacert-file= -@itemx x509-dh-key-file= -The x509 file names can also be configured individually. + ``tls-ciphers=`` + Specify which ciphers to use. -@item tls-ciphers= -Specify which ciphers to use. + ``tls-channel=[main|display|cursor|inputs|record|playback]``; \ ``plaintext-channel=[main|display|cursor|inputs|record|playback]`` + Force specific channel to be used with or without TLS + encryption. The options can be specified multiple times to + configure multiple channels. The special name "default" can be + used to set the default mode. For channels which are not + explicitly forced into one mode the spice client is allowed to + pick tls/plaintext as he pleases. -@item tls-channel=[main|display|cursor|inputs|record|playback] -@itemx plaintext-channel=[main|display|cursor|inputs|record|playback] -Force specific channel to be used with or without TLS encryption. The -options can be specified multiple times to configure multiple -channels. The special name "default" can be used to set the default -mode. For channels which are not explicitly forced into one mode the -spice client is allowed to pick tls/plaintext as he pleases. + ``image-compression=[auto_glz|auto_lz|quic|glz|lz|off]`` + Configure image compression (lossless). Default is auto\_glz. -@item image-compression=[auto_glz|auto_lz|quic|glz|lz|off] -Configure image compression (lossless). -Default is auto_glz. + ``jpeg-wan-compression=[auto|never|always]``; \ ``zlib-glz-wan-compression=[auto|never|always]`` + Configure wan image compression (lossy for slow links). Default + is auto. -@item jpeg-wan-compression=[auto|never|always] -@itemx zlib-glz-wan-compression=[auto|never|always] -Configure wan image compression (lossy for slow links). -Default is auto. + ``streaming-video=[off|all|filter]`` + Configure video stream detection. Default is off. -@item streaming-video=[off|all|filter] -Configure video stream detection. Default is off. + ``agent-mouse=[on|off]`` + Enable/disable passing mouse events via vdagent. Default is on. -@item agent-mouse=[on|off] -Enable/disable passing mouse events via vdagent. Default is on. + ``playback-compression=[on|off]`` + Enable/disable audio stream compression (using celt 0.5.1). + Default is on. -@item playback-compression=[on|off] -Enable/disable audio stream compression (using celt 0.5.1). Default is on. + ``seamless-migration=[on|off]`` + Enable/disable spice seamless migration. Default is off. -@item seamless-migration=[on|off] -Enable/disable spice seamless migration. Default is off. + ``gl=[on|off]`` + Enable/disable OpenGL context. Default is off. -@item gl=[on|off] -Enable/disable OpenGL context. Default is off. - -@item rendernode= -DRM render node for OpenGL rendering. If not specified, it will pick -the first available. (Since 2.9) - -@end table -ETEXI + ``rendernode=`` + DRM render node for OpenGL rendering. If not specified, it will + pick the first available. (Since 2.9) +ERST DEF("portrait", 0, QEMU_OPTION_portrait, "-portrait rotate graphical output 90 deg left (only PXA LCD)\n", QEMU_ARCH_ALL) -STEXI -@item -portrait -@findex -portrait -Rotate graphical output 90 deg left (only PXA LCD). -ETEXI +SRST +``-portrait`` + Rotate graphical output 90 deg left (only PXA LCD). +ERST DEF("rotate", HAS_ARG, QEMU_OPTION_rotate, "-rotate rotate graphical output some deg left (only PXA LCD)\n", QEMU_ARCH_ALL) -STEXI -@item -rotate @var{deg} -@findex -rotate -Rotate graphical output some deg left (only PXA LCD). -ETEXI +SRST +``-rotate deg`` + Rotate graphical output some deg left (only PXA LCD). +ERST DEF("vga", HAS_ARG, QEMU_OPTION_vga, "-vga [std|cirrus|vmware|qxl|xenfb|tcx|cg3|virtio|none]\n" " select video card type\n", QEMU_ARCH_ALL) -STEXI -@item -vga @var{type} -@findex -vga -Select type of VGA card to emulate. Valid values for @var{type} are -@table @option -@item cirrus -Cirrus Logic GD5446 Video card. All Windows versions starting from -Windows 95 should recognize and use this graphic card. For optimal -performances, use 16 bit color depth in the guest and the host OS. -(This card was the default before QEMU 2.2) -@item std -Standard VGA card with Bochs VBE extensions. If your guest OS -supports the VESA 2.0 VBE extensions (e.g. Windows XP) and if you want -to use high resolution modes (>= 1280x1024x16) then you should use -this option. (This card is the default since QEMU 2.2) -@item vmware -VMWare SVGA-II compatible adapter. Use it if you have sufficiently -recent XFree86/XOrg server or Windows guest with a driver for this -card. -@item qxl -QXL paravirtual graphic card. It is VGA compatible (including VESA -2.0 VBE support). Works best with qxl guest drivers installed though. -Recommended choice when using the spice protocol. -@item tcx -(sun4m only) Sun TCX framebuffer. This is the default framebuffer for -sun4m machines and offers both 8-bit and 24-bit colour depths at a -fixed resolution of 1024x768. -@item cg3 -(sun4m only) Sun cgthree framebuffer. This is a simple 8-bit framebuffer -for sun4m machines available in both 1024x768 (OpenBIOS) and 1152x900 (OBP) -resolutions aimed at people wishing to run older Solaris versions. -@item virtio -Virtio VGA card. -@item none -Disable VGA card. -@end table -ETEXI +SRST +``-vga type`` + Select type of VGA card to emulate. Valid values for type are + + ``cirrus`` + Cirrus Logic GD5446 Video card. All Windows versions starting + from Windows 95 should recognize and use this graphic card. For + optimal performances, use 16 bit color depth in the guest and + the host OS. (This card was the default before QEMU 2.2) + + ``std`` + Standard VGA card with Bochs VBE extensions. If your guest OS + supports the VESA 2.0 VBE extensions (e.g. Windows XP) and if + you want to use high resolution modes (>= 1280x1024x16) then you + should use this option. (This card is the default since QEMU + 2.2) + + ``vmware`` + VMWare SVGA-II compatible adapter. Use it if you have + sufficiently recent XFree86/XOrg server or Windows guest with a + driver for this card. + + ``qxl`` + QXL paravirtual graphic card. It is VGA compatible (including + VESA 2.0 VBE support). Works best with qxl guest drivers + installed though. Recommended choice when using the spice + protocol. + + ``tcx`` + (sun4m only) Sun TCX framebuffer. This is the default + framebuffer for sun4m machines and offers both 8-bit and 24-bit + colour depths at a fixed resolution of 1024x768. + + ``cg3`` + (sun4m only) Sun cgthree framebuffer. This is a simple 8-bit + framebuffer for sun4m machines available in both 1024x768 + (OpenBIOS) and 1152x900 (OBP) resolutions aimed at people + wishing to run older Solaris versions. + + ``virtio`` + Virtio VGA card. + + ``none`` + Disable VGA card. +ERST DEF("full-screen", 0, QEMU_OPTION_full_screen, "-full-screen start in full screen\n", QEMU_ARCH_ALL) -STEXI -@item -full-screen -@findex -full-screen -Start in full screen. -ETEXI +SRST +``-full-screen`` + Start in full screen. +ERST -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 -@item -g @var{width}x@var{height}[x@var{depth}] -@findex -g -Set the initial graphical resolution and depth (PPC, SPARC only). -ETEXI +SRST +``-g`` *width*\ ``x``\ *height*\ ``[x``\ *depth*\ ``]`` + Set the initial graphical resolution and depth (PPC, SPARC only). + + For PPC the default is 800x600x32. + + For SPARC with the TCX graphics device, the default is 1024x768x8 + with the option of 1024x768x24. For cgthree, the default is + 1024x768x8 with the option of 1152x900x8 for people who wish to use + OBP. +ERST DEF("vnc", HAS_ARG, QEMU_OPTION_vnc , "-vnc shorthand for -display vnc=\n", QEMU_ARCH_ALL) -STEXI -@item -vnc @var{display}[,@var{option}[,@var{option}[,...]]] -@findex -vnc -Normally, if QEMU is compiled with graphical window support, it displays -output such as guest graphics, guest console, and the QEMU monitor in a -window. With this option, you can have QEMU listen on VNC display -@var{display} and redirect the VGA display over the VNC session. It is -very useful to enable the usb tablet device when using this option -(option @option{-device usb-tablet}). When using the VNC display, you -must use the @option{-k} parameter to set the keyboard layout if you are -not using en-us. Valid syntax for the @var{display} is +SRST +``-vnc display[,option[,option[,...]]]`` + Normally, if QEMU is compiled with graphical window support, it + displays output such as guest graphics, guest console, and the QEMU + monitor in a window. With this option, you can have QEMU listen on + VNC display display and redirect the VGA display over the VNC + session. It is very useful to enable the usb tablet device when + using this option (option ``-device usb-tablet``). When using the + VNC display, you must use the ``-k`` parameter to set the keyboard + layout if you are not using en-us. Valid syntax for the display is -@table @option + ``to=L`` + With this option, QEMU will try next available VNC displays, + until the number L, if the origianlly defined "-vnc display" is + not available, e.g. port 5900+display is already used by another + application. By default, to=0. -@item to=@var{L} + ``host:d`` + TCP connections will only be allowed from host on display d. By + convention the TCP port is 5900+d. Optionally, host can be + omitted in which case the server will accept connections from + any host. -With this option, QEMU will try next available VNC @var{display}s, until the -number @var{L}, if the origianlly defined "-vnc @var{display}" is not -available, e.g. port 5900+@var{display} is already used by another -application. By default, to=0. + ``unix:path`` + Connections will be allowed over UNIX domain sockets where path + is the location of a unix socket to listen for connections on. -@item @var{host}:@var{d} + ``none`` + VNC is initialized but not started. The monitor ``change`` + command can be used to later start the VNC server. -TCP connections will only be allowed from @var{host} on display @var{d}. -By convention the TCP port is 5900+@var{d}. Optionally, @var{host} can -be omitted in which case the server will accept connections from any host. + Following the display value there may be one or more option flags + separated by commas. Valid options are -@item unix:@var{path} + ``reverse`` + Connect to a listening VNC client via a "reverse" connection. + The client is specified by the display. For reverse network + connections (host:d,``reverse``), the d argument is a TCP port + number, not a display number. -Connections will be allowed over UNIX domain sockets where @var{path} is the -location of a unix socket to listen for connections on. + ``websocket`` + Opens an additional TCP listening port dedicated to VNC + Websocket connections. If a bare websocket option is given, the + Websocket port is 5700+display. An alternative port can be + specified with the syntax ``websocket``\ =port. -@item none + If host is specified connections will only be allowed from this + host. It is possible to control the websocket listen address + independently, using the syntax ``websocket``\ =host:port. -VNC is initialized but not started. The monitor @code{change} command -can be used to later start the VNC server. + If no TLS credentials are provided, the websocket connection + runs in unencrypted mode. If TLS credentials are provided, the + websocket connection requires encrypted client connections. -@end table + ``password`` + Require that password based authentication is used for client + connections. -Following the @var{display} value there may be one or more @var{option} flags -separated by commas. Valid options are + The password must be set separately using the ``set_password`` + command in the :ref:`pcsys_005fmonitor`. The + syntax to change your password is: + ``set_password `` where could be + either "vnc" or "spice". -@table @option + If you would like to change password expiration, you + should use ``expire_password `` + where expiration time could be one of the following options: + now, never, +seconds or UNIX time of expiration, e.g. +60 to + make password expire in 60 seconds, or 1335196800 to make + password expire on "Mon Apr 23 12:00:00 EDT 2012" (UNIX time for + this date and time). -@item reverse + You can also use keywords "now" or "never" for the expiration + time to allow password to expire immediately or never + expire. -Connect to a listening VNC client via a ``reverse'' connection. The -client is specified by the @var{display}. For reverse network -connections (@var{host}:@var{d},@code{reverse}), the @var{d} argument -is a TCP port number, not a display number. + ``tls-creds=ID`` + Provides the ID of a set of TLS credentials to use to secure the + VNC server. They will apply to both the normal VNC server socket + and the websocket socket (if enabled). Setting TLS credentials + will cause the VNC server socket to enable the VeNCrypt auth + mechanism. The credentials should have been previously created + using the ``-object tls-creds`` argument. -@item websocket + ``tls-authz=ID`` + Provides the ID of the QAuthZ authorization object against which + the client's x509 distinguished name will validated. This object + is only resolved at time of use, so can be deleted and recreated + on the fly while the VNC server is active. If missing, it will + default to denying access. -Opens an additional TCP listening port dedicated to VNC Websocket connections. -If a bare @var{websocket} option is given, the Websocket port is -5700+@var{display}. An alternative port can be specified with the -syntax @code{websocket}=@var{port}. + ``sasl`` + Require that the client use SASL to authenticate with the VNC + server. The exact choice of authentication method used is + controlled from the system / user's SASL configuration file for + the 'qemu' service. This is typically found in + /etc/sasl2/qemu.conf. If running QEMU as an unprivileged user, + an environment variable SASL\_CONF\_PATH can be used to make it + search alternate locations for the service config. While some + SASL auth methods can also provide data encryption (eg GSSAPI), + it is recommended that SASL always be combined with the 'tls' + and 'x509' settings to enable use of SSL and server + certificates. This ensures a data encryption preventing + compromise of authentication credentials. See the + :ref:`vnc_005fsecurity` section for details on + using SASL authentication. -If @var{host} is specified connections will only be allowed from this host. -It is possible to control the websocket listen address independently, using -the syntax @code{websocket}=@var{host}:@var{port}. + ``sasl-authz=ID`` + Provides the ID of the QAuthZ authorization object against which + the client's SASL username will validated. This object is only + resolved at time of use, so can be deleted and recreated on the + fly while the VNC server is active. If missing, it will default + to denying access. -If no TLS credentials are provided, the websocket connection runs in -unencrypted mode. If TLS credentials are provided, the websocket connection -requires encrypted client connections. + ``acl`` + Legacy method for enabling authorization of clients against the + x509 distinguished name and SASL username. It results in the + creation of two ``authz-list`` objects with IDs of + ``vnc.username`` and ``vnc.x509dname``. The rules for these + objects must be configured with the HMP ACL commands. -@item password + This option is deprecated and should no longer be used. The new + ``sasl-authz`` and ``tls-authz`` options are a replacement. -Require that password based authentication is used for client connections. + ``lossy`` + Enable lossy compression methods (gradient, JPEG, ...). If this + option is set, VNC client may receive lossy framebuffer updates + depending on its encoding settings. Enabling this option can + save a lot of bandwidth at the expense of quality. -The password must be set separately using the @code{set_password} command in -the @ref{pcsys_monitor}. The syntax to change your password is: -@code{set_password } where could be either -"vnc" or "spice". + ``non-adaptive`` + Disable adaptive encodings. Adaptive encodings are enabled by + default. An adaptive encoding will try to detect frequently + updated screen regions, and send updates in these regions using + a lossy encoding (like JPEG). This can be really helpful to save + bandwidth when playing videos. Disabling adaptive encodings + restores the original static behavior of encodings like Tight. -If you would like to change password expiration, you should use -@code{expire_password } where expiration time could -be one of the following options: now, never, +seconds or UNIX time of -expiration, e.g. +60 to make password expire in 60 seconds, or 1335196800 -to make password expire on "Mon Apr 23 12:00:00 EDT 2012" (UNIX time for this -date and time). + ``share=[allow-exclusive|force-shared|ignore]`` + Set display sharing policy. 'allow-exclusive' allows clients to + ask for exclusive access. As suggested by the rfb spec this is + implemented by dropping other connections. Connecting multiple + clients in parallel requires all clients asking for a shared + session (vncviewer: -shared switch). This is the default. + 'force-shared' disables exclusive client access. Useful for + shared desktop sessions, where you don't want someone forgetting + specify -shared disconnect everybody else. 'ignore' completely + ignores the shared flag and allows everybody connect + unconditionally. Doesn't conform to the rfb spec but is + traditional QEMU behavior. -You can also use keywords "now" or "never" for the expiration time to -allow password to expire immediately or never expire. + ``key-delay-ms`` + Set keyboard delay, for key down and key up events, in + milliseconds. Default is 10. Keyboards are low-bandwidth + devices, so this slowdown can help the device and guest to keep + up and not lose events in case events are arriving in bulk. + Possible causes for the latter are flaky network connections, or + scripts for automated testing. -@item tls-creds=@var{ID} + ``audiodev=audiodev`` + Use the specified audiodev when the VNC client requests audio + transmission. When not using an -audiodev argument, this option + must be omitted, otherwise is must be present and specify a + valid audiodev. +ERST -Provides the ID of a set of TLS credentials to use to secure the -VNC server. They will apply to both the normal VNC server socket -and the websocket socket (if enabled). Setting TLS credentials -will cause the VNC server socket to enable the VeNCrypt auth -mechanism. The credentials should have been previously created -using the @option{-object tls-creds} argument. - -@item tls-authz=@var{ID} - -Provides the ID of the QAuthZ authorization object against which -the client's x509 distinguished name will validated. This object is -only resolved at time of use, so can be deleted and recreated on the -fly while the VNC server is active. If missing, it will default -to denying access. - -@item sasl - -Require that the client use SASL to authenticate with the VNC server. -The exact choice of authentication method used is controlled from the -system / user's SASL configuration file for the 'qemu' service. This -is typically found in /etc/sasl2/qemu.conf. If running QEMU as an -unprivileged user, an environment variable SASL_CONF_PATH can be used -to make it search alternate locations for the service config. -While some SASL auth methods can also provide data encryption (eg GSSAPI), -it is recommended that SASL always be combined with the 'tls' and -'x509' settings to enable use of SSL and server certificates. This -ensures a data encryption preventing compromise of authentication -credentials. See the @ref{vnc_security} section for details on using -SASL authentication. - -@item sasl-authz=@var{ID} - -Provides the ID of the QAuthZ authorization object against which -the client's SASL username will validated. This object is -only resolved at time of use, so can be deleted and recreated on the -fly while the VNC server is active. If missing, it will default -to denying access. - -@item acl - -Legacy method for enabling authorization of clients against the -x509 distinguished name and SASL username. It results in the creation -of two @code{authz-list} objects with IDs of @code{vnc.username} and -@code{vnc.x509dname}. The rules for these objects must be configured -with the HMP ACL commands. - -This option is deprecated and should no longer be used. The new -@option{sasl-authz} and @option{tls-authz} options are a -replacement. - -@item lossy - -Enable lossy compression methods (gradient, JPEG, ...). If this -option is set, VNC client may receive lossy framebuffer updates -depending on its encoding settings. Enabling this option can save -a lot of bandwidth at the expense of quality. - -@item non-adaptive - -Disable adaptive encodings. Adaptive encodings are enabled by default. -An adaptive encoding will try to detect frequently updated screen regions, -and send updates in these regions using a lossy encoding (like JPEG). -This can be really helpful to save bandwidth when playing videos. Disabling -adaptive encodings restores the original static behavior of encodings -like Tight. - -@item share=[allow-exclusive|force-shared|ignore] - -Set display sharing policy. 'allow-exclusive' allows clients to ask -for exclusive access. As suggested by the rfb spec this is -implemented by dropping other connections. Connecting multiple -clients in parallel requires all clients asking for a shared session -(vncviewer: -shared switch). This is the default. 'force-shared' -disables exclusive client access. Useful for shared desktop sessions, -where you don't want someone forgetting specify -shared disconnect -everybody else. 'ignore' completely ignores the shared flag and -allows everybody connect unconditionally. Doesn't conform to the rfb -spec but is traditional QEMU behavior. - -@item key-delay-ms - -Set keyboard delay, for key down and key up events, in milliseconds. -Default is 10. Keyboards are low-bandwidth devices, so this slowdown -can help the device and guest to keep up and not lose events in case -events are arriving in bulk. Possible causes for the latter are flaky -network connections, or scripts for automated testing. - -@item audiodev=@var{audiodev} - -Use the specified @var{audiodev} when the VNC client requests audio -transmission. When not using an -audiodev argument, this option must -be omitted, otherwise is must be present and specify a valid audiodev. - -@end table -ETEXI - -STEXI -@end table -ETEXI ARCHHEADING(, QEMU_ARCH_I386) ARCHHEADING(i386 target only:, QEMU_ARCH_I386) -STEXI -@table @option -ETEXI DEF("win2k-hack", 0, QEMU_OPTION_win2k_hack, "-win2k-hack use it when installing Windows 2000 to avoid a disk full bug\n", QEMU_ARCH_I386) -STEXI -@item -win2k-hack -@findex -win2k-hack -Use it when installing Windows 2000 to avoid a disk full bug. After -Windows 2000 is installed, you no longer need this option (this option -slows down the IDE transfers). -ETEXI +SRST +``-win2k-hack`` + Use it when installing Windows 2000 to avoid a disk full bug. After + Windows 2000 is installed, you no longer need this option (this + option slows down the IDE transfers). +ERST DEF("no-fd-bootchk", 0, QEMU_OPTION_no_fd_bootchk, "-no-fd-bootchk disable boot signature checking for floppy disks\n", QEMU_ARCH_I386) -STEXI -@item -no-fd-bootchk -@findex -no-fd-bootchk -Disable boot signature checking for floppy disks in BIOS. May -be needed to boot from old floppy disks. -ETEXI +SRST +``-no-fd-bootchk`` + Disable boot signature checking for floppy disks in BIOS. May be + needed to boot from old floppy disks. +ERST DEF("no-acpi", 0, QEMU_OPTION_no_acpi, "-no-acpi disable ACPI\n", QEMU_ARCH_I386 | QEMU_ARCH_ARM) -STEXI -@item -no-acpi -@findex -no-acpi -Disable ACPI (Advanced Configuration and Power Interface) support. Use -it if your guest OS complains about ACPI problems (PC target machine -only). -ETEXI +SRST +``-no-acpi`` + Disable ACPI (Advanced Configuration and Power Interface) support. + Use it if your guest OS complains about ACPI problems (PC target + machine only). +ERST DEF("no-hpet", 0, QEMU_OPTION_no_hpet, "-no-hpet disable HPET\n", QEMU_ARCH_I386) -STEXI -@item -no-hpet -@findex -no-hpet -Disable HPET support. -ETEXI +SRST +``-no-hpet`` + Disable HPET support. +ERST DEF("acpitable", HAS_ARG, QEMU_OPTION_acpitable, "-acpitable [sig=str][,rev=n][,oem_id=str][,oem_table_id=str][,oem_rev=n][,asl_compiler_id=str][,asl_compiler_rev=n][,{data|file}=file1[:file2]...]\n" " ACPI table description\n", QEMU_ARCH_I386) -STEXI -@item -acpitable [sig=@var{str}][,rev=@var{n}][,oem_id=@var{str}][,oem_table_id=@var{str}][,oem_rev=@var{n}] [,asl_compiler_id=@var{str}][,asl_compiler_rev=@var{n}][,data=@var{file1}[:@var{file2}]...] -@findex -acpitable -Add ACPI table with specified header fields and context from specified files. -For file=, take whole ACPI table from the specified files, including all -ACPI headers (possible overridden by other options). -For data=, only data -portion of the table is used, all header information is specified in the -command line. -If a SLIC table is supplied to QEMU, then the SLIC's oem_id and oem_table_id -fields will override the same in the RSDT and the FADT (a.k.a. FACP), in order -to ensure the field matches required by the Microsoft SLIC spec and the ACPI -spec. -ETEXI +SRST +``-acpitable [sig=str][,rev=n][,oem_id=str][,oem_table_id=str][,oem_rev=n] [,asl_compiler_id=str][,asl_compiler_rev=n][,data=file1[:file2]...]`` + Add ACPI table with specified header fields and context from + specified files. For file=, take whole ACPI table from the specified + files, including all ACPI headers (possible overridden by other + options). For data=, only data portion of the table is used, all + header information is specified in the command line. If a SLIC table + is supplied to QEMU, then the SLIC's oem\_id and oem\_table\_id + fields will override the same in the RSDT and the FADT (a.k.a. + FACP), in order to ensure the field matches required by the + Microsoft SLIC spec and the ACPI spec. +ERST DEF("smbios", HAS_ARG, QEMU_OPTION_smbios, "-smbios file=binary\n" @@ -2157,39 +2282,32 @@ DEF("smbios", HAS_ARG, QEMU_OPTION_smbios, " [,asset=str][,part=str][,speed=%d]\n" " specify SMBIOS type 17 fields\n", QEMU_ARCH_I386 | QEMU_ARCH_ARM) -STEXI -@item -smbios file=@var{binary} -@findex -smbios -Load SMBIOS entry from binary file. +SRST +``-smbios file=binary`` + Load SMBIOS entry from binary file. -@item -smbios type=0[,vendor=@var{str}][,version=@var{str}][,date=@var{str}][,release=@var{%d.%d}][,uefi=on|off] -Specify SMBIOS type 0 fields +``-smbios type=0[,vendor=str][,version=str][,date=str][,release=%d.%d][,uefi=on|off]`` + Specify SMBIOS type 0 fields -@item -smbios type=1[,manufacturer=@var{str}][,product=@var{str}][,version=@var{str}][,serial=@var{str}][,uuid=@var{uuid}][,sku=@var{str}][,family=@var{str}] -Specify SMBIOS type 1 fields +``-smbios type=1[,manufacturer=str][,product=str][,version=str][,serial=str][,uuid=uuid][,sku=str][,family=str]`` + Specify SMBIOS type 1 fields -@item -smbios type=2[,manufacturer=@var{str}][,product=@var{str}][,version=@var{str}][,serial=@var{str}][,asset=@var{str}][,location=@var{str}] -Specify SMBIOS type 2 fields +``-smbios type=2[,manufacturer=str][,product=str][,version=str][,serial=str][,asset=str][,location=str]`` + Specify SMBIOS type 2 fields -@item -smbios type=3[,manufacturer=@var{str}][,version=@var{str}][,serial=@var{str}][,asset=@var{str}][,sku=@var{str}] -Specify SMBIOS type 3 fields +``-smbios type=3[,manufacturer=str][,version=str][,serial=str][,asset=str][,sku=str]`` + Specify SMBIOS type 3 fields -@item -smbios type=4[,sock_pfx=@var{str}][,manufacturer=@var{str}][,version=@var{str}][,serial=@var{str}][,asset=@var{str}][,part=@var{str}] -Specify SMBIOS type 4 fields +``-smbios type=4[,sock_pfx=str][,manufacturer=str][,version=str][,serial=str][,asset=str][,part=str]`` + Specify SMBIOS type 4 fields -@item -smbios type=17[,loc_pfx=@var{str}][,bank=@var{str}][,manufacturer=@var{str}][,serial=@var{str}][,asset=@var{str}][,part=@var{str}][,speed=@var{%d}] -Specify SMBIOS type 17 fields -ETEXI +``-smbios type=17[,loc_pfx=str][,bank=str][,manufacturer=str][,serial=str][,asset=str][,part=str][,speed=%d]`` + Specify SMBIOS type 17 fields +ERST -STEXI -@end table -ETEXI DEFHEADING() DEFHEADING(Network options:) -STEXI -@table @option -ETEXI DEF("netdev", HAS_ARG, QEMU_OPTION_netdev, #ifdef CONFIG_SLIRP @@ -2249,7 +2367,7 @@ DEF("netdev", HAS_ARG, QEMU_OPTION_netdev, " Linux kernel 3.3+ as well as most routers can talk\n" " L2TPv3. This transport allows connecting a VM to a VM,\n" " VM to a router and even VM to Host. It is a nearly-universal\n" - " standard (RFC3391). Note - this implementation uses static\n" + " standard (RFC3931). Note - this implementation uses static\n" " pre-configured tunnels (same as the Linux kernel).\n" " use 'src=' to specify source address\n" " use 'dst=' to specify destination address\n" @@ -2336,450 +2454,471 @@ DEF("net", HAS_ARG, QEMU_OPTION_net, "socket][,option][,option][,...]\n" " old way to initialize a host network interface\n" " (use the -netdev option if possible instead)\n", QEMU_ARCH_ALL) -STEXI -@item -nic [tap|bridge|user|l2tpv3|vde|netmap|vhost-user|socket][,...][,mac=macaddr][,model=mn] -@findex -nic -This option is a shortcut for configuring both the on-board (default) guest -NIC hardware and the host network backend in one go. The host backend options -are the same as with the corresponding @option{-netdev} options below. -The guest NIC model can be set with @option{model=@var{modelname}}. -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): -@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 -@end example - -@item -nic none -Indicate that no network devices should be configured. It is used to override -the default configuration (default NIC with ``user'' host network backend) -which is activated if no other networking options are provided. - -@item -netdev user,id=@var{id}[,@var{option}][,@var{option}][,...] -@findex -netdev -Configure user mode host network backend which requires no administrator -privilege to run. Valid options are: - -@table @option -@item id=@var{id} -Assign symbolic name for use in monitor commands. - -@item ipv4=on|off and ipv6=on|off -Specify that either IPv4 or IPv6 must be enabled. If neither is specified -both protocols are enabled. - -@item net=@var{addr}[/@var{mask}] -Set IP network address the guest will see. Optionally specify the netmask, -either in the form a.b.c.d or as number of valid top-most bits. Default is -10.0.2.0/24. - -@item host=@var{addr} -Specify the guest-visible address of the host. Default is the 2nd IP in the -guest network, i.e. x.x.x.2. - -@item ipv6-net=@var{addr}[/@var{int}] -Set IPv6 network address the guest will see (default is fec0::/64). The -network prefix is given in the usual hexadecimal IPv6 address -notation. The prefix size is optional, and is given as the number of -valid top-most bits (default is 64). - -@item ipv6-host=@var{addr} -Specify the guest-visible IPv6 address of the host. Default is the 2nd IPv6 in -the guest network, i.e. xxxx::2. - -@item restrict=on|off -If this option is enabled, the guest will be isolated, i.e. it will not be -able to contact the host and no guest IP packets will be routed over the host -to the outside. This option does not affect any explicitly set forwarding rules. - -@item hostname=@var{name} -Specifies the client hostname reported by the built-in DHCP server. - -@item dhcpstart=@var{addr} -Specify the first of the 16 IPs the built-in DHCP server can assign. Default -is the 15th to 31st IP in the guest network, i.e. x.x.x.15 to x.x.x.31. - -@item dns=@var{addr} -Specify the guest-visible address of the virtual nameserver. The address must -be different from the host address. Default is the 3rd IP in the guest network, -i.e. x.x.x.3. - -@item ipv6-dns=@var{addr} -Specify the guest-visible address of the IPv6 virtual nameserver. The address -must be different from the host address. Default is the 3rd IP in the guest -network, i.e. xxxx::3. - -@item dnssearch=@var{domain} -Provides an entry for the domain-search list sent by the built-in -DHCP server. More than one domain suffix can be transmitted by specifying -this option multiple times. If supported, this will cause the guest to -automatically try to append the given domain suffix(es) in case a domain name -can not be resolved. - -Example: -@example -@value{qemu_system} -nic user,dnssearch=mgmt.example.org,dnssearch=example.org -@end example - -@item domainname=@var{domain} -Specifies the client domain name reported by the built-in DHCP server. - -@item tftp=@var{dir} -When using the user mode network stack, activate a built-in TFTP -server. The files in @var{dir} will be exposed as the root of a TFTP server. -The TFTP client on the guest must be configured in binary mode (use the command -@code{bin} of the Unix TFTP client). - -@item tftp-server-name=@var{name} -In BOOTP reply, broadcast @var{name} as the "TFTP server name" (RFC2132 option -66). This can be used to advise the guest to load boot files or configurations -from a different server than the host address. - -@item bootfile=@var{file} -When using the user mode network stack, broadcast @var{file} as the BOOTP -filename. In conjunction with @option{tftp}, this can be used to network boot -a guest from a local directory. - -Example (using pxelinux): -@example -@value{qemu_system} -hda linux.img -boot n -device e1000,netdev=n1 \ - -netdev user,id=n1,tftp=/path/to/tftp/files,bootfile=/pxelinux.0 -@end example - -@item smb=@var{dir}[,smbserver=@var{addr}] -When using the user mode network stack, activate a built-in SMB -server so that Windows OSes can access to the host files in @file{@var{dir}} -transparently. The IP address of the SMB server can be set to @var{addr}. By -default the 4th IP in the guest network is used, i.e. x.x.x.4. - -In the guest Windows OS, the line: -@example -10.0.2.4 smbserver -@end example -must be added in the file @file{C:\WINDOWS\LMHOSTS} (for windows 9x/Me) -or @file{C:\WINNT\SYSTEM32\DRIVERS\ETC\LMHOSTS} (Windows NT/2000). - -Then @file{@var{dir}} can be accessed in @file{\\smbserver\qemu}. - -Note that a SAMBA server must be installed on the host OS. - -@item hostfwd=[tcp|udp]:[@var{hostaddr}]:@var{hostport}-[@var{guestaddr}]:@var{guestport} -Redirect incoming TCP or UDP connections to the host port @var{hostport} to -the guest IP address @var{guestaddr} on guest port @var{guestport}. If -@var{guestaddr} is not specified, its value is x.x.x.15 (default first address -given by the built-in DHCP server). By specifying @var{hostaddr}, the rule can -be bound to a specific host interface. If no connection type is set, TCP is -used. This option can be given multiple times. - -For example, to redirect host X11 connection from screen 1 to guest -screen 0, use the following: - -@example -# on the host -@value{qemu_system} -nic user,hostfwd=tcp:127.0.0.1:6001-:6000 -# this host xterm should open in the guest X11 server -xterm -display :1 -@end example - -To redirect telnet connections from host port 5555 to telnet port on -the guest, use the following: - -@example -# on the host -@value{qemu_system} -nic user,hostfwd=tcp::5555-:23 -telnet localhost 5555 -@end example - -Then when you use on the host @code{telnet localhost 5555}, you -connect to the guest telnet server. - -@item guestfwd=[tcp]:@var{server}:@var{port}-@var{dev} -@itemx guestfwd=[tcp]:@var{server}:@var{port}-@var{cmd:command} -Forward guest TCP connections to the IP address @var{server} on port @var{port} -to the character device @var{dev} or to a program executed by @var{cmd:command} -which gets spawned for each connection. This option can be given multiple times. - -You can either use a chardev directly and have that one used throughout QEMU's -lifetime, like in the following example: - -@example -# open 10.10.1.1:4321 on bootup, connect 10.0.2.100:1234 to it whenever -# the guest accesses it -@value{qemu_system} -nic user,guestfwd=tcp:10.0.2.100:1234-tcp:10.10.1.1:4321 -@end example - -Or you can execute a command on every TCP connection established by the guest, -so that QEMU behaves similar to an inetd process for that virtual server: - -@example -# call "netcat 10.10.1.1 4321" on every TCP connection to 10.0.2.100:1234 -# and connect the TCP stream to its stdin/stdout -@value{qemu_system} -nic 'user,id=n1,guestfwd=tcp:10.0.2.100:1234-cmd:netcat 10.10.1.1 4321' -@end example - -@end table - -@item -netdev tap,id=@var{id}[,fd=@var{h}][,ifname=@var{name}][,script=@var{file}][,downscript=@var{dfile}][,br=@var{bridge}][,helper=@var{helper}] -Configure a host TAP network backend with ID @var{id}. - -Use the network script @var{file} to configure it and the network script -@var{dfile} to deconfigure it. If @var{name} is not provided, the OS -automatically provides one. The default network configure script is -@file{/etc/qemu-ifup} and the default network deconfigure script is -@file{/etc/qemu-ifdown}. Use @option{script=no} or @option{downscript=no} -to disable script execution. - -If running QEMU as an unprivileged user, use the network helper -@var{helper} to configure the TAP interface and attach it to the bridge. -The default network helper executable is @file{/path/to/qemu-bridge-helper} -and the default bridge device is @file{br0}. - -@option{fd}=@var{h} can be used to specify the handle of an already -opened host TAP interface. - -Examples: - -@example -#launch a QEMU instance with the default network script -@value{qemu_system} linux.img -nic tap -@end example - -@example -#launch a QEMU instance with two NICs, each one connected -#to a TAP device -@value{qemu_system} linux.img \ - -netdev tap,id=nd0,ifname=tap0 -device e1000,netdev=nd0 \ - -netdev tap,id=nd1,ifname=tap1 -device rtl8139,netdev=nd1 -@end example - -@example -#launch a QEMU instance with the default network helper to -#connect a TAP device to bridge br0 -@value{qemu_system} linux.img -device virtio-net-pci,netdev=n1 \ - -netdev tap,id=n1,"helper=/path/to/qemu-bridge-helper" -@end example - -@item -netdev bridge,id=@var{id}[,br=@var{bridge}][,helper=@var{helper}] -Connect a host TAP network interface to a host bridge device. - -Use the network helper @var{helper} to configure the TAP interface and -attach it to the bridge. The default network helper executable is -@file{/path/to/qemu-bridge-helper} and the default bridge -device is @file{br0}. - -Examples: - -@example -#launch a QEMU instance with the default network helper to -#connect a TAP device to bridge br0 -@value{qemu_system} linux.img -netdev bridge,id=n1 -device virtio-net,netdev=n1 -@end example - -@example -#launch a QEMU instance with the default network helper to -#connect a TAP device to bridge qemubr0 -@value{qemu_system} linux.img -netdev bridge,br=qemubr0,id=n1 -device virtio-net,netdev=n1 -@end example - -@item -netdev socket,id=@var{id}[,fd=@var{h}][,listen=[@var{host}]:@var{port}][,connect=@var{host}:@var{port}] - -This host network backend can be used to connect the guest's network to -another QEMU virtual machine using a TCP socket connection. If @option{listen} -is specified, QEMU waits for incoming connections on @var{port} -(@var{host} is optional). @option{connect} is used to connect to -another QEMU instance using the @option{listen} option. @option{fd}=@var{h} -specifies an already opened TCP socket. - -Example: -@example -# launch a first QEMU instance -@value{qemu_system} linux.img \ - -device e1000,netdev=n1,mac=52:54:00:12:34:56 \ - -netdev socket,id=n1,listen=:1234 -# connect the network of this instance to the network of the first instance -@value{qemu_system} linux.img \ - -device e1000,netdev=n2,mac=52:54:00:12:34:57 \ - -netdev socket,id=n2,connect=127.0.0.1:1234 -@end example - -@item -netdev socket,id=@var{id}[,fd=@var{h}][,mcast=@var{maddr}:@var{port}[,localaddr=@var{addr}]] - -Configure a socket host network backend to share the guest's network traffic -with another QEMU virtual machines using a UDP multicast socket, effectively -making a bus for every QEMU with same multicast address @var{maddr} and @var{port}. -NOTES: -@enumerate -@item -Several QEMU can be running on different hosts and share same bus (assuming -correct multicast setup for these hosts). -@item -mcast support is compatible with User Mode Linux (argument @option{eth@var{N}=mcast}), see -@url{http://user-mode-linux.sf.net}. -@item -Use @option{fd=h} to specify an already opened UDP multicast socket. -@end enumerate - -Example: -@example -# launch one QEMU instance -@value{qemu_system} linux.img \ - -device e1000,netdev=n1,mac=52:54:00:12:34:56 \ - -netdev socket,id=n1,mcast=230.0.0.1:1234 -# launch another QEMU instance on same "bus" -@value{qemu_system} linux.img \ - -device e1000,netdev=n2,mac=52:54:00:12:34:57 \ - -netdev socket,id=n2,mcast=230.0.0.1:1234 -# launch yet another QEMU instance on same "bus" -@value{qemu_system} linux.img \ - -device e1000,netdev=n3,mac=52:54:00:12:34:58 \ - -netdev socket,id=n3,mcast=230.0.0.1:1234 -@end example - -Example (User Mode Linux compat.): -@example -# launch QEMU instance (note mcast address selected is UML's default) -@value{qemu_system} linux.img \ - -device e1000,netdev=n1,mac=52:54:00:12:34:56 \ - -netdev socket,id=n1,mcast=239.192.168.1:1102 -# launch UML -/path/to/linux ubd0=/path/to/root_fs eth0=mcast -@end example - -Example (send packets from host's 1.2.3.4): -@example -@value{qemu_system} linux.img \ - -device e1000,netdev=n1,mac=52:54:00:12:34:56 \ - -netdev socket,id=n1,mcast=239.192.168.1:1102,localaddr=1.2.3.4 -@end example - -@item -netdev l2tpv3,id=@var{id},src=@var{srcaddr},dst=@var{dstaddr}[,srcport=@var{srcport}][,dstport=@var{dstport}],txsession=@var{txsession}[,rxsession=@var{rxsession}][,ipv6][,udp][,cookie64][,counter][,pincounter][,txcookie=@var{txcookie}][,rxcookie=@var{rxcookie}][,offset=@var{offset}] -Configure a L2TPv3 pseudowire host network backend. L2TPv3 (RFC3391) is a -popular protocol to transport Ethernet (and other Layer 2) data frames between -two systems. It is present in routers, firewalls and the Linux kernel -(from version 3.3 onwards). - -This transport allows a VM to communicate to another VM, router or firewall directly. - -@table @option -@item src=@var{srcaddr} - source address (mandatory) -@item dst=@var{dstaddr} - destination address (mandatory) -@item udp - select udp encapsulation (default is ip). -@item srcport=@var{srcport} - source udp port. -@item dstport=@var{dstport} - destination udp port. -@item ipv6 - force v6, otherwise defaults to v4. -@item rxcookie=@var{rxcookie} -@itemx txcookie=@var{txcookie} - Cookies are a weak form of security in the l2tpv3 specification. -Their function is mostly to prevent misconfiguration. By default they are 32 -bit. -@item cookie64 - Set cookie size to 64 bit instead of the default 32 -@item counter=off - Force a 'cut-down' L2TPv3 with no counter as in -draft-mkonstan-l2tpext-keyed-ipv6-tunnel-00 -@item pincounter=on - Work around broken counter handling in peer. This may also help on -networks which have packet reorder. -@item offset=@var{offset} - Add an extra offset between header and data -@end table - -For example, to attach a VM running on host 4.3.2.1 via L2TPv3 to the bridge br-lan -on the remote Linux host 1.2.3.4: -@example -# Setup tunnel on linux host using raw ip as encapsulation -# on 1.2.3.4 -ip l2tp add tunnel remote 4.3.2.1 local 1.2.3.4 tunnel_id 1 peer_tunnel_id 1 \ - encap udp udp_sport 16384 udp_dport 16384 -ip l2tp add session tunnel_id 1 name vmtunnel0 session_id \ - 0xFFFFFFFF peer_session_id 0xFFFFFFFF -ifconfig vmtunnel0 mtu 1500 -ifconfig vmtunnel0 up -brctl addif br-lan vmtunnel0 - - -# on 4.3.2.1 -# launch QEMU instance - if your network has reorder or is very lossy add ,pincounter - -@value{qemu_system} linux.img -device e1000,netdev=n1 \ - -netdev l2tpv3,id=n1,src=4.2.3.1,dst=1.2.3.4,udp,srcport=16384,dstport=16384,rxsession=0xffffffff,txsession=0xffffffff,counter - -@end example - -@item -netdev vde,id=@var{id}[,sock=@var{socketpath}][,port=@var{n}][,group=@var{groupname}][,mode=@var{octalmode}] -Configure VDE backend to connect to PORT @var{n} of a vde switch running on host and -listening for incoming connections on @var{socketpath}. Use GROUP @var{groupname} -and MODE @var{octalmode} to change default ownership and permissions for -communication port. This option is only available if QEMU has been compiled -with vde support enabled. - -Example: -@example -# launch vde switch -vde_switch -F -sock /tmp/myswitch -# launch QEMU instance -@value{qemu_system} linux.img -nic vde,sock=/tmp/myswitch -@end example - -@item -netdev vhost-user,chardev=@var{id}[,vhostforce=on|off][,queues=n] - -Establish a vhost-user netdev, backed by a chardev @var{id}. The chardev should -be a unix domain socket backed one. The vhost-user uses a specifically defined -protocol to pass vhost ioctl replacement messages to an application on the other -end of the socket. On non-MSIX guests, the feature can be forced with -@var{vhostforce}. Use 'queues=@var{n}' to specify the number of queues to -be created for multiqueue vhost-user. - -Example: -@example -qemu -m 512 -object memory-backend-file,id=mem,size=512M,mem-path=/hugetlbfs,share=on \ - -numa node,memdev=mem \ - -chardev socket,id=chr0,path=/path/to/socket \ - -netdev type=vhost-user,id=net0,chardev=chr0 \ - -device virtio-net-pci,netdev=net0 -@end example - -@item -netdev hubport,id=@var{id},hubid=@var{hubid}[,netdev=@var{nd}] - -Create a hub port on the emulated hub with ID @var{hubid}. - -The hubport netdev lets you connect a NIC to a QEMU emulated hub instead of a -single netdev. Alternatively, you can also connect the hubport to another -netdev with ID @var{nd} by using the @option{netdev=@var{nd}} option. - -@item -net nic[,netdev=@var{nd}][,macaddr=@var{mac}][,model=@var{type}] [,name=@var{name}][,addr=@var{addr}][,vectors=@var{v}] -@findex -net -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. -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 -NIC is created. QEMU can emulate several different models of network card. -Use @code{-net nic,model=help} for a list of available devices for your target. - -@item -net user|tap|bridge|socket|l2tpv3|vde[,...][,name=@var{name}] -Configure a host network backend (with the options corresponding to the same -@option{-netdev} option) and connect it to the emulated hub 0 (the default -hub). Use @var{name} to specify the name of the hub port. -ETEXI - -STEXI -@end table -ETEXI +SRST +``-nic [tap|bridge|user|l2tpv3|vde|netmap|vhost-user|socket][,...][,mac=macaddr][,model=mn]`` + This option is a shortcut for configuring both the on-board + (default) guest NIC hardware and the host network backend in one go. + The host backend options are the same as with the corresponding + ``-netdev`` options below. The guest NIC model can be set with + ``model=modelname``. Use ``model=help`` to list the available device + types. The hardware MAC address can be set with ``mac=macaddr``. + + The following two example do exactly the same, to show how ``-nic`` + can be used to shorten the command line length: + + .. parsed-literal:: + + |qemu_system| -netdev user,id=n1,ipv6=off -device e1000,netdev=n1,mac=52:54:98:76:54:32 + |qemu_system| -nic user,ipv6=off,model=e1000,mac=52:54:98:76:54:32 + +``-nic none`` + Indicate that no network devices should be configured. It is used to + override the default configuration (default NIC with "user" host + network backend) which is activated if no other networking options + are provided. + +``-netdev user,id=id[,option][,option][,...]`` + Configure user mode host network backend which requires no + administrator privilege to run. Valid options are: + + ``id=id`` + Assign symbolic name for use in monitor commands. + + ``ipv4=on|off and ipv6=on|off`` + Specify that either IPv4 or IPv6 must be enabled. If neither is + specified both protocols are enabled. + + ``net=addr[/mask]`` + Set IP network address the guest will see. Optionally specify + the netmask, either in the form a.b.c.d or as number of valid + top-most bits. Default is 10.0.2.0/24. + + ``host=addr`` + Specify the guest-visible address of the host. Default is the + 2nd IP in the guest network, i.e. x.x.x.2. + + ``ipv6-net=addr[/int]`` + Set IPv6 network address the guest will see (default is + fec0::/64). The network prefix is given in the usual hexadecimal + IPv6 address notation. The prefix size is optional, and is given + as the number of valid top-most bits (default is 64). + + ``ipv6-host=addr`` + Specify the guest-visible IPv6 address of the host. Default is + the 2nd IPv6 in the guest network, i.e. xxxx::2. + + ``restrict=on|off`` + If this option is enabled, the guest will be isolated, i.e. it + will not be able to contact the host and no guest IP packets + will be routed over the host to the outside. This option does + not affect any explicitly set forwarding rules. + + ``hostname=name`` + Specifies the client hostname reported by the built-in DHCP + server. + + ``dhcpstart=addr`` + Specify the first of the 16 IPs the built-in DHCP server can + assign. Default is the 15th to 31st IP in the guest network, + i.e. x.x.x.15 to x.x.x.31. + + ``dns=addr`` + Specify the guest-visible address of the virtual nameserver. The + address must be different from the host address. Default is the + 3rd IP in the guest network, i.e. x.x.x.3. + + ``ipv6-dns=addr`` + Specify the guest-visible address of the IPv6 virtual + nameserver. The address must be different from the host address. + Default is the 3rd IP in the guest network, i.e. xxxx::3. + + ``dnssearch=domain`` + Provides an entry for the domain-search list sent by the + built-in DHCP server. More than one domain suffix can be + transmitted by specifying this option multiple times. If + supported, this will cause the guest to automatically try to + append the given domain suffix(es) in case a domain name can not + be resolved. + + Example: + + .. parsed-literal:: + + |qemu_system| -nic user,dnssearch=mgmt.example.org,dnssearch=example.org + + ``domainname=domain`` + Specifies the client domain name reported by the built-in DHCP + server. + + ``tftp=dir`` + When using the user mode network stack, activate a built-in TFTP + server. The files in dir will be exposed as the root of a TFTP + server. The TFTP client on the guest must be configured in + binary mode (use the command ``bin`` of the Unix TFTP client). + + ``tftp-server-name=name`` + In BOOTP reply, broadcast name as the "TFTP server name" + (RFC2132 option 66). This can be used to advise the guest to + load boot files or configurations from a different server than + the host address. + + ``bootfile=file`` + When using the user mode network stack, broadcast file as the + BOOTP filename. In conjunction with ``tftp``, this can be used + to network boot a guest from a local directory. + + Example (using pxelinux): + + .. parsed-literal:: + + |qemu_system| -hda linux.img -boot n -device e1000,netdev=n1 \ + -netdev user,id=n1,tftp=/path/to/tftp/files,bootfile=/pxelinux.0 + + ``smb=dir[,smbserver=addr]`` + When using the user mode network stack, activate a built-in SMB + server so that Windows OSes can access to the host files in + ``dir`` transparently. The IP address of the SMB server can be + set to addr. By default the 4th IP in the guest network is used, + i.e. x.x.x.4. + + In the guest Windows OS, the line: + + :: + + 10.0.2.4 smbserver + + must be added in the file ``C:\WINDOWS\LMHOSTS`` (for windows + 9x/Me) or ``C:\WINNT\SYSTEM32\DRIVERS\ETC\LMHOSTS`` (Windows + NT/2000). + + Then ``dir`` can be accessed in ``\\smbserver\qemu``. + + Note that a SAMBA server must be installed on the host OS. + + ``hostfwd=[tcp|udp]:[hostaddr]:hostport-[guestaddr]:guestport`` + Redirect incoming TCP or UDP connections to the host port + hostport to the guest IP address guestaddr on guest port + guestport. If guestaddr is not specified, its value is x.x.x.15 + (default first address given by the built-in DHCP server). By + specifying hostaddr, the rule can be bound to a specific host + interface. If no connection type is set, TCP is used. This + option can be given multiple times. + + For example, to redirect host X11 connection from screen 1 to + guest screen 0, use the following: + + .. parsed-literal:: + + # on the host + |qemu_system| -nic user,hostfwd=tcp:127.0.0.1:6001-:6000 + # this host xterm should open in the guest X11 server + xterm -display :1 + + To redirect telnet connections from host port 5555 to telnet + port on the guest, use the following: + + .. parsed-literal:: + + # on the host + |qemu_system| -nic user,hostfwd=tcp::5555-:23 + telnet localhost 5555 + + Then when you use on the host ``telnet localhost 5555``, you + connect to the guest telnet server. + + ``guestfwd=[tcp]:server:port-dev``; \ ``guestfwd=[tcp]:server:port-cmd:command`` + Forward guest TCP connections to the IP address server on port + port to the character device dev or to a program executed by + cmd:command which gets spawned for each connection. This option + can be given multiple times. + + You can either use a chardev directly and have that one used + throughout QEMU's lifetime, like in the following example: + + .. parsed-literal:: + + # open 10.10.1.1:4321 on bootup, connect 10.0.2.100:1234 to it whenever + # the guest accesses it + |qemu_system| -nic user,guestfwd=tcp:10.0.2.100:1234-tcp:10.10.1.1:4321 + + Or you can execute a command on every TCP connection established + by the guest, so that QEMU behaves similar to an inetd process + for that virtual server: + + .. parsed-literal:: + + # call "netcat 10.10.1.1 4321" on every TCP connection to 10.0.2.100:1234 + # and connect the TCP stream to its stdin/stdout + |qemu_system| -nic 'user,id=n1,guestfwd=tcp:10.0.2.100:1234-cmd:netcat 10.10.1.1 4321' + +``-netdev tap,id=id[,fd=h][,ifname=name][,script=file][,downscript=dfile][,br=bridge][,helper=helper]`` + Configure a host TAP network backend with ID id. + + Use the network script file to configure it and the network script + dfile to deconfigure it. If name is not provided, the OS + automatically provides one. The default network configure script is + ``/etc/qemu-ifup`` and the default network deconfigure script is + ``/etc/qemu-ifdown``. Use ``script=no`` or ``downscript=no`` to + disable script execution. + + If running QEMU as an unprivileged user, use the network helper + helper to configure the TAP interface and attach it to the bridge. + The default network helper executable is + ``/path/to/qemu-bridge-helper`` and the default bridge device is + ``br0``. + + ``fd``\ =h can be used to specify the handle of an already opened + host TAP interface. + + Examples: + + .. parsed-literal:: + + #launch a QEMU instance with the default network script + |qemu_system| linux.img -nic tap + + .. parsed-literal:: + + #launch a QEMU instance with two NICs, each one connected + #to a TAP device + |qemu_system| linux.img \ + -netdev tap,id=nd0,ifname=tap0 -device e1000,netdev=nd0 \ + -netdev tap,id=nd1,ifname=tap1 -device rtl8139,netdev=nd1 + + .. parsed-literal:: + + #launch a QEMU instance with the default network helper to + #connect a TAP device to bridge br0 + |qemu_system| linux.img -device virtio-net-pci,netdev=n1 \ + -netdev tap,id=n1,"helper=/path/to/qemu-bridge-helper" + +``-netdev bridge,id=id[,br=bridge][,helper=helper]`` + Connect a host TAP network interface to a host bridge device. + + Use the network helper helper to configure the TAP interface and + attach it to the bridge. The default network helper executable is + ``/path/to/qemu-bridge-helper`` and the default bridge device is + ``br0``. + + Examples: + + .. parsed-literal:: + + #launch a QEMU instance with the default network helper to + #connect a TAP device to bridge br0 + |qemu_system| linux.img -netdev bridge,id=n1 -device virtio-net,netdev=n1 + + .. parsed-literal:: + + #launch a QEMU instance with the default network helper to + #connect a TAP device to bridge qemubr0 + |qemu_system| linux.img -netdev bridge,br=qemubr0,id=n1 -device virtio-net,netdev=n1 + +``-netdev socket,id=id[,fd=h][,listen=[host]:port][,connect=host:port]`` + This host network backend can be used to connect the guest's network + to another QEMU virtual machine using a TCP socket connection. If + ``listen`` is specified, QEMU waits for incoming connections on port + (host is optional). ``connect`` is used to connect to another QEMU + instance using the ``listen`` option. ``fd``\ =h specifies an + already opened TCP socket. + + Example: + + .. parsed-literal:: + + # launch a first QEMU instance + |qemu_system| linux.img \ + -device e1000,netdev=n1,mac=52:54:00:12:34:56 \ + -netdev socket,id=n1,listen=:1234 + # connect the network of this instance to the network of the first instance + |qemu_system| linux.img \ + -device e1000,netdev=n2,mac=52:54:00:12:34:57 \ + -netdev socket,id=n2,connect=127.0.0.1:1234 + +``-netdev socket,id=id[,fd=h][,mcast=maddr:port[,localaddr=addr]]`` + Configure a socket host network backend to share the guest's network + traffic with another QEMU virtual machines using a UDP multicast + socket, effectively making a bus for every QEMU with same multicast + address maddr and port. NOTES: + + 1. Several QEMU can be running on different hosts and share same bus + (assuming correct multicast setup for these hosts). + + 2. mcast support is compatible with User Mode Linux (argument + ``ethN=mcast``), see http://user-mode-linux.sf.net. + + 3. Use ``fd=h`` to specify an already opened UDP multicast socket. + + Example: + + .. parsed-literal:: + + # launch one QEMU instance + |qemu_system| linux.img \ + -device e1000,netdev=n1,mac=52:54:00:12:34:56 \ + -netdev socket,id=n1,mcast=230.0.0.1:1234 + # launch another QEMU instance on same "bus" + |qemu_system| linux.img \ + -device e1000,netdev=n2,mac=52:54:00:12:34:57 \ + -netdev socket,id=n2,mcast=230.0.0.1:1234 + # launch yet another QEMU instance on same "bus" + |qemu_system| linux.img \ + -device e1000,netdev=n3,mac=52:54:00:12:34:58 \ + -netdev socket,id=n3,mcast=230.0.0.1:1234 + + Example (User Mode Linux compat.): + + .. parsed-literal:: + + # launch QEMU instance (note mcast address selected is UML's default) + |qemu_system| linux.img \ + -device e1000,netdev=n1,mac=52:54:00:12:34:56 \ + -netdev socket,id=n1,mcast=239.192.168.1:1102 + # launch UML + /path/to/linux ubd0=/path/to/root_fs eth0=mcast + + Example (send packets from host's 1.2.3.4): + + .. parsed-literal:: + + |qemu_system| linux.img \ + -device e1000,netdev=n1,mac=52:54:00:12:34:56 \ + -netdev socket,id=n1,mcast=239.192.168.1:1102,localaddr=1.2.3.4 + +``-netdev l2tpv3,id=id,src=srcaddr,dst=dstaddr[,srcport=srcport][,dstport=dstport],txsession=txsession[,rxsession=rxsession][,ipv6][,udp][,cookie64][,counter][,pincounter][,txcookie=txcookie][,rxcookie=rxcookie][,offset=offset]`` + Configure a L2TPv3 pseudowire host network backend. L2TPv3 (RFC3931) + is a popular protocol to transport Ethernet (and other Layer 2) data + frames between two systems. It is present in routers, firewalls and + the Linux kernel (from version 3.3 onwards). + + This transport allows a VM to communicate to another VM, router or + firewall directly. + + ``src=srcaddr`` + source address (mandatory) + + ``dst=dstaddr`` + destination address (mandatory) + + ``udp`` + select udp encapsulation (default is ip). + + ``srcport=srcport`` + source udp port. + + ``dstport=dstport`` + destination udp port. + + ``ipv6`` + force v6, otherwise defaults to v4. + + ``rxcookie=rxcookie``; \ ``txcookie=txcookie`` + Cookies are a weak form of security in the l2tpv3 specification. + Their function is mostly to prevent misconfiguration. By default + they are 32 bit. + + ``cookie64`` + Set cookie size to 64 bit instead of the default 32 + + ``counter=off`` + Force a 'cut-down' L2TPv3 with no counter as in + draft-mkonstan-l2tpext-keyed-ipv6-tunnel-00 + + ``pincounter=on`` + Work around broken counter handling in peer. This may also help + on networks which have packet reorder. + + ``offset=offset`` + Add an extra offset between header and data + + For example, to attach a VM running on host 4.3.2.1 via L2TPv3 to + the bridge br-lan on the remote Linux host 1.2.3.4: + + .. parsed-literal:: + + # Setup tunnel on linux host using raw ip as encapsulation + # on 1.2.3.4 + ip l2tp add tunnel remote 4.3.2.1 local 1.2.3.4 tunnel_id 1 peer_tunnel_id 1 \ + encap udp udp_sport 16384 udp_dport 16384 + ip l2tp add session tunnel_id 1 name vmtunnel0 session_id \ + 0xFFFFFFFF peer_session_id 0xFFFFFFFF + ifconfig vmtunnel0 mtu 1500 + ifconfig vmtunnel0 up + brctl addif br-lan vmtunnel0 + + + # on 4.3.2.1 + # launch QEMU instance - if your network has reorder or is very lossy add ,pincounter + + |qemu_system| linux.img -device e1000,netdev=n1 \ + -netdev l2tpv3,id=n1,src=4.2.3.1,dst=1.2.3.4,udp,srcport=16384,dstport=16384,rxsession=0xffffffff,txsession=0xffffffff,counter + +``-netdev vde,id=id[,sock=socketpath][,port=n][,group=groupname][,mode=octalmode]`` + Configure VDE backend to connect to PORT n of a vde switch running + on host and listening for incoming connections on socketpath. Use + GROUP groupname and MODE octalmode to change default ownership and + permissions for communication port. This option is only available if + QEMU has been compiled with vde support enabled. + + Example: + + .. parsed-literal:: + + # launch vde switch + vde_switch -F -sock /tmp/myswitch + # launch QEMU instance + |qemu_system| linux.img -nic vde,sock=/tmp/myswitch + +``-netdev vhost-user,chardev=id[,vhostforce=on|off][,queues=n]`` + Establish a vhost-user netdev, backed by a chardev id. The chardev + should be a unix domain socket backed one. The vhost-user uses a + specifically defined protocol to pass vhost ioctl replacement + messages to an application on the other end of the socket. On + non-MSIX guests, the feature can be forced with vhostforce. Use + 'queues=n' to specify the number of queues to be created for + multiqueue vhost-user. + + Example: + + :: + + qemu -m 512 -object memory-backend-file,id=mem,size=512M,mem-path=/hugetlbfs,share=on \ + -numa node,memdev=mem \ + -chardev socket,id=chr0,path=/path/to/socket \ + -netdev type=vhost-user,id=net0,chardev=chr0 \ + -device virtio-net-pci,netdev=net0 + +``-netdev hubport,id=id,hubid=hubid[,netdev=nd]`` + Create a hub port on the emulated hub with ID hubid. + + The hubport netdev lets you connect a NIC to a QEMU emulated hub + instead of a single netdev. Alternatively, you can also connect the + hubport to another netdev with ID nd by using the ``netdev=nd`` + option. + +``-net nic[,netdev=nd][,macaddr=mac][,model=type] [,name=name][,addr=addr][,vectors=v]`` + 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 nd. + If 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 mac, the + device address set to addr (PCI cards only), and a name can be + assigned for use in monitor commands. Optionally, for PCI cards, you + can specify the number v of MSI-X vectors that the card should have; + this option currently only affects virtio cards; set v = 0 to + disable MSI-X. If no ``-net`` option is specified, a single NIC is + created. QEMU can emulate several different models of network card. + Use ``-net nic,model=help`` for a list of available devices for your + target. + +``-net user|tap|bridge|socket|l2tpv3|vde[,...][,name=name]`` + Configure a host network backend (with the options corresponding to + the same ``-netdev`` option) and connect it to the emulated hub 0 + (the default hub). Use name to specify the name of the hub port. +ERST + DEFHEADING() DEFHEADING(Character device options:) @@ -2827,381 +2966,267 @@ DEF("chardev", HAS_ARG, QEMU_OPTION_chardev, , QEMU_ARCH_ALL ) -STEXI - +SRST The general form of a character device option is: -@table @option -@item -chardev @var{backend},id=@var{id}[,mux=on|off][,@var{options}] -@findex -chardev -Backend is one of: -@option{null}, -@option{socket}, -@option{udp}, -@option{msmouse}, -@option{vc}, -@option{ringbuf}, -@option{file}, -@option{pipe}, -@option{console}, -@option{serial}, -@option{pty}, -@option{stdio}, -@option{braille}, -@option{tty}, -@option{parallel}, -@option{parport}, -@option{spicevmc}, -@option{spiceport}. -The specific backend will determine the applicable options. -Use @code{-chardev help} to print all available chardev backend types. +``-chardev backend,id=id[,mux=on|off][,options]`` + Backend is one of: ``null``, ``socket``, ``udp``, ``msmouse``, + ``vc``, ``ringbuf``, ``file``, ``pipe``, ``console``, ``serial``, + ``pty``, ``stdio``, ``braille``, ``tty``, ``parallel``, ``parport``, + ``spicevmc``, ``spiceport``. The specific backend will determine the + applicable options. -All devices must have an id, which can be any string up to 127 characters long. -It is used to uniquely identify this device in other command line directives. + Use ``-chardev help`` to print all available chardev backend types. -A character device may be used in multiplexing mode by multiple front-ends. -Specify @option{mux=on} to enable this mode. -A multiplexer is a "1:N" device, and here the "1" end is your specified chardev -backend, and the "N" end is the various parts of QEMU that can talk to a chardev. -If you create a chardev with @option{id=myid} and @option{mux=on}, QEMU will -create a multiplexer with your specified ID, and you can then configure multiple -front ends to use that chardev ID for their input/output. Up to four different -front ends can be connected to a single multiplexed chardev. (Without -multiplexing enabled, a chardev can only be used by a single front end.) -For instance you could use this to allow a single stdio chardev to be used by -two serial ports and the QEMU monitor: + All devices must have an id, which can be any string up to 127 + characters long. It is used to uniquely identify this device in + other command line directives. -@example --chardev stdio,mux=on,id=char0 \ --mon chardev=char0,mode=readline \ --serial chardev:char0 \ --serial chardev:char0 -@end example + A character device may be used in multiplexing mode by multiple + front-ends. Specify ``mux=on`` to enable this mode. A multiplexer is + a "1:N" device, and here the "1" end is your specified chardev + backend, and the "N" end is the various parts of QEMU that can talk + to a chardev. If you create a chardev with ``id=myid`` and + ``mux=on``, QEMU will create a multiplexer with your specified ID, + and you can then configure multiple front ends to use that chardev + ID for their input/output. Up to four different front ends can be + connected to a single multiplexed chardev. (Without multiplexing + enabled, a chardev can only be used by a single front end.) For + instance you could use this to allow a single stdio chardev to be + used by two serial ports and the QEMU monitor: -You can have more than one multiplexer in a system configuration; for instance -you could have a TCP port multiplexed between UART 0 and UART 1, and stdio -multiplexed between the QEMU monitor and a parallel port: + :: -@example --chardev stdio,mux=on,id=char0 \ --mon chardev=char0,mode=readline \ --parallel chardev:char0 \ --chardev tcp,...,mux=on,id=char1 \ --serial chardev:char1 \ --serial chardev:char1 -@end example + -chardev stdio,mux=on,id=char0 \ + -mon chardev=char0,mode=readline \ + -serial chardev:char0 \ + -serial chardev:char0 -When you're using a multiplexed character device, some escape sequences are -interpreted in the input. @xref{mux_keys, Keys in the character backend -multiplexer}. + You can have more than one multiplexer in a system configuration; + for instance you could have a TCP port multiplexed between UART 0 + and UART 1, and stdio multiplexed between the QEMU monitor and a + parallel port: -Note that some other command line options may implicitly create multiplexed -character backends; for instance @option{-serial mon:stdio} creates a -multiplexed stdio backend connected to the serial port and the QEMU monitor, -and @option{-nographic} also multiplexes the console and the monitor to -stdio. + :: -There is currently no support for multiplexing in the other direction -(where a single QEMU front end takes input and output from multiple chardevs). + -chardev stdio,mux=on,id=char0 \ + -mon chardev=char0,mode=readline \ + -parallel chardev:char0 \ + -chardev tcp,...,mux=on,id=char1 \ + -serial chardev:char1 \ + -serial chardev:char1 -Every backend supports the @option{logfile} option, which supplies the path -to a file to record all data transmitted via the backend. The @option{logappend} -option controls whether the log file will be truncated or appended to when -opened. + When you're using a multiplexed character device, some escape + sequences are interpreted in the input. See :ref:`mux_005fkeys`. -@end table + Note that some other command line options may implicitly create + multiplexed character backends; for instance ``-serial mon:stdio`` + creates a multiplexed stdio backend connected to the serial port and + the QEMU monitor, and ``-nographic`` also multiplexes the console + and the monitor to stdio. + + There is currently no support for multiplexing in the other + direction (where a single QEMU front end takes input and output from + multiple chardevs). + + Every backend supports the ``logfile`` option, which supplies the + path to a file to record all data transmitted via the backend. The + ``logappend`` option controls whether the log file will be truncated + or appended to when opened. The available backends are: -@table @option -@item -chardev null,id=@var{id} -A void device. This device will not emit any data, and will drop any data it -receives. The null backend does not take any options. +``-chardev null,id=id`` + A void device. This device will not emit any data, and will drop any + data it receives. The null backend does not take any options. -@item -chardev socket,id=@var{id}[,@var{TCP options} or @var{unix options}][,server][,nowait][,telnet][,websocket][,reconnect=@var{seconds}][,tls-creds=@var{id}][,tls-authz=@var{id}] +``-chardev socket,id=id[,TCP options or unix options][,server][,nowait][,telnet][,websocket][,reconnect=seconds][,tls-creds=id][,tls-authz=id]`` + Create a two-way stream socket, which can be either a TCP or a unix + socket. A unix socket will be created if ``path`` is specified. + Behaviour is undefined if TCP options are specified for a unix + socket. -Create a two-way stream socket, which can be either a TCP or a unix socket. A -unix socket will be created if @option{path} is specified. Behaviour is -undefined if TCP options are specified for a unix socket. + ``server`` specifies that the socket shall be a listening socket. -@option{server} specifies that the socket shall be a listening socket. + ``nowait`` specifies that QEMU should not block waiting for a client + to connect to a listening socket. -@option{nowait} specifies that QEMU should not block waiting for a client to -connect to a listening socket. + ``telnet`` specifies that traffic on the socket should interpret + telnet escape sequences. -@option{telnet} specifies that traffic on the socket should interpret telnet -escape sequences. + ``websocket`` specifies that the socket uses WebSocket protocol for + communication. -@option{websocket} specifies that the socket uses WebSocket protocol for -communication. + ``reconnect`` sets the timeout for reconnecting on non-server + sockets when the remote end goes away. qemu will delay this many + seconds and then attempt to reconnect. Zero disables reconnecting, + and is the default. -@option{reconnect} sets the timeout for reconnecting on non-server sockets when -the remote end goes away. qemu will delay this many seconds and then attempt -to reconnect. Zero disables reconnecting, and is the default. + ``tls-creds`` requests enablement of the TLS protocol for + encryption, and specifies the id of the TLS credentials to use for + the handshake. The credentials must be previously created with the + ``-object tls-creds`` argument. -@option{tls-creds} requests enablement of the TLS protocol for encryption, -and specifies the id of the TLS credentials to use for the handshake. The -credentials must be previously created with the @option{-object tls-creds} -argument. + ``tls-auth`` provides the ID of the QAuthZ authorization object + against which the client's x509 distinguished name will be + validated. This object is only resolved at time of use, so can be + deleted and recreated on the fly while the chardev server is active. + If missing, it will default to denying access. -@option{tls-auth} provides the ID of the QAuthZ authorization object against -which the client's x509 distinguished name will be validated. This object is -only resolved at time of use, so can be deleted and recreated on the fly -while the chardev server is active. If missing, it will default to denying -access. + TCP and unix socket options are given below: -TCP and unix socket options are given below: + ``TCP options: port=port[,host=host][,to=to][,ipv4][,ipv6][,nodelay]`` + ``host`` for a listening socket specifies the local address to + be bound. For a connecting socket species the remote host to + connect to. ``host`` is optional for listening sockets. If not + specified it defaults to ``0.0.0.0``. -@table @option + ``port`` for a listening socket specifies the local port to be + bound. For a connecting socket specifies the port on the remote + host to connect to. ``port`` can be given as either a port + number or a service name. ``port`` is required. -@item TCP options: port=@var{port}[,host=@var{host}][,to=@var{to}][,ipv4][,ipv6][,nodelay] + ``to`` is only relevant to listening sockets. If it is + specified, and ``port`` cannot be bound, QEMU will attempt to + bind to subsequent ports up to and including ``to`` until it + succeeds. ``to`` must be specified as a port number. -@option{host} for a listening socket specifies the local address to be bound. -For a connecting socket species the remote host to connect to. @option{host} is -optional for listening sockets. If not specified it defaults to @code{0.0.0.0}. + ``ipv4`` and ``ipv6`` specify that either IPv4 or IPv6 must be + used. If neither is specified the socket may use either + protocol. -@option{port} for a listening socket specifies the local port to be bound. For a -connecting socket specifies the port on the remote host to connect to. -@option{port} can be given as either a port number or a service name. -@option{port} is required. + ``nodelay`` disables the Nagle algorithm. -@option{to} is only relevant to listening sockets. If it is specified, and -@option{port} cannot be bound, QEMU will attempt to bind to subsequent ports up -to and including @option{to} until it succeeds. @option{to} must be specified -as a port number. + ``unix options: path=path`` + ``path`` specifies the local path of the unix socket. ``path`` + is required. -@option{ipv4} and @option{ipv6} specify that either IPv4 or IPv6 must be used. -If neither is specified the socket may use either protocol. +``-chardev udp,id=id[,host=host],port=port[,localaddr=localaddr][,localport=localport][,ipv4][,ipv6]`` + Sends all traffic from the guest to a remote host over UDP. -@option{nodelay} disables the Nagle algorithm. + ``host`` specifies the remote host to connect to. If not specified + it defaults to ``localhost``. -@item unix options: path=@var{path} + ``port`` specifies the port on the remote host to connect to. + ``port`` is required. -@option{path} specifies the local path of the unix socket. @option{path} is -required. + ``localaddr`` specifies the local address to bind to. If not + specified it defaults to ``0.0.0.0``. -@end table + ``localport`` specifies the local port to bind to. If not specified + any available local port will be used. -@item -chardev udp,id=@var{id}[,host=@var{host}],port=@var{port}[,localaddr=@var{localaddr}][,localport=@var{localport}][,ipv4][,ipv6] + ``ipv4`` and ``ipv6`` specify that either IPv4 or IPv6 must be used. + If neither is specified the device may use either protocol. -Sends all traffic from the guest to a remote host over UDP. +``-chardev msmouse,id=id`` + Forward QEMU's emulated msmouse events to the guest. ``msmouse`` + does not take any options. -@option{host} specifies the remote host to connect to. If not specified it -defaults to @code{localhost}. +``-chardev vc,id=id[[,width=width][,height=height]][[,cols=cols][,rows=rows]]`` + Connect to a QEMU text console. ``vc`` may optionally be given a + specific size. -@option{port} specifies the port on the remote host to connect to. @option{port} -is required. + ``width`` and ``height`` specify the width and height respectively + of the console, in pixels. -@option{localaddr} specifies the local address to bind to. If not specified it -defaults to @code{0.0.0.0}. + ``cols`` and ``rows`` specify that the console be sized to fit a + text console with the given dimensions. -@option{localport} specifies the local port to bind to. If not specified any -available local port will be used. +``-chardev ringbuf,id=id[,size=size]`` + Create a ring buffer with fixed size ``size``. size must be a power + of two and defaults to ``64K``. -@option{ipv4} and @option{ipv6} specify that either IPv4 or IPv6 must be used. -If neither is specified the device may use either protocol. +``-chardev file,id=id,path=path`` + Log all traffic received from the guest to a file. -@item -chardev msmouse,id=@var{id} + ``path`` specifies the path of the file to be opened. This file will + be created if it does not already exist, and overwritten if it does. + ``path`` is required. -Forward QEMU's emulated msmouse events to the guest. @option{msmouse} does not -take any options. +``-chardev pipe,id=id,path=path`` + Create a two-way connection to the guest. The behaviour differs + slightly between Windows hosts and other hosts: -@item -chardev vc,id=@var{id}[[,width=@var{width}][,height=@var{height}]][[,cols=@var{cols}][,rows=@var{rows}]] + On Windows, a single duplex pipe will be created at + ``\\.pipe\path``. -Connect to a QEMU text console. @option{vc} may optionally be given a specific -size. + On other hosts, 2 pipes will be created called ``path.in`` and + ``path.out``. Data written to ``path.in`` will be received by the + guest. Data written by the guest can be read from ``path.out``. QEMU + will not create these fifos, and requires them to be present. -@option{width} and @option{height} specify the width and height respectively of -the console, in pixels. + ``path`` forms part of the pipe path as described above. ``path`` is + required. -@option{cols} and @option{rows} specify that the console be sized to fit a text -console with the given dimensions. +``-chardev console,id=id`` + Send traffic from the guest to QEMU's standard output. ``console`` + does not take any options. -@item -chardev ringbuf,id=@var{id}[,size=@var{size}] + ``console`` is only available on Windows hosts. -Create a ring buffer with fixed size @option{size}. -@var{size} must be a power of two and defaults to @code{64K}. +``-chardev serial,id=id,path=path`` + Send traffic from the guest to a serial device on the host. -@item -chardev file,id=@var{id},path=@var{path} + On Unix hosts serial will actually accept any tty device, not only + serial lines. -Log all traffic received from the guest to a file. + ``path`` specifies the name of the serial device to open. -@option{path} specifies the path of the file to be opened. This file will be -created if it does not already exist, and overwritten if it does. @option{path} -is required. +``-chardev pty,id=id`` + Create a new pseudo-terminal on the host and connect to it. ``pty`` + does not take any options. -@item -chardev pipe,id=@var{id},path=@var{path} + ``pty`` is not available on Windows hosts. -Create a two-way connection to the guest. The behaviour differs slightly between -Windows hosts and other hosts: +``-chardev stdio,id=id[,signal=on|off]`` + Connect to standard input and standard output of the QEMU process. -On Windows, a single duplex pipe will be created at -@file{\\.pipe\@option{path}}. + ``signal`` controls if signals are enabled on the terminal, that + includes exiting QEMU with the key sequence Control-c. This option + is enabled by default, use ``signal=off`` to disable it. -On other hosts, 2 pipes will be created called @file{@option{path}.in} and -@file{@option{path}.out}. Data written to @file{@option{path}.in} will be -received by the guest. Data written by the guest can be read from -@file{@option{path}.out}. QEMU will not create these fifos, and requires them to -be present. +``-chardev braille,id=id`` + Connect to a local BrlAPI server. ``braille`` does not take any + options. -@option{path} forms part of the pipe path as described above. @option{path} is -required. +``-chardev tty,id=id,path=path`` + ``tty`` is only available on Linux, Sun, FreeBSD, NetBSD, OpenBSD + and DragonFlyBSD hosts. It is an alias for ``serial``. -@item -chardev console,id=@var{id} + ``path`` specifies the path to the tty. ``path`` is required. -Send traffic from the guest to QEMU's standard output. @option{console} does not -take any options. +``-chardev parallel,id=id,path=path`` + \ +``-chardev parport,id=id,path=path`` + ``parallel`` is only available on Linux, FreeBSD and DragonFlyBSD + hosts. -@option{console} is only available on Windows hosts. + Connect to a local parallel port. -@item -chardev serial,id=@var{id},path=@option{path} + ``path`` specifies the path to the parallel port device. ``path`` is + required. -Send traffic from the guest to a serial device on the host. +``-chardev spicevmc,id=id,debug=debug,name=name`` + ``spicevmc`` is only available when spice support is built in. -On Unix hosts serial will actually accept any tty device, -not only serial lines. + ``debug`` debug level for spicevmc -@option{path} specifies the name of the serial device to open. + ``name`` name of spice channel to connect to -@item -chardev pty,id=@var{id} + Connect to a spice virtual machine channel, such as vdiport. -Create a new pseudo-terminal on the host and connect to it. @option{pty} does -not take any options. +``-chardev spiceport,id=id,debug=debug,name=name`` + ``spiceport`` is only available when spice support is built in. -@option{pty} is not available on Windows hosts. + ``debug`` debug level for spicevmc -@item -chardev stdio,id=@var{id}[,signal=on|off] -Connect to standard input and standard output of the QEMU process. + ``name`` name of spice port to connect to -@option{signal} controls if signals are enabled on the terminal, that includes -exiting QEMU with the key sequence @key{Control-c}. This option is enabled by -default, use @option{signal=off} to disable it. + Connect to a spice port, allowing a Spice client to handle the + traffic identified by a name (preferably a fqdn). +ERST -@item -chardev braille,id=@var{id} - -Connect to a local BrlAPI server. @option{braille} does not take any options. - -@item -chardev tty,id=@var{id},path=@var{path} - -@option{tty} is only available on Linux, Sun, FreeBSD, NetBSD, OpenBSD and -DragonFlyBSD hosts. It is an alias for @option{serial}. - -@option{path} specifies the path to the tty. @option{path} is required. - -@item -chardev parallel,id=@var{id},path=@var{path} -@itemx -chardev parport,id=@var{id},path=@var{path} - -@option{parallel} is only available on Linux, FreeBSD and DragonFlyBSD hosts. - -Connect to a local parallel port. - -@option{path} specifies the path to the parallel port device. @option{path} is -required. - -@item -chardev spicevmc,id=@var{id},debug=@var{debug},name=@var{name} - -@option{spicevmc} is only available when spice support is built in. - -@option{debug} debug level for spicevmc - -@option{name} name of spice channel to connect to - -Connect to a spice virtual machine channel, such as vdiport. - -@item -chardev spiceport,id=@var{id},debug=@var{debug},name=@var{name} - -@option{spiceport} is only available when spice support is built in. - -@option{debug} debug level for spicevmc - -@option{name} name of spice port to connect to - -Connect to a spice port, allowing a Spice client to handle the traffic -identified by a name (preferably a fqdn). -ETEXI - -STEXI -@end table -ETEXI -DEFHEADING() - -DEFHEADING(Bluetooth(R) options:) -STEXI -@table @option -ETEXI - -DEF("bt", HAS_ARG, QEMU_OPTION_bt, \ - "-bt hci,null dumb bluetooth HCI - doesn't respond to commands\n" \ - "-bt hci,host[:id]\n" \ - " use host's HCI with the given name\n" \ - "-bt hci[,vlan=n]\n" \ - " emulate a standard HCI in virtual scatternet 'n'\n" \ - "-bt vhci[,vlan=n]\n" \ - " add host computer to virtual scatternet 'n' using VHCI\n" \ - "-bt device:dev[,vlan=n]\n" \ - " emulate a bluetooth device 'dev' in scatternet 'n'\n", - QEMU_ARCH_ALL) -STEXI -@item -bt hci[...] -@findex -bt -Defines the function of the corresponding Bluetooth HCI. -bt options -are matched with the HCIs present in the chosen machine type. For -example when emulating a machine with only one HCI built into it, only -the first @code{-bt hci[...]} option is valid and defines the HCI's -logic. The Transport Layer is decided by the machine type. Currently -the machines @code{n800} and @code{n810} have one HCI and all other -machines have none. - -Note: This option and the whole bluetooth subsystem is considered as deprecated. -If you still use it, please send a mail to @email{qemu-devel@@nongnu.org} where -you describe your usecase. - -@anchor{bt-hcis} -The following three types are recognized: - -@table @option -@item -bt hci,null -(default) The corresponding Bluetooth HCI assumes no internal logic -and will not respond to any HCI commands or emit events. - -@item -bt hci,host[:@var{id}] -(@code{bluez} only) The corresponding HCI passes commands / events -to / from the physical HCI identified by the name @var{id} (default: -@code{hci0}) on the computer running QEMU. Only available on @code{bluez} -capable systems like Linux. - -@item -bt hci[,vlan=@var{n}] -Add a virtual, standard HCI that will participate in the Bluetooth -scatternet @var{n} (default @code{0}). Similarly to @option{-net} -VLANs, devices inside a bluetooth network @var{n} can only communicate -with other devices in the same network (scatternet). -@end table - -@item -bt vhci[,vlan=@var{n}] -(Linux-host only) Create a HCI in scatternet @var{n} (default 0) attached -to the host bluetooth stack instead of to the emulated target. This -allows the host and target machines to participate in a common scatternet -and communicate. Requires the Linux @code{vhci} driver installed. Can -be used as following: - -@example -@value{qemu_system} [...OPTIONS...] -bt hci,vlan=5 -bt vhci,vlan=5 -@end example - -@item -bt device:@var{dev}[,vlan=@var{n}] -Emulate a bluetooth device @var{dev} and place it in network @var{n} -(default @code{0}). QEMU can only emulate one type of bluetooth devices -currently: - -@table @option -@item keyboard -Virtual wireless keyboard implementing the HIDP bluetooth profile. -@end table -ETEXI - -STEXI -@end table -ETEXI DEFHEADING() #ifdef CONFIG_TPM @@ -3215,146 +3240,122 @@ DEF("tpmdev", HAS_ARG, QEMU_OPTION_tpmdev, \ "-tpmdev emulator,id=id,chardev=dev\n" " configure the TPM device using chardev backend\n", QEMU_ARCH_ALL) -STEXI - +SRST The general form of a TPM device option is: -@table @option -@item -tpmdev @var{backend},id=@var{id}[,@var{options}] -@findex -tpmdev +``-tpmdev backend,id=id[,options]`` + The specific backend type will determine the applicable options. The + ``-tpmdev`` option creates the TPM backend and requires a + ``-device`` option that specifies the TPM frontend interface model. -The specific backend type will determine the applicable options. -The @code{-tpmdev} option creates the TPM backend and requires a -@code{-device} option that specifies the TPM frontend interface model. - -Use @code{-tpmdev help} to print all available TPM backend types. - -@end table + Use ``-tpmdev help`` to print all available TPM backend types. The available backends are: -@table @option +``-tpmdev passthrough,id=id,path=path,cancel-path=cancel-path`` + (Linux-host only) Enable access to the host's TPM using the + passthrough driver. -@item -tpmdev passthrough,id=@var{id},path=@var{path},cancel-path=@var{cancel-path} + ``path`` specifies the path to the host's TPM device, i.e., on a + Linux host this would be ``/dev/tpm0``. ``path`` is optional and by + default ``/dev/tpm0`` is used. -(Linux-host only) Enable access to the host's TPM using the passthrough -driver. + ``cancel-path`` specifies the path to the host TPM device's sysfs + entry allowing for cancellation of an ongoing TPM command. + ``cancel-path`` is optional and by default QEMU will search for the + sysfs entry to use. -@option{path} specifies the path to the host's TPM device, i.e., on -a Linux host this would be @code{/dev/tpm0}. -@option{path} is optional and by default @code{/dev/tpm0} is used. + Some notes about using the host's TPM with the passthrough driver: -@option{cancel-path} specifies the path to the host TPM device's sysfs -entry allowing for cancellation of an ongoing TPM command. -@option{cancel-path} is optional and by default QEMU will search for the -sysfs entry to use. + The TPM device accessed by the passthrough driver must not be used + by any other application on the host. -Some notes about using the host's TPM with the passthrough driver: + Since the host's firmware (BIOS/UEFI) has already initialized the + TPM, the VM's firmware (BIOS/UEFI) will not be able to initialize + the TPM again and may therefore not show a TPM-specific menu that + would otherwise allow the user to configure the TPM, e.g., allow the + user to enable/disable or activate/deactivate the TPM. Further, if + TPM ownership is released from within a VM then the host's TPM will + get disabled and deactivated. To enable and activate the TPM again + afterwards, the host has to be rebooted and the user is required to + enter the firmware's menu to enable and activate the TPM. If the TPM + is left disabled and/or deactivated most TPM commands will fail. -The TPM device accessed by the passthrough driver must not be -used by any other application on the host. + To create a passthrough TPM use the following two options: -Since the host's firmware (BIOS/UEFI) has already initialized the TPM, -the VM's firmware (BIOS/UEFI) will not be able to initialize the -TPM again and may therefore not show a TPM-specific menu that would -otherwise allow the user to configure the TPM, e.g., allow the user to -enable/disable or activate/deactivate the TPM. -Further, if TPM ownership is released from within a VM then the host's TPM -will get disabled and deactivated. To enable and activate the -TPM again afterwards, the host has to be rebooted and the user is -required to enter the firmware's menu to enable and activate the TPM. -If the TPM is left disabled and/or deactivated most TPM commands will fail. + :: -To create a passthrough TPM use the following two options: -@example --tpmdev passthrough,id=tpm0 -device tpm-tis,tpmdev=tpm0 -@end example -Note that the @code{-tpmdev} id is @code{tpm0} and is referenced by -@code{tpmdev=tpm0} in the device option. + -tpmdev passthrough,id=tpm0 -device tpm-tis,tpmdev=tpm0 -@item -tpmdev emulator,id=@var{id},chardev=@var{dev} + Note that the ``-tpmdev`` id is ``tpm0`` and is referenced by + ``tpmdev=tpm0`` in the device option. -(Linux-host only) Enable access to a TPM emulator using Unix domain socket based -chardev backend. +``-tpmdev emulator,id=id,chardev=dev`` + (Linux-host only) Enable access to a TPM emulator using Unix domain + socket based chardev backend. -@option{chardev} specifies the unique ID of a character device backend that provides connection to the software TPM server. + ``chardev`` specifies the unique ID of a character device backend + that provides connection to the software TPM server. -To create a TPM emulator backend device with chardev socket backend: -@example + To create a TPM emulator backend device with chardev socket backend: --chardev socket,id=chrtpm,path=/tmp/swtpm-sock -tpmdev emulator,id=tpm0,chardev=chrtpm -device tpm-tis,tpmdev=tpm0 + :: -@end example + -chardev socket,id=chrtpm,path=/tmp/swtpm-sock -tpmdev emulator,id=tpm0,chardev=chrtpm -device tpm-tis,tpmdev=tpm0 +ERST -ETEXI - -STEXI -@end table -ETEXI DEFHEADING() #endif DEFHEADING(Linux/Multiboot boot specific:) -STEXI +SRST +When using these options, you can use a given Linux or Multiboot kernel +without installing it in the disk image. It can be useful for easier +testing of various kernels. -When using these options, you can use a given Linux or Multiboot -kernel without installing it in the disk image. It can be useful -for easier testing of various kernels. -@table @option -ETEXI +ERST DEF("kernel", HAS_ARG, QEMU_OPTION_kernel, \ "-kernel bzImage use 'bzImage' as kernel image\n", QEMU_ARCH_ALL) -STEXI -@item -kernel @var{bzImage} -@findex -kernel -Use @var{bzImage} as kernel image. The kernel can be either a Linux kernel -or in multiboot format. -ETEXI +SRST +``-kernel bzImage`` + Use bzImage as kernel image. The kernel can be either a Linux kernel + or in multiboot format. +ERST DEF("append", HAS_ARG, QEMU_OPTION_append, \ "-append cmdline use 'cmdline' as kernel command line\n", QEMU_ARCH_ALL) -STEXI -@item -append @var{cmdline} -@findex -append -Use @var{cmdline} as kernel command line -ETEXI +SRST +``-append cmdline`` + Use cmdline as kernel command line +ERST DEF("initrd", HAS_ARG, QEMU_OPTION_initrd, \ "-initrd file use 'file' as initial ram disk\n", QEMU_ARCH_ALL) -STEXI -@item -initrd @var{file} -@findex -initrd -Use @var{file} as initial ram disk. +SRST +``-initrd file`` + Use file as initial ram disk. -@item -initrd "@var{file1} arg=foo,@var{file2}" +``-initrd "file1 arg=foo,file2"`` + This syntax is only available with multiboot. -This syntax is only available with multiboot. - -Use @var{file1} and @var{file2} as modules and pass arg=foo as parameter to the -first module. -ETEXI + Use file1 and file2 as modules and pass arg=foo as parameter to the + first module. +ERST DEF("dtb", HAS_ARG, QEMU_OPTION_dtb, \ "-dtb file use 'file' as device tree image\n", QEMU_ARCH_ALL) -STEXI -@item -dtb @var{file} -@findex -dtb -Use @var{file} as a device tree binary (dtb) image and pass it to the kernel -on boot. -ETEXI +SRST +``-dtb file`` + Use file as a device tree binary (dtb) image and pass it to the + kernel on boot. +ERST -STEXI -@end table -ETEXI DEFHEADING() DEFHEADING(Debug/Expert options:) -STEXI -@table @option -ETEXI DEF("fw_cfg", HAS_ARG, QEMU_OPTION_fwcfg, "-fw_cfg [name=],file=\n" @@ -3362,288 +3363,295 @@ DEF("fw_cfg", HAS_ARG, QEMU_OPTION_fwcfg, "-fw_cfg [name=],string=\n" " add named fw_cfg entry with contents from string\n", QEMU_ARCH_ALL) -STEXI +SRST +``-fw_cfg [name=]name,file=file`` + Add named fw\_cfg entry with contents from file file. -@item -fw_cfg [name=]@var{name},file=@var{file} -@findex -fw_cfg -Add named fw_cfg entry with contents from file @var{file}. +``-fw_cfg [name=]name,string=str`` + Add named fw\_cfg entry with contents from string str. -@item -fw_cfg [name=]@var{name},string=@var{str} -Add named fw_cfg entry with contents from string @var{str}. + The terminating NUL character of the contents of str will not be + included as part of the fw\_cfg item data. To insert contents with + embedded NUL characters, you have to use the file parameter. -The terminating NUL character of the contents of @var{str} will not be -included as part of the fw_cfg item data. To insert contents with -embedded NUL characters, you have to use the @var{file} parameter. + The fw\_cfg entries are passed by QEMU through to the guest. -The fw_cfg entries are passed by QEMU through to the guest. + Example: -Example: -@example - -fw_cfg name=opt/com.mycompany/blob,file=./my_blob.bin -@end example -creates an fw_cfg entry named opt/com.mycompany/blob with contents -from ./my_blob.bin. + :: -ETEXI + -fw_cfg name=opt/com.mycompany/blob,file=./my_blob.bin + + creates an fw\_cfg entry named opt/com.mycompany/blob with contents + from ./my\_blob.bin. +ERST DEF("serial", HAS_ARG, QEMU_OPTION_serial, \ "-serial dev redirect the serial port to char device 'dev'\n", QEMU_ARCH_ALL) -STEXI -@item -serial @var{dev} -@findex -serial -Redirect the virtual serial port to host character device -@var{dev}. The default device is @code{vc} in graphical mode and -@code{stdio} in non graphical mode. +SRST +``-serial dev`` + Redirect the virtual serial port to host character device dev. The + default device is ``vc`` in graphical mode and ``stdio`` in non + graphical mode. -This option can be used several times to simulate up to 4 serial -ports. + This option can be used several times to simulate up to 4 serial + ports. -Use @code{-serial none} to disable all serial ports. + Use ``-serial none`` to disable all serial ports. -Available character devices are: -@table @option -@item vc[:@var{W}x@var{H}] -Virtual console. Optionally, a width and height can be given in pixel with -@example -vc:800x600 -@end example -It is also possible to specify width or height in characters: -@example -vc:80Cx24C -@end example -@item pty -[Linux only] Pseudo TTY (a new PTY is automatically allocated) -@item none -No device is allocated. -@item null -void device -@item chardev:@var{id} -Use a named character device defined with the @code{-chardev} option. -@item /dev/XXX -[Linux only] Use host tty, e.g. @file{/dev/ttyS0}. The host serial port -parameters are set according to the emulated ones. -@item /dev/parport@var{N} -[Linux only, parallel port only] Use host parallel port -@var{N}. Currently SPP and EPP parallel port features can be used. -@item file:@var{filename} -Write output to @var{filename}. No character can be read. -@item stdio -[Unix only] standard input/output -@item pipe:@var{filename} -name pipe @var{filename} -@item COM@var{n} -[Windows only] Use host serial port @var{n} -@item udp:[@var{remote_host}]:@var{remote_port}[@@[@var{src_ip}]:@var{src_port}] -This implements UDP Net Console. -When @var{remote_host} or @var{src_ip} are not specified -they default to @code{0.0.0.0}. -When not using a specified @var{src_port} a random port is automatically chosen. + Available character devices are: -If you just want a simple readonly console you can use @code{netcat} or -@code{nc}, by starting QEMU with: @code{-serial udp::4555} and nc as: -@code{nc -u -l -p 4555}. Any time QEMU writes something to that port it -will appear in the netconsole session. + ``vc[:WxH]`` + Virtual console. Optionally, a width and height can be given in + pixel with -If you plan to send characters back via netconsole or you want to stop -and start QEMU a lot of times, you should have QEMU use the same -source port each time by using something like @code{-serial -udp::4555@@:4556} to QEMU. Another approach is to use a patched -version of netcat which can listen to a TCP port and send and receive -characters via udp. If you have a patched version of netcat which -activates telnet remote echo and single char transfer, then you can -use the following options to set up a netcat redirector to allow -telnet on port 5555 to access the QEMU port. -@table @code -@item QEMU Options: --serial udp::4555@@:4556 -@item netcat options: --u -P 4555 -L 0.0.0.0:4556 -t -p 5555 -I -T -@item telnet options: -localhost 5555 -@end table + :: -@item tcp:[@var{host}]:@var{port}[,@var{server}][,nowait][,nodelay][,reconnect=@var{seconds}] -The TCP Net Console has two modes of operation. It can send the serial -I/O to a location or wait for a connection from a location. By default -the TCP Net Console is sent to @var{host} at the @var{port}. If you use -the @var{server} option QEMU will wait for a client socket application -to connect to the port before continuing, unless the @code{nowait} -option was specified. The @code{nodelay} option disables the Nagle buffering -algorithm. The @code{reconnect} option only applies if @var{noserver} is -set, if the connection goes down it will attempt to reconnect at the -given interval. If @var{host} is omitted, 0.0.0.0 is assumed. Only -one TCP connection at a time is accepted. You can use @code{telnet} to -connect to the corresponding character device. -@table @code -@item Example to send tcp console to 192.168.0.2 port 4444 --serial tcp:192.168.0.2:4444 -@item Example to listen and wait on port 4444 for connection --serial tcp::4444,server -@item Example to not wait and listen on ip 192.168.0.100 port 4444 --serial tcp:192.168.0.100:4444,server,nowait -@end table + vc:800x600 -@item telnet:@var{host}:@var{port}[,server][,nowait][,nodelay] -The telnet protocol is used instead of raw tcp sockets. The options -work the same as if you had specified @code{-serial tcp}. The -difference is that the port acts like a telnet server or client using -telnet option negotiation. This will also allow you to send the -MAGIC_SYSRQ sequence if you use a telnet that supports sending the break -sequence. Typically in unix telnet you do it with Control-] and then -type "send break" followed by pressing the enter key. + It is also possible to specify width or height in characters: -@item websocket:@var{host}:@var{port},server[,nowait][,nodelay] -The WebSocket protocol is used instead of raw tcp socket. The port acts as -a WebSocket server. Client mode is not supported. + :: -@item unix:@var{path}[,server][,nowait][,reconnect=@var{seconds}] -A unix domain socket is used instead of a tcp socket. The option works the -same as if you had specified @code{-serial tcp} except the unix domain socket -@var{path} is used for connections. + vc:80Cx24C -@item mon:@var{dev_string} -This is a special option to allow the monitor to be multiplexed onto -another serial port. The monitor is accessed with key sequence of -@key{Control-a} and then pressing @key{c}. -@var{dev_string} should be any one of the serial devices specified -above. An example to multiplex the monitor onto a telnet server -listening on port 4444 would be: -@table @code -@item -serial mon:telnet::4444,server,nowait -@end table -When the monitor is multiplexed to stdio in this way, Ctrl+C will not terminate -QEMU any more but will be passed to the guest instead. + ``pty`` + [Linux only] Pseudo TTY (a new PTY is automatically allocated) -@item braille -Braille device. This will use BrlAPI to display the braille output on a real -or fake device. + ``none`` + No device is allocated. -@item msmouse -Three button serial mouse. Configure the guest to use Microsoft protocol. -@end table -ETEXI + ``null`` + void device + + ``chardev:id`` + Use a named character device defined with the ``-chardev`` + option. + + ``/dev/XXX`` + [Linux only] Use host tty, e.g. ``/dev/ttyS0``. The host serial + port parameters are set according to the emulated ones. + + ``/dev/parportN`` + [Linux only, parallel port only] Use host parallel port N. + Currently SPP and EPP parallel port features can be used. + + ``file:filename`` + Write output to filename. No character can be read. + + ``stdio`` + [Unix only] standard input/output + + ``pipe:filename`` + name pipe filename + + ``COMn`` + [Windows only] Use host serial port n + + ``udp:[remote_host]:remote_port[@[src_ip]:src_port]`` + This implements UDP Net Console. When remote\_host or src\_ip + are not specified they default to ``0.0.0.0``. When not using a + specified src\_port a random port is automatically chosen. + + If you just want a simple readonly console you can use + ``netcat`` or ``nc``, by starting QEMU with: + ``-serial udp::4555`` and nc as: ``nc -u -l -p 4555``. Any time + QEMU writes something to that port it will appear in the + netconsole session. + + If you plan to send characters back via netconsole or you want + to stop and start QEMU a lot of times, you should have QEMU use + the same source port each time by using something like ``-serial + udp::4555@:4556`` to QEMU. Another approach is to use a patched + version of netcat which can listen to a TCP port and send and + receive characters via udp. If you have a patched version of + netcat which activates telnet remote echo and single char + transfer, then you can use the following options to set up a + netcat redirector to allow telnet on port 5555 to access the + QEMU port. + + ``QEMU Options:`` + -serial udp::4555@:4556 + + ``netcat options:`` + -u -P 4555 -L 0.0.0.0:4556 -t -p 5555 -I -T + + ``telnet options:`` + localhost 5555 + + ``tcp:[host]:port[,server][,nowait][,nodelay][,reconnect=seconds]`` + The TCP Net Console has two modes of operation. It can send the + serial I/O to a location or wait for a connection from a + location. By default the TCP Net Console is sent to host at the + port. If you use the server option QEMU will wait for a client + socket application to connect to the port before continuing, + unless the ``nowait`` option was specified. The ``nodelay`` + option disables the Nagle buffering algorithm. The ``reconnect`` + option only applies if noserver is set, if the connection goes + down it will attempt to reconnect at the given interval. If host + is omitted, 0.0.0.0 is assumed. Only one TCP connection at a + time is accepted. You can use ``telnet`` to connect to the + corresponding character device. + + ``Example to send tcp console to 192.168.0.2 port 4444`` + -serial tcp:192.168.0.2:4444 + + ``Example to listen and wait on port 4444 for connection`` + -serial tcp::4444,server + + ``Example to not wait and listen on ip 192.168.0.100 port 4444`` + -serial tcp:192.168.0.100:4444,server,nowait + + ``telnet:host:port[,server][,nowait][,nodelay]`` + The telnet protocol is used instead of raw tcp sockets. The + options work the same as if you had specified ``-serial tcp``. + The difference is that the port acts like a telnet server or + client using telnet option negotiation. This will also allow you + to send the MAGIC\_SYSRQ sequence if you use a telnet that + supports sending the break sequence. Typically in unix telnet + you do it with Control-] and then type "send break" followed by + pressing the enter key. + + ``websocket:host:port,server[,nowait][,nodelay]`` + The WebSocket protocol is used instead of raw tcp socket. The + port acts as a WebSocket server. Client mode is not supported. + + ``unix:path[,server][,nowait][,reconnect=seconds]`` + A unix domain socket is used instead of a tcp socket. The option + works the same as if you had specified ``-serial tcp`` except + the unix domain socket path is used for connections. + + ``mon:dev_string`` + This is a special option to allow the monitor to be multiplexed + onto another serial port. The monitor is accessed with key + sequence of Control-a and then pressing c. dev\_string should be + any one of the serial devices specified above. An example to + multiplex the monitor onto a telnet server listening on port + 4444 would be: + + ``-serial mon:telnet::4444,server,nowait`` + + When the monitor is multiplexed to stdio in this way, Ctrl+C + will not terminate QEMU any more but will be passed to the guest + instead. + + ``braille`` + Braille device. This will use BrlAPI to display the braille + output on a real or fake device. + + ``msmouse`` + Three button serial mouse. Configure the guest to use Microsoft + protocol. +ERST DEF("parallel", HAS_ARG, QEMU_OPTION_parallel, \ "-parallel dev redirect the parallel port to char device 'dev'\n", QEMU_ARCH_ALL) -STEXI -@item -parallel @var{dev} -@findex -parallel -Redirect the virtual parallel port to host device @var{dev} (same -devices as the serial port). On Linux hosts, @file{/dev/parportN} can -be used to use hardware devices connected on the corresponding host -parallel port. +SRST +``-parallel dev`` + Redirect the virtual parallel port to host device dev (same devices + as the serial port). On Linux hosts, ``/dev/parportN`` can be used + to use hardware devices connected on the corresponding host parallel + port. -This option can be used several times to simulate up to 3 parallel -ports. + This option can be used several times to simulate up to 3 parallel + ports. -Use @code{-parallel none} to disable all parallel ports. -ETEXI + Use ``-parallel none`` to disable all parallel ports. +ERST DEF("monitor", HAS_ARG, QEMU_OPTION_monitor, \ "-monitor dev redirect the monitor to char device 'dev'\n", QEMU_ARCH_ALL) -STEXI -@item -monitor @var{dev} -@findex -monitor -Redirect the monitor to host device @var{dev} (same devices as the -serial port). -The default device is @code{vc} in graphical mode and @code{stdio} in -non graphical mode. -Use @code{-monitor none} to disable the default monitor. -ETEXI +SRST +``-monitor dev`` + Redirect the monitor to host device dev (same devices as the serial + port). The default device is ``vc`` in graphical mode and ``stdio`` + in non graphical mode. Use ``-monitor none`` to disable the default + monitor. +ERST DEF("qmp", HAS_ARG, QEMU_OPTION_qmp, \ "-qmp dev like -monitor but opens in 'control' mode\n", QEMU_ARCH_ALL) -STEXI -@item -qmp @var{dev} -@findex -qmp -Like -monitor but opens in 'control' mode. -ETEXI +SRST +``-qmp dev`` + Like -monitor but opens in 'control' mode. +ERST DEF("qmp-pretty", HAS_ARG, QEMU_OPTION_qmp_pretty, \ "-qmp-pretty dev like -qmp but uses pretty JSON formatting\n", QEMU_ARCH_ALL) -STEXI -@item -qmp-pretty @var{dev} -@findex -qmp-pretty -Like -qmp but uses pretty JSON formatting. -ETEXI +SRST +``-qmp-pretty dev`` + Like -qmp but uses pretty JSON formatting. +ERST DEF("mon", HAS_ARG, QEMU_OPTION_mon, \ "-mon [chardev=]name[,mode=readline|control][,pretty[=on|off]]\n", QEMU_ARCH_ALL) -STEXI -@item -mon [chardev=]name[,mode=readline|control][,pretty[=on|off]] -@findex -mon -Setup monitor on chardev @var{name}. @code{pretty} turns on JSON pretty printing -easing human reading and debugging. -ETEXI +SRST +``-mon [chardev=]name[,mode=readline|control][,pretty[=on|off]]`` + Setup monitor on chardev name. ``pretty`` turns on JSON pretty + printing easing human reading and debugging. +ERST DEF("debugcon", HAS_ARG, QEMU_OPTION_debugcon, \ "-debugcon dev redirect the debug console to char device 'dev'\n", QEMU_ARCH_ALL) -STEXI -@item -debugcon @var{dev} -@findex -debugcon -Redirect the debug console to host device @var{dev} (same devices as the -serial port). The debug console is an I/O port which is typically port -0xe9; writing to that I/O port sends output to this device. -The default device is @code{vc} in graphical mode and @code{stdio} in -non graphical mode. -ETEXI +SRST +``-debugcon dev`` + Redirect the debug console to host device dev (same devices as the + serial port). The debug console is an I/O port which is typically + port 0xe9; writing to that I/O port sends output to this device. The + default device is ``vc`` in graphical mode and ``stdio`` in non + graphical mode. +ERST DEF("pidfile", HAS_ARG, QEMU_OPTION_pidfile, \ "-pidfile file write PID to 'file'\n", QEMU_ARCH_ALL) -STEXI -@item -pidfile @var{file} -@findex -pidfile -Store the QEMU process PID in @var{file}. It is useful if you launch QEMU -from a script. -ETEXI +SRST +``-pidfile file`` + Store the QEMU process PID in file. It is useful if you launch QEMU + from a script. +ERST DEF("singlestep", 0, QEMU_OPTION_singlestep, \ "-singlestep always run in singlestep mode\n", QEMU_ARCH_ALL) -STEXI -@item -singlestep -@findex -singlestep -Run the emulation in single step mode. -ETEXI +SRST +``-singlestep`` + Run the emulation in single step mode. +ERST DEF("preconfig", 0, QEMU_OPTION_preconfig, \ "--preconfig pause QEMU before machine is initialized (experimental)\n", QEMU_ARCH_ALL) -STEXI -@item --preconfig -@findex --preconfig -Pause QEMU for interactive configuration before the machine is created, -which allows querying and configuring properties that will affect -machine initialization. Use QMP command 'x-exit-preconfig' to exit -the preconfig state and move to the next state (i.e. run guest if -S -isn't used or pause the second time if -S is used). This option is -experimental. -ETEXI +SRST +``--preconfig`` + Pause QEMU for interactive configuration before the machine is + created, which allows querying and configuring properties that will + affect machine initialization. Use QMP command 'x-exit-preconfig' to + exit the preconfig state and move to the next state (i.e. run guest + if -S isn't used or pause the second time if -S is used). This + option is experimental. +ERST DEF("S", 0, QEMU_OPTION_S, \ "-S freeze CPU at startup (use 'c' to start execution)\n", QEMU_ARCH_ALL) -STEXI -@item -S -@findex -S -Do not start CPU at startup (you must type 'c' in the monitor). -ETEXI +SRST +``-S`` + Do not start CPU at startup (you must type 'c' in the monitor). +ERST DEF("realtime", HAS_ARG, QEMU_OPTION_realtime, "-realtime [mlock=on|off]\n" " run qemu with realtime features\n" " mlock=on|off controls mlock support (default: on)\n", QEMU_ARCH_ALL) -STEXI -@item -realtime mlock=on|off -@findex -realtime -Run qemu with realtime features. -mlocking qemu and guest memory can be enabled via @option{mlock=on} -(enabled by default). -ETEXI +SRST +``-realtime mlock=on|off`` + Run qemu with realtime features. mlocking qemu and guest memory can + be enabled via ``mlock=on`` (enabled by default). +ERST DEF("overcommit", HAS_ARG, QEMU_OPTION_overcommit, "-overcommit [mem-lock=on|off][cpu-pm=on|off]\n" @@ -3651,121 +3659,132 @@ DEF("overcommit", HAS_ARG, QEMU_OPTION_overcommit, " mem-lock=on|off controls memory lock support (default: off)\n" " cpu-pm=on|off controls cpu power management (default: off)\n", QEMU_ARCH_ALL) -STEXI -@item -overcommit mem-lock=on|off -@item -overcommit cpu-pm=on|off -@findex -overcommit -Run qemu with hints about host resource overcommit. The default is -to assume that host overcommits all resources. +SRST +``-overcommit mem-lock=on|off`` + \ +``-overcommit cpu-pm=on|off`` + Run qemu with hints about host resource overcommit. The default is + to assume that host overcommits all resources. -Locking qemu and guest memory can be enabled via @option{mem-lock=on} (disabled -by default). This works when host memory is not overcommitted and reduces the -worst-case latency for guest. This is equivalent to @option{realtime}. + Locking qemu and guest memory can be enabled via ``mem-lock=on`` + (disabled by default). This works when host memory is not + overcommitted and reduces the worst-case latency for guest. This is + equivalent to ``realtime``. -Guest ability to manage power state of host cpus (increasing latency for other -processes on the same host cpu, but decreasing latency for guest) can be -enabled via @option{cpu-pm=on} (disabled by default). This works best when -host CPU is not overcommitted. When used, host estimates of CPU cycle and power -utilization will be incorrect, not taking into account guest idle time. -ETEXI + Guest ability to manage power state of host cpus (increasing latency + for other processes on the same host cpu, but decreasing latency for + guest) can be enabled via ``cpu-pm=on`` (disabled by default). This + works best when host CPU is not overcommitted. When used, host + estimates of CPU cycle and power utilization will be incorrect, not + taking into account guest idle time. +ERST DEF("gdb", HAS_ARG, QEMU_OPTION_gdb, \ - "-gdb dev wait for gdb connection on 'dev'\n", QEMU_ARCH_ALL) -STEXI -@item -gdb @var{dev} -@findex -gdb -Wait for gdb connection on device @var{dev} (@pxref{gdb_usage}). Typical -connections will likely be TCP-based, but also UDP, pseudo TTY, or even -stdio are reasonable use case. The latter is allowing to start QEMU from -within gdb and establish the connection via a pipe: -@example -(gdb) target remote | exec @value{qemu_system} -gdb stdio ... -@end example -ETEXI + "-gdb dev accept gdb connection on 'dev'. (QEMU defaults to starting\n" + " the guest without waiting for gdb to connect; use -S too\n" + " if you want it to not start execution.)\n", + QEMU_ARCH_ALL) +SRST +``-gdb dev`` + Accept a gdb connection on device dev (see + :ref:`gdb_005fusage`). Note that this option does not pause QEMU + execution -- if you want QEMU to not start the guest until you + connect with gdb and issue a ``continue`` command, you will need to + also pass the ``-S`` option to QEMU. + + The most usual configuration is to listen on a local TCP socket:: + + -gdb tcp::3117 + + but you can specify other backends; UDP, pseudo TTY, or even stdio + are all reasonable use cases. For example, a stdio connection + allows you to start QEMU from within gdb and establish the + connection via a pipe: + + .. parsed-literal:: + + (gdb) target remote | exec |qemu_system| -gdb stdio ... +ERST DEF("s", 0, QEMU_OPTION_s, \ "-s shorthand for -gdb tcp::" DEFAULT_GDBSTUB_PORT "\n", QEMU_ARCH_ALL) -STEXI -@item -s -@findex -s -Shorthand for -gdb tcp::1234, i.e. open a gdbserver on TCP port 1234 -(@pxref{gdb_usage}). -ETEXI +SRST +``-s`` + Shorthand for -gdb tcp::1234, i.e. open a gdbserver on TCP port 1234 + (see :ref:`gdb_005fusage`). +ERST DEF("d", HAS_ARG, QEMU_OPTION_d, \ "-d item1,... enable logging of specified items (use '-d help' for a list of log items)\n", QEMU_ARCH_ALL) -STEXI -@item -d @var{item1}[,...] -@findex -d -Enable logging of specified items. Use '-d help' for a list of log items. -ETEXI +SRST +``-d item1[,...]`` + Enable logging of specified items. Use '-d help' for a list of log + items. +ERST DEF("D", HAS_ARG, QEMU_OPTION_D, \ "-D logfile output log to logfile (default stderr)\n", QEMU_ARCH_ALL) -STEXI -@item -D @var{logfile} -@findex -D -Output log in @var{logfile} instead of to stderr -ETEXI +SRST +``-D logfile`` + Output log in logfile instead of to stderr +ERST DEF("dfilter", HAS_ARG, QEMU_OPTION_DFILTER, \ "-dfilter range,.. filter debug output to range of addresses (useful for -d cpu,exec,etc..)\n", QEMU_ARCH_ALL) -STEXI -@item -dfilter @var{range1}[,...] -@findex -dfilter -Filter debug output to that relevant to a range of target addresses. The filter -spec can be either @var{start}+@var{size}, @var{start}-@var{size} or -@var{start}..@var{end} where @var{start} @var{end} and @var{size} are the -addresses and sizes required. For example: -@example - -dfilter 0x8000..0x8fff,0xffffffc000080000+0x200,0xffffffc000060000-0x1000 -@end example -Will dump output for any code in the 0x1000 sized block starting at 0x8000 and -the 0x200 sized block starting at 0xffffffc000080000 and another 0x1000 sized -block starting at 0xffffffc00005f000. -ETEXI +SRST +``-dfilter range1[,...]`` + Filter debug output to that relevant to a range of target addresses. + The filter spec can be either start+size, start-size or start..end + where start end and size are the addresses and sizes required. For + example: + + :: + + -dfilter 0x8000..0x8fff,0xffffffc000080000+0x200,0xffffffc000060000-0x1000 + + Will dump output for any code in the 0x1000 sized block starting at + 0x8000 and the 0x200 sized block starting at 0xffffffc000080000 and + another 0x1000 sized block starting at 0xffffffc00005f000. +ERST DEF("seed", HAS_ARG, QEMU_OPTION_seed, \ "-seed number seed the pseudo-random number generator\n", QEMU_ARCH_ALL) -STEXI -@item -seed @var{number} -@findex -seed -Force the guest to use a deterministic pseudo-random number generator, seeded -with @var{number}. This does not affect crypto routines within the host. -ETEXI +SRST +``-seed number`` + Force the guest to use a deterministic pseudo-random number + generator, seeded with number. This does not affect crypto routines + within the host. +ERST DEF("L", HAS_ARG, QEMU_OPTION_L, \ "-L path set the directory for the BIOS, VGA BIOS and keymaps\n", QEMU_ARCH_ALL) -STEXI -@item -L @var{path} -@findex -L -Set the directory for the BIOS, VGA BIOS and keymaps. +SRST +``-L path`` + Set the directory for the BIOS, VGA BIOS and keymaps. -To list all the data directories, use @code{-L help}. -ETEXI + To list all the data directories, use ``-L help``. +ERST DEF("bios", HAS_ARG, QEMU_OPTION_bios, \ "-bios file set the filename for the BIOS\n", QEMU_ARCH_ALL) -STEXI -@item -bios @var{file} -@findex -bios -Set the filename for the BIOS. -ETEXI +SRST +``-bios file`` + Set the filename for the BIOS. +ERST DEF("enable-kvm", 0, QEMU_OPTION_enable_kvm, \ "-enable-kvm enable KVM full virtualization support\n", QEMU_ARCH_ALL) -STEXI -@item -enable-kvm -@findex -enable-kvm -Enable KVM full virtualization support. This option is only available -if KVM support is enabled when compiling. -ETEXI +SRST +``-enable-kvm`` + Enable KVM full virtualization support. This option is only + available if KVM support is enabled when compiling. +ERST DEF("xen-domid", HAS_ARG, QEMU_OPTION_xen_domid, "-xen-domid id specify xen guest domain id\n", QEMU_ARCH_ALL) @@ -3778,242 +3797,225 @@ DEF("xen-domid-restrict", 0, QEMU_OPTION_xen_domid_restrict, " to specified domain id. (Does not affect\n" " xenpv machine type).\n", QEMU_ARCH_ALL) -STEXI -@item -xen-domid @var{id} -@findex -xen-domid -Specify xen guest domain @var{id} (XEN only). -@item -xen-attach -@findex -xen-attach -Attach to existing xen domain. -libxl will use this when starting QEMU (XEN only). -@findex -xen-domid-restrict -Restrict set of available xen operations to specified domain id (XEN only). -ETEXI +SRST +``-xen-domid id`` + Specify xen guest domain id (XEN only). + +``-xen-attach`` + Attach to existing xen domain. libxl will use this when starting + QEMU (XEN only). Restrict set of available xen operations to + specified domain id (XEN only). +ERST DEF("no-reboot", 0, QEMU_OPTION_no_reboot, \ "-no-reboot exit instead of rebooting\n", QEMU_ARCH_ALL) -STEXI -@item -no-reboot -@findex -no-reboot -Exit instead of rebooting. -ETEXI +SRST +``-no-reboot`` + Exit instead of rebooting. +ERST DEF("no-shutdown", 0, QEMU_OPTION_no_shutdown, \ "-no-shutdown stop before shutdown\n", QEMU_ARCH_ALL) -STEXI -@item -no-shutdown -@findex -no-shutdown -Don't exit QEMU on guest shutdown, but instead only stop the emulation. -This allows for instance switching to monitor to commit changes to the -disk image. -ETEXI +SRST +``-no-shutdown`` + Don't exit QEMU on guest shutdown, but instead only stop the + emulation. This allows for instance switching to monitor to commit + changes to the disk image. +ERST DEF("loadvm", HAS_ARG, QEMU_OPTION_loadvm, \ "-loadvm [tag|id]\n" \ " start right away with a saved state (loadvm in monitor)\n", QEMU_ARCH_ALL) -STEXI -@item -loadvm @var{file} -@findex -loadvm -Start right away with a saved state (@code{loadvm} in monitor) -ETEXI +SRST +``-loadvm file`` + Start right away with a saved state (``loadvm`` in monitor) +ERST #ifndef _WIN32 DEF("daemonize", 0, QEMU_OPTION_daemonize, \ "-daemonize daemonize QEMU after initializing\n", QEMU_ARCH_ALL) #endif -STEXI -@item -daemonize -@findex -daemonize -Daemonize the QEMU process after initialization. QEMU will not detach from -standard IO until it is ready to receive connections on any of its devices. -This option is a useful way for external programs to launch QEMU without having -to cope with initialization race conditions. -ETEXI +SRST +``-daemonize`` + Daemonize the QEMU process after initialization. QEMU will not + detach from standard IO until it is ready to receive connections on + any of its devices. This option is a useful way for external + programs to launch QEMU without having to cope with initialization + race conditions. +ERST DEF("option-rom", HAS_ARG, QEMU_OPTION_option_rom, \ "-option-rom rom load a file, rom, into the option ROM space\n", QEMU_ARCH_ALL) -STEXI -@item -option-rom @var{file} -@findex -option-rom -Load the contents of @var{file} as an option ROM. -This option is useful to load things like EtherBoot. -ETEXI +SRST +``-option-rom file`` + Load the contents of file as an option ROM. This option is useful to + load things like EtherBoot. +ERST DEF("rtc", HAS_ARG, QEMU_OPTION_rtc, \ "-rtc [base=utc|localtime|][,clock=host|rt|vm][,driftfix=none|slew]\n" \ " set the RTC base and clock, enable drift fix for clock ticks (x86 only)\n", QEMU_ARCH_ALL) -STEXI +SRST +``-rtc [base=utc|localtime|datetime][,clock=host|rt|vm][,driftfix=none|slew]`` + Specify ``base`` as ``utc`` or ``localtime`` to let the RTC start at + the current UTC or local time, respectively. ``localtime`` is + required for correct date in MS-DOS or Windows. To start at a + specific point in time, provide datetime in the format + ``2006-06-17T16:01:21`` or ``2006-06-17``. The default base is UTC. -@item -rtc [base=utc|localtime|@var{datetime}][,clock=host|rt|vm][,driftfix=none|slew] -@findex -rtc -Specify @option{base} as @code{utc} or @code{localtime} to let the RTC start at the current -UTC or local time, respectively. @code{localtime} is required for correct date in -MS-DOS or Windows. To start at a specific point in time, provide @var{datetime} in the -format @code{2006-06-17T16:01:21} or @code{2006-06-17}. The default base is UTC. + By default the RTC is driven by the host system time. This allows + using of the RTC as accurate reference clock inside the guest, + specifically if the host time is smoothly following an accurate + external reference clock, e.g. via NTP. If you want to isolate the + guest time from the host, you can set ``clock`` to ``rt`` instead, + which provides a host monotonic clock if host support it. To even + prevent the RTC from progressing during suspension, you can set + ``clock`` to ``vm`` (virtual clock). '\ ``clock=vm``\ ' is + recommended especially in icount mode in order to preserve + determinism; however, note that in icount mode the speed of the + virtual clock is variable and can in general differ from the host + clock. -By default the RTC is driven by the host system time. This allows using of the -RTC as accurate reference clock inside the guest, specifically if the host -time is smoothly following an accurate external reference clock, e.g. via NTP. -If you want to isolate the guest time from the host, you can set @option{clock} -to @code{rt} instead, which provides a host monotonic clock if host support it. -To even prevent the RTC from progressing during suspension, you can set @option{clock} -to @code{vm} (virtual clock). @samp{clock=vm} is recommended especially in -icount mode in order to preserve determinism; however, note that in icount mode -the speed of the virtual clock is variable and can in general differ from the -host clock. - -Enable @option{driftfix} (i386 targets only) if you experience time drift problems, -specifically with Windows' ACPI HAL. This option will try to figure out how -many timer interrupts were not processed by the Windows guest and will -re-inject them. -ETEXI + Enable ``driftfix`` (i386 targets only) if you experience time drift + problems, specifically with Windows' ACPI HAL. This option will try + to figure out how many timer interrupts were not processed by the + Windows guest and will re-inject them. +ERST DEF("icount", HAS_ARG, QEMU_OPTION_icount, \ "-icount [shift=N|auto][,align=on|off][,sleep=on|off,rr=record|replay,rrfile=,rrsnapshot=]\n" \ " enable virtual instruction counter with 2^N clock ticks per\n" \ " instruction, enable aligning the host and virtual clocks\n" \ " or disable real time cpu sleeping\n", QEMU_ARCH_ALL) -STEXI -@item -icount [shift=@var{N}|auto][,rr=record|replay,rrfile=@var{filename},rrsnapshot=@var{snapshot}] -@findex -icount -Enable virtual instruction counter. The virtual cpu will execute one -instruction every 2^@var{N} ns of virtual time. If @code{auto} is specified -then the virtual cpu speed will be automatically adjusted to keep virtual -time within a few seconds of real time. +SRST +``-icount [shift=N|auto][,rr=record|replay,rrfile=filename,rrsnapshot=snapshot]`` + Enable virtual instruction counter. The virtual cpu will execute one + instruction every 2^N ns of virtual time. If ``auto`` is specified + then the virtual cpu speed will be automatically adjusted to keep + virtual time within a few seconds of real time. -When the virtual cpu is sleeping, the virtual time will advance at default -speed unless @option{sleep=on|off} is specified. -With @option{sleep=on|off}, the virtual time will jump to the next timer deadline -instantly whenever the virtual cpu goes to sleep mode and will not advance -if no timer is enabled. This behavior give deterministic execution times from -the guest point of view. + When the virtual cpu is sleeping, the virtual time will advance at + default speed unless ``sleep=on|off`` is specified. With + ``sleep=on|off``, the virtual time will jump to the next timer + deadline instantly whenever the virtual cpu goes to sleep mode and + will not advance if no timer is enabled. This behavior give + deterministic execution times from the guest point of view. -Note that while this option can give deterministic behavior, it does not -provide cycle accurate emulation. Modern CPUs contain superscalar out of -order cores with complex cache hierarchies. The number of instructions -executed often has little or no correlation with actual performance. + Note that while this option can give deterministic behavior, it does + not provide cycle accurate emulation. Modern CPUs contain + superscalar out of order cores with complex cache hierarchies. The + number of instructions executed often has little or no correlation + with actual performance. -@option{align=on} will activate the delay algorithm which will try -to synchronise the host clock and the virtual clock. The goal is to -have a guest running at the real frequency imposed by the shift option. -Whenever the guest clock is behind the host clock and if -@option{align=on} is specified then we print a message to the user -to inform about the delay. -Currently this option does not work when @option{shift} is @code{auto}. -Note: The sync algorithm will work for those shift values for which -the guest clock runs ahead of the host clock. Typically this happens -when the shift value is high (how high depends on the host machine). + ``align=on`` will activate the delay algorithm which will try to + synchronise the host clock and the virtual clock. The goal is to + have a guest running at the real frequency imposed by the shift + option. Whenever the guest clock is behind the host clock and if + ``align=on`` is specified then we print a message to the user to + inform about the delay. Currently this option does not work when + ``shift`` is ``auto``. Note: The sync algorithm will work for those + shift values for which the guest clock runs ahead of the host clock. + Typically this happens when the shift value is high (how high + depends on the host machine). -When @option{rr} option is specified deterministic record/replay is enabled. -Replay log is written into @var{filename} file in record mode and -read from this file in replay mode. + When ``rr`` option is specified deterministic record/replay is + enabled. Replay log is written into filename file in record mode and + read from this file in replay mode. -Option rrsnapshot is used to create new vm snapshot named @var{snapshot} -at the start of execution recording. In replay mode this option is used -to load the initial VM state. -ETEXI + Option rrsnapshot is used to create new vm snapshot named snapshot + at the start of execution recording. In replay mode this option is + used to load the initial VM state. +ERST DEF("watchdog", HAS_ARG, QEMU_OPTION_watchdog, \ "-watchdog model\n" \ " enable virtual hardware watchdog [default=none]\n", QEMU_ARCH_ALL) -STEXI -@item -watchdog @var{model} -@findex -watchdog -Create a virtual hardware watchdog device. Once enabled (by a guest -action), the watchdog must be periodically polled by an agent inside -the guest or else the guest will be restarted. Choose a model for -which your guest has drivers. +SRST +``-watchdog model`` + Create a virtual hardware watchdog device. Once enabled (by a guest + action), the watchdog must be periodically polled by an agent inside + the guest or else the guest will be restarted. Choose a model for + which your guest has drivers. -The @var{model} is the model of hardware watchdog to emulate. Use -@code{-watchdog help} to list available hardware models. Only one -watchdog can be enabled for a guest. + The model is the model of hardware watchdog to emulate. Use + ``-watchdog help`` to list available hardware models. Only one + watchdog can be enabled for a guest. -The following models may be available: -@table @option -@item ib700 -iBASE 700 is a very simple ISA watchdog with a single timer. -@item i6300esb -Intel 6300ESB I/O controller hub is a much more featureful PCI-based -dual-timer watchdog. -@item diag288 -A virtual watchdog for s390x backed by the diagnose 288 hypercall -(currently KVM only). -@end table -ETEXI + The following models may be available: + + ``ib700`` + iBASE 700 is a very simple ISA watchdog with a single timer. + + ``i6300esb`` + Intel 6300ESB I/O controller hub is a much more featureful + PCI-based dual-timer watchdog. + + ``diag288`` + A virtual watchdog for s390x backed by the diagnose 288 + hypercall (currently KVM only). +ERST DEF("watchdog-action", HAS_ARG, QEMU_OPTION_watchdog_action, \ "-watchdog-action reset|shutdown|poweroff|inject-nmi|pause|debug|none\n" \ " action when watchdog fires [default=reset]\n", QEMU_ARCH_ALL) -STEXI -@item -watchdog-action @var{action} -@findex -watchdog-action +SRST +``-watchdog-action action`` + The action controls what QEMU will do when the watchdog timer + expires. The default is ``reset`` (forcefully reset the guest). + Other possible actions are: ``shutdown`` (attempt to gracefully + shutdown the guest), ``poweroff`` (forcefully poweroff the guest), + ``inject-nmi`` (inject a NMI into the guest), ``pause`` (pause the + guest), ``debug`` (print a debug message and continue), or ``none`` + (do nothing). -The @var{action} controls what QEMU will do when the watchdog timer -expires. -The default is -@code{reset} (forcefully reset the guest). -Other possible actions are: -@code{shutdown} (attempt to gracefully shutdown the guest), -@code{poweroff} (forcefully poweroff the guest), -@code{inject-nmi} (inject a NMI into the guest), -@code{pause} (pause the guest), -@code{debug} (print a debug message and continue), or -@code{none} (do nothing). + Note that the ``shutdown`` action requires that the guest responds + to ACPI signals, which it may not be able to do in the sort of + situations where the watchdog would have expired, and thus + ``-watchdog-action shutdown`` is not recommended for production use. -Note that the @code{shutdown} action requires that the guest responds -to ACPI signals, which it may not be able to do in the sort of -situations where the watchdog would have expired, and thus -@code{-watchdog-action shutdown} is not recommended for production use. + Examples: -Examples: + ``-watchdog i6300esb -watchdog-action pause``; \ ``-watchdog ib700`` -@table @code -@item -watchdog i6300esb -watchdog-action pause -@itemx -watchdog ib700 -@end table -ETEXI +ERST DEF("echr", HAS_ARG, QEMU_OPTION_echr, \ "-echr chr set terminal escape character instead of ctrl-a\n", QEMU_ARCH_ALL) -STEXI +SRST +``-echr numeric_ascii_value`` + Change the escape character used for switching to the monitor when + using monitor and serial sharing. The default is ``0x01`` when using + the ``-nographic`` option. ``0x01`` is equal to pressing + ``Control-a``. You can select a different character from the ascii + control keys where 1 through 26 map to Control-a through Control-z. + For instance you could use the either of the following to change the + escape character to Control-t. -@item -echr @var{numeric_ascii_value} -@findex -echr -Change the escape character used for switching to the monitor when using -monitor and serial sharing. The default is @code{0x01} when using the -@code{-nographic} option. @code{0x01} is equal to pressing -@code{Control-a}. You can select a different character from the ascii -control keys where 1 through 26 map to Control-a through Control-z. For -instance you could use the either of the following to change the escape -character to Control-t. -@table @code -@item -echr 0x14 -@itemx -echr 20 -@end table -ETEXI + ``-echr 0x14``; \ ``-echr 20`` + +ERST DEF("show-cursor", 0, QEMU_OPTION_show_cursor, \ "-show-cursor show cursor\n", QEMU_ARCH_ALL) -STEXI -@item -show-cursor -@findex -show-cursor -Show cursor. -ETEXI +SRST +``-show-cursor`` + Show cursor. +ERST DEF("tb-size", HAS_ARG, QEMU_OPTION_tb_size, \ "-tb-size n set TB size\n", QEMU_ARCH_ALL) -STEXI -@item -tb-size @var{n} -@findex -tb-size -Set TB size. -ETEXI +SRST +``-tb-size n`` + Set TCG translation block cache size. Deprecated, use + '\ ``-accel tcg,tb-size=n``\ ' instead. +ERST DEF("incoming", HAS_ARG, QEMU_OPTION_incoming, \ "-incoming tcp:[host]:port[,to=maxport][,ipv4][,ipv6]\n" \ @@ -4028,58 +4030,56 @@ DEF("incoming", HAS_ARG, QEMU_OPTION_incoming, \ "-incoming defer\n" \ " wait for the URI to be specified via migrate_incoming\n", QEMU_ARCH_ALL) -STEXI -@item -incoming tcp:[@var{host}]:@var{port}[,to=@var{maxport}][,ipv4][,ipv6] -@itemx -incoming rdma:@var{host}:@var{port}[,ipv4][,ipv6] -@findex -incoming -Prepare for incoming migration, listen on a given tcp port. +SRST +``-incoming tcp:[host]:port[,to=maxport][,ipv4][,ipv6]`` + \ +``-incoming rdma:host:port[,ipv4][,ipv6]`` + Prepare for incoming migration, listen on a given tcp port. -@item -incoming unix:@var{socketpath} -Prepare for incoming migration, listen on a given unix socket. +``-incoming unix:socketpath`` + Prepare for incoming migration, listen on a given unix socket. -@item -incoming fd:@var{fd} -Accept incoming migration from a given filedescriptor. +``-incoming fd:fd`` + Accept incoming migration from a given filedescriptor. -@item -incoming exec:@var{cmdline} -Accept incoming migration as an output from specified external command. +``-incoming exec:cmdline`` + Accept incoming migration as an output from specified external + command. -@item -incoming defer -Wait for the URI to be specified via migrate_incoming. The monitor can -be used to change settings (such as migration parameters) prior to issuing -the migrate_incoming to allow the migration to begin. -ETEXI +``-incoming defer`` + Wait for the URI to be specified via migrate\_incoming. The monitor + can be used to change settings (such as migration parameters) prior + to issuing the migrate\_incoming to allow the migration to begin. +ERST DEF("only-migratable", 0, QEMU_OPTION_only_migratable, \ "-only-migratable allow only migratable devices\n", QEMU_ARCH_ALL) -STEXI -@item -only-migratable -@findex -only-migratable -Only allow migratable devices. Devices will not be allowed to enter an -unmigratable state. -ETEXI +SRST +``-only-migratable`` + Only allow migratable devices. Devices will not be allowed to enter + an unmigratable state. +ERST DEF("nodefaults", 0, QEMU_OPTION_nodefaults, \ "-nodefaults don't create default devices\n", QEMU_ARCH_ALL) -STEXI -@item -nodefaults -@findex -nodefaults -Don't create default devices. Normally, QEMU sets the default devices like serial -port, parallel port, virtual console, monitor device, VGA adapter, floppy and -CD-ROM drive and others. The @code{-nodefaults} option will disable all those -default devices. -ETEXI +SRST +``-nodefaults`` + Don't create default devices. Normally, QEMU sets the default + devices like serial port, parallel port, virtual console, monitor + device, VGA adapter, floppy and CD-ROM drive and others. The + ``-nodefaults`` option will disable all those default devices. +ERST #ifndef _WIN32 DEF("chroot", HAS_ARG, QEMU_OPTION_chroot, \ "-chroot dir chroot to dir just before starting the VM\n", QEMU_ARCH_ALL) #endif -STEXI -@item -chroot @var{dir} -@findex -chroot -Immediately before starting guest execution, chroot to the specified -directory. Especially useful in combination with -runas. -ETEXI +SRST +``-chroot dir`` + Immediately before starting guest execution, chroot to the specified + directory. Especially useful in combination with -runas. +ERST #ifndef _WIN32 DEF("runas", HAS_ARG, QEMU_OPTION_runas, \ @@ -4087,62 +4087,91 @@ DEF("runas", HAS_ARG, QEMU_OPTION_runas, \ " user can be numeric uid:gid instead\n", QEMU_ARCH_ALL) #endif -STEXI -@item -runas @var{user} -@findex -runas -Immediately before starting guest execution, drop root privileges, switching -to the specified user. -ETEXI +SRST +``-runas user`` + Immediately before starting guest execution, drop root privileges, + switching to the specified user. +ERST DEF("prom-env", HAS_ARG, QEMU_OPTION_prom_env, "-prom-env variable=value\n" " set OpenBIOS nvram variables\n", QEMU_ARCH_PPC | QEMU_ARCH_SPARC) -STEXI -@item -prom-env @var{variable}=@var{value} -@findex -prom-env -Set OpenBIOS nvram @var{variable} to given @var{value} (PPC, SPARC only). -ETEXI +SRST +``-prom-env variable=value`` + Set OpenBIOS nvram variable to given value (PPC, SPARC only). + + :: + + qemu-system-sparc -prom-env 'auto-boot?=false' \ + -prom-env 'boot-device=sd(0,2,0):d' -prom-env 'boot-args=linux single' + + :: + + qemu-system-ppc -prom-env 'auto-boot?=false' \ + -prom-env 'boot-device=hd:2,\yaboot' \ + -prom-env 'boot-args=conf=hd:2,\yaboot.conf' +ERST DEF("semihosting", 0, QEMU_OPTION_semihosting, "-semihosting semihosting mode\n", QEMU_ARCH_ARM | QEMU_ARCH_M68K | QEMU_ARCH_XTENSA | QEMU_ARCH_LM32 | QEMU_ARCH_MIPS | QEMU_ARCH_NIOS2) -STEXI -@item -semihosting -@findex -semihosting -Enable semihosting mode (ARM, M68K, Xtensa, MIPS, Nios II only). -ETEXI +SRST +``-semihosting`` + Enable semihosting mode (ARM, M68K, Xtensa, MIPS, Nios II only). + + Note that this allows guest direct access to the host filesystem, so + should only be used with a trusted guest OS. + + See the -semihosting-config option documentation for further + information about the facilities this enables. +ERST DEF("semihosting-config", HAS_ARG, QEMU_OPTION_semihosting_config, "-semihosting-config [enable=on|off][,target=native|gdb|auto][,chardev=id][,arg=str[,...]]\n" \ " semihosting configuration\n", QEMU_ARCH_ARM | QEMU_ARCH_M68K | QEMU_ARCH_XTENSA | QEMU_ARCH_LM32 | QEMU_ARCH_MIPS | QEMU_ARCH_NIOS2) -STEXI -@item -semihosting-config [enable=on|off][,target=native|gdb|auto][,chardev=id][,arg=str[,...]] -@findex -semihosting-config -Enable and configure semihosting (ARM, M68K, Xtensa, MIPS, Nios II only). -@table @option -@item target=@code{native|gdb|auto} -Defines where the semihosting calls will be addressed, to QEMU (@code{native}) -or to GDB (@code{gdb}). The default is @code{auto}, which means @code{gdb} -during debug sessions and @code{native} otherwise. -@item chardev=@var{str1} -Send the output to a chardev backend output for native or auto output when not in gdb -@item arg=@var{str1},arg=@var{str2},... -Allows the user to pass input arguments, and can be used multiple times to build -up a list. The old-style @code{-kernel}/@code{-append} method of passing a -command line is still supported for backward compatibility. If both the -@code{--semihosting-config arg} and the @code{-kernel}/@code{-append} are -specified, the former is passed to semihosting as it always takes precedence. -@end table -ETEXI +SRST +``-semihosting-config [enable=on|off][,target=native|gdb|auto][,chardev=id][,arg=str[,...]]`` + Enable and configure semihosting (ARM, M68K, Xtensa, MIPS, Nios II + only). + + Note that this allows guest direct access to the host filesystem, so + should only be used with a trusted guest OS. + + On Arm this implements the standard semihosting API, version 2.0. + + On M68K this implements the "ColdFire GDB" interface used by + libgloss. + + Xtensa semihosting provides basic file IO calls, such as + open/read/write/seek/select. Tensilica baremetal libc for ISS and + linux platform "sim" use this interface. + + ``target=native|gdb|auto`` + Defines where the semihosting calls will be addressed, to QEMU + (``native``) or to GDB (``gdb``). The default is ``auto``, which + means ``gdb`` during debug sessions and ``native`` otherwise. + + ``chardev=str1`` + Send the output to a chardev backend output for native or auto + output when not in gdb + + ``arg=str1,arg=str2,...`` + Allows the user to pass input arguments, and can be used + multiple times to build up a list. The old-style + ``-kernel``/``-append`` method of passing a command line is + still supported for backward compatibility. If both the + ``--semihosting-config arg`` and the ``-kernel``/``-append`` are + specified, the former is passed to semihosting as it always + takes precedence. +ERST DEF("old-param", 0, QEMU_OPTION_old_param, "-old-param old param mode\n", QEMU_ARCH_ARM) -STEXI -@item -old-param -@findex -old-param (ARM) -Old param mode (ARM only). -ETEXI +SRST +``-old-param`` + Old param mode (ARM only). +ERST DEF("sandbox", HAS_ARG, QEMU_OPTION_sandbox, \ "-sandbox on[,obsolete=allow|deny][,elevateprivileges=allow|deny|children]\n" \ @@ -4159,82 +4188,77 @@ DEF("sandbox", HAS_ARG, QEMU_OPTION_sandbox, \ " blacklisting *fork and execve\n" \ " use 'resourcecontrol' to disable process affinity and schedular priority\n", QEMU_ARCH_ALL) -STEXI -@item -sandbox @var{arg}[,obsolete=@var{string}][,elevateprivileges=@var{string}][,spawn=@var{string}][,resourcecontrol=@var{string}] -@findex -sandbox -Enable Seccomp mode 2 system call filter. 'on' will enable syscall filtering and 'off' will -disable it. The default is 'off'. -@table @option -@item obsolete=@var{string} -Enable Obsolete system calls -@item elevateprivileges=@var{string} -Disable set*uid|gid system calls -@item spawn=@var{string} -Disable *fork and execve -@item resourcecontrol=@var{string} -Disable process affinity and schedular priority -@end table -ETEXI +SRST +``-sandbox arg[,obsolete=string][,elevateprivileges=string][,spawn=string][,resourcecontrol=string]`` + Enable Seccomp mode 2 system call filter. 'on' will enable syscall + filtering and 'off' will disable it. The default is 'off'. + + ``obsolete=string`` + Enable Obsolete system calls + + ``elevateprivileges=string`` + Disable set\*uid\|gid system calls + + ``spawn=string`` + Disable \*fork and execve + + ``resourcecontrol=string`` + Disable process affinity and schedular priority +ERST DEF("readconfig", HAS_ARG, QEMU_OPTION_readconfig, "-readconfig \n", QEMU_ARCH_ALL) -STEXI -@item -readconfig @var{file} -@findex -readconfig -Read device configuration from @var{file}. This approach is useful when you want to spawn -QEMU process with many command line options but you don't want to exceed the command line -character limit. -ETEXI +SRST +``-readconfig file`` + Read device configuration from file. This approach is useful when + you want to spawn QEMU process with many command line options but + you don't want to exceed the command line character limit. +ERST DEF("writeconfig", HAS_ARG, QEMU_OPTION_writeconfig, "-writeconfig \n" " read/write config file\n", QEMU_ARCH_ALL) -STEXI -@item -writeconfig @var{file} -@findex -writeconfig -Write device configuration to @var{file}. The @var{file} can be either filename to save -command line and device configuration into file or dash @code{-}) character to print the -output to stdout. This can be later used as input file for @code{-readconfig} option. -ETEXI +SRST +``-writeconfig file`` + Write device configuration to file. The file can be either filename + to save command line and device configuration into file or dash + ``-``) character to print the output to stdout. This can be later + used as input file for ``-readconfig`` option. +ERST DEF("no-user-config", 0, QEMU_OPTION_nouserconfig, "-no-user-config\n" " do not load default user-provided config files at startup\n", QEMU_ARCH_ALL) -STEXI -@item -no-user-config -@findex -no-user-config -The @code{-no-user-config} option makes QEMU not load any of the user-provided -config files on @var{sysconfdir}. -ETEXI +SRST +``-no-user-config`` + The ``-no-user-config`` option makes QEMU not load any of the + user-provided config files on sysconfdir. +ERST DEF("trace", HAS_ARG, QEMU_OPTION_trace, "-trace [[enable=]][,events=][,file=]\n" " specify tracing options\n", QEMU_ARCH_ALL) -STEXI -HXCOMM This line is not accurate, as some sub-options are backend-specific but -HXCOMM HX does not support conditional compilation of text. -@item -trace [[enable=]@var{pattern}][,events=@var{file}][,file=@var{file}] -@findex -trace -@include qemu-option-trace.texi -ETEXI +SRST +``-trace [[enable=]pattern][,events=file][,file=file]`` + .. include:: ../qemu-option-trace.rst.inc + +ERST DEF("plugin", HAS_ARG, QEMU_OPTION_plugin, "-plugin [file=][,arg=]\n" " load a plugin\n", QEMU_ARCH_ALL) -STEXI -@item -plugin file=@var{file}[,arg=@var{string}] -@findex -plugin +SRST +``-plugin file=file[,arg=string]`` + Load a plugin. -Load a plugin. + ``file=file`` + Load the given plugin from a shared library file. -@table @option -@item file=@var{file} -Load the given plugin from a shared library file. -@item arg=@var{string} -Argument string passed to the plugin. (Can be given multiple times.) -@end table -ETEXI + ``arg=string`` + Argument string passed to the plugin. (Can be given multiple + times.) +ERST HXCOMM Internal use DEF("qtest", HAS_ARG, QEMU_OPTION_qtest, "", QEMU_ARCH_ALL) @@ -4245,25 +4269,26 @@ DEF("enable-fips", 0, QEMU_OPTION_enablefips, "-enable-fips enable FIPS 140-2 compliance\n", QEMU_ARCH_ALL) #endif -STEXI -@item -enable-fips -@findex -enable-fips -Enable FIPS 140-2 compliance mode. -ETEXI +SRST +``-enable-fips`` + Enable FIPS 140-2 compliance mode. +ERST HXCOMM Deprecated by -accel tcg DEF("no-kvm", 0, QEMU_OPTION_no_kvm, "", QEMU_ARCH_I386) DEF("msg", HAS_ARG, QEMU_OPTION_msg, "-msg timestamp[=on|off]\n" - " change the format of messages\n" - " on|off controls leading timestamps (default:on)\n", + " control error message format\n" + " timestamp=on enables timestamps (default: off)\n", QEMU_ARCH_ALL) -STEXI -@item -msg timestamp[=on|off] -@findex -msg -prepend a timestamp to each log message.(default:on) -ETEXI +SRST +``-msg timestamp[=on|off]`` + Control error message format. + + ``timestamp=on|off`` + Prefix messages with a timestamp. Default is off. +ERST DEF("dump-vmstate", HAS_ARG, QEMU_OPTION_dump_vmstate, "-dump-vmstate \n" @@ -4272,32 +4297,24 @@ DEF("dump-vmstate", HAS_ARG, QEMU_OPTION_dump_vmstate, " check for possible regressions in migration code\n" " by comparing two such vmstate dumps.\n", QEMU_ARCH_ALL) -STEXI -@item -dump-vmstate @var{file} -@findex -dump-vmstate -Dump json-encoded vmstate information for current machine type to file -in @var{file} -ETEXI +SRST +``-dump-vmstate file`` + Dump json-encoded vmstate information for current machine type to + file in file +ERST DEF("enable-sync-profile", 0, QEMU_OPTION_enable_sync_profile, "-enable-sync-profile\n" " enable synchronization profiling\n", QEMU_ARCH_ALL) -STEXI -@item -enable-sync-profile -@findex -enable-sync-profile -Enable synchronization profiling. -ETEXI +SRST +``-enable-sync-profile`` + Enable synchronization profiling. +ERST -STEXI -@end table -ETEXI DEFHEADING() DEFHEADING(Generic object creation:) -STEXI -@table @option -ETEXI DEF("object", HAS_ARG, QEMU_OPTION_object, "-object TYPENAME[,PROP1=VALUE1,...]\n" @@ -4306,633 +4323,701 @@ DEF("object", HAS_ARG, QEMU_OPTION_object, " property must be set. These objects are placed in the\n" " '/objects' path.\n", QEMU_ARCH_ALL) -STEXI -@item -object @var{typename}[,@var{prop1}=@var{value1},...] -@findex -object -Create a new object of type @var{typename} setting properties -in the order they are specified. Note that the 'id' -property must be set. These objects are placed in the -'/objects' path. +SRST +``-object typename[,prop1=value1,...]`` + Create a new object of type typename setting properties in the order + they are specified. Note that the 'id' property must be set. These + objects are placed in the '/objects' path. + + ``-object memory-backend-file,id=id,size=size,mem-path=dir,share=on|off,discard-data=on|off,merge=on|off,dump=on|off,prealloc=on|off,host-nodes=host-nodes,policy=default|preferred|bind|interleave,align=align`` + Creates a memory file backend object, which can be used to back + the guest RAM with huge pages. + + The ``id`` parameter is a unique ID that will be used to + reference this memory region when configuring the ``-numa`` + argument. + + The ``size`` option provides the size of the memory region, and + accepts common suffixes, eg ``500M``. + + The ``mem-path`` provides the path to either a shared memory or + huge page filesystem mount. + + The ``share`` boolean option determines whether the memory + region is marked as private to QEMU, or shared. The latter + allows a co-operating external process to access the QEMU memory + region. + + The ``share`` is also required for pvrdma devices due to + limitations in the RDMA API provided by Linux. + + Setting share=on might affect the ability to configure NUMA + bindings for the memory backend under some circumstances, see + Documentation/vm/numa\_memory\_policy.txt on the Linux kernel + source tree for additional details. + + Setting the ``discard-data`` boolean option to on indicates that + file contents can be destroyed when QEMU exits, to avoid + unnecessarily flushing data to the backing file. Note that + ``discard-data`` is only an optimization, and QEMU might not + discard file contents if it aborts unexpectedly or is terminated + using SIGKILL. + + The ``merge`` boolean option enables memory merge, also known as + MADV\_MERGEABLE, so that Kernel Samepage Merging will consider + the pages for memory deduplication. + + Setting the ``dump`` boolean option to off excludes the memory + from core dumps. This feature is also known as MADV\_DONTDUMP. + + The ``prealloc`` boolean option enables memory preallocation. + + The ``host-nodes`` option binds the memory range to a list of + NUMA host nodes. + + The ``policy`` option sets the NUMA policy to one of the + following values: + + ``default`` + default host policy + + ``preferred`` + prefer the given host node list for allocation + + ``bind`` + restrict memory allocation to the given host node list + + ``interleave`` + interleave memory allocations across the given host node + list + + The ``align`` option specifies the base address alignment when + QEMU mmap(2) ``mem-path``, and accepts common suffixes, eg + ``2M``. Some backend store specified by ``mem-path`` requires an + alignment different than the default one used by QEMU, eg the + device DAX /dev/dax0.0 requires 2M alignment rather than 4K. In + such cases, users can specify the required alignment via this + option. + + The ``pmem`` option specifies whether the backing file specified + by ``mem-path`` is in host persistent memory that can be + accessed using the SNIA NVM programming model (e.g. Intel + NVDIMM). If ``pmem`` is set to 'on', QEMU will take necessary + operations to guarantee the persistence of its own writes to + ``mem-path`` (e.g. in vNVDIMM label emulation and live + migration). Also, we will map the backend-file with MAP\_SYNC + flag, which ensures the file metadata is in sync for + ``mem-path`` in case of host crash or a power failure. MAP\_SYNC + requires support from both the host kernel (since Linux kernel + 4.15) and the filesystem of ``mem-path`` mounted with DAX + option. + + ``-object memory-backend-ram,id=id,merge=on|off,dump=on|off,share=on|off,prealloc=on|off,size=size,host-nodes=host-nodes,policy=default|preferred|bind|interleave`` + Creates a memory backend object, which can be used to back the + guest RAM. Memory backend objects offer more control than the + ``-m`` option that is traditionally used to define guest RAM. + Please refer to ``memory-backend-file`` for a description of the + options. + + ``-object memory-backend-memfd,id=id,merge=on|off,dump=on|off,share=on|off,prealloc=on|off,size=size,host-nodes=host-nodes,policy=default|preferred|bind|interleave,seal=on|off,hugetlb=on|off,hugetlbsize=size`` + Creates an anonymous memory file backend object, which allows + QEMU to share the memory with an external process (e.g. when + using vhost-user). The memory is allocated with memfd and + optional sealing. (Linux only) + + The ``seal`` option creates a sealed-file, that will block + further resizing the memory ('on' by default). + + The ``hugetlb`` option specify the file to be created resides in + the hugetlbfs filesystem (since Linux 4.14). Used in conjunction + with the ``hugetlb`` option, the ``hugetlbsize`` option specify + the hugetlb page size on systems that support multiple hugetlb + page sizes (it must be a power of 2 value supported by the + system). + + In some versions of Linux, the ``hugetlb`` option is + incompatible with the ``seal`` option (requires at least Linux + 4.16). + + Please refer to ``memory-backend-file`` for a description of the + other options. + + The ``share`` boolean option is on by default with memfd. + + ``-object rng-builtin,id=id`` + Creates a random number generator backend which obtains entropy + from QEMU builtin functions. The ``id`` parameter is a unique ID + that will be used to reference this entropy backend from the + ``virtio-rng`` device. By default, the ``virtio-rng`` device + uses this RNG backend. + + ``-object rng-random,id=id,filename=/dev/random`` + Creates a random number generator backend which obtains entropy + from a device on the host. The ``id`` parameter is a unique ID + that will be used to reference this entropy backend from the + ``virtio-rng`` device. The ``filename`` parameter specifies + which file to obtain entropy from and if omitted defaults to + ``/dev/urandom``. + + ``-object rng-egd,id=id,chardev=chardevid`` + Creates a random number generator backend which obtains entropy + from an external daemon running on the host. The ``id`` + parameter is a unique ID that will be used to reference this + entropy backend from the ``virtio-rng`` device. The ``chardev`` + parameter is the unique ID of a character device backend that + provides the connection to the RNG daemon. + + ``-object tls-creds-anon,id=id,endpoint=endpoint,dir=/path/to/cred/dir,verify-peer=on|off`` + Creates a TLS anonymous credentials object, which can be used to + provide TLS support on network backends. The ``id`` parameter is + a unique ID which network backends will use to access the + credentials. The ``endpoint`` is either ``server`` or ``client`` + depending on whether the QEMU network backend that uses the + credentials will be acting as a client or as a server. If + ``verify-peer`` is enabled (the default) then once the handshake + is completed, the peer credentials will be verified, though this + is a no-op for anonymous credentials. + + The dir parameter tells QEMU where to find the credential files. + For server endpoints, this directory may contain a file + dh-params.pem providing diffie-hellman parameters to use for the + TLS server. If the file is missing, QEMU will generate a set of + DH parameters at startup. This is a computationally expensive + operation that consumes random pool entropy, so it is + recommended that a persistent set of parameters be generated + upfront and saved. + + ``-object tls-creds-psk,id=id,endpoint=endpoint,dir=/path/to/keys/dir[,username=username]`` + Creates a TLS Pre-Shared Keys (PSK) credentials object, which + can be used to provide TLS support on network backends. The + ``id`` parameter is a unique ID which network backends will use + to access the credentials. The ``endpoint`` is either ``server`` + or ``client`` depending on whether the QEMU network backend that + uses the credentials will be acting as a client or as a server. + For clients only, ``username`` is the username which will be + sent to the server. If omitted it defaults to "qemu". + + The dir parameter tells QEMU where to find the keys file. It is + called "dir/keys.psk" and contains "username:key" pairs. This + file can most easily be created using the GnuTLS ``psktool`` + program. + + For server endpoints, dir may also contain a file dh-params.pem + providing diffie-hellman parameters to use for the TLS server. + If the file is missing, QEMU will generate a set of DH + parameters at startup. This is a computationally expensive + operation that consumes random pool entropy, so it is + recommended that a persistent set of parameters be generated up + front and saved. + + ``-object tls-creds-x509,id=id,endpoint=endpoint,dir=/path/to/cred/dir,priority=priority,verify-peer=on|off,passwordid=id`` + Creates a TLS anonymous credentials object, which can be used to + provide TLS support on network backends. The ``id`` parameter is + a unique ID which network backends will use to access the + credentials. The ``endpoint`` is either ``server`` or ``client`` + depending on whether the QEMU network backend that uses the + credentials will be acting as a client or as a server. If + ``verify-peer`` is enabled (the default) then once the handshake + is completed, the peer credentials will be verified. With x509 + certificates, this implies that the clients must be provided + with valid client certificates too. + + The dir parameter tells QEMU where to find the credential files. + For server endpoints, this directory may contain a file + dh-params.pem providing diffie-hellman parameters to use for the + TLS server. If the file is missing, QEMU will generate a set of + DH parameters at startup. This is a computationally expensive + operation that consumes random pool entropy, so it is + recommended that a persistent set of parameters be generated + upfront and saved. + + For x509 certificate credentials the directory will contain + further files providing the x509 certificates. The certificates + must be stored in PEM format, in filenames ca-cert.pem, + ca-crl.pem (optional), server-cert.pem (only servers), + server-key.pem (only servers), client-cert.pem (only clients), + and client-key.pem (only clients). + + For the server-key.pem and client-key.pem files which contain + sensitive private keys, it is possible to use an encrypted + version by providing the passwordid parameter. This provides the + ID of a previously created ``secret`` object containing the + password for decryption. + + The priority parameter allows to override the global default + priority used by gnutls. This can be useful if the system + administrator needs to use a weaker set of crypto priorities for + QEMU without potentially forcing the weakness onto all + applications. Or conversely if one wants wants a stronger + default for QEMU than for all other applications, they can do + this through this parameter. Its format is a gnutls priority + string as described at + https://gnutls.org/manual/html_node/Priority-Strings.html. + + ``-object filter-buffer,id=id,netdev=netdevid,interval=t[,queue=all|rx|tx][,status=on|off][,position=head|tail|id=][,insert=behind|before]`` + Interval t can't be 0, this filter batches the packet delivery: + all packets arriving in a given interval on netdev netdevid are + delayed until the end of the interval. Interval is in + microseconds. ``status`` is optional that indicate whether the + netfilter is on (enabled) or off (disabled), the default status + for netfilter will be 'on'. + + queue all\|rx\|tx is an option that can be applied to any + netfilter. + + ``all``: the filter is attached both to the receive and the + transmit queue of the netdev (default). + + ``rx``: the filter is attached to the receive queue of the + netdev, where it will receive packets sent to the netdev. + + ``tx``: the filter is attached to the transmit queue of the + netdev, where it will receive packets sent by the netdev. + + position head\|tail\|id= is an option to specify where the + filter should be inserted in the filter list. It can be applied + to any netfilter. + + ``head``: the filter is inserted at the head of the filter list, + before any existing filters. + + ``tail``: the filter is inserted at the tail of the filter list, + behind any existing filters (default). + + ``id=``: the filter is inserted before or behind the filter + specified by , see the insert option below. + + insert behind\|before is an option to specify where to insert + the new filter relative to the one specified with + position=id=. It can be applied to any netfilter. + + ``before``: insert before the specified filter. + + ``behind``: insert behind the specified filter (default). + + ``-object filter-mirror,id=id,netdev=netdevid,outdev=chardevid,queue=all|rx|tx[,vnet_hdr_support][,position=head|tail|id=][,insert=behind|before]`` + filter-mirror on netdev netdevid,mirror net packet to + chardevchardevid, if it has the vnet\_hdr\_support flag, + filter-mirror will mirror packet with vnet\_hdr\_len. + + ``-object filter-redirector,id=id,netdev=netdevid,indev=chardevid,outdev=chardevid,queue=all|rx|tx[,vnet_hdr_support][,position=head|tail|id=][,insert=behind|before]`` + filter-redirector on netdev netdevid,redirect filter's net + packet to chardev chardevid,and redirect indev's packet to + filter.if it has the vnet\_hdr\_support flag, filter-redirector + will redirect packet with vnet\_hdr\_len. Create a + filter-redirector we need to differ outdev id from indev id, id + can not be the same. we can just use indev or outdev, but at + least one of indev or outdev need to be specified. + + ``-object filter-rewriter,id=id,netdev=netdevid,queue=all|rx|tx,[vnet_hdr_support][,position=head|tail|id=][,insert=behind|before]`` + Filter-rewriter is a part of COLO project.It will rewrite tcp + packet to secondary from primary to keep secondary tcp + connection,and rewrite tcp packet to primary from secondary make + tcp packet can be handled by client.if it has the + vnet\_hdr\_support flag, we can parse packet with vnet header. + + usage: colo secondary: -object + filter-redirector,id=f1,netdev=hn0,queue=tx,indev=red0 -object + filter-redirector,id=f2,netdev=hn0,queue=rx,outdev=red1 -object + filter-rewriter,id=rew0,netdev=hn0,queue=all + + ``-object filter-dump,id=id,netdev=dev[,file=filename][,maxlen=len][,position=head|tail|id=][,insert=behind|before]`` + Dump the network traffic on netdev dev to the file specified by + filename. At most len bytes (64k by default) per packet are + stored. The file format is libpcap, so it can be analyzed with + tools such as tcpdump or Wireshark. + + ``-object colo-compare,id=id,primary_in=chardevid,secondary_in=chardevid,outdev=chardevid,iothread=id[,vnet_hdr_support][,notify_dev=id][,compare_timeout=@var{ms}][,expired_scan_cycle=@var{ms}`` + Colo-compare gets packet from primary\_inchardevid and + secondary\_inchardevid, than compare primary packet with + secondary packet. If the packets are same, we will output + primary packet to outdevchardevid, else we will notify + colo-frame do checkpoint and send primary packet to + outdevchardevid. In order to improve efficiency, we need to put + the task of comparison in another thread. If it has the + vnet\_hdr\_support flag, colo compare will send/recv packet with + vnet\_hdr\_len. Then compare\_timeout=@var{ms} determines the + maximum delay colo-compare wait for the packet. + The expired\_scan\_cycle=@var{ms} to set the period of scanning + expired primary node network packets. + If you want to use Xen COLO, will need the notify\_dev to + notify Xen colo-frame to do checkpoint. + + we must use it with the help of filter-mirror and + filter-redirector. + + :: + + KVM COLO + + primary: + -netdev tap,id=hn0,vhost=off,script=/etc/qemu-ifup,downscript=/etc/qemu-ifdown + -device e1000,id=e0,netdev=hn0,mac=52:a4:00:12:78:66 + -chardev socket,id=mirror0,host=3.3.3.3,port=9003,server,nowait + -chardev socket,id=compare1,host=3.3.3.3,port=9004,server,nowait + -chardev socket,id=compare0,host=3.3.3.3,port=9001,server,nowait + -chardev socket,id=compare0-0,host=3.3.3.3,port=9001 + -chardev socket,id=compare_out,host=3.3.3.3,port=9005,server,nowait + -chardev socket,id=compare_out0,host=3.3.3.3,port=9005 + -object iothread,id=iothread1 + -object filter-mirror,id=m0,netdev=hn0,queue=tx,outdev=mirror0 + -object filter-redirector,netdev=hn0,id=redire0,queue=rx,indev=compare_out + -object filter-redirector,netdev=hn0,id=redire1,queue=rx,outdev=compare0 + -object colo-compare,id=comp0,primary_in=compare0-0,secondary_in=compare1,outdev=compare_out0,iothread=iothread1 + + secondary: + -netdev tap,id=hn0,vhost=off,script=/etc/qemu-ifup,down script=/etc/qemu-ifdown + -device e1000,netdev=hn0,mac=52:a4:00:12:78:66 + -chardev socket,id=red0,host=3.3.3.3,port=9003 + -chardev socket,id=red1,host=3.3.3.3,port=9004 + -object filter-redirector,id=f1,netdev=hn0,queue=tx,indev=red0 + -object filter-redirector,id=f2,netdev=hn0,queue=rx,outdev=red1 + + + Xen COLO + + primary: + -netdev tap,id=hn0,vhost=off,script=/etc/qemu-ifup,downscript=/etc/qemu-ifdown + -device e1000,id=e0,netdev=hn0,mac=52:a4:00:12:78:66 + -chardev socket,id=mirror0,host=3.3.3.3,port=9003,server,nowait + -chardev socket,id=compare1,host=3.3.3.3,port=9004,server,nowait + -chardev socket,id=compare0,host=3.3.3.3,port=9001,server,nowait + -chardev socket,id=compare0-0,host=3.3.3.3,port=9001 + -chardev socket,id=compare_out,host=3.3.3.3,port=9005,server,nowait + -chardev socket,id=compare_out0,host=3.3.3.3,port=9005 + -chardev socket,id=notify_way,host=3.3.3.3,port=9009,server,nowait + -object filter-mirror,id=m0,netdev=hn0,queue=tx,outdev=mirror0 + -object filter-redirector,netdev=hn0,id=redire0,queue=rx,indev=compare_out + -object filter-redirector,netdev=hn0,id=redire1,queue=rx,outdev=compare0 + -object iothread,id=iothread1 + -object colo-compare,id=comp0,primary_in=compare0-0,secondary_in=compare1,outdev=compare_out0,notify_dev=nofity_way,iothread=iothread1 + + secondary: + -netdev tap,id=hn0,vhost=off,script=/etc/qemu-ifup,down script=/etc/qemu-ifdown + -device e1000,netdev=hn0,mac=52:a4:00:12:78:66 + -chardev socket,id=red0,host=3.3.3.3,port=9003 + -chardev socket,id=red1,host=3.3.3.3,port=9004 + -object filter-redirector,id=f1,netdev=hn0,queue=tx,indev=red0 + -object filter-redirector,id=f2,netdev=hn0,queue=rx,outdev=red1 + + If you want to know the detail of above command line, you can + read the colo-compare git log. + + ``-object cryptodev-backend-builtin,id=id[,queues=queues]`` + Creates a cryptodev backend which executes crypto opreation from + the QEMU cipher APIS. The id parameter is a unique ID that will + be used to reference this cryptodev backend from the + ``virtio-crypto`` device. The queues parameter is optional, + which specify the queue number of cryptodev backend, the default + of queues is 1. + + .. parsed-literal:: + + # |qemu_system| \ + [...] \ + -object cryptodev-backend-builtin,id=cryptodev0 \ + -device virtio-crypto-pci,id=crypto0,cryptodev=cryptodev0 \ + [...] + + ``-object cryptodev-vhost-user,id=id,chardev=chardevid[,queues=queues]`` + Creates a vhost-user cryptodev backend, backed by a chardev + chardevid. The id parameter is a unique ID that will be used to + reference this cryptodev backend from the ``virtio-crypto`` + device. The chardev should be a unix domain socket backed one. + The vhost-user uses a specifically defined protocol to pass + vhost ioctl replacement messages to an application on the other + end of the socket. The queues parameter is optional, which + specify the queue number of cryptodev backend for multiqueue + vhost-user, the default of queues is 1. + + .. parsed-literal:: + + # |qemu_system| \ + [...] \ + -chardev socket,id=chardev0,path=/path/to/socket \ + -object cryptodev-vhost-user,id=cryptodev0,chardev=chardev0 \ + -device virtio-crypto-pci,id=crypto0,cryptodev=cryptodev0 \ + [...] + + ``-object secret,id=id,data=string,format=raw|base64[,keyid=secretid,iv=string]`` + \ + ``-object secret,id=id,file=filename,format=raw|base64[,keyid=secretid,iv=string]`` + Defines a secret to store a password, encryption key, or some + other sensitive data. The sensitive data can either be passed + directly via the data parameter, or indirectly via the file + parameter. Using the data parameter is insecure unless the + sensitive data is encrypted. + + The sensitive data can be provided in raw format (the default), + or base64. When encoded as JSON, the raw format only supports + valid UTF-8 characters, so base64 is recommended for sending + binary data. QEMU will convert from which ever format is + provided to the format it needs internally. eg, an RBD password + can be provided in raw format, even though it will be base64 + encoded when passed onto the RBD sever. + + For added protection, it is possible to encrypt the data + associated with a secret using the AES-256-CBC cipher. Use of + encryption is indicated by providing the keyid and iv + parameters. The keyid parameter provides the ID of a previously + defined secret that contains the AES-256 decryption key. This + key should be 32-bytes long and be base64 encoded. The iv + parameter provides the random initialization vector used for + encryption of this particular secret and should be a base64 + encrypted string of the 16-byte IV. + + The simplest (insecure) usage is to provide the secret inline + + .. parsed-literal:: + + # |qemu_system| -object secret,id=sec0,data=letmein,format=raw + + The simplest secure usage is to provide the secret via a file + + # printf "letmein" > mypasswd.txt # QEMU\_SYSTEM\_MACRO -object + secret,id=sec0,file=mypasswd.txt,format=raw + + For greater security, AES-256-CBC should be used. To illustrate + usage, consider the openssl command line tool which can encrypt + the data. Note that when encrypting, the plaintext must be + padded to the cipher block size (32 bytes) using the standard + PKCS#5/6 compatible padding algorithm. + + First a master key needs to be created in base64 encoding: + + :: + + # openssl rand -base64 32 > key.b64 + # KEY=$(base64 -d key.b64 | hexdump -v -e '/1 "%02X"') + + Each secret to be encrypted needs to have a random + initialization vector generated. These do not need to be kept + secret + + :: + + # openssl rand -base64 16 > iv.b64 + # IV=$(base64 -d iv.b64 | hexdump -v -e '/1 "%02X"') + + The secret to be defined can now be encrypted, in this case + we're telling openssl to base64 encode the result, but it could + be left as raw bytes if desired. + + :: + + # SECRET=$(printf "letmein" | + openssl enc -aes-256-cbc -a -K $KEY -iv $IV) + + When launching QEMU, create a master secret pointing to + ``key.b64`` and specify that to be used to decrypt the user + password. Pass the contents of ``iv.b64`` to the second secret + + .. parsed-literal:: + + # |qemu_system| \ + -object secret,id=secmaster0,format=base64,file=key.b64 \ + -object secret,id=sec0,keyid=secmaster0,format=base64,\ + data=$SECRET,iv=$( mypasswd.txt - # @value{qemu_system} -object secret,id=sec0,file=mypasswd.txt,format=raw - -For greater security, AES-256-CBC should be used. To illustrate usage, -consider the openssl command line tool which can encrypt the data. Note -that when encrypting, the plaintext must be padded to the cipher block -size (32 bytes) using the standard PKCS#5/6 compatible padding algorithm. - -First a master key needs to be created in base64 encoding: - -@example - # openssl rand -base64 32 > key.b64 - # KEY=$(base64 -d key.b64 | hexdump -v -e '/1 "%02X"') -@end example - -Each secret to be encrypted needs to have a random initialization vector -generated. These do not need to be kept secret - -@example - # openssl rand -base64 16 > iv.b64 - # IV=$(base64 -d iv.b64 | hexdump -v -e '/1 "%02X"') -@end example - -The secret to be defined can now be encrypted, in this case we're -telling openssl to base64 encode the result, but it could be left -as raw bytes if desired. - -@example - # SECRET=$(printf "letmein" | - openssl enc -aes-256-cbc -a -K $KEY -iv $IV) -@end example - -When launching QEMU, create a master secret pointing to @code{key.b64} -and specify that to be used to decrypt the user password. Pass the -contents of @code{iv.b64} to the second secret - -@example - # @value{qemu_system} \ - -object secret,id=secmaster0,format=base64,file=key.b64 \ - -object secret,id=sec0,keyid=secmaster0,format=base64,\ - data=$SECRET,iv=$( + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" + +#include + +#include "block/block.h" +#include "block/nbd.h" +#include "chardev/char.h" +#include "crypto/init.h" +#include "monitor/monitor.h" +#include "monitor/monitor-internal.h" + +#include "qapi/error.h" +#include "qapi/qapi-visit-block.h" +#include "qapi/qapi-visit-block-core.h" +#include "qapi/qapi-visit-control.h" +#include "qapi/qmp/qdict.h" +#include "qapi/qmp/qstring.h" +#include "qapi/qobject-input-visitor.h" + +#include "qemu-common.h" +#include "qemu-version.h" +#include "qemu/config-file.h" +#include "qemu/error-report.h" +#include "qemu/help_option.h" +#include "qemu/log.h" +#include "qemu/main-loop.h" +#include "qemu/module.h" +#include "qemu/option.h" +#include "qom/object_interfaces.h" + +#include "storage-daemon/qapi/qapi-commands.h" +#include "storage-daemon/qapi/qapi-init-commands.h" + +#include "sysemu/runstate.h" +#include "trace/control.h" + +static volatile bool exit_requested = false; + +void qemu_system_killed(int signal, pid_t pid) +{ + exit_requested = true; +} + +void qmp_quit(Error **errp) +{ + exit_requested = true; +} + +static void help(void) +{ + printf( +"Usage: %s [options]\n" +"QEMU storage daemon\n" +"\n" +" -h, --help display this help and exit\n" +" -T, --trace [[enable=]][,events=][,file=]\n" +" specify tracing options\n" +" -V, --version output version information and exit\n" +"\n" +" --blockdev [driver=][,node-name=][,discard=ignore|unmap]\n" +" [,cache.direct=on|off][,cache.no-flush=on|off]\n" +" [,read-only=on|off][,auto-read-only=on|off]\n" +" [,force-share=on|off][,detect-zeroes=on|off|unmap]\n" +" [,driver specific parameters...]\n" +" configure a block backend\n" +"\n" +" --chardev configure a character device backend\n" +" (see the qemu(1) man page for possible options)\n" +"\n" +" --export [type=]nbd,device=[,name=]\n" +" [,writable=on|off][,bitmap=]\n" +" export the specified block node over NBD\n" +" (requires --nbd-server)\n" +"\n" +" --monitor [chardev=]name[,mode=control][,pretty[=on|off]]\n" +" configure a QMP monitor\n" +"\n" +" --nbd-server addr.type=inet,addr.host=,addr.port=\n" +" [,tls-creds=][,tls-authz=]\n" +" --nbd-server addr.type=unix,addr.path=\n" +" [,tls-creds=][,tls-authz=]\n" +" start an NBD server for exporting block nodes\n" +"\n" +" --object help list object types that can be added\n" +" --object ,help list properties for the given object type\n" +" --object [,=...]\n" +" create a new object of type , setting\n" +" properties in the order they are specified. Note\n" +" that the 'id' property must be set.\n" +" See the qemu(1) man page for documentation of the\n" +" objects that can be added.\n" +"\n" +QEMU_HELP_BOTTOM "\n", + error_get_progname()); +} + +enum { + OPTION_BLOCKDEV = 256, + OPTION_CHARDEV, + OPTION_EXPORT, + OPTION_MONITOR, + OPTION_NBD_SERVER, + OPTION_OBJECT, +}; + +extern QemuOptsList qemu_chardev_opts; + +static QemuOptsList qemu_object_opts = { + .name = "object", + .implied_opt_name = "qom-type", + .head = QTAILQ_HEAD_INITIALIZER(qemu_object_opts.head), + .desc = { + { } + }, +}; + +static void init_qmp_commands(void) +{ + qmp_init_marshal(&qmp_commands); + qmp_register_command(&qmp_commands, "query-qmp-schema", + qmp_query_qmp_schema, QCO_ALLOW_PRECONFIG); + + QTAILQ_INIT(&qmp_cap_negotiation_commands); + qmp_register_command(&qmp_cap_negotiation_commands, "qmp_capabilities", + qmp_marshal_qmp_capabilities, QCO_ALLOW_PRECONFIG); +} + +static void init_export(BlockExport *export, Error **errp) +{ + switch (export->type) { + case BLOCK_EXPORT_TYPE_NBD: + qmp_nbd_server_add(&export->u.nbd, errp); + break; + default: + g_assert_not_reached(); + } +} + +static void process_options(int argc, char *argv[]) +{ + int c; + + static const struct option long_options[] = { + {"blockdev", required_argument, NULL, OPTION_BLOCKDEV}, + {"chardev", required_argument, NULL, OPTION_CHARDEV}, + {"export", required_argument, NULL, OPTION_EXPORT}, + {"help", no_argument, NULL, 'h'}, + {"monitor", required_argument, NULL, OPTION_MONITOR}, + {"nbd-server", required_argument, NULL, OPTION_NBD_SERVER}, + {"object", required_argument, NULL, OPTION_OBJECT}, + {"trace", required_argument, NULL, 'T'}, + {"version", no_argument, NULL, 'V'}, + {0, 0, 0, 0} + }; + + /* + * In contrast to the system emulator, options are processed in the order + * they are given on the command lines. This means that things must be + * defined first before they can be referenced in another option. + */ + while ((c = getopt_long(argc, argv, "hT:V", long_options, NULL)) != -1) { + switch (c) { + case '?': + exit(EXIT_FAILURE); + case 'h': + help(); + exit(EXIT_SUCCESS); + case 'T': + { + char *trace_file = trace_opt_parse(optarg); + trace_init_file(trace_file); + g_free(trace_file); + break; + } + case 'V': + printf("qemu-storage-daemon version " + QEMU_FULL_VERSION "\n" QEMU_COPYRIGHT "\n"); + exit(EXIT_SUCCESS); + case OPTION_BLOCKDEV: + { + Visitor *v; + BlockdevOptions *options; + + v = qobject_input_visitor_new_str(optarg, "driver", + &error_fatal); + + visit_type_BlockdevOptions(v, NULL, &options, &error_fatal); + visit_free(v); + + qmp_blockdev_add(options, &error_fatal); + qapi_free_BlockdevOptions(options); + break; + } + case OPTION_CHARDEV: + { + /* TODO This interface is not stable until we QAPIfy it */ + QemuOpts *opts = qemu_opts_parse_noisily(&qemu_chardev_opts, + optarg, true); + if (opts == NULL) { + exit(EXIT_FAILURE); + } + + if (!qemu_chr_new_from_opts(opts, NULL, &error_fatal)) { + /* No error, but NULL returned means help was printed */ + exit(EXIT_SUCCESS); + } + qemu_opts_del(opts); + break; + } + case OPTION_EXPORT: + { + Visitor *v; + BlockExport *export; + + v = qobject_input_visitor_new_str(optarg, "type", &error_fatal); + visit_type_BlockExport(v, NULL, &export, &error_fatal); + visit_free(v); + + init_export(export, &error_fatal); + qapi_free_BlockExport(export); + break; + } + case OPTION_MONITOR: + { + Visitor *v; + MonitorOptions *monitor; + + v = qobject_input_visitor_new_str(optarg, "chardev", + &error_fatal); + visit_type_MonitorOptions(v, NULL, &monitor, &error_fatal); + visit_free(v); + + /* TODO Catch duplicate monitor IDs */ + monitor_init(monitor, false, &error_fatal); + qapi_free_MonitorOptions(monitor); + break; + } + case OPTION_NBD_SERVER: + { + Visitor *v; + NbdServerOptions *options; + + v = qobject_input_visitor_new_str(optarg, NULL, &error_fatal); + visit_type_NbdServerOptions(v, NULL, &options, &error_fatal); + visit_free(v); + + nbd_server_start_options(options, &error_fatal); + qapi_free_NbdServerOptions(options); + break; + } + case OPTION_OBJECT: + { + QemuOpts *opts; + const char *type; + QDict *args; + QObject *ret_data = NULL; + + /* FIXME The keyval parser rejects 'help' arguments, so we must + * unconditionall try QemuOpts first. */ + opts = qemu_opts_parse(&qemu_object_opts, + optarg, true, &error_fatal); + type = qemu_opt_get(opts, "qom-type"); + if (type && user_creatable_print_help(type, opts)) { + exit(EXIT_SUCCESS); + } + qemu_opts_del(opts); + + args = keyval_parse(optarg, "qom-type", &error_fatal); + qmp_object_add(args, &ret_data, &error_fatal); + qobject_unref(args); + qobject_unref(ret_data); + break; + } + default: + g_assert_not_reached(); + } + } + if (optind != argc) { + error_report("Unexpected argument: %s", argv[optind]); + exit(EXIT_FAILURE); + } +} + +int main(int argc, char *argv[]) +{ +#ifdef CONFIG_POSIX + signal(SIGPIPE, SIG_IGN); +#endif + + error_init(argv[0]); + qemu_init_exec_dir(argv[0]); + os_setup_signal_handling(); + + module_call_init(MODULE_INIT_QOM); + module_call_init(MODULE_INIT_TRACE); + qemu_add_opts(&qemu_trace_opts); + qcrypto_init(&error_fatal); + bdrv_init(); + monitor_init_globals_core(); + init_qmp_commands(); + + if (!trace_init_backends()) { + return EXIT_FAILURE; + } + qemu_set_log(LOG_TRACE); + + qemu_init_main_loop(&error_fatal); + process_options(argc, argv); + + while (!exit_requested) { + main_loop_wait(false); + } + + return EXIT_SUCCESS; +} diff --git a/qemu-tech.texi b/qemu-tech.texi deleted file mode 100644 index 0380de77b6..0000000000 --- a/qemu-tech.texi +++ /dev/null @@ -1,195 +0,0 @@ -@node Implementation notes -@appendix Implementation notes - -@menu -* CPU emulation:: -* Managed start up options:: -@end menu - -@node CPU emulation -@section CPU emulation - -@menu -* x86:: x86 and x86-64 emulation -* ARM:: ARM emulation -* MIPS:: MIPS emulation -* PPC:: PowerPC emulation -* SPARC:: Sparc32 and Sparc64 emulation -* Xtensa:: Xtensa emulation -@end menu - -@node x86 -@subsection x86 and x86-64 emulation - -QEMU x86 target features: - -@itemize - -@item The virtual x86 CPU supports 16 bit and 32 bit addressing with segmentation. -LDT/GDT and IDT are emulated. VM86 mode is also supported to run -DOSEMU. There is some support for MMX/3DNow!, SSE, SSE2, SSE3, SSSE3, -and SSE4 as well as x86-64 SVM. - -@item Support of host page sizes bigger than 4KB in user mode emulation. - -@item QEMU can emulate itself on x86. - -@item An extensive Linux x86 CPU test program is included @file{tests/test-i386}. -It can be used to test other x86 virtual CPUs. - -@end itemize - -Current QEMU limitations: - -@itemize - -@item Limited x86-64 support. - -@item IPC syscalls are missing. - -@item The x86 segment limits and access rights are not tested at every -memory access (yet). Hopefully, very few OSes seem to rely on that for -normal use. - -@end itemize - -@node ARM -@subsection ARM emulation - -@itemize - -@item Full ARM 7 user emulation. - -@item NWFPE FPU support included in user Linux emulation. - -@item Can run most ARM Linux binaries. - -@end itemize - -@node MIPS -@subsection MIPS emulation - -@itemize - -@item The system emulation allows full MIPS32/MIPS64 Release 2 emulation, -including privileged instructions, FPU and MMU, in both little and big -endian modes. - -@item The Linux userland emulation can run many 32 bit MIPS Linux binaries. - -@end itemize - -Current QEMU limitations: - -@itemize - -@item Self-modifying code is not always handled correctly. - -@item 64 bit userland emulation is not implemented. - -@item The system emulation is not complete enough to run real firmware. - -@item The watchpoint debug facility is not implemented. - -@end itemize - -@node PPC -@subsection PowerPC emulation - -@itemize - -@item Full PowerPC 32 bit emulation, including privileged instructions, -FPU and MMU. - -@item Can run most PowerPC Linux binaries. - -@end itemize - -@node SPARC -@subsection Sparc32 and Sparc64 emulation - -@itemize - -@item Full SPARC V8 emulation, including privileged -instructions, FPU and MMU. SPARC V9 emulation includes most privileged -and VIS instructions, FPU and I/D MMU. Alignment is fully enforced. - -@item Can run most 32-bit SPARC Linux binaries, SPARC32PLUS Linux binaries and -some 64-bit SPARC Linux binaries. - -@end itemize - -Current QEMU limitations: - -@itemize - -@item IPC syscalls are missing. - -@item Floating point exception support is buggy. - -@item Atomic instructions are not correctly implemented. - -@item There are still some problems with Sparc64 emulators. - -@end itemize - -@node Xtensa -@subsection Xtensa emulation - -@itemize - -@item Core Xtensa ISA emulation, including most options: code density, -loop, extended L32R, 16- and 32-bit multiplication, 32-bit division, -MAC16, miscellaneous operations, boolean, FP coprocessor, coprocessor -context, debug, multiprocessor synchronization, -conditional store, exceptions, relocatable vectors, unaligned exception, -interrupts (including high priority and timer), hardware alignment, -region protection, region translation, MMU, windowed registers, thread -pointer, processor ID. - -@item Not implemented options: data/instruction cache (including cache -prefetch and locking), XLMI, processor interface. Also options not -covered by the core ISA (e.g. FLIX, wide branches) are not implemented. - -@item Can run most Xtensa Linux binaries. - -@item New core configuration that requires no additional instructions -may be created from overlay with minimal amount of hand-written code. - -@end itemize - -@node Managed start up options -@section Managed start up options - -In system mode emulation, it's possible to create a VM in a paused state using -the -S command line option. In this state the machine is completely initialized -according to command line options and ready to execute VM code but VCPU threads -are not executing any code. The VM state in this paused state depends on the way -QEMU was started. It could be in: -@table @asis -@item initial state (after reset/power on state) -@item with direct kernel loading, the initial state could be amended to execute -code loaded by QEMU in the VM's RAM and with incoming migration -@item with incoming migration, initial state will by amended with the migrated -machine state after migration completes. -@end table - -This paused state is typically used by users to query machine state and/or -additionally configure the machine (by hotplugging devices) in runtime before -allowing VM code to run. - -However, at the -S pause point, it's impossible to configure options that affect -initial VM creation (like: -smp/-m/-numa ...) or cold plug devices. The -experimental --preconfig command line option allows pausing QEMU -before the initial VM creation, in a ``preconfig'' state, where additional -queries and configuration can be performed via QMP before moving on to -the resulting configuration startup. In the preconfig state, QEMU only allows -a limited set of commands over the QMP monitor, where the commands do not -depend on an initialized machine, including but not limited to: -@table @asis -@item qmp_capabilities -@item query-qmp-schema -@item query-commands -@item query-status -@item x-exit-preconfig -@end table diff --git a/qemu.nsi b/qemu.nsi index 0c29ba359c..1a0112265b 100644 --- a/qemu.nsi +++ b/qemu.nsi @@ -177,9 +177,20 @@ SectionEnd !ifdef CONFIG_DOCUMENTATION Section "Documentation" SectionDoc SetOutPath "$INSTDIR" - File "${BINDIR}\qemu-doc.html" + File "${BINDIR}\index.html" + SetOutPath "$INSTDIR\interop" + FILE /r "${BINDIR}\interop\*.*" + SetOutPath "$INSTDIR\specs" + FILE /r "${BINDIR}\specs\*.*" + SetOutPath "$INSTDIR\system" + FILE /r "${BINDIR}\system\*.*" + SetOutPath "$INSTDIR\tools" + FILE /r "${BINDIR}\tools\*.*" + SetOutPath "$INSTDIR\user" + FILE /r "${BINDIR}\user\*.*" + SetOutPath "$INSTDIR" CreateDirectory "$SMPROGRAMS\${PRODUCT}" - CreateShortCut "$SMPROGRAMS\${PRODUCT}\User Documentation.lnk" "$INSTDIR\qemu-doc.html" "" "$INSTDIR\qemu-doc.html" 0 + CreateShortCut "$SMPROGRAMS\${PRODUCT}\User Documentation.lnk" "$INSTDIR\index.html" "" "$INSTDIR\index.html" 0 SectionEnd !endif @@ -227,7 +238,12 @@ Section "Uninstall" Delete "$INSTDIR\qemu-io.exe" Delete "$INSTDIR\qemu.exe" Delete "$INSTDIR\qemu-system-*.exe" - Delete "$INSTDIR\qemu-doc.html" + Delete "$INSTDIR\index.html" + RMDir /r "$INSTDIR\interop" + RMDir /r "$INSTDIR\specs" + RMDir /r "$INSTDIR\system" + RMDir /r "$INSTDIR\tools" + RMDir /r "$INSTDIR\user" RMDir /r "$INSTDIR\keymaps" RMDir /r "$INSTDIR\share" ; Remove generated files diff --git a/qga/Makefile.objs b/qga/Makefile.objs index 80e6bb3c2e..9c558ae51c 100644 --- a/qga/Makefile.objs +++ b/qga/Makefile.objs @@ -5,5 +5,6 @@ qga-obj-$(CONFIG_WIN32) += commands-win32.o channel-win32.o service-win32.o qga-obj-$(CONFIG_WIN32) += vss-win32.o qga-obj-y += qapi-generated/qga-qapi-types.o qapi-generated/qga-qapi-visit.o qga-obj-y += qapi-generated/qga-qapi-commands.o +qga-obj-y += qapi-generated/qga-qapi-init-commands.o qga-vss-dll-obj-$(CONFIG_QGA_VSS) += vss-win32/ diff --git a/qga/channel-win32.c b/qga/channel-win32.c index c86f4388db..4f04868a76 100644 --- a/qga/channel-win32.c +++ b/qga/channel-win32.c @@ -302,13 +302,14 @@ static gboolean ga_channel_open(GAChannel *c, GAChannelMethod method, OPEN_EXISTING, FILE_FLAG_NO_BUFFERING | FILE_FLAG_OVERLAPPED, NULL); if (c->handle == INVALID_HANDLE_VALUE) { - g_critical("error opening path %s: %s", newpath, - g_win32_error_message(GetLastError())); + g_autofree gchar *emsg = g_win32_error_message(GetLastError()); + g_critical("error opening path %s: %s", newpath, emsg); return false; } if (method == GA_CHANNEL_ISA_SERIAL && !SetCommTimeouts(c->handle,&comTimeOut)) { - g_critical("error setting timeout for com port: %lu",GetLastError()); + g_autofree gchar *emsg = g_win32_error_message(GetLastError()); + g_critical("error setting timeout for com port: %s", emsg); CloseHandle(c->handle); return false; } diff --git a/qga/commands-common.h b/qga/commands-common.h new file mode 100644 index 0000000000..90785ed4bb --- /dev/null +++ b/qga/commands-common.h @@ -0,0 +1,21 @@ +/* + * QEMU Guest Agent common/cross-platform common commands + * + * Copyright (c) 2020 Red Hat, Inc. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ +#ifndef QGA_COMMANDS_COMMON_H +#define QGA_COMMANDS_COMMON_H + +#include "qga-qapi-types.h" + +typedef struct GuestFileHandle GuestFileHandle; + +GuestFileHandle *guest_file_handle_find(int64_t id, Error **errp); + +GuestFileRead *guest_file_read_unsafe(GuestFileHandle *gfh, + int64_t count, Error **errp); + +#endif diff --git a/qga/commands-posix.c b/qga/commands-posix.c index 1c1a165dae..a52af0315f 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -26,6 +26,7 @@ #include "qemu/sockets.h" #include "qemu/base64.h" #include "qemu/cutils.h" +#include "commands-common.h" #ifdef HAVE_UTMPX #include @@ -156,6 +157,17 @@ void qmp_guest_set_time(bool has_time, int64_t time_ns, Error **errp) pid_t pid; Error *local_err = NULL; struct timeval tv; + static const char hwclock_path[] = "/sbin/hwclock"; + static int hwclock_available = -1; + + if (hwclock_available < 0) { + hwclock_available = (access(hwclock_path, X_OK) == 0); + } + + if (!hwclock_available) { + error_setg(errp, QERR_UNSUPPORTED); + return; + } /* If user has passed a time, validate and set it. */ if (has_time) { @@ -195,7 +207,7 @@ void qmp_guest_set_time(bool has_time, int64_t time_ns, Error **errp) /* Use '/sbin/hwclock -w' to set RTC from the system time, * or '/sbin/hwclock -s' to set the system time from RTC. */ - execle("/sbin/hwclock", "hwclock", has_time ? "-w" : "-s", + execle(hwclock_path, "hwclock", has_time ? "-w" : "-s", NULL, environ); _exit(EXIT_FAILURE); } else if (pid < 0) { @@ -226,12 +238,12 @@ typedef enum { RW_STATE_WRITING, } RwState; -typedef struct GuestFileHandle { +struct GuestFileHandle { uint64_t id; FILE *fh; RwState state; QTAILQ_ENTRY(GuestFileHandle) next; -} GuestFileHandle; +}; static struct { QTAILQ_HEAD(, GuestFileHandle) filehandles; @@ -257,7 +269,7 @@ static int64_t guest_file_handle_add(FILE *fh, Error **errp) return handle; } -static GuestFileHandle *guest_file_handle_find(int64_t id, Error **errp) +GuestFileHandle *guest_file_handle_find(int64_t id, Error **errp) { GuestFileHandle *gfh; @@ -449,29 +461,14 @@ void qmp_guest_file_close(int64_t handle, Error **errp) g_free(gfh); } -struct GuestFileRead *qmp_guest_file_read(int64_t handle, bool has_count, - int64_t count, Error **errp) +GuestFileRead *guest_file_read_unsafe(GuestFileHandle *gfh, + int64_t count, Error **errp) { - GuestFileHandle *gfh = guest_file_handle_find(handle, errp); GuestFileRead *read_data = NULL; guchar *buf; - FILE *fh; + FILE *fh = gfh->fh; size_t read_count; - if (!gfh) { - return NULL; - } - - if (!has_count) { - count = QGA_READ_COUNT_DEFAULT; - } else if (count < 0 || count >= UINT32_MAX) { - error_setg(errp, "value '%" PRId64 "' is invalid for argument count", - count); - return NULL; - } - - fh = gfh->fh; - /* explicitly flush when switching from writing to reading */ if (gfh->state == RW_STATE_WRITING) { int ret = fflush(fh); @@ -486,7 +483,6 @@ struct GuestFileRead *qmp_guest_file_read(int64_t handle, bool has_count, read_count = fread(buf, 1, count, fh); if (ferror(fh)) { error_setg_errno(errp, errno, "failed to read file"); - slog("guest-file-read failed, handle: %" PRId64, handle); } else { buf[read_count] = 0; read_data = g_new0(GuestFileRead, 1); @@ -1049,6 +1045,7 @@ static void build_guest_fsinfo_for_virtual_device(char const *syspath, GuestFilesystemInfo *fs, Error **errp) { + Error *err = NULL; DIR *dir; char *dirpath; struct dirent *entry; @@ -1078,10 +1075,11 @@ static void build_guest_fsinfo_for_virtual_device(char const *syspath, g_debug(" slave device '%s'", entry->d_name); path = g_strdup_printf("%s/slaves/%s", syspath, entry->d_name); - build_guest_fsinfo_for_device(path, fs, errp); + build_guest_fsinfo_for_device(path, fs, &err); g_free(path); - if (*errp) { + if (err) { + error_propagate(errp, err); break; } } @@ -1760,6 +1758,7 @@ static void guest_suspend(SuspendMode mode, Error **errp) } error_free(local_err); + local_err = NULL; if (pmutils_supports_mode(mode, &local_err)) { mode_supported = true; @@ -1771,6 +1770,7 @@ static void guest_suspend(SuspendMode mode, Error **errp) } error_free(local_err); + local_err = NULL; if (linux_sys_state_supports_mode(mode, &local_err)) { mode_supported = true; @@ -1778,6 +1778,7 @@ static void guest_suspend(SuspendMode mode, Error **errp) } if (!mode_supported) { + error_free(local_err); error_setg(errp, "the requested suspend mode is not supported by the guest"); } else { @@ -2781,7 +2782,7 @@ static double ga_get_login_time(struct utmpx *user_info) return seconds + useconds; } -GuestUserList *qmp_guest_get_users(Error **err) +GuestUserList *qmp_guest_get_users(Error **errp) { GHashTable *cache = NULL; GuestUserList *head = NULL, *cur_item = NULL; diff --git a/qga/commands-win32.c b/qga/commands-win32.c index 55ba5b263a..9717a8d52d 100644 --- a/qga/commands-win32.c +++ b/qga/commands-win32.c @@ -37,6 +37,7 @@ #include "qemu/queue.h" #include "qemu/host-utils.h" #include "qemu/base64.h" +#include "commands-common.h" #ifndef SHTDN_REASON_FLAG_PLANNED #define SHTDN_REASON_FLAG_PLANNED 0x80000000 @@ -50,11 +51,11 @@ #define INVALID_SET_FILE_POINTER ((DWORD)-1) -typedef struct GuestFileHandle { +struct GuestFileHandle { int64_t id; HANDLE fh; QTAILQ_ENTRY(GuestFileHandle) next; -} GuestFileHandle; +}; static struct { QTAILQ_HEAD(, GuestFileHandle) filehandles; @@ -126,7 +127,7 @@ static int64_t guest_file_handle_add(HANDLE fh, Error **errp) return handle; } -static GuestFileHandle *guest_file_handle_find(int64_t id, Error **errp) +GuestFileHandle *guest_file_handle_find(int64_t id, Error **errp) { GuestFileHandle *gfh; QTAILQ_FOREACH(gfh, &guest_file_state.filehandles, next) { @@ -315,38 +316,25 @@ void qmp_guest_shutdown(bool has_mode, const char *mode, Error **errp) } if (!ExitWindowsEx(shutdown_flag, SHTDN_REASON_FLAG_PLANNED)) { - slog("guest-shutdown failed: %lu", GetLastError()); - error_setg(errp, QERR_UNDEFINED_ERROR); + g_autofree gchar *emsg = g_win32_error_message(GetLastError()); + slog("guest-shutdown failed: %s", emsg); + error_setg_win32(errp, GetLastError(), "guest-shutdown failed"); } } -GuestFileRead *qmp_guest_file_read(int64_t handle, bool has_count, - int64_t count, Error **errp) +GuestFileRead *guest_file_read_unsafe(GuestFileHandle *gfh, + int64_t count, Error **errp) { GuestFileRead *read_data = NULL; guchar *buf; - HANDLE fh; + HANDLE fh = gfh->fh; bool is_ok; DWORD read_count; - GuestFileHandle *gfh = guest_file_handle_find(handle, errp); - if (!gfh) { - return NULL; - } - if (!has_count) { - count = QGA_READ_COUNT_DEFAULT; - } else if (count < 0 || count >= UINT32_MAX) { - error_setg(errp, "value '%" PRId64 - "' is invalid for argument count", count); - return NULL; - } - - fh = gfh->fh; - buf = g_malloc0(count+1); + buf = g_malloc0(count + 1); is_ok = ReadFile(fh, buf, count, &read_count, NULL); if (!is_ok) { error_setg_win32(errp, GetLastError(), "failed to read file"); - slog("guest-file-read failed, handle %" PRId64, handle); } else { buf[read_count] = 0; read_data = g_new0(GuestFileRead, 1); @@ -1319,7 +1307,8 @@ static DWORD WINAPI do_suspend(LPVOID opaque) DWORD ret = 0; if (!SetSuspendState(*mode == GUEST_SUSPEND_MODE_DISK, TRUE, TRUE)) { - slog("failed to suspend guest, %lu", GetLastError()); + g_autofree gchar *emsg = g_win32_error_message(GetLastError()); + slog("failed to suspend guest: %s", emsg); ret = -1; } g_free(mode); @@ -1946,7 +1935,7 @@ typedef struct _GA_WTSINFOA { } GA_WTSINFOA; -GuestUserList *qmp_guest_get_users(Error **err) +GuestUserList *qmp_guest_get_users(Error **errp) { #define QGA_NANOSECONDS 10000000 diff --git a/qga/commands.c b/qga/commands.c index 0c7d1385c2..efc8b90281 100644 --- a/qga/commands.c +++ b/qga/commands.c @@ -11,6 +11,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "guest-agent-core.h" #include "qga-qapi-commands.h" #include "qapi/error.h" @@ -18,11 +19,18 @@ #include "qemu/base64.h" #include "qemu/cutils.h" #include "qemu/atomic.h" +#include "commands-common.h" /* Maximum captured guest-exec out_data/err_data - 16MB */ #define GUEST_EXEC_MAX_OUTPUT (16*1024*1024) /* Allocation and I/O buffer for reading guest-exec out_data/err_data - 4KB */ #define GUEST_EXEC_IO_SIZE (4*1024) +/* + * Maximum file size to read - 48MB + * + * (48MB + Base64 3:4 overhead = JSON parser 64 MB limit) + */ +#define GUEST_FILE_READ_COUNT_MAX (48 * MiB) /* Note: in some situations, like with the fsfreeze, logging may be * temporarilly disabled. if it is necessary that a command be able @@ -54,7 +62,7 @@ void qmp_guest_ping(Error **errp) slog("guest-ping called"); } -static void qmp_command_info(QmpCommand *cmd, void *opaque) +static void qmp_command_info(const QmpCommand *cmd, void *opaque) { GuestAgentInfo *info = opaque; GuestAgentCommandInfo *cmd_info; @@ -143,7 +151,7 @@ static GuestExecInfo *guest_exec_info_find(int64_t pid_numeric) return NULL; } -GuestExecStatus *qmp_guest_exec_status(int64_t pid, Error **err) +GuestExecStatus *qmp_guest_exec_status(int64_t pid, Error **errp) { GuestExecInfo *gei; GuestExecStatus *ges; @@ -152,7 +160,7 @@ GuestExecStatus *qmp_guest_exec_status(int64_t pid, Error **err) gei = guest_exec_info_find(pid); if (gei == NULL) { - error_setg(err, QERR_INVALID_PARAMETER, "pid"); + error_setg(errp, QERR_INVALID_PARAMETER, "pid"); return NULL; } @@ -385,7 +393,7 @@ GuestExec *qmp_guest_exec(const char *path, bool has_env, strList *env, bool has_input_data, const char *input_data, bool has_capture_output, bool capture_output, - Error **err) + Error **errp) { GPid pid; GuestExec *ge = NULL; @@ -405,7 +413,7 @@ GuestExec *qmp_guest_exec(const char *path, arglist.next = has_arg ? arg : NULL; if (has_input_data) { - input = qbase64_decode(input_data, -1, &ninput, err); + input = qbase64_decode(input_data, -1, &ninput, errp); if (!input) { return NULL; } @@ -424,7 +432,7 @@ GuestExec *qmp_guest_exec(const char *path, guest_exec_task_setup, NULL, &pid, has_input_data ? &in_fd : NULL, has_output ? &out_fd : NULL, has_output ? &err_fd : NULL, &gerr); if (!ret) { - error_setg(err, QERR_QGA_COMMAND_FAILED, gerr->message); + error_setg(errp, QERR_QGA_COMMAND_FAILED, gerr->message); g_error_free(gerr); goto done; } @@ -482,10 +490,15 @@ done: * the guest's SEEK_ constants. */ int ga_parse_whence(GuestFileWhence *whence, Error **errp) { - /* Exploit the fact that we picked values to match QGA_SEEK_*. */ + /* + * Exploit the fact that we picked values to match QGA_SEEK_*; + * however, we have to use a temporary variable since the union + * members may have different size. + */ if (whence->type == QTYPE_QSTRING) { + int value = whence->u.name; whence->type = QTYPE_QNUM; - whence->u.value = whence->u.name; + whence->u.value = value; } switch (whence->u.value) { case QGA_SEEK_SET: @@ -499,7 +512,7 @@ int ga_parse_whence(GuestFileWhence *whence, Error **errp) return -1; } -GuestHostName *qmp_guest_get_host_name(Error **err) +GuestHostName *qmp_guest_get_host_name(Error **errp) { GuestHostName *result = NULL; gchar const *hostname = g_get_host_name(); @@ -542,3 +555,28 @@ error: g_free(info); return NULL; } + +GuestFileRead *qmp_guest_file_read(int64_t handle, bool has_count, + int64_t count, Error **errp) +{ + GuestFileHandle *gfh = guest_file_handle_find(handle, errp); + GuestFileRead *read_data; + + if (!gfh) { + return NULL; + } + if (!has_count) { + count = QGA_READ_COUNT_DEFAULT; + } else if (count < 0 || count > GUEST_FILE_READ_COUNT_MAX) { + error_setg(errp, "value '%" PRId64 "' is invalid for argument count", + count); + return NULL; + } + + read_data = guest_file_read_unsafe(gfh, count, errp); + if (!read_data) { + slog("guest-file-write failed, handle: %" PRId64, handle); + } + + return read_data; +} diff --git a/qga/installer/qemu-ga.wxs b/qga/installer/qemu-ga.wxs index 64bf90bd85..f6781752e6 100644 --- a/qga/installer/qemu-ga.wxs +++ b/qga/installer/qemu-ga.wxs @@ -81,7 +81,7 @@ Arguments="-d --retry-path" > - + diff --git a/qga/main.c b/qga/main.c index c35c2a2120..f0e454f28d 100644 --- a/qga/main.c +++ b/qga/main.c @@ -24,7 +24,7 @@ #include "qapi/qmp/qjson.h" #include "qapi/qmp/qstring.h" #include "guest-agent-core.h" -#include "qga-qapi-commands.h" +#include "qga-qapi-init-commands.h" #include "qapi/qmp/qerror.h" #include "qapi/error.h" #include "channel.h" @@ -234,7 +234,9 @@ QEMU_COPYRIGHT "\n" " -p, --path device/socket path (the default for virtio-serial is:\n" " %s,\n" " the default for isa-serial is:\n" -" %s)\n" +" %s).\n" +" Socket addresses for vsock-listen are written as\n" +" :.\n" " -l, --logfile set logfile path, logs to stderr by default\n" " -f, --pidfile specify pidfile (default is %s)\n" #ifdef CONFIG_FSFREEZE @@ -359,7 +361,7 @@ static gint ga_strcmp(gconstpointer str1, gconstpointer str2) } /* disable commands that aren't safe for fsfreeze */ -static void ga_disable_non_whitelisted(QmpCommand *cmd, void *opaque) +static void ga_disable_non_whitelisted(const QmpCommand *cmd, void *opaque) { bool whitelisted = false; int i = 0; @@ -378,7 +380,7 @@ static void ga_disable_non_whitelisted(QmpCommand *cmd, void *opaque) } /* [re-]enable all commands, except those explicitly blacklisted by user */ -static void ga_enable_non_blacklisted(QmpCommand *cmd, void *opaque) +static void ga_enable_non_blacklisted(const QmpCommand *cmd, void *opaque) { GList *blacklist = opaque; const char *name = qmp_command_name(cmd); @@ -918,7 +920,7 @@ int64_t ga_get_fd_handle(GAState *s, Error **errp) return handle; } -static void ga_print_cmd(QmpCommand *cmd, void *opaque) +static void ga_print_cmd(const QmpCommand *cmd, void *opaque) { printf("%s\n", qmp_command_name(cmd)); } diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json index fb4605cc19..4be9aad48e 100644 --- a/qga/qapi-schema.json +++ b/qga/qapi-schema.json @@ -266,11 +266,13 @@ ## # @guest-file-read: # -# Read from an open file in the guest. Data will be base64-encoded +# Read from an open file in the guest. Data will be base64-encoded. +# As this command is just for limited, ad-hoc debugging, such as log +# file access, the number of bytes to read is limited to 48 MB. # # @handle: filehandle returned by guest-file-open # -# @count: maximum number of bytes to read (default is 4KB) +# @count: maximum number of bytes to read (default is 4KB, maximum is 48MB) # # Returns: @GuestFileRead on success. # @@ -416,7 +418,7 @@ # Returns: GuestFsfreezeStatus ("thawed", "frozen", etc., as defined below) # # Note: This may fail to properly report the current state as a result of -# some other guest processes having issued an fs freeze/thaw. +# some other guest processes having issued an fs freeze/thaw. # # Since: 0.15.0 ## @@ -431,13 +433,13 @@ # unfreeze. # # Note: On Windows, the command is implemented with the help of a -# Volume Shadow-copy Service DLL helper. The frozen state is limited -# for up to 10 seconds by VSS. +# Volume Shadow-copy Service DLL helper. The frozen state is limited +# for up to 10 seconds by VSS. # # Returns: Number of file systems currently frozen. On error, all filesystems -# will be thawed. If no filesystems are frozen as a result of this call, -# then @guest-fsfreeze-status will remain "thawed" and calling -# @guest-fsfreeze-thaw is not necessary. +# will be thawed. If no filesystems are frozen as a result of this call, +# then @guest-fsfreeze-status will remain "thawed" and calling +# @guest-fsfreeze-thaw is not necessary. # # Since: 0.15.0 ## @@ -455,7 +457,7 @@ # Invalid mount points are ignored. # # Returns: Number of file systems currently frozen. On error, all filesystems -# will be thawed. +# will be thawed. # # Since: 2.2 ## @@ -510,13 +512,12 @@ # # Discard (or "trim") blocks which are not in use by the filesystem. # -# @minimum: -# Minimum contiguous free range to discard, in bytes. Free ranges -# smaller than this may be ignored (this is a hint and the guest -# may not respect it). By increasing this value, the fstrim -# operation will complete more quickly for filesystems with badly -# fragmented free space, although not all blocks will be discarded. -# The default value is zero, meaning "discard every free block". +# @minimum: Minimum contiguous free range to discard, in bytes. Free ranges +# smaller than this may be ignored (this is a hint and the guest +# may not respect it). By increasing this value, the fstrim +# operation will complete more quickly for filesystems with badly +# fragmented free space, although not all blocks will be discarded. +# The default value is zero, meaning "discard every free block". # # Returns: A @GuestFilesystemTrimResponse which contains the # status of all trimmed paths. (since 2.4) @@ -546,7 +547,8 @@ # (or set its status to "shutdown") due to other reasons. # # The following errors may be returned: -# If suspend to disk is not supported, Unsupported +# +# - If suspend to disk is not supported, Unsupported # # Notes: It's strongly recommended to issue the guest-sync command before # sending commands when the guest resumes @@ -575,12 +577,14 @@ # # This command does NOT return a response on success. There are two options # to check for success: -# 1. Wait for the SUSPEND QMP event from QEMU -# 2. Issue the query-status QMP command to confirm the VM status is -# "suspended" +# +# 1. Wait for the SUSPEND QMP event from QEMU +# 2. Issue the query-status QMP command to confirm the VM status is +# "suspended" # # The following errors may be returned: -# If suspend to ram is not supported, Unsupported +# +# - If suspend to ram is not supported, Unsupported # # Notes: It's strongly recommended to issue the guest-sync command before # sending commands when the guest resumes @@ -607,12 +611,14 @@ # # This command does NOT return a response on success. There are two options # to check for success: -# 1. Wait for the SUSPEND QMP event from QEMU -# 2. Issue the query-status QMP command to confirm the VM status is -# "suspended" +# +# 1. Wait for the SUSPEND QMP event from QEMU +# 2. Issue the query-status QMP command to confirm the VM status is +# "suspended" # # The following errors may be returned: -# If hybrid suspend is not supported, Unsupported +# +# - If hybrid suspend is not supported, Unsupported # # Notes: It's strongly recommended to issue the guest-sync command before # sending commands when the guest resumes @@ -693,7 +699,7 @@ # @ip-addresses: List of addresses assigned to @name # # @statistics: various statistic counters related to @name -# (since 2.11) +# (since 2.11) # # Since: 1.1 ## @@ -743,7 +749,7 @@ # This is a read-only operation. # # Returns: The list of all VCPUs the guest knows about. Each VCPU is put on the -# list exactly once, but their order is unspecified. +# list exactly once, but their order is unspecified. # # Since: 1.5 ## @@ -767,18 +773,22 @@ # Returns: The length of the initial sublist that has been successfully # processed. The guest agent maximizes this value. Possible cases: # -# - 0: if the @vcpus list was empty on input. Guest state -# has not been changed. Otherwise, -# - Error: processing the first node of @vcpus failed for the -# reason returned. Guest state has not been changed. -# Otherwise, -# - < length(@vcpus): more than zero initial nodes have been processed, -# but not the entire @vcpus list. Guest state has -# changed accordingly. To retrieve the error -# (assuming it persists), repeat the call with the -# successfully processed initial sublist removed. -# Otherwise, -# - length(@vcpus): call successful. +# - 0: +# if the @vcpus list was empty on input. Guest state +# has not been changed. Otherwise, +# - Error: +# processing the first node of @vcpus failed for the +# reason returned. Guest state has not been changed. +# Otherwise, +# - < length(@vcpus): +# more than zero initial nodes have been processed, +# but not the entire @vcpus list. Guest state has +# changed accordingly. To retrieve the error +# (assuming it persists), repeat the call with the +# successfully processed initial sublist removed. +# Otherwise, +# - length(@vcpus): +# call successful. # # Since: 1.5 ## @@ -809,7 +819,7 @@ # @sas: Win serial-attaches SCSI bus type # @mmc: Win multimedia card (MMC) bus type # @virtual: Win virtual bus type -# @file-backed virtual: Win file-backed bus type +# @file-backed-virtual: Win file-backed bus type # # Since: 2.2; 'Unknown' and all entries below since 2.4 ## @@ -937,8 +947,8 @@ # This is a read-only operation. # # Returns: The list of all memory blocks the guest knows about. -# Each memory block is put on the list exactly once, but their order -# is unspecified. +# Each memory block is put on the list exactly once, but their order +# is unspecified. # # Since: 2.3 ## @@ -971,9 +981,9 @@ # @response: the result of memory block operation. # # @error-code: the error number. -# When memory block operation fails, we assign the value of -# 'errno' to this member, it indicates what goes wrong. -# When the operation succeeds, it will be omitted. +# When memory block operation fails, we assign the value of +# 'errno' to this member, it indicates what goes wrong. +# When the operation succeeds, it will be omitted. # # Since: 2.3 ## @@ -1040,15 +1050,15 @@ # @exited: true if process has already terminated. # @exitcode: process exit code if it was normally terminated. # @signal: signal number (linux) or unhandled exception code -# (windows) if the process was abnormally terminated. +# (windows) if the process was abnormally terminated. # @out-data: base64-encoded stdout of the process # @err-data: base64-encoded stderr of the process -# Note: @out-data and @err-data are present only -# if 'capture-output' was specified for 'guest-exec' +# Note: @out-data and @err-data are present only +# if 'capture-output' was specified for 'guest-exec' # @out-truncated: true if stdout was not fully captured -# due to size limitation. +# due to size limitation. # @err-truncated: true if stderr was not fully captured -# due to size limitation. +# due to size limitation. # # Since: 2.5 ## @@ -1131,8 +1141,8 @@ ## # @GuestUser: -# @user: Username -# @domain: Logon domain (windows only) +# @user: Username +# @domain: Logon domain (windows only) # @login-time: Time of login of this user on the computer. If multiple # instances of the user are logged in, the earliest login time is # reported. The value is in fractional seconds since epoch time. @@ -1156,10 +1166,10 @@ ## # @GuestTimezone: # -# @zone: Timezone name. These values may differ depending on guest/OS and -# should only be used for informational purposes. -# @offset: Offset to UTC in seconds, negative numbers for time zones west of -# GMT, positive numbers for east +# @zone: Timezone name. These values may differ depending on guest/OS and +# should only be used for informational purposes. +# @offset: Offset to UTC in seconds, negative numbers for time zones west of +# GMT, positive numbers for east # # Since: 2.10 ## @@ -1182,35 +1192,35 @@ # @GuestOSInfo: # # @kernel-release: -# * POSIX: release field returned by uname(2) -# * Windows: build number of the OS +# * POSIX: release field returned by uname(2) +# * Windows: build number of the OS # @kernel-version: -# * POSIX: version field returned by uname(2) -# * Windows: version number of the OS +# * POSIX: version field returned by uname(2) +# * Windows: version number of the OS # @machine: -# * POSIX: machine field returned by uname(2) -# * Windows: one of x86, x86_64, arm, ia64 +# * POSIX: machine field returned by uname(2) +# * Windows: one of x86, x86_64, arm, ia64 # @id: -# * POSIX: as defined by os-release(5) -# * Windows: contains string "mswindows" +# * POSIX: as defined by os-release(5) +# * Windows: contains string "mswindows" # @name: -# * POSIX: as defined by os-release(5) -# * Windows: contains string "Microsoft Windows" +# * POSIX: as defined by os-release(5) +# * Windows: contains string "Microsoft Windows" # @pretty-name: -# * POSIX: as defined by os-release(5) -# * Windows: product name, e.g. "Microsoft Windows 10 Enterprise" +# * POSIX: as defined by os-release(5) +# * Windows: product name, e.g. "Microsoft Windows 10 Enterprise" # @version: -# * POSIX: as defined by os-release(5) -# * Windows: long version string, e.g. "Microsoft Windows Server 2008" +# * POSIX: as defined by os-release(5) +# * Windows: long version string, e.g. "Microsoft Windows Server 2008" # @version-id: -# * POSIX: as defined by os-release(5) -# * Windows: short version identifier, e.g. "7" or "20012r2" +# * POSIX: as defined by os-release(5) +# * Windows: short version identifier, e.g. "7" or "20012r2" # @variant: -# * POSIX: as defined by os-release(5) -# * Windows: contains string "server" or "client" +# * POSIX: as defined by os-release(5) +# * Windows: contains string "server" or "client" # @variant-id: -# * POSIX: as defined by os-release(5) -# * Windows: contains string "server" or "client" +# * POSIX: as defined by os-release(5) +# * Windows: contains string "server" or "client" # # Notes: # diff --git a/qga/vss-win32/Makefile.objs b/qga/vss-win32/Makefile.objs index fd3ba1896b..c82676aeb8 100644 --- a/qga/vss-win32/Makefile.objs +++ b/qga/vss-win32/Makefile.objs @@ -5,9 +5,9 @@ qga-vss-dll-obj-y += requester.o provider.o install.o obj-qga-vss-dll-obj-y = $(addprefix $(obj)/, $(qga-vss-dll-obj-y)) $(obj-qga-vss-dll-obj-y): QEMU_CXXFLAGS := $(filter-out -fstack-protector-all -fstack-protector-strong, $(QEMU_CXXFLAGS)) -Wno-unknown-pragmas -Wno-delete-non-virtual-dtor -$(obj)/qga-vss.dll: LDFLAGS = -shared -Wl,--add-stdcall-alias,--enable-stdcall-fixup -lglib-2.0 -lole32 -loleaut32 -lshlwapi -luuid -lintl -lws2_32 -static +QGA_VSS_LDFLAGS = -shared -Wl,--add-stdcall-alias,--enable-stdcall-fixup -lglib-2.0 -lole32 -loleaut32 -lshlwapi -luuid -lintl -lws2_32 -static $(obj)/qga-vss.dll: $(obj-qga-vss-dll-obj-y) $(SRC_PATH)/$(obj)/qga-vss.def - $(call quiet-command,$(CXX) -o $@ $(qga-vss-dll-obj-y) $(SRC_PATH)/qga/vss-win32/qga-vss.def $(CXXFLAGS) $(LDFLAGS),"LINK","$(TARGET_DIR)$@") + $(call quiet-command,$(CXX) -o $@ $(qga-vss-dll-obj-y) $(SRC_PATH)/qga/vss-win32/qga-vss.def $(CXXFLAGS) $(QGA_VSS_LDFLAGS),"LINK","$(TARGET_DIR)$@") # rules to build qga-provider.tlb diff --git a/qga/vss-win32/install.cpp b/qga/vss-win32/install.cpp index 6713e58670..a456841360 100644 --- a/qga/vss-win32/install.cpp +++ b/qga/vss-win32/install.cpp @@ -443,6 +443,17 @@ STDAPI DllRegisterServer(void) VSS_PROV_SOFTWARE, const_cast(QGA_PROVIDER_VERSION), g_gProviderVersion); + if (hr == (long int) VSS_E_PROVIDER_ALREADY_REGISTERED) { + DllUnregisterServer(); + hr = pVssAdmin->RegisterProvider(g_gProviderId, CLSID_QGAVSSProvider, + const_cast + (QGA_PROVIDER_LNAME), + VSS_PROV_SOFTWARE, + const_cast + (QGA_PROVIDER_VERSION), + g_gProviderVersion); + } + if (FAILED(hr)) { errmsg_dialog(hr, "RegisterProvider failed"); } diff --git a/qobject/json-streamer.c b/qobject/json-streamer.c index 47dd7ea576..b93d97b995 100644 --- a/qobject/json-streamer.c +++ b/qobject/json-streamer.c @@ -85,7 +85,7 @@ void json_message_process_token(JSONLexer *lexer, GString *input, g_queue_push_tail(&parser->tokens, token); if ((parser->brace_count > 0 || parser->bracket_count > 0) - && parser->bracket_count >= 0 && parser->bracket_count >= 0) { + && parser->brace_count >= 0 && parser->bracket_count >= 0) { return; } diff --git a/qobject/qstring.c b/qobject/qstring.c index 1c6897df00..b66a2c35f2 100644 --- a/qobject/qstring.c +++ b/qobject/qstring.c @@ -149,16 +149,33 @@ bool qstring_is_equal(const QObject *x, const QObject *y) qobject_to(QString, y)->string); } +/** + * qstring_free(): Free the memory allocated by a QString object + * + * Return: if @return_str, return the underlying string, to be + * g_free(), otherwise NULL is returned. + */ +char *qstring_free(QString *qstring, bool return_str) +{ + char *rv = NULL; + + if (return_str) { + rv = qstring->string; + } else { + g_free(qstring->string); + } + + g_free(qstring); + + return rv; +} + /** * qstring_destroy_obj(): Free all memory allocated by a QString * object */ void qstring_destroy_obj(QObject *obj) { - QString *qs; - assert(obj != NULL); - qs = qobject_to(QString, obj); - g_free(qs->string); - g_free(qs); + qstring_free(qobject_to(QString, obj), FALSE); } diff --git a/qom/Makefile.objs b/qom/Makefile.objs index f9d77350ac..1b45d104ba 100644 --- a/qom/Makefile.objs +++ b/qom/Makefile.objs @@ -2,3 +2,4 @@ qom-obj-y = object.o container.o qom-qobject.o qom-obj-y += object_interfaces.o common-obj-$(CONFIG_SOFTMMU) += qom-hmp-cmds.o qom-qmp-cmds.o +storage-daemon-obj-y += qom-qmp-cmds.o diff --git a/qom/object.c b/qom/object.c index d51b57fba1..1812f79224 100644 --- a/qom/object.c +++ b/qom/object.c @@ -19,8 +19,10 @@ #include "qapi/visitor.h" #include "qapi/string-input-visitor.h" #include "qapi/string-output-visitor.h" +#include "qapi/qobject-input-visitor.h" #include "qapi/qapi-builtin-visit.h" #include "qapi/qmp/qerror.h" +#include "qapi/qmp/qjson.h" #include "trace.h" /* TODO: replace QObject with a simpler visitor to avoid a dependency @@ -173,7 +175,11 @@ static TypeImpl *type_get_parent(TypeImpl *type) { if (!type->parent_type && type->parent) { type->parent_type = type_get_by_name(type->parent); - g_assert(type->parent_type != NULL); + if (!type->parent_type) { + fprintf(stderr, "Type '%s' is missing its parent '%s'\n", + type->name, type->parent); + abort(); + } } return type->parent_type; @@ -264,6 +270,10 @@ static void object_property_free(gpointer data) { ObjectProperty *prop = data; + if (prop->defval) { + qobject_unref(prop->defval); + prop->defval = NULL; + } g_free(prop->name); g_free(prop->type); g_free(prop->description); @@ -303,10 +313,11 @@ static void type_initialize(TypeImpl *ti) int i; g_assert(parent->class_size <= ti->class_size); + g_assert(parent->instance_size <= ti->instance_size); memcpy(ti->class, parent->class, parent->class_size); ti->class->interfaces = NULL; ti->class->properties = g_hash_table_new_full( - g_str_hash, g_str_equal, g_free, object_property_free); + g_str_hash, g_str_equal, NULL, object_property_free); for (e = parent->class->interfaces; e; e = e->next) { InterfaceClass *iface = e->data; @@ -317,6 +328,11 @@ static void type_initialize(TypeImpl *ti) for (i = 0; i < ti->num_interfaces; i++) { TypeImpl *t = type_get_by_name(ti->interfaces[i].typename); + if (!t) { + error_report("missing interface '%s' for object '%s'", + ti->interfaces[i].typename, parent->name); + abort(); + } for (e = ti->class->interfaces; e; e = e->next) { TypeImpl *target_type = OBJECT_CLASS(e->data)->type; @@ -333,7 +349,7 @@ static void type_initialize(TypeImpl *ti) } } else { ti->class->properties = g_hash_table_new_full( - g_str_hash, g_str_equal, g_free, object_property_free); + g_str_hash, g_str_equal, NULL, object_property_free); } ti->class->type = ti; @@ -414,10 +430,29 @@ void object_apply_global_props(Object *obj, const GPtrArray *props, Error **errp * Global property defaults * Slot 0: accelerator's global property defaults * Slot 1: machine's global property defaults + * Slot 2: global properties from legacy command line option * Each is a GPtrArray of of GlobalProperty. * Applied in order, later entries override earlier ones. */ -static GPtrArray *object_compat_props[2]; +static GPtrArray *object_compat_props[3]; + +/* + * Retrieve @GPtrArray for global property defined with options + * other than "-global". These are generally used for syntactic + * sugar and legacy command line options. + */ +void object_register_sugar_prop(const char *driver, const char *prop, const char *value) +{ + GlobalProperty *g; + if (!object_compat_props[2]) { + object_compat_props[2] = g_ptr_array_new(); + } + g = g_new0(GlobalProperty, 1); + g->driver = g_strdup(driver); + g->property = g_strdup(prop); + g->value = g_strdup(value); + g_ptr_array_add(object_compat_props[2], g); +} /* * Set machine's global property defaults to @compat_props. @@ -445,7 +480,20 @@ void object_apply_compat_props(Object *obj) for (i = 0; i < ARRAY_SIZE(object_compat_props); i++) { object_apply_global_props(obj, object_compat_props[i], - &error_abort); + i == 2 ? &error_fatal : &error_abort); + } +} + +static void object_class_property_init_all(Object *obj) +{ + ObjectPropertyIterator iter; + ObjectProperty *prop; + + object_class_property_iter_init(&iter, object_get_class(obj)); + while ((prop = object_property_iter_next(&iter))) { + if (prop->init) { + prop->init(obj, prop); + } } } @@ -462,6 +510,7 @@ static void object_initialize_with_type(void *data, size_t size, TypeImpl *type) memset(obj, 0, type->instance_size); obj->class = type->class; object_ref(obj); + object_class_property_init_all(obj); obj->properties = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, object_property_free); object_init_with_type(obj, type); @@ -543,23 +592,22 @@ static inline bool object_property_is_child(ObjectProperty *prop) static void object_property_del_all(Object *obj) { + g_autoptr(GHashTable) done = g_hash_table_new(NULL, NULL); ObjectProperty *prop; - GHashTableIter iter; - gpointer key, value; + ObjectPropertyIterator iter; bool released; do { released = false; - g_hash_table_iter_init(&iter, obj->properties); - while (g_hash_table_iter_next(&iter, &key, &value)) { - prop = value; - if (prop->release) { - prop->release(obj, prop->name, prop->opaque); - prop->release = NULL; - released = true; - break; + object_property_iter_init(&iter, obj); + while ((prop = object_property_iter_next(&iter)) != NULL) { + if (g_hash_table_add(done, prop)) { + if (prop->release) { + prop->release(obj, prop->name, prop->opaque); + released = true; + break; + } } - g_hash_table_iter_remove(&iter); } } while (released); @@ -639,6 +687,11 @@ static Object *object_new_with_type(Type type) return obj; } +Object *object_new_with_class(ObjectClass *klass) +{ + return object_new_with_type(klass->type); +} + Object *object_new(const char *typename) { TypeImpl *ti = type_get_by_name(typename); @@ -1054,12 +1107,13 @@ GSList *object_class_get_list_sorted(const char *implements_type, object_class_cmp); } -void object_ref(Object *obj) +Object *object_ref(Object *obj) { if (!obj) { - return; + return NULL; } atomic_inc(&obj->ref); + return obj; } void object_unref(Object *obj) @@ -1153,7 +1207,7 @@ object_class_property_add(ObjectClass *klass, prop->release = release; prop->opaque = opaque; - g_hash_table_insert(klass->properties, g_strdup(name), prop); + g_hash_table_insert(klass->properties, prop->name, prop); return prop; } @@ -1390,6 +1444,45 @@ int64_t object_property_get_int(Object *obj, const char *name, return retval; } +static void object_property_init_defval(Object *obj, ObjectProperty *prop) +{ + Visitor *v = qobject_input_visitor_new(prop->defval); + + assert(prop->set != NULL); + prop->set(obj, v, prop->name, prop->opaque, &error_abort); + + visit_free(v); +} + +static void object_property_set_default(ObjectProperty *prop, QObject *defval) +{ + assert(!prop->defval); + assert(!prop->init); + + prop->defval = defval; + prop->init = object_property_init_defval; +} + +void object_property_set_default_bool(ObjectProperty *prop, bool value) +{ + object_property_set_default(prop, QOBJECT(qbool_from_bool(value))); +} + +void object_property_set_default_str(ObjectProperty *prop, const char *value) +{ + object_property_set_default(prop, QOBJECT(qstring_from_str(value))); +} + +void object_property_set_default_int(ObjectProperty *prop, int64_t value) +{ + object_property_set_default(prop, QOBJECT(qnum_from_int(value))); +} + +void object_property_set_default_uint(ObjectProperty *prop, uint64_t value) +{ + object_property_set_default(prop, QOBJECT(qnum_from_uint(value))); +} + void object_property_set_uint(Object *obj, uint64_t value, const char *name, Error **errp) { @@ -1620,21 +1713,37 @@ void object_property_allow_set_link(const Object *obj, const char *name, } typedef struct { - Object **child; + union { + Object **targetp; + Object *target; /* if OBJ_PROP_LINK_DIRECT, when holding the pointer */ + ptrdiff_t offset; /* if OBJ_PROP_LINK_CLASS */ + }; void (*check)(const Object *, const char *, Object *, Error **); ObjectPropertyLinkFlags flags; } LinkProperty; +static Object ** +object_link_get_targetp(Object *obj, LinkProperty *lprop) +{ + if (lprop->flags & OBJ_PROP_LINK_DIRECT) { + return &lprop->target; + } else if (lprop->flags & OBJ_PROP_LINK_CLASS) { + return (void *)obj + lprop->offset; + } else { + return lprop->targetp; + } +} + static void object_get_link_property(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { LinkProperty *lprop = opaque; - Object **child = lprop->child; + Object **targetp = object_link_get_targetp(obj, lprop); gchar *path; - if (*child) { - path = object_get_canonical_path(*child); + if (*targetp) { + path = object_get_canonical_path(*targetp); visit_type_str(v, name, &path, errp); g_free(path); } else { @@ -1689,8 +1798,8 @@ static void object_set_link_property(Object *obj, Visitor *v, { Error *local_err = NULL; LinkProperty *prop = opaque; - Object **child = prop->child; - Object *old_target = *child; + Object **targetp = object_link_get_targetp(obj, prop); + Object *old_target = *targetp; Object *new_target = NULL; char *path = NULL; @@ -1712,8 +1821,8 @@ static void object_set_link_property(Object *obj, Visitor *v, return; } - *child = new_target; - if (prop->flags == OBJ_PROP_LINK_STRONG) { + *targetp = new_target; + if (prop->flags & OBJ_PROP_LINK_STRONG) { object_ref(new_target); object_unref(old_target); } @@ -1723,33 +1832,40 @@ static Object *object_resolve_link_property(Object *parent, void *opaque, const { LinkProperty *lprop = opaque; - return *lprop->child; + return *object_link_get_targetp(parent, lprop); } static void object_release_link_property(Object *obj, const char *name, void *opaque) { LinkProperty *prop = opaque; + Object **targetp = object_link_get_targetp(obj, prop); - if ((prop->flags & OBJ_PROP_LINK_STRONG) && *prop->child) { - object_unref(*prop->child); + if ((prop->flags & OBJ_PROP_LINK_STRONG) && *targetp) { + object_unref(*targetp); + } + if (!(prop->flags & OBJ_PROP_LINK_CLASS)) { + g_free(prop); } - g_free(prop); } -void object_property_add_link(Object *obj, const char *name, - const char *type, Object **child, - void (*check)(const Object *, const char *, - Object *, Error **), - ObjectPropertyLinkFlags flags, - Error **errp) +static void object_add_link_prop(Object *obj, const char *name, + const char *type, void *ptr, + void (*check)(const Object *, const char *, + Object *, Error **), + ObjectPropertyLinkFlags flags, + Error **errp) { Error *local_err = NULL; LinkProperty *prop = g_malloc(sizeof(*prop)); gchar *full_type; ObjectProperty *op; - prop->child = child; + if (flags & OBJ_PROP_LINK_DIRECT) { + prop->target = ptr; + } else { + prop->targetp = ptr; + } prop->check = check; prop->flags = flags; @@ -1773,20 +1889,60 @@ out: g_free(full_type); } +void object_property_add_link(Object *obj, const char *name, + const char *type, Object **targetp, + void (*check)(const Object *, const char *, + Object *, Error **), + ObjectPropertyLinkFlags flags, + Error **errp) +{ + object_add_link_prop(obj, name, type, targetp, check, flags, errp); +} + +ObjectProperty * +object_class_property_add_link(ObjectClass *oc, + const char *name, + const char *type, ptrdiff_t offset, + void (*check)(const Object *obj, const char *name, + Object *val, Error **errp), + ObjectPropertyLinkFlags flags, + Error **errp) +{ + Error *local_err = NULL; + LinkProperty *prop = g_new0(LinkProperty, 1); + gchar *full_type; + ObjectProperty *op; + + prop->offset = offset; + prop->check = check; + prop->flags = flags | OBJ_PROP_LINK_CLASS; + + full_type = g_strdup_printf("link<%s>", type); + + op = object_class_property_add(oc, name, full_type, + object_get_link_property, + check ? object_set_link_property : NULL, + object_release_link_property, + prop, + &local_err); + if (local_err) { + error_propagate(errp, local_err); + g_free(prop); + goto out; + } + + op->resolve = object_resolve_link_property; + +out: + g_free(full_type); + return op; +} + void object_property_add_const_link(Object *obj, const char *name, Object *target, Error **errp) { - char *link_type; - ObjectProperty *op; - - link_type = g_strdup_printf("link<%s>", object_get_typename(target)); - op = object_property_add(obj, name, link_type, - object_get_child_property, NULL, - NULL, target, errp); - if (op != NULL) { - op->resolve = object_resolve_child_property; - } - g_free(link_type); + object_add_link_prop(obj, name, object_get_typename(target), target, + NULL, OBJ_PROP_LINK_DIRECT, errp); } gchar *object_get_canonical_path_component(Object *obj) @@ -2017,7 +2173,8 @@ void object_property_add_str(Object *obj, const char *name, } } -void object_class_property_add_str(ObjectClass *klass, const char *name, +ObjectProperty * +object_class_property_add_str(ObjectClass *klass, const char *name, char *(*get)(Object *, Error **), void (*set)(Object *, const char *, Error **), @@ -2025,19 +2182,22 @@ void object_class_property_add_str(ObjectClass *klass, const char *name, { Error *local_err = NULL; StringProperty *prop = g_malloc0(sizeof(*prop)); + ObjectProperty *rv; prop->get = get; prop->set = set; - object_class_property_add(klass, name, "string", + rv = object_class_property_add(klass, name, "string", get ? property_get_str : NULL, set ? property_set_str : NULL, - property_release_str, + NULL, prop, &local_err); if (local_err) { error_propagate(errp, local_err); g_free(prop); } + + return rv; } typedef struct BoolProperty @@ -2107,26 +2267,30 @@ void object_property_add_bool(Object *obj, const char *name, } } -void object_class_property_add_bool(ObjectClass *klass, const char *name, +ObjectProperty * +object_class_property_add_bool(ObjectClass *klass, const char *name, bool (*get)(Object *, Error **), void (*set)(Object *, bool, Error **), Error **errp) { Error *local_err = NULL; BoolProperty *prop = g_malloc0(sizeof(*prop)); + ObjectProperty *rv; prop->get = get; prop->set = set; - object_class_property_add(klass, name, "bool", + rv = object_class_property_add(klass, name, "bool", get ? property_get_bool : NULL, set ? property_set_bool : NULL, - property_release_bool, + NULL, prop, &local_err); if (local_err) { error_propagate(errp, local_err); g_free(prop); } + + return rv; } static void property_get_enum(Object *obj, Visitor *v, const char *name, @@ -2192,7 +2356,8 @@ void object_property_add_enum(Object *obj, const char *name, } } -void object_class_property_add_enum(ObjectClass *klass, const char *name, +ObjectProperty * +object_class_property_add_enum(ObjectClass *klass, const char *name, const char *typename, const QEnumLookup *lookup, int (*get)(Object *, Error **), @@ -2201,20 +2366,23 @@ void object_class_property_add_enum(ObjectClass *klass, const char *name, { Error *local_err = NULL; EnumProperty *prop = g_malloc(sizeof(*prop)); + ObjectProperty *rv; prop->lookup = lookup; prop->get = get; prop->set = set; - object_class_property_add(klass, name, typename, + rv = object_class_property_add(klass, name, typename, get ? property_get_enum : NULL, set ? property_set_enum : NULL, - property_release_enum, + NULL, prop, &local_err); if (local_err) { error_propagate(errp, local_err); g_free(prop); } + + return rv; } typedef struct TMProperty { @@ -2295,23 +2463,27 @@ void object_property_add_tm(Object *obj, const char *name, } } -void object_class_property_add_tm(ObjectClass *klass, const char *name, +ObjectProperty * +object_class_property_add_tm(ObjectClass *klass, const char *name, void (*get)(Object *, struct tm *, Error **), Error **errp) { Error *local_err = NULL; TMProperty *prop = g_malloc0(sizeof(*prop)); + ObjectProperty *rv; prop->get = get; - object_class_property_add(klass, name, "struct tm", + rv = object_class_property_add(klass, name, "struct tm", get ? property_get_tm : NULL, NULL, - property_release_tm, + NULL, prop, &local_err); if (local_err) { error_propagate(errp, local_err); g_free(prop); } + + return rv; } static char *qdev_get_type(Object *obj, Error **errp) @@ -2326,6 +2498,22 @@ static void property_get_uint8_ptr(Object *obj, Visitor *v, const char *name, visit_type_uint8(v, name, &value, errp); } +static void property_set_uint8_ptr(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + uint8_t *field = opaque; + uint8_t value; + Error *local_err = NULL; + + visit_type_uint8(v, name, &value, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + *field = value; +} + static void property_get_uint16_ptr(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { @@ -2333,6 +2521,22 @@ static void property_get_uint16_ptr(Object *obj, Visitor *v, const char *name, visit_type_uint16(v, name, &value, errp); } +static void property_set_uint16_ptr(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + uint16_t *field = opaque; + uint16_t value; + Error *local_err = NULL; + + visit_type_uint16(v, name, &value, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + *field = value; +} + static void property_get_uint32_ptr(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { @@ -2340,6 +2544,22 @@ static void property_get_uint32_ptr(Object *obj, Visitor *v, const char *name, visit_type_uint32(v, name, &value, errp); } +static void property_set_uint32_ptr(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + uint32_t *field = opaque; + uint32_t value; + Error *local_err = NULL; + + visit_type_uint32(v, name, &value, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + *field = value; +} + static void property_get_uint64_ptr(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { @@ -2347,60 +2567,184 @@ static void property_get_uint64_ptr(Object *obj, Visitor *v, const char *name, visit_type_uint64(v, name, &value, errp); } -void object_property_add_uint8_ptr(Object *obj, const char *name, - const uint8_t *v, Error **errp) +static void property_set_uint64_ptr(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { - object_property_add(obj, name, "uint8", property_get_uint8_ptr, - NULL, NULL, (void *)v, errp); + uint64_t *field = opaque; + uint64_t value; + Error *local_err = NULL; + + visit_type_uint64(v, name, &value, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + *field = value; } -void object_class_property_add_uint8_ptr(ObjectClass *klass, const char *name, - const uint8_t *v, Error **errp) +void object_property_add_uint8_ptr(Object *obj, const char *name, + const uint8_t *v, + ObjectPropertyFlags flags, + Error **errp) { - object_class_property_add(klass, name, "uint8", property_get_uint8_ptr, - NULL, NULL, (void *)v, errp); + ObjectPropertyAccessor *getter = NULL; + ObjectPropertyAccessor *setter = NULL; + + if ((flags & OBJ_PROP_FLAG_READ) == OBJ_PROP_FLAG_READ) { + getter = property_get_uint8_ptr; + } + + if ((flags & OBJ_PROP_FLAG_WRITE) == OBJ_PROP_FLAG_WRITE) { + setter = property_set_uint8_ptr; + } + + object_property_add(obj, name, "uint8", + getter, setter, NULL, (void *)v, errp); +} + +ObjectProperty * +object_class_property_add_uint8_ptr(ObjectClass *klass, const char *name, + const uint8_t *v, + ObjectPropertyFlags flags, + Error **errp) +{ + ObjectPropertyAccessor *getter = NULL; + ObjectPropertyAccessor *setter = NULL; + + if ((flags & OBJ_PROP_FLAG_READ) == OBJ_PROP_FLAG_READ) { + getter = property_get_uint8_ptr; + } + + if ((flags & OBJ_PROP_FLAG_WRITE) == OBJ_PROP_FLAG_WRITE) { + setter = property_set_uint8_ptr; + } + + return object_class_property_add(klass, name, "uint8", + getter, setter, NULL, (void *)v, errp); } void object_property_add_uint16_ptr(Object *obj, const char *name, - const uint16_t *v, Error **errp) + const uint16_t *v, + ObjectPropertyFlags flags, + Error **errp) { - object_property_add(obj, name, "uint16", property_get_uint16_ptr, - NULL, NULL, (void *)v, errp); + ObjectPropertyAccessor *getter = NULL; + ObjectPropertyAccessor *setter = NULL; + + if ((flags & OBJ_PROP_FLAG_READ) == OBJ_PROP_FLAG_READ) { + getter = property_get_uint16_ptr; + } + + if ((flags & OBJ_PROP_FLAG_WRITE) == OBJ_PROP_FLAG_WRITE) { + setter = property_set_uint16_ptr; + } + + object_property_add(obj, name, "uint16", + getter, setter, NULL, (void *)v, errp); } -void object_class_property_add_uint16_ptr(ObjectClass *klass, const char *name, - const uint16_t *v, Error **errp) +ObjectProperty * +object_class_property_add_uint16_ptr(ObjectClass *klass, const char *name, + const uint16_t *v, + ObjectPropertyFlags flags, + Error **errp) { - object_class_property_add(klass, name, "uint16", property_get_uint16_ptr, - NULL, NULL, (void *)v, errp); + ObjectPropertyAccessor *getter = NULL; + ObjectPropertyAccessor *setter = NULL; + + if ((flags & OBJ_PROP_FLAG_READ) == OBJ_PROP_FLAG_READ) { + getter = property_get_uint16_ptr; + } + + if ((flags & OBJ_PROP_FLAG_WRITE) == OBJ_PROP_FLAG_WRITE) { + setter = property_set_uint16_ptr; + } + + return object_class_property_add(klass, name, "uint16", + getter, setter, NULL, (void *)v, errp); } void object_property_add_uint32_ptr(Object *obj, const char *name, - const uint32_t *v, Error **errp) + const uint32_t *v, + ObjectPropertyFlags flags, + Error **errp) { - object_property_add(obj, name, "uint32", property_get_uint32_ptr, - NULL, NULL, (void *)v, errp); + ObjectPropertyAccessor *getter = NULL; + ObjectPropertyAccessor *setter = NULL; + + if ((flags & OBJ_PROP_FLAG_READ) == OBJ_PROP_FLAG_READ) { + getter = property_get_uint32_ptr; + } + + if ((flags & OBJ_PROP_FLAG_WRITE) == OBJ_PROP_FLAG_WRITE) { + setter = property_set_uint32_ptr; + } + + object_property_add(obj, name, "uint32", + getter, setter, NULL, (void *)v, errp); } -void object_class_property_add_uint32_ptr(ObjectClass *klass, const char *name, - const uint32_t *v, Error **errp) +ObjectProperty * +object_class_property_add_uint32_ptr(ObjectClass *klass, const char *name, + const uint32_t *v, + ObjectPropertyFlags flags, + Error **errp) { - object_class_property_add(klass, name, "uint32", property_get_uint32_ptr, - NULL, NULL, (void *)v, errp); + ObjectPropertyAccessor *getter = NULL; + ObjectPropertyAccessor *setter = NULL; + + if ((flags & OBJ_PROP_FLAG_READ) == OBJ_PROP_FLAG_READ) { + getter = property_get_uint32_ptr; + } + + if ((flags & OBJ_PROP_FLAG_WRITE) == OBJ_PROP_FLAG_WRITE) { + setter = property_set_uint32_ptr; + } + + return object_class_property_add(klass, name, "uint32", + getter, setter, NULL, (void *)v, errp); } void object_property_add_uint64_ptr(Object *obj, const char *name, - const uint64_t *v, Error **errp) + const uint64_t *v, + ObjectPropertyFlags flags, + Error **errp) { - object_property_add(obj, name, "uint64", property_get_uint64_ptr, - NULL, NULL, (void *)v, errp); + ObjectPropertyAccessor *getter = NULL; + ObjectPropertyAccessor *setter = NULL; + + if ((flags & OBJ_PROP_FLAG_READ) == OBJ_PROP_FLAG_READ) { + getter = property_get_uint64_ptr; + } + + if ((flags & OBJ_PROP_FLAG_WRITE) == OBJ_PROP_FLAG_WRITE) { + setter = property_set_uint64_ptr; + } + + object_property_add(obj, name, "uint64", + getter, setter, NULL, (void *)v, errp); } -void object_class_property_add_uint64_ptr(ObjectClass *klass, const char *name, - const uint64_t *v, Error **errp) +ObjectProperty * +object_class_property_add_uint64_ptr(ObjectClass *klass, const char *name, + const uint64_t *v, + ObjectPropertyFlags flags, + Error **errp) { - object_class_property_add(klass, name, "uint64", property_get_uint64_ptr, - NULL, NULL, (void *)v, errp); + ObjectPropertyAccessor *getter = NULL; + ObjectPropertyAccessor *setter = NULL; + + if ((flags & OBJ_PROP_FLAG_READ) == OBJ_PROP_FLAG_READ) { + getter = property_get_uint64_ptr; + } + + if ((flags & OBJ_PROP_FLAG_WRITE) == OBJ_PROP_FLAG_WRITE) { + setter = property_set_uint64_ptr; + } + + return object_class_property_add(klass, name, "uint64", + getter, setter, NULL, (void *)v, errp); } typedef struct { @@ -2477,6 +2821,9 @@ void object_property_add_alias(Object *obj, const char *name, goto out; } op->resolve = property_resolve_alias; + if (target_prop->defval) { + op->defval = qobject_ref(target_prop->defval); + } object_property_set_description(obj, op->name, target_prop->description, diff --git a/qom/object_interfaces.c b/qom/object_interfaces.c index 46cd6eab5c..72cb9e32a9 100644 --- a/qom/object_interfaces.c +++ b/qom/object_interfaces.c @@ -4,6 +4,8 @@ #include "qapi/error.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qerror.h" +#include "qapi/qmp/qjson.h" +#include "qapi/qmp/qstring.h" #include "qom/object_interfaces.h" #include "qemu/help_option.h" #include "qemu/module.h" @@ -158,6 +160,29 @@ int user_creatable_add_opts_foreach(void *opaque, QemuOpts *opts, Error **errp) return 0; } +char *object_property_help(const char *name, const char *type, + QObject *defval, const char *description) +{ + GString *str = g_string_new(NULL); + + g_string_append_printf(str, " %s=<%s>", name, type); + if (description || defval) { + if (str->len < 24) { + g_string_append_printf(str, "%*s", 24 - (int)str->len, ""); + } + g_string_append(str, " - "); + } + if (description) { + g_string_append(str, description); + } + if (defval) { + g_autofree char *def_json = qstring_free(qobject_to_json(defval), TRUE); + g_string_append_printf(str, " (default: %s)", def_json); + } + + return g_string_free(str, false); +} + bool user_creatable_print_help(const char *type, QemuOpts *opts) { ObjectClass *klass; @@ -184,21 +209,13 @@ bool user_creatable_print_help(const char *type, QemuOpts *opts) object_class_property_iter_init(&iter, klass); while ((prop = object_property_iter_next(&iter))) { - GString *str; - if (!prop->set) { continue; } - str = g_string_new(NULL); - g_string_append_printf(str, " %s=<%s>", prop->name, prop->type); - if (prop->description) { - if (str->len < 24) { - g_string_append_printf(str, "%*s", 24 - (int)str->len, ""); - } - g_string_append_printf(str, " - %s", prop->description); - } - g_ptr_array_add(array, g_string_free(str, false)); + g_ptr_array_add(array, + object_property_help(prop->name, prop->type, + prop->defval, prop->description)); } g_ptr_array_sort(array, (GCompareFunc)qemu_pstrcmp0); if (array->len > 0) { diff --git a/qom/qom-hmp-cmds.c b/qom/qom-hmp-cmds.c index a268e01eb4..cd08233a4c 100644 --- a/qom/qom-hmp-cmds.c +++ b/qom/qom-hmp-cmds.c @@ -37,7 +37,7 @@ void hmp_qom_list(Monitor *mon, const QDict *qdict) } qapi_free_ObjectPropertyInfoList(start); } - hmp_handle_error(mon, &err); + hmp_handle_error(mon, err); } void hmp_qom_set(Monitor *mon, const QDict *qdict) @@ -59,7 +59,7 @@ void hmp_qom_set(Monitor *mon, const QDict *qdict) } object_property_parse(obj, value, property, &err); } - hmp_handle_error(mon, &err); + hmp_handle_error(mon, err); } typedef struct QOMCompositionState { diff --git a/qom/qom-qmp-cmds.c b/qom/qom-qmp-cmds.c index 236e8e41dd..e47ebe8ed1 100644 --- a/qom/qom-qmp-cmds.c +++ b/qom/qom-qmp-cmds.c @@ -14,6 +14,7 @@ */ #include "qemu/osdep.h" +#include "block/qdict.h" #include "hw/qdev-core.h" #include "qapi/error.h" #include "qapi/qapi-commands-qdev.h" @@ -121,58 +122,6 @@ ObjectTypeInfoList *qmp_qom_list_types(bool has_implements, return ret; } -/* Return a DevicePropertyInfo for a qdev property. - * - * If a qdev property with the given name does not exist, use the given default - * type. If the qdev property info should not be shown, return NULL. - * - * The caller must free the return value. - */ -static ObjectPropertyInfo *make_device_property_info(ObjectClass *klass, - const char *name, - const char *default_type, - const char *description) -{ - ObjectPropertyInfo *info; - Property *prop; - - do { - for (prop = DEVICE_CLASS(klass)->props; prop && prop->name; prop++) { - if (strcmp(name, prop->name) != 0) { - continue; - } - - /* - * TODO Properties without a parser are just for dirty hacks. - * qdev_prop_ptr is the only such PropertyInfo. It's marked - * for removal. This conditional should be removed along with - * it. - */ - if (!prop->info->set && !prop->info->create) { - return NULL; /* no way to set it, don't show */ - } - - info = g_malloc0(sizeof(*info)); - info->name = g_strdup(prop->name); - info->type = default_type ? g_strdup(default_type) - : g_strdup(prop->info->name); - info->has_description = !!prop->info->description; - info->description = g_strdup(prop->info->description); - return info; - } - klass = object_class_get_parent(klass); - } while (klass != object_class_by_name(TYPE_DEVICE)); - - /* Not a qdev property, use the default type */ - info = g_malloc0(sizeof(*info)); - info->name = g_strdup(name); - info->type = g_strdup(default_type); - info->has_description = !!description; - info->description = g_strdup(description); - - return info; -} - ObjectPropertyInfoList *qmp_device_list_properties(const char *typename, Error **errp) { @@ -224,11 +173,13 @@ ObjectPropertyInfoList *qmp_device_list_properties(const char *typename, continue; } - info = make_device_property_info(klass, prop->name, prop->type, - prop->description); - if (!info) { - continue; - } + info = g_new0(ObjectPropertyInfo, 1); + info->name = g_strdup(prop->name); + info->type = g_strdup(prop->type); + info->has_description = !!prop->description; + info->description = g_strdup(prop->description); + info->default_value = qobject_ref(prop->defval); + info->has_default_value = !!info->default_value; entry = g_malloc0(sizeof(*entry)); entry->value = info; @@ -290,13 +241,30 @@ ObjectPropertyInfoList *qmp_qom_list_properties(const char *typename, return prop_list; } -void qmp_object_add(const char *type, const char *id, - bool has_props, QObject *props, Error **errp) +void qmp_object_add(QDict *qdict, QObject **ret_data, Error **errp) { + QObject *props; QDict *pdict; Visitor *v; Object *obj; + g_autofree char *type = NULL; + g_autofree char *id = NULL; + type = g_strdup(qdict_get_try_str(qdict, "qom-type")); + if (!type) { + error_setg(errp, QERR_MISSING_PARAMETER, "qom-type"); + return; + } + qdict_del(qdict, "qom-type"); + + id = g_strdup(qdict_get_try_str(qdict, "id")); + if (!id) { + error_setg(errp, QERR_MISSING_PARAMETER, "id"); + return; + } + qdict_del(qdict, "id"); + + props = qdict_get(qdict, "props"); if (props) { pdict = qobject_to(QDict, props); if (!pdict) { @@ -304,17 +272,20 @@ void qmp_object_add(const char *type, const char *id, return; } qobject_ref(pdict); - } else { - pdict = qdict_new(); + qdict_del(qdict, "props"); + qdict_join(qdict, pdict, false); + if (qdict_size(pdict) != 0) { + error_setg(errp, "Option in 'props' conflicts with top level"); + qobject_unref(pdict); + return; + } + qobject_unref(pdict); } - v = qobject_input_visitor_new(QOBJECT(pdict)); - obj = user_creatable_add_type(type, id, pdict, v, errp); + v = qobject_input_visitor_new(QOBJECT(qdict)); + obj = user_creatable_add_type(type, id, qdict, v, errp); visit_free(v); - if (obj) { - object_unref(obj); - } - qobject_unref(pdict); + object_unref(obj); } void qmp_object_del(const char *id, Error **errp) diff --git a/qtest.c b/qtest.c index 8b50e2783e..5672b75c35 100644 --- a/qtest.c +++ b/qtest.c @@ -27,7 +27,8 @@ #include "qemu/error-report.h" #include "qemu/module.h" #include "qemu/cutils.h" -#ifdef TARGET_PPC64 +#include "config-devices.h" +#ifdef CONFIG_PSERIES #include "hw/ppc/spapr_rtas.h" #endif @@ -42,6 +43,8 @@ static GString *inbuf; static int irq_levels[MAX_IRQ]; static qemu_timeval start_time; static bool qtest_opened; +static void (*qtest_server_send)(void*, const char*); +static void *qtest_server_send_opaque; #define FMT_timeval "%ld.%06ld" @@ -228,8 +231,10 @@ static void GCC_FMT_ATTR(1, 2) qtest_log_send(const char *fmt, ...) va_end(ap); } -static void do_qtest_send(CharBackend *chr, const char *str, size_t len) +static void qtest_server_char_be_send(void *opaque, const char *str) { + size_t len = strlen(str); + CharBackend* chr = (CharBackend *)opaque; qemu_chr_fe_write_all(chr, (uint8_t *)str, len); if (qtest_log_fp && qtest_opened) { fprintf(qtest_log_fp, "%s", str); @@ -238,7 +243,7 @@ static void do_qtest_send(CharBackend *chr, const char *str, size_t len) static void qtest_send(CharBackend *chr, const char *str) { - do_qtest_send(chr, str, strlen(str)); + qtest_server_send(qtest_server_send_opaque, str); } static void GCC_FMT_ATTR(2, 3) qtest_sendf(CharBackend *chr, @@ -429,23 +434,23 @@ static void qtest_process_command(CharBackend *chr, gchar **words) if (words[0][5] == 'b') { uint8_t data = value; - address_space_rw(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, - &data, 1, true); + address_space_write(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, + &data, 1); } else if (words[0][5] == 'w') { uint16_t data = value; tswap16s(&data); - address_space_rw(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, - (uint8_t *) &data, 2, true); + address_space_write(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, + &data, 2); } else if (words[0][5] == 'l') { uint32_t data = value; tswap32s(&data); - address_space_rw(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, - (uint8_t *) &data, 4, true); + address_space_write(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, + &data, 4); } else if (words[0][5] == 'q') { uint64_t data = value; tswap64s(&data); - address_space_rw(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, - (uint8_t *) &data, 8, true); + address_space_write(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, + &data, 8); } qtest_send_prefix(chr); qtest_send(chr, "OK\n"); @@ -463,22 +468,22 @@ static void qtest_process_command(CharBackend *chr, gchar **words) if (words[0][4] == 'b') { uint8_t data; - address_space_rw(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, - &data, 1, false); + address_space_read(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, + &data, 1); value = data; } else if (words[0][4] == 'w') { uint16_t data; - address_space_rw(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, - (uint8_t *) &data, 2, false); + address_space_read(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, + &data, 2); value = tswap16(data); } else if (words[0][4] == 'l') { uint32_t data; - address_space_rw(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, - (uint8_t *) &data, 4, false); + address_space_read(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, + &data, 4); value = tswap32(data); } else if (words[0][4] == 'q') { - address_space_rw(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, - (uint8_t *) &value, 8, false); + address_space_read(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, + &value, 8); tswap64s(&value); } qtest_send_prefix(chr); @@ -498,8 +503,8 @@ static void qtest_process_command(CharBackend *chr, gchar **words) g_assert(len); data = g_malloc(len); - address_space_rw(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, - data, len, false); + address_space_read(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, data, + len); enc = g_malloc(2 * len + 1); for (i = 0; i < len; i++) { @@ -524,8 +529,8 @@ static void qtest_process_command(CharBackend *chr, gchar **words) g_assert(ret == 0); data = g_malloc(len); - address_space_rw(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, - data, len, false); + address_space_read(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, data, + len); b64_data = g_base64_encode(data, len); qtest_send_prefix(chr); qtest_sendf(chr, "OK %s\n", b64_data); @@ -559,8 +564,8 @@ static void qtest_process_command(CharBackend *chr, gchar **words) data[i] = 0; } } - address_space_rw(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, - data, len, true); + address_space_write(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, data, + len); g_free(data); qtest_send_prefix(chr); @@ -582,8 +587,8 @@ static void qtest_process_command(CharBackend *chr, gchar **words) if (len) { data = g_malloc(len); memset(data, pattern, len); - address_space_rw(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, - data, len, true); + address_space_write(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, + data, len); g_free(data); } @@ -616,8 +621,8 @@ static void qtest_process_command(CharBackend *chr, gchar **words) out_len = MIN(out_len, len); } - address_space_rw(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, - data, len, true); + address_space_write(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, data, + len); qtest_send_prefix(chr); qtest_send(chr, "OK\n"); @@ -628,7 +633,7 @@ static void qtest_process_command(CharBackend *chr, gchar **words) #else qtest_sendf(chr, "OK little\n"); #endif -#ifdef TARGET_PPC64 +#ifdef CONFIG_PSERIES } else if (strcmp(words[0], "rtas") == 0) { uint64_t res, args, ret; unsigned long nargs, nret; @@ -722,7 +727,7 @@ static int qtest_can_read(void *opaque) return 1024; } -static void qtest_event(void *opaque, int event) +static void qtest_event(void *opaque, QEMUChrEvent event) { int i; @@ -783,9 +788,33 @@ void qtest_server_init(const char *qtest_chrdev, const char *qtest_log, Error ** qemu_chr_fe_set_echo(&qtest_chr, true); inbuf = g_string_new(""); + + if (!qtest_server_send) { + qtest_server_set_send_handler(qtest_server_char_be_send, &qtest_chr); + } +} + +void qtest_server_set_send_handler(void (*send)(void*, const char*), + void *opaque) +{ + qtest_server_send = send; + qtest_server_send_opaque = opaque; } bool qtest_driver(void) { return qtest_chr.chr != NULL; } + +void qtest_server_inproc_recv(void *dummy, const char *buf) +{ + static GString *gstr; + if (!gstr) { + gstr = g_string_new(NULL); + } + g_string_append(gstr, buf); + if (gstr->str[gstr->len - 1] == '\n') { + qtest_process_inbuf(NULL, gstr); + g_string_truncate(gstr, 0); + } +} diff --git a/replay/Makefile.objs b/replay/Makefile.objs index cee6539a23..939be964a9 100644 --- a/replay/Makefile.objs +++ b/replay/Makefile.objs @@ -6,4 +6,5 @@ common-obj-y += replay-input.o common-obj-y += replay-char.o common-obj-y += replay-snapshot.o common-obj-y += replay-net.o -common-obj-y += replay-audio.o \ No newline at end of file +common-obj-y += replay-audio.o +common-obj-y += replay-random.o diff --git a/replay/replay-internal.h b/replay/replay-internal.h index 55fca1ac6b..33ac551e78 100644 --- a/replay/replay-internal.h +++ b/replay/replay-internal.h @@ -34,6 +34,8 @@ enum ReplayEvents { EVENT_AUDIO_OUT, /* for audio in event */ EVENT_AUDIO_IN, + /* for random number generator */ + EVENT_RANDOM, /* for clock read/writes */ /* some of greater codes are reserved for clocks */ EVENT_CLOCK, diff --git a/replay/replay-random.c b/replay/replay-random.c new file mode 100644 index 0000000000..afc7a0fccc --- /dev/null +++ b/replay/replay-random.c @@ -0,0 +1,44 @@ +/* + * replay-random.c + * + * Copyright (c) 2010-2020 Institute for System Programming + * of the Russian Academy of Sciences. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "sysemu/replay.h" +#include "replay-internal.h" + +void replay_save_random(int ret, void *buf, size_t len) +{ + g_assert(replay_mutex_locked()); + + replay_save_instructions(); + replay_put_event(EVENT_RANDOM); + replay_put_dword(ret); + replay_put_array(buf, len); +} + +int replay_read_random(void *buf, size_t len) +{ + int ret = 0; + g_assert(replay_mutex_locked()); + + replay_account_executed_instructions(); + if (replay_next_event_is(EVENT_RANDOM)) { + size_t buf_size = 0; + ret = replay_get_dword(); + replay_get_array(buf, &buf_size); + replay_finish_event(); + g_assert(buf_size == len); + } else { + error_report("Missing random event in the replay log"); + exit(1); + } + return ret; +} diff --git a/replay/replay.c b/replay/replay.c index 5cc25bd2f8..706c7b4f4b 100644 --- a/replay/replay.c +++ b/replay/replay.c @@ -22,7 +22,7 @@ /* Current version of the replay mechanism. Increase it when file format changes. */ -#define REPLAY_VERSION 0xe02008 +#define REPLAY_VERSION 0xe02009 /* Size of replay log header */ #define HEADER_SIZE (sizeof(uint32_t) + sizeof(uint64_t)) diff --git a/roms/Makefile b/roms/Makefile index 28e1e557b0..f9acf39954 100644 --- a/roms/Makefile +++ b/roms/Makefile @@ -66,6 +66,7 @@ default help: @echo " efi -- update UEFI (edk2) platform firmware" @echo " opensbi32-virt -- update OpenSBI for 32-bit virt machine" @echo " opensbi64-virt -- update OpenSBI for 64-bit virt machine" + @echo " opensbi32-sifive_u -- update OpenSBI for 32-bit sifive_u machine" @echo " opensbi64-sifive_u -- update OpenSBI for 64-bit sifive_u machine" @echo " bios-microvm -- update bios-microvm.bin (qboot)" @echo " clean -- delete the files generated by the previous" \ @@ -181,6 +182,12 @@ opensbi64-virt: PLATFORM="qemu/virt" cp opensbi/build/platform/qemu/virt/firmware/fw_jump.bin ../pc-bios/opensbi-riscv64-virt-fw_jump.bin +opensbi32-sifive_u: + $(MAKE) -C opensbi \ + CROSS_COMPILE=$(riscv32_cross_prefix) \ + PLATFORM="sifive/fu540" + cp opensbi/build/platform/sifive/fu540/firmware/fw_jump.bin ../pc-bios/opensbi-riscv32-sifive_u-fw_jump.bin + opensbi64-sifive_u: $(MAKE) -C opensbi \ CROSS_COMPILE=$(riscv64_cross_prefix) \ diff --git a/roms/SLOF b/roms/SLOF index 8ebf2f55e1..8e012d6fdd 160000 --- a/roms/SLOF +++ b/roms/SLOF @@ -1 +1 @@ -Subproject commit 8ebf2f55e1ba1492b942ba4b682160e644fc0f98 +Subproject commit 8e012d6fddb62be833d746cef3f03e6c8beecde0 diff --git a/roms/config.seabios-128k b/roms/config.seabios-128k index a17502ca0f..c43912bf9d 100644 --- a/roms/config.seabios-128k +++ b/roms/config.seabios-128k @@ -15,3 +15,4 @@ CONFIG_PVSCSI=n CONFIG_NVME=n CONFIG_USE_SMM=n CONFIG_VGAHOOKS=n +CONFIG_HOST_BIOS_GEOMETRY=n diff --git a/roms/edk2-funcs.sh b/roms/edk2-funcs.sh index 3f4485b201..cd6e4f2c82 100644 --- a/roms/edk2-funcs.sh +++ b/roms/edk2-funcs.sh @@ -112,6 +112,9 @@ qemu_edk2_get_cross_prefix() ( [ "$gcc_arch" == i686 ] && [ "$host_arch" == x86_64 ] ); then # no cross-compiler needed : + elif ( [ -e /etc/debian_version ] && [ "$gcc_arch" == arm ] ); then + # force soft-float cross-compiler on Debian + printf 'arm-linux-gnueabi-' else printf '%s-linux-gnu-\n' "$gcc_arch" fi diff --git a/roms/openhackware b/roms/openhackware deleted file mode 160000 index c559da7c8e..0000000000 --- a/roms/openhackware +++ /dev/null @@ -1 +0,0 @@ -Subproject commit c559da7c8eec5e45ef1f67978827af6f0b9546f5 diff --git a/roms/opensbi b/roms/opensbi index be92da280d..ac5e821d50 160000 --- a/roms/opensbi +++ b/roms/opensbi @@ -1 +1 @@ -Subproject commit be92da280d87c38a2e0adc5d3f43bab7b5468f09 +Subproject commit ac5e821d50be631f26274765a59bc1b444ffd862 diff --git a/roms/seabios b/roms/seabios index c9ba5276e3..f21b5a4aeb 160000 --- a/roms/seabios +++ b/roms/seabios @@ -1 +1 @@ -Subproject commit c9ba5276e3217ac6a1ec772dbebf568ba3a8a55d +Subproject commit f21b5a4aeb020f2a5e2c6503f906a9349dd2f069 diff --git a/roms/seabios-hppa b/roms/seabios-hppa index 0f4fe84658..1630ac7d65 160000 --- a/roms/seabios-hppa +++ b/roms/seabios-hppa @@ -1 +1 @@ -Subproject commit 0f4fe84658165e96ce35870fd19fc634e182e77b +Subproject commit 1630ac7d65c4a09218cc677f1fa56cd5b3140447 diff --git a/rules.mak b/rules.mak index 967295dd2b..694865b63e 100644 --- a/rules.mak +++ b/rules.mak @@ -76,7 +76,7 @@ expand-objs = $(strip $(sort $(filter %.o,$1)) \ # must link with the C++ compiler, not the plain C compiler. LINKPROG = $(or $(CXX),$(CC)) -LINK = $(call quiet-command, $(LINKPROG) $(QEMU_LDFLAGS) $(QEMU_CFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ \ +LINK = $(call quiet-command, $(LINKPROG) $(CFLAGS) $(QEMU_LDFLAGS) -o $@ \ $(call process-archive-undefs, $1) \ $(version-obj-y) $(call extract-libs,$1) $(LIBS),"LINK","$(TARGET_DIR)$@") @@ -105,7 +105,7 @@ LINK = $(call quiet-command, $(LINKPROG) $(QEMU_LDFLAGS) $(QEMU_CFLAGS) $(CFLAGS DSO_OBJ_CFLAGS := -fPIC -DBUILD_DSO module-common.o: CFLAGS += $(DSO_OBJ_CFLAGS) -%$(DSOSUF): LDFLAGS += $(LDFLAGS_SHARED) +%$(DSOSUF): QEMU_LDFLAGS += $(LDFLAGS_SHARED) %$(DSOSUF): %.mo $(call LINK,$^) @# Copy to build root so modules can be loaded when program started without install @@ -399,3 +399,42 @@ GEN_SUBST = $(call quiet-command, \ %.json: %.json.in $(call GEN_SUBST) + +# Support for building multiple output files by atomically executing +# a single rule which depends on several input files (so the rule +# will be executed exactly once, not once per output file, and +# not multiple times in parallel.) For more explanation see: +# https://www.cmcrossroads.com/article/atomic-rules-gnu-make + +# Given a space-separated list of filenames, create the name of +# a 'sentinel' file to use to indicate that they have been built. +# We use fixed text on the end to avoid accidentally triggering +# automatic pattern rules, and . on the start to make the file +# not show up in ls output. +sentinel = .$(subst $(SPACE),_,$(subst /,_,$1)).sentinel. + +# Define an atomic rule that builds multiple outputs from multiple inputs. +# To use: +# $(call atomic,out1 out2 ...,in1 in2 ...) +# rule to do the operation +# +# Make 4.3 will have native support for this, and you would be able +# to instead write: +# out1 out2 ... &: in1 in2 ... +# rule to do the operation +# +# The way this works is that it creates a make rule +# "out1 out2 ... : sentinel-file ; @:" which says that the sentinel +# depends on the dependencies, and the rule to do that is "do nothing". +# Then we have a rule +# "sentinel-file : in1 in2 ..." +# whose commands start with "touch sentinel-file" and then continue +# with the rule text provided by the user of this 'atomic' function. +# The foreach... is there to delete the sentinel file if any of the +# output files don't exist, so that we correctly rebuild in that situation. +atomic = $(eval $1: $(call sentinel,$1) ; @:) \ + $(call sentinel,$1) : $2 ; @touch $$@ \ + $(foreach t,$1,$(if $(wildcard $t),,$(shell rm -f $(call sentinel,$1)))) + +print-%: + @echo '$*=$($*)' 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 # # 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 # -from __future__ import print_function import simpletrace import argparse import numpy as np diff --git a/scripts/analyze-migration.py b/scripts/analyze-migration.py index e527eb168e..96a31d3974 100755 --- a/scripts/analyze-migration.py +++ b/scripts/analyze-migration.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Migration Stream Analyzer # @@ -17,12 +17,18 @@ # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, see . -from __future__ import print_function -import numpy as np import json import os import argparse import collections +import struct +import sys + + +MIN_PYTHON = (3, 2) +if sys.version_info < MIN_PYTHON: + sys.exit("Python %s.%s or later is required.\n" % MIN_PYTHON) + def mkdir_p(path): try: @@ -30,29 +36,26 @@ def mkdir_p(path): except OSError: pass + class MigrationFile(object): def __init__(self, filename): self.filename = filename self.file = open(self.filename, "rb") def read64(self): - return np.asscalar(np.fromfile(self.file, count=1, dtype='>i8')[0]) + return int.from_bytes(self.file.read(8), byteorder='big', signed=True) def read32(self): - return np.asscalar(np.fromfile(self.file, count=1, dtype='>i4')[0]) + return int.from_bytes(self.file.read(4), byteorder='big', signed=True) def read16(self): - return np.asscalar(np.fromfile(self.file, count=1, dtype='>i2')[0]) + return int.from_bytes(self.file.read(2), byteorder='big', signed=True) def read8(self): - return np.asscalar(np.fromfile(self.file, count=1, dtype='>i1')[0]) + return int.from_bytes(self.file.read(1), byteorder='big', signed=True) def readstr(self, len = None): - if len is None: - len = self.read8() - if len == 0: - return "" - return np.fromfile(self.file, count=1, dtype=('S%d' % len))[0] + return self.readvar(len).decode('utf-8') def readvar(self, size = None): if size is None: @@ -86,8 +89,8 @@ class MigrationFile(object): # Find the last NULL byte, then the first brace after that. This should # be the beginning of our JSON data. - nulpos = data.rfind("\0") - jsonpos = data.find("{", nulpos) + nulpos = data.rfind(b'\0') + jsonpos = data.find(b'{', nulpos) # Check backwards from there and see whether we guessed right self.file.seek(datapos + jsonpos - 5, 0) @@ -275,7 +278,7 @@ class VMSDFieldGeneric(object): return str(self.__str__()) def __str__(self): - return " ".join("{0:02x}".format(ord(c)) for c in self.data) + return " ".join("{0:02x}".format(c) for c in self.data) def getDict(self): return self.__str__() @@ -307,8 +310,8 @@ class VMSDFieldInt(VMSDFieldGeneric): def read(self): super(VMSDFieldInt, self).read() - self.sdata = np.fromstring(self.data, count=1, dtype=(self.sdtype))[0] - self.udata = np.fromstring(self.data, count=1, dtype=(self.udtype))[0] + self.sdata = int.from_bytes(self.data, byteorder='big', signed=True) + self.udata = int.from_bytes(self.data, byteorder='big', signed=False) self.data = self.sdata return self.data @@ -363,7 +366,7 @@ class VMSDFieldStruct(VMSDFieldGeneric): array_len = field.pop('array_len') field['index'] = 0 new_fields.append(field) - for i in xrange(1, array_len): + for i in range(1, array_len): c = field.copy() c['index'] = i new_fields.append(c) diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 3aef6e3dfe..e658e6546f 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -35,6 +35,8 @@ my $summary_file = 0; my $root; my %debug; my $help = 0; +my $acpi_testexpected; +my $acpi_nontestexpected; sub help { my ($exitcode) = @_; @@ -462,7 +464,7 @@ sub top_of_kernel_tree { my @tree_check = ( "COPYING", "MAINTAINERS", "Makefile", "README.rst", "docs", "VERSION", - "vl.c" + "linux-user", "softmmu" ); foreach my $check (@tree_check) { @@ -1256,6 +1258,27 @@ sub WARN { } } +# According to tests/qtest/bios-tables-test.c: do not +# change expected file in the same commit with adding test +sub checkfilename { + my ($name) = @_; + if ($name =~ m#^tests/data/acpi/# and + # make exception for a shell script that rebuilds the files + not $name =~ m#^\.sh$# or + $name =~ m#^tests/qtest/bios-tables-test-allowed-diff.h$#) { + $acpi_testexpected = $name; + } else { + $acpi_nontestexpected = $name; + } + if (defined $acpi_testexpected and defined $acpi_nontestexpected) { + ERROR("Do not add expected files together with tests, " . + "follow instructions in " . + "tests/qtest/bios-tables-test.c: both " . + $acpi_testexpected . " and " . + $acpi_nontestexpected . " found\n"); + } +} + sub process { my $filename = shift; @@ -1431,9 +1454,11 @@ sub process { if ($line =~ /^diff --git.*?(\S+)$/) { $realfile = $1; $realfile =~ s@^([^/]*)/@@ if (!$file); + checkfilename($realfile); } elsif ($line =~ /^\+\+\+\s+(\S+)/) { $realfile = $1; $realfile =~ s@^([^/]*)/@@ if (!$file); + checkfilename($realfile); $p1_prefix = $1; if (!$file && $tree && $p1_prefix ne '' && @@ -1460,6 +1485,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; @@ -1824,6 +1855,11 @@ sub process { ERROR("suspicious ; after while (0)\n" . $herecurr); } +# Check superfluous trailing ';' + if ($line =~ /;;$/) { + ERROR("superfluous trailing semicolon\n" . $herecurr); + } + # Check relative indent for conditionals and blocks. if ($line =~ /\b(?:(?:if|while|for)\s*\(|do\b)/ && $line !~ /^.\s*#/ && $line !~ /\}\s*while\s*/) { my ($s, $c) = ($stat, $cond); diff --git a/scripts/coccinelle/cpu-reset.cocci b/scripts/coccinelle/cpu-reset.cocci new file mode 100644 index 0000000000..396a724e51 --- /dev/null +++ b/scripts/coccinelle/cpu-reset.cocci @@ -0,0 +1,47 @@ +// Convert targets using the old CPUState reset to DeviceState reset +// +// Copyright Linaro Ltd 2020 +// This work is licensed under the terms of the GNU GPLv2 or later. +// +// spatch --macro-file scripts/cocci-macro-file.h \ +// --sp-file scripts/coccinelle/cpu-reset.cocci \ +// --keep-comments --smpl-spacing --in-place --include-headers --dir target +// +// For simplicity we assume some things about the code we're modifying +// that happen to be true for all our targets: +// * all cpu_class_set_parent_reset() callsites have a 'DeviceClass *dc' local +// * the parent reset field in the target CPU class is 'parent_reset' +// * no reset function already has a 'dev' local + +@@ +identifier cpu, x; +typedef CPUState; +@@ +struct x { +... +- void (*parent_reset)(CPUState *cpu); ++ DeviceReset parent_reset; +... +}; +@ rule1 @ +identifier resetfn; +expression resetfield; +identifier cc; +@@ +- cpu_class_set_parent_reset(cc, resetfn, resetfield) ++ device_class_set_parent_reset(dc, resetfn, resetfield) +@@ +identifier rule1.resetfn; +identifier cpu, cc; +typedef CPUState, DeviceState; +@@ +-resetfn(CPUState *cpu) +-{ ++resetfn(DeviceState *dev) ++{ ++ CPUState *cpu = CPU(dev); +<... +- cc->parent_reset(cpu); ++ cc->parent_reset(dev); +...> +} diff --git a/scripts/coccinelle/error-use-after-free.cocci b/scripts/coccinelle/error-use-after-free.cocci new file mode 100644 index 0000000000..72ae9fdebf --- /dev/null +++ b/scripts/coccinelle/error-use-after-free.cocci @@ -0,0 +1,52 @@ +// Find and fix trivial use-after-free of Error objects +// +// Copyright (c) 2020 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 +// . +// +// How to use: +// spatch --sp-file scripts/coccinelle/error-use-after-free.cocci \ +// --macro-file scripts/cocci-macro-file.h --in-place \ +// --no-show-diff ( FILES... | --use-gitgrep . ) + +@ exists@ +identifier fn, fn2; +expression err; +@@ + + fn(...) + { + <... +( + error_free(err); ++ err = NULL; +| + error_report_err(err); ++ err = NULL; +| + error_reportf_err(err, ...); ++ err = NULL; +| + warn_report_err(err); ++ err = NULL; +| + warn_reportf_err(err, ...); ++ err = NULL; +) + ... when != err = NULL + when != exit(...) + fn2(..., err, ...) + ...> + } diff --git a/scripts/coccinelle/exec_rw_const.cocci b/scripts/coccinelle/exec_rw_const.cocci new file mode 100644 index 0000000000..1a20296951 --- /dev/null +++ b/scripts/coccinelle/exec_rw_const.cocci @@ -0,0 +1,111 @@ +/* + Usage: + + spatch \ + --macro-file scripts/cocci-macro-file.h \ + --sp-file scripts/coccinelle/exec_rw_const.cocci \ + --keep-comments \ + --in-place \ + --dir . +*/ + +// Convert to boolean +@@ +expression E1, E2, E3, E4, E5; +@@ +( +- address_space_rw(E1, E2, E3, E4, E5, 0) ++ address_space_rw(E1, E2, E3, E4, E5, false) +| +- address_space_rw(E1, E2, E3, E4, E5, 1) ++ address_space_rw(E1, E2, E3, E4, E5, true) +| + +- cpu_physical_memory_rw(E1, E2, E3, 0) ++ cpu_physical_memory_rw(E1, E2, E3, false) +| +- cpu_physical_memory_rw(E1, E2, E3, 1) ++ cpu_physical_memory_rw(E1, E2, E3, true) +| + +- cpu_physical_memory_map(E1, E2, 0) ++ cpu_physical_memory_map(E1, E2, false) +| +- cpu_physical_memory_map(E1, E2, 1) ++ cpu_physical_memory_map(E1, E2, true) +) + +// Use address_space_write instead of casting to non-const +@@ +type T; +const T *V; +expression E1, E2, E3, E4; +@@ +( +- address_space_rw(E1, E2, E3, (T *)V, E4, 1) ++ address_space_write(E1, E2, E3, V, E4) +| +- address_space_rw(E1, E2, E3, (void *)V, E4, 1) ++ address_space_write(E1, E2, E3, V, E4) +) + +// Avoid uses of address_space_rw() with a constant is_write argument. +@@ +expression E1, E2, E3, E4, E5; +symbol true, false; +@@ +( +- address_space_rw(E1, E2, E3, E4, E5, false) ++ address_space_read(E1, E2, E3, E4, E5) +| +- address_space_rw(E1, E2, E3, E4, E5, true) ++ address_space_write(E1, E2, E3, E4, E5) +) + +// Avoid uses of cpu_physical_memory_rw() with a constant is_write argument. +@@ +expression E1, E2, E3; +@@ +( +- cpu_physical_memory_rw(E1, E2, E3, false) ++ cpu_physical_memory_read(E1, E2, E3) +| +- cpu_physical_memory_rw(E1, E2, E3, true) ++ cpu_physical_memory_write(E1, E2, E3) +) + +// Remove useless cast +@@ +expression E1, E2, E3, E4, E5, E6; +type T; +@@ +( +- address_space_rw(E1, E2, E3, (T *)(E4), E5, E6) ++ address_space_rw(E1, E2, E3, E4, E5, E6) +| +- address_space_read(E1, E2, E3, (T *)(E4), E5) ++ address_space_read(E1, E2, E3, E4, E5) +| +- address_space_write(E1, E2, E3, (T *)(E4), E5) ++ address_space_write(E1, E2, E3, E4, E5) +| +- address_space_write_rom(E1, E2, E3, (T *)(E4), E5) ++ address_space_write_rom(E1, E2, E3, E4, E5) +| + +- cpu_physical_memory_rw(E1, (T *)(E2), E3, E4) ++ cpu_physical_memory_rw(E1, E2, E3, E4) +| +- cpu_physical_memory_read(E1, (T *)(E2), E3) ++ cpu_physical_memory_read(E1, E2, E3) +| +- cpu_physical_memory_write(E1, (T *)(E2), E3) ++ cpu_physical_memory_write(E1, E2, E3) +| + +- dma_memory_read(E1, E2, (T *)(E3), E4) ++ dma_memory_read(E1, E2, E3, E4) +| +- dma_memory_write(E1, E2, (T *)(E3), E4) ++ dma_memory_write(E1, E2, E3, E4) +) diff --git a/scripts/coccinelle/memory-region-housekeeping.cocci b/scripts/coccinelle/memory-region-housekeeping.cocci new file mode 100644 index 0000000000..c768d8140a --- /dev/null +++ b/scripts/coccinelle/memory-region-housekeeping.cocci @@ -0,0 +1,159 @@ +/* + Usage: + + spatch \ + --macro-file scripts/cocci-macro-file.h \ + --sp-file scripts/coccinelle/memory-region-housekeeping.cocci \ + --keep-comments \ + --in-place \ + --dir . + +*/ + + +// Replace memory_region_init_ram(readonly) by memory_region_init_rom() +@@ +expression E1, E2, E3, E4, E5; +symbol true; +@@ +( +- memory_region_init_ram(E1, E2, E3, E4, E5); ++ memory_region_init_rom(E1, E2, E3, E4, E5); + ... WHEN != E1 +- memory_region_set_readonly(E1, true); +| +- memory_region_init_ram_nomigrate(E1, E2, E3, E4, E5); ++ memory_region_init_rom_nomigrate(E1, E2, E3, E4, E5); + ... WHEN != E1 +- memory_region_set_readonly(E1, true); +) + + +@possible_memory_region_init_rom@ +expression E1, E2, E3, E4, E5; +position p; +@@ +( + memory_region_init_ram@p(E1, E2, E3, E4, E5); + ... + memory_region_set_readonly(E1, true); +| + memory_region_init_ram_nomigrate@p(E1, E2, E3, E4, E5); + ... + memory_region_set_readonly(E1, true); +) +@script:python@ +p << possible_memory_region_init_rom.p; +@@ +cocci.print_main("potential use of memory_region_init_rom*() in ", p) + + +// Do not call memory_region_set_readonly() on ROM alias +@@ +expression ROM, E1, E2, E3, E4; +expression ALIAS, E5, E6, E7, E8; +@@ +( + memory_region_init_rom(ROM, E1, E2, E3, E4); +| + memory_region_init_rom_nomigrate(ROM, E1, E2, E3, E4); +) + ... + memory_region_init_alias(ALIAS, E5, E6, ROM, E7, E8); +- memory_region_set_readonly(ALIAS, true); + + +// Replace by-hand memory_region_init_ram_nomigrate/vmstate_register_ram +// code sequences with use of the new memory_region_init_ram function. +// Similarly for the _rom and _rom_device functions. +// We don't try to replace sequences with a non-NULL owner, because +// there are none in the tree that can be automatically converted +// (and only a handful that can be manually converted). +@@ +expression MR; +expression NAME; +expression SIZE; +expression ERRP; +@@ +-memory_region_init_ram_nomigrate(MR, NULL, NAME, SIZE, ERRP); ++memory_region_init_ram(MR, NULL, NAME, SIZE, ERRP); + ... +-vmstate_register_ram_global(MR); +@@ +expression MR; +expression NAME; +expression SIZE; +expression ERRP; +@@ +-memory_region_init_rom_nomigrate(MR, NULL, NAME, SIZE, ERRP); ++memory_region_init_rom(MR, NULL, NAME, SIZE, ERRP); + ... +-vmstate_register_ram_global(MR); +@@ +expression MR; +expression OPS; +expression OPAQUE; +expression NAME; +expression SIZE; +expression ERRP; +@@ +-memory_region_init_rom_device_nomigrate(MR, NULL, OPS, OPAQUE, NAME, SIZE, ERRP); ++memory_region_init_rom_device(MR, NULL, OPS, OPAQUE, NAME, SIZE, ERRP); + ... +-vmstate_register_ram_global(MR); + + +// Device is owner +@@ +typedef DeviceState; +identifier device_fn, dev, obj; +expression E1, E2, E3, E4, E5; +@@ +static void device_fn(DeviceState *dev, ...) +{ + ... + Object *obj = OBJECT(dev); + <+... +( +- memory_region_init(E1, NULL, E2, E3); ++ memory_region_init(E1, obj, E2, E3); +| +- memory_region_init_io(E1, NULL, E2, E3, E4, E5); ++ memory_region_init_io(E1, obj, E2, E3, E4, E5); +| +- memory_region_init_alias(E1, NULL, E2, E3, E4, E5); ++ memory_region_init_alias(E1, obj, E2, E3, E4, E5); +| +- memory_region_init_rom(E1, NULL, E2, E3, E4); ++ memory_region_init_rom(E1, obj, E2, E3, E4); +| +- memory_region_init_ram_shared_nomigrate(E1, NULL, E2, E3, E4, E5); ++ memory_region_init_ram_shared_nomigrate(E1, obj, E2, E3, E4, E5); +) + ...+> +} +@@ +identifier device_fn, dev; +expression E1, E2, E3, E4, E5; +@@ +static void device_fn(DeviceState *dev, ...) +{ + <+... +( +- memory_region_init(E1, NULL, E2, E3); ++ memory_region_init(E1, OBJECT(dev), E2, E3); +| +- memory_region_init_io(E1, NULL, E2, E3, E4, E5); ++ memory_region_init_io(E1, OBJECT(dev), E2, E3, E4, E5); +| +- memory_region_init_alias(E1, NULL, E2, E3, E4, E5); ++ memory_region_init_alias(E1, OBJECT(dev), E2, E3, E4, E5); +| +- memory_region_init_rom(E1, NULL, E2, E3, E4); ++ memory_region_init_rom(E1, OBJECT(dev), E2, E3, E4); +| +- memory_region_init_ram_shared_nomigrate(E1, NULL, E2, E3, E4, E5); ++ memory_region_init_ram_shared_nomigrate(E1, OBJECT(dev), E2, E3, E4, E5); +) + ...+> +} diff --git a/scripts/coccinelle/memory-region-init-ram.cocci b/scripts/coccinelle/memory-region-init-ram.cocci deleted file mode 100644 index d290150872..0000000000 --- a/scripts/coccinelle/memory-region-init-ram.cocci +++ /dev/null @@ -1,38 +0,0 @@ -// Replace by-hand memory_region_init_ram_nomigrate/vmstate_register_ram -// code sequences with use of the new memory_region_init_ram function. -// Similarly for the _rom and _rom_device functions. -// We don't try to replace sequences with a non-NULL owner, because -// there are none in the tree that can be automatically converted -// (and only a handful that can be manually converted). -@@ -expression MR; -expression NAME; -expression SIZE; -expression ERRP; -@@ --memory_region_init_ram_nomigrate(MR, NULL, NAME, SIZE, ERRP); -+memory_region_init_ram(MR, NULL, NAME, SIZE, ERRP); - ... --vmstate_register_ram_global(MR); -@@ -expression MR; -expression NAME; -expression SIZE; -expression ERRP; -@@ --memory_region_init_rom_nomigrate(MR, NULL, NAME, SIZE, ERRP); -+memory_region_init_rom(MR, NULL, NAME, SIZE, ERRP); - ... --vmstate_register_ram_global(MR); -@@ -expression MR; -expression OPS; -expression OPAQUE; -expression NAME; -expression SIZE; -expression ERRP; -@@ --memory_region_init_rom_device_nomigrate(MR, NULL, OPS, OPAQUE, NAME, SIZE, ERRP); -+memory_region_init_rom_device(MR, NULL, OPS, OPAQUE, NAME, SIZE, ERRP); - ... --vmstate_register_ram_global(MR); diff --git a/scripts/coverity-scan/coverity-scan.docker b/scripts/coverity-scan/coverity-scan.docker new file mode 100644 index 0000000000..a4f64d1283 --- /dev/null +++ b/scripts/coverity-scan/coverity-scan.docker @@ -0,0 +1,131 @@ +# syntax=docker/dockerfile:1.0.0-experimental +# +# Docker setup for running the "Coverity Scan" tools over the source +# tree and uploading them to the website, as per +# https://scan.coverity.com/projects/qemu/builds/new +# We do this on a fixed config (currently Fedora 30 with a known +# set of dependencies and a configure command that enables a specific +# set of options) so that random changes don't result in our accidentally +# dropping some files from the scan. +# +# We don't build on top of the fedora.docker file because we don't +# want to accidentally change or break the scan config when that +# is updated. + +# The work of actually doing the build is handled by the +# run-coverity-scan script. + +FROM fedora:30 +ENV PACKAGES \ + alsa-lib-devel \ + bc \ + bison \ + brlapi-devel \ + bzip2 \ + bzip2-devel \ + ccache \ + clang \ + curl \ + cyrus-sasl-devel \ + dbus-daemon \ + device-mapper-multipath-devel \ + findutils \ + flex \ + gcc \ + gcc-c++ \ + gettext \ + git \ + glib2-devel \ + glusterfs-api-devel \ + gnutls-devel \ + gtk3-devel \ + hostname \ + libaio-devel \ + libasan \ + libattr-devel \ + libblockdev-mpath-devel \ + libcap-devel \ + libcap-ng-devel \ + libcurl-devel \ + libepoxy-devel \ + libfdt-devel \ + libgbm-devel \ + libiscsi-devel \ + libjpeg-devel \ + libpmem-devel \ + libnfs-devel \ + libpng-devel \ + librbd-devel \ + libseccomp-devel \ + libssh-devel \ + libubsan \ + libudev-devel \ + libusbx-devel \ + libxml2-devel \ + libzstd-devel \ + llvm \ + lzo-devel \ + make \ + mingw32-bzip2 \ + mingw32-curl \ + mingw32-glib2 \ + mingw32-gmp \ + mingw32-gnutls \ + mingw32-gtk3 \ + mingw32-libjpeg-turbo \ + mingw32-libpng \ + mingw32-libtasn1 \ + mingw32-nettle \ + mingw32-nsis \ + mingw32-pixman \ + mingw32-pkg-config \ + mingw32-SDL2 \ + mingw64-bzip2 \ + mingw64-curl \ + mingw64-glib2 \ + mingw64-gmp \ + mingw64-gnutls \ + mingw64-gtk3 \ + mingw64-libjpeg-turbo \ + mingw64-libpng \ + mingw64-libtasn1 \ + mingw64-nettle \ + mingw64-pixman \ + mingw64-pkg-config \ + mingw64-SDL2 \ + ncurses-devel \ + nettle-devel \ + nss-devel \ + numactl-devel \ + perl \ + perl-Test-Harness \ + pixman-devel \ + pulseaudio-libs-devel \ + python3 \ + python3-sphinx \ + PyYAML \ + rdma-core-devel \ + SDL2-devel \ + snappy-devel \ + sparse \ + spice-server-devel \ + systemd-devel \ + systemtap-sdt-devel \ + tar \ + texinfo \ + usbredir-devel \ + virglrenderer-devel \ + vte291-devel \ + wget \ + which \ + xen-devel \ + xfsprogs-devel \ + zlib-devel +ENV QEMU_CONFIGURE_OPTS --python=/usr/bin/python3 + +RUN dnf install -y $PACKAGES +RUN rpm -q $PACKAGES | sort > /packages.txt +ENV PATH $PATH:/usr/libexec/python3-sphinx/ +ENV COVERITY_TOOL_BASE=/coverity-tools +COPY run-coverity-scan run-coverity-scan +RUN --mount=type=secret,id=coverity.token,required ./run-coverity-scan --update-tools-only --tokenfile /run/secrets/coverity.token diff --git a/scripts/coverity-scan/run-coverity-scan b/scripts/coverity-scan/run-coverity-scan new file mode 100755 index 0000000000..2e067ef5cf --- /dev/null +++ b/scripts/coverity-scan/run-coverity-scan @@ -0,0 +1,401 @@ +#!/bin/sh -e + +# Upload a created tarball to Coverity Scan, as per +# https://scan.coverity.com/projects/qemu/builds/new + +# This work is licensed under the terms of the GNU GPL version 2, +# or (at your option) any later version. +# See the COPYING file in the top-level directory. +# +# Copyright (c) 2017-2020 Linaro Limited +# Written by Peter Maydell + +# Note that this script will automatically download and +# run the (closed-source) coverity build tools, so don't +# use it if you don't trust them! + +# This script assumes that you're running it from a QEMU source +# tree, and that tree is a fresh clean one, because we do an in-tree +# build. (This is necessary so that the filenames that the Coverity +# Scan server sees are relative paths that match up with the component +# regular expressions it uses; an out-of-tree build won't work for this.) +# The host machine should have as many of QEMU's dependencies +# installed as possible, for maximum coverity coverage. + +# To do an upload you need to be a maintainer in the Coverity online +# service, and you will need to know the "Coverity token", which is a +# secret 8 digit hex string. You can find that from the web UI in the +# project settings, if you have maintainer access there. + +# Command line options: +# --dry-run : run the tools, but don't actually do the upload +# --docker : create and work inside a docker container +# --update-tools-only : update the cached copy of the tools, but don't run them +# --tokenfile : file to read Coverity token from +# --version ver : specify version being analyzed (default: ask git) +# --description desc : specify description of this version (default: ask git) +# --srcdir : QEMU source tree to analyze (default: current working dir) +# --results-tarball : path to copy the results tarball to (default: don't +# copy it anywhere, just upload it) +# --src-tarball : tarball to untar into src dir (default: none); this +# is intended mainly for internal use by the Docker support +# +# User-specifiable environment variables: +# COVERITY_TOKEN -- Coverity token +# COVERITY_EMAIL -- the email address to use for uploads (default: +# looks at your git user.email config) +# COVERITY_BUILD_CMD -- make command (default: 'make -jN' where N is +# number of CPUs as determined by 'nproc') +# COVERITY_TOOL_BASE -- set to directory to put coverity tools +# (default: /tmp/coverity-tools) +# +# You must specify the token, either by environment variable or by +# putting it in a file and using --tokenfile. Everything else has +# a reasonable default if this is run from a git tree. + +check_upload_permissions() { + # Check whether we can do an upload to the server; will exit the script + # with status 1 if the check failed (usually a bad token); + # will exit the script with status 0 if the check indicated that we + # can't upload yet (ie we are at quota) + # Assumes that PROJTOKEN, PROJNAME and DRYRUN have been initialized. + + echo "Checking upload permissions..." + + if ! up_perm="$(wget https://scan.coverity.com/api/upload_permitted --post-data "token=$PROJTOKEN&project=$PROJNAME" -q -O -)"; then + echo "Coverity Scan API access denied: bad token?" + exit 1 + fi + + # Really up_perm is a JSON response with either + # {upload_permitted:true} or {next_upload_permitted_at:} + # We do some hacky string parsing instead of properly parsing it. + case "$up_perm" in + *upload_permitted*true*) + echo "Coverity Scan: upload permitted" + ;; + *next_upload_permitted_at*) + if [ "$DRYRUN" = yes ]; then + echo "Coverity Scan: upload quota reached, continuing dry run" + else + echo "Coverity Scan: upload quota reached; stopping here" + # Exit success as this isn't a build error. + exit 0 + fi + ;; + *) + echo "Coverity Scan upload check: unexpected result $up_perm" + exit 1 + ;; + esac +} + + +update_coverity_tools () { + # Check for whether we need to download the Coverity tools + # (either because we don't have a copy, or because it's out of date) + # Assumes that COVERITY_TOOL_BASE, PROJTOKEN and PROJNAME are set. + + mkdir -p "$COVERITY_TOOL_BASE" + cd "$COVERITY_TOOL_BASE" + + echo "Checking for new version of coverity build tools..." + wget https://scan.coverity.com/download/linux64 --post-data "token=$PROJTOKEN&project=$PROJNAME&md5=1" -O coverity_tool.md5.new + + if ! cmp -s coverity_tool.md5 coverity_tool.md5.new; then + # out of date md5 or no md5: download new build tool + # blow away the old build tool + echo "Downloading coverity build tools..." + rm -rf coverity_tool coverity_tool.tgz + wget https://scan.coverity.com/download/linux64 --post-data "token=$PROJTOKEN&project=$PROJNAME" -O coverity_tool.tgz + if ! (cat coverity_tool.md5.new; echo " coverity_tool.tgz") | md5sum -c --status; then + echo "Downloaded tarball didn't match md5sum!" + exit 1 + fi + # extract the new one, keeping it corralled in a 'coverity_tool' directory + echo "Unpacking coverity build tools..." + mkdir -p coverity_tool + cd coverity_tool + tar xf ../coverity_tool.tgz + cd .. + mv coverity_tool.md5.new coverity_tool.md5 + fi + + rm -f coverity_tool.md5.new +} + + +# Check user-provided environment variables and arguments +DRYRUN=no +UPDATE_ONLY=no +DOCKER=no + +while [ "$#" -ge 1 ]; do + case "$1" in + --dry-run) + shift + DRYRUN=yes + ;; + --update-tools-only) + shift + UPDATE_ONLY=yes + ;; + --version) + shift + if [ $# -eq 0 ]; then + echo "--version needs an argument" + exit 1 + fi + VERSION="$1" + shift + ;; + --description) + shift + if [ $# -eq 0 ]; then + echo "--description needs an argument" + exit 1 + fi + DESCRIPTION="$1" + shift + ;; + --tokenfile) + shift + if [ $# -eq 0 ]; then + echo "--tokenfile needs an argument" + exit 1 + fi + COVERITY_TOKEN="$(cat "$1")" + shift + ;; + --srcdir) + shift + if [ $# -eq 0 ]; then + echo "--srcdir needs an argument" + exit 1 + fi + SRCDIR="$1" + shift + ;; + --results-tarball) + shift + if [ $# -eq 0 ]; then + echo "--results-tarball needs an argument" + exit 1 + fi + RESULTSTARBALL="$1" + shift + ;; + --src-tarball) + shift + if [ $# -eq 0 ]; then + echo "--src-tarball needs an argument" + exit 1 + fi + SRCTARBALL="$1" + shift + ;; + --docker) + DOCKER=yes + shift + ;; + *) + echo "Unexpected argument '$1'" + exit 1 + ;; + esac +done + +if [ -z "$COVERITY_TOKEN" ]; then + echo "COVERITY_TOKEN environment variable not set" + exit 1 +fi + +if [ -z "$COVERITY_BUILD_CMD" ]; then + NPROC=$(nproc) + COVERITY_BUILD_CMD="make -j$NPROC" + echo "COVERITY_BUILD_CMD: using default '$COVERITY_BUILD_CMD'" +fi + +if [ -z "$COVERITY_TOOL_BASE" ]; then + echo "COVERITY_TOOL_BASE: using default /tmp/coverity-tools" + COVERITY_TOOL_BASE=/tmp/coverity-tools +fi + +if [ -z "$SRCDIR" ]; then + SRCDIR="$PWD" +fi + +PROJTOKEN="$COVERITY_TOKEN" +PROJNAME=QEMU +TARBALL=cov-int.tar.xz + +if [ "$UPDATE_ONLY" = yes ] && [ "$DOCKER" = yes ]; then + echo "Combining --docker and --update-only is not supported" + exit 1 +fi + +if [ "$UPDATE_ONLY" = yes ]; then + # Just do the tools update; we don't need to check whether + # we are in a source tree or have upload rights for this, + # so do it before some of the command line and source tree checks. + update_coverity_tools + exit 0 +fi + +if [ ! -e "$SRCDIR" ]; then + mkdir "$SRCDIR" +fi + +cd "$SRCDIR" + +if [ ! -z "$SRCTARBALL" ]; then + echo "Untarring source tarball into $SRCDIR..." + tar xvf "$SRCTARBALL" +fi + +echo "Checking this is a QEMU source tree..." +if ! [ -e "$SRCDIR/VERSION" ]; then + echo "Not in a QEMU source tree?" + exit 1 +fi + +# Fill in defaults used by the non-update-only process +if [ -z "$VERSION" ]; then + VERSION="$(git describe --always HEAD)" +fi + +if [ -z "$DESCRIPTION" ]; then + DESCRIPTION="$(git rev-parse HEAD)" +fi + +if [ -z "$COVERITY_EMAIL" ]; then + COVERITY_EMAIL="$(git config user.email)" +fi + +# Run ourselves inside docker if that's what the user wants +if [ "$DOCKER" = yes ]; then + # build docker container including the coverity-scan tools + # Put the Coverity token into a temporary file that only + # we have read access to, and then pass it to docker build + # using --secret. This requires at least Docker 18.09. + # Mostly what we are trying to do here is ensure we don't leak + # the token into the Docker image. + umask 077 + SECRETDIR=$(mktemp -d) + if [ -z "$SECRETDIR" ]; then + echo "Failed to create temporary directory" + exit 1 + fi + trap 'rm -rf "$SECRETDIR"' INT TERM EXIT + echo "Created temporary directory $SECRETDIR" + SECRET="$SECRETDIR/token" + echo "$COVERITY_TOKEN" > "$SECRET" + echo "Building docker container..." + # TODO: This re-downloads the tools every time, rather than + # caching and reusing the image produced with the downloaded tools. + # Not sure why. + # TODO: how do you get 'docker build' to print the output of the + # commands it is running to its stdout? This would be useful for debug. + DOCKER_BUILDKIT=1 docker build -t coverity-scanner \ + --secret id=coverity.token,src="$SECRET" \ + -f scripts/coverity-scan/coverity-scan.docker \ + scripts/coverity-scan + echo "Archiving sources to be analyzed..." + ./scripts/archive-source.sh "$SECRETDIR/qemu-sources.tgz" + if [ "$DRYRUN" = yes ]; then + DRYRUNARG=--dry-run + fi + echo "Running scanner..." + # If we need to capture the output tarball, get the inner run to + # save it to the secrets directory so we can copy it out before the + # directory is cleaned up. + if [ ! -z "$RESULTSTARBALL" ]; then + RTARGS="--results-tarball /work/cov-int.tar.xz" + else + RTARGS="" + fi + # Arrange for this docker run to get access to the sources with -v. + # We pass through all the configuration from the outer script to the inner. + export COVERITY_EMAIL COVERITY_BUILD_CMD + docker run -it --env COVERITY_EMAIL --env COVERITY_BUILD_CMD \ + -v "$SECRETDIR:/work" coverity-scanner \ + ./run-coverity-scan --version "$VERSION" \ + --description "$DESCRIPTION" $DRYRUNARG --tokenfile /work/token \ + --srcdir /qemu --src-tarball /work/qemu-sources.tgz $RTARGS + if [ ! -z "$RESULTSTARBALL" ]; then + echo "Copying results tarball to $RESULTSTARBALL..." + cp "$SECRETDIR/cov-int.tar.xz" "$RESULTSTARBALL" + fi + echo "Docker work complete." + exit 0 +fi + +# Otherwise, continue with the full build and upload process. + +check_upload_permissions + +update_coverity_tools + +TOOLBIN="$(cd "$COVERITY_TOOL_BASE" && echo $PWD/coverity_tool/cov-analysis-*/bin)" + +if ! test -x "$TOOLBIN/cov-build"; then + echo "Couldn't find cov-build in the coverity build-tool directory??" + exit 1 +fi + +export PATH="$TOOLBIN:$PATH" + +cd "$SRCDIR" + +echo "Doing make distclean..." +make distclean + +echo "Configuring..." +# We configure with a fixed set of enables here to ensure that we don't +# accidentally reduce the scope of the analysis by doing the build on +# the system that's missing a dependency that we need to build part of +# the codebase. +./configure --disable-modules --enable-sdl --enable-gtk \ + --enable-opengl --enable-vte --enable-gnutls \ + --enable-nettle --enable-curses --enable-curl \ + --audio-drv-list=oss,alsa,sdl,pa --enable-virtfs \ + --enable-vnc --enable-vnc-sasl --enable-vnc-jpeg --enable-vnc-png \ + --enable-xen --enable-brlapi \ + --enable-linux-aio --enable-attr \ + --enable-cap-ng --enable-trace-backends=log --enable-spice --enable-rbd \ + --enable-xfsctl --enable-libusb --enable-usb-redir \ + --enable-libiscsi --enable-libnfs --enable-seccomp \ + --enable-tpm --enable-libssh --enable-lzo --enable-snappy --enable-bzip2 \ + --enable-numa --enable-rdma --enable-smartcard --enable-virglrenderer \ + --enable-mpath --enable-libxml2 --enable-glusterfs \ + --enable-virtfs --enable-zstd + +echo "Making libqemustub.a..." +make libqemustub.a + +echo "Running cov-build..." +rm -rf cov-int +mkdir cov-int +cov-build --dir cov-int $COVERITY_BUILD_CMD + +echo "Creating results tarball..." +tar cvf - cov-int | xz > "$TARBALL" + +if [ ! -z "$RESULTSTARBALL" ]; then + echo "Copying results tarball to $RESULTSTARBALL..." + cp "$TARBALL" "$RESULTSTARBALL" +fi + +echo "Uploading results tarball..." + +if [ "$DRYRUN" = yes ]; then + echo "Dry run only, not uploading $TARBALL" + exit 0 +fi + +curl --form token="$PROJTOKEN" --form email="$COVERITY_EMAIL" \ + --form file=@"$TARBALL" --form version="$VERSION" \ + --form description="$DESCRIPTION" \ + https://scan.coverity.com/builds?project="$PROJNAME" + +echo "Done." diff --git a/scripts/decodetree.py b/scripts/decodetree.py index d8c59cab60..46ab917807 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 @@ -1025,7 +1025,7 @@ class SizeTree: if extracted < self.width: output(ind, 'insn = ', decode_function, '_load_bytes(ctx, insn, {0}, {1});\n' - .format(extracted / 8, self.width / 8)); + .format(extracted // 8, self.width // 8)); extracted = self.width # Attempt to aid the compiler in producing compact switch statements. @@ -1079,7 +1079,7 @@ class SizeLeaf: if extracted < self.width: output(ind, 'insn = ', decode_function, '_load_bytes(ctx, insn, {0}, {1});\n' - .format(extracted / 8, self.width / 8)); + .format(extracted // 8, self.width // 8)); extracted = self.width output(ind, 'return insn;\n') # end SizeLeaf 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/gensyscalls.sh b/scripts/gensyscalls.sh new file mode 100755 index 0000000000..b7b8456f63 --- /dev/null +++ b/scripts/gensyscalls.sh @@ -0,0 +1,102 @@ +#!/bin/sh +# +# Update syscall_nr.h files from linux headers asm-generic/unistd.h +# +# This code is licensed under the GPL version 2 or later. See +# the COPYING file in the top-level directory. +# + +linux="$1" +output="$2" + +TMP=$(mktemp -d) + +if [ "$linux" = "" ] ; then + echo "Needs path to linux source tree" 1>&2 + exit 1 +fi + +if [ "$output" = "" ] ; then + output="$PWD" +fi + +upper() +{ + echo "$1" | tr "[:lower:]" "[:upper:]" | tr "[:punct:]" "_" +} + +qemu_arch() +{ + case "$1" in + arm64) + echo "aarch64" + ;; + *) + echo "$1" + ;; + esac +} + +read_includes() +{ + arch=$1 + bits=$2 + + cpp -P -nostdinc -fdirectives-only \ + -D_UAPI_ASM_$(upper ${arch})_BITSPERLONG_H \ + -D__BITS_PER_LONG=${bits} \ + -I${linux}/arch/${arch}/include/uapi/ \ + -I${linux}/include/uapi \ + -I${TMP} \ + "${linux}/arch/${arch}/include/uapi/asm/unistd.h" +} + +filter_defines() +{ + grep -e "#define __NR_" -e "#define __NR3264" +} + +rename_defines() +{ + sed "s/ __NR_/ TARGET_NR_/g;s/(__NR_/(TARGET_NR_/g" +} + +evaluate_values() +{ + sed "s/#define TARGET_NR_/QEMU TARGET_NR_/" | \ + cpp -P -nostdinc | \ + sed "s/^QEMU /#define /" +} + +generate_syscall_nr() +{ + arch=$1 + bits=$2 + file="$3" + guard="$(upper LINUX_USER_$(qemu_arch $arch)_$(basename "$file"))" + + (echo "/*" + echo " * This file contains the system call numbers." + echo " * Do not modify." + echo " * This file is generated by scripts/gensyscalls.sh" + echo " */" + echo "#ifndef ${guard}" + echo "#define ${guard}" + echo + read_includes $arch $bits | filter_defines | rename_defines | \ + evaluate_values | sort -n -k 3 + echo + echo "#endif /* ${guard} */" + echo) > "$file" +} + +mkdir "$TMP/asm" +> "$TMP/asm/bitsperlong.h" + +generate_syscall_nr arm64 64 "$output/linux-user/aarch64/syscall_nr.h" +generate_syscall_nr nios2 32 "$output/linux-user/nios2/syscall_nr.h" +generate_syscall_nr openrisc 32 "$output/linux-user/openrisc/syscall_nr.h" + +generate_syscall_nr riscv 32 "$output/linux-user/riscv/syscall32_nr.h" +generate_syscall_nr riscv 64 "$output/linux-user/riscv/syscall64_nr.h" +rm -fr "$TMP" diff --git a/scripts/get_maintainer.pl b/scripts/get_maintainer.pl index 27991eb1cf..271f5ff42a 100755 --- a/scripts/get_maintainer.pl +++ b/scripts/get_maintainer.pl @@ -795,7 +795,8 @@ sub top_of_tree { && (-f "${lk_path}Makefile") && (-d "${lk_path}docs") && (-f "${lk_path}VERSION") - && (-f "${lk_path}vl.c")) { + && (-d "${lk_path}linux-user/") + && (-d "${lk_path}softmmu/")) { return 1; } return 0; diff --git a/scripts/git-submodule.sh b/scripts/git-submodule.sh index 98ca0f2737..65ed877aef 100755 --- a/scripts/git-submodule.sh +++ b/scripts/git-submodule.sh @@ -59,10 +59,14 @@ status) fi test -f "$substat" || exit 1 - CURSTATUS=$($GIT submodule status $modules) - OLDSTATUS=$(cat $substat) - test "$CURSTATUS" = "$OLDSTATUS" - exit $? + for module in $modules; do + CURSTATUS=$($GIT submodule status $module) + OLDSTATUS=$(cat $substat | grep $module) + if test "$CURSTATUS" != "$OLDSTATUS"; then + exit 1 + fi + done + exit 0 ;; update) if test -z "$maybe_modules" diff --git a/scripts/git.orderfile b/scripts/git.orderfile index e89790941c..73fd818d7f 100644 --- a/scripts/git.orderfile +++ b/scripts/git.orderfile @@ -11,6 +11,7 @@ # Documentation docs/* +*.rst *.texi # build system @@ -22,8 +23,14 @@ Makefile* qapi/*.json qga/*.json +# semantic patches +*.cocci + # headers *.h +# decoding tree specification +*.decode + # code *.c diff --git a/scripts/hxtool b/scripts/hxtool index 7d7c4289e3..7b1452f3cf 100644 --- a/scripts/hxtool +++ b/scripts/hxtool @@ -7,7 +7,7 @@ hxtoh() case $str in HXCOMM*) ;; - STEXI*|ETEXI*) flag=$(($flag^1)) + SRST*|ERST*) flag=$(($flag^1)) ;; *) test $flag -eq 1 && printf "%s\n" "$str" @@ -16,53 +16,8 @@ hxtoh() done } -print_texi_heading() -{ - if test "$*" != ""; then - title="$*" - printf "@subsection %s\n" "${title%:}" - fi -} - -hxtotexi() -{ - flag=0 - line=1 - while read -r str; do - case "$str" in - HXCOMM*) - ;; - STEXI*) - if test $flag -eq 1 ; then - printf "line %d: syntax error: expected ETEXI, found '%s'\n" "$line" "$str" >&2 - exit 1 - fi - flag=1 - ;; - ETEXI*) - if test $flag -ne 1 ; then - printf "line %d: syntax error: expected STEXI, found '%s'\n" "$line" "$str" >&2 - exit 1 - fi - flag=0 - ;; - DEFHEADING*) - print_texi_heading "$(expr "$str" : "DEFHEADING(\(.*\))")" - ;; - ARCHHEADING*) - print_texi_heading "$(expr "$str" : "ARCHHEADING(\(.*\),.*)")" - ;; - *) - test $flag -eq 1 && printf '%s\n' "$str" - ;; - esac - line=$((line+1)) - done -} - case "$1" in "-h") hxtoh ;; -"-t") hxtotexi ;; *) exit 1 ;; esac diff --git a/scripts/hxtool-conv.pl b/scripts/hxtool-conv.pl new file mode 100755 index 0000000000..eede40b346 --- /dev/null +++ b/scripts/hxtool-conv.pl @@ -0,0 +1,137 @@ +#!/usr/bin/perl -w +# +# Script to convert .hx file STEXI/ETEXI blocks to SRST/ERST +# +# Copyright (C) 2020 Linaro +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# (at your option) any later version. See the COPYING file in the +# top-level directory. + +# This script was only ever intended as a one-off conversion operation. +# Please excuse the places where it is a bit hacky. +# Some manual intervention after the conversion is expected, as are +# some warnings from makeinfo. +# Warning: this script is not idempotent: don't try to run it on +# a .hx file that already has SRST/ERST sections. + +# Expected usage: +# scripts/hxtool-conv.pl file.hx > file.hx.new + +use utf8; + +my $reading_texi = 0; +my $texiblock = ''; +my @tables = (); + +sub update_tables($) { + my ($texi) = @_; + # Update our list of open table directives: every @table + # line in the texi fragment is added to the list, and every + # @end table line means we remove an entry from the list. + # If this fragment had a completely self contained table with + # both the @table and @end table lines, this will be a no-op. + foreach (split(/\n/, $texi)) { + push @tables, $_ if /^\@table/; + pop @tables if /^\@end table/; + } +} + +sub only_table_directives($) { + # Return true if every line in the fragment is a start or end table directive + my ($texi) = @_; + foreach (split(/\n/, $texi)) { + return 0 unless /^\@table/ or /^\@end table/; + } + return 1; +} + +sub output_rstblock($) { + # Write the output to /tmp/frag.texi, wrapped in whatever current @table + # lines we need. + my ($texi) = @_; + + # As a special case, if this fragment is only table directives and + # nothing else, update our set of open table directives but otherwise + # ignore it. This avoids emitting an empty SRST/ERST block. + if (only_table_directives($texi)) { + update_tables($texi); + return; + } + + open(my $fragfh, '>', '/tmp/frag.texi'); + # First output the currently active set of open table directives + print $fragfh join("\n", @tables); + # Next, update our list of open table directives. + # We need to do this before we emit the closing table directives + # so that we emit the right number if this fragment had an + # unbalanced set of directives. + update_tables($texi); + # Then emit the texi fragment itself. + print $fragfh "\n$texi\n"; + # Finally, add the necessary closing table directives. + print $fragfh "\@end table\n" x scalar @tables; + close $fragfh; + + # Now invoke makeinfo/pandoc on it and slurp the results into a string + open(my $fh, '-|', "makeinfo --force -o - --docbook " + . "-D 'qemu_system_x86 QEMU_SYSTEM_X86_MACRO' " + . "-D 'qemu_system QEMU_SYSTEM_MACRO' /tmp/frag.texi " + . " | pandoc -f docbook -t rst") + or die "can't start makeinfo/pandoc: $!"; + + binmode $fh, ':encoding(utf8)'; + + print "SRST\n"; + + # Slurp the whole thing into a string so we can do multiline + # string matches on it. + my $rst = do { + local $/ = undef; + <$fh>; + }; + $rst =~ s/^- − /- /gm; + $rst =~ s/“/"/gm; + $rst =~ s/”/"/gm; + $rst =~ s/‘/'/gm; + $rst =~ s/’/'/gm; + $rst =~ s/QEMU_SYSTEM_MACRO/|qemu_system|/g; + $rst =~ s/QEMU_SYSTEM_X86_MACRO/|qemu_system_x86|/g; + $rst =~ s/(?=::\n\n +\|qemu)/.. parsed-literal/g; + $rst =~ s/:\n\n::$/::/gm; + + # Fix up the invalid reference format makeinfo/pandoc emit: + # `Some string here <#anchorname>`__ + # should be: + # :ref:`anchorname` + $rst =~ s/\`[^<`]+\<\#([^>]+)\>\`__/:ref:`$1`/gm; + print $rst; + + close $fh or die "error on close: $!"; + print "ERST\n"; +} + +# Read the whole .hx input file. +while (<>) { + # Always print the current line + print; + if (/STEXI/) { + $reading_texi = 1; + $texiblock = ''; + next; + } + if (/ETEXI/) { + $reading_texi = 0; + # dump RST version of block + output_rstblock($texiblock); + next; + } + if ($reading_texi) { + # Accumulate the texi into a string + # but drop findex entries as they will confuse makeinfo + next if /^\@findex/; + $texiblock .= $_; + } +} + +die "Unexpectedly still in texi block at EOF" if $reading_texi; diff --git a/scripts/kernel-doc b/scripts/kernel-doc new file mode 100755 index 0000000000..030b5c8691 --- /dev/null +++ b/scripts/kernel-doc @@ -0,0 +1,2250 @@ +#!/usr/bin/env perl +# SPDX-License-Identifier: GPL-2.0 + +use warnings; +use strict; + +## Copyright (c) 1998 Michael Zucchi, All Rights Reserved ## +## Copyright (C) 2000, 1 Tim Waugh ## +## Copyright (C) 2001 Simon Huggins ## +## Copyright (C) 2005-2012 Randy Dunlap ## +## Copyright (C) 2012 Dan Luedtke ## +## ## +## #define enhancements by Armin Kuster ## +## Copyright (c) 2000 MontaVista Software, Inc. ## +## ## +## This software falls under the GNU General Public License. ## +## Please read the COPYING file for more information ## + +# 18/01/2001 - Cleanups +# Functions prototyped as foo(void) same as foo() +# Stop eval'ing where we don't need to. +# -- huggie@earth.li + +# 27/06/2001 - Allowed whitespace after initial "/**" and +# allowed comments before function declarations. +# -- Christian Kreibich + +# Still to do: +# - add perldoc documentation +# - Look more closely at some of the scarier bits :) + +# 26/05/2001 - Support for separate source and object trees. +# Return error code. +# Keith Owens + +# 23/09/2001 - Added support for typedefs, structs, enums and unions +# Support for Context section; can be terminated using empty line +# Small fixes (like spaces vs. \s in regex) +# -- Tim Jansen + +# 25/07/2012 - Added support for HTML5 +# -- Dan Luedtke + +sub usage { + my $message = <<"EOF"; +Usage: $0 [OPTION ...] FILE ... + +Read C language source or header FILEs, extract embedded documentation comments, +and print formatted documentation to standard output. + +The documentation comments are identified by "/**" opening comment mark. See +Documentation/doc-guide/kernel-doc.rst for the documentation comment syntax. + +Output format selection (mutually exclusive): + -man Output troff manual page format. This is the default. + -rst Output reStructuredText format. + -none Do not output documentation, only warnings. + +Output selection (mutually exclusive): + -export Only output documentation for symbols that have been + exported using EXPORT_SYMBOL() or EXPORT_SYMBOL_GPL() + in any input FILE or -export-file FILE. + -internal Only output documentation for symbols that have NOT been + exported using EXPORT_SYMBOL() or EXPORT_SYMBOL_GPL() + in any input FILE or -export-file FILE. + -function NAME Only output documentation for the given function(s) + or DOC: section title(s). All other functions and DOC: + sections are ignored. May be specified multiple times. + -nofunction NAME Do NOT output documentation for the given function(s); + only output documentation for the other functions and + DOC: sections. May be specified multiple times. + +Output selection modifiers: + -sphinx-version VER Generate rST syntax for the specified Sphinx version. + Only works with reStructuredTextFormat. + -no-doc-sections Do not output DOC: sections. + -enable-lineno Enable output of #define LINENO lines. Only works with + reStructuredText format. + -export-file FILE Specify an additional FILE in which to look for + EXPORT_SYMBOL() and EXPORT_SYMBOL_GPL(). To be used with + -export or -internal. May be specified multiple times. + +Other parameters: + -v Verbose output, more warnings and other information. + -h Print this help. + +EOF + print $message; + exit 1; +} + +# +# format of comments. +# In the following table, (...)? signifies optional structure. +# (...)* signifies 0 or more structure elements +# /** +# * function_name(:)? (- short description)? +# (* @parameterx: (description of parameter x)?)* +# (* a blank line)? +# * (Description:)? (Description of function)? +# * (section header: (section description)? )* +# (*)?*/ +# +# So .. the trivial example would be: +# +# /** +# * my_function +# */ +# +# If the Description: header tag is omitted, then there must be a blank line +# after the last parameter specification. +# e.g. +# /** +# * my_function - does my stuff +# * @my_arg: its mine damnit +# * +# * Does my stuff explained. +# */ +# +# or, could also use: +# /** +# * my_function - does my stuff +# * @my_arg: its mine damnit +# * Description: Does my stuff explained. +# */ +# etc. +# +# Besides functions you can also write documentation for structs, unions, +# enums and typedefs. Instead of the function name you must write the name +# of the declaration; the struct/union/enum/typedef must always precede +# the name. Nesting of declarations is not supported. +# Use the argument mechanism to document members or constants. +# e.g. +# /** +# * struct my_struct - short description +# * @a: first member +# * @b: second member +# * +# * Longer description +# */ +# struct my_struct { +# int a; +# int b; +# /* private: */ +# int c; +# }; +# +# All descriptions can be multiline, except the short function description. +# +# For really longs structs, you can also describe arguments inside the +# body of the struct. +# eg. +# /** +# * struct my_struct - short description +# * @a: first member +# * @b: second member +# * +# * Longer description +# */ +# struct my_struct { +# int a; +# int b; +# /** +# * @c: This is longer description of C +# * +# * You can use paragraphs to describe arguments +# * using this method. +# */ +# int c; +# }; +# +# This should be use only for struct/enum members. +# +# You can also add additional sections. When documenting kernel functions you +# should document the "Context:" of the function, e.g. whether the functions +# can be called form interrupts. Unlike other sections you can end it with an +# empty line. +# A non-void function should have a "Return:" section describing the return +# value(s). +# Example-sections should contain the string EXAMPLE so that they are marked +# appropriately in DocBook. +# +# Example: +# /** +# * user_function - function that can only be called in user context +# * @a: some argument +# * Context: !in_interrupt() +# * +# * Some description +# * Example: +# * user_function(22); +# */ +# ... +# +# +# All descriptive text is further processed, scanning for the following special +# patterns, which are highlighted appropriately. +# +# 'funcname()' - function +# '$ENVVAR' - environmental variable +# '&struct_name' - name of a structure (up to two words including 'struct') +# '&struct_name.member' - name of a structure member +# '@parameter' - name of a parameter +# '%CONST' - name of a constant. +# '``LITERAL``' - literal string without any spaces on it. + +## init lots of data + +my $errors = 0; +my $warnings = 0; +my $anon_struct_union = 0; + +# match expressions used to find embedded type information +my $type_constant = '\b``([^\`]+)``\b'; +my $type_constant2 = '\%([-_\w]+)'; +my $type_func = '(\w+)\(\)'; +my $type_param = '\@(\w*((\.\w+)|(->\w+))*(\.\.\.)?)'; +my $type_fp_param = '\@(\w+)\(\)'; # Special RST handling for func ptr params +my $type_env = '(\$\w+)'; +my $type_enum = '#(enum\s*([_\w]+))'; +my $type_struct = '#(struct\s*([_\w]+))'; +my $type_typedef = '#(([A-Z][_\w]*))'; +my $type_union = '#(union\s*([_\w]+))'; +my $type_member = '#([_\w]+)(\.|->)([_\w]+)'; +my $type_fallback = '(?!)'; # this never matches +my $type_member_func = $type_member . '\(\)'; + +# Output conversion substitutions. +# One for each output format + +# these are pretty rough +my @highlights_man = ( + [$type_constant, "\$1"], + [$type_constant2, "\$1"], + [$type_func, "\\\\fB\$1\\\\fP"], + [$type_enum, "\\\\fI\$1\\\\fP"], + [$type_struct, "\\\\fI\$1\\\\fP"], + [$type_typedef, "\\\\fI\$1\\\\fP"], + [$type_union, "\\\\fI\$1\\\\fP"], + [$type_param, "\\\\fI\$1\\\\fP"], + [$type_member, "\\\\fI\$1\$2\$3\\\\fP"], + [$type_fallback, "\\\\fI\$1\\\\fP"] + ); +my $blankline_man = ""; + +# rst-mode +my @highlights_rst = ( + [$type_constant, "``\$1``"], + [$type_constant2, "``\$1``"], + # Note: need to escape () to avoid func matching later + [$type_member_func, "\\:c\\:type\\:`\$1\$2\$3\\\\(\\\\) <\$1>`"], + [$type_member, "\\:c\\:type\\:`\$1\$2\$3 <\$1>`"], + [$type_fp_param, "**\$1\\\\(\\\\)**"], + [$type_func, "\$1()"], + [$type_enum, "\\:c\\:type\\:`\$1 <\$2>`"], + [$type_struct, "\\:c\\:type\\:`\$1 <\$2>`"], + [$type_typedef, "\\:c\\:type\\:`\$1 <\$2>`"], + [$type_union, "\\:c\\:type\\:`\$1 <\$2>`"], + # in rst this can refer to any type + [$type_fallback, "\\:c\\:type\\:`\$1`"], + [$type_param, "**\$1**"] + ); +my $blankline_rst = "\n"; + +# read arguments +if ($#ARGV == -1) { + usage(); +} + +my $kernelversion; +my $dohighlight = ""; + +my $verbose = 0; +my $output_mode = "rst"; +my $output_preformatted = 0; +my $no_doc_sections = 0; +my $enable_lineno = 0; +my @highlights = @highlights_rst; +my $blankline = $blankline_rst; +my $modulename = "Kernel API"; + +use constant { + OUTPUT_ALL => 0, # output all symbols and doc sections + OUTPUT_INCLUDE => 1, # output only specified symbols + OUTPUT_EXCLUDE => 2, # output everything except specified symbols + OUTPUT_EXPORTED => 3, # output exported symbols + OUTPUT_INTERNAL => 4, # output non-exported symbols +}; +my $output_selection = OUTPUT_ALL; +my $show_not_found = 0; # No longer used +my $sphinx_version = "0.0"; # if not specified, assume old + +my @export_file_list; + +my @build_time; +if (defined($ENV{'KBUILD_BUILD_TIMESTAMP'}) && + (my $seconds = `date -d"${ENV{'KBUILD_BUILD_TIMESTAMP'}}" +%s`) ne '') { + @build_time = gmtime($seconds); +} else { + @build_time = localtime; +} + +my $man_date = ('January', 'February', 'March', 'April', 'May', 'June', + 'July', 'August', 'September', 'October', + 'November', 'December')[$build_time[4]] . + " " . ($build_time[5]+1900); + +# Essentially these are globals. +# They probably want to be tidied up, made more localised or something. +# CAVEAT EMPTOR! Some of the others I localised may not want to be, which +# could cause "use of undefined value" or other bugs. +my ($function, %function_table, %parametertypes, $declaration_purpose); +my $declaration_start_line; +my ($type, $declaration_name, $return_type); +my ($newsection, $newcontents, $prototype, $brcount, %source_map); + +if (defined($ENV{'KBUILD_VERBOSE'})) { + $verbose = "$ENV{'KBUILD_VERBOSE'}"; +} + +# Generated docbook code is inserted in a template at a point where +# docbook v3.1 requires a non-zero sequence of RefEntry's; see: +# http://www.oasis-open.org/docbook/documentation/reference/html/refentry.html +# We keep track of number of generated entries and generate a dummy +# if needs be to ensure the expanded template can be postprocessed +# into html. +my $section_counter = 0; + +my $lineprefix=""; + +# Parser states +use constant { + STATE_NORMAL => 0, # normal code + STATE_NAME => 1, # looking for function name + STATE_BODY_MAYBE => 2, # body - or maybe more description + STATE_BODY => 3, # the body of the comment + STATE_PROTO => 4, # scanning prototype + STATE_DOCBLOCK => 5, # documentation block + STATE_INLINE => 6, # gathering documentation outside main block +}; +my $state; +my $in_doc_sect; +my $leading_space; + +# Inline documentation state +use constant { + STATE_INLINE_NA => 0, # not applicable ($state != STATE_INLINE) + STATE_INLINE_NAME => 1, # looking for member name (@foo:) + STATE_INLINE_TEXT => 2, # looking for member documentation + STATE_INLINE_END => 3, # done + STATE_INLINE_ERROR => 4, # error - Comment without header was found. + # Spit a warning as it's not + # proper kernel-doc and ignore the rest. +}; +my $inline_doc_state; + +#declaration types: can be +# 'function', 'struct', 'union', 'enum', 'typedef' +my $decl_type; + +my $doc_start = '^/\*\*\s*$'; # Allow whitespace at end of comment start. +my $doc_end = '\*/'; +my $doc_com = '\s*\*\s*'; +my $doc_com_body = '\s*\* ?'; +my $doc_decl = $doc_com . '(\w+)'; +# @params and a strictly limited set of supported section names +my $doc_sect = $doc_com . + '\s*(\@[.\w]+|\@\.\.\.|description|context|returns?|notes?|examples?)\s*:(.*)'; +my $doc_content = $doc_com_body . '(.*)'; +my $doc_block = $doc_com . 'DOC:\s*(.*)?'; +my $doc_inline_start = '^\s*/\*\*\s*$'; +my $doc_inline_sect = '\s*\*\s*(@\s*[\w][\w\.]*\s*):(.*)'; +my $doc_inline_end = '^\s*\*/\s*$'; +my $doc_inline_oneline = '^\s*/\*\*\s*(@[\w\s]+):\s*(.*)\s*\*/\s*$'; +my $export_symbol = '^\s*EXPORT_SYMBOL(_GPL)?\s*\(\s*(\w+)\s*\)\s*;'; + +my %parameterdescs; +my %parameterdesc_start_lines; +my @parameterlist; +my %sections; +my @sectionlist; +my %section_start_lines; +my $sectcheck; +my $struct_actual; + +my $contents = ""; +my $new_start_line = 0; + +# the canonical section names. see also $doc_sect above. +my $section_default = "Description"; # default section +my $section_intro = "Introduction"; +my $section = $section_default; +my $section_context = "Context"; +my $section_return = "Return"; + +my $undescribed = "-- undescribed --"; + +reset_state(); + +while ($ARGV[0] =~ m/^--?(.*)/) { + my $cmd = $1; + shift @ARGV; + if ($cmd eq "man") { + $output_mode = "man"; + @highlights = @highlights_man; + $blankline = $blankline_man; + } elsif ($cmd eq "rst") { + $output_mode = "rst"; + @highlights = @highlights_rst; + $blankline = $blankline_rst; + } elsif ($cmd eq "none") { + $output_mode = "none"; + } elsif ($cmd eq "module") { # not needed for XML, inherits from calling document + $modulename = shift @ARGV; + } elsif ($cmd eq "function") { # to only output specific functions + $output_selection = OUTPUT_INCLUDE; + $function = shift @ARGV; + $function_table{$function} = 1; + } elsif ($cmd eq "nofunction") { # output all except specific functions + $output_selection = OUTPUT_EXCLUDE; + $function = shift @ARGV; + $function_table{$function} = 1; + } elsif ($cmd eq "export") { # only exported symbols + $output_selection = OUTPUT_EXPORTED; + %function_table = (); + } elsif ($cmd eq "internal") { # only non-exported symbols + $output_selection = OUTPUT_INTERNAL; + %function_table = (); + } elsif ($cmd eq "export-file") { + my $file = shift @ARGV; + push(@export_file_list, $file); + } elsif ($cmd eq "v") { + $verbose = 1; + } elsif (($cmd eq "h") || ($cmd eq "help")) { + usage(); + } elsif ($cmd eq 'no-doc-sections') { + $no_doc_sections = 1; + } elsif ($cmd eq 'enable-lineno') { + $enable_lineno = 1; + } elsif ($cmd eq 'show-not-found') { + $show_not_found = 1; # A no-op but don't fail + } elsif ($cmd eq 'sphinx-version') { + $sphinx_version = shift @ARGV; + } else { + # Unknown argument + usage(); + } +} + +# continue execution near EOF; + +# get kernel version from env +sub get_kernel_version() { + my $version = 'unknown kernel version'; + + if (defined($ENV{'KERNELVERSION'})) { + $version = $ENV{'KERNELVERSION'}; + } + return $version; +} + +# +sub print_lineno { + my $lineno = shift; + if ($enable_lineno && defined($lineno)) { + print "#define LINENO " . $lineno . "\n"; + } +} +## +# dumps section contents to arrays/hashes intended for that purpose. +# +sub dump_section { + my $file = shift; + my $name = shift; + my $contents = join "\n", @_; + + if ($name =~ m/$type_param/) { + $name = $1; + $parameterdescs{$name} = $contents; + $sectcheck = $sectcheck . $name . " "; + $parameterdesc_start_lines{$name} = $new_start_line; + $new_start_line = 0; + } elsif ($name eq "@\.\.\.") { + $name = "..."; + $parameterdescs{$name} = $contents; + $sectcheck = $sectcheck . $name . " "; + $parameterdesc_start_lines{$name} = $new_start_line; + $new_start_line = 0; + } else { + if (defined($sections{$name}) && ($sections{$name} ne "")) { + # Only warn on user specified duplicate section names. + if ($name ne $section_default) { + print STDERR "${file}:$.: warning: duplicate section name '$name'\n"; + ++$warnings; + } + $sections{$name} .= $contents; + } else { + $sections{$name} = $contents; + push @sectionlist, $name; + $section_start_lines{$name} = $new_start_line; + $new_start_line = 0; + } + } +} + +## +# dump DOC: section after checking that it should go out +# +sub dump_doc_section { + my $file = shift; + my $name = shift; + my $contents = join "\n", @_; + + if ($no_doc_sections) { + return; + } + + if (($output_selection == OUTPUT_ALL) || + ($output_selection == OUTPUT_INCLUDE && + defined($function_table{$name})) || + ($output_selection == OUTPUT_EXCLUDE && + !defined($function_table{$name}))) + { + dump_section($file, $name, $contents); + output_blockhead({'sectionlist' => \@sectionlist, + 'sections' => \%sections, + 'module' => $modulename, + 'content-only' => ($output_selection != OUTPUT_ALL), }); + } +} + +## +# output function +# +# parameterdescs, a hash. +# function => "function name" +# parameterlist => @list of parameters +# parameterdescs => %parameter descriptions +# sectionlist => @list of sections +# sections => %section descriptions +# + +sub output_highlight { + my $contents = join "\n",@_; + my $line; + +# DEBUG +# if (!defined $contents) { +# use Carp; +# confess "output_highlight got called with no args?\n"; +# } + +# print STDERR "contents b4:$contents\n"; + eval $dohighlight; + die $@ if $@; +# print STDERR "contents af:$contents\n"; + + foreach $line (split "\n", $contents) { + if (! $output_preformatted) { + $line =~ s/^\s*//; + } + if ($line eq ""){ + if (! $output_preformatted) { + print $lineprefix, $blankline; + } + } else { + if ($output_mode eq "man" && substr($line, 0, 1) eq ".") { + print "\\&$line"; + } else { + print $lineprefix, $line; + } + } + print "\n"; + } +} + +## +# output function in man +sub output_function_man(%) { + my %args = %{$_[0]}; + my ($parameter, $section); + my $count; + + print ".TH \"$args{'function'}\" 9 \"$args{'function'}\" \"$man_date\" \"Kernel Hacker's Manual\" LINUX\n"; + + print ".SH NAME\n"; + print $args{'function'} . " \\- " . $args{'purpose'} . "\n"; + + print ".SH SYNOPSIS\n"; + if ($args{'functiontype'} ne "") { + print ".B \"" . $args{'functiontype'} . "\" " . $args{'function'} . "\n"; + } else { + print ".B \"" . $args{'function'} . "\n"; + } + $count = 0; + my $parenth = "("; + my $post = ","; + foreach my $parameter (@{$args{'parameterlist'}}) { + if ($count == $#{$args{'parameterlist'}}) { + $post = ");"; + } + $type = $args{'parametertypes'}{$parameter}; + if ($type =~ m/([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)/) { + # pointer-to-function + print ".BI \"" . $parenth . $1 . "\" " . $parameter . " \") (" . $2 . ")" . $post . "\"\n"; + } else { + $type =~ s/([^\*])$/$1 /; + print ".BI \"" . $parenth . $type . "\" " . $parameter . " \"" . $post . "\"\n"; + } + $count++; + $parenth = ""; + } + + print ".SH ARGUMENTS\n"; + foreach $parameter (@{$args{'parameterlist'}}) { + my $parameter_name = $parameter; + $parameter_name =~ s/\[.*//; + + print ".IP \"" . $parameter . "\" 12\n"; + output_highlight($args{'parameterdescs'}{$parameter_name}); + } + foreach $section (@{$args{'sectionlist'}}) { + print ".SH \"", uc $section, "\"\n"; + output_highlight($args{'sections'}{$section}); + } +} + +## +# output enum in man +sub output_enum_man(%) { + my %args = %{$_[0]}; + my ($parameter, $section); + my $count; + + print ".TH \"$args{'module'}\" 9 \"enum $args{'enum'}\" \"$man_date\" \"API Manual\" LINUX\n"; + + print ".SH NAME\n"; + print "enum " . $args{'enum'} . " \\- " . $args{'purpose'} . "\n"; + + print ".SH SYNOPSIS\n"; + print "enum " . $args{'enum'} . " {\n"; + $count = 0; + foreach my $parameter (@{$args{'parameterlist'}}) { + print ".br\n.BI \" $parameter\"\n"; + if ($count == $#{$args{'parameterlist'}}) { + print "\n};\n"; + last; + } + else { + print ", \n.br\n"; + } + $count++; + } + + print ".SH Constants\n"; + foreach $parameter (@{$args{'parameterlist'}}) { + my $parameter_name = $parameter; + $parameter_name =~ s/\[.*//; + + print ".IP \"" . $parameter . "\" 12\n"; + output_highlight($args{'parameterdescs'}{$parameter_name}); + } + foreach $section (@{$args{'sectionlist'}}) { + print ".SH \"$section\"\n"; + output_highlight($args{'sections'}{$section}); + } +} + +## +# output struct in man +sub output_struct_man(%) { + my %args = %{$_[0]}; + my ($parameter, $section); + + print ".TH \"$args{'module'}\" 9 \"" . $args{'type'} . " " . $args{'struct'} . "\" \"$man_date\" \"API Manual\" LINUX\n"; + + print ".SH NAME\n"; + print $args{'type'} . " " . $args{'struct'} . " \\- " . $args{'purpose'} . "\n"; + + my $declaration = $args{'definition'}; + $declaration =~ s/\t/ /g; + $declaration =~ s/\n/"\n.br\n.BI \"/g; + print ".SH SYNOPSIS\n"; + print $args{'type'} . " " . $args{'struct'} . " {\n.br\n"; + print ".BI \"$declaration\n};\n.br\n\n"; + + print ".SH Members\n"; + foreach $parameter (@{$args{'parameterlist'}}) { + ($parameter =~ /^#/) && next; + + my $parameter_name = $parameter; + $parameter_name =~ s/\[.*//; + + ($args{'parameterdescs'}{$parameter_name} ne $undescribed) || next; + print ".IP \"" . $parameter . "\" 12\n"; + output_highlight($args{'parameterdescs'}{$parameter_name}); + } + foreach $section (@{$args{'sectionlist'}}) { + print ".SH \"$section\"\n"; + output_highlight($args{'sections'}{$section}); + } +} + +## +# output typedef in man +sub output_typedef_man(%) { + my %args = %{$_[0]}; + my ($parameter, $section); + + print ".TH \"$args{'module'}\" 9 \"$args{'typedef'}\" \"$man_date\" \"API Manual\" LINUX\n"; + + print ".SH NAME\n"; + print "typedef " . $args{'typedef'} . " \\- " . $args{'purpose'} . "\n"; + + foreach $section (@{$args{'sectionlist'}}) { + print ".SH \"$section\"\n"; + output_highlight($args{'sections'}{$section}); + } +} + +sub output_blockhead_man(%) { + my %args = %{$_[0]}; + my ($parameter, $section); + my $count; + + print ".TH \"$args{'module'}\" 9 \"$args{'module'}\" \"$man_date\" \"API Manual\" LINUX\n"; + + foreach $section (@{$args{'sectionlist'}}) { + print ".SH \"$section\"\n"; + output_highlight($args{'sections'}{$section}); + } +} + +## +# output in restructured text +# + +# +# This could use some work; it's used to output the DOC: sections, and +# starts by putting out the name of the doc section itself, but that tends +# to duplicate a header already in the template file. +# +sub output_blockhead_rst(%) { + my %args = %{$_[0]}; + my ($parameter, $section); + + foreach $section (@{$args{'sectionlist'}}) { + if ($output_selection != OUTPUT_INCLUDE) { + print "**$section**\n\n"; + } + print_lineno($section_start_lines{$section}); + output_highlight_rst($args{'sections'}{$section}); + print "\n"; + } +} + +# +# Apply the RST highlights to a sub-block of text. +# +sub highlight_block($) { + # The dohighlight kludge requires the text be called $contents + my $contents = shift; + eval $dohighlight; + die $@ if $@; + return $contents; +} + +# +# Regexes used only here. +# +my $sphinx_literal = '^[^.].*::$'; +my $sphinx_cblock = '^\.\.\ +code-block::'; + +sub output_highlight_rst { + my $input = join "\n",@_; + my $output = ""; + my $line; + my $in_literal = 0; + my $litprefix; + my $block = ""; + + foreach $line (split "\n",$input) { + # + # If we're in a literal block, see if we should drop out + # of it. Otherwise pass the line straight through unmunged. + # + if ($in_literal) { + if (! ($line =~ /^\s*$/)) { + # + # If this is the first non-blank line in a literal + # block we need to figure out what the proper indent is. + # + if ($litprefix eq "") { + $line =~ /^(\s*)/; + $litprefix = '^' . $1; + $output .= $line . "\n"; + } elsif (! ($line =~ /$litprefix/)) { + $in_literal = 0; + } else { + $output .= $line . "\n"; + } + } else { + $output .= $line . "\n"; + } + } + # + # Not in a literal block (or just dropped out) + # + if (! $in_literal) { + $block .= $line . "\n"; + if (($line =~ /$sphinx_literal/) || ($line =~ /$sphinx_cblock/)) { + $in_literal = 1; + $litprefix = ""; + $output .= highlight_block($block); + $block = "" + } + } + } + + if ($block) { + $output .= highlight_block($block); + } + foreach $line (split "\n", $output) { + print $lineprefix . $line . "\n"; + } +} + +sub output_function_rst(%) { + my %args = %{$_[0]}; + my ($parameter, $section); + my $oldprefix = $lineprefix; + my $start = ""; + + if ($args{'typedef'}) { + print ".. c:type:: ". $args{'function'} . "\n\n"; + print_lineno($declaration_start_line); + print " **Typedef**: "; + $lineprefix = ""; + output_highlight_rst($args{'purpose'}); + $start = "\n\n**Syntax**\n\n ``"; + } else { + print ".. c:function:: "; + } + if ($args{'functiontype'} ne "") { + $start .= $args{'functiontype'} . " " . $args{'function'} . " ("; + } else { + $start .= $args{'function'} . " ("; + } + print $start; + + my $count = 0; + foreach my $parameter (@{$args{'parameterlist'}}) { + if ($count ne 0) { + print ", "; + } + $count++; + $type = $args{'parametertypes'}{$parameter}; + + if ($type =~ m/([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)/) { + # pointer-to-function + print $1 . $parameter . ") (" . $2 . ")"; + } else { + print $type . " " . $parameter; + } + } + if ($args{'typedef'}) { + print ");``\n\n"; + } else { + print ")\n\n"; + print_lineno($declaration_start_line); + $lineprefix = " "; + output_highlight_rst($args{'purpose'}); + print "\n"; + } + + print "**Parameters**\n\n"; + $lineprefix = " "; + foreach $parameter (@{$args{'parameterlist'}}) { + my $parameter_name = $parameter; + $parameter_name =~ s/\[.*//; + $type = $args{'parametertypes'}{$parameter}; + + if ($type ne "") { + print "``$type $parameter``\n"; + } else { + print "``$parameter``\n"; + } + + print_lineno($parameterdesc_start_lines{$parameter_name}); + + if (defined($args{'parameterdescs'}{$parameter_name}) && + $args{'parameterdescs'}{$parameter_name} ne $undescribed) { + output_highlight_rst($args{'parameterdescs'}{$parameter_name}); + } else { + print " *undescribed*\n"; + } + print "\n"; + } + + $lineprefix = $oldprefix; + output_section_rst(@_); +} + +sub output_section_rst(%) { + my %args = %{$_[0]}; + my $section; + my $oldprefix = $lineprefix; + $lineprefix = ""; + + foreach $section (@{$args{'sectionlist'}}) { + print "**$section**\n\n"; + print_lineno($section_start_lines{$section}); + output_highlight_rst($args{'sections'}{$section}); + print "\n"; + } + print "\n"; + $lineprefix = $oldprefix; +} + +sub output_enum_rst(%) { + my %args = %{$_[0]}; + my ($parameter); + my $oldprefix = $lineprefix; + my $count; + my $name = "enum " . $args{'enum'}; + + print "\n\n.. c:type:: " . $name . "\n\n"; + print_lineno($declaration_start_line); + $lineprefix = " "; + output_highlight_rst($args{'purpose'}); + print "\n"; + + print "**Constants**\n\n"; + $lineprefix = " "; + foreach $parameter (@{$args{'parameterlist'}}) { + print "``$parameter``\n"; + if ($args{'parameterdescs'}{$parameter} ne $undescribed) { + output_highlight_rst($args{'parameterdescs'}{$parameter}); + } else { + print " *undescribed*\n"; + } + print "\n"; + } + + $lineprefix = $oldprefix; + output_section_rst(@_); +} + +sub output_typedef_rst(%) { + my %args = %{$_[0]}; + my ($parameter); + my $oldprefix = $lineprefix; + my $name = "typedef " . $args{'typedef'}; + + print "\n\n.. c:type:: " . $name . "\n\n"; + print_lineno($declaration_start_line); + $lineprefix = " "; + output_highlight_rst($args{'purpose'}); + print "\n"; + + $lineprefix = $oldprefix; + output_section_rst(@_); +} + +sub output_struct_rst(%) { + my %args = %{$_[0]}; + my ($parameter); + my $oldprefix = $lineprefix; + my $name = $args{'type'} . " " . $args{'struct'}; + + # Sphinx 3.0 and up will emit warnings for "c:type:: struct Foo". + # It wants to see "c:struct:: Foo" (and will add the word 'struct' in + # the rendered output). + if ((split(/\./, $sphinx_version))[0] >= 3) { + my $sname = $name; + $sname =~ s/^struct //; + print "\n\n.. c:struct:: " . $sname . "\n\n"; + } else { + print "\n\n.. c:type:: " . $name . "\n\n"; + } + print_lineno($declaration_start_line); + $lineprefix = " "; + output_highlight_rst($args{'purpose'}); + print "\n"; + + print "**Definition**\n\n"; + print "::\n\n"; + my $declaration = $args{'definition'}; + $declaration =~ s/\t/ /g; + print " " . $args{'type'} . " " . $args{'struct'} . " {\n$declaration };\n\n"; + + print "**Members**\n\n"; + $lineprefix = " "; + foreach $parameter (@{$args{'parameterlist'}}) { + ($parameter =~ /^#/) && next; + + my $parameter_name = $parameter; + $parameter_name =~ s/\[.*//; + + ($args{'parameterdescs'}{$parameter_name} ne $undescribed) || next; + $type = $args{'parametertypes'}{$parameter}; + print_lineno($parameterdesc_start_lines{$parameter_name}); + print "``" . $parameter . "``\n"; + output_highlight_rst($args{'parameterdescs'}{$parameter_name}); + print "\n"; + } + print "\n"; + + $lineprefix = $oldprefix; + output_section_rst(@_); +} + +## none mode output functions + +sub output_function_none(%) { +} + +sub output_enum_none(%) { +} + +sub output_typedef_none(%) { +} + +sub output_struct_none(%) { +} + +sub output_blockhead_none(%) { +} + +## +# generic output function for all types (function, struct/union, typedef, enum); +# calls the generated, variable output_ function name based on +# functype and output_mode +sub output_declaration { + no strict 'refs'; + my $name = shift; + my $functype = shift; + my $func = "output_${functype}_$output_mode"; + if (($output_selection == OUTPUT_ALL) || + (($output_selection == OUTPUT_INCLUDE || + $output_selection == OUTPUT_EXPORTED) && + defined($function_table{$name})) || + (($output_selection == OUTPUT_EXCLUDE || + $output_selection == OUTPUT_INTERNAL) && + !($functype eq "function" && defined($function_table{$name})))) + { + &$func(@_); + $section_counter++; + } +} + +## +# generic output function - calls the right one based on current output mode. +sub output_blockhead { + no strict 'refs'; + my $func = "output_blockhead_" . $output_mode; + &$func(@_); + $section_counter++; +} + +## +# takes a declaration (struct, union, enum, typedef) and +# invokes the right handler. NOT called for functions. +sub dump_declaration($$) { + no strict 'refs'; + my ($prototype, $file) = @_; + if ($decl_type eq 'type name') { + if ($prototype =~ /^(enum|struct|union)\s+/) { + $decl_type = $1; + } else { + return; + } + } + + my $func = "dump_" . $decl_type; + &$func(@_); +} + +sub dump_union($$) { + dump_struct(@_); +} + +sub dump_struct($$) { + my $x = shift; + my $file = shift; + + if ($x =~ /(struct|union)\s+(\w+)\s*\{(.*)\}(\s*(__packed|__aligned|__attribute__\s*\(\([a-z0-9,_\s\(\)]*\)\)))*/) { + my $decl_type = $1; + $declaration_name = $2; + my $members = $3; + + # ignore members marked private: + $members =~ s/\/\*\s*private:.*?\/\*\s*public:.*?\*\///gosi; + $members =~ s/\/\*\s*private:.*//gosi; + # strip comments: + $members =~ s/\/\*.*?\*\///gos; + # strip attributes + $members =~ s/\s*__attribute__\s*\(\([a-z0-9,_\*\s\(\)]*\)\)//gi; + $members =~ s/\s*__aligned\s*\([^;]*\)//gos; + $members =~ s/\s*__packed\s*//gos; + $members =~ s/\s*CRYPTO_MINALIGN_ATTR//gos; + # replace DECLARE_BITMAP + $members =~ s/DECLARE_BITMAP\s*\(([^,)]+),\s*([^,)]+)\)/unsigned long $1\[BITS_TO_LONGS($2)\]/gos; + # replace DECLARE_HASHTABLE + $members =~ s/DECLARE_HASHTABLE\s*\(([^,)]+),\s*([^,)]+)\)/unsigned long $1\[1 << (($2) - 1)\]/gos; + # replace DECLARE_KFIFO + $members =~ s/DECLARE_KFIFO\s*\(([^,)]+),\s*([^,)]+),\s*([^,)]+)\)/$2 \*$1/gos; + # replace DECLARE_KFIFO_PTR + $members =~ s/DECLARE_KFIFO_PTR\s*\(([^,)]+),\s*([^,)]+)\)/$2 \*$1/gos; + + my $declaration = $members; + + # Split nested struct/union elements as newer ones + while ($members =~ m/(struct|union)([^\{\};]+)\{([^\{\}]*)\}([^\{\}\;]*)\;/) { + my $newmember; + my $maintype = $1; + my $ids = $4; + my $content = $3; + foreach my $id(split /,/, $ids) { + $newmember .= "$maintype $id; "; + + $id =~ s/[:\[].*//; + $id =~ s/^\s*\**(\S+)\s*/$1/; + foreach my $arg (split /;/, $content) { + next if ($arg =~ m/^\s*$/); + if ($arg =~ m/^([^\(]+\(\*?\s*)([\w\.]*)(\s*\).*)/) { + # pointer-to-function + my $type = $1; + my $name = $2; + my $extra = $3; + next if (!$name); + if ($id =~ m/^\s*$/) { + # anonymous struct/union + $newmember .= "$type$name$extra; "; + } else { + $newmember .= "$type$id.$name$extra; "; + } + } else { + my $type; + my $names; + $arg =~ s/^\s+//; + $arg =~ s/\s+$//; + # Handle bitmaps + $arg =~ s/:\s*\d+\s*//g; + # Handle arrays + $arg =~ s/\[.*\]//g; + # The type may have multiple words, + # and multiple IDs can be defined, like: + # const struct foo, *bar, foobar + # So, we remove spaces when parsing the + # names, in order to match just names + # and commas for the names + $arg =~ s/\s*,\s*/,/g; + if ($arg =~ m/(.*)\s+([\S+,]+)/) { + $type = $1; + $names = $2; + } else { + $newmember .= "$arg; "; + next; + } + foreach my $name (split /,/, $names) { + $name =~ s/^\s*\**(\S+)\s*/$1/; + next if (($name =~ m/^\s*$/)); + if ($id =~ m/^\s*$/) { + # anonymous struct/union + $newmember .= "$type $name; "; + } else { + $newmember .= "$type $id.$name; "; + } + } + } + } + } + $members =~ s/(struct|union)([^\{\};]+)\{([^\{\}]*)\}([^\{\}\;]*)\;/$newmember/; + } + + # Ignore other nested elements, like enums + $members =~ s/(\{[^\{\}]*\})//g; + + create_parameterlist($members, ';', $file, $declaration_name); + check_sections($file, $declaration_name, $decl_type, $sectcheck, $struct_actual); + + # Adjust declaration for better display + $declaration =~ s/([\{;])/$1\n/g; + $declaration =~ s/\}\s+;/};/g; + # Better handle inlined enums + do {} while ($declaration =~ s/(enum\s+\{[^\}]+),([^\n])/$1,\n$2/); + + my @def_args = split /\n/, $declaration; + my $level = 1; + $declaration = ""; + foreach my $clause (@def_args) { + $clause =~ s/^\s+//; + $clause =~ s/\s+$//; + $clause =~ s/\s+/ /; + next if (!$clause); + $level-- if ($clause =~ m/(\})/ && $level > 1); + if (!($clause =~ m/^\s*#/)) { + $declaration .= "\t" x $level; + } + $declaration .= "\t" . $clause . "\n"; + $level++ if ($clause =~ m/(\{)/ && !($clause =~m/\}/)); + } + output_declaration($declaration_name, + 'struct', + {'struct' => $declaration_name, + 'module' => $modulename, + 'definition' => $declaration, + 'parameterlist' => \@parameterlist, + 'parameterdescs' => \%parameterdescs, + 'parametertypes' => \%parametertypes, + 'sectionlist' => \@sectionlist, + 'sections' => \%sections, + 'purpose' => $declaration_purpose, + 'type' => $decl_type + }); + } + else { + print STDERR "${file}:$.: error: Cannot parse struct or union!\n"; + ++$errors; + } +} + + +sub show_warnings($$) { + my $functype = shift; + my $name = shift; + + return 1 if ($output_selection == OUTPUT_ALL); + + if ($output_selection == OUTPUT_EXPORTED) { + if (defined($function_table{$name})) { + return 1; + } else { + return 0; + } + } + if ($output_selection == OUTPUT_INTERNAL) { + if (!($functype eq "function" && defined($function_table{$name}))) { + return 1; + } else { + return 0; + } + } + if ($output_selection == OUTPUT_INCLUDE) { + if (defined($function_table{$name})) { + return 1; + } else { + return 0; + } + } + if ($output_selection == OUTPUT_EXCLUDE) { + if (!defined($function_table{$name})) { + return 1; + } else { + return 0; + } + } + die("Please add the new output type at show_warnings()"); +} + +sub dump_enum($$) { + my $x = shift; + my $file = shift; + + $x =~ s@/\*.*?\*/@@gos; # strip comments. + # strip #define macros inside enums + $x =~ s@#\s*((define|ifdef)\s+|endif)[^;]*;@@gos; + + if ($x =~ /enum\s+(\w*)\s*\{(.*)\}/) { + $declaration_name = $1; + my $members = $2; + my %_members; + + $members =~ s/\s+$//; + + foreach my $arg (split ',', $members) { + $arg =~ s/^\s*(\w+).*/$1/; + push @parameterlist, $arg; + if (!$parameterdescs{$arg}) { + $parameterdescs{$arg} = $undescribed; + if (show_warnings("enum", $declaration_name)) { + print STDERR "${file}:$.: warning: Enum value '$arg' not described in enum '$declaration_name'\n"; + } + } + $_members{$arg} = 1; + } + + while (my ($k, $v) = each %parameterdescs) { + if (!exists($_members{$k})) { + if (show_warnings("enum", $declaration_name)) { + print STDERR "${file}:$.: warning: Excess enum value '$k' description in '$declaration_name'\n"; + } + } + } + + output_declaration($declaration_name, + 'enum', + {'enum' => $declaration_name, + 'module' => $modulename, + 'parameterlist' => \@parameterlist, + 'parameterdescs' => \%parameterdescs, + 'sectionlist' => \@sectionlist, + 'sections' => \%sections, + 'purpose' => $declaration_purpose + }); + } + else { + print STDERR "${file}:$.: error: Cannot parse enum!\n"; + ++$errors; + } +} + +sub dump_typedef($$) { + my $x = shift; + my $file = shift; + + $x =~ s@/\*.*?\*/@@gos; # strip comments. + + # Parse function prototypes + if ($x =~ /typedef\s+(\w+)\s*\(\*\s*(\w\S+)\s*\)\s*\((.*)\);/ || + $x =~ /typedef\s+(\w+)\s*(\w\S+)\s*\s*\((.*)\);/) { + + # Function typedefs + $return_type = $1; + $declaration_name = $2; + my $args = $3; + + create_parameterlist($args, ',', $file, $declaration_name); + + output_declaration($declaration_name, + 'function', + {'function' => $declaration_name, + 'typedef' => 1, + 'module' => $modulename, + 'functiontype' => $return_type, + 'parameterlist' => \@parameterlist, + 'parameterdescs' => \%parameterdescs, + 'parametertypes' => \%parametertypes, + 'sectionlist' => \@sectionlist, + 'sections' => \%sections, + 'purpose' => $declaration_purpose + }); + return; + } + + while (($x =~ /\(*.\)\s*;$/) || ($x =~ /\[*.\]\s*;$/)) { + $x =~ s/\(*.\)\s*;$/;/; + $x =~ s/\[*.\]\s*;$/;/; + } + + if ($x =~ /typedef.*\s+(\w+)\s*;/) { + $declaration_name = $1; + + output_declaration($declaration_name, + 'typedef', + {'typedef' => $declaration_name, + 'module' => $modulename, + 'sectionlist' => \@sectionlist, + 'sections' => \%sections, + 'purpose' => $declaration_purpose + }); + } + else { + print STDERR "${file}:$.: error: Cannot parse typedef!\n"; + ++$errors; + } +} + +sub save_struct_actual($) { + my $actual = shift; + + # strip all spaces from the actual param so that it looks like one string item + $actual =~ s/\s*//g; + $struct_actual = $struct_actual . $actual . " "; +} + +sub create_parameterlist($$$$) { + my $args = shift; + my $splitter = shift; + my $file = shift; + my $declaration_name = shift; + my $type; + my $param; + + # temporarily replace commas inside function pointer definition + while ($args =~ /(\([^\),]+),/) { + $args =~ s/(\([^\),]+),/$1#/g; + } + + foreach my $arg (split($splitter, $args)) { + # strip comments + $arg =~ s/\/\*.*\*\///; + # strip leading/trailing spaces + $arg =~ s/^\s*//; + $arg =~ s/\s*$//; + $arg =~ s/\s+/ /; + + if ($arg =~ /^#/) { + # Treat preprocessor directive as a typeless variable just to fill + # corresponding data structures "correctly". Catch it later in + # output_* subs. + push_parameter($arg, "", $file); + } elsif ($arg =~ m/\(.+\)\s*\(/) { + # pointer-to-function + $arg =~ tr/#/,/; + $arg =~ m/[^\(]+\(\*?\s*([\w\.]*)\s*\)/; + $param = $1; + $type = $arg; + $type =~ s/([^\(]+\(\*?)\s*$param/$1/; + save_struct_actual($param); + push_parameter($param, $type, $file, $declaration_name); + } elsif ($arg) { + $arg =~ s/\s*:\s*/:/g; + $arg =~ s/\s*\[/\[/g; + + my @args = split('\s*,\s*', $arg); + if ($args[0] =~ m/\*/) { + $args[0] =~ s/(\*+)\s*/ $1/; + } + + my @first_arg; + if ($args[0] =~ /^(.*\s+)(.*?\[.*\].*)$/) { + shift @args; + push(@first_arg, split('\s+', $1)); + push(@first_arg, $2); + } else { + @first_arg = split('\s+', shift @args); + } + + unshift(@args, pop @first_arg); + $type = join " ", @first_arg; + + foreach $param (@args) { + if ($param =~ m/^(\*+)\s*(.*)/) { + save_struct_actual($2); + push_parameter($2, "$type $1", $file, $declaration_name); + } + elsif ($param =~ m/(.*?):(\d+)/) { + if ($type ne "") { # skip unnamed bit-fields + save_struct_actual($1); + push_parameter($1, "$type:$2", $file, $declaration_name) + } + } + else { + save_struct_actual($param); + push_parameter($param, $type, $file, $declaration_name); + } + } + } + } +} + +sub push_parameter($$$$) { + my $param = shift; + my $type = shift; + my $file = shift; + my $declaration_name = shift; + + if (($anon_struct_union == 1) && ($type eq "") && + ($param eq "}")) { + return; # ignore the ending }; from anon. struct/union + } + + $anon_struct_union = 0; + $param =~ s/[\[\)].*//; + + if ($type eq "" && $param =~ /\.\.\.$/) + { + if (!$param =~ /\w\.\.\.$/) { + # handles unnamed variable parameters + $param = "..."; + } + if (!defined $parameterdescs{$param} || $parameterdescs{$param} eq "") { + $parameterdescs{$param} = "variable arguments"; + } + } + elsif ($type eq "" && ($param eq "" or $param eq "void")) + { + $param="void"; + $parameterdescs{void} = "no arguments"; + } + elsif ($type eq "" && ($param eq "struct" or $param eq "union")) + # handle unnamed (anonymous) union or struct: + { + $type = $param; + $param = "{unnamed_" . $param . "}"; + $parameterdescs{$param} = "anonymous\n"; + $anon_struct_union = 1; + } + + # warn if parameter has no description + # (but ignore ones starting with # as these are not parameters + # but inline preprocessor statements); + # Note: It will also ignore void params and unnamed structs/unions + if (!defined $parameterdescs{$param} && $param !~ /^#/) { + $parameterdescs{$param} = $undescribed; + + if (show_warnings($type, $declaration_name) && $param !~ /\./) { + print STDERR + "${file}:$.: warning: Function parameter or member '$param' not described in '$declaration_name'\n"; + ++$warnings; + } + } + + # strip spaces from $param so that it is one continuous string + # on @parameterlist; + # this fixes a problem where check_sections() cannot find + # a parameter like "addr[6 + 2]" because it actually appears + # as "addr[6", "+", "2]" on the parameter list; + # but it's better to maintain the param string unchanged for output, + # so just weaken the string compare in check_sections() to ignore + # "[blah" in a parameter string; + ###$param =~ s/\s*//g; + push @parameterlist, $param; + $type =~ s/\s\s+/ /g; + $parametertypes{$param} = $type; +} + +sub check_sections($$$$$) { + my ($file, $decl_name, $decl_type, $sectcheck, $prmscheck) = @_; + my @sects = split ' ', $sectcheck; + my @prms = split ' ', $prmscheck; + my $err; + my ($px, $sx); + my $prm_clean; # strip trailing "[array size]" and/or beginning "*" + + foreach $sx (0 .. $#sects) { + $err = 1; + foreach $px (0 .. $#prms) { + $prm_clean = $prms[$px]; + $prm_clean =~ s/\[.*\]//; + $prm_clean =~ s/__attribute__\s*\(\([a-z,_\*\s\(\)]*\)\)//i; + # ignore array size in a parameter string; + # however, the original param string may contain + # spaces, e.g.: addr[6 + 2] + # and this appears in @prms as "addr[6" since the + # parameter list is split at spaces; + # hence just ignore "[..." for the sections check; + $prm_clean =~ s/\[.*//; + + ##$prm_clean =~ s/^\**//; + if ($prm_clean eq $sects[$sx]) { + $err = 0; + last; + } + } + if ($err) { + if ($decl_type eq "function") { + print STDERR "${file}:$.: warning: " . + "Excess function parameter " . + "'$sects[$sx]' " . + "description in '$decl_name'\n"; + ++$warnings; + } + } + } +} + +## +# Checks the section describing the return value of a function. +sub check_return_section { + my $file = shift; + my $declaration_name = shift; + my $return_type = shift; + + # Ignore an empty return type (It's a macro) + # Ignore functions with a "void" return type. (But don't ignore "void *") + if (($return_type eq "") || ($return_type =~ /void\s*\w*\s*$/)) { + return; + } + + if (!defined($sections{$section_return}) || + $sections{$section_return} eq "") { + print STDERR "${file}:$.: warning: " . + "No description found for return value of " . + "'$declaration_name'\n"; + ++$warnings; + } +} + +## +# takes a function prototype and the name of the current file being +# processed and spits out all the details stored in the global +# arrays/hashes. +sub dump_function($$) { + my $prototype = shift; + my $file = shift; + my $noret = 0; + + $prototype =~ s/^static +//; + $prototype =~ s/^extern +//; + $prototype =~ s/^asmlinkage +//; + $prototype =~ s/^inline +//; + $prototype =~ s/^__inline__ +//; + $prototype =~ s/^__inline +//; + $prototype =~ s/^__always_inline +//; + $prototype =~ s/^noinline +//; + $prototype =~ s/__init +//; + $prototype =~ s/__init_or_module +//; + $prototype =~ s/__meminit +//; + $prototype =~ s/__must_check +//; + $prototype =~ s/__weak +//; + $prototype =~ s/__sched +//; + $prototype =~ s/__printf\s*\(\s*\d*\s*,\s*\d*\s*\) +//; + my $define = $prototype =~ s/^#\s*define\s+//; #ak added + $prototype =~ s/__attribute__\s*\(\( + (?: + [\w\s]++ # attribute name + (?:\([^)]*+\))? # attribute arguments + \s*+,? # optional comma at the end + )+ + \)\)\s+//x; + + # Yes, this truly is vile. We are looking for: + # 1. Return type (may be nothing if we're looking at a macro) + # 2. Function name + # 3. Function parameters. + # + # All the while we have to watch out for function pointer parameters + # (which IIRC is what the two sections are for), C types (these + # regexps don't even start to express all the possibilities), and + # so on. + # + # If you mess with these regexps, it's a good idea to check that + # the following functions' documentation still comes out right: + # - parport_register_device (function pointer parameters) + # - atomic_set (macro) + # - pci_match_device, __copy_to_user (long return type) + + if ($define && $prototype =~ m/^()([a-zA-Z0-9_~:]+)\s+/) { + # This is an object-like macro, it has no return type and no parameter + # list. + # Function-like macros are not allowed to have spaces between + # declaration_name and opening parenthesis (notice the \s+). + $return_type = $1; + $declaration_name = $2; + $noret = 1; + } elsif ($prototype =~ m/^()([a-zA-Z0-9_~:]+)\s*\(([^\(]*)\)/ || + $prototype =~ m/^(\w+)\s+([a-zA-Z0-9_~:]+)\s*\(([^\(]*)\)/ || + $prototype =~ m/^(\w+\s*\*+)\s*([a-zA-Z0-9_~:]+)\s*\(([^\(]*)\)/ || + $prototype =~ m/^(\w+\s+\w+)\s+([a-zA-Z0-9_~:]+)\s*\(([^\(]*)\)/ || + $prototype =~ m/^(\w+\s+\w+\s*\*+)\s*([a-zA-Z0-9_~:]+)\s*\(([^\(]*)\)/ || + $prototype =~ m/^(\w+\s+\w+\s+\w+)\s+([a-zA-Z0-9_~:]+)\s*\(([^\(]*)\)/ || + $prototype =~ m/^(\w+\s+\w+\s+\w+\s*\*+)\s*([a-zA-Z0-9_~:]+)\s*\(([^\(]*)\)/ || + $prototype =~ m/^()([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ || + $prototype =~ m/^(\w+)\s+([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ || + $prototype =~ m/^(\w+\s*\*+)\s*([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ || + $prototype =~ m/^(\w+\s+\w+)\s+([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ || + $prototype =~ m/^(\w+\s+\w+\s*\*+)\s*([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ || + $prototype =~ m/^(\w+\s+\w+\s+\w+)\s+([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ || + $prototype =~ m/^(\w+\s+\w+\s+\w+\s*\*+)\s*([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ || + $prototype =~ m/^(\w+\s+\w+\s+\w+\s+\w+)\s+([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ || + $prototype =~ m/^(\w+\s+\w+\s+\w+\s+\w+\s*\*+)\s*([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ || + $prototype =~ m/^(\w+\s+\w+\s*\*+\s*\w+\s*\*+\s*)\s*([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/) { + $return_type = $1; + $declaration_name = $2; + my $args = $3; + + create_parameterlist($args, ',', $file, $declaration_name); + } else { + print STDERR "${file}:$.: warning: cannot understand function prototype: '$prototype'\n"; + return; + } + + my $prms = join " ", @parameterlist; + check_sections($file, $declaration_name, "function", $sectcheck, $prms); + + # This check emits a lot of warnings at the moment, because many + # functions don't have a 'Return' doc section. So until the number + # of warnings goes sufficiently down, the check is only performed in + # verbose mode. + # TODO: always perform the check. + if ($verbose && !$noret) { + check_return_section($file, $declaration_name, $return_type); + } + + output_declaration($declaration_name, + 'function', + {'function' => $declaration_name, + 'module' => $modulename, + 'functiontype' => $return_type, + 'parameterlist' => \@parameterlist, + 'parameterdescs' => \%parameterdescs, + 'parametertypes' => \%parametertypes, + 'sectionlist' => \@sectionlist, + 'sections' => \%sections, + 'purpose' => $declaration_purpose + }); +} + +sub reset_state { + $function = ""; + %parameterdescs = (); + %parametertypes = (); + @parameterlist = (); + %sections = (); + @sectionlist = (); + $sectcheck = ""; + $struct_actual = ""; + $prototype = ""; + + $state = STATE_NORMAL; + $inline_doc_state = STATE_INLINE_NA; +} + +sub tracepoint_munge($) { + my $file = shift; + my $tracepointname = 0; + my $tracepointargs = 0; + + if ($prototype =~ m/TRACE_EVENT\((.*?),/) { + $tracepointname = $1; + } + if ($prototype =~ m/DEFINE_SINGLE_EVENT\((.*?),/) { + $tracepointname = $1; + } + if ($prototype =~ m/DEFINE_EVENT\((.*?),(.*?),/) { + $tracepointname = $2; + } + $tracepointname =~ s/^\s+//; #strip leading whitespace + if ($prototype =~ m/TP_PROTO\((.*?)\)/) { + $tracepointargs = $1; + } + if (($tracepointname eq 0) || ($tracepointargs eq 0)) { + print STDERR "${file}:$.: warning: Unrecognized tracepoint format: \n". + "$prototype\n"; + } else { + $prototype = "static inline void trace_$tracepointname($tracepointargs)"; + } +} + +sub syscall_munge() { + my $void = 0; + + $prototype =~ s@[\r\n]+@ @gos; # strip newlines/CR's +## if ($prototype =~ m/SYSCALL_DEFINE0\s*\(\s*(a-zA-Z0-9_)*\s*\)/) { + if ($prototype =~ m/SYSCALL_DEFINE0/) { + $void = 1; +## $prototype = "long sys_$1(void)"; + } + + $prototype =~ s/SYSCALL_DEFINE.*\(/long sys_/; # fix return type & func name + if ($prototype =~ m/long (sys_.*?),/) { + $prototype =~ s/,/\(/; + } elsif ($void) { + $prototype =~ s/\)/\(void\)/; + } + + # now delete all of the odd-number commas in $prototype + # so that arg types & arg names don't have a comma between them + my $count = 0; + my $len = length($prototype); + if ($void) { + $len = 0; # skip the for-loop + } + for (my $ix = 0; $ix < $len; $ix++) { + if (substr($prototype, $ix, 1) eq ',') { + $count++; + if ($count % 2 == 1) { + substr($prototype, $ix, 1) = ' '; + } + } + } +} + +sub process_proto_function($$) { + my $x = shift; + my $file = shift; + + $x =~ s@\/\/.*$@@gos; # strip C99-style comments to end of line + + if ($x =~ m#\s*/\*\s+MACDOC\s*#io || ($x =~ /^#/ && $x !~ /^#\s*define/)) { + # do nothing + } + elsif ($x =~ /([^\{]*)/) { + $prototype .= $1; + } + + if (($x =~ /\{/) || ($x =~ /\#\s*define/) || ($x =~ /;/)) { + $prototype =~ s@/\*.*?\*/@@gos; # strip comments. + $prototype =~ s@[\r\n]+@ @gos; # strip newlines/cr's. + $prototype =~ s@^\s+@@gos; # strip leading spaces + if ($prototype =~ /SYSCALL_DEFINE/) { + syscall_munge(); + } + if ($prototype =~ /TRACE_EVENT/ || $prototype =~ /DEFINE_EVENT/ || + $prototype =~ /DEFINE_SINGLE_EVENT/) + { + tracepoint_munge($file); + } + dump_function($prototype, $file); + reset_state(); + } +} + +sub process_proto_type($$) { + my $x = shift; + my $file = shift; + + $x =~ s@[\r\n]+@ @gos; # strip newlines/cr's. + $x =~ s@^\s+@@gos; # strip leading spaces + $x =~ s@\s+$@@gos; # strip trailing spaces + $x =~ s@\/\/.*$@@gos; # strip C99-style comments to end of line + + if ($x =~ /^#/) { + # To distinguish preprocessor directive from regular declaration later. + $x .= ";"; + } + + while (1) { + if ( $x =~ /([^\{\};]*)([\{\};])(.*)/ ) { + if( length $prototype ) { + $prototype .= " " + } + $prototype .= $1 . $2; + ($2 eq '{') && $brcount++; + ($2 eq '}') && $brcount--; + if (($2 eq ';') && ($brcount == 0)) { + dump_declaration($prototype, $file); + reset_state(); + last; + } + $x = $3; + } else { + $prototype .= $x; + last; + } + } +} + + +sub map_filename($) { + my $file; + my ($orig_file) = @_; + + if (defined($ENV{'SRCTREE'})) { + $file = "$ENV{'SRCTREE'}" . "/" . $orig_file; + } else { + $file = $orig_file; + } + + if (defined($source_map{$file})) { + $file = $source_map{$file}; + } + + return $file; +} + +sub process_export_file($) { + my ($orig_file) = @_; + my $file = map_filename($orig_file); + + if (!open(IN,"<$file")) { + print STDERR "Error: Cannot open file $file\n"; + ++$errors; + return; + } + + while () { + if (/$export_symbol/) { + $function_table{$2} = 1; + } + } + + close(IN); +} + +# +# Parsers for the various processing states. +# +# STATE_NORMAL: looking for the /** to begin everything. +# +sub process_normal() { + if (/$doc_start/o) { + $state = STATE_NAME; # next line is always the function name + $in_doc_sect = 0; + $declaration_start_line = $. + 1; + } +} + +# +# STATE_NAME: Looking for the "name - description" line +# +sub process_name($$) { + my $file = shift; + my $identifier; + my $descr; + + if (/$doc_block/o) { + $state = STATE_DOCBLOCK; + $contents = ""; + $new_start_line = $. + 1; + + if ( $1 eq "" ) { + $section = $section_intro; + } else { + $section = $1; + } + } + elsif (/$doc_decl/o) { + $identifier = $1; + if (/\s*([\w\s]+?)(\s*-|:)/) { + $identifier = $1; + } + + $state = STATE_BODY; + # if there's no @param blocks need to set up default section + # here + $contents = ""; + $section = $section_default; + $new_start_line = $. + 1; + if (/[-:](.*)/) { + # strip leading/trailing/multiple spaces + $descr= $1; + $descr =~ s/^\s*//; + $descr =~ s/\s*$//; + $descr =~ s/\s+/ /g; + $declaration_purpose = $descr; + $state = STATE_BODY_MAYBE; + } else { + $declaration_purpose = ""; + } + + if (($declaration_purpose eq "") && $verbose) { + print STDERR "${file}:$.: warning: missing initial short description on line:\n"; + print STDERR $_; + ++$warnings; + } + + if ($identifier =~ m/^[A-Z]/) { + $decl_type = 'type name'; + } elsif ($identifier =~ m/^struct\b/) { + $decl_type = 'struct'; + } elsif ($identifier =~ m/^union\b/) { + $decl_type = 'union'; + } elsif ($identifier =~ m/^enum\b/) { + $decl_type = 'enum'; + } elsif ($identifier =~ m/^typedef\b/) { + $decl_type = 'typedef'; + } else { + $decl_type = 'function'; + } + + if ($verbose) { + print STDERR "${file}:$.: info: Scanning doc for $identifier\n"; + } + } else { + print STDERR "${file}:$.: warning: Cannot understand $_ on line $.", + " - I thought it was a doc line\n"; + ++$warnings; + $state = STATE_NORMAL; + } +} + + +# +# STATE_BODY and STATE_BODY_MAYBE: the bulk of a kerneldoc comment. +# +sub process_body($$) { + my $file = shift; + + if (/$doc_sect/i) { # case insensitive for supported section names + $newsection = $1; + $newcontents = $2; + + # map the supported section names to the canonical names + if ($newsection =~ m/^description$/i) { + $newsection = $section_default; + } elsif ($newsection =~ m/^context$/i) { + $newsection = $section_context; + } elsif ($newsection =~ m/^returns?$/i) { + $newsection = $section_return; + } elsif ($newsection =~ m/^\@return$/) { + # special: @return is a section, not a param description + $newsection = $section_return; + } + + if (($contents ne "") && ($contents ne "\n")) { + if (!$in_doc_sect && $verbose) { + print STDERR "${file}:$.: warning: contents before sections\n"; + ++$warnings; + } + dump_section($file, $section, $contents); + $section = $section_default; + } + + $in_doc_sect = 1; + $state = STATE_BODY; + $contents = $newcontents; + $new_start_line = $.; + while (substr($contents, 0, 1) eq " ") { + $contents = substr($contents, 1); + } + if ($contents ne "") { + $contents .= "\n"; + } + $section = $newsection; + $leading_space = undef; + } elsif (/$doc_end/) { + if (($contents ne "") && ($contents ne "\n")) { + dump_section($file, $section, $contents); + $section = $section_default; + $contents = ""; + } + # look for doc_com + + doc_end: + if ($_ =~ m'\s*\*\s*[a-zA-Z_0-9:\.]+\*/') { + print STDERR "${file}:$.: warning: suspicious ending line: $_"; + ++$warnings; + } + + $prototype = ""; + $state = STATE_PROTO; + $brcount = 0; + } elsif (/$doc_content/) { + # miguel-style comment kludge, look for blank lines after + # @parameter line to signify start of description + if ($1 eq "") { + if ($section =~ m/^@/ || $section eq $section_context) { + dump_section($file, $section, $contents); + $section = $section_default; + $contents = ""; + $new_start_line = $.; + } else { + $contents .= "\n"; + } + $state = STATE_BODY; + } elsif ($state == STATE_BODY_MAYBE) { + # Continued declaration purpose + chomp($declaration_purpose); + $declaration_purpose .= " " . $1; + $declaration_purpose =~ s/\s+/ /g; + } else { + my $cont = $1; + if ($section =~ m/^@/ || $section eq $section_context) { + if (!defined $leading_space) { + if ($cont =~ m/^(\s+)/) { + $leading_space = $1; + } else { + $leading_space = ""; + } + } + $cont =~ s/^$leading_space//; + } + $contents .= $cont . "\n"; + } + } else { + # i dont know - bad line? ignore. + print STDERR "${file}:$.: warning: bad line: $_"; + ++$warnings; + } +} + + +# +# STATE_PROTO: reading a function/whatever prototype. +# +sub process_proto($$) { + my $file = shift; + + if (/$doc_inline_oneline/) { + $section = $1; + $contents = $2; + if ($contents ne "") { + $contents .= "\n"; + dump_section($file, $section, $contents); + $section = $section_default; + $contents = ""; + } + } elsif (/$doc_inline_start/) { + $state = STATE_INLINE; + $inline_doc_state = STATE_INLINE_NAME; + } elsif ($decl_type eq 'function') { + process_proto_function($_, $file); + } else { + process_proto_type($_, $file); + } +} + +# +# STATE_DOCBLOCK: within a DOC: block. +# +sub process_docblock($$) { + my $file = shift; + + if (/$doc_end/) { + dump_doc_section($file, $section, $contents); + $section = $section_default; + $contents = ""; + $function = ""; + %parameterdescs = (); + %parametertypes = (); + @parameterlist = (); + %sections = (); + @sectionlist = (); + $prototype = ""; + $state = STATE_NORMAL; + } elsif (/$doc_content/) { + if ( $1 eq "" ) { + $contents .= $blankline; + } else { + $contents .= $1 . "\n"; + } + } +} + +# +# STATE_INLINE: docbook comments within a prototype. +# +sub process_inline($$) { + my $file = shift; + + # First line (state 1) needs to be a @parameter + if ($inline_doc_state == STATE_INLINE_NAME && /$doc_inline_sect/o) { + $section = $1; + $contents = $2; + $new_start_line = $.; + if ($contents ne "") { + while (substr($contents, 0, 1) eq " ") { + $contents = substr($contents, 1); + } + $contents .= "\n"; + } + $inline_doc_state = STATE_INLINE_TEXT; + # Documentation block end */ + } elsif (/$doc_inline_end/) { + if (($contents ne "") && ($contents ne "\n")) { + dump_section($file, $section, $contents); + $section = $section_default; + $contents = ""; + } + $state = STATE_PROTO; + $inline_doc_state = STATE_INLINE_NA; + # Regular text + } elsif (/$doc_content/) { + if ($inline_doc_state == STATE_INLINE_TEXT) { + $contents .= $1 . "\n"; + # nuke leading blank lines + if ($contents =~ /^\s*$/) { + $contents = ""; + } + } elsif ($inline_doc_state == STATE_INLINE_NAME) { + $inline_doc_state = STATE_INLINE_ERROR; + print STDERR "${file}:$.: warning: "; + print STDERR "Incorrect use of kernel-doc format: $_"; + ++$warnings; + } + } +} + + +sub process_file($) { + my $file; + my $initial_section_counter = $section_counter; + my ($orig_file) = @_; + + $file = map_filename($orig_file); + + if (!open(IN,"<$file")) { + print STDERR "Error: Cannot open file $file\n"; + ++$errors; + return; + } + + $. = 1; + + $section_counter = 0; + while () { + while (s/\\\s*$//) { + $_ .= ; + } + # Replace tabs by spaces + while ($_ =~ s/\t+/' ' x (length($&) * 8 - length($`) % 8)/e) {}; + # Hand this line to the appropriate state handler + if ($state == STATE_NORMAL) { + process_normal(); + } elsif ($state == STATE_NAME) { + process_name($file, $_); + } elsif ($state == STATE_BODY || $state == STATE_BODY_MAYBE) { + process_body($file, $_); + } elsif ($state == STATE_INLINE) { # scanning for inline parameters + process_inline($file, $_); + } elsif ($state == STATE_PROTO) { + process_proto($file, $_); + } elsif ($state == STATE_DOCBLOCK) { + process_docblock($file, $_); + } + } + + # Make sure we got something interesting. + if ($initial_section_counter == $section_counter && $ + output_mode ne "none") { + if ($output_selection == OUTPUT_INCLUDE) { + print STDERR "${file}:1: warning: '$_' not found\n" + for keys %function_table; + } + else { + print STDERR "${file}:1: warning: no structured comments found\n"; + } + } +} + + +$kernelversion = get_kernel_version(); + +# generate a sequence of code that will splice in highlighting information +# using the s// operator. +for (my $k = 0; $k < @highlights; $k++) { + my $pattern = $highlights[$k][0]; + my $result = $highlights[$k][1]; +# print STDERR "scanning pattern:$pattern, highlight:($result)\n"; + $dohighlight .= "\$contents =~ s:$pattern:$result:gs;\n"; +} + +# Read the file that maps relative names to absolute names for +# separate source and object directories and for shadow trees. +if (open(SOURCE_MAP, "<.tmp_filelist.txt")) { + my ($relname, $absname); + while() { + chop(); + ($relname, $absname) = (split())[0..1]; + $relname =~ s:^/+::; + $source_map{$relname} = $absname; + } + close(SOURCE_MAP); +} + +if ($output_selection == OUTPUT_EXPORTED || + $output_selection == OUTPUT_INTERNAL) { + + push(@export_file_list, @ARGV); + + foreach (@export_file_list) { + chomp; + process_export_file($_); + } +} + +foreach (@ARGV) { + chomp; + process_file($_); +} +if ($verbose && $errors) { + print STDERR "$errors errors\n"; +} +if ($verbose && $warnings) { + print STDERR "$warnings warnings\n"; +} + +exit($output_mode eq "none" ? 0 : $errors); 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 old mode 100644 new mode 100755 index 40ae1989e1..90b99517c1 --- 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 @@ -645,7 +645,7 @@ class KconfigParser: self.cursor = self.src.find('\n', self.cursor) self.val = self.src[start:self.cursor] return TOK_SOURCE - elif self.tok.isalpha(): + elif self.tok.isalnum(): # identifier while self.src[self.cursor].isalnum() or self.src[self.cursor] == '_': self.cursor += 1 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/commands.py b/scripts/qapi/commands.py index ab98e504f3..bc30876c88 100644 --- a/scripts/qapi/commands.py +++ b/scripts/qapi/commands.py @@ -237,9 +237,9 @@ void %(c_prefix)sqmp_init_marshal(QmpCommandList *cmds) class QAPISchemaGenCommandVisitor(QAPISchemaModularCVisitor): def __init__(self, prefix): - QAPISchemaModularCVisitor.__init__( - self, prefix, 'qapi-commands', - ' * Schema-defined QAPI/QMP commands', __doc__) + super().__init__( + prefix, 'qapi-commands', + ' * Schema-defined QAPI/QMP commands', None, __doc__) self._regy = QAPIGenCCode(None) self._visited_ret_types = {} @@ -263,22 +263,29 @@ class QAPISchemaGenCommandVisitor(QAPISchemaModularCVisitor): commands=commands, visit=visit)) self._genh.add(mcgen(''' #include "%(types)s.h" -#include "qapi/qmp/dispatch.h" ''', types=types)) def visit_end(self): - (genc, genh) = self._module[self._main_module] - genh.add(mcgen(''' + self._add_system_module('init', ' * QAPI Commands initialization') + self._genh.add(mcgen(''' +#include "qapi/qmp/dispatch.h" + void %(c_prefix)sqmp_init_marshal(QmpCommandList *cmds); ''', - c_prefix=c_name(self._prefix, protect=False))) - genc.add(gen_registry(self._regy.get_content(), self._prefix)) + c_prefix=c_name(self._prefix, protect=False))) + self._genc.preamble_add(mcgen(''' +#include "qemu/osdep.h" +#include "%(prefix)sqapi-commands.h" +#include "%(prefix)sqapi-init-commands.h" +''', + prefix=self._prefix)) + self._genc.add(gen_registry(self._regy.get_content(), self._prefix)) - def visit_command(self, name, info, ifcond, arg_type, ret_type, gen, - success_response, boxed, allow_oob, allow_preconfig, - features): + def visit_command(self, name, info, ifcond, features, + arg_type, ret_type, gen, success_response, boxed, + allow_oob, allow_preconfig): if not gen: return # FIXME: If T is a user-defined type, the user is responsible diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py index e00dcafce7..ba35abea47 100644 --- a/scripts/qapi/common.py +++ b/scripts/qapi/common.py @@ -12,7 +12,6 @@ # See the COPYING file in the top-level directory. import re -import string # ENUMName -> ENUM_NAME, EnumName1 -> ENUM_NAME1 @@ -43,10 +42,7 @@ def c_enum_const(type_name, const_name, prefix=None): return camel_to_upper(type_name) + '_' + c_name(const_name, False).upper() -if hasattr(str, 'maketrans'): - c_name_trans = str.maketrans('.-', '__') -else: - c_name_trans = string.maketrans('.-', '__') +c_name_trans = str.maketrans('.-', '__') # Map @name to a valid C identifier. diff --git a/scripts/qapi/doc.py b/scripts/qapi/doc.py index 6f1c17f71f..92f584edcf 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 @@ -244,34 +243,34 @@ class QAPISchemaGenDocVisitor(QAPISchemaVisitor): def write(self, output_dir): self._gen.write(output_dir) - def visit_enum_type(self, name, info, ifcond, members, prefix): + def visit_enum_type(self, name, info, ifcond, features, members, prefix): doc = self.cur_doc self._gen.add(texi_type('Enum', doc, ifcond, texi_members(doc, 'Values', member_func=texi_enum_value))) - def visit_object_type(self, name, info, ifcond, base, members, variants, - features): + def visit_object_type(self, name, info, ifcond, features, + base, members, variants): doc = self.cur_doc if base and base.is_implicit(): base = None self._gen.add(texi_type('Object', doc, ifcond, texi_members(doc, 'Members', base, variants))) - def visit_alternate_type(self, name, info, ifcond, variants): + def visit_alternate_type(self, name, info, ifcond, features, variants): doc = self.cur_doc self._gen.add(texi_type('Alternate', doc, ifcond, texi_members(doc, 'Members'))) - def visit_command(self, name, info, ifcond, arg_type, ret_type, gen, - success_response, boxed, allow_oob, allow_preconfig, - features): + def visit_command(self, name, info, ifcond, features, + arg_type, ret_type, gen, success_response, boxed, + allow_oob, allow_preconfig): doc = self.cur_doc self._gen.add(texi_msg('Command', doc, ifcond, texi_arguments(doc, arg_type if boxed else None))) - def visit_event(self, name, info, ifcond, arg_type, boxed): + def visit_event(self, name, info, ifcond, features, arg_type, boxed): doc = self.cur_doc self._gen.add(texi_msg('Event', doc, ifcond, texi_arguments(doc, diff --git a/scripts/qapi/error.py b/scripts/qapi/error.py index b9f3751bea..ae60d9e2fe 100644 --- a/scripts/qapi/error.py +++ b/scripts/qapi/error.py @@ -35,9 +35,9 @@ class QAPIParseError(QAPIError): col = (col + 7) % 8 + 1 else: col += 1 - QAPIError.__init__(self, parser.info, col, msg) + super().__init__(parser.info, col, msg) class QAPISemError(QAPIError): def __init__(self, info, msg): - QAPIError.__init__(self, info, None, msg) + super().__init__(info, None, msg) diff --git a/scripts/qapi/events.py b/scripts/qapi/events.py index 10fc509fa9..b544af5a1c 100644 --- a/scripts/qapi/events.py +++ b/scripts/qapi/events.py @@ -138,9 +138,9 @@ def gen_event_send(name, arg_type, boxed, event_enum_name, event_emit): class QAPISchemaGenEventVisitor(QAPISchemaModularCVisitor): def __init__(self, prefix): - QAPISchemaModularCVisitor.__init__( - self, prefix, 'qapi-events', - ' * Schema-defined QAPI/QMP events', __doc__) + super().__init__( + prefix, 'qapi-events', + ' * Schema-defined QAPI/QMP events', None, __doc__) self._event_enum_name = c_name(prefix + 'QAPIEvent', protect=False) self._event_enum_members = [] self._event_emit_name = c_name(prefix + 'qapi_event_emit') @@ -189,7 +189,7 @@ void %(event_emit)s(%(event_enum)s event, QDict *qdict); event_emit=self._event_emit_name, event_enum=self._event_enum_name)) - def visit_event(self, name, info, ifcond, arg_type, boxed): + def visit_event(self, name, info, ifcond, features, arg_type, boxed): with ifcontext(ifcond, self._genh, self._genc): self._genh.add(gen_event_send_decl(name, arg_type, boxed)) self._genc.add(gen_event_send(name, arg_type, boxed, diff --git a/scripts/qapi/expr.py b/scripts/qapi/expr.py index d7a289eded..2942520399 100644 --- a/scripts/qapi/expr.py +++ b/scripts/qapi/expr.py @@ -35,7 +35,6 @@ def check_name_is_str(name, info, source): def check_name_str(name, info, source, allow_optional=False, enum_member=False, permit_upper=False): - global valid_name membername = name if allow_optional and name.startswith('*'): @@ -168,8 +167,9 @@ def check_type(value, info, source, allow_optional=True, permit_upper=permit_upper) if c_name(key, False) == 'u' or c_name(key, False).startswith('has_'): raise QAPISemError(info, "%s uses reserved name" % key_source) - check_keys(arg, info, key_source, ['type'], ['if']) + check_keys(arg, info, key_source, ['type'], ['if', 'features']) check_if(arg, info, key_source) + check_features(arg.get('features'), info) check_type(arg['type'], info, key_source, allow_array=True) @@ -220,7 +220,6 @@ def check_struct(expr, info): check_type(members, info, "'data'", allow_dict=name) check_type(expr.get('base'), info, "'base'") - check_features(expr.get('features'), info) def check_union(expr, info): @@ -249,7 +248,7 @@ def check_union(expr, info): def check_alternate(expr, info): members = expr['data'] - if len(members) == 0: + if not members: raise QAPISemError(info, "'data' must not be empty") for (key, value) in members.items(): source = "'data' member '%s'" % key @@ -268,7 +267,6 @@ def check_command(expr, info): raise QAPISemError(info, "'boxed': true requires 'data'") check_type(args, info, "'data'", allow_dict=not boxed) check_type(rets, info, "'returns'", allow_array=True) - check_features(expr.get('features'), info) def check_event(expr, info): @@ -320,18 +318,18 @@ def check_exprs(exprs): if meta == 'enum': check_keys(expr, info, meta, - ['enum', 'data'], ['if', 'prefix']) + ['enum', 'data'], ['if', 'features', 'prefix']) check_enum(expr, info) elif meta == 'union': check_keys(expr, info, meta, ['union', 'data'], - ['base', 'discriminator', 'if']) + ['base', 'discriminator', 'if', 'features']) normalize_members(expr.get('base')) normalize_members(expr['data']) check_union(expr, info) elif meta == 'alternate': check_keys(expr, info, meta, - ['alternate', 'data'], ['if']) + ['alternate', 'data'], ['if', 'features']) normalize_members(expr['data']) check_alternate(expr, info) elif meta == 'struct': @@ -349,13 +347,14 @@ def check_exprs(exprs): check_command(expr, info) elif meta == 'event': check_keys(expr, info, meta, - ['event'], ['data', 'boxed', 'if']) + ['event'], ['data', 'boxed', 'if', 'features']) normalize_members(expr.get('data')) check_event(expr, info) else: assert False, 'unexpected meta type' check_if(expr, info, meta) + check_features(expr.get('features'), info) check_flags(expr, info) return exprs diff --git a/scripts/qapi/gen.py b/scripts/qapi/gen.py index 112b6d94c5..bf5552a4e7 100644 --- a/scripts/qapi/gen.py +++ b/scripts/qapi/gen.py @@ -15,14 +15,13 @@ import errno import os import re -import sys from contextlib import contextmanager from qapi.common import * from qapi.schema import QAPISchemaVisitor -class QAPIGen(object): +class QAPIGen: def __init__(self, fname): self.fname = fname @@ -45,19 +44,21 @@ class QAPIGen(object): return '' def write(self, output_dir): + # Include paths starting with ../ are used to reuse modules of the main + # schema in specialised schemas. Don't overwrite the files that are + # already generated for the main schema. + if self.fname.startswith('../'): + return pathname = os.path.join(output_dir, self.fname) - dir = os.path.dirname(pathname) - if dir: + odir = os.path.dirname(pathname) + if odir: try: - os.makedirs(dir) + os.makedirs(odir) except os.error as e: if e.errno != errno.EEXIST: raise fd = os.open(pathname, os.O_RDWR | os.O_CREAT, 0o666) - if sys.version_info[0] >= 3: - f = open(fd, 'r+', encoding='utf-8') - else: - f = os.fdopen(fd, 'r+') + f = open(fd, 'r+', encoding='utf-8') text = self.get_content() oldtext = f.read(len(text) + 1) if text != oldtext: @@ -86,7 +87,7 @@ def _wrap_ifcond(ifcond, before, after): class QAPIGenCCode(QAPIGen): def __init__(self, fname): - QAPIGen.__init__(self, fname) + super().__init__(fname) self._start_if = None def start_if(self, ifcond): @@ -106,13 +107,13 @@ class QAPIGenCCode(QAPIGen): def get_content(self): assert self._start_if is None - return QAPIGen.get_content(self) + return super().get_content() class QAPIGenC(QAPIGenCCode): def __init__(self, fname, blurb, pydoc): - QAPIGenCCode.__init__(self, fname) + super().__init__(fname) self._blurb = blurb self._copyright = '\n * '.join(re.findall(r'^Copyright .*', pydoc, re.MULTILINE)) @@ -145,7 +146,7 @@ char qapi_dummy_%(name)s; class QAPIGenH(QAPIGenC): def _top(self): - return QAPIGenC._top(self) + guardstart(self.fname) + return super()._top() + guardstart(self.fname) def _bottom(self): return guardend(self.fname) @@ -180,7 +181,7 @@ def ifcontext(ifcond, *args): class QAPIGenDoc(QAPIGen): def _top(self): - return (QAPIGen._top(self) + return (super()._top() + '@c AUTOMATICALLY GENERATED, DO NOT MODIFY\n\n') @@ -201,10 +202,11 @@ class QAPISchemaMonolithicCVisitor(QAPISchemaVisitor): class QAPISchemaModularCVisitor(QAPISchemaVisitor): - def __init__(self, prefix, what, blurb, pydoc): + def __init__(self, prefix, what, user_blurb, builtin_blurb, pydoc): self._prefix = prefix self._what = what - self._blurb = blurb + self._user_blurb = user_blurb + self._builtin_blurb = builtin_blurb self._pydoc = pydoc self._genc = None self._genh = None @@ -245,7 +247,7 @@ class QAPISchemaModularCVisitor(QAPISchemaVisitor): genc = QAPIGenC(basename + '.c', blurb, self._pydoc) genh = QAPIGenH(basename + '.h', blurb, self._pydoc) self._module[name] = (genc, genh) - self._set_module(name) + self._genc, self._genh = self._module[name] def _add_user_module(self, name, blurb): assert self._is_user_module(name) @@ -256,9 +258,6 @@ class QAPISchemaModularCVisitor(QAPISchemaVisitor): def _add_system_module(self, name, blurb): self._add_module(name and './' + name, blurb) - def _set_module(self, name): - self._genc, self._genh = self._module[name] - def write(self, output_dir, opt_builtins=False): for name in self._module: if self._is_builtin_module(name) and not opt_builtins: @@ -267,19 +266,24 @@ class QAPISchemaModularCVisitor(QAPISchemaVisitor): genc.write(output_dir) genh.write(output_dir) + def _begin_system_module(self, name): + pass + def _begin_user_module(self, name): pass def visit_module(self, name): - if name in self._module: - self._set_module(name) - elif self._is_builtin_module(name): - # The built-in module has not been created. No code may - # be generated. - self._genc = None - self._genh = None + if name is None: + if self._builtin_blurb: + self._add_system_module(None, self._builtin_blurb) + self._begin_system_module(name) + else: + # The built-in module has not been created. No code may + # be generated. + self._genc = None + self._genh = None else: - self._add_user_module(name, self._blurb) + self._add_user_module(name, self._user_blurb) self._begin_user_module(name) def visit_include(self, name, info): diff --git a/scripts/qapi/introspect.py b/scripts/qapi/introspect.py index b3a463dd8b..23652be810 100644 --- a/scripts/qapi/introspect.py +++ b/scripts/qapi/introspect.py @@ -10,15 +10,25 @@ This work is licensed under the terms of the GNU GPL, version 2. See the COPYING file in the top-level directory. """ -import string - from qapi.common import * from qapi.gen import QAPISchemaMonolithicCVisitor from qapi.schema import (QAPISchemaArrayType, QAPISchemaBuiltinType, QAPISchemaType) -def to_qlit(obj, level=0, suppress_first_indent=False): +def _make_tree(obj, ifcond, features, extra=None): + if extra is None: + extra = {} + if ifcond: + extra['if'] = ifcond + if features: + obj['features'] = [(f.name, {'if': f.ifcond}) for f in features] + if extra: + return (obj, extra) + return obj + + +def _tree_to_qlit(obj, level=0, suppress_first_indent=False): def indent(level): return level * 4 * ' ' @@ -32,7 +42,7 @@ def to_qlit(obj, level=0, suppress_first_indent=False): ret += indent(level) + '/* %s */\n' % comment if ifcond: ret += gen_if(ifcond) - ret += to_qlit(ifobj, level) + ret += _tree_to_qlit(ifobj, level) if ifcond: ret += '\n' + gen_endif(ifcond) return ret @@ -45,7 +55,7 @@ def to_qlit(obj, level=0, suppress_first_indent=False): elif isinstance(obj, str): ret += 'QLIT_QSTR(' + to_c_string(obj) + ')' elif isinstance(obj, list): - elts = [to_qlit(elt, level + 1).strip('\n') + elts = [_tree_to_qlit(elt, level + 1).strip('\n') for elt in obj] elts.append(indent(level + 1) + "{}") ret += 'QLIT_QLIST(((QLitObject[]) {\n' @@ -55,7 +65,8 @@ def to_qlit(obj, level=0, suppress_first_indent=False): elts = [] for key, value in sorted(obj.items()): elts.append(indent(level + 1) + '{ %s, %s }' % - (to_c_string(key), to_qlit(value, level + 1, True))) + (to_c_string(key), + _tree_to_qlit(value, level + 1, True))) elts.append(indent(level + 1) + '{}') ret += 'QLIT_QDICT(((QLitDictEntry[]) {\n' ret += ',\n'.join(elts) + '\n' @@ -76,12 +87,12 @@ def to_c_string(string): class QAPISchemaGenIntrospectVisitor(QAPISchemaMonolithicCVisitor): def __init__(self, prefix, unmask): - QAPISchemaMonolithicCVisitor.__init__( - self, prefix, 'qapi-introspect', + super().__init__( + prefix, 'qapi-introspect', ' * QAPI/QMP schema introspection', __doc__) self._unmask = unmask self._schema = None - self._qlits = [] + self._trees = [] self._used_types = [] self._name_map = {} self._genc.add(mcgen(''' @@ -110,9 +121,9 @@ extern const QLitObject %(c_name)s; const QLitObject %(c_name)s = %(c_string)s; ''', c_name=c_name(name), - c_string=to_qlit(self._qlits))) + c_string=_tree_to_qlit(self._trees))) self._schema = None - self._qlits = [] + self._trees = [] self._used_types = [] self._name_map = {} @@ -146,89 +157,78 @@ const QLitObject %(c_name)s = %(c_string)s; return '[' + self._use_type(typ.element_type) + ']' return self._name(typ.name) - def _gen_qlit(self, name, mtype, obj, ifcond): - extra = {} + def _gen_tree(self, name, mtype, obj, ifcond, features): + extra = None if mtype not in ('command', 'event', 'builtin', 'array'): if not self._unmask: # Output a comment to make it easy to map masked names # back to the source when reading the generated output. - extra['comment'] = '"%s" = %s' % (self._name(name), name) + extra = {'comment': '"%s" = %s' % (self._name(name), name)} name = self._name(name) obj['name'] = name obj['meta-type'] = mtype - if ifcond: - extra['if'] = ifcond - if extra: - self._qlits.append((obj, extra)) - else: - self._qlits.append(obj) + self._trees.append(_make_tree(obj, ifcond, features, extra)) def _gen_member(self, member): - ret = {'name': member.name, 'type': self._use_type(member.type)} + obj = {'name': member.name, 'type': self._use_type(member.type)} if member.optional: - ret['default'] = None - if member.ifcond: - ret = (ret, {'if': member.ifcond}) - return ret + obj['default'] = None + return _make_tree(obj, member.ifcond, member.features) def _gen_variants(self, tag_name, variants): return {'tag': tag_name, 'variants': [self._gen_variant(v) for v in variants]} def _gen_variant(self, variant): - return ({'case': variant.name, 'type': self._use_type(variant.type)}, - {'if': variant.ifcond}) + obj = {'case': variant.name, 'type': self._use_type(variant.type)} + return _make_tree(obj, variant.ifcond, None) def visit_builtin_type(self, name, info, json_type): - self._gen_qlit(name, 'builtin', {'json-type': json_type}, []) + self._gen_tree(name, 'builtin', {'json-type': json_type}, [], None) - def visit_enum_type(self, name, info, ifcond, members, prefix): - self._gen_qlit(name, 'enum', - {'values': - [(m.name, {'if': m.ifcond}) for m in members]}, - ifcond) + def visit_enum_type(self, name, info, ifcond, features, members, prefix): + self._gen_tree(name, 'enum', + {'values': [_make_tree(m.name, m.ifcond, None) + for m in members]}, + ifcond, features) def visit_array_type(self, name, info, ifcond, element_type): element = self._use_type(element_type) - self._gen_qlit('[' + element + ']', 'array', {'element-type': element}, - ifcond) + self._gen_tree('[' + element + ']', 'array', {'element-type': element}, + ifcond, None) - def visit_object_type_flat(self, name, info, ifcond, members, variants, - features): + def visit_object_type_flat(self, name, info, ifcond, features, + members, variants): obj = {'members': [self._gen_member(m) for m in members]} if variants: obj.update(self._gen_variants(variants.tag_member.name, variants.variants)) - if features: - obj['features'] = [(f.name, {'if': f.ifcond}) for f in features] - self._gen_qlit(name, 'object', obj, ifcond) + self._gen_tree(name, 'object', obj, ifcond, features) - def visit_alternate_type(self, name, info, ifcond, variants): - self._gen_qlit(name, 'alternate', + def visit_alternate_type(self, name, info, ifcond, features, variants): + self._gen_tree(name, 'alternate', {'members': [ - ({'type': self._use_type(m.type)}, {'if': m.ifcond}) - for m in variants.variants]}, ifcond) + _make_tree({'type': self._use_type(m.type)}, + m.ifcond, None) + for m in variants.variants]}, + ifcond, features) - def visit_command(self, name, info, ifcond, arg_type, ret_type, gen, - success_response, boxed, allow_oob, allow_preconfig, - features): + def visit_command(self, name, info, ifcond, features, + arg_type, ret_type, gen, success_response, boxed, + allow_oob, allow_preconfig): arg_type = arg_type or self._schema.the_empty_object_type ret_type = ret_type or self._schema.the_empty_object_type obj = {'arg-type': self._use_type(arg_type), 'ret-type': self._use_type(ret_type)} if allow_oob: obj['allow-oob'] = allow_oob + self._gen_tree(name, 'command', obj, ifcond, features) - if features: - obj['features'] = [(f.name, {'if': f.ifcond}) for f in features] - - self._gen_qlit(name, 'command', obj, ifcond) - - def visit_event(self, name, info, ifcond, arg_type, boxed): + def visit_event(self, name, info, ifcond, features, arg_type, boxed): arg_type = arg_type or self._schema.the_empty_object_type - self._gen_qlit(name, 'event', {'arg-type': self._use_type(arg_type)}, - ifcond) + self._gen_tree(name, 'event', {'arg-type': self._use_type(arg_type)}, + ifcond, features) def gen_introspect(schema, output_dir, prefix, opt_unmask): diff --git a/scripts/qapi/parser.py b/scripts/qapi/parser.py index 342792e410..abadacbb0e 100644 --- a/scripts/qapi/parser.py +++ b/scripts/qapi/parser.py @@ -16,24 +16,20 @@ import os import re -import sys from collections import OrderedDict from qapi.error import QAPIParseError, QAPISemError from qapi.source import QAPISourceInfo -class QAPISchemaParser(object): +class QAPISchemaParser: def __init__(self, fname, previously_included=None, incl_info=None): previously_included = previously_included or set() previously_included.add(os.path.abspath(fname)) try: - if sys.version_info[0] >= 3: - fp = open(fname, 'r', encoding='utf-8') - else: - fp = open(fname, 'r') + fp = open(fname, 'r', encoding='utf-8') self.src = fp.read() except IOError as e: raise QAPISemError(incl_info or QAPISourceInfo(None, None, None), @@ -286,14 +282,13 @@ class QAPISchemaParser(object): doc.end_comment() self.accept() return doc - else: - doc.append(self.val) + doc.append(self.val) self.accept(False) raise QAPIParseError(self, "documentation comment must end with '##'") -class QAPIDoc(object): +class QAPIDoc: """ A documentation comment block, either definition or free-form @@ -312,7 +307,7 @@ class QAPIDoc(object): Free-form documentation blocks consist only of a body section. """ - class Section(object): + class Section: def __init__(self, name=None): # optional section name (argument/member or section name) self.name = name @@ -324,7 +319,7 @@ class QAPIDoc(object): class ArgSection(Section): def __init__(self, name): - QAPIDoc.Section.__init__(self, name) + super().__init__(name) self.member = None def connect(self, member): @@ -496,7 +491,7 @@ class QAPIDoc(object): raise QAPIParseError(self._parser, "'%s' can't follow '%s' section" % (name, self.sections[0].name)) - elif self._is_section_tag(name): + if self._is_section_tag(name): line = line[len(name)+1:] self._start_section(name[:-1]) @@ -560,7 +555,6 @@ class QAPIDoc(object): raise QAPISemError(feature.info, "feature '%s' lacks documentation" % feature.name) - self.features[feature.name] = QAPIDoc.ArgSection(feature.name) self.features[feature.name].connect(feature) def check_expr(self, expr): diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py index cf0045f34e..78309a00f0 100644 --- a/scripts/qapi/schema.py +++ b/scripts/qapi/schema.py @@ -19,12 +19,12 @@ import re from collections import OrderedDict from qapi.common import c_name, pointer_suffix -from qapi.error import QAPIError, QAPIParseError, QAPISemError +from qapi.error import QAPIError, QAPISemError from qapi.expr import check_exprs from qapi.parser import QAPISchemaParser -class QAPISchemaEntity(object): +class QAPISchemaEntity: meta = None def __init__(self, name, info, doc, ifcond=None, features=None): @@ -50,34 +50,34 @@ class QAPISchemaEntity(object): def check(self, schema): assert not self._checked - if self.info: - self._module = os.path.relpath(self.info.fname, - os.path.dirname(schema.fname)) seen = {} for f in self.features: f.check_clash(self.info, seen) - if self.doc: - self.doc.connect_feature(f) - self._checked = True def connect_doc(self, doc=None): - pass + doc = doc or self.doc + if doc: + for f in self.features: + doc.connect_feature(f) def check_doc(self): if self.doc: self.doc.check() + def _set_module(self, schema, info): + assert self._checked + self._module = schema.module_by_fname(info and info.fname) + self._module.add_entity(self) + + def set_module(self, schema): + self._set_module(schema, self.info) + @property def ifcond(self): assert self._checked return self._ifcond - @property - def module(self): - assert self._checked - return self._module - def is_implicit(self): return not self.info @@ -89,61 +89,75 @@ class QAPISchemaEntity(object): return "%s '%s'" % (self.meta, self.name) -class QAPISchemaVisitor(object): +class QAPISchemaVisitor: def visit_begin(self, schema): pass def visit_end(self): pass - def visit_module(self, fname): + def visit_module(self, name): pass def visit_needed(self, entity): # Default to visiting everything return True - def visit_include(self, fname, info): + def visit_include(self, name, info): pass def visit_builtin_type(self, name, info, json_type): pass - def visit_enum_type(self, name, info, ifcond, members, prefix): + def visit_enum_type(self, name, info, ifcond, features, members, prefix): pass def visit_array_type(self, name, info, ifcond, element_type): pass - def visit_object_type(self, name, info, ifcond, base, members, variants, - features): + def visit_object_type(self, name, info, ifcond, features, + base, members, variants): pass - def visit_object_type_flat(self, name, info, ifcond, members, variants, - features): + def visit_object_type_flat(self, name, info, ifcond, features, + members, variants): pass - def visit_alternate_type(self, name, info, ifcond, variants): + def visit_alternate_type(self, name, info, ifcond, features, variants): pass - def visit_command(self, name, info, ifcond, arg_type, ret_type, gen, - success_response, boxed, allow_oob, allow_preconfig, - features): + def visit_command(self, name, info, ifcond, features, + arg_type, ret_type, gen, success_response, boxed, + allow_oob, allow_preconfig): pass - def visit_event(self, name, info, ifcond, arg_type, boxed): + def visit_event(self, name, info, ifcond, features, arg_type, boxed): pass +class QAPISchemaModule: + def __init__(self, name): + self.name = name + self._entity_list = [] + + def add_entity(self, ent): + self._entity_list.append(ent) + + def visit(self, visitor): + visitor.visit_module(self.name) + for entity in self._entity_list: + if visitor.visit_needed(entity): + entity.visit(visitor) + + class QAPISchemaInclude(QAPISchemaEntity): - - def __init__(self, fname, info): - QAPISchemaEntity.__init__(self, None, info, None) - self.fname = fname + def __init__(self, sub_module, info): + super().__init__(None, info, None) + self._sub_module = sub_module def visit(self, visitor): - QAPISchemaEntity.visit(self, visitor) - visitor.visit_include(self.fname, self.info) + super().visit(visitor) + visitor.visit_include(self._sub_module.name, self.info) class QAPISchemaType(QAPISchemaEntity): @@ -179,6 +193,12 @@ class QAPISchemaType(QAPISchemaEntity): return None return self.name + def check(self, schema): + QAPISchemaEntity.check(self, schema) + if 'deprecated' in [f.name for f in self.features]: + raise QAPISemError( + self.info, "feature 'deprecated' is not supported for types") + def describe(self): assert self.meta return "%s type '%s'" % (self.meta, self.name) @@ -188,7 +208,7 @@ class QAPISchemaBuiltinType(QAPISchemaType): meta = 'built-in' def __init__(self, name, json_type, c_type): - QAPISchemaType.__init__(self, name, None, None) + super().__init__(name, None, None) assert not c_type or isinstance(c_type, str) assert json_type in ('string', 'number', 'int', 'boolean', 'null', 'value') @@ -213,15 +233,15 @@ class QAPISchemaBuiltinType(QAPISchemaType): return self.json_type() def visit(self, visitor): - QAPISchemaType.visit(self, visitor) + super().visit(visitor) visitor.visit_builtin_type(self.name, self.info, self.json_type()) class QAPISchemaEnumType(QAPISchemaType): meta = 'enum' - def __init__(self, name, info, doc, ifcond, members, prefix): - QAPISchemaType.__init__(self, name, info, doc, ifcond) + def __init__(self, name, info, doc, ifcond, features, members, prefix): + super().__init__(name, info, doc, ifcond, features) for m in members: assert isinstance(m, QAPISchemaEnumMember) m.set_defined_in(name) @@ -230,16 +250,16 @@ class QAPISchemaEnumType(QAPISchemaType): self.prefix = prefix def check(self, schema): - QAPISchemaType.check(self, schema) + super().check(schema) seen = {} for m in self.members: m.check_clash(self.info, seen) def connect_doc(self, doc=None): + super().connect_doc(doc) doc = doc or self.doc - if doc: - for m in self.members: - doc.connect_member(m) + for m in self.members: + m.connect_doc(doc) def is_implicit(self): # See QAPISchema._make_implicit_enum_type() and ._def_predefineds() @@ -255,37 +275,36 @@ class QAPISchemaEnumType(QAPISchemaType): return 'string' def visit(self, visitor): - QAPISchemaType.visit(self, visitor) - visitor.visit_enum_type(self.name, self.info, self.ifcond, - self.members, self.prefix) + super().visit(visitor) + visitor.visit_enum_type( + self.name, self.info, self.ifcond, self.features, + self.members, self.prefix) class QAPISchemaArrayType(QAPISchemaType): meta = 'array' def __init__(self, name, info, element_type): - QAPISchemaType.__init__(self, name, info, None, None) + super().__init__(name, info, None) assert isinstance(element_type, str) self._element_type_name = element_type self.element_type = None def check(self, schema): - QAPISchemaType.check(self, schema) + super().check(schema) self.element_type = schema.resolve_type( self._element_type_name, self.info, self.info and self.info.defn_meta) assert not isinstance(self.element_type, QAPISchemaArrayType) + def set_module(self, schema): + self._set_module(schema, self.element_type.info) + @property def ifcond(self): assert self._checked return self.element_type.ifcond - @property - def module(self): - assert self._checked - return self.element_type.module - def is_implicit(self): return True @@ -302,7 +321,7 @@ class QAPISchemaArrayType(QAPISchemaType): return 'array of ' + elt_doc_type def visit(self, visitor): - QAPISchemaType.visit(self, visitor) + super().visit(visitor) visitor.visit_array_type(self.name, self.info, self.ifcond, self.element_type) @@ -312,19 +331,19 @@ class QAPISchemaArrayType(QAPISchemaType): class QAPISchemaObjectType(QAPISchemaType): - def __init__(self, name, info, doc, ifcond, - base, local_members, variants, features): + def __init__(self, name, info, doc, ifcond, features, + base, local_members, variants): # struct has local_members, optional base, and no variants # flat union has base, variants, and no local_members # simple union has local_members, variants, and no base - QAPISchemaType.__init__(self, name, info, doc, ifcond, features) + super().__init__(name, info, doc, ifcond, features) self.meta = 'union' if variants else 'struct' assert base is None or isinstance(base, str) for m in local_members: assert isinstance(m, QAPISchemaObjectTypeMember) m.set_defined_in(name) if variants is not None: - assert isinstance(variants, QAPISchemaObjectTypeVariants) + assert isinstance(variants, QAPISchemaVariants) variants.set_defined_in(name) self._base_name = base self.base = None @@ -344,7 +363,7 @@ class QAPISchemaObjectType(QAPISchemaType): raise QAPISemError(self.info, "object %s contains itself" % self.name) - QAPISchemaType.check(self, schema) + super().check(schema) assert self._checked and self.members is None seen = OrderedDict() @@ -380,12 +399,12 @@ class QAPISchemaObjectType(QAPISchemaType): m.check_clash(info, seen) def connect_doc(self, doc=None): + super().connect_doc(doc) doc = doc or self.doc - if doc: - if self.base and self.base.is_implicit(): - self.base.connect_doc(doc) - for m in self.local_members: - doc.connect_member(m) + if self.base and self.base.is_implicit(): + self.base.connect_doc(doc) + for m in self.local_members: + m.connect_doc(doc) @property def ifcond(self): @@ -407,7 +426,7 @@ class QAPISchemaObjectType(QAPISchemaType): def c_name(self): assert self.name != 'q_empty' - return QAPISchemaType.c_name(self) + return super().c_name() def c_type(self): assert not self.is_implicit() @@ -420,94 +439,83 @@ class QAPISchemaObjectType(QAPISchemaType): return 'object' def visit(self, visitor): - QAPISchemaType.visit(self, visitor) - visitor.visit_object_type(self.name, self.info, self.ifcond, - self.base, self.local_members, self.variants, - self.features) - visitor.visit_object_type_flat(self.name, self.info, self.ifcond, - self.members, self.variants, - self.features) + super().visit(visitor) + visitor.visit_object_type( + self.name, self.info, self.ifcond, self.features, + self.base, self.local_members, self.variants) + visitor.visit_object_type_flat( + self.name, self.info, self.ifcond, self.features, + self.members, self.variants) -class QAPISchemaMember(object): - """ Represents object members, enum members and features """ - role = 'member' +class QAPISchemaAlternateType(QAPISchemaType): + meta = 'alternate' - def __init__(self, name, info, ifcond=None): - assert isinstance(name, str) - self.name = name - self.info = info - self.ifcond = ifcond or [] - self.defined_in = None - - def set_defined_in(self, name): - assert not self.defined_in - self.defined_in = name - - def check_clash(self, info, seen): - cname = c_name(self.name) - if cname in seen: - raise QAPISemError( - info, - "%s collides with %s" - % (self.describe(info), seen[cname].describe(info))) - seen[cname] = self - - def describe(self, info): - role = self.role - defined_in = self.defined_in - assert defined_in - - if defined_in.startswith('q_obj_'): - # See QAPISchema._make_implicit_object_type() - reverse the - # mapping there to create a nice human-readable description - defined_in = defined_in[6:] - if defined_in.endswith('-arg'): - # Implicit type created for a command's dict 'data' - assert role == 'member' - role = 'parameter' - elif defined_in.endswith('-base'): - # Implicit type created for a flat union's dict 'base' - role = 'base ' + role - else: - # Implicit type created for a simple union's branch - assert defined_in.endswith('-wrapper') - # Unreachable and not implemented - assert False - elif defined_in.endswith('Kind'): - # See QAPISchema._make_implicit_enum_type() - # Implicit enum created for simple union's branches - assert role == 'value' - role = 'branch' - elif defined_in != info.defn_name: - return "%s '%s' of type '%s'" % (role, self.name, defined_in) - return "%s '%s'" % (role, self.name) - - -class QAPISchemaEnumMember(QAPISchemaMember): - role = 'value' - - -class QAPISchemaFeature(QAPISchemaMember): - role = 'feature' - - -class QAPISchemaObjectTypeMember(QAPISchemaMember): - def __init__(self, name, info, typ, optional, ifcond=None): - QAPISchemaMember.__init__(self, name, info, ifcond) - assert isinstance(typ, str) - assert isinstance(optional, bool) - self._type_name = typ - self.type = None - self.optional = optional + def __init__(self, name, info, doc, ifcond, features, variants): + super().__init__(name, info, doc, ifcond, features) + assert isinstance(variants, QAPISchemaVariants) + assert variants.tag_member + variants.set_defined_in(name) + variants.tag_member.set_defined_in(self.name) + self.variants = variants def check(self, schema): - assert self.defined_in - self.type = schema.resolve_type(self._type_name, self.info, - self.describe) + super().check(schema) + self.variants.tag_member.check(schema) + # Not calling self.variants.check_clash(), because there's nothing + # to clash with + self.variants.check(schema, {}) + # Alternate branch names have no relation to the tag enum values; + # so we have to check for potential name collisions ourselves. + seen = {} + types_seen = {} + for v in self.variants.variants: + v.check_clash(self.info, seen) + qtype = v.type.alternate_qtype() + if not qtype: + raise QAPISemError( + self.info, + "%s cannot use %s" + % (v.describe(self.info), v.type.describe())) + conflicting = set([qtype]) + if qtype == 'QTYPE_QSTRING': + if isinstance(v.type, QAPISchemaEnumType): + for m in v.type.members: + if m.name in ['on', 'off']: + conflicting.add('QTYPE_QBOOL') + if re.match(r'[-+0-9.]', m.name): + # lazy, could be tightened + conflicting.add('QTYPE_QNUM') + else: + conflicting.add('QTYPE_QNUM') + conflicting.add('QTYPE_QBOOL') + for qt in conflicting: + if qt in types_seen: + raise QAPISemError( + self.info, + "%s can't be distinguished from '%s'" + % (v.describe(self.info), types_seen[qt])) + types_seen[qt] = v.name + + def connect_doc(self, doc=None): + super().connect_doc(doc) + doc = doc or self.doc + for v in self.variants.variants: + v.connect_doc(doc) + + def c_type(self): + return c_name(self.name) + pointer_suffix + + def json_type(self): + return 'value' + + def visit(self, visitor): + super().visit(visitor) + visitor.visit_alternate_type( + self.name, self.info, self.ifcond, self.features, self.variants) -class QAPISchemaObjectTypeVariants(object): +class QAPISchemaVariants: def __init__(self, tag_name, info, tag_member, variants): # Flat unions pass tag_name but not tag_member. # Simple unions and alternates pass tag_member but not tag_name. @@ -517,7 +525,7 @@ class QAPISchemaObjectTypeVariants(object): assert (isinstance(tag_name, str) or isinstance(tag_member, QAPISchemaObjectTypeMember)) for v in variants: - assert isinstance(v, QAPISchemaObjectTypeVariant) + assert isinstance(v, QAPISchemaVariant) self._tag_name = tag_name self.info = info self.tag_member = tag_member @@ -564,11 +572,11 @@ class QAPISchemaObjectTypeVariants(object): assert self.tag_member.ifcond == [] if self._tag_name: # flat union # branches that are not explicitly covered get an empty type - cases = set([v.name for v in self.variants]) + cases = {v.name for v in self.variants} for m in self.tag_member.type.members: if m.name not in cases: - v = QAPISchemaObjectTypeVariant(m.name, self.info, - 'q_empty', m.ifcond) + v = QAPISchemaVariant(m.name, self.info, + 'q_empty', m.ifcond) v.set_defined_in(self.tag_member.defined_in) self.variants.append(v) if not self.variants: @@ -598,88 +606,115 @@ class QAPISchemaObjectTypeVariants(object): v.type.check_clash(info, dict(seen)) -class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember): +class QAPISchemaMember: + """ Represents object members, enum members and features """ + role = 'member' + + def __init__(self, name, info, ifcond=None): + assert isinstance(name, str) + self.name = name + self.info = info + self.ifcond = ifcond or [] + self.defined_in = None + + def set_defined_in(self, name): + assert not self.defined_in + self.defined_in = name + + def check_clash(self, info, seen): + cname = c_name(self.name) + if cname in seen: + raise QAPISemError( + info, + "%s collides with %s" + % (self.describe(info), seen[cname].describe(info))) + seen[cname] = self + + def connect_doc(self, doc): + if doc: + doc.connect_member(self) + + def describe(self, info): + role = self.role + defined_in = self.defined_in + assert defined_in + + if defined_in.startswith('q_obj_'): + # See QAPISchema._make_implicit_object_type() - reverse the + # mapping there to create a nice human-readable description + defined_in = defined_in[6:] + if defined_in.endswith('-arg'): + # Implicit type created for a command's dict 'data' + assert role == 'member' + role = 'parameter' + elif defined_in.endswith('-base'): + # Implicit type created for a flat union's dict 'base' + role = 'base ' + role + else: + # Implicit type created for a simple union's branch + assert defined_in.endswith('-wrapper') + # Unreachable and not implemented + assert False + elif defined_in.endswith('Kind'): + # See QAPISchema._make_implicit_enum_type() + # Implicit enum created for simple union's branches + assert role == 'value' + role = 'branch' + elif defined_in != info.defn_name: + return "%s '%s' of type '%s'" % (role, self.name, defined_in) + return "%s '%s'" % (role, self.name) + + +class QAPISchemaEnumMember(QAPISchemaMember): + role = 'value' + + +class QAPISchemaFeature(QAPISchemaMember): + role = 'feature' + + +class QAPISchemaObjectTypeMember(QAPISchemaMember): + def __init__(self, name, info, typ, optional, ifcond=None, features=None): + super().__init__(name, info, ifcond) + assert isinstance(typ, str) + assert isinstance(optional, bool) + for f in features or []: + assert isinstance(f, QAPISchemaFeature) + f.set_defined_in(name) + self._type_name = typ + self.type = None + self.optional = optional + self.features = features or [] + + def check(self, schema): + assert self.defined_in + self.type = schema.resolve_type(self._type_name, self.info, + self.describe) + seen = {} + for f in self.features: + f.check_clash(self.info, seen) + + def connect_doc(self, doc): + super().connect_doc(doc) + if doc: + for f in self.features: + doc.connect_feature(f) + + +class QAPISchemaVariant(QAPISchemaObjectTypeMember): role = 'branch' def __init__(self, name, info, typ, ifcond=None): - QAPISchemaObjectTypeMember.__init__(self, name, info, typ, - False, ifcond) - - -class QAPISchemaAlternateType(QAPISchemaType): - meta = 'alternate' - - def __init__(self, name, info, doc, ifcond, variants): - QAPISchemaType.__init__(self, name, info, doc, ifcond) - assert isinstance(variants, QAPISchemaObjectTypeVariants) - assert variants.tag_member - variants.set_defined_in(name) - variants.tag_member.set_defined_in(self.name) - self.variants = variants - - def check(self, schema): - QAPISchemaType.check(self, schema) - self.variants.tag_member.check(schema) - # Not calling self.variants.check_clash(), because there's nothing - # to clash with - self.variants.check(schema, {}) - # Alternate branch names have no relation to the tag enum values; - # so we have to check for potential name collisions ourselves. - seen = {} - types_seen = {} - for v in self.variants.variants: - v.check_clash(self.info, seen) - qtype = v.type.alternate_qtype() - if not qtype: - raise QAPISemError( - self.info, - "%s cannot use %s" - % (v.describe(self.info), v.type.describe())) - conflicting = set([qtype]) - if qtype == 'QTYPE_QSTRING': - if isinstance(v.type, QAPISchemaEnumType): - for m in v.type.members: - if m.name in ['on', 'off']: - conflicting.add('QTYPE_QBOOL') - if re.match(r'[-+0-9.]', m.name): - # lazy, could be tightened - conflicting.add('QTYPE_QNUM') - else: - conflicting.add('QTYPE_QNUM') - conflicting.add('QTYPE_QBOOL') - for qt in conflicting: - if qt in types_seen: - raise QAPISemError( - self.info, - "%s can't be distinguished from '%s'" - % (v.describe(self.info), types_seen[qt])) - types_seen[qt] = v.name - - def connect_doc(self, doc=None): - doc = doc or self.doc - if doc: - for v in self.variants.variants: - doc.connect_member(v) - - def c_type(self): - return c_name(self.name) + pointer_suffix - - def json_type(self): - return 'value' - - def visit(self, visitor): - QAPISchemaType.visit(self, visitor) - visitor.visit_alternate_type(self.name, self.info, self.ifcond, - self.variants) + super().__init__(name, info, typ, False, ifcond) class QAPISchemaCommand(QAPISchemaEntity): meta = 'command' - def __init__(self, name, info, doc, ifcond, arg_type, ret_type, - gen, success_response, boxed, allow_oob, allow_preconfig, - features): - QAPISchemaEntity.__init__(self, name, info, doc, ifcond, features) + def __init__(self, name, info, doc, ifcond, features, + arg_type, ret_type, + gen, success_response, boxed, allow_oob, allow_preconfig): + super().__init__(name, info, doc, ifcond, features) assert not arg_type or isinstance(arg_type, str) assert not ret_type or isinstance(ret_type, str) self._arg_type_name = arg_type @@ -693,7 +728,7 @@ class QAPISchemaCommand(QAPISchemaEntity): self.allow_preconfig = allow_preconfig def check(self, schema): - QAPISchemaEntity.check(self, schema) + super().check(schema) if self._arg_type_name: self.arg_type = schema.resolve_type( self._arg_type_name, self.info, "command's 'data'") @@ -711,43 +746,43 @@ class QAPISchemaCommand(QAPISchemaEntity): self.ret_type = schema.resolve_type( self._ret_type_name, self.info, "command's 'returns'") if self.name not in self.info.pragma.returns_whitelist: - if not (isinstance(self.ret_type, QAPISchemaObjectType) - or (isinstance(self.ret_type, QAPISchemaArrayType) - and isinstance(self.ret_type.element_type, - QAPISchemaObjectType))): + typ = self.ret_type + if isinstance(typ, QAPISchemaArrayType): + typ = self.ret_type.element_type + assert typ + if not isinstance(typ, QAPISchemaObjectType): raise QAPISemError( self.info, "command's 'returns' cannot take %s" % self.ret_type.describe()) def connect_doc(self, doc=None): + super().connect_doc(doc) doc = doc or self.doc if doc: if self.arg_type and self.arg_type.is_implicit(): self.arg_type.connect_doc(doc) def visit(self, visitor): - QAPISchemaEntity.visit(self, visitor) - visitor.visit_command(self.name, self.info, self.ifcond, - self.arg_type, self.ret_type, - self.gen, self.success_response, - self.boxed, self.allow_oob, - self.allow_preconfig, - self.features) + super().visit(visitor) + visitor.visit_command( + self.name, self.info, self.ifcond, self.features, + self.arg_type, self.ret_type, self.gen, self.success_response, + self.boxed, self.allow_oob, self.allow_preconfig) class QAPISchemaEvent(QAPISchemaEntity): meta = 'event' - def __init__(self, name, info, doc, ifcond, arg_type, boxed): - QAPISchemaEntity.__init__(self, name, info, doc, ifcond) + def __init__(self, name, info, doc, ifcond, features, arg_type, boxed): + super().__init__(name, info, doc, ifcond, features) assert not arg_type or isinstance(arg_type, str) self._arg_type_name = arg_type self.arg_type = None self.boxed = boxed def check(self, schema): - QAPISchemaEntity.check(self, schema) + super().check(schema) if self._arg_type_name: self.arg_type = schema.resolve_type( self._arg_type_name, self.info, "event's 'data'") @@ -763,18 +798,20 @@ class QAPISchemaEvent(QAPISchemaEntity): % self.arg_type.describe()) def connect_doc(self, doc=None): + super().connect_doc(doc) doc = doc or self.doc if doc: if self.arg_type and self.arg_type.is_implicit(): self.arg_type.connect_doc(doc) def visit(self, visitor): - QAPISchemaEntity.visit(self, visitor) - visitor.visit_event(self.name, self.info, self.ifcond, - self.arg_type, self.boxed) + super().visit(visitor) + visitor.visit_event( + self.name, self.info, self.ifcond, self.features, + self.arg_type, self.boxed) -class QAPISchema(object): +class QAPISchema: def __init__(self, fname): self.fname = fname parser = QAPISchemaParser(fname) @@ -782,6 +819,10 @@ class QAPISchema(object): self.docs = parser.docs self._entity_list = [] self._entity_dict = {} + self._module_dict = OrderedDict() + self._schema_dir = os.path.dirname(fname) + self._make_module(None) # built-ins + self._make_module(fname) self._predefining = True self._def_predefineds() self._predefining = False @@ -825,14 +866,26 @@ class QAPISchema(object): info, "%s uses unknown type '%s'" % (what, name)) return typ + def _module_name(self, fname): + if fname is None: + return None + return os.path.relpath(fname, self._schema_dir) + + def _make_module(self, fname): + name = self._module_name(fname) + if name not in self._module_dict: + self._module_dict[name] = QAPISchemaModule(name) + return self._module_dict[name] + + def module_by_fname(self, fname): + name = self._module_name(fname) + assert name in self._module_dict + return self._module_dict[name] + def _def_include(self, expr, info, doc): include = expr['include'] assert doc is None - main_info = info - while main_info.parent: - main_info = main_info.parent - fname = os.path.relpath(include, os.path.dirname(main_info.fname)) - self._def_entity(QAPISchemaInclude(fname, info)) + self._def_entity(QAPISchemaInclude(self._make_module(include), info)) def _def_builtin_type(self, name, json_type, c_type): self._def_entity(QAPISchemaBuiltinType(name, json_type, c_type)) @@ -860,7 +913,7 @@ class QAPISchema(object): ('null', 'null', 'QNull' + pointer_suffix)]: self._def_builtin_type(*t) self.the_empty_object_type = QAPISchemaObjectType( - 'q_empty', None, None, None, None, [], None, []) + 'q_empty', None, None, None, None, None, [], None) self._def_entity(self.the_empty_object_type) qtypes = ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', @@ -868,10 +921,12 @@ class QAPISchema(object): qtype_values = self._make_enum_members( [{'name': n} for n in qtypes], None) - self._def_entity(QAPISchemaEnumType('QType', None, None, None, + self._def_entity(QAPISchemaEnumType('QType', None, None, None, None, qtype_values, 'QTYPE')) def _make_features(self, features, info): + if features is None: + return [] return [QAPISchemaFeature(f['name'], info, f.get('if')) for f in features] @@ -883,7 +938,8 @@ class QAPISchema(object): # See also QAPISchemaObjectTypeMember.describe() name = name + 'Kind' # reserved by check_defn_name_str() self._def_entity(QAPISchemaEnumType( - name, info, None, ifcond, self._make_enum_members(values, info), + name, info, None, ifcond, None, + self._make_enum_members(values, info), None)) return name @@ -911,8 +967,8 @@ class QAPISchema(object): # TODO kill simple unions or implement the disjunction assert (ifcond or []) == typ._ifcond # pylint: disable=protected-access else: - self._def_entity(QAPISchemaObjectType(name, info, None, ifcond, - None, members, None, [])) + self._def_entity(QAPISchemaObjectType( + name, info, None, ifcond, None, None, members, None)) return name def _def_enum_type(self, expr, info, doc): @@ -920,11 +976,12 @@ class QAPISchema(object): data = expr['data'] prefix = expr.get('prefix') ifcond = expr.get('if') + features = self._make_features(expr.get('features'), info) self._def_entity(QAPISchemaEnumType( - name, info, doc, ifcond, + name, info, doc, ifcond, features, self._make_enum_members(data, info), prefix)) - def _make_member(self, name, typ, ifcond, info): + def _make_member(self, name, typ, ifcond, features, info): optional = False if name.startswith('*'): name = name[1:] @@ -932,10 +989,12 @@ class QAPISchema(object): if isinstance(typ, list): assert len(typ) == 1 typ = self._make_array_type(typ[0], info) - return QAPISchemaObjectTypeMember(name, info, typ, optional, ifcond) + return QAPISchemaObjectTypeMember(name, info, typ, optional, ifcond, + self._make_features(features, info)) def _make_members(self, data, info): - return [self._make_member(key, value['type'], value.get('if'), info) + return [self._make_member(key, value['type'], value.get('if'), + value.get('features'), info) for (key, value) in data.items()] def _def_struct_type(self, expr, info, doc): @@ -943,15 +1002,14 @@ class QAPISchema(object): base = expr.get('base') data = expr['data'] ifcond = expr.get('if') - features = expr.get('features', []) + features = self._make_features(expr.get('features'), info) self._def_entity(QAPISchemaObjectType( - name, info, doc, ifcond, base, + name, info, doc, ifcond, features, base, self._make_members(data, info), - None, - self._make_features(features, info))) + None)) def _make_variant(self, case, typ, ifcond, info): - return QAPISchemaObjectTypeVariant(case, info, typ, ifcond) + return QAPISchemaVariant(case, info, typ, ifcond) def _make_simple_variant(self, case, typ, ifcond, info): if isinstance(typ, list): @@ -959,14 +1017,15 @@ class QAPISchema(object): typ = self._make_array_type(typ[0], info) typ = self._make_implicit_object_type( typ, info, self.lookup_type(typ), - 'wrapper', [self._make_member('data', typ, None, info)]) - return QAPISchemaObjectTypeVariant(case, info, typ, ifcond) + 'wrapper', [self._make_member('data', typ, None, None, info)]) + return QAPISchemaVariant(case, info, typ, ifcond) def _def_union_type(self, expr, info, doc): name = expr['union'] data = expr['data'] base = expr.get('base') ifcond = expr.get('if') + features = self._make_features(expr.get('features'), info) tag_name = expr.get('discriminator') tag_member = None if isinstance(base, dict): @@ -987,22 +1046,23 @@ class QAPISchema(object): tag_member = QAPISchemaObjectTypeMember('type', info, typ, False) members = [tag_member] self._def_entity( - QAPISchemaObjectType(name, info, doc, ifcond, base, members, - QAPISchemaObjectTypeVariants( - tag_name, info, tag_member, variants), - [])) + QAPISchemaObjectType(name, info, doc, ifcond, features, + base, members, + QAPISchemaVariants( + tag_name, info, tag_member, variants))) def _def_alternate_type(self, expr, info, doc): name = expr['alternate'] data = expr['data'] ifcond = expr.get('if') + features = self._make_features(expr.get('features'), info) variants = [self._make_variant(key, value['type'], value.get('if'), info) for (key, value) in data.items()] tag_member = QAPISchemaObjectTypeMember('type', info, 'QType', False) self._def_entity( - QAPISchemaAlternateType(name, info, doc, ifcond, - QAPISchemaObjectTypeVariants( + QAPISchemaAlternateType(name, info, doc, ifcond, features, + QAPISchemaVariants( None, info, tag_member, variants))) def _def_command(self, expr, info, doc): @@ -1015,27 +1075,31 @@ class QAPISchema(object): allow_oob = expr.get('allow-oob', False) allow_preconfig = expr.get('allow-preconfig', False) ifcond = expr.get('if') - features = expr.get('features', []) + features = self._make_features(expr.get('features'), info) if isinstance(data, OrderedDict): data = self._make_implicit_object_type( - name, info, ifcond, 'arg', self._make_members(data, info)) + name, info, ifcond, + 'arg', self._make_members(data, info)) if isinstance(rets, list): assert len(rets) == 1 rets = self._make_array_type(rets[0], info) - self._def_entity(QAPISchemaCommand(name, info, doc, ifcond, data, rets, + self._def_entity(QAPISchemaCommand(name, info, doc, ifcond, features, + data, rets, gen, success_response, - boxed, allow_oob, allow_preconfig, - self._make_features(features, info))) + boxed, allow_oob, allow_preconfig)) def _def_event(self, expr, info, doc): name = expr['event'] data = expr.get('data') boxed = expr.get('boxed', False) ifcond = expr.get('if') + features = self._make_features(expr.get('features'), info) if isinstance(data, OrderedDict): data = self._make_implicit_object_type( - name, info, ifcond, 'arg', self._make_members(data, info)) - self._def_entity(QAPISchemaEvent(name, info, doc, ifcond, data, boxed)) + name, info, ifcond, + 'arg', self._make_members(data, info)) + self._def_entity(QAPISchemaEvent(name, info, doc, ifcond, features, + data, boxed)) def _def_exprs(self, exprs): for expr_elem in exprs: @@ -1064,15 +1128,11 @@ class QAPISchema(object): ent.check(self) ent.connect_doc() ent.check_doc() + for ent in self._entity_list: + ent.set_module(self) def visit(self, visitor): visitor.visit_begin(self) - module = None - visitor.visit_module(module) - for entity in self._entity_list: - if visitor.visit_needed(entity): - if entity.module != module: - module = entity.module - visitor.visit_module(module) - entity.visit(visitor) + for mod in self._module_dict.values(): + mod.visit(visitor) visitor.visit_end() diff --git a/scripts/qapi/source.py b/scripts/qapi/source.py index 8956885033..e97b9a8e15 100644 --- a/scripts/qapi/source.py +++ b/scripts/qapi/source.py @@ -13,7 +13,7 @@ import copy import sys -class QAPISchemaPragma(object): +class QAPISchemaPragma: def __init__(self): # Are documentation comments required? self.doc_required = False @@ -23,7 +23,7 @@ class QAPISchemaPragma(object): self.name_case_whitelist = [] -class QAPISourceInfo(object): +class QAPISourceInfo: def __init__(self, fname, line, parent): self.fname = fname self.line = line diff --git a/scripts/qapi/types.py b/scripts/qapi/types.py index d8751daa04..3ad33af4ee 100644 --- a/scripts/qapi/types.py +++ b/scripts/qapi/types.py @@ -241,10 +241,11 @@ void qapi_free_%(c_name)s(%(c_name)s *obj) class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor): def __init__(self, prefix): - QAPISchemaModularCVisitor.__init__( - self, prefix, 'qapi-types', ' * Schema-defined QAPI types', - __doc__) - self._add_system_module(None, ' * Built-in QAPI types') + super().__init__( + prefix, 'qapi-types', ' * Schema-defined QAPI types', + ' * Built-in QAPI types', __doc__) + + def _begin_system_module(self, name): self._genc.preamble_add(mcgen(''' #include "qemu/osdep.h" #include "qapi/dealloc-visitor.h" @@ -277,7 +278,7 @@ class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor): self._genh.add(gen_type_cleanup_decl(name)) self._genc.add(gen_type_cleanup(name)) - def visit_enum_type(self, name, info, ifcond, members, prefix): + def visit_enum_type(self, name, info, ifcond, features, members, prefix): with ifcontext(ifcond, self._genh, self._genc): self._genh.preamble_add(gen_enum(name, members, prefix)) self._genc.add(gen_enum_lookup(name, members, prefix)) @@ -288,8 +289,8 @@ class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor): self._genh.add(gen_array(name, element_type)) self._gen_type_cleanup(name) - def visit_object_type(self, name, info, ifcond, base, members, variants, - features): + def visit_object_type(self, name, info, ifcond, features, + base, members, variants): # Nothing to do for the special empty builtin if name == 'q_empty': return @@ -305,7 +306,7 @@ class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor): # implicit types won't be directly allocated/freed self._gen_type_cleanup(name) - def visit_alternate_type(self, name, info, ifcond, variants): + def visit_alternate_type(self, name, info, ifcond, features, variants): with ifcontext(ifcond, self._genh): self._genh.preamble_add(gen_fwd_object_or_array(name)) self._genh.add(gen_object(name, ifcond, None, diff --git a/scripts/qapi/visit.py b/scripts/qapi/visit.py index c72f2bc5c0..23d9194aa4 100644 --- a/scripts/qapi/visit.py +++ b/scripts/qapi/visit.py @@ -283,10 +283,11 @@ out: class QAPISchemaGenVisitVisitor(QAPISchemaModularCVisitor): def __init__(self, prefix): - QAPISchemaModularCVisitor.__init__( - self, prefix, 'qapi-visit', ' * Schema-defined QAPI visitors', - __doc__) - self._add_system_module(None, ' * Built-in QAPI visitors') + super().__init__( + prefix, 'qapi-visit', ' * Schema-defined QAPI visitors', + ' * Built-in QAPI visitors', __doc__) + + def _begin_system_module(self, name): self._genc.preamble_add(mcgen(''' #include "qemu/osdep.h" #include "qapi/error.h" @@ -296,8 +297,7 @@ class QAPISchemaGenVisitVisitor(QAPISchemaModularCVisitor): #include "qapi/visitor.h" #include "qapi/qapi-builtin-types.h" -''', - prefix=prefix)) +''')) def _begin_user_module(self, name): types = self._module_basename('qapi-types', name) @@ -316,7 +316,7 @@ class QAPISchemaGenVisitVisitor(QAPISchemaModularCVisitor): ''', types=types)) - def visit_enum_type(self, name, info, ifcond, members, prefix): + def visit_enum_type(self, name, info, ifcond, features, members, prefix): with ifcontext(ifcond, self._genh, self._genc): self._genh.add(gen_visit_decl(name, scalar=True)) self._genc.add(gen_visit_enum(name)) @@ -326,8 +326,8 @@ class QAPISchemaGenVisitVisitor(QAPISchemaModularCVisitor): self._genh.add(gen_visit_decl(name)) self._genc.add(gen_visit_list(name, element_type)) - def visit_object_type(self, name, info, ifcond, base, members, variants, - features): + def visit_object_type(self, name, info, ifcond, features, + base, members, variants): # Nothing to do for the special empty builtin if name == 'q_empty': return @@ -342,7 +342,7 @@ class QAPISchemaGenVisitVisitor(QAPISchemaModularCVisitor): self._genh.add(gen_visit_decl(name)) self._genc.add(gen_visit_object(name, base, members, variants)) - def visit_alternate_type(self, name, info, ifcond, variants): + def visit_alternate_type(self, name, info, ifcond, features, variants): with ifcontext(ifcond, self._genh, self._genc): self._genh.add(gen_visit_decl(name)) self._genc.add(gen_visit_alternate(name, variants)) diff --git a/scripts/qemu-trace-stap b/scripts/qemu-trace-stap index 91d1051cdc..90527eb974 100755 --- a/scripts/qemu-trace-stap +++ b/scripts/qemu-trace-stap @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python3 # -*- python -*- # # Copyright (C) 2019 Red Hat, Inc @@ -18,8 +18,6 @@ # You should have received a copy of the GNU General Public License # along with this program; if not, see . -from __future__ import print_function - import argparse import copy import os.path @@ -104,7 +102,9 @@ def cmd_list(args): if verbose: print("Listing probes with name '%s'" % script) proc = subprocess.Popen(["stap", "-l", script], - stdout=subprocess.PIPE, env=tapset_env(tapsets)) + stdout=subprocess.PIPE, + universal_newlines=True, + env=tapset_env(tapsets)) out, err = proc.communicate() if proc.returncode != 0: print("No probes found, are the tapsets installed in %s" % tapset_dir(args.binary)) diff --git a/scripts/qemu-trace-stap.texi b/scripts/qemu-trace-stap.texi deleted file mode 100644 index 07bb9eb94e..0000000000 --- a/scripts/qemu-trace-stap.texi +++ /dev/null @@ -1,140 +0,0 @@ -@example -@c man begin SYNOPSIS -@command{qemu-trace-stap} @var{GLOBAL-OPTIONS} @var{COMMAND} @var{COMMAND-OPTIONS} @var{ARGS...} -@c man end -@end example - -@c man begin DESCRIPTION - -The @command{qemu-trace-stap} program facilitates tracing of the execution -of QEMU emulators using SystemTap. - -It is required to have the SystemTap runtime environment installed to use -this program, since it is a wrapper around execution of the @command{stap} -program. - -@c man end - -@c man begin OPTIONS - -The following global options may be used regardless of which command -is executed: - -@table @option -@item @var{--verbose}, @var{-v} - -Display verbose information about command execution. - -@end table - -The following commands are valid: - -@table @option - -@item @var{list} @var{BINARY} @var{PATTERN...} - -List all the probe names provided by @var{BINARY} that match -@var{PATTERN}. - -If @var{BINARY} is not an absolute path, it will be located by searching -the directories listed in the @code{$PATH} environment variable. - -@var{PATTERN} is a plain string that is used to filter the results of -this command. It may optionally contain a @code{*} wildcard to facilitate -matching multiple probes without listing each one explicitly. Multiple -@var{PATTERN} arguments may be given, causing listing of probes that match -any of the listed names. If no @var{PATTERN} is given, the all possible -probes will be listed. - -For example, to list all probes available in the @command{qemu-system-x86_64} -binary: - -@example -$ qemu-trace-stap list qemu-system-x86_64 -@end example - -To filter the list to only cover probes related to QEMU's cryptographic -subsystem, in a binary outside @code{$PATH} - -@example -$ qemu-trace-stap list /opt/qemu/4.0.0/bin/qemu-system-x86_64 'qcrypto*' -@end example - - -@item @var{run} @var{OPTIONS} @var{BINARY} @var{PATTERN...} - -Run a trace session, printing formatted output any time a process that is -executing @var{BINARY} triggers a probe matching @var{PATTERN}. - -If @var{BINARY} is not an absolute path, it will be located by searching -the directories listed in the @code{$PATH} environment variable. - -@var{PATTERN} is a plain string that matches a probe name shown by the -@var{list} command. It may optionally contain a @code{*} wildcard to -facilitate matching multiple probes without listing each one explicitly. -Multiple @var{PATTERN} arguments may be given, causing all matching probes -to be monitored. At least one @var{PATTERN} is required, since stap is not -capable of tracing all known QEMU probes concurrently without overflowing -its trace buffer. - -Invocation of this command does not need to be synchronized with -invocation of the QEMU process(es). It will match probes on all -existing running processes and all future launched processes, -unless told to only monitor a specific process. - -Valid command specific options are: - -@table @option -@item @var{--pid=PID}, @var{-p PID} - -Restrict the tracing session so that it only triggers for the process -identified by @code{PID}. - -@end table - -For example, to monitor all processes executing @command{qemu-system-x86_64} -as found on $PATH, displaying all I/O related probes: - -@example -$ qemu-trace-stap run qemu-system-x86_64 'qio*' -@end example - -To monitor only the QEMU process with PID 1732 - -@example -$ qemu-trace-stap run --pid=1732 qemu-system-x86_64 'qio*' -@end example - -To monitor QEMU processes running an alternative binary outside of -@code{$PATH}, displaying verbose information about setup of the -tracing environment: - -@example -$ qemu-trace-stap -v run /opt/qemu/4.0.0/qemu-system-x86_64 'qio*' -@end example - -@end table - -@c man end - -@ignore - -@setfilename qemu-trace-stap -@settitle QEMU SystemTap trace tool - -@c man begin LICENSE - -Copyright (C) 2019 Red Hat, Inc. - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. - -@c man end - -@c man begin SEEALSO -qemu(1), stap(1) -@c man end - -@end ignore 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 . -from __future__ import print_function import argparse import struct from collections import namedtuple diff --git a/scripts/signrom.py b/scripts/signrom.py old mode 100644 new mode 100755 index 313ee28a17..43693dba56 --- 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 + checksum = (checksum - b) & 255 -# Python 3 no longer allows chr(checksum) fout.write(struct.pack('B', checksum)) fin.close() diff --git a/scripts/simplebench/bench-example.py b/scripts/simplebench/bench-example.py new file mode 100644 index 0000000000..c642a5b891 --- /dev/null +++ b/scripts/simplebench/bench-example.py @@ -0,0 +1,80 @@ +#!/usr/bin/env python3 +# +# Benchmark example +# +# 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 . +# + +import simplebench +from bench_block_job import bench_block_copy, drv_file, drv_nbd + + +def bench_func(env, case): + """ Handle one "cell" of benchmarking table. """ + return bench_block_copy(env['qemu_binary'], env['cmd'], + case['source'], case['target']) + + +# You may set the following five variables to correct values, to turn this +# example to real benchmark. +ssd_source = '/path-to-raw-source-image-at-ssd' +ssd_target = '/path-to-raw-target-image-at-ssd' +hdd_target = '/path-to-raw-source-image-at-hdd' +nbd_ip = 'nbd-ip-addr' +nbd_port = 'nbd-port-number' + +# Test-cases are "rows" in benchmark resulting table, 'id' is a caption for +# the row, other fields are handled by bench_func. +test_cases = [ + { + 'id': 'ssd -> ssd', + 'source': drv_file(ssd_source), + 'target': drv_file(ssd_target) + }, + { + 'id': 'ssd -> hdd', + 'source': drv_file(ssd_source), + 'target': drv_file(hdd_target) + }, + { + 'id': 'ssd -> nbd', + 'source': drv_file(ssd_source), + 'target': drv_nbd(nbd_ip, nbd_port) + }, +] + +# Test-envs are "columns" in benchmark resulting table, 'id is a caption for +# the column, other fields are handled by bench_func. +test_envs = [ + { + 'id': 'backup-1', + 'cmd': 'blockdev-backup', + 'qemu_binary': '/path-to-qemu-binary-1' + }, + { + 'id': 'backup-2', + 'cmd': 'blockdev-backup', + 'qemu_binary': '/path-to-qemu-binary-2' + }, + { + 'id': 'mirror', + 'cmd': 'blockdev-mirror', + 'qemu_binary': '/path-to-qemu-binary-1' + } +] + +result = simplebench.bench(bench_func, test_envs, test_cases, count=3) +print(simplebench.ascii(result)) diff --git a/scripts/simplebench/bench_block_job.py b/scripts/simplebench/bench_block_job.py new file mode 100755 index 0000000000..9808d696cf --- /dev/null +++ b/scripts/simplebench/bench_block_job.py @@ -0,0 +1,119 @@ +#!/usr/bin/env python +# +# Benchmark block jobs +# +# 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 . +# + + +import sys +import os +import socket +import json + +sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python')) +from qemu.machine import QEMUMachine +from qemu.qmp import QMPConnectError + + +def bench_block_job(cmd, cmd_args, qemu_args): + """Benchmark block-job + + cmd -- qmp command to run block-job (like blockdev-backup) + cmd_args -- dict of qmp command arguments + qemu_args -- list of Qemu command line arguments, including path to Qemu + binary + + Returns {'seconds': int} on success and {'error': str} on failure, dict may + contain addional 'vm-log' field. Return value is compatible with + simplebench lib. + """ + + vm = QEMUMachine(qemu_args[0], args=qemu_args[1:]) + + try: + vm.launch() + except OSError as e: + return {'error': 'popen failed: ' + str(e)} + except (QMPConnectError, socket.timeout): + return {'error': 'qemu failed: ' + str(vm.get_log())} + + try: + res = vm.qmp(cmd, **cmd_args) + if res != {'return': {}}: + vm.shutdown() + return {'error': '"{}" command failed: {}'.format(cmd, str(res))} + + e = vm.event_wait('JOB_STATUS_CHANGE') + assert e['data']['status'] == 'created' + start_ms = e['timestamp']['seconds'] * 1000000 + \ + e['timestamp']['microseconds'] + + e = vm.events_wait((('BLOCK_JOB_READY', None), + ('BLOCK_JOB_COMPLETED', None), + ('BLOCK_JOB_FAILED', None)), timeout=True) + if e['event'] not in ('BLOCK_JOB_READY', 'BLOCK_JOB_COMPLETED'): + vm.shutdown() + return {'error': 'block-job failed: ' + str(e), + 'vm-log': vm.get_log()} + end_ms = e['timestamp']['seconds'] * 1000000 + \ + e['timestamp']['microseconds'] + finally: + vm.shutdown() + + return {'seconds': (end_ms - start_ms) / 1000000.0} + + +# Bench backup or mirror +def bench_block_copy(qemu_binary, cmd, source, target): + """Helper to run bench_block_job() for mirror or backup""" + assert cmd in ('blockdev-backup', 'blockdev-mirror') + + source['node-name'] = 'source' + target['node-name'] = 'target' + + return bench_block_job(cmd, + {'job-id': 'job0', 'device': 'source', + 'target': 'target', 'sync': 'full'}, + [qemu_binary, + '-blockdev', json.dumps(source), + '-blockdev', json.dumps(target)]) + + +def drv_file(filename): + return {'driver': 'file', 'filename': filename, + 'cache': {'direct': True}, 'aio': 'native'} + + +def drv_nbd(host, port): + return {'driver': 'nbd', + 'server': {'type': 'inet', 'host': host, 'port': port}} + + +if __name__ == '__main__': + import sys + + if len(sys.argv) < 4: + print('USAGE: {} ' + ' ' + ''.format(sys.argv[0])) + exit(1) + + res = bench_block_job(sys.argv[1], json.loads(sys.argv[2]), sys.argv[3:]) + if 'seconds' in res: + print('{:.2f}'.format(res['seconds'])) + else: + print(res) diff --git a/scripts/simplebench/simplebench.py b/scripts/simplebench/simplebench.py new file mode 100644 index 0000000000..59e7314ff6 --- /dev/null +++ b/scripts/simplebench/simplebench.py @@ -0,0 +1,128 @@ +#!/usr/bin/env python +# +# Simple benchmarking framework +# +# 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 . +# + + +def bench_one(test_func, test_env, test_case, count=5, initial_run=True): + """Benchmark one test-case + + test_func -- benchmarking function with prototype + test_func(env, case), which takes test_env and test_case + arguments and returns {'seconds': int} (which is benchmark + result) on success and {'error': str} on error. Returned + dict may contain any other additional fields. + test_env -- test environment - opaque first argument for test_func + test_case -- test case - opaque second argument for test_func + count -- how many times to call test_func, to calculate average + initial_run -- do initial run of test_func, which don't get into result + + Returns dict with the following fields: + 'runs': list of test_func results + 'average': average seconds per run (exists only if at least one run + succeeded) + 'delta': maximum delta between test_func result and the average + (exists only if at least one run succeeded) + 'n-failed': number of failed runs (exists only if at least one run + failed) + """ + if initial_run: + print(' #initial run:') + print(' ', test_func(test_env, test_case)) + + runs = [] + for i in range(count): + print(' #run {}'.format(i+1)) + res = test_func(test_env, test_case) + print(' ', res) + runs.append(res) + + result = {'runs': runs} + + successed = [r for r in runs if ('seconds' in r)] + if successed: + avg = sum(r['seconds'] for r in successed) / len(successed) + result['average'] = avg + result['delta'] = max(abs(r['seconds'] - avg) for r in successed) + + if len(successed) < count: + result['n-failed'] = count - len(successed) + + return result + + +def ascii_one(result): + """Return ASCII representation of bench_one() returned dict.""" + if 'average' in result: + s = '{:.2f} +- {:.2f}'.format(result['average'], result['delta']) + if 'n-failed' in result: + s += '\n({} failed)'.format(result['n-failed']) + return s + else: + return 'FAILED' + + +def bench(test_func, test_envs, test_cases, *args, **vargs): + """Fill benchmark table + + test_func -- benchmarking function, see bench_one for description + test_envs -- list of test environments, see bench_one + test_cases -- list of test cases, see bench_one + args, vargs -- additional arguments for bench_one + + Returns dict with the following fields: + 'envs': test_envs + 'cases': test_cases + 'tab': filled 2D array, where cell [i][j] is bench_one result for + test_cases[i] for test_envs[j] (i.e., rows are test cases and + columns are test environments) + """ + tab = {} + results = { + 'envs': test_envs, + 'cases': test_cases, + 'tab': tab + } + n = 1 + n_tests = len(test_envs) * len(test_cases) + for env in test_envs: + for case in test_cases: + print('Testing {}/{}: {} :: {}'.format(n, n_tests, + env['id'], case['id'])) + if case['id'] not in tab: + tab[case['id']] = {} + tab[case['id']][env['id']] = bench_one(test_func, env, case, + *args, **vargs) + n += 1 + + print('Done') + return results + + +def ascii(results): + """Return ASCII representation of bench() returned dict.""" + from tabulate import tabulate + + tab = [[""] + [c['id'] for c in results['envs']]] + for case in results['cases']: + row = [case['id']] + for env in results['envs']: + row.append(ascii_one(results['tab'][case['id']][env['id']])) + tab.append(row) + + return tabulate(tab) 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/texi2pod.pl b/scripts/texi2pod.pl index 839b7917cf..8bfc6f6f4c 100755 --- a/scripts/texi2pod.pl +++ b/scripts/texi2pod.pl @@ -143,6 +143,24 @@ while(<$inf>) { next; }; + # Single line command handlers. + + /^\@include\s+(.+)$/ and do { + push @instack, $inf; + $inf = gensym(); + $file = postprocess($1); + + # Try cwd and $ibase, then explicit -I paths. + $done = 0; + foreach $path ("", $ibase, @ipath) { + $mypath = $file; + $mypath = $path . "/" . $mypath if ($path ne ""); + open($inf, "<" . $mypath) and ($done = 1, last); + } + die "cannot find $file" if !$done; + next; + }; + next unless $output; # Discard comments. (Can't do it above, because then we'd never see @@ -242,24 +260,6 @@ while(<$inf>) { s/>/>/g; } - # Single line command handlers. - - /^\@include\s+(.+)$/ and do { - push @instack, $inf; - $inf = gensym(); - $file = postprocess($1); - - # Try cwd and $ibase, then explicit -I paths. - $done = 0; - foreach $path ("", $ibase, @ipath) { - $mypath = $file; - $mypath = $path . "/" . $mypath if ($path ne ""); - open($inf, "<" . $mypath) and ($done = 1, last); - } - die "cannot find $file" if !$done; - next; - }; - /^\@(?:section|unnumbered|unnumberedsec|center)\s+(.+)$/ and $_ = "\n=head2 $1\n"; /^\@subsection\s+(.+)$/ 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/update-linux-headers.sh b/scripts/update-linux-headers.sh index f76d77363b..29c27f4681 100755 --- a/scripts/update-linux-headers.sh +++ b/scripts/update-linux-headers.sh @@ -186,6 +186,7 @@ rm -rf "$output/include/standard-headers/linux" mkdir -p "$output/include/standard-headers/linux" for i in "$tmpdir"/include/linux/*virtio*.h \ "$tmpdir/include/linux/qemu_fw_cfg.h" \ + "$tmpdir/include/linux/fuse.h" \ "$tmpdir/include/linux/input.h" \ "$tmpdir/include/linux/input-event-codes.h" \ "$tmpdir/include/linux/pci_regs.h" \ diff --git a/scripts/update-mips-syscall-args.sh b/scripts/update-mips-syscall-args.sh new file mode 100755 index 0000000000..4f0dda4b83 --- /dev/null +++ b/scripts/update-mips-syscall-args.sh @@ -0,0 +1,57 @@ +#!/bin/sh + +URL=https://raw.githubusercontent.com/strace/strace/master +FILES="sysent.h sysent_shorthand_defs.h linux/mips/syscallent-compat.h \ + linux/mips/syscallent-o32.h linux/syscallent-common-32.h \ + linux/syscallent-common.h" + +output="$1" +if [ "$output" = "" ] ; then + output="$PWD" +fi + +INC=linux-user/mips/syscall-args-o32.c.inc + +TMP=$(mktemp -d) +cd $TMP + +for file in $FILES; do + curl -O $URL/$file +done + +> subcall32.h + +cat > gen_mips_o32.c < + +#define LINUX_MIPSO32 +#define MAX_ARGS 7 + +#include "sysent.h" +#include "sysent_shorthand_defs.h" + +#define SEN(syscall_name) 0,0 +const struct_sysent sysent0[] = { +#include "syscallent-o32.h" +}; + +int main(void) +{ + int i; + + for (i = 4000; i < sizeof(sysent0) / sizeof(struct_sysent); i++) { + if (sysent0[i].sys_name == NULL) { + printf(" [% 4d] = MIPS_SYSCALL_NUMBER_UNUSED,\n", i - 4000); + } else { + printf(" [% 4d] = %d, /* %s */\n", i - 4000, + sysent0[i].nargs, sysent0[i].sys_name); + } + } + + return 0; +} +EOF + +cc -o gen_mips_o32 gen_mips_o32.c && ./gen_mips_o32 > "$output/$INC" + +rm -fr "$TMP" diff --git a/scripts/update-syscalltbl.sh b/scripts/update-syscalltbl.sh new file mode 100755 index 0000000000..2d23e56800 --- /dev/null +++ b/scripts/update-syscalltbl.sh @@ -0,0 +1,49 @@ +TBL_LIST="\ +arch/alpha/kernel/syscalls/syscall.tbl,linux-user/alpha/syscall.tbl \ +arch/arm/tools/syscall.tbl,linux-user/arm/syscall.tbl \ +arch/m68k/kernel/syscalls/syscall.tbl,linux-user/m68k/syscall.tbl \ +arch/microblaze/kernel/syscalls/syscall.tbl,linux-user/microblaze/syscall.tbl \ +arch/mips/kernel/syscalls/syscall_n32.tbl,linux-user/mips64/syscall_n32.tbl \ +arch/mips/kernel/syscalls/syscall_n64.tbl,linux-user/mips64/syscall_n64.tbl \ +arch/mips/kernel/syscalls/syscall_o32.tbl,linux-user/mips/syscall_o32.tbl \ +arch/parisc/kernel/syscalls/syscall.tbl,linux-user/hppa/syscall.tbl \ +arch/powerpc/kernel/syscalls/syscall.tbl,linux-user/ppc/syscall.tbl \ +arch/s390/kernel/syscalls/syscall.tbl,linux-user/s390x/syscall.tbl \ +arch/sh/kernel/syscalls/syscall.tbl,linux-user/sh4/syscall.tbl \ +arch/sparc/kernel/syscalls/syscall.tbl,linux-user/sparc64/syscall.tbl \ +arch/sparc/kernel/syscalls/syscall.tbl,linux-user/sparc/syscall.tbl \ +arch/x86/entry/syscalls/syscall_32.tbl,linux-user/i386/syscall_32.tbl \ +arch/x86/entry/syscalls/syscall_64.tbl,linux-user/x86_64/syscall_64.tbl \ +arch/xtensa/kernel/syscalls/syscall.tbl,linux-user/xtensa/syscall.tbl\ +" + +linux="$1" +output="$2" + +if [ -z "$linux" ] || ! [ -d "$linux" ]; then + cat << EOF +usage: update-syscalltbl.sh LINUX_PATH [OUTPUT_PATH] + +LINUX_PATH Linux kernel directory to obtain the syscall.tbl from +OUTPUT_PATH output directory, usually the qemu source tree (default: $PWD) +EOF + exit 1 +fi + +if [ -z "$output" ]; then + output="$PWD" +fi + +for entry in $TBL_LIST; do + OFS="$IFS" + IFS=, + set $entry + src=$1 + dst=$2 + IFS="$OFS" + if ! cp "$linux/$src" "$output/$dst" ; then + echo "Cannot copy $linux/$src to $output/$dst" 1>&2 + exit 1 + fi +done + 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 . -from __future__ import print_function import argparse import json import sys diff --git a/scsi/qemu-pr-helper.c b/scsi/qemu-pr-helper.c index debb18f4aa..181ed4a186 100644 --- a/scsi/qemu-pr-helper.c +++ b/scsi/qemu-pr-helper.c @@ -24,7 +24,7 @@ #include #include -#ifdef CONFIG_LIBCAP +#ifdef CONFIG_LIBCAP_NG #include #endif #include @@ -70,7 +70,7 @@ static int num_active_sockets = 1; static int noisy; static int verbose; -#ifdef CONFIG_LIBCAP +#ifdef CONFIG_LIBCAP_NG static int uid = -1; static int gid = -1; #endif @@ -97,7 +97,7 @@ static void usage(const char *name) " (default '%s')\n" " -T, --trace [[enable=]][,events=][,file=]\n" " specify tracing options\n" -#ifdef CONFIG_LIBCAP +#ifdef CONFIG_LIBCAP_NG " -u, --user=USER user to drop privileges to\n" " -g, --group=GROUP group to drop privileges to\n" #endif @@ -421,10 +421,13 @@ static int multipath_pr_out(int fd, const uint8_t *cdb, uint8_t *sense, int rq_servact = cdb[1]; int rq_scope = cdb[2] >> 4; int rq_type = cdb[2] & 0xf; - struct prout_param_descriptor paramp; + g_autofree struct prout_param_descriptor *paramp = NULL; char transportids[PR_HELPER_DATA_SIZE]; int r; + paramp = g_malloc0(sizeof(struct prout_param_descriptor) + + sizeof(struct transportid *) * MPATH_MX_TIDS); + if (sz < PR_OUT_FIXED_PARAM_SIZE) { /* Illegal request, Parameter list length error. This isn't fatal; * we have read the data, send an error without closing the socket. @@ -454,10 +457,9 @@ static int multipath_pr_out(int fd, const uint8_t *cdb, uint8_t *sense, * used by libmpathpersist (which, of course, will immediately * do the opposite). */ - memset(¶mp, 0, sizeof(paramp)); - memcpy(¶mp.key, ¶m[0], 8); - memcpy(¶mp.sa_key, ¶m[8], 8); - paramp.sa_flags = param[20]; + memcpy(¶mp->key, ¶m[0], 8); + memcpy(¶mp->sa_key, ¶m[8], 8); + paramp->sa_flags = param[20]; if (sz > PR_OUT_FIXED_PARAM_SIZE) { size_t transportid_len; int i, j; @@ -520,12 +522,13 @@ static int multipath_pr_out(int fd, const uint8_t *cdb, uint8_t *sense, return CHECK_CONDITION; } - paramp.trnptid_list[paramp.num_transportid++] = id; + assert(paramp->num_transportid < MPATH_MX_TIDS); + paramp->trnptid_list[paramp->num_transportid++] = id; } } r = mpath_persistent_reserve_out(fd, rq_servact, rq_scope, rq_type, - ¶mp, noisy, verbose); + paramp, noisy, verbose); return mpath_reconstruct_sense(fd, r, sense); } #endif @@ -827,7 +830,7 @@ static void close_server_socket(void) num_active_sockets--; } -#ifdef CONFIG_LIBCAP +#ifdef CONFIG_LIBCAP_NG static int drop_privileges(void) { /* clear all capabilities */ @@ -920,7 +923,7 @@ int main(int argc, char **argv) pidfile = g_strdup(optarg); pidfile_specified = true; break; -#ifdef CONFIG_LIBCAP +#ifdef CONFIG_LIBCAP_NG case 'u': { unsigned long res; struct passwd *userinfo = getpwnam(optarg); @@ -1056,7 +1059,7 @@ int main(int argc, char **argv) exit(EXIT_FAILURE); } -#ifdef CONFIG_LIBCAP +#ifdef CONFIG_LIBCAP_NG if (drop_privileges() < 0) { error_report("Failed to drop privileges: %s", strerror(errno)); exit(EXIT_FAILURE); diff --git a/slirp b/slirp index 126c04acba..2faae0f778 160000 --- a/slirp +++ b/slirp @@ -1 +1 @@ -Subproject commit 126c04acbabd7ad32c2b018fe10dfac2a3bc1210 +Subproject commit 2faae0f778f818fadc873308f983289df697eb93 diff --git a/softmmu/Makefile.objs b/softmmu/Makefile.objs new file mode 100644 index 0000000000..dd15c24346 --- /dev/null +++ b/softmmu/Makefile.objs @@ -0,0 +1,3 @@ +softmmu-main-y = softmmu/main.o +obj-y += vl.o +vl.o-cflags := $(GPROF_CFLAGS) $(SDL_CFLAGS) diff --git a/softmmu/main.c b/softmmu/main.c new file mode 100644 index 0000000000..7adc530c73 --- /dev/null +++ b/softmmu/main.c @@ -0,0 +1,53 @@ +/* + * QEMU System Emulator + * + * Copyright (c) 2003-2020 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "sysemu/sysemu.h" + +#ifdef CONFIG_SDL +#if defined(__APPLE__) || defined(main) +#include +int main(int argc, char **argv) +{ + return qemu_main(argc, argv, NULL); +} +#undef main +#define main qemu_main +#endif +#endif /* CONFIG_SDL */ + +#ifdef CONFIG_COCOA +#undef main +#define main qemu_main +#endif /* CONFIG_COCOA */ + +int main(int argc, char **argv, char **envp) +{ + qemu_init(argc, argv, envp); + qemu_main_loop(); + qemu_cleanup(); + + return 0; +} diff --git a/vl.c b/softmmu/vl.c similarity index 91% rename from vl.c rename to softmmu/vl.c index 6a65a64bfd..32c0047889 100644 --- a/vl.c +++ b/softmmu/vl.c @@ -25,6 +25,7 @@ #include "qemu/osdep.h" #include "qemu-common.h" #include "qemu/units.h" +#include "hw/boards.h" #include "hw/qdev-properties.h" #include "qapi/error.h" #include "qemu-version.h" @@ -36,25 +37,6 @@ #include "sysemu/seccomp.h" #include "sysemu/tcg.h" -#ifdef CONFIG_SDL -#if defined(__APPLE__) || defined(main) -#include -int qemu_main(int argc, char **argv, char **envp); -int main(int argc, char **argv) -{ - return qemu_main(argc, argv, NULL); -} -#undef main -#define main qemu_main -#endif -#endif /* CONFIG_SDL */ - -#ifdef CONFIG_COCOA -#undef main -#define main qemu_main -#endif /* CONFIG_COCOA */ - - #include "qemu/error-report.h" #include "qemu/sockets.h" #include "sysemu/accel.h" @@ -62,14 +44,12 @@ int main(int argc, char **argv) #include "hw/isa/isa.h" #include "hw/scsi/scsi.h" #include "hw/display/vga.h" -#include "hw/bt.h" #include "sysemu/watchdog.h" #include "hw/firmware/smbios.h" #include "hw/acpi/acpi.h" #include "hw/xen/xen.h" #include "hw/loader.h" #include "monitor/qdev.h" -#include "sysemu/bt.h" #include "net/net.h" #include "net/slirp.h" #include "monitor/monitor.h" @@ -77,6 +57,7 @@ int main(int argc, char **argv) #include "ui/input.h" #include "sysemu/sysemu.h" #include "sysemu/numa.h" +#include "sysemu/hostmem.h" #include "exec/gdbstub.h" #include "qemu/timer.h" #include "chardev/char.h" @@ -141,8 +122,6 @@ enum vga_retrace_method vga_retrace_method = VGA_RETRACE_DUMB; int display_opengl; const char* keyboard_layout = NULL; ram_addr_t ram_size; -const char *mem_path = NULL; -int mem_prealloc = 0; /* force preallocation of physical target memory */ bool enable_mlock = false; bool enable_cpu_pm = false; int nb_nics; @@ -165,12 +144,10 @@ static Chardev **serial_hds; Chardev *parallel_hds[MAX_PARALLEL_PORTS]; int win2k_install_hack = 0; int singlestep = 0; -int acpi_enabled = 1; 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]; @@ -294,19 +271,13 @@ static QemuOptsList qemu_accel_opts = { .name = "accel", .implied_opt_name = "accel", .head = QTAILQ_HEAD_INITIALIZER(qemu_accel_opts.head), - .merge_lists = true, .desc = { - { - .name = "accel", - .type = QEMU_OPT_STRING, - .help = "Select the type of accelerator", - }, - { - .name = "thread", - .type = QEMU_OPT_STRING, - .help = "Enable/disable multi-threaded TCG", - }, - { /* end of list */ } + /* + * no elements => accept any + * sanity checking will happen later + * when setting accelerator properties + */ + { } }, }; @@ -898,13 +869,9 @@ static void configure_rtc(QemuOpts *opts) value = qemu_opt_get(opts, "driftfix"); if (value) { if (!strcmp(value, "slew")) { - static GlobalProperty slew_lost_ticks = { - .driver = "mc146818rtc", - .property = "lost_tick_policy", - .value = "slew", - }; - - qdev_prop_register_global(&slew_lost_ticks); + object_register_sugar_prop("mc146818rtc", + "lost_tick_policy", + "slew"); } else if (!strcmp(value, "none")) { /* discard is default */ } else { @@ -914,128 +881,6 @@ static void configure_rtc(QemuOpts *opts) } } -/***********************************************************/ -/* Bluetooth support */ -static int nb_hcis; -static int cur_hci; -static struct HCIInfo *hci_table[MAX_NICS]; - -struct HCIInfo *qemu_next_hci(void) -{ - if (cur_hci == nb_hcis) - return &null_hci; - - return hci_table[cur_hci++]; -} - -static int bt_hci_parse(const char *str) -{ - struct HCIInfo *hci; - bdaddr_t bdaddr; - - if (nb_hcis >= MAX_NICS) { - error_report("too many bluetooth HCIs (max %i)", MAX_NICS); - return -1; - } - - hci = hci_init(str); - if (!hci) - return -1; - - bdaddr.b[0] = 0x52; - bdaddr.b[1] = 0x54; - bdaddr.b[2] = 0x00; - bdaddr.b[3] = 0x12; - bdaddr.b[4] = 0x34; - bdaddr.b[5] = 0x56 + nb_hcis; - hci->bdaddr_set(hci, bdaddr.b); - - hci_table[nb_hcis++] = hci; - - return 0; -} - -static void bt_vhci_add(int vlan_id) -{ - struct bt_scatternet_s *vlan = qemu_find_bt_vlan(vlan_id); - - if (!vlan->slave) - warn_report("adding a VHCI to an empty scatternet %i", - vlan_id); - - bt_vhci_init(bt_new_hci(vlan)); -} - -static struct bt_device_s *bt_device_add(const char *opt) -{ - struct bt_scatternet_s *vlan; - int vlan_id = 0; - char *endp = strstr(opt, ",vlan="); - int len = (endp ? endp - opt : strlen(opt)) + 1; - char devname[10]; - - pstrcpy(devname, MIN(sizeof(devname), len), opt); - - if (endp) { - vlan_id = strtol(endp + 6, &endp, 0); - if (*endp) { - error_report("unrecognised bluetooth vlan Id"); - return 0; - } - } - - vlan = qemu_find_bt_vlan(vlan_id); - - if (!vlan->slave) - warn_report("adding a slave device to an empty scatternet %i", - vlan_id); - - if (!strcmp(devname, "keyboard")) - return bt_keyboard_init(vlan); - - error_report("unsupported bluetooth device '%s'", devname); - return 0; -} - -static int bt_parse(const char *opt) -{ - const char *endp, *p; - int vlan; - - if (strstart(opt, "hci", &endp)) { - if (!*endp || *endp == ',') { - if (*endp) - if (!strstart(endp, ",vlan=", 0)) - opt = endp + 1; - - return bt_hci_parse(opt); - } - } else if (strstart(opt, "vhci", &endp)) { - if (!*endp || *endp == ',') { - if (*endp) { - if (strstart(endp, ",vlan=", &p)) { - vlan = strtol(p, (char **) &endp, 0); - if (*endp) { - error_report("bad scatternet '%s'", p); - return 1; - } - } else { - error_report("bad parameter '%s'", endp + 1); - return 1; - } - } else - vlan = 0; - - bt_vhci_add(vlan); - return 0; - } - } else if (strstart(opt, "device:", &endp)) - return !bt_device_add(endp); - - error_report("bad bluetooth parameter '%s'", opt); - return 1; -} - static int parse_name(void *opaque, QemuOpts *opts, Error **errp) { const char *proc_name; @@ -1263,17 +1108,10 @@ static void realtime_init(void) static void configure_msg(QemuOpts *opts) { - enable_timestamp_msg = qemu_opt_get_bool(opts, "timestamp", true); + error_with_timestamp = qemu_opt_get_bool(opts, "timestamp", false); } -/* Now we still need this for compatibility with XEN. */ -bool has_igd_gfx_passthru; -static void igd_gfx_passthru(void) -{ - has_igd_gfx_passthru = current_machine->igd_gfx_passthru; -} - /***********************************************************/ /* USB devices */ @@ -1325,16 +1163,18 @@ static MachineClass *find_machine(const char *name, GSList *machines) static MachineClass *find_default_machine(GSList *machines) { GSList *el; + MachineClass *default_machineclass = NULL; for (el = machines; el; el = el->next) { MachineClass *mc = el->data; if (mc->is_default) { - return mc; + assert(default_machineclass == NULL && "Multiple default machines"); + default_machineclass = mc; } } - return NULL; + return default_machineclass; } static int machine_help_func(QemuOpts *opts, MachineState *machine) @@ -1609,6 +1449,18 @@ void qemu_system_guest_panicked(GuestPanicInformation *info) } } +void qemu_system_guest_crashloaded(GuestPanicInformation *info) +{ + qemu_log_mask(LOG_GUEST_ERROR, "Guest crash loaded"); + + qapi_event_send_guest_crashloaded(GUEST_PANIC_ACTION_RUN, + !!info, info); + + if (info) { + qapi_free_GuestPanicInformation(info); + } +} + void qemu_system_reset_request(ShutdownCause reason) { if (no_reboot && reason != SHUTDOWN_CAUSE_SUBSYSTEM_RESET) { @@ -1745,9 +1597,6 @@ static bool main_loop_should_exit(void) RunState r; ShutdownCause request; - if (runstate_check(RUN_STATE_FINISH_MIGRATE)) { - return false; - } if (preconfig_exit_requested) { if (runstate_check(RUN_STATE_PRECONFIG)) { runstate_set(RUN_STATE_PRELAUNCH); @@ -1776,8 +1625,13 @@ static bool main_loop_should_exit(void) pause_all_vcpus(); qemu_system_reset(request); resume_all_vcpus(); + /* + * runstate can change in pause_all_vcpus() + * as iothread mutex is unlocked + */ if (!runstate_check(RUN_STATE_RUNNING) && - !runstate_check(RUN_STATE_INMIGRATE)) { + !runstate_check(RUN_STATE_INMIGRATE) && + !runstate_check(RUN_STATE_FINISH_MIGRATE)) { runstate_set(RUN_STATE_PRELAUNCH); } } @@ -1798,7 +1652,7 @@ static bool main_loop_should_exit(void) return false; } -static void main_loop(void) +void qemu_main_loop(void) { #ifdef CONFIG_PROFILER int64_t ti; @@ -2010,6 +1864,11 @@ static void parse_display(const char *p) { const char *opts; + if (is_help_option(p)) { + qemu_display_help(); + exit(0); + } + if (strstart(p, "sdl", &opts)) { /* * sdl DisplayType needs hand-crafted parser instead of @@ -2053,6 +1912,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; @@ -2240,50 +2109,7 @@ static int fsdev_init_func(void *opaque, QemuOpts *opts, Error **errp) static int mon_init_func(void *opaque, QemuOpts *opts, Error **errp) { - Chardev *chr; - bool qmp; - bool pretty = false; - const char *chardev; - const char *mode; - - mode = qemu_opt_get(opts, "mode"); - if (mode == NULL) { - mode = "readline"; - } - if (strcmp(mode, "readline") == 0) { - qmp = false; - } else if (strcmp(mode, "control") == 0) { - qmp = true; - } else { - error_setg(errp, "unknown monitor mode \"%s\"", mode); - return -1; - } - - if (!qmp && qemu_opt_get(opts, "pretty")) { - warn_report("'pretty' is deprecated for HMP monitors, it has no effect " - "and will be removed in future versions"); - } - if (qemu_opt_get_bool(opts, "pretty", 0)) { - pretty = true; - } - - chardev = qemu_opt_get(opts, "chardev"); - if (!chardev) { - error_report("chardev is required"); - exit(1); - } - chr = qemu_chr_find(chardev); - if (chr == NULL) { - error_setg(errp, "chardev \"%s\" not found", chardev); - return -1; - } - - if (qmp) { - monitor_init_qmp(chr, pretty); - } else { - monitor_init_hmp(chr, true); - } - return 0; + return monitor_init_opts(opts, errp); } static void monitor_parse(const char *optarg, const char *mode, bool pretty) @@ -2319,7 +2145,6 @@ static void monitor_parse(const char *optarg, const char *mode, bool pretty) struct device_config { enum { DEV_USB, /* -usbdevice */ - DEV_BT, /* -bt */ DEV_SERIAL, /* -serial */ DEV_PARALLEL, /* -parallel */ DEV_DEBUGCON, /* -debugcon */ @@ -2615,27 +2440,17 @@ static MachineClass *select_machine(void) return machine_class; } -static int machine_set_property(void *opaque, - const char *name, const char *value, - Error **errp) +static int object_parse_property_opt(Object *obj, + const char *name, const char *value, + const char *skip, Error **errp) { - Object *obj = OBJECT(opaque); Error *local_err = NULL; - char *p, *qom_name; - if (strcmp(name, "type") == 0) { + if (g_str_equal(name, skip)) { return 0; } - qom_name = g_strdup(name); - for (p = qom_name; *p; p++) { - if (*p == '_') { - *p = '-'; - } - } - - object_property_parse(obj, value, qom_name, &local_err); - g_free(qom_name); + object_property_parse(obj, value, name, &local_err); if (local_err) { error_propagate(errp, local_err); @@ -2645,6 +2460,35 @@ static int machine_set_property(void *opaque, return 0; } +static int machine_set_property(void *opaque, + const char *name, const char *value, + Error **errp) +{ + g_autofree char *qom_name = g_strdup(name); + char *p; + + for (p = qom_name; *p; p++) { + if (*p == '_') { + *p = '-'; + } + } + + /* Legacy options do not correspond to MachineState properties. */ + if (g_str_equal(qom_name, "accel")) { + return 0; + } + if (g_str_equal(qom_name, "igd-passthru")) { + object_register_sugar_prop(ACCEL_CLASS_NAME("xen"), qom_name, value); + return 0; + } + if (g_str_equal(qom_name, "kvm-shadow-mem") || + g_str_equal(qom_name, "kernel-irqchip")) { + object_register_sugar_prop(ACCEL_CLASS_NAME("kvm"), qom_name, value); + return 0; + } + + return object_parse_property_opt(opaque, name, value, "type", errp); +} /* * Initial object creation happens before all other @@ -2716,7 +2560,7 @@ static bool object_create_delayed(const char *type, QemuOpts *opts) } -static void set_memory_options(uint64_t *ram_slots, ram_addr_t *maxram_size, +static bool set_memory_options(uint64_t *ram_slots, ram_addr_t *maxram_size, MachineClass *mc) { uint64_t sz; @@ -2756,6 +2600,9 @@ static void set_memory_options(uint64_t *ram_slots, ram_addr_t *maxram_size, } sz = QEMU_ALIGN_UP(sz, 8192); + if (mc->fixup_ram_size) { + sz = mc->fixup_ram_size(sz); + } ram_size = sz; if (ram_size != sz) { error_report("ram size too large"); @@ -2793,6 +2640,7 @@ static void set_memory_options(uint64_t *ram_slots, ram_addr_t *maxram_size, } loc_pop(&loc); + return !!mem_str; } static int global_init_func(void *opaque, QemuOpts *opts, Error **errp) @@ -2825,7 +2673,146 @@ static void user_register_global_props(void) global_init_func, NULL, NULL); } -int main(int argc, char **argv, char **envp) +static int do_configure_icount(void *opaque, QemuOpts *opts, Error **errp) +{ + configure_icount(opts, errp); + return 0; +} + +static int accelerator_set_property(void *opaque, + const char *name, const char *value, + Error **errp) +{ + return object_parse_property_opt(opaque, name, value, "accel", errp); +} + +static int do_configure_accelerator(void *opaque, QemuOpts *opts, Error **errp) +{ + bool *p_init_failed = opaque; + const char *acc = qemu_opt_get(opts, "accel"); + AccelClass *ac = accel_find(acc); + AccelState *accel; + int ret; + + if (!ac) { + *p_init_failed = true; + error_report("invalid accelerator %s", acc); + return 0; + } + accel = ACCEL(object_new_with_class(OBJECT_CLASS(ac))); + object_apply_compat_props(OBJECT(accel)); + qemu_opt_foreach(opts, accelerator_set_property, + accel, + &error_fatal); + + ret = accel_init_machine(accel, current_machine); + if (ret < 0) { + *p_init_failed = true; + error_report("failed to initialize %s: %s", + acc, strerror(-ret)); + return 0; + } + + return 1; +} + +static void configure_accelerators(const char *progname) +{ + const char *accel; + bool init_failed = false; + + qemu_opts_foreach(qemu_find_opts("icount"), + do_configure_icount, NULL, &error_fatal); + + accel = qemu_opt_get(qemu_get_machine_opts(), "accel"); + if (QTAILQ_EMPTY(&qemu_accel_opts.head)) { + char **accel_list, **tmp; + + if (accel == NULL) { + /* Select the default accelerator */ + bool have_tcg = accel_find("tcg"); + bool have_kvm = accel_find("kvm"); + + if (have_tcg && have_kvm) { + if (g_str_has_suffix(progname, "kvm")) { + /* If the program name ends with "kvm", we prefer KVM */ + accel = "kvm:tcg"; + } else { + accel = "tcg:kvm"; + } + } else if (have_kvm) { + accel = "kvm"; + } else if (have_tcg) { + accel = "tcg"; + } else { + error_report("No accelerator selected and" + " no default accelerator available"); + exit(1); + } + } + accel_list = g_strsplit(accel, ":", 0); + + for (tmp = accel_list; *tmp; tmp++) { + /* + * Filter invalid accelerators here, to prevent obscenities + * such as "-machine accel=tcg,,thread=single". + */ + if (accel_find(*tmp)) { + qemu_opts_parse_noisily(qemu_find_opts("accel"), *tmp, true); + } else { + init_failed = true; + error_report("invalid accelerator %s", *tmp); + } + } + g_strfreev(accel_list); + } else { + if (accel != NULL) { + error_report("The -accel and \"-machine accel=\" options are incompatible"); + exit(1); + } + } + + if (!qemu_opts_foreach(qemu_find_opts("accel"), + do_configure_accelerator, &init_failed, &error_fatal)) { + if (!init_failed) { + error_report("no accelerator found"); + } + exit(1); + } + + if (init_failed) { + AccelClass *ac = ACCEL_GET_CLASS(current_accel()); + error_report("falling back to %s", ac->name); + } + + if (use_icount && !(tcg_enabled() || qtest_enabled())) { + error_report("-icount is not allowed with hardware virtualization"); + exit(1); + } +} + +static void create_default_memdev(MachineState *ms, const char *path) +{ + Object *obj; + MachineClass *mc = MACHINE_GET_CLASS(ms); + + obj = object_new(path ? TYPE_MEMORY_BACKEND_FILE : TYPE_MEMORY_BACKEND_RAM); + if (path) { + object_property_set_str(obj, path, "mem-path", &error_fatal); + } + object_property_set_int(obj, ms->ram_size, "size", &error_fatal); + object_property_add_child(object_get_objects_root(), mc->default_ram_id, + obj, &error_fatal); + /* Ensure backend's memory region name is equal to mc->default_ram_id */ + object_property_set_bool(obj, false, "x-use-canonical-path-for-ramblock-id", + &error_fatal); + user_creatable_complete(USER_CREATABLE(obj), &error_fatal); + object_unref(obj); + object_property_set_str(OBJECT(ms), mc->default_ram_id, "memory-backend", + &error_fatal); +} + +void qemu_init(int argc, char **argv, char **envp) { int i; int snapshot, linux_boot; @@ -2859,8 +2846,11 @@ int main(int argc, char **argv, char **envp) Error *err = NULL; bool list_data_dirs = false; char *dir, **dirs; + const char *mem_path = NULL; + bool have_custom_ram_size; BlockdevOptionsQueue bdo_queue = QSIMPLEQ_HEAD_INITIALIZER(bdo_queue); QemuPluginList plugin_list = QTAILQ_HEAD_INITIALIZER(plugin_list); + int mem_prealloc = 0; /* force preallocation of physical target memory */ os_set_line_buffering(); @@ -2876,6 +2866,7 @@ int main(int argc, char **argv, char **envp) qemu_init_exec_dir(argv[0]); module_call_init(MODULE_INIT_QOM); + module_call_init(MODULE_INIT_MIGRATION); qemu_add_opts(&qemu_drive_opts); qemu_add_drive_opts(&qemu_legacy_drive_opts); @@ -3128,13 +3119,6 @@ int main(int argc, char **argv, char **envp) } break; #endif - case QEMU_OPTION_bt: - warn_report("The bluetooth subsystem is deprecated and will " - "be removed soon. If the bluetooth subsystem is " - "still useful for you, please send a mail to " - "qemu-devel@nongnu.org with your usecase."); - add_device_config(DEV_BT, optarg); - break; case QEMU_OPTION_audio_help: audio_legacy_help(); exit (0); @@ -3374,29 +3358,6 @@ int main(int argc, char **argv, char **envp) qemu_opt_get(opts, "mount_tag"), &error_abort); break; } - case QEMU_OPTION_virtfs_synth: { - QemuOpts *fsdev; - QemuOpts *device; - - warn_report("'-virtfs_synth' is deprecated, please use " - "'-fsdev synth' and '-device virtio-9p-...' " - "instead"); - - fsdev = qemu_opts_create(qemu_find_opts("fsdev"), "v_synth", - 1, NULL); - if (!fsdev) { - error_report("duplicate option: %s", "virtfs_synth"); - exit(1); - } - qemu_opt_set(fsdev, "fsdriver", "synth", &error_abort); - - device = qemu_opts_create(qemu_find_opts("device"), NULL, 0, - &error_abort); - qemu_opt_set(device, "driver", "virtio-9p-pci", &error_abort); - qemu_opt_set(device, "fsdev", "v_synth", &error_abort); - qemu_opt_set(device, "mount_tag", "v_synth", &error_abort); - break; - } case QEMU_OPTION_serial: add_device_config(DEV_SERIAL, optarg); default_serial = 0; @@ -3407,7 +3368,7 @@ int main(int argc, char **argv, char **envp) case QEMU_OPTION_watchdog: if (watchdog) { error_report("only one watchdog option may be given"); - return 1; + exit(1); } watchdog = optarg; break; @@ -3517,7 +3478,7 @@ int main(int argc, char **argv, char **envp) gchar **optname = g_strsplit(typename, ACCEL_CLASS_SUFFIX, 0); printf("%s\n", optname[0]); - g_free(optname); + g_strfreev(optname); } g_free(typename); } @@ -3529,9 +3490,6 @@ int main(int argc, char **argv, char **envp) "use -M accel=... for now instead"); exit(1); } - opts = qemu_opts_create(qemu_find_opts("machine"), NULL, - false, &error_abort); - qemu_opt_set(opts, "accel", optarg, &error_abort); break; case QEMU_OPTION_usb: olist = qemu_find_opts("machine"); @@ -3560,7 +3518,8 @@ int main(int argc, char **argv, char **envp) vnc_parse(optarg, &error_fatal); break; case QEMU_OPTION_no_acpi: - acpi_enabled = 0; + olist = qemu_find_opts("machine"); + qemu_opts_parse_noisily(olist, "acpi=off", false); break; case QEMU_OPTION_no_hpet: no_hpet = 1; @@ -3572,7 +3531,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) { @@ -3638,10 +3600,8 @@ int main(int argc, char **argv, char **envp) error_report("TCG is disabled"); exit(1); #endif - if (qemu_strtoul(optarg, NULL, 0, &tcg_tb_size) < 0) { - error_report("Invalid argument to -tb-size"); - exit(1); - } + warn_report("The -tb-size option is deprecated, use -accel tcg,tb-size instead"); + object_register_sugar_prop(ACCEL_CLASS_NAME("tcg"), "tb-size", optarg); break; case QEMU_OPTION_icount: icount_opts = qemu_opts_parse_noisily(qemu_find_opts("icount"), @@ -3835,6 +3795,22 @@ int main(int argc, char **argv, char **envp) */ loc_set_none(); + /* + * Check for -cpu help and -device help before we call select_machine(), + * which will return an error if the architecture has no default machine + * type and the user did not specify one, so that the user doesn't need + * to say '-cpu help -machine something'. + */ + if (cpu_option && is_help_option(cpu_option)) { + list_cpus(cpu_option); + exit(0); + } + + if (qemu_opts_foreach(qemu_find_opts("device"), + device_help_func, NULL, NULL)) { + exit(0); + } + user_register_global_props(); replay_configure(icount_opts); @@ -3850,10 +3826,21 @@ int main(int argc, char **argv, char **envp) machine_class = select_machine(); object_set_machine_compat_props(machine_class->compat_props); - set_memory_options(&ram_slots, &maxram_size, machine_class); + have_custom_ram_size = set_memory_options(&ram_slots, &maxram_size, + machine_class); os_daemonize(); - rcu_disable_atfork(); + + /* + * If QTest is enabled, keep the rcu_atfork enabled, since system processes + * may be forked testing purposes (e.g. fork-server based fuzzing) The fork + * should happen before a signle cpu instruction is executed, to prevent + * deadlocks. See commit 73c6e40, rcu: "completely disable pthread_atfork + * callbacks as soon as possible" + */ + if (!qtest_enabled()) { + rcu_disable_atfork(); + } if (pid_file && !qemu_write_pidfile(pid_file, &err)) { error_reportf_err(err, "cannot create PID file: "); @@ -3886,8 +3873,7 @@ int main(int argc, char **argv, char **envp) cleanup_add_fd, NULL, &error_fatal); #endif - current_machine = MACHINE(object_new(object_class_get_name( - OBJECT_CLASS(machine_class)))); + current_machine = MACHINE(object_new_with_class(OBJECT_CLASS(machine_class))); if (machine_help_func(qemu_get_machine_opts(), current_machine)) { exit(0); } @@ -3913,11 +3899,6 @@ int main(int argc, char **argv, char **envp) qemu_set_hw_version(machine_class->hw_version); } - if (cpu_option && is_help_option(cpu_option)) { - list_cpus(cpu_option); - exit(0); - } - if (!trace_init_backends()) { exit(1); } @@ -3925,10 +3906,7 @@ int main(int argc, char **argv, char **envp) /* Open the logfile at this point and set the log mask if necessary. */ - if (log_file) { - qemu_set_log_filename(log_file, &error_fatal); - } - + qemu_set_log_filename(log_file, &error_fatal); if (log_mask) { int mask; mask = qemu_str_to_log_mask(log_mask); @@ -3974,6 +3952,7 @@ int main(int argc, char **argv, char **envp) current_machine->smp.max_cpus = machine_class->default_cpus; current_machine->smp.cores = 1; current_machine->smp.threads = 1; + current_machine->smp.sockets = 1; machine_class->smp_parse(current_machine, qemu_opts_find(qemu_find_opts("smp-opts"), NULL)); @@ -3994,6 +3973,15 @@ int main(int argc, char **argv, char **envp) exit(1); } + if (mem_prealloc) { + char *val; + + val = g_strdup_printf("%d", current_machine->smp.cpus); + object_register_sugar_prop("memory-backend", "prealloc-threads", val); + g_free(val); + object_register_sugar_prop("memory-backend", "prealloc", "on"); + } + /* * Get the default machine options from the machine if it is not already * specified either by the configuration file or by the command line. @@ -4142,11 +4130,6 @@ int main(int argc, char **argv, char **envp) fsdev_init_func, NULL, &error_fatal); #endif - if (qemu_opts_foreach(qemu_find_opts("device"), - device_help_func, NULL, NULL)) { - exit(0); - } - /* * Note: we need to create block backends before * machine_set_property(), so machine properties can refer to @@ -4165,7 +4148,7 @@ int main(int argc, char **argv, char **envp) * Note: uses machine properties such as kernel-irqchip, must run * after machine_set_property(). */ - configure_accelerator(current_machine, argv[0]); + configure_accelerators(argv[0]); /* * Beware, QOM objects created before this point miss global and @@ -4250,18 +4233,6 @@ int main(int argc, char **argv, char **envp) qemu_spice_init(); cpu_ticks_init(); - if (icount_opts) { - if (!tcg_enabled()) { - error_report("-icount is not allowed with hardware virtualization"); - exit(1); - } - configure_icount(icount_opts, &error_abort); - qemu_opts_del(icount_opts); - } - - if (tcg_enabled()) { - qemu_tcg_configure(accel_opts, &error_fatal); - } if (default_net) { QemuOptsList *net = qemu_find_opts("net"); @@ -4282,18 +4253,6 @@ int main(int argc, char **argv, char **envp) tpm_init(); - /* init the bluetooth world */ - if (foreach_device_config(DEV_BT, bt_parse)) - exit(1); - - if (!xen_enabled()) { - /* On 32-bit hosts, QEMU is limited by virtual address space */ - if (ram_size > (2047 << 20) && HOST_LONG_BITS == 32) { - error_report("at most 2047 MB RAM can be simulated"); - exit(1); - } - } - blk_mig_init(); ram_mig_init(); dirty_bitmap_mig_init(); @@ -4301,6 +4260,9 @@ int main(int argc, char **argv, char **envp) qemu_opts_foreach(qemu_find_opts("mon"), mon_init_func, NULL, &error_fatal); + /* connect semihosting console input if requested */ + qemu_semihosting_console_init(); + if (foreach_device_config(DEV_SERIAL, serial_parse) < 0) exit(1); if (foreach_device_config(DEV_PARALLEL, parallel_parse) < 0) @@ -4335,10 +4297,48 @@ int main(int argc, char **argv, char **envp) if (cpu_option) { current_machine->cpu_type = parse_cpu_option(cpu_option); } + + if (current_machine->ram_memdev_id) { + Object *backend; + ram_addr_t backend_size; + + backend = object_resolve_path_type(current_machine->ram_memdev_id, + TYPE_MEMORY_BACKEND, NULL); + if (!backend) { + error_report("Memory backend '%s' not found", + current_machine->ram_memdev_id); + exit(EXIT_FAILURE); + } + backend_size = object_property_get_uint(backend, "size", &error_abort); + if (have_custom_ram_size && backend_size != ram_size) { + error_report("Size specified by -m option must match size of " + "explicitly specified 'memory-backend' property"); + exit(EXIT_FAILURE); + } + if (mem_path) { + error_report("'-mem-path' can't be used together with" + "'-machine memory-backend'"); + exit(EXIT_FAILURE); + } + ram_size = backend_size; + } + + if (!xen_enabled()) { + /* On 32-bit hosts, QEMU is limited by virtual address space */ + if (ram_size > (2047 << 20) && HOST_LONG_BITS == 32) { + error_report("at most 2047 MB RAM can be simulated"); + exit(1); + } + } + parse_numa_opts(current_machine); + if (machine_class->default_ram_id && current_machine->ram_size && + numa_uses_legacy_mem() && !current_machine->ram_memdev_id) { + create_default_memdev(current_machine, mem_path); + } /* do monitor/qmp handling at preconfig state if requested */ - main_loop(); + qemu_main_loop(); audio_init_audiodevs(); @@ -4362,9 +4362,6 @@ int main(int argc, char **argv, char **envp) exit(1); } - /* Check if IGD GFX passthrough. */ - igd_gfx_passthru(); - /* init generic devices */ rom_set_order_override(FW_CFG_ORDER_OVERRIDE_DEVICE); qemu_opts_foreach(qemu_find_opts("device"), @@ -4419,7 +4416,15 @@ int main(int argc, char **argv, char **envp) /* TODO: once all bus devices are qdevified, this should be done * when bus is created by qdev.c */ - qemu_register_reset(qbus_reset_all_fn, sysbus_get_default()); + /* + * TODO: If we had a main 'reset container' that the whole system + * lived in, we could reset that using the multi-phase reset + * APIs. For the moment, we just reset the sysbus, which will cause + * all devices hanging off it (and all their child buses, recursively) + * to be reset. Note that this will *not* reset any Device objects + * which are not attached to some part of the qbus tree! + */ + qemu_register_reset(resettable_cold_reset_fn, sysbus_get_default()); qemu_run_machine_init_done_notifiers(); if (rom_check_and_register_reset() != 0) { @@ -4451,7 +4456,7 @@ int main(int argc, char **argv, char **envp) if (vmstate_dump_file) { /* dump and exit */ dump_vmstate_json_to_file(vmstate_dump_file); - return 0; + exit(0); } if (incoming) { @@ -4468,8 +4473,11 @@ int main(int argc, char **argv, char **envp) accel_setup_post(current_machine); os_setup_post(); - main_loop(); + return; +} +void qemu_cleanup(void) +{ gdbserver_cleanup(); /* @@ -4506,6 +4514,4 @@ int main(int argc, char **argv, char **envp) qemu_chr_cleanup(); user_creatable_cleanup(); /* TODO: unref root container, check all devices are ok */ - - return 0; } diff --git a/storage-daemon/Makefile.objs b/storage-daemon/Makefile.objs new file mode 100644 index 0000000000..cfe6beee52 --- /dev/null +++ b/storage-daemon/Makefile.objs @@ -0,0 +1 @@ +storage-daemon-obj-y += qapi/ diff --git a/storage-daemon/qapi/Makefile.objs b/storage-daemon/qapi/Makefile.objs new file mode 100644 index 0000000000..8a4b220c96 --- /dev/null +++ b/storage-daemon/qapi/Makefile.objs @@ -0,0 +1 @@ +storage-daemon-obj-y += qapi-commands.o qapi-init-commands.o qapi-introspect.o diff --git a/storage-daemon/qapi/qapi-schema.json b/storage-daemon/qapi/qapi-schema.json new file mode 100644 index 0000000000..14f4f8fe61 --- /dev/null +++ b/storage-daemon/qapi/qapi-schema.json @@ -0,0 +1,26 @@ +# -*- Mode: Python -*- + +# Note that modules are shared with the QEMU main schema under the assumption +# that the storage daemon schema is a subset of the main schema. For the shared +# modules, no code is generated here, but we reuse the code files generated +# from the main schema. +# +# If you wish to extend the storage daemon schema to contain things that are +# not in the main schema, be aware that array types of types defined in shared +# modules are only generated if an array of the respective type is already used +# in the main schema. Therefore, if you use such arrays, you may need to define +# the array type in the main schema, even if it is unused outside of the +# storage daemon. + +{ 'include': '../../qapi/pragma.json' } + +{ 'include': '../../qapi/block-core.json' } +{ 'include': '../../qapi/char.json' } +{ 'include': '../../qapi/common.json' } +{ 'include': '../../qapi/control.json' } +{ 'include': '../../qapi/crypto.json' } +{ 'include': '../../qapi/introspect.json' } +{ 'include': '../../qapi/job.json' } +{ 'include': '../../qapi/qom.json' } +{ 'include': '../../qapi/sockets.json' } +{ 'include': '../../qapi/transaction.json' } diff --git a/stubs/Makefile.objs b/stubs/Makefile.objs index 4a50e95ec3..45be5dc0ed 100644 --- a/stubs/Makefile.objs +++ b/stubs/Makefile.objs @@ -1,3 +1,4 @@ +stub-obj-y += arch_type.o stub-obj-y += bdrv-next-monitor-owned.o stub-obj-y += blk-commit-all.o stub-obj-y += blockdev-close-all-bdrv-states.o @@ -13,10 +14,12 @@ stub-obj-y += iothread.o stub-obj-y += iothread-lock.o stub-obj-y += is-daemonized.o stub-obj-$(CONFIG_LINUX_AIO) += linux-aio.o +stub-obj-$(CONFIG_LINUX_IO_URING) += io_uring.o stub-obj-y += machine-init-done.o stub-obj-y += migr-blocker.o stub-obj-y += change-state-handler.o stub-obj-y += monitor.o +stub-obj-y += monitor-core.o stub-obj-y += notify-event.o stub-obj-y += qtest.o stub-obj-y += replay.o @@ -33,7 +36,6 @@ stub-obj-y += fd-register.o stub-obj-y += qmp_memory_device.o stub-obj-y += target-monitor-defs.o stub-obj-y += target-get-monitor-def.o -stub-obj-y += pc_madt_cpu_entry.o stub-obj-y += vmgenid.o stub-obj-y += xen-common.o stub-obj-y += xen-hvm.o diff --git a/stubs/arch_type.c b/stubs/arch_type.c new file mode 100644 index 0000000000..fc5423bc98 --- /dev/null +++ b/stubs/arch_type.c @@ -0,0 +1,4 @@ +#include "qemu/osdep.h" +#include "sysemu/arch_init.h" + +const uint32_t arch_type = QEMU_ARCH_NONE; diff --git a/stubs/io_uring.c b/stubs/io_uring.c new file mode 100644 index 0000000000..622d1e4648 --- /dev/null +++ b/stubs/io_uring.c @@ -0,0 +1,32 @@ +/* + * Linux io_uring support. + * + * Copyright (C) 2009 IBM, Corp. + * Copyright (C) 2009 Red Hat, Inc. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ +#include "qemu/osdep.h" +#include "block/aio.h" +#include "block/raw-aio.h" + +void luring_detach_aio_context(LuringState *s, AioContext *old_context) +{ + abort(); +} + +void luring_attach_aio_context(LuringState *s, AioContext *new_context) +{ + abort(); +} + +LuringState *luring_init(Error **errp) +{ + abort(); +} + +void luring_cleanup(LuringState *s) +{ + abort(); +} diff --git a/stubs/monitor-core.c b/stubs/monitor-core.c new file mode 100644 index 0000000000..6cff1c4e1d --- /dev/null +++ b/stubs/monitor-core.c @@ -0,0 +1,21 @@ +#include "qemu/osdep.h" +#include "monitor/monitor.h" +#include "qemu-common.h" +#include "qapi/qapi-emit-events.h" + +__thread Monitor *cur_mon; + +void monitor_init_qmp(Chardev *chr, bool pretty, Error **errp) +{ +} + +void qapi_event_emit(QAPIEvent event, QDict *qdict) +{ +} + +int monitor_vprintf(Monitor *mon, const char *fmt, va_list ap) +{ + abort(); +} + + diff --git a/stubs/monitor.c b/stubs/monitor.c index c3e9a2e4dc..20786ac4ff 100644 --- a/stubs/monitor.c +++ b/stubs/monitor.c @@ -1,14 +1,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "qapi/qapi-emit-events.h" #include "monitor/monitor.h" - -__thread Monitor *cur_mon; - -int monitor_vprintf(Monitor *mon, const char *fmt, va_list ap) -{ - abort(); -} +#include "../monitor/monitor-internal.h" int monitor_get_fd(Monitor *mon, const char *name, Error **errp) { @@ -16,14 +9,10 @@ int monitor_get_fd(Monitor *mon, const char *name, Error **errp) return -1; } -void monitor_init_qmp(Chardev *chr, bool pretty) +void monitor_init_hmp(Chardev *chr, bool use_readline, Error **errp) { } -void monitor_init_hmp(Chardev *chr, bool use_readline) -{ -} - -void qapi_event_emit(QAPIEvent event, QDict *qdict) +void monitor_fdsets_cleanup(void) { } diff --git a/stubs/replay.c b/stubs/replay.c index 10b3925e2f..5974ec1f50 100644 --- a/stubs/replay.c +++ b/stubs/replay.c @@ -79,3 +79,12 @@ void replay_mutex_lock(void) void replay_mutex_unlock(void) { } + +void replay_save_random(int ret, void *buf, size_t len) +{ +} + +int replay_read_random(void *buf, size_t len) +{ + return 0; +} diff --git a/stubs/semihost.c b/stubs/semihost.c index f90589259c..1d8b37f7b2 100644 --- a/stubs/semihost.c +++ b/stubs/semihost.c @@ -69,3 +69,7 @@ void semihosting_arg_fallback(const char *file, const char *cmd) void qemu_semihosting_connect_chardevs(void) { } + +void qemu_semihosting_console_init(void) +{ +} diff --git a/stubs/vmstate.c b/stubs/vmstate.c index e1e89b87f0..cc4fe41dfc 100644 --- a/stubs/vmstate.c +++ b/stubs/vmstate.c @@ -3,8 +3,8 @@ const VMStateDescription vmstate_dummy = {}; -int vmstate_register_with_alias_id(DeviceState *dev, - int instance_id, +int vmstate_register_with_alias_id(VMStateIf *obj, + uint32_t instance_id, const VMStateDescription *vmsd, void *base, int alias_id, int required_for_version, @@ -13,7 +13,7 @@ int vmstate_register_with_alias_id(DeviceState *dev, return 0; } -void vmstate_unregister(DeviceState *dev, +void vmstate_unregister(VMStateIf *obj, const VMStateDescription *vmsd, void *opaque) { diff --git a/target/alpha/cpu-qom.h b/target/alpha/cpu-qom.h index 6f0a0adb9e..08832fa767 100644 --- a/target/alpha/cpu-qom.h +++ b/target/alpha/cpu-qom.h @@ -44,7 +44,7 @@ typedef struct AlphaCPUClass { /*< public >*/ DeviceRealize parent_realize; - void (*parent_reset)(CPUState *cpu); + DeviceReset parent_reset; } AlphaCPUClass; typedef struct AlphaCPU AlphaCPU; diff --git a/target/alpha/cpu.h b/target/alpha/cpu.h index a530249a5b..be29bdd530 100644 --- a/target/alpha/cpu.h +++ b/target/alpha/cpu.h @@ -193,8 +193,6 @@ enum { PALcode cheats and usees the KSEG mapping for its code+data rather than physical addresses. */ -#define MMU_MODE0_SUFFIX _kernel -#define MMU_MODE1_SUFFIX _user #define MMU_KERNEL_IDX 0 #define MMU_USER_IDX 1 #define MMU_PHYS_IDX 2 @@ -282,7 +280,7 @@ void alpha_cpu_do_interrupt(CPUState *cpu); bool alpha_cpu_exec_interrupt(CPUState *cpu, int int_req); void alpha_cpu_dump_state(CPUState *cs, FILE *f, int flags); hwaddr alpha_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); -int alpha_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); +int alpha_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int alpha_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); void alpha_cpu_do_unaligned_access(CPUState *cpu, vaddr addr, MMUAccessType access_type, diff --git a/target/alpha/gdbstub.c b/target/alpha/gdbstub.c index 7f9cc092a9..0cd76ddaa9 100644 --- a/target/alpha/gdbstub.c +++ b/target/alpha/gdbstub.c @@ -21,7 +21,7 @@ #include "cpu.h" #include "exec/gdbstub.h" -int alpha_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +int alpha_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) { AlphaCPU *cpu = ALPHA_CPU(cs); CPUAlphaState *env = &cpu->env; diff --git a/target/alpha/translate.c b/target/alpha/translate.c index f7f1ed0f41..8870284f57 100644 --- a/target/alpha/translate.c +++ b/target/alpha/translate.c @@ -23,7 +23,7 @@ #include "disas/disas.h" #include "qemu/host-utils.h" #include "exec/exec-all.h" -#include "tcg-op.h" +#include "tcg/tcg-op.h" #include "exec/cpu_ldst.h" #include "exec/helper-proto.h" #include "exec/helper-gen.h" diff --git a/target/arm/arch_dump.c b/target/arm/arch_dump.c index 26a2c09868..7693e17e96 100644 --- a/target/arm/arch_dump.c +++ b/target/arm/arch_dump.c @@ -62,12 +62,23 @@ struct aarch64_user_vfp_state { QEMU_BUILD_BUG_ON(sizeof(struct aarch64_user_vfp_state) != 528); +/* struct user_sve_header from arch/arm64/include/uapi/asm/ptrace.h */ +struct aarch64_user_sve_header { + uint32_t size; + uint32_t max_size; + uint16_t vl; + uint16_t max_vl; + uint16_t flags; + uint16_t reserved; +} QEMU_PACKED; + struct aarch64_note { Elf64_Nhdr hdr; char name[8]; /* align_up(sizeof("CORE"), 4) */ union { struct aarch64_elf_prstatus prstatus; struct aarch64_user_vfp_state vfp; + struct aarch64_user_sve_header sve; }; } QEMU_PACKED; @@ -76,6 +87,8 @@ struct aarch64_note { (AARCH64_NOTE_HEADER_SIZE + sizeof(struct aarch64_elf_prstatus)) #define AARCH64_PRFPREG_NOTE_SIZE \ (AARCH64_NOTE_HEADER_SIZE + sizeof(struct aarch64_user_vfp_state)) +#define AARCH64_SVE_NOTE_SIZE(env) \ + (AARCH64_NOTE_HEADER_SIZE + sve_size(env)) static void aarch64_note_init(struct aarch64_note *note, DumpState *s, const char *name, Elf64_Word namesz, @@ -128,11 +141,102 @@ static int aarch64_write_elf64_prfpreg(WriteCoreDumpFunction f, return 0; } +#ifdef TARGET_AARCH64 +static off_t sve_zreg_offset(uint32_t vq, int n) +{ + off_t off = sizeof(struct aarch64_user_sve_header); + return ROUND_UP(off, 16) + vq * 16 * n; +} + +static off_t sve_preg_offset(uint32_t vq, int n) +{ + return sve_zreg_offset(vq, 32) + vq * 16 / 8 * n; +} + +static off_t sve_fpsr_offset(uint32_t vq) +{ + off_t off = sve_preg_offset(vq, 17); + return ROUND_UP(off, 16); +} + +static off_t sve_fpcr_offset(uint32_t vq) +{ + return sve_fpsr_offset(vq) + sizeof(uint32_t); +} + +static uint32_t sve_current_vq(CPUARMState *env) +{ + return sve_zcr_len_for_el(env, arm_current_el(env)) + 1; +} + +static size_t sve_size_vq(uint32_t vq) +{ + off_t off = sve_fpcr_offset(vq) + sizeof(uint32_t); + return ROUND_UP(off, 16); +} + +static size_t sve_size(CPUARMState *env) +{ + return sve_size_vq(sve_current_vq(env)); +} + +static int aarch64_write_elf64_sve(WriteCoreDumpFunction f, + CPUARMState *env, int cpuid, + DumpState *s) +{ + struct aarch64_note *note; + ARMCPU *cpu = env_archcpu(env); + uint32_t vq = sve_current_vq(env); + uint64_t tmp[ARM_MAX_VQ * 2], *r; + uint32_t fpr; + uint8_t *buf; + int ret, i; + + note = g_malloc0(AARCH64_SVE_NOTE_SIZE(env)); + buf = (uint8_t *)¬e->sve; + + aarch64_note_init(note, s, "LINUX", 6, NT_ARM_SVE, sve_size_vq(vq)); + + note->sve.size = cpu_to_dump32(s, sve_size_vq(vq)); + note->sve.max_size = cpu_to_dump32(s, sve_size_vq(cpu->sve_max_vq)); + note->sve.vl = cpu_to_dump16(s, vq * 16); + note->sve.max_vl = cpu_to_dump16(s, cpu->sve_max_vq * 16); + note->sve.flags = cpu_to_dump16(s, 1); + + for (i = 0; i < 32; ++i) { + r = sve_bswap64(tmp, &env->vfp.zregs[i].d[0], vq * 2); + memcpy(&buf[sve_zreg_offset(vq, i)], r, vq * 16); + } + + for (i = 0; i < 17; ++i) { + r = sve_bswap64(tmp, r = &env->vfp.pregs[i].p[0], + DIV_ROUND_UP(vq * 2, 8)); + memcpy(&buf[sve_preg_offset(vq, i)], r, vq * 16 / 8); + } + + fpr = cpu_to_dump32(s, vfp_get_fpsr(env)); + memcpy(&buf[sve_fpsr_offset(vq)], &fpr, sizeof(uint32_t)); + + fpr = cpu_to_dump32(s, vfp_get_fpcr(env)); + memcpy(&buf[sve_fpcr_offset(vq)], &fpr, sizeof(uint32_t)); + + ret = f(note, AARCH64_SVE_NOTE_SIZE(env), s); + g_free(note); + + if (ret < 0) { + return -1; + } + + return 0; +} +#endif + int arm_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs, int cpuid, void *opaque) { struct aarch64_note note; - CPUARMState *env = &ARM_CPU(cs)->env; + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; DumpState *s = opaque; uint64_t pstate, sp; int ret, i; @@ -163,7 +267,18 @@ int arm_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs, return -1; } - return aarch64_write_elf64_prfpreg(f, env, cpuid, s); + ret = aarch64_write_elf64_prfpreg(f, env, cpuid, s); + if (ret) { + return ret; + } + +#ifdef TARGET_AARCH64 + if (cpu_isar_feature(aa64_sve, cpu)) { + ret = aarch64_write_elf64_sve(f, env, cpuid, s); + } +#endif + + return ret; } /* struct pt_regs from arch/arm/include/asm/ptrace.h */ @@ -248,9 +363,11 @@ int arm_cpu_write_elf32_note(WriteCoreDumpFunction f, CPUState *cs, int cpuid, void *opaque) { struct arm_note note; - CPUARMState *env = &ARM_CPU(cs)->env; + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; DumpState *s = opaque; - int ret, i, fpvalid = !!arm_feature(env, ARM_FEATURE_VFP); + int ret, i; + bool fpvalid = cpu_isar_feature(aa32_vfp_simd, cpu); arm_note_init(¬e, s, "CORE", 5, NT_PRSTATUS, sizeof(note.prstatus)); @@ -329,15 +446,19 @@ int cpu_get_dump_info(ArchDumpInfo *info, ssize_t cpu_get_note_size(int class, int machine, int nr_cpus) { ARMCPU *cpu = ARM_CPU(first_cpu); - CPUARMState *env = &cpu->env; size_t note_size; if (class == ELFCLASS64) { note_size = AARCH64_PRSTATUS_NOTE_SIZE; note_size += AARCH64_PRFPREG_NOTE_SIZE; +#ifdef TARGET_AARCH64 + if (cpu_isar_feature(aa64_sve, cpu)) { + note_size += AARCH64_SVE_NOTE_SIZE(&cpu->env); + } +#endif } else { note_size = ARM_PRSTATUS_NOTE_SIZE; - if (arm_feature(env, ARM_FEATURE_VFP)) { + if (cpu_isar_feature(aa32_vfp_simd, cpu)) { note_size += ARM_VFP_NOTE_SIZE; } } diff --git a/target/arm/arm-powerctl.c b/target/arm/arm-powerctl.c index f77a950db6..b75f813b40 100644 --- a/target/arm/arm-powerctl.c +++ b/target/arm/arm-powerctl.c @@ -104,6 +104,9 @@ static void arm_set_cpu_on_async_work(CPUState *target_cpu_state, /* Processor is not in secure mode */ target_cpu->env.cp15.scr_el3 |= SCR_NS; + /* Set NSACR.{CP11,CP10} so NS can access the FPU */ + target_cpu->env.cp15.nsacr |= 3 << 10; + /* * If QEMU is providing the equivalent of EL3 firmware, then we need * to make sure a CPU targeting EL2 comes out of reset with a @@ -124,6 +127,9 @@ static void arm_set_cpu_on_async_work(CPUState *target_cpu_state, target_cpu->env.regs[0] = info->context_id; } + /* CP15 update requires rebuilding hflags */ + arm_rebuild_hflags(&target_cpu->env); + /* Start the new CPU at the requested address */ cpu_set_pc(target_cpu_state, info->entry); diff --git a/target/arm/arm-semi.c b/target/arm/arm-semi.c index 6f7b6d801b..8718fd0194 100644 --- a/target/arm/arm-semi.c +++ b/target/arm/arm-semi.c @@ -144,7 +144,8 @@ static int alloc_guestfd(void) guestfd_array = g_array_new(FALSE, TRUE, sizeof(GuestFD)); } - for (i = 0; i < guestfd_array->len; i++) { + /* SYS_OPEN should return nonzero handle on success. Start guestfd from 1 */ + for (i = 1; i < guestfd_array->len; i++) { GuestFD *gf = &g_array_index(guestfd_array, GuestFD, i); if (gf->type == GuestFDUnused) { @@ -168,7 +169,7 @@ static GuestFD *do_get_guestfd(int guestfd) return NULL; } - if (guestfd < 0 || guestfd >= guestfd_array->len) { + if (guestfd <= 0 || guestfd >= guestfd_array->len) { return NULL; } @@ -402,6 +403,15 @@ static uint32_t host_closefn(ARMCPU *cpu, GuestFD *gf) { CPUARMState *env = &cpu->env; + /* + * Only close the underlying host fd if it's one we opened on behalf + * of the guest in SYS_OPEN. + */ + if (gf->hostfd == STDIN_FILENO || + gf->hostfd == STDOUT_FILENO || + gf->hostfd == STDERR_FILENO) { + return 0; + } return set_swi_errno(env, close(gf->hostfd)); } @@ -802,8 +812,7 @@ target_ulong do_arm_semihosting(CPUARMState *env) return guestfd_fns[gf->type].readfn(cpu, gf, arg1, len); case TARGET_SYS_READC: - qemu_log_mask(LOG_UNIMP, "%s: SYS_READC not implemented", __func__); - return 0; + return qemu_semihosting_console_inc(env); case TARGET_SYS_ISTTY: GET_ARG(0); diff --git a/target/arm/cpu-param.h b/target/arm/cpu-param.h index 6e6948e960..d593b60b28 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 12 #endif diff --git a/target/arm/cpu-qom.h b/target/arm/cpu-qom.h index 7f5b244bde..d95568bf05 100644 --- a/target/arm/cpu-qom.h +++ b/target/arm/cpu-qom.h @@ -51,7 +51,7 @@ typedef struct ARMCPUClass { const ARMCPUInfo *info; DeviceRealize parent_realize; - void (*parent_reset)(CPUState *cpu); + DeviceReset parent_reset; } ARMCPUClass; typedef struct ARMCPU ARMCPU; @@ -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 7a4ac9339b..a79f233b17 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -155,14 +155,14 @@ static void cp_reg_check_reset(gpointer key, gpointer value, gpointer opaque) assert(oldvalue == newvalue); } -/* CPUClass::reset() */ -static void arm_cpu_reset(CPUState *s) +static void arm_cpu_reset(DeviceState *dev) { + CPUState *s = CPU(dev); ARMCPU *cpu = ARM_CPU(s); ARMCPUClass *acc = ARM_CPU_GET_CLASS(cpu); CPUARMState *env = &cpu->env; - acc->parent_reset(s); + acc->parent_reset(dev); memset(env, 0, offsetof(CPUARMState, end_reset_fields)); @@ -191,19 +191,14 @@ static void arm_cpu_reset(CPUState *s) /* Enable all PAC keys. */ env->cp15.sctlr_el[1] |= (SCTLR_EnIA | SCTLR_EnIB | SCTLR_EnDA | SCTLR_EnDB); - /* Enable all PAC instructions */ - env->cp15.hcr_el2 |= HCR_API; - env->cp15.scr_el3 |= SCR_API; /* and to the FP/Neon instructions */ env->cp15.cpacr_el1 = deposit64(env->cp15.cpacr_el1, 20, 2, 3); /* and to the SVE instructions */ env->cp15.cpacr_el1 = deposit64(env->cp15.cpacr_el1, 16, 2, 3); - env->cp15.cptr_el[3] |= CPTR_EZ; - /* with maximum vector length */ - env->vfp.zcr_el[1] = cpu_isar_feature(aa64_sve, cpu) ? - cpu->sve_max_vq - 1 : 0; - env->vfp.zcr_el[2] = env->vfp.zcr_el[1]; - env->vfp.zcr_el[3] = env->vfp.zcr_el[1]; + /* with reasonable vector length */ + if (cpu_isar_feature(aa64_sve, cpu)) { + env->vfp.zcr_el[1] = MIN(cpu->sve_max_vq - 1, 3); + } /* * Enable TBI0 and TBI1. While the real kernel only enables TBI0, * turning on both here will produce smaller code and otherwise @@ -293,7 +288,7 @@ static void arm_cpu_reset(CPUState *s) env->v7m.ccr[M_REG_S] |= R_V7M_CCR_UNALIGN_TRP_MASK; } - if (arm_feature(env, ARM_FEATURE_VFP)) { + if (cpu_isar_feature(aa32_vfp_simd, cpu)) { env->v7m.fpccr[M_REG_NS] = R_V7M_FPCCR_ASPEN_MASK; env->v7m.fpccr[M_REG_S] = R_V7M_FPCCR_ASPEN_MASK | R_V7M_FPCCR_LSPEN_MASK | R_V7M_FPCCR_S_MASK; @@ -410,58 +405,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) @@ -894,11 +1004,10 @@ static void arm_cpu_dump_state(CPUState *cs, FILE *f, int flags) if (flags & CPU_DUMP_FPU) { int numvfpregs = 0; - if (arm_feature(env, ARM_FEATURE_VFP)) { - numvfpregs += 16; - } - if (arm_feature(env, ARM_FEATURE_VFP3)) { - numvfpregs += 16; + if (cpu_isar_feature(aa32_simd_r32, cpu)) { + numvfpregs = 32; + } else if (cpu_isar_feature(aa32_vfp_simd, cpu)) { + numvfpregs = 16; } for (i = 0; i < numvfpregs; i++) { uint64_t v = *aa32_vfp_dreg(env, i); @@ -976,6 +1085,10 @@ static void arm_cpu_initfn(Object *obj) } } +static Property arm_cpu_gt_cntfrq_property = + DEFINE_PROP_UINT64("cntfrq", ARMCPU, gt_cntfrq_hz, + NANOSECONDS_PER_SECOND / GTIMER_SCALE); + static Property arm_cpu_reset_cbar_property = DEFINE_PROP_UINT64("reset-cbar", ARMCPU, reset_cbar, 0); @@ -985,11 +1098,13 @@ static Property arm_cpu_reset_hivecs_property = static Property arm_cpu_rvbar_property = DEFINE_PROP_UINT64("rvbar", ARMCPU, rvbar, 0); +#ifndef CONFIG_USER_ONLY static Property arm_cpu_has_el2_property = DEFINE_PROP_BOOL("has_el2", ARMCPU, has_el2, true); static Property arm_cpu_has_el3_property = DEFINE_PROP_BOOL("has_el3", ARMCPU, has_el3, true); +#endif static Property arm_cpu_cfgend_property = DEFINE_PROP_BOOL("cfgend", ARMCPU, cfgend, false); @@ -1039,20 +1154,28 @@ static void arm_set_pmu(Object *obj, bool value, Error **errp) cpu->has_pmu = value; } -static void arm_get_init_svtor(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) +unsigned int gt_cntfrq_period_ns(ARMCPU *cpu) { - ARMCPU *cpu = ARM_CPU(obj); - - visit_type_uint32(v, name, &cpu->init_svtor, errp); -} - -static void arm_set_init_svtor(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - ARMCPU *cpu = ARM_CPU(obj); - - visit_type_uint32(v, name, &cpu->init_svtor, errp); + /* + * The exact approach to calculating guest ticks is: + * + * muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), cpu->gt_cntfrq_hz, + * NANOSECONDS_PER_SECOND); + * + * We don't do that. Rather we intentionally use integer division + * truncation below and in the caller for the conversion of host monotonic + * time to guest ticks to provide the exact inverse for the semantics of + * the QEMUTimer scale factor. QEMUTimer's scale facter is an integer, so + * it loses precision when representing frequencies where + * `(NANOSECONDS_PER_SECOND % cpu->gt_cntfrq) > 0` holds. Failing to + * provide an exact inverse leads to scheduling timers with negative + * periods, which in turn leads to sticky behaviour in the guest. + * + * Finally, CNTFRQ is effectively capped at 1GHz to ensure our scale factor + * cannot become zero. + */ + return NANOSECONDS_PER_SECOND > cpu->gt_cntfrq_hz ? + NANOSECONDS_PER_SECOND / cpu->gt_cntfrq_hz : 1; } void arm_cpu_post_init(Object *obj) @@ -1066,51 +1189,39 @@ void arm_cpu_post_init(Object *obj) if (arm_feature(&cpu->env, ARM_FEATURE_M)) { set_feature(&cpu->env, ARM_FEATURE_PMSA); } - /* Similarly for the VFP feature bits */ - if (arm_feature(&cpu->env, ARM_FEATURE_VFP4)) { - set_feature(&cpu->env, ARM_FEATURE_VFP3); - } - if (arm_feature(&cpu->env, ARM_FEATURE_VFP3)) { - set_feature(&cpu->env, ARM_FEATURE_VFP); - } if (arm_feature(&cpu->env, ARM_FEATURE_CBAR) || arm_feature(&cpu->env, ARM_FEATURE_CBAR_RO)) { - qdev_property_add_static(DEVICE(obj), &arm_cpu_reset_cbar_property, - &error_abort); + qdev_property_add_static(DEVICE(obj), &arm_cpu_reset_cbar_property); } if (!arm_feature(&cpu->env, ARM_FEATURE_M)) { - qdev_property_add_static(DEVICE(obj), &arm_cpu_reset_hivecs_property, - &error_abort); + qdev_property_add_static(DEVICE(obj), &arm_cpu_reset_hivecs_property); } if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) { - qdev_property_add_static(DEVICE(obj), &arm_cpu_rvbar_property, - &error_abort); + qdev_property_add_static(DEVICE(obj), &arm_cpu_rvbar_property); } +#ifndef CONFIG_USER_ONLY if (arm_feature(&cpu->env, ARM_FEATURE_EL3)) { /* Add the has_el3 state CPU property only if EL3 is allowed. This will * prevent "has_el3" from existing on CPUs which cannot support EL3. */ - qdev_property_add_static(DEVICE(obj), &arm_cpu_has_el3_property, - &error_abort); + qdev_property_add_static(DEVICE(obj), &arm_cpu_has_el3_property); -#ifndef CONFIG_USER_ONLY object_property_add_link(obj, "secure-memory", TYPE_MEMORY_REGION, (Object **)&cpu->secure_memory, qdev_prop_allow_set_link_before_realize, OBJ_PROP_LINK_STRONG, &error_abort); -#endif } if (arm_feature(&cpu->env, ARM_FEATURE_EL2)) { - qdev_property_add_static(DEVICE(obj), &arm_cpu_has_el2_property, - &error_abort); + qdev_property_add_static(DEVICE(obj), &arm_cpu_has_el2_property); } +#endif if (arm_feature(&cpu->env, ARM_FEATURE_PMU)) { cpu->has_pmu = true; @@ -1123,35 +1234,32 @@ void arm_cpu_post_init(Object *obj) * KVM does not currently allow us to lie to the guest about its * ID/feature registers, so the guest always sees what the host has. */ - if (arm_feature(&cpu->env, ARM_FEATURE_VFP)) { + if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64) + ? cpu_isar_feature(aa64_fp_simd, cpu) + : cpu_isar_feature(aa32_vfp, cpu)) { cpu->has_vfp = true; if (!kvm_enabled()) { - qdev_property_add_static(DEVICE(obj), &arm_cpu_has_vfp_property, - &error_abort); + qdev_property_add_static(DEVICE(obj), &arm_cpu_has_vfp_property); } } if (arm_feature(&cpu->env, ARM_FEATURE_NEON)) { cpu->has_neon = true; if (!kvm_enabled()) { - qdev_property_add_static(DEVICE(obj), &arm_cpu_has_neon_property, - &error_abort); + qdev_property_add_static(DEVICE(obj), &arm_cpu_has_neon_property); } } if (arm_feature(&cpu->env, ARM_FEATURE_M) && arm_feature(&cpu->env, ARM_FEATURE_THUMB_DSP)) { - qdev_property_add_static(DEVICE(obj), &arm_cpu_has_dsp_property, - &error_abort); + qdev_property_add_static(DEVICE(obj), &arm_cpu_has_dsp_property); } if (arm_feature(&cpu->env, ARM_FEATURE_PMSA)) { - qdev_property_add_static(DEVICE(obj), &arm_cpu_has_mpu_property, - &error_abort); + qdev_property_add_static(DEVICE(obj), &arm_cpu_has_mpu_property); if (arm_feature(&cpu->env, ARM_FEATURE_V7)) { qdev_property_add_static(DEVICE(obj), - &arm_cpu_pmsav7_dregion_property, - &error_abort); + &arm_cpu_pmsav7_dregion_property); } } @@ -1165,13 +1273,16 @@ void arm_cpu_post_init(Object *obj) * a simple DEFINE_PROP_UINT32 for this because we want to permit * the property to be set after realize. */ - object_property_add(obj, "init-svtor", "uint32", - arm_get_init_svtor, arm_set_init_svtor, - NULL, NULL, &error_abort); + object_property_add_uint32_ptr(obj, "init-svtor", + &cpu->init_svtor, + OBJ_PROP_FLAG_READWRITE, &error_abort); } - qdev_property_add_static(DEVICE(obj), &arm_cpu_cfgend_property, - &error_abort); + qdev_property_add_static(DEVICE(obj), &arm_cpu_cfgend_property); + + if (arm_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER)) { + qdev_property_add_static(DEVICE(cpu), &arm_cpu_gt_cntfrq_property); + } } static void arm_cpu_finalizefn(Object *obj) @@ -1251,14 +1362,31 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) } } - cpu->gt_timer[GTIMER_PHYS] = timer_new(QEMU_CLOCK_VIRTUAL, GTIMER_SCALE, - arm_gt_ptimer_cb, cpu); - cpu->gt_timer[GTIMER_VIRT] = timer_new(QEMU_CLOCK_VIRTUAL, GTIMER_SCALE, - arm_gt_vtimer_cb, cpu); - cpu->gt_timer[GTIMER_HYP] = timer_new(QEMU_CLOCK_VIRTUAL, GTIMER_SCALE, - arm_gt_htimer_cb, cpu); - cpu->gt_timer[GTIMER_SEC] = timer_new(QEMU_CLOCK_VIRTUAL, GTIMER_SCALE, - arm_gt_stimer_cb, cpu); + { + uint64_t scale; + + if (arm_feature(env, ARM_FEATURE_GENERIC_TIMER)) { + if (!cpu->gt_cntfrq_hz) { + error_setg(errp, "Invalid CNTFRQ: %"PRId64"Hz", + cpu->gt_cntfrq_hz); + return; + } + scale = gt_cntfrq_period_ns(cpu); + } else { + scale = GTIMER_SCALE; + } + + cpu->gt_timer[GTIMER_PHYS] = timer_new(QEMU_CLOCK_VIRTUAL, scale, + arm_gt_ptimer_cb, cpu); + cpu->gt_timer[GTIMER_VIRT] = timer_new(QEMU_CLOCK_VIRTUAL, scale, + arm_gt_vtimer_cb, cpu); + cpu->gt_timer[GTIMER_HYP] = timer_new(QEMU_CLOCK_VIRTUAL, scale, + 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 cpu_exec_realizefn(cs, &local_err); @@ -1288,10 +1416,6 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) uint64_t t; uint32_t u; - unset_feature(env, ARM_FEATURE_VFP); - unset_feature(env, ARM_FEATURE_VFP3); - unset_feature(env, ARM_FEATURE_VFP4); - t = cpu->isar.id_aa64isar1; t = FIELD_DP64(t, ID_AA64ISAR1, JSCVT, 0); cpu->isar.id_aa64isar1 = t; @@ -1358,7 +1482,6 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) u = FIELD_DP32(u, MVFR1, SIMDINT, 0); u = FIELD_DP32(u, MVFR1, SIMDSP, 0); u = FIELD_DP32(u, MVFR1, SIMDHP, 0); - u = FIELD_DP32(u, MVFR1, SIMDFMAC, 0); cpu->isar.mvfr1 = u; u = cpu->isar.mvfr2; @@ -1381,6 +1504,11 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) u = cpu->isar.mvfr0; u = FIELD_DP32(u, MVFR0, SIMDREG, 0); cpu->isar.mvfr0 = u; + + /* Despite the name, this field covers both VFP and Neon */ + u = cpu->isar.mvfr1; + u = FIELD_DP32(u, MVFR1, SIMDFMAC, 0); + cpu->isar.mvfr1 = u; } if (arm_feature(env, ARM_FEATURE_M) && !cpu->has_dsp) { @@ -1433,7 +1561,8 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) * Presence of EL2 itself is ARM_FEATURE_EL2, and of the * Security Extensions is ARM_FEATURE_EL3. */ - assert(!tcg_enabled() || no_aa32 || cpu_isar_feature(arm_div, cpu)); + assert(!tcg_enabled() || no_aa32 || + cpu_isar_feature(aa32_arm_div, cpu)); set_feature(env, ARM_FEATURE_LPAE); set_feature(env, ARM_FEATURE_V7); } @@ -1459,7 +1588,8 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) if (arm_feature(env, ARM_FEATURE_V6)) { set_feature(env, ARM_FEATURE_V5); if (!arm_feature(env, ARM_FEATURE_M)) { - assert(!tcg_enabled() || no_aa32 || cpu_isar_feature(jazelle, cpu)); + assert(!tcg_enabled() || no_aa32 || + cpu_isar_feature(aa32_jazelle, cpu)); set_feature(env, ARM_FEATURE_AUXCR); } } @@ -1482,8 +1612,9 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) * We rely on no XScale CPU having VFP so we can use the same bits in the * TB flags field for VECSTRIDE and XSCALE_CPAR. */ - assert(!(arm_feature(env, ARM_FEATURE_VFP) && - arm_feature(env, ARM_FEATURE_XSCALE))); + assert(arm_feature(&cpu->env, ARM_FEATURE_AARCH64) || + !cpu_isar_feature(aa32_vfp_simd, cpu) || + !arm_feature(env, ARM_FEATURE_XSCALE)); if (arm_feature(env, ARM_FEATURE_V7) && !arm_feature(env, ARM_FEATURE_M) && @@ -1563,8 +1694,9 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) cpu); #endif } else { - cpu->id_aa64dfr0 &= ~0xf00; - cpu->id_dfr0 &= ~(0xf << 24); + cpu->isar.id_aa64dfr0 = + FIELD_DP64(cpu->isar.id_aa64dfr0, ID_AA64DFR0, PMUVER, 0); + cpu->isar.id_dfr0 = FIELD_DP32(cpu->isar.id_dfr0, ID_DFR0, PERFMON, 0); cpu->pmceid0 = 0; cpu->pmceid1 = 0; } @@ -1703,7 +1835,6 @@ static void arm926_initfn(Object *obj) cpu->dtb_compatible = "arm,arm926"; set_feature(&cpu->env, ARM_FEATURE_V5); - set_feature(&cpu->env, ARM_FEATURE_VFP); set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS); set_feature(&cpu->env, ARM_FEATURE_CACHE_TEST_CLEAN); cpu->midr = 0x41069265; @@ -1717,10 +1848,11 @@ static void arm926_initfn(Object *obj) */ cpu->isar.id_isar1 = FIELD_DP32(cpu->isar.id_isar1, ID_ISAR1, JAZELLE, 1); /* - * Similarly, we need to set MVFR0 fields to enable double precision - * and short vector support even though ARMv5 doesn't have this register. + * Similarly, we need to set MVFR0 fields to enable vfp and short vector + * support even though ARMv5 doesn't have this register. */ cpu->isar.mvfr0 = FIELD_DP32(cpu->isar.mvfr0, MVFR0, FPSHVEC, 1); + cpu->isar.mvfr0 = FIELD_DP32(cpu->isar.mvfr0, MVFR0, FPSP, 1); cpu->isar.mvfr0 = FIELD_DP32(cpu->isar.mvfr0, MVFR0, FPDP, 1); } @@ -1743,7 +1875,6 @@ static void arm1026_initfn(Object *obj) cpu->dtb_compatible = "arm,arm1026"; set_feature(&cpu->env, ARM_FEATURE_V5); - set_feature(&cpu->env, ARM_FEATURE_VFP); set_feature(&cpu->env, ARM_FEATURE_AUXCR); set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS); set_feature(&cpu->env, ARM_FEATURE_CACHE_TEST_CLEAN); @@ -1759,10 +1890,11 @@ static void arm1026_initfn(Object *obj) */ cpu->isar.id_isar1 = FIELD_DP32(cpu->isar.id_isar1, ID_ISAR1, JAZELLE, 1); /* - * Similarly, we need to set MVFR0 fields to enable double precision - * and short vector support even though ARMv5 doesn't have this register. + * Similarly, we need to set MVFR0 fields to enable vfp and short vector + * support even though ARMv5 doesn't have this register. */ cpu->isar.mvfr0 = FIELD_DP32(cpu->isar.mvfr0, MVFR0, FPSHVEC, 1); + cpu->isar.mvfr0 = FIELD_DP32(cpu->isar.mvfr0, MVFR0, FPSP, 1); cpu->isar.mvfr0 = FIELD_DP32(cpu->isar.mvfr0, MVFR0, FPDP, 1); { @@ -1790,7 +1922,6 @@ static void arm1136_r2_initfn(Object *obj) cpu->dtb_compatible = "arm,arm1136"; set_feature(&cpu->env, ARM_FEATURE_V6); - set_feature(&cpu->env, ARM_FEATURE_VFP); set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS); set_feature(&cpu->env, ARM_FEATURE_CACHE_DIRTY_REG); set_feature(&cpu->env, ARM_FEATURE_CACHE_BLOCK_OPS); @@ -1802,11 +1933,11 @@ static void arm1136_r2_initfn(Object *obj) cpu->reset_sctlr = 0x00050078; cpu->id_pfr0 = 0x111; cpu->id_pfr1 = 0x1; - cpu->id_dfr0 = 0x2; + cpu->isar.id_dfr0 = 0x2; cpu->id_afr0 = 0x3; - cpu->id_mmfr0 = 0x01130003; - cpu->id_mmfr1 = 0x10030302; - cpu->id_mmfr2 = 0x01222110; + cpu->isar.id_mmfr0 = 0x01130003; + cpu->isar.id_mmfr1 = 0x10030302; + cpu->isar.id_mmfr2 = 0x01222110; cpu->isar.id_isar0 = 0x00140011; cpu->isar.id_isar1 = 0x12002111; cpu->isar.id_isar2 = 0x11231111; @@ -1822,7 +1953,6 @@ static void arm1136_initfn(Object *obj) cpu->dtb_compatible = "arm,arm1136"; set_feature(&cpu->env, ARM_FEATURE_V6K); set_feature(&cpu->env, ARM_FEATURE_V6); - set_feature(&cpu->env, ARM_FEATURE_VFP); set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS); set_feature(&cpu->env, ARM_FEATURE_CACHE_DIRTY_REG); set_feature(&cpu->env, ARM_FEATURE_CACHE_BLOCK_OPS); @@ -1834,11 +1964,11 @@ static void arm1136_initfn(Object *obj) cpu->reset_sctlr = 0x00050078; cpu->id_pfr0 = 0x111; cpu->id_pfr1 = 0x1; - cpu->id_dfr0 = 0x2; + cpu->isar.id_dfr0 = 0x2; cpu->id_afr0 = 0x3; - cpu->id_mmfr0 = 0x01130003; - cpu->id_mmfr1 = 0x10030302; - cpu->id_mmfr2 = 0x01222110; + cpu->isar.id_mmfr0 = 0x01130003; + cpu->isar.id_mmfr1 = 0x10030302; + cpu->isar.id_mmfr2 = 0x01222110; cpu->isar.id_isar0 = 0x00140011; cpu->isar.id_isar1 = 0x12002111; cpu->isar.id_isar2 = 0x11231111; @@ -1853,7 +1983,6 @@ static void arm1176_initfn(Object *obj) cpu->dtb_compatible = "arm,arm1176"; set_feature(&cpu->env, ARM_FEATURE_V6K); - set_feature(&cpu->env, ARM_FEATURE_VFP); set_feature(&cpu->env, ARM_FEATURE_VAPA); set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS); set_feature(&cpu->env, ARM_FEATURE_CACHE_DIRTY_REG); @@ -1867,11 +1996,11 @@ static void arm1176_initfn(Object *obj) cpu->reset_sctlr = 0x00050078; cpu->id_pfr0 = 0x111; cpu->id_pfr1 = 0x11; - cpu->id_dfr0 = 0x33; + cpu->isar.id_dfr0 = 0x33; cpu->id_afr0 = 0; - cpu->id_mmfr0 = 0x01130003; - cpu->id_mmfr1 = 0x10030302; - cpu->id_mmfr2 = 0x01222100; + cpu->isar.id_mmfr0 = 0x01130003; + cpu->isar.id_mmfr1 = 0x10030302; + cpu->isar.id_mmfr2 = 0x01222100; cpu->isar.id_isar0 = 0x0140011; cpu->isar.id_isar1 = 0x12002111; cpu->isar.id_isar2 = 0x11231121; @@ -1886,7 +2015,6 @@ static void arm11mpcore_initfn(Object *obj) cpu->dtb_compatible = "arm,arm11mpcore"; set_feature(&cpu->env, ARM_FEATURE_V6K); - set_feature(&cpu->env, ARM_FEATURE_VFP); set_feature(&cpu->env, ARM_FEATURE_VAPA); set_feature(&cpu->env, ARM_FEATURE_MPIDR); set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS); @@ -1897,11 +2025,11 @@ static void arm11mpcore_initfn(Object *obj) cpu->ctr = 0x1d192992; /* 32K icache 32K dcache */ cpu->id_pfr0 = 0x111; cpu->id_pfr1 = 0x1; - cpu->id_dfr0 = 0; + cpu->isar.id_dfr0 = 0; cpu->id_afr0 = 0x2; - cpu->id_mmfr0 = 0x01100103; - cpu->id_mmfr1 = 0x10020302; - cpu->id_mmfr2 = 0x01222000; + cpu->isar.id_mmfr0 = 0x01100103; + cpu->isar.id_mmfr1 = 0x10020302; + cpu->isar.id_mmfr2 = 0x01222000; cpu->isar.id_isar0 = 0x00100011; cpu->isar.id_isar1 = 0x12002111; cpu->isar.id_isar2 = 0x11221011; @@ -1929,12 +2057,12 @@ static void cortex_m3_initfn(Object *obj) cpu->pmsav7_dregion = 8; cpu->id_pfr0 = 0x00000030; cpu->id_pfr1 = 0x00000200; - cpu->id_dfr0 = 0x00100000; + cpu->isar.id_dfr0 = 0x00100000; cpu->id_afr0 = 0x00000000; - cpu->id_mmfr0 = 0x00000030; - cpu->id_mmfr1 = 0x00000000; - cpu->id_mmfr2 = 0x00000000; - cpu->id_mmfr3 = 0x00000000; + cpu->isar.id_mmfr0 = 0x00000030; + cpu->isar.id_mmfr1 = 0x00000000; + cpu->isar.id_mmfr2 = 0x00000000; + cpu->isar.id_mmfr3 = 0x00000000; cpu->isar.id_isar0 = 0x01141110; cpu->isar.id_isar1 = 0x02111000; cpu->isar.id_isar2 = 0x21112231; @@ -1952,7 +2080,6 @@ static void cortex_m4_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_M); set_feature(&cpu->env, ARM_FEATURE_M_MAIN); set_feature(&cpu->env, ARM_FEATURE_THUMB_DSP); - set_feature(&cpu->env, ARM_FEATURE_VFP4); cpu->midr = 0x410fc240; /* r0p0 */ cpu->pmsav7_dregion = 8; cpu->isar.mvfr0 = 0x10110021; @@ -1960,12 +2087,12 @@ static void cortex_m4_initfn(Object *obj) cpu->isar.mvfr2 = 0x00000000; cpu->id_pfr0 = 0x00000030; cpu->id_pfr1 = 0x00000200; - cpu->id_dfr0 = 0x00100000; + cpu->isar.id_dfr0 = 0x00100000; cpu->id_afr0 = 0x00000000; - cpu->id_mmfr0 = 0x00000030; - cpu->id_mmfr1 = 0x00000000; - cpu->id_mmfr2 = 0x00000000; - cpu->id_mmfr3 = 0x00000000; + cpu->isar.id_mmfr0 = 0x00000030; + cpu->isar.id_mmfr1 = 0x00000000; + cpu->isar.id_mmfr2 = 0x00000000; + cpu->isar.id_mmfr3 = 0x00000000; cpu->isar.id_isar0 = 0x01141110; cpu->isar.id_isar1 = 0x02111000; cpu->isar.id_isar2 = 0x21112231; @@ -1975,6 +2102,36 @@ static void cortex_m4_initfn(Object *obj) cpu->isar.id_isar6 = 0x00000000; } +static void cortex_m7_initfn(Object *obj) +{ + ARMCPU *cpu = ARM_CPU(obj); + + set_feature(&cpu->env, ARM_FEATURE_V7); + set_feature(&cpu->env, ARM_FEATURE_M); + set_feature(&cpu->env, ARM_FEATURE_M_MAIN); + set_feature(&cpu->env, ARM_FEATURE_THUMB_DSP); + cpu->midr = 0x411fc272; /* r1p2 */ + cpu->pmsav7_dregion = 8; + cpu->isar.mvfr0 = 0x10110221; + cpu->isar.mvfr1 = 0x12000011; + cpu->isar.mvfr2 = 0x00000040; + cpu->id_pfr0 = 0x00000030; + cpu->id_pfr1 = 0x00000200; + cpu->isar.id_dfr0 = 0x00100000; + cpu->id_afr0 = 0x00000000; + cpu->isar.id_mmfr0 = 0x00100030; + cpu->isar.id_mmfr1 = 0x00000000; + cpu->isar.id_mmfr2 = 0x01000000; + cpu->isar.id_mmfr3 = 0x00000000; + cpu->isar.id_isar0 = 0x01101110; + cpu->isar.id_isar1 = 0x02112000; + cpu->isar.id_isar2 = 0x20232231; + cpu->isar.id_isar3 = 0x01111131; + cpu->isar.id_isar4 = 0x01310132; + cpu->isar.id_isar5 = 0x00000000; + cpu->isar.id_isar6 = 0x00000000; +} + static void cortex_m33_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); @@ -1984,7 +2141,6 @@ static void cortex_m33_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_M_MAIN); set_feature(&cpu->env, ARM_FEATURE_M_SECURITY); set_feature(&cpu->env, ARM_FEATURE_THUMB_DSP); - set_feature(&cpu->env, ARM_FEATURE_VFP4); cpu->midr = 0x410fd213; /* r0p3 */ cpu->pmsav7_dregion = 16; cpu->sau_sregion = 8; @@ -1993,12 +2149,12 @@ static void cortex_m33_initfn(Object *obj) cpu->isar.mvfr2 = 0x00000040; cpu->id_pfr0 = 0x00000030; cpu->id_pfr1 = 0x00000210; - cpu->id_dfr0 = 0x00200000; + cpu->isar.id_dfr0 = 0x00200000; cpu->id_afr0 = 0x00000000; - cpu->id_mmfr0 = 0x00101F40; - cpu->id_mmfr1 = 0x00000000; - cpu->id_mmfr2 = 0x01000000; - cpu->id_mmfr3 = 0x00000000; + cpu->isar.id_mmfr0 = 0x00101F40; + cpu->isar.id_mmfr1 = 0x00000000; + cpu->isar.id_mmfr2 = 0x01000000; + cpu->isar.id_mmfr3 = 0x00000000; cpu->isar.id_isar0 = 0x01101110; cpu->isar.id_isar1 = 0x02212000; cpu->isar.id_isar2 = 0x20232232; @@ -2041,15 +2197,16 @@ static void cortex_r5_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_V7); set_feature(&cpu->env, ARM_FEATURE_V7MP); set_feature(&cpu->env, ARM_FEATURE_PMSA); + set_feature(&cpu->env, ARM_FEATURE_PMU); cpu->midr = 0x411fc153; /* r1p3 */ cpu->id_pfr0 = 0x0131; cpu->id_pfr1 = 0x001; - cpu->id_dfr0 = 0x010400; + cpu->isar.id_dfr0 = 0x010400; cpu->id_afr0 = 0x0; - cpu->id_mmfr0 = 0x0210030; - cpu->id_mmfr1 = 0x00000000; - cpu->id_mmfr2 = 0x01200000; - cpu->id_mmfr3 = 0x0211; + cpu->isar.id_mmfr0 = 0x0210030; + cpu->isar.id_mmfr1 = 0x00000000; + cpu->isar.id_mmfr2 = 0x01200000; + cpu->isar.id_mmfr3 = 0x0211; cpu->isar.id_isar0 = 0x02101111; cpu->isar.id_isar1 = 0x13112111; cpu->isar.id_isar2 = 0x21232141; @@ -2067,7 +2224,6 @@ static void cortex_r5f_initfn(Object *obj) ARMCPU *cpu = ARM_CPU(obj); cortex_r5_initfn(obj); - set_feature(&cpu->env, ARM_FEATURE_VFP3); cpu->isar.mvfr0 = 0x10110221; cpu->isar.mvfr1 = 0x00000011; } @@ -2086,7 +2242,6 @@ static void cortex_a8_initfn(Object *obj) cpu->dtb_compatible = "arm,cortex-a8"; set_feature(&cpu->env, ARM_FEATURE_V7); - set_feature(&cpu->env, ARM_FEATURE_VFP3); set_feature(&cpu->env, ARM_FEATURE_NEON); set_feature(&cpu->env, ARM_FEATURE_THUMB2EE); set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS); @@ -2099,18 +2254,18 @@ static void cortex_a8_initfn(Object *obj) cpu->reset_sctlr = 0x00c50078; cpu->id_pfr0 = 0x1031; cpu->id_pfr1 = 0x11; - cpu->id_dfr0 = 0x400; + cpu->isar.id_dfr0 = 0x400; cpu->id_afr0 = 0; - cpu->id_mmfr0 = 0x31100003; - cpu->id_mmfr1 = 0x20000000; - cpu->id_mmfr2 = 0x01202000; - cpu->id_mmfr3 = 0x11; + cpu->isar.id_mmfr0 = 0x31100003; + cpu->isar.id_mmfr1 = 0x20000000; + cpu->isar.id_mmfr2 = 0x01202000; + cpu->isar.id_mmfr3 = 0x11; cpu->isar.id_isar0 = 0x00101111; cpu->isar.id_isar1 = 0x12112111; cpu->isar.id_isar2 = 0x21232031; cpu->isar.id_isar3 = 0x11112131; cpu->isar.id_isar4 = 0x00111142; - cpu->dbgdidr = 0x15141000; + cpu->isar.dbgdidr = 0x15141000; cpu->clidr = (1 << 27) | (2 << 24) | 3; cpu->ccsidr[0] = 0xe007e01a; /* 16k L1 dcache. */ cpu->ccsidr[1] = 0x2007e01a; /* 16k L1 icache. */ @@ -2154,7 +2309,6 @@ static void cortex_a9_initfn(Object *obj) cpu->dtb_compatible = "arm,cortex-a9"; set_feature(&cpu->env, ARM_FEATURE_V7); - set_feature(&cpu->env, ARM_FEATURE_VFP3); set_feature(&cpu->env, ARM_FEATURE_NEON); set_feature(&cpu->env, ARM_FEATURE_THUMB2EE); set_feature(&cpu->env, ARM_FEATURE_EL3); @@ -2172,18 +2326,18 @@ static void cortex_a9_initfn(Object *obj) cpu->reset_sctlr = 0x00c50078; cpu->id_pfr0 = 0x1031; cpu->id_pfr1 = 0x11; - cpu->id_dfr0 = 0x000; + cpu->isar.id_dfr0 = 0x000; cpu->id_afr0 = 0; - cpu->id_mmfr0 = 0x00100103; - cpu->id_mmfr1 = 0x20000000; - cpu->id_mmfr2 = 0x01230000; - cpu->id_mmfr3 = 0x00002111; + cpu->isar.id_mmfr0 = 0x00100103; + cpu->isar.id_mmfr1 = 0x20000000; + cpu->isar.id_mmfr2 = 0x01230000; + cpu->isar.id_mmfr3 = 0x00002111; cpu->isar.id_isar0 = 0x00101111; cpu->isar.id_isar1 = 0x13112111; cpu->isar.id_isar2 = 0x21232041; cpu->isar.id_isar3 = 0x11112131; cpu->isar.id_isar4 = 0x00111142; - cpu->dbgdidr = 0x35141000; + cpu->isar.dbgdidr = 0x35141000; cpu->clidr = (1 << 27) | (1 << 24) | 3; cpu->ccsidr[0] = 0xe00fe019; /* 16k L1 dcache. */ cpu->ccsidr[1] = 0x200fe019; /* 16k L1 icache. */ @@ -2219,7 +2373,6 @@ static void cortex_a7_initfn(Object *obj) cpu->dtb_compatible = "arm,cortex-a7"; set_feature(&cpu->env, ARM_FEATURE_V7VE); - set_feature(&cpu->env, ARM_FEATURE_VFP4); set_feature(&cpu->env, ARM_FEATURE_NEON); set_feature(&cpu->env, ARM_FEATURE_THUMB2EE); set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); @@ -2237,12 +2390,12 @@ static void cortex_a7_initfn(Object *obj) cpu->reset_sctlr = 0x00c50078; cpu->id_pfr0 = 0x00001131; cpu->id_pfr1 = 0x00011011; - cpu->id_dfr0 = 0x02010555; + cpu->isar.id_dfr0 = 0x02010555; cpu->id_afr0 = 0x00000000; - cpu->id_mmfr0 = 0x10101105; - cpu->id_mmfr1 = 0x40000000; - cpu->id_mmfr2 = 0x01240000; - cpu->id_mmfr3 = 0x02102211; + cpu->isar.id_mmfr0 = 0x10101105; + cpu->isar.id_mmfr1 = 0x40000000; + cpu->isar.id_mmfr2 = 0x01240000; + cpu->isar.id_mmfr3 = 0x02102211; /* a7_mpcore_r0p5_trm, page 4-4 gives 0x01101110; but * table 4-41 gives 0x02101110, which includes the arm div insns. */ @@ -2251,7 +2404,7 @@ static void cortex_a7_initfn(Object *obj) cpu->isar.id_isar2 = 0x21232041; cpu->isar.id_isar3 = 0x11112131; cpu->isar.id_isar4 = 0x10011142; - cpu->dbgdidr = 0x3515f005; + cpu->isar.dbgdidr = 0x3515f005; cpu->clidr = 0x0a200023; cpu->ccsidr[0] = 0x701fe00a; /* 32K L1 dcache */ cpu->ccsidr[1] = 0x201fe00a; /* 32K L1 icache */ @@ -2265,7 +2418,6 @@ static void cortex_a15_initfn(Object *obj) cpu->dtb_compatible = "arm,cortex-a15"; set_feature(&cpu->env, ARM_FEATURE_V7VE); - set_feature(&cpu->env, ARM_FEATURE_VFP4); set_feature(&cpu->env, ARM_FEATURE_NEON); set_feature(&cpu->env, ARM_FEATURE_THUMB2EE); set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); @@ -2283,18 +2435,18 @@ static void cortex_a15_initfn(Object *obj) cpu->reset_sctlr = 0x00c50078; cpu->id_pfr0 = 0x00001131; cpu->id_pfr1 = 0x00011011; - cpu->id_dfr0 = 0x02010555; + cpu->isar.id_dfr0 = 0x02010555; cpu->id_afr0 = 0x00000000; - cpu->id_mmfr0 = 0x10201105; - cpu->id_mmfr1 = 0x20000000; - cpu->id_mmfr2 = 0x01240000; - cpu->id_mmfr3 = 0x02102211; + cpu->isar.id_mmfr0 = 0x10201105; + cpu->isar.id_mmfr1 = 0x20000000; + cpu->isar.id_mmfr2 = 0x01240000; + cpu->isar.id_mmfr3 = 0x02102211; cpu->isar.id_isar0 = 0x02101110; cpu->isar.id_isar1 = 0x13112111; cpu->isar.id_isar2 = 0x21232041; cpu->isar.id_isar3 = 0x11112131; cpu->isar.id_isar4 = 0x10011142; - cpu->dbgdidr = 0x3515f021; + cpu->isar.dbgdidr = 0x3515f021; cpu->clidr = 0x0a200023; cpu->ccsidr[0] = 0x701fe00a; /* 32K L1 dcache */ cpu->ccsidr[1] = 0x201fe00a; /* 32K L1 icache */ @@ -2482,6 +2634,7 @@ static void arm_max_initfn(Object *obj) if (kvm_enabled()) { kvm_arm_set_cpu_features_from_host(cpu); + kvm_arm_add_vcpu_properties(obj); } else { cortex_a15_initfn(obj); @@ -2523,9 +2676,15 @@ static void arm_max_initfn(Object *obj) t = FIELD_DP32(t, MVFR2, FPMISC, 4); /* FP MaxNum */ cpu->isar.mvfr2 = t; - t = cpu->id_mmfr4; + t = cpu->isar.id_mmfr3; + t = FIELD_DP32(t, ID_MMFR3, PAN, 2); /* ATS1E1 */ + cpu->isar.id_mmfr3 = t; + + t = cpu->isar.id_mmfr4; t = FIELD_DP32(t, ID_MMFR4, HPDS, 1); /* AA32HPD */ - cpu->id_mmfr4 = t; + t = FIELD_DP32(t, ID_MMFR4, AC2, 1); /* ACTLR2, HACTLR2 */ + t = FIELD_DP32(t, ID_MMFR4, CNP, 1); /* TTCNP */ + cpu->isar.id_mmfr4 = t; } #endif } @@ -2559,6 +2718,8 @@ static const ARMCPUInfo arm_cpus[] = { .class_init = arm_v7m_class_init }, { .name = "cortex-m4", .initfn = cortex_m4_initfn, .class_init = arm_v7m_class_init }, + { .name = "cortex-m7", .initfn = cortex_m7_initfn, + .class_init = arm_v7m_class_init }, { .name = "cortex-m33", .initfn = cortex_m33_initfn, .class_init = arm_v7m_class_init }, { .name = "cortex-r5", .initfn = cortex_r5_initfn }, @@ -2623,10 +2784,9 @@ static void arm_cpu_class_init(ObjectClass *oc, void *data) device_class_set_parent_realize(dc, arm_cpu_realizefn, &acc->parent_realize); - dc->props = arm_cpu_properties; - acc->parent_reset = cc->reset; - cc->reset = arm_cpu_reset; + device_class_set_props(dc, arm_cpu_properties); + device_class_set_parent_reset(dc, arm_cpu_reset, &acc->parent_reset); cc->class_by_name = arm_cpu_class_by_name; cc->has_work = arm_cpu_has_work; @@ -2673,6 +2833,7 @@ static void arm_host_initfn(Object *obj) if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) { aarch64_add_sve_properties(obj); } + kvm_arm_add_vcpu_properties(obj); arm_cpu_post_init(obj); } diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 83a809d4ba..8b9f2961ba 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -128,14 +128,20 @@ enum { /** * DynamicGDBXMLInfo: * @desc: Contains the XML descriptions. - * @num_cpregs: Number of the Coprocessor registers seen by GDB. - * @cpregs_keys: Array that contains the corresponding Key of - * a given cpreg with the same order of the cpreg in the XML description. + * @num: Number of the registers in this XML seen by GDB. + * @data: A union with data specific to the set of registers + * @cpregs_keys: Array that contains the corresponding Key of + * a given cpreg with the same order of the cpreg + * in the XML description. */ typedef struct DynamicGDBXMLInfo { char *desc; - int num_cpregs; - uint32_t *cpregs_keys; + int num; + union { + struct { + uint32_t *keys; + } cpregs; + } data; } DynamicGDBXMLInfo; /* CPU state for each instance of a generic timer (in cp15 c14) */ @@ -144,11 +150,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; @@ -748,7 +755,8 @@ struct ARMCPU { uint64_t *cpreg_vmstate_values; int32_t cpreg_vmstate_array_len; - DynamicGDBXMLInfo dyn_xml; + DynamicGDBXMLInfo dyn_sysreg_xml; + DynamicGDBXMLInfo dyn_svereg_xml; /* Timers used by the generic (architected) timer */ QEMUTimer *gt_timer[NUM_GTIMERS]; @@ -821,6 +829,13 @@ struct ARMCPU { /* KVM init features for this CPU */ uint32_t kvm_init_features[7]; + /* KVM CPU state */ + + /* KVM virtual time adjustment */ + bool kvm_adjvtime; + bool kvm_vtime_dirty; + uint64_t kvm_vtime; + /* Uniprocessor system with MP extensions */ bool mp_is_up; @@ -845,6 +860,11 @@ struct ARMCPU { * prefix means a constant register. * Some of these registers are split out into a substructure that * is shared with the translators to control the ISA. + * + * Note that if you add an ID register to the ARMISARegisters struct + * you need to also update the 32-bit and 64-bit versions of the + * kvm_arm_get_host_cpu_features() function to correctly populate the + * field by reading the value from the KVM vCPU. */ struct ARMISARegisters { uint32_t id_isar0; @@ -854,15 +874,25 @@ struct ARMCPU { uint32_t id_isar4; uint32_t id_isar5; uint32_t id_isar6; + uint32_t id_mmfr0; + uint32_t id_mmfr1; + uint32_t id_mmfr2; + uint32_t id_mmfr3; + uint32_t id_mmfr4; uint32_t mvfr0; uint32_t mvfr1; uint32_t mvfr2; + uint32_t id_dfr0; + uint32_t dbgdidr; uint64_t id_aa64isar0; uint64_t id_aa64isar1; uint64_t id_aa64pfr0; uint64_t id_aa64pfr1; uint64_t id_aa64mmfr0; uint64_t id_aa64mmfr1; + uint64_t id_aa64mmfr2; + uint64_t id_aa64dfr0; + uint64_t id_aa64dfr1; } isar; uint32_t midr; uint32_t revidr; @@ -871,26 +901,17 @@ struct ARMCPU { uint32_t reset_sctlr; uint32_t id_pfr0; uint32_t id_pfr1; - uint32_t id_dfr0; uint64_t pmceid0; uint64_t pmceid1; uint32_t id_afr0; - uint32_t id_mmfr0; - uint32_t id_mmfr1; - uint32_t id_mmfr2; - uint32_t id_mmfr3; - uint32_t id_mmfr4; - uint64_t id_aa64dfr0; - uint64_t id_aa64dfr1; uint64_t id_aa64afr0; uint64_t id_aa64afr1; - uint32_t dbgdidr; uint32_t clidr; uint64_t mp_affinity; /* MP ID without feature bits */ /* The elements of this array are the CCSIDR values for each cache, * in the order L1DCache, L1ICache, L2DCache, L2ICache, etc. */ - uint32_t ccsidr[16]; + uint64_t ccsidr[16]; uint64_t reset_cbar; uint32_t reset_auxcr; bool reset_hivecs; @@ -932,8 +953,13 @@ struct ARMCPU { */ DECLARE_BITMAP(sve_vq_map, ARM_MAX_VQ); DECLARE_BITMAP(sve_vq_init, ARM_MAX_VQ); + + /* Generic timer counter frequency, in Hz */ + uint64_t gt_cntfrq_hz; }; +unsigned int gt_cntfrq_period_ns(ARMCPU *cpu); + void arm_cpu_post_init(Object *obj); uint64_t arm_cpu_mp_affinity(int idx, uint8_t clustersz); @@ -949,13 +975,15 @@ bool arm_cpu_exec_interrupt(CPUState *cpu, int int_req); hwaddr arm_cpu_get_phys_page_attrs_debug(CPUState *cpu, vaddr addr, MemTxAttrs *attrs); -int arm_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); +int arm_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int arm_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); -/* Dynamically generates for gdb stub an XML description of the sysregs from - * the cp_regs hashtable. Returns the registered sysregs number. +/* + * Helpers to dynamically generates XML descriptions of the sysregs + * and SVE registers. Returns the number of registers in each set. */ -int arm_gen_dynamic_xml(CPUState *cpu); +int arm_gen_dynamic_sysreg_xml(CPUState *cpu, int base_reg); +int arm_gen_dynamic_svereg_xml(CPUState *cpu, int base_reg); /* Returns the dynamically generated XML for the gdb stub. * Returns a pointer to the XML contents for the specified XML file or NULL @@ -969,12 +997,37 @@ int arm_cpu_write_elf32_note(WriteCoreDumpFunction f, CPUState *cs, int cpuid, void *opaque); #ifdef TARGET_AARCH64 -int aarch64_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); +int aarch64_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int aarch64_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); void aarch64_sve_narrow_vq(CPUARMState *env, unsigned vq); void aarch64_sve_change_el(CPUARMState *env, int old_el, int new_el, bool el0_a64); void aarch64_add_sve_properties(Object *obj); + +/* + * SVE registers are encoded in KVM's memory in an endianness-invariant format. + * The byte at offset i from the start of the in-memory representation contains + * the bits [(7 + 8 * i) : (8 * i)] of the register value. As this means the + * lowest offsets are stored in the lowest memory addresses, then that nearly + * matches QEMU's representation, which is to use an array of host-endian + * uint64_t's, where the lower offsets are at the lower indices. To complete + * the translation we just need to byte swap the uint64_t's on big-endian hosts. + */ +static inline uint64_t *sve_bswap64(uint64_t *dst, uint64_t *src, int nr) +{ +#ifdef HOST_WORDS_BIGENDIAN + int i; + + for (i = 0; i < nr; ++i) { + dst[i] = bswap64(src[i]); + } + + return dst; +#else + return src; +#endif +} + #else static inline void aarch64_sve_narrow_vq(CPUARMState *env, unsigned vq) { } static inline void aarch64_sve_change_el(CPUARMState *env, int o, @@ -1148,12 +1201,7 @@ void pmu_init(ARMCPU *cpu); #define CPSR_IT_2_7 (0xfc00U) #define CPSR_GE (0xfU << 16) #define CPSR_IL (1U << 20) -/* Note that the RESERVED bits include bit 21, which is PSTATE_SS in - * an AArch64 SPSR but RES0 in AArch32 SPSR and CPSR. In QEMU we use - * env->uncached_cpsr bit 21 to store PSTATE.SS when executing in AArch32, - * where it is live state but not accessible to the AArch32 code. - */ -#define CPSR_RESERVED (0x7U << 21) +#define CPSR_PAN (1U << 22) #define CPSR_J (1U << 24) #define CPSR_IT_0_1 (3U << 25) #define CPSR_Q (1U << 27) @@ -1171,8 +1219,6 @@ void pmu_init(ARMCPU *cpu); #define CPSR_USER (CPSR_NZCV | CPSR_Q | CPSR_GE) /* Execution state bits. MRS read as zero, MSR writes ignored. */ #define CPSR_EXEC (CPSR_T | CPSR_IT | CPSR_J | CPSR_IL) -/* Mask of bits which may be set by exception return copying them from SPSR */ -#define CPSR_ERET_MASK (~CPSR_RESERVED) /* Bit definitions for M profile XPSR. Most are the same as CPSR. */ #define XPSR_EXCP 0x1ffU @@ -1220,6 +1266,8 @@ void pmu_init(ARMCPU *cpu); #define PSTATE_BTYPE (3U << 10) #define PSTATE_IL (1U << 20) #define PSTATE_SS (1U << 21) +#define PSTATE_PAN (1U << 22) +#define PSTATE_UAO (1U << 23) #define PSTATE_V (1U << 28) #define PSTATE_C (1U << 29) #define PSTATE_Z (1U << 30) @@ -1371,6 +1419,7 @@ static inline void xpsr_write(CPUARMState *env, uint32_t val, uint32_t mask) #define HCR_TERR (1ULL << 36) #define HCR_TEA (1ULL << 37) #define HCR_MIOCNCE (1ULL << 38) +/* RES0 bit 39 */ #define HCR_APK (1ULL << 40) #define HCR_API (1ULL << 41) #define HCR_NV (1ULL << 42) @@ -1379,20 +1428,19 @@ static inline void xpsr_write(CPUARMState *env, uint32_t val, uint32_t mask) #define HCR_NV2 (1ULL << 45) #define HCR_FWB (1ULL << 46) #define HCR_FIEN (1ULL << 47) +/* RES0 bit 48 */ #define HCR_TID4 (1ULL << 49) #define HCR_TICAB (1ULL << 50) +#define HCR_AMVOFFEN (1ULL << 51) #define HCR_TOCU (1ULL << 52) +#define HCR_ENSCXT (1ULL << 53) #define HCR_TTLBIS (1ULL << 54) #define HCR_TTLBOS (1ULL << 55) #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 HCR_TID5 (1ULL << 58) +#define HCR_TWEDEN (1ULL << 59) +#define HCR_TWEDEL MAKE_64BIT_MASK(60, 4) #define SCR_NS (1U << 0) #define SCR_IRQ (1U << 1) @@ -1696,6 +1744,15 @@ FIELD(ID_ISAR6, FHM, 8, 4) FIELD(ID_ISAR6, SB, 12, 4) FIELD(ID_ISAR6, SPECRES, 16, 4) +FIELD(ID_MMFR3, CMAINTVA, 0, 4) +FIELD(ID_MMFR3, CMAINTSW, 4, 4) +FIELD(ID_MMFR3, BPMAINT, 8, 4) +FIELD(ID_MMFR3, MAINTBCST, 12, 4) +FIELD(ID_MMFR3, PAN, 16, 4) +FIELD(ID_MMFR3, COHWALK, 20, 4) +FIELD(ID_MMFR3, CMEMSZ, 24, 4) +FIELD(ID_MMFR3, SUPERSEC, 28, 4) + FIELD(ID_MMFR4, SPECSEI, 0, 4) FIELD(ID_MMFR4, AC2, 4, 4) FIELD(ID_MMFR4, XNX, 8, 4) @@ -1769,6 +1826,32 @@ FIELD(ID_AA64MMFR1, PAN, 20, 4) FIELD(ID_AA64MMFR1, SPECSEI, 24, 4) FIELD(ID_AA64MMFR1, XNX, 28, 4) +FIELD(ID_AA64MMFR2, CNP, 0, 4) +FIELD(ID_AA64MMFR2, UAO, 4, 4) +FIELD(ID_AA64MMFR2, LSM, 8, 4) +FIELD(ID_AA64MMFR2, IESB, 12, 4) +FIELD(ID_AA64MMFR2, VARANGE, 16, 4) +FIELD(ID_AA64MMFR2, CCIDX, 20, 4) +FIELD(ID_AA64MMFR2, NV, 24, 4) +FIELD(ID_AA64MMFR2, ST, 28, 4) +FIELD(ID_AA64MMFR2, AT, 32, 4) +FIELD(ID_AA64MMFR2, IDS, 36, 4) +FIELD(ID_AA64MMFR2, FWB, 40, 4) +FIELD(ID_AA64MMFR2, TTL, 48, 4) +FIELD(ID_AA64MMFR2, BBM, 52, 4) +FIELD(ID_AA64MMFR2, EVT, 56, 4) +FIELD(ID_AA64MMFR2, E0PD, 60, 4) + +FIELD(ID_AA64DFR0, DEBUGVER, 0, 4) +FIELD(ID_AA64DFR0, TRACEVER, 4, 4) +FIELD(ID_AA64DFR0, PMUVER, 8, 4) +FIELD(ID_AA64DFR0, BRPS, 12, 4) +FIELD(ID_AA64DFR0, WRPS, 20, 4) +FIELD(ID_AA64DFR0, CTX_CMPS, 28, 4) +FIELD(ID_AA64DFR0, PMSVER, 32, 4) +FIELD(ID_AA64DFR0, DOUBLELOCK, 36, 4) +FIELD(ID_AA64DFR0, TRACEFILT, 40, 4) + FIELD(ID_DFR0, COPDBG, 0, 4) FIELD(ID_DFR0, COPSDBG, 4, 4) FIELD(ID_DFR0, MMAPDBG, 8, 4) @@ -1778,6 +1861,13 @@ FIELD(ID_DFR0, MPROFDBG, 20, 4) FIELD(ID_DFR0, PERFMON, 24, 4) FIELD(ID_DFR0, TRACEFILT, 28, 4) +FIELD(DBGDIDR, SE_IMP, 12, 1) +FIELD(DBGDIDR, NSUHD_IMP, 14, 1) +FIELD(DBGDIDR, VERSION, 16, 4) +FIELD(DBGDIDR, CTX_CMPS, 20, 4) +FIELD(DBGDIDR, BRPS, 24, 4) +FIELD(DBGDIDR, WRPS, 28, 4) + FIELD(MVFR0, SIMDREG, 0, 4) FIELD(MVFR0, FPSP, 4, 4) FIELD(MVFR0, FPDP, 8, 4) @@ -1806,7 +1896,6 @@ QEMU_BUILD_BUG_ON(ARRAY_SIZE(((ARMCPU *)0)->ccsidr) <= R_V7M_CSSELR_INDEX_MASK); * mapping in linux-user/elfload.c:get_elf_hwcap(). */ enum arm_features { - ARM_FEATURE_VFP, ARM_FEATURE_AUXCR, /* ARM1026 Auxiliary control register. */ ARM_FEATURE_XSCALE, /* Intel XScale extensions. */ ARM_FEATURE_IWMMXT, /* Intel iwMMXt extension. */ @@ -1815,7 +1904,6 @@ enum arm_features { ARM_FEATURE_V7, ARM_FEATURE_THUMB2, ARM_FEATURE_PMSA, /* no MMU; may have Memory Protection Unit */ - ARM_FEATURE_VFP3, ARM_FEATURE_NEON, ARM_FEATURE_M, /* Microcontroller profile. */ ARM_FEATURE_OMAPCP, /* OMAP specific CP15 ops handling. */ @@ -1826,7 +1914,6 @@ enum arm_features { ARM_FEATURE_V5, ARM_FEATURE_STRONGARM, ARM_FEATURE_VAPA, /* cp15 VA to PA lookups */ - ARM_FEATURE_VFP4, /* VFPv4 (implies that NEON is v2) */ ARM_FEATURE_GENERIC_TIMER, ARM_FEATURE_MVFR, /* Media and VFP Feature Registers 0 and 1 */ ARM_FEATURE_DUMMY_C15_REGS, /* RAZ/WI all of cp15 crn=15 */ @@ -2238,6 +2325,9 @@ static inline uint64_t cpreg_to_kvm_id(uint32_t cpregid) * RAISES_EXC is for when the read or write hook might raise an exception; * the generated code will synchronize the CPU state before calling the hook * so that it is safe for the hook to call raise_exception(). + * NEWEL is for writes to registers that might change the exception + * level - typically on older ARM chips. For those cases we need to + * re-read the new el when recomputing the translation flags. */ #define ARM_CP_SPECIAL 0x0001 #define ARM_CP_CONST 0x0002 @@ -2257,10 +2347,11 @@ static inline uint64_t cpreg_to_kvm_id(uint32_t cpregid) #define ARM_CP_SVE 0x2000 #define ARM_CP_NO_GDB 0x4000 #define ARM_CP_RAISES_EXC 0x8000 +#define ARM_CP_NEWEL 0x10000 /* Used only as a terminator for ARMCPRegInfo lists */ -#define ARM_CP_SENTINEL 0xffff +#define ARM_CP_SENTINEL 0xfffff /* Mask of only the flag bits in a type field */ -#define ARM_CP_FLAG_MASK 0xf0ff +#define ARM_CP_FLAG_MASK 0x1f0ff /* Valid values for ARMCPRegInfo state field, indicating which of * the AArch32 and AArch64 execution states this register is visible in. @@ -2541,6 +2632,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 @@ -2661,117 +2765,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 @@ -2785,18 +2778,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 @@ -2808,19 +2804,27 @@ 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. + * 6. we need separate EL1/EL2 mmu_idx for handling the Privileged Access + * Never (PAN) bit within PSTATE. * - * NS EL0 (aka NS PL0) stage 1+2 - * NS EL1 (aka NS PL1) stage 1+2 + * This gives us the following list of cases: + * + * NS EL0 EL1&0 stage 1+2 (aka NS PL0) + * NS EL1 EL1&0 stage 1+2 (aka NS PL1) + * NS EL1 EL1&0 stage 1+2 +PAN + * NS EL0 EL2&0 + * NS EL2 EL2&0 +PAN * NS EL2 (aka NS PL2) + * S EL0 EL1&0 (aka S PL0) + * S EL1 EL1&0 (not used if EL3 is 32 bit) + * S EL1 EL1&0 +PAN * 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 12 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 @@ -2858,117 +2862,96 @@ 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 + /* + * A-profile. + */ + ARMMMUIdx_E10_0 = 0 | ARM_MMU_IDX_A, + ARMMMUIdx_E20_0 = 1 | ARM_MMU_IDX_A, + + ARMMMUIdx_E10_1 = 2 | ARM_MMU_IDX_A, + ARMMMUIdx_E10_1_PAN = 3 | ARM_MMU_IDX_A, + + ARMMMUIdx_E2 = 4 | ARM_MMU_IDX_A, + ARMMMUIdx_E20_2 = 5 | ARM_MMU_IDX_A, + ARMMMUIdx_E20_2_PAN = 6 | ARM_MMU_IDX_A, + + ARMMMUIdx_SE10_0 = 7 | ARM_MMU_IDX_A, + ARMMMUIdx_SE10_1 = 8 | ARM_MMU_IDX_A, + ARMMMUIdx_SE10_1_PAN = 9 | ARM_MMU_IDX_A, + ARMMMUIdx_SE3 = 10 | ARM_MMU_IDX_A, + + ARMMMUIdx_Stage2 = 11 | ARM_MMU_IDX_A, + + /* + * 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_S1NSE0 = 0 | ARM_MMU_IDX_NOTLB, - ARMMMUIdx_S1NSE1 = 1 | ARM_MMU_IDX_NOTLB, + ARMMMUIdx_Stage1_E0 = 0 | ARM_MMU_IDX_NOTLB, + ARMMMUIdx_Stage1_E1 = 1 | ARM_MMU_IDX_NOTLB, + ARMMMUIdx_Stage1_E1_PAN = 2 | 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; -/* Bit macros for the core-mmu-index values for each index, +/* + * Bit macros for the core-mmu-index values for each index, * for use when calling tlb_flush_by_mmuidx() and friends. */ +#define TO_CORE_BIT(NAME) \ + ARMMMUIdxBit_##NAME = 1 << (ARMMMUIdx_##NAME & ARM_MMU_IDX_COREIDX_MASK) + 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, + TO_CORE_BIT(E10_0), + TO_CORE_BIT(E20_0), + TO_CORE_BIT(E10_1), + TO_CORE_BIT(E10_1_PAN), + TO_CORE_BIT(E2), + TO_CORE_BIT(E20_2), + TO_CORE_BIT(E20_2_PAN), + TO_CORE_BIT(SE10_0), + TO_CORE_BIT(SE10_1), + TO_CORE_BIT(SE10_1_PAN), + 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; +#undef TO_CORE_BIT + #define MMU_USER_IDX 0 -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; - } -} - -/* 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(); - } -} - -/* - * 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); - -/** - * cpu_mmu_index: - * @env: The cpu environment - * @ifetch: True for code access, false for data access. - * - * Return the core mmu index for the current translation regime. - * This function is used by generic TCG code paths. - */ -int cpu_mmu_index(CPUARMState *env, bool ifetch); - /* Indexes used when registering address spaces with cpu_address_space_init */ typedef enum ARMASIdx { ARMASIdx_NS = 0, @@ -3118,15 +3101,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) @@ -3180,53 +3155,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) -/* - * For A-profile only, target EL for debug exceptions. - * Note that this overlaps with the M-profile-only HANDLER and STACKCHECK bits. - */ -FIELD(TBFLAG_ANY, DEBUG_TARGET_EL, 21, 2) +FIELD(TBFLAG_ANY, FPEXC_EL, 22, 2) +/* For A-profile only, target EL for debug exceptions. */ +FIELD(TBFLAG_ANY, DEBUG_TARGET_EL, 20, 2) -/* 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, both A- and M-profile. + */ +FIELD(TBFLAG_AM32, CONDEXEC, 0, 8) /* Not cached. */ +FIELD(TBFLAG_AM32, THUMB, 8, 1) /* 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) -/* 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) +FIELD(TBFLAG_A32, NS, 17, 1) -/* Bit usage when in AArch64 state */ +/* + * 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) @@ -3234,6 +3229,20 @@ 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) + +/** + * cpu_mmu_index: + * @env: The cpu environment + * @ifetch: True for code access, false for data access. + * + * Return the core mmu index for the current translation regime. + * This function is used by generic TCG code paths. + */ +static inline int cpu_mmu_index(CPUARMState *env, bool ifetch) +{ + return FIELD_EX32(env->hflags, TBFLAG_ANY, MMUIDX); +} static inline bool bswap_code(bool sctlr_b) { @@ -3353,20 +3362,36 @@ static inline uint64_t *aa64_vfp_qreg(CPUARMState *env, unsigned regno) /* Shared between translate-sve.c and sve_helper.c. */ extern const uint64_t pred_esz_masks[4]; +/* + * Naming convention for isar_feature functions: + * Functions which test 32-bit ID registers should have _aa32_ in + * their name. Functions which test 64-bit ID registers should have + * _aa64_ in their name. These must only be used in code where we + * know for certain that the CPU has AArch32 or AArch64 respectively + * or where the correct answer for a CPU which doesn't implement that + * CPU state is "false" (eg when generating A32 or A64 code, if adding + * system registers that are specific to that CPU state, for "should + * we let this system register bit be set" tests where the 32-bit + * flavour of the register doesn't have the bit, and so on). + * Functions which simply ask "does this feature exist at all" have + * _any_ in their name, and always return the logical OR of the _aa64_ + * and the _aa32_ function. + */ + /* * 32-bit feature tests via id registers. */ -static inline bool isar_feature_thumb_div(const ARMISARegisters *id) +static inline bool isar_feature_aa32_thumb_div(const ARMISARegisters *id) { return FIELD_EX32(id->id_isar0, ID_ISAR0, DIVIDE) != 0; } -static inline bool isar_feature_arm_div(const ARMISARegisters *id) +static inline bool isar_feature_aa32_arm_div(const ARMISARegisters *id) { return FIELD_EX32(id->id_isar0, ID_ISAR0, DIVIDE) > 1; } -static inline bool isar_feature_jazelle(const ARMISARegisters *id) +static inline bool isar_feature_aa32_jazelle(const ARMISARegisters *id) { return FIELD_EX32(id->id_isar1, ID_ISAR1, JAZELLE) != 0; } @@ -3441,21 +3466,53 @@ static inline bool isar_feature_aa32_fp16_arith(const ARMISARegisters *id) return FIELD_EX64(id->id_aa64pfr0, ID_AA64PFR0, FP) == 1; } -static inline bool isar_feature_aa32_fp_d32(const ARMISARegisters *id) +static inline bool isar_feature_aa32_vfp_simd(const ARMISARegisters *id) +{ + /* + * Return true if either VFP or SIMD is implemented. + * In this case, a minimum of VFP w/ D0-D15. + */ + return FIELD_EX32(id->mvfr0, MVFR0, SIMDREG) > 0; +} + +static inline bool isar_feature_aa32_simd_r32(const ARMISARegisters *id) { /* Return true if D16-D31 are implemented */ - return FIELD_EX64(id->mvfr0, MVFR0, SIMDREG) >= 2; + return FIELD_EX32(id->mvfr0, MVFR0, SIMDREG) >= 2; } static inline bool isar_feature_aa32_fpshvec(const ARMISARegisters *id) { - return FIELD_EX64(id->mvfr0, MVFR0, FPSHVEC) > 0; + return FIELD_EX32(id->mvfr0, MVFR0, FPSHVEC) > 0; } -static inline bool isar_feature_aa32_fpdp(const ARMISARegisters *id) +static inline bool isar_feature_aa32_fpsp_v2(const ARMISARegisters *id) { - /* Return true if CPU supports double precision floating point */ - return FIELD_EX64(id->mvfr0, MVFR0, FPDP) > 0; + /* Return true if CPU supports single precision floating point, VFPv2 */ + return FIELD_EX32(id->mvfr0, MVFR0, FPSP) > 0; +} + +static inline bool isar_feature_aa32_fpsp_v3(const ARMISARegisters *id) +{ + /* Return true if CPU supports single precision floating point, VFPv3 */ + return FIELD_EX32(id->mvfr0, MVFR0, FPSP) >= 2; +} + +static inline bool isar_feature_aa32_fpdp_v2(const ARMISARegisters *id) +{ + /* Return true if CPU supports double precision floating point, VFPv2 */ + return FIELD_EX32(id->mvfr0, MVFR0, FPDP) > 0; +} + +static inline bool isar_feature_aa32_fpdp_v3(const ARMISARegisters *id) +{ + /* Return true if CPU supports double precision floating point, VFPv3 */ + return FIELD_EX32(id->mvfr0, MVFR0, FPDP) >= 2; +} + +static inline bool isar_feature_aa32_vfp(const ARMISARegisters *id) +{ + return isar_feature_aa32_fpsp_v2(id) || isar_feature_aa32_fpdp_v2(id); } /* @@ -3465,32 +3522,83 @@ static inline bool isar_feature_aa32_fpdp(const ARMISARegisters *id) */ static inline bool isar_feature_aa32_fp16_spconv(const ARMISARegisters *id) { - return FIELD_EX64(id->mvfr1, MVFR1, FPHP) > 0; + return FIELD_EX32(id->mvfr1, MVFR1, FPHP) > 0; } static inline bool isar_feature_aa32_fp16_dpconv(const ARMISARegisters *id) { - return FIELD_EX64(id->mvfr1, MVFR1, FPHP) > 1; + return FIELD_EX32(id->mvfr1, MVFR1, FPHP) > 1; +} + +/* + * Note that this ID register field covers both VFP and Neon FMAC, + * so should usually be tested in combination with some other + * check that confirms the presence of whichever of VFP or Neon is + * relevant, to avoid accidentally enabling a Neon feature on + * a VFP-no-Neon core or vice-versa. + */ +static inline bool isar_feature_aa32_simdfmac(const ARMISARegisters *id) +{ + return FIELD_EX32(id->mvfr1, MVFR1, SIMDFMAC) != 0; } static inline bool isar_feature_aa32_vsel(const ARMISARegisters *id) { - return FIELD_EX64(id->mvfr2, MVFR2, FPMISC) >= 1; + return FIELD_EX32(id->mvfr2, MVFR2, FPMISC) >= 1; } static inline bool isar_feature_aa32_vcvt_dr(const ARMISARegisters *id) { - return FIELD_EX64(id->mvfr2, MVFR2, FPMISC) >= 2; + return FIELD_EX32(id->mvfr2, MVFR2, FPMISC) >= 2; } static inline bool isar_feature_aa32_vrint(const ARMISARegisters *id) { - return FIELD_EX64(id->mvfr2, MVFR2, FPMISC) >= 3; + return FIELD_EX32(id->mvfr2, MVFR2, FPMISC) >= 3; } static inline bool isar_feature_aa32_vminmaxnm(const ARMISARegisters *id) { - return FIELD_EX64(id->mvfr2, MVFR2, FPMISC) >= 4; + return FIELD_EX32(id->mvfr2, MVFR2, FPMISC) >= 4; +} + +static inline bool isar_feature_aa32_pan(const ARMISARegisters *id) +{ + return FIELD_EX32(id->id_mmfr3, ID_MMFR3, PAN) != 0; +} + +static inline bool isar_feature_aa32_ats1e1(const ARMISARegisters *id) +{ + return FIELD_EX32(id->id_mmfr3, ID_MMFR3, PAN) >= 2; +} + +static inline bool isar_feature_aa32_pmu_8_1(const ARMISARegisters *id) +{ + /* 0xf means "non-standard IMPDEF PMU" */ + return FIELD_EX32(id->id_dfr0, ID_DFR0, PERFMON) >= 4 && + FIELD_EX32(id->id_dfr0, ID_DFR0, PERFMON) != 0xf; +} + +static inline bool isar_feature_aa32_pmu_8_4(const ARMISARegisters *id) +{ + /* 0xf means "non-standard IMPDEF PMU" */ + return FIELD_EX32(id->id_dfr0, ID_DFR0, PERFMON) >= 5 && + FIELD_EX32(id->id_dfr0, ID_DFR0, PERFMON) != 0xf; +} + +static inline bool isar_feature_aa32_hpd(const ARMISARegisters *id) +{ + return FIELD_EX32(id->id_mmfr4, ID_MMFR4, HPDS) != 0; +} + +static inline bool isar_feature_aa32_ac2(const ARMISARegisters *id) +{ + return FIELD_EX32(id->id_mmfr4, ID_MMFR4, AC2) != 0; +} + +static inline bool isar_feature_aa32_ccidx(const ARMISARegisters *id) +{ + return FIELD_EX32(id->id_mmfr4, ID_MMFR4, CCIDX) != 0; } /* @@ -3616,6 +3724,22 @@ static inline bool isar_feature_aa64_frint(const ARMISARegisters *id) return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, FRINTTS) != 0; } +static inline bool isar_feature_aa64_dcpop(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, DPB) != 0; +} + +static inline bool isar_feature_aa64_dcpodp(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, DPB) >= 2; +} + +static inline bool isar_feature_aa64_fp_simd(const ARMISARegisters *id) +{ + /* We always set the AdvSIMD and FP fields identically. */ + return FIELD_EX64(id->id_aa64pfr0, ID_AA64PFR0, FP) != 0xf; +} + static inline bool isar_feature_aa64_fp16(const ARMISARegisters *id) { /* We always set the AdvSIMD and FP fields identically wrt FP16. */ @@ -3632,16 +3756,91 @@ 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; } +static inline bool isar_feature_aa64_pan(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64mmfr1, ID_AA64MMFR1, PAN) != 0; +} + +static inline bool isar_feature_aa64_ats1e1(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64mmfr1, ID_AA64MMFR1, PAN) >= 2; +} + +static inline bool isar_feature_aa64_uao(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64mmfr2, ID_AA64MMFR2, UAO) != 0; +} + static inline bool isar_feature_aa64_bti(const ARMISARegisters *id) { return FIELD_EX64(id->id_aa64pfr1, ID_AA64PFR1, BT) != 0; } +static inline bool isar_feature_aa64_pmu_8_1(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64dfr0, ID_AA64DFR0, PMUVER) >= 4 && + FIELD_EX64(id->id_aa64dfr0, ID_AA64DFR0, PMUVER) != 0xf; +} + +static inline bool isar_feature_aa64_pmu_8_4(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64dfr0, ID_AA64DFR0, PMUVER) >= 5 && + FIELD_EX64(id->id_aa64dfr0, ID_AA64DFR0, PMUVER) != 0xf; +} + +static inline bool isar_feature_aa64_rcpc_8_3(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, LRCPC) != 0; +} + +static inline bool isar_feature_aa64_rcpc_8_4(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, LRCPC) >= 2; +} + +static inline bool isar_feature_aa64_ccidx(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64mmfr2, ID_AA64MMFR2, CCIDX) != 0; +} + +/* + * Feature tests for "does this exist in either 32-bit or 64-bit?" + */ +static inline bool isar_feature_any_fp16(const ARMISARegisters *id) +{ + return isar_feature_aa64_fp16(id) || isar_feature_aa32_fp16_arith(id); +} + +static inline bool isar_feature_any_predinv(const ARMISARegisters *id) +{ + return isar_feature_aa64_predinv(id) || isar_feature_aa32_predinv(id); +} + +static inline bool isar_feature_any_pmu_8_1(const ARMISARegisters *id) +{ + return isar_feature_aa64_pmu_8_1(id) || isar_feature_aa32_pmu_8_1(id); +} + +static inline bool isar_feature_any_pmu_8_4(const ARMISARegisters *id) +{ + return isar_feature_aa64_pmu_8_4(id) || isar_feature_aa32_pmu_8_4(id); +} + +static inline bool isar_feature_any_ccidx(const ARMISARegisters *id) +{ + return isar_feature_aa64_ccidx(id) || isar_feature_aa32_ccidx(id); +} + /* * Forward to the above feature tests given an ARMCPU pointer. */ diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c index a39d6fcea3..95d0c8c101 100644 --- a/target/arm/cpu64.c +++ b/target/arm/cpu64.c @@ -102,7 +102,6 @@ static void aarch64_a57_initfn(Object *obj) cpu->dtb_compatible = "arm,cortex-a57"; set_feature(&cpu->env, ARM_FEATURE_V8); - set_feature(&cpu->env, ARM_FEATURE_VFP4); set_feature(&cpu->env, ARM_FEATURE_NEON); set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); set_feature(&cpu->env, ARM_FEATURE_AARCH64); @@ -121,12 +120,12 @@ static void aarch64_a57_initfn(Object *obj) cpu->reset_sctlr = 0x00c50838; cpu->id_pfr0 = 0x00000131; cpu->id_pfr1 = 0x00011011; - cpu->id_dfr0 = 0x03010066; + cpu->isar.id_dfr0 = 0x03010066; cpu->id_afr0 = 0x00000000; - cpu->id_mmfr0 = 0x10101105; - cpu->id_mmfr1 = 0x40000000; - cpu->id_mmfr2 = 0x01260000; - cpu->id_mmfr3 = 0x02102211; + cpu->isar.id_mmfr0 = 0x10101105; + cpu->isar.id_mmfr1 = 0x40000000; + cpu->isar.id_mmfr2 = 0x01260000; + cpu->isar.id_mmfr3 = 0x02102211; cpu->isar.id_isar0 = 0x02101110; cpu->isar.id_isar1 = 0x13112111; cpu->isar.id_isar2 = 0x21232042; @@ -135,10 +134,10 @@ static void aarch64_a57_initfn(Object *obj) cpu->isar.id_isar5 = 0x00011121; cpu->isar.id_isar6 = 0; cpu->isar.id_aa64pfr0 = 0x00002222; - cpu->id_aa64dfr0 = 0x10305106; + cpu->isar.id_aa64dfr0 = 0x10305106; cpu->isar.id_aa64isar0 = 0x00011120; cpu->isar.id_aa64mmfr0 = 0x00001124; - cpu->dbgdidr = 0x3516d000; + cpu->isar.dbgdidr = 0x3516d000; cpu->clidr = 0x0a200023; cpu->ccsidr[0] = 0x701fe00a; /* 32KB L1 dcache */ cpu->ccsidr[1] = 0x201fe012; /* 48KB L1 icache */ @@ -156,7 +155,6 @@ static void aarch64_a53_initfn(Object *obj) cpu->dtb_compatible = "arm,cortex-a53"; set_feature(&cpu->env, ARM_FEATURE_V8); - set_feature(&cpu->env, ARM_FEATURE_VFP4); set_feature(&cpu->env, ARM_FEATURE_NEON); set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); set_feature(&cpu->env, ARM_FEATURE_AARCH64); @@ -175,12 +173,12 @@ static void aarch64_a53_initfn(Object *obj) cpu->reset_sctlr = 0x00c50838; cpu->id_pfr0 = 0x00000131; cpu->id_pfr1 = 0x00011011; - cpu->id_dfr0 = 0x03010066; + cpu->isar.id_dfr0 = 0x03010066; cpu->id_afr0 = 0x00000000; - cpu->id_mmfr0 = 0x10101105; - cpu->id_mmfr1 = 0x40000000; - cpu->id_mmfr2 = 0x01260000; - cpu->id_mmfr3 = 0x02102211; + cpu->isar.id_mmfr0 = 0x10101105; + cpu->isar.id_mmfr1 = 0x40000000; + cpu->isar.id_mmfr2 = 0x01260000; + cpu->isar.id_mmfr3 = 0x02102211; cpu->isar.id_isar0 = 0x02101110; cpu->isar.id_isar1 = 0x13112111; cpu->isar.id_isar2 = 0x21232042; @@ -189,10 +187,10 @@ static void aarch64_a53_initfn(Object *obj) cpu->isar.id_isar5 = 0x00011121; cpu->isar.id_isar6 = 0; cpu->isar.id_aa64pfr0 = 0x00002222; - cpu->id_aa64dfr0 = 0x10305106; + cpu->isar.id_aa64dfr0 = 0x10305106; cpu->isar.id_aa64isar0 = 0x00011120; cpu->isar.id_aa64mmfr0 = 0x00001122; /* 40 bit physical addr */ - cpu->dbgdidr = 0x3516d000; + cpu->isar.dbgdidr = 0x3516d000; cpu->clidr = 0x0a200023; cpu->ccsidr[0] = 0x700fe01a; /* 32KB L1 dcache */ cpu->ccsidr[1] = 0x201fe00a; /* 32KB L1 icache */ @@ -210,7 +208,6 @@ static void aarch64_a72_initfn(Object *obj) cpu->dtb_compatible = "arm,cortex-a72"; set_feature(&cpu->env, ARM_FEATURE_V8); - set_feature(&cpu->env, ARM_FEATURE_VFP4); set_feature(&cpu->env, ARM_FEATURE_NEON); set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); set_feature(&cpu->env, ARM_FEATURE_AARCH64); @@ -228,12 +225,12 @@ static void aarch64_a72_initfn(Object *obj) cpu->reset_sctlr = 0x00c50838; cpu->id_pfr0 = 0x00000131; cpu->id_pfr1 = 0x00011011; - cpu->id_dfr0 = 0x03010066; + cpu->isar.id_dfr0 = 0x03010066; cpu->id_afr0 = 0x00000000; - cpu->id_mmfr0 = 0x10201105; - cpu->id_mmfr1 = 0x40000000; - cpu->id_mmfr2 = 0x01260000; - cpu->id_mmfr3 = 0x02102211; + cpu->isar.id_mmfr0 = 0x10201105; + cpu->isar.id_mmfr1 = 0x40000000; + cpu->isar.id_mmfr2 = 0x01260000; + cpu->isar.id_mmfr3 = 0x02102211; cpu->isar.id_isar0 = 0x02101110; cpu->isar.id_isar1 = 0x13112111; cpu->isar.id_isar2 = 0x21232042; @@ -241,10 +238,10 @@ static void aarch64_a72_initfn(Object *obj) cpu->isar.id_isar4 = 0x00011142; cpu->isar.id_isar5 = 0x00011121; cpu->isar.id_aa64pfr0 = 0x00002222; - cpu->id_aa64dfr0 = 0x10305106; + cpu->isar.id_aa64dfr0 = 0x10305106; cpu->isar.id_aa64isar0 = 0x00011120; cpu->isar.id_aa64mmfr0 = 0x00001124; - cpu->dbgdidr = 0x3516d000; + cpu->isar.dbgdidr = 0x3516d000; cpu->clidr = 0x0a200023; cpu->ccsidr[0] = 0x701fe00a; /* 32KB L1 dcache */ cpu->ccsidr[1] = 0x201fe012; /* 48KB L1 icache */ @@ -605,6 +602,7 @@ static void aarch64_max_initfn(Object *obj) if (kvm_enabled()) { kvm_arm_set_cpu_features_from_host(cpu); + kvm_arm_add_vcpu_properties(obj); } else { uint64_t t; uint32_t u; @@ -646,6 +644,7 @@ static void aarch64_max_initfn(Object *obj) cpu->isar.id_aa64isar0 = t; t = cpu->isar.id_aa64isar1; + t = FIELD_DP64(t, ID_AA64ISAR1, DPB, 2); t = FIELD_DP64(t, ID_AA64ISAR1, JSCVT, 1); t = FIELD_DP64(t, ID_AA64ISAR1, FCMA, 1); t = FIELD_DP64(t, ID_AA64ISAR1, APA, 1); /* PAuth, architected only */ @@ -655,6 +654,7 @@ static void aarch64_max_initfn(Object *obj) t = FIELD_DP64(t, ID_AA64ISAR1, SB, 1); t = FIELD_DP64(t, ID_AA64ISAR1, SPECRES, 1); t = FIELD_DP64(t, ID_AA64ISAR1, FRINTTS, 1); + t = FIELD_DP64(t, ID_AA64ISAR1, LRCPC, 2); /* ARMv8.4-RCPC */ cpu->isar.id_aa64isar1 = t; t = cpu->isar.id_aa64pfr0; @@ -670,8 +670,16 @@ 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); + t = FIELD_DP64(t, ID_AA64MMFR1, PAN, 2); /* ATS1E1 */ + t = FIELD_DP64(t, ID_AA64MMFR1, VMIDBITS, 2); /* VMID16 */ cpu->isar.id_aa64mmfr1 = t; + t = cpu->isar.id_aa64mmfr2; + t = FIELD_DP64(t, ID_AA64MMFR2, UAO, 1); + t = FIELD_DP64(t, ID_AA64MMFR2, CNP, 1); /* TTCNP */ + cpu->isar.id_aa64mmfr2 = t; + /* Replicate the same data to the 32-bit id registers. */ u = cpu->isar.id_isar5; u = FIELD_DP32(u, ID_ISAR5, AES, 2); /* AES + PMULL */ @@ -690,6 +698,24 @@ static void aarch64_max_initfn(Object *obj) u = FIELD_DP32(u, ID_ISAR6, SPECRES, 1); cpu->isar.id_isar6 = u; + u = cpu->isar.id_mmfr3; + u = FIELD_DP32(u, ID_MMFR3, PAN, 2); /* ATS1E1 */ + cpu->isar.id_mmfr3 = u; + + u = cpu->isar.id_mmfr4; + u = FIELD_DP32(u, ID_MMFR4, HPDS, 1); /* AA32HPD */ + u = FIELD_DP32(u, ID_MMFR4, AC2, 1); /* ACTLR2, HACTLR2 */ + u = FIELD_DP32(u, ID_MMFR4, CNP, 1); /* TTCNP */ + cpu->isar.id_mmfr4 = u; + + u = cpu->isar.id_aa64dfr0; + u = FIELD_DP64(u, ID_AA64DFR0, PMUVER, 5); /* v8.4-PMU */ + cpu->isar.id_aa64dfr0 = u; + + u = cpu->isar.id_dfr0; + u = FIELD_DP32(u, ID_DFR0, PERFMON, 5); /* v8.4-PMU */ + cpu->isar.id_dfr0 = u; + /* * FIXME: We do not yet support ARMv8.2-fp16 for AArch32 yet, * so do not set MVFR1.FPHP. Strictly speaking this is not legal, diff --git a/target/arm/debug_helper.c b/target/arm/debug_helper.c index dde80273ff..2ff72d47d1 100644 --- a/target/arm/debug_helper.c +++ b/target/arm/debug_helper.c @@ -16,10 +16,11 @@ static bool linked_bp_matches(ARMCPU *cpu, int lbn) { CPUARMState *env = &cpu->env; uint64_t bcr = env->cp15.dbgbcr[lbn]; - int brps = extract32(cpu->dbgdidr, 24, 4); - int ctx_cmps = extract32(cpu->dbgdidr, 20, 4); + int brps = arm_num_brps(cpu); + int ctx_cmps = arm_num_ctx_cmps(cpu); int bt; uint32_t contextidr; + uint64_t hcr_el2; /* * Links to unimplemented or non-context aware breakpoints are @@ -28,7 +29,7 @@ static bool linked_bp_matches(ARMCPU *cpu, int lbn) * case DBGWCR_EL1.LBN must indicate that breakpoint). * We choose the former. */ - if (lbn > brps || lbn < (brps - ctx_cmps)) { + if (lbn >= brps || lbn < (brps - ctx_cmps)) { return false; } @@ -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/gdbstub.c b/target/arm/gdbstub.c index 1239abd984..063551df23 100644 --- a/target/arm/gdbstub.c +++ b/target/arm/gdbstub.c @@ -24,6 +24,7 @@ typedef struct RegisterSysregXmlParam { CPUState *cs; GString *s; + int n; } RegisterSysregXmlParam; /* Old gdb always expect FPA registers. Newer (xml-aware) gdb only expect @@ -32,7 +33,7 @@ typedef struct RegisterSysregXmlParam { We hack round this by giving the FPA regs zero size when talking to a newer gdb. */ -int arm_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +int arm_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) { ARMCPU *cpu = ARM_CPU(cs); CPUARMState *env = &cpu->env; @@ -46,8 +47,7 @@ int arm_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) if (gdb_has_xml) { return 0; } - memset(mem_buf, 0, 12); - return 12; + return gdb_get_zeroes(mem_buf, 12); } switch (n) { case 24: @@ -106,15 +106,16 @@ int arm_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) return 0; } -static void arm_gen_one_xml_reg_tag(GString *s, DynamicGDBXMLInfo *dyn_xml, - ARMCPRegInfo *ri, uint32_t ri_key, - int bitsize) +static void arm_gen_one_xml_sysreg_tag(GString *s, DynamicGDBXMLInfo *dyn_xml, + ARMCPRegInfo *ri, uint32_t ri_key, + int bitsize, int regnum) { g_string_append_printf(s, "name); g_string_append_printf(s, " bitsize=\"%d\"", bitsize); + g_string_append_printf(s, " regnum=\"%d\"", regnum); g_string_append_printf(s, " group=\"cp_regs\"/>"); - dyn_xml->num_cpregs++; - dyn_xml->cpregs_keys[dyn_xml->num_cpregs - 1] = ri_key; + dyn_xml->data.cpregs.keys[dyn_xml->num] = ri_key; + dyn_xml->num++; } static void arm_register_sysreg_for_xml(gpointer key, gpointer value, @@ -126,12 +127,13 @@ static void arm_register_sysreg_for_xml(gpointer key, gpointer value, GString *s = param->s; ARMCPU *cpu = ARM_CPU(param->cs); CPUARMState *env = &cpu->env; - DynamicGDBXMLInfo *dyn_xml = &cpu->dyn_xml; + DynamicGDBXMLInfo *dyn_xml = &cpu->dyn_sysreg_xml; if (!(ri->type & (ARM_CP_NO_RAW | ARM_CP_NO_GDB))) { if (arm_feature(env, ARM_FEATURE_AARCH64)) { if (ri->state == ARM_CP_STATE_AA64) { - arm_gen_one_xml_reg_tag(s , dyn_xml, ri, ri_key, 64); + arm_gen_one_xml_sysreg_tag(s , dyn_xml, ri, ri_key, 64, + param->n++); } } else { if (ri->state == ARM_CP_STATE_AA32) { @@ -140,38 +142,179 @@ static void arm_register_sysreg_for_xml(gpointer key, gpointer value, return; } if (ri->type & ARM_CP_64BIT) { - arm_gen_one_xml_reg_tag(s , dyn_xml, ri, ri_key, 64); + arm_gen_one_xml_sysreg_tag(s , dyn_xml, ri, ri_key, 64, + param->n++); } else { - arm_gen_one_xml_reg_tag(s , dyn_xml, ri, ri_key, 32); + arm_gen_one_xml_sysreg_tag(s , dyn_xml, ri, ri_key, 32, + param->n++); } } } } } -int arm_gen_dynamic_xml(CPUState *cs) +int arm_gen_dynamic_sysreg_xml(CPUState *cs, int base_reg) { ARMCPU *cpu = ARM_CPU(cs); GString *s = g_string_new(NULL); - RegisterSysregXmlParam param = {cs, s}; + RegisterSysregXmlParam param = {cs, s, base_reg}; - cpu->dyn_xml.num_cpregs = 0; - cpu->dyn_xml.cpregs_keys = g_new(uint32_t, g_hash_table_size(cpu->cp_regs)); + cpu->dyn_sysreg_xml.num = 0; + cpu->dyn_sysreg_xml.data.cpregs.keys = g_new(uint32_t, g_hash_table_size(cpu->cp_regs)); g_string_printf(s, ""); g_string_append_printf(s, ""); g_string_append_printf(s, ""); g_hash_table_foreach(cpu->cp_regs, arm_register_sysreg_for_xml, ¶m); g_string_append_printf(s, ""); - cpu->dyn_xml.desc = g_string_free(s, false); - return cpu->dyn_xml.num_cpregs; + cpu->dyn_sysreg_xml.desc = g_string_free(s, false); + return cpu->dyn_sysreg_xml.num; } +struct TypeSize { + const char *gdb_type; + int size; + const char sz, suffix; +}; + +static const struct TypeSize vec_lanes[] = { + /* quads */ + { "uint128", 128, 'q', 'u' }, + { "int128", 128, 'q', 's' }, + /* 64 bit */ + { "uint64", 64, 'd', 'u' }, + { "int64", 64, 'd', 's' }, + { "ieee_double", 64, 'd', 'f' }, + /* 32 bit */ + { "uint32", 32, 's', 'u' }, + { "int32", 32, 's', 's' }, + { "ieee_single", 32, 's', 'f' }, + /* 16 bit */ + { "uint16", 16, 'h', 'u' }, + { "int16", 16, 'h', 's' }, + /* + * TODO: currently there is no reliable way of telling + * if the remote gdb actually understands ieee_half so + * we don't expose it in the target description for now. + * { "ieee_half", 16, 'h', 'f' }, + */ + /* bytes */ + { "uint8", 8, 'b', 'u' }, + { "int8", 8, 'b', 's' }, +}; + + +int arm_gen_dynamic_svereg_xml(CPUState *cs, int base_reg) +{ + ARMCPU *cpu = ARM_CPU(cs); + GString *s = g_string_new(NULL); + DynamicGDBXMLInfo *info = &cpu->dyn_svereg_xml; + g_autoptr(GString) ts = g_string_new(""); + int i, bits, reg_width = (cpu->sve_max_vq * 128); + info->num = 0; + g_string_printf(s, ""); + g_string_append_printf(s, ""); + g_string_append_printf(s, ""); + + /* First define types and totals in a whole VL */ + for (i = 0; i < ARRAY_SIZE(vec_lanes); i++) { + int count = reg_width / vec_lanes[i].size; + g_string_printf(ts, "vq%d%c%c", count, + vec_lanes[i].sz, vec_lanes[i].suffix); + g_string_append_printf(s, + "", + ts->str, vec_lanes[i].gdb_type, count); + } + /* + * Now define a union for each size group containing unsigned and + * signed and potentially float versions of each size from 128 to + * 8 bits. + */ + for (bits = 128; bits >= 8; bits /= 2) { + int count = reg_width / bits; + g_string_append_printf(s, "", count); + for (i = 0; i < ARRAY_SIZE(vec_lanes); i++) { + if (vec_lanes[i].size == bits) { + g_string_append_printf(s, "", + vec_lanes[i].suffix, + count, + vec_lanes[i].sz, vec_lanes[i].suffix); + } + } + g_string_append(s, ""); + } + /* And now the final union of unions */ + g_string_append(s, ""); + for (bits = 128; bits >= 8; bits /= 2) { + int count = reg_width / bits; + for (i = 0; i < ARRAY_SIZE(vec_lanes); i++) { + if (vec_lanes[i].size == bits) { + g_string_append_printf(s, "", + vec_lanes[i].sz, count); + break; + } + } + } + g_string_append(s, ""); + + /* Then define each register in parts for each vq */ + for (i = 0; i < 32; i++) { + g_string_append_printf(s, + "", + i, reg_width, base_reg++); + info->num++; + } + /* fpscr & status registers */ + g_string_append_printf(s, "", base_reg++); + g_string_append_printf(s, "", base_reg++); + info->num += 2; + /* + * Predicate registers aren't so big they are worth splitting up + * but we do need to define a type to hold the array of quad + * references. + */ + g_string_append_printf(s, + "", + cpu->sve_max_vq); + for (i = 0; i < 16; i++) { + g_string_append_printf(s, + "", + i, cpu->sve_max_vq * 16, base_reg++); + info->num++; + } + g_string_append_printf(s, + "", + cpu->sve_max_vq * 16, base_reg++); + g_string_append_printf(s, + "", + base_reg++); + info->num += 2; + g_string_append_printf(s, ""); + cpu->dyn_svereg_xml.desc = g_string_free(s, false); + + return cpu->dyn_svereg_xml.num; +} + + const char *arm_gdb_get_dynamic_xml(CPUState *cs, const char *xmlname) { ARMCPU *cpu = ARM_CPU(cs); if (strcmp(xmlname, "system-registers.xml") == 0) { - return cpu->dyn_xml.desc; + return cpu->dyn_sysreg_xml.desc; + } else if (strcmp(xmlname, "sve-registers.xml") == 0) { + return cpu->dyn_svereg_xml.desc; } return NULL; } diff --git a/target/arm/gdbstub64.c b/target/arm/gdbstub64.c index 665ebb3ef6..35d0b80c2d 100644 --- a/target/arm/gdbstub64.c +++ b/target/arm/gdbstub64.c @@ -20,7 +20,7 @@ #include "cpu.h" #include "exec/gdbstub.h" -int aarch64_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +int aarch64_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) { ARMCPU *cpu = ARM_CPU(cs); CPUARMState *env = &cpu->env; diff --git a/target/arm/helper-a64.c b/target/arm/helper-a64.c index b4cd680fc4..bc0649a44a 100644 --- a/target/arm/helper-a64.c +++ b/target/arm/helper-a64.c @@ -18,6 +18,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "cpu.h" #include "exec/gdbstub.h" #include "exec/helper-proto.h" @@ -31,7 +32,7 @@ #include "exec/cpu_ldst.h" #include "qemu/int128.h" #include "qemu/atomic128.h" -#include "tcg.h" +#include "tcg/tcg.h" #include "fpu/softfloat.h" #include /* For crc32 */ @@ -70,7 +71,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, @@ -959,7 +960,7 @@ void HELPER(exception_return)(CPUARMState *env, uint64_t new_pc) { int cur_el = arm_current_el(env); unsigned int spsr_idx = aarch64_banked_spsr_index(cur_el); - uint32_t spsr = env->banked_spsr[spsr_idx]; + uint32_t mask, spsr = env->banked_spsr[spsr_idx]; int new_el; bool return_to_aa64 = (spsr & PSTATE_nRW) == 0; @@ -1014,7 +1015,8 @@ void HELPER(exception_return)(CPUARMState *env, uint64_t new_pc) * will sort the register banks out for us, and we've already * caught all the bad-mode cases in el_from_spsr(). */ - cpsr_write(env, spsr, ~0, CPSRWriteRaw); + mask = aarch32_cpsr_valid_mask(env->features, &env_archcpu(env)->isar); + cpsr_write(env, spsr, mask, CPSRWriteRaw); if (!arm_singlestep_active(env)) { env->uncached_cpsr &= ~PSTATE_SS; } @@ -1030,14 +1032,36 @@ void HELPER(exception_return)(CPUARMState *env, uint64_t new_pc) "AArch32 EL%d PC 0x%" PRIx32 "\n", cur_el, new_el, env->regs[15]); } else { + int tbii; + env->aarch64 = 1; + spsr &= aarch64_pstate_valid_mask(&env_archcpu(env)->isar); pstate_write(env, spsr); if (!arm_singlestep_active(env)) { env->pstate &= ~PSTATE_SS; } aarch64_restore_sp(env, new_el); - env->pc = new_pc; helper_rebuild_hflags_a64(env, new_el); + + /* + * Apply TBI to the exception return address. We had to delay this + * until after we selected the new EL, so that we could select the + * correct TBI+TBID bits. This is made easier by waiting until after + * the hflags rebuild, since we can pull the composite TBII field + * from there. + */ + tbii = FIELD_EX32(env->hflags, TBFLAG_A64, TBII); + if ((tbii >> extract64(new_pc, 55, 1)) & 1) { + /* TBI is enabled. */ + int core_mmu_idx = cpu_mmu_index(env, false); + if (regime_has_2_ranges(core_to_aa64_mmu_idx(core_mmu_idx))) { + new_pc = sextract64(new_pc, 0, 56); + } else { + new_pc = extract64(new_pc, 0, 56); + } + } + env->pc = new_pc; + qemu_log_mask(CPU_LOG_INT, "Exception return from AArch64 EL%d to " "AArch64 EL%d PC 0x%" PRIx64 "\n", cur_el, new_el, env->pc); @@ -1086,4 +1110,94 @@ uint32_t HELPER(sqrt_f16)(uint32_t a, void *fpstp) return float16_sqrt(a, s); } +void HELPER(dc_zva)(CPUARMState *env, uint64_t vaddr_in) +{ + /* + * Implement DC ZVA, which zeroes a fixed-length block of memory. + * Note that we do not implement the (architecturally mandated) + * alignment fault for attempts to use this on Device memory + * (which matches the usual QEMU behaviour of not implementing either + * alignment faults or any memory attribute handling). + */ + ARMCPU *cpu = env_archcpu(env); + uint64_t blocklen = 4 << cpu->dcz_blocksize; + uint64_t vaddr = vaddr_in & ~(blocklen - 1); + +#ifndef CONFIG_USER_ONLY + { + /* + * Slightly awkwardly, QEMU's TARGET_PAGE_SIZE may be less than + * the block size so we might have to do more than one TLB lookup. + * We know that in fact for any v8 CPU the page size is at least 4K + * and the block size must be 2K or less, but TARGET_PAGE_SIZE is only + * 1K as an artefact of legacy v5 subpage support being present in the + * same QEMU executable. So in practice the hostaddr[] array has + * two entries, given the current setting of TARGET_PAGE_BITS_MIN. + */ + int maxidx = DIV_ROUND_UP(blocklen, TARGET_PAGE_SIZE); + void *hostaddr[DIV_ROUND_UP(2 * KiB, 1 << TARGET_PAGE_BITS_MIN)]; + int try, i; + unsigned mmu_idx = cpu_mmu_index(env, false); + TCGMemOpIdx oi = make_memop_idx(MO_UB, mmu_idx); + + assert(maxidx <= ARRAY_SIZE(hostaddr)); + + for (try = 0; try < 2; try++) { + + for (i = 0; i < maxidx; i++) { + hostaddr[i] = tlb_vaddr_to_host(env, + vaddr + TARGET_PAGE_SIZE * i, + 1, mmu_idx); + if (!hostaddr[i]) { + break; + } + } + if (i == maxidx) { + /* + * If it's all in the TLB it's fair game for just writing to; + * we know we don't need to update dirty status, etc. + */ + for (i = 0; i < maxidx - 1; i++) { + memset(hostaddr[i], 0, TARGET_PAGE_SIZE); + } + memset(hostaddr[i], 0, blocklen - (i * TARGET_PAGE_SIZE)); + return; + } + /* + * OK, try a store and see if we can populate the tlb. This + * might cause an exception if the memory isn't writable, + * in which case we will longjmp out of here. We must for + * this purpose use the actual register value passed to us + * so that we get the fault address right. + */ + helper_ret_stb_mmu(env, vaddr_in, 0, oi, GETPC()); + /* Now we can populate the other TLB entries, if any */ + for (i = 0; i < maxidx; i++) { + uint64_t va = vaddr + TARGET_PAGE_SIZE * i; + if (va != (vaddr_in & TARGET_PAGE_MASK)) { + helper_ret_stb_mmu(env, va, 0, oi, GETPC()); + } + } + } + + /* + * Slow path (probably attempt to do this to an I/O device or + * similar, or clearing of a block of code we have translations + * cached for). Just do a series of byte writes as the architecture + * demands. It's not worth trying to use a cpu_physical_memory_map(), + * memset(), unmap() sequence here because: + * + we'd need to account for the blocksize being larger than a page + * + the direct-RAM access case is almost always going to be dealt + * with in the fastpath code above, so there's no speed benefit + * + we would have to deal with the map returning NULL because the + * bounce buffer was in use + */ + for (i = 0; i < blocklen; i++) { + helper_ret_stb_mmu(env, vaddr + i, 0, oi, GETPC()); + } + } +#else + memset(g2h(vaddr), 0, blocklen); +#endif +} diff --git a/target/arm/helper-a64.h b/target/arm/helper-a64.h index a915c1247f..3df7c185aa 100644 --- a/target/arm/helper-a64.h +++ b/target/arm/helper-a64.h @@ -90,6 +90,7 @@ DEF_HELPER_2(advsimd_f16touinth, i32, f16, ptr) DEF_HELPER_2(sqrt_f16, f16, f16, ptr) DEF_HELPER_2(exception_return, void, env, i64) +DEF_HELPER_FLAGS_2(dc_zva, TCG_CALL_NO_WG, void, env, i64) DEF_HELPER_FLAGS_3(pacia, TCG_CALL_NO_WG, i64, env, i64, i64) DEF_HELPER_FLAGS_3(pacib, TCG_CALL_NO_WG, i64, env, i64, i64) diff --git a/target/arm/helper-sve.h b/target/arm/helper-sve.h index 9e79182ab4..2f47279155 100644 --- a/target/arm/helper-sve.h +++ b/target/arm/helper-sve.h @@ -1574,3 +1574,5 @@ DEF_HELPER_FLAGS_6(sve_stdd_le_zd, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) DEF_HELPER_FLAGS_6(sve_stdd_be_zd, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) + +DEF_HELPER_FLAGS_4(sve2_pmull_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) diff --git a/target/arm/helper.c b/target/arm/helper.c index 0bf8f53d4b..7e9ea5d20f 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -25,6 +25,7 @@ #include "hw/semihosting/semihost.h" #include "sysemu/cpus.h" #include "sysemu/kvm.h" +#include "sysemu/tcg.h" #include "qemu/range.h" #include "qapi/qapi-commands-machine-target.h" #include "qapi/error.h" @@ -47,39 +48,36 @@ static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address, static void switch_mode(CPUARMState *env, int mode); -static int vfp_gdb_get_reg(CPUARMState *env, uint8_t *buf, int reg) +static int vfp_gdb_get_reg(CPUARMState *env, GByteArray *buf, int reg) { - int nregs; + ARMCPU *cpu = env_archcpu(env); + int nregs = cpu_isar_feature(aa32_simd_r32, cpu) ? 32 : 16; /* VFP data registers are always little-endian. */ - nregs = arm_feature(env, ARM_FEATURE_VFP3) ? 32 : 16; if (reg < nregs) { - stq_le_p(buf, *aa32_vfp_dreg(env, reg)); - return 8; + return gdb_get_reg64(buf, *aa32_vfp_dreg(env, reg)); } if (arm_feature(env, ARM_FEATURE_NEON)) { /* Aliases for Q regs. */ nregs += 16; if (reg < nregs) { uint64_t *q = aa32_vfp_qreg(env, reg - 32); - stq_le_p(buf, q[0]); - stq_le_p(buf + 8, q[1]); - return 16; + return gdb_get_reg128(buf, q[0], q[1]); } } switch (reg - nregs) { - case 0: stl_p(buf, env->vfp.xregs[ARM_VFP_FPSID]); return 4; - case 1: stl_p(buf, vfp_get_fpscr(env)); return 4; - case 2: stl_p(buf, env->vfp.xregs[ARM_VFP_FPEXC]); return 4; + case 0: return gdb_get_reg32(buf, env->vfp.xregs[ARM_VFP_FPSID]); break; + case 1: return gdb_get_reg32(buf, vfp_get_fpscr(env)); break; + case 2: return gdb_get_reg32(buf, env->vfp.xregs[ARM_VFP_FPEXC]); break; } return 0; } static int vfp_gdb_set_reg(CPUARMState *env, uint8_t *buf, int reg) { - int nregs; + ARMCPU *cpu = env_archcpu(env); + int nregs = cpu_isar_feature(aa32_simd_r32, cpu) ? 32 : 16; - nregs = arm_feature(env, ARM_FEATURE_VFP3) ? 32 : 16; if (reg < nregs) { *aa32_vfp_dreg(env, reg) = ldq_le_p(buf); return 8; @@ -101,25 +99,21 @@ static int vfp_gdb_set_reg(CPUARMState *env, uint8_t *buf, int reg) return 0; } -static int aarch64_fpu_gdb_get_reg(CPUARMState *env, uint8_t *buf, int reg) +static int aarch64_fpu_gdb_get_reg(CPUARMState *env, GByteArray *buf, int reg) { switch (reg) { case 0 ... 31: - /* 128 bit FP register */ - { - uint64_t *q = aa64_vfp_qreg(env, reg); - stq_le_p(buf, q[0]); - stq_le_p(buf + 8, q[1]); - return 16; - } + { + /* 128 bit FP register - quads are in LE order */ + uint64_t *q = aa64_vfp_qreg(env, reg); + return gdb_get_reg128(buf, q[1], q[0]); + } case 32: /* FPSR */ - stl_p(buf, vfp_get_fpsr(env)); - return 4; + return gdb_get_reg32(buf, vfp_get_fpsr(env)); case 33: /* FPCR */ - stl_p(buf, vfp_get_fpcr(env)); - return 4; + return gdb_get_reg32(buf,vfp_get_fpcr(env)); default: return 0; } @@ -208,13 +202,22 @@ static void write_raw_cp_reg(CPUARMState *env, const ARMCPRegInfo *ri, } } -static int arm_gdb_get_sysreg(CPUARMState *env, uint8_t *buf, int reg) +/** + * arm_get/set_gdb_*: get/set a gdb register + * @env: the CPU state + * @buf: a buffer to copy to/from + * @reg: register number (offset from start of group) + * + * We return the number of bytes copied + */ + +static int arm_gdb_get_sysreg(CPUARMState *env, GByteArray *buf, int reg) { ARMCPU *cpu = env_archcpu(env); const ARMCPRegInfo *ri; uint32_t key; - key = cpu->dyn_xml.cpregs_keys[reg]; + key = cpu->dyn_sysreg_xml.data.cpregs.keys[reg]; ri = get_arm_cp_reginfo(cpu->cp_regs, key); if (ri) { if (cpreg_field_is_64bit(ri)) { @@ -231,6 +234,102 @@ static int arm_gdb_set_sysreg(CPUARMState *env, uint8_t *buf, int reg) return 0; } +#ifdef TARGET_AARCH64 +static int arm_gdb_get_svereg(CPUARMState *env, GByteArray *buf, int reg) +{ + ARMCPU *cpu = env_archcpu(env); + + switch (reg) { + /* The first 32 registers are the zregs */ + case 0 ... 31: + { + int vq, len = 0; + for (vq = 0; vq < cpu->sve_max_vq; vq++) { + len += gdb_get_reg128(buf, + env->vfp.zregs[reg].d[vq * 2 + 1], + env->vfp.zregs[reg].d[vq * 2]); + } + return len; + } + case 32: + return gdb_get_reg32(buf, vfp_get_fpsr(env)); + case 33: + return gdb_get_reg32(buf, vfp_get_fpcr(env)); + /* then 16 predicates and the ffr */ + case 34 ... 50: + { + int preg = reg - 34; + int vq, len = 0; + for (vq = 0; vq < cpu->sve_max_vq; vq = vq + 4) { + len += gdb_get_reg64(buf, env->vfp.pregs[preg].p[vq / 4]); + } + return len; + } + case 51: + { + /* + * We report in Vector Granules (VG) which is 64bit in a Z reg + * while the ZCR works in Vector Quads (VQ) which is 128bit chunks. + */ + int vq = sve_zcr_len_for_el(env, arm_current_el(env)) + 1; + return gdb_get_reg32(buf, vq * 2); + } + default: + /* gdbstub asked for something out our range */ + qemu_log_mask(LOG_UNIMP, "%s: out of range register %d", __func__, reg); + break; + } + + return 0; +} + +static int arm_gdb_set_svereg(CPUARMState *env, uint8_t *buf, int reg) +{ + ARMCPU *cpu = env_archcpu(env); + + /* The first 32 registers are the zregs */ + switch (reg) { + /* The first 32 registers are the zregs */ + case 0 ... 31: + { + int vq, len = 0; + uint64_t *p = (uint64_t *) buf; + for (vq = 0; vq < cpu->sve_max_vq; vq++) { + env->vfp.zregs[reg].d[vq * 2 + 1] = *p++; + env->vfp.zregs[reg].d[vq * 2] = *p++; + len += 16; + } + return len; + } + case 32: + vfp_set_fpsr(env, *(uint32_t *)buf); + return 4; + case 33: + vfp_set_fpcr(env, *(uint32_t *)buf); + return 4; + case 34 ... 50: + { + int preg = reg - 34; + int vq, len = 0; + uint64_t *p = (uint64_t *) buf; + for (vq = 0; vq < cpu->sve_max_vq; vq = vq + 4) { + env->vfp.pregs[preg].p[vq / 4] = *p++; + len += 8; + } + return len; + } + case 51: + /* cannot set vg via gdbstub */ + return 0; + default: + /* gdbstub asked for something out our range */ + break; + } + + return 0; +} +#endif /* TARGET_AARCH64 */ + static bool raw_accessors_invalid(const ARMCPRegInfo *ri) { /* Return true if the regdef would cause an assertion if you called @@ -529,6 +628,49 @@ static CPAccessResult access_tpm(CPUARMState *env, const ARMCPRegInfo *ri, return CP_ACCESS_OK; } +/* Check for traps from EL1 due to HCR_EL2.TVM and HCR_EL2.TRVM. */ +static CPAccessResult access_tvm_trvm(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) +{ + if (arm_current_el(env) == 1) { + uint64_t trap = isread ? HCR_TRVM : HCR_TVM; + if (arm_hcr_el2_eff(env) & trap) { + return CP_ACCESS_TRAP_EL2; + } + } + return CP_ACCESS_OK; +} + +/* Check for traps from EL1 due to HCR_EL2.TSW. */ +static CPAccessResult access_tsw(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) +{ + if (arm_current_el(env) == 1 && (arm_hcr_el2_eff(env) & HCR_TSW)) { + return CP_ACCESS_TRAP_EL2; + } + return CP_ACCESS_OK; +} + +/* Check for traps from EL1 due to HCR_EL2.TACR. */ +static CPAccessResult access_tacr(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) +{ + if (arm_current_el(env) == 1 && (arm_hcr_el2_eff(env) & HCR_TACR)) { + return CP_ACCESS_TRAP_EL2; + } + return CP_ACCESS_OK; +} + +/* Check for traps from EL1 due to HCR_EL2.TTLB. */ +static CPAccessResult access_ttlb(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) +{ + if (arm_current_el(env) == 1 && (arm_hcr_el2_eff(env) & HCR_TTLB)) { + return CP_ACCESS_TRAP_EL2; + } + return CP_ACCESS_OK; +} + static void dacr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { ARMCPU *cpu = env_archcpu(env); @@ -614,56 +756,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 +812,10 @@ 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_1_PAN | + ARMMMUIdxBit_E10_0 | + ARMMMUIdxBit_Stage2); } static void tlbiall_nsnh_is_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -683,9 +824,10 @@ 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_1_PAN | + ARMMMUIdxBit_E10_0 | + ARMMMUIdxBit_Stage2); } static void tlbiipas2_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -706,7 +848,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 +864,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 +872,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 +880,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 +889,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 +899,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[] = { @@ -784,12 +926,14 @@ static const ARMCPRegInfo cp_reginfo[] = { */ { .name = "CONTEXTIDR_EL1", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 13, .crm = 0, .opc2 = 1, - .access = PL1_RW, .secure = ARM_CP_SECSTATE_NS, + .access = PL1_RW, .accessfn = access_tvm_trvm, + .secure = ARM_CP_SECSTATE_NS, .fieldoffset = offsetof(CPUARMState, cp15.contextidr_el[1]), .resetvalue = 0, .writefn = contextidr_write, .raw_writefn = raw_write, }, { .name = "CONTEXTIDR_S", .state = ARM_CP_STATE_AA32, .cp = 15, .opc1 = 0, .crn = 13, .crm = 0, .opc2 = 1, - .access = PL1_RW, .secure = ARM_CP_SECSTATE_S, + .access = PL1_RW, .accessfn = access_tvm_trvm, + .secure = ARM_CP_SECSTATE_S, .fieldoffset = offsetof(CPUARMState, cp15.contextidr_s), .resetvalue = 0, .writefn = contextidr_write, .raw_writefn = raw_write, }, REGINFO_SENTINEL @@ -802,7 +946,7 @@ static const ARMCPRegInfo not_v8_cp_reginfo[] = { /* MMU Domain access control / MPU write buffer control */ { .name = "DACR", .cp = 15, .opc1 = CP_ANY, .crn = 3, .crm = CP_ANY, .opc2 = CP_ANY, - .access = PL1_RW, .resetvalue = 0, + .access = PL1_RW, .accessfn = access_tvm_trvm, .resetvalue = 0, .writefn = dacr_write, .raw_writefn = raw_write, .bank_fieldoffsets = { offsetoflow32(CPUARMState, cp15.dacr_s), offsetoflow32(CPUARMState, cp15.dacr_ns) } }, @@ -893,7 +1037,7 @@ static void cpacr_write(CPUARMState *env, const ARMCPRegInfo *ri, * ASEDIS [31] and D32DIS [30] are both UNK/SBZP without VFP. * TRCDIS [28] is RAZ/WI since we do not implement a trace macrocell. */ - if (arm_feature(env, ARM_FEATURE_VFP)) { + if (cpu_isar_feature(aa32_vfp_simd, env_archcpu(env))) { /* VFP coprocessor: cp10 & cp11 [23:20] */ mask |= (1 << 31) | (1 << 30) | (0xf << 20); @@ -905,8 +1049,7 @@ static void cpacr_write(CPUARMState *env, const ARMCPRegInfo *ri, /* VFPv3 and upwards with NEON implement 32 double precision * registers (D0-D31). */ - if (!arm_feature(env, ARM_FEATURE_NEON) || - !arm_feature(env, ARM_FEATURE_VFP3)) { + if (!cpu_isar_feature(aa32_simd_r32, env_archcpu(env))) { /* D32DIS [30] is RAO/WI if D16-31 are not implemented. */ value |= (1 << 30); } @@ -996,7 +1139,7 @@ static const ARMCPRegInfo v6_cp_reginfo[] = { { .name = "DMB", .cp = 15, .crn = 7, .crm = 10, .opc1 = 0, .opc2 = 5, .access = PL0_W, .type = ARM_CP_NOP }, { .name = "IFAR", .cp = 15, .crn = 6, .crm = 0, .opc1 = 0, .opc2 = 2, - .access = PL1_RW, + .access = PL1_RW, .accessfn = access_tvm_trvm, .bank_fieldoffsets = { offsetof(CPUARMState, cp15.ifar_s), offsetof(CPUARMState, cp15.ifar_ns) }, .resetvalue = 0, }, @@ -1016,11 +1159,17 @@ static const ARMCPRegInfo v6_cp_reginfo[] = { #define PMCRN_MASK 0xf800 #define PMCRN_SHIFT 11 #define PMCRLC 0x40 -#define PMCRDP 0x10 +#define PMCRDP 0x20 +#define PMCRX 0x10 #define PMCRD 0x8 #define PMCRC 0x4 #define PMCRP 0x2 #define PMCRE 0x1 +/* + * Mask of PMCR bits writeable by guest (not including WO bits like C, P, + * which can be written as 1 to trigger behaviour but which stay RAZ). + */ +#define PMCR_WRITEABLE_MASK (PMCRLC | PMCRDP | PMCRX | PMCRD | PMCRE) #define PMXEVTYPER_P 0x80000000 #define PMXEVTYPER_U 0x40000000 @@ -1123,6 +1272,30 @@ static int64_t instructions_ns_per(uint64_t icount) } #endif +static bool pmu_8_1_events_supported(CPUARMState *env) +{ + /* For events which are supported in any v8.1 PMU */ + return cpu_isar_feature(any_pmu_8_1, env_archcpu(env)); +} + +static bool pmu_8_4_events_supported(CPUARMState *env) +{ + /* For events which are supported in any v8.1 PMU */ + return cpu_isar_feature(any_pmu_8_4, env_archcpu(env)); +} + +static uint64_t zero_event_get_count(CPUARMState *env) +{ + /* For events which on QEMU never fire, so their count is always zero */ + return 0; +} + +static int64_t zero_event_ns_per(uint64_t cycles) +{ + /* An event which never fires can never overflow */ + return -1; +} + static const pm_event pm_events[] = { { .number = 0x000, /* SW_INCR */ .supported = event_always_supported, @@ -1139,8 +1312,23 @@ static const pm_event pm_events[] = { .supported = event_always_supported, .get_count = cycles_get_count, .ns_per_count = cycles_ns_per, - } + }, #endif + { .number = 0x023, /* STALL_FRONTEND */ + .supported = pmu_8_1_events_supported, + .get_count = zero_event_get_count, + .ns_per_count = zero_event_ns_per, + }, + { .number = 0x024, /* STALL_BACKEND */ + .supported = pmu_8_1_events_supported, + .get_count = zero_event_get_count, + .ns_per_count = zero_event_ns_per, + }, + { .number = 0x03c, /* STALL */ + .supported = pmu_8_4_events_supported, + .get_count = zero_event_get_count, + .ns_per_count = zero_event_ns_per, + }, }; /* @@ -1149,7 +1337,7 @@ static const pm_event pm_events[] = { * should first be updated to something sparse instead of the current * supported_event_map[] array. */ -#define MAX_EVENT_ID 0x11 +#define MAX_EVENT_ID 0x3c #define UNSUPPORTED_EVENT UINT16_MAX static uint16_t supported_event_map[MAX_EVENT_ID + 1]; @@ -1536,9 +1724,8 @@ static void pmcr_write(CPUARMState *env, const ARMCPRegInfo *ri, } } - /* only the DP, X, D and E bits are writable */ - env->cp15.c9_pmcr &= ~0x39; - env->cp15.c9_pmcr |= (value & 0x39); + env->cp15.c9_pmcr &= ~PMCR_WRITEABLE_MASK; + env->cp15.c9_pmcr |= (value & PMCR_WRITEABLE_MASK); pmu_op_finish(env); } @@ -1910,6 +2097,17 @@ static void scr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) raw_write(env, ri, value); } +static CPAccessResult access_aa64_tid2(CPUARMState *env, + const ARMCPRegInfo *ri, + bool isread) +{ + if (arm_current_el(env) == 1 && (arm_hcr_el2_eff(env) & HCR_TID2)) { + return CP_ACCESS_TRAP_EL2; + } + + return CP_ACCESS_OK; +} + static uint64_t ccsidr_read(CPUARMState *env, const ARMCPRegInfo *ri) { ARMCPU *cpu = env_archcpu(env); @@ -1962,6 +2160,26 @@ static uint64_t isr_read(CPUARMState *env, const ARMCPRegInfo *ri) return ret; } +static CPAccessResult access_aa64_tid1(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) +{ + if (arm_current_el(env) == 1 && (arm_hcr_el2_eff(env) & HCR_TID1)) { + return CP_ACCESS_TRAP_EL2; + } + + return CP_ACCESS_OK; +} + +static CPAccessResult access_aa32_tid1(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) +{ + if (arm_feature(env, ARM_FEATURE_V8)) { + return access_aa64_tid1(env, ri, isread); + } + + return CP_ACCESS_OK; +} + static const ARMCPRegInfo v7_cp_reginfo[] = { /* the old v6 WFI, UNPREDICTABLE in v7 but we choose to NOP */ { .name = "NOP", .cp = 15, .crn = 7, .crm = 0, .opc1 = 0, .opc2 = 4, @@ -2110,10 +2328,14 @@ static const ARMCPRegInfo v7_cp_reginfo[] = { .writefn = pmintenclr_write }, { .name = "CCSIDR", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .crn = 0, .crm = 0, .opc1 = 1, .opc2 = 0, - .access = PL1_R, .readfn = ccsidr_read, .type = ARM_CP_NO_RAW }, + .access = PL1_R, + .accessfn = access_aa64_tid2, + .readfn = ccsidr_read, .type = ARM_CP_NO_RAW }, { .name = "CSSELR", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .crn = 0, .crm = 0, .opc1 = 2, .opc2 = 0, - .access = PL1_RW, .writefn = csselr_write, .resetvalue = 0, + .access = PL1_RW, + .accessfn = access_aa64_tid2, + .writefn = csselr_write, .resetvalue = 0, .bank_fieldoffsets = { offsetof(CPUARMState, cp15.csselr_s), offsetof(CPUARMState, cp15.csselr_ns) } }, /* Auxiliary ID register: this actually has an IMPDEF value but for now @@ -2121,22 +2343,27 @@ static const ARMCPRegInfo v7_cp_reginfo[] = { */ { .name = "AIDR", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 1, .crn = 0, .crm = 0, .opc2 = 7, - .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = 0 }, + .access = PL1_R, .type = ARM_CP_CONST, + .accessfn = access_aa64_tid1, + .resetvalue = 0 }, /* Auxiliary fault status registers: these also are IMPDEF, and we * choose to RAZ/WI for all cores. */ { .name = "AFSR0_EL1", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 5, .crm = 1, .opc2 = 0, - .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + .access = PL1_RW, .accessfn = access_tvm_trvm, + .type = ARM_CP_CONST, .resetvalue = 0 }, { .name = "AFSR1_EL1", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 5, .crm = 1, .opc2 = 1, - .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + .access = PL1_RW, .accessfn = access_tvm_trvm, + .type = ARM_CP_CONST, .resetvalue = 0 }, /* MAIR can just read-as-written because we don't implement caches * and so don't need to care about memory attributes. */ { .name = "MAIR_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 10, .crm = 2, .opc2 = 0, - .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.mair_el[1]), + .access = PL1_RW, .accessfn = access_tvm_trvm, + .fieldoffset = offsetof(CPUARMState, cp15.mair_el[1]), .resetvalue = 0 }, { .name = "MAIR_EL3", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 6, .crn = 10, .crm = 2, .opc2 = 0, @@ -2150,12 +2377,14 @@ static const ARMCPRegInfo v7_cp_reginfo[] = { * handled in the field definitions. */ { .name = "MAIR0", .state = ARM_CP_STATE_AA32, - .cp = 15, .opc1 = 0, .crn = 10, .crm = 2, .opc2 = 0, .access = PL1_RW, + .cp = 15, .opc1 = 0, .crn = 10, .crm = 2, .opc2 = 0, + .access = PL1_RW, .accessfn = access_tvm_trvm, .bank_fieldoffsets = { offsetof(CPUARMState, cp15.mair0_s), offsetof(CPUARMState, cp15.mair0_ns) }, .resetfn = arm_cp_reset_ignore }, { .name = "MAIR1", .state = ARM_CP_STATE_AA32, - .cp = 15, .opc1 = 0, .crn = 10, .crm = 2, .opc2 = 1, .access = PL1_RW, + .cp = 15, .opc1 = 0, .crn = 10, .crm = 2, .opc2 = 1, + .access = PL1_RW, .accessfn = access_tvm_trvm, .bank_fieldoffsets = { offsetof(CPUARMState, cp15.mair1_s), offsetof(CPUARMState, cp15.mair1_ns) }, .resetfn = arm_cp_reset_ignore }, @@ -2164,41 +2393,53 @@ static const ARMCPRegInfo v7_cp_reginfo[] = { .type = ARM_CP_NO_RAW, .access = PL1_R, .readfn = isr_read }, /* 32 bit ITLB invalidates */ { .name = "ITLBIALL", .cp = 15, .opc1 = 0, .crn = 8, .crm = 5, .opc2 = 0, - .type = ARM_CP_NO_RAW, .access = PL1_W, .writefn = tlbiall_write }, + .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, + .writefn = tlbiall_write }, { .name = "ITLBIMVA", .cp = 15, .opc1 = 0, .crn = 8, .crm = 5, .opc2 = 1, - .type = ARM_CP_NO_RAW, .access = PL1_W, .writefn = tlbimva_write }, + .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, + .writefn = tlbimva_write }, { .name = "ITLBIASID", .cp = 15, .opc1 = 0, .crn = 8, .crm = 5, .opc2 = 2, - .type = ARM_CP_NO_RAW, .access = PL1_W, .writefn = tlbiasid_write }, + .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, + .writefn = tlbiasid_write }, /* 32 bit DTLB invalidates */ { .name = "DTLBIALL", .cp = 15, .opc1 = 0, .crn = 8, .crm = 6, .opc2 = 0, - .type = ARM_CP_NO_RAW, .access = PL1_W, .writefn = tlbiall_write }, + .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, + .writefn = tlbiall_write }, { .name = "DTLBIMVA", .cp = 15, .opc1 = 0, .crn = 8, .crm = 6, .opc2 = 1, - .type = ARM_CP_NO_RAW, .access = PL1_W, .writefn = tlbimva_write }, + .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, + .writefn = tlbimva_write }, { .name = "DTLBIASID", .cp = 15, .opc1 = 0, .crn = 8, .crm = 6, .opc2 = 2, - .type = ARM_CP_NO_RAW, .access = PL1_W, .writefn = tlbiasid_write }, + .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, + .writefn = tlbiasid_write }, /* 32 bit TLB invalidates */ { .name = "TLBIALL", .cp = 15, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 0, - .type = ARM_CP_NO_RAW, .access = PL1_W, .writefn = tlbiall_write }, + .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, + .writefn = tlbiall_write }, { .name = "TLBIMVA", .cp = 15, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 1, - .type = ARM_CP_NO_RAW, .access = PL1_W, .writefn = tlbimva_write }, + .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, + .writefn = tlbimva_write }, { .name = "TLBIASID", .cp = 15, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 2, - .type = ARM_CP_NO_RAW, .access = PL1_W, .writefn = tlbiasid_write }, + .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, + .writefn = tlbiasid_write }, { .name = "TLBIMVAA", .cp = 15, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 3, - .type = ARM_CP_NO_RAW, .access = PL1_W, .writefn = tlbimvaa_write }, + .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, + .writefn = tlbimvaa_write }, REGINFO_SENTINEL }; static const ARMCPRegInfo v7mp_cp_reginfo[] = { /* 32 bit TLB invalidates, Inner Shareable */ { .name = "TLBIALLIS", .cp = 15, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 0, - .type = ARM_CP_NO_RAW, .access = PL1_W, .writefn = tlbiall_is_write }, + .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, + .writefn = tlbiall_is_write }, { .name = "TLBIMVAIS", .cp = 15, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 1, - .type = ARM_CP_NO_RAW, .access = PL1_W, .writefn = tlbimva_is_write }, + .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, + .writefn = tlbimva_is_write }, { .name = "TLBIASIDIS", .cp = 15, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 2, - .type = ARM_CP_NO_RAW, .access = PL1_W, + .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, .writefn = tlbiasid_is_write }, { .name = "TLBIMVAAIS", .cp = 15, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 3, - .type = ARM_CP_NO_RAW, .access = PL1_W, + .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, .writefn = tlbimvaa_is_write }, REGINFO_SENTINEL }; @@ -2289,10 +2530,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; @@ -2320,17 +2569,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. == '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. == '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. == 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; } @@ -2340,19 +2619,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. == '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. == '10': check CNTHCTL_EL2.EL1PTEN. */ + if (!extract32(env->cp15.cnthctl_el2, 11, 1)) { + return CP_ACCESS_TRAP_EL2; + } + } else { + /* If HCR_EL2. == 0: check CNTHCTL_EL2.EL1PCEN. */ + if (!extract32(env->cp15.cnthctl_el2, 1, 1)) { + return CP_ACCESS_TRAP_EL2; + } + } + } + break; } return CP_ACCESS_OK; } @@ -2412,7 +2713,9 @@ static CPAccessResult gt_stimer_access(CPUARMState *env, static uint64_t gt_get_countervalue(CPUARMState *env) { - return qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) / GTIMER_SCALE; + ARMCPU *cpu = env_archcpu(env); + + return qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) / gt_cntfrq_period_ns(cpu); } static void gt_recalc_timer(ARMCPU *cpu, int timeridx) @@ -2448,10 +2751,11 @@ static void gt_recalc_timer(ARMCPU *cpu, int timeridx) * set the timer for as far in the future as possible. When the * timer expires we will reset the timer for any remaining period. */ - if (nexttick > INT64_MAX / GTIMER_SCALE) { - nexttick = INT64_MAX / GTIMER_SCALE; + if (nexttick > INT64_MAX / gt_cntfrq_period_ns(cpu)) { + timer_mod_ns(cpu->gt_timer[timeridx], INT64_MAX); + } else { + timer_mod(cpu->gt_timer[timeridx], nexttick); } - timer_mod(cpu->gt_timer[timeridx], nexttick); trace_arm_gt_recalc(timeridx, irqstate, nexttick); } else { /* Timer disabled: ISTATUS and timer output always clear */ @@ -2475,9 +2779,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, @@ -2492,7 +2818,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)); @@ -2502,7 +2835,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 + @@ -2561,6 +2901,72 @@ 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: + case ARMMMUIdx_E20_2_PAN: + 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: + case ARMMMUIdx_E20_2_PAN: + 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); @@ -2599,6 +3005,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); @@ -2655,6 +3103,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; @@ -2683,6 +3159,20 @@ 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); + + cpu->env.cp15.c14_cntfrq = cpu->gt_cntfrq_hz; +} + static const ARMCPRegInfo generic_timer_cp_reginfo[] = { /* Note that CNTFRQ is purely reads-as-written for the benefit * of software; writing it doesn't actually change the timer frequency. @@ -2697,7 +3187,7 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = { .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 0, .opc2 = 0, .access = PL1_RW | PL0_R, .accessfn = gt_cntfrq_access, .fieldoffset = offsetof(CPUARMState, cp15.c14_cntfrq), - .resetvalue = (1000 * 1000 * 1000) / GTIMER_SCALE, + .resetfn = arm_gt_cntfrq_reset, }, /* overall control: mostly access permissions */ { .name = "CNTKCTL", .state = ARM_CP_STATE_BOTH, @@ -2713,7 +3203,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, @@ -2730,14 +3221,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, @@ -2745,14 +3238,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, @@ -2765,18 +3259,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, @@ -2806,7 +3300,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, @@ -2822,14 +3317,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, @@ -2837,7 +3334,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. @@ -2868,6 +3366,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 @@ -2876,11 +3383,13 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = { static uint64_t gt_virt_cnt_read(CPUARMState *env, const ARMCPRegInfo *ri) { + ARMCPU *cpu = env_archcpu(env); + /* Currently we have no support for QEMUTimer in linux-user so we * can't call gt_get_countervalue(env), instead we directly * call the lower level functions. */ - return cpu_get_clock() / GTIMER_SCALE; + return cpu_get_clock() / gt_cntfrq_period_ns(cpu); } static const ARMCPRegInfo generic_timer_cp_reginfo[] = { @@ -2960,7 +3469,7 @@ 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)) { + && arm_mmu_idx_is_stage1_of_2(mmu_idx)) { /* * Synchronous stage 2 fault on an access made as part of the * translation table walk for AT S1E0* or AT S1E1* insn @@ -3036,7 +3545,9 @@ 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 || + mmu_idx == ARMMMUIdx_E10_1_PAN) { format64 |= env->cp15.hcr_el2 & (HCR_VM | HCR_DC); } else { format64 |= arm_current_el(env) == 2; @@ -3102,16 +3613,21 @@ static void ats_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) switch (ri->opc2 & 6) { case 0: - /* stage 1 current state PL1: ATS1CPR, ATS1CPW */ + /* stage 1 current state PL1: ATS1CPR, ATS1CPW, ATS1CPRP, ATS1CPWP */ switch (el) { case 3: - mmu_idx = ARMMMUIdx_S1E3; + mmu_idx = ARMMMUIdx_SE3; break; case 2: - mmu_idx = ARMMMUIdx_S1NSE1; - break; + g_assert(!secure); /* TODO: ARMv8.4-SecEL2 */ + /* fall through */ case 1: - mmu_idx = secure ? ARMMMUIdx_S1SE1 : ARMMMUIdx_S1NSE1; + if (ri->crm == 9 && (env->uncached_cpsr & CPSR_PAN)) { + mmu_idx = (secure ? ARMMMUIdx_SE10_1_PAN + : ARMMMUIdx_Stage1_E1_PAN); + } else { + mmu_idx = secure ? ARMMMUIdx_SE10_1 : ARMMMUIdx_Stage1_E1; + } break; default: g_assert_not_reached(); @@ -3121,13 +3637,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(); @@ -3135,11 +3651,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(); @@ -3156,7 +3672,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); } @@ -3180,27 +3696,32 @@ static void ats_write64(CPUARMState *env, const ARMCPRegInfo *ri, switch (ri->opc2 & 6) { case 0: switch (ri->opc1) { - case 0: /* AT S1E1R, AT S1E1W */ - mmu_idx = secure ? ARMMMUIdx_S1SE1 : ARMMMUIdx_S1NSE1; + case 0: /* AT S1E1R, AT S1E1W, AT S1E1RP, AT S1E1WP */ + if (ri->crm == 9 && (env->pstate & PSTATE_PAN)) { + mmu_idx = (secure ? ARMMMUIdx_SE10_1_PAN + : ARMMMUIdx_Stage1_E1_PAN); + } else { + 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(); @@ -3461,7 +3982,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); @@ -3484,38 +4005,62 @@ 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_2_PAN | + 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_1_PAN | + ARMMMUIdxBit_E10_0 | + ARMMMUIdxBit_Stage2); raw_write(env, ri, value); } } static const ARMCPRegInfo vmsa_pmsa_cp_reginfo[] = { { .name = "DFSR", .cp = 15, .crn = 5, .crm = 0, .opc1 = 0, .opc2 = 0, - .access = PL1_RW, .type = ARM_CP_ALIAS, + .access = PL1_RW, .accessfn = access_tvm_trvm, .type = ARM_CP_ALIAS, .bank_fieldoffsets = { offsetoflow32(CPUARMState, cp15.dfsr_s), offsetoflow32(CPUARMState, cp15.dfsr_ns) }, }, { .name = "IFSR", .cp = 15, .crn = 5, .crm = 0, .opc1 = 0, .opc2 = 1, - .access = PL1_RW, .resetvalue = 0, + .access = PL1_RW, .accessfn = access_tvm_trvm, .resetvalue = 0, .bank_fieldoffsets = { offsetoflow32(CPUARMState, cp15.ifsr_s), offsetoflow32(CPUARMState, cp15.ifsr_ns) } }, { .name = "DFAR", .cp = 15, .opc1 = 0, .crn = 6, .crm = 0, .opc2 = 0, - .access = PL1_RW, .resetvalue = 0, + .access = PL1_RW, .accessfn = access_tvm_trvm, .resetvalue = 0, .bank_fieldoffsets = { offsetof(CPUARMState, cp15.dfar_s), offsetof(CPUARMState, cp15.dfar_ns) } }, { .name = "FAR_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .crn = 6, .crm = 0, .opc1 = 0, .opc2 = 0, - .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.far_el[1]), + .access = PL1_RW, .accessfn = access_tvm_trvm, + .fieldoffset = offsetof(CPUARMState, cp15.far_el[1]), .resetvalue = 0, }, REGINFO_SENTINEL }; @@ -3523,25 +4068,29 @@ static const ARMCPRegInfo vmsa_pmsa_cp_reginfo[] = { static const ARMCPRegInfo vmsa_cp_reginfo[] = { { .name = "ESR_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .crn = 5, .crm = 2, .opc1 = 0, .opc2 = 0, - .access = PL1_RW, + .access = PL1_RW, .accessfn = access_tvm_trvm, .fieldoffset = offsetof(CPUARMState, cp15.esr_el[1]), .resetvalue = 0, }, { .name = "TTBR0_EL1", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 2, .crm = 0, .opc2 = 0, - .access = PL1_RW, .writefn = vmsa_ttbr_write, .resetvalue = 0, + .access = PL1_RW, .accessfn = access_tvm_trvm, + .writefn = vmsa_ttbr_write, .resetvalue = 0, .bank_fieldoffsets = { offsetof(CPUARMState, cp15.ttbr0_s), offsetof(CPUARMState, cp15.ttbr0_ns) } }, { .name = "TTBR1_EL1", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 2, .crm = 0, .opc2 = 1, - .access = PL1_RW, .writefn = vmsa_ttbr_write, .resetvalue = 0, + .access = PL1_RW, .accessfn = access_tvm_trvm, + .writefn = vmsa_ttbr_write, .resetvalue = 0, .bank_fieldoffsets = { offsetof(CPUARMState, cp15.ttbr1_s), offsetof(CPUARMState, cp15.ttbr1_ns) } }, { .name = "TCR_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .crn = 2, .crm = 0, .opc1 = 0, .opc2 = 2, - .access = PL1_RW, .writefn = vmsa_tcr_el1_write, + .access = PL1_RW, .accessfn = access_tvm_trvm, + .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, - .access = PL1_RW, .type = ARM_CP_ALIAS, .writefn = vmsa_ttbcr_write, + .access = PL1_RW, .accessfn = access_tvm_trvm, + .type = ARM_CP_ALIAS, .writefn = vmsa_ttbcr_write, .raw_writefn = vmsa_ttbcr_raw_write, .bank_fieldoffsets = { offsetoflow32(CPUARMState, cp15.tcr_el[3]), offsetoflow32(CPUARMState, cp15.tcr_el[1])} }, @@ -3553,7 +4102,8 @@ static const ARMCPRegInfo vmsa_cp_reginfo[] = { */ static const ARMCPRegInfo ttbcr2_reginfo = { .name = "TTBCR2", .cp = 15, .opc1 = 0, .crn = 2, .crm = 0, .opc2 = 3, - .access = PL1_RW, .type = ARM_CP_ALIAS, + .access = PL1_RW, .accessfn = access_tvm_trvm, + .type = ARM_CP_ALIAS, .bank_fieldoffsets = { offsetofhigh32(CPUARMState, cp15.tcr_el[3]), offsetofhigh32(CPUARMState, cp15.tcr_el[1]) }, }; @@ -3773,23 +4323,25 @@ static const ARMCPRegInfo lpae_cp_reginfo[] = { /* NOP AMAIR0/1 */ { .name = "AMAIR0", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .crn = 10, .crm = 3, .opc1 = 0, .opc2 = 0, - .access = PL1_RW, .type = ARM_CP_CONST, - .resetvalue = 0 }, + .access = PL1_RW, .accessfn = access_tvm_trvm, + .type = ARM_CP_CONST, .resetvalue = 0 }, /* AMAIR1 is mapped to AMAIR_EL1[63:32] */ { .name = "AMAIR1", .cp = 15, .crn = 10, .crm = 3, .opc1 = 0, .opc2 = 1, - .access = PL1_RW, .type = ARM_CP_CONST, - .resetvalue = 0 }, + .access = PL1_RW, .accessfn = access_tvm_trvm, + .type = ARM_CP_CONST, .resetvalue = 0 }, { .name = "PAR", .cp = 15, .crm = 7, .opc1 = 0, .access = PL1_RW, .type = ARM_CP_64BIT, .resetvalue = 0, .bank_fieldoffsets = { offsetof(CPUARMState, cp15.par_s), offsetof(CPUARMState, cp15.par_ns)} }, { .name = "TTBR0", .cp = 15, .crm = 2, .opc1 = 0, - .access = PL1_RW, .type = ARM_CP_64BIT | ARM_CP_ALIAS, + .access = PL1_RW, .accessfn = access_tvm_trvm, + .type = ARM_CP_64BIT | ARM_CP_ALIAS, .bank_fieldoffsets = { offsetof(CPUARMState, cp15.ttbr0_s), offsetof(CPUARMState, cp15.ttbr0_ns) }, .writefn = vmsa_ttbr_write, }, { .name = "TTBR1", .cp = 15, .crm = 2, .opc1 = 1, - .access = PL1_RW, .type = ARM_CP_64BIT | ARM_CP_ALIAS, + .access = PL1_RW, .accessfn = access_tvm_trvm, + .type = ARM_CP_64BIT | ARM_CP_ALIAS, .bank_fieldoffsets = { offsetof(CPUARMState, cp15.ttbr1_s), offsetof(CPUARMState, cp15.ttbr1_ns) }, .writefn = vmsa_ttbr_write, }, @@ -3821,7 +4373,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; @@ -3833,15 +4385,82 @@ static void aa64_daif_write(CPUARMState *env, const ARMCPRegInfo *ri, env->daif = value & PSTATE_DAIF; } -static CPAccessResult aa64_cacheop_access(CPUARMState *env, - const ARMCPRegInfo *ri, - bool isread) +static uint64_t aa64_pan_read(CPUARMState *env, const ARMCPRegInfo *ri) { - /* 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)) { - return CP_ACCESS_TRAP; + return env->pstate & PSTATE_PAN; +} + +static void aa64_pan_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + env->pstate = (env->pstate & ~PSTATE_PAN) | (value & PSTATE_PAN); +} + +static const ARMCPRegInfo pan_reginfo = { + .name = "PAN", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 4, .crm = 2, .opc2 = 3, + .type = ARM_CP_NO_RAW, .access = PL1_RW, + .readfn = aa64_pan_read, .writefn = aa64_pan_write +}; + +static uint64_t aa64_uao_read(CPUARMState *env, const ARMCPRegInfo *ri) +{ + return env->pstate & PSTATE_UAO; +} + +static void aa64_uao_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + env->pstate = (env->pstate & ~PSTATE_UAO) | (value & PSTATE_UAO); +} + +static const ARMCPRegInfo uao_reginfo = { + .name = "UAO", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 4, .crm = 2, .opc2 = 4, + .type = ARM_CP_NO_RAW, .access = PL1_RW, + .readfn = aa64_uao_read, .writefn = aa64_uao_write +}; + +static CPAccessResult aa64_cacheop_poc_access(CPUARMState *env, + const ARMCPRegInfo *ri, + bool isread) +{ + /* Cache invalidate/clean to Point of Coherency or Persistence... */ + switch (arm_current_el(env)) { + case 0: + /* ... EL0 must UNDEF unless SCTLR_EL1.UCI is set. */ + if (!(arm_sctlr(env, 0) & SCTLR_UCI)) { + return CP_ACCESS_TRAP; + } + /* fall through */ + case 1: + /* ... EL1 must trap to EL2 if HCR_EL2.TPCP is set. */ + if (arm_hcr_el2_eff(env) & HCR_TPCP) { + return CP_ACCESS_TRAP_EL2; + } + break; + } + return CP_ACCESS_OK; +} + +static CPAccessResult aa64_cacheop_pou_access(CPUARMState *env, + const ARMCPRegInfo *ri, + bool isread) +{ + /* Cache invalidate/clean to Point of Unification... */ + switch (arm_current_el(env)) { + case 0: + /* ... EL0 must UNDEF unless SCTLR_EL1.UCI is set. */ + if (!(arm_sctlr(env, 0) & SCTLR_UCI)) { + return CP_ACCESS_TRAP; + } + /* fall through */ + case 1: + /* ... EL1 must trap to EL2 if HCR_EL2.TPU is set. */ + if (arm_hcr_el2_eff(env) & HCR_TPU) { + return CP_ACCESS_TRAP_EL2; + } + break; } return CP_ACCESS_OK; } @@ -3850,79 +4469,95 @@ 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_1_PAN | + ARMMMUIdxBit_SE10_0; + } else if ((env->cp15.hcr_el2 & (HCR_E2H | HCR_TGE)) + == (HCR_E2H | HCR_TGE)) { + return ARMMMUIdxBit_E20_2 | + ARMMMUIdxBit_E20_2_PAN | + ARMMMUIdxBit_E20_0; + } else { + return ARMMMUIdxBit_E10_1 | + ARMMMUIdxBit_E10_1_PAN | + 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 int alle1_tlbmask(CPUARMState *env) +{ + /* + * Note that the 'ALL' scope must invalidate both stage 1 and + * stage 2 translations, whereas most other scopes only invalidate + * stage 1 translations. + */ + if (arm_is_secure_below_el3(env)) { + return ARMMMUIdxBit_SE10_1 | + ARMMMUIdxBit_SE10_1_PAN | + ARMMMUIdxBit_SE10_0; + } else if (arm_feature(env, ARM_FEATURE_EL2)) { + return ARMMMUIdxBit_E10_1 | + ARMMMUIdxBit_E10_1_PAN | + ARMMMUIdxBit_E10_0 | + ARMMMUIdxBit_Stage2; + } else { + return ARMMMUIdxBit_E10_1 | + ARMMMUIdxBit_E10_1_PAN | + ARMMMUIdxBit_E10_0; + } +} + +static int e2_tlbmask(CPUARMState *env) +{ + /* TODO: ARMv8.4-SecEL2 */ + return ARMMMUIdxBit_E20_0 | + ARMMMUIdxBit_E20_2 | + ARMMMUIdxBit_E20_2_PAN | + ARMMMUIdxBit_E2; +} + static void tlbi_aa64_alle1_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. - */ - ARMCPU *cpu = env_archcpu(env); - CPUState *cs = CPU(cpu); + CPUState *cs = env_cpu(env); + int mask = alle1_tlbmask(env); - if (arm_is_secure_below_el3(env)) { - tlb_flush_by_mmuidx(cs, - ARMMMUIdxBit_S1SE1 | - ARMMMUIdxBit_S1SE0); - } 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); - } - } + 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, @@ -3931,42 +4566,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); + int mask = alle1_tlbmask(env); - 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); - } + 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, @@ -3974,7 +4592,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, @@ -3984,11 +4602,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, @@ -4002,26 +4620,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, @@ -4032,23 +4641,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); } } @@ -4059,7 +4659,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, @@ -4069,7 +4669,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, @@ -4091,7 +4691,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, @@ -4107,17 +4707,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; } @@ -4246,86 +4862,89 @@ static const ARMCPRegInfo v8_cp_reginfo[] = { /* Cache ops: all NOPs since we don't emulate caches */ { .name = "IC_IALLUIS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 1, .opc2 = 0, - .access = PL1_W, .type = ARM_CP_NOP }, + .access = PL1_W, .type = ARM_CP_NOP, + .accessfn = aa64_cacheop_pou_access }, { .name = "IC_IALLU", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 5, .opc2 = 0, - .access = PL1_W, .type = ARM_CP_NOP }, + .access = PL1_W, .type = ARM_CP_NOP, + .accessfn = aa64_cacheop_pou_access }, { .name = "IC_IVAU", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 3, .crn = 7, .crm = 5, .opc2 = 1, .access = PL0_W, .type = ARM_CP_NOP, - .accessfn = aa64_cacheop_access }, + .accessfn = aa64_cacheop_pou_access }, { .name = "DC_IVAC", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 6, .opc2 = 1, - .access = PL1_W, .type = ARM_CP_NOP }, + .access = PL1_W, .accessfn = aa64_cacheop_poc_access, + .type = ARM_CP_NOP }, { .name = "DC_ISW", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 6, .opc2 = 2, - .access = PL1_W, .type = ARM_CP_NOP }, + .access = PL1_W, .accessfn = access_tsw, .type = ARM_CP_NOP }, { .name = "DC_CVAC", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 3, .crn = 7, .crm = 10, .opc2 = 1, .access = PL0_W, .type = ARM_CP_NOP, - .accessfn = aa64_cacheop_access }, + .accessfn = aa64_cacheop_poc_access }, { .name = "DC_CSW", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 10, .opc2 = 2, - .access = PL1_W, .type = ARM_CP_NOP }, + .access = PL1_W, .accessfn = access_tsw, .type = ARM_CP_NOP }, { .name = "DC_CVAU", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 3, .crn = 7, .crm = 11, .opc2 = 1, .access = PL0_W, .type = ARM_CP_NOP, - .accessfn = aa64_cacheop_access }, + .accessfn = aa64_cacheop_pou_access }, { .name = "DC_CIVAC", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 3, .crn = 7, .crm = 14, .opc2 = 1, .access = PL0_W, .type = ARM_CP_NOP, - .accessfn = aa64_cacheop_access }, + .accessfn = aa64_cacheop_poc_access }, { .name = "DC_CISW", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 14, .opc2 = 2, - .access = PL1_W, .type = ARM_CP_NOP }, + .access = PL1_W, .accessfn = access_tsw, .type = ARM_CP_NOP }, /* TLBI operations */ { .name = "TLBI_VMALLE1IS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 0, - .access = PL1_W, .type = ARM_CP_NO_RAW, + .access = PL1_W, .accessfn = access_ttlb, .type = ARM_CP_NO_RAW, .writefn = tlbi_aa64_vmalle1is_write }, { .name = "TLBI_VAE1IS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 1, - .access = PL1_W, .type = ARM_CP_NO_RAW, + .access = PL1_W, .accessfn = access_ttlb, .type = ARM_CP_NO_RAW, .writefn = tlbi_aa64_vae1is_write }, { .name = "TLBI_ASIDE1IS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 2, - .access = PL1_W, .type = ARM_CP_NO_RAW, + .access = PL1_W, .accessfn = access_ttlb, .type = ARM_CP_NO_RAW, .writefn = tlbi_aa64_vmalle1is_write }, { .name = "TLBI_VAAE1IS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 3, - .access = PL1_W, .type = ARM_CP_NO_RAW, + .access = PL1_W, .accessfn = access_ttlb, .type = ARM_CP_NO_RAW, .writefn = tlbi_aa64_vae1is_write }, { .name = "TLBI_VALE1IS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 5, - .access = PL1_W, .type = ARM_CP_NO_RAW, + .access = PL1_W, .accessfn = access_ttlb, .type = ARM_CP_NO_RAW, .writefn = tlbi_aa64_vae1is_write }, { .name = "TLBI_VAALE1IS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 7, - .access = PL1_W, .type = ARM_CP_NO_RAW, + .access = PL1_W, .accessfn = access_ttlb, .type = ARM_CP_NO_RAW, .writefn = tlbi_aa64_vae1is_write }, { .name = "TLBI_VMALLE1", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 0, - .access = PL1_W, .type = ARM_CP_NO_RAW, + .access = PL1_W, .accessfn = access_ttlb, .type = ARM_CP_NO_RAW, .writefn = tlbi_aa64_vmalle1_write }, { .name = "TLBI_VAE1", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 1, - .access = PL1_W, .type = ARM_CP_NO_RAW, + .access = PL1_W, .accessfn = access_ttlb, .type = ARM_CP_NO_RAW, .writefn = tlbi_aa64_vae1_write }, { .name = "TLBI_ASIDE1", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 2, - .access = PL1_W, .type = ARM_CP_NO_RAW, + .access = PL1_W, .accessfn = access_ttlb, .type = ARM_CP_NO_RAW, .writefn = tlbi_aa64_vmalle1_write }, { .name = "TLBI_VAAE1", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 3, - .access = PL1_W, .type = ARM_CP_NO_RAW, + .access = PL1_W, .accessfn = access_ttlb, .type = ARM_CP_NO_RAW, .writefn = tlbi_aa64_vae1_write }, { .name = "TLBI_VALE1", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 5, - .access = PL1_W, .type = ARM_CP_NO_RAW, + .access = PL1_W, .accessfn = access_ttlb, .type = ARM_CP_NO_RAW, .writefn = tlbi_aa64_vae1_write }, { .name = "TLBI_VAALE1", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 7, - .access = PL1_W, .type = ARM_CP_NO_RAW, + .access = PL1_W, .accessfn = access_ttlb, .type = ARM_CP_NO_RAW, .writefn = tlbi_aa64_vae1_write }, { .name = "TLBI_IPAS2E1IS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 0, .opc2 = 1, @@ -4411,14 +5030,17 @@ static const ARMCPRegInfo v8_cp_reginfo[] = { #endif /* TLB invalidate last level of translation table walk */ { .name = "TLBIMVALIS", .cp = 15, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 5, - .type = ARM_CP_NO_RAW, .access = PL1_W, .writefn = tlbimva_is_write }, + .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, + .writefn = tlbimva_is_write }, { .name = "TLBIMVAALIS", .cp = 15, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 7, - .type = ARM_CP_NO_RAW, .access = PL1_W, + .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, .writefn = tlbimvaa_is_write }, { .name = "TLBIMVAL", .cp = 15, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 5, - .type = ARM_CP_NO_RAW, .access = PL1_W, .writefn = tlbimva_write }, + .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, + .writefn = tlbimva_write }, { .name = "TLBIMVAAL", .cp = 15, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 7, - .type = ARM_CP_NO_RAW, .access = PL1_W, .writefn = tlbimvaa_write }, + .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, + .writefn = tlbimvaa_write }, { .name = "TLBIMVALH", .cp = 15, .opc1 = 4, .crn = 8, .crm = 7, .opc2 = 5, .type = ARM_CP_NO_RAW, .access = PL2_W, .writefn = tlbimva_hyp_write }, @@ -4444,34 +5066,34 @@ static const ARMCPRegInfo v8_cp_reginfo[] = { .writefn = tlbiipas2_is_write }, /* 32 bit cache operations */ { .name = "ICIALLUIS", .cp = 15, .opc1 = 0, .crn = 7, .crm = 1, .opc2 = 0, - .type = ARM_CP_NOP, .access = PL1_W }, + .type = ARM_CP_NOP, .access = PL1_W, .accessfn = aa64_cacheop_pou_access }, { .name = "BPIALLUIS", .cp = 15, .opc1 = 0, .crn = 7, .crm = 1, .opc2 = 6, .type = ARM_CP_NOP, .access = PL1_W }, { .name = "ICIALLU", .cp = 15, .opc1 = 0, .crn = 7, .crm = 5, .opc2 = 0, - .type = ARM_CP_NOP, .access = PL1_W }, + .type = ARM_CP_NOP, .access = PL1_W, .accessfn = aa64_cacheop_pou_access }, { .name = "ICIMVAU", .cp = 15, .opc1 = 0, .crn = 7, .crm = 5, .opc2 = 1, - .type = ARM_CP_NOP, .access = PL1_W }, + .type = ARM_CP_NOP, .access = PL1_W, .accessfn = aa64_cacheop_pou_access }, { .name = "BPIALL", .cp = 15, .opc1 = 0, .crn = 7, .crm = 5, .opc2 = 6, .type = ARM_CP_NOP, .access = PL1_W }, { .name = "BPIMVA", .cp = 15, .opc1 = 0, .crn = 7, .crm = 5, .opc2 = 7, .type = ARM_CP_NOP, .access = PL1_W }, { .name = "DCIMVAC", .cp = 15, .opc1 = 0, .crn = 7, .crm = 6, .opc2 = 1, - .type = ARM_CP_NOP, .access = PL1_W }, + .type = ARM_CP_NOP, .access = PL1_W, .accessfn = aa64_cacheop_poc_access }, { .name = "DCISW", .cp = 15, .opc1 = 0, .crn = 7, .crm = 6, .opc2 = 2, - .type = ARM_CP_NOP, .access = PL1_W }, + .type = ARM_CP_NOP, .access = PL1_W, .accessfn = access_tsw }, { .name = "DCCMVAC", .cp = 15, .opc1 = 0, .crn = 7, .crm = 10, .opc2 = 1, - .type = ARM_CP_NOP, .access = PL1_W }, + .type = ARM_CP_NOP, .access = PL1_W, .accessfn = aa64_cacheop_poc_access }, { .name = "DCCSW", .cp = 15, .opc1 = 0, .crn = 7, .crm = 10, .opc2 = 2, - .type = ARM_CP_NOP, .access = PL1_W }, + .type = ARM_CP_NOP, .access = PL1_W, .accessfn = access_tsw }, { .name = "DCCMVAU", .cp = 15, .opc1 = 0, .crn = 7, .crm = 11, .opc2 = 1, - .type = ARM_CP_NOP, .access = PL1_W }, + .type = ARM_CP_NOP, .access = PL1_W, .accessfn = aa64_cacheop_pou_access }, { .name = "DCCIMVAC", .cp = 15, .opc1 = 0, .crn = 7, .crm = 14, .opc2 = 1, - .type = ARM_CP_NOP, .access = PL1_W }, + .type = ARM_CP_NOP, .access = PL1_W, .accessfn = aa64_cacheop_poc_access }, { .name = "DCCISW", .cp = 15, .opc1 = 0, .crn = 7, .crm = 14, .opc2 = 2, - .type = ARM_CP_NOP, .access = PL1_W }, + .type = ARM_CP_NOP, .access = PL1_W, .accessfn = access_tsw }, /* MMU Domain access control / MPU write buffer control */ { .name = "DACR", .cp = 15, .opc1 = 0, .crn = 3, .crm = 0, .opc2 = 0, - .access = PL1_RW, .resetvalue = 0, + .access = PL1_RW, .accessfn = access_tvm_trvm, .resetvalue = 0, .writefn = dacr_write, .raw_writefn = raw_write, .bank_fieldoffsets = { offsetoflow32(CPUARMState, cp15.dacr_s), offsetoflow32(CPUARMState, cp15.dacr_ns) } }, @@ -4669,10 +5291,15 @@ static const ARMCPRegInfo el3_no_el2_v8_cp_reginfo[] = { REGINFO_SENTINEL }; -static void hcr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) +static void do_hcr_write(CPUARMState *env, uint64_t value, uint64_t valid_mask) { ARMCPU *cpu = env_archcpu(env); - uint64_t valid_mask = HCR_MASK; + + if (arm_feature(env, ARM_FEATURE_V8)) { + valid_mask |= MAKE_64BIT_MASK(0, 34); /* ARMv8.0 */ + } else { + valid_mask |= MAKE_64BIT_MASK(0, 28); /* ARMv7VE */ + } if (arm_feature(env, ARM_FEATURE_EL3)) { valid_mask &= ~HCR_HCD; @@ -4686,11 +5313,17 @@ static void hcr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) */ valid_mask &= ~HCR_TSC; } - if (cpu_isar_feature(aa64_lor, cpu)) { - valid_mask |= HCR_TLOR; - } - if (cpu_isar_feature(aa64_pauth, cpu)) { - valid_mask |= HCR_API | HCR_APK; + + if (arm_feature(env, ARM_FEATURE_AARCH64)) { + if (cpu_isar_feature(aa64_vh, cpu)) { + valid_mask |= HCR_E2H; + } + if (cpu_isar_feature(aa64_lor, cpu)) { + valid_mask |= HCR_TLOR; + } + if (cpu_isar_feature(aa64_pauth, cpu)) { + valid_mask |= HCR_API | HCR_APK; + } } /* Clear RES0 bits. */ @@ -4722,12 +5355,17 @@ static void hcr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) arm_cpu_update_vfiq(cpu); } +static void hcr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) +{ + do_hcr_write(env, value, 0); +} + static void hcr_writehigh(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { /* Handle HCR2 write, i.e. write to high half of HCR_EL2 */ value = deposit64(env->cp15.hcr_el2, 32, 32, value); - hcr_write(env, NULL, value); + do_hcr_write(env, value, MAKE_64BIT_MASK(0, 32)); } static void hcr_writelow(CPUARMState *env, const ARMCPRegInfo *ri, @@ -4735,7 +5373,7 @@ static void hcr_writelow(CPUARMState *env, const ARMCPRegInfo *ri, { /* Handle HCR write, i.e. write to low half of HCR_EL2 */ value = deposit64(env->cp15.hcr_el2, 0, 32, value); - hcr_write(env, NULL, value); + do_hcr_write(env, value, MAKE_64BIT_MASK(32, 32)); } /* @@ -4763,14 +5401,37 @@ uint64_t arm_hcr_el2_eff(CPUARMState *env) * Since the v8.4 language applies to the entire register, and * appears to be backward compatible, use that. */ - ret = 0; - } else if (ret & HCR_TGE) { - /* These bits are up-to-date as of ARMv8.4. */ + return 0; + } + + /* + * For a cpu that supports both aarch64 and aarch32, we can set bits + * in HCR_EL2 (e.g. via EL3) that are RES0 when we enter EL2 as aa32. + * Ignore all of the bits in HCR+HCR2 that are not valid for aarch32. + */ + if (!arm_el_is_aa64(env, 2)) { + uint64_t aa32_valid; + + /* + * These bits are up-to-date as of ARMv8.6. + * For HCR, it's easiest to list just the 2 bits that are invalid. + * For HCR2, list those that are valid. + */ + aa32_valid = MAKE_64BIT_MASK(0, 32) & ~(HCR_RW | HCR_TDZ); + aa32_valid |= (HCR_CD | HCR_ID | HCR_TERR | HCR_TEA | HCR_MIOCNCE | + HCR_TID4 | HCR_TICAB | HCR_TOCU | HCR_TTLBIS); + ret &= aa32_valid; + } + + if (ret & HCR_TGE) { + /* These bits are up-to-date as of ARMv8.6. */ if (ret & HCR_E2H) { ret &= ~(HCR_VM | HCR_FMO | HCR_IMO | HCR_AMO | HCR_BSU_MASK | HCR_DC | HCR_TWI | HCR_TWE | HCR_TID0 | HCR_TID2 | HCR_TPCP | HCR_TPU | - HCR_TDZ | HCR_CD | HCR_ID | HCR_MIOCNCE); + HCR_TDZ | HCR_CD | HCR_ID | HCR_MIOCNCE | + HCR_TID4 | HCR_TICAB | HCR_TOCU | HCR_ENSCXT | + HCR_TTLBIS | HCR_TTLBOS | HCR_TID5); } else { ret |= HCR_FMO | HCR_IMO | HCR_AMO; } @@ -4889,10 +5550,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, @@ -4926,7 +5585,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, @@ -5096,7 +5755,7 @@ static const ARMCPRegInfo el3_cp_reginfo[] = { .opc0 = 3, .opc1 = 6, .crn = 1, .crm = 1, .opc2 = 0, .access = PL3_RW, .fieldoffset = offsetof(CPUARMState, cp15.scr_el3), .resetvalue = 0, .writefn = scr_write }, - { .name = "SCR", .type = ARM_CP_ALIAS, + { .name = "SCR", .type = ARM_CP_ALIAS | ARM_CP_NEWEL, .cp = 15, .opc1 = 0, .crn = 1, .crm = 1, .opc2 = 0, .access = PL1_RW, .accessfn = access_trap_aa32s_el1, .fieldoffset = offsetoflow32(CPUARMState, cp15.scr_el3), @@ -5195,15 +5854,188 @@ 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) { + return CP_ACCESS_TRAP_EL2; + } + return CP_ACCESS_OK; } @@ -5313,7 +6145,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: @@ -5328,8 +6162,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. */ @@ -5507,17 +6340,18 @@ void hw_watchpoint_update(ARMCPU *cpu, int n) int bas = extract64(wcr, 5, 8); int basstart; - if (bas == 0) { - /* This must act as if the watchpoint is disabled */ - return; - } - if (extract64(wvr, 2, 1)) { /* Deprecated case of an only 4-aligned address. BAS[7:4] are * ignored, and BAS[3:0] define which bytes to watch. */ bas &= 0xf; } + + if (bas == 0) { + /* This must act as if the watchpoint is disabled */ + return; + } + /* The BAS bits are supposed to be programmed to indicate a contiguous * range of bytes. Otherwise it is CONSTRAINED UNPREDICTABLE whether * we fire for each byte in the word/doubleword addressed by the WVR. @@ -5702,26 +6536,16 @@ static void define_debug_regs(ARMCPU *cpu) ARMCPRegInfo dbgdidr = { .name = "DBGDIDR", .cp = 14, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = 0, .access = PL0_R, .accessfn = access_tda, - .type = ARM_CP_CONST, .resetvalue = cpu->dbgdidr, + .type = ARM_CP_CONST, .resetvalue = cpu->isar.dbgdidr, }; /* Note that all these register fields hold "number of Xs minus 1". */ - brps = extract32(cpu->dbgdidr, 24, 4); - wrps = extract32(cpu->dbgdidr, 28, 4); - ctx_cmps = extract32(cpu->dbgdidr, 20, 4); + brps = arm_num_brps(cpu); + wrps = arm_num_wrps(cpu); + ctx_cmps = arm_num_ctx_cmps(cpu); assert(ctx_cmps <= brps); - /* The DBGDIDR and ID_AA64DFR0_EL1 define various properties - * of the debug registers such as number of breakpoints; - * check that if they both exist then they agree. - */ - if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) { - assert(extract32(cpu->id_aa64dfr0, 12, 4) == brps); - assert(extract32(cpu->id_aa64dfr0, 20, 4) == wrps); - assert(extract32(cpu->id_aa64dfr0, 28, 4) == ctx_cmps); - } - define_one_arm_cp_reg(cpu, &dbgdidr); define_arm_cp_regs(cpu, debug_cp_reginfo); @@ -5729,7 +6553,7 @@ static void define_debug_regs(ARMCPU *cpu) define_arm_cp_regs(cpu, debug_lpae_cp_reginfo); } - for (i = 0; i < brps + 1; i++) { + for (i = 0; i < brps; i++) { ARMCPRegInfo dbgregs[] = { { .name = "DBGBVR", .state = ARM_CP_STATE_BOTH, .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 0, .crm = i, .opc2 = 4, @@ -5748,7 +6572,7 @@ static void define_debug_regs(ARMCPU *cpu) define_arm_cp_regs(cpu, dbgregs); } - for (i = 0; i < wrps + 1; i++) { + for (i = 0; i < wrps; i++) { ARMCPRegInfo dbgregs[] = { { .name = "DBGWVR", .state = ARM_CP_STATE_BOTH, .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 0, .crm = i, .opc2 = 6, @@ -5768,6 +6592,96 @@ static void define_debug_regs(ARMCPU *cpu) } } +static void define_pmu_regs(ARMCPU *cpu) +{ + /* + * v7 performance monitor control register: same implementor + * field as main ID register, and we implement four counters in + * addition to the cycle count register. + */ + unsigned int i, pmcrn = 4; + ARMCPRegInfo pmcr = { + .name = "PMCR", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 0, + .access = PL0_RW, + .type = ARM_CP_IO | ARM_CP_ALIAS, + .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pmcr), + .accessfn = pmreg_access, .writefn = pmcr_write, + .raw_writefn = raw_write, + }; + ARMCPRegInfo pmcr64 = { + .name = "PMCR_EL0", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 0, + .access = PL0_RW, .accessfn = pmreg_access, + .type = ARM_CP_IO, + .fieldoffset = offsetof(CPUARMState, cp15.c9_pmcr), + .resetvalue = (cpu->midr & 0xff000000) | (pmcrn << PMCRN_SHIFT) | + PMCRLC, + .writefn = pmcr_write, .raw_writefn = raw_write, + }; + define_one_arm_cp_reg(cpu, &pmcr); + define_one_arm_cp_reg(cpu, &pmcr64); + for (i = 0; i < pmcrn; i++) { + char *pmevcntr_name = g_strdup_printf("PMEVCNTR%d", i); + char *pmevcntr_el0_name = g_strdup_printf("PMEVCNTR%d_EL0", i); + char *pmevtyper_name = g_strdup_printf("PMEVTYPER%d", i); + char *pmevtyper_el0_name = g_strdup_printf("PMEVTYPER%d_EL0", i); + ARMCPRegInfo pmev_regs[] = { + { .name = pmevcntr_name, .cp = 15, .crn = 14, + .crm = 8 | (3 & (i >> 3)), .opc1 = 0, .opc2 = i & 7, + .access = PL0_RW, .type = ARM_CP_IO | ARM_CP_ALIAS, + .readfn = pmevcntr_readfn, .writefn = pmevcntr_writefn, + .accessfn = pmreg_access }, + { .name = pmevcntr_el0_name, .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 8 | (3 & (i >> 3)), + .opc2 = i & 7, .access = PL0_RW, .accessfn = pmreg_access, + .type = ARM_CP_IO, + .readfn = pmevcntr_readfn, .writefn = pmevcntr_writefn, + .raw_readfn = pmevcntr_rawread, + .raw_writefn = pmevcntr_rawwrite }, + { .name = pmevtyper_name, .cp = 15, .crn = 14, + .crm = 12 | (3 & (i >> 3)), .opc1 = 0, .opc2 = i & 7, + .access = PL0_RW, .type = ARM_CP_IO | ARM_CP_ALIAS, + .readfn = pmevtyper_readfn, .writefn = pmevtyper_writefn, + .accessfn = pmreg_access }, + { .name = pmevtyper_el0_name, .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 12 | (3 & (i >> 3)), + .opc2 = i & 7, .access = PL0_RW, .accessfn = pmreg_access, + .type = ARM_CP_IO, + .readfn = pmevtyper_readfn, .writefn = pmevtyper_writefn, + .raw_writefn = pmevtyper_rawwrite }, + REGINFO_SENTINEL + }; + define_arm_cp_regs(cpu, pmev_regs); + g_free(pmevcntr_name); + g_free(pmevcntr_el0_name); + g_free(pmevtyper_name); + g_free(pmevtyper_el0_name); + } + if (cpu_isar_feature(aa32_pmu_8_1, cpu)) { + ARMCPRegInfo v81_pmu_regs[] = { + { .name = "PMCEID2", .state = ARM_CP_STATE_AA32, + .cp = 15, .opc1 = 0, .crn = 9, .crm = 14, .opc2 = 4, + .access = PL0_R, .accessfn = pmreg_access, .type = ARM_CP_CONST, + .resetvalue = extract64(cpu->pmceid0, 32, 32) }, + { .name = "PMCEID3", .state = ARM_CP_STATE_AA32, + .cp = 15, .opc1 = 0, .crn = 9, .crm = 14, .opc2 = 5, + .access = PL0_R, .accessfn = pmreg_access, .type = ARM_CP_CONST, + .resetvalue = extract64(cpu->pmceid1, 32, 32) }, + REGINFO_SENTINEL + }; + define_arm_cp_regs(cpu, v81_pmu_regs); + } + if (cpu_isar_feature(any_pmu_8_4, cpu)) { + static const ARMCPRegInfo v84_pmmir = { + .name = "PMMIR_EL1", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 0, .crn = 9, .crm = 14, .opc2 = 6, + .access = PL1_R, .accessfn = pmreg_access, .type = ARM_CP_CONST, + .resetvalue = 0 + }; + define_one_arm_cp_reg(cpu, &v84_pmmir); + } +} + /* We don't know until after realize whether there's a GICv3 * attached, and that is what registers the gicv3 sysregs. * So we have to fill in the GIC fields in ID_PFR/ID_PFR1_EL1/ID_AA64PFR0_EL1 @@ -5784,6 +6698,7 @@ static uint64_t id_pfr1_read(CPUARMState *env, const ARMCPRegInfo *ri) return pfr1; } +#ifndef CONFIG_USER_ONLY static uint64_t id_aa64pfr0_read(CPUARMState *env, const ARMCPRegInfo *ri) { ARMCPU *cpu = env_archcpu(env); @@ -5794,6 +6709,7 @@ static uint64_t id_aa64pfr0_read(CPUARMState *env, const ARMCPRegInfo *ri) } return pfr0; } +#endif /* Shared logic between LORID and the rest of the LOR* registers. * Secure state has already been delt with. @@ -5831,6 +6747,35 @@ static CPAccessResult access_lor_other(CPUARMState *env, return access_lor_ns(env); } +/* + * A trivial implementation of ARMv8.1-LOR leaves all of these + * registers fixed at 0, which indicates that there are zero + * supported Limited Ordering regions. + */ +static const ARMCPRegInfo lor_reginfo[] = { + { .name = "LORSA_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 10, .crm = 4, .opc2 = 0, + .access = PL1_RW, .accessfn = access_lor_other, + .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "LOREA_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 10, .crm = 4, .opc2 = 1, + .access = PL1_RW, .accessfn = access_lor_other, + .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "LORN_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 10, .crm = 4, .opc2 = 2, + .access = PL1_RW, .accessfn = access_lor_other, + .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "LORC_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 10, .crm = 4, .opc2 = 3, + .access = PL1_RW, .accessfn = access_lor_other, + .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "LORID_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 10, .crm = 4, .opc2 = 7, + .access = PL1_R, .accessfn = access_lorid, + .type = ARM_CP_CONST, .resetvalue = 0 }, + REGINFO_SENTINEL +}; + #ifdef TARGET_AARCH64 static CPAccessResult access_pauth(CPUARMState *env, const ARMCPRegInfo *ri, bool isread) @@ -5932,6 +6877,52 @@ static const ARMCPRegInfo rndr_reginfo[] = { .access = PL0_R, .readfn = rndr_readfn }, REGINFO_SENTINEL }; + +#ifndef CONFIG_USER_ONLY +static void dccvap_writefn(CPUARMState *env, const ARMCPRegInfo *opaque, + uint64_t value) +{ + ARMCPU *cpu = env_archcpu(env); + /* CTR_EL0 System register -> DminLine, bits [19:16] */ + uint64_t dline_size = 4 << ((cpu->ctr >> 16) & 0xF); + uint64_t vaddr_in = (uint64_t) value; + uint64_t vaddr = vaddr_in & ~(dline_size - 1); + void *haddr; + int mem_idx = cpu_mmu_index(env, false); + + /* This won't be crossing page boundaries */ + haddr = probe_read(env, vaddr, dline_size, mem_idx, GETPC()); + if (haddr) { + + ram_addr_t offset; + MemoryRegion *mr; + + /* RCU lock is already being held */ + mr = memory_region_from_host(haddr, &offset); + + if (mr) { + memory_region_do_writeback(mr, offset, dline_size); + } + } +} + +static const ARMCPRegInfo dcpop_reg[] = { + { .name = "DC_CVAP", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 3, .crn = 7, .crm = 12, .opc2 = 1, + .access = PL0_W, .type = ARM_CP_NO_RAW | ARM_CP_SUPPRESS_TB_END, + .accessfn = aa64_cacheop_poc_access, .writefn = dccvap_writefn }, + REGINFO_SENTINEL +}; + +static const ARMCPRegInfo dcpodp_reg[] = { + { .name = "DC_CVADP", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 3, .crn = 7, .crm = 13, .opc2 = 1, + .access = PL0_W, .type = ARM_CP_NO_RAW | ARM_CP_SUPPRESS_TB_END, + .accessfn = aa64_cacheop_poc_access, .writefn = dccvap_writefn }, + REGINFO_SENTINEL +}; +#endif /*CONFIG_USER_ONLY*/ + #endif static CPAccessResult access_predinv(CPUARMState *env, const ARMCPRegInfo *ri, @@ -5978,6 +6969,21 @@ static const ARMCPRegInfo predinv_reginfo[] = { REGINFO_SENTINEL }; +static uint64_t ccsidr2_read(CPUARMState *env, const ARMCPRegInfo *ri) +{ + /* Read the high 32 bits of the current CCSIDR */ + return extract64(ccsidr_read(env, ri), 32, 32); +} + +static const ARMCPRegInfo ccsidr2_reginfo[] = { + { .name = "CCSIDR2", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 1, .crn = 0, .crm = 0, .opc2 = 2, + .access = PL1_R, + .accessfn = access_aa64_tid2, + .readfn = ccsidr2_read, .type = ARM_CP_NO_RAW }, + REGINFO_SENTINEL +}; + static CPAccessResult access_aa64_tid3(CPUARMState *env, const ARMCPRegInfo *ri, bool isread) { @@ -5998,6 +7004,142 @@ static CPAccessResult access_aa32_tid3(CPUARMState *env, const ARMCPRegInfo *ri, return CP_ACCESS_OK; } +static CPAccessResult access_jazelle(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) +{ + if (arm_current_el(env) == 1 && (arm_hcr_el2_eff(env) & HCR_TID0)) { + return CP_ACCESS_TRAP_EL2; + } + + return CP_ACCESS_OK; +} + +static const ARMCPRegInfo jazelle_regs[] = { + { .name = "JIDR", + .cp = 14, .crn = 0, .crm = 0, .opc1 = 7, .opc2 = 0, + .access = PL1_R, .accessfn = access_jazelle, + .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "JOSCR", + .cp = 14, .crn = 1, .crm = 0, .opc1 = 7, .opc2 = 0, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "JMCR", + .cp = 14, .crn = 2, .crm = 0, .opc1 = 7, .opc2 = 0, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + 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 +}; + +#ifndef CONFIG_USER_ONLY +static const ARMCPRegInfo ats1e1_reginfo[] = { + { .name = "AT_S1E1R", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 9, .opc2 = 0, + .access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, + .writefn = ats_write64 }, + { .name = "AT_S1E1W", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 9, .opc2 = 1, + .access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, + .writefn = ats_write64 }, + REGINFO_SENTINEL +}; + +static const ARMCPRegInfo ats1cp_reginfo[] = { + { .name = "ATS1CPRP", + .cp = 15, .opc1 = 0, .crn = 7, .crm = 9, .opc2 = 0, + .access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, + .writefn = ats_write }, + { .name = "ATS1CPWP", + .cp = 15, .opc1 = 0, .crn = 7, .crm = 9, .opc2 = 1, + .access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, + .writefn = ats_write }, + REGINFO_SENTINEL +}; +#endif + +/* + * ACTLR2 and HACTLR2 map to ACTLR_EL1[63:32] and + * ACTLR_EL2[63:32]. They exist only if the ID_MMFR4.AC2 field + * is non-zero, which is never for ARMv7, optionally in ARMv8 + * and mandatorily for ARMv8.2 and up. + * ACTLR2 is banked for S and NS if EL3 is AArch32. Since QEMU's + * implementation is RAZ/WI we can ignore this detail, as we + * do for ACTLR. + */ +static const ARMCPRegInfo actlr2_hactlr2_reginfo[] = { + { .name = "ACTLR2", .state = ARM_CP_STATE_AA32, + .cp = 15, .opc1 = 0, .crn = 1, .crm = 0, .opc2 = 3, + .access = PL1_RW, .accessfn = access_tacr, + .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "HACTLR2", .state = ARM_CP_STATE_AA32, + .cp = 15, .opc1 = 4, .crn = 1, .crm = 0, .opc2 = 3, + .access = PL2_RW, .type = ARM_CP_CONST, + .resetvalue = 0 }, + REGINFO_SENTINEL +}; + void register_cp_regs_for_features(ARMCPU *cpu) { /* Register all the coprocessor registers based on feature bits */ @@ -6036,7 +7178,7 @@ void register_cp_regs_for_features(ARMCPU *cpu) .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 1, .opc2 = 2, .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa32_tid3, - .resetvalue = cpu->id_dfr0 }, + .resetvalue = cpu->isar.id_dfr0 }, { .name = "ID_AFR0", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 1, .opc2 = 3, .access = PL1_R, .type = ARM_CP_CONST, @@ -6046,22 +7188,22 @@ void register_cp_regs_for_features(ARMCPU *cpu) .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 1, .opc2 = 4, .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa32_tid3, - .resetvalue = cpu->id_mmfr0 }, + .resetvalue = cpu->isar.id_mmfr0 }, { .name = "ID_MMFR1", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 1, .opc2 = 5, .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa32_tid3, - .resetvalue = cpu->id_mmfr1 }, + .resetvalue = cpu->isar.id_mmfr1 }, { .name = "ID_MMFR2", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 1, .opc2 = 6, .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa32_tid3, - .resetvalue = cpu->id_mmfr2 }, + .resetvalue = cpu->isar.id_mmfr2 }, { .name = "ID_MMFR3", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 1, .opc2 = 7, .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa32_tid3, - .resetvalue = cpu->id_mmfr3 }, + .resetvalue = cpu->isar.id_mmfr3 }, { .name = "ID_ISAR0", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 2, .opc2 = 0, .access = PL1_R, .type = ARM_CP_CONST, @@ -6096,7 +7238,7 @@ void register_cp_regs_for_features(ARMCPU *cpu) .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 2, .opc2 = 6, .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa32_tid3, - .resetvalue = cpu->id_mmfr4 }, + .resetvalue = cpu->isar.id_mmfr4 }, { .name = "ID_ISAR6", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 2, .opc2 = 7, .access = PL1_R, .type = ARM_CP_CONST, @@ -6120,93 +7262,20 @@ void register_cp_regs_for_features(ARMCPU *cpu) define_arm_cp_regs(cpu, pmovsset_cp_reginfo); } if (arm_feature(env, ARM_FEATURE_V7)) { - /* v7 performance monitor control register: same implementor - * field as main ID register, and we implement four counters in - * addition to the cycle count register. - */ - unsigned int i, pmcrn = 4; - ARMCPRegInfo pmcr = { - .name = "PMCR", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 0, - .access = PL0_RW, - .type = ARM_CP_IO | ARM_CP_ALIAS, - .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pmcr), - .accessfn = pmreg_access, .writefn = pmcr_write, - .raw_writefn = raw_write, - }; - ARMCPRegInfo pmcr64 = { - .name = "PMCR_EL0", .state = ARM_CP_STATE_AA64, - .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 0, - .access = PL0_RW, .accessfn = pmreg_access, - .type = ARM_CP_IO, - .fieldoffset = offsetof(CPUARMState, cp15.c9_pmcr), - .resetvalue = (cpu->midr & 0xff000000) | (pmcrn << PMCRN_SHIFT), - .writefn = pmcr_write, .raw_writefn = raw_write, - }; - define_one_arm_cp_reg(cpu, &pmcr); - define_one_arm_cp_reg(cpu, &pmcr64); - for (i = 0; i < pmcrn; i++) { - char *pmevcntr_name = g_strdup_printf("PMEVCNTR%d", i); - char *pmevcntr_el0_name = g_strdup_printf("PMEVCNTR%d_EL0", i); - char *pmevtyper_name = g_strdup_printf("PMEVTYPER%d", i); - char *pmevtyper_el0_name = g_strdup_printf("PMEVTYPER%d_EL0", i); - ARMCPRegInfo pmev_regs[] = { - { .name = pmevcntr_name, .cp = 15, .crn = 14, - .crm = 8 | (3 & (i >> 3)), .opc1 = 0, .opc2 = i & 7, - .access = PL0_RW, .type = ARM_CP_IO | ARM_CP_ALIAS, - .readfn = pmevcntr_readfn, .writefn = pmevcntr_writefn, - .accessfn = pmreg_access }, - { .name = pmevcntr_el0_name, .state = ARM_CP_STATE_AA64, - .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 8 | (3 & (i >> 3)), - .opc2 = i & 7, .access = PL0_RW, .accessfn = pmreg_access, - .type = ARM_CP_IO, - .readfn = pmevcntr_readfn, .writefn = pmevcntr_writefn, - .raw_readfn = pmevcntr_rawread, - .raw_writefn = pmevcntr_rawwrite }, - { .name = pmevtyper_name, .cp = 15, .crn = 14, - .crm = 12 | (3 & (i >> 3)), .opc1 = 0, .opc2 = i & 7, - .access = PL0_RW, .type = ARM_CP_IO | ARM_CP_ALIAS, - .readfn = pmevtyper_readfn, .writefn = pmevtyper_writefn, - .accessfn = pmreg_access }, - { .name = pmevtyper_el0_name, .state = ARM_CP_STATE_AA64, - .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 12 | (3 & (i >> 3)), - .opc2 = i & 7, .access = PL0_RW, .accessfn = pmreg_access, - .type = ARM_CP_IO, - .readfn = pmevtyper_readfn, .writefn = pmevtyper_writefn, - .raw_writefn = pmevtyper_rawwrite }, - REGINFO_SENTINEL - }; - define_arm_cp_regs(cpu, pmev_regs); - g_free(pmevcntr_name); - g_free(pmevcntr_el0_name); - g_free(pmevtyper_name); - g_free(pmevtyper_el0_name); - } ARMCPRegInfo clidr = { .name = "CLIDR", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .crn = 0, .crm = 0, .opc1 = 1, .opc2 = 1, - .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = cpu->clidr + .access = PL1_R, .type = ARM_CP_CONST, + .accessfn = access_aa64_tid2, + .resetvalue = cpu->clidr }; define_one_arm_cp_reg(cpu, &clidr); define_arm_cp_regs(cpu, v7_cp_reginfo); define_debug_regs(cpu); + define_pmu_regs(cpu); } else { define_arm_cp_regs(cpu, not_v7_cp_reginfo); } - if (FIELD_EX32(cpu->id_dfr0, ID_DFR0, PERFMON) >= 4 && - FIELD_EX32(cpu->id_dfr0, ID_DFR0, PERFMON) != 0xf) { - ARMCPRegInfo v81_pmu_regs[] = { - { .name = "PMCEID2", .state = ARM_CP_STATE_AA32, - .cp = 15, .opc1 = 0, .crn = 9, .crm = 14, .opc2 = 4, - .access = PL0_R, .accessfn = pmreg_access, .type = ARM_CP_CONST, - .resetvalue = extract64(cpu->pmceid0, 32, 32) }, - { .name = "PMCEID3", .state = ARM_CP_STATE_AA32, - .cp = 15, .opc1 = 0, .crn = 9, .crm = 14, .opc2 = 5, - .access = PL0_R, .accessfn = pmreg_access, .type = ARM_CP_CONST, - .resetvalue = extract64(cpu->pmceid1, 32, 32) }, - REGINFO_SENTINEL - }; - define_arm_cp_regs(cpu, v81_pmu_regs); - } if (arm_feature(env, ARM_FEATURE_V8)) { /* AArch64 ID registers, which all have impdef reset values. * Note that within the ID register ranges the unused slots @@ -6214,16 +7283,24 @@ void register_cp_regs_for_features(ARMCPU *cpu) * define new registers here. */ ARMCPRegInfo v8_idregs[] = { - /* ID_AA64PFR0_EL1 is not a plain ARM_CP_CONST because we don't - * know the right value for the GIC field until after we - * define these regs. + /* + * ID_AA64PFR0_EL1 is not a plain ARM_CP_CONST in system + * emulation because we don't know the right value for the + * GIC field until after we define these regs. */ { .name = "ID_AA64PFR0_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 4, .opc2 = 0, - .access = PL1_R, .type = ARM_CP_NO_RAW, + .access = PL1_R, +#ifdef CONFIG_USER_ONLY + .type = ARM_CP_CONST, + .resetvalue = cpu->isar.id_aa64pfr0 +#else + .type = ARM_CP_NO_RAW, .accessfn = access_aa64_tid3, .readfn = id_aa64pfr0_read, - .writefn = arm_cp_write_ignore }, + .writefn = arm_cp_write_ignore +#endif + }, { .name = "ID_AA64PFR1_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 4, .opc2 = 1, .access = PL1_R, .type = ARM_CP_CONST, @@ -6264,12 +7341,12 @@ void register_cp_regs_for_features(ARMCPU *cpu) .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 5, .opc2 = 0, .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa64_tid3, - .resetvalue = cpu->id_aa64dfr0 }, + .resetvalue = cpu->isar.id_aa64dfr0 }, { .name = "ID_AA64DFR1_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 5, .opc2 = 1, .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa64_tid3, - .resetvalue = cpu->id_aa64dfr1 }, + .resetvalue = cpu->isar.id_aa64dfr1 }, { .name = "ID_AA64DFR2_EL1_RESERVED", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 5, .opc2 = 2, .access = PL1_R, .type = ARM_CP_CONST, @@ -6350,11 +7427,11 @@ void register_cp_regs_for_features(ARMCPU *cpu) .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa64_tid3, .resetvalue = cpu->isar.id_aa64mmfr1 }, - { .name = "ID_AA64MMFR2_EL1_RESERVED", .state = ARM_CP_STATE_AA64, + { .name = "ID_AA64MMFR2_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 7, .opc2 = 2, .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa64_tid3, - .resetvalue = 0 }, + .resetvalue = cpu->isar.id_aa64mmfr2 }, { .name = "ID_AA64MMFR3_EL1_RESERVED", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 7, .opc2 = 3, .access = PL1_R, .type = ARM_CP_CONST, @@ -6617,8 +7694,8 @@ void register_cp_regs_for_features(ARMCPU *cpu) } else { define_arm_cp_regs(cpu, vmsa_pmsa_cp_reginfo); define_arm_cp_regs(cpu, vmsa_cp_reginfo); - /* TTCBR2 is introduced with ARMv8.2-A32HPD. */ - if (FIELD_EX32(cpu->id_mmfr4, ID_MMFR4, HPDS) != 0) { + /* TTCBR2 is introduced with ARMv8.2-AA32HPD. */ + if (cpu_isar_feature(aa32_hpd, cpu)) { define_one_arm_cp_reg(cpu, &ttbcr2_reginfo); } } @@ -6655,6 +7732,9 @@ void register_cp_regs_for_features(ARMCPU *cpu) if (arm_feature(env, ARM_FEATURE_LPAE)) { define_arm_cp_regs(cpu, lpae_cp_reginfo); } + if (cpu_isar_feature(aa32_jazelle, cpu)) { + define_arm_cp_regs(cpu, jazelle_regs); + } /* Slightly awkwardly, the OMAP and StrongARM cores need all of * cp15 crn=0 to be writes-ignored, whereas for other cores they should * be read-only (ie write causes UNDEF exception). @@ -6710,14 +7790,17 @@ void register_cp_regs_for_features(ARMCPU *cpu) .access = PL1_R, .resetvalue = cpu->midr }, { .name = "REVIDR_EL1", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 0, .opc2 = 6, - .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = cpu->revidr }, + .access = PL1_R, + .accessfn = access_aa64_tid1, + .type = ARM_CP_CONST, .resetvalue = cpu->revidr }, REGINFO_SENTINEL }; ARMCPRegInfo id_cp_reginfo[] = { /* These are common to v8 and pre-v8 */ { .name = "CTR", .cp = 15, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = 1, - .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = cpu->ctr }, + .access = PL1_R, .accessfn = ctr_el0_access, + .type = ARM_CP_CONST, .resetvalue = cpu->ctr }, { .name = "CTR_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .opc2 = 1, .crn = 0, .crm = 0, .access = PL0_R, .accessfn = ctr_el0_access, @@ -6725,14 +7808,18 @@ void register_cp_regs_for_features(ARMCPU *cpu) /* TCMTR and TLBTR exist in v8 but have no 64-bit versions */ { .name = "TCMTR", .cp = 15, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = 2, - .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = 0 }, + .access = PL1_R, + .accessfn = access_aa32_tid1, + .type = ARM_CP_CONST, .resetvalue = 0 }, REGINFO_SENTINEL }; /* TLBTR is specific to VMSA */ ARMCPRegInfo id_tlbtr_reginfo = { .name = "TLBTR", .cp = 15, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = 3, - .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = 0, + .access = PL1_R, + .accessfn = access_aa32_tid1, + .type = ARM_CP_CONST, .resetvalue = 0, }; /* MPUIR is specific to PMSA V6+ */ ARMCPRegInfo id_mpuir_reginfo = { @@ -6809,8 +7896,8 @@ void register_cp_regs_for_features(ARMCPU *cpu) ARMCPRegInfo auxcr_reginfo[] = { { .name = "ACTLR_EL1", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 1, .crm = 0, .opc2 = 1, - .access = PL1_RW, .type = ARM_CP_CONST, - .resetvalue = cpu->reset_auxcr }, + .access = PL1_RW, .accessfn = access_tacr, + .type = ARM_CP_CONST, .resetvalue = cpu->reset_auxcr }, { .name = "ACTLR_EL2", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 0, .opc2 = 1, .access = PL2_RW, .type = ARM_CP_CONST, @@ -6822,15 +7909,8 @@ void register_cp_regs_for_features(ARMCPU *cpu) REGINFO_SENTINEL }; define_arm_cp_regs(cpu, auxcr_reginfo); - if (arm_feature(env, ARM_FEATURE_V8)) { - /* HACTLR2 maps to ACTLR_EL2[63:32] and is not in ARMv7 */ - ARMCPRegInfo hactlr2_reginfo = { - .name = "HACTLR2", .state = ARM_CP_STATE_AA32, - .cp = 15, .opc1 = 4, .crn = 1, .crm = 0, .opc2 = 3, - .access = PL2_RW, .type = ARM_CP_CONST, - .resetvalue = 0 - }; - define_one_arm_cp_reg(cpu, &hactlr2_reginfo); + if (cpu_isar_feature(aa32_ac2, cpu)) { + define_arm_cp_regs(cpu, actlr2_hactlr2_reginfo); } } @@ -6901,7 +7981,7 @@ void register_cp_regs_for_features(ARMCPU *cpu) ARMCPRegInfo sctlr = { .name = "SCTLR", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 1, .crm = 0, .opc2 = 0, - .access = PL1_RW, + .access = PL1_RW, .accessfn = access_tvm_trvm, .bank_fieldoffsets = { offsetof(CPUARMState, cp15.sctlr_s), offsetof(CPUARMState, cp15.sctlr_ns) }, .writefn = sctlr_write, .resetvalue = cpu->reset_sctlr, @@ -6918,36 +7998,26 @@ void register_cp_regs_for_features(ARMCPU *cpu) } if (cpu_isar_feature(aa64_lor, cpu)) { - /* - * A trivial implementation of ARMv8.1-LOR leaves all of these - * registers fixed at 0, which indicates that there are zero - * supported Limited Ordering regions. - */ - static const ARMCPRegInfo lor_reginfo[] = { - { .name = "LORSA_EL1", .state = ARM_CP_STATE_AA64, - .opc0 = 3, .opc1 = 0, .crn = 10, .crm = 4, .opc2 = 0, - .access = PL1_RW, .accessfn = access_lor_other, - .type = ARM_CP_CONST, .resetvalue = 0 }, - { .name = "LOREA_EL1", .state = ARM_CP_STATE_AA64, - .opc0 = 3, .opc1 = 0, .crn = 10, .crm = 4, .opc2 = 1, - .access = PL1_RW, .accessfn = access_lor_other, - .type = ARM_CP_CONST, .resetvalue = 0 }, - { .name = "LORN_EL1", .state = ARM_CP_STATE_AA64, - .opc0 = 3, .opc1 = 0, .crn = 10, .crm = 4, .opc2 = 2, - .access = PL1_RW, .accessfn = access_lor_other, - .type = ARM_CP_CONST, .resetvalue = 0 }, - { .name = "LORC_EL1", .state = ARM_CP_STATE_AA64, - .opc0 = 3, .opc1 = 0, .crn = 10, .crm = 4, .opc2 = 3, - .access = PL1_RW, .accessfn = access_lor_other, - .type = ARM_CP_CONST, .resetvalue = 0 }, - { .name = "LORID_EL1", .state = ARM_CP_STATE_AA64, - .opc0 = 3, .opc1 = 0, .crn = 10, .crm = 4, .opc2 = 7, - .access = PL1_R, .accessfn = access_lorid, - .type = ARM_CP_CONST, .resetvalue = 0 }, - REGINFO_SENTINEL - }; define_arm_cp_regs(cpu, lor_reginfo); } + if (cpu_isar_feature(aa64_pan, cpu)) { + define_one_arm_cp_reg(cpu, &pan_reginfo); + } +#ifndef CONFIG_USER_ONLY + if (cpu_isar_feature(aa64_ats1e1, cpu)) { + define_arm_cp_regs(cpu, ats1e1_reginfo); + } + if (cpu_isar_feature(aa32_ats1e1, cpu)) { + define_arm_cp_regs(cpu, ats1cp_reginfo); + } +#endif + if (cpu_isar_feature(aa64_uao, cpu)) { + define_one_arm_cp_reg(cpu, &uao_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); @@ -6968,18 +8038,35 @@ void register_cp_regs_for_features(ARMCPU *cpu) if (cpu_isar_feature(aa64_rndr, cpu)) { define_arm_cp_regs(cpu, rndr_reginfo); } +#ifndef CONFIG_USER_ONLY + /* Data Cache clean instructions up to PoP */ + if (cpu_isar_feature(aa64_dcpop, cpu)) { + define_one_arm_cp_reg(cpu, dcpop_reg); + + if (cpu_isar_feature(aa64_dcpodp, cpu)) { + define_one_arm_cp_reg(cpu, dcpodp_reg); + } + } +#endif /*CONFIG_USER_ONLY*/ #endif - /* - * While all v8.0 cpus support aarch64, QEMU does have configurations - * that do not set ID_AA64ISAR1, e.g. user-only qemu-arm -cpu max, - * which will set ID_ISAR6. - */ - if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64) - ? cpu_isar_feature(aa64_predinv, cpu) - : cpu_isar_feature(aa32_predinv, cpu)) { + if (cpu_isar_feature(any_predinv, cpu)) { define_arm_cp_regs(cpu, predinv_reginfo); } + + if (cpu_isar_feature(any_ccidx, cpu)) { + define_arm_cp_regs(cpu, ccsidr2_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) @@ -6988,22 +8075,36 @@ void arm_cpu_register_gdb_regs_for_features(ARMCPU *cpu) CPUARMState *env = &cpu->env; if (arm_feature(env, ARM_FEATURE_AARCH64)) { - gdb_register_coprocessor(cs, aarch64_fpu_gdb_get_reg, - aarch64_fpu_gdb_set_reg, - 34, "aarch64-fpu.xml", 0); + /* + * The lower part of each SVE register aliases to the FPU + * registers so we don't need to include both. + */ +#ifdef TARGET_AARCH64 + if (isar_feature_aa64_sve(&cpu->isar)) { + gdb_register_coprocessor(cs, arm_gdb_get_svereg, arm_gdb_set_svereg, + arm_gen_dynamic_svereg_xml(cs, cs->gdb_num_regs), + "sve-registers.xml", 0); + } else +#endif + { + gdb_register_coprocessor(cs, aarch64_fpu_gdb_get_reg, + aarch64_fpu_gdb_set_reg, + 34, "aarch64-fpu.xml", 0); + } } else if (arm_feature(env, ARM_FEATURE_NEON)) { gdb_register_coprocessor(cs, vfp_gdb_get_reg, vfp_gdb_set_reg, 51, "arm-neon.xml", 0); - } else if (arm_feature(env, ARM_FEATURE_VFP3)) { + } else if (cpu_isar_feature(aa32_simd_r32, cpu)) { gdb_register_coprocessor(cs, vfp_gdb_get_reg, vfp_gdb_set_reg, 35, "arm-vfp3.xml", 0); - } else if (arm_feature(env, ARM_FEATURE_VFP)) { + } else if (cpu_isar_feature(aa32_vfp_simd, cpu)) { gdb_register_coprocessor(cs, vfp_gdb_get_reg, vfp_gdb_set_reg, 19, "arm-vfp.xml", 0); } gdb_register_coprocessor(cs, arm_gdb_get_sysreg, arm_gdb_set_sysreg, - arm_gen_dynamic_xml(cs), + arm_gen_dynamic_sysreg_xml(cs, cs->gdb_num_regs), "system-registers.xml", 0); + } /* Sort alphabetically by type name, except for "any". */ @@ -7275,13 +8376,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; @@ -7800,6 +8898,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]; @@ -8064,8 +9168,11 @@ static void take_aarch32_exception(CPUARMState *env, int new_mode, uint32_t mask, uint32_t offset, uint32_t newpc) { + int new_el; + /* Change the CPU state so as to actually take the exception. */ switch_mode(env, new_mode); + /* * For exceptions taken to AArch32 we must clear the SS bit in both * PSTATE and in the old-state value we save to SPSR_, so zero it now. @@ -8076,9 +9183,13 @@ static void take_aarch32_exception(CPUARMState *env, int new_mode, env->condexec_bits = 0; /* Switch to the new mode, and to the correct instruction set. */ env->uncached_cpsr = (env->uncached_cpsr & ~CPSR_M) | new_mode; + + /* This must be after mode switching. */ + new_el = arm_current_el(env); + /* Set new mode endianness */ env->uncached_cpsr &= ~CPSR_E; - if (env->cp15.sctlr_el[arm_current_el(env)] & SCTLR_EE) { + if (env->cp15.sctlr_el[new_el] & SCTLR_EE) { env->uncached_cpsr |= CPSR_E; } /* J and IL must always be cleared for exception entry */ @@ -8089,6 +9200,25 @@ static void take_aarch32_exception(CPUARMState *env, int new_mode, env->thumb = (env->cp15.sctlr_el[2] & SCTLR_TE) != 0; env->elr_el[2] = env->regs[15]; } else { + /* CPSR.PAN is normally preserved preserved unless... */ + if (cpu_isar_feature(aa32_pan, env_archcpu(env))) { + switch (new_el) { + case 3: + if (!arm_is_secure_below_el3(env)) { + /* ... the target is EL3, from non-secure state. */ + env->uncached_cpsr &= ~CPSR_PAN; + break; + } + /* ... the target is EL3, from secure state ... */ + /* fall through */ + case 1: + /* ... the target is EL1 and SCTLR.SPAN is 0. */ + if (!(env->cp15.sctlr_el[new_el] & SCTLR_SPAN)) { + env->uncached_cpsr |= CPSR_PAN; + } + break; + } + } /* * this is a lie, as there was no c1_sys on V4T/V5, but who cares * and we should just guard the thumb mode on V4 @@ -8351,6 +9481,7 @@ static void arm_cpu_do_interrupt_aarch64(CPUState *cs) unsigned int new_el = env->exception.target_el; target_ulong addr = env->cp15.vbar_el[new_el]; unsigned int new_mode = aarch64_pstate_mode(new_el, true); + unsigned int old_mode; unsigned int cur_el = arm_current_el(env); /* @@ -8364,14 +9495,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; @@ -8420,31 +9556,48 @@ static void arm_cpu_do_interrupt_aarch64(CPUState *cs) case EXCP_VFIQ: addr += 0x100; break; - case EXCP_SEMIHOST: - qemu_log_mask(CPU_LOG_INT, - "...handling as semihosting call 0x%" PRIx64 "\n", - env->xregs[0]); - env->xregs[0] = do_arm_semihosting(env); - return; default: cpu_abort(cs, "Unhandled exception 0x%x\n", cs->exception_index); } if (is_a64(env)) { - env->banked_spsr[aarch64_banked_spsr_index(new_el)] = pstate_read(env); + old_mode = pstate_read(env); aarch64_save_sp(env, arm_current_el(env)); env->elr_el[new_el] = env->pc; } else { - env->banked_spsr[aarch64_banked_spsr_index(new_el)] = cpsr_read(env); + old_mode = cpsr_read(env); env->elr_el[new_el] = env->regs[15]; aarch64_sync_32_to_64(env); env->condexec_bits = 0; } + env->banked_spsr[aarch64_banked_spsr_index(new_el)] = old_mode; + qemu_log_mask(CPU_LOG_INT, "...with ELR 0x%" PRIx64 "\n", env->elr_el[new_el]); + if (cpu_isar_feature(aa64_pan, cpu)) { + /* The value of PSTATE.PAN is normally preserved, except when ... */ + new_mode |= old_mode & PSTATE_PAN; + switch (new_el) { + case 2: + /* ... the target is EL2 with HCR_EL2.{E2H,TGE} == '11' ... */ + if ((arm_hcr_el2_eff(env) & (HCR_E2H | HCR_TGE)) + != (HCR_E2H | HCR_TGE)) { + break; + } + /* fall through */ + case 1: + /* ... the target is EL1 ... */ + /* ... and SCTLR_ELx.SPAN == 0, then set to 1. */ + if ((env->cp15.sctlr_el[new_el] & SCTLR_SPAN) == 0) { + new_mode |= PSTATE_PAN; + } + break; + } + } + pstate_write(env, PSTATE_DAIF | new_mode); env->aarch64 = 1; aarch64_restore_sp(env, new_el); @@ -8474,11 +9627,13 @@ static void handle_semihosting(CPUState *cs) "...handling as semihosting call 0x%" PRIx64 "\n", env->xregs[0]); env->xregs[0] = do_arm_semihosting(env); + env->pc += 4; } else { qemu_log_mask(CPU_LOG_INT, "...handling as semihosting call 0x%x\n", env->regs[0]); env->regs[0] = do_arm_semihosting(env); + env->regs[15] += env->thumb ? 2 : 4; } } #endif @@ -8548,19 +9703,27 @@ 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_E20_2_PAN: + 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_SE10_1_PAN: + case ARMMMUIdx_Stage1_E0: + case ARMMMUIdx_Stage1_E1: + case ARMMMUIdx_Stage1_E1_PAN: + case ARMMMUIdx_E10_0: + case ARMMMUIdx_E10_1: + case ARMMMUIdx_E10_1_PAN: case ARMMMUIdx_MPrivNegPri: case ARMMMUIdx_MUserNegPri: case ARMMMUIdx_MPriv: @@ -8575,14 +9738,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) @@ -8605,7 +9778,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; } @@ -8617,8 +9790,7 @@ static inline bool regime_translation_disabled(CPUARMState *env, } } - if ((env->cp15.hcr_el2 & HCR_DC) && - (mmu_idx == ARMMMUIdx_S1NSE0 || mmu_idx == ARMMMUIdx_S1NSE1)) { + if ((env->cp15.hcr_el2 & HCR_DC) && arm_mmu_idx_is_stage1_of_2(mmu_idx)) { /* HCR.DC means SCTLR_EL1.M behaves as 0 */ return true; } @@ -8636,7 +9808,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) { @@ -8651,7 +9823,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)]; @@ -8662,10 +9834,16 @@ 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; + case ARMMMUIdx_E10_1_PAN: + return ARMMMUIdx_Stage1_E1_PAN; + default: + return mmu_idx; } - return mmu_idx; } /* Return true if the translation regime is using LPAE format page tables */ @@ -8697,8 +9875,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: @@ -8706,8 +9885,9 @@ 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: + case ARMMMUIdx_E10_1_PAN: g_assert_not_reached(); } } @@ -8838,13 +10018,18 @@ 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) { prot_rw = user_rw; } else { - prot_rw = simple_ap_to_rw_prot_is_user(ap, false); + if (user_rw && regime_is_pan(env, mmu_idx)) { + /* PAN forbids data accesses but doesn't affect insn fetch */ + prot_rw = 0; + } else { + prot_rw = simple_ap_to_rw_prot_is_user(ap, false); + } } if (ns && arm_is_secure(env) && (env->cp15.scr_el3 & SCR_SIF)) { @@ -8863,15 +10048,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)) { @@ -8929,8 +10107,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 (arm_mmu_idx_is_stage1_of_2(mmu_idx) && + !regime_translation_disabled(env, ARMMMUIdx_Stage2)) { target_ulong s2size; hwaddr s2pa; int s2prot; @@ -8947,7 +10125,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); @@ -9401,74 +10579,89 @@ static uint8_t convert_stage2_attrs(CPUARMState *env, uint8_t s2attrs) } #endif /* !CONFIG_USER_ONLY */ -ARMVAParameters aa64_va_parameters_both(CPUARMState *env, uint64_t va, - ARMMMUIdx mmu_idx) +static int aa64_va_parameter_tbi(uint64_t tcr, 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; - - /* - * Bit 55 is always between the two regions, and is canonical for - * determining if address tagging is enabled. - */ - select = extract64(va, 55, 1); - - if (el > 1) { - tsz = extract32(tcr, 0, 6); - using64k = extract32(tcr, 14, 1); - using16k = extract32(tcr, 15, 1); - if (mmu_idx == ARMMMUIdx_S2NS) { - /* VTCR_EL2 */ - tbi = tbid = hpd = false; - } else { - tbi = extract32(tcr, 20, 1); - hpd = extract32(tcr, 24, 1); - tbid = extract32(tcr, 29, 1); - } - epd = false; - } else if (!select) { - tsz = extract32(tcr, 0, 6); - epd = extract32(tcr, 7, 1); - using64k = extract32(tcr, 14, 1); - using16k = extract32(tcr, 15, 1); - tbi = extract64(tcr, 37, 1); - hpd = extract64(tcr, 41, 1); - tbid = extract64(tcr, 51, 1); + if (regime_has_2_ranges(mmu_idx)) { + return extract64(tcr, 37, 2); + } else if (mmu_idx == ARMMMUIdx_Stage2) { + return 0; /* VTCR_EL2 */ } else { - int tg = extract32(tcr, 30, 2); - using16k = tg == 1; - using64k = tg == 3; - tsz = extract32(tcr, 16, 6); - epd = extract32(tcr, 23, 1); - tbi = extract64(tcr, 38, 1); - hpd = extract64(tcr, 42, 1); - tbid = extract64(tcr, 52, 1); + /* Replicate the single TBI bit so we always have 2 bits. */ + return extract32(tcr, 20, 1) * 3; } - tsz = MIN(tsz, 39); /* TODO: ARMv8.4-TTST */ - tsz = MAX(tsz, 16); /* TODO: ARMv8.2-LVA */ +} - return (ARMVAParameters) { - .tsz = tsz, - .select = select, - .tbi = tbi, - .tbid = tbid, - .epd = epd, - .hpd = hpd, - .using16k = using16k, - .using64k = using64k, - }; +static int aa64_va_parameter_tbid(uint64_t tcr, ARMMMUIdx mmu_idx) +{ + if (regime_has_2_ranges(mmu_idx)) { + return extract64(tcr, 51, 2); + } else if (mmu_idx == ARMMMUIdx_Stage2) { + return 0; /* VTCR_EL2 */ + } else { + /* Replicate the single TBID bit so we always have 2 bits. */ + return extract32(tcr, 29, 1) * 3; + } } ARMVAParameters aa64_va_parameters(CPUARMState *env, uint64_t va, ARMMMUIdx mmu_idx, bool data) { - ARMVAParameters ret = aa64_va_parameters_both(env, va, mmu_idx); + uint64_t tcr = regime_tcr(env, mmu_idx)->raw_tcr; + bool epd, hpd, using16k, using64k; + int select, tsz, tbi; + + if (!regime_has_2_ranges(mmu_idx)) { + select = 0; + tsz = extract32(tcr, 0, 6); + using64k = extract32(tcr, 14, 1); + using16k = extract32(tcr, 15, 1); + if (mmu_idx == ARMMMUIdx_Stage2) { + /* VTCR_EL2 */ + hpd = false; + } else { + hpd = extract32(tcr, 24, 1); + } + epd = false; + } else { + /* + * Bit 55 is always between the two regions, and is canonical for + * determining if address tagging is enabled. + */ + select = extract64(va, 55, 1); + if (!select) { + tsz = extract32(tcr, 0, 6); + epd = extract32(tcr, 7, 1); + using64k = extract32(tcr, 14, 1); + using16k = extract32(tcr, 15, 1); + hpd = extract64(tcr, 41, 1); + } else { + int tg = extract32(tcr, 30, 2); + using16k = tg == 1; + using64k = tg == 3; + tsz = extract32(tcr, 16, 6); + epd = extract32(tcr, 23, 1); + hpd = extract64(tcr, 42, 1); + } + } + tsz = MIN(tsz, 39); /* TODO: ARMv8.4-TTST */ + tsz = MAX(tsz, 16); /* TODO: ARMv8.2-LVA */ /* Present TBI as a composite with TBID. */ - ret.tbi &= (data || !ret.tbid); - return ret; + tbi = aa64_va_parameter_tbi(tcr, mmu_idx); + if (!data) { + tbi &= ~aa64_va_parameter_tbid(tcr, mmu_idx); + } + tbi = (tbi >> select) & 1; + + return (ARMVAParameters) { + .tsz = tsz, + .select = select, + .tbi = tbi, + .epd = epd, + .hpd = hpd, + .using16k = using16k, + .using64k = using64k, + }; } #ifndef CONFIG_USER_ONLY @@ -9480,7 +10673,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); @@ -9556,33 +10749,21 @@ static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address, TCR *tcr = regime_tcr(env, mmu_idx); int ap, ns, xn, pxn; uint32_t el = regime_el(env, mmu_idx); - bool ttbr1_valid; uint64_t descaddrmask; bool aarch64 = arm_el_is_aa64(env, el); bool guarded = false; - /* TODO: - * This code does not handle the different format TCR for VTCR_EL2. - * This code also does not support shareability levels. - * Attribute and permission bit handling should also be checked when adding - * support for those page table walks. - */ + /* TODO: This code does not support shareability levels. */ if (aarch64) { 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); addrsize = 64 - 8 * param.tbi; inputsize = 64 - param.tsz; } else { param = aa32_va_parameters(env, address, mmu_idx); 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; } @@ -9598,7 +10779,7 @@ static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address, if (inputsize < addrsize) { target_ulong top_bits = sextract64(address, inputsize, addrsize - inputsize); - if (-top_bits != param.select || (param.select && !ttbr1_valid)) { + if (-top_bits != param.select) { /* The gap between the two regions is a Translation fault */ fault_type = ARMFault_Translation; goto do_fault; @@ -9633,7 +10814,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 @@ -9677,6 +10858,10 @@ static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address, /* Now we can extract the actual base address from the TTBR */ descaddr = extract64(ttbr, 0, 48); + /* + * We rely on this masking to clear the RES0 bits at the bottom of the TTBR + * and also to mask out CnP (bit 0) which could validly be non-zero. + */ descaddr &= ~indexmask; /* The address field in the descriptor goes up to bit 39 for ARMv7 @@ -9733,7 +10918,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; } @@ -9764,7 +10949,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 { @@ -9791,7 +10976,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 { @@ -9812,7 +10997,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; } @@ -10611,7 +11796,9 @@ 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 || + mmu_idx == ARMMMUIdx_E10_1_PAN) { /* Call ourselves recursively to do the stage 1 and then stage 2 * translations. */ @@ -10626,13 +11813,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); @@ -10674,7 +11861,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; @@ -10716,7 +11903,40 @@ bool get_phys_addr(CPUARMState *env, target_ulong address, /* Definitely a real MMU, not an MPU */ if (regime_translation_disabled(env, mmu_idx)) { - /* MMU disabled. */ + /* + * MMU disabled. S1 addresses within aa64 translation regimes are + * still checked for bounds -- see AArch64.TranslateAddressS1Off. + */ + if (mmu_idx != ARMMMUIdx_Stage2) { + int r_el = regime_el(env, mmu_idx); + if (arm_el_is_aa64(env, r_el)) { + int pamax = arm_pamax(env_archcpu(env)); + uint64_t tcr = env->cp15.tcr_el[r_el].raw_tcr; + int addrtop, tbi; + + tbi = aa64_va_parameter_tbi(tcr, mmu_idx); + if (access_type == MMU_INST_FETCH) { + tbi &= ~aa64_va_parameter_tbid(tcr, mmu_idx); + } + tbi = (tbi >> extract64(address, 55, 1)) & 1; + addrtop = (tbi ? 55 : 63); + + if (extract64(address, pamax, addrtop - pamax + 1) != 0) { + fi->type = ARMFault_AddressSize; + fi->level = 0; + fi->stage2 = false; + return 1; + } + + /* + * When TBI is disabled, we've just validated that all of the + * bits above PAMax are zero, so logically we only need to + * clear the top byte for TBI. But it's clearer to follow + * the pseudocode set of addrdesc.paddress. + */ + address = extract64(address, 0, 52); + } + } *phys_ptr = address; *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; *page_size = TARGET_PAGE_SIZE; @@ -11035,8 +12255,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 */ @@ -11064,30 +12282,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; + break; + case 1: + if (cur_el == 0) { + return 1; + } + break; + case 3: + break; } - 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 3: - break; } /* @@ -11123,6 +12345,34 @@ 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_E10_1_PAN: + case ARMMMUIdx_SE10_1: + case ARMMMUIdx_SE10_1_PAN: + return 1; + case ARMMMUIdx_E2: + case ARMMMUIdx_E20_2: + case ARMMMUIdx_E20_2_PAN: + 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) { @@ -11136,10 +12386,42 @@ 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)) { + if (env->pstate & PSTATE_PAN) { + return ARMMMUIdx_SE10_1_PAN; + } + return ARMMMUIdx_SE10_1; + } + if (env->pstate & PSTATE_PAN) { + return ARMMMUIdx_E10_1_PAN; + } + 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)) { + if (env->pstate & PSTATE_PAN) { + return ARMMMUIdx_E20_2_PAN; + } + return ARMMMUIdx_E20_2; + } + return ARMMMUIdx_E2; + case 3: + return ARMMMUIdx_SE3; + default: + g_assert_not_reached(); } } @@ -11148,11 +12430,6 @@ ARMMMUIdx arm_mmu_idx(CPUARMState *env) return arm_mmu_idx_el(env, arm_current_el(env)); } -int cpu_mmu_index(CPUARMState *env, bool ifetch) -{ - return arm_to_core_mmu_idx(arm_mmu_idx(env)); -} - #ifndef CONFIG_USER_ONLY ARMMMUIdx arm_stage1_mmu_idx(CPUARMState *env) { @@ -11194,11 +12471,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); } /* @@ -11209,7 +12483,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); @@ -11232,6 +12506,12 @@ static uint32_t rebuild_hflags_a32(CPUARMState *env, int fp_el, if (arm_el_is_aa64(env, 1)) { flags = FIELD_DP32(flags, TBFLAG_A32, VFPEN, 1); } + + if (arm_current_el(env) < 2 && env->cp15.hstr_el2 && + (arm_hcr_el2_eff(env) & (HCR_E2H | HCR_TGE)) != (HCR_E2H | HCR_TGE)) { + flags = FIELD_DP32(flags, TBFLAG_A32, HSTR_ACTIVE, 1); + } + return rebuild_hflags_common_32(env, fp_el, mmu_idx, flags); } @@ -11240,21 +12520,15 @@ static uint32_t rebuild_hflags_a64(CPUARMState *env, int el, int fp_el, { uint32_t flags = rebuild_hflags_aprofile(env); ARMMMUIdx stage1 = stage_1_mmu_idx(mmu_idx); - ARMVAParameters p0 = aa64_va_parameters_both(env, 0, stage1); + uint64_t tcr = regime_tcr(env, mmu_idx)->raw_tcr; uint64_t sctlr; int tbii, tbid; flags = FIELD_DP32(flags, TBFLAG_ANY, AARCH64_STATE, 1); - /* FIXME: ARMv8.1-VHE S2 translation regime. */ - if (regime_el(env, stage1) < 2) { - ARMVAParameters p1 = aa64_va_parameters_both(env, -1, stage1); - tbid = (p1.tbi << 1) | p0.tbi; - tbii = tbid & ~((p1.tbid << 1) | p0.tbid); - } else { - tbid = p0.tbi; - tbii = tbid & !p0.tbid; - } + /* Get control bits for tagged addresses. */ + tbid = aa64_va_parameter_tbi(tcr, mmu_idx); + tbii = tbid & ~aa64_va_parameter_tbid(tcr, mmu_idx); flags = FIELD_DP32(flags, TBFLAG_A64, TBII, tbii); flags = FIELD_DP32(flags, TBFLAG_A64, TBID, tbid); @@ -11276,7 +12550,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); @@ -11301,6 +12575,32 @@ static uint32_t rebuild_hflags_a64(CPUARMState *env, int el, int fp_el, } } + /* Compute the condition for using AccType_UNPRIV for LDTR et al. */ + if (!(env->pstate & PSTATE_UAO)) { + switch (mmu_idx) { + case ARMMMUIdx_E10_1: + case ARMMMUIdx_E10_1_PAN: + case ARMMMUIdx_SE10_1: + case ARMMMUIdx_SE10_1_PAN: + /* TODO: ARMv8.3-NV */ + flags = FIELD_DP32(flags, TBFLAG_A64, UNPRIV, 1); + break; + case ARMMMUIdx_E20_2: + case ARMMMUIdx_E20_2_PAN: + /* TODO: ARMv8.4-SecEL2 */ + /* + * Note that EL20_2 is gated by HCR_EL2.E2H == 1, but EL20_0 is + * gated by HCR_EL2. == '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); } @@ -11324,6 +12624,18 @@ void arm_rebuild_hflags(CPUARMState *env) env->hflags = rebuild_hflags_internal(env); } +/* + * If we have triggered a EL state change we can't rely on the + * translator having passed it to us, we need to recompute. + */ +void HELPER(rebuild_hflags_m32_newel)(CPUARMState *env) +{ + int el = arm_current_el(env); + int fp_el = fp_exception_el(env, el); + ARMMMUIdx mmu_idx = arm_mmu_idx_el(env, el); + env->hflags = rebuild_hflags_m32(env, fp_el, mmu_idx); +} + void HELPER(rebuild_hflags_m32)(CPUARMState *env, int el) { int fp_el = fp_exception_el(env, el); @@ -11332,6 +12644,18 @@ void HELPER(rebuild_hflags_m32)(CPUARMState *env, int el) env->hflags = rebuild_hflags_m32(env, fp_el, mmu_idx); } +/* + * If we have triggered a EL state change we can't rely on the + * translator having passed it to us, we need to recompute. + */ +void HELPER(rebuild_hflags_a32_newel)(CPUARMState *env) +{ + int el = arm_current_el(env); + int fp_el = fp_exception_el(env, el); + ARMMMUIdx mmu_idx = arm_mmu_idx_el(env, el); + env->hflags = rebuild_hflags_a32(env, fp_el, mmu_idx); +} + void HELPER(rebuild_hflags_a32)(CPUARMState *env, int el) { int fp_el = fp_exception_el(env, el); @@ -11348,6 +12672,20 @@ void HELPER(rebuild_hflags_a64)(CPUARMState *env, int el) env->hflags = rebuild_hflags_a64(env, el, fp_el, mmu_idx); } +static inline void assert_hflags_rebuild_correctly(CPUARMState *env) +{ +#ifdef CONFIG_DEBUG_TCG + uint32_t env_flags_current = env->hflags; + uint32_t env_flags_rebuilt = rebuild_hflags_internal(env); + + if (unlikely(env_flags_current != env_flags_rebuilt)) { + fprintf(stderr, "TCG hflags mismatch (current:0x%08x rebuilt:0x%08x)\n", + env_flags_current, env_flags_rebuilt); + abort(); + } +#endif +} + void cpu_get_tb_cpu_state(CPUARMState *env, target_ulong *pc, target_ulong *cs_base, uint32_t *pflags) { @@ -11355,9 +12693,7 @@ void cpu_get_tb_cpu_state(CPUARMState *env, target_ulong *pc, uint32_t pstate_for_ss; *cs_base = 0; -#ifdef CONFIG_DEBUG_TCG - assert(flags == rebuild_hflags_internal(env)); -#endif + assert_hflags_rebuild_correctly(env); if (FIELD_EX32(flags, TBFLAG_ANY, AARCH64_STATE)) { *pc = env->pc; @@ -11372,7 +12708,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) && @@ -11384,12 +12720,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 { /* @@ -11410,8 +12746,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/helper.h b/target/arm/helper.h index 3d4ec267a2..f37b8670a5 100644 --- a/target/arm/helper.h +++ b/target/arm/helper.h @@ -90,7 +90,9 @@ DEF_HELPER_4(msr_banked, void, env, i32, i32, i32) DEF_HELPER_2(get_user_reg, i32, env, i32) DEF_HELPER_3(set_user_reg, void, env, i32, i32) +DEF_HELPER_FLAGS_1(rebuild_hflags_m32_newel, TCG_CALL_NO_RWG, void, env) DEF_HELPER_FLAGS_2(rebuild_hflags_m32, TCG_CALL_NO_RWG, void, env, int) +DEF_HELPER_FLAGS_1(rebuild_hflags_a32_newel, TCG_CALL_NO_RWG, void, env) DEF_HELPER_FLAGS_2(rebuild_hflags_a32, TCG_CALL_NO_RWG, void, env, int) DEF_HELPER_FLAGS_2(rebuild_hflags_a64, TCG_CALL_NO_RWG, void, env, int) @@ -226,6 +228,8 @@ DEF_HELPER_FLAGS_2(rintd, TCG_CALL_NO_RWG, f64, f64, ptr) DEF_HELPER_FLAGS_2(vjcvt, TCG_CALL_NO_RWG, i32, f64, env) DEF_HELPER_FLAGS_2(fjcvtzs, TCG_CALL_NO_RWG, i64, f64, ptr) +DEF_HELPER_FLAGS_3(check_hcr_el2_trap, TCG_CALL_NO_WG, void, env, i32, i32) + /* neon_helper.c */ DEF_HELPER_FLAGS_3(neon_qadd_u8, TCG_CALL_NO_RWG, i32, env, i32, i32) DEF_HELPER_FLAGS_3(neon_qadd_s8, TCG_CALL_NO_RWG, i32, env, i32, i32) @@ -300,14 +304,8 @@ DEF_HELPER_2(neon_abd_s16, i32, i32, i32) DEF_HELPER_2(neon_abd_u32, i32, i32, i32) DEF_HELPER_2(neon_abd_s32, i32, i32, i32) -DEF_HELPER_2(neon_shl_u8, i32, i32, i32) -DEF_HELPER_2(neon_shl_s8, i32, i32, i32) DEF_HELPER_2(neon_shl_u16, i32, i32, i32) DEF_HELPER_2(neon_shl_s16, i32, i32, i32) -DEF_HELPER_2(neon_shl_u32, i32, i32, i32) -DEF_HELPER_2(neon_shl_s32, i32, i32, i32) -DEF_HELPER_2(neon_shl_u64, i64, i64, i64) -DEF_HELPER_2(neon_shl_s64, i64, i64, i64) DEF_HELPER_2(neon_rshl_u8, i32, i32, i32) DEF_HELPER_2(neon_rshl_s8, i32, i32, i32) DEF_HELPER_2(neon_rshl_u16, i32, i32, i32) @@ -345,8 +343,6 @@ DEF_HELPER_2(neon_sub_u8, i32, i32, i32) DEF_HELPER_2(neon_sub_u16, i32, i32, i32) DEF_HELPER_2(neon_mul_u8, i32, i32, i32) DEF_HELPER_2(neon_mul_u16, i32, i32, i32) -DEF_HELPER_2(neon_mul_p8, i32, i32, i32) -DEF_HELPER_2(neon_mull_p8, i64, i32, i32) DEF_HELPER_2(neon_tst_u8, i32, i32, i32) DEF_HELPER_2(neon_tst_u16, i32, i32, i32) @@ -564,10 +560,6 @@ DEF_HELPER_FLAGS_3(crypto_sm4ekey, TCG_CALL_NO_RWG, void, ptr, ptr, ptr) DEF_HELPER_FLAGS_3(crc32, TCG_CALL_NO_RWG_SE, i32, i32, i32, i32) DEF_HELPER_FLAGS_3(crc32c, TCG_CALL_NO_RWG_SE, i32, i32, i32, i32) -DEF_HELPER_2(dc_zva, void, env, i64) - -DEF_HELPER_FLAGS_2(neon_pmull_64_lo, TCG_CALL_NO_RWG_SE, i64, i64, i64) -DEF_HELPER_FLAGS_2(neon_pmull_64_hi, TCG_CALL_NO_RWG_SE, i64, i64, i64) DEF_HELPER_FLAGS_5(gvec_qrdmlah_s16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) @@ -694,6 +686,16 @@ DEF_HELPER_FLAGS_2(frint64_s, TCG_CALL_NO_RWG, f32, f32, ptr) DEF_HELPER_FLAGS_2(frint32_d, TCG_CALL_NO_RWG, f64, f64, ptr) DEF_HELPER_FLAGS_2(frint64_d, TCG_CALL_NO_RWG, f64, f64, ptr) +DEF_HELPER_FLAGS_4(gvec_sshl_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_sshl_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_ushl_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_ushl_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(gvec_pmul_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_pmull_q, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(neon_pmull_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + #ifdef TARGET_AARCH64 #include "helper-a64.h" #include "helper-sve.h" diff --git a/target/arm/internals.h b/target/arm/internals.h index f5313dd3d4..e633aff36e 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -769,6 +769,45 @@ 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; + } +} + +static inline ARMMMUIdx core_to_aa64_mmu_idx(int mmu_idx) +{ + /* AArch64 is always a-profile. */ + 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 +843,52 @@ 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_Stage1_E1_PAN: + case ARMMMUIdx_E10_0: + case ARMMMUIdx_E10_1: + case ARMMMUIdx_E10_1_PAN: + case ARMMMUIdx_E20_0: + case ARMMMUIdx_E20_2: + case ARMMMUIdx_E20_2_PAN: + case ARMMMUIdx_SE10_0: + case ARMMMUIdx_SE10_1: + case ARMMMUIdx_SE10_1_PAN: + 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_E10_1_PAN: + case ARMMMUIdx_E20_0: + case ARMMMUIdx_E20_2: + case ARMMMUIdx_E20_2_PAN: + case ARMMMUIdx_Stage1_E0: + case ARMMMUIdx_Stage1_E1: + case ARMMMUIdx_Stage1_E1_PAN: + 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_SE10_1_PAN: case ARMMMUIdx_MSPrivNegPri: case ARMMMUIdx_MSUserNegPri: case ARMMMUIdx_MSPriv: @@ -832,6 +899,19 @@ static inline bool regime_is_secure(CPUARMState *env, ARMMMUIdx mmu_idx) } } +static inline bool regime_is_pan(CPUARMState *env, ARMMMUIdx mmu_idx) +{ + switch (mmu_idx) { + case ARMMMUIdx_Stage1_E1_PAN: + case ARMMMUIdx_E10_1_PAN: + case ARMMMUIdx_E20_2_PAN: + case ARMMMUIdx_SE10_1_PAN: + return true; + default: + return false; + } +} + /* Return the FSR value for a debug exception (watchpoint, hardware * breakpoint or BKPT insn) targeting the specified exception level. */ @@ -857,6 +937,48 @@ static inline uint32_t arm_debug_exception_fsr(CPUARMState *env) } } +/** + * arm_num_brps: Return number of implemented breakpoints. + * Note that the ID register BRPS field is "number of bps - 1", + * and we return the actual number of breakpoints. + */ +static inline int arm_num_brps(ARMCPU *cpu) +{ + if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) { + return FIELD_EX64(cpu->isar.id_aa64dfr0, ID_AA64DFR0, BRPS) + 1; + } else { + return FIELD_EX32(cpu->isar.dbgdidr, DBGDIDR, BRPS) + 1; + } +} + +/** + * arm_num_wrps: Return number of implemented watchpoints. + * Note that the ID register WRPS field is "number of wps - 1", + * and we return the actual number of watchpoints. + */ +static inline int arm_num_wrps(ARMCPU *cpu) +{ + if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) { + return FIELD_EX64(cpu->isar.id_aa64dfr0, ID_AA64DFR0, WRPS) + 1; + } else { + return FIELD_EX32(cpu->isar.dbgdidr, DBGDIDR, WRPS) + 1; + } +} + +/** + * arm_num_ctx_cmps: Return number of implemented context comparators. + * Note that the ID register CTX_CMPS field is "number of cmps - 1", + * and we return the actual number of comparators. + */ +static inline int arm_num_ctx_cmps(ARMCPU *cpu) +{ + if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) { + return FIELD_EX64(cpu->isar.id_aa64dfr0, ID_AA64DFR0, CTX_CMPS) + 1; + } else { + return FIELD_EX32(cpu->isar.dbgdidr, DBGDIDR, CTX_CMPS) + 1; + } +} + /* Note make_memop_idx reserves 4 bits for mmu_idx, and MO_BSWAP is bit 3. * Thus a TCGMemOpIdx, without any MO_ALIGN bits, fits in 8 bits. */ @@ -975,12 +1097,76 @@ 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); #endif +/** + * arm_mmu_idx_is_stage1_of_2: + * @mmu_idx: The ARMMMUIdx to test + * + * Return true if @mmu_idx is a NOTLB mmu_idx that is the + * first stage of a two stage regime. + */ +static inline bool arm_mmu_idx_is_stage1_of_2(ARMMMUIdx mmu_idx) +{ + switch (mmu_idx) { + case ARMMMUIdx_Stage1_E0: + case ARMMMUIdx_Stage1_E1: + case ARMMMUIdx_Stage1_E1_PAN: + return true; + default: + return false; + } +} + +static inline uint32_t aarch32_cpsr_valid_mask(uint64_t features, + const ARMISARegisters *id) +{ + uint32_t valid = CPSR_M | CPSR_AIF | CPSR_IL | CPSR_NZCV; + + if ((features >> ARM_FEATURE_V4T) & 1) { + valid |= CPSR_T; + } + if ((features >> ARM_FEATURE_V5) & 1) { + valid |= CPSR_Q; /* V5TE in reality*/ + } + if ((features >> ARM_FEATURE_V6) & 1) { + valid |= CPSR_E | CPSR_GE; + } + if ((features >> ARM_FEATURE_THUMB2) & 1) { + valid |= CPSR_IT; + } + if (isar_feature_aa32_jazelle(id)) { + valid |= CPSR_J; + } + if (isar_feature_aa32_pan(id)) { + valid |= CPSR_PAN; + } + + return valid; +} + +static inline uint32_t aarch64_pstate_valid_mask(const ARMISARegisters *id) +{ + uint32_t valid; + + valid = PSTATE_M | PSTATE_DAIF | PSTATE_IL | PSTATE_SS | PSTATE_NZCV; + if (isar_feature_aa64_bti(id)) { + valid |= PSTATE_BTYPE; + } + if (isar_feature_aa64_pan(id)) { + valid |= PSTATE_PAN; + } + if (isar_feature_aa64_uao(id)) { + valid |= PSTATE_UAO; + } + + return valid; +} + /* * Parameters of a given virtual address, as extracted from the * translation control register (TCR) for a given regime. @@ -989,15 +1175,12 @@ typedef struct ARMVAParameters { unsigned tsz : 8; unsigned select : 1; bool tbi : 1; - bool tbid : 1; bool epd : 1; bool hpd : 1; bool using16k : 1; bool using64k : 1; } ARMVAParameters; -ARMVAParameters aa64_va_parameters_both(CPUARMState *env, uint64_t va, - ARMMMUIdx mmu_idx); ARMVAParameters aa64_va_parameters(CPUARMState *env, uint64_t va, ARMMMUIdx mmu_idx, bool data); diff --git a/target/arm/kvm.c b/target/arm/kvm.c index 5b82cefef6..390077c518 100644 --- a/target/arm/kvm.c +++ b/target/arm/kvm.c @@ -17,6 +17,8 @@ #include "qemu/timer.h" #include "qemu/error-report.h" #include "qemu/main-loop.h" +#include "qom/object.h" +#include "qapi/error.h" #include "sysemu/sysemu.h" #include "sysemu/kvm.h" #include "sysemu/kvm_int.h" @@ -179,11 +181,35 @@ void kvm_arm_set_cpu_features_from_host(ARMCPU *cpu) env->features = arm_host_cpu_features.features; } +static bool kvm_no_adjvtime_get(Object *obj, Error **errp) +{ + return !ARM_CPU(obj)->kvm_adjvtime; +} + +static void kvm_no_adjvtime_set(Object *obj, bool value, Error **errp) +{ + ARM_CPU(obj)->kvm_adjvtime = !value; +} + +/* KVM VCPU properties should be prefixed with "kvm-". */ +void kvm_arm_add_vcpu_properties(Object *obj) +{ + if (!kvm_enabled()) { + return; + } + + ARM_CPU(obj)->kvm_adjvtime = true; + object_property_add_bool(obj, "kvm-no-adjvtime", kvm_no_adjvtime_get, + kvm_no_adjvtime_set, &error_abort); + object_property_set_description(obj, "kvm-no-adjvtime", + "Set on to disable the adjustment of " + "the virtual counter. VM stopped time " + "will be counted.", &error_abort); +} + bool kvm_arm_pmu_supported(CPUState *cpu) { - KVMState *s = KVM_STATE(current_machine->accelerator); - - return kvm_check_extension(s, KVM_CAP_ARM_PMU_V3); + return kvm_check_extension(cpu->kvm_state, KVM_CAP_ARM_PMU_V3); } int kvm_arm_get_max_vm_ipa_size(MachineState *ms) @@ -359,6 +385,22 @@ static int compare_u64(const void *a, const void *b) return 0; } +/* + * cpreg_values are sorted in ascending order by KVM register ID + * (see kvm_arm_init_cpreg_list). This allows us to cheaply find + * the storage for a KVM register by ID with a binary search. + */ +static uint64_t *kvm_arm_get_cpreg_ptr(ARMCPU *cpu, uint64_t regidx) +{ + uint64_t *res; + + res = bsearch(®idx, cpu->cpreg_indexes, cpu->cpreg_array_len, + sizeof(uint64_t), compare_u64); + assert(res); + + return &cpu->cpreg_values[res - cpu->cpreg_indexes]; +} + /* Initialize the ARMCPU cpreg list according to the kernel's * definition of what CPU registers it knows about (and throw away * the previous TCG-created cpreg list). @@ -512,6 +554,23 @@ bool write_list_to_kvmstate(ARMCPU *cpu, int level) return ok; } +void kvm_arm_cpu_pre_save(ARMCPU *cpu) +{ + /* KVM virtual time adjustment */ + if (cpu->kvm_vtime_dirty) { + *kvm_arm_get_cpreg_ptr(cpu, KVM_REG_ARM_TIMER_CNT) = cpu->kvm_vtime; + } +} + +void kvm_arm_cpu_post_load(ARMCPU *cpu) +{ + /* KVM virtual time adjustment */ + if (cpu->kvm_adjvtime) { + cpu->kvm_vtime = *kvm_arm_get_cpreg_ptr(cpu, KVM_REG_ARM_TIMER_CNT); + cpu->kvm_vtime_dirty = true; + } +} + void kvm_arm_reset_vcpu(ARMCPU *cpu) { int ret; @@ -579,6 +638,50 @@ int kvm_arm_sync_mpstate_to_qemu(ARMCPU *cpu) return 0; } +void kvm_arm_get_virtual_time(CPUState *cs) +{ + ARMCPU *cpu = ARM_CPU(cs); + struct kvm_one_reg reg = { + .id = KVM_REG_ARM_TIMER_CNT, + .addr = (uintptr_t)&cpu->kvm_vtime, + }; + int ret; + + if (cpu->kvm_vtime_dirty) { + return; + } + + ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®); + if (ret) { + error_report("Failed to get KVM_REG_ARM_TIMER_CNT"); + abort(); + } + + cpu->kvm_vtime_dirty = true; +} + +void kvm_arm_put_virtual_time(CPUState *cs) +{ + ARMCPU *cpu = ARM_CPU(cs); + struct kvm_one_reg reg = { + .id = KVM_REG_ARM_TIMER_CNT, + .addr = (uintptr_t)&cpu->kvm_vtime, + }; + int ret; + + if (!cpu->kvm_vtime_dirty) { + return; + } + + ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®); + if (ret) { + error_report("Failed to set KVM_REG_ARM_TIMER_CNT"); + abort(); + } + + cpu->kvm_vtime_dirty = false; +} + int kvm_put_vcpu_events(ARMCPU *cpu) { CPUARMState *env = &cpu->env; @@ -690,6 +793,21 @@ MemTxAttrs kvm_arch_post_run(CPUState *cs, struct kvm_run *run) return MEMTXATTRS_UNSPECIFIED; } +void kvm_arm_vm_state_change(void *opaque, int running, RunState state) +{ + CPUState *cs = opaque; + ARMCPU *cpu = ARM_CPU(cs); + + if (running) { + if (cpu->kvm_adjvtime) { + kvm_arm_put_virtual_time(cs); + } + } else { + if (cpu->kvm_adjvtime) { + kvm_arm_get_virtual_time(cs); + } + } +} int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) { @@ -741,11 +859,11 @@ void kvm_arch_init_irq_routing(KVMState *s) { } -int kvm_arch_irqchip_create(MachineState *ms, KVMState *s) +int kvm_arch_irqchip_create(KVMState *s) { - if (machine_kernel_irqchip_split(ms)) { - perror("-machine kernel_irqchip=split is not supported on ARM."); - exit(1); + if (kvm_kernel_irqchip_split()) { + perror("-machine kernel_irqchip=split is not supported on ARM."); + exit(1); } /* If we can create the VGIC using the newer device control API, we @@ -756,15 +874,17 @@ int kvm_arch_irqchip_create(MachineState *ms, KVMState *s) int kvm_arm_vgic_probe(void) { + int val = 0; + if (kvm_create_device(kvm_state, KVM_DEV_TYPE_ARM_VGIC_V3, true) == 0) { - return 3; - } else if (kvm_create_device(kvm_state, - KVM_DEV_TYPE_ARM_VGIC_V2, true) == 0) { - return 2; - } else { - return 0; + val |= KVM_ARM_VGIC_V3; } + if (kvm_create_device(kvm_state, + KVM_DEV_TYPE_ARM_VGIC_V2, true) == 0) { + val |= KVM_ARM_VGIC_V2; + } + return val; } int kvm_arm_set_irq(int cpu, int irqtype, int irq, int level) diff --git a/target/arm/kvm32.c b/target/arm/kvm32.c index 32bf8d6757..f271181ab8 100644 --- a/target/arm/kvm32.c +++ b/target/arm/kvm32.c @@ -16,6 +16,7 @@ #include "qemu-common.h" #include "cpu.h" #include "qemu/timer.h" +#include "sysemu/runstate.h" #include "sysemu/kvm.h" #include "kvm_arm.h" #include "internals.h" @@ -96,6 +97,9 @@ bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) ahcf->isar.id_isar6 = 0; } + err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_dfr0, + ARM_CP15_REG32(0, 0, 1, 2)); + err |= read_sys_reg32(fdarray[2], &ahcf->isar.mvfr0, KVM_REG_ARM | KVM_REG_SIZE_U32 | KVM_REG_ARM_VFP | KVM_REG_ARM_VFP_MVFR0); @@ -107,6 +111,28 @@ bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) * Fortunately there is not yet anything in there that affects migration. */ + err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_mmfr0, + ARM_CP15_REG32(0, 0, 1, 4)); + err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_mmfr1, + ARM_CP15_REG32(0, 0, 1, 5)); + err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_mmfr2, + ARM_CP15_REG32(0, 0, 1, 6)); + err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_mmfr3, + ARM_CP15_REG32(0, 0, 1, 7)); + if (read_sys_reg32(fdarray[2], &ahcf->isar.id_mmfr4, + ARM_CP15_REG32(0, 0, 2, 6))) { + /* + * Older kernels don't support reading ID_MMFR4 (a new in v8 + * register); assume it's zero. + */ + ahcf->isar.id_mmfr4 = 0; + } + + /* + * There is no way to read DBGDIDR, because currently 32-bit KVM + * doesn't implement debug at all. Leave it at zero. + */ + kvm_arm_destroy_scratch_host_vcpu(fdarray); if (err < 0) { @@ -121,7 +147,6 @@ bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) * bits, but a few must be tested. */ set_feature(&features, ARM_FEATURE_V7VE); - set_feature(&features, ARM_FEATURE_VFP3); set_feature(&features, ARM_FEATURE_GENERIC_TIMER); if (extract32(id_pfr0, 12, 4) == 1) { @@ -130,10 +155,6 @@ bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) if (extract32(ahcf->isar.mvfr1, 12, 4) == 1) { set_feature(&features, ARM_FEATURE_NEON); } - if (extract32(ahcf->isar.mvfr1, 28, 4) == 1) { - /* FMAC support implies VFPv4 */ - set_feature(&features, ARM_FEATURE_VFP4); - } ahcf->features = features; @@ -198,6 +219,8 @@ int kvm_arch_init_vcpu(CPUState *cs) return -EINVAL; } + qemu_add_vm_change_state_handler(kvm_arm_vm_state_change, cs); + /* Determine init features for this CPU */ memset(cpu->kvm_init_features, 0, sizeof(cpu->kvm_init_features)); if (cpu->start_powered_off) { @@ -386,17 +409,22 @@ int kvm_arch_put_registers(CPUState *cs, int level) return ret; } - ret = kvm_put_vcpu_events(cpu); - if (ret) { - return ret; - } - write_cpustate_to_list(cpu, true); if (!write_list_to_kvmstate(cpu, level)) { return EINVAL; } + /* + * Setting VCPU events should be triggered after syncing the registers + * to avoid overwriting potential changes made by KVM upon calling + * KVM_SET_VCPU_EVENTS ioctl + */ + ret = kvm_put_vcpu_events(cpu); + if (ret) { + return ret; + } + kvm_arm_sync_mpstate_to_kvm(cpu); return ret; diff --git a/target/arm/kvm64.c b/target/arm/kvm64.c index 876184b8fe..be5b31c2b0 100644 --- a/target/arm/kvm64.c +++ b/target/arm/kvm64.c @@ -23,10 +23,10 @@ #include "qemu/host-utils.h" #include "qemu/main-loop.h" #include "exec/gdbstub.h" +#include "sysemu/runstate.h" #include "sysemu/kvm.h" #include "sysemu/kvm_int.h" #include "kvm_arm.h" -#include "hw/boards.h" #include "internals.h" static bool have_guest_debug; @@ -541,6 +541,10 @@ bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) } else { err |= read_sys_reg64(fdarray[2], &ahcf->isar.id_aa64pfr1, ARM64_SYS_REG(3, 0, 0, 4, 1)); + err |= read_sys_reg64(fdarray[2], &ahcf->isar.id_aa64dfr0, + ARM64_SYS_REG(3, 0, 0, 5, 0)); + err |= read_sys_reg64(fdarray[2], &ahcf->isar.id_aa64dfr1, + ARM64_SYS_REG(3, 0, 0, 5, 1)); err |= read_sys_reg64(fdarray[2], &ahcf->isar.id_aa64isar0, ARM64_SYS_REG(3, 0, 0, 6, 0)); err |= read_sys_reg64(fdarray[2], &ahcf->isar.id_aa64isar1, @@ -549,6 +553,8 @@ bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) ARM64_SYS_REG(3, 0, 0, 7, 0)); err |= read_sys_reg64(fdarray[2], &ahcf->isar.id_aa64mmfr1, ARM64_SYS_REG(3, 0, 0, 7, 1)); + err |= read_sys_reg64(fdarray[2], &ahcf->isar.id_aa64mmfr2, + ARM64_SYS_REG(3, 0, 0, 7, 2)); /* * Note that if AArch32 support is not present in the host, @@ -557,6 +563,16 @@ bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) * than skipping the reads and leaving 0, as we must avoid * considering the values in every case. */ + err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_dfr0, + ARM64_SYS_REG(3, 0, 0, 1, 2)); + err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_mmfr0, + ARM64_SYS_REG(3, 0, 0, 1, 4)); + err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_mmfr1, + ARM64_SYS_REG(3, 0, 0, 1, 5)); + err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_mmfr2, + ARM64_SYS_REG(3, 0, 0, 1, 6)); + err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_mmfr3, + ARM64_SYS_REG(3, 0, 0, 1, 7)); err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_isar0, ARM64_SYS_REG(3, 0, 0, 2, 0)); err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_isar1, @@ -569,6 +585,8 @@ bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) ARM64_SYS_REG(3, 0, 0, 2, 4)); err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_isar5, ARM64_SYS_REG(3, 0, 0, 2, 5)); + err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_mmfr4, + ARM64_SYS_REG(3, 0, 0, 2, 6)); err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_isar6, ARM64_SYS_REG(3, 0, 0, 2, 7)); @@ -578,6 +596,36 @@ bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) ARM64_SYS_REG(3, 0, 0, 3, 1)); err |= read_sys_reg32(fdarray[2], &ahcf->isar.mvfr2, ARM64_SYS_REG(3, 0, 0, 3, 2)); + + /* + * DBGDIDR is a bit complicated because the kernel doesn't + * provide an accessor for it in 64-bit mode, which is what this + * scratch VM is in, and there's no architected "64-bit sysreg + * which reads the same as the 32-bit register" the way there is + * for other ID registers. Instead we synthesize a value from the + * AArch64 ID_AA64DFR0, the same way the kernel code in + * arch/arm64/kvm/sys_regs.c:trap_dbgidr() does. + * We only do this if the CPU supports AArch32 at EL1. + */ + if (FIELD_EX32(ahcf->isar.id_aa64pfr0, ID_AA64PFR0, EL1) >= 2) { + int wrps = FIELD_EX64(ahcf->isar.id_aa64dfr0, ID_AA64DFR0, WRPS); + int brps = FIELD_EX64(ahcf->isar.id_aa64dfr0, ID_AA64DFR0, BRPS); + int ctx_cmps = + FIELD_EX64(ahcf->isar.id_aa64dfr0, ID_AA64DFR0, CTX_CMPS); + int version = 6; /* ARMv8 debug architecture */ + bool has_el3 = + !!FIELD_EX32(ahcf->isar.id_aa64pfr0, ID_AA64PFR0, EL3); + uint32_t dbgdidr = 0; + + dbgdidr = FIELD_DP32(dbgdidr, DBGDIDR, WRPS, wrps); + dbgdidr = FIELD_DP32(dbgdidr, DBGDIDR, BRPS, brps); + dbgdidr = FIELD_DP32(dbgdidr, DBGDIDR, CTX_CMPS, ctx_cmps); + dbgdidr = FIELD_DP32(dbgdidr, DBGDIDR, VERSION, version); + dbgdidr = FIELD_DP32(dbgdidr, DBGDIDR, NSUHD_IMP, has_el3); + dbgdidr = FIELD_DP32(dbgdidr, DBGDIDR, SE_IMP, has_el3); + dbgdidr |= (1 << 15); /* RES1 bit */ + ahcf->isar.dbgdidr = dbgdidr; + } } sve_supported = ioctl(fdarray[0], KVM_CHECK_EXTENSION, KVM_CAP_ARM_SVE) > 0; @@ -601,10 +649,10 @@ bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) * feature bits. */ set_feature(&features, ARM_FEATURE_V8); - set_feature(&features, ARM_FEATURE_VFP4); set_feature(&features, ARM_FEATURE_NEON); set_feature(&features, ARM_FEATURE_AARCH64); set_feature(&features, ARM_FEATURE_PMU); + set_feature(&features, ARM_FEATURE_GENERIC_TIMER); ahcf->features = features; @@ -613,14 +661,14 @@ bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) bool kvm_arm_aarch32_supported(CPUState *cpu) { - KVMState *s = KVM_STATE(current_machine->accelerator); + KVMState *s = KVM_STATE(current_accel()); return kvm_check_extension(s, KVM_CAP_ARM_EL1_32BIT); } bool kvm_arm_sve_supported(CPUState *cpu) { - KVMState *s = KVM_STATE(current_machine->accelerator); + KVMState *s = KVM_STATE(current_accel()); return kvm_check_extension(s, KVM_CAP_ARM_SVE); } @@ -734,6 +782,8 @@ int kvm_arch_init_vcpu(CPUState *cs) return -EINVAL; } + qemu_add_vm_change_state_handler(kvm_arm_vm_state_change, cs); + /* Determine init features for this CPU */ memset(cpu->kvm_init_features, 0, sizeof(cpu->kvm_init_features)); if (cpu->start_powered_off) { @@ -876,30 +926,6 @@ static int kvm_arch_put_fpsimd(CPUState *cs) return 0; } -/* - * SVE registers are encoded in KVM's memory in an endianness-invariant format. - * The byte at offset i from the start of the in-memory representation contains - * the bits [(7 + 8 * i) : (8 * i)] of the register value. As this means the - * lowest offsets are stored in the lowest memory addresses, then that nearly - * matches QEMU's representation, which is to use an array of host-endian - * uint64_t's, where the lower offsets are at the lower indices. To complete - * the translation we just need to byte swap the uint64_t's on big-endian hosts. - */ -static uint64_t *sve_bswap64(uint64_t *dst, uint64_t *src, int nr) -{ -#ifdef HOST_WORDS_BIGENDIAN - int i; - - for (i = 0; i < nr; ++i) { - dst[i] = bswap64(src[i]); - } - - return dst; -#else - return src; -#endif -} - /* * KVM SVE registers come in slices where ZREGs have a slice size of 2048 bits * and PREGS and the FFR have a slice size of 256 bits. However we simply hard @@ -1068,17 +1094,22 @@ int kvm_arch_put_registers(CPUState *cs, int level) return ret; } - ret = kvm_put_vcpu_events(cpu); - if (ret) { - return ret; - } - write_cpustate_to_list(cpu, true); if (!write_list_to_kvmstate(cpu, level)) { return -EINVAL; } + /* + * Setting VCPU events should be triggered after syncing the registers + * to avoid overwriting potential changes made by KVM upon calling + * KVM_SET_VCPU_EVENTS ioctl + */ + ret = kvm_put_vcpu_events(cpu); + if (ret) { + return ret; + } + kvm_arm_sync_mpstate_to_kvm(cpu); return ret; diff --git a/target/arm/kvm_arm.h b/target/arm/kvm_arm.h index 8e14d400e8..48bf5e16d5 100644 --- a/target/arm/kvm_arm.h +++ b/target/arm/kvm_arm.h @@ -15,6 +15,9 @@ #include "exec/memory.h" #include "qemu/error-report.h" +#define KVM_ARM_VGIC_V2 (1 << 0) +#define KVM_ARM_VGIC_V3 (1 << 1) + /** * kvm_arm_vcpu_init: * @cs: CPUState @@ -28,9 +31,9 @@ int kvm_arm_vcpu_init(CPUState *cs); /** - * kvm_arm_vcpu_finalize + * kvm_arm_vcpu_finalize: * @cs: CPUState - * @feature: int + * @feature: feature to finalize * * Finalizes the configuration of the specified VCPU feature by * invoking the KVM_ARM_VCPU_FINALIZE ioctl. Features requiring @@ -75,8 +78,8 @@ void kvm_arm_register_device(MemoryRegion *mr, uint64_t devid, uint64_t group, int kvm_arm_init_cpreg_list(ARMCPU *cpu); /** - * kvm_arm_reg_syncs_via_cpreg_list - * regidx: KVM register index + * kvm_arm_reg_syncs_via_cpreg_list: + * @regidx: KVM register index * * Return true if this KVM register should be synchronized via the * cpreg list of arbitrary system registers, false if it is synchronized @@ -85,8 +88,8 @@ int kvm_arm_init_cpreg_list(ARMCPU *cpu); bool kvm_arm_reg_syncs_via_cpreg_list(uint64_t regidx); /** - * kvm_arm_cpreg_level - * regidx: KVM register index + * kvm_arm_cpreg_level: + * @regidx: KVM register index * * Return the level of this coprocessor/system register. Return value is * either KVM_PUT_RUNTIME_STATE, KVM_PUT_RESET_STATE, or KVM_PUT_FULL_STATE. @@ -127,6 +130,23 @@ bool write_list_to_kvmstate(ARMCPU *cpu, int level); */ bool write_kvmstate_to_list(ARMCPU *cpu); +/** + * kvm_arm_cpu_pre_save: + * @cpu: ARMCPU + * + * Called after write_kvmstate_to_list() from cpu_pre_save() to update + * the cpreg list with KVM CPU state. + */ +void kvm_arm_cpu_pre_save(ARMCPU *cpu); + +/** + * kvm_arm_cpu_post_load: + * @cpu: ARMCPU + * + * Called from cpu_post_load() to update KVM CPU state from the cpreg list. + */ +void kvm_arm_cpu_post_load(ARMCPU *cpu); + /** * kvm_arm_reset_vcpu: * @cpu: ARMCPU @@ -148,6 +168,8 @@ void kvm_arm_init_serror_injection(CPUState *cs); * @cpu: ARMCPU * * Get VCPU related state from kvm. + * + * Returns: 0 if success else < 0 error code */ int kvm_get_vcpu_events(ARMCPU *cpu); @@ -156,6 +178,8 @@ int kvm_get_vcpu_events(ARMCPU *cpu); * @cpu: ARMCPU * * Put VCPU related state to kvm. + * + * Returns: 0 if success else < 0 error code */ int kvm_put_vcpu_events(ARMCPU *cpu); @@ -205,10 +229,12 @@ typedef struct ARMHostCPUFeatures { /** * kvm_arm_get_host_cpu_features: - * @ahcc: ARMHostCPUClass to fill in + * @ahcf: ARMHostCPUClass to fill in * * Probe the capabilities of the host kernel's preferred CPU and fill * in the ARMHostCPUClass struct accordingly. + * + * Returns true on success and false otherwise. */ bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf); @@ -232,6 +258,15 @@ void kvm_arm_sve_get_vls(CPUState *cs, unsigned long *map); */ void kvm_arm_set_cpu_features_from_host(ARMCPU *cpu); +/** + * kvm_arm_add_vcpu_properties: + * @obj: The CPU object to add the properties to + * + * Add all KVM specific CPU properties to the CPU object. These + * are the CPU properties with "kvm-" prefixed names. + */ +void kvm_arm_add_vcpu_properties(Object *obj); + /** * kvm_arm_aarch32_supported: * @cs: CPUState @@ -242,7 +277,7 @@ void kvm_arm_set_cpu_features_from_host(ARMCPU *cpu); bool kvm_arm_aarch32_supported(CPUState *cs); /** - * bool kvm_arm_pmu_supported: + * kvm_arm_pmu_supported: * @cs: CPUState * * Returns: true if the KVM VCPU can enable its PMU @@ -251,7 +286,7 @@ bool kvm_arm_aarch32_supported(CPUState *cs); bool kvm_arm_pmu_supported(CPUState *cs); /** - * bool kvm_arm_sve_supported: + * kvm_arm_sve_supported: * @cs: CPUState * * Returns true if the KVM VCPU can enable SVE and false otherwise. @@ -259,29 +294,51 @@ bool kvm_arm_pmu_supported(CPUState *cs); bool kvm_arm_sve_supported(CPUState *cs); /** - * kvm_arm_get_max_vm_ipa_size - Returns the number of bits in the - * IPA address space supported by KVM - * + * kvm_arm_get_max_vm_ipa_size: * @ms: Machine state handle + * + * Returns the number of bits in the IPA address space supported by KVM */ int kvm_arm_get_max_vm_ipa_size(MachineState *ms); /** - * kvm_arm_sync_mpstate_to_kvm + * kvm_arm_sync_mpstate_to_kvm: * @cpu: ARMCPU * * If supported set the KVM MP_STATE based on QEMU's model. + * + * Returns 0 on success and -1 on failure. */ int kvm_arm_sync_mpstate_to_kvm(ARMCPU *cpu); /** - * kvm_arm_sync_mpstate_to_qemu + * kvm_arm_sync_mpstate_to_qemu: * @cpu: ARMCPU * * If supported get the MP_STATE from KVM and store in QEMU's model. + * + * Returns 0 on success and aborts on failure. */ int kvm_arm_sync_mpstate_to_qemu(ARMCPU *cpu); +/** + * kvm_arm_get_virtual_time: + * @cs: CPUState + * + * Gets the VCPU's virtual counter and stores it in the KVM CPU state. + */ +void kvm_arm_get_virtual_time(CPUState *cs); + +/** + * kvm_arm_put_virtual_time: + * @cs: CPUState + * + * Sets the VCPU's virtual counter to the value stored in the KVM CPU state. + */ +void kvm_arm_put_virtual_time(CPUState *cs); + +void kvm_arm_vm_state_change(void *opaque, int running, RunState state); + int kvm_arm_vgic_probe(void); void kvm_arm_pmu_set_irq(CPUState *cs, int irq); @@ -292,13 +349,16 @@ int kvm_arm_set_irq(int cpu, int irqtype, int irq, int level); static inline void kvm_arm_set_cpu_features_from_host(ARMCPU *cpu) { - /* This should never actually be called in the "not KVM" case, + /* + * This should never actually be called in the "not KVM" case, * but set up the fields to indicate an error anyway. */ cpu->kvm_target = QEMU_KVM_ARM_TARGET_NONE; cpu->host_cpu_probe_failed = true; } +static inline void kvm_arm_add_vcpu_properties(Object *obj) {} + static inline bool kvm_arm_aarch32_supported(CPUState *cs) { return false; @@ -328,6 +388,9 @@ static inline void kvm_arm_pmu_set_irq(CPUState *cs, int irq) {} static inline void kvm_arm_pmu_init(CPUState *cs) {} static inline void kvm_arm_sve_get_vls(CPUState *cs, unsigned long *map) {} + +static inline void kvm_arm_get_virtual_time(CPUState *cs) {} +static inline void kvm_arm_put_virtual_time(CPUState *cs) {} #endif static inline const char *gic_class_name(void) @@ -377,23 +440,20 @@ bool kvm_arm_handle_debug(CPUState *cs, struct kvm_debug_exit_arch *debug_exit); * * Return: TRUE if any hardware breakpoints in use. */ - bool kvm_arm_hw_debug_active(CPUState *cs); /** * kvm_arm_copy_hw_debug_data: - * * @ptr: kvm_guest_debug_arch structure * * Copy the architecture specific debug registers into the * kvm_guest_debug ioctl structure. */ struct kvm_guest_debug_arch; - void kvm_arm_copy_hw_debug_data(struct kvm_guest_debug_arch *ptr); /** - * its_class_name + * its_class_name: * * Return the ITS class name to use depending on whether KVM acceleration * and KVM CAP_SIGNAL_MSI are supported diff --git a/target/arm/m_helper.c b/target/arm/m_helper.c index 76de317e6a..5e8a795d20 100644 --- a/target/arm/m_helper.c +++ b/target/arm/m_helper.c @@ -738,7 +738,8 @@ static uint32_t v7m_integrity_sig(CPUARMState *env, uint32_t lr) */ uint32_t sig = 0xfefa125a; - if (!arm_feature(env, ARM_FEATURE_VFP) || (lr & R_V7M_EXCRET_FTYPE_MASK)) { + if (!cpu_isar_feature(aa32_vfp_simd, env_archcpu(env)) + || (lr & R_V7M_EXCRET_FTYPE_MASK)) { sig |= 1; } return sig; @@ -841,7 +842,7 @@ static void v7m_exception_taken(ARMCPU *cpu, uint32_t lr, bool dotailchain, if (dotailchain) { /* Sanitize LR FType and PREFIX bits */ - if (!arm_feature(env, ARM_FEATURE_VFP)) { + if (!cpu_isar_feature(aa32_vfp_simd, cpu)) { lr |= R_V7M_EXCRET_FTYPE_MASK; } lr = deposit32(lr, 24, 8, 0xff); @@ -1373,7 +1374,7 @@ static void do_v7m_exception_exit(ARMCPU *cpu) ftype = excret & R_V7M_EXCRET_FTYPE_MASK; - if (!arm_feature(env, ARM_FEATURE_VFP) && !ftype) { + if (!ftype && !cpu_isar_feature(aa32_vfp_simd, cpu)) { qemu_log_mask(LOG_GUEST_ERROR, "M profile: zero FTYPE in exception " "exit PC value 0x%" PRIx32 " is UNPREDICTABLE " "if FPU not present\n", @@ -2185,6 +2186,7 @@ void arm_v7m_cpu_do_interrupt(CPUState *cs) "...handling as semihosting call 0x%x\n", env->regs[0]); env->regs[0] = do_arm_semihosting(env); + env->regs[15] += env->thumb ? 2 : 4; return; case EXCP_BKPT: armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_DEBUG, false); @@ -2449,7 +2451,7 @@ void HELPER(v7m_msr)(CPUARMState *env, uint32_t maskreg, uint32_t val) * SFPA is RAZ/WI from NS. FPCA is RO if NSACR.CP10 == 0, * RES0 if the FPU is not present, and is stored in the S bank */ - if (arm_feature(env, ARM_FEATURE_VFP) && + if (cpu_isar_feature(aa32_vfp_simd, env_archcpu(env)) && extract32(env->v7m.nsacr, 10, 1)) { env->v7m.control[M_REG_S] &= ~R_V7M_CONTROL_FPCA_MASK; env->v7m.control[M_REG_S] |= val & R_V7M_CONTROL_FPCA_MASK; @@ -2564,7 +2566,7 @@ void HELPER(v7m_msr)(CPUARMState *env, uint32_t maskreg, uint32_t val) env->v7m.control[env->v7m.secure] &= ~R_V7M_CONTROL_NPRIV_MASK; env->v7m.control[env->v7m.secure] |= val & R_V7M_CONTROL_NPRIV_MASK; } - if (arm_feature(env, ARM_FEATURE_VFP)) { + if (cpu_isar_feature(aa32_vfp_simd, env_archcpu(env))) { /* * SFPA is RAZ/WI from NS or if no FPU. * FPCA is RO if NSACR.CP10 == 0, RES0 if the FPU is not present. diff --git a/target/arm/machine.c b/target/arm/machine.c index eb28b2381b..c5a2114f51 100644 --- a/target/arm/machine.c +++ b/target/arm/machine.c @@ -9,9 +9,10 @@ static bool vfp_needed(void *opaque) { ARMCPU *cpu = opaque; - CPUARMState *env = &cpu->env; - return arm_feature(env, ARM_FEATURE_VFP); + return (arm_feature(&cpu->env, ARM_FEATURE_AARCH64) + ? cpu_isar_feature(aa64_fp_simd, cpu) + : cpu_isar_feature(aa32_vfp_simd, cpu)); } static int get_fpscr(QEMUFile *f, void *opaque, size_t size, @@ -642,6 +643,12 @@ static int cpu_pre_save(void *opaque) /* This should never fail */ abort(); } + + /* + * kvm_arm_cpu_pre_save() must be called after + * write_kvmstate_to_list() + */ + kvm_arm_cpu_pre_save(cpu); } else { if (!write_cpustate_to_list(cpu, false)) { /* This should never fail. */ @@ -744,6 +751,7 @@ static int cpu_post_load(void *opaque, int version_id) * we're using it. */ write_list_to_cpustate(cpu); + kvm_arm_cpu_post_load(cpu); } else { if (!write_list_to_cpustate(cpu)) { return -1; diff --git a/target/arm/monitor.c b/target/arm/monitor.c index fa054f8a36..ea6598c412 100644 --- a/target/arm/monitor.c +++ b/target/arm/monitor.c @@ -103,6 +103,7 @@ static const char *cpu_model_advertised_features[] = { "sve128", "sve256", "sve384", "sve512", "sve640", "sve768", "sve896", "sve1024", "sve1152", "sve1280", "sve1408", "sve1536", "sve1664", "sve1792", "sve1920", "sve2048", + "kvm-no-adjvtime", NULL }; @@ -136,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 " @@ -202,9 +206,7 @@ CpuModelExpansionInfo *qmp_query_cpu_model_expansion(CpuModelExpansionType type, return NULL; } } else { - Error *err = NULL; - arm_cpu_finalize_features(ARM_CPU(obj), &err); - assert(err == NULL); + arm_cpu_finalize_features(ARM_CPU(obj), &error_abort); } expansion_info = g_new0(CpuModelExpansionInfo, 1); @@ -217,12 +219,10 @@ CpuModelExpansionInfo *qmp_query_cpu_model_expansion(CpuModelExpansionType type, while ((name = cpu_model_advertised_features[i++]) != NULL) { ObjectProperty *prop = object_property_find(obj, name, NULL); if (prop) { - Error *err = NULL; QObject *value; assert(prop->get); - value = object_property_get_qobject(obj, name, &err); - assert(!err); + value = object_property_get_qobject(obj, name, &error_abort); qdict_put_obj(qdict_out, name, value); } diff --git a/target/arm/neon_helper.c b/target/arm/neon_helper.c index 4259056723..c7a8438b42 100644 --- a/target/arm/neon_helper.c +++ b/target/arm/neon_helper.c @@ -615,24 +615,9 @@ NEON_VOP(abd_u32, neon_u32, 1) } else { \ dest = src1 << tmp; \ }} while (0) -NEON_VOP(shl_u8, neon_u8, 4) NEON_VOP(shl_u16, neon_u16, 2) -NEON_VOP(shl_u32, neon_u32, 1) #undef NEON_FN -uint64_t HELPER(neon_shl_u64)(uint64_t val, uint64_t shiftop) -{ - int8_t shift = (int8_t)shiftop; - if (shift >= 64 || shift <= -64) { - val = 0; - } else if (shift < 0) { - val >>= -shift; - } else { - val <<= shift; - } - return val; -} - #define NEON_FN(dest, src1, src2) do { \ int8_t tmp; \ tmp = (int8_t)src2; \ @@ -645,27 +630,9 @@ uint64_t HELPER(neon_shl_u64)(uint64_t val, uint64_t shiftop) } else { \ dest = src1 << tmp; \ }} while (0) -NEON_VOP(shl_s8, neon_s8, 4) NEON_VOP(shl_s16, neon_s16, 2) -NEON_VOP(shl_s32, neon_s32, 1) #undef NEON_FN -uint64_t HELPER(neon_shl_s64)(uint64_t valop, uint64_t shiftop) -{ - int8_t shift = (int8_t)shiftop; - int64_t val = valop; - if (shift >= 64) { - val = 0; - } else if (shift <= -64) { - val >>= 63; - } else if (shift < 0) { - val >>= -shift; - } else { - val <<= shift; - } - return val; -} - #define NEON_FN(dest, src1, src2) do { \ int8_t tmp; \ tmp = (int8_t)src2; \ @@ -1162,60 +1129,6 @@ NEON_VOP(mul_u8, neon_u8, 4) NEON_VOP(mul_u16, neon_u16, 2) #undef NEON_FN -/* Polynomial multiplication is like integer multiplication except the - partial products are XORed, not added. */ -uint32_t HELPER(neon_mul_p8)(uint32_t op1, uint32_t op2) -{ - uint32_t mask; - uint32_t result; - result = 0; - while (op1) { - mask = 0; - if (op1 & 1) - mask |= 0xff; - if (op1 & (1 << 8)) - mask |= (0xff << 8); - if (op1 & (1 << 16)) - mask |= (0xff << 16); - if (op1 & (1 << 24)) - mask |= (0xff << 24); - result ^= op2 & mask; - op1 = (op1 >> 1) & 0x7f7f7f7f; - op2 = (op2 << 1) & 0xfefefefe; - } - return result; -} - -uint64_t HELPER(neon_mull_p8)(uint32_t op1, uint32_t op2) -{ - uint64_t result = 0; - uint64_t mask; - uint64_t op2ex = op2; - op2ex = (op2ex & 0xff) | - ((op2ex & 0xff00) << 8) | - ((op2ex & 0xff0000) << 16) | - ((op2ex & 0xff000000) << 24); - while (op1) { - mask = 0; - if (op1 & 1) { - mask |= 0xffff; - } - if (op1 & (1 << 8)) { - mask |= (0xffffU << 16); - } - if (op1 & (1 << 16)) { - mask |= (0xffffULL << 32); - } - if (op1 & (1 << 24)) { - mask |= (0xffffULL << 48); - } - result ^= op2ex & mask; - op1 = (op1 >> 1) & 0x7f7f7f7f; - op2ex <<= 1; - } - return result; -} - #define NEON_FN(dest, src1, src2) dest = (src1 & src2) ? -1 : 0 NEON_VOP(tst_u8, neon_u8, 4) NEON_VOP(tst_u16, neon_u16, 2) @@ -2207,33 +2120,3 @@ void HELPER(neon_zip16)(void *vd, void *vm) rm[0] = m0; rd[0] = d0; } - -/* Helper function for 64 bit polynomial multiply case: - * perform PolynomialMult(op1, op2) and return either the top or - * bottom half of the 128 bit result. - */ -uint64_t HELPER(neon_pmull_64_lo)(uint64_t op1, uint64_t op2) -{ - int bitnum; - uint64_t res = 0; - - for (bitnum = 0; bitnum < 64; bitnum++) { - if (op1 & (1ULL << bitnum)) { - res ^= op2 << bitnum; - } - } - return res; -} -uint64_t HELPER(neon_pmull_64_hi)(uint64_t op1, uint64_t op2) -{ - int bitnum; - uint64_t res = 0; - - /* bit 0 of op1 can't influence the high 64 bits at all */ - for (bitnum = 1; bitnum < 64; bitnum++) { - if (op1 & (1ULL << bitnum)) { - res ^= op2 >> (64 - bitnum); - } - } - return res; -} diff --git a/target/arm/op_helper.c b/target/arm/op_helper.c index b529d6c1bf..eb0de080f1 100644 --- a/target/arm/op_helper.c +++ b/target/arm/op_helper.c @@ -17,7 +17,6 @@ * License along with this library; if not, see . */ #include "qemu/osdep.h" -#include "qemu/units.h" #include "qemu/log.h" #include "qemu/main-loop.h" #include "cpu.h" @@ -295,7 +294,12 @@ void HELPER(wfi)(CPUARMState *env, uint32_t insn_len) } if (target_el) { - env->pc -= insn_len; + if (env->aarch64) { + env->pc -= insn_len; + } else { + env->regs[15] -= insn_len; + } + raise_exception(env, EXCP_UDEF, syn_wfx(1, 0xe, 0, insn_len == 2), target_el); } @@ -382,7 +386,14 @@ void HELPER(exception_bkpt_insn)(CPUARMState *env, uint32_t syndrome) uint32_t HELPER(cpsr_read)(CPUARMState *env) { - return cpsr_read(env) & ~(CPSR_EXEC | CPSR_RESERVED); + /* + * We store the ARMv8 PSTATE.SS bit in env->uncached_cpsr. + * This is convenient for populating SPSR_ELx, but must be + * hidden from aarch32 mode, where it is not visible. + * + * TODO: ARMv8.4-DIT -- need to move SS somewhere else. + */ + return cpsr_read(env) & ~(CPSR_EXEC | PSTATE_SS); } void HELPER(cpsr_write)(CPUARMState *env, uint32_t val, uint32_t mask) @@ -395,11 +406,14 @@ void HELPER(cpsr_write)(CPUARMState *env, uint32_t val, uint32_t mask) /* Write the CPSR for a 32-bit exception return */ void HELPER(cpsr_write_eret)(CPUARMState *env, uint32_t val) { + uint32_t mask; + qemu_mutex_lock_iothread(); arm_call_pre_el_change_hook(env_archcpu(env)); qemu_mutex_unlock_iothread(); - cpsr_write(env, val, CPSR_ERET_MASK, CPSRWriteExceptionReturn); + mask = aarch32_cpsr_valid_mask(env->features, &env_archcpu(env)->isar); + cpsr_write(env, val, mask, CPSRWriteExceptionReturn); /* Generated code has already stored the new PC value, but * without masking out its low bits, because which bits need @@ -603,6 +617,27 @@ void HELPER(access_check_cp_reg)(CPUARMState *env, void *rip, uint32_t syndrome, raise_exception(env, EXCP_UDEF, syndrome, exception_target_el(env)); } + /* + * Check for an EL2 trap due to HSTR_EL2. We expect EL0 accesses + * to sysregs non accessible at EL0 to have UNDEF-ed already. + */ + if (!is_a64(env) && arm_current_el(env) < 2 && ri->cp == 15 && + (arm_hcr_el2_eff(env) & (HCR_E2H | HCR_TGE)) != (HCR_E2H | HCR_TGE)) { + uint32_t mask = 1 << ri->crn; + + if (ri->type & ARM_CP_64BIT) { + mask = 1 << ri->crm; + } + + /* T4 and T14 are RES0 */ + mask &= ~((1 << 4) | (1 << 14)); + + if (env->cp15.hstr_el2 & mask) { + target_el = 2; + goto exept; + } + } + if (!ri->accessfn) { return; } @@ -652,6 +687,7 @@ void HELPER(access_check_cp_reg)(CPUARMState *env, void *rip, uint32_t syndrome, g_assert_not_reached(); } +exept: raise_exception(env, EXCP_UDEF, syndrome, target_el); } @@ -899,95 +935,3 @@ uint32_t HELPER(ror_cc)(CPUARMState *env, uint32_t x, uint32_t i) return ((uint32_t)x >> shift) | (x << (32 - shift)); } } - -void HELPER(dc_zva)(CPUARMState *env, uint64_t vaddr_in) -{ - /* - * Implement DC ZVA, which zeroes a fixed-length block of memory. - * Note that we do not implement the (architecturally mandated) - * alignment fault for attempts to use this on Device memory - * (which matches the usual QEMU behaviour of not implementing either - * alignment faults or any memory attribute handling). - */ - - ARMCPU *cpu = env_archcpu(env); - uint64_t blocklen = 4 << cpu->dcz_blocksize; - uint64_t vaddr = vaddr_in & ~(blocklen - 1); - -#ifndef CONFIG_USER_ONLY - { - /* - * Slightly awkwardly, QEMU's TARGET_PAGE_SIZE may be less than - * the block size so we might have to do more than one TLB lookup. - * We know that in fact for any v8 CPU the page size is at least 4K - * and the block size must be 2K or less, but TARGET_PAGE_SIZE is only - * 1K as an artefact of legacy v5 subpage support being present in the - * same QEMU executable. So in practice the hostaddr[] array has - * two entries, given the current setting of TARGET_PAGE_BITS_MIN. - */ - int maxidx = DIV_ROUND_UP(blocklen, TARGET_PAGE_SIZE); - void *hostaddr[DIV_ROUND_UP(2 * KiB, 1 << TARGET_PAGE_BITS_MIN)]; - int try, i; - unsigned mmu_idx = cpu_mmu_index(env, false); - TCGMemOpIdx oi = make_memop_idx(MO_UB, mmu_idx); - - assert(maxidx <= ARRAY_SIZE(hostaddr)); - - for (try = 0; try < 2; try++) { - - for (i = 0; i < maxidx; i++) { - hostaddr[i] = tlb_vaddr_to_host(env, - vaddr + TARGET_PAGE_SIZE * i, - 1, mmu_idx); - if (!hostaddr[i]) { - break; - } - } - if (i == maxidx) { - /* - * If it's all in the TLB it's fair game for just writing to; - * we know we don't need to update dirty status, etc. - */ - for (i = 0; i < maxidx - 1; i++) { - memset(hostaddr[i], 0, TARGET_PAGE_SIZE); - } - memset(hostaddr[i], 0, blocklen - (i * TARGET_PAGE_SIZE)); - return; - } - /* - * OK, try a store and see if we can populate the tlb. This - * might cause an exception if the memory isn't writable, - * in which case we will longjmp out of here. We must for - * this purpose use the actual register value passed to us - * so that we get the fault address right. - */ - helper_ret_stb_mmu(env, vaddr_in, 0, oi, GETPC()); - /* Now we can populate the other TLB entries, if any */ - for (i = 0; i < maxidx; i++) { - uint64_t va = vaddr + TARGET_PAGE_SIZE * i; - if (va != (vaddr_in & TARGET_PAGE_MASK)) { - helper_ret_stb_mmu(env, va, 0, oi, GETPC()); - } - } - } - - /* - * Slow path (probably attempt to do this to an I/O device or - * similar, or clearing of a block of code we have translations - * cached for). Just do a series of byte writes as the architecture - * demands. It's not worth trying to use a cpu_physical_memory_map(), - * memset(), unmap() sequence here because: - * + we'd need to account for the blocksize being larger than a page - * + the direct-RAM access case is almost always going to be dealt - * with in the fastpath code above, so there's no speed benefit - * + we would have to deal with the map returning NULL because the - * bounce buffer was in use - */ - for (i = 0; i < blocklen; i++) { - helper_ret_stb_mmu(env, vaddr + i, 0, oi, GETPC()); - } - } -#else - memset(g2h(vaddr), 0, blocklen); -#endif -} diff --git a/target/arm/pauth_helper.c b/target/arm/pauth_helper.c index d3194f2043..b909630317 100644 --- a/target/arm/pauth_helper.c +++ b/target/arm/pauth_helper.c @@ -89,7 +89,7 @@ static uint64_t pac_sub(uint64_t i) uint64_t o = 0; int b; - for (b = 0; b < 64; b += 16) { + for (b = 0; b < 64; b += 4) { o |= (uint64_t)sub[(i >> b) & 0xf] << b; } return o; @@ -104,7 +104,7 @@ static uint64_t pac_inv_sub(uint64_t i) uint64_t o = 0; int b; - for (b = 0; b < 64; b += 16) { + for (b = 0; b < 64; b += 4) { o |= (uint64_t)inv_sub[(i >> b) & 0xf] << b; } return o; @@ -320,7 +320,8 @@ static uint64_t pauth_addpac(CPUARMState *env, uint64_t ptr, uint64_t modifier, static uint64_t pauth_original_ptr(uint64_t ptr, ARMVAParameters param) { - uint64_t extfield = -param.select; + /* Note that bit 55 is used whether or not the regime has 2 ranges. */ + uint64_t extfield = sextract64(ptr, 55, 1); int bot_pac_bit = 64 - param.tsz; int top_pac_bit = 64 - 8 * param.tbi; @@ -371,7 +372,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 +390,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/sve_helper.c b/target/arm/sve_helper.c index fc0c1755d2..fdfa652094 100644 --- a/target/arm/sve_helper.c +++ b/target/arm/sve_helper.c @@ -25,6 +25,7 @@ #include "exec/helper-proto.h" #include "tcg/tcg-gvec-desc.h" #include "fpu/softfloat.h" +#include "tcg/tcg.h" /* Note that vector data is stored in host-endian 64-bit chunks, diff --git a/target/arm/tlb_helper.c b/target/arm/tlb_helper.c index 5feb312941..e63f8bda29 100644 --- a/target/arm/tlb_helper.c +++ b/target/arm/tlb_helper.c @@ -44,7 +44,7 @@ static inline uint32_t merge_syn_data_abort(uint32_t template_syn, syn = syn_data_abort_with_iss(same_el, 0, 0, 0, 0, 0, ea, 0, s1ptw, is_write, fsc, - false); + true); /* Merge the runtime syndrome with the template syndrome. */ syn |= template_syn; } diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c index d4bebbe629..7580e46367 100644 --- a/target/arm/translate-a64.c +++ b/target/arm/translate-a64.c @@ -20,8 +20,8 @@ #include "cpu.h" #include "exec/exec-all.h" -#include "tcg-op.h" -#include "tcg-op-gvec.h" +#include "tcg/tcg-op.h" +#include "tcg/tcg-op-gvec.h" #include "qemu/log.h" #include "arm_ldst.h" #include "translate.h" @@ -105,25 +105,39 @@ 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: + case ARMMMUIdx_E10_1_PAN: + useridx = ARMMMUIdx_E10_0; + break; + case ARMMMUIdx_E20_2: + case ARMMMUIdx_E20_2_PAN: + useridx = ARMMMUIdx_E20_0; + break; + case ARMMMUIdx_SE10_1: + case ARMMMUIdx_SE10_1_PAN: + useridx = ARMMMUIdx_SE10_0; + break; + default: + g_assert_not_reached(); + } } return arm_to_core_mmu_idx(useridx); } @@ -175,8 +189,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 { @@ -215,7 +228,18 @@ static void gen_a64_set_pc(DisasContext *s, TCGv_i64 src) static TCGv_i64 clean_data_tbi(DisasContext *s, TCGv_i64 addr) { TCGv_i64 clean = new_tmp_a64(s); + /* + * In order to get the correct value in the FAR_ELx register, + * we must present the memory subsystem with the "dirty" address + * including the TBI. In system mode we can make this work via + * the TLB, dropping the TBI during translation. But for user-only + * mode we don't have that option, and must remove the top byte now. + */ +#ifdef CONFIG_USER_ONLY gen_top_byte_ignore(s, clean, addr, s->tbid); +#else + tcg_gen_mov_i64(clean, addr); +#endif return clean; } @@ -1589,6 +1613,34 @@ static void handle_msr_i(DisasContext *s, uint32_t insn, s->base.is_jmp = DISAS_NEXT; break; + case 0x03: /* UAO */ + if (!dc_isar_feature(aa64_uao, s) || s->current_el == 0) { + goto do_unallocated; + } + if (crm & 1) { + set_pstate_bits(PSTATE_UAO); + } else { + clear_pstate_bits(PSTATE_UAO); + } + t1 = tcg_const_i32(s->current_el); + gen_helper_rebuild_hflags_a64(cpu_env, t1); + tcg_temp_free_i32(t1); + break; + + case 0x04: /* PAN */ + if (!dc_isar_feature(aa64_pan, s) || s->current_el == 0) { + goto do_unallocated; + } + if (crm & 1) { + set_pstate_bits(PSTATE_PAN); + } else { + clear_pstate_bits(PSTATE_PAN); + } + t1 = tcg_const_i32(s->current_el); + gen_helper_rebuild_hflags_a64(cpu_env, t1); + tcg_temp_free_i32(t1); + break; + case 0x05: /* SPSel */ if (s->current_el == 0) { goto do_unallocated; @@ -1743,7 +1795,7 @@ static void handle_sys(DisasContext *s, uint32_t insn, bool isread, return; case ARM_CP_DC_ZVA: /* Writes clear the aligned block of memory which rt points into. */ - tcg_rt = cpu_reg(s, rt); + tcg_rt = clean_data_tbi(s, cpu_reg(s, rt)); gen_helper_dc_zva(cpu_env, tcg_rt); return; default: @@ -1937,7 +1989,7 @@ static void disas_exc(DisasContext *s, uint32_t insn) break; } #endif - gen_exception_internal_insn(s, s->base.pc_next, EXCP_SEMIHOST); + gen_exception_internal_insn(s, s->pc_curr, EXCP_SEMIHOST); } else { unsupported_encoding(s, insn); } @@ -3101,6 +3153,8 @@ static void disas_ldst_atomic(DisasContext *s, uint32_t insn, int rs = extract32(insn, 16, 5); int rn = extract32(insn, 5, 5); int o3_opc = extract32(insn, 12, 4); + bool r = extract32(insn, 22, 1); + bool a = extract32(insn, 23, 1); TCGv_i64 tcg_rs, clean_addr; AtomicThreeOpFn *fn; @@ -3136,6 +3190,13 @@ static void disas_ldst_atomic(DisasContext *s, uint32_t insn, case 010: /* SWP */ fn = tcg_gen_atomic_xchg_i64; break; + case 014: /* LDAPR, LDAPRH, LDAPRB */ + if (!dc_isar_feature(aa64_rcpc_8_3, s) || + rs != 31 || a != 1 || r != 0) { + unallocated_encoding(s); + return; + } + break; default: unallocated_encoding(s); return; @@ -3145,6 +3206,21 @@ static void disas_ldst_atomic(DisasContext *s, uint32_t insn, gen_check_sp_alignment(s); } clean_addr = clean_data_tbi(s, cpu_reg_sp(s, rn)); + + if (o3_opc == 014) { + /* + * LDAPR* are a special case because they are a simple load, not a + * fetch-and-do-something op. + * The architectural consistency requirements here are weaker than + * full load-acquire (we only need "load-acquire processor consistent"), + * but we choose to implement them as full LDAQ. + */ + do_gpr_ld(s, cpu_reg(s, rt), clean_addr, size, false, false, + true, rt, disas_ldst_compute_iss_sf(size, false, 0), true); + tcg_gen_mb(TCG_MO_ALL | TCG_BAR_LDAQ); + return; + } + tcg_rs = read_cpu_reg(s, rs, true); if (o3_opc == 1) { /* LDCLR */ @@ -3218,6 +3294,88 @@ static void disas_ldst_pac(DisasContext *s, uint32_t insn, } } +/* + * LDAPR/STLR (unscaled immediate) + * + * 31 30 24 22 21 12 10 5 0 + * +------+-------------+-----+---+--------+-----+----+-----+ + * | size | 0 1 1 0 0 1 | opc | 0 | imm9 | 0 0 | Rn | Rt | + * +------+-------------+-----+---+--------+-----+----+-----+ + * + * Rt: source or destination register + * Rn: base register + * imm9: unscaled immediate offset + * opc: 00: STLUR*, 01/10/11: various LDAPUR* + * size: size of load/store + */ +static void disas_ldst_ldapr_stlr(DisasContext *s, uint32_t insn) +{ + int rt = extract32(insn, 0, 5); + int rn = extract32(insn, 5, 5); + int offset = sextract32(insn, 12, 9); + int opc = extract32(insn, 22, 2); + int size = extract32(insn, 30, 2); + TCGv_i64 clean_addr, dirty_addr; + bool is_store = false; + bool is_signed = false; + bool extend = false; + bool iss_sf; + + if (!dc_isar_feature(aa64_rcpc_8_4, s)) { + unallocated_encoding(s); + return; + } + + switch (opc) { + case 0: /* STLURB */ + is_store = true; + break; + case 1: /* LDAPUR* */ + break; + case 2: /* LDAPURS* 64-bit variant */ + if (size == 3) { + unallocated_encoding(s); + return; + } + is_signed = true; + break; + case 3: /* LDAPURS* 32-bit variant */ + if (size > 1) { + unallocated_encoding(s); + return; + } + is_signed = true; + extend = true; /* zero-extend 32->64 after signed load */ + break; + default: + g_assert_not_reached(); + } + + iss_sf = disas_ldst_compute_iss_sf(size, is_signed, opc); + + if (rn == 31) { + gen_check_sp_alignment(s); + } + + dirty_addr = read_cpu_reg_sp(s, rn, 1); + tcg_gen_addi_i64(dirty_addr, dirty_addr, offset); + clean_addr = clean_data_tbi(s, dirty_addr); + + if (is_store) { + /* Store-Release semantics */ + tcg_gen_mb(TCG_MO_ALL | TCG_BAR_STRL); + do_gpr_st(s, cpu_reg(s, rt), clean_addr, size, true, rt, iss_sf, true); + } else { + /* + * Load-AcquirePC semantics; we implement as the slightly more + * restrictive Load-Acquire. + */ + do_gpr_ld(s, cpu_reg(s, rt), clean_addr, size, is_signed, extend, + true, rt, iss_sf, true); + tcg_gen_mb(TCG_MO_ALL | TCG_BAR_LDAQ); + } +} + /* Load/store register (all forms) */ static void disas_ldst_reg(DisasContext *s, uint32_t insn) { @@ -3569,6 +3727,14 @@ static void disas_ldst(DisasContext *s, uint32_t insn) case 0x0d: /* AdvSIMD load/store single structure */ disas_ldst_single_struct(s, insn); break; + case 0x19: /* LDAPR/STLR (unscaled immediate) */ + if (extract32(insn, 10, 2) != 0 || + extract32(insn, 21, 1) != 0) { + unallocated_encoding(s); + break; + } + disas_ldst_ldapr_stlr(s, insn); + break; default: unallocated_encoding(s); break; @@ -5778,8 +5944,6 @@ static void handle_fp_fcvt(DisasContext *s, int opcode, TCGv_i32 tcg_rd = tcg_temp_new_i32(); gen_helper_vfp_fcvt_f16_to_f32(tcg_rd, tcg_rn, tcg_fpst, tcg_ahp); write_fp_sreg(s, rd, tcg_rd); - tcg_temp_free_ptr(tcg_fpst); - tcg_temp_free_i32(tcg_ahp); tcg_temp_free_i32(tcg_rd); } else { /* Half to double */ @@ -5789,6 +5953,8 @@ static void handle_fp_fcvt(DisasContext *s, int opcode, tcg_temp_free_i64(tcg_rd); } tcg_temp_free_i32(tcg_rn); + tcg_temp_free_ptr(tcg_fpst); + tcg_temp_free_i32(tcg_ahp); break; } default: @@ -6854,6 +7020,7 @@ static void disas_simd_ext(DisasContext *s, uint32_t insn) tcg_temp_free_i64(tcg_resl); write_vec_element(s, tcg_resh, rd, 1, MO_64); tcg_temp_free_i64(tcg_resh); + clear_vec_high(s, true, rd); } /* TBL/TBX @@ -6922,6 +7089,7 @@ static void disas_simd_tb(DisasContext *s, uint32_t insn) tcg_temp_free_i64(tcg_resl); write_vec_element(s, tcg_resh, rd, 1, MO_64); tcg_temp_free_i64(tcg_resh); + clear_vec_high(s, true, rd); } /* ZIP/UZP/TRN @@ -7011,6 +7179,7 @@ static void disas_simd_zip_trn(DisasContext *s, uint32_t insn) tcg_temp_free_i64(tcg_resl); write_vec_element(s, tcg_resh, rd, 1, MO_64); tcg_temp_free_i64(tcg_resh); + clear_vec_high(s, true, rd); } /* @@ -7253,7 +7422,7 @@ static void handle_simd_dupe(DisasContext *s, int is_q, int rd, int rn, int imm5) { int size = ctz32(imm5); - int index = imm5 >> (size + 1); + int index; if (size > 3 || (size == 3 && !is_q)) { unallocated_encoding(s); @@ -7264,6 +7433,7 @@ static void handle_simd_dupe(DisasContext *s, int is_q, int rd, int rn, return; } + index = imm5 >> (size + 1); tcg_gen_gvec_dup_mem(size, vec_full_reg_offset(s, rd), vec_reg_offset(s, rn, index, size), is_q ? 16 : 8, vec_full_reg_size(s)); @@ -7368,6 +7538,9 @@ static void handle_simd_inse(DisasContext *s, int rd, int rn, write_vec_element(s, tmp, rd, dst_index, size); tcg_temp_free_i64(tmp); + + /* INS is considered a 128-bit write for SVE. */ + clear_vec_high(s, true, rd); } @@ -7397,6 +7570,9 @@ static void handle_simd_insg(DisasContext *s, int rd, int rn, int imm5) idx = extract32(imm5, 1 + size, 4 - size); write_vec_element(s, cpu_reg(s, rn), rd, idx, size); + + /* INS is considered a 128-bit write for SVE. */ + clear_vec_high(s, true, rd); } /* @@ -8694,9 +8870,9 @@ static void handle_3same_64(DisasContext *s, int opcode, bool u, break; case 0x8: /* SSHL, USHL */ if (u) { - gen_helper_neon_shl_u64(tcg_rd, tcg_rn, tcg_rm); + gen_ushl_i64(tcg_rd, tcg_rn, tcg_rm); } else { - gen_helper_neon_shl_s64(tcg_rd, tcg_rn, tcg_rm); + gen_sshl_i64(tcg_rd, tcg_rn, tcg_rm); } break; case 0x9: /* SQSHL, UQSHL */ @@ -10230,6 +10406,9 @@ static void disas_simd_shift_imm(DisasContext *s, uint32_t insn) bool is_u = extract32(insn, 29, 1); bool is_q = extract32(insn, 30, 1); + /* data_proc_simd[] has sent immh == 0 to disas_simd_mod_imm. */ + assert(immh != 0); + switch (opcode) { case 0x08: /* SRI */ if (!is_u) { @@ -10492,10 +10671,6 @@ static void handle_3rd_widening(DisasContext *s, int is_q, int is_u, int size, gen_helper_neon_addl_saturate_s32(tcg_passres, cpu_env, tcg_passres, tcg_passres); break; - case 14: /* PMULL */ - assert(size == 0); - gen_helper_neon_mull_p8(tcg_passres, tcg_op1, tcg_op2); - break; default: g_assert_not_reached(); } @@ -10607,30 +10782,6 @@ static void handle_3rd_narrowing(DisasContext *s, int is_q, int is_u, int size, clear_vec_high(s, is_q, rd); } -static void handle_pmull_64(DisasContext *s, int is_q, int rd, int rn, int rm) -{ - /* PMULL of 64 x 64 -> 128 is an odd special case because it - * is the only three-reg-diff instruction which produces a - * 128-bit wide result from a single operation. However since - * it's possible to calculate the two halves more or less - * separately we just use two helper calls. - */ - TCGv_i64 tcg_op1 = tcg_temp_new_i64(); - TCGv_i64 tcg_op2 = tcg_temp_new_i64(); - TCGv_i64 tcg_res = tcg_temp_new_i64(); - - read_vec_element(s, tcg_op1, rn, is_q, MO_64); - read_vec_element(s, tcg_op2, rm, is_q, MO_64); - gen_helper_neon_pmull_64_lo(tcg_res, tcg_op1, tcg_op2); - write_vec_element(s, tcg_res, rd, 0, MO_64); - gen_helper_neon_pmull_64_hi(tcg_res, tcg_op1, tcg_op2); - write_vec_element(s, tcg_res, rd, 1, MO_64); - - tcg_temp_free_i64(tcg_op1); - tcg_temp_free_i64(tcg_op2); - tcg_temp_free_i64(tcg_res); -} - /* AdvSIMD three different * 31 30 29 28 24 23 22 21 20 16 15 12 11 10 9 5 4 0 * +---+---+---+-----------+------+---+------+--------+-----+------+------+ @@ -10683,11 +10834,21 @@ static void disas_simd_three_reg_diff(DisasContext *s, uint32_t insn) handle_3rd_narrowing(s, is_q, is_u, size, opcode, rd, rn, rm); break; case 14: /* PMULL, PMULL2 */ - if (is_u || size == 1 || size == 2) { + if (is_u) { unallocated_encoding(s); return; } - if (size == 3) { + switch (size) { + case 0: /* PMULL.P8 */ + if (!fp_access_check(s)) { + return; + } + /* The Q field specifies lo/hi half input for this insn. */ + gen_gvec_op3_ool(s, true, rd, rn, rm, is_q, + gen_helper_neon_pmull_h); + break; + + case 3: /* PMULL.P64 */ if (!dc_isar_feature(aa64_pmull, s)) { unallocated_encoding(s); return; @@ -10695,10 +10856,16 @@ static void disas_simd_three_reg_diff(DisasContext *s, uint32_t insn) if (!fp_access_check(s)) { return; } - handle_pmull_64(s, is_q, rd, rn, rm); - return; + /* The Q field specifies lo/hi half input for this insn. */ + gen_gvec_op3_ool(s, true, rd, rn, rm, is_q, + gen_helper_gvec_pmull_q); + break; + + default: + unallocated_encoding(s); + break; } - goto is_widening; + return; case 9: /* SQDMLAL, SQDMLAL2 */ case 11: /* SQDMLSL, SQDMLSL2 */ case 13: /* SQDMULL, SQDMULL2 */ @@ -10719,7 +10886,6 @@ static void disas_simd_three_reg_diff(DisasContext *s, uint32_t insn) unallocated_encoding(s); return; } - is_widening: if (!fp_access_check(s)) { return; } @@ -11091,6 +11257,10 @@ static void disas_simd_3same_int(DisasContext *s, uint32_t insn) is_q ? 16 : 8, vec_full_reg_size(s), (u ? uqsub_op : sqsub_op) + size); return; + case 0x08: /* SSHL, USHL */ + gen_gvec_op3(s, is_q, rd, rn, rm, + u ? &ushl_op[size] : &sshl_op[size]); + return; case 0x0c: /* SMAX, UMAX */ if (u) { gen_gvec_fn3(s, is_q, rd, rn, rm, tcg_gen_gvec_umax, size); @@ -11115,9 +11285,10 @@ static void disas_simd_3same_int(DisasContext *s, uint32_t insn) case 0x13: /* MUL, PMUL */ if (!u) { /* MUL */ gen_gvec_fn3(s, is_q, rd, rn, rm, tcg_gen_gvec_mul, size); - return; + } else { /* PMUL */ + gen_gvec_op3_ool(s, is_q, rd, rn, rm, 0, gen_helper_gvec_pmul_b); } - break; + return; case 0x12: /* MLA, MLS */ if (u) { gen_gvec_op3(s, is_q, rd, rn, rm, &mls_op[size]); @@ -11206,16 +11377,6 @@ static void disas_simd_3same_int(DisasContext *s, uint32_t insn) genfn = fns[size][u]; break; } - case 0x8: /* SSHL, USHL */ - { - static NeonGenTwoOpFn * const fns[3][2] = { - { gen_helper_neon_shl_s8, gen_helper_neon_shl_u8 }, - { gen_helper_neon_shl_s16, gen_helper_neon_shl_u16 }, - { gen_helper_neon_shl_s32, gen_helper_neon_shl_u32 }, - }; - genfn = fns[size][u]; - break; - } case 0x9: /* SQSHL, UQSHL */ { static NeonGenTwoOpEnvFn * const fns[3][2] = { @@ -11257,11 +11418,6 @@ static void disas_simd_3same_int(DisasContext *s, uint32_t insn) genfn = fns[size][u]; break; } - case 0x13: /* MUL, PMUL */ - assert(u); /* PMUL */ - assert(size == 0); - genfn = gen_helper_neon_mul_p8; - break; case 0x16: /* SQDMULH, SQRDMULH */ { static NeonGenTwoOpEnvFn * const fns[2][2] = { @@ -13585,6 +13741,8 @@ static void disas_crypto_three_reg_sha512(DisasContext *s, uint32_t insn) feature = dc_isar_feature(aa64_sha3, s); genfn = NULL; break; + default: + g_assert_not_reached(); } } else { switch (opcode) { @@ -14157,7 +14315,7 @@ static void aarch64_tr_init_disas_context(DisasContextBase *dcbase, dc->condexec_mask = 0; dc->condexec_cond = 0; core_mmu_idx = FIELD_EX32(tb_flags, TBFLAG_ANY, MMUIDX); - dc->mmu_idx = core_to_arm_mmu_idx(env, core_mmu_idx); + dc->mmu_idx = core_to_aa64_mmu_idx(core_mmu_idx); dc->tbii = FIELD_EX32(tb_flags, TBFLAG_A64, TBII); dc->tbid = FIELD_EX32(tb_flags, TBFLAG_A64, TBID); dc->current_el = arm_mmu_idx_to_el(dc->mmu_idx); @@ -14170,6 +14328,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-sve.c b/target/arm/translate-sve.c index 5d7edd0907..b35bad245e 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/translate-sve.c @@ -20,9 +20,9 @@ #include "qemu/osdep.h" #include "cpu.h" #include "exec/exec-all.h" -#include "tcg-op.h" -#include "tcg-op-gvec.h" -#include "tcg-gvec-desc.h" +#include "tcg/tcg-op.h" +#include "tcg/tcg-op-gvec.h" +#include "tcg/tcg-gvec-desc.h" #include "qemu/log.h" #include "arm_ldst.h" #include "translate.h" diff --git a/target/arm/translate-vfp.inc.c b/target/arm/translate-vfp.inc.c index 85c5ef897b..b087bbd812 100644 --- a/target/arm/translate-vfp.inc.c +++ b/target/arm/translate-vfp.inc.c @@ -200,13 +200,13 @@ static bool trans_VSEL(DisasContext *s, arg_VSEL *a) return false; } - /* UNDEF accesses to D16-D31 if they don't exist */ - if (dp && !dc_isar_feature(aa32_fp_d32, s) && - ((a->vm | a->vn | a->vd) & 0x10)) { + if (dp && !dc_isar_feature(aa32_fpdp_v2, s)) { return false; } - if (dp && !dc_isar_feature(aa32_fpdp, s)) { + /* UNDEF accesses to D16-D31 if they don't exist */ + if (dp && !dc_isar_feature(aa32_simd_r32, s) && + ((a->vm | a->vn | a->vd) & 0x10)) { return false; } @@ -322,79 +322,6 @@ static bool trans_VSEL(DisasContext *s, arg_VSEL *a) return true; } -static bool trans_VMINMAXNM(DisasContext *s, arg_VMINMAXNM *a) -{ - uint32_t rd, rn, rm; - bool dp = a->dp; - bool vmin = a->op; - TCGv_ptr fpst; - - if (!dc_isar_feature(aa32_vminmaxnm, s)) { - return false; - } - - /* UNDEF accesses to D16-D31 if they don't exist */ - if (dp && !dc_isar_feature(aa32_fp_d32, s) && - ((a->vm | a->vn | a->vd) & 0x10)) { - return false; - } - - if (dp && !dc_isar_feature(aa32_fpdp, s)) { - return false; - } - - rd = a->vd; - rn = a->vn; - rm = a->vm; - - if (!vfp_access_check(s)) { - return true; - } - - fpst = get_fpstatus_ptr(0); - - if (dp) { - TCGv_i64 frn, frm, dest; - - frn = tcg_temp_new_i64(); - frm = tcg_temp_new_i64(); - dest = tcg_temp_new_i64(); - - neon_load_reg64(frn, rn); - neon_load_reg64(frm, rm); - if (vmin) { - gen_helper_vfp_minnumd(dest, frn, frm, fpst); - } else { - gen_helper_vfp_maxnumd(dest, frn, frm, fpst); - } - neon_store_reg64(dest, rd); - tcg_temp_free_i64(frn); - tcg_temp_free_i64(frm); - tcg_temp_free_i64(dest); - } else { - TCGv_i32 frn, frm, dest; - - frn = tcg_temp_new_i32(); - frm = tcg_temp_new_i32(); - dest = tcg_temp_new_i32(); - - neon_load_reg32(frn, rn); - neon_load_reg32(frm, rm); - if (vmin) { - gen_helper_vfp_minnums(dest, frn, frm, fpst); - } else { - gen_helper_vfp_maxnums(dest, frn, frm, fpst); - } - neon_store_reg32(dest, rd); - tcg_temp_free_i32(frn); - tcg_temp_free_i32(frm); - tcg_temp_free_i32(dest); - } - - tcg_temp_free_ptr(fpst); - return true; -} - /* * Table for converting the most common AArch32 encoding of * rounding mode to arm_fprounding order (which matches the @@ -419,13 +346,13 @@ static bool trans_VRINT(DisasContext *s, arg_VRINT *a) return false; } - /* UNDEF accesses to D16-D31 if they don't exist */ - if (dp && !dc_isar_feature(aa32_fp_d32, s) && - ((a->vm | a->vd) & 0x10)) { + if (dp && !dc_isar_feature(aa32_fpdp_v2, s)) { return false; } - if (dp && !dc_isar_feature(aa32_fpdp, s)) { + /* UNDEF accesses to D16-D31 if they don't exist */ + if (dp && !dc_isar_feature(aa32_simd_r32, s) && + ((a->vm | a->vd) & 0x10)) { return false; } @@ -483,12 +410,12 @@ static bool trans_VCVT(DisasContext *s, arg_VCVT *a) return false; } - /* UNDEF accesses to D16-D31 if they don't exist */ - if (dp && !dc_isar_feature(aa32_fp_d32, s) && (a->vm & 0x10)) { + if (dp && !dc_isar_feature(aa32_fpdp_v2, s)) { return false; } - if (dp && !dc_isar_feature(aa32_fpdp, s)) { + /* UNDEF accesses to D16-D31 if they don't exist */ + if (dp && !dc_isar_feature(aa32_simd_r32, s) && (a->vm & 0x10)) { return false; } @@ -555,8 +482,15 @@ static bool trans_VMOV_to_gp(DisasContext *s, arg_VMOV_to_gp *a) int pass; uint32_t offset; + /* SIZE == 2 is a VFP instruction; otherwise NEON. */ + if (a->size == 2 + ? !dc_isar_feature(aa32_fpsp_v2, s) + : !arm_dc_feature(s, ARM_FEATURE_NEON)) { + return false; + } + /* UNDEF accesses to D16-D31 if they don't exist */ - if (!dc_isar_feature(aa32_fp_d32, s) && (a->vn & 0x10)) { + if (!dc_isar_feature(aa32_simd_r32, s) && (a->vn & 0x10)) { return false; } @@ -564,10 +498,6 @@ static bool trans_VMOV_to_gp(DisasContext *s, arg_VMOV_to_gp *a) pass = extract32(offset, 2, 1); offset = extract32(offset, 0, 2) * 8; - if (a->size != 2 && !arm_dc_feature(s, ARM_FEATURE_NEON)) { - return false; - } - if (!vfp_access_check(s)) { return true; } @@ -614,8 +544,15 @@ static bool trans_VMOV_from_gp(DisasContext *s, arg_VMOV_from_gp *a) int pass; uint32_t offset; + /* SIZE == 2 is a VFP instruction; otherwise NEON. */ + if (a->size == 2 + ? !dc_isar_feature(aa32_fpsp_v2, s) + : !arm_dc_feature(s, ARM_FEATURE_NEON)) { + return false; + } + /* UNDEF accesses to D16-D31 if they don't exist */ - if (!dc_isar_feature(aa32_fp_d32, s) && (a->vn & 0x10)) { + if (!dc_isar_feature(aa32_simd_r32, s) && (a->vn & 0x10)) { return false; } @@ -623,10 +560,6 @@ static bool trans_VMOV_from_gp(DisasContext *s, arg_VMOV_from_gp *a) pass = extract32(offset, 2, 1); offset = extract32(offset, 0, 2) * 8; - if (a->size != 2 && !arm_dc_feature(s, ARM_FEATURE_NEON)) { - return false; - } - if (!vfp_access_check(s)) { return true; } @@ -662,7 +595,7 @@ static bool trans_VDUP(DisasContext *s, arg_VDUP *a) } /* UNDEF accesses to D16-D31 if they don't exist */ - if (!dc_isar_feature(aa32_fp_d32, s) && (a->vn & 0x10)) { + if (!dc_isar_feature(aa32_simd_r32, s) && (a->vn & 0x10)) { return false; } @@ -700,6 +633,10 @@ static bool trans_VMSR_VMRS(DisasContext *s, arg_VMSR_VMRS *a) TCGv_i32 tmp; bool ignore_vfp_enabled = false; + if (!dc_isar_feature(aa32_fpsp_v2, s)) { + return false; + } + if (arm_dc_feature(s, ARM_FEATURE_M)) { /* * The only M-profile VFP vmrs/vmsr sysreg is FPSCR. @@ -717,7 +654,7 @@ static bool trans_VMSR_VMRS(DisasContext *s, arg_VMSR_VMRS *a) * VFPv2 allows access to FPSID from userspace; VFPv3 restricts * all ID registers to privileged access only. */ - if (IS_USER(s) && arm_dc_feature(s, ARM_FEATURE_VFP3)) { + if (IS_USER(s) && dc_isar_feature(aa32_fpsp_v3, s)) { return false; } ignore_vfp_enabled = true; @@ -746,7 +683,7 @@ static bool trans_VMSR_VMRS(DisasContext *s, arg_VMSR_VMRS *a) case ARM_VFP_FPINST: case ARM_VFP_FPINST2: /* Not present in VFPv3 */ - if (IS_USER(s) || arm_dc_feature(s, ARM_FEATURE_VFP3)) { + if (IS_USER(s) || dc_isar_feature(aa32_fpsp_v3, s)) { return false; } break; @@ -761,13 +698,25 @@ static bool trans_VMSR_VMRS(DisasContext *s, arg_VMSR_VMRS *a) if (a->l) { /* VMRS, move VFP special register to gp register */ switch (a->reg) { - case ARM_VFP_FPSID: - case ARM_VFP_FPEXC: - case ARM_VFP_FPINST: - case ARM_VFP_FPINST2: case ARM_VFP_MVFR0: case ARM_VFP_MVFR1: case ARM_VFP_MVFR2: + case ARM_VFP_FPSID: + if (s->current_el == 1) { + TCGv_i32 tcg_reg, tcg_rt; + + gen_set_condexec(s); + gen_set_pc_im(s, s->pc_curr); + tcg_reg = tcg_const_i32(a->reg); + tcg_rt = tcg_const_i32(a->rt); + gen_helper_check_hcr_el2_trap(cpu_env, tcg_rt, tcg_reg); + tcg_temp_free_i32(tcg_reg); + tcg_temp_free_i32(tcg_rt); + } + /* fall through */ + case ARM_VFP_FPEXC: + case ARM_VFP_FPINST: + case ARM_VFP_FPINST2: tmp = load_cpu_field(vfp.xregs[a->reg]); break; case ARM_VFP_FPSCR: @@ -832,6 +781,10 @@ static bool trans_VMOV_single(DisasContext *s, arg_VMOV_single *a) { TCGv_i32 tmp; + if (!dc_isar_feature(aa32_fpsp_v2, s)) { + return false; + } + if (!vfp_access_check(s)) { return true; } @@ -861,6 +814,10 @@ static bool trans_VMOV_64_sp(DisasContext *s, arg_VMOV_64_sp *a) { TCGv_i32 tmp; + if (!dc_isar_feature(aa32_fpsp_v2, s)) { + return false; + } + /* * VMOV between two general-purpose registers and two single precision * floating point registers @@ -896,11 +853,15 @@ static bool trans_VMOV_64_dp(DisasContext *s, arg_VMOV_64_dp *a) /* * VMOV between two general-purpose registers and one double precision - * floating point register + * floating point register. Note that this does not require support + * for double precision arithmetic. */ + if (!dc_isar_feature(aa32_fpsp_v2, s)) { + return false; + } /* UNDEF accesses to D16-D31 if they don't exist */ - if (!dc_isar_feature(aa32_fp_d32, s) && (a->vm & 0x10)) { + if (!dc_isar_feature(aa32_simd_r32, s) && (a->vm & 0x10)) { return false; } @@ -934,6 +895,10 @@ static bool trans_VLDR_VSTR_sp(DisasContext *s, arg_VLDR_VSTR_sp *a) uint32_t offset; TCGv_i32 addr, tmp; + if (!dc_isar_feature(aa32_fpsp_v2, s)) { + return false; + } + if (!vfp_access_check(s)) { return true; } @@ -965,8 +930,13 @@ static bool trans_VLDR_VSTR_dp(DisasContext *s, arg_VLDR_VSTR_dp *a) TCGv_i32 addr; TCGv_i64 tmp; + /* Note that this does not require support for double arithmetic. */ + if (!dc_isar_feature(aa32_fpsp_v2, s)) { + return false; + } + /* UNDEF accesses to D16-D31 if they don't exist */ - if (!dc_isar_feature(aa32_fp_d32, s) && (a->vd & 0x10)) { + if (!dc_isar_feature(aa32_simd_r32, s) && (a->vd & 0x10)) { return false; } @@ -1001,6 +971,10 @@ static bool trans_VLDM_VSTM_sp(DisasContext *s, arg_VLDM_VSTM_sp *a) TCGv_i32 addr, tmp; int i, n; + if (!dc_isar_feature(aa32_fpsp_v2, s)) { + return false; + } + n = a->imm; if (n == 0 || (a->vd + n) > 32) { @@ -1074,6 +1048,11 @@ static bool trans_VLDM_VSTM_dp(DisasContext *s, arg_VLDM_VSTM_dp *a) TCGv_i64 tmp; int i, n; + /* Note that this does not require support for double arithmetic. */ + if (!dc_isar_feature(aa32_fpsp_v2, s)) { + return false; + } + n = a->imm >> 1; if (n == 0 || (a->vd + n) > 32 || n > 16) { @@ -1089,7 +1068,7 @@ static bool trans_VLDM_VSTM_dp(DisasContext *s, arg_VLDM_VSTM_dp *a) } /* UNDEF accesses to D16-D31 if they don't exist */ - if (!dc_isar_feature(aa32_fp_d32, s) && (a->vd + n) > 16) { + if (!dc_isar_feature(aa32_simd_r32, s) && (a->vd + n) > 16) { return false; } @@ -1222,6 +1201,10 @@ static bool do_vfp_3op_sp(DisasContext *s, VFPGen3OpSPFn *fn, TCGv_i32 f0, f1, fd; TCGv_ptr fpst; + if (!dc_isar_feature(aa32_fpsp_v2, s)) { + return false; + } + if (!dc_isar_feature(aa32_fpshvec, s) && (veclen != 0 || s->vec_stride != 0)) { return false; @@ -1296,12 +1279,12 @@ static bool do_vfp_3op_dp(DisasContext *s, VFPGen3OpDPFn *fn, TCGv_i64 f0, f1, fd; TCGv_ptr fpst; - /* UNDEF accesses to D16-D31 if they don't exist */ - if (!dc_isar_feature(aa32_fp_d32, s) && ((vd | vn | vm) & 0x10)) { + if (!dc_isar_feature(aa32_fpdp_v2, s)) { return false; } - if (!dc_isar_feature(aa32_fpdp, s)) { + /* UNDEF accesses to D16-D31 if they don't exist */ + if (!dc_isar_feature(aa32_simd_r32, s) && ((vd | vn | vm) & 0x10)) { return false; } @@ -1376,6 +1359,10 @@ static bool do_vfp_2op_sp(DisasContext *s, VFPGen2OpSPFn *fn, int vd, int vm) int veclen = s->vec_len; TCGv_i32 f0, fd; + if (!dc_isar_feature(aa32_fpsp_v2, s)) { + return false; + } + if (!dc_isar_feature(aa32_fpshvec, s) && (veclen != 0 || s->vec_stride != 0)) { return false; @@ -1445,12 +1432,12 @@ static bool do_vfp_2op_dp(DisasContext *s, VFPGen2OpDPFn *fn, int vd, int vm) int veclen = s->vec_len; TCGv_i64 f0, fd; - /* UNDEF accesses to D16-D31 if they don't exist */ - if (!dc_isar_feature(aa32_fp_d32, s) && ((vd | vm) & 0x10)) { + if (!dc_isar_feature(aa32_fpdp_v2, s)) { return false; } - if (!dc_isar_feature(aa32_fpdp, s)) { + /* UNDEF accesses to D16-D31 if they don't exist */ + if (!dc_isar_feature(aa32_simd_r32, s) && ((vd | vm) & 0x10)) { return false; } @@ -1724,7 +1711,43 @@ static bool trans_VDIV_dp(DisasContext *s, arg_VDIV_dp *a) return do_vfp_3op_dp(s, gen_helper_vfp_divd, a->vd, a->vn, a->vm, false); } -static bool trans_VFM_sp(DisasContext *s, arg_VFM_sp *a) +static bool trans_VMINNM_sp(DisasContext *s, arg_VMINNM_sp *a) +{ + if (!dc_isar_feature(aa32_vminmaxnm, s)) { + return false; + } + return do_vfp_3op_sp(s, gen_helper_vfp_minnums, + a->vd, a->vn, a->vm, false); +} + +static bool trans_VMAXNM_sp(DisasContext *s, arg_VMAXNM_sp *a) +{ + if (!dc_isar_feature(aa32_vminmaxnm, s)) { + return false; + } + return do_vfp_3op_sp(s, gen_helper_vfp_maxnums, + a->vd, a->vn, a->vm, false); +} + +static bool trans_VMINNM_dp(DisasContext *s, arg_VMINNM_dp *a) +{ + if (!dc_isar_feature(aa32_vminmaxnm, s)) { + return false; + } + return do_vfp_3op_dp(s, gen_helper_vfp_minnumd, + a->vd, a->vn, a->vm, false); +} + +static bool trans_VMAXNM_dp(DisasContext *s, arg_VMAXNM_dp *a) +{ + if (!dc_isar_feature(aa32_vminmaxnm, s)) { + return false; + } + return do_vfp_3op_dp(s, gen_helper_vfp_maxnumd, + a->vd, a->vn, a->vm, false); +} + +static bool do_vfm_sp(DisasContext *s, arg_VFMA_sp *a, bool neg_n, bool neg_d) { /* * VFNMA : fd = muladd(-fd, fn, fm) @@ -1743,11 +1766,18 @@ static bool trans_VFM_sp(DisasContext *s, arg_VFM_sp *a) /* * Present in VFPv4 only. + * Note that we can't rely on the SIMDFMAC check alone, because + * in a Neon-no-VFP core that ID register field will be non-zero. + */ + if (!dc_isar_feature(aa32_simdfmac, s) || + !dc_isar_feature(aa32_fpsp_v2, s)) { + return false; + } + /* * In v7A, UNPREDICTABLE with non-zero vector length/stride; from * v8A, must UNDEF. We choose to UNDEF for both v7A and v8A. */ - if (!arm_dc_feature(s, ARM_FEATURE_VFP4) || - (s->vec_len != 0 || s->vec_stride != 0)) { + if (s->vec_len != 0 || s->vec_stride != 0) { return false; } @@ -1761,12 +1791,12 @@ static bool trans_VFM_sp(DisasContext *s, arg_VFM_sp *a) neon_load_reg32(vn, a->vn); neon_load_reg32(vm, a->vm); - if (a->o2) { + if (neg_n) { /* VFNMS, VFMS */ gen_helper_vfp_negs(vn, vn); } neon_load_reg32(vd, a->vd); - if (a->o1 & 1) { + if (neg_d) { /* VFNMA, VFNMS */ gen_helper_vfp_negs(vd, vd); } @@ -1782,7 +1812,27 @@ static bool trans_VFM_sp(DisasContext *s, arg_VFM_sp *a) return true; } -static bool trans_VFM_dp(DisasContext *s, arg_VFM_dp *a) +static bool trans_VFMA_sp(DisasContext *s, arg_VFMA_sp *a) +{ + return do_vfm_sp(s, a, false, false); +} + +static bool trans_VFMS_sp(DisasContext *s, arg_VFMS_sp *a) +{ + return do_vfm_sp(s, a, true, false); +} + +static bool trans_VFNMA_sp(DisasContext *s, arg_VFNMA_sp *a) +{ + return do_vfm_sp(s, a, false, true); +} + +static bool trans_VFNMS_sp(DisasContext *s, arg_VFNMS_sp *a) +{ + return do_vfm_sp(s, a, true, true); +} + +static bool do_vfm_dp(DisasContext *s, arg_VFMA_dp *a, bool neg_n, bool neg_d) { /* * VFNMA : fd = muladd(-fd, fn, fm) @@ -1801,20 +1851,30 @@ static bool trans_VFM_dp(DisasContext *s, arg_VFM_dp *a) /* * Present in VFPv4 only. + * Note that we can't rely on the SIMDFMAC check alone, because + * in a Neon-no-VFP core that ID register field will be non-zero. + */ + if (!dc_isar_feature(aa32_simdfmac, s) || + !dc_isar_feature(aa32_fpdp_v2, s)) { + return false; + } + /* * In v7A, UNPREDICTABLE with non-zero vector length/stride; from * v8A, must UNDEF. We choose to UNDEF for both v7A and v8A. */ - if (!arm_dc_feature(s, ARM_FEATURE_VFP4) || - (s->vec_len != 0 || s->vec_stride != 0)) { + if (s->vec_len != 0 || s->vec_stride != 0) { return false; } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_fp_d32, s) && ((a->vd | a->vn | a->vm) & 0x10)) { + if (!dc_isar_feature(aa32_simd_r32, s) && + ((a->vd | a->vn | a->vm) & 0x10)) { return false; } - if (!dc_isar_feature(aa32_fpdp, s)) { + /* UNDEF accesses to D16-D31 if they don't exist. */ + if (!dc_isar_feature(aa32_simd_r32, s) && + ((a->vd | a->vn | a->vm) & 0x10)) { return false; } @@ -1828,12 +1888,12 @@ static bool trans_VFM_dp(DisasContext *s, arg_VFM_dp *a) neon_load_reg64(vn, a->vn); neon_load_reg64(vm, a->vm); - if (a->o2) { + if (neg_n) { /* VFNMS, VFMS */ gen_helper_vfp_negd(vn, vn); } neon_load_reg64(vd, a->vd); - if (a->o1 & 1) { + if (neg_d) { /* VFNMA, VFNMS */ gen_helper_vfp_negd(vd, vd); } @@ -1849,6 +1909,26 @@ static bool trans_VFM_dp(DisasContext *s, arg_VFM_dp *a) return true; } +static bool trans_VFMA_dp(DisasContext *s, arg_VFMA_dp *a) +{ + return do_vfm_dp(s, a, false, false); +} + +static bool trans_VFMS_dp(DisasContext *s, arg_VFMS_dp *a) +{ + return do_vfm_dp(s, a, true, false); +} + +static bool trans_VFNMA_dp(DisasContext *s, arg_VFNMA_dp *a) +{ + return do_vfm_dp(s, a, false, true); +} + +static bool trans_VFNMS_dp(DisasContext *s, arg_VFNMS_dp *a) +{ + return do_vfm_dp(s, a, true, true); +} + static bool trans_VMOV_imm_sp(DisasContext *s, arg_VMOV_imm_sp *a) { uint32_t delta_d = 0; @@ -1858,12 +1938,12 @@ static bool trans_VMOV_imm_sp(DisasContext *s, arg_VMOV_imm_sp *a) vd = a->vd; - if (!dc_isar_feature(aa32_fpshvec, s) && - (veclen != 0 || s->vec_stride != 0)) { + if (!dc_isar_feature(aa32_fpsp_v3, s)) { return false; } - if (!arm_dc_feature(s, ARM_FEATURE_VFP3)) { + if (!dc_isar_feature(aa32_fpshvec, s) && + (veclen != 0 || s->vec_stride != 0)) { return false; } @@ -1908,12 +1988,12 @@ static bool trans_VMOV_imm_dp(DisasContext *s, arg_VMOV_imm_dp *a) vd = a->vd; - /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_fp_d32, s) && (vd & 0x10)) { + if (!dc_isar_feature(aa32_fpdp_v3, s)) { return false; } - if (!dc_isar_feature(aa32_fpdp, s)) { + /* UNDEF accesses to D16-D31 if they don't exist. */ + if (!dc_isar_feature(aa32_simd_r32, s) && (vd & 0x10)) { return false; } @@ -1922,10 +2002,6 @@ static bool trans_VMOV_imm_dp(DisasContext *s, arg_VMOV_imm_dp *a) return false; } - if (!arm_dc_feature(s, ARM_FEATURE_VFP3)) { - return false; - } - if (!vfp_access_check(s)) { return true; } @@ -2012,6 +2088,10 @@ static bool trans_VCMP_sp(DisasContext *s, arg_VCMP_sp *a) { TCGv_i32 vd, vm; + if (!dc_isar_feature(aa32_fpsp_v2, s)) { + return false; + } + /* Vm/M bits must be zero for the Z variant */ if (a->z && a->vm != 0) { return false; @@ -2047,17 +2127,17 @@ static bool trans_VCMP_dp(DisasContext *s, arg_VCMP_dp *a) { TCGv_i64 vd, vm; + if (!dc_isar_feature(aa32_fpdp_v2, s)) { + return false; + } + /* Vm/M bits must be zero for the Z variant */ if (a->z && a->vm != 0) { return false; } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_fp_d32, s) && ((a->vd | a->vm) & 0x10)) { - return false; - } - - if (!dc_isar_feature(aa32_fpdp, s)) { + if (!dc_isar_feature(aa32_simd_r32, s) && ((a->vd | a->vm) & 0x10)) { return false; } @@ -2121,16 +2201,16 @@ static bool trans_VCVT_f64_f16(DisasContext *s, arg_VCVT_f64_f16 *a) TCGv_i32 tmp; TCGv_i64 vd; + if (!dc_isar_feature(aa32_fpdp_v2, s)) { + return false; + } + if (!dc_isar_feature(aa32_fp16_dpconv, s)) { return false; } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_fp_d32, s) && (a->vd & 0x10)) { - return false; - } - - if (!dc_isar_feature(aa32_fpdp, s)) { + if (!dc_isar_feature(aa32_simd_r32, s) && (a->vd & 0x10)) { return false; } @@ -2187,16 +2267,16 @@ static bool trans_VCVT_f16_f64(DisasContext *s, arg_VCVT_f16_f64 *a) TCGv_i32 tmp; TCGv_i64 vm; + if (!dc_isar_feature(aa32_fpdp_v2, s)) { + return false; + } + if (!dc_isar_feature(aa32_fp16_dpconv, s)) { return false; } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_fp_d32, s) && (a->vm & 0x10)) { - return false; - } - - if (!dc_isar_feature(aa32_fpdp, s)) { + if (!dc_isar_feature(aa32_simd_r32, s) && (a->vm & 0x10)) { return false; } @@ -2247,16 +2327,16 @@ static bool trans_VRINTR_dp(DisasContext *s, arg_VRINTR_dp *a) TCGv_ptr fpst; TCGv_i64 tmp; + if (!dc_isar_feature(aa32_fpdp_v2, s)) { + return false; + } + if (!dc_isar_feature(aa32_vrint, s)) { return false; } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_fp_d32, s) && ((a->vd | a->vm) & 0x10)) { - return false; - } - - if (!dc_isar_feature(aa32_fpdp, s)) { + if (!dc_isar_feature(aa32_simd_r32, s) && ((a->vd | a->vm) & 0x10)) { return false; } @@ -2308,16 +2388,16 @@ static bool trans_VRINTZ_dp(DisasContext *s, arg_VRINTZ_dp *a) TCGv_i64 tmp; TCGv_i32 tcg_rmode; + if (!dc_isar_feature(aa32_fpdp_v2, s)) { + return false; + } + if (!dc_isar_feature(aa32_vrint, s)) { return false; } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_fp_d32, s) && ((a->vd | a->vm) & 0x10)) { - return false; - } - - if (!dc_isar_feature(aa32_fpdp, s)) { + if (!dc_isar_feature(aa32_simd_r32, s) && ((a->vd | a->vm) & 0x10)) { return false; } @@ -2367,16 +2447,16 @@ static bool trans_VRINTX_dp(DisasContext *s, arg_VRINTX_dp *a) TCGv_ptr fpst; TCGv_i64 tmp; + if (!dc_isar_feature(aa32_fpdp_v2, s)) { + return false; + } + if (!dc_isar_feature(aa32_vrint, s)) { return false; } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_fp_d32, s) && ((a->vd | a->vm) & 0x10)) { - return false; - } - - if (!dc_isar_feature(aa32_fpdp, s)) { + if (!dc_isar_feature(aa32_simd_r32, s) && ((a->vd | a->vm) & 0x10)) { return false; } @@ -2399,12 +2479,12 @@ static bool trans_VCVT_sp(DisasContext *s, arg_VCVT_sp *a) TCGv_i64 vd; TCGv_i32 vm; - /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_fp_d32, s) && (a->vd & 0x10)) { + if (!dc_isar_feature(aa32_fpdp_v2, s)) { return false; } - if (!dc_isar_feature(aa32_fpdp, s)) { + /* UNDEF accesses to D16-D31 if they don't exist. */ + if (!dc_isar_feature(aa32_simd_r32, s) && (a->vd & 0x10)) { return false; } @@ -2427,12 +2507,12 @@ static bool trans_VCVT_dp(DisasContext *s, arg_VCVT_dp *a) TCGv_i64 vm; TCGv_i32 vd; - /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_fp_d32, s) && (a->vm & 0x10)) { + if (!dc_isar_feature(aa32_fpdp_v2, s)) { return false; } - if (!dc_isar_feature(aa32_fpdp, s)) { + /* UNDEF accesses to D16-D31 if they don't exist. */ + if (!dc_isar_feature(aa32_simd_r32, s) && (a->vm & 0x10)) { return false; } @@ -2455,6 +2535,10 @@ static bool trans_VCVT_int_sp(DisasContext *s, arg_VCVT_int_sp *a) TCGv_i32 vm; TCGv_ptr fpst; + if (!dc_isar_feature(aa32_fpsp_v2, s)) { + return false; + } + if (!vfp_access_check(s)) { return true; } @@ -2481,12 +2565,12 @@ static bool trans_VCVT_int_dp(DisasContext *s, arg_VCVT_int_dp *a) TCGv_i64 vd; TCGv_ptr fpst; - /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_fp_d32, s) && (a->vd & 0x10)) { + if (!dc_isar_feature(aa32_fpdp_v2, s)) { return false; } - if (!dc_isar_feature(aa32_fpdp, s)) { + /* UNDEF accesses to D16-D31 if they don't exist. */ + if (!dc_isar_feature(aa32_simd_r32, s) && (a->vd & 0x10)) { return false; } @@ -2517,16 +2601,16 @@ static bool trans_VJCVT(DisasContext *s, arg_VJCVT *a) TCGv_i32 vd; TCGv_i64 vm; + if (!dc_isar_feature(aa32_fpdp_v2, s)) { + return false; + } + if (!dc_isar_feature(aa32_jscvt, s)) { return false; } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_fp_d32, s) && (a->vm & 0x10)) { - return false; - } - - if (!dc_isar_feature(aa32_fpdp, s)) { + if (!dc_isar_feature(aa32_simd_r32, s) && (a->vm & 0x10)) { return false; } @@ -2550,7 +2634,7 @@ static bool trans_VCVT_fix_sp(DisasContext *s, arg_VCVT_fix_sp *a) TCGv_ptr fpst; int frac_bits; - if (!arm_dc_feature(s, ARM_FEATURE_VFP3)) { + if (!dc_isar_feature(aa32_fpsp_v3, s)) { return false; } @@ -2610,16 +2694,12 @@ static bool trans_VCVT_fix_dp(DisasContext *s, arg_VCVT_fix_dp *a) TCGv_ptr fpst; int frac_bits; - if (!arm_dc_feature(s, ARM_FEATURE_VFP3)) { + if (!dc_isar_feature(aa32_fpdp_v3, s)) { return false; } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_fp_d32, s) && (a->vd & 0x10)) { - return false; - } - - if (!dc_isar_feature(aa32_fpdp, s)) { + if (!dc_isar_feature(aa32_simd_r32, s) && (a->vd & 0x10)) { return false; } @@ -2677,6 +2757,10 @@ static bool trans_VCVT_sp_int(DisasContext *s, arg_VCVT_sp_int *a) TCGv_i32 vm; TCGv_ptr fpst; + if (!dc_isar_feature(aa32_fpsp_v2, s)) { + return false; + } + if (!vfp_access_check(s)) { return true; } @@ -2710,12 +2794,12 @@ static bool trans_VCVT_dp_int(DisasContext *s, arg_VCVT_dp_int *a) TCGv_i64 vm; TCGv_ptr fpst; - /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_fp_d32, s) && (a->vm & 0x10)) { + if (!dc_isar_feature(aa32_fpdp_v2, s)) { return false; } - if (!dc_isar_feature(aa32_fpdp, s)) { + /* UNDEF accesses to D16-D31 if they don't exist. */ + if (!dc_isar_feature(aa32_simd_r32, s) && (a->vm & 0x10)) { return false; } @@ -2747,3 +2831,42 @@ static bool trans_VCVT_dp_int(DisasContext *s, arg_VCVT_dp_int *a) tcg_temp_free_ptr(fpst); return true; } + +/* + * Decode VLLDM and VLSTM are nonstandard because: + * * if there is no FPU then these insns must NOP in + * Secure state and UNDEF in Nonsecure state + * * if there is an FPU then these insns do not have + * the usual behaviour that vfp_access_check() provides of + * being controlled by CPACR/NSACR enable bits or the + * lazy-stacking logic. + */ +static bool trans_VLLDM_VLSTM(DisasContext *s, arg_VLLDM_VLSTM *a) +{ + TCGv_i32 fptr; + + if (!arm_dc_feature(s, ARM_FEATURE_M) || + !arm_dc_feature(s, ARM_FEATURE_V8)) { + return false; + } + /* If not secure, UNDEF. */ + if (!s->v8m_secure) { + return false; + } + /* If no fpu, NOP. */ + if (!dc_isar_feature(aa32_vfp, s)) { + return true; + } + + fptr = load_reg(s, a->rn); + if (a->l) { + gen_helper_v7m_vlldm(cpu_env, fptr); + } else { + gen_helper_v7m_vlstm(cpu_env, fptr); + } + tcg_temp_free_i32(fptr); + + /* End the TB, because we have updated FP control bits */ + s->base.is_jmp = DISAS_UPDATE; + return true; +} diff --git a/target/arm/translate.c b/target/arm/translate.c index 4d5d4bd888..9f9f4e19e0 100644 --- a/target/arm/translate.c +++ b/target/arm/translate.c @@ -24,8 +24,8 @@ #include "internals.h" #include "disas/disas.h" #include "exec/exec-all.h" -#include "tcg-op.h" -#include "tcg-op-gvec.h" +#include "tcg/tcg-op.h" +#include "tcg/tcg-op-gvec.h" #include "qemu/log.h" #include "qemu/bitops.h" #include "arm_ldst.h" @@ -42,7 +42,7 @@ #define ENABLE_ARCH_5 arm_dc_feature(s, ARM_FEATURE_V5) /* currently all emulated v5 cores are also v5TE, so don't bother */ #define ENABLE_ARCH_5TE arm_dc_feature(s, ARM_FEATURE_V5) -#define ENABLE_ARCH_5J dc_isar_feature(jazelle, s) +#define ENABLE_ARCH_5J dc_isar_feature(aa32_jazelle, s) #define ENABLE_ARCH_6 arm_dc_feature(s, ARM_FEATURE_V6) #define ENABLE_ARCH_6K arm_dc_feature(s, ARM_FEATURE_V6K) #define ENABLE_ARCH_6T2 arm_dc_feature(s, ARM_FEATURE_THUMB2) @@ -152,14 +152,16 @@ 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: + case ARMMMUIdx_E10_1_PAN: + return arm_to_core_mmu_idx(ARMMMUIdx_E10_0); + case ARMMMUIdx_SE3: + case ARMMMUIdx_SE10_0: + case ARMMMUIdx_SE10_1: + case ARMMMUIdx_SE10_1_PAN: + 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 +174,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(); } @@ -1124,7 +1125,7 @@ static inline void gen_hlt(DisasContext *s, int imm) s->current_el != 0 && #endif (imm == (s->thumb ? 0x3c : 0xf000))) { - gen_exception_internal_insn(s, s->base.pc_next, EXCP_SEMIHOST); + gen_exception_internal_insn(s, s->pc_curr, EXCP_SEMIHOST); return; } @@ -2611,7 +2612,7 @@ static int disas_dsp_insn(DisasContext *s, uint32_t insn) #define VFP_SREG(insn, bigbit, smallbit) \ ((VFP_REG_SHR(insn, bigbit - 1) & 0x1e) | (((insn) >> (smallbit)) & 1)) #define VFP_DREG(reg, insn, bigbit, smallbit) do { \ - if (arm_dc_feature(s, ARM_FEATURE_VFP3)) { \ + if (dc_isar_feature(aa32_simd_r32, s)) { \ reg = (((insn) >> (bigbit)) & 0x0f) \ | (((insn) >> ((smallbit) - 4)) & 0x10); \ } else { \ @@ -2645,35 +2646,6 @@ static void gen_neon_dup_high16(TCGv_i32 var) tcg_temp_free_i32(tmp); } -/* - * Disassemble a VFP instruction. Returns nonzero if an error occurred - * (ie. an undefined instruction). - */ -static int disas_vfp_insn(DisasContext *s, uint32_t insn) -{ - if (!arm_dc_feature(s, ARM_FEATURE_VFP)) { - return 1; - } - - /* - * If the decodetree decoder handles this insn it will always - * emit code to either execute the insn or generate an appropriate - * exception; so we don't need to ever return non-zero to tell - * the calling code to emit an UNDEF exception. - */ - if (extract32(insn, 28, 4) == 0xf) { - if (disas_vfp_uncond(s, insn)) { - return 0; - } - } else { - if (disas_vfp(s, insn)) { - return 0; - } - } - /* If the decodetree decoder didn't handle this insn, it must be UNDEF */ - return 1; -} - static inline bool use_goto_tb(DisasContext *s, target_ulong dest) { #ifndef CONFIG_USER_ONLY @@ -2733,39 +2705,33 @@ static inline void gen_mulxy(TCGv_i32 t0, TCGv_i32 t1, int x, int y) /* Return the mask of PSR bits set by a MSR instruction. */ static uint32_t msr_mask(DisasContext *s, int flags, int spsr) { - uint32_t mask; + uint32_t mask = 0; - mask = 0; - if (flags & (1 << 0)) + if (flags & (1 << 0)) { mask |= 0xff; - if (flags & (1 << 1)) + } + if (flags & (1 << 1)) { mask |= 0xff00; - if (flags & (1 << 2)) + } + if (flags & (1 << 2)) { mask |= 0xff0000; - if (flags & (1 << 3)) + } + if (flags & (1 << 3)) { mask |= 0xff000000; + } - /* Mask out undefined bits. */ - mask &= ~CPSR_RESERVED; - if (!arm_dc_feature(s, ARM_FEATURE_V4T)) { - mask &= ~CPSR_T; - } - if (!arm_dc_feature(s, ARM_FEATURE_V5)) { - mask &= ~CPSR_Q; /* V5TE in reality*/ - } - if (!arm_dc_feature(s, ARM_FEATURE_V6)) { - mask &= ~(CPSR_E | CPSR_GE); - } - if (!arm_dc_feature(s, ARM_FEATURE_THUMB2)) { - mask &= ~CPSR_IT; - } - /* Mask out execution state and reserved bits. */ + /* Mask out undefined and reserved bits. */ + mask &= aarch32_cpsr_valid_mask(s->features, s->isar); + + /* Mask out execution state. */ if (!spsr) { - mask &= ~(CPSR_EXEC | CPSR_RESERVED); + mask &= ~CPSR_EXEC; } + /* Mask out privileged bits. */ - if (IS_USER(s)) + if (IS_USER(s)) { mask &= CPSR_USER; + } return mask; } @@ -3580,13 +3546,13 @@ static inline void gen_neon_shift_narrow(int size, TCGv_i32 var, TCGv_i32 shift, if (u) { switch (size) { case 1: gen_helper_neon_shl_u16(var, var, shift); break; - case 2: gen_helper_neon_shl_u32(var, var, shift); break; + case 2: gen_ushl_i32(var, var, shift); break; default: abort(); } } else { switch (size) { case 1: gen_helper_neon_shl_s16(var, var, shift); break; - case 2: gen_helper_neon_shl_s32(var, var, shift); break; + case 2: gen_sshl_i32(var, var, shift); break; default: abort(); } } @@ -4389,6 +4355,280 @@ const GVecGen3 cmtst_op[4] = { .vece = MO_64 }, }; +void gen_ushl_i32(TCGv_i32 dst, TCGv_i32 src, TCGv_i32 shift) +{ + TCGv_i32 lval = tcg_temp_new_i32(); + TCGv_i32 rval = tcg_temp_new_i32(); + TCGv_i32 lsh = tcg_temp_new_i32(); + TCGv_i32 rsh = tcg_temp_new_i32(); + TCGv_i32 zero = tcg_const_i32(0); + TCGv_i32 max = tcg_const_i32(32); + + /* + * Rely on the TCG guarantee that out of range shifts produce + * unspecified results, not undefined behaviour (i.e. no trap). + * Discard out-of-range results after the fact. + */ + tcg_gen_ext8s_i32(lsh, shift); + tcg_gen_neg_i32(rsh, lsh); + tcg_gen_shl_i32(lval, src, lsh); + tcg_gen_shr_i32(rval, src, rsh); + tcg_gen_movcond_i32(TCG_COND_LTU, dst, lsh, max, lval, zero); + tcg_gen_movcond_i32(TCG_COND_LTU, dst, rsh, max, rval, dst); + + tcg_temp_free_i32(lval); + tcg_temp_free_i32(rval); + tcg_temp_free_i32(lsh); + tcg_temp_free_i32(rsh); + tcg_temp_free_i32(zero); + tcg_temp_free_i32(max); +} + +void gen_ushl_i64(TCGv_i64 dst, TCGv_i64 src, TCGv_i64 shift) +{ + TCGv_i64 lval = tcg_temp_new_i64(); + TCGv_i64 rval = tcg_temp_new_i64(); + TCGv_i64 lsh = tcg_temp_new_i64(); + TCGv_i64 rsh = tcg_temp_new_i64(); + TCGv_i64 zero = tcg_const_i64(0); + TCGv_i64 max = tcg_const_i64(64); + + /* + * Rely on the TCG guarantee that out of range shifts produce + * unspecified results, not undefined behaviour (i.e. no trap). + * Discard out-of-range results after the fact. + */ + tcg_gen_ext8s_i64(lsh, shift); + tcg_gen_neg_i64(rsh, lsh); + tcg_gen_shl_i64(lval, src, lsh); + tcg_gen_shr_i64(rval, src, rsh); + tcg_gen_movcond_i64(TCG_COND_LTU, dst, lsh, max, lval, zero); + tcg_gen_movcond_i64(TCG_COND_LTU, dst, rsh, max, rval, dst); + + tcg_temp_free_i64(lval); + tcg_temp_free_i64(rval); + tcg_temp_free_i64(lsh); + tcg_temp_free_i64(rsh); + tcg_temp_free_i64(zero); + tcg_temp_free_i64(max); +} + +static void gen_ushl_vec(unsigned vece, TCGv_vec dst, + TCGv_vec src, TCGv_vec shift) +{ + TCGv_vec lval = tcg_temp_new_vec_matching(dst); + TCGv_vec rval = tcg_temp_new_vec_matching(dst); + TCGv_vec lsh = tcg_temp_new_vec_matching(dst); + TCGv_vec rsh = tcg_temp_new_vec_matching(dst); + TCGv_vec msk, max; + + tcg_gen_neg_vec(vece, rsh, shift); + if (vece == MO_8) { + tcg_gen_mov_vec(lsh, shift); + } else { + msk = tcg_temp_new_vec_matching(dst); + tcg_gen_dupi_vec(vece, msk, 0xff); + tcg_gen_and_vec(vece, lsh, shift, msk); + tcg_gen_and_vec(vece, rsh, rsh, msk); + tcg_temp_free_vec(msk); + } + + /* + * Rely on the TCG guarantee that out of range shifts produce + * unspecified results, not undefined behaviour (i.e. no trap). + * Discard out-of-range results after the fact. + */ + tcg_gen_shlv_vec(vece, lval, src, lsh); + tcg_gen_shrv_vec(vece, rval, src, rsh); + + max = tcg_temp_new_vec_matching(dst); + tcg_gen_dupi_vec(vece, max, 8 << vece); + + /* + * The choice of LT (signed) and GEU (unsigned) are biased toward + * the instructions of the x86_64 host. For MO_8, the whole byte + * is significant so we must use an unsigned compare; otherwise we + * have already masked to a byte and so a signed compare works. + * Other tcg hosts have a full set of comparisons and do not care. + */ + if (vece == MO_8) { + tcg_gen_cmp_vec(TCG_COND_GEU, vece, lsh, lsh, max); + tcg_gen_cmp_vec(TCG_COND_GEU, vece, rsh, rsh, max); + tcg_gen_andc_vec(vece, lval, lval, lsh); + tcg_gen_andc_vec(vece, rval, rval, rsh); + } else { + tcg_gen_cmp_vec(TCG_COND_LT, vece, lsh, lsh, max); + tcg_gen_cmp_vec(TCG_COND_LT, vece, rsh, rsh, max); + tcg_gen_and_vec(vece, lval, lval, lsh); + tcg_gen_and_vec(vece, rval, rval, rsh); + } + tcg_gen_or_vec(vece, dst, lval, rval); + + tcg_temp_free_vec(max); + tcg_temp_free_vec(lval); + tcg_temp_free_vec(rval); + tcg_temp_free_vec(lsh); + tcg_temp_free_vec(rsh); +} + +static const TCGOpcode ushl_list[] = { + INDEX_op_neg_vec, INDEX_op_shlv_vec, + INDEX_op_shrv_vec, INDEX_op_cmp_vec, 0 +}; + +const GVecGen3 ushl_op[4] = { + { .fniv = gen_ushl_vec, + .fno = gen_helper_gvec_ushl_b, + .opt_opc = ushl_list, + .vece = MO_8 }, + { .fniv = gen_ushl_vec, + .fno = gen_helper_gvec_ushl_h, + .opt_opc = ushl_list, + .vece = MO_16 }, + { .fni4 = gen_ushl_i32, + .fniv = gen_ushl_vec, + .opt_opc = ushl_list, + .vece = MO_32 }, + { .fni8 = gen_ushl_i64, + .fniv = gen_ushl_vec, + .opt_opc = ushl_list, + .vece = MO_64 }, +}; + +void gen_sshl_i32(TCGv_i32 dst, TCGv_i32 src, TCGv_i32 shift) +{ + TCGv_i32 lval = tcg_temp_new_i32(); + TCGv_i32 rval = tcg_temp_new_i32(); + TCGv_i32 lsh = tcg_temp_new_i32(); + TCGv_i32 rsh = tcg_temp_new_i32(); + TCGv_i32 zero = tcg_const_i32(0); + TCGv_i32 max = tcg_const_i32(31); + + /* + * Rely on the TCG guarantee that out of range shifts produce + * unspecified results, not undefined behaviour (i.e. no trap). + * Discard out-of-range results after the fact. + */ + tcg_gen_ext8s_i32(lsh, shift); + tcg_gen_neg_i32(rsh, lsh); + tcg_gen_shl_i32(lval, src, lsh); + tcg_gen_umin_i32(rsh, rsh, max); + tcg_gen_sar_i32(rval, src, rsh); + tcg_gen_movcond_i32(TCG_COND_LEU, lval, lsh, max, lval, zero); + tcg_gen_movcond_i32(TCG_COND_LT, dst, lsh, zero, rval, lval); + + tcg_temp_free_i32(lval); + tcg_temp_free_i32(rval); + tcg_temp_free_i32(lsh); + tcg_temp_free_i32(rsh); + tcg_temp_free_i32(zero); + tcg_temp_free_i32(max); +} + +void gen_sshl_i64(TCGv_i64 dst, TCGv_i64 src, TCGv_i64 shift) +{ + TCGv_i64 lval = tcg_temp_new_i64(); + TCGv_i64 rval = tcg_temp_new_i64(); + TCGv_i64 lsh = tcg_temp_new_i64(); + TCGv_i64 rsh = tcg_temp_new_i64(); + TCGv_i64 zero = tcg_const_i64(0); + TCGv_i64 max = tcg_const_i64(63); + + /* + * Rely on the TCG guarantee that out of range shifts produce + * unspecified results, not undefined behaviour (i.e. no trap). + * Discard out-of-range results after the fact. + */ + tcg_gen_ext8s_i64(lsh, shift); + tcg_gen_neg_i64(rsh, lsh); + tcg_gen_shl_i64(lval, src, lsh); + tcg_gen_umin_i64(rsh, rsh, max); + tcg_gen_sar_i64(rval, src, rsh); + tcg_gen_movcond_i64(TCG_COND_LEU, lval, lsh, max, lval, zero); + tcg_gen_movcond_i64(TCG_COND_LT, dst, lsh, zero, rval, lval); + + tcg_temp_free_i64(lval); + tcg_temp_free_i64(rval); + tcg_temp_free_i64(lsh); + tcg_temp_free_i64(rsh); + tcg_temp_free_i64(zero); + tcg_temp_free_i64(max); +} + +static void gen_sshl_vec(unsigned vece, TCGv_vec dst, + TCGv_vec src, TCGv_vec shift) +{ + TCGv_vec lval = tcg_temp_new_vec_matching(dst); + TCGv_vec rval = tcg_temp_new_vec_matching(dst); + TCGv_vec lsh = tcg_temp_new_vec_matching(dst); + TCGv_vec rsh = tcg_temp_new_vec_matching(dst); + TCGv_vec tmp = tcg_temp_new_vec_matching(dst); + + /* + * Rely on the TCG guarantee that out of range shifts produce + * unspecified results, not undefined behaviour (i.e. no trap). + * Discard out-of-range results after the fact. + */ + tcg_gen_neg_vec(vece, rsh, shift); + if (vece == MO_8) { + tcg_gen_mov_vec(lsh, shift); + } else { + tcg_gen_dupi_vec(vece, tmp, 0xff); + tcg_gen_and_vec(vece, lsh, shift, tmp); + tcg_gen_and_vec(vece, rsh, rsh, tmp); + } + + /* Bound rsh so out of bound right shift gets -1. */ + tcg_gen_dupi_vec(vece, tmp, (8 << vece) - 1); + tcg_gen_umin_vec(vece, rsh, rsh, tmp); + tcg_gen_cmp_vec(TCG_COND_GT, vece, tmp, lsh, tmp); + + tcg_gen_shlv_vec(vece, lval, src, lsh); + tcg_gen_sarv_vec(vece, rval, src, rsh); + + /* Select in-bound left shift. */ + tcg_gen_andc_vec(vece, lval, lval, tmp); + + /* Select between left and right shift. */ + if (vece == MO_8) { + tcg_gen_dupi_vec(vece, tmp, 0); + tcg_gen_cmpsel_vec(TCG_COND_LT, vece, dst, lsh, tmp, rval, lval); + } else { + tcg_gen_dupi_vec(vece, tmp, 0x80); + tcg_gen_cmpsel_vec(TCG_COND_LT, vece, dst, lsh, tmp, lval, rval); + } + + tcg_temp_free_vec(lval); + tcg_temp_free_vec(rval); + tcg_temp_free_vec(lsh); + tcg_temp_free_vec(rsh); + tcg_temp_free_vec(tmp); +} + +static const TCGOpcode sshl_list[] = { + INDEX_op_neg_vec, INDEX_op_umin_vec, INDEX_op_shlv_vec, + INDEX_op_sarv_vec, INDEX_op_cmp_vec, INDEX_op_cmpsel_vec, 0 +}; + +const GVecGen3 sshl_op[4] = { + { .fniv = gen_sshl_vec, + .fno = gen_helper_gvec_sshl_b, + .opt_opc = sshl_list, + .vece = MO_8 }, + { .fniv = gen_sshl_vec, + .fno = gen_helper_gvec_sshl_h, + .opt_opc = sshl_list, + .vece = MO_16 }, + { .fni4 = gen_sshl_i32, + .fniv = gen_sshl_vec, + .opt_opc = sshl_list, + .vece = MO_32 }, + { .fni8 = gen_sshl_i64, + .fniv = gen_sshl_vec, + .opt_opc = sshl_list, + .vece = MO_64 }, +}; + static void gen_uqadd_vec(unsigned vece, TCGv_vec t, TCGv_vec sat, TCGv_vec a, TCGv_vec b) { @@ -4738,16 +4978,17 @@ static int disas_neon_data_insn(DisasContext *s, uint32_t insn) case NEON_3R_VMUL: /* VMUL */ if (u) { - /* Polynomial case allows only P8 and is handled below. */ + /* Polynomial case allows only P8. */ if (size != 0) { return 1; } + tcg_gen_gvec_3_ool(rd_ofs, rn_ofs, rm_ofs, vec_size, vec_size, + 0, gen_helper_gvec_pmul_b); } else { tcg_gen_gvec_mul(size, rd_ofs, rn_ofs, rm_ofs, vec_size, vec_size); - return 0; } - break; + return 0; case NEON_3R_VML: /* VMLA, VMLS */ tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, vec_size, vec_size, @@ -4792,6 +5033,12 @@ static int disas_neon_data_insn(DisasContext *s, uint32_t insn) vec_size, vec_size); } return 0; + + case NEON_3R_VSHL: + /* Note the operation is vshl vd,vm,vn */ + tcg_gen_gvec_3(rd_ofs, rm_ofs, rn_ofs, vec_size, vec_size, + u ? &ushl_op[size] : &sshl_op[size]); + return 0; } if (size == 3) { @@ -4800,13 +5047,6 @@ static int disas_neon_data_insn(DisasContext *s, uint32_t insn) neon_load_reg64(cpu_V0, rn + pass); neon_load_reg64(cpu_V1, rm + pass); switch (op) { - case NEON_3R_VSHL: - if (u) { - gen_helper_neon_shl_u64(cpu_V0, cpu_V1, cpu_V0); - } else { - gen_helper_neon_shl_s64(cpu_V0, cpu_V1, cpu_V0); - } - break; case NEON_3R_VQSHL: if (u) { gen_helper_neon_qshl_u64(cpu_V0, cpu_env, @@ -4841,7 +5081,6 @@ static int disas_neon_data_insn(DisasContext *s, uint32_t insn) } pairwise = 0; switch (op) { - case NEON_3R_VSHL: case NEON_3R_VQSHL: case NEON_3R_VRSHL: case NEON_3R_VQRSHL: @@ -4882,7 +5121,7 @@ static int disas_neon_data_insn(DisasContext *s, uint32_t insn) } break; case NEON_3R_VFM_VQRDMLSH: - if (!arm_dc_feature(s, ARM_FEATURE_VFP4)) { + if (!dc_isar_feature(aa32_simdfmac, s)) { return 1; } break; @@ -4921,9 +5160,6 @@ static int disas_neon_data_insn(DisasContext *s, uint32_t insn) case NEON_3R_VHSUB: GEN_NEON_INTEGER_OP(hsub); break; - case NEON_3R_VSHL: - GEN_NEON_INTEGER_OP(shl); - break; case NEON_3R_VQSHL: GEN_NEON_INTEGER_OP_ENV(qshl); break; @@ -4942,10 +5178,6 @@ static int disas_neon_data_insn(DisasContext *s, uint32_t insn) tmp2 = neon_load_reg(rd, pass); gen_neon_add(size, tmp, tmp2); break; - case NEON_3R_VMUL: - /* VMUL.P8; other cases already eliminated. */ - gen_helper_neon_mul_p8(tmp, tmp, tmp2); - break; case NEON_3R_VPMAX: GEN_NEON_INTEGER_OP(pmax); break; @@ -5332,9 +5564,9 @@ static int disas_neon_data_insn(DisasContext *s, uint32_t insn) } } else { if (input_unsigned) { - gen_helper_neon_shl_u64(cpu_V0, in, tmp64); + gen_ushl_i64(cpu_V0, in, tmp64); } else { - gen_helper_neon_shl_s64(cpu_V0, in, tmp64); + gen_sshl_i64(cpu_V0, in, tmp64); } } tmp = tcg_temp_new_i32(); @@ -5605,27 +5837,20 @@ static int disas_neon_data_insn(DisasContext *s, uint32_t insn) return 1; } - /* Handle VMULL.P64 (Polynomial 64x64 to 128 bit multiply) - * outside the loop below as it only performs a single pass. - */ - if (op == 14 && size == 2) { - TCGv_i64 tcg_rn, tcg_rm, tcg_rd; - - if (!dc_isar_feature(aa32_pmull, s)) { - return 1; + /* Handle polynomial VMULL in a single pass. */ + if (op == 14) { + if (size == 0) { + /* VMULL.P8 */ + tcg_gen_gvec_3_ool(rd_ofs, rn_ofs, rm_ofs, 16, 16, + 0, gen_helper_neon_pmull_h); + } else { + /* VMULL.P64 */ + if (!dc_isar_feature(aa32_pmull, s)) { + return 1; + } + tcg_gen_gvec_3_ool(rd_ofs, rn_ofs, rm_ofs, 16, 16, + 0, gen_helper_gvec_pmull_q); } - tcg_rn = tcg_temp_new_i64(); - tcg_rm = tcg_temp_new_i64(); - tcg_rd = tcg_temp_new_i64(); - neon_load_reg64(tcg_rn, rn); - neon_load_reg64(tcg_rm, rm); - gen_helper_neon_pmull_64_lo(tcg_rd, tcg_rn, tcg_rm); - neon_store_reg64(tcg_rd, rd); - gen_helper_neon_pmull_64_hi(tcg_rd, tcg_rn, tcg_rm); - neon_store_reg64(tcg_rd, rd + 1); - tcg_temp_free_i64(tcg_rn); - tcg_temp_free_i64(tcg_rm); - tcg_temp_free_i64(tcg_rd); return 0; } @@ -5703,11 +5928,6 @@ static int disas_neon_data_insn(DisasContext *s, uint32_t insn) /* VMLAL, VQDMLAL, VMLSL, VQDMLSL, VMULL, VQDMULL */ gen_neon_mull(cpu_V0, tmp, tmp2, size, u); break; - case 14: /* Polynomial VMULL */ - gen_helper_neon_mull_p8(cpu_V0, tmp, tmp2); - tcg_temp_free_i32(tmp2); - tcg_temp_free_i32(tmp); - break; default: /* 15 is RESERVED: caught earlier */ abort(); } @@ -6897,7 +7117,7 @@ static int disas_coproc_insn(DisasContext *s, uint32_t insn) return 1; } - if (ri->accessfn || + if (s->hstr_active || ri->accessfn || (arm_dc_feature(s, ARM_FEATURE_XSCALE) && cpnum < 14)) { /* Emit code to perform further access permissions checks at * runtime; this may result in an exception. @@ -7076,14 +7296,18 @@ static int disas_coproc_insn(DisasContext *s, uint32_t insn) if (!isread && !(ri->type & ARM_CP_SUPPRESS_TB_END)) { /* - * A write to any coprocessor regiser that ends a TB + * A write to any coprocessor register that ends a TB * must rebuild the hflags for the next TB. */ TCGv_i32 tcg_el = tcg_const_i32(s->current_el); if (arm_dc_feature(s, ARM_FEATURE_M)) { gen_helper_rebuild_hflags_m32(cpu_env, tcg_el); } else { - gen_helper_rebuild_hflags_a32(cpu_env, tcg_el); + if (ri->type & ARM_CP_NEWEL) { + gen_helper_rebuild_hflags_a32_newel(cpu_env); + } else { + gen_helper_rebuild_hflags_a32(cpu_env, tcg_el); + } } tcg_temp_free_i32(tcg_el); /* @@ -8327,7 +8551,7 @@ static bool trans_MRS_v7m(DisasContext *s, arg_MRS_v7m *a) static bool trans_MSR_v7m(DisasContext *s, arg_MSR_v7m *a) { - TCGv_i32 addr, reg, el; + TCGv_i32 addr, reg; if (!arm_dc_feature(s, ARM_FEATURE_M)) { return false; @@ -8337,9 +8561,8 @@ static bool trans_MSR_v7m(DisasContext *s, arg_MSR_v7m *a) gen_helper_v7m_msr(cpu_env, addr, reg); tcg_temp_free_i32(addr); tcg_temp_free_i32(reg); - el = tcg_const_i32(s->current_el); - gen_helper_rebuild_hflags_m32(cpu_env, el); - tcg_temp_free_i32(el); + /* If we wrote to CONTROL, the EL might have changed */ + gen_helper_rebuild_hflags_m32_newel(cpu_env); gen_lookup_tb(s); return true; } @@ -8453,7 +8676,7 @@ static bool trans_BKPT(DisasContext *s, arg_BKPT *a) !IS_USER(s) && #endif (a->imm == 0xab)) { - gen_exception_internal_insn(s, s->base.pc_next, EXCP_SEMIHOST); + gen_exception_internal_insn(s, s->pc_curr, EXCP_SEMIHOST); } else { gen_exception_bkpt_insn(s, syn_aa32_bkpt(a->imm, false)); } @@ -8552,6 +8775,9 @@ static ISSInfo make_issinfo(DisasContext *s, int rd, bool p, bool w) /* ISS not valid if writeback */ if (p && !w) { ret = rd; + if (s->base.pc_next - s->pc_curr == 2) { + ret |= ISSIs16Bit; + } } else { ret = ISSInvalid; } @@ -9843,8 +10069,8 @@ static bool op_div(DisasContext *s, arg_rrr *a, bool u) TCGv_i32 t1, t2; if (s->thumb - ? !dc_isar_feature(thumb_div, s) - : !dc_isar_feature(arm_div, s)) { + ? !dc_isar_feature(aa32_thumb_div, s) + : !dc_isar_feature(aa32_arm_div, s)) { return false; } @@ -10262,7 +10488,7 @@ static bool trans_SVC(DisasContext *s, arg_SVC *a) !IS_USER(s) && #endif (a->imm == semihost_imm)) { - gen_exception_internal_insn(s, s->base.pc_next, EXCP_SEMIHOST); + gen_exception_internal_insn(s, s->pc_curr, EXCP_SEMIHOST); } else { gen_set_pc_im(s, s->base.pc_next); s->svc_imm = a->imm; @@ -10363,7 +10589,7 @@ static bool trans_CPS(DisasContext *s, arg_CPS *a) static bool trans_CPS_v7m(DisasContext *s, arg_CPS_v7m *a) { - TCGv_i32 tmp, addr; + TCGv_i32 tmp, addr, el; if (!arm_dc_feature(s, ARM_FEATURE_M)) { return false; @@ -10386,6 +10612,9 @@ static bool trans_CPS_v7m(DisasContext *s, arg_CPS_v7m *a) gen_helper_v7m_msr(cpu_env, addr, tmp); tcg_temp_free_i32(addr); } + el = tcg_const_i32(s->current_el); + gen_helper_rebuild_hflags_m32(cpu_env, el); + tcg_temp_free_i32(el); tcg_temp_free_i32(tmp); gen_lookup_tb(s); return true; @@ -10526,7 +10755,9 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) ARCH(5); /* Unconditional instructions. */ - if (disas_a32_uncond(s, insn)) { + /* TODO: Perhaps merge these into one decodetree output file. */ + if (disas_a32_uncond(s, insn) || + disas_vfp_uncond(s, insn)) { return; } /* fall back to legacy decoder */ @@ -10553,13 +10784,6 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) } return; } - if ((insn & 0x0f000e10) == 0x0e000a00) { - /* VFP. */ - if (disas_vfp_insn(s, insn)) { - goto illegal_op; - } - return; - } if ((insn & 0x0e000f00) == 0x0c000100) { if (arm_dc_feature(s, ARM_FEATURE_IWMMXT)) { /* iWMMXt register transfer. */ @@ -10590,7 +10814,9 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) arm_skip_unless(s, cond); } - if (disas_a32(s, insn)) { + /* TODO: Perhaps merge these into one decodetree output file. */ + if (disas_a32(s, insn) || + disas_vfp(s, insn)) { return; } /* fall back to legacy decoder */ @@ -10600,11 +10826,10 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) case 0xd: case 0xe: if (((insn >> 8) & 0xe) == 10) { - /* VFP. */ - if (disas_vfp_insn(s, insn)) { - goto illegal_op; - } - } else if (disas_coproc_insn(s, insn)) { + /* VFP, but failed disas_vfp. */ + goto illegal_op; + } + if (disas_coproc_insn(s, insn)) { /* Coprocessor. */ goto illegal_op; } @@ -10693,7 +10918,14 @@ static void disas_thumb2_insn(DisasContext *s, uint32_t insn) ARCH(6T2); } - if (disas_t32(s, insn)) { + /* + * TODO: Perhaps merge these into one decodetree output file. + * Note disas_vfp is written for a32 with cond field in the + * top nibble. The t32 encoding requires 0xe in the top nibble. + */ + if (disas_t32(s, insn) || + disas_vfp_uncond(s, insn) || + ((insn >> 28) == 0xe && disas_vfp(s, insn))) { return; } /* fall back to legacy decoder */ @@ -10710,53 +10942,16 @@ static void disas_thumb2_insn(DisasContext *s, uint32_t insn) goto illegal_op; /* op0 = 0b11 : unallocated */ } - /* - * Decode VLLDM and VLSTM first: these are nonstandard because: - * * if there is no FPU then these insns must NOP in - * Secure state and UNDEF in Nonsecure state - * * if there is an FPU then these insns do not have - * the usual behaviour that disas_vfp_insn() provides of - * being controlled by CPACR/NSACR enable bits or the - * lazy-stacking logic. - */ - if (arm_dc_feature(s, ARM_FEATURE_V8) && - (insn & 0xffa00f00) == 0xec200a00) { - /* 0b1110_1100_0x1x_xxxx_xxxx_1010_xxxx_xxxx - * - VLLDM, VLSTM - * We choose to UNDEF if the RAZ bits are non-zero. - */ - if (!s->v8m_secure || (insn & 0x0040f0ff)) { - goto illegal_op; - } - - if (arm_dc_feature(s, ARM_FEATURE_VFP)) { - uint32_t rn = (insn >> 16) & 0xf; - TCGv_i32 fptr = load_reg(s, rn); - - if (extract32(insn, 20, 1)) { - gen_helper_v7m_vlldm(cpu_env, fptr); - } else { - gen_helper_v7m_vlstm(cpu_env, fptr); - } - tcg_temp_free_i32(fptr); - - /* End the TB, because we have updated FP control bits */ - s->base.is_jmp = DISAS_UPDATE; - } - break; - } - if (arm_dc_feature(s, ARM_FEATURE_VFP) && - ((insn >> 8) & 0xe) == 10) { + if (((insn >> 8) & 0xe) == 10 && + dc_isar_feature(aa32_fpsp_v2, s)) { /* FP, and the CPU supports it */ - if (disas_vfp_insn(s, insn)) { - goto illegal_op; - } - break; + goto illegal_op; + } else { + /* All other insns: NOCP */ + gen_exception_insn(s, s->pc_curr, EXCP_NOCP, + syn_uncategorized(), + default_exception_el(s)); } - - /* All other insns: NOCP */ - gen_exception_insn(s, s->pc_curr, EXCP_NOCP, syn_uncategorized(), - default_exception_el(s)); break; } if ((insn & 0xfe000a00) == 0xfc000800 @@ -10778,9 +10973,8 @@ static void disas_thumb2_insn(DisasContext *s, uint32_t insn) goto illegal_op; } } else if (((insn >> 8) & 0xe) == 10) { - if (disas_vfp_insn(s, insn)) { - goto illegal_op; - } + /* VFP, but failed disas_vfp. */ + goto illegal_op; } else { if (insn & (1 << 28)) goto illegal_op; @@ -10841,37 +11035,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->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->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->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->cp_regs = cpu->cp_regs; dc->features = env->features; @@ -10893,9 +11098,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; @@ -11332,10 +11534,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 dd24f91f26..d9ea0c99cc 100644 --- a/target/arm/translate.h +++ b/target/arm/translate.h @@ -73,10 +73,14 @@ 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. */ bool bt; + /* True if any CP15 access is trapped by HSTR_EL2 */ + bool hstr_active; /* * >= 0, a copy of PSTATE.BTYPE, which will be 0 without v8.5-BTI. * < 0, set by the current instruction. @@ -124,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); } @@ -274,6 +278,8 @@ uint64_t vfp_expand_imm(int size, uint8_t imm8); extern const GVecGen3 mla_op[4]; extern const GVecGen3 mls_op[4]; extern const GVecGen3 cmtst_op[4]; +extern const GVecGen3 sshl_op[4]; +extern const GVecGen3 ushl_op[4]; extern const GVecGen2i ssra_op[4]; extern const GVecGen2i usra_op[4]; extern const GVecGen2i sri_op[4]; @@ -283,6 +289,10 @@ extern const GVecGen4 sqadd_op[4]; extern const GVecGen4 uqsub_op[4]; extern const GVecGen4 sqsub_op[4]; void gen_cmtst_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b); +void gen_ushl_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b); +void gen_sshl_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b); +void gen_ushl_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b); +void gen_sshl_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b); /* * Forward to the isar_feature_* tests given a DisasContext pointer. diff --git a/target/arm/vec_helper.c b/target/arm/vec_helper.c index dedef62403..8017bd88c4 100644 --- a/target/arm/vec_helper.c +++ b/target/arm/vec_helper.c @@ -1046,3 +1046,214 @@ void HELPER(gvec_fmlal_idx_a64)(void *vd, void *vn, void *vm, do_fmlal_idx(vd, vn, vm, &env->vfp.fp_status, desc, get_flush_inputs_to_zero(&env->vfp.fp_status_f16)); } + +void HELPER(gvec_sshl_b)(void *vd, void *vn, void *vm, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc); + int8_t *d = vd, *n = vn, *m = vm; + + for (i = 0; i < opr_sz; ++i) { + int8_t mm = m[i]; + int8_t nn = n[i]; + int8_t res = 0; + if (mm >= 0) { + if (mm < 8) { + res = nn << mm; + } + } else { + res = nn >> (mm > -8 ? -mm : 7); + } + d[i] = res; + } + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + +void HELPER(gvec_sshl_h)(void *vd, void *vn, void *vm, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc); + int16_t *d = vd, *n = vn, *m = vm; + + for (i = 0; i < opr_sz / 2; ++i) { + int8_t mm = m[i]; /* only 8 bits of shift are significant */ + int16_t nn = n[i]; + int16_t res = 0; + if (mm >= 0) { + if (mm < 16) { + res = nn << mm; + } + } else { + res = nn >> (mm > -16 ? -mm : 15); + } + d[i] = res; + } + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + +void HELPER(gvec_ushl_b)(void *vd, void *vn, void *vm, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc); + uint8_t *d = vd, *n = vn, *m = vm; + + for (i = 0; i < opr_sz; ++i) { + int8_t mm = m[i]; + uint8_t nn = n[i]; + uint8_t res = 0; + if (mm >= 0) { + if (mm < 8) { + res = nn << mm; + } + } else { + if (mm > -8) { + res = nn >> -mm; + } + } + d[i] = res; + } + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + +void HELPER(gvec_ushl_h)(void *vd, void *vn, void *vm, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc); + uint16_t *d = vd, *n = vn, *m = vm; + + for (i = 0; i < opr_sz / 2; ++i) { + int8_t mm = m[i]; /* only 8 bits of shift are significant */ + uint16_t nn = n[i]; + uint16_t res = 0; + if (mm >= 0) { + if (mm < 16) { + res = nn << mm; + } + } else { + if (mm > -16) { + res = nn >> -mm; + } + } + d[i] = res; + } + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + +/* + * 8x8->8 polynomial multiply. + * + * Polynomial multiplication is like integer multiplication except the + * partial products are XORed, not added. + * + * TODO: expose this as a generic vector operation, as it is a common + * crypto building block. + */ +void HELPER(gvec_pmul_b)(void *vd, void *vn, void *vm, uint32_t desc) +{ + intptr_t i, j, opr_sz = simd_oprsz(desc); + uint64_t *d = vd, *n = vn, *m = vm; + + for (i = 0; i < opr_sz / 8; ++i) { + uint64_t nn = n[i]; + uint64_t mm = m[i]; + uint64_t rr = 0; + + for (j = 0; j < 8; ++j) { + uint64_t mask = (nn & 0x0101010101010101ull) * 0xff; + rr ^= mm & mask; + mm = (mm << 1) & 0xfefefefefefefefeull; + nn >>= 1; + } + d[i] = rr; + } + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + +/* + * 64x64->128 polynomial multiply. + * Because of the lanes are not accessed in strict columns, + * this probably cannot be turned into a generic helper. + */ +void HELPER(gvec_pmull_q)(void *vd, void *vn, void *vm, uint32_t desc) +{ + intptr_t i, j, opr_sz = simd_oprsz(desc); + intptr_t hi = simd_data(desc); + uint64_t *d = vd, *n = vn, *m = vm; + + for (i = 0; i < opr_sz / 8; i += 2) { + uint64_t nn = n[i + hi]; + uint64_t mm = m[i + hi]; + uint64_t rhi = 0; + uint64_t rlo = 0; + + /* Bit 0 can only influence the low 64-bit result. */ + if (nn & 1) { + rlo = mm; + } + + for (j = 1; j < 64; ++j) { + uint64_t mask = -((nn >> j) & 1); + rlo ^= (mm << j) & mask; + rhi ^= (mm >> (64 - j)) & mask; + } + d[i] = rlo; + d[i + 1] = rhi; + } + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + +/* + * 8x8->16 polynomial multiply. + * + * The byte inputs are expanded to (or extracted from) half-words. + * Note that neon and sve2 get the inputs from different positions. + * This allows 4 bytes to be processed in parallel with uint64_t. + */ + +static uint64_t expand_byte_to_half(uint64_t x) +{ + return (x & 0x000000ff) + | ((x & 0x0000ff00) << 8) + | ((x & 0x00ff0000) << 16) + | ((x & 0xff000000) << 24); +} + +static uint64_t pmull_h(uint64_t op1, uint64_t op2) +{ + uint64_t result = 0; + int i; + + for (i = 0; i < 8; ++i) { + uint64_t mask = (op1 & 0x0001000100010001ull) * 0xffff; + result ^= op2 & mask; + op1 >>= 1; + op2 <<= 1; + } + return result; +} + +void HELPER(neon_pmull_h)(void *vd, void *vn, void *vm, uint32_t desc) +{ + int hi = simd_data(desc); + uint64_t *d = vd, *n = vn, *m = vm; + uint64_t nn = n[hi], mm = m[hi]; + + d[0] = pmull_h(expand_byte_to_half(nn), expand_byte_to_half(mm)); + nn >>= 32; + mm >>= 32; + d[1] = pmull_h(expand_byte_to_half(nn), expand_byte_to_half(mm)); + + clear_tail(d, 16, simd_maxsz(desc)); +} + +#ifdef TARGET_AARCH64 +void HELPER(sve2_pmull_h)(void *vd, void *vn, void *vm, uint32_t desc) +{ + int shift = simd_data(desc) * 8; + intptr_t i, opr_sz = simd_oprsz(desc); + uint64_t *d = vd, *n = vn, *m = vm; + + for (i = 0; i < opr_sz / 8; ++i) { + uint64_t nn = (n[i] >> shift) & 0x00ff00ff00ff00ffull; + uint64_t mm = (m[i] >> shift) & 0x00ff00ff00ff00ffull; + + d[i] = pmull_h(nn, mm); + } +} +#endif diff --git a/target/arm/vfp-uncond.decode b/target/arm/vfp-uncond.decode index 5af1f2ee66..34ca164266 100644 --- a/target/arm/vfp-uncond.decode +++ b/target/arm/vfp-uncond.decode @@ -41,15 +41,19 @@ %vd_dp 22:1 12:4 %vd_sp 12:4 22:1 +@vfp_dnm_s ................................ vm=%vm_sp vn=%vn_sp vd=%vd_sp +@vfp_dnm_d ................................ vm=%vm_dp vn=%vn_dp vd=%vd_dp + VSEL 1111 1110 0. cc:2 .... .... 1010 .0.0 .... \ vm=%vm_sp vn=%vn_sp vd=%vd_sp dp=0 VSEL 1111 1110 0. cc:2 .... .... 1011 .0.0 .... \ vm=%vm_dp vn=%vn_dp vd=%vd_dp dp=1 -VMINMAXNM 1111 1110 1.00 .... .... 1010 . op:1 .0 .... \ - vm=%vm_sp vn=%vn_sp vd=%vd_sp dp=0 -VMINMAXNM 1111 1110 1.00 .... .... 1011 . op:1 .0 .... \ - vm=%vm_dp vn=%vn_dp vd=%vd_dp dp=1 +VMAXNM_sp 1111 1110 1.00 .... .... 1010 .0.0 .... @vfp_dnm_s +VMINNM_sp 1111 1110 1.00 .... .... 1010 .1.0 .... @vfp_dnm_s + +VMAXNM_dp 1111 1110 1.00 .... .... 1011 .0.0 .... @vfp_dnm_d +VMINNM_dp 1111 1110 1.00 .... .... 1011 .1.0 .... @vfp_dnm_d VRINT 1111 1110 1.11 10 rm:2 .... 1010 01.0 .... \ vm=%vm_sp vd=%vd_sp dp=0 diff --git a/target/arm/vfp.decode b/target/arm/vfp.decode index a67b3f29ee..5fd70f975a 100644 --- a/target/arm/vfp.decode +++ b/target/arm/vfp.decode @@ -46,6 +46,14 @@ %vmov_imm 16:4 0:4 +@vfp_dnm_s ................................ vm=%vm_sp vn=%vn_sp vd=%vd_sp +@vfp_dnm_d ................................ vm=%vm_dp vn=%vn_dp vd=%vd_dp + +@vfp_dm_ss ................................ vm=%vm_sp vd=%vd_sp +@vfp_dm_dd ................................ vm=%vm_dp vd=%vd_dp +@vfp_dm_ds ................................ vm=%vm_sp vd=%vd_dp +@vfp_dm_sd ................................ vm=%vm_dp vd=%vd_sp + # VMOV scalar to general-purpose register; note that this does # include some Neon cases. VMOV_to_gp ---- 1110 u:1 1. 1 .... rt:4 1011 ... 1 0000 \ @@ -66,20 +74,15 @@ VDUP ---- 1110 1 b:1 q:1 0 .... rt:4 1011 . 0 e:1 1 0000 \ vn=%vn_dp VMSR_VMRS ---- 1110 111 l:1 reg:4 rt:4 1010 0001 0000 -VMOV_single ---- 1110 000 l:1 .... rt:4 1010 . 001 0000 \ - vn=%vn_sp +VMOV_single ---- 1110 000 l:1 .... rt:4 1010 . 001 0000 vn=%vn_sp -VMOV_64_sp ---- 1100 010 op:1 rt2:4 rt:4 1010 00.1 .... \ - vm=%vm_sp -VMOV_64_dp ---- 1100 010 op:1 rt2:4 rt:4 1011 00.1 .... \ - vm=%vm_dp +VMOV_64_sp ---- 1100 010 op:1 rt2:4 rt:4 1010 00.1 .... vm=%vm_sp +VMOV_64_dp ---- 1100 010 op:1 rt2:4 rt:4 1011 00.1 .... vm=%vm_dp # Note that the half-precision variants of VLDR and VSTR are # not part of this decodetree at all because they have bits [9:8] == 0b01 -VLDR_VSTR_sp ---- 1101 u:1 .0 l:1 rn:4 .... 1010 imm:8 \ - vd=%vd_sp -VLDR_VSTR_dp ---- 1101 u:1 .0 l:1 rn:4 .... 1011 imm:8 \ - vd=%vd_dp +VLDR_VSTR_sp ---- 1101 u:1 .0 l:1 rn:4 .... 1010 imm:8 vd=%vd_sp +VLDR_VSTR_dp ---- 1101 u:1 .0 l:1 rn:4 .... 1011 imm:8 vd=%vd_dp # We split the load/store multiple up into two patterns to avoid # overlap with other insns in the "Advanced SIMD load/store and 64-bit move" @@ -100,84 +103,59 @@ VLDM_VSTM_dp ---- 1101 0.1 l:1 rn:4 .... 1011 imm:8 \ vd=%vd_dp p=1 u=0 w=1 # 3-register VFP data-processing; bits [23,21:20,6] identify the operation. -VMLA_sp ---- 1110 0.00 .... .... 1010 .0.0 .... \ - vm=%vm_sp vn=%vn_sp vd=%vd_sp -VMLA_dp ---- 1110 0.00 .... .... 1011 .0.0 .... \ - vm=%vm_dp vn=%vn_dp vd=%vd_dp +VMLA_sp ---- 1110 0.00 .... .... 1010 .0.0 .... @vfp_dnm_s +VMLA_dp ---- 1110 0.00 .... .... 1011 .0.0 .... @vfp_dnm_d -VMLS_sp ---- 1110 0.00 .... .... 1010 .1.0 .... \ - vm=%vm_sp vn=%vn_sp vd=%vd_sp -VMLS_dp ---- 1110 0.00 .... .... 1011 .1.0 .... \ - vm=%vm_dp vn=%vn_dp vd=%vd_dp +VMLS_sp ---- 1110 0.00 .... .... 1010 .1.0 .... @vfp_dnm_s +VMLS_dp ---- 1110 0.00 .... .... 1011 .1.0 .... @vfp_dnm_d -VNMLS_sp ---- 1110 0.01 .... .... 1010 .0.0 .... \ - vm=%vm_sp vn=%vn_sp vd=%vd_sp -VNMLS_dp ---- 1110 0.01 .... .... 1011 .0.0 .... \ - vm=%vm_dp vn=%vn_dp vd=%vd_dp +VNMLS_sp ---- 1110 0.01 .... .... 1010 .0.0 .... @vfp_dnm_s +VNMLS_dp ---- 1110 0.01 .... .... 1011 .0.0 .... @vfp_dnm_d -VNMLA_sp ---- 1110 0.01 .... .... 1010 .1.0 .... \ - vm=%vm_sp vn=%vn_sp vd=%vd_sp -VNMLA_dp ---- 1110 0.01 .... .... 1011 .1.0 .... \ - vm=%vm_dp vn=%vn_dp vd=%vd_dp +VNMLA_sp ---- 1110 0.01 .... .... 1010 .1.0 .... @vfp_dnm_s +VNMLA_dp ---- 1110 0.01 .... .... 1011 .1.0 .... @vfp_dnm_d -VMUL_sp ---- 1110 0.10 .... .... 1010 .0.0 .... \ - vm=%vm_sp vn=%vn_sp vd=%vd_sp -VMUL_dp ---- 1110 0.10 .... .... 1011 .0.0 .... \ - vm=%vm_dp vn=%vn_dp vd=%vd_dp +VMUL_sp ---- 1110 0.10 .... .... 1010 .0.0 .... @vfp_dnm_s +VMUL_dp ---- 1110 0.10 .... .... 1011 .0.0 .... @vfp_dnm_d -VNMUL_sp ---- 1110 0.10 .... .... 1010 .1.0 .... \ - vm=%vm_sp vn=%vn_sp vd=%vd_sp -VNMUL_dp ---- 1110 0.10 .... .... 1011 .1.0 .... \ - vm=%vm_dp vn=%vn_dp vd=%vd_dp +VNMUL_sp ---- 1110 0.10 .... .... 1010 .1.0 .... @vfp_dnm_s +VNMUL_dp ---- 1110 0.10 .... .... 1011 .1.0 .... @vfp_dnm_d -VADD_sp ---- 1110 0.11 .... .... 1010 .0.0 .... \ - vm=%vm_sp vn=%vn_sp vd=%vd_sp -VADD_dp ---- 1110 0.11 .... .... 1011 .0.0 .... \ - vm=%vm_dp vn=%vn_dp vd=%vd_dp +VADD_sp ---- 1110 0.11 .... .... 1010 .0.0 .... @vfp_dnm_s +VADD_dp ---- 1110 0.11 .... .... 1011 .0.0 .... @vfp_dnm_d -VSUB_sp ---- 1110 0.11 .... .... 1010 .1.0 .... \ - vm=%vm_sp vn=%vn_sp vd=%vd_sp -VSUB_dp ---- 1110 0.11 .... .... 1011 .1.0 .... \ - vm=%vm_dp vn=%vn_dp vd=%vd_dp +VSUB_sp ---- 1110 0.11 .... .... 1010 .1.0 .... @vfp_dnm_s +VSUB_dp ---- 1110 0.11 .... .... 1011 .1.0 .... @vfp_dnm_d -VDIV_sp ---- 1110 1.00 .... .... 1010 .0.0 .... \ - vm=%vm_sp vn=%vn_sp vd=%vd_sp -VDIV_dp ---- 1110 1.00 .... .... 1011 .0.0 .... \ - vm=%vm_dp vn=%vn_dp vd=%vd_dp +VDIV_sp ---- 1110 1.00 .... .... 1010 .0.0 .... @vfp_dnm_s +VDIV_dp ---- 1110 1.00 .... .... 1011 .0.0 .... @vfp_dnm_d -VFM_sp ---- 1110 1.01 .... .... 1010 . o2:1 . 0 .... \ - vm=%vm_sp vn=%vn_sp vd=%vd_sp o1=1 -VFM_dp ---- 1110 1.01 .... .... 1011 . o2:1 . 0 .... \ - vm=%vm_dp vn=%vn_dp vd=%vd_dp o1=1 -VFM_sp ---- 1110 1.10 .... .... 1010 . o2:1 . 0 .... \ - vm=%vm_sp vn=%vn_sp vd=%vd_sp o1=2 -VFM_dp ---- 1110 1.10 .... .... 1011 . o2:1 . 0 .... \ - vm=%vm_dp vn=%vn_dp vd=%vd_dp o1=2 +VFMA_sp ---- 1110 1.10 .... .... 1010 .0. 0 .... @vfp_dnm_s +VFMS_sp ---- 1110 1.10 .... .... 1010 .1. 0 .... @vfp_dnm_s +VFNMA_sp ---- 1110 1.01 .... .... 1010 .0. 0 .... @vfp_dnm_s +VFNMS_sp ---- 1110 1.01 .... .... 1010 .1. 0 .... @vfp_dnm_s + +VFMA_dp ---- 1110 1.10 .... .... 1011 .0.0 .... @vfp_dnm_d +VFMS_dp ---- 1110 1.10 .... .... 1011 .1.0 .... @vfp_dnm_d +VFNMA_dp ---- 1110 1.01 .... .... 1011 .0.0 .... @vfp_dnm_d +VFNMS_dp ---- 1110 1.01 .... .... 1011 .1.0 .... @vfp_dnm_d VMOV_imm_sp ---- 1110 1.11 .... .... 1010 0000 .... \ vd=%vd_sp imm=%vmov_imm VMOV_imm_dp ---- 1110 1.11 .... .... 1011 0000 .... \ vd=%vd_dp imm=%vmov_imm -VMOV_reg_sp ---- 1110 1.11 0000 .... 1010 01.0 .... \ - vd=%vd_sp vm=%vm_sp -VMOV_reg_dp ---- 1110 1.11 0000 .... 1011 01.0 .... \ - vd=%vd_dp vm=%vm_dp +VMOV_reg_sp ---- 1110 1.11 0000 .... 1010 01.0 .... @vfp_dm_ss +VMOV_reg_dp ---- 1110 1.11 0000 .... 1011 01.0 .... @vfp_dm_dd -VABS_sp ---- 1110 1.11 0000 .... 1010 11.0 .... \ - vd=%vd_sp vm=%vm_sp -VABS_dp ---- 1110 1.11 0000 .... 1011 11.0 .... \ - vd=%vd_dp vm=%vm_dp +VABS_sp ---- 1110 1.11 0000 .... 1010 11.0 .... @vfp_dm_ss +VABS_dp ---- 1110 1.11 0000 .... 1011 11.0 .... @vfp_dm_dd -VNEG_sp ---- 1110 1.11 0001 .... 1010 01.0 .... \ - vd=%vd_sp vm=%vm_sp -VNEG_dp ---- 1110 1.11 0001 .... 1011 01.0 .... \ - vd=%vd_dp vm=%vm_dp +VNEG_sp ---- 1110 1.11 0001 .... 1010 01.0 .... @vfp_dm_ss +VNEG_dp ---- 1110 1.11 0001 .... 1011 01.0 .... @vfp_dm_dd -VSQRT_sp ---- 1110 1.11 0001 .... 1010 11.0 .... \ - vd=%vd_sp vm=%vm_sp -VSQRT_dp ---- 1110 1.11 0001 .... 1011 11.0 .... \ - vd=%vd_dp vm=%vm_dp +VSQRT_sp ---- 1110 1.11 0001 .... 1010 11.0 .... @vfp_dm_ss +VSQRT_dp ---- 1110 1.11 0001 .... 1011 11.0 .... @vfp_dm_dd VCMP_sp ---- 1110 1.11 010 z:1 .... 1010 e:1 1.0 .... \ vd=%vd_sp vm=%vm_sp @@ -190,32 +168,26 @@ VCVT_f32_f16 ---- 1110 1.11 0010 .... 1010 t:1 1.0 .... \ VCVT_f64_f16 ---- 1110 1.11 0010 .... 1011 t:1 1.0 .... \ vd=%vd_dp vm=%vm_sp -# VCVTB and VCVTT to f16: Vd format is always vd_sp; Vm format depends on size bit +# VCVTB and VCVTT to f16: Vd format is always vd_sp; +# Vm format depends on size bit VCVT_f16_f32 ---- 1110 1.11 0011 .... 1010 t:1 1.0 .... \ vd=%vd_sp vm=%vm_sp VCVT_f16_f64 ---- 1110 1.11 0011 .... 1011 t:1 1.0 .... \ vd=%vd_sp vm=%vm_dp -VRINTR_sp ---- 1110 1.11 0110 .... 1010 01.0 .... \ - vd=%vd_sp vm=%vm_sp -VRINTR_dp ---- 1110 1.11 0110 .... 1011 01.0 .... \ - vd=%vd_dp vm=%vm_dp +VRINTR_sp ---- 1110 1.11 0110 .... 1010 01.0 .... @vfp_dm_ss +VRINTR_dp ---- 1110 1.11 0110 .... 1011 01.0 .... @vfp_dm_dd -VRINTZ_sp ---- 1110 1.11 0110 .... 1010 11.0 .... \ - vd=%vd_sp vm=%vm_sp -VRINTZ_dp ---- 1110 1.11 0110 .... 1011 11.0 .... \ - vd=%vd_dp vm=%vm_dp +VRINTZ_sp ---- 1110 1.11 0110 .... 1010 11.0 .... @vfp_dm_ss +VRINTZ_dp ---- 1110 1.11 0110 .... 1011 11.0 .... @vfp_dm_dd -VRINTX_sp ---- 1110 1.11 0111 .... 1010 01.0 .... \ - vd=%vd_sp vm=%vm_sp -VRINTX_dp ---- 1110 1.11 0111 .... 1011 01.0 .... \ - vd=%vd_dp vm=%vm_dp +VRINTX_sp ---- 1110 1.11 0111 .... 1010 01.0 .... @vfp_dm_ss +VRINTX_dp ---- 1110 1.11 0111 .... 1011 01.0 .... @vfp_dm_dd -# VCVT between single and double: Vm precision depends on size; Vd is its reverse -VCVT_sp ---- 1110 1.11 0111 .... 1010 11.0 .... \ - vd=%vd_dp vm=%vm_sp -VCVT_dp ---- 1110 1.11 0111 .... 1011 11.0 .... \ - vd=%vd_sp vm=%vm_dp +# VCVT between single and double: +# Vm precision depends on size; Vd is its reverse +VCVT_sp ---- 1110 1.11 0111 .... 1010 11.0 .... @vfp_dm_ds +VCVT_dp ---- 1110 1.11 0111 .... 1011 11.0 .... @vfp_dm_sd # VCVT from integer to floating point: Vm always single; Vd depends on size VCVT_int_sp ---- 1110 1.11 1000 .... 1010 s:1 1.0 .... \ @@ -224,8 +196,7 @@ VCVT_int_dp ---- 1110 1.11 1000 .... 1011 s:1 1.0 .... \ vd=%vd_dp vm=%vm_sp # VJCVT is always dp to sp -VJCVT ---- 1110 1.11 1001 .... 1011 11.0 .... \ - vd=%vd_sp vm=%vm_dp +VJCVT ---- 1110 1.11 1001 .... 1011 11.0 .... @vfp_dm_sd # VCVT between floating-point and fixed-point. The immediate value # is in the same format as a Vm single-precision register number. @@ -242,3 +213,5 @@ VCVT_sp_int ---- 1110 1.11 110 s:1 .... 1010 rz:1 1.0 .... \ vd=%vd_sp vm=%vm_sp VCVT_dp_int ---- 1110 1.11 110 s:1 .... 1011 rz:1 1.0 .... \ vd=%vd_sp vm=%vm_dp + +VLLDM_VLSTM 1110 1100 001 l:1 rn:4 0000 1010 0000 0000 diff --git a/target/arm/vfp_helper.c b/target/arm/vfp_helper.c index 9710ef1c3e..930d6e747f 100644 --- a/target/arm/vfp_helper.c +++ b/target/arm/vfp_helper.c @@ -185,7 +185,7 @@ uint32_t vfp_get_fpscr(CPUARMState *env) void HELPER(vfp_set_fpscr)(CPUARMState *env, uint32_t val) { /* When ARMv8.2-FP16 is not supported, FZ16 is RES0. */ - if (!cpu_isar_feature(aa64_fp16, env_archcpu(env))) { + if (!cpu_isar_feature(any_fp16, env_archcpu(env))) { val &= ~FPCR_FZ16; } @@ -1322,4 +1322,33 @@ float64 HELPER(frint64_d)(float64 f, void *fpst) return frint_d(f, fpst, 64); } +void HELPER(check_hcr_el2_trap)(CPUARMState *env, uint32_t rt, uint32_t reg) +{ + uint32_t syndrome; + + switch (reg) { + case ARM_VFP_MVFR0: + case ARM_VFP_MVFR1: + case ARM_VFP_MVFR2: + if (!(arm_hcr_el2_eff(env) & HCR_TID3)) { + return; + } + break; + case ARM_VFP_FPSID: + if (!(arm_hcr_el2_eff(env) & HCR_TID0)) { + return; + } + break; + default: + g_assert_not_reached(); + } + + syndrome = ((EC_FPIDTRAP << ARM_EL_EC_SHIFT) + | ARM_EL_IL + | (1 << 24) | (0xe << 20) | (7 << 14) + | (reg << 10) | (rt << 5) | 1); + + raise_exception(env, EXCP_HYP_TRAP, syndrome, 2); +} + #endif diff --git a/target/cris/cpu-qom.h b/target/cris/cpu-qom.h index 308c1f95bd..f1de6041dc 100644 --- a/target/cris/cpu-qom.h +++ b/target/cris/cpu-qom.h @@ -45,7 +45,7 @@ typedef struct CRISCPUClass { /*< public >*/ DeviceRealize parent_realize; - void (*parent_reset)(CPUState *cpu); + DeviceReset parent_reset; uint32_t vr; } CRISCPUClass; diff --git a/target/cris/cpu.c b/target/cris/cpu.c index 7adfd6caf4..cff6b9eabf 100644 --- a/target/cris/cpu.c +++ b/target/cris/cpu.c @@ -40,15 +40,15 @@ static bool cris_cpu_has_work(CPUState *cs) return cs->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_NMI); } -/* CPUClass::reset() */ -static void cris_cpu_reset(CPUState *s) +static void cris_cpu_reset(DeviceState *dev) { + CPUState *s = CPU(dev); CRISCPU *cpu = CRIS_CPU(s); CRISCPUClass *ccc = CRIS_CPU_GET_CLASS(cpu); CPUCRISState *env = &cpu->env; uint32_t vr; - ccc->parent_reset(s); + ccc->parent_reset(dev); vr = env->pregs[PR_VR]; memset(env, 0, offsetof(CPUCRISState, end_reset_fields)); @@ -147,6 +147,14 @@ static void cris_cpu_set_irq(void *opaque, int irq, int level) CPUState *cs = CPU(cpu); int type = irq == CRIS_CPU_IRQ ? CPU_INTERRUPT_HARD : CPU_INTERRUPT_NMI; + if (irq == CRIS_CPU_IRQ) { + /* + * The PIC passes us the vector for the IRQ as the value it sends + * over the qemu_irq line + */ + cpu->env.interrupt_vector = level; + } + if (level) { cpu_interrupt(cs, type); } else { @@ -256,8 +264,7 @@ static void cris_cpu_class_init(ObjectClass *oc, void *data) device_class_set_parent_realize(dc, cris_cpu_realizefn, &ccc->parent_realize); - ccc->parent_reset = cc->reset; - cc->reset = cris_cpu_reset; + device_class_set_parent_reset(dc, cris_cpu_reset, &ccc->parent_reset); cc->class_by_name = cris_cpu_class_by_name; cc->has_work = cris_cpu_has_work; diff --git a/target/cris/cpu.h b/target/cris/cpu.h index aba0a66474..8f08d7628b 100644 --- a/target/cris/cpu.h +++ b/target/cris/cpu.h @@ -34,6 +34,7 @@ #define CPU_INTERRUPT_NMI CPU_INTERRUPT_TGT_EXT_3 /* CRUS CPU device objects interrupt lines. */ +/* PIC passes the vector for the IRQ as the value of it sends over qemu_irq */ #define CRIS_CPU_IRQ 0 #define CRIS_CPU_NMI 1 @@ -194,8 +195,8 @@ void cris_cpu_dump_state(CPUState *cs, FILE *f, int flags); hwaddr cris_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); -int crisv10_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); -int cris_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); +int crisv10_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); +int cris_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int cris_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); /* you can call this signal handler from your SIGBUS and SIGSEGV @@ -252,8 +253,6 @@ enum { #define cpu_signal_handler cpu_cris_signal_handler /* MMU modes definitions */ -#define MMU_MODE0_SUFFIX _kernel -#define MMU_MODE1_SUFFIX _user #define MMU_USER_IDX 1 static inline int cpu_mmu_index (CPUCRISState *env, bool ifetch) { diff --git a/target/cris/gdbstub.c b/target/cris/gdbstub.c index a3d76d2e8c..b01b2aa081 100644 --- a/target/cris/gdbstub.c +++ b/target/cris/gdbstub.c @@ -21,7 +21,7 @@ #include "cpu.h" #include "exec/gdbstub.h" -int crisv10_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +int crisv10_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) { CRISCPU *cpu = CRIS_CPU(cs); CPUCRISState *env = &cpu->env; @@ -53,7 +53,7 @@ int crisv10_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) return 0; } -int cris_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +int cris_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) { CRISCPU *cpu = CRIS_CPU(cs); CPUCRISState *env = &cpu->env; diff --git a/target/cris/translate.c b/target/cris/translate.c index e752bd0609..aaa46b5bca 100644 --- a/target/cris/translate.c +++ b/target/cris/translate.c @@ -27,7 +27,7 @@ #include "cpu.h" #include "disas/disas.h" #include "exec/exec-all.h" -#include "tcg-op.h" +#include "tcg/tcg-op.h" #include "exec/helper-proto.h" #include "mmu.h" #include "exec/cpu_ldst.h" @@ -3273,11 +3273,11 @@ void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int max_insns) #if !DISAS_CRIS if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM) && qemu_log_in_addr_range(pc_start)) { - qemu_log_lock(); + FILE *logfile = qemu_log_lock(); qemu_log("--------------\n"); qemu_log("IN: %s\n", lookup_symbol(pc_start)); log_target_disas(cs, pc_start, dc->pc - pc_start); - qemu_log_unlock(); + qemu_log_unlock(logfile); } #endif #endif diff --git a/target/hppa/cpu-qom.h b/target/hppa/cpu-qom.h index 6367dc4793..b1f6045495 100644 --- a/target/hppa/cpu-qom.h +++ b/target/hppa/cpu-qom.h @@ -44,7 +44,7 @@ typedef struct HPPACPUClass { /*< public >*/ DeviceRealize parent_realize; - void (*parent_reset)(CPUState *cpu); + DeviceReset parent_reset; } HPPACPUClass; typedef struct HPPACPU HPPACPU; diff --git a/target/hppa/cpu.h b/target/hppa/cpu.h index 6713d04f11..801a4fb1ba 100644 --- a/target/hppa/cpu.h +++ b/target/hppa/cpu.h @@ -321,7 +321,7 @@ void cpu_hppa_change_prot_id(CPUHPPAState *env); int cpu_hppa_signal_handler(int host_signum, void *pinfo, void *puc); hwaddr hppa_cpu_get_phys_page_debug(CPUState *cs, vaddr addr); -int hppa_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); +int hppa_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int hppa_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); void hppa_cpu_do_interrupt(CPUState *cpu); bool hppa_cpu_exec_interrupt(CPUState *cpu, int int_req); diff --git a/target/hppa/gdbstub.c b/target/hppa/gdbstub.c index 341888a9da..a6428a2893 100644 --- a/target/hppa/gdbstub.c +++ b/target/hppa/gdbstub.c @@ -21,7 +21,7 @@ #include "cpu.h" #include "exec/gdbstub.h" -int hppa_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +int hppa_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) { HPPACPU *cpu = HPPA_CPU(cs); CPUHPPAState *env = &cpu->env; diff --git a/target/hppa/helper.h b/target/hppa/helper.h index 38d834ef6b..2d483aab58 100644 --- a/target/hppa/helper.h +++ b/target/hppa/helper.h @@ -17,6 +17,8 @@ DEF_HELPER_FLAGS_3(stby_b_parallel, TCG_CALL_NO_WG, void, env, tl, tr) DEF_HELPER_FLAGS_3(stby_e, TCG_CALL_NO_WG, void, env, tl, tr) DEF_HELPER_FLAGS_3(stby_e_parallel, TCG_CALL_NO_WG, void, env, tl, tr) +DEF_HELPER_FLAGS_1(ldc_check, TCG_CALL_NO_RWG, void, tl) + DEF_HELPER_FLAGS_4(probe, TCG_CALL_NO_WG, tr, env, tl, i32, i32) DEF_HELPER_FLAGS_1(loaded_fr0, TCG_CALL_NO_RWG, void, env) diff --git a/target/hppa/op_helper.c b/target/hppa/op_helper.c index f0516e81f1..7823706e9c 100644 --- a/target/hppa/op_helper.c +++ b/target/hppa/op_helper.c @@ -153,6 +153,15 @@ void HELPER(stby_e_parallel)(CPUHPPAState *env, target_ulong addr, do_stby_e(env, addr, val, true, GETPC()); } +void HELPER(ldc_check)(target_ulong addr) +{ + if (unlikely(addr & 0xf)) { + qemu_log_mask(LOG_GUEST_ERROR, + "Undefined ldc to unaligned address mod 16: " + TARGET_FMT_lx "\n", addr); + } +} + target_ureg HELPER(probe)(CPUHPPAState *env, target_ulong addr, uint32_t level, uint32_t want) { diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 2f8d407a82..52d7bea1ea 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -22,7 +22,7 @@ #include "disas/disas.h" #include "qemu/host-utils.h" #include "exec/exec-all.h" -#include "tcg-op.h" +#include "tcg/tcg-op.h" #include "exec/cpu_ldst.h" #include "exec/helper-proto.h" #include "exec/helper-gen.h" @@ -2942,7 +2942,7 @@ static bool trans_st(DisasContext *ctx, arg_ldst *a) static bool trans_ldc(DisasContext *ctx, arg_ldst *a) { - MemOp mop = MO_TEUL | MO_ALIGN_16 | a->size; + MemOp mop = MO_TE | MO_ALIGN | a->size; TCGv_reg zero, dest, ofs; TCGv_tl addr; @@ -2958,8 +2958,21 @@ static bool trans_ldc(DisasContext *ctx, arg_ldst *a) form_gva(ctx, &addr, &ofs, a->b, a->x, a->scale ? a->size : 0, a->disp, a->sp, a->m, ctx->mmu_idx == MMU_PHYS_IDX); + + /* + * For hppa1.1, LDCW is undefined unless aligned mod 16. + * However actual hardware succeeds with aligned mod 4. + * Detect this case and log a GUEST_ERROR. + * + * TODO: HPPA64 relaxes the over-alignment requirement + * with the ,co completer. + */ + gen_helper_ldc_check(addr); + zero = tcg_const_reg(0); tcg_gen_atomic_xchg_reg(dest, addr, zero, ctx->mmu_idx, mop); + tcg_temp_free(zero); + if (a->m) { save_gpr(ctx, a->b, ofs); } diff --git a/target/i386/cpu-qom.h b/target/i386/cpu-qom.h index 0efab2fc67..3e96f8d668 100644 --- a/target/i386/cpu-qom.h +++ b/target/i386/cpu-qom.h @@ -71,7 +71,7 @@ typedef struct X86CPUClass { DeviceRealize parent_realize; DeviceUnrealize parent_unrealize; - void (*parent_reset)(CPUState *cpu); + DeviceReset parent_reset; } X86CPUClass; typedef struct X86CPU X86CPU; diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 69f518a21a..90ffc5f3b1 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -338,68 +338,15 @@ static void encode_cache_cpuid80000006(CPUCacheInfo *l2, } } -/* - * Definitions used for building CPUID Leaf 0x8000001D and 0x8000001E - * Please refer to the AMD64 Architecture Programmer’s Manual Volume 3. - * Define the constants to build the cpu topology. Right now, TOPOEXT - * feature is enabled only on EPYC. So, these constants are based on - * EPYC supported configurations. We may need to handle the cases if - * these values change in future. - */ -/* Maximum core complexes in a node */ -#define MAX_CCX 2 -/* Maximum cores in a core complex */ -#define MAX_CORES_IN_CCX 4 -/* Maximum cores in a node */ -#define MAX_CORES_IN_NODE 8 -/* Maximum nodes in a socket */ -#define MAX_NODES_PER_SOCKET 4 - -/* - * Figure out the number of nodes required to build this config. - * Max cores in a node is 8 - */ -static int nodes_in_socket(int nr_cores) -{ - int nodes; - - nodes = DIV_ROUND_UP(nr_cores, MAX_CORES_IN_NODE); - - /* Hardware does not support config with 3 nodes, return 4 in that case */ - return (nodes == 3) ? 4 : nodes; -} - -/* - * Decide the number of cores in a core complex with the given nr_cores using - * following set constants MAX_CCX, MAX_CORES_IN_CCX, MAX_CORES_IN_NODE and - * MAX_NODES_PER_SOCKET. Maintain symmetry as much as possible - * L3 cache is shared across all cores in a core complex. So, this will also - * tell us how many cores are sharing the L3 cache. - */ -static int cores_in_core_complex(int nr_cores) -{ - int nodes; - - /* Check if we can fit all the cores in one core complex */ - if (nr_cores <= MAX_CORES_IN_CCX) { - return nr_cores; - } - /* Get the number of nodes required to build this config */ - nodes = nodes_in_socket(nr_cores); - - /* - * Divide the cores accros all the core complexes - * Return rounded up value - */ - return DIV_ROUND_UP(nr_cores, nodes * MAX_CCX); -} - /* Encode cache info for CPUID[8000001D] */ -static void encode_cache_cpuid8000001d(CPUCacheInfo *cache, CPUState *cs, - uint32_t *eax, uint32_t *ebx, - uint32_t *ecx, uint32_t *edx) +static void encode_cache_cpuid8000001d(CPUCacheInfo *cache, + X86CPUTopoInfo *topo_info, + uint32_t *eax, uint32_t *ebx, + uint32_t *ecx, uint32_t *edx) { uint32_t l3_cores; + unsigned nodes = MAX(topo_info->nodes_per_pkg, 1); + assert(cache->size == cache->line_size * cache->associativity * cache->partitions * cache->sets); @@ -408,10 +355,13 @@ static void encode_cache_cpuid8000001d(CPUCacheInfo *cache, CPUState *cs, /* L3 is shared among multiple cores */ if (cache->level == 3) { - l3_cores = cores_in_core_complex(cs->nr_cores); - *eax |= ((l3_cores * cs->nr_threads) - 1) << 14; + l3_cores = DIV_ROUND_UP((topo_info->dies_per_pkg * + topo_info->cores_per_die * + topo_info->threads_per_core), + nodes); + *eax |= (l3_cores - 1) << 14; } else { - *eax |= ((cs->nr_threads - 1) << 14); + *eax |= ((topo_info->threads_per_core - 1) << 14); } assert(cache->line_size > 0); @@ -431,55 +381,17 @@ static void encode_cache_cpuid8000001d(CPUCacheInfo *cache, CPUState *cs, (cache->complex_indexing ? CACHE_COMPLEX_IDX : 0); } -/* Data structure to hold the configuration info for a given core index */ -struct core_topology { - /* core complex id of the current core index */ - int ccx_id; - /* - * Adjusted core index for this core in the topology - * This can be 0,1,2,3 with max 4 cores in a core complex - */ - int core_id; - /* Node id for this core index */ - int node_id; - /* Number of nodes in this config */ - int num_nodes; -}; - -/* - * Build the configuration closely match the EPYC hardware. Using the EPYC - * hardware configuration values (MAX_CCX, MAX_CORES_IN_CCX, MAX_CORES_IN_NODE) - * right now. This could change in future. - * nr_cores : Total number of cores in the config - * core_id : Core index of the current CPU - * topo : Data structure to hold all the config info for this core index - */ -static void build_core_topology(int nr_cores, int core_id, - struct core_topology *topo) -{ - int nodes, cores_in_ccx; - - /* First get the number of nodes required */ - nodes = nodes_in_socket(nr_cores); - - cores_in_ccx = cores_in_core_complex(nr_cores); - - topo->node_id = core_id / (cores_in_ccx * MAX_CCX); - topo->ccx_id = (core_id % (cores_in_ccx * MAX_CCX)) / cores_in_ccx; - topo->core_id = core_id % cores_in_ccx; - topo->num_nodes = nodes; -} - /* Encode cache info for CPUID[8000001E] */ -static void encode_topo_cpuid8000001e(CPUState *cs, X86CPU *cpu, +static void encode_topo_cpuid8000001e(X86CPUTopoInfo *topo_info, X86CPU *cpu, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx) { - struct core_topology topo = {0}; - unsigned long nodes; + X86CPUTopoIDs topo_ids = {0}; + unsigned long nodes = MAX(topo_info->nodes_per_pkg, 1); int shift; - build_core_topology(cs->nr_cores, cpu->core_id, &topo); + x86_topo_ids_from_apicid_epyc(cpu->apic_id, topo_info, &topo_ids); + *eax = cpu->apic_id; /* * CPUID_Fn8000001E_EBX @@ -496,12 +408,8 @@ static void encode_topo_cpuid8000001e(CPUState *cs, X86CPU *cpu, * 3 Core complex id * 1:0 Core id */ - if (cs->nr_threads - 1) { - *ebx = ((cs->nr_threads - 1) << 8) | (topo.node_id << 3) | - (topo.ccx_id << 2) | topo.core_id; - } else { - *ebx = (topo.node_id << 4) | (topo.ccx_id << 3) | topo.core_id; - } + *ebx = ((topo_info->threads_per_core - 1) << 8) | (topo_ids.node_id << 3) | + (topo_ids.core_id); /* * CPUID_Fn8000001E_ECX * 31:11 Reserved @@ -510,9 +418,8 @@ static void encode_topo_cpuid8000001e(CPUState *cs, X86CPU *cpu, * 2 Socket id * 1:0 Node id */ - if (topo.num_nodes <= 4) { - *ecx = ((topo.num_nodes - 1) << 8) | (cpu->socket_id << 2) | - topo.node_id; + if (nodes <= 4) { + *ecx = ((nodes - 1) << 8) | (topo_ids.pkg_id << 2) | topo_ids.node_id; } else { /* * Node id fix up. Actual hardware supports up to 4 nodes. But with @@ -527,10 +434,10 @@ static void encode_topo_cpuid8000001e(CPUState *cs, X86CPU *cpu, * number of nodes. find_last_bit returns last set bit(0 based). Left * shift(+1) the socket id to represent all the nodes. */ - nodes = topo.num_nodes - 1; + nodes -= 1; shift = find_last_bit(&nodes, 8); - *ecx = ((topo.num_nodes - 1) << 8) | (cpu->socket_id << (shift + 1)) | - topo.node_id; + *ecx = (nodes << 8) | (topo_ids.pkg_id << (shift + 1)) | + topo_ids.node_id; } *edx = 0; } @@ -1133,7 +1040,7 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { "clzero", NULL, "xsaveerptr", NULL, NULL, NULL, NULL, NULL, NULL, "wbnoinvd", NULL, NULL, - "ibpb", NULL, NULL, NULL, + "ibpb", NULL, NULL, "amd-stibp", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "amd-ssbd", "virt-ssbd", "amd-no-ssb", NULL, @@ -1671,11 +1578,8 @@ static char *x86_cpu_type_name(const char *model_name) static ObjectClass *x86_cpu_class_by_name(const char *cpu_model) { - ObjectClass *oc; - char *typename = x86_cpu_type_name(cpu_model); - oc = object_class_by_name(typename); - g_free(typename); - return oc; + g_autofree char *typename = x86_cpu_type_name(cpu_model); + return object_class_by_name(typename); } static char *x86_cpu_class_get_model_name(X86CPUClass *cc) @@ -1693,6 +1597,7 @@ typedef struct PropValue { typedef struct X86CPUVersionDefinition { X86CPUVersion version; const char *alias; + const char *note; PropValue *props; } X86CPUVersionDefinition; @@ -1709,6 +1614,10 @@ typedef struct X86CPUDefinition { FeatureWordArray features; const char *model_id; CPUCaches *cache_info; + + /* Use AMD EPYC encoding for apic id */ + bool use_epyc_apic_id_encoding; + /* * Definitions for alternative versions of CPU model. * List is terminated by item with version == 0. @@ -1723,6 +1632,7 @@ struct X86CPUModel { X86CPUDefinition *cpudef; /* CPU model version */ X86CPUVersion version; + const char *note; /* * If true, this is an alias CPU model. * This matters only for "-cpu help" and query-cpu-definitions @@ -1749,6 +1659,18 @@ static const X86CPUVersionDefinition *x86_cpu_def_get_versions(X86CPUDefinition return def->versions ?: default_version_list; } +bool cpu_x86_use_epyc_apic_id_encoding(const char *cpu_type) +{ + X86CPUClass *xcc = X86_CPU_CLASS(object_class_by_name(cpu_type)); + + assert(xcc); + if (xcc->model && xcc->model->cpudef) { + return xcc->model->cpudef->use_epyc_apic_id_encoding; + } else { + return false; + } +} + static CPUCaches epyc_cache_info = { .l1d_cache = &(CPUCacheInfo) { .type = DATA_CACHE, @@ -1799,6 +1721,56 @@ static CPUCaches epyc_cache_info = { }, }; +static CPUCaches epyc_rome_cache_info = { + .l1d_cache = &(CPUCacheInfo) { + .type = DATA_CACHE, + .level = 1, + .size = 32 * KiB, + .line_size = 64, + .associativity = 8, + .partitions = 1, + .sets = 64, + .lines_per_tag = 1, + .self_init = 1, + .no_invd_sharing = true, + }, + .l1i_cache = &(CPUCacheInfo) { + .type = INSTRUCTION_CACHE, + .level = 1, + .size = 32 * KiB, + .line_size = 64, + .associativity = 8, + .partitions = 1, + .sets = 64, + .lines_per_tag = 1, + .self_init = 1, + .no_invd_sharing = true, + }, + .l2_cache = &(CPUCacheInfo) { + .type = UNIFIED_CACHE, + .level = 2, + .size = 512 * KiB, + .line_size = 64, + .associativity = 8, + .partitions = 1, + .sets = 1024, + .lines_per_tag = 1, + }, + .l3_cache = &(CPUCacheInfo) { + .type = UNIFIED_CACHE, + .level = 3, + .size = 16 * MiB, + .line_size = 64, + .associativity = 16, + .partitions = 1, + .sets = 16384, + .lines_per_tag = 1, + .self_init = true, + .inclusive = true, + .complex_indexing = true, + }, +}; + /* The following VMX features are not supported by KVM and are left out in the * CPU definitions: * @@ -2908,6 +2880,8 @@ static X86CPUDefinition builtin_x86_defs[] = { .props = (PropValue[]) { { "hle", "off" }, { "rtm", "off" }, + { "model-id", + "Intel Core Processor (Skylake, IBRS, no TSX)" }, { /* end of list */ } } }, @@ -3031,6 +3005,8 @@ static X86CPUDefinition builtin_x86_defs[] = { .props = (PropValue[]) { { "hle", "off" }, { "rtm", "off" }, + { "model-id", + "Intel Xeon Processor (Skylake, IBRS, no TSX)" }, { /* end of list */ } } }, @@ -3159,6 +3135,115 @@ static X86CPUDefinition builtin_x86_defs[] = { { /* end of list */ } } }, + { + .name = "Cooperlake", + .level = 0xd, + .vendor = CPUID_VENDOR_INTEL, + .family = 6, + .model = 85, + .stepping = 10, + .features[FEAT_1_EDX] = + CPUID_VME | CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX | + CPUID_CLFLUSH | CPUID_PSE36 | CPUID_PAT | CPUID_CMOV | CPUID_MCA | + CPUID_PGE | CPUID_MTRR | CPUID_SEP | CPUID_APIC | CPUID_CX8 | + CPUID_MCE | CPUID_PAE | CPUID_MSR | CPUID_TSC | CPUID_PSE | + CPUID_DE | CPUID_FP87, + .features[FEAT_1_ECX] = + CPUID_EXT_AVX | CPUID_EXT_XSAVE | CPUID_EXT_AES | + CPUID_EXT_POPCNT | CPUID_EXT_X2APIC | CPUID_EXT_SSE42 | + CPUID_EXT_SSE41 | CPUID_EXT_CX16 | CPUID_EXT_SSSE3 | + CPUID_EXT_PCLMULQDQ | CPUID_EXT_SSE3 | + CPUID_EXT_TSC_DEADLINE_TIMER | CPUID_EXT_FMA | CPUID_EXT_MOVBE | + CPUID_EXT_PCID | CPUID_EXT_F16C | CPUID_EXT_RDRAND, + .features[FEAT_8000_0001_EDX] = + CPUID_EXT2_LM | CPUID_EXT2_PDPE1GB | CPUID_EXT2_RDTSCP | + CPUID_EXT2_NX | CPUID_EXT2_SYSCALL, + .features[FEAT_8000_0001_ECX] = + CPUID_EXT3_ABM | CPUID_EXT3_LAHF_LM | CPUID_EXT3_3DNOWPREFETCH, + .features[FEAT_7_0_EBX] = + CPUID_7_0_EBX_FSGSBASE | CPUID_7_0_EBX_BMI1 | + CPUID_7_0_EBX_HLE | CPUID_7_0_EBX_AVX2 | CPUID_7_0_EBX_SMEP | + CPUID_7_0_EBX_BMI2 | CPUID_7_0_EBX_ERMS | CPUID_7_0_EBX_INVPCID | + CPUID_7_0_EBX_RTM | CPUID_7_0_EBX_RDSEED | CPUID_7_0_EBX_ADX | + CPUID_7_0_EBX_SMAP | CPUID_7_0_EBX_CLWB | + CPUID_7_0_EBX_AVX512F | CPUID_7_0_EBX_AVX512DQ | + CPUID_7_0_EBX_AVX512BW | CPUID_7_0_EBX_AVX512CD | + CPUID_7_0_EBX_AVX512VL | CPUID_7_0_EBX_CLFLUSHOPT, + .features[FEAT_7_0_ECX] = + CPUID_7_0_ECX_PKU | + CPUID_7_0_ECX_AVX512VNNI, + .features[FEAT_7_0_EDX] = + CPUID_7_0_EDX_SPEC_CTRL | CPUID_7_0_EDX_STIBP | + CPUID_7_0_EDX_SPEC_CTRL_SSBD | CPUID_7_0_EDX_ARCH_CAPABILITIES, + .features[FEAT_ARCH_CAPABILITIES] = + MSR_ARCH_CAP_RDCL_NO | MSR_ARCH_CAP_IBRS_ALL | + MSR_ARCH_CAP_SKIP_L1DFL_VMENTRY | MSR_ARCH_CAP_MDS_NO | + MSR_ARCH_CAP_PSCHANGE_MC_NO | MSR_ARCH_CAP_TAA_NO, + .features[FEAT_7_1_EAX] = + CPUID_7_1_EAX_AVX512_BF16, + /* + * Missing: XSAVES (not supported by some Linux versions, + * including v4.1 to v4.12). + * KVM doesn't yet expose any XSAVES state save component, + * and the only one defined in Skylake (processor tracing) + * probably will block migration anyway. + */ + .features[FEAT_XSAVE] = + CPUID_XSAVE_XSAVEOPT | CPUID_XSAVE_XSAVEC | + CPUID_XSAVE_XGETBV1, + .features[FEAT_6_EAX] = + CPUID_6_EAX_ARAT, + /* Missing: Mode-based execute control (XS/XU), processor tracing, TSC scaling */ + .features[FEAT_VMX_BASIC] = MSR_VMX_BASIC_INS_OUTS | + MSR_VMX_BASIC_TRUE_CTLS, + .features[FEAT_VMX_ENTRY_CTLS] = VMX_VM_ENTRY_IA32E_MODE | + VMX_VM_ENTRY_LOAD_IA32_PERF_GLOBAL_CTRL | VMX_VM_ENTRY_LOAD_IA32_PAT | + VMX_VM_ENTRY_LOAD_DEBUG_CONTROLS | VMX_VM_ENTRY_LOAD_IA32_EFER, + .features[FEAT_VMX_EPT_VPID_CAPS] = MSR_VMX_EPT_EXECONLY | + MSR_VMX_EPT_PAGE_WALK_LENGTH_4 | MSR_VMX_EPT_WB | MSR_VMX_EPT_2MB | + MSR_VMX_EPT_1GB | MSR_VMX_EPT_INVEPT | + MSR_VMX_EPT_INVEPT_SINGLE_CONTEXT | MSR_VMX_EPT_INVEPT_ALL_CONTEXT | + MSR_VMX_EPT_INVVPID | MSR_VMX_EPT_INVVPID_SINGLE_ADDR | + MSR_VMX_EPT_INVVPID_SINGLE_CONTEXT | MSR_VMX_EPT_INVVPID_ALL_CONTEXT | + MSR_VMX_EPT_INVVPID_SINGLE_CONTEXT_NOGLOBALS | MSR_VMX_EPT_AD_BITS, + .features[FEAT_VMX_EXIT_CTLS] = + VMX_VM_EXIT_ACK_INTR_ON_EXIT | VMX_VM_EXIT_SAVE_DEBUG_CONTROLS | + VMX_VM_EXIT_LOAD_IA32_PERF_GLOBAL_CTRL | + VMX_VM_EXIT_LOAD_IA32_PAT | VMX_VM_EXIT_LOAD_IA32_EFER | + VMX_VM_EXIT_SAVE_IA32_PAT | VMX_VM_EXIT_SAVE_IA32_EFER | + VMX_VM_EXIT_SAVE_VMX_PREEMPTION_TIMER, + .features[FEAT_VMX_MISC] = MSR_VMX_MISC_ACTIVITY_HLT | + MSR_VMX_MISC_STORE_LMA | MSR_VMX_MISC_VMWRITE_VMEXIT, + .features[FEAT_VMX_PINBASED_CTLS] = VMX_PIN_BASED_EXT_INTR_MASK | + VMX_PIN_BASED_NMI_EXITING | VMX_PIN_BASED_VIRTUAL_NMIS | + VMX_PIN_BASED_VMX_PREEMPTION_TIMER | VMX_PIN_BASED_POSTED_INTR, + .features[FEAT_VMX_PROCBASED_CTLS] = VMX_CPU_BASED_VIRTUAL_INTR_PENDING | + VMX_CPU_BASED_USE_TSC_OFFSETING | VMX_CPU_BASED_HLT_EXITING | + VMX_CPU_BASED_INVLPG_EXITING | VMX_CPU_BASED_MWAIT_EXITING | + VMX_CPU_BASED_RDPMC_EXITING | VMX_CPU_BASED_RDTSC_EXITING | + VMX_CPU_BASED_CR8_LOAD_EXITING | VMX_CPU_BASED_CR8_STORE_EXITING | + VMX_CPU_BASED_TPR_SHADOW | VMX_CPU_BASED_MOV_DR_EXITING | + VMX_CPU_BASED_UNCOND_IO_EXITING | VMX_CPU_BASED_USE_IO_BITMAPS | + VMX_CPU_BASED_MONITOR_EXITING | VMX_CPU_BASED_PAUSE_EXITING | + VMX_CPU_BASED_VIRTUAL_NMI_PENDING | VMX_CPU_BASED_USE_MSR_BITMAPS | + VMX_CPU_BASED_CR3_LOAD_EXITING | VMX_CPU_BASED_CR3_STORE_EXITING | + VMX_CPU_BASED_MONITOR_TRAP_FLAG | + VMX_CPU_BASED_ACTIVATE_SECONDARY_CONTROLS, + .features[FEAT_VMX_SECONDARY_CTLS] = + VMX_SECONDARY_EXEC_VIRTUALIZE_APIC_ACCESSES | + VMX_SECONDARY_EXEC_WBINVD_EXITING | VMX_SECONDARY_EXEC_ENABLE_EPT | + VMX_SECONDARY_EXEC_DESC | VMX_SECONDARY_EXEC_RDTSCP | + VMX_SECONDARY_EXEC_VIRTUALIZE_X2APIC_MODE | + VMX_SECONDARY_EXEC_ENABLE_VPID | VMX_SECONDARY_EXEC_UNRESTRICTED_GUEST | + VMX_SECONDARY_EXEC_APIC_REGISTER_VIRT | + VMX_SECONDARY_EXEC_VIRTUAL_INTR_DELIVERY | + VMX_SECONDARY_EXEC_RDRAND_EXITING | VMX_SECONDARY_EXEC_ENABLE_INVPCID | + VMX_SECONDARY_EXEC_ENABLE_VMFUNC | VMX_SECONDARY_EXEC_SHADOW_VMCS | + VMX_SECONDARY_EXEC_RDSEED_EXITING | VMX_SECONDARY_EXEC_ENABLE_PML, + .features[FEAT_VMX_VMFUNC] = MSR_VMX_VMFUNC_EPT_SWITCHING, + .xlevel = 0x80000008, + .model_id = "Intel Xeon Processor (Cooperlake)", + }, { .name = "Icelake-Client", .level = 0xd, @@ -3386,6 +3471,19 @@ static X86CPUDefinition builtin_x86_defs[] = { { /* end of list */ } }, }, + { + .version = 3, + .props = (PropValue[]) { + { "arch-capabilities", "on" }, + { "rdctl-no", "on" }, + { "ibrs-all", "on" }, + { "skip-l1dfl-vmentry", "on" }, + { "mds-no", "on" }, + { "pschange-mc-no", "on" }, + { "taa-no", "on" }, + { /* end of list */ } + }, + }, { /* end of list */ } } }, @@ -3482,6 +3580,18 @@ static X86CPUDefinition builtin_x86_defs[] = { .features[FEAT_VMX_VMFUNC] = MSR_VMX_VMFUNC_EPT_SWITCHING, .xlevel = 0x80000008, .model_id = "Intel Atom Processor (Denverton)", + .versions = (X86CPUVersionDefinition[]) { + { .version = 1 }, + { + .version = 2, + .props = (PropValue[]) { + { "monitor", "off" }, + { "mpx", "off" }, + { /* end of list */ }, + }, + }, + { /* end of list */ }, + }, }, { .name = "Snowridge", @@ -3818,10 +3928,6 @@ static X86CPUDefinition builtin_x86_defs[] = { CPUID_7_0_EBX_SMEP | CPUID_7_0_EBX_BMI2 | CPUID_7_0_EBX_RDSEED | CPUID_7_0_EBX_ADX | CPUID_7_0_EBX_SMAP | CPUID_7_0_EBX_CLFLUSHOPT | CPUID_7_0_EBX_SHA_NI, - /* Missing: XSAVES (not supported by some Linux versions, - * including v4.1 to v4.12). - * KVM doesn't yet expose any XSAVES state save component. - */ .features[FEAT_XSAVE] = CPUID_XSAVE_XSAVEOPT | CPUID_XSAVE_XSAVEC | CPUID_XSAVE_XGETBV1, @@ -3832,6 +3938,7 @@ static X86CPUDefinition builtin_x86_defs[] = { .xlevel = 0x8000001E, .model_id = "AMD EPYC Processor", .cache_info = &epyc_cache_info, + .use_epyc_apic_id_encoding = 1, .versions = (X86CPUVersionDefinition[]) { { .version = 1 }, { @@ -3844,6 +3951,19 @@ static X86CPUDefinition builtin_x86_defs[] = { { /* end of list */ } } }, + { + .version = 3, + .props = (PropValue[]) { + { "ibpb", "on" }, + { "perfctr-core", "on" }, + { "clzero", "on" }, + { "xsaveerptr", "on" }, + { "xsaves", "on" }, + { "model-id", + "AMD EPYC Processor" }, + { /* end of list */ } + } + }, { /* end of list */ } } }, @@ -3897,6 +4017,57 @@ static X86CPUDefinition builtin_x86_defs[] = { .model_id = "Hygon Dhyana Processor", .cache_info = &epyc_cache_info, }, + { + .name = "EPYC-Rome", + .level = 0xd, + .vendor = CPUID_VENDOR_AMD, + .family = 23, + .model = 49, + .stepping = 0, + .features[FEAT_1_EDX] = + CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX | CPUID_CLFLUSH | + CPUID_PSE36 | CPUID_PAT | CPUID_CMOV | CPUID_MCA | CPUID_PGE | + CPUID_MTRR | CPUID_SEP | CPUID_APIC | CPUID_CX8 | CPUID_MCE | + CPUID_PAE | CPUID_MSR | CPUID_TSC | CPUID_PSE | CPUID_DE | + CPUID_VME | CPUID_FP87, + .features[FEAT_1_ECX] = + CPUID_EXT_RDRAND | CPUID_EXT_F16C | CPUID_EXT_AVX | + CPUID_EXT_XSAVE | CPUID_EXT_AES | CPUID_EXT_POPCNT | + CPUID_EXT_MOVBE | CPUID_EXT_SSE42 | CPUID_EXT_SSE41 | + CPUID_EXT_CX16 | CPUID_EXT_FMA | CPUID_EXT_SSSE3 | + CPUID_EXT_MONITOR | CPUID_EXT_PCLMULQDQ | CPUID_EXT_SSE3, + .features[FEAT_8000_0001_EDX] = + CPUID_EXT2_LM | CPUID_EXT2_RDTSCP | CPUID_EXT2_PDPE1GB | + CPUID_EXT2_FFXSR | CPUID_EXT2_MMXEXT | CPUID_EXT2_NX | + CPUID_EXT2_SYSCALL, + .features[FEAT_8000_0001_ECX] = + CPUID_EXT3_OSVW | CPUID_EXT3_3DNOWPREFETCH | + CPUID_EXT3_MISALIGNSSE | CPUID_EXT3_SSE4A | CPUID_EXT3_ABM | + CPUID_EXT3_CR8LEG | CPUID_EXT3_SVM | CPUID_EXT3_LAHF_LM | + CPUID_EXT3_TOPOEXT | CPUID_EXT3_PERFCORE, + .features[FEAT_8000_0008_EBX] = + CPUID_8000_0008_EBX_CLZERO | CPUID_8000_0008_EBX_XSAVEERPTR | + CPUID_8000_0008_EBX_WBNOINVD | CPUID_8000_0008_EBX_IBPB | + CPUID_8000_0008_EBX_STIBP, + .features[FEAT_7_0_EBX] = + CPUID_7_0_EBX_FSGSBASE | CPUID_7_0_EBX_BMI1 | CPUID_7_0_EBX_AVX2 | + CPUID_7_0_EBX_SMEP | CPUID_7_0_EBX_BMI2 | CPUID_7_0_EBX_RDSEED | + CPUID_7_0_EBX_ADX | CPUID_7_0_EBX_SMAP | CPUID_7_0_EBX_CLFLUSHOPT | + CPUID_7_0_EBX_SHA_NI | CPUID_7_0_EBX_CLWB, + .features[FEAT_7_0_ECX] = + CPUID_7_0_ECX_UMIP | CPUID_7_0_ECX_RDPID, + .features[FEAT_XSAVE] = + CPUID_XSAVE_XSAVEOPT | CPUID_XSAVE_XSAVEC | + CPUID_XSAVE_XGETBV1 | CPUID_XSAVE_XSAVES, + .features[FEAT_6_EAX] = + CPUID_6_EAX_ARAT, + .features[FEAT_SVM] = + CPUID_SVM_NPT | CPUID_SVM_NRIPSAVE, + .xlevel = 0x8000001E, + .model_id = "AMD EPYC-Rome Processor", + .cache_info = &epyc_rome_cache_info, + .use_epyc_apic_id_encoding = 1, + }, }; /* KVM-specific features that are automatically added/removed @@ -3924,7 +4095,13 @@ static PropValue tcg_default_props[] = { }; -X86CPUVersion default_cpu_version = CPU_VERSION_LATEST; +/* + * We resolve CPU model aliases using -v1 when using "-machine + * none", but this is just for compatibility while libvirt isn't + * adapted to resolve CPU model versions before creating VMs. + * See "Runnability guarantee of CPU models" at * qemu-deprecated.texi. + */ +X86CPUVersion default_cpu_version = 1; void x86_cpu_set_default_version(X86CPUVersion version) { @@ -4032,7 +4209,7 @@ static void max_x86_cpu_class_init(ObjectClass *oc, void *data) xcc->model_description = "Enables all features supported by the accelerator in the current host"; - dc->props = max_x86_cpu_properties; + device_class_set_props(dc, max_x86_cpu_properties); } static void max_x86_cpu_initfn(Object *obj) @@ -4166,7 +4343,6 @@ static void mark_unavailable_features(X86CPU *cpu, FeatureWord w, uint64_t mask, CPUX86State *env = &cpu->env; FeatureWordInfo *f = &feature_word_info[w]; int i; - char *feat_word_str; if (!cpu->force_features) { env->features[w] &= ~mask; @@ -4179,13 +4355,12 @@ static void mark_unavailable_features(X86CPU *cpu, FeatureWord w, uint64_t mask, for (i = 0; i < 64; ++i) { if ((1ULL << i) & mask) { - feat_word_str = feature_word_description(f, i); + g_autofree char *feat_word_str = feature_word_description(f, i); warn_report("%s: %s%s%s [bit %d]", verbose_prefix, feat_word_str, f->feat_names[i] ? "." : "", f->feat_names[i] ? f->feat_names[i] : "", i); - g_free(feat_word_str); } } } @@ -4640,7 +4815,7 @@ static void x86_cpu_class_check_missing_features(X86CPUClass *xcc, return; } - xc = X86_CPU(object_new(object_class_get_name(OBJECT_CLASS(xcc)))); + xc = X86_CPU(object_new_with_class(OBJECT_CLASS(xcc))); x86_cpu_expand_features(xc, &err); if (err) { @@ -4687,17 +4862,14 @@ static gint x86_cpu_list_compare(gconstpointer a, gconstpointer b) ObjectClass *class_b = (ObjectClass *)b; X86CPUClass *cc_a = X86_CPU_CLASS(class_a); X86CPUClass *cc_b = X86_CPU_CLASS(class_b); - char *name_a, *name_b; int ret; if (cc_a->ordering != cc_b->ordering) { ret = cc_a->ordering - cc_b->ordering; } else { - name_a = x86_cpu_class_get_model_name(cc_a); - name_b = x86_cpu_class_get_model_name(cc_b); + g_autofree char *name_a = x86_cpu_class_get_model_name(cc_a); + g_autofree char *name_b = x86_cpu_class_get_model_name(cc_b); ret = strcmp(name_a, name_b); - g_free(name_a); - g_free(name_b); } return ret; } @@ -4711,7 +4883,7 @@ static GSList *get_sorted_cpu_model_list(void) static char *x86_cpu_class_get_model_id(X86CPUClass *xc) { - Object *obj = object_new(object_class_get_name(OBJECT_CLASS(xc))); + Object *obj = object_new_with_class(OBJECT_CLASS(xc)); char *r = object_property_get_str(obj, "model-id", &error_abort); object_unref(obj); return r; @@ -4735,9 +4907,10 @@ static void x86_cpu_list_entry(gpointer data, gpointer user_data) { ObjectClass *oc = data; X86CPUClass *cc = X86_CPU_CLASS(oc); - char *name = x86_cpu_class_get_model_name(cc); - char *desc = g_strdup(cc->model_description); - char *alias_of = x86_cpu_class_get_alias_of(cc); + g_autofree char *name = x86_cpu_class_get_model_name(cc); + g_autofree char *desc = g_strdup(cc->model_description); + g_autofree char *alias_of = x86_cpu_class_get_alias_of(cc); + g_autofree char *model_id = x86_cpu_class_get_model_id(cc); if (!desc && alias_of) { if (cc->model && cc->model->version == CPU_VERSION_AUTO) { @@ -4746,14 +4919,14 @@ static void x86_cpu_list_entry(gpointer data, gpointer user_data) desc = g_strdup_printf("(alias of %s)", alias_of); } } + if (!desc && cc->model && cc->model->note) { + desc = g_strdup_printf("%s [%s]", model_id, cc->model->note); + } if (!desc) { - desc = x86_cpu_class_get_model_id(cc); + desc = g_strdup_printf("%s", model_id); } - qemu_printf("x86 %-20s %-48s\n", name, desc); - g_free(name); - g_free(desc); - g_free(alias_of); + qemu_printf("x86 %-20s %-58s\n", name, desc); } /* list available CPU models and flags */ @@ -5092,7 +5265,7 @@ static X86CPU *x86_cpu_from_model(const char *model, QDict *props, Error **errp) goto out; } - xc = X86_CPU(object_new(object_class_get_name(OBJECT_CLASS(xcc)))); + xc = X86_CPU(object_new_with_class(OBJECT_CLASS(xcc))); if (props) { object_apply_props(OBJECT(xc), props, &err); if (err) { @@ -5192,7 +5365,7 @@ static void x86_cpu_cpudef_class_init(ObjectClass *oc, void *data) static void x86_register_cpu_model_type(const char *name, X86CPUModel *model) { - char *typename = x86_cpu_type_name(name); + g_autofree char *typename = x86_cpu_type_name(name); TypeInfo ti = { .name = typename, .parent = TYPE_X86_CPU, @@ -5201,14 +5374,12 @@ static void x86_register_cpu_model_type(const char *name, X86CPUModel *model) }; type_register(&ti); - g_free(typename); } static void x86_register_cpudef_types(X86CPUDefinition *def) { X86CPUModel *m; const X86CPUVersionDefinition *vdef; - char *name; /* AMD aliases are handled at runtime based on CPUID vendor, so * they shouldn't be set on the CPU model table. @@ -5228,11 +5399,12 @@ static void x86_register_cpudef_types(X86CPUDefinition *def) for (vdef = x86_cpu_def_get_versions(def); vdef->version; vdef++) { X86CPUModel *m = g_new0(X86CPUModel, 1); + g_autofree char *name = + x86_cpu_versioned_model_name(def, vdef->version); m->cpudef = def; m->version = vdef->version; - name = x86_cpu_versioned_model_name(def, vdef->version); + m->note = vdef->note; x86_register_cpu_model_type(name, m); - g_free(name); if (vdef->alias) { X86CPUModel *am = g_new0(X86CPUModel, 1); @@ -5263,6 +5435,12 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, uint32_t die_offset; uint32_t limit; uint32_t signature[3]; + X86CPUTopoInfo topo_info; + + topo_info.nodes_per_pkg = env->nr_nodes; + topo_info.dies_per_pkg = env->nr_dies; + topo_info.cores_per_die = cs->nr_cores; + topo_info.threads_per_core = cs->nr_threads; /* Calculate & apply limits for different index ranges */ if (index >= 0xC0000000) { @@ -5349,8 +5527,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, eax, ebx, ecx, edx); break; case 3: /* L3 cache info */ - die_offset = apicid_die_offset(env->nr_dies, - cs->nr_cores, cs->nr_threads); + die_offset = apicid_die_offset(&topo_info); if (cpu->enable_l3_cache) { encode_cache_cpuid4(env->cache_info_cpuid4.l3_cache, (1 << die_offset), cs->nr_cores, @@ -5441,14 +5618,12 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, switch (count) { case 0: - *eax = apicid_core_offset(env->nr_dies, - cs->nr_cores, cs->nr_threads); + *eax = apicid_core_offset(&topo_info); *ebx = cs->nr_threads; *ecx |= CPUID_TOPOLOGY_LEVEL_SMT; break; case 1: - *eax = apicid_pkg_offset(env->nr_dies, - cs->nr_cores, cs->nr_threads); + *eax = env->pkg_offset; *ebx = cs->nr_cores * cs->nr_threads; *ecx |= CPUID_TOPOLOGY_LEVEL_CORE; break; @@ -5472,20 +5647,17 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, *edx = cpu->apic_id; switch (count) { case 0: - *eax = apicid_core_offset(env->nr_dies, cs->nr_cores, - cs->nr_threads); + *eax = apicid_core_offset(&topo_info); *ebx = cs->nr_threads; *ecx |= CPUID_TOPOLOGY_LEVEL_SMT; break; case 1: - *eax = apicid_die_offset(env->nr_dies, cs->nr_cores, - cs->nr_threads); + *eax = apicid_die_offset(&topo_info); *ebx = cs->nr_cores * cs->nr_threads; *ecx |= CPUID_TOPOLOGY_LEVEL_CORE; break; case 2: - *eax = apicid_pkg_offset(env->nr_dies, cs->nr_cores, - cs->nr_threads); + *eax = env->pkg_offset; *ebx = env->nr_dies * cs->nr_cores * cs->nr_threads; *ecx |= CPUID_TOPOLOGY_LEVEL_DIE; break; @@ -5685,20 +5857,20 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, } switch (count) { case 0: /* L1 dcache info */ - encode_cache_cpuid8000001d(env->cache_info_amd.l1d_cache, cs, - eax, ebx, ecx, edx); + encode_cache_cpuid8000001d(env->cache_info_amd.l1d_cache, + &topo_info, eax, ebx, ecx, edx); break; case 1: /* L1 icache info */ - encode_cache_cpuid8000001d(env->cache_info_amd.l1i_cache, cs, - eax, ebx, ecx, edx); + encode_cache_cpuid8000001d(env->cache_info_amd.l1i_cache, + &topo_info, eax, ebx, ecx, edx); break; case 2: /* L2 cache info */ - encode_cache_cpuid8000001d(env->cache_info_amd.l2_cache, cs, - eax, ebx, ecx, edx); + encode_cache_cpuid8000001d(env->cache_info_amd.l2_cache, + &topo_info, eax, ebx, ecx, edx); break; case 3: /* L3 cache info */ - encode_cache_cpuid8000001d(env->cache_info_amd.l3_cache, cs, - eax, ebx, ecx, edx); + encode_cache_cpuid8000001d(env->cache_info_amd.l3_cache, + &topo_info, eax, ebx, ecx, edx); break; default: /* end of info */ *eax = *ebx = *ecx = *edx = 0; @@ -5707,8 +5879,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, break; case 0x8000001E: assert(cpu->core_id <= 255); - encode_topo_cpuid8000001e(cs, cpu, - eax, ebx, ecx, edx); + encode_topo_cpuid8000001e(&topo_info, cpu, eax, ebx, ecx, edx); break; case 0xC0000000: *eax = env->cpuid_xlevel2; @@ -5749,9 +5920,9 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, } } -/* CPUClass::reset() */ -static void x86_cpu_reset(CPUState *s) +static void x86_cpu_reset(DeviceState *dev) { + CPUState *s = CPU(dev); X86CPU *cpu = X86_CPU(s); X86CPUClass *xcc = X86_CPU_GET_CLASS(cpu); CPUX86State *env = &cpu->env; @@ -5759,7 +5930,7 @@ static void x86_cpu_reset(CPUState *s) uint64_t xcr0; int i; - xcc->parent_reset(s); + xcc->parent_reset(dev); memset(env, 0, offsetof(CPUX86State, end_reset_fields)); @@ -5936,7 +6107,7 @@ static void x86_cpu_apic_create(X86CPU *cpu, Error **errp) APICCommonState *apic; ObjectClass *apic_class = OBJECT_CLASS(apic_get_class()); - cpu->apic_state = DEVICE(object_new(object_class_get_name(apic_class))); + cpu->apic_state = DEVICE(object_new_with_class(apic_class)); object_property_add_child(OBJECT(cpu), "lapic", OBJECT(cpu->apic_state), &error_abort); @@ -6198,9 +6369,14 @@ static void x86_cpu_expand_features(X86CPU *cpu, Error **errp) x86_cpu_adjust_feat_level(cpu, FEAT_XSAVE); /* Intel Processor Trace requires CPUID[0x14] */ - if ((env->features[FEAT_7_0_EBX] & CPUID_7_0_EBX_INTEL_PT) && - kvm_enabled() && cpu->intel_pt_auto_level) { - x86_cpu_adjust_level(cpu, &cpu->env.cpuid_min_level, 0x14); + if ((env->features[FEAT_7_0_EBX] & CPUID_7_0_EBX_INTEL_PT)) { + if (cpu->intel_pt_auto_level) { + x86_cpu_adjust_level(cpu, &cpu->env.cpuid_min_level, 0x14); + } else if (cpu->env.cpuid_min_level < 0x14) { + mark_unavailable_features(cpu, FEAT_7_0_EBX, + CPUID_7_0_EBX_INTEL_PT, + "Intel PT need CPUID leaf 0x14, please set by \"-cpu ...,+intel-pt,level=0x14\""); + } } /* CPU topology with multi-dies support requires CPUID[0x1F] */ @@ -6304,17 +6480,31 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp) if (xcc->host_cpuid_required) { if (!accel_uses_host_cpuid()) { - char *name = x86_cpu_class_get_model_name(xcc); + g_autofree char *name = x86_cpu_class_get_model_name(xcc); error_setg(&local_err, "CPU model '%s' requires KVM", name); - g_free(name); goto out; } + } + if (cpu->max_features && accel_uses_host_cpuid()) { if (enable_cpu_pm) { host_cpuid(5, 0, &cpu->mwait.eax, &cpu->mwait.ebx, &cpu->mwait.ecx, &cpu->mwait.edx); env->features[FEAT_1_ECX] |= CPUID_EXT_MONITOR; } + if (kvm_enabled() && cpu->ucode_rev == 0) { + cpu->ucode_rev = kvm_arch_get_supported_msr_feature(kvm_state, + MSR_IA32_UCODE_REV); + } + } + + if (cpu->ucode_rev == 0) { + /* The default is the same as KVM's. */ + if (IS_AMD_CPU(env)) { + cpu->ucode_rev = 0x01000065; + } else { + cpu->ucode_rev = 0x100000000ULL; + } } /* mwait extended info: needed for Core compatibility */ @@ -6422,10 +6612,9 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp) /* Cache information initialization */ if (!cpu->legacy_cache) { if (!xcc->model || !xcc->model->cpudef->cache_info) { - char *name = x86_cpu_class_get_model_name(xcc); + g_autofree char *name = x86_cpu_class_get_model_name(xcc); error_setg(errp, "CPU model '%s' doesn't support legacy-cache=off", name); - g_free(name); return; } env->cache_info_cpuid2 = env->cache_info_cpuid4 = env->cache_info_amd = @@ -6711,6 +6900,7 @@ static void x86_cpu_initfn(Object *obj) FeatureWord w; env->nr_dies = 1; + env->nr_nodes = 1; cpu_set_cpustate_pointers(cpu); object_property_add(obj, "family", "int", @@ -7000,6 +7190,7 @@ static Property x86_cpu_properties[] = { DEFINE_PROP_UINT32("min-level", X86CPU, env.cpuid_min_level, 0), DEFINE_PROP_UINT32("min-xlevel", X86CPU, env.cpuid_min_xlevel, 0), DEFINE_PROP_UINT32("min-xlevel2", X86CPU, env.cpuid_min_xlevel2, 0), + DEFINE_PROP_UINT64("ucode-rev", X86CPU, ucode_rev, 0), DEFINE_PROP_BOOL("full-cpuid-auto-level", X86CPU, full_cpuid_auto_level, true), DEFINE_PROP_STRING("hv-vendor-id", X86CPU, hyperv_vendor_id), DEFINE_PROP_BOOL("cpuid-0xb", X86CPU, enable_cpuid_0xb, true), @@ -7047,10 +7238,9 @@ static void x86_cpu_common_class_init(ObjectClass *oc, void *data) &xcc->parent_realize); device_class_set_parent_unrealize(dc, x86_cpu_unrealizefn, &xcc->parent_unrealize); - dc->props = x86_cpu_properties; + device_class_set_props(dc, x86_cpu_properties); - xcc->parent_reset = cc->reset; - cc->reset = x86_cpu_reset; + device_class_set_parent_reset(dc, x86_cpu_reset, &xcc->parent_reset); cc->reset_dump_flags = CPU_DUMP_FPU | CPU_DUMP_CCOP; cc->class_by_name = x86_cpu_class_by_name; diff --git a/target/i386/cpu.h b/target/i386/cpu.h index cde2a16b94..e818fc712a 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -348,6 +348,7 @@ typedef enum X86Seg { #define MSR_IA32_SPEC_CTRL 0x48 #define MSR_VIRT_SSBD 0xc001011f #define MSR_IA32_PRED_CMD 0x49 +#define MSR_IA32_UCODE_REV 0x8b #define MSR_IA32_CORE_CAPABILITY 0xcf #define MSR_IA32_ARCH_CAPABILITIES 0x10a @@ -771,6 +772,8 @@ typedef uint64_t FeatureWordArray[FEATURE_WORDS]; #define CPUID_7_0_EDX_AVX512_4FMAPS (1U << 3) /* Speculation Control */ #define CPUID_7_0_EDX_SPEC_CTRL (1U << 26) +/* Single Thread Indirect Branch Predictors */ +#define CPUID_7_0_EDX_STIBP (1U << 27) /* Arch Capabilities */ #define CPUID_7_0_EDX_ARCH_CAPABILITIES (1U << 29) /* Core Capability */ @@ -789,6 +792,8 @@ typedef uint64_t FeatureWordArray[FEATURE_WORDS]; #define CPUID_8000_0008_EBX_WBNOINVD (1U << 9) /* Indirect Branch Prediction Barrier */ #define CPUID_8000_0008_EBX_IBPB (1U << 12) +/* Single Thread Indirect Branch Predictors */ +#define CPUID_8000_0008_EBX_STIBP (1U << 15) #define CPUID_XSAVE_XSAVEOPT (1U << 0) #define CPUID_XSAVE_XSAVEC (1U << 1) @@ -833,11 +838,15 @@ typedef uint64_t FeatureWordArray[FEATURE_WORDS]; #define CPUID_TOPOLOGY_LEVEL_DIE (5U << 8) /* MSR Feature Bits */ -#define MSR_ARCH_CAP_RDCL_NO (1U << 0) -#define MSR_ARCH_CAP_IBRS_ALL (1U << 1) -#define MSR_ARCH_CAP_RSBA (1U << 2) +#define MSR_ARCH_CAP_RDCL_NO (1U << 0) +#define MSR_ARCH_CAP_IBRS_ALL (1U << 1) +#define MSR_ARCH_CAP_RSBA (1U << 2) #define MSR_ARCH_CAP_SKIP_L1DFL_VMENTRY (1U << 3) -#define MSR_ARCH_CAP_SSB_NO (1U << 4) +#define MSR_ARCH_CAP_SSB_NO (1U << 4) +#define MSR_ARCH_CAP_MDS_NO (1U << 5) +#define MSR_ARCH_CAP_PSCHANGE_MC_NO (1U << 6) +#define MSR_ARCH_CAP_TSX_CTRL_MSR (1U << 7) +#define MSR_ARCH_CAP_TAA_NO (1U << 8) #define MSR_CORE_CAP_SPLIT_LOCK_DETECT (1U << 5) @@ -992,9 +1001,9 @@ typedef uint64_t FeatureWordArray[FEATURE_WORDS]; #define EXCP11_ALGN 17 #define EXCP12_MCHK 18 -#define EXCP_SYSCALL 0x100 /* only happens in user only emulation - for syscall instruction */ -#define EXCP_VMEXIT 0x100 +#define EXCP_VMEXIT 0x100 /* only for system emulation */ +#define EXCP_SYSCALL 0x101 /* only for user emulation */ +#define EXCP_VSYSCALL 0x102 /* only for user emulation */ /* i386-specific interrupt pending bits. */ #define CPU_INTERRUPT_POLL CPU_INTERRUPT_TGT_EXT_1 @@ -1600,6 +1609,8 @@ typedef struct CPUX86State { TPRAccess tpr_access_type; unsigned nr_dies; + unsigned nr_nodes; + unsigned pkg_offset; } CPUX86State; struct kvm_msrs; @@ -1621,6 +1632,8 @@ struct X86CPU { CPUNegativeOffsetState neg; CPUX86State env; + uint64_t ucode_rev; + uint32_t hyperv_spinlock_attempts; char *hyperv_vendor_id; bool hyperv_synic_kvm_only; @@ -1757,7 +1770,7 @@ void x86_cpu_dump_state(CPUState *cs, FILE *f, int flags); hwaddr x86_cpu_get_phys_page_attrs_debug(CPUState *cpu, vaddr addr, MemTxAttrs *attrs); -int x86_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); +int x86_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int x86_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); void x86_cpu_exec_enter(CPUState *cpu); @@ -1885,6 +1898,7 @@ void cpu_clear_apic_feature(CPUX86State *env); void host_cpuid(uint32_t function, uint32_t count, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx); void host_vendor_fms(char *vendor, int *family, int *model, int *stepping); +bool cpu_x86_use_epyc_apic_id_encoding(const char *cpu_type); /* helper.c */ bool x86_cpu_tlb_fill(CPUState *cs, vaddr address, int size, @@ -1949,9 +1963,6 @@ uint64_t cpu_get_tsc(CPUX86State *env); #define cpu_list x86_cpu_list /* MMU modes definitions */ -#define MMU_MODE0_SUFFIX _ksmap -#define MMU_MODE1_SUFFIX _user -#define MMU_MODE2_SUFFIX _knosmap /* SMAP disabled or CPL<3 && AC=1 */ #define MMU_KSMAP_IDX 0 #define MMU_USER_IDX 1 #define MMU_KNOSMAP_IDX 2 @@ -2210,4 +2221,10 @@ static inline bool hyperv_feat_enabled(X86CPU *cpu, int feat) return !!(cpu->hyperv_features & BIT(feat)); } +#if defined(TARGET_X86_64) && \ + defined(CONFIG_USER_ONLY) && \ + defined(CONFIG_LINUX) +# define TARGET_VSYSCALL_PAGE (UINT64_C(-10) << 20) +#endif + #endif /* I386_CPU_H */ diff --git a/target/i386/fpu_helper.c b/target/i386/fpu_helper.c index 99f28f267f..792a128a6d 100644 --- a/target/i386/fpu_helper.c +++ b/target/i386/fpu_helper.c @@ -991,7 +991,11 @@ void helper_fxam_ST0(CPUX86State *env) env->fpus |= 0x200; /* C1 <-- 1 */ } - /* XXX: test fptags too */ + if (env->fptags[env->fpstt]) { + env->fpus |= 0x4100; /* Empty */ + return; + } + expdif = EXPD(temp); if (expdif == MAXEXPD) { if (MANTD(temp) == 0x8000000000000000ULL) { diff --git a/target/i386/gdbstub.c b/target/i386/gdbstub.c index aef25b70f1..b98a99500a 100644 --- a/target/i386/gdbstub.c +++ b/target/i386/gdbstub.c @@ -79,7 +79,7 @@ static const int gpr_map32[8] = { 0, 1, 2, 3, 4, 5, 6, 7 }; #endif -int x86_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +int x86_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) { X86CPU *cpu = X86_CPU(cs); CPUX86State *env = &cpu->env; @@ -98,26 +98,22 @@ int x86_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) return gdb_get_reg64(mem_buf, env->regs[gpr_map[n]] & 0xffffffffUL); } else { - memset(mem_buf, 0, sizeof(target_ulong)); - return sizeof(target_ulong); + return gdb_get_regl(mem_buf, 0); } } else { return gdb_get_reg32(mem_buf, env->regs[gpr_map32[n]]); } } else if (n >= IDX_FP_REGS && n < IDX_FP_REGS + 8) { -#ifdef USE_X86LDOUBLE - /* FIXME: byteswap float values - after fixing fpregs layout. */ - memcpy(mem_buf, &env->fpregs[n - IDX_FP_REGS], 10); -#else - memset(mem_buf, 0, 10); -#endif - return 10; + floatx80 *fp = (floatx80 *) &env->fpregs[n - IDX_FP_REGS]; + int len = gdb_get_reg64(mem_buf, cpu_to_le64(fp->low)); + len += gdb_get_reg16(mem_buf, cpu_to_le16(fp->high)); + return len; } else if (n >= IDX_XMM_REGS && n < IDX_XMM_REGS + CPU_NB_REGS) { n -= IDX_XMM_REGS; if (n < CPU_NB_REGS32 || TARGET_LONG_BITS == 64) { - stq_p(mem_buf, env->xmm_regs[n].ZMM_Q(0)); - stq_p(mem_buf + 8, env->xmm_regs[n].ZMM_Q(1)); - return 16; + return gdb_get_reg128(mem_buf, + env->xmm_regs[n].ZMM_Q(0), + env->xmm_regs[n].ZMM_Q(1)); } } else { switch (n) { @@ -290,10 +286,9 @@ int x86_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) return 4; } } else if (n >= IDX_FP_REGS && n < IDX_FP_REGS + 8) { -#ifdef USE_X86LDOUBLE - /* FIXME: byteswap float values - after fixing fpregs layout. */ - memcpy(&env->fpregs[n - IDX_FP_REGS], mem_buf, 10); -#endif + floatx80 *fp = (floatx80 *) &env->fpregs[n - IDX_FP_REGS]; + fp->low = le64_to_cpu(* (uint64_t *) mem_buf); + fp->high = le16_to_cpu(* (uint16_t *) (mem_buf + 8)); return 10; } else if (n >= IDX_XMM_REGS && n < IDX_XMM_REGS + CPU_NB_REGS) { n -= IDX_XMM_REGS; @@ -350,15 +345,15 @@ int x86_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) env->segs[R_GS].base = ldl_p(mem_buf); return 4; -#ifdef TARGET_X86_64 case IDX_SEG_REGS + 8: +#ifdef TARGET_X86_64 if (env->hflags & HF_CS64_MASK) { env->kernelgsbase = ldq_p(mem_buf); return 8; } env->kernelgsbase = ldl_p(mem_buf); - return 4; #endif + return 4; case IDX_FP_REGS + 8: cpu_set_fpuc(env, ldl_p(mem_buf)); diff --git a/target/i386/hax-all.c b/target/i386/hax-all.c index a8b6e5aeb8..f9c83fff25 100644 --- a/target/i386/hax-all.c +++ b/target/i386/hax-all.c @@ -367,7 +367,7 @@ static int hax_accel_init(MachineState *ms) static int hax_handle_fastmmio(CPUArchState *env, struct hax_fastmmio *hft) { if (hft->direction < 2) { - cpu_physical_memory_rw(hft->gpa, (uint8_t *) &hft->value, hft->size, + cpu_physical_memory_rw(hft->gpa, &hft->value, hft->size, hft->direction); } else { /* @@ -376,8 +376,8 @@ static int hax_handle_fastmmio(CPUArchState *env, struct hax_fastmmio *hft) * hft->direction == 2: gpa ==> gpa2 */ uint64_t value; - cpu_physical_memory_rw(hft->gpa, (uint8_t *) &value, hft->size, 0); - cpu_physical_memory_rw(hft->gpa2, (uint8_t *) &value, hft->size, 1); + cpu_physical_memory_read(hft->gpa, &value, hft->size); + cpu_physical_memory_write(hft->gpa2, &value, hft->size); } return 0; diff --git a/target/i386/hax-posix.c b/target/i386/hax-posix.c index a5426a6dac..3bad89f133 100644 --- a/target/i386/hax-posix.c +++ b/target/i386/hax-posix.c @@ -108,41 +108,12 @@ int hax_mod_version(struct hax_state *hax, struct hax_module_version *version) static char *hax_vm_devfs_string(int vm_id) { - char *name; - - if (vm_id > MAX_VM_ID) { - fprintf(stderr, "Too big VM id\n"); - return NULL; - } - -#define HAX_VM_DEVFS "/dev/hax_vm/vmxx" - name = g_strdup(HAX_VM_DEVFS); - if (!name) { - return NULL; - } - - snprintf(name, sizeof HAX_VM_DEVFS, "/dev/hax_vm/vm%02d", vm_id); - return name; + return g_strdup_printf("/dev/hax_vm/vm%02d", vm_id); } static char *hax_vcpu_devfs_string(int vm_id, int vcpu_id) { - char *name; - - if (vm_id > MAX_VM_ID || vcpu_id > MAX_VCPU_ID) { - fprintf(stderr, "Too big vm id %x or vcpu id %x\n", vm_id, vcpu_id); - return NULL; - } - -#define HAX_VCPU_DEVFS "/dev/hax_vmxx/vcpuxx" - name = g_strdup(HAX_VCPU_DEVFS); - if (!name) { - return NULL; - } - - snprintf(name, sizeof HAX_VCPU_DEVFS, "/dev/hax_vm%02d/vcpu%02d", - vm_id, vcpu_id); - return name; + return g_strdup_printf("/dev/hax_vm%02d/vcpu%02d", vm_id, vcpu_id); } int hax_host_create_vm(struct hax_state *hax, int *vmid) diff --git a/target/i386/hax-windows.c b/target/i386/hax-windows.c index 5729ad9b48..863c2bcc19 100644 --- a/target/i386/hax-windows.c +++ b/target/i386/hax-windows.c @@ -185,41 +185,12 @@ int hax_mod_version(struct hax_state *hax, struct hax_module_version *version) static char *hax_vm_devfs_string(int vm_id) { - char *name; - - if (vm_id > MAX_VM_ID) { - fprintf(stderr, "Too big VM id\n"); - return NULL; - } - -#define HAX_VM_DEVFS "\\\\.\\hax_vmxx" - name = g_strdup(HAX_VM_DEVFS); - if (!name) { - return NULL; - } - - snprintf(name, sizeof HAX_VM_DEVFS, "\\\\.\\hax_vm%02d", vm_id); - return name; + return g_strdup_printf("\\\\.\\hax_vm%02d", vm_id); } static char *hax_vcpu_devfs_string(int vm_id, int vcpu_id) { - char *name; - - if (vm_id > MAX_VM_ID || vcpu_id > MAX_VCPU_ID) { - fprintf(stderr, "Too big vm id %x or vcpu id %x\n", vm_id, vcpu_id); - return NULL; - } - -#define HAX_VCPU_DEVFS "\\\\.\\hax_vmxx_vcpuxx" - name = g_strdup(HAX_VCPU_DEVFS); - if (!name) { - return NULL; - } - - snprintf(name, sizeof HAX_VCPU_DEVFS, "\\\\.\\hax_vm%02d_vcpu%02d", - vm_id, vcpu_id); - return name; + return g_strdup_printf("\\\\.\\hax_vm%02d_vcpu%02d", vm_id, vcpu_id); } int hax_host_create_vm(struct hax_state *hax, int *vmid) diff --git a/target/i386/hvf/vmx.h b/target/i386/hvf/vmx.h index eb8894cd58..ce2a1532d5 100644 --- a/target/i386/hvf/vmx.h +++ b/target/i386/hvf/vmx.h @@ -125,10 +125,9 @@ static inline void macvm_set_cr0(hv_vcpuid_t vcpu, uint64_t cr0) if ((cr0 & CR0_PG) && (rvmcs(vcpu, VMCS_GUEST_CR4) & CR4_PAE) && !(efer & MSR_EFER_LME)) { - address_space_rw(&address_space_memory, - rvmcs(vcpu, VMCS_GUEST_CR3) & ~0x1f, - MEMTXATTRS_UNSPECIFIED, - (uint8_t *)pdpte, 32, 0); + address_space_read(&address_space_memory, + rvmcs(vcpu, VMCS_GUEST_CR3) & ~0x1f, + MEMTXATTRS_UNSPECIFIED, pdpte, 32); /* Only set PDPTE when appropriate. */ for (i = 0; i < 4; i++) { wvmcs(vcpu, VMCS_GUEST_PDPTE0 + i * 2, pdpte[i]); @@ -168,6 +167,8 @@ static inline void macvm_set_cr4(hv_vcpuid_t vcpu, uint64_t cr4) static inline void macvm_set_rip(CPUState *cpu, uint64_t rip) { + X86CPU *x86_cpu = X86_CPU(cpu); + CPUX86State *env = &x86_cpu->env; uint64_t val; /* BUG, should take considering overlap.. */ @@ -177,6 +178,7 @@ static inline void macvm_set_rip(CPUState *cpu, uint64_t rip) val = rvmcs(cpu->hvf_fd, VMCS_GUEST_INTERRUPTIBILITY); if (val & (VMCS_INTERRUPTIBILITY_STI_BLOCKING | VMCS_INTERRUPTIBILITY_MOVSS_BLOCKING)) { + env->hflags &= ~HF_INHIBIT_IRQ_MASK; wvmcs(cpu->hvf_fd, VMCS_GUEST_INTERRUPTIBILITY, val & ~(VMCS_INTERRUPTIBILITY_STI_BLOCKING | VMCS_INTERRUPTIBILITY_MOVSS_BLOCKING)); diff --git a/target/i386/hvf/x86_emu.c b/target/i386/hvf/x86_emu.c index a3019a671d..1e1c7c226a 100644 --- a/target/i386/hvf/x86_emu.c +++ b/target/i386/hvf/x86_emu.c @@ -664,8 +664,6 @@ static void exec_lods(struct CPUX86State *env, struct x86_decode *decode) RIP(env) += decode->len; } -#define MSR_IA32_UCODE_REV 0x00000017 - void simulate_rdmsr(struct CPUState *cpu) { X86CPU *x86_cpu = X86_CPU(cpu); @@ -681,7 +679,7 @@ void simulate_rdmsr(struct CPUState *cpu) val = cpu_get_apic_base(X86_CPU(cpu)->apic_state); break; case MSR_IA32_UCODE_REV: - val = (0x100000000ULL << 32) | 0x100000000ULL; + val = x86_cpu->ucode_rev; break; case MSR_EFER: val = rvmcs(cpu->hvf_fd, VMCS_GUEST_IA32_EFER); diff --git a/target/i386/hvf/x86_mmu.c b/target/i386/hvf/x86_mmu.c index d7eb637a3e..f0c2f48cd5 100644 --- a/target/i386/hvf/x86_mmu.c +++ b/target/i386/hvf/x86_mmu.c @@ -88,8 +88,8 @@ static bool get_pt_entry(struct CPUState *cpu, struct gpt_translation *pt, } index = gpt_entry(pt->gva, level, pae); - address_space_rw(&address_space_memory, gpa + index * pte_size(pae), - MEMTXATTRS_UNSPECIFIED, (uint8_t *)&pte, pte_size(pae), 0); + address_space_read(&address_space_memory, gpa + index * pte_size(pae), + MEMTXATTRS_UNSPECIFIED, &pte, pte_size(pae)); pt->pte[level - 1] = pte; @@ -238,8 +238,8 @@ void vmx_write_mem(struct CPUState *cpu, target_ulong gva, void *data, int bytes if (!mmu_gva_to_gpa(cpu, gva, &gpa)) { VM_PANIC_EX("%s: mmu_gva_to_gpa 0x" TARGET_FMT_lx " failed\n", __func__, gva); } else { - address_space_rw(&address_space_memory, gpa, MEMTXATTRS_UNSPECIFIED, - data, copy, 1); + address_space_write(&address_space_memory, gpa, + MEMTXATTRS_UNSPECIFIED, data, copy); } bytes -= copy; @@ -259,8 +259,8 @@ void vmx_read_mem(struct CPUState *cpu, void *data, target_ulong gva, int bytes) if (!mmu_gva_to_gpa(cpu, gva, &gpa)) { VM_PANIC_EX("%s: mmu_gva_to_gpa 0x" TARGET_FMT_lx " failed\n", __func__, gva); } - address_space_rw(&address_space_memory, gpa, MEMTXATTRS_UNSPECIFIED, - data, copy, 0); + address_space_read(&address_space_memory, gpa, MEMTXATTRS_UNSPECIFIED, + data, copy); bytes -= copy; gva += copy; diff --git a/target/i386/kvm.c b/target/i386/kvm.c index 1d10046a6c..4901c6dd74 100644 --- a/target/i386/kvm.c +++ b/target/i386/kvm.c @@ -35,7 +35,7 @@ #include "qemu/main-loop.h" #include "qemu/config-file.h" #include "qemu/error-report.h" -#include "hw/i386/pc.h" +#include "hw/i386/x86.h" #include "hw/i386/apic.h" #include "hw/i386/apic_internal.h" #include "hw/i386/apic-msidef.h" @@ -67,6 +67,8 @@ * 255 kvm_msr_entry structs */ #define MSR_BUF_SIZE 4096 +static void kvm_init_msrs(X86CPU *cpu); + const KVMCapabilityInfo kvm_arch_required_capabilities[] = { KVM_CAP_INFO(SET_TSS_ADDR), KVM_CAP_INFO(EXT_CPUID), @@ -103,6 +105,8 @@ static bool has_msr_smi_count; static bool has_msr_arch_capabs; static bool has_msr_core_capabs; static bool has_msr_vmx_vmfunc; +static bool has_msr_ucode_rev; +static bool has_msr_vmx_procbased_ctls2; static uint32_t has_architectural_pmu_version; static uint32_t num_architectural_pmu_gp_counters; @@ -147,7 +151,7 @@ bool kvm_allows_irq0_override(void) static bool kvm_x2apic_api_set_flags(uint64_t flags) { - KVMState *s = KVM_STATE(current_machine->accelerator); + KVMState *s = KVM_STATE(current_accel()); return !kvm_vm_enable_cap(s, KVM_CAP_X2APIC_API, 0, flags); } @@ -487,21 +491,28 @@ uint64_t kvm_arch_get_supported_msr_feature(KVMState *s, uint32_t index) value = msr_data.entries[0].data; switch (index) { case MSR_IA32_VMX_PROCBASED_CTLS2: - /* KVM forgot to add these bits for some time, do this ourselves. */ - if (kvm_arch_get_supported_cpuid(s, 0xD, 1, R_ECX) & CPUID_XSAVE_XSAVES) { - value |= (uint64_t)VMX_SECONDARY_EXEC_XSAVES << 32; - } - if (kvm_arch_get_supported_cpuid(s, 1, 0, R_ECX) & CPUID_EXT_RDRAND) { - value |= (uint64_t)VMX_SECONDARY_EXEC_RDRAND_EXITING << 32; - } - if (kvm_arch_get_supported_cpuid(s, 7, 0, R_EBX) & CPUID_7_0_EBX_INVPCID) { - value |= (uint64_t)VMX_SECONDARY_EXEC_ENABLE_INVPCID << 32; - } - if (kvm_arch_get_supported_cpuid(s, 7, 0, R_EBX) & CPUID_7_0_EBX_RDSEED) { - value |= (uint64_t)VMX_SECONDARY_EXEC_RDSEED_EXITING << 32; - } - if (kvm_arch_get_supported_cpuid(s, 0x80000001, 0, R_EDX) & CPUID_EXT2_RDTSCP) { - value |= (uint64_t)VMX_SECONDARY_EXEC_RDTSCP << 32; + if (!has_msr_vmx_procbased_ctls2) { + /* KVM forgot to add these bits for some time, do this ourselves. */ + if (kvm_arch_get_supported_cpuid(s, 0xD, 1, R_ECX) & + CPUID_XSAVE_XSAVES) { + value |= (uint64_t)VMX_SECONDARY_EXEC_XSAVES << 32; + } + if (kvm_arch_get_supported_cpuid(s, 1, 0, R_ECX) & + CPUID_EXT_RDRAND) { + value |= (uint64_t)VMX_SECONDARY_EXEC_RDRAND_EXITING << 32; + } + if (kvm_arch_get_supported_cpuid(s, 7, 0, R_EBX) & + CPUID_7_0_EBX_INVPCID) { + value |= (uint64_t)VMX_SECONDARY_EXEC_ENABLE_INVPCID << 32; + } + if (kvm_arch_get_supported_cpuid(s, 7, 0, R_EBX) & + CPUID_7_0_EBX_RDSEED) { + value |= (uint64_t)VMX_SECONDARY_EXEC_RDSEED_EXITING << 32; + } + if (kvm_arch_get_supported_cpuid(s, 0x80000001, 0, R_EDX) & + CPUID_EXT2_RDTSCP) { + value |= (uint64_t)VMX_SECONDARY_EXEC_RDTSCP << 32; + } } /* fall through */ case MSR_IA32_VMX_TRUE_PINBASED_CTLS: @@ -1842,6 +1853,8 @@ int kvm_arch_init_vcpu(CPUState *cs) has_msr_tsc_aux = false; } + kvm_init_msrs(cpu); + r = hyperv_init_vcpu(cpu); if (r) { goto fail; @@ -2052,6 +2065,12 @@ static int kvm_get_supported_msrs(KVMState *s) case MSR_IA32_VMX_VMFUNC: has_msr_vmx_vmfunc = true; break; + case MSR_IA32_UCODE_REV: + has_msr_ucode_rev = true; + break; + case MSR_IA32_VMX_PROCBASED_CTLS2: + has_msr_vmx_procbased_ctls2 = true; + break; } } } @@ -2163,7 +2182,7 @@ int kvm_arch_init(MachineState *ms, KVMState *s) } qemu_register_reset(kvm_unpoison_all, NULL); - shadow_mem = machine_kvm_shadow_mem(ms); + shadow_mem = object_property_get_int(OBJECT(s), "kvm-shadow-mem", &error_abort); if (shadow_mem != -1) { shadow_mem /= 4096; ret = kvm_vm_ioctl(s, KVM_SET_NR_MMU_PAGES, shadow_mem); @@ -2173,8 +2192,8 @@ int kvm_arch_init(MachineState *ms, KVMState *s) } if (kvm_check_extension(s, KVM_CAP_X86_SMM) && - object_dynamic_cast(OBJECT(ms), TYPE_PC_MACHINE) && - pc_machine_is_smm_enabled(PC_MACHINE(ms))) { + object_dynamic_cast(OBJECT(ms), TYPE_X86_MACHINE) && + x86_machine_is_smm_enabled(X86_MACHINE(ms))) { smram_machine_done.notify = register_smram_listener; qemu_add_machine_init_done_notifier(&smram_machine_done); } @@ -2660,11 +2679,57 @@ static void kvm_msr_entry_add_vmx(X86CPU *cpu, FeatureWordArray f) VMCS12_MAX_FIELD_INDEX << 1); } +static int kvm_buf_set_msrs(X86CPU *cpu) +{ + int ret = kvm_vcpu_ioctl(CPU(cpu), KVM_SET_MSRS, cpu->kvm_msr_buf); + if (ret < 0) { + return ret; + } + + if (ret < cpu->kvm_msr_buf->nmsrs) { + struct kvm_msr_entry *e = &cpu->kvm_msr_buf->entries[ret]; + error_report("error: failed to set MSR 0x%" PRIx32 " to 0x%" PRIx64, + (uint32_t)e->index, (uint64_t)e->data); + } + + assert(ret == cpu->kvm_msr_buf->nmsrs); + return 0; +} + +static void kvm_init_msrs(X86CPU *cpu) +{ + CPUX86State *env = &cpu->env; + + kvm_msr_buf_reset(cpu); + if (has_msr_arch_capabs) { + kvm_msr_entry_add(cpu, MSR_IA32_ARCH_CAPABILITIES, + env->features[FEAT_ARCH_CAPABILITIES]); + } + + if (has_msr_core_capabs) { + kvm_msr_entry_add(cpu, MSR_IA32_CORE_CAPABILITY, + env->features[FEAT_CORE_CAPABILITY]); + } + + if (has_msr_ucode_rev) { + kvm_msr_entry_add(cpu, MSR_IA32_UCODE_REV, cpu->ucode_rev); + } + + /* + * Older kernels do not include VMX MSRs in KVM_GET_MSR_INDEX_LIST, but + * all kernels with MSR features should have them. + */ + if (kvm_feature_msrs && cpu_has_vmx(env)) { + kvm_msr_entry_add_vmx(cpu, env->features); + } + + assert(kvm_buf_set_msrs(cpu) == 0); +} + static int kvm_put_msrs(X86CPU *cpu, int level) { CPUX86State *env = &cpu->env; int i; - int ret; kvm_msr_buf_reset(cpu); @@ -2722,17 +2787,6 @@ static int kvm_put_msrs(X86CPU *cpu, int level) } #endif - /* If host supports feature MSR, write down. */ - if (has_msr_arch_capabs) { - kvm_msr_entry_add(cpu, MSR_IA32_ARCH_CAPABILITIES, - env->features[FEAT_ARCH_CAPABILITIES]); - } - - if (has_msr_core_capabs) { - kvm_msr_entry_add(cpu, MSR_IA32_CORE_CAPABILITY, - env->features[FEAT_CORE_CAPABILITY]); - } - /* * The following MSRs have side effects on the guest or are too heavy * for normal writeback. Limit them to reset or full state updates. @@ -2910,14 +2964,6 @@ static int kvm_put_msrs(X86CPU *cpu, int level) /* Note: MSR_IA32_FEATURE_CONTROL is written separately, see * kvm_put_msr_feature_control. */ - - /* - * Older kernels do not include VMX MSRs in KVM_GET_MSR_INDEX_LIST, but - * all kernels with MSR features should have them. - */ - if (kvm_feature_msrs && cpu_has_vmx(env)) { - kvm_msr_entry_add_vmx(cpu, env->features); - } } if (env->mcg_cap) { @@ -2933,19 +2979,7 @@ static int kvm_put_msrs(X86CPU *cpu, int level) } } - ret = kvm_vcpu_ioctl(CPU(cpu), KVM_SET_MSRS, cpu->kvm_msr_buf); - if (ret < 0) { - return ret; - } - - if (ret < cpu->kvm_msr_buf->nmsrs) { - struct kvm_msr_entry *e = &cpu->kvm_msr_buf->entries[ret]; - error_report("error: failed to set MSR 0x%" PRIx32 " to 0x%" PRIx64, - (uint32_t)e->index, (uint64_t)e->data); - } - - assert(ret == cpu->kvm_msr_buf->nmsrs); - return 0; + return kvm_buf_set_msrs(cpu); } @@ -4494,10 +4528,10 @@ void kvm_arch_init_irq_routing(KVMState *s) } } -int kvm_arch_irqchip_create(MachineState *ms, KVMState *s) +int kvm_arch_irqchip_create(KVMState *s) { int ret; - if (machine_kernel_irqchip_split(ms)) { + if (kvm_kernel_irqchip_split()) { ret = kvm_vm_enable_cap(s, KVM_CAP_SPLIT_IRQCHIP, 0, 24); if (ret) { error_report("Could not enable split irqchip mode: %s", @@ -4513,146 +4547,6 @@ int kvm_arch_irqchip_create(MachineState *ms, KVMState *s) } } -/* Classic KVM device assignment interface. Will remain x86 only. */ -int kvm_device_pci_assign(KVMState *s, PCIHostDeviceAddress *dev_addr, - uint32_t flags, uint32_t *dev_id) -{ - struct kvm_assigned_pci_dev dev_data = { - .segnr = dev_addr->domain, - .busnr = dev_addr->bus, - .devfn = PCI_DEVFN(dev_addr->slot, dev_addr->function), - .flags = flags, - }; - int ret; - - dev_data.assigned_dev_id = - (dev_addr->domain << 16) | (dev_addr->bus << 8) | dev_data.devfn; - - ret = kvm_vm_ioctl(s, KVM_ASSIGN_PCI_DEVICE, &dev_data); - if (ret < 0) { - return ret; - } - - *dev_id = dev_data.assigned_dev_id; - - return 0; -} - -int kvm_device_pci_deassign(KVMState *s, uint32_t dev_id) -{ - struct kvm_assigned_pci_dev dev_data = { - .assigned_dev_id = dev_id, - }; - - return kvm_vm_ioctl(s, KVM_DEASSIGN_PCI_DEVICE, &dev_data); -} - -static int kvm_assign_irq_internal(KVMState *s, uint32_t dev_id, - uint32_t irq_type, uint32_t guest_irq) -{ - struct kvm_assigned_irq assigned_irq = { - .assigned_dev_id = dev_id, - .guest_irq = guest_irq, - .flags = irq_type, - }; - - if (kvm_check_extension(s, KVM_CAP_ASSIGN_DEV_IRQ)) { - return kvm_vm_ioctl(s, KVM_ASSIGN_DEV_IRQ, &assigned_irq); - } else { - return kvm_vm_ioctl(s, KVM_ASSIGN_IRQ, &assigned_irq); - } -} - -int kvm_device_intx_assign(KVMState *s, uint32_t dev_id, bool use_host_msi, - uint32_t guest_irq) -{ - uint32_t irq_type = KVM_DEV_IRQ_GUEST_INTX | - (use_host_msi ? KVM_DEV_IRQ_HOST_MSI : KVM_DEV_IRQ_HOST_INTX); - - return kvm_assign_irq_internal(s, dev_id, irq_type, guest_irq); -} - -int kvm_device_intx_set_mask(KVMState *s, uint32_t dev_id, bool masked) -{ - struct kvm_assigned_pci_dev dev_data = { - .assigned_dev_id = dev_id, - .flags = masked ? KVM_DEV_ASSIGN_MASK_INTX : 0, - }; - - return kvm_vm_ioctl(s, KVM_ASSIGN_SET_INTX_MASK, &dev_data); -} - -static int kvm_deassign_irq_internal(KVMState *s, uint32_t dev_id, - uint32_t type) -{ - struct kvm_assigned_irq assigned_irq = { - .assigned_dev_id = dev_id, - .flags = type, - }; - - return kvm_vm_ioctl(s, KVM_DEASSIGN_DEV_IRQ, &assigned_irq); -} - -int kvm_device_intx_deassign(KVMState *s, uint32_t dev_id, bool use_host_msi) -{ - return kvm_deassign_irq_internal(s, dev_id, KVM_DEV_IRQ_GUEST_INTX | - (use_host_msi ? KVM_DEV_IRQ_HOST_MSI : KVM_DEV_IRQ_HOST_INTX)); -} - -int kvm_device_msi_assign(KVMState *s, uint32_t dev_id, int virq) -{ - return kvm_assign_irq_internal(s, dev_id, KVM_DEV_IRQ_HOST_MSI | - KVM_DEV_IRQ_GUEST_MSI, virq); -} - -int kvm_device_msi_deassign(KVMState *s, uint32_t dev_id) -{ - return kvm_deassign_irq_internal(s, dev_id, KVM_DEV_IRQ_GUEST_MSI | - KVM_DEV_IRQ_HOST_MSI); -} - -bool kvm_device_msix_supported(KVMState *s) -{ - /* The kernel lacks a corresponding KVM_CAP, so we probe by calling - * KVM_ASSIGN_SET_MSIX_NR with an invalid parameter. */ - return kvm_vm_ioctl(s, KVM_ASSIGN_SET_MSIX_NR, NULL) == -EFAULT; -} - -int kvm_device_msix_init_vectors(KVMState *s, uint32_t dev_id, - uint32_t nr_vectors) -{ - struct kvm_assigned_msix_nr msix_nr = { - .assigned_dev_id = dev_id, - .entry_nr = nr_vectors, - }; - - return kvm_vm_ioctl(s, KVM_ASSIGN_SET_MSIX_NR, &msix_nr); -} - -int kvm_device_msix_set_vector(KVMState *s, uint32_t dev_id, uint32_t vector, - int virq) -{ - struct kvm_assigned_msix_entry msix_entry = { - .assigned_dev_id = dev_id, - .gsi = virq, - .entry = vector, - }; - - return kvm_vm_ioctl(s, KVM_ASSIGN_SET_MSIX_ENTRY, &msix_entry); -} - -int kvm_device_msix_assign(KVMState *s, uint32_t dev_id) -{ - return kvm_assign_irq_internal(s, dev_id, KVM_DEV_IRQ_HOST_MSIX | - KVM_DEV_IRQ_GUEST_MSIX, 0); -} - -int kvm_device_msix_deassign(KVMState *s, uint32_t dev_id) -{ - return kvm_deassign_irq_internal(s, dev_id, KVM_DEV_IRQ_GUEST_MSIX | - KVM_DEV_IRQ_HOST_MSIX); -} - int kvm_arch_fixup_msi_route(struct kvm_irq_routing_entry *route, uint64_t address, uint32_t data, PCIDevice *dev) { diff --git a/target/i386/kvm_i386.h b/target/i386/kvm_i386.h index 06fe06bdb3..00bde7acaf 100644 --- a/target/i386/kvm_i386.h +++ b/target/i386/kvm_i386.h @@ -40,30 +40,11 @@ void kvm_synchronize_all_tsc(void); void kvm_arch_reset_vcpu(X86CPU *cs); void kvm_arch_do_init_vcpu(X86CPU *cs); -int kvm_device_pci_assign(KVMState *s, PCIHostDeviceAddress *dev_addr, - uint32_t flags, uint32_t *dev_id); -int kvm_device_pci_deassign(KVMState *s, uint32_t dev_id); - -int kvm_device_intx_assign(KVMState *s, uint32_t dev_id, - bool use_host_msi, uint32_t guest_irq); -int kvm_device_intx_set_mask(KVMState *s, uint32_t dev_id, bool masked); -int kvm_device_intx_deassign(KVMState *s, uint32_t dev_id, bool use_host_msi); - -int kvm_device_msi_assign(KVMState *s, uint32_t dev_id, int virq); -int kvm_device_msi_deassign(KVMState *s, uint32_t dev_id); - -bool kvm_device_msix_supported(KVMState *s); -int kvm_device_msix_init_vectors(KVMState *s, uint32_t dev_id, - uint32_t nr_vectors); -int kvm_device_msix_set_vector(KVMState *s, uint32_t dev_id, uint32_t vector, - int virq); -int kvm_device_msix_assign(KVMState *s, uint32_t dev_id); -int kvm_device_msix_deassign(KVMState *s, uint32_t dev_id); - void kvm_put_apicbase(X86CPU *cpu, uint64_t value); bool kvm_enable_x2apic(void); bool kvm_has_x2apic_api(void); bool kvm_hv_vpindex_settable(void); + #endif diff --git a/target/i386/machine.c b/target/i386/machine.c index 2699eed94e..0c96531a56 100644 --- a/target/i386/machine.c +++ b/target/i386/machine.c @@ -1,10 +1,10 @@ #include "qemu/osdep.h" #include "cpu.h" #include "exec/exec-all.h" -#include "hw/i386/pc.h" #include "hw/isa/isa.h" #include "migration/cpu.h" #include "hyperv.h" +#include "hw/i386/x86.h" #include "kvm_i386.h" #include "sysemu/kvm.h" @@ -261,7 +261,7 @@ static int cpu_pre_save(void *opaque) * intercepted anymore. * * Furthermore, when a L2 exception is intercepted by L1 - * hypervisor, it's exception payload (CR2/DR6 on #PF/#DB) + * hypervisor, its exception payload (CR2/DR6 on #PF/#DB) * should not be set yet in the respective vCPU register. * Thus, in case an exception is pending, it is * important to save the exception payload seperately. @@ -271,9 +271,9 @@ static int cpu_pre_save(void *opaque) * distinguish between a pending and injected exception * and we don't need to store seperately the exception payload. * - * In order to preserve better backwards-compatabile migration, + * In order to preserve better backwards-compatible migration, * convert a pending exception to an injected exception in - * case it is not important to distingiush between them + * case it is not important to distinguish between them * as described above. */ if (env->exception_pending && !(env->hflags & HF_GUEST_MASK)) { @@ -415,7 +415,7 @@ static bool exception_info_needed(void *opaque) /* * It is important to save exception-info only in case - * we need to distingiush between a pending and injected + * we need to distinguish between a pending and injected * exception. Which is only required in case there is a * pending exception and vCPU is running L2. * For more info, refer to comment in cpu_pre_save(). diff --git a/target/i386/mem_helper.c b/target/i386/mem_helper.c index d50d4b0c40..acf41f8885 100644 --- a/target/i386/mem_helper.c +++ b/target/i386/mem_helper.c @@ -24,7 +24,7 @@ #include "exec/cpu_ldst.h" #include "qemu/int128.h" #include "qemu/atomic128.h" -#include "tcg.h" +#include "tcg/tcg.h" void helper_cmpxchg8b_unlocked(CPUX86State *env, target_ulong a0) { diff --git a/target/i386/misc_helper.c b/target/i386/misc_helper.c index 3eff6885f8..7d61221024 100644 --- a/target/i386/misc_helper.c +++ b/target/i386/misc_helper.c @@ -389,6 +389,7 @@ void helper_wrmsr(CPUX86State *env) void helper_rdmsr(CPUX86State *env) { + X86CPU *x86_cpu = env_archcpu(env); uint64_t val; cpu_svm_check_intercept_param(env, SVM_EXIT_MSR, 0, GETPC()); @@ -522,6 +523,9 @@ void helper_rdmsr(CPUX86State *env) case MSR_IA32_BNDCFGS: val = env->msr_bndcfgs; break; + case MSR_IA32_UCODE_REV: + val = x86_cpu->ucode_rev; + break; default: if ((uint32_t)env->regs[R_ECX] >= MSR_MC0_CTL && (uint32_t)env->regs[R_ECX] < MSR_MC0_CTL + diff --git a/target/i386/monitor.c b/target/i386/monitor.c index 9fb4d641d5..27ebfa3ad2 100644 --- a/target/i386/monitor.c +++ b/target/i386/monitor.c @@ -28,7 +28,6 @@ #include "monitor/hmp-target.h" #include "monitor/hmp.h" #include "qapi/qmp/qdict.h" -#include "hw/i386/pc.h" #include "sysemu/kvm.h" #include "sysemu/sev.h" #include "qapi/error.h" diff --git a/target/i386/seg_helper.c b/target/i386/seg_helper.c index 87a627f9dc..b96de068ca 100644 --- a/target/i386/seg_helper.c +++ b/target/i386/seg_helper.c @@ -37,37 +37,37 @@ # define LOG_PCALL_STATE(cpu) do { } while (0) #endif -#ifdef CONFIG_USER_ONLY -#define MEMSUFFIX _kernel -#define DATA_SIZE 1 -#include "exec/cpu_ldst_useronly_template.h" +/* + * TODO: Convert callers to compute cpu_mmu_index_kernel once + * and use *_mmuidx_ra directly. + */ +#define cpu_ldub_kernel_ra(e, p, r) \ + cpu_ldub_mmuidx_ra(e, p, cpu_mmu_index_kernel(e), r) +#define cpu_lduw_kernel_ra(e, p, r) \ + cpu_lduw_mmuidx_ra(e, p, cpu_mmu_index_kernel(e), r) +#define cpu_ldl_kernel_ra(e, p, r) \ + cpu_ldl_mmuidx_ra(e, p, cpu_mmu_index_kernel(e), r) +#define cpu_ldq_kernel_ra(e, p, r) \ + cpu_ldq_mmuidx_ra(e, p, cpu_mmu_index_kernel(e), r) -#define DATA_SIZE 2 -#include "exec/cpu_ldst_useronly_template.h" +#define cpu_stb_kernel_ra(e, p, v, r) \ + cpu_stb_mmuidx_ra(e, p, v, cpu_mmu_index_kernel(e), r) +#define cpu_stw_kernel_ra(e, p, v, r) \ + cpu_stw_mmuidx_ra(e, p, v, cpu_mmu_index_kernel(e), r) +#define cpu_stl_kernel_ra(e, p, v, r) \ + cpu_stl_mmuidx_ra(e, p, v, cpu_mmu_index_kernel(e), r) +#define cpu_stq_kernel_ra(e, p, v, r) \ + cpu_stq_mmuidx_ra(e, p, v, cpu_mmu_index_kernel(e), r) -#define DATA_SIZE 4 -#include "exec/cpu_ldst_useronly_template.h" +#define cpu_ldub_kernel(e, p) cpu_ldub_kernel_ra(e, p, 0) +#define cpu_lduw_kernel(e, p) cpu_lduw_kernel_ra(e, p, 0) +#define cpu_ldl_kernel(e, p) cpu_ldl_kernel_ra(e, p, 0) +#define cpu_ldq_kernel(e, p) cpu_ldq_kernel_ra(e, p, 0) -#define DATA_SIZE 8 -#include "exec/cpu_ldst_useronly_template.h" -#undef MEMSUFFIX -#else -#define CPU_MMU_INDEX (cpu_mmu_index_kernel(env)) -#define MEMSUFFIX _kernel -#define DATA_SIZE 1 -#include "exec/cpu_ldst_template.h" - -#define DATA_SIZE 2 -#include "exec/cpu_ldst_template.h" - -#define DATA_SIZE 4 -#include "exec/cpu_ldst_template.h" - -#define DATA_SIZE 8 -#include "exec/cpu_ldst_template.h" -#undef CPU_MMU_INDEX -#undef MEMSUFFIX -#endif +#define cpu_stb_kernel(e, p, v) cpu_stb_kernel_ra(e, p, v, 0) +#define cpu_stw_kernel(e, p, v) cpu_stw_kernel_ra(e, p, v, 0) +#define cpu_stl_kernel(e, p, v) cpu_stl_kernel_ra(e, p, v, 0) +#define cpu_stq_kernel(e, p, v) cpu_stq_kernel_ra(e, p, v, 0) /* return non zero if error */ static inline int load_segment_ra(CPUX86State *env, uint32_t *e1_ptr, diff --git a/target/i386/sev.c b/target/i386/sev.c index 024bb24e51..846018a12d 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -266,94 +266,6 @@ qsev_guest_class_init(ObjectClass *oc, void *data) "guest owners session parameters (encoded with base64)", NULL); } -static void -qsev_guest_set_handle(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - QSevGuestInfo *sev = QSEV_GUEST_INFO(obj); - uint32_t value; - - visit_type_uint32(v, name, &value, errp); - sev->handle = value; -} - -static void -qsev_guest_set_policy(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - QSevGuestInfo *sev = QSEV_GUEST_INFO(obj); - uint32_t value; - - visit_type_uint32(v, name, &value, errp); - sev->policy = value; -} - -static void -qsev_guest_set_cbitpos(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - QSevGuestInfo *sev = QSEV_GUEST_INFO(obj); - uint32_t value; - - visit_type_uint32(v, name, &value, errp); - sev->cbitpos = value; -} - -static void -qsev_guest_set_reduced_phys_bits(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - QSevGuestInfo *sev = QSEV_GUEST_INFO(obj); - uint32_t value; - - visit_type_uint32(v, name, &value, errp); - sev->reduced_phys_bits = value; -} - -static void -qsev_guest_get_policy(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - uint32_t value; - QSevGuestInfo *sev = QSEV_GUEST_INFO(obj); - - value = sev->policy; - visit_type_uint32(v, name, &value, errp); -} - -static void -qsev_guest_get_handle(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - uint32_t value; - QSevGuestInfo *sev = QSEV_GUEST_INFO(obj); - - value = sev->handle; - visit_type_uint32(v, name, &value, errp); -} - -static void -qsev_guest_get_cbitpos(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - uint32_t value; - QSevGuestInfo *sev = QSEV_GUEST_INFO(obj); - - value = sev->cbitpos; - visit_type_uint32(v, name, &value, errp); -} - -static void -qsev_guest_get_reduced_phys_bits(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - uint32_t value; - QSevGuestInfo *sev = QSEV_GUEST_INFO(obj); - - value = sev->reduced_phys_bits; - visit_type_uint32(v, name, &value, errp); -} - static void qsev_guest_init(Object *obj) { @@ -361,15 +273,15 @@ qsev_guest_init(Object *obj) sev->sev_device = g_strdup(DEFAULT_SEV_DEVICE); sev->policy = DEFAULT_GUEST_POLICY; - object_property_add(obj, "policy", "uint32", qsev_guest_get_policy, - qsev_guest_set_policy, NULL, NULL, NULL); - object_property_add(obj, "handle", "uint32", qsev_guest_get_handle, - qsev_guest_set_handle, NULL, NULL, NULL); - object_property_add(obj, "cbitpos", "uint32", qsev_guest_get_cbitpos, - qsev_guest_set_cbitpos, NULL, NULL, NULL); - object_property_add(obj, "reduced-phys-bits", "uint32", - qsev_guest_get_reduced_phys_bits, - qsev_guest_set_reduced_phys_bits, NULL, NULL, NULL); + object_property_add_uint32_ptr(obj, "policy", &sev->policy, + OBJ_PROP_FLAG_READWRITE, NULL); + object_property_add_uint32_ptr(obj, "handle", &sev->handle, + OBJ_PROP_FLAG_READWRITE, NULL); + object_property_add_uint32_ptr(obj, "cbitpos", &sev->cbitpos, + OBJ_PROP_FLAG_READWRITE, NULL); + object_property_add_uint32_ptr(obj, "reduced-phys-bits", + &sev->reduced_phys_bits, + OBJ_PROP_FLAG_READWRITE, NULL); } /* sev guest info */ diff --git a/target/i386/translate.c b/target/i386/translate.c index 77e932d827..5e5dbb41b0 100644 --- a/target/i386/translate.c +++ b/target/i386/translate.c @@ -22,7 +22,7 @@ #include "cpu.h" #include "disas/disas.h" #include "exec/exec-all.h" -#include "tcg-op.h" +#include "tcg/tcg-op.h" #include "exec/cpu_ldst.h" #include "exec/translator.h" @@ -2502,14 +2502,15 @@ static void gen_unknown_opcode(CPUX86State *env, DisasContext *s) gen_illegal_opcode(s); if (qemu_loglevel_mask(LOG_UNIMP)) { + FILE *logfile = qemu_log_lock(); target_ulong pc = s->pc_start, end = s->pc; - qemu_log_lock(); + qemu_log("ILLOPC: " TARGET_FMT_lx ":", pc); for (; pc < end; ++pc) { qemu_log(" %02x", cpu_ldub_code(env, pc)); } qemu_log("\n"); - qemu_log_unlock(); + qemu_log_unlock(logfile); } } @@ -8554,7 +8555,19 @@ static bool i386_tr_breakpoint_check(DisasContextBase *dcbase, CPUState *cpu, static void i386_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) { DisasContext *dc = container_of(dcbase, DisasContext, base); - target_ulong pc_next = disas_insn(dc, cpu); + target_ulong pc_next; + +#ifdef TARGET_VSYSCALL_PAGE + /* + * Detect entry into the vsyscall page and invoke the syscall. + */ + if ((dc->base.pc_next & TARGET_PAGE_MASK) == TARGET_VSYSCALL_PAGE) { + gen_exception(dc, EXCP_VSYSCALL, dc->base.pc_next); + return; + } +#endif + + pc_next = disas_insn(dc, cpu); if (dc->tf || (dc->base.tb->flags & HF_INHIBIT_IRQ_MASK)) { /* if single step mode, we generate only one instruction and diff --git a/target/i386/whp-dispatch.h b/target/i386/whp-dispatch.h index 23791fbb47..e4695c349f 100644 --- a/target/i386/whp-dispatch.h +++ b/target/i386/whp-dispatch.h @@ -23,6 +23,12 @@ X(HRESULT, WHvGetVirtualProcessorRegisters, (WHV_PARTITION_HANDLE Partition, UINT32 VpIndex, const WHV_REGISTER_NAME* RegisterNames, UINT32 RegisterCount, WHV_REGISTER_VALUE* RegisterValues)) \ X(HRESULT, WHvSetVirtualProcessorRegisters, (WHV_PARTITION_HANDLE Partition, UINT32 VpIndex, const WHV_REGISTER_NAME* RegisterNames, UINT32 RegisterCount, const WHV_REGISTER_VALUE* RegisterValues)) \ +/* + * These are supplemental functions that may not be present + * on all versions and are not critical for basic functionality. + */ +#define LIST_WINHVPLATFORM_FUNCTIONS_SUPPLEMENTAL(X) \ + X(HRESULT, WHvSuspendPartitionTime, (WHV_PARTITION_HANDLE Partition)) \ #define LIST_WINHVEMULATION_FUNCTIONS(X) \ X(HRESULT, WHvEmulatorCreateEmulator, (const WHV_EMULATOR_CALLBACKS* Callbacks, WHV_EMULATOR_HANDLE* Emulator)) \ @@ -40,15 +46,22 @@ /* Define function typedef */ LIST_WINHVPLATFORM_FUNCTIONS(WHP_DEFINE_TYPE) LIST_WINHVEMULATION_FUNCTIONS(WHP_DEFINE_TYPE) +LIST_WINHVPLATFORM_FUNCTIONS_SUPPLEMENTAL(WHP_DEFINE_TYPE) struct WHPDispatch { LIST_WINHVPLATFORM_FUNCTIONS(WHP_DECLARE_MEMBER) LIST_WINHVEMULATION_FUNCTIONS(WHP_DECLARE_MEMBER) + LIST_WINHVPLATFORM_FUNCTIONS_SUPPLEMENTAL(WHP_DECLARE_MEMBER) }; extern struct WHPDispatch whp_dispatch; bool init_whp_dispatch(void); +typedef enum WHPFunctionList { + WINHV_PLATFORM_FNS_DEFAULT, + WINHV_EMULATION_FNS_DEFAULT, + WINHV_PLATFORM_FNS_SUPPLEMENTAL +} WHPFunctionList; #endif /* WHP_DISPATCH_H */ diff --git a/target/i386/whpx-all.c b/target/i386/whpx-all.c index def0c28480..c78baac6df 100644 --- a/target/i386/whpx-all.c +++ b/target/i386/whpx-all.c @@ -114,7 +114,6 @@ static const WHV_REGISTER_NAME whpx_register_names[] = { WHvX64RegisterXmmControlStatus, /* X64 MSRs */ - WHvX64RegisterTsc, WHvX64RegisterEfer, #ifdef TARGET_X86_64 WHvX64RegisterKernelGsBase, @@ -215,7 +214,44 @@ static SegmentCache whpx_seg_h2q(const WHV_X64_SEGMENT_REGISTER *hs) return qs; } -static void whpx_set_registers(CPUState *cpu) +static int whpx_set_tsc(CPUState *cpu) +{ + struct CPUX86State *env = (CPUArchState *)(cpu->env_ptr); + WHV_REGISTER_NAME tsc_reg = WHvX64RegisterTsc; + WHV_REGISTER_VALUE tsc_val; + HRESULT hr; + struct whpx_state *whpx = &whpx_global; + + /* + * Suspend the partition prior to setting the TSC to reduce the variance + * in TSC across vCPUs. When the first vCPU runs post suspend, the + * partition is automatically resumed. + */ + if (whp_dispatch.WHvSuspendPartitionTime) { + + /* + * Unable to suspend partition while setting TSC is not a fatal + * error. It just increases the likelihood of TSC variance between + * vCPUs and some guest OS are able to handle that just fine. + */ + hr = whp_dispatch.WHvSuspendPartitionTime(whpx->partition); + if (FAILED(hr)) { + warn_report("WHPX: Failed to suspend partition, hr=%08lx", hr); + } + } + + tsc_val.Reg64 = env->tsc; + hr = whp_dispatch.WHvSetVirtualProcessorRegisters( + whpx->partition, cpu->cpu_index, &tsc_reg, 1, &tsc_val); + if (FAILED(hr)) { + error_report("WHPX: Failed to set TSC, hr=%08lx", hr); + return -1; + } + + return 0; +} + +static void whpx_set_registers(CPUState *cpu, int level) { struct whpx_state *whpx = &whpx_global; struct whpx_vcpu *vcpu = get_whpx_vcpu(cpu); @@ -230,6 +266,14 @@ static void whpx_set_registers(CPUState *cpu) assert(cpu_is_stopped(cpu) || qemu_cpu_is_self(cpu)); + /* + * Following MSRs have side effects on the guest or are too heavy for + * runtime. Limit them to full state update. + */ + if (level >= WHPX_SET_RESET_STATE) { + whpx_set_tsc(cpu); + } + memset(&vcxt, 0, sizeof(struct whpx_register_set)); v86 = (env->eflags & VM_MASK); @@ -330,8 +374,6 @@ static void whpx_set_registers(CPUState *cpu) idx += 1; /* MSRs */ - assert(whpx_register_names[idx] == WHvX64RegisterTsc); - vcxt.values[idx++].Reg64 = env->tsc; assert(whpx_register_names[idx] == WHvX64RegisterEfer); vcxt.values[idx++].Reg64 = env->efer; #ifdef TARGET_X86_64 @@ -379,6 +421,25 @@ static void whpx_set_registers(CPUState *cpu) return; } +static int whpx_get_tsc(CPUState *cpu) +{ + struct CPUX86State *env = (CPUArchState *)(cpu->env_ptr); + WHV_REGISTER_NAME tsc_reg = WHvX64RegisterTsc; + WHV_REGISTER_VALUE tsc_val; + HRESULT hr; + struct whpx_state *whpx = &whpx_global; + + hr = whp_dispatch.WHvGetVirtualProcessorRegisters( + whpx->partition, cpu->cpu_index, &tsc_reg, 1, &tsc_val); + if (FAILED(hr)) { + error_report("WHPX: Failed to get TSC, hr=%08lx", hr); + return -1; + } + + env->tsc = tsc_val.Reg64; + return 0; +} + static void whpx_get_registers(CPUState *cpu) { struct whpx_state *whpx = &whpx_global; @@ -394,6 +455,11 @@ static void whpx_get_registers(CPUState *cpu) assert(cpu_is_stopped(cpu) || qemu_cpu_is_self(cpu)); + if (!env->tsc_valid) { + whpx_get_tsc(cpu); + env->tsc_valid = !runstate_is_running(); + } + hr = whp_dispatch.WHvGetVirtualProcessorRegisters( whpx->partition, cpu->cpu_index, whpx_register_names, @@ -492,8 +558,6 @@ static void whpx_get_registers(CPUState *cpu) idx += 1; /* MSRs */ - assert(whpx_register_names[idx] == WHvX64RegisterTsc); - env->tsc = vcxt.values[idx++].Reg64; assert(whpx_register_names[idx] == WHvX64RegisterEfer); env->efer = vcxt.values[idx++].Reg64; #ifdef TARGET_X86_64 @@ -511,7 +575,7 @@ static void whpx_get_registers(CPUState *cpu) /* WHvX64RegisterPat - Skipped */ assert(whpx_register_names[idx] == WHvX64RegisterSysenterCs); - env->sysenter_cs = vcxt.values[idx++].Reg64;; + env->sysenter_cs = vcxt.values[idx++].Reg64; assert(whpx_register_names[idx] == WHvX64RegisterSysenterEip); env->sysenter_eip = vcxt.values[idx++].Reg64; assert(whpx_register_names[idx] == WHvX64RegisterSysenterEsp); @@ -540,7 +604,7 @@ static HRESULT CALLBACK whpx_emu_ioport_callback( { MemTxAttrs attrs = { 0 }; address_space_rw(&address_space_io, IoAccess->Port, attrs, - (uint8_t *)&IoAccess->Data, IoAccess->AccessSize, + &IoAccess->Data, IoAccess->AccessSize, IoAccess->Direction); return S_OK; } @@ -841,9 +905,8 @@ static void whpx_vcpu_process_async_events(CPUState *cpu) if ((cpu->interrupt_request & CPU_INTERRUPT_INIT) && !(env->hflags & HF_SMM_MASK)) { - + whpx_cpu_synchronize_state(cpu); do_cpu_init(x86_cpu); - cpu->vcpu_dirty = true; vcpu->interruptable = true; } @@ -859,17 +922,13 @@ static void whpx_vcpu_process_async_events(CPUState *cpu) } if (cpu->interrupt_request & CPU_INTERRUPT_SIPI) { - if (!cpu->vcpu_dirty) { - whpx_get_registers(cpu); - } + whpx_cpu_synchronize_state(cpu); do_cpu_sipi(x86_cpu); } if (cpu->interrupt_request & CPU_INTERRUPT_TPR) { cpu->interrupt_request &= ~CPU_INTERRUPT_TPR; - if (!cpu->vcpu_dirty) { - whpx_get_registers(cpu); - } + whpx_cpu_synchronize_state(cpu); apic_handle_tpr_access_report(x86_cpu->apic_state, env->eip, env->tpr_access_type); } @@ -896,7 +955,7 @@ static int whpx_vcpu_run(CPUState *cpu) do { if (cpu->vcpu_dirty) { - whpx_set_registers(cpu); + whpx_set_registers(cpu, WHPX_SET_RUNTIME_STATE); cpu->vcpu_dirty = false; } @@ -980,38 +1039,32 @@ static int whpx_vcpu_run(CPUState *cpu) WHV_REGISTER_VALUE reg_values[5]; WHV_REGISTER_NAME reg_names[5]; UINT32 reg_count = 5; - UINT64 rip, rax, rcx, rdx, rbx; + UINT64 cpuid_fn, rip = 0, rax = 0, rcx = 0, rdx = 0, rbx = 0; + X86CPU *x86_cpu = X86_CPU(cpu); + CPUX86State *env = &x86_cpu->env; memset(reg_values, 0, sizeof(reg_values)); rip = vcpu->exit_ctx.VpContext.Rip + vcpu->exit_ctx.VpContext.InstructionLength; - switch (vcpu->exit_ctx.CpuidAccess.Rax) { - case 1: - rax = vcpu->exit_ctx.CpuidAccess.DefaultResultRax; - /* Advertise that we are running on a hypervisor */ - rcx = - vcpu->exit_ctx.CpuidAccess.DefaultResultRcx | - CPUID_EXT_HYPERVISOR; + cpuid_fn = vcpu->exit_ctx.CpuidAccess.Rax; - rdx = vcpu->exit_ctx.CpuidAccess.DefaultResultRdx; - rbx = vcpu->exit_ctx.CpuidAccess.DefaultResultRbx; - break; + /* + * Ideally, these should be supplied to the hypervisor during VCPU + * initialization and it should be able to satisfy this request. + * But, currently, WHPX doesn't support setting CPUID values in the + * hypervisor once the partition has been setup, which is too late + * since VCPUs are realized later. For now, use the values from + * QEMU to satisfy these requests, until WHPX adds support for + * being able to set these values in the hypervisor at runtime. + */ + cpu_x86_cpuid(env, cpuid_fn, 0, (UINT32 *)&rax, (UINT32 *)&rbx, + (UINT32 *)&rcx, (UINT32 *)&rdx); + switch (cpuid_fn) { case 0x80000001: - rax = vcpu->exit_ctx.CpuidAccess.DefaultResultRax; /* Remove any support of OSVW */ - rcx = - vcpu->exit_ctx.CpuidAccess.DefaultResultRcx & - ~CPUID_EXT3_OSVW; - - rdx = vcpu->exit_ctx.CpuidAccess.DefaultResultRdx; - rbx = vcpu->exit_ctx.CpuidAccess.DefaultResultRbx; + rcx &= ~CPUID_EXT3_OSVW; break; - default: - rax = vcpu->exit_ctx.CpuidAccess.DefaultResultRax; - rcx = vcpu->exit_ctx.CpuidAccess.DefaultResultRcx; - rdx = vcpu->exit_ctx.CpuidAccess.DefaultResultRdx; - rbx = vcpu->exit_ctx.CpuidAccess.DefaultResultRbx; } reg_names[0] = WHvX64RegisterRip; @@ -1067,21 +1120,23 @@ static int whpx_vcpu_run(CPUState *cpu) static void do_whpx_cpu_synchronize_state(CPUState *cpu, run_on_cpu_data arg) { - whpx_get_registers(cpu); - cpu->vcpu_dirty = true; + if (!cpu->vcpu_dirty) { + whpx_get_registers(cpu); + cpu->vcpu_dirty = true; + } } static void do_whpx_cpu_synchronize_post_reset(CPUState *cpu, run_on_cpu_data arg) { - whpx_set_registers(cpu); + whpx_set_registers(cpu, WHPX_SET_RESET_STATE); cpu->vcpu_dirty = false; } static void do_whpx_cpu_synchronize_post_init(CPUState *cpu, run_on_cpu_data arg) { - whpx_set_registers(cpu); + whpx_set_registers(cpu, WHPX_SET_FULL_STATE); cpu->vcpu_dirty = false; } @@ -1123,6 +1178,15 @@ void whpx_cpu_synchronize_pre_loadvm(CPUState *cpu) static Error *whpx_migration_blocker; +static void whpx_cpu_update_state(void *opaque, int running, RunState state) +{ + CPUX86State *env = opaque; + + if (running) { + env->tsc_valid = false; + } +} + int whpx_init_vcpu(CPUState *cpu) { HRESULT hr; @@ -1178,6 +1242,7 @@ int whpx_init_vcpu(CPUState *cpu) cpu->vcpu_dirty = true; cpu->hax_vcpu = (struct hax_vcpu_state *)vcpu; + qemu_add_vm_change_state_handler(whpx_cpu_update_state, cpu->env_ptr); return 0; } @@ -1355,6 +1420,67 @@ static void whpx_handle_interrupt(CPUState *cpu, int mask) } } +/* + * Load the functions from the given library, using the given handle. If a + * handle is provided, it is used, otherwise the library is opened. The + * handle will be updated on return with the opened one. + */ +static bool load_whp_dispatch_fns(HMODULE *handle, + WHPFunctionList function_list) +{ + HMODULE hLib = *handle; + + #define WINHV_PLATFORM_DLL "WinHvPlatform.dll" + #define WINHV_EMULATION_DLL "WinHvEmulation.dll" + #define WHP_LOAD_FIELD_OPTIONAL(return_type, function_name, signature) \ + whp_dispatch.function_name = \ + (function_name ## _t)GetProcAddress(hLib, #function_name); \ + + #define WHP_LOAD_FIELD(return_type, function_name, signature) \ + whp_dispatch.function_name = \ + (function_name ## _t)GetProcAddress(hLib, #function_name); \ + if (!whp_dispatch.function_name) { \ + error_report("Could not load function %s", #function_name); \ + goto error; \ + } \ + + #define WHP_LOAD_LIB(lib_name, handle_lib) \ + if (!handle_lib) { \ + handle_lib = LoadLibrary(lib_name); \ + if (!handle_lib) { \ + error_report("Could not load library %s.", lib_name); \ + goto error; \ + } \ + } \ + + switch (function_list) { + case WINHV_PLATFORM_FNS_DEFAULT: + WHP_LOAD_LIB(WINHV_PLATFORM_DLL, hLib) + LIST_WINHVPLATFORM_FUNCTIONS(WHP_LOAD_FIELD) + break; + + case WINHV_EMULATION_FNS_DEFAULT: + WHP_LOAD_LIB(WINHV_EMULATION_DLL, hLib) + LIST_WINHVEMULATION_FUNCTIONS(WHP_LOAD_FIELD) + break; + + case WINHV_PLATFORM_FNS_SUPPLEMENTAL: + WHP_LOAD_LIB(WINHV_PLATFORM_DLL, hLib) + LIST_WINHVPLATFORM_FUNCTIONS_SUPPLEMENTAL(WHP_LOAD_FIELD_OPTIONAL) + break; + } + + *handle = hLib; + return true; + +error: + if (hLib) { + FreeLibrary(hLib); + } + + return false; +} + /* * Partition support */ @@ -1490,51 +1616,32 @@ static void whpx_type_init(void) bool init_whp_dispatch(void) { - const char *lib_name; - HMODULE hLib; - if (whp_dispatch_initialized) { return true; } - #define WHP_LOAD_FIELD(return_type, function_name, signature) \ - whp_dispatch.function_name = \ - (function_name ## _t)GetProcAddress(hLib, #function_name); \ - if (!whp_dispatch.function_name) { \ - error_report("Could not load function %s from library %s.", \ - #function_name, lib_name); \ - goto error; \ - } \ - - lib_name = "WinHvPlatform.dll"; - hWinHvPlatform = LoadLibrary(lib_name); - if (!hWinHvPlatform) { - error_report("Could not load library %s.", lib_name); + if (!load_whp_dispatch_fns(&hWinHvPlatform, WINHV_PLATFORM_FNS_DEFAULT)) { goto error; } - hLib = hWinHvPlatform; - LIST_WINHVPLATFORM_FUNCTIONS(WHP_LOAD_FIELD) - lib_name = "WinHvEmulation.dll"; - hWinHvEmulation = LoadLibrary(lib_name); - if (!hWinHvEmulation) { - error_report("Could not load library %s.", lib_name); + if (!load_whp_dispatch_fns(&hWinHvEmulation, WINHV_EMULATION_FNS_DEFAULT)) { goto error; } - hLib = hWinHvEmulation; - LIST_WINHVEMULATION_FUNCTIONS(WHP_LOAD_FIELD) + assert(load_whp_dispatch_fns(&hWinHvPlatform, + WINHV_PLATFORM_FNS_SUPPLEMENTAL)); whp_dispatch_initialized = true; + return true; - - error: - +error: if (hWinHvPlatform) { FreeLibrary(hWinHvPlatform); } + if (hWinHvEmulation) { FreeLibrary(hWinHvEmulation); } + return false; } diff --git a/target/lm32/cpu-qom.h b/target/lm32/cpu-qom.h index dc9ac9ac9f..bdedb3759a 100644 --- a/target/lm32/cpu-qom.h +++ b/target/lm32/cpu-qom.h @@ -44,7 +44,7 @@ typedef struct LM32CPUClass { /*< public >*/ DeviceRealize parent_realize; - void (*parent_reset)(CPUState *cpu); + DeviceReset parent_reset; } LM32CPUClass; typedef struct LM32CPU LM32CPU; diff --git a/target/lm32/cpu.c b/target/lm32/cpu.c index b35537de62..c50ad5fa15 100644 --- a/target/lm32/cpu.c +++ b/target/lm32/cpu.c @@ -99,14 +99,14 @@ static bool lm32_cpu_has_work(CPUState *cs) return cs->interrupt_request & CPU_INTERRUPT_HARD; } -/* CPUClass::reset() */ -static void lm32_cpu_reset(CPUState *s) +static void lm32_cpu_reset(DeviceState *dev) { + CPUState *s = CPU(dev); LM32CPU *cpu = LM32_CPU(s); LM32CPUClass *lcc = LM32_CPU_GET_CLASS(cpu); CPULM32State *env = &cpu->env; - lcc->parent_reset(s); + lcc->parent_reset(dev); /* reset cpu state */ memset(env, 0, offsetof(CPULM32State, end_reset_fields)); @@ -218,8 +218,7 @@ static void lm32_cpu_class_init(ObjectClass *oc, void *data) device_class_set_parent_realize(dc, lm32_cpu_realizefn, &lcc->parent_realize); - lcc->parent_reset = cc->reset; - cc->reset = lm32_cpu_reset; + device_class_set_parent_reset(dc, lm32_cpu_reset, &lcc->parent_reset); cc->class_by_name = lm32_cpu_class_by_name; cc->has_work = lm32_cpu_has_work; diff --git a/target/lm32/cpu.h b/target/lm32/cpu.h index 064c6b1267..01d408eb55 100644 --- a/target/lm32/cpu.h +++ b/target/lm32/cpu.h @@ -202,7 +202,7 @@ void lm32_cpu_do_interrupt(CPUState *cpu); bool lm32_cpu_exec_interrupt(CPUState *cs, int int_req); void lm32_cpu_dump_state(CPUState *cpu, FILE *f, int flags); hwaddr lm32_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); -int lm32_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); +int lm32_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int lm32_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); typedef enum { diff --git a/target/lm32/gdbstub.c b/target/lm32/gdbstub.c index 82ede436e1..b6fe12e1d6 100644 --- a/target/lm32/gdbstub.c +++ b/target/lm32/gdbstub.c @@ -22,7 +22,7 @@ #include "exec/gdbstub.h" #include "hw/lm32/lm32_pic.h" -int lm32_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +int lm32_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) { LM32CPU *cpu = LM32_CPU(cs); CPULM32State *env = &cpu->env; diff --git a/target/lm32/translate.c b/target/lm32/translate.c index 778cae1e81..e583d52d03 100644 --- a/target/lm32/translate.c +++ b/target/lm32/translate.c @@ -23,7 +23,7 @@ #include "exec/helper-proto.h" #include "exec/exec-all.h" #include "exec/translator.h" -#include "tcg-op.h" +#include "tcg/tcg-op.h" #include "qemu/qemu-print.h" #include "exec/cpu_ldst.h" @@ -1137,10 +1137,10 @@ void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int max_insns) #ifdef DEBUG_DISAS if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM) && qemu_log_in_addr_range(pc_start)) { - qemu_log_lock(); + FILE *logfile = qemu_log_lock(); qemu_log("\n"); log_target_disas(cs, pc_start, dc->pc - pc_start); - qemu_log_unlock(); + qemu_log_unlock(logfile); } #endif } diff --git a/target/m68k/cpu-qom.h b/target/m68k/cpu-qom.h index b56da8a213..88b11b60f1 100644 --- a/target/m68k/cpu-qom.h +++ b/target/m68k/cpu-qom.h @@ -44,7 +44,7 @@ typedef struct M68kCPUClass { /*< public >*/ DeviceRealize parent_realize; - void (*parent_reset)(CPUState *cpu); + DeviceReset parent_reset; } M68kCPUClass; typedef struct M68kCPU M68kCPU; diff --git a/target/m68k/cpu.c b/target/m68k/cpu.c index e6596de29c..9445fcd6df 100644 --- a/target/m68k/cpu.c +++ b/target/m68k/cpu.c @@ -41,16 +41,16 @@ static void m68k_set_feature(CPUM68KState *env, int feature) env->features |= (1u << feature); } -/* CPUClass::reset() */ -static void m68k_cpu_reset(CPUState *s) +static void m68k_cpu_reset(DeviceState *dev) { + CPUState *s = CPU(dev); M68kCPU *cpu = M68K_CPU(s); M68kCPUClass *mcc = M68K_CPU_GET_CLASS(cpu); CPUM68KState *env = &cpu->env; floatx80 nan = floatx80_default_nan(NULL); int i; - mcc->parent_reset(s); + mcc->parent_reset(dev); memset(env, 0, offsetof(CPUM68KState, end_reset_fields)); #ifdef CONFIG_SOFTMMU @@ -114,11 +114,9 @@ static void m68000_cpu_initfn(Object *obj) m68k_set_feature(env, M68K_FEATURE_MOVEP); } -static void m68020_cpu_initfn(Object *obj) +/* common features for 68020, 68030 and 68040 */ +static void m680x0_cpu_common(CPUM68KState *env) { - M68kCPU *cpu = M68K_CPU(obj); - CPUM68KState *env = &cpu->env; - m68k_set_feature(env, M68K_FEATURE_M68000); m68k_set_feature(env, M68K_FEATURE_USP); m68k_set_feature(env, M68K_FEATURE_WORD_INDEX); @@ -136,14 +134,31 @@ static void m68020_cpu_initfn(Object *obj) m68k_set_feature(env, M68K_FEATURE_CHK2); m68k_set_feature(env, M68K_FEATURE_MOVEP); } -#define m68030_cpu_initfn m68020_cpu_initfn + +static void m68020_cpu_initfn(Object *obj) +{ + M68kCPU *cpu = M68K_CPU(obj); + CPUM68KState *env = &cpu->env; + + m680x0_cpu_common(env); + m68k_set_feature(env, M68K_FEATURE_M68020); +} + +static void m68030_cpu_initfn(Object *obj) +{ + M68kCPU *cpu = M68K_CPU(obj); + CPUM68KState *env = &cpu->env; + + m680x0_cpu_common(env); + m68k_set_feature(env, M68K_FEATURE_M68030); +} static void m68040_cpu_initfn(Object *obj) { M68kCPU *cpu = M68K_CPU(obj); CPUM68KState *env = &cpu->env; - m68020_cpu_initfn(obj); + m680x0_cpu_common(env); m68k_set_feature(env, M68K_FEATURE_M68040); } @@ -166,6 +181,7 @@ static void m68060_cpu_initfn(Object *obj) m68k_set_feature(env, M68K_FEATURE_BKPT); m68k_set_feature(env, M68K_FEATURE_RTD); m68k_set_feature(env, M68K_FEATURE_CHK2); + m68k_set_feature(env, M68K_FEATURE_M68060); } static void m5208_cpu_initfn(Object *obj) @@ -257,8 +273,7 @@ static void m68k_cpu_class_init(ObjectClass *c, void *data) device_class_set_parent_realize(dc, m68k_cpu_realizefn, &mcc->parent_realize); - mcc->parent_reset = cc->reset; - cc->reset = m68k_cpu_reset; + device_class_set_parent_reset(dc, m68k_cpu_reset, &mcc->parent_reset); cc->class_by_name = m68k_cpu_class_by_name; cc->has_work = m68k_cpu_has_work; diff --git a/target/m68k/cpu.h b/target/m68k/cpu.h index 20de3c379a..521ac67cdd 100644 --- a/target/m68k/cpu.h +++ b/target/m68k/cpu.h @@ -168,7 +168,7 @@ void m68k_cpu_do_interrupt(CPUState *cpu); bool m68k_cpu_exec_interrupt(CPUState *cpu, int int_req); void m68k_cpu_dump_state(CPUState *cpu, FILE *f, int flags); hwaddr m68k_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); -int m68k_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); +int m68k_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int m68k_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); void m68k_tcg_init(void); @@ -460,6 +460,10 @@ void do_m68k_semihosting(CPUM68KState *env, int nr); enum m68k_features { M68K_FEATURE_M68000, + M68K_FEATURE_M68020, + M68K_FEATURE_M68030, + M68K_FEATURE_M68040, + M68K_FEATURE_M68060, M68K_FEATURE_CF_ISA_A, M68K_FEATURE_CF_ISA_B, /* (ISA B or C). */ M68K_FEATURE_CF_ISA_APLUSC, /* BIT/BITREV, FF1, STRLDSR (ISA A+ or C). */ @@ -481,7 +485,6 @@ enum m68k_features { M68K_FEATURE_BKPT, M68K_FEATURE_RTD, M68K_FEATURE_CHK2, - M68K_FEATURE_M68040, /* instructions specific to MC68040 */ M68K_FEATURE_MOVEP, }; @@ -516,8 +519,6 @@ enum { #define cpu_list m68k_cpu_list /* MMU modes definitions */ -#define MMU_MODE0_SUFFIX _kernel -#define MMU_MODE1_SUFFIX _user #define MMU_KERNEL_IDX 0 #define MMU_USER_IDX 1 static inline int cpu_mmu_index (CPUM68KState *env, bool ifetch) diff --git a/target/m68k/gdbstub.c b/target/m68k/gdbstub.c index fdc96f57ff..eb2d030e14 100644 --- a/target/m68k/gdbstub.c +++ b/target/m68k/gdbstub.c @@ -21,7 +21,7 @@ #include "cpu.h" #include "exec/gdbstub.h" -int m68k_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +int m68k_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) { M68kCPU *cpu = M68K_CPU(cs); CPUM68KState *env = &cpu->env; diff --git a/target/m68k/helper.c b/target/m68k/helper.c index ae766a6cb0..cad4083895 100644 --- a/target/m68k/helper.c +++ b/target/m68k/helper.c @@ -68,23 +68,19 @@ void m68k_cpu_list(void) g_slist_free(list); } -static int cf_fpu_gdb_get_reg(CPUM68KState *env, uint8_t *mem_buf, int n) +static int cf_fpu_gdb_get_reg(CPUM68KState *env, GByteArray *mem_buf, int n) { if (n < 8) { float_status s; - stfq_p(mem_buf, floatx80_to_float64(env->fregs[n].d, &s)); - return 8; + return gdb_get_reg64(mem_buf, floatx80_to_float64(env->fregs[n].d, &s)); } switch (n) { case 8: /* fpcontrol */ - stl_be_p(mem_buf, env->fpcr); - return 4; + return gdb_get_reg32(mem_buf, env->fpcr); case 9: /* fpstatus */ - stl_be_p(mem_buf, env->fpsr); - return 4; + return gdb_get_reg32(mem_buf, env->fpsr); case 10: /* fpiar, not implemented */ - memset(mem_buf, 0, 4); - return 4; + return gdb_get_reg32(mem_buf, 0); } return 0; } @@ -109,24 +105,21 @@ static int cf_fpu_gdb_set_reg(CPUM68KState *env, uint8_t *mem_buf, int n) return 0; } -static int m68k_fpu_gdb_get_reg(CPUM68KState *env, uint8_t *mem_buf, int n) +static int m68k_fpu_gdb_get_reg(CPUM68KState *env, GByteArray *mem_buf, int n) { if (n < 8) { - stw_be_p(mem_buf, env->fregs[n].l.upper); - memset(mem_buf + 2, 0, 2); - stq_be_p(mem_buf + 4, env->fregs[n].l.lower); - return 12; + int len = gdb_get_reg16(mem_buf, env->fregs[n].l.upper); + len += gdb_get_reg16(mem_buf, 0); + len += gdb_get_reg64(mem_buf, env->fregs[n].l.lower); + return len; } switch (n) { case 8: /* fpcontrol */ - stl_be_p(mem_buf, env->fpcr); - return 4; + return gdb_get_reg32(mem_buf, env->fpcr); case 9: /* fpstatus */ - stl_be_p(mem_buf, env->fpsr); - return 4; + return gdb_get_reg32(mem_buf, env->fpsr); case 10: /* fpiar, not implemented */ - memset(mem_buf, 0, 4); - return 4; + return gdb_get_reg32(mem_buf, 0); } return 0; } @@ -203,9 +196,17 @@ void HELPER(m68k_movec_to)(CPUM68KState *env, uint32_t reg, uint32_t val) case M68K_CR_VBR: env->vbr = val; return; - /* MC680[234]0 */ + /* MC680[2346]0 */ case M68K_CR_CACR: - env->cacr = val; + if (m68k_feature(env, M68K_FEATURE_M68020)) { + env->cacr = val & 0x0000000f; + } else if (m68k_feature(env, M68K_FEATURE_M68030)) { + env->cacr = val & 0x00003f1f; + } else if (m68k_feature(env, M68K_FEATURE_M68040)) { + env->cacr = val & 0x80008000; + } else if (m68k_feature(env, M68K_FEATURE_M68060)) { + env->cacr = val & 0xf8e0e000; + } m68k_switch_sp(env); return; /* MC680[34]0 */ diff --git a/target/m68k/op_helper.c b/target/m68k/op_helper.c index bc4f845e3f..202498deb5 100644 --- a/target/m68k/op_helper.c +++ b/target/m68k/op_helper.c @@ -42,8 +42,8 @@ static void cf_rte(CPUM68KState *env) uint32_t fmt; sp = env->aregs[7]; - fmt = cpu_ldl_kernel(env, sp); - env->pc = cpu_ldl_kernel(env, sp + 4); + fmt = cpu_ldl_mmuidx_ra(env, sp, MMU_KERNEL_IDX, 0); + env->pc = cpu_ldl_mmuidx_ra(env, sp + 4, MMU_KERNEL_IDX, 0); sp |= (fmt >> 28) & 3; env->aregs[7] = sp + 8; @@ -58,13 +58,13 @@ static void m68k_rte(CPUM68KState *env) sp = env->aregs[7]; throwaway: - sr = cpu_lduw_kernel(env, sp); + sr = cpu_lduw_mmuidx_ra(env, sp, MMU_KERNEL_IDX, 0); sp += 2; - env->pc = cpu_ldl_kernel(env, sp); + env->pc = cpu_ldl_mmuidx_ra(env, sp, MMU_KERNEL_IDX, 0); sp += 4; if (m68k_feature(env, M68K_FEATURE_QUAD_MULDIV)) { /* all except 68000 */ - fmt = cpu_lduw_kernel(env, sp); + fmt = cpu_lduw_mmuidx_ra(env, sp, MMU_KERNEL_IDX, 0); sp += 2; switch (fmt >> 12) { case 0: @@ -260,12 +260,12 @@ static void cf_interrupt_all(CPUM68KState *env, int is_hw) /* ??? This could cause MMU faults. */ sp &= ~3; sp -= 4; - cpu_stl_kernel(env, sp, retaddr); + cpu_stl_mmuidx_ra(env, sp, retaddr, MMU_KERNEL_IDX, 0); sp -= 4; - cpu_stl_kernel(env, sp, fmt); + cpu_stl_mmuidx_ra(env, sp, fmt, MMU_KERNEL_IDX, 0); env->aregs[7] = sp; /* Jump to vector. */ - env->pc = cpu_ldl_kernel(env, env->vbr + vector); + env->pc = cpu_ldl_mmuidx_ra(env, env->vbr + vector, MMU_KERNEL_IDX, 0); } static inline void do_stack_frame(CPUM68KState *env, uint32_t *sp, @@ -278,23 +278,24 @@ static inline void do_stack_frame(CPUM68KState *env, uint32_t *sp, switch (format) { case 4: *sp -= 4; - cpu_stl_kernel(env, *sp, env->pc); + cpu_stl_mmuidx_ra(env, *sp, env->pc, MMU_KERNEL_IDX, 0); *sp -= 4; - cpu_stl_kernel(env, *sp, addr); + cpu_stl_mmuidx_ra(env, *sp, addr, MMU_KERNEL_IDX, 0); break; case 3: case 2: *sp -= 4; - cpu_stl_kernel(env, *sp, addr); + cpu_stl_mmuidx_ra(env, *sp, addr, MMU_KERNEL_IDX, 0); break; } *sp -= 2; - cpu_stw_kernel(env, *sp, (format << 12) + (cs->exception_index << 2)); + cpu_stw_mmuidx_ra(env, *sp, (format << 12) + (cs->exception_index << 2), + MMU_KERNEL_IDX, 0); } *sp -= 4; - cpu_stl_kernel(env, *sp, retaddr); + cpu_stl_mmuidx_ra(env, *sp, retaddr, MMU_KERNEL_IDX, 0); *sp -= 2; - cpu_stw_kernel(env, *sp, sr); + cpu_stw_mmuidx_ra(env, *sp, sr, MMU_KERNEL_IDX, 0); } static void m68k_interrupt_all(CPUM68KState *env, int is_hw) @@ -353,36 +354,52 @@ static void m68k_interrupt_all(CPUM68KState *env, int is_hw) cpu_abort(cs, "DOUBLE MMU FAULT\n"); } env->mmu.fault = true; + /* push data 3 */ sp -= 4; - cpu_stl_kernel(env, sp, 0); /* push data 3 */ + cpu_stl_mmuidx_ra(env, sp, 0, MMU_KERNEL_IDX, 0); + /* push data 2 */ sp -= 4; - cpu_stl_kernel(env, sp, 0); /* push data 2 */ + cpu_stl_mmuidx_ra(env, sp, 0, MMU_KERNEL_IDX, 0); + /* push data 1 */ sp -= 4; - cpu_stl_kernel(env, sp, 0); /* push data 1 */ + cpu_stl_mmuidx_ra(env, sp, 0, MMU_KERNEL_IDX, 0); + /* write back 1 / push data 0 */ sp -= 4; - cpu_stl_kernel(env, sp, 0); /* write back 1 / push data 0 */ + cpu_stl_mmuidx_ra(env, sp, 0, MMU_KERNEL_IDX, 0); + /* write back 1 address */ sp -= 4; - cpu_stl_kernel(env, sp, 0); /* write back 1 address */ + cpu_stl_mmuidx_ra(env, sp, 0, MMU_KERNEL_IDX, 0); + /* write back 2 data */ sp -= 4; - cpu_stl_kernel(env, sp, 0); /* write back 2 data */ + cpu_stl_mmuidx_ra(env, sp, 0, MMU_KERNEL_IDX, 0); + /* write back 2 address */ sp -= 4; - cpu_stl_kernel(env, sp, 0); /* write back 2 address */ + cpu_stl_mmuidx_ra(env, sp, 0, MMU_KERNEL_IDX, 0); + /* write back 3 data */ sp -= 4; - cpu_stl_kernel(env, sp, 0); /* write back 3 data */ + cpu_stl_mmuidx_ra(env, sp, 0, MMU_KERNEL_IDX, 0); + /* write back 3 address */ sp -= 4; - cpu_stl_kernel(env, sp, env->mmu.ar); /* write back 3 address */ + cpu_stl_mmuidx_ra(env, sp, env->mmu.ar, MMU_KERNEL_IDX, 0); + /* fault address */ sp -= 4; - cpu_stl_kernel(env, sp, env->mmu.ar); /* fault address */ + cpu_stl_mmuidx_ra(env, sp, env->mmu.ar, MMU_KERNEL_IDX, 0); + /* write back 1 status */ sp -= 2; - cpu_stw_kernel(env, sp, 0); /* write back 1 status */ + cpu_stw_mmuidx_ra(env, sp, 0, MMU_KERNEL_IDX, 0); + /* write back 2 status */ sp -= 2; - cpu_stw_kernel(env, sp, 0); /* write back 2 status */ + cpu_stw_mmuidx_ra(env, sp, 0, MMU_KERNEL_IDX, 0); + /* write back 3 status */ sp -= 2; - cpu_stw_kernel(env, sp, 0); /* write back 3 status */ + cpu_stw_mmuidx_ra(env, sp, 0, MMU_KERNEL_IDX, 0); + /* special status word */ sp -= 2; - cpu_stw_kernel(env, sp, env->mmu.ssw); /* special status word */ + cpu_stw_mmuidx_ra(env, sp, env->mmu.ssw, MMU_KERNEL_IDX, 0); + /* effective address */ sp -= 4; - cpu_stl_kernel(env, sp, env->mmu.ar); /* effective address */ + cpu_stl_mmuidx_ra(env, sp, env->mmu.ar, MMU_KERNEL_IDX, 0); + do_stack_frame(env, &sp, 7, oldsr, 0, retaddr); env->mmu.fault = false; if (qemu_loglevel_mask(CPU_LOG_INT)) { @@ -414,7 +431,7 @@ static void m68k_interrupt_all(CPUM68KState *env, int is_hw) env->aregs[7] = sp; /* Jump to vector. */ - env->pc = cpu_ldl_kernel(env, env->vbr + vector); + env->pc = cpu_ldl_mmuidx_ra(env, env->vbr + vector, MMU_KERNEL_IDX, 0); } static void do_interrupt_all(CPUM68KState *env, int is_hw) diff --git a/target/m68k/translate.c b/target/m68k/translate.c index fcdb7bc8e4..0f80888203 100644 --- a/target/m68k/translate.c +++ b/target/m68k/translate.c @@ -22,7 +22,7 @@ #include "cpu.h" #include "disas/disas.h" #include "exec/exec-all.h" -#include "tcg-op.h" +#include "tcg/tcg-op.h" #include "qemu/log.h" #include "qemu/qemu-print.h" #include "exec/cpu_ldst.h" @@ -289,16 +289,21 @@ static void gen_jmp(DisasContext *s, TCGv dest) s->base.is_jmp = DISAS_JUMP; } -static void gen_exception(DisasContext *s, uint32_t dest, int nr) +static void gen_raise_exception(int nr) { TCGv_i32 tmp; - update_cc_op(s); - tcg_gen_movi_i32(QREG_PC, dest); - tmp = tcg_const_i32(nr); gen_helper_raise_exception(cpu_env, tmp); tcg_temp_free_i32(tmp); +} + +static void gen_exception(DisasContext *s, uint32_t dest, int nr) +{ + update_cc_op(s); + tcg_gen_movi_i32(QREG_PC, dest); + + gen_raise_exception(nr); s->base.is_jmp = DISAS_NORETURN; } @@ -6198,29 +6203,36 @@ static void m68k_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) { DisasContext *dc = container_of(dcbase, DisasContext, base); - if (dc->base.is_jmp == DISAS_NORETURN) { - return; - } - if (dc->base.singlestep_enabled) { - gen_helper_raise_exception(cpu_env, tcg_const_i32(EXCP_DEBUG)); - return; - } - switch (dc->base.is_jmp) { + case DISAS_NORETURN: + break; case DISAS_TOO_MANY: update_cc_op(dc); - gen_jmp_tb(dc, 0, dc->pc); + if (dc->base.singlestep_enabled) { + tcg_gen_movi_i32(QREG_PC, dc->pc); + gen_raise_exception(EXCP_DEBUG); + } else { + gen_jmp_tb(dc, 0, dc->pc); + } break; case DISAS_JUMP: /* We updated CC_OP and PC in gen_jmp/gen_jmp_im. */ - tcg_gen_lookup_and_goto_ptr(); + if (dc->base.singlestep_enabled) { + gen_raise_exception(EXCP_DEBUG); + } else { + tcg_gen_lookup_and_goto_ptr(); + } break; case DISAS_EXIT: /* * We updated CC_OP and PC in gen_exit_tb, but also modified * other state that may require returning to the main loop. */ - tcg_gen_exit_tb(NULL, 0); + if (dc->base.singlestep_enabled) { + gen_raise_exception(EXCP_DEBUG); + } else { + tcg_gen_exit_tb(NULL, 0); + } break; default: g_assert_not_reached(); diff --git a/target/microblaze/cpu-qom.h b/target/microblaze/cpu-qom.h index 49b07cc697..053ba44ee8 100644 --- a/target/microblaze/cpu-qom.h +++ b/target/microblaze/cpu-qom.h @@ -44,7 +44,7 @@ typedef struct MicroBlazeCPUClass { /*< public >*/ DeviceRealize parent_realize; - void (*parent_reset)(CPUState *cpu); + DeviceReset parent_reset; } MicroBlazeCPUClass; typedef struct MicroBlazeCPU MicroBlazeCPU; diff --git a/target/microblaze/cpu.c b/target/microblaze/cpu.c index 9cfd7445e7..a2c2f271df 100644 --- a/target/microblaze/cpu.c +++ b/target/microblaze/cpu.c @@ -102,14 +102,14 @@ static void microblaze_cpu_set_irq(void *opaque, int irq, int level) } #endif -/* CPUClass::reset() */ -static void mb_cpu_reset(CPUState *s) +static void mb_cpu_reset(DeviceState *dev) { + CPUState *s = CPU(dev); MicroBlazeCPU *cpu = MICROBLAZE_CPU(s); MicroBlazeCPUClass *mcc = MICROBLAZE_CPU_GET_CLASS(cpu); CPUMBState *env = &cpu->env; - mcc->parent_reset(s); + mcc->parent_reset(dev); memset(env, 0, offsetof(CPUMBState, end_reset_fields)); env->res_addr = RES_ADDR_NONE; @@ -292,8 +292,7 @@ static void mb_cpu_class_init(ObjectClass *oc, void *data) device_class_set_parent_realize(dc, mb_cpu_realizefn, &mcc->parent_realize); - mcc->parent_reset = cc->reset; - cc->reset = mb_cpu_reset; + device_class_set_parent_reset(dc, mb_cpu_reset, &mcc->parent_reset); cc->class_by_name = mb_cpu_class_by_name; cc->has_work = mb_cpu_has_work; @@ -309,7 +308,7 @@ static void mb_cpu_class_init(ObjectClass *oc, void *data) cc->get_phys_page_debug = mb_cpu_get_phys_page_debug; #endif dc->vmsd = &vmstate_mb_cpu; - dc->props = mb_properties; + device_class_set_props(dc, mb_properties); cc->gdb_num_core_regs = 32 + 5; cc->disas_set_info = mb_disas_set_info; diff --git a/target/microblaze/cpu.h b/target/microblaze/cpu.h index 95773089aa..1a700a880c 100644 --- a/target/microblaze/cpu.h +++ b/target/microblaze/cpu.h @@ -313,7 +313,7 @@ void mb_cpu_do_interrupt(CPUState *cs); bool mb_cpu_exec_interrupt(CPUState *cs, int int_req); void mb_cpu_dump_state(CPUState *cpu, FILE *f, int flags); hwaddr mb_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); -int mb_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); +int mb_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int mb_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); void mb_tcg_init(void); @@ -328,9 +328,6 @@ int cpu_mb_signal_handler(int host_signum, void *pinfo, #define cpu_signal_handler cpu_mb_signal_handler /* MMU modes definitions */ -#define MMU_MODE0_SUFFIX _nommu -#define MMU_MODE1_SUFFIX _kernel -#define MMU_MODE2_SUFFIX _user #define MMU_NOMMU_IDX 0 #define MMU_KERNEL_IDX 1 #define MMU_USER_IDX 2 diff --git a/target/microblaze/gdbstub.c b/target/microblaze/gdbstub.c index 30677b6d1f..f41ebf1f33 100644 --- a/target/microblaze/gdbstub.c +++ b/target/microblaze/gdbstub.c @@ -21,7 +21,7 @@ #include "cpu.h" #include "exec/gdbstub.h" -int mb_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +int mb_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) { MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs); CPUMBState *env = &cpu->env; diff --git a/target/microblaze/translate.c b/target/microblaze/translate.c index bdc7d5326a..37a844db99 100644 --- a/target/microblaze/translate.c +++ b/target/microblaze/translate.c @@ -22,7 +22,7 @@ #include "cpu.h" #include "disas/disas.h" #include "exec/exec-all.h" -#include "tcg-op.h" +#include "tcg/tcg-op.h" #include "exec/helper-proto.h" #include "microblaze-decode.h" #include "exec/cpu_ldst.h" @@ -1765,10 +1765,10 @@ void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int max_insns) #if !SIM_COMPAT if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM) && qemu_log_in_addr_range(pc_start)) { - qemu_log_lock(); + FILE *logfile = qemu_log_lock(); qemu_log("--------------\n"); log_target_disas(cs, pc_start, dc->pc - pc_start); - qemu_log_unlock(); + qemu_log_unlock(logfile); } #endif #endif diff --git a/target/mips/Makefile.objs b/target/mips/Makefile.objs index 3448ad5e19..91eb691833 100644 --- a/target/mips/Makefile.objs +++ b/target/mips/Makefile.objs @@ -1,5 +1,6 @@ -obj-y += translate.o dsp_helper.o op_helper.o lmi_helper.o helper.o cpu.o -obj-y += gdbstub.o msa_helper.o +obj-y += translate.o cpu.o gdbstub.o helper.o +obj-y += op_helper.o cp0_helper.o fpu_helper.o +obj-y += dsp_helper.o lmi_helper.o msa_helper.o obj-$(CONFIG_SOFTMMU) += mips-semi.o obj-$(CONFIG_SOFTMMU) += machine.o cp0_timer.o obj-$(CONFIG_KVM) += kvm.o diff --git a/target/mips/cp0_helper.c b/target/mips/cp0_helper.c new file mode 100644 index 0000000000..bbf12e4a97 --- /dev/null +++ b/target/mips/cp0_helper.c @@ -0,0 +1,1678 @@ +/* + * Helpers for emulation of CP0-related MIPS instructions. + * + * Copyright (C) 2004-2005 Jocelyn Mayer + * Copyright (C) 2020 Wave Computing, Inc. + * Copyright (C) 2020 Aleksandar Markovic + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + * + */ + +#include "qemu/osdep.h" +#include "qemu/main-loop.h" +#include "cpu.h" +#include "internal.h" +#include "qemu/host-utils.h" +#include "exec/helper-proto.h" +#include "exec/exec-all.h" +#include "exec/cpu_ldst.h" +#include "exec/memop.h" +#include "sysemu/kvm.h" + + +#ifndef CONFIG_USER_ONLY +/* SMP helpers. */ +static bool mips_vpe_is_wfi(MIPSCPU *c) +{ + CPUState *cpu = CPU(c); + CPUMIPSState *env = &c->env; + + /* + * If the VPE is halted but otherwise active, it means it's waiting for + * an interrupt.\ + */ + return cpu->halted && mips_vpe_active(env); +} + +static bool mips_vp_is_wfi(MIPSCPU *c) +{ + CPUState *cpu = CPU(c); + CPUMIPSState *env = &c->env; + + return cpu->halted && mips_vp_active(env); +} + +static inline void mips_vpe_wake(MIPSCPU *c) +{ + /* + * Don't set ->halted = 0 directly, let it be done via cpu_has_work + * because there might be other conditions that state that c should + * be sleeping. + */ + qemu_mutex_lock_iothread(); + cpu_interrupt(CPU(c), CPU_INTERRUPT_WAKE); + qemu_mutex_unlock_iothread(); +} + +static inline void mips_vpe_sleep(MIPSCPU *cpu) +{ + CPUState *cs = CPU(cpu); + + /* + * The VPE was shut off, really go to bed. + * Reset any old _WAKE requests. + */ + cs->halted = 1; + cpu_reset_interrupt(cs, CPU_INTERRUPT_WAKE); +} + +static inline void mips_tc_wake(MIPSCPU *cpu, int tc) +{ + CPUMIPSState *c = &cpu->env; + + /* FIXME: TC reschedule. */ + if (mips_vpe_active(c) && !mips_vpe_is_wfi(cpu)) { + mips_vpe_wake(cpu); + } +} + +static inline void mips_tc_sleep(MIPSCPU *cpu, int tc) +{ + CPUMIPSState *c = &cpu->env; + + /* FIXME: TC reschedule. */ + if (!mips_vpe_active(c)) { + mips_vpe_sleep(cpu); + } +} + +/** + * mips_cpu_map_tc: + * @env: CPU from which mapping is performed. + * @tc: Should point to an int with the value of the global TC index. + * + * This function will transform @tc into a local index within the + * returned #CPUMIPSState. + */ + +/* + * FIXME: This code assumes that all VPEs have the same number of TCs, + * which depends on runtime setup. Can probably be fixed by + * walking the list of CPUMIPSStates. + */ +static CPUMIPSState *mips_cpu_map_tc(CPUMIPSState *env, int *tc) +{ + MIPSCPU *cpu; + CPUState *cs; + CPUState *other_cs; + int vpe_idx; + int tc_idx = *tc; + + if (!(env->CP0_VPEConf0 & (1 << CP0VPEC0_MVP))) { + /* Not allowed to address other CPUs. */ + *tc = env->current_tc; + return env; + } + + cs = env_cpu(env); + vpe_idx = tc_idx / cs->nr_threads; + *tc = tc_idx % cs->nr_threads; + other_cs = qemu_get_cpu(vpe_idx); + if (other_cs == NULL) { + return env; + } + cpu = MIPS_CPU(other_cs); + return &cpu->env; +} + +/* + * The per VPE CP0_Status register shares some fields with the per TC + * CP0_TCStatus registers. These fields are wired to the same registers, + * so changes to either of them should be reflected on both registers. + * + * Also, EntryHi shares the bottom 8 bit ASID with TCStauts. + * + * These helper call synchronizes the regs for a given cpu. + */ + +/* + * Called for updates to CP0_Status. Defined in "cpu.h" for gdbstub.c. + * static inline void sync_c0_status(CPUMIPSState *env, CPUMIPSState *cpu, + * int tc); + */ + +/* Called for updates to CP0_TCStatus. */ +static void sync_c0_tcstatus(CPUMIPSState *cpu, int tc, + target_ulong v) +{ + uint32_t status; + uint32_t tcu, tmx, tasid, tksu; + uint32_t mask = ((1U << CP0St_CU3) + | (1 << CP0St_CU2) + | (1 << CP0St_CU1) + | (1 << CP0St_CU0) + | (1 << CP0St_MX) + | (3 << CP0St_KSU)); + + tcu = (v >> CP0TCSt_TCU0) & 0xf; + tmx = (v >> CP0TCSt_TMX) & 0x1; + tasid = v & cpu->CP0_EntryHi_ASID_mask; + tksu = (v >> CP0TCSt_TKSU) & 0x3; + + status = tcu << CP0St_CU0; + status |= tmx << CP0St_MX; + status |= tksu << CP0St_KSU; + + cpu->CP0_Status &= ~mask; + cpu->CP0_Status |= status; + + /* Sync the TASID with EntryHi. */ + cpu->CP0_EntryHi &= ~cpu->CP0_EntryHi_ASID_mask; + cpu->CP0_EntryHi |= tasid; + + compute_hflags(cpu); +} + +/* Called for updates to CP0_EntryHi. */ +static void sync_c0_entryhi(CPUMIPSState *cpu, int tc) +{ + int32_t *tcst; + uint32_t asid, v = cpu->CP0_EntryHi; + + asid = v & cpu->CP0_EntryHi_ASID_mask; + + if (tc == cpu->current_tc) { + tcst = &cpu->active_tc.CP0_TCStatus; + } else { + tcst = &cpu->tcs[tc].CP0_TCStatus; + } + + *tcst &= ~cpu->CP0_EntryHi_ASID_mask; + *tcst |= asid; +} + +/* CP0 helpers */ +target_ulong helper_mfc0_mvpcontrol(CPUMIPSState *env) +{ + return env->mvp->CP0_MVPControl; +} + +target_ulong helper_mfc0_mvpconf0(CPUMIPSState *env) +{ + return env->mvp->CP0_MVPConf0; +} + +target_ulong helper_mfc0_mvpconf1(CPUMIPSState *env) +{ + return env->mvp->CP0_MVPConf1; +} + +target_ulong helper_mfc0_random(CPUMIPSState *env) +{ + return (int32_t)cpu_mips_get_random(env); +} + +target_ulong helper_mfc0_tcstatus(CPUMIPSState *env) +{ + return env->active_tc.CP0_TCStatus; +} + +target_ulong helper_mftc0_tcstatus(CPUMIPSState *env) +{ + int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); + CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); + + if (other_tc == other->current_tc) { + return other->active_tc.CP0_TCStatus; + } else { + return other->tcs[other_tc].CP0_TCStatus; + } +} + +target_ulong helper_mfc0_tcbind(CPUMIPSState *env) +{ + return env->active_tc.CP0_TCBind; +} + +target_ulong helper_mftc0_tcbind(CPUMIPSState *env) +{ + int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); + CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); + + if (other_tc == other->current_tc) { + return other->active_tc.CP0_TCBind; + } else { + return other->tcs[other_tc].CP0_TCBind; + } +} + +target_ulong helper_mfc0_tcrestart(CPUMIPSState *env) +{ + return env->active_tc.PC; +} + +target_ulong helper_mftc0_tcrestart(CPUMIPSState *env) +{ + int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); + CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); + + if (other_tc == other->current_tc) { + return other->active_tc.PC; + } else { + return other->tcs[other_tc].PC; + } +} + +target_ulong helper_mfc0_tchalt(CPUMIPSState *env) +{ + return env->active_tc.CP0_TCHalt; +} + +target_ulong helper_mftc0_tchalt(CPUMIPSState *env) +{ + int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); + CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); + + if (other_tc == other->current_tc) { + return other->active_tc.CP0_TCHalt; + } else { + return other->tcs[other_tc].CP0_TCHalt; + } +} + +target_ulong helper_mfc0_tccontext(CPUMIPSState *env) +{ + return env->active_tc.CP0_TCContext; +} + +target_ulong helper_mftc0_tccontext(CPUMIPSState *env) +{ + int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); + CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); + + if (other_tc == other->current_tc) { + return other->active_tc.CP0_TCContext; + } else { + return other->tcs[other_tc].CP0_TCContext; + } +} + +target_ulong helper_mfc0_tcschedule(CPUMIPSState *env) +{ + return env->active_tc.CP0_TCSchedule; +} + +target_ulong helper_mftc0_tcschedule(CPUMIPSState *env) +{ + int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); + CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); + + if (other_tc == other->current_tc) { + return other->active_tc.CP0_TCSchedule; + } else { + return other->tcs[other_tc].CP0_TCSchedule; + } +} + +target_ulong helper_mfc0_tcschefback(CPUMIPSState *env) +{ + return env->active_tc.CP0_TCScheFBack; +} + +target_ulong helper_mftc0_tcschefback(CPUMIPSState *env) +{ + int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); + CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); + + if (other_tc == other->current_tc) { + return other->active_tc.CP0_TCScheFBack; + } else { + return other->tcs[other_tc].CP0_TCScheFBack; + } +} + +target_ulong helper_mfc0_count(CPUMIPSState *env) +{ + return (int32_t)cpu_mips_get_count(env); +} + +target_ulong helper_mfc0_saar(CPUMIPSState *env) +{ + if ((env->CP0_SAARI & 0x3f) < 2) { + return (int32_t) env->CP0_SAAR[env->CP0_SAARI & 0x3f]; + } + return 0; +} + +target_ulong helper_mfhc0_saar(CPUMIPSState *env) +{ + if ((env->CP0_SAARI & 0x3f) < 2) { + return env->CP0_SAAR[env->CP0_SAARI & 0x3f] >> 32; + } + return 0; +} + +target_ulong helper_mftc0_entryhi(CPUMIPSState *env) +{ + int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); + CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); + + return other->CP0_EntryHi; +} + +target_ulong helper_mftc0_cause(CPUMIPSState *env) +{ + int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); + int32_t tccause; + CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); + + if (other_tc == other->current_tc) { + tccause = other->CP0_Cause; + } else { + tccause = other->CP0_Cause; + } + + return tccause; +} + +target_ulong helper_mftc0_status(CPUMIPSState *env) +{ + int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); + CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); + + return other->CP0_Status; +} + +target_ulong helper_mfc0_lladdr(CPUMIPSState *env) +{ + return (int32_t)(env->CP0_LLAddr >> env->CP0_LLAddr_shift); +} + +target_ulong helper_mfc0_maar(CPUMIPSState *env) +{ + return (int32_t) env->CP0_MAAR[env->CP0_MAARI]; +} + +target_ulong helper_mfhc0_maar(CPUMIPSState *env) +{ + return env->CP0_MAAR[env->CP0_MAARI] >> 32; +} + +target_ulong helper_mfc0_watchlo(CPUMIPSState *env, uint32_t sel) +{ + return (int32_t)env->CP0_WatchLo[sel]; +} + +target_ulong helper_mfc0_watchhi(CPUMIPSState *env, uint32_t sel) +{ + return (int32_t) env->CP0_WatchHi[sel]; +} + +target_ulong helper_mfhc0_watchhi(CPUMIPSState *env, uint32_t sel) +{ + return env->CP0_WatchHi[sel] >> 32; +} + +target_ulong helper_mfc0_debug(CPUMIPSState *env) +{ + target_ulong t0 = env->CP0_Debug; + if (env->hflags & MIPS_HFLAG_DM) { + t0 |= 1 << CP0DB_DM; + } + + return t0; +} + +target_ulong helper_mftc0_debug(CPUMIPSState *env) +{ + int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); + int32_t tcstatus; + CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); + + if (other_tc == other->current_tc) { + tcstatus = other->active_tc.CP0_Debug_tcstatus; + } else { + tcstatus = other->tcs[other_tc].CP0_Debug_tcstatus; + } + + /* XXX: Might be wrong, check with EJTAG spec. */ + return (other->CP0_Debug & ~((1 << CP0DB_SSt) | (1 << CP0DB_Halt))) | + (tcstatus & ((1 << CP0DB_SSt) | (1 << CP0DB_Halt))); +} + +#if defined(TARGET_MIPS64) +target_ulong helper_dmfc0_tcrestart(CPUMIPSState *env) +{ + return env->active_tc.PC; +} + +target_ulong helper_dmfc0_tchalt(CPUMIPSState *env) +{ + return env->active_tc.CP0_TCHalt; +} + +target_ulong helper_dmfc0_tccontext(CPUMIPSState *env) +{ + return env->active_tc.CP0_TCContext; +} + +target_ulong helper_dmfc0_tcschedule(CPUMIPSState *env) +{ + return env->active_tc.CP0_TCSchedule; +} + +target_ulong helper_dmfc0_tcschefback(CPUMIPSState *env) +{ + return env->active_tc.CP0_TCScheFBack; +} + +target_ulong helper_dmfc0_lladdr(CPUMIPSState *env) +{ + return env->CP0_LLAddr >> env->CP0_LLAddr_shift; +} + +target_ulong helper_dmfc0_maar(CPUMIPSState *env) +{ + return env->CP0_MAAR[env->CP0_MAARI]; +} + +target_ulong helper_dmfc0_watchlo(CPUMIPSState *env, uint32_t sel) +{ + return env->CP0_WatchLo[sel]; +} + +target_ulong helper_dmfc0_watchhi(CPUMIPSState *env, uint32_t sel) +{ + return env->CP0_WatchHi[sel]; +} + +target_ulong helper_dmfc0_saar(CPUMIPSState *env) +{ + if ((env->CP0_SAARI & 0x3f) < 2) { + return env->CP0_SAAR[env->CP0_SAARI & 0x3f]; + } + return 0; +} +#endif /* TARGET_MIPS64 */ + +void helper_mtc0_index(CPUMIPSState *env, target_ulong arg1) +{ + uint32_t index_p = env->CP0_Index & 0x80000000; + uint32_t tlb_index = arg1 & 0x7fffffff; + if (tlb_index < env->tlb->nb_tlb) { + if (env->insn_flags & ISA_MIPS32R6) { + index_p |= arg1 & 0x80000000; + } + env->CP0_Index = index_p | tlb_index; + } +} + +void helper_mtc0_mvpcontrol(CPUMIPSState *env, target_ulong arg1) +{ + uint32_t mask = 0; + uint32_t newval; + + if (env->CP0_VPEConf0 & (1 << CP0VPEC0_MVP)) { + mask |= (1 << CP0MVPCo_CPA) | (1 << CP0MVPCo_VPC) | + (1 << CP0MVPCo_EVP); + } + if (env->mvp->CP0_MVPControl & (1 << CP0MVPCo_VPC)) { + mask |= (1 << CP0MVPCo_STLB); + } + newval = (env->mvp->CP0_MVPControl & ~mask) | (arg1 & mask); + + /* TODO: Enable/disable shared TLB, enable/disable VPEs. */ + + env->mvp->CP0_MVPControl = newval; +} + +void helper_mtc0_vpecontrol(CPUMIPSState *env, target_ulong arg1) +{ + uint32_t mask; + uint32_t newval; + + mask = (1 << CP0VPECo_YSI) | (1 << CP0VPECo_GSI) | + (1 << CP0VPECo_TE) | (0xff << CP0VPECo_TargTC); + newval = (env->CP0_VPEControl & ~mask) | (arg1 & mask); + + /* + * Yield scheduler intercept not implemented. + * Gating storage scheduler intercept not implemented. + */ + + /* TODO: Enable/disable TCs. */ + + env->CP0_VPEControl = newval; +} + +void helper_mttc0_vpecontrol(CPUMIPSState *env, target_ulong arg1) +{ + int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); + CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); + uint32_t mask; + uint32_t newval; + + mask = (1 << CP0VPECo_YSI) | (1 << CP0VPECo_GSI) | + (1 << CP0VPECo_TE) | (0xff << CP0VPECo_TargTC); + newval = (other->CP0_VPEControl & ~mask) | (arg1 & mask); + + /* TODO: Enable/disable TCs. */ + + other->CP0_VPEControl = newval; +} + +target_ulong helper_mftc0_vpecontrol(CPUMIPSState *env) +{ + int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); + CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); + /* FIXME: Mask away return zero on read bits. */ + return other->CP0_VPEControl; +} + +target_ulong helper_mftc0_vpeconf0(CPUMIPSState *env) +{ + int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); + CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); + + return other->CP0_VPEConf0; +} + +void helper_mtc0_vpeconf0(CPUMIPSState *env, target_ulong arg1) +{ + uint32_t mask = 0; + uint32_t newval; + + if (env->CP0_VPEConf0 & (1 << CP0VPEC0_MVP)) { + if (env->CP0_VPEConf0 & (1 << CP0VPEC0_VPA)) { + mask |= (0xff << CP0VPEC0_XTC); + } + mask |= (1 << CP0VPEC0_MVP) | (1 << CP0VPEC0_VPA); + } + newval = (env->CP0_VPEConf0 & ~mask) | (arg1 & mask); + + /* TODO: TC exclusive handling due to ERL/EXL. */ + + env->CP0_VPEConf0 = newval; +} + +void helper_mttc0_vpeconf0(CPUMIPSState *env, target_ulong arg1) +{ + int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); + CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); + uint32_t mask = 0; + uint32_t newval; + + mask |= (1 << CP0VPEC0_MVP) | (1 << CP0VPEC0_VPA); + newval = (other->CP0_VPEConf0 & ~mask) | (arg1 & mask); + + /* TODO: TC exclusive handling due to ERL/EXL. */ + other->CP0_VPEConf0 = newval; +} + +void helper_mtc0_vpeconf1(CPUMIPSState *env, target_ulong arg1) +{ + uint32_t mask = 0; + uint32_t newval; + + if (env->mvp->CP0_MVPControl & (1 << CP0MVPCo_VPC)) + mask |= (0xff << CP0VPEC1_NCX) | (0xff << CP0VPEC1_NCP2) | + (0xff << CP0VPEC1_NCP1); + newval = (env->CP0_VPEConf1 & ~mask) | (arg1 & mask); + + /* UDI not implemented. */ + /* CP2 not implemented. */ + + /* TODO: Handle FPU (CP1) binding. */ + + env->CP0_VPEConf1 = newval; +} + +void helper_mtc0_yqmask(CPUMIPSState *env, target_ulong arg1) +{ + /* Yield qualifier inputs not implemented. */ + env->CP0_YQMask = 0x00000000; +} + +void helper_mtc0_vpeopt(CPUMIPSState *env, target_ulong arg1) +{ + env->CP0_VPEOpt = arg1 & 0x0000ffff; +} + +#define MTC0_ENTRYLO_MASK(env) ((env->PAMask >> 6) & 0x3FFFFFFF) + +void helper_mtc0_entrylo0(CPUMIPSState *env, target_ulong arg1) +{ + /* 1k pages not implemented */ + target_ulong rxi = arg1 & (env->CP0_PageGrain & (3u << CP0PG_XIE)); + env->CP0_EntryLo0 = (arg1 & MTC0_ENTRYLO_MASK(env)) + | (rxi << (CP0EnLo_XI - 30)); +} + +#if defined(TARGET_MIPS64) +#define DMTC0_ENTRYLO_MASK(env) (env->PAMask >> 6) + +void helper_dmtc0_entrylo0(CPUMIPSState *env, uint64_t arg1) +{ + uint64_t rxi = arg1 & ((env->CP0_PageGrain & (3ull << CP0PG_XIE)) << 32); + env->CP0_EntryLo0 = (arg1 & DMTC0_ENTRYLO_MASK(env)) | rxi; +} +#endif + +void helper_mtc0_tcstatus(CPUMIPSState *env, target_ulong arg1) +{ + uint32_t mask = env->CP0_TCStatus_rw_bitmask; + uint32_t newval; + + newval = (env->active_tc.CP0_TCStatus & ~mask) | (arg1 & mask); + + env->active_tc.CP0_TCStatus = newval; + sync_c0_tcstatus(env, env->current_tc, newval); +} + +void helper_mttc0_tcstatus(CPUMIPSState *env, target_ulong arg1) +{ + int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); + CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); + + if (other_tc == other->current_tc) { + other->active_tc.CP0_TCStatus = arg1; + } else { + other->tcs[other_tc].CP0_TCStatus = arg1; + } + sync_c0_tcstatus(other, other_tc, arg1); +} + +void helper_mtc0_tcbind(CPUMIPSState *env, target_ulong arg1) +{ + uint32_t mask = (1 << CP0TCBd_TBE); + uint32_t newval; + + if (env->mvp->CP0_MVPControl & (1 << CP0MVPCo_VPC)) { + mask |= (1 << CP0TCBd_CurVPE); + } + newval = (env->active_tc.CP0_TCBind & ~mask) | (arg1 & mask); + env->active_tc.CP0_TCBind = newval; +} + +void helper_mttc0_tcbind(CPUMIPSState *env, target_ulong arg1) +{ + int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); + uint32_t mask = (1 << CP0TCBd_TBE); + uint32_t newval; + CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); + + if (other->mvp->CP0_MVPControl & (1 << CP0MVPCo_VPC)) { + mask |= (1 << CP0TCBd_CurVPE); + } + if (other_tc == other->current_tc) { + newval = (other->active_tc.CP0_TCBind & ~mask) | (arg1 & mask); + other->active_tc.CP0_TCBind = newval; + } else { + newval = (other->tcs[other_tc].CP0_TCBind & ~mask) | (arg1 & mask); + other->tcs[other_tc].CP0_TCBind = newval; + } +} + +void helper_mtc0_tcrestart(CPUMIPSState *env, target_ulong arg1) +{ + env->active_tc.PC = arg1; + env->active_tc.CP0_TCStatus &= ~(1 << CP0TCSt_TDS); + env->CP0_LLAddr = 0; + env->lladdr = 0; + /* MIPS16 not implemented. */ +} + +void helper_mttc0_tcrestart(CPUMIPSState *env, target_ulong arg1) +{ + int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); + CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); + + if (other_tc == other->current_tc) { + other->active_tc.PC = arg1; + other->active_tc.CP0_TCStatus &= ~(1 << CP0TCSt_TDS); + other->CP0_LLAddr = 0; + other->lladdr = 0; + /* MIPS16 not implemented. */ + } else { + other->tcs[other_tc].PC = arg1; + other->tcs[other_tc].CP0_TCStatus &= ~(1 << CP0TCSt_TDS); + other->CP0_LLAddr = 0; + other->lladdr = 0; + /* MIPS16 not implemented. */ + } +} + +void helper_mtc0_tchalt(CPUMIPSState *env, target_ulong arg1) +{ + MIPSCPU *cpu = env_archcpu(env); + + env->active_tc.CP0_TCHalt = arg1 & 0x1; + + /* TODO: Halt TC / Restart (if allocated+active) TC. */ + if (env->active_tc.CP0_TCHalt & 1) { + mips_tc_sleep(cpu, env->current_tc); + } else { + mips_tc_wake(cpu, env->current_tc); + } +} + +void helper_mttc0_tchalt(CPUMIPSState *env, target_ulong arg1) +{ + int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); + CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); + MIPSCPU *other_cpu = env_archcpu(other); + + /* TODO: Halt TC / Restart (if allocated+active) TC. */ + + if (other_tc == other->current_tc) { + other->active_tc.CP0_TCHalt = arg1; + } else { + other->tcs[other_tc].CP0_TCHalt = arg1; + } + + if (arg1 & 1) { + mips_tc_sleep(other_cpu, other_tc); + } else { + mips_tc_wake(other_cpu, other_tc); + } +} + +void helper_mtc0_tccontext(CPUMIPSState *env, target_ulong arg1) +{ + env->active_tc.CP0_TCContext = arg1; +} + +void helper_mttc0_tccontext(CPUMIPSState *env, target_ulong arg1) +{ + int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); + CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); + + if (other_tc == other->current_tc) { + other->active_tc.CP0_TCContext = arg1; + } else { + other->tcs[other_tc].CP0_TCContext = arg1; + } +} + +void helper_mtc0_tcschedule(CPUMIPSState *env, target_ulong arg1) +{ + env->active_tc.CP0_TCSchedule = arg1; +} + +void helper_mttc0_tcschedule(CPUMIPSState *env, target_ulong arg1) +{ + int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); + CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); + + if (other_tc == other->current_tc) { + other->active_tc.CP0_TCSchedule = arg1; + } else { + other->tcs[other_tc].CP0_TCSchedule = arg1; + } +} + +void helper_mtc0_tcschefback(CPUMIPSState *env, target_ulong arg1) +{ + env->active_tc.CP0_TCScheFBack = arg1; +} + +void helper_mttc0_tcschefback(CPUMIPSState *env, target_ulong arg1) +{ + int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); + CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); + + if (other_tc == other->current_tc) { + other->active_tc.CP0_TCScheFBack = arg1; + } else { + other->tcs[other_tc].CP0_TCScheFBack = arg1; + } +} + +void helper_mtc0_entrylo1(CPUMIPSState *env, target_ulong arg1) +{ + /* 1k pages not implemented */ + target_ulong rxi = arg1 & (env->CP0_PageGrain & (3u << CP0PG_XIE)); + env->CP0_EntryLo1 = (arg1 & MTC0_ENTRYLO_MASK(env)) + | (rxi << (CP0EnLo_XI - 30)); +} + +#if defined(TARGET_MIPS64) +void helper_dmtc0_entrylo1(CPUMIPSState *env, uint64_t arg1) +{ + uint64_t rxi = arg1 & ((env->CP0_PageGrain & (3ull << CP0PG_XIE)) << 32); + env->CP0_EntryLo1 = (arg1 & DMTC0_ENTRYLO_MASK(env)) | rxi; +} +#endif + +void helper_mtc0_context(CPUMIPSState *env, target_ulong arg1) +{ + env->CP0_Context = (env->CP0_Context & 0x007FFFFF) | (arg1 & ~0x007FFFFF); +} + +void helper_mtc0_memorymapid(CPUMIPSState *env, target_ulong arg1) +{ + int32_t old; + old = env->CP0_MemoryMapID; + env->CP0_MemoryMapID = (int32_t) arg1; + /* If the MemoryMapID changes, flush qemu's TLB. */ + if (old != env->CP0_MemoryMapID) { + cpu_mips_tlb_flush(env); + } +} + +void update_pagemask(CPUMIPSState *env, target_ulong arg1, int32_t *pagemask) +{ + uint64_t mask = arg1 >> (TARGET_PAGE_BITS + 1); + if (!(env->insn_flags & ISA_MIPS32R6) || (arg1 == ~0) || + (mask == 0x0000 || mask == 0x0003 || mask == 0x000F || + mask == 0x003F || mask == 0x00FF || mask == 0x03FF || + mask == 0x0FFF || mask == 0x3FFF || mask == 0xFFFF)) { + env->CP0_PageMask = arg1 & (0x1FFFFFFF & (TARGET_PAGE_MASK << 1)); + } +} + +void helper_mtc0_pagemask(CPUMIPSState *env, target_ulong arg1) +{ + update_pagemask(env, arg1, &env->CP0_PageMask); +} + +void helper_mtc0_pagegrain(CPUMIPSState *env, target_ulong arg1) +{ + /* SmartMIPS not implemented */ + /* 1k pages not implemented */ + env->CP0_PageGrain = (arg1 & env->CP0_PageGrain_rw_bitmask) | + (env->CP0_PageGrain & ~env->CP0_PageGrain_rw_bitmask); + compute_hflags(env); + restore_pamask(env); +} + +void helper_mtc0_segctl0(CPUMIPSState *env, target_ulong arg1) +{ + CPUState *cs = env_cpu(env); + + env->CP0_SegCtl0 = arg1 & CP0SC0_MASK; + tlb_flush(cs); +} + +void helper_mtc0_segctl1(CPUMIPSState *env, target_ulong arg1) +{ + CPUState *cs = env_cpu(env); + + env->CP0_SegCtl1 = arg1 & CP0SC1_MASK; + tlb_flush(cs); +} + +void helper_mtc0_segctl2(CPUMIPSState *env, target_ulong arg1) +{ + CPUState *cs = env_cpu(env); + + env->CP0_SegCtl2 = arg1 & CP0SC2_MASK; + tlb_flush(cs); +} + +void helper_mtc0_pwfield(CPUMIPSState *env, target_ulong arg1) +{ +#if defined(TARGET_MIPS64) + uint64_t mask = 0x3F3FFFFFFFULL; + uint32_t old_ptei = (env->CP0_PWField >> CP0PF_PTEI) & 0x3FULL; + uint32_t new_ptei = (arg1 >> CP0PF_PTEI) & 0x3FULL; + + if ((env->insn_flags & ISA_MIPS32R6)) { + if (((arg1 >> CP0PF_BDI) & 0x3FULL) < 12) { + mask &= ~(0x3FULL << CP0PF_BDI); + } + if (((arg1 >> CP0PF_GDI) & 0x3FULL) < 12) { + mask &= ~(0x3FULL << CP0PF_GDI); + } + if (((arg1 >> CP0PF_UDI) & 0x3FULL) < 12) { + mask &= ~(0x3FULL << CP0PF_UDI); + } + if (((arg1 >> CP0PF_MDI) & 0x3FULL) < 12) { + mask &= ~(0x3FULL << CP0PF_MDI); + } + if (((arg1 >> CP0PF_PTI) & 0x3FULL) < 12) { + mask &= ~(0x3FULL << CP0PF_PTI); + } + } + env->CP0_PWField = arg1 & mask; + + if ((new_ptei >= 32) || + ((env->insn_flags & ISA_MIPS32R6) && + (new_ptei == 0 || new_ptei == 1))) { + env->CP0_PWField = (env->CP0_PWField & ~0x3FULL) | + (old_ptei << CP0PF_PTEI); + } +#else + uint32_t mask = 0x3FFFFFFF; + uint32_t old_ptew = (env->CP0_PWField >> CP0PF_PTEW) & 0x3F; + uint32_t new_ptew = (arg1 >> CP0PF_PTEW) & 0x3F; + + if ((env->insn_flags & ISA_MIPS32R6)) { + if (((arg1 >> CP0PF_GDW) & 0x3F) < 12) { + mask &= ~(0x3F << CP0PF_GDW); + } + if (((arg1 >> CP0PF_UDW) & 0x3F) < 12) { + mask &= ~(0x3F << CP0PF_UDW); + } + if (((arg1 >> CP0PF_MDW) & 0x3F) < 12) { + mask &= ~(0x3F << CP0PF_MDW); + } + if (((arg1 >> CP0PF_PTW) & 0x3F) < 12) { + mask &= ~(0x3F << CP0PF_PTW); + } + } + env->CP0_PWField = arg1 & mask; + + if ((new_ptew >= 32) || + ((env->insn_flags & ISA_MIPS32R6) && + (new_ptew == 0 || new_ptew == 1))) { + env->CP0_PWField = (env->CP0_PWField & ~0x3F) | + (old_ptew << CP0PF_PTEW); + } +#endif +} + +void helper_mtc0_pwsize(CPUMIPSState *env, target_ulong arg1) +{ +#if defined(TARGET_MIPS64) + env->CP0_PWSize = arg1 & 0x3F7FFFFFFFULL; +#else + env->CP0_PWSize = arg1 & 0x3FFFFFFF; +#endif +} + +void helper_mtc0_wired(CPUMIPSState *env, target_ulong arg1) +{ + if (env->insn_flags & ISA_MIPS32R6) { + if (arg1 < env->tlb->nb_tlb) { + env->CP0_Wired = arg1; + } + } else { + env->CP0_Wired = arg1 % env->tlb->nb_tlb; + } +} + +void helper_mtc0_pwctl(CPUMIPSState *env, target_ulong arg1) +{ +#if defined(TARGET_MIPS64) + /* PWEn = 0. Hardware page table walking is not implemented. */ + env->CP0_PWCtl = (env->CP0_PWCtl & 0x000000C0) | (arg1 & 0x5C00003F); +#else + env->CP0_PWCtl = (arg1 & 0x800000FF); +#endif +} + +void helper_mtc0_srsconf0(CPUMIPSState *env, target_ulong arg1) +{ + env->CP0_SRSConf0 |= arg1 & env->CP0_SRSConf0_rw_bitmask; +} + +void helper_mtc0_srsconf1(CPUMIPSState *env, target_ulong arg1) +{ + env->CP0_SRSConf1 |= arg1 & env->CP0_SRSConf1_rw_bitmask; +} + +void helper_mtc0_srsconf2(CPUMIPSState *env, target_ulong arg1) +{ + env->CP0_SRSConf2 |= arg1 & env->CP0_SRSConf2_rw_bitmask; +} + +void helper_mtc0_srsconf3(CPUMIPSState *env, target_ulong arg1) +{ + env->CP0_SRSConf3 |= arg1 & env->CP0_SRSConf3_rw_bitmask; +} + +void helper_mtc0_srsconf4(CPUMIPSState *env, target_ulong arg1) +{ + env->CP0_SRSConf4 |= arg1 & env->CP0_SRSConf4_rw_bitmask; +} + +void helper_mtc0_hwrena(CPUMIPSState *env, target_ulong arg1) +{ + uint32_t mask = 0x0000000F; + + if ((env->CP0_Config1 & (1 << CP0C1_PC)) && + (env->insn_flags & ISA_MIPS32R6)) { + mask |= (1 << 4); + } + if (env->insn_flags & ISA_MIPS32R6) { + mask |= (1 << 5); + } + if (env->CP0_Config3 & (1 << CP0C3_ULRI)) { + mask |= (1 << 29); + + if (arg1 & (1 << 29)) { + env->hflags |= MIPS_HFLAG_HWRENA_ULR; + } else { + env->hflags &= ~MIPS_HFLAG_HWRENA_ULR; + } + } + + env->CP0_HWREna = arg1 & mask; +} + +void helper_mtc0_count(CPUMIPSState *env, target_ulong arg1) +{ + cpu_mips_store_count(env, arg1); +} + +void helper_mtc0_saari(CPUMIPSState *env, target_ulong arg1) +{ + uint32_t target = arg1 & 0x3f; + if (target <= 1) { + env->CP0_SAARI = target; + } +} + +void helper_mtc0_saar(CPUMIPSState *env, target_ulong arg1) +{ + uint32_t target = env->CP0_SAARI & 0x3f; + if (target < 2) { + env->CP0_SAAR[target] = arg1 & 0x00000ffffffff03fULL; + switch (target) { + case 0: + if (env->itu) { + itc_reconfigure(env->itu); + } + break; + } + } +} + +void helper_mthc0_saar(CPUMIPSState *env, target_ulong arg1) +{ + uint32_t target = env->CP0_SAARI & 0x3f; + if (target < 2) { + env->CP0_SAAR[target] = + (((uint64_t) arg1 << 32) & 0x00000fff00000000ULL) | + (env->CP0_SAAR[target] & 0x00000000ffffffffULL); + switch (target) { + case 0: + if (env->itu) { + itc_reconfigure(env->itu); + } + break; + } + } +} + +void helper_mtc0_entryhi(CPUMIPSState *env, target_ulong arg1) +{ + target_ulong old, val, mask; + mask = (TARGET_PAGE_MASK << 1) | env->CP0_EntryHi_ASID_mask; + if (((env->CP0_Config4 >> CP0C4_IE) & 0x3) >= 2) { + mask |= 1 << CP0EnHi_EHINV; + } + + /* 1k pages not implemented */ +#if defined(TARGET_MIPS64) + if (env->insn_flags & ISA_MIPS32R6) { + int entryhi_r = extract64(arg1, 62, 2); + int config0_at = extract32(env->CP0_Config0, 13, 2); + bool no_supervisor = (env->CP0_Status_rw_bitmask & 0x8) == 0; + if ((entryhi_r == 2) || + (entryhi_r == 1 && (no_supervisor || config0_at == 1))) { + /* skip EntryHi.R field if new value is reserved */ + mask &= ~(0x3ull << 62); + } + } + mask &= env->SEGMask; +#endif + old = env->CP0_EntryHi; + val = (arg1 & mask) | (old & ~mask); + env->CP0_EntryHi = val; + if (env->CP0_Config3 & (1 << CP0C3_MT)) { + sync_c0_entryhi(env, env->current_tc); + } + /* If the ASID changes, flush qemu's TLB. */ + if ((old & env->CP0_EntryHi_ASID_mask) != + (val & env->CP0_EntryHi_ASID_mask)) { + tlb_flush(env_cpu(env)); + } +} + +void helper_mttc0_entryhi(CPUMIPSState *env, target_ulong arg1) +{ + int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); + CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); + + other->CP0_EntryHi = arg1; + sync_c0_entryhi(other, other_tc); +} + +void helper_mtc0_compare(CPUMIPSState *env, target_ulong arg1) +{ + cpu_mips_store_compare(env, arg1); +} + +void helper_mtc0_status(CPUMIPSState *env, target_ulong arg1) +{ + uint32_t val, old; + + old = env->CP0_Status; + cpu_mips_store_status(env, arg1); + val = env->CP0_Status; + + if (qemu_loglevel_mask(CPU_LOG_EXEC)) { + qemu_log("Status %08x (%08x) => %08x (%08x) Cause %08x", + old, old & env->CP0_Cause & CP0Ca_IP_mask, + val, val & env->CP0_Cause & CP0Ca_IP_mask, + env->CP0_Cause); + switch (cpu_mmu_index(env, false)) { + case 3: + qemu_log(", ERL\n"); + break; + case MIPS_HFLAG_UM: + qemu_log(", UM\n"); + break; + case MIPS_HFLAG_SM: + qemu_log(", SM\n"); + break; + case MIPS_HFLAG_KM: + qemu_log("\n"); + break; + default: + cpu_abort(env_cpu(env), "Invalid MMU mode!\n"); + break; + } + } +} + +void helper_mttc0_status(CPUMIPSState *env, target_ulong arg1) +{ + int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); + uint32_t mask = env->CP0_Status_rw_bitmask & ~0xf1000018; + CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); + + other->CP0_Status = (other->CP0_Status & ~mask) | (arg1 & mask); + sync_c0_status(env, other, other_tc); +} + +void helper_mtc0_intctl(CPUMIPSState *env, target_ulong arg1) +{ + env->CP0_IntCtl = (env->CP0_IntCtl & ~0x000003e0) | (arg1 & 0x000003e0); +} + +void helper_mtc0_srsctl(CPUMIPSState *env, target_ulong arg1) +{ + uint32_t mask = (0xf << CP0SRSCtl_ESS) | (0xf << CP0SRSCtl_PSS); + env->CP0_SRSCtl = (env->CP0_SRSCtl & ~mask) | (arg1 & mask); +} + +void helper_mtc0_cause(CPUMIPSState *env, target_ulong arg1) +{ + cpu_mips_store_cause(env, arg1); +} + +void helper_mttc0_cause(CPUMIPSState *env, target_ulong arg1) +{ + int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); + CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); + + cpu_mips_store_cause(other, arg1); +} + +target_ulong helper_mftc0_epc(CPUMIPSState *env) +{ + int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); + CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); + + return other->CP0_EPC; +} + +target_ulong helper_mftc0_ebase(CPUMIPSState *env) +{ + int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); + CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); + + return other->CP0_EBase; +} + +void helper_mtc0_ebase(CPUMIPSState *env, target_ulong arg1) +{ + target_ulong mask = 0x3FFFF000 | env->CP0_EBaseWG_rw_bitmask; + if (arg1 & env->CP0_EBaseWG_rw_bitmask) { + mask |= ~0x3FFFFFFF; + } + env->CP0_EBase = (env->CP0_EBase & ~mask) | (arg1 & mask); +} + +void helper_mttc0_ebase(CPUMIPSState *env, target_ulong arg1) +{ + int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); + CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); + target_ulong mask = 0x3FFFF000 | env->CP0_EBaseWG_rw_bitmask; + if (arg1 & env->CP0_EBaseWG_rw_bitmask) { + mask |= ~0x3FFFFFFF; + } + other->CP0_EBase = (other->CP0_EBase & ~mask) | (arg1 & mask); +} + +target_ulong helper_mftc0_configx(CPUMIPSState *env, target_ulong idx) +{ + int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); + CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); + + switch (idx) { + case 0: return other->CP0_Config0; + case 1: return other->CP0_Config1; + case 2: return other->CP0_Config2; + case 3: return other->CP0_Config3; + /* 4 and 5 are reserved. */ + case 6: return other->CP0_Config6; + case 7: return other->CP0_Config7; + default: + break; + } + return 0; +} + +void helper_mtc0_config0(CPUMIPSState *env, target_ulong arg1) +{ + env->CP0_Config0 = (env->CP0_Config0 & 0x81FFFFF8) | (arg1 & 0x00000007); +} + +void helper_mtc0_config2(CPUMIPSState *env, target_ulong arg1) +{ + /* tertiary/secondary caches not implemented */ + env->CP0_Config2 = (env->CP0_Config2 & 0x8FFF0FFF); +} + +void helper_mtc0_config3(CPUMIPSState *env, target_ulong arg1) +{ + if (env->insn_flags & ASE_MICROMIPS) { + env->CP0_Config3 = (env->CP0_Config3 & ~(1 << CP0C3_ISA_ON_EXC)) | + (arg1 & (1 << CP0C3_ISA_ON_EXC)); + } +} + +void helper_mtc0_config4(CPUMIPSState *env, target_ulong arg1) +{ + env->CP0_Config4 = (env->CP0_Config4 & (~env->CP0_Config4_rw_bitmask)) | + (arg1 & env->CP0_Config4_rw_bitmask); +} + +void helper_mtc0_config5(CPUMIPSState *env, target_ulong arg1) +{ + env->CP0_Config5 = (env->CP0_Config5 & (~env->CP0_Config5_rw_bitmask)) | + (arg1 & env->CP0_Config5_rw_bitmask); + env->CP0_EntryHi_ASID_mask = (env->CP0_Config5 & (1 << CP0C5_MI)) ? + 0x0 : (env->CP0_Config4 & (1 << CP0C4_AE)) ? 0x3ff : 0xff; + compute_hflags(env); +} + +void helper_mtc0_lladdr(CPUMIPSState *env, target_ulong arg1) +{ + target_long mask = env->CP0_LLAddr_rw_bitmask; + arg1 = arg1 << env->CP0_LLAddr_shift; + env->CP0_LLAddr = (env->CP0_LLAddr & ~mask) | (arg1 & mask); +} + +#define MTC0_MAAR_MASK(env) \ + ((0x1ULL << 63) | ((env->PAMask >> 4) & ~0xFFFull) | 0x3) + +void helper_mtc0_maar(CPUMIPSState *env, target_ulong arg1) +{ + env->CP0_MAAR[env->CP0_MAARI] = arg1 & MTC0_MAAR_MASK(env); +} + +void helper_mthc0_maar(CPUMIPSState *env, target_ulong arg1) +{ + env->CP0_MAAR[env->CP0_MAARI] = + (((uint64_t) arg1 << 32) & MTC0_MAAR_MASK(env)) | + (env->CP0_MAAR[env->CP0_MAARI] & 0x00000000ffffffffULL); +} + +void helper_mtc0_maari(CPUMIPSState *env, target_ulong arg1) +{ + int index = arg1 & 0x3f; + if (index == 0x3f) { + /* + * Software may write all ones to INDEX to determine the + * maximum value supported. + */ + env->CP0_MAARI = MIPS_MAAR_MAX - 1; + } else if (index < MIPS_MAAR_MAX) { + env->CP0_MAARI = index; + } + /* + * Other than the all ones, if the value written is not supported, + * then INDEX is unchanged from its previous value. + */ +} + +void helper_mtc0_watchlo(CPUMIPSState *env, target_ulong arg1, uint32_t sel) +{ + /* + * Watch exceptions for instructions, data loads, data stores + * not implemented. + */ + env->CP0_WatchLo[sel] = (arg1 & ~0x7); +} + +void helper_mtc0_watchhi(CPUMIPSState *env, target_ulong arg1, uint32_t sel) +{ + uint64_t mask = 0x40000FF8 | (env->CP0_EntryHi_ASID_mask << CP0WH_ASID); + if ((env->CP0_Config5 >> CP0C5_MI) & 1) { + mask |= 0xFFFFFFFF00000000ULL; /* MMID */ + } + env->CP0_WatchHi[sel] = arg1 & mask; + env->CP0_WatchHi[sel] &= ~(env->CP0_WatchHi[sel] & arg1 & 0x7); +} + +void helper_mthc0_watchhi(CPUMIPSState *env, target_ulong arg1, uint32_t sel) +{ + env->CP0_WatchHi[sel] = ((uint64_t) (arg1) << 32) | + (env->CP0_WatchHi[sel] & 0x00000000ffffffffULL); +} + +void helper_mtc0_xcontext(CPUMIPSState *env, target_ulong arg1) +{ + target_ulong mask = (1ULL << (env->SEGBITS - 7)) - 1; + env->CP0_XContext = (env->CP0_XContext & mask) | (arg1 & ~mask); +} + +void helper_mtc0_framemask(CPUMIPSState *env, target_ulong arg1) +{ + env->CP0_Framemask = arg1; /* XXX */ +} + +void helper_mtc0_debug(CPUMIPSState *env, target_ulong arg1) +{ + env->CP0_Debug = (env->CP0_Debug & 0x8C03FC1F) | (arg1 & 0x13300120); + if (arg1 & (1 << CP0DB_DM)) { + env->hflags |= MIPS_HFLAG_DM; + } else { + env->hflags &= ~MIPS_HFLAG_DM; + } +} + +void helper_mttc0_debug(CPUMIPSState *env, target_ulong arg1) +{ + int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); + uint32_t val = arg1 & ((1 << CP0DB_SSt) | (1 << CP0DB_Halt)); + CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); + + /* XXX: Might be wrong, check with EJTAG spec. */ + if (other_tc == other->current_tc) { + other->active_tc.CP0_Debug_tcstatus = val; + } else { + other->tcs[other_tc].CP0_Debug_tcstatus = val; + } + other->CP0_Debug = (other->CP0_Debug & + ((1 << CP0DB_SSt) | (1 << CP0DB_Halt))) | + (arg1 & ~((1 << CP0DB_SSt) | (1 << CP0DB_Halt))); +} + +void helper_mtc0_performance0(CPUMIPSState *env, target_ulong arg1) +{ + env->CP0_Performance0 = arg1 & 0x000007ff; +} + +void helper_mtc0_errctl(CPUMIPSState *env, target_ulong arg1) +{ + int32_t wst = arg1 & (1 << CP0EC_WST); + int32_t spr = arg1 & (1 << CP0EC_SPR); + int32_t itc = env->itc_tag ? (arg1 & (1 << CP0EC_ITC)) : 0; + + env->CP0_ErrCtl = wst | spr | itc; + + if (itc && !wst && !spr) { + env->hflags |= MIPS_HFLAG_ITC_CACHE; + } else { + env->hflags &= ~MIPS_HFLAG_ITC_CACHE; + } +} + +void helper_mtc0_taglo(CPUMIPSState *env, target_ulong arg1) +{ + if (env->hflags & MIPS_HFLAG_ITC_CACHE) { + /* + * If CACHE instruction is configured for ITC tags then make all + * CP0.TagLo bits writable. The actual write to ITC Configuration + * Tag will take care of the read-only bits. + */ + env->CP0_TagLo = arg1; + } else { + env->CP0_TagLo = arg1 & 0xFFFFFCF6; + } +} + +void helper_mtc0_datalo(CPUMIPSState *env, target_ulong arg1) +{ + env->CP0_DataLo = arg1; /* XXX */ +} + +void helper_mtc0_taghi(CPUMIPSState *env, target_ulong arg1) +{ + env->CP0_TagHi = arg1; /* XXX */ +} + +void helper_mtc0_datahi(CPUMIPSState *env, target_ulong arg1) +{ + env->CP0_DataHi = arg1; /* XXX */ +} + +/* MIPS MT functions */ +target_ulong helper_mftgpr(CPUMIPSState *env, uint32_t sel) +{ + int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); + CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); + + if (other_tc == other->current_tc) { + return other->active_tc.gpr[sel]; + } else { + return other->tcs[other_tc].gpr[sel]; + } +} + +target_ulong helper_mftlo(CPUMIPSState *env, uint32_t sel) +{ + int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); + CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); + + if (other_tc == other->current_tc) { + return other->active_tc.LO[sel]; + } else { + return other->tcs[other_tc].LO[sel]; + } +} + +target_ulong helper_mfthi(CPUMIPSState *env, uint32_t sel) +{ + int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); + CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); + + if (other_tc == other->current_tc) { + return other->active_tc.HI[sel]; + } else { + return other->tcs[other_tc].HI[sel]; + } +} + +target_ulong helper_mftacx(CPUMIPSState *env, uint32_t sel) +{ + int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); + CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); + + if (other_tc == other->current_tc) { + return other->active_tc.ACX[sel]; + } else { + return other->tcs[other_tc].ACX[sel]; + } +} + +target_ulong helper_mftdsp(CPUMIPSState *env) +{ + int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); + CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); + + if (other_tc == other->current_tc) { + return other->active_tc.DSPControl; + } else { + return other->tcs[other_tc].DSPControl; + } +} + +void helper_mttgpr(CPUMIPSState *env, target_ulong arg1, uint32_t sel) +{ + int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); + CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); + + if (other_tc == other->current_tc) { + other->active_tc.gpr[sel] = arg1; + } else { + other->tcs[other_tc].gpr[sel] = arg1; + } +} + +void helper_mttlo(CPUMIPSState *env, target_ulong arg1, uint32_t sel) +{ + int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); + CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); + + if (other_tc == other->current_tc) { + other->active_tc.LO[sel] = arg1; + } else { + other->tcs[other_tc].LO[sel] = arg1; + } +} + +void helper_mtthi(CPUMIPSState *env, target_ulong arg1, uint32_t sel) +{ + int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); + CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); + + if (other_tc == other->current_tc) { + other->active_tc.HI[sel] = arg1; + } else { + other->tcs[other_tc].HI[sel] = arg1; + } +} + +void helper_mttacx(CPUMIPSState *env, target_ulong arg1, uint32_t sel) +{ + int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); + CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); + + if (other_tc == other->current_tc) { + other->active_tc.ACX[sel] = arg1; + } else { + other->tcs[other_tc].ACX[sel] = arg1; + } +} + +void helper_mttdsp(CPUMIPSState *env, target_ulong arg1) +{ + int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); + CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); + + if (other_tc == other->current_tc) { + other->active_tc.DSPControl = arg1; + } else { + other->tcs[other_tc].DSPControl = arg1; + } +} + +/* MIPS MT functions */ +target_ulong helper_dmt(void) +{ + /* TODO */ + return 0; +} + +target_ulong helper_emt(void) +{ + /* TODO */ + return 0; +} + +target_ulong helper_dvpe(CPUMIPSState *env) +{ + CPUState *other_cs = first_cpu; + target_ulong prev = env->mvp->CP0_MVPControl; + + CPU_FOREACH(other_cs) { + MIPSCPU *other_cpu = MIPS_CPU(other_cs); + /* Turn off all VPEs except the one executing the dvpe. */ + if (&other_cpu->env != env) { + other_cpu->env.mvp->CP0_MVPControl &= ~(1 << CP0MVPCo_EVP); + mips_vpe_sleep(other_cpu); + } + } + return prev; +} + +target_ulong helper_evpe(CPUMIPSState *env) +{ + CPUState *other_cs = first_cpu; + target_ulong prev = env->mvp->CP0_MVPControl; + + CPU_FOREACH(other_cs) { + MIPSCPU *other_cpu = MIPS_CPU(other_cs); + + if (&other_cpu->env != env + /* If the VPE is WFI, don't disturb its sleep. */ + && !mips_vpe_is_wfi(other_cpu)) { + /* Enable the VPE. */ + other_cpu->env.mvp->CP0_MVPControl |= (1 << CP0MVPCo_EVP); + mips_vpe_wake(other_cpu); /* And wake it up. */ + } + } + return prev; +} +#endif /* !CONFIG_USER_ONLY */ + +/* R6 Multi-threading */ +#ifndef CONFIG_USER_ONLY +target_ulong helper_dvp(CPUMIPSState *env) +{ + CPUState *other_cs = first_cpu; + target_ulong prev = env->CP0_VPControl; + + if (!((env->CP0_VPControl >> CP0VPCtl_DIS) & 1)) { + CPU_FOREACH(other_cs) { + MIPSCPU *other_cpu = MIPS_CPU(other_cs); + /* Turn off all VPs except the one executing the dvp. */ + if (&other_cpu->env != env) { + mips_vpe_sleep(other_cpu); + } + } + env->CP0_VPControl |= (1 << CP0VPCtl_DIS); + } + return prev; +} + +target_ulong helper_evp(CPUMIPSState *env) +{ + CPUState *other_cs = first_cpu; + target_ulong prev = env->CP0_VPControl; + + if ((env->CP0_VPControl >> CP0VPCtl_DIS) & 1) { + CPU_FOREACH(other_cs) { + MIPSCPU *other_cpu = MIPS_CPU(other_cs); + if ((&other_cpu->env != env) && !mips_vp_is_wfi(other_cpu)) { + /* + * If the VP is WFI, don't disturb its sleep. + * Otherwise, wake it up. + */ + mips_vpe_wake(other_cpu); + } + } + env->CP0_VPControl &= ~(1 << CP0VPCtl_DIS); + } + return prev; +} +#endif /* !CONFIG_USER_ONLY */ diff --git a/target/mips/cpu-qom.h b/target/mips/cpu-qom.h index a430c0fe4b..9d0df6c034 100644 --- a/target/mips/cpu-qom.h +++ b/target/mips/cpu-qom.h @@ -48,7 +48,7 @@ typedef struct MIPSCPUClass { /*< public >*/ DeviceRealize parent_realize; - void (*parent_reset)(CPUState *cpu); + DeviceReset parent_reset; const struct mips_def_t *cpu_def; } MIPSCPUClass; diff --git a/target/mips/cpu.c b/target/mips/cpu.c index bbcf7ca463..e86cd06548 100644 --- a/target/mips/cpu.c +++ b/target/mips/cpu.c @@ -96,14 +96,14 @@ static bool mips_cpu_has_work(CPUState *cs) return has_work; } -/* CPUClass::reset() */ -static void mips_cpu_reset(CPUState *s) +static void mips_cpu_reset(DeviceState *dev) { + CPUState *s = CPU(dev); MIPSCPU *cpu = MIPS_CPU(s); MIPSCPUClass *mcc = MIPS_CPU_GET_CLASS(cpu); CPUMIPSState *env = &cpu->env; - mcc->parent_reset(s); + mcc->parent_reset(dev); memset(env, 0, offsetof(CPUMIPSState, end_reset_fields)); @@ -189,8 +189,7 @@ static void mips_cpu_class_init(ObjectClass *c, void *data) device_class_set_parent_realize(dc, mips_cpu_realizefn, &mcc->parent_realize); - mcc->parent_reset = cc->reset; - cc->reset = mips_cpu_reset; + device_class_set_parent_reset(dc, mips_cpu_reset, &mcc->parent_reset); cc->class_by_name = mips_cpu_class_by_name; cc->has_work = mips_cpu_has_work; diff --git a/target/mips/cpu.h b/target/mips/cpu.h index ca00f41daf..94d01ea798 100644 --- a/target/mips/cpu.h +++ b/target/mips/cpu.h @@ -309,7 +309,7 @@ typedef struct mips_def_t mips_def_t; #define CP0_REG04__USERLOCAL 2 #define CP0_REG04__XCONTEXTCONFIG 3 #define CP0_REG04__DBGCONTEXTID 4 -#define CP0_REG00__MMID 5 +#define CP0_REG04__MMID 5 /* CP0 Register 05 */ #define CP0_REG05__PAGEMASK 0 #define CP0_REG05__PAGEGRAIN 1 @@ -961,7 +961,7 @@ struct CPUMIPSState { /* * CP0 Register 19 */ - int32_t CP0_WatchHi[8]; + uint64_t CP0_WatchHi[8]; #define CP0WH_ASID 16 /* * CP0 Register 20 @@ -1147,10 +1147,6 @@ extern uint32_t cpu_rddsp(uint32_t mask_num, CPUMIPSState *env); * MMU modes definitions. We carefully match the indices with our * hflags layout. */ -#define MMU_MODE0_SUFFIX _kernel -#define MMU_MODE1_SUFFIX _super -#define MMU_MODE2_SUFFIX _user -#define MMU_MODE3_SUFFIX _error #define MMU_USER_IDX 2 static inline int hflags_mmu_index(uint32_t hflags) diff --git a/target/mips/fpu_helper.c b/target/mips/fpu_helper.c new file mode 100644 index 0000000000..5287c86c61 --- /dev/null +++ b/target/mips/fpu_helper.c @@ -0,0 +1,1911 @@ +/* + * Helpers for emulation of FPU-related MIPS instructions. + * + * Copyright (C) 2004-2005 Jocelyn Mayer + * Copyright (C) 2020 Wave Computing, Inc. + * Copyright (C) 2020 Aleksandar Markovic + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + * + */ + +#include "qemu/osdep.h" +#include "qemu/main-loop.h" +#include "cpu.h" +#include "internal.h" +#include "qemu/host-utils.h" +#include "exec/helper-proto.h" +#include "exec/exec-all.h" +#include "exec/cpu_ldst.h" +#include "exec/memop.h" +#include "sysemu/kvm.h" +#include "fpu/softfloat.h" + + +/* Complex FPU operations which may need stack space. */ + +#define FLOAT_TWO32 make_float32(1 << 30) +#define FLOAT_TWO64 make_float64(1ULL << 62) + +#define FP_TO_INT32_OVERFLOW 0x7fffffff +#define FP_TO_INT64_OVERFLOW 0x7fffffffffffffffULL + +/* convert MIPS rounding mode in FCR31 to IEEE library */ +unsigned int ieee_rm[] = { + float_round_nearest_even, + float_round_to_zero, + float_round_up, + float_round_down +}; + +target_ulong helper_cfc1(CPUMIPSState *env, uint32_t reg) +{ + target_ulong arg1 = 0; + + switch (reg) { + case 0: + arg1 = (int32_t)env->active_fpu.fcr0; + break; + case 1: + /* UFR Support - Read Status FR */ + if (env->active_fpu.fcr0 & (1 << FCR0_UFRP)) { + if (env->CP0_Config5 & (1 << CP0C5_UFR)) { + arg1 = (int32_t) + ((env->CP0_Status & (1 << CP0St_FR)) >> CP0St_FR); + } else { + do_raise_exception(env, EXCP_RI, GETPC()); + } + } + break; + case 5: + /* FRE Support - read Config5.FRE bit */ + if (env->active_fpu.fcr0 & (1 << FCR0_FREP)) { + if (env->CP0_Config5 & (1 << CP0C5_UFE)) { + arg1 = (env->CP0_Config5 >> CP0C5_FRE) & 1; + } else { + helper_raise_exception(env, EXCP_RI); + } + } + break; + case 25: + arg1 = ((env->active_fpu.fcr31 >> 24) & 0xfe) | + ((env->active_fpu.fcr31 >> 23) & 0x1); + break; + case 26: + arg1 = env->active_fpu.fcr31 & 0x0003f07c; + break; + case 28: + arg1 = (env->active_fpu.fcr31 & 0x00000f83) | + ((env->active_fpu.fcr31 >> 22) & 0x4); + break; + default: + arg1 = (int32_t)env->active_fpu.fcr31; + break; + } + + return arg1; +} + +void helper_ctc1(CPUMIPSState *env, target_ulong arg1, uint32_t fs, uint32_t rt) +{ + switch (fs) { + case 1: + /* UFR Alias - Reset Status FR */ + if (!((env->active_fpu.fcr0 & (1 << FCR0_UFRP)) && (rt == 0))) { + return; + } + if (env->CP0_Config5 & (1 << CP0C5_UFR)) { + env->CP0_Status &= ~(1 << CP0St_FR); + compute_hflags(env); + } else { + do_raise_exception(env, EXCP_RI, GETPC()); + } + break; + case 4: + /* UNFR Alias - Set Status FR */ + if (!((env->active_fpu.fcr0 & (1 << FCR0_UFRP)) && (rt == 0))) { + return; + } + if (env->CP0_Config5 & (1 << CP0C5_UFR)) { + env->CP0_Status |= (1 << CP0St_FR); + compute_hflags(env); + } else { + do_raise_exception(env, EXCP_RI, GETPC()); + } + break; + case 5: + /* FRE Support - clear Config5.FRE bit */ + if (!((env->active_fpu.fcr0 & (1 << FCR0_FREP)) && (rt == 0))) { + return; + } + if (env->CP0_Config5 & (1 << CP0C5_UFE)) { + env->CP0_Config5 &= ~(1 << CP0C5_FRE); + compute_hflags(env); + } else { + helper_raise_exception(env, EXCP_RI); + } + break; + case 6: + /* FRE Support - set Config5.FRE bit */ + if (!((env->active_fpu.fcr0 & (1 << FCR0_FREP)) && (rt == 0))) { + return; + } + if (env->CP0_Config5 & (1 << CP0C5_UFE)) { + env->CP0_Config5 |= (1 << CP0C5_FRE); + compute_hflags(env); + } else { + helper_raise_exception(env, EXCP_RI); + } + break; + case 25: + if ((env->insn_flags & ISA_MIPS32R6) || (arg1 & 0xffffff00)) { + return; + } + env->active_fpu.fcr31 = (env->active_fpu.fcr31 & 0x017fffff) | + ((arg1 & 0xfe) << 24) | + ((arg1 & 0x1) << 23); + break; + case 26: + if (arg1 & 0x007c0000) { + return; + } + env->active_fpu.fcr31 = (env->active_fpu.fcr31 & 0xfffc0f83) | + (arg1 & 0x0003f07c); + break; + case 28: + if (arg1 & 0x007c0000) { + return; + } + env->active_fpu.fcr31 = (env->active_fpu.fcr31 & 0xfefff07c) | + (arg1 & 0x00000f83) | + ((arg1 & 0x4) << 22); + break; + case 31: + env->active_fpu.fcr31 = (arg1 & env->active_fpu.fcr31_rw_bitmask) | + (env->active_fpu.fcr31 & ~(env->active_fpu.fcr31_rw_bitmask)); + break; + default: + if (env->insn_flags & ISA_MIPS32R6) { + do_raise_exception(env, EXCP_RI, GETPC()); + } + return; + } + restore_fp_status(env); + set_float_exception_flags(0, &env->active_fpu.fp_status); + if ((GET_FP_ENABLE(env->active_fpu.fcr31) | 0x20) & + GET_FP_CAUSE(env->active_fpu.fcr31)) { + do_raise_exception(env, EXCP_FPE, GETPC()); + } +} + +int ieee_ex_to_mips(int xcpt) +{ + int ret = 0; + if (xcpt) { + if (xcpt & float_flag_invalid) { + ret |= FP_INVALID; + } + if (xcpt & float_flag_overflow) { + ret |= FP_OVERFLOW; + } + if (xcpt & float_flag_underflow) { + ret |= FP_UNDERFLOW; + } + if (xcpt & float_flag_divbyzero) { + ret |= FP_DIV0; + } + if (xcpt & float_flag_inexact) { + ret |= FP_INEXACT; + } + } + return ret; +} + +static inline void update_fcr31(CPUMIPSState *env, uintptr_t pc) +{ + int tmp = ieee_ex_to_mips(get_float_exception_flags( + &env->active_fpu.fp_status)); + + SET_FP_CAUSE(env->active_fpu.fcr31, tmp); + + if (tmp) { + set_float_exception_flags(0, &env->active_fpu.fp_status); + + if (GET_FP_ENABLE(env->active_fpu.fcr31) & tmp) { + do_raise_exception(env, EXCP_FPE, pc); + } else { + UPDATE_FP_FLAGS(env->active_fpu.fcr31, tmp); + } + } +} + +/* + * Float support. + * Single precition routines have a "s" suffix, double precision a + * "d" suffix, 32bit integer "w", 64bit integer "l", paired single "ps", + * paired single lower "pl", paired single upper "pu". + */ + +/* unary operations, modifying fp status */ +uint64_t helper_float_sqrt_d(CPUMIPSState *env, uint64_t fdt0) +{ + fdt0 = float64_sqrt(fdt0, &env->active_fpu.fp_status); + update_fcr31(env, GETPC()); + return fdt0; +} + +uint32_t helper_float_sqrt_s(CPUMIPSState *env, uint32_t fst0) +{ + fst0 = float32_sqrt(fst0, &env->active_fpu.fp_status); + update_fcr31(env, GETPC()); + return fst0; +} + +uint64_t helper_float_cvtd_s(CPUMIPSState *env, uint32_t fst0) +{ + uint64_t fdt2; + + fdt2 = float32_to_float64(fst0, &env->active_fpu.fp_status); + update_fcr31(env, GETPC()); + return fdt2; +} + +uint64_t helper_float_cvtd_w(CPUMIPSState *env, uint32_t wt0) +{ + uint64_t fdt2; + + fdt2 = int32_to_float64(wt0, &env->active_fpu.fp_status); + update_fcr31(env, GETPC()); + return fdt2; +} + +uint64_t helper_float_cvtd_l(CPUMIPSState *env, uint64_t dt0) +{ + uint64_t fdt2; + + fdt2 = int64_to_float64(dt0, &env->active_fpu.fp_status); + update_fcr31(env, GETPC()); + return fdt2; +} + +uint64_t helper_float_cvt_l_d(CPUMIPSState *env, uint64_t fdt0) +{ + uint64_t dt2; + + dt2 = float64_to_int64(fdt0, &env->active_fpu.fp_status); + if (get_float_exception_flags(&env->active_fpu.fp_status) + & (float_flag_invalid | float_flag_overflow)) { + dt2 = FP_TO_INT64_OVERFLOW; + } + update_fcr31(env, GETPC()); + return dt2; +} + +uint64_t helper_float_cvt_l_s(CPUMIPSState *env, uint32_t fst0) +{ + uint64_t dt2; + + dt2 = float32_to_int64(fst0, &env->active_fpu.fp_status); + if (get_float_exception_flags(&env->active_fpu.fp_status) + & (float_flag_invalid | float_flag_overflow)) { + dt2 = FP_TO_INT64_OVERFLOW; + } + update_fcr31(env, GETPC()); + return dt2; +} + +uint64_t helper_float_cvtps_pw(CPUMIPSState *env, uint64_t dt0) +{ + uint32_t fst2; + uint32_t fsth2; + + fst2 = int32_to_float32(dt0 & 0XFFFFFFFF, &env->active_fpu.fp_status); + fsth2 = int32_to_float32(dt0 >> 32, &env->active_fpu.fp_status); + update_fcr31(env, GETPC()); + return ((uint64_t)fsth2 << 32) | fst2; +} + +uint64_t helper_float_cvtpw_ps(CPUMIPSState *env, uint64_t fdt0) +{ + uint32_t wt2; + uint32_t wth2; + int excp, excph; + + wt2 = float32_to_int32(fdt0 & 0XFFFFFFFF, &env->active_fpu.fp_status); + excp = get_float_exception_flags(&env->active_fpu.fp_status); + if (excp & (float_flag_overflow | float_flag_invalid)) { + wt2 = FP_TO_INT32_OVERFLOW; + } + + set_float_exception_flags(0, &env->active_fpu.fp_status); + wth2 = float32_to_int32(fdt0 >> 32, &env->active_fpu.fp_status); + excph = get_float_exception_flags(&env->active_fpu.fp_status); + if (excph & (float_flag_overflow | float_flag_invalid)) { + wth2 = FP_TO_INT32_OVERFLOW; + } + + set_float_exception_flags(excp | excph, &env->active_fpu.fp_status); + update_fcr31(env, GETPC()); + + return ((uint64_t)wth2 << 32) | wt2; +} + +uint32_t helper_float_cvts_d(CPUMIPSState *env, uint64_t fdt0) +{ + uint32_t fst2; + + fst2 = float64_to_float32(fdt0, &env->active_fpu.fp_status); + update_fcr31(env, GETPC()); + return fst2; +} + +uint32_t helper_float_cvts_w(CPUMIPSState *env, uint32_t wt0) +{ + uint32_t fst2; + + fst2 = int32_to_float32(wt0, &env->active_fpu.fp_status); + update_fcr31(env, GETPC()); + return fst2; +} + +uint32_t helper_float_cvts_l(CPUMIPSState *env, uint64_t dt0) +{ + uint32_t fst2; + + fst2 = int64_to_float32(dt0, &env->active_fpu.fp_status); + update_fcr31(env, GETPC()); + return fst2; +} + +uint32_t helper_float_cvts_pl(CPUMIPSState *env, uint32_t wt0) +{ + uint32_t wt2; + + wt2 = wt0; + update_fcr31(env, GETPC()); + return wt2; +} + +uint32_t helper_float_cvts_pu(CPUMIPSState *env, uint32_t wth0) +{ + uint32_t wt2; + + wt2 = wth0; + update_fcr31(env, GETPC()); + return wt2; +} + +uint32_t helper_float_cvt_w_s(CPUMIPSState *env, uint32_t fst0) +{ + uint32_t wt2; + + wt2 = float32_to_int32(fst0, &env->active_fpu.fp_status); + if (get_float_exception_flags(&env->active_fpu.fp_status) + & (float_flag_invalid | float_flag_overflow)) { + wt2 = FP_TO_INT32_OVERFLOW; + } + update_fcr31(env, GETPC()); + return wt2; +} + +uint32_t helper_float_cvt_w_d(CPUMIPSState *env, uint64_t fdt0) +{ + uint32_t wt2; + + wt2 = float64_to_int32(fdt0, &env->active_fpu.fp_status); + if (get_float_exception_flags(&env->active_fpu.fp_status) + & (float_flag_invalid | float_flag_overflow)) { + wt2 = FP_TO_INT32_OVERFLOW; + } + update_fcr31(env, GETPC()); + return wt2; +} + +uint64_t helper_float_round_l_d(CPUMIPSState *env, uint64_t fdt0) +{ + uint64_t dt2; + + set_float_rounding_mode(float_round_nearest_even, + &env->active_fpu.fp_status); + dt2 = float64_to_int64(fdt0, &env->active_fpu.fp_status); + restore_rounding_mode(env); + if (get_float_exception_flags(&env->active_fpu.fp_status) + & (float_flag_invalid | float_flag_overflow)) { + dt2 = FP_TO_INT64_OVERFLOW; + } + update_fcr31(env, GETPC()); + return dt2; +} + +uint64_t helper_float_round_l_s(CPUMIPSState *env, uint32_t fst0) +{ + uint64_t dt2; + + set_float_rounding_mode(float_round_nearest_even, + &env->active_fpu.fp_status); + dt2 = float32_to_int64(fst0, &env->active_fpu.fp_status); + restore_rounding_mode(env); + if (get_float_exception_flags(&env->active_fpu.fp_status) + & (float_flag_invalid | float_flag_overflow)) { + dt2 = FP_TO_INT64_OVERFLOW; + } + update_fcr31(env, GETPC()); + return dt2; +} + +uint32_t helper_float_round_w_d(CPUMIPSState *env, uint64_t fdt0) +{ + uint32_t wt2; + + set_float_rounding_mode(float_round_nearest_even, + &env->active_fpu.fp_status); + wt2 = float64_to_int32(fdt0, &env->active_fpu.fp_status); + restore_rounding_mode(env); + if (get_float_exception_flags(&env->active_fpu.fp_status) + & (float_flag_invalid | float_flag_overflow)) { + wt2 = FP_TO_INT32_OVERFLOW; + } + update_fcr31(env, GETPC()); + return wt2; +} + +uint32_t helper_float_round_w_s(CPUMIPSState *env, uint32_t fst0) +{ + uint32_t wt2; + + set_float_rounding_mode(float_round_nearest_even, + &env->active_fpu.fp_status); + wt2 = float32_to_int32(fst0, &env->active_fpu.fp_status); + restore_rounding_mode(env); + if (get_float_exception_flags(&env->active_fpu.fp_status) + & (float_flag_invalid | float_flag_overflow)) { + wt2 = FP_TO_INT32_OVERFLOW; + } + update_fcr31(env, GETPC()); + return wt2; +} + +uint64_t helper_float_trunc_l_d(CPUMIPSState *env, uint64_t fdt0) +{ + uint64_t dt2; + + dt2 = float64_to_int64_round_to_zero(fdt0, + &env->active_fpu.fp_status); + if (get_float_exception_flags(&env->active_fpu.fp_status) + & (float_flag_invalid | float_flag_overflow)) { + dt2 = FP_TO_INT64_OVERFLOW; + } + update_fcr31(env, GETPC()); + return dt2; +} + +uint64_t helper_float_trunc_l_s(CPUMIPSState *env, uint32_t fst0) +{ + uint64_t dt2; + + dt2 = float32_to_int64_round_to_zero(fst0, &env->active_fpu.fp_status); + if (get_float_exception_flags(&env->active_fpu.fp_status) + & (float_flag_invalid | float_flag_overflow)) { + dt2 = FP_TO_INT64_OVERFLOW; + } + update_fcr31(env, GETPC()); + return dt2; +} + +uint32_t helper_float_trunc_w_d(CPUMIPSState *env, uint64_t fdt0) +{ + uint32_t wt2; + + wt2 = float64_to_int32_round_to_zero(fdt0, &env->active_fpu.fp_status); + if (get_float_exception_flags(&env->active_fpu.fp_status) + & (float_flag_invalid | float_flag_overflow)) { + wt2 = FP_TO_INT32_OVERFLOW; + } + update_fcr31(env, GETPC()); + return wt2; +} + +uint32_t helper_float_trunc_w_s(CPUMIPSState *env, uint32_t fst0) +{ + uint32_t wt2; + + wt2 = float32_to_int32_round_to_zero(fst0, &env->active_fpu.fp_status); + if (get_float_exception_flags(&env->active_fpu.fp_status) + & (float_flag_invalid | float_flag_overflow)) { + wt2 = FP_TO_INT32_OVERFLOW; + } + update_fcr31(env, GETPC()); + return wt2; +} + +uint64_t helper_float_ceil_l_d(CPUMIPSState *env, uint64_t fdt0) +{ + uint64_t dt2; + + set_float_rounding_mode(float_round_up, &env->active_fpu.fp_status); + dt2 = float64_to_int64(fdt0, &env->active_fpu.fp_status); + restore_rounding_mode(env); + if (get_float_exception_flags(&env->active_fpu.fp_status) + & (float_flag_invalid | float_flag_overflow)) { + dt2 = FP_TO_INT64_OVERFLOW; + } + update_fcr31(env, GETPC()); + return dt2; +} + +uint64_t helper_float_ceil_l_s(CPUMIPSState *env, uint32_t fst0) +{ + uint64_t dt2; + + set_float_rounding_mode(float_round_up, &env->active_fpu.fp_status); + dt2 = float32_to_int64(fst0, &env->active_fpu.fp_status); + restore_rounding_mode(env); + if (get_float_exception_flags(&env->active_fpu.fp_status) + & (float_flag_invalid | float_flag_overflow)) { + dt2 = FP_TO_INT64_OVERFLOW; + } + update_fcr31(env, GETPC()); + return dt2; +} + +uint32_t helper_float_ceil_w_d(CPUMIPSState *env, uint64_t fdt0) +{ + uint32_t wt2; + + set_float_rounding_mode(float_round_up, &env->active_fpu.fp_status); + wt2 = float64_to_int32(fdt0, &env->active_fpu.fp_status); + restore_rounding_mode(env); + if (get_float_exception_flags(&env->active_fpu.fp_status) + & (float_flag_invalid | float_flag_overflow)) { + wt2 = FP_TO_INT32_OVERFLOW; + } + update_fcr31(env, GETPC()); + return wt2; +} + +uint32_t helper_float_ceil_w_s(CPUMIPSState *env, uint32_t fst0) +{ + uint32_t wt2; + + set_float_rounding_mode(float_round_up, &env->active_fpu.fp_status); + wt2 = float32_to_int32(fst0, &env->active_fpu.fp_status); + restore_rounding_mode(env); + if (get_float_exception_flags(&env->active_fpu.fp_status) + & (float_flag_invalid | float_flag_overflow)) { + wt2 = FP_TO_INT32_OVERFLOW; + } + update_fcr31(env, GETPC()); + return wt2; +} + +uint64_t helper_float_floor_l_d(CPUMIPSState *env, uint64_t fdt0) +{ + uint64_t dt2; + + set_float_rounding_mode(float_round_down, &env->active_fpu.fp_status); + dt2 = float64_to_int64(fdt0, &env->active_fpu.fp_status); + restore_rounding_mode(env); + if (get_float_exception_flags(&env->active_fpu.fp_status) + & (float_flag_invalid | float_flag_overflow)) { + dt2 = FP_TO_INT64_OVERFLOW; + } + update_fcr31(env, GETPC()); + return dt2; +} + +uint64_t helper_float_floor_l_s(CPUMIPSState *env, uint32_t fst0) +{ + uint64_t dt2; + + set_float_rounding_mode(float_round_down, &env->active_fpu.fp_status); + dt2 = float32_to_int64(fst0, &env->active_fpu.fp_status); + restore_rounding_mode(env); + if (get_float_exception_flags(&env->active_fpu.fp_status) + & (float_flag_invalid | float_flag_overflow)) { + dt2 = FP_TO_INT64_OVERFLOW; + } + update_fcr31(env, GETPC()); + return dt2; +} + +uint32_t helper_float_floor_w_d(CPUMIPSState *env, uint64_t fdt0) +{ + uint32_t wt2; + + set_float_rounding_mode(float_round_down, &env->active_fpu.fp_status); + wt2 = float64_to_int32(fdt0, &env->active_fpu.fp_status); + restore_rounding_mode(env); + if (get_float_exception_flags(&env->active_fpu.fp_status) + & (float_flag_invalid | float_flag_overflow)) { + wt2 = FP_TO_INT32_OVERFLOW; + } + update_fcr31(env, GETPC()); + return wt2; +} + +uint32_t helper_float_floor_w_s(CPUMIPSState *env, uint32_t fst0) +{ + uint32_t wt2; + + set_float_rounding_mode(float_round_down, &env->active_fpu.fp_status); + wt2 = float32_to_int32(fst0, &env->active_fpu.fp_status); + restore_rounding_mode(env); + if (get_float_exception_flags(&env->active_fpu.fp_status) + & (float_flag_invalid | float_flag_overflow)) { + wt2 = FP_TO_INT32_OVERFLOW; + } + update_fcr31(env, GETPC()); + return wt2; +} + +uint64_t helper_float_cvt_2008_l_d(CPUMIPSState *env, uint64_t fdt0) +{ + uint64_t dt2; + + dt2 = float64_to_int64(fdt0, &env->active_fpu.fp_status); + if (get_float_exception_flags(&env->active_fpu.fp_status) + & float_flag_invalid) { + if (float64_is_any_nan(fdt0)) { + dt2 = 0; + } + } + update_fcr31(env, GETPC()); + return dt2; +} + +uint64_t helper_float_cvt_2008_l_s(CPUMIPSState *env, uint32_t fst0) +{ + uint64_t dt2; + + dt2 = float32_to_int64(fst0, &env->active_fpu.fp_status); + if (get_float_exception_flags(&env->active_fpu.fp_status) + & float_flag_invalid) { + if (float32_is_any_nan(fst0)) { + dt2 = 0; + } + } + update_fcr31(env, GETPC()); + return dt2; +} + +uint32_t helper_float_cvt_2008_w_d(CPUMIPSState *env, uint64_t fdt0) +{ + uint32_t wt2; + + wt2 = float64_to_int32(fdt0, &env->active_fpu.fp_status); + if (get_float_exception_flags(&env->active_fpu.fp_status) + & float_flag_invalid) { + if (float64_is_any_nan(fdt0)) { + wt2 = 0; + } + } + update_fcr31(env, GETPC()); + return wt2; +} + +uint32_t helper_float_cvt_2008_w_s(CPUMIPSState *env, uint32_t fst0) +{ + uint32_t wt2; + + wt2 = float32_to_int32(fst0, &env->active_fpu.fp_status); + if (get_float_exception_flags(&env->active_fpu.fp_status) + & float_flag_invalid) { + if (float32_is_any_nan(fst0)) { + wt2 = 0; + } + } + update_fcr31(env, GETPC()); + return wt2; +} + +uint64_t helper_float_round_2008_l_d(CPUMIPSState *env, uint64_t fdt0) +{ + uint64_t dt2; + + set_float_rounding_mode(float_round_nearest_even, + &env->active_fpu.fp_status); + dt2 = float64_to_int64(fdt0, &env->active_fpu.fp_status); + restore_rounding_mode(env); + if (get_float_exception_flags(&env->active_fpu.fp_status) + & float_flag_invalid) { + if (float64_is_any_nan(fdt0)) { + dt2 = 0; + } + } + update_fcr31(env, GETPC()); + return dt2; +} + +uint64_t helper_float_round_2008_l_s(CPUMIPSState *env, uint32_t fst0) +{ + uint64_t dt2; + + set_float_rounding_mode(float_round_nearest_even, + &env->active_fpu.fp_status); + dt2 = float32_to_int64(fst0, &env->active_fpu.fp_status); + restore_rounding_mode(env); + if (get_float_exception_flags(&env->active_fpu.fp_status) + & float_flag_invalid) { + if (float32_is_any_nan(fst0)) { + dt2 = 0; + } + } + update_fcr31(env, GETPC()); + return dt2; +} + +uint32_t helper_float_round_2008_w_d(CPUMIPSState *env, uint64_t fdt0) +{ + uint32_t wt2; + + set_float_rounding_mode(float_round_nearest_even, + &env->active_fpu.fp_status); + wt2 = float64_to_int32(fdt0, &env->active_fpu.fp_status); + restore_rounding_mode(env); + if (get_float_exception_flags(&env->active_fpu.fp_status) + & float_flag_invalid) { + if (float64_is_any_nan(fdt0)) { + wt2 = 0; + } + } + update_fcr31(env, GETPC()); + return wt2; +} + +uint32_t helper_float_round_2008_w_s(CPUMIPSState *env, uint32_t fst0) +{ + uint32_t wt2; + + set_float_rounding_mode(float_round_nearest_even, + &env->active_fpu.fp_status); + wt2 = float32_to_int32(fst0, &env->active_fpu.fp_status); + restore_rounding_mode(env); + if (get_float_exception_flags(&env->active_fpu.fp_status) + & float_flag_invalid) { + if (float32_is_any_nan(fst0)) { + wt2 = 0; + } + } + update_fcr31(env, GETPC()); + return wt2; +} + +uint64_t helper_float_trunc_2008_l_d(CPUMIPSState *env, uint64_t fdt0) +{ + uint64_t dt2; + + dt2 = float64_to_int64_round_to_zero(fdt0, &env->active_fpu.fp_status); + if (get_float_exception_flags(&env->active_fpu.fp_status) + & float_flag_invalid) { + if (float64_is_any_nan(fdt0)) { + dt2 = 0; + } + } + update_fcr31(env, GETPC()); + return dt2; +} + +uint64_t helper_float_trunc_2008_l_s(CPUMIPSState *env, uint32_t fst0) +{ + uint64_t dt2; + + dt2 = float32_to_int64_round_to_zero(fst0, &env->active_fpu.fp_status); + if (get_float_exception_flags(&env->active_fpu.fp_status) + & float_flag_invalid) { + if (float32_is_any_nan(fst0)) { + dt2 = 0; + } + } + update_fcr31(env, GETPC()); + return dt2; +} + +uint32_t helper_float_trunc_2008_w_d(CPUMIPSState *env, uint64_t fdt0) +{ + uint32_t wt2; + + wt2 = float64_to_int32_round_to_zero(fdt0, &env->active_fpu.fp_status); + if (get_float_exception_flags(&env->active_fpu.fp_status) + & float_flag_invalid) { + if (float64_is_any_nan(fdt0)) { + wt2 = 0; + } + } + update_fcr31(env, GETPC()); + return wt2; +} + +uint32_t helper_float_trunc_2008_w_s(CPUMIPSState *env, uint32_t fst0) +{ + uint32_t wt2; + + wt2 = float32_to_int32_round_to_zero(fst0, &env->active_fpu.fp_status); + if (get_float_exception_flags(&env->active_fpu.fp_status) + & float_flag_invalid) { + if (float32_is_any_nan(fst0)) { + wt2 = 0; + } + } + update_fcr31(env, GETPC()); + return wt2; +} + +uint64_t helper_float_ceil_2008_l_d(CPUMIPSState *env, uint64_t fdt0) +{ + uint64_t dt2; + + set_float_rounding_mode(float_round_up, &env->active_fpu.fp_status); + dt2 = float64_to_int64(fdt0, &env->active_fpu.fp_status); + restore_rounding_mode(env); + if (get_float_exception_flags(&env->active_fpu.fp_status) + & float_flag_invalid) { + if (float64_is_any_nan(fdt0)) { + dt2 = 0; + } + } + update_fcr31(env, GETPC()); + return dt2; +} + +uint64_t helper_float_ceil_2008_l_s(CPUMIPSState *env, uint32_t fst0) +{ + uint64_t dt2; + + set_float_rounding_mode(float_round_up, &env->active_fpu.fp_status); + dt2 = float32_to_int64(fst0, &env->active_fpu.fp_status); + restore_rounding_mode(env); + if (get_float_exception_flags(&env->active_fpu.fp_status) + & float_flag_invalid) { + if (float32_is_any_nan(fst0)) { + dt2 = 0; + } + } + update_fcr31(env, GETPC()); + return dt2; +} + +uint32_t helper_float_ceil_2008_w_d(CPUMIPSState *env, uint64_t fdt0) +{ + uint32_t wt2; + + set_float_rounding_mode(float_round_up, &env->active_fpu.fp_status); + wt2 = float64_to_int32(fdt0, &env->active_fpu.fp_status); + restore_rounding_mode(env); + if (get_float_exception_flags(&env->active_fpu.fp_status) + & float_flag_invalid) { + if (float64_is_any_nan(fdt0)) { + wt2 = 0; + } + } + update_fcr31(env, GETPC()); + return wt2; +} + +uint32_t helper_float_ceil_2008_w_s(CPUMIPSState *env, uint32_t fst0) +{ + uint32_t wt2; + + set_float_rounding_mode(float_round_up, &env->active_fpu.fp_status); + wt2 = float32_to_int32(fst0, &env->active_fpu.fp_status); + restore_rounding_mode(env); + if (get_float_exception_flags(&env->active_fpu.fp_status) + & float_flag_invalid) { + if (float32_is_any_nan(fst0)) { + wt2 = 0; + } + } + update_fcr31(env, GETPC()); + return wt2; +} + +uint64_t helper_float_floor_2008_l_d(CPUMIPSState *env, uint64_t fdt0) +{ + uint64_t dt2; + + set_float_rounding_mode(float_round_down, &env->active_fpu.fp_status); + dt2 = float64_to_int64(fdt0, &env->active_fpu.fp_status); + restore_rounding_mode(env); + if (get_float_exception_flags(&env->active_fpu.fp_status) + & float_flag_invalid) { + if (float64_is_any_nan(fdt0)) { + dt2 = 0; + } + } + update_fcr31(env, GETPC()); + return dt2; +} + +uint64_t helper_float_floor_2008_l_s(CPUMIPSState *env, uint32_t fst0) +{ + uint64_t dt2; + + set_float_rounding_mode(float_round_down, &env->active_fpu.fp_status); + dt2 = float32_to_int64(fst0, &env->active_fpu.fp_status); + restore_rounding_mode(env); + if (get_float_exception_flags(&env->active_fpu.fp_status) + & float_flag_invalid) { + if (float32_is_any_nan(fst0)) { + dt2 = 0; + } + } + update_fcr31(env, GETPC()); + return dt2; +} + +uint32_t helper_float_floor_2008_w_d(CPUMIPSState *env, uint64_t fdt0) +{ + uint32_t wt2; + + set_float_rounding_mode(float_round_down, &env->active_fpu.fp_status); + wt2 = float64_to_int32(fdt0, &env->active_fpu.fp_status); + restore_rounding_mode(env); + if (get_float_exception_flags(&env->active_fpu.fp_status) + & float_flag_invalid) { + if (float64_is_any_nan(fdt0)) { + wt2 = 0; + } + } + update_fcr31(env, GETPC()); + return wt2; +} + +uint32_t helper_float_floor_2008_w_s(CPUMIPSState *env, uint32_t fst0) +{ + uint32_t wt2; + + set_float_rounding_mode(float_round_down, &env->active_fpu.fp_status); + wt2 = float32_to_int32(fst0, &env->active_fpu.fp_status); + restore_rounding_mode(env); + if (get_float_exception_flags(&env->active_fpu.fp_status) + & float_flag_invalid) { + if (float32_is_any_nan(fst0)) { + wt2 = 0; + } + } + update_fcr31(env, GETPC()); + return wt2; +} + +/* unary operations, not modifying fp status */ +#define FLOAT_UNOP(name) \ +uint64_t helper_float_ ## name ## _d(uint64_t fdt0) \ +{ \ + return float64_ ## name(fdt0); \ +} \ +uint32_t helper_float_ ## name ## _s(uint32_t fst0) \ +{ \ + return float32_ ## name(fst0); \ +} \ +uint64_t helper_float_ ## name ## _ps(uint64_t fdt0) \ +{ \ + uint32_t wt0; \ + uint32_t wth0; \ + \ + wt0 = float32_ ## name(fdt0 & 0XFFFFFFFF); \ + wth0 = float32_ ## name(fdt0 >> 32); \ + return ((uint64_t)wth0 << 32) | wt0; \ +} +FLOAT_UNOP(abs) +FLOAT_UNOP(chs) +#undef FLOAT_UNOP + +/* MIPS specific unary operations */ +uint64_t helper_float_recip_d(CPUMIPSState *env, uint64_t fdt0) +{ + uint64_t fdt2; + + fdt2 = float64_div(float64_one, fdt0, &env->active_fpu.fp_status); + update_fcr31(env, GETPC()); + return fdt2; +} + +uint32_t helper_float_recip_s(CPUMIPSState *env, uint32_t fst0) +{ + uint32_t fst2; + + fst2 = float32_div(float32_one, fst0, &env->active_fpu.fp_status); + update_fcr31(env, GETPC()); + return fst2; +} + +uint64_t helper_float_rsqrt_d(CPUMIPSState *env, uint64_t fdt0) +{ + uint64_t fdt2; + + fdt2 = float64_sqrt(fdt0, &env->active_fpu.fp_status); + fdt2 = float64_div(float64_one, fdt2, &env->active_fpu.fp_status); + update_fcr31(env, GETPC()); + return fdt2; +} + +uint32_t helper_float_rsqrt_s(CPUMIPSState *env, uint32_t fst0) +{ + uint32_t fst2; + + fst2 = float32_sqrt(fst0, &env->active_fpu.fp_status); + fst2 = float32_div(float32_one, fst2, &env->active_fpu.fp_status); + update_fcr31(env, GETPC()); + return fst2; +} + +uint64_t helper_float_recip1_d(CPUMIPSState *env, uint64_t fdt0) +{ + uint64_t fdt2; + + fdt2 = float64_div(float64_one, fdt0, &env->active_fpu.fp_status); + update_fcr31(env, GETPC()); + return fdt2; +} + +uint32_t helper_float_recip1_s(CPUMIPSState *env, uint32_t fst0) +{ + uint32_t fst2; + + fst2 = float32_div(float32_one, fst0, &env->active_fpu.fp_status); + update_fcr31(env, GETPC()); + return fst2; +} + +uint64_t helper_float_recip1_ps(CPUMIPSState *env, uint64_t fdt0) +{ + uint32_t fst2; + uint32_t fsth2; + + fst2 = float32_div(float32_one, fdt0 & 0XFFFFFFFF, + &env->active_fpu.fp_status); + fsth2 = float32_div(float32_one, fdt0 >> 32, &env->active_fpu.fp_status); + update_fcr31(env, GETPC()); + return ((uint64_t)fsth2 << 32) | fst2; +} + +uint64_t helper_float_rsqrt1_d(CPUMIPSState *env, uint64_t fdt0) +{ + uint64_t fdt2; + + fdt2 = float64_sqrt(fdt0, &env->active_fpu.fp_status); + fdt2 = float64_div(float64_one, fdt2, &env->active_fpu.fp_status); + update_fcr31(env, GETPC()); + return fdt2; +} + +uint32_t helper_float_rsqrt1_s(CPUMIPSState *env, uint32_t fst0) +{ + uint32_t fst2; + + fst2 = float32_sqrt(fst0, &env->active_fpu.fp_status); + fst2 = float32_div(float32_one, fst2, &env->active_fpu.fp_status); + update_fcr31(env, GETPC()); + return fst2; +} + +uint64_t helper_float_rsqrt1_ps(CPUMIPSState *env, uint64_t fdt0) +{ + uint32_t fst2; + uint32_t fsth2; + + fst2 = float32_sqrt(fdt0 & 0XFFFFFFFF, &env->active_fpu.fp_status); + fsth2 = float32_sqrt(fdt0 >> 32, &env->active_fpu.fp_status); + fst2 = float32_div(float32_one, fst2, &env->active_fpu.fp_status); + fsth2 = float32_div(float32_one, fsth2, &env->active_fpu.fp_status); + update_fcr31(env, GETPC()); + return ((uint64_t)fsth2 << 32) | fst2; +} + +#define FLOAT_RINT(name, bits) \ +uint ## bits ## _t helper_float_ ## name(CPUMIPSState *env, \ + uint ## bits ## _t fs) \ +{ \ + uint ## bits ## _t fdret; \ + \ + fdret = float ## bits ## _round_to_int(fs, &env->active_fpu.fp_status); \ + update_fcr31(env, GETPC()); \ + return fdret; \ +} + +FLOAT_RINT(rint_s, 32) +FLOAT_RINT(rint_d, 64) +#undef FLOAT_RINT + +#define FLOAT_CLASS_SIGNALING_NAN 0x001 +#define FLOAT_CLASS_QUIET_NAN 0x002 +#define FLOAT_CLASS_NEGATIVE_INFINITY 0x004 +#define FLOAT_CLASS_NEGATIVE_NORMAL 0x008 +#define FLOAT_CLASS_NEGATIVE_SUBNORMAL 0x010 +#define FLOAT_CLASS_NEGATIVE_ZERO 0x020 +#define FLOAT_CLASS_POSITIVE_INFINITY 0x040 +#define FLOAT_CLASS_POSITIVE_NORMAL 0x080 +#define FLOAT_CLASS_POSITIVE_SUBNORMAL 0x100 +#define FLOAT_CLASS_POSITIVE_ZERO 0x200 + +#define FLOAT_CLASS(name, bits) \ +uint ## bits ## _t float_ ## name(uint ## bits ## _t arg, \ + float_status *status) \ +{ \ + if (float ## bits ## _is_signaling_nan(arg, status)) { \ + return FLOAT_CLASS_SIGNALING_NAN; \ + } else if (float ## bits ## _is_quiet_nan(arg, status)) { \ + return FLOAT_CLASS_QUIET_NAN; \ + } else if (float ## bits ## _is_neg(arg)) { \ + if (float ## bits ## _is_infinity(arg)) { \ + return FLOAT_CLASS_NEGATIVE_INFINITY; \ + } else if (float ## bits ## _is_zero(arg)) { \ + return FLOAT_CLASS_NEGATIVE_ZERO; \ + } else if (float ## bits ## _is_zero_or_denormal(arg)) { \ + return FLOAT_CLASS_NEGATIVE_SUBNORMAL; \ + } else { \ + return FLOAT_CLASS_NEGATIVE_NORMAL; \ + } \ + } else { \ + if (float ## bits ## _is_infinity(arg)) { \ + return FLOAT_CLASS_POSITIVE_INFINITY; \ + } else if (float ## bits ## _is_zero(arg)) { \ + return FLOAT_CLASS_POSITIVE_ZERO; \ + } else if (float ## bits ## _is_zero_or_denormal(arg)) { \ + return FLOAT_CLASS_POSITIVE_SUBNORMAL; \ + } else { \ + return FLOAT_CLASS_POSITIVE_NORMAL; \ + } \ + } \ +} \ + \ +uint ## bits ## _t helper_float_ ## name(CPUMIPSState *env, \ + uint ## bits ## _t arg) \ +{ \ + return float_ ## name(arg, &env->active_fpu.fp_status); \ +} + +FLOAT_CLASS(class_s, 32) +FLOAT_CLASS(class_d, 64) +#undef FLOAT_CLASS + +/* binary operations */ +#define FLOAT_BINOP(name) \ +uint64_t helper_float_ ## name ## _d(CPUMIPSState *env, \ + uint64_t fdt0, uint64_t fdt1) \ +{ \ + uint64_t dt2; \ + \ + dt2 = float64_ ## name(fdt0, fdt1, &env->active_fpu.fp_status);\ + update_fcr31(env, GETPC()); \ + return dt2; \ +} \ + \ +uint32_t helper_float_ ## name ## _s(CPUMIPSState *env, \ + uint32_t fst0, uint32_t fst1) \ +{ \ + uint32_t wt2; \ + \ + wt2 = float32_ ## name(fst0, fst1, &env->active_fpu.fp_status);\ + update_fcr31(env, GETPC()); \ + return wt2; \ +} \ + \ +uint64_t helper_float_ ## name ## _ps(CPUMIPSState *env, \ + uint64_t fdt0, \ + uint64_t fdt1) \ +{ \ + uint32_t fst0 = fdt0 & 0XFFFFFFFF; \ + uint32_t fsth0 = fdt0 >> 32; \ + uint32_t fst1 = fdt1 & 0XFFFFFFFF; \ + uint32_t fsth1 = fdt1 >> 32; \ + uint32_t wt2; \ + uint32_t wth2; \ + \ + wt2 = float32_ ## name(fst0, fst1, &env->active_fpu.fp_status); \ + wth2 = float32_ ## name(fsth0, fsth1, &env->active_fpu.fp_status); \ + update_fcr31(env, GETPC()); \ + return ((uint64_t)wth2 << 32) | wt2; \ +} + +FLOAT_BINOP(add) +FLOAT_BINOP(sub) +FLOAT_BINOP(mul) +FLOAT_BINOP(div) +#undef FLOAT_BINOP + +/* MIPS specific binary operations */ +uint64_t helper_float_recip2_d(CPUMIPSState *env, uint64_t fdt0, uint64_t fdt2) +{ + fdt2 = float64_mul(fdt0, fdt2, &env->active_fpu.fp_status); + fdt2 = float64_chs(float64_sub(fdt2, float64_one, + &env->active_fpu.fp_status)); + update_fcr31(env, GETPC()); + return fdt2; +} + +uint32_t helper_float_recip2_s(CPUMIPSState *env, uint32_t fst0, uint32_t fst2) +{ + fst2 = float32_mul(fst0, fst2, &env->active_fpu.fp_status); + fst2 = float32_chs(float32_sub(fst2, float32_one, + &env->active_fpu.fp_status)); + update_fcr31(env, GETPC()); + return fst2; +} + +uint64_t helper_float_recip2_ps(CPUMIPSState *env, uint64_t fdt0, uint64_t fdt2) +{ + uint32_t fst0 = fdt0 & 0XFFFFFFFF; + uint32_t fsth0 = fdt0 >> 32; + uint32_t fst2 = fdt2 & 0XFFFFFFFF; + uint32_t fsth2 = fdt2 >> 32; + + fst2 = float32_mul(fst0, fst2, &env->active_fpu.fp_status); + fsth2 = float32_mul(fsth0, fsth2, &env->active_fpu.fp_status); + fst2 = float32_chs(float32_sub(fst2, float32_one, + &env->active_fpu.fp_status)); + fsth2 = float32_chs(float32_sub(fsth2, float32_one, + &env->active_fpu.fp_status)); + update_fcr31(env, GETPC()); + return ((uint64_t)fsth2 << 32) | fst2; +} + +uint64_t helper_float_rsqrt2_d(CPUMIPSState *env, uint64_t fdt0, uint64_t fdt2) +{ + fdt2 = float64_mul(fdt0, fdt2, &env->active_fpu.fp_status); + fdt2 = float64_sub(fdt2, float64_one, &env->active_fpu.fp_status); + fdt2 = float64_chs(float64_div(fdt2, FLOAT_TWO64, + &env->active_fpu.fp_status)); + update_fcr31(env, GETPC()); + return fdt2; +} + +uint32_t helper_float_rsqrt2_s(CPUMIPSState *env, uint32_t fst0, uint32_t fst2) +{ + fst2 = float32_mul(fst0, fst2, &env->active_fpu.fp_status); + fst2 = float32_sub(fst2, float32_one, &env->active_fpu.fp_status); + fst2 = float32_chs(float32_div(fst2, FLOAT_TWO32, + &env->active_fpu.fp_status)); + update_fcr31(env, GETPC()); + return fst2; +} + +uint64_t helper_float_rsqrt2_ps(CPUMIPSState *env, uint64_t fdt0, uint64_t fdt2) +{ + uint32_t fst0 = fdt0 & 0XFFFFFFFF; + uint32_t fsth0 = fdt0 >> 32; + uint32_t fst2 = fdt2 & 0XFFFFFFFF; + uint32_t fsth2 = fdt2 >> 32; + + fst2 = float32_mul(fst0, fst2, &env->active_fpu.fp_status); + fsth2 = float32_mul(fsth0, fsth2, &env->active_fpu.fp_status); + fst2 = float32_sub(fst2, float32_one, &env->active_fpu.fp_status); + fsth2 = float32_sub(fsth2, float32_one, &env->active_fpu.fp_status); + fst2 = float32_chs(float32_div(fst2, FLOAT_TWO32, + &env->active_fpu.fp_status)); + fsth2 = float32_chs(float32_div(fsth2, FLOAT_TWO32, + &env->active_fpu.fp_status)); + update_fcr31(env, GETPC()); + return ((uint64_t)fsth2 << 32) | fst2; +} + +uint64_t helper_float_addr_ps(CPUMIPSState *env, uint64_t fdt0, uint64_t fdt1) +{ + uint32_t fst0 = fdt0 & 0XFFFFFFFF; + uint32_t fsth0 = fdt0 >> 32; + uint32_t fst1 = fdt1 & 0XFFFFFFFF; + uint32_t fsth1 = fdt1 >> 32; + uint32_t fst2; + uint32_t fsth2; + + fst2 = float32_add(fst0, fsth0, &env->active_fpu.fp_status); + fsth2 = float32_add(fst1, fsth1, &env->active_fpu.fp_status); + update_fcr31(env, GETPC()); + return ((uint64_t)fsth2 << 32) | fst2; +} + +uint64_t helper_float_mulr_ps(CPUMIPSState *env, uint64_t fdt0, uint64_t fdt1) +{ + uint32_t fst0 = fdt0 & 0XFFFFFFFF; + uint32_t fsth0 = fdt0 >> 32; + uint32_t fst1 = fdt1 & 0XFFFFFFFF; + uint32_t fsth1 = fdt1 >> 32; + uint32_t fst2; + uint32_t fsth2; + + fst2 = float32_mul(fst0, fsth0, &env->active_fpu.fp_status); + fsth2 = float32_mul(fst1, fsth1, &env->active_fpu.fp_status); + update_fcr31(env, GETPC()); + return ((uint64_t)fsth2 << 32) | fst2; +} + +#define FLOAT_MINMAX(name, bits, minmaxfunc) \ +uint ## bits ## _t helper_float_ ## name(CPUMIPSState *env, \ + uint ## bits ## _t fs, \ + uint ## bits ## _t ft) \ +{ \ + uint ## bits ## _t fdret; \ + \ + fdret = float ## bits ## _ ## minmaxfunc(fs, ft, \ + &env->active_fpu.fp_status); \ + update_fcr31(env, GETPC()); \ + return fdret; \ +} + +FLOAT_MINMAX(max_s, 32, maxnum) +FLOAT_MINMAX(max_d, 64, maxnum) +FLOAT_MINMAX(maxa_s, 32, maxnummag) +FLOAT_MINMAX(maxa_d, 64, maxnummag) + +FLOAT_MINMAX(min_s, 32, minnum) +FLOAT_MINMAX(min_d, 64, minnum) +FLOAT_MINMAX(mina_s, 32, minnummag) +FLOAT_MINMAX(mina_d, 64, minnummag) +#undef FLOAT_MINMAX + +/* ternary operations */ +#define UNFUSED_FMA(prefix, a, b, c, flags) \ +{ \ + a = prefix##_mul(a, b, &env->active_fpu.fp_status); \ + if ((flags) & float_muladd_negate_c) { \ + a = prefix##_sub(a, c, &env->active_fpu.fp_status); \ + } else { \ + a = prefix##_add(a, c, &env->active_fpu.fp_status); \ + } \ + if ((flags) & float_muladd_negate_result) { \ + a = prefix##_chs(a); \ + } \ +} + +/* FMA based operations */ +#define FLOAT_FMA(name, type) \ +uint64_t helper_float_ ## name ## _d(CPUMIPSState *env, \ + uint64_t fdt0, uint64_t fdt1, \ + uint64_t fdt2) \ +{ \ + UNFUSED_FMA(float64, fdt0, fdt1, fdt2, type); \ + update_fcr31(env, GETPC()); \ + return fdt0; \ +} \ + \ +uint32_t helper_float_ ## name ## _s(CPUMIPSState *env, \ + uint32_t fst0, uint32_t fst1, \ + uint32_t fst2) \ +{ \ + UNFUSED_FMA(float32, fst0, fst1, fst2, type); \ + update_fcr31(env, GETPC()); \ + return fst0; \ +} \ + \ +uint64_t helper_float_ ## name ## _ps(CPUMIPSState *env, \ + uint64_t fdt0, uint64_t fdt1, \ + uint64_t fdt2) \ +{ \ + uint32_t fst0 = fdt0 & 0XFFFFFFFF; \ + uint32_t fsth0 = fdt0 >> 32; \ + uint32_t fst1 = fdt1 & 0XFFFFFFFF; \ + uint32_t fsth1 = fdt1 >> 32; \ + uint32_t fst2 = fdt2 & 0XFFFFFFFF; \ + uint32_t fsth2 = fdt2 >> 32; \ + \ + UNFUSED_FMA(float32, fst0, fst1, fst2, type); \ + UNFUSED_FMA(float32, fsth0, fsth1, fsth2, type); \ + update_fcr31(env, GETPC()); \ + return ((uint64_t)fsth0 << 32) | fst0; \ +} +FLOAT_FMA(madd, 0) +FLOAT_FMA(msub, float_muladd_negate_c) +FLOAT_FMA(nmadd, float_muladd_negate_result) +FLOAT_FMA(nmsub, float_muladd_negate_result | float_muladd_negate_c) +#undef FLOAT_FMA + +#define FLOAT_FMADDSUB(name, bits, muladd_arg) \ +uint ## bits ## _t helper_float_ ## name(CPUMIPSState *env, \ + uint ## bits ## _t fs, \ + uint ## bits ## _t ft, \ + uint ## bits ## _t fd) \ +{ \ + uint ## bits ## _t fdret; \ + \ + fdret = float ## bits ## _muladd(fs, ft, fd, muladd_arg, \ + &env->active_fpu.fp_status); \ + update_fcr31(env, GETPC()); \ + return fdret; \ +} + +FLOAT_FMADDSUB(maddf_s, 32, 0) +FLOAT_FMADDSUB(maddf_d, 64, 0) +FLOAT_FMADDSUB(msubf_s, 32, float_muladd_negate_product) +FLOAT_FMADDSUB(msubf_d, 64, float_muladd_negate_product) +#undef FLOAT_FMADDSUB + +/* compare operations */ +#define FOP_COND_D(op, cond) \ +void helper_cmp_d_ ## op(CPUMIPSState *env, uint64_t fdt0, \ + uint64_t fdt1, int cc) \ +{ \ + int c; \ + c = cond; \ + update_fcr31(env, GETPC()); \ + if (c) \ + SET_FP_COND(cc, env->active_fpu); \ + else \ + CLEAR_FP_COND(cc, env->active_fpu); \ +} \ +void helper_cmpabs_d_ ## op(CPUMIPSState *env, uint64_t fdt0, \ + uint64_t fdt1, int cc) \ +{ \ + int c; \ + fdt0 = float64_abs(fdt0); \ + fdt1 = float64_abs(fdt1); \ + c = cond; \ + update_fcr31(env, GETPC()); \ + if (c) \ + SET_FP_COND(cc, env->active_fpu); \ + else \ + CLEAR_FP_COND(cc, env->active_fpu); \ +} + +/* + * NOTE: the comma operator will make "cond" to eval to false, + * but float64_unordered_quiet() is still called. + */ +FOP_COND_D(f, (float64_unordered_quiet(fdt1, fdt0, + &env->active_fpu.fp_status), 0)) +FOP_COND_D(un, float64_unordered_quiet(fdt1, fdt0, + &env->active_fpu.fp_status)) +FOP_COND_D(eq, float64_eq_quiet(fdt0, fdt1, + &env->active_fpu.fp_status)) +FOP_COND_D(ueq, float64_unordered_quiet(fdt1, fdt0, + &env->active_fpu.fp_status) + || float64_eq_quiet(fdt0, fdt1, + &env->active_fpu.fp_status)) +FOP_COND_D(olt, float64_lt_quiet(fdt0, fdt1, + &env->active_fpu.fp_status)) +FOP_COND_D(ult, float64_unordered_quiet(fdt1, fdt0, + &env->active_fpu.fp_status) + || float64_lt_quiet(fdt0, fdt1, + &env->active_fpu.fp_status)) +FOP_COND_D(ole, float64_le_quiet(fdt0, fdt1, + &env->active_fpu.fp_status)) +FOP_COND_D(ule, float64_unordered_quiet(fdt1, fdt0, + &env->active_fpu.fp_status) + || float64_le_quiet(fdt0, fdt1, + &env->active_fpu.fp_status)) +/* + * NOTE: the comma operator will make "cond" to eval to false, + * but float64_unordered() is still called. + */ +FOP_COND_D(sf, (float64_unordered(fdt1, fdt0, + &env->active_fpu.fp_status), 0)) +FOP_COND_D(ngle, float64_unordered(fdt1, fdt0, + &env->active_fpu.fp_status)) +FOP_COND_D(seq, float64_eq(fdt0, fdt1, + &env->active_fpu.fp_status)) +FOP_COND_D(ngl, float64_unordered(fdt1, fdt0, + &env->active_fpu.fp_status) + || float64_eq(fdt0, fdt1, + &env->active_fpu.fp_status)) +FOP_COND_D(lt, float64_lt(fdt0, fdt1, + &env->active_fpu.fp_status)) +FOP_COND_D(nge, float64_unordered(fdt1, fdt0, + &env->active_fpu.fp_status) + || float64_lt(fdt0, fdt1, + &env->active_fpu.fp_status)) +FOP_COND_D(le, float64_le(fdt0, fdt1, + &env->active_fpu.fp_status)) +FOP_COND_D(ngt, float64_unordered(fdt1, fdt0, + &env->active_fpu.fp_status) + || float64_le(fdt0, fdt1, + &env->active_fpu.fp_status)) + +#define FOP_COND_S(op, cond) \ +void helper_cmp_s_ ## op(CPUMIPSState *env, uint32_t fst0, \ + uint32_t fst1, int cc) \ +{ \ + int c; \ + c = cond; \ + update_fcr31(env, GETPC()); \ + if (c) \ + SET_FP_COND(cc, env->active_fpu); \ + else \ + CLEAR_FP_COND(cc, env->active_fpu); \ +} \ +void helper_cmpabs_s_ ## op(CPUMIPSState *env, uint32_t fst0, \ + uint32_t fst1, int cc) \ +{ \ + int c; \ + fst0 = float32_abs(fst0); \ + fst1 = float32_abs(fst1); \ + c = cond; \ + update_fcr31(env, GETPC()); \ + if (c) \ + SET_FP_COND(cc, env->active_fpu); \ + else \ + CLEAR_FP_COND(cc, env->active_fpu); \ +} + +/* + * NOTE: the comma operator will make "cond" to eval to false, + * but float32_unordered_quiet() is still called. + */ +FOP_COND_S(f, (float32_unordered_quiet(fst1, fst0, + &env->active_fpu.fp_status), 0)) +FOP_COND_S(un, float32_unordered_quiet(fst1, fst0, + &env->active_fpu.fp_status)) +FOP_COND_S(eq, float32_eq_quiet(fst0, fst1, + &env->active_fpu.fp_status)) +FOP_COND_S(ueq, float32_unordered_quiet(fst1, fst0, + &env->active_fpu.fp_status) + || float32_eq_quiet(fst0, fst1, + &env->active_fpu.fp_status)) +FOP_COND_S(olt, float32_lt_quiet(fst0, fst1, + &env->active_fpu.fp_status)) +FOP_COND_S(ult, float32_unordered_quiet(fst1, fst0, + &env->active_fpu.fp_status) + || float32_lt_quiet(fst0, fst1, + &env->active_fpu.fp_status)) +FOP_COND_S(ole, float32_le_quiet(fst0, fst1, + &env->active_fpu.fp_status)) +FOP_COND_S(ule, float32_unordered_quiet(fst1, fst0, + &env->active_fpu.fp_status) + || float32_le_quiet(fst0, fst1, + &env->active_fpu.fp_status)) +/* + * NOTE: the comma operator will make "cond" to eval to false, + * but float32_unordered() is still called. + */ +FOP_COND_S(sf, (float32_unordered(fst1, fst0, + &env->active_fpu.fp_status), 0)) +FOP_COND_S(ngle, float32_unordered(fst1, fst0, + &env->active_fpu.fp_status)) +FOP_COND_S(seq, float32_eq(fst0, fst1, + &env->active_fpu.fp_status)) +FOP_COND_S(ngl, float32_unordered(fst1, fst0, + &env->active_fpu.fp_status) + || float32_eq(fst0, fst1, + &env->active_fpu.fp_status)) +FOP_COND_S(lt, float32_lt(fst0, fst1, + &env->active_fpu.fp_status)) +FOP_COND_S(nge, float32_unordered(fst1, fst0, + &env->active_fpu.fp_status) + || float32_lt(fst0, fst1, + &env->active_fpu.fp_status)) +FOP_COND_S(le, float32_le(fst0, fst1, + &env->active_fpu.fp_status)) +FOP_COND_S(ngt, float32_unordered(fst1, fst0, + &env->active_fpu.fp_status) + || float32_le(fst0, fst1, + &env->active_fpu.fp_status)) + +#define FOP_COND_PS(op, condl, condh) \ +void helper_cmp_ps_ ## op(CPUMIPSState *env, uint64_t fdt0, \ + uint64_t fdt1, int cc) \ +{ \ + uint32_t fst0, fsth0, fst1, fsth1; \ + int ch, cl; \ + fst0 = fdt0 & 0XFFFFFFFF; \ + fsth0 = fdt0 >> 32; \ + fst1 = fdt1 & 0XFFFFFFFF; \ + fsth1 = fdt1 >> 32; \ + cl = condl; \ + ch = condh; \ + update_fcr31(env, GETPC()); \ + if (cl) \ + SET_FP_COND(cc, env->active_fpu); \ + else \ + CLEAR_FP_COND(cc, env->active_fpu); \ + if (ch) \ + SET_FP_COND(cc + 1, env->active_fpu); \ + else \ + CLEAR_FP_COND(cc + 1, env->active_fpu); \ +} \ +void helper_cmpabs_ps_ ## op(CPUMIPSState *env, uint64_t fdt0, \ + uint64_t fdt1, int cc) \ +{ \ + uint32_t fst0, fsth0, fst1, fsth1; \ + int ch, cl; \ + fst0 = float32_abs(fdt0 & 0XFFFFFFFF); \ + fsth0 = float32_abs(fdt0 >> 32); \ + fst1 = float32_abs(fdt1 & 0XFFFFFFFF); \ + fsth1 = float32_abs(fdt1 >> 32); \ + cl = condl; \ + ch = condh; \ + update_fcr31(env, GETPC()); \ + if (cl) \ + SET_FP_COND(cc, env->active_fpu); \ + else \ + CLEAR_FP_COND(cc, env->active_fpu); \ + if (ch) \ + SET_FP_COND(cc + 1, env->active_fpu); \ + else \ + CLEAR_FP_COND(cc + 1, env->active_fpu); \ +} + +/* + * NOTE: the comma operator will make "cond" to eval to false, + * but float32_unordered_quiet() is still called. + */ +FOP_COND_PS(f, (float32_unordered_quiet(fst1, fst0, + &env->active_fpu.fp_status), 0), + (float32_unordered_quiet(fsth1, fsth0, + &env->active_fpu.fp_status), 0)) +FOP_COND_PS(un, float32_unordered_quiet(fst1, fst0, + &env->active_fpu.fp_status), + float32_unordered_quiet(fsth1, fsth0, + &env->active_fpu.fp_status)) +FOP_COND_PS(eq, float32_eq_quiet(fst0, fst1, + &env->active_fpu.fp_status), + float32_eq_quiet(fsth0, fsth1, + &env->active_fpu.fp_status)) +FOP_COND_PS(ueq, float32_unordered_quiet(fst1, fst0, + &env->active_fpu.fp_status) + || float32_eq_quiet(fst0, fst1, + &env->active_fpu.fp_status), + float32_unordered_quiet(fsth1, fsth0, + &env->active_fpu.fp_status) + || float32_eq_quiet(fsth0, fsth1, + &env->active_fpu.fp_status)) +FOP_COND_PS(olt, float32_lt_quiet(fst0, fst1, + &env->active_fpu.fp_status), + float32_lt_quiet(fsth0, fsth1, + &env->active_fpu.fp_status)) +FOP_COND_PS(ult, float32_unordered_quiet(fst1, fst0, + &env->active_fpu.fp_status) + || float32_lt_quiet(fst0, fst1, + &env->active_fpu.fp_status), + float32_unordered_quiet(fsth1, fsth0, + &env->active_fpu.fp_status) + || float32_lt_quiet(fsth0, fsth1, + &env->active_fpu.fp_status)) +FOP_COND_PS(ole, float32_le_quiet(fst0, fst1, + &env->active_fpu.fp_status), + float32_le_quiet(fsth0, fsth1, + &env->active_fpu.fp_status)) +FOP_COND_PS(ule, float32_unordered_quiet(fst1, fst0, + &env->active_fpu.fp_status) + || float32_le_quiet(fst0, fst1, + &env->active_fpu.fp_status), + float32_unordered_quiet(fsth1, fsth0, + &env->active_fpu.fp_status) + || float32_le_quiet(fsth0, fsth1, + &env->active_fpu.fp_status)) +/* + * NOTE: the comma operator will make "cond" to eval to false, + * but float32_unordered() is still called. + */ +FOP_COND_PS(sf, (float32_unordered(fst1, fst0, + &env->active_fpu.fp_status), 0), + (float32_unordered(fsth1, fsth0, + &env->active_fpu.fp_status), 0)) +FOP_COND_PS(ngle, float32_unordered(fst1, fst0, + &env->active_fpu.fp_status), + float32_unordered(fsth1, fsth0, + &env->active_fpu.fp_status)) +FOP_COND_PS(seq, float32_eq(fst0, fst1, + &env->active_fpu.fp_status), + float32_eq(fsth0, fsth1, + &env->active_fpu.fp_status)) +FOP_COND_PS(ngl, float32_unordered(fst1, fst0, + &env->active_fpu.fp_status) + || float32_eq(fst0, fst1, + &env->active_fpu.fp_status), + float32_unordered(fsth1, fsth0, + &env->active_fpu.fp_status) + || float32_eq(fsth0, fsth1, + &env->active_fpu.fp_status)) +FOP_COND_PS(lt, float32_lt(fst0, fst1, + &env->active_fpu.fp_status), + float32_lt(fsth0, fsth1, + &env->active_fpu.fp_status)) +FOP_COND_PS(nge, float32_unordered(fst1, fst0, + &env->active_fpu.fp_status) + || float32_lt(fst0, fst1, + &env->active_fpu.fp_status), + float32_unordered(fsth1, fsth0, + &env->active_fpu.fp_status) + || float32_lt(fsth0, fsth1, + &env->active_fpu.fp_status)) +FOP_COND_PS(le, float32_le(fst0, fst1, + &env->active_fpu.fp_status), + float32_le(fsth0, fsth1, + &env->active_fpu.fp_status)) +FOP_COND_PS(ngt, float32_unordered(fst1, fst0, + &env->active_fpu.fp_status) + || float32_le(fst0, fst1, + &env->active_fpu.fp_status), + float32_unordered(fsth1, fsth0, + &env->active_fpu.fp_status) + || float32_le(fsth0, fsth1, + &env->active_fpu.fp_status)) + +/* R6 compare operations */ +#define FOP_CONDN_D(op, cond) \ +uint64_t helper_r6_cmp_d_ ## op(CPUMIPSState *env, uint64_t fdt0, \ + uint64_t fdt1) \ +{ \ + uint64_t c; \ + c = cond; \ + update_fcr31(env, GETPC()); \ + if (c) { \ + return -1; \ + } else { \ + return 0; \ + } \ +} + +/* + * NOTE: the comma operator will make "cond" to eval to false, + * but float64_unordered_quiet() is still called. + */ +FOP_CONDN_D(af, (float64_unordered_quiet(fdt1, fdt0, + &env->active_fpu.fp_status), 0)) +FOP_CONDN_D(un, (float64_unordered_quiet(fdt1, fdt0, + &env->active_fpu.fp_status))) +FOP_CONDN_D(eq, (float64_eq_quiet(fdt0, fdt1, + &env->active_fpu.fp_status))) +FOP_CONDN_D(ueq, (float64_unordered_quiet(fdt1, fdt0, + &env->active_fpu.fp_status) + || float64_eq_quiet(fdt0, fdt1, + &env->active_fpu.fp_status))) +FOP_CONDN_D(lt, (float64_lt_quiet(fdt0, fdt1, + &env->active_fpu.fp_status))) +FOP_CONDN_D(ult, (float64_unordered_quiet(fdt1, fdt0, + &env->active_fpu.fp_status) + || float64_lt_quiet(fdt0, fdt1, + &env->active_fpu.fp_status))) +FOP_CONDN_D(le, (float64_le_quiet(fdt0, fdt1, + &env->active_fpu.fp_status))) +FOP_CONDN_D(ule, (float64_unordered_quiet(fdt1, fdt0, + &env->active_fpu.fp_status) + || float64_le_quiet(fdt0, fdt1, + &env->active_fpu.fp_status))) +/* + * NOTE: the comma operator will make "cond" to eval to false, + * but float64_unordered() is still called.\ + */ +FOP_CONDN_D(saf, (float64_unordered(fdt1, fdt0, + &env->active_fpu.fp_status), 0)) +FOP_CONDN_D(sun, (float64_unordered(fdt1, fdt0, + &env->active_fpu.fp_status))) +FOP_CONDN_D(seq, (float64_eq(fdt0, fdt1, + &env->active_fpu.fp_status))) +FOP_CONDN_D(sueq, (float64_unordered(fdt1, fdt0, + &env->active_fpu.fp_status) + || float64_eq(fdt0, fdt1, + &env->active_fpu.fp_status))) +FOP_CONDN_D(slt, (float64_lt(fdt0, fdt1, + &env->active_fpu.fp_status))) +FOP_CONDN_D(sult, (float64_unordered(fdt1, fdt0, + &env->active_fpu.fp_status) + || float64_lt(fdt0, fdt1, + &env->active_fpu.fp_status))) +FOP_CONDN_D(sle, (float64_le(fdt0, fdt1, + &env->active_fpu.fp_status))) +FOP_CONDN_D(sule, (float64_unordered(fdt1, fdt0, + &env->active_fpu.fp_status) + || float64_le(fdt0, fdt1, + &env->active_fpu.fp_status))) +FOP_CONDN_D(or, (float64_le_quiet(fdt1, fdt0, + &env->active_fpu.fp_status) + || float64_le_quiet(fdt0, fdt1, + &env->active_fpu.fp_status))) +FOP_CONDN_D(une, (float64_unordered_quiet(fdt1, fdt0, + &env->active_fpu.fp_status) + || float64_lt_quiet(fdt1, fdt0, + &env->active_fpu.fp_status) + || float64_lt_quiet(fdt0, fdt1, + &env->active_fpu.fp_status))) +FOP_CONDN_D(ne, (float64_lt_quiet(fdt1, fdt0, + &env->active_fpu.fp_status) + || float64_lt_quiet(fdt0, fdt1, + &env->active_fpu.fp_status))) +FOP_CONDN_D(sor, (float64_le(fdt1, fdt0, + &env->active_fpu.fp_status) + || float64_le(fdt0, fdt1, + &env->active_fpu.fp_status))) +FOP_CONDN_D(sune, (float64_unordered(fdt1, fdt0, + &env->active_fpu.fp_status) + || float64_lt(fdt1, fdt0, + &env->active_fpu.fp_status) + || float64_lt(fdt0, fdt1, + &env->active_fpu.fp_status))) +FOP_CONDN_D(sne, (float64_lt(fdt1, fdt0, + &env->active_fpu.fp_status) + || float64_lt(fdt0, fdt1, + &env->active_fpu.fp_status))) + +#define FOP_CONDN_S(op, cond) \ +uint32_t helper_r6_cmp_s_ ## op(CPUMIPSState *env, uint32_t fst0, \ + uint32_t fst1) \ +{ \ + uint64_t c; \ + c = cond; \ + update_fcr31(env, GETPC()); \ + if (c) { \ + return -1; \ + } else { \ + return 0; \ + } \ +} + +/* + * NOTE: the comma operator will make "cond" to eval to false, + * but float32_unordered_quiet() is still called. + */ +FOP_CONDN_S(af, (float32_unordered_quiet(fst1, fst0, + &env->active_fpu.fp_status), 0)) +FOP_CONDN_S(un, (float32_unordered_quiet(fst1, fst0, + &env->active_fpu.fp_status))) +FOP_CONDN_S(eq, (float32_eq_quiet(fst0, fst1, + &env->active_fpu.fp_status))) +FOP_CONDN_S(ueq, (float32_unordered_quiet(fst1, fst0, + &env->active_fpu.fp_status) + || float32_eq_quiet(fst0, fst1, + &env->active_fpu.fp_status))) +FOP_CONDN_S(lt, (float32_lt_quiet(fst0, fst1, + &env->active_fpu.fp_status))) +FOP_CONDN_S(ult, (float32_unordered_quiet(fst1, fst0, + &env->active_fpu.fp_status) + || float32_lt_quiet(fst0, fst1, + &env->active_fpu.fp_status))) +FOP_CONDN_S(le, (float32_le_quiet(fst0, fst1, + &env->active_fpu.fp_status))) +FOP_CONDN_S(ule, (float32_unordered_quiet(fst1, fst0, + &env->active_fpu.fp_status) + || float32_le_quiet(fst0, fst1, + &env->active_fpu.fp_status))) +/* + * NOTE: the comma operator will make "cond" to eval to false, + * but float32_unordered() is still called. + */ +FOP_CONDN_S(saf, (float32_unordered(fst1, fst0, + &env->active_fpu.fp_status), 0)) +FOP_CONDN_S(sun, (float32_unordered(fst1, fst0, + &env->active_fpu.fp_status))) +FOP_CONDN_S(seq, (float32_eq(fst0, fst1, + &env->active_fpu.fp_status))) +FOP_CONDN_S(sueq, (float32_unordered(fst1, fst0, + &env->active_fpu.fp_status) + || float32_eq(fst0, fst1, + &env->active_fpu.fp_status))) +FOP_CONDN_S(slt, (float32_lt(fst0, fst1, + &env->active_fpu.fp_status))) +FOP_CONDN_S(sult, (float32_unordered(fst1, fst0, + &env->active_fpu.fp_status) + || float32_lt(fst0, fst1, + &env->active_fpu.fp_status))) +FOP_CONDN_S(sle, (float32_le(fst0, fst1, + &env->active_fpu.fp_status))) +FOP_CONDN_S(sule, (float32_unordered(fst1, fst0, + &env->active_fpu.fp_status) + || float32_le(fst0, fst1, + &env->active_fpu.fp_status))) +FOP_CONDN_S(or, (float32_le_quiet(fst1, fst0, + &env->active_fpu.fp_status) + || float32_le_quiet(fst0, fst1, + &env->active_fpu.fp_status))) +FOP_CONDN_S(une, (float32_unordered_quiet(fst1, fst0, + &env->active_fpu.fp_status) + || float32_lt_quiet(fst1, fst0, + &env->active_fpu.fp_status) + || float32_lt_quiet(fst0, fst1, + &env->active_fpu.fp_status))) +FOP_CONDN_S(ne, (float32_lt_quiet(fst1, fst0, + &env->active_fpu.fp_status) + || float32_lt_quiet(fst0, fst1, + &env->active_fpu.fp_status))) +FOP_CONDN_S(sor, (float32_le(fst1, fst0, + &env->active_fpu.fp_status) + || float32_le(fst0, fst1, + &env->active_fpu.fp_status))) +FOP_CONDN_S(sune, (float32_unordered(fst1, fst0, + &env->active_fpu.fp_status) + || float32_lt(fst1, fst0, + &env->active_fpu.fp_status) + || float32_lt(fst0, fst1, + &env->active_fpu.fp_status))) +FOP_CONDN_S(sne, (float32_lt(fst1, fst0, + &env->active_fpu.fp_status) + || float32_lt(fst0, fst1, + &env->active_fpu.fp_status))) diff --git a/target/mips/gdbstub.c b/target/mips/gdbstub.c index bbb2544939..98f56e660d 100644 --- a/target/mips/gdbstub.c +++ b/target/mips/gdbstub.c @@ -22,7 +22,7 @@ #include "internal.h" #include "exec/gdbstub.h" -int mips_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +int mips_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) { MIPSCPU *cpu = MIPS_CPU(cs); CPUMIPSState *env = &cpu->env; diff --git a/target/mips/helper.c b/target/mips/helper.c index 781930a7dd..afd78b1990 100644 --- a/target/mips/helper.c +++ b/target/mips/helper.c @@ -72,8 +72,13 @@ int r4k_map_address(CPUMIPSState *env, hwaddr *physical, int *prot, target_ulong address, int rw, int access_type) { uint16_t ASID = env->CP0_EntryHi & env->CP0_EntryHi_ASID_mask; + uint32_t MMID = env->CP0_MemoryMapID; + bool mi = !!((env->CP0_Config5 >> CP0C5_MI) & 1); + uint32_t tlb_mmid; int i; + MMID = mi ? MMID : (uint32_t) ASID; + for (i = 0; i < env->tlb->tlb_in_use; i++) { r4k_tlb_t *tlb = &env->tlb->mmu.r4k.tlb[i]; /* 1k pages are not supported. */ @@ -84,8 +89,9 @@ int r4k_map_address(CPUMIPSState *env, hwaddr *physical, int *prot, tag &= env->SEGMask; #endif - /* Check ASID, virtual page number & size */ - if ((tlb->G == 1 || tlb->ASID == ASID) && VPN == tag && !tlb->EHINV) { + /* Check ASID/MMID, virtual page number & size */ + tlb_mmid = mi ? tlb->MMID : (uint32_t) tlb->ASID; + if ((tlb->G == 1 || tlb_mmid == MMID) && VPN == tag && !tlb->EHINV) { /* TLB match */ int n = !!(address & mask & ~(mask >> 1)); /* Check access rights */ @@ -1418,14 +1424,20 @@ void r4k_invalidate_tlb(CPUMIPSState *env, int idx, int use_extra) target_ulong addr; target_ulong end; uint16_t ASID = env->CP0_EntryHi & env->CP0_EntryHi_ASID_mask; + uint32_t MMID = env->CP0_MemoryMapID; + bool mi = !!((env->CP0_Config5 >> CP0C5_MI) & 1); + uint32_t tlb_mmid; target_ulong mask; + MMID = mi ? MMID : (uint32_t) ASID; + tlb = &env->tlb->mmu.r4k.tlb[idx]; /* - * The qemu TLB is flushed when the ASID changes, so no need to + * The qemu TLB is flushed when the ASID/MMID changes, so no need to * flush these entries again. */ - if (tlb->G == 0 && tlb->ASID != ASID) { + tlb_mmid = mi ? tlb->MMID : (uint32_t) tlb->ASID; + if (tlb->G == 0 && tlb_mmid != MMID) { return; } diff --git a/target/mips/helper.h b/target/mips/helper.h index 7b8ad74d67..84fdd9fd27 100644 --- a/target/mips/helper.h +++ b/target/mips/helper.h @@ -78,6 +78,7 @@ DEF_HELPER_1(mfc0_maar, tl, env) DEF_HELPER_1(mfhc0_maar, tl, env) DEF_HELPER_2(mfc0_watchlo, tl, env, i32) DEF_HELPER_2(mfc0_watchhi, tl, env, i32) +DEF_HELPER_2(mfhc0_watchhi, tl, env, i32) DEF_HELPER_1(mfc0_debug, tl, env) DEF_HELPER_1(mftc0_debug, tl, env) #ifdef TARGET_MIPS64 @@ -89,6 +90,7 @@ DEF_HELPER_1(dmfc0_tcschefback, tl, env) DEF_HELPER_1(dmfc0_lladdr, tl, env) DEF_HELPER_1(dmfc0_maar, tl, env) DEF_HELPER_2(dmfc0_watchlo, tl, env, i32) +DEF_HELPER_2(dmfc0_watchhi, tl, env, i32) DEF_HELPER_1(dmfc0_saar, tl, env) #endif /* TARGET_MIPS64 */ @@ -118,6 +120,7 @@ DEF_HELPER_2(mtc0_tcschefback, void, env, tl) DEF_HELPER_2(mttc0_tcschefback, void, env, tl) DEF_HELPER_2(mtc0_entrylo1, void, env, tl) DEF_HELPER_2(mtc0_context, void, env, tl) +DEF_HELPER_2(mtc0_memorymapid, void, env, tl) DEF_HELPER_2(mtc0_pagemask, void, env, tl) DEF_HELPER_2(mtc0_pagegrain, void, env, tl) DEF_HELPER_2(mtc0_segctl0, void, env, tl) @@ -159,6 +162,7 @@ DEF_HELPER_2(mthc0_maar, void, env, tl) DEF_HELPER_2(mtc0_maari, void, env, tl) DEF_HELPER_3(mtc0_watchlo, void, env, tl, i32) DEF_HELPER_3(mtc0_watchhi, void, env, tl, i32) +DEF_HELPER_3(mthc0_watchhi, void, env, tl, i32) DEF_HELPER_2(mtc0_xcontext, void, env, tl) DEF_HELPER_2(mtc0_framemask, void, env, tl) DEF_HELPER_2(mtc0_debug, void, env, tl) @@ -373,6 +377,7 @@ DEF_HELPER_1(ei, tl, env) DEF_HELPER_1(eret, void, env) DEF_HELPER_1(eretnc, void, env) DEF_HELPER_1(deret, void, env) +DEF_HELPER_3(ginvt, void, env, tl, i32) #endif /* !CONFIG_USER_ONLY */ DEF_HELPER_1(rdhwr_cpunum, tl, env) DEF_HELPER_1(rdhwr_synci_step, tl, env) diff --git a/target/mips/internal.h b/target/mips/internal.h index 3f435b5e63..1bf274b3ef 100644 --- a/target/mips/internal.h +++ b/target/mips/internal.h @@ -82,7 +82,7 @@ void mips_cpu_do_interrupt(CPUState *cpu); bool mips_cpu_exec_interrupt(CPUState *cpu, int int_req); void mips_cpu_dump_state(CPUState *cpu, FILE *f, int flags); hwaddr mips_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); -int mips_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); +int mips_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int mips_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); void mips_cpu_do_unaligned_access(CPUState *cpu, vaddr addr, MMUAccessType access_type, @@ -95,6 +95,7 @@ struct r4k_tlb_t { target_ulong VPN; uint32_t PageMask; uint16_t ASID; + uint32_t MMID; unsigned int G:1; unsigned int C0:3; unsigned int C1:3; diff --git a/target/mips/kvm.c b/target/mips/kvm.c index 578bc14625..de3e26ef1f 100644 --- a/target/mips/kvm.c +++ b/target/mips/kvm.c @@ -57,7 +57,7 @@ int kvm_arch_init(MachineState *ms, KVMState *s) return 0; } -int kvm_arch_irqchip_create(MachineState *ms, KVMState *s) +int kvm_arch_irqchip_create(KVMState *s) { return 0; } diff --git a/target/mips/machine.c b/target/mips/machine.c index c1392395ab..8d5b18bea2 100644 --- a/target/mips/machine.c +++ b/target/mips/machine.c @@ -212,8 +212,8 @@ const VMStateDescription vmstate_tlb = { const VMStateDescription vmstate_mips_cpu = { .name = "cpu", - .version_id = 18, - .minimum_version_id = 18, + .version_id = 19, + .minimum_version_id = 19, .post_load = cpu_post_load, .fields = (VMStateField[]) { /* Active TC */ @@ -296,7 +296,7 @@ const VMStateDescription vmstate_mips_cpu = { VMSTATE_INT32(env.CP0_MAARI, MIPSCPU), VMSTATE_UINTTL(env.lladdr, MIPSCPU), VMSTATE_UINTTL_ARRAY(env.CP0_WatchLo, MIPSCPU, 8), - VMSTATE_INT32_ARRAY(env.CP0_WatchHi, MIPSCPU, 8), + VMSTATE_UINT64_ARRAY(env.CP0_WatchHi, MIPSCPU, 8), VMSTATE_UINTTL(env.CP0_XContext, MIPSCPU), VMSTATE_INT32(env.CP0_Framemask, MIPSCPU), VMSTATE_INT32(env.CP0_Debug, MIPSCPU), diff --git a/target/mips/mips-semi.c b/target/mips/mips-semi.c index 35bdfd7c77..10a710c1e8 100644 --- a/target/mips/mips-semi.c +++ b/target/mips/mips-semi.c @@ -218,7 +218,7 @@ static int copy_argn_to_target(CPUMIPSState *env, int arg_num, if (!p) { \ gpr[2] = -1; \ gpr[3] = EFAULT; \ - goto uhi_done; \ + return; \ } \ } while (0) @@ -228,14 +228,14 @@ static int copy_argn_to_target(CPUMIPSState *env, int arg_num, if (!p) { \ gpr[2] = -1; \ gpr[3] = EFAULT; \ - goto uhi_done; \ + return; \ } \ p2 = lock_user_string(addr2); \ if (!p2) { \ unlock_user(p, addr, 0); \ gpr[2] = -1; \ gpr[3] = EFAULT; \ - goto uhi_done; \ + return; \ } \ } while (0) @@ -272,7 +272,7 @@ void helper_do_semihosting(CPUMIPSState *env) if (gpr[4] < 3) { /* ignore closing stdin/stdout/stderr */ gpr[2] = 0; - goto uhi_done; + return; } gpr[2] = close(gpr[4]); gpr[3] = errno_mips(errno); @@ -302,7 +302,7 @@ void helper_do_semihosting(CPUMIPSState *env) gpr[2] = fstat(gpr[4], &sbuf); gpr[3] = errno_mips(errno); if (gpr[2]) { - goto uhi_done; + return; } gpr[2] = copy_stat_to_target(env, &sbuf, gpr[5]); gpr[3] = errno_mips(errno); @@ -314,14 +314,14 @@ void helper_do_semihosting(CPUMIPSState *env) case UHI_argnlen: if (gpr[4] >= semihosting_get_argc()) { gpr[2] = -1; - goto uhi_done; + return; } gpr[2] = strlen(semihosting_get_arg(gpr[4])); break; case UHI_argn: if (gpr[4] >= semihosting_get_argc()) { gpr[2] = -1; - goto uhi_done; + return; } gpr[2] = copy_argn_to_target(env, gpr[4], gpr[5]); break; @@ -369,6 +369,5 @@ void helper_do_semihosting(CPUMIPSState *env) fprintf(stderr, "Unknown UHI operation %d\n", op); abort(); } -uhi_done: return; } diff --git a/target/mips/op_helper.c b/target/mips/op_helper.c index 18fcee4a78..9552b280e0 100644 --- a/target/mips/op_helper.c +++ b/target/mips/op_helper.c @@ -15,7 +15,9 @@ * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . + * */ + #include "qemu/osdep.h" #include "qemu/main-loop.h" #include "cpu.h" @@ -26,7 +28,7 @@ #include "exec/cpu_ldst.h" #include "exec/memop.h" #include "sysemu/kvm.h" -#include "fpu/softfloat.h" + /*****************************************************************************/ /* Exceptions processing helpers */ @@ -52,69 +54,6 @@ static void raise_exception(CPUMIPSState *env, uint32_t exception) do_raise_exception(env, exception, 0); } -#if defined(CONFIG_USER_ONLY) -#define HELPER_LD(name, insn, type) \ -static inline type do_##name(CPUMIPSState *env, target_ulong addr, \ - int mem_idx, uintptr_t retaddr) \ -{ \ - return (type) cpu_##insn##_data_ra(env, addr, retaddr); \ -} -#else -#define HELPER_LD(name, insn, type) \ -static inline type do_##name(CPUMIPSState *env, target_ulong addr, \ - int mem_idx, uintptr_t retaddr) \ -{ \ - switch (mem_idx) { \ - case 0: return (type) cpu_##insn##_kernel_ra(env, addr, retaddr); \ - case 1: return (type) cpu_##insn##_super_ra(env, addr, retaddr); \ - default: \ - case 2: return (type) cpu_##insn##_user_ra(env, addr, retaddr); \ - case 3: return (type) cpu_##insn##_error_ra(env, addr, retaddr); \ - } \ -} -#endif -HELPER_LD(lw, ldl, int32_t) -#if defined(TARGET_MIPS64) -HELPER_LD(ld, ldq, int64_t) -#endif -#undef HELPER_LD - -#if defined(CONFIG_USER_ONLY) -#define HELPER_ST(name, insn, type) \ -static inline void do_##name(CPUMIPSState *env, target_ulong addr, \ - type val, int mem_idx, uintptr_t retaddr) \ -{ \ - cpu_##insn##_data_ra(env, addr, val, retaddr); \ -} -#else -#define HELPER_ST(name, insn, type) \ -static inline void do_##name(CPUMIPSState *env, target_ulong addr, \ - type val, int mem_idx, uintptr_t retaddr) \ -{ \ - switch (mem_idx) { \ - case 0: \ - cpu_##insn##_kernel_ra(env, addr, val, retaddr); \ - break; \ - case 1: \ - cpu_##insn##_super_ra(env, addr, val, retaddr); \ - break; \ - default: \ - case 2: \ - cpu_##insn##_user_ra(env, addr, val, retaddr); \ - break; \ - case 3: \ - cpu_##insn##_error_ra(env, addr, val, retaddr); \ - break; \ - } \ -} -#endif -HELPER_ST(sb, stb, uint8_t) -HELPER_ST(sw, stl, uint32_t) -#if defined(TARGET_MIPS64) -HELPER_ST(sd, stq, uint64_t) -#endif -#undef HELPER_ST - /* 64 bits arithmetic for 32 bits hosts */ static inline uint64_t get_HILO(CPUMIPSState *env) { @@ -368,7 +307,7 @@ static inline hwaddr do_translate_address(CPUMIPSState *env, } } -#define HELPER_LD_ATOMIC(name, insn, almask) \ +#define HELPER_LD_ATOMIC(name, insn, almask, do_cast) \ target_ulong helper_##name(CPUMIPSState *env, target_ulong arg, int mem_idx) \ { \ if (arg & almask) { \ @@ -379,12 +318,12 @@ target_ulong helper_##name(CPUMIPSState *env, target_ulong arg, int mem_idx) \ } \ env->CP0_LLAddr = do_translate_address(env, arg, 0, GETPC()); \ env->lladdr = arg; \ - env->llval = do_##insn(env, arg, mem_idx, GETPC()); \ + env->llval = do_cast cpu_##insn##_mmuidx_ra(env, arg, mem_idx, GETPC()); \ return env->llval; \ } -HELPER_LD_ATOMIC(ll, lw, 0x3) +HELPER_LD_ATOMIC(ll, ldl, 0x3, (target_long)(int32_t)) #ifdef TARGET_MIPS64 -HELPER_LD_ATOMIC(lld, ld, 0x7) +HELPER_LD_ATOMIC(lld, ldq, 0x7, (target_ulong)) #endif #undef HELPER_LD_ATOMIC #endif @@ -400,42 +339,42 @@ HELPER_LD_ATOMIC(lld, ld, 0x7) void helper_swl(CPUMIPSState *env, target_ulong arg1, target_ulong arg2, int mem_idx) { - do_sb(env, arg2, (uint8_t)(arg1 >> 24), mem_idx, GETPC()); + cpu_stb_mmuidx_ra(env, arg2, (uint8_t)(arg1 >> 24), mem_idx, GETPC()); if (GET_LMASK(arg2) <= 2) { - do_sb(env, GET_OFFSET(arg2, 1), (uint8_t)(arg1 >> 16), mem_idx, - GETPC()); + cpu_stb_mmuidx_ra(env, GET_OFFSET(arg2, 1), (uint8_t)(arg1 >> 16), + mem_idx, GETPC()); } if (GET_LMASK(arg2) <= 1) { - do_sb(env, GET_OFFSET(arg2, 2), (uint8_t)(arg1 >> 8), mem_idx, - GETPC()); + cpu_stb_mmuidx_ra(env, GET_OFFSET(arg2, 2), (uint8_t)(arg1 >> 8), + mem_idx, GETPC()); } if (GET_LMASK(arg2) == 0) { - do_sb(env, GET_OFFSET(arg2, 3), (uint8_t)arg1, mem_idx, - GETPC()); + cpu_stb_mmuidx_ra(env, GET_OFFSET(arg2, 3), (uint8_t)arg1, + mem_idx, GETPC()); } } void helper_swr(CPUMIPSState *env, target_ulong arg1, target_ulong arg2, int mem_idx) { - do_sb(env, arg2, (uint8_t)arg1, mem_idx, GETPC()); + cpu_stb_mmuidx_ra(env, arg2, (uint8_t)arg1, mem_idx, GETPC()); if (GET_LMASK(arg2) >= 1) { - do_sb(env, GET_OFFSET(arg2, -1), (uint8_t)(arg1 >> 8), mem_idx, - GETPC()); + cpu_stb_mmuidx_ra(env, GET_OFFSET(arg2, -1), (uint8_t)(arg1 >> 8), + mem_idx, GETPC()); } if (GET_LMASK(arg2) >= 2) { - do_sb(env, GET_OFFSET(arg2, -2), (uint8_t)(arg1 >> 16), mem_idx, - GETPC()); + cpu_stb_mmuidx_ra(env, GET_OFFSET(arg2, -2), (uint8_t)(arg1 >> 16), + mem_idx, GETPC()); } if (GET_LMASK(arg2) == 3) { - do_sb(env, GET_OFFSET(arg2, -3), (uint8_t)(arg1 >> 24), mem_idx, - GETPC()); + cpu_stb_mmuidx_ra(env, GET_OFFSET(arg2, -3), (uint8_t)(arg1 >> 24), + mem_idx, GETPC()); } } @@ -453,82 +392,82 @@ void helper_swr(CPUMIPSState *env, target_ulong arg1, target_ulong arg2, void helper_sdl(CPUMIPSState *env, target_ulong arg1, target_ulong arg2, int mem_idx) { - do_sb(env, arg2, (uint8_t)(arg1 >> 56), mem_idx, GETPC()); + cpu_stb_mmuidx_ra(env, arg2, (uint8_t)(arg1 >> 56), mem_idx, GETPC()); if (GET_LMASK64(arg2) <= 6) { - do_sb(env, GET_OFFSET(arg2, 1), (uint8_t)(arg1 >> 48), mem_idx, - GETPC()); + cpu_stb_mmuidx_ra(env, GET_OFFSET(arg2, 1), (uint8_t)(arg1 >> 48), + mem_idx, GETPC()); } if (GET_LMASK64(arg2) <= 5) { - do_sb(env, GET_OFFSET(arg2, 2), (uint8_t)(arg1 >> 40), mem_idx, - GETPC()); + cpu_stb_mmuidx_ra(env, GET_OFFSET(arg2, 2), (uint8_t)(arg1 >> 40), + mem_idx, GETPC()); } if (GET_LMASK64(arg2) <= 4) { - do_sb(env, GET_OFFSET(arg2, 3), (uint8_t)(arg1 >> 32), mem_idx, - GETPC()); + cpu_stb_mmuidx_ra(env, GET_OFFSET(arg2, 3), (uint8_t)(arg1 >> 32), + mem_idx, GETPC()); } if (GET_LMASK64(arg2) <= 3) { - do_sb(env, GET_OFFSET(arg2, 4), (uint8_t)(arg1 >> 24), mem_idx, - GETPC()); + cpu_stb_mmuidx_ra(env, GET_OFFSET(arg2, 4), (uint8_t)(arg1 >> 24), + mem_idx, GETPC()); } if (GET_LMASK64(arg2) <= 2) { - do_sb(env, GET_OFFSET(arg2, 5), (uint8_t)(arg1 >> 16), mem_idx, - GETPC()); + cpu_stb_mmuidx_ra(env, GET_OFFSET(arg2, 5), (uint8_t)(arg1 >> 16), + mem_idx, GETPC()); } if (GET_LMASK64(arg2) <= 1) { - do_sb(env, GET_OFFSET(arg2, 6), (uint8_t)(arg1 >> 8), mem_idx, - GETPC()); + cpu_stb_mmuidx_ra(env, GET_OFFSET(arg2, 6), (uint8_t)(arg1 >> 8), + mem_idx, GETPC()); } if (GET_LMASK64(arg2) <= 0) { - do_sb(env, GET_OFFSET(arg2, 7), (uint8_t)arg1, mem_idx, - GETPC()); + cpu_stb_mmuidx_ra(env, GET_OFFSET(arg2, 7), (uint8_t)arg1, + mem_idx, GETPC()); } } void helper_sdr(CPUMIPSState *env, target_ulong arg1, target_ulong arg2, int mem_idx) { - do_sb(env, arg2, (uint8_t)arg1, mem_idx, GETPC()); + cpu_stb_mmuidx_ra(env, arg2, (uint8_t)arg1, mem_idx, GETPC()); if (GET_LMASK64(arg2) >= 1) { - do_sb(env, GET_OFFSET(arg2, -1), (uint8_t)(arg1 >> 8), mem_idx, - GETPC()); + cpu_stb_mmuidx_ra(env, GET_OFFSET(arg2, -1), (uint8_t)(arg1 >> 8), + mem_idx, GETPC()); } if (GET_LMASK64(arg2) >= 2) { - do_sb(env, GET_OFFSET(arg2, -2), (uint8_t)(arg1 >> 16), mem_idx, - GETPC()); + cpu_stb_mmuidx_ra(env, GET_OFFSET(arg2, -2), (uint8_t)(arg1 >> 16), + mem_idx, GETPC()); } if (GET_LMASK64(arg2) >= 3) { - do_sb(env, GET_OFFSET(arg2, -3), (uint8_t)(arg1 >> 24), mem_idx, - GETPC()); + cpu_stb_mmuidx_ra(env, GET_OFFSET(arg2, -3), (uint8_t)(arg1 >> 24), + mem_idx, GETPC()); } if (GET_LMASK64(arg2) >= 4) { - do_sb(env, GET_OFFSET(arg2, -4), (uint8_t)(arg1 >> 32), mem_idx, - GETPC()); + cpu_stb_mmuidx_ra(env, GET_OFFSET(arg2, -4), (uint8_t)(arg1 >> 32), + mem_idx, GETPC()); } if (GET_LMASK64(arg2) >= 5) { - do_sb(env, GET_OFFSET(arg2, -5), (uint8_t)(arg1 >> 40), mem_idx, - GETPC()); + cpu_stb_mmuidx_ra(env, GET_OFFSET(arg2, -5), (uint8_t)(arg1 >> 40), + mem_idx, GETPC()); } if (GET_LMASK64(arg2) >= 6) { - do_sb(env, GET_OFFSET(arg2, -6), (uint8_t)(arg1 >> 48), mem_idx, - GETPC()); + cpu_stb_mmuidx_ra(env, GET_OFFSET(arg2, -6), (uint8_t)(arg1 >> 48), + mem_idx, GETPC()); } if (GET_LMASK64(arg2) == 7) { - do_sb(env, GET_OFFSET(arg2, -7), (uint8_t)(arg1 >> 56), mem_idx, - GETPC()); + cpu_stb_mmuidx_ra(env, GET_OFFSET(arg2, -7), (uint8_t)(arg1 >> 56), + mem_idx, GETPC()); } } #endif /* TARGET_MIPS64 */ @@ -546,14 +485,14 @@ void helper_lwm(CPUMIPSState *env, target_ulong addr, target_ulong reglist, for (i = 0; i < base_reglist; i++) { env->active_tc.gpr[multiple_regs[i]] = - (target_long)do_lw(env, addr, mem_idx, GETPC()); + (target_long)cpu_ldl_mmuidx_ra(env, addr, mem_idx, GETPC()); addr += 4; } } if (do_r31) { - env->active_tc.gpr[31] = (target_long)do_lw(env, addr, mem_idx, - GETPC()); + env->active_tc.gpr[31] = + (target_long)cpu_ldl_mmuidx_ra(env, addr, mem_idx, GETPC()); } } @@ -567,14 +506,14 @@ void helper_swm(CPUMIPSState *env, target_ulong addr, target_ulong reglist, target_ulong i; for (i = 0; i < base_reglist; i++) { - do_sw(env, addr, env->active_tc.gpr[multiple_regs[i]], mem_idx, - GETPC()); + cpu_stw_mmuidx_ra(env, addr, env->active_tc.gpr[multiple_regs[i]], + mem_idx, GETPC()); addr += 4; } } if (do_r31) { - do_sw(env, addr, env->active_tc.gpr[31], mem_idx, GETPC()); + cpu_stw_mmuidx_ra(env, addr, env->active_tc.gpr[31], mem_idx, GETPC()); } } @@ -589,14 +528,15 @@ void helper_ldm(CPUMIPSState *env, target_ulong addr, target_ulong reglist, target_ulong i; for (i = 0; i < base_reglist; i++) { - env->active_tc.gpr[multiple_regs[i]] = do_ld(env, addr, mem_idx, - GETPC()); + env->active_tc.gpr[multiple_regs[i]] = + cpu_ldq_mmuidx_ra(env, addr, mem_idx, GETPC()); addr += 8; } } if (do_r31) { - env->active_tc.gpr[31] = do_ld(env, addr, mem_idx, GETPC()); + env->active_tc.gpr[31] = + cpu_ldq_mmuidx_ra(env, addr, mem_idx, GETPC()); } } @@ -610,1588 +550,18 @@ void helper_sdm(CPUMIPSState *env, target_ulong addr, target_ulong reglist, target_ulong i; for (i = 0; i < base_reglist; i++) { - do_sd(env, addr, env->active_tc.gpr[multiple_regs[i]], mem_idx, - GETPC()); + cpu_stq_mmuidx_ra(env, addr, env->active_tc.gpr[multiple_regs[i]], + mem_idx, GETPC()); addr += 8; } } if (do_r31) { - do_sd(env, addr, env->active_tc.gpr[31], mem_idx, GETPC()); + cpu_stq_mmuidx_ra(env, addr, env->active_tc.gpr[31], mem_idx, GETPC()); } } #endif -#ifndef CONFIG_USER_ONLY -/* SMP helpers. */ -static bool mips_vpe_is_wfi(MIPSCPU *c) -{ - CPUState *cpu = CPU(c); - CPUMIPSState *env = &c->env; - - /* - * If the VPE is halted but otherwise active, it means it's waiting for - * an interrupt.\ - */ - return cpu->halted && mips_vpe_active(env); -} - -static bool mips_vp_is_wfi(MIPSCPU *c) -{ - CPUState *cpu = CPU(c); - CPUMIPSState *env = &c->env; - - return cpu->halted && mips_vp_active(env); -} - -static inline void mips_vpe_wake(MIPSCPU *c) -{ - /* - * Don't set ->halted = 0 directly, let it be done via cpu_has_work - * because there might be other conditions that state that c should - * be sleeping. - */ - qemu_mutex_lock_iothread(); - cpu_interrupt(CPU(c), CPU_INTERRUPT_WAKE); - qemu_mutex_unlock_iothread(); -} - -static inline void mips_vpe_sleep(MIPSCPU *cpu) -{ - CPUState *cs = CPU(cpu); - - /* - * The VPE was shut off, really go to bed. - * Reset any old _WAKE requests. - */ - cs->halted = 1; - cpu_reset_interrupt(cs, CPU_INTERRUPT_WAKE); -} - -static inline void mips_tc_wake(MIPSCPU *cpu, int tc) -{ - CPUMIPSState *c = &cpu->env; - - /* FIXME: TC reschedule. */ - if (mips_vpe_active(c) && !mips_vpe_is_wfi(cpu)) { - mips_vpe_wake(cpu); - } -} - -static inline void mips_tc_sleep(MIPSCPU *cpu, int tc) -{ - CPUMIPSState *c = &cpu->env; - - /* FIXME: TC reschedule. */ - if (!mips_vpe_active(c)) { - mips_vpe_sleep(cpu); - } -} - -/** - * mips_cpu_map_tc: - * @env: CPU from which mapping is performed. - * @tc: Should point to an int with the value of the global TC index. - * - * This function will transform @tc into a local index within the - * returned #CPUMIPSState. - */ - -/* - * FIXME: This code assumes that all VPEs have the same number of TCs, - * which depends on runtime setup. Can probably be fixed by - * walking the list of CPUMIPSStates. - */ -static CPUMIPSState *mips_cpu_map_tc(CPUMIPSState *env, int *tc) -{ - MIPSCPU *cpu; - CPUState *cs; - CPUState *other_cs; - int vpe_idx; - int tc_idx = *tc; - - if (!(env->CP0_VPEConf0 & (1 << CP0VPEC0_MVP))) { - /* Not allowed to address other CPUs. */ - *tc = env->current_tc; - return env; - } - - cs = env_cpu(env); - vpe_idx = tc_idx / cs->nr_threads; - *tc = tc_idx % cs->nr_threads; - other_cs = qemu_get_cpu(vpe_idx); - if (other_cs == NULL) { - return env; - } - cpu = MIPS_CPU(other_cs); - return &cpu->env; -} - -/* - * The per VPE CP0_Status register shares some fields with the per TC - * CP0_TCStatus registers. These fields are wired to the same registers, - * so changes to either of them should be reflected on both registers. - * - * Also, EntryHi shares the bottom 8 bit ASID with TCStauts. - * - * These helper call synchronizes the regs for a given cpu. - */ - -/* - * Called for updates to CP0_Status. Defined in "cpu.h" for gdbstub.c. - * static inline void sync_c0_status(CPUMIPSState *env, CPUMIPSState *cpu, - * int tc); - */ - -/* Called for updates to CP0_TCStatus. */ -static void sync_c0_tcstatus(CPUMIPSState *cpu, int tc, - target_ulong v) -{ - uint32_t status; - uint32_t tcu, tmx, tasid, tksu; - uint32_t mask = ((1U << CP0St_CU3) - | (1 << CP0St_CU2) - | (1 << CP0St_CU1) - | (1 << CP0St_CU0) - | (1 << CP0St_MX) - | (3 << CP0St_KSU)); - - tcu = (v >> CP0TCSt_TCU0) & 0xf; - tmx = (v >> CP0TCSt_TMX) & 0x1; - tasid = v & cpu->CP0_EntryHi_ASID_mask; - tksu = (v >> CP0TCSt_TKSU) & 0x3; - - status = tcu << CP0St_CU0; - status |= tmx << CP0St_MX; - status |= tksu << CP0St_KSU; - - cpu->CP0_Status &= ~mask; - cpu->CP0_Status |= status; - - /* Sync the TASID with EntryHi. */ - cpu->CP0_EntryHi &= ~cpu->CP0_EntryHi_ASID_mask; - cpu->CP0_EntryHi |= tasid; - - compute_hflags(cpu); -} - -/* Called for updates to CP0_EntryHi. */ -static void sync_c0_entryhi(CPUMIPSState *cpu, int tc) -{ - int32_t *tcst; - uint32_t asid, v = cpu->CP0_EntryHi; - - asid = v & cpu->CP0_EntryHi_ASID_mask; - - if (tc == cpu->current_tc) { - tcst = &cpu->active_tc.CP0_TCStatus; - } else { - tcst = &cpu->tcs[tc].CP0_TCStatus; - } - - *tcst &= ~cpu->CP0_EntryHi_ASID_mask; - *tcst |= asid; -} - -/* CP0 helpers */ -target_ulong helper_mfc0_mvpcontrol(CPUMIPSState *env) -{ - return env->mvp->CP0_MVPControl; -} - -target_ulong helper_mfc0_mvpconf0(CPUMIPSState *env) -{ - return env->mvp->CP0_MVPConf0; -} - -target_ulong helper_mfc0_mvpconf1(CPUMIPSState *env) -{ - return env->mvp->CP0_MVPConf1; -} - -target_ulong helper_mfc0_random(CPUMIPSState *env) -{ - return (int32_t)cpu_mips_get_random(env); -} - -target_ulong helper_mfc0_tcstatus(CPUMIPSState *env) -{ - return env->active_tc.CP0_TCStatus; -} - -target_ulong helper_mftc0_tcstatus(CPUMIPSState *env) -{ - int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); - CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); - - if (other_tc == other->current_tc) { - return other->active_tc.CP0_TCStatus; - } else { - return other->tcs[other_tc].CP0_TCStatus; - } -} - -target_ulong helper_mfc0_tcbind(CPUMIPSState *env) -{ - return env->active_tc.CP0_TCBind; -} - -target_ulong helper_mftc0_tcbind(CPUMIPSState *env) -{ - int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); - CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); - - if (other_tc == other->current_tc) { - return other->active_tc.CP0_TCBind; - } else { - return other->tcs[other_tc].CP0_TCBind; - } -} - -target_ulong helper_mfc0_tcrestart(CPUMIPSState *env) -{ - return env->active_tc.PC; -} - -target_ulong helper_mftc0_tcrestart(CPUMIPSState *env) -{ - int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); - CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); - - if (other_tc == other->current_tc) { - return other->active_tc.PC; - } else { - return other->tcs[other_tc].PC; - } -} - -target_ulong helper_mfc0_tchalt(CPUMIPSState *env) -{ - return env->active_tc.CP0_TCHalt; -} - -target_ulong helper_mftc0_tchalt(CPUMIPSState *env) -{ - int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); - CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); - - if (other_tc == other->current_tc) { - return other->active_tc.CP0_TCHalt; - } else { - return other->tcs[other_tc].CP0_TCHalt; - } -} - -target_ulong helper_mfc0_tccontext(CPUMIPSState *env) -{ - return env->active_tc.CP0_TCContext; -} - -target_ulong helper_mftc0_tccontext(CPUMIPSState *env) -{ - int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); - CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); - - if (other_tc == other->current_tc) { - return other->active_tc.CP0_TCContext; - } else { - return other->tcs[other_tc].CP0_TCContext; - } -} - -target_ulong helper_mfc0_tcschedule(CPUMIPSState *env) -{ - return env->active_tc.CP0_TCSchedule; -} - -target_ulong helper_mftc0_tcschedule(CPUMIPSState *env) -{ - int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); - CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); - - if (other_tc == other->current_tc) { - return other->active_tc.CP0_TCSchedule; - } else { - return other->tcs[other_tc].CP0_TCSchedule; - } -} - -target_ulong helper_mfc0_tcschefback(CPUMIPSState *env) -{ - return env->active_tc.CP0_TCScheFBack; -} - -target_ulong helper_mftc0_tcschefback(CPUMIPSState *env) -{ - int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); - CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); - - if (other_tc == other->current_tc) { - return other->active_tc.CP0_TCScheFBack; - } else { - return other->tcs[other_tc].CP0_TCScheFBack; - } -} - -target_ulong helper_mfc0_count(CPUMIPSState *env) -{ - return (int32_t)cpu_mips_get_count(env); -} - -target_ulong helper_mfc0_saar(CPUMIPSState *env) -{ - if ((env->CP0_SAARI & 0x3f) < 2) { - return (int32_t) env->CP0_SAAR[env->CP0_SAARI & 0x3f]; - } - return 0; -} - -target_ulong helper_mfhc0_saar(CPUMIPSState *env) -{ - if ((env->CP0_SAARI & 0x3f) < 2) { - return env->CP0_SAAR[env->CP0_SAARI & 0x3f] >> 32; - } - return 0; -} - -target_ulong helper_mftc0_entryhi(CPUMIPSState *env) -{ - int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); - CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); - - return other->CP0_EntryHi; -} - -target_ulong helper_mftc0_cause(CPUMIPSState *env) -{ - int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); - int32_t tccause; - CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); - - if (other_tc == other->current_tc) { - tccause = other->CP0_Cause; - } else { - tccause = other->CP0_Cause; - } - - return tccause; -} - -target_ulong helper_mftc0_status(CPUMIPSState *env) -{ - int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); - CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); - - return other->CP0_Status; -} - -target_ulong helper_mfc0_lladdr(CPUMIPSState *env) -{ - return (int32_t)(env->CP0_LLAddr >> env->CP0_LLAddr_shift); -} - -target_ulong helper_mfc0_maar(CPUMIPSState *env) -{ - return (int32_t) env->CP0_MAAR[env->CP0_MAARI]; -} - -target_ulong helper_mfhc0_maar(CPUMIPSState *env) -{ - return env->CP0_MAAR[env->CP0_MAARI] >> 32; -} - -target_ulong helper_mfc0_watchlo(CPUMIPSState *env, uint32_t sel) -{ - return (int32_t)env->CP0_WatchLo[sel]; -} - -target_ulong helper_mfc0_watchhi(CPUMIPSState *env, uint32_t sel) -{ - return env->CP0_WatchHi[sel]; -} - -target_ulong helper_mfc0_debug(CPUMIPSState *env) -{ - target_ulong t0 = env->CP0_Debug; - if (env->hflags & MIPS_HFLAG_DM) { - t0 |= 1 << CP0DB_DM; - } - - return t0; -} - -target_ulong helper_mftc0_debug(CPUMIPSState *env) -{ - int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); - int32_t tcstatus; - CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); - - if (other_tc == other->current_tc) { - tcstatus = other->active_tc.CP0_Debug_tcstatus; - } else { - tcstatus = other->tcs[other_tc].CP0_Debug_tcstatus; - } - - /* XXX: Might be wrong, check with EJTAG spec. */ - return (other->CP0_Debug & ~((1 << CP0DB_SSt) | (1 << CP0DB_Halt))) | - (tcstatus & ((1 << CP0DB_SSt) | (1 << CP0DB_Halt))); -} - -#if defined(TARGET_MIPS64) -target_ulong helper_dmfc0_tcrestart(CPUMIPSState *env) -{ - return env->active_tc.PC; -} - -target_ulong helper_dmfc0_tchalt(CPUMIPSState *env) -{ - return env->active_tc.CP0_TCHalt; -} - -target_ulong helper_dmfc0_tccontext(CPUMIPSState *env) -{ - return env->active_tc.CP0_TCContext; -} - -target_ulong helper_dmfc0_tcschedule(CPUMIPSState *env) -{ - return env->active_tc.CP0_TCSchedule; -} - -target_ulong helper_dmfc0_tcschefback(CPUMIPSState *env) -{ - return env->active_tc.CP0_TCScheFBack; -} - -target_ulong helper_dmfc0_lladdr(CPUMIPSState *env) -{ - return env->CP0_LLAddr >> env->CP0_LLAddr_shift; -} - -target_ulong helper_dmfc0_maar(CPUMIPSState *env) -{ - return env->CP0_MAAR[env->CP0_MAARI]; -} - -target_ulong helper_dmfc0_watchlo(CPUMIPSState *env, uint32_t sel) -{ - return env->CP0_WatchLo[sel]; -} - -target_ulong helper_dmfc0_saar(CPUMIPSState *env) -{ - if ((env->CP0_SAARI & 0x3f) < 2) { - return env->CP0_SAAR[env->CP0_SAARI & 0x3f]; - } - return 0; -} -#endif /* TARGET_MIPS64 */ - -void helper_mtc0_index(CPUMIPSState *env, target_ulong arg1) -{ - uint32_t index_p = env->CP0_Index & 0x80000000; - uint32_t tlb_index = arg1 & 0x7fffffff; - if (tlb_index < env->tlb->nb_tlb) { - if (env->insn_flags & ISA_MIPS32R6) { - index_p |= arg1 & 0x80000000; - } - env->CP0_Index = index_p | tlb_index; - } -} - -void helper_mtc0_mvpcontrol(CPUMIPSState *env, target_ulong arg1) -{ - uint32_t mask = 0; - uint32_t newval; - - if (env->CP0_VPEConf0 & (1 << CP0VPEC0_MVP)) { - mask |= (1 << CP0MVPCo_CPA) | (1 << CP0MVPCo_VPC) | - (1 << CP0MVPCo_EVP); - } - if (env->mvp->CP0_MVPControl & (1 << CP0MVPCo_VPC)) { - mask |= (1 << CP0MVPCo_STLB); - } - newval = (env->mvp->CP0_MVPControl & ~mask) | (arg1 & mask); - - /* TODO: Enable/disable shared TLB, enable/disable VPEs. */ - - env->mvp->CP0_MVPControl = newval; -} - -void helper_mtc0_vpecontrol(CPUMIPSState *env, target_ulong arg1) -{ - uint32_t mask; - uint32_t newval; - - mask = (1 << CP0VPECo_YSI) | (1 << CP0VPECo_GSI) | - (1 << CP0VPECo_TE) | (0xff << CP0VPECo_TargTC); - newval = (env->CP0_VPEControl & ~mask) | (arg1 & mask); - - /* - * Yield scheduler intercept not implemented. - * Gating storage scheduler intercept not implemented. - */ - - /* TODO: Enable/disable TCs. */ - - env->CP0_VPEControl = newval; -} - -void helper_mttc0_vpecontrol(CPUMIPSState *env, target_ulong arg1) -{ - int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); - CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); - uint32_t mask; - uint32_t newval; - - mask = (1 << CP0VPECo_YSI) | (1 << CP0VPECo_GSI) | - (1 << CP0VPECo_TE) | (0xff << CP0VPECo_TargTC); - newval = (other->CP0_VPEControl & ~mask) | (arg1 & mask); - - /* TODO: Enable/disable TCs. */ - - other->CP0_VPEControl = newval; -} - -target_ulong helper_mftc0_vpecontrol(CPUMIPSState *env) -{ - int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); - CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); - /* FIXME: Mask away return zero on read bits. */ - return other->CP0_VPEControl; -} - -target_ulong helper_mftc0_vpeconf0(CPUMIPSState *env) -{ - int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); - CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); - - return other->CP0_VPEConf0; -} - -void helper_mtc0_vpeconf0(CPUMIPSState *env, target_ulong arg1) -{ - uint32_t mask = 0; - uint32_t newval; - - if (env->CP0_VPEConf0 & (1 << CP0VPEC0_MVP)) { - if (env->CP0_VPEConf0 & (1 << CP0VPEC0_VPA)) { - mask |= (0xff << CP0VPEC0_XTC); - } - mask |= (1 << CP0VPEC0_MVP) | (1 << CP0VPEC0_VPA); - } - newval = (env->CP0_VPEConf0 & ~mask) | (arg1 & mask); - - /* TODO: TC exclusive handling due to ERL/EXL. */ - - env->CP0_VPEConf0 = newval; -} - -void helper_mttc0_vpeconf0(CPUMIPSState *env, target_ulong arg1) -{ - int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); - CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); - uint32_t mask = 0; - uint32_t newval; - - mask |= (1 << CP0VPEC0_MVP) | (1 << CP0VPEC0_VPA); - newval = (other->CP0_VPEConf0 & ~mask) | (arg1 & mask); - - /* TODO: TC exclusive handling due to ERL/EXL. */ - other->CP0_VPEConf0 = newval; -} - -void helper_mtc0_vpeconf1(CPUMIPSState *env, target_ulong arg1) -{ - uint32_t mask = 0; - uint32_t newval; - - if (env->mvp->CP0_MVPControl & (1 << CP0MVPCo_VPC)) - mask |= (0xff << CP0VPEC1_NCX) | (0xff << CP0VPEC1_NCP2) | - (0xff << CP0VPEC1_NCP1); - newval = (env->CP0_VPEConf1 & ~mask) | (arg1 & mask); - - /* UDI not implemented. */ - /* CP2 not implemented. */ - - /* TODO: Handle FPU (CP1) binding. */ - - env->CP0_VPEConf1 = newval; -} - -void helper_mtc0_yqmask(CPUMIPSState *env, target_ulong arg1) -{ - /* Yield qualifier inputs not implemented. */ - env->CP0_YQMask = 0x00000000; -} - -void helper_mtc0_vpeopt(CPUMIPSState *env, target_ulong arg1) -{ - env->CP0_VPEOpt = arg1 & 0x0000ffff; -} - -#define MTC0_ENTRYLO_MASK(env) ((env->PAMask >> 6) & 0x3FFFFFFF) - -void helper_mtc0_entrylo0(CPUMIPSState *env, target_ulong arg1) -{ - /* 1k pages not implemented */ - target_ulong rxi = arg1 & (env->CP0_PageGrain & (3u << CP0PG_XIE)); - env->CP0_EntryLo0 = (arg1 & MTC0_ENTRYLO_MASK(env)) - | (rxi << (CP0EnLo_XI - 30)); -} - -#if defined(TARGET_MIPS64) -#define DMTC0_ENTRYLO_MASK(env) (env->PAMask >> 6) - -void helper_dmtc0_entrylo0(CPUMIPSState *env, uint64_t arg1) -{ - uint64_t rxi = arg1 & ((env->CP0_PageGrain & (3ull << CP0PG_XIE)) << 32); - env->CP0_EntryLo0 = (arg1 & DMTC0_ENTRYLO_MASK(env)) | rxi; -} -#endif - -void helper_mtc0_tcstatus(CPUMIPSState *env, target_ulong arg1) -{ - uint32_t mask = env->CP0_TCStatus_rw_bitmask; - uint32_t newval; - - newval = (env->active_tc.CP0_TCStatus & ~mask) | (arg1 & mask); - - env->active_tc.CP0_TCStatus = newval; - sync_c0_tcstatus(env, env->current_tc, newval); -} - -void helper_mttc0_tcstatus(CPUMIPSState *env, target_ulong arg1) -{ - int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); - CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); - - if (other_tc == other->current_tc) { - other->active_tc.CP0_TCStatus = arg1; - } else { - other->tcs[other_tc].CP0_TCStatus = arg1; - } - sync_c0_tcstatus(other, other_tc, arg1); -} - -void helper_mtc0_tcbind(CPUMIPSState *env, target_ulong arg1) -{ - uint32_t mask = (1 << CP0TCBd_TBE); - uint32_t newval; - - if (env->mvp->CP0_MVPControl & (1 << CP0MVPCo_VPC)) { - mask |= (1 << CP0TCBd_CurVPE); - } - newval = (env->active_tc.CP0_TCBind & ~mask) | (arg1 & mask); - env->active_tc.CP0_TCBind = newval; -} - -void helper_mttc0_tcbind(CPUMIPSState *env, target_ulong arg1) -{ - int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); - uint32_t mask = (1 << CP0TCBd_TBE); - uint32_t newval; - CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); - - if (other->mvp->CP0_MVPControl & (1 << CP0MVPCo_VPC)) { - mask |= (1 << CP0TCBd_CurVPE); - } - if (other_tc == other->current_tc) { - newval = (other->active_tc.CP0_TCBind & ~mask) | (arg1 & mask); - other->active_tc.CP0_TCBind = newval; - } else { - newval = (other->tcs[other_tc].CP0_TCBind & ~mask) | (arg1 & mask); - other->tcs[other_tc].CP0_TCBind = newval; - } -} - -void helper_mtc0_tcrestart(CPUMIPSState *env, target_ulong arg1) -{ - env->active_tc.PC = arg1; - env->active_tc.CP0_TCStatus &= ~(1 << CP0TCSt_TDS); - env->CP0_LLAddr = 0; - env->lladdr = 0; - /* MIPS16 not implemented. */ -} - -void helper_mttc0_tcrestart(CPUMIPSState *env, target_ulong arg1) -{ - int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); - CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); - - if (other_tc == other->current_tc) { - other->active_tc.PC = arg1; - other->active_tc.CP0_TCStatus &= ~(1 << CP0TCSt_TDS); - other->CP0_LLAddr = 0; - other->lladdr = 0; - /* MIPS16 not implemented. */ - } else { - other->tcs[other_tc].PC = arg1; - other->tcs[other_tc].CP0_TCStatus &= ~(1 << CP0TCSt_TDS); - other->CP0_LLAddr = 0; - other->lladdr = 0; - /* MIPS16 not implemented. */ - } -} - -void helper_mtc0_tchalt(CPUMIPSState *env, target_ulong arg1) -{ - MIPSCPU *cpu = env_archcpu(env); - - env->active_tc.CP0_TCHalt = arg1 & 0x1; - - /* TODO: Halt TC / Restart (if allocated+active) TC. */ - if (env->active_tc.CP0_TCHalt & 1) { - mips_tc_sleep(cpu, env->current_tc); - } else { - mips_tc_wake(cpu, env->current_tc); - } -} - -void helper_mttc0_tchalt(CPUMIPSState *env, target_ulong arg1) -{ - int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); - CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); - MIPSCPU *other_cpu = env_archcpu(other); - - /* TODO: Halt TC / Restart (if allocated+active) TC. */ - - if (other_tc == other->current_tc) { - other->active_tc.CP0_TCHalt = arg1; - } else { - other->tcs[other_tc].CP0_TCHalt = arg1; - } - - if (arg1 & 1) { - mips_tc_sleep(other_cpu, other_tc); - } else { - mips_tc_wake(other_cpu, other_tc); - } -} - -void helper_mtc0_tccontext(CPUMIPSState *env, target_ulong arg1) -{ - env->active_tc.CP0_TCContext = arg1; -} - -void helper_mttc0_tccontext(CPUMIPSState *env, target_ulong arg1) -{ - int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); - CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); - - if (other_tc == other->current_tc) { - other->active_tc.CP0_TCContext = arg1; - } else { - other->tcs[other_tc].CP0_TCContext = arg1; - } -} - -void helper_mtc0_tcschedule(CPUMIPSState *env, target_ulong arg1) -{ - env->active_tc.CP0_TCSchedule = arg1; -} - -void helper_mttc0_tcschedule(CPUMIPSState *env, target_ulong arg1) -{ - int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); - CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); - - if (other_tc == other->current_tc) { - other->active_tc.CP0_TCSchedule = arg1; - } else { - other->tcs[other_tc].CP0_TCSchedule = arg1; - } -} - -void helper_mtc0_tcschefback(CPUMIPSState *env, target_ulong arg1) -{ - env->active_tc.CP0_TCScheFBack = arg1; -} - -void helper_mttc0_tcschefback(CPUMIPSState *env, target_ulong arg1) -{ - int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); - CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); - - if (other_tc == other->current_tc) { - other->active_tc.CP0_TCScheFBack = arg1; - } else { - other->tcs[other_tc].CP0_TCScheFBack = arg1; - } -} - -void helper_mtc0_entrylo1(CPUMIPSState *env, target_ulong arg1) -{ - /* 1k pages not implemented */ - target_ulong rxi = arg1 & (env->CP0_PageGrain & (3u << CP0PG_XIE)); - env->CP0_EntryLo1 = (arg1 & MTC0_ENTRYLO_MASK(env)) - | (rxi << (CP0EnLo_XI - 30)); -} - -#if defined(TARGET_MIPS64) -void helper_dmtc0_entrylo1(CPUMIPSState *env, uint64_t arg1) -{ - uint64_t rxi = arg1 & ((env->CP0_PageGrain & (3ull << CP0PG_XIE)) << 32); - env->CP0_EntryLo1 = (arg1 & DMTC0_ENTRYLO_MASK(env)) | rxi; -} -#endif - -void helper_mtc0_context(CPUMIPSState *env, target_ulong arg1) -{ - env->CP0_Context = (env->CP0_Context & 0x007FFFFF) | (arg1 & ~0x007FFFFF); -} - -void update_pagemask(CPUMIPSState *env, target_ulong arg1, int32_t *pagemask) -{ - uint64_t mask = arg1 >> (TARGET_PAGE_BITS + 1); - if (!(env->insn_flags & ISA_MIPS32R6) || (arg1 == ~0) || - (mask == 0x0000 || mask == 0x0003 || mask == 0x000F || - mask == 0x003F || mask == 0x00FF || mask == 0x03FF || - mask == 0x0FFF || mask == 0x3FFF || mask == 0xFFFF)) { - env->CP0_PageMask = arg1 & (0x1FFFFFFF & (TARGET_PAGE_MASK << 1)); - } -} - -void helper_mtc0_pagemask(CPUMIPSState *env, target_ulong arg1) -{ - update_pagemask(env, arg1, &env->CP0_PageMask); -} - -void helper_mtc0_pagegrain(CPUMIPSState *env, target_ulong arg1) -{ - /* SmartMIPS not implemented */ - /* 1k pages not implemented */ - env->CP0_PageGrain = (arg1 & env->CP0_PageGrain_rw_bitmask) | - (env->CP0_PageGrain & ~env->CP0_PageGrain_rw_bitmask); - compute_hflags(env); - restore_pamask(env); -} - -void helper_mtc0_segctl0(CPUMIPSState *env, target_ulong arg1) -{ - CPUState *cs = env_cpu(env); - - env->CP0_SegCtl0 = arg1 & CP0SC0_MASK; - tlb_flush(cs); -} - -void helper_mtc0_segctl1(CPUMIPSState *env, target_ulong arg1) -{ - CPUState *cs = env_cpu(env); - - env->CP0_SegCtl1 = arg1 & CP0SC1_MASK; - tlb_flush(cs); -} - -void helper_mtc0_segctl2(CPUMIPSState *env, target_ulong arg1) -{ - CPUState *cs = env_cpu(env); - - env->CP0_SegCtl2 = arg1 & CP0SC2_MASK; - tlb_flush(cs); -} - -void helper_mtc0_pwfield(CPUMIPSState *env, target_ulong arg1) -{ -#if defined(TARGET_MIPS64) - uint64_t mask = 0x3F3FFFFFFFULL; - uint32_t old_ptei = (env->CP0_PWField >> CP0PF_PTEI) & 0x3FULL; - uint32_t new_ptei = (arg1 >> CP0PF_PTEI) & 0x3FULL; - - if ((env->insn_flags & ISA_MIPS32R6)) { - if (((arg1 >> CP0PF_BDI) & 0x3FULL) < 12) { - mask &= ~(0x3FULL << CP0PF_BDI); - } - if (((arg1 >> CP0PF_GDI) & 0x3FULL) < 12) { - mask &= ~(0x3FULL << CP0PF_GDI); - } - if (((arg1 >> CP0PF_UDI) & 0x3FULL) < 12) { - mask &= ~(0x3FULL << CP0PF_UDI); - } - if (((arg1 >> CP0PF_MDI) & 0x3FULL) < 12) { - mask &= ~(0x3FULL << CP0PF_MDI); - } - if (((arg1 >> CP0PF_PTI) & 0x3FULL) < 12) { - mask &= ~(0x3FULL << CP0PF_PTI); - } - } - env->CP0_PWField = arg1 & mask; - - if ((new_ptei >= 32) || - ((env->insn_flags & ISA_MIPS32R6) && - (new_ptei == 0 || new_ptei == 1))) { - env->CP0_PWField = (env->CP0_PWField & ~0x3FULL) | - (old_ptei << CP0PF_PTEI); - } -#else - uint32_t mask = 0x3FFFFFFF; - uint32_t old_ptew = (env->CP0_PWField >> CP0PF_PTEW) & 0x3F; - uint32_t new_ptew = (arg1 >> CP0PF_PTEW) & 0x3F; - - if ((env->insn_flags & ISA_MIPS32R6)) { - if (((arg1 >> CP0PF_GDW) & 0x3F) < 12) { - mask &= ~(0x3F << CP0PF_GDW); - } - if (((arg1 >> CP0PF_UDW) & 0x3F) < 12) { - mask &= ~(0x3F << CP0PF_UDW); - } - if (((arg1 >> CP0PF_MDW) & 0x3F) < 12) { - mask &= ~(0x3F << CP0PF_MDW); - } - if (((arg1 >> CP0PF_PTW) & 0x3F) < 12) { - mask &= ~(0x3F << CP0PF_PTW); - } - } - env->CP0_PWField = arg1 & mask; - - if ((new_ptew >= 32) || - ((env->insn_flags & ISA_MIPS32R6) && - (new_ptew == 0 || new_ptew == 1))) { - env->CP0_PWField = (env->CP0_PWField & ~0x3F) | - (old_ptew << CP0PF_PTEW); - } -#endif -} - -void helper_mtc0_pwsize(CPUMIPSState *env, target_ulong arg1) -{ -#if defined(TARGET_MIPS64) - env->CP0_PWSize = arg1 & 0x3F7FFFFFFFULL; -#else - env->CP0_PWSize = arg1 & 0x3FFFFFFF; -#endif -} - -void helper_mtc0_wired(CPUMIPSState *env, target_ulong arg1) -{ - if (env->insn_flags & ISA_MIPS32R6) { - if (arg1 < env->tlb->nb_tlb) { - env->CP0_Wired = arg1; - } - } else { - env->CP0_Wired = arg1 % env->tlb->nb_tlb; - } -} - -void helper_mtc0_pwctl(CPUMIPSState *env, target_ulong arg1) -{ -#if defined(TARGET_MIPS64) - /* PWEn = 0. Hardware page table walking is not implemented. */ - env->CP0_PWCtl = (env->CP0_PWCtl & 0x000000C0) | (arg1 & 0x5C00003F); -#else - env->CP0_PWCtl = (arg1 & 0x800000FF); -#endif -} - -void helper_mtc0_srsconf0(CPUMIPSState *env, target_ulong arg1) -{ - env->CP0_SRSConf0 |= arg1 & env->CP0_SRSConf0_rw_bitmask; -} - -void helper_mtc0_srsconf1(CPUMIPSState *env, target_ulong arg1) -{ - env->CP0_SRSConf1 |= arg1 & env->CP0_SRSConf1_rw_bitmask; -} - -void helper_mtc0_srsconf2(CPUMIPSState *env, target_ulong arg1) -{ - env->CP0_SRSConf2 |= arg1 & env->CP0_SRSConf2_rw_bitmask; -} - -void helper_mtc0_srsconf3(CPUMIPSState *env, target_ulong arg1) -{ - env->CP0_SRSConf3 |= arg1 & env->CP0_SRSConf3_rw_bitmask; -} - -void helper_mtc0_srsconf4(CPUMIPSState *env, target_ulong arg1) -{ - env->CP0_SRSConf4 |= arg1 & env->CP0_SRSConf4_rw_bitmask; -} - -void helper_mtc0_hwrena(CPUMIPSState *env, target_ulong arg1) -{ - uint32_t mask = 0x0000000F; - - if ((env->CP0_Config1 & (1 << CP0C1_PC)) && - (env->insn_flags & ISA_MIPS32R6)) { - mask |= (1 << 4); - } - if (env->insn_flags & ISA_MIPS32R6) { - mask |= (1 << 5); - } - if (env->CP0_Config3 & (1 << CP0C3_ULRI)) { - mask |= (1 << 29); - - if (arg1 & (1 << 29)) { - env->hflags |= MIPS_HFLAG_HWRENA_ULR; - } else { - env->hflags &= ~MIPS_HFLAG_HWRENA_ULR; - } - } - - env->CP0_HWREna = arg1 & mask; -} - -void helper_mtc0_count(CPUMIPSState *env, target_ulong arg1) -{ - cpu_mips_store_count(env, arg1); -} - -void helper_mtc0_saari(CPUMIPSState *env, target_ulong arg1) -{ - uint32_t target = arg1 & 0x3f; - if (target <= 1) { - env->CP0_SAARI = target; - } -} - -void helper_mtc0_saar(CPUMIPSState *env, target_ulong arg1) -{ - uint32_t target = env->CP0_SAARI & 0x3f; - if (target < 2) { - env->CP0_SAAR[target] = arg1 & 0x00000ffffffff03fULL; - switch (target) { - case 0: - if (env->itu) { - itc_reconfigure(env->itu); - } - break; - } - } -} - -void helper_mthc0_saar(CPUMIPSState *env, target_ulong arg1) -{ - uint32_t target = env->CP0_SAARI & 0x3f; - if (target < 2) { - env->CP0_SAAR[target] = - (((uint64_t) arg1 << 32) & 0x00000fff00000000ULL) | - (env->CP0_SAAR[target] & 0x00000000ffffffffULL); - switch (target) { - case 0: - if (env->itu) { - itc_reconfigure(env->itu); - } - break; - } - } -} - -void helper_mtc0_entryhi(CPUMIPSState *env, target_ulong arg1) -{ - target_ulong old, val, mask; - mask = (TARGET_PAGE_MASK << 1) | env->CP0_EntryHi_ASID_mask; - if (((env->CP0_Config4 >> CP0C4_IE) & 0x3) >= 2) { - mask |= 1 << CP0EnHi_EHINV; - } - - /* 1k pages not implemented */ -#if defined(TARGET_MIPS64) - if (env->insn_flags & ISA_MIPS32R6) { - int entryhi_r = extract64(arg1, 62, 2); - int config0_at = extract32(env->CP0_Config0, 13, 2); - bool no_supervisor = (env->CP0_Status_rw_bitmask & 0x8) == 0; - if ((entryhi_r == 2) || - (entryhi_r == 1 && (no_supervisor || config0_at == 1))) { - /* skip EntryHi.R field if new value is reserved */ - mask &= ~(0x3ull << 62); - } - } - mask &= env->SEGMask; -#endif - old = env->CP0_EntryHi; - val = (arg1 & mask) | (old & ~mask); - env->CP0_EntryHi = val; - if (env->CP0_Config3 & (1 << CP0C3_MT)) { - sync_c0_entryhi(env, env->current_tc); - } - /* If the ASID changes, flush qemu's TLB. */ - if ((old & env->CP0_EntryHi_ASID_mask) != - (val & env->CP0_EntryHi_ASID_mask)) { - tlb_flush(env_cpu(env)); - } -} - -void helper_mttc0_entryhi(CPUMIPSState *env, target_ulong arg1) -{ - int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); - CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); - - other->CP0_EntryHi = arg1; - sync_c0_entryhi(other, other_tc); -} - -void helper_mtc0_compare(CPUMIPSState *env, target_ulong arg1) -{ - cpu_mips_store_compare(env, arg1); -} - -void helper_mtc0_status(CPUMIPSState *env, target_ulong arg1) -{ - uint32_t val, old; - - old = env->CP0_Status; - cpu_mips_store_status(env, arg1); - val = env->CP0_Status; - - if (qemu_loglevel_mask(CPU_LOG_EXEC)) { - qemu_log("Status %08x (%08x) => %08x (%08x) Cause %08x", - old, old & env->CP0_Cause & CP0Ca_IP_mask, - val, val & env->CP0_Cause & CP0Ca_IP_mask, - env->CP0_Cause); - switch (cpu_mmu_index(env, false)) { - case 3: - qemu_log(", ERL\n"); - break; - case MIPS_HFLAG_UM: - qemu_log(", UM\n"); - break; - case MIPS_HFLAG_SM: - qemu_log(", SM\n"); - break; - case MIPS_HFLAG_KM: - qemu_log("\n"); - break; - default: - cpu_abort(env_cpu(env), "Invalid MMU mode!\n"); - break; - } - } -} - -void helper_mttc0_status(CPUMIPSState *env, target_ulong arg1) -{ - int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); - uint32_t mask = env->CP0_Status_rw_bitmask & ~0xf1000018; - CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); - - other->CP0_Status = (other->CP0_Status & ~mask) | (arg1 & mask); - sync_c0_status(env, other, other_tc); -} - -void helper_mtc0_intctl(CPUMIPSState *env, target_ulong arg1) -{ - env->CP0_IntCtl = (env->CP0_IntCtl & ~0x000003e0) | (arg1 & 0x000003e0); -} - -void helper_mtc0_srsctl(CPUMIPSState *env, target_ulong arg1) -{ - uint32_t mask = (0xf << CP0SRSCtl_ESS) | (0xf << CP0SRSCtl_PSS); - env->CP0_SRSCtl = (env->CP0_SRSCtl & ~mask) | (arg1 & mask); -} - -void helper_mtc0_cause(CPUMIPSState *env, target_ulong arg1) -{ - cpu_mips_store_cause(env, arg1); -} - -void helper_mttc0_cause(CPUMIPSState *env, target_ulong arg1) -{ - int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); - CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); - - cpu_mips_store_cause(other, arg1); -} - -target_ulong helper_mftc0_epc(CPUMIPSState *env) -{ - int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); - CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); - - return other->CP0_EPC; -} - -target_ulong helper_mftc0_ebase(CPUMIPSState *env) -{ - int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); - CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); - - return other->CP0_EBase; -} - -void helper_mtc0_ebase(CPUMIPSState *env, target_ulong arg1) -{ - target_ulong mask = 0x3FFFF000 | env->CP0_EBaseWG_rw_bitmask; - if (arg1 & env->CP0_EBaseWG_rw_bitmask) { - mask |= ~0x3FFFFFFF; - } - env->CP0_EBase = (env->CP0_EBase & ~mask) | (arg1 & mask); -} - -void helper_mttc0_ebase(CPUMIPSState *env, target_ulong arg1) -{ - int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); - CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); - target_ulong mask = 0x3FFFF000 | env->CP0_EBaseWG_rw_bitmask; - if (arg1 & env->CP0_EBaseWG_rw_bitmask) { - mask |= ~0x3FFFFFFF; - } - other->CP0_EBase = (other->CP0_EBase & ~mask) | (arg1 & mask); -} - -target_ulong helper_mftc0_configx(CPUMIPSState *env, target_ulong idx) -{ - int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); - CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); - - switch (idx) { - case 0: return other->CP0_Config0; - case 1: return other->CP0_Config1; - case 2: return other->CP0_Config2; - case 3: return other->CP0_Config3; - /* 4 and 5 are reserved. */ - case 6: return other->CP0_Config6; - case 7: return other->CP0_Config7; - default: - break; - } - return 0; -} - -void helper_mtc0_config0(CPUMIPSState *env, target_ulong arg1) -{ - env->CP0_Config0 = (env->CP0_Config0 & 0x81FFFFF8) | (arg1 & 0x00000007); -} - -void helper_mtc0_config2(CPUMIPSState *env, target_ulong arg1) -{ - /* tertiary/secondary caches not implemented */ - env->CP0_Config2 = (env->CP0_Config2 & 0x8FFF0FFF); -} - -void helper_mtc0_config3(CPUMIPSState *env, target_ulong arg1) -{ - if (env->insn_flags & ASE_MICROMIPS) { - env->CP0_Config3 = (env->CP0_Config3 & ~(1 << CP0C3_ISA_ON_EXC)) | - (arg1 & (1 << CP0C3_ISA_ON_EXC)); - } -} - -void helper_mtc0_config4(CPUMIPSState *env, target_ulong arg1) -{ - env->CP0_Config4 = (env->CP0_Config4 & (~env->CP0_Config4_rw_bitmask)) | - (arg1 & env->CP0_Config4_rw_bitmask); -} - -void helper_mtc0_config5(CPUMIPSState *env, target_ulong arg1) -{ - env->CP0_Config5 = (env->CP0_Config5 & (~env->CP0_Config5_rw_bitmask)) | - (arg1 & env->CP0_Config5_rw_bitmask); - compute_hflags(env); -} - -void helper_mtc0_lladdr(CPUMIPSState *env, target_ulong arg1) -{ - target_long mask = env->CP0_LLAddr_rw_bitmask; - arg1 = arg1 << env->CP0_LLAddr_shift; - env->CP0_LLAddr = (env->CP0_LLAddr & ~mask) | (arg1 & mask); -} - -#define MTC0_MAAR_MASK(env) \ - ((0x1ULL << 63) | ((env->PAMask >> 4) & ~0xFFFull) | 0x3) - -void helper_mtc0_maar(CPUMIPSState *env, target_ulong arg1) -{ - env->CP0_MAAR[env->CP0_MAARI] = arg1 & MTC0_MAAR_MASK(env); -} - -void helper_mthc0_maar(CPUMIPSState *env, target_ulong arg1) -{ - env->CP0_MAAR[env->CP0_MAARI] = - (((uint64_t) arg1 << 32) & MTC0_MAAR_MASK(env)) | - (env->CP0_MAAR[env->CP0_MAARI] & 0x00000000ffffffffULL); -} - -void helper_mtc0_maari(CPUMIPSState *env, target_ulong arg1) -{ - int index = arg1 & 0x3f; - if (index == 0x3f) { - /* - * Software may write all ones to INDEX to determine the - * maximum value supported. - */ - env->CP0_MAARI = MIPS_MAAR_MAX - 1; - } else if (index < MIPS_MAAR_MAX) { - env->CP0_MAARI = index; - } - /* - * Other than the all ones, if the value written is not supported, - * then INDEX is unchanged from its previous value. - */ -} - -void helper_mtc0_watchlo(CPUMIPSState *env, target_ulong arg1, uint32_t sel) -{ - /* - * Watch exceptions for instructions, data loads, data stores - * not implemented. - */ - env->CP0_WatchLo[sel] = (arg1 & ~0x7); -} - -void helper_mtc0_watchhi(CPUMIPSState *env, target_ulong arg1, uint32_t sel) -{ - int mask = 0x40000FF8 | (env->CP0_EntryHi_ASID_mask << CP0WH_ASID); - env->CP0_WatchHi[sel] = arg1 & mask; - env->CP0_WatchHi[sel] &= ~(env->CP0_WatchHi[sel] & arg1 & 0x7); -} - -void helper_mtc0_xcontext(CPUMIPSState *env, target_ulong arg1) -{ - target_ulong mask = (1ULL << (env->SEGBITS - 7)) - 1; - env->CP0_XContext = (env->CP0_XContext & mask) | (arg1 & ~mask); -} - -void helper_mtc0_framemask(CPUMIPSState *env, target_ulong arg1) -{ - env->CP0_Framemask = arg1; /* XXX */ -} - -void helper_mtc0_debug(CPUMIPSState *env, target_ulong arg1) -{ - env->CP0_Debug = (env->CP0_Debug & 0x8C03FC1F) | (arg1 & 0x13300120); - if (arg1 & (1 << CP0DB_DM)) { - env->hflags |= MIPS_HFLAG_DM; - } else { - env->hflags &= ~MIPS_HFLAG_DM; - } -} - -void helper_mttc0_debug(CPUMIPSState *env, target_ulong arg1) -{ - int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); - uint32_t val = arg1 & ((1 << CP0DB_SSt) | (1 << CP0DB_Halt)); - CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); - - /* XXX: Might be wrong, check with EJTAG spec. */ - if (other_tc == other->current_tc) { - other->active_tc.CP0_Debug_tcstatus = val; - } else { - other->tcs[other_tc].CP0_Debug_tcstatus = val; - } - other->CP0_Debug = (other->CP0_Debug & - ((1 << CP0DB_SSt) | (1 << CP0DB_Halt))) | - (arg1 & ~((1 << CP0DB_SSt) | (1 << CP0DB_Halt))); -} - -void helper_mtc0_performance0(CPUMIPSState *env, target_ulong arg1) -{ - env->CP0_Performance0 = arg1 & 0x000007ff; -} - -void helper_mtc0_errctl(CPUMIPSState *env, target_ulong arg1) -{ - int32_t wst = arg1 & (1 << CP0EC_WST); - int32_t spr = arg1 & (1 << CP0EC_SPR); - int32_t itc = env->itc_tag ? (arg1 & (1 << CP0EC_ITC)) : 0; - - env->CP0_ErrCtl = wst | spr | itc; - - if (itc && !wst && !spr) { - env->hflags |= MIPS_HFLAG_ITC_CACHE; - } else { - env->hflags &= ~MIPS_HFLAG_ITC_CACHE; - } -} - -void helper_mtc0_taglo(CPUMIPSState *env, target_ulong arg1) -{ - if (env->hflags & MIPS_HFLAG_ITC_CACHE) { - /* - * If CACHE instruction is configured for ITC tags then make all - * CP0.TagLo bits writable. The actual write to ITC Configuration - * Tag will take care of the read-only bits. - */ - env->CP0_TagLo = arg1; - } else { - env->CP0_TagLo = arg1 & 0xFFFFFCF6; - } -} - -void helper_mtc0_datalo(CPUMIPSState *env, target_ulong arg1) -{ - env->CP0_DataLo = arg1; /* XXX */ -} - -void helper_mtc0_taghi(CPUMIPSState *env, target_ulong arg1) -{ - env->CP0_TagHi = arg1; /* XXX */ -} - -void helper_mtc0_datahi(CPUMIPSState *env, target_ulong arg1) -{ - env->CP0_DataHi = arg1; /* XXX */ -} - -/* MIPS MT functions */ -target_ulong helper_mftgpr(CPUMIPSState *env, uint32_t sel) -{ - int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); - CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); - - if (other_tc == other->current_tc) { - return other->active_tc.gpr[sel]; - } else { - return other->tcs[other_tc].gpr[sel]; - } -} - -target_ulong helper_mftlo(CPUMIPSState *env, uint32_t sel) -{ - int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); - CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); - - if (other_tc == other->current_tc) { - return other->active_tc.LO[sel]; - } else { - return other->tcs[other_tc].LO[sel]; - } -} - -target_ulong helper_mfthi(CPUMIPSState *env, uint32_t sel) -{ - int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); - CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); - - if (other_tc == other->current_tc) { - return other->active_tc.HI[sel]; - } else { - return other->tcs[other_tc].HI[sel]; - } -} - -target_ulong helper_mftacx(CPUMIPSState *env, uint32_t sel) -{ - int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); - CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); - - if (other_tc == other->current_tc) { - return other->active_tc.ACX[sel]; - } else { - return other->tcs[other_tc].ACX[sel]; - } -} - -target_ulong helper_mftdsp(CPUMIPSState *env) -{ - int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); - CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); - - if (other_tc == other->current_tc) { - return other->active_tc.DSPControl; - } else { - return other->tcs[other_tc].DSPControl; - } -} - -void helper_mttgpr(CPUMIPSState *env, target_ulong arg1, uint32_t sel) -{ - int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); - CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); - - if (other_tc == other->current_tc) { - other->active_tc.gpr[sel] = arg1; - } else { - other->tcs[other_tc].gpr[sel] = arg1; - } -} - -void helper_mttlo(CPUMIPSState *env, target_ulong arg1, uint32_t sel) -{ - int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); - CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); - - if (other_tc == other->current_tc) { - other->active_tc.LO[sel] = arg1; - } else { - other->tcs[other_tc].LO[sel] = arg1; - } -} - -void helper_mtthi(CPUMIPSState *env, target_ulong arg1, uint32_t sel) -{ - int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); - CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); - - if (other_tc == other->current_tc) { - other->active_tc.HI[sel] = arg1; - } else { - other->tcs[other_tc].HI[sel] = arg1; - } -} - -void helper_mttacx(CPUMIPSState *env, target_ulong arg1, uint32_t sel) -{ - int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); - CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); - - if (other_tc == other->current_tc) { - other->active_tc.ACX[sel] = arg1; - } else { - other->tcs[other_tc].ACX[sel] = arg1; - } -} - -void helper_mttdsp(CPUMIPSState *env, target_ulong arg1) -{ - int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); - CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); - - if (other_tc == other->current_tc) { - other->active_tc.DSPControl = arg1; - } else { - other->tcs[other_tc].DSPControl = arg1; - } -} - -/* MIPS MT functions */ -target_ulong helper_dmt(void) -{ - /* TODO */ - return 0; -} - -target_ulong helper_emt(void) -{ - /* TODO */ - return 0; -} - -target_ulong helper_dvpe(CPUMIPSState *env) -{ - CPUState *other_cs = first_cpu; - target_ulong prev = env->mvp->CP0_MVPControl; - - CPU_FOREACH(other_cs) { - MIPSCPU *other_cpu = MIPS_CPU(other_cs); - /* Turn off all VPEs except the one executing the dvpe. */ - if (&other_cpu->env != env) { - other_cpu->env.mvp->CP0_MVPControl &= ~(1 << CP0MVPCo_EVP); - mips_vpe_sleep(other_cpu); - } - } - return prev; -} - -target_ulong helper_evpe(CPUMIPSState *env) -{ - CPUState *other_cs = first_cpu; - target_ulong prev = env->mvp->CP0_MVPControl; - - CPU_FOREACH(other_cs) { - MIPSCPU *other_cpu = MIPS_CPU(other_cs); - - if (&other_cpu->env != env - /* If the VPE is WFI, don't disturb its sleep. */ - && !mips_vpe_is_wfi(other_cpu)) { - /* Enable the VPE. */ - other_cpu->env.mvp->CP0_MVPControl |= (1 << CP0MVPCo_EVP); - mips_vpe_wake(other_cpu); /* And wake it up. */ - } - } - return prev; -} -#endif /* !CONFIG_USER_ONLY */ void helper_fork(target_ulong arg1, target_ulong arg2) { @@ -2232,48 +602,6 @@ target_ulong helper_yield(CPUMIPSState *env, target_ulong arg) return env->CP0_YQMask; } -/* R6 Multi-threading */ -#ifndef CONFIG_USER_ONLY -target_ulong helper_dvp(CPUMIPSState *env) -{ - CPUState *other_cs = first_cpu; - target_ulong prev = env->CP0_VPControl; - - if (!((env->CP0_VPControl >> CP0VPCtl_DIS) & 1)) { - CPU_FOREACH(other_cs) { - MIPSCPU *other_cpu = MIPS_CPU(other_cs); - /* Turn off all VPs except the one executing the dvp. */ - if (&other_cpu->env != env) { - mips_vpe_sleep(other_cpu); - } - } - env->CP0_VPControl |= (1 << CP0VPCtl_DIS); - } - return prev; -} - -target_ulong helper_evp(CPUMIPSState *env) -{ - CPUState *other_cs = first_cpu; - target_ulong prev = env->CP0_VPControl; - - if ((env->CP0_VPControl >> CP0VPCtl_DIS) & 1) { - CPU_FOREACH(other_cs) { - MIPSCPU *other_cpu = MIPS_CPU(other_cs); - if ((&other_cpu->env != env) && !mips_vp_is_wfi(other_cpu)) { - /* - * If the VP is WFI, don't disturb its sleep. - * Otherwise, wake it up. - */ - mips_vpe_wake(other_cpu); - } - } - env->CP0_VPControl &= ~(1 << CP0VPCtl_DIS); - } - return prev; -} -#endif /* !CONFIG_USER_ONLY */ - #ifndef CONFIG_USER_ONLY /* TLB management */ static void r4k_mips_tlb_flush_extra(CPUMIPSState *env, int first) @@ -2311,6 +639,7 @@ static void r4k_fill_tlb(CPUMIPSState *env, int idx) tlb->VPN &= env->SEGMask; #endif tlb->ASID = env->CP0_EntryHi & env->CP0_EntryHi_ASID_mask; + tlb->MMID = env->CP0_MemoryMapID; tlb->PageMask = env->CP0_PageMask; tlb->G = env->CP0_EntryLo0 & env->CP0_EntryLo1 & 1; tlb->V0 = (env->CP0_EntryLo0 & 2) != 0; @@ -2329,13 +658,18 @@ static void r4k_fill_tlb(CPUMIPSState *env, int idx) void r4k_helper_tlbinv(CPUMIPSState *env) { - int idx; - r4k_tlb_t *tlb; + bool mi = !!((env->CP0_Config5 >> CP0C5_MI) & 1); uint16_t ASID = env->CP0_EntryHi & env->CP0_EntryHi_ASID_mask; + uint32_t MMID = env->CP0_MemoryMapID; + uint32_t tlb_mmid; + r4k_tlb_t *tlb; + int idx; + MMID = mi ? MMID : (uint32_t) ASID; for (idx = 0; idx < env->tlb->nb_tlb; idx++) { tlb = &env->tlb->mmu.r4k.tlb[idx]; - if (!tlb->G && tlb->ASID == ASID) { + tlb_mmid = mi ? tlb->MMID : (uint32_t) tlb->ASID; + if (!tlb->G && tlb_mmid == MMID) { tlb->EHINV = 1; } } @@ -2354,11 +688,16 @@ void r4k_helper_tlbinvf(CPUMIPSState *env) void r4k_helper_tlbwi(CPUMIPSState *env) { + bool mi = !!((env->CP0_Config5 >> CP0C5_MI) & 1); + target_ulong VPN; + uint16_t ASID = env->CP0_EntryHi & env->CP0_EntryHi_ASID_mask; + uint32_t MMID = env->CP0_MemoryMapID; + uint32_t tlb_mmid; + bool EHINV, G, V0, D0, V1, D1, XI0, XI1, RI0, RI1; r4k_tlb_t *tlb; int idx; - target_ulong VPN; - uint16_t ASID; - bool EHINV, G, V0, D0, V1, D1, XI0, XI1, RI0, RI1; + + MMID = mi ? MMID : (uint32_t) ASID; idx = (env->CP0_Index & ~0x80000000) % env->tlb->nb_tlb; tlb = &env->tlb->mmu.r4k.tlb[idx]; @@ -2366,7 +705,6 @@ void r4k_helper_tlbwi(CPUMIPSState *env) #if defined(TARGET_MIPS64) VPN &= env->SEGMask; #endif - ASID = env->CP0_EntryHi & env->CP0_EntryHi_ASID_mask; EHINV = (env->CP0_EntryHi & (1 << CP0EnHi_EHINV)) != 0; G = env->CP0_EntryLo0 & env->CP0_EntryLo1 & 1; V0 = (env->CP0_EntryLo0 & 2) != 0; @@ -2378,11 +716,12 @@ void r4k_helper_tlbwi(CPUMIPSState *env) XI1 = (env->CP0_EntryLo1 >> CP0EnLo_XI) &1; RI1 = (env->CP0_EntryLo1 >> CP0EnLo_RI) &1; + tlb_mmid = mi ? tlb->MMID : (uint32_t) tlb->ASID; /* * Discard cached TLB entries, unless tlbwi is just upgrading access * permissions on the current entry. */ - if (tlb->VPN != VPN || tlb->ASID != ASID || tlb->G != G || + if (tlb->VPN != VPN || tlb_mmid != MMID || tlb->G != G || (!tlb->EHINV && EHINV) || (tlb->V0 && !V0) || (tlb->D0 && !D0) || (!tlb->XI0 && XI0) || (!tlb->RI0 && RI0) || @@ -2405,14 +744,17 @@ void r4k_helper_tlbwr(CPUMIPSState *env) void r4k_helper_tlbp(CPUMIPSState *env) { + bool mi = !!((env->CP0_Config5 >> CP0C5_MI) & 1); r4k_tlb_t *tlb; target_ulong mask; target_ulong tag; target_ulong VPN; - uint16_t ASID; + uint16_t ASID = env->CP0_EntryHi & env->CP0_EntryHi_ASID_mask; + uint32_t MMID = env->CP0_MemoryMapID; + uint32_t tlb_mmid; int i; - ASID = env->CP0_EntryHi & env->CP0_EntryHi_ASID_mask; + MMID = mi ? MMID : (uint32_t) ASID; for (i = 0; i < env->tlb->nb_tlb; i++) { tlb = &env->tlb->mmu.r4k.tlb[i]; /* 1k pages are not supported. */ @@ -2422,8 +764,9 @@ void r4k_helper_tlbp(CPUMIPSState *env) #if defined(TARGET_MIPS64) tag &= env->SEGMask; #endif - /* Check ASID, virtual page number & size */ - if ((tlb->G == 1 || tlb->ASID == ASID) && VPN == tag && !tlb->EHINV) { + tlb_mmid = mi ? tlb->MMID : (uint32_t) tlb->ASID; + /* Check ASID/MMID, virtual page number & size */ + if ((tlb->G == 1 || tlb_mmid == MMID) && VPN == tag && !tlb->EHINV) { /* TLB match */ env->CP0_Index = i; break; @@ -2440,8 +783,9 @@ void r4k_helper_tlbp(CPUMIPSState *env) #if defined(TARGET_MIPS64) tag &= env->SEGMask; #endif - /* Check ASID, virtual page number & size */ - if ((tlb->G == 1 || tlb->ASID == ASID) && VPN == tag) { + tlb_mmid = mi ? tlb->MMID : (uint32_t) tlb->ASID; + /* Check ASID/MMID, virtual page number & size */ + if ((tlb->G == 1 || tlb_mmid == MMID) && VPN == tag) { r4k_mips_tlb_flush_extra(env, i); break; } @@ -2463,16 +807,20 @@ static inline uint64_t get_entrylo_pfn_from_tlb(uint64_t tlb_pfn) void r4k_helper_tlbr(CPUMIPSState *env) { + bool mi = !!((env->CP0_Config5 >> CP0C5_MI) & 1); + uint16_t ASID = env->CP0_EntryHi & env->CP0_EntryHi_ASID_mask; + uint32_t MMID = env->CP0_MemoryMapID; + uint32_t tlb_mmid; r4k_tlb_t *tlb; - uint16_t ASID; int idx; - ASID = env->CP0_EntryHi & env->CP0_EntryHi_ASID_mask; + MMID = mi ? MMID : (uint32_t) ASID; idx = (env->CP0_Index & ~0x80000000) % env->tlb->nb_tlb; tlb = &env->tlb->mmu.r4k.tlb[idx]; - /* If this will change the current ASID, flush qemu's TLB. */ - if (ASID != tlb->ASID) { + tlb_mmid = mi ? tlb->MMID : (uint32_t) tlb->ASID; + /* If this will change the current ASID/MMID, flush qemu's TLB. */ + if (MMID != tlb_mmid) { cpu_mips_tlb_flush(env); } @@ -2484,7 +832,8 @@ void r4k_helper_tlbr(CPUMIPSState *env) env->CP0_EntryLo0 = 0; env->CP0_EntryLo1 = 0; } else { - env->CP0_EntryHi = tlb->VPN | tlb->ASID; + env->CP0_EntryHi = mi ? tlb->VPN : tlb->VPN | tlb->ASID; + env->CP0_MemoryMapID = tlb->MMID; env->CP0_PageMask = tlb->PageMask; env->CP0_EntryLo0 = tlb->G | (tlb->V0 << 1) | (tlb->D0 << 2) | ((uint64_t)tlb->RI0 << CP0EnLo_RI) | @@ -2527,6 +876,63 @@ void helper_tlbinvf(CPUMIPSState *env) env->tlb->helper_tlbinvf(env); } +static void global_invalidate_tlb(CPUMIPSState *env, + uint32_t invMsgVPN2, + uint8_t invMsgR, + uint32_t invMsgMMid, + bool invAll, + bool invVAMMid, + bool invMMid, + bool invVA) +{ + + int idx; + r4k_tlb_t *tlb; + bool VAMatch; + bool MMidMatch; + + for (idx = 0; idx < env->tlb->nb_tlb; idx++) { + tlb = &env->tlb->mmu.r4k.tlb[idx]; + VAMatch = + (((tlb->VPN & ~tlb->PageMask) == (invMsgVPN2 & ~tlb->PageMask)) +#ifdef TARGET_MIPS64 + && + (extract64(env->CP0_EntryHi, 62, 2) == invMsgR) +#endif + ); + MMidMatch = tlb->MMID == invMsgMMid; + if ((invAll && (idx > env->CP0_Wired)) || + (VAMatch && invVAMMid && (tlb->G || MMidMatch)) || + (VAMatch && invVA) || + (MMidMatch && !(tlb->G) && invMMid)) { + tlb->EHINV = 1; + } + } + cpu_mips_tlb_flush(env); +} + +void helper_ginvt(CPUMIPSState *env, target_ulong arg, uint32_t type) +{ + bool invAll = type == 0; + bool invVA = type == 1; + bool invMMid = type == 2; + bool invVAMMid = type == 3; + uint32_t invMsgVPN2 = arg & (TARGET_PAGE_MASK << 1); + uint8_t invMsgR = 0; + uint32_t invMsgMMid = env->CP0_MemoryMapID; + CPUState *other_cs = first_cpu; + +#ifdef TARGET_MIPS64 + invMsgR = extract64(arg, 62, 2); +#endif + + CPU_FOREACH(other_cs) { + MIPSCPU *other_cpu = MIPS_CPU(other_cs); + global_invalidate_tlb(&other_cpu->env, invMsgVPN2, invMsgR, invMsgMMid, + invAll, invVAMMid, invMMid, invVA); + } +} + /* Specials */ target_ulong helper_di(CPUMIPSState *env) { @@ -2771,1882 +1177,6 @@ void mips_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr, } #endif /* !CONFIG_USER_ONLY */ -/* Complex FPU operations which may need stack space. */ - -#define FLOAT_TWO32 make_float32(1 << 30) -#define FLOAT_TWO64 make_float64(1ULL << 62) - -#define FP_TO_INT32_OVERFLOW 0x7fffffff -#define FP_TO_INT64_OVERFLOW 0x7fffffffffffffffULL - -/* convert MIPS rounding mode in FCR31 to IEEE library */ -unsigned int ieee_rm[] = { - float_round_nearest_even, - float_round_to_zero, - float_round_up, - float_round_down -}; - -target_ulong helper_cfc1(CPUMIPSState *env, uint32_t reg) -{ - target_ulong arg1 = 0; - - switch (reg) { - case 0: - arg1 = (int32_t)env->active_fpu.fcr0; - break; - case 1: - /* UFR Support - Read Status FR */ - if (env->active_fpu.fcr0 & (1 << FCR0_UFRP)) { - if (env->CP0_Config5 & (1 << CP0C5_UFR)) { - arg1 = (int32_t) - ((env->CP0_Status & (1 << CP0St_FR)) >> CP0St_FR); - } else { - do_raise_exception(env, EXCP_RI, GETPC()); - } - } - break; - case 5: - /* FRE Support - read Config5.FRE bit */ - if (env->active_fpu.fcr0 & (1 << FCR0_FREP)) { - if (env->CP0_Config5 & (1 << CP0C5_UFE)) { - arg1 = (env->CP0_Config5 >> CP0C5_FRE) & 1; - } else { - helper_raise_exception(env, EXCP_RI); - } - } - break; - case 25: - arg1 = ((env->active_fpu.fcr31 >> 24) & 0xfe) | - ((env->active_fpu.fcr31 >> 23) & 0x1); - break; - case 26: - arg1 = env->active_fpu.fcr31 & 0x0003f07c; - break; - case 28: - arg1 = (env->active_fpu.fcr31 & 0x00000f83) | - ((env->active_fpu.fcr31 >> 22) & 0x4); - break; - default: - arg1 = (int32_t)env->active_fpu.fcr31; - break; - } - - return arg1; -} - -void helper_ctc1(CPUMIPSState *env, target_ulong arg1, uint32_t fs, uint32_t rt) -{ - switch (fs) { - case 1: - /* UFR Alias - Reset Status FR */ - if (!((env->active_fpu.fcr0 & (1 << FCR0_UFRP)) && (rt == 0))) { - return; - } - if (env->CP0_Config5 & (1 << CP0C5_UFR)) { - env->CP0_Status &= ~(1 << CP0St_FR); - compute_hflags(env); - } else { - do_raise_exception(env, EXCP_RI, GETPC()); - } - break; - case 4: - /* UNFR Alias - Set Status FR */ - if (!((env->active_fpu.fcr0 & (1 << FCR0_UFRP)) && (rt == 0))) { - return; - } - if (env->CP0_Config5 & (1 << CP0C5_UFR)) { - env->CP0_Status |= (1 << CP0St_FR); - compute_hflags(env); - } else { - do_raise_exception(env, EXCP_RI, GETPC()); - } - break; - case 5: - /* FRE Support - clear Config5.FRE bit */ - if (!((env->active_fpu.fcr0 & (1 << FCR0_FREP)) && (rt == 0))) { - return; - } - if (env->CP0_Config5 & (1 << CP0C5_UFE)) { - env->CP0_Config5 &= ~(1 << CP0C5_FRE); - compute_hflags(env); - } else { - helper_raise_exception(env, EXCP_RI); - } - break; - case 6: - /* FRE Support - set Config5.FRE bit */ - if (!((env->active_fpu.fcr0 & (1 << FCR0_FREP)) && (rt == 0))) { - return; - } - if (env->CP0_Config5 & (1 << CP0C5_UFE)) { - env->CP0_Config5 |= (1 << CP0C5_FRE); - compute_hflags(env); - } else { - helper_raise_exception(env, EXCP_RI); - } - break; - case 25: - if ((env->insn_flags & ISA_MIPS32R6) || (arg1 & 0xffffff00)) { - return; - } - env->active_fpu.fcr31 = (env->active_fpu.fcr31 & 0x017fffff) | - ((arg1 & 0xfe) << 24) | - ((arg1 & 0x1) << 23); - break; - case 26: - if (arg1 & 0x007c0000) { - return; - } - env->active_fpu.fcr31 = (env->active_fpu.fcr31 & 0xfffc0f83) | - (arg1 & 0x0003f07c); - break; - case 28: - if (arg1 & 0x007c0000) { - return; - } - env->active_fpu.fcr31 = (env->active_fpu.fcr31 & 0xfefff07c) | - (arg1 & 0x00000f83) | - ((arg1 & 0x4) << 22); - break; - case 31: - env->active_fpu.fcr31 = (arg1 & env->active_fpu.fcr31_rw_bitmask) | - (env->active_fpu.fcr31 & ~(env->active_fpu.fcr31_rw_bitmask)); - break; - default: - if (env->insn_flags & ISA_MIPS32R6) { - do_raise_exception(env, EXCP_RI, GETPC()); - } - return; - } - restore_fp_status(env); - set_float_exception_flags(0, &env->active_fpu.fp_status); - if ((GET_FP_ENABLE(env->active_fpu.fcr31) | 0x20) & - GET_FP_CAUSE(env->active_fpu.fcr31)) { - do_raise_exception(env, EXCP_FPE, GETPC()); - } -} - -int ieee_ex_to_mips(int xcpt) -{ - int ret = 0; - if (xcpt) { - if (xcpt & float_flag_invalid) { - ret |= FP_INVALID; - } - if (xcpt & float_flag_overflow) { - ret |= FP_OVERFLOW; - } - if (xcpt & float_flag_underflow) { - ret |= FP_UNDERFLOW; - } - if (xcpt & float_flag_divbyzero) { - ret |= FP_DIV0; - } - if (xcpt & float_flag_inexact) { - ret |= FP_INEXACT; - } - } - return ret; -} - -static inline void update_fcr31(CPUMIPSState *env, uintptr_t pc) -{ - int tmp = ieee_ex_to_mips(get_float_exception_flags( - &env->active_fpu.fp_status)); - - SET_FP_CAUSE(env->active_fpu.fcr31, tmp); - - if (tmp) { - set_float_exception_flags(0, &env->active_fpu.fp_status); - - if (GET_FP_ENABLE(env->active_fpu.fcr31) & tmp) { - do_raise_exception(env, EXCP_FPE, pc); - } else { - UPDATE_FP_FLAGS(env->active_fpu.fcr31, tmp); - } - } -} - -/* - * Float support. - * Single precition routines have a "s" suffix, double precision a - * "d" suffix, 32bit integer "w", 64bit integer "l", paired single "ps", - * paired single lower "pl", paired single upper "pu". - */ - -/* unary operations, modifying fp status */ -uint64_t helper_float_sqrt_d(CPUMIPSState *env, uint64_t fdt0) -{ - fdt0 = float64_sqrt(fdt0, &env->active_fpu.fp_status); - update_fcr31(env, GETPC()); - return fdt0; -} - -uint32_t helper_float_sqrt_s(CPUMIPSState *env, uint32_t fst0) -{ - fst0 = float32_sqrt(fst0, &env->active_fpu.fp_status); - update_fcr31(env, GETPC()); - return fst0; -} - -uint64_t helper_float_cvtd_s(CPUMIPSState *env, uint32_t fst0) -{ - uint64_t fdt2; - - fdt2 = float32_to_float64(fst0, &env->active_fpu.fp_status); - update_fcr31(env, GETPC()); - return fdt2; -} - -uint64_t helper_float_cvtd_w(CPUMIPSState *env, uint32_t wt0) -{ - uint64_t fdt2; - - fdt2 = int32_to_float64(wt0, &env->active_fpu.fp_status); - update_fcr31(env, GETPC()); - return fdt2; -} - -uint64_t helper_float_cvtd_l(CPUMIPSState *env, uint64_t dt0) -{ - uint64_t fdt2; - - fdt2 = int64_to_float64(dt0, &env->active_fpu.fp_status); - update_fcr31(env, GETPC()); - return fdt2; -} - -uint64_t helper_float_cvt_l_d(CPUMIPSState *env, uint64_t fdt0) -{ - uint64_t dt2; - - dt2 = float64_to_int64(fdt0, &env->active_fpu.fp_status); - if (get_float_exception_flags(&env->active_fpu.fp_status) - & (float_flag_invalid | float_flag_overflow)) { - dt2 = FP_TO_INT64_OVERFLOW; - } - update_fcr31(env, GETPC()); - return dt2; -} - -uint64_t helper_float_cvt_l_s(CPUMIPSState *env, uint32_t fst0) -{ - uint64_t dt2; - - dt2 = float32_to_int64(fst0, &env->active_fpu.fp_status); - if (get_float_exception_flags(&env->active_fpu.fp_status) - & (float_flag_invalid | float_flag_overflow)) { - dt2 = FP_TO_INT64_OVERFLOW; - } - update_fcr31(env, GETPC()); - return dt2; -} - -uint64_t helper_float_cvtps_pw(CPUMIPSState *env, uint64_t dt0) -{ - uint32_t fst2; - uint32_t fsth2; - - fst2 = int32_to_float32(dt0 & 0XFFFFFFFF, &env->active_fpu.fp_status); - fsth2 = int32_to_float32(dt0 >> 32, &env->active_fpu.fp_status); - update_fcr31(env, GETPC()); - return ((uint64_t)fsth2 << 32) | fst2; -} - -uint64_t helper_float_cvtpw_ps(CPUMIPSState *env, uint64_t fdt0) -{ - uint32_t wt2; - uint32_t wth2; - int excp, excph; - - wt2 = float32_to_int32(fdt0 & 0XFFFFFFFF, &env->active_fpu.fp_status); - excp = get_float_exception_flags(&env->active_fpu.fp_status); - if (excp & (float_flag_overflow | float_flag_invalid)) { - wt2 = FP_TO_INT32_OVERFLOW; - } - - set_float_exception_flags(0, &env->active_fpu.fp_status); - wth2 = float32_to_int32(fdt0 >> 32, &env->active_fpu.fp_status); - excph = get_float_exception_flags(&env->active_fpu.fp_status); - if (excph & (float_flag_overflow | float_flag_invalid)) { - wth2 = FP_TO_INT32_OVERFLOW; - } - - set_float_exception_flags(excp | excph, &env->active_fpu.fp_status); - update_fcr31(env, GETPC()); - - return ((uint64_t)wth2 << 32) | wt2; -} - -uint32_t helper_float_cvts_d(CPUMIPSState *env, uint64_t fdt0) -{ - uint32_t fst2; - - fst2 = float64_to_float32(fdt0, &env->active_fpu.fp_status); - update_fcr31(env, GETPC()); - return fst2; -} - -uint32_t helper_float_cvts_w(CPUMIPSState *env, uint32_t wt0) -{ - uint32_t fst2; - - fst2 = int32_to_float32(wt0, &env->active_fpu.fp_status); - update_fcr31(env, GETPC()); - return fst2; -} - -uint32_t helper_float_cvts_l(CPUMIPSState *env, uint64_t dt0) -{ - uint32_t fst2; - - fst2 = int64_to_float32(dt0, &env->active_fpu.fp_status); - update_fcr31(env, GETPC()); - return fst2; -} - -uint32_t helper_float_cvts_pl(CPUMIPSState *env, uint32_t wt0) -{ - uint32_t wt2; - - wt2 = wt0; - update_fcr31(env, GETPC()); - return wt2; -} - -uint32_t helper_float_cvts_pu(CPUMIPSState *env, uint32_t wth0) -{ - uint32_t wt2; - - wt2 = wth0; - update_fcr31(env, GETPC()); - return wt2; -} - -uint32_t helper_float_cvt_w_s(CPUMIPSState *env, uint32_t fst0) -{ - uint32_t wt2; - - wt2 = float32_to_int32(fst0, &env->active_fpu.fp_status); - if (get_float_exception_flags(&env->active_fpu.fp_status) - & (float_flag_invalid | float_flag_overflow)) { - wt2 = FP_TO_INT32_OVERFLOW; - } - update_fcr31(env, GETPC()); - return wt2; -} - -uint32_t helper_float_cvt_w_d(CPUMIPSState *env, uint64_t fdt0) -{ - uint32_t wt2; - - wt2 = float64_to_int32(fdt0, &env->active_fpu.fp_status); - if (get_float_exception_flags(&env->active_fpu.fp_status) - & (float_flag_invalid | float_flag_overflow)) { - wt2 = FP_TO_INT32_OVERFLOW; - } - update_fcr31(env, GETPC()); - return wt2; -} - -uint64_t helper_float_round_l_d(CPUMIPSState *env, uint64_t fdt0) -{ - uint64_t dt2; - - set_float_rounding_mode(float_round_nearest_even, - &env->active_fpu.fp_status); - dt2 = float64_to_int64(fdt0, &env->active_fpu.fp_status); - restore_rounding_mode(env); - if (get_float_exception_flags(&env->active_fpu.fp_status) - & (float_flag_invalid | float_flag_overflow)) { - dt2 = FP_TO_INT64_OVERFLOW; - } - update_fcr31(env, GETPC()); - return dt2; -} - -uint64_t helper_float_round_l_s(CPUMIPSState *env, uint32_t fst0) -{ - uint64_t dt2; - - set_float_rounding_mode(float_round_nearest_even, - &env->active_fpu.fp_status); - dt2 = float32_to_int64(fst0, &env->active_fpu.fp_status); - restore_rounding_mode(env); - if (get_float_exception_flags(&env->active_fpu.fp_status) - & (float_flag_invalid | float_flag_overflow)) { - dt2 = FP_TO_INT64_OVERFLOW; - } - update_fcr31(env, GETPC()); - return dt2; -} - -uint32_t helper_float_round_w_d(CPUMIPSState *env, uint64_t fdt0) -{ - uint32_t wt2; - - set_float_rounding_mode(float_round_nearest_even, - &env->active_fpu.fp_status); - wt2 = float64_to_int32(fdt0, &env->active_fpu.fp_status); - restore_rounding_mode(env); - if (get_float_exception_flags(&env->active_fpu.fp_status) - & (float_flag_invalid | float_flag_overflow)) { - wt2 = FP_TO_INT32_OVERFLOW; - } - update_fcr31(env, GETPC()); - return wt2; -} - -uint32_t helper_float_round_w_s(CPUMIPSState *env, uint32_t fst0) -{ - uint32_t wt2; - - set_float_rounding_mode(float_round_nearest_even, - &env->active_fpu.fp_status); - wt2 = float32_to_int32(fst0, &env->active_fpu.fp_status); - restore_rounding_mode(env); - if (get_float_exception_flags(&env->active_fpu.fp_status) - & (float_flag_invalid | float_flag_overflow)) { - wt2 = FP_TO_INT32_OVERFLOW; - } - update_fcr31(env, GETPC()); - return wt2; -} - -uint64_t helper_float_trunc_l_d(CPUMIPSState *env, uint64_t fdt0) -{ - uint64_t dt2; - - dt2 = float64_to_int64_round_to_zero(fdt0, - &env->active_fpu.fp_status); - if (get_float_exception_flags(&env->active_fpu.fp_status) - & (float_flag_invalid | float_flag_overflow)) { - dt2 = FP_TO_INT64_OVERFLOW; - } - update_fcr31(env, GETPC()); - return dt2; -} - -uint64_t helper_float_trunc_l_s(CPUMIPSState *env, uint32_t fst0) -{ - uint64_t dt2; - - dt2 = float32_to_int64_round_to_zero(fst0, &env->active_fpu.fp_status); - if (get_float_exception_flags(&env->active_fpu.fp_status) - & (float_flag_invalid | float_flag_overflow)) { - dt2 = FP_TO_INT64_OVERFLOW; - } - update_fcr31(env, GETPC()); - return dt2; -} - -uint32_t helper_float_trunc_w_d(CPUMIPSState *env, uint64_t fdt0) -{ - uint32_t wt2; - - wt2 = float64_to_int32_round_to_zero(fdt0, &env->active_fpu.fp_status); - if (get_float_exception_flags(&env->active_fpu.fp_status) - & (float_flag_invalid | float_flag_overflow)) { - wt2 = FP_TO_INT32_OVERFLOW; - } - update_fcr31(env, GETPC()); - return wt2; -} - -uint32_t helper_float_trunc_w_s(CPUMIPSState *env, uint32_t fst0) -{ - uint32_t wt2; - - wt2 = float32_to_int32_round_to_zero(fst0, &env->active_fpu.fp_status); - if (get_float_exception_flags(&env->active_fpu.fp_status) - & (float_flag_invalid | float_flag_overflow)) { - wt2 = FP_TO_INT32_OVERFLOW; - } - update_fcr31(env, GETPC()); - return wt2; -} - -uint64_t helper_float_ceil_l_d(CPUMIPSState *env, uint64_t fdt0) -{ - uint64_t dt2; - - set_float_rounding_mode(float_round_up, &env->active_fpu.fp_status); - dt2 = float64_to_int64(fdt0, &env->active_fpu.fp_status); - restore_rounding_mode(env); - if (get_float_exception_flags(&env->active_fpu.fp_status) - & (float_flag_invalid | float_flag_overflow)) { - dt2 = FP_TO_INT64_OVERFLOW; - } - update_fcr31(env, GETPC()); - return dt2; -} - -uint64_t helper_float_ceil_l_s(CPUMIPSState *env, uint32_t fst0) -{ - uint64_t dt2; - - set_float_rounding_mode(float_round_up, &env->active_fpu.fp_status); - dt2 = float32_to_int64(fst0, &env->active_fpu.fp_status); - restore_rounding_mode(env); - if (get_float_exception_flags(&env->active_fpu.fp_status) - & (float_flag_invalid | float_flag_overflow)) { - dt2 = FP_TO_INT64_OVERFLOW; - } - update_fcr31(env, GETPC()); - return dt2; -} - -uint32_t helper_float_ceil_w_d(CPUMIPSState *env, uint64_t fdt0) -{ - uint32_t wt2; - - set_float_rounding_mode(float_round_up, &env->active_fpu.fp_status); - wt2 = float64_to_int32(fdt0, &env->active_fpu.fp_status); - restore_rounding_mode(env); - if (get_float_exception_flags(&env->active_fpu.fp_status) - & (float_flag_invalid | float_flag_overflow)) { - wt2 = FP_TO_INT32_OVERFLOW; - } - update_fcr31(env, GETPC()); - return wt2; -} - -uint32_t helper_float_ceil_w_s(CPUMIPSState *env, uint32_t fst0) -{ - uint32_t wt2; - - set_float_rounding_mode(float_round_up, &env->active_fpu.fp_status); - wt2 = float32_to_int32(fst0, &env->active_fpu.fp_status); - restore_rounding_mode(env); - if (get_float_exception_flags(&env->active_fpu.fp_status) - & (float_flag_invalid | float_flag_overflow)) { - wt2 = FP_TO_INT32_OVERFLOW; - } - update_fcr31(env, GETPC()); - return wt2; -} - -uint64_t helper_float_floor_l_d(CPUMIPSState *env, uint64_t fdt0) -{ - uint64_t dt2; - - set_float_rounding_mode(float_round_down, &env->active_fpu.fp_status); - dt2 = float64_to_int64(fdt0, &env->active_fpu.fp_status); - restore_rounding_mode(env); - if (get_float_exception_flags(&env->active_fpu.fp_status) - & (float_flag_invalid | float_flag_overflow)) { - dt2 = FP_TO_INT64_OVERFLOW; - } - update_fcr31(env, GETPC()); - return dt2; -} - -uint64_t helper_float_floor_l_s(CPUMIPSState *env, uint32_t fst0) -{ - uint64_t dt2; - - set_float_rounding_mode(float_round_down, &env->active_fpu.fp_status); - dt2 = float32_to_int64(fst0, &env->active_fpu.fp_status); - restore_rounding_mode(env); - if (get_float_exception_flags(&env->active_fpu.fp_status) - & (float_flag_invalid | float_flag_overflow)) { - dt2 = FP_TO_INT64_OVERFLOW; - } - update_fcr31(env, GETPC()); - return dt2; -} - -uint32_t helper_float_floor_w_d(CPUMIPSState *env, uint64_t fdt0) -{ - uint32_t wt2; - - set_float_rounding_mode(float_round_down, &env->active_fpu.fp_status); - wt2 = float64_to_int32(fdt0, &env->active_fpu.fp_status); - restore_rounding_mode(env); - if (get_float_exception_flags(&env->active_fpu.fp_status) - & (float_flag_invalid | float_flag_overflow)) { - wt2 = FP_TO_INT32_OVERFLOW; - } - update_fcr31(env, GETPC()); - return wt2; -} - -uint32_t helper_float_floor_w_s(CPUMIPSState *env, uint32_t fst0) -{ - uint32_t wt2; - - set_float_rounding_mode(float_round_down, &env->active_fpu.fp_status); - wt2 = float32_to_int32(fst0, &env->active_fpu.fp_status); - restore_rounding_mode(env); - if (get_float_exception_flags(&env->active_fpu.fp_status) - & (float_flag_invalid | float_flag_overflow)) { - wt2 = FP_TO_INT32_OVERFLOW; - } - update_fcr31(env, GETPC()); - return wt2; -} - -uint64_t helper_float_cvt_2008_l_d(CPUMIPSState *env, uint64_t fdt0) -{ - uint64_t dt2; - - dt2 = float64_to_int64(fdt0, &env->active_fpu.fp_status); - if (get_float_exception_flags(&env->active_fpu.fp_status) - & float_flag_invalid) { - if (float64_is_any_nan(fdt0)) { - dt2 = 0; - } - } - update_fcr31(env, GETPC()); - return dt2; -} - -uint64_t helper_float_cvt_2008_l_s(CPUMIPSState *env, uint32_t fst0) -{ - uint64_t dt2; - - dt2 = float32_to_int64(fst0, &env->active_fpu.fp_status); - if (get_float_exception_flags(&env->active_fpu.fp_status) - & float_flag_invalid) { - if (float32_is_any_nan(fst0)) { - dt2 = 0; - } - } - update_fcr31(env, GETPC()); - return dt2; -} - -uint32_t helper_float_cvt_2008_w_d(CPUMIPSState *env, uint64_t fdt0) -{ - uint32_t wt2; - - wt2 = float64_to_int32(fdt0, &env->active_fpu.fp_status); - if (get_float_exception_flags(&env->active_fpu.fp_status) - & float_flag_invalid) { - if (float64_is_any_nan(fdt0)) { - wt2 = 0; - } - } - update_fcr31(env, GETPC()); - return wt2; -} - -uint32_t helper_float_cvt_2008_w_s(CPUMIPSState *env, uint32_t fst0) -{ - uint32_t wt2; - - wt2 = float32_to_int32(fst0, &env->active_fpu.fp_status); - if (get_float_exception_flags(&env->active_fpu.fp_status) - & float_flag_invalid) { - if (float32_is_any_nan(fst0)) { - wt2 = 0; - } - } - update_fcr31(env, GETPC()); - return wt2; -} - -uint64_t helper_float_round_2008_l_d(CPUMIPSState *env, uint64_t fdt0) -{ - uint64_t dt2; - - set_float_rounding_mode(float_round_nearest_even, - &env->active_fpu.fp_status); - dt2 = float64_to_int64(fdt0, &env->active_fpu.fp_status); - restore_rounding_mode(env); - if (get_float_exception_flags(&env->active_fpu.fp_status) - & float_flag_invalid) { - if (float64_is_any_nan(fdt0)) { - dt2 = 0; - } - } - update_fcr31(env, GETPC()); - return dt2; -} - -uint64_t helper_float_round_2008_l_s(CPUMIPSState *env, uint32_t fst0) -{ - uint64_t dt2; - - set_float_rounding_mode(float_round_nearest_even, - &env->active_fpu.fp_status); - dt2 = float32_to_int64(fst0, &env->active_fpu.fp_status); - restore_rounding_mode(env); - if (get_float_exception_flags(&env->active_fpu.fp_status) - & float_flag_invalid) { - if (float32_is_any_nan(fst0)) { - dt2 = 0; - } - } - update_fcr31(env, GETPC()); - return dt2; -} - -uint32_t helper_float_round_2008_w_d(CPUMIPSState *env, uint64_t fdt0) -{ - uint32_t wt2; - - set_float_rounding_mode(float_round_nearest_even, - &env->active_fpu.fp_status); - wt2 = float64_to_int32(fdt0, &env->active_fpu.fp_status); - restore_rounding_mode(env); - if (get_float_exception_flags(&env->active_fpu.fp_status) - & float_flag_invalid) { - if (float64_is_any_nan(fdt0)) { - wt2 = 0; - } - } - update_fcr31(env, GETPC()); - return wt2; -} - -uint32_t helper_float_round_2008_w_s(CPUMIPSState *env, uint32_t fst0) -{ - uint32_t wt2; - - set_float_rounding_mode(float_round_nearest_even, - &env->active_fpu.fp_status); - wt2 = float32_to_int32(fst0, &env->active_fpu.fp_status); - restore_rounding_mode(env); - if (get_float_exception_flags(&env->active_fpu.fp_status) - & float_flag_invalid) { - if (float32_is_any_nan(fst0)) { - wt2 = 0; - } - } - update_fcr31(env, GETPC()); - return wt2; -} - -uint64_t helper_float_trunc_2008_l_d(CPUMIPSState *env, uint64_t fdt0) -{ - uint64_t dt2; - - dt2 = float64_to_int64_round_to_zero(fdt0, &env->active_fpu.fp_status); - if (get_float_exception_flags(&env->active_fpu.fp_status) - & float_flag_invalid) { - if (float64_is_any_nan(fdt0)) { - dt2 = 0; - } - } - update_fcr31(env, GETPC()); - return dt2; -} - -uint64_t helper_float_trunc_2008_l_s(CPUMIPSState *env, uint32_t fst0) -{ - uint64_t dt2; - - dt2 = float32_to_int64_round_to_zero(fst0, &env->active_fpu.fp_status); - if (get_float_exception_flags(&env->active_fpu.fp_status) - & float_flag_invalid) { - if (float32_is_any_nan(fst0)) { - dt2 = 0; - } - } - update_fcr31(env, GETPC()); - return dt2; -} - -uint32_t helper_float_trunc_2008_w_d(CPUMIPSState *env, uint64_t fdt0) -{ - uint32_t wt2; - - wt2 = float64_to_int32_round_to_zero(fdt0, &env->active_fpu.fp_status); - if (get_float_exception_flags(&env->active_fpu.fp_status) - & float_flag_invalid) { - if (float64_is_any_nan(fdt0)) { - wt2 = 0; - } - } - update_fcr31(env, GETPC()); - return wt2; -} - -uint32_t helper_float_trunc_2008_w_s(CPUMIPSState *env, uint32_t fst0) -{ - uint32_t wt2; - - wt2 = float32_to_int32_round_to_zero(fst0, &env->active_fpu.fp_status); - if (get_float_exception_flags(&env->active_fpu.fp_status) - & float_flag_invalid) { - if (float32_is_any_nan(fst0)) { - wt2 = 0; - } - } - update_fcr31(env, GETPC()); - return wt2; -} - -uint64_t helper_float_ceil_2008_l_d(CPUMIPSState *env, uint64_t fdt0) -{ - uint64_t dt2; - - set_float_rounding_mode(float_round_up, &env->active_fpu.fp_status); - dt2 = float64_to_int64(fdt0, &env->active_fpu.fp_status); - restore_rounding_mode(env); - if (get_float_exception_flags(&env->active_fpu.fp_status) - & float_flag_invalid) { - if (float64_is_any_nan(fdt0)) { - dt2 = 0; - } - } - update_fcr31(env, GETPC()); - return dt2; -} - -uint64_t helper_float_ceil_2008_l_s(CPUMIPSState *env, uint32_t fst0) -{ - uint64_t dt2; - - set_float_rounding_mode(float_round_up, &env->active_fpu.fp_status); - dt2 = float32_to_int64(fst0, &env->active_fpu.fp_status); - restore_rounding_mode(env); - if (get_float_exception_flags(&env->active_fpu.fp_status) - & float_flag_invalid) { - if (float32_is_any_nan(fst0)) { - dt2 = 0; - } - } - update_fcr31(env, GETPC()); - return dt2; -} - -uint32_t helper_float_ceil_2008_w_d(CPUMIPSState *env, uint64_t fdt0) -{ - uint32_t wt2; - - set_float_rounding_mode(float_round_up, &env->active_fpu.fp_status); - wt2 = float64_to_int32(fdt0, &env->active_fpu.fp_status); - restore_rounding_mode(env); - if (get_float_exception_flags(&env->active_fpu.fp_status) - & float_flag_invalid) { - if (float64_is_any_nan(fdt0)) { - wt2 = 0; - } - } - update_fcr31(env, GETPC()); - return wt2; -} - -uint32_t helper_float_ceil_2008_w_s(CPUMIPSState *env, uint32_t fst0) -{ - uint32_t wt2; - - set_float_rounding_mode(float_round_up, &env->active_fpu.fp_status); - wt2 = float32_to_int32(fst0, &env->active_fpu.fp_status); - restore_rounding_mode(env); - if (get_float_exception_flags(&env->active_fpu.fp_status) - & float_flag_invalid) { - if (float32_is_any_nan(fst0)) { - wt2 = 0; - } - } - update_fcr31(env, GETPC()); - return wt2; -} - -uint64_t helper_float_floor_2008_l_d(CPUMIPSState *env, uint64_t fdt0) -{ - uint64_t dt2; - - set_float_rounding_mode(float_round_down, &env->active_fpu.fp_status); - dt2 = float64_to_int64(fdt0, &env->active_fpu.fp_status); - restore_rounding_mode(env); - if (get_float_exception_flags(&env->active_fpu.fp_status) - & float_flag_invalid) { - if (float64_is_any_nan(fdt0)) { - dt2 = 0; - } - } - update_fcr31(env, GETPC()); - return dt2; -} - -uint64_t helper_float_floor_2008_l_s(CPUMIPSState *env, uint32_t fst0) -{ - uint64_t dt2; - - set_float_rounding_mode(float_round_down, &env->active_fpu.fp_status); - dt2 = float32_to_int64(fst0, &env->active_fpu.fp_status); - restore_rounding_mode(env); - if (get_float_exception_flags(&env->active_fpu.fp_status) - & float_flag_invalid) { - if (float32_is_any_nan(fst0)) { - dt2 = 0; - } - } - update_fcr31(env, GETPC()); - return dt2; -} - -uint32_t helper_float_floor_2008_w_d(CPUMIPSState *env, uint64_t fdt0) -{ - uint32_t wt2; - - set_float_rounding_mode(float_round_down, &env->active_fpu.fp_status); - wt2 = float64_to_int32(fdt0, &env->active_fpu.fp_status); - restore_rounding_mode(env); - if (get_float_exception_flags(&env->active_fpu.fp_status) - & float_flag_invalid) { - if (float64_is_any_nan(fdt0)) { - wt2 = 0; - } - } - update_fcr31(env, GETPC()); - return wt2; -} - -uint32_t helper_float_floor_2008_w_s(CPUMIPSState *env, uint32_t fst0) -{ - uint32_t wt2; - - set_float_rounding_mode(float_round_down, &env->active_fpu.fp_status); - wt2 = float32_to_int32(fst0, &env->active_fpu.fp_status); - restore_rounding_mode(env); - if (get_float_exception_flags(&env->active_fpu.fp_status) - & float_flag_invalid) { - if (float32_is_any_nan(fst0)) { - wt2 = 0; - } - } - update_fcr31(env, GETPC()); - return wt2; -} - -/* unary operations, not modifying fp status */ -#define FLOAT_UNOP(name) \ -uint64_t helper_float_ ## name ## _d(uint64_t fdt0) \ -{ \ - return float64_ ## name(fdt0); \ -} \ -uint32_t helper_float_ ## name ## _s(uint32_t fst0) \ -{ \ - return float32_ ## name(fst0); \ -} \ -uint64_t helper_float_ ## name ## _ps(uint64_t fdt0) \ -{ \ - uint32_t wt0; \ - uint32_t wth0; \ - \ - wt0 = float32_ ## name(fdt0 & 0XFFFFFFFF); \ - wth0 = float32_ ## name(fdt0 >> 32); \ - return ((uint64_t)wth0 << 32) | wt0; \ -} -FLOAT_UNOP(abs) -FLOAT_UNOP(chs) -#undef FLOAT_UNOP - -/* MIPS specific unary operations */ -uint64_t helper_float_recip_d(CPUMIPSState *env, uint64_t fdt0) -{ - uint64_t fdt2; - - fdt2 = float64_div(float64_one, fdt0, &env->active_fpu.fp_status); - update_fcr31(env, GETPC()); - return fdt2; -} - -uint32_t helper_float_recip_s(CPUMIPSState *env, uint32_t fst0) -{ - uint32_t fst2; - - fst2 = float32_div(float32_one, fst0, &env->active_fpu.fp_status); - update_fcr31(env, GETPC()); - return fst2; -} - -uint64_t helper_float_rsqrt_d(CPUMIPSState *env, uint64_t fdt0) -{ - uint64_t fdt2; - - fdt2 = float64_sqrt(fdt0, &env->active_fpu.fp_status); - fdt2 = float64_div(float64_one, fdt2, &env->active_fpu.fp_status); - update_fcr31(env, GETPC()); - return fdt2; -} - -uint32_t helper_float_rsqrt_s(CPUMIPSState *env, uint32_t fst0) -{ - uint32_t fst2; - - fst2 = float32_sqrt(fst0, &env->active_fpu.fp_status); - fst2 = float32_div(float32_one, fst2, &env->active_fpu.fp_status); - update_fcr31(env, GETPC()); - return fst2; -} - -uint64_t helper_float_recip1_d(CPUMIPSState *env, uint64_t fdt0) -{ - uint64_t fdt2; - - fdt2 = float64_div(float64_one, fdt0, &env->active_fpu.fp_status); - update_fcr31(env, GETPC()); - return fdt2; -} - -uint32_t helper_float_recip1_s(CPUMIPSState *env, uint32_t fst0) -{ - uint32_t fst2; - - fst2 = float32_div(float32_one, fst0, &env->active_fpu.fp_status); - update_fcr31(env, GETPC()); - return fst2; -} - -uint64_t helper_float_recip1_ps(CPUMIPSState *env, uint64_t fdt0) -{ - uint32_t fst2; - uint32_t fsth2; - - fst2 = float32_div(float32_one, fdt0 & 0XFFFFFFFF, - &env->active_fpu.fp_status); - fsth2 = float32_div(float32_one, fdt0 >> 32, &env->active_fpu.fp_status); - update_fcr31(env, GETPC()); - return ((uint64_t)fsth2 << 32) | fst2; -} - -uint64_t helper_float_rsqrt1_d(CPUMIPSState *env, uint64_t fdt0) -{ - uint64_t fdt2; - - fdt2 = float64_sqrt(fdt0, &env->active_fpu.fp_status); - fdt2 = float64_div(float64_one, fdt2, &env->active_fpu.fp_status); - update_fcr31(env, GETPC()); - return fdt2; -} - -uint32_t helper_float_rsqrt1_s(CPUMIPSState *env, uint32_t fst0) -{ - uint32_t fst2; - - fst2 = float32_sqrt(fst0, &env->active_fpu.fp_status); - fst2 = float32_div(float32_one, fst2, &env->active_fpu.fp_status); - update_fcr31(env, GETPC()); - return fst2; -} - -uint64_t helper_float_rsqrt1_ps(CPUMIPSState *env, uint64_t fdt0) -{ - uint32_t fst2; - uint32_t fsth2; - - fst2 = float32_sqrt(fdt0 & 0XFFFFFFFF, &env->active_fpu.fp_status); - fsth2 = float32_sqrt(fdt0 >> 32, &env->active_fpu.fp_status); - fst2 = float32_div(float32_one, fst2, &env->active_fpu.fp_status); - fsth2 = float32_div(float32_one, fsth2, &env->active_fpu.fp_status); - update_fcr31(env, GETPC()); - return ((uint64_t)fsth2 << 32) | fst2; -} - -#define FLOAT_RINT(name, bits) \ -uint ## bits ## _t helper_float_ ## name(CPUMIPSState *env, \ - uint ## bits ## _t fs) \ -{ \ - uint ## bits ## _t fdret; \ - \ - fdret = float ## bits ## _round_to_int(fs, &env->active_fpu.fp_status); \ - update_fcr31(env, GETPC()); \ - return fdret; \ -} - -FLOAT_RINT(rint_s, 32) -FLOAT_RINT(rint_d, 64) -#undef FLOAT_RINT - -#define FLOAT_CLASS_SIGNALING_NAN 0x001 -#define FLOAT_CLASS_QUIET_NAN 0x002 -#define FLOAT_CLASS_NEGATIVE_INFINITY 0x004 -#define FLOAT_CLASS_NEGATIVE_NORMAL 0x008 -#define FLOAT_CLASS_NEGATIVE_SUBNORMAL 0x010 -#define FLOAT_CLASS_NEGATIVE_ZERO 0x020 -#define FLOAT_CLASS_POSITIVE_INFINITY 0x040 -#define FLOAT_CLASS_POSITIVE_NORMAL 0x080 -#define FLOAT_CLASS_POSITIVE_SUBNORMAL 0x100 -#define FLOAT_CLASS_POSITIVE_ZERO 0x200 - -#define FLOAT_CLASS(name, bits) \ -uint ## bits ## _t float_ ## name(uint ## bits ## _t arg, \ - float_status *status) \ -{ \ - if (float ## bits ## _is_signaling_nan(arg, status)) { \ - return FLOAT_CLASS_SIGNALING_NAN; \ - } else if (float ## bits ## _is_quiet_nan(arg, status)) { \ - return FLOAT_CLASS_QUIET_NAN; \ - } else if (float ## bits ## _is_neg(arg)) { \ - if (float ## bits ## _is_infinity(arg)) { \ - return FLOAT_CLASS_NEGATIVE_INFINITY; \ - } else if (float ## bits ## _is_zero(arg)) { \ - return FLOAT_CLASS_NEGATIVE_ZERO; \ - } else if (float ## bits ## _is_zero_or_denormal(arg)) { \ - return FLOAT_CLASS_NEGATIVE_SUBNORMAL; \ - } else { \ - return FLOAT_CLASS_NEGATIVE_NORMAL; \ - } \ - } else { \ - if (float ## bits ## _is_infinity(arg)) { \ - return FLOAT_CLASS_POSITIVE_INFINITY; \ - } else if (float ## bits ## _is_zero(arg)) { \ - return FLOAT_CLASS_POSITIVE_ZERO; \ - } else if (float ## bits ## _is_zero_or_denormal(arg)) { \ - return FLOAT_CLASS_POSITIVE_SUBNORMAL; \ - } else { \ - return FLOAT_CLASS_POSITIVE_NORMAL; \ - } \ - } \ -} \ - \ -uint ## bits ## _t helper_float_ ## name(CPUMIPSState *env, \ - uint ## bits ## _t arg) \ -{ \ - return float_ ## name(arg, &env->active_fpu.fp_status); \ -} - -FLOAT_CLASS(class_s, 32) -FLOAT_CLASS(class_d, 64) -#undef FLOAT_CLASS - -/* binary operations */ -#define FLOAT_BINOP(name) \ -uint64_t helper_float_ ## name ## _d(CPUMIPSState *env, \ - uint64_t fdt0, uint64_t fdt1) \ -{ \ - uint64_t dt2; \ - \ - dt2 = float64_ ## name(fdt0, fdt1, &env->active_fpu.fp_status);\ - update_fcr31(env, GETPC()); \ - return dt2; \ -} \ - \ -uint32_t helper_float_ ## name ## _s(CPUMIPSState *env, \ - uint32_t fst0, uint32_t fst1) \ -{ \ - uint32_t wt2; \ - \ - wt2 = float32_ ## name(fst0, fst1, &env->active_fpu.fp_status);\ - update_fcr31(env, GETPC()); \ - return wt2; \ -} \ - \ -uint64_t helper_float_ ## name ## _ps(CPUMIPSState *env, \ - uint64_t fdt0, \ - uint64_t fdt1) \ -{ \ - uint32_t fst0 = fdt0 & 0XFFFFFFFF; \ - uint32_t fsth0 = fdt0 >> 32; \ - uint32_t fst1 = fdt1 & 0XFFFFFFFF; \ - uint32_t fsth1 = fdt1 >> 32; \ - uint32_t wt2; \ - uint32_t wth2; \ - \ - wt2 = float32_ ## name(fst0, fst1, &env->active_fpu.fp_status); \ - wth2 = float32_ ## name(fsth0, fsth1, &env->active_fpu.fp_status); \ - update_fcr31(env, GETPC()); \ - return ((uint64_t)wth2 << 32) | wt2; \ -} - -FLOAT_BINOP(add) -FLOAT_BINOP(sub) -FLOAT_BINOP(mul) -FLOAT_BINOP(div) -#undef FLOAT_BINOP - -/* MIPS specific binary operations */ -uint64_t helper_float_recip2_d(CPUMIPSState *env, uint64_t fdt0, uint64_t fdt2) -{ - fdt2 = float64_mul(fdt0, fdt2, &env->active_fpu.fp_status); - fdt2 = float64_chs(float64_sub(fdt2, float64_one, - &env->active_fpu.fp_status)); - update_fcr31(env, GETPC()); - return fdt2; -} - -uint32_t helper_float_recip2_s(CPUMIPSState *env, uint32_t fst0, uint32_t fst2) -{ - fst2 = float32_mul(fst0, fst2, &env->active_fpu.fp_status); - fst2 = float32_chs(float32_sub(fst2, float32_one, - &env->active_fpu.fp_status)); - update_fcr31(env, GETPC()); - return fst2; -} - -uint64_t helper_float_recip2_ps(CPUMIPSState *env, uint64_t fdt0, uint64_t fdt2) -{ - uint32_t fst0 = fdt0 & 0XFFFFFFFF; - uint32_t fsth0 = fdt0 >> 32; - uint32_t fst2 = fdt2 & 0XFFFFFFFF; - uint32_t fsth2 = fdt2 >> 32; - - fst2 = float32_mul(fst0, fst2, &env->active_fpu.fp_status); - fsth2 = float32_mul(fsth0, fsth2, &env->active_fpu.fp_status); - fst2 = float32_chs(float32_sub(fst2, float32_one, - &env->active_fpu.fp_status)); - fsth2 = float32_chs(float32_sub(fsth2, float32_one, - &env->active_fpu.fp_status)); - update_fcr31(env, GETPC()); - return ((uint64_t)fsth2 << 32) | fst2; -} - -uint64_t helper_float_rsqrt2_d(CPUMIPSState *env, uint64_t fdt0, uint64_t fdt2) -{ - fdt2 = float64_mul(fdt0, fdt2, &env->active_fpu.fp_status); - fdt2 = float64_sub(fdt2, float64_one, &env->active_fpu.fp_status); - fdt2 = float64_chs(float64_div(fdt2, FLOAT_TWO64, - &env->active_fpu.fp_status)); - update_fcr31(env, GETPC()); - return fdt2; -} - -uint32_t helper_float_rsqrt2_s(CPUMIPSState *env, uint32_t fst0, uint32_t fst2) -{ - fst2 = float32_mul(fst0, fst2, &env->active_fpu.fp_status); - fst2 = float32_sub(fst2, float32_one, &env->active_fpu.fp_status); - fst2 = float32_chs(float32_div(fst2, FLOAT_TWO32, - &env->active_fpu.fp_status)); - update_fcr31(env, GETPC()); - return fst2; -} - -uint64_t helper_float_rsqrt2_ps(CPUMIPSState *env, uint64_t fdt0, uint64_t fdt2) -{ - uint32_t fst0 = fdt0 & 0XFFFFFFFF; - uint32_t fsth0 = fdt0 >> 32; - uint32_t fst2 = fdt2 & 0XFFFFFFFF; - uint32_t fsth2 = fdt2 >> 32; - - fst2 = float32_mul(fst0, fst2, &env->active_fpu.fp_status); - fsth2 = float32_mul(fsth0, fsth2, &env->active_fpu.fp_status); - fst2 = float32_sub(fst2, float32_one, &env->active_fpu.fp_status); - fsth2 = float32_sub(fsth2, float32_one, &env->active_fpu.fp_status); - fst2 = float32_chs(float32_div(fst2, FLOAT_TWO32, - &env->active_fpu.fp_status)); - fsth2 = float32_chs(float32_div(fsth2, FLOAT_TWO32, - &env->active_fpu.fp_status)); - update_fcr31(env, GETPC()); - return ((uint64_t)fsth2 << 32) | fst2; -} - -uint64_t helper_float_addr_ps(CPUMIPSState *env, uint64_t fdt0, uint64_t fdt1) -{ - uint32_t fst0 = fdt0 & 0XFFFFFFFF; - uint32_t fsth0 = fdt0 >> 32; - uint32_t fst1 = fdt1 & 0XFFFFFFFF; - uint32_t fsth1 = fdt1 >> 32; - uint32_t fst2; - uint32_t fsth2; - - fst2 = float32_add(fst0, fsth0, &env->active_fpu.fp_status); - fsth2 = float32_add(fst1, fsth1, &env->active_fpu.fp_status); - update_fcr31(env, GETPC()); - return ((uint64_t)fsth2 << 32) | fst2; -} - -uint64_t helper_float_mulr_ps(CPUMIPSState *env, uint64_t fdt0, uint64_t fdt1) -{ - uint32_t fst0 = fdt0 & 0XFFFFFFFF; - uint32_t fsth0 = fdt0 >> 32; - uint32_t fst1 = fdt1 & 0XFFFFFFFF; - uint32_t fsth1 = fdt1 >> 32; - uint32_t fst2; - uint32_t fsth2; - - fst2 = float32_mul(fst0, fsth0, &env->active_fpu.fp_status); - fsth2 = float32_mul(fst1, fsth1, &env->active_fpu.fp_status); - update_fcr31(env, GETPC()); - return ((uint64_t)fsth2 << 32) | fst2; -} - -#define FLOAT_MINMAX(name, bits, minmaxfunc) \ -uint ## bits ## _t helper_float_ ## name(CPUMIPSState *env, \ - uint ## bits ## _t fs, \ - uint ## bits ## _t ft) \ -{ \ - uint ## bits ## _t fdret; \ - \ - fdret = float ## bits ## _ ## minmaxfunc(fs, ft, \ - &env->active_fpu.fp_status); \ - update_fcr31(env, GETPC()); \ - return fdret; \ -} - -FLOAT_MINMAX(max_s, 32, maxnum) -FLOAT_MINMAX(max_d, 64, maxnum) -FLOAT_MINMAX(maxa_s, 32, maxnummag) -FLOAT_MINMAX(maxa_d, 64, maxnummag) - -FLOAT_MINMAX(min_s, 32, minnum) -FLOAT_MINMAX(min_d, 64, minnum) -FLOAT_MINMAX(mina_s, 32, minnummag) -FLOAT_MINMAX(mina_d, 64, minnummag) -#undef FLOAT_MINMAX - -/* ternary operations */ -#define UNFUSED_FMA(prefix, a, b, c, flags) \ -{ \ - a = prefix##_mul(a, b, &env->active_fpu.fp_status); \ - if ((flags) & float_muladd_negate_c) { \ - a = prefix##_sub(a, c, &env->active_fpu.fp_status); \ - } else { \ - a = prefix##_add(a, c, &env->active_fpu.fp_status); \ - } \ - if ((flags) & float_muladd_negate_result) { \ - a = prefix##_chs(a); \ - } \ -} - -/* FMA based operations */ -#define FLOAT_FMA(name, type) \ -uint64_t helper_float_ ## name ## _d(CPUMIPSState *env, \ - uint64_t fdt0, uint64_t fdt1, \ - uint64_t fdt2) \ -{ \ - UNFUSED_FMA(float64, fdt0, fdt1, fdt2, type); \ - update_fcr31(env, GETPC()); \ - return fdt0; \ -} \ - \ -uint32_t helper_float_ ## name ## _s(CPUMIPSState *env, \ - uint32_t fst0, uint32_t fst1, \ - uint32_t fst2) \ -{ \ - UNFUSED_FMA(float32, fst0, fst1, fst2, type); \ - update_fcr31(env, GETPC()); \ - return fst0; \ -} \ - \ -uint64_t helper_float_ ## name ## _ps(CPUMIPSState *env, \ - uint64_t fdt0, uint64_t fdt1, \ - uint64_t fdt2) \ -{ \ - uint32_t fst0 = fdt0 & 0XFFFFFFFF; \ - uint32_t fsth0 = fdt0 >> 32; \ - uint32_t fst1 = fdt1 & 0XFFFFFFFF; \ - uint32_t fsth1 = fdt1 >> 32; \ - uint32_t fst2 = fdt2 & 0XFFFFFFFF; \ - uint32_t fsth2 = fdt2 >> 32; \ - \ - UNFUSED_FMA(float32, fst0, fst1, fst2, type); \ - UNFUSED_FMA(float32, fsth0, fsth1, fsth2, type); \ - update_fcr31(env, GETPC()); \ - return ((uint64_t)fsth0 << 32) | fst0; \ -} -FLOAT_FMA(madd, 0) -FLOAT_FMA(msub, float_muladd_negate_c) -FLOAT_FMA(nmadd, float_muladd_negate_result) -FLOAT_FMA(nmsub, float_muladd_negate_result | float_muladd_negate_c) -#undef FLOAT_FMA - -#define FLOAT_FMADDSUB(name, bits, muladd_arg) \ -uint ## bits ## _t helper_float_ ## name(CPUMIPSState *env, \ - uint ## bits ## _t fs, \ - uint ## bits ## _t ft, \ - uint ## bits ## _t fd) \ -{ \ - uint ## bits ## _t fdret; \ - \ - fdret = float ## bits ## _muladd(fs, ft, fd, muladd_arg, \ - &env->active_fpu.fp_status); \ - update_fcr31(env, GETPC()); \ - return fdret; \ -} - -FLOAT_FMADDSUB(maddf_s, 32, 0) -FLOAT_FMADDSUB(maddf_d, 64, 0) -FLOAT_FMADDSUB(msubf_s, 32, float_muladd_negate_product) -FLOAT_FMADDSUB(msubf_d, 64, float_muladd_negate_product) -#undef FLOAT_FMADDSUB - -/* compare operations */ -#define FOP_COND_D(op, cond) \ -void helper_cmp_d_ ## op(CPUMIPSState *env, uint64_t fdt0, \ - uint64_t fdt1, int cc) \ -{ \ - int c; \ - c = cond; \ - update_fcr31(env, GETPC()); \ - if (c) \ - SET_FP_COND(cc, env->active_fpu); \ - else \ - CLEAR_FP_COND(cc, env->active_fpu); \ -} \ -void helper_cmpabs_d_ ## op(CPUMIPSState *env, uint64_t fdt0, \ - uint64_t fdt1, int cc) \ -{ \ - int c; \ - fdt0 = float64_abs(fdt0); \ - fdt1 = float64_abs(fdt1); \ - c = cond; \ - update_fcr31(env, GETPC()); \ - if (c) \ - SET_FP_COND(cc, env->active_fpu); \ - else \ - CLEAR_FP_COND(cc, env->active_fpu); \ -} - -/* - * NOTE: the comma operator will make "cond" to eval to false, - * but float64_unordered_quiet() is still called. - */ -FOP_COND_D(f, (float64_unordered_quiet(fdt1, fdt0, - &env->active_fpu.fp_status), 0)) -FOP_COND_D(un, float64_unordered_quiet(fdt1, fdt0, - &env->active_fpu.fp_status)) -FOP_COND_D(eq, float64_eq_quiet(fdt0, fdt1, - &env->active_fpu.fp_status)) -FOP_COND_D(ueq, float64_unordered_quiet(fdt1, fdt0, - &env->active_fpu.fp_status) - || float64_eq_quiet(fdt0, fdt1, - &env->active_fpu.fp_status)) -FOP_COND_D(olt, float64_lt_quiet(fdt0, fdt1, - &env->active_fpu.fp_status)) -FOP_COND_D(ult, float64_unordered_quiet(fdt1, fdt0, - &env->active_fpu.fp_status) - || float64_lt_quiet(fdt0, fdt1, - &env->active_fpu.fp_status)) -FOP_COND_D(ole, float64_le_quiet(fdt0, fdt1, - &env->active_fpu.fp_status)) -FOP_COND_D(ule, float64_unordered_quiet(fdt1, fdt0, - &env->active_fpu.fp_status) - || float64_le_quiet(fdt0, fdt1, - &env->active_fpu.fp_status)) -/* - * NOTE: the comma operator will make "cond" to eval to false, - * but float64_unordered() is still called. - */ -FOP_COND_D(sf, (float64_unordered(fdt1, fdt0, - &env->active_fpu.fp_status), 0)) -FOP_COND_D(ngle, float64_unordered(fdt1, fdt0, - &env->active_fpu.fp_status)) -FOP_COND_D(seq, float64_eq(fdt0, fdt1, - &env->active_fpu.fp_status)) -FOP_COND_D(ngl, float64_unordered(fdt1, fdt0, - &env->active_fpu.fp_status) - || float64_eq(fdt0, fdt1, - &env->active_fpu.fp_status)) -FOP_COND_D(lt, float64_lt(fdt0, fdt1, - &env->active_fpu.fp_status)) -FOP_COND_D(nge, float64_unordered(fdt1, fdt0, - &env->active_fpu.fp_status) - || float64_lt(fdt0, fdt1, - &env->active_fpu.fp_status)) -FOP_COND_D(le, float64_le(fdt0, fdt1, - &env->active_fpu.fp_status)) -FOP_COND_D(ngt, float64_unordered(fdt1, fdt0, - &env->active_fpu.fp_status) - || float64_le(fdt0, fdt1, - &env->active_fpu.fp_status)) - -#define FOP_COND_S(op, cond) \ -void helper_cmp_s_ ## op(CPUMIPSState *env, uint32_t fst0, \ - uint32_t fst1, int cc) \ -{ \ - int c; \ - c = cond; \ - update_fcr31(env, GETPC()); \ - if (c) \ - SET_FP_COND(cc, env->active_fpu); \ - else \ - CLEAR_FP_COND(cc, env->active_fpu); \ -} \ -void helper_cmpabs_s_ ## op(CPUMIPSState *env, uint32_t fst0, \ - uint32_t fst1, int cc) \ -{ \ - int c; \ - fst0 = float32_abs(fst0); \ - fst1 = float32_abs(fst1); \ - c = cond; \ - update_fcr31(env, GETPC()); \ - if (c) \ - SET_FP_COND(cc, env->active_fpu); \ - else \ - CLEAR_FP_COND(cc, env->active_fpu); \ -} - -/* - * NOTE: the comma operator will make "cond" to eval to false, - * but float32_unordered_quiet() is still called. - */ -FOP_COND_S(f, (float32_unordered_quiet(fst1, fst0, - &env->active_fpu.fp_status), 0)) -FOP_COND_S(un, float32_unordered_quiet(fst1, fst0, - &env->active_fpu.fp_status)) -FOP_COND_S(eq, float32_eq_quiet(fst0, fst1, - &env->active_fpu.fp_status)) -FOP_COND_S(ueq, float32_unordered_quiet(fst1, fst0, - &env->active_fpu.fp_status) - || float32_eq_quiet(fst0, fst1, - &env->active_fpu.fp_status)) -FOP_COND_S(olt, float32_lt_quiet(fst0, fst1, - &env->active_fpu.fp_status)) -FOP_COND_S(ult, float32_unordered_quiet(fst1, fst0, - &env->active_fpu.fp_status) - || float32_lt_quiet(fst0, fst1, - &env->active_fpu.fp_status)) -FOP_COND_S(ole, float32_le_quiet(fst0, fst1, - &env->active_fpu.fp_status)) -FOP_COND_S(ule, float32_unordered_quiet(fst1, fst0, - &env->active_fpu.fp_status) - || float32_le_quiet(fst0, fst1, - &env->active_fpu.fp_status)) -/* - * NOTE: the comma operator will make "cond" to eval to false, - * but float32_unordered() is still called. - */ -FOP_COND_S(sf, (float32_unordered(fst1, fst0, - &env->active_fpu.fp_status), 0)) -FOP_COND_S(ngle, float32_unordered(fst1, fst0, - &env->active_fpu.fp_status)) -FOP_COND_S(seq, float32_eq(fst0, fst1, - &env->active_fpu.fp_status)) -FOP_COND_S(ngl, float32_unordered(fst1, fst0, - &env->active_fpu.fp_status) - || float32_eq(fst0, fst1, - &env->active_fpu.fp_status)) -FOP_COND_S(lt, float32_lt(fst0, fst1, - &env->active_fpu.fp_status)) -FOP_COND_S(nge, float32_unordered(fst1, fst0, - &env->active_fpu.fp_status) - || float32_lt(fst0, fst1, - &env->active_fpu.fp_status)) -FOP_COND_S(le, float32_le(fst0, fst1, - &env->active_fpu.fp_status)) -FOP_COND_S(ngt, float32_unordered(fst1, fst0, - &env->active_fpu.fp_status) - || float32_le(fst0, fst1, - &env->active_fpu.fp_status)) - -#define FOP_COND_PS(op, condl, condh) \ -void helper_cmp_ps_ ## op(CPUMIPSState *env, uint64_t fdt0, \ - uint64_t fdt1, int cc) \ -{ \ - uint32_t fst0, fsth0, fst1, fsth1; \ - int ch, cl; \ - fst0 = fdt0 & 0XFFFFFFFF; \ - fsth0 = fdt0 >> 32; \ - fst1 = fdt1 & 0XFFFFFFFF; \ - fsth1 = fdt1 >> 32; \ - cl = condl; \ - ch = condh; \ - update_fcr31(env, GETPC()); \ - if (cl) \ - SET_FP_COND(cc, env->active_fpu); \ - else \ - CLEAR_FP_COND(cc, env->active_fpu); \ - if (ch) \ - SET_FP_COND(cc + 1, env->active_fpu); \ - else \ - CLEAR_FP_COND(cc + 1, env->active_fpu); \ -} \ -void helper_cmpabs_ps_ ## op(CPUMIPSState *env, uint64_t fdt0, \ - uint64_t fdt1, int cc) \ -{ \ - uint32_t fst0, fsth0, fst1, fsth1; \ - int ch, cl; \ - fst0 = float32_abs(fdt0 & 0XFFFFFFFF); \ - fsth0 = float32_abs(fdt0 >> 32); \ - fst1 = float32_abs(fdt1 & 0XFFFFFFFF); \ - fsth1 = float32_abs(fdt1 >> 32); \ - cl = condl; \ - ch = condh; \ - update_fcr31(env, GETPC()); \ - if (cl) \ - SET_FP_COND(cc, env->active_fpu); \ - else \ - CLEAR_FP_COND(cc, env->active_fpu); \ - if (ch) \ - SET_FP_COND(cc + 1, env->active_fpu); \ - else \ - CLEAR_FP_COND(cc + 1, env->active_fpu); \ -} - -/* - * NOTE: the comma operator will make "cond" to eval to false, - * but float32_unordered_quiet() is still called. - */ -FOP_COND_PS(f, (float32_unordered_quiet(fst1, fst0, - &env->active_fpu.fp_status), 0), - (float32_unordered_quiet(fsth1, fsth0, - &env->active_fpu.fp_status), 0)) -FOP_COND_PS(un, float32_unordered_quiet(fst1, fst0, - &env->active_fpu.fp_status), - float32_unordered_quiet(fsth1, fsth0, - &env->active_fpu.fp_status)) -FOP_COND_PS(eq, float32_eq_quiet(fst0, fst1, - &env->active_fpu.fp_status), - float32_eq_quiet(fsth0, fsth1, - &env->active_fpu.fp_status)) -FOP_COND_PS(ueq, float32_unordered_quiet(fst1, fst0, - &env->active_fpu.fp_status) - || float32_eq_quiet(fst0, fst1, - &env->active_fpu.fp_status), - float32_unordered_quiet(fsth1, fsth0, - &env->active_fpu.fp_status) - || float32_eq_quiet(fsth0, fsth1, - &env->active_fpu.fp_status)) -FOP_COND_PS(olt, float32_lt_quiet(fst0, fst1, - &env->active_fpu.fp_status), - float32_lt_quiet(fsth0, fsth1, - &env->active_fpu.fp_status)) -FOP_COND_PS(ult, float32_unordered_quiet(fst1, fst0, - &env->active_fpu.fp_status) - || float32_lt_quiet(fst0, fst1, - &env->active_fpu.fp_status), - float32_unordered_quiet(fsth1, fsth0, - &env->active_fpu.fp_status) - || float32_lt_quiet(fsth0, fsth1, - &env->active_fpu.fp_status)) -FOP_COND_PS(ole, float32_le_quiet(fst0, fst1, - &env->active_fpu.fp_status), - float32_le_quiet(fsth0, fsth1, - &env->active_fpu.fp_status)) -FOP_COND_PS(ule, float32_unordered_quiet(fst1, fst0, - &env->active_fpu.fp_status) - || float32_le_quiet(fst0, fst1, - &env->active_fpu.fp_status), - float32_unordered_quiet(fsth1, fsth0, - &env->active_fpu.fp_status) - || float32_le_quiet(fsth0, fsth1, - &env->active_fpu.fp_status)) -/* - * NOTE: the comma operator will make "cond" to eval to false, - * but float32_unordered() is still called. - */ -FOP_COND_PS(sf, (float32_unordered(fst1, fst0, - &env->active_fpu.fp_status), 0), - (float32_unordered(fsth1, fsth0, - &env->active_fpu.fp_status), 0)) -FOP_COND_PS(ngle, float32_unordered(fst1, fst0, - &env->active_fpu.fp_status), - float32_unordered(fsth1, fsth0, - &env->active_fpu.fp_status)) -FOP_COND_PS(seq, float32_eq(fst0, fst1, - &env->active_fpu.fp_status), - float32_eq(fsth0, fsth1, - &env->active_fpu.fp_status)) -FOP_COND_PS(ngl, float32_unordered(fst1, fst0, - &env->active_fpu.fp_status) - || float32_eq(fst0, fst1, - &env->active_fpu.fp_status), - float32_unordered(fsth1, fsth0, - &env->active_fpu.fp_status) - || float32_eq(fsth0, fsth1, - &env->active_fpu.fp_status)) -FOP_COND_PS(lt, float32_lt(fst0, fst1, - &env->active_fpu.fp_status), - float32_lt(fsth0, fsth1, - &env->active_fpu.fp_status)) -FOP_COND_PS(nge, float32_unordered(fst1, fst0, - &env->active_fpu.fp_status) - || float32_lt(fst0, fst1, - &env->active_fpu.fp_status), - float32_unordered(fsth1, fsth0, - &env->active_fpu.fp_status) - || float32_lt(fsth0, fsth1, - &env->active_fpu.fp_status)) -FOP_COND_PS(le, float32_le(fst0, fst1, - &env->active_fpu.fp_status), - float32_le(fsth0, fsth1, - &env->active_fpu.fp_status)) -FOP_COND_PS(ngt, float32_unordered(fst1, fst0, - &env->active_fpu.fp_status) - || float32_le(fst0, fst1, - &env->active_fpu.fp_status), - float32_unordered(fsth1, fsth0, - &env->active_fpu.fp_status) - || float32_le(fsth0, fsth1, - &env->active_fpu.fp_status)) - -/* R6 compare operations */ -#define FOP_CONDN_D(op, cond) \ -uint64_t helper_r6_cmp_d_ ## op(CPUMIPSState *env, uint64_t fdt0, \ - uint64_t fdt1) \ -{ \ - uint64_t c; \ - c = cond; \ - update_fcr31(env, GETPC()); \ - if (c) { \ - return -1; \ - } else { \ - return 0; \ - } \ -} - -/* - * NOTE: the comma operator will make "cond" to eval to false, - * but float64_unordered_quiet() is still called. - */ -FOP_CONDN_D(af, (float64_unordered_quiet(fdt1, fdt0, - &env->active_fpu.fp_status), 0)) -FOP_CONDN_D(un, (float64_unordered_quiet(fdt1, fdt0, - &env->active_fpu.fp_status))) -FOP_CONDN_D(eq, (float64_eq_quiet(fdt0, fdt1, - &env->active_fpu.fp_status))) -FOP_CONDN_D(ueq, (float64_unordered_quiet(fdt1, fdt0, - &env->active_fpu.fp_status) - || float64_eq_quiet(fdt0, fdt1, - &env->active_fpu.fp_status))) -FOP_CONDN_D(lt, (float64_lt_quiet(fdt0, fdt1, - &env->active_fpu.fp_status))) -FOP_CONDN_D(ult, (float64_unordered_quiet(fdt1, fdt0, - &env->active_fpu.fp_status) - || float64_lt_quiet(fdt0, fdt1, - &env->active_fpu.fp_status))) -FOP_CONDN_D(le, (float64_le_quiet(fdt0, fdt1, - &env->active_fpu.fp_status))) -FOP_CONDN_D(ule, (float64_unordered_quiet(fdt1, fdt0, - &env->active_fpu.fp_status) - || float64_le_quiet(fdt0, fdt1, - &env->active_fpu.fp_status))) -/* - * NOTE: the comma operator will make "cond" to eval to false, - * but float64_unordered() is still called.\ - */ -FOP_CONDN_D(saf, (float64_unordered(fdt1, fdt0, - &env->active_fpu.fp_status), 0)) -FOP_CONDN_D(sun, (float64_unordered(fdt1, fdt0, - &env->active_fpu.fp_status))) -FOP_CONDN_D(seq, (float64_eq(fdt0, fdt1, - &env->active_fpu.fp_status))) -FOP_CONDN_D(sueq, (float64_unordered(fdt1, fdt0, - &env->active_fpu.fp_status) - || float64_eq(fdt0, fdt1, - &env->active_fpu.fp_status))) -FOP_CONDN_D(slt, (float64_lt(fdt0, fdt1, - &env->active_fpu.fp_status))) -FOP_CONDN_D(sult, (float64_unordered(fdt1, fdt0, - &env->active_fpu.fp_status) - || float64_lt(fdt0, fdt1, - &env->active_fpu.fp_status))) -FOP_CONDN_D(sle, (float64_le(fdt0, fdt1, - &env->active_fpu.fp_status))) -FOP_CONDN_D(sule, (float64_unordered(fdt1, fdt0, - &env->active_fpu.fp_status) - || float64_le(fdt0, fdt1, - &env->active_fpu.fp_status))) -FOP_CONDN_D(or, (float64_le_quiet(fdt1, fdt0, - &env->active_fpu.fp_status) - || float64_le_quiet(fdt0, fdt1, - &env->active_fpu.fp_status))) -FOP_CONDN_D(une, (float64_unordered_quiet(fdt1, fdt0, - &env->active_fpu.fp_status) - || float64_lt_quiet(fdt1, fdt0, - &env->active_fpu.fp_status) - || float64_lt_quiet(fdt0, fdt1, - &env->active_fpu.fp_status))) -FOP_CONDN_D(ne, (float64_lt_quiet(fdt1, fdt0, - &env->active_fpu.fp_status) - || float64_lt_quiet(fdt0, fdt1, - &env->active_fpu.fp_status))) -FOP_CONDN_D(sor, (float64_le(fdt1, fdt0, - &env->active_fpu.fp_status) - || float64_le(fdt0, fdt1, - &env->active_fpu.fp_status))) -FOP_CONDN_D(sune, (float64_unordered(fdt1, fdt0, - &env->active_fpu.fp_status) - || float64_lt(fdt1, fdt0, - &env->active_fpu.fp_status) - || float64_lt(fdt0, fdt1, - &env->active_fpu.fp_status))) -FOP_CONDN_D(sne, (float64_lt(fdt1, fdt0, - &env->active_fpu.fp_status) - || float64_lt(fdt0, fdt1, - &env->active_fpu.fp_status))) - -#define FOP_CONDN_S(op, cond) \ -uint32_t helper_r6_cmp_s_ ## op(CPUMIPSState *env, uint32_t fst0, \ - uint32_t fst1) \ -{ \ - uint64_t c; \ - c = cond; \ - update_fcr31(env, GETPC()); \ - if (c) { \ - return -1; \ - } else { \ - return 0; \ - } \ -} - -/* - * NOTE: the comma operator will make "cond" to eval to false, - * but float32_unordered_quiet() is still called. - */ -FOP_CONDN_S(af, (float32_unordered_quiet(fst1, fst0, - &env->active_fpu.fp_status), 0)) -FOP_CONDN_S(un, (float32_unordered_quiet(fst1, fst0, - &env->active_fpu.fp_status))) -FOP_CONDN_S(eq, (float32_eq_quiet(fst0, fst1, - &env->active_fpu.fp_status))) -FOP_CONDN_S(ueq, (float32_unordered_quiet(fst1, fst0, - &env->active_fpu.fp_status) - || float32_eq_quiet(fst0, fst1, - &env->active_fpu.fp_status))) -FOP_CONDN_S(lt, (float32_lt_quiet(fst0, fst1, - &env->active_fpu.fp_status))) -FOP_CONDN_S(ult, (float32_unordered_quiet(fst1, fst0, - &env->active_fpu.fp_status) - || float32_lt_quiet(fst0, fst1, - &env->active_fpu.fp_status))) -FOP_CONDN_S(le, (float32_le_quiet(fst0, fst1, - &env->active_fpu.fp_status))) -FOP_CONDN_S(ule, (float32_unordered_quiet(fst1, fst0, - &env->active_fpu.fp_status) - || float32_le_quiet(fst0, fst1, - &env->active_fpu.fp_status))) -/* - * NOTE: the comma operator will make "cond" to eval to false, - * but float32_unordered() is still called. - */ -FOP_CONDN_S(saf, (float32_unordered(fst1, fst0, - &env->active_fpu.fp_status), 0)) -FOP_CONDN_S(sun, (float32_unordered(fst1, fst0, - &env->active_fpu.fp_status))) -FOP_CONDN_S(seq, (float32_eq(fst0, fst1, - &env->active_fpu.fp_status))) -FOP_CONDN_S(sueq, (float32_unordered(fst1, fst0, - &env->active_fpu.fp_status) - || float32_eq(fst0, fst1, - &env->active_fpu.fp_status))) -FOP_CONDN_S(slt, (float32_lt(fst0, fst1, - &env->active_fpu.fp_status))) -FOP_CONDN_S(sult, (float32_unordered(fst1, fst0, - &env->active_fpu.fp_status) - || float32_lt(fst0, fst1, - &env->active_fpu.fp_status))) -FOP_CONDN_S(sle, (float32_le(fst0, fst1, - &env->active_fpu.fp_status))) -FOP_CONDN_S(sule, (float32_unordered(fst1, fst0, - &env->active_fpu.fp_status) - || float32_le(fst0, fst1, - &env->active_fpu.fp_status))) -FOP_CONDN_S(or, (float32_le_quiet(fst1, fst0, - &env->active_fpu.fp_status) - || float32_le_quiet(fst0, fst1, - &env->active_fpu.fp_status))) -FOP_CONDN_S(une, (float32_unordered_quiet(fst1, fst0, - &env->active_fpu.fp_status) - || float32_lt_quiet(fst1, fst0, - &env->active_fpu.fp_status) - || float32_lt_quiet(fst0, fst1, - &env->active_fpu.fp_status))) -FOP_CONDN_S(ne, (float32_lt_quiet(fst1, fst0, - &env->active_fpu.fp_status) - || float32_lt_quiet(fst0, fst1, - &env->active_fpu.fp_status))) -FOP_CONDN_S(sor, (float32_le(fst1, fst0, - &env->active_fpu.fp_status) - || float32_le(fst0, fst1, - &env->active_fpu.fp_status))) -FOP_CONDN_S(sune, (float32_unordered(fst1, fst0, - &env->active_fpu.fp_status) - || float32_lt(fst1, fst0, - &env->active_fpu.fp_status) - || float32_lt(fst0, fst1, - &env->active_fpu.fp_status))) -FOP_CONDN_S(sne, (float32_lt(fst1, fst0, - &env->active_fpu.fp_status) - || float32_lt(fst0, fst1, - &env->active_fpu.fp_status))) /* MSA */ /* Data format min and max values */ diff --git a/target/mips/translate.c b/target/mips/translate.c index 4bff585bd6..25b595a17d 100644 --- a/target/mips/translate.c +++ b/target/mips/translate.c @@ -26,7 +26,7 @@ #include "internal.h" #include "disas/disas.h" #include "exec/exec-all.h" -#include "tcg-op.h" +#include "tcg/tcg-op.h" #include "exec/cpu_ldst.h" #include "hw/mips/cpudevs.h" @@ -388,6 +388,7 @@ enum { OPC_BSHFL = 0x20 | OPC_SPECIAL3, OPC_DBSHFL = 0x24 | OPC_SPECIAL3, OPC_RDHWR = 0x3B | OPC_SPECIAL3, + OPC_GINV = 0x3D | OPC_SPECIAL3, /* Loongson 2E */ OPC_MULT_G_2E = 0x18 | OPC_SPECIAL3, @@ -2547,6 +2548,8 @@ typedef struct DisasContext { bool nan2008; bool abs2008; bool saar; + bool mi; + int gi; } DisasContext; #define DISAS_STOP DISAS_TARGET_0 @@ -5526,6 +5529,7 @@ static void gen_loongson_multimedia(DisasContext *ctx, int rd, int rs, int rt) { uint32_t opc, shift_max; TCGv_i64 t0, t1; + TCGCond cond; opc = MASK_LMI(ctx->opcode); switch (opc) { @@ -5859,14 +5863,39 @@ static void gen_loongson_multimedia(DisasContext *ctx, int rd, int rs, int rt) case OPC_SEQU_CP2: case OPC_SEQ_CP2: + cond = TCG_COND_EQ; + goto do_cc_cond; + break; case OPC_SLTU_CP2: + cond = TCG_COND_LTU; + goto do_cc_cond; + break; case OPC_SLT_CP2: + cond = TCG_COND_LT; + goto do_cc_cond; + break; case OPC_SLEU_CP2: + cond = TCG_COND_LEU; + goto do_cc_cond; + break; case OPC_SLE_CP2: - /* - * ??? Document is unclear: Set FCC[CC]. Does that mean the - * FD field is the CC field? - */ + cond = TCG_COND_LE; + do_cc_cond: + { + int cc = (ctx->opcode >> 8) & 0x7; + TCGv_i64 t64 = tcg_temp_new_i64(); + TCGv_i32 t32 = tcg_temp_new_i32(); + + tcg_gen_setcond_i64(cond, t64, t0, t1); + tcg_gen_extrl_i64_i32(t32, t64); + tcg_gen_deposit_i32(fpu_fcr31, fpu_fcr31, t32, + get_fp_bit(cc), 1); + + tcg_temp_free_i32(t32); + tcg_temp_free_i64(t64); + } + goto no_rd; + break; default: MIPS_INVAL("loongson_cp2"); generate_exception_end(ctx, EXCP_RI); @@ -5875,6 +5904,7 @@ static void gen_loongson_multimedia(DisasContext *ctx, int rd, int rs, int rt) gen_store_fpr64(ctx, t0, rd); +no_rd: tcg_temp_free_i64(t0); tcg_temp_free_i64(t1); } @@ -6783,6 +6813,25 @@ static void gen_mfhc0(DisasContext *ctx, TCGv arg, int reg, int sel) goto cp0_unimplemented; } break; + case CP0_REGISTER_19: + switch (sel) { + case CP0_REG19__WATCHHI0: + case CP0_REG19__WATCHHI1: + case CP0_REG19__WATCHHI2: + case CP0_REG19__WATCHHI3: + case CP0_REG19__WATCHHI4: + case CP0_REG19__WATCHHI5: + case CP0_REG19__WATCHHI6: + case CP0_REG19__WATCHHI7: + /* upper 32 bits are only available when Config5MI != 0 */ + CP0_CHECK(ctx->mi); + gen_mfhc0_load64(arg, offsetof(CPUMIPSState, CP0_WatchHi[sel]), 0); + register_name = "WatchHi"; + break; + default: + goto cp0_unimplemented; + } + break; case CP0_REGISTER_28: switch (sel) { case 0: @@ -6869,6 +6918,25 @@ static void gen_mthc0(DisasContext *ctx, TCGv arg, int reg, int sel) goto cp0_unimplemented; } break; + case CP0_REGISTER_19: + switch (sel) { + case CP0_REG19__WATCHHI0: + case CP0_REG19__WATCHHI1: + case CP0_REG19__WATCHHI2: + case CP0_REG19__WATCHHI3: + case CP0_REG19__WATCHHI4: + case CP0_REG19__WATCHHI5: + case CP0_REG19__WATCHHI6: + case CP0_REG19__WATCHHI7: + /* upper 32 bits are only available when Config5MI != 0 */ + CP0_CHECK(ctx->mi); + gen_helper_0e1i(mthc0_watchhi, arg, sel); + register_name = "WatchHi"; + break; + default: + goto cp0_unimplemented; + } + break; case CP0_REGISTER_28: switch (sel) { case 0: @@ -7092,6 +7160,11 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel) tcg_gen_ext32s_tl(arg, arg); register_name = "UserLocal"; break; + case CP0_REG04__MMID: + CP0_CHECK(ctx->mi); + gen_helper_mtc0_memorymapid(cpu_env, arg); + register_name = "MMID"; + break; default: goto cp0_unimplemented; } @@ -7832,6 +7905,11 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) offsetof(CPUMIPSState, active_tc.CP0_UserLocal)); register_name = "UserLocal"; break; + case CP0_REG04__MMID: + CP0_CHECK(ctx->mi); + gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_MemoryMapID)); + register_name = "MMID"; + break; default: goto cp0_unimplemented; } @@ -8590,6 +8668,11 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel) offsetof(CPUMIPSState, active_tc.CP0_UserLocal)); register_name = "UserLocal"; break; + case CP0_REG04__MMID: + CP0_CHECK(ctx->mi); + gen_helper_mtc0_memorymapid(cpu_env, arg); + register_name = "MMID"; + break; default: goto cp0_unimplemented; } @@ -8922,7 +9005,7 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel) case CP0_REG19__WATCHHI6: case CP0_REG19__WATCHHI7: CP0_CHECK(ctx->CP0_Config1 & (1 << CP0C1_WR)); - gen_helper_1e0i(mfc0_watchhi, arg, sel); + gen_helper_1e0i(dmfc0_watchhi, arg, sel); register_name = "WatchHi"; break; default: @@ -9312,6 +9395,11 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) offsetof(CPUMIPSState, active_tc.CP0_UserLocal)); register_name = "UserLocal"; break; + case CP0_REG04__MMID: + CP0_CHECK(ctx->mi); + gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_MemoryMapID)); + register_name = "MMID"; + break; default: goto cp0_unimplemented; } @@ -27170,6 +27258,25 @@ static void decode_opc_special3_r6(CPUMIPSState *env, DisasContext *ctx) } } break; +#ifndef CONFIG_USER_ONLY + case OPC_GINV: + if (unlikely(ctx->gi <= 1)) { + generate_exception_end(ctx, EXCP_RI); + } + check_cp0_enabled(ctx); + switch ((ctx->opcode >> 6) & 3) { + case 0: /* GINVI */ + /* Treat as NOP. */ + break; + case 2: /* GINVT */ + gen_helper_0e1i(ginvt, cpu_gpr[rs], extract32(ctx->opcode, 8, 2)); + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } + break; +#endif #if defined(TARGET_MIPS64) case R6_OPC_SCD: gen_st_cond(ctx, rt, rs, imm, MO_TEQ, false); @@ -30727,6 +30834,8 @@ static void mips_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) ctx->mrp = (env->CP0_Config5 >> CP0C5_MRP) & 1; ctx->nan2008 = (env->active_fpu.fcr31 >> FCR31_NAN2008) & 1; ctx->abs2008 = (env->active_fpu.fcr31 >> FCR31_ABS2008) & 1; + ctx->mi = (env->CP0_Config5 >> CP0C5_MI) & 1; + ctx->gi = (env->CP0_Config5 >> CP0C5_GI) & 3; restore_cpu_state(env, ctx); #ifdef CONFIG_USER_ONLY ctx->mem_idx = MIPS_HFLAG_UM; @@ -31187,8 +31296,8 @@ void cpu_state_reset(CPUMIPSState *env) if (env->CP0_Config3 & (1 << CP0C3_CMGCR)) { env->CP0_CMGCRBase = 0x1fbf8000 >> 4; } - env->CP0_EntryHi_ASID_mask = (env->CP0_Config4 & (1 << CP0C4_AE)) ? - 0x3ff : 0xff; + env->CP0_EntryHi_ASID_mask = (env->CP0_Config5 & (1 << CP0C5_MI)) ? + 0x0 : (env->CP0_Config4 & (1 << CP0C4_AE)) ? 0x3ff : 0xff; env->CP0_Status = (1 << CP0St_BEV) | (1 << CP0St_ERL); /* * Vectored interrupts not implemented, timer on int 7, diff --git a/target/moxie/cpu.c b/target/moxie/cpu.c index 48996d0554..6e0443ccb7 100644 --- a/target/moxie/cpu.c +++ b/target/moxie/cpu.c @@ -35,13 +35,14 @@ static bool moxie_cpu_has_work(CPUState *cs) return cs->interrupt_request & CPU_INTERRUPT_HARD; } -static void moxie_cpu_reset(CPUState *s) +static void moxie_cpu_reset(DeviceState *dev) { + CPUState *s = CPU(dev); MoxieCPU *cpu = MOXIE_CPU(s); MoxieCPUClass *mcc = MOXIE_CPU_GET_CLASS(cpu); CPUMoxieState *env = &cpu->env; - mcc->parent_reset(s); + mcc->parent_reset(dev); memset(env, 0, offsetof(CPUMoxieState, end_reset_fields)); env->pc = 0x1000; @@ -101,8 +102,7 @@ static void moxie_cpu_class_init(ObjectClass *oc, void *data) device_class_set_parent_realize(dc, moxie_cpu_realizefn, &mcc->parent_realize); - mcc->parent_reset = cc->reset; - cc->reset = moxie_cpu_reset; + device_class_set_parent_reset(dc, moxie_cpu_reset, &mcc->parent_reset); cc->class_by_name = moxie_cpu_class_by_name; diff --git a/target/moxie/cpu.h b/target/moxie/cpu.h index 01dca548e5..455553b794 100644 --- a/target/moxie/cpu.h +++ b/target/moxie/cpu.h @@ -69,7 +69,7 @@ typedef struct MoxieCPUClass { /*< public >*/ DeviceRealize parent_realize; - void (*parent_reset)(CPUState *cpu); + DeviceReset parent_reset; } MoxieCPUClass; /** diff --git a/target/moxie/translate.c b/target/moxie/translate.c index c87e9ec2b1..d5fb27dfb8 100644 --- a/target/moxie/translate.c +++ b/target/moxie/translate.c @@ -26,7 +26,7 @@ #include "cpu.h" #include "exec/exec-all.h" #include "disas/disas.h" -#include "tcg-op.h" +#include "tcg/tcg-op.h" #include "exec/cpu_ldst.h" #include "qemu/qemu-print.h" diff --git a/target/nios2/cpu.c b/target/nios2/cpu.c index ca9c7a6df5..8f7011fcb9 100644 --- a/target/nios2/cpu.c +++ b/target/nios2/cpu.c @@ -39,9 +39,9 @@ static bool nios2_cpu_has_work(CPUState *cs) return cs->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_NMI); } -/* CPUClass::reset() */ -static void nios2_cpu_reset(CPUState *cs) +static void nios2_cpu_reset(DeviceState *dev) { + CPUState *cs = CPU(dev); Nios2CPU *cpu = NIOS2_CPU(cs); Nios2CPUClass *ncc = NIOS2_CPU_GET_CLASS(cpu); CPUNios2State *env = &cpu->env; @@ -51,7 +51,7 @@ static void nios2_cpu_reset(CPUState *cs) log_cpu_state(cs, 0); } - ncc->parent_reset(cs); + ncc->parent_reset(dev); memset(env->regs, 0, sizeof(uint32_t) * NUM_CORE_REGS); env->regs[R_PC] = cpu->reset_addr; @@ -124,7 +124,7 @@ static void nios2_cpu_disas_set_info(CPUState *cpu, disassemble_info *info) #endif } -static int nios2_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +static int nios2_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) { Nios2CPU *cpu = NIOS2_CPU(cs); CPUClass *cc = CPU_GET_CLASS(cs); @@ -187,9 +187,8 @@ static void nios2_cpu_class_init(ObjectClass *oc, void *data) device_class_set_parent_realize(dc, nios2_cpu_realizefn, &ncc->parent_realize); - dc->props = nios2_properties; - ncc->parent_reset = cc->reset; - cc->reset = nios2_cpu_reset; + device_class_set_props(dc, nios2_properties); + device_class_set_parent_reset(dc, nios2_cpu_reset, &ncc->parent_reset); cc->class_by_name = nios2_cpu_class_by_name; cc->has_work = nios2_cpu_has_work; diff --git a/target/nios2/cpu.h b/target/nios2/cpu.h index 361b06ffeb..4dddf9c3a1 100644 --- a/target/nios2/cpu.h +++ b/target/nios2/cpu.h @@ -50,7 +50,7 @@ typedef struct Nios2CPUClass { /*< public >*/ DeviceRealize parent_realize; - void (*parent_reset)(CPUState *cpu); + DeviceReset parent_reset; } Nios2CPUClass; #define TARGET_HAS_ICE 1 @@ -217,8 +217,6 @@ void do_nios2_semihosting(CPUNios2State *env); #define CPU_SAVE_VERSION 1 /* MMU modes definitions */ -#define MMU_MODE0_SUFFIX _kernel -#define MMU_MODE1_SUFFIX _user #define MMU_SUPERVISOR_IDX 0 #define MMU_USER_IDX 1 diff --git a/target/nios2/translate.c b/target/nios2/translate.c index e17656e66f..6c34cd3193 100644 --- a/target/nios2/translate.c +++ b/target/nios2/translate.c @@ -23,7 +23,7 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "tcg-op.h" +#include "tcg/tcg-op.h" #include "exec/exec-all.h" #include "disas/disas.h" #include "exec/helper-proto.h" @@ -892,11 +892,11 @@ void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int max_insns) #ifdef DEBUG_DISAS if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM) && qemu_log_in_addr_range(tb->pc)) { - qemu_log_lock(); + FILE *logfile = qemu_log_lock(); qemu_log("IN: %s\n", lookup_symbol(tb->pc)); log_target_disas(cs, tb->pc, dc->pc - tb->pc); qemu_log("\n"); - qemu_log_unlock(); + qemu_log_unlock(logfile); } #endif } diff --git a/target/openrisc/cpu.c b/target/openrisc/cpu.c index 506aec6bfb..5528c0918f 100644 --- a/target/openrisc/cpu.c +++ b/target/openrisc/cpu.c @@ -41,13 +41,13 @@ static void openrisc_disas_set_info(CPUState *cpu, disassemble_info *info) info->print_insn = print_insn_or1k; } -/* CPUClass::reset() */ -static void openrisc_cpu_reset(CPUState *s) +static void openrisc_cpu_reset(DeviceState *dev) { + CPUState *s = CPU(dev); OpenRISCCPU *cpu = OPENRISC_CPU(s); OpenRISCCPUClass *occ = OPENRISC_CPU_GET_CLASS(cpu); - occ->parent_reset(s); + occ->parent_reset(dev); memset(&cpu->env, 0, offsetof(CPUOpenRISCState, end_reset_fields)); @@ -150,8 +150,7 @@ static void openrisc_cpu_class_init(ObjectClass *oc, void *data) device_class_set_parent_realize(dc, openrisc_cpu_realizefn, &occ->parent_realize); - occ->parent_reset = cc->reset; - cc->reset = openrisc_cpu_reset; + device_class_set_parent_reset(dc, openrisc_cpu_reset, &occ->parent_reset); cc->class_by_name = openrisc_cpu_class_by_name; cc->has_work = openrisc_cpu_has_work; diff --git a/target/openrisc/cpu.h b/target/openrisc/cpu.h index 0ad02eab79..f37a52e153 100644 --- a/target/openrisc/cpu.h +++ b/target/openrisc/cpu.h @@ -48,7 +48,7 @@ typedef struct OpenRISCCPUClass { /*< public >*/ DeviceRealize parent_realize; - void (*parent_reset)(CPUState *cpu); + DeviceReset parent_reset; } OpenRISCCPUClass; #define TARGET_INSN_START_EXTRA_WORDS 1 @@ -320,7 +320,7 @@ void openrisc_cpu_do_interrupt(CPUState *cpu); bool openrisc_cpu_exec_interrupt(CPUState *cpu, int int_req); void openrisc_cpu_dump_state(CPUState *cpu, FILE *f, int flags); hwaddr openrisc_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); -int openrisc_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); +int openrisc_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int openrisc_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); void openrisc_translate_init(void); bool openrisc_cpu_tlb_fill(CPUState *cs, vaddr address, int size, diff --git a/target/openrisc/fpu_helper.c b/target/openrisc/fpu_helper.c index 59e1413279..6f75ea0505 100644 --- a/target/openrisc/fpu_helper.c +++ b/target/openrisc/fpu_helper.c @@ -70,7 +70,7 @@ void cpu_set_fpcsr(CPUOpenRISCState *env, uint32_t val) float_round_down }; - env->fpcsr = val & 0x7ff; + env->fpcsr = val & 0xfff; set_float_rounding_mode(rm_to_sf[extract32(val, 1, 2)], &env->fp_status); } diff --git a/target/openrisc/gdbstub.c b/target/openrisc/gdbstub.c index 0fcdb79668..095bf76c12 100644 --- a/target/openrisc/gdbstub.c +++ b/target/openrisc/gdbstub.c @@ -21,7 +21,7 @@ #include "cpu.h" #include "exec/gdbstub.h" -int openrisc_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +int openrisc_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) { OpenRISCCPU *cpu = OPENRISC_CPU(cs); CPUOpenRISCState *env = &cpu->env; diff --git a/target/openrisc/translate.c b/target/openrisc/translate.c index 8dd28d6cf1..52323a16df 100644 --- a/target/openrisc/translate.c +++ b/target/openrisc/translate.c @@ -22,7 +22,7 @@ #include "cpu.h" #include "exec/exec-all.h" #include "disas/disas.h" -#include "tcg-op.h" +#include "tcg/tcg-op.h" #include "qemu/log.h" #include "qemu/bitops.h" #include "qemu/qemu-print.h" diff --git a/target/ppc/compat.c b/target/ppc/compat.c index 7de4bf3122..f48df25944 100644 --- a/target/ppc/compat.c +++ b/target/ppc/compat.c @@ -51,36 +51,38 @@ static const CompatInfo compat_table[] = { { /* POWER6, ISA2.05 */ .name = "power6", .pvr = CPU_POWERPC_LOGICAL_2_05, - .pcr = PCR_COMPAT_3_00 | PCR_COMPAT_2_07 | PCR_COMPAT_2_06 | - PCR_COMPAT_2_05 | PCR_TM_DIS | PCR_VSX_DIS, + .pcr = PCR_COMPAT_3_10 | PCR_COMPAT_3_00 | PCR_COMPAT_2_07 | + PCR_COMPAT_2_06 | PCR_COMPAT_2_05 | PCR_TM_DIS | PCR_VSX_DIS, .pcr_level = PCR_COMPAT_2_05, .max_vthreads = 2, }, { /* POWER7, ISA2.06 */ .name = "power7", .pvr = CPU_POWERPC_LOGICAL_2_06, - .pcr = PCR_COMPAT_3_00 | PCR_COMPAT_2_07 | PCR_COMPAT_2_06 | PCR_TM_DIS, + .pcr = PCR_COMPAT_3_10 | PCR_COMPAT_3_00 | PCR_COMPAT_2_07 | + PCR_COMPAT_2_06 | PCR_TM_DIS, .pcr_level = PCR_COMPAT_2_06, .max_vthreads = 4, }, { .name = "power7+", .pvr = CPU_POWERPC_LOGICAL_2_06_PLUS, - .pcr = PCR_COMPAT_3_00 | PCR_COMPAT_2_07 | PCR_COMPAT_2_06 | PCR_TM_DIS, + .pcr = PCR_COMPAT_3_10 | PCR_COMPAT_3_00 | PCR_COMPAT_2_07 | + PCR_COMPAT_2_06 | PCR_TM_DIS, .pcr_level = PCR_COMPAT_2_06, .max_vthreads = 4, }, { /* POWER8, ISA2.07 */ .name = "power8", .pvr = CPU_POWERPC_LOGICAL_2_07, - .pcr = PCR_COMPAT_3_00 | PCR_COMPAT_2_07, + .pcr = PCR_COMPAT_3_10 | PCR_COMPAT_3_00 | PCR_COMPAT_2_07, .pcr_level = PCR_COMPAT_2_07, .max_vthreads = 8, }, { /* POWER9, ISA3.00 */ .name = "power9", .pvr = CPU_POWERPC_LOGICAL_3_00, - .pcr = PCR_COMPAT_3_00, + .pcr = PCR_COMPAT_3_10 | PCR_COMPAT_3_00, .pcr_level = PCR_COMPAT_3_00, /* * POWER9 hardware only supports 4 threads / core, but this @@ -91,6 +93,13 @@ static const CompatInfo compat_table[] = { */ .max_vthreads = 8, }, + { /* POWER10, ISA3.10 */ + .name = "power10", + .pvr = CPU_POWERPC_LOGICAL_3_10, + .pcr = PCR_COMPAT_3_10, + .pcr_level = PCR_COMPAT_3_10, + .max_vthreads = 8, + }, }; static const CompatInfo *compat_by_pvr(uint32_t pvr) diff --git a/target/ppc/cpu-models.c b/target/ppc/cpu-models.c index 086548e9b9..4ad16863c0 100644 --- a/target/ppc/cpu-models.c +++ b/target/ppc/cpu-models.c @@ -774,6 +774,8 @@ "POWER9 v1.0") POWERPC_DEF("power9_v2.0", CPU_POWERPC_POWER9_DD20, POWER9, "POWER9 v2.0") + POWERPC_DEF("power10_v1.0", CPU_POWERPC_POWER10_DD1, POWER10, + "POWER10 v1.0") #endif /* defined (TARGET_PPC64) */ /***************************************************************************/ @@ -950,6 +952,7 @@ PowerPCCPUAlias ppc_cpu_aliases[] = { { "power8", "power8_v2.0" }, { "power8nvl", "power8nvl_v1.0" }, { "power9", "power9_v2.0" }, + { "power10", "power10_v1.0" }, #endif /* Generic PowerPCs */ diff --git a/target/ppc/cpu-models.h b/target/ppc/cpu-models.h index 4fdb73034d..ce750b2d55 100644 --- a/target/ppc/cpu-models.h +++ b/target/ppc/cpu-models.h @@ -373,6 +373,8 @@ enum { CPU_POWERPC_POWER9_BASE = 0x004E0000, CPU_POWERPC_POWER9_DD1 = 0x004E0100, CPU_POWERPC_POWER9_DD20 = 0x004E1200, + CPU_POWERPC_POWER10_BASE = 0x00800000, + CPU_POWERPC_POWER10_DD1 = 0x00800100, CPU_POWERPC_970_v22 = 0x00390202, CPU_POWERPC_970FX_v10 = 0x00391100, CPU_POWERPC_970FX_v20 = 0x003C0200, @@ -409,6 +411,7 @@ enum { CPU_POWERPC_LOGICAL_2_06_PLUS = 0x0F100003, CPU_POWERPC_LOGICAL_2_07 = 0x0F000004, CPU_POWERPC_LOGICAL_3_00 = 0x0F000005, + CPU_POWERPC_LOGICAL_3_10 = 0x0F000006, }; /* System version register (used on MPC 8xxx) */ diff --git a/target/ppc/cpu-qom.h b/target/ppc/cpu-qom.h index e499575dc8..000c7d405b 100644 --- a/target/ppc/cpu-qom.h +++ b/target/ppc/cpu-qom.h @@ -166,7 +166,7 @@ typedef struct PowerPCCPUClass { DeviceRealize parent_realize; DeviceUnrealize parent_unrealize; - void (*parent_reset)(CPUState *cpu); + DeviceReset parent_reset; void (*parent_parse_features)(const char *type, char *str, Error **errp); uint32_t pvr; @@ -177,6 +177,7 @@ typedef struct PowerPCCPUClass { uint64_t insns_flags; uint64_t insns_flags2; uint64_t msr_mask; + uint64_t lpcr_mask; /* Available bits in the LPCR */ uint64_t lpcr_pm; /* Power-saving mode Exit Cause Enable bits */ powerpc_mmu_t mmu_model; powerpc_excp_t excp_model; diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index e3e82327b7..88d9449555 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -23,10 +23,6 @@ #include "qemu/int128.h" #include "exec/cpu-defs.h" #include "cpu-qom.h" -#include "exec/cpu-defs.h" -#include "cpu-qom.h" - -/* #define PPC_EMULATE_32BITS_HYPV */ #define TCG_GUEST_DEFAULT_MO 0 @@ -180,7 +176,7 @@ enum { POWERPC_EXCP_TRAP = 0x40, }; -#define PPC_INPUT(env) (env->bus_model) +#define PPC_INPUT(env) ((env)->bus_model) /*****************************************************************************/ typedef struct opc_handler_t opc_handler_t; @@ -302,13 +298,12 @@ typedef struct ppc_v3_pate_t { #define MSR_SF 63 /* Sixty-four-bit mode hflags */ #define MSR_TAG 62 /* Tag-active mode (POWERx ?) */ #define MSR_ISF 61 /* Sixty-four-bit interrupt mode on 630 */ -#define MSR_SHV 60 /* hypervisor state hflags */ +#define MSR_HV 60 /* hypervisor state hflags */ #define MSR_TS0 34 /* Transactional state, 2 bits (Book3s) */ #define MSR_TS1 33 #define MSR_TM 32 /* Transactional Memory Available (Book3s) */ #define MSR_CM 31 /* Computation mode for BookE hflags */ #define MSR_ICM 30 /* Interrupt computation mode for BookE */ -#define MSR_THV 29 /* hypervisor state for 32 bits PowerPC hflags */ #define MSR_GS 28 /* guest state for BookE */ #define MSR_UCLE 26 /* User-mode cache lock enable for BookE */ #define MSR_VR 25 /* altivec available x hflags */ @@ -397,12 +392,19 @@ typedef struct ppc_v3_pate_t { #define PSSCR_ESL PPC_BIT(42) /* Enable State Loss */ #define PSSCR_EC PPC_BIT(43) /* Exit Criterion */ +/* HFSCR bits */ +#define HFSCR_MSGP PPC_BIT(53) /* Privileged Message Send Facilities */ +#define HFSCR_IC_MSGP 0xA + #define msr_sf ((env->msr >> MSR_SF) & 1) #define msr_isf ((env->msr >> MSR_ISF) & 1) -#define msr_shv ((env->msr >> MSR_SHV) & 1) +#if defined(TARGET_PPC64) +#define msr_hv ((env->msr >> MSR_HV) & 1) +#else +#define msr_hv (0) +#endif #define msr_cm ((env->msr >> MSR_CM) & 1) #define msr_icm ((env->msr >> MSR_ICM) & 1) -#define msr_thv ((env->msr >> MSR_THV) & 1) #define msr_gs ((env->msr >> MSR_GS) & 1) #define msr_ucle ((env->msr >> MSR_UCLE) & 1) #define msr_vr ((env->msr >> MSR_VR) & 1) @@ -447,16 +449,9 @@ typedef struct ppc_v3_pate_t { /* Hypervisor bit is more specific */ #if defined(TARGET_PPC64) -#define MSR_HVB (1ULL << MSR_SHV) -#define msr_hv msr_shv -#else -#if defined(PPC_EMULATE_32BITS_HYPV) -#define MSR_HVB (1ULL << MSR_THV) -#define msr_hv msr_thv +#define MSR_HVB (1ULL << MSR_HV) #else #define MSR_HVB (0ULL) -#define msr_hv (0) -#endif #endif /* DSISR */ @@ -951,8 +946,6 @@ struct ppc_radix_page_info { * + real/paged mode combinations. The other two modes are for * external PID load/store. */ -#define MMU_MODE8_SUFFIX _epl -#define MMU_MODE9_SUFFIX _eps #define PPC_TLB_EPID_LOAD 8 #define PPC_TLB_EPID_STORE 9 @@ -960,117 +953,88 @@ struct ppc_radix_page_info { #define PPC_CPU_INDIRECT_OPCODES_LEN 0x20 struct CPUPPCState { - /* - * First are the most commonly used resources during translated - * code execution - */ - /* general purpose registers */ - target_ulong gpr[32]; - /* Storage for GPR MSB, used by the SPE extension */ - target_ulong gprh[32]; - /* LR */ + /* Most commonly used resources during translated code execution first */ + target_ulong gpr[32]; /* general purpose registers */ + target_ulong gprh[32]; /* storage for GPR MSB, used by the SPE extension */ target_ulong lr; - /* CTR */ target_ulong ctr; - /* condition register */ - uint32_t crf[8]; + uint32_t crf[8]; /* condition register */ #if defined(TARGET_PPC64) - /* CFAR */ target_ulong cfar; #endif - /* XER (with SO, OV, CA split out) */ - target_ulong xer; + target_ulong xer; /* XER (with SO, OV, CA split out) */ target_ulong so; target_ulong ov; target_ulong ca; target_ulong ov32; target_ulong ca32; - /* Reservation address */ - target_ulong reserve_addr; - /* Reservation value */ - target_ulong reserve_val; + + target_ulong reserve_addr; /* Reservation address */ + target_ulong reserve_val; /* Reservation value */ target_ulong reserve_val2; - /* Those ones are used in supervisor mode only */ - /* machine state register */ - target_ulong msr; - /* temporary general purpose registers */ - target_ulong tgpr[4]; /* Used to speed-up TLB assist handlers */ + /* These are used in supervisor mode only */ + target_ulong msr; /* machine state register */ + target_ulong tgpr[4]; /* temporary general purpose registers, */ + /* used to speed-up TLB assist handlers */ - /* Floating point execution context */ - float_status fp_status; - /* floating point status and control register */ - target_ulong fpscr; - - /* Next instruction pointer */ - target_ulong nip; - - /* High part of 128-bit helper return. */ - uint64_t retxh; + target_ulong nip; /* next instruction pointer */ + uint64_t retxh; /* high part of 128-bit helper return */ /* when a memory exception occurs, the access type is stored here */ int access_type; - /* MMU context - only relevant for full system emulation */ #if !defined(CONFIG_USER_ONLY) + /* MMU context, only relevant for full system emulation */ #if defined(TARGET_PPC64) - /* PowerPC 64 SLB area */ - ppc_slb_t slb[MAX_SLB_ENTRIES]; - /* tcg TLB needs flush (deferred slb inval instruction typically) */ + ppc_slb_t slb[MAX_SLB_ENTRIES]; /* PowerPC 64 SLB area */ #endif - /* segment registers */ - target_ulong sr[32]; - /* BATs */ - uint32_t nb_BATs; + target_ulong sr[32]; /* segment registers */ + uint32_t nb_BATs; /* number of BATs */ target_ulong DBAT[2][8]; target_ulong IBAT[2][8]; /* PowerPC TLB registers (for 4xx, e500 and 60x software driven TLBs) */ - int32_t nb_tlb; /* Total number of TLB */ + int32_t nb_tlb; /* Total number of TLB */ int tlb_per_way; /* Speed-up helper: used to avoid divisions at run time */ - int nb_ways; /* Number of ways in the TLB set */ - int last_way; /* Last used way used to allocate TLB in a LRU way */ + int nb_ways; /* Number of ways in the TLB set */ + int last_way; /* Last used way used to allocate TLB in a LRU way */ int id_tlbs; /* If 1, MMU has separated TLBs for instructions & data */ - int nb_pids; /* Number of available PID registers */ - int tlb_type; /* Type of TLB we're dealing with */ - ppc_tlb_t tlb; /* TLB is optional. Allocate them only if needed */ - /* 403 dedicated access protection registers */ - target_ulong pb[4]; - bool tlb_dirty; /* Set to non-zero when modifying TLB */ - bool kvm_sw_tlb; /* non-zero if KVM SW TLB API is active */ + int nb_pids; /* Number of available PID registers */ + int tlb_type; /* Type of TLB we're dealing with */ + ppc_tlb_t tlb; /* TLB is optional. Allocate them only if needed */ + target_ulong pb[4]; /* 403 dedicated access protection registers */ + bool tlb_dirty; /* Set to non-zero when modifying TLB */ + bool kvm_sw_tlb; /* non-zero if KVM SW TLB API is active */ uint32_t tlb_need_flush; /* Delayed flush needed */ #define TLB_NEED_LOCAL_FLUSH 0x1 #define TLB_NEED_GLOBAL_FLUSH 0x2 #endif /* Other registers */ - /* Special purpose registers */ - target_ulong spr[1024]; + target_ulong spr[1024]; /* special purpose registers */ ppc_spr_t spr_cb[1024]; - /* Vector status and control register, minus VSCR_SAT. */ + /* Vector status and control register, minus VSCR_SAT */ uint32_t vscr; /* VSX registers (including FP and AVR) */ ppc_vsr_t vsr[64] QEMU_ALIGNED(16); - /* Non-zero if and only if VSCR_SAT should be set. */ + /* Non-zero if and only if VSCR_SAT should be set */ ppc_vsr_t vscr_sat QEMU_ALIGNED(16); /* SPE registers */ uint64_t spe_acc; uint32_t spe_fscr; - /* - * SPE and Altivec can share a status since they will never be - * used simultaneously - */ + /* SPE and Altivec share status as they'll never be used simultaneously */ float_status vec_status; + float_status fp_status; /* Floating point execution context */ + target_ulong fpscr; /* Floating point status and control register */ /* Internal devices resources */ - /* Time base and decrementer */ - ppc_tb_t *tb_env; - /* Device control registers */ - ppc_dcr_t *dcr_env; + ppc_tb_t *tb_env; /* Time base and decrementer */ + ppc_dcr_t *dcr_env; /* Device control registers */ int dcache_line_size; int icache_line_size; - /* Those resources are used during exception processing */ + /* These resources are used during exception processing */ /* CPU model definition */ target_ulong msr_mask; powerpc_mmu_t mmu_model; @@ -1080,65 +1044,54 @@ struct CPUPPCState { uint32_t flags; uint64_t insns_flags; uint64_t insns_flags2; -#if defined(TARGET_PPC64) - ppc_slb_t vrma_slb; - target_ulong rmls; -#endif int error_code; uint32_t pending_interrupts; #if !defined(CONFIG_USER_ONLY) /* - * This is the IRQ controller, which is implementation dependent - * and only relevant when emulating a complete machine. + * This is the IRQ controller, which is implementation dependent and only + * relevant when emulating a complete machine. Note that this isn't used + * by recent Book3s compatible CPUs (POWER7 and newer). */ uint32_t irq_input_state; void **irq_inputs; - /* Exception vectors */ - target_ulong excp_vectors[POWERPC_EXCP_NB]; + + target_ulong excp_vectors[POWERPC_EXCP_NB]; /* Exception vectors */ target_ulong excp_prefix; target_ulong ivor_mask; target_ulong ivpr_mask; target_ulong hreset_vector; hwaddr mpic_iack; - /* true when the external proxy facility mode is enabled */ - bool mpic_proxy; + bool mpic_proxy; /* true if the external proxy facility mode is enabled */ + bool has_hv_mode; /* set when the processor has an HV mode, thus HV priv */ + /* instructions and SPRs are diallowed if MSR:HV is 0 */ /* - * set when the processor has an HV mode, thus HV priv - * instructions and SPRs are diallowed if MSR:HV is 0 - */ - bool has_hv_mode; - - /* - * On P7/P8/P9, set when in PM state, we need to handle resume in - * a special way (such as routing some resume causes to 0x100, ie, - * sreset), so flag this here. + * On P7/P8/P9, set when in PM state so we need to handle resume in a + * special way (such as routing some resume causes to 0x100, i.e. sreset). */ bool resume_as_sreset; #endif - /* Those resources are used only in QEMU core */ - target_ulong hflags; /* hflags is a MSR & HFLAGS_MASK */ + /* These resources are used only in QEMU core */ + target_ulong hflags; /* hflags is MSR & HFLAGS_MASK */ target_ulong hflags_nmsr; /* specific hflags, not coming from MSR */ - int immu_idx; /* precomputed MMU index to speed up insn access */ - int dmmu_idx; /* precomputed MMU index to speed up data accesses */ + int immu_idx; /* precomputed MMU index to speed up insn accesses */ + int dmmu_idx; /* precomputed MMU index to speed up data accesses */ /* Power management */ int (*check_pow)(CPUPPCState *env); #if !defined(CONFIG_USER_ONLY) - void *load_info; /* Holds boot loading state. */ + void *load_info; /* holds boot loading state */ #endif /* booke timers */ /* - * Specifies bit locations of the Time Base used to signal a fixed - * timer exception on a transition from 0 to 1. (watchdog or - * fixed-interval timer) + * Specifies bit locations of the Time Base used to signal a fixed timer + * exception on a transition from 0 to 1 (watchdog or fixed-interval timer) * - * 0 selects the least significant bit. - * 63 selects the most significant bit. + * 0 selects the least significant bit, 63 selects the most significant bit */ uint8_t fit_period[4]; uint8_t wdt_period[4]; @@ -1220,10 +1173,6 @@ PowerPCCPUClass *ppc_cpu_class_by_pvr(uint32_t pvr); PowerPCCPUClass *ppc_cpu_class_by_pvr_mask(uint32_t pvr); PowerPCCPUClass *ppc_cpu_get_family_class(PowerPCCPUClass *pcc); -struct PPCVirtualHypervisor { - Object parent; -}; - struct PPCVirtualHypervisorClass { InterfaceClass parent; void (*hypercall)(PPCVirtualHypervisor *vhyp, PowerPCCPU *cpu); @@ -1258,8 +1207,8 @@ bool ppc_cpu_exec_interrupt(CPUState *cpu, int int_req); void ppc_cpu_dump_state(CPUState *cpu, FILE *f, int flags); void ppc_cpu_dump_statistics(CPUState *cpu, int flags); hwaddr ppc_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); -int ppc_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); -int ppc_cpu_gdb_read_register_apple(CPUState *cpu, uint8_t *buf, int reg); +int ppc_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); +int ppc_cpu_gdb_read_register_apple(CPUState *cpu, GByteArray *buf, int reg); int ppc_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); int ppc_cpu_gdb_write_register_apple(CPUState *cpu, uint8_t *buf, int reg); #ifndef CONFIG_USER_ONLY @@ -1271,7 +1220,8 @@ int ppc64_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs, int ppc32_cpu_write_elf32_note(WriteCoreDumpFunction f, CPUState *cs, int cpuid, void *opaque); #ifndef CONFIG_USER_ONLY -void ppc_cpu_do_system_reset(CPUState *cs); +void ppc_cpu_do_system_reset(CPUState *cs, target_ulong vector); +void ppc_cpu_do_fwnmi_machine_check(CPUState *cs, target_ulong vector); extern const VMStateDescription vmstate_ppc_cpu; #endif @@ -1305,12 +1255,16 @@ uint64_t cpu_ppc_load_atbl(CPUPPCState *env); uint32_t cpu_ppc_load_atbu(CPUPPCState *env); void cpu_ppc_store_atbl(CPUPPCState *env, uint32_t value); void cpu_ppc_store_atbu(CPUPPCState *env, uint32_t value); +uint64_t cpu_ppc_load_vtb(CPUPPCState *env); +void cpu_ppc_store_vtb(CPUPPCState *env, uint64_t value); bool ppc_decr_clear_on_delivery(CPUPPCState *env); target_ulong cpu_ppc_load_decr(CPUPPCState *env); void cpu_ppc_store_decr(CPUPPCState *env, target_ulong value); target_ulong cpu_ppc_load_hdecr(CPUPPCState *env); void cpu_ppc_store_hdecr(CPUPPCState *env, target_ulong value); +void cpu_ppc_store_tbu40(CPUPPCState *env, uint64_t value); uint64_t cpu_ppc_load_purr(CPUPPCState *env); +void cpu_ppc_store_purr(CPUPPCState *env, uint64_t value); uint32_t cpu_ppc601_load_rtcl(CPUPPCState *env); uint32_t cpu_ppc601_load_rtcu(CPUPPCState *env); #if !defined(CONFIG_USER_ONLY) @@ -1329,6 +1283,8 @@ void cpu_ppc_set_vhyp(PowerPCCPU *cpu, PPCVirtualHypervisor *vhyp); #endif void store_fpscr(CPUPPCState *env, uint64_t arg, uint32_t mask); +void helper_hfscr_facility_check(CPUPPCState *env, uint32_t bit, + const char *caller, uint32_t cause); static inline uint64_t ppc_dump_gpr(CPUPPCState *env, int gprn) { @@ -1777,6 +1733,7 @@ typedef PowerPCCPU ArchCPU; #define SPR_MPC_MD_DBRAM1 (0x32A) #define SPR_RCPU_L2U_RA3 (0x32B) #define SPR_TAR (0x32F) +#define SPR_ASDR (0x330) #define SPR_IC (0x350) #define SPR_VTB (0x351) #define SPR_MMCRC (0x353) @@ -2366,6 +2323,7 @@ enum { PCR_COMPAT_2_06 = PPC_BIT(61), PCR_COMPAT_2_07 = PPC_BIT(60), PCR_COMPAT_3_00 = PPC_BIT(59), + PCR_COMPAT_3_10 = PPC_BIT(58), PCR_VEC_DIS = PPC_BIT(0), /* Vec. disable (bit NA since POWER8) */ PCR_VSX_DIS = PPC_BIT(1), /* VSX disable (bit NA since POWER8) */ PCR_TM_DIS = PPC_BIT(2), /* Trans. memory disable (POWER8) */ diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index 50b004d00d..08bc885ca6 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -112,6 +112,8 @@ static uint64_t ppc_excp_vector_offset(CPUState *cs, int ail) uint64_t offset = 0; switch (ail) { + case AIL_NONE: + break; case AIL_0001_8000: offset = 0x18000; break; @@ -126,6 +128,37 @@ static uint64_t ppc_excp_vector_offset(CPUState *cs, int ail) return offset; } +static inline void powerpc_set_excp_state(PowerPCCPU *cpu, + target_ulong vector, target_ulong msr) +{ + CPUState *cs = CPU(cpu); + CPUPPCState *env = &cpu->env; + + /* + * We don't use hreg_store_msr here as already have treated any + * special case that could occur. Just store MSR and update hflags + * + * Note: We *MUST* not use hreg_store_msr() as-is anyway because it + * will prevent setting of the HV bit which some exceptions might need + * to do. + */ + env->msr = msr & env->msr_mask; + hreg_compute_hflags(env); + env->nip = vector; + /* Reset exception state */ + cs->exception_index = POWERPC_EXCP_NONE; + env->error_code = 0; + + /* Reset the reservation */ + env->reserve_addr = -1; + + /* + * Any interrupt is context synchronizing, check if TCG TLB needs + * a delayed flush on ppc64 + */ + check_tlb_flush(env, false); +} + /* * Note that this function should be greatly optimized when called * with a constant excp, from ppc_hw_interrupt @@ -469,6 +502,15 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) case POWERPC_EXCP_FU: /* Facility unavailable exception */ #ifdef TARGET_PPC64 env->spr[SPR_FSCR] |= ((target_ulong)env->error_code << 56); +#endif + break; + case POWERPC_EXCP_HV_FU: /* Hypervisor Facility Unavailable Exception */ +#ifdef TARGET_PPC64 + env->spr[SPR_HFSCR] |= ((target_ulong)env->error_code << FSCR_IC_POS); + srr0 = SPR_HSRR0; + srr1 = SPR_HSRR1; + new_msr |= (target_ulong)MSR_HVB; + new_msr |= env->msr & ((target_ulong)1 << MSR_RI); #endif break; case POWERPC_EXCP_PIT: /* Programmable interval timer interrupt */ @@ -757,29 +799,8 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) } } #endif - /* - * We don't use hreg_store_msr here as already have treated any - * special case that could occur. Just store MSR and update hflags - * - * Note: We *MUST* not use hreg_store_msr() as-is anyway because it - * will prevent setting of the HV bit which some exceptions might need - * to do. - */ - env->msr = new_msr & env->msr_mask; - hreg_compute_hflags(env); - env->nip = vector; - /* Reset exception state */ - cs->exception_index = POWERPC_EXCP_NONE; - env->error_code = 0; - /* Reset the reservation */ - env->reserve_addr = -1; - - /* - * Any interrupt is context synchronizing, check if TCG TLB needs - * a delayed flush on ppc64 - */ - check_tlb_flush(env, false); + powerpc_set_excp_state(cpu, vector, new_msr); } void ppc_cpu_do_interrupt(CPUState *cs) @@ -898,7 +919,11 @@ static void ppc_hw_interrupt(CPUPPCState *env) } if (env->pending_interrupts & (1 << PPC_INTERRUPT_DOORBELL)) { env->pending_interrupts &= ~(1 << PPC_INTERRUPT_DOORBELL); - powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_DOORI); + if (is_book3s_arch2x(env)) { + powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_SDOOR); + } else { + powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_DOORI); + } return; } if (env->pending_interrupts & (1 << PPC_INTERRUPT_HDOORBELL)) { @@ -936,12 +961,35 @@ static void ppc_hw_interrupt(CPUPPCState *env) } } -void ppc_cpu_do_system_reset(CPUState *cs) +void ppc_cpu_do_system_reset(CPUState *cs, target_ulong vector) { PowerPCCPU *cpu = POWERPC_CPU(cs); CPUPPCState *env = &cpu->env; powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_RESET); + if (vector != -1) { + env->nip = vector; + } +} + +void ppc_cpu_do_fwnmi_machine_check(CPUState *cs, target_ulong vector) +{ + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; + PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); + target_ulong msr = 0; + + /* + * Set MSR and NIP for the handler, SRR0/1, DAR and DSISR have already + * been set by KVM. + */ + msr = (1ULL << MSR_ME); + msr |= env->msr & (1ULL << MSR_SF); + if (!(*pcc->interrupts_big_endian)(cpu)) { + msr |= (1ULL << MSR_LE); + } + + powerpc_set_excp_state(cpu, vector, msr); } #endif /* !CONFIG_USER_ONLY */ @@ -1219,39 +1267,30 @@ void helper_msgsnd(target_ulong rb) } /* Server Processor Control */ -static int book3s_dbell2irq(target_ulong rb) -{ - int msg = rb & DBELL_TYPE_MASK; +static bool dbell_type_server(target_ulong rb) +{ /* * A Directed Hypervisor Doorbell message is sent only if the * message type is 5. All other types are reserved and the * instruction is a no-op */ - return msg == DBELL_TYPE_DBELL_SERVER ? PPC_INTERRUPT_HDOORBELL : -1; + return (rb & DBELL_TYPE_MASK) == DBELL_TYPE_DBELL_SERVER; } void helper_book3s_msgclr(CPUPPCState *env, target_ulong rb) { - int irq = book3s_dbell2irq(rb); - - if (irq < 0) { + if (!dbell_type_server(rb)) { return; } - env->pending_interrupts &= ~(1 << irq); + env->pending_interrupts &= ~(1 << PPC_INTERRUPT_HDOORBELL); } -void helper_book3s_msgsnd(target_ulong rb) +static void book3s_msgsnd_common(int pir, int irq) { - int irq = book3s_dbell2irq(rb); - int pir = rb & DBELL_PROCIDTAG_MASK; CPUState *cs; - if (irq < 0) { - return; - } - qemu_mutex_lock_iothread(); CPU_FOREACH(cs) { PowerPCCPU *cpu = POWERPC_CPU(cs); @@ -1265,6 +1304,49 @@ void helper_book3s_msgsnd(target_ulong rb) } qemu_mutex_unlock_iothread(); } + +void helper_book3s_msgsnd(target_ulong rb) +{ + int pir = rb & DBELL_PROCIDTAG_MASK; + + if (!dbell_type_server(rb)) { + return; + } + + book3s_msgsnd_common(pir, PPC_INTERRUPT_HDOORBELL); +} + +#if defined(TARGET_PPC64) +void helper_book3s_msgclrp(CPUPPCState *env, target_ulong rb) +{ + helper_hfscr_facility_check(env, HFSCR_MSGP, "msgclrp", HFSCR_IC_MSGP); + + if (!dbell_type_server(rb)) { + return; + } + + env->pending_interrupts &= ~(1 << PPC_INTERRUPT_DOORBELL); +} + +/* + * sends a message to other threads that are on the same + * multi-threaded processor + */ +void helper_book3s_msgsndp(CPUPPCState *env, target_ulong rb) +{ + int pir = env->spr_cb[SPR_PIR].default_value; + + helper_hfscr_facility_check(env, HFSCR_MSGP, "msgsndp", HFSCR_IC_MSGP); + + if (!dbell_type_server(rb)) { + return; + } + + /* TODO: TCG supports only one thread */ + + book3s_msgsnd_common(pir, PPC_INTERRUPT_DOORBELL); +} +#endif #endif void ppc_cpu_do_unaligned_access(CPUState *cs, vaddr vaddr, diff --git a/target/ppc/fpu_helper.c b/target/ppc/fpu_helper.c index dc383242f7..ae43b08eb5 100644 --- a/target/ppc/fpu_helper.c +++ b/target/ppc/fpu_helper.c @@ -293,7 +293,7 @@ static void float_invalid_op_vxvc(CPUPPCState *env, bool set_fpcc, env->error_code = POWERPC_EXCP_FP | POWERPC_EXCP_FP_VXVC; /* Update the floating-point enabled exception summary */ env->fpscr |= FP_FEX; - /* Exception is differed */ + /* Exception is deferred */ } } @@ -644,7 +644,7 @@ static void do_float_check_status(CPUPPCState *env, uintptr_t raddr) if (cs->exception_index == POWERPC_EXCP_PROGRAM && (env->error_code & POWERPC_EXCP_FP)) { - /* Differred floating-point exception after target FPR update */ + /* Deferred floating-point exception after target FPR update */ if (fp_exceptions_enabled(env)) { raise_exception_err_ra(env, cs->exception_index, env->error_code, raddr); diff --git a/target/ppc/gdbstub.c b/target/ppc/gdbstub.c index 823759c92e..eb362dd9ae 100644 --- a/target/ppc/gdbstub.c +++ b/target/ppc/gdbstub.c @@ -114,10 +114,11 @@ void ppc_maybe_bswap_register(CPUPPCState *env, uint8_t *mem_buf, int len) * the FP regs zero size when talking to a newer gdb. */ -int ppc_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +int ppc_cpu_gdb_read_register(CPUState *cs, GByteArray *buf, int n) { PowerPCCPU *cpu = POWERPC_CPU(cs); CPUPPCState *env = &cpu->env; + uint8_t *mem_buf; int r = ppc_gdb_register_len(n); if (!r) { @@ -126,17 +127,17 @@ int ppc_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) if (n < 32) { /* gprs */ - gdb_get_regl(mem_buf, env->gpr[n]); + gdb_get_regl(buf, env->gpr[n]); } else if (n < 64) { /* fprs */ - stfq_p(mem_buf, *cpu_fpr_ptr(env, n - 32)); + gdb_get_reg64(buf, *cpu_fpr_ptr(env, n - 32)); } else { switch (n) { case 64: - gdb_get_regl(mem_buf, env->nip); + gdb_get_regl(buf, env->nip); break; case 65: - gdb_get_regl(mem_buf, env->msr); + gdb_get_regl(buf, env->msr); break; case 66: { @@ -145,31 +146,33 @@ int ppc_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) for (i = 0; i < 8; i++) { cr |= env->crf[i] << (32 - ((i + 1) * 4)); } - gdb_get_reg32(mem_buf, cr); + gdb_get_reg32(buf, cr); break; } case 67: - gdb_get_regl(mem_buf, env->lr); + gdb_get_regl(buf, env->lr); break; case 68: - gdb_get_regl(mem_buf, env->ctr); + gdb_get_regl(buf, env->ctr); break; case 69: - gdb_get_reg32(mem_buf, env->xer); + gdb_get_reg32(buf, env->xer); break; case 70: - gdb_get_reg32(mem_buf, env->fpscr); + gdb_get_reg32(buf, env->fpscr); break; } } + mem_buf = buf->data + buf->len - r; ppc_maybe_bswap_register(env, mem_buf, r); return r; } -int ppc_cpu_gdb_read_register_apple(CPUState *cs, uint8_t *mem_buf, int n) +int ppc_cpu_gdb_read_register_apple(CPUState *cs, GByteArray *buf, int n) { PowerPCCPU *cpu = POWERPC_CPU(cs); CPUPPCState *env = &cpu->env; + uint8_t *mem_buf; int r = ppc_gdb_register_len_apple(n); if (!r) { @@ -178,21 +181,21 @@ int ppc_cpu_gdb_read_register_apple(CPUState *cs, uint8_t *mem_buf, int n) if (n < 32) { /* gprs */ - gdb_get_reg64(mem_buf, env->gpr[n]); + gdb_get_reg64(buf, env->gpr[n]); } else if (n < 64) { /* fprs */ - stfq_p(mem_buf, *cpu_fpr_ptr(env, n - 32)); + gdb_get_reg64(buf, *cpu_fpr_ptr(env, n - 32)); } else if (n < 96) { /* Altivec */ - stq_p(mem_buf, n - 64); - stq_p(mem_buf + 8, 0); + gdb_get_reg64(buf, n - 64); + gdb_get_reg64(buf, 0); } else { switch (n) { case 64 + 32: - gdb_get_reg64(mem_buf, env->nip); + gdb_get_reg64(buf, env->nip); break; case 65 + 32: - gdb_get_reg64(mem_buf, env->msr); + gdb_get_reg64(buf, env->msr); break; case 66 + 32: { @@ -201,23 +204,24 @@ int ppc_cpu_gdb_read_register_apple(CPUState *cs, uint8_t *mem_buf, int n) for (i = 0; i < 8; i++) { cr |= env->crf[i] << (32 - ((i + 1) * 4)); } - gdb_get_reg32(mem_buf, cr); + gdb_get_reg32(buf, cr); break; } case 67 + 32: - gdb_get_reg64(mem_buf, env->lr); + gdb_get_reg64(buf, env->lr); break; case 68 + 32: - gdb_get_reg64(mem_buf, env->ctr); + gdb_get_reg64(buf, env->ctr); break; case 69 + 32: - gdb_get_reg32(mem_buf, env->xer); + gdb_get_reg32(buf, env->xer); break; case 70 + 32: - gdb_get_reg64(mem_buf, env->fpscr); + gdb_get_reg64(buf, env->fpscr); break; } } + mem_buf = buf->data + buf->len - r; ppc_maybe_bswap_register(env, mem_buf, r); return r; } diff --git a/target/ppc/helper.h b/target/ppc/helper.h index f843814b8a..a95c010391 100644 --- a/target/ppc/helper.h +++ b/target/ppc/helper.h @@ -614,7 +614,7 @@ DEF_HELPER_FLAGS_3(store_slb, TCG_CALL_NO_RWG, void, env, tl, tl) DEF_HELPER_2(load_slb_esid, tl, env, tl) DEF_HELPER_2(load_slb_vsid, tl, env, tl) DEF_HELPER_2(find_slb_vsid, tl, env, tl) -DEF_HELPER_FLAGS_1(slbia, TCG_CALL_NO_RWG, void, env) +DEF_HELPER_FLAGS_2(slbia, TCG_CALL_NO_RWG, void, env, i32) DEF_HELPER_FLAGS_2(slbie, TCG_CALL_NO_RWG, void, env, tl) DEF_HELPER_FLAGS_2(slbieg, TCG_CALL_NO_RWG, void, env, tl) #endif @@ -649,12 +649,18 @@ DEF_HELPER_FLAGS_1(load_tbl, TCG_CALL_NO_RWG, tl, env) DEF_HELPER_FLAGS_1(load_tbu, TCG_CALL_NO_RWG, tl, env) DEF_HELPER_FLAGS_1(load_atbl, TCG_CALL_NO_RWG, tl, env) DEF_HELPER_FLAGS_1(load_atbu, TCG_CALL_NO_RWG, tl, env) +DEF_HELPER_FLAGS_1(load_vtb, TCG_CALL_NO_RWG, tl, env) DEF_HELPER_FLAGS_1(load_601_rtcl, TCG_CALL_NO_RWG, tl, env) DEF_HELPER_FLAGS_1(load_601_rtcu, TCG_CALL_NO_RWG, tl, env) #if !defined(CONFIG_USER_ONLY) #if defined(TARGET_PPC64) DEF_HELPER_FLAGS_1(load_purr, TCG_CALL_NO_RWG, tl, env) +DEF_HELPER_FLAGS_2(store_purr, TCG_CALL_NO_RWG, void, env, tl) DEF_HELPER_2(store_ptcr, void, env, tl) +DEF_HELPER_FLAGS_1(load_dpdes, TCG_CALL_NO_RWG, tl, env) +DEF_HELPER_FLAGS_2(store_dpdes, TCG_CALL_NO_RWG, void, env, tl) +DEF_HELPER_2(book3s_msgsndp, void, env, tl) +DEF_HELPER_2(book3s_msgclrp, void, env, tl) #endif DEF_HELPER_2(store_sdr1, void, env, tl) DEF_HELPER_2(store_pidr, void, env, tl) @@ -669,6 +675,8 @@ DEF_HELPER_FLAGS_1(load_decr, TCG_CALL_NO_RWG, tl, env) DEF_HELPER_FLAGS_2(store_decr, TCG_CALL_NO_RWG, void, env, tl) DEF_HELPER_FLAGS_1(load_hdecr, TCG_CALL_NO_RWG, tl, env) DEF_HELPER_FLAGS_2(store_hdecr, TCG_CALL_NO_RWG, void, env, tl) +DEF_HELPER_FLAGS_2(store_vtb, TCG_CALL_NO_RWG, void, env, tl) +DEF_HELPER_FLAGS_2(store_tbu40, TCG_CALL_NO_RWG, void, env, tl) DEF_HELPER_2(store_hid0_601, void, env, tl) DEF_HELPER_3(store_403_pbr, void, env, i32, tl) DEF_HELPER_FLAGS_1(load_40x_pit, TCG_CALL_NO_RWG, tl, env) diff --git a/target/ppc/helper_regs.h b/target/ppc/helper_regs.h index 85dfe7687f..d78c2af63e 100644 --- a/target/ppc/helper_regs.h +++ b/target/ppc/helper_regs.h @@ -22,6 +22,7 @@ #include "qemu/main-loop.h" #include "exec/exec-all.h" +#include "sysemu/kvm.h" /* Swap temporary saved registers with GPRs */ static inline void hreg_swap_gpr_tgpr(CPUPPCState *env) @@ -102,6 +103,10 @@ static inline void hreg_compute_hflags(CPUPPCState *env) static inline void cpu_interrupt_exittb(CPUState *cs) { + if (!kvm_enabled()) { + return; + } + if (!qemu_mutex_iothread_locked()) { qemu_mutex_lock_iothread(); cpu_interrupt(cs, CPU_INTERRUPT_EXITTB); diff --git a/target/ppc/kvm.c b/target/ppc/kvm.c index c77f9848ec..2692f76130 100644 --- a/target/ppc/kvm.c +++ b/target/ppc/kvm.c @@ -53,6 +53,9 @@ #define PROC_DEVTREE_CPU "/proc/device-tree/cpus/" +#define DEBUG_RETURN_GUEST 0 +#define DEBUG_RETURN_GDB 1 + const KVMCapabilityInfo kvm_arch_required_capabilities[] = { KVM_CAP_LAST_INFO }; @@ -85,6 +88,7 @@ static int cap_ppc_safe_indirect_branch; static int cap_ppc_count_cache_flush_assist; static int cap_ppc_nested_kvm_hv; static int cap_large_decr; +static int cap_fwnmi; static uint32_t debug_inst_opcode; @@ -133,6 +137,7 @@ int kvm_arch_init(MachineState *ms, KVMState *s) kvmppc_get_cpu_characteristics(s); cap_ppc_nested_kvm_hv = kvm_vm_check_extension(s, KVM_CAP_PPC_NESTED_HV); cap_large_decr = kvmppc_get_dec_bits(); + cap_fwnmi = kvm_vm_check_extension(s, KVM_CAP_PPC_FWNMI); /* * Note: setting it to false because there is not such capability * in KVM at this moment. @@ -152,7 +157,7 @@ int kvm_arch_init(MachineState *ms, KVMState *s) return 0; } -int kvm_arch_irqchip_create(MachineState *ms, KVMState *s) +int kvm_arch_irqchip_create(KVMState *s) { return 0; } @@ -258,7 +263,7 @@ static void kvm_get_smmu_info(struct kvm_ppc_smmu_info *info, Error **errp) struct ppc_radix_page_info *kvm_get_radix_page_info(void) { - KVMState *s = KVM_STATE(current_machine->accelerator); + KVMState *s = KVM_STATE(current_accel()); struct ppc_radix_page_info *radix_page_info; struct kvm_ppc_rmmu_info rmmu_info; int i; @@ -1325,12 +1330,6 @@ int kvmppc_set_interrupt(PowerPCCPU *cpu, int irq, int level) return 0; } -#if defined(TARGET_PPC64) -#define PPC_INPUT_INT PPC970_INPUT_INT -#else -#define PPC_INPUT_INT PPC6xx_INPUT_INT -#endif - void kvm_arch_pre_run(CPUState *cs, struct kvm_run *run) { return; @@ -1570,7 +1569,7 @@ void kvm_arch_update_guest_debug(CPUState *cs, struct kvm_guest_debug *dbg) static int kvm_handle_hw_breakpoint(CPUState *cs, struct kvm_debug_exit_arch *arch_info) { - int handle = 0; + int handle = DEBUG_RETURN_GUEST; int n; int flag = 0; @@ -1578,13 +1577,13 @@ static int kvm_handle_hw_breakpoint(CPUState *cs, if (arch_info->status & KVMPPC_DEBUG_BREAKPOINT) { n = find_hw_breakpoint(arch_info->address, GDB_BREAKPOINT_HW); if (n >= 0) { - handle = 1; + handle = DEBUG_RETURN_GDB; } } else if (arch_info->status & (KVMPPC_DEBUG_WATCH_READ | KVMPPC_DEBUG_WATCH_WRITE)) { n = find_hw_watchpoint(arch_info->address, &flag); if (n >= 0) { - handle = 1; + handle = DEBUG_RETURN_GDB; cs->watchpoint_hit = &hw_watchpoint; hw_watchpoint.vaddr = hw_debug_points[n].addr; hw_watchpoint.flags = flag; @@ -1596,12 +1595,12 @@ static int kvm_handle_hw_breakpoint(CPUState *cs, static int kvm_handle_singlestep(void) { - return 1; + return DEBUG_RETURN_GDB; } static int kvm_handle_sw_breakpoint(void) { - return 1; + return DEBUG_RETURN_GDB; } static int kvm_handle_debug(PowerPCCPU *cpu, struct kvm_run *run) @@ -1653,7 +1652,7 @@ static int kvm_handle_debug(PowerPCCPU *cpu, struct kvm_run *run) env->error_code = POWERPC_EXCP_INVAL; ppc_cpu_do_interrupt(cs); - return 0; + return DEBUG_RETURN_GUEST; } int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) @@ -1708,6 +1707,13 @@ int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) ret = 0; break; +#if defined(TARGET_PPC64) + case KVM_EXIT_NMI: + trace_kvm_handle_nmi_exception(); + ret = kvm_handle_nmi(cpu, run); + break; +#endif + default: fprintf(stderr, "KVM: unknown exit reason %d\n", run->exit_reason); ret = -1; @@ -2060,6 +2066,19 @@ void kvmppc_set_mpic_proxy(PowerPCCPU *cpu, int mpic_proxy) } } +bool kvmppc_get_fwnmi(void) +{ + return cap_fwnmi; +} + +int kvmppc_set_fwnmi(void) +{ + PowerPCCPU *cpu = POWERPC_CPU(first_cpu); + CPUState *cs = CPU(cpu); + + return kvm_vcpu_enable_cap(cs, KVM_CAP_PPC_FWNMI, 0); +} + int kvmppc_smt_threads(void) { return cap_ppc_smt ? cap_ppc_smt : 1; @@ -2076,7 +2095,7 @@ int kvmppc_set_smt_threads(int smt) return ret; } -void kvmppc_hint_smt_possible(Error **errp) +void kvmppc_error_append_smt_possible_hint(Error *const *errp) { int i; GString *g; @@ -2101,7 +2120,7 @@ void kvmppc_hint_smt_possible(Error **errp) #ifdef TARGET_PPC64 -uint64_t kvmppc_rma_size(uint64_t current_size, unsigned int hash_shift) +uint64_t kvmppc_vrma_limit(unsigned int hash_shift) { struct kvm_ppc_smmu_info info; long rampagesize, best_page_shift; @@ -2128,8 +2147,7 @@ uint64_t kvmppc_rma_size(uint64_t current_size, unsigned int hash_shift) } } - return MIN(current_size, - 1ULL << (best_page_shift + hash_shift - 7)); + return 1ULL << (best_page_shift + hash_shift - 7); } #endif @@ -2795,6 +2813,19 @@ int kvm_arch_msi_data_to_gsi(uint32_t data) return data & 0xffff; } +#if defined(TARGET_PPC64) +int kvm_handle_nmi(PowerPCCPU *cpu, struct kvm_run *run) +{ + uint16_t flags = run->flags & KVM_RUN_PPC_NMI_DISP_MASK; + + cpu_synchronize_state(CPU(cpu)); + + spapr_mce_req_event(cpu, flags == KVM_RUN_PPC_NMI_DISP_FULLY_RECOV); + + return 0; +} +#endif + int kvmppc_enable_hwrng(void) { if (!kvm_enabled() || !kvm_check_extension(kvm_state, KVM_CAP_PPC_HWRNG)) { @@ -2900,3 +2931,21 @@ void kvmppc_set_reg_tb_offset(PowerPCCPU *cpu, int64_t tb_offset) kvm_set_one_reg(cs, KVM_REG_PPC_TB_OFFSET, &tb_offset); } } + +/* + * Don't set error if KVM_PPC_SVM_OFF ioctl is invoked on kernels + * that don't support this ioctl. + */ +void kvmppc_svm_off(Error **errp) +{ + int rc; + + if (!kvm_enabled()) { + return; + } + + rc = kvm_vm_ioctl(KVM_STATE(current_accel()), KVM_PPC_SVM_OFF); + if (rc && rc != -ENOTTY) { + error_setg_errno(errp, -rc, "KVM_PPC_SVM_OFF ioctl failed"); + } +} diff --git a/target/ppc/kvm_ppc.h b/target/ppc/kvm_ppc.h index 98bd7d5da6..fcaf745516 100644 --- a/target/ppc/kvm_ppc.h +++ b/target/ppc/kvm_ppc.h @@ -27,8 +27,10 @@ void kvmppc_enable_h_page_init(void); void kvmppc_set_papr(PowerPCCPU *cpu); int kvmppc_set_compat(PowerPCCPU *cpu, uint32_t compat_pvr); void kvmppc_set_mpic_proxy(PowerPCCPU *cpu, int mpic_proxy); +bool kvmppc_get_fwnmi(void); +int kvmppc_set_fwnmi(void); int kvmppc_smt_threads(void); -void kvmppc_hint_smt_possible(Error **errp); +void kvmppc_error_append_smt_possible_hint(Error *const *errp); int kvmppc_set_smt_threads(int smt); int kvmppc_clear_tsr_bits(PowerPCCPU *cpu, uint32_t tsr_bits); int kvmppc_or_tsr_bits(PowerPCCPU *cpu, uint32_t tsr_bits); @@ -37,6 +39,7 @@ int kvmppc_booke_watchdog_enable(PowerPCCPU *cpu); target_ulong kvmppc_configure_v3_mmu(PowerPCCPU *cpu, bool radix, bool gtse, uint64_t proc_tbl); +void kvmppc_svm_off(Error **errp); #ifndef CONFIG_USER_ONLY bool kvmppc_spapr_use_multitce(void); int kvmppc_spapr_enable_inkernel_multitce(void); @@ -45,7 +48,7 @@ void *kvmppc_create_spapr_tce(uint32_t liobn, uint32_t page_shift, int *pfd, bool need_vfio); int kvmppc_remove_spapr_tce(void *table, int pfd, uint32_t window_size); int kvmppc_reset_htab(int shift_hint); -uint64_t kvmppc_rma_size(uint64_t current_size, unsigned int hash_shift); +uint64_t kvmppc_vrma_limit(unsigned int hash_shift); bool kvmppc_has_cap_spapr_vfio(void); #endif /* !CONFIG_USER_ONLY */ bool kvmppc_has_cap_epr(void); @@ -82,6 +85,8 @@ void kvm_check_mmu(PowerPCCPU *cpu, Error **errp); void kvmppc_set_reg_ppc_online(PowerPCCPU *cpu, unsigned int online); void kvmppc_set_reg_tb_offset(PowerPCCPU *cpu, int64_t tb_offset); +int kvm_handle_nmi(PowerPCCPU *cpu, struct kvm_run *run); + #else static inline uint32_t kvmppc_get_tbfreq(void) @@ -159,12 +164,22 @@ static inline void kvmppc_set_mpic_proxy(PowerPCCPU *cpu, int mpic_proxy) { } +static inline bool kvmppc_get_fwnmi(void) +{ + return false; +} + +static inline int kvmppc_set_fwnmi(void) +{ + return -1; +} + static inline int kvmppc_smt_threads(void) { return 1; } -static inline void kvmppc_hint_smt_possible(Error **errp) +static inline void kvmppc_error_append_smt_possible_hint(Error *const *errp) { return; } @@ -201,6 +216,11 @@ static inline target_ulong kvmppc_configure_v3_mmu(PowerPCCPU *cpu, return 0; } +static inline void kvmppc_svm_off(Error **errp) +{ + return; +} + static inline void kvmppc_set_reg_ppc_online(PowerPCCPU *cpu, unsigned int online) { @@ -241,10 +261,9 @@ static inline int kvmppc_reset_htab(int shift_hint) return 0; } -static inline uint64_t kvmppc_rma_size(uint64_t current_size, - unsigned int hash_shift) +static inline uint64_t kvmppc_vrma_limit(unsigned int hash_shift) { - return ram_size; + g_assert_not_reached(); } static inline bool kvmppc_hpt_needs_host_contiguous_pages(void) diff --git a/target/ppc/mem_helper.c b/target/ppc/mem_helper.c index 1351b53f28..98f589552b 100644 --- a/target/ppc/mem_helper.c +++ b/target/ppc/mem_helper.c @@ -25,7 +25,7 @@ #include "exec/helper-proto.h" #include "helper_regs.h" #include "exec/cpu_ldst.h" -#include "tcg.h" +#include "tcg/tcg.h" #include "internal.h" #include "qemu/atomic128.h" @@ -56,51 +56,138 @@ static inline target_ulong addr_add(CPUPPCState *env, target_ulong addr, } } +static void *probe_contiguous(CPUPPCState *env, target_ulong addr, uint32_t nb, + MMUAccessType access_type, int mmu_idx, + uintptr_t raddr) +{ + void *host1, *host2; + uint32_t nb_pg1, nb_pg2; + + nb_pg1 = -(addr | TARGET_PAGE_MASK); + if (likely(nb <= nb_pg1)) { + /* The entire operation is on a single page. */ + return probe_access(env, addr, nb, access_type, mmu_idx, raddr); + } + + /* The operation spans two pages. */ + nb_pg2 = nb - nb_pg1; + host1 = probe_access(env, addr, nb_pg1, access_type, mmu_idx, raddr); + addr = addr_add(env, addr, nb_pg1); + host2 = probe_access(env, addr, nb_pg2, access_type, mmu_idx, raddr); + + /* If the two host pages are contiguous, optimize. */ + if (host2 == host1 + nb_pg1) { + return host1; + } + return NULL; +} + void helper_lmw(CPUPPCState *env, target_ulong addr, uint32_t reg) { - for (; reg < 32; reg++) { - if (needs_byteswap(env)) { - env->gpr[reg] = bswap32(cpu_ldl_data_ra(env, addr, GETPC())); - } else { - env->gpr[reg] = cpu_ldl_data_ra(env, addr, GETPC()); + uintptr_t raddr = GETPC(); + int mmu_idx = cpu_mmu_index(env, false); + void *host = probe_contiguous(env, addr, (32 - reg) * 4, + MMU_DATA_LOAD, mmu_idx, raddr); + + if (likely(host)) { + /* Fast path -- the entire operation is in RAM at host. */ + for (; reg < 32; reg++) { + env->gpr[reg] = (uint32_t)ldl_be_p(host); + host += 4; + } + } else { + /* Slow path -- at least some of the operation requires i/o. */ + for (; reg < 32; reg++) { + env->gpr[reg] = cpu_ldl_mmuidx_ra(env, addr, mmu_idx, raddr); + addr = addr_add(env, addr, 4); } - addr = addr_add(env, addr, 4); } } void helper_stmw(CPUPPCState *env, target_ulong addr, uint32_t reg) { - for (; reg < 32; reg++) { - if (needs_byteswap(env)) { - cpu_stl_data_ra(env, addr, bswap32((uint32_t)env->gpr[reg]), - GETPC()); - } else { - cpu_stl_data_ra(env, addr, (uint32_t)env->gpr[reg], GETPC()); + uintptr_t raddr = GETPC(); + int mmu_idx = cpu_mmu_index(env, false); + void *host = probe_contiguous(env, addr, (32 - reg) * 4, + MMU_DATA_STORE, mmu_idx, raddr); + + if (likely(host)) { + /* Fast path -- the entire operation is in RAM at host. */ + for (; reg < 32; reg++) { + stl_be_p(host, env->gpr[reg]); + host += 4; + } + } else { + /* Slow path -- at least some of the operation requires i/o. */ + for (; reg < 32; reg++) { + cpu_stl_mmuidx_ra(env, addr, env->gpr[reg], mmu_idx, raddr); + addr = addr_add(env, addr, 4); } - addr = addr_add(env, addr, 4); } } static void do_lsw(CPUPPCState *env, target_ulong addr, uint32_t nb, uint32_t reg, uintptr_t raddr) { - int sh; + int mmu_idx; + void *host; + uint32_t val; - for (; nb > 3; nb -= 4) { - env->gpr[reg] = cpu_ldl_data_ra(env, addr, raddr); - reg = (reg + 1) % 32; - addr = addr_add(env, addr, 4); + if (unlikely(nb == 0)) { + return; } - if (unlikely(nb > 0)) { - env->gpr[reg] = 0; - for (sh = 24; nb > 0; nb--, sh -= 8) { - env->gpr[reg] |= cpu_ldub_data_ra(env, addr, raddr) << sh; - addr = addr_add(env, addr, 1); + + mmu_idx = cpu_mmu_index(env, false); + host = probe_contiguous(env, addr, nb, MMU_DATA_LOAD, mmu_idx, raddr); + + if (likely(host)) { + /* Fast path -- the entire operation is in RAM at host. */ + for (; nb > 3; nb -= 4) { + env->gpr[reg] = (uint32_t)ldl_be_p(host); + reg = (reg + 1) % 32; + host += 4; + } + switch (nb) { + default: + return; + case 1: + val = ldub_p(host) << 24; + break; + case 2: + val = lduw_be_p(host) << 16; + break; + case 3: + val = (lduw_be_p(host) << 16) | (ldub_p(host + 2) << 8); + break; + } + } else { + /* Slow path -- at least some of the operation requires i/o. */ + for (; nb > 3; nb -= 4) { + env->gpr[reg] = cpu_ldl_mmuidx_ra(env, addr, mmu_idx, raddr); + reg = (reg + 1) % 32; + addr = addr_add(env, addr, 4); + } + switch (nb) { + default: + return; + case 1: + val = cpu_ldub_mmuidx_ra(env, addr, mmu_idx, raddr) << 24; + break; + case 2: + val = cpu_lduw_mmuidx_ra(env, addr, mmu_idx, raddr) << 16; + break; + case 3: + val = cpu_lduw_mmuidx_ra(env, addr, mmu_idx, raddr) << 16; + addr = addr_add(env, addr, 2); + val |= cpu_ldub_mmuidx_ra(env, addr, mmu_idx, raddr) << 8; + break; } } + env->gpr[reg] = val; } -void helper_lsw(CPUPPCState *env, target_ulong addr, uint32_t nb, uint32_t reg) +void helper_lsw(CPUPPCState *env, target_ulong addr, + uint32_t nb, uint32_t reg) { do_lsw(env, addr, nb, reg, GETPC()); } @@ -130,17 +217,57 @@ void helper_lswx(CPUPPCState *env, target_ulong addr, uint32_t reg, void helper_stsw(CPUPPCState *env, target_ulong addr, uint32_t nb, uint32_t reg) { - int sh; + uintptr_t raddr = GETPC(); + int mmu_idx; + void *host; + uint32_t val; - for (; nb > 3; nb -= 4) { - cpu_stl_data_ra(env, addr, env->gpr[reg], GETPC()); - reg = (reg + 1) % 32; - addr = addr_add(env, addr, 4); + if (unlikely(nb == 0)) { + return; } - if (unlikely(nb > 0)) { - for (sh = 24; nb > 0; nb--, sh -= 8) { - cpu_stb_data_ra(env, addr, (env->gpr[reg] >> sh) & 0xFF, GETPC()); - addr = addr_add(env, addr, 1); + + mmu_idx = cpu_mmu_index(env, false); + host = probe_contiguous(env, addr, nb, MMU_DATA_STORE, mmu_idx, raddr); + + if (likely(host)) { + /* Fast path -- the entire operation is in RAM at host. */ + for (; nb > 3; nb -= 4) { + stl_be_p(host, env->gpr[reg]); + reg = (reg + 1) % 32; + host += 4; + } + val = env->gpr[reg]; + switch (nb) { + case 1: + stb_p(host, val >> 24); + break; + case 2: + stw_be_p(host, val >> 16); + break; + case 3: + stw_be_p(host, val >> 16); + stb_p(host + 2, val >> 8); + break; + } + } else { + for (; nb > 3; nb -= 4) { + cpu_stl_mmuidx_ra(env, addr, env->gpr[reg], mmu_idx, raddr); + reg = (reg + 1) % 32; + addr = addr_add(env, addr, 4); + } + val = env->gpr[reg]; + switch (nb) { + case 1: + cpu_stb_mmuidx_ra(env, addr, val >> 24, mmu_idx, raddr); + break; + case 2: + cpu_stw_mmuidx_ra(env, addr, val >> 16, mmu_idx, raddr); + break; + case 3: + cpu_stw_mmuidx_ra(env, addr, val >> 16, mmu_idx, raddr); + addr = addr_add(env, addr, 2); + cpu_stb_mmuidx_ra(env, addr, val >> 8, mmu_idx, raddr); + break; } } } @@ -166,25 +293,18 @@ static void dcbz_common(CPUPPCState *env, target_ulong addr, addr &= mask; /* Check reservation */ - if ((env->reserve_addr & mask) == (addr & mask)) { + if ((env->reserve_addr & mask) == addr) { env->reserve_addr = (target_ulong)-1ULL; } /* Try fast path translate */ - haddr = tlb_vaddr_to_host(env, addr, MMU_DATA_STORE, mmu_idx); + haddr = probe_write(env, addr, dcbz_size, mmu_idx, retaddr); if (haddr) { memset(haddr, 0, dcbz_size); } else { /* Slow path */ for (i = 0; i < dcbz_size; i += 8) { - if (epid) { -#if !defined(CONFIG_USER_ONLY) - /* Does not make sense on USER_ONLY config */ - cpu_stq_eps_ra(env, addr + i, 0, retaddr); -#endif - } else { - cpu_stq_data_ra(env, addr + i, 0, retaddr); - } + cpu_stq_mmuidx_ra(env, addr + i, 0, mmu_idx, retaddr); } } } @@ -216,7 +336,7 @@ void helper_icbiep(CPUPPCState *env, target_ulong addr) #if !defined(CONFIG_USER_ONLY) /* See comments above */ addr &= ~(env->dcache_line_size - 1); - cpu_ldl_epl_ra(env, addr, GETPC()); + cpu_ldl_mmuidx_ra(env, addr, PPC_TLB_EPID_LOAD, GETPC()); #endif } diff --git a/target/ppc/misc_helper.c b/target/ppc/misc_helper.c index 2318f3ab45..55b68d1246 100644 --- a/target/ppc/misc_helper.c +++ b/target/ppc/misc_helper.c @@ -41,6 +41,18 @@ void helper_store_dump_spr(CPUPPCState *env, uint32_t sprn) } #ifdef TARGET_PPC64 +static void raise_hv_fu_exception(CPUPPCState *env, uint32_t bit, + const char *caller, uint32_t cause, + uintptr_t raddr) +{ + qemu_log_mask(CPU_LOG_INT, "HV Facility %d is unavailable (%s)\n", + bit, caller); + + env->spr[SPR_HFSCR] &= ~((target_ulong)FSCR_IC_MASK << FSCR_IC_POS); + + raise_exception_err_ra(env, POWERPC_EXCP_HV_FU, cause, raddr); +} + static void raise_fu_exception(CPUPPCState *env, uint32_t bit, uint32_t sprn, uint32_t cause, uintptr_t raddr) @@ -55,6 +67,17 @@ static void raise_fu_exception(CPUPPCState *env, uint32_t bit, } #endif +void helper_hfscr_facility_check(CPUPPCState *env, uint32_t bit, + const char *caller, uint32_t cause) +{ +#ifdef TARGET_PPC64 + if ((env->msr_mask & MSR_HVB) && !msr_hv && + !(env->spr[SPR_HFSCR] & (1UL << bit))) { + raise_hv_fu_exception(env, bit, caller, cause, GETPC()); + } +#endif +} + void helper_fscr_facility_check(CPUPPCState *env, uint32_t bit, uint32_t sprn, uint32_t cause) { @@ -105,6 +128,46 @@ void helper_store_pcr(CPUPPCState *env, target_ulong value) env->spr[SPR_PCR] = value & pcc->pcr_mask; } + +/* + * DPDES register is shared. Each bit reflects the state of the + * doorbell interrupt of a thread of the same core. + */ +target_ulong helper_load_dpdes(CPUPPCState *env) +{ + target_ulong dpdes = 0; + + helper_hfscr_facility_check(env, HFSCR_MSGP, "load DPDES", HFSCR_IC_MSGP); + + /* TODO: TCG supports only one thread */ + if (env->pending_interrupts & (1 << PPC_INTERRUPT_DOORBELL)) { + dpdes = 1; + } + + return dpdes; +} + +void helper_store_dpdes(CPUPPCState *env, target_ulong val) +{ + PowerPCCPU *cpu = env_archcpu(env); + CPUState *cs = CPU(cpu); + + helper_hfscr_facility_check(env, HFSCR_MSGP, "store DPDES", HFSCR_IC_MSGP); + + /* TODO: TCG supports only one thread */ + if (val & ~0x1) { + qemu_log_mask(LOG_GUEST_ERROR, "Invalid DPDES register value " + TARGET_FMT_lx"\n", val); + return; + } + + if (val & 0x1) { + env->pending_interrupts |= 1 << PPC_INTERRUPT_DOORBELL; + cpu_interrupt(cs, CPU_INTERRUPT_HARD); + } else { + env->pending_interrupts &= ~(1 << PPC_INTERRUPT_DOORBELL); + } +} #endif /* defined(TARGET_PPC64) */ void helper_store_pidr(CPUPPCState *env, target_ulong val) diff --git a/target/ppc/mmu-hash64.c b/target/ppc/mmu-hash64.c index da8966ccf5..e5baabf0e1 100644 --- a/target/ppc/mmu-hash64.c +++ b/target/ppc/mmu-hash64.c @@ -18,6 +18,7 @@ * License along with this library; if not, see . */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "cpu.h" #include "exec/exec-all.h" #include "exec/helper-proto.h" @@ -94,24 +95,75 @@ void dump_slb(PowerPCCPU *cpu) } } -void helper_slbia(CPUPPCState *env) +void helper_slbia(CPUPPCState *env, uint32_t ih) { PowerPCCPU *cpu = env_archcpu(env); + int starting_entry; int n; - /* XXX: Warning: slbia never invalidates the first segment */ - for (n = 1; n < cpu->hash64_opts->slb_size; n++) { + /* + * slbia must always flush all TLB (which is equivalent to ERAT in ppc + * architecture). Matching on SLB_ESID_V is not good enough, because slbmte + * can overwrite a valid SLB without flushing its lookaside information. + * + * It would be possible to keep the TLB in synch with the SLB by flushing + * when a valid entry is overwritten by slbmte, and therefore slbia would + * not have to flush unless it evicts a valid SLB entry. However it is + * expected that slbmte is more common than slbia, and slbia is usually + * going to evict valid SLB entries, so that tradeoff is unlikely to be a + * good one. + * + * ISA v2.05 introduced IH field with values 0,1,2,6. These all invalidate + * the same SLB entries (everything but entry 0), but differ in what + * "lookaside information" is invalidated. TCG can ignore this and flush + * everything. + * + * ISA v3.0 introduced additional values 3,4,7, which change what SLBs are + * invalidated. + */ + + env->tlb_need_flush |= TLB_NEED_LOCAL_FLUSH; + + starting_entry = 1; /* default for IH=0,1,2,6 */ + + if (env->mmu_model == POWERPC_MMU_3_00) { + switch (ih) { + case 0x7: + /* invalidate no SLBs, but all lookaside information */ + return; + + case 0x3: + case 0x4: + /* also considers SLB entry 0 */ + starting_entry = 0; + break; + + case 0x5: + /* treat undefined values as ih==0, and warn */ + qemu_log_mask(LOG_GUEST_ERROR, + "slbia undefined IH field %u.\n", ih); + break; + + default: + /* 0,1,2,6 */ + break; + } + } + + for (n = starting_entry; n < cpu->hash64_opts->slb_size; n++) { ppc_slb_t *slb = &env->slb[n]; - if (slb->esid & SLB_ESID_V) { - slb->esid &= ~SLB_ESID_V; - /* - * XXX: given the fact that segment size is 256 MB or 1TB, - * and we still don't have a tlb_flush_mask(env, n, mask) - * in QEMU, we just invalidate all TLBs - */ - env->tlb_need_flush |= TLB_NEED_LOCAL_FLUSH; + if (!(slb->esid & SLB_ESID_V)) { + continue; } + if (env->mmu_model == POWERPC_MMU_3_00) { + if (ih == 0x3 && (slb->vsid & SLB_VSID_C) == 0) { + /* preserves entries with a class value of 0 */ + continue; + } + } + + slb->esid &= ~SLB_ESID_V; } } @@ -668,6 +720,21 @@ unsigned ppc_hash64_hpte_page_shift_noslb(PowerPCCPU *cpu, return 0; } +static bool ppc_hash64_use_vrma(CPUPPCState *env) +{ + switch (env->mmu_model) { + case POWERPC_MMU_3_00: + /* + * ISAv3.0 (POWER9) always uses VRMA, the VPM0 field and RMOR + * register no longer exist + */ + return true; + + default: + return !!(env->spr[SPR_LPCR] & LPCR_VPM0); + } +} + static void ppc_hash64_set_isi(CPUState *cs, uint64_t error_code) { CPUPPCState *env = &POWERPC_CPU(cs)->env; @@ -676,15 +743,7 @@ static void ppc_hash64_set_isi(CPUState *cs, uint64_t error_code) if (msr_ir) { vpm = !!(env->spr[SPR_LPCR] & LPCR_VPM1); } else { - switch (env->mmu_model) { - case POWERPC_MMU_3_00: - /* Field deprecated in ISAv3.00 - interrupts always go to hyperv */ - vpm = true; - break; - default: - vpm = !!(env->spr[SPR_LPCR] & LPCR_VPM0); - break; - } + vpm = ppc_hash64_use_vrma(env); } if (vpm && !msr_hv) { cs->exception_index = POWERPC_EXCP_HISI; @@ -702,15 +761,7 @@ static void ppc_hash64_set_dsi(CPUState *cs, uint64_t dar, uint64_t dsisr) if (msr_dr) { vpm = !!(env->spr[SPR_LPCR] & LPCR_VPM1); } else { - switch (env->mmu_model) { - case POWERPC_MMU_3_00: - /* Field deprecated in ISAv3.00 - interrupts always go to hyperv */ - vpm = true; - break; - default: - vpm = !!(env->spr[SPR_LPCR] & LPCR_VPM0); - break; - } + vpm = ppc_hash64_use_vrma(env); } if (vpm && !msr_hv) { cs->exception_index = POWERPC_EXCP_HDSI; @@ -758,11 +809,67 @@ static void ppc_hash64_set_c(PowerPCCPU *cpu, hwaddr ptex, uint64_t pte1) stb_phys(CPU(cpu)->as, base + offset, (pte1 & 0xff) | 0x80); } +static target_ulong rmls_limit(PowerPCCPU *cpu) +{ + CPUPPCState *env = &cpu->env; + /* + * In theory the meanings of RMLS values are implementation + * dependent. In practice, this seems to have been the set from + * POWER4+..POWER8, and RMLS is no longer supported in POWER9. + * + * Unsupported values mean the OS has shot itself in the + * foot. Return a 0-sized RMA in this case, which we expect + * to trigger an immediate DSI or ISI + */ + static const target_ulong rma_sizes[16] = { + [0] = 256 * GiB, + [1] = 16 * GiB, + [2] = 1 * GiB, + [3] = 64 * MiB, + [4] = 256 * MiB, + [7] = 128 * MiB, + [8] = 32 * MiB, + }; + target_ulong rmls = (env->spr[SPR_LPCR] & LPCR_RMLS) >> LPCR_RMLS_SHIFT; + + return rma_sizes[rmls]; +} + +static int build_vrma_slbe(PowerPCCPU *cpu, ppc_slb_t *slb) +{ + CPUPPCState *env = &cpu->env; + target_ulong lpcr = env->spr[SPR_LPCR]; + uint32_t vrmasd = (lpcr & LPCR_VRMASD) >> LPCR_VRMASD_SHIFT; + target_ulong vsid = SLB_VSID_VRMA | ((vrmasd << 4) & SLB_VSID_LLP_MASK); + int i; + + for (i = 0; i < PPC_PAGE_SIZES_MAX_SZ; i++) { + const PPCHash64SegmentPageSizes *sps = &cpu->hash64_opts->sps[i]; + + if (!sps->page_shift) { + break; + } + + if ((vsid & SLB_VSID_LLP_MASK) == sps->slb_enc) { + slb->esid = SLB_ESID_V; + slb->vsid = vsid; + slb->sps = sps; + return 0; + } + } + + error_report("Bad page size encoding in LPCR[VRMASD]; LPCR=0x" + TARGET_FMT_lx"\n", lpcr); + + return -1; +} + int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, int rwx, int mmu_idx) { CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; + ppc_slb_t vrma_slbe; ppc_slb_t *slb; unsigned apshift; hwaddr ptex; @@ -789,27 +896,32 @@ int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, */ raddr = eaddr & 0x0FFFFFFFFFFFFFFFULL; - /* In HV mode, add HRMOR if top EA bit is clear */ - if (msr_hv || !env->has_hv_mode) { + if (cpu->vhyp) { + /* + * In virtual hypervisor mode, there's nothing to do: + * EA == GPA == qemu guest address + */ + } else if (msr_hv || !env->has_hv_mode) { + /* In HV mode, add HRMOR if top EA bit is clear */ if (!(eaddr >> 63)) { raddr |= env->spr[SPR_HRMOR]; } - } else { - /* Otherwise, check VPM for RMA vs VRMA */ - if (env->spr[SPR_LPCR] & LPCR_VPM0) { - slb = &env->vrma_slb; - if (slb->sps) { - goto skip_slb_search; - } - /* Not much else to do here */ + } else if (ppc_hash64_use_vrma(env)) { + /* Emulated VRMA mode */ + slb = &vrma_slbe; + if (build_vrma_slbe(cpu, slb) != 0) { + /* Invalid VRMA setup, machine check */ cs->exception_index = POWERPC_EXCP_MCHECK; env->error_code = 0; return 1; - } else if (raddr < env->rmls) { - /* RMA. Check bounds in RMLS */ - raddr |= env->spr[SPR_RMOR]; - } else { - /* The access failed, generate the approriate interrupt */ + } + + goto skip_slb_search; + } else { + target_ulong limit = rmls_limit(cpu); + + /* Emulated old-style RMO mode, bounds check against RMLS */ + if (raddr >= limit) { if (rwx == 2) { ppc_hash64_set_isi(cs, SRR1_PROTFAULT); } else { @@ -821,6 +933,8 @@ int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, } return 1; } + + raddr |= env->spr[SPR_RMOR]; } tlb_set_page(cs, eaddr & TARGET_PAGE_MASK, raddr & TARGET_PAGE_MASK, PAGE_READ | PAGE_WRITE | PAGE_EXEC, mmu_idx, @@ -943,6 +1057,7 @@ skip_slb_search: hwaddr ppc_hash64_get_phys_page_debug(PowerPCCPU *cpu, target_ulong addr) { CPUPPCState *env = &cpu->env; + ppc_slb_t vrma_slbe; ppc_slb_t *slb; hwaddr ptex, raddr; ppc_hash_pte64_t pte; @@ -953,22 +1068,29 @@ hwaddr ppc_hash64_get_phys_page_debug(PowerPCCPU *cpu, target_ulong addr) /* In real mode the top 4 effective address bits are ignored */ raddr = addr & 0x0FFFFFFFFFFFFFFFULL; - /* In HV mode, add HRMOR if top EA bit is clear */ - if ((msr_hv || !env->has_hv_mode) && !(addr >> 63)) { + if (cpu->vhyp) { + /* + * In virtual hypervisor mode, there's nothing to do: + * EA == GPA == qemu guest address + */ + return raddr; + } else if ((msr_hv || !env->has_hv_mode) && !(addr >> 63)) { + /* In HV mode, add HRMOR if top EA bit is clear */ return raddr | env->spr[SPR_HRMOR]; - } - - /* Otherwise, check VPM for RMA vs VRMA */ - if (env->spr[SPR_LPCR] & LPCR_VPM0) { - slb = &env->vrma_slb; - if (!slb->sps) { + } else if (ppc_hash64_use_vrma(env)) { + /* Emulated VRMA mode */ + slb = &vrma_slbe; + if (build_vrma_slbe(cpu, slb) != 0) { return -1; } - } else if (raddr < env->rmls) { - /* RMA. Check bounds in RMLS */ - return raddr | env->spr[SPR_RMOR]; } else { - return -1; + target_ulong limit = rmls_limit(cpu); + + /* Emulated old-style RMO mode, bounds check against RMLS */ + if (raddr >= limit) { + return -1; + } + return raddr | env->spr[SPR_RMOR]; } } else { slb = slb_lookup(cpu, addr); @@ -997,168 +1119,12 @@ void ppc_hash64_tlb_flush_hpte(PowerPCCPU *cpu, target_ulong ptex, cpu->env.tlb_need_flush = TLB_NEED_GLOBAL_FLUSH | TLB_NEED_LOCAL_FLUSH; } -static void ppc_hash64_update_rmls(PowerPCCPU *cpu) -{ - CPUPPCState *env = &cpu->env; - uint64_t lpcr = env->spr[SPR_LPCR]; - - /* - * This is the full 4 bits encoding of POWER8. Previous - * CPUs only support a subset of these but the filtering - * is done when writing LPCR - */ - switch ((lpcr & LPCR_RMLS) >> LPCR_RMLS_SHIFT) { - case 0x8: /* 32MB */ - env->rmls = 0x2000000ull; - break; - case 0x3: /* 64MB */ - env->rmls = 0x4000000ull; - break; - case 0x7: /* 128MB */ - env->rmls = 0x8000000ull; - break; - case 0x4: /* 256MB */ - env->rmls = 0x10000000ull; - break; - case 0x2: /* 1GB */ - env->rmls = 0x40000000ull; - break; - case 0x1: /* 16GB */ - env->rmls = 0x400000000ull; - break; - default: - /* What to do here ??? */ - env->rmls = 0; - } -} - -static void ppc_hash64_update_vrma(PowerPCCPU *cpu) -{ - CPUPPCState *env = &cpu->env; - const PPCHash64SegmentPageSizes *sps = NULL; - target_ulong esid, vsid, lpcr; - ppc_slb_t *slb = &env->vrma_slb; - uint32_t vrmasd; - int i; - - /* First clear it */ - slb->esid = slb->vsid = 0; - slb->sps = NULL; - - /* Is VRMA enabled ? */ - lpcr = env->spr[SPR_LPCR]; - if (!(lpcr & LPCR_VPM0)) { - return; - } - - /* - * Make one up. Mostly ignore the ESID which will not be needed - * for translation - */ - vsid = SLB_VSID_VRMA; - vrmasd = (lpcr & LPCR_VRMASD) >> LPCR_VRMASD_SHIFT; - vsid |= (vrmasd << 4) & (SLB_VSID_L | SLB_VSID_LP); - esid = SLB_ESID_V; - - for (i = 0; i < PPC_PAGE_SIZES_MAX_SZ; i++) { - const PPCHash64SegmentPageSizes *sps1 = &cpu->hash64_opts->sps[i]; - - if (!sps1->page_shift) { - break; - } - - if ((vsid & SLB_VSID_LLP_MASK) == sps1->slb_enc) { - sps = sps1; - break; - } - } - - if (!sps) { - error_report("Bad page size encoding esid 0x"TARGET_FMT_lx - " vsid 0x"TARGET_FMT_lx, esid, vsid); - return; - } - - slb->vsid = vsid; - slb->esid = esid; - slb->sps = sps; -} - void ppc_store_lpcr(PowerPCCPU *cpu, target_ulong val) { + PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); CPUPPCState *env = &cpu->env; - uint64_t lpcr = 0; - /* Filter out bits */ - switch (env->mmu_model) { - case POWERPC_MMU_64B: /* 970 */ - if (val & 0x40) { - lpcr |= LPCR_LPES0; - } - if (val & 0x8000000000000000ull) { - lpcr |= LPCR_LPES1; - } - if (val & 0x20) { - lpcr |= (0x4ull << LPCR_RMLS_SHIFT); - } - if (val & 0x4000000000000000ull) { - lpcr |= (0x2ull << LPCR_RMLS_SHIFT); - } - if (val & 0x2000000000000000ull) { - lpcr |= (0x1ull << LPCR_RMLS_SHIFT); - } - env->spr[SPR_RMOR] = ((lpcr >> 41) & 0xffffull) << 26; - - /* - * XXX We could also write LPID from HID4 here - * but since we don't tag any translation on it - * it doesn't actually matter - * - * XXX For proper emulation of 970 we also need - * to dig HRMOR out of HID5 - */ - break; - case POWERPC_MMU_2_03: /* P5p */ - lpcr = val & (LPCR_RMLS | LPCR_ILE | - LPCR_LPES0 | LPCR_LPES1 | - LPCR_RMI | LPCR_HDICE); - break; - case POWERPC_MMU_2_06: /* P7 */ - lpcr = val & (LPCR_VPM0 | LPCR_VPM1 | LPCR_ISL | LPCR_DPFD | - LPCR_VRMASD | LPCR_RMLS | LPCR_ILE | - LPCR_P7_PECE0 | LPCR_P7_PECE1 | LPCR_P7_PECE2 | - LPCR_MER | LPCR_TC | - LPCR_LPES0 | LPCR_LPES1 | LPCR_HDICE); - break; - case POWERPC_MMU_2_07: /* P8 */ - lpcr = val & (LPCR_VPM0 | LPCR_VPM1 | LPCR_ISL | LPCR_KBV | - LPCR_DPFD | LPCR_VRMASD | LPCR_RMLS | LPCR_ILE | - LPCR_AIL | LPCR_ONL | LPCR_P8_PECE0 | LPCR_P8_PECE1 | - LPCR_P8_PECE2 | LPCR_P8_PECE3 | LPCR_P8_PECE4 | - LPCR_MER | LPCR_TC | LPCR_LPES0 | LPCR_HDICE); - break; - case POWERPC_MMU_3_00: /* P9 */ - lpcr = val & (LPCR_VPM1 | LPCR_ISL | LPCR_KBV | LPCR_DPFD | - (LPCR_PECE_U_MASK & LPCR_HVEE) | LPCR_ILE | LPCR_AIL | - LPCR_UPRT | LPCR_EVIRT | LPCR_ONL | LPCR_HR | LPCR_LD | - (LPCR_PECE_L_MASK & (LPCR_PDEE | LPCR_HDEE | LPCR_EEE | - LPCR_DEE | LPCR_OEE)) | LPCR_MER | LPCR_GTSE | LPCR_TC | - LPCR_HEIC | LPCR_LPES0 | LPCR_HVICE | LPCR_HDICE); - /* - * If we have a virtual hypervisor, we need to bring back RMLS. It - * doesn't exist on an actual P9 but that's all we know how to - * configure with softmmu at the moment - */ - if (cpu->vhyp) { - lpcr |= (val & LPCR_RMLS); - } - break; - default: - ; - } - env->spr[SPR_LPCR] = lpcr; - ppc_hash64_update_rmls(cpu); - ppc_hash64_update_vrma(cpu); + env->spr[SPR_LPCR] = val & pcc->lpcr_mask; } void helper_store_lpcr(CPUPPCState *env, target_ulong val) diff --git a/target/ppc/mmu-radix64.c b/target/ppc/mmu-radix64.c index 066e324464..224e646c50 100644 --- a/target/ppc/mmu-radix64.c +++ b/target/ppc/mmu-radix64.c @@ -235,6 +235,12 @@ int ppc_radix64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, int rwx, /* In real mode top 4 effective addr bits (mostly) ignored */ raddr = eaddr & 0x0FFFFFFFFFFFFFFFULL; + /* In HV mode, add HRMOR if top EA bit is clear */ + if (msr_hv || !env->has_hv_mode) { + if (!(eaddr >> 63)) { + raddr |= env->spr[SPR_HRMOR]; + } + } tlb_set_page(cs, eaddr & TARGET_PAGE_MASK, raddr & TARGET_PAGE_MASK, PAGE_READ | PAGE_WRITE | PAGE_EXEC, mmu_idx, TARGET_PAGE_SIZE); diff --git a/target/ppc/timebase_helper.c b/target/ppc/timebase_helper.c index 73363e08ae..d16360ab66 100644 --- a/target/ppc/timebase_helper.c +++ b/target/ppc/timebase_helper.c @@ -21,6 +21,7 @@ #include "exec/helper-proto.h" #include "exec/exec-all.h" #include "qemu/log.h" +#include "qemu/main-loop.h" /*****************************************************************************/ /* SPR accesses */ @@ -45,11 +46,21 @@ target_ulong helper_load_atbu(CPUPPCState *env) return cpu_ppc_load_atbu(env); } +target_ulong helper_load_vtb(CPUPPCState *env) +{ + return cpu_ppc_load_vtb(env); +} + #if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY) target_ulong helper_load_purr(CPUPPCState *env) { return (target_ulong)cpu_ppc_load_purr(env); } + +void helper_store_purr(CPUPPCState *env, target_ulong val) +{ + cpu_ppc_store_purr(env, val); +} #endif target_ulong helper_load_601_rtcl(CPUPPCState *env) @@ -113,6 +124,16 @@ void helper_store_hdecr(CPUPPCState *env, target_ulong val) cpu_ppc_store_hdecr(env, val); } +void helper_store_vtb(CPUPPCState *env, target_ulong val) +{ + cpu_ppc_store_vtb(env, val); +} + +void helper_store_tbu40(CPUPPCState *env, target_ulong val) +{ + cpu_ppc_store_tbu40(env, val); +} + target_ulong helper_load_40x_pit(CPUPPCState *env) { return load_40x_pit(env); @@ -147,13 +168,19 @@ target_ulong helper_load_dcr(CPUPPCState *env, target_ulong dcrn) raise_exception_err_ra(env, POWERPC_EXCP_PROGRAM, POWERPC_EXCP_INVAL | POWERPC_EXCP_INVAL_INVAL, GETPC()); - } else if (unlikely(ppc_dcr_read(env->dcr_env, - (uint32_t)dcrn, &val) != 0)) { - qemu_log_mask(LOG_GUEST_ERROR, "DCR read error %d %03x\n", - (uint32_t)dcrn, (uint32_t)dcrn); - raise_exception_err_ra(env, POWERPC_EXCP_PROGRAM, - POWERPC_EXCP_INVAL | - POWERPC_EXCP_PRIV_REG, GETPC()); + } else { + int ret; + + qemu_mutex_lock_iothread(); + ret = ppc_dcr_read(env->dcr_env, (uint32_t)dcrn, &val); + qemu_mutex_unlock_iothread(); + if (unlikely(ret != 0)) { + qemu_log_mask(LOG_GUEST_ERROR, "DCR read error %d %03x\n", + (uint32_t)dcrn, (uint32_t)dcrn); + raise_exception_err_ra(env, POWERPC_EXCP_PROGRAM, + POWERPC_EXCP_INVAL | + POWERPC_EXCP_PRIV_REG, GETPC()); + } } return val; } @@ -165,12 +192,17 @@ void helper_store_dcr(CPUPPCState *env, target_ulong dcrn, target_ulong val) raise_exception_err_ra(env, POWERPC_EXCP_PROGRAM, POWERPC_EXCP_INVAL | POWERPC_EXCP_INVAL_INVAL, GETPC()); - } else if (unlikely(ppc_dcr_write(env->dcr_env, (uint32_t)dcrn, - (uint32_t)val) != 0)) { - qemu_log_mask(LOG_GUEST_ERROR, "DCR write error %d %03x\n", - (uint32_t)dcrn, (uint32_t)dcrn); - raise_exception_err_ra(env, POWERPC_EXCP_PROGRAM, - POWERPC_EXCP_INVAL | - POWERPC_EXCP_PRIV_REG, GETPC()); + } else { + int ret; + qemu_mutex_lock_iothread(); + ret = ppc_dcr_write(env->dcr_env, (uint32_t)dcrn, (uint32_t)val); + qemu_mutex_unlock_iothread(); + if (unlikely(ret != 0)) { + qemu_log_mask(LOG_GUEST_ERROR, "DCR write error %d %03x\n", + (uint32_t)dcrn, (uint32_t)dcrn); + raise_exception_err_ra(env, POWERPC_EXCP_PROGRAM, + POWERPC_EXCP_INVAL | + POWERPC_EXCP_PRIV_REG, GETPC()); + } } } diff --git a/target/ppc/trace-events b/target/ppc/trace-events index 3dc6740706..6d15aa90b4 100644 --- a/target/ppc/trace-events +++ b/target/ppc/trace-events @@ -28,3 +28,4 @@ kvm_handle_papr_hcall(void) "handle PAPR hypercall" kvm_handle_epr(void) "handle epr" kvm_handle_watchdog_expiry(void) "handle watchdog expiry" kvm_handle_debug_exception(void) "handle debug exception" +kvm_handle_nmi_exception(void) "handle NMI exception" diff --git a/target/ppc/translate.c b/target/ppc/translate.c index f5fe5d0611..807d14faaa 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -23,8 +23,8 @@ #include "internal.h" #include "disas/disas.h" #include "exec/exec-all.h" -#include "tcg-op.h" -#include "tcg-op-gvec.h" +#include "tcg/tcg-op.h" +#include "tcg/tcg-op-gvec.h" #include "qemu/host-utils.h" #include "qemu/main-loop.h" #include "exec/cpu_ldst.h" @@ -1938,15 +1938,17 @@ static void gen_rlwinm(DisasContext *ctx) me += 32; #endif mask = MASK(mb, me); - if (sh == 0) { - tcg_gen_andi_tl(t_ra, t_rs, mask); - } else if (mask <= 0xffffffffu) { - TCGv_i32 t0 = tcg_temp_new_i32(); - tcg_gen_trunc_tl_i32(t0, t_rs); - tcg_gen_rotli_i32(t0, t0, sh); - tcg_gen_andi_i32(t0, t0, mask); - tcg_gen_extu_i32_tl(t_ra, t0); - tcg_temp_free_i32(t0); + if (mask <= 0xffffffffu) { + if (sh == 0) { + tcg_gen_andi_tl(t_ra, t_rs, mask); + } else { + TCGv_i32 t0 = tcg_temp_new_i32(); + tcg_gen_trunc_tl_i32(t0, t_rs); + tcg_gen_rotli_i32(t0, t0, sh); + tcg_gen_andi_i32(t0, t0, mask); + tcg_gen_extu_i32_tl(t_ra, t0); + tcg_temp_free_i32(t0); + } } else { #if defined(TARGET_PPC64) tcg_gen_deposit_i64(t_ra, t_rs, t_rs, 32, 32); @@ -4359,30 +4361,34 @@ static void gen_mtmsrd(DisasContext *ctx) CHK_SV; #if !defined(CONFIG_USER_ONLY) + if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { + gen_io_start(); + } if (ctx->opcode & 0x00010000) { - /* Special form that does not need any synchronisation */ + /* L=1 form only updates EE and RI */ TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); tcg_gen_andi_tl(t0, cpu_gpr[rS(ctx->opcode)], (1 << MSR_RI) | (1 << MSR_EE)); - tcg_gen_andi_tl(cpu_msr, cpu_msr, + tcg_gen_andi_tl(t1, cpu_msr, ~(target_ulong)((1 << MSR_RI) | (1 << MSR_EE))); - tcg_gen_or_tl(cpu_msr, cpu_msr, t0); + tcg_gen_or_tl(t1, t1, t0); + + gen_helper_store_msr(cpu_env, t1); tcg_temp_free(t0); + tcg_temp_free(t1); + } else { /* * XXX: we need to update nip before the store if we enter * power saving mode, we will exit the loop directly from * ppc_store_msr */ - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } gen_update_nip(ctx, ctx->base.pc_next); gen_helper_store_msr(cpu_env, cpu_gpr[rS(ctx->opcode)]); - /* Must stop the translation as machine state (may have) changed */ - /* Note that mtmsr is not always defined as context-synchronizing */ - gen_stop_exception(ctx); } + /* Must stop the translation as machine state (may have) changed */ + gen_stop_exception(ctx); #endif /* !defined(CONFIG_USER_ONLY) */ } #endif /* defined(TARGET_PPC64) */ @@ -4392,15 +4398,23 @@ static void gen_mtmsr(DisasContext *ctx) CHK_SV; #if !defined(CONFIG_USER_ONLY) - if (ctx->opcode & 0x00010000) { - /* Special form that does not need any synchronisation */ + if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { + gen_io_start(); + } + if (ctx->opcode & 0x00010000) { + /* L=1 form only updates EE and RI */ TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); tcg_gen_andi_tl(t0, cpu_gpr[rS(ctx->opcode)], (1 << MSR_RI) | (1 << MSR_EE)); - tcg_gen_andi_tl(cpu_msr, cpu_msr, + tcg_gen_andi_tl(t1, cpu_msr, ~(target_ulong)((1 << MSR_RI) | (1 << MSR_EE))); - tcg_gen_or_tl(cpu_msr, cpu_msr, t0); + tcg_gen_or_tl(t1, t1, t0); + + gen_helper_store_msr(cpu_env, t1); tcg_temp_free(t0); + tcg_temp_free(t1); + } else { TCGv msr = tcg_temp_new(); @@ -4409,9 +4423,6 @@ static void gen_mtmsr(DisasContext *ctx) * power saving mode, we will exit the loop directly from * ppc_store_msr */ - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } gen_update_nip(ctx, ctx->base.pc_next); #if defined(TARGET_PPC64) tcg_gen_deposit_tl(msr, cpu_msr, cpu_gpr[rS(ctx->opcode)], 0, 32); @@ -4420,10 +4431,9 @@ static void gen_mtmsr(DisasContext *ctx) #endif gen_helper_store_msr(cpu_env, msr); tcg_temp_free(msr); - /* Must stop the translation as machine state (may have) changed */ - /* Note that mtmsr is not always defined as context-synchronizing */ - gen_stop_exception(ctx); } + /* Must stop the translation as machine state (may have) changed */ + gen_stop_exception(ctx); #endif } @@ -4995,9 +5005,13 @@ static void gen_slbia(DisasContext *ctx) #if defined(CONFIG_USER_ONLY) GEN_PRIV; #else + uint32_t ih = (ctx->opcode >> 21) & 0x7; + TCGv_i32 t0 = tcg_const_i32(ih); + CHK_SV; - gen_helper_slbia(cpu_env); + gen_helper_slbia(cpu_env, t0); + tcg_temp_free_i32(t0); #endif /* defined(CONFIG_USER_ONLY) */ } @@ -6645,6 +6659,28 @@ static void gen_msgsnd(DisasContext *ctx) #endif /* defined(CONFIG_USER_ONLY) */ } +#if defined(TARGET_PPC64) +static void gen_msgclrp(DisasContext *ctx) +{ +#if defined(CONFIG_USER_ONLY) + GEN_PRIV; +#else + CHK_SV; + gen_helper_book3s_msgclrp(cpu_env, cpu_gpr[rB(ctx->opcode)]); +#endif /* defined(CONFIG_USER_ONLY) */ +} + +static void gen_msgsndp(DisasContext *ctx) +{ +#if defined(CONFIG_USER_ONLY) + GEN_PRIV; +#else + CHK_SV; + gen_helper_book3s_msgsndp(cpu_env, cpu_gpr[rB(ctx->opcode)]); +#endif /* defined(CONFIG_USER_ONLY) */ +} +#endif + static void gen_msgsync(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) @@ -7187,6 +7223,10 @@ GEN_HANDLER(vmladduhm, 0x04, 0x11, 0xFF, 0x00000000, PPC_ALTIVEC), GEN_HANDLER_E(maddhd_maddhdu, 0x04, 0x18, 0xFF, 0x00000000, PPC_NONE, PPC2_ISA300), GEN_HANDLER_E(maddld, 0x04, 0x19, 0xFF, 0x00000000, PPC_NONE, PPC2_ISA300), +GEN_HANDLER2_E(msgsndp, "msgsndp", 0x1F, 0x0E, 0x04, 0x03ff0001, + PPC_NONE, PPC2_ISA207S), +GEN_HANDLER2_E(msgclrp, "msgclrp", 0x1F, 0x0E, 0x05, 0x03ff0001, + PPC_NONE, PPC2_ISA207S), #endif #undef GEN_INT_ARITH_ADD diff --git a/target/ppc/translate/fp-impl.inc.c b/target/ppc/translate/fp-impl.inc.c index d8e27bf4d5..9f7868ee28 100644 --- a/target/ppc/translate/fp-impl.inc.c +++ b/target/ppc/translate/fp-impl.inc.c @@ -781,7 +781,7 @@ static void gen_mtfsb1(DisasContext *ctx) tcg_gen_trunc_tl_i32(cpu_crf[1], cpu_fpscr); tcg_gen_shri_i32(cpu_crf[1], cpu_crf[1], FPSCR_OX); } - /* We can raise a differed exception */ + /* We can raise a deferred exception */ gen_helper_float_check_status(cpu_env); } @@ -817,7 +817,7 @@ static void gen_mtfsf(DisasContext *ctx) tcg_gen_trunc_tl_i32(cpu_crf[1], cpu_fpscr); tcg_gen_shri_i32(cpu_crf[1], cpu_crf[1], FPSCR_OX); } - /* We can raise a differed exception */ + /* We can raise a deferred exception */ gen_helper_float_check_status(cpu_env); tcg_temp_free_i64(t1); } @@ -850,7 +850,7 @@ static void gen_mtfsfi(DisasContext *ctx) tcg_gen_trunc_tl_i32(cpu_crf[1], cpu_fpscr); tcg_gen_shri_i32(cpu_crf[1], cpu_crf[1], FPSCR_OX); } - /* We can raise a differed exception */ + /* We can raise a deferred exception */ gen_helper_float_check_status(cpu_env); } diff --git a/target/ppc/translate_init.inc.c b/target/ppc/translate_init.inc.c index ba726dec4d..e853164a86 100644 --- a/target/ppc/translate_init.inc.c +++ b/target/ppc/translate_init.inc.c @@ -287,6 +287,11 @@ static void spr_read_purr(DisasContext *ctx, int gprn, int sprn) gen_helper_load_purr(cpu_gpr[gprn], cpu_env); } +static void spr_write_purr(DisasContext *ctx, int sprn, int gprn) +{ + gen_helper_store_purr(cpu_env, cpu_gpr[gprn]); +} + /* HDECR */ static void spr_read_hdecr(DisasContext *ctx, int gprn, int sprn) { @@ -312,6 +317,21 @@ static void spr_write_hdecr(DisasContext *ctx, int sprn, int gprn) } } +static void spr_read_vtb(DisasContext *ctx, int gprn, int sprn) +{ + gen_helper_load_vtb(cpu_gpr[gprn], cpu_env); +} + +static void spr_write_vtb(DisasContext *ctx, int sprn, int gprn) +{ + gen_helper_store_vtb(cpu_env, cpu_gpr[gprn]); +} + +static void spr_write_tbu40(DisasContext *ctx, int sprn, int gprn) +{ + gen_helper_store_tbu40(cpu_env, cpu_gpr[gprn]); +} + #endif #endif @@ -444,6 +464,17 @@ static void spr_write_pcr(DisasContext *ctx, int sprn, int gprn) { gen_helper_store_pcr(cpu_env, cpu_gpr[gprn]); } + +/* DPDES */ +static void spr_read_dpdes(DisasContext *ctx, int gprn, int sprn) +{ + gen_helper_load_dpdes(cpu_gpr[gprn], cpu_env); +} + +static void spr_write_dpdes(DisasContext *ctx, int sprn, int gprn) +{ + gen_helper_store_dpdes(cpu_env, cpu_gpr[gprn]); +} #endif #endif @@ -3354,6 +3385,11 @@ static void init_excp_POWER9(CPUPPCState *env) #endif } +static void init_excp_POWER10(CPUPPCState *env) +{ + init_excp_POWER9(env); +} + #endif /*****************************************************************************/ @@ -7833,6 +7869,16 @@ static void gen_spr_power5p_ear(CPUPPCState *env) 0x00000000); } +static void gen_spr_power5p_tb(CPUPPCState *env) +{ + /* TBU40 (High 40 bits of the Timebase register */ + spr_register_hv(env, SPR_TBU40, "TBU40", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, &spr_write_tbu40, + 0x00000000); +} + #if !defined(CONFIG_USER_ONLY) static void spr_write_hmer(DisasContext *ctx, int sprn, int gprn) { @@ -7849,25 +7895,21 @@ static void spr_write_lpcr(DisasContext *ctx, int sprn, int gprn) { gen_helper_store_lpcr(cpu_env, cpu_gpr[gprn]); } - -static void spr_write_970_hid4(DisasContext *ctx, int sprn, int gprn) -{ -#if defined(TARGET_PPC64) - spr_write_generic(ctx, sprn, gprn); - gen_helper_store_lpcr(cpu_env, cpu_gpr[gprn]); -#endif -} - #endif /* !defined(CONFIG_USER_ONLY) */ static void gen_spr_970_lpar(CPUPPCState *env) { #if !defined(CONFIG_USER_ONLY) - /* Logical partitionning */ - /* PPC970: HID4 is effectively the LPCR */ + /* + * PPC970: HID4 covers things later controlled by the LPCR and + * RMOR in later CPUs, but with a different encoding. We only + * support the 970 in "Apple mode" which has all hypervisor + * facilities disabled by strapping, so we can basically just + * ignore it + */ spr_register(env, SPR_970_HID4, "HID4", SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_970_hid4, + &spr_read_generic, &spr_write_generic, 0x00000000); #endif } @@ -7973,12 +8015,16 @@ static void gen_spr_book3s_ids(CPUPPCState *env) SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, &spr_write_generic, 0x00000000); - spr_register_hv(env, SPR_RMOR, "RMOR", + spr_register_hv(env, SPR_HRMOR, "HRMOR", SPR_NOACCESS, SPR_NOACCESS, SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, &spr_write_generic, 0x00000000); - spr_register_hv(env, SPR_HRMOR, "HRMOR", +} + +static void gen_spr_rmor(CPUPPCState *env) +{ + spr_register_hv(env, SPR_RMOR, "RMOR", SPR_NOACCESS, SPR_NOACCESS, SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, &spr_write_generic, @@ -7998,14 +8044,16 @@ static void gen_spr_book3s_purr(CPUPPCState *env) { #if !defined(CONFIG_USER_ONLY) /* PURR & SPURR: Hack - treat these as aliases for the TB for now */ - spr_register_kvm(env, SPR_PURR, "PURR", - &spr_read_purr, SPR_NOACCESS, - &spr_read_purr, SPR_NOACCESS, - KVM_REG_PPC_PURR, 0x00000000); - spr_register_kvm(env, SPR_SPURR, "SPURR", - &spr_read_purr, SPR_NOACCESS, - &spr_read_purr, SPR_NOACCESS, - KVM_REG_PPC_SPURR, 0x00000000); + spr_register_kvm_hv(env, SPR_PURR, "PURR", + &spr_read_purr, SPR_NOACCESS, + &spr_read_purr, SPR_NOACCESS, + &spr_read_purr, &spr_write_purr, + KVM_REG_PPC_PURR, 0x00000000); + spr_register_kvm_hv(env, SPR_SPURR, "SPURR", + &spr_read_purr, SPR_NOACCESS, + &spr_read_purr, SPR_NOACCESS, + &spr_read_purr, &spr_write_purr, + KVM_REG_PPC_SPURR, 0x00000000); #endif } @@ -8169,10 +8217,11 @@ static void gen_spr_power8_ebb(CPUPPCState *env) /* Virtual Time Base */ static void gen_spr_vtb(CPUPPCState *env) { - spr_register_kvm(env, SPR_VTB, "VTB", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_tbl, SPR_NOACCESS, - KVM_REG_PPC_VTB, 0x00000000); + spr_register_kvm_hv(env, SPR_VTB, "VTB", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_vtb, SPR_NOACCESS, + &spr_read_vtb, &spr_write_vtb, + KVM_REG_PPC_VTB, 0x00000000); } static void gen_spr_power8_fscr(CPUPPCState *env) @@ -8200,10 +8249,11 @@ static void gen_spr_power8_dpdes(CPUPPCState *env) { #if !defined(CONFIG_USER_ONLY) /* Directed Privileged Door-bell Exception State, used for IPI */ - spr_register(env, SPR_DPDES, "DPDES", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, SPR_NOACCESS, - 0x00000000); + spr_register_kvm_hv(env, SPR_DPDES, "DPDES", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_dpdes, SPR_NOACCESS, + &spr_read_dpdes, &spr_write_dpdes, + KVM_REG_PPC_DPDES, 0x00000000); #endif } @@ -8272,6 +8322,12 @@ static void gen_spr_power9_mmu(CPUPPCState *env) SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, &spr_write_ptcr, KVM_REG_PPC_PTCR, 0x00000000); + /* Address Segment Descriptor Register */ + spr_register_hv(env, SPR_ASDR, "ASDR", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x0000000000000000); #endif } @@ -8375,6 +8431,7 @@ static void init_proc_power5plus(CPUPPCState *env) gen_spr_power5p_common(env); gen_spr_power5p_lpar(env); gen_spr_power5p_ear(env); + gen_spr_power5p_tb(env); /* env variables */ env->dcache_line_size = 128; @@ -8419,6 +8476,8 @@ POWERPC_FAMILY(POWER5P)(ObjectClass *oc, void *data) (1ull << MSR_DR) | (1ull << MSR_PMM) | (1ull << MSR_RI); + pcc->lpcr_mask = LPCR_RMLS | LPCR_ILE | LPCR_LPES0 | LPCR_LPES1 | + LPCR_RMI | LPCR_HDICE; pcc->mmu_model = POWERPC_MMU_2_03; #if defined(CONFIG_SOFTMMU) pcc->handle_mmu_fault = ppc_hash64_handle_mmu_fault; @@ -8435,44 +8494,6 @@ POWERPC_FAMILY(POWER5P)(ObjectClass *oc, void *data) pcc->l1_icache_size = 0x10000; } -/* - * The CPU used to have a "compat" property which set the - * compatibility mode PVR. However, this was conceptually broken - it - * only makes sense on the pseries machine type (otherwise the guest - * owns the PCR and can control the compatibility mode itself). It's - * been replaced with the 'max-cpu-compat' property on the pseries - * machine type. For backwards compatibility, pseries specially - * parses the -cpu parameter and converts old compat= parameters into - * the appropriate machine parameters. This stub implementation of - * the parameter catches any uses on explicitly created CPUs. - */ -static void getset_compat_deprecated(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - QNull *null = NULL; - - if (!qtest_enabled()) { - warn_report("CPU 'compat' property is deprecated and has no effect; " - "use max-cpu-compat machine property instead"); - } - visit_type_null(v, name, &null, NULL); - qobject_unref(null); -} - -static const PropertyInfo ppc_compat_deprecated_propinfo = { - .name = "str", - .description = "compatibility mode (deprecated)", - .get = getset_compat_deprecated, - .set = getset_compat_deprecated, -}; -static Property powerpc_servercpu_properties[] = { - { - .name = "compat", - .info = &ppc_compat_deprecated_propinfo, - }, - DEFINE_PROP_END_OF_LIST(), -}; - static void init_proc_POWER7(CPUPPCState *env) { /* Common Registers */ @@ -8482,11 +8503,13 @@ static void init_proc_POWER7(CPUPPCState *env) /* POWER7 Specific Registers */ gen_spr_book3s_ids(env); + gen_spr_rmor(env); gen_spr_amr(env); gen_spr_book3s_purr(env); gen_spr_power5p_common(env); gen_spr_power5p_lpar(env); gen_spr_power5p_ear(env); + gen_spr_power5p_tb(env); gen_spr_power6_common(env); gen_spr_power6_dbg(env); gen_spr_power7_book4(env); @@ -8553,7 +8576,6 @@ POWERPC_FAMILY(POWER7)(ObjectClass *oc, void *data) dc->fw_name = "PowerPC,POWER7"; dc->desc = "POWER7"; - dc->props = powerpc_servercpu_properties; pcc->pvr_match = ppc_pvr_match_power7; pcc->pcr_mask = PCR_VEC_DIS | PCR_VSX_DIS | PCR_COMPAT_2_05; pcc->pcr_supported = PCR_COMPAT_2_06 | PCR_COMPAT_2_05; @@ -8594,6 +8616,12 @@ POWERPC_FAMILY(POWER7)(ObjectClass *oc, void *data) (1ull << MSR_PMM) | (1ull << MSR_RI) | (1ull << MSR_LE); + pcc->lpcr_mask = LPCR_VPM0 | LPCR_VPM1 | LPCR_ISL | LPCR_DPFD | + LPCR_VRMASD | LPCR_RMLS | LPCR_ILE | + LPCR_P7_PECE0 | LPCR_P7_PECE1 | LPCR_P7_PECE2 | + LPCR_MER | LPCR_TC | + LPCR_LPES0 | LPCR_LPES1 | LPCR_HDICE; + pcc->lpcr_pm = LPCR_P7_PECE0 | LPCR_P7_PECE1 | LPCR_P7_PECE2; pcc->mmu_model = POWERPC_MMU_2_06; #if defined(CONFIG_SOFTMMU) pcc->handle_mmu_fault = ppc_hash64_handle_mmu_fault; @@ -8610,7 +8638,6 @@ POWERPC_FAMILY(POWER7)(ObjectClass *oc, void *data) pcc->l1_dcache_size = 0x8000; pcc->l1_icache_size = 0x8000; pcc->interrupts_big_endian = ppc_cpu_interrupts_big_endian_lpcr; - pcc->lpcr_pm = LPCR_P7_PECE0 | LPCR_P7_PECE1 | LPCR_P7_PECE2; } static void init_proc_POWER8(CPUPPCState *env) @@ -8622,12 +8649,14 @@ static void init_proc_POWER8(CPUPPCState *env) /* POWER8 Specific Registers */ gen_spr_book3s_ids(env); + gen_spr_rmor(env); gen_spr_amr(env); gen_spr_iamr(env); gen_spr_book3s_purr(env); gen_spr_power5p_common(env); gen_spr_power5p_lpar(env); gen_spr_power5p_ear(env); + gen_spr_power5p_tb(env); gen_spr_power6_common(env); gen_spr_power6_dbg(env); gen_spr_power8_tce_address_control(env); @@ -8717,7 +8746,6 @@ POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data) dc->fw_name = "PowerPC,POWER8"; dc->desc = "POWER8"; - dc->props = powerpc_servercpu_properties; pcc->pvr_match = ppc_pvr_match_power8; pcc->pcr_mask = PCR_TM_DIS | PCR_COMPAT_2_06 | PCR_COMPAT_2_05; pcc->pcr_supported = PCR_COMPAT_2_07 | PCR_COMPAT_2_06 | PCR_COMPAT_2_05; @@ -8745,7 +8773,7 @@ POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data) PPC2_ISA205 | PPC2_ISA207S | PPC2_FP_CVT_S64 | PPC2_TM | PPC2_PM_ISA206; pcc->msr_mask = (1ull << MSR_SF) | - (1ull << MSR_SHV) | + (1ull << MSR_HV) | (1ull << MSR_TM) | (1ull << MSR_VR) | (1ull << MSR_VSX) | @@ -8764,6 +8792,13 @@ POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data) (1ull << MSR_TS0) | (1ull << MSR_TS1) | (1ull << MSR_LE); + pcc->lpcr_mask = LPCR_VPM0 | LPCR_VPM1 | LPCR_ISL | LPCR_KBV | + LPCR_DPFD | LPCR_VRMASD | LPCR_RMLS | LPCR_ILE | + LPCR_AIL | LPCR_ONL | LPCR_P8_PECE0 | LPCR_P8_PECE1 | + LPCR_P8_PECE2 | LPCR_P8_PECE3 | LPCR_P8_PECE4 | + LPCR_MER | LPCR_TC | LPCR_LPES0 | LPCR_HDICE; + pcc->lpcr_pm = LPCR_P8_PECE0 | LPCR_P8_PECE1 | LPCR_P8_PECE2 | + LPCR_P8_PECE3 | LPCR_P8_PECE4; pcc->mmu_model = POWERPC_MMU_2_07; #if defined(CONFIG_SOFTMMU) pcc->handle_mmu_fault = ppc_hash64_handle_mmu_fault; @@ -8781,8 +8816,6 @@ POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data) pcc->l1_dcache_size = 0x8000; pcc->l1_icache_size = 0x8000; pcc->interrupts_big_endian = ppc_cpu_interrupts_big_endian_lpcr; - pcc->lpcr_pm = LPCR_P8_PECE0 | LPCR_P8_PECE1 | LPCR_P8_PECE2 | - LPCR_P8_PECE3 | LPCR_P8_PECE4; } #ifdef CONFIG_SOFTMMU @@ -8818,6 +8851,7 @@ static void init_proc_POWER9(CPUPPCState *env) gen_spr_power5p_common(env); gen_spr_power5p_lpar(env); gen_spr_power5p_ear(env); + gen_spr_power5p_tb(env); gen_spr_power6_common(env); gen_spr_power6_dbg(env); gen_spr_power8_tce_address_control(env); @@ -8928,7 +8962,6 @@ POWERPC_FAMILY(POWER9)(ObjectClass *oc, void *data) dc->fw_name = "PowerPC,POWER9"; dc->desc = "POWER9"; - dc->props = powerpc_servercpu_properties; pcc->pvr_match = ppc_pvr_match_power9; pcc->pcr_mask = PCR_COMPAT_2_05 | PCR_COMPAT_2_06 | PCR_COMPAT_2_07; pcc->pcr_supported = PCR_COMPAT_3_00 | PCR_COMPAT_2_07 | PCR_COMPAT_2_06 | @@ -8957,7 +8990,7 @@ POWERPC_FAMILY(POWER9)(ObjectClass *oc, void *data) PPC2_ISA205 | PPC2_ISA207S | PPC2_FP_CVT_S64 | PPC2_TM | PPC2_ISA300 | PPC2_PRCNTL; pcc->msr_mask = (1ull << MSR_SF) | - (1ull << MSR_SHV) | + (1ull << MSR_HV) | (1ull << MSR_TM) | (1ull << MSR_VR) | (1ull << MSR_VSX) | @@ -8974,6 +9007,14 @@ POWERPC_FAMILY(POWER9)(ObjectClass *oc, void *data) (1ull << MSR_PMM) | (1ull << MSR_RI) | (1ull << MSR_LE); + pcc->lpcr_mask = LPCR_VPM1 | LPCR_ISL | LPCR_KBV | LPCR_DPFD | + (LPCR_PECE_U_MASK & LPCR_HVEE) | LPCR_ILE | LPCR_AIL | + LPCR_UPRT | LPCR_EVIRT | LPCR_ONL | LPCR_HR | LPCR_LD | + (LPCR_PECE_L_MASK & (LPCR_PDEE | LPCR_HDEE | LPCR_EEE | + LPCR_DEE | LPCR_OEE)) + | LPCR_MER | LPCR_GTSE | LPCR_TC | + LPCR_HEIC | LPCR_LPES0 | LPCR_HVICE | LPCR_HDICE; + pcc->lpcr_pm = LPCR_PDEE | LPCR_HDEE | LPCR_EEE | LPCR_DEE | LPCR_OEE; pcc->mmu_model = POWERPC_MMU_3_00; #if defined(CONFIG_SOFTMMU) pcc->handle_mmu_fault = ppc64_v3_handle_mmu_fault; @@ -8993,7 +9034,222 @@ POWERPC_FAMILY(POWER9)(ObjectClass *oc, void *data) pcc->l1_dcache_size = 0x8000; pcc->l1_icache_size = 0x8000; pcc->interrupts_big_endian = ppc_cpu_interrupts_big_endian_lpcr; +} + +#ifdef CONFIG_SOFTMMU +/* + * Radix pg sizes and AP encodings for dt node ibm,processor-radix-AP-encodings + * Encoded as array of int_32s in the form: + * 0bxxxyyyyyyyyyyyyyyyyyyyyyyyyyyyyy + * x -> AP encoding + * y -> radix mode supported page size (encoded as a shift) + */ +static struct ppc_radix_page_info POWER10_radix_page_info = { + .count = 4, + .entries = { + 0x0000000c, /* 4K - enc: 0x0 */ + 0xa0000010, /* 64K - enc: 0x5 */ + 0x20000015, /* 2M - enc: 0x1 */ + 0x4000001e /* 1G - enc: 0x2 */ + } +}; +#endif /* CONFIG_SOFTMMU */ + +static void init_proc_POWER10(CPUPPCState *env) +{ + /* Common Registers */ + init_proc_book3s_common(env); + gen_spr_book3s_207_dbg(env); + + /* POWER8 Specific Registers */ + gen_spr_book3s_ids(env); + gen_spr_amr(env); + gen_spr_iamr(env); + gen_spr_book3s_purr(env); + gen_spr_power5p_common(env); + gen_spr_power5p_lpar(env); + gen_spr_power5p_ear(env); + gen_spr_power6_common(env); + gen_spr_power6_dbg(env); + gen_spr_power8_tce_address_control(env); + gen_spr_power8_ids(env); + gen_spr_power8_ebb(env); + gen_spr_power8_fscr(env); + gen_spr_power8_pmu_sup(env); + gen_spr_power8_pmu_user(env); + gen_spr_power8_tm(env); + gen_spr_power8_pspb(env); + gen_spr_vtb(env); + gen_spr_power8_ic(env); + gen_spr_power8_book4(env); + gen_spr_power8_rpr(env); + gen_spr_power9_mmu(env); + + /* POWER9 Specific registers */ + spr_register_kvm(env, SPR_TIDR, "TIDR", NULL, NULL, + spr_read_generic, spr_write_generic, + KVM_REG_PPC_TIDR, 0); + + /* FIXME: Filter fields properly based on privilege level */ + spr_register_kvm_hv(env, SPR_PSSCR, "PSSCR", NULL, NULL, NULL, NULL, + spr_read_generic, spr_write_generic, + KVM_REG_PPC_PSSCR, 0); + + /* env variables */ + env->dcache_line_size = 128; + env->icache_line_size = 128; + + /* Allocate hardware IRQ controller */ + init_excp_POWER10(env); + ppcPOWER9_irq_init(env_archcpu(env)); +} + +static bool ppc_pvr_match_power10(PowerPCCPUClass *pcc, uint32_t pvr) +{ + if ((pvr & CPU_POWERPC_POWER_SERVER_MASK) == CPU_POWERPC_POWER10_BASE) { + return true; + } + return false; +} + +static bool cpu_has_work_POWER10(CPUState *cs) +{ + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; + + if (cs->halted) { + uint64_t psscr = env->spr[SPR_PSSCR]; + + if (!(cs->interrupt_request & CPU_INTERRUPT_HARD)) { + return false; + } + + /* If EC is clear, just return true on any pending interrupt */ + if (!(psscr & PSSCR_EC)) { + return true; + } + /* External Exception */ + if ((env->pending_interrupts & (1u << PPC_INTERRUPT_EXT)) && + (env->spr[SPR_LPCR] & LPCR_EEE)) { + bool heic = !!(env->spr[SPR_LPCR] & LPCR_HEIC); + if (heic == 0 || !msr_hv || msr_pr) { + return true; + } + } + /* Decrementer Exception */ + if ((env->pending_interrupts & (1u << PPC_INTERRUPT_DECR)) && + (env->spr[SPR_LPCR] & LPCR_DEE)) { + return true; + } + /* Machine Check or Hypervisor Maintenance Exception */ + if ((env->pending_interrupts & (1u << PPC_INTERRUPT_MCK | + 1u << PPC_INTERRUPT_HMI)) && (env->spr[SPR_LPCR] & LPCR_OEE)) { + return true; + } + /* Privileged Doorbell Exception */ + if ((env->pending_interrupts & (1u << PPC_INTERRUPT_DOORBELL)) && + (env->spr[SPR_LPCR] & LPCR_PDEE)) { + return true; + } + /* Hypervisor Doorbell Exception */ + if ((env->pending_interrupts & (1u << PPC_INTERRUPT_HDOORBELL)) && + (env->spr[SPR_LPCR] & LPCR_HDEE)) { + return true; + } + /* Hypervisor virtualization exception */ + if ((env->pending_interrupts & (1u << PPC_INTERRUPT_HVIRT)) && + (env->spr[SPR_LPCR] & LPCR_HVEE)) { + return true; + } + if (env->pending_interrupts & (1u << PPC_INTERRUPT_RESET)) { + return true; + } + return false; + } else { + return msr_ee && (cs->interrupt_request & CPU_INTERRUPT_HARD); + } +} + +POWERPC_FAMILY(POWER10)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + CPUClass *cc = CPU_CLASS(oc); + + dc->fw_name = "PowerPC,POWER10"; + dc->desc = "POWER10"; + pcc->pvr_match = ppc_pvr_match_power10; + pcc->pcr_mask = PCR_COMPAT_2_05 | PCR_COMPAT_2_06 | PCR_COMPAT_2_07 | + PCR_COMPAT_3_00; + pcc->pcr_supported = PCR_COMPAT_3_10 | PCR_COMPAT_3_00 | PCR_COMPAT_2_07 | + PCR_COMPAT_2_06 | PCR_COMPAT_2_05; + pcc->init_proc = init_proc_POWER10; + pcc->check_pow = check_pow_nocheck; + cc->has_work = cpu_has_work_POWER10; + pcc->insns_flags = PPC_INSNS_BASE | PPC_ISEL | PPC_STRING | PPC_MFTB | + PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | + PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | + PPC_FLOAT_FRSQRTES | + PPC_FLOAT_STFIWX | + PPC_FLOAT_EXT | + PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | + PPC_MEM_SYNC | PPC_MEM_EIEIO | + PPC_MEM_TLBSYNC | + PPC_64B | PPC_64H | PPC_64BX | PPC_ALTIVEC | + PPC_SEGMENT_64B | PPC_SLBI | + PPC_POPCNTB | PPC_POPCNTWD | + PPC_CILDST; + pcc->insns_flags2 = PPC2_VSX | PPC2_VSX207 | PPC2_DFP | PPC2_DBRX | + PPC2_PERM_ISA206 | PPC2_DIVE_ISA206 | + PPC2_ATOMIC_ISA206 | PPC2_FP_CVT_ISA206 | + PPC2_FP_TST_ISA206 | PPC2_BCTAR_ISA207 | + PPC2_LSQ_ISA207 | PPC2_ALTIVEC_207 | + PPC2_ISA205 | PPC2_ISA207S | PPC2_FP_CVT_S64 | + PPC2_TM | PPC2_ISA300 | PPC2_PRCNTL; + pcc->msr_mask = (1ull << MSR_SF) | + (1ull << MSR_HV) | + (1ull << MSR_TM) | + (1ull << MSR_VR) | + (1ull << MSR_VSX) | + (1ull << MSR_EE) | + (1ull << MSR_PR) | + (1ull << MSR_FP) | + (1ull << MSR_ME) | + (1ull << MSR_FE0) | + (1ull << MSR_SE) | + (1ull << MSR_DE) | + (1ull << MSR_FE1) | + (1ull << MSR_IR) | + (1ull << MSR_DR) | + (1ull << MSR_PMM) | + (1ull << MSR_RI) | + (1ull << MSR_LE); + pcc->lpcr_mask = LPCR_VPM1 | LPCR_ISL | LPCR_KBV | LPCR_DPFD | + (LPCR_PECE_U_MASK & LPCR_HVEE) | LPCR_ILE | LPCR_AIL | + LPCR_UPRT | LPCR_EVIRT | LPCR_ONL | LPCR_HR | LPCR_LD | + (LPCR_PECE_L_MASK & (LPCR_PDEE | LPCR_HDEE | LPCR_EEE | + LPCR_DEE | LPCR_OEE)) + | LPCR_MER | LPCR_GTSE | LPCR_TC | + LPCR_HEIC | LPCR_LPES0 | LPCR_HVICE | LPCR_HDICE; pcc->lpcr_pm = LPCR_PDEE | LPCR_HDEE | LPCR_EEE | LPCR_DEE | LPCR_OEE; + pcc->mmu_model = POWERPC_MMU_3_00; +#if defined(CONFIG_SOFTMMU) + pcc->handle_mmu_fault = ppc64_v3_handle_mmu_fault; + /* segment page size remain the same */ + pcc->hash64_opts = &ppc_hash64_opts_POWER7; + pcc->radix_page_info = &POWER10_radix_page_info; + pcc->lrg_decr_bits = 56; +#endif + pcc->excp_model = POWERPC_EXCP_POWER9; + pcc->bus_model = PPC_FLAGS_INPUT_POWER9; + pcc->bfd_mach = bfd_mach_ppc64; + pcc->flags = POWERPC_FLAG_VRE | POWERPC_FLAG_SE | + POWERPC_FLAG_BE | POWERPC_FLAG_PMM | + POWERPC_FLAG_BUS_CLK | POWERPC_FLAG_CFAR | + POWERPC_FLAG_VSX | POWERPC_FLAG_TM; + pcc->l1_dcache_size = 0x8000; + pcc->l1_icache_size = 0x8000; + pcc->interrupts_big_endian = ppc_cpu_interrupts_big_endian_lpcr; } #if !defined(CONFIG_USER_ONLY) @@ -9587,7 +9843,7 @@ static int gdb_find_spr_idx(CPUPPCState *env, int n) return -1; } -static int gdb_get_spr_reg(CPUPPCState *env, uint8_t *mem_buf, int n) +static int gdb_get_spr_reg(CPUPPCState *env, GByteArray *buf, int n) { int reg; int len; @@ -9598,8 +9854,8 @@ static int gdb_get_spr_reg(CPUPPCState *env, uint8_t *mem_buf, int n) } len = TARGET_LONG_SIZE; - stn_p(mem_buf, len, env->spr[reg]); - ppc_maybe_bswap_register(env, mem_buf, len); + gdb_get_regl(buf, env->spr[reg]); + ppc_maybe_bswap_register(env, gdb_get_reg_ptr(buf, len), len); return len; } @@ -9621,15 +9877,18 @@ static int gdb_set_spr_reg(CPUPPCState *env, uint8_t *mem_buf, int n) } #endif -static int gdb_get_float_reg(CPUPPCState *env, uint8_t *mem_buf, int n) +static int gdb_get_float_reg(CPUPPCState *env, GByteArray *buf, int n) { + uint8_t *mem_buf; if (n < 32) { - stfq_p(mem_buf, *cpu_fpr_ptr(env, n)); + gdb_get_reg64(buf, *cpu_fpr_ptr(env, n)); + mem_buf = gdb_get_reg_ptr(buf, 8); ppc_maybe_bswap_register(env, mem_buf, 8); return 8; } if (n == 32) { - stl_p(mem_buf, env->fpscr); + gdb_get_reg32(buf, env->fpscr); + mem_buf = gdb_get_reg_ptr(buf, 4); ppc_maybe_bswap_register(env, mem_buf, 4); return 4; } @@ -9651,28 +9910,31 @@ static int gdb_set_float_reg(CPUPPCState *env, uint8_t *mem_buf, int n) return 0; } -static int gdb_get_avr_reg(CPUPPCState *env, uint8_t *mem_buf, int n) +static int gdb_get_avr_reg(CPUPPCState *env, GByteArray *buf, int n) { + uint8_t *mem_buf; + if (n < 32) { ppc_avr_t *avr = cpu_avr_ptr(env, n); if (!avr_need_swap(env)) { - stq_p(mem_buf, avr->u64[0]); - stq_p(mem_buf + 8, avr->u64[1]); + gdb_get_reg128(buf, avr->u64[0] , avr->u64[1]); } else { - stq_p(mem_buf, avr->u64[1]); - stq_p(mem_buf + 8, avr->u64[0]); + gdb_get_reg128(buf, avr->u64[1] , avr->u64[0]); } + mem_buf = gdb_get_reg_ptr(buf, 16); ppc_maybe_bswap_register(env, mem_buf, 8); ppc_maybe_bswap_register(env, mem_buf + 8, 8); return 16; } if (n == 32) { - stl_p(mem_buf, helper_mfvscr(env)); + gdb_get_reg32(buf, helper_mfvscr(env)); + mem_buf = gdb_get_reg_ptr(buf, 4); ppc_maybe_bswap_register(env, mem_buf, 4); return 4; } if (n == 33) { - stl_p(mem_buf, (uint32_t)env->spr[SPR_VRSAVE]); + gdb_get_reg32(buf, (uint32_t)env->spr[SPR_VRSAVE]); + mem_buf = gdb_get_reg_ptr(buf, 4); ppc_maybe_bswap_register(env, mem_buf, 4); return 4; } @@ -9707,25 +9969,25 @@ static int gdb_set_avr_reg(CPUPPCState *env, uint8_t *mem_buf, int n) return 0; } -static int gdb_get_spe_reg(CPUPPCState *env, uint8_t *mem_buf, int n) +static int gdb_get_spe_reg(CPUPPCState *env, GByteArray *buf, int n) { if (n < 32) { #if defined(TARGET_PPC64) - stl_p(mem_buf, env->gpr[n] >> 32); - ppc_maybe_bswap_register(env, mem_buf, 4); + gdb_get_reg32(buf, env->gpr[n] >> 32); + ppc_maybe_bswap_register(env, gdb_get_reg_ptr(buf, 4), 4); #else - stl_p(mem_buf, env->gprh[n]); + gdb_get_reg32(buf, env->gprh[n]); #endif return 4; } if (n == 32) { - stq_p(mem_buf, env->spe_acc); - ppc_maybe_bswap_register(env, mem_buf, 8); + gdb_get_reg64(buf, env->spe_acc); + ppc_maybe_bswap_register(env, gdb_get_reg_ptr(buf, 8), 8); return 8; } if (n == 33) { - stl_p(mem_buf, env->spe_fscr); - ppc_maybe_bswap_register(env, mem_buf, 4); + gdb_get_reg32(buf, env->spe_fscr); + ppc_maybe_bswap_register(env, gdb_get_reg_ptr(buf, 4), 4); return 4; } return 0; @@ -9760,11 +10022,11 @@ static int gdb_set_spe_reg(CPUPPCState *env, uint8_t *mem_buf, int n) return 0; } -static int gdb_get_vsx_reg(CPUPPCState *env, uint8_t *mem_buf, int n) +static int gdb_get_vsx_reg(CPUPPCState *env, GByteArray *buf, int n) { if (n < 32) { - stq_p(mem_buf, *cpu_vsrl_ptr(env, n)); - ppc_maybe_bswap_register(env, mem_buf, 8); + gdb_get_reg64(buf, *cpu_vsrl_ptr(env, n)); + ppc_maybe_bswap_register(env, gdb_get_reg_ptr(buf, 8), 8); return 8; } return 0; @@ -10216,6 +10478,8 @@ static void ppc_cpu_parse_featurestr(const char *type, char *features, *s = '\0'; for (i = 0; inpieces[i]; i++) { if (g_str_has_prefix(inpieces[i], "compat=")) { + warn_report_once("CPU 'compat' property is deprecated; " + "use max-cpu-compat machine property instead"); compat_str = inpieces[i]; continue; } @@ -10399,16 +10663,16 @@ static bool ppc_cpu_has_work(CPUState *cs) return msr_ee && (cs->interrupt_request & CPU_INTERRUPT_HARD); } -/* CPUClass::reset() */ -static void ppc_cpu_reset(CPUState *s) +static void ppc_cpu_reset(DeviceState *dev) { + CPUState *s = CPU(dev); PowerPCCPU *cpu = POWERPC_CPU(s); PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); CPUPPCState *env = &cpu->env; target_ulong msr; int i; - pcc->parent_reset(s); + pcc->parent_reset(dev); msr = (target_ulong)0; msr |= (target_ulong)MSR_HVB; @@ -10461,6 +10725,7 @@ static void ppc_cpu_reset(CPUState *s) env->pending_interrupts = 0; s->exception_index = POWERPC_EXCP_NONE; env->error_code = 0; + ppc_irq_reset(cpu); /* tininess for underflow is detected before rounding */ set_float_detect_tininess(float_tininess_before_rounding, @@ -10612,10 +10877,9 @@ static void ppc_cpu_class_init(ObjectClass *oc, void *data) &pcc->parent_unrealize); pcc->pvr_match = ppc_pvr_match_default; pcc->interrupts_big_endian = ppc_cpu_interrupts_big_endian_always; - dc->props = ppc_cpu_properties; + device_class_set_props(dc, ppc_cpu_properties); - pcc->parent_reset = cc->reset; - cc->reset = ppc_cpu_reset; + device_class_set_parent_reset(dc, ppc_cpu_reset, &pcc->parent_reset); cc->class_by_name = ppc_cpu_class_by_name; pcc->parent_parse_features = cc->parse_features; diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index d37861a430..4e578239d3 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -66,20 +66,28 @@ const char * const riscv_excp_names[] = { "exec_page_fault", "load_page_fault", "reserved", - "store_page_fault" + "store_page_fault", + "reserved", + "reserved", + "reserved", + "reserved", + "guest_exec_page_fault", + "guest_load_page_fault", + "reserved", + "guest_store_page_fault", }; const char * const riscv_intr_names[] = { "u_software", "s_software", - "h_software", + "vs_software", "m_software", "u_timer", "s_timer", - "h_timer", + "vs_timer", "m_timer", "u_external", - "s_external", + "vs_external", "h_external", "m_external", "reserved", @@ -220,17 +228,53 @@ static void riscv_cpu_dump_state(CPUState *cs, FILE *f, int flags) CPURISCVState *env = &cpu->env; int i; +#if !defined(CONFIG_USER_ONLY) + if (riscv_has_ext(env, RVH)) { + qemu_fprintf(f, " %s %d\n", "V = ", riscv_cpu_virt_enabled(env)); + } +#endif qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "pc ", env->pc); #ifndef CONFIG_USER_ONLY qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mhartid ", env->mhartid); qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mstatus ", env->mstatus); - qemu_fprintf(f, " %s 0x%x\n", "mip ", env->mip); +#ifdef TARGET_RISCV32 + qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mstatush ", env->mstatush); +#endif + if (riscv_has_ext(env, RVH)) { + qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "hstatus ", env->hstatus); + qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "vsstatus ", env->vsstatus); + } + qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mip ", env->mip); qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mie ", env->mie); qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mideleg ", env->mideleg); + if (riscv_has_ext(env, RVH)) { + qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "hideleg ", env->hideleg); + } qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "medeleg ", env->medeleg); + if (riscv_has_ext(env, RVH)) { + qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "hedeleg ", env->hedeleg); + } qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mtvec ", env->mtvec); + qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "stvec ", env->stvec); + if (riscv_has_ext(env, RVH)) { + qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "vstvec ", env->vstvec); + } qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mepc ", env->mepc); + qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "sepc ", env->sepc); + if (riscv_has_ext(env, RVH)) { + qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "vsepc ", env->vsepc); + } qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mcause ", env->mcause); + qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "scause ", env->scause); + if (riscv_has_ext(env, RVH)) { + qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "vscause ", env->vscause); + } + qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mtval ", env->mtval); + qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "stval ", env->sbadaddr); + if (riscv_has_ext(env, RVH)) { + qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "htval ", env->htval); + qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mtval2 ", env->mtval2); + } #endif for (i = 0; i < 32; i++) { @@ -286,13 +330,14 @@ void restore_state_to_opc(CPURISCVState *env, TranslationBlock *tb, env->pc = data[0]; } -static void riscv_cpu_reset(CPUState *cs) +static void riscv_cpu_reset(DeviceState *dev) { + CPUState *cs = CPU(dev); RISCVCPU *cpu = RISCV_CPU(cs); RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(cpu); CPURISCVState *env = &cpu->env; - mcc->parent_reset(cs); + mcc->parent_reset(dev); #ifndef CONFIG_USER_ONLY env->priv = PRV_M; env->mstatus &= ~(MSTATUS_MIE | MSTATUS_MPRV); @@ -409,6 +454,9 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp) if (cpu->cfg.ext_u) { target_misa |= RVU; } + if (cpu->cfg.ext_h) { + target_misa |= RVH; + } set_misa(env, RVXLEN | target_misa); } @@ -444,6 +492,8 @@ static Property riscv_cpu_properties[] = { DEFINE_PROP_BOOL("c", RISCVCPU, cfg.ext_c, true), DEFINE_PROP_BOOL("s", RISCVCPU, cfg.ext_s, true), DEFINE_PROP_BOOL("u", RISCVCPU, cfg.ext_u, true), + /* This is experimental so mark with 'x-' */ + DEFINE_PROP_BOOL("x-h", RISCVCPU, cfg.ext_h, false), DEFINE_PROP_BOOL("Counters", RISCVCPU, cfg.ext_counters, true), DEFINE_PROP_BOOL("Zifencei", RISCVCPU, cfg.ext_ifencei, true), DEFINE_PROP_BOOL("Zicsr", RISCVCPU, cfg.ext_icsr, true), @@ -462,8 +512,7 @@ static void riscv_cpu_class_init(ObjectClass *c, void *data) device_class_set_parent_realize(dc, riscv_cpu_realize, &mcc->parent_realize); - mcc->parent_reset = cc->reset; - cc->reset = riscv_cpu_reset; + device_class_set_parent_reset(dc, riscv_cpu_reset, &mcc->parent_reset); cc->class_by_name = riscv_cpu_class_by_name; cc->has_work = riscv_cpu_has_work; @@ -493,7 +542,7 @@ static void riscv_cpu_class_init(ObjectClass *c, void *data) #endif /* For now, mark unmigratable: */ cc->vmsd = &vmstate_riscv_cpu; - dc->props = riscv_cpu_properties; + device_class_set_props(dc, riscv_cpu_properties); } char *riscv_isa_string(RISCVCPU *cpu) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index e59343e13c..7d21addbab 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -67,6 +67,7 @@ #define RVC RV('C') #define RVS RV('S') #define RVU RV('U') +#define RVH RV('H') /* S extension denotes that Supervisor mode exists, however it is possible to have a core that support S mode but does not have an MMU and there @@ -103,6 +104,7 @@ struct CPURISCVState { target_ulong frm; target_ulong badaddr; + target_ulong guest_phys_fault_addr; target_ulong priv_ver; target_ulong misa; @@ -116,12 +118,19 @@ struct CPURISCVState { #ifndef CONFIG_USER_ONLY target_ulong priv; + /* This contains QEMU specific information about the virt state. */ + target_ulong virt; target_ulong resetvec; target_ulong mhartid; target_ulong mstatus; - uint32_t mip; + target_ulong mip; + +#ifdef TARGET_RISCV32 + target_ulong mstatush; +#endif + uint32_t miclaim; target_ulong mie; @@ -142,6 +151,43 @@ struct CPURISCVState { target_ulong mcause; target_ulong mtval; /* since: priv-1.10.0 */ + /* Hypervisor CSRs */ + target_ulong hstatus; + target_ulong hedeleg; + target_ulong hideleg; + target_ulong hcounteren; + target_ulong htval; + target_ulong htinst; + target_ulong hgatp; + uint64_t htimedelta; + + /* Virtual CSRs */ + target_ulong vsstatus; + target_ulong vstvec; + target_ulong vsscratch; + target_ulong vsepc; + target_ulong vscause; + target_ulong vstval; + target_ulong vsatp; +#ifdef TARGET_RISCV32 + target_ulong vsstatush; +#endif + + target_ulong mtval2; + target_ulong mtinst; + + /* HS Backup CSRs */ + target_ulong stvec_hs; + target_ulong sscratch_hs; + target_ulong sepc_hs; + target_ulong scause_hs; + target_ulong stval_hs; + target_ulong satp_hs; + target_ulong mstatus_hs; +#ifdef TARGET_RISCV32 + target_ulong mstatush_hs; +#endif + target_ulong scounteren; target_ulong mcounteren; @@ -156,6 +202,9 @@ struct CPURISCVState { /* physical memory protection */ pmp_table_t pmp_state; + /* machine specific rdtime callback */ + uint64_t (*rdtime_fn)(void); + /* True if in debugger mode. */ bool debugger; #endif @@ -185,7 +234,7 @@ typedef struct RISCVCPUClass { CPUClass parent_class; /*< public >*/ DeviceRealize parent_realize; - void (*parent_reset)(CPUState *cpu); + DeviceReset parent_reset; } RISCVCPUClass; /** @@ -213,6 +262,7 @@ typedef struct RISCVCPU { bool ext_c; bool ext_s; bool ext_u; + bool ext_h; bool ext_counters; bool ext_ifencei; bool ext_icsr; @@ -243,10 +293,14 @@ extern const char * const riscv_excp_names[]; extern const char * const riscv_intr_names[]; void riscv_cpu_do_interrupt(CPUState *cpu); -int riscv_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); +int riscv_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int riscv_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); bool riscv_cpu_exec_interrupt(CPUState *cs, int interrupt_request); bool riscv_cpu_fp_enabled(CPURISCVState *env); +bool riscv_cpu_virt_enabled(CPURISCVState *env); +void riscv_cpu_set_virt_enabled(CPURISCVState *env, bool enable); +bool riscv_cpu_force_hs_excep_enabled(CPURISCVState *env); +void riscv_cpu_set_force_hs_excep(CPURISCVState *env, bool enable); int riscv_cpu_mmu_index(CPURISCVState *env, bool ifetch); hwaddr riscv_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); void riscv_cpu_do_unaligned_access(CPUState *cs, vaddr addr, @@ -268,9 +322,11 @@ void riscv_cpu_list(void); #define cpu_mmu_index riscv_cpu_mmu_index #ifndef CONFIG_USER_ONLY +void riscv_cpu_swap_hypervisor_regs(CPURISCVState *env); int riscv_cpu_claim_interrupts(RISCVCPU *cpu, uint32_t interrupts); uint32_t riscv_cpu_update_mip(RISCVCPU *cpu, uint32_t mask, uint32_t value); #define BOOL_TO_MASK(x) (-!!(x)) /* helper for riscv_cpu_update_mip value */ +void riscv_cpu_set_rdtime_fn(CPURISCVState *env, uint64_t (*fn)(void)); #endif void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv); @@ -295,7 +351,7 @@ static inline void cpu_get_tb_cpu_state(CPURISCVState *env, target_ulong *pc, #else *flags = cpu_mmu_index(env, 0); if (riscv_cpu_fp_enabled(env)) { - *flags |= TB_FLAGS_MSTATUS_FS; + *flags |= env->mstatus & MSTATUS_FS; } #endif } diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h index e99834856c..7f64ee1174 100644 --- a/target/riscv/cpu_bits.h +++ b/target/riscv/cpu_bits.h @@ -135,6 +135,9 @@ #define CSR_MTVEC 0x305 #define CSR_MCOUNTEREN 0x306 +/* 32-bit only */ +#define CSR_MSTATUSH 0x310 + /* Legacy Counter Setup (priv v1.9.1) */ /* Update to #define CSR_MCOUNTINHIBIT 0x320 for 1.11.0 */ #define CSR_MUCOUNTEREN 0x320 @@ -177,8 +180,14 @@ #define CSR_HSTATUS 0x600 #define CSR_HEDELEG 0x602 #define CSR_HIDELEG 0x603 -#define CSR_HCOUNTERNEN 0x606 +#define CSR_HIE 0x604 +#define CSR_HCOUNTEREN 0x606 +#define CSR_HTVAL 0x643 +#define CSR_HIP 0x644 +#define CSR_HTINST 0x64A #define CSR_HGATP 0x680 +#define CSR_HTIMEDELTA 0x605 +#define CSR_HTIMEDELTAH 0x615 #if defined(TARGET_RISCV32) #define HGATP_MODE SATP32_MODE @@ -191,6 +200,20 @@ #define HGATP_PPN SATP64_PPN #endif +/* Virtual CSRs */ +#define CSR_VSSTATUS 0x200 +#define CSR_VSIE 0x204 +#define CSR_VSTVEC 0x205 +#define CSR_VSSCRATCH 0x240 +#define CSR_VSEPC 0x241 +#define CSR_VSCAUSE 0x242 +#define CSR_VSTVAL 0x243 +#define CSR_VSIP 0x244 +#define CSR_VSATP 0x280 + +#define CSR_MTINST 0x34a +#define CSR_MTVAL2 0x34b + /* Physical Memory Protection */ #define CSR_PMPCFG0 0x3a0 #define CSR_PMPCFG1 0x3a1 @@ -313,17 +336,6 @@ #define CSR_MHPMCOUNTER30H 0xb9e #define CSR_MHPMCOUNTER31H 0xb9f -/* Legacy Hypervisor Trap Setup (priv v1.9.1) */ -#define CSR_HIE 0x204 -#define CSR_HTVEC 0x205 - -/* Legacy Hypervisor Trap Handling (priv v1.9.1) */ -#define CSR_HSCRATCH 0x240 -#define CSR_HEPC 0x241 -#define CSR_HCAUSE 0x242 -#define CSR_HBADADDR 0x243 -#define CSR_HIP 0x244 - /* Legacy Machine Protection and Translation (priv v1.9.1) */ #define CSR_MBASE 0x380 #define CSR_MBOUND 0x381 @@ -351,8 +363,19 @@ #define MSTATUS_TVM 0x00100000 /* since: priv-1.10 */ #define MSTATUS_TW 0x20000000 /* since: priv-1.10 */ #define MSTATUS_TSR 0x40000000 /* since: priv-1.10 */ +#if defined(TARGET_RISCV64) #define MSTATUS_MTL 0x4000000000ULL #define MSTATUS_MPV 0x8000000000ULL +#elif defined(TARGET_RISCV32) +#define MSTATUS_MTL 0x00000040 +#define MSTATUS_MPV 0x00000080 +#endif + +#ifdef TARGET_RISCV32 +# define MSTATUS_MPV_ISSET(env) get_field(env->mstatush, MSTATUS_MPV) +#else +# define MSTATUS_MPV_ISSET(env) get_field(env->mstatus, MSTATUS_MPV) +#endif #define MSTATUS64_UXL 0x0000000300000000ULL #define MSTATUS64_SXL 0x0000000C00000000ULL @@ -400,7 +423,6 @@ /* hstatus CSR bits */ #define HSTATUS_SPRV 0x00000001 -#define HSTATUS_STL 0x00000040 #define HSTATUS_SPV 0x00000080 #define HSTATUS_SP2P 0x00000100 #define HSTATUS_SP2V 0x00000200 @@ -422,6 +444,15 @@ #define PRV_H 2 /* Reserved */ #define PRV_M 3 +/* Virtulisation Register Fields */ +#define VIRT_ONOFF 1 +/* This is used to save state for when we take an exception. If this is set + * that means that we want to force a HS level exception (no matter what the + * delegation is set to). This will occur for things such as a second level + * page table fault. + */ +#define FORCE_HS_EXCEP 2 + /* RV32 satp CSR field masks */ #define SATP32_MODE 0x80000000 #define SATP32_ASID 0x7fc00000 @@ -480,22 +511,25 @@ #define DEFAULT_RSTVEC 0x1000 /* Exception causes */ -#define EXCP_NONE -1 /* sentinel value */ -#define RISCV_EXCP_INST_ADDR_MIS 0x0 -#define RISCV_EXCP_INST_ACCESS_FAULT 0x1 -#define RISCV_EXCP_ILLEGAL_INST 0x2 -#define RISCV_EXCP_BREAKPOINT 0x3 -#define RISCV_EXCP_LOAD_ADDR_MIS 0x4 -#define RISCV_EXCP_LOAD_ACCESS_FAULT 0x5 -#define RISCV_EXCP_STORE_AMO_ADDR_MIS 0x6 -#define RISCV_EXCP_STORE_AMO_ACCESS_FAULT 0x7 -#define RISCV_EXCP_U_ECALL 0x8 -#define RISCV_EXCP_S_ECALL 0x9 -#define RISCV_EXCP_H_ECALL 0xa -#define RISCV_EXCP_M_ECALL 0xb -#define RISCV_EXCP_INST_PAGE_FAULT 0xc /* since: priv-1.10.0 */ -#define RISCV_EXCP_LOAD_PAGE_FAULT 0xd /* since: priv-1.10.0 */ -#define RISCV_EXCP_STORE_PAGE_FAULT 0xf /* since: priv-1.10.0 */ +#define EXCP_NONE -1 /* sentinel value */ +#define RISCV_EXCP_INST_ADDR_MIS 0x0 +#define RISCV_EXCP_INST_ACCESS_FAULT 0x1 +#define RISCV_EXCP_ILLEGAL_INST 0x2 +#define RISCV_EXCP_BREAKPOINT 0x3 +#define RISCV_EXCP_LOAD_ADDR_MIS 0x4 +#define RISCV_EXCP_LOAD_ACCESS_FAULT 0x5 +#define RISCV_EXCP_STORE_AMO_ADDR_MIS 0x6 +#define RISCV_EXCP_STORE_AMO_ACCESS_FAULT 0x7 +#define RISCV_EXCP_U_ECALL 0x8 +#define RISCV_EXCP_S_ECALL 0x9 +#define RISCV_EXCP_VS_ECALL 0xa +#define RISCV_EXCP_M_ECALL 0xb +#define RISCV_EXCP_INST_PAGE_FAULT 0xc /* since: priv-1.10.0 */ +#define RISCV_EXCP_LOAD_PAGE_FAULT 0xd /* since: priv-1.10.0 */ +#define RISCV_EXCP_STORE_PAGE_FAULT 0xf /* since: priv-1.10.0 */ +#define RISCV_EXCP_INST_GUEST_PAGE_FAULT 0x14 +#define RISCV_EXCP_LOAD_GUEST_ACCESS_FAULT 0x15 +#define RISCV_EXCP_STORE_GUEST_AMO_ACCESS_FAULT 0x17 #define RISCV_EXCP_INT_FLAG 0x80000000 #define RISCV_EXCP_INT_MASK 0x7fffffff @@ -503,29 +537,29 @@ /* Interrupt causes */ #define IRQ_U_SOFT 0 #define IRQ_S_SOFT 1 -#define IRQ_H_SOFT 2 /* reserved */ +#define IRQ_VS_SOFT 2 #define IRQ_M_SOFT 3 #define IRQ_U_TIMER 4 #define IRQ_S_TIMER 5 -#define IRQ_H_TIMER 6 /* reserved */ +#define IRQ_VS_TIMER 6 #define IRQ_M_TIMER 7 #define IRQ_U_EXT 8 #define IRQ_S_EXT 9 -#define IRQ_H_EXT 10 /* reserved */ +#define IRQ_VS_EXT 10 #define IRQ_M_EXT 11 /* mip masks */ #define MIP_USIP (1 << IRQ_U_SOFT) #define MIP_SSIP (1 << IRQ_S_SOFT) -#define MIP_HSIP (1 << IRQ_H_SOFT) +#define MIP_VSSIP (1 << IRQ_VS_SOFT) #define MIP_MSIP (1 << IRQ_M_SOFT) #define MIP_UTIP (1 << IRQ_U_TIMER) #define MIP_STIP (1 << IRQ_S_TIMER) -#define MIP_HTIP (1 << IRQ_H_TIMER) +#define MIP_VSTIP (1 << IRQ_VS_TIMER) #define MIP_MTIP (1 << IRQ_M_TIMER) #define MIP_UEIP (1 << IRQ_U_EXT) #define MIP_SEIP (1 << IRQ_S_EXT) -#define MIP_HEIP (1 << IRQ_H_EXT) +#define MIP_VSEIP (1 << IRQ_VS_EXT) #define MIP_MEIP (1 << IRQ_M_EXT) /* sip masks */ @@ -533,4 +567,11 @@ #define SIP_STIP MIP_STIP #define SIP_SEIP MIP_SEIP +/* MIE masks */ +#define MIE_SEIE (1 << IRQ_S_EXT) +#define MIE_UEIE (1 << IRQ_U_EXT) +#define MIE_STIE (1 << IRQ_S_TIMER) +#define MIE_UTIE (1 << IRQ_U_TIMER) +#define MIE_SSIE (1 << IRQ_S_SOFT) +#define MIE_USIE (1 << IRQ_U_SOFT) #endif diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 767c8762ac..d3ba9efb02 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -22,7 +22,7 @@ #include "qemu/main-loop.h" #include "cpu.h" #include "exec/exec-all.h" -#include "tcg-op.h" +#include "tcg/tcg-op.h" #include "trace.h" int riscv_cpu_mmu_index(CPURISCVState *env, bool ifetch) @@ -37,13 +37,36 @@ int riscv_cpu_mmu_index(CPURISCVState *env, bool ifetch) #ifndef CONFIG_USER_ONLY static int riscv_cpu_local_irq_pending(CPURISCVState *env) { + target_ulong irqs; + target_ulong mstatus_mie = get_field(env->mstatus, MSTATUS_MIE); target_ulong mstatus_sie = get_field(env->mstatus, MSTATUS_SIE); - target_ulong pending = env->mip & env->mie; - target_ulong mie = env->priv < PRV_M || (env->priv == PRV_M && mstatus_mie); - target_ulong sie = env->priv < PRV_S || (env->priv == PRV_S && mstatus_sie); - target_ulong irqs = (pending & ~env->mideleg & -mie) | - (pending & env->mideleg & -sie); + target_ulong hs_mstatus_sie = get_field(env->mstatus_hs, MSTATUS_SIE); + + target_ulong pending = env->mip & env->mie & + ~(MIP_VSSIP | MIP_VSTIP | MIP_VSEIP); + target_ulong vspending = (env->mip & env->mie & + (MIP_VSSIP | MIP_VSTIP | MIP_VSEIP)); + + target_ulong mie = env->priv < PRV_M || + (env->priv == PRV_M && mstatus_mie); + target_ulong sie = env->priv < PRV_S || + (env->priv == PRV_S && mstatus_sie); + target_ulong hs_sie = env->priv < PRV_S || + (env->priv == PRV_S && hs_mstatus_sie); + + if (riscv_cpu_virt_enabled(env)) { + target_ulong pending_hs_irq = pending & -hs_sie; + + if (pending_hs_irq) { + riscv_cpu_set_force_hs_excep(env, FORCE_HS_EXCEP); + return ctz64(pending_hs_irq); + } + + pending = vspending; + } + + irqs = (pending & ~env->mideleg & -mie) | (pending & env->mideleg & -sie); if (irqs) { return ctz64(irqs); /* since non-zero */ @@ -76,12 +99,127 @@ bool riscv_cpu_exec_interrupt(CPUState *cs, int interrupt_request) bool riscv_cpu_fp_enabled(CPURISCVState *env) { if (env->mstatus & MSTATUS_FS) { + if (riscv_cpu_virt_enabled(env) && !(env->mstatus_hs & MSTATUS_FS)) { + return false; + } return true; } return false; } +void riscv_cpu_swap_hypervisor_regs(CPURISCVState *env) +{ + target_ulong mstatus_mask = MSTATUS_MXR | MSTATUS_SUM | MSTATUS_FS | + MSTATUS_SPP | MSTATUS_SPIE | MSTATUS_SIE; + bool current_virt = riscv_cpu_virt_enabled(env); + + g_assert(riscv_has_ext(env, RVH)); + +#if defined(TARGET_RISCV64) + mstatus_mask |= MSTATUS64_UXL; +#endif + + if (current_virt) { + /* Current V=1 and we are about to change to V=0 */ + env->vsstatus = env->mstatus & mstatus_mask; + env->mstatus &= ~mstatus_mask; + env->mstatus |= env->mstatus_hs; + +#if defined(TARGET_RISCV32) + env->vsstatush = env->mstatush; + env->mstatush |= env->mstatush_hs; +#endif + + env->vstvec = env->stvec; + env->stvec = env->stvec_hs; + + env->vsscratch = env->sscratch; + env->sscratch = env->sscratch_hs; + + env->vsepc = env->sepc; + env->sepc = env->sepc_hs; + + env->vscause = env->scause; + env->scause = env->scause_hs; + + env->vstval = env->sbadaddr; + env->sbadaddr = env->stval_hs; + + env->vsatp = env->satp; + env->satp = env->satp_hs; + } else { + /* Current V=0 and we are about to change to V=1 */ + env->mstatus_hs = env->mstatus & mstatus_mask; + env->mstatus &= ~mstatus_mask; + env->mstatus |= env->vsstatus; + +#if defined(TARGET_RISCV32) + env->mstatush_hs = env->mstatush; + env->mstatush |= env->vsstatush; +#endif + + env->stvec_hs = env->stvec; + env->stvec = env->vstvec; + + env->sscratch_hs = env->sscratch; + env->sscratch = env->vsscratch; + + env->sepc_hs = env->sepc; + env->sepc = env->vsepc; + + env->scause_hs = env->scause; + env->scause = env->vscause; + + env->stval_hs = env->sbadaddr; + env->sbadaddr = env->vstval; + + env->satp_hs = env->satp; + env->satp = env->vsatp; + } +} + +bool riscv_cpu_virt_enabled(CPURISCVState *env) +{ + if (!riscv_has_ext(env, RVH)) { + return false; + } + + return get_field(env->virt, VIRT_ONOFF); +} + +void riscv_cpu_set_virt_enabled(CPURISCVState *env, bool enable) +{ + if (!riscv_has_ext(env, RVH)) { + return; + } + + /* Flush the TLB on all virt mode changes. */ + if (get_field(env->virt, VIRT_ONOFF) != enable) { + tlb_flush(env_cpu(env)); + } + + env->virt = set_field(env->virt, VIRT_ONOFF, enable); +} + +bool riscv_cpu_force_hs_excep_enabled(CPURISCVState *env) +{ + if (!riscv_has_ext(env, RVH)) { + return false; + } + + return get_field(env->virt, FORCE_HS_EXCEP); +} + +void riscv_cpu_set_force_hs_excep(CPURISCVState *env, bool enable) +{ + if (!riscv_has_ext(env, RVH)) { + return; + } + + env->virt = set_field(env->virt, FORCE_HS_EXCEP, enable); +} + int riscv_cpu_claim_interrupts(RISCVCPU *cpu, uint32_t interrupts) { CPURISCVState *env = &cpu->env; @@ -120,6 +258,11 @@ uint32_t riscv_cpu_update_mip(RISCVCPU *cpu, uint32_t mask, uint32_t value) return old; } +void riscv_cpu_set_rdtime_fn(CPURISCVState *env, uint64_t (*fn)(void)) +{ + env->rdtime_fn = fn; +} + void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv) { if (newpriv > PRV_M) { @@ -149,10 +292,20 @@ void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv) * * Adapted from Spike's mmu_t::translate and mmu_t::walk * + * @env: CPURISCVState + * @physical: This will be set to the calculated physical address + * @prot: The returned protection attributes + * @addr: The virtual address to be translated + * @access_type: The type of MMU access + * @mmu_idx: Indicates current privilege level + * @first_stage: Are we in first stage translation? + * Second stage is used for hypervisor guest translation + * @two_stage: Are we going to perform two stage translation */ static int get_physical_address(CPURISCVState *env, hwaddr *physical, int *prot, target_ulong addr, - int access_type, int mmu_idx) + int access_type, int mmu_idx, + bool first_stage, bool two_stage) { /* NOTE: the env->pc value visible here will not be * correct, but the value visible to the exception handler @@ -160,13 +313,40 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical, MemTxResult res; MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED; int mode = mmu_idx; + bool use_background = false; + /* + * Check if we should use the background registers for the two + * stage translation. We don't need to check if we actually need + * two stage translation as that happened before this function + * was called. Background registers will be used if the guest has + * forced a two stage translation to be on (in HS or M mode). + */ if (mode == PRV_M && access_type != MMU_INST_FETCH) { if (get_field(env->mstatus, MSTATUS_MPRV)) { mode = get_field(env->mstatus, MSTATUS_MPP); + + if (riscv_has_ext(env, RVH) && + MSTATUS_MPV_ISSET(env)) { + use_background = true; + } } } + if (mode == PRV_S && access_type != MMU_INST_FETCH && + riscv_has_ext(env, RVH) && !riscv_cpu_virt_enabled(env)) { + if (get_field(env->hstatus, HSTATUS_SPRV)) { + mode = get_field(env->mstatus, SSTATUS_SPP); + use_background = true; + } + } + + if (first_stage == false) { + /* We are in stage 2 translation, this is similar to stage 1. */ + /* Stage 2 is always taken as U-mode */ + mode = PRV_U; + } + if (mode == PRV_M || !riscv_feature(env, RISCV_FEATURE_MMU)) { *physical = addr; *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; @@ -176,13 +356,30 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical, *prot = 0; hwaddr base; - int levels, ptidxbits, ptesize, vm, sum; - int mxr = get_field(env->mstatus, MSTATUS_MXR); + int levels, ptidxbits, ptesize, vm, sum, mxr, widened; + + if (first_stage == true) { + mxr = get_field(env->mstatus, MSTATUS_MXR); + } else { + mxr = get_field(env->vsstatus, MSTATUS_MXR); + } if (env->priv_ver >= PRIV_VERSION_1_10_0) { - base = (hwaddr)get_field(env->satp, SATP_PPN) << PGSHIFT; + if (first_stage == true) { + if (use_background) { + base = (hwaddr)get_field(env->vsatp, SATP_PPN) << PGSHIFT; + vm = get_field(env->vsatp, SATP_MODE); + } else { + base = (hwaddr)get_field(env->satp, SATP_PPN) << PGSHIFT; + vm = get_field(env->satp, SATP_MODE); + } + widened = 0; + } else { + base = (hwaddr)get_field(env->hgatp, HGATP_PPN) << PGSHIFT; + vm = get_field(env->hgatp, HGATP_MODE); + widened = 2; + } sum = get_field(env->mstatus, MSTATUS_SUM); - vm = get_field(env->satp, SATP_MODE); switch (vm) { case VM_1_10_SV32: levels = 2; ptidxbits = 10; ptesize = 4; break; @@ -200,6 +397,7 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical, g_assert_not_reached(); } } else { + widened = 0; base = (hwaddr)(env->sptbr) << PGSHIFT; sum = !get_field(env->mstatus, MSTATUS_PUM); vm = get_field(env->mstatus, MSTATUS_VM); @@ -220,9 +418,16 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical, } CPUState *cs = env_cpu(env); - int va_bits = PGSHIFT + levels * ptidxbits; - target_ulong mask = (1L << (TARGET_LONG_BITS - (va_bits - 1))) - 1; - target_ulong masked_msbs = (addr >> (va_bits - 1)) & mask; + int va_bits = PGSHIFT + levels * ptidxbits + widened; + target_ulong mask, masked_msbs; + + if (TARGET_LONG_BITS > (va_bits - 1)) { + mask = (1L << (TARGET_LONG_BITS - (va_bits - 1))) - 1; + } else { + mask = 0; + } + masked_msbs = (addr >> (va_bits - 1)) & mask; + if (masked_msbs != 0 && masked_msbs != mask) { return TRANSLATE_FAIL; } @@ -234,11 +439,29 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical, restart: #endif for (i = 0; i < levels; i++, ptshift -= ptidxbits) { - target_ulong idx = (addr >> (PGSHIFT + ptshift)) & + target_ulong idx; + if (i == 0) { + idx = (addr >> (PGSHIFT + ptshift)) & + ((1 << (ptidxbits + widened)) - 1); + } else { + idx = (addr >> (PGSHIFT + ptshift)) & ((1 << ptidxbits) - 1); + } /* check that physical address of PTE is legal */ - hwaddr pte_addr = base + idx * ptesize; + hwaddr pte_addr; + + if (two_stage && first_stage) { + hwaddr vbase; + + /* Do the second stage translation on the base PTE address. */ + get_physical_address(env, &vbase, prot, base, access_type, + mmu_idx, false, true); + + pte_addr = vbase + idx * ptesize; + } else { + pte_addr = base + idx * ptesize; + } if (riscv_feature(env, RISCV_FEATURE_PMP) && !pmp_hart_has_privs(env, pte_addr, sizeof(target_ulong), @@ -335,7 +558,12 @@ restart: /* for superpage mappings, make a fake leaf PTE for the TLB's benefit. */ target_ulong vpn = addr >> PGSHIFT; - *physical = (ppn | (vpn & ((1L << ptshift) - 1))) << PGSHIFT; + if (i == 0) { + *physical = (ppn | (vpn & ((1L << (ptshift + widened)) - 1))) << + PGSHIFT; + } else { + *physical = (ppn | (vpn & ((1L << ptshift) - 1))) << PGSHIFT; + } /* set permissions on the TLB entry */ if ((pte & PTE_R) || ((pte & PTE_X) && mxr)) { @@ -357,25 +585,45 @@ restart: } static void raise_mmu_exception(CPURISCVState *env, target_ulong address, - MMUAccessType access_type, bool pmp_violation) + MMUAccessType access_type, bool pmp_violation, + bool first_stage) { CPUState *cs = env_cpu(env); - int page_fault_exceptions = - (env->priv_ver >= PRIV_VERSION_1_10_0) && - get_field(env->satp, SATP_MODE) != VM_1_10_MBARE && - !pmp_violation; + int page_fault_exceptions; + if (first_stage) { + page_fault_exceptions = + (env->priv_ver >= PRIV_VERSION_1_10_0) && + get_field(env->satp, SATP_MODE) != VM_1_10_MBARE && + !pmp_violation; + } else { + page_fault_exceptions = + get_field(env->hgatp, HGATP_MODE) != VM_1_10_MBARE && + !pmp_violation; + } switch (access_type) { case MMU_INST_FETCH: - cs->exception_index = page_fault_exceptions ? - RISCV_EXCP_INST_PAGE_FAULT : RISCV_EXCP_INST_ACCESS_FAULT; + if (riscv_cpu_virt_enabled(env) && !first_stage) { + cs->exception_index = RISCV_EXCP_INST_GUEST_PAGE_FAULT; + } else { + cs->exception_index = page_fault_exceptions ? + RISCV_EXCP_INST_PAGE_FAULT : RISCV_EXCP_INST_ACCESS_FAULT; + } break; case MMU_DATA_LOAD: - cs->exception_index = page_fault_exceptions ? - RISCV_EXCP_LOAD_PAGE_FAULT : RISCV_EXCP_LOAD_ACCESS_FAULT; + if (riscv_cpu_virt_enabled(env) && !first_stage) { + cs->exception_index = RISCV_EXCP_LOAD_GUEST_ACCESS_FAULT; + } else { + cs->exception_index = page_fault_exceptions ? + RISCV_EXCP_LOAD_PAGE_FAULT : RISCV_EXCP_LOAD_ACCESS_FAULT; + } break; case MMU_DATA_STORE: - cs->exception_index = page_fault_exceptions ? - RISCV_EXCP_STORE_PAGE_FAULT : RISCV_EXCP_STORE_AMO_ACCESS_FAULT; + if (riscv_cpu_virt_enabled(env) && !first_stage) { + cs->exception_index = RISCV_EXCP_STORE_GUEST_AMO_ACCESS_FAULT; + } else { + cs->exception_index = page_fault_exceptions ? + RISCV_EXCP_STORE_PAGE_FAULT : RISCV_EXCP_STORE_AMO_ACCESS_FAULT; + } break; default: g_assert_not_reached(); @@ -386,13 +634,23 @@ static void raise_mmu_exception(CPURISCVState *env, target_ulong address, hwaddr riscv_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) { RISCVCPU *cpu = RISCV_CPU(cs); + CPURISCVState *env = &cpu->env; hwaddr phys_addr; int prot; int mmu_idx = cpu_mmu_index(&cpu->env, false); - if (get_physical_address(&cpu->env, &phys_addr, &prot, addr, 0, mmu_idx)) { + if (get_physical_address(env, &phys_addr, &prot, addr, 0, mmu_idx, + true, riscv_cpu_virt_enabled(env))) { return -1; } + + if (riscv_cpu_virt_enabled(env)) { + if (get_physical_address(env, &phys_addr, &prot, phys_addr, + 0, mmu_idx, false, true)) { + return -1; + } + } + return phys_addr; } @@ -446,16 +704,37 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, RISCVCPU *cpu = RISCV_CPU(cs); CPURISCVState *env = &cpu->env; #ifndef CONFIG_USER_ONLY + vaddr im_address; hwaddr pa = 0; int prot; bool pmp_violation = false; + bool m_mode_two_stage = false; + bool hs_mode_two_stage = false; + bool first_stage_error = true; int ret = TRANSLATE_FAIL; int mode = mmu_idx; + env->guest_phys_fault_addr = 0; + qemu_log_mask(CPU_LOG_MMU, "%s ad %" VADDR_PRIx " rw %d mmu_idx %d\n", __func__, address, access_type, mmu_idx); - ret = get_physical_address(env, &pa, &prot, address, access_type, mmu_idx); + /* + * Determine if we are in M mode and MPRV is set or in HS mode and SPRV is + * set and we want to access a virtulisation address. + */ + if (riscv_has_ext(env, RVH)) { + m_mode_two_stage = env->priv == PRV_M && + access_type != MMU_INST_FETCH && + get_field(env->mstatus, MSTATUS_MPRV) && + MSTATUS_MPV_ISSET(env); + + hs_mode_two_stage = env->priv == PRV_S && + !riscv_cpu_virt_enabled(env) && + access_type != MMU_INST_FETCH && + get_field(env->hstatus, HSTATUS_SPRV) && + get_field(env->hstatus, HSTATUS_SPV); + } if (mode == PRV_M && access_type != MMU_INST_FETCH) { if (get_field(env->mstatus, MSTATUS_MPRV)) { @@ -463,9 +742,55 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, } } - qemu_log_mask(CPU_LOG_MMU, - "%s address=%" VADDR_PRIx " ret %d physical " TARGET_FMT_plx - " prot %d\n", __func__, address, ret, pa, prot); + if (riscv_cpu_virt_enabled(env) || m_mode_two_stage || hs_mode_two_stage) { + /* Two stage lookup */ + ret = get_physical_address(env, &pa, &prot, address, access_type, + mmu_idx, true, true); + + qemu_log_mask(CPU_LOG_MMU, + "%s 1st-stage address=%" VADDR_PRIx " ret %d physical " + TARGET_FMT_plx " prot %d\n", + __func__, address, ret, pa, prot); + + if (ret != TRANSLATE_FAIL) { + /* Second stage lookup */ + im_address = pa; + + ret = get_physical_address(env, &pa, &prot, im_address, + access_type, mmu_idx, false, true); + + qemu_log_mask(CPU_LOG_MMU, + "%s 2nd-stage address=%" VADDR_PRIx " ret %d physical " + TARGET_FMT_plx " prot %d\n", + __func__, im_address, ret, pa, prot); + + if (riscv_feature(env, RISCV_FEATURE_PMP) && + (ret == TRANSLATE_SUCCESS) && + !pmp_hart_has_privs(env, pa, size, 1 << access_type, mode)) { + ret = TRANSLATE_PMP_FAIL; + } + + if (ret != TRANSLATE_SUCCESS) { + /* + * Guest physical address translation failed, this is a HS + * level exception + */ + first_stage_error = false; + env->guest_phys_fault_addr = (im_address | + (address & + (TARGET_PAGE_SIZE - 1))) >> 2; + } + } + } else { + /* Single stage lookup */ + ret = get_physical_address(env, &pa, &prot, address, access_type, + mmu_idx, true, false); + + qemu_log_mask(CPU_LOG_MMU, + "%s address=%" VADDR_PRIx " ret %d physical " + TARGET_FMT_plx " prot %d\n", + __func__, address, ret, pa, prot); + } if (riscv_feature(env, RISCV_FEATURE_PMP) && (ret == TRANSLATE_SUCCESS) && @@ -475,6 +800,7 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, if (ret == TRANSLATE_PMP_FAIL) { pmp_violation = true; } + if (ret == TRANSLATE_SUCCESS) { tlb_set_page(cs, address & TARGET_PAGE_MASK, pa & TARGET_PAGE_MASK, prot, mmu_idx, TARGET_PAGE_SIZE); @@ -482,9 +808,12 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, } else if (probe) { return false; } else { - raise_mmu_exception(env, address, access_type, pmp_violation); + raise_mmu_exception(env, address, access_type, pmp_violation, first_stage_error); riscv_raise_exception(env, cs->exception_index, retaddr); } + + return true; + #else switch (access_type) { case MMU_INST_FETCH: @@ -516,6 +845,8 @@ void riscv_cpu_do_interrupt(CPUState *cs) RISCVCPU *cpu = RISCV_CPU(cs); CPURISCVState *env = &cpu->env; + bool force_hs_execp = riscv_cpu_force_hs_excep_enabled(env); + target_ulong s; /* cs->exception is 32-bits wide unlike mcause which is XLEN-bits wide * so we mask off the MSB and separate into trap type and cause. @@ -524,17 +855,17 @@ void riscv_cpu_do_interrupt(CPUState *cs) target_ulong cause = cs->exception_index & RISCV_EXCP_INT_MASK; target_ulong deleg = async ? env->mideleg : env->medeleg; target_ulong tval = 0; - - static const int ecall_cause_map[] = { - [PRV_U] = RISCV_EXCP_U_ECALL, - [PRV_S] = RISCV_EXCP_S_ECALL, - [PRV_H] = RISCV_EXCP_H_ECALL, - [PRV_M] = RISCV_EXCP_M_ECALL - }; + target_ulong htval = 0; + target_ulong mtval2 = 0; if (!async) { /* set tval to badaddr for traps with address information */ switch (cause) { + case RISCV_EXCP_INST_GUEST_PAGE_FAULT: + case RISCV_EXCP_LOAD_GUEST_ACCESS_FAULT: + case RISCV_EXCP_STORE_GUEST_AMO_ACCESS_FAULT: + force_hs_execp = true; + /* fallthrough */ case RISCV_EXCP_INST_ADDR_MIS: case RISCV_EXCP_INST_ACCESS_FAULT: case RISCV_EXCP_LOAD_ADDR_MIS: @@ -552,17 +883,66 @@ void riscv_cpu_do_interrupt(CPUState *cs) /* ecall is dispatched as one cause so translate based on mode */ if (cause == RISCV_EXCP_U_ECALL) { assert(env->priv <= 3); - cause = ecall_cause_map[env->priv]; + + if (env->priv == PRV_M) { + cause = RISCV_EXCP_M_ECALL; + } else if (env->priv == PRV_S && riscv_cpu_virt_enabled(env)) { + cause = RISCV_EXCP_VS_ECALL; + } else if (env->priv == PRV_S && !riscv_cpu_virt_enabled(env)) { + cause = RISCV_EXCP_S_ECALL; + } else if (env->priv == PRV_U) { + cause = RISCV_EXCP_U_ECALL; + } } } - trace_riscv_trap(env->mhartid, async, cause, env->pc, tval, cause < 16 ? + trace_riscv_trap(env->mhartid, async, cause, env->pc, tval, cause < 23 ? (async ? riscv_intr_names : riscv_excp_names)[cause] : "(unknown)"); if (env->priv <= PRV_S && cause < TARGET_LONG_BITS && ((deleg >> cause) & 1)) { /* handle the trap in S-mode */ - target_ulong s = env->mstatus; + if (riscv_has_ext(env, RVH)) { + target_ulong hdeleg = async ? env->hideleg : env->hedeleg; + + if (riscv_cpu_virt_enabled(env) && ((hdeleg >> cause) & 1) && + !force_hs_execp) { + /* + * See if we need to adjust cause. Yes if its VS mode interrupt + * no if hypervisor has delegated one of hs mode's interrupt + */ + if (cause == IRQ_VS_TIMER || cause == IRQ_VS_SOFT || + cause == IRQ_VS_EXT) + cause = cause - 1; + /* Trap to VS mode */ + } else if (riscv_cpu_virt_enabled(env)) { + /* Trap into HS mode, from virt */ + riscv_cpu_swap_hypervisor_regs(env); + env->hstatus = set_field(env->hstatus, HSTATUS_SP2V, + get_field(env->hstatus, HSTATUS_SPV)); + env->hstatus = set_field(env->hstatus, HSTATUS_SP2P, + get_field(env->mstatus, SSTATUS_SPP)); + env->hstatus = set_field(env->hstatus, HSTATUS_SPV, + riscv_cpu_virt_enabled(env)); + + htval = env->guest_phys_fault_addr; + + riscv_cpu_set_virt_enabled(env, 0); + riscv_cpu_set_force_hs_excep(env, 0); + } else { + /* Trap into HS mode */ + env->hstatus = set_field(env->hstatus, HSTATUS_SP2V, + get_field(env->hstatus, HSTATUS_SPV)); + env->hstatus = set_field(env->hstatus, HSTATUS_SP2P, + get_field(env->mstatus, SSTATUS_SPP)); + env->hstatus = set_field(env->hstatus, HSTATUS_SPV, + riscv_cpu_virt_enabled(env)); + + htval = env->guest_phys_fault_addr; + } + } + + s = env->mstatus; s = set_field(s, MSTATUS_SPIE, env->priv_ver >= PRIV_VERSION_1_10_0 ? get_field(s, MSTATUS_SIE) : get_field(s, MSTATUS_UIE << env->priv)); s = set_field(s, MSTATUS_SPP, env->priv); @@ -571,12 +951,36 @@ void riscv_cpu_do_interrupt(CPUState *cs) env->scause = cause | ((target_ulong)async << (TARGET_LONG_BITS - 1)); env->sepc = env->pc; env->sbadaddr = tval; + env->htval = htval; env->pc = (env->stvec >> 2 << 2) + ((async && (env->stvec & 3) == 1) ? cause * 4 : 0); riscv_cpu_set_mode(env, PRV_S); } else { /* handle the trap in M-mode */ - target_ulong s = env->mstatus; + if (riscv_has_ext(env, RVH)) { + if (riscv_cpu_virt_enabled(env)) { + riscv_cpu_swap_hypervisor_regs(env); + } +#ifdef TARGET_RISCV32 + env->mstatush = set_field(env->mstatush, MSTATUS_MPV, + riscv_cpu_virt_enabled(env)); + env->mstatush = set_field(env->mstatush, MSTATUS_MTL, + riscv_cpu_force_hs_excep_enabled(env)); +#else + env->mstatus = set_field(env->mstatus, MSTATUS_MPV, + riscv_cpu_virt_enabled(env)); + env->mstatus = set_field(env->mstatus, MSTATUS_MTL, + riscv_cpu_force_hs_excep_enabled(env)); +#endif + + mtval2 = env->guest_phys_fault_addr; + + /* Trapping to M mode, virt is disabled */ + riscv_cpu_set_virt_enabled(env, 0); + riscv_cpu_set_force_hs_excep(env, 0); + } + + s = env->mstatus; s = set_field(s, MSTATUS_MPIE, env->priv_ver >= PRIV_VERSION_1_10_0 ? get_field(s, MSTATUS_MIE) : get_field(s, MSTATUS_UIE << env->priv)); s = set_field(s, MSTATUS_MPP, env->priv); @@ -585,6 +989,7 @@ void riscv_cpu_do_interrupt(CPUState *cs) env->mcause = cause | ~(((target_ulong)-1) >> async); env->mepc = env->pc; env->mbadaddr = tval; + env->mtval2 = mtval2; env->pc = (env->mtvec >> 2 << 2) + ((async && (env->mtvec & 3) == 1) ? cause * 4 : 0); riscv_cpu_set_mode(env, PRV_M); diff --git a/target/riscv/csr.c b/target/riscv/csr.c index da02f9f0b1..11d184cd16 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -98,6 +98,20 @@ static int smode(CPURISCVState *env, int csrno) return -!riscv_has_ext(env, RVS); } +static int hmode(CPURISCVState *env, int csrno) +{ + if (riscv_has_ext(env, RVS) && + riscv_has_ext(env, RVH)) { + /* Hypervisor extension is supported */ + if ((env->priv == PRV_S && !riscv_cpu_virt_enabled(env)) || + env->priv == PRV_M) { + return 0; + } + } + + return -1; +} + static int pmp(CPURISCVState *env, int csrno) { return -!riscv_feature(env, RISCV_FEATURE_PMP); @@ -224,13 +238,42 @@ static int read_timeh(CPURISCVState *env, int csrno, target_ulong *val) #else /* CONFIG_USER_ONLY */ +static int read_time(CPURISCVState *env, int csrno, target_ulong *val) +{ + uint64_t delta = riscv_cpu_virt_enabled(env) ? env->htimedelta : 0; + + if (!env->rdtime_fn) { + return -1; + } + + *val = env->rdtime_fn() + delta; + return 0; +} + +#if defined(TARGET_RISCV32) +static int read_timeh(CPURISCVState *env, int csrno, target_ulong *val) +{ + uint64_t delta = riscv_cpu_virt_enabled(env) ? env->htimedelta : 0; + + if (!env->rdtime_fn) { + return -1; + } + + *val = (env->rdtime_fn() + delta) >> 32; + return 0; +} +#endif + /* Machine constants */ -#define M_MODE_INTERRUPTS (MIP_MSIP | MIP_MTIP | MIP_MEIP) -#define S_MODE_INTERRUPTS (MIP_SSIP | MIP_STIP | MIP_SEIP) +#define M_MODE_INTERRUPTS (MIP_MSIP | MIP_MTIP | MIP_MEIP) +#define S_MODE_INTERRUPTS (MIP_SSIP | MIP_STIP | MIP_SEIP) +#define VS_MODE_INTERRUPTS (MIP_VSSIP | MIP_VSTIP | MIP_VSEIP) -static const target_ulong delegable_ints = S_MODE_INTERRUPTS; -static const target_ulong all_ints = M_MODE_INTERRUPTS | S_MODE_INTERRUPTS; +static const target_ulong delegable_ints = S_MODE_INTERRUPTS | + VS_MODE_INTERRUPTS; +static const target_ulong all_ints = M_MODE_INTERRUPTS | S_MODE_INTERRUPTS | + VS_MODE_INTERRUPTS; static const target_ulong delegable_excps = (1ULL << (RISCV_EXCP_INST_ADDR_MIS)) | (1ULL << (RISCV_EXCP_INST_ACCESS_FAULT)) | @@ -242,11 +285,14 @@ static const target_ulong delegable_excps = (1ULL << (RISCV_EXCP_STORE_AMO_ACCESS_FAULT)) | (1ULL << (RISCV_EXCP_U_ECALL)) | (1ULL << (RISCV_EXCP_S_ECALL)) | - (1ULL << (RISCV_EXCP_H_ECALL)) | + (1ULL << (RISCV_EXCP_VS_ECALL)) | (1ULL << (RISCV_EXCP_M_ECALL)) | (1ULL << (RISCV_EXCP_INST_PAGE_FAULT)) | (1ULL << (RISCV_EXCP_LOAD_PAGE_FAULT)) | - (1ULL << (RISCV_EXCP_STORE_PAGE_FAULT)); + (1ULL << (RISCV_EXCP_STORE_PAGE_FAULT)) | + (1ULL << (RISCV_EXCP_INST_GUEST_PAGE_FAULT)) | + (1ULL << (RISCV_EXCP_LOAD_GUEST_ACCESS_FAULT)) | + (1ULL << (RISCV_EXCP_STORE_GUEST_AMO_ACCESS_FAULT)); static const target_ulong sstatus_v1_9_mask = SSTATUS_SIE | SSTATUS_SPIE | SSTATUS_UIE | SSTATUS_UPIE | SSTATUS_SPP | SSTATUS_FS | SSTATUS_XS | SSTATUS_SUM | SSTATUS_SD; @@ -254,6 +300,8 @@ static const target_ulong sstatus_v1_10_mask = SSTATUS_SIE | SSTATUS_SPIE | SSTATUS_UIE | SSTATUS_UPIE | SSTATUS_SPP | SSTATUS_FS | SSTATUS_XS | SSTATUS_SUM | SSTATUS_MXR | SSTATUS_SD; static const target_ulong sip_writable_mask = SIP_SSIP | MIP_USIP | MIP_UEIP; +static const target_ulong hip_writable_mask = MIP_VSSIP | MIP_VSTIP | MIP_VSEIP; +static const target_ulong vsip_writable_mask = MIP_VSSIP; #if defined(TARGET_RISCV32) static const char valid_vm_1_09[16] = { @@ -341,8 +389,7 @@ static int write_mstatus(CPURISCVState *env, int csrno, target_ulong val) mstatus = (mstatus & ~mask) | (val & mask); - dirty = (riscv_cpu_fp_enabled(env) && - ((mstatus & MSTATUS_FS) == MSTATUS_FS)) | + dirty = ((mstatus & MSTATUS_FS) == MSTATUS_FS) | ((mstatus & MSTATUS_XS) == MSTATUS_XS); mstatus = set_field(mstatus, MSTATUS_SD, dirty); env->mstatus = mstatus; @@ -350,6 +397,27 @@ static int write_mstatus(CPURISCVState *env, int csrno, target_ulong val) return 0; } +#ifdef TARGET_RISCV32 +static int read_mstatush(CPURISCVState *env, int csrno, target_ulong *val) +{ + *val = env->mstatush; + return 0; +} + +static int write_mstatush(CPURISCVState *env, int csrno, target_ulong val) +{ + if ((val ^ env->mstatush) & (MSTATUS_MPV)) { + tlb_flush(env_cpu(env)); + } + + val &= MSTATUS_MPV | MSTATUS_MTL; + + env->mstatush = val; + + return 0; +} +#endif + static int read_misa(CPURISCVState *env, int csrno, target_ulong *val) { *val = env->misa; @@ -429,6 +497,9 @@ static int read_mideleg(CPURISCVState *env, int csrno, target_ulong *val) static int write_mideleg(CPURISCVState *env, int csrno, target_ulong val) { env->mideleg = (env->mideleg & ~delegable_ints) | (val & delegable_ints); + if (riscv_has_ext(env, RVH)) { + env->mideleg |= VS_MODE_INTERRUPTS; + } return 0; } @@ -608,13 +679,27 @@ static int write_sstatus(CPURISCVState *env, int csrno, target_ulong val) static int read_sie(CPURISCVState *env, int csrno, target_ulong *val) { - *val = env->mie & env->mideleg; + if (riscv_cpu_virt_enabled(env)) { + /* Tell the guest the VS bits, shifted to the S bit locations */ + *val = (env->mie & env->mideleg & VS_MODE_INTERRUPTS) >> 1; + } else { + *val = env->mie & env->mideleg; + } return 0; } static int write_sie(CPURISCVState *env, int csrno, target_ulong val) { - target_ulong newval = (env->mie & ~env->mideleg) | (val & env->mideleg); + target_ulong newval; + + if (riscv_cpu_virt_enabled(env)) { + /* Shift the guests S bits to VS */ + newval = (env->mie & ~VS_MODE_INTERRUPTS) | + ((val << 1) & VS_MODE_INTERRUPTS); + } else { + newval = (env->mie & ~S_MODE_INTERRUPTS) | (val & S_MODE_INTERRUPTS); + } + return write_mie(env, CSR_MIE, newval); } @@ -705,8 +790,19 @@ static int write_sbadaddr(CPURISCVState *env, int csrno, target_ulong val) static int rmw_sip(CPURISCVState *env, int csrno, target_ulong *ret_value, target_ulong new_value, target_ulong write_mask) { - int ret = rmw_mip(env, CSR_MSTATUS, ret_value, new_value, + int ret; + + if (riscv_cpu_virt_enabled(env)) { + /* Shift the new values to line up with the VS bits */ + ret = rmw_mip(env, CSR_MSTATUS, ret_value, new_value << 1, + (write_mask & sip_writable_mask) << 1 & env->mideleg); + ret &= vsip_writable_mask; + ret >>= 1; + } else { + ret = rmw_mip(env, CSR_MSTATUS, ret_value, new_value, write_mask & env->mideleg & sip_writable_mask); + } + *ret_value &= env->mideleg; return ret; } @@ -754,6 +850,291 @@ static int write_satp(CPURISCVState *env, int csrno, target_ulong val) return 0; } +/* Hypervisor Extensions */ +static int read_hstatus(CPURISCVState *env, int csrno, target_ulong *val) +{ + *val = env->hstatus; + return 0; +} + +static int write_hstatus(CPURISCVState *env, int csrno, target_ulong val) +{ + env->hstatus = val; + return 0; +} + +static int read_hedeleg(CPURISCVState *env, int csrno, target_ulong *val) +{ + *val = env->hedeleg; + return 0; +} + +static int write_hedeleg(CPURISCVState *env, int csrno, target_ulong val) +{ + env->hedeleg = val; + return 0; +} + +static int read_hideleg(CPURISCVState *env, int csrno, target_ulong *val) +{ + *val = env->hideleg; + return 0; +} + +static int write_hideleg(CPURISCVState *env, int csrno, target_ulong val) +{ + env->hideleg = val; + return 0; +} + +static int rmw_hip(CPURISCVState *env, int csrno, target_ulong *ret_value, + target_ulong new_value, target_ulong write_mask) +{ + int ret = rmw_mip(env, 0, ret_value, new_value, + write_mask & hip_writable_mask); + + return ret; +} + +static int read_hie(CPURISCVState *env, int csrno, target_ulong *val) +{ + *val = env->mie & VS_MODE_INTERRUPTS; + return 0; +} + +static int write_hie(CPURISCVState *env, int csrno, target_ulong val) +{ + target_ulong newval = (env->mie & ~VS_MODE_INTERRUPTS) | (val & VS_MODE_INTERRUPTS); + return write_mie(env, CSR_MIE, newval); +} + +static int read_hcounteren(CPURISCVState *env, int csrno, target_ulong *val) +{ + *val = env->hcounteren; + return 0; +} + +static int write_hcounteren(CPURISCVState *env, int csrno, target_ulong val) +{ + env->hcounteren = val; + return 0; +} + +static int read_htval(CPURISCVState *env, int csrno, target_ulong *val) +{ + *val = env->htval; + return 0; +} + +static int write_htval(CPURISCVState *env, int csrno, target_ulong val) +{ + env->htval = val; + return 0; +} + +static int read_htinst(CPURISCVState *env, int csrno, target_ulong *val) +{ + *val = env->htinst; + return 0; +} + +static int write_htinst(CPURISCVState *env, int csrno, target_ulong val) +{ + env->htinst = val; + return 0; +} + +static int read_hgatp(CPURISCVState *env, int csrno, target_ulong *val) +{ + *val = env->hgatp; + return 0; +} + +static int write_hgatp(CPURISCVState *env, int csrno, target_ulong val) +{ + env->hgatp = val; + return 0; +} + +static int read_htimedelta(CPURISCVState *env, int csrno, target_ulong *val) +{ + if (!env->rdtime_fn) { + return -1; + } + +#if defined(TARGET_RISCV32) + *val = env->htimedelta & 0xffffffff; +#else + *val = env->htimedelta; +#endif + return 0; +} + +static int write_htimedelta(CPURISCVState *env, int csrno, target_ulong val) +{ + if (!env->rdtime_fn) { + return -1; + } + +#if defined(TARGET_RISCV32) + env->htimedelta = deposit64(env->htimedelta, 0, 32, (uint64_t)val); +#else + env->htimedelta = val; +#endif + return 0; +} + +#if defined(TARGET_RISCV32) +static int read_htimedeltah(CPURISCVState *env, int csrno, target_ulong *val) +{ + if (!env->rdtime_fn) { + return -1; + } + + *val = env->htimedelta >> 32; + return 0; +} + +static int write_htimedeltah(CPURISCVState *env, int csrno, target_ulong val) +{ + if (!env->rdtime_fn) { + return -1; + } + + env->htimedelta = deposit64(env->htimedelta, 32, 32, (uint64_t)val); + return 0; +} +#endif + +/* Virtual CSR Registers */ +static int read_vsstatus(CPURISCVState *env, int csrno, target_ulong *val) +{ + *val = env->vsstatus; + return 0; +} + +static int write_vsstatus(CPURISCVState *env, int csrno, target_ulong val) +{ + env->vsstatus = val; + return 0; +} + +static int rmw_vsip(CPURISCVState *env, int csrno, target_ulong *ret_value, + target_ulong new_value, target_ulong write_mask) +{ + int ret = rmw_mip(env, 0, ret_value, new_value, + write_mask & env->mideleg & vsip_writable_mask); + return ret; +} + +static int read_vsie(CPURISCVState *env, int csrno, target_ulong *val) +{ + *val = env->mie & env->mideleg & VS_MODE_INTERRUPTS; + return 0; +} + +static int write_vsie(CPURISCVState *env, int csrno, target_ulong val) +{ + target_ulong newval = (env->mie & ~env->mideleg) | (val & env->mideleg & MIP_VSSIP); + return write_mie(env, CSR_MIE, newval); +} + +static int read_vstvec(CPURISCVState *env, int csrno, target_ulong *val) +{ + *val = env->vstvec; + return 0; +} + +static int write_vstvec(CPURISCVState *env, int csrno, target_ulong val) +{ + env->vstvec = val; + return 0; +} + +static int read_vsscratch(CPURISCVState *env, int csrno, target_ulong *val) +{ + *val = env->vsscratch; + return 0; +} + +static int write_vsscratch(CPURISCVState *env, int csrno, target_ulong val) +{ + env->vsscratch = val; + return 0; +} + +static int read_vsepc(CPURISCVState *env, int csrno, target_ulong *val) +{ + *val = env->vsepc; + return 0; +} + +static int write_vsepc(CPURISCVState *env, int csrno, target_ulong val) +{ + env->vsepc = val; + return 0; +} + +static int read_vscause(CPURISCVState *env, int csrno, target_ulong *val) +{ + *val = env->vscause; + return 0; +} + +static int write_vscause(CPURISCVState *env, int csrno, target_ulong val) +{ + env->vscause = val; + return 0; +} + +static int read_vstval(CPURISCVState *env, int csrno, target_ulong *val) +{ + *val = env->vstval; + return 0; +} + +static int write_vstval(CPURISCVState *env, int csrno, target_ulong val) +{ + env->vstval = val; + return 0; +} + +static int read_vsatp(CPURISCVState *env, int csrno, target_ulong *val) +{ + *val = env->vsatp; + return 0; +} + +static int write_vsatp(CPURISCVState *env, int csrno, target_ulong val) +{ + env->vsatp = val; + return 0; +} + +static int read_mtval2(CPURISCVState *env, int csrno, target_ulong *val) +{ + *val = env->mtval2; + return 0; +} + +static int write_mtval2(CPURISCVState *env, int csrno, target_ulong val) +{ + env->mtval2 = val; + return 0; +} + +static int read_mtinst(CPURISCVState *env, int csrno, target_ulong *val) +{ + *val = env->mtinst; + return 0; +} + +static int write_mtinst(CPURISCVState *env, int csrno, target_ulong val) +{ + env->mtinst = val; + return 0; +} + /* Physical Memory Protection */ static int read_pmpcfg(CPURISCVState *env, int csrno, target_ulong *val) { @@ -799,12 +1180,22 @@ int riscv_csrrw(CPURISCVState *env, int csrno, target_ulong *ret_value, /* check privileges and return -1 if check fails */ #if !defined(CONFIG_USER_ONLY) - int csr_priv = get_field(csrno, 0x300); + int effective_priv = env->priv; int read_only = get_field(csrno, 0xC00) == 3; - if ((!env->debugger) && (env->priv < csr_priv)) { - return -1; + + if (riscv_has_ext(env, RVH) && + env->priv == PRV_S && + !riscv_cpu_virt_enabled(env)) { + /* + * We are in S mode without virtualisation, therefore we are in HS Mode. + * Add 1 to the effective privledge level to allow us to access the + * Hypervisor CSRs. + */ + effective_priv++; } - if (write_mask && read_only) { + + if ((write_mask && read_only) || + (!env->debugger && (effective_priv < get_field(csrno, 0x300)))) { return -1; } #endif @@ -887,14 +1278,12 @@ static riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [CSR_INSTRETH] = { ctr, read_instreth }, #endif - /* User-level time CSRs are only available in linux-user - * In privileged mode, the monitor emulates these CSRs */ -#if defined(CONFIG_USER_ONLY) + /* In privileged mode, the monitor will have to emulate TIME CSRs only if + * rdtime callback is not provided by machine/platform emulation */ [CSR_TIME] = { ctr, read_time }, #if defined(TARGET_RISCV32) [CSR_TIMEH] = { ctr, read_timeh }, #endif -#endif #if !defined(CONFIG_USER_ONLY) /* Machine Timers and Counters */ @@ -920,6 +1309,10 @@ static riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [CSR_MTVEC] = { any, read_mtvec, write_mtvec }, [CSR_MCOUNTEREN] = { any, read_mcounteren, write_mcounteren }, +#if defined(TARGET_RISCV32) + [CSR_MSTATUSH] = { any, read_mstatush, write_mstatush }, +#endif + /* Legacy Counter Setup (priv v1.9.1) */ [CSR_MUCOUNTEREN] = { any, read_mucounteren, write_mucounteren }, [CSR_MSCOUNTEREN] = { any, read_mscounteren, write_mscounteren }, @@ -947,6 +1340,33 @@ static riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { /* Supervisor Protection and Translation */ [CSR_SATP] = { smode, read_satp, write_satp }, + [CSR_HSTATUS] = { hmode, read_hstatus, write_hstatus }, + [CSR_HEDELEG] = { hmode, read_hedeleg, write_hedeleg }, + [CSR_HIDELEG] = { hmode, read_hideleg, write_hideleg }, + [CSR_HIP] = { hmode, NULL, NULL, rmw_hip }, + [CSR_HIE] = { hmode, read_hie, write_hie }, + [CSR_HCOUNTEREN] = { hmode, read_hcounteren, write_hcounteren }, + [CSR_HTVAL] = { hmode, read_htval, write_htval }, + [CSR_HTINST] = { hmode, read_htinst, write_htinst }, + [CSR_HGATP] = { hmode, read_hgatp, write_hgatp }, + [CSR_HTIMEDELTA] = { hmode, read_htimedelta, write_htimedelta }, +#if defined(TARGET_RISCV32) + [CSR_HTIMEDELTAH] = { hmode, read_htimedeltah, write_htimedeltah}, +#endif + + [CSR_VSSTATUS] = { hmode, read_vsstatus, write_vsstatus }, + [CSR_VSIP] = { hmode, NULL, NULL, rmw_vsip }, + [CSR_VSIE] = { hmode, read_vsie, write_vsie }, + [CSR_VSTVEC] = { hmode, read_vstvec, write_vstvec }, + [CSR_VSSCRATCH] = { hmode, read_vsscratch, write_vsscratch }, + [CSR_VSEPC] = { hmode, read_vsepc, write_vsepc }, + [CSR_VSCAUSE] = { hmode, read_vscause, write_vscause }, + [CSR_VSTVAL] = { hmode, read_vstval, write_vstval }, + [CSR_VSATP] = { hmode, read_vsatp, write_vsatp }, + + [CSR_MTVAL2] = { hmode, read_mtval2, write_mtval2 }, + [CSR_MTINST] = { hmode, read_mtinst, write_mtinst }, + /* Physical Memory Protection */ [CSR_PMPCFG0 ... CSR_PMPADDR9] = { pmp, read_pmpcfg, write_pmpcfg }, [CSR_PMPADDR0 ... CSR_PMPADDR15] = { pmp, read_pmpaddr, write_pmpaddr }, diff --git a/target/riscv/gdbstub.c b/target/riscv/gdbstub.c index 1a7947e019..eba12a86f2 100644 --- a/target/riscv/gdbstub.c +++ b/target/riscv/gdbstub.c @@ -130,6 +130,8 @@ static int csr_register_map[] = { CSR_MCAUSE, CSR_MTVAL, CSR_MIP, + CSR_MTINST, + CSR_MTVAL2, CSR_PMPCFG0, CSR_PMPCFG1, CSR_PMPCFG2, @@ -252,12 +254,11 @@ static int csr_register_map[] = { CSR_HEDELEG, CSR_HIDELEG, CSR_HIE, - CSR_HTVEC, - CSR_HSCRATCH, - CSR_HEPC, - CSR_HCAUSE, - CSR_HBADADDR, + CSR_HCOUNTEREN, + CSR_HTVAL, CSR_HIP, + CSR_HTINST, + CSR_HGATP, CSR_MBASE, CSR_MBOUND, CSR_MIBASE, @@ -269,7 +270,7 @@ static int csr_register_map[] = { CSR_MHCOUNTEREN, }; -int riscv_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +int riscv_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) { RISCVCPU *cpu = RISCV_CPU(cs); CPURISCVState *env = &cpu->env; @@ -300,10 +301,15 @@ int riscv_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) return 0; } -static int riscv_gdb_get_fpu(CPURISCVState *env, uint8_t *mem_buf, int n) +static int riscv_gdb_get_fpu(CPURISCVState *env, GByteArray *buf, int n) { if (n < 32) { - return gdb_get_reg64(mem_buf, env->fpr[n]); + if (env->misa & RVD) { + return gdb_get_reg64(buf, env->fpr[n]); + } + if (env->misa & RVF) { + return gdb_get_reg32(buf, env->fpr[n]); + } /* there is hole between ft11 and fflags in fpu.xml */ } else if (n < 36 && n > 32) { target_ulong val = 0; @@ -316,7 +322,7 @@ static int riscv_gdb_get_fpu(CPURISCVState *env, uint8_t *mem_buf, int n) result = riscv_csrrw_debug(env, n - 33 + csr_register_map[8], &val, 0, 0); if (result == 0) { - return gdb_get_regl(mem_buf, val); + return gdb_get_regl(buf, val); } } return 0; @@ -345,7 +351,7 @@ static int riscv_gdb_set_fpu(CPURISCVState *env, uint8_t *mem_buf, int n) return 0; } -static int riscv_gdb_get_csr(CPURISCVState *env, uint8_t *mem_buf, int n) +static int riscv_gdb_get_csr(CPURISCVState *env, GByteArray *buf, int n) { if (n < ARRAY_SIZE(csr_register_map)) { target_ulong val = 0; @@ -353,7 +359,7 @@ static int riscv_gdb_get_csr(CPURISCVState *env, uint8_t *mem_buf, int n) result = riscv_csrrw_debug(env, csr_register_map[n], &val, 0, 0); if (result == 0) { - return gdb_get_regl(mem_buf, val); + return gdb_get_regl(buf, val); } } return 0; @@ -373,13 +379,13 @@ static int riscv_gdb_set_csr(CPURISCVState *env, uint8_t *mem_buf, int n) return 0; } -static int riscv_gdb_get_virtual(CPURISCVState *cs, uint8_t *mem_buf, int n) +static int riscv_gdb_get_virtual(CPURISCVState *cs, GByteArray *buf, int n) { if (n == 0) { #ifdef CONFIG_USER_ONLY - return gdb_get_regl(mem_buf, 0); + return gdb_get_regl(buf, 0); #else - return gdb_get_regl(mem_buf, cs->priv); + return gdb_get_regl(buf, cs->priv); #endif } return 0; @@ -403,23 +409,20 @@ void riscv_cpu_register_gdb_regs_for_features(CPUState *cs) { RISCVCPU *cpu = RISCV_CPU(cs); CPURISCVState *env = &cpu->env; -#if defined(TARGET_RISCV32) - if (env->misa & RVF) { + if (env->misa & RVD) { + gdb_register_coprocessor(cs, riscv_gdb_get_fpu, riscv_gdb_set_fpu, + 36, "riscv-64bit-fpu.xml", 0); + } else if (env->misa & RVF) { gdb_register_coprocessor(cs, riscv_gdb_get_fpu, riscv_gdb_set_fpu, 36, "riscv-32bit-fpu.xml", 0); } - +#if defined(TARGET_RISCV32) gdb_register_coprocessor(cs, riscv_gdb_get_csr, riscv_gdb_set_csr, 240, "riscv-32bit-csr.xml", 0); gdb_register_coprocessor(cs, riscv_gdb_get_virtual, riscv_gdb_set_virtual, 1, "riscv-32bit-virtual.xml", 0); #elif defined(TARGET_RISCV64) - if (env->misa & RVF) { - gdb_register_coprocessor(cs, riscv_gdb_get_fpu, riscv_gdb_set_fpu, - 36, "riscv-64bit-fpu.xml", 0); - } - gdb_register_coprocessor(cs, riscv_gdb_get_csr, riscv_gdb_set_csr, 240, "riscv-64bit-csr.xml", 0); diff --git a/target/riscv/insn32.decode b/target/riscv/insn32.decode index 77f794ed70..b883672e63 100644 --- a/target/riscv/insn32.decode +++ b/target/riscv/insn32.decode @@ -63,20 +63,24 @@ @r2_rm ....... ..... ..... ... ..... ....... %rs1 %rm %rd @r2 ....... ..... ..... ... ..... ....... %rs1 %rd +@hfence_gvma ....... ..... ..... ... ..... ....... %rs2 %rs1 +@hfence_bvma ....... ..... ..... ... ..... ....... %rs2 %rs1 + @sfence_vma ....... ..... ..... ... ..... ....... %rs2 %rs1 @sfence_vm ....... ..... ..... ... ..... ....... %rs1 # *** Privileged Instructions *** -ecall 000000000000 00000 000 00000 1110011 -ebreak 000000000001 00000 000 00000 1110011 -uret 0000000 00010 00000 000 00000 1110011 -sret 0001000 00010 00000 000 00000 1110011 -hret 0010000 00010 00000 000 00000 1110011 -mret 0011000 00010 00000 000 00000 1110011 -wfi 0001000 00101 00000 000 00000 1110011 -sfence_vma 0001001 ..... ..... 000 00000 1110011 @sfence_vma -sfence_vm 0001000 00100 ..... 000 00000 1110011 @sfence_vm +ecall 000000000000 00000 000 00000 1110011 +ebreak 000000000001 00000 000 00000 1110011 +uret 0000000 00010 00000 000 00000 1110011 +sret 0001000 00010 00000 000 00000 1110011 +mret 0011000 00010 00000 000 00000 1110011 +wfi 0001000 00101 00000 000 00000 1110011 +hfence_gvma 0110001 ..... ..... 000 00000 1110011 @hfence_gvma +hfence_bvma 0010001 ..... ..... 000 00000 1110011 @hfence_bvma +sfence_vma 0001001 ..... ..... 000 00000 1110011 @sfence_vma +sfence_vm 0001000 00100 ..... 000 00000 1110011 @sfence_vm # *** RV32I Base Instruction Set *** lui .................... ..... 0110111 @u diff --git a/target/riscv/insn_trans/trans_privileged.inc.c b/target/riscv/insn_trans/trans_privileged.inc.c index c5e4b3e49a..76c2fad71c 100644 --- a/target/riscv/insn_trans/trans_privileged.inc.c +++ b/target/riscv/insn_trans/trans_privileged.inc.c @@ -58,11 +58,6 @@ static bool trans_sret(DisasContext *ctx, arg_sret *a) #endif } -static bool trans_hret(DisasContext *ctx, arg_hret *a) -{ - return false; -} - static bool trans_mret(DisasContext *ctx, arg_mret *a) { #ifndef CONFIG_USER_ONLY @@ -108,3 +103,43 @@ static bool trans_sfence_vm(DisasContext *ctx, arg_sfence_vm *a) #endif return false; } + +static bool trans_hfence_gvma(DisasContext *ctx, arg_sfence_vma *a) +{ +#ifndef CONFIG_USER_ONLY + if (ctx->priv_ver >= PRIV_VERSION_1_10_0 && + has_ext(ctx, RVH)) { + /* Hpervisor extensions exist */ + /* + * if (env->priv == PRV_M || + * (env->priv == PRV_S && + * !riscv_cpu_virt_enabled(env) && + * get_field(ctx->mstatus_fs, MSTATUS_TVM))) { + */ + gen_helper_tlb_flush(cpu_env); + return true; + /* } */ + } +#endif + return false; +} + +static bool trans_hfence_bvma(DisasContext *ctx, arg_sfence_vma *a) +{ +#ifndef CONFIG_USER_ONLY + if (ctx->priv_ver >= PRIV_VERSION_1_10_0 && + has_ext(ctx, RVH)) { + /* Hpervisor extensions exist */ + /* + * if (env->priv == PRV_M || + * (env->priv == PRV_S && + * !riscv_cpu_virt_enabled(env) && + * get_field(ctx->mstatus_fs, MSTATUS_TVM))) { + */ + gen_helper_tlb_flush(cpu_env); + return true; + /* } */ + } +#endif + return false; +} diff --git a/target/riscv/insn_trans/trans_rvd.inc.c b/target/riscv/insn_trans/trans_rvd.inc.c index 393fa0248c..ea1044f13b 100644 --- a/target/riscv/insn_trans/trans_rvd.inc.c +++ b/target/riscv/insn_trans/trans_rvd.inc.c @@ -43,7 +43,6 @@ static bool trans_fsd(DisasContext *ctx, arg_fsd *a) tcg_gen_qemu_st_i64(cpu_fpr[a->rs2], t0, ctx->mem_idx, MO_TEQ); - mark_fs_dirty(ctx); tcg_temp_free(t0); return true; } diff --git a/target/riscv/insn_trans/trans_rvf.inc.c b/target/riscv/insn_trans/trans_rvf.inc.c index 172dbfa919..e23cd639a6 100644 --- a/target/riscv/insn_trans/trans_rvf.inc.c +++ b/target/riscv/insn_trans/trans_rvf.inc.c @@ -52,7 +52,6 @@ static bool trans_fsw(DisasContext *ctx, arg_fsw *a) tcg_gen_qemu_st_i64(cpu_fpr[a->rs2], t0, ctx->mem_idx, MO_TEUL); tcg_temp_free(t0); - mark_fs_dirty(ctx); return true; } diff --git a/target/riscv/instmap.h b/target/riscv/instmap.h index f8ad7d60fd..40b6d2b64d 100644 --- a/target/riscv/instmap.h +++ b/target/riscv/instmap.h @@ -344,8 +344,8 @@ enum { #define GET_C_LW_IMM(inst) ((extract32(inst, 6, 1) << 2) \ | (extract32(inst, 10, 3) << 3) \ | (extract32(inst, 5, 1) << 6)) -#define GET_C_LD_IMM(inst) ((extract32(inst, 10, 3) << 3) \ - | (extract32(inst, 5, 2) << 6)) +#define GET_C_LD_IMM(inst) ((extract16(inst, 10, 3) << 3) \ + | (extract16(inst, 5, 2) << 6)) #define GET_C_J_IMM(inst) ((extract32(inst, 3, 3) << 1) \ | (extract32(inst, 11, 1) << 4) \ | (extract32(inst, 2, 1) << 5) \ @@ -363,7 +363,7 @@ enum { #define GET_C_RD(inst) GET_RD(inst) #define GET_C_RS1(inst) GET_RD(inst) #define GET_C_RS2(inst) extract32(inst, 2, 5) -#define GET_C_RS1S(inst) (8 + extract32(inst, 7, 3)) -#define GET_C_RS2S(inst) (8 + extract32(inst, 2, 3)) +#define GET_C_RS1S(inst) (8 + extract16(inst, 7, 3)) +#define GET_C_RS2S(inst) (8 + extract16(inst, 2, 3)) #endif diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c index 331cc36232..c6412f680c 100644 --- a/target/riscv/op_helper.c +++ b/target/riscv/op_helper.c @@ -73,6 +73,8 @@ target_ulong helper_csrrc(CPURISCVState *env, target_ulong src, target_ulong helper_sret(CPURISCVState *env, target_ulong cpu_pc_deb) { + target_ulong prev_priv, prev_virt, mstatus; + if (!(env->priv >= PRV_S)) { riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); } @@ -83,20 +85,50 @@ target_ulong helper_sret(CPURISCVState *env, target_ulong cpu_pc_deb) } if (env->priv_ver >= PRIV_VERSION_1_10_0 && - get_field(env->mstatus, MSTATUS_TSR)) { + get_field(env->mstatus, MSTATUS_TSR) && !(env->priv >= PRV_M)) { riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); } - target_ulong mstatus = env->mstatus; - target_ulong prev_priv = get_field(mstatus, MSTATUS_SPP); - mstatus = set_field(mstatus, - env->priv_ver >= PRIV_VERSION_1_10_0 ? - MSTATUS_SIE : MSTATUS_UIE << prev_priv, - get_field(mstatus, MSTATUS_SPIE)); - mstatus = set_field(mstatus, MSTATUS_SPIE, 0); - mstatus = set_field(mstatus, MSTATUS_SPP, PRV_U); + mstatus = env->mstatus; + + if (riscv_has_ext(env, RVH) && !riscv_cpu_virt_enabled(env)) { + /* We support Hypervisor extensions and virtulisation is disabled */ + target_ulong hstatus = env->hstatus; + + prev_priv = get_field(mstatus, MSTATUS_SPP); + prev_virt = get_field(hstatus, HSTATUS_SPV); + + hstatus = set_field(hstatus, HSTATUS_SPV, + get_field(hstatus, HSTATUS_SP2V)); + mstatus = set_field(mstatus, MSTATUS_SPP, + get_field(hstatus, HSTATUS_SP2P)); + hstatus = set_field(hstatus, HSTATUS_SP2V, 0); + hstatus = set_field(hstatus, HSTATUS_SP2P, 0); + mstatus = set_field(mstatus, SSTATUS_SIE, + get_field(mstatus, SSTATUS_SPIE)); + mstatus = set_field(mstatus, SSTATUS_SPIE, 1); + + env->mstatus = mstatus; + env->hstatus = hstatus; + + if (prev_virt) { + riscv_cpu_swap_hypervisor_regs(env); + } + + riscv_cpu_set_virt_enabled(env, prev_virt); + } else { + prev_priv = get_field(mstatus, MSTATUS_SPP); + + mstatus = set_field(mstatus, + env->priv_ver >= PRIV_VERSION_1_10_0 ? + MSTATUS_SIE : MSTATUS_UIE << prev_priv, + get_field(mstatus, MSTATUS_SPIE)); + mstatus = set_field(mstatus, MSTATUS_SPIE, 1); + mstatus = set_field(mstatus, MSTATUS_SPP, PRV_U); + env->mstatus = mstatus; + } + riscv_cpu_set_mode(env, prev_priv); - env->mstatus = mstatus; return retpc; } @@ -114,14 +146,28 @@ target_ulong helper_mret(CPURISCVState *env, target_ulong cpu_pc_deb) target_ulong mstatus = env->mstatus; target_ulong prev_priv = get_field(mstatus, MSTATUS_MPP); + target_ulong prev_virt = MSTATUS_MPV_ISSET(env); mstatus = set_field(mstatus, env->priv_ver >= PRIV_VERSION_1_10_0 ? MSTATUS_MIE : MSTATUS_UIE << prev_priv, get_field(mstatus, MSTATUS_MPIE)); - mstatus = set_field(mstatus, MSTATUS_MPIE, 0); + mstatus = set_field(mstatus, MSTATUS_MPIE, 1); mstatus = set_field(mstatus, MSTATUS_MPP, PRV_U); - riscv_cpu_set_mode(env, prev_priv); +#ifdef TARGET_RISCV32 + env->mstatush = set_field(env->mstatush, MSTATUS_MPV, 0); +#else + mstatus = set_field(mstatus, MSTATUS_MPV, 0); +#endif env->mstatus = mstatus; + riscv_cpu_set_mode(env, prev_priv); + + if (riscv_has_ext(env, RVH)) { + if (prev_virt) { + riscv_cpu_swap_hypervisor_regs(env); + } + + riscv_cpu_set_virt_enabled(env, prev_virt); + } return retpc; } @@ -130,9 +176,10 @@ void helper_wfi(CPURISCVState *env) { CPUState *cs = env_cpu(env); - if (env->priv == PRV_S && + if ((env->priv == PRV_S && env->priv_ver >= PRIV_VERSION_1_10_0 && - get_field(env->mstatus, MSTATUS_TW)) { + get_field(env->mstatus, MSTATUS_TW)) || + riscv_cpu_virt_enabled(env)) { riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); } else { cs->halted = 1; diff --git a/target/riscv/translate.c b/target/riscv/translate.c index ab6a891dc3..43bf7e39a6 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -19,7 +19,7 @@ #include "qemu/osdep.h" #include "qemu/log.h" #include "cpu.h" -#include "tcg-op.h" +#include "tcg/tcg-op.h" #include "disas/disas.h" #include "exec/cpu_ldst.h" #include "exec/exec-all.h" @@ -44,6 +44,7 @@ typedef struct DisasContext { /* pc_succ_insn points to the instruction following base.pc_next */ target_ulong pc_succ_insn; target_ulong priv_ver; + bool virt_enabled; uint32_t opcode; uint32_t mstatus_fs; uint32_t misa; @@ -394,8 +395,14 @@ static void mark_fs_dirty(DisasContext *ctx) tmp = tcg_temp_new(); tcg_gen_ld_tl(tmp, cpu_env, offsetof(CPURISCVState, mstatus)); - tcg_gen_ori_tl(tmp, tmp, MSTATUS_FS); + tcg_gen_ori_tl(tmp, tmp, MSTATUS_FS | MSTATUS_SD); tcg_gen_st_tl(tmp, cpu_env, offsetof(CPURISCVState, mstatus)); + + if (ctx->virt_enabled) { + tcg_gen_ld_tl(tmp, cpu_env, offsetof(CPURISCVState, mstatus_hs)); + tcg_gen_ori_tl(tmp, tmp, MSTATUS_FS | MSTATUS_SD); + tcg_gen_st_tl(tmp, cpu_env, offsetof(CPURISCVState, mstatus_hs)); + } tcg_temp_free(tmp); } #else @@ -492,45 +499,45 @@ static void gen_set_rm(DisasContext *ctx, int rm) tcg_temp_free_i32(t0); } -static void decode_RV32_64C0(DisasContext *ctx) +static void decode_RV32_64C0(DisasContext *ctx, uint16_t opcode) { - uint8_t funct3 = extract32(ctx->opcode, 13, 3); - uint8_t rd_rs2 = GET_C_RS2S(ctx->opcode); - uint8_t rs1s = GET_C_RS1S(ctx->opcode); + uint8_t funct3 = extract16(opcode, 13, 3); + uint8_t rd_rs2 = GET_C_RS2S(opcode); + uint8_t rs1s = GET_C_RS1S(opcode); switch (funct3) { case 3: #if defined(TARGET_RISCV64) /* C.LD(RV64/128) -> ld rd', offset[7:3](rs1')*/ gen_load_c(ctx, OPC_RISC_LD, rd_rs2, rs1s, - GET_C_LD_IMM(ctx->opcode)); + GET_C_LD_IMM(opcode)); #else /* C.FLW (RV32) -> flw rd', offset[6:2](rs1')*/ gen_fp_load(ctx, OPC_RISC_FLW, rd_rs2, rs1s, - GET_C_LW_IMM(ctx->opcode)); + GET_C_LW_IMM(opcode)); #endif break; case 7: #if defined(TARGET_RISCV64) /* C.SD (RV64/128) -> sd rs2', offset[7:3](rs1')*/ gen_store_c(ctx, OPC_RISC_SD, rs1s, rd_rs2, - GET_C_LD_IMM(ctx->opcode)); + GET_C_LD_IMM(opcode)); #else /* C.FSW (RV32) -> fsw rs2', offset[6:2](rs1')*/ gen_fp_store(ctx, OPC_RISC_FSW, rs1s, rd_rs2, - GET_C_LW_IMM(ctx->opcode)); + GET_C_LW_IMM(opcode)); #endif break; } } -static void decode_RV32_64C(DisasContext *ctx) +static void decode_RV32_64C(DisasContext *ctx, uint16_t opcode) { - uint8_t op = extract32(ctx->opcode, 0, 2); + uint8_t op = extract16(opcode, 0, 2); switch (op) { case 0: - decode_RV32_64C0(ctx); + decode_RV32_64C0(ctx, opcode); break; } } @@ -709,22 +716,25 @@ static bool gen_shift(DisasContext *ctx, arg_r *a, /* Include the auto-generated decoder for 16 bit insn */ #include "decode_insn16.inc.c" -static void decode_opc(DisasContext *ctx) +static void decode_opc(CPURISCVState *env, DisasContext *ctx, uint16_t opcode) { /* check for compressed insn */ - if (extract32(ctx->opcode, 0, 2) != 3) { + if (extract16(opcode, 0, 2) != 3) { if (!has_ext(ctx, RVC)) { gen_exception_illegal(ctx); } else { ctx->pc_succ_insn = ctx->base.pc_next + 2; - if (!decode_insn16(ctx, ctx->opcode)) { + if (!decode_insn16(ctx, opcode)) { /* fall back to old decoder */ - decode_RV32_64C(ctx); + decode_RV32_64C(ctx, opcode); } } } else { + uint32_t opcode32 = opcode; + opcode32 = deposit32(opcode32, 16, 16, + translator_lduw(env, ctx->base.pc_next + 2)); ctx->pc_succ_insn = ctx->base.pc_next + 4; - if (!decode_insn32(ctx, ctx->opcode)) { + if (!decode_insn32(ctx, opcode32)) { gen_exception_illegal(ctx); } } @@ -740,6 +750,25 @@ static void riscv_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) ctx->mem_idx = ctx->base.tb->flags & TB_FLAGS_MMU_MASK; ctx->mstatus_fs = ctx->base.tb->flags & TB_FLAGS_MSTATUS_FS; ctx->priv_ver = env->priv_ver; +#if !defined(CONFIG_USER_ONLY) + if (riscv_has_ext(env, RVH)) { + ctx->virt_enabled = riscv_cpu_virt_enabled(env); + if (env->priv_ver == PRV_M && + get_field(env->mstatus, MSTATUS_MPRV) && + MSTATUS_MPV_ISSET(env)) { + ctx->virt_enabled = true; + } else if (env->priv == PRV_S && + !riscv_cpu_virt_enabled(env) && + get_field(env->hstatus, HSTATUS_SPRV) && + get_field(env->hstatus, HSTATUS_SPV)) { + ctx->virt_enabled = true; + } + } else { + ctx->virt_enabled = false; + } +#else + ctx->virt_enabled = false; +#endif ctx->misa = env->misa; ctx->frm = -1; /* unknown rounding mode */ ctx->ext_ifencei = cpu->cfg.ext_ifencei; @@ -776,9 +805,9 @@ static void riscv_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) { DisasContext *ctx = container_of(dcbase, DisasContext, base); CPURISCVState *env = cpu->env_ptr; + uint16_t opcode16 = translator_lduw(env, ctx->base.pc_next); - ctx->opcode = translator_ldl(env, ctx->base.pc_next); - decode_opc(ctx); + decode_opc(env, ctx, opcode16); ctx->base.pc_next = ctx->pc_succ_insn; if (ctx->base.is_jmp == DISAS_NEXT) { @@ -808,7 +837,15 @@ static void riscv_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) static void riscv_tr_disas_log(const DisasContextBase *dcbase, CPUState *cpu) { +#ifndef CONFIG_USER_ONLY + RISCVCPU *rvcpu = RISCV_CPU(cpu); + CPURISCVState *env = &rvcpu->env; +#endif + qemu_log("IN: %s\n", lookup_symbol(dcbase->pc_first)); +#ifndef CONFIG_USER_ONLY + qemu_log("Priv: "TARGET_FMT_ld"; Virt: "TARGET_FMT_ld"\n", env->priv, env->virt); +#endif log_target_disas(cpu, dcbase->pc_first, dcbase->tb->size); } diff --git a/target/rx/Makefile.objs b/target/rx/Makefile.objs new file mode 100644 index 0000000000..a0018d5bc5 --- /dev/null +++ b/target/rx/Makefile.objs @@ -0,0 +1,11 @@ +obj-y += translate.o op_helper.o helper.o cpu.o gdbstub.o disas.o + +DECODETREE = $(SRC_PATH)/scripts/decodetree.py + +target/rx/decode.inc.c: \ + $(SRC_PATH)/target/rx/insns.decode $(DECODETREE) + $(call quiet-command,\ + $(PYTHON) $(DECODETREE) --varinsnwidth 32 -o $@ $<, "GEN", $(TARGET_DIR)$@) + +target/rx/translate.o: target/rx/decode.inc.c +target/rx/disas.o: target/rx/decode.inc.c diff --git a/target/rx/cpu-param.h b/target/rx/cpu-param.h new file mode 100644 index 0000000000..b156ad1ca0 --- /dev/null +++ b/target/rx/cpu-param.h @@ -0,0 +1,30 @@ +/* + * RX cpu parameters + * + * Copyright (c) 2019 Yoshinori Sato + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#ifndef RX_CPU_PARAM_H +#define RX_CPU_PARAM_H + +#define TARGET_LONG_BITS 32 +#define TARGET_PAGE_BITS 12 + +#define TARGET_PHYS_ADDR_SPACE_BITS 32 +#define TARGET_VIRT_ADDR_SPACE_BITS 32 + +#define NB_MMU_MODES 1 + +#endif diff --git a/target/rx/cpu-qom.h b/target/rx/cpu-qom.h new file mode 100644 index 0000000000..3e81856ef5 --- /dev/null +++ b/target/rx/cpu-qom.h @@ -0,0 +1,53 @@ +/* + * RX CPU + * + * Copyright (c) 2019 Yoshinori Sato + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#ifndef RX_CPU_QOM_H +#define RX_CPU_QOM_H + +#include "hw/core/cpu.h" + +#define TYPE_RX_CPU "rx-cpu" + +#define TYPE_RX62N_CPU RX_CPU_TYPE_NAME("rx62n") + +#define RXCPU_CLASS(klass) \ + OBJECT_CLASS_CHECK(RXCPUClass, (klass), TYPE_RX_CPU) +#define RXCPU(obj) \ + OBJECT_CHECK(RXCPU, (obj), TYPE_RX_CPU) +#define RXCPU_GET_CLASS(obj) \ + OBJECT_GET_CLASS(RXCPUClass, (obj), TYPE_RX_CPU) + +/* + * RXCPUClass: + * @parent_realize: The parent class' realize handler. + * @parent_reset: The parent class' reset handler. + * + * A RX CPU model. + */ +typedef struct RXCPUClass { + /*< private >*/ + CPUClass parent_class; + /*< public >*/ + + DeviceRealize parent_realize; + DeviceReset parent_reset; +} RXCPUClass; + +#define CPUArchState struct CPURXState + +#endif diff --git a/target/rx/cpu.c b/target/rx/cpu.c new file mode 100644 index 0000000000..219e05397b --- /dev/null +++ b/target/rx/cpu.c @@ -0,0 +1,225 @@ +/* + * QEMU RX CPU + * + * Copyright (c) 2019 Yoshinori Sato + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include "qemu/osdep.h" +#include "qemu/qemu-print.h" +#include "qapi/error.h" +#include "cpu.h" +#include "qemu-common.h" +#include "migration/vmstate.h" +#include "exec/exec-all.h" +#include "hw/loader.h" +#include "fpu/softfloat.h" + +static void rx_cpu_set_pc(CPUState *cs, vaddr value) +{ + RXCPU *cpu = RXCPU(cs); + + cpu->env.pc = value; +} + +static void rx_cpu_synchronize_from_tb(CPUState *cs, TranslationBlock *tb) +{ + RXCPU *cpu = RXCPU(cs); + + cpu->env.pc = tb->pc; +} + +static bool rx_cpu_has_work(CPUState *cs) +{ + return cs->interrupt_request & + (CPU_INTERRUPT_HARD | CPU_INTERRUPT_FIR); +} + +static void rx_cpu_reset(DeviceState *dev) +{ + RXCPU *cpu = RXCPU(dev); + RXCPUClass *rcc = RXCPU_GET_CLASS(cpu); + CPURXState *env = &cpu->env; + uint32_t *resetvec; + + rcc->parent_reset(dev); + + memset(env, 0, offsetof(CPURXState, end_reset_fields)); + + resetvec = rom_ptr(0xfffffffc, 4); + if (resetvec) { + /* In the case of kernel, it is ignored because it is not set. */ + env->pc = ldl_p(resetvec); + } + rx_cpu_unpack_psw(env, 0, 1); + env->regs[0] = env->isp = env->usp = 0; + env->fpsw = 0; + set_flush_to_zero(1, &env->fp_status); + set_flush_inputs_to_zero(1, &env->fp_status); +} + +static void rx_cpu_list_entry(gpointer data, gpointer user_data) +{ + ObjectClass *oc = data; + + qemu_printf(" %s\n", object_class_get_name(oc)); +} + +void rx_cpu_list(void) +{ + GSList *list; + list = object_class_get_list_sorted(TYPE_RX_CPU, false); + qemu_printf("Available CPUs:\n"); + g_slist_foreach(list, rx_cpu_list_entry, NULL); + g_slist_free(list); +} + +static ObjectClass *rx_cpu_class_by_name(const char *cpu_model) +{ + ObjectClass *oc; + char *typename; + + oc = object_class_by_name(cpu_model); + if (oc != NULL && object_class_dynamic_cast(oc, TYPE_RX_CPU) != NULL && + !object_class_is_abstract(oc)) { + return oc; + } + typename = g_strdup_printf(RX_CPU_TYPE_NAME("%s"), cpu_model); + oc = object_class_by_name(typename); + g_free(typename); + if (oc != NULL && object_class_is_abstract(oc)) { + oc = NULL; + } + + return oc; +} + +static void rx_cpu_realize(DeviceState *dev, Error **errp) +{ + CPUState *cs = CPU(dev); + RXCPUClass *rcc = RXCPU_GET_CLASS(dev); + Error *local_err = NULL; + + cpu_exec_realizefn(cs, &local_err); + if (local_err != NULL) { + error_propagate(errp, local_err); + return; + } + + qemu_init_vcpu(cs); + cpu_reset(cs); + + rcc->parent_realize(dev, errp); +} + +static void rx_cpu_set_irq(void *opaque, int no, int request) +{ + RXCPU *cpu = opaque; + CPUState *cs = CPU(cpu); + int irq = request & 0xff; + + static const int mask[] = { + [RX_CPU_IRQ] = CPU_INTERRUPT_HARD, + [RX_CPU_FIR] = CPU_INTERRUPT_FIR, + }; + if (irq) { + cpu->env.req_irq = irq; + cpu->env.req_ipl = (request >> 8) & 0x0f; + cpu_interrupt(cs, mask[no]); + } else { + cpu_reset_interrupt(cs, mask[no]); + } +} + +static void rx_cpu_disas_set_info(CPUState *cpu, disassemble_info *info) +{ + info->mach = bfd_mach_rx; + info->print_insn = print_insn_rx; +} + +static bool rx_cpu_tlb_fill(CPUState *cs, vaddr addr, int size, + MMUAccessType access_type, int mmu_idx, + bool probe, uintptr_t retaddr) +{ + uint32_t address, physical, prot; + + /* Linear mapping */ + address = physical = addr & TARGET_PAGE_MASK; + prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; + tlb_set_page(cs, address, physical, prot, mmu_idx, TARGET_PAGE_SIZE); + return true; +} + +static void rx_cpu_init(Object *obj) +{ + CPUState *cs = CPU(obj); + RXCPU *cpu = RXCPU(obj); + CPURXState *env = &cpu->env; + + cpu_set_cpustate_pointers(cpu); + cs->env_ptr = env; + qdev_init_gpio_in(DEVICE(cpu), rx_cpu_set_irq, 2); +} + +static void rx_cpu_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + CPUClass *cc = CPU_CLASS(klass); + RXCPUClass *rcc = RXCPU_CLASS(klass); + + device_class_set_parent_realize(dc, rx_cpu_realize, + &rcc->parent_realize); + device_class_set_parent_reset(dc, rx_cpu_reset, + &rcc->parent_reset); + + cc->class_by_name = rx_cpu_class_by_name; + cc->has_work = rx_cpu_has_work; + cc->do_interrupt = rx_cpu_do_interrupt; + cc->cpu_exec_interrupt = rx_cpu_exec_interrupt; + cc->dump_state = rx_cpu_dump_state; + cc->set_pc = rx_cpu_set_pc; + cc->synchronize_from_tb = rx_cpu_synchronize_from_tb; + cc->gdb_read_register = rx_cpu_gdb_read_register; + cc->gdb_write_register = rx_cpu_gdb_write_register; + cc->get_phys_page_debug = rx_cpu_get_phys_page_debug; + cc->disas_set_info = rx_cpu_disas_set_info; + cc->tcg_initialize = rx_translate_init; + cc->tlb_fill = rx_cpu_tlb_fill; + + cc->gdb_num_core_regs = 26; + cc->gdb_core_xml_file = "rx-core.xml"; +} + +static const TypeInfo rx_cpu_info = { + .name = TYPE_RX_CPU, + .parent = TYPE_CPU, + .instance_size = sizeof(RXCPU), + .instance_init = rx_cpu_init, + .abstract = true, + .class_size = sizeof(RXCPUClass), + .class_init = rx_cpu_class_init, +}; + +static const TypeInfo rx62n_rx_cpu_info = { + .name = TYPE_RX62N_CPU, + .parent = TYPE_RX_CPU, +}; + +static void rx_cpu_register_types(void) +{ + type_register_static(&rx_cpu_info); + type_register_static(&rx62n_rx_cpu_info); +} + +type_init(rx_cpu_register_types) diff --git a/target/rx/cpu.h b/target/rx/cpu.h new file mode 100644 index 0000000000..d1fb1ef3ca --- /dev/null +++ b/target/rx/cpu.h @@ -0,0 +1,180 @@ +/* + * RX emulation definition + * + * Copyright (c) 2019 Yoshinori Sato + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#ifndef RX_CPU_H +#define RX_CPU_H + +#include "qemu/bitops.h" +#include "qemu-common.h" +#include "hw/registerfields.h" +#include "cpu-qom.h" + +#include "exec/cpu-defs.h" + +/* PSW define */ +REG32(PSW, 0) +FIELD(PSW, C, 0, 1) +FIELD(PSW, Z, 1, 1) +FIELD(PSW, S, 2, 1) +FIELD(PSW, O, 3, 1) +FIELD(PSW, I, 16, 1) +FIELD(PSW, U, 17, 1) +FIELD(PSW, PM, 20, 1) +FIELD(PSW, IPL, 24, 4) + +/* FPSW define */ +REG32(FPSW, 0) +FIELD(FPSW, RM, 0, 2) +FIELD(FPSW, CV, 2, 1) +FIELD(FPSW, CO, 3, 1) +FIELD(FPSW, CZ, 4, 1) +FIELD(FPSW, CU, 5, 1) +FIELD(FPSW, CX, 6, 1) +FIELD(FPSW, CE, 7, 1) +FIELD(FPSW, CAUSE, 2, 6) +FIELD(FPSW, DN, 8, 1) +FIELD(FPSW, EV, 10, 1) +FIELD(FPSW, EO, 11, 1) +FIELD(FPSW, EZ, 12, 1) +FIELD(FPSW, EU, 13, 1) +FIELD(FPSW, EX, 14, 1) +FIELD(FPSW, ENABLE, 10, 5) +FIELD(FPSW, FV, 26, 1) +FIELD(FPSW, FO, 27, 1) +FIELD(FPSW, FZ, 28, 1) +FIELD(FPSW, FU, 29, 1) +FIELD(FPSW, FX, 30, 1) +FIELD(FPSW, FLAGS, 26, 4) +FIELD(FPSW, FS, 31, 1) + +enum { + NUM_REGS = 16, +}; + +typedef struct CPURXState { + /* CPU registers */ + uint32_t regs[NUM_REGS]; /* general registers */ + uint32_t psw_o; /* O bit of status register */ + uint32_t psw_s; /* S bit of status register */ + uint32_t psw_z; /* Z bit of status register */ + uint32_t psw_c; /* C bit of status register */ + uint32_t psw_u; + uint32_t psw_i; + uint32_t psw_pm; + uint32_t psw_ipl; + uint32_t bpsw; /* backup status */ + uint32_t bpc; /* backup pc */ + uint32_t isp; /* global base register */ + uint32_t usp; /* vector base register */ + uint32_t pc; /* program counter */ + uint32_t intb; /* interrupt vector */ + uint32_t fintv; + uint32_t fpsw; + uint64_t acc; + + /* Fields up to this point are cleared by a CPU reset */ + struct {} end_reset_fields; + + /* Internal use */ + uint32_t in_sleep; + uint32_t req_irq; /* Requested interrupt no (hard) */ + uint32_t req_ipl; /* Requested interrupt level */ + uint32_t ack_irq; /* execute irq */ + uint32_t ack_ipl; /* execute ipl */ + float_status fp_status; + qemu_irq ack; /* Interrupt acknowledge */ +} CPURXState; + +/* + * RXCPU: + * @env: #CPURXState + * + * A RX CPU + */ +struct RXCPU { + /*< private >*/ + CPUState parent_obj; + /*< public >*/ + + CPUNegativeOffsetState neg; + CPURXState env; +}; + +typedef struct RXCPU RXCPU; +typedef RXCPU ArchCPU; + +#define ENV_OFFSET offsetof(RXCPU, env) + +#define RX_CPU_TYPE_SUFFIX "-" TYPE_RX_CPU +#define RX_CPU_TYPE_NAME(model) model RX_CPU_TYPE_SUFFIX +#define CPU_RESOLVING_TYPE TYPE_RX_CPU + +const char *rx_crname(uint8_t cr); +void rx_cpu_do_interrupt(CPUState *cpu); +bool rx_cpu_exec_interrupt(CPUState *cpu, int int_req); +void rx_cpu_dump_state(CPUState *cpu, FILE *f, int flags); +int rx_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); +int rx_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); +hwaddr rx_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); + +void rx_translate_init(void); +int cpu_rx_signal_handler(int host_signum, void *pinfo, + void *puc); + +void rx_cpu_list(void); +void rx_cpu_unpack_psw(CPURXState *env, uint32_t psw, int rte); + +#define cpu_signal_handler cpu_rx_signal_handler +#define cpu_list rx_cpu_list + +#include "exec/cpu-all.h" + +#define CPU_INTERRUPT_SOFT CPU_INTERRUPT_TGT_INT_0 +#define CPU_INTERRUPT_FIR CPU_INTERRUPT_TGT_INT_1 + +#define RX_CPU_IRQ 0 +#define RX_CPU_FIR 1 + +static inline void cpu_get_tb_cpu_state(CPURXState *env, target_ulong *pc, + target_ulong *cs_base, uint32_t *flags) +{ + *pc = env->pc; + *cs_base = 0; + *flags = FIELD_DP32(0, PSW, PM, env->psw_pm); +} + +static inline int cpu_mmu_index(CPURXState *env, bool ifetch) +{ + return 0; +} + +static inline uint32_t rx_cpu_pack_psw(CPURXState *env) +{ + uint32_t psw = 0; + psw = FIELD_DP32(psw, PSW, IPL, env->psw_ipl); + psw = FIELD_DP32(psw, PSW, PM, env->psw_pm); + psw = FIELD_DP32(psw, PSW, U, env->psw_u); + psw = FIELD_DP32(psw, PSW, I, env->psw_i); + psw = FIELD_DP32(psw, PSW, O, env->psw_o >> 31); + psw = FIELD_DP32(psw, PSW, S, env->psw_s >> 31); + psw = FIELD_DP32(psw, PSW, Z, env->psw_z == 0); + psw = FIELD_DP32(psw, PSW, C, env->psw_c); + return psw; +} + +#endif /* RX_CPU_H */ diff --git a/target/rx/disas.c b/target/rx/disas.c new file mode 100644 index 0000000000..6dee7a0342 --- /dev/null +++ b/target/rx/disas.c @@ -0,0 +1,1446 @@ +/* + * Renesas RX Disassembler + * + * Copyright (c) 2019 Yoshinori Sato + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include "qemu/osdep.h" +#include "disas/dis-asm.h" +#include "qemu/bitops.h" +#include "cpu.h" + +typedef struct DisasContext { + disassemble_info *dis; + uint32_t addr; + uint32_t pc; + uint8_t len; + uint8_t bytes[8]; +} DisasContext; + + +static uint32_t decode_load_bytes(DisasContext *ctx, uint32_t insn, + int i, int n) +{ + uint32_t addr = ctx->addr; + + g_assert(ctx->len == i); + g_assert(n <= ARRAY_SIZE(ctx->bytes)); + + while (++i <= n) { + ctx->dis->read_memory_func(addr++, &ctx->bytes[i - 1], 1, ctx->dis); + insn |= ctx->bytes[i - 1] << (32 - i * 8); + } + ctx->addr = addr; + ctx->len = n; + + return insn; +} + +static int32_t li(DisasContext *ctx, int sz) +{ + uint32_t addr = ctx->addr; + uintptr_t len = ctx->len; + + switch (sz) { + case 1: + g_assert(len + 1 <= ARRAY_SIZE(ctx->bytes)); + ctx->addr += 1; + ctx->len += 1; + ctx->dis->read_memory_func(addr, ctx->bytes + len, 1, ctx->dis); + return (int8_t)ctx->bytes[len]; + case 2: + g_assert(len + 2 <= ARRAY_SIZE(ctx->bytes)); + ctx->addr += 2; + ctx->len += 2; + ctx->dis->read_memory_func(addr, ctx->bytes + len, 2, ctx->dis); + return ldsw_le_p(ctx->bytes + len); + case 3: + g_assert(len + 3 <= ARRAY_SIZE(ctx->bytes)); + ctx->addr += 3; + ctx->len += 3; + ctx->dis->read_memory_func(addr, ctx->bytes + len, 3, ctx->dis); + return (int8_t)ctx->bytes[len + 2] << 16 | lduw_le_p(ctx->bytes + len); + case 0: + g_assert(len + 4 <= ARRAY_SIZE(ctx->bytes)); + ctx->addr += 4; + ctx->len += 4; + ctx->dis->read_memory_func(addr, ctx->bytes + len, 4, ctx->dis); + return ldl_le_p(ctx->bytes + len); + default: + g_assert_not_reached(); + } +} + +static int bdsp_s(DisasContext *ctx, int d) +{ + /* + * 0 -> 8 + * 1 -> 9 + * 2 -> 10 + * 3 -> 3 + * : + * 7 -> 7 + */ + if (d < 3) { + d += 8; + } + return d; +} + +/* Include the auto-generated decoder. */ +#include "decode.inc.c" + +static void dump_bytes(DisasContext *ctx) +{ + int i, len = ctx->len; + + for (i = 0; i < len; ++i) { + ctx->dis->fprintf_func(ctx->dis->stream, "%02x ", ctx->bytes[i]); + } + ctx->dis->fprintf_func(ctx->dis->stream, "%*c", (8 - i) * 3, '\t'); +} + +#define prt(...) \ + do { \ + dump_bytes(ctx); \ + ctx->dis->fprintf_func(ctx->dis->stream, __VA_ARGS__); \ + } while (0) + +#define RX_MEMORY_BYTE 0 +#define RX_MEMORY_WORD 1 +#define RX_MEMORY_LONG 2 + +#define RX_IM_BYTE 0 +#define RX_IM_WORD 1 +#define RX_IM_LONG 2 +#define RX_IM_UWORD 3 + +static const char size[] = {'b', 'w', 'l'}; +static const char cond[][4] = { + "eq", "ne", "c", "nc", "gtu", "leu", "pz", "n", + "ge", "lt", "gt", "le", "o", "no", "ra", "f" +}; +static const char psw[] = { + 'c', 'z', 's', 'o', 0, 0, 0, 0, + 'i', 'u', 0, 0, 0, 0, 0, 0, +}; + +static void rx_index_addr(DisasContext *ctx, char out[8], int ld, int mi) +{ + uint32_t addr = ctx->addr; + uintptr_t len = ctx->len; + uint16_t dsp; + + switch (ld) { + case 0: + /* No index; return empty string. */ + out[0] = '\0'; + return; + case 1: + g_assert(len + 1 <= ARRAY_SIZE(ctx->bytes)); + ctx->addr += 1; + ctx->len += 1; + ctx->dis->read_memory_func(addr, ctx->bytes + len, 1, ctx->dis); + dsp = ctx->bytes[len]; + break; + case 2: + g_assert(len + 2 <= ARRAY_SIZE(ctx->bytes)); + ctx->addr += 2; + ctx->len += 2; + ctx->dis->read_memory_func(addr, ctx->bytes + len, 2, ctx->dis); + dsp = lduw_le_p(ctx->bytes + len); + break; + default: + g_assert_not_reached(); + } + + sprintf(out, "%u", dsp << (mi < 3 ? mi : 4 - mi)); +} + +static void prt_ldmi(DisasContext *ctx, const char *insn, + int ld, int mi, int rs, int rd) +{ + static const char sizes[][4] = {".b", ".w", ".l", ".uw", ".ub"}; + char dsp[8]; + + if (ld < 3) { + rx_index_addr(ctx, dsp, ld, mi); + prt("%s\t%s[r%d]%s, r%d", insn, dsp, rs, sizes[mi], rd); + } else { + prt("%s\tr%d, r%d", insn, rs, rd); + } +} + +static void prt_ir(DisasContext *ctx, const char *insn, int imm, int rd) +{ + if (imm < 0x100) { + prt("%s\t#%d, r%d", insn, imm, rd); + } else { + prt("%s\t#0x%08x, r%d", insn, imm, rd); + } +} + +/* mov.[bwl] rs,dsp:[rd] */ +static bool trans_MOV_rm(DisasContext *ctx, arg_MOV_rm *a) +{ + if (a->dsp > 0) { + prt("mov.%c\tr%d,%d[r%d]", + size[a->sz], a->rs, a->dsp << a->sz, a->rd); + } else { + prt("mov.%c\tr%d,[r%d]", + size[a->sz], a->rs, a->rd); + } + return true; +} + +/* mov.[bwl] dsp:[rs],rd */ +static bool trans_MOV_mr(DisasContext *ctx, arg_MOV_mr *a) +{ + if (a->dsp > 0) { + prt("mov.%c\t%d[r%d], r%d", + size[a->sz], a->dsp << a->sz, a->rs, a->rd); + } else { + prt("mov.%c\t[r%d], r%d", + size[a->sz], a->rs, a->rd); + } + return true; +} + +/* mov.l #uimm4,rd */ +/* mov.l #uimm8,rd */ +/* mov.l #imm,rd */ +static bool trans_MOV_ir(DisasContext *ctx, arg_MOV_ir *a) +{ + prt_ir(ctx, "mov.l", a->imm, a->rd); + return true; +} + +/* mov.[bwl] #uimm8,dsp:[rd] */ +/* mov #imm, dsp:[rd] */ +static bool trans_MOV_im(DisasContext *ctx, arg_MOV_im *a) +{ + if (a->dsp > 0) { + prt("mov.%c\t#%d,%d[r%d]", + size[a->sz], a->imm, a->dsp << a->sz, a->rd); + } else { + prt("mov.%c\t#%d,[r%d]", + size[a->sz], a->imm, a->rd); + } + return true; +} + +/* mov.[bwl] [ri,rb],rd */ +static bool trans_MOV_ar(DisasContext *ctx, arg_MOV_ar *a) +{ + prt("mov.%c\t[r%d,r%d], r%d", size[a->sz], a->ri, a->rb, a->rd); + return true; +} + +/* mov.[bwl] rd,[ri,rb] */ +static bool trans_MOV_ra(DisasContext *ctx, arg_MOV_ra *a) +{ + prt("mov.%c\tr%d, [r%d, r%d]", size[a->sz], a->rs, a->ri, a->rb); + return true; +} + + +/* mov.[bwl] dsp:[rs],dsp:[rd] */ +/* mov.[bwl] rs,dsp:[rd] */ +/* mov.[bwl] dsp:[rs],rd */ +/* mov.[bwl] rs,rd */ +static bool trans_MOV_mm(DisasContext *ctx, arg_MOV_mm *a) +{ + char dspd[8], dsps[8], szc = size[a->sz]; + + if (a->lds == 3 && a->ldd == 3) { + /* mov.[bwl] rs,rd */ + prt("mov.%c\tr%d, r%d", szc, a->rs, a->rd); + } else if (a->lds == 3) { + rx_index_addr(ctx, dspd, a->ldd, a->sz); + prt("mov.%c\tr%d, %s[r%d]", szc, a->rs, dspd, a->rd); + } else if (a->ldd == 3) { + rx_index_addr(ctx, dsps, a->lds, a->sz); + prt("mov.%c\t%s[r%d], r%d", szc, dsps, a->rs, a->rd); + } else { + rx_index_addr(ctx, dsps, a->lds, a->sz); + rx_index_addr(ctx, dspd, a->ldd, a->sz); + prt("mov.%c\t%s[r%d], %s[r%d]", szc, dsps, a->rs, dspd, a->rd); + } + return true; +} + +/* mov.[bwl] rs,[rd+] */ +/* mov.[bwl] rs,[-rd] */ +static bool trans_MOV_rp(DisasContext *ctx, arg_MOV_rp *a) +{ + if (a->ad) { + prt("mov.%c\tr%d, [-r%d]", size[a->sz], a->rs, a->rd); + } else { + prt("mov.%c\tr%d, [r%d+]", size[a->sz], a->rs, a->rd); + } + return true; +} + +/* mov.[bwl] [rd+],rs */ +/* mov.[bwl] [-rd],rs */ +static bool trans_MOV_pr(DisasContext *ctx, arg_MOV_pr *a) +{ + if (a->ad) { + prt("mov.%c\t[-r%d], r%d", size[a->sz], a->rd, a->rs); + } else { + prt("mov.%c\t[r%d+], r%d", size[a->sz], a->rd, a->rs); + } + return true; +} + +/* movu.[bw] dsp5:[rs],rd */ +static bool trans_MOVU_mr(DisasContext *ctx, arg_MOVU_mr *a) +{ + if (a->dsp > 0) { + prt("movu.%c\t%d[r%d], r%d", size[a->sz], + a->dsp << a->sz, a->rs, a->rd); + } else { + prt("movu.%c\t[r%d], r%d", size[a->sz], a->rs, a->rd); + } + return true; +} + +/* movu.[bw] rs,rd */ +static bool trans_MOVU_rr(DisasContext *ctx, arg_MOVU_rr *a) +{ + prt("movu.%c\tr%d, r%d", size[a->sz], a->rs, a->rd); + return true; +} + +/* movu.[bw] [ri,rb],rd */ +static bool trans_MOVU_ar(DisasContext *ctx, arg_MOVU_ar *a) +{ + prt("mov.%c\t[r%d,r%d], r%d", size[a->sz], a->ri, a->rb, a->rd); + return true; +} + +/* movu.[bw] [rs+],rd */ +/* movu.[bw] [-rs],rd */ +static bool trans_MOVU_pr(DisasContext *ctx, arg_MOVU_pr *a) +{ + if (a->ad) { + prt("movu.%c\t[-r%d], r%d", size[a->sz], a->rd, a->rs); + } else { + prt("movu.%c\t[r%d+], r%d", size[a->sz], a->rd, a->rs); + } + return true; +} + +/* pop rd */ +static bool trans_POP(DisasContext *ctx, arg_POP *a) +{ + prt("pop\tr%d", a->rd); + return true; +} + +/* popc rx */ +static bool trans_POPC(DisasContext *ctx, arg_POPC *a) +{ + prt("pop\tr%s", rx_crname(a->cr)); + return true; +} + +/* popm rd-rd2 */ +static bool trans_POPM(DisasContext *ctx, arg_POPM *a) +{ + prt("popm\tr%d-r%d", a->rd, a->rd2); + return true; +} + +/* push rs */ +static bool trans_PUSH_r(DisasContext *ctx, arg_PUSH_r *a) +{ + prt("push\tr%d", a->rs); + return true; +} + +/* push dsp[rs] */ +static bool trans_PUSH_m(DisasContext *ctx, arg_PUSH_m *a) +{ + char dsp[8]; + + rx_index_addr(ctx, dsp, a->ld, a->sz); + prt("push\t%s[r%d]", dsp, a->rs); + return true; +} + +/* pushc rx */ +static bool trans_PUSHC(DisasContext *ctx, arg_PUSHC *a) +{ + prt("push\t%s", rx_crname(a->cr)); + return true; +} + +/* pushm rs-rs2*/ +static bool trans_PUSHM(DisasContext *ctx, arg_PUSHM *a) +{ + prt("pushm\tr%d-r%d", a->rs, a->rs2); + return true; +} + +/* xchg rs,rd */ +static bool trans_XCHG_rr(DisasContext *ctx, arg_XCHG_rr *a) +{ + prt("xchg\tr%d, r%d", a->rs, a->rd); + return true; +} +/* xchg dsp[rs].,rd */ +static bool trans_XCHG_mr(DisasContext *ctx, arg_XCHG_mr *a) +{ + prt_ldmi(ctx, "xchg", a->ld, a->mi, a->rs, a->rd); + return true; +} + +/* stz #imm,rd */ +static bool trans_STZ(DisasContext *ctx, arg_STZ *a) +{ + prt_ir(ctx, "stz", a->imm, a->rd); + return true; +} + +/* stnz #imm,rd */ +static bool trans_STNZ(DisasContext *ctx, arg_STNZ *a) +{ + prt_ir(ctx, "stnz", a->imm, a->rd); + return true; +} + +/* rtsd #imm */ +static bool trans_RTSD_i(DisasContext *ctx, arg_RTSD_i *a) +{ + prt("rtsd\t#%d", a->imm << 2); + return true; +} + +/* rtsd #imm, rd-rd2 */ +static bool trans_RTSD_irr(DisasContext *ctx, arg_RTSD_irr *a) +{ + prt("rtsd\t#%d, r%d - r%d", a->imm << 2, a->rd, a->rd2); + return true; +} + +/* and #uimm:4, rd */ +/* and #imm, rd */ +static bool trans_AND_ir(DisasContext *ctx, arg_AND_ir *a) +{ + prt_ir(ctx, "and", a->imm, a->rd); + return true; +} + +/* and dsp[rs], rd */ +/* and rs,rd */ +static bool trans_AND_mr(DisasContext *ctx, arg_AND_mr *a) +{ + prt_ldmi(ctx, "and", a->ld, a->mi, a->rs, a->rd); + return true; +} + +/* and rs,rs2,rd */ +static bool trans_AND_rrr(DisasContext *ctx, arg_AND_rrr *a) +{ + prt("and\tr%d,r%d, r%d", a->rs, a->rs2, a->rd); + return true; +} + +/* or #uimm:4, rd */ +/* or #imm, rd */ +static bool trans_OR_ir(DisasContext *ctx, arg_OR_ir *a) +{ + prt_ir(ctx, "or", a->imm, a->rd); + return true; +} + +/* or dsp[rs], rd */ +/* or rs,rd */ +static bool trans_OR_mr(DisasContext *ctx, arg_OR_mr *a) +{ + prt_ldmi(ctx, "or", a->ld, a->mi, a->rs, a->rd); + return true; +} + +/* or rs,rs2,rd */ +static bool trans_OR_rrr(DisasContext *ctx, arg_OR_rrr *a) +{ + prt("or\tr%d, r%d, r%d", a->rs, a->rs2, a->rd); + return true; +} + +/* xor #imm, rd */ +static bool trans_XOR_ir(DisasContext *ctx, arg_XOR_ir *a) +{ + prt_ir(ctx, "xor", a->imm, a->rd); + return true; +} + +/* xor dsp[rs], rd */ +/* xor rs,rd */ +static bool trans_XOR_mr(DisasContext *ctx, arg_XOR_mr *a) +{ + prt_ldmi(ctx, "xor", a->ld, a->mi, a->rs, a->rd); + return true; +} + +/* tst #imm, rd */ +static bool trans_TST_ir(DisasContext *ctx, arg_TST_ir *a) +{ + prt_ir(ctx, "tst", a->imm, a->rd); + return true; +} + +/* tst dsp[rs], rd */ +/* tst rs, rd */ +static bool trans_TST_mr(DisasContext *ctx, arg_TST_mr *a) +{ + prt_ldmi(ctx, "tst", a->ld, a->mi, a->rs, a->rd); + return true; +} + +/* not rd */ +/* not rs, rd */ +static bool trans_NOT_rr(DisasContext *ctx, arg_NOT_rr *a) +{ + if (a->rs != a->rd) { + prt("not\tr%d, r%d", a->rs, a->rd); + } else { + prt("not\tr%d", a->rs); + } + return true; +} + +/* neg rd */ +/* neg rs, rd */ +static bool trans_NEG_rr(DisasContext *ctx, arg_NEG_rr *a) +{ + if (a->rs != a->rd) { + prt("neg\tr%d, r%d", a->rs, a->rd); + } else { + prt("neg\tr%d", a->rs); + } + return true; +} + +/* adc #imm, rd */ +static bool trans_ADC_ir(DisasContext *ctx, arg_ADC_ir *a) +{ + prt_ir(ctx, "adc", a->imm, a->rd); + return true; +} + +/* adc rs, rd */ +static bool trans_ADC_rr(DisasContext *ctx, arg_ADC_rr *a) +{ + prt("adc\tr%d, r%d", a->rs, a->rd); + return true; +} + +/* adc dsp[rs], rd */ +static bool trans_ADC_mr(DisasContext *ctx, arg_ADC_mr *a) +{ + char dsp[8]; + + rx_index_addr(ctx, dsp, a->ld, 2); + prt("adc\t%s[r%d], r%d", dsp, a->rs, a->rd); + return true; +} + +/* add #uimm4, rd */ +/* add #imm, rs, rd */ +static bool trans_ADD_irr(DisasContext *ctx, arg_ADD_irr *a) +{ + if (a->imm < 0x10 && a->rs2 == a->rd) { + prt("add\t#%d, r%d", a->imm, a->rd); + } else { + prt("add\t#0x%08x, r%d, r%d", a->imm, a->rs2, a->rd); + } + return true; +} + +/* add rs, rd */ +/* add dsp[rs], rd */ +static bool trans_ADD_mr(DisasContext *ctx, arg_ADD_mr *a) +{ + prt_ldmi(ctx, "add", a->ld, a->mi, a->rs, a->rd); + return true; +} + +/* add rs, rs2, rd */ +static bool trans_ADD_rrr(DisasContext *ctx, arg_ADD_rrr *a) +{ + prt("add\tr%d, r%d, r%d", a->rs, a->rs2, a->rd); + return true; +} + +/* cmp #imm4, rd */ +/* cmp #imm8, rd */ +/* cmp #imm, rs2 */ +static bool trans_CMP_ir(DisasContext *ctx, arg_CMP_ir *a) +{ + prt_ir(ctx, "cmp", a->imm, a->rs2); + return true; +} + +/* cmp rs, rs2 */ +/* cmp dsp[rs], rs2 */ +static bool trans_CMP_mr(DisasContext *ctx, arg_CMP_mr *a) +{ + prt_ldmi(ctx, "cmp", a->ld, a->mi, a->rs, a->rd); + return true; +} + +/* sub #imm4, rd */ +static bool trans_SUB_ir(DisasContext *ctx, arg_SUB_ir *a) +{ + prt("sub\t#%d, r%d", a->imm, a->rd); + return true; +} + +/* sub rs, rd */ +/* sub dsp[rs], rd */ +static bool trans_SUB_mr(DisasContext *ctx, arg_SUB_mr *a) +{ + prt_ldmi(ctx, "sub", a->ld, a->mi, a->rs, a->rd); + return true; +} + +/* sub rs, rs2, rd */ +static bool trans_SUB_rrr(DisasContext *ctx, arg_SUB_rrr *a) +{ + prt("sub\tr%d, r%d, r%d", a->rs, a->rs2, a->rd); + return true; +} + +/* sbb rs, rd */ +static bool trans_SBB_rr(DisasContext *ctx, arg_SBB_rr *a) +{ + prt("sbb\tr%d, r%d", a->rs, a->rd); + return true; +} + +/* sbb dsp[rs], rd */ +static bool trans_SBB_mr(DisasContext *ctx, arg_SBB_mr *a) +{ + prt_ldmi(ctx, "sbb", a->ld, RX_IM_LONG, a->rs, a->rd); + return true; +} + +/* abs rd */ +/* abs rs, rd */ +static bool trans_ABS_rr(DisasContext *ctx, arg_ABS_rr *a) +{ + if (a->rs != a->rd) { + prt("abs\tr%d, r%d", a->rs, a->rd); + } else { + prt("abs\tr%d", a->rs); + } + return true; +} + +/* max #imm, rd */ +static bool trans_MAX_ir(DisasContext *ctx, arg_MAX_ir *a) +{ + prt_ir(ctx, "max", a->imm, a->rd); + return true; +} + +/* max rs, rd */ +/* max dsp[rs], rd */ +static bool trans_MAX_mr(DisasContext *ctx, arg_MAX_mr *a) +{ + prt_ldmi(ctx, "max", a->ld, a->mi, a->rs, a->rd); + return true; +} + +/* min #imm, rd */ +static bool trans_MIN_ir(DisasContext *ctx, arg_MIN_ir *a) +{ + prt_ir(ctx, "min", a->imm, a->rd); + return true; +} + +/* min rs, rd */ +/* min dsp[rs], rd */ +static bool trans_MIN_mr(DisasContext *ctx, arg_MIN_mr *a) +{ + prt_ldmi(ctx, "min", a->ld, a->mi, a->rs, a->rd); + return true; +} + +/* mul #uimm4, rd */ +/* mul #imm, rd */ +static bool trans_MUL_ir(DisasContext *ctx, arg_MUL_ir *a) +{ + prt_ir(ctx, "mul", a->imm, a->rd); + return true; +} + +/* mul rs, rd */ +/* mul dsp[rs], rd */ +static bool trans_MUL_mr(DisasContext *ctx, arg_MUL_mr *a) +{ + prt_ldmi(ctx, "mul", a->ld, a->mi, a->rs, a->rd); + return true; +} + +/* mul rs, rs2, rd */ +static bool trans_MUL_rrr(DisasContext *ctx, arg_MUL_rrr *a) +{ + prt("mul\tr%d,r%d,r%d", a->rs, a->rs2, a->rd); + return true; +} + +/* emul #imm, rd */ +static bool trans_EMUL_ir(DisasContext *ctx, arg_EMUL_ir *a) +{ + prt_ir(ctx, "emul", a->imm, a->rd); + return true; +} + +/* emul rs, rd */ +/* emul dsp[rs], rd */ +static bool trans_EMUL_mr(DisasContext *ctx, arg_EMUL_mr *a) +{ + prt_ldmi(ctx, "emul", a->ld, a->mi, a->rs, a->rd); + return true; +} + +/* emulu #imm, rd */ +static bool trans_EMULU_ir(DisasContext *ctx, arg_EMULU_ir *a) +{ + prt_ir(ctx, "emulu", a->imm, a->rd); + return true; +} + +/* emulu rs, rd */ +/* emulu dsp[rs], rd */ +static bool trans_EMULU_mr(DisasContext *ctx, arg_EMULU_mr *a) +{ + prt_ldmi(ctx, "emulu", a->ld, a->mi, a->rs, a->rd); + return true; +} + +/* div #imm, rd */ +static bool trans_DIV_ir(DisasContext *ctx, arg_DIV_ir *a) +{ + prt_ir(ctx, "div", a->imm, a->rd); + return true; +} + +/* div rs, rd */ +/* div dsp[rs], rd */ +static bool trans_DIV_mr(DisasContext *ctx, arg_DIV_mr *a) +{ + prt_ldmi(ctx, "div", a->ld, a->mi, a->rs, a->rd); + return true; +} + +/* divu #imm, rd */ +static bool trans_DIVU_ir(DisasContext *ctx, arg_DIVU_ir *a) +{ + prt_ir(ctx, "divu", a->imm, a->rd); + return true; +} + +/* divu rs, rd */ +/* divu dsp[rs], rd */ +static bool trans_DIVU_mr(DisasContext *ctx, arg_DIVU_mr *a) +{ + prt_ldmi(ctx, "divu", a->ld, a->mi, a->rs, a->rd); + return true; +} + + +/* shll #imm:5, rd */ +/* shll #imm:5, rs, rd */ +static bool trans_SHLL_irr(DisasContext *ctx, arg_SHLL_irr *a) +{ + if (a->rs2 != a->rd) { + prt("shll\t#%d, r%d, r%d", a->imm, a->rs2, a->rd); + } else { + prt("shll\t#%d, r%d", a->imm, a->rd); + } + return true; +} + +/* shll rs, rd */ +static bool trans_SHLL_rr(DisasContext *ctx, arg_SHLL_rr *a) +{ + prt("shll\tr%d, r%d", a->rs, a->rd); + return true; +} + +/* shar #imm:5, rd */ +/* shar #imm:5, rs, rd */ +static bool trans_SHAR_irr(DisasContext *ctx, arg_SHAR_irr *a) +{ + if (a->rs2 != a->rd) { + prt("shar\t#%d, r%d, r%d", a->imm, a->rs2, a->rd); + } else { + prt("shar\t#%d, r%d", a->imm, a->rd); + } + return true; +} + +/* shar rs, rd */ +static bool trans_SHAR_rr(DisasContext *ctx, arg_SHAR_rr *a) +{ + prt("shar\tr%d, r%d", a->rs, a->rd); + return true; +} + +/* shlr #imm:5, rd */ +/* shlr #imm:5, rs, rd */ +static bool trans_SHLR_irr(DisasContext *ctx, arg_SHLR_irr *a) +{ + if (a->rs2 != a->rd) { + prt("shlr\t#%d, r%d, r%d", a->imm, a->rs2, a->rd); + } else { + prt("shlr\t#%d, r%d", a->imm, a->rd); + } + return true; +} + +/* shlr rs, rd */ +static bool trans_SHLR_rr(DisasContext *ctx, arg_SHLR_rr *a) +{ + prt("shlr\tr%d, r%d", a->rs, a->rd); + return true; +} + +/* rolc rd */ +static bool trans_ROLC(DisasContext *ctx, arg_ROLC *a) +{ + prt("rorc\tr%d", a->rd); + return true; +} + +/* rorc rd */ +static bool trans_RORC(DisasContext *ctx, arg_RORC *a) +{ + prt("rorc\tr%d", a->rd); + return true; +} + +/* rotl #imm, rd */ +static bool trans_ROTL_ir(DisasContext *ctx, arg_ROTL_ir *a) +{ + prt("rotl\t#%d, r%d", a->imm, a->rd); + return true; +} + +/* rotl rs, rd */ +static bool trans_ROTL_rr(DisasContext *ctx, arg_ROTL_rr *a) +{ + prt("rotl\tr%d, r%d", a->rs, a->rd); + return true; +} + +/* rotr #imm, rd */ +static bool trans_ROTR_ir(DisasContext *ctx, arg_ROTR_ir *a) +{ + prt("rotr\t#%d, r%d", a->imm, a->rd); + return true; +} + +/* rotr rs, rd */ +static bool trans_ROTR_rr(DisasContext *ctx, arg_ROTR_rr *a) +{ + prt("rotr\tr%d, r%d", a->rs, a->rd); + return true; +} + +/* revl rs, rd */ +static bool trans_REVL(DisasContext *ctx, arg_REVL *a) +{ + prt("revl\tr%d, r%d", a->rs, a->rd); + return true; +} + +/* revw rs, rd */ +static bool trans_REVW(DisasContext *ctx, arg_REVW *a) +{ + prt("revw\tr%d, r%d", a->rs, a->rd); + return true; +} + +/* conditional branch helper */ +static void rx_bcnd_main(DisasContext *ctx, int cd, int len, int dst) +{ + static const char sz[] = {'s', 'b', 'w', 'a'}; + prt("b%s.%c\t%08x", cond[cd], sz[len - 1], ctx->pc + dst); +} + +/* beq dsp:3 / bne dsp:3 */ +/* beq dsp:8 / bne dsp:8 */ +/* bc dsp:8 / bnc dsp:8 */ +/* bgtu dsp:8 / bleu dsp:8 */ +/* bpz dsp:8 / bn dsp:8 */ +/* bge dsp:8 / blt dsp:8 */ +/* bgt dsp:8 / ble dsp:8 */ +/* bo dsp:8 / bno dsp:8 */ +/* beq dsp:16 / bne dsp:16 */ +static bool trans_BCnd(DisasContext *ctx, arg_BCnd *a) +{ + rx_bcnd_main(ctx, a->cd, a->sz, a->dsp); + return true; +} + +/* bra dsp:3 */ +/* bra dsp:8 */ +/* bra dsp:16 */ +/* bra dsp:24 */ +static bool trans_BRA(DisasContext *ctx, arg_BRA *a) +{ + rx_bcnd_main(ctx, 14, a->sz, a->dsp); + return true; +} + +/* bra rs */ +static bool trans_BRA_l(DisasContext *ctx, arg_BRA_l *a) +{ + prt("bra.l\tr%d", a->rd); + return true; +} + +/* jmp rs */ +static bool trans_JMP(DisasContext *ctx, arg_JMP *a) +{ + prt("jmp\tr%d", a->rs); + return true; +} + +/* jsr rs */ +static bool trans_JSR(DisasContext *ctx, arg_JSR *a) +{ + prt("jsr\tr%d", a->rs); + return true; +} + +/* bsr dsp:16 */ +/* bsr dsp:24 */ +static bool trans_BSR(DisasContext *ctx, arg_BSR *a) +{ + static const char sz[] = {'w', 'a'}; + prt("bsr.%c\t%08x", sz[a->sz - 3], ctx->pc + a->dsp); + return true; +} + +/* bsr rs */ +static bool trans_BSR_l(DisasContext *ctx, arg_BSR_l *a) +{ + prt("bsr.l\tr%d", a->rd); + return true; +} + +/* rts */ +static bool trans_RTS(DisasContext *ctx, arg_RTS *a) +{ + prt("rts"); + return true; +} + +/* nop */ +static bool trans_NOP(DisasContext *ctx, arg_NOP *a) +{ + prt("nop"); + return true; +} + +/* scmpu */ +static bool trans_SCMPU(DisasContext *ctx, arg_SCMPU *a) +{ + prt("scmpu"); + return true; +} + +/* smovu */ +static bool trans_SMOVU(DisasContext *ctx, arg_SMOVU *a) +{ + prt("smovu"); + return true; +} + +/* smovf */ +static bool trans_SMOVF(DisasContext *ctx, arg_SMOVF *a) +{ + prt("smovf"); + return true; +} + +/* smovb */ +static bool trans_SMOVB(DisasContext *ctx, arg_SMOVB *a) +{ + prt("smovb"); + return true; +} + +/* suntile */ +static bool trans_SUNTIL(DisasContext *ctx, arg_SUNTIL *a) +{ + prt("suntil.%c", size[a->sz]); + return true; +} + +/* swhile */ +static bool trans_SWHILE(DisasContext *ctx, arg_SWHILE *a) +{ + prt("swhile.%c", size[a->sz]); + return true; +} +/* sstr */ +static bool trans_SSTR(DisasContext *ctx, arg_SSTR *a) +{ + prt("sstr.%c", size[a->sz]); + return true; +} + +/* rmpa */ +static bool trans_RMPA(DisasContext *ctx, arg_RMPA *a) +{ + prt("rmpa.%c", size[a->sz]); + return true; +} + +/* mulhi rs,rs2 */ +static bool trans_MULHI(DisasContext *ctx, arg_MULHI *a) +{ + prt("mulhi\tr%d,r%d", a->rs, a->rs2); + return true; +} + +/* mullo rs,rs2 */ +static bool trans_MULLO(DisasContext *ctx, arg_MULLO *a) +{ + prt("mullo\tr%d, r%d", a->rs, a->rs2); + return true; +} + +/* machi rs,rs2 */ +static bool trans_MACHI(DisasContext *ctx, arg_MACHI *a) +{ + prt("machi\tr%d, r%d", a->rs, a->rs2); + return true; +} + +/* maclo rs,rs2 */ +static bool trans_MACLO(DisasContext *ctx, arg_MACLO *a) +{ + prt("maclo\tr%d, r%d", a->rs, a->rs2); + return true; +} + +/* mvfachi rd */ +static bool trans_MVFACHI(DisasContext *ctx, arg_MVFACHI *a) +{ + prt("mvfachi\tr%d", a->rd); + return true; +} + +/* mvfacmi rd */ +static bool trans_MVFACMI(DisasContext *ctx, arg_MVFACMI *a) +{ + prt("mvfacmi\tr%d", a->rd); + return true; +} + +/* mvtachi rs */ +static bool trans_MVTACHI(DisasContext *ctx, arg_MVTACHI *a) +{ + prt("mvtachi\tr%d", a->rs); + return true; +} + +/* mvtaclo rs */ +static bool trans_MVTACLO(DisasContext *ctx, arg_MVTACLO *a) +{ + prt("mvtaclo\tr%d", a->rs); + return true; +} + +/* racw #imm */ +static bool trans_RACW(DisasContext *ctx, arg_RACW *a) +{ + prt("racw\t#%d", a->imm + 1); + return true; +} + +/* sat rd */ +static bool trans_SAT(DisasContext *ctx, arg_SAT *a) +{ + prt("sat\tr%d", a->rd); + return true; +} + +/* satr */ +static bool trans_SATR(DisasContext *ctx, arg_SATR *a) +{ + prt("satr"); + return true; +} + +/* fadd #imm, rd */ +static bool trans_FADD_ir(DisasContext *ctx, arg_FADD_ir *a) +{ + prt("fadd\t#%d,r%d", li(ctx, 0), a->rd); + return true; +} + +/* fadd dsp[rs], rd */ +/* fadd rs, rd */ +static bool trans_FADD_mr(DisasContext *ctx, arg_FADD_mr *a) +{ + prt_ldmi(ctx, "fadd", a->ld, RX_IM_LONG, a->rs, a->rd); + return true; +} + +/* fcmp #imm, rd */ +static bool trans_FCMP_ir(DisasContext *ctx, arg_FCMP_ir *a) +{ + prt("fadd\t#%d,r%d", li(ctx, 0), a->rd); + return true; +} + +/* fcmp dsp[rs], rd */ +/* fcmp rs, rd */ +static bool trans_FCMP_mr(DisasContext *ctx, arg_FCMP_mr *a) +{ + prt_ldmi(ctx, "fcmp", a->ld, RX_IM_LONG, a->rs, a->rd); + return true; +} + +/* fsub #imm, rd */ +static bool trans_FSUB_ir(DisasContext *ctx, arg_FSUB_ir *a) +{ + prt("fsub\t#%d,r%d", li(ctx, 0), a->rd); + return true; +} + +/* fsub dsp[rs], rd */ +/* fsub rs, rd */ +static bool trans_FSUB_mr(DisasContext *ctx, arg_FSUB_mr *a) +{ + prt_ldmi(ctx, "fsub", a->ld, RX_IM_LONG, a->rs, a->rd); + return true; +} + +/* ftoi dsp[rs], rd */ +/* ftoi rs, rd */ +static bool trans_FTOI(DisasContext *ctx, arg_FTOI *a) +{ + prt_ldmi(ctx, "ftoi", a->ld, RX_IM_LONG, a->rs, a->rd); + return true; +} + +/* fmul #imm, rd */ +static bool trans_FMUL_ir(DisasContext *ctx, arg_FMUL_ir *a) +{ + prt("fmul\t#%d,r%d", li(ctx, 0), a->rd); + return true; +} + +/* fmul dsp[rs], rd */ +/* fmul rs, rd */ +static bool trans_FMUL_mr(DisasContext *ctx, arg_FMUL_mr *a) +{ + prt_ldmi(ctx, "fmul", a->ld, RX_IM_LONG, a->rs, a->rd); + return true; +} + +/* fdiv #imm, rd */ +static bool trans_FDIV_ir(DisasContext *ctx, arg_FDIV_ir *a) +{ + prt("fdiv\t#%d,r%d", li(ctx, 0), a->rd); + return true; +} + +/* fdiv dsp[rs], rd */ +/* fdiv rs, rd */ +static bool trans_FDIV_mr(DisasContext *ctx, arg_FDIV_mr *a) +{ + prt_ldmi(ctx, "fdiv", a->ld, RX_IM_LONG, a->rs, a->rd); + return true; +} + +/* round dsp[rs], rd */ +/* round rs, rd */ +static bool trans_ROUND(DisasContext *ctx, arg_ROUND *a) +{ + prt_ldmi(ctx, "round", a->ld, RX_IM_LONG, a->rs, a->rd); + return true; +} + +/* itof rs, rd */ +/* itof dsp[rs], rd */ +static bool trans_ITOF(DisasContext *ctx, arg_ITOF *a) +{ + prt_ldmi(ctx, "itof", a->ld, RX_IM_LONG, a->rs, a->rd); + return true; +} + +#define BOP_IM(name, reg) \ + do { \ + char dsp[8]; \ + rx_index_addr(ctx, dsp, a->ld, RX_MEMORY_BYTE); \ + prt("b%s\t#%d, %s[r%d]", #name, a->imm, dsp, reg); \ + return true; \ + } while (0) + +#define BOP_RM(name) \ + do { \ + char dsp[8]; \ + rx_index_addr(ctx, dsp, a->ld, RX_MEMORY_BYTE); \ + prt("b%s\tr%d, %s[r%d]", #name, a->rd, dsp, a->rs); \ + return true; \ + } while (0) + +/* bset #imm, dsp[rd] */ +static bool trans_BSET_im(DisasContext *ctx, arg_BSET_im *a) +{ + BOP_IM(bset, a->rs); +} + +/* bset rs, dsp[rd] */ +static bool trans_BSET_rm(DisasContext *ctx, arg_BSET_rm *a) +{ + BOP_RM(set); +} + +/* bset rs, rd */ +static bool trans_BSET_rr(DisasContext *ctx, arg_BSET_rr *a) +{ + prt("bset\tr%d,r%d", a->rs, a->rd); + return true; +} + +/* bset #imm, rd */ +static bool trans_BSET_ir(DisasContext *ctx, arg_BSET_ir *a) +{ + prt("bset\t#%d, r%d", a->imm, a->rd); + return true; +} + +/* bclr #imm, dsp[rd] */ +static bool trans_BCLR_im(DisasContext *ctx, arg_BCLR_im *a) +{ + BOP_IM(clr, a->rs); +} + +/* bclr rs, dsp[rd] */ +static bool trans_BCLR_rm(DisasContext *ctx, arg_BCLR_rm *a) +{ + BOP_RM(clr); +} + +/* bclr rs, rd */ +static bool trans_BCLR_rr(DisasContext *ctx, arg_BCLR_rr *a) +{ + prt("bclr\tr%d, r%d", a->rs, a->rd); + return true; +} + +/* bclr #imm, rd */ +static bool trans_BCLR_ir(DisasContext *ctx, arg_BCLR_ir *a) +{ + prt("bclr\t#%d,r%d", a->imm, a->rd); + return true; +} + +/* btst #imm, dsp[rd] */ +static bool trans_BTST_im(DisasContext *ctx, arg_BTST_im *a) +{ + BOP_IM(tst, a->rs); +} + +/* btst rs, dsp[rd] */ +static bool trans_BTST_rm(DisasContext *ctx, arg_BTST_rm *a) +{ + BOP_RM(tst); +} + +/* btst rs, rd */ +static bool trans_BTST_rr(DisasContext *ctx, arg_BTST_rr *a) +{ + prt("btst\tr%d, r%d", a->rs, a->rd); + return true; +} + +/* btst #imm, rd */ +static bool trans_BTST_ir(DisasContext *ctx, arg_BTST_ir *a) +{ + prt("btst\t#%d, r%d", a->imm, a->rd); + return true; +} + +/* bnot rs, dsp[rd] */ +static bool trans_BNOT_rm(DisasContext *ctx, arg_BNOT_rm *a) +{ + BOP_RM(not); +} + +/* bnot rs, rd */ +static bool trans_BNOT_rr(DisasContext *ctx, arg_BNOT_rr *a) +{ + prt("bnot\tr%d, r%d", a->rs, a->rd); + return true; +} + +/* bnot #imm, dsp[rd] */ +static bool trans_BNOT_im(DisasContext *ctx, arg_BNOT_im *a) +{ + BOP_IM(not, a->rs); +} + +/* bnot #imm, rd */ +static bool trans_BNOT_ir(DisasContext *ctx, arg_BNOT_ir *a) +{ + prt("bnot\t#%d, r%d", a->imm, a->rd); + return true; +} + +/* bmcond #imm, dsp[rd] */ +static bool trans_BMCnd_im(DisasContext *ctx, arg_BMCnd_im *a) +{ + char dsp[8]; + + rx_index_addr(ctx, dsp, a->ld, RX_MEMORY_BYTE); + prt("bm%s\t#%d, %s[r%d]", cond[a->cd], a->imm, dsp, a->rd); + return true; +} + +/* bmcond #imm, rd */ +static bool trans_BMCnd_ir(DisasContext *ctx, arg_BMCnd_ir *a) +{ + prt("bm%s\t#%d, r%d", cond[a->cd], a->imm, a->rd); + return true; +} + +/* clrpsw psw */ +static bool trans_CLRPSW(DisasContext *ctx, arg_CLRPSW *a) +{ + prt("clrpsw\t%c", psw[a->cb]); + return true; +} + +/* setpsw psw */ +static bool trans_SETPSW(DisasContext *ctx, arg_SETPSW *a) +{ + prt("setpsw\t%c", psw[a->cb]); + return true; +} + +/* mvtipl #imm */ +static bool trans_MVTIPL(DisasContext *ctx, arg_MVTIPL *a) +{ + prt("movtipl\t#%d", a->imm); + return true; +} + +/* mvtc #imm, rd */ +static bool trans_MVTC_i(DisasContext *ctx, arg_MVTC_i *a) +{ + prt("mvtc\t#0x%08x, %s", a->imm, rx_crname(a->cr)); + return true; +} + +/* mvtc rs, rd */ +static bool trans_MVTC_r(DisasContext *ctx, arg_MVTC_r *a) +{ + prt("mvtc\tr%d, %s", a->rs, rx_crname(a->cr)); + return true; +} + +/* mvfc rs, rd */ +static bool trans_MVFC(DisasContext *ctx, arg_MVFC *a) +{ + prt("mvfc\t%s, r%d", rx_crname(a->cr), a->rd); + return true; +} + +/* rtfi */ +static bool trans_RTFI(DisasContext *ctx, arg_RTFI *a) +{ + prt("rtfi"); + return true; +} + +/* rte */ +static bool trans_RTE(DisasContext *ctx, arg_RTE *a) +{ + prt("rte"); + return true; +} + +/* brk */ +static bool trans_BRK(DisasContext *ctx, arg_BRK *a) +{ + prt("brk"); + return true; +} + +/* int #imm */ +static bool trans_INT(DisasContext *ctx, arg_INT *a) +{ + prt("int\t#%d", a->imm); + return true; +} + +/* wait */ +static bool trans_WAIT(DisasContext *ctx, arg_WAIT *a) +{ + prt("wait"); + return true; +} + +/* sccnd.[bwl] rd */ +/* sccnd.[bwl] dsp:[rd] */ +static bool trans_SCCnd(DisasContext *ctx, arg_SCCnd *a) +{ + if (a->ld < 3) { + char dsp[8]; + rx_index_addr(ctx, dsp, a->sz, a->ld); + prt("sc%s.%c\t%s[r%d]", cond[a->cd], size[a->sz], dsp, a->rd); + } else { + prt("sc%s.%c\tr%d", cond[a->cd], size[a->sz], a->rd); + } + return true; +} + +int print_insn_rx(bfd_vma addr, disassemble_info *dis) +{ + DisasContext ctx; + uint32_t insn; + int i; + + ctx.dis = dis; + ctx.pc = ctx.addr = addr; + ctx.len = 0; + + insn = decode_load(&ctx); + if (!decode(&ctx, insn)) { + ctx.dis->fprintf_func(ctx.dis->stream, ".byte\t"); + for (i = 0; i < ctx.addr - addr; i++) { + if (i > 0) { + ctx.dis->fprintf_func(ctx.dis->stream, ","); + } + ctx.dis->fprintf_func(ctx.dis->stream, "0x%02x", insn >> 24); + insn <<= 8; + } + } + return ctx.addr - addr; +} diff --git a/target/rx/gdbstub.c b/target/rx/gdbstub.c new file mode 100644 index 0000000000..9391e8151e --- /dev/null +++ b/target/rx/gdbstub.c @@ -0,0 +1,112 @@ +/* + * RX gdb server stub + * + * Copyright (c) 2019 Yoshinori Sato + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "cpu.h" +#include "exec/gdbstub.h" + +int rx_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) +{ + RXCPU *cpu = RXCPU(cs); + CPURXState *env = &cpu->env; + + switch (n) { + case 0 ... 15: + return gdb_get_regl(mem_buf, env->regs[n]); + case 16: + return gdb_get_regl(mem_buf, (env->psw_u) ? env->regs[0] : env->usp); + case 17: + return gdb_get_regl(mem_buf, (!env->psw_u) ? env->regs[0] : env->isp); + case 18: + return gdb_get_regl(mem_buf, rx_cpu_pack_psw(env)); + case 19: + return gdb_get_regl(mem_buf, env->pc); + case 20: + return gdb_get_regl(mem_buf, env->intb); + case 21: + return gdb_get_regl(mem_buf, env->bpsw); + case 22: + return gdb_get_regl(mem_buf, env->bpc); + case 23: + return gdb_get_regl(mem_buf, env->fintv); + case 24: + return gdb_get_regl(mem_buf, env->fpsw); + case 25: + return 0; + } + return 0; +} + +int rx_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) +{ + RXCPU *cpu = RXCPU(cs); + CPURXState *env = &cpu->env; + uint32_t psw; + switch (n) { + case 0 ... 15: + env->regs[n] = ldl_p(mem_buf); + if (n == 0) { + if (env->psw_u) { + env->usp = env->regs[0]; + } else { + env->isp = env->regs[0]; + } + } + break; + case 16: + env->usp = ldl_p(mem_buf); + if (env->psw_u) { + env->regs[0] = ldl_p(mem_buf); + } + break; + case 17: + env->isp = ldl_p(mem_buf); + if (!env->psw_u) { + env->regs[0] = ldl_p(mem_buf); + } + break; + case 18: + psw = ldl_p(mem_buf); + rx_cpu_unpack_psw(env, psw, 1); + break; + case 19: + env->pc = ldl_p(mem_buf); + break; + case 20: + env->intb = ldl_p(mem_buf); + break; + case 21: + env->bpsw = ldl_p(mem_buf); + break; + case 22: + env->bpc = ldl_p(mem_buf); + break; + case 23: + env->fintv = ldl_p(mem_buf); + break; + case 24: + env->fpsw = ldl_p(mem_buf); + break; + case 25: + return 8; + default: + return 0; + } + + return 4; +} diff --git a/target/rx/helper.c b/target/rx/helper.c new file mode 100644 index 0000000000..a6a337a311 --- /dev/null +++ b/target/rx/helper.c @@ -0,0 +1,149 @@ +/* + * RX emulation + * + * Copyright (c) 2019 Yoshinori Sato + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include "qemu/osdep.h" +#include "qemu/bitops.h" +#include "cpu.h" +#include "exec/log.h" +#include "exec/cpu_ldst.h" +#include "sysemu/sysemu.h" +#include "hw/irq.h" + +void rx_cpu_unpack_psw(CPURXState *env, uint32_t psw, int rte) +{ + if (env->psw_pm == 0) { + env->psw_ipl = FIELD_EX32(psw, PSW, IPL); + if (rte) { + /* PSW.PM can write RTE and RTFI */ + env->psw_pm = FIELD_EX32(psw, PSW, PM); + } + env->psw_u = FIELD_EX32(psw, PSW, U); + env->psw_i = FIELD_EX32(psw, PSW, I); + } + env->psw_o = FIELD_EX32(psw, PSW, O) << 31; + env->psw_s = FIELD_EX32(psw, PSW, S) << 31; + env->psw_z = 1 - FIELD_EX32(psw, PSW, Z); + env->psw_c = FIELD_EX32(psw, PSW, C); +} + +#define INT_FLAGS (CPU_INTERRUPT_HARD | CPU_INTERRUPT_FIR) +void rx_cpu_do_interrupt(CPUState *cs) +{ + RXCPU *cpu = RXCPU(cs); + CPURXState *env = &cpu->env; + int do_irq = cs->interrupt_request & INT_FLAGS; + uint32_t save_psw; + + env->in_sleep = 0; + + if (env->psw_u) { + env->usp = env->regs[0]; + } else { + env->isp = env->regs[0]; + } + save_psw = rx_cpu_pack_psw(env); + env->psw_pm = env->psw_i = env->psw_u = 0; + + if (do_irq) { + if (do_irq & CPU_INTERRUPT_FIR) { + env->bpc = env->pc; + env->bpsw = save_psw; + env->pc = env->fintv; + env->psw_ipl = 15; + cs->interrupt_request &= ~CPU_INTERRUPT_FIR; + qemu_set_irq(env->ack, env->ack_irq); + qemu_log_mask(CPU_LOG_INT, "fast interrupt raised\n"); + } else if (do_irq & CPU_INTERRUPT_HARD) { + env->isp -= 4; + cpu_stl_data(env, env->isp, save_psw); + env->isp -= 4; + cpu_stl_data(env, env->isp, env->pc); + env->pc = cpu_ldl_data(env, env->intb + env->ack_irq * 4); + env->psw_ipl = env->ack_ipl; + cs->interrupt_request &= ~CPU_INTERRUPT_HARD; + qemu_set_irq(env->ack, env->ack_irq); + qemu_log_mask(CPU_LOG_INT, + "interrupt 0x%02x raised\n", env->ack_irq); + } + } else { + uint32_t vec = cs->exception_index; + const char *expname = "unknown exception"; + + env->isp -= 4; + cpu_stl_data(env, env->isp, save_psw); + env->isp -= 4; + cpu_stl_data(env, env->isp, env->pc); + + if (vec < 0x100) { + env->pc = cpu_ldl_data(env, 0xffffffc0 + vec * 4); + } else { + env->pc = cpu_ldl_data(env, env->intb + (vec & 0xff) * 4); + } + switch (vec) { + case 20: + expname = "privilege violation"; + break; + case 21: + expname = "access exception"; + break; + case 23: + expname = "illegal instruction"; + break; + case 25: + expname = "fpu exception"; + break; + case 30: + expname = "non-maskable interrupt"; + break; + case 0x100 ... 0x1ff: + expname = "unconditional trap"; + } + qemu_log_mask(CPU_LOG_INT, "exception 0x%02x [%s] raised\n", + (vec & 0xff), expname); + } + env->regs[0] = env->isp; +} + +bool rx_cpu_exec_interrupt(CPUState *cs, int interrupt_request) +{ + RXCPU *cpu = RXCPU(cs); + CPURXState *env = &cpu->env; + int accept = 0; + /* hardware interrupt (Normal) */ + if ((interrupt_request & CPU_INTERRUPT_HARD) && + env->psw_i && (env->psw_ipl < env->req_ipl)) { + env->ack_irq = env->req_irq; + env->ack_ipl = env->req_ipl; + accept = 1; + } + /* hardware interrupt (FIR) */ + if ((interrupt_request & CPU_INTERRUPT_FIR) && + env->psw_i && (env->psw_ipl < 15)) { + accept = 1; + } + if (accept) { + rx_cpu_do_interrupt(cs); + return true; + } + return false; +} + +hwaddr rx_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) +{ + return addr; +} diff --git a/target/rx/helper.h b/target/rx/helper.h new file mode 100644 index 0000000000..f0b7ebbbf7 --- /dev/null +++ b/target/rx/helper.h @@ -0,0 +1,31 @@ +DEF_HELPER_1(raise_illegal_instruction, noreturn, env) +DEF_HELPER_1(raise_access_fault, noreturn, env) +DEF_HELPER_1(raise_privilege_violation, noreturn, env) +DEF_HELPER_1(wait, noreturn, env) +DEF_HELPER_1(debug, noreturn, env) +DEF_HELPER_2(rxint, noreturn, env, i32) +DEF_HELPER_1(rxbrk, noreturn, env) +DEF_HELPER_FLAGS_3(fadd, TCG_CALL_NO_WG, f32, env, f32, f32) +DEF_HELPER_FLAGS_3(fsub, TCG_CALL_NO_WG, f32, env, f32, f32) +DEF_HELPER_FLAGS_3(fmul, TCG_CALL_NO_WG, f32, env, f32, f32) +DEF_HELPER_FLAGS_3(fdiv, TCG_CALL_NO_WG, f32, env, f32, f32) +DEF_HELPER_FLAGS_3(fcmp, TCG_CALL_NO_WG, void, env, f32, f32) +DEF_HELPER_FLAGS_2(ftoi, TCG_CALL_NO_WG, i32, env, f32) +DEF_HELPER_FLAGS_2(round, TCG_CALL_NO_WG, i32, env, f32) +DEF_HELPER_FLAGS_2(itof, TCG_CALL_NO_WG, f32, env, i32) +DEF_HELPER_2(set_fpsw, void, env, i32) +DEF_HELPER_FLAGS_2(racw, TCG_CALL_NO_WG, void, env, i32) +DEF_HELPER_FLAGS_2(set_psw_rte, TCG_CALL_NO_WG, void, env, i32) +DEF_HELPER_FLAGS_2(set_psw, TCG_CALL_NO_WG, void, env, i32) +DEF_HELPER_1(pack_psw, i32, env) +DEF_HELPER_FLAGS_3(div, TCG_CALL_NO_WG, i32, env, i32, i32) +DEF_HELPER_FLAGS_3(divu, TCG_CALL_NO_WG, i32, env, i32, i32) +DEF_HELPER_FLAGS_1(scmpu, TCG_CALL_NO_WG, void, env) +DEF_HELPER_1(smovu, void, env) +DEF_HELPER_1(smovf, void, env) +DEF_HELPER_1(smovb, void, env) +DEF_HELPER_2(sstr, void, env, i32) +DEF_HELPER_FLAGS_2(swhile, TCG_CALL_NO_WG, void, env, i32) +DEF_HELPER_FLAGS_2(suntil, TCG_CALL_NO_WG, void, env, i32) +DEF_HELPER_FLAGS_2(rmpa, TCG_CALL_NO_WG, void, env, i32) +DEF_HELPER_1(satr, void, env) diff --git a/target/rx/insns.decode b/target/rx/insns.decode new file mode 100644 index 0000000000..232a61fc8e --- /dev/null +++ b/target/rx/insns.decode @@ -0,0 +1,621 @@ +# +# Renesas RX instruction decode definitions. +# +# Copyright (c) 2019 Richard Henderson +# Copyright (c) 2019 Yoshinori Sato +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, see . +# + +&bcnd cd dsp sz +&jdsp dsp sz +&jreg rs +&rr rd rs +&ri rd imm +&rrr rd rs rs2 +&rri rd imm rs2 +&rm rd rs ld mi +&mi rs ld mi imm +&mr rs ld mi rs2 +&mcnd ld sz rd cd +######## +%b1_bdsp 24:3 !function=bdsp_s + +@b1_bcnd_s .... cd:1 ... &bcnd dsp=%b1_bdsp sz=1 +@b1_bra_s .... .... &jdsp dsp=%b1_bdsp sz=1 + +%b2_r_0 16:4 +%b2_li_2 18:2 !function=li +%b2_li_8 24:2 !function=li +%b2_dsp5_3 23:4 19:1 + +@b2_rds .... .... .... rd:4 &rr rs=%b2_r_0 +@b2_rds_li .... .... .... rd:4 &rri rs2=%b2_r_0 imm=%b2_li_8 +@b2_rds_uimm4 .... .... imm:4 rd:4 &rri rs2=%b2_r_0 +@b2_rs2_uimm4 .... .... imm:4 rs2:4 &rri rd=0 +@b2_rds_imm5 .... ... imm:5 rd:4 &rri rs2=%b2_r_0 +@b2_rd_rs_li .... .... rs2:4 rd:4 &rri imm=%b2_li_8 +@b2_rd_ld_ub .... .. ld:2 rs:4 rd:4 &rm mi=4 +@b2_ld_imm3 .... .. ld:2 rs:4 . imm:3 &mi mi=4 +@b2_bcnd_b .... cd:4 dsp:s8 &bcnd sz=2 +@b2_bra_b .... .... dsp:s8 &jdsp sz=2 + +######## + +%b3_r_0 8:4 +%b3_li_10 18:2 !function=li +%b3_dsp5_8 23:1 16:4 +%b3_bdsp 8:s8 16:8 + +@b3_rd_rs .... .... .... .... rs:4 rd:4 &rr +@b3_rs_rd .... .... .... .... rd:4 rs:4 &rr +@b3_rd_li .... .... .... .... .... rd:4 \ + &rri rs2=%b3_r_0 imm=%b3_li_10 +@b3_rd_ld .... .... mi:2 .... ld:2 rs:4 rd:4 &rm +@b3_rd_ld_ub .... .... .... .. ld:2 rs:4 rd:4 &rm mi=4 +@b3_rd_ld_ul .... .... .... .. ld:2 rs:4 rd:4 &rm mi=2 +@b3_rd_rs_rs2 .... .... .... rd:4 rs:4 rs2:4 &rrr +@b3_rds_imm5 .... .... ....... imm:5 rd:4 &rri rs2=%b3_r_0 +@b3_rd_rs_imm5 .... .... ... imm:5 rs2:4 rd:4 &rri +@b3_bcnd_w .... ... cd:1 .... .... .... .... &bcnd dsp=%b3_bdsp sz=3 +@b3_bra_w .... .... .... .... .... .... &jdsp dsp=%b3_bdsp sz=3 +@b3_ld_rd_rs .... .... .... .. ld:2 rs:4 rd:4 &rm mi=0 +@b3_sz_ld_rd_cd .... .... .... sz:2 ld:2 rd:4 cd:4 &mcnd + +######## + +%b4_li_18 18:2 !function=li +%b4_dsp_16 0:s8 8:8 +%b4_bdsp 0:s8 8:8 16:8 + +@b4_rd_ldmi .... .... mi:2 .... ld:2 .... .... rs:4 rd:4 &rm +@b4_bra_a .... .... .... .... .... .... .... .... \ + &jdsp dsp=%b4_bdsp sz=4 +######## +# ABS rd +ABS_rr 0111 1110 0010 .... @b2_rds +# ABS rs, rd +ABS_rr 1111 1100 0000 1111 .... .... @b3_rd_rs + +# ADC #imm, rd +ADC_ir 1111 1101 0111 ..00 0010 .... @b3_rd_li +# ADC rs, rd +ADC_rr 1111 1100 0000 1011 .... .... @b3_rd_rs +# ADC dsp[rs].l, rd +# Note only mi==2 allowed. +ADC_mr 0000 0110 ..10 00.. 0000 0010 .... .... @b4_rd_ldmi + +# ADD #uimm4, rd +ADD_irr 0110 0010 .... .... @b2_rds_uimm4 +# ADD #imm, rs, rd +ADD_irr 0111 00.. .... .... @b2_rd_rs_li +# ADD dsp[rs].ub, rd +# ADD rs, rd +ADD_mr 0100 10.. .... .... @b2_rd_ld_ub +# ADD dsp[rs], rd +ADD_mr 0000 0110 ..00 10.. .... .... @b3_rd_ld +# ADD rs, rs2, rd +ADD_rrr 1111 1111 0010 .... .... .... @b3_rd_rs_rs2 + +# AND #uimm4, rd +AND_ir 0110 0100 .... .... @b2_rds_uimm4 +# AND #imm, rd +AND_ir 0111 01.. 0010 .... @b2_rds_li +# AND dsp[rs].ub, rd +# AND rs, rd +AND_mr 0101 00.. .... .... @b2_rd_ld_ub +# AND dsp[rs], rd +AND_mr 0000 0110 ..01 00.. .... .... @b3_rd_ld +# AND rs, rs2, rd +AND_rrr 1111 1111 0100 .... .... .... @b3_rd_rs_rs2 + +# BCLR #imm, dsp[rd] +BCLR_im 1111 00.. .... 1... @b2_ld_imm3 +# BCLR #imm, rs +BCLR_ir 0111 101. .... .... @b2_rds_imm5 +# BCLR rs, rd +# BCLR rs, dsp[rd] +{ + BCLR_rr 1111 1100 0110 0111 .... .... @b3_rs_rd + BCLR_rm 1111 1100 0110 01.. .... .... @b3_rd_ld_ub +} + +# BCnd.s dsp +BCnd 0001 .... @b1_bcnd_s +# BRA.b dsp +# BCnd.b dsp +{ + BRA 0010 1110 .... .... @b2_bra_b + BCnd 0010 .... .... .... @b2_bcnd_b +} + +# BCnd.w dsp +BCnd 0011 101 . .... .... .... .... @b3_bcnd_w + +# BNOT #imm, dsp[rd] +# BMCnd #imm, dsp[rd] +{ + BNOT_im 1111 1100 111 imm:3 ld:2 rs:4 1111 + BMCnd_im 1111 1100 111 imm:3 ld:2 rd:4 cd:4 +} + +# BNOT #imm, rd +# BMCnd #imm, rd +{ + BNOT_ir 1111 1101 111 imm:5 1111 rd:4 + BMCnd_ir 1111 1101 111 imm:5 cd:4 rd:4 +} + +# BNOT rs, rd +# BNOT rs, dsp[rd] +{ + BNOT_rr 1111 1100 0110 1111 .... .... @b3_rs_rd + BNOT_rm 1111 1100 0110 11.. .... .... @b3_rd_ld_ub +} + +# BRA.s dsp +BRA 0000 1 ... @b1_bra_s +# BRA.w dsp +BRA 0011 1000 .... .... .... .... @b3_bra_w +# BRA.a dsp +BRA 0000 0100 .... .... .... .... .... .... @b4_bra_a +# BRA.l rs +BRA_l 0111 1111 0100 rd:4 + +BRK 0000 0000 + +# BSET #imm, dsp[rd] +BSET_im 1111 00.. .... 0... @b2_ld_imm3 +# BSET #imm, rd +BSET_ir 0111 100. .... .... @b2_rds_imm5 +# BSET rs, rd +# BSET rs, dsp[rd] +{ + BSET_rr 1111 1100 0110 0011 .... .... @b3_rs_rd + BSET_rm 1111 1100 0110 00.. .... .... @b3_rd_ld_ub +} + +# BSR.w dsp +BSR 0011 1001 .... .... .... .... @b3_bra_w +# BSR.a dsp +BSR 0000 0101 .... .... .... .... .... .... @b4_bra_a +# BSR.l rs +BSR_l 0111 1111 0101 rd:4 + +# BSET #imm, dsp[rd] +BTST_im 1111 01.. .... 0... @b2_ld_imm3 +# BSET #imm, rd +BTST_ir 0111 110. .... .... @b2_rds_imm5 +# BSET rs, rd +# BSET rs, dsp[rd] +{ + BTST_rr 1111 1100 0110 1011 .... .... @b3_rs_rd + BTST_rm 1111 1100 0110 10.. .... .... @b3_rd_ld_ub +} + +# CLRSPW psw +CLRPSW 0111 1111 1011 cb:4 + +# CMP #uimm4, rs2 +CMP_ir 0110 0001 .... .... @b2_rs2_uimm4 +# CMP #uimm8, rs2 +CMP_ir 0111 0101 0101 rs2:4 imm:8 &rri rd=0 +# CMP #imm, rs2 +CMP_ir 0111 01.. 0000 rs2:4 &rri imm=%b2_li_8 rd=0 +# CMP dsp[rs].ub, rs2 +# CMP rs, rs2 +CMP_mr 0100 01.. .... .... @b2_rd_ld_ub +# CMP dsp[rs], rs2 +CMP_mr 0000 0110 ..00 01.. .... .... @b3_rd_ld + +# DIV #imm, rd +DIV_ir 1111 1101 0111 ..00 1000 .... @b3_rd_li +# DIV dsp[rs].ub, rd +# DIV rs, rd +DIV_mr 1111 1100 0010 00.. .... .... @b3_rd_ld_ub +# DIV dsp[rs], rd +DIV_mr 0000 0110 ..10 00.. 0000 1000 .... .... @b4_rd_ldmi + +# DIVU #imm, rd +DIVU_ir 1111 1101 0111 ..00 1001 .... @b3_rd_li +# DIVU dsp[rs].ub, rd +# DIVU rs, rd +DIVU_mr 1111 1100 0010 01.. .... .... @b3_rd_ld_ub +# DIVU dsp[rs], rd +DIVU_mr 0000 0110 ..10 00.. 0000 1001 .... .... @b4_rd_ldmi + +# EMUL #imm, rd +EMUL_ir 1111 1101 0111 ..00 0110 .... @b3_rd_li +# EMUL dsp[rs].ub, rd +# EMUL rs, rd +EMUL_mr 1111 1100 0001 10.. .... .... @b3_rd_ld_ub +# EMUL dsp[rs], rd +EMUL_mr 0000 0110 ..10 00.. 0000 0110 .... .... @b4_rd_ldmi + +# EMULU #imm, rd +EMULU_ir 1111 1101 0111 ..00 0111 .... @b3_rd_li +# EMULU dsp[rs].ub, rd +# EMULU rs, rd +EMULU_mr 1111 1100 0001 11.. .... .... @b3_rd_ld_ub +# EMULU dsp[rs], rd +EMULU_mr 0000 0110 ..10 00.. 0000 0111 .... .... @b4_rd_ldmi + +# FADD #imm, rd +FADD_ir 1111 1101 0111 0010 0010 rd:4 +# FADD rs, rd +# FADD dsp[rs], rd +FADD_mr 1111 1100 1000 10.. .... .... @b3_rd_ld_ul + +# FCMP #imm, rd +FCMP_ir 1111 1101 0111 0010 0001 rd:4 +# FCMP rs, rd +# FCMP dsp[rs], rd +FCMP_mr 1111 1100 1000 01.. .... .... @b3_rd_ld_ul + +# FDIV #imm, rd +FDIV_ir 1111 1101 0111 0010 0100 rd:4 +# FDIV rs, rd +# FDIV dsp[rs], rd +FDIV_mr 1111 1100 1001 00.. .... .... @b3_rd_ld_ul + +# FMUL #imm, rd +FMUL_ir 1111 1101 0111 0010 0011 rd:4 +# FMUL rs, rd +# FMUL dsp[rs], rd +FMUL_mr 1111 1100 1000 11.. .... .... @b3_rd_ld_ul + +# FSUB #imm, rd +FSUB_ir 1111 1101 0111 0010 0000 rd:4 +# FSUB rs, rd +# FSUB dsp[rs], rd +FSUB_mr 1111 1100 1000 00.. .... .... @b3_rd_ld_ul + +# FTOI rs, rd +# FTOI dsp[rs], rd +FTOI 1111 1100 1001 01.. .... .... @b3_rd_ld_ul + +# INT #uimm8 +INT 0111 0101 0110 0000 imm:8 + +# ITOF dsp[rs].ub, rd +# ITOF rs, rd +ITOF 1111 1100 0100 01.. .... .... @b3_rd_ld_ub +# ITOF dsp[rs], rd +ITOF 0000 0110 ..10 00.. 0001 0001 .... .... @b4_rd_ldmi + +# JMP rs +JMP 0111 1111 0000 rs:4 &jreg +# JSR rs +JSR 0111 1111 0001 rs:4 &jreg + +# MACHI rs, rs2 +MACHI 1111 1101 0000 0100 rs:4 rs2:4 +# MACLO rs, rs2 +MACLO 1111 1101 0000 0101 rs:4 rs2:4 + +# MAX #imm, rd +MAX_ir 1111 1101 0111 ..00 0100 .... @b3_rd_li +# MAX dsp[rs].ub, rd +# MAX rs, rd +MAX_mr 1111 1100 0001 00.. .... .... @b3_rd_ld_ub +# MAX dsp[rs], rd +MAX_mr 0000 0110 ..10 00.. 0000 0100 .... .... @b4_rd_ldmi + +# MIN #imm, rd +MIN_ir 1111 1101 0111 ..00 0101 .... @b3_rd_li +# MIN dsp[rs].ub, rd +# MIN rs, rd +MIN_mr 1111 1100 0001 01.. .... .... @b3_rd_ld_ub +# MIN dsp[rs], rd +MIN_mr 0000 0110 ..10 00.. 0000 0101 .... .... @b4_rd_ldmi + +# MOV.b rs, dsp5[rd] +MOV_rm 1000 0 .... rd:3 . rs:3 dsp=%b2_dsp5_3 sz=0 +# MOV.w rs, dsp5[rd] +MOV_rm 1001 0 .... rd:3 . rs:3 dsp=%b2_dsp5_3 sz=1 +# MOV.l rs, dsp5[rd] +MOV_rm 1010 0 .... rd:3 . rs:3 dsp=%b2_dsp5_3 sz=2 +# MOV.b dsp5[rs], rd +MOV_mr 1000 1 .... rs:3 . rd:3 dsp=%b2_dsp5_3 sz=0 +# MOV.w dsp5[rs], rd +MOV_mr 1001 1 .... rs:3 . rd:3 dsp=%b2_dsp5_3 sz=1 +# MOV.l dsp5[rs], rd +MOV_mr 1010 1 .... rs:3 . rd:3 dsp=%b2_dsp5_3 sz=2 +# MOV.l #uimm4, rd +MOV_ir 0110 0110 imm:4 rd:4 +# MOV.b #imm8, dsp5[rd] +MOV_im 0011 1100 . rd:3 .... imm:8 sz=0 dsp=%b3_dsp5_8 +# MOV.w #imm8, dsp5[rd] +MOV_im 0011 1101 . rd:3 .... imm:8 sz=1 dsp=%b3_dsp5_8 +# MOV.l #imm8, dsp5[rd] +MOV_im 0011 1110 . rd:3 .... imm:8 sz=2 dsp=%b3_dsp5_8 +# MOV.l #imm8, rd +MOV_ir 0111 0101 0100 rd:4 imm:8 +# MOV.l #mm8, rd +MOV_ir 1111 1011 rd:4 .. 10 imm=%b2_li_2 +# MOV. #imm, [rd] +MOV_im 1111 1000 rd:4 .. sz:2 dsp=0 imm=%b2_li_2 +# MOV. #imm, dsp8[rd] +MOV_im 1111 1001 rd:4 .. sz:2 dsp:8 imm=%b3_li_10 +# MOV. #imm, dsp16[rd] +MOV_im 1111 1010 rd:4 .. sz:2 .... .... .... .... \ + imm=%b4_li_18 dsp=%b4_dsp_16 +# MOV. [ri,rb], rd +MOV_ar 1111 1110 01 sz:2 ri:4 rb:4 rd:4 +# MOV. rs, [ri,rb] +MOV_ra 1111 1110 00 sz:2 ri:4 rb:4 rs:4 +# Note ldd=3 and lds=3 indicate register src or dst +# MOV.b rs, rd +# MOV.b rs, dsp[rd] +# MOV.b dsp[rs], rd +# MOV.b dsp[rs], dsp[rd] +MOV_mm 1100 ldd:2 lds:2 rs:4 rd:4 sz=0 +# MOV.w rs, rd +# MOV.w rs, dsp[rd] +# MOV.w dsp[rs], rd +# MOV.w dsp[rs], dsp[rd] +MOV_mm 1101 ldd:2 lds:2 rs:4 rd:4 sz=1 +# MOV.l rs, rd +# MOV.l rs, dsp[rd] +# MOV.l dsp[rs], rd +# MOV.l dsp[rs], dsp[rd] +MOV_mm 1110 ldd:2 lds:2 rs:4 rd:4 sz=2 +# MOV.l rs, [rd+] +# MOV.l rs, [-rd] +MOV_rp 1111 1101 0010 0 ad:1 sz:2 rd:4 rs:4 +# MOV.l [rs+], rd +# MOV.l [-rs], rd +MOV_pr 1111 1101 0010 1 ad:1 sz:2 rd:4 rs:4 + +# MOVU. dsp5[rs], rd +MOVU_mr 1011 sz:1 ... . rs:3 . rd:3 dsp=%b2_dsp5_3 +# MOVU. [rs], rd +MOVU_mr 0101 1 sz:1 00 rs:4 rd:4 dsp=0 +# MOVU. dsp8[rs], rd +MOVU_mr 0101 1 sz:1 01 rs:4 rd:4 dsp:8 +# MOVU. dsp16[rs], rd +MOVU_mr 0101 1 sz:1 10 rs:4 rd:4 .... .... .... .... dsp=%b4_dsp_16 +# MOVU. rs, rd +MOVU_rr 0101 1 sz:1 11 rs:4 rd:4 +# MOVU. [ri, rb], rd +MOVU_ar 1111 1110 110 sz:1 ri:4 rb:4 rd:4 +# MOVU. [rs+], rd +MOVU_pr 1111 1101 0011 1 ad:1 0 sz:1 rd:4 rs:4 + +# MUL #uimm4, rd +MUL_ir 0110 0011 .... .... @b2_rds_uimm4 +# MUL #imm4, rd +MUL_ir 0111 01.. 0001 .... @b2_rds_li +# MUL dsp[rs].ub, rd +# MUL rs, rd +MUL_mr 0100 11.. .... .... @b2_rd_ld_ub +# MUL dsp[rs], rd +MUL_mr 0000 0110 ..00 11.. .... .... @b3_rd_ld +# MOV rs, rs2, rd +MUL_rrr 1111 1111 0011 .... .... .... @b3_rd_rs_rs2 + +# MULHI rs, rs2 +MULHI 1111 1101 0000 0000 rs:4 rs2:4 +# MULLO rs, rs2 +MULLO 1111 1101 0000 0001 rs:4 rs2:4 + +# MVFACHI rd +MVFACHI 1111 1101 0001 1111 0000 rd:4 +# MVFACMI rd +MVFACMI 1111 1101 0001 1111 0010 rd:4 + +# MVFC cr, rd +MVFC 1111 1101 0110 1010 cr:4 rd:4 + +# MVTACHI rs +MVTACHI 1111 1101 0001 0111 0000 rs:4 +# MVTACLO rs +MVTACLO 1111 1101 0001 0111 0001 rs:4 + +# MVTC #imm, cr +MVTC_i 1111 1101 0111 ..11 0000 cr:4 imm=%b3_li_10 +# MVTC rs, cr +MVTC_r 1111 1101 0110 1000 rs:4 cr:4 + +# MVTIPL #imm +MVTIPL 0111 0101 0111 0000 0000 imm:4 + +# NEG rd +NEG_rr 0111 1110 0001 .... @b2_rds +# NEG rs, rd +NEG_rr 1111 1100 0000 0111 .... .... @b3_rd_rs + +NOP 0000 0011 + +# NOT rd +NOT_rr 0111 1110 0000 .... @b2_rds +# NOT rs, rd +NOT_rr 1111 1100 0011 1011 .... .... @b3_rd_rs + +# OR #uimm4, rd +OR_ir 0110 0101 .... .... @b2_rds_uimm4 +# OR #imm, rd +OR_ir 0111 01.. 0011 .... @b2_rds_li +# OR dsp[rs].ub, rd +# OR rs, rd +OR_mr 0101 01.. .... .... @b2_rd_ld_ub +# OR dsp[rs], rd +OR_mr 0000 0110 .. 0101 .. .... .... @b3_rd_ld +# OR rs, rs2, rd +OR_rrr 1111 1111 0101 .... .... .... @b3_rd_rs_rs2 + +# POP cr +POPC 0111 1110 1110 cr:4 +# POP rd-rd2 +POPM 0110 1111 rd:4 rd2:4 + +# POP rd +# PUSH. rs +{ + POP 0111 1110 1011 rd:4 + PUSH_r 0111 1110 10 sz:2 rs:4 +} +# PUSH. dsp[rs] +PUSH_m 1111 01 ld:2 rs:4 10 sz:2 +# PUSH cr +PUSHC 0111 1110 1100 cr:4 +# PUSHM rs-rs2 +PUSHM 0110 1110 rs:4 rs2:4 + +# RACW #imm +RACW 1111 1101 0001 1000 000 imm:1 0000 + +# REVL rs,rd +REVL 1111 1101 0110 0111 .... .... @b3_rd_rs +# REVW rs,rd +REVW 1111 1101 0110 0101 .... .... @b3_rd_rs + +# SMOVF +# RPMA. +{ + SMOVF 0111 1111 1000 1111 + RMPA 0111 1111 1000 11 sz:2 +} + +# ROLC rd +ROLC 0111 1110 0101 .... @b2_rds +# RORC rd +RORC 0111 1110 0100 .... @b2_rds + +# ROTL #imm, rd +ROTL_ir 1111 1101 0110 111. .... .... @b3_rds_imm5 +# ROTL rs, rd +ROTL_rr 1111 1101 0110 0110 .... .... @b3_rd_rs + +# ROTR #imm, rd +ROTR_ir 1111 1101 0110 110. .... .... @b3_rds_imm5 +# ROTR #imm, rd +ROTR_rr 1111 1101 0110 0100 .... .... @b3_rd_rs + +# ROUND rs,rd +# ROUND dsp[rs],rd +ROUND 1111 1100 1001 10 .. .... .... @b3_ld_rd_rs + +RTE 0111 1111 1001 0101 + +RTFI 0111 1111 1001 0100 + +RTS 0000 0010 + +# RTSD #imm +RTSD_i 0110 0111 imm:8 +# RTSD #imm, rd-rd2 +RTSD_irr 0011 1111 rd:4 rd2:4 imm:8 + +# SAT rd +SAT 0111 1110 0011 .... @b2_rds +# SATR +SATR 0111 1111 1001 0011 + +# SBB rs, rd +SBB_rr 1111 1100 0000 0011 .... .... @b3_rd_rs +# SBB dsp[rs].l, rd +# Note only mi==2 allowed. +SBB_mr 0000 0110 ..10 00.. 0000 0000 .... .... @b4_rd_ldmi + +# SCCnd dsp[rd] +# SCCnd rd +SCCnd 1111 1100 1101 .... .... .... @b3_sz_ld_rd_cd + +# SETPSW psw +SETPSW 0111 1111 1010 cb:4 + +# SHAR #imm, rd +SHAR_irr 0110 101. .... .... @b2_rds_imm5 +# SHAR #imm, rs, rd +SHAR_irr 1111 1101 101. .... .... .... @b3_rd_rs_imm5 +# SHAR rs, rd +SHAR_rr 1111 1101 0110 0001 .... .... @b3_rd_rs + +# SHLL #imm, rd +SHLL_irr 0110 110. .... .... @b2_rds_imm5 +# SHLL #imm, rs, rd +SHLL_irr 1111 1101 110. .... .... .... @b3_rd_rs_imm5 +# SHLL rs, rd +SHLL_rr 1111 1101 0110 0010 .... .... @b3_rd_rs + +# SHLR #imm, rd +SHLR_irr 0110 100. .... .... @b2_rds_imm5 +# SHLR #imm, rs, rd +SHLR_irr 1111 1101 100. .... .... .... @b3_rd_rs_imm5 +# SHLR rs, rd +SHLR_rr 1111 1101 0110 0000 .... .... @b3_rd_rs + +# SMOVB +# SSTR. +{ + SMOVB 0111 1111 1000 1011 + SSTR 0111 1111 1000 10 sz:2 +} + +# STNZ #imm, rd +STNZ 1111 1101 0111 ..00 1111 .... @b3_rd_li +# STZ #imm, rd +STZ 1111 1101 0111 ..00 1110 .... @b3_rd_li + +# SUB #uimm4, rd +SUB_ir 0110 0000 .... .... @b2_rds_uimm4 +# SUB dsp[rs].ub, rd +# SUB rs, rd +SUB_mr 0100 00.. .... .... @b2_rd_ld_ub +# SUB dsp[rs], rd +SUB_mr 0000 0110 ..00 00.. .... .... @b3_rd_ld +# SUB rs, rs2, rd +SUB_rrr 1111 1111 0000 .... .... .... @b3_rd_rs_rs2 + +# SCMPU +# SUNTIL. +{ + SCMPU 0111 1111 1000 0011 + SUNTIL 0111 1111 1000 00 sz:2 +} + +# SMOVU +# SWHILE. +{ + SMOVU 0111 1111 1000 0111 + SWHILE 0111 1111 1000 01 sz:2 +} + +# TST #imm, rd +TST_ir 1111 1101 0111 ..00 1100 .... @b3_rd_li +# TST dsp[rs].ub, rd +# TST rs, rd +TST_mr 1111 1100 0011 00.. .... .... @b3_rd_ld_ub +# TST dsp[rs], rd +TST_mr 0000 0110 ..10 00.. 0000 1100 .... .... @b4_rd_ldmi + +WAIT 0111 1111 1001 0110 + +# XCHG rs, rd +# XCHG dsp[rs].ub, rd +{ + XCHG_rr 1111 1100 0100 0011 .... .... @b3_rd_rs + XCHG_mr 1111 1100 0100 00.. .... .... @b3_rd_ld_ub +} +# XCHG dsp[rs], rd +XCHG_mr 0000 0110 ..10 00.. 0001 0000 .... .... @b4_rd_ldmi + +# XOR #imm, rd +XOR_ir 1111 1101 0111 ..00 1101 .... @b3_rd_li +# XOR dsp[rs].ub, rd +# XOR rs, rd +XOR_mr 1111 1100 0011 01.. .... .... @b3_rd_ld_ub +# XOR dsp[rs], rd +XOR_mr 0000 0110 ..10 00.. 0000 1101 .... .... @b4_rd_ldmi diff --git a/target/rx/op_helper.c b/target/rx/op_helper.c new file mode 100644 index 0000000000..f89d294f2b --- /dev/null +++ b/target/rx/op_helper.c @@ -0,0 +1,470 @@ +/* + * RX helper functions + * + * Copyright (c) 2019 Yoshinori Sato + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include "qemu/osdep.h" +#include "qemu/bitops.h" +#include "cpu.h" +#include "exec/exec-all.h" +#include "exec/helper-proto.h" +#include "exec/cpu_ldst.h" +#include "fpu/softfloat.h" + +static inline void QEMU_NORETURN raise_exception(CPURXState *env, int index, + uintptr_t retaddr); + +static void _set_psw(CPURXState *env, uint32_t psw, uint32_t rte) +{ + uint32_t prev_u; + prev_u = env->psw_u; + rx_cpu_unpack_psw(env, psw, rte); + if (prev_u != env->psw_u) { + /* switch r0 */ + if (env->psw_u) { + env->isp = env->regs[0]; + env->regs[0] = env->usp; + } else { + env->usp = env->regs[0]; + env->regs[0] = env->isp; + } + } +} + +void helper_set_psw(CPURXState *env, uint32_t psw) +{ + _set_psw(env, psw, 0); +} + +void helper_set_psw_rte(CPURXState *env, uint32_t psw) +{ + _set_psw(env, psw, 1); +} + +uint32_t helper_pack_psw(CPURXState *env) +{ + return rx_cpu_pack_psw(env); +} + +#define SET_FPSW(b) \ + do { \ + env->fpsw = FIELD_DP32(env->fpsw, FPSW, C ## b, 1); \ + if (!FIELD_EX32(env->fpsw, FPSW, E ## b)) { \ + env->fpsw = FIELD_DP32(env->fpsw, FPSW, F ## b, 1); \ + } \ + } while (0) + +/* fp operations */ +static void update_fpsw(CPURXState *env, float32 ret, uintptr_t retaddr) +{ + int xcpt, cause, enable; + + env->psw_z = ret & ~(1 << 31); /* mask sign bit */ + env->psw_s = ret; + + xcpt = get_float_exception_flags(&env->fp_status); + + /* Clear the cause entries */ + env->fpsw = FIELD_DP32(env->fpsw, FPSW, CAUSE, 0); + + /* set FPSW */ + if (unlikely(xcpt)) { + if (xcpt & float_flag_invalid) { + SET_FPSW(V); + } + if (xcpt & float_flag_divbyzero) { + SET_FPSW(Z); + } + if (xcpt & float_flag_overflow) { + SET_FPSW(O); + } + if (xcpt & float_flag_underflow) { + SET_FPSW(U); + } + if (xcpt & float_flag_inexact) { + SET_FPSW(X); + } + if ((xcpt & (float_flag_input_denormal + | float_flag_output_denormal)) + && !FIELD_EX32(env->fpsw, FPSW, DN)) { + env->fpsw = FIELD_DP32(env->fpsw, FPSW, CE, 1); + } + + /* update FPSW_FLAG_S */ + if (FIELD_EX32(env->fpsw, FPSW, FLAGS) != 0) { + env->fpsw = FIELD_DP32(env->fpsw, FPSW, FS, 1); + } + + /* Generate an exception if enabled */ + cause = FIELD_EX32(env->fpsw, FPSW, CAUSE); + enable = FIELD_EX32(env->fpsw, FPSW, ENABLE); + enable |= 1 << 5; /* CE always enabled */ + if (cause & enable) { + raise_exception(env, 21, retaddr); + } + } +} + +void helper_set_fpsw(CPURXState *env, uint32_t val) +{ + static const int roundmode[] = { + float_round_nearest_even, + float_round_to_zero, + float_round_up, + float_round_down, + }; + uint32_t fpsw = env->fpsw; + fpsw |= 0x7fffff03; + val &= ~0x80000000; + fpsw &= val; + FIELD_DP32(fpsw, FPSW, FS, FIELD_EX32(fpsw, FPSW, FLAGS) != 0); + env->fpsw = fpsw; + set_float_rounding_mode(roundmode[FIELD_EX32(env->fpsw, FPSW, RM)], + &env->fp_status); +} + +#define FLOATOP(op, func) \ + float32 helper_##op(CPURXState *env, float32 t0, float32 t1) \ + { \ + float32 ret; \ + ret = func(t0, t1, &env->fp_status); \ + update_fpsw(env, *(uint32_t *)&ret, GETPC()); \ + return ret; \ + } + +FLOATOP(fadd, float32_add) +FLOATOP(fsub, float32_sub) +FLOATOP(fmul, float32_mul) +FLOATOP(fdiv, float32_div) + +void helper_fcmp(CPURXState *env, float32 t0, float32 t1) +{ + int st; + st = float32_compare(t0, t1, &env->fp_status); + update_fpsw(env, 0, GETPC()); + env->psw_z = 1; + env->psw_s = env->psw_o = 0; + switch (st) { + case float_relation_equal: + env->psw_z = 0; + break; + case float_relation_less: + env->psw_s = -1; + break; + case float_relation_unordered: + env->psw_o = -1; + break; + } +} + +uint32_t helper_ftoi(CPURXState *env, float32 t0) +{ + uint32_t ret; + ret = float32_to_int32_round_to_zero(t0, &env->fp_status); + update_fpsw(env, ret, GETPC()); + return ret; +} + +uint32_t helper_round(CPURXState *env, float32 t0) +{ + uint32_t ret; + ret = float32_to_int32(t0, &env->fp_status); + update_fpsw(env, ret, GETPC()); + return ret; +} + +float32 helper_itof(CPURXState *env, uint32_t t0) +{ + float32 ret; + ret = int32_to_float32(t0, &env->fp_status); + update_fpsw(env, ret, GETPC()); + return ret; +} + +/* string operations */ +void helper_scmpu(CPURXState *env) +{ + uint8_t tmp0, tmp1; + if (env->regs[3] == 0) { + return; + } + while (env->regs[3] != 0) { + tmp0 = cpu_ldub_data_ra(env, env->regs[1]++, GETPC()); + tmp1 = cpu_ldub_data_ra(env, env->regs[2]++, GETPC()); + env->regs[3]--; + if (tmp0 != tmp1 || tmp0 == '\0') { + break; + } + } + env->psw_z = tmp0 - tmp1; + env->psw_c = (tmp0 >= tmp1); +} + +static uint32_t (* const cpu_ldufn[])(CPUArchState *env, + target_ulong ptr, + uintptr_t retaddr) = { + cpu_ldub_data_ra, cpu_lduw_data_ra, cpu_ldl_data_ra, +}; + +static uint32_t (* const cpu_ldfn[])(CPUArchState *env, + target_ulong ptr, + uintptr_t retaddr) = { + cpu_ldub_data_ra, cpu_lduw_data_ra, cpu_ldl_data_ra, +}; + +static void (* const cpu_stfn[])(CPUArchState *env, + target_ulong ptr, + uint32_t val, + uintptr_t retaddr) = { + cpu_stb_data_ra, cpu_stw_data_ra, cpu_stl_data_ra, +}; + +void helper_sstr(CPURXState *env, uint32_t sz) +{ + tcg_debug_assert(sz < 3); + while (env->regs[3] != 0) { + cpu_stfn[sz](env, env->regs[1], env->regs[2], GETPC()); + env->regs[1] += 1 << sz; + env->regs[3]--; + } +} + +#define OP_SMOVU 1 +#define OP_SMOVF 0 +#define OP_SMOVB 2 + +static void smov(uint32_t mode, CPURXState *env) +{ + uint8_t tmp; + int dir; + + dir = (mode & OP_SMOVB) ? -1 : 1; + while (env->regs[3] != 0) { + tmp = cpu_ldub_data_ra(env, env->regs[2], GETPC()); + cpu_stb_data_ra(env, env->regs[1], tmp, GETPC()); + env->regs[1] += dir; + env->regs[2] += dir; + env->regs[3]--; + if ((mode & OP_SMOVU) && tmp == 0) { + break; + } + } +} + +void helper_smovu(CPURXState *env) +{ + smov(OP_SMOVU, env); +} + +void helper_smovf(CPURXState *env) +{ + smov(OP_SMOVF, env); +} + +void helper_smovb(CPURXState *env) +{ + smov(OP_SMOVB, env); +} + + +void helper_suntil(CPURXState *env, uint32_t sz) +{ + uint32_t tmp; + tcg_debug_assert(sz < 3); + if (env->regs[3] == 0) { + return ; + } + while (env->regs[3] != 0) { + tmp = cpu_ldufn[sz](env, env->regs[1], GETPC()); + env->regs[1] += 1 << sz; + env->regs[3]--; + if (tmp == env->regs[2]) { + break; + } + } + env->psw_z = tmp - env->regs[2]; + env->psw_c = (tmp <= env->regs[2]); +} + +void helper_swhile(CPURXState *env, uint32_t sz) +{ + uint32_t tmp; + tcg_debug_assert(sz < 3); + if (env->regs[3] == 0) { + return ; + } + while (env->regs[3] != 0) { + tmp = cpu_ldufn[sz](env, env->regs[1], GETPC()); + env->regs[1] += 1 << sz; + env->regs[3]--; + if (tmp != env->regs[2]) { + break; + } + } + env->psw_z = env->regs[3]; + env->psw_c = (tmp <= env->regs[2]); +} + +/* accumlator operations */ +void helper_rmpa(CPURXState *env, uint32_t sz) +{ + uint64_t result_l, prev; + int32_t result_h; + int64_t tmp0, tmp1; + + if (env->regs[3] == 0) { + return; + } + result_l = env->regs[5]; + result_l <<= 32; + result_l |= env->regs[4]; + result_h = env->regs[6]; + env->psw_o = 0; + + while (env->regs[3] != 0) { + tmp0 = cpu_ldfn[sz](env, env->regs[1], GETPC()); + tmp1 = cpu_ldfn[sz](env, env->regs[2], GETPC()); + tmp0 *= tmp1; + prev = result_l; + result_l += tmp0; + /* carry / bollow */ + if (tmp0 < 0) { + if (prev > result_l) { + result_h--; + } + } else { + if (prev < result_l) { + result_h++; + } + } + + env->regs[1] += 1 << sz; + env->regs[2] += 1 << sz; + } + env->psw_s = result_h; + env->psw_o = (result_h != 0 && result_h != -1) << 31; + env->regs[6] = result_h; + env->regs[5] = result_l >> 32; + env->regs[4] = result_l & 0xffffffff; +} + +void helper_racw(CPURXState *env, uint32_t imm) +{ + int64_t acc; + acc = env->acc; + acc <<= (imm + 1); + acc += 0x0000000080000000LL; + if (acc > 0x00007fff00000000LL) { + acc = 0x00007fff00000000LL; + } else if (acc < -0x800000000000LL) { + acc = -0x800000000000LL; + } else { + acc &= 0xffffffff00000000LL; + } + env->acc = acc; +} + +void helper_satr(CPURXState *env) +{ + if (env->psw_o >> 31) { + if ((int)env->psw_s < 0) { + env->regs[6] = 0x00000000; + env->regs[5] = 0x7fffffff; + env->regs[4] = 0xffffffff; + } else { + env->regs[6] = 0xffffffff; + env->regs[5] = 0x80000000; + env->regs[4] = 0x00000000; + } + } +} + +/* div */ +uint32_t helper_div(CPURXState *env, uint32_t num, uint32_t den) +{ + uint32_t ret = num; + if (!((num == INT_MIN && den == -1) || den == 0)) { + ret = (int32_t)num / (int32_t)den; + env->psw_o = 0; + } else { + env->psw_o = -1; + } + return ret; +} + +uint32_t helper_divu(CPURXState *env, uint32_t num, uint32_t den) +{ + uint32_t ret = num; + if (den != 0) { + ret = num / den; + env->psw_o = 0; + } else { + env->psw_o = -1; + } + return ret; +} + +/* exception */ +static inline void QEMU_NORETURN raise_exception(CPURXState *env, int index, + uintptr_t retaddr) +{ + CPUState *cs = env_cpu(env); + + cs->exception_index = index; + cpu_loop_exit_restore(cs, retaddr); +} + +void QEMU_NORETURN helper_raise_privilege_violation(CPURXState *env) +{ + raise_exception(env, 20, GETPC()); +} + +void QEMU_NORETURN helper_raise_access_fault(CPURXState *env) +{ + raise_exception(env, 21, GETPC()); +} + +void QEMU_NORETURN helper_raise_illegal_instruction(CPURXState *env) +{ + raise_exception(env, 23, GETPC()); +} + +void QEMU_NORETURN helper_wait(CPURXState *env) +{ + CPUState *cs = env_cpu(env); + + cs->halted = 1; + env->in_sleep = 1; + raise_exception(env, EXCP_HLT, 0); +} + +void QEMU_NORETURN helper_debug(CPURXState *env) +{ + CPUState *cs = env_cpu(env); + + cs->exception_index = EXCP_DEBUG; + cpu_loop_exit(cs); +} + +void QEMU_NORETURN helper_rxint(CPURXState *env, uint32_t vec) +{ + raise_exception(env, 0x100 + vec, 0); +} + +void QEMU_NORETURN helper_rxbrk(CPURXState *env) +{ + raise_exception(env, 0x100, 0); +} diff --git a/target/rx/translate.c b/target/rx/translate.c new file mode 100644 index 0000000000..61e86653a4 --- /dev/null +++ b/target/rx/translate.c @@ -0,0 +1,2440 @@ +/* + * RX translation + * + * Copyright (c) 2019 Yoshinori Sato + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include "qemu/osdep.h" +#include "qemu/bswap.h" +#include "qemu/qemu-print.h" +#include "cpu.h" +#include "exec/exec-all.h" +#include "tcg/tcg-op.h" +#include "exec/cpu_ldst.h" +#include "exec/helper-proto.h" +#include "exec/helper-gen.h" +#include "exec/translator.h" +#include "trace-tcg.h" +#include "exec/log.h" + +typedef struct DisasContext { + DisasContextBase base; + CPURXState *env; + uint32_t pc; +} DisasContext; + +typedef struct DisasCompare { + TCGv value; + TCGv temp; + TCGCond cond; +} DisasCompare; + +const char *rx_crname(uint8_t cr) +{ + static const char *cr_names[] = { + "psw", "pc", "usp", "fpsw", "", "", "", "", + "bpsw", "bpc", "isp", "fintv", "intb", "", "", "" + }; + if (cr >= ARRAY_SIZE(cr_names)) { + return "illegal"; + } + return cr_names[cr]; +} + +/* Target-specific values for dc->base.is_jmp. */ +#define DISAS_JUMP DISAS_TARGET_0 +#define DISAS_UPDATE DISAS_TARGET_1 +#define DISAS_EXIT DISAS_TARGET_2 + +/* global register indexes */ +static TCGv cpu_regs[16]; +static TCGv cpu_psw_o, cpu_psw_s, cpu_psw_z, cpu_psw_c; +static TCGv cpu_psw_i, cpu_psw_pm, cpu_psw_u, cpu_psw_ipl; +static TCGv cpu_usp, cpu_fpsw, cpu_bpsw, cpu_bpc, cpu_isp; +static TCGv cpu_fintv, cpu_intb, cpu_pc; +static TCGv_i64 cpu_acc; + +#define cpu_sp cpu_regs[0] + +#include "exec/gen-icount.h" + +/* decoder helper */ +static uint32_t decode_load_bytes(DisasContext *ctx, uint32_t insn, + int i, int n) +{ + while (++i <= n) { + uint8_t b = cpu_ldub_code(ctx->env, ctx->base.pc_next++); + insn |= b << (32 - i * 8); + } + return insn; +} + +static uint32_t li(DisasContext *ctx, int sz) +{ + int32_t tmp, addr; + CPURXState *env = ctx->env; + addr = ctx->base.pc_next; + + tcg_debug_assert(sz < 4); + switch (sz) { + case 1: + ctx->base.pc_next += 1; + return cpu_ldsb_code(env, addr); + case 2: + ctx->base.pc_next += 2; + return cpu_ldsw_code(env, addr); + case 3: + ctx->base.pc_next += 3; + tmp = cpu_ldsb_code(env, addr + 2) << 16; + tmp |= cpu_lduw_code(env, addr) & 0xffff; + return tmp; + case 0: + ctx->base.pc_next += 4; + return cpu_ldl_code(env, addr); + } + return 0; +} + +static int bdsp_s(DisasContext *ctx, int d) +{ + /* + * 0 -> 8 + * 1 -> 9 + * 2 -> 10 + * 3 -> 3 + * : + * 7 -> 7 + */ + if (d < 3) { + d += 8; + } + return d; +} + +/* Include the auto-generated decoder. */ +#include "decode.inc.c" + +void rx_cpu_dump_state(CPUState *cs, FILE *f, int flags) +{ + RXCPU *cpu = RXCPU(cs); + CPURXState *env = &cpu->env; + int i; + uint32_t psw; + + psw = rx_cpu_pack_psw(env); + qemu_fprintf(f, "pc=0x%08x psw=0x%08x\n", + env->pc, psw); + for (i = 0; i < 16; i += 4) { + qemu_fprintf(f, "r%d=0x%08x r%d=0x%08x r%d=0x%08x r%d=0x%08x\n", + i, env->regs[i], i + 1, env->regs[i + 1], + i + 2, env->regs[i + 2], i + 3, env->regs[i + 3]); + } +} + +static bool use_goto_tb(DisasContext *dc, target_ulong dest) +{ + if (unlikely(dc->base.singlestep_enabled)) { + return false; + } else { + return true; + } +} + +static void gen_goto_tb(DisasContext *dc, int n, target_ulong dest) +{ + if (use_goto_tb(dc, dest)) { + tcg_gen_goto_tb(n); + tcg_gen_movi_i32(cpu_pc, dest); + tcg_gen_exit_tb(dc->base.tb, n); + } else { + tcg_gen_movi_i32(cpu_pc, dest); + if (dc->base.singlestep_enabled) { + gen_helper_debug(cpu_env); + } else { + tcg_gen_lookup_and_goto_ptr(); + } + } + dc->base.is_jmp = DISAS_NORETURN; +} + +/* generic load wrapper */ +static inline void rx_gen_ld(unsigned int size, TCGv reg, TCGv mem) +{ + tcg_gen_qemu_ld_i32(reg, mem, 0, size | MO_SIGN | MO_TE); +} + +/* unsigned load wrapper */ +static inline void rx_gen_ldu(unsigned int size, TCGv reg, TCGv mem) +{ + tcg_gen_qemu_ld_i32(reg, mem, 0, size | MO_TE); +} + +/* generic store wrapper */ +static inline void rx_gen_st(unsigned int size, TCGv reg, TCGv mem) +{ + tcg_gen_qemu_st_i32(reg, mem, 0, size | MO_TE); +} + +/* [ri, rb] */ +static inline void rx_gen_regindex(DisasContext *ctx, TCGv mem, + int size, int ri, int rb) +{ + tcg_gen_shli_i32(mem, cpu_regs[ri], size); + tcg_gen_add_i32(mem, mem, cpu_regs[rb]); +} + +/* dsp[reg] */ +static inline TCGv rx_index_addr(DisasContext *ctx, TCGv mem, + int ld, int size, int reg) +{ + uint32_t dsp; + + tcg_debug_assert(ld < 3); + switch (ld) { + case 0: + return cpu_regs[reg]; + case 1: + dsp = cpu_ldub_code(ctx->env, ctx->base.pc_next) << size; + tcg_gen_addi_i32(mem, cpu_regs[reg], dsp); + ctx->base.pc_next += 1; + return mem; + case 2: + dsp = cpu_lduw_code(ctx->env, ctx->base.pc_next) << size; + tcg_gen_addi_i32(mem, cpu_regs[reg], dsp); + ctx->base.pc_next += 2; + return mem; + } + return NULL; +} + +static inline MemOp mi_to_mop(unsigned mi) +{ + static const MemOp mop[5] = { MO_SB, MO_SW, MO_UL, MO_UW, MO_UB }; + tcg_debug_assert(mi < 5); + return mop[mi]; +} + +/* load source operand */ +static inline TCGv rx_load_source(DisasContext *ctx, TCGv mem, + int ld, int mi, int rs) +{ + TCGv addr; + MemOp mop; + if (ld < 3) { + mop = mi_to_mop(mi); + addr = rx_index_addr(ctx, mem, ld, mop & MO_SIZE, rs); + tcg_gen_qemu_ld_i32(mem, addr, 0, mop | MO_TE); + return mem; + } else { + return cpu_regs[rs]; + } +} + +/* Processor mode check */ +static int is_privileged(DisasContext *ctx, int is_exception) +{ + if (FIELD_EX32(ctx->base.tb->flags, PSW, PM)) { + if (is_exception) { + gen_helper_raise_privilege_violation(cpu_env); + } + return 0; + } else { + return 1; + } +} + +/* generate QEMU condition */ +static void psw_cond(DisasCompare *dc, uint32_t cond) +{ + tcg_debug_assert(cond < 16); + switch (cond) { + case 0: /* z */ + dc->cond = TCG_COND_EQ; + dc->value = cpu_psw_z; + break; + case 1: /* nz */ + dc->cond = TCG_COND_NE; + dc->value = cpu_psw_z; + break; + case 2: /* c */ + dc->cond = TCG_COND_NE; + dc->value = cpu_psw_c; + break; + case 3: /* nc */ + dc->cond = TCG_COND_EQ; + dc->value = cpu_psw_c; + break; + case 4: /* gtu (C& ~Z) == 1 */ + case 5: /* leu (C& ~Z) == 0 */ + tcg_gen_setcondi_i32(TCG_COND_NE, dc->temp, cpu_psw_z, 0); + tcg_gen_and_i32(dc->temp, dc->temp, cpu_psw_c); + dc->cond = (cond == 4) ? TCG_COND_NE : TCG_COND_EQ; + dc->value = dc->temp; + break; + case 6: /* pz (S == 0) */ + dc->cond = TCG_COND_GE; + dc->value = cpu_psw_s; + break; + case 7: /* n (S == 1) */ + dc->cond = TCG_COND_LT; + dc->value = cpu_psw_s; + break; + case 8: /* ge (S^O)==0 */ + case 9: /* lt (S^O)==1 */ + tcg_gen_xor_i32(dc->temp, cpu_psw_o, cpu_psw_s); + dc->cond = (cond == 8) ? TCG_COND_GE : TCG_COND_LT; + dc->value = dc->temp; + break; + case 10: /* gt ((S^O)|Z)==0 */ + case 11: /* le ((S^O)|Z)==1 */ + tcg_gen_xor_i32(dc->temp, cpu_psw_o, cpu_psw_s); + tcg_gen_sari_i32(dc->temp, dc->temp, 31); + tcg_gen_andc_i32(dc->temp, cpu_psw_z, dc->temp); + dc->cond = (cond == 10) ? TCG_COND_NE : TCG_COND_EQ; + dc->value = dc->temp; + break; + case 12: /* o */ + dc->cond = TCG_COND_LT; + dc->value = cpu_psw_o; + break; + case 13: /* no */ + dc->cond = TCG_COND_GE; + dc->value = cpu_psw_o; + break; + case 14: /* always true */ + dc->cond = TCG_COND_ALWAYS; + dc->value = dc->temp; + break; + case 15: /* always false */ + dc->cond = TCG_COND_NEVER; + dc->value = dc->temp; + break; + } +} + +static void move_from_cr(TCGv ret, int cr, uint32_t pc) +{ + TCGv z = tcg_const_i32(0); + switch (cr) { + case 0: /* PSW */ + gen_helper_pack_psw(ret, cpu_env); + break; + case 1: /* PC */ + tcg_gen_movi_i32(ret, pc); + break; + case 2: /* USP */ + tcg_gen_movcond_i32(TCG_COND_NE, ret, + cpu_psw_u, z, cpu_sp, cpu_usp); + break; + case 3: /* FPSW */ + tcg_gen_mov_i32(ret, cpu_fpsw); + break; + case 8: /* BPSW */ + tcg_gen_mov_i32(ret, cpu_bpsw); + break; + case 9: /* BPC */ + tcg_gen_mov_i32(ret, cpu_bpc); + break; + case 10: /* ISP */ + tcg_gen_movcond_i32(TCG_COND_EQ, ret, + cpu_psw_u, z, cpu_sp, cpu_isp); + break; + case 11: /* FINTV */ + tcg_gen_mov_i32(ret, cpu_fintv); + break; + case 12: /* INTB */ + tcg_gen_mov_i32(ret, cpu_intb); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "Unimplement control register %d", cr); + /* Unimplement registers return 0 */ + tcg_gen_movi_i32(ret, 0); + break; + } + tcg_temp_free(z); +} + +static void move_to_cr(DisasContext *ctx, TCGv val, int cr) +{ + TCGv z; + if (cr >= 8 && !is_privileged(ctx, 0)) { + /* Some control registers can only be written in privileged mode. */ + qemu_log_mask(LOG_GUEST_ERROR, + "disallow control register write %s", rx_crname(cr)); + return; + } + z = tcg_const_i32(0); + switch (cr) { + case 0: /* PSW */ + gen_helper_set_psw(cpu_env, val); + break; + /* case 1: to PC not supported */ + case 2: /* USP */ + tcg_gen_mov_i32(cpu_usp, val); + tcg_gen_movcond_i32(TCG_COND_NE, cpu_sp, + cpu_psw_u, z, cpu_usp, cpu_sp); + break; + case 3: /* FPSW */ + gen_helper_set_fpsw(cpu_env, val); + break; + case 8: /* BPSW */ + tcg_gen_mov_i32(cpu_bpsw, val); + break; + case 9: /* BPC */ + tcg_gen_mov_i32(cpu_bpc, val); + break; + case 10: /* ISP */ + tcg_gen_mov_i32(cpu_isp, val); + /* if PSW.U is 0, copy isp to r0 */ + tcg_gen_movcond_i32(TCG_COND_EQ, cpu_sp, + cpu_psw_u, z, cpu_isp, cpu_sp); + break; + case 11: /* FINTV */ + tcg_gen_mov_i32(cpu_fintv, val); + break; + case 12: /* INTB */ + tcg_gen_mov_i32(cpu_intb, val); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "Unimplement control register %d", cr); + break; + } + tcg_temp_free(z); +} + +static void push(TCGv val) +{ + tcg_gen_subi_i32(cpu_sp, cpu_sp, 4); + rx_gen_st(MO_32, val, cpu_sp); +} + +static void pop(TCGv ret) +{ + rx_gen_ld(MO_32, ret, cpu_sp); + tcg_gen_addi_i32(cpu_sp, cpu_sp, 4); +} + +/* mov. rs,dsp5[rd] */ +static bool trans_MOV_rm(DisasContext *ctx, arg_MOV_rm *a) +{ + TCGv mem; + mem = tcg_temp_new(); + tcg_gen_addi_i32(mem, cpu_regs[a->rd], a->dsp << a->sz); + rx_gen_st(a->sz, cpu_regs[a->rs], mem); + tcg_temp_free(mem); + return true; +} + +/* mov. dsp5[rs],rd */ +static bool trans_MOV_mr(DisasContext *ctx, arg_MOV_mr *a) +{ + TCGv mem; + mem = tcg_temp_new(); + tcg_gen_addi_i32(mem, cpu_regs[a->rs], a->dsp << a->sz); + rx_gen_ld(a->sz, cpu_regs[a->rd], mem); + tcg_temp_free(mem); + return true; +} + +/* mov.l #uimm4,rd */ +/* mov.l #uimm8,rd */ +/* mov.l #imm,rd */ +static bool trans_MOV_ir(DisasContext *ctx, arg_MOV_ir *a) +{ + tcg_gen_movi_i32(cpu_regs[a->rd], a->imm); + return true; +} + +/* mov. #uimm8,dsp[rd] */ +/* mov. #imm, dsp[rd] */ +static bool trans_MOV_im(DisasContext *ctx, arg_MOV_im *a) +{ + TCGv imm, mem; + imm = tcg_const_i32(a->imm); + mem = tcg_temp_new(); + tcg_gen_addi_i32(mem, cpu_regs[a->rd], a->dsp << a->sz); + rx_gen_st(a->sz, imm, mem); + tcg_temp_free(imm); + tcg_temp_free(mem); + return true; +} + +/* mov. [ri,rb],rd */ +static bool trans_MOV_ar(DisasContext *ctx, arg_MOV_ar *a) +{ + TCGv mem; + mem = tcg_temp_new(); + rx_gen_regindex(ctx, mem, a->sz, a->ri, a->rb); + rx_gen_ld(a->sz, cpu_regs[a->rd], mem); + tcg_temp_free(mem); + return true; +} + +/* mov. rd,[ri,rb] */ +static bool trans_MOV_ra(DisasContext *ctx, arg_MOV_ra *a) +{ + TCGv mem; + mem = tcg_temp_new(); + rx_gen_regindex(ctx, mem, a->sz, a->ri, a->rb); + rx_gen_st(a->sz, cpu_regs[a->rs], mem); + tcg_temp_free(mem); + return true; +} + +/* mov. dsp[rs],dsp[rd] */ +/* mov. rs,dsp[rd] */ +/* mov. dsp[rs],rd */ +/* mov. rs,rd */ +static bool trans_MOV_mm(DisasContext *ctx, arg_MOV_mm *a) +{ + static void (* const mov[])(TCGv ret, TCGv arg) = { + tcg_gen_ext8s_i32, tcg_gen_ext16s_i32, tcg_gen_mov_i32, + }; + TCGv tmp, mem, addr; + if (a->lds == 3 && a->ldd == 3) { + /* mov. rs,rd */ + mov[a->sz](cpu_regs[a->rd], cpu_regs[a->rs]); + return true; + } + + mem = tcg_temp_new(); + if (a->lds == 3) { + /* mov. rs,dsp[rd] */ + addr = rx_index_addr(ctx, mem, a->ldd, a->sz, a->rs); + rx_gen_st(a->sz, cpu_regs[a->rd], addr); + } else if (a->ldd == 3) { + /* mov. dsp[rs],rd */ + addr = rx_index_addr(ctx, mem, a->lds, a->sz, a->rs); + rx_gen_ld(a->sz, cpu_regs[a->rd], addr); + } else { + /* mov. dsp[rs],dsp[rd] */ + tmp = tcg_temp_new(); + addr = rx_index_addr(ctx, mem, a->lds, a->sz, a->rs); + rx_gen_ld(a->sz, tmp, addr); + addr = rx_index_addr(ctx, mem, a->ldd, a->sz, a->rd); + rx_gen_st(a->sz, tmp, addr); + tcg_temp_free(tmp); + } + tcg_temp_free(mem); + return true; +} + +/* mov. rs,[rd+] */ +/* mov. rs,[-rd] */ +static bool trans_MOV_rp(DisasContext *ctx, arg_MOV_rp *a) +{ + TCGv val; + val = tcg_temp_new(); + tcg_gen_mov_i32(val, cpu_regs[a->rs]); + if (a->ad == 1) { + tcg_gen_subi_i32(cpu_regs[a->rd], cpu_regs[a->rd], 1 << a->sz); + } + rx_gen_st(a->sz, val, cpu_regs[a->rd]); + if (a->ad == 0) { + tcg_gen_addi_i32(cpu_regs[a->rd], cpu_regs[a->rd], 1 << a->sz); + } + tcg_temp_free(val); + return true; +} + +/* mov. [rd+],rs */ +/* mov. [-rd],rs */ +static bool trans_MOV_pr(DisasContext *ctx, arg_MOV_pr *a) +{ + TCGv val; + val = tcg_temp_new(); + if (a->ad == 1) { + tcg_gen_subi_i32(cpu_regs[a->rd], cpu_regs[a->rd], 1 << a->sz); + } + rx_gen_ld(a->sz, val, cpu_regs[a->rd]); + if (a->ad == 0) { + tcg_gen_addi_i32(cpu_regs[a->rd], cpu_regs[a->rd], 1 << a->sz); + } + tcg_gen_mov_i32(cpu_regs[a->rs], val); + tcg_temp_free(val); + return true; +} + +/* movu. dsp5[rs],rd */ +/* movu. dsp[rs],rd */ +static bool trans_MOVU_mr(DisasContext *ctx, arg_MOVU_mr *a) +{ + TCGv mem; + mem = tcg_temp_new(); + tcg_gen_addi_i32(mem, cpu_regs[a->rs], a->dsp << a->sz); + rx_gen_ldu(a->sz, cpu_regs[a->rd], mem); + tcg_temp_free(mem); + return true; +} + +/* movu. rs,rd */ +static bool trans_MOVU_rr(DisasContext *ctx, arg_MOVU_rr *a) +{ + static void (* const ext[])(TCGv ret, TCGv arg) = { + tcg_gen_ext8u_i32, tcg_gen_ext16u_i32, + }; + ext[a->sz](cpu_regs[a->rd], cpu_regs[a->rs]); + return true; +} + +/* movu. [ri,rb],rd */ +static bool trans_MOVU_ar(DisasContext *ctx, arg_MOVU_ar *a) +{ + TCGv mem; + mem = tcg_temp_new(); + rx_gen_regindex(ctx, mem, a->sz, a->ri, a->rb); + rx_gen_ldu(a->sz, cpu_regs[a->rd], mem); + tcg_temp_free(mem); + return true; +} + +/* movu. [rd+],rs */ +/* mov. [-rd],rs */ +static bool trans_MOVU_pr(DisasContext *ctx, arg_MOVU_pr *a) +{ + TCGv val; + val = tcg_temp_new(); + if (a->ad == 1) { + tcg_gen_subi_i32(cpu_regs[a->rd], cpu_regs[a->rd], 1 << a->sz); + } + rx_gen_ldu(a->sz, val, cpu_regs[a->rd]); + if (a->ad == 0) { + tcg_gen_addi_i32(cpu_regs[a->rd], cpu_regs[a->rd], 1 << a->sz); + } + tcg_gen_mov_i32(cpu_regs[a->rs], val); + tcg_temp_free(val); + return true; +} + + +/* pop rd */ +static bool trans_POP(DisasContext *ctx, arg_POP *a) +{ + /* mov.l [r0+], rd */ + arg_MOV_rp mov_a; + mov_a.rd = 0; + mov_a.rs = a->rd; + mov_a.ad = 0; + mov_a.sz = MO_32; + trans_MOV_pr(ctx, &mov_a); + return true; +} + +/* popc cr */ +static bool trans_POPC(DisasContext *ctx, arg_POPC *a) +{ + TCGv val; + val = tcg_temp_new(); + pop(val); + move_to_cr(ctx, val, a->cr); + if (a->cr == 0 && is_privileged(ctx, 0)) { + /* PSW.I may be updated here. exit TB. */ + ctx->base.is_jmp = DISAS_UPDATE; + } + tcg_temp_free(val); + return true; +} + +/* popm rd-rd2 */ +static bool trans_POPM(DisasContext *ctx, arg_POPM *a) +{ + int r; + if (a->rd == 0 || a->rd >= a->rd2) { + qemu_log_mask(LOG_GUEST_ERROR, + "Invalid register ranges r%d-r%d", a->rd, a->rd2); + } + r = a->rd; + while (r <= a->rd2 && r < 16) { + pop(cpu_regs[r++]); + } + return true; +} + + +/* push. rs */ +static bool trans_PUSH_r(DisasContext *ctx, arg_PUSH_r *a) +{ + TCGv val; + val = tcg_temp_new(); + tcg_gen_mov_i32(val, cpu_regs[a->rs]); + tcg_gen_subi_i32(cpu_sp, cpu_sp, 4); + rx_gen_st(a->sz, val, cpu_sp); + tcg_temp_free(val); + return true; +} + +/* push. dsp[rs] */ +static bool trans_PUSH_m(DisasContext *ctx, arg_PUSH_m *a) +{ + TCGv mem, val, addr; + mem = tcg_temp_new(); + val = tcg_temp_new(); + addr = rx_index_addr(ctx, mem, a->ld, a->sz, a->rs); + rx_gen_ld(a->sz, val, addr); + tcg_gen_subi_i32(cpu_sp, cpu_sp, 4); + rx_gen_st(a->sz, val, cpu_sp); + tcg_temp_free(mem); + tcg_temp_free(val); + return true; +} + +/* pushc rx */ +static bool trans_PUSHC(DisasContext *ctx, arg_PUSHC *a) +{ + TCGv val; + val = tcg_temp_new(); + move_from_cr(val, a->cr, ctx->pc); + push(val); + tcg_temp_free(val); + return true; +} + +/* pushm rs-rs2 */ +static bool trans_PUSHM(DisasContext *ctx, arg_PUSHM *a) +{ + int r; + + if (a->rs == 0 || a->rs >= a->rs2) { + qemu_log_mask(LOG_GUEST_ERROR, + "Invalid register ranges r%d-r%d", a->rs, a->rs2); + } + r = a->rs2; + while (r >= a->rs && r >= 0) { + push(cpu_regs[r--]); + } + return true; +} + +/* xchg rs,rd */ +static bool trans_XCHG_rr(DisasContext *ctx, arg_XCHG_rr *a) +{ + TCGv tmp; + tmp = tcg_temp_new(); + tcg_gen_mov_i32(tmp, cpu_regs[a->rs]); + tcg_gen_mov_i32(cpu_regs[a->rs], cpu_regs[a->rd]); + tcg_gen_mov_i32(cpu_regs[a->rd], tmp); + tcg_temp_free(tmp); + return true; +} + +/* xchg dsp[rs].,rd */ +static bool trans_XCHG_mr(DisasContext *ctx, arg_XCHG_mr *a) +{ + TCGv mem, addr; + mem = tcg_temp_new(); + switch (a->mi) { + case 0: /* dsp[rs].b */ + case 1: /* dsp[rs].w */ + case 2: /* dsp[rs].l */ + addr = rx_index_addr(ctx, mem, a->ld, a->mi, a->rs); + break; + case 3: /* dsp[rs].uw */ + case 4: /* dsp[rs].ub */ + addr = rx_index_addr(ctx, mem, a->ld, 4 - a->mi, a->rs); + break; + default: + g_assert_not_reached(); + } + tcg_gen_atomic_xchg_i32(cpu_regs[a->rd], addr, cpu_regs[a->rd], + 0, mi_to_mop(a->mi)); + tcg_temp_free(mem); + return true; +} + +static inline void stcond(TCGCond cond, int rd, int imm) +{ + TCGv z; + TCGv _imm; + z = tcg_const_i32(0); + _imm = tcg_const_i32(imm); + tcg_gen_movcond_i32(cond, cpu_regs[rd], cpu_psw_z, z, + _imm, cpu_regs[rd]); + tcg_temp_free(z); + tcg_temp_free(_imm); +} + +/* stz #imm,rd */ +static bool trans_STZ(DisasContext *ctx, arg_STZ *a) +{ + stcond(TCG_COND_EQ, a->rd, a->imm); + return true; +} + +/* stnz #imm,rd */ +static bool trans_STNZ(DisasContext *ctx, arg_STNZ *a) +{ + stcond(TCG_COND_NE, a->rd, a->imm); + return true; +} + +/* sccnd. rd */ +/* sccnd. dsp:[rd] */ +static bool trans_SCCnd(DisasContext *ctx, arg_SCCnd *a) +{ + DisasCompare dc; + TCGv val, mem, addr; + dc.temp = tcg_temp_new(); + psw_cond(&dc, a->cd); + if (a->ld < 3) { + val = tcg_temp_new(); + mem = tcg_temp_new(); + tcg_gen_setcondi_i32(dc.cond, val, dc.value, 0); + addr = rx_index_addr(ctx, mem, a->sz, a->ld, a->rd); + rx_gen_st(a->sz, val, addr); + tcg_temp_free(val); + tcg_temp_free(mem); + } else { + tcg_gen_setcondi_i32(dc.cond, cpu_regs[a->rd], dc.value, 0); + } + tcg_temp_free(dc.temp); + return true; +} + +/* rtsd #imm */ +static bool trans_RTSD_i(DisasContext *ctx, arg_RTSD_i *a) +{ + tcg_gen_addi_i32(cpu_sp, cpu_sp, a->imm << 2); + pop(cpu_pc); + ctx->base.is_jmp = DISAS_JUMP; + return true; +} + +/* rtsd #imm, rd-rd2 */ +static bool trans_RTSD_irr(DisasContext *ctx, arg_RTSD_irr *a) +{ + int dst; + int adj; + + if (a->rd2 >= a->rd) { + adj = a->imm - (a->rd2 - a->rd + 1); + } else { + adj = a->imm - (15 - a->rd + 1); + } + + tcg_gen_addi_i32(cpu_sp, cpu_sp, adj << 2); + dst = a->rd; + while (dst <= a->rd2 && dst < 16) { + pop(cpu_regs[dst++]); + } + pop(cpu_pc); + ctx->base.is_jmp = DISAS_JUMP; + return true; +} + +typedef void (*op2fn)(TCGv ret, TCGv arg1); +typedef void (*op3fn)(TCGv ret, TCGv arg1, TCGv arg2); + +static inline void rx_gen_op_rr(op2fn opr, int dst, int src) +{ + opr(cpu_regs[dst], cpu_regs[src]); +} + +static inline void rx_gen_op_rrr(op3fn opr, int dst, int src, int src2) +{ + opr(cpu_regs[dst], cpu_regs[src], cpu_regs[src2]); +} + +static inline void rx_gen_op_irr(op3fn opr, int dst, int src, uint32_t src2) +{ + TCGv imm = tcg_const_i32(src2); + opr(cpu_regs[dst], cpu_regs[src], imm); + tcg_temp_free(imm); +} + +static inline void rx_gen_op_mr(op3fn opr, DisasContext *ctx, + int dst, int src, int ld, int mi) +{ + TCGv val, mem; + mem = tcg_temp_new(); + val = rx_load_source(ctx, mem, ld, mi, src); + opr(cpu_regs[dst], cpu_regs[dst], val); + tcg_temp_free(mem); +} + +static void rx_and(TCGv ret, TCGv arg1, TCGv arg2) +{ + tcg_gen_and_i32(cpu_psw_s, arg1, arg2); + tcg_gen_mov_i32(cpu_psw_z, cpu_psw_s); + tcg_gen_mov_i32(ret, cpu_psw_s); +} + +/* and #uimm:4, rd */ +/* and #imm, rd */ +static bool trans_AND_ir(DisasContext *ctx, arg_AND_ir *a) +{ + rx_gen_op_irr(rx_and, a->rd, a->rd, a->imm); + return true; +} + +/* and dsp[rs], rd */ +/* and rs,rd */ +static bool trans_AND_mr(DisasContext *ctx, arg_AND_mr *a) +{ + rx_gen_op_mr(rx_and, ctx, a->rd, a->rs, a->ld, a->mi); + return true; +} + +/* and rs,rs2,rd */ +static bool trans_AND_rrr(DisasContext *ctx, arg_AND_rrr *a) +{ + rx_gen_op_rrr(rx_and, a->rd, a->rs, a->rs2); + return true; +} + +static void rx_or(TCGv ret, TCGv arg1, TCGv arg2) +{ + tcg_gen_or_i32(cpu_psw_s, arg1, arg2); + tcg_gen_mov_i32(cpu_psw_z, cpu_psw_s); + tcg_gen_mov_i32(ret, cpu_psw_s); +} + +/* or #uimm:4, rd */ +/* or #imm, rd */ +static bool trans_OR_ir(DisasContext *ctx, arg_OR_ir *a) +{ + rx_gen_op_irr(rx_or, a->rd, a->rd, a->imm); + return true; +} + +/* or dsp[rs], rd */ +/* or rs,rd */ +static bool trans_OR_mr(DisasContext *ctx, arg_OR_mr *a) +{ + rx_gen_op_mr(rx_or, ctx, a->rd, a->rs, a->ld, a->mi); + return true; +} + +/* or rs,rs2,rd */ +static bool trans_OR_rrr(DisasContext *ctx, arg_OR_rrr *a) +{ + rx_gen_op_rrr(rx_or, a->rd, a->rs, a->rs2); + return true; +} + +static void rx_xor(TCGv ret, TCGv arg1, TCGv arg2) +{ + tcg_gen_xor_i32(cpu_psw_s, arg1, arg2); + tcg_gen_mov_i32(cpu_psw_z, cpu_psw_s); + tcg_gen_mov_i32(ret, cpu_psw_s); +} + +/* xor #imm, rd */ +static bool trans_XOR_ir(DisasContext *ctx, arg_XOR_ir *a) +{ + rx_gen_op_irr(rx_xor, a->rd, a->rd, a->imm); + return true; +} + +/* xor dsp[rs], rd */ +/* xor rs,rd */ +static bool trans_XOR_mr(DisasContext *ctx, arg_XOR_mr *a) +{ + rx_gen_op_mr(rx_xor, ctx, a->rd, a->rs, a->ld, a->mi); + return true; +} + +static void rx_tst(TCGv ret, TCGv arg1, TCGv arg2) +{ + tcg_gen_and_i32(cpu_psw_s, arg1, arg2); + tcg_gen_mov_i32(cpu_psw_z, cpu_psw_s); +} + +/* tst #imm, rd */ +static bool trans_TST_ir(DisasContext *ctx, arg_TST_ir *a) +{ + rx_gen_op_irr(rx_tst, a->rd, a->rd, a->imm); + return true; +} + +/* tst dsp[rs], rd */ +/* tst rs, rd */ +static bool trans_TST_mr(DisasContext *ctx, arg_TST_mr *a) +{ + rx_gen_op_mr(rx_tst, ctx, a->rd, a->rs, a->ld, a->mi); + return true; +} + +static void rx_not(TCGv ret, TCGv arg1) +{ + tcg_gen_not_i32(ret, arg1); + tcg_gen_mov_i32(cpu_psw_z, ret); + tcg_gen_mov_i32(cpu_psw_s, ret); +} + +/* not rd */ +/* not rs, rd */ +static bool trans_NOT_rr(DisasContext *ctx, arg_NOT_rr *a) +{ + rx_gen_op_rr(rx_not, a->rd, a->rs); + return true; +} + +static void rx_neg(TCGv ret, TCGv arg1) +{ + tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_psw_o, arg1, 0x80000000); + tcg_gen_neg_i32(ret, arg1); + tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_psw_c, ret, 0); + tcg_gen_mov_i32(cpu_psw_z, ret); + tcg_gen_mov_i32(cpu_psw_s, ret); +} + + +/* neg rd */ +/* neg rs, rd */ +static bool trans_NEG_rr(DisasContext *ctx, arg_NEG_rr *a) +{ + rx_gen_op_rr(rx_neg, a->rd, a->rs); + return true; +} + +/* ret = arg1 + arg2 + psw_c */ +static void rx_adc(TCGv ret, TCGv arg1, TCGv arg2) +{ + TCGv z; + z = tcg_const_i32(0); + tcg_gen_add2_i32(cpu_psw_s, cpu_psw_c, arg1, z, cpu_psw_c, z); + tcg_gen_add2_i32(cpu_psw_s, cpu_psw_c, cpu_psw_s, cpu_psw_c, arg2, z); + tcg_gen_mov_i32(cpu_psw_z, cpu_psw_s); + tcg_gen_xor_i32(cpu_psw_o, cpu_psw_s, arg1); + tcg_gen_xor_i32(z, arg1, arg2); + tcg_gen_andc_i32(cpu_psw_o, cpu_psw_o, z); + tcg_gen_mov_i32(ret, cpu_psw_s); + tcg_temp_free(z); +} + +/* adc #imm, rd */ +static bool trans_ADC_ir(DisasContext *ctx, arg_ADC_ir *a) +{ + rx_gen_op_irr(rx_adc, a->rd, a->rd, a->imm); + return true; +} + +/* adc rs, rd */ +static bool trans_ADC_rr(DisasContext *ctx, arg_ADC_rr *a) +{ + rx_gen_op_rrr(rx_adc, a->rd, a->rd, a->rs); + return true; +} + +/* adc dsp[rs], rd */ +static bool trans_ADC_mr(DisasContext *ctx, arg_ADC_mr *a) +{ + /* mi only 2 */ + if (a->mi != 2) { + return false; + } + rx_gen_op_mr(rx_adc, ctx, a->rd, a->rs, a->ld, a->mi); + return true; +} + +/* ret = arg1 + arg2 */ +static void rx_add(TCGv ret, TCGv arg1, TCGv arg2) +{ + TCGv z; + z = tcg_const_i32(0); + tcg_gen_add2_i32(cpu_psw_s, cpu_psw_c, arg1, z, arg2, z); + tcg_gen_mov_i32(cpu_psw_z, cpu_psw_s); + tcg_gen_xor_i32(cpu_psw_o, cpu_psw_s, arg1); + tcg_gen_xor_i32(z, arg1, arg2); + tcg_gen_andc_i32(cpu_psw_o, cpu_psw_o, z); + tcg_gen_mov_i32(ret, cpu_psw_s); + tcg_temp_free(z); +} + +/* add #uimm4, rd */ +/* add #imm, rs, rd */ +static bool trans_ADD_irr(DisasContext *ctx, arg_ADD_irr *a) +{ + rx_gen_op_irr(rx_add, a->rd, a->rs2, a->imm); + return true; +} + +/* add rs, rd */ +/* add dsp[rs], rd */ +static bool trans_ADD_mr(DisasContext *ctx, arg_ADD_mr *a) +{ + rx_gen_op_mr(rx_add, ctx, a->rd, a->rs, a->ld, a->mi); + return true; +} + +/* add rs, rs2, rd */ +static bool trans_ADD_rrr(DisasContext *ctx, arg_ADD_rrr *a) +{ + rx_gen_op_rrr(rx_add, a->rd, a->rs, a->rs2); + return true; +} + +/* ret = arg1 - arg2 */ +static void rx_sub(TCGv ret, TCGv arg1, TCGv arg2) +{ + TCGv temp; + tcg_gen_sub_i32(cpu_psw_s, arg1, arg2); + tcg_gen_mov_i32(cpu_psw_z, cpu_psw_s); + tcg_gen_setcond_i32(TCG_COND_GEU, cpu_psw_c, arg1, arg2); + tcg_gen_xor_i32(cpu_psw_o, cpu_psw_s, arg1); + temp = tcg_temp_new_i32(); + tcg_gen_xor_i32(temp, arg1, arg2); + tcg_gen_and_i32(cpu_psw_o, cpu_psw_o, temp); + tcg_temp_free_i32(temp); + /* CMP not requred return */ + if (ret) { + tcg_gen_mov_i32(ret, cpu_psw_s); + } +} +static void rx_cmp(TCGv dummy, TCGv arg1, TCGv arg2) +{ + rx_sub(NULL, arg1, arg2); +} +/* ret = arg1 - arg2 - !psw_c */ +/* -> ret = arg1 + ~arg2 + psw_c */ +static void rx_sbb(TCGv ret, TCGv arg1, TCGv arg2) +{ + TCGv temp; + temp = tcg_temp_new(); + tcg_gen_not_i32(temp, arg2); + rx_adc(ret, arg1, temp); + tcg_temp_free(temp); +} + +/* cmp #imm4, rs2 */ +/* cmp #imm8, rs2 */ +/* cmp #imm, rs2 */ +static bool trans_CMP_ir(DisasContext *ctx, arg_CMP_ir *a) +{ + rx_gen_op_irr(rx_cmp, 0, a->rs2, a->imm); + return true; +} + +/* cmp rs, rs2 */ +/* cmp dsp[rs], rs2 */ +static bool trans_CMP_mr(DisasContext *ctx, arg_CMP_mr *a) +{ + rx_gen_op_mr(rx_cmp, ctx, a->rd, a->rs, a->ld, a->mi); + return true; +} + +/* sub #imm4, rd */ +static bool trans_SUB_ir(DisasContext *ctx, arg_SUB_ir *a) +{ + rx_gen_op_irr(rx_sub, a->rd, a->rd, a->imm); + return true; +} + +/* sub rs, rd */ +/* sub dsp[rs], rd */ +static bool trans_SUB_mr(DisasContext *ctx, arg_SUB_mr *a) +{ + rx_gen_op_mr(rx_sub, ctx, a->rd, a->rs, a->ld, a->mi); + return true; +} + +/* sub rs2, rs, rd */ +static bool trans_SUB_rrr(DisasContext *ctx, arg_SUB_rrr *a) +{ + rx_gen_op_rrr(rx_sub, a->rd, a->rs2, a->rs); + return true; +} + +/* sbb rs, rd */ +static bool trans_SBB_rr(DisasContext *ctx, arg_SBB_rr *a) +{ + rx_gen_op_rrr(rx_sbb, a->rd, a->rd, a->rs); + return true; +} + +/* sbb dsp[rs], rd */ +static bool trans_SBB_mr(DisasContext *ctx, arg_SBB_mr *a) +{ + /* mi only 2 */ + if (a->mi != 2) { + return false; + } + rx_gen_op_mr(rx_sbb, ctx, a->rd, a->rs, a->ld, a->mi); + return true; +} + +static void rx_abs(TCGv ret, TCGv arg1) +{ + TCGv neg; + TCGv zero; + neg = tcg_temp_new(); + zero = tcg_const_i32(0); + tcg_gen_neg_i32(neg, arg1); + tcg_gen_movcond_i32(TCG_COND_LT, ret, arg1, zero, neg, arg1); + tcg_temp_free(neg); + tcg_temp_free(zero); +} + +/* abs rd */ +/* abs rs, rd */ +static bool trans_ABS_rr(DisasContext *ctx, arg_ABS_rr *a) +{ + rx_gen_op_rr(rx_abs, a->rd, a->rs); + return true; +} + +/* max #imm, rd */ +static bool trans_MAX_ir(DisasContext *ctx, arg_MAX_ir *a) +{ + rx_gen_op_irr(tcg_gen_smax_i32, a->rd, a->rd, a->imm); + return true; +} + +/* max rs, rd */ +/* max dsp[rs], rd */ +static bool trans_MAX_mr(DisasContext *ctx, arg_MAX_mr *a) +{ + rx_gen_op_mr(tcg_gen_smax_i32, ctx, a->rd, a->rs, a->ld, a->mi); + return true; +} + +/* min #imm, rd */ +static bool trans_MIN_ir(DisasContext *ctx, arg_MIN_ir *a) +{ + rx_gen_op_irr(tcg_gen_smin_i32, a->rd, a->rd, a->imm); + return true; +} + +/* min rs, rd */ +/* min dsp[rs], rd */ +static bool trans_MIN_mr(DisasContext *ctx, arg_MIN_mr *a) +{ + rx_gen_op_mr(tcg_gen_smin_i32, ctx, a->rd, a->rs, a->ld, a->mi); + return true; +} + +/* mul #uimm4, rd */ +/* mul #imm, rd */ +static bool trans_MUL_ir(DisasContext *ctx, arg_MUL_ir *a) +{ + rx_gen_op_irr(tcg_gen_mul_i32, a->rd, a->rd, a->imm); + return true; +} + +/* mul rs, rd */ +/* mul dsp[rs], rd */ +static bool trans_MUL_mr(DisasContext *ctx, arg_MUL_mr *a) +{ + rx_gen_op_mr(tcg_gen_mul_i32, ctx, a->rd, a->rs, a->ld, a->mi); + return true; +} + +/* mul rs, rs2, rd */ +static bool trans_MUL_rrr(DisasContext *ctx, arg_MUL_rrr *a) +{ + rx_gen_op_rrr(tcg_gen_mul_i32, a->rd, a->rs, a->rs2); + return true; +} + +/* emul #imm, rd */ +static bool trans_EMUL_ir(DisasContext *ctx, arg_EMUL_ir *a) +{ + TCGv imm = tcg_const_i32(a->imm); + if (a->rd > 14) { + qemu_log_mask(LOG_GUEST_ERROR, "rd too large %d", a->rd); + } + tcg_gen_muls2_i32(cpu_regs[a->rd], cpu_regs[(a->rd + 1) & 15], + cpu_regs[a->rd], imm); + tcg_temp_free(imm); + return true; +} + +/* emul rs, rd */ +/* emul dsp[rs], rd */ +static bool trans_EMUL_mr(DisasContext *ctx, arg_EMUL_mr *a) +{ + TCGv val, mem; + if (a->rd > 14) { + qemu_log_mask(LOG_GUEST_ERROR, "rd too large %d", a->rd); + } + mem = tcg_temp_new(); + val = rx_load_source(ctx, mem, a->ld, a->mi, a->rs); + tcg_gen_muls2_i32(cpu_regs[a->rd], cpu_regs[(a->rd + 1) & 15], + cpu_regs[a->rd], val); + tcg_temp_free(mem); + return true; +} + +/* emulu #imm, rd */ +static bool trans_EMULU_ir(DisasContext *ctx, arg_EMULU_ir *a) +{ + TCGv imm = tcg_const_i32(a->imm); + if (a->rd > 14) { + qemu_log_mask(LOG_GUEST_ERROR, "rd too large %d", a->rd); + } + tcg_gen_mulu2_i32(cpu_regs[a->rd], cpu_regs[(a->rd + 1) & 15], + cpu_regs[a->rd], imm); + tcg_temp_free(imm); + return true; +} + +/* emulu rs, rd */ +/* emulu dsp[rs], rd */ +static bool trans_EMULU_mr(DisasContext *ctx, arg_EMULU_mr *a) +{ + TCGv val, mem; + if (a->rd > 14) { + qemu_log_mask(LOG_GUEST_ERROR, "rd too large %d", a->rd); + } + mem = tcg_temp_new(); + val = rx_load_source(ctx, mem, a->ld, a->mi, a->rs); + tcg_gen_mulu2_i32(cpu_regs[a->rd], cpu_regs[(a->rd + 1) & 15], + cpu_regs[a->rd], val); + tcg_temp_free(mem); + return true; +} + +static void rx_div(TCGv ret, TCGv arg1, TCGv arg2) +{ + gen_helper_div(ret, cpu_env, arg1, arg2); +} + +static void rx_divu(TCGv ret, TCGv arg1, TCGv arg2) +{ + gen_helper_divu(ret, cpu_env, arg1, arg2); +} + +/* div #imm, rd */ +static bool trans_DIV_ir(DisasContext *ctx, arg_DIV_ir *a) +{ + rx_gen_op_irr(rx_div, a->rd, a->rd, a->imm); + return true; +} + +/* div rs, rd */ +/* div dsp[rs], rd */ +static bool trans_DIV_mr(DisasContext *ctx, arg_DIV_mr *a) +{ + rx_gen_op_mr(rx_div, ctx, a->rd, a->rs, a->ld, a->mi); + return true; +} + +/* divu #imm, rd */ +static bool trans_DIVU_ir(DisasContext *ctx, arg_DIVU_ir *a) +{ + rx_gen_op_irr(rx_divu, a->rd, a->rd, a->imm); + return true; +} + +/* divu rs, rd */ +/* divu dsp[rs], rd */ +static bool trans_DIVU_mr(DisasContext *ctx, arg_DIVU_mr *a) +{ + rx_gen_op_mr(rx_divu, ctx, a->rd, a->rs, a->ld, a->mi); + return true; +} + + +/* shll #imm:5, rd */ +/* shll #imm:5, rs2, rd */ +static bool trans_SHLL_irr(DisasContext *ctx, arg_SHLL_irr *a) +{ + TCGv tmp; + tmp = tcg_temp_new(); + if (a->imm) { + tcg_gen_sari_i32(cpu_psw_c, cpu_regs[a->rs2], 32 - a->imm); + tcg_gen_shli_i32(cpu_regs[a->rd], cpu_regs[a->rs2], a->imm); + tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_psw_o, cpu_psw_c, 0); + tcg_gen_setcondi_i32(TCG_COND_EQ, tmp, cpu_psw_c, 0xffffffff); + tcg_gen_or_i32(cpu_psw_o, cpu_psw_o, tmp); + tcg_gen_setcondi_i32(TCG_COND_NE, cpu_psw_c, cpu_psw_c, 0); + } else { + tcg_gen_mov_i32(cpu_regs[a->rd], cpu_regs[a->rs2]); + tcg_gen_movi_i32(cpu_psw_c, 0); + tcg_gen_movi_i32(cpu_psw_o, 0); + } + tcg_gen_mov_i32(cpu_psw_z, cpu_regs[a->rd]); + tcg_gen_mov_i32(cpu_psw_s, cpu_regs[a->rd]); + return true; +} + +/* shll rs, rd */ +static bool trans_SHLL_rr(DisasContext *ctx, arg_SHLL_rr *a) +{ + TCGLabel *noshift, *done; + TCGv count, tmp; + + noshift = gen_new_label(); + done = gen_new_label(); + /* if (cpu_regs[a->rs]) { */ + tcg_gen_brcondi_i32(TCG_COND_EQ, cpu_regs[a->rs], 0, noshift); + count = tcg_const_i32(32); + tmp = tcg_temp_new(); + tcg_gen_andi_i32(tmp, cpu_regs[a->rs], 31); + tcg_gen_sub_i32(count, count, tmp); + tcg_gen_sar_i32(cpu_psw_c, cpu_regs[a->rd], count); + tcg_gen_shl_i32(cpu_regs[a->rd], cpu_regs[a->rd], tmp); + tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_psw_o, cpu_psw_c, 0); + tcg_gen_setcondi_i32(TCG_COND_EQ, tmp, cpu_psw_c, 0xffffffff); + tcg_gen_or_i32(cpu_psw_o, cpu_psw_o, tmp); + tcg_gen_setcondi_i32(TCG_COND_NE, cpu_psw_c, cpu_psw_c, 0); + tcg_gen_br(done); + /* } else { */ + gen_set_label(noshift); + tcg_gen_movi_i32(cpu_psw_c, 0); + tcg_gen_movi_i32(cpu_psw_o, 0); + /* } */ + gen_set_label(done); + tcg_gen_mov_i32(cpu_psw_z, cpu_regs[a->rd]); + tcg_gen_mov_i32(cpu_psw_s, cpu_regs[a->rd]); + tcg_temp_free(count); + tcg_temp_free(tmp); + return true; +} + +static inline void shiftr_imm(uint32_t rd, uint32_t rs, uint32_t imm, + unsigned int alith) +{ + static void (* const gen_sXri[])(TCGv ret, TCGv arg1, int arg2) = { + tcg_gen_shri_i32, tcg_gen_sari_i32, + }; + tcg_debug_assert(alith < 2); + if (imm) { + gen_sXri[alith](cpu_regs[rd], cpu_regs[rs], imm - 1); + tcg_gen_andi_i32(cpu_psw_c, cpu_regs[rd], 0x00000001); + gen_sXri[alith](cpu_regs[rd], cpu_regs[rd], 1); + } else { + tcg_gen_mov_i32(cpu_regs[rd], cpu_regs[rs]); + tcg_gen_movi_i32(cpu_psw_c, 0); + } + tcg_gen_movi_i32(cpu_psw_o, 0); + tcg_gen_mov_i32(cpu_psw_z, cpu_regs[rd]); + tcg_gen_mov_i32(cpu_psw_s, cpu_regs[rd]); +} + +static inline void shiftr_reg(uint32_t rd, uint32_t rs, unsigned int alith) +{ + TCGLabel *noshift, *done; + TCGv count; + static void (* const gen_sXri[])(TCGv ret, TCGv arg1, int arg2) = { + tcg_gen_shri_i32, tcg_gen_sari_i32, + }; + static void (* const gen_sXr[])(TCGv ret, TCGv arg1, TCGv arg2) = { + tcg_gen_shr_i32, tcg_gen_sar_i32, + }; + tcg_debug_assert(alith < 2); + noshift = gen_new_label(); + done = gen_new_label(); + count = tcg_temp_new(); + /* if (cpu_regs[rs]) { */ + tcg_gen_brcondi_i32(TCG_COND_EQ, cpu_regs[rs], 0, noshift); + tcg_gen_andi_i32(count, cpu_regs[rs], 31); + tcg_gen_subi_i32(count, count, 1); + gen_sXr[alith](cpu_regs[rd], cpu_regs[rd], count); + tcg_gen_andi_i32(cpu_psw_c, cpu_regs[rd], 0x00000001); + gen_sXri[alith](cpu_regs[rd], cpu_regs[rd], 1); + tcg_gen_br(done); + /* } else { */ + gen_set_label(noshift); + tcg_gen_movi_i32(cpu_psw_c, 0); + /* } */ + gen_set_label(done); + tcg_gen_movi_i32(cpu_psw_o, 0); + tcg_gen_mov_i32(cpu_psw_z, cpu_regs[rd]); + tcg_gen_mov_i32(cpu_psw_s, cpu_regs[rd]); + tcg_temp_free(count); +} + +/* shar #imm:5, rd */ +/* shar #imm:5, rs2, rd */ +static bool trans_SHAR_irr(DisasContext *ctx, arg_SHAR_irr *a) +{ + shiftr_imm(a->rd, a->rs2, a->imm, 1); + return true; +} + +/* shar rs, rd */ +static bool trans_SHAR_rr(DisasContext *ctx, arg_SHAR_rr *a) +{ + shiftr_reg(a->rd, a->rs, 1); + return true; +} + +/* shlr #imm:5, rd */ +/* shlr #imm:5, rs2, rd */ +static bool trans_SHLR_irr(DisasContext *ctx, arg_SHLR_irr *a) +{ + shiftr_imm(a->rd, a->rs2, a->imm, 0); + return true; +} + +/* shlr rs, rd */ +static bool trans_SHLR_rr(DisasContext *ctx, arg_SHLR_rr *a) +{ + shiftr_reg(a->rd, a->rs, 0); + return true; +} + +/* rolc rd */ +static bool trans_ROLC(DisasContext *ctx, arg_ROLC *a) +{ + TCGv tmp; + tmp = tcg_temp_new(); + tcg_gen_shri_i32(tmp, cpu_regs[a->rd], 31); + tcg_gen_shli_i32(cpu_regs[a->rd], cpu_regs[a->rd], 1); + tcg_gen_or_i32(cpu_regs[a->rd], cpu_regs[a->rd], cpu_psw_c); + tcg_gen_mov_i32(cpu_psw_c, tmp); + tcg_gen_mov_i32(cpu_psw_z, cpu_regs[a->rd]); + tcg_gen_mov_i32(cpu_psw_s, cpu_regs[a->rd]); + tcg_temp_free(tmp); + return true; +} + +/* rorc rd */ +static bool trans_RORC(DisasContext *ctx, arg_RORC *a) +{ + TCGv tmp; + tmp = tcg_temp_new(); + tcg_gen_andi_i32(tmp, cpu_regs[a->rd], 0x00000001); + tcg_gen_shri_i32(cpu_regs[a->rd], cpu_regs[a->rd], 1); + tcg_gen_shli_i32(cpu_psw_c, cpu_psw_c, 31); + tcg_gen_or_i32(cpu_regs[a->rd], cpu_regs[a->rd], cpu_psw_c); + tcg_gen_mov_i32(cpu_psw_c, tmp); + tcg_gen_mov_i32(cpu_psw_z, cpu_regs[a->rd]); + tcg_gen_mov_i32(cpu_psw_s, cpu_regs[a->rd]); + return true; +} + +enum {ROTR = 0, ROTL = 1}; +enum {ROT_IMM = 0, ROT_REG = 1}; +static inline void rx_rot(int ir, int dir, int rd, int src) +{ + switch (dir) { + case ROTL: + if (ir == ROT_IMM) { + tcg_gen_rotli_i32(cpu_regs[rd], cpu_regs[rd], src); + } else { + tcg_gen_rotl_i32(cpu_regs[rd], cpu_regs[rd], cpu_regs[src]); + } + tcg_gen_andi_i32(cpu_psw_c, cpu_regs[rd], 0x00000001); + break; + case ROTR: + if (ir == ROT_IMM) { + tcg_gen_rotri_i32(cpu_regs[rd], cpu_regs[rd], src); + } else { + tcg_gen_rotr_i32(cpu_regs[rd], cpu_regs[rd], cpu_regs[src]); + } + tcg_gen_shri_i32(cpu_psw_c, cpu_regs[rd], 31); + break; + } + tcg_gen_mov_i32(cpu_psw_z, cpu_regs[rd]); + tcg_gen_mov_i32(cpu_psw_s, cpu_regs[rd]); +} + +/* rotl #imm, rd */ +static bool trans_ROTL_ir(DisasContext *ctx, arg_ROTL_ir *a) +{ + rx_rot(ROT_IMM, ROTL, a->rd, a->imm); + return true; +} + +/* rotl rs, rd */ +static bool trans_ROTL_rr(DisasContext *ctx, arg_ROTL_rr *a) +{ + rx_rot(ROT_REG, ROTL, a->rd, a->rs); + return true; +} + +/* rotr #imm, rd */ +static bool trans_ROTR_ir(DisasContext *ctx, arg_ROTR_ir *a) +{ + rx_rot(ROT_IMM, ROTR, a->rd, a->imm); + return true; +} + +/* rotr rs, rd */ +static bool trans_ROTR_rr(DisasContext *ctx, arg_ROTR_rr *a) +{ + rx_rot(ROT_REG, ROTR, a->rd, a->rs); + return true; +} + +/* revl rs, rd */ +static bool trans_REVL(DisasContext *ctx, arg_REVL *a) +{ + tcg_gen_bswap32_i32(cpu_regs[a->rd], cpu_regs[a->rs]); + return true; +} + +/* revw rs, rd */ +static bool trans_REVW(DisasContext *ctx, arg_REVW *a) +{ + TCGv tmp; + tmp = tcg_temp_new(); + tcg_gen_andi_i32(tmp, cpu_regs[a->rs], 0x00ff00ff); + tcg_gen_shli_i32(tmp, tmp, 8); + tcg_gen_shri_i32(cpu_regs[a->rd], cpu_regs[a->rs], 8); + tcg_gen_andi_i32(cpu_regs[a->rd], cpu_regs[a->rd], 0x00ff00ff); + tcg_gen_or_i32(cpu_regs[a->rd], cpu_regs[a->rd], tmp); + tcg_temp_free(tmp); + return true; +} + +/* conditional branch helper */ +static void rx_bcnd_main(DisasContext *ctx, int cd, int dst) +{ + DisasCompare dc; + TCGLabel *t, *done; + + switch (cd) { + case 0 ... 13: + dc.temp = tcg_temp_new(); + psw_cond(&dc, cd); + t = gen_new_label(); + done = gen_new_label(); + tcg_gen_brcondi_i32(dc.cond, dc.value, 0, t); + gen_goto_tb(ctx, 0, ctx->base.pc_next); + tcg_gen_br(done); + gen_set_label(t); + gen_goto_tb(ctx, 1, ctx->pc + dst); + gen_set_label(done); + tcg_temp_free(dc.temp); + break; + case 14: + /* always true case */ + gen_goto_tb(ctx, 0, ctx->pc + dst); + break; + case 15: + /* always false case */ + /* Nothing do */ + break; + } +} + +/* beq dsp:3 / bne dsp:3 */ +/* beq dsp:8 / bne dsp:8 */ +/* bc dsp:8 / bnc dsp:8 */ +/* bgtu dsp:8 / bleu dsp:8 */ +/* bpz dsp:8 / bn dsp:8 */ +/* bge dsp:8 / blt dsp:8 */ +/* bgt dsp:8 / ble dsp:8 */ +/* bo dsp:8 / bno dsp:8 */ +/* beq dsp:16 / bne dsp:16 */ +static bool trans_BCnd(DisasContext *ctx, arg_BCnd *a) +{ + rx_bcnd_main(ctx, a->cd, a->dsp); + return true; +} + +/* bra dsp:3 */ +/* bra dsp:8 */ +/* bra dsp:16 */ +/* bra dsp:24 */ +static bool trans_BRA(DisasContext *ctx, arg_BRA *a) +{ + rx_bcnd_main(ctx, 14, a->dsp); + return true; +} + +/* bra rs */ +static bool trans_BRA_l(DisasContext *ctx, arg_BRA_l *a) +{ + tcg_gen_addi_i32(cpu_pc, cpu_regs[a->rd], ctx->pc); + ctx->base.is_jmp = DISAS_JUMP; + return true; +} + +static inline void rx_save_pc(DisasContext *ctx) +{ + TCGv pc = tcg_const_i32(ctx->base.pc_next); + push(pc); + tcg_temp_free(pc); +} + +/* jmp rs */ +static bool trans_JMP(DisasContext *ctx, arg_JMP *a) +{ + tcg_gen_mov_i32(cpu_pc, cpu_regs[a->rs]); + ctx->base.is_jmp = DISAS_JUMP; + return true; +} + +/* jsr rs */ +static bool trans_JSR(DisasContext *ctx, arg_JSR *a) +{ + rx_save_pc(ctx); + tcg_gen_mov_i32(cpu_pc, cpu_regs[a->rs]); + ctx->base.is_jmp = DISAS_JUMP; + return true; +} + +/* bsr dsp:16 */ +/* bsr dsp:24 */ +static bool trans_BSR(DisasContext *ctx, arg_BSR *a) +{ + rx_save_pc(ctx); + rx_bcnd_main(ctx, 14, a->dsp); + return true; +} + +/* bsr rs */ +static bool trans_BSR_l(DisasContext *ctx, arg_BSR_l *a) +{ + rx_save_pc(ctx); + tcg_gen_addi_i32(cpu_pc, cpu_regs[a->rd], ctx->pc); + ctx->base.is_jmp = DISAS_JUMP; + return true; +} + +/* rts */ +static bool trans_RTS(DisasContext *ctx, arg_RTS *a) +{ + pop(cpu_pc); + ctx->base.is_jmp = DISAS_JUMP; + return true; +} + +/* nop */ +static bool trans_NOP(DisasContext *ctx, arg_NOP *a) +{ + return true; +} + +/* scmpu */ +static bool trans_SCMPU(DisasContext *ctx, arg_SCMPU *a) +{ + gen_helper_scmpu(cpu_env); + return true; +} + +/* smovu */ +static bool trans_SMOVU(DisasContext *ctx, arg_SMOVU *a) +{ + gen_helper_smovu(cpu_env); + return true; +} + +/* smovf */ +static bool trans_SMOVF(DisasContext *ctx, arg_SMOVF *a) +{ + gen_helper_smovf(cpu_env); + return true; +} + +/* smovb */ +static bool trans_SMOVB(DisasContext *ctx, arg_SMOVB *a) +{ + gen_helper_smovb(cpu_env); + return true; +} + +#define STRING(op) \ + do { \ + TCGv size = tcg_const_i32(a->sz); \ + gen_helper_##op(cpu_env, size); \ + tcg_temp_free(size); \ + } while (0) + +/* suntile. */ +static bool trans_SUNTIL(DisasContext *ctx, arg_SUNTIL *a) +{ + STRING(suntil); + return true; +} + +/* swhile. */ +static bool trans_SWHILE(DisasContext *ctx, arg_SWHILE *a) +{ + STRING(swhile); + return true; +} +/* sstr. */ +static bool trans_SSTR(DisasContext *ctx, arg_SSTR *a) +{ + STRING(sstr); + return true; +} + +/* rmpa. */ +static bool trans_RMPA(DisasContext *ctx, arg_RMPA *a) +{ + STRING(rmpa); + return true; +} + +static void rx_mul64hi(TCGv_i64 ret, int rs, int rs2) +{ + TCGv_i64 tmp0, tmp1; + tmp0 = tcg_temp_new_i64(); + tmp1 = tcg_temp_new_i64(); + tcg_gen_ext_i32_i64(tmp0, cpu_regs[rs]); + tcg_gen_sari_i64(tmp0, tmp0, 16); + tcg_gen_ext_i32_i64(tmp1, cpu_regs[rs2]); + tcg_gen_sari_i64(tmp1, tmp1, 16); + tcg_gen_mul_i64(ret, tmp0, tmp1); + tcg_gen_shli_i64(ret, ret, 16); + tcg_temp_free_i64(tmp0); + tcg_temp_free_i64(tmp1); +} + +static void rx_mul64lo(TCGv_i64 ret, int rs, int rs2) +{ + TCGv_i64 tmp0, tmp1; + tmp0 = tcg_temp_new_i64(); + tmp1 = tcg_temp_new_i64(); + tcg_gen_ext_i32_i64(tmp0, cpu_regs[rs]); + tcg_gen_ext16s_i64(tmp0, tmp0); + tcg_gen_ext_i32_i64(tmp1, cpu_regs[rs2]); + tcg_gen_ext16s_i64(tmp1, tmp1); + tcg_gen_mul_i64(ret, tmp0, tmp1); + tcg_gen_shli_i64(ret, ret, 16); + tcg_temp_free_i64(tmp0); + tcg_temp_free_i64(tmp1); +} + +/* mulhi rs,rs2 */ +static bool trans_MULHI(DisasContext *ctx, arg_MULHI *a) +{ + rx_mul64hi(cpu_acc, a->rs, a->rs2); + return true; +} + +/* mullo rs,rs2 */ +static bool trans_MULLO(DisasContext *ctx, arg_MULLO *a) +{ + rx_mul64lo(cpu_acc, a->rs, a->rs2); + return true; +} + +/* machi rs,rs2 */ +static bool trans_MACHI(DisasContext *ctx, arg_MACHI *a) +{ + TCGv_i64 tmp; + tmp = tcg_temp_new_i64(); + rx_mul64hi(tmp, a->rs, a->rs2); + tcg_gen_add_i64(cpu_acc, cpu_acc, tmp); + tcg_temp_free_i64(tmp); + return true; +} + +/* maclo rs,rs2 */ +static bool trans_MACLO(DisasContext *ctx, arg_MACLO *a) +{ + TCGv_i64 tmp; + tmp = tcg_temp_new_i64(); + rx_mul64lo(tmp, a->rs, a->rs2); + tcg_gen_add_i64(cpu_acc, cpu_acc, tmp); + tcg_temp_free_i64(tmp); + return true; +} + +/* mvfachi rd */ +static bool trans_MVFACHI(DisasContext *ctx, arg_MVFACHI *a) +{ + tcg_gen_extrh_i64_i32(cpu_regs[a->rd], cpu_acc); + return true; +} + +/* mvfacmi rd */ +static bool trans_MVFACMI(DisasContext *ctx, arg_MVFACMI *a) +{ + TCGv_i64 rd64; + rd64 = tcg_temp_new_i64(); + tcg_gen_extract_i64(rd64, cpu_acc, 16, 32); + tcg_gen_extrl_i64_i32(cpu_regs[a->rd], rd64); + tcg_temp_free_i64(rd64); + return true; +} + +/* mvtachi rs */ +static bool trans_MVTACHI(DisasContext *ctx, arg_MVTACHI *a) +{ + TCGv_i64 rs64; + rs64 = tcg_temp_new_i64(); + tcg_gen_extu_i32_i64(rs64, cpu_regs[a->rs]); + tcg_gen_deposit_i64(cpu_acc, cpu_acc, rs64, 32, 32); + tcg_temp_free_i64(rs64); + return true; +} + +/* mvtaclo rs */ +static bool trans_MVTACLO(DisasContext *ctx, arg_MVTACLO *a) +{ + TCGv_i64 rs64; + rs64 = tcg_temp_new_i64(); + tcg_gen_extu_i32_i64(rs64, cpu_regs[a->rs]); + tcg_gen_deposit_i64(cpu_acc, cpu_acc, rs64, 0, 32); + tcg_temp_free_i64(rs64); + return true; +} + +/* racw #imm */ +static bool trans_RACW(DisasContext *ctx, arg_RACW *a) +{ + TCGv imm = tcg_const_i32(a->imm + 1); + gen_helper_racw(cpu_env, imm); + tcg_temp_free(imm); + return true; +} + +/* sat rd */ +static bool trans_SAT(DisasContext *ctx, arg_SAT *a) +{ + TCGv tmp, z; + tmp = tcg_temp_new(); + z = tcg_const_i32(0); + /* S == 1 -> 0xffffffff / S == 0 -> 0x00000000 */ + tcg_gen_sari_i32(tmp, cpu_psw_s, 31); + /* S == 1 -> 0x7fffffff / S == 0 -> 0x80000000 */ + tcg_gen_xori_i32(tmp, tmp, 0x80000000); + tcg_gen_movcond_i32(TCG_COND_LT, cpu_regs[a->rd], + cpu_psw_o, z, tmp, cpu_regs[a->rd]); + tcg_temp_free(tmp); + tcg_temp_free(z); + return true; +} + +/* satr */ +static bool trans_SATR(DisasContext *ctx, arg_SATR *a) +{ + gen_helper_satr(cpu_env); + return true; +} + +#define cat3(a, b, c) a##b##c +#define FOP(name, op) \ + static bool cat3(trans_, name, _ir)(DisasContext *ctx, \ + cat3(arg_, name, _ir) * a) \ + { \ + TCGv imm = tcg_const_i32(li(ctx, 0)); \ + gen_helper_##op(cpu_regs[a->rd], cpu_env, \ + cpu_regs[a->rd], imm); \ + tcg_temp_free(imm); \ + return true; \ + } \ + static bool cat3(trans_, name, _mr)(DisasContext *ctx, \ + cat3(arg_, name, _mr) * a) \ + { \ + TCGv val, mem; \ + mem = tcg_temp_new(); \ + val = rx_load_source(ctx, mem, a->ld, MO_32, a->rs); \ + gen_helper_##op(cpu_regs[a->rd], cpu_env, \ + cpu_regs[a->rd], val); \ + tcg_temp_free(mem); \ + return true; \ + } + +#define FCONVOP(name, op) \ + static bool trans_##name(DisasContext *ctx, arg_##name * a) \ + { \ + TCGv val, mem; \ + mem = tcg_temp_new(); \ + val = rx_load_source(ctx, mem, a->ld, MO_32, a->rs); \ + gen_helper_##op(cpu_regs[a->rd], cpu_env, val); \ + tcg_temp_free(mem); \ + return true; \ + } + +FOP(FADD, fadd) +FOP(FSUB, fsub) +FOP(FMUL, fmul) +FOP(FDIV, fdiv) + +/* fcmp #imm, rd */ +static bool trans_FCMP_ir(DisasContext *ctx, arg_FCMP_ir * a) +{ + TCGv imm = tcg_const_i32(li(ctx, 0)); + gen_helper_fcmp(cpu_env, cpu_regs[a->rd], imm); + tcg_temp_free(imm); + return true; +} + +/* fcmp dsp[rs], rd */ +/* fcmp rs, rd */ +static bool trans_FCMP_mr(DisasContext *ctx, arg_FCMP_mr *a) +{ + TCGv val, mem; + mem = tcg_temp_new(); + val = rx_load_source(ctx, mem, a->ld, MO_32, a->rs); + gen_helper_fcmp(cpu_env, cpu_regs[a->rd], val); + tcg_temp_free(mem); + return true; +} + +FCONVOP(FTOI, ftoi) +FCONVOP(ROUND, round) + +/* itof rs, rd */ +/* itof dsp[rs], rd */ +static bool trans_ITOF(DisasContext *ctx, arg_ITOF * a) +{ + TCGv val, mem; + mem = tcg_temp_new(); + val = rx_load_source(ctx, mem, a->ld, a->mi, a->rs); + gen_helper_itof(cpu_regs[a->rd], cpu_env, val); + tcg_temp_free(mem); + return true; +} + +static void rx_bsetm(TCGv mem, TCGv mask) +{ + TCGv val; + val = tcg_temp_new(); + rx_gen_ld(MO_8, val, mem); + tcg_gen_or_i32(val, val, mask); + rx_gen_st(MO_8, val, mem); + tcg_temp_free(val); +} + +static void rx_bclrm(TCGv mem, TCGv mask) +{ + TCGv val; + val = tcg_temp_new(); + rx_gen_ld(MO_8, val, mem); + tcg_gen_andc_i32(val, val, mask); + rx_gen_st(MO_8, val, mem); + tcg_temp_free(val); +} + +static void rx_btstm(TCGv mem, TCGv mask) +{ + TCGv val; + val = tcg_temp_new(); + rx_gen_ld(MO_8, val, mem); + tcg_gen_and_i32(val, val, mask); + tcg_gen_setcondi_i32(TCG_COND_NE, cpu_psw_c, val, 0); + tcg_gen_mov_i32(cpu_psw_z, cpu_psw_c); + tcg_temp_free(val); +} + +static void rx_bnotm(TCGv mem, TCGv mask) +{ + TCGv val; + val = tcg_temp_new(); + rx_gen_ld(MO_8, val, mem); + tcg_gen_xor_i32(val, val, mask); + rx_gen_st(MO_8, val, mem); + tcg_temp_free(val); +} + +static void rx_bsetr(TCGv reg, TCGv mask) +{ + tcg_gen_or_i32(reg, reg, mask); +} + +static void rx_bclrr(TCGv reg, TCGv mask) +{ + tcg_gen_andc_i32(reg, reg, mask); +} + +static inline void rx_btstr(TCGv reg, TCGv mask) +{ + TCGv t0; + t0 = tcg_temp_new(); + tcg_gen_and_i32(t0, reg, mask); + tcg_gen_setcondi_i32(TCG_COND_NE, cpu_psw_c, t0, 0); + tcg_gen_mov_i32(cpu_psw_z, cpu_psw_c); + tcg_temp_free(t0); +} + +static inline void rx_bnotr(TCGv reg, TCGv mask) +{ + tcg_gen_xor_i32(reg, reg, mask); +} + +#define BITOP(name, op) \ + static bool cat3(trans_, name, _im)(DisasContext *ctx, \ + cat3(arg_, name, _im) * a) \ + { \ + TCGv mask, mem, addr; \ + mem = tcg_temp_new(); \ + mask = tcg_const_i32(1 << a->imm); \ + addr = rx_index_addr(ctx, mem, a->ld, MO_8, a->rs); \ + cat3(rx_, op, m)(addr, mask); \ + tcg_temp_free(mask); \ + tcg_temp_free(mem); \ + return true; \ + } \ + static bool cat3(trans_, name, _ir)(DisasContext *ctx, \ + cat3(arg_, name, _ir) * a) \ + { \ + TCGv mask; \ + mask = tcg_const_i32(1 << a->imm); \ + cat3(rx_, op, r)(cpu_regs[a->rd], mask); \ + tcg_temp_free(mask); \ + return true; \ + } \ + static bool cat3(trans_, name, _rr)(DisasContext *ctx, \ + cat3(arg_, name, _rr) * a) \ + { \ + TCGv mask, b; \ + mask = tcg_const_i32(1); \ + b = tcg_temp_new(); \ + tcg_gen_andi_i32(b, cpu_regs[a->rs], 31); \ + tcg_gen_shl_i32(mask, mask, b); \ + cat3(rx_, op, r)(cpu_regs[a->rd], mask); \ + tcg_temp_free(mask); \ + tcg_temp_free(b); \ + return true; \ + } \ + static bool cat3(trans_, name, _rm)(DisasContext *ctx, \ + cat3(arg_, name, _rm) * a) \ + { \ + TCGv mask, mem, addr, b; \ + mask = tcg_const_i32(1); \ + b = tcg_temp_new(); \ + tcg_gen_andi_i32(b, cpu_regs[a->rd], 7); \ + tcg_gen_shl_i32(mask, mask, b); \ + mem = tcg_temp_new(); \ + addr = rx_index_addr(ctx, mem, a->ld, MO_8, a->rs); \ + cat3(rx_, op, m)(addr, mask); \ + tcg_temp_free(mem); \ + tcg_temp_free(mask); \ + tcg_temp_free(b); \ + return true; \ + } + +BITOP(BSET, bset) +BITOP(BCLR, bclr) +BITOP(BTST, btst) +BITOP(BNOT, bnot) + +static inline void bmcnd_op(TCGv val, TCGCond cond, int pos) +{ + TCGv bit; + DisasCompare dc; + dc.temp = tcg_temp_new(); + bit = tcg_temp_new(); + psw_cond(&dc, cond); + tcg_gen_andi_i32(val, val, ~(1 << pos)); + tcg_gen_setcondi_i32(dc.cond, bit, dc.value, 0); + tcg_gen_deposit_i32(val, val, bit, pos, 1); + tcg_temp_free(bit); + tcg_temp_free(dc.temp); + } + +/* bmcnd #imm, dsp[rd] */ +static bool trans_BMCnd_im(DisasContext *ctx, arg_BMCnd_im *a) +{ + TCGv val, mem, addr; + val = tcg_temp_new(); + mem = tcg_temp_new(); + addr = rx_index_addr(ctx, mem, a->ld, MO_8, a->rd); + rx_gen_ld(MO_8, val, addr); + bmcnd_op(val, a->cd, a->imm); + rx_gen_st(MO_8, val, addr); + tcg_temp_free(val); + tcg_temp_free(mem); + return true; +} + +/* bmcond #imm, rd */ +static bool trans_BMCnd_ir(DisasContext *ctx, arg_BMCnd_ir *a) +{ + bmcnd_op(cpu_regs[a->rd], a->cd, a->imm); + return true; +} + +enum { + PSW_C = 0, + PSW_Z = 1, + PSW_S = 2, + PSW_O = 3, + PSW_I = 8, + PSW_U = 9, +}; + +static inline void clrsetpsw(DisasContext *ctx, int cb, int val) +{ + if (cb < 8) { + switch (cb) { + case PSW_C: + tcg_gen_movi_i32(cpu_psw_c, val); + break; + case PSW_Z: + tcg_gen_movi_i32(cpu_psw_z, val == 0); + break; + case PSW_S: + tcg_gen_movi_i32(cpu_psw_s, val ? -1 : 0); + break; + case PSW_O: + tcg_gen_movi_i32(cpu_psw_o, val << 31); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "Invalid distination %d", cb); + break; + } + } else if (is_privileged(ctx, 0)) { + switch (cb) { + case PSW_I: + tcg_gen_movi_i32(cpu_psw_i, val); + ctx->base.is_jmp = DISAS_UPDATE; + break; + case PSW_U: + tcg_gen_movi_i32(cpu_psw_u, val); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "Invalid distination %d", cb); + break; + } + } +} + +/* clrpsw psw */ +static bool trans_CLRPSW(DisasContext *ctx, arg_CLRPSW *a) +{ + clrsetpsw(ctx, a->cb, 0); + return true; +} + +/* setpsw psw */ +static bool trans_SETPSW(DisasContext *ctx, arg_SETPSW *a) +{ + clrsetpsw(ctx, a->cb, 1); + return true; +} + +/* mvtipl #imm */ +static bool trans_MVTIPL(DisasContext *ctx, arg_MVTIPL *a) +{ + if (is_privileged(ctx, 1)) { + tcg_gen_movi_i32(cpu_psw_ipl, a->imm); + ctx->base.is_jmp = DISAS_UPDATE; + } + return true; +} + +/* mvtc #imm, rd */ +static bool trans_MVTC_i(DisasContext *ctx, arg_MVTC_i *a) +{ + TCGv imm; + + imm = tcg_const_i32(a->imm); + move_to_cr(ctx, imm, a->cr); + if (a->cr == 0 && is_privileged(ctx, 0)) { + ctx->base.is_jmp = DISAS_UPDATE; + } + tcg_temp_free(imm); + return true; +} + +/* mvtc rs, rd */ +static bool trans_MVTC_r(DisasContext *ctx, arg_MVTC_r *a) +{ + move_to_cr(ctx, cpu_regs[a->rs], a->cr); + if (a->cr == 0 && is_privileged(ctx, 0)) { + ctx->base.is_jmp = DISAS_UPDATE; + } + return true; +} + +/* mvfc rs, rd */ +static bool trans_MVFC(DisasContext *ctx, arg_MVFC *a) +{ + move_from_cr(cpu_regs[a->rd], a->cr, ctx->pc); + return true; +} + +/* rtfi */ +static bool trans_RTFI(DisasContext *ctx, arg_RTFI *a) +{ + TCGv psw; + if (is_privileged(ctx, 1)) { + psw = tcg_temp_new(); + tcg_gen_mov_i32(cpu_pc, cpu_bpc); + tcg_gen_mov_i32(psw, cpu_bpsw); + gen_helper_set_psw_rte(cpu_env, psw); + ctx->base.is_jmp = DISAS_EXIT; + tcg_temp_free(psw); + } + return true; +} + +/* rte */ +static bool trans_RTE(DisasContext *ctx, arg_RTE *a) +{ + TCGv psw; + if (is_privileged(ctx, 1)) { + psw = tcg_temp_new(); + pop(cpu_pc); + pop(psw); + gen_helper_set_psw_rte(cpu_env, psw); + ctx->base.is_jmp = DISAS_EXIT; + tcg_temp_free(psw); + } + return true; +} + +/* brk */ +static bool trans_BRK(DisasContext *ctx, arg_BRK *a) +{ + tcg_gen_movi_i32(cpu_pc, ctx->base.pc_next); + gen_helper_rxbrk(cpu_env); + ctx->base.is_jmp = DISAS_NORETURN; + return true; +} + +/* int #imm */ +static bool trans_INT(DisasContext *ctx, arg_INT *a) +{ + TCGv vec; + + tcg_debug_assert(a->imm < 0x100); + vec = tcg_const_i32(a->imm); + tcg_gen_movi_i32(cpu_pc, ctx->base.pc_next); + gen_helper_rxint(cpu_env, vec); + tcg_temp_free(vec); + ctx->base.is_jmp = DISAS_NORETURN; + return true; +} + +/* wait */ +static bool trans_WAIT(DisasContext *ctx, arg_WAIT *a) +{ + if (is_privileged(ctx, 1)) { + tcg_gen_addi_i32(cpu_pc, cpu_pc, 2); + gen_helper_wait(cpu_env); + } + return true; +} + +static void rx_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) +{ + CPURXState *env = cs->env_ptr; + DisasContext *ctx = container_of(dcbase, DisasContext, base); + ctx->env = env; +} + +static void rx_tr_tb_start(DisasContextBase *dcbase, CPUState *cs) +{ +} + +static void rx_tr_insn_start(DisasContextBase *dcbase, CPUState *cs) +{ + DisasContext *ctx = container_of(dcbase, DisasContext, base); + + tcg_gen_insn_start(ctx->base.pc_next); +} + +static bool rx_tr_breakpoint_check(DisasContextBase *dcbase, CPUState *cs, + const CPUBreakpoint *bp) +{ + DisasContext *ctx = container_of(dcbase, DisasContext, base); + + /* We have hit a breakpoint - make sure PC is up-to-date */ + tcg_gen_movi_i32(cpu_pc, ctx->base.pc_next); + gen_helper_debug(cpu_env); + ctx->base.is_jmp = DISAS_NORETURN; + ctx->base.pc_next += 1; + return true; +} + +static void rx_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) +{ + DisasContext *ctx = container_of(dcbase, DisasContext, base); + uint32_t insn; + + ctx->pc = ctx->base.pc_next; + insn = decode_load(ctx); + if (!decode(ctx, insn)) { + gen_helper_raise_illegal_instruction(cpu_env); + } +} + +static void rx_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) +{ + DisasContext *ctx = container_of(dcbase, DisasContext, base); + + switch (ctx->base.is_jmp) { + case DISAS_NEXT: + case DISAS_TOO_MANY: + gen_goto_tb(ctx, 0, dcbase->pc_next); + break; + case DISAS_JUMP: + if (ctx->base.singlestep_enabled) { + gen_helper_debug(cpu_env); + } else { + tcg_gen_lookup_and_goto_ptr(); + } + break; + case DISAS_UPDATE: + tcg_gen_movi_i32(cpu_pc, ctx->base.pc_next); + /* fall through */ + case DISAS_EXIT: + tcg_gen_exit_tb(NULL, 0); + break; + case DISAS_NORETURN: + break; + default: + g_assert_not_reached(); + } +} + +static void rx_tr_disas_log(const DisasContextBase *dcbase, CPUState *cs) +{ + qemu_log("IN:\n"); /* , lookup_symbol(dcbase->pc_first)); */ + log_target_disas(cs, dcbase->pc_first, dcbase->tb->size); +} + +static const TranslatorOps rx_tr_ops = { + .init_disas_context = rx_tr_init_disas_context, + .tb_start = rx_tr_tb_start, + .insn_start = rx_tr_insn_start, + .breakpoint_check = rx_tr_breakpoint_check, + .translate_insn = rx_tr_translate_insn, + .tb_stop = rx_tr_tb_stop, + .disas_log = rx_tr_disas_log, +}; + +void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int max_insns) +{ + DisasContext dc; + + translator_loop(&rx_tr_ops, &dc.base, cs, tb, max_insns); +} + +void restore_state_to_opc(CPURXState *env, TranslationBlock *tb, + target_ulong *data) +{ + env->pc = data[0]; +} + +#define ALLOC_REGISTER(sym, name) \ + cpu_##sym = tcg_global_mem_new_i32(cpu_env, \ + offsetof(CPURXState, sym), name) + +void rx_translate_init(void) +{ + static const char * const regnames[NUM_REGS] = { + "R0", "R1", "R2", "R3", "R4", "R5", "R6", "R7", + "R8", "R9", "R10", "R11", "R12", "R13", "R14", "R15" + }; + int i; + + for (i = 0; i < NUM_REGS; i++) { + cpu_regs[i] = tcg_global_mem_new_i32(cpu_env, + offsetof(CPURXState, regs[i]), + regnames[i]); + } + ALLOC_REGISTER(pc, "PC"); + ALLOC_REGISTER(psw_o, "PSW(O)"); + ALLOC_REGISTER(psw_s, "PSW(S)"); + ALLOC_REGISTER(psw_z, "PSW(Z)"); + ALLOC_REGISTER(psw_c, "PSW(C)"); + ALLOC_REGISTER(psw_u, "PSW(U)"); + ALLOC_REGISTER(psw_i, "PSW(I)"); + ALLOC_REGISTER(psw_pm, "PSW(PM)"); + ALLOC_REGISTER(psw_ipl, "PSW(IPL)"); + ALLOC_REGISTER(usp, "USP"); + ALLOC_REGISTER(fpsw, "FPSW"); + ALLOC_REGISTER(bpsw, "BPSW"); + ALLOC_REGISTER(bpc, "BPC"); + ALLOC_REGISTER(isp, "ISP"); + ALLOC_REGISTER(fintv, "FINTV"); + ALLOC_REGISTER(intb, "INTB"); + cpu_acc = tcg_global_mem_new_i64(cpu_env, + offsetof(CPURXState, acc), "ACC"); +} diff --git a/target/s390x/cpu-qom.h b/target/s390x/cpu-qom.h index b809ec8418..1630818c28 100644 --- a/target/s390x/cpu-qom.h +++ b/target/s390x/cpu-qom.h @@ -34,6 +34,12 @@ typedef struct S390CPUModel S390CPUModel; typedef struct S390CPUDef S390CPUDef; +typedef enum cpu_reset_type { + S390_CPU_RESET_NORMAL, + S390_CPU_RESET_INITIAL, + S390_CPU_RESET_CLEAR, +} cpu_reset_type; + /** * S390CPUClass: * @parent_realize: The parent class' realize handler. @@ -55,10 +61,9 @@ typedef struct S390CPUClass { const char *desc; DeviceRealize parent_realize; - void (*parent_reset)(CPUState *cpu); + DeviceReset parent_reset; void (*load_normal)(CPUState *cpu); - void (*cpu_reset)(CPUState *cpu); - void (*initial_cpu_reset)(CPUState *cpu); + void (*reset)(CPUState *cpu, cpu_reset_type type); } S390CPUClass; typedef struct S390CPU S390CPU; diff --git a/target/s390x/cpu.c b/target/s390x/cpu.c index 3abe7e80fd..427a46e3e1 100644 --- a/target/s390x/cpu.c +++ b/target/s390x/cpu.c @@ -76,94 +76,87 @@ static bool s390_cpu_has_work(CPUState *cs) static void s390_cpu_load_normal(CPUState *s) { S390CPU *cpu = S390_CPU(s); - cpu->env.psw.addr = ldl_phys(s->as, 4) & PSW_MASK_ESA_ADDR; - cpu->env.psw.mask = PSW_MASK_32 | PSW_MASK_64; + uint64_t spsw = ldq_phys(s->as, 0); + + cpu->env.psw.mask = spsw & PSW_MASK_SHORT_CTRL; + /* + * Invert short psw indication, so SIE will report a specification + * exception if it was not set. + */ + cpu->env.psw.mask ^= PSW_MASK_SHORTPSW; + cpu->env.psw.addr = spsw & PSW_MASK_SHORT_ADDR; + s390_cpu_set_state(S390_CPU_STATE_OPERATING, cpu); } #endif -/* S390CPUClass::cpu_reset() */ -static void s390_cpu_reset(CPUState *s) +/* S390CPUClass::reset() */ +static void s390_cpu_reset(CPUState *s, cpu_reset_type type) { S390CPU *cpu = S390_CPU(s); S390CPUClass *scc = S390_CPU_GET_CLASS(cpu); CPUS390XState *env = &cpu->env; + DeviceState *dev = DEVICE(s); - env->pfault_token = -1UL; - env->bpbc = false; - scc->parent_reset(s); - cpu->env.sigp_order = 0; - s390_cpu_set_state(S390_CPU_STATE_STOPPED, cpu); -} - -/* S390CPUClass::initial_reset() */ -static void s390_cpu_initial_reset(CPUState *s) -{ - S390CPU *cpu = S390_CPU(s); - CPUS390XState *env = &cpu->env; - - s390_cpu_reset(s); - /* initial reset does not clear everything! */ - memset(&env->start_initial_reset_fields, 0, - offsetof(CPUS390XState, end_reset_fields) - - offsetof(CPUS390XState, start_initial_reset_fields)); - - /* architectured initial values for CR 0 and 14 */ - env->cregs[0] = CR0_RESET; - env->cregs[14] = CR14_RESET; - - /* architectured initial value for Breaking-Event-Address register */ - env->gbea = 1; - - env->pfault_token = -1UL; - - /* tininess for underflow is detected before rounding */ - set_float_detect_tininess(float_tininess_before_rounding, - &env->fpu_status); - - /* Reset state inside the kernel that we cannot access yet from QEMU. */ - if (kvm_enabled()) { - kvm_s390_reset_vcpu(cpu); - } -} - -/* CPUClass:reset() */ -static void s390_cpu_full_reset(CPUState *s) -{ - S390CPU *cpu = S390_CPU(s); - S390CPUClass *scc = S390_CPU_GET_CLASS(cpu); - CPUS390XState *env = &cpu->env; - - scc->parent_reset(s); + scc->parent_reset(dev); cpu->env.sigp_order = 0; s390_cpu_set_state(S390_CPU_STATE_STOPPED, cpu); - memset(env, 0, offsetof(CPUS390XState, end_reset_fields)); + switch (type) { + case S390_CPU_RESET_CLEAR: + memset(env, 0, offsetof(CPUS390XState, start_initial_reset_fields)); + /* fall through */ + case S390_CPU_RESET_INITIAL: + /* initial reset does not clear everything! */ + memset(&env->start_initial_reset_fields, 0, + offsetof(CPUS390XState, start_normal_reset_fields) - + offsetof(CPUS390XState, start_initial_reset_fields)); - /* architectured initial values for CR 0 and 14 */ - env->cregs[0] = CR0_RESET; - env->cregs[14] = CR14_RESET; + /* architectured initial value for Breaking-Event-Address register */ + env->gbea = 1; + + /* architectured initial values for CR 0 and 14 */ + env->cregs[0] = CR0_RESET; + env->cregs[14] = CR14_RESET; #if defined(CONFIG_USER_ONLY) - /* user mode should always be allowed to use the full FPU */ - env->cregs[0] |= CR0_AFP; - if (s390_has_feat(S390_FEAT_VECTOR)) { - env->cregs[0] |= CR0_VECTOR; - } + /* user mode should always be allowed to use the full FPU */ + env->cregs[0] |= CR0_AFP; + if (s390_has_feat(S390_FEAT_VECTOR)) { + env->cregs[0] |= CR0_VECTOR; + } #endif - /* architectured initial value for Breaking-Event-Address register */ - env->gbea = 1; + /* tininess for underflow is detected before rounding */ + set_float_detect_tininess(float_tininess_before_rounding, + &env->fpu_status); + /* fall through */ + case S390_CPU_RESET_NORMAL: + env->psw.mask &= ~PSW_MASK_RI; + memset(&env->start_normal_reset_fields, 0, + offsetof(CPUS390XState, end_reset_fields) - + offsetof(CPUS390XState, start_normal_reset_fields)); - env->pfault_token = -1UL; - - /* tininess for underflow is detected before rounding */ - set_float_detect_tininess(float_tininess_before_rounding, - &env->fpu_status); + env->pfault_token = -1UL; + env->bpbc = false; + break; + default: + g_assert_not_reached(); + } /* Reset state inside the kernel that we cannot access yet from QEMU. */ if (kvm_enabled()) { - kvm_s390_reset_vcpu(cpu); + switch (type) { + case S390_CPU_RESET_CLEAR: + kvm_s390_reset_vcpu_clear(cpu); + break; + case S390_CPU_RESET_INITIAL: + kvm_s390_reset_vcpu_initial(cpu); + break; + case S390_CPU_RESET_NORMAL: + kvm_s390_reset_vcpu_normal(cpu); + break; + } } } @@ -458,6 +451,12 @@ static Property s390x_cpu_properties[] = { DEFINE_PROP_END_OF_LIST() }; +static void s390_cpu_reset_full(DeviceState *dev) +{ + CPUState *s = CPU(dev); + return s390_cpu_reset(s, S390_CPU_RESET_CLEAR); +} + static void s390_cpu_class_init(ObjectClass *oc, void *data) { S390CPUClass *scc = S390_CPU_CLASS(oc); @@ -466,16 +465,14 @@ static void s390_cpu_class_init(ObjectClass *oc, void *data) device_class_set_parent_realize(dc, s390_cpu_realizefn, &scc->parent_realize); - dc->props = s390x_cpu_properties; + device_class_set_props(dc, s390x_cpu_properties); dc->user_creatable = true; - scc->parent_reset = cc->reset; + device_class_set_parent_reset(dc, s390_cpu_reset_full, &scc->parent_reset); #if !defined(CONFIG_USER_ONLY) scc->load_normal = s390_cpu_load_normal; #endif - scc->cpu_reset = s390_cpu_reset; - scc->initial_cpu_reset = s390_cpu_initial_reset; - cc->reset = s390_cpu_full_reset; + scc->reset = s390_cpu_reset; cc->class_by_name = s390_cpu_class_by_name, cc->has_work = s390_cpu_has_work; #ifdef CONFIG_TCG diff --git a/target/s390x/cpu.h b/target/s390x/cpu.h index 17460ed7b3..1d17709d6e 100644 --- a/target/s390x/cpu.h +++ b/target/s390x/cpu.h @@ -36,11 +36,6 @@ #define TARGET_INSN_START_EXTRA_WORDS 2 -#define MMU_MODE0_SUFFIX _primary -#define MMU_MODE1_SUFFIX _secondary -#define MMU_MODE2_SUFFIX _home -#define MMU_MODE3_SUFFIX _real - #define MMU_USER_IDX 0 #define S390_MAX_CPUS 248 @@ -58,7 +53,6 @@ struct CPUS390XState { */ uint64_t vregs[32][2] QEMU_ALIGNED(16); /* vector registers */ uint32_t aregs[16]; /* access registers */ - uint8_t riccb[64]; /* runtime instrumentation control */ uint64_t gscb[4]; /* guarded storage control */ uint64_t etoken; /* etoken */ uint64_t etoken_extension; /* etoken extension */ @@ -99,10 +93,6 @@ struct CPUS390XState { uint64_t cregs[16]; /* control registers */ - int pending_int; - uint16_t external_call_addr; - DECLARE_BITMAP(emergency_signals, S390_MAX_CPUS); - uint64_t ckc; uint64_t cputm; uint32_t todpr; @@ -114,6 +104,14 @@ struct CPUS390XState { uint64_t gbea; uint64_t pp; + /* Fields up to this point are not cleared by normal CPU reset */ + struct {} start_normal_reset_fields; + uint8_t riccb[64]; /* runtime instrumentation control */ + + int pending_int; + uint16_t external_call_addr; + DECLARE_BITMAP(emergency_signals, S390_MAX_CPUS); + /* Fields up to this point are cleared by a CPU reset */ struct {} end_reset_fields; @@ -252,6 +250,7 @@ extern const VMStateDescription vmstate_s390_cpu; #undef PSW_SHIFT_ASC #undef PSW_MASK_CC #undef PSW_MASK_PM +#undef PSW_MASK_RI #undef PSW_SHIFT_MASK_PM #undef PSW_MASK_64 #undef PSW_MASK_32 @@ -265,6 +264,7 @@ extern const VMStateDescription vmstate_s390_cpu; #define PSW_MASK_EXT 0x0100000000000000ULL #define PSW_MASK_KEY 0x00F0000000000000ULL #define PSW_SHIFT_KEY 52 +#define PSW_MASK_SHORTPSW 0x0008000000000000ULL #define PSW_MASK_MCHECK 0x0004000000000000ULL #define PSW_MASK_WAIT 0x0002000000000000ULL #define PSW_MASK_PSTATE 0x0001000000000000ULL @@ -273,9 +273,11 @@ extern const VMStateDescription vmstate_s390_cpu; #define PSW_MASK_CC 0x0000300000000000ULL #define PSW_MASK_PM 0x00000F0000000000ULL #define PSW_SHIFT_MASK_PM 40 +#define PSW_MASK_RI 0x0000008000000000ULL #define PSW_MASK_64 0x0000000100000000ULL #define PSW_MASK_32 0x0000000080000000ULL -#define PSW_MASK_ESA_ADDR 0x000000007fffffffULL +#define PSW_MASK_SHORT_ADDR 0x000000007fffffffULL +#define PSW_MASK_SHORT_CTRL 0xffffffff80000000ULL #undef PSW_ASC_PRIMARY #undef PSW_ASC_ACCREG @@ -741,14 +743,14 @@ static inline void s390_do_cpu_reset(CPUState *cs, run_on_cpu_data arg) { S390CPUClass *scc = S390_CPU_GET_CLASS(cs); - scc->cpu_reset(cs); + scc->reset(cs, S390_CPU_RESET_NORMAL); } static inline void s390_do_cpu_initial_reset(CPUState *cs, run_on_cpu_data arg) { S390CPUClass *scc = S390_CPU_GET_CLASS(cs); - scc->initial_cpu_reset(cs); + scc->reset(cs, S390_CPU_RESET_INITIAL); } static inline void s390_do_cpu_load_normal(CPUState *cs, run_on_cpu_data arg) diff --git a/target/s390x/cpu_models.c b/target/s390x/cpu_models.c index 7e92fb2e15..7c32180269 100644 --- a/target/s390x/cpu_models.c +++ b/target/s390x/cpu_models.c @@ -440,7 +440,7 @@ static void create_cpu_model_list(ObjectClass *klass, void *opaque) if (cpu_list_data->model) { Object *obj; S390CPU *sc; - obj = object_new(object_class_get_name(klass)); + obj = object_new_with_class(klass); sc = S390_CPU(obj); if (sc->model) { info->has_unavailable_features = true; @@ -462,11 +462,7 @@ CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp) .list = NULL, }; - list_data.model = get_max_cpu_model(errp); - if (*errp) { - error_free(*errp); - *errp = NULL; - } + list_data.model = get_max_cpu_model(NULL); object_class_foreach(create_cpu_model_list, TYPE_S390_CPU, false, &list_data); @@ -477,6 +473,7 @@ CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp) static void cpu_model_from_info(S390CPUModel *model, const CpuModelInfo *info, Error **errp) { + Error *err = NULL; const QDict *qdict = NULL; const QDictEntry *e; Visitor *visitor; @@ -501,7 +498,7 @@ static void cpu_model_from_info(S390CPUModel *model, const CpuModelInfo *info, error_setg(errp, "The CPU definition '%s' requires KVM", info->name); return; } - obj = object_new(object_class_get_name(oc)); + obj = object_new_with_class(oc); cpu = S390_CPU(obj); if (!cpu->model) { @@ -513,24 +510,26 @@ static void cpu_model_from_info(S390CPUModel *model, const CpuModelInfo *info, if (qdict) { visitor = qobject_input_visitor_new(info->props); - visit_start_struct(visitor, NULL, NULL, 0, errp); - if (*errp) { + visit_start_struct(visitor, NULL, NULL, 0, &err); + if (err) { + error_propagate(errp, err); visit_free(visitor); object_unref(obj); return; } for (e = qdict_first(qdict); e; e = qdict_next(qdict, e)) { - object_property_set(obj, visitor, e->key, errp); - if (*errp) { + object_property_set(obj, visitor, e->key, &err); + if (err) { break; } } - if (!*errp) { + if (!err) { visit_check_struct(visitor, errp); } visit_end_struct(visitor, NULL); visit_free(visitor); - if (*errp) { + if (err) { + error_propagate(errp, err); object_unref(obj); return; } @@ -595,13 +594,15 @@ CpuModelExpansionInfo *qmp_query_cpu_model_expansion(CpuModelExpansionType type, CpuModelInfo *model, Error **errp) { + Error *err = NULL; CpuModelExpansionInfo *expansion_info = NULL; S390CPUModel s390_model; bool delta_changes = false; /* convert it to our internal representation */ - cpu_model_from_info(&s390_model, model, errp); - if (*errp) { + cpu_model_from_info(&s390_model, model, &err); + if (err) { + error_propagate(errp, err); return NULL; } @@ -634,18 +635,21 @@ CpuModelCompareInfo *qmp_query_cpu_model_comparison(CpuModelInfo *infoa, CpuModelInfo *infob, Error **errp) { + Error *err = NULL; CpuModelCompareResult feat_result, gen_result; CpuModelCompareInfo *compare_info; S390FeatBitmap missing, added; S390CPUModel modela, modelb; /* convert both models to our internal representation */ - cpu_model_from_info(&modela, infoa, errp); - if (*errp) { + cpu_model_from_info(&modela, infoa, &err); + if (err) { + error_propagate(errp, err); return NULL; } - cpu_model_from_info(&modelb, infob, errp); - if (*errp) { + cpu_model_from_info(&modelb, infob, &err); + if (err) { + error_propagate(errp, err); return NULL; } compare_info = g_new0(CpuModelCompareInfo, 1); @@ -707,6 +711,7 @@ CpuModelBaselineInfo *qmp_query_cpu_model_baseline(CpuModelInfo *infoa, CpuModelInfo *infob, Error **errp) { + Error *err = NULL; CpuModelBaselineInfo *baseline_info; S390CPUModel modela, modelb, model; uint16_t cpu_type; @@ -714,13 +719,15 @@ CpuModelBaselineInfo *qmp_query_cpu_model_baseline(CpuModelInfo *infoa, uint8_t max_gen; /* convert both models to our internal representation */ - cpu_model_from_info(&modela, infoa, errp); - if (*errp) { + cpu_model_from_info(&modela, infoa, &err); + if (err) { + error_propagate(errp, err); return NULL; } - cpu_model_from_info(&modelb, infob, errp); - if (*errp) { + cpu_model_from_info(&modelb, infob, &err); + if (err) { + error_propagate(errp, err); return NULL; } @@ -870,6 +877,7 @@ static void check_compatibility(const S390CPUModel *max_model, static S390CPUModel *get_max_cpu_model(Error **errp) { + Error *err = NULL; static S390CPUModel max_model; static bool cached; @@ -878,22 +886,24 @@ static S390CPUModel *get_max_cpu_model(Error **errp) } if (kvm_enabled()) { - kvm_s390_get_host_cpu_model(&max_model, errp); + kvm_s390_get_host_cpu_model(&max_model, &err); } else { max_model.def = s390_find_cpu_def(QEMU_MAX_CPU_TYPE, QEMU_MAX_CPU_GEN, QEMU_MAX_CPU_EC_GA, NULL); bitmap_copy(max_model.features, qemu_max_cpu_feat, S390_FEAT_MAX); - } - if (!*errp) { - cached = true; - return &max_model; } - return NULL; + if (err) { + error_propagate(errp, err); + return NULL; + } + cached = true; + return &max_model; } static inline void apply_cpu_model(const S390CPUModel *model, Error **errp) { #ifndef CONFIG_USER_ONLY + Error *err = NULL; static S390CPUModel applied_model; static bool applied; @@ -909,20 +919,23 @@ static inline void apply_cpu_model(const S390CPUModel *model, Error **errp) } if (kvm_enabled()) { - kvm_s390_apply_cpu_model(model, errp); + kvm_s390_apply_cpu_model(model, &err); + if (err) { + error_propagate(errp, err); + return; + } } - if (!*errp) { - applied = true; - if (model) { - applied_model = *model; - } + applied = true; + if (model) { + applied_model = *model; } #endif } void s390_realize_cpu_model(CPUState *cs, Error **errp) { + Error *err = NULL; S390CPUClass *xcc = S390_CPU_GET_CLASS(cs); S390CPU *cpu = S390_CPU(cs); const S390CPUModel *max_model; @@ -939,7 +952,7 @@ void s390_realize_cpu_model(CPUState *cs, Error **errp) } max_model = get_max_cpu_model(errp); - if (*errp) { + if (!max_model) { error_prepend(errp, "CPU models are not available: "); return; } @@ -951,8 +964,9 @@ void s390_realize_cpu_model(CPUState *cs, Error **errp) cpu->model->cpu_ver = max_model->cpu_ver; check_consistency(cpu->model); - check_compatibility(max_model, cpu->model, errp); - if (*errp) { + check_compatibility(max_model, cpu->model, &err); + if (err) { + error_propagate(errp, err); return; } @@ -987,6 +1001,7 @@ static void get_feature(Object *obj, Visitor *v, const char *name, static void set_feature(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { + Error *err = NULL; S390Feat feat = (S390Feat) opaque; DeviceState *dev = DEVICE(obj); S390CPU *cpu = S390_CPU(obj); @@ -1002,8 +1017,9 @@ static void set_feature(Object *obj, Visitor *v, const char *name, return; } - visit_type_bool(v, name, &value, errp); - if (*errp) { + visit_type_bool(v, name, &value, &err); + if (err) { + error_propagate(errp, err); return; } if (value) { @@ -1043,6 +1059,7 @@ static void get_feature_group(Object *obj, Visitor *v, const char *name, static void set_feature_group(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { + Error *err = NULL; S390FeatGroup group = (S390FeatGroup) opaque; const S390FeatGroupDef *def = s390_feat_group_def(group); DeviceState *dev = DEVICE(obj); @@ -1059,8 +1076,9 @@ static void set_feature_group(Object *obj, Visitor *v, const char *name, return; } - visit_type_bool(v, name, &value, errp); - if (*errp) { + visit_type_bool(v, name, &value, &err); + if (err) { + error_propagate(errp, err); return; } if (value) { diff --git a/target/s390x/diag.c b/target/s390x/diag.c index 53c2f81f2a..54e5670b3f 100644 --- a/target/s390x/diag.c +++ b/target/s390x/diag.c @@ -53,6 +53,29 @@ int handle_diag_288(CPUS390XState *env, uint64_t r1, uint64_t r3) #define DIAG_308_RC_NO_CONF 0x0102 #define DIAG_308_RC_INVALID 0x0402 +#define DIAG308_RESET_MOD_CLR 0 +#define DIAG308_RESET_LOAD_NORM 1 +#define DIAG308_LOAD_CLEAR 3 +#define DIAG308_LOAD_NORMAL_DUMP 4 +#define DIAG308_SET 5 +#define DIAG308_STORE 6 + +static int diag308_parm_check(CPUS390XState *env, uint64_t r1, uint64_t addr, + uintptr_t ra, bool write) +{ + if ((r1 & 1) || (addr & ~TARGET_PAGE_MASK)) { + s390_program_interrupt(env, PGM_SPECIFICATION, ra); + return -1; + } + if (!address_space_access_valid(&address_space_memory, addr, + sizeof(IplParameterBlock), write, + MEMTXATTRS_UNSPECIFIED)) { + s390_program_interrupt(env, PGM_ADDRESSING, ra); + return -1; + } + return 0; +} + void handle_diag_308(CPUS390XState *env, uint64_t r1, uint64_t r3, uintptr_t ra) { CPUState *cs = env_cpu(env); @@ -65,30 +88,24 @@ void handle_diag_308(CPUS390XState *env, uint64_t r1, uint64_t r3, uintptr_t ra) return; } - if ((subcode & ~0x0ffffULL) || (subcode > 6)) { + if (subcode & ~0x0ffffULL) { s390_program_interrupt(env, PGM_SPECIFICATION, ra); return; } switch (subcode) { - case 0: + case DIAG308_RESET_MOD_CLR: s390_ipl_reset_request(cs, S390_RESET_MODIFIED_CLEAR); break; - case 1: + case DIAG308_RESET_LOAD_NORM: s390_ipl_reset_request(cs, S390_RESET_LOAD_NORMAL); break; - case 3: + case DIAG308_LOAD_CLEAR: + /* Well we still lack the clearing bit... */ s390_ipl_reset_request(cs, S390_RESET_REIPL); break; - case 5: - if ((r1 & 1) || (addr & 0x0fffULL)) { - s390_program_interrupt(env, PGM_SPECIFICATION, ra); - return; - } - if (!address_space_access_valid(&address_space_memory, addr, - sizeof(IplParameterBlock), false, - MEMTXATTRS_UNSPECIFIED)) { - s390_program_interrupt(env, PGM_ADDRESSING, ra); + case DIAG308_SET: + if (diag308_parm_check(env, r1, addr, ra, false)) { return; } iplb = g_new0(IplParameterBlock, 1); @@ -100,7 +117,7 @@ void handle_diag_308(CPUS390XState *env, uint64_t r1, uint64_t r3, uintptr_t ra) cpu_physical_memory_read(addr, iplb, be32_to_cpu(iplb->len)); - if (!iplb_valid_ccw(iplb) && !iplb_valid_fcp(iplb)) { + if (!iplb_valid(iplb)) { env->regs[r1 + 1] = DIAG_308_RC_INVALID; goto out; } @@ -110,15 +127,8 @@ void handle_diag_308(CPUS390XState *env, uint64_t r1, uint64_t r3, uintptr_t ra) out: g_free(iplb); return; - case 6: - if ((r1 & 1) || (addr & 0x0fffULL)) { - s390_program_interrupt(env, PGM_SPECIFICATION, ra); - return; - } - if (!address_space_access_valid(&address_space_memory, addr, - sizeof(IplParameterBlock), true, - MEMTXATTRS_UNSPECIFIED)) { - s390_program_interrupt(env, PGM_ADDRESSING, ra); + case DIAG308_STORE: + if (diag308_parm_check(env, r1, addr, ra, true)) { return; } iplb = s390_ipl_get_iplb(); diff --git a/target/s390x/excp_helper.c b/target/s390x/excp_helper.c index e70c20d363..3b58d10df3 100644 --- a/target/s390x/excp_helper.c +++ b/target/s390x/excp_helper.c @@ -305,15 +305,14 @@ static void do_ext_interrupt(CPUS390XState *env) if ((env->pending_int & INTERRUPT_EMERGENCY_SIGNAL) && (env->cregs[0] & CR0_EMERGENCY_SIGNAL_SC)) { + MachineState *ms = MACHINE(qdev_get_machine()); + unsigned int max_cpus = ms->smp.max_cpus; + lowcore->ext_int_code = cpu_to_be16(EXT_EMERGENCY); cpu_addr = find_first_bit(env->emergency_signals, S390_MAX_CPUS); g_assert(cpu_addr < S390_MAX_CPUS); lowcore->cpu_addr = cpu_to_be16(cpu_addr); clear_bit(cpu_addr, env->emergency_signals); -#ifndef CONFIG_USER_ONLY - MachineState *ms = MACHINE(qdev_get_machine()); - unsigned int max_cpus = ms->smp.max_cpus; -#endif if (bitmap_empty(env->emergency_signals, max_cpus)) { env->pending_int &= ~INTERRUPT_EMERGENCY_SIGNAL; } @@ -394,7 +393,7 @@ static int mchk_store_vregs(CPUS390XState *env, uint64_t mcesao) MchkExtSaveArea *sa; int i; - sa = cpu_physical_memory_map(mcesao, &len, 1); + sa = cpu_physical_memory_map(mcesao, &len, true); if (!sa) { return -EFAULT; } diff --git a/target/s390x/gdbstub.c b/target/s390x/gdbstub.c index e24a49f4a9..d6fce5ff1e 100644 --- a/target/s390x/gdbstub.c +++ b/target/s390x/gdbstub.c @@ -27,7 +27,7 @@ #include "sysemu/hw_accel.h" #include "sysemu/tcg.h" -int s390_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +int s390_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) { S390CPU *cpu = S390_CPU(cs); CPUS390XState *env = &cpu->env; @@ -82,11 +82,11 @@ int s390_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) /* total number of registers in s390-acr.xml */ #define S390_NUM_AC_REGS 16 -static int cpu_read_ac_reg(CPUS390XState *env, uint8_t *mem_buf, int n) +static int cpu_read_ac_reg(CPUS390XState *env, GByteArray *buf, int n) { switch (n) { case S390_A0_REGNUM ... S390_A15_REGNUM: - return gdb_get_reg32(mem_buf, env->aregs[n]); + return gdb_get_reg32(buf, env->aregs[n]); default: return 0; } @@ -111,13 +111,13 @@ static int cpu_write_ac_reg(CPUS390XState *env, uint8_t *mem_buf, int n) /* total number of registers in s390-fpr.xml */ #define S390_NUM_FP_REGS 17 -static int cpu_read_fp_reg(CPUS390XState *env, uint8_t *mem_buf, int n) +static int cpu_read_fp_reg(CPUS390XState *env, GByteArray *buf, int n) { switch (n) { case S390_FPC_REGNUM: - return gdb_get_reg32(mem_buf, env->fpc); + return gdb_get_reg32(buf, env->fpc); case S390_F0_REGNUM ... S390_F15_REGNUM: - return gdb_get_reg64(mem_buf, *get_freg(env, n - S390_F0_REGNUM)); + return gdb_get_reg64(buf, *get_freg(env, n - S390_F0_REGNUM)); default: return 0; } @@ -145,17 +145,17 @@ static int cpu_write_fp_reg(CPUS390XState *env, uint8_t *mem_buf, int n) /* total number of registers in s390-vx.xml */ #define S390_NUM_VREGS 32 -static int cpu_read_vreg(CPUS390XState *env, uint8_t *mem_buf, int n) +static int cpu_read_vreg(CPUS390XState *env, GByteArray *buf, int n) { int ret; switch (n) { case S390_V0L_REGNUM ... S390_V15L_REGNUM: - ret = gdb_get_reg64(mem_buf, env->vregs[n][1]); + ret = gdb_get_reg64(buf, env->vregs[n][1]); break; case S390_V16_REGNUM ... S390_V31_REGNUM: - ret = gdb_get_reg64(mem_buf, env->vregs[n][0]); - ret += gdb_get_reg64(mem_buf + 8, env->vregs[n][1]); + ret = gdb_get_reg64(buf, env->vregs[n][0]); + ret += gdb_get_reg64(buf, env->vregs[n][1]); break; default: ret = 0; @@ -186,11 +186,11 @@ static int cpu_write_vreg(CPUS390XState *env, uint8_t *mem_buf, int n) #define S390_NUM_C_REGS 16 #ifndef CONFIG_USER_ONLY -static int cpu_read_c_reg(CPUS390XState *env, uint8_t *mem_buf, int n) +static int cpu_read_c_reg(CPUS390XState *env, GByteArray *buf, int n) { switch (n) { case S390_C0_REGNUM ... S390_C15_REGNUM: - return gdb_get_regl(mem_buf, env->cregs[n]); + return gdb_get_regl(buf, env->cregs[n]); default: return 0; } @@ -223,7 +223,7 @@ static int cpu_write_c_reg(CPUS390XState *env, uint8_t *mem_buf, int n) /* total number of registers in s390-virt.xml */ #define S390_NUM_VIRT_REGS 8 -static int cpu_read_virt_reg(CPUS390XState *env, uint8_t *mem_buf, int n) +static int cpu_read_virt_reg(CPUS390XState *env, GByteArray *mem_buf, int n) { switch (n) { case S390_VIRT_CKC_REGNUM: @@ -296,9 +296,9 @@ static int cpu_write_virt_reg(CPUS390XState *env, uint8_t *mem_buf, int n) /* total number of registers in s390-gs.xml */ #define S390_NUM_GS_REGS 4 -static int cpu_read_gs_reg(CPUS390XState *env, uint8_t *mem_buf, int n) +static int cpu_read_gs_reg(CPUS390XState *env, GByteArray *buf, int n) { - return gdb_get_regl(mem_buf, env->gscb[n]); + return gdb_get_regl(buf, env->gscb[n]); } static int cpu_write_gs_reg(CPUS390XState *env, uint8_t *mem_buf, int n) diff --git a/target/s390x/helper.c b/target/s390x/helper.c index a3a49164e4..ed72684911 100644 --- a/target/s390x/helper.c +++ b/target/s390x/helper.c @@ -89,7 +89,7 @@ hwaddr s390_cpu_get_phys_addr_debug(CPUState *cs, vaddr vaddr) static inline bool is_special_wait_psw(uint64_t psw_addr) { /* signal quiesce */ - return psw_addr == 0xfffUL; + return (psw_addr & 0xfffUL) == 0xfffUL; } void s390_handle_wait(S390CPU *cpu) @@ -151,7 +151,7 @@ LowCore *cpu_map_lowcore(CPUS390XState *env) LowCore *lowcore; hwaddr len = sizeof(LowCore); - lowcore = cpu_physical_memory_map(env->psa, &len, 1); + lowcore = cpu_physical_memory_map(env->psa, &len, true); if (len < sizeof(LowCore)) { cpu_abort(env_cpu(env), "Could not map lowcore\n"); @@ -246,7 +246,7 @@ int s390_store_status(S390CPU *cpu, hwaddr addr, bool store_arch) hwaddr len = sizeof(*sa); int i; - sa = cpu_physical_memory_map(addr, &len, 1); + sa = cpu_physical_memory_map(addr, &len, true); if (!sa) { return -EFAULT; } @@ -298,7 +298,7 @@ int s390_store_adtl_status(S390CPU *cpu, hwaddr addr, hwaddr len) hwaddr save = len; int i; - sa = cpu_physical_memory_map(addr, &save, 1); + sa = cpu_physical_memory_map(addr, &save, true); if (!sa) { return -EFAULT; } diff --git a/target/s390x/helper.h b/target/s390x/helper.h index 56e8149866..b5813c2ac2 100644 --- a/target/s390x/helper.h +++ b/target/s390x/helper.h @@ -324,13 +324,10 @@ DEF_HELPER_FLAGS_4(ipte, TCG_CALL_NO_RWG, void, env, i64, i64, i32) DEF_HELPER_FLAGS_1(ptlb, TCG_CALL_NO_RWG, void, env) DEF_HELPER_FLAGS_1(purge, TCG_CALL_NO_RWG, void, env) DEF_HELPER_2(lra, i64, env, i64) -DEF_HELPER_FLAGS_2(lura, TCG_CALL_NO_WG, i64, env, i64) -DEF_HELPER_FLAGS_2(lurag, TCG_CALL_NO_WG, i64, env, i64) -DEF_HELPER_FLAGS_3(stura, TCG_CALL_NO_WG, void, env, i64, i64) -DEF_HELPER_FLAGS_3(sturg, TCG_CALL_NO_WG, void, env, i64, i64) DEF_HELPER_1(per_check_exception, void, env) DEF_HELPER_FLAGS_3(per_branch, TCG_CALL_NO_RWG, void, env, i64, i64) DEF_HELPER_FLAGS_2(per_ifetch, TCG_CALL_NO_RWG, void, env, i64) +DEF_HELPER_FLAGS_1(per_store_real, TCG_CALL_NO_RWG, void, env) DEF_HELPER_FLAGS_1(stfl, TCG_CALL_NO_RWG, void, env) DEF_HELPER_2(xsch, void, env, i64) diff --git a/target/s390x/insn-data.def b/target/s390x/insn-data.def index 449eee1662..2bc77f0871 100644 --- a/target/s390x/insn-data.def +++ b/target/s390x/insn-data.def @@ -1275,8 +1275,8 @@ F(0xe313, LRAY, RXY_a, LD, 0, a2, r1, 0, lra, 0, IF_PRIV) F(0xe303, LRAG, RXY_a, Z, 0, a2, r1, 0, lra, 0, IF_PRIV) /* LOAD USING REAL ADDRESS */ - F(0xb24b, LURA, RRE, Z, 0, r2, new, r1_32, lura, 0, IF_PRIV) - F(0xb905, LURAG, RRE, Z, 0, r2, r1, 0, lurag, 0, IF_PRIV) + E(0xb24b, LURA, RRE, Z, 0, 0, new, r1_32, lura, 0, MO_TEUL, IF_PRIV) + E(0xb905, LURAG, RRE, Z, 0, 0, r1, 0, lura, 0, MO_TEQ, IF_PRIV) /* MOVE TO PRIMARY */ F(0xda00, MVCP, SS_d, Z, la1, a2, 0, 0, mvcp, 0, IF_PRIV) /* MOVE TO SECONDARY */ @@ -1329,8 +1329,8 @@ /* STORE THEN OR SYSTEM MASK */ F(0xad00, STOSM, SI, Z, la1, 0, 0, 0, stnosm, 0, IF_PRIV) /* STORE USING REAL ADDRESS */ - F(0xb246, STURA, RRE, Z, r1_o, r2_o, 0, 0, stura, 0, IF_PRIV) - F(0xb925, STURG, RRE, Z, r1_o, r2_o, 0, 0, sturg, 0, IF_PRIV) + E(0xb246, STURA, RRE, Z, r1_o, 0, 0, 0, stura, 0, MO_TEUL, IF_PRIV) + E(0xb925, STURG, RRE, Z, r1_o, 0, 0, 0, stura, 0, MO_TEQ, IF_PRIV) /* TEST BLOCK */ F(0xb22c, TB, RRE, Z, 0, r2_o, 0, 0, testblock, 0, IF_PRIV) /* TEST PROTECTION */ diff --git a/target/s390x/internal.h b/target/s390x/internal.h index d37816104d..8c95c734db 100644 --- a/target/s390x/internal.h +++ b/target/s390x/internal.h @@ -292,7 +292,7 @@ uint16_t float128_dcmask(CPUS390XState *env, float128 f1); /* gdbstub.c */ -int s390_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); +int s390_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int s390_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); void s390_cpu_gdb_init(CPUState *cs); diff --git a/target/s390x/ioinst.c b/target/s390x/ioinst.c index c437a1d8c6..0e840cc579 100644 --- a/target/s390x/ioinst.c +++ b/target/s390x/ioinst.c @@ -347,7 +347,7 @@ typedef struct ChscResp { uint16_t len; uint16_t code; uint32_t param; - char data[0]; + char data[]; } QEMU_PACKED ChscResp; #define CHSC_MIN_RESP_LEN 0x0008 diff --git a/target/s390x/kvm-stub.c b/target/s390x/kvm-stub.c index 5152e2bdf1..c4cd497f85 100644 --- a/target/s390x/kvm-stub.c +++ b/target/s390x/kvm-stub.c @@ -83,7 +83,15 @@ void kvm_s390_cmma_reset(void) { } -void kvm_s390_reset_vcpu(S390CPU *cpu) +void kvm_s390_reset_vcpu_initial(S390CPU *cpu) +{ +} + +void kvm_s390_reset_vcpu_clear(S390CPU *cpu) +{ +} + +void kvm_s390_reset_vcpu_normal(S390CPU *cpu) { } diff --git a/target/s390x/kvm.c b/target/s390x/kvm.c index 0c9d14b4b1..7f7ebab842 100644 --- a/target/s390x/kvm.c +++ b/target/s390x/kvm.c @@ -151,6 +151,7 @@ static int cap_s390_irq; static int cap_ri; static int cap_gs; static int cap_hpage_1m; +static int cap_vcpu_resets; static int active_cmma; @@ -342,6 +343,7 @@ int kvm_arch_init(MachineState *ms, KVMState *s) cap_async_pf = kvm_check_extension(s, KVM_CAP_ASYNC_PF); cap_mem_op = kvm_check_extension(s, KVM_CAP_S390_MEM_OP); cap_s390_irq = kvm_check_extension(s, KVM_CAP_S390_INJECT_IRQ); + cap_vcpu_resets = kvm_check_extension(s, KVM_CAP_S390_VCPU_RESETS); if (!kvm_check_extension(s, KVM_CAP_S390_GMAP) || !kvm_check_extension(s, KVM_CAP_S390_COW)) { @@ -365,16 +367,19 @@ int kvm_arch_init(MachineState *ms, KVMState *s) /* * The migration interface for ais was introduced with kernel 4.13 * but the capability itself had been active since 4.12. As migration - * support is considered necessary let's disable ais in the 2.10 - * machine. + * support is considered necessary, we only try to enable this for + * newer machine types if KVM_CAP_S390_AIS_MIGRATION is available. */ - /* kvm_vm_enable_cap(s, KVM_CAP_S390_AIS, 0); */ + if (cpu_model_allowed() && kvm_kernel_irqchip_allowed() && + kvm_check_extension(s, KVM_CAP_S390_AIS_MIGRATION)) { + kvm_vm_enable_cap(s, KVM_CAP_S390_AIS, 0); + } kvm_set_max_memslot_size(KVM_SLOT_MAX_BYTES); return 0; } -int kvm_arch_irqchip_create(MachineState *ms, KVMState *s) +int kvm_arch_irqchip_create(KVMState *s) { return 0; } @@ -403,17 +408,41 @@ int kvm_arch_destroy_vcpu(CPUState *cs) return 0; } -void kvm_s390_reset_vcpu(S390CPU *cpu) +static void kvm_s390_reset_vcpu(S390CPU *cpu, unsigned long type) { CPUState *cs = CPU(cpu); - /* The initial reset call is needed here to reset in-kernel - * vcpu data that we can't access directly from QEMU - * (i.e. with older kernels which don't support sync_regs/ONE_REG). - * Before this ioctl cpu_synchronize_state() is called in common kvm - * code (kvm-all) */ - if (kvm_vcpu_ioctl(cs, KVM_S390_INITIAL_RESET, NULL)) { - error_report("Initial CPU reset failed on CPU %i", cs->cpu_index); + /* + * The reset call is needed here to reset in-kernel vcpu data that + * we can't access directly from QEMU (i.e. with older kernels + * which don't support sync_regs/ONE_REG). Before this ioctl + * cpu_synchronize_state() is called in common kvm code + * (kvm-all). + */ + if (kvm_vcpu_ioctl(cs, type)) { + error_report("CPU reset failed on CPU %i type %lx", + cs->cpu_index, type); + } +} + +void kvm_s390_reset_vcpu_initial(S390CPU *cpu) +{ + kvm_s390_reset_vcpu(cpu, KVM_S390_INITIAL_RESET); +} + +void kvm_s390_reset_vcpu_clear(S390CPU *cpu) +{ + if (cap_vcpu_resets) { + kvm_s390_reset_vcpu(cpu, KVM_S390_CLEAR_RESET); + } else { + kvm_s390_reset_vcpu(cpu, KVM_S390_INITIAL_RESET); + } +} + +void kvm_s390_reset_vcpu_normal(S390CPU *cpu) +{ + if (cap_vcpu_resets) { + kvm_s390_reset_vcpu(cpu, KVM_S390_NORMAL_RESET); } } @@ -1159,13 +1188,13 @@ void kvm_s390_access_exception(S390CPU *cpu, uint16_t code, uint64_t te_code) kvm_s390_vcpu_interrupt(cpu, &irq); } -static int kvm_sclp_service_call(S390CPU *cpu, struct kvm_run *run, +static void kvm_sclp_service_call(S390CPU *cpu, struct kvm_run *run, uint16_t ipbh0) { CPUS390XState *env = &cpu->env; uint64_t sccb; uint32_t code; - int r = 0; + int r; sccb = env->regs[ipbh0 & 0xf]; code = env->regs[(ipbh0 & 0xf0) >> 4]; @@ -1173,11 +1202,9 @@ static int kvm_sclp_service_call(S390CPU *cpu, struct kvm_run *run, r = sclp_service_call(env, sccb, code); if (r < 0) { kvm_s390_program_interrupt(cpu, -r); - } else { - setcc(cpu, r); + return; } - - return 0; + setcc(cpu, r); } static int handle_b2(S390CPU *cpu, struct kvm_run *run, uint8_t ipa1) @@ -1240,7 +1267,7 @@ static int handle_b2(S390CPU *cpu, struct kvm_run *run, uint8_t ipa1) setcc(cpu, 3); break; case PRIV_B2_SCLP_CALL: - rc = kvm_sclp_service_call(cpu, run, ipbh0); + kvm_sclp_service_call(cpu, run, ipbh0); break; default: rc = -1; @@ -1741,8 +1768,10 @@ static int handle_tsch(S390CPU *cpu) static void insert_stsi_3_2_2(S390CPU *cpu, __u64 addr, uint8_t ar) { + const MachineState *ms = MACHINE(qdev_get_machine()); + uint16_t conf_cpus = 0, reserved_cpus = 0; SysIB_322 sysib; - int del; + int del, i; if (s390_cpu_virt_mem_read(cpu, addr, ar, &sysib, sizeof(sysib))) { return; @@ -1762,6 +1791,19 @@ static void insert_stsi_3_2_2(S390CPU *cpu, __u64 addr, uint8_t ar) memset(sysib.ext_names[del], 0, sizeof(sysib.ext_names[0]) * (sysib.count - del)); } + + /* count the cpus and split them into configured and reserved ones */ + for (i = 0; i < ms->possible_cpus->len; i++) { + if (ms->possible_cpus->cpus[i].cpu) { + conf_cpus++; + } else { + reserved_cpus++; + } + } + sysib.vm[0].total_cpus = conf_cpus + reserved_cpus; + sysib.vm[0].conf_cpus = conf_cpus; + sysib.vm[0].reserved_cpus = reserved_cpus; + /* Insert short machine name in EBCDIC, padded with blanks */ if (qemu_name) { memset(sysib.vm[0].name, 0x40, sizeof(sysib.vm[0].name)); diff --git a/target/s390x/kvm_s390x.h b/target/s390x/kvm_s390x.h index caf985955b..0b21789796 100644 --- a/target/s390x/kvm_s390x.h +++ b/target/s390x/kvm_s390x.h @@ -34,7 +34,9 @@ int kvm_s390_assign_subch_ioeventfd(EventNotifier *notifier, uint32_t sch, int vq, bool assign); int kvm_s390_cmma_active(void); void kvm_s390_cmma_reset(void); -void kvm_s390_reset_vcpu(S390CPU *cpu); +void kvm_s390_reset_vcpu_clear(S390CPU *cpu); +void kvm_s390_reset_vcpu_normal(S390CPU *cpu); +void kvm_s390_reset_vcpu_initial(S390CPU *cpu); int kvm_s390_set_mem_limit(uint64_t new_limit, uint64_t *hw_limit); void kvm_s390_set_max_pagesize(uint64_t pagesize, Error **errp); void kvm_s390_crypto_reset(void); diff --git a/target/s390x/mem_helper.c b/target/s390x/mem_helper.c index 2325767f17..a237dec757 100644 --- a/target/s390x/mem_helper.c +++ b/target/s390x/mem_helper.c @@ -27,6 +27,7 @@ #include "exec/cpu_ldst.h" #include "qemu/int128.h" #include "qemu/atomic128.h" +#include "tcg/tcg.h" #if !defined(CONFIG_USER_ONLY) #include "hw/s390x/storage-keys.h" @@ -2025,7 +2026,7 @@ uint32_t HELPER(testblock)(CPUS390XState *env, uint64_t real_addr) real_addr = wrap_address(env, real_addr) & TARGET_PAGE_MASK; for (i = 0; i < TARGET_PAGE_SIZE; i += 8) { - cpu_stq_real_ra(env, real_addr + i, 0, ra); + cpu_stq_mmuidx_ra(env, real_addr + i, 0, MMU_REAL_IDX, ra); } return 0; @@ -2259,11 +2260,11 @@ void HELPER(idte)(CPUS390XState *env, uint64_t r1, uint64_t r2, uint32_t m4) for (i = 0; i < entries; i++) { /* addresses are not wrapped in 24/31bit mode but table index is */ raddr = table + ((index + i) & 0x7ff) * sizeof(entry); - entry = cpu_ldq_real_ra(env, raddr, ra); + entry = cpu_ldq_mmuidx_ra(env, raddr, MMU_REAL_IDX, ra); if (!(entry & REGION_ENTRY_I)) { /* we are allowed to not store if already invalid */ entry |= REGION_ENTRY_I; - cpu_stq_real_ra(env, raddr, entry, ra); + cpu_stq_mmuidx_ra(env, raddr, entry, MMU_REAL_IDX, ra); } } } @@ -2290,9 +2291,9 @@ void HELPER(ipte)(CPUS390XState *env, uint64_t pto, uint64_t vaddr, pte_addr += VADDR_PAGE_TX(vaddr) * 8; /* Mark the page table entry as invalid */ - pte = cpu_ldq_real_ra(env, pte_addr, ra); + pte = cpu_ldq_mmuidx_ra(env, pte_addr, MMU_REAL_IDX, ra); pte |= PAGE_ENTRY_I; - cpu_stq_real_ra(env, pte_addr, pte, ra); + cpu_stq_mmuidx_ra(env, pte_addr, pte, MMU_REAL_IDX, ra); /* XXX we exploit the fact that Linux passes the exact virtual address here - it's not obliged to! */ @@ -2329,44 +2330,6 @@ void HELPER(purge)(CPUS390XState *env) tlb_flush_all_cpus_synced(env_cpu(env)); } -/* load using real address */ -uint64_t HELPER(lura)(CPUS390XState *env, uint64_t addr) -{ - return cpu_ldl_real_ra(env, wrap_address(env, addr), GETPC()); -} - -uint64_t HELPER(lurag)(CPUS390XState *env, uint64_t addr) -{ - return cpu_ldq_real_ra(env, wrap_address(env, addr), GETPC()); -} - -/* store using real address */ -void HELPER(stura)(CPUS390XState *env, uint64_t addr, uint64_t v1) -{ - cpu_stl_real_ra(env, wrap_address(env, addr), (uint32_t)v1, GETPC()); - - if ((env->psw.mask & PSW_MASK_PER) && - (env->cregs[9] & PER_CR9_EVENT_STORE) && - (env->cregs[9] & PER_CR9_EVENT_STORE_REAL)) { - /* PSW is saved just before calling the helper. */ - env->per_address = env->psw.addr; - env->per_perc_atmid = PER_CODE_EVENT_STORE_REAL | get_per_atmid(env); - } -} - -void HELPER(sturg)(CPUS390XState *env, uint64_t addr, uint64_t v1) -{ - cpu_stq_real_ra(env, wrap_address(env, addr), v1, GETPC()); - - if ((env->psw.mask & PSW_MASK_PER) && - (env->cregs[9] & PER_CR9_EVENT_STORE) && - (env->cregs[9] & PER_CR9_EVENT_STORE_REAL)) { - /* PSW is saved just before calling the helper. */ - env->per_address = env->psw.addr; - env->per_perc_atmid = PER_CODE_EVENT_STORE_REAL | get_per_atmid(env); - } -} - /* load real address */ uint64_t HELPER(lra)(CPUS390XState *env, uint64_t addr) { diff --git a/target/s390x/misc_helper.c b/target/s390x/misc_helper.c index bfb457fb63..58dbc023eb 100644 --- a/target/s390x/misc_helper.c +++ b/target/s390x/misc_helper.c @@ -620,6 +620,16 @@ void HELPER(per_ifetch)(CPUS390XState *env, uint64_t addr) } } } + +void HELPER(per_store_real)(CPUS390XState *env) +{ + if ((env->cregs[9] & PER_CR9_EVENT_STORE) && + (env->cregs[9] & PER_CR9_EVENT_STORE_REAL)) { + /* PSW is saved just before calling the helper. */ + env->per_address = env->psw.addr; + env->per_perc_atmid = PER_CODE_EVENT_STORE_REAL | get_per_atmid(env); + } +} #endif static uint8_t stfl_bytes[2048]; diff --git a/target/s390x/mmu_helper.c b/target/s390x/mmu_helper.c index c9f3f34750..0be2f300bb 100644 --- a/target/s390x/mmu_helper.c +++ b/target/s390x/mmu_helper.c @@ -106,7 +106,7 @@ static inline bool read_table_entry(CPUS390XState *env, hwaddr gaddr, * We treat them as absolute addresses and don't wrap them. */ if (unlikely(address_space_read(cs->as, gaddr, MEMTXATTRS_UNSPECIFIED, - (uint8_t *)entry, sizeof(*entry)) != + entry, sizeof(*entry)) != MEMTX_OK)) { return false; } diff --git a/target/s390x/sigp.c b/target/s390x/sigp.c index 2ce22d4dc1..c604f17710 100644 --- a/target/s390x/sigp.c +++ b/target/s390x/sigp.c @@ -254,7 +254,7 @@ static void sigp_initial_cpu_reset(CPUState *cs, run_on_cpu_data arg) SigpInfo *si = arg.host_ptr; cpu_synchronize_state(cs); - scc->initial_cpu_reset(cs); + scc->reset(cs, S390_CPU_RESET_INITIAL); cpu_synchronize_post_reset(cs); si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; } @@ -266,7 +266,7 @@ static void sigp_cpu_reset(CPUState *cs, run_on_cpu_data arg) SigpInfo *si = arg.host_ptr; cpu_synchronize_state(cs); - scc->cpu_reset(cs); + scc->reset(cs, S390_CPU_RESET_NORMAL); cpu_synchronize_post_reset(cs); si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; } @@ -348,9 +348,9 @@ static void sigp_sense_running(S390CPU *dst_cpu, SigpInfo *si) /* If halted (which includes also STOPPED), it is not running */ if (CPU(dst_cpu)->halted) { - si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; - } else { set_sigp_status(si, SIGP_STAT_NOT_RUNNING); + } else { + si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; } } diff --git a/target/s390x/translate.c b/target/s390x/translate.c index 151dfa91fb..4f6f1e31cd 100644 --- a/target/s390x/translate.c +++ b/target/s390x/translate.c @@ -33,8 +33,8 @@ #include "internal.h" #include "disas/disas.h" #include "exec/exec-all.h" -#include "tcg-op.h" -#include "tcg-op-gvec.h" +#include "tcg/tcg-op.h" +#include "tcg/tcg-op-gvec.h" #include "qemu/log.h" #include "qemu/host-utils.h" #include "exec/cpu_ldst.h" @@ -53,10 +53,93 @@ typedef struct DisasContext DisasContext; typedef struct DisasInsn DisasInsn; typedef struct DisasFields DisasFields; +/* + * Define a structure to hold the decoded fields. We'll store each inside + * an array indexed by an enum. In order to conserve memory, we'll arrange + * for fields that do not exist at the same time to overlap, thus the "C" + * for compact. For checking purposes there is an "O" for original index + * as well that will be applied to availability bitmaps. + */ + +enum DisasFieldIndexO { + FLD_O_r1, + FLD_O_r2, + FLD_O_r3, + FLD_O_m1, + FLD_O_m3, + FLD_O_m4, + FLD_O_m5, + FLD_O_m6, + FLD_O_b1, + FLD_O_b2, + FLD_O_b4, + FLD_O_d1, + FLD_O_d2, + FLD_O_d4, + FLD_O_x2, + FLD_O_l1, + FLD_O_l2, + FLD_O_i1, + FLD_O_i2, + FLD_O_i3, + FLD_O_i4, + FLD_O_i5, + FLD_O_v1, + FLD_O_v2, + FLD_O_v3, + FLD_O_v4, +}; + +enum DisasFieldIndexC { + FLD_C_r1 = 0, + FLD_C_m1 = 0, + FLD_C_b1 = 0, + FLD_C_i1 = 0, + FLD_C_v1 = 0, + + FLD_C_r2 = 1, + FLD_C_b2 = 1, + FLD_C_i2 = 1, + + FLD_C_r3 = 2, + FLD_C_m3 = 2, + FLD_C_i3 = 2, + FLD_C_v3 = 2, + + FLD_C_m4 = 3, + FLD_C_b4 = 3, + FLD_C_i4 = 3, + FLD_C_l1 = 3, + FLD_C_v4 = 3, + + FLD_C_i5 = 4, + FLD_C_d1 = 4, + FLD_C_m5 = 4, + + FLD_C_d2 = 5, + FLD_C_m6 = 5, + + FLD_C_d4 = 6, + FLD_C_x2 = 6, + FLD_C_l2 = 6, + FLD_C_v2 = 6, + + NUM_C_FIELD = 7 +}; + +struct DisasFields { + uint64_t raw_insn; + unsigned op:8; + unsigned op2:8; + unsigned presentC:16; + unsigned int presentO; + int c[NUM_C_FIELD]; +}; + struct DisasContext { DisasContextBase base; const DisasInsn *insn; - DisasFields *fields; + DisasFields fields; uint64_t ex_value; /* * During translate_one(), pc_tmp is used to determine the instruction @@ -1005,101 +1088,20 @@ typedef enum { #undef F5 #undef F6 -/* Define a structure to hold the decoded fields. We'll store each inside - an array indexed by an enum. In order to conserve memory, we'll arrange - for fields that do not exist at the same time to overlap, thus the "C" - for compact. For checking purposes there is an "O" for original index - as well that will be applied to availability bitmaps. */ - -enum DisasFieldIndexO { - FLD_O_r1, - FLD_O_r2, - FLD_O_r3, - FLD_O_m1, - FLD_O_m3, - FLD_O_m4, - FLD_O_m5, - FLD_O_m6, - FLD_O_b1, - FLD_O_b2, - FLD_O_b4, - FLD_O_d1, - FLD_O_d2, - FLD_O_d4, - FLD_O_x2, - FLD_O_l1, - FLD_O_l2, - FLD_O_i1, - FLD_O_i2, - FLD_O_i3, - FLD_O_i4, - FLD_O_i5, - FLD_O_v1, - FLD_O_v2, - FLD_O_v3, - FLD_O_v4, -}; - -enum DisasFieldIndexC { - FLD_C_r1 = 0, - FLD_C_m1 = 0, - FLD_C_b1 = 0, - FLD_C_i1 = 0, - FLD_C_v1 = 0, - - FLD_C_r2 = 1, - FLD_C_b2 = 1, - FLD_C_i2 = 1, - - FLD_C_r3 = 2, - FLD_C_m3 = 2, - FLD_C_i3 = 2, - FLD_C_v3 = 2, - - FLD_C_m4 = 3, - FLD_C_b4 = 3, - FLD_C_i4 = 3, - FLD_C_l1 = 3, - FLD_C_v4 = 3, - - FLD_C_i5 = 4, - FLD_C_d1 = 4, - FLD_C_m5 = 4, - - FLD_C_d2 = 5, - FLD_C_m6 = 5, - - FLD_C_d4 = 6, - FLD_C_x2 = 6, - FLD_C_l2 = 6, - FLD_C_v2 = 6, - - NUM_C_FIELD = 7 -}; - -struct DisasFields { - uint64_t raw_insn; - unsigned op:8; - unsigned op2:8; - unsigned presentC:16; - unsigned int presentO; - int c[NUM_C_FIELD]; -}; - /* This is the way fields are to be accessed out of DisasFields. */ #define have_field(S, F) have_field1((S), FLD_O_##F) #define get_field(S, F) get_field1((S), FLD_O_##F, FLD_C_##F) -static bool have_field1(const DisasFields *f, enum DisasFieldIndexO c) +static bool have_field1(const DisasContext *s, enum DisasFieldIndexO c) { - return (f->presentO >> c) & 1; + return (s->fields.presentO >> c) & 1; } -static int get_field1(const DisasFields *f, enum DisasFieldIndexO o, +static int get_field1(const DisasContext *s, enum DisasFieldIndexO o, enum DisasFieldIndexC c) { - assert(have_field1(f, o)); - return f->c[c]; + assert(have_field1(s, o)); + return s->fields.c[c]; } /* Describe the layout of each field in each format. */ @@ -1221,15 +1223,15 @@ struct DisasInsn { const char *name; /* Pre-process arguments before HELP_OP. */ - void (*help_in1)(DisasContext *, DisasFields *, DisasOps *); - void (*help_in2)(DisasContext *, DisasFields *, DisasOps *); - void (*help_prep)(DisasContext *, DisasFields *, DisasOps *); + void (*help_in1)(DisasContext *, DisasOps *); + void (*help_in2)(DisasContext *, DisasOps *); + void (*help_prep)(DisasContext *, DisasOps *); /* * Post-process output after HELP_OP. * Note that these are not called if HELP_OP returns DISAS_NORETURN. */ - void (*help_wout)(DisasContext *, DisasFields *, DisasOps *); + void (*help_wout)(DisasContext *, DisasOps *); void (*help_cout)(DisasContext *, DisasOps *); /* Implement the operation itself. */ @@ -1241,11 +1243,10 @@ struct DisasInsn { /* ====================================================================== */ /* Miscellaneous helpers, used by several operations. */ -static void help_l2_shift(DisasContext *s, DisasFields *f, - DisasOps *o, int mask) +static void help_l2_shift(DisasContext *s, DisasOps *o, int mask) { - int b2 = get_field(f, b2); - int d2 = get_field(f, d2); + int b2 = get_field(s, b2); + int d2 = get_field(s, d2); if (b2 == 0) { o->in2 = tcg_const_i64(d2 & mask); @@ -1600,18 +1601,18 @@ static DisasJumpType op_bal(DisasContext *s, DisasOps *o) static DisasJumpType op_basi(DisasContext *s, DisasOps *o) { pc_to_link_info(o->out, s, s->pc_tmp); - return help_goto_direct(s, s->base.pc_next + 2 * get_field(s->fields, i2)); + return help_goto_direct(s, s->base.pc_next + 2 * get_field(s, i2)); } static DisasJumpType op_bc(DisasContext *s, DisasOps *o) { - int m1 = get_field(s->fields, m1); - bool is_imm = have_field(s->fields, i2); - int imm = is_imm ? get_field(s->fields, i2) : 0; + int m1 = get_field(s, m1); + bool is_imm = have_field(s, i2); + int imm = is_imm ? get_field(s, i2) : 0; DisasCompare c; /* BCR with R2 = 0 causes no branching */ - if (have_field(s->fields, r2) && get_field(s->fields, r2) == 0) { + if (have_field(s, r2) && get_field(s, r2) == 0) { if (m1 == 14) { /* Perform serialization */ /* FIXME: check for fast-BCR-serialization facility */ @@ -1631,9 +1632,9 @@ static DisasJumpType op_bc(DisasContext *s, DisasOps *o) static DisasJumpType op_bct32(DisasContext *s, DisasOps *o) { - int r1 = get_field(s->fields, r1); - bool is_imm = have_field(s->fields, i2); - int imm = is_imm ? get_field(s->fields, i2) : 0; + int r1 = get_field(s, r1); + bool is_imm = have_field(s, i2); + int imm = is_imm ? get_field(s, i2) : 0; DisasCompare c; TCGv_i64 t; @@ -1655,8 +1656,8 @@ static DisasJumpType op_bct32(DisasContext *s, DisasOps *o) static DisasJumpType op_bcth(DisasContext *s, DisasOps *o) { - int r1 = get_field(s->fields, r1); - int imm = get_field(s->fields, i2); + int r1 = get_field(s, r1); + int imm = get_field(s, i2); DisasCompare c; TCGv_i64 t; @@ -1679,9 +1680,9 @@ static DisasJumpType op_bcth(DisasContext *s, DisasOps *o) static DisasJumpType op_bct64(DisasContext *s, DisasOps *o) { - int r1 = get_field(s->fields, r1); - bool is_imm = have_field(s->fields, i2); - int imm = is_imm ? get_field(s->fields, i2) : 0; + int r1 = get_field(s, r1); + bool is_imm = have_field(s, i2); + int imm = is_imm ? get_field(s, i2) : 0; DisasCompare c; c.cond = TCG_COND_NE; @@ -1698,10 +1699,10 @@ static DisasJumpType op_bct64(DisasContext *s, DisasOps *o) static DisasJumpType op_bx32(DisasContext *s, DisasOps *o) { - int r1 = get_field(s->fields, r1); - int r3 = get_field(s->fields, r3); - bool is_imm = have_field(s->fields, i2); - int imm = is_imm ? get_field(s->fields, i2) : 0; + int r1 = get_field(s, r1); + int r3 = get_field(s, r3); + bool is_imm = have_field(s, i2); + int imm = is_imm ? get_field(s, i2) : 0; DisasCompare c; TCGv_i64 t; @@ -1724,10 +1725,10 @@ static DisasJumpType op_bx32(DisasContext *s, DisasOps *o) static DisasJumpType op_bx64(DisasContext *s, DisasOps *o) { - int r1 = get_field(s->fields, r1); - int r3 = get_field(s->fields, r3); - bool is_imm = have_field(s->fields, i2); - int imm = is_imm ? get_field(s->fields, i2) : 0; + int r1 = get_field(s, r1); + int r3 = get_field(s, r3); + bool is_imm = have_field(s, i2); + int imm = is_imm ? get_field(s, i2) : 0; DisasCompare c; c.cond = (s->insn->data ? TCG_COND_LE : TCG_COND_GT); @@ -1750,7 +1751,7 @@ static DisasJumpType op_bx64(DisasContext *s, DisasOps *o) static DisasJumpType op_cj(DisasContext *s, DisasOps *o) { - int imm, m3 = get_field(s->fields, m3); + int imm, m3 = get_field(s, m3); bool is_imm; DisasCompare c; @@ -1762,13 +1763,13 @@ static DisasJumpType op_cj(DisasContext *s, DisasOps *o) c.u.s64.a = o->in1; c.u.s64.b = o->in2; - is_imm = have_field(s->fields, i4); + is_imm = have_field(s, i4); if (is_imm) { - imm = get_field(s->fields, i4); + imm = get_field(s, i4); } else { imm = 0; - o->out = get_address(s, 0, get_field(s->fields, b4), - get_field(s->fields, d4)); + o->out = get_address(s, 0, get_field(s, b4), + get_field(s, d4)); } return help_branch(s, &c, is_imm, imm, o->out); @@ -1799,8 +1800,8 @@ static TCGv_i32 fpinst_extract_m34(DisasContext *s, bool m3_with_fpe, bool m4_with_fpe) { const bool fpe = s390_has_feat(S390_FEAT_FLOATING_POINT_EXT); - uint8_t m3 = get_field(s->fields, m3); - uint8_t m4 = get_field(s->fields, m4); + uint8_t m3 = get_field(s, m3); + uint8_t m4 = get_field(s, m4); /* m3 field was introduced with FPE */ if (!fpe && m3_with_fpe) { @@ -2052,7 +2053,7 @@ static DisasJumpType op_cxlgb(DisasContext *s, DisasOps *o) static DisasJumpType op_cksm(DisasContext *s, DisasOps *o) { - int r2 = get_field(s->fields, r2); + int r2 = get_field(s, r2); TCGv_i64 len = tcg_temp_new_i64(); gen_helper_cksm(len, cpu_env, o->in1, o->in2, regs[r2 + 1]); @@ -2068,7 +2069,7 @@ static DisasJumpType op_cksm(DisasContext *s, DisasOps *o) static DisasJumpType op_clc(DisasContext *s, DisasOps *o) { - int l = get_field(s->fields, l1); + int l = get_field(s, l1); TCGv_i32 vl; switch (l + 1) { @@ -2101,8 +2102,8 @@ static DisasJumpType op_clc(DisasContext *s, DisasOps *o) static DisasJumpType op_clcl(DisasContext *s, DisasOps *o) { - int r1 = get_field(s->fields, r1); - int r2 = get_field(s->fields, r2); + int r1 = get_field(s, r1); + int r2 = get_field(s, r2); TCGv_i32 t1, t2; /* r1 and r2 must be even. */ @@ -2122,8 +2123,8 @@ static DisasJumpType op_clcl(DisasContext *s, DisasOps *o) static DisasJumpType op_clcle(DisasContext *s, DisasOps *o) { - int r1 = get_field(s->fields, r1); - int r3 = get_field(s->fields, r3); + int r1 = get_field(s, r1); + int r3 = get_field(s, r3); TCGv_i32 t1, t3; /* r1 and r3 must be even. */ @@ -2143,8 +2144,8 @@ static DisasJumpType op_clcle(DisasContext *s, DisasOps *o) static DisasJumpType op_clclu(DisasContext *s, DisasOps *o) { - int r1 = get_field(s->fields, r1); - int r3 = get_field(s->fields, r3); + int r1 = get_field(s, r1); + int r3 = get_field(s, r3); TCGv_i32 t1, t3; /* r1 and r3 must be even. */ @@ -2164,7 +2165,7 @@ static DisasJumpType op_clclu(DisasContext *s, DisasOps *o) static DisasJumpType op_clm(DisasContext *s, DisasOps *o) { - TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); + TCGv_i32 m3 = tcg_const_i32(get_field(s, m3)); TCGv_i32 t1 = tcg_temp_new_i32(); tcg_gen_extrl_i64_i32(t1, o->in1); gen_helper_clm(cc_op, cpu_env, t1, m3, o->in2); @@ -2194,8 +2195,8 @@ static DisasJumpType op_cps(DisasContext *s, DisasOps *o) static DisasJumpType op_cs(DisasContext *s, DisasOps *o) { - int d2 = get_field(s->fields, d2); - int b2 = get_field(s->fields, b2); + int d2 = get_field(s, d2); + int b2 = get_field(s, b2); TCGv_i64 addr, cc; /* Note that in1 = R3 (new value) and @@ -2219,10 +2220,10 @@ static DisasJumpType op_cs(DisasContext *s, DisasOps *o) static DisasJumpType op_cdsg(DisasContext *s, DisasOps *o) { - int r1 = get_field(s->fields, r1); - int r3 = get_field(s->fields, r3); - int d2 = get_field(s->fields, d2); - int b2 = get_field(s->fields, b2); + int r1 = get_field(s, r1); + int r3 = get_field(s, r3); + int d2 = get_field(s, d2); + int b2 = get_field(s, b2); DisasJumpType ret = DISAS_NEXT; TCGv_i64 addr; TCGv_i32 t_r1, t_r3; @@ -2249,7 +2250,7 @@ static DisasJumpType op_cdsg(DisasContext *s, DisasOps *o) static DisasJumpType op_csst(DisasContext *s, DisasOps *o) { - int r3 = get_field(s->fields, r3); + int r3 = get_field(s, r3); TCGv_i32 t_r3 = tcg_const_i32(r3); if (tb_cflags(s->base.tb) & CF_PARALLEL) { @@ -2322,7 +2323,7 @@ static DisasJumpType op_cvd(DisasContext *s, DisasOps *o) static DisasJumpType op_ct(DisasContext *s, DisasOps *o) { - int m3 = get_field(s->fields, m3); + int m3 = get_field(s, m3); TCGLabel *lab = gen_new_label(); TCGCond c; @@ -2341,9 +2342,9 @@ static DisasJumpType op_ct(DisasContext *s, DisasOps *o) static DisasJumpType op_cuXX(DisasContext *s, DisasOps *o) { - int m3 = get_field(s->fields, m3); - int r1 = get_field(s->fields, r1); - int r2 = get_field(s->fields, r2); + int m3 = get_field(s, m3); + int r1 = get_field(s, r1); + int r2 = get_field(s, r2); TCGv_i32 tr1, tr2, chk; /* R1 and R2 must both be even. */ @@ -2392,9 +2393,9 @@ static DisasJumpType op_cuXX(DisasContext *s, DisasOps *o) #ifndef CONFIG_USER_ONLY static DisasJumpType op_diag(DisasContext *s, DisasOps *o) { - TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1)); - TCGv_i32 r3 = tcg_const_i32(get_field(s->fields, r3)); - TCGv_i32 func_code = tcg_const_i32(get_field(s->fields, i2)); + TCGv_i32 r1 = tcg_const_i32(get_field(s, r1)); + TCGv_i32 r3 = tcg_const_i32(get_field(s, r3)); + TCGv_i32 func_code = tcg_const_i32(get_field(s, i2)); gen_helper_diag(cpu_env, r1, r3, func_code); @@ -2454,7 +2455,7 @@ static DisasJumpType op_dxb(DisasContext *s, DisasOps *o) static DisasJumpType op_ear(DisasContext *s, DisasOps *o) { - int r2 = get_field(s->fields, r2); + int r2 = get_field(s, r2); tcg_gen_ld32u_i64(o->out, cpu_env, offsetof(CPUS390XState, aregs[r2])); return DISAS_NEXT; } @@ -2474,8 +2475,8 @@ static DisasJumpType op_efpc(DisasContext *s, DisasOps *o) static DisasJumpType op_epsw(DisasContext *s, DisasOps *o) { - int r1 = get_field(s->fields, r1); - int r2 = get_field(s->fields, r2); + int r1 = get_field(s, r1); + int r2 = get_field(s, r2); TCGv_i64 t = tcg_temp_new_i64(); /* Note the "subsequently" in the PoO, which implies a defined result @@ -2492,7 +2493,7 @@ static DisasJumpType op_epsw(DisasContext *s, DisasOps *o) static DisasJumpType op_ex(DisasContext *s, DisasOps *o) { - int r1 = get_field(s->fields, r1); + int r1 = get_field(s, r1); TCGv_i32 ilen; TCGv_i64 v1; @@ -2581,7 +2582,7 @@ static DisasJumpType op_flogr(DisasContext *s, DisasOps *o) static DisasJumpType op_icm(DisasContext *s, DisasOps *o) { - int m3 = get_field(s->fields, m3); + int m3 = get_field(s, m3); int pos, len, base = s->insn->data; TCGv_i64 tmp = tcg_temp_new_i64(); uint64_t ccm; @@ -2669,7 +2670,7 @@ static DisasJumpType op_idte(DisasContext *s, DisasOps *o) TCGv_i32 m4; if (s390_has_feat(S390_FEAT_LOCAL_TLB_CLEARING)) { - m4 = tcg_const_i32(get_field(s->fields, m4)); + m4 = tcg_const_i32(get_field(s, m4)); } else { m4 = tcg_const_i32(0); } @@ -2683,7 +2684,7 @@ static DisasJumpType op_ipte(DisasContext *s, DisasOps *o) TCGv_i32 m4; if (s390_has_feat(S390_FEAT_LOCAL_TLB_CLEARING)) { - m4 = tcg_const_i32(get_field(s->fields, m4)); + m4 = tcg_const_i32(get_field(s, m4)); } else { m4 = tcg_const_i32(0); } @@ -2701,9 +2702,9 @@ static DisasJumpType op_iske(DisasContext *s, DisasOps *o) static DisasJumpType op_msa(DisasContext *s, DisasOps *o) { - int r1 = have_field(s->fields, r1) ? get_field(s->fields, r1) : 0; - int r2 = have_field(s->fields, r2) ? get_field(s->fields, r2) : 0; - int r3 = have_field(s->fields, r3) ? get_field(s->fields, r3) : 0; + int r1 = have_field(s, r1) ? get_field(s, r1) : 0; + int r2 = have_field(s, r2) ? get_field(s, r2) : 0; + int r3 = have_field(s, r3) ? get_field(s, r3) : 0; TCGv_i32 t_r1, t_r2, t_r3, type; switch (s->insn->data) { @@ -2929,7 +2930,7 @@ static DisasJumpType op_ld64(DisasContext *s, DisasOps *o) static DisasJumpType op_lat(DisasContext *s, DisasOps *o) { TCGLabel *lab = gen_new_label(); - store_reg32_i64(get_field(s->fields, r1), o->in2); + store_reg32_i64(get_field(s, r1), o->in2); /* The value is stored even in case of trap. */ tcg_gen_brcondi_i64(TCG_COND_NE, o->in2, 0, lab); gen_trap(s); @@ -2951,7 +2952,7 @@ static DisasJumpType op_lgat(DisasContext *s, DisasOps *o) static DisasJumpType op_lfhat(DisasContext *s, DisasOps *o) { TCGLabel *lab = gen_new_label(); - store_reg32h_i64(get_field(s->fields, r1), o->in2); + store_reg32h_i64(get_field(s, r1), o->in2); /* The value is stored even in case of trap. */ tcg_gen_brcondi_i64(TCG_COND_NE, o->in2, 0, lab); gen_trap(s); @@ -2985,7 +2986,7 @@ static DisasJumpType op_loc(DisasContext *s, DisasOps *o) { DisasCompare c; - disas_jcc(s, &c, get_field(s->fields, m3)); + disas_jcc(s, &c, get_field(s, m3)); if (c.is_64) { tcg_gen_movcond_i64(c.cond, o->out, c.u.s64.a, c.u.s64.b, @@ -3014,8 +3015,8 @@ static DisasJumpType op_loc(DisasContext *s, DisasOps *o) #ifndef CONFIG_USER_ONLY static DisasJumpType op_lctl(DisasContext *s, DisasOps *o) { - TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1)); - TCGv_i32 r3 = tcg_const_i32(get_field(s->fields, r3)); + TCGv_i32 r1 = tcg_const_i32(get_field(s, r1)); + TCGv_i32 r3 = tcg_const_i32(get_field(s, r3)); gen_helper_lctl(cpu_env, r1, o->in2, r3); tcg_temp_free_i32(r1); tcg_temp_free_i32(r3); @@ -3025,8 +3026,8 @@ static DisasJumpType op_lctl(DisasContext *s, DisasOps *o) static DisasJumpType op_lctlg(DisasContext *s, DisasOps *o) { - TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1)); - TCGv_i32 r3 = tcg_const_i32(get_field(s->fields, r3)); + TCGv_i32 r1 = tcg_const_i32(get_field(s, r1)); + TCGv_i32 r3 = tcg_const_i32(get_field(s, r3)); gen_helper_lctlg(cpu_env, r1, o->in2, r3); tcg_temp_free_i32(r1); tcg_temp_free_i32(r3); @@ -3088,8 +3089,8 @@ static DisasJumpType op_lpswe(DisasContext *s, DisasOps *o) static DisasJumpType op_lam(DisasContext *s, DisasOps *o) { - TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1)); - TCGv_i32 r3 = tcg_const_i32(get_field(s->fields, r3)); + TCGv_i32 r1 = tcg_const_i32(get_field(s, r1)); + TCGv_i32 r3 = tcg_const_i32(get_field(s, r3)); gen_helper_lam(cpu_env, r1, o->in2, r3); tcg_temp_free_i32(r1); tcg_temp_free_i32(r3); @@ -3098,8 +3099,8 @@ static DisasJumpType op_lam(DisasContext *s, DisasOps *o) static DisasJumpType op_lm32(DisasContext *s, DisasOps *o) { - int r1 = get_field(s->fields, r1); - int r3 = get_field(s->fields, r3); + int r1 = get_field(s, r1); + int r3 = get_field(s, r3); TCGv_i64 t1, t2; /* Only one register to read. */ @@ -3144,8 +3145,8 @@ static DisasJumpType op_lm32(DisasContext *s, DisasOps *o) static DisasJumpType op_lmh(DisasContext *s, DisasOps *o) { - int r1 = get_field(s->fields, r1); - int r3 = get_field(s->fields, r3); + int r1 = get_field(s, r1); + int r3 = get_field(s, r3); TCGv_i64 t1, t2; /* Only one register to read. */ @@ -3190,8 +3191,8 @@ static DisasJumpType op_lmh(DisasContext *s, DisasOps *o) static DisasJumpType op_lm64(DisasContext *s, DisasOps *o) { - int r1 = get_field(s->fields, r1); - int r3 = get_field(s->fields, r3); + int r1 = get_field(s, r1); + int r3 = get_field(s, r3); TCGv_i64 t1, t2; /* Only one register to read. */ @@ -3243,8 +3244,8 @@ static DisasJumpType op_lpd(DisasContext *s, DisasOps *o) } /* In a serial context, perform the two loads ... */ - a1 = get_address(s, 0, get_field(s->fields, b1), get_field(s->fields, d1)); - a2 = get_address(s, 0, get_field(s->fields, b2), get_field(s->fields, d2)); + a1 = get_address(s, 0, get_field(s, b1), get_field(s, d1)); + a2 = get_address(s, 0, get_field(s, b2), get_field(s, d2)); tcg_gen_qemu_ld_i64(o->out, a1, get_mem_index(s), mop | MO_ALIGN); tcg_gen_qemu_ld_i64(o->out2, a2, get_mem_index(s), mop | MO_ALIGN); tcg_temp_free_i64(a1); @@ -3272,13 +3273,8 @@ static DisasJumpType op_lpq(DisasContext *s, DisasOps *o) #ifndef CONFIG_USER_ONLY static DisasJumpType op_lura(DisasContext *s, DisasOps *o) { - gen_helper_lura(o->out, cpu_env, o->in2); - return DISAS_NEXT; -} - -static DisasJumpType op_lurag(DisasContext *s, DisasOps *o) -{ - gen_helper_lurag(o->out, cpu_env, o->in2); + o->addr1 = get_address(s, 0, get_field(s, r2), 0); + tcg_gen_qemu_ld_tl(o->out, o->addr1, MMU_REAL_IDX, s->insn->data); return DISAS_NEXT; } #endif @@ -3291,9 +3287,9 @@ static DisasJumpType op_lzrb(DisasContext *s, DisasOps *o) static DisasJumpType op_lcbb(DisasContext *s, DisasOps *o) { - const int64_t block_size = (1ull << (get_field(s->fields, m3) + 6)); + const int64_t block_size = (1ull << (get_field(s, m3) + 6)); - if (get_field(s->fields, m3) > 6) { + if (get_field(s, m3) > 6) { gen_program_exception(s, PGM_SPECIFICATION); return DISAS_NORETURN; } @@ -3317,7 +3313,7 @@ static DisasJumpType op_mov2(DisasContext *s, DisasOps *o) static DisasJumpType op_mov2e(DisasContext *s, DisasOps *o) { - int b2 = get_field(s->fields, b2); + int b2 = get_field(s, b2); TCGv ar1 = tcg_temp_new_i64(); o->out = o->in2; @@ -3364,7 +3360,7 @@ static DisasJumpType op_movx(DisasContext *s, DisasOps *o) static DisasJumpType op_mvc(DisasContext *s, DisasOps *o) { - TCGv_i32 l = tcg_const_i32(get_field(s->fields, l1)); + TCGv_i32 l = tcg_const_i32(get_field(s, l1)); gen_helper_mvc(cpu_env, l, o->addr1, o->in2); tcg_temp_free_i32(l); return DISAS_NEXT; @@ -3372,7 +3368,7 @@ static DisasJumpType op_mvc(DisasContext *s, DisasOps *o) static DisasJumpType op_mvcin(DisasContext *s, DisasOps *o) { - TCGv_i32 l = tcg_const_i32(get_field(s->fields, l1)); + TCGv_i32 l = tcg_const_i32(get_field(s, l1)); gen_helper_mvcin(cpu_env, l, o->addr1, o->in2); tcg_temp_free_i32(l); return DISAS_NEXT; @@ -3380,8 +3376,8 @@ static DisasJumpType op_mvcin(DisasContext *s, DisasOps *o) static DisasJumpType op_mvcl(DisasContext *s, DisasOps *o) { - int r1 = get_field(s->fields, r1); - int r2 = get_field(s->fields, r2); + int r1 = get_field(s, r1); + int r2 = get_field(s, r2); TCGv_i32 t1, t2; /* r1 and r2 must be even. */ @@ -3401,8 +3397,8 @@ static DisasJumpType op_mvcl(DisasContext *s, DisasOps *o) static DisasJumpType op_mvcle(DisasContext *s, DisasOps *o) { - int r1 = get_field(s->fields, r1); - int r3 = get_field(s->fields, r3); + int r1 = get_field(s, r1); + int r3 = get_field(s, r3); TCGv_i32 t1, t3; /* r1 and r3 must be even. */ @@ -3422,8 +3418,8 @@ static DisasJumpType op_mvcle(DisasContext *s, DisasOps *o) static DisasJumpType op_mvclu(DisasContext *s, DisasOps *o) { - int r1 = get_field(s->fields, r1); - int r3 = get_field(s->fields, r3); + int r1 = get_field(s, r1); + int r3 = get_field(s, r3); TCGv_i32 t1, t3; /* r1 and r3 must be even. */ @@ -3443,7 +3439,7 @@ static DisasJumpType op_mvclu(DisasContext *s, DisasOps *o) static DisasJumpType op_mvcos(DisasContext *s, DisasOps *o) { - int r3 = get_field(s->fields, r3); + int r3 = get_field(s, r3); gen_helper_mvcos(cc_op, cpu_env, o->addr1, o->in2, regs[r3]); set_cc_static(s); return DISAS_NEXT; @@ -3452,7 +3448,7 @@ static DisasJumpType op_mvcos(DisasContext *s, DisasOps *o) #ifndef CONFIG_USER_ONLY static DisasJumpType op_mvcp(DisasContext *s, DisasOps *o) { - int r1 = get_field(s->fields, l1); + int r1 = get_field(s, l1); gen_helper_mvcp(cc_op, cpu_env, regs[r1], o->addr1, o->in2); set_cc_static(s); return DISAS_NEXT; @@ -3460,7 +3456,7 @@ static DisasJumpType op_mvcp(DisasContext *s, DisasOps *o) static DisasJumpType op_mvcs(DisasContext *s, DisasOps *o) { - int r1 = get_field(s->fields, l1); + int r1 = get_field(s, l1); gen_helper_mvcs(cc_op, cpu_env, regs[r1], o->addr1, o->in2); set_cc_static(s); return DISAS_NEXT; @@ -3469,7 +3465,7 @@ static DisasJumpType op_mvcs(DisasContext *s, DisasOps *o) static DisasJumpType op_mvn(DisasContext *s, DisasOps *o) { - TCGv_i32 l = tcg_const_i32(get_field(s->fields, l1)); + TCGv_i32 l = tcg_const_i32(get_field(s, l1)); gen_helper_mvn(cpu_env, l, o->addr1, o->in2); tcg_temp_free_i32(l); return DISAS_NEXT; @@ -3477,7 +3473,7 @@ static DisasJumpType op_mvn(DisasContext *s, DisasOps *o) static DisasJumpType op_mvo(DisasContext *s, DisasOps *o) { - TCGv_i32 l = tcg_const_i32(get_field(s->fields, l1)); + TCGv_i32 l = tcg_const_i32(get_field(s, l1)); gen_helper_mvo(cpu_env, l, o->addr1, o->in2); tcg_temp_free_i32(l); return DISAS_NEXT; @@ -3492,8 +3488,8 @@ static DisasJumpType op_mvpg(DisasContext *s, DisasOps *o) static DisasJumpType op_mvst(DisasContext *s, DisasOps *o) { - TCGv_i32 t1 = tcg_const_i32(get_field(s->fields, r1)); - TCGv_i32 t2 = tcg_const_i32(get_field(s->fields, r2)); + TCGv_i32 t1 = tcg_const_i32(get_field(s, r1)); + TCGv_i32 t2 = tcg_const_i32(get_field(s, r2)); gen_helper_mvst(cc_op, cpu_env, t1, t2); tcg_temp_free_i32(t1); @@ -3504,7 +3500,7 @@ static DisasJumpType op_mvst(DisasContext *s, DisasOps *o) static DisasJumpType op_mvz(DisasContext *s, DisasOps *o) { - TCGv_i32 l = tcg_const_i32(get_field(s->fields, l1)); + TCGv_i32 l = tcg_const_i32(get_field(s, l1)); gen_helper_mvz(cpu_env, l, o->addr1, o->in2); tcg_temp_free_i32(l); return DISAS_NEXT; @@ -3556,7 +3552,7 @@ static DisasJumpType op_mxdb(DisasContext *s, DisasOps *o) static DisasJumpType op_maeb(DisasContext *s, DisasOps *o) { - TCGv_i64 r3 = load_freg32_i64(get_field(s->fields, r3)); + TCGv_i64 r3 = load_freg32_i64(get_field(s, r3)); gen_helper_maeb(o->out, cpu_env, o->in1, o->in2, r3); tcg_temp_free_i64(r3); return DISAS_NEXT; @@ -3564,7 +3560,7 @@ static DisasJumpType op_maeb(DisasContext *s, DisasOps *o) static DisasJumpType op_madb(DisasContext *s, DisasOps *o) { - TCGv_i64 r3 = load_freg(get_field(s->fields, r3)); + TCGv_i64 r3 = load_freg(get_field(s, r3)); gen_helper_madb(o->out, cpu_env, o->in1, o->in2, r3); tcg_temp_free_i64(r3); return DISAS_NEXT; @@ -3572,7 +3568,7 @@ static DisasJumpType op_madb(DisasContext *s, DisasOps *o) static DisasJumpType op_mseb(DisasContext *s, DisasOps *o) { - TCGv_i64 r3 = load_freg32_i64(get_field(s->fields, r3)); + TCGv_i64 r3 = load_freg32_i64(get_field(s, r3)); gen_helper_mseb(o->out, cpu_env, o->in1, o->in2, r3); tcg_temp_free_i64(r3); return DISAS_NEXT; @@ -3580,7 +3576,7 @@ static DisasJumpType op_mseb(DisasContext *s, DisasOps *o) static DisasJumpType op_msdb(DisasContext *s, DisasOps *o) { - TCGv_i64 r3 = load_freg(get_field(s->fields, r3)); + TCGv_i64 r3 = load_freg(get_field(s, r3)); gen_helper_msdb(o->out, cpu_env, o->in1, o->in2, r3); tcg_temp_free_i64(r3); return DISAS_NEXT; @@ -3619,7 +3615,7 @@ static DisasJumpType op_nabsf128(DisasContext *s, DisasOps *o) static DisasJumpType op_nc(DisasContext *s, DisasOps *o) { - TCGv_i32 l = tcg_const_i32(get_field(s->fields, l1)); + TCGv_i32 l = tcg_const_i32(get_field(s, l1)); gen_helper_nc(cc_op, cpu_env, l, o->addr1, o->in2); tcg_temp_free_i32(l); set_cc_static(s); @@ -3653,7 +3649,7 @@ static DisasJumpType op_negf128(DisasContext *s, DisasOps *o) static DisasJumpType op_oc(DisasContext *s, DisasOps *o) { - TCGv_i32 l = tcg_const_i32(get_field(s->fields, l1)); + TCGv_i32 l = tcg_const_i32(get_field(s, l1)); gen_helper_oc(cc_op, cpu_env, l, o->addr1, o->in2); tcg_temp_free_i32(l); set_cc_static(s); @@ -3705,7 +3701,7 @@ static DisasJumpType op_oi(DisasContext *s, DisasOps *o) static DisasJumpType op_pack(DisasContext *s, DisasOps *o) { - TCGv_i32 l = tcg_const_i32(get_field(s->fields, l1)); + TCGv_i32 l = tcg_const_i32(get_field(s, l1)); gen_helper_pack(cpu_env, l, o->addr1, o->in2); tcg_temp_free_i32(l); return DISAS_NEXT; @@ -3713,7 +3709,7 @@ static DisasJumpType op_pack(DisasContext *s, DisasOps *o) static DisasJumpType op_pka(DisasContext *s, DisasOps *o) { - int l2 = get_field(s->fields, l2) + 1; + int l2 = get_field(s, l2) + 1; TCGv_i32 l; /* The length must not exceed 32 bytes. */ @@ -3729,7 +3725,7 @@ static DisasJumpType op_pka(DisasContext *s, DisasOps *o) static DisasJumpType op_pku(DisasContext *s, DisasOps *o) { - int l2 = get_field(s->fields, l2) + 1; + int l2 = get_field(s, l2) + 1; TCGv_i32 l; /* The length must be even and should not exceed 64 bytes. */ @@ -3759,15 +3755,15 @@ static DisasJumpType op_ptlb(DisasContext *s, DisasOps *o) static DisasJumpType op_risbg(DisasContext *s, DisasOps *o) { - int i3 = get_field(s->fields, i3); - int i4 = get_field(s->fields, i4); - int i5 = get_field(s->fields, i5); + int i3 = get_field(s, i3); + int i4 = get_field(s, i4); + int i5 = get_field(s, i5); int do_zero = i4 & 0x80; uint64_t mask, imask, pmask; int pos, len, rot; /* Adjust the arguments for the specific insn. */ - switch (s->fields->op2) { + switch (s->fields.op2) { case 0x55: /* risbg */ case 0x59: /* risbgn */ i3 &= 63; @@ -3808,7 +3804,7 @@ static DisasJumpType op_risbg(DisasContext *s, DisasOps *o) len = i4 - i3 + 1; pos = 63 - i4; rot = i5 & 63; - if (s->fields->op2 == 0x5d) { + if (s->fields.op2 == 0x5d) { pos += 32; } @@ -3849,9 +3845,9 @@ static DisasJumpType op_risbg(DisasContext *s, DisasOps *o) static DisasJumpType op_rosbg(DisasContext *s, DisasOps *o) { - int i3 = get_field(s->fields, i3); - int i4 = get_field(s->fields, i4); - int i5 = get_field(s->fields, i5); + int i3 = get_field(s, i3); + int i4 = get_field(s, i4); + int i5 = get_field(s, i5); uint64_t mask; /* If this is a test-only form, arrange to discard the result. */ @@ -3877,8 +3873,8 @@ static DisasJumpType op_rosbg(DisasContext *s, DisasOps *o) tcg_gen_rotli_i64(o->in2, o->in2, i5); /* Operate. */ - switch (s->fields->op2) { - case 0x55: /* AND */ + switch (s->fields.op2) { + case 0x54: /* AND */ tcg_gen_ori_i64(o->in2, o->in2, ~mask); tcg_gen_and_i64(o->out, o->out, o->in2); break; @@ -3992,7 +3988,7 @@ static DisasJumpType op_sam(DisasContext *s, DisasOps *o) static DisasJumpType op_sar(DisasContext *s, DisasOps *o) { - int r1 = get_field(s->fields, r1); + int r1 = get_field(s, r1); tcg_gen_st32_i64(o->in2, cpu_env, offsetof(CPUS390XState, aregs[r1])); return DISAS_NEXT; } @@ -4045,8 +4041,8 @@ static DisasJumpType op_servc(DisasContext *s, DisasOps *o) static DisasJumpType op_sigp(DisasContext *s, DisasOps *o) { - TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1)); - TCGv_i32 r3 = tcg_const_i32(get_field(s->fields, r3)); + TCGv_i32 r1 = tcg_const_i32(get_field(s, r1)); + TCGv_i32 r3 = tcg_const_i32(get_field(s, r3)); gen_helper_sigp(cc_op, cpu_env, o->in2, r1, r3); set_cc_static(s); tcg_temp_free_i32(r1); @@ -4062,7 +4058,7 @@ static DisasJumpType op_soc(DisasContext *s, DisasOps *o) TCGLabel *lab; int r1; - disas_jcc(s, &c, get_field(s->fields, m3)); + disas_jcc(s, &c, get_field(s, m3)); /* We want to store when the condition is fulfilled, so branch out when it's not */ @@ -4076,8 +4072,8 @@ static DisasJumpType op_soc(DisasContext *s, DisasOps *o) } free_compare(&c); - r1 = get_field(s->fields, r1); - a = get_address(s, 0, get_field(s->fields, b2), get_field(s->fields, d2)); + r1 = get_field(s, r1); + a = get_address(s, 0, get_field(s, b2), get_field(s, d2)); switch (s->insn->data) { case 1: /* STOCG */ tcg_gen_qemu_st64(regs[r1], a, get_mem_index(s)); @@ -4189,11 +4185,11 @@ static DisasJumpType op_spm(DisasContext *s, DisasOps *o) static DisasJumpType op_ectg(DisasContext *s, DisasOps *o) { - int b1 = get_field(s->fields, b1); - int d1 = get_field(s->fields, d1); - int b2 = get_field(s->fields, b2); - int d2 = get_field(s->fields, d2); - int r3 = get_field(s->fields, r3); + int b1 = get_field(s, b1); + int d1 = get_field(s, d1); + int b2 = get_field(s, b2); + int d2 = get_field(s, d2); + int r3 = get_field(s, r3); TCGv_i64 tmp = tcg_temp_new_i64(); /* fetch all operands first */ @@ -4309,8 +4305,8 @@ static DisasJumpType op_stckc(DisasContext *s, DisasOps *o) static DisasJumpType op_stctg(DisasContext *s, DisasOps *o) { - TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1)); - TCGv_i32 r3 = tcg_const_i32(get_field(s->fields, r3)); + TCGv_i32 r1 = tcg_const_i32(get_field(s, r1)); + TCGv_i32 r3 = tcg_const_i32(get_field(s, r3)); gen_helper_stctg(cpu_env, r1, o->in2, r3); tcg_temp_free_i32(r1); tcg_temp_free_i32(r3); @@ -4319,8 +4315,8 @@ static DisasJumpType op_stctg(DisasContext *s, DisasOps *o) static DisasJumpType op_stctl(DisasContext *s, DisasOps *o) { - TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1)); - TCGv_i32 r3 = tcg_const_i32(get_field(s->fields, r3)); + TCGv_i32 r1 = tcg_const_i32(get_field(s, r1)); + TCGv_i32 r3 = tcg_const_i32(get_field(s, r3)); gen_helper_stctl(cpu_env, r1, o->in2, r3); tcg_temp_free_i32(r1); tcg_temp_free_i32(r3); @@ -4482,7 +4478,7 @@ static DisasJumpType op_stpx(DisasContext *s, DisasOps *o) static DisasJumpType op_stnosm(DisasContext *s, DisasOps *o) { - uint64_t i2 = get_field(s->fields, i2); + uint64_t i2 = get_field(s, i2); TCGv_i64 t; /* It is important to do what the instruction name says: STORE THEN. @@ -4493,7 +4489,7 @@ static DisasJumpType op_stnosm(DisasContext *s, DisasOps *o) tcg_gen_qemu_st8(t, o->addr1, get_mem_index(s)); tcg_temp_free_i64(t); - if (s->fields->op == 0xac) { + if (s->fields.op == 0xac) { tcg_gen_andi_i64(psw_mask, psw_mask, (i2 << 56) | 0x00ffffffffffffffull); } else { @@ -4506,13 +4502,13 @@ static DisasJumpType op_stnosm(DisasContext *s, DisasOps *o) static DisasJumpType op_stura(DisasContext *s, DisasOps *o) { - gen_helper_stura(cpu_env, o->in2, o->in1); - return DISAS_NEXT; -} + o->addr1 = get_address(s, 0, get_field(s, r2), 0); + tcg_gen_qemu_st_tl(o->in1, o->addr1, MMU_REAL_IDX, s->insn->data); -static DisasJumpType op_sturg(DisasContext *s, DisasOps *o) -{ - gen_helper_sturg(cpu_env, o->in2, o->in1); + if (s->base.tb->flags & FLAG_MASK_PER) { + update_psw_addr(s); + gen_helper_per_store_real(cpu_env); + } return DISAS_NEXT; } #endif @@ -4550,8 +4546,8 @@ static DisasJumpType op_st64(DisasContext *s, DisasOps *o) static DisasJumpType op_stam(DisasContext *s, DisasOps *o) { - TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1)); - TCGv_i32 r3 = tcg_const_i32(get_field(s->fields, r3)); + TCGv_i32 r1 = tcg_const_i32(get_field(s, r1)); + TCGv_i32 r3 = tcg_const_i32(get_field(s, r3)); gen_helper_stam(cpu_env, r1, o->in2, r3); tcg_temp_free_i32(r1); tcg_temp_free_i32(r3); @@ -4560,7 +4556,7 @@ static DisasJumpType op_stam(DisasContext *s, DisasOps *o) static DisasJumpType op_stcm(DisasContext *s, DisasOps *o) { - int m3 = get_field(s->fields, m3); + int m3 = get_field(s, m3); int pos, base = s->insn->data; TCGv_i64 tmp = tcg_temp_new_i64(); @@ -4609,8 +4605,8 @@ static DisasJumpType op_stcm(DisasContext *s, DisasOps *o) static DisasJumpType op_stm(DisasContext *s, DisasOps *o) { - int r1 = get_field(s->fields, r1); - int r3 = get_field(s->fields, r3); + int r1 = get_field(s, r1); + int r3 = get_field(s, r3); int size = s->insn->data; TCGv_i64 tsize = tcg_const_i64(size); @@ -4633,8 +4629,8 @@ static DisasJumpType op_stm(DisasContext *s, DisasOps *o) static DisasJumpType op_stmh(DisasContext *s, DisasOps *o) { - int r1 = get_field(s->fields, r1); - int r3 = get_field(s->fields, r3); + int r1 = get_field(s, r1); + int r3 = get_field(s, r3); TCGv_i64 t = tcg_temp_new_i64(); TCGv_i64 t4 = tcg_const_i64(4); TCGv_i64 t32 = tcg_const_i64(32); @@ -4670,8 +4666,8 @@ static DisasJumpType op_stpq(DisasContext *s, DisasOps *o) static DisasJumpType op_srst(DisasContext *s, DisasOps *o) { - TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1)); - TCGv_i32 r2 = tcg_const_i32(get_field(s->fields, r2)); + TCGv_i32 r1 = tcg_const_i32(get_field(s, r1)); + TCGv_i32 r2 = tcg_const_i32(get_field(s, r2)); gen_helper_srst(cpu_env, r1, r2); @@ -4683,8 +4679,8 @@ static DisasJumpType op_srst(DisasContext *s, DisasOps *o) static DisasJumpType op_srstu(DisasContext *s, DisasOps *o) { - TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1)); - TCGv_i32 r2 = tcg_const_i32(get_field(s->fields, r2)); + TCGv_i32 r1 = tcg_const_i32(get_field(s, r1)); + TCGv_i32 r2 = tcg_const_i32(get_field(s, r2)); gen_helper_srstu(cpu_env, r1, r2); @@ -4733,7 +4729,7 @@ static DisasJumpType op_svc(DisasContext *s, DisasOps *o) update_psw_addr(s); update_cc_op(s); - t = tcg_const_i32(get_field(s->fields, i1) & 0xff); + t = tcg_const_i32(get_field(s, i1) & 0xff); tcg_gen_st_i32(t, cpu_env, offsetof(CPUS390XState, int_svc_code)); tcg_temp_free_i32(t); @@ -4796,7 +4792,7 @@ static DisasJumpType op_tprot(DisasContext *s, DisasOps *o) static DisasJumpType op_tp(DisasContext *s, DisasOps *o) { - TCGv_i32 l1 = tcg_const_i32(get_field(s->fields, l1) + 1); + TCGv_i32 l1 = tcg_const_i32(get_field(s, l1) + 1); gen_helper_tp(cc_op, cpu_env, o->addr1, l1); tcg_temp_free_i32(l1); set_cc_static(s); @@ -4805,7 +4801,7 @@ static DisasJumpType op_tp(DisasContext *s, DisasOps *o) static DisasJumpType op_tr(DisasContext *s, DisasOps *o) { - TCGv_i32 l = tcg_const_i32(get_field(s->fields, l1)); + TCGv_i32 l = tcg_const_i32(get_field(s, l1)); gen_helper_tr(cpu_env, l, o->addr1, o->in2); tcg_temp_free_i32(l); set_cc_static(s); @@ -4822,7 +4818,7 @@ static DisasJumpType op_tre(DisasContext *s, DisasOps *o) static DisasJumpType op_trt(DisasContext *s, DisasOps *o) { - TCGv_i32 l = tcg_const_i32(get_field(s->fields, l1)); + TCGv_i32 l = tcg_const_i32(get_field(s, l1)); gen_helper_trt(cc_op, cpu_env, l, o->addr1, o->in2); tcg_temp_free_i32(l); set_cc_static(s); @@ -4831,7 +4827,7 @@ static DisasJumpType op_trt(DisasContext *s, DisasOps *o) static DisasJumpType op_trtr(DisasContext *s, DisasOps *o) { - TCGv_i32 l = tcg_const_i32(get_field(s->fields, l1)); + TCGv_i32 l = tcg_const_i32(get_field(s, l1)); gen_helper_trtr(cc_op, cpu_env, l, o->addr1, o->in2); tcg_temp_free_i32(l); set_cc_static(s); @@ -4840,11 +4836,11 @@ static DisasJumpType op_trtr(DisasContext *s, DisasOps *o) static DisasJumpType op_trXX(DisasContext *s, DisasOps *o) { - TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1)); - TCGv_i32 r2 = tcg_const_i32(get_field(s->fields, r2)); + TCGv_i32 r1 = tcg_const_i32(get_field(s, r1)); + TCGv_i32 r2 = tcg_const_i32(get_field(s, r2)); TCGv_i32 sizes = tcg_const_i32(s->insn->opc & 3); TCGv_i32 tst = tcg_temp_new_i32(); - int m3 = get_field(s->fields, m3); + int m3 = get_field(s, m3); if (!s390_has_feat(S390_FEAT_ETF2_ENH)) { m3 = 0; @@ -4881,7 +4877,7 @@ static DisasJumpType op_ts(DisasContext *s, DisasOps *o) static DisasJumpType op_unpk(DisasContext *s, DisasOps *o) { - TCGv_i32 l = tcg_const_i32(get_field(s->fields, l1)); + TCGv_i32 l = tcg_const_i32(get_field(s, l1)); gen_helper_unpk(cpu_env, l, o->addr1, o->in2); tcg_temp_free_i32(l); return DISAS_NEXT; @@ -4889,7 +4885,7 @@ static DisasJumpType op_unpk(DisasContext *s, DisasOps *o) static DisasJumpType op_unpka(DisasContext *s, DisasOps *o) { - int l1 = get_field(s->fields, l1) + 1; + int l1 = get_field(s, l1) + 1; TCGv_i32 l; /* The length must not exceed 32 bytes. */ @@ -4906,7 +4902,7 @@ static DisasJumpType op_unpka(DisasContext *s, DisasOps *o) static DisasJumpType op_unpku(DisasContext *s, DisasOps *o) { - int l1 = get_field(s->fields, l1) + 1; + int l1 = get_field(s, l1) + 1; TCGv_i32 l; /* The length must be even and should not exceed 64 bytes. */ @@ -4924,11 +4920,11 @@ static DisasJumpType op_unpku(DisasContext *s, DisasOps *o) static DisasJumpType op_xc(DisasContext *s, DisasOps *o) { - int d1 = get_field(s->fields, d1); - int d2 = get_field(s->fields, d2); - int b1 = get_field(s->fields, b1); - int b2 = get_field(s->fields, b2); - int l = get_field(s->fields, l1); + int d1 = get_field(s, d1); + int d2 = get_field(s, d2); + int b1 = get_field(s, b1); + int b2 = get_field(s, b2); + int l = get_field(s, l1); TCGv_i32 t32; o->addr1 = get_address(s, 0, b1, d1); @@ -5035,7 +5031,7 @@ static DisasJumpType op_zero2(DisasContext *s, DisasOps *o) #ifndef CONFIG_USER_ONLY static DisasJumpType op_clp(DisasContext *s, DisasOps *o) { - TCGv_i32 r2 = tcg_const_i32(get_field(s->fields, r2)); + TCGv_i32 r2 = tcg_const_i32(get_field(s, r2)); gen_helper_clp(cpu_env, r2); tcg_temp_free_i32(r2); @@ -5045,8 +5041,8 @@ static DisasJumpType op_clp(DisasContext *s, DisasOps *o) static DisasJumpType op_pcilg(DisasContext *s, DisasOps *o) { - TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1)); - TCGv_i32 r2 = tcg_const_i32(get_field(s->fields, r2)); + TCGv_i32 r1 = tcg_const_i32(get_field(s, r1)); + TCGv_i32 r2 = tcg_const_i32(get_field(s, r2)); gen_helper_pcilg(cpu_env, r1, r2); tcg_temp_free_i32(r1); @@ -5057,8 +5053,8 @@ static DisasJumpType op_pcilg(DisasContext *s, DisasOps *o) static DisasJumpType op_pcistg(DisasContext *s, DisasOps *o) { - TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1)); - TCGv_i32 r2 = tcg_const_i32(get_field(s->fields, r2)); + TCGv_i32 r1 = tcg_const_i32(get_field(s, r1)); + TCGv_i32 r2 = tcg_const_i32(get_field(s, r2)); gen_helper_pcistg(cpu_env, r1, r2); tcg_temp_free_i32(r1); @@ -5069,8 +5065,8 @@ static DisasJumpType op_pcistg(DisasContext *s, DisasOps *o) static DisasJumpType op_stpcifc(DisasContext *s, DisasOps *o) { - TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1)); - TCGv_i32 ar = tcg_const_i32(get_field(s->fields, b2)); + TCGv_i32 r1 = tcg_const_i32(get_field(s, r1)); + TCGv_i32 ar = tcg_const_i32(get_field(s, b2)); gen_helper_stpcifc(cpu_env, r1, o->addr1, ar); tcg_temp_free_i32(ar); @@ -5087,8 +5083,8 @@ static DisasJumpType op_sic(DisasContext *s, DisasOps *o) static DisasJumpType op_rpcit(DisasContext *s, DisasOps *o) { - TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1)); - TCGv_i32 r2 = tcg_const_i32(get_field(s->fields, r2)); + TCGv_i32 r1 = tcg_const_i32(get_field(s, r1)); + TCGv_i32 r2 = tcg_const_i32(get_field(s, r2)); gen_helper_rpcit(cpu_env, r1, r2); tcg_temp_free_i32(r1); @@ -5099,9 +5095,9 @@ static DisasJumpType op_rpcit(DisasContext *s, DisasOps *o) static DisasJumpType op_pcistb(DisasContext *s, DisasOps *o) { - TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1)); - TCGv_i32 r3 = tcg_const_i32(get_field(s->fields, r3)); - TCGv_i32 ar = tcg_const_i32(get_field(s->fields, b2)); + TCGv_i32 r1 = tcg_const_i32(get_field(s, r1)); + TCGv_i32 r3 = tcg_const_i32(get_field(s, r3)); + TCGv_i32 ar = tcg_const_i32(get_field(s, b2)); gen_helper_pcistb(cpu_env, r1, r3, o->addr1, ar); tcg_temp_free_i32(ar); @@ -5113,8 +5109,8 @@ static DisasJumpType op_pcistb(DisasContext *s, DisasOps *o) static DisasJumpType op_mpcifc(DisasContext *s, DisasOps *o) { - TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1)); - TCGv_i32 ar = tcg_const_i32(get_field(s->fields, b2)); + TCGv_i32 r1 = tcg_const_i32(get_field(s, r1)); + TCGv_i32 ar = tcg_const_i32(get_field(s, b2)); gen_helper_mpcifc(cpu_env, r1, o->addr1, ar); tcg_temp_free_i32(ar); @@ -5293,29 +5289,29 @@ static void cout_tm64(DisasContext *s, DisasOps *o) the "wout" generators, in some cases we need a new temporary, and in some cases we can write to a TCG global. */ -static void prep_new(DisasContext *s, DisasFields *f, DisasOps *o) +static void prep_new(DisasContext *s, DisasOps *o) { o->out = tcg_temp_new_i64(); } #define SPEC_prep_new 0 -static void prep_new_P(DisasContext *s, DisasFields *f, DisasOps *o) +static void prep_new_P(DisasContext *s, DisasOps *o) { o->out = tcg_temp_new_i64(); o->out2 = tcg_temp_new_i64(); } #define SPEC_prep_new_P 0 -static void prep_r1(DisasContext *s, DisasFields *f, DisasOps *o) +static void prep_r1(DisasContext *s, DisasOps *o) { - o->out = regs[get_field(f, r1)]; + o->out = regs[get_field(s, r1)]; o->g_out = true; } #define SPEC_prep_r1 0 -static void prep_r1_P(DisasContext *s, DisasFields *f, DisasOps *o) +static void prep_r1_P(DisasContext *s, DisasOps *o) { - int r1 = get_field(f, r1); + int r1 = get_field(s, r1); o->out = regs[r1]; o->out2 = regs[r1 + 1]; o->g_out = o->g_out2 = true; @@ -5323,10 +5319,10 @@ static void prep_r1_P(DisasContext *s, DisasFields *f, DisasOps *o) #define SPEC_prep_r1_P SPEC_r1_even /* Whenever we need x1 in addition to other inputs, we'll load it to out/out2 */ -static void prep_x1(DisasContext *s, DisasFields *f, DisasOps *o) +static void prep_x1(DisasContext *s, DisasOps *o) { - o->out = load_freg(get_field(f, r1)); - o->out2 = load_freg(get_field(f, r1) + 2); + o->out = load_freg(get_field(s, r1)); + o->out2 = load_freg(get_field(s, r1) + 2); } #define SPEC_prep_x1 SPEC_r1_f128 @@ -5336,367 +5332,367 @@ static void prep_x1(DisasContext *s, DisasFields *f, DisasOps *o) generally handled by having a "prep" generator install the TCG global as the destination of the operation. */ -static void wout_r1(DisasContext *s, DisasFields *f, DisasOps *o) +static void wout_r1(DisasContext *s, DisasOps *o) { - store_reg(get_field(f, r1), o->out); + store_reg(get_field(s, r1), o->out); } #define SPEC_wout_r1 0 -static void wout_r1_8(DisasContext *s, DisasFields *f, DisasOps *o) +static void wout_r1_8(DisasContext *s, DisasOps *o) { - int r1 = get_field(f, r1); + int r1 = get_field(s, r1); tcg_gen_deposit_i64(regs[r1], regs[r1], o->out, 0, 8); } #define SPEC_wout_r1_8 0 -static void wout_r1_16(DisasContext *s, DisasFields *f, DisasOps *o) +static void wout_r1_16(DisasContext *s, DisasOps *o) { - int r1 = get_field(f, r1); + int r1 = get_field(s, r1); tcg_gen_deposit_i64(regs[r1], regs[r1], o->out, 0, 16); } #define SPEC_wout_r1_16 0 -static void wout_r1_32(DisasContext *s, DisasFields *f, DisasOps *o) +static void wout_r1_32(DisasContext *s, DisasOps *o) { - store_reg32_i64(get_field(f, r1), o->out); + store_reg32_i64(get_field(s, r1), o->out); } #define SPEC_wout_r1_32 0 -static void wout_r1_32h(DisasContext *s, DisasFields *f, DisasOps *o) +static void wout_r1_32h(DisasContext *s, DisasOps *o) { - store_reg32h_i64(get_field(f, r1), o->out); + store_reg32h_i64(get_field(s, r1), o->out); } #define SPEC_wout_r1_32h 0 -static void wout_r1_P32(DisasContext *s, DisasFields *f, DisasOps *o) +static void wout_r1_P32(DisasContext *s, DisasOps *o) { - int r1 = get_field(f, r1); + int r1 = get_field(s, r1); store_reg32_i64(r1, o->out); store_reg32_i64(r1 + 1, o->out2); } #define SPEC_wout_r1_P32 SPEC_r1_even -static void wout_r1_D32(DisasContext *s, DisasFields *f, DisasOps *o) +static void wout_r1_D32(DisasContext *s, DisasOps *o) { - int r1 = get_field(f, r1); + int r1 = get_field(s, r1); store_reg32_i64(r1 + 1, o->out); tcg_gen_shri_i64(o->out, o->out, 32); store_reg32_i64(r1, o->out); } #define SPEC_wout_r1_D32 SPEC_r1_even -static void wout_r3_P32(DisasContext *s, DisasFields *f, DisasOps *o) +static void wout_r3_P32(DisasContext *s, DisasOps *o) { - int r3 = get_field(f, r3); + int r3 = get_field(s, r3); store_reg32_i64(r3, o->out); store_reg32_i64(r3 + 1, o->out2); } #define SPEC_wout_r3_P32 SPEC_r3_even -static void wout_r3_P64(DisasContext *s, DisasFields *f, DisasOps *o) +static void wout_r3_P64(DisasContext *s, DisasOps *o) { - int r3 = get_field(f, r3); + int r3 = get_field(s, r3); store_reg(r3, o->out); store_reg(r3 + 1, o->out2); } #define SPEC_wout_r3_P64 SPEC_r3_even -static void wout_e1(DisasContext *s, DisasFields *f, DisasOps *o) +static void wout_e1(DisasContext *s, DisasOps *o) { - store_freg32_i64(get_field(f, r1), o->out); + store_freg32_i64(get_field(s, r1), o->out); } #define SPEC_wout_e1 0 -static void wout_f1(DisasContext *s, DisasFields *f, DisasOps *o) +static void wout_f1(DisasContext *s, DisasOps *o) { - store_freg(get_field(f, r1), o->out); + store_freg(get_field(s, r1), o->out); } #define SPEC_wout_f1 0 -static void wout_x1(DisasContext *s, DisasFields *f, DisasOps *o) +static void wout_x1(DisasContext *s, DisasOps *o) { - int f1 = get_field(s->fields, r1); + int f1 = get_field(s, r1); store_freg(f1, o->out); store_freg(f1 + 2, o->out2); } #define SPEC_wout_x1 SPEC_r1_f128 -static void wout_cond_r1r2_32(DisasContext *s, DisasFields *f, DisasOps *o) +static void wout_cond_r1r2_32(DisasContext *s, DisasOps *o) { - if (get_field(f, r1) != get_field(f, r2)) { - store_reg32_i64(get_field(f, r1), o->out); + if (get_field(s, r1) != get_field(s, r2)) { + store_reg32_i64(get_field(s, r1), o->out); } } #define SPEC_wout_cond_r1r2_32 0 -static void wout_cond_e1e2(DisasContext *s, DisasFields *f, DisasOps *o) +static void wout_cond_e1e2(DisasContext *s, DisasOps *o) { - if (get_field(f, r1) != get_field(f, r2)) { - store_freg32_i64(get_field(f, r1), o->out); + if (get_field(s, r1) != get_field(s, r2)) { + store_freg32_i64(get_field(s, r1), o->out); } } #define SPEC_wout_cond_e1e2 0 -static void wout_m1_8(DisasContext *s, DisasFields *f, DisasOps *o) +static void wout_m1_8(DisasContext *s, DisasOps *o) { tcg_gen_qemu_st8(o->out, o->addr1, get_mem_index(s)); } #define SPEC_wout_m1_8 0 -static void wout_m1_16(DisasContext *s, DisasFields *f, DisasOps *o) +static void wout_m1_16(DisasContext *s, DisasOps *o) { tcg_gen_qemu_st16(o->out, o->addr1, get_mem_index(s)); } #define SPEC_wout_m1_16 0 #ifndef CONFIG_USER_ONLY -static void wout_m1_16a(DisasContext *s, DisasFields *f, DisasOps *o) +static void wout_m1_16a(DisasContext *s, DisasOps *o) { tcg_gen_qemu_st_tl(o->out, o->addr1, get_mem_index(s), MO_TEUW | MO_ALIGN); } #define SPEC_wout_m1_16a 0 #endif -static void wout_m1_32(DisasContext *s, DisasFields *f, DisasOps *o) +static void wout_m1_32(DisasContext *s, DisasOps *o) { tcg_gen_qemu_st32(o->out, o->addr1, get_mem_index(s)); } #define SPEC_wout_m1_32 0 #ifndef CONFIG_USER_ONLY -static void wout_m1_32a(DisasContext *s, DisasFields *f, DisasOps *o) +static void wout_m1_32a(DisasContext *s, DisasOps *o) { tcg_gen_qemu_st_tl(o->out, o->addr1, get_mem_index(s), MO_TEUL | MO_ALIGN); } #define SPEC_wout_m1_32a 0 #endif -static void wout_m1_64(DisasContext *s, DisasFields *f, DisasOps *o) +static void wout_m1_64(DisasContext *s, DisasOps *o) { tcg_gen_qemu_st64(o->out, o->addr1, get_mem_index(s)); } #define SPEC_wout_m1_64 0 #ifndef CONFIG_USER_ONLY -static void wout_m1_64a(DisasContext *s, DisasFields *f, DisasOps *o) +static void wout_m1_64a(DisasContext *s, DisasOps *o) { tcg_gen_qemu_st_i64(o->out, o->addr1, get_mem_index(s), MO_TEQ | MO_ALIGN); } #define SPEC_wout_m1_64a 0 #endif -static void wout_m2_32(DisasContext *s, DisasFields *f, DisasOps *o) +static void wout_m2_32(DisasContext *s, DisasOps *o) { tcg_gen_qemu_st32(o->out, o->in2, get_mem_index(s)); } #define SPEC_wout_m2_32 0 -static void wout_in2_r1(DisasContext *s, DisasFields *f, DisasOps *o) +static void wout_in2_r1(DisasContext *s, DisasOps *o) { - store_reg(get_field(f, r1), o->in2); + store_reg(get_field(s, r1), o->in2); } #define SPEC_wout_in2_r1 0 -static void wout_in2_r1_32(DisasContext *s, DisasFields *f, DisasOps *o) +static void wout_in2_r1_32(DisasContext *s, DisasOps *o) { - store_reg32_i64(get_field(f, r1), o->in2); + store_reg32_i64(get_field(s, r1), o->in2); } #define SPEC_wout_in2_r1_32 0 /* ====================================================================== */ /* The "INput 1" generators. These load the first operand to an insn. */ -static void in1_r1(DisasContext *s, DisasFields *f, DisasOps *o) +static void in1_r1(DisasContext *s, DisasOps *o) { - o->in1 = load_reg(get_field(f, r1)); + o->in1 = load_reg(get_field(s, r1)); } #define SPEC_in1_r1 0 -static void in1_r1_o(DisasContext *s, DisasFields *f, DisasOps *o) +static void in1_r1_o(DisasContext *s, DisasOps *o) { - o->in1 = regs[get_field(f, r1)]; + o->in1 = regs[get_field(s, r1)]; o->g_in1 = true; } #define SPEC_in1_r1_o 0 -static void in1_r1_32s(DisasContext *s, DisasFields *f, DisasOps *o) +static void in1_r1_32s(DisasContext *s, DisasOps *o) { o->in1 = tcg_temp_new_i64(); - tcg_gen_ext32s_i64(o->in1, regs[get_field(f, r1)]); + tcg_gen_ext32s_i64(o->in1, regs[get_field(s, r1)]); } #define SPEC_in1_r1_32s 0 -static void in1_r1_32u(DisasContext *s, DisasFields *f, DisasOps *o) +static void in1_r1_32u(DisasContext *s, DisasOps *o) { o->in1 = tcg_temp_new_i64(); - tcg_gen_ext32u_i64(o->in1, regs[get_field(f, r1)]); + tcg_gen_ext32u_i64(o->in1, regs[get_field(s, r1)]); } #define SPEC_in1_r1_32u 0 -static void in1_r1_sr32(DisasContext *s, DisasFields *f, DisasOps *o) +static void in1_r1_sr32(DisasContext *s, DisasOps *o) { o->in1 = tcg_temp_new_i64(); - tcg_gen_shri_i64(o->in1, regs[get_field(f, r1)], 32); + tcg_gen_shri_i64(o->in1, regs[get_field(s, r1)], 32); } #define SPEC_in1_r1_sr32 0 -static void in1_r1p1(DisasContext *s, DisasFields *f, DisasOps *o) +static void in1_r1p1(DisasContext *s, DisasOps *o) { - o->in1 = load_reg(get_field(f, r1) + 1); + o->in1 = load_reg(get_field(s, r1) + 1); } #define SPEC_in1_r1p1 SPEC_r1_even -static void in1_r1p1_32s(DisasContext *s, DisasFields *f, DisasOps *o) +static void in1_r1p1_32s(DisasContext *s, DisasOps *o) { o->in1 = tcg_temp_new_i64(); - tcg_gen_ext32s_i64(o->in1, regs[get_field(f, r1) + 1]); + tcg_gen_ext32s_i64(o->in1, regs[get_field(s, r1) + 1]); } #define SPEC_in1_r1p1_32s SPEC_r1_even -static void in1_r1p1_32u(DisasContext *s, DisasFields *f, DisasOps *o) +static void in1_r1p1_32u(DisasContext *s, DisasOps *o) { o->in1 = tcg_temp_new_i64(); - tcg_gen_ext32u_i64(o->in1, regs[get_field(f, r1) + 1]); + tcg_gen_ext32u_i64(o->in1, regs[get_field(s, r1) + 1]); } #define SPEC_in1_r1p1_32u SPEC_r1_even -static void in1_r1_D32(DisasContext *s, DisasFields *f, DisasOps *o) +static void in1_r1_D32(DisasContext *s, DisasOps *o) { - int r1 = get_field(f, r1); + int r1 = get_field(s, r1); o->in1 = tcg_temp_new_i64(); tcg_gen_concat32_i64(o->in1, regs[r1 + 1], regs[r1]); } #define SPEC_in1_r1_D32 SPEC_r1_even -static void in1_r2(DisasContext *s, DisasFields *f, DisasOps *o) +static void in1_r2(DisasContext *s, DisasOps *o) { - o->in1 = load_reg(get_field(f, r2)); + o->in1 = load_reg(get_field(s, r2)); } #define SPEC_in1_r2 0 -static void in1_r2_sr32(DisasContext *s, DisasFields *f, DisasOps *o) +static void in1_r2_sr32(DisasContext *s, DisasOps *o) { o->in1 = tcg_temp_new_i64(); - tcg_gen_shri_i64(o->in1, regs[get_field(f, r2)], 32); + tcg_gen_shri_i64(o->in1, regs[get_field(s, r2)], 32); } #define SPEC_in1_r2_sr32 0 -static void in1_r3(DisasContext *s, DisasFields *f, DisasOps *o) +static void in1_r3(DisasContext *s, DisasOps *o) { - o->in1 = load_reg(get_field(f, r3)); + o->in1 = load_reg(get_field(s, r3)); } #define SPEC_in1_r3 0 -static void in1_r3_o(DisasContext *s, DisasFields *f, DisasOps *o) +static void in1_r3_o(DisasContext *s, DisasOps *o) { - o->in1 = regs[get_field(f, r3)]; + o->in1 = regs[get_field(s, r3)]; o->g_in1 = true; } #define SPEC_in1_r3_o 0 -static void in1_r3_32s(DisasContext *s, DisasFields *f, DisasOps *o) +static void in1_r3_32s(DisasContext *s, DisasOps *o) { o->in1 = tcg_temp_new_i64(); - tcg_gen_ext32s_i64(o->in1, regs[get_field(f, r3)]); + tcg_gen_ext32s_i64(o->in1, regs[get_field(s, r3)]); } #define SPEC_in1_r3_32s 0 -static void in1_r3_32u(DisasContext *s, DisasFields *f, DisasOps *o) +static void in1_r3_32u(DisasContext *s, DisasOps *o) { o->in1 = tcg_temp_new_i64(); - tcg_gen_ext32u_i64(o->in1, regs[get_field(f, r3)]); + tcg_gen_ext32u_i64(o->in1, regs[get_field(s, r3)]); } #define SPEC_in1_r3_32u 0 -static void in1_r3_D32(DisasContext *s, DisasFields *f, DisasOps *o) +static void in1_r3_D32(DisasContext *s, DisasOps *o) { - int r3 = get_field(f, r3); + int r3 = get_field(s, r3); o->in1 = tcg_temp_new_i64(); tcg_gen_concat32_i64(o->in1, regs[r3 + 1], regs[r3]); } #define SPEC_in1_r3_D32 SPEC_r3_even -static void in1_e1(DisasContext *s, DisasFields *f, DisasOps *o) +static void in1_e1(DisasContext *s, DisasOps *o) { - o->in1 = load_freg32_i64(get_field(f, r1)); + o->in1 = load_freg32_i64(get_field(s, r1)); } #define SPEC_in1_e1 0 -static void in1_f1(DisasContext *s, DisasFields *f, DisasOps *o) +static void in1_f1(DisasContext *s, DisasOps *o) { - o->in1 = load_freg(get_field(f, r1)); + o->in1 = load_freg(get_field(s, r1)); } #define SPEC_in1_f1 0 /* Load the high double word of an extended (128-bit) format FP number */ -static void in1_x2h(DisasContext *s, DisasFields *f, DisasOps *o) +static void in1_x2h(DisasContext *s, DisasOps *o) { - o->in1 = load_freg(get_field(f, r2)); + o->in1 = load_freg(get_field(s, r2)); } #define SPEC_in1_x2h SPEC_r2_f128 -static void in1_f3(DisasContext *s, DisasFields *f, DisasOps *o) +static void in1_f3(DisasContext *s, DisasOps *o) { - o->in1 = load_freg(get_field(f, r3)); + o->in1 = load_freg(get_field(s, r3)); } #define SPEC_in1_f3 0 -static void in1_la1(DisasContext *s, DisasFields *f, DisasOps *o) +static void in1_la1(DisasContext *s, DisasOps *o) { - o->addr1 = get_address(s, 0, get_field(f, b1), get_field(f, d1)); + o->addr1 = get_address(s, 0, get_field(s, b1), get_field(s, d1)); } #define SPEC_in1_la1 0 -static void in1_la2(DisasContext *s, DisasFields *f, DisasOps *o) +static void in1_la2(DisasContext *s, DisasOps *o) { - int x2 = have_field(f, x2) ? get_field(f, x2) : 0; - o->addr1 = get_address(s, x2, get_field(f, b2), get_field(f, d2)); + int x2 = have_field(s, x2) ? get_field(s, x2) : 0; + o->addr1 = get_address(s, x2, get_field(s, b2), get_field(s, d2)); } #define SPEC_in1_la2 0 -static void in1_m1_8u(DisasContext *s, DisasFields *f, DisasOps *o) +static void in1_m1_8u(DisasContext *s, DisasOps *o) { - in1_la1(s, f, o); + in1_la1(s, o); o->in1 = tcg_temp_new_i64(); tcg_gen_qemu_ld8u(o->in1, o->addr1, get_mem_index(s)); } #define SPEC_in1_m1_8u 0 -static void in1_m1_16s(DisasContext *s, DisasFields *f, DisasOps *o) +static void in1_m1_16s(DisasContext *s, DisasOps *o) { - in1_la1(s, f, o); + in1_la1(s, o); o->in1 = tcg_temp_new_i64(); tcg_gen_qemu_ld16s(o->in1, o->addr1, get_mem_index(s)); } #define SPEC_in1_m1_16s 0 -static void in1_m1_16u(DisasContext *s, DisasFields *f, DisasOps *o) +static void in1_m1_16u(DisasContext *s, DisasOps *o) { - in1_la1(s, f, o); + in1_la1(s, o); o->in1 = tcg_temp_new_i64(); tcg_gen_qemu_ld16u(o->in1, o->addr1, get_mem_index(s)); } #define SPEC_in1_m1_16u 0 -static void in1_m1_32s(DisasContext *s, DisasFields *f, DisasOps *o) +static void in1_m1_32s(DisasContext *s, DisasOps *o) { - in1_la1(s, f, o); + in1_la1(s, o); o->in1 = tcg_temp_new_i64(); tcg_gen_qemu_ld32s(o->in1, o->addr1, get_mem_index(s)); } #define SPEC_in1_m1_32s 0 -static void in1_m1_32u(DisasContext *s, DisasFields *f, DisasOps *o) +static void in1_m1_32u(DisasContext *s, DisasOps *o) { - in1_la1(s, f, o); + in1_la1(s, o); o->in1 = tcg_temp_new_i64(); tcg_gen_qemu_ld32u(o->in1, o->addr1, get_mem_index(s)); } #define SPEC_in1_m1_32u 0 -static void in1_m1_64(DisasContext *s, DisasFields *f, DisasOps *o) +static void in1_m1_64(DisasContext *s, DisasOps *o) { - in1_la1(s, f, o); + in1_la1(s, o); o->in1 = tcg_temp_new_i64(); tcg_gen_qemu_ld64(o->in1, o->addr1, get_mem_index(s)); } @@ -5705,306 +5701,306 @@ static void in1_m1_64(DisasContext *s, DisasFields *f, DisasOps *o) /* ====================================================================== */ /* The "INput 2" generators. These load the second operand to an insn. */ -static void in2_r1_o(DisasContext *s, DisasFields *f, DisasOps *o) +static void in2_r1_o(DisasContext *s, DisasOps *o) { - o->in2 = regs[get_field(f, r1)]; + o->in2 = regs[get_field(s, r1)]; o->g_in2 = true; } #define SPEC_in2_r1_o 0 -static void in2_r1_16u(DisasContext *s, DisasFields *f, DisasOps *o) +static void in2_r1_16u(DisasContext *s, DisasOps *o) { o->in2 = tcg_temp_new_i64(); - tcg_gen_ext16u_i64(o->in2, regs[get_field(f, r1)]); + tcg_gen_ext16u_i64(o->in2, regs[get_field(s, r1)]); } #define SPEC_in2_r1_16u 0 -static void in2_r1_32u(DisasContext *s, DisasFields *f, DisasOps *o) +static void in2_r1_32u(DisasContext *s, DisasOps *o) { o->in2 = tcg_temp_new_i64(); - tcg_gen_ext32u_i64(o->in2, regs[get_field(f, r1)]); + tcg_gen_ext32u_i64(o->in2, regs[get_field(s, r1)]); } #define SPEC_in2_r1_32u 0 -static void in2_r1_D32(DisasContext *s, DisasFields *f, DisasOps *o) +static void in2_r1_D32(DisasContext *s, DisasOps *o) { - int r1 = get_field(f, r1); + int r1 = get_field(s, r1); o->in2 = tcg_temp_new_i64(); tcg_gen_concat32_i64(o->in2, regs[r1 + 1], regs[r1]); } #define SPEC_in2_r1_D32 SPEC_r1_even -static void in2_r2(DisasContext *s, DisasFields *f, DisasOps *o) +static void in2_r2(DisasContext *s, DisasOps *o) { - o->in2 = load_reg(get_field(f, r2)); + o->in2 = load_reg(get_field(s, r2)); } #define SPEC_in2_r2 0 -static void in2_r2_o(DisasContext *s, DisasFields *f, DisasOps *o) +static void in2_r2_o(DisasContext *s, DisasOps *o) { - o->in2 = regs[get_field(f, r2)]; + o->in2 = regs[get_field(s, r2)]; o->g_in2 = true; } #define SPEC_in2_r2_o 0 -static void in2_r2_nz(DisasContext *s, DisasFields *f, DisasOps *o) +static void in2_r2_nz(DisasContext *s, DisasOps *o) { - int r2 = get_field(f, r2); + int r2 = get_field(s, r2); if (r2 != 0) { o->in2 = load_reg(r2); } } #define SPEC_in2_r2_nz 0 -static void in2_r2_8s(DisasContext *s, DisasFields *f, DisasOps *o) +static void in2_r2_8s(DisasContext *s, DisasOps *o) { o->in2 = tcg_temp_new_i64(); - tcg_gen_ext8s_i64(o->in2, regs[get_field(f, r2)]); + tcg_gen_ext8s_i64(o->in2, regs[get_field(s, r2)]); } #define SPEC_in2_r2_8s 0 -static void in2_r2_8u(DisasContext *s, DisasFields *f, DisasOps *o) +static void in2_r2_8u(DisasContext *s, DisasOps *o) { o->in2 = tcg_temp_new_i64(); - tcg_gen_ext8u_i64(o->in2, regs[get_field(f, r2)]); + tcg_gen_ext8u_i64(o->in2, regs[get_field(s, r2)]); } #define SPEC_in2_r2_8u 0 -static void in2_r2_16s(DisasContext *s, DisasFields *f, DisasOps *o) +static void in2_r2_16s(DisasContext *s, DisasOps *o) { o->in2 = tcg_temp_new_i64(); - tcg_gen_ext16s_i64(o->in2, regs[get_field(f, r2)]); + tcg_gen_ext16s_i64(o->in2, regs[get_field(s, r2)]); } #define SPEC_in2_r2_16s 0 -static void in2_r2_16u(DisasContext *s, DisasFields *f, DisasOps *o) +static void in2_r2_16u(DisasContext *s, DisasOps *o) { o->in2 = tcg_temp_new_i64(); - tcg_gen_ext16u_i64(o->in2, regs[get_field(f, r2)]); + tcg_gen_ext16u_i64(o->in2, regs[get_field(s, r2)]); } #define SPEC_in2_r2_16u 0 -static void in2_r3(DisasContext *s, DisasFields *f, DisasOps *o) +static void in2_r3(DisasContext *s, DisasOps *o) { - o->in2 = load_reg(get_field(f, r3)); + o->in2 = load_reg(get_field(s, r3)); } #define SPEC_in2_r3 0 -static void in2_r3_sr32(DisasContext *s, DisasFields *f, DisasOps *o) +static void in2_r3_sr32(DisasContext *s, DisasOps *o) { o->in2 = tcg_temp_new_i64(); - tcg_gen_shri_i64(o->in2, regs[get_field(f, r3)], 32); + tcg_gen_shri_i64(o->in2, regs[get_field(s, r3)], 32); } #define SPEC_in2_r3_sr32 0 -static void in2_r3_32u(DisasContext *s, DisasFields *f, DisasOps *o) +static void in2_r3_32u(DisasContext *s, DisasOps *o) { o->in2 = tcg_temp_new_i64(); - tcg_gen_ext32u_i64(o->in2, regs[get_field(f, r3)]); + tcg_gen_ext32u_i64(o->in2, regs[get_field(s, r3)]); } #define SPEC_in2_r3_32u 0 -static void in2_r2_32s(DisasContext *s, DisasFields *f, DisasOps *o) +static void in2_r2_32s(DisasContext *s, DisasOps *o) { o->in2 = tcg_temp_new_i64(); - tcg_gen_ext32s_i64(o->in2, regs[get_field(f, r2)]); + tcg_gen_ext32s_i64(o->in2, regs[get_field(s, r2)]); } #define SPEC_in2_r2_32s 0 -static void in2_r2_32u(DisasContext *s, DisasFields *f, DisasOps *o) +static void in2_r2_32u(DisasContext *s, DisasOps *o) { o->in2 = tcg_temp_new_i64(); - tcg_gen_ext32u_i64(o->in2, regs[get_field(f, r2)]); + tcg_gen_ext32u_i64(o->in2, regs[get_field(s, r2)]); } #define SPEC_in2_r2_32u 0 -static void in2_r2_sr32(DisasContext *s, DisasFields *f, DisasOps *o) +static void in2_r2_sr32(DisasContext *s, DisasOps *o) { o->in2 = tcg_temp_new_i64(); - tcg_gen_shri_i64(o->in2, regs[get_field(f, r2)], 32); + tcg_gen_shri_i64(o->in2, regs[get_field(s, r2)], 32); } #define SPEC_in2_r2_sr32 0 -static void in2_e2(DisasContext *s, DisasFields *f, DisasOps *o) +static void in2_e2(DisasContext *s, DisasOps *o) { - o->in2 = load_freg32_i64(get_field(f, r2)); + o->in2 = load_freg32_i64(get_field(s, r2)); } #define SPEC_in2_e2 0 -static void in2_f2(DisasContext *s, DisasFields *f, DisasOps *o) +static void in2_f2(DisasContext *s, DisasOps *o) { - o->in2 = load_freg(get_field(f, r2)); + o->in2 = load_freg(get_field(s, r2)); } #define SPEC_in2_f2 0 /* Load the low double word of an extended (128-bit) format FP number */ -static void in2_x2l(DisasContext *s, DisasFields *f, DisasOps *o) +static void in2_x2l(DisasContext *s, DisasOps *o) { - o->in2 = load_freg(get_field(f, r2) + 2); + o->in2 = load_freg(get_field(s, r2) + 2); } #define SPEC_in2_x2l SPEC_r2_f128 -static void in2_ra2(DisasContext *s, DisasFields *f, DisasOps *o) +static void in2_ra2(DisasContext *s, DisasOps *o) { - o->in2 = get_address(s, 0, get_field(f, r2), 0); + o->in2 = get_address(s, 0, get_field(s, r2), 0); } #define SPEC_in2_ra2 0 -static void in2_a2(DisasContext *s, DisasFields *f, DisasOps *o) +static void in2_a2(DisasContext *s, DisasOps *o) { - int x2 = have_field(f, x2) ? get_field(f, x2) : 0; - o->in2 = get_address(s, x2, get_field(f, b2), get_field(f, d2)); + int x2 = have_field(s, x2) ? get_field(s, x2) : 0; + o->in2 = get_address(s, x2, get_field(s, b2), get_field(s, d2)); } #define SPEC_in2_a2 0 -static void in2_ri2(DisasContext *s, DisasFields *f, DisasOps *o) +static void in2_ri2(DisasContext *s, DisasOps *o) { - o->in2 = tcg_const_i64(s->base.pc_next + (int64_t)get_field(f, i2) * 2); + o->in2 = tcg_const_i64(s->base.pc_next + (int64_t)get_field(s, i2) * 2); } #define SPEC_in2_ri2 0 -static void in2_sh32(DisasContext *s, DisasFields *f, DisasOps *o) +static void in2_sh32(DisasContext *s, DisasOps *o) { - help_l2_shift(s, f, o, 31); + help_l2_shift(s, o, 31); } #define SPEC_in2_sh32 0 -static void in2_sh64(DisasContext *s, DisasFields *f, DisasOps *o) +static void in2_sh64(DisasContext *s, DisasOps *o) { - help_l2_shift(s, f, o, 63); + help_l2_shift(s, o, 63); } #define SPEC_in2_sh64 0 -static void in2_m2_8u(DisasContext *s, DisasFields *f, DisasOps *o) +static void in2_m2_8u(DisasContext *s, DisasOps *o) { - in2_a2(s, f, o); + in2_a2(s, o); tcg_gen_qemu_ld8u(o->in2, o->in2, get_mem_index(s)); } #define SPEC_in2_m2_8u 0 -static void in2_m2_16s(DisasContext *s, DisasFields *f, DisasOps *o) +static void in2_m2_16s(DisasContext *s, DisasOps *o) { - in2_a2(s, f, o); + in2_a2(s, o); tcg_gen_qemu_ld16s(o->in2, o->in2, get_mem_index(s)); } #define SPEC_in2_m2_16s 0 -static void in2_m2_16u(DisasContext *s, DisasFields *f, DisasOps *o) +static void in2_m2_16u(DisasContext *s, DisasOps *o) { - in2_a2(s, f, o); + in2_a2(s, o); tcg_gen_qemu_ld16u(o->in2, o->in2, get_mem_index(s)); } #define SPEC_in2_m2_16u 0 -static void in2_m2_32s(DisasContext *s, DisasFields *f, DisasOps *o) +static void in2_m2_32s(DisasContext *s, DisasOps *o) { - in2_a2(s, f, o); + in2_a2(s, o); tcg_gen_qemu_ld32s(o->in2, o->in2, get_mem_index(s)); } #define SPEC_in2_m2_32s 0 -static void in2_m2_32u(DisasContext *s, DisasFields *f, DisasOps *o) +static void in2_m2_32u(DisasContext *s, DisasOps *o) { - in2_a2(s, f, o); + in2_a2(s, o); tcg_gen_qemu_ld32u(o->in2, o->in2, get_mem_index(s)); } #define SPEC_in2_m2_32u 0 #ifndef CONFIG_USER_ONLY -static void in2_m2_32ua(DisasContext *s, DisasFields *f, DisasOps *o) +static void in2_m2_32ua(DisasContext *s, DisasOps *o) { - in2_a2(s, f, o); + in2_a2(s, o); tcg_gen_qemu_ld_tl(o->in2, o->in2, get_mem_index(s), MO_TEUL | MO_ALIGN); } #define SPEC_in2_m2_32ua 0 #endif -static void in2_m2_64(DisasContext *s, DisasFields *f, DisasOps *o) +static void in2_m2_64(DisasContext *s, DisasOps *o) { - in2_a2(s, f, o); + in2_a2(s, o); tcg_gen_qemu_ld64(o->in2, o->in2, get_mem_index(s)); } #define SPEC_in2_m2_64 0 #ifndef CONFIG_USER_ONLY -static void in2_m2_64a(DisasContext *s, DisasFields *f, DisasOps *o) +static void in2_m2_64a(DisasContext *s, DisasOps *o) { - in2_a2(s, f, o); + in2_a2(s, o); tcg_gen_qemu_ld_i64(o->in2, o->in2, get_mem_index(s), MO_TEQ | MO_ALIGN); } #define SPEC_in2_m2_64a 0 #endif -static void in2_mri2_16u(DisasContext *s, DisasFields *f, DisasOps *o) +static void in2_mri2_16u(DisasContext *s, DisasOps *o) { - in2_ri2(s, f, o); + in2_ri2(s, o); tcg_gen_qemu_ld16u(o->in2, o->in2, get_mem_index(s)); } #define SPEC_in2_mri2_16u 0 -static void in2_mri2_32s(DisasContext *s, DisasFields *f, DisasOps *o) +static void in2_mri2_32s(DisasContext *s, DisasOps *o) { - in2_ri2(s, f, o); + in2_ri2(s, o); tcg_gen_qemu_ld32s(o->in2, o->in2, get_mem_index(s)); } #define SPEC_in2_mri2_32s 0 -static void in2_mri2_32u(DisasContext *s, DisasFields *f, DisasOps *o) +static void in2_mri2_32u(DisasContext *s, DisasOps *o) { - in2_ri2(s, f, o); + in2_ri2(s, o); tcg_gen_qemu_ld32u(o->in2, o->in2, get_mem_index(s)); } #define SPEC_in2_mri2_32u 0 -static void in2_mri2_64(DisasContext *s, DisasFields *f, DisasOps *o) +static void in2_mri2_64(DisasContext *s, DisasOps *o) { - in2_ri2(s, f, o); + in2_ri2(s, o); tcg_gen_qemu_ld64(o->in2, o->in2, get_mem_index(s)); } #define SPEC_in2_mri2_64 0 -static void in2_i2(DisasContext *s, DisasFields *f, DisasOps *o) +static void in2_i2(DisasContext *s, DisasOps *o) { - o->in2 = tcg_const_i64(get_field(f, i2)); + o->in2 = tcg_const_i64(get_field(s, i2)); } #define SPEC_in2_i2 0 -static void in2_i2_8u(DisasContext *s, DisasFields *f, DisasOps *o) +static void in2_i2_8u(DisasContext *s, DisasOps *o) { - o->in2 = tcg_const_i64((uint8_t)get_field(f, i2)); + o->in2 = tcg_const_i64((uint8_t)get_field(s, i2)); } #define SPEC_in2_i2_8u 0 -static void in2_i2_16u(DisasContext *s, DisasFields *f, DisasOps *o) +static void in2_i2_16u(DisasContext *s, DisasOps *o) { - o->in2 = tcg_const_i64((uint16_t)get_field(f, i2)); + o->in2 = tcg_const_i64((uint16_t)get_field(s, i2)); } #define SPEC_in2_i2_16u 0 -static void in2_i2_32u(DisasContext *s, DisasFields *f, DisasOps *o) +static void in2_i2_32u(DisasContext *s, DisasOps *o) { - o->in2 = tcg_const_i64((uint32_t)get_field(f, i2)); + o->in2 = tcg_const_i64((uint32_t)get_field(s, i2)); } #define SPEC_in2_i2_32u 0 -static void in2_i2_16u_shl(DisasContext *s, DisasFields *f, DisasOps *o) +static void in2_i2_16u_shl(DisasContext *s, DisasOps *o) { - uint64_t i2 = (uint16_t)get_field(f, i2); + uint64_t i2 = (uint16_t)get_field(s, i2); o->in2 = tcg_const_i64(i2 << s->insn->data); } #define SPEC_in2_i2_16u_shl 0 -static void in2_i2_32u_shl(DisasContext *s, DisasFields *f, DisasOps *o) +static void in2_i2_32u_shl(DisasContext *s, DisasOps *o) { - uint64_t i2 = (uint32_t)get_field(f, i2); + uint64_t i2 = (uint32_t)get_field(s, i2); o->in2 = tcg_const_i64(i2 << s->insn->data); } #define SPEC_in2_i2_32u_shl 0 #ifndef CONFIG_USER_ONLY -static void in2_insn(DisasContext *s, DisasFields *f, DisasOps *o) +static void in2_insn(DisasContext *s, DisasOps *o) { - o->in2 = tcg_const_i64(s->fields->raw_insn); + o->in2 = tcg_const_i64(s->fields.raw_insn); } #define SPEC_in2_insn 0 #endif @@ -6187,8 +6183,7 @@ static void extract_field(DisasFields *o, const DisasField *f, uint64_t insn) /* Lookup the insn at the current PC, extracting the operands into O and returning the info struct for the insn. Returns NULL for invalid insn. */ -static const DisasInsn *extract_insn(CPUS390XState *env, DisasContext *s, - DisasFields *f) +static const DisasInsn *extract_insn(CPUS390XState *env, DisasContext *s) { uint64_t insn, pc = s->base.pc_next; int op, op2, ilen; @@ -6268,13 +6263,14 @@ static const DisasInsn *extract_insn(CPUS390XState *env, DisasContext *s, break; } - memset(f, 0, sizeof(*f)); - f->raw_insn = insn; - f->op = op; - f->op2 = op2; + memset(&s->fields, 0, sizeof(s->fields)); + s->fields.raw_insn = insn; + s->fields.op = op; + s->fields.op2 = op2; /* Lookup the instruction. */ info = lookup_opc(op << 8 | op2); + s->insn = info; /* If we found it, extract the operands. */ if (info != NULL) { @@ -6282,7 +6278,7 @@ static const DisasInsn *extract_insn(CPUS390XState *env, DisasContext *s, int i; for (i = 0; i < NUM_C_FIELD; ++i) { - extract_field(f, &format_info[fmt].op[i], insn); + extract_field(&s->fields, &format_info[fmt].op[i], insn); } } return info; @@ -6303,11 +6299,10 @@ static DisasJumpType translate_one(CPUS390XState *env, DisasContext *s) { const DisasInsn *insn; DisasJumpType ret = DISAS_NEXT; - DisasFields f; DisasOps o = {}; /* Search for the insn in the table. */ - insn = extract_insn(env, s, &f); + insn = extract_insn(env, s); /* Emit insn_start now that we know the ILEN. */ tcg_gen_insn_start(s->base.pc_next, s->cc_op, s->ilen); @@ -6315,7 +6310,7 @@ static DisasJumpType translate_one(CPUS390XState *env, DisasContext *s) /* Not found means unimplemented/illegal opcode. */ if (insn == NULL) { qemu_log_mask(LOG_UNIMP, "unimplemented opcode 0x%02x%02x\n", - f.op, f.op2); + s->fields.op, s->fields.op2); gen_illegal_opcode(s); return DISAS_NORETURN; } @@ -6340,13 +6335,13 @@ static DisasJumpType translate_one(CPUS390XState *env, DisasContext *s) if (!(s->base.tb->flags & FLAG_MASK_AFP)) { uint8_t dxc = 0; - if ((insn->flags & IF_AFP1) && is_afp_reg(get_field(&f, r1))) { + if ((insn->flags & IF_AFP1) && is_afp_reg(get_field(s, r1))) { dxc = 1; } - if ((insn->flags & IF_AFP2) && is_afp_reg(get_field(&f, r2))) { + if ((insn->flags & IF_AFP2) && is_afp_reg(get_field(s, r2))) { dxc = 1; } - if ((insn->flags & IF_AFP3) && is_afp_reg(get_field(&f, r3))) { + if ((insn->flags & IF_AFP3) && is_afp_reg(get_field(s, r3))) { dxc = 1; } if (insn->flags & IF_BFP) { @@ -6375,36 +6370,32 @@ static DisasJumpType translate_one(CPUS390XState *env, DisasContext *s) /* Check for insn specification exceptions. */ if (insn->spec) { - if ((insn->spec & SPEC_r1_even && get_field(&f, r1) & 1) || - (insn->spec & SPEC_r2_even && get_field(&f, r2) & 1) || - (insn->spec & SPEC_r3_even && get_field(&f, r3) & 1) || - (insn->spec & SPEC_r1_f128 && !is_fp_pair(get_field(&f, r1))) || - (insn->spec & SPEC_r2_f128 && !is_fp_pair(get_field(&f, r2)))) { + if ((insn->spec & SPEC_r1_even && get_field(s, r1) & 1) || + (insn->spec & SPEC_r2_even && get_field(s, r2) & 1) || + (insn->spec & SPEC_r3_even && get_field(s, r3) & 1) || + (insn->spec & SPEC_r1_f128 && !is_fp_pair(get_field(s, r1))) || + (insn->spec & SPEC_r2_f128 && !is_fp_pair(get_field(s, r2)))) { gen_program_exception(s, PGM_SPECIFICATION); return DISAS_NORETURN; } } - /* Set up the strutures we use to communicate with the helpers. */ - s->insn = insn; - s->fields = &f; - /* Implement the instruction. */ if (insn->help_in1) { - insn->help_in1(s, &f, &o); + insn->help_in1(s, &o); } if (insn->help_in2) { - insn->help_in2(s, &f, &o); + insn->help_in2(s, &o); } if (insn->help_prep) { - insn->help_prep(s, &f, &o); + insn->help_prep(s, &o); } if (insn->help_op) { ret = insn->help_op(s, &o); } if (ret != DISAS_NORETURN) { if (insn->help_wout) { - insn->help_wout(s, &f, &o); + insn->help_wout(s, &o); } if (insn->help_cout) { insn->help_cout(s, &o); diff --git a/target/s390x/translate_vx.inc.c b/target/s390x/translate_vx.inc.c index 71059f9ca0..24558cce80 100644 --- a/target/s390x/translate_vx.inc.c +++ b/target/s390x/translate_vx.inc.c @@ -355,7 +355,7 @@ static void gen_addi2_i64(TCGv_i64 dl, TCGv_i64 dh, TCGv_i64 al, TCGv_i64 ah, static DisasJumpType op_vge(DisasContext *s, DisasOps *o) { const uint8_t es = s->insn->data; - const uint8_t enr = get_field(s->fields, m3); + const uint8_t enr = get_field(s, m3); TCGv_i64 tmp; if (!valid_vec_element(enr, es)) { @@ -364,12 +364,12 @@ static DisasJumpType op_vge(DisasContext *s, DisasOps *o) } tmp = tcg_temp_new_i64(); - read_vec_element_i64(tmp, get_field(s->fields, v2), enr, es); + read_vec_element_i64(tmp, get_field(s, v2), enr, es); tcg_gen_add_i64(o->addr1, o->addr1, tmp); gen_addi_and_wrap_i64(s, o->addr1, o->addr1, 0); tcg_gen_qemu_ld_i64(tmp, o->addr1, get_mem_index(s), MO_TE | es); - write_vec_element_i64(tmp, get_field(s->fields, v1), enr, es); + write_vec_element_i64(tmp, get_field(s, v1), enr, es); tcg_temp_free_i64(tmp); return DISAS_NEXT; } @@ -389,22 +389,22 @@ static uint64_t generate_byte_mask(uint8_t mask) static DisasJumpType op_vgbm(DisasContext *s, DisasOps *o) { - const uint16_t i2 = get_field(s->fields, i2); + const uint16_t i2 = get_field(s, i2); if (i2 == (i2 & 0xff) * 0x0101) { /* * Masks for both 64 bit elements of the vector are the same. * Trust tcg to produce a good constant loading. */ - gen_gvec_dup64i(get_field(s->fields, v1), + gen_gvec_dup64i(get_field(s, v1), generate_byte_mask(i2 & 0xff)); } else { TCGv_i64 t = tcg_temp_new_i64(); tcg_gen_movi_i64(t, generate_byte_mask(i2 >> 8)); - write_vec_element_i64(t, get_field(s->fields, v1), 0, ES_64); + write_vec_element_i64(t, get_field(s, v1), 0, ES_64); tcg_gen_movi_i64(t, generate_byte_mask(i2)); - write_vec_element_i64(t, get_field(s->fields, v1), 1, ES_64); + write_vec_element_i64(t, get_field(s, v1), 1, ES_64); tcg_temp_free_i64(t); } return DISAS_NEXT; @@ -412,10 +412,10 @@ static DisasJumpType op_vgbm(DisasContext *s, DisasOps *o) static DisasJumpType op_vgm(DisasContext *s, DisasOps *o) { - const uint8_t es = get_field(s->fields, m4); + const uint8_t es = get_field(s, m4); const uint8_t bits = NUM_VEC_ELEMENT_BITS(es); - const uint8_t i2 = get_field(s->fields, i2) & (bits - 1); - const uint8_t i3 = get_field(s->fields, i3) & (bits - 1); + const uint8_t i2 = get_field(s, i2) & (bits - 1); + const uint8_t i3 = get_field(s, i3) & (bits - 1); uint64_t mask = 0; int i; @@ -432,7 +432,7 @@ static DisasJumpType op_vgm(DisasContext *s, DisasOps *o) } } - gen_gvec_dupi(es, get_field(s->fields, v1), mask); + gen_gvec_dupi(es, get_field(s, v1), mask); return DISAS_NEXT; } @@ -444,8 +444,8 @@ static DisasJumpType op_vl(DisasContext *s, DisasOps *o) tcg_gen_qemu_ld_i64(t0, o->addr1, get_mem_index(s), MO_TEQ); gen_addi_and_wrap_i64(s, o->addr1, o->addr1, 8); tcg_gen_qemu_ld_i64(t1, o->addr1, get_mem_index(s), MO_TEQ); - write_vec_element_i64(t0, get_field(s->fields, v1), 0, ES_64); - write_vec_element_i64(t1, get_field(s->fields, v1), 1, ES_64); + write_vec_element_i64(t0, get_field(s, v1), 0, ES_64); + write_vec_element_i64(t1, get_field(s, v1), 1, ES_64); tcg_temp_free(t0); tcg_temp_free(t1); return DISAS_NEXT; @@ -453,13 +453,13 @@ static DisasJumpType op_vl(DisasContext *s, DisasOps *o) static DisasJumpType op_vlr(DisasContext *s, DisasOps *o) { - gen_gvec_mov(get_field(s->fields, v1), get_field(s->fields, v2)); + gen_gvec_mov(get_field(s, v1), get_field(s, v2)); return DISAS_NEXT; } static DisasJumpType op_vlrep(DisasContext *s, DisasOps *o) { - const uint8_t es = get_field(s->fields, m3); + const uint8_t es = get_field(s, m3); TCGv_i64 tmp; if (es > ES_64) { @@ -469,7 +469,7 @@ static DisasJumpType op_vlrep(DisasContext *s, DisasOps *o) tmp = tcg_temp_new_i64(); tcg_gen_qemu_ld_i64(tmp, o->addr1, get_mem_index(s), MO_TE | es); - gen_gvec_dup_i64(es, get_field(s->fields, v1), tmp); + gen_gvec_dup_i64(es, get_field(s, v1), tmp); tcg_temp_free_i64(tmp); return DISAS_NEXT; } @@ -477,7 +477,7 @@ static DisasJumpType op_vlrep(DisasContext *s, DisasOps *o) static DisasJumpType op_vle(DisasContext *s, DisasOps *o) { const uint8_t es = s->insn->data; - const uint8_t enr = get_field(s->fields, m3); + const uint8_t enr = get_field(s, m3); TCGv_i64 tmp; if (!valid_vec_element(enr, es)) { @@ -487,7 +487,7 @@ static DisasJumpType op_vle(DisasContext *s, DisasOps *o) tmp = tcg_temp_new_i64(); tcg_gen_qemu_ld_i64(tmp, o->addr1, get_mem_index(s), MO_TE | es); - write_vec_element_i64(tmp, get_field(s->fields, v1), enr, es); + write_vec_element_i64(tmp, get_field(s, v1), enr, es); tcg_temp_free_i64(tmp); return DISAS_NEXT; } @@ -495,7 +495,7 @@ static DisasJumpType op_vle(DisasContext *s, DisasOps *o) static DisasJumpType op_vlei(DisasContext *s, DisasOps *o) { const uint8_t es = s->insn->data; - const uint8_t enr = get_field(s->fields, m3); + const uint8_t enr = get_field(s, m3); TCGv_i64 tmp; if (!valid_vec_element(enr, es)) { @@ -503,15 +503,15 @@ static DisasJumpType op_vlei(DisasContext *s, DisasOps *o) return DISAS_NORETURN; } - tmp = tcg_const_i64((int16_t)get_field(s->fields, i2)); - write_vec_element_i64(tmp, get_field(s->fields, v1), enr, es); + tmp = tcg_const_i64((int16_t)get_field(s, i2)); + write_vec_element_i64(tmp, get_field(s, v1), enr, es); tcg_temp_free_i64(tmp); return DISAS_NEXT; } static DisasJumpType op_vlgv(DisasContext *s, DisasOps *o) { - const uint8_t es = get_field(s->fields, m4); + const uint8_t es = get_field(s, m4); TCGv_ptr ptr; if (es > ES_64) { @@ -520,15 +520,15 @@ static DisasJumpType op_vlgv(DisasContext *s, DisasOps *o) } /* fast path if we don't need the register content */ - if (!get_field(s->fields, b2)) { - uint8_t enr = get_field(s->fields, d2) & (NUM_VEC_ELEMENTS(es) - 1); + if (!get_field(s, b2)) { + uint8_t enr = get_field(s, d2) & (NUM_VEC_ELEMENTS(es) - 1); - read_vec_element_i64(o->out, get_field(s->fields, v3), enr, es); + read_vec_element_i64(o->out, get_field(s, v3), enr, es); return DISAS_NEXT; } ptr = tcg_temp_new_ptr(); - get_vec_element_ptr_i64(ptr, get_field(s->fields, v3), o->addr1, es); + get_vec_element_ptr_i64(ptr, get_field(s, v3), o->addr1, es); switch (es) { case ES_8: tcg_gen_ld8u_i64(o->out, ptr, 0); @@ -552,7 +552,7 @@ static DisasJumpType op_vlgv(DisasContext *s, DisasOps *o) static DisasJumpType op_vllez(DisasContext *s, DisasOps *o) { - uint8_t es = get_field(s->fields, m3); + uint8_t es = get_field(s, m3); uint8_t enr; TCGv_i64 t; @@ -585,16 +585,16 @@ static DisasJumpType op_vllez(DisasContext *s, DisasOps *o) t = tcg_temp_new_i64(); tcg_gen_qemu_ld_i64(t, o->addr1, get_mem_index(s), MO_TE | es); - zero_vec(get_field(s->fields, v1)); - write_vec_element_i64(t, get_field(s->fields, v1), enr, es); + zero_vec(get_field(s, v1)); + write_vec_element_i64(t, get_field(s, v1), enr, es); tcg_temp_free_i64(t); return DISAS_NEXT; } static DisasJumpType op_vlm(DisasContext *s, DisasOps *o) { - const uint8_t v3 = get_field(s->fields, v3); - uint8_t v1 = get_field(s->fields, v1); + const uint8_t v3 = get_field(s, v3); + uint8_t v1 = get_field(s, v1); TCGv_i64 t0, t1; if (v3 < v1 || (v3 - v1 + 1) > 16) { @@ -633,12 +633,12 @@ static DisasJumpType op_vlm(DisasContext *s, DisasOps *o) static DisasJumpType op_vlbb(DisasContext *s, DisasOps *o) { - const int64_t block_size = (1ull << (get_field(s->fields, m3) + 6)); - const int v1_offs = vec_full_reg_offset(get_field(s->fields, v1)); + const int64_t block_size = (1ull << (get_field(s, m3) + 6)); + const int v1_offs = vec_full_reg_offset(get_field(s, v1)); TCGv_ptr a0; TCGv_i64 bytes; - if (get_field(s->fields, m3) > 6) { + if (get_field(s, m3) > 6) { gen_program_exception(s, PGM_SPECIFICATION); return DISAS_NORETURN; } @@ -658,7 +658,7 @@ static DisasJumpType op_vlbb(DisasContext *s, DisasOps *o) static DisasJumpType op_vlvg(DisasContext *s, DisasOps *o) { - const uint8_t es = get_field(s->fields, m4); + const uint8_t es = get_field(s, m4); TCGv_ptr ptr; if (es > ES_64) { @@ -667,15 +667,15 @@ static DisasJumpType op_vlvg(DisasContext *s, DisasOps *o) } /* fast path if we don't need the register content */ - if (!get_field(s->fields, b2)) { - uint8_t enr = get_field(s->fields, d2) & (NUM_VEC_ELEMENTS(es) - 1); + if (!get_field(s, b2)) { + uint8_t enr = get_field(s, d2) & (NUM_VEC_ELEMENTS(es) - 1); - write_vec_element_i64(o->in2, get_field(s->fields, v1), enr, es); + write_vec_element_i64(o->in2, get_field(s, v1), enr, es); return DISAS_NEXT; } ptr = tcg_temp_new_ptr(); - get_vec_element_ptr_i64(ptr, get_field(s->fields, v1), o->addr1, es); + get_vec_element_ptr_i64(ptr, get_field(s, v1), o->addr1, es); switch (es) { case ES_8: tcg_gen_st8_i64(o->in2, ptr, 0); @@ -699,14 +699,14 @@ static DisasJumpType op_vlvg(DisasContext *s, DisasOps *o) static DisasJumpType op_vlvgp(DisasContext *s, DisasOps *o) { - write_vec_element_i64(o->in1, get_field(s->fields, v1), 0, ES_64); - write_vec_element_i64(o->in2, get_field(s->fields, v1), 1, ES_64); + write_vec_element_i64(o->in1, get_field(s, v1), 0, ES_64); + write_vec_element_i64(o->in2, get_field(s, v1), 1, ES_64); return DISAS_NEXT; } static DisasJumpType op_vll(DisasContext *s, DisasOps *o) { - const int v1_offs = vec_full_reg_offset(get_field(s->fields, v1)); + const int v1_offs = vec_full_reg_offset(get_field(s, v1)); TCGv_ptr a0 = tcg_temp_new_ptr(); /* convert highest index into an actual length */ @@ -719,10 +719,10 @@ static DisasJumpType op_vll(DisasContext *s, DisasOps *o) static DisasJumpType op_vmr(DisasContext *s, DisasOps *o) { - const uint8_t v1 = get_field(s->fields, v1); - const uint8_t v2 = get_field(s->fields, v2); - const uint8_t v3 = get_field(s->fields, v3); - const uint8_t es = get_field(s->fields, m4); + const uint8_t v1 = get_field(s, v1); + const uint8_t v2 = get_field(s, v2); + const uint8_t v3 = get_field(s, v3); + const uint8_t es = get_field(s, m4); int dst_idx, src_idx; TCGv_i64 tmp; @@ -732,7 +732,7 @@ static DisasJumpType op_vmr(DisasContext *s, DisasOps *o) } tmp = tcg_temp_new_i64(); - if (s->fields->op2 == 0x61) { + if (s->fields.op2 == 0x61) { /* iterate backwards to avoid overwriting data we might need later */ for (dst_idx = NUM_VEC_ELEMENTS(es) - 1; dst_idx >= 0; dst_idx--) { src_idx = dst_idx / 2; @@ -761,10 +761,10 @@ static DisasJumpType op_vmr(DisasContext *s, DisasOps *o) static DisasJumpType op_vpk(DisasContext *s, DisasOps *o) { - const uint8_t v1 = get_field(s->fields, v1); - const uint8_t v2 = get_field(s->fields, v2); - const uint8_t v3 = get_field(s->fields, v3); - const uint8_t es = get_field(s->fields, m4); + const uint8_t v1 = get_field(s, v1); + const uint8_t v2 = get_field(s, v2); + const uint8_t v3 = get_field(s, v3); + const uint8_t es = get_field(s, m4); static gen_helper_gvec_3 * const vpk[3] = { gen_helper_gvec_vpk16, gen_helper_gvec_vpk32, @@ -796,9 +796,9 @@ static DisasJumpType op_vpk(DisasContext *s, DisasOps *o) return DISAS_NORETURN; } - switch (s->fields->op2) { + switch (s->fields.op2) { case 0x97: - if (get_field(s->fields, m5) & 0x1) { + if (get_field(s, m5) & 0x1) { gen_gvec_3_ptr(v1, v2, v3, cpu_env, 0, vpks_cc[es - 1]); set_cc_static(s); } else { @@ -806,7 +806,7 @@ static DisasJumpType op_vpk(DisasContext *s, DisasOps *o) } break; case 0x95: - if (get_field(s->fields, m5) & 0x1) { + if (get_field(s, m5) & 0x1) { gen_gvec_3_ptr(v1, v2, v3, cpu_env, 0, vpkls_cc[es - 1]); set_cc_static(s); } else { @@ -816,7 +816,7 @@ static DisasJumpType op_vpk(DisasContext *s, DisasOps *o) case 0x94: /* If sources and destination dont't overlap -> fast path */ if (v1 != v2 && v1 != v3) { - const uint8_t src_es = get_field(s->fields, m4); + const uint8_t src_es = get_field(s, m4); const uint8_t dst_es = src_es - 1; TCGv_i64 tmp = tcg_temp_new_i64(); int dst_idx, src_idx; @@ -844,23 +844,23 @@ static DisasJumpType op_vpk(DisasContext *s, DisasOps *o) static DisasJumpType op_vperm(DisasContext *s, DisasOps *o) { - gen_gvec_4_ool(get_field(s->fields, v1), get_field(s->fields, v2), - get_field(s->fields, v3), get_field(s->fields, v4), + gen_gvec_4_ool(get_field(s, v1), get_field(s, v2), + get_field(s, v3), get_field(s, v4), 0, gen_helper_gvec_vperm); return DISAS_NEXT; } static DisasJumpType op_vpdi(DisasContext *s, DisasOps *o) { - const uint8_t i2 = extract32(get_field(s->fields, m4), 2, 1); - const uint8_t i3 = extract32(get_field(s->fields, m4), 0, 1); + const uint8_t i2 = extract32(get_field(s, m4), 2, 1); + const uint8_t i3 = extract32(get_field(s, m4), 0, 1); TCGv_i64 t0 = tcg_temp_new_i64(); TCGv_i64 t1 = tcg_temp_new_i64(); - read_vec_element_i64(t0, get_field(s->fields, v2), i2, ES_64); - read_vec_element_i64(t1, get_field(s->fields, v3), i3, ES_64); - write_vec_element_i64(t0, get_field(s->fields, v1), 0, ES_64); - write_vec_element_i64(t1, get_field(s->fields, v1), 1, ES_64); + read_vec_element_i64(t0, get_field(s, v2), i2, ES_64); + read_vec_element_i64(t1, get_field(s, v3), i3, ES_64); + write_vec_element_i64(t0, get_field(s, v1), 0, ES_64); + write_vec_element_i64(t1, get_field(s, v1), 1, ES_64); tcg_temp_free_i64(t0); tcg_temp_free_i64(t1); return DISAS_NEXT; @@ -868,38 +868,38 @@ static DisasJumpType op_vpdi(DisasContext *s, DisasOps *o) static DisasJumpType op_vrep(DisasContext *s, DisasOps *o) { - const uint8_t enr = get_field(s->fields, i2); - const uint8_t es = get_field(s->fields, m4); + const uint8_t enr = get_field(s, i2); + const uint8_t es = get_field(s, m4); if (es > ES_64 || !valid_vec_element(enr, es)) { gen_program_exception(s, PGM_SPECIFICATION); return DISAS_NORETURN; } - tcg_gen_gvec_dup_mem(es, vec_full_reg_offset(get_field(s->fields, v1)), - vec_reg_offset(get_field(s->fields, v3), enr, es), + tcg_gen_gvec_dup_mem(es, vec_full_reg_offset(get_field(s, v1)), + vec_reg_offset(get_field(s, v3), enr, es), 16, 16); return DISAS_NEXT; } static DisasJumpType op_vrepi(DisasContext *s, DisasOps *o) { - const int64_t data = (int16_t)get_field(s->fields, i2); - const uint8_t es = get_field(s->fields, m3); + const int64_t data = (int16_t)get_field(s, i2); + const uint8_t es = get_field(s, m3); if (es > ES_64) { gen_program_exception(s, PGM_SPECIFICATION); return DISAS_NORETURN; } - gen_gvec_dupi(es, get_field(s->fields, v1), data); + gen_gvec_dupi(es, get_field(s, v1), data); return DISAS_NEXT; } static DisasJumpType op_vsce(DisasContext *s, DisasOps *o) { const uint8_t es = s->insn->data; - const uint8_t enr = get_field(s->fields, m3); + const uint8_t enr = get_field(s, m3); TCGv_i64 tmp; if (!valid_vec_element(enr, es)) { @@ -908,11 +908,11 @@ static DisasJumpType op_vsce(DisasContext *s, DisasOps *o) } tmp = tcg_temp_new_i64(); - read_vec_element_i64(tmp, get_field(s->fields, v2), enr, es); + read_vec_element_i64(tmp, get_field(s, v2), enr, es); tcg_gen_add_i64(o->addr1, o->addr1, tmp); gen_addi_and_wrap_i64(s, o->addr1, o->addr1, 0); - read_vec_element_i64(tmp, get_field(s->fields, v1), enr, es); + read_vec_element_i64(tmp, get_field(s, v1), enr, es); tcg_gen_qemu_st_i64(tmp, o->addr1, get_mem_index(s), MO_TE | es); tcg_temp_free_i64(tmp); return DISAS_NEXT; @@ -920,15 +920,15 @@ static DisasJumpType op_vsce(DisasContext *s, DisasOps *o) static DisasJumpType op_vsel(DisasContext *s, DisasOps *o) { - gen_gvec_fn_4(bitsel, ES_8, get_field(s->fields, v1), - get_field(s->fields, v4), get_field(s->fields, v2), - get_field(s->fields, v3)); + gen_gvec_fn_4(bitsel, ES_8, get_field(s, v1), + get_field(s, v4), get_field(s, v2), + get_field(s, v3)); return DISAS_NEXT; } static DisasJumpType op_vseg(DisasContext *s, DisasOps *o) { - const uint8_t es = get_field(s->fields, m3); + const uint8_t es = get_field(s, m3); int idx1, idx2; TCGv_i64 tmp; @@ -951,10 +951,10 @@ static DisasJumpType op_vseg(DisasContext *s, DisasOps *o) } tmp = tcg_temp_new_i64(); - read_vec_element_i64(tmp, get_field(s->fields, v2), idx1, es | MO_SIGN); - write_vec_element_i64(tmp, get_field(s->fields, v1), 0, ES_64); - read_vec_element_i64(tmp, get_field(s->fields, v2), idx2, es | MO_SIGN); - write_vec_element_i64(tmp, get_field(s->fields, v1), 1, ES_64); + read_vec_element_i64(tmp, get_field(s, v2), idx1, es | MO_SIGN); + write_vec_element_i64(tmp, get_field(s, v1), 0, ES_64); + read_vec_element_i64(tmp, get_field(s, v2), idx2, es | MO_SIGN); + write_vec_element_i64(tmp, get_field(s, v1), 1, ES_64); tcg_temp_free_i64(tmp); return DISAS_NEXT; } @@ -966,10 +966,10 @@ static DisasJumpType op_vst(DisasContext *s, DisasOps *o) /* Probe write access before actually modifying memory */ gen_helper_probe_write_access(cpu_env, o->addr1, tmp); - read_vec_element_i64(tmp, get_field(s->fields, v1), 0, ES_64); + read_vec_element_i64(tmp, get_field(s, v1), 0, ES_64); tcg_gen_qemu_st_i64(tmp, o->addr1, get_mem_index(s), MO_TEQ); gen_addi_and_wrap_i64(s, o->addr1, o->addr1, 8); - read_vec_element_i64(tmp, get_field(s->fields, v1), 1, ES_64); + read_vec_element_i64(tmp, get_field(s, v1), 1, ES_64); tcg_gen_qemu_st_i64(tmp, o->addr1, get_mem_index(s), MO_TEQ); tcg_temp_free_i64(tmp); return DISAS_NEXT; @@ -978,7 +978,7 @@ static DisasJumpType op_vst(DisasContext *s, DisasOps *o) static DisasJumpType op_vste(DisasContext *s, DisasOps *o) { const uint8_t es = s->insn->data; - const uint8_t enr = get_field(s->fields, m3); + const uint8_t enr = get_field(s, m3); TCGv_i64 tmp; if (!valid_vec_element(enr, es)) { @@ -987,7 +987,7 @@ static DisasJumpType op_vste(DisasContext *s, DisasOps *o) } tmp = tcg_temp_new_i64(); - read_vec_element_i64(tmp, get_field(s->fields, v1), enr, es); + read_vec_element_i64(tmp, get_field(s, v1), enr, es); tcg_gen_qemu_st_i64(tmp, o->addr1, get_mem_index(s), MO_TE | es); tcg_temp_free_i64(tmp); return DISAS_NEXT; @@ -995,8 +995,8 @@ static DisasJumpType op_vste(DisasContext *s, DisasOps *o) static DisasJumpType op_vstm(DisasContext *s, DisasOps *o) { - const uint8_t v3 = get_field(s->fields, v3); - uint8_t v1 = get_field(s->fields, v1); + const uint8_t v3 = get_field(s, v3); + uint8_t v1 = get_field(s, v1); TCGv_i64 tmp; while (v3 < v1 || (v3 - v1 + 1) > 16) { @@ -1025,7 +1025,7 @@ static DisasJumpType op_vstm(DisasContext *s, DisasOps *o) static DisasJumpType op_vstl(DisasContext *s, DisasOps *o) { - const int v1_offs = vec_full_reg_offset(get_field(s->fields, v1)); + const int v1_offs = vec_full_reg_offset(get_field(s, v1)); TCGv_ptr a0 = tcg_temp_new_ptr(); /* convert highest index into an actual length */ @@ -1038,10 +1038,10 @@ static DisasJumpType op_vstl(DisasContext *s, DisasOps *o) static DisasJumpType op_vup(DisasContext *s, DisasOps *o) { - const bool logical = s->fields->op2 == 0xd4 || s->fields->op2 == 0xd5; - const uint8_t v1 = get_field(s->fields, v1); - const uint8_t v2 = get_field(s->fields, v2); - const uint8_t src_es = get_field(s->fields, m3); + const bool logical = s->fields.op2 == 0xd4 || s->fields.op2 == 0xd5; + const uint8_t v1 = get_field(s, v1); + const uint8_t v2 = get_field(s, v2); + const uint8_t src_es = get_field(s, m3); const uint8_t dst_es = src_es + 1; int dst_idx, src_idx; TCGv_i64 tmp; @@ -1052,7 +1052,7 @@ static DisasJumpType op_vup(DisasContext *s, DisasOps *o) } tmp = tcg_temp_new_i64(); - if (s->fields->op2 == 0xd7 || s->fields->op2 == 0xd5) { + if (s->fields.op2 == 0xd7 || s->fields.op2 == 0xd5) { /* iterate backwards to avoid overwriting data we might need later */ for (dst_idx = NUM_VEC_ELEMENTS(dst_es) - 1; dst_idx >= 0; dst_idx--) { src_idx = dst_idx; @@ -1076,18 +1076,18 @@ static DisasJumpType op_vup(DisasContext *s, DisasOps *o) static DisasJumpType op_va(DisasContext *s, DisasOps *o) { - const uint8_t es = get_field(s->fields, m4); + const uint8_t es = get_field(s, m4); if (es > ES_128) { gen_program_exception(s, PGM_SPECIFICATION); return DISAS_NORETURN; } else if (es == ES_128) { - gen_gvec128_3_i64(tcg_gen_add2_i64, get_field(s->fields, v1), - get_field(s->fields, v2), get_field(s->fields, v3)); + gen_gvec128_3_i64(tcg_gen_add2_i64, get_field(s, v1), + get_field(s, v2), get_field(s, v3)); return DISAS_NEXT; } - gen_gvec_fn_3(add, es, get_field(s->fields, v1), get_field(s->fields, v2), - get_field(s->fields, v3)); + gen_gvec_fn_3(add, es, get_field(s, v1), get_field(s, v2), + get_field(s, v3)); return DISAS_NEXT; } @@ -1165,7 +1165,7 @@ static void gen_acc2_i64(TCGv_i64 dl, TCGv_i64 dh, TCGv_i64 al, static DisasJumpType op_vacc(DisasContext *s, DisasOps *o) { - const uint8_t es = get_field(s->fields, m4); + const uint8_t es = get_field(s, m4); static const GVecGen3 g[4] = { { .fni8 = gen_acc8_i64, }, { .fni8 = gen_acc16_i64, }, @@ -1177,12 +1177,12 @@ static DisasJumpType op_vacc(DisasContext *s, DisasOps *o) gen_program_exception(s, PGM_SPECIFICATION); return DISAS_NORETURN; } else if (es == ES_128) { - gen_gvec128_3_i64(gen_acc2_i64, get_field(s->fields, v1), - get_field(s->fields, v2), get_field(s->fields, v3)); + gen_gvec128_3_i64(gen_acc2_i64, get_field(s, v1), + get_field(s, v2), get_field(s, v3)); return DISAS_NEXT; } - gen_gvec_3(get_field(s->fields, v1), get_field(s->fields, v2), - get_field(s->fields, v3), &g[es]); + gen_gvec_3(get_field(s, v1), get_field(s, v2), + get_field(s, v3), &g[es]); return DISAS_NEXT; } @@ -1203,14 +1203,14 @@ static void gen_ac2_i64(TCGv_i64 dl, TCGv_i64 dh, TCGv_i64 al, TCGv_i64 ah, static DisasJumpType op_vac(DisasContext *s, DisasOps *o) { - if (get_field(s->fields, m5) != ES_128) { + if (get_field(s, m5) != ES_128) { gen_program_exception(s, PGM_SPECIFICATION); return DISAS_NORETURN; } - gen_gvec128_4_i64(gen_ac2_i64, get_field(s->fields, v1), - get_field(s->fields, v2), get_field(s->fields, v3), - get_field(s->fields, v4)); + gen_gvec128_4_i64(gen_ac2_i64, get_field(s, v1), + get_field(s, v2), get_field(s, v3), + get_field(s, v4)); return DISAS_NEXT; } @@ -1235,28 +1235,28 @@ static void gen_accc2_i64(TCGv_i64 dl, TCGv_i64 dh, TCGv_i64 al, TCGv_i64 ah, static DisasJumpType op_vaccc(DisasContext *s, DisasOps *o) { - if (get_field(s->fields, m5) != ES_128) { + if (get_field(s, m5) != ES_128) { gen_program_exception(s, PGM_SPECIFICATION); return DISAS_NORETURN; } - gen_gvec128_4_i64(gen_accc2_i64, get_field(s->fields, v1), - get_field(s->fields, v2), get_field(s->fields, v3), - get_field(s->fields, v4)); + gen_gvec128_4_i64(gen_accc2_i64, get_field(s, v1), + get_field(s, v2), get_field(s, v3), + get_field(s, v4)); return DISAS_NEXT; } static DisasJumpType op_vn(DisasContext *s, DisasOps *o) { - gen_gvec_fn_3(and, ES_8, get_field(s->fields, v1), get_field(s->fields, v2), - get_field(s->fields, v3)); + gen_gvec_fn_3(and, ES_8, get_field(s, v1), get_field(s, v2), + get_field(s, v3)); return DISAS_NEXT; } static DisasJumpType op_vnc(DisasContext *s, DisasOps *o) { - gen_gvec_fn_3(andc, ES_8, get_field(s->fields, v1), - get_field(s->fields, v2), get_field(s->fields, v3)); + gen_gvec_fn_3(andc, ES_8, get_field(s, v1), + get_field(s, v2), get_field(s, v3)); return DISAS_NEXT; } @@ -1296,7 +1296,7 @@ static void gen_avg_i64(TCGv_i64 dl, TCGv_i64 al, TCGv_i64 bl) static DisasJumpType op_vavg(DisasContext *s, DisasOps *o) { - const uint8_t es = get_field(s->fields, m4); + const uint8_t es = get_field(s, m4); static const GVecGen3 g[4] = { { .fno = gen_helper_gvec_vavg8, }, { .fno = gen_helper_gvec_vavg16, }, @@ -1308,8 +1308,8 @@ static DisasJumpType op_vavg(DisasContext *s, DisasOps *o) gen_program_exception(s, PGM_SPECIFICATION); return DISAS_NORETURN; } - gen_gvec_3(get_field(s->fields, v1), get_field(s->fields, v2), - get_field(s->fields, v3), &g[es]); + gen_gvec_3(get_field(s, v1), get_field(s, v2), + get_field(s, v3), &g[es]); return DISAS_NEXT; } @@ -1344,7 +1344,7 @@ static void gen_avgl_i64(TCGv_i64 dl, TCGv_i64 al, TCGv_i64 bl) static DisasJumpType op_vavgl(DisasContext *s, DisasOps *o) { - const uint8_t es = get_field(s->fields, m4); + const uint8_t es = get_field(s, m4); static const GVecGen3 g[4] = { { .fno = gen_helper_gvec_vavgl8, }, { .fno = gen_helper_gvec_vavgl16, }, @@ -1356,8 +1356,8 @@ static DisasJumpType op_vavgl(DisasContext *s, DisasOps *o) gen_program_exception(s, PGM_SPECIFICATION); return DISAS_NORETURN; } - gen_gvec_3(get_field(s->fields, v1), get_field(s->fields, v2), - get_field(s->fields, v3), &g[es]); + gen_gvec_3(get_field(s, v1), get_field(s, v2), + get_field(s, v3), &g[es]); return DISAS_NEXT; } @@ -1367,13 +1367,13 @@ static DisasJumpType op_vcksm(DisasContext *s, DisasOps *o) TCGv_i32 sum = tcg_temp_new_i32(); int i; - read_vec_element_i32(sum, get_field(s->fields, v3), 1, ES_32); + read_vec_element_i32(sum, get_field(s, v3), 1, ES_32); for (i = 0; i < 4; i++) { - read_vec_element_i32(tmp, get_field(s->fields, v2), i, ES_32); + read_vec_element_i32(tmp, get_field(s, v2), i, ES_32); tcg_gen_add2_i32(tmp, sum, sum, sum, tmp, tmp); } - zero_vec(get_field(s->fields, v1)); - write_vec_element_i32(sum, get_field(s->fields, v1), 1, ES_32); + zero_vec(get_field(s, v1)); + write_vec_element_i32(sum, get_field(s, v1), 1, ES_32); tcg_temp_free_i32(tmp); tcg_temp_free_i32(sum); @@ -1382,27 +1382,27 @@ static DisasJumpType op_vcksm(DisasContext *s, DisasOps *o) static DisasJumpType op_vec(DisasContext *s, DisasOps *o) { - uint8_t es = get_field(s->fields, m3); + uint8_t es = get_field(s, m3); const uint8_t enr = NUM_VEC_ELEMENTS(es) / 2 - 1; if (es > ES_64) { gen_program_exception(s, PGM_SPECIFICATION); return DISAS_NORETURN; } - if (s->fields->op2 == 0xdb) { + if (s->fields.op2 == 0xdb) { es |= MO_SIGN; } o->in1 = tcg_temp_new_i64(); o->in2 = tcg_temp_new_i64(); - read_vec_element_i64(o->in1, get_field(s->fields, v1), enr, es); - read_vec_element_i64(o->in2, get_field(s->fields, v2), enr, es); + read_vec_element_i64(o->in1, get_field(s, v1), enr, es); + read_vec_element_i64(o->in2, get_field(s, v2), enr, es); return DISAS_NEXT; } static DisasJumpType op_vc(DisasContext *s, DisasOps *o) { - const uint8_t es = get_field(s->fields, m4); + const uint8_t es = get_field(s, m4); TCGCond cond = s->insn->data; if (es > ES_64) { @@ -1411,15 +1411,15 @@ static DisasJumpType op_vc(DisasContext *s, DisasOps *o) } tcg_gen_gvec_cmp(cond, es, - vec_full_reg_offset(get_field(s->fields, v1)), - vec_full_reg_offset(get_field(s->fields, v2)), - vec_full_reg_offset(get_field(s->fields, v3)), 16, 16); - if (get_field(s->fields, m5) & 0x1) { + vec_full_reg_offset(get_field(s, v1)), + vec_full_reg_offset(get_field(s, v2)), + vec_full_reg_offset(get_field(s, v3)), 16, 16); + if (get_field(s, m5) & 0x1) { TCGv_i64 low = tcg_temp_new_i64(); TCGv_i64 high = tcg_temp_new_i64(); - read_vec_element_i64(high, get_field(s->fields, v1), 0, ES_64); - read_vec_element_i64(low, get_field(s->fields, v1), 1, ES_64); + read_vec_element_i64(high, get_field(s, v1), 0, ES_64); + read_vec_element_i64(low, get_field(s, v1), 1, ES_64); gen_op_update2_cc_i64(s, CC_OP_VC, low, high); tcg_temp_free_i64(low); @@ -1440,7 +1440,7 @@ static void gen_clz_i64(TCGv_i64 d, TCGv_i64 a) static DisasJumpType op_vclz(DisasContext *s, DisasOps *o) { - const uint8_t es = get_field(s->fields, m3); + const uint8_t es = get_field(s, m3); static const GVecGen2 g[4] = { { .fno = gen_helper_gvec_vclz8, }, { .fno = gen_helper_gvec_vclz16, }, @@ -1452,7 +1452,7 @@ static DisasJumpType op_vclz(DisasContext *s, DisasOps *o) gen_program_exception(s, PGM_SPECIFICATION); return DISAS_NORETURN; } - gen_gvec_2(get_field(s->fields, v1), get_field(s->fields, v2), &g[es]); + gen_gvec_2(get_field(s, v1), get_field(s, v2), &g[es]); return DISAS_NEXT; } @@ -1468,7 +1468,7 @@ static void gen_ctz_i64(TCGv_i64 d, TCGv_i64 a) static DisasJumpType op_vctz(DisasContext *s, DisasOps *o) { - const uint8_t es = get_field(s->fields, m3); + const uint8_t es = get_field(s, m3); static const GVecGen2 g[4] = { { .fno = gen_helper_gvec_vctz8, }, { .fno = gen_helper_gvec_vctz16, }, @@ -1480,20 +1480,20 @@ static DisasJumpType op_vctz(DisasContext *s, DisasOps *o) gen_program_exception(s, PGM_SPECIFICATION); return DISAS_NORETURN; } - gen_gvec_2(get_field(s->fields, v1), get_field(s->fields, v2), &g[es]); + gen_gvec_2(get_field(s, v1), get_field(s, v2), &g[es]); return DISAS_NEXT; } static DisasJumpType op_vx(DisasContext *s, DisasOps *o) { - gen_gvec_fn_3(xor, ES_8, get_field(s->fields, v1), get_field(s->fields, v2), - get_field(s->fields, v3)); + gen_gvec_fn_3(xor, ES_8, get_field(s, v1), get_field(s, v2), + get_field(s, v3)); return DISAS_NEXT; } static DisasJumpType op_vgfm(DisasContext *s, DisasOps *o) { - const uint8_t es = get_field(s->fields, m4); + const uint8_t es = get_field(s, m4); static const GVecGen3 g[4] = { { .fno = gen_helper_gvec_vgfm8, }, { .fno = gen_helper_gvec_vgfm16, }, @@ -1505,14 +1505,14 @@ static DisasJumpType op_vgfm(DisasContext *s, DisasOps *o) gen_program_exception(s, PGM_SPECIFICATION); return DISAS_NORETURN; } - gen_gvec_3(get_field(s->fields, v1), get_field(s->fields, v2), - get_field(s->fields, v3), &g[es]); + gen_gvec_3(get_field(s, v1), get_field(s, v2), + get_field(s, v3), &g[es]); return DISAS_NEXT; } static DisasJumpType op_vgfma(DisasContext *s, DisasOps *o) { - const uint8_t es = get_field(s->fields, m5); + const uint8_t es = get_field(s, m5); static const GVecGen4 g[4] = { { .fno = gen_helper_gvec_vgfma8, }, { .fno = gen_helper_gvec_vgfma16, }, @@ -1524,50 +1524,50 @@ static DisasJumpType op_vgfma(DisasContext *s, DisasOps *o) gen_program_exception(s, PGM_SPECIFICATION); return DISAS_NORETURN; } - gen_gvec_4(get_field(s->fields, v1), get_field(s->fields, v2), - get_field(s->fields, v3), get_field(s->fields, v4), &g[es]); + gen_gvec_4(get_field(s, v1), get_field(s, v2), + get_field(s, v3), get_field(s, v4), &g[es]); return DISAS_NEXT; } static DisasJumpType op_vlc(DisasContext *s, DisasOps *o) { - const uint8_t es = get_field(s->fields, m3); + const uint8_t es = get_field(s, m3); if (es > ES_64) { gen_program_exception(s, PGM_SPECIFICATION); return DISAS_NORETURN; } - gen_gvec_fn_2(neg, es, get_field(s->fields, v1), get_field(s->fields, v2)); + gen_gvec_fn_2(neg, es, get_field(s, v1), get_field(s, v2)); return DISAS_NEXT; } static DisasJumpType op_vlp(DisasContext *s, DisasOps *o) { - const uint8_t es = get_field(s->fields, m3); + const uint8_t es = get_field(s, m3); if (es > ES_64) { gen_program_exception(s, PGM_SPECIFICATION); return DISAS_NORETURN; } - gen_gvec_fn_2(abs, es, get_field(s->fields, v1), get_field(s->fields, v2)); + gen_gvec_fn_2(abs, es, get_field(s, v1), get_field(s, v2)); return DISAS_NEXT; } static DisasJumpType op_vmx(DisasContext *s, DisasOps *o) { - const uint8_t v1 = get_field(s->fields, v1); - const uint8_t v2 = get_field(s->fields, v2); - const uint8_t v3 = get_field(s->fields, v3); - const uint8_t es = get_field(s->fields, m4); + const uint8_t v1 = get_field(s, v1); + const uint8_t v2 = get_field(s, v2); + const uint8_t v3 = get_field(s, v3); + const uint8_t es = get_field(s, m4); if (es > ES_64) { gen_program_exception(s, PGM_SPECIFICATION); return DISAS_NORETURN; } - switch (s->fields->op2) { + switch (s->fields.op2) { case 0xff: gen_gvec_fn_3(smax, es, v1, v2, v3); break; @@ -1634,7 +1634,7 @@ static void gen_malh_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b, TCGv_i32 c) static DisasJumpType op_vma(DisasContext *s, DisasOps *o) { - const uint8_t es = get_field(s->fields, m5); + const uint8_t es = get_field(s, m5); static const GVecGen4 g_vmal[3] = { { .fno = gen_helper_gvec_vmal8, }, { .fno = gen_helper_gvec_vmal16, }, @@ -1677,7 +1677,7 @@ static DisasJumpType op_vma(DisasContext *s, DisasOps *o) return DISAS_NORETURN; } - switch (s->fields->op2) { + switch (s->fields.op2) { case 0xaa: fn = &g_vmal[es]; break; @@ -1703,8 +1703,8 @@ static DisasJumpType op_vma(DisasContext *s, DisasOps *o) g_assert_not_reached(); } - gen_gvec_4(get_field(s->fields, v1), get_field(s->fields, v2), - get_field(s->fields, v3), get_field(s->fields, v4), fn); + gen_gvec_4(get_field(s, v1), get_field(s, v2), + get_field(s, v3), get_field(s, v4), fn); return DISAS_NEXT; } @@ -1726,7 +1726,7 @@ static void gen_mlh_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) static DisasJumpType op_vm(DisasContext *s, DisasOps *o) { - const uint8_t es = get_field(s->fields, m4); + const uint8_t es = get_field(s, m4); static const GVecGen3 g_vmh[3] = { { .fno = gen_helper_gvec_vmh8, }, { .fno = gen_helper_gvec_vmh16, }, @@ -1764,10 +1764,10 @@ static DisasJumpType op_vm(DisasContext *s, DisasOps *o) return DISAS_NORETURN; } - switch (s->fields->op2) { + switch (s->fields.op2) { case 0xa2: - gen_gvec_fn_3(mul, es, get_field(s->fields, v1), - get_field(s->fields, v2), get_field(s->fields, v3)); + gen_gvec_fn_3(mul, es, get_field(s, v1), + get_field(s, v2), get_field(s, v3)); return DISAS_NEXT; case 0xa3: fn = &g_vmh[es]; @@ -1791,49 +1791,49 @@ static DisasJumpType op_vm(DisasContext *s, DisasOps *o) g_assert_not_reached(); } - gen_gvec_3(get_field(s->fields, v1), get_field(s->fields, v2), - get_field(s->fields, v3), fn); + gen_gvec_3(get_field(s, v1), get_field(s, v2), + get_field(s, v3), fn); return DISAS_NEXT; } static DisasJumpType op_vnn(DisasContext *s, DisasOps *o) { - gen_gvec_fn_3(nand, ES_8, get_field(s->fields, v1), - get_field(s->fields, v2), get_field(s->fields, v3)); + gen_gvec_fn_3(nand, ES_8, get_field(s, v1), + get_field(s, v2), get_field(s, v3)); return DISAS_NEXT; } static DisasJumpType op_vno(DisasContext *s, DisasOps *o) { - gen_gvec_fn_3(nor, ES_8, get_field(s->fields, v1), get_field(s->fields, v2), - get_field(s->fields, v3)); + gen_gvec_fn_3(nor, ES_8, get_field(s, v1), get_field(s, v2), + get_field(s, v3)); return DISAS_NEXT; } static DisasJumpType op_vnx(DisasContext *s, DisasOps *o) { - gen_gvec_fn_3(eqv, ES_8, get_field(s->fields, v1), get_field(s->fields, v2), - get_field(s->fields, v3)); + gen_gvec_fn_3(eqv, ES_8, get_field(s, v1), get_field(s, v2), + get_field(s, v3)); return DISAS_NEXT; } static DisasJumpType op_vo(DisasContext *s, DisasOps *o) { - gen_gvec_fn_3(or, ES_8, get_field(s->fields, v1), get_field(s->fields, v2), - get_field(s->fields, v3)); + gen_gvec_fn_3(or, ES_8, get_field(s, v1), get_field(s, v2), + get_field(s, v3)); return DISAS_NEXT; } static DisasJumpType op_voc(DisasContext *s, DisasOps *o) { - gen_gvec_fn_3(orc, ES_8, get_field(s->fields, v1), get_field(s->fields, v2), - get_field(s->fields, v3)); + gen_gvec_fn_3(orc, ES_8, get_field(s, v1), get_field(s, v2), + get_field(s, v3)); return DISAS_NEXT; } static DisasJumpType op_vpopct(DisasContext *s, DisasOps *o) { - const uint8_t es = get_field(s->fields, m3); + const uint8_t es = get_field(s, m3); static const GVecGen2 g[4] = { { .fno = gen_helper_gvec_vpopct8, }, { .fno = gen_helper_gvec_vpopct16, }, @@ -1846,7 +1846,7 @@ static DisasJumpType op_vpopct(DisasContext *s, DisasOps *o) return DISAS_NORETURN; } - gen_gvec_2(get_field(s->fields, v1), get_field(s->fields, v2), &g[es]); + gen_gvec_2(get_field(s, v1), get_field(s, v2), &g[es]); return DISAS_NEXT; } @@ -1870,7 +1870,7 @@ static void gen_rll_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) static DisasJumpType op_verllv(DisasContext *s, DisasOps *o) { - const uint8_t es = get_field(s->fields, m4); + const uint8_t es = get_field(s, m4); static const GVecGen3 g[4] = { { .fno = gen_helper_gvec_verllv8, }, { .fno = gen_helper_gvec_verllv16, }, @@ -1883,14 +1883,14 @@ static DisasJumpType op_verllv(DisasContext *s, DisasOps *o) return DISAS_NORETURN; } - gen_gvec_3(get_field(s->fields, v1), get_field(s->fields, v2), - get_field(s->fields, v3), &g[es]); + gen_gvec_3(get_field(s, v1), get_field(s, v2), + get_field(s, v3), &g[es]); return DISAS_NEXT; } static DisasJumpType op_verll(DisasContext *s, DisasOps *o) { - const uint8_t es = get_field(s->fields, m4); + const uint8_t es = get_field(s, m4); static const GVecGen2s g[4] = { { .fno = gen_helper_gvec_verll8, }, { .fno = gen_helper_gvec_verll16, }, @@ -1902,7 +1902,7 @@ static DisasJumpType op_verll(DisasContext *s, DisasOps *o) gen_program_exception(s, PGM_SPECIFICATION); return DISAS_NORETURN; } - gen_gvec_2s(get_field(s->fields, v1), get_field(s->fields, v3), o->addr1, + gen_gvec_2s(get_field(s, v1), get_field(s, v3), o->addr1, &g[es]); return DISAS_NEXT; } @@ -1933,8 +1933,8 @@ static void gen_rim_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b, int64_t c) static DisasJumpType op_verim(DisasContext *s, DisasOps *o) { - const uint8_t es = get_field(s->fields, m5); - const uint8_t i4 = get_field(s->fields, i4) & + const uint8_t es = get_field(s, m5); + const uint8_t i4 = get_field(s, i4) & (NUM_VEC_ELEMENT_BITS(es) - 1); static const GVecGen3i g[4] = { { .fno = gen_helper_gvec_verim8, }, @@ -1950,24 +1950,24 @@ static DisasJumpType op_verim(DisasContext *s, DisasOps *o) return DISAS_NORETURN; } - gen_gvec_3i(get_field(s->fields, v1), get_field(s->fields, v2), - get_field(s->fields, v3), i4, &g[es]); + gen_gvec_3i(get_field(s, v1), get_field(s, v2), + get_field(s, v3), i4, &g[es]); return DISAS_NEXT; } static DisasJumpType op_vesv(DisasContext *s, DisasOps *o) { - const uint8_t es = get_field(s->fields, m4); - const uint8_t v1 = get_field(s->fields, v1); - const uint8_t v2 = get_field(s->fields, v2); - const uint8_t v3 = get_field(s->fields, v3); + const uint8_t es = get_field(s, m4); + const uint8_t v1 = get_field(s, v1); + const uint8_t v2 = get_field(s, v2); + const uint8_t v3 = get_field(s, v3); if (es > ES_64) { gen_program_exception(s, PGM_SPECIFICATION); return DISAS_NORETURN; } - switch (s->fields->op2) { + switch (s->fields.op2) { case 0x70: gen_gvec_fn_3(shlv, es, v1, v2, v3); break; @@ -1985,11 +1985,11 @@ static DisasJumpType op_vesv(DisasContext *s, DisasOps *o) static DisasJumpType op_ves(DisasContext *s, DisasOps *o) { - const uint8_t es = get_field(s->fields, m4); - const uint8_t d2 = get_field(s->fields, d2) & + const uint8_t es = get_field(s, m4); + const uint8_t d2 = get_field(s, d2) & (NUM_VEC_ELEMENT_BITS(es) - 1); - const uint8_t v1 = get_field(s->fields, v1); - const uint8_t v3 = get_field(s->fields, v3); + const uint8_t v1 = get_field(s, v1); + const uint8_t v3 = get_field(s, v3); TCGv_i32 shift; if (es > ES_64) { @@ -1997,8 +1997,8 @@ static DisasJumpType op_ves(DisasContext *s, DisasOps *o) return DISAS_NORETURN; } - if (likely(!get_field(s->fields, b2))) { - switch (s->fields->op2) { + if (likely(!get_field(s, b2))) { + switch (s->fields.op2) { case 0x30: gen_gvec_fn_2i(shli, es, v1, v3, d2); break; @@ -2015,7 +2015,7 @@ static DisasJumpType op_ves(DisasContext *s, DisasOps *o) shift = tcg_temp_new_i32(); tcg_gen_extrl_i64_i32(shift, o->addr1); tcg_gen_andi_i32(shift, shift, NUM_VEC_ELEMENT_BITS(es) - 1); - switch (s->fields->op2) { + switch (s->fields.op2) { case 0x30: gen_gvec_fn_2s(shls, es, v1, v3, shift); break; @@ -2037,14 +2037,14 @@ static DisasJumpType op_vsl(DisasContext *s, DisasOps *o) { TCGv_i64 shift = tcg_temp_new_i64(); - read_vec_element_i64(shift, get_field(s->fields, v3), 7, ES_8); - if (s->fields->op2 == 0x74) { + read_vec_element_i64(shift, get_field(s, v3), 7, ES_8); + if (s->fields.op2 == 0x74) { tcg_gen_andi_i64(shift, shift, 0x7); } else { tcg_gen_andi_i64(shift, shift, 0x78); } - gen_gvec_2i_ool(get_field(s->fields, v1), get_field(s->fields, v2), + gen_gvec_2i_ool(get_field(s, v1), get_field(s, v2), shift, 0, gen_helper_gvec_vsl); tcg_temp_free_i64(shift); return DISAS_NEXT; @@ -2052,7 +2052,7 @@ static DisasJumpType op_vsl(DisasContext *s, DisasOps *o) static DisasJumpType op_vsldb(DisasContext *s, DisasOps *o) { - const uint8_t i4 = get_field(s->fields, i4) & 0xf; + const uint8_t i4 = get_field(s, i4) & 0xf; const int left_shift = (i4 & 7) * 8; const int right_shift = 64 - left_shift; TCGv_i64 t0 = tcg_temp_new_i64(); @@ -2060,18 +2060,18 @@ static DisasJumpType op_vsldb(DisasContext *s, DisasOps *o) TCGv_i64 t2 = tcg_temp_new_i64(); if ((i4 & 8) == 0) { - read_vec_element_i64(t0, get_field(s->fields, v2), 0, ES_64); - read_vec_element_i64(t1, get_field(s->fields, v2), 1, ES_64); - read_vec_element_i64(t2, get_field(s->fields, v3), 0, ES_64); + read_vec_element_i64(t0, get_field(s, v2), 0, ES_64); + read_vec_element_i64(t1, get_field(s, v2), 1, ES_64); + read_vec_element_i64(t2, get_field(s, v3), 0, ES_64); } else { - read_vec_element_i64(t0, get_field(s->fields, v2), 1, ES_64); - read_vec_element_i64(t1, get_field(s->fields, v3), 0, ES_64); - read_vec_element_i64(t2, get_field(s->fields, v3), 1, ES_64); + read_vec_element_i64(t0, get_field(s, v2), 1, ES_64); + read_vec_element_i64(t1, get_field(s, v3), 0, ES_64); + read_vec_element_i64(t2, get_field(s, v3), 1, ES_64); } tcg_gen_extract2_i64(t0, t1, t0, right_shift); tcg_gen_extract2_i64(t1, t2, t1, right_shift); - write_vec_element_i64(t0, get_field(s->fields, v1), 0, ES_64); - write_vec_element_i64(t1, get_field(s->fields, v1), 1, ES_64); + write_vec_element_i64(t0, get_field(s, v1), 0, ES_64); + write_vec_element_i64(t1, get_field(s, v1), 1, ES_64); tcg_temp_free(t0); tcg_temp_free(t1); @@ -2083,14 +2083,14 @@ static DisasJumpType op_vsra(DisasContext *s, DisasOps *o) { TCGv_i64 shift = tcg_temp_new_i64(); - read_vec_element_i64(shift, get_field(s->fields, v3), 7, ES_8); - if (s->fields->op2 == 0x7e) { + read_vec_element_i64(shift, get_field(s, v3), 7, ES_8); + if (s->fields.op2 == 0x7e) { tcg_gen_andi_i64(shift, shift, 0x7); } else { tcg_gen_andi_i64(shift, shift, 0x78); } - gen_gvec_2i_ool(get_field(s->fields, v1), get_field(s->fields, v2), + gen_gvec_2i_ool(get_field(s, v1), get_field(s, v2), shift, 0, gen_helper_gvec_vsra); tcg_temp_free_i64(shift); return DISAS_NEXT; @@ -2100,14 +2100,14 @@ static DisasJumpType op_vsrl(DisasContext *s, DisasOps *o) { TCGv_i64 shift = tcg_temp_new_i64(); - read_vec_element_i64(shift, get_field(s->fields, v3), 7, ES_8); - if (s->fields->op2 == 0x7c) { + read_vec_element_i64(shift, get_field(s, v3), 7, ES_8); + if (s->fields.op2 == 0x7c) { tcg_gen_andi_i64(shift, shift, 0x7); } else { tcg_gen_andi_i64(shift, shift, 0x78); } - gen_gvec_2i_ool(get_field(s->fields, v1), get_field(s->fields, v2), + gen_gvec_2i_ool(get_field(s, v1), get_field(s, v2), shift, 0, gen_helper_gvec_vsrl); tcg_temp_free_i64(shift); return DISAS_NEXT; @@ -2115,18 +2115,18 @@ static DisasJumpType op_vsrl(DisasContext *s, DisasOps *o) static DisasJumpType op_vs(DisasContext *s, DisasOps *o) { - const uint8_t es = get_field(s->fields, m4); + const uint8_t es = get_field(s, m4); if (es > ES_128) { gen_program_exception(s, PGM_SPECIFICATION); return DISAS_NORETURN; } else if (es == ES_128) { - gen_gvec128_3_i64(tcg_gen_sub2_i64, get_field(s->fields, v1), - get_field(s->fields, v2), get_field(s->fields, v3)); + gen_gvec128_3_i64(tcg_gen_sub2_i64, get_field(s, v1), + get_field(s, v2), get_field(s, v3)); return DISAS_NEXT; } - gen_gvec_fn_3(sub, es, get_field(s->fields, v1), get_field(s->fields, v2), - get_field(s->fields, v3)); + gen_gvec_fn_3(sub, es, get_field(s, v1), get_field(s, v2), + get_field(s, v3)); return DISAS_NEXT; } @@ -2162,7 +2162,7 @@ static void gen_scbi2_i64(TCGv_i64 dl, TCGv_i64 dh, TCGv_i64 al, static DisasJumpType op_vscbi(DisasContext *s, DisasOps *o) { - const uint8_t es = get_field(s->fields, m4); + const uint8_t es = get_field(s, m4); static const GVecGen3 g[4] = { { .fno = gen_helper_gvec_vscbi8, }, { .fno = gen_helper_gvec_vscbi16, }, @@ -2174,12 +2174,12 @@ static DisasJumpType op_vscbi(DisasContext *s, DisasOps *o) gen_program_exception(s, PGM_SPECIFICATION); return DISAS_NORETURN; } else if (es == ES_128) { - gen_gvec128_3_i64(gen_scbi2_i64, get_field(s->fields, v1), - get_field(s->fields, v2), get_field(s->fields, v3)); + gen_gvec128_3_i64(gen_scbi2_i64, get_field(s, v1), + get_field(s, v2), get_field(s, v3)); return DISAS_NEXT; } - gen_gvec_3(get_field(s->fields, v1), get_field(s->fields, v2), - get_field(s->fields, v3), &g[es]); + gen_gvec_3(get_field(s, v1), get_field(s, v2), + get_field(s, v3), &g[es]); return DISAS_NEXT; } @@ -2198,14 +2198,14 @@ static void gen_sbi2_i64(TCGv_i64 dl, TCGv_i64 dh, TCGv_i64 al, TCGv_i64 ah, static DisasJumpType op_vsbi(DisasContext *s, DisasOps *o) { - if (get_field(s->fields, m5) != ES_128) { + if (get_field(s, m5) != ES_128) { gen_program_exception(s, PGM_SPECIFICATION); return DISAS_NORETURN; } - gen_gvec128_4_i64(gen_sbi2_i64, get_field(s->fields, v1), - get_field(s->fields, v2), get_field(s->fields, v3), - get_field(s->fields, v4)); + gen_gvec128_4_i64(gen_sbi2_i64, get_field(s, v1), + get_field(s, v2), get_field(s, v3), + get_field(s, v4)); return DISAS_NEXT; } @@ -2225,20 +2225,20 @@ static void gen_sbcbi2_i64(TCGv_i64 dl, TCGv_i64 dh, TCGv_i64 al, TCGv_i64 ah, static DisasJumpType op_vsbcbi(DisasContext *s, DisasOps *o) { - if (get_field(s->fields, m5) != ES_128) { + if (get_field(s, m5) != ES_128) { gen_program_exception(s, PGM_SPECIFICATION); return DISAS_NORETURN; } - gen_gvec128_4_i64(gen_sbcbi2_i64, get_field(s->fields, v1), - get_field(s->fields, v2), get_field(s->fields, v3), - get_field(s->fields, v4)); + gen_gvec128_4_i64(gen_sbcbi2_i64, get_field(s, v1), + get_field(s, v2), get_field(s, v3), + get_field(s, v4)); return DISAS_NEXT; } static DisasJumpType op_vsumg(DisasContext *s, DisasOps *o) { - const uint8_t es = get_field(s->fields, m4); + const uint8_t es = get_field(s, m4); TCGv_i64 sum, tmp; uint8_t dst_idx; @@ -2253,12 +2253,12 @@ static DisasJumpType op_vsumg(DisasContext *s, DisasOps *o) uint8_t idx = dst_idx * NUM_VEC_ELEMENTS(es) / 2; const uint8_t max_idx = idx + NUM_VEC_ELEMENTS(es) / 2 - 1; - read_vec_element_i64(sum, get_field(s->fields, v3), max_idx, es); + read_vec_element_i64(sum, get_field(s, v3), max_idx, es); for (; idx <= max_idx; idx++) { - read_vec_element_i64(tmp, get_field(s->fields, v2), idx, es); + read_vec_element_i64(tmp, get_field(s, v2), idx, es); tcg_gen_add_i64(sum, sum, tmp); } - write_vec_element_i64(sum, get_field(s->fields, v1), dst_idx, ES_64); + write_vec_element_i64(sum, get_field(s, v1), dst_idx, ES_64); } tcg_temp_free_i64(sum); tcg_temp_free_i64(tmp); @@ -2267,7 +2267,7 @@ static DisasJumpType op_vsumg(DisasContext *s, DisasOps *o) static DisasJumpType op_vsumq(DisasContext *s, DisasOps *o) { - const uint8_t es = get_field(s->fields, m4); + const uint8_t es = get_field(s, m4); const uint8_t max_idx = NUM_VEC_ELEMENTS(es) - 1; TCGv_i64 sumh, suml, zero, tmpl; uint8_t idx; @@ -2282,13 +2282,13 @@ static DisasJumpType op_vsumq(DisasContext *s, DisasOps *o) zero = tcg_const_i64(0); tmpl = tcg_temp_new_i64(); - read_vec_element_i64(suml, get_field(s->fields, v3), max_idx, es); + read_vec_element_i64(suml, get_field(s, v3), max_idx, es); for (idx = 0; idx <= max_idx; idx++) { - read_vec_element_i64(tmpl, get_field(s->fields, v2), idx, es); + read_vec_element_i64(tmpl, get_field(s, v2), idx, es); tcg_gen_add2_i64(suml, sumh, suml, sumh, tmpl, zero); } - write_vec_element_i64(sumh, get_field(s->fields, v1), 0, ES_64); - write_vec_element_i64(suml, get_field(s->fields, v1), 1, ES_64); + write_vec_element_i64(sumh, get_field(s, v1), 0, ES_64); + write_vec_element_i64(suml, get_field(s, v1), 1, ES_64); tcg_temp_free_i64(sumh); tcg_temp_free_i64(suml); @@ -2299,7 +2299,7 @@ static DisasJumpType op_vsumq(DisasContext *s, DisasOps *o) static DisasJumpType op_vsum(DisasContext *s, DisasOps *o) { - const uint8_t es = get_field(s->fields, m4); + const uint8_t es = get_field(s, m4); TCGv_i32 sum, tmp; uint8_t dst_idx; @@ -2314,12 +2314,12 @@ static DisasJumpType op_vsum(DisasContext *s, DisasOps *o) uint8_t idx = dst_idx * NUM_VEC_ELEMENTS(es) / 4; const uint8_t max_idx = idx + NUM_VEC_ELEMENTS(es) / 4 - 1; - read_vec_element_i32(sum, get_field(s->fields, v3), max_idx, es); + read_vec_element_i32(sum, get_field(s, v3), max_idx, es); for (; idx <= max_idx; idx++) { - read_vec_element_i32(tmp, get_field(s->fields, v2), idx, es); + read_vec_element_i32(tmp, get_field(s, v2), idx, es); tcg_gen_add_i32(sum, sum, tmp); } - write_vec_element_i32(sum, get_field(s->fields, v1), dst_idx, ES_32); + write_vec_element_i32(sum, get_field(s, v1), dst_idx, ES_32); } tcg_temp_free_i32(sum); tcg_temp_free_i32(tmp); @@ -2328,7 +2328,7 @@ static DisasJumpType op_vsum(DisasContext *s, DisasOps *o) static DisasJumpType op_vtm(DisasContext *s, DisasOps *o) { - gen_gvec_2_ptr(get_field(s->fields, v1), get_field(s->fields, v2), + gen_gvec_2_ptr(get_field(s, v1), get_field(s, v2), cpu_env, 0, gen_helper_gvec_vtm); set_cc_static(s); return DISAS_NEXT; @@ -2336,8 +2336,8 @@ static DisasJumpType op_vtm(DisasContext *s, DisasOps *o) static DisasJumpType op_vfae(DisasContext *s, DisasOps *o) { - const uint8_t es = get_field(s->fields, m4); - const uint8_t m5 = get_field(s->fields, m5); + const uint8_t es = get_field(s, m4); + const uint8_t m5 = get_field(s, m5); static gen_helper_gvec_3 * const g[3] = { gen_helper_gvec_vfae8, gen_helper_gvec_vfae16, @@ -2354,20 +2354,20 @@ static DisasJumpType op_vfae(DisasContext *s, DisasOps *o) } if (extract32(m5, 0, 1)) { - gen_gvec_3_ptr(get_field(s->fields, v1), get_field(s->fields, v2), - get_field(s->fields, v3), cpu_env, m5, g_cc[es]); + gen_gvec_3_ptr(get_field(s, v1), get_field(s, v2), + get_field(s, v3), cpu_env, m5, g_cc[es]); set_cc_static(s); } else { - gen_gvec_3_ool(get_field(s->fields, v1), get_field(s->fields, v2), - get_field(s->fields, v3), m5, g[es]); + gen_gvec_3_ool(get_field(s, v1), get_field(s, v2), + get_field(s, v3), m5, g[es]); } return DISAS_NEXT; } static DisasJumpType op_vfee(DisasContext *s, DisasOps *o) { - const uint8_t es = get_field(s->fields, m4); - const uint8_t m5 = get_field(s->fields, m5); + const uint8_t es = get_field(s, m4); + const uint8_t m5 = get_field(s, m5); static gen_helper_gvec_3 * const g[3] = { gen_helper_gvec_vfee8, gen_helper_gvec_vfee16, @@ -2385,20 +2385,20 @@ static DisasJumpType op_vfee(DisasContext *s, DisasOps *o) } if (extract32(m5, 0, 1)) { - gen_gvec_3_ptr(get_field(s->fields, v1), get_field(s->fields, v2), - get_field(s->fields, v3), cpu_env, m5, g_cc[es]); + gen_gvec_3_ptr(get_field(s, v1), get_field(s, v2), + get_field(s, v3), cpu_env, m5, g_cc[es]); set_cc_static(s); } else { - gen_gvec_3_ool(get_field(s->fields, v1), get_field(s->fields, v2), - get_field(s->fields, v3), m5, g[es]); + gen_gvec_3_ool(get_field(s, v1), get_field(s, v2), + get_field(s, v3), m5, g[es]); } return DISAS_NEXT; } static DisasJumpType op_vfene(DisasContext *s, DisasOps *o) { - const uint8_t es = get_field(s->fields, m4); - const uint8_t m5 = get_field(s->fields, m5); + const uint8_t es = get_field(s, m4); + const uint8_t m5 = get_field(s, m5); static gen_helper_gvec_3 * const g[3] = { gen_helper_gvec_vfene8, gen_helper_gvec_vfene16, @@ -2416,20 +2416,20 @@ static DisasJumpType op_vfene(DisasContext *s, DisasOps *o) } if (extract32(m5, 0, 1)) { - gen_gvec_3_ptr(get_field(s->fields, v1), get_field(s->fields, v2), - get_field(s->fields, v3), cpu_env, m5, g_cc[es]); + gen_gvec_3_ptr(get_field(s, v1), get_field(s, v2), + get_field(s, v3), cpu_env, m5, g_cc[es]); set_cc_static(s); } else { - gen_gvec_3_ool(get_field(s->fields, v1), get_field(s->fields, v2), - get_field(s->fields, v3), m5, g[es]); + gen_gvec_3_ool(get_field(s, v1), get_field(s, v2), + get_field(s, v3), m5, g[es]); } return DISAS_NEXT; } static DisasJumpType op_vistr(DisasContext *s, DisasOps *o) { - const uint8_t es = get_field(s->fields, m4); - const uint8_t m5 = get_field(s->fields, m5); + const uint8_t es = get_field(s, m4); + const uint8_t m5 = get_field(s, m5); static gen_helper_gvec_2 * const g[3] = { gen_helper_gvec_vistr8, gen_helper_gvec_vistr16, @@ -2447,11 +2447,11 @@ static DisasJumpType op_vistr(DisasContext *s, DisasOps *o) } if (extract32(m5, 0, 1)) { - gen_gvec_2_ptr(get_field(s->fields, v1), get_field(s->fields, v2), + gen_gvec_2_ptr(get_field(s, v1), get_field(s, v2), cpu_env, 0, g_cc[es]); set_cc_static(s); } else { - gen_gvec_2_ool(get_field(s->fields, v1), get_field(s->fields, v2), 0, + gen_gvec_2_ool(get_field(s, v1), get_field(s, v2), 0, g[es]); } return DISAS_NEXT; @@ -2459,8 +2459,8 @@ static DisasJumpType op_vistr(DisasContext *s, DisasOps *o) static DisasJumpType op_vstrc(DisasContext *s, DisasOps *o) { - const uint8_t es = get_field(s->fields, m5); - const uint8_t m6 = get_field(s->fields, m6); + const uint8_t es = get_field(s, m5); + const uint8_t m6 = get_field(s, m6); static gen_helper_gvec_4 * const g[3] = { gen_helper_gvec_vstrc8, gen_helper_gvec_vstrc16, @@ -2489,23 +2489,23 @@ static DisasJumpType op_vstrc(DisasContext *s, DisasOps *o) if (extract32(m6, 0, 1)) { if (extract32(m6, 2, 1)) { - gen_gvec_4_ptr(get_field(s->fields, v1), get_field(s->fields, v2), - get_field(s->fields, v3), get_field(s->fields, v4), + gen_gvec_4_ptr(get_field(s, v1), get_field(s, v2), + get_field(s, v3), get_field(s, v4), cpu_env, m6, g_cc_rt[es]); } else { - gen_gvec_4_ptr(get_field(s->fields, v1), get_field(s->fields, v2), - get_field(s->fields, v3), get_field(s->fields, v4), + gen_gvec_4_ptr(get_field(s, v1), get_field(s, v2), + get_field(s, v3), get_field(s, v4), cpu_env, m6, g_cc[es]); } set_cc_static(s); } else { if (extract32(m6, 2, 1)) { - gen_gvec_4_ool(get_field(s->fields, v1), get_field(s->fields, v2), - get_field(s->fields, v3), get_field(s->fields, v4), + gen_gvec_4_ool(get_field(s, v1), get_field(s, v2), + get_field(s, v3), get_field(s, v4), m6, g_rt[es]); } else { - gen_gvec_4_ool(get_field(s->fields, v1), get_field(s->fields, v2), - get_field(s->fields, v3), get_field(s->fields, v4), + gen_gvec_4_ool(get_field(s, v1), get_field(s, v2), + get_field(s, v3), get_field(s, v4), m6, g[es]); } } @@ -2514,8 +2514,8 @@ static DisasJumpType op_vstrc(DisasContext *s, DisasOps *o) static DisasJumpType op_vfa(DisasContext *s, DisasOps *o) { - const uint8_t fpf = get_field(s->fields, m4); - const uint8_t m5 = get_field(s->fields, m5); + const uint8_t fpf = get_field(s, m4); + const uint8_t m5 = get_field(s, m5); const bool se = extract32(m5, 3, 1); gen_helper_gvec_3_ptr *fn; @@ -2524,7 +2524,7 @@ static DisasJumpType op_vfa(DisasContext *s, DisasOps *o) return DISAS_NORETURN; } - switch (s->fields->op2) { + switch (s->fields.op2) { case 0xe3: fn = se ? gen_helper_gvec_vfa64s : gen_helper_gvec_vfa64; break; @@ -2540,26 +2540,26 @@ static DisasJumpType op_vfa(DisasContext *s, DisasOps *o) default: g_assert_not_reached(); } - gen_gvec_3_ptr(get_field(s->fields, v1), get_field(s->fields, v2), - get_field(s->fields, v3), cpu_env, 0, fn); + gen_gvec_3_ptr(get_field(s, v1), get_field(s, v2), + get_field(s, v3), cpu_env, 0, fn); return DISAS_NEXT; } static DisasJumpType op_wfc(DisasContext *s, DisasOps *o) { - const uint8_t fpf = get_field(s->fields, m3); - const uint8_t m4 = get_field(s->fields, m4); + const uint8_t fpf = get_field(s, m3); + const uint8_t m4 = get_field(s, m4); if (fpf != FPF_LONG || m4) { gen_program_exception(s, PGM_SPECIFICATION); return DISAS_NORETURN; } - if (s->fields->op2 == 0xcb) { - gen_gvec_2_ptr(get_field(s->fields, v1), get_field(s->fields, v2), + if (s->fields.op2 == 0xcb) { + gen_gvec_2_ptr(get_field(s, v1), get_field(s, v2), cpu_env, 0, gen_helper_gvec_wfc64); } else { - gen_gvec_2_ptr(get_field(s->fields, v1), get_field(s->fields, v2), + gen_gvec_2_ptr(get_field(s, v1), get_field(s, v2), cpu_env, 0, gen_helper_gvec_wfk64); } set_cc_static(s); @@ -2568,9 +2568,9 @@ static DisasJumpType op_wfc(DisasContext *s, DisasOps *o) static DisasJumpType op_vfc(DisasContext *s, DisasOps *o) { - const uint8_t fpf = get_field(s->fields, m4); - const uint8_t m5 = get_field(s->fields, m5); - const uint8_t m6 = get_field(s->fields, m6); + const uint8_t fpf = get_field(s, m4); + const uint8_t m5 = get_field(s, m5); + const uint8_t m6 = get_field(s, m6); const bool se = extract32(m5, 3, 1); const bool cs = extract32(m6, 0, 1); gen_helper_gvec_3_ptr *fn; @@ -2581,7 +2581,7 @@ static DisasJumpType op_vfc(DisasContext *s, DisasOps *o) } if (cs) { - switch (s->fields->op2) { + switch (s->fields.op2) { case 0xe8: fn = se ? gen_helper_gvec_vfce64s_cc : gen_helper_gvec_vfce64_cc; break; @@ -2595,7 +2595,7 @@ static DisasJumpType op_vfc(DisasContext *s, DisasOps *o) g_assert_not_reached(); } } else { - switch (s->fields->op2) { + switch (s->fields.op2) { case 0xe8: fn = se ? gen_helper_gvec_vfce64s : gen_helper_gvec_vfce64; break; @@ -2609,8 +2609,8 @@ static DisasJumpType op_vfc(DisasContext *s, DisasOps *o) g_assert_not_reached(); } } - gen_gvec_3_ptr(get_field(s->fields, v1), get_field(s->fields, v2), - get_field(s->fields, v3), cpu_env, 0, fn); + gen_gvec_3_ptr(get_field(s, v1), get_field(s, v2), + get_field(s, v3), cpu_env, 0, fn); if (cs) { set_cc_static(s); } @@ -2619,9 +2619,9 @@ static DisasJumpType op_vfc(DisasContext *s, DisasOps *o) static DisasJumpType op_vcdg(DisasContext *s, DisasOps *o) { - const uint8_t fpf = get_field(s->fields, m3); - const uint8_t m4 = get_field(s->fields, m4); - const uint8_t erm = get_field(s->fields, m5); + const uint8_t fpf = get_field(s, m3); + const uint8_t m4 = get_field(s, m4); + const uint8_t erm = get_field(s, m5); const bool se = extract32(m4, 3, 1); gen_helper_gvec_2_ptr *fn; @@ -2630,7 +2630,7 @@ static DisasJumpType op_vcdg(DisasContext *s, DisasOps *o) return DISAS_NORETURN; } - switch (s->fields->op2) { + switch (s->fields.op2) { case 0xc3: fn = se ? gen_helper_gvec_vcdg64s : gen_helper_gvec_vcdg64; break; @@ -2652,15 +2652,15 @@ static DisasJumpType op_vcdg(DisasContext *s, DisasOps *o) default: g_assert_not_reached(); } - gen_gvec_2_ptr(get_field(s->fields, v1), get_field(s->fields, v2), cpu_env, + gen_gvec_2_ptr(get_field(s, v1), get_field(s, v2), cpu_env, deposit32(m4, 4, 4, erm), fn); return DISAS_NEXT; } static DisasJumpType op_vfll(DisasContext *s, DisasOps *o) { - const uint8_t fpf = get_field(s->fields, m3); - const uint8_t m4 = get_field(s->fields, m4); + const uint8_t fpf = get_field(s, m3); + const uint8_t m4 = get_field(s, m4); gen_helper_gvec_2_ptr *fn = gen_helper_gvec_vfll32; if (fpf != FPF_SHORT || extract32(m4, 0, 3)) { @@ -2671,15 +2671,15 @@ static DisasJumpType op_vfll(DisasContext *s, DisasOps *o) if (extract32(m4, 3, 1)) { fn = gen_helper_gvec_vfll32s; } - gen_gvec_2_ptr(get_field(s->fields, v1), get_field(s->fields, v2), cpu_env, + gen_gvec_2_ptr(get_field(s, v1), get_field(s, v2), cpu_env, 0, fn); return DISAS_NEXT; } static DisasJumpType op_vfma(DisasContext *s, DisasOps *o) { - const uint8_t m5 = get_field(s->fields, m5); - const uint8_t fpf = get_field(s->fields, m6); + const uint8_t m5 = get_field(s, m5); + const uint8_t fpf = get_field(s, m6); const bool se = extract32(m5, 3, 1); gen_helper_gvec_4_ptr *fn; @@ -2688,24 +2688,24 @@ static DisasJumpType op_vfma(DisasContext *s, DisasOps *o) return DISAS_NORETURN; } - if (s->fields->op2 == 0x8f) { + if (s->fields.op2 == 0x8f) { fn = se ? gen_helper_gvec_vfma64s : gen_helper_gvec_vfma64; } else { fn = se ? gen_helper_gvec_vfms64s : gen_helper_gvec_vfms64; } - gen_gvec_4_ptr(get_field(s->fields, v1), get_field(s->fields, v2), - get_field(s->fields, v3), get_field(s->fields, v4), cpu_env, + gen_gvec_4_ptr(get_field(s, v1), get_field(s, v2), + get_field(s, v3), get_field(s, v4), cpu_env, 0, fn); return DISAS_NEXT; } static DisasJumpType op_vfpso(DisasContext *s, DisasOps *o) { - const uint8_t v1 = get_field(s->fields, v1); - const uint8_t v2 = get_field(s->fields, v2); - const uint8_t fpf = get_field(s->fields, m3); - const uint8_t m4 = get_field(s->fields, m4); - const uint8_t m5 = get_field(s->fields, m5); + const uint8_t v1 = get_field(s, v1); + const uint8_t v2 = get_field(s, v2); + const uint8_t fpf = get_field(s, m3); + const uint8_t m4 = get_field(s, m4); + const uint8_t m5 = get_field(s, m5); TCGv_i64 tmp; if (fpf != FPF_LONG || extract32(m4, 0, 3) || m5 > 2) { @@ -2753,8 +2753,8 @@ static DisasJumpType op_vfpso(DisasContext *s, DisasOps *o) static DisasJumpType op_vfsq(DisasContext *s, DisasOps *o) { - const uint8_t fpf = get_field(s->fields, m3); - const uint8_t m4 = get_field(s->fields, m4); + const uint8_t fpf = get_field(s, m3); + const uint8_t m4 = get_field(s, m4); gen_helper_gvec_2_ptr *fn = gen_helper_gvec_vfsq64; if (fpf != FPF_LONG || extract32(m4, 0, 3)) { @@ -2765,16 +2765,16 @@ static DisasJumpType op_vfsq(DisasContext *s, DisasOps *o) if (extract32(m4, 3, 1)) { fn = gen_helper_gvec_vfsq64s; } - gen_gvec_2_ptr(get_field(s->fields, v1), get_field(s->fields, v2), cpu_env, + gen_gvec_2_ptr(get_field(s, v1), get_field(s, v2), cpu_env, 0, fn); return DISAS_NEXT; } static DisasJumpType op_vftci(DisasContext *s, DisasOps *o) { - const uint16_t i3 = get_field(s->fields, i3); - const uint8_t fpf = get_field(s->fields, m4); - const uint8_t m5 = get_field(s->fields, m5); + const uint16_t i3 = get_field(s, i3); + const uint8_t fpf = get_field(s, m4); + const uint8_t m5 = get_field(s, m5); gen_helper_gvec_2_ptr *fn = gen_helper_gvec_vftci64; if (fpf != FPF_LONG || extract32(m5, 0, 3)) { @@ -2785,8 +2785,7 @@ static DisasJumpType op_vftci(DisasContext *s, DisasOps *o) if (extract32(m5, 3, 1)) { fn = gen_helper_gvec_vftci64s; } - gen_gvec_2_ptr(get_field(s->fields, v1), get_field(s->fields, v2), cpu_env, - i3, fn); + gen_gvec_2_ptr(get_field(s, v1), get_field(s, v2), cpu_env, i3, fn); set_cc_static(s); return DISAS_NEXT; } diff --git a/target/sh4/cpu-qom.h b/target/sh4/cpu-qom.h index 0c56d055ba..72a63f3fd3 100644 --- a/target/sh4/cpu-qom.h +++ b/target/sh4/cpu-qom.h @@ -51,7 +51,7 @@ typedef struct SuperHCPUClass { /*< public >*/ DeviceRealize parent_realize; - void (*parent_reset)(CPUState *cpu); + DeviceReset parent_reset; uint32_t pvr; uint32_t prr; diff --git a/target/sh4/cpu.c b/target/sh4/cpu.c index d0a7707991..3c68021c56 100644 --- a/target/sh4/cpu.c +++ b/target/sh4/cpu.c @@ -47,14 +47,14 @@ static bool superh_cpu_has_work(CPUState *cs) return cs->interrupt_request & CPU_INTERRUPT_HARD; } -/* CPUClass::reset() */ -static void superh_cpu_reset(CPUState *s) +static void superh_cpu_reset(DeviceState *dev) { + CPUState *s = CPU(dev); SuperHCPU *cpu = SUPERH_CPU(s); SuperHCPUClass *scc = SUPERH_CPU_GET_CLASS(cpu); CPUSH4State *env = &cpu->env; - scc->parent_reset(s); + scc->parent_reset(dev); memset(env, 0, offsetof(CPUSH4State, end_reset_fields)); @@ -214,8 +214,7 @@ static void superh_cpu_class_init(ObjectClass *oc, void *data) device_class_set_parent_realize(dc, superh_cpu_realizefn, &scc->parent_realize); - scc->parent_reset = cc->reset; - cc->reset = superh_cpu_reset; + device_class_set_parent_reset(dc, superh_cpu_reset, &scc->parent_reset); cc->class_by_name = superh_cpu_class_by_name; cc->has_work = superh_cpu_has_work; diff --git a/target/sh4/cpu.h b/target/sh4/cpu.h index ecaa7a18a9..dbe58c7888 100644 --- a/target/sh4/cpu.h +++ b/target/sh4/cpu.h @@ -208,7 +208,7 @@ void superh_cpu_do_interrupt(CPUState *cpu); bool superh_cpu_exec_interrupt(CPUState *cpu, int int_req); void superh_cpu_dump_state(CPUState *cpu, FILE *f, int flags); hwaddr superh_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); -int superh_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); +int superh_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int superh_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); void superh_cpu_do_unaligned_access(CPUState *cpu, vaddr addr, MMUAccessType access_type, @@ -254,8 +254,6 @@ void cpu_load_tlb(CPUSH4State * env); #define cpu_list sh4_cpu_list /* MMU modes definitions */ -#define MMU_MODE0_SUFFIX _kernel -#define MMU_MODE1_SUFFIX _user #define MMU_USER_IDX 1 static inline int cpu_mmu_index (CPUSH4State *env, bool ifetch) { diff --git a/target/sh4/gdbstub.c b/target/sh4/gdbstub.c index 44c1679e9d..34ad3ca050 100644 --- a/target/sh4/gdbstub.c +++ b/target/sh4/gdbstub.c @@ -24,7 +24,7 @@ /* Hint: Use "set architecture sh4" in GDB to see fpu registers */ /* FIXME: We should use XML for this. */ -int superh_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +int superh_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) { SuperHCPU *cpu = SUPERH_CPU(cs); CPUSH4State *env = &cpu->env; @@ -58,11 +58,9 @@ int superh_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) return gdb_get_regl(mem_buf, env->fpscr); case 25 ... 40: if (env->fpscr & FPSCR_FR) { - stfl_p(mem_buf, env->fregs[n - 9]); - } else { - stfl_p(mem_buf, env->fregs[n - 25]); + return gdb_get_float32(mem_buf, env->fregs[n - 9]); } - return 4; + return gdb_get_float32(mem_buf, env->fregs[n - 25]); case 41: return gdb_get_regl(mem_buf, env->ssr); case 42: diff --git a/target/sh4/translate.c b/target/sh4/translate.c index 922785e225..6192d83e8c 100644 --- a/target/sh4/translate.c +++ b/target/sh4/translate.c @@ -23,7 +23,7 @@ #include "cpu.h" #include "disas/disas.h" #include "exec/exec-all.h" -#include "tcg-op.h" +#include "tcg/tcg-op.h" #include "exec/cpu_ldst.h" #include "exec/helper-proto.h" #include "exec/helper-gen.h" diff --git a/target/sparc/TODO b/target/sparc/TODO deleted file mode 100644 index b8c727e858..0000000000 --- a/target/sparc/TODO +++ /dev/null @@ -1,88 +0,0 @@ -TODO-list: - -CPU common: -- Unimplemented features/bugs: - - Delay slot handling may fail sometimes (branch end of page, delay - slot next page) - - Atomical instructions - - CPU features should match real CPUs (also ASI selection) -- Optimizations/improvements: - - Condition code/branch handling like x86, also for FPU? - - Remove remaining explicit alignment checks - - Global register for regwptr, so that windowed registers can be - accessed directly - - Improve Sparc32plus addressing - - NPC/PC static optimisations (use JUMP_TB when possible)? (Is this - obsolete?) - - Synthetic instructions - - MMU model dependent on CPU model - - Select ASI helper at translation time (on V9 only if known) - - KQemu/KVM support for VM only - - Hardware breakpoint/watchpoint support - - Cache emulation mode - - Reverse-endian pages - - Faster FPU emulation - - Busy loop detection - -Sparc32 CPUs: -- Unimplemented features/bugs: - - Sun4/Sun4c MMUs - - Some V8 ASIs - -Sparc64 CPUs: -- Unimplemented features/bugs: - - Interrupt handling - - Secondary address space, other MMU functions - - Many V9/UA2005/UA2007 ASIs - - Rest of V9 instructions, missing VIS instructions - - IG/MG/AG vs. UA2007 globals - - Full hypervisor support - - SMP/CMT - - Sun4v CPUs - -Sun4: -- To be added - -Sun4c: -- A lot of unimplemented features -- Maybe split from Sun4m - -Sun4m: -- Unimplemented features/bugs: - - Hardware devices do not match real boards - - Floppy does not work - - CS4231: merge with cs4231a, add DMA - - Add cg6, bwtwo - - Arbitrary resolution support - - PCI for MicroSparc-IIe - - JavaStation machines - - SBus slot probing, FCode ROM support - - SMP probing support - - Interrupt routing does not match real HW - - SuSE 7.3 keyboard sometimes unresponsive - - Gentoo 2004.1 SMP does not work - - SS600MP ledma -> lebuffer - - Type 5 keyboard - - Less fixed hardware choices - - DBRI audio (Am7930) - - BPP parallel - - Diagnostic switch - - ESP PIO mode - -Sun4d: -- A lot of unimplemented features: - - SBI - - IO-unit -- Maybe split from Sun4m - -Sun4u: -- Unimplemented features/bugs: - - Interrupt controller - - PCI/IOMMU support (Simba, JIO, Tomatillo, Psycho, Schizo, Safari...) - - SMP - - Happy Meal Ethernet, flash, I2C, GPIO - - A lot of real machine types - -Sun4v: -- A lot of unimplemented features - - A lot of real machine types diff --git a/target/sparc/cpu-qom.h b/target/sparc/cpu-qom.h index 7442e2768e..8b4d33c21e 100644 --- a/target/sparc/cpu-qom.h +++ b/target/sparc/cpu-qom.h @@ -49,7 +49,7 @@ typedef struct SPARCCPUClass { /*< public >*/ DeviceRealize parent_realize; - void (*parent_reset)(CPUState *cpu); + DeviceReset parent_reset; sparc_def_t *cpu_def; } SPARCCPUClass; diff --git a/target/sparc/cpu.c b/target/sparc/cpu.c index bc65929552..3f05aba9d6 100644 --- a/target/sparc/cpu.c +++ b/target/sparc/cpu.c @@ -28,14 +28,14 @@ //#define DEBUG_FEATURES -/* CPUClass::reset() */ -static void sparc_cpu_reset(CPUState *s) +static void sparc_cpu_reset(DeviceState *dev) { + CPUState *s = CPU(dev); SPARCCPU *cpu = SPARC_CPU(s); SPARCCPUClass *scc = SPARC_CPU_GET_CLASS(cpu); CPUSPARCState *env = &cpu->env; - scc->parent_reset(s); + scc->parent_reset(dev); memset(env, 0, offsetof(CPUSPARCState, end_reset_fields)); env->cwp = 0; @@ -857,10 +857,9 @@ static void sparc_cpu_class_init(ObjectClass *oc, void *data) device_class_set_parent_realize(dc, sparc_cpu_realizefn, &scc->parent_realize); - dc->props = sparc_cpu_properties; + device_class_set_props(dc, sparc_cpu_properties); - scc->parent_reset = cc->reset; - cc->reset = sparc_cpu_reset; + device_class_set_parent_reset(dc, sparc_cpu_reset, &scc->parent_reset); cc->class_by_name = sparc_cpu_class_by_name; cc->parse_features = sparc_cpu_parse_features; diff --git a/target/sparc/cpu.h b/target/sparc/cpu.h index ae97c7d9f7..b9369398f2 100644 --- a/target/sparc/cpu.h +++ b/target/sparc/cpu.h @@ -571,7 +571,7 @@ extern const VMStateDescription vmstate_sparc_cpu; void sparc_cpu_do_interrupt(CPUState *cpu); void sparc_cpu_dump_state(CPUState *cpu, FILE *f, int flags); hwaddr sparc_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); -int sparc_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); +int sparc_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int sparc_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); void QEMU_NORETURN sparc_cpu_do_unaligned_access(CPUState *cpu, vaddr addr, MMUAccessType access_type, diff --git a/target/sparc/gdbstub.c b/target/sparc/gdbstub.c index 8be742b5a3..78dc8dcc98 100644 --- a/target/sparc/gdbstub.c +++ b/target/sparc/gdbstub.c @@ -27,7 +27,7 @@ #define gdb_get_rega(buf, val) gdb_get_regl(buf, val) #endif -int sparc_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +int sparc_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) { SPARCCPU *cpu = SPARC_CPU(cs); CPUSPARCState *env = &cpu->env; diff --git a/target/sparc/ldst_helper.c b/target/sparc/ldst_helper.c index 7345827a96..e91cfdecd3 100644 --- a/target/sparc/ldst_helper.c +++ b/target/sparc/ldst_helper.c @@ -19,7 +19,7 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "tcg.h" +#include "tcg/tcg.h" #include "exec/helper-proto.h" #include "exec/exec-all.h" #include "exec/cpu_ldst.h" diff --git a/target/sparc/translate.c b/target/sparc/translate.c index edc23a7c40..9416a551cf 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -24,7 +24,7 @@ #include "disas/disas.h" #include "exec/helper-proto.h" #include "exec/exec-all.h" -#include "tcg-op.h" +#include "tcg/tcg-op.h" #include "exec/cpu_ldst.h" #include "exec/helper-gen.h" diff --git a/target/tilegx/cpu.c b/target/tilegx/cpu.c index 2b2a7ccc31..1fee87c094 100644 --- a/target/tilegx/cpu.c +++ b/target/tilegx/cpu.c @@ -68,13 +68,14 @@ static bool tilegx_cpu_has_work(CPUState *cs) return true; } -static void tilegx_cpu_reset(CPUState *s) +static void tilegx_cpu_reset(DeviceState *dev) { + CPUState *s = CPU(dev); TileGXCPU *cpu = TILEGX_CPU(s); TileGXCPUClass *tcc = TILEGX_CPU_GET_CLASS(cpu); CPUTLGState *env = &cpu->env; - tcc->parent_reset(s); + tcc->parent_reset(dev); memset(env, 0, offsetof(CPUTLGState, end_reset_fields)); } @@ -142,8 +143,7 @@ static void tilegx_cpu_class_init(ObjectClass *oc, void *data) device_class_set_parent_realize(dc, tilegx_cpu_realizefn, &tcc->parent_realize); - tcc->parent_reset = cc->reset; - cc->reset = tilegx_cpu_reset; + device_class_set_parent_reset(dc, tilegx_cpu_reset, &tcc->parent_reset); cc->class_by_name = tilegx_cpu_class_by_name; cc->has_work = tilegx_cpu_has_work; diff --git a/target/tilegx/cpu.h b/target/tilegx/cpu.h index 9cbec247d2..193b6bbccb 100644 --- a/target/tilegx/cpu.h +++ b/target/tilegx/cpu.h @@ -118,7 +118,7 @@ typedef struct TileGXCPUClass { /*< public >*/ DeviceRealize parent_realize; - void (*parent_reset)(CPUState *cpu); + DeviceReset parent_reset; } TileGXCPUClass; /** diff --git a/target/tilegx/translate.c b/target/tilegx/translate.c index 68dd4aa2d8..65f1c91f4f 100644 --- a/target/tilegx/translate.c +++ b/target/tilegx/translate.c @@ -24,7 +24,7 @@ #include "exec/log.h" #include "disas/disas.h" #include "exec/exec-all.h" -#include "tcg-op.h" +#include "tcg/tcg-op.h" #include "exec/cpu_ldst.h" #include "linux-user/syscall_defs.h" @@ -2388,7 +2388,6 @@ void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int max_insns) dc->zero = NULL; if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) { - qemu_log_lock(); qemu_log("IN: %s\n", lookup_symbol(pc_start)); } gen_tb_start(tb); @@ -2417,11 +2416,6 @@ void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int max_insns) gen_tb_end(tb, num_insns); tb->size = dc->pc - pc_start; tb->icount = num_insns; - - if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) { - qemu_log("\n"); - qemu_log_unlock(); - } } void restore_state_to_opc(CPUTLGState *env, TranslationBlock *tb, diff --git a/target/tricore/cpu-qom.h b/target/tricore/cpu-qom.h index 7c1e130b4e..cd819e6f24 100644 --- a/target/tricore/cpu-qom.h +++ b/target/tricore/cpu-qom.h @@ -36,7 +36,7 @@ typedef struct TriCoreCPUClass { /*< public >*/ DeviceRealize parent_realize; - void (*parent_reset)(CPUState *cpu); + DeviceReset parent_reset; } TriCoreCPUClass; typedef struct TriCoreCPU TriCoreCPU; diff --git a/target/tricore/cpu.c b/target/tricore/cpu.c index df807c1d74..743b404a95 100644 --- a/target/tricore/cpu.c +++ b/target/tricore/cpu.c @@ -53,13 +53,14 @@ static void tricore_cpu_synchronize_from_tb(CPUState *cs, env->PC = tb->pc; } -static void tricore_cpu_reset(CPUState *s) +static void tricore_cpu_reset(DeviceState *dev) { + CPUState *s = CPU(dev); TriCoreCPU *cpu = TRICORE_CPU(s); TriCoreCPUClass *tcc = TRICORE_CPU_GET_CLASS(cpu); CPUTriCoreState *env = &cpu->env; - tcc->parent_reset(s); + tcc->parent_reset(dev); cpu_state_reset(env); } @@ -153,8 +154,7 @@ static void tricore_cpu_class_init(ObjectClass *c, void *data) device_class_set_parent_realize(dc, tricore_cpu_realizefn, &mcc->parent_realize); - mcc->parent_reset = cc->reset; - cc->reset = tricore_cpu_reset; + device_class_set_parent_reset(dc, tricore_cpu_reset, &mcc->parent_reset); cc->class_by_name = tricore_cpu_class_by_name; cc->has_work = tricore_cpu_has_work; diff --git a/target/tricore/translate.c b/target/tricore/translate.c index c574638c9f..609d75ae8a 100644 --- a/target/tricore/translate.c +++ b/target/tricore/translate.c @@ -22,7 +22,7 @@ #include "cpu.h" #include "disas/disas.h" #include "exec/exec-all.h" -#include "tcg-op.h" +#include "tcg/tcg-op.h" #include "exec/cpu_ldst.h" #include "qemu/qemu-print.h" diff --git a/target/unicore32/cpu.h b/target/unicore32/cpu.h index 50ed9dd99c..7a32e086ed 100644 --- a/target/unicore32/cpu.h +++ b/target/unicore32/cpu.h @@ -133,8 +133,6 @@ void cpu_asr_write(CPUUniCore32State *env1, target_ulong val, target_ulong mask) int uc32_cpu_signal_handler(int host_signum, void *pinfo, void *puc); /* MMU modes definitions */ -#define MMU_MODE0_SUFFIX _kernel -#define MMU_MODE1_SUFFIX _user #define MMU_USER_IDX 1 static inline int cpu_mmu_index(CPUUniCore32State *env, bool ifetch) { diff --git a/target/unicore32/translate.c b/target/unicore32/translate.c index 0e01f35856..d4b06df672 100644 --- a/target/unicore32/translate.c +++ b/target/unicore32/translate.c @@ -13,7 +13,7 @@ #include "cpu.h" #include "disas/disas.h" #include "exec/exec-all.h" -#include "tcg-op.h" +#include "tcg/tcg-op.h" #include "qemu/log.h" #include "exec/cpu_ldst.h" #include "exec/translator.h" @@ -1994,12 +1994,12 @@ done_generating: #ifdef DEBUG_DISAS if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM) && qemu_log_in_addr_range(pc_start)) { - qemu_log_lock(); + FILE *logfile = qemu_log_lock(); qemu_log("----------------\n"); qemu_log("IN: %s\n", lookup_symbol(pc_start)); log_target_disas(cs, pc_start, dc->pc - pc_start); qemu_log("\n"); - qemu_log_unlock(); + qemu_log_unlock(logfile); } #endif tb->size = dc->pc - pc_start; diff --git a/target/xtensa/cpu-qom.h b/target/xtensa/cpu-qom.h index 9ac54241bd..3ea93ce1f9 100644 --- a/target/xtensa/cpu-qom.h +++ b/target/xtensa/cpu-qom.h @@ -56,7 +56,7 @@ typedef struct XtensaCPUClass { /*< public >*/ DeviceRealize parent_realize; - void (*parent_reset)(CPUState *cpu); + DeviceReset parent_reset; const XtensaConfig *config; } XtensaCPUClass; diff --git a/target/xtensa/cpu.c b/target/xtensa/cpu.c index c65dcf9dd7..82c2ee0679 100644 --- a/target/xtensa/cpu.c +++ b/target/xtensa/cpu.c @@ -67,14 +67,14 @@ bool xtensa_abi_call0(void) } #endif -/* CPUClass::reset() */ -static void xtensa_cpu_reset(CPUState *s) +static void xtensa_cpu_reset(DeviceState *dev) { + CPUState *s = CPU(dev); XtensaCPU *cpu = XTENSA_CPU(s); XtensaCPUClass *xcc = XTENSA_CPU_GET_CLASS(cpu); CPUXtensaState *env = &cpu->env; - xcc->parent_reset(s); + xcc->parent_reset(dev); env->exception_taken = 0; env->pc = env->config->exception_vector[EXC_RESET0 + env->static_vectors]; @@ -184,8 +184,7 @@ static void xtensa_cpu_class_init(ObjectClass *oc, void *data) device_class_set_parent_realize(dc, xtensa_cpu_realizefn, &xcc->parent_realize); - xcc->parent_reset = cc->reset; - cc->reset = xtensa_cpu_reset; + device_class_set_parent_reset(dc, xtensa_cpu_reset, &xcc->parent_reset); cc->class_by_name = xtensa_cpu_class_by_name; cc->has_work = xtensa_cpu_has_work; diff --git a/target/xtensa/cpu.h b/target/xtensa/cpu.h index b363ffcf10..7a46dccbe1 100644 --- a/target/xtensa/cpu.h +++ b/target/xtensa/cpu.h @@ -213,6 +213,9 @@ enum { #define MEMCTL_IL0EN 0x1 #define MAX_INSN_LENGTH 64 +#define MAX_INSNBUF_LENGTH \ + ((MAX_INSN_LENGTH + sizeof(xtensa_insnbuf_word) - 1) / \ + sizeof(xtensa_insnbuf_word)) #define MAX_INSN_SLOTS 32 #define MAX_OPCODE_ARGS 16 #define MAX_NAREG 64 @@ -569,7 +572,7 @@ void xtensa_cpu_dump_state(CPUState *cpu, FILE *f, int flags); hwaddr xtensa_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); void xtensa_count_regs(const XtensaConfig *config, unsigned *n_regs, unsigned *n_core_regs); -int xtensa_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); +int xtensa_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int xtensa_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); void xtensa_cpu_do_unaligned_access(CPUState *cpu, vaddr addr, MMUAccessType access_type, @@ -645,7 +648,9 @@ static inline int xtensa_get_cintlevel(const CPUXtensaState *env) static inline int xtensa_get_ring(const CPUXtensaState *env) { - if (xtensa_option_enabled(env->config, XTENSA_OPTION_MMU)) { + if (xtensa_option_bits_enabled(env->config, + XTENSA_OPTION_BIT(XTENSA_OPTION_MMU) | + XTENSA_OPTION_BIT(XTENSA_OPTION_MPU))) { return (env->sregs[PS] & PS_RING) >> PS_RING_SHIFT; } else { return 0; @@ -654,8 +659,10 @@ static inline int xtensa_get_ring(const CPUXtensaState *env) static inline int xtensa_get_cring(const CPUXtensaState *env) { - if (xtensa_option_enabled(env->config, XTENSA_OPTION_MMU) && - (env->sregs[PS] & PS_EXCM) == 0) { + if (xtensa_option_bits_enabled(env->config, + XTENSA_OPTION_BIT(XTENSA_OPTION_MMU) | + XTENSA_OPTION_BIT(XTENSA_OPTION_MPU)) && + (env->sregs[PS] & PS_EXCM) == 0) { return (env->sregs[PS] & PS_RING) >> PS_RING_SHIFT; } else { return 0; @@ -685,10 +692,6 @@ static inline uint32_t xtensa_replicate_windowstart(CPUXtensaState *env) } /* MMU modes definitions */ -#define MMU_MODE0_SUFFIX _ring0 -#define MMU_MODE1_SUFFIX _ring1 -#define MMU_MODE2_SUFFIX _ring2 -#define MMU_MODE3_SUFFIX _ring3 #define MMU_USER_IDX 3 static inline int cpu_mmu_index(CPUXtensaState *env, bool ifetch) diff --git a/target/xtensa/gdbstub.c b/target/xtensa/gdbstub.c index 54727881f3..4d43f1340a 100644 --- a/target/xtensa/gdbstub.c +++ b/target/xtensa/gdbstub.c @@ -63,7 +63,7 @@ void xtensa_count_regs(const XtensaConfig *config, } } -int xtensa_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +int xtensa_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) { XtensaCPU *cpu = XTENSA_CPU(cs); CPUXtensaState *env = &cpu->env; @@ -105,8 +105,7 @@ int xtensa_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) default: qemu_log_mask(LOG_UNIMP, "%s from reg %d of unsupported size %d\n", __func__, n, reg->size); - memset(mem_buf, 0, reg->size); - return reg->size; + return gdb_get_zeroes(mem_buf, reg->size); } case xtRegisterTypeWindow: /*a*/ @@ -115,8 +114,7 @@ int xtensa_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) default: qemu_log_mask(LOG_UNIMP, "%s from reg %d of unsupported type %d\n", __func__, n, reg->type); - memset(mem_buf, 0, reg->size); - return reg->size; + return gdb_get_zeroes(mem_buf, reg->size); } } diff --git a/target/xtensa/helper.c b/target/xtensa/helper.c index 376a61f339..7073381f03 100644 --- a/target/xtensa/helper.c +++ b/target/xtensa/helper.c @@ -96,6 +96,7 @@ static void init_libisa(XtensaConfig *config) config->isa = xtensa_isa_init(config->isa_internal, NULL, NULL); assert(xtensa_isa_maxlength(config->isa) <= MAX_INSN_LENGTH); + assert(xtensa_insnbuf_size(config->isa) <= MAX_INSNBUF_LENGTH); opcodes = xtensa_isa_num_opcodes(config->isa); formats = xtensa_isa_num_formats(config->isa); regfiles = xtensa_isa_num_regfiles(config->isa); diff --git a/target/xtensa/import_core.sh b/target/xtensa/import_core.sh index e4a2e39f63..8f844cf9e2 100755 --- a/target/xtensa/import_core.sh +++ b/target/xtensa/import_core.sh @@ -19,8 +19,9 @@ exit [ $# -ge 3 ] && FREQ="$3" mkdir -p "$TARGET" -tar -xf "$OVERLAY" -C "$TARGET" --strip-components=1 \ - --xform='s/core/core-isa/' config/core.h +tar -xf "$OVERLAY" -C "$TARGET" --strip-components=2 \ + xtensa/config/core-isa.h \ + xtensa/config/core-matmap.h tar -xf "$OVERLAY" -O gdb/xtensa-config.c | \ sed -n '1,/*\//p;/XTREG/,/XTREG_END/p' > "$TARGET"/gdb-config.inc.c # @@ -44,6 +45,7 @@ cat < "${TARGET}.c" #include "qemu/host-utils.h" #include "core-$NAME/core-isa.h" +#include "core-$NAME/core-matmap.h" #include "overlay_tool.h" #define xtensa_modules xtensa_modules_$NAME diff --git a/target/xtensa/mmu_helper.c b/target/xtensa/mmu_helper.c index f15bff306f..b01ff9399a 100644 --- a/target/xtensa/mmu_helper.c +++ b/target/xtensa/mmu_helper.c @@ -63,10 +63,11 @@ void HELPER(itlb_hit_test)(CPUXtensaState *env, uint32_t vaddr) { /* - * Attempt the memory load; we don't care about the result but + * Probe the memory; we don't care about the result but * only the side-effects (ie any MMU or other exception) */ - cpu_ldub_code_ra(env, vaddr, GETPC()); + probe_access(env, vaddr, 1, MMU_INST_FETCH, + cpu_mmu_index(env, true), GETPC()); } void HELPER(wsr_rasid)(CPUXtensaState *env, uint32_t v) diff --git a/target/xtensa/overlay_tool.h b/target/xtensa/overlay_tool.h index f0cc33adfe..cab532095c 100644 --- a/target/xtensa/overlay_tool.h +++ b/target/xtensa/overlay_tool.h @@ -373,15 +373,28 @@ #elif XCHAL_HAVE_MPU #ifndef XTENSA_MPU_BG_MAP +#ifdef XCHAL_MPU_BACKGROUND_MAP +#define XCHAL_MPU_BGMAP(s, vaddr_start, vaddr_last, rights, memtype, x...) \ + { .vaddr = (vaddr_start), .attr = ((rights) << 8) | ((memtype) << 12), }, + +#define XTENSA_MPU_BG_MAP (xtensa_mpu_entry []){\ + XCHAL_MPU_BACKGROUND_MAP(0) \ +} + +#define XTENSA_MPU_BG_MAP_ENTRIES XCHAL_MPU_BACKGROUND_ENTRIES +#else #define XTENSA_MPU_BG_MAP (xtensa_mpu_entry []){\ { .vaddr = 0, .attr = 0x00006700, }, \ } + +#define XTENSA_MPU_BG_MAP_ENTRIES 1 +#endif #endif #define TLB_SECTION \ .mpu_align = XCHAL_MPU_ALIGN, \ .n_mpu_fg_segments = XCHAL_MPU_ENTRIES, \ - .n_mpu_bg_segments = 1, \ + .n_mpu_bg_segments = XTENSA_MPU_BG_MAP_ENTRIES, \ .mpu_bg = XTENSA_MPU_BG_MAP #ifndef XCHAL_SYSROM0_PADDR diff --git a/target/xtensa/translate.c b/target/xtensa/translate.c index a99f5296e2..e0beaf7abb 100644 --- a/target/xtensa/translate.c +++ b/target/xtensa/translate.c @@ -33,7 +33,7 @@ #include "cpu.h" #include "exec/exec-all.h" #include "disas/disas.h" -#include "tcg-op.h" +#include "tcg/tcg-op.h" #include "qemu/log.h" #include "qemu/qemu-print.h" #include "exec/cpu_ldst.h" @@ -72,8 +72,8 @@ struct DisasContext { unsigned cpenable; uint32_t op_flags; - xtensa_insnbuf insnbuf; - xtensa_insnbuf slotbuf; + xtensa_insnbuf_word insnbuf[MAX_INSNBUF_LENGTH]; + xtensa_insnbuf_word slotbuf[MAX_INSNBUF_LENGTH]; }; static TCGv_i32 cpu_pc; @@ -1173,11 +1173,6 @@ static void xtensa_tr_init_disas_context(DisasContextBase *dcbase, dc->cwoe = tb_flags & XTENSA_TBFLAG_CWOE; dc->callinc = ((tb_flags & XTENSA_TBFLAG_CALLINC_MASK) >> XTENSA_TBFLAG_CALLINC_SHIFT); - - if (dc->config->isa) { - dc->insnbuf = xtensa_insnbuf_alloc(dc->config->isa); - dc->slotbuf = xtensa_insnbuf_alloc(dc->config->isa); - } init_sar_tracker(dc); } @@ -1267,10 +1262,6 @@ static void xtensa_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) DisasContext *dc = container_of(dcbase, DisasContext, base); reset_sar_tracker(dc); - if (dc->config->isa) { - xtensa_insnbuf_free(dc->config->isa, dc->insnbuf); - xtensa_insnbuf_free(dc->config->isa, dc->slotbuf); - } if (dc->icount) { tcg_temp_free(dc->next_icount); } @@ -2713,7 +2704,8 @@ static void translate_wsr_ps(DisasContext *dc, const OpcodeArg arg[], uint32_t mask = PS_WOE | PS_CALLINC | PS_OWB | PS_UM | PS_EXCM | PS_INTLEVEL; - if (option_enabled(dc, XTENSA_OPTION_MMU)) { + if (option_enabled(dc, XTENSA_OPTION_MMU) || + option_enabled(dc, XTENSA_OPTION_MPU)) { mask |= PS_RING; } tcg_gen_andi_i32(cpu_SR[par[0]], arg[0].in, mask); @@ -3740,7 +3732,7 @@ static const XtensaOpcodeOps core_ops[] = { .name = "pfwait.a", .translate = translate_nop, }, { - .name = "pfwait.o", + .name = "pfwait.r", .translate = translate_nop, }, { .name = "pitlb", diff --git a/tcg/aarch64/tcg-target.inc.c b/tcg/aarch64/tcg-target.inc.c index 3f921015d3..843fd0ca69 100644 --- a/tcg/aarch64/tcg-target.inc.c +++ b/tcg/aarch64/tcg-target.inc.c @@ -10,7 +10,7 @@ * See the COPYING file in the top-level directory for details. */ -#include "tcg-pool.inc.c" +#include "../tcg-pool.inc.c" #include "qemu/bitops.h" /* We're going to re-use TCGType in setting of the SF bit, which controls @@ -1541,7 +1541,7 @@ static void tcg_out_cltz(TCGContext *s, TCGType ext, TCGReg d, } #ifdef CONFIG_SOFTMMU -#include "tcg-ldst.inc.c" +#include "../tcg-ldst.inc.c" /* helper signature: helper_ret_ld_mmu(CPUState *env, target_ulong addr, * TCGMemOpIdx oi, uintptr_t ra) diff --git a/tcg/arm/tcg-target.inc.c b/tcg/arm/tcg-target.inc.c index 94d80d79d1..6aa7757aac 100644 --- a/tcg/arm/tcg-target.inc.c +++ b/tcg/arm/tcg-target.inc.c @@ -23,7 +23,7 @@ */ #include "elf.h" -#include "tcg-pool.inc.c" +#include "../tcg-pool.inc.c" int arm_arch = __ARM_ARCH; @@ -1131,7 +1131,7 @@ static TCGCond tcg_out_cmp2(TCGContext *s, const TCGArg *args, } #ifdef CONFIG_SOFTMMU -#include "tcg-ldst.inc.c" +#include "../tcg-ldst.inc.c" /* helper signature: helper_ret_ld_mmu(CPUState *env, target_ulong addr, * int mmu_idx, uintptr_t ra) @@ -1745,7 +1745,7 @@ static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, bool is64) #endif } -static tcg_insn_unit *tb_ret_addr; +static void tcg_out_epilogue(TCGContext *s); static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, const TCGArg *args, const int *const_args) @@ -1755,14 +1755,8 @@ static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, switch (opc) { case INDEX_op_exit_tb: - /* Reuse the zeroing that exists for goto_ptr. */ - a0 = args[0]; - if (a0 == 0) { - tcg_out_goto(s, COND_AL, s->code_gen_epilogue); - } else { - tcg_out_movi32(s, COND_AL, TCG_REG_R0, args[0]); - tcg_out_goto(s, COND_AL, tb_ret_addr); - } + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_R0, args[0]); + tcg_out_epilogue(s); break; case INDEX_op_goto_tb: { @@ -2284,19 +2278,17 @@ static void tcg_out_nop_fill(tcg_insn_unit *p, int count) + TCG_TARGET_STACK_ALIGN - 1) \ & -TCG_TARGET_STACK_ALIGN) +#define STACK_ADDEND (FRAME_SIZE - PUSH_SIZE) + static void tcg_target_qemu_prologue(TCGContext *s) { - int stack_addend; - /* Calling convention requires us to save r4-r11 and lr. */ /* stmdb sp!, { r4 - r11, lr } */ tcg_out32(s, (COND_AL << 28) | 0x092d4ff0); /* Reserve callee argument and tcg temp space. */ - stack_addend = FRAME_SIZE - PUSH_SIZE; - tcg_out_dat_rI(s, COND_AL, ARITH_SUB, TCG_REG_CALL_STACK, - TCG_REG_CALL_STACK, stack_addend, 1); + TCG_REG_CALL_STACK, STACK_ADDEND, 1); tcg_set_frame(s, TCG_REG_CALL_STACK, TCG_STATIC_CALL_ARGS_SIZE, CPU_TEMP_BUF_NLONGS * sizeof(long)); @@ -2310,11 +2302,14 @@ static void tcg_target_qemu_prologue(TCGContext *s) */ s->code_gen_epilogue = s->code_ptr; tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_R0, 0); + tcg_out_epilogue(s); +} - /* TB epilogue */ - tb_ret_addr = s->code_ptr; +static void tcg_out_epilogue(TCGContext *s) +{ + /* Release local stack frame. */ tcg_out_dat_rI(s, COND_AL, ARITH_ADD, TCG_REG_CALL_STACK, - TCG_REG_CALL_STACK, stack_addend, 1); + TCG_REG_CALL_STACK, STACK_ADDEND, 1); /* ldmia sp!, { r4 - r11, pc } */ tcg_out32(s, (COND_AL << 28) | 0x08bd8ff0); diff --git a/tcg/i386/tcg-target.h b/tcg/i386/tcg-target.h index 928e8b87bb..bfb3f5f6e9 100644 --- a/tcg/i386/tcg-target.h +++ b/tcg/i386/tcg-target.h @@ -223,7 +223,7 @@ static inline void tb_target_set_jmp_target(uintptr_t tc_ptr, * The x86 has a pretty strong memory ordering which only really * allows for some stores to be re-ordered after loads. */ -#include "tcg-mo.h" +#include "tcg/tcg-mo.h" #define TCG_TARGET_DEFAULT_MO (TCG_MO_ALL & ~TCG_MO_ST_LD) diff --git a/tcg/i386/tcg-target.inc.c b/tcg/i386/tcg-target.inc.c index 9d8ed974e0..ec083bddcf 100644 --- a/tcg/i386/tcg-target.inc.c +++ b/tcg/i386/tcg-target.inc.c @@ -22,7 +22,7 @@ * THE SOFTWARE. */ -#include "tcg-pool.inc.c" +#include "../tcg-pool.inc.c" #ifdef CONFIG_DEBUG_TCG static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = { @@ -1647,7 +1647,7 @@ static void tcg_out_nopn(TCGContext *s, int n) } #if defined(CONFIG_SOFTMMU) -#include "tcg-ldst.inc.c" +#include "../tcg-ldst.inc.c" /* helper signature: helper_ret_ld_mmu(CPUState *env, target_ulong addr, * int mmu_idx, uintptr_t ra) @@ -2855,9 +2855,13 @@ static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, goto gen_simd; #if TCG_TARGET_REG_BITS == 32 case INDEX_op_dup2_vec: - /* Constraints have already placed both 32-bit inputs in xmm regs. */ - insn = OPC_PUNPCKLDQ; - goto gen_simd; + /* First merge the two 32-bit inputs to a single 64-bit element. */ + tcg_out_vex_modrm(s, OPC_PUNPCKLDQ, a0, a1, a2); + /* Then replicate the 64-bit elements across the rest of the vector. */ + if (type != TCG_TYPE_V64) { + tcg_out_dup_vec(s, type, MO_64, a0, a0); + } + break; #endif case INDEX_op_abs_vec: insn = abs_insn[vece]; @@ -3391,12 +3395,15 @@ static void expand_vec_sari(TCGType type, unsigned vece, case MO_64: if (imm <= 32) { - /* We can emulate a small sign extend by performing an arithmetic + /* + * We can emulate a small sign extend by performing an arithmetic * 32-bit shift and overwriting the high half of a 64-bit logical - * shift (note that the ISA says shift of 32 is valid). + * shift. Note that the ISA says shift of 32 is valid, but TCG + * does not, so we have to bound the smaller shift -- we get the + * same result in the high half either way. */ t1 = tcg_temp_new_vec(type); - tcg_gen_sari_vec(MO_32, t1, v1, imm); + tcg_gen_sari_vec(MO_32, t1, v1, MIN(imm, 31)); tcg_gen_shri_vec(MO_64, v0, v1, imm); vec_gen_4(INDEX_op_x86_blend_vec, type, MO_32, tcgv_vec_arg(v0), tcgv_vec_arg(v0), @@ -3730,7 +3737,7 @@ static void tcg_target_qemu_prologue(TCGContext *s) } else { /* Choose R12 because, as a base, it requires a SIB byte. */ x86_guest_base_index = TCG_REG_R12; - tcg_out_mov(s, TCG_TYPE_PTR, x86_guest_base_index, guest_base); + tcg_out_movi(s, TCG_TYPE_PTR, x86_guest_base_index, guest_base); tcg_regset_set_reg(s->reserved_regs, x86_guest_base_index); } } diff --git a/tcg/mips/tcg-target.inc.c b/tcg/mips/tcg-target.inc.c index 5442167045..4d32ebc1df 100644 --- a/tcg/mips/tcg-target.inc.c +++ b/tcg/mips/tcg-target.inc.c @@ -404,11 +404,11 @@ typedef enum { /* MIPS r6 introduced names for weaker variants of SYNC. These are backward compatible to previous architecture revisions. */ - OPC_SYNC_WMB = OPC_SYNC | 0x04 << 5, - OPC_SYNC_MB = OPC_SYNC | 0x10 << 5, - OPC_SYNC_ACQUIRE = OPC_SYNC | 0x11 << 5, - OPC_SYNC_RELEASE = OPC_SYNC | 0x12 << 5, - OPC_SYNC_RMB = OPC_SYNC | 0x13 << 5, + OPC_SYNC_WMB = OPC_SYNC | 0x04 << 6, + OPC_SYNC_MB = OPC_SYNC | 0x10 << 6, + OPC_SYNC_ACQUIRE = OPC_SYNC | 0x11 << 6, + OPC_SYNC_RELEASE = OPC_SYNC | 0x12 << 6, + OPC_SYNC_RMB = OPC_SYNC | 0x13 << 6, /* Aliases for convenience. */ ALIAS_PADD = sizeof(void *) == 4 ? OPC_ADDU : OPC_DADDU, @@ -1107,7 +1107,7 @@ static void tcg_out_call(TCGContext *s, tcg_insn_unit *arg) } #if defined(CONFIG_SOFTMMU) -#include "tcg-ldst.inc.c" +#include "../tcg-ldst.inc.c" static void * const qemu_ld_helpers[16] = { [MO_UB] = helper_ret_ldub_mmu, diff --git a/tcg/optimize.c b/tcg/optimize.c index f7f4e873c9..53aa8e5329 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -24,7 +24,7 @@ */ #include "qemu/osdep.h" -#include "tcg-op.h" +#include "tcg/tcg-op.h" #define CASE_OP_32_64(x) \ glue(glue(case INDEX_op_, x), _i32): \ diff --git a/tcg/ppc/tcg-target.inc.c b/tcg/ppc/tcg-target.inc.c index d308d69aba..ee1f9227c1 100644 --- a/tcg/ppc/tcg-target.inc.c +++ b/tcg/ppc/tcg-target.inc.c @@ -23,7 +23,7 @@ */ #include "elf.h" -#include "tcg-pool.inc.c" +#include "../tcg-pool.inc.c" #if defined _CALL_DARWIN || defined __APPLE__ #define TCG_TARGET_CALL_DARWIN @@ -1845,7 +1845,7 @@ static const uint32_t qemu_exts_opc[4] = { }; #if defined (CONFIG_SOFTMMU) -#include "tcg-ldst.inc.c" +#include "../tcg-ldst.inc.c" /* helper signature: helper_ld_mmu(CPUState *env, target_ulong addr, * int mmu_idx, uintptr_t ra) diff --git a/tcg/riscv/tcg-target.inc.c b/tcg/riscv/tcg-target.inc.c index 7018509693..2bc0ba71f2 100644 --- a/tcg/riscv/tcg-target.inc.c +++ b/tcg/riscv/tcg-target.inc.c @@ -27,7 +27,7 @@ * THE SOFTWARE. */ -#include "tcg-pool.inc.c" +#include "../tcg-pool.inc.c" #ifdef CONFIG_DEBUG_TCG static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = { @@ -921,7 +921,7 @@ static void tcg_out_mb(TCGContext *s, TCGArg a0) */ #if defined(CONFIG_SOFTMMU) -#include "tcg-ldst.inc.c" +#include "../tcg-ldst.inc.c" /* helper signature: helper_ret_ld_mmu(CPUState *env, target_ulong addr, * TCGMemOpIdx oi, uintptr_t ra) diff --git a/tcg/s390/tcg-target.inc.c b/tcg/s390/tcg-target.inc.c index 8aaa4cebe8..b07e9ff7d6 100644 --- a/tcg/s390/tcg-target.inc.c +++ b/tcg/s390/tcg-target.inc.c @@ -29,7 +29,7 @@ #error "unsupported code generation mode" #endif -#include "tcg-pool.inc.c" +#include "../tcg-pool.inc.c" #include "elf.h" /* ??? The translation blocks produced by TCG are generally small enough to @@ -1536,7 +1536,7 @@ static void tcg_out_qemu_st_direct(TCGContext *s, MemOp opc, TCGReg data, } #if defined(CONFIG_SOFTMMU) -#include "tcg-ldst.inc.c" +#include "../tcg-ldst.inc.c" /* We're expecting to use a 20-bit negative offset on the tlb memory ops. */ QEMU_BUILD_BUG_ON(TLB_MASK_TABLE_OFS(0) > 0); diff --git a/tcg/sparc/tcg-target.inc.c b/tcg/sparc/tcg-target.inc.c index d7986cda5c..65fddb310d 100644 --- a/tcg/sparc/tcg-target.inc.c +++ b/tcg/sparc/tcg-target.inc.c @@ -22,7 +22,7 @@ * THE SOFTWARE. */ -#include "tcg-pool.inc.c" +#include "../tcg-pool.inc.c" #ifdef CONFIG_DEBUG_TCG static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = { diff --git a/tcg/tcg-common.c b/tcg/tcg-common.c index 97305a3efc..7e1992e79e 100644 --- a/tcg/tcg-common.c +++ b/tcg/tcg-common.c @@ -32,7 +32,7 @@ uintptr_t tci_tb_ptr; TCGOpDef tcg_op_defs[] = { #define DEF(s, oargs, iargs, cargs, flags) \ { #s, oargs, iargs, cargs, iargs + oargs + cargs, flags }, -#include "tcg-opc.h" +#include "tcg/tcg-opc.h" #undef DEF }; const size_t tcg_op_defs_max = ARRAY_SIZE(tcg_op_defs); diff --git a/tcg/tcg-op-gvec.c b/tcg/tcg-op-gvec.c index 5c95ecd51c..327d9588e0 100644 --- a/tcg/tcg-op-gvec.c +++ b/tcg/tcg-op-gvec.c @@ -18,11 +18,11 @@ */ #include "qemu/osdep.h" -#include "tcg.h" -#include "tcg-op.h" -#include "tcg-op-gvec.h" +#include "tcg/tcg.h" +#include "tcg/tcg-op.h" +#include "tcg/tcg-op-gvec.h" #include "qemu/main-loop.h" -#include "tcg-gvec-desc.h" +#include "tcg/tcg-gvec-desc.h" #define MAX_UNROLL 4 @@ -290,6 +290,38 @@ void tcg_gen_gvec_4_ptr(uint32_t dofs, uint32_t aofs, uint32_t bofs, tcg_temp_free_i32(desc); } +/* Generate a call to a gvec-style helper with five vector operands + and an extra pointer operand. */ +void tcg_gen_gvec_5_ptr(uint32_t dofs, uint32_t aofs, uint32_t bofs, + uint32_t cofs, uint32_t eofs, TCGv_ptr ptr, + uint32_t oprsz, uint32_t maxsz, int32_t data, + gen_helper_gvec_5_ptr *fn) +{ + TCGv_ptr a0, a1, a2, a3, a4; + TCGv_i32 desc = tcg_const_i32(simd_desc(oprsz, maxsz, data)); + + a0 = tcg_temp_new_ptr(); + a1 = tcg_temp_new_ptr(); + a2 = tcg_temp_new_ptr(); + a3 = tcg_temp_new_ptr(); + a4 = tcg_temp_new_ptr(); + + tcg_gen_addi_ptr(a0, cpu_env, dofs); + tcg_gen_addi_ptr(a1, cpu_env, aofs); + tcg_gen_addi_ptr(a2, cpu_env, bofs); + tcg_gen_addi_ptr(a3, cpu_env, cofs); + tcg_gen_addi_ptr(a4, cpu_env, eofs); + + fn(a0, a1, a2, a3, a4, ptr, desc); + + tcg_temp_free_ptr(a0); + tcg_temp_free_ptr(a1); + tcg_temp_free_ptr(a2); + tcg_temp_free_ptr(a3); + tcg_temp_free_ptr(a4); + tcg_temp_free_i32(desc); +} + /* Return true if we want to implement something of OPRSZ bytes in units of LNSZ. This limits the expansion of inline code. */ static inline bool check_size_impl(uint32_t oprsz, uint32_t lnsz) diff --git a/tcg/tcg-op-vec.c b/tcg/tcg-op-vec.c index 6714991bf4..b6937e8d64 100644 --- a/tcg/tcg-op-vec.c +++ b/tcg/tcg-op-vec.c @@ -19,9 +19,9 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "tcg.h" -#include "tcg-op.h" -#include "tcg-mo.h" +#include "tcg/tcg.h" +#include "tcg/tcg-op.h" +#include "tcg/tcg-mo.h" /* Reduce the number of ifdefs below. This assumes that all uses of TCGV_HIGH and TCGV_LOW are properly protected by a conditional that diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index c245126f98..e2e25ebf7d 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -25,9 +25,9 @@ #include "qemu/osdep.h" #include "cpu.h" #include "exec/exec-all.h" -#include "tcg.h" -#include "tcg-op.h" -#include "tcg-mo.h" +#include "tcg/tcg.h" +#include "tcg/tcg-op.h" +#include "tcg/tcg-mo.h" #include "trace-tcg.h" #include "trace/mem.h" #include "exec/plugin-gen.h" @@ -2794,13 +2794,26 @@ static void tcg_gen_req_mo(TCGBar type) } } +static inline TCGv plugin_prep_mem_callbacks(TCGv vaddr) +{ +#ifdef CONFIG_PLUGIN + if (tcg_ctx->plugin_insn != NULL) { + /* Save a copy of the vaddr for use after a load. */ + TCGv temp = tcg_temp_new(); + tcg_gen_mov_tl(temp, vaddr); + return temp; + } +#endif + return vaddr; +} + static inline void plugin_gen_mem_callbacks(TCGv vaddr, uint16_t info) { #ifdef CONFIG_PLUGIN - if (tcg_ctx->plugin_insn == NULL) { - return; + if (tcg_ctx->plugin_insn != NULL) { + plugin_gen_empty_mem_callback(vaddr, info); + tcg_temp_free(vaddr); } - plugin_gen_empty_mem_callback(vaddr, info); #endif } @@ -2822,6 +2835,7 @@ void tcg_gen_qemu_ld_i32(TCGv_i32 val, TCGv addr, TCGArg idx, MemOp memop) } } + addr = plugin_prep_mem_callbacks(addr); gen_ldst_i32(INDEX_op_qemu_ld_i32, val, addr, memop, idx); plugin_gen_mem_callbacks(addr, info); @@ -2868,6 +2882,7 @@ void tcg_gen_qemu_st_i32(TCGv_i32 val, TCGv addr, TCGArg idx, MemOp memop) memop &= ~MO_BSWAP; } + addr = plugin_prep_mem_callbacks(addr); gen_ldst_i32(INDEX_op_qemu_st_i32, val, addr, memop, idx); plugin_gen_mem_callbacks(addr, info); @@ -2905,6 +2920,7 @@ void tcg_gen_qemu_ld_i64(TCGv_i64 val, TCGv addr, TCGArg idx, MemOp memop) } } + addr = plugin_prep_mem_callbacks(addr); gen_ldst_i64(INDEX_op_qemu_ld_i64, val, addr, memop, idx); plugin_gen_mem_callbacks(addr, info); @@ -2967,6 +2983,7 @@ void tcg_gen_qemu_st_i64(TCGv_i64 val, TCGv addr, TCGArg idx, MemOp memop) memop &= ~MO_BSWAP; } + addr = plugin_prep_mem_callbacks(addr); gen_ldst_i64(INDEX_op_qemu_st_i64, val, addr, memop, idx); plugin_gen_mem_callbacks(addr, info); diff --git a/tcg/tcg.c b/tcg/tcg.c index 5475d49ed1..dd4b3d7684 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -48,7 +48,7 @@ #include "hw/boards.h" #endif -#include "tcg-op.h" +#include "tcg/tcg-op.h" #if UINTPTR_MAX == UINT32_MAX # define ELF_CLASS ELFCLASS32 @@ -1085,7 +1085,7 @@ void tcg_prologue_init(TCGContext *s) #ifdef DEBUG_DISAS if (qemu_loglevel_mask(CPU_LOG_TB_OUT_ASM)) { - qemu_log_lock(); + FILE *logfile = qemu_log_lock(); qemu_log("PROLOGUE: [size=%zu]\n", prologue_size); if (s->data_gen_ptr) { size_t code_size = s->data_gen_ptr - buf0; @@ -1110,7 +1110,7 @@ void tcg_prologue_init(TCGContext *s) } qemu_log("\n"); qemu_log_flush(); - qemu_log_unlock(); + qemu_log_unlock(logfile); } #endif @@ -2114,9 +2114,17 @@ static void tcg_dump_ops(TCGContext *s, bool have_prefs) } if (have_prefs || op->life) { - for (; col < 40; ++col) { - putc(' ', qemu_logfile); + + QemuLogFile *logfile; + + rcu_read_lock(); + logfile = atomic_rcu_read(&qemu_logfile); + if (logfile) { + for (; col < 40; ++col) { + putc(' ', logfile->fd); + } } + rcu_read_unlock(); } if (op->life) { @@ -4041,11 +4049,11 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb) #ifdef DEBUG_DISAS if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP) && qemu_log_in_addr_range(tb->pc))) { - qemu_log_lock(); + FILE *logfile = qemu_log_lock(); qemu_log("OP:\n"); tcg_dump_ops(s, false); qemu_log("\n"); - qemu_log_unlock(); + qemu_log_unlock(logfile); } #endif @@ -4086,11 +4094,11 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb) #ifdef DEBUG_DISAS if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP_IND) && qemu_log_in_addr_range(tb->pc))) { - qemu_log_lock(); + FILE *logfile = qemu_log_lock(); qemu_log("OP before indirect lowering:\n"); tcg_dump_ops(s, false); qemu_log("\n"); - qemu_log_unlock(); + qemu_log_unlock(logfile); } #endif /* Replace indirect temps with direct temps. */ @@ -4107,11 +4115,11 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb) #ifdef DEBUG_DISAS if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP_OPT) && qemu_log_in_addr_range(tb->pc))) { - qemu_log_lock(); + FILE *logfile = qemu_log_lock(); qemu_log("OP after optimization and liveness analysis:\n"); tcg_dump_ops(s, true); qemu_log("\n"); - qemu_log_unlock(); + qemu_log_unlock(logfile); } #endif diff --git a/tcg/tci.c b/tcg/tci.c index a6208653e8..46fe9ce63f 100644 --- a/tcg/tci.c +++ b/tcg/tci.c @@ -30,7 +30,7 @@ #include "qemu-common.h" #include "tcg/tcg.h" /* MAX_OPC_PARAM_IARGS */ #include "exec/cpu_ldst.h" -#include "tcg-op.h" +#include "tcg/tcg-op.h" /* Marker for missing code. */ #define TODO() \ diff --git a/tests/.gitignore b/tests/.gitignore index f9c0170881..d03c037d77 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -10,8 +10,10 @@ qht-bench rcutorture test-* !test-*.c +!test-*.py !docker/test-* test-qapi-commands.[ch] +test-qapi-init-commands.[ch] include/test-qapi-commands-sub-module.[ch] test-qapi-commands-sub-sub-module.[ch] test-qapi-emit-events.[ch] diff --git a/tests/Makefile.include b/tests/Makefile.include index 8566f5f119..51de676298 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -16,12 +16,12 @@ check-help: @echo " $(MAKE) check-softfloat Run FPU emulation tests" @echo " $(MAKE) check-acceptance Run all acceptance (functional) tests" @echo - @echo " $(MAKE) check-report.html Generates an HTML test report" + @echo " $(MAKE) check-report.tap Generates an aggregated TAP test report" @echo " $(MAKE) check-venv Creates a Python venv for tests" @echo " $(MAKE) check-clean Clean the tests and related data" @echo - @echo "Please note that HTML reports do not regenerate if the unit tests" - @echo "have not changed." + @echo " $(MAKE) get-vm-images Downloads all images used by acceptance tests, according to configured targets (~350 MB each, 1.5 GB max)" + @echo @echo @echo "The variable SPEED can be set to control the gtester speed setting." @echo "Default options are -k and (for $(MAKE) V=1) --verbose; they can be" @@ -100,6 +100,7 @@ check-unit-y += tests/rcutorture$(EXESUF) check-unit-y += tests/test-rcu-list$(EXESUF) check-unit-y += tests/test-rcu-simpleq$(EXESUF) check-unit-y += tests/test-rcu-tailq$(EXESUF) +check-unit-y += tests/test-rcu-slist$(EXESUF) check-unit-y += tests/test-qdist$(EXESUF) check-unit-y += tests/test-qht$(EXESUF) check-unit-y += tests/test-qht-par$(EXESUF) @@ -151,155 +152,6 @@ check-unit-y += tests/test-qapi-util$(EXESUF) check-block-$(call land,$(CONFIG_POSIX),$(CONFIG_SOFTMMU)) += tests/check-block.sh -# All QTests for now are POSIX-only, but the dependencies are -# really in libqtest, not in the testcases themselves. - -check-qtest-generic-y += tests/qmp-test$(EXESUF) -check-qtest-generic-y += tests/qmp-cmd-test$(EXESUF) -check-qtest-generic-$(CONFIG_MODULES) += tests/modules-test$(EXESUF) - -check-qtest-generic-y += tests/device-introspect-test$(EXESUF) -check-qtest-generic-y += tests/cdrom-test$(EXESUF) - -check-qtest-pci-$(CONFIG_RTL8139_PCI) += tests/rtl8139-test$(EXESUF) -check-qtest-pci-$(CONFIG_VGA) += tests/display-vga-test$(EXESUF) -check-qtest-pci-$(CONFIG_HDA) += tests/intel-hda-test$(EXESUF) -check-qtest-pci-$(CONFIG_IVSHMEM_DEVICE) += tests/ivshmem-test$(EXESUF) - -check-qtest-i386-$(CONFIG_ISA_TESTDEV) = tests/endianness-test$(EXESUF) -check-qtest-i386-y += tests/fdc-test$(EXESUF) -check-qtest-i386-y += tests/ide-test$(EXESUF) -check-qtest-i386-y += tests/ahci-test$(EXESUF) -check-qtest-i386-y += tests/hd-geo-test$(EXESUF) -check-qtest-i386-y += tests/boot-order-test$(EXESUF) -check-qtest-i386-y += tests/bios-tables-test$(EXESUF) -check-qtest-i386-$(CONFIG_SGA) += tests/boot-serial-test$(EXESUF) -check-qtest-i386-$(CONFIG_SLIRP) += tests/pxe-test$(EXESUF) -check-qtest-i386-y += tests/rtc-test$(EXESUF) -check-qtest-i386-$(CONFIG_ISA_IPMI_KCS) += tests/ipmi-kcs-test$(EXESUF) -ifdef CONFIG_LINUX -check-qtest-i386-$(CONFIG_ISA_IPMI_BT) += tests/ipmi-bt-test$(EXESUF) -endif -check-qtest-i386-y += tests/i440fx-test$(EXESUF) -check-qtest-i386-y += tests/fw_cfg-test$(EXESUF) -check-qtest-i386-y += tests/device-plug-test$(EXESUF) -check-qtest-i386-y += tests/drive_del-test$(EXESUF) -check-qtest-i386-$(CONFIG_WDT_IB700) += tests/wdt_ib700-test$(EXESUF) -check-qtest-i386-y += tests/tco-test$(EXESUF) -check-qtest-i386-y += $(check-qtest-pci-y) -check-qtest-i386-$(CONFIG_PVPANIC) += tests/pvpanic-test$(EXESUF) -check-qtest-i386-$(CONFIG_I82801B11) += tests/i82801b11-test$(EXESUF) -check-qtest-i386-$(CONFIG_IOH3420) += tests/ioh3420-test$(EXESUF) -check-qtest-i386-$(CONFIG_USB_UHCI) += tests/usb-hcd-uhci-test$(EXESUF) -check-qtest-i386-$(call land,$(CONFIG_USB_EHCI),$(CONFIG_USB_UHCI)) += tests/usb-hcd-ehci-test$(EXESUF) -check-qtest-i386-$(CONFIG_USB_XHCI_NEC) += tests/usb-hcd-xhci-test$(EXESUF) -check-qtest-i386-y += tests/cpu-plug-test$(EXESUF) -check-qtest-i386-y += tests/q35-test$(EXESUF) -check-qtest-i386-y += tests/vmgenid-test$(EXESUF) -check-qtest-i386-$(CONFIG_TPM_CRB) += tests/tpm-crb-swtpm-test$(EXESUF) -check-qtest-i386-$(CONFIG_TPM_CRB) += tests/tpm-crb-test$(EXESUF) -check-qtest-i386-$(CONFIG_TPM_TIS) += tests/tpm-tis-swtpm-test$(EXESUF) -check-qtest-i386-$(CONFIG_TPM_TIS) += tests/tpm-tis-test$(EXESUF) -check-qtest-i386-$(CONFIG_SLIRP) += tests/test-netfilter$(EXESUF) -check-qtest-i386-$(CONFIG_POSIX) += tests/test-filter-mirror$(EXESUF) -check-qtest-i386-$(CONFIG_RTL8139_PCI) += tests/test-filter-redirector$(EXESUF) -check-qtest-i386-y += tests/migration-test$(EXESUF) -check-qtest-i386-y += tests/test-x86-cpuid-compat$(EXESUF) -check-qtest-i386-y += tests/numa-test$(EXESUF) -check-qtest-x86_64-y += $(check-qtest-i386-y) - -check-qtest-alpha-y += tests/boot-serial-test$(EXESUF) -check-qtest-alpha-$(CONFIG_VGA) += tests/display-vga-test$(EXESUF) - -check-qtest-hppa-y += tests/boot-serial-test$(EXESUF) -check-qtest-hppa-$(CONFIG_VGA) += tests/display-vga-test$(EXESUF) - -check-qtest-m68k-y = tests/boot-serial-test$(EXESUF) - -check-qtest-microblaze-y += tests/boot-serial-test$(EXESUF) - -check-qtest-mips-$(CONFIG_ISA_TESTDEV) = tests/endianness-test$(EXESUF) -check-qtest-mips-$(CONFIG_VGA) += tests/display-vga-test$(EXESUF) - -check-qtest-mips64-$(CONFIG_ISA_TESTDEV) = tests/endianness-test$(EXESUF) -check-qtest-mips64-$(CONFIG_VGA) += tests/display-vga-test$(EXESUF) - -check-qtest-mips64el-$(CONFIG_ISA_TESTDEV) = tests/endianness-test$(EXESUF) -check-qtest-mips64el-$(CONFIG_VGA) += tests/display-vga-test$(EXESUF) - -check-qtest-moxie-y += tests/boot-serial-test$(EXESUF) - -check-qtest-ppc-$(CONFIG_ISA_TESTDEV) = tests/endianness-test$(EXESUF) -check-qtest-ppc-y += tests/boot-order-test$(EXESUF) -check-qtest-ppc-y += tests/prom-env-test$(EXESUF) -check-qtest-ppc-y += tests/drive_del-test$(EXESUF) -check-qtest-ppc-y += tests/boot-serial-test$(EXESUF) -check-qtest-ppc-$(CONFIG_M48T59) += tests/m48t59-test$(EXESUF) - -check-qtest-ppc64-y += $(check-qtest-ppc-y) -check-qtest-ppc64-$(CONFIG_PSERIES) += tests/device-plug-test$(EXESUF) -check-qtest-ppc64-$(CONFIG_POWERNV) += tests/pnv-xscom-test$(EXESUF) -check-qtest-ppc64-y += tests/migration-test$(EXESUF) -check-qtest-ppc64-$(CONFIG_PSERIES) += tests/rtas-test$(EXESUF) -check-qtest-ppc64-$(CONFIG_SLIRP) += tests/pxe-test$(EXESUF) -check-qtest-ppc64-$(CONFIG_USB_UHCI) += tests/usb-hcd-uhci-test$(EXESUF) -check-qtest-ppc64-$(CONFIG_USB_XHCI_NEC) += tests/usb-hcd-xhci-test$(EXESUF) -check-qtest-ppc64-$(CONFIG_SLIRP) += tests/test-netfilter$(EXESUF) -check-qtest-ppc64-$(CONFIG_POSIX) += tests/test-filter-mirror$(EXESUF) -check-qtest-ppc64-$(CONFIG_RTL8139_PCI) += tests/test-filter-redirector$(EXESUF) -check-qtest-ppc64-$(CONFIG_VGA) += tests/display-vga-test$(EXESUF) -check-qtest-ppc64-y += tests/numa-test$(EXESUF) -check-qtest-ppc64-$(CONFIG_IVSHMEM_DEVICE) += tests/ivshmem-test$(EXESUF) -check-qtest-ppc64-y += tests/cpu-plug-test$(EXESUF) - -check-qtest-sh4-$(CONFIG_ISA_TESTDEV) = tests/endianness-test$(EXESUF) - -check-qtest-sh4eb-$(CONFIG_ISA_TESTDEV) = tests/endianness-test$(EXESUF) - -check-qtest-sparc-y += tests/prom-env-test$(EXESUF) -check-qtest-sparc-y += tests/m48t59-test$(EXESUF) -check-qtest-sparc-y += tests/boot-serial-test$(EXESUF) - -check-qtest-sparc64-$(CONFIG_ISA_TESTDEV) = tests/endianness-test$(EXESUF) -check-qtest-sparc64-y += tests/prom-env-test$(EXESUF) -check-qtest-sparc64-y += tests/boot-serial-test$(EXESUF) - -check-qtest-arm-y += tests/arm-cpu-features$(EXESUF) -check-qtest-arm-y += tests/microbit-test$(EXESUF) -check-qtest-arm-y += tests/m25p80-test$(EXESUF) -check-qtest-arm-y += tests/test-arm-mptimer$(EXESUF) -check-qtest-arm-y += tests/boot-serial-test$(EXESUF) -check-qtest-arm-y += tests/hexloader-test$(EXESUF) -check-qtest-arm-$(CONFIG_PFLASH_CFI02) += tests/pflash-cfi02-test$(EXESUF) - -check-qtest-aarch64-y += tests/arm-cpu-features$(EXESUF) -check-qtest-aarch64-y += tests/numa-test$(EXESUF) -check-qtest-aarch64-y += tests/boot-serial-test$(EXESUF) -check-qtest-aarch64-y += tests/migration-test$(EXESUF) -# TODO: once aarch64 TCG is fixed on ARM 32 bit host, make test unconditional -ifneq ($(ARCH),arm) -check-qtest-aarch64-y += tests/bios-tables-test$(EXESUF) -endif - -check-qtest-microblazeel-y += $(check-qtest-microblaze-y) - -check-qtest-xtensaeb-y += $(check-qtest-xtensa-y) - -check-qtest-s390x-y = tests/boot-serial-test$(EXESUF) -check-qtest-s390x-$(CONFIG_SLIRP) += tests/pxe-test$(EXESUF) -check-qtest-s390x-$(CONFIG_SLIRP) += tests/test-netfilter$(EXESUF) -check-qtest-s390x-$(CONFIG_POSIX) += tests/test-filter-mirror$(EXESUF) -check-qtest-s390x-$(CONFIG_POSIX) += tests/test-filter-redirector$(EXESUF) -check-qtest-s390x-y += tests/drive_del-test$(EXESUF) -check-qtest-s390x-y += tests/device-plug-test$(EXESUF) -check-qtest-s390x-y += tests/virtio-ccw-test$(EXESUF) -check-qtest-s390x-y += tests/cpu-plug-test$(EXESUF) -check-qtest-s390x-y += tests/migration-test$(EXESUF) - -check-qtest-generic-y += tests/machine-none-test$(EXESUF) -check-qtest-generic-y += tests/qom-test$(EXESUF) -check-qtest-generic-y += tests/test-hmp$(EXESUF) - qapi-schema += alternate-any.json qapi-schema += alternate-array.json qapi-schema += alternate-base.json @@ -392,6 +244,7 @@ qapi-schema += event-case.json qapi-schema += event-member-invalid-dict.json qapi-schema += event-nest-struct.json qapi-schema += features-bad-type.json +qapi-schema += features-deprecated-type.json qapi-schema += features-duplicate-name.json qapi-schema += features-if-invalid.json qapi-schema += features-missing-name.json @@ -501,14 +354,16 @@ generated-files-y += tests/test-qapi-visit.h generated-files-y += tests/include/test-qapi-visit-sub-module.h generated-files-y += tests/test-qapi-visit-sub-sub-module.h generated-files-y += tests/test-qapi-commands.h +generated-files-y += tests/test-qapi-init-commands.h generated-files-y += tests/include/test-qapi-commands-sub-module.h generated-files-y += tests/test-qapi-commands-sub-sub-module.h +generated-files-y += tests/test-qapi-emit-events.h generated-files-y += tests/test-qapi-events.h generated-files-y += tests/include/test-qapi-events-sub-module.h generated-files-y += tests/test-qapi-events-sub-sub-module.h generated-files-y += tests/test-qapi-introspect.h -QEMU_CFLAGS += -I$(SRC_PATH)/tests +QEMU_CFLAGS += -I$(SRC_PATH)/tests -I$(SRC_PATH)/tests/qtest # Deps that are common to various different sets of tests below @@ -540,7 +395,7 @@ tests/check-qlit$(EXESUF): tests/check-qlit.o $(test-util-obj-y) tests/check-qom-interface$(EXESUF): tests/check-qom-interface.o $(test-qom-obj-y) tests/check-qom-proplist$(EXESUF): tests/check-qom-proplist.o $(test-qom-obj-y) -tests/test-char$(EXESUF): tests/test-char.o $(test-util-obj-y) $(qtest-obj-y) $(test-io-obj-y) $(chardev-obj-y) tests/socket-helpers.o +tests/test-char$(EXESUF): tests/test-char.o $(test-util-obj-y) $(test-io-obj-y) $(chardev-obj-y) tests/socket-helpers.o tests/test-coroutine$(EXESUF): tests/test-coroutine.o $(test-block-obj-y) tests/test-aio$(EXESUF): tests/test-aio.o $(test-block-obj-y) tests/test-aio-multithread$(EXESUF): tests/test-aio-multithread.o $(test-block-obj-y) @@ -564,6 +419,7 @@ tests/rcutorture$(EXESUF): tests/rcutorture.o $(test-util-obj-y) tests/test-rcu-list$(EXESUF): tests/test-rcu-list.o $(test-util-obj-y) tests/test-rcu-simpleq$(EXESUF): tests/test-rcu-simpleq.o $(test-util-obj-y) tests/test-rcu-tailq$(EXESUF): tests/test-rcu-tailq.o $(test-util-obj-y) +tests/test-rcu-slist$(EXESUF): tests/test-rcu-slist.o $(test-util-obj-y) tests/test-qdist$(EXESUF): tests/test-qdist.o $(test-util-obj-y) tests/test-qht$(EXESUF): tests/test-qht.o $(test-util-obj-y) tests/test-qht-par$(EXESUF): tests/test-qht-par.o tests/qht-bench$(EXESUF) $(test-util-obj-y) @@ -578,9 +434,11 @@ tests/fp/%: tests/test-qdev-global-props$(EXESUF): tests/test-qdev-global-props.o \ hw/core/qdev.o hw/core/qdev-properties.o hw/core/hotplug.o\ hw/core/bus.o \ + hw/core/resettable.o \ hw/core/irq.o \ hw/core/fw-path-provider.o \ hw/core/reset.o \ + hw/core/vmstate-if.o \ $(test-qapi-obj-y) tests/test-vmstate$(EXESUF): tests/test-vmstate.o \ migration/vmstate.o migration/vmstate-types.o migration/qemu-file.o \ @@ -589,6 +447,11 @@ tests/test-vmstate$(EXESUF): tests/test-vmstate.o \ tests/test-timed-average$(EXESUF): tests/test-timed-average.o $(test-util-obj-y) tests/test-base64$(EXESUF): tests/test-base64.o $(test-util-obj-y) tests/ptimer-test$(EXESUF): tests/ptimer-test.o tests/ptimer-test-stubs.o hw/core/ptimer.o +tests/test-qemu-opts$(EXESUF): tests/test-qemu-opts.o $(test-util-obj-y) +tests/test-keyval$(EXESUF): tests/test-keyval.o $(test-util-obj-y) $(test-qapi-obj-y) +tests/test-write-threshold$(EXESUF): tests/test-write-threshold.o $(test-block-obj-y) +tests/test-uuid$(EXESUF): tests/test-uuid.o $(test-util-obj-y) +tests/test-qapi-util$(EXESUF): tests/test-qapi-util.o $(test-util-obj-y) tests/test-logging$(EXESUF): tests/test-logging.o $(test-util-obj-y) @@ -610,7 +473,10 @@ tests/include/test-qapi-commands-sub-module.h \ tests/include/test-qapi-commands-sub-module.c \ tests/test-qapi-commands-sub-sub-module.h \ tests/test-qapi-commands-sub-sub-module.c \ +tests/test-qapi-emit-events.c tests/test-qapi-emit-events.h \ tests/test-qapi-events.c tests/test-qapi-events.h \ +tests/test-qapi-init-commands.c \ +tests/test-qapi-init-commands.h \ tests/include/test-qapi-events-sub-module.c \ tests/include/test-qapi-events-sub-module.h \ tests/test-qapi-events-sub-sub-module.c \ @@ -635,13 +501,26 @@ tests/qapi-schema/doc-good.test.texi: $(SRC_PATH)/tests/qapi-schema/doc-good.jso @mv tests/qapi-schema/doc-good-qapi-doc.texi $@ @rm -f tests/qapi-schema/doc-good-qapi-*.[ch] tests/qapi-schema/doc-good-qmp-*.[ch] +tests/qtest/dbus-vmstate1.h tests/qtest/dbus-vmstate1.c: tests/qtest/dbus-vmstate1-gen-timestamp ; +tests/qtest/dbus-vmstate1-gen-timestamp: $(SRC_PATH)/tests/qtest/dbus-vmstate1.xml + $(call quiet-command,$(GDBUS_CODEGEN) $< \ + --interface-prefix org.qemu --generate-c-code tests/qtest/dbus-vmstate1, \ + "GEN","$(@:%-timestamp=%)") + @>$@ + +tests/qtest/dbus-vmstate-test.o-cflags := -DSRCDIR="$(SRC_PATH)" +tests/qtest/dbus-vmstate1.o-cflags := $(GIO_CFLAGS) +tests/qtest/dbus-vmstate1.o-libs := $(GIO_LIBS) + +tests/qtest/dbus-vmstate-test.o: tests/qtest/dbus-vmstate1.h + tests/test-string-output-visitor$(EXESUF): tests/test-string-output-visitor.o $(test-qapi-obj-y) tests/test-string-input-visitor$(EXESUF): tests/test-string-input-visitor.o $(test-qapi-obj-y) -tests/test-qmp-event$(EXESUF): tests/test-qmp-event.o $(test-qapi-obj-y) tests/test-qapi-events.o +tests/test-qmp-event$(EXESUF): tests/test-qmp-event.o $(test-qapi-obj-y) tests/test-qapi-emit-events.o tests/test-qapi-events.o tests/test-qobject-output-visitor$(EXESUF): tests/test-qobject-output-visitor.o $(test-qapi-obj-y) tests/test-clone-visitor$(EXESUF): tests/test-clone-visitor.o $(test-qapi-obj-y) tests/test-qobject-input-visitor$(EXESUF): tests/test-qobject-input-visitor.o $(test-qapi-obj-y) -tests/test-qmp-cmds$(EXESUF): tests/test-qmp-cmds.o tests/test-qapi-commands.o $(test-qapi-obj-y) +tests/test-qmp-cmds$(EXESUF): tests/test-qmp-cmds.o tests/test-qapi-commands.o tests/test-qapi-init-commands.o $(test-qapi-obj-y) tests/test-visitor-serialization$(EXESUF): tests/test-visitor-serialization.o $(test-qapi-obj-y) tests/test-opts-visitor$(EXESUF): tests/test-opts-visitor.o $(test-qapi-obj-y) @@ -682,12 +561,6 @@ tests/test-authz-pam$(EXESUF): tests/test-authz-pam.o $(test-authz-obj-y) tests/test-io-task$(EXESUF): tests/test-io-task.o $(test-io-obj-y) tests/test-io-channel-socket$(EXESUF): tests/test-io-channel-socket.o \ tests/io-channel-helpers.o tests/socket-helpers.o $(test-io-obj-y) -tests/tpm-crb-swtpm-test$(EXESUF): tests/tpm-crb-swtpm-test.o tests/tpm-emu.o \ - tests/tpm-util.o tests/tpm-tests.o $(test-io-obj-y) -tests/tpm-crb-test$(EXESUF): tests/tpm-crb-test.o tests/tpm-emu.o $(test-io-obj-y) -tests/tpm-tis-swtpm-test$(EXESUF): tests/tpm-tis-swtpm-test.o tests/tpm-emu.o \ - tests/tpm-util.o tests/tpm-tests.o $(test-io-obj-y) -tests/tpm-tis-test$(EXESUF): tests/tpm-tis-test.o tests/tpm-emu.o $(test-io-obj-y) tests/test-io-channel-file$(EXESUF): tests/test-io-channel-file.o \ tests/io-channel-helpers.o $(test-io-obj-y) tests/test-io-channel-tls$(EXESUF): tests/test-io-channel-tls.o \ @@ -702,151 +575,6 @@ tests/test-crypto-ivgen$(EXESUF): tests/test-crypto-ivgen.o $(test-crypto-obj-y) tests/test-crypto-afsplit$(EXESUF): tests/test-crypto-afsplit.o $(test-crypto-obj-y) tests/test-crypto-block$(EXESUF): tests/test-crypto-block.o $(test-crypto-obj-y) -libqgraph-obj-y = tests/libqos/qgraph.o - -libqos-obj-y = $(libqgraph-obj-y) tests/libqos/pci.o tests/libqos/fw_cfg.o -libqos-obj-y += tests/libqos/malloc.o -libqos-obj-y += tests/libqos/libqos.o -libqos-spapr-obj-y = $(libqos-obj-y) tests/libqos/malloc-spapr.o -libqos-spapr-obj-y += tests/libqos/libqos-spapr.o -libqos-spapr-obj-y += tests/libqos/rtas.o -libqos-spapr-obj-y += tests/libqos/pci-spapr.o -libqos-pc-obj-y = $(libqos-obj-y) tests/libqos/pci-pc.o -libqos-pc-obj-y += tests/libqos/malloc-pc.o tests/libqos/libqos-pc.o -libqos-pc-obj-y += tests/libqos/ahci.o -libqos-usb-obj-y = $(libqos-spapr-obj-y) $(libqos-pc-obj-y) tests/libqos/usb.o - -# Devices -qos-test-obj-y = tests/qos-test.o $(libqgraph-obj-y) -qos-test-obj-y += $(libqos-pc-obj-y) $(libqos-spapr-obj-y) -qos-test-obj-y += tests/libqos/e1000e.o -qos-test-obj-y += tests/libqos/i2c.o -qos-test-obj-y += tests/libqos/i2c-imx.o -qos-test-obj-y += tests/libqos/i2c-omap.o -qos-test-obj-y += tests/libqos/sdhci.o -qos-test-obj-y += tests/libqos/tpci200.o -qos-test-obj-y += tests/libqos/virtio.o -qos-test-obj-$(CONFIG_VIRTFS) += tests/libqos/virtio-9p.o -qos-test-obj-y += tests/libqos/virtio-balloon.o -qos-test-obj-y += tests/libqos/virtio-blk.o -qos-test-obj-y += tests/libqos/virtio-mmio.o -qos-test-obj-y += tests/libqos/virtio-net.o -qos-test-obj-y += tests/libqos/virtio-pci.o -qos-test-obj-y += tests/libqos/virtio-pci-modern.o -qos-test-obj-y += tests/libqos/virtio-rng.o -qos-test-obj-y += tests/libqos/virtio-scsi.o -qos-test-obj-y += tests/libqos/virtio-serial.o - -# Machines -qos-test-obj-y += tests/libqos/aarch64-xlnx-zcu102-machine.o -qos-test-obj-y += tests/libqos/arm-imx25-pdk-machine.o -qos-test-obj-y += tests/libqos/arm-n800-machine.o -qos-test-obj-y += tests/libqos/arm-raspi2-machine.o -qos-test-obj-y += tests/libqos/arm-sabrelite-machine.o -qos-test-obj-y += tests/libqos/arm-smdkc210-machine.o -qos-test-obj-y += tests/libqos/arm-virt-machine.o -qos-test-obj-y += tests/libqos/arm-xilinx-zynq-a9-machine.o -qos-test-obj-y += tests/libqos/ppc64_pseries-machine.o -qos-test-obj-y += tests/libqos/x86_64_pc-machine.o - -# Tests -qos-test-obj-y += tests/ac97-test.o -qos-test-obj-y += tests/ds1338-test.o -qos-test-obj-y += tests/e1000-test.o -qos-test-obj-y += tests/e1000e-test.o -qos-test-obj-y += tests/eepro100-test.o -qos-test-obj-y += tests/es1370-test.o -qos-test-obj-y += tests/ipoctal232-test.o -qos-test-obj-y += tests/megasas-test.o -qos-test-obj-y += tests/ne2000-test.o -qos-test-obj-y += tests/nvme-test.o -qos-test-obj-y += tests/pca9552-test.o -qos-test-obj-y += tests/pci-test.o -qos-test-obj-y += tests/pcnet-test.o -qos-test-obj-y += tests/sdhci-test.o -qos-test-obj-y += tests/spapr-phb-test.o -qos-test-obj-y += tests/tmp105-test.o -qos-test-obj-y += tests/usb-hcd-ohci-test.o $(libqos-usb-obj-y) -qos-test-obj-$(CONFIG_VHOST_NET_USER) += tests/vhost-user-test.o $(chardev-obj-y) $(test-io-obj-y) -qos-test-obj-y += tests/virtio-test.o -qos-test-obj-$(CONFIG_VIRTFS) += tests/virtio-9p-test.o -qos-test-obj-y += tests/virtio-blk-test.o -qos-test-obj-y += tests/virtio-net-test.o -qos-test-obj-y += tests/virtio-rng-test.o -qos-test-obj-y += tests/virtio-scsi-test.o -qos-test-obj-y += tests/virtio-serial-test.o -qos-test-obj-y += tests/vmxnet3-test.o - -check-unit-y += tests/test-qgraph$(EXESUF) -tests/test-qgraph$(EXESUF): tests/test-qgraph.o $(libqgraph-obj-y) - -check-qtest-generic-y += tests/qos-test$(EXESUF) -tests/qos-test$(EXESUF): $(qos-test-obj-y) - -tests/qmp-test$(EXESUF): tests/qmp-test.o -tests/qmp-cmd-test$(EXESUF): tests/qmp-cmd-test.o -tests/device-introspect-test$(EXESUF): tests/device-introspect-test.o -tests/rtc-test$(EXESUF): tests/rtc-test.o -tests/m48t59-test$(EXESUF): tests/m48t59-test.o -tests/hexloader-test$(EXESUF): tests/hexloader-test.o -tests/pflash-cfi02$(EXESUF): tests/pflash-cfi02-test.o -tests/endianness-test$(EXESUF): tests/endianness-test.o -tests/prom-env-test$(EXESUF): tests/prom-env-test.o $(libqos-obj-y) -tests/rtas-test$(EXESUF): tests/rtas-test.o $(libqos-spapr-obj-y) -tests/fdc-test$(EXESUF): tests/fdc-test.o -tests/ide-test$(EXESUF): tests/ide-test.o $(libqos-pc-obj-y) -tests/ahci-test$(EXESUF): tests/ahci-test.o $(libqos-pc-obj-y) qemu-img$(EXESUF) -tests/ipmi-kcs-test$(EXESUF): tests/ipmi-kcs-test.o -tests/ipmi-bt-test$(EXESUF): tests/ipmi-bt-test.o -tests/hd-geo-test$(EXESUF): tests/hd-geo-test.o $(libqos-obj-y) -tests/boot-order-test$(EXESUF): tests/boot-order-test.o $(libqos-obj-y) -tests/boot-serial-test$(EXESUF): tests/boot-serial-test.o $(libqos-obj-y) -tests/bios-tables-test$(EXESUF): tests/bios-tables-test.o \ - tests/boot-sector.o tests/acpi-utils.o $(libqos-obj-y) -tests/pxe-test$(EXESUF): tests/pxe-test.o tests/boot-sector.o $(libqos-obj-y) -tests/microbit-test$(EXESUF): tests/microbit-test.o -tests/m25p80-test$(EXESUF): tests/m25p80-test.o -tests/i440fx-test$(EXESUF): tests/i440fx-test.o $(libqos-pc-obj-y) -tests/q35-test$(EXESUF): tests/q35-test.o $(libqos-pc-obj-y) -tests/fw_cfg-test$(EXESUF): tests/fw_cfg-test.o $(libqos-pc-obj-y) -tests/rtl8139-test$(EXESUF): tests/rtl8139-test.o $(libqos-pc-obj-y) -tests/pnv-xscom-test$(EXESUF): tests/pnv-xscom-test.o -tests/wdt_ib700-test$(EXESUF): tests/wdt_ib700-test.o -tests/tco-test$(EXESUF): tests/tco-test.o $(libqos-pc-obj-y) -tests/virtio-ccw-test$(EXESUF): tests/virtio-ccw-test.o -tests/display-vga-test$(EXESUF): tests/display-vga-test.o -tests/qom-test$(EXESUF): tests/qom-test.o -tests/test-hmp$(EXESUF): tests/test-hmp.o -tests/machine-none-test$(EXESUF): tests/machine-none-test.o -tests/device-plug-test$(EXESUF): tests/device-plug-test.o -tests/drive_del-test$(EXESUF): tests/drive_del-test.o -tests/pvpanic-test$(EXESUF): tests/pvpanic-test.o -tests/i82801b11-test$(EXESUF): tests/i82801b11-test.o -tests/intel-hda-test$(EXESUF): tests/intel-hda-test.o -tests/ioh3420-test$(EXESUF): tests/ioh3420-test.o -tests/usb-hcd-uhci-test$(EXESUF): tests/usb-hcd-uhci-test.o $(libqos-usb-obj-y) -tests/usb-hcd-ehci-test$(EXESUF): tests/usb-hcd-ehci-test.o $(libqos-usb-obj-y) -tests/usb-hcd-xhci-test$(EXESUF): tests/usb-hcd-xhci-test.o $(libqos-usb-obj-y) -tests/cpu-plug-test$(EXESUF): tests/cpu-plug-test.o -tests/migration-test$(EXESUF): tests/migration-test.o -tests/qemu-iotests/socket_scm_helper$(EXESUF): tests/qemu-iotests/socket_scm_helper.o -tests/test-qemu-opts$(EXESUF): tests/test-qemu-opts.o $(test-util-obj-y) -tests/test-keyval$(EXESUF): tests/test-keyval.o $(test-util-obj-y) $(test-qapi-obj-y) -tests/test-write-threshold$(EXESUF): tests/test-write-threshold.o $(test-block-obj-y) -tests/test-netfilter$(EXESUF): tests/test-netfilter.o $(qtest-obj-y) -tests/test-filter-mirror$(EXESUF): tests/test-filter-mirror.o $(qtest-obj-y) -tests/test-filter-redirector$(EXESUF): tests/test-filter-redirector.o $(qtest-obj-y) -tests/test-x86-cpuid-compat$(EXESUF): tests/test-x86-cpuid-compat.o $(qtest-obj-y) -tests/ivshmem-test$(EXESUF): tests/ivshmem-test.o contrib/ivshmem-server/ivshmem-server.o $(libqos-pc-obj-y) $(libqos-spapr-obj-y) -tests/vhost-user-bridge$(EXESUF): tests/vhost-user-bridge.o $(test-util-obj-y) libvhost-user.a -tests/test-uuid$(EXESUF): tests/test-uuid.o $(test-util-obj-y) -tests/test-arm-mptimer$(EXESUF): tests/test-arm-mptimer.o -tests/test-qapi-util$(EXESUF): tests/test-qapi-util.o $(test-util-obj-y) -tests/numa-test$(EXESUF): tests/numa-test.o -tests/vmgenid-test$(EXESUF): tests/vmgenid-test.o tests/boot-sector.o tests/acpi-utils.o -tests/cdrom-test$(EXESUF): tests/cdrom-test.o tests/boot-sector.o $(libqos-obj-y) -tests/arm-cpu-features$(EXESUF): tests/arm-cpu-features.o - tests/migration/stress$(EXESUF): tests/migration/stress.o $(call quiet-command, $(LINKPROG) -static -O3 $(PTHREAD_LIB) -o $@ $< ,"LINK","$(TARGET_DIR)$@") @@ -859,22 +587,12 @@ tests/migration/initrd-stress.img: tests/migration/stress$(EXESUF) rm $(INITRD_WORK_DIR)/init rmdir $(INITRD_WORK_DIR) -# QTest rules - -TARGETS=$(patsubst %-softmmu,%, $(filter %-softmmu,$(TARGET_DIRS))) -ifeq ($(CONFIG_POSIX),y) -QTEST_TARGETS = $(TARGETS) -check-qtest-y=$(foreach TARGET,$(TARGETS), $(check-qtest-$(TARGET)-y)) -check-qtest-y += $(check-qtest-generic-y) -else -QTEST_TARGETS = -endif - -qtest-obj-y = tests/libqtest.o $(test-util-obj-y) -$(check-qtest-y): $(qtest-obj-y) +include $(SRC_PATH)/tests/qtest/Makefile.include tests/test-qga$(EXESUF): qemu-ga$(EXESUF) tests/test-qga$(EXESUF): tests/test-qga.o $(qtest-obj-y) +tests/vhost-user-bridge$(EXESUF): tests/vhost-user-bridge.o $(test-util-obj-y) libvhost-user.a +tests/qemu-iotests/socket_scm_helper$(EXESUF): tests/qemu-iotests/socket_scm_helper.o SPEED = quick @@ -915,7 +633,7 @@ endef .PHONY: $(patsubst %, check-qtest-%, $(QTEST_TARGETS)) $(patsubst %, check-qtest-%, $(QTEST_TARGETS)): check-qtest-%: %-softmmu/all $(check-qtest-y) - $(call do_test_human,$(check-qtest-$*-y) $(check-qtest-generic-y), \ + $(call do_test_human,$(check-qtest-$*-y:%=tests/qtest/%$(EXESUF)) $(check-qtest-generic-y:%=tests/qtest/%$(EXESUF)), \ QTEST_QEMU_BINARY=$*-softmmu/qemu-system-$* \ QTEST_QEMU_IMG=qemu-img$(EXESUF)) @@ -928,7 +646,7 @@ check-speed: $(check-speed-y) # gtester tests with TAP output $(patsubst %, check-report-qtest-%.tap, $(QTEST_TARGETS)): check-report-qtest-%.tap: %-softmmu/all $(check-qtest-y) - $(call do_test_tap, $(check-qtest-$*-y) $(check-qtest-generic-y), \ + $(call do_test_tap, $(check-qtest-$*-y:%=tests/qtest/%$(EXESUF)) $(check-qtest-generic-y:%=tests/qtest/%$(EXESUF)), \ QTEST_QEMU_BINARY=$*-softmmu/qemu-system-$* \ QTEST_QEMU_IMG=qemu-img$(EXESUF)) @@ -951,7 +669,7 @@ FP_TEST_BIN=$(BUILD_DIR)/tests/fp/fp-test # the build dir is created by configure .PHONY: $(FP_TEST_BIN) -$(FP_TEST_BIN): +$(FP_TEST_BIN): config-host.h $(test-util-obj-y) $(call quiet-command, \ $(MAKE) $(SUBDIR_MAKEFLAGS) -C $(dir $@) V="$(V)" $(notdir $@), \ "BUILD", "$(notdir $@)") @@ -1159,7 +877,6 @@ TESTS_RESULTS_DIR=$(BUILD_DIR)/tests/results AVOCADO_SHOW=app AVOCADO_TAGS=$(patsubst %-softmmu,-t arch:%, $(filter %-softmmu,$(TARGET_DIRS))) -ifneq ($(PYTHON2),y) $(TESTS_VENV_DIR): $(TESTS_VENV_REQ) $(call quiet-command, \ $(PYTHON) -m venv --system-site-packages $@, \ @@ -1168,10 +885,6 @@ $(TESTS_VENV_DIR): $(TESTS_VENV_REQ) $(TESTS_VENV_DIR)/bin/python -m pip -q install -r $(TESTS_VENV_REQ), \ PIP, $(TESTS_VENV_REQ)) $(call quiet-command, touch $@) -else -$(TESTS_VENV_DIR): - $(error "venv directory for tests requires Python 3") -endif $(TESTS_RESULTS_DIR): $(call quiet-command, mkdir -p $@, \ @@ -1179,26 +892,43 @@ $(TESTS_RESULTS_DIR): check-venv: $(TESTS_VENV_DIR) -check-acceptance: check-venv $(TESTS_RESULTS_DIR) +FEDORA_31_ARCHES_CANDIDATES=$(patsubst ppc64,ppc64le,$(TARGETS)) +FEDORA_31_ARCHES := x86_64 aarch64 ppc64le s390x +FEDORA_31_DOWNLOAD=$(filter $(FEDORA_31_ARCHES),$(FEDORA_31_ARCHES_CANDIDATES)) + +# download one specific Fedora 31 image +get-vm-image-fedora-31-%: check-venv + $(call quiet-command, \ + $(TESTS_VENV_DIR)/bin/python -m avocado vmimage get \ + --distro=fedora --distro-version=31 --arch=$*, \ + "AVOCADO", "Downloading acceptance tests VM image for $*") + +# download all vm images, according to defined targets +get-vm-images: check-venv $(patsubst %,get-vm-image-fedora-31-%, $(FEDORA_31_DOWNLOAD)) + +check-acceptance: check-venv $(TESTS_RESULTS_DIR) get-vm-images $(call quiet-command, \ $(TESTS_VENV_DIR)/bin/python -m avocado \ --show=$(AVOCADO_SHOW) run --job-results-dir=$(TESTS_RESULTS_DIR) \ --filter-by-tags-include-empty --filter-by-tags-include-empty-key \ $(AVOCADO_TAGS) \ - --failfast=on $(SRC_PATH)/tests/acceptance, \ + --failfast=on tests/acceptance, \ "AVOCADO", "tests/acceptance") # Consolidated targets -.PHONY: check-block check-qapi-schema check-qtest check-unit check check-clean +.PHONY: check-block check-qapi-schema check-qtest check-unit check check-clean get-vm-images check-qapi-schema: check-tests/qapi-schema/frontend check-tests/qapi-schema/doc-good.texi check-qtest: $(patsubst %,check-qtest-%, $(QTEST_TARGETS)) +ifeq ($(CONFIG_TOOLS),y) check-block: $(patsubst %,check-%, $(check-block-y)) +endif check: check-block check-qapi-schema check-unit check-softfloat check-qtest check-decodetree check-clean: - rm -rf $(check-unit-y) tests/*.o $(QEMU_IOTESTS_HELPERS-y) - rm -rf $(sort $(foreach target,$(SYSEMU_TARGET_LIST), $(check-qtest-$(target)-y)) $(check-qtest-generic-y)) + rm -rf $(check-unit-y) tests/*.o tests/*/*.o $(QEMU_IOTESTS_HELPERS-y) + rm -rf $(sort $(foreach target,$(SYSEMU_TARGET_LIST), $(check-qtest-$(target)-y:%=tests/qtest/%$(EXESUF))) $(check-qtest-generic-y:%=tests/qtest/%$(EXESUF))) rm -f tests/test-qapi-gen-timestamp + rm -f tests/qtest/dbus-vmstate1-gen-timestamp rm -rf $(TESTS_VENV_DIR) $(TESTS_RESULTS_DIR) clean: check-clean @@ -1208,6 +938,7 @@ clean: check-clean all: $(QEMU_IOTESTS_HELPERS-y) -include $(wildcard tests/*.d) --include $(wildcard tests/libqos/*.d) +-include $(wildcard tests/qtest/*.d) +-include $(wildcard tests/qtest/libqos/*.d) endif diff --git a/tests/acceptance/avocado_qemu/__init__.py b/tests/acceptance/avocado_qemu/__init__.py index 9a57c020d8..59e7b4f763 100644 --- a/tests/acceptance/avocado_qemu/__init__.py +++ b/tests/acceptance/avocado_qemu/__init__.py @@ -16,8 +16,21 @@ import tempfile import avocado -SRC_ROOT_DIR = os.path.join(os.path.dirname(__file__), '..', '..', '..') -sys.path.append(os.path.join(SRC_ROOT_DIR, 'python')) +#: The QEMU build root directory. It may also be the source directory +#: if building from the source dir, but it's safer to use BUILD_DIR for +#: that purpose. Be aware that if this code is moved outside of a source +#: and build tree, it will not be accurate. +BUILD_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(__file__)))) + +if os.path.islink(os.path.dirname(os.path.dirname(__file__))): + # The link to the acceptance tests dir in the source code directory + lnk = os.path.dirname(os.path.dirname(__file__)) + #: The QEMU root source directory + SOURCE_DIR = os.path.dirname(os.path.dirname(os.readlink(lnk))) +else: + SOURCE_DIR = BUILD_DIR + +sys.path.append(os.path.join(SOURCE_DIR, 'python')) from qemu.machine import QEMUMachine @@ -49,25 +62,22 @@ def pick_default_qemu_bin(arch=None): if is_readable_executable_file(qemu_bin_relative_path): return qemu_bin_relative_path - qemu_bin_from_src_dir_path = os.path.join(SRC_ROOT_DIR, + qemu_bin_from_bld_dir_path = os.path.join(BUILD_DIR, qemu_bin_relative_path) - if is_readable_executable_file(qemu_bin_from_src_dir_path): - return qemu_bin_from_src_dir_path + if is_readable_executable_file(qemu_bin_from_bld_dir_path): + return qemu_bin_from_bld_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 +89,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,25 +141,32 @@ 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): + """ + Gets a tag value, if unique for a key + """ + vals = self.tags.get(tag_name, []) + if len(vals) == 1: + return vals.pop() + return None + def setUp(self): self._vms = {} - arches = self.tags.get('arch', []) - if len(arches) == 1: - arch = arches.pop() - else: - arch = None - self.arch = self.params.get('arch', default=arch) + + self.arch = self.params.get('arch', + default=self._get_unique_tag_val('arch')) + + self.machine = self.params.get('machine', + default=self._get_unique_tag_val('machine')) + default_qemu_bin = pick_default_qemu_bin(arch=self.arch) self.qemu_bin = self.params.get('qemu_bin', default=default_qemu_bin) if self.qemu_bin is None: - self.cancel("No QEMU binary defined or found in the source tree") + self.cancel("No QEMU binary defined or found in the build tree") def _new_vm(self, *args): vm = QEMUMachine(self.qemu_bin, sock_dir=tempfile.mkdtemp()) @@ -129,6 +183,8 @@ class Test(avocado.Test): name = str(uuid.uuid4()) if self._vms.get(name) is None: self._vms[name] = self._new_vm(*args) + if self.machine is not None: + self._vms[name].set_machine(self.machine) return self._vms[name] def tearDown(self): diff --git a/tests/acceptance/boot_linux.py b/tests/acceptance/boot_linux.py new file mode 100644 index 0000000000..075a386300 --- /dev/null +++ b/tests/acceptance/boot_linux.py @@ -0,0 +1,222 @@ +# Functional test that boots a complete Linux system via a cloud image +# +# Copyright (c) 2018-2020 Red Hat, Inc. +# +# Author: +# Cleber Rosa +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. + +import os + +from avocado_qemu import Test, BUILD_DIR + +from qemu.accel import kvm_available +from qemu.accel import tcg_available + +from avocado.utils import cloudinit +from avocado.utils import network +from avocado.utils import vmimage +from avocado.utils import datadrainer +from avocado.utils.path import find_command + +ACCEL_NOT_AVAILABLE_FMT = "%s accelerator does not seem to be available" +KVM_NOT_AVAILABLE = ACCEL_NOT_AVAILABLE_FMT % "KVM" +TCG_NOT_AVAILABLE = ACCEL_NOT_AVAILABLE_FMT % "TCG" + + +class BootLinux(Test): + """ + Boots a Linux system, checking for a successful initialization + """ + + timeout = 900 + chksum = None + + def setUp(self): + super(BootLinux, self).setUp() + self.vm.add_args('-smp', '2') + self.vm.add_args('-m', '1024') + self.prepare_boot() + self.prepare_cloudinit() + + def prepare_boot(self): + self.log.debug('Looking for and selecting a qemu-img binary to be ' + 'used to create the bootable snapshot image') + # If qemu-img has been built, use it, otherwise the system wide one + # will be used. If none is available, the test will cancel. + qemu_img = os.path.join(BUILD_DIR, 'qemu-img') + if not os.path.exists(qemu_img): + qemu_img = find_command('qemu-img', False) + if qemu_img is False: + self.cancel('Could not find "qemu-img", which is required to ' + 'create the bootable image') + vmimage.QEMU_IMG = qemu_img + + self.log.info('Downloading/preparing boot image') + # Fedora 31 only provides ppc64le images + image_arch = self.arch + if image_arch == 'ppc64': + image_arch = 'ppc64le' + try: + self.boot = vmimage.get( + 'fedora', arch=image_arch, version='31', + checksum=self.chksum, + algorithm='sha256', + cache_dir=self.cache_dirs[0], + snapshot_dir=self.workdir) + self.vm.add_args('-drive', 'file=%s' % self.boot.path) + except: + self.cancel('Failed to download/prepare boot image') + + def prepare_cloudinit(self): + self.log.info('Preparing cloudinit image') + try: + cloudinit_iso = os.path.join(self.workdir, 'cloudinit.iso') + self.phone_home_port = network.find_free_port() + cloudinit.iso(cloudinit_iso, self.name, + username='root', + password='password', + # QEMU's hard coded usermode router address + phone_home_host='10.0.2.2', + phone_home_port=self.phone_home_port) + self.vm.add_args('-drive', 'file=%s,format=raw' % cloudinit_iso) + except Exception: + self.cancel('Failed to prepared cloudinit image') + + def launch_and_wait(self): + self.vm.set_console() + self.vm.launch() + console_drainer = datadrainer.LineLogger(self.vm.console_socket.fileno(), + logger=self.log.getChild('console')) + console_drainer.start() + self.log.info('VM launched, waiting for boot confirmation from guest') + cloudinit.wait_for_phone_home(('0.0.0.0', self.phone_home_port), self.name) + + +class BootLinuxX8664(BootLinux): + """ + :avocado: tags=arch:x86_64 + """ + + chksum = 'e3c1b309d9203604922d6e255c2c5d098a309c2d46215d8fc026954f3c5c27a0' + + def test_pc_i440fx_tcg(self): + """ + :avocado: tags=machine:pc + :avocado: tags=accel:tcg + """ + if not tcg_available(self.qemu_bin): + self.cancel(TCG_NOT_AVAILABLE) + self.vm.add_args("-accel", "tcg") + self.launch_and_wait() + + def test_pc_i440fx_kvm(self): + """ + :avocado: tags=machine:pc + :avocado: tags=accel:kvm + """ + if not kvm_available(self.arch, self.qemu_bin): + self.cancel(KVM_NOT_AVAILABLE) + self.vm.add_args("-accel", "kvm") + self.launch_and_wait() + + def test_pc_q35_tcg(self): + """ + :avocado: tags=machine:q35 + :avocado: tags=accel:tcg + """ + if not tcg_available(self.qemu_bin): + self.cancel(TCG_NOT_AVAILABLE) + self.vm.add_args("-accel", "tcg") + self.launch_and_wait() + + def test_pc_q35_kvm(self): + """ + :avocado: tags=machine:q35 + :avocado: tags=accel:kvm + """ + if not kvm_available(self.arch, self.qemu_bin): + self.cancel(KVM_NOT_AVAILABLE) + self.vm.add_args("-accel", "kvm") + self.launch_and_wait() + + +class BootLinuxAarch64(BootLinux): + """ + :avocado: tags=arch:aarch64 + :avocado: tags=machine:virt + :avocado: tags=machine:gic-version=2 + """ + + chksum = '1e18d9c0cf734940c4b5d5ec592facaed2af0ad0329383d5639c997fdf16fe49' + + def add_common_args(self): + self.vm.add_args('-bios', + os.path.join(BUILD_DIR, 'pc-bios', + 'edk2-aarch64-code.fd')) + self.vm.add_args('-device', 'virtio-rng-pci,rng=rng0') + self.vm.add_args('-object', 'rng-random,id=rng0,filename=/dev/urandom') + + def test_virt_tcg(self): + """ + :avocado: tags=accel:tcg + :avocado: tags=cpu:max + """ + if not tcg_available(self.qemu_bin): + self.cancel(TCG_NOT_AVAILABLE) + self.vm.add_args("-accel", "tcg") + self.vm.add_args("-cpu", "max") + self.vm.add_args("-machine", "virt,gic-version=2") + self.add_common_args() + self.launch_and_wait() + + def test_virt_kvm(self): + """ + :avocado: tags=accel:kvm + :avocado: tags=cpu:host + """ + if not kvm_available(self.arch, self.qemu_bin): + self.cancel(KVM_NOT_AVAILABLE) + self.vm.add_args("-accel", "kvm") + self.vm.add_args("-cpu", "host") + self.vm.add_args("-machine", "virt,gic-version=2") + self.add_common_args() + self.launch_and_wait() + + +class BootLinuxPPC64(BootLinux): + """ + :avocado: tags=arch:ppc64 + """ + + chksum = '7c3528b85a3df4b2306e892199a9e1e43f991c506f2cc390dc4efa2026ad2f58' + + def test_pseries_tcg(self): + """ + :avocado: tags=machine:pseries + :avocado: tags=accel:tcg + """ + if not tcg_available(self.qemu_bin): + self.cancel(TCG_NOT_AVAILABLE) + self.vm.add_args("-accel", "tcg") + self.launch_and_wait() + + +class BootLinuxS390X(BootLinux): + """ + :avocado: tags=arch:s390x + """ + + chksum = '4caaab5a434fd4d1079149a072fdc7891e354f834d355069ca982fdcaf5a122d' + + def test_s390_ccw_virtio_tcg(self): + """ + :avocado: tags=machine:s390-ccw-virtio + :avocado: tags=accel:tcg + """ + if not tcg_available(self.qemu_bin): + self.cancel(TCG_NOT_AVAILABLE) + self.vm.add_args("-accel", "tcg") + self.launch_and_wait() diff --git a/tests/acceptance/boot_linux_console.py b/tests/acceptance/boot_linux_console.py index 7e41cebd47..f825cd9ef5 100644 --- a/tests/acceptance/boot_linux_console.py +++ b/tests/acceptance/boot_linux_console.py @@ -16,10 +16,17 @@ import shutil from avocado import skipUnless from avocado_qemu import Test from avocado_qemu import exec_command_and_wait_for_pattern +from avocado_qemu import interrupt_interactive_console_until_pattern from avocado_qemu import wait_for_console_pattern from avocado.utils import process from avocado.utils import archive +from avocado.utils.path import find_command, CmdNotFoundError +P7ZIP_AVAILABLE = True +try: + find_command('7z') +except CmdNotFoundError: + P7ZIP_AVAILABLE = False class BootLinuxConsole(Test): """ @@ -40,7 +47,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 +56,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): """ @@ -62,7 +90,6 @@ class BootLinuxConsole(Test): kernel_hash = '23bebd2680757891cf7adedb033532163a792495' kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - self.vm.set_machine('pc') self.vm.set_console() kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyS0' self.vm.add_args('-kernel', kernel_path, @@ -85,7 +112,6 @@ class BootLinuxConsole(Test): kernel_path = self.extract_from_deb(deb_path, '/boot/vmlinux-2.6.32-5-4kc-malta') - self.vm.set_machine('malta') self.vm.set_console() kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyS0' self.vm.add_args('-kernel', kernel_path, @@ -118,7 +144,6 @@ class BootLinuxConsole(Test): kernel_path = self.extract_from_deb(deb_path, '/boot/vmlinux-2.6.32-5-5kc-malta') - self.vm.set_machine('malta') self.vm.set_console() kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyS0' self.vm.add_args('-kernel', kernel_path, @@ -148,7 +173,6 @@ class BootLinuxConsole(Test): initrd_path = self.workdir + "rootfs.cpio" archive.gzip_uncompress(initrd_path_gz, initrd_path) - self.vm.set_machine('malta') self.vm.set_console() kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyS0 console=tty ' @@ -188,7 +212,6 @@ class BootLinuxConsole(Test): initrd_path = self.workdir + "rootfs.cpio" archive.gzip_uncompress(initrd_path_gz, initrd_path) - self.vm.set_machine('malta') self.vm.set_console() kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyS0 console=tty ' @@ -215,7 +238,6 @@ class BootLinuxConsole(Test): with open(kernel_path, 'wb') as f_out: shutil.copyfileobj(f_in, f_out) - self.vm.set_machine('malta') self.vm.set_console() kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + 'mem=256m@@0x0 ' @@ -275,7 +297,6 @@ class BootLinuxConsole(Test): kernel_hash = '8c73e469fc6ea06a58dc83a628fc695b693b8493' kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - self.vm.set_machine('virt') self.vm.set_console() kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyAMA0') @@ -297,7 +318,6 @@ class BootLinuxConsole(Test): kernel_hash = 'e9826d741b4fb04cadba8d4824d1ed3b7fb8b4d4' kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - self.vm.set_machine('virt') self.vm.set_console() kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyAMA0') @@ -310,8 +330,9 @@ class BootLinuxConsole(Test): def test_arm_emcraft_sf2(self): """ :avocado: tags=arch:arm - :avocado: tags=machine:emcraft_sf2 + :avocado: tags=machine:emcraft-sf2 :avocado: tags=endian:little + :avocado: tags=u-boot """ uboot_url = ('https://raw.githubusercontent.com/' 'Subbaraya-Sundeep/qemu-test-binaries/' @@ -324,7 +345,6 @@ class BootLinuxConsole(Test): spi_hash = '85f698329d38de63aea6e884a86fbde70890a78a' spi_path = self.fetch_asset(spi_url, asset_hash=spi_hash) - self.vm.set_machine('emcraft-sf2') self.vm.set_console() kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE self.vm.add_args('-kernel', uboot_path, @@ -351,7 +371,6 @@ class BootLinuxConsole(Test): kernel_path = self.extract_from_deb(deb_path, '/boot/kernel7.img') dtb_path = self.extract_from_deb(deb_path, '/boot/bcm2709-rpi-2-b.dtb') - self.vm.set_machine('raspi2') self.vm.set_console() kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + serial_kernel_cmdline[uart_id]) @@ -393,7 +412,6 @@ class BootLinuxConsole(Test): initrd_path = os.path.join(self.workdir, 'rootfs.cpio') archive.gzip_uncompress(initrd_path_gz, initrd_path) - self.vm.set_machine('smdkc210') self.vm.set_console() kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + 'earlycon=exynos4210,0x13800000 earlyprintk ' + @@ -411,10 +429,318 @@ class BootLinuxConsole(Test): self.wait_for_console_pattern('Boot successful.') # TODO user command, for now the uart is stuck + def test_arm_cubieboard_initrd(self): + """ + :avocado: tags=arch:arm + :avocado: tags=machine:cubieboard + """ + deb_url = ('https://apt.armbian.com/pool/main/l/' + 'linux-4.20.7-sunxi/linux-image-dev-sunxi_5.75_armhf.deb') + deb_hash = '1334c29c44d984ffa05ed10de8c3361f33d78315' + deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) + kernel_path = self.extract_from_deb(deb_path, + '/boot/vmlinuz-4.20.7-sunxi') + dtb_path = '/usr/lib/linux-image-dev-sunxi/sun4i-a10-cubieboard.dtb' + dtb_path = self.extract_from_deb(deb_path, dtb_path) + initrd_url = ('https://github.com/groeck/linux-build-test/raw/' + '2eb0a73b5d5a28df3170c546ddaaa9757e1e0848/rootfs/' + 'arm/rootfs-armv5.cpio.gz') + initrd_hash = '2b50f1873e113523967806f4da2afe385462ff9b' + initrd_path_gz = self.fetch_asset(initrd_url, asset_hash=initrd_hash) + initrd_path = os.path.join(self.workdir, 'rootfs.cpio') + archive.gzip_uncompress(initrd_path_gz, initrd_path) + + self.vm.set_console() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyS0,115200 ' + 'usbcore.nousb ' + 'panic=-1 noreboot') + self.vm.add_args('-kernel', kernel_path, + '-dtb', dtb_path, + '-initrd', initrd_path, + '-append', kernel_command_line, + '-no-reboot') + self.vm.launch() + self.wait_for_console_pattern('Boot successful.') + + exec_command_and_wait_for_pattern(self, 'cat /proc/cpuinfo', + 'Allwinner sun4i/sun5i') + exec_command_and_wait_for_pattern(self, 'cat /proc/iomem', + 'system-control@1c00000') + exec_command_and_wait_for_pattern(self, 'reboot', + 'reboot: Restarting system') + + def test_arm_cubieboard_sata(self): + """ + :avocado: tags=arch:arm + :avocado: tags=machine:cubieboard + """ + deb_url = ('https://apt.armbian.com/pool/main/l/' + 'linux-4.20.7-sunxi/linux-image-dev-sunxi_5.75_armhf.deb') + deb_hash = '1334c29c44d984ffa05ed10de8c3361f33d78315' + deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) + kernel_path = self.extract_from_deb(deb_path, + '/boot/vmlinuz-4.20.7-sunxi') + dtb_path = '/usr/lib/linux-image-dev-sunxi/sun4i-a10-cubieboard.dtb' + dtb_path = self.extract_from_deb(deb_path, dtb_path) + rootfs_url = ('https://github.com/groeck/linux-build-test/raw/' + '2eb0a73b5d5a28df3170c546ddaaa9757e1e0848/rootfs/' + 'arm/rootfs-armv5.ext2.gz') + rootfs_hash = '093e89d2b4d982234bf528bc9fb2f2f17a9d1f93' + rootfs_path_gz = self.fetch_asset(rootfs_url, asset_hash=rootfs_hash) + rootfs_path = os.path.join(self.workdir, 'rootfs.cpio') + archive.gzip_uncompress(rootfs_path_gz, rootfs_path) + + self.vm.set_console() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyS0,115200 ' + 'usbcore.nousb ' + 'root=/dev/sda ro ' + 'panic=-1 noreboot') + self.vm.add_args('-kernel', kernel_path, + '-dtb', dtb_path, + '-drive', 'if=none,format=raw,id=disk0,file=' + + rootfs_path, + '-device', 'ide-hd,bus=ide.0,drive=disk0', + '-append', kernel_command_line, + '-no-reboot') + self.vm.launch() + self.wait_for_console_pattern('Boot successful.') + + exec_command_and_wait_for_pattern(self, 'cat /proc/cpuinfo', + 'Allwinner sun4i/sun5i') + exec_command_and_wait_for_pattern(self, 'cat /proc/partitions', + 'sda') + exec_command_and_wait_for_pattern(self, 'reboot', + 'reboot: Restarting system') + + def test_arm_orangepi(self): + """ + :avocado: tags=arch:arm + :avocado: tags=machine:orangepi-pc + """ + deb_url = ('https://apt.armbian.com/pool/main/l/' + 'linux-4.20.7-sunxi/linux-image-dev-sunxi_5.75_armhf.deb') + deb_hash = '1334c29c44d984ffa05ed10de8c3361f33d78315' + deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) + kernel_path = self.extract_from_deb(deb_path, + '/boot/vmlinuz-4.20.7-sunxi') + dtb_path = '/usr/lib/linux-image-dev-sunxi/sun8i-h3-orangepi-pc.dtb' + dtb_path = self.extract_from_deb(deb_path, dtb_path) + + self.vm.set_console() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyS0,115200n8 ' + 'earlycon=uart,mmio32,0x1c28000') + self.vm.add_args('-kernel', kernel_path, + '-dtb', dtb_path, + '-append', kernel_command_line) + self.vm.launch() + console_pattern = 'Kernel command line: %s' % kernel_command_line + self.wait_for_console_pattern(console_pattern) + + def test_arm_orangepi_initrd(self): + """ + :avocado: tags=arch:arm + :avocado: tags=machine:orangepi-pc + """ + deb_url = ('https://apt.armbian.com/pool/main/l/' + 'linux-4.20.7-sunxi/linux-image-dev-sunxi_5.75_armhf.deb') + deb_hash = '1334c29c44d984ffa05ed10de8c3361f33d78315' + deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) + kernel_path = self.extract_from_deb(deb_path, + '/boot/vmlinuz-4.20.7-sunxi') + dtb_path = '/usr/lib/linux-image-dev-sunxi/sun8i-h3-orangepi-pc.dtb' + dtb_path = self.extract_from_deb(deb_path, dtb_path) + initrd_url = ('https://github.com/groeck/linux-build-test/raw/' + '2eb0a73b5d5a28df3170c546ddaaa9757e1e0848/rootfs/' + 'arm/rootfs-armv7a.cpio.gz') + initrd_hash = '604b2e45cdf35045846b8bbfbf2129b1891bdc9c' + initrd_path_gz = self.fetch_asset(initrd_url, asset_hash=initrd_hash) + initrd_path = os.path.join(self.workdir, 'rootfs.cpio') + archive.gzip_uncompress(initrd_path_gz, initrd_path) + + self.vm.set_console() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyS0,115200 ' + 'panic=-1 noreboot') + self.vm.add_args('-kernel', kernel_path, + '-dtb', dtb_path, + '-initrd', initrd_path, + '-append', kernel_command_line, + '-no-reboot') + self.vm.launch() + self.wait_for_console_pattern('Boot successful.') + + exec_command_and_wait_for_pattern(self, 'cat /proc/cpuinfo', + 'Allwinner sun8i Family') + exec_command_and_wait_for_pattern(self, 'cat /proc/iomem', + 'system-control@1c00000') + exec_command_and_wait_for_pattern(self, 'reboot', + 'reboot: Restarting system') + + def test_arm_orangepi_sd(self): + """ + :avocado: tags=arch:arm + :avocado: tags=machine:orangepi-pc + """ + deb_url = ('https://apt.armbian.com/pool/main/l/' + 'linux-4.20.7-sunxi/linux-image-dev-sunxi_5.75_armhf.deb') + deb_hash = '1334c29c44d984ffa05ed10de8c3361f33d78315' + deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) + kernel_path = self.extract_from_deb(deb_path, + '/boot/vmlinuz-4.20.7-sunxi') + dtb_path = '/usr/lib/linux-image-dev-sunxi/sun8i-h3-orangepi-pc.dtb' + dtb_path = self.extract_from_deb(deb_path, dtb_path) + rootfs_url = ('http://storage.kernelci.org/images/rootfs/buildroot/' + 'kci-2019.02/armel/base/rootfs.ext2.xz') + rootfs_hash = '692510cb625efda31640d1de0a8d60e26040f061' + rootfs_path_xz = self.fetch_asset(rootfs_url, asset_hash=rootfs_hash) + rootfs_path = os.path.join(self.workdir, 'rootfs.cpio') + archive.lzma_uncompress(rootfs_path_xz, rootfs_path) + + self.vm.set_console() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyS0,115200 ' + 'root=/dev/mmcblk0 rootwait rw ' + 'panic=-1 noreboot') + self.vm.add_args('-kernel', kernel_path, + '-dtb', dtb_path, + '-drive', 'file=' + rootfs_path + ',if=sd,format=raw', + '-append', kernel_command_line, + '-no-reboot') + self.vm.launch() + shell_ready = "/bin/sh: can't access tty; job control turned off" + self.wait_for_console_pattern(shell_ready) + + exec_command_and_wait_for_pattern(self, 'cat /proc/cpuinfo', + 'Allwinner sun8i Family') + exec_command_and_wait_for_pattern(self, 'cat /proc/partitions', + 'mmcblk0') + exec_command_and_wait_for_pattern(self, 'ifconfig eth0 up', + 'eth0: Link is Up') + exec_command_and_wait_for_pattern(self, 'udhcpc eth0', + 'udhcpc: lease of 10.0.2.15 obtained') + exec_command_and_wait_for_pattern(self, 'ping -c 3 10.0.2.2', + '3 packets transmitted, 3 packets received, 0% packet loss') + exec_command_and_wait_for_pattern(self, 'reboot', + 'reboot: Restarting system') + + @skipUnless(os.getenv('AVOCADO_ALLOW_LARGE_STORAGE'), 'storage limited') + @skipUnless(P7ZIP_AVAILABLE, '7z not installed') + def test_arm_orangepi_bionic(self): + """ + :avocado: tags=arch:arm + :avocado: tags=machine:orangepi-pc + """ + + # This test download a 196MB compressed image and expand it to 932MB... + image_url = ('https://dl.armbian.com/orangepipc/archive/' + 'Armbian_19.11.3_Orangepipc_bionic_current_5.3.9.7z') + image_hash = '196a8ffb72b0123d92cea4a070894813d305c71e' + image_path_7z = self.fetch_asset(image_url, asset_hash=image_hash) + image_name = 'Armbian_19.11.3_Orangepipc_bionic_current_5.3.9.img' + image_path = os.path.join(self.workdir, image_name) + process.run("7z e -o%s %s" % (self.workdir, image_path_7z)) + + self.vm.set_console() + self.vm.add_args('-drive', 'file=' + image_path + ',if=sd,format=raw', + '-nic', 'user', + '-no-reboot') + self.vm.launch() + + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyS0,115200 ' + 'loglevel=7 ' + 'nosmp ' + 'systemd.default_timeout_start_sec=9000 ' + 'systemd.mask=armbian-zram-config.service ' + 'systemd.mask=armbian-ramlog.service') + + self.wait_for_console_pattern('U-Boot SPL') + self.wait_for_console_pattern('Autoboot in ') + exec_command_and_wait_for_pattern(self, ' ', '=>') + exec_command_and_wait_for_pattern(self, "setenv extraargs '" + + kernel_command_line + "'", '=>') + exec_command_and_wait_for_pattern(self, 'boot', 'Starting kernel ...'); + + self.wait_for_console_pattern('systemd[1]: Set hostname ' + + 'to ') + self.wait_for_console_pattern('Starting Load Kernel Modules...') + + @skipUnless(os.getenv('AVOCADO_ALLOW_LARGE_STORAGE'), 'storage limited') + def test_arm_orangepi_uboot_netbsd9(self): + """ + :avocado: tags=arch:arm + :avocado: tags=machine:orangepi-pc + """ + # This test download a 304MB compressed image and expand it to 1.3GB... + deb_url = ('http://snapshot.debian.org/archive/debian/' + '20200108T145233Z/pool/main/u/u-boot/' + 'u-boot-sunxi_2020.01%2Bdfsg-1_armhf.deb') + deb_hash = 'f67f404a80753ca3d1258f13e38f2b060e13db99' + deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) + # We use the common OrangePi PC 'plus' build of U-Boot for our secondary + # program loader (SPL). We will then set the path to the more specific + # OrangePi "PC" device tree blob with 'setenv fdtfile' in U-Boot prompt, + # before to boot NetBSD. + uboot_path = '/usr/lib/u-boot/orangepi_plus/u-boot-sunxi-with-spl.bin' + uboot_path = self.extract_from_deb(deb_path, uboot_path) + image_url = ('https://cdn.netbsd.org/pub/NetBSD/NetBSD-9.0/' + 'evbarm-earmv7hf/binary/gzimg/armv7.img.gz') + image_hash = '2babb29d36d8360adcb39c09e31060945259917a' + image_path_gz = self.fetch_asset(image_url, asset_hash=image_hash) + image_path = os.path.join(self.workdir, 'armv7.img') + image_drive_args = 'if=sd,format=raw,snapshot=on,file=' + image_path + archive.gzip_uncompress(image_path_gz, image_path) + + # dd if=u-boot-sunxi-with-spl.bin of=armv7.img bs=1K seek=8 conv=notrunc + with open(uboot_path, 'rb') as f_in: + with open(image_path, 'r+b') as f_out: + f_out.seek(8 * 1024) + shutil.copyfileobj(f_in, f_out) + + # Extend image, to avoid that NetBSD thinks the partition + # inside the image is larger than device size itself + f_out.seek(0, 2) + f_out.seek(64 * 1024 * 1024, 1) + f_out.write(bytearray([0x00])) + + self.vm.set_console() + self.vm.add_args('-nic', 'user', + '-drive', image_drive_args, + '-global', 'allwinner-rtc.base-year=2000', + '-no-reboot') + self.vm.launch() + wait_for_console_pattern(self, 'U-Boot 2020.01+dfsg-1') + interrupt_interactive_console_until_pattern(self, + 'Hit any key to stop autoboot:', + 'switch to partitions #0, OK') + + exec_command_and_wait_for_pattern(self, '', '=>') + cmd = 'setenv bootargs root=ld0a' + exec_command_and_wait_for_pattern(self, cmd, '=>') + cmd = 'setenv kernel netbsd-GENERIC.ub' + exec_command_and_wait_for_pattern(self, cmd, '=>') + cmd = 'setenv fdtfile dtb/sun8i-h3-orangepi-pc.dtb' + exec_command_and_wait_for_pattern(self, cmd, '=>') + cmd = ("setenv bootcmd 'fatload mmc 0:1 ${kernel_addr_r} ${kernel}; " + "fatload mmc 0:1 ${fdt_addr_r} ${fdtfile}; " + "fdt addr ${fdt_addr_r}; " + "bootm ${kernel_addr_r} - ${fdt_addr_r}'") + exec_command_and_wait_for_pattern(self, cmd, '=>') + + exec_command_and_wait_for_pattern(self, 'boot', + 'Booting kernel from Legacy Image') + wait_for_console_pattern(self, 'Starting kernel ...') + wait_for_console_pattern(self, 'NetBSD 9.0 (GENERIC)') + # Wait for user-space + wait_for_console_pattern(self, 'Starting root file system check') + def test_s390x_s390_ccw_virtio(self): """ :avocado: tags=arch:s390x - :avocado: tags=machine:s390_ccw_virtio + :avocado: tags=machine:s390-ccw-virtio """ kernel_url = ('https://archives.fedoraproject.org/pub/archive' '/fedora-secondary/releases/29/Everything/s390x/os/images' @@ -422,7 +748,6 @@ class BootLinuxConsole(Test): kernel_hash = 'e8e8439103ef8053418ef062644ffd46a7919313' kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - self.vm.set_machine('s390-ccw-virtio') self.vm.set_console() kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=sclp0' self.vm.add_args('-nodefaults', @@ -444,10 +769,9 @@ class BootLinuxConsole(Test): uncompressed_kernel = archive.uncompress(kernel_path, self.workdir) - self.vm.set_machine('clipper') 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() @@ -465,7 +789,6 @@ class BootLinuxConsole(Test): kernel_hash = '3fe04abfc852b66653b8c3c897a59a689270bc77' kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - self.vm.set_machine('pseries') self.vm.set_console() kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=hvc0' self.vm.add_args('-kernel', kernel_path, @@ -479,17 +802,14 @@ class BootLinuxConsole(Test): :avocado: tags=arch:m68k :avocado: tags=machine:q800 """ - deb_url = ('http://ftp.ports.debian.org/debian-ports/pool-m68k/main' + deb_url = ('https://snapshot.debian.org/archive/debian-ports' + '/20191021T083923Z/pool-m68k/main' '/l/linux/kernel-image-5.3.0-1-m68k-di_5.3.7-1_m68k.udeb') deb_hash = '044954bb9be4160a3ce81f8bc1b5e856b75cccd1' - try: - deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) - except OSError as exp: - self.cancel(exp) + deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) kernel_path = self.extract_from_deb(deb_path, '/boot/vmlinux-5.3.0-1-m68k') - self.vm.set_machine('q800') self.vm.set_console() kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyS0 vga=off') @@ -500,3 +820,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/cpu_queries.py b/tests/acceptance/cpu_queries.py index af47d2795a..293dccb89a 100644 --- a/tests/acceptance/cpu_queries.py +++ b/tests/acceptance/cpu_queries.py @@ -20,8 +20,8 @@ class QueryCPUModelExpansion(Test): def test(self): """ :avocado: tags=arch:x86_64 + :avocado: tags=machine:none """ - self.vm.set_machine('none') self.vm.add_args('-S') self.vm.launch() diff --git a/tests/acceptance/empty_cpu_model.py b/tests/acceptance/empty_cpu_model.py index 3f4f663582..a1e59e45e4 100644 --- a/tests/acceptance/empty_cpu_model.py +++ b/tests/acceptance/empty_cpu_model.py @@ -7,13 +7,13 @@ # # This work is licensed under the terms of the GNU GPL, version 2 or # later. See the COPYING file in the top-level directory. -import subprocess from avocado_qemu import Test class EmptyCPUModel(Test): def test(self): - cmd = [self.qemu_bin, '-S', '-display', 'none', '-machine', 'none', '-cpu', ''] - r = subprocess.run(cmd, stderr=subprocess.PIPE, stdout=subprocess.PIPE) - self.assertEquals(r.returncode, 1, "QEMU exit code should be 1") - self.assertEquals(r.stdout, b'', "QEMU stdout should be empty") - self.assertNotEquals(r.stderr, b'', "QEMU stderr shouldn't be empty") + self.vm.add_args('-S', '-display', 'none', '-machine', 'none', '-cpu', '') + self.vm.set_qmp_monitor(enabled=False) + self.vm.launch() + self.vm.wait() + self.assertEquals(self.vm.exitcode(), 1, "QEMU exit code should be 1") + self.assertRegex(self.vm.get_log(), r'-cpu option cannot be empty') diff --git a/tests/acceptance/linux_initrd.py b/tests/acceptance/linux_initrd.py index c61d9826a4..a3e54d3fc9 100644 --- a/tests/acceptance/linux_initrd.py +++ b/tests/acceptance/linux_initrd.py @@ -10,7 +10,6 @@ import logging import tempfile -from avocado.utils.process import run from avocado_qemu import Test @@ -20,6 +19,7 @@ class LinuxInitrd(Test): Checks QEMU evaluates correctly the initrd file passed as -initrd option. :avocado: tags=arch:x86_64 + :avocado: tags=machine:pc """ timeout = 300 @@ -41,13 +41,15 @@ class LinuxInitrd(Test): initrd.seek(max_size) initrd.write(b'\0') initrd.flush() - cmd = "%s -kernel %s -initrd %s -m 4096" % ( - self.qemu_bin, kernel_path, initrd.name) - res = run(cmd, ignore_status=True) - self.assertEqual(res.exit_status, 1) + self.vm.add_args('-kernel', kernel_path, '-initrd', initrd.name, + '-m', '4096') + self.vm.set_qmp_monitor(enabled=False) + self.vm.launch() + self.vm.wait() + self.assertEqual(self.vm.exitcode(), 1) expected_msg = r'.*initrd is too large.*max: \d+, need %s.*' % ( max_size + 1) - self.assertRegex(res.stderr_text, expected_msg) + self.assertRegex(self.vm.get_log(), expected_msg) def test_with_2gib_file_should_work_with_linux_v4_16(self): """ @@ -66,7 +68,6 @@ class LinuxInitrd(Test): initrd.write(b'\0') initrd.flush() - self.vm.set_machine('pc') self.vm.set_console() kernel_command_line = 'console=ttyS0' self.vm.add_args('-kernel', kernel_path, diff --git a/tests/acceptance/linux_ssh_mips_malta.py b/tests/acceptance/linux_ssh_mips_malta.py index fc13f9e4d4..90d7f2f167 100644 --- a/tests/acceptance/linux_ssh_mips_malta.py +++ b/tests/acceptance/linux_ssh_mips_malta.py @@ -99,10 +99,12 @@ class LinuxSSH(Test): def ssh_command(self, command, is_root=True): self.ssh_logger.info(command) result = self.ssh_session.cmd(command) - stdout_lines = [line.rstrip() for line in result.stdout_text.splitlines()] + stdout_lines = [line.rstrip() for line + in result.stdout_text.splitlines()] for line in stdout_lines: self.ssh_logger.info(line) - stderr_lines = [line.rstrip() for line in result.stderr_text.splitlines()] + stderr_lines = [line.rstrip() for line + in result.stderr_text.splitlines()] for line in stderr_lines: self.ssh_logger.warning(line) return stdout_lines, stderr_lines @@ -111,7 +113,6 @@ class LinuxSSH(Test): image_url, image_hash = self.get_image_info(endianess) image_path = self.fetch_asset(image_url, asset_hash=image_hash) - self.vm.set_machine('malta') self.vm.set_console() kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyS0 root=/dev/sda1') @@ -215,7 +216,6 @@ class LinuxSSH(Test): def test_mips_malta32eb_kernel3_2_0(self): """ :avocado: tags=arch:mips - :avocado: tags=machine:malta :avocado: tags=endian:big :avocado: tags=device:pcnet32 """ @@ -224,7 +224,6 @@ class LinuxSSH(Test): def test_mips_malta32el_kernel3_2_0(self): """ :avocado: tags=arch:mipsel - :avocado: tags=machine:malta :avocado: tags=endian:little :avocado: tags=device:pcnet32 """ @@ -233,7 +232,6 @@ class LinuxSSH(Test): def test_mips_malta64eb_kernel3_2_0(self): """ :avocado: tags=arch:mips64 - :avocado: tags=machine:malta :avocado: tags=endian:big :avocado: tags=device:pcnet32 """ @@ -242,7 +240,6 @@ class LinuxSSH(Test): def test_mips_malta64el_kernel3_2_0(self): """ :avocado: tags=arch:mips64el - :avocado: tags=machine:malta :avocado: tags=endian:little :avocado: tags=device:pcnet32 """ diff --git a/tests/acceptance/machine_arm_integratorcp.py b/tests/acceptance/machine_arm_integratorcp.py new file mode 100644 index 0000000000..49c8ebff78 --- /dev/null +++ b/tests/acceptance/machine_arm_integratorcp.py @@ -0,0 +1,99 @@ +# Functional test that boots a Linux kernel and checks the console +# +# Copyright (c) 2020 Red Hat, Inc. +# +# Author: +# Thomas Huth +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. + +import os +import logging + +from avocado import skipUnless +from avocado_qemu import Test +from avocado_qemu import wait_for_console_pattern + + +NUMPY_AVAILABLE = True +try: + import numpy as np +except ImportError: + NUMPY_AVAILABLE = False + +CV2_AVAILABLE = True +try: + import cv2 +except ImportError: + CV2_AVAILABLE = False + + +class IntegratorMachine(Test): + + timeout = 90 + + def boot_integratorcp(self): + kernel_url = ('https://github.com/zayac/qemu-arm/raw/master/' + 'arm-test/kernel/zImage.integrator') + kernel_hash = '0d7adba893c503267c946a3cbdc63b4b54f25468' + kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) + + initrd_url = ('https://github.com/zayac/qemu-arm/raw/master/' + 'arm-test/kernel/arm_root.img') + initrd_hash = 'b51e4154285bf784e017a37586428332d8c7bd8b' + initrd_path = self.fetch_asset(initrd_url, asset_hash=initrd_hash) + + self.vm.set_console() + self.vm.add_args('-kernel', kernel_path, + '-initrd', initrd_path, + '-append', 'printk.time=0 console=ttyAMA0') + self.vm.launch() + + @skipUnless(os.getenv('AVOCADO_ALLOW_UNTRUSTED_CODE'), 'untrusted code') + def test_integratorcp_console(self): + """ + Boots the Linux kernel and checks that the console is operational + :avocado: tags=arch:arm + :avocado: tags=machine:integratorcp + :avocado: tags=device:pl011 + """ + self.boot_integratorcp() + wait_for_console_pattern(self, 'Log in as root') + + @skipUnless(NUMPY_AVAILABLE, 'Python NumPy not installed') + @skipUnless(CV2_AVAILABLE, 'Python OpenCV not installed') + @skipUnless(os.getenv('AVOCADO_ALLOW_UNTRUSTED_CODE'), 'untrusted code') + def test_framebuffer_tux_logo(self): + """ + Boot Linux and verify the Tux logo is displayed on the framebuffer. + :avocado: tags=arch:arm + :avocado: tags=machine:integratorcp + :avocado: tags=device:pl110 + :avocado: tags=device:framebuffer + """ + screendump_path = os.path.join(self.workdir, "screendump.pbm") + tuxlogo_url = ('https://github.com/torvalds/linux/raw/v2.6.12/' + 'drivers/video/logo/logo_linux_vga16.ppm') + tuxlogo_hash = '3991c2ddbd1ddaecda7601f8aafbcf5b02dc86af' + tuxlogo_path = self.fetch_asset(tuxlogo_url, asset_hash=tuxlogo_hash) + + self.boot_integratorcp() + framebuffer_ready = 'Console: switching to colour frame buffer device' + wait_for_console_pattern(self, framebuffer_ready) + self.vm.command('human-monitor-command', command_line='stop') + self.vm.command('human-monitor-command', + command_line='screendump %s' % screendump_path) + logger = logging.getLogger('framebuffer') + + cpu_count = 1 + match_threshold = 0.92 + screendump_bgr = cv2.imread(screendump_path) + screendump_gray = cv2.cvtColor(screendump_bgr, cv2.COLOR_BGR2GRAY) + result = cv2.matchTemplate(screendump_gray, cv2.imread(tuxlogo_path, 0), + cv2.TM_CCOEFF_NORMED) + loc = np.where(result >= match_threshold) + tux_count = 0 + for tux_count, pt in enumerate(zip(*loc[::-1]), start=1): + logger.debug('found Tux at position [x, y] = %s', pt) + self.assertGreaterEqual(tux_count, cpu_count) diff --git a/tests/acceptance/machine_arm_n8x0.py b/tests/acceptance/machine_arm_n8x0.py new file mode 100644 index 0000000000..e5741f2d8d --- /dev/null +++ b/tests/acceptance/machine_arm_n8x0.py @@ -0,0 +1,49 @@ +# Functional test that boots a Linux kernel and checks the console +# +# Copyright (c) 2020 Red Hat, Inc. +# +# Author: +# Thomas Huth +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. + +import os + +from avocado import skipUnless +from avocado_qemu import Test +from avocado_qemu import wait_for_console_pattern + +class N8x0Machine(Test): + """Boots the Linux kernel and checks that the console is operational""" + + timeout = 90 + + def __do_test_n8x0(self): + kernel_url = ('http://stskeeps.subnetmask.net/meego-n8x0/' + 'meego-arm-n8x0-1.0.80.20100712.1431-' + 'vmlinuz-2.6.35~rc4-129.1-n8x0') + kernel_hash = 'e9d5ab8d7548923a0061b6fbf601465e479ed269' + kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) + + self.vm.set_console(console_index=1) + self.vm.add_args('-kernel', kernel_path, + '-append', 'printk.time=0 console=ttyS1') + self.vm.launch() + wait_for_console_pattern(self, 'TSC2005 driver initializing') + + @skipUnless(os.getenv('AVOCADO_ALLOW_UNTRUSTED_CODE'), 'untrusted code') + def test_n800(self): + """ + :avocado: tags=arch:arm + :avocado: tags=machine:n800 + """ + self.__do_test_n8x0() + + @skipUnless(os.getenv('AVOCADO_ALLOW_UNTRUSTED_CODE'), 'untrusted code') + def test_n810(self): + """ + :avocado: tags=arch:arm + :avocado: tags=machine:n810 + """ + self.__do_test_n8x0() diff --git a/tests/acceptance/machine_m68k_nextcube.py b/tests/acceptance/machine_m68k_nextcube.py index fcd2c58ee7..32cf571f94 100644 --- a/tests/acceptance/machine_m68k_nextcube.py +++ b/tests/acceptance/machine_m68k_nextcube.py @@ -43,6 +43,11 @@ def tesseract_available(expected_version): class NextCubeMachine(Test): + """ + :avocado: tags=arch:m68k + :avocado: tags=machine:next-cube + :avocado: tags=device:framebuffer + """ timeout = 15 @@ -52,7 +57,6 @@ class NextCubeMachine(Test): rom_hash = 'b3534796abae238a0111299fc406a9349f7fee24' rom_path = self.fetch_asset(rom_url, asset_hash=rom_hash) - self.vm.set_machine('next-cube') self.vm.add_args('-bios', rom_path) self.vm.launch() @@ -66,11 +70,6 @@ class NextCubeMachine(Test): @skipUnless(PIL_AVAILABLE, 'Python PIL not installed') def test_bootrom_framebuffer_size(self): - """ - :avocado: tags=arch:m68k - :avocado: tags=machine:next_cube - :avocado: tags=device:framebuffer - """ screenshot_path = os.path.join(self.workdir, "dump.png") self.check_bootrom_framebuffer(screenshot_path) @@ -80,11 +79,6 @@ class NextCubeMachine(Test): @skipUnless(tesseract_available(3), 'tesseract v3 OCR tool not available') def test_bootrom_framebuffer_ocr_with_tesseract_v3(self): - """ - :avocado: tags=arch:m68k - :avocado: tags=machine:next_cube - :avocado: tags=device:framebuffer - """ screenshot_path = os.path.join(self.workdir, "dump.png") self.check_bootrom_framebuffer(screenshot_path) @@ -101,11 +95,6 @@ class NextCubeMachine(Test): # that it is still alpha-level software. @skipUnless(tesseract_available(4), 'tesseract v4 OCR tool not available') def test_bootrom_framebuffer_ocr_with_tesseract_v4(self): - """ - :avocado: tags=arch:m68k - :avocado: tags=machine:next_cube - :avocado: tags=device:framebuffer - """ screenshot_path = os.path.join(self.workdir, "dump.png") self.check_bootrom_framebuffer(screenshot_path) diff --git a/tests/acceptance/machine_mips_malta.py b/tests/acceptance/machine_mips_malta.py new file mode 100644 index 0000000000..92b4f28a11 --- /dev/null +++ b/tests/acceptance/machine_mips_malta.py @@ -0,0 +1,118 @@ +# Functional tests for the MIPS Malta board +# +# Copyright (c) Philippe Mathieu-Daudé +# +# This work is licensed under the terms of the GNU GPL, version 2 or later. +# See the COPYING file in the top-level directory. +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import os +import gzip +import logging + +from avocado import skipUnless +from avocado_qemu import Test +from avocado_qemu import wait_for_console_pattern +from avocado.utils import archive + + +NUMPY_AVAILABLE = True +try: + import numpy as np +except ImportError: + NUMPY_AVAILABLE = False + +CV2_AVAILABLE = True +try: + import cv2 +except ImportError: + CV2_AVAILABLE = False + + +@skipUnless(NUMPY_AVAILABLE, 'Python NumPy not installed') +@skipUnless(CV2_AVAILABLE, 'Python OpenCV not installed') +class MaltaMachineFramebuffer(Test): + + timeout = 30 + + KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 ' + + def do_test_i6400_framebuffer_logo(self, cpu_cores_count): + """ + Boot Linux kernel and check Tux logo is displayed on the framebuffer. + """ + screendump_path = os.path.join(self.workdir, 'screendump.pbm') + + kernel_url = ('https://github.com/philmd/qemu-testing-blob/raw/' + 'a5966ca4b5/mips/malta/mips64el/' + 'vmlinux-4.7.0-rc1.I6400.gz') + kernel_hash = '096f50c377ec5072e6a366943324622c312045f6' + kernel_path_gz = self.fetch_asset(kernel_url, asset_hash=kernel_hash) + kernel_path = self.workdir + "vmlinux" + archive.gzip_uncompress(kernel_path_gz, kernel_path) + + tuxlogo_url = ('https://github.com/torvalds/linux/raw/v2.6.12/' + 'drivers/video/logo/logo_linux_vga16.ppm') + tuxlogo_hash = '3991c2ddbd1ddaecda7601f8aafbcf5b02dc86af' + tuxlogo_path = self.fetch_asset(tuxlogo_url, asset_hash=tuxlogo_hash) + + self.vm.set_console() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'clocksource=GIC console=tty0 console=ttyS0') + self.vm.add_args('-kernel', kernel_path, + '-cpu', 'I6400', + '-smp', '%u' % cpu_cores_count, + '-vga', 'std', + '-append', kernel_command_line) + self.vm.launch() + framebuffer_ready = 'Console: switching to colour frame buffer device' + wait_for_console_pattern(self, framebuffer_ready, + failure_message='Kernel panic - not syncing') + self.vm.command('human-monitor-command', command_line='stop') + self.vm.command('human-monitor-command', + command_line='screendump %s' % screendump_path) + logger = logging.getLogger('framebuffer') + + match_threshold = 0.95 + screendump_bgr = cv2.imread(screendump_path, cv2.IMREAD_COLOR) + tuxlogo_bgr = cv2.imread(tuxlogo_path, cv2.IMREAD_COLOR) + result = cv2.matchTemplate(screendump_bgr, tuxlogo_bgr, + cv2.TM_CCOEFF_NORMED) + loc = np.where(result >= match_threshold) + tuxlogo_count = 0 + h, w = tuxlogo_bgr.shape[:2] + debug_png = os.getenv('AVOCADO_CV2_SCREENDUMP_PNG_PATH') + for tuxlogo_count, pt in enumerate(zip(*loc[::-1]), start=1): + logger.debug('found Tux at position (x, y) = %s', pt) + cv2.rectangle(screendump_bgr, pt, + (pt[0] + w, pt[1] + h), (0, 0, 255), 2) + if debug_png: + cv2.imwrite(debug_png, screendump_bgr) + self.assertGreaterEqual(tuxlogo_count, cpu_cores_count) + + def test_mips_malta_i6400_framebuffer_logo_1core(self): + """ + :avocado: tags=arch:mips64el + :avocado: tags=machine:malta + :avocado: tags=cpu:i6400 + """ + self.do_test_i6400_framebuffer_logo(1) + + def test_mips_malta_i6400_framebuffer_logo_7cores(self): + """ + :avocado: tags=arch:mips64el + :avocado: tags=machine:malta + :avocado: tags=cpu:i6400 + :avocado: tags=mips:smp + """ + self.do_test_i6400_framebuffer_logo(7) + + def test_mips_malta_i6400_framebuffer_logo_8cores(self): + """ + :avocado: tags=arch:mips64el + :avocado: tags=machine:malta + :avocado: tags=cpu:i6400 + :avocado: tags=mips:smp + """ + self.do_test_i6400_framebuffer_logo(8) diff --git a/tests/acceptance/machine_sparc_leon3.py b/tests/acceptance/machine_sparc_leon3.py index 298f1e25e6..2405cd7a0d 100644 --- a/tests/acceptance/machine_sparc_leon3.py +++ b/tests/acceptance/machine_sparc_leon3.py @@ -7,16 +7,20 @@ from avocado_qemu import Test from avocado_qemu import wait_for_console_pattern +from avocado import skip class Leon3Machine(Test): timeout = 60 + @skip("Test currently broken") + # A Window Underflow exception occurs before booting the kernel, + # and QEMU exit calling cpu_abort(), which makes this test to fail. def test_leon3_helenos_uimage(self): """ :avocado: tags=arch:sparc - :avocado: tags=machine:leon3 + :avocado: tags=machine:leon3_generic :avocado: tags=binfmt:uimage """ kernel_url = ('http://www.helenos.org/releases/' @@ -24,7 +28,6 @@ class Leon3Machine(Test): kernel_hash = 'a88c9cfdb8430c66650e5290a08765f9bf049a30' kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - self.vm.set_machine('leon3_generic') self.vm.set_console() self.vm.add_args('-kernel', kernel_path) diff --git a/tests/acceptance/migration.py b/tests/acceptance/migration.py index a44c1ae58f..0365289cda 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 + src_uri = 'exec:nc localhost %u' % free_port + self.do_migrate(dest_uri, src_uri) diff --git a/tests/acceptance/ppc_prep_40p.py b/tests/acceptance/ppc_prep_40p.py index 6f507fb0a6..1515561249 100644 --- a/tests/acceptance/ppc_prep_40p.py +++ b/tests/acceptance/ppc_prep_40p.py @@ -30,16 +30,16 @@ class IbmPrep40pMachine(Test): :avocado: tags=machine:40p :avocado: tags=slowness:high """ - bios_url = ('ftp://ftp.boulder.ibm.com/rs6000/firmware/' + bios_url = ('http://ftpmirror.your.org/pub/misc/' + 'ftp.software.ibm.com/rs6000/firmware/' '7020-40p/P12H0456.IMG') bios_hash = '1775face4e6dc27f3a6ed955ef6eb331bf817f03' bios_path = self.fetch_asset(bios_url, asset_hash=bios_hash) - drive_url = ('https://ftp.netbsd.org/pub/NetBSD/NetBSD-archive/' + drive_url = ('https://cdn.netbsd.org/pub/NetBSD/NetBSD-archive/' 'NetBSD-4.0/prep/installation/floppy/generic_com0.fs') drive_hash = 'dbcfc09912e71bd5f0d82c7c1ee43082fb596ceb' drive_path = self.fetch_asset(drive_url, asset_hash=drive_hash) - self.vm.set_machine('40p') self.vm.set_console() self.vm.add_args('-bios', bios_path, '-fda', drive_path) @@ -53,7 +53,6 @@ class IbmPrep40pMachine(Test): :avocado: tags=arch:ppc :avocado: tags=machine:40p """ - self.vm.set_machine('40p') self.vm.set_console() self.vm.add_args('-m', '192') # test fw_cfg @@ -68,12 +67,11 @@ class IbmPrep40pMachine(Test): :avocado: tags=arch:ppc :avocado: tags=machine:40p """ - drive_url = ('https://ftp.netbsd.org/pub/NetBSD/iso/7.1.2/' + drive_url = ('https://cdn.netbsd.org/pub/NetBSD/iso/7.1.2/' 'NetBSD-7.1.2-prep.iso') drive_hash = 'ac6fa2707d888b36d6fa64de6e7fe48e' drive_path = self.fetch_asset(drive_url, asset_hash=drive_hash, algorithm='md5') - self.vm.set_machine('40p') self.vm.set_console() self.vm.add_args('-cdrom', drive_path, '-boot', 'd') 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_check_params.py b/tests/acceptance/virtio_check_params.py new file mode 100644 index 0000000000..87e6c839d1 --- /dev/null +++ b/tests/acceptance/virtio_check_params.py @@ -0,0 +1,144 @@ +# +# Test virtio-scsi and virtio-blk queue settings for all machine types +# +# 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 . +# + +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'} +VIRTIO_BLK_PROPS = {'seg_max_adjust': 'seg-max-adjust'} + +DEV_TYPES = {'virtio-scsi-pci': VIRTIO_SCSI_PROPS, + 'virtio-blk-pci': VIRTIO_BLK_PROPS} + +VM_DEV_PARAMS = {'virtio-scsi-pci': ['-device', 'virtio-scsi-pci,id=scsi0'], + 'virtio-blk-pci': ['-device', + 'virtio-blk-pci,id=scsi0,drive=drive0', + '-drive', + 'driver=null-co,id=drive0,if=none']} + + +class VirtioMaxSegSettingsCheck(Test): + @staticmethod + def make_pattern(props): + pattern_items = ['{0} = \w+'.format(prop) for prop in props] + return '|'.join(pattern_items) + + def query_virtqueue(self, vm, dev_type_name): + query_ok = False + error = None + props = None + + output = vm.command('human-monitor-command', + command_line = 'info qtree') + props_list = DEV_TYPES[dev_type_name].values(); + pattern = self.make_pattern(props_list) + res = re.findall(pattern, output) + + if len(res) != len(props_list): + props_list = set(props_list) + res = set(res) + not_found = props_list.difference(res) + not_found = ', '.join(not_found) + error = '({0}): The following properties not found: {1}'\ + .format(dev_type_name, not_found) + else: + query_ok = True + props = dict() + for prop in res: + p = prop.split(' = ') + props[p[0]] = p[1] + 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) + 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)) + + for prop_name, prop_val in props.items(): + expected_val = mt[prop_name] + self.assertEqual(expected_val, prop_val) + + @staticmethod + def seg_max_adjust_enabled(mt): + # machine types >= 5.0 should have seg_max_adjust = true + # others seg_max_adjust = false + mt = mt.split("-") + + # machine types with one line name and name like pc-x.x + if len(mt) <= 2: + return False + + # machine types like pc--x.x[.x] + ver = mt[2] + ver = ver.split("."); + + # versions >= 5.0 goes with seg_max_adjust enabled + major = int(ver[0]) + + if major >= 5: + 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: + vm.launch() + machines = [m['name'] for m in vm.command('query-machines')] + vm.shutdown() + machines.remove('none') + machines.remove('isapc') + machines.remove('microvm') + + for dev_type in DEV_TYPES: + # create the list of machine types and their parameters. + mtypes = list() + for m in machines: + if self.seg_max_adjust_enabled(m): + enabled = 'true' + else: + enabled = 'false' + mtypes.append({'name': m, + DEV_TYPES[dev_type]['seg_max_adjust']: enabled}) + + # test each machine type for a device type + for mt in mtypes: + self.check_mt(mt, dev_type) diff --git a/tests/acceptance/x86_cpu_model_versions.py b/tests/acceptance/x86_cpu_model_versions.py index 5fc9ca4bc6..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 # @@ -25,10 +24,6 @@ import avocado_qemu import re -def get_cpu_prop(vm, prop): - cpu_path = vm.command('query-cpus')[0].get('qom_path') - return vm.command('qom-get', path=cpu_path, property=prop) - class X86CPUModelAliases(avocado_qemu.Test): """ Validation of PC CPU model versions and CPU model aliases @@ -79,12 +74,15 @@ class X86CPUModelAliases(avocado_qemu.Test): "EPYC-IBPB shouldn't be versioned") def test_4_0_alias_compatibility(self): - """Check if pc-*-4.0 unversioned CPU model won't be reported as aliases""" + """ + Check if pc-*-4.0 unversioned CPU model won't be reported as aliases + + :avocado: tags=machine:pc-i440fx-4.0 + """ # pc-*-4.0 won't expose non-versioned CPU models as aliases # We do this to help management software to keep compatibility # with older QEMU versions that didn't have the versioned CPU model self.vm.add_args('-S') - self.vm.set_machine('pc-i440fx-4.0') self.vm.launch() cpus = dict((m['name'], m) for m in self.vm.command('query-cpu-definitions')) @@ -109,9 +107,12 @@ class X86CPUModelAliases(avocado_qemu.Test): self.assertNotIn('alias-of', c, "%s shouldn't be an alias" % (name)) def test_4_1_alias(self): - """Check if unversioned CPU model is an alias pointing to right version""" + """ + Check if unversioned CPU model is an alias pointing to right version + + :avocado: tags=machine:pc-i440fx-4.1 + """ self.vm.add_args('-S') - self.vm.set_machine('pc-i440fx-4.1') self.vm.launch() cpus = dict((m['name'], m) for m in self.vm.command('query-cpu-definitions')) @@ -211,9 +212,12 @@ class X86CPUModelAliases(avocado_qemu.Test): self.validate_aliases(cpus) def test_none_alias(self): - """Check if unversioned CPU model is an alias pointing to some version""" + """ + Check if unversioned CPU model is an alias pointing to some version + + :avocado: tags=machine:none + """ self.vm.add_args('-S') - self.vm.set_machine('none') self.vm.launch() cpus = dict((m['name'], m) for m in self.vm.command('query-cpu-definitions')) @@ -241,78 +245,90 @@ class CascadelakeArchCapabilities(avocado_qemu.Test): :avocado: tags=arch:x86_64 """ + def get_cpu_prop(self, prop): + cpu_path = self.vm.command('query-cpus')[0].get('qom_path') + return self.vm.command('qom-get', path=cpu_path, property=prop) + def test_4_1(self): + """ + :avocado: tags=machine:pc-i440fx-4.1 + """ # machine-type only: - vm = self.get_vm() - vm.add_args('-S') - vm.set_machine('pc-i440fx-4.1') - vm.add_args('-cpu', 'Cascadelake-Server,x-force-features=on,check=off,enforce=off') - vm.launch() - self.assertFalse(get_cpu_prop(vm, 'arch-capabilities'), + self.vm.add_args('-S') + self.vm.add_args('-cpu', 'Cascadelake-Server,x-force-features=on,check=off,enforce=off') + self.vm.launch() + self.assertFalse(self.get_cpu_prop('arch-capabilities'), 'pc-i440fx-4.1 + Cascadelake-Server should not have arch-capabilities') def test_4_0(self): - vm = self.get_vm() - vm.add_args('-S') - vm.set_machine('pc-i440fx-4.0') - vm.add_args('-cpu', 'Cascadelake-Server,x-force-features=on,check=off,enforce=off') - vm.launch() - self.assertFalse(get_cpu_prop(vm, 'arch-capabilities'), + """ + :avocado: tags=machine:pc-i440fx-4.0 + """ + self.vm.add_args('-S') + self.vm.add_args('-cpu', 'Cascadelake-Server,x-force-features=on,check=off,enforce=off') + self.vm.launch() + self.assertFalse(self.get_cpu_prop('arch-capabilities'), 'pc-i440fx-4.0 + Cascadelake-Server should not have arch-capabilities') def test_set_4_0(self): + """ + :avocado: tags=machine:pc-i440fx-4.0 + """ # command line must override machine-type if CPU model is not versioned: - vm = self.get_vm() - vm.add_args('-S') - vm.set_machine('pc-i440fx-4.0') - vm.add_args('-cpu', 'Cascadelake-Server,x-force-features=on,check=off,enforce=off,+arch-capabilities') - vm.launch() - self.assertTrue(get_cpu_prop(vm, 'arch-capabilities'), + self.vm.add_args('-S') + self.vm.add_args('-cpu', 'Cascadelake-Server,x-force-features=on,check=off,enforce=off,+arch-capabilities') + self.vm.launch() + self.assertTrue(self.get_cpu_prop('arch-capabilities'), 'pc-i440fx-4.0 + Cascadelake-Server,+arch-capabilities should have arch-capabilities') def test_unset_4_1(self): - vm = self.get_vm() - vm.add_args('-S') - vm.set_machine('pc-i440fx-4.1') - vm.add_args('-cpu', 'Cascadelake-Server,x-force-features=on,check=off,enforce=off,-arch-capabilities') - vm.launch() - self.assertFalse(get_cpu_prop(vm, 'arch-capabilities'), + """ + :avocado: tags=machine:pc-i440fx-4.1 + """ + self.vm.add_args('-S') + self.vm.add_args('-cpu', 'Cascadelake-Server,x-force-features=on,check=off,enforce=off,-arch-capabilities') + self.vm.launch() + self.assertFalse(self.get_cpu_prop('arch-capabilities'), 'pc-i440fx-4.1 + Cascadelake-Server,-arch-capabilities should not have arch-capabilities') def test_v1_4_0(self): + """ + :avocado: tags=machine:pc-i440fx-4.0 + """ # versioned CPU model overrides machine-type: - vm = self.get_vm() - vm.add_args('-S') - vm.set_machine('pc-i440fx-4.0') - vm.add_args('-cpu', 'Cascadelake-Server-v1,x-force-features=on,check=off,enforce=off') - vm.launch() - self.assertFalse(get_cpu_prop(vm, 'arch-capabilities'), + self.vm.add_args('-S') + self.vm.add_args('-cpu', 'Cascadelake-Server-v1,x-force-features=on,check=off,enforce=off') + self.vm.launch() + self.assertFalse(self.get_cpu_prop('arch-capabilities'), 'pc-i440fx-4.0 + Cascadelake-Server-v1 should not have arch-capabilities') def test_v2_4_0(self): - vm = self.get_vm() - vm.add_args('-S') - vm.set_machine('pc-i440fx-4.0') - vm.add_args('-cpu', 'Cascadelake-Server-v2,x-force-features=on,check=off,enforce=off') - vm.launch() - self.assertTrue(get_cpu_prop(vm, 'arch-capabilities'), - 'pc-i440fx-4.0 + Cascadelake-Server-v2 should have arch-capabilities') + """ + :avocado: tags=machine:pc-i440fx-4.0 + """ + self.vm.add_args('-S') + self.vm.add_args('-cpu', 'Cascadelake-Server-v2,x-force-features=on,check=off,enforce=off') + self.vm.launch() + self.assertTrue(self.get_cpu_prop('arch-capabilities'), + 'pc-i440fx-4.0 + Cascadelake-Server-v2 should have arch-capabilities') def test_v1_set_4_0(self): + """ + :avocado: tags=machine:pc-i440fx-4.0 + """ # command line must override machine-type and versioned CPU model: - vm = self.get_vm() - vm.add_args('-S') - vm.set_machine('pc-i440fx-4.0') - vm.add_args('-cpu', 'Cascadelake-Server-v1,x-force-features=on,check=off,enforce=off,+arch-capabilities') - vm.launch() - self.assertTrue(get_cpu_prop(vm, 'arch-capabilities'), - 'pc-i440fx-4.0 + Cascadelake-Server-v1,+arch-capabilities should have arch-capabilities') + self.vm.add_args('-S') + self.vm.add_args('-cpu', 'Cascadelake-Server-v1,x-force-features=on,check=off,enforce=off,+arch-capabilities') + self.vm.launch() + self.assertTrue(self.get_cpu_prop('arch-capabilities'), + 'pc-i440fx-4.0 + Cascadelake-Server-v1,+arch-capabilities should have arch-capabilities') def test_v2_unset_4_1(self): - vm = self.get_vm() - vm.add_args('-S') - vm.set_machine('pc-i440fx-4.1') - vm.add_args('-cpu', 'Cascadelake-Server-v2,x-force-features=on,check=off,enforce=off,-arch-capabilities') - vm.launch() - self.assertFalse(get_cpu_prop(vm, 'arch-capabilities'), + """ + :avocado: tags=machine:pc-i440fx-4.1 + """ + self.vm.add_args('-S') + self.vm.add_args('-cpu', 'Cascadelake-Server-v2,x-force-features=on,check=off,enforce=off,-arch-capabilities') + self.vm.launch() + self.assertFalse(self.get_cpu_prop('arch-capabilities'), 'pc-i440fx-4.1 + Cascadelake-Server-v2,-arch-capabilities should not have arch-capabilities') diff --git a/tests/check-block.sh b/tests/check-block.sh index 679aedec50..ad320c21ba 100755 --- a/tests/check-block.sh +++ b/tests/check-block.sh @@ -16,7 +16,7 @@ if [ "$#" -ne 0 ]; then format_list="$@" fi -if grep -q "TARGET_GPROF=y" *-softmmu/config-target.mak 2>/dev/null ; then +if grep -q "CONFIG_GPROF=y" config-host.mak 2>/dev/null ; then echo "GPROF is enabled ==> Not running the qemu-iotests." exit 0 fi diff --git a/tests/data/acpi/pc/APIC.acpihmat b/tests/data/acpi/pc/APIC.acpihmat new file mode 100644 index 0000000000..a21f164699 Binary files /dev/null and b/tests/data/acpi/pc/APIC.acpihmat differ diff --git a/tests/data/acpi/pc/DSDT.acpihmat b/tests/data/acpi/pc/DSDT.acpihmat new file mode 100644 index 0000000000..ad890e09aa Binary files /dev/null and b/tests/data/acpi/pc/DSDT.acpihmat differ diff --git a/tests/data/acpi/pc/HMAT.acpihmat b/tests/data/acpi/pc/HMAT.acpihmat new file mode 100644 index 0000000000..c00f7ba6cd Binary files /dev/null and b/tests/data/acpi/pc/HMAT.acpihmat differ diff --git a/tests/data/acpi/pc/SRAT.acpihmat b/tests/data/acpi/pc/SRAT.acpihmat new file mode 100644 index 0000000000..1dcae90aec Binary files /dev/null and b/tests/data/acpi/pc/SRAT.acpihmat differ diff --git a/tests/data/acpi/q35/APIC.acpihmat b/tests/data/acpi/q35/APIC.acpihmat new file mode 100644 index 0000000000..a21f164699 Binary files /dev/null and b/tests/data/acpi/q35/APIC.acpihmat differ diff --git a/tests/data/acpi/q35/DSDT b/tests/data/acpi/q35/DSDT index 77ea60ffed..1f91888d7a 100644 Binary files a/tests/data/acpi/q35/DSDT and b/tests/data/acpi/q35/DSDT differ diff --git a/tests/data/acpi/q35/DSDT.acpihmat b/tests/data/acpi/q35/DSDT.acpihmat new file mode 100644 index 0000000000..3586f6368a Binary files /dev/null and b/tests/data/acpi/q35/DSDT.acpihmat differ diff --git a/tests/data/acpi/q35/DSDT.bridge b/tests/data/acpi/q35/DSDT.bridge index fbc2d40000..eae3a2a865 100644 Binary files a/tests/data/acpi/q35/DSDT.bridge and b/tests/data/acpi/q35/DSDT.bridge differ diff --git a/tests/data/acpi/q35/DSDT.cphp b/tests/data/acpi/q35/DSDT.cphp index 6a896cb214..53d735a4de 100644 Binary files a/tests/data/acpi/q35/DSDT.cphp and b/tests/data/acpi/q35/DSDT.cphp differ diff --git a/tests/data/acpi/q35/DSDT.dimmpxm b/tests/data/acpi/q35/DSDT.dimmpxm index 23fdf5e60a..02ccdd5f38 100644 Binary files a/tests/data/acpi/q35/DSDT.dimmpxm and b/tests/data/acpi/q35/DSDT.dimmpxm differ diff --git a/tests/data/acpi/q35/DSDT.ipmibt b/tests/data/acpi/q35/DSDT.ipmibt index c3fca0a71e..9e2d4f785c 100644 Binary files a/tests/data/acpi/q35/DSDT.ipmibt and b/tests/data/acpi/q35/DSDT.ipmibt differ diff --git a/tests/data/acpi/q35/DSDT.memhp b/tests/data/acpi/q35/DSDT.memhp index 2abd0e36cd..baefa611ac 100644 Binary files a/tests/data/acpi/q35/DSDT.memhp and b/tests/data/acpi/q35/DSDT.memhp differ diff --git a/tests/data/acpi/q35/DSDT.mmio64 b/tests/data/acpi/q35/DSDT.mmio64 index b32034a11c..aae0ea2110 100644 Binary files a/tests/data/acpi/q35/DSDT.mmio64 and b/tests/data/acpi/q35/DSDT.mmio64 differ diff --git a/tests/data/acpi/q35/DSDT.numamem b/tests/data/acpi/q35/DSDT.numamem index d8b2b47f8b..859a2e0871 100644 Binary files a/tests/data/acpi/q35/DSDT.numamem and b/tests/data/acpi/q35/DSDT.numamem differ diff --git a/tests/data/acpi/q35/HMAT.acpihmat b/tests/data/acpi/q35/HMAT.acpihmat new file mode 100644 index 0000000000..c00f7ba6cd Binary files /dev/null and b/tests/data/acpi/q35/HMAT.acpihmat differ diff --git a/tests/data/acpi/q35/SRAT.acpihmat b/tests/data/acpi/q35/SRAT.acpihmat new file mode 100644 index 0000000000..1dcae90aec Binary files /dev/null and b/tests/data/acpi/q35/SRAT.acpihmat differ diff --git a/tests/data/acpi/rebuild-expected-aml.sh b/tests/data/acpi/rebuild-expected-aml.sh index f89d4624bc..9cbaab1a4d 100755 --- a/tests/data/acpi/rebuild-expected-aml.sh +++ b/tests/data/acpi/rebuild-expected-aml.sh @@ -14,7 +14,7 @@ qemu_bins="x86_64-softmmu/qemu-system-x86_64 aarch64-softmmu/qemu-system-aarch64" -if [ ! -e "tests/bios-tables-test" ]; then +if [ ! -e "tests/qtest/bios-tables-test" ]; then echo "Test: bios-tables-test is required! Run make check before this script." echo "Run this script from the build directory." exit 1; @@ -26,11 +26,18 @@ for qemu in $qemu_bins; do echo "Also, run this script from the build directory." exit 1; fi - TEST_ACPI_REBUILD_AML=y QTEST_QEMU_BINARY=$qemu tests/bios-tables-test + TEST_ACPI_REBUILD_AML=y QTEST_QEMU_BINARY=$qemu tests/qtest/bios-tables-test done eval `grep SRC_PATH= config-host.mak` -echo '/* List of comma-separated changed AML files to ignore */' > ${SRC_PATH}/tests/bios-tables-test-allowed-diff.h +old_allowed_dif=`grep -v -e 'List of comma-separated changed AML files to ignore' ${SRC_PATH}/tests/qtest/bios-tables-test-allowed-diff.h` + +echo '/* List of comma-separated changed AML files to ignore */' > ${SRC_PATH}/tests/qtest/bios-tables-test-allowed-diff.h echo "The files were rebuilt and can be added to git." + +if [ -z "$old_allowed_dif" ]; then + echo "Note! Please do not commit expected files with source changes" + echo "Note! Please follow the process documented in ${SRC_PATH}/tests/qtest/bios-tables-test.c" +fi diff --git a/tests/data/acpi/virt/DSDT b/tests/data/acpi/virt/DSDT index bce76e3d23..d6f5c61788 100644 Binary files a/tests/data/acpi/virt/DSDT and b/tests/data/acpi/virt/DSDT differ diff --git a/tests/data/acpi/virt/DSDT.memhp b/tests/data/acpi/virt/DSDT.memhp index b4b153fcdc..c527ac4739 100644 Binary files a/tests/data/acpi/virt/DSDT.memhp and b/tests/data/acpi/virt/DSDT.memhp differ diff --git a/tests/data/acpi/virt/DSDT.numamem b/tests/data/acpi/virt/DSDT.numamem index bce76e3d23..d6f5c61788 100644 Binary files a/tests/data/acpi/virt/DSDT.numamem and b/tests/data/acpi/virt/DSDT.numamem differ diff --git a/tests/dbus-vmstate-daemon.sh b/tests/dbus-vmstate-daemon.sh new file mode 100755 index 0000000000..474e250154 --- /dev/null +++ b/tests/dbus-vmstate-daemon.sh @@ -0,0 +1,95 @@ +#!/bin/sh + +# dbus-daemon wrapper script for dbus-vmstate testing +# +# This script allows to tweak the dbus-daemon policy during the test +# to test different configurations. +# +# 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 . +# +# Copyright (C) 2019 Red Hat, Inc. + +write_config() +{ + CONF="$1" + cat > "$CONF" < + session + unix:tmpdir=$DBUS_VMSTATE_TEST_TMPDIR + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + contexts/dbus_contexts + + +EOF +} + +ARGS= +for arg in "$@" +do + case $arg in + --config-file=*) + CONF="${arg#*=}" + write_config "$CONF" + ARGS="$ARGS $1" + shift + ;; + *) + ARGS="$ARGS $1" + shift + ;; + esac +done + +exec dbus-daemon $ARGS 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/common.rc b/tests/docker/common.rc index 512202b0a1..02cd67a8c5 100755 --- a/tests/docker/common.rc +++ b/tests/docker/common.rc @@ -53,12 +53,7 @@ check_qemu() INVOCATION="$@" fi - if command -v gtester > /dev/null 2>&1 && \ - gtester --version > /dev/null 2>&1; then - make $MAKEFLAGS $INVOCATION - else - echo "No working gtester, skipping make $INVOCATION" - fi + make $MAKEFLAGS $INVOCATION } test_fail() 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/centos7.docker b/tests/docker/dockerfiles/centos7.docker index 953637065c..9a2a2e515d 100644 --- a/tests/docker/dockerfiles/centos7.docker +++ b/tests/docker/dockerfiles/centos7.docker @@ -2,12 +2,15 @@ FROM centos:7 RUN yum install -y epel-release centos-release-xen-48 RUN yum -y update + +# Please keep this list sorted alphabetically ENV PACKAGES \ bison \ bzip2 \ bzip2-devel \ ccache \ csnappy-devel \ + dbus-daemon \ flex \ gcc-c++ \ gcc \ @@ -18,6 +21,7 @@ ENV PACKAGES \ libepoxy-devel \ libfdt-devel \ librdmacm-devel \ + libzstd-devel \ lzo-devel \ make \ mesa-libEGL-devel \ diff --git a/tests/docker/dockerfiles/centos8.docker b/tests/docker/dockerfiles/centos8.docker new file mode 100644 index 0000000000..bfa0d33c9c --- /dev/null +++ b/tests/docker/dockerfiles/centos8.docker @@ -0,0 +1,32 @@ +FROM centos:8.1.1911 + +RUN dnf -y update +ENV PACKAGES \ + SDL-devel \ + bison \ + bzip2 \ + bzip2-devel \ + dbus-daemon \ + flex \ + gcc \ + gcc-c++ \ + gettext \ + git \ + glib2-devel \ + libaio-devel \ + libepoxy-devel \ + lzo-devel \ + make \ + mesa-libEGL-devel \ + nettle-devel \ + perl-Test-Harness \ + pixman-devel \ + python36 \ + rdma-core-devel \ + spice-glib-devel \ + spice-server \ + tar \ + zlib-devel + +RUN dnf install -y $PACKAGES +RUN rpm -q $PACKAGES | sort > /packages.txt diff --git a/tests/docker/dockerfiles/debian-amd64.docker b/tests/docker/dockerfiles/debian-amd64.docker index 431e947ebd..957f0bc2e7 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é RUN apt update && \ @@ -16,7 +16,9 @@ RUN apt update && \ apt install -y --no-install-recommends \ libbz2-dev \ liblzo2-dev \ + libgcrypt20-dev \ librdmacm-dev \ + libsasl2-dev \ libsnappy-dev \ libvte-dev @@ -27,9 +29,9 @@ RUN apt update && \ libegl1-mesa-dev \ libepoxy-dev \ libgbm-dev -RUN git clone https://anongit.freedesktop.org/git/virglrenderer.git /usr/src/virglrenderer && \ - cd /usr/src/virglrenderer && git checkout virglrenderer-0.7.0 -RUN cd /usr/src/virglrenderer && ./autogen.sh && ./configure --with-glx --disable-tests && make install +RUN git clone https://gitlab.freedesktop.org/virgl/virglrenderer.git /usr/src/virglrenderer && \ + cd /usr/src/virglrenderer && git checkout virglrenderer-0.8.0 +RUN cd /usr/src/virglrenderer && ./autogen.sh && ./configure --disable-tests && make install # netmap 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é # 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é 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é 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/dockerfiles/debian-win32-cross.docker b/tests/docker/dockerfiles/debian-win32-cross.docker index 9d7053e59d..d16d6431bc 100644 --- a/tests/docker/dockerfiles/debian-win32-cross.docker +++ b/tests/docker/dockerfiles/debian-win32-cross.docker @@ -9,7 +9,7 @@ MAINTAINER Philippe Mathieu-Daudé ENV TARGET i686 -ENV PATH $PATH:/usr/lib/mxe/usr/$TARGET-w64-mingw32.shared/bin +ENV PATH $PATH:/usr/lib/mxe/usr/bin:/usr/lib/mxe/usr/$TARGET-w64-mingw32.shared/bin ENV PKG_CONFIG_PATH \ $PKG_CONFIG_PATH:/usr/lib/mxe/usr/$TARGET-w64-mingw32.shared/lib/pkgconfig diff --git a/tests/docker/dockerfiles/debian10.docker b/tests/docker/dockerfiles/debian10.docker index dad498b52e..0769700a41 100644 --- a/tests/docker/dockerfiles/debian10.docker +++ b/tests/docker/dockerfiles/debian10.docker @@ -17,16 +17,22 @@ RUN apt update && \ DEBIAN_FRONTEND=noninteractive apt install -yy eatmydata && \ DEBIAN_FRONTEND=noninteractive eatmydata \ apt install -y --no-install-recommends \ + bc \ bison \ build-essential \ ca-certificates \ clang \ + dbus \ flex \ + gdb-multiarch \ gettext \ git \ + libncurses5-dev \ pkg-config \ psmisc \ python3 \ python3-sphinx \ texinfo \ $(apt-get -s build-dep qemu | egrep ^Inst | fgrep '[all]' | cut -d\ -f2) + +ENV FEATURES docs diff --git a/tests/docker/dockerfiles/debian9.docker b/tests/docker/dockerfiles/debian9.docker index 8cbd742bb5..08cc970feb 100644 --- a/tests/docker/dockerfiles/debian9.docker +++ b/tests/docker/dockerfiles/debian9.docker @@ -17,16 +17,17 @@ RUN apt update && \ DEBIAN_FRONTEND=noninteractive apt install -yy eatmydata && \ DEBIAN_FRONTEND=noninteractive eatmydata \ apt install -y --no-install-recommends \ + bc \ bison \ build-essential \ ca-certificates \ clang \ flex \ + gdb-multiarch \ gettext \ git \ + libncurses5-dev \ pkg-config \ psmisc \ python3 \ - python3-sphinx \ - texinfo \ $(apt-get -s build-dep qemu | egrep ^Inst | fgrep '[all]' | cut -d\ -f2) diff --git a/tests/docker/dockerfiles/fedora-i386-cross.docker b/tests/docker/dockerfiles/fedora-i386-cross.docker index 9106cf9ebe..cd16cd1bfa 100644 --- a/tests/docker/dockerfiles/fedora-i386-cross.docker +++ b/tests/docker/dockerfiles/fedora-i386-cross.docker @@ -7,7 +7,8 @@ ENV PACKAGES \ gnutls-devel.i686 \ nettle-devel.i686 \ pixman-devel.i686 \ - zlib-devel.i686 + zlib-devel.i686 \ + libzstd-devel.i686 RUN dnf install -y $PACKAGES RUN rpm -q $PACKAGES | sort > /packages.txt diff --git a/tests/docker/dockerfiles/fedora.docker b/tests/docker/dockerfiles/fedora.docker index 4ddc7dd112..179575ecaa 100644 --- a/tests/docker/dockerfiles/fedora.docker +++ b/tests/docker/dockerfiles/fedora.docker @@ -1,14 +1,16 @@ FROM fedora:30 + +# Please keep this list sorted alphabetically ENV PACKAGES \ bc \ bison \ - bluez-libs-devel \ brlapi-devel \ bzip2 \ bzip2-devel \ ccache \ clang \ cyrus-sasl-devel \ + dbus-daemon \ device-mapper-multipath-devel \ findutils \ flex \ @@ -25,9 +27,9 @@ ENV PACKAGES \ libasan \ libattr-devel \ libblockdev-mpath-devel \ - libcap-devel \ libcap-ng-devel \ libcurl-devel \ + libepoxy-devel \ libfdt-devel \ libiscsi-devel \ libjpeg-devel \ @@ -37,8 +39,10 @@ ENV PACKAGES \ libseccomp-devel \ libssh-devel \ libubsan \ + libudev-devel \ libusbx-devel \ libxml2-devel \ + libzstd-devel \ llvm \ lzo-devel \ make \ @@ -77,8 +81,8 @@ ENV PACKAGES \ perl-Test-Harness \ pixman-devel \ python3 \ + python3-PyYAML \ python3-sphinx \ - PyYAML \ rdma-core-devel \ SDL2-devel \ snappy-devel \ @@ -99,4 +103,4 @@ ENV QEMU_CONFIGURE_OPTS --python=/usr/bin/python3 RUN dnf install -y $PACKAGES RUN rpm -q $PACKAGES | sort > /packages.txt ENV PATH $PATH:/usr/libexec/python3-sphinx/ -ENV FEATURES mingw clang pyyaml asan +ENV FEATURES mingw clang pyyaml asan docs diff --git a/tests/docker/dockerfiles/travis.docker b/tests/docker/dockerfiles/travis.docker index e8eb48dccf..591282561b 100644 --- a/tests/docker/dockerfiles/travis.docker +++ b/tests/docker/dockerfiles/travis.docker @@ -13,5 +13,5 @@ RUN apt-get -y install device-tree-compiler python3 python3-yaml dh-autoreconf g # Travis tools require PhantomJS / Neo4j / Maven accessible # in their PATH (QEMU build won't access them). ENV PATH /usr/local/phantomjs/bin:/usr/local/phantomjs:/usr/local/neo4j-3.2.7/bin:/usr/local/maven-3.5.2/bin:/usr/local/cmake-3.9.2/bin:/usr/local/clang-5.0.0/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin -ENV FEATURES clang pyyaml +ENV FEATURES clang pyyaml docs USER travis diff --git a/tests/docker/dockerfiles/ubuntu.docker b/tests/docker/dockerfiles/ubuntu.docker index f486492224..eeb3b22bf2 100644 --- a/tests/docker/dockerfiles/ubuntu.docker +++ b/tests/docker/dockerfiles/ubuntu.docker @@ -13,17 +13,16 @@ FROM ubuntu:19.04 ENV PACKAGES flex bison \ ccache \ clang \ + dbus \ gcc \ gettext \ git \ glusterfs-common \ libaio-dev \ libattr1-dev \ - libbluetooth-dev \ libbrlapi-dev \ libbz2-dev \ libcacard-dev \ - libcap-dev \ libcap-ng-dev \ libcurl4-gnutls-dev \ libdrm-dev \ @@ -59,6 +58,7 @@ ENV PACKAGES flex bison \ libvdeplug-dev \ libvte-2.91-dev \ libxen-dev \ + libzstd-dev \ make \ python3-yaml \ python3-sphinx \ @@ -68,4 +68,4 @@ ENV PACKAGES flex bison \ RUN apt-get update && \ DEBIAN_FRONTEND=noninteractive apt-get -y install $PACKAGES RUN dpkg -l $PACKAGES | sort > /packages.txt -ENV FEATURES clang pyyaml sdl2 +ENV FEATURES clang pyyaml sdl2 docs diff --git a/tests/docker/dockerfiles/ubuntu1804.docker b/tests/docker/dockerfiles/ubuntu1804.docker index 3cc4f492c4..f66b06f4cf 100644 --- a/tests/docker/dockerfiles/ubuntu1804.docker +++ b/tests/docker/dockerfiles/ubuntu1804.docker @@ -8,11 +8,9 @@ ENV PACKAGES flex bison \ glusterfs-common \ libaio-dev \ libattr1-dev \ - libbluetooth-dev \ libbrlapi-dev \ libbz2-dev \ libcacard-dev \ - libcap-dev \ libcap-ng-dev \ libcurl4-gnutls-dev \ libdrm-dev \ @@ -46,6 +44,7 @@ ENV PACKAGES flex bison \ libvdeplug-dev \ libvte-2.91-dev \ libxen-dev \ + libzstd-dev \ make \ python3-yaml \ python3-sphinx \ @@ -55,7 +54,7 @@ ENV PACKAGES flex bison \ RUN apt-get update && \ DEBIAN_FRONTEND=noninteractive apt-get -y install $PACKAGES RUN dpkg -l $PACKAGES | sort > /packages.txt -ENV FEATURES clang pyyaml sdl2 +ENV FEATURES clang pyyaml sdl2 docs # https://bugs.launchpad.net/qemu/+bug/1838763 ENV QEMU_CONFIGURE_OPTS --disable-libssh diff --git a/tests/docker/test-misc b/tests/docker/test-misc index d480afedca..cc94a738dd 100755 --- a/tests/docker/test-misc +++ b/tests/docker/test-misc @@ -14,6 +14,8 @@ . common.rc +requires docs + cd "$BUILD_DIR" # build everything else but QEMU 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/fp/Makefile b/tests/fp/Makefile index 5a35e7c210..56768ecfd2 100644 --- a/tests/fp/Makefile +++ b/tests/fp/Makefile @@ -554,15 +554,13 @@ TF_OBJS_LIB += $(TF_OBJS_TEST) BINARIES := fp-test$(EXESUF) fp-bench$(EXESUF) -# everything depends on config-host.h because platform.h includes it -all: $(BUILD_DIR)/config-host.h - $(MAKE) $(BINARIES) +# We require artefacts from the main build including config-host.h +# because platform.h includes it. Rather than re-invoking the main +# build we just error out if things aren't there. +$(LIBQEMUUTIL) $(BUILD_DIR)/config-host.h: + $(error $@ missing, re-run parent build) -$(LIBQEMUUTIL): - $(MAKE) -C $(BUILD_DIR) libqemuutil.a - -$(BUILD_DIR)/config-host.h: - $(MAKE) -C $(BUILD_DIR) config-host.h +all: $(BUILD_DIR)/config-host.h $(BINARIES) # libtestfloat.a depends on libsoftfloat.a, so specify it first FP_TEST_LIBS := libtestfloat.a libsoftfloat.a $(LIBQEMUUTIL) diff --git a/tests/guest-debug/run-test.py b/tests/guest-debug/run-test.py new file mode 100755 index 0000000000..8c49ee2f22 --- /dev/null +++ b/tests/guest-debug/run-test.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python3 +# +# Run a gdbstub test case +# +# Copyright (c) 2019 Linaro +# +# Author: Alex Bennée +# +# This work is licensed under the terms of the GNU GPL, version 2 or later. +# See the COPYING file in the top-level directory. +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import argparse +import subprocess +import shutil +import shlex + +def get_args(): + parser = argparse.ArgumentParser(description="A gdbstub test runner") + parser.add_argument("--qemu", help="Qemu binary for test", + required=True) + parser.add_argument("--qargs", help="Qemu arguments for test") + parser.add_argument("--binary", help="Binary to debug", + required=True) + parser.add_argument("--test", help="GDB test script", + required=True) + parser.add_argument("--gdb", help="The gdb binary to use", default=None) + + return parser.parse_args() + +if __name__ == '__main__': + args = get_args() + + # Search for a gdb we can use + if not args.gdb: + args.gdb = shutil.which("gdb-multiarch") + if not args.gdb: + args.gdb = shutil.which("gdb") + if not args.gdb: + print("We need gdb to run the test") + exit(-1) + + # Launch QEMU with binary + if "system" in args.qemu: + cmd = "%s %s %s -s -S" % (args.qemu, args.qargs, args.binary) + else: + cmd = "%s %s -g 1234 %s" % (args.qemu, args.qargs, args.binary) + + inferior = subprocess.Popen(shlex.split(cmd)) + + # Now launch gdb with our test and collect the result + gdb_cmd = "%s %s -ex 'target remote localhost:1234' -x %s" % (args.gdb, args.binary, args.test) + + result = subprocess.call(gdb_cmd, shell=True); + + exit(result) 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/iothread.c b/tests/iothread.c index 13c9fdcd8d..d3a2ee9a01 100644 --- a/tests/iothread.c +++ b/tests/iothread.c @@ -21,6 +21,8 @@ struct IOThread { AioContext *ctx; + GMainContext *worker_context; + GMainLoop *main_loop; QemuThread thread; QemuMutex init_done_lock; @@ -35,6 +37,17 @@ AioContext *qemu_get_current_aio_context(void) return my_iothread ? my_iothread->ctx : qemu_get_aio_context(); } +static void iothread_init_gcontext(IOThread *iothread) +{ + GSource *source; + + iothread->worker_context = g_main_context_new(); + source = aio_get_g_source(iothread_get_aio_context(iothread)); + g_source_attach(source, iothread->worker_context); + g_source_unref(source); + iothread->main_loop = g_main_loop_new(iothread->worker_context, TRUE); +} + static void *iothread_run(void *opaque) { IOThread *iothread = opaque; @@ -44,6 +57,20 @@ static void *iothread_run(void *opaque) my_iothread = iothread; qemu_mutex_lock(&iothread->init_done_lock); iothread->ctx = aio_context_new(&error_abort); + + /* + * We must connect the ctx to a GMainContext, because in older versions + * of glib the g_source_ref()/unref() functions are not threadsafe + * on sources without a context. + */ + iothread_init_gcontext(iothread); + + /* + * g_main_context_push_thread_default() must be called before anything + * in this new thread uses glib. + */ + g_main_context_push_thread_default(iothread->worker_context); + qemu_cond_signal(&iothread->init_done_cond); qemu_mutex_unlock(&iothread->init_done_lock); @@ -51,6 +78,7 @@ static void *iothread_run(void *opaque) aio_poll(iothread->ctx, true); } + g_main_context_pop_thread_default(iothread->worker_context); rcu_unregister_thread(); return NULL; } @@ -66,6 +94,8 @@ void iothread_join(IOThread *iothread) { aio_bh_schedule_oneshot(iothread->ctx, iothread_stop_bh, iothread); qemu_thread_join(&iothread->thread); + g_main_context_unref(iothread->worker_context); + g_main_loop_unref(iothread->main_loop); qemu_cond_destroy(&iothread->init_done_cond); qemu_mutex_destroy(&iothread->init_done_lock); aio_context_unref(iothread->ctx); diff --git a/tests/libqos/libqos-spapr.h b/tests/libqos/libqos-spapr.h deleted file mode 100644 index dcb5c43ad3..0000000000 --- a/tests/libqos/libqos-spapr.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef LIBQOS_SPAPR_H -#define LIBQOS_SPAPR_H - -#include "libqos/libqos.h" - -QOSState *qtest_spapr_vboot(const char *cmdline_fmt, va_list ap); -QOSState *qtest_spapr_boot(const char *cmdline_fmt, ...); -void qtest_spapr_shutdown(QOSState *qs); - -#endif 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/numa-test.c b/tests/numa-test.c deleted file mode 100644 index 8de8581231..0000000000 --- a/tests/numa-test.c +++ /dev/null @@ -1,361 +0,0 @@ -/* - * NUMA configuration test cases - * - * Copyright (c) 2017 Red Hat Inc. - * Authors: - * Igor Mammedov - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -#include "qemu/osdep.h" -#include "libqtest.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qlist.h" - -static char *make_cli(const char *generic_cli, const char *test_cli) -{ - return g_strdup_printf("%s %s", generic_cli ? generic_cli : "", test_cli); -} - -static void test_mon_explicit(const void *data) -{ - char *s; - char *cli; - QTestState *qts; - - cli = make_cli(data, "-smp 8 " - "-numa node,nodeid=0,cpus=0-3 " - "-numa node,nodeid=1,cpus=4-7 "); - qts = qtest_init(cli); - - s = qtest_hmp(qts, "info numa"); - g_assert(strstr(s, "node 0 cpus: 0 1 2 3")); - g_assert(strstr(s, "node 1 cpus: 4 5 6 7")); - g_free(s); - - qtest_quit(qts); - g_free(cli); -} - -static void test_mon_default(const void *data) -{ - char *s; - char *cli; - QTestState *qts; - - cli = make_cli(data, "-smp 8 -numa node -numa node"); - qts = qtest_init(cli); - - s = qtest_hmp(qts, "info numa"); - g_assert(strstr(s, "node 0 cpus: 0 2 4 6")); - g_assert(strstr(s, "node 1 cpus: 1 3 5 7")); - g_free(s); - - qtest_quit(qts); - g_free(cli); -} - -static void test_mon_partial(const void *data) -{ - char *s; - char *cli; - QTestState *qts; - - cli = make_cli(data, "-smp 8 " - "-numa node,nodeid=0,cpus=0-1 " - "-numa node,nodeid=1,cpus=4-5 "); - qts = qtest_init(cli); - - s = qtest_hmp(qts, "info numa"); - g_assert(strstr(s, "node 0 cpus: 0 1 2 3 6 7")); - g_assert(strstr(s, "node 1 cpus: 4 5")); - g_free(s); - - qtest_quit(qts); - g_free(cli); -} - -static QList *get_cpus(QTestState *qts, QDict **resp) -{ - *resp = qtest_qmp(qts, "{ 'execute': 'query-cpus' }"); - g_assert(*resp); - g_assert(qdict_haskey(*resp, "return")); - return qdict_get_qlist(*resp, "return"); -} - -static void test_query_cpus(const void *data) -{ - char *cli; - QDict *resp; - QList *cpus; - QObject *e; - QTestState *qts; - - cli = make_cli(data, "-smp 8 -numa node,cpus=0-3 -numa node,cpus=4-7"); - qts = qtest_init(cli); - cpus = get_cpus(qts, &resp); - g_assert(cpus); - - while ((e = qlist_pop(cpus))) { - QDict *cpu, *props; - int64_t cpu_idx, node; - - cpu = qobject_to(QDict, e); - g_assert(qdict_haskey(cpu, "CPU")); - g_assert(qdict_haskey(cpu, "props")); - - cpu_idx = qdict_get_int(cpu, "CPU"); - props = qdict_get_qdict(cpu, "props"); - g_assert(qdict_haskey(props, "node-id")); - node = qdict_get_int(props, "node-id"); - if (cpu_idx >= 0 && cpu_idx < 4) { - g_assert_cmpint(node, ==, 0); - } else { - g_assert_cmpint(node, ==, 1); - } - qobject_unref(e); - } - - qobject_unref(resp); - qtest_quit(qts); - g_free(cli); -} - -static void pc_numa_cpu(const void *data) -{ - char *cli; - QDict *resp; - QList *cpus; - QObject *e; - QTestState *qts; - - cli = make_cli(data, "-cpu pentium -smp 8,sockets=2,cores=2,threads=2 " - "-numa node,nodeid=0 -numa node,nodeid=1 " - "-numa cpu,node-id=1,socket-id=0 " - "-numa cpu,node-id=0,socket-id=1,core-id=0 " - "-numa cpu,node-id=0,socket-id=1,core-id=1,thread-id=0 " - "-numa cpu,node-id=1,socket-id=1,core-id=1,thread-id=1"); - qts = qtest_init(cli); - cpus = get_cpus(qts, &resp); - g_assert(cpus); - - while ((e = qlist_pop(cpus))) { - QDict *cpu, *props; - int64_t socket, core, thread, node; - - cpu = qobject_to(QDict, e); - g_assert(qdict_haskey(cpu, "props")); - props = qdict_get_qdict(cpu, "props"); - - g_assert(qdict_haskey(props, "node-id")); - node = qdict_get_int(props, "node-id"); - g_assert(qdict_haskey(props, "socket-id")); - socket = qdict_get_int(props, "socket-id"); - g_assert(qdict_haskey(props, "core-id")); - core = qdict_get_int(props, "core-id"); - g_assert(qdict_haskey(props, "thread-id")); - thread = qdict_get_int(props, "thread-id"); - - if (socket == 0) { - g_assert_cmpint(node, ==, 1); - } else if (socket == 1 && core == 0) { - g_assert_cmpint(node, ==, 0); - } else if (socket == 1 && core == 1 && thread == 0) { - g_assert_cmpint(node, ==, 0); - } else if (socket == 1 && core == 1 && thread == 1) { - g_assert_cmpint(node, ==, 1); - } else { - g_assert(false); - } - qobject_unref(e); - } - - qobject_unref(resp); - qtest_quit(qts); - g_free(cli); -} - -static void spapr_numa_cpu(const void *data) -{ - char *cli; - QDict *resp; - QList *cpus; - QObject *e; - QTestState *qts; - - cli = make_cli(data, "-smp 4,cores=4 " - "-numa node,nodeid=0 -numa node,nodeid=1 " - "-numa cpu,node-id=0,core-id=0 " - "-numa cpu,node-id=0,core-id=1 " - "-numa cpu,node-id=0,core-id=2 " - "-numa cpu,node-id=1,core-id=3"); - qts = qtest_init(cli); - cpus = get_cpus(qts, &resp); - g_assert(cpus); - - while ((e = qlist_pop(cpus))) { - QDict *cpu, *props; - int64_t core, node; - - cpu = qobject_to(QDict, e); - g_assert(qdict_haskey(cpu, "props")); - props = qdict_get_qdict(cpu, "props"); - - g_assert(qdict_haskey(props, "node-id")); - node = qdict_get_int(props, "node-id"); - g_assert(qdict_haskey(props, "core-id")); - core = qdict_get_int(props, "core-id"); - - if (core >= 0 && core < 3) { - g_assert_cmpint(node, ==, 0); - } else if (core == 3) { - g_assert_cmpint(node, ==, 1); - } else { - g_assert(false); - } - qobject_unref(e); - } - - qobject_unref(resp); - qtest_quit(qts); - g_free(cli); -} - -static void aarch64_numa_cpu(const void *data) -{ - char *cli; - QDict *resp; - QList *cpus; - QObject *e; - QTestState *qts; - - cli = make_cli(data, "-smp 2 " - "-numa node,nodeid=0 -numa node,nodeid=1 " - "-numa cpu,node-id=1,thread-id=0 " - "-numa cpu,node-id=0,thread-id=1"); - qts = qtest_init(cli); - cpus = get_cpus(qts, &resp); - g_assert(cpus); - - while ((e = qlist_pop(cpus))) { - QDict *cpu, *props; - int64_t thread, node; - - cpu = qobject_to(QDict, e); - g_assert(qdict_haskey(cpu, "props")); - props = qdict_get_qdict(cpu, "props"); - - g_assert(qdict_haskey(props, "node-id")); - node = qdict_get_int(props, "node-id"); - g_assert(qdict_haskey(props, "thread-id")); - thread = qdict_get_int(props, "thread-id"); - - if (thread == 0) { - g_assert_cmpint(node, ==, 1); - } else if (thread == 1) { - g_assert_cmpint(node, ==, 0); - } else { - g_assert(false); - } - qobject_unref(e); - } - - qobject_unref(resp); - qtest_quit(qts); - g_free(cli); -} - -static void pc_dynamic_cpu_cfg(const void *data) -{ - QObject *e; - QDict *resp; - QList *cpus; - QTestState *qs; - - qs = qtest_initf("%s -nodefaults --preconfig -smp 2", - data ? (char *)data : ""); - - /* create 2 numa nodes */ - g_assert(!qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," - " 'arguments': { 'type': 'node', 'nodeid': 0 } }"))); - g_assert(!qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," - " 'arguments': { 'type': 'node', 'nodeid': 1 } }"))); - - /* map 2 cpus in non default reverse order - * i.e socket1->node0, socket0->node1 - */ - g_assert(!qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," - " 'arguments': { 'type': 'cpu', 'node-id': 0, 'socket-id': 1 } }"))); - g_assert(!qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," - " 'arguments': { 'type': 'cpu', 'node-id': 1, 'socket-id': 0 } }"))); - - /* let machine initialization to complete and run */ - g_assert(!qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'x-exit-preconfig' }"))); - qtest_qmp_eventwait(qs, "RESUME"); - - /* check that CPUs are mapped as expected */ - resp = qtest_qmp(qs, "{ 'execute': 'query-hotpluggable-cpus'}"); - g_assert(qdict_haskey(resp, "return")); - cpus = qdict_get_qlist(resp, "return"); - g_assert(cpus); - while ((e = qlist_pop(cpus))) { - const QDict *cpu, *props; - int64_t socket, node; - - cpu = qobject_to(QDict, e); - g_assert(qdict_haskey(cpu, "props")); - props = qdict_get_qdict(cpu, "props"); - - g_assert(qdict_haskey(props, "node-id")); - node = qdict_get_int(props, "node-id"); - g_assert(qdict_haskey(props, "socket-id")); - socket = qdict_get_int(props, "socket-id"); - - if (socket == 0) { - g_assert_cmpint(node, ==, 1); - } else if (socket == 1) { - g_assert_cmpint(node, ==, 0); - } else { - g_assert(false); - } - qobject_unref(e); - } - qobject_unref(resp); - - qtest_quit(qs); -} - -int main(int argc, char **argv) -{ - const char *args = NULL; - const char *arch = qtest_get_arch(); - - if (strcmp(arch, "aarch64") == 0) { - args = "-machine virt"; - } - - g_test_init(&argc, &argv, NULL); - - qtest_add_data_func("/numa/mon/default", args, test_mon_default); - qtest_add_data_func("/numa/mon/cpus/explicit", args, test_mon_explicit); - qtest_add_data_func("/numa/mon/cpus/partial", args, test_mon_partial); - qtest_add_data_func("/numa/qmp/cpus/query-cpus", args, test_query_cpus); - - if (!strcmp(arch, "i386") || !strcmp(arch, "x86_64")) { - qtest_add_data_func("/numa/pc/cpu/explicit", args, pc_numa_cpu); - qtest_add_data_func("/numa/pc/dynamic/cpu", args, pc_dynamic_cpu_cfg); - } - - if (!strcmp(arch, "ppc64")) { - qtest_add_data_func("/numa/spapr/cpu/explicit", args, spapr_numa_cpu); - } - - if (!strcmp(arch, "aarch64")) { - qtest_add_data_func("/numa/aarch64/cpu/explicit", args, - aarch64_numa_cpu); - } - - return g_test_run(); -} diff --git a/tests/plugin/bb.c b/tests/plugin/bb.c index f30bea08dc..df19fd359d 100644 --- a/tests/plugin/bb.c +++ b/tests/plugin/bb.c @@ -22,9 +22,9 @@ static bool do_inline; static void plugin_exit(qemu_plugin_id_t id, void *p) { - g_autofree gchar *out; - out = g_strdup_printf("bb's: %" PRIu64", insns: %" PRIu64 "\n", - bb_count, insn_count); + g_autofree gchar *out = g_strdup_printf( + "bb's: %" PRIu64", insns: %" PRIu64 "\n", + bb_count, insn_count); qemu_plugin_outs(out); } diff --git a/tests/plugin/howvec.c b/tests/plugin/howvec.c index 4ca555e123..3b9a6939f2 100644 --- a/tests/plugin/howvec.c +++ b/tests/plugin/howvec.c @@ -163,6 +163,13 @@ static gint cmp_exec_count(gconstpointer a, gconstpointer b) return ea->count > eb->count ? -1 : 1; } +static void free_record(gpointer data) +{ + InsnExecCount *rec = (InsnExecCount *) data; + g_free(rec->insn); + g_free(rec); +} + static void plugin_exit(qemu_plugin_id_t id, void *p) { g_autoptr(GString) report = g_string_new("Instruction Classes:\n"); @@ -195,30 +202,31 @@ static void plugin_exit(qemu_plugin_id_t id, void *p) counts = g_hash_table_get_values(insns); if (counts && g_list_next(counts)) { - GList *it; - g_string_append_printf(report,"Individual Instructions:\n"); + counts = g_list_sort(counts, cmp_exec_count); - it = g_list_sort(counts, cmp_exec_count); - - for (i = 0; i < limit && it->next; i++, it = it->next) { - InsnExecCount *rec = (InsnExecCount *) it->data; - g_string_append_printf(report, "Instr: %-24s\t(%ld hits)\t(op=%#08x/%s)\n", + for (i = 0; i < limit && g_list_next(counts); + i++, counts = g_list_next(counts)) { + InsnExecCount *rec = (InsnExecCount *) counts->data; + g_string_append_printf(report, + "Instr: %-24s\t(%ld hits)\t(op=%#08x/%s)\n", rec->insn, rec->count, rec->opcode, rec->class ? rec->class->class : "un-categorised"); } - g_list_free(it); + g_list_free(counts); } + g_hash_table_destroy(insns); + qemu_plugin_outs(report->str); } static void plugin_init(void) { - insns = g_hash_table_new(NULL, g_direct_equal); + insns = g_hash_table_new_full(NULL, g_direct_equal, NULL, &free_record); } static void vcpu_insn_exec_before(unsigned int cpu_index, void *udata) diff --git a/tests/plugin/insn.c b/tests/plugin/insn.c index 0a8f5a0000..a9a6e41237 100644 --- a/tests/plugin/insn.c +++ b/tests/plugin/insn.c @@ -44,8 +44,7 @@ static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) static void plugin_exit(qemu_plugin_id_t id, void *p) { - g_autofree gchar *out; - out = g_strdup_printf("insns: %" PRIu64 "\n", insn_count); + g_autofree gchar *out = g_strdup_printf("insns: %" PRIu64 "\n", insn_count); qemu_plugin_outs(out); } diff --git a/tests/ptimer-test.c b/tests/ptimer-test.c index e16c30ce57..9176b96c1c 100644 --- a/tests/ptimer-test.c +++ b/tests/ptimer-test.c @@ -14,7 +14,6 @@ #include "qemu/main-loop.h" #include "hw/ptimer.h" -#include "libqtest.h" #include "ptimer-test.h" static bool triggered; diff --git a/tests/qapi-schema/alternate-base.err b/tests/qapi-schema/alternate-base.err index 31ebe56bbf..970a08ab26 100644 --- a/tests/qapi-schema/alternate-base.err +++ b/tests/qapi-schema/alternate-base.err @@ -1,3 +1,3 @@ alternate-base.json: In alternate 'Alt': alternate-base.json:4: alternate has unknown key 'base' -Valid keys are 'alternate', 'data', 'if'. +Valid keys are 'alternate', 'data', 'features', 'if'. diff --git a/tests/qapi-schema/doc-good.json b/tests/qapi-schema/doc-good.json index d992e713d9..ddd89d1233 100644 --- a/tests/qapi-schema/doc-good.json +++ b/tests/qapi-schema/doc-good.json @@ -53,10 +53,14 @@ # @Enum: # @one: The _one_ {and only} # +# Features: +# @enum-feat: Also _one_ {and only} +# # @two is undocumented ## { 'enum': 'Enum', 'data': [ { 'name': 'one', 'if': 'defined(IFONE)' }, 'two' ], + 'features': [ 'enum-feat' ], 'if': 'defined(IFCOND)' } ## @@ -74,10 +78,13 @@ # # Features: # @variant1-feat: a feature +# @member-feat: a member feature ## { 'struct': 'Variant1', 'features': [ 'variant1-feat' ], - 'data': { 'var1': { 'type': 'str', 'if': 'defined(IFSTR)' } } } + 'data': { 'var1': { 'type': 'str', + 'features': [ 'member-feat' ], + 'if': 'defined(IFSTR)' } } } ## # @Variant2: @@ -86,24 +93,34 @@ ## # @Object: +# Features: +# @union-feat1: a feature ## { 'union': 'Object', + 'features': [ 'union-feat1' ], 'base': 'Base', 'discriminator': 'base1', 'data': { 'one': 'Variant1', 'two': { 'type': 'Variant2', 'if': 'IFTWO' } } } ## # @SugaredUnion: +# Features: +# @union-feat2: a feature ## { 'union': 'SugaredUnion', + 'features': [ 'union-feat2' ], 'data': { 'one': 'Variant1', 'two': { 'type': 'Variant2', 'if': 'IFTWO' } } } ## # @Alternate: # @i: an integer # @b is undocumented +# +# Features: +# @alt-feat: a feature ## { 'alternate': 'Alternate', + 'features': [ 'alt-feat' ], 'data': { 'i': 'int', 'b': 'bool' } } ## @@ -160,6 +177,9 @@ ## # @EVT-BOXED: +# Features: +# @feat3: a feature ## { 'event': 'EVT-BOXED', 'boxed': true, + 'features': [ 'feat3' ], 'data': 'Object' } diff --git a/tests/qapi-schema/doc-good.out b/tests/qapi-schema/doc-good.out index 4c9406a464..6757dd26a2 100644 --- a/tests/qapi-schema/doc-good.out +++ b/tests/qapi-schema/doc-good.out @@ -15,11 +15,13 @@ enum Enum if ['defined(IFONE)'] member two if ['defined(IFCOND)'] + feature enum-feat object Base member base1: Enum optional=False object Variant1 member var1: str optional=False if ['defined(IFSTR)'] + feature member-feat feature variant1-feat object Variant2 object Object @@ -28,6 +30,7 @@ object Object case one: Variant1 case two: Variant2 if ['IFTWO'] + feature union-feat1 object q_obj_Variant1-wrapper member data: Variant1 optional=False object q_obj_Variant2-wrapper @@ -42,10 +45,12 @@ object SugaredUnion case one: q_obj_Variant1-wrapper case two: q_obj_Variant2-wrapper if ['IFTWO'] + feature union-feat2 alternate Alternate tag type case i: int case b: bool + feature alt-feat object q_obj_cmd-arg member arg1: int optional=False member arg2: str optional=True @@ -60,6 +65,7 @@ command cmd-boxed Object -> None feature cmd-feat2 event EVT-BOXED Object boxed=True + feature feat3 doc freeform body= = Section @@ -112,6 +118,8 @@ doc symbol=Enum The _one_ {and only} arg=two + feature=enum-feat +Also _one_ {and only} section=None @two is undocumented doc symbol=Base @@ -128,17 +136,23 @@ Another paragraph (but no @var: line) feature=variant1-feat a feature + feature=member-feat +a member feature doc symbol=Variant2 body= doc symbol=Object body= + feature=union-feat1 +a feature doc symbol=SugaredUnion body= arg=type + feature=union-feat2 +a feature doc symbol=Alternate body= @@ -147,6 +161,8 @@ an integer @b is undocumented arg=b + feature=alt-feat +a feature doc freeform body= == Another subsection @@ -197,3 +213,5 @@ another feature doc symbol=EVT-BOXED body= + feature=feat3 +a feature diff --git a/tests/qapi-schema/doc-good.texi b/tests/qapi-schema/doc-good.texi index d4b15dabf0..7f28fb7a0f 100644 --- a/tests/qapi-schema/doc-good.texi +++ b/tests/qapi-schema/doc-good.texi @@ -88,6 +88,12 @@ The @emph{one} @{and only@} @item @code{two} Not documented @end table + +@b{Features:} +@table @asis +@item @code{enum-feat} +Also @emph{one} @{and only@} +@end table @code{two} is undocumented @b{If:} @code{defined(IFCOND)} @@ -126,6 +132,8 @@ Not documented @table @asis @item @code{variant1-feat} a feature +@item @code{member-feat} +a member feature @end table @end deftp @@ -151,6 +159,12 @@ a feature @item The members of @code{Variant2} when @code{base1} is @t{"two"} (@b{If:} @code{IFTWO}) @end table +@b{Features:} +@table @asis +@item @code{union-feat1} +a feature +@end table + @end deftp @@ -167,6 +181,12 @@ One of @t{"one"}, @t{"two"} @item @code{data: Variant2} when @code{type} is @t{"two"} (@b{If:} @code{IFTWO}) @end table +@b{Features:} +@table @asis +@item @code{union-feat2} +a feature +@end table + @end deftp @@ -184,6 +204,12 @@ an integer Not documented @end table +@b{Features:} +@table @asis +@item @code{alt-feat} +a feature +@end table + @end deftp @@ -283,5 +309,11 @@ another feature @b{Arguments:} the members of @code{Object} +@b{Features:} +@table @asis +@item @code{feat3} +a feature +@end table + @end deftypefn diff --git a/tests/qapi-schema/empty.out b/tests/qapi-schema/empty.out index 5b53d00702..69666c39ad 100644 --- a/tests/qapi-schema/empty.out +++ b/tests/qapi-schema/empty.out @@ -9,3 +9,4 @@ enum QType member qdict member qlist member qbool +module empty.json diff --git a/tests/qapi-schema/features-deprecated-type.err b/tests/qapi-schema/features-deprecated-type.err new file mode 100644 index 0000000000..af4ffe20aa --- /dev/null +++ b/tests/qapi-schema/features-deprecated-type.err @@ -0,0 +1,2 @@ +features-deprecated-type.json: In struct 'S': +features-deprecated-type.json:2: feature 'deprecated' is not supported for types diff --git a/tests/qapi-schema/features-deprecated-type.json b/tests/qapi-schema/features-deprecated-type.json new file mode 100644 index 0000000000..4b5bf5b86e --- /dev/null +++ b/tests/qapi-schema/features-deprecated-type.json @@ -0,0 +1,3 @@ +# Feature 'deprecated' is not supported for types +{ 'struct': 'S', 'data': {}, + 'features': [ 'deprecated' ] } diff --git a/tests/qapi-schema/features-deprecated-type.out b/tests/qapi-schema/features-deprecated-type.out new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/qapi-schema/include-repetition.out b/tests/qapi-schema/include-repetition.out index 5423983239..0b654ddebb 100644 --- a/tests/qapi-schema/include-repetition.out +++ b/tests/qapi-schema/include-repetition.out @@ -11,15 +11,13 @@ enum QType member qbool module include-repetition.json include comments.json +include include-repetition-sub.json +include comments.json module comments.json enum Status member good member bad member ugly -module include-repetition.json -include include-repetition-sub.json module include-repetition-sub.json include comments.json include comments.json -module include-repetition.json -include comments.json diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json index 9abf175fe0..6b1f05afa7 100644 --- a/tests/qapi-schema/qapi-schema-test.json +++ b/tests/qapi-schema/qapi-schema-test.json @@ -252,13 +252,13 @@ 'bar': { 'type': ['TestIfEnum'], 'if': 'defined(TEST_IF_EVT_BAR)' } }, 'if': 'defined(TEST_IF_EVT) && defined(TEST_IF_STRUCT)' } -# test 'features' for structs +# test 'features' { 'struct': 'FeatureStruct0', 'data': { 'foo': 'int' }, 'features': [] } { 'struct': 'FeatureStruct1', - 'data': { 'foo': 'int' }, + 'data': { 'foo': { 'type': 'int', 'features': [ 'deprecated' ] } }, 'features': [ 'feature1' ] } { 'struct': 'FeatureStruct2', 'data': { 'foo': 'int' }, @@ -281,7 +281,22 @@ 'data': { 'foo': 'int' }, 'features': [ { 'name': 'feature1', 'if': [ 'defined(TEST_IF_COND_1)', 'defined(TEST_IF_COND_2)'] } ] } -{ 'command': 'test-features', + +{ 'enum': 'FeatureEnum1', + 'data': [ 'eins', 'zwei', 'drei' ], + 'features': [ 'feature1' ] } + +{ 'union': 'FeatureUnion1', + 'base': { 'tag': 'FeatureEnum1' }, + 'discriminator': 'tag', + 'data': { 'eins': 'FeatureStruct1' }, + 'features': [ 'feature1' ] } + +{ 'alternate': 'FeatureAlternate1', + 'data': { 'eins': 'FeatureStruct1' }, + 'features': [ 'feature1' ] } + +{ 'command': 'test-features0', 'data': { 'fs0': 'FeatureStruct0', 'fs1': 'FeatureStruct1', 'fs2': 'FeatureStruct2', @@ -289,14 +304,11 @@ 'fs4': 'FeatureStruct4', 'cfs1': 'CondFeatureStruct1', 'cfs2': 'CondFeatureStruct2', - 'cfs3': 'CondFeatureStruct3' } } - -# test 'features' for command - -{ 'command': 'test-command-features0', + 'cfs3': 'CondFeatureStruct3' }, 'features': [] } + { 'command': 'test-command-features1', - 'features': [ 'feature1' ] } + 'features': [ 'deprecated' ] } { 'command': 'test-command-features3', 'features': [ 'feature1', 'feature2' ] } @@ -308,3 +320,6 @@ { 'command': 'test-command-cond-features3', 'features': [ { 'name': 'feature1', 'if': [ 'defined(TEST_IF_COND_1)', 'defined(TEST_IF_COND_2)'] } ] } + +{ 'event': 'TEST-EVENT-FEATURES1', + 'features': [ 'deprecated' ] } diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out index 3660e75a48..891b4101e0 100644 --- a/tests/qapi-schema/qapi-schema-test.out +++ b/tests/qapi-schema/qapi-schema-test.out @@ -153,9 +153,6 @@ object q_obj_sizeList-wrapper member data: sizeList optional=False object q_obj_anyList-wrapper member data: anyList optional=False -module sub-sub-module.json -array StatusList Status -module qapi-schema-test.json object q_obj_StatusList-wrapper member data: StatusList optional=False enum UserDefListUnionKind @@ -193,17 +190,6 @@ object UserDefListUnion case any: q_obj_anyList-wrapper case user: q_obj_StatusList-wrapper include include/sub-module.json -module include/sub-module.json -include sub-sub-module.json -module sub-sub-module.json -enum Status - member good - member bad - member ugly -module include/sub-module.json -object SecondArrayRef - member s: StatusList optional=False -module qapi-schema-test.json command user_def_cmd None -> None gen=True success_response=True boxed=False oob=False preconfig=False object q_obj_user_def_cmd1-arg @@ -373,6 +359,7 @@ object FeatureStruct0 member foo: int optional=False object FeatureStruct1 member foo: int optional=False + feature deprecated feature feature1 object FeatureStruct2 member foo: int optional=False @@ -401,7 +388,25 @@ object CondFeatureStruct3 member foo: int optional=False feature feature1 if ['defined(TEST_IF_COND_1)', 'defined(TEST_IF_COND_2)'] -object q_obj_test-features-arg +enum FeatureEnum1 + member eins + member zwei + member drei + feature feature1 +object q_obj_FeatureUnion1-base + member tag: FeatureEnum1 optional=False +object FeatureUnion1 + base q_obj_FeatureUnion1-base + tag tag + case eins: FeatureStruct1 + case zwei: q_empty + case drei: q_empty + feature feature1 +alternate FeatureAlternate1 + tag type + case eins: FeatureStruct1 + feature feature1 +object q_obj_test-features0-arg member fs0: FeatureStruct0 optional=False member fs1: FeatureStruct1 optional=False member fs2: FeatureStruct2 optional=False @@ -410,13 +415,11 @@ object q_obj_test-features-arg member cfs1: CondFeatureStruct1 optional=False member cfs2: CondFeatureStruct2 optional=False member cfs3: CondFeatureStruct3 optional=False -command test-features q_obj_test-features-arg -> None - gen=True success_response=True boxed=False oob=False preconfig=False -command test-command-features0 None -> None +command test-features0 q_obj_test-features0-arg -> None gen=True success_response=True boxed=False oob=False preconfig=False command test-command-features1 None -> None gen=True success_response=True boxed=False oob=False preconfig=False - feature feature1 + feature deprecated command test-command-features3 None -> None gen=True success_response=True boxed=False oob=False preconfig=False feature feature1 @@ -435,3 +438,16 @@ command test-command-cond-features3 None -> None gen=True success_response=True boxed=False oob=False preconfig=False feature feature1 if ['defined(TEST_IF_COND_1)', 'defined(TEST_IF_COND_2)'] +event TEST-EVENT-FEATURES1 None + boxed=False + feature deprecated +module include/sub-module.json +include sub-sub-module.json +object SecondArrayRef + member s: StatusList optional=False +module sub-sub-module.json +array StatusList Status +enum Status + member good + member bad + member ugly diff --git a/tests/qapi-schema/test-qapi.py b/tests/qapi-schema/test-qapi.py index bad14edb47..f396b471eb 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,21 +11,16 @@ # See the COPYING file in the top-level directory. # -from __future__ import print_function import argparse import difflib import os import sys +from io import StringIO from qapi.error import QAPIError from qapi.schema import QAPISchema, QAPISchemaVisitor -if sys.version_info[0] < 3: - from cStringIO import StringIO -else: - from io import StringIO - class QAPISchemaTestVisitor(QAPISchemaVisitor): @@ -35,7 +30,7 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor): def visit_include(self, name, info): print('include %s' % name) - def visit_enum_type(self, name, info, ifcond, members, prefix): + def visit_enum_type(self, name, info, ifcond, features, members, prefix): print('enum %s' % name) if prefix: print(' prefix %s' % prefix) @@ -43,6 +38,7 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor): print(' member %s' % m.name) self._print_if(m.ifcond, indent=8) self._print_if(ifcond) + self._print_features(features) def visit_array_type(self, name, info, ifcond, element_type): if not info: @@ -50,8 +46,8 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor): print('array %s %s' % (name, element_type.name)) self._print_if(ifcond) - def visit_object_type(self, name, info, ifcond, base, members, variants, - features): + def visit_object_type(self, name, info, ifcond, features, + base, members, variants): print('object %s' % name) if base: print(' base %s' % base.name) @@ -59,18 +55,20 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor): print(' member %s: %s optional=%s' % (m.name, m.type.name, m.optional)) self._print_if(m.ifcond, 8) + self._print_features(m.features, indent=8) self._print_variants(variants) self._print_if(ifcond) self._print_features(features) - def visit_alternate_type(self, name, info, ifcond, variants): + def visit_alternate_type(self, name, info, ifcond, features, variants): print('alternate %s' % name) self._print_variants(variants) self._print_if(ifcond) + self._print_features(features) - def visit_command(self, name, info, ifcond, arg_type, ret_type, gen, - success_response, boxed, allow_oob, allow_preconfig, - features): + def visit_command(self, name, info, ifcond, features, + arg_type, ret_type, gen, success_response, boxed, + allow_oob, allow_preconfig): print('command %s %s -> %s' % (name, arg_type and arg_type.name, ret_type and ret_type.name)) @@ -79,10 +77,11 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor): self._print_if(ifcond) self._print_features(features) - def visit_event(self, name, info, ifcond, arg_type, boxed): + def visit_event(self, name, info, ifcond, features, arg_type, boxed): print('event %s %s' % (name, arg_type and arg_type.name)) print(' boxed=%s' % boxed) self._print_if(ifcond) + self._print_features(features) @staticmethod def _print_variants(variants): @@ -98,11 +97,11 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor): print('%sif %s' % (' ' * indent, ifcond)) @classmethod - def _print_features(cls, features): + def _print_features(cls, features, indent=4): if features: for f in features: - print(' feature %s' % f.name) - cls._print_if(f.ifcond, 8) + print('%sfeature %s' % (' ' * indent, f.name)) + cls._print_if(f.ifcond, indent + 4) def test_frontend(fname): diff --git a/tests/qemu-iotests/005 b/tests/qemu-iotests/005 index 58442762fe..b6d03ac37d 100755 --- a/tests/qemu-iotests/005 +++ b/tests/qemu-iotests/005 @@ -59,10 +59,7 @@ fi # Sanity check: For raw, we require a file system that permits the creation # of a HUGE (but very sparse) file. Check we can create it before continuing. if [ "$IMGFMT" = "raw" ]; then - if ! truncate --size=5T "$TEST_IMG"; then - _notrun "file system on $TEST_DIR does not support large enough files" - fi - rm "$TEST_IMG" + _require_large_file 5T fi echo diff --git a/tests/qemu-iotests/007 b/tests/qemu-iotests/007 index 7d3544b479..160683adf8 100755 --- a/tests/qemu-iotests/007 +++ b/tests/qemu-iotests/007 @@ -41,8 +41,9 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 _supported_fmt qcow2 _supported_proto generic # refcount_bits must be at least 4 so we can create ten internal snapshots -# (1 bit supports none, 2 bits support two, 4 bits support 14) -_unsupported_imgopts 'refcount_bits=\(1\|2\)[^0-9]' +# (1 bit supports none, 2 bits support two, 4 bits support 14); +# snapshot are generally impossible with external data files +_unsupported_imgopts 'refcount_bits=\(1\|2\)[^0-9]' data_file echo echo "creating image" diff --git a/tests/qemu-iotests/014 b/tests/qemu-iotests/014 index 2f728a1956..e1221c0fff 100755 --- a/tests/qemu-iotests/014 +++ b/tests/qemu-iotests/014 @@ -43,6 +43,8 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 _supported_fmt qcow2 _supported_proto file _supported_os Linux +# Compression and snapshots do not work with external data files +_unsupported_imgopts data_file TEST_OFFSETS="0 4294967296" TEST_OPS="writev read write readv" diff --git a/tests/qemu-iotests/015 b/tests/qemu-iotests/015 index eec5387f3d..4d8effd0ae 100755 --- a/tests/qemu-iotests/015 +++ b/tests/qemu-iotests/015 @@ -40,8 +40,9 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 # actually any format that supports snapshots _supported_fmt qcow2 _supported_proto generic -# Internal snapshots are (currently) impossible with refcount_bits=1 -_unsupported_imgopts 'refcount_bits=1[^0-9]' +# Internal snapshots are (currently) impossible with refcount_bits=1, +# and generally impossible with external data files +_unsupported_imgopts 'refcount_bits=1[^0-9]' data_file echo echo "creating image" diff --git a/tests/qemu-iotests/019 b/tests/qemu-iotests/019 index b4f5234609..813a84acac 100755 --- a/tests/qemu-iotests/019 +++ b/tests/qemu-iotests/019 @@ -30,9 +30,9 @@ status=1 # failure is the default! _cleanup() { - _cleanup_test_img - rm -f "$TEST_IMG.base" - rm -f "$TEST_IMG.orig" + _cleanup_test_img + _rm_test_img "$TEST_IMG.base" + _rm_test_img "$TEST_IMG.orig" } trap "_cleanup; exit \$status" 0 1 2 3 15 diff --git a/tests/qemu-iotests/020 b/tests/qemu-iotests/020 index f41b92f35f..20f8f185d0 100755 --- a/tests/qemu-iotests/020 +++ b/tests/qemu-iotests/020 @@ -28,9 +28,9 @@ status=1 # failure is the default! _cleanup() { - _cleanup_test_img - rm -f "$TEST_IMG.base" - rm -f "$TEST_IMG.orig" + _cleanup_test_img + _rm_test_img "$TEST_IMG.base" + _rm_test_img "$TEST_IMG.orig" } trap "_cleanup; exit \$status" 0 1 2 3 15 diff --git a/tests/qemu-iotests/024 b/tests/qemu-iotests/024 index 23298c6f59..e2e766241e 100755 --- a/tests/qemu-iotests/024 +++ b/tests/qemu-iotests/024 @@ -29,12 +29,12 @@ status=1 # failure is the default! _cleanup() { _cleanup_test_img - rm -f "$TEST_DIR/t.$IMGFMT.base_old" - rm -f "$TEST_DIR/t.$IMGFMT.base_new" + _rm_test_img "$TEST_DIR/t.$IMGFMT.base_old" + _rm_test_img "$TEST_DIR/t.$IMGFMT.base_new" - rm -f "$TEST_DIR/subdir/t.$IMGFMT" - rm -f "$TEST_DIR/subdir/t.$IMGFMT.base_old" - rm -f "$TEST_DIR/subdir/t.$IMGFMT.base_new" + _rm_test_img "$TEST_DIR/subdir/t.$IMGFMT" + _rm_test_img "$TEST_DIR/subdir/t.$IMGFMT.base_old" + _rm_test_img "$TEST_DIR/subdir/t.$IMGFMT.base_new" rmdir "$TEST_DIR/subdir" 2> /dev/null } trap "_cleanup; exit \$status" 0 1 2 3 15 diff --git a/tests/qemu-iotests/026 b/tests/qemu-iotests/026 index 3430029ed6..b9713eb591 100755 --- a/tests/qemu-iotests/026 +++ b/tests/qemu-iotests/026 @@ -30,6 +30,7 @@ _cleanup() { _cleanup_test_img rm "$TEST_DIR/blkdebug.conf" + rm -f "$TEST_IMG.data_file" } trap "_cleanup; exit \$status" 0 1 2 3 15 @@ -49,7 +50,10 @@ _supported_cache_modes writethrough none # 32 and 64 bits do not work either, however, due to different leaked cluster # count on error. # Thus, the only remaining option is refcount_bits=16. -_unsupported_imgopts 'refcount_bits=\([^1]\|.\([^6]\|$\)\)' +# +# As for data_file, none of the refcount tests can work for it. +_unsupported_imgopts 'refcount_bits=\([^1]\|.\([^6]\|$\)\)' \ + data_file echo "Errors while writing 128 kB" echo @@ -215,6 +219,27 @@ _make_test_img 64M $QEMU_IO -c "write 0 1M" -c "write 0 1M" "$BLKDBG_TEST_IMG" | _filter_qemu_io _check_test_img +echo +echo === Avoid freeing preallocated zero clusters on failure === +echo + +cat > "$TEST_DIR/blkdebug.conf" < Header extension: @@ -142,15 +142,15 @@ refcount_table_offset 0x10000 refcount_table_clusters 1 nb_snapshots 0 snapshot_offset 0x0 -incompatible_features 0x0 -compatible_features 0x0 -autoclear_features 0x0 +incompatible_features [] +compatible_features [] +autoclear_features [] refcount_order 4 header_length 104 Header extension: magic 0x6803f857 -length 192 +length 288 data Header extension: @@ -164,7 +164,7 @@ No errors were found on the image. magic 0x514649fb version 3 -backing_file_offset 0x178 +backing_file_offset 0x1d8 backing_file_size 0x17 cluster_bits 16 size 67108864 @@ -175,9 +175,9 @@ refcount_table_offset 0x10000 refcount_table_clusters 1 nb_snapshots 0 snapshot_offset 0x0 -incompatible_features 0x0 -compatible_features 0x0 -autoclear_features 0x0 +incompatible_features [] +compatible_features [] +autoclear_features [] refcount_order 4 header_length 104 @@ -188,7 +188,7 @@ data 'host_device' Header extension: magic 0x6803f857 -length 192 +length 288 data Header extension: diff --git a/tests/qemu-iotests/036 b/tests/qemu-iotests/036 index f06ff67408..cf522de7a1 100755 --- a/tests/qemu-iotests/036 +++ b/tests/qemu-iotests/036 @@ -43,9 +43,11 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 # This tests qcow2-specific low-level functionality _supported_fmt qcow2 _supported_proto file - -# Only qcow2v3 and later supports feature bits -IMGOPTS="compat=1.1" +# Only qcow2v3 and later supports feature bits; +# qcow2.py does not support external data files; +# this test requires a cluster size large enough for the feature table +_unsupported_imgopts 'compat=0.10' data_file \ + 'cluster_size=\(512\|1024\|2048\|4096\)' echo echo === Image with unknown incompatible feature bit === @@ -55,7 +57,8 @@ $PYTHON qcow2.py "$TEST_IMG" set-feature-bit incompatible 63 # Without feature table $PYTHON qcow2.py "$TEST_IMG" del-header-ext 0x6803f857 -$PYTHON qcow2.py "$TEST_IMG" dump-header +$PYTHON qcow2.py "$TEST_IMG" dump-header | grep features +$PYTHON qcow2.py "$TEST_IMG" dump-header-exts _img_info # With feature table containing bit 63 @@ -103,14 +106,16 @@ echo === Create image with unknown autoclear feature bit === echo _make_test_img 64M $PYTHON qcow2.py "$TEST_IMG" set-feature-bit autoclear 63 -$PYTHON qcow2.py "$TEST_IMG" dump-header +$PYTHON qcow2.py "$TEST_IMG" dump-header | grep features +$PYTHON qcow2.py "$TEST_IMG" dump-header-exts echo echo === Repair image === echo _check_test_img -r all -$PYTHON qcow2.py "$TEST_IMG" dump-header +$PYTHON qcow2.py "$TEST_IMG" dump-header | grep features +$PYTHON qcow2.py "$TEST_IMG" dump-header-exts # success, all done echo "*** done" diff --git a/tests/qemu-iotests/036.out b/tests/qemu-iotests/036.out index e489b44386..23b699ce06 100644 --- a/tests/qemu-iotests/036.out +++ b/tests/qemu-iotests/036.out @@ -3,25 +3,9 @@ QA output created by 036 === Image with unknown incompatible feature bit === Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 -magic 0x514649fb -version 3 -backing_file_offset 0x0 -backing_file_size 0x0 -cluster_bits 16 -size 67108864 -crypt_method 0 -l1_size 1 -l1_table_offset 0x30000 -refcount_table_offset 0x10000 -refcount_table_clusters 1 -nb_snapshots 0 -snapshot_offset 0x0 -incompatible_features 0x8000000000000000 -compatible_features 0x0 -autoclear_features 0x0 -refcount_order 4 -header_length 104 - +incompatible_features [63] +compatible_features [] +autoclear_features [] qemu-img: Could not open 'TEST_DIR/t.IMGFMT': Unsupported IMGFMT feature(s): Unknown incompatible feature: 8000000000000000 qemu-img: Could not open 'TEST_DIR/t.IMGFMT': Unsupported IMGFMT feature(s): Test feature @@ -37,56 +21,24 @@ qemu-img: Could not open 'TEST_DIR/t.IMGFMT': Unsupported IMGFMT feature(s): tes === Create image with unknown autoclear feature bit === Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 -magic 0x514649fb -version 3 -backing_file_offset 0x0 -backing_file_size 0x0 -cluster_bits 16 -size 67108864 -crypt_method 0 -l1_size 1 -l1_table_offset 0x30000 -refcount_table_offset 0x10000 -refcount_table_clusters 1 -nb_snapshots 0 -snapshot_offset 0x0 -incompatible_features 0x0 -compatible_features 0x0 -autoclear_features 0x8000000000000000 -refcount_order 4 -header_length 104 - +incompatible_features [] +compatible_features [] +autoclear_features [63] Header extension: magic 0x6803f857 -length 192 +length 288 data === Repair image === No errors were found on the image. -magic 0x514649fb -version 3 -backing_file_offset 0x0 -backing_file_size 0x0 -cluster_bits 16 -size 67108864 -crypt_method 0 -l1_size 1 -l1_table_offset 0x30000 -refcount_table_offset 0x10000 -refcount_table_clusters 1 -nb_snapshots 0 -snapshot_offset 0x0 -incompatible_features 0x0 -compatible_features 0x0 -autoclear_features 0x0 -refcount_order 4 -header_length 104 - +incompatible_features [] +compatible_features [] +autoclear_features [] Header extension: magic 0x6803f857 -length 192 +length 288 data *** done diff --git a/tests/qemu-iotests/039 b/tests/qemu-iotests/039 index 325da63a4c..ddce48ab47 100755 --- a/tests/qemu-iotests/039 +++ b/tests/qemu-iotests/039 @@ -44,14 +44,16 @@ _supported_proto file _supported_os Linux _default_cache_mode writethrough _supported_cache_modes writethrough +# Some of these test cases expect no external data file so that all +# clusters are part of the qcow2 image and refcounted +_unsupported_imgopts data_file size=128M echo echo "== Checking that image is clean on shutdown ==" -IMGOPTS="compat=1.1,lazy_refcounts=on" -_make_test_img $size +_make_test_img -o "compat=1.1,lazy_refcounts=on" $size $QEMU_IO -c "write -P 0x5a 0 512" "$TEST_IMG" | _filter_qemu_io @@ -62,8 +64,7 @@ _check_test_img echo echo "== Creating a dirty image file ==" -IMGOPTS="compat=1.1,lazy_refcounts=on" -_make_test_img $size +_make_test_img -o "compat=1.1,lazy_refcounts=on" $size _NO_VALGRIND \ $QEMU_IO -c "write -P 0x5a 0 512" \ @@ -98,8 +99,7 @@ $QEMU_IO -c "read -P 0x5a 0 512" "$TEST_IMG" | _filter_qemu_io echo echo "== Opening a dirty image read/write should repair it ==" -IMGOPTS="compat=1.1,lazy_refcounts=on" -_make_test_img $size +_make_test_img -o "compat=1.1,lazy_refcounts=on" $size _NO_VALGRIND \ $QEMU_IO -c "write -P 0x5a 0 512" \ @@ -117,8 +117,7 @@ $PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features echo echo "== Creating an image file with lazy_refcounts=off ==" -IMGOPTS="compat=1.1,lazy_refcounts=off" -_make_test_img $size +_make_test_img -o "compat=1.1,lazy_refcounts=off" $size _NO_VALGRIND \ $QEMU_IO -c "write -P 0x5a 0 512" \ @@ -132,11 +131,9 @@ _check_test_img echo echo "== Committing to a backing file with lazy_refcounts=on ==" -IMGOPTS="compat=1.1,lazy_refcounts=on" -TEST_IMG="$TEST_IMG".base _make_test_img $size +TEST_IMG="$TEST_IMG".base _make_test_img -o "compat=1.1,lazy_refcounts=on" $size -IMGOPTS="compat=1.1,lazy_refcounts=on,backing_file=$TEST_IMG.base" -_make_test_img $size +_make_test_img -o "compat=1.1,lazy_refcounts=on,backing_file=$TEST_IMG.base" $size $QEMU_IO -c "write 0 512" "$TEST_IMG" | _filter_qemu_io $QEMU_IMG commit "$TEST_IMG" @@ -151,8 +148,7 @@ TEST_IMG="$TEST_IMG".base _check_test_img echo echo "== Changing lazy_refcounts setting at runtime ==" -IMGOPTS="compat=1.1,lazy_refcounts=off" -_make_test_img $size +_make_test_img -o "compat=1.1,lazy_refcounts=off" $size _NO_VALGRIND \ $QEMU_IO -c "reopen -o lazy-refcounts=on" \ @@ -164,8 +160,7 @@ $QEMU_IO -c "reopen -o lazy-refcounts=on" \ $PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features _check_test_img -IMGOPTS="compat=1.1,lazy_refcounts=on" -_make_test_img $size +_make_test_img -o "compat=1.1,lazy_refcounts=on" $size _NO_VALGRIND \ $QEMU_IO -c "reopen -o lazy-refcounts=off" \ diff --git a/tests/qemu-iotests/039.out b/tests/qemu-iotests/039.out index 2e356d51b6..bdafa3ace3 100644 --- a/tests/qemu-iotests/039.out +++ b/tests/qemu-iotests/039.out @@ -4,7 +4,7 @@ QA output created by 039 Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 wrote 512/512 bytes at offset 0 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -incompatible_features 0x0 +incompatible_features [] No errors were found on the image. == Creating a dirty image file == @@ -12,7 +12,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 wrote 512/512 bytes at offset 0 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) ./common.rc: Killed ( VALGRIND_QEMU="${VALGRIND_QEMU_IO}" _qemu_proc_exec "${VALGRIND_LOGFILE}" "$QEMU_IO_PROG" $QEMU_IO_ARGS "$@" ) -incompatible_features 0x1 +incompatible_features [0] ERROR cluster 5 refcount=0 reference=1 ERROR OFLAG_COPIED data cluster: l2_entry=8000000000050000 refcount=0 @@ -22,7 +22,7 @@ Data may be corrupted, or further writes to the image may corrupt it. == Read-only access must still work == read 512/512 bytes at offset 0 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -incompatible_features 0x1 +incompatible_features [0] == Repairing the image file must succeed == ERROR cluster 5 refcount=0 reference=1 @@ -36,7 +36,7 @@ The following inconsistencies were found and repaired: Double checking the fixed image now... No errors were found on the image. -incompatible_features 0x0 +incompatible_features [] == Data should still be accessible after repair == read 512/512 bytes at offset 0 @@ -47,21 +47,21 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 wrote 512/512 bytes at offset 0 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) ./common.rc: Killed ( VALGRIND_QEMU="${VALGRIND_QEMU_IO}" _qemu_proc_exec "${VALGRIND_LOGFILE}" "$QEMU_IO_PROG" $QEMU_IO_ARGS "$@" ) -incompatible_features 0x1 +incompatible_features [0] ERROR cluster 5 refcount=0 reference=1 Rebuilding refcount structure Repairing cluster 1 refcount=1 reference=0 Repairing cluster 2 refcount=1 reference=0 wrote 512/512 bytes at offset 0 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -incompatible_features 0x0 +incompatible_features [] == Creating an image file with lazy_refcounts=off == Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 wrote 512/512 bytes at offset 0 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) ./common.rc: Killed ( VALGRIND_QEMU="${VALGRIND_QEMU_IO}" _qemu_proc_exec "${VALGRIND_LOGFILE}" "$QEMU_IO_PROG" $QEMU_IO_ARGS "$@" ) -incompatible_features 0x0 +incompatible_features [] No errors were found on the image. == Committing to a backing file with lazy_refcounts=on == @@ -70,8 +70,8 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/ wrote 512/512 bytes at offset 0 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) Image committed. -incompatible_features 0x0 -incompatible_features 0x0 +incompatible_features [] +incompatible_features [] No errors were found on the image. No errors were found on the image. @@ -80,7 +80,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 wrote 512/512 bytes at offset 0 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) ./common.rc: Killed ( VALGRIND_QEMU="${VALGRIND_QEMU_IO}" _qemu_proc_exec "${VALGRIND_LOGFILE}" "$QEMU_IO_PROG" $QEMU_IO_ARGS "$@" ) -incompatible_features 0x1 +incompatible_features [0] ERROR cluster 5 refcount=0 reference=1 ERROR OFLAG_COPIED data cluster: l2_entry=8000000000050000 refcount=0 @@ -90,6 +90,6 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 wrote 512/512 bytes at offset 0 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) ./common.rc: Killed ( VALGRIND_QEMU="${VALGRIND_QEMU_IO}" _qemu_proc_exec "${VALGRIND_LOGFILE}" "$QEMU_IO_PROG" $QEMU_IO_ARGS "$@" ) -incompatible_features 0x0 +incompatible_features [] No errors were found on the image. *** done diff --git a/tests/qemu-iotests/040 b/tests/qemu-iotests/040 index 762ad1ebcb..32c82b4ec6 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. # @@ -106,6 +106,7 @@ class TestSingleDrive(ImageCommitTestCase): self.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xab 0 524288', backing_img).find("verification failed")) self.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xef 524288 524288', backing_img).find("verification failed")) + @iotests.skip_if_unsupported(['throttle']) def test_commit_with_filter_and_quit(self): result = self.vm.qmp('object-add', qom_type='throttle-group', id='tg') self.assert_qmp(result, 'return', {}) @@ -125,6 +126,7 @@ class TestSingleDrive(ImageCommitTestCase): self.has_quit = True # Same as above, but this time we add the filter after starting the job + @iotests.skip_if_unsupported(['throttle']) def test_commit_plus_filter_and_quit(self): result = self.vm.qmp('object-add', qom_type='throttle-group', id='tg') self.assert_qmp(result, 'return', {}) @@ -428,6 +430,289 @@ class TestReopenOverlay(ImageCommitTestCase): def test_reopen_overlay(self): self.run_commit_test(self.img1, self.img0) +class TestErrorHandling(iotests.QMPTestCase): + image_len = 2 * 1024 * 1024 + + def setUp(self): + iotests.create_image(backing_img, self.image_len) + qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, mid_img) + qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % mid_img, test_img) + + qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0x11 0 512k', mid_img) + qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0x22 0 512k', test_img) + + self.vm = iotests.VM() + self.vm.launch() + + self.blkdebug_file = iotests.file_path("blkdebug.conf") + + def tearDown(self): + self.vm.shutdown() + os.remove(test_img) + os.remove(mid_img) + os.remove(backing_img) + + def blockdev_add(self, **kwargs): + result = self.vm.qmp('blockdev-add', **kwargs) + self.assert_qmp(result, 'return', {}) + + def add_block_nodes(self, base_debug=None, mid_debug=None, top_debug=None): + self.blockdev_add(node_name='base-file', driver='file', + filename=backing_img) + self.blockdev_add(node_name='mid-file', driver='file', + filename=mid_img) + self.blockdev_add(node_name='top-file', driver='file', + filename=test_img) + + if base_debug: + self.blockdev_add(node_name='base-dbg', driver='blkdebug', + image='base-file', inject_error=base_debug) + if mid_debug: + self.blockdev_add(node_name='mid-dbg', driver='blkdebug', + image='mid-file', inject_error=mid_debug) + if top_debug: + self.blockdev_add(node_name='top-dbg', driver='blkdebug', + image='top-file', inject_error=top_debug) + + self.blockdev_add(node_name='base-fmt', driver='raw', + file=('base-dbg' if base_debug else 'base-file')) + self.blockdev_add(node_name='mid-fmt', driver=iotests.imgfmt, + file=('mid-dbg' if mid_debug else 'mid-file'), + backing='base-fmt') + self.blockdev_add(node_name='top-fmt', driver=iotests.imgfmt, + file=('top-dbg' if top_debug else 'top-file'), + backing='mid-fmt') + + def run_job(self, expected_events, error_pauses_job=False): + match_device = {'data': {'device': 'job0'}} + events = [ + ('BLOCK_JOB_COMPLETED', match_device), + ('BLOCK_JOB_CANCELLED', match_device), + ('BLOCK_JOB_ERROR', match_device), + ('BLOCK_JOB_READY', match_device), + ] + + completed = False + log = [] + while not completed: + ev = self.vm.events_wait(events, timeout=5.0) + if ev['event'] == 'BLOCK_JOB_COMPLETED': + completed = True + elif ev['event'] == 'BLOCK_JOB_ERROR': + if error_pauses_job: + result = self.vm.qmp('block-job-resume', device='job0') + self.assert_qmp(result, 'return', {}) + elif ev['event'] == 'BLOCK_JOB_READY': + result = self.vm.qmp('block-job-complete', device='job0') + self.assert_qmp(result, 'return', {}) + else: + self.fail("Unexpected event: %s" % ev) + log.append(iotests.filter_qmp_event(ev)) + + self.maxDiff = None + self.assertEqual(expected_events, log) + + def event_error(self, op, action): + return { + 'event': 'BLOCK_JOB_ERROR', + 'data': {'action': action, 'device': 'job0', 'operation': op}, + 'timestamp': {'microseconds': 'USECS', 'seconds': 'SECS'} + } + + def event_ready(self): + return { + 'event': 'BLOCK_JOB_READY', + 'data': {'device': 'job0', + 'len': 524288, + 'offset': 524288, + 'speed': 0, + 'type': 'commit'}, + 'timestamp': {'microseconds': 'USECS', 'seconds': 'SECS'}, + } + + def event_completed(self, errmsg=None, active=True): + max_len = 524288 if active else self.image_len + data = { + 'device': 'job0', + 'len': max_len, + 'offset': 0 if errmsg else max_len, + 'speed': 0, + 'type': 'commit' + } + if errmsg: + data['error'] = errmsg + + return { + 'event': 'BLOCK_JOB_COMPLETED', + 'data': data, + 'timestamp': {'microseconds': 'USECS', 'seconds': 'SECS'}, + } + + def blkdebug_event(self, event, is_raw=False): + if event: + return [{ + 'event': event, + 'sector': 512 if is_raw else 1024, + 'once': True, + }] + return None + + def prepare_and_start_job(self, on_error, active=True, + top_event=None, mid_event=None, base_event=None): + + top_debug = self.blkdebug_event(top_event) + mid_debug = self.blkdebug_event(mid_event) + base_debug = self.blkdebug_event(base_event, True) + + self.add_block_nodes(top_debug=top_debug, mid_debug=mid_debug, + base_debug=base_debug) + + result = self.vm.qmp('block-commit', job_id='job0', device='top-fmt', + top_node='top-fmt' if active else 'mid-fmt', + base_node='mid-fmt' if active else 'base-fmt', + on_error=on_error) + self.assert_qmp(result, 'return', {}) + + def testActiveReadErrorReport(self): + self.prepare_and_start_job('report', top_event='read_aio') + self.run_job([ + self.event_error('read', 'report'), + self.event_completed('Input/output error') + ]) + + self.vm.shutdown() + self.assertFalse(iotests.compare_images(test_img, mid_img), + 'target image matches source after error') + + def testActiveReadErrorStop(self): + self.prepare_and_start_job('stop', top_event='read_aio') + self.run_job([ + self.event_error('read', 'stop'), + self.event_ready(), + self.event_completed() + ], error_pauses_job=True) + + self.vm.shutdown() + self.assertTrue(iotests.compare_images(test_img, mid_img), + 'target image does not match source after commit') + + def testActiveReadErrorIgnore(self): + self.prepare_and_start_job('ignore', top_event='read_aio') + self.run_job([ + self.event_error('read', 'ignore'), + self.event_ready(), + self.event_completed() + ]) + + # For commit, 'ignore' actually means retry, so this will succeed + self.vm.shutdown() + self.assertTrue(iotests.compare_images(test_img, mid_img), + 'target image does not match source after commit') + + def testActiveWriteErrorReport(self): + self.prepare_and_start_job('report', mid_event='write_aio') + self.run_job([ + self.event_error('write', 'report'), + self.event_completed('Input/output error') + ]) + + self.vm.shutdown() + self.assertFalse(iotests.compare_images(test_img, mid_img), + 'target image matches source after error') + + def testActiveWriteErrorStop(self): + self.prepare_and_start_job('stop', mid_event='write_aio') + self.run_job([ + self.event_error('write', 'stop'), + self.event_ready(), + self.event_completed() + ], error_pauses_job=True) + + self.vm.shutdown() + self.assertTrue(iotests.compare_images(test_img, mid_img), + 'target image does not match source after commit') + + def testActiveWriteErrorIgnore(self): + self.prepare_and_start_job('ignore', mid_event='write_aio') + self.run_job([ + self.event_error('write', 'ignore'), + self.event_ready(), + self.event_completed() + ]) + + # For commit, 'ignore' actually means retry, so this will succeed + self.vm.shutdown() + self.assertTrue(iotests.compare_images(test_img, mid_img), + 'target image does not match source after commit') + + def testIntermediateReadErrorReport(self): + self.prepare_and_start_job('report', active=False, mid_event='read_aio') + self.run_job([ + self.event_error('read', 'report'), + self.event_completed('Input/output error', active=False) + ]) + + self.vm.shutdown() + self.assertFalse(iotests.compare_images(mid_img, backing_img, fmt2='raw'), + 'target image matches source after error') + + def testIntermediateReadErrorStop(self): + self.prepare_and_start_job('stop', active=False, mid_event='read_aio') + self.run_job([ + self.event_error('read', 'stop'), + self.event_completed(active=False) + ], error_pauses_job=True) + + self.vm.shutdown() + self.assertTrue(iotests.compare_images(mid_img, backing_img, fmt2='raw'), + 'target image does not match source after commit') + + def testIntermediateReadErrorIgnore(self): + self.prepare_and_start_job('ignore', active=False, mid_event='read_aio') + self.run_job([ + self.event_error('read', 'ignore'), + self.event_completed(active=False) + ]) + + # For commit, 'ignore' actually means retry, so this will succeed + self.vm.shutdown() + self.assertTrue(iotests.compare_images(mid_img, backing_img, fmt2='raw'), + 'target image does not match source after commit') + + def testIntermediateWriteErrorReport(self): + self.prepare_and_start_job('report', active=False, base_event='write_aio') + self.run_job([ + self.event_error('write', 'report'), + self.event_completed('Input/output error', active=False) + ]) + + self.vm.shutdown() + self.assertFalse(iotests.compare_images(mid_img, backing_img, fmt2='raw'), + 'target image matches source after error') + + def testIntermediateWriteErrorStop(self): + self.prepare_and_start_job('stop', active=False, base_event='write_aio') + self.run_job([ + self.event_error('write', 'stop'), + self.event_completed(active=False) + ], error_pauses_job=True) + + self.vm.shutdown() + self.assertTrue(iotests.compare_images(mid_img, backing_img, fmt2='raw'), + 'target image does not match source after commit') + + def testIntermediateWriteErrorIgnore(self): + self.prepare_and_start_job('ignore', active=False, base_event='write_aio') + self.run_job([ + self.event_error('write', 'ignore'), + self.event_completed(active=False) + ]) + + # For commit, 'ignore' actually means retry, so this will succeed + self.vm.shutdown() + self.assertTrue(iotests.compare_images(mid_img, backing_img, fmt2='raw'), + 'target image does not match source after commit') + if __name__ == '__main__': iotests.main(supported_fmts=['qcow2', 'qed'], supported_protocols=['file']) diff --git a/tests/qemu-iotests/040.out b/tests/qemu-iotests/040.out index 220a5fa82c..6a917130b6 100644 --- a/tests/qemu-iotests/040.out +++ b/tests/qemu-iotests/040.out @@ -1,5 +1,5 @@ -............................................... +........................................................... ---------------------------------------------------------------------- -Ran 47 tests +Ran 59 tests OK diff --git a/tests/qemu-iotests/041 b/tests/qemu-iotests/041 index 8568426311..5d67bf14bf 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. # @@ -20,6 +20,7 @@ import time import os +import re import iotests from iotests import qemu_img, qemu_io @@ -34,6 +35,8 @@ quorum_img3 = os.path.join(iotests.test_dir, 'quorum3.img') quorum_repair_img = os.path.join(iotests.test_dir, 'quorum_repair.img') quorum_snapshot_file = os.path.join(iotests.test_dir, 'quorum_snapshot.img') +nbd_sock_path = os.path.join(iotests.test_dir, 'nbd.sock') + class TestSingleDrive(iotests.QMPTestCase): image_len = 1 * 1024 * 1024 # MB qmp_cmd = 'drive-mirror' @@ -80,7 +83,6 @@ class TestSingleDrive(iotests.QMPTestCase): self.cancel_and_wait(force=True) result = self.vm.qmp('query-block') self.assert_qmp(result, 'return[0]/inserted/file', test_img) - self.vm.shutdown() def test_cancel_after_ready(self): self.assert_no_active_block_jobs() @@ -201,8 +203,6 @@ class TestSingleDrive(iotests.QMPTestCase): self.assert_qmp(result, 'return[0]/node-name', 'top') self.assert_qmp(result, 'return[0]/backing/node-name', 'base') - self.vm.shutdown() - def test_medium_not_found(self): if iotests.qemu_default_machine != 'pc': return @@ -455,7 +455,6 @@ new_state = "1" self.assert_qmp(event, 'data/id', 'drive0') self.assert_no_active_block_jobs() - self.vm.shutdown() def test_ignore_read(self): self.assert_no_active_block_jobs() @@ -475,7 +474,6 @@ new_state = "1" result = self.vm.qmp('query-block-jobs') self.assert_qmp(result, 'return[0]/paused', False) self.complete_and_wait() - self.vm.shutdown() def test_large_cluster(self): self.assert_no_active_block_jobs() @@ -540,7 +538,6 @@ new_state = "1" self.complete_and_wait(wait_ready=False) self.assert_no_active_block_jobs() - self.vm.shutdown() class TestWriteErrors(iotests.QMPTestCase): image_len = 2 * 1024 * 1024 # MB @@ -614,7 +611,6 @@ new_state = "1" completed = True self.assert_no_active_block_jobs() - self.vm.shutdown() def test_ignore_write(self): self.assert_no_active_block_jobs() @@ -631,7 +627,6 @@ new_state = "1" result = self.vm.qmp('query-block-jobs') self.assert_qmp(result, 'return[0]/paused', False) self.complete_and_wait() - self.vm.shutdown() def test_stop_write(self): self.assert_no_active_block_jobs() @@ -667,7 +662,6 @@ new_state = "1" self.complete_and_wait(wait_ready=False) self.assert_no_active_block_jobs() - self.vm.shutdown() class TestSetSpeed(iotests.QMPTestCase): image_len = 80 * 1024 * 1024 # MB @@ -871,6 +865,7 @@ class TestRepairQuorum(iotests.QMPTestCase): image_len = 1 * 1024 * 1024 # MB IMAGES = [ quorum_img1, quorum_img2, quorum_img3 ] + @iotests.skip_if_unsupported(['quorum']) def setUp(self): self.vm = iotests.VM() @@ -880,25 +875,28 @@ class TestRepairQuorum(iotests.QMPTestCase): # Add each individual quorum images for i in self.IMAGES: qemu_img('create', '-f', iotests.imgfmt, i, - str(TestSingleDrive.image_len)) + str(self.image_len)) # Assign a node name to each quorum image in order to manipulate # them opts = "node-name=img%i" % self.IMAGES.index(i) - self.vm = self.vm.add_drive(i, opts) + opts += ',driver=%s' % iotests.imgfmt + opts += ',file.driver=file' + opts += ',file.filename=%s' % i + self.vm = self.vm.add_blockdev(opts) self.vm.launch() #assemble the quorum block device from the individual files args = { "driver": "quorum", "node-name": "quorum0", "vote-threshold": 2, "children": [ "img0", "img1", "img2" ] } - if iotests.supports_quorum(): - result = self.vm.qmp("blockdev-add", **args) - self.assert_qmp(result, 'return', {}) + result = self.vm.qmp("blockdev-add", **args) + self.assert_qmp(result, 'return', {}) def tearDown(self): self.vm.shutdown() - for i in self.IMAGES + [ quorum_repair_img, quorum_snapshot_file ]: + for i in self.IMAGES + [ quorum_repair_img, quorum_snapshot_file, + nbd_sock_path ]: # Do a try/except because the test may have deleted some images try: os.remove(i) @@ -906,9 +904,6 @@ class TestRepairQuorum(iotests.QMPTestCase): pass def test_complete(self): - if not iotests.supports_quorum(): - return - self.assert_no_active_block_jobs() result = self.vm.qmp('drive-mirror', job_id='job0', device='quorum0', @@ -918,16 +913,12 @@ class TestRepairQuorum(iotests.QMPTestCase): self.complete_and_wait(drive="job0") self.assert_has_block_node("repair0", quorum_repair_img) - # TODO: a better test requiring some QEMU infrastructure will be added - # to check that this file is really driven by quorum + self.vm.assert_block_path('quorum0', '/children.1', 'repair0') self.vm.shutdown() self.assertTrue(iotests.compare_images(quorum_img2, quorum_repair_img), 'target image does not match source after mirroring') def test_cancel(self): - if not iotests.supports_quorum(): - return - self.assert_no_active_block_jobs() result = self.vm.qmp('drive-mirror', job_id='job0', device='quorum0', @@ -939,12 +930,8 @@ class TestRepairQuorum(iotests.QMPTestCase): # here we check that the last registered quorum file has not been # swapped out and unref self.assert_has_block_node(None, quorum_img3) - self.vm.shutdown() def test_cancel_after_ready(self): - if not iotests.supports_quorum(): - return - self.assert_no_active_block_jobs() result = self.vm.qmp('drive-mirror', job_id='job0', device='quorum0', @@ -961,9 +948,6 @@ class TestRepairQuorum(iotests.QMPTestCase): 'target image does not match source after mirroring') def test_pause(self): - if not iotests.supports_quorum(): - return - self.assert_no_active_block_jobs() result = self.vm.qmp('drive-mirror', job_id='job0', device='quorum0', @@ -989,9 +973,6 @@ class TestRepairQuorum(iotests.QMPTestCase): 'target image does not match source after mirroring') def test_medium_not_found(self): - if not iotests.supports_quorum(): - return - if iotests.qemu_default_machine != 'pc': return @@ -1003,9 +984,6 @@ class TestRepairQuorum(iotests.QMPTestCase): self.assert_qmp(result, 'error/class', 'GenericError') def test_image_not_found(self): - if not iotests.supports_quorum(): - return - result = self.vm.qmp('drive-mirror', job_id='job0', device='quorum0', sync='full', node_name='repair0', replaces='img1', mode='existing', target=quorum_repair_img, @@ -1013,9 +991,6 @@ class TestRepairQuorum(iotests.QMPTestCase): self.assert_qmp(result, 'error/class', 'GenericError') def test_device_not_found(self): - if not iotests.supports_quorum(): - return - result = self.vm.qmp('drive-mirror', job_id='job0', device='nonexistent', sync='full', node_name='repair0', @@ -1024,9 +999,6 @@ class TestRepairQuorum(iotests.QMPTestCase): self.assert_qmp(result, 'error/class', 'GenericError') def test_wrong_sync_mode(self): - if not iotests.supports_quorum(): - return - result = self.vm.qmp('drive-mirror', device='quorum0', job_id='job0', node_name='repair0', replaces='img1', @@ -1034,27 +1006,18 @@ class TestRepairQuorum(iotests.QMPTestCase): self.assert_qmp(result, 'error/class', 'GenericError') def test_no_node_name(self): - if not iotests.supports_quorum(): - return - result = self.vm.qmp('drive-mirror', job_id='job0', device='quorum0', sync='full', replaces='img1', target=quorum_repair_img, format=iotests.imgfmt) self.assert_qmp(result, 'error/class', 'GenericError') def test_nonexistent_replaces(self): - if not iotests.supports_quorum(): - return - result = self.vm.qmp('drive-mirror', job_id='job0', device='quorum0', sync='full', node_name='repair0', replaces='img77', target=quorum_repair_img, format=iotests.imgfmt) self.assert_qmp(result, 'error/class', 'GenericError') def test_after_a_quorum_snapshot(self): - if not iotests.supports_quorum(): - return - result = self.vm.qmp('blockdev-snapshot-sync', node_name='img1', snapshot_file=quorum_snapshot_file, snapshot_node_name="snap1"); @@ -1071,9 +1034,71 @@ class TestRepairQuorum(iotests.QMPTestCase): self.complete_and_wait('job0') self.assert_has_block_node("repair0", quorum_repair_img) - # TODO: a better test requiring some QEMU infrastructure will be added - # to check that this file is really driven by quorum + self.vm.assert_block_path('quorum0', '/children.1', 'repair0') + + def test_with_other_parent(self): + """ + Check that we cannot replace a Quorum child when it has other + parents. + """ + result = self.vm.qmp('nbd-server-start', + addr={ + 'type': 'unix', + 'data': {'path': nbd_sock_path} + }) + self.assert_qmp(result, 'return', {}) + + result = self.vm.qmp('nbd-server-add', device='img1') + self.assert_qmp(result, 'return', {}) + + result = self.vm.qmp('drive-mirror', job_id='mirror', device='quorum0', + sync='full', node_name='repair0', replaces='img1', + target=quorum_repair_img, format=iotests.imgfmt) + self.assert_qmp(result, 'error/desc', + "Cannot replace 'img1' by a node mirrored from " + "'quorum0', because it cannot be guaranteed that doing " + "so would not lead to an abrupt change of visible data") + + def test_with_other_parents_after_mirror_start(self): + """ + The same as test_with_other_parent(), but add the NBD server + only when the mirror job is already running. + """ + result = self.vm.qmp('nbd-server-start', + addr={ + 'type': 'unix', + 'data': {'path': nbd_sock_path} + }) + self.assert_qmp(result, 'return', {}) + + result = self.vm.qmp('drive-mirror', job_id='mirror', device='quorum0', + sync='full', node_name='repair0', replaces='img1', + target=quorum_repair_img, format=iotests.imgfmt) + self.assert_qmp(result, 'return', {}) + + result = self.vm.qmp('nbd-server-add', device='img1') + self.assert_qmp(result, 'return', {}) + + # The full error message goes to stderr, we will check it later + self.complete_and_wait('mirror', + completion_error='Operation not permitted') + + # Should not have been replaced + self.vm.assert_block_path('quorum0', '/children.1', 'img1') + + # Check the full error message now self.vm.shutdown() + log = self.vm.get_log() + log = re.sub(r'^\[I \d+\.\d+\] OPENED\n', '', log) + log = re.sub(r'^Formatting.*\n', '', log) + log = re.sub(r'\n\[I \+\d+\.\d+\] CLOSED\n?$', '', log) + log = re.sub(r'^%s: ' % os.path.basename(iotests.qemu_prog), '', log) + + self.assertEqual(log, + "Can no longer replace 'img1' by 'repair0', because " + + "it can no longer be guaranteed that doing so would " + + "not lead to an abrupt change of visible data") + # Test mirroring with a source that does not have any parents (not even a # BlockBackend) @@ -1121,6 +1146,97 @@ class TestOrphanedSource(iotests.QMPTestCase): target='dest-ro') self.assert_qmp(result, 'error/class', 'GenericError') + def test_failing_permission_in_complete(self): + self.assert_no_active_block_jobs() + + # Unshare consistent-read on the target + # (The mirror job does not care) + result = self.vm.qmp('blockdev-add', + driver='blkdebug', + node_name='dest-perm', + image='dest', + unshare_child_perms=['consistent-read']) + self.assert_qmp(result, 'return', {}) + + result = self.vm.qmp('blockdev-mirror', job_id='job', device='src', + sync='full', target='dest', + filter_node_name='mirror-filter') + self.assert_qmp(result, 'return', {}) + + # Require consistent-read on the source + # (We can only add this node once the job has started, or it + # will complain that it does not want to run on non-root nodes) + result = self.vm.qmp('blockdev-add', + driver='blkdebug', + node_name='src-perm', + image='src', + take_child_perms=['consistent-read']) + self.assert_qmp(result, 'return', {}) + + # While completing, mirror will attempt to replace src by + # dest, which must fail because src-perm requires + # consistent-read but dest-perm does not share it; thus + # aborting the job when it is supposed to complete + self.complete_and_wait('job', + completion_error='Operation not permitted') + + # Assert that all of our nodes are still there (except for the + # mirror filter, which should be gone despite the failure) + nodes = self.vm.qmp('query-named-block-nodes')['return'] + nodes = [node['node-name'] for node in nodes] + + for expect in ('src', 'src-perm', 'dest', 'dest-perm'): + self.assertTrue(expect in nodes, '%s disappeared' % expect) + self.assertFalse('mirror-filter' in nodes, + 'Mirror filter node did not disappear') + +# Test cases for @replaces that do not necessarily involve Quorum +class TestReplaces(iotests.QMPTestCase): + # Each of these test cases needs their own block graph, so do not + # create any nodes here + def setUp(self): + self.vm = iotests.VM() + self.vm.launch() + + def tearDown(self): + self.vm.shutdown() + for img in (test_img, target_img): + try: + os.remove(img) + except OSError: + pass + + @iotests.skip_if_unsupported(['copy-on-read']) + def test_replace_filter(self): + """ + Check that we can replace filter nodes. + """ + result = self.vm.qmp('blockdev-add', **{ + 'driver': 'copy-on-read', + 'node-name': 'filter0', + 'file': { + 'driver': 'copy-on-read', + 'node-name': 'filter1', + 'file': { + 'driver': 'null-co' + } + } + }) + self.assert_qmp(result, 'return', {}) + + result = self.vm.qmp('blockdev-add', + node_name='target', driver='null-co') + self.assert_qmp(result, 'return', {}) + + result = self.vm.qmp('blockdev-mirror', job_id='mirror', device='filter0', + target='target', sync='full', replaces='filter1') + self.assert_qmp(result, 'return', {}) + + self.complete_and_wait('mirror') + + self.vm.assert_block_path('filter0', '/file', 'target') + 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/041.out b/tests/qemu-iotests/041.out index 2c448b4239..877b76fd31 100644 --- a/tests/qemu-iotests/041.out +++ b/tests/qemu-iotests/041.out @@ -1,5 +1,5 @@ -.......................................................................................... +.............................................................................................. ---------------------------------------------------------------------- -Ran 90 tests +Ran 94 tests OK diff --git a/tests/qemu-iotests/043 b/tests/qemu-iotests/043 index 67cc7e74c2..b102e49208 100755 --- a/tests/qemu-iotests/043 +++ b/tests/qemu-iotests/043 @@ -29,7 +29,9 @@ status=1 # failure is the default! _cleanup() { _cleanup_test_img - rm -f "$TEST_IMG".[123].base + for img in "$TEST_IMG".[123].base; do + _rm_test_img "$img" + done } trap "_cleanup; exit \$status" 0 1 2 3 15 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/046 b/tests/qemu-iotests/046 index 4e03ead7b1..ecbe5fc0f4 100755 --- a/tests/qemu-iotests/046 +++ b/tests/qemu-iotests/046 @@ -38,6 +38,8 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 _supported_fmt qcow2 _supported_proto file +# data_file does not support compressed clusters +_unsupported_imgopts data_file CLUSTER_SIZE=64k size=128M @@ -191,8 +193,8 @@ echo "== Verify image content ==" verify_io() { if ($QEMU_IMG info -U -f "$IMGFMT" "$TEST_IMG" | grep "compat: 0.10" > /dev/null); then - # For v2 images, discarded clusters are read from the backing file - # Keep the variable empty so that the backing file value can be used as + # In v2 images clusters are not discarded when there is a backing file. + # Keep the variable empty so that the previous value can be used as # the default below discarded= else @@ -228,14 +230,16 @@ verify_io() echo read -P 70 0x78000 0x6000 echo read -P 7 0x7e000 0x2000 - echo read -P ${discarded:-8} 0x80000 0x6000 + echo read -P ${discarded:-89} 0x80000 0x1000 + echo read -P ${discarded:-8} 0x81000 0x5000 echo read -P 80 0x86000 0x2000 echo read -P ${discarded:-8} 0x88000 0x2000 echo read -P 81 0x8a000 0xe000 echo read -P 90 0x98000 0x6000 echo read -P 9 0x9e000 0x2000 - echo read -P ${discarded:-10} 0xa0000 0x6000 + echo read -P ${discarded:-109} 0xa0000 0x1000 + echo read -P ${discarded:-10} 0xa1000 0x5000 echo read -P 100 0xa6000 0x2000 echo read -P ${discarded:-10} 0xa8000 0x2000 echo read -P 101 0xaa000 0xe000 diff --git a/tests/qemu-iotests/046.out b/tests/qemu-iotests/046.out index ca2c7404a9..70783041e2 100644 --- a/tests/qemu-iotests/046.out +++ b/tests/qemu-iotests/046.out @@ -187,8 +187,10 @@ read 24576/24576 bytes at offset 491520 24 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) read 8192/8192 bytes at offset 516096 8 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -read 24576/24576 bytes at offset 524288 -24 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 4096/4096 bytes at offset 524288 +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 20480/20480 bytes at offset 528384 +20 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) read 8192/8192 bytes at offset 548864 8 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) read 8192/8192 bytes at offset 557056 @@ -199,8 +201,10 @@ read 24576/24576 bytes at offset 622592 24 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) read 8192/8192 bytes at offset 647168 8 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -read 24576/24576 bytes at offset 655360 -24 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 4096/4096 bytes at offset 655360 +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 20480/20480 bytes at offset 659456 +20 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) read 8192/8192 bytes at offset 679936 8 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) read 8192/8192 bytes at offset 688128 diff --git a/tests/qemu-iotests/048 b/tests/qemu-iotests/048 index bde408ca92..2af6b74b41 100755 --- a/tests/qemu-iotests/048 +++ b/tests/qemu-iotests/048 @@ -31,7 +31,7 @@ _cleanup() { echo "Cleanup" _cleanup_test_img - rm "${TEST_IMG_FILE2}" + _rm_test_img "${TEST_IMG_FILE2}" } trap "_cleanup; exit \$status" 0 1 2 3 15 @@ -49,6 +49,8 @@ _compare() _supported_fmt raw qcow2 qed luks _supported_proto file _supported_os Linux +# Using 'cp' is incompatible with external data files +_unsupported_imgopts data_file # Remove once all tests are fixed to use TEST_IMG_FILE # correctly and common.rc sets it unconditionally diff --git a/tests/qemu-iotests/049 b/tests/qemu-iotests/049 index c100d30ed0..051a1c79e0 100755 --- a/tests/qemu-iotests/049 +++ b/tests/qemu-iotests/049 @@ -78,6 +78,11 @@ for s in $sizes; do test_qemu_img create -f $IMGFMT -o size=$s "$TEST_IMG" done +echo "== 4. Specify size twice (-o and traditional parameter) ==" +echo + +test_qemu_img create -f $IMGFMT -o size=10M "$TEST_IMG" 20M + echo "== Check correct interpretation of suffixes for cluster size ==" echo sizes="1024 1024b 1k 1K 1M " diff --git a/tests/qemu-iotests/049.out b/tests/qemu-iotests/049.out index 6b505408dd..affa55b341 100644 --- a/tests/qemu-iotests/049.out +++ b/tests/qemu-iotests/049.out @@ -121,6 +121,11 @@ qemu-img: TEST_DIR/t.qcow2: Parameter 'size' expects a non-negative number below Optional suffix k, M, G, T, P or E means kilo-, mega-, giga-, tera-, peta- and exabytes, respectively. +== 4. Specify size twice (-o and traditional parameter) == + +qemu-img create -f qcow2 -o size=10M TEST_DIR/t.qcow2 20M +qemu-img: TEST_DIR/t.qcow2: The image size must be specified only once + == Check correct interpretation of suffixes for cluster size == qemu-img create -f qcow2 -o cluster_size=1024 TEST_DIR/t.qcow2 64M diff --git a/tests/qemu-iotests/050 b/tests/qemu-iotests/050 index 211fc00797..cdc5356541 100755 --- a/tests/qemu-iotests/050 +++ b/tests/qemu-iotests/050 @@ -29,8 +29,8 @@ status=1 # failure is the default! _cleanup() { _cleanup_test_img - rm -f "$TEST_IMG.old" - rm -f "$TEST_IMG.new" + _rm_test_img "$TEST_IMG.old" + _rm_test_img "$TEST_IMG.new" } trap "_cleanup; exit \$status" 0 1 2 3 15 @@ -41,10 +41,6 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 _supported_fmt qcow2 qed _supported_proto file -if test "$IMGFMT" = qcow2 && test $IMGOPTS = ""; then - IMGOPTS=compat=1.1 -fi - echo echo "== Creating images ==" diff --git a/tests/qemu-iotests/051 b/tests/qemu-iotests/051 index 53bcdbc911..034d3a3250 100755 --- a/tests/qemu-iotests/051 +++ b/tests/qemu-iotests/051 @@ -39,8 +39,10 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 _supported_fmt qcow2 _supported_proto file # A compat=0.10 image is created in this test which does not support anything -# other than refcount_bits=16 -_unsupported_imgopts 'refcount_bits=\([^1]\|.\([^6]\|$\)\)' +# other than refcount_bits=16; +# it also will not support an external data file +_unsupported_imgopts 'refcount_bits=\([^1]\|.\([^6]\|$\)\)' data_file +_require_drivers nbd do_run_qemu() { @@ -157,7 +159,7 @@ echo echo === With version 2 images enabling lazy refcounts must fail === echo -_make_test_img -ocompat=0.10 $size +_make_test_img -o compat=0.10 $size run_qemu -drive file="$TEST_IMG",format=qcow2,lazy-refcounts=on run_qemu -drive file="$TEST_IMG",format=qcow2,lazy-refcounts=off diff --git a/tests/qemu-iotests/053 b/tests/qemu-iotests/053 index e82bb69881..71d299c4f9 100755 --- a/tests/qemu-iotests/053 +++ b/tests/qemu-iotests/053 @@ -28,8 +28,8 @@ status=1 # failure is the default! _cleanup() { - rm -f "$TEST_IMG.orig" - _cleanup_test_img + _rm_test_img "$TEST_IMG.orig" + _cleanup_test_img } trap "_cleanup; exit \$status" 0 1 2 3 15 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/058 b/tests/qemu-iotests/058 index 8c3212a72f..d84740ed9f 100755 --- a/tests/qemu-iotests/058 +++ b/tests/qemu-iotests/058 @@ -42,7 +42,7 @@ _cleanup() { nbd_server_stop _cleanup_test_img - rm -f "$converted_image" + _rm_test_img "$converted_image" } trap "_cleanup; exit \$status" 0 1 2 3 15 @@ -56,15 +56,16 @@ _supported_fmt qcow2 _supported_proto file _supported_os Linux _require_command QEMU_NBD -# Internal snapshots are (currently) impossible with refcount_bits=1 -_unsupported_imgopts 'refcount_bits=1[^0-9]' +# Internal snapshots are (currently) impossible with refcount_bits=1, +# and generally impossible with external data files +_unsupported_imgopts 'refcount_bits=1[^0-9]' data_file nbd_snapshot_img="nbd:unix:$nbd_unix_socket" converted_image=$TEST_IMG.converted # Use -f raw instead of -f $IMGFMT for the NBD connection -QEMU_IO_NBD="$QEMU_IO -f raw --cache=$CACHEMODE" +QEMU_IO_NBD="$QEMU_IO -f raw --cache=$CACHEMODE --aio=$AIOMODE" echo echo "== preparing image ==" diff --git a/tests/qemu-iotests/059 b/tests/qemu-iotests/059 index 10bfbaecec..5438025285 100755 --- a/tests/qemu-iotests/059 +++ b/tests/qemu-iotests/059 @@ -29,7 +29,7 @@ status=1 # failure is the default! _cleanup() { _cleanup_test_img - rm -f "$TEST_IMG.qcow2" + IMGFMT=qcow2 _rm_test_img "$TEST_IMG.qcow2" } trap "_cleanup; exit \$status" 0 1 2 3 15 @@ -70,18 +70,18 @@ poke_file "$TEST_IMG" "$grain_table_size_offset" "\x01\x00\x00\x00" echo echo "=== Testing monolithicFlat creation and opening ===" -IMGOPTS="subformat=monolithicFlat" _make_test_img 2G +_make_test_img -o "subformat=monolithicFlat" 2G _img_info _cleanup_test_img echo echo "=== Testing monolithicFlat with zeroed_grain ===" -IMGOPTS="subformat=monolithicFlat,zeroed_grain=on" _make_test_img 2G +_make_test_img -o "subformat=monolithicFlat,zeroed_grain=on" 2G _cleanup_test_img echo echo "=== Testing big twoGbMaxExtentFlat ===" -IMGOPTS="subformat=twoGbMaxExtentFlat" _make_test_img 1000G +_make_test_img -o "subformat=twoGbMaxExtentFlat" 1000G $QEMU_IMG info $TEST_IMG | _filter_testdir | sed -e 's/cid: [0-9]*/cid: XXXXXXXX/' _cleanup_test_img @@ -101,13 +101,13 @@ _img_info echo echo "=== Testing truncated sparse ===" -IMGOPTS="subformat=monolithicSparse" _make_test_img 100G +_make_test_img -o "subformat=monolithicSparse" 100G truncate -s 10M $TEST_IMG _img_info echo echo "=== Converting to streamOptimized from image with small cluster size===" -TEST_IMG="$TEST_IMG.qcow2" IMGFMT=qcow2 IMGOPTS="cluster_size=4096" _make_test_img 1G +TEST_IMG="$TEST_IMG.qcow2" IMGFMT=qcow2 _make_test_img -o "cluster_size=4096" 1G $QEMU_IO -f qcow2 -c "write -P 0xa 0 512" "$TEST_IMG.qcow2" | _filter_qemu_io $QEMU_IO -f qcow2 -c "write -P 0xb 10240 512" "$TEST_IMG.qcow2" | _filter_qemu_io $QEMU_IMG convert -f qcow2 -O vmdk -o subformat=streamOptimized "$TEST_IMG.qcow2" "$TEST_IMG" 2>&1 @@ -117,7 +117,7 @@ echo "=== Testing monolithicFlat with internally generated JSON file name ===" echo '--- blkdebug ---' # Should work, because bdrv_dirname() works fine with blkdebug -IMGOPTS="subformat=monolithicFlat" _make_test_img 64M +_make_test_img -o "subformat=monolithicFlat" 64M $QEMU_IO -c "open -o driver=$IMGFMT,file.driver=blkdebug,file.image.filename=$TEST_IMG,file.inject-error.0.event=read_aio" \ -c info \ 2>&1 \ @@ -126,7 +126,7 @@ _cleanup_test_img echo '--- quorum ---' # Should not work, because bdrv_dirname() does not work with quorum -IMGOPTS="subformat=monolithicFlat" _make_test_img 64M +_make_test_img -o "subformat=monolithicFlat" 64M cp "$TEST_IMG" "$TEST_IMG.orig" filename="json:{ @@ -161,7 +161,7 @@ _cleanup_test_img echo echo "=== Testing 4TB monolithicFlat creation and IO ===" -IMGOPTS="subformat=monolithicFlat" _make_test_img 4T +_make_test_img -o "subformat=monolithicFlat" 4T _img_info $QEMU_IO -c "write -P 0xa 900G 512" "$TEST_IMG" | _filter_qemu_io $QEMU_IO -c "read -v 900G 1024" "$TEST_IMG" | _filter_qemu_io @@ -170,7 +170,7 @@ _cleanup_test_img echo echo "=== Testing qemu-img map on extents ===" for fmt in monolithicSparse twoGbMaxExtentSparse; do - IMGOPTS="subformat=$fmt" _make_test_img 31G + _make_test_img -o "subformat=$fmt" 31G $QEMU_IO -c "write 65024 1k" "$TEST_IMG" | _filter_qemu_io $QEMU_IO -c "write 2147483136 1k" "$TEST_IMG" | _filter_qemu_io $QEMU_IO -c "write 5G 1k" "$TEST_IMG" | _filter_qemu_io diff --git a/tests/qemu-iotests/060 b/tests/qemu-iotests/060 index b91d8321bb..32c0ecce9e 100755 --- a/tests/qemu-iotests/060 +++ b/tests/qemu-iotests/060 @@ -44,10 +44,17 @@ _filter_io_error() . ./common.rc . ./common.filter -# This tests qocw2-specific low-level functionality +# This tests qcow2-specific low-level functionality _supported_fmt qcow2 _supported_proto file _supported_os Linux +# These tests only work for compat=1.1 images without an external +# data file with refcount_bits=16 +_unsupported_imgopts 'compat=0.10' data_file \ + 'refcount_bits=\([^1]\|.\([^6]\|$\)\)' + +# The repair process will create a large file - so check for availability first +_require_large_file 64G rt_offset=65536 # 0x10000 (XXX: just an assumption) rb_offset=131072 # 0x20000 (XXX: just an assumption) @@ -55,8 +62,6 @@ l1_offset=196608 # 0x30000 (XXX: just an assumption) l2_offset=262144 # 0x40000 (XXX: just an assumption) l2_offset_after_snapshot=524288 # 0x80000 (XXX: just an assumption) -IMGOPTS="compat=1.1" - OPEN_RW="open -o overlap-check=all $TEST_IMG" # Overlap checks are done before write operations only, therefore opening an # image read-only makes the overlap-check option irrelevant @@ -155,18 +160,16 @@ TEST_IMG=$BACKING_IMG _make_test_img 1G $QEMU_IO -c 'write 0k 64k' "$BACKING_IMG" | _filter_qemu_io -# compat=0.10 is required in order to make the following discard actually -# unallocate the sector rather than make it a zero sector - we want COW, after -# all. -IMGOPTS='compat=0.10' _make_test_img -b "$BACKING_IMG" 1G +_make_test_img -b "$BACKING_IMG" 1G # Write two clusters, the second one enforces creation of an L2 table after # the first data cluster. $QEMU_IO -c 'write 0k 64k' -c 'write 512M 64k' "$TEST_IMG" | _filter_qemu_io -# Discard the first cluster. This cluster will soon enough be reallocated and +# Free the first cluster. This cluster will soon enough be reallocated and # used for COW. -$QEMU_IO -c 'discard 0k 64k' "$TEST_IMG" | _filter_qemu_io +poke_file "$TEST_IMG" "$l2_offset" "\x00\x00\x00\x00\x00\x00\x00\x00" +poke_file "$TEST_IMG" "$(($rb_offset+10))" "\x00\x00" # Now, corrupt the image by marking the second L2 table cluster as free. -poke_file "$TEST_IMG" '131084' "\x00\x00" # 0x2000c +poke_file "$TEST_IMG" "$(($rb_offset+12))" "\x00\x00" # Start a write operation requiring COW on the image stopping it right before # doing the read; then, trigger the corruption prevention by writing anything to # any unallocated cluster, leading to an attempt to overwrite the second L2 @@ -398,7 +401,7 @@ echo echo "=== Discarding a non-covered in-bounds refblock ===" echo -IMGOPTS='refcount_bits=1' _make_test_img 64M +_make_test_img -o 'refcount_bits=1' 64M # Pretend there's a refblock somewhere where there is no refblock to # cover it (but the covering refblock has a valid index in the @@ -422,7 +425,7 @@ echo echo "=== Discarding a refblock covered by an unaligned refblock ===" echo -IMGOPTS='refcount_bits=1' _make_test_img 64M +_make_test_img -o 'refcount_bits=1' 64M # Same as above poke_file "$TEST_IMG" "$(($rt_offset+8))" "\x00\x00\x00\x10\x00\x00\x00\x00" diff --git a/tests/qemu-iotests/060.out b/tests/qemu-iotests/060.out index 0f6b0658a1..09caaea865 100644 --- a/tests/qemu-iotests/060.out +++ b/tests/qemu-iotests/060.out @@ -7,10 +7,10 @@ ERROR cluster 3 refcount=1 reference=3 1 errors were found on the image. Data may be corrupted, or further writes to the image may corrupt it. -incompatible_features 0x0 +incompatible_features [] qcow2: Marking image as corrupt: Preventing invalid write on metadata (overlaps with active L1 table); further corruption events will be suppressed write failed: Input/output error -incompatible_features 0x2 +incompatible_features [1] image: TEST_DIR/t.IMGFMT file format: IMGFMT virtual size: 64 MiB (67108864 bytes) @@ -33,10 +33,10 @@ ERROR cluster 2 refcount=1 reference=2 2 errors were found on the image. Data may be corrupted, or further writes to the image may corrupt it. -incompatible_features 0x0 +incompatible_features [] qcow2: Marking image as corrupt: Preventing invalid write on metadata (overlaps with refcount block); further corruption events will be suppressed write failed: Input/output error -incompatible_features 0x2 +incompatible_features [1] ERROR refcount block 0 refcount=2 ERROR cluster 2 refcount=1 reference=2 Rebuilding refcount structure @@ -49,10 +49,10 @@ The following inconsistencies were found and repaired: Double checking the fixed image now... No errors were found on the image. -incompatible_features 0x0 +incompatible_features [] wrote 512/512 bytes at offset 0 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -incompatible_features 0x0 +incompatible_features [] === Testing cluster data reference into inactive L2 table === @@ -69,10 +69,10 @@ Data may be corrupted, or further writes to the image may corrupt it. 1 leaked clusters were found on the image. This means waste of disk space, but no harm to data. -incompatible_features 0x0 +incompatible_features [] qcow2: Marking image as corrupt: Preventing invalid write on metadata (overlaps with inactive L2 table); further corruption events will be suppressed write failed: Input/output error -incompatible_features 0x2 +incompatible_features [1] ERROR cluster 4 refcount=1 reference=2 Leaked cluster 9 refcount=1 reference=0 Repairing cluster 4 refcount=1 reference=2 @@ -85,10 +85,10 @@ The following inconsistencies were found and repaired: Double checking the fixed image now... No errors were found on the image. -incompatible_features 0x0 +incompatible_features [] wrote 512/512 bytes at offset 0 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -incompatible_features 0x0 +incompatible_features [] read 512/512 bytes at offset 0 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) No errors were found on the image. @@ -105,8 +105,6 @@ wrote 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) wrote 65536/65536 bytes at offset 536870912 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -discard 65536/65536 bytes at offset 0 -64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) qcow2: Marking image as corrupt: Preventing invalid write on metadata (overlaps with active L2 table); further corruption events will be suppressed blkdebug: Suspended request '0' write failed: Input/output error diff --git a/tests/qemu-iotests/061 b/tests/qemu-iotests/061 index 4eac5b83bd..ce285d3084 100755 --- a/tests/qemu-iotests/061 +++ b/tests/qemu-iotests/061 @@ -29,7 +29,7 @@ status=1 # failure is the default! _cleanup() { _cleanup_test_img - rm -f $TEST_IMG.data + _rm_test_img "$TEST_IMG.data" } trap "_cleanup; exit \$status" 0 1 2 3 15 @@ -37,15 +37,22 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 . ./common.rc . ./common.filter -# This tests qocw2-specific low-level functionality +# This tests qcow2-specific low-level functionality _supported_fmt qcow2 _supported_proto file _supported_os Linux +# Conversion between different compat versions can only really work +# with refcount_bits=16; +# we have explicit tests for data_file here, but the whole test does +# not work with it; +# we have explicit tests for various cluster sizes, the remaining tests +# require the default 64k cluster +_unsupported_imgopts 'refcount_bits=\([^1]\|.\([^6]\|$\)\)' data_file cluster_size echo echo "=== Testing version downgrade with zero expansion ===" echo -IMGOPTS="compat=1.1,lazy_refcounts=on" _make_test_img 64M +_make_test_img -o "compat=1.1,lazy_refcounts=on" 64M $QEMU_IO -c "write -z 0 128k" "$TEST_IMG" | _filter_qemu_io $PYTHON qcow2.py "$TEST_IMG" dump-header $QEMU_IMG amend -o "compat=0.10" "$TEST_IMG" @@ -56,7 +63,7 @@ _check_test_img echo echo "=== Testing version downgrade with zero expansion and 4K cache entries ===" echo -IMGOPTS="compat=1.1,lazy_refcounts=on" _make_test_img 64M +_make_test_img -o "compat=1.1,lazy_refcounts=on" 64M $QEMU_IO -c "write -z 0 128k" "$TEST_IMG" | _filter_qemu_io $QEMU_IO -c "write -z 32M 128k" "$TEST_IMG" | _filter_qemu_io $QEMU_IO -c map "$TEST_IMG" | _filter_qemu_io @@ -72,7 +79,7 @@ _check_test_img echo echo "=== Testing dirty version downgrade ===" echo -IMGOPTS="compat=1.1,lazy_refcounts=on" _make_test_img 64M +_make_test_img -o "compat=1.1,lazy_refcounts=on" 64M _NO_VALGRIND \ $QEMU_IO -c "write -P 0x2a 0 128k" -c flush \ -c "sigraise $(kill -l KILL)" "$TEST_IMG" 2>&1 | _filter_qemu_io @@ -85,7 +92,7 @@ _check_test_img echo echo "=== Testing version downgrade with unknown compat/autoclear flags ===" echo -IMGOPTS="compat=1.1" _make_test_img 64M +_make_test_img -o "compat=1.1" 64M $PYTHON qcow2.py "$TEST_IMG" set-feature-bit compatible 42 $PYTHON qcow2.py "$TEST_IMG" set-feature-bit autoclear 42 $PYTHON qcow2.py "$TEST_IMG" dump-header @@ -96,7 +103,7 @@ _check_test_img echo echo "=== Testing version upgrade and resize ===" echo -IMGOPTS="compat=0.10" _make_test_img 64M +_make_test_img -o "compat=0.10" 64M $QEMU_IO -c "write -P 0x2a 42M 64k" "$TEST_IMG" | _filter_qemu_io $PYTHON qcow2.py "$TEST_IMG" dump-header $QEMU_IMG amend -o "compat=1.1,lazy_refcounts=on,size=128M" "$TEST_IMG" @@ -107,7 +114,7 @@ _check_test_img echo echo "=== Testing dirty lazy_refcounts=off ===" echo -IMGOPTS="compat=1.1,lazy_refcounts=on" _make_test_img 64M +_make_test_img -o "compat=1.1,lazy_refcounts=on" 64M _NO_VALGRIND \ $QEMU_IO -c "write -P 0x2a 0 128k" -c flush \ -c "sigraise $(kill -l KILL)" "$TEST_IMG" 2>&1 | _filter_qemu_io @@ -120,8 +127,8 @@ _check_test_img echo echo "=== Testing backing file ===" echo -IMGOPTS="compat=1.1" _make_test_img 64M -IMGOPTS="compat=1.1" TEST_IMG="$TEST_IMG.base" _make_test_img 64M +_make_test_img -o "compat=1.1" 64M +TEST_IMG="$TEST_IMG.base" _make_test_img -o "compat=1.1" 64M $QEMU_IO -c "write -P 0x2a 0 128k" "$TEST_IMG.base" | _filter_qemu_io $QEMU_IO -c "read -P 0 0 128k" "$TEST_IMG" | _filter_qemu_io $QEMU_IMG amend -o "backing_file=$TEST_IMG.base,backing_fmt=qcow2" "$TEST_IMG" @@ -131,7 +138,7 @@ _check_test_img echo echo "=== Testing invalid configurations ===" echo -IMGOPTS="compat=0.10" _make_test_img 64M +_make_test_img -o "compat=0.10" 64M $QEMU_IMG amend -o "lazy_refcounts=on" "$TEST_IMG" $QEMU_IMG amend -o "compat=1.1" "$TEST_IMG" # actually valid $QEMU_IMG amend -o "compat=0.10,lazy_refcounts=on" "$TEST_IMG" @@ -144,7 +151,7 @@ $QEMU_IMG amend -o "preallocation=on" "$TEST_IMG" echo echo "=== Testing correct handling of unset value ===" echo -IMGOPTS="compat=1.1,cluster_size=1k" _make_test_img 64M +_make_test_img -o "compat=1.1,cluster_size=1k" 64M echo "Should work:" $QEMU_IMG amend -o "lazy_refcounts=on" "$TEST_IMG" echo "Should not work:" # Just to know which of these tests actually fails @@ -153,7 +160,7 @@ $QEMU_IMG amend -o "cluster_size=64k" "$TEST_IMG" echo echo "=== Testing zero expansion on inactive clusters ===" echo -IMGOPTS="compat=1.1" _make_test_img 64M +_make_test_img -o "compat=1.1" 64M $QEMU_IO -c "write -z 0 128k" "$TEST_IMG" | _filter_qemu_io $QEMU_IMG snapshot -c foo "$TEST_IMG" $QEMU_IO -c "write -P 0x2a 0 128k" "$TEST_IMG" | _filter_qemu_io @@ -167,7 +174,7 @@ $QEMU_IO -c "read -P 0 0 128k" "$TEST_IMG" | _filter_qemu_io echo echo "=== Testing zero expansion on shared L2 table ===" echo -IMGOPTS="compat=1.1" _make_test_img 64M +_make_test_img -o "compat=1.1" 64M $QEMU_IO -c "write -z 0 128k" "$TEST_IMG" | _filter_qemu_io $QEMU_IMG snapshot -c foo "$TEST_IMG" $QEMU_IMG amend -o "compat=0.10" "$TEST_IMG" @@ -180,9 +187,9 @@ $QEMU_IO -c "read -P 0 0 128k" "$TEST_IMG" | _filter_qemu_io echo echo "=== Testing zero expansion on backed image ===" echo -IMGOPTS="compat=1.1" TEST_IMG="$TEST_IMG.base" _make_test_img 64M +TEST_IMG="$TEST_IMG.base" _make_test_img -o "compat=1.1" 64M $QEMU_IO -c "write -P 0x2a 0 128k" "$TEST_IMG.base" | _filter_qemu_io -IMGOPTS="compat=1.1" _make_test_img -b "$TEST_IMG.base" 64M +_make_test_img -o "compat=1.1" -b "$TEST_IMG.base" 64M $QEMU_IO -c "read -P 0x2a 0 128k" -c "write -z 0 64k" "$TEST_IMG" | _filter_qemu_io $QEMU_IMG amend -o "compat=0.10" "$TEST_IMG" _check_test_img @@ -191,9 +198,9 @@ $QEMU_IO -c "read -P 0 0 64k" -c "read -P 0x2a 64k 64k" "$TEST_IMG" | _filter_qe echo echo "=== Testing zero expansion on backed inactive clusters ===" echo -IMGOPTS="compat=1.1" TEST_IMG="$TEST_IMG.base" _make_test_img 64M +TEST_IMG="$TEST_IMG.base" _make_test_img -o "compat=1.1" 64M $QEMU_IO -c "write -P 0x2a 0 128k" "$TEST_IMG.base" | _filter_qemu_io -IMGOPTS="compat=1.1" _make_test_img -b "$TEST_IMG.base" 64M +_make_test_img -o "compat=1.1" -b "$TEST_IMG.base" 64M $QEMU_IO -c "write -z 0 64k" "$TEST_IMG" | _filter_qemu_io $QEMU_IMG snapshot -c foo "$TEST_IMG" $QEMU_IO -c "write -P 0x42 0 128k" "$TEST_IMG" | _filter_qemu_io @@ -207,9 +214,9 @@ $QEMU_IO -c "read -P 0 0 64k" -c "read -P 0x2a 64k 64k" "$TEST_IMG" | _filter_qe echo echo "=== Testing zero expansion on backed image with shared L2 table ===" echo -IMGOPTS="compat=1.1" TEST_IMG="$TEST_IMG.base" _make_test_img 64M +TEST_IMG="$TEST_IMG.base" _make_test_img -o "compat=1.1" 64M $QEMU_IO -c "write -P 0x2a 0 128k" "$TEST_IMG.base" | _filter_qemu_io -IMGOPTS="compat=1.1" _make_test_img -b "$TEST_IMG.base" 64M +_make_test_img -o "compat=1.1" -b "$TEST_IMG.base" 64M $QEMU_IO -c "write -z 0 128k" "$TEST_IMG" | _filter_qemu_io $QEMU_IMG snapshot -c foo "$TEST_IMG" $QEMU_IMG amend -o "compat=0.10" "$TEST_IMG" @@ -222,7 +229,7 @@ $QEMU_IO -c "read -P 0 0 128k" "$TEST_IMG" | _filter_qemu_io echo echo "=== Testing preallocated zero expansion on full image ===" echo -IMGOPTS="compat=1.1" TEST_IMG="$TEST_IMG" _make_test_img 64M +TEST_IMG="$TEST_IMG" _make_test_img -o "compat=1.1" 64M $QEMU_IO -c "write -P 0x2a 0 64M" "$TEST_IMG" -c "write -z 0 64M" | _filter_qemu_io $QEMU_IMG amend -o "compat=0.10" "$TEST_IMG" _check_test_img @@ -231,8 +238,8 @@ $QEMU_IO -c "read -P 0 0 64M" "$TEST_IMG" | _filter_qemu_io echo echo "=== Testing progress report without snapshot ===" echo -IMGOPTS="compat=1.1" TEST_IMG="$TEST_IMG.base" _make_test_img 4G -IMGOPTS="compat=1.1" _make_test_img -b "$TEST_IMG.base" 4G +TEST_IMG="$TEST_IMG.base" _make_test_img -o "compat=1.1" 4G +_make_test_img -o "compat=1.1" -b "$TEST_IMG.base" 4G $QEMU_IO -c "write -z 0 64k" \ -c "write -z 1G 64k" \ -c "write -z 2G 64k" \ @@ -243,8 +250,8 @@ _check_test_img echo echo "=== Testing progress report with snapshot ===" echo -IMGOPTS="compat=1.1" TEST_IMG="$TEST_IMG.base" _make_test_img 4G -IMGOPTS="compat=1.1" _make_test_img -b "$TEST_IMG.base" 4G +TEST_IMG="$TEST_IMG.base" _make_test_img -o "compat=1.1" 4G +_make_test_img -o "compat=1.1" -b "$TEST_IMG.base" 4G $QEMU_IO -c "write -z 0 64k" \ -c "write -z 1G 64k" \ -c "write -z 2G 64k" \ @@ -256,7 +263,7 @@ _check_test_img echo echo "=== Testing version downgrade with external data file ===" echo -IMGOPTS="compat=1.1,data_file=$TEST_IMG.data" _make_test_img 64M +_make_test_img -o "compat=1.1,data_file=$TEST_IMG.data" 64M $QEMU_IMG amend -o "compat=0.10" "$TEST_IMG" _img_info --format-specific _check_test_img @@ -264,11 +271,11 @@ _check_test_img echo echo "=== Try changing the external data file ===" echo -IMGOPTS="compat=1.1" _make_test_img 64M +_make_test_img -o "compat=1.1" 64M $QEMU_IMG amend -o "data_file=foo" "$TEST_IMG" echo -IMGOPTS="compat=1.1,data_file=$TEST_IMG.data" _make_test_img 64M +_make_test_img -o "compat=1.1,data_file=$TEST_IMG.data" 64M $QEMU_IMG amend -o "data_file=foo" "$TEST_IMG" _img_info --format-specific TEST_IMG="data-file.filename=$TEST_IMG.data,file.filename=$TEST_IMG" _img_info --format-specific --image-opts @@ -281,7 +288,7 @@ TEST_IMG="data-file.filename=$TEST_IMG.data,file.filename=$TEST_IMG" _img_info - echo echo "=== Clearing and setting data-file-raw ===" echo -IMGOPTS="compat=1.1,data_file=$TEST_IMG.data,data_file_raw=on" _make_test_img 64M +_make_test_img -o "compat=1.1,data_file=$TEST_IMG.data,data_file_raw=on" 64M $QEMU_IMG amend -o "data_file_raw=on" "$TEST_IMG" _img_info --format-specific _check_test_img diff --git a/tests/qemu-iotests/061.out b/tests/qemu-iotests/061.out index d6a7c2af95..413cc4e0f4 100644 --- a/tests/qemu-iotests/061.out +++ b/tests/qemu-iotests/061.out @@ -18,15 +18,15 @@ refcount_table_offset 0x10000 refcount_table_clusters 1 nb_snapshots 0 snapshot_offset 0x0 -incompatible_features 0x0 -compatible_features 0x1 -autoclear_features 0x0 +incompatible_features [] +compatible_features [0] +autoclear_features [] refcount_order 4 header_length 104 Header extension: magic 0x6803f857 -length 192 +length 288 data magic 0x514649fb @@ -42,9 +42,9 @@ refcount_table_offset 0x10000 refcount_table_clusters 1 nb_snapshots 0 snapshot_offset 0x0 -incompatible_features 0x0 -compatible_features 0x0 -autoclear_features 0x0 +incompatible_features [] +compatible_features [] +autoclear_features [] refcount_order 4 header_length 72 @@ -76,15 +76,15 @@ refcount_table_offset 0x10000 refcount_table_clusters 1 nb_snapshots 0 snapshot_offset 0x0 -incompatible_features 0x0 -compatible_features 0x1 -autoclear_features 0x0 +incompatible_features [] +compatible_features [0] +autoclear_features [] refcount_order 4 header_length 104 Header extension: magic 0x6803f857 -length 192 +length 288 data magic 0x514649fb @@ -100,9 +100,9 @@ refcount_table_offset 0x10000 refcount_table_clusters 1 nb_snapshots 0 snapshot_offset 0x0 -incompatible_features 0x0 -compatible_features 0x0 -autoclear_features 0x0 +incompatible_features [] +compatible_features [] +autoclear_features [] refcount_order 4 header_length 72 @@ -132,15 +132,15 @@ refcount_table_offset 0x10000 refcount_table_clusters 1 nb_snapshots 0 snapshot_offset 0x0 -incompatible_features 0x1 -compatible_features 0x1 -autoclear_features 0x0 +incompatible_features [0] +compatible_features [0] +autoclear_features [] refcount_order 4 header_length 104 Header extension: magic 0x6803f857 -length 192 +length 288 data ERROR cluster 5 refcount=0 reference=1 @@ -161,9 +161,9 @@ refcount_table_offset 0x80000 refcount_table_clusters 1 nb_snapshots 0 snapshot_offset 0x0 -incompatible_features 0x0 -compatible_features 0x0 -autoclear_features 0x0 +incompatible_features [] +compatible_features [] +autoclear_features [] refcount_order 4 header_length 72 @@ -187,15 +187,15 @@ refcount_table_offset 0x10000 refcount_table_clusters 1 nb_snapshots 0 snapshot_offset 0x0 -incompatible_features 0x0 -compatible_features 0x40000000000 -autoclear_features 0x40000000000 +incompatible_features [] +compatible_features [42] +autoclear_features [42] refcount_order 4 header_length 104 Header extension: magic 0x6803f857 -length 192 +length 288 data magic 0x514649fb @@ -211,9 +211,9 @@ refcount_table_offset 0x10000 refcount_table_clusters 1 nb_snapshots 0 snapshot_offset 0x0 -incompatible_features 0x0 -compatible_features 0x0 -autoclear_features 0x0 +incompatible_features [] +compatible_features [] +autoclear_features [] refcount_order 4 header_length 72 @@ -237,9 +237,9 @@ refcount_table_offset 0x10000 refcount_table_clusters 1 nb_snapshots 0 snapshot_offset 0x0 -incompatible_features 0x0 -compatible_features 0x0 -autoclear_features 0x0 +incompatible_features [] +compatible_features [] +autoclear_features [] refcount_order 4 header_length 72 @@ -256,15 +256,15 @@ refcount_table_offset 0x10000 refcount_table_clusters 1 nb_snapshots 0 snapshot_offset 0x0 -incompatible_features 0x0 -compatible_features 0x1 -autoclear_features 0x0 +incompatible_features [] +compatible_features [0] +autoclear_features [] refcount_order 4 header_length 104 Header extension: magic 0x6803f857 -length 192 +length 288 data read 65536/65536 bytes at offset 44040192 @@ -290,15 +290,15 @@ refcount_table_offset 0x10000 refcount_table_clusters 1 nb_snapshots 0 snapshot_offset 0x0 -incompatible_features 0x1 -compatible_features 0x1 -autoclear_features 0x0 +incompatible_features [0] +compatible_features [0] +autoclear_features [] refcount_order 4 header_length 104 Header extension: magic 0x6803f857 -length 192 +length 288 data ERROR cluster 5 refcount=0 reference=1 @@ -319,15 +319,15 @@ refcount_table_offset 0x80000 refcount_table_clusters 1 nb_snapshots 0 snapshot_offset 0x0 -incompatible_features 0x0 -compatible_features 0x0 -autoclear_features 0x0 +incompatible_features [] +compatible_features [] +autoclear_features [] refcount_order 4 header_length 104 Header extension: magic 0x6803f857 -length 192 +length 288 data read 131072/131072 bytes at offset 0 diff --git a/tests/qemu-iotests/062 b/tests/qemu-iotests/062 index d5f818fcce..f26b88df9d 100755 --- a/tests/qemu-iotests/062 +++ b/tests/qemu-iotests/062 @@ -37,11 +37,12 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 . ./common.rc . ./common.filter -# This tests qocw2-specific low-level functionality +# This tests qcow2-specific low-level functionality _supported_fmt qcow2 _supported_proto generic +# We need zero clusters and snapshots +_unsupported_imgopts 'compat=0.10' 'refcount_bits=1[^0-9]' data_file -IMGOPTS="compat=1.1" IMG_SIZE=64M echo diff --git a/tests/qemu-iotests/063 b/tests/qemu-iotests/063 index 7cf0427af4..c750b3806e 100755 --- a/tests/qemu-iotests/063 +++ b/tests/qemu-iotests/063 @@ -29,8 +29,10 @@ status=1 # failure is the default! _cleanup() { - _cleanup_test_img - rm -f "$TEST_IMG.orig" "$TEST_IMG.raw1" "$TEST_IMG.raw2" + _cleanup_test_img + for img in "$TEST_IMG".{orig,raw1,raw2,target}; do + _rm_test_img "$img" + done } trap "_cleanup; exit \$status" 0 1 2 3 15 @@ -49,15 +51,13 @@ _unsupported_imgopts "subformat=monolithicFlat" \ _make_test_img 4M echo "== Testing conversion with -n fails with no target file ==" -# check .orig file does not exist -rm -f "$TEST_IMG.orig" if $QEMU_IMG convert -f $IMGFMT -O $IMGFMT -n "$TEST_IMG" "$TEST_IMG.orig" >/dev/null 2>&1; then exit 1 fi echo "== Testing conversion with -n succeeds with a target file ==" -rm -f "$TEST_IMG.orig" -cp "$TEST_IMG" "$TEST_IMG.orig" +_rm_test_img "$TEST_IMG.orig" +TEST_IMG="$TEST_IMG.orig" _make_test_img 4M if ! $QEMU_IMG convert -f $IMGFMT -O $IMGFMT -n "$TEST_IMG" "$TEST_IMG.orig" ; then exit 1 fi @@ -83,10 +83,8 @@ fi _check_test_img echo "== Testing conversion to a smaller file fails ==" -rm -f "$TEST_IMG.orig" -mv "$TEST_IMG" "$TEST_IMG.orig" -_make_test_img 2M -if $QEMU_IMG convert -f $IMGFMT -O $IMGFMT -n "$TEST_IMG.orig" "$TEST_IMG" >/dev/null 2>&1; then +TEST_IMG="$TEST_IMG.target" _make_test_img 2M +if $QEMU_IMG convert -f $IMGFMT -O $IMGFMT -n "$TEST_IMG" "$TEST_IMG.target" >/dev/null 2>&1; then exit 1 fi diff --git a/tests/qemu-iotests/063.out b/tests/qemu-iotests/063.out index 7b691b2c9e..890b719bf0 100644 --- a/tests/qemu-iotests/063.out +++ b/tests/qemu-iotests/063.out @@ -2,11 +2,12 @@ QA output created by 063 Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4194304 == Testing conversion with -n fails with no target file == == Testing conversion with -n succeeds with a target file == +Formatting 'TEST_DIR/t.IMGFMT.orig', fmt=IMGFMT size=4194304 == Testing conversion to raw is the same after conversion with -n == == Testing conversion back to original format == No errors were found on the image. == Testing conversion to a smaller file fails == -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2097152 +Formatting 'TEST_DIR/t.IMGFMT.target', fmt=IMGFMT size=2097152 == Regression testing for copy offloading bug == Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 Formatting 'TEST_DIR/t.IMGFMT.target', fmt=IMGFMT size=1048576 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/066 b/tests/qemu-iotests/066 index 28f8c98412..a4ac613f8e 100755 --- a/tests/qemu-iotests/066 +++ b/tests/qemu-iotests/066 @@ -36,12 +36,15 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 . ./common.rc . ./common.filter -# This tests qocw2-specific low-level functionality +# This tests qcow2-specific low-level functionality _supported_fmt qcow2 _supported_proto generic +# We need zero clusters and snapshots +# (TODO: Consider splitting the snapshot part into a separate test +# file, so this one runs with refcount_bits=1 and data_file) +_unsupported_imgopts 'compat=0.10' 'refcount_bits=1[^0-9]' data_file # Intentionally create an unaligned image -IMGOPTS="compat=1.1" IMG_SIZE=$((64 * 1024 * 1024 + 512)) echo diff --git a/tests/qemu-iotests/067 b/tests/qemu-iotests/067 index 926c79b37c..a63be9cabf 100755 --- a/tests/qemu-iotests/067 +++ b/tests/qemu-iotests/067 @@ -32,8 +32,10 @@ status=1 # failure is the default! _supported_fmt qcow2 _supported_proto file -# Because anything other than 16 would change the output of query-block -_unsupported_imgopts 'refcount_bits=\([^1]\|.\([^6]\|$\)\)' +# Because anything other than 16 would change the output of query-block, +# and external data files would change the output of +# query-named-block-nodes +_unsupported_imgopts 'refcount_bits=\([^1]\|.\([^6]\|$\)\)' data_file do_run_qemu() { diff --git a/tests/qemu-iotests/068 b/tests/qemu-iotests/068 index 22f5ca3ba6..ccd1a9f1db 100755 --- a/tests/qemu-iotests/068 +++ b/tests/qemu-iotests/068 @@ -36,11 +36,13 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 . ./common.rc . ./common.filter -# This tests qocw2-specific low-level functionality +# This tests qcow2-specific low-level functionality _supported_fmt qcow2 _supported_proto generic +# Internal snapshots are (currently) impossible with refcount_bits=1, +# and generally impossible with external data files +_unsupported_imgopts 'compat=0.10' 'refcount_bits=1[^0-9]' data_file -IMGOPTS="compat=1.1" IMG_SIZE=128K case "$QEMU_DEFAULT_MACHINE" in diff --git a/tests/qemu-iotests/069 b/tests/qemu-iotests/069 index 3974714852..b997b127f0 100755 --- a/tests/qemu-iotests/069 +++ b/tests/qemu-iotests/069 @@ -47,7 +47,7 @@ echo "=== Creating an image with a backing file and deleting that file ===" echo TEST_IMG="$TEST_IMG.base" _make_test_img $IMG_SIZE _make_test_img -b "$TEST_IMG.base" $IMG_SIZE -rm -f "$TEST_IMG.base" +_rm_test_img "$TEST_IMG.base" # Just open the image and close it right again (this should print an error message) $QEMU_IO -c quit "$TEST_IMG" 2>&1 | _filter_testdir | _filter_imgfmt diff --git a/tests/qemu-iotests/071 b/tests/qemu-iotests/071 index fab526666b..88faebcc1d 100755 --- a/tests/qemu-iotests/071 +++ b/tests/qemu-iotests/071 @@ -39,6 +39,9 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 _supported_fmt qcow2 _supported_proto file _require_drivers blkdebug blkverify +# blkdebug can only inject errors on bs->file, not on the data_file, +# so thie test does not work with external data files +_unsupported_imgopts data_file do_run_qemu() { @@ -58,7 +61,7 @@ echo echo "=== Testing blkverify through filename ===" echo -TEST_IMG="$TEST_IMG.base" IMGOPTS="" IMGFMT="raw" _make_test_img $IMG_SIZE |\ +TEST_IMG="$TEST_IMG.base" IMGFMT="raw" _make_test_img --no-opts $IMG_SIZE |\ _filter_imgfmt _make_test_img $IMG_SIZE $QEMU_IO -c "open -o driver=raw,file.driver=blkverify,file.raw.filename=$TEST_IMG.base $TEST_IMG" \ @@ -73,7 +76,7 @@ echo echo "=== Testing blkverify through file blockref ===" echo -TEST_IMG="$TEST_IMG.base" IMGOPTS="" IMGFMT="raw" _make_test_img $IMG_SIZE |\ +TEST_IMG="$TEST_IMG.base" IMGFMT="raw" _make_test_img --no-opts $IMG_SIZE |\ _filter_imgfmt _make_test_img $IMG_SIZE $QEMU_IO -c "open -o driver=raw,file.driver=blkverify,file.raw.filename=$TEST_IMG.base,file.test.driver=$IMGFMT,file.test.file.filename=$TEST_IMG" \ diff --git a/tests/qemu-iotests/073 b/tests/qemu-iotests/073 index e684b1b780..23a1bdf890 100755 --- a/tests/qemu-iotests/073 +++ b/tests/qemu-iotests/073 @@ -39,6 +39,10 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 _supported_fmt qcow2 _supported_proto generic _unsupported_proto vxhs +# External data files do not support compressed clusters +# (TODO: Consider writing a version for external data files that does +# not test compressed clusters) +_unsupported_imgopts data_file CLUSTER_SIZE=64k size=128M diff --git a/tests/qemu-iotests/074 b/tests/qemu-iotests/074 index bb4ad1cc08..db03edf0b0 100755 --- a/tests/qemu-iotests/074 +++ b/tests/qemu-iotests/074 @@ -31,7 +31,7 @@ _cleanup() { echo "Cleanup" _cleanup_test_img - rm "${TEST_IMG2}" + _rm_test_img "${TEST_IMG2}" rm -f "$TEST_DIR/blkdebug.conf" } trap "_cleanup; exit \$status" 0 1 2 3 15 @@ -50,6 +50,8 @@ _compare() _supported_fmt qcow2 _supported_proto file _supported_os Linux +# blkdebug can only inject errors on bs->file +_unsupported_imgopts data_file # Setup test basic parameters TEST_IMG2=$TEST_IMG.2 diff --git a/tests/qemu-iotests/079 b/tests/qemu-iotests/079 index 81f0c21f53..3642b51feb 100755 --- a/tests/qemu-iotests/079 +++ b/tests/qemu-iotests/079 @@ -39,13 +39,15 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 _supported_fmt qcow2 _supported_proto file nfs +# Some containers (e.g. non-x86 on Travis) do not allow large files +_require_large_file 4G + echo "=== Check option preallocation and cluster_size ===" echo cluster_sizes="16384 32768 65536 131072 262144 524288 1048576 2097152 4194304" for s in $cluster_sizes; do - IMGOPTS=$(_optstr_add "$IMGOPTS" "preallocation=metadata,cluster_size=$s") \ - _make_test_img 4G + _make_test_img -o "preallocation=metadata,cluster_size=$s" 4G done # success, all done diff --git a/tests/qemu-iotests/080 b/tests/qemu-iotests/080 index 4bcb5021e8..a3d13c414e 100755 --- a/tests/qemu-iotests/080 +++ b/tests/qemu-iotests/080 @@ -28,7 +28,7 @@ status=1 # failure is the default! _cleanup() { - rm -f $TEST_IMG.snap + _rm_test_img "$TEST_IMG.snap" _cleanup_test_img } trap "_cleanup; exit \$status" 0 1 2 3 15 @@ -40,9 +40,10 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 _supported_fmt qcow2 _supported_proto file _supported_os Linux -# - Internal snapshots are (currently) impossible with refcount_bits=1 +# - Internal snapshots are (currently) impossible with refcount_bits=1, +# and generally impossible with external data files # - This is generally a test for compat=1.1 images -_unsupported_imgopts 'refcount_bits=1[^0-9]' 'compat=0.10' +_unsupported_imgopts 'refcount_bits=1[^0-9]' data_file 'compat=0.10' header_size=104 diff --git a/tests/qemu-iotests/081 b/tests/qemu-iotests/081 index 85acdf76d4..537d40dfd5 100755 --- a/tests/qemu-iotests/081 +++ b/tests/qemu-iotests/081 @@ -28,9 +28,9 @@ status=1 # failure is the default! _cleanup() { - rm -rf $TEST_DIR/1.raw - rm -rf $TEST_DIR/2.raw - rm -rf $TEST_DIR/3.raw + _rm_test_img "$TEST_DIR/1.raw" + _rm_test_img "$TEST_DIR/2.raw" + _rm_test_img "$TEST_DIR/3.raw" } trap "_cleanup; exit \$status" 0 1 2 3 15 diff --git a/tests/qemu-iotests/085 b/tests/qemu-iotests/085 index d40fdab542..dd3c993a2d 100755 --- a/tests/qemu-iotests/085 +++ b/tests/qemu-iotests/085 @@ -39,12 +39,16 @@ SNAPSHOTS=10 _cleanup() { _cleanup_qemu + _cleanup_test_img for i in $(seq 1 ${SNAPSHOTS}) do - rm -f "${TEST_DIR}/${i}-${snapshot_virt0}" - rm -f "${TEST_DIR}/${i}-${snapshot_virt1}" + _rm_test_img "${TEST_DIR}/${i}-${snapshot_virt0}" + _rm_test_img "${TEST_DIR}/${i}-${snapshot_virt1}" + done + for img in "${TEST_IMG}".{1,2,base} + do + _rm_test_img "$img" done - rm -f "${TEST_IMG}" "${TEST_IMG}.1" "${TEST_IMG}.2" "${TEST_IMG}.base" } trap "_cleanup; exit \$status" 0 1 2 3 15 @@ -102,8 +106,7 @@ add_snapshot_image() { base_image="${TEST_DIR}/$((${1}-1))-${snapshot_virt0}" snapshot_file="${TEST_DIR}/${1}-${snapshot_virt0}" - _make_test_img -u -b "${base_image}" "$size" - mv "${TEST_IMG}" "${snapshot_file}" + TEST_IMG=$snapshot_file _make_test_img -u -b "${base_image}" "$size" do_blockdev_add "$1" "'backing': null, " "${snapshot_file}" } @@ -119,10 +122,8 @@ blockdev_snapshot() size=128M -_make_test_img $size -mv "${TEST_IMG}" "${TEST_IMG}.1" -_make_test_img $size -mv "${TEST_IMG}" "${TEST_IMG}.2" +TEST_IMG="$TEST_IMG.1" _make_test_img $size +TEST_IMG="$TEST_IMG.2" _make_test_img $size echo echo === Running QEMU === diff --git a/tests/qemu-iotests/085.out b/tests/qemu-iotests/085.out index bb50227b82..fd11aae678 100644 --- a/tests/qemu-iotests/085.out +++ b/tests/qemu-iotests/085.out @@ -1,6 +1,6 @@ QA output created by 085 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 +Formatting 'TEST_DIR/t.IMGFMT.1', fmt=IMGFMT size=134217728 +Formatting 'TEST_DIR/t.IMGFMT.2', fmt=IMGFMT size=134217728 === Running QEMU === @@ -68,12 +68,12 @@ Formatting 'TEST_DIR/10-snapshot-v1.qcow2', fmt=qcow2 size=134217728 backing_fil === Create a couple of snapshots using blockdev-snapshot === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/10-snapshot-v0.IMGFMT +Formatting 'TEST_DIR/11-snapshot-v0.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/10-snapshot-v0.IMGFMT { 'execute': 'blockdev-add', 'arguments': { 'driver': 'IMGFMT', 'node-name': 'snap_11', 'backing': null, 'file': { 'driver': 'file', 'filename': 'TEST_DIR/11-snapshot-v0.IMGFMT', 'node-name': 'file_11' } } } {"return": {}} { 'execute': 'blockdev-snapshot', 'arguments': { 'node': 'virtio0', 'overlay':'snap_11' } } {"return": {}} -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/11-snapshot-v0.IMGFMT +Formatting 'TEST_DIR/12-snapshot-v0.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/11-snapshot-v0.IMGFMT { 'execute': 'blockdev-add', 'arguments': { 'driver': 'IMGFMT', 'node-name': 'snap_12', 'backing': null, 'file': { 'driver': 'file', 'filename': 'TEST_DIR/12-snapshot-v0.IMGFMT', 'node-name': 'file_12' } } } {"return": {}} { 'execute': 'blockdev-snapshot', 'arguments': { 'node': 'virtio0', 'overlay':'snap_12' } } @@ -82,7 +82,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/ === Invalid command - cannot create a snapshot using a file BDS === { 'execute': 'blockdev-snapshot', 'arguments': { 'node':'virtio0', 'overlay':'file_12' } } -{"error": {"class": "GenericError", "desc": "The overlay does not support backing images"}} +{"error": {"class": "GenericError", "desc": "The overlay is already in use"}} === Invalid command - snapshot node used as active layer === @@ -96,7 +96,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/ === Invalid command - snapshot node used as backing hd === { 'execute': 'blockdev-snapshot', 'arguments': { 'node': 'virtio0', 'overlay':'snap_11' } } -{"error": {"class": "GenericError", "desc": "Node 'snap_11' is busy: node is used as backing hd of 'snap_12'"}} +{"error": {"class": "GenericError", "desc": "The overlay is already in use"}} === Invalid command - snapshot node has a backing image === diff --git a/tests/qemu-iotests/087 b/tests/qemu-iotests/087 index d6c8613419..bdfdad3454 100755 --- a/tests/qemu-iotests/087 +++ b/tests/qemu-iotests/087 @@ -26,6 +26,12 @@ echo "QA output created by $seq" status=1 # failure is the default! +_cleanup() +{ + _cleanup_test_img +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + # get standard environment, filters and checks . ./common.rc . ./common.filter diff --git a/tests/qemu-iotests/088 b/tests/qemu-iotests/088 index b44edd0cf9..ef1163346c 100755 --- a/tests/qemu-iotests/088 +++ b/tests/qemu-iotests/088 @@ -28,7 +28,7 @@ status=1 # failure is the default! _cleanup() { - rm -f $TEST_IMG.snap + _rm_test_img "$TEST_IMG.snap" _cleanup_test_img } trap "_cleanup; exit \$status" 0 1 2 3 15 diff --git a/tests/qemu-iotests/089 b/tests/qemu-iotests/089 index ad029f1f09..059ad75e28 100755 --- a/tests/qemu-iotests/089 +++ b/tests/qemu-iotests/089 @@ -64,7 +64,7 @@ $QEMU_IO -c 'write -P 42 0 512' -c 'write -P 23 512 512' \ $QEMU_IMG convert -f raw -O $IMGFMT "$TEST_IMG.base" "$TEST_IMG" -$QEMU_IO_PROG --cache $CACHEMODE \ +$QEMU_IO_PROG --cache $CACHEMODE --aio $AIOMODE \ -c 'read -P 42 0 512' -c 'read -P 23 512 512' \ -c 'read -P 66 1024 512' "json:{ \"driver\": \"$IMGFMT\", @@ -111,7 +111,7 @@ $QEMU_IO -c 'write -P 42 0x38000 512' "$TEST_IMG" | _filter_qemu_io # The "image.filename" part tests whether "a": { "b": "c" } and "a.b": "c" do # the same (which they should). -$QEMU_IO_PROG --cache $CACHEMODE \ +$QEMU_IO_PROG --cache $CACHEMODE --aio $AIOMODE \ -c 'read -P 42 0x38000 512' "json:{ \"driver\": \"$IMGFMT\", \"file\": { diff --git a/tests/qemu-iotests/090 b/tests/qemu-iotests/090 index 9f8cfbb80f..1246e4f910 100755 --- a/tests/qemu-iotests/090 +++ b/tests/qemu-iotests/090 @@ -38,6 +38,8 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 _supported_fmt qcow2 _supported_proto file nfs +# External data files do not support compressed clusters +_unsupported_imgopts data_file IMG_SIZE=128K diff --git a/tests/qemu-iotests/091 b/tests/qemu-iotests/091 index f4b44659ae..d2a2aca347 100755 --- a/tests/qemu-iotests/091 +++ b/tests/qemu-iotests/091 @@ -60,13 +60,13 @@ echo === Starting QEMU VM1 === echo qemu_comm_method="monitor" -_launch_qemu -drive file="${TEST_IMG}",cache=${CACHEMODE},id=disk +_launch_qemu -drive file="${TEST_IMG}",cache=${CACHEMODE},aio=${AIOMODE},id=disk h1=$QEMU_HANDLE echo echo === Starting QEMU VM2 === echo -_launch_qemu -drive file="${TEST_IMG}",cache=${CACHEMODE},id=disk \ +_launch_qemu -drive file="${TEST_IMG}",cache=${CACHEMODE},aio=${AIOMODE},id=disk \ -incoming "exec: cat '${MIG_FIFO}'" h2=$QEMU_HANDLE @@ -101,7 +101,7 @@ echo "Check image pattern" ${QEMU_IO} -c "read -P 0x22 0 4M" "${TEST_IMG}" | _filter_testdir | _filter_qemu_io echo "Running 'qemu-img check -r all \$TEST_IMG'" -"${QEMU_IMG}" check -r all "${TEST_IMG}" 2>&1 | _filter_testdir | _filter_qemu +_check_test_img -r all echo "*** done" rm -f $seq.full diff --git a/tests/qemu-iotests/091.out b/tests/qemu-iotests/091.out index 5017f8c2d9..5ec7b00f13 100644 --- a/tests/qemu-iotests/091.out +++ b/tests/qemu-iotests/091.out @@ -23,6 +23,4 @@ read 4194304/4194304 bytes at offset 0 4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) Running 'qemu-img check -r all $TEST_IMG' No errors were found on the image. -80/16384 = 0.49% allocated, 0.00% fragmented, 0.00% compressed clusters -Image end offset: 5570560 *** done diff --git a/tests/qemu-iotests/092 b/tests/qemu-iotests/092 index e2e0726de1..40ec62b6f1 100755 --- a/tests/qemu-iotests/092 +++ b/tests/qemu-iotests/092 @@ -28,7 +28,7 @@ status=1 # failure is the default! _cleanup() { - rm -f $TEST_IMG.snap + _rm_test_img "$TEST_IMG.snap" _cleanup_test_img } trap "_cleanup; exit \$status" 0 1 2 3 15 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/094 b/tests/qemu-iotests/094 index 9343e09492..2d3e1004d3 100755 --- a/tests/qemu-iotests/094 +++ b/tests/qemu-iotests/094 @@ -30,7 +30,7 @@ _cleanup() { _cleanup_qemu _cleanup_test_img - rm -f "$TEST_DIR/source.$IMGFMT" + _rm_test_img "$TEST_DIR/source.$IMGFMT" } trap "_cleanup; exit \$status" 0 1 2 3 15 @@ -45,7 +45,7 @@ _supported_proto nbd _unsupported_imgopts "subformat=monolithicFlat" "subformat=twoGbMaxExtentFlat" _make_test_img 64M -$QEMU_IMG create -f $IMGFMT "$TEST_DIR/source.$IMGFMT" 64M | _filter_img_create +TEST_IMG_FILE="$TEST_DIR/source.$IMGFMT" IMGPROTO=file _make_test_img 64M _launch_qemu -drive if=none,id=src,file="$TEST_DIR/source.$IMGFMT",format=raw \ -nodefaults diff --git a/tests/qemu-iotests/095 b/tests/qemu-iotests/095 index 58fe174b5e..155ae86aa7 100755 --- a/tests/qemu-iotests/095 +++ b/tests/qemu-iotests/095 @@ -32,8 +32,9 @@ status=1 # failure is the default! _cleanup() { _cleanup_qemu - rm -f "${TEST_IMG}.base" "${TEST_IMG}.snp1" - _cleanup_test_img + _rm_test_img "${TEST_IMG}.base" + _rm_test_img "${TEST_IMG}.snp1" + _cleanup_test_img } trap "_cleanup; exit \$status" 0 1 2 3 15 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/098 b/tests/qemu-iotests/098 index 1c1d1c468f..1e29d96b3d 100755 --- a/tests/qemu-iotests/098 +++ b/tests/qemu-iotests/098 @@ -40,8 +40,10 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 _supported_fmt qcow2 _supported_proto file - -IMGOPTS="compat=1.1" +# The code path we want to test here only works for compat=1.1 images; +# blkdebug can only inject errors on bs->file, so external data files +# do not work with this test +_unsupported_imgopts 'compat=0.10' data_file for event in l1_update empty_image_prepare reftable_update refblock_alloc; do diff --git a/tests/qemu-iotests/099 b/tests/qemu-iotests/099 index c3cf66798a..65e8e92572 100755 --- a/tests/qemu-iotests/099 +++ b/tests/qemu-iotests/099 @@ -29,7 +29,10 @@ status=1 # failure is the default! _cleanup() { - _cleanup_test_img + _cleanup_test_img + _rm_test_img "$TEST_IMG.compare" + rm -f "$TEST_DIR/blkdebug.conf" + } trap "_cleanup; exit \$status" 0 1 2 3 15 @@ -43,8 +46,9 @@ _supported_fmt qcow qcow2 qed vdi vhdx vmdk vpc _supported_proto file _supported_os Linux _require_drivers blkdebug blkverify +# data_file would change the json:{} filenames _unsupported_imgopts "subformat=monolithicFlat" "subformat=twoGbMaxExtentFlat" \ - "subformat=twoGbMaxExtentSparse" + "subformat=twoGbMaxExtentSparse" data_file do_run_qemu() { @@ -121,8 +125,6 @@ echo test_qemu "file.driver=blkdebug,file.image.filename=$TEST_IMG" -rm -f "$TEST_IMG.compare" "$TEST_DIR/blkdebug.conf" - # success, all done echo "*** done" rm -f $seq.full diff --git a/tests/qemu-iotests/103 b/tests/qemu-iotests/103 index 554b9de054..8c1ebe0443 100755 --- a/tests/qemu-iotests/103 +++ b/tests/qemu-iotests/103 @@ -38,8 +38,9 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 _supported_fmt qcow2 _supported_proto file nfs -# Internal snapshots are (currently) impossible with refcount_bits=1 -_unsupported_imgopts 'refcount_bits=1[^0-9]' +# Internal snapshots are (currently) impossible with refcount_bits=1, +# and generally impossible with external data files +_unsupported_imgopts 'refcount_bits=1[^0-9]' data_file IMG_SIZE=64K diff --git a/tests/qemu-iotests/106 b/tests/qemu-iotests/106 index ac47eaa0f5..b5d1ec4078 100755 --- a/tests/qemu-iotests/106 +++ b/tests/qemu-iotests/106 @@ -51,7 +51,7 @@ for create_mode in off falloc full; do echo echo "--- create_mode=$create_mode growth_mode=$growth_mode ---" - IMGOPTS="preallocation=$create_mode" _make_test_img ${CREATION_SIZE}K + _make_test_img -o "preallocation=$create_mode" ${CREATION_SIZE}K $QEMU_IMG resize -f "$IMGFMT" --preallocation=$growth_mode "$TEST_IMG" +${GROWTH_SIZE}K expected_size=0 diff --git a/tests/qemu-iotests/108 b/tests/qemu-iotests/108 index 9c08172237..5f7076fba4 100755 --- a/tests/qemu-iotests/108 +++ b/tests/qemu-iotests/108 @@ -37,12 +37,14 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 . ./common.rc . ./common.filter -# This tests qocw2-specific low-level functionality +# This tests qcow2-specific low-level functionality _supported_fmt qcow2 _supported_proto file _supported_os Linux -# This test directly modifies a refblock so it relies on refcount_bits being 16 -_unsupported_imgopts 'refcount_bits=\([^1]\|.\([^6]\|$\)\)' +# This test directly modifies a refblock so it relies on refcount_bits being 16; +# and the low-level modification it performs are not tuned for external data +# files +_unsupported_imgopts 'refcount_bits=\([^1]\|.\([^6]\|$\)\)' data_file echo echo '=== Repairing an image without any refcount table ===' @@ -65,7 +67,7 @@ echo echo '=== Repairing unreferenced data cluster in new refblock area ===' echo -IMGOPTS='cluster_size=512' _make_test_img 64M +_make_test_img -o 'cluster_size=512' 64M # Allocate the first 128 kB in the image (first refblock) $QEMU_IO -c 'write 0 0x1b200' "$TEST_IMG" | _filter_qemu_io # should be 131072 == 0x20000 diff --git a/tests/qemu-iotests/109 b/tests/qemu-iotests/109 index 9897ceb6cd..a51dd84b3d 100755 --- a/tests/qemu-iotests/109 +++ b/tests/qemu-iotests/109 @@ -29,8 +29,8 @@ status=1 # failure is the default! _cleanup() { _cleanup_qemu - rm -f $TEST_IMG.src - _cleanup_test_img + _rm_test_img "$TEST_IMG.src" + _cleanup_test_img } trap "_cleanup; exit \$status" 0 1 2 3 15 @@ -52,7 +52,7 @@ run_qemu() local qmp_format="$3" local qmp_event="$4" - _launch_qemu -drive file="${source_img}",format=raw,cache=${CACHEMODE},id=src + _launch_qemu -drive file="${source_img}",format=raw,cache=${CACHEMODE},aio=${AIOMODE},id=src _send_qemu_cmd $QEMU_HANDLE "{ 'execute': 'qmp_capabilities' }" "return" _send_qemu_cmd $QEMU_HANDLE \ diff --git a/tests/qemu-iotests/110 b/tests/qemu-iotests/110 index 2ef516baf1..139c02c2cf 100755 --- a/tests/qemu-iotests/110 +++ b/tests/qemu-iotests/110 @@ -28,8 +28,8 @@ status=1 # failure is the default! _cleanup() { - _cleanup_test_img - rm -f "$TEST_IMG.copy" + _cleanup_test_img + _rm_test_img "$TEST_IMG.copy" } trap "_cleanup; exit \$status" 0 1 2 3 15 @@ -67,6 +67,7 @@ echo # Across blkdebug without a config file, you cannot reconstruct filenames, so # qemu is incapable of knowing the directory of the top image from the filename # alone. However, using bdrv_dirname(), it should still work. +# (Filter out the json:{} filename so this test works with external data files) TEST_IMG="json:{ 'driver': '$IMGFMT', 'file': { @@ -82,7 +83,8 @@ TEST_IMG="json:{ } ] } -}" _img_info | _filter_img_info | grep -v 'backing file format' +}" _img_info | _filter_img_info | grep -v 'backing file format' \ + | _filter_json_filename echo echo '=== Backing name is always relative to the backed image ===' @@ -114,7 +116,8 @@ TEST_IMG="json:{ } ] } -}" _img_info | _filter_img_info | grep -v 'backing file format' +}" _img_info | _filter_img_info | grep -v 'backing file format' \ + | _filter_json_filename # success, all done diff --git a/tests/qemu-iotests/110.out b/tests/qemu-iotests/110.out index f60b26390e..f835553a99 100644 --- a/tests/qemu-iotests/110.out +++ b/tests/qemu-iotests/110.out @@ -11,7 +11,7 @@ backing file: t.IMGFMT.base (actual path: TEST_DIR/t.IMGFMT.base) === Non-reconstructable filename === -image: json:{"driver": "IMGFMT", "file": {"set-state.0.event": "read_aio", "image": {"driver": "file", "filename": "TEST_DIR/t.IMGFMT"}, "driver": "blkdebug", "set-state.0.new_state": 42}} +image: json:{ /* filtered */ } file format: IMGFMT virtual size: 64 MiB (67108864 bytes) backing file: t.IMGFMT.base (actual path: TEST_DIR/t.IMGFMT.base) @@ -22,7 +22,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=t.IMGFMT.b === Nodes without a common directory === -image: json:{"driver": "IMGFMT", "file": {"children": [{"driver": "file", "filename": "TEST_DIR/t.IMGFMT"}, {"driver": "file", "filename": "TEST_DIR/t.IMGFMT.copy"}], "driver": "quorum", "vote-threshold": 1}} +image: json:{ /* filtered */ } file format: IMGFMT virtual size: 64 MiB (67108864 bytes) backing file: t.IMGFMT.base (cannot determine actual path) diff --git a/tests/qemu-iotests/111 b/tests/qemu-iotests/111 index 490a5bbcb5..3b43d1bd83 100755 --- a/tests/qemu-iotests/111 +++ b/tests/qemu-iotests/111 @@ -41,8 +41,7 @@ _supported_fmt qed qcow qcow2 vmdk _supported_proto file _unsupported_imgopts "subformat=monolithicFlat" "subformat=twoGbMaxExtentFlat" -$QEMU_IMG create -f $IMGFMT -b "$TEST_IMG.inexistent" "$TEST_IMG" 2>&1 \ - | _filter_testdir | _filter_imgfmt +_make_test_img -b "$TEST_IMG.inexistent" # success, all done echo '*** done' diff --git a/tests/qemu-iotests/112 b/tests/qemu-iotests/112 index 706c10b600..20ff5c224a 100755 --- a/tests/qemu-iotests/112 +++ b/tests/qemu-iotests/112 @@ -40,8 +40,9 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 _supported_fmt qcow2 _supported_proto file # This test will set refcount_bits on its own which would conflict with the -# manual setting; compat will be overridden as well -_unsupported_imgopts refcount_bits 'compat=0.10' +# manual setting; compat will be overridden as well; +# and external data files do not work well with our refcount testing +_unsupported_imgopts refcount_bits 'compat=0.10' data_file print_refcount_bits() { @@ -53,20 +54,20 @@ echo '=== refcount_bits limits ===' echo # Must be positive (non-zero) -IMGOPTS="$IMGOPTS,refcount_bits=0" _make_test_img 64M +_make_test_img -o "refcount_bits=0" 64M # Must be positive (non-negative) -IMGOPTS="$IMGOPTS,refcount_bits=-1" _make_test_img 64M +_make_test_img -o "refcount_bits=-1" 64M # May not exceed 64 -IMGOPTS="$IMGOPTS,refcount_bits=128" _make_test_img 64M +_make_test_img -o "refcount_bits=128" 64M # Must be a power of two -IMGOPTS="$IMGOPTS,refcount_bits=42" _make_test_img 64M +_make_test_img -o "refcount_bits=42" 64M # 1 is the minimum -IMGOPTS="$IMGOPTS,refcount_bits=1" _make_test_img 64M +_make_test_img -o "refcount_bits=1" 64M print_refcount_bits # 64 is the maximum -IMGOPTS="$IMGOPTS,refcount_bits=64" _make_test_img 64M +_make_test_img -o "refcount_bits=64" 64M print_refcount_bits # 16 is the default @@ -78,19 +79,19 @@ echo '=== refcount_bits and compat=0.10 ===' echo # Should work -IMGOPTS="$IMGOPTS,compat=0.10,refcount_bits=16" _make_test_img 64M +_make_test_img -o "compat=0.10,refcount_bits=16" 64M print_refcount_bits # Should not work -IMGOPTS="$IMGOPTS,compat=0.10,refcount_bits=1" _make_test_img 64M -IMGOPTS="$IMGOPTS,compat=0.10,refcount_bits=64" _make_test_img 64M +_make_test_img -o "compat=0.10,refcount_bits=1" 64M +_make_test_img -o "compat=0.10,refcount_bits=64" 64M echo echo '=== Snapshot limit on refcount_bits=1 ===' echo -IMGOPTS="$IMGOPTS,refcount_bits=1" _make_test_img 64M +_make_test_img -o "refcount_bits=1" 64M print_refcount_bits $QEMU_IO -c 'write 0 512' "$TEST_IMG" | _filter_qemu_io @@ -106,7 +107,7 @@ echo echo '=== Snapshot limit on refcount_bits=2 ===' echo -IMGOPTS="$IMGOPTS,refcount_bits=2" _make_test_img 64M +_make_test_img -o "refcount_bits=2" 64M print_refcount_bits $QEMU_IO -c 'write 0 512' "$TEST_IMG" | _filter_qemu_io @@ -124,7 +125,7 @@ echo echo '=== Compressed clusters with refcount_bits=1 ===' echo -IMGOPTS="$IMGOPTS,refcount_bits=1" _make_test_img 64M +_make_test_img -o "refcount_bits=1" 64M print_refcount_bits # Both should fit into a single host cluster; instead of failing to increase the @@ -140,7 +141,7 @@ echo echo '=== MSb set in 64 bit refcount ===' echo -IMGOPTS="$IMGOPTS,refcount_bits=64" _make_test_img 64M +_make_test_img -o "refcount_bits=64" 64M print_refcount_bits $QEMU_IO -c 'write 0 512' "$TEST_IMG" | _filter_qemu_io @@ -158,7 +159,7 @@ echo echo '=== Snapshot on maximum 64 bit refcount value ===' echo -IMGOPTS="$IMGOPTS,refcount_bits=64" _make_test_img 64M +_make_test_img -o "refcount_bits=64" 64M print_refcount_bits $QEMU_IO -c 'write 0 512' "$TEST_IMG" | _filter_qemu_io @@ -239,7 +240,7 @@ echo echo '=== Testing too many references for check ===' echo -IMGOPTS="$IMGOPTS,refcount_bits=1" _make_test_img 64M +_make_test_img -o "refcount_bits=1" 64M print_refcount_bits # This cluster should be created at 0x50000 @@ -262,7 +263,7 @@ echo echo '=== Multiple walks necessary during amend ===' echo -IMGOPTS="$IMGOPTS,refcount_bits=1,cluster_size=512" _make_test_img 64k +_make_test_img -o "refcount_bits=1,cluster_size=512" 64k # Cluster 0 is the image header, clusters 1 to 4 are used by the L1 table, a # single L2 table, the reftable and a single refblock. This creates 58 data diff --git a/tests/qemu-iotests/114 b/tests/qemu-iotests/114 index f90a744fc0..26104fff6c 100755 --- a/tests/qemu-iotests/114 +++ b/tests/qemu-iotests/114 @@ -39,6 +39,8 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 _supported_fmt qcow2 _supported_proto generic _unsupported_proto vxhs +# qcow2.py does not work too well with external data files +_unsupported_imgopts data_file TEST_IMG="$TEST_IMG.base" _make_test_img 64M diff --git a/tests/qemu-iotests/115 b/tests/qemu-iotests/115 index 9ed3cb6a83..d254b18342 100755 --- a/tests/qemu-iotests/115 +++ b/tests/qemu-iotests/115 @@ -64,8 +64,7 @@ echo # least 256 MB. We can achieve that by using preallocation=metadata for an image # which has a guest disk size of 256 MB. -IMGOPTS="$IMGOPTS,refcount_bits=64,cluster_size=512,preallocation=metadata" \ - _make_test_img 256M +_make_test_img -o "refcount_bits=64,cluster_size=512,preallocation=metadata" 256M # We know for sure that the L1 and refcount tables do not overlap with any other # structure because the metadata overlap checks would have caught that case. 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/121 b/tests/qemu-iotests/121 index 90a0424edb..90ea0db737 100755 --- a/tests/qemu-iotests/121 +++ b/tests/qemu-iotests/121 @@ -39,6 +39,9 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 _supported_fmt qcow2 _supported_proto file _supported_os Linux +# Refcount structures are used much differently with external data +# files +_unsupported_imgopts data_file echo echo '=== New refcount structures may not conflict with existing structures ===' @@ -50,7 +53,7 @@ echo # Preallocation speeds up the write operation, but preallocating everything will # destroy the purpose of the write; so preallocate one KB less than what would # cause a reftable growth... -IMGOPTS='preallocation=metadata,cluster_size=1k' _make_test_img 64512K +_make_test_img -o 'preallocation=metadata,cluster_size=1k' 64512K # ...and make the image the desired size afterwards. $QEMU_IMG resize "$TEST_IMG" 65M @@ -73,7 +76,7 @@ echo echo '--- Test 2 ---' echo -IMGOPTS='preallocation=metadata,cluster_size=1k' _make_test_img 64513K +_make_test_img -o 'preallocation=metadata,cluster_size=1k' 64513K # This results in an L1 table growth which in turn results in some clusters at # the start of the image becoming free $QEMU_IMG resize "$TEST_IMG" 65M @@ -96,7 +99,7 @@ echo echo '=== Allocating a new refcount block must not leave holes in the image ===' echo -IMGOPTS='cluster_size=512,refcount_bits=16' _make_test_img 1M +_make_test_img -o 'cluster_size=512,refcount_bits=16' 1M # This results in an image with 256 used clusters: the qcow2 header, # the refcount table, one refcount block, the L1 table, four L2 tables diff --git a/tests/qemu-iotests/122 b/tests/qemu-iotests/122 index 059011ebb1..f7a3ae684a 100755 --- a/tests/qemu-iotests/122 +++ b/tests/qemu-iotests/122 @@ -28,8 +28,10 @@ status=1 # failure is the default! _cleanup() { - rm -f "$TEST_IMG".[123] - _cleanup_test_img + for img in "$TEST_IMG".[123]; do + _rm_test_img "$img" + done + _cleanup_test_img } trap "_cleanup; exit \$status" 0 1 2 3 15 @@ -274,6 +276,20 @@ $QEMU_IMG convert -O $IMGFMT -n "$TEST_IMG" "$TEST_IMG".orig $QEMU_IMG compare "$TEST_IMG" "$TEST_IMG".orig +echo +echo '=== -n -B to an image without a backing file ===' +echo + +# Base for the output +TEST_IMG="$TEST_IMG".base _make_test_img 64M + +# Output that does have $TEST_IMG.base set as its (implicit) backing file +TEST_IMG="$TEST_IMG".orig _make_test_img 64M + +# Convert with -n, which should not confuse -B with "target BDS has a +# backing file" +$QEMU_IMG convert -O $IMGFMT -B "$TEST_IMG".base -n "$TEST_IMG" "$TEST_IMG".orig + # success, all done echo '*** done' rm -f $seq.full diff --git a/tests/qemu-iotests/122.out b/tests/qemu-iotests/122.out index 849b6cc2ef..1a35951a80 100644 --- a/tests/qemu-iotests/122.out +++ b/tests/qemu-iotests/122.out @@ -228,4 +228,9 @@ Formatting 'TEST_DIR/t.IMGFMT.orig', fmt=IMGFMT size=67108864 wrote 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) Images are identical. + +=== -n -B to an image without a backing file === + +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT.orig', fmt=IMGFMT size=67108864 *** done diff --git a/tests/qemu-iotests/123 b/tests/qemu-iotests/123 index d33950eb54..01b771c76e 100755 --- a/tests/qemu-iotests/123 +++ b/tests/qemu-iotests/123 @@ -29,7 +29,7 @@ status=1 # failure is the default! _cleanup() { _cleanup_test_img - rm -f "$SRC_IMG" + _rm_test_img "$SRC_IMG" } trap "_cleanup; exit \$status" 0 1 2 3 15 @@ -44,7 +44,7 @@ _supported_os Linux SRC_IMG="$TEST_DIR/source.$IMGFMT" _make_test_img 1M -$QEMU_IMG create -f $IMGFMT "$SRC_IMG" 1M | _filter_img_create +TEST_IMG_FILE=$SRC_IMG IMGPROTO=file _make_test_img 1M $QEMU_IO -c 'write -P 42 0 1M' "$SRC_IMG" | _filter_qemu_io 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/125 b/tests/qemu-iotests/125 index 4e31aa4e5f..d510984045 100755 --- a/tests/qemu-iotests/125 +++ b/tests/qemu-iotests/125 @@ -114,7 +114,7 @@ for GROWTH_SIZE in 16 48 80; do for growth_mode in off metadata falloc full; do echo "--- cluster_size=$cluster_size growth_size=$GROWTH_SIZE create_mode=$create_mode growth_mode=$growth_mode ---" - IMGOPTS="preallocation=$create_mode,cluster_size=$cluster_size" _make_test_img ${CREATION_SIZE} + _make_test_img -o "preallocation=$create_mode,cluster_size=$cluster_size" ${CREATION_SIZE} $QEMU_IMG resize -f "$IMGFMT" --preallocation=$growth_mode "$TEST_IMG" +${GROWTH_SIZE}K host_size_0=$(get_image_size_on_host) 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/137 b/tests/qemu-iotests/137 index 089821da0c..7ae86892f7 100755 --- a/tests/qemu-iotests/137 +++ b/tests/qemu-iotests/137 @@ -117,7 +117,7 @@ $QEMU_IO \ -c "reopen -o cache-clean-interval=-1" \ "$TEST_IMG" | _filter_qemu_io -IMGOPTS="cluster_size=256k" _make_test_img 32P +_make_test_img -o "cluster_size=256k" 32P $QEMU_IO \ -c "reopen -o l2-cache-entry-size=512,l2-cache-size=1T" \ "$TEST_IMG" | _filter_qemu_io @@ -138,14 +138,21 @@ $QEMU_IO \ "$TEST_IMG" 2>&1 | _filter_qemu_io # The dirty bit must not be set -$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features +# (Filter the external data file bit) +if $PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features \ + | grep -q '\<0\>' +then + echo 'ERROR: Dirty bit set' +else + echo 'OK: Dirty bit not set' +fi # Similarly we can test whether corruption detection has been enabled: -# Create L1/L2, overwrite first entry in refcount block, allocate something. +# Create L1, overwrite refcounts, force allocation of L2 by writing +# data. # Disabling the checks should fail, so the corruption must be detected. _make_test_img 64M -$QEMU_IO -c "write 0 64k" "$TEST_IMG" | _filter_qemu_io -poke_file "$TEST_IMG" "$((0x20000))" "\x00\x00" +poke_file "$TEST_IMG" "$((0x20000))" "\x00\x00\x00\x00\x00\x00\x00\x00" $QEMU_IO \ -c "reopen -o overlap-check=none,lazy-refcounts=42" \ -c "write 64k 64k" \ diff --git a/tests/qemu-iotests/137.out b/tests/qemu-iotests/137.out index 1c6569eb2c..86377c80cd 100644 --- a/tests/qemu-iotests/137.out +++ b/tests/qemu-iotests/137.out @@ -36,11 +36,9 @@ qemu-io: Unsupported value 'blubb' for qcow2 option 'overlap-check'. Allowed are wrote 512/512 bytes at offset 0 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) ./common.rc: Killed ( VALGRIND_QEMU="${VALGRIND_QEMU_IO}" _qemu_proc_exec "${VALGRIND_LOGFILE}" "$QEMU_IO_PROG" $QEMU_IO_ARGS "$@" ) -incompatible_features 0x0 +OK: Dirty bit not set Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 -wrote 65536/65536 bytes at offset 0 -64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) qemu-io: Parameter 'lazy-refcounts' expects 'on' or 'off' -qcow2: Marking image as corrupt: Preventing invalid write on metadata (overlaps with qcow2_header); further corruption events will be suppressed +qcow2: Marking image as corrupt: Preventing invalid allocation of L2 table at offset 0; further corruption events will be suppressed write failed: Input/output error *** done diff --git a/tests/qemu-iotests/138 b/tests/qemu-iotests/138 index 6a731370db..1d5b0bed6d 100755 --- a/tests/qemu-iotests/138 +++ b/tests/qemu-iotests/138 @@ -36,17 +36,21 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 . ./common.rc . ./common.filter -# This tests qocw2-specific low-level functionality +# This tests qcow2-specific low-level functionality _supported_fmt qcow2 _supported_proto file _supported_os Linux +# With an external data file, data clusters are not refcounted +# (so qemu-img check would not do much); +# we want to modify the refcounts, so we need them to have a specific +# format (namely u16) +_unsupported_imgopts data_file 'refcount_bits=\([^1]\|.\([^6]\|$\)\)' echo echo '=== Check on an image with a multiple of 2^32 clusters ===' echo -IMGOPTS=$(_optstr_add "$IMGOPTS" "cluster_size=512") \ - _make_test_img 512 +_make_test_img -o "cluster_size=512" 512 # Allocate L2 table $QEMU_IO -c 'write 0 512' "$TEST_IMG" | _filter_qemu_io @@ -63,6 +67,41 @@ poke_file "$TEST_IMG" $((2048 + 8)) "\x00\x80\x00\x00\x00\x00\x00\x00" # allocate memory", we have an error showing that l2 entry is invalid. _check_test_img +echo +echo '=== Check leaks-fixed/corruptions-fixed report' +echo + +# After leaks and corruptions were fixed, those numbers should be +# reported by qemu-img check +_make_test_img 64k + +# Allocate data cluster +$QEMU_IO -c 'write 0 64k' "$TEST_IMG" | _filter_qemu_io + +reftable_ofs=$(peek_file_be "$TEST_IMG" 48 8) +refblock_ofs=$(peek_file_be "$TEST_IMG" $reftable_ofs 8) + +# Introduce a leak: Make the image header's refcount 2 +poke_file_be "$TEST_IMG" "$refblock_ofs" 2 2 + +l1_ofs=$(peek_file_be "$TEST_IMG" 40 8) + +# Introduce a corruption: Drop the COPIED flag from the (first) L1 entry +l1_entry=$(peek_file_be "$TEST_IMG" $l1_ofs 8) +l1_entry=$((l1_entry & ~(1 << 63))) +poke_file_be "$TEST_IMG" $l1_ofs 8 $l1_entry + +echo +# Should print the number of corruptions and leaks fixed +# (Filter out all JSON fields (recognizable by their four-space +# indentation), but keep the "-fixed" fields (by removing two spaces +# from their indentation)) +# (Also filter out the L1 entry, because why not) +_check_test_img -r all --output=json \ + | sed -e 's/^ \(.*\)-fixed"/\1-fixed"/' \ + -e '/^ /d' \ + -e "s/\\([^0-9a-f]\\)$(printf %x $l1_entry)\\([^0-9a-f]\\)/\1L1_ENTRY_VALUE\2/" + # success, all done echo "*** done" rm -f $seq.full diff --git a/tests/qemu-iotests/138.out b/tests/qemu-iotests/138.out index aca7d47a80..79681e7cc9 100644 --- a/tests/qemu-iotests/138.out +++ b/tests/qemu-iotests/138.out @@ -9,4 +9,18 @@ ERROR: counting reference for region exceeding the end of the file by one cluste 1 errors were found on the image. Data may be corrupted, or further writes to the image may corrupt it. + +=== Check leaks-fixed/corruptions-fixed report + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=65536 +wrote 65536/65536 bytes at offset 0 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +Leaked cluster 0 refcount=2 reference=1 +Repairing cluster 0 refcount=2 reference=1 +Repairing OFLAG_COPIED L2 cluster: l1_index=0 l1_entry=L1_ENTRY_VALUE refcount=1 +{ + "corruptions-fixed": 1, + "leaks-fixed": 1, +} *** done diff --git a/tests/qemu-iotests/139 b/tests/qemu-iotests/139 index cbb5a76530..7120d3142b 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 # @@ -344,9 +344,6 @@ class TestBlockdevDel(iotests.QMPTestCase): @iotests.skip_if_unsupported(['quorum']) def testQuorum(self): - if not iotests.supports_quorum(): - return - self.addQuorum('quorum0', 'node0', 'node1') # We cannot remove the children of a Quorum device self.delBlockDriverState('node0', expect_error = True) diff --git a/tests/qemu-iotests/141 b/tests/qemu-iotests/141 index 8c2ae79f2b..5192d256e3 100755 --- a/tests/qemu-iotests/141 +++ b/tests/qemu-iotests/141 @@ -30,7 +30,9 @@ _cleanup() { _cleanup_qemu _cleanup_test_img - rm -f "$TEST_DIR"/{b,m,o}.$IMGFMT + for img in "$TEST_DIR"/{b,m,o}.$IMGFMT; do + _rm_test_img "$img" + done } trap "_cleanup; exit \$status" 0 1 2 3 15 diff --git a/tests/qemu-iotests/141.out b/tests/qemu-iotests/141.out index 3645675ce8..263b680bdf 100644 --- a/tests/qemu-iotests/141.out +++ b/tests/qemu-iotests/141.out @@ -13,6 +13,8 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/m. Formatting 'TEST_DIR/o.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.IMGFMT backing_fmt=IMGFMT {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job0"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job0"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "paused", "id": "job0"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job0"}} {'execute': 'blockdev-del', 'arguments': {'node-name': 'drv0'}} {"error": {"class": "GenericError", "desc": "Node 'drv0' is busy: node is used as backing hd of 'NODE_NAME'"}} {'execute': 'block-job-cancel', 'arguments': {'device': 'job0'}} diff --git a/tests/qemu-iotests/142 b/tests/qemu-iotests/142 index 6b62271876..daefcbaa58 100755 --- a/tests/qemu-iotests/142 +++ b/tests/qemu-iotests/142 @@ -29,7 +29,7 @@ status=1 # failure is the default! _cleanup() { _cleanup_test_img - rm -f $TEST_IMG.snap + _rm_test_img "$TEST_IMG.snap" } trap "_cleanup; exit \$status" 0 1 2 3 15 diff --git a/tests/qemu-iotests/144 b/tests/qemu-iotests/144 index 011ed4f2bc..4569ac0b4b 100755 --- a/tests/qemu-iotests/144 +++ b/tests/qemu-iotests/144 @@ -34,7 +34,9 @@ TMP_SNAP2=${TEST_DIR}/tmp2.qcow2 _cleanup() { _cleanup_qemu - rm -f "${TEST_IMG}" "${TMP_SNAP1}" "${TMP_SNAP2}" + for img in "${TEST_IMG}" "${TMP_SNAP1}" "${TMP_SNAP2}"; do + _rm_test_img "$img" + done } trap "_cleanup; exit \$status" 0 1 2 3 15 diff --git a/tests/qemu-iotests/147 b/tests/qemu-iotests/147 index 03fc2fabcf..d7a9f31089 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 # @@ -24,7 +24,7 @@ import socket import stat import time import iotests -from iotests import cachemode, imgfmt, qemu_img, qemu_nbd, qemu_nbd_early_pipe +from iotests import cachemode, aiomode, imgfmt, qemu_img, qemu_nbd, qemu_nbd_early_pipe NBD_PORT_START = 32768 NBD_PORT_END = NBD_PORT_START + 1024 @@ -134,7 +134,8 @@ class BuiltinNBD(NBDBlockdevAddBase): self.server.add_drive_raw('if=none,id=nbd-export,' + 'file=%s,' % test_img + 'format=%s,' % imgfmt + - 'cache=%s' % cachemode) + 'cache=%s,' % cachemode + + 'aio=%s' % aiomode) self.server.launch() def tearDown(self): 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/153 b/tests/qemu-iotests/153 index c969a1a16f..2b13111768 100755 --- a/tests/qemu-iotests/153 +++ b/tests/qemu-iotests/153 @@ -30,13 +30,9 @@ status=1 # failure is the default! _cleanup() { _cleanup_test_img - rm -f "${TEST_IMG}.base" - rm -f "${TEST_IMG}.overlay" - rm -f "${TEST_IMG}.convert" - rm -f "${TEST_IMG}.a" - rm -f "${TEST_IMG}.b" - rm -f "${TEST_IMG}.c" - rm -f "${TEST_IMG}.lnk" + for img in "${TEST_IMG}".{base,overlay,convert,a,b,c,lnk}; do + _rm_test_img "$img" + done } trap "_cleanup; exit \$status" 0 1 2 3 15 @@ -98,7 +94,7 @@ for opts1 in "" "read-only=on" "read-only=on,force-share=on"; do echo echo "== Creating test image ==" - $QEMU_IMG create -f $IMGFMT "${TEST_IMG}" -b ${TEST_IMG}.base | _filter_img_create + _make_test_img -b "${TEST_IMG}.base" echo echo "== Launching QEMU, opts: '$opts1' ==" diff --git a/tests/qemu-iotests/155 b/tests/qemu-iotests/155 index e19485911c..571bce9de4 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 @@ -45,10 +45,18 @@ target_img = os.path.join(iotests.test_dir, 'target.' + iotests.imgfmt) # image during runtime, only makes sense if # target_blockdev_backing is not None # (None: same as target_backing) +# target_open_with_backing: If True, the target image is added with its backing +# chain opened right away. If False, blockdev-add +# opens it without a backing file and job completion +# is supposed to open the backing chain. +# use_iothread: If True, an iothread is configured for the virtio-blk device +# that uses the image being mirrored class BaseClass(iotests.QMPTestCase): target_blockdev_backing = None target_real_backing = None + target_open_with_backing = True + use_iothread = False def setUp(self): qemu_img('create', '-f', iotests.imgfmt, back0_img, '1440K') @@ -64,7 +72,16 @@ class BaseClass(iotests.QMPTestCase): 'file': {'driver': 'file', 'filename': source_img}} self.vm.add_blockdev(self.vm.qmp_to_opts(blockdev)) - self.vm.add_device('virtio-blk,id=qdev0,drive=source') + + if self.use_iothread: + self.vm.add_object('iothread,id=iothread0') + iothread = ",iothread=iothread0" + else: + iothread = "" + + self.vm.add_device('virtio-scsi%s' % iothread) + self.vm.add_device('scsi-hd,id=qdev0,drive=source') + self.vm.launch() self.assertIntactSourceBackingChain() @@ -80,9 +97,13 @@ class BaseClass(iotests.QMPTestCase): options = { 'node-name': 'target', 'driver': iotests.imgfmt, 'file': { 'driver': 'file', + 'node-name': 'target-file', 'filename': target_img } } - if self.target_blockdev_backing: - options['backing'] = self.target_blockdev_backing + + if not self.target_open_with_backing: + options['backing'] = None + elif self.target_blockdev_backing: + options['backing'] = self.target_blockdev_backing result = self.vm.qmp('blockdev-add', **options) self.assert_qmp(result, 'return', {}) @@ -147,10 +168,14 @@ class BaseClass(iotests.QMPTestCase): # cmd: Mirroring command to execute, either drive-mirror or blockdev-mirror class MirrorBaseClass(BaseClass): + def openBacking(self): + pass + def runMirror(self, sync): if self.cmd == 'blockdev-mirror': result = self.vm.qmp(self.cmd, job_id='mirror-job', device='source', - sync=sync, target='target') + sync=sync, target='target', + auto_finalize=False) else: if self.existing: mode = 'existing' @@ -159,38 +184,31 @@ class MirrorBaseClass(BaseClass): result = self.vm.qmp(self.cmd, job_id='mirror-job', device='source', sync=sync, target=target_img, format=iotests.imgfmt, mode=mode, - node_name='target') + node_name='target', auto_finalize=False) self.assert_qmp(result, 'return', {}) - self.vm.event_wait('BLOCK_JOB_READY') - - result = self.vm.qmp('block-job-complete', device='mirror-job') - self.assert_qmp(result, 'return', {}) - - self.vm.event_wait('BLOCK_JOB_COMPLETED') + self.vm.run_job('mirror-job', use_log=False, auto_finalize=False, + pre_finalize=self.openBacking, auto_dismiss=True) def testFull(self): self.runMirror('full') - node = self.findBlockNode('target', - '/machine/peripheral/qdev0/virtio-backend') + node = self.findBlockNode('target', 'qdev0') self.assertCorrectBackingImage(node, None) self.assertIntactSourceBackingChain() def testTop(self): self.runMirror('top') - node = self.findBlockNode('target', - '/machine/peripheral/qdev0/virtio-backend') + node = self.findBlockNode('target', 'qdev0') self.assertCorrectBackingImage(node, back2_img) self.assertIntactSourceBackingChain() def testNone(self): self.runMirror('none') - node = self.findBlockNode('target', - '/machine/peripheral/qdev0/virtio-backend') + node = self.findBlockNode('target', 'qdev0') self.assertCorrectBackingImage(node, source_img) self.assertIntactSourceBackingChain() @@ -226,6 +244,44 @@ class TestBlockdevMirrorForcedBacking(MirrorBaseClass): target_blockdev_backing = { 'driver': 'null-co' } target_real_backing = 'null-co://' +# Attach the backing chain only during completion, with blockdev-reopen +class TestBlockdevMirrorReopen(MirrorBaseClass): + cmd = 'blockdev-mirror' + existing = True + target_backing = 'null-co://' + target_open_with_backing = False + + def openBacking(self): + if not self.target_open_with_backing: + result = self.vm.qmp('blockdev-add', node_name="backing", + driver="null-co") + self.assert_qmp(result, 'return', {}) + result = self.vm.qmp('x-blockdev-reopen', node_name="target", + driver=iotests.imgfmt, file="target-file", + backing="backing") + self.assert_qmp(result, 'return', {}) + +class TestBlockdevMirrorReopenIothread(TestBlockdevMirrorReopen): + use_iothread = True + +# Attach the backing chain only during completion, with blockdev-snapshot +class TestBlockdevMirrorSnapshot(MirrorBaseClass): + cmd = 'blockdev-mirror' + existing = True + target_backing = 'null-co://' + target_open_with_backing = False + + def openBacking(self): + if not self.target_open_with_backing: + result = self.vm.qmp('blockdev-add', node_name="backing", + driver="null-co") + self.assert_qmp(result, 'return', {}) + result = self.vm.qmp('blockdev-snapshot', node="backing", + overlay="target") + self.assert_qmp(result, 'return', {}) + +class TestBlockdevMirrorSnapshotIothread(TestBlockdevMirrorSnapshot): + use_iothread = True class TestCommit(BaseClass): existing = False @@ -242,8 +298,7 @@ class TestCommit(BaseClass): self.vm.event_wait('BLOCK_JOB_COMPLETED') - node = self.findBlockNode(None, - '/machine/peripheral/qdev0/virtio-backend') + node = self.findBlockNode(None, 'qdev0') self.assert_qmp(node, 'image' + '/backing-image' * 0 + '/filename', back1_img) self.assert_qmp(node, 'image' + '/backing-image' * 1 + '/filename', diff --git a/tests/qemu-iotests/155.out b/tests/qemu-iotests/155.out index 4176bb9402..ed714d5263 100644 --- a/tests/qemu-iotests/155.out +++ b/tests/qemu-iotests/155.out @@ -1,5 +1,5 @@ -................... +............................... ---------------------------------------------------------------------- -Ran 19 tests +Ran 31 tests OK diff --git a/tests/qemu-iotests/156 b/tests/qemu-iotests/156 index 2ffa3ca942..5559df63a5 100755 --- a/tests/qemu-iotests/156 +++ b/tests/qemu-iotests/156 @@ -37,7 +37,9 @@ status=1 # failure is the default! _cleanup() { _cleanup_qemu - rm -f "$TEST_IMG"{,.target}{,.backing,.overlay} + for img in "$TEST_IMG"{,.target}{,.backing,.overlay}; do + _rm_test_img "$img" + done } trap "_cleanup; exit \$status" 0 1 2 3 15 @@ -49,6 +51,8 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 _supported_fmt qcow2 qed _supported_proto generic _unsupported_proto vxhs +# Copying files around with cp does not work with external data files +_unsupported_imgopts data_file # Create source disk TEST_IMG="$TEST_IMG.backing" _make_test_img 1M @@ -120,7 +124,9 @@ _send_qemu_cmd $QEMU_HANDLE \ '"status": "null"' # Remove the source images -rm -f "$TEST_IMG{,.backing,.overlay}" +for img in "$TEST_IMG{,.backing,.overlay}"; do + _rm_test_img "$img" +done echo diff --git a/tests/qemu-iotests/159 b/tests/qemu-iotests/159 index 2557140ac2..f9690053a2 100755 --- a/tests/qemu-iotests/159 +++ b/tests/qemu-iotests/159 @@ -28,7 +28,7 @@ status=1 _cleanup() { _cleanup_test_img - rm -f "$TEST_IMG.out" + _rm_test_img "$TEST_IMG.out" } trap "_cleanup; exit \$status" 0 1 2 3 15 diff --git a/tests/qemu-iotests/160 b/tests/qemu-iotests/160 index df89d3864b..0572b5ae9a 100755 --- a/tests/qemu-iotests/160 +++ b/tests/qemu-iotests/160 @@ -28,7 +28,8 @@ status=1 _cleanup() { _cleanup_test_img - rm -f "$TEST_IMG.out" "$TEST_IMG.out.dd" + _rm_test_img "$TEST_IMG.out" + _rm_test_img "$TEST_IMG.out.dd" } trap "_cleanup; exit \$status" 0 1 2 3 15 diff --git a/tests/qemu-iotests/161 b/tests/qemu-iotests/161 index 456a4bd8c4..f572a19af2 100755 --- a/tests/qemu-iotests/161 +++ b/tests/qemu-iotests/161 @@ -30,8 +30,8 @@ status=1 # failure is the default! _cleanup() { _cleanup_test_img - rm -f "$TEST_IMG.base" - rm -f "$TEST_IMG.int" + _rm_test_img "$TEST_IMG.base" + _rm_test_img "$TEST_IMG.int" } trap "_cleanup; exit \$status" 0 1 2 3 15 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 . # -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/170 b/tests/qemu-iotests/170 index 05dd6ed6c3..6c8f0e8085 100755 --- a/tests/qemu-iotests/170 +++ b/tests/qemu-iotests/170 @@ -28,7 +28,7 @@ status=1 _cleanup() { _cleanup_test_img - rm -f "$TEST_IMG.out" + _rm_test_img "$TEST_IMG.out" } trap "_cleanup; exit \$status" 0 1 2 3 15 diff --git a/tests/qemu-iotests/172 b/tests/qemu-iotests/172 index d67997e5f6..7195fb895a 100755 --- a/tests/qemu-iotests/172 +++ b/tests/qemu-iotests/172 @@ -28,9 +28,9 @@ status=1 # failure is the default! _cleanup() { - _cleanup_test_img - rm -f "$TEST_IMG.2" - rm -f "$TEST_IMG.3" + _cleanup_test_img + _rm_test_img "$TEST_IMG.2" + _rm_test_img "$TEST_IMG.3" } trap "_cleanup; exit \$status" 0 1 2 3 15 diff --git a/tests/qemu-iotests/173 b/tests/qemu-iotests/173 index 29dcaa1960..ec6d1705e5 100755 --- a/tests/qemu-iotests/173 +++ b/tests/qemu-iotests/173 @@ -29,7 +29,8 @@ status=1 # failure is the default! _cleanup() { _cleanup_qemu - rm -f "${QEMU_TEST_DIR}/image.base" "${QEMU_TEST_DIR}/image.snp1" + _rm_test_img "${TEST_DIR}/image.base" + _rm_test_img "${TEST_DIR}/image.snp1" _cleanup_test_img } trap "_cleanup; exit \$status" 0 1 2 3 15 diff --git a/tests/qemu-iotests/174 b/tests/qemu-iotests/174 index 0a952a73fd..e2f14a38c6 100755 --- a/tests/qemu-iotests/174 +++ b/tests/qemu-iotests/174 @@ -40,7 +40,7 @@ _unsupported_fmt raw size=256K -IMGFMT=raw IMGKEYSECRET= IMGOPTS= _make_test_img $size | _filter_imgfmt +IMGFMT=raw IMGKEYSECRET= _make_test_img --no-opts $size | _filter_imgfmt echo echo "== reading wrong format should fail ==" diff --git a/tests/qemu-iotests/175 b/tests/qemu-iotests/175 index 55db2803ed..020ed8e61f 100755 --- a/tests/qemu-iotests/175 +++ b/tests/qemu-iotests/175 @@ -95,7 +95,7 @@ stat -c "size=%s, blocks=%b" $TEST_IMG | _filter_blocks $extra_blocks $min_block for mode in off full falloc; do echo echo "== creating image with preallocation $mode ==" - IMGOPTS=preallocation=$mode _make_test_img $size | _filter_imgfmt + _make_test_img -o preallocation=$mode $size | _filter_imgfmt stat -c "size=%s, blocks=%b" $TEST_IMG | _filter_blocks $extra_blocks $min_blocks $size done diff --git a/tests/qemu-iotests/176 b/tests/qemu-iotests/176 index 50df4c00fa..117c8b6954 100755 --- a/tests/qemu-iotests/176 +++ b/tests/qemu-iotests/176 @@ -47,8 +47,11 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 _supported_fmt qcow2 _supported_proto file _supported_os Linux -# Persistent dirty bitmaps require compat=1.1 -_unsupported_imgopts 'compat=0.10' +# Persistent dirty bitmaps require compat=1.1; +# Internal snapshots forbid using an external data file +# (they work with refcount_bits=1 here, though, because there actually +# is no data when creating the snapshot) +_unsupported_imgopts 'compat=0.10' data_file run_qemu() { diff --git a/tests/qemu-iotests/177 b/tests/qemu-iotests/177 index 752d29f8ad..eadc2c7ef6 100755 --- a/tests/qemu-iotests/177 +++ b/tests/qemu-iotests/177 @@ -89,8 +89,9 @@ verify_io() { if ($QEMU_IMG info -f "$IMGFMT" "$TEST_IMG" | grep "compat: 0.10" > /dev/null); then - # For v2 images, discarded clusters are read from the backing file - discarded=11 + # In v2 images clusters are not discarded when there is a backing file + # so the previous value is read + discarded=22 else # Discarded clusters are zeroed for v3 or later discarded=0 diff --git a/tests/qemu-iotests/178 b/tests/qemu-iotests/178 index 21231cadd3..7cf0e27154 100755 --- a/tests/qemu-iotests/178 +++ b/tests/qemu-iotests/178 @@ -29,7 +29,7 @@ status=1 # failure is the default! _cleanup() { _cleanup_test_img - rm -f "$TEST_IMG.converted" + _rm_test_img "$TEST_IMG.converted" } trap "_cleanup; exit \$status" 0 1 2 3 15 @@ -50,7 +50,7 @@ _make_test_img 1G $QEMU_IMG measure # missing arguments $QEMU_IMG measure --size 2G "$TEST_IMG" # only one allowed $QEMU_IMG measure "$TEST_IMG" a # only one filename allowed -$QEMU_IMG measure --object secret,id=sec0,data=MTIzNDU2,format=base64 # missing filename +$QEMU_IMG measure --object secret,id=sec0,data=MTIzNDU2,format=base64 # size or filename needed $QEMU_IMG measure --image-opts # missing filename $QEMU_IMG measure -f qcow2 # missing filename $QEMU_IMG measure -l snap1 # missing filename @@ -62,8 +62,8 @@ $QEMU_IMG measure -O foo "$TEST_IMG" # unknown image file format make_test_img_with_fmt() { # Shadow global variables within this function - local IMGFMT="$1" IMGOPTS="" - _make_test_img "$2" + local IMGFMT="$1" + _make_test_img --no-opts "$2" } qemu_io_with_fmt() { diff --git a/tests/qemu-iotests/178.out.qcow2 b/tests/qemu-iotests/178.out.qcow2 index 9e7d8c44df..f59bf4b2fb 100644 --- a/tests/qemu-iotests/178.out.qcow2 +++ b/tests/qemu-iotests/178.out.qcow2 @@ -5,10 +5,10 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 qemu-img: Either --size N or one filename must be specified. qemu-img: --size N cannot be used together with a filename. qemu-img: At most one filename argument is allowed. -qemu-img: --object, --image-opts, -f, and -l require a filename argument. -qemu-img: --object, --image-opts, -f, and -l require a filename argument. -qemu-img: --object, --image-opts, -f, and -l require a filename argument. -qemu-img: --object, --image-opts, -f, and -l require a filename argument. +qemu-img: Either --size N or one filename must be specified. +qemu-img: --image-opts, -f, and -l require a filename argument. +qemu-img: --image-opts, -f, and -l require a filename argument. +qemu-img: --image-opts, -f, and -l require a filename argument. qemu-img: Invalid option list: , qemu-img: Invalid parameter 'snapshot.foo' qemu-img: Failed in parsing snapshot param 'snapshot.foo' diff --git a/tests/qemu-iotests/178.out.raw b/tests/qemu-iotests/178.out.raw index 6478365905..404ca908d8 100644 --- a/tests/qemu-iotests/178.out.raw +++ b/tests/qemu-iotests/178.out.raw @@ -5,10 +5,10 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 qemu-img: Either --size N or one filename must be specified. qemu-img: --size N cannot be used together with a filename. qemu-img: At most one filename argument is allowed. -qemu-img: --object, --image-opts, -f, and -l require a filename argument. -qemu-img: --object, --image-opts, -f, and -l require a filename argument. -qemu-img: --object, --image-opts, -f, and -l require a filename argument. -qemu-img: --object, --image-opts, -f, and -l require a filename argument. +qemu-img: Either --size N or one filename must be specified. +qemu-img: --image-opts, -f, and -l require a filename argument. +qemu-img: --image-opts, -f, and -l require a filename argument. +qemu-img: --image-opts, -f, and -l require a filename argument. qemu-img: Invalid option list: , qemu-img: Invalid parameter 'snapshot.foo' qemu-img: Failed in parsing snapshot param 'snapshot.foo' diff --git a/tests/qemu-iotests/181 b/tests/qemu-iotests/181 index 378c2899d1..438c2dcd80 100755 --- a/tests/qemu-iotests/181 +++ b/tests/qemu-iotests/181 @@ -58,20 +58,20 @@ qemu_comm_method="monitor" if [ "$IMGOPTSSYNTAX" = "true" ]; then _launch_qemu \ - -drive "${TEST_IMG}",cache=${CACHEMODE},id=disk + -drive "${TEST_IMG}",cache=${CACHEMODE},aio=$AIOMODE,id=disk else _launch_qemu \ - -drive file="${TEST_IMG}",cache=${CACHEMODE},driver=$IMGFMT,id=disk + -drive file="${TEST_IMG}",cache=${CACHEMODE},aio=$AIOMODE,driver=$IMGFMT,id=disk fi src=$QEMU_HANDLE if [ "$IMGOPTSSYNTAX" = "true" ]; then _launch_qemu \ - -drive "${TEST_IMG}",cache=${CACHEMODE},id=disk \ + -drive "${TEST_IMG}",cache=${CACHEMODE},aio=$AIOMODE,id=disk \ -incoming "unix:${MIG_SOCKET}" else _launch_qemu \ - -drive file="${TEST_IMG}",cache=${CACHEMODE},driver=$IMGFMT,id=disk \ + -drive file="${TEST_IMG}",cache=${CACHEMODE},aio=$AIOMODE,driver=$IMGFMT,id=disk \ -incoming "unix:${MIG_SOCKET}" fi dest=$QEMU_HANDLE diff --git a/tests/qemu-iotests/182 b/tests/qemu-iotests/182 index 1ccb850055..56a2dd58e6 100755 --- a/tests/qemu-iotests/182 +++ b/tests/qemu-iotests/182 @@ -30,7 +30,7 @@ status=1 # failure is the default! _cleanup() { _cleanup_test_img - rm -f "$TEST_IMG.overlay" + _rm_test_img "$TEST_IMG.overlay" rm -f "$SOCK_DIR/nbd.socket" } trap "_cleanup; exit \$status" 0 1 2 3 15 diff --git a/tests/qemu-iotests/183 b/tests/qemu-iotests/183 index bced83fae0..acdbefa310 100755 --- a/tests/qemu-iotests/183 +++ b/tests/qemu-iotests/183 @@ -31,7 +31,7 @@ MIG_SOCKET="${SOCK_DIR}/migrate" _cleanup() { rm -f "${MIG_SOCKET}" - rm -f "${TEST_IMG}.dest" + _rm_test_img "${TEST_IMG}.dest" _cleanup_test_img _cleanup_qemu } @@ -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 @@ -56,12 +57,12 @@ echo qemu_comm_method="qmp" _launch_qemu \ - -drive file="${TEST_IMG}",cache=$CACHEMODE,driver=$IMGFMT,id=disk + -drive file="${TEST_IMG}",cache=$CACHEMODE,aio=$AIOMODE,driver=$IMGFMT,id=disk src=$QEMU_HANDLE _send_qemu_cmd $src "{ 'execute': 'qmp_capabilities' }" 'return' _launch_qemu \ - -drive file="${TEST_IMG}.dest",cache=$CACHEMODE,driver=$IMGFMT,id=disk \ + -drive file="${TEST_IMG}.dest",cache=$CACHEMODE,aio=$AIOMODE,driver=$IMGFMT,id=disk \ -incoming "unix:${MIG_SOCKET}" dest=$QEMU_HANDLE _send_qemu_cmd $dest "{ 'execute': 'qmp_capabilities' }" 'return' diff --git a/tests/qemu-iotests/185 b/tests/qemu-iotests/185 index 454ff600cc..fd5e6ebe11 100755 --- a/tests/qemu-iotests/185 +++ b/tests/qemu-iotests/185 @@ -28,8 +28,8 @@ status=1 # failure is the default! _cleanup() { - rm -f "${TEST_IMG}.mid" - rm -f "${TEST_IMG}.copy" + _rm_test_img "${TEST_IMG}.mid" + _rm_test_img "${TEST_IMG}.copy" _cleanup_test_img _cleanup_qemu } @@ -54,7 +54,7 @@ echo qemu_comm_method="qmp" _launch_qemu \ - -drive file="${TEST_IMG}.base",cache=$CACHEMODE,driver=$IMGFMT,id=disk + -drive file="${TEST_IMG}.base",cache=$CACHEMODE,aio=$AIOMODE,driver=$IMGFMT,id=disk h=$QEMU_HANDLE _send_qemu_cmd $h "{ 'execute': 'qmp_capabilities' }" 'return' @@ -125,7 +125,7 @@ echo === Start active commit job and exit qemu === echo _launch_qemu \ - -drive file="${TEST_IMG}",cache=$CACHEMODE,driver=$IMGFMT,id=disk + -drive file="${TEST_IMG}",cache=$CACHEMODE,aio=$AIOMODE,driver=$IMGFMT,id=disk h=$QEMU_HANDLE _send_qemu_cmd $h "{ 'execute': 'qmp_capabilities' }" 'return' @@ -147,7 +147,7 @@ echo === Start mirror job and exit qemu === echo _launch_qemu \ - -drive file="${TEST_IMG}",cache=$CACHEMODE,driver=$IMGFMT,id=disk + -drive file="${TEST_IMG}",cache=$CACHEMODE,aio=$AIOMODE,driver=$IMGFMT,id=disk h=$QEMU_HANDLE _send_qemu_cmd $h "{ 'execute': 'qmp_capabilities' }" 'return' @@ -172,7 +172,7 @@ echo === Start backup job and exit qemu === echo _launch_qemu \ - -drive file="${TEST_IMG}",cache=$CACHEMODE,driver=$IMGFMT,id=disk + -drive file="${TEST_IMG}",cache=$CACHEMODE,aio=$AIOMODE,driver=$IMGFMT,id=disk h=$QEMU_HANDLE _send_qemu_cmd $h "{ 'execute': 'qmp_capabilities' }" 'return' @@ -196,7 +196,7 @@ echo === Start streaming job and exit qemu === echo _launch_qemu \ - -drive file="${TEST_IMG}",cache=$CACHEMODE,driver=$IMGFMT,id=disk + -drive file="${TEST_IMG}",cache=$CACHEMODE,aio=$AIOMODE,driver=$IMGFMT,id=disk h=$QEMU_HANDLE _send_qemu_cmd $h "{ 'execute': 'qmp_capabilities' }" 'return' diff --git a/tests/qemu-iotests/185.out b/tests/qemu-iotests/185.out index 8379ac5854..9a3b65782b 100644 --- a/tests/qemu-iotests/185.out +++ b/tests/qemu-iotests/185.out @@ -65,6 +65,8 @@ Formatting 'TEST_DIR/t.qcow2.copy', fmt=qcow2 size=67108864 cluster_size=65536 l Formatting 'TEST_DIR/t.qcow2.copy', fmt=qcow2 size=67108864 cluster_size=65536 lazy_refcounts=off refcount_bits=16 {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "disk"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "paused", "id": "disk"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}} {"return": {}} { 'execute': 'quit' } {"return": {}} diff --git a/tests/qemu-iotests/187 b/tests/qemu-iotests/187 index 2fcef9e2bd..c6e1dc57a0 100755 --- a/tests/qemu-iotests/187 +++ b/tests/qemu-iotests/187 @@ -28,9 +28,9 @@ status=1 # failure is the default! _cleanup() { - _cleanup_test_img - rm -f "$TEST_IMG.2" - rm -f "$TEST_IMG.3" + _cleanup_test_img + _rm_test_img "$TEST_IMG.2" + _rm_test_img "$TEST_IMG.3" } trap "_cleanup; exit \$status" 0 1 2 3 15 diff --git a/tests/qemu-iotests/190 b/tests/qemu-iotests/190 index eb766ad09f..6d41650438 100755 --- a/tests/qemu-iotests/190 +++ b/tests/qemu-iotests/190 @@ -29,7 +29,7 @@ status=1 # failure is the default! _cleanup() { _cleanup_test_img - rm -f "$TEST_IMG.converted" + _rm_test_img "$TEST_IMG.converted" } trap "_cleanup; exit \$status" 0 1 2 3 15 @@ -45,7 +45,7 @@ _supported_proto file echo "== Huge file ==" echo -IMGOPTS='cluster_size=2M' _make_test_img 2T +_make_test_img -o 'cluster_size=2M' 2T $QEMU_IMG measure -O raw -f qcow2 "$TEST_IMG" $QEMU_IMG measure -O qcow2 -o cluster_size=64k -f qcow2 "$TEST_IMG" diff --git a/tests/qemu-iotests/191 b/tests/qemu-iotests/191 index 528022e8d8..b05db68141 100755 --- a/tests/qemu-iotests/191 +++ b/tests/qemu-iotests/191 @@ -28,9 +28,9 @@ status=1 # failure is the default! _cleanup() { - rm -f "${TEST_IMG}.mid" - rm -f "${TEST_IMG}.ovl2" - rm -f "${TEST_IMG}.ovl3" + _rm_test_img "${TEST_IMG}.mid" + _rm_test_img "${TEST_IMG}.ovl2" + _rm_test_img "${TEST_IMG}.ovl3" _cleanup_test_img _cleanup_qemu } @@ -43,6 +43,8 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 _supported_fmt qcow2 _supported_proto file +# An external data file would change the query-named-block-nodes output +_unsupported_imgopts data_file size=64M @@ -51,8 +53,7 @@ echo === Preparing and starting VM === echo TEST_IMG="${TEST_IMG}.base" _make_test_img $size -IMGOPTS=$(_optstr_add "$IMGOPTS" "backing_fmt=$IMGFMT") \ - TEST_IMG="${TEST_IMG}.mid" _make_test_img -b "${TEST_IMG}.base" +TEST_IMG="${TEST_IMG}.mid" _make_test_img -o "backing_fmt=$IMGFMT" -b "${TEST_IMG}.base" _make_test_img -b "${TEST_IMG}.mid" TEST_IMG="${TEST_IMG}.ovl2" _make_test_img -b "${TEST_IMG}.mid" 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/195 b/tests/qemu-iotests/195 index ef7b9a94e2..48984b7ac1 100755 --- a/tests/qemu-iotests/195 +++ b/tests/qemu-iotests/195 @@ -29,7 +29,7 @@ status=1 # failure is the default! _cleanup() { _cleanup_test_img - rm -f "$TEST_IMG.mid" + _rm_test_img "$TEST_IMG.mid" } trap "_cleanup; exit \$status" 0 1 2 3 15 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/197 b/tests/qemu-iotests/197 index 1d4f6786db..95f05b0e34 100755 --- a/tests/qemu-iotests/197 +++ b/tests/qemu-iotests/197 @@ -43,7 +43,7 @@ esac _cleanup() { _cleanup_test_img - rm -f "$TEST_WRAP" + _rm_test_img "$TEST_WRAP" rm -f "$BLKDBG_CONF" } trap "_cleanup; exit \$status" 0 1 2 3 15 @@ -66,8 +66,8 @@ if [ "$IMGFMT" = "vpc" ]; then fi _make_test_img 4G $QEMU_IO -c "write -P 55 3G 1k" "$TEST_IMG" | _filter_qemu_io -IMGPROTO=file IMGFMT=qcow2 IMGOPTS= TEST_IMG_FILE="$TEST_WRAP" \ - _make_test_img -F "$IMGFMT" -b "$TEST_IMG" | _filter_img_create +IMGPROTO=file IMGFMT=qcow2 TEST_IMG_FILE="$TEST_WRAP" \ + _make_test_img --no-opts -F "$IMGFMT" -b "$TEST_IMG" | _filter_img_create $QEMU_IO -f qcow2 -c "write -z -u 1M 64k" "$TEST_WRAP" | _filter_qemu_io # Ensure that a read of two clusters, but where one is already allocated, diff --git a/tests/qemu-iotests/198 b/tests/qemu-iotests/198 index c8f824cfae..fb0d5a29d3 100755 --- a/tests/qemu-iotests/198 +++ b/tests/qemu-iotests/198 @@ -92,13 +92,15 @@ echo echo "== checking image base ==" $QEMU_IMG info --image-opts $IMGSPECBASE | _filter_img_info --format-specific \ | sed -e "/^disk size:/ D" -e '/refcount bits:/ D' -e '/compat:/ D' \ - -e '/lazy refcounts:/ D' -e '/corrupt:/ D' + -e '/lazy refcounts:/ D' -e '/corrupt:/ D' -e '/^\s*data file/ D' \ + | _filter_json_filename echo echo "== checking image layer ==" $QEMU_IMG info --image-opts $IMGSPECLAYER | _filter_img_info --format-specific \ | sed -e "/^disk size:/ D" -e '/refcount bits:/ D' -e '/compat:/ D' \ - -e '/lazy refcounts:/ D' -e '/corrupt:/ D' + -e '/lazy refcounts:/ D' -e '/corrupt:/ D' -e '/^\s*data file/ D' \ + | _filter_json_filename # success, all done diff --git a/tests/qemu-iotests/198.out b/tests/qemu-iotests/198.out index e86b175e39..831ce3a289 100644 --- a/tests/qemu-iotests/198.out +++ b/tests/qemu-iotests/198.out @@ -32,7 +32,7 @@ read 16777216/16777216 bytes at offset 0 16 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) == checking image base == -image: json:{"encrypt.key-secret": "sec0", "driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/t.IMGFMT.base"}} +image: json:{ /* filtered */ } file format: IMGFMT virtual size: 16 MiB (16777216 bytes) Format specific information: @@ -74,7 +74,7 @@ Format specific information: master key iters: 1024 == checking image layer == -image: json:{"encrypt.key-secret": "sec1", "driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/t.IMGFMT"}} +image: json:{ /* filtered */ } file format: IMGFMT virtual size: 16 MiB (16777216 bytes) backing file: TEST_DIR/t.IMGFMT.base 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/200 b/tests/qemu-iotests/200 index 72d431f251..59f7854b9f 100755 --- a/tests/qemu-iotests/200 +++ b/tests/qemu-iotests/200 @@ -31,7 +31,8 @@ status=1 # failure is the default! _cleanup() { _cleanup_qemu - rm -f "${TEST_IMG}" "${BACKING_IMG}" + _rm_test_img "${TEST_IMG}" + _rm_test_img "${BACKING_IMG}" } trap "_cleanup; exit \$status" 0 1 2 3 15 @@ -46,8 +47,8 @@ _supported_proto file BACKING_IMG="${TEST_DIR}/backing.img" TEST_IMG="${TEST_DIR}/test.img" -${QEMU_IMG} create -f $IMGFMT "${BACKING_IMG}" 512M | _filter_img_create -${QEMU_IMG} create -f $IMGFMT -F $IMGFMT "${TEST_IMG}" -b "${BACKING_IMG}" 512M | _filter_img_create +TEST_IMG="$BACKING_IMG" _make_test_img 512M +_make_test_img -F $IMGFMT -b "$BACKING_IMG" 512M ${QEMU_IO} -c "write -P 0xa5 512 300M" "${BACKING_IMG}" | _filter_qemu_io @@ -66,7 +67,7 @@ echo === Starting QEMU VM === echo qemu_comm_method="qmp" _launch_qemu -object iothread,id=iothread0 $virtio_scsi \ - -drive file="${TEST_IMG}",media=disk,if=none,cache=$CACHEMODE,id=drive_sysdisk,format=$IMGFMT \ + -drive file="${TEST_IMG}",media=disk,if=none,cache=$CACHEMODE,aio=$AIOMODE,id=drive_sysdisk,format=$IMGFMT \ -device scsi-hd,drive=drive_sysdisk,bus=scsi0.0,id=sysdisk,bootindex=0 h1=$QEMU_HANDLE diff --git a/tests/qemu-iotests/201 b/tests/qemu-iotests/201 index 86fa37e714..483eb189c5 100755 --- a/tests/qemu-iotests/201 +++ b/tests/qemu-iotests/201 @@ -43,9 +43,9 @@ _supported_fmt qcow2 _supported_proto generic _supported_os Linux -# Internal snapshots are (currently) impossible with refcount_bits=1 -# This was taken from test 080 -_unsupported_imgopts 'refcount_bits=1[^0-9]' +# Internal snapshots are (currently) impossible with refcount_bits=1, +# and generally impossible with external data files +_unsupported_imgopts 'refcount_bits=1[^0-9]' data_file size=64M _make_test_img $size @@ -58,20 +58,20 @@ qemu_comm_method="monitor" if [ "$IMGOPTSSYNTAX" = "true" ]; then _launch_qemu \ - -drive "${TEST_IMG}",cache=${CACHEMODE},id=disk + -drive "${TEST_IMG}",cache=${CACHEMODE},aio=$AIOMODE,id=disk else _launch_qemu \ - -drive file="${TEST_IMG}",cache=${CACHEMODE},driver=$IMGFMT,id=disk + -drive file="${TEST_IMG}",cache=${CACHEMODE},aio=$AIOMODE,driver=$IMGFMT,id=disk fi src=$QEMU_HANDLE if [ "$IMGOPTSSYNTAX" = "true" ]; then _launch_qemu \ - -drive "${TEST_IMG}",cache=${CACHEMODE},id=disk \ + -drive "${TEST_IMG}",cache=${CACHEMODE},aio=$AIOMODE,id=disk \ -incoming "unix:${MIG_SOCKET}" else _launch_qemu \ - -drive file="${TEST_IMG}",cache=${CACHEMODE},driver=$IMGFMT,id=disk \ + -drive file="${TEST_IMG}",cache=${CACHEMODE},aio=$AIOMODE,driver=$IMGFMT,id=disk \ -incoming "unix:${MIG_SOCKET}" fi dest=$QEMU_HANDLE 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 5bb738bf23..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 # @@ -25,16 +25,6 @@ from iotests import imgfmt iotests.verify_image_format(supported_fmts=['qcow2']) -def blockdev_create(vm, options): - result = vm.qmp_log('blockdev-create', - filters=[iotests.filter_qmp_testfiles], - job_id='job0', options=options) - - if 'return' in result: - assert result['return'] == {} - vm.run_job('job0') - iotests.log("") - with iotests.FilePath('t.qcow2') as disk_path, \ iotests.FilePath('t.qcow2.base') as backing_path, \ iotests.VM() as vm: @@ -50,18 +40,18 @@ with iotests.FilePath('t.qcow2') as disk_path, \ size = 128 * 1024 * 1024 vm.launch() - blockdev_create(vm, { 'driver': 'file', - 'filename': disk_path, - 'size': 0 }) + vm.blockdev_create({ 'driver': 'file', + 'filename': disk_path, + 'size': 0 }) vm.qmp_log('blockdev-add', filters=[iotests.filter_qmp_testfiles], driver='file', filename=disk_path, node_name='imgfile') - blockdev_create(vm, { 'driver': imgfmt, - 'file': 'imgfile', - 'size': size }) + vm.blockdev_create({ 'driver': imgfmt, + 'file': 'imgfile', + 'size': size }) vm.shutdown() iotests.img_info_log(disk_path) @@ -76,23 +66,23 @@ with iotests.FilePath('t.qcow2') as disk_path, \ size = 64 * 1024 * 1024 vm.launch() - blockdev_create(vm, { 'driver': 'file', - 'filename': disk_path, - 'size': 0, - 'preallocation': 'off', - 'nocow': False }) + vm.blockdev_create({ 'driver': 'file', + 'filename': disk_path, + 'size': 0, + 'preallocation': 'off', + 'nocow': False }) - blockdev_create(vm, { 'driver': imgfmt, - 'file': { - 'driver': 'file', - 'filename': disk_path, - }, - 'size': size, - 'version': 'v3', - 'cluster-size': 65536, - 'preallocation': 'off', - 'lazy-refcounts': False, - 'refcount-bits': 16 }) + vm.blockdev_create({ 'driver': imgfmt, + 'file': { + 'driver': 'file', + 'filename': disk_path, + }, + 'size': size, + 'version': 'v3', + 'cluster-size': 65536, + 'preallocation': 'off', + 'lazy-refcounts': False, + 'refcount-bits': 16 }) vm.shutdown() iotests.img_info_log(disk_path) @@ -107,23 +97,23 @@ with iotests.FilePath('t.qcow2') as disk_path, \ size = 32 * 1024 * 1024 vm.launch() - blockdev_create(vm, { 'driver': 'file', - 'filename': disk_path, - 'size': 0, - 'preallocation': 'falloc', - 'nocow': True }) + vm.blockdev_create({ 'driver': 'file', + 'filename': disk_path, + 'size': 0, + 'preallocation': 'falloc', + 'nocow': True }) - blockdev_create(vm, { 'driver': imgfmt, - 'file': { - 'driver': 'file', - 'filename': disk_path, - }, - 'size': size, - 'version': 'v3', - 'cluster-size': 2097152, - 'preallocation': 'metadata', - 'lazy-refcounts': True, - 'refcount-bits': 1 }) + vm.blockdev_create({ 'driver': imgfmt, + 'file': { + 'driver': 'file', + 'filename': disk_path, + }, + 'size': size, + 'version': 'v3', + 'cluster-size': 2097152, + 'preallocation': 'metadata', + 'lazy-refcounts': True, + 'refcount-bits': 1 }) vm.shutdown() iotests.img_info_log(disk_path) @@ -135,20 +125,20 @@ with iotests.FilePath('t.qcow2') as disk_path, \ iotests.log("") vm.launch() - blockdev_create(vm, { 'driver': 'file', - 'filename': disk_path, - 'size': 0 }) + vm.blockdev_create({ 'driver': 'file', + 'filename': disk_path, + 'size': 0 }) - blockdev_create(vm, { 'driver': imgfmt, - 'file': { - 'driver': 'file', - 'filename': disk_path, - }, - 'size': size, - 'backing-file': backing_path, - 'backing-fmt': 'qcow2', - 'version': 'v2', - 'cluster-size': 512 }) + vm.blockdev_create({ 'driver': imgfmt, + 'file': { + 'driver': 'file', + 'filename': disk_path, + }, + 'size': size, + 'backing-file': backing_path, + 'backing-fmt': 'qcow2', + 'version': 'v2', + 'cluster-size': 512 }) vm.shutdown() iotests.img_info_log(disk_path) @@ -160,22 +150,22 @@ with iotests.FilePath('t.qcow2') as disk_path, \ iotests.log("") vm.launch() - blockdev_create(vm, { 'driver': imgfmt, - 'file': { - 'driver': 'file', - 'filename': disk_path, - }, - 'size': size, - 'encrypt': { - 'format': 'luks', - 'key-secret': 'keysec0', - 'cipher-alg': 'twofish-128', - 'cipher-mode': 'ctr', - 'ivgen-alg': 'plain64', - 'ivgen-hash-alg': 'md5', - 'hash-alg': 'sha1', - 'iter-time': 10, - }}) + vm.blockdev_create({ 'driver': imgfmt, + 'file': { + 'driver': 'file', + 'filename': disk_path, + }, + 'size': size, + 'encrypt': { + 'format': 'luks', + 'key-secret': 'keysec0', + 'cipher-alg': 'twofish-128', + 'cipher-mode': 'ctr', + 'ivgen-alg': 'plain64', + 'ivgen-hash-alg': 'md5', + 'hash-alg': 'sha1', + 'iter-time': 10, + }}) vm.shutdown() iotests.img_info_log(disk_path) @@ -187,9 +177,9 @@ with iotests.FilePath('t.qcow2') as disk_path, \ iotests.log("") vm.launch() - blockdev_create(vm, { 'driver': imgfmt, - 'file': "this doesn't exist", - 'size': size }) + vm.blockdev_create({ 'driver': imgfmt, + 'file': "this doesn't exist", + 'size': size }) vm.shutdown() # @@ -211,9 +201,9 @@ with iotests.FilePath('t.qcow2') as disk_path, \ vm.launch() for size in [ 1234, 18446744073709551104, 9223372036854775808, 9223372036854775296 ]: - blockdev_create(vm, { 'driver': imgfmt, - 'file': 'node0', - 'size': size }) + vm.blockdev_create({ 'driver': imgfmt, + 'file': 'node0', + 'size': size }) vm.shutdown() # @@ -222,20 +212,20 @@ with iotests.FilePath('t.qcow2') as disk_path, \ iotests.log("=== Invalid version ===") vm.launch() - blockdev_create(vm, { 'driver': imgfmt, - 'file': 'node0', - 'size': 67108864, - 'version': 'v1' }) - blockdev_create(vm, { 'driver': imgfmt, - 'file': 'node0', - 'size': 67108864, - 'version': 'v2', - 'lazy-refcounts': True }) - blockdev_create(vm, { 'driver': imgfmt, - 'file': 'node0', - 'size': 67108864, - 'version': 'v2', - 'refcount-bits': 8 }) + vm.blockdev_create({ 'driver': imgfmt, + 'file': 'node0', + 'size': 67108864, + 'version': 'v1' }) + vm.blockdev_create({ 'driver': imgfmt, + 'file': 'node0', + 'size': 67108864, + 'version': 'v2', + 'lazy-refcounts': True }) + vm.blockdev_create({ 'driver': imgfmt, + 'file': 'node0', + 'size': 67108864, + 'version': 'v2', + 'refcount-bits': 8 }) vm.shutdown() # @@ -244,15 +234,15 @@ with iotests.FilePath('t.qcow2') as disk_path, \ iotests.log("=== Invalid backing file options ===") vm.launch() - blockdev_create(vm, { 'driver': imgfmt, - 'file': 'node0', - 'size': 67108864, - 'backing-file': '/dev/null', - 'preallocation': 'full' }) - blockdev_create(vm, { 'driver': imgfmt, - 'file': 'node0', - 'size': 67108864, - 'backing-fmt': imgfmt }) + vm.blockdev_create({ 'driver': imgfmt, + 'file': 'node0', + 'size': 67108864, + 'backing-file': '/dev/null', + 'preallocation': 'full' }) + vm.blockdev_create({ 'driver': imgfmt, + 'file': 'node0', + 'size': 67108864, + 'backing-fmt': imgfmt }) vm.shutdown() # @@ -262,14 +252,14 @@ with iotests.FilePath('t.qcow2') as disk_path, \ vm.launch() for csize in [ 1234, 128, 4194304, 0 ]: - blockdev_create(vm, { 'driver': imgfmt, - 'file': 'node0', - 'size': 67108864, - 'cluster-size': csize }) - blockdev_create(vm, { 'driver': imgfmt, - 'file': 'node0', - 'size': 281474976710656, - 'cluster-size': 512 }) + vm.blockdev_create({ 'driver': imgfmt, + 'file': 'node0', + 'size': 67108864, + 'cluster-size': csize }) + vm.blockdev_create({ 'driver': imgfmt, + 'file': 'node0', + 'size': 281474976710656, + 'cluster-size': 512 }) vm.shutdown() # @@ -279,8 +269,8 @@ with iotests.FilePath('t.qcow2') as disk_path, \ vm.launch() for refcount_bits in [ 128, 0, 7 ]: - blockdev_create(vm, { 'driver': imgfmt, - 'file': 'node0', - 'size': 67108864, - 'refcount-bits': refcount_bits }) + vm.blockdev_create({ 'driver': imgfmt, + 'file': 'node0', + 'size': 67108864, + 'refcount-bits': refcount_bits }) vm.shutdown() diff --git a/tests/qemu-iotests/207 b/tests/qemu-iotests/207 index ec8c1d06f0..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 # @@ -35,13 +35,7 @@ def filter_hash(qmsg): return iotests.filter_qmp(qmsg, _filter) def blockdev_create(vm, options): - result = vm.qmp_log('blockdev-create', job_id='job0', options=options, - filters=[iotests.filter_qmp_testfiles, filter_hash]) - - if 'return' in result: - assert result['return'] == {} - vm.run_job('job0') - iotests.log("") + vm.blockdev_create(options, filters=[iotests.filter_qmp_testfiles, filter_hash]) with iotests.FilePath('t.img') as disk_path, \ iotests.VM() as vm: 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 565e3b7b9b..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 # @@ -26,15 +26,6 @@ from iotests import imgfmt iotests.verify_image_format(supported_fmts=['luks']) iotests.verify_protocol(supported=['file']) -def blockdev_create(vm, options): - result = vm.qmp_log('blockdev-create', job_id='job0', options=options, - filters=[iotests.filter_qmp_testfiles]) - - if 'return' in result: - assert result['return'] == {} - vm.run_job('job0') - iotests.log("") - with iotests.FilePath('t.luks') as disk_path, \ iotests.VM() as vm: @@ -49,18 +40,18 @@ with iotests.FilePath('t.luks') as disk_path, \ size = 128 * 1024 * 1024 vm.launch() - blockdev_create(vm, { 'driver': 'file', - 'filename': disk_path, - 'size': 0 }) + vm.blockdev_create({ 'driver': 'file', + 'filename': disk_path, + 'size': 0 }) vm.qmp_log('blockdev-add', driver='file', filename=disk_path, node_name='imgfile', filters=[iotests.filter_qmp_testfiles]) - blockdev_create(vm, { 'driver': imgfmt, - 'file': 'imgfile', - 'key-secret': 'keysec0', - 'size': size, - 'iter-time': 10 }) + vm.blockdev_create({ 'driver': imgfmt, + 'file': 'imgfile', + 'key-secret': 'keysec0', + 'size': size, + 'iter-time': 10 }) vm.shutdown() # TODO Proper support for images to be used with imgopts and/or protocols @@ -79,22 +70,22 @@ with iotests.FilePath('t.luks') as disk_path, \ size = 64 * 1024 * 1024 vm.launch() - blockdev_create(vm, { 'driver': 'file', - 'filename': disk_path, - 'size': 0 }) - blockdev_create(vm, { 'driver': imgfmt, - 'file': { - 'driver': 'file', - 'filename': disk_path, - }, - 'size': size, - 'key-secret': 'keysec0', - 'cipher-alg': 'twofish-128', - 'cipher-mode': 'ctr', - 'ivgen-alg': 'plain64', - 'ivgen-hash-alg': 'md5', - 'hash-alg': 'sha1', - 'iter-time': 10 }) + vm.blockdev_create({ 'driver': 'file', + 'filename': disk_path, + 'size': 0 }) + vm.blockdev_create({ 'driver': imgfmt, + 'file': { + 'driver': 'file', + 'filename': disk_path, + }, + 'size': size, + 'key-secret': 'keysec0', + 'cipher-alg': 'twofish-128', + 'cipher-mode': 'ctr', + 'ivgen-alg': 'plain64', + 'ivgen-hash-alg': 'md5', + 'hash-alg': 'sha1', + 'iter-time': 10 }) vm.shutdown() # TODO Proper support for images to be used with imgopts and/or protocols @@ -113,9 +104,9 @@ with iotests.FilePath('t.luks') as disk_path, \ size = 64 * 1024 * 1024 vm.launch() - blockdev_create(vm, { 'driver': imgfmt, - 'file': "this doesn't exist", - 'size': size }) + vm.blockdev_create({ 'driver': imgfmt, + 'file': "this doesn't exist", + 'size': size }) vm.shutdown() # @@ -126,11 +117,11 @@ with iotests.FilePath('t.luks') as disk_path, \ vm.add_blockdev('driver=file,filename=%s,node-name=node0' % (disk_path)) vm.launch() - blockdev_create(vm, { 'driver': imgfmt, - 'file': 'node0', - 'key-secret': 'keysec0', - 'size': 0, - 'iter-time': 10 }) + vm.blockdev_create({ 'driver': imgfmt, + 'file': 'node0', + 'key-secret': 'keysec0', + 'size': 0, + 'iter-time': 10 }) vm.shutdown() # TODO Proper support for images to be used with imgopts and/or protocols @@ -157,10 +148,10 @@ with iotests.FilePath('t.luks') as disk_path, \ vm.launch() for size in [ 18446744073709551104, 9223372036854775808, 9223372036854775296 ]: - blockdev_create(vm, { 'driver': imgfmt, - 'file': 'node0', - 'key-secret': 'keysec0', - 'size': size }) + vm.blockdev_create({ 'driver': imgfmt, + 'file': 'node0', + 'key-secret': 'keysec0', + 'size': size }) vm.shutdown() # diff --git a/tests/qemu-iotests/211 b/tests/qemu-iotests/211 index 6afc894f76..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 # @@ -27,15 +27,9 @@ iotests.verify_image_format(supported_fmts=['vdi']) iotests.verify_protocol(supported=['file']) def blockdev_create(vm, options): - result = vm.qmp_log('blockdev-create', job_id='job0', options=options, - filters=[iotests.filter_qmp_testfiles]) - - if 'return' in result: - assert result['return'] == {} - error = vm.run_job('job0') - if error and 'Could not allocate bmap' in error: - iotests.notrun('Insufficient memory') - iotests.log("") + error = vm.blockdev_create(options) + if error and 'Could not allocate bmap' in error: + iotests.notrun('Insufficient memory') with iotests.FilePath('t.vdi') as disk_path, \ iotests.VM() as vm: diff --git a/tests/qemu-iotests/212 b/tests/qemu-iotests/212 index 42b74f208b..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 # @@ -26,15 +26,6 @@ from iotests import imgfmt iotests.verify_image_format(supported_fmts=['parallels']) iotests.verify_protocol(supported=['file']) -def blockdev_create(vm, options): - result = vm.qmp_log('blockdev-create', job_id='job0', options=options, - filters=[iotests.filter_qmp_testfiles]) - - if 'return' in result: - assert result['return'] == {} - vm.run_job('job0') - iotests.log("") - with iotests.FilePath('t.parallels') as disk_path, \ iotests.VM() as vm: @@ -47,16 +38,16 @@ with iotests.FilePath('t.parallels') as disk_path, \ size = 128 * 1024 * 1024 vm.launch() - blockdev_create(vm, { 'driver': 'file', - 'filename': disk_path, - 'size': 0 }) + vm.blockdev_create({ 'driver': 'file', + 'filename': disk_path, + 'size': 0 }) vm.qmp_log('blockdev-add', driver='file', filename=disk_path, node_name='imgfile', filters=[iotests.filter_qmp_testfiles]) - blockdev_create(vm, { 'driver': imgfmt, - 'file': 'imgfile', - 'size': size }) + vm.blockdev_create({ 'driver': imgfmt, + 'file': 'imgfile', + 'size': size }) vm.shutdown() iotests.img_info_log(disk_path) @@ -71,16 +62,16 @@ with iotests.FilePath('t.parallels') as disk_path, \ size = 64 * 1024 * 1024 vm.launch() - blockdev_create(vm, { 'driver': 'file', - 'filename': disk_path, - 'size': 0 }) - blockdev_create(vm, { 'driver': imgfmt, - 'file': { - 'driver': 'file', - 'filename': disk_path, - }, - 'size': size, - 'cluster-size': 1048576 }) + vm.blockdev_create({ 'driver': 'file', + 'filename': disk_path, + 'size': 0 }) + vm.blockdev_create({ 'driver': imgfmt, + 'file': { + 'driver': 'file', + 'filename': disk_path, + }, + 'size': size, + 'cluster-size': 1048576 }) vm.shutdown() iotests.img_info_log(disk_path) @@ -95,16 +86,16 @@ with iotests.FilePath('t.parallels') as disk_path, \ size = 32 * 1024 * 1024 vm.launch() - blockdev_create(vm, { 'driver': 'file', - 'filename': disk_path, - 'size': 0 }) - blockdev_create(vm, { 'driver': imgfmt, - 'file': { - 'driver': 'file', - 'filename': disk_path, - }, - 'size': size, - 'cluster-size': 65536 }) + vm.blockdev_create({ 'driver': 'file', + 'filename': disk_path, + 'size': 0 }) + vm.blockdev_create({ 'driver': imgfmt, + 'file': { + 'driver': 'file', + 'filename': disk_path, + }, + 'size': size, + 'cluster-size': 65536 }) vm.shutdown() iotests.img_info_log(disk_path) @@ -116,9 +107,9 @@ with iotests.FilePath('t.parallels') as disk_path, \ iotests.log("") vm.launch() - blockdev_create(vm, { 'driver': imgfmt, - 'file': "this doesn't exist", - 'size': size }) + vm.blockdev_create({ 'driver': imgfmt, + 'file': "this doesn't exist", + 'size': size }) vm.shutdown() # @@ -129,9 +120,9 @@ with iotests.FilePath('t.parallels') as disk_path, \ vm.add_blockdev('driver=file,filename=%s,node-name=node0' % (disk_path)) vm.launch() - blockdev_create(vm, { 'driver': imgfmt, - 'file': 'node0', - 'size': 0 }) + vm.blockdev_create({ 'driver': imgfmt, + 'file': 'node0', + 'size': 0 }) vm.shutdown() iotests.img_info_log(disk_path) @@ -143,9 +134,9 @@ with iotests.FilePath('t.parallels') as disk_path, \ iotests.log("") vm.launch() - blockdev_create(vm, { 'driver': imgfmt, - 'file': 'node0', - 'size': 4503599627369984}) + vm.blockdev_create({ 'driver': imgfmt, + 'file': 'node0', + 'size': 4503599627369984}) vm.shutdown() iotests.img_info_log(disk_path) @@ -171,9 +162,9 @@ with iotests.FilePath('t.parallels') as disk_path, \ vm.launch() for size in [ 1234, 18446744073709551104, 9223372036854775808, 9223372036854775296, 4503599627370497 ]: - blockdev_create(vm, { 'driver': imgfmt, - 'file': 'node0', - 'size': size }) + vm.blockdev_create({ 'driver': imgfmt, + 'file': 'node0', + 'size': size }) vm.shutdown() # @@ -185,12 +176,12 @@ with iotests.FilePath('t.parallels') as disk_path, \ vm.launch() for csize in [ 1234, 128, 4294967296, 9223372036854775808, 18446744073709551104, 0 ]: - blockdev_create(vm, { 'driver': imgfmt, - 'file': 'node0', - 'size': 67108864, - 'cluster-size': csize }) - blockdev_create(vm, { 'driver': imgfmt, - 'file': 'node0', - 'size': 281474976710656, - 'cluster-size': 512 }) + vm.blockdev_create({ 'driver': imgfmt, + 'file': 'node0', + 'size': 67108864, + 'cluster-size': csize }) + vm.blockdev_create({ 'driver': imgfmt, + 'file': 'node0', + 'size': 281474976710656, + 'cluster-size': 512 }) vm.shutdown() diff --git a/tests/qemu-iotests/213 b/tests/qemu-iotests/213 index 5604f3cebb..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 # @@ -26,15 +26,6 @@ from iotests import imgfmt iotests.verify_image_format(supported_fmts=['vhdx']) iotests.verify_protocol(supported=['file']) -def blockdev_create(vm, options): - result = vm.qmp_log('blockdev-create', job_id='job0', options=options, - filters=[iotests.filter_qmp_testfiles]) - - if 'return' in result: - assert result['return'] == {} - vm.run_job('job0') - iotests.log("") - with iotests.FilePath('t.vhdx') as disk_path, \ iotests.VM() as vm: @@ -47,16 +38,16 @@ with iotests.FilePath('t.vhdx') as disk_path, \ size = 128 * 1024 * 1024 vm.launch() - blockdev_create(vm, { 'driver': 'file', - 'filename': disk_path, - 'size': 0 }) + vm.blockdev_create({ 'driver': 'file', + 'filename': disk_path, + 'size': 0 }) vm.qmp_log('blockdev-add', driver='file', filename=disk_path, node_name='imgfile', filters=[iotests.filter_qmp_testfiles]) - blockdev_create(vm, { 'driver': imgfmt, - 'file': 'imgfile', - 'size': size }) + vm.blockdev_create({ 'driver': imgfmt, + 'file': 'imgfile', + 'size': size }) vm.shutdown() iotests.img_info_log(disk_path) @@ -71,19 +62,19 @@ with iotests.FilePath('t.vhdx') as disk_path, \ size = 64 * 1024 * 1024 vm.launch() - blockdev_create(vm, { 'driver': 'file', - 'filename': disk_path, - 'size': 0 }) - blockdev_create(vm, { 'driver': imgfmt, - 'file': { - 'driver': 'file', - 'filename': disk_path, - }, - 'size': size, - 'log-size': 1048576, - 'block-size': 8388608, - 'subformat': 'dynamic', - 'block-state-zero': True }) + vm.blockdev_create({ 'driver': 'file', + 'filename': disk_path, + 'size': 0 }) + vm.blockdev_create({ 'driver': imgfmt, + 'file': { + 'driver': 'file', + 'filename': disk_path, + }, + 'size': size, + 'log-size': 1048576, + 'block-size': 8388608, + 'subformat': 'dynamic', + 'block-state-zero': True }) vm.shutdown() iotests.img_info_log(disk_path) @@ -98,19 +89,19 @@ with iotests.FilePath('t.vhdx') as disk_path, \ size = 32 * 1024 * 1024 vm.launch() - blockdev_create(vm, { 'driver': 'file', - 'filename': disk_path, - 'size': 0 }) - blockdev_create(vm, { 'driver': imgfmt, - 'file': { - 'driver': 'file', - 'filename': disk_path, - }, - 'size': size, - 'log-size': 8388608, - 'block-size': 268435456, - 'subformat': 'fixed', - 'block-state-zero': False }) + vm.blockdev_create({ 'driver': 'file', + 'filename': disk_path, + 'size': 0 }) + vm.blockdev_create({ 'driver': imgfmt, + 'file': { + 'driver': 'file', + 'filename': disk_path, + }, + 'size': size, + 'log-size': 8388608, + 'block-size': 268435456, + 'subformat': 'fixed', + 'block-state-zero': False }) vm.shutdown() iotests.img_info_log(disk_path) @@ -122,9 +113,9 @@ with iotests.FilePath('t.vhdx') as disk_path, \ iotests.log("") vm.launch() - blockdev_create(vm, { 'driver': imgfmt, - 'file': "this doesn't exist", - 'size': size }) + vm.blockdev_create({ 'driver': imgfmt, + 'file': "this doesn't exist", + 'size': size }) vm.shutdown() # @@ -135,9 +126,9 @@ with iotests.FilePath('t.vhdx') as disk_path, \ vm.add_blockdev('driver=file,filename=%s,node-name=node0' % (disk_path)) vm.launch() - blockdev_create(vm, { 'driver': imgfmt, - 'file': 'node0', - 'size': 0 }) + vm.blockdev_create({ 'driver': imgfmt, + 'file': 'node0', + 'size': 0 }) vm.shutdown() iotests.img_info_log(disk_path) @@ -149,9 +140,9 @@ with iotests.FilePath('t.vhdx') as disk_path, \ iotests.log("") vm.launch() - blockdev_create(vm, { 'driver': imgfmt, - 'file': 'node0', - 'size': 70368744177664 }) + vm.blockdev_create({ 'driver': imgfmt, + 'file': 'node0', + 'size': 70368744177664 }) vm.shutdown() iotests.img_info_log(disk_path) @@ -176,9 +167,9 @@ with iotests.FilePath('t.vhdx') as disk_path, \ vm.launch() for size in [ 18446744073709551104, 9223372036854775808, 9223372036854775296, 70368744177665 ]: - blockdev_create(vm, { 'driver': imgfmt, - 'file': 'node0', - 'size': size }) + vm.blockdev_create({ 'driver': imgfmt, + 'file': 'node0', + 'size': size }) vm.shutdown() # @@ -189,10 +180,10 @@ with iotests.FilePath('t.vhdx') as disk_path, \ vm.launch() for bsize in [ 1234567, 128, 3145728, 536870912, 0 ]: - blockdev_create(vm, { 'driver': imgfmt, - 'file': 'node0', - 'size': 67108864, - 'block-size': bsize }) + vm.blockdev_create({ 'driver': imgfmt, + 'file': 'node0', + 'size': 67108864, + 'block-size': bsize }) vm.shutdown() # @@ -203,8 +194,8 @@ with iotests.FilePath('t.vhdx') as disk_path, \ vm.launch() for lsize in [ 1234567, 128, 4294967296, 0 ]: - blockdev_create(vm, { 'driver': imgfmt, - 'file': 'node0', - 'size': 67108864, - 'log-size': lsize }) + vm.blockdev_create({ 'driver': imgfmt, + 'file': 'node0', + 'size': 67108864, + 'log-size': lsize }) vm.shutdown() diff --git a/tests/qemu-iotests/214 b/tests/qemu-iotests/214 index 21ec8a2ad8..af677d90b8 100755 --- a/tests/qemu-iotests/214 +++ b/tests/qemu-iotests/214 @@ -39,7 +39,8 @@ _supported_proto file # Repairing the corrupted image requires qemu-img check to store a # refcount up to 3, which requires at least two refcount bits. -_unsupported_imgopts 'refcount_bits=1[^0-9]' +# External data files do not support compressed clusters. +_unsupported_imgopts 'refcount_bits=1[^0-9]' data_file echo @@ -89,6 +90,49 @@ _check_test_img -r all $QEMU_IO -c "read -P 0x11 0 4M" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir $QEMU_IO -c "read -P 0x22 4M 4M" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir +echo +echo "=== Write compressed data of multiple clusters ===" +echo +cluster_size=0x10000 +_make_test_img 2M -o cluster_size=$cluster_size + +echo "Write uncompressed data:" +let data_size="8 * $cluster_size" +$QEMU_IO -c "write -P 0xaa 0 $data_size" "$TEST_IMG" \ + 2>&1 | _filter_qemu_io | _filter_testdir +sizeA=$($QEMU_IMG info --output=json "$TEST_IMG" | + sed -n '/"actual-size":/ s/[^0-9]//gp') + +_make_test_img 2M -o cluster_size=$cluster_size +echo "Write compressed data:" +let data_size="3 * $cluster_size + $cluster_size / 2" +# Set compress on. That will align the written data +# by the cluster size and will write them compressed. +QEMU_IO_OPTIONS=$QEMU_IO_OPTIONS_NO_FMT \ +$QEMU_IO -c "write -P 0xbb 0 $data_size" --image-opts \ + "driver=compress,file.driver=$IMGFMT,file.file.driver=file,file.file.filename=$TEST_IMG" \ + 2>&1 | _filter_qemu_io | _filter_testdir + +let offset="4 * $cluster_size + $cluster_size / 4" +QEMU_IO_OPTIONS=$QEMU_IO_OPTIONS_NO_FMT \ +$QEMU_IO -c "write -P 0xcc $offset $data_size" "json:{\ + 'driver': 'compress', + 'file': {'driver': '$IMGFMT', + 'file': {'driver': 'file', + 'filename': '$TEST_IMG'}}}" | \ + _filter_qemu_io | _filter_testdir + +sizeB=$($QEMU_IMG info --output=json "$TEST_IMG" | + sed -n '/"actual-size":/ s/[^0-9]//gp') + +if [ $sizeA -lt $sizeB ] +then + echo "Compression ERROR ($sizeA < $sizeB)" +fi + +$QEMU_IMG check --output=json "$TEST_IMG" | + sed -n 's/,$//; /"compressed-clusters":/ s/^ *//p' + # success, all done echo '*** done' rm -f $seq.full diff --git a/tests/qemu-iotests/214.out b/tests/qemu-iotests/214.out index 0fcd8dc051..9fc67287f8 100644 --- a/tests/qemu-iotests/214.out +++ b/tests/qemu-iotests/214.out @@ -32,4 +32,18 @@ read 4194304/4194304 bytes at offset 0 4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) read 4194304/4194304 bytes at offset 4194304 4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +=== Write compressed data of multiple clusters === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2097152 +Write uncompressed data: +wrote 524288/524288 bytes at offset 0 +512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2097152 +Write compressed data: +wrote 229376/229376 bytes at offset 0 +224 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 229376/229376 bytes at offset 278528 +224 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +"compressed-clusters": 8 *** done diff --git a/tests/qemu-iotests/215 b/tests/qemu-iotests/215 index 2eb377d682..f99bae78c7 100755 --- a/tests/qemu-iotests/215 +++ b/tests/qemu-iotests/215 @@ -40,7 +40,7 @@ esac _cleanup() { _cleanup_test_img - rm -f "$TEST_WRAP" + _rm_test_img "$TEST_WRAP" rm -f "$BLKDBG_CONF" } trap "_cleanup; exit \$status" 0 1 2 3 15 @@ -63,8 +63,8 @@ if [ "$IMGFMT" = "vpc" ]; then fi _make_test_img 4G $QEMU_IO -c "write -P 55 3G 1k" "$TEST_IMG" | _filter_qemu_io -IMGPROTO=file IMGFMT=qcow2 IMGOPTS= TEST_IMG_FILE="$TEST_WRAP" \ - _make_test_img -F "$IMGFMT" -b "$TEST_IMG" | _filter_img_create +IMGPROTO=file IMGFMT=qcow2 TEST_IMG_FILE="$TEST_WRAP" \ + _make_test_img --no-opts -F "$IMGFMT" -b "$TEST_IMG" | _filter_img_create $QEMU_IO -f qcow2 -c "write -z -u 1M 64k" "$TEST_WRAP" | _filter_qemu_io # Ensure that a read of two clusters, but where one is already allocated, 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/217 b/tests/qemu-iotests/217 index 58a78a6098..d89116ccad 100755 --- a/tests/qemu-iotests/217 +++ b/tests/qemu-iotests/217 @@ -40,7 +40,8 @@ _supported_proto file # This test needs clusters with at least a refcount of 2 so that # OFLAG_COPIED is not set. refcount_bits=1 is therefore unsupported. -_unsupported_imgopts 'refcount_bits=1[^0-9]' +# (As are external data files.) +_unsupported_imgopts 'refcount_bits=1[^0-9]' data_file echo echo '=== Simulating an I/O error during snapshot deletion ===' 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 e0c51662c0..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. # @@ -63,7 +63,7 @@ def test_pause_resume(vm): # logged immediately iotests.log(vm.qmp('query-jobs')) -def test_job_lifecycle(vm, job, job_args, has_ready=False): +def test_job_lifecycle(vm, job, job_args, has_ready=False, is_mirror=False): global img_size iotests.log('') @@ -135,6 +135,9 @@ def test_job_lifecycle(vm, job, job_args, has_ready=False): iotests.log('Waiting for PENDING state...') iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE'))) iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE'))) + if is_mirror: + iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE'))) + iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE'))) if not job_args.get('auto-finalize', True): # PENDING state: @@ -218,7 +221,7 @@ with iotests.FilePath('disk.img') as disk_path, \ for auto_finalize in [True, False]: for auto_dismiss in [True, False]: - test_job_lifecycle(vm, 'drive-backup', job_args={ + test_job_lifecycle(vm, 'drive-backup', is_mirror=True, job_args={ 'device': 'drive0-node', 'target': copy_path, 'sync': 'full', diff --git a/tests/qemu-iotests/219.out b/tests/qemu-iotests/219.out index 8ebd3fee60..0ea5d0b9d5 100644 --- a/tests/qemu-iotests/219.out +++ b/tests/qemu-iotests/219.out @@ -135,6 +135,8 @@ Pause/resume in RUNNING {"return": {}} Waiting for PENDING state... +{"data": {"id": "job0", "status": "paused"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"data": {"id": "job0", "status": "running"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} {"data": {"id": "job0", "status": "waiting"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} {"data": {"id": "job0", "status": "pending"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} {"data": {"id": "job0", "status": "concluded"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} @@ -186,6 +188,8 @@ Pause/resume in RUNNING {"return": {}} Waiting for PENDING state... +{"data": {"id": "job0", "status": "paused"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"data": {"id": "job0", "status": "running"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} {"data": {"id": "job0", "status": "waiting"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} {"data": {"id": "job0", "status": "pending"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} {"data": {"id": "job0", "status": "concluded"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} @@ -245,6 +249,8 @@ Pause/resume in RUNNING {"return": {}} Waiting for PENDING state... +{"data": {"id": "job0", "status": "paused"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"data": {"id": "job0", "status": "running"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} {"data": {"id": "job0", "status": "waiting"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} {"data": {"id": "job0", "status": "pending"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} {"return": [{"current-progress": 4194304, "id": "job0", "status": "pending", "total-progress": 4194304, "type": "backup"}]} @@ -304,6 +310,8 @@ Pause/resume in RUNNING {"return": {}} Waiting for PENDING state... +{"data": {"id": "job0", "status": "paused"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"data": {"id": "job0", "status": "running"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} {"data": {"id": "job0", "status": "waiting"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} {"data": {"id": "job0", "status": "pending"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} {"return": [{"current-progress": 4194304, "id": "job0", "status": "pending", "total-progress": 4194304, "type": "backup"}]} diff --git a/tests/qemu-iotests/220 b/tests/qemu-iotests/220 index 2d62c5dcac..a9259b7127 100755 --- a/tests/qemu-iotests/220 +++ b/tests/qemu-iotests/220 @@ -37,16 +37,17 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 _supported_fmt qcow2 _supported_proto file _supported_os Linux +# To use a different refcount width but 16 bits we need compat=1.1, +# and external data files do not support compressed clusters. +_unsupported_imgopts 'compat=0.10' data_file echo "== Creating huge file ==" # Sanity check: We require a file system that permits the creation # of a HUGE (but very sparse) file. tmpfs works, ext4 does not. -if ! truncate --size=513T "$TEST_IMG"; then - _notrun "file system on $TEST_DIR does not support large enough files" -fi -rm "$TEST_IMG" -IMGOPTS='cluster_size=2M,refcount_bits=1' _make_test_img 513T +_require_large_file 513T + +_make_test_img -o 'cluster_size=2M,refcount_bits=1' 513T echo "== Populating refcounts ==" # We want an image with 256M refcounts * 2M clusters = 512T referenced. diff --git a/tests/qemu-iotests/222 b/tests/qemu-iotests/222 old mode 100644 new mode 100755 index 3f9f934ad8..bf1718e179 --- 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/225 b/tests/qemu-iotests/225 index fbd7404791..c9a334c7e9 100755 --- a/tests/qemu-iotests/225 +++ b/tests/qemu-iotests/225 @@ -29,7 +29,7 @@ status=1 # failure is the default! _cleanup() { _cleanup_test_img - rm -f "$TEST_IMG.not_base" + _rm_test_img "$TEST_IMG.not_base" } trap "_cleanup; exit \$status" 0 1 2 3 15 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/229 b/tests/qemu-iotests/229 index e18a464fe0..866168b236 100755 --- a/tests/qemu-iotests/229 +++ b/tests/qemu-iotests/229 @@ -31,7 +31,8 @@ _cleanup() { _cleanup_qemu _cleanup_test_img - rm -f "$TEST_IMG" "$DEST_IMG" + _rm_test_img "$TEST_IMG" + _rm_test_img "$DEST_IMG" } trap "_cleanup; exit \$status" 0 1 2 3 15 diff --git a/tests/qemu-iotests/232 b/tests/qemu-iotests/232 index 65b0e42063..685356ac3b 100755 --- a/tests/qemu-iotests/232 +++ b/tests/qemu-iotests/232 @@ -29,7 +29,9 @@ status=1 # failure is the default! _cleanup() { _cleanup_test_img - rm -f $TEST_IMG.[01234] + for img in "$TEST_IMG".[01234]; do + _rm_test_img "$img" + done } trap "_cleanup; exit \$status" 0 1 2 3 15 diff --git a/tests/qemu-iotests/234 b/tests/qemu-iotests/234 index 34c818c485..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. # @@ -69,9 +69,9 @@ with iotests.FilePath('img') as img_path, \ iotests.log(vm_a.qmp('migrate', uri='exec:cat >%s' % (fifo_a))) with iotests.Timeout(3, 'Migration does not complete'): # Wait for the source first (which includes setup=setup) - vm_a.wait_migration() + vm_a.wait_migration('postmigrate') # Wait for the destination second (which does not) - vm_b.wait_migration() + vm_b.wait_migration('running') iotests.log(vm_a.qmp('query-migrate')['return']['status']) iotests.log(vm_b.qmp('query-migrate')['return']['status']) @@ -98,9 +98,9 @@ with iotests.FilePath('img') as img_path, \ iotests.log(vm_b.qmp('migrate', uri='exec:cat >%s' % (fifo_b))) with iotests.Timeout(3, 'Migration does not complete'): # Wait for the source first (which includes setup=setup) - vm_b.wait_migration() + vm_b.wait_migration('postmigrate') # Wait for the destination second (which does not) - vm_a.wait_migration() + vm_a.wait_migration('running') iotests.log(vm_a.qmp('query-migrate')['return']['status']) iotests.log(vm_b.qmp('query-migrate')['return']['status']) diff --git a/tests/qemu-iotests/235 b/tests/qemu-iotests/235 index fedd111fd4..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 # @@ -49,7 +49,7 @@ qemu_img_create('-f', iotests.imgfmt, '-o', 'preallocation=metadata', disk, str(size)) vm = QEMUMachine(iotests.qemu_prog) -vm.add_args('-machine', 'accel=kvm:tcg') +vm.add_args('-accel', 'kvm', '-accel', 'tcg') if iotests.qemu_default_machine == 's390-ccw-virtio': vm.add_args('-no-shutdown') vm.add_args('-drive', 'id=src,file=' + disk) 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 06897f8c87..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 # @@ -26,15 +26,6 @@ from iotests import imgfmt iotests.verify_image_format(supported_fmts=['vmdk']) -def blockdev_create(vm, options): - result = vm.qmp_log('blockdev-create', job_id='job0', options=options, - filters=[iotests.filter_qmp_testfiles]) - - if 'return' in result: - assert result['return'] == {} - vm.run_job('job0') - iotests.log("") - with iotests.FilePath('t.vmdk') as disk_path, \ iotests.FilePath('t.vmdk.1') as extent1_path, \ iotests.FilePath('t.vmdk.2') as extent2_path, \ @@ -50,16 +41,16 @@ with iotests.FilePath('t.vmdk') as disk_path, \ size = 5 * 1024 * 1024 * 1024 vm.launch() - blockdev_create(vm, { 'driver': 'file', - 'filename': disk_path, - 'size': 0 }) + vm.blockdev_create({ 'driver': 'file', + 'filename': disk_path, + 'size': 0 }) vm.qmp_log('blockdev-add', driver='file', filename=disk_path, node_name='imgfile', filters=[iotests.filter_qmp_testfiles]) - blockdev_create(vm, { 'driver': imgfmt, - 'file': 'imgfile', - 'size': size }) + vm.blockdev_create({ 'driver': imgfmt, + 'file': 'imgfile', + 'size': size }) vm.shutdown() iotests.img_info_log(disk_path) @@ -74,21 +65,21 @@ with iotests.FilePath('t.vmdk') as disk_path, \ size = 64 * 1024 * 1024 vm.launch() - blockdev_create(vm, { 'driver': 'file', - 'filename': disk_path, - 'size': 0 }) + vm.blockdev_create({ 'driver': 'file', + 'filename': disk_path, + 'size': 0 }) - blockdev_create(vm, { 'driver': imgfmt, - 'file': { - 'driver': 'file', - 'filename': disk_path, - }, - 'size': size, - 'extents': [], - 'subformat': 'monolithicSparse', - 'adapter-type': 'ide', - 'hwversion': '4', - 'zeroed-grain': False }) + vm.blockdev_create({ 'driver': imgfmt, + 'file': { + 'driver': 'file', + 'filename': disk_path, + }, + 'size': size, + 'extents': [], + 'subformat': 'monolithicSparse', + 'adapter-type': 'ide', + 'hwversion': '4', + 'zeroed-grain': False }) vm.shutdown() iotests.img_info_log(disk_path) @@ -103,20 +94,20 @@ with iotests.FilePath('t.vmdk') as disk_path, \ size = 32 * 1024 * 1024 vm.launch() - blockdev_create(vm, { 'driver': 'file', - 'filename': disk_path, - 'size': 0 }) + vm.blockdev_create({ 'driver': 'file', + 'filename': disk_path, + 'size': 0 }) - blockdev_create(vm, { 'driver': imgfmt, - 'file': { - 'driver': 'file', - 'filename': disk_path, - }, - 'size': size, - 'extents': [], - 'subformat': 'monolithicSparse', - 'adapter-type': 'buslogic', - 'zeroed-grain': True }) + vm.blockdev_create({ 'driver': imgfmt, + 'file': { + 'driver': 'file', + 'filename': disk_path, + }, + 'size': size, + 'extents': [], + 'subformat': 'monolithicSparse', + 'adapter-type': 'buslogic', + 'zeroed-grain': True }) vm.shutdown() iotests.img_info_log(disk_path) @@ -128,9 +119,9 @@ with iotests.FilePath('t.vmdk') as disk_path, \ iotests.log("") vm.launch() - blockdev_create(vm, { 'driver': imgfmt, - 'file': "this doesn't exist", - 'size': size }) + vm.blockdev_create({ 'driver': imgfmt, + 'file': "this doesn't exist", + 'size': size }) vm.shutdown() # @@ -148,10 +139,10 @@ with iotests.FilePath('t.vmdk') as disk_path, \ vm.launch() for adapter_type in [ 'ide', 'buslogic', 'lsilogic', 'legacyESX' ]: - blockdev_create(vm, { 'driver': imgfmt, - 'file': 'node0', - 'size': size, - 'adapter-type': adapter_type }) + vm.blockdev_create({ 'driver': imgfmt, + 'file': 'node0', + 'size': size, + 'adapter-type': adapter_type }) vm.shutdown() # Invalid @@ -160,10 +151,10 @@ with iotests.FilePath('t.vmdk') as disk_path, \ vm.launch() for adapter_type in [ 'foo', 'IDE', 'legacyesx', 1 ]: - blockdev_create(vm, { 'driver': imgfmt, - 'file': 'node0', - 'size': size, - 'adapter-type': adapter_type }) + vm.blockdev_create({ 'driver': imgfmt, + 'file': 'node0', + 'size': size, + 'adapter-type': adapter_type }) vm.shutdown() # @@ -185,10 +176,10 @@ with iotests.FilePath('t.vmdk') as disk_path, \ iotests.log("") vm.launch() - blockdev_create(vm, { 'driver': imgfmt, - 'file': 'node0', - 'size': size, - 'subformat': 'monolithicFlat' }) + vm.blockdev_create({ 'driver': imgfmt, + 'file': 'node0', + 'size': size, + 'subformat': 'monolithicFlat' }) vm.shutdown() # Correct extent @@ -196,11 +187,11 @@ with iotests.FilePath('t.vmdk') as disk_path, \ iotests.log("") vm.launch() - blockdev_create(vm, { 'driver': imgfmt, - 'file': 'node0', - 'size': size, - 'subformat': 'monolithicFlat', - 'extents': ['ext1'] }) + vm.blockdev_create({ 'driver': imgfmt, + 'file': 'node0', + 'size': size, + 'subformat': 'monolithicFlat', + 'extents': ['ext1'] }) vm.shutdown() # Extra extent @@ -208,11 +199,11 @@ with iotests.FilePath('t.vmdk') as disk_path, \ iotests.log("") vm.launch() - blockdev_create(vm, { 'driver': imgfmt, - 'file': 'node0', - 'size': 512, - 'subformat': 'monolithicFlat', - 'extents': ['ext1', 'ext2', 'ext3'] }) + vm.blockdev_create({ 'driver': imgfmt, + 'file': 'node0', + 'size': 512, + 'subformat': 'monolithicFlat', + 'extents': ['ext1', 'ext2', 'ext3'] }) vm.shutdown() # Split formats @@ -228,11 +219,11 @@ with iotests.FilePath('t.vmdk') as disk_path, \ extents = [ "ext%d" % (i) for i in range(1, num_extents + 1) ] vm.launch() - blockdev_create(vm, { 'driver': imgfmt, - 'file': 'node0', - 'size': size, - 'subformat': subfmt, - 'extents': extents }) + vm.blockdev_create({ 'driver': imgfmt, + 'file': 'node0', + 'size': size, + 'subformat': subfmt, + 'extents': extents }) vm.shutdown() iotests.img_info_log(disk_path) 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/243 b/tests/qemu-iotests/243 index e563761307..a61852f6d9 100755 --- a/tests/qemu-iotests/243 +++ b/tests/qemu-iotests/243 @@ -29,7 +29,7 @@ status=1 # failure is the default! _cleanup() { _cleanup_test_img - rm -f $TEST_IMG.data + _rm_test_img "$TEST_IMG.data" } trap "_cleanup; exit \$status" 0 1 2 3 15 @@ -40,6 +40,10 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 _supported_fmt qcow2 _supported_proto file _supported_os Linux +# External data files do not work with compat=0.10, and because there +# is an explicit case for external data files here, we cannot allow +# the user to specify whether to use one +_unsupported_imgopts 'compat=0.10' data_file for mode in off metadata falloc full; do @@ -47,7 +51,7 @@ for mode in off metadata falloc full; do echo "=== preallocation=$mode ===" echo - IMGOPTS="preallocation=$mode" _make_test_img 64M + _make_test_img -o "preallocation=$mode" 64M printf "File size: " du -b $TEST_IMG | cut -f1 @@ -64,7 +68,7 @@ for mode in off metadata falloc full; do echo "=== External data file: preallocation=$mode ===" echo - IMGOPTS="data_file=$TEST_IMG.data,preallocation=$mode" _make_test_img 64M + _make_test_img -o "data_file=$TEST_IMG.data,preallocation=$mode" 64M echo -n "qcow2 file size: " du -b $TEST_IMG | cut -f1 diff --git a/tests/qemu-iotests/244 b/tests/qemu-iotests/244 index 13978f93d2..2ec1815e6f 100755 --- a/tests/qemu-iotests/244 +++ b/tests/qemu-iotests/244 @@ -29,8 +29,8 @@ status=1 # failure is the default! _cleanup() { _cleanup_test_img - rm -f $TEST_IMG.data - rm -f $TEST_IMG.src + _rm_test_img "$TEST_IMG.data" + _rm_test_img "$TEST_IMG.src" } trap "_cleanup; exit \$status" 0 1 2 3 15 @@ -41,13 +41,16 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 _supported_fmt qcow2 _supported_proto file _supported_os Linux +# External data files do not work with compat=0.10, and because we use +# our own external data file, we cannot let the user specify one +_unsupported_imgopts 'compat=0.10' data_file echo echo "=== Create and open image with external data file ===" echo echo "With data file name in the image:" -IMGOPTS="data_file=$TEST_IMG.data" _make_test_img 64M +_make_test_img -o "data_file=$TEST_IMG.data" 64M _check_test_img $QEMU_IO -c "open $TEST_IMG" -c "read -P 0 0 64k" 2>&1 | _filter_qemu_io | _filter_testdir @@ -104,7 +107,7 @@ echo echo "=== Standalone image with external data file (efficient) ===" echo -IMGOPTS="data_file=$TEST_IMG.data" _make_test_img 64M +_make_test_img -o "data_file=$TEST_IMG.data" 64M echo -n "qcow2 file size before I/O: " du -b $TEST_IMG | cut -f1 @@ -154,7 +157,7 @@ echo echo "=== Standalone image with external data file (valid raw) ===" echo -IMGOPTS="data_file=$TEST_IMG.data,data_file_raw=on" _make_test_img 64M +_make_test_img -o "data_file=$TEST_IMG.data,data_file_raw=on" 64M echo -n "qcow2 file size before I/O: " du -b $TEST_IMG | cut -f1 @@ -187,13 +190,27 @@ echo echo "=== bdrv_co_block_status test for file and offset=0 ===" echo -IMGOPTS="data_file=$TEST_IMG.data" _make_test_img 64M +_make_test_img -o "data_file=$TEST_IMG.data" 64M $QEMU_IO -c 'write -P 0x11 0 1M' -f $IMGFMT "$TEST_IMG" | _filter_qemu_io $QEMU_IO -c 'read -P 0x11 0 1M' -f $IMGFMT "$TEST_IMG" | _filter_qemu_io $QEMU_IMG map --output=human "$TEST_IMG" | _filter_testdir $QEMU_IMG map --output=json "$TEST_IMG" +echo +echo "=== Copy offloading ===" +echo + +# Make use of copy offloading if the test host can provide it +_make_test_img -o "data_file=$TEST_IMG.data" 64M +$QEMU_IMG convert -f $IMGFMT -O $IMGFMT -n -C "$TEST_IMG.src" "$TEST_IMG" +$QEMU_IMG compare -f $IMGFMT -F $IMGFMT "$TEST_IMG.src" "$TEST_IMG" + +# blkdebug doesn't support copy offloading, so this tests the error path +$QEMU_IMG amend -f $IMGFMT -o "data_file=blkdebug::$TEST_IMG.data" "$TEST_IMG" +$QEMU_IMG convert -f $IMGFMT -O $IMGFMT -n -C "$TEST_IMG.src" "$TEST_IMG" +$QEMU_IMG compare -f $IMGFMT -F $IMGFMT "$TEST_IMG.src" "$TEST_IMG" + # success, all done echo "*** done" rm -f $seq.full diff --git a/tests/qemu-iotests/244.out b/tests/qemu-iotests/244.out index 6a3d0067cc..56329deb4b 100644 --- a/tests/qemu-iotests/244.out +++ b/tests/qemu-iotests/244.out @@ -33,7 +33,7 @@ Convert to compressed target with data file: Formatting 'TEST_DIR/t.IMGFMT.src', fmt=IMGFMT size=67108864 wrote 1048576/1048576 bytes at offset 0 1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -qemu-img: error while writing sector 0: Operation not supported +qemu-img: error while writing at byte 0: Operation not supported Convert uncompressed, then write compressed data manually: Images are identical. @@ -122,4 +122,10 @@ Offset Length Mapped to File 0 0x100000 0 TEST_DIR/t.qcow2.data [{ "start": 0, "length": 1048576, "depth": 0, "zero": false, "data": true, "offset": 0}, { "start": 1048576, "length": 66060288, "depth": 0, "zero": true, "data": false}] + +=== Copy offloading === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 data_file=TEST_DIR/t.IMGFMT.data +Images are identical. +Images are identical. *** done diff --git a/tests/qemu-iotests/245 b/tests/qemu-iotests/245 old mode 100644 new mode 100755 index e66a23c5f0..1001275a44 --- 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 # @@ -478,6 +478,7 @@ class TestBlockdevReopen(iotests.QMPTestCase): # This test verifies that we can't change the children of a block # device during a reopen operation in a way that would create # cycles in the node graph + @iotests.skip_if_unsupported(['blkverify']) def test_graph_cycles(self): opts = [] @@ -534,6 +535,7 @@ class TestBlockdevReopen(iotests.QMPTestCase): self.assert_qmp(result, 'return', {}) # Misc reopen tests with different block drivers + @iotests.skip_if_unsupported(['quorum', 'throttle']) def test_misc_drivers(self): #################### ###### quorum ###### @@ -968,8 +970,7 @@ class TestBlockdevReopen(iotests.QMPTestCase): self.assertEqual(self.get_node('hd1'), None) self.assert_qmp(self.get_node('hd2'), 'ro', True) - # We don't allow setting a backing file that uses a different AioContext - def test_iothreads(self): + def run_test_iothreads(self, iothread_a, iothread_b, errmsg = None): opts = hd_opts(0) result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) self.assert_qmp(result, 'return', {}) @@ -984,20 +985,46 @@ class TestBlockdevReopen(iotests.QMPTestCase): result = self.vm.qmp('object-add', qom_type='iothread', id='iothread1') self.assert_qmp(result, 'return', {}) - result = self.vm.qmp('x-blockdev-set-iothread', node_name='hd0', iothread='iothread0') + result = self.vm.qmp('device_add', driver='virtio-scsi', id='scsi0', + iothread=iothread_a) self.assert_qmp(result, 'return', {}) - self.reopen(opts, {'backing': 'hd2'}, "Cannot use a new backing file with a different AioContext") - - result = self.vm.qmp('x-blockdev-set-iothread', node_name='hd2', iothread='iothread1') + result = self.vm.qmp('device_add', driver='virtio-scsi', id='scsi1', + iothread=iothread_b) self.assert_qmp(result, 'return', {}) - self.reopen(opts, {'backing': 'hd2'}, "Cannot use a new backing file with a different AioContext") + if iothread_a: + result = self.vm.qmp('device_add', driver='scsi-hd', drive='hd0', + share_rw=True, bus="scsi0.0") + self.assert_qmp(result, 'return', {}) - result = self.vm.qmp('x-blockdev-set-iothread', node_name='hd2', iothread='iothread0') - self.assert_qmp(result, 'return', {}) + if iothread_b: + result = self.vm.qmp('device_add', driver='scsi-hd', drive='hd2', + share_rw=True, bus="scsi1.0") + self.assert_qmp(result, 'return', {}) - self.reopen(opts, {'backing': 'hd2'}) + # Attaching the backing file may or may not work + self.reopen(opts, {'backing': 'hd2'}, errmsg) + + # But removing the backing file should always work + self.reopen(opts, {'backing': None}) + + self.vm.shutdown() + + # We don't allow setting a backing file that uses a different AioContext if + # neither of them can switch to the other AioContext + def test_iothreads_error(self): + self.run_test_iothreads('iothread0', 'iothread1', + "Cannot change iothread of active block backend") + + def test_iothreads_compatible_users(self): + self.run_test_iothreads('iothread0', 'iothread0') + + def test_iothreads_switch_backing(self): + self.run_test_iothreads('iothread0', None) + + def test_iothreads_switch_overlay(self): + self.run_test_iothreads(None, 'iothread0') if __name__ == '__main__': iotests.main(supported_fmts=["qcow2"], diff --git a/tests/qemu-iotests/245.out b/tests/qemu-iotests/245.out index a19de5214d..682b93394d 100644 --- a/tests/qemu-iotests/245.out +++ b/tests/qemu-iotests/245.out @@ -1,6 +1,6 @@ -.................. +..................... ---------------------------------------------------------------------- -Ran 18 tests +Ran 21 tests OK {"execute": "job-finalize", "arguments": {"id": "commit0"}} 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/247 b/tests/qemu-iotests/247 index c853b73819..87e37b39e2 100755 --- a/tests/qemu-iotests/247 +++ b/tests/qemu-iotests/247 @@ -29,7 +29,9 @@ status=1 # failure is the default! _cleanup() { _cleanup_test_img - rm -f $TEST_IMG.[01234] + for img in "$TEST_IMG".[01234]; do + _rm_test_img "$img" + done } trap "_cleanup; exit \$status" 0 1 2 3 15 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/249 b/tests/qemu-iotests/249 index e4650ecf6b..2b99c9789e 100755 --- a/tests/qemu-iotests/249 +++ b/tests/qemu-iotests/249 @@ -30,8 +30,8 @@ status=1 # failure is the default! _cleanup() { _cleanup_test_img - rm -f "$TEST_IMG.base" - rm -f "$TEST_IMG.int" + _rm_test_img "$TEST_IMG.base" + _rm_test_img "$TEST_IMG.int" } trap "_cleanup; exit \$status" 0 1 2 3 15 diff --git a/tests/qemu-iotests/250 b/tests/qemu-iotests/250 index c9c0a84a5a..9bb6b94d74 100755 --- a/tests/qemu-iotests/250 +++ b/tests/qemu-iotests/250 @@ -39,6 +39,8 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 _supported_fmt qcow2 _supported_proto file _supported_os Linux +# This test does not make much sense with external data files +_unsupported_imgopts data_file # This test checks that qcow2_process_discards does not truncate a discard # request > 2G. @@ -55,9 +57,8 @@ disk_usage() } size=2100M -IMGOPTS="cluster_size=1M,preallocation=metadata" -_make_test_img $size +_make_test_img -o "cluster_size=1M,preallocation=metadata" $size $QEMU_IO -c 'discard 0 10M' -c 'discard 2090M 10M' \ -c 'write 2090M 10M' -c 'write 0 10M' "$TEST_IMG" | _filter_qemu_io diff --git a/tests/qemu-iotests/252 b/tests/qemu-iotests/252 index f6c8f71444..83280c1715 100755 --- a/tests/qemu-iotests/252 +++ b/tests/qemu-iotests/252 @@ -29,7 +29,7 @@ status=1 # failure is the default! _cleanup() { _cleanup_test_img - rm -f "$TEST_IMG.base_new" + _rm_test_img "$TEST_IMG.base_new" } trap "_cleanup; exit \$status" 0 1 2 3 15 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 3632d507d0..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 # @@ -25,16 +25,6 @@ from iotests import imgfmt iotests.verify_image_format(supported_fmts=['qcow2']) -def blockdev_create(vm, options): - result = vm.qmp_log('blockdev-create', - filters=[iotests.filter_qmp_testfiles], - job_id='job0', options=options) - - if 'return' in result: - assert result['return'] == {} - vm.run_job('job0') - iotests.log("") - iotests.log('Finishing a commit job with background reads') iotests.log('============================================') iotests.log('') 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/259 b/tests/qemu-iotests/259 new file mode 100755 index 0000000000..62e29af05f --- /dev/null +++ b/tests/qemu-iotests/259 @@ -0,0 +1,62 @@ +#!/usr/bin/env bash +# +# Test generic image creation fallback (by using NBD) +# +# Copyright (C) 2019 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# creator +owner=mreitz@redhat.com + +seq=$(basename $0) +echo "QA output created by $seq" + +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter + +_supported_fmt raw +_supported_proto nbd +_supported_os Linux + + +_make_test_img 64M + +echo +echo '--- Testing creation ---' + +$QEMU_IMG create -f qcow2 "$TEST_IMG" 64M | _filter_img_create +$QEMU_IMG info "$TEST_IMG" | _filter_img_info + +echo +echo '--- Testing creation for which the node would need to grow ---' + +# NBD does not support resizing, so this will fail +$QEMU_IMG create -f qcow2 -o preallocation=metadata "$TEST_IMG" 64M 2>&1 \ + | _filter_img_create + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/259.out b/tests/qemu-iotests/259.out new file mode 100644 index 0000000000..ffed19c2a0 --- /dev/null +++ b/tests/qemu-iotests/259.out @@ -0,0 +1,14 @@ +QA output created by 259 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 + +--- Testing creation --- +Formatting 'TEST_DIR/t.IMGFMT', fmt=qcow2 size=67108864 +image: TEST_DIR/t.IMGFMT +file format: qcow2 +virtual size: 64 MiB (67108864 bytes) +disk size: unavailable + +--- Testing creation for which the node would need to grow --- +qemu-img: TEST_DIR/t.IMGFMT: Could not resize image: Image format driver does not support resize +Formatting 'TEST_DIR/t.IMGFMT', fmt=qcow2 size=67108864 preallocation=metadata +*** done 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/261 b/tests/qemu-iotests/261 index fb96bcfbe2..ddcb04f285 100755 --- a/tests/qemu-iotests/261 +++ b/tests/qemu-iotests/261 @@ -40,14 +40,15 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 . ./common.rc . ./common.filter -# This tests qocw2-specific low-level functionality +# This tests qcow2-specific low-level functionality _supported_fmt qcow2 _supported_proto file _supported_os Linux # (1) We create a v2 image that supports nothing but refcount_bits=16 # (2) We do some refcount management on our own which expects # refcount_bits=16 -_unsupported_imgopts 'refcount_bits=\([^1]\|.\([^6]\|$\)\)' +# As for data files, they do not support snapshots at all. +_unsupported_imgopts 'refcount_bits=\([^1]\|.\([^6]\|$\)\)' data_file # Parameters: # $1: image filename diff --git a/tests/qemu-iotests/262 b/tests/qemu-iotests/262 index 0963daa806..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. # @@ -71,9 +71,9 @@ with iotests.FilePath('img') as img_path, \ iotests.log(vm_a.qmp('migrate', uri='exec:cat >%s' % (fifo))) with iotests.Timeout(3, 'Migration does not complete'): # Wait for the source first (which includes setup=setup) - vm_a.wait_migration() + vm_a.wait_migration('postmigrate') # Wait for the destination second (which does not) - vm_b.wait_migration() + vm_b.wait_migration('running') iotests.log(vm_a.qmp('query-migrate')['return']['status']) iotests.log(vm_b.qmp('query-migrate')['return']['status']) 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/265 b/tests/qemu-iotests/265 index dce6f77be3..00f2ec769e 100755 --- a/tests/qemu-iotests/265 +++ b/tests/qemu-iotests/265 @@ -41,7 +41,7 @@ _supported_os Linux echo '--- Writing to the image ---' # Reduce cluster size so we get more and quicker I/O -IMGOPTS='cluster_size=4096' _make_test_img 1M +_make_test_img -o 'cluster_size=4096' 1M (for ((kb = 1024 - 4; kb >= 0; kb -= 4)); do \ echo "aio_write -P 42 $((kb + 1))k 2k"; \ done) \ diff --git a/tests/qemu-iotests/266 b/tests/qemu-iotests/266 index 5b35cd67e4..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 # @@ -22,15 +22,6 @@ import iotests from iotests import imgfmt -def blockdev_create(vm, options): - result = vm.qmp_log('blockdev-create', job_id='job0', options=options, - filters=[iotests.filter_qmp_testfiles]) - - if 'return' in result: - assert result['return'] == {} - vm.run_job('job0') - - # Successful image creation (defaults) def implicit_defaults(vm, file_path): iotests.log("=== Successful image creation (defaults) ===") @@ -40,9 +31,9 @@ def implicit_defaults(vm, file_path): # (Close to 64 MB) size = 8 * 964 * 17 * 512 - blockdev_create(vm, { 'driver': imgfmt, - 'file': 'protocol-node', - 'size': size }) + vm.blockdev_create({ 'driver': imgfmt, + 'file': 'protocol-node', + 'size': size }) # Successful image creation (explicit defaults) @@ -54,11 +45,11 @@ def explicit_defaults(vm, file_path): # (Close to 128 MB) size = 16 * 964 * 17 * 512 - blockdev_create(vm, { 'driver': imgfmt, - 'file': 'protocol-node', - 'size': size, - 'subformat': 'dynamic', - 'force-size': False }) + vm.blockdev_create({ 'driver': imgfmt, + 'file': 'protocol-node', + 'size': size, + 'subformat': 'dynamic', + 'force-size': False }) # Successful image creation (non-default options) @@ -69,11 +60,11 @@ def non_defaults(vm, file_path): # Not representable in CHS (fine with force-size=True) size = 1048576 - blockdev_create(vm, { 'driver': imgfmt, - 'file': 'protocol-node', - 'size': size, - 'subformat': 'fixed', - 'force-size': True }) + vm.blockdev_create({ 'driver': imgfmt, + 'file': 'protocol-node', + 'size': size, + 'subformat': 'fixed', + 'force-size': True }) # Size not representable in CHS with force-size=False @@ -84,10 +75,10 @@ def non_chs_size_without_force(vm, file_path): # Not representable in CHS (will not work with force-size=False) size = 1048576 - blockdev_create(vm, { 'driver': imgfmt, - 'file': 'protocol-node', - 'size': size, - 'force-size': False }) + vm.blockdev_create({ 'driver': imgfmt, + 'file': 'protocol-node', + 'size': size, + 'force-size': False }) # Zero size @@ -95,9 +86,9 @@ def zero_size(vm, file_path): iotests.log("=== Zero size===") iotests.log("") - blockdev_create(vm, { 'driver': imgfmt, - 'file': 'protocol-node', - 'size': 0 }) + vm.blockdev_create({ 'driver': imgfmt, + 'file': 'protocol-node', + 'size': 0 }) # Maximum CHS size @@ -105,9 +96,9 @@ def maximum_chs_size(vm, file_path): iotests.log("=== Maximum CHS size===") iotests.log("") - blockdev_create(vm, { 'driver': imgfmt, - 'file': 'protocol-node', - 'size': 16 * 65535 * 255 * 512 }) + vm.blockdev_create({ 'driver': imgfmt, + 'file': 'protocol-node', + 'size': 16 * 65535 * 255 * 512 }) # Actual maximum size @@ -115,10 +106,10 @@ def maximum_size(vm, file_path): iotests.log("=== Actual maximum size===") iotests.log("") - blockdev_create(vm, { 'driver': imgfmt, - 'file': 'protocol-node', - 'size': 0xff000000 * 512, - 'force-size': True }) + vm.blockdev_create({ 'driver': imgfmt, + 'file': 'protocol-node', + 'size': 0xff000000 * 512, + 'force-size': True }) def main(): @@ -132,9 +123,9 @@ def main(): vm.launch() iotests.log('--- Creating empty file ---') - blockdev_create(vm, { 'driver': 'file', - 'filename': file_path, - 'size': 0 }) + vm.blockdev_create({ 'driver': 'file', + 'filename': file_path, + 'size': 0 }) vm.qmp_log('blockdev-add', driver='file', filename=file_path, node_name='protocol-node', diff --git a/tests/qemu-iotests/266.out b/tests/qemu-iotests/266.out index b11953e81f..5a7d7d01aa 100644 --- a/tests/qemu-iotests/266.out +++ b/tests/qemu-iotests/266.out @@ -3,6 +3,7 @@ {"return": {}} {"execute": "job-dismiss", "arguments": {"id": "job0"}} {"return": {}} + {"execute": "blockdev-add", "arguments": {"driver": "file", "filename": "TEST_DIR/PID-t.vpc", "node-name": "protocol-node"}} {"return": {}} @@ -13,6 +14,7 @@ {"execute": "job-dismiss", "arguments": {"id": "job0"}} {"return": {}} + image: TEST_IMG file format: IMGFMT virtual size: 64 MiB (67125248 bytes) @@ -23,6 +25,7 @@ cluster_size: 2097152 {"return": {}} {"execute": "job-dismiss", "arguments": {"id": "job0"}} {"return": {}} + {"execute": "blockdev-add", "arguments": {"driver": "file", "filename": "TEST_DIR/PID-t.vpc", "node-name": "protocol-node"}} {"return": {}} @@ -33,6 +36,7 @@ cluster_size: 2097152 {"execute": "job-dismiss", "arguments": {"id": "job0"}} {"return": {}} + image: TEST_IMG file format: IMGFMT virtual size: 128 MiB (134250496 bytes) @@ -43,6 +47,7 @@ cluster_size: 2097152 {"return": {}} {"execute": "job-dismiss", "arguments": {"id": "job0"}} {"return": {}} + {"execute": "blockdev-add", "arguments": {"driver": "file", "filename": "TEST_DIR/PID-t.vpc", "node-name": "protocol-node"}} {"return": {}} @@ -53,6 +58,7 @@ cluster_size: 2097152 {"execute": "job-dismiss", "arguments": {"id": "job0"}} {"return": {}} + image: TEST_IMG file format: IMGFMT virtual size: 1 MiB (1048576 bytes) @@ -62,6 +68,7 @@ virtual size: 1 MiB (1048576 bytes) {"return": {}} {"execute": "job-dismiss", "arguments": {"id": "job0"}} {"return": {}} + {"execute": "blockdev-add", "arguments": {"driver": "file", "filename": "TEST_DIR/PID-t.vpc", "node-name": "protocol-node"}} {"return": {}} @@ -73,6 +80,7 @@ Job failed: The requested image size cannot be represented in CHS geometry {"execute": "job-dismiss", "arguments": {"id": "job0"}} {"return": {}} + qemu-img: Could not open 'TEST_IMG': File too small for a VHD header --- Creating empty file --- @@ -80,6 +88,7 @@ qemu-img: Could not open 'TEST_IMG': File too small for a VHD header {"return": {}} {"execute": "job-dismiss", "arguments": {"id": "job0"}} {"return": {}} + {"execute": "blockdev-add", "arguments": {"driver": "file", "filename": "TEST_DIR/PID-t.vpc", "node-name": "protocol-node"}} {"return": {}} @@ -90,6 +99,7 @@ qemu-img: Could not open 'TEST_IMG': File too small for a VHD header {"execute": "job-dismiss", "arguments": {"id": "job0"}} {"return": {}} + image: TEST_IMG file format: IMGFMT virtual size: 0 B (0 bytes) @@ -100,6 +110,7 @@ cluster_size: 2097152 {"return": {}} {"execute": "job-dismiss", "arguments": {"id": "job0"}} {"return": {}} + {"execute": "blockdev-add", "arguments": {"driver": "file", "filename": "TEST_DIR/PID-t.vpc", "node-name": "protocol-node"}} {"return": {}} @@ -110,6 +121,7 @@ cluster_size: 2097152 {"execute": "job-dismiss", "arguments": {"id": "job0"}} {"return": {}} + image: TEST_IMG file format: IMGFMT virtual size: 127 GiB (136899993600 bytes) @@ -120,6 +132,7 @@ cluster_size: 2097152 {"return": {}} {"execute": "job-dismiss", "arguments": {"id": "job0"}} {"return": {}} + {"execute": "blockdev-add", "arguments": {"driver": "file", "filename": "TEST_DIR/PID-t.vpc", "node-name": "protocol-node"}} {"return": {}} @@ -130,6 +143,7 @@ cluster_size: 2097152 {"execute": "job-dismiss", "arguments": {"id": "job0"}} {"return": {}} + image: TEST_IMG file format: IMGFMT virtual size: 1.99 TiB (2190433320960 bytes) diff --git a/tests/qemu-iotests/267 b/tests/qemu-iotests/267 index 170e173c0a..3146273eef 100755 --- a/tests/qemu-iotests/267 +++ b/tests/qemu-iotests/267 @@ -40,9 +40,13 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 _supported_fmt qcow2 _supported_proto file _supported_os Linux +_require_drivers copy-on-read -# Internal snapshots are (currently) impossible with refcount_bits=1 -_unsupported_imgopts 'refcount_bits=1[^0-9]' +# Internal snapshots are (currently) impossible with refcount_bits=1, +# and generally impossible with external data files +_unsupported_imgopts 'refcount_bits=1[^0-9]' data_file + +_require_devices virtio-blk do_run_qemu() { @@ -68,7 +72,11 @@ size=128M run_test() { - _make_test_img $size + if [ -n "$BACKING_FILE" ]; then + _make_test_img -b "$BACKING_FILE" $size + else + _make_test_img $size + fi printf "savevm snap0\ninfo snapshots\nloadvm snap0\n" | run_qemu "$@" | _filter_date } @@ -119,12 +127,12 @@ echo TEST_IMG="$TEST_IMG.base" _make_test_img $size -IMGOPTS="backing_file=$TEST_IMG.base" \ +BACKING_FILE="$TEST_IMG.base" \ run_test -blockdev driver=file,filename="$TEST_IMG.base",node-name=backing-file \ -blockdev driver=file,filename="$TEST_IMG",node-name=file \ -blockdev driver=$IMGFMT,file=file,backing=backing-file,node-name=fmt -IMGOPTS="backing_file=$TEST_IMG.base" \ +BACKING_FILE="$TEST_IMG.base" \ run_test -blockdev driver=file,filename="$TEST_IMG.base",node-name=backing-file \ -blockdev driver=$IMGFMT,file=backing-file,node-name=backing-fmt \ -blockdev driver=file,filename="$TEST_IMG",node-name=file \ @@ -141,7 +149,7 @@ echo echo "=== -blockdev with NBD server on the backing file ===" echo -IMGOPTS="backing_file=$TEST_IMG.base" _make_test_img $size +_make_test_img -b "$TEST_IMG.base" $size cat <&1 | _filter_testdir | _filter_qemu | _filter_qmp | - _filter_generated_node_ids | _filter_imgfmt | _filter_actual_image_size + _filter_generated_node_ids | _filter_imgfmt | + _filter_actual_image_size | _filter_img_info } TEST_IMG="$TEST_IMG.base" _make_test_img 64M diff --git a/tests/qemu-iotests/273.out b/tests/qemu-iotests/273.out index c410fee5c4..684b8d6f77 100644 --- a/tests/qemu-iotests/273.out +++ b/tests/qemu-iotests/273.out @@ -38,15 +38,6 @@ Testing: -blockdev file,node-name=base,filename=TEST_DIR/t.IMGFMT.base -blockdev "cluster-size": 65536, "format": "IMGFMT", "actual-size": SIZE, - "format-specific": { - "type": "IMGFMT", - "data": { - "compat": "1.1", - "lazy-refcounts": false, - "refcount-bits": 16, - "corrupt": false - } - }, "full-backing-filename": "TEST_DIR/t.IMGFMT.base", "backing-filename": "TEST_DIR/t.IMGFMT.base", "dirty-flag": false @@ -57,15 +48,6 @@ Testing: -blockdev file,node-name=base,filename=TEST_DIR/t.IMGFMT.base -blockdev "cluster-size": 65536, "format": "IMGFMT", "actual-size": SIZE, - "format-specific": { - "type": "IMGFMT", - "data": { - "compat": "1.1", - "lazy-refcounts": false, - "refcount-bits": 16, - "corrupt": false - } - }, "full-backing-filename": "TEST_DIR/t.IMGFMT.mid", "backing-filename": "TEST_DIR/t.IMGFMT.mid", "dirty-flag": false @@ -136,15 +118,6 @@ Testing: -blockdev file,node-name=base,filename=TEST_DIR/t.IMGFMT.base -blockdev "cluster-size": 65536, "format": "IMGFMT", "actual-size": SIZE, - "format-specific": { - "type": "IMGFMT", - "data": { - "compat": "1.1", - "lazy-refcounts": false, - "refcount-bits": 16, - "corrupt": false - } - }, "full-backing-filename": "TEST_DIR/t.IMGFMT.base", "backing-filename": "TEST_DIR/t.IMGFMT.base", "dirty-flag": false 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/279 b/tests/qemu-iotests/279 new file mode 100755 index 0000000000..75a4747e6b --- /dev/null +++ b/tests/qemu-iotests/279 @@ -0,0 +1,60 @@ +#!/usr/bin/env bash +# +# Test qemu-img --backing-chain --image-opts +# +# Copyright (C) 2019 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +seq=$(basename "$0") +echo "QA output created by $seq" + +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img + _rm_test_img "$TEST_IMG.mid" +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter + +# Backing files are required... +_supported_fmt qcow qcow2 vmdk qed +_supported_proto file +_supported_os Linux +_unsupported_imgopts "subformat=monolithicFlat" \ + "subformat=twoGbMaxExtentFlat" \ + +TEST_IMG="$TEST_IMG.base" _make_test_img 64M +TEST_IMG="$TEST_IMG.mid" _make_test_img -b "$TEST_IMG.base" +_make_test_img -b "$TEST_IMG.mid" + +echo +echo '== qemu-img info --backing-chain ==' +_img_info --backing-chain | _filter_img_info | grep -v 'backing file format' + +echo +echo '== qemu-img info --backing-chain --image-opts ==' +TEST_IMG="driver=$IMGFMT,file.driver=file,file.filename=$TEST_IMG" _img_info --backing-chain --image-opts \ + | _filter_img_info | grep -v 'backing file format' + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/279.out b/tests/qemu-iotests/279.out new file mode 100644 index 0000000000..f4dc6c69cb --- /dev/null +++ b/tests/qemu-iotests/279.out @@ -0,0 +1,35 @@ +QA output created by 279 +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT.mid', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.base +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.mid + +== qemu-img info --backing-chain == +image: TEST_DIR/t.IMGFMT +file format: IMGFMT +virtual size: 64 MiB (67108864 bytes) +backing file: TEST_DIR/t.IMGFMT.mid + +image: TEST_DIR/t.IMGFMT.mid +file format: IMGFMT +virtual size: 64 MiB (67108864 bytes) +backing file: TEST_DIR/t.IMGFMT.base + +image: TEST_DIR/t.IMGFMT.base +file format: IMGFMT +virtual size: 64 MiB (67108864 bytes) + +== qemu-img info --backing-chain --image-opts == +image: TEST_DIR/t.IMGFMT +file format: IMGFMT +virtual size: 64 MiB (67108864 bytes) +backing file: TEST_DIR/t.IMGFMT.mid + +image: TEST_DIR/t.IMGFMT.mid +file format: IMGFMT +virtual size: 64 MiB (67108864 bytes) +backing file: TEST_DIR/t.IMGFMT.base + +image: TEST_DIR/t.IMGFMT.base +file format: IMGFMT +virtual size: 64 MiB (67108864 bytes) +*** done diff --git a/tests/qemu-iotests/280 b/tests/qemu-iotests/280 new file mode 100755 index 0000000000..69288fdd0e --- /dev/null +++ b/tests/qemu-iotests/280 @@ -0,0 +1,83 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2019 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# Creator/Owner: Kevin Wolf +# +# Test migration to file for taking an external snapshot with VM state. + +import iotests +import os + +iotests.verify_image_format(supported_fmts=['qcow2']) +iotests.verify_protocol(supported=['file']) +iotests.verify_platform(['linux']) + +with iotests.FilePath('base') as base_path , \ + iotests.FilePath('top') as top_path, \ + iotests.VM() as vm: + + iotests.qemu_img_log('create', '-f', iotests.imgfmt, base_path, '64M') + + iotests.log('=== Launch VM ===') + vm.add_object('iothread,id=iothread0') + vm.add_blockdev('file,filename=%s,node-name=base-file' % (base_path)) + vm.add_blockdev('%s,file=base-file,node-name=base-fmt' % (iotests.imgfmt)) + vm.add_device('virtio-blk,drive=base-fmt,iothread=iothread0,id=vda') + vm.launch() + + vm.enable_migration_events('VM') + + iotests.log('\n=== Migrate to file ===') + vm.qmp_log('migrate', uri='exec:cat > /dev/null') + + with iotests.Timeout(3, 'Migration does not complete'): + vm.wait_migration('postmigrate') + + iotests.log('\nVM is now stopped:') + iotests.log(vm.qmp('query-migrate')['return']['status']) + vm.qmp_log('query-status') + + iotests.log('\n=== Create a snapshot of the disk image ===') + vm.blockdev_create({ + 'driver': 'file', + 'filename': top_path, + 'size': 0, + }) + vm.qmp_log('blockdev-add', node_name='top-file', + driver='file', filename=top_path, + filters=[iotests.filter_qmp_testfiles]) + + vm.blockdev_create({ + 'driver': iotests.imgfmt, + 'file': 'top-file', + 'size': 1024 * 1024, + }) + vm.qmp_log('blockdev-add', node_name='top-fmt', + driver=iotests.imgfmt, file='top-file') + + vm.qmp_log('blockdev-snapshot', node='base-fmt', overlay='top-fmt') + + iotests.log('\n=== Resume the VM and simulate a write request ===') + vm.qmp_log('cont') + iotests.log(vm.hmp_qemu_io('-d vda/virtio-backend', 'write 4k 4k')) + + iotests.log('\n=== Commit it to the backing file ===') + result = vm.qmp_log('block-commit', job_id='job0', auto_dismiss=False, + device='top-fmt', top_node='top-fmt', + filters=[iotests.filter_qmp_testfiles]) + if 'return' in result: + vm.run_job('job0') diff --git a/tests/qemu-iotests/280.out b/tests/qemu-iotests/280.out new file mode 100644 index 0000000000..5d382faaa8 --- /dev/null +++ b/tests/qemu-iotests/280.out @@ -0,0 +1,50 @@ +Formatting 'TEST_DIR/PID-base', fmt=qcow2 size=67108864 cluster_size=65536 lazy_refcounts=off refcount_bits=16 + +=== Launch VM === +Enabling migration QMP events on VM... +{"return": {}} + +=== Migrate to file === +{"execute": "migrate", "arguments": {"uri": "exec:cat > /dev/null"}} +{"return": {}} +{"data": {"status": "setup"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"data": {"status": "active"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"data": {"status": "completed"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} + +VM is now stopped: +completed +{"execute": "query-status", "arguments": {}} +{"return": {"running": false, "singlestep": false, "status": "postmigrate"}} + +=== Create a snapshot of the disk image === +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-top", "size": 0}}} +{"return": {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} + +{"execute": "blockdev-add", "arguments": {"driver": "file", "filename": "TEST_DIR/PID-top", "node-name": "top-file"}} +{"return": {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "qcow2", "file": "top-file", "size": 1048576}}} +{"return": {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} + +{"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": "top-file", "node-name": "top-fmt"}} +{"return": {}} +{"execute": "blockdev-snapshot", "arguments": {"node": "base-fmt", "overlay": "top-fmt"}} +{"return": {}} + +=== Resume the VM and simulate a write request === +{"execute": "cont", "arguments": {}} +{"return": {}} +{"return": ""} + +=== Commit it to the backing file === +{"execute": "block-commit", "arguments": {"auto-dismiss": false, "device": "top-fmt", "job-id": "job0", "top-node": "top-fmt"}} +{"return": {}} +{"execute": "job-complete", "arguments": {"id": "job0"}} +{"return": {}} +{"data": {"device": "job0", "len": 65536, "offset": 65536, "speed": 0, "type": "commit"}, "event": "BLOCK_JOB_READY", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"data": {"device": "job0", "len": 65536, "offset": 65536, "speed": 0, "type": "commit"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} diff --git a/tests/qemu-iotests/281 b/tests/qemu-iotests/281 new file mode 100755 index 0000000000..0bf973bca6 --- /dev/null +++ b/tests/qemu-iotests/281 @@ -0,0 +1,247 @@ +#!/usr/bin/env python3 +# +# Test cases for blockdev + IOThread interactions +# +# Copyright (C) 2019 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +import os +import iotests +from iotests import qemu_img + +image_len = 64 * 1024 * 1024 + +# Test for RHBZ#1782175 +class TestDirtyBitmapIOThread(iotests.QMPTestCase): + drive0_img = os.path.join(iotests.test_dir, 'drive0.img') + images = { 'drive0': drive0_img } + + def setUp(self): + for name in self.images: + qemu_img('create', '-f', iotests.imgfmt, + self.images[name], str(image_len)) + + self.vm = iotests.VM() + self.vm.add_object('iothread,id=iothread0') + + for name in self.images: + self.vm.add_blockdev('driver=file,filename=%s,node-name=file_%s' + % (self.images[name], name)) + self.vm.add_blockdev('driver=qcow2,file=file_%s,node-name=%s' + % (name, name)) + + self.vm.launch() + self.vm.qmp('x-blockdev-set-iothread', + node_name='drive0', iothread='iothread0', + force=True) + + def tearDown(self): + self.vm.shutdown() + for name in self.images: + os.remove(self.images[name]) + + def test_add_dirty_bitmap(self): + result = self.vm.qmp( + 'block-dirty-bitmap-add', + node='drive0', + name='bitmap1', + persistent=True, + ) + + self.assert_qmp(result, 'return', {}) + + +# Test for RHBZ#1746217 & RHBZ#1773517 +class TestNBDMirrorIOThread(iotests.QMPTestCase): + nbd_sock = os.path.join(iotests.sock_dir, 'nbd.sock') + drive0_img = os.path.join(iotests.test_dir, 'drive0.img') + mirror_img = os.path.join(iotests.test_dir, 'mirror.img') + images = { 'drive0': drive0_img, 'mirror': mirror_img } + + def setUp(self): + for name in self.images: + qemu_img('create', '-f', iotests.imgfmt, + self.images[name], str(image_len)) + + self.vm_src = iotests.VM(path_suffix='src') + self.vm_src.add_object('iothread,id=iothread0') + self.vm_src.add_blockdev('driver=file,filename=%s,node-name=file0' + % (self.drive0_img)) + self.vm_src.add_blockdev('driver=qcow2,file=file0,node-name=drive0') + self.vm_src.launch() + self.vm_src.qmp('x-blockdev-set-iothread', + node_name='drive0', iothread='iothread0', + force=True) + + self.vm_tgt = iotests.VM(path_suffix='tgt') + self.vm_tgt.add_object('iothread,id=iothread0') + self.vm_tgt.add_blockdev('driver=file,filename=%s,node-name=file0' + % (self.mirror_img)) + self.vm_tgt.add_blockdev('driver=qcow2,file=file0,node-name=drive0') + self.vm_tgt.launch() + self.vm_tgt.qmp('x-blockdev-set-iothread', + node_name='drive0', iothread='iothread0', + force=True) + + def tearDown(self): + self.vm_src.shutdown() + self.vm_tgt.shutdown() + for name in self.images: + os.remove(self.images[name]) + + def test_nbd_mirror(self): + result = self.vm_tgt.qmp( + 'nbd-server-start', + addr={ + 'type': 'unix', + 'data': { 'path': self.nbd_sock } + } + ) + self.assert_qmp(result, 'return', {}) + + result = self.vm_tgt.qmp( + 'nbd-server-add', + device='drive0', + writable=True + ) + self.assert_qmp(result, 'return', {}) + + result = self.vm_src.qmp( + 'drive-mirror', + device='drive0', + target='nbd+unix:///drive0?socket=' + self.nbd_sock, + sync='full', + mode='existing', + speed=64*1024*1024, + job_id='j1' + ) + self.assert_qmp(result, 'return', {}) + + self.vm_src.event_wait(name="BLOCK_JOB_READY") + + +# Test for RHBZ#1779036 +class TestExternalSnapshotAbort(iotests.QMPTestCase): + drive0_img = os.path.join(iotests.test_dir, 'drive0.img') + snapshot_img = os.path.join(iotests.test_dir, 'snapshot.img') + images = { 'drive0': drive0_img, 'snapshot': snapshot_img } + + def setUp(self): + for name in self.images: + qemu_img('create', '-f', iotests.imgfmt, + self.images[name], str(image_len)) + + self.vm = iotests.VM() + self.vm.add_object('iothread,id=iothread0') + self.vm.add_blockdev('driver=file,filename=%s,node-name=file0' + % (self.drive0_img)) + self.vm.add_blockdev('driver=qcow2,file=file0,node-name=drive0') + self.vm.launch() + self.vm.qmp('x-blockdev-set-iothread', + node_name='drive0', iothread='iothread0', + force=True) + + def tearDown(self): + self.vm.shutdown() + for name in self.images: + os.remove(self.images[name]) + + def test_external_snapshot_abort(self): + # Use a two actions transaction with a bogus values on the second + # one to trigger an abort of the transaction. + result = self.vm.qmp('transaction', actions=[ + { + 'type': 'blockdev-snapshot-sync', + 'data': { 'node-name': 'drive0', + 'snapshot-file': self.snapshot_img, + 'snapshot-node-name': 'snap1', + 'mode': 'absolute-paths', + 'format': 'qcow2' } + }, + { + 'type': 'blockdev-snapshot-sync', + 'data': { 'node-name': 'drive0', + 'snapshot-file': '/fakesnapshot', + 'snapshot-node-name': 'snap2', + 'mode': 'absolute-paths', + 'format': 'qcow2' } + }, + ]) + + # Crashes on failure, we expect this error. + self.assert_qmp(result, 'error/class', 'GenericError') + + +# Test for RHBZ#1782111 +class TestBlockdevBackupAbort(iotests.QMPTestCase): + drive0_img = os.path.join(iotests.test_dir, 'drive0.img') + drive1_img = os.path.join(iotests.test_dir, 'drive1.img') + snap0_img = os.path.join(iotests.test_dir, 'snap0.img') + snap1_img = os.path.join(iotests.test_dir, 'snap1.img') + images = { 'drive0': drive0_img, + 'drive1': drive1_img, + 'snap0': snap0_img, + 'snap1': snap1_img } + + def setUp(self): + for name in self.images: + qemu_img('create', '-f', iotests.imgfmt, + self.images[name], str(image_len)) + + self.vm = iotests.VM() + self.vm.add_object('iothread,id=iothread0') + self.vm.add_device('virtio-scsi,iothread=iothread0') + + for name in self.images: + self.vm.add_blockdev('driver=file,filename=%s,node-name=file_%s' + % (self.images[name], name)) + self.vm.add_blockdev('driver=qcow2,file=file_%s,node-name=%s' + % (name, name)) + + self.vm.add_device('scsi-hd,drive=drive0') + self.vm.add_device('scsi-hd,drive=drive1') + self.vm.launch() + + def tearDown(self): + self.vm.shutdown() + for name in self.images: + os.remove(self.images[name]) + + def test_blockdev_backup_abort(self): + # Use a two actions transaction with a bogus values on the second + # one to trigger an abort of the transaction. + result = self.vm.qmp('transaction', actions=[ + { + 'type': 'blockdev-backup', + 'data': { 'device': 'drive0', + 'target': 'snap0', + 'sync': 'full', + 'job-id': 'j1' } + }, + { + 'type': 'blockdev-backup', + 'data': { 'device': 'drive1', + 'target': 'snap1', + 'sync': 'full' } + }, + ]) + + # Hangs on failure, we expect this error. + self.assert_qmp(result, 'error/class', 'GenericError') + +if __name__ == '__main__': + iotests.main(supported_fmts=['qcow2'], + supported_protocols=['file']) diff --git a/tests/qemu-iotests/281.out b/tests/qemu-iotests/281.out new file mode 100644 index 0000000000..89968f35d7 --- /dev/null +++ b/tests/qemu-iotests/281.out @@ -0,0 +1,5 @@ +.... +---------------------------------------------------------------------- +Ran 4 tests + +OK diff --git a/tests/qemu-iotests/282 b/tests/qemu-iotests/282 new file mode 100755 index 0000000000..081eb12080 --- /dev/null +++ b/tests/qemu-iotests/282 @@ -0,0 +1,67 @@ +#!/usr/bin/env bash +# +# Test qemu-img file cleanup for LUKS when using a non-UTF8 secret +# +# Copyright (C) 2020, IBM Corporation. +# +# 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 . +# + +seq=`basename $0` +echo "QA output created by $seq" + +status=1 # failure is the default! +TEST_IMAGE_FILE='vol.img' + +_cleanup() +{ + _cleanup_test_img + rm non_utf8_secret + rm -f $TEST_IMAGE_FILE +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter + +_supported_fmt luks +_supported_proto generic +_unsupported_proto vxhs + +echo "== Create non-UTF8 secret ==" +echo -n -e '\x3a\x3c\x3b\xff' > non_utf8_secret +SECRET="secret,id=sec0,file=non_utf8_secret" + +echo "== Throws an error because of invalid UTF-8 secret ==" +$QEMU_IMG create -f $IMGFMT --object $SECRET -o "key-secret=sec0" $TEST_IMAGE_FILE 4M + +echo "== Image file should not exist after the error ==" +if test -f "$TEST_IMAGE_FILE"; then + exit 1 +fi + +echo "== Create a stub image file and run qemu-img again ==" +touch $TEST_IMAGE_FILE +$QEMU_IMG create -f $IMGFMT --object $SECRET -o "key-secret=sec0" $TEST_IMAGE_FILE 4M + +echo "== Pre-existing image file should also be deleted after the error ==" +if test -f "$TEST_IMAGE_FILE"; then + exit 1 +fi + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/282.out b/tests/qemu-iotests/282.out new file mode 100644 index 0000000000..5d079dabce --- /dev/null +++ b/tests/qemu-iotests/282.out @@ -0,0 +1,11 @@ +QA output created by 282 +== Create non-UTF8 secret == +== Throws an error because of invalid UTF-8 secret == +qemu-img: vol.img: Data from secret sec0 is not valid UTF-8 +Formatting 'vol.img', fmt=luks size=4194304 key-secret=sec0 +== Image file should not exist after the error == +== Create a stub image file and run qemu-img again == +qemu-img: vol.img: Data from secret sec0 is not valid UTF-8 +Formatting 'vol.img', fmt=luks size=4194304 key-secret=sec0 +== Pre-existing image file should also be deleted after the error == + *** done 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 . +# + +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/284 b/tests/qemu-iotests/284 new file mode 100755 index 0000000000..071e89b33e --- /dev/null +++ b/tests/qemu-iotests/284 @@ -0,0 +1,97 @@ +#!/usr/bin/env bash +# +# Test ref count checks on encrypted images +# +# Copyright (C) 2019 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# creator +owner=berrange@redhat.com + +seq=`basename $0` +echo "QA output created by $seq" + +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter + +_supported_fmt qcow2 +_supported_proto generic +_supported_os Linux + + +size=1M + +SECRET="secret,id=sec0,data=astrochicken" + +IMGSPEC="driver=$IMGFMT,file.filename=$TEST_IMG,encrypt.key-secret=sec0" +QEMU_IO_OPTIONS=$QEMU_IO_OPTIONS_NO_FMT + +_run_test() +{ + IMGOPTSSYNTAX=true + OLD_TEST_IMG="$TEST_IMG" + TEST_IMG="driver=$IMGFMT,file.filename=$TEST_IMG,encrypt.key-secret=sec0" + QEMU_IMG_EXTRA_ARGS="--image-opts --object $SECRET" + + echo + echo "== cluster size $csize" + echo "== checking image refcounts ==" + _check_test_img + + echo + echo "== writing some data ==" + $QEMU_IO -c "write -P 0x9 0 1" $QEMU_IMG_EXTRA_ARGS $TEST_IMG | _filter_qemu_io | _filter_testdir + echo + echo "== rechecking image refcounts ==" + _check_test_img + + echo + echo "== writing some more data ==" + $QEMU_IO -c "write -P 0x9 $csize 1" $QEMU_IMG_EXTRA_ARGS $TEST_IMG | _filter_qemu_io | _filter_testdir + echo + echo "== rechecking image refcounts ==" + _check_test_img + + TEST_IMG="$OLD_TEST_IMG" + QEMU_IMG_EXTRA_ARGS= + IMGOPTSSYNTAX= +} + + +echo +echo "testing LUKS qcow2 encryption" +echo + +for csize in 512 2048 32768 +do + _make_test_img --object $SECRET -o "encrypt.format=luks,encrypt.key-secret=sec0,encrypt.iter-time=10,cluster_size=$csize" $size + _run_test + _cleanup_test_img +done + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/284.out b/tests/qemu-iotests/284.out new file mode 100644 index 0000000000..48216f5742 --- /dev/null +++ b/tests/qemu-iotests/284.out @@ -0,0 +1,62 @@ +QA output created by 284 + +testing LUKS qcow2 encryption + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 encrypt.format=luks encrypt.key-secret=sec0 encrypt.iter-time=10 + +== cluster size 512 +== checking image refcounts == +No errors were found on the image. + +== writing some data == +wrote 1/1 bytes at offset 0 +1 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +== rechecking image refcounts == +No errors were found on the image. + +== writing some more data == +wrote 1/1 bytes at offset 512 +1 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +== rechecking image refcounts == +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 encrypt.format=luks encrypt.key-secret=sec0 encrypt.iter-time=10 + +== cluster size 2048 +== checking image refcounts == +No errors were found on the image. + +== writing some data == +wrote 1/1 bytes at offset 0 +1 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +== rechecking image refcounts == +No errors were found on the image. + +== writing some more data == +wrote 1/1 bytes at offset 2048 +1 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +== rechecking image refcounts == +No errors were found on the image. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 encrypt.format=luks encrypt.key-secret=sec0 encrypt.iter-time=10 + +== cluster size 32768 +== checking image refcounts == +No errors were found on the image. + +== writing some data == +wrote 1/1 bytes at offset 0 +1 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +== rechecking image refcounts == +No errors were found on the image. + +== writing some more data == +wrote 1/1 bytes at offset 32768 +1 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +== rechecking image refcounts == +No errors were found on the image. +*** done diff --git a/tests/qemu-iotests/286 b/tests/qemu-iotests/286 new file mode 100755 index 0000000000..f14445ba4a --- /dev/null +++ b/tests/qemu-iotests/286 @@ -0,0 +1,76 @@ +#!/usr/bin/env bash +# +# Test qemu-img snapshot -l +# +# Copyright (C) 2019 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +seq=$(basename "$0") +echo "QA output created by $seq" + +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter +. ./common.qemu + +_supported_fmt qcow2 +_supported_proto file +# Internal snapshots are (currently) impossible with refcount_bits=1, +# and generally impossible with external data files +_unsupported_imgopts 'refcount_bits=1[^0-9]' data_file + +_make_test_img 64M + +# Should be so long as to take up the whole field width +sn_name=abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz + +# More memory will give us a larger VM state, i.e. one above 1 MB. +# This way, we get a number with a decimal point. +qemu_comm_method=monitor _launch_qemu -m 512 "$TEST_IMG" + +_send_qemu_cmd $QEMU_HANDLE "savevm $sn_name" '(qemu)' +_send_qemu_cmd $QEMU_HANDLE 'quit' '(qemu)' +wait=yes _cleanup_qemu + +# Check that all fields are separated by spaces. +# We first collapse all space sequences into one space each; +# then we turn every space-separated field into a '.'; +# and finally, we name the '.'s so the output is not just a confusing +# sequence of dots. + +echo 'Output structure:' +$QEMU_IMG snapshot -l "$TEST_IMG" | tail -n 1 | tr -s ' ' \ + | sed -e 's/\S\+/./g' \ + | sed -e 's/\./(snapshot ID)/' \ + -e 's/\./(snapshot name)/' \ + -e 's/\./(VM state size value)/' \ + -e 's/\./(VM state size unit)/' \ + -e 's/\./(snapshot date)/' \ + -e 's/\./(snapshot time)/' \ + -e 's/\./(VM clock)/' + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/286.out b/tests/qemu-iotests/286.out new file mode 100644 index 0000000000..39ff07e12c --- /dev/null +++ b/tests/qemu-iotests/286.out @@ -0,0 +1,8 @@ +QA output created by 286 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) savevm abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz +(qemu) quit +Output structure: +(snapshot ID) (snapshot name) (VM state size value) (VM state size unit) (snapshot date) (snapshot time) (VM clock) +*** done diff --git a/tests/qemu-iotests/288 b/tests/qemu-iotests/288 new file mode 100755 index 0000000000..6c62065aef --- /dev/null +++ b/tests/qemu-iotests/288 @@ -0,0 +1,93 @@ +#!/usr/bin/env bash +# +# qemu-img measure tests for LUKS images +# +# Copyright (C) 2020 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# creator +owner=stefanha@redhat.com + +seq=`basename $0` +echo "QA output created by $seq" + +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img + rm -f "$TEST_IMG.converted" +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter +. ./common.pattern + +_supported_fmt luks +_supported_proto file +_supported_os Linux + +SECRET=secret,id=sec0,data=passphrase + +echo "== measure 1G image file ==" +echo + +$QEMU_IMG measure --object "$SECRET" \ + -O "$IMGFMT" \ + -o key-secret=sec0,iter-time=10 \ + --size 1G + +echo +echo "== create 1G image file (size should be no greater than measured) ==" +echo + +_make_test_img 1G +stat -c "image file size in bytes: %s" "$TEST_IMG_FILE" + +echo +echo "== modified 1G image file (size should be no greater than measured) ==" +echo + +$QEMU_IO --object "$SECRET" --image-opts "$TEST_IMG" -c "write -P 0x51 0x10000 0x400" | _filter_qemu_io | _filter_testdir +stat -c "image file size in bytes: %s" "$TEST_IMG_FILE" + +echo +echo "== measure preallocation=falloc 1G image file ==" +echo + +$QEMU_IMG measure --object "$SECRET" \ + -O "$IMGFMT" \ + -o key-secret=sec0,iter-time=10,preallocation=falloc \ + --size 1G + +echo +echo "== measure with input image file ==" +echo + +IMGFMT=raw IMGKEYSECRET= IMGOPTS= _make_test_img 1G | _filter_imgfmt +QEMU_IO_OPTIONS= IMGOPTSSYNTAX= $QEMU_IO -f raw -c "write -P 0x51 0x10000 0x400" "$TEST_IMG_FILE" | _filter_qemu_io | _filter_testdir +$QEMU_IMG measure --object "$SECRET" \ + -O "$IMGFMT" \ + -o key-secret=sec0,iter-time=10 \ + -f raw \ + "$TEST_IMG_FILE" + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/288.out b/tests/qemu-iotests/288.out new file mode 100644 index 0000000000..4bc593dc48 --- /dev/null +++ b/tests/qemu-iotests/288.out @@ -0,0 +1,30 @@ +QA output created by 288 +== measure 1G image file == + +required size: 1075810304 +fully allocated size: 1075810304 + +== create 1G image file (size should be no greater than measured) == + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +image file size in bytes: 1075810304 + +== modified 1G image file (size should be no greater than measured) == + +wrote 1024/1024 bytes at offset 65536 +1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +image file size in bytes: 1075810304 + +== measure preallocation=falloc 1G image file == + +required size: 1075810304 +fully allocated size: 1075810304 + +== measure with input image file == + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +wrote 1024/1024 bytes at offset 65536 +1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +required size: 1075810304 +fully allocated size: 1075810304 +*** done diff --git a/tests/qemu-iotests/289 b/tests/qemu-iotests/289 new file mode 100755 index 0000000000..1c11d4030e --- /dev/null +++ b/tests/qemu-iotests/289 @@ -0,0 +1,89 @@ +#!/usr/bin/env bash +# +# qcow2 v3-exclusive error path testing +# (026 tests paths common to v2 and v3) +# +# Copyright (C) 2020 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +seq=$(basename $0) +echo "QA output created by $seq" + +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img + rm "$TEST_DIR/blkdebug.conf" + rm -f "$TEST_IMG.data_file" +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter +. ./common.pattern + +_supported_fmt qcow2 +_supported_proto file +# This is a v3-exclusive test; +# As for data_file, error paths often very much depend on whether +# there is an external data file or not; so we create one exactly when +# we want to test it +_unsupported_imgopts 'compat=0.10' data_file + +echo +echo === Avoid freeing external data clusters on failure === +echo + +cat > "$TEST_DIR/blkdebug.conf" < +# +# 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 . +# + +# creator +owner=berto@igalia.com + +seq=`basename $0` +echo "QA output created by $seq" + +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter + +_supported_fmt qcow2 +_supported_proto file +_supported_os Linux +_unsupported_imgopts 'compat=0.10' refcount_bits data_file + +echo +echo "### Test 'qemu-io -c discard' on a QCOW2 image without a backing file" +echo +for qcow2_compat in 0.10 1.1; do + echo "# Create an image with compat=$qcow2_compat without a backing file" + _make_test_img -o "compat=$qcow2_compat" 128k + + echo "# Fill all clusters with data and then discard them" + $QEMU_IO -c 'write -P 0x01 0 128k' "$TEST_IMG" | _filter_qemu_io + $QEMU_IO -c 'discard 0 128k' "$TEST_IMG" | _filter_qemu_io + + echo "# Read the data from the discarded clusters" + $QEMU_IO -c 'read -P 0x00 0 128k' "$TEST_IMG" | _filter_qemu_io + + echo "# Output of qemu-img map" + $QEMU_IMG map "$TEST_IMG" | _filter_testdir +done + +echo +echo "### Test 'qemu-io -c discard' on a QCOW2 image with a backing file" +echo + +echo "# Create a backing image and fill it with data" +BACKING_IMG="$TEST_IMG.base" +TEST_IMG="$BACKING_IMG" _make_test_img 128k +$QEMU_IO -c 'write -P 0xff 0 128k' "$BACKING_IMG" | _filter_qemu_io + +for qcow2_compat in 0.10 1.1; do + echo "# Create an image with compat=$qcow2_compat and a backing file" + _make_test_img -o "compat=$qcow2_compat" -b "$BACKING_IMG" + + echo "# Fill all clusters with data and then discard them" + $QEMU_IO -c 'write -P 0x01 0 128k' "$TEST_IMG" | _filter_qemu_io + $QEMU_IO -c 'discard 0 128k' "$TEST_IMG" | _filter_qemu_io + + echo "# Read the data from the discarded clusters" + if [ "$qcow2_compat" = "1.1" ]; then + # In qcow2 v3 clusters are zeroed (with QCOW_OFLAG_ZERO) + $QEMU_IO -c 'read -P 0x00 0 128k' "$TEST_IMG" | _filter_qemu_io + else + # In qcow2 v2 if there's a backing image we cannot zero the clusters + # without exposing the backing file data so discard does nothing + $QEMU_IO -c 'read -P 0x01 0 128k' "$TEST_IMG" | _filter_qemu_io + fi + + echo "# Output of qemu-img map" + $QEMU_IMG map "$TEST_IMG" | _filter_testdir +done + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/290.out b/tests/qemu-iotests/290.out new file mode 100644 index 0000000000..d2259c823b --- /dev/null +++ b/tests/qemu-iotests/290.out @@ -0,0 +1,61 @@ +QA output created by 290 + +### Test 'qemu-io -c discard' on a QCOW2 image without a backing file + +# Create an image with compat=0.10 without a backing file +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=131072 +# Fill all clusters with data and then discard them +wrote 131072/131072 bytes at offset 0 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +discard 131072/131072 bytes at offset 0 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +# Read the data from the discarded clusters +read 131072/131072 bytes at offset 0 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +# Output of qemu-img map +Offset Length Mapped to File +# Create an image with compat=1.1 without a backing file +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=131072 +# Fill all clusters with data and then discard them +wrote 131072/131072 bytes at offset 0 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +discard 131072/131072 bytes at offset 0 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +# Read the data from the discarded clusters +read 131072/131072 bytes at offset 0 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +# Output of qemu-img map +Offset Length Mapped to File + +### Test 'qemu-io -c discard' on a QCOW2 image with a backing file + +# Create a backing image and fill it with data +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=131072 +wrote 131072/131072 bytes at offset 0 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +# Create an image with compat=0.10 and a backing file +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=131072 backing_file=TEST_DIR/t.IMGFMT.base +# Fill all clusters with data and then discard them +wrote 131072/131072 bytes at offset 0 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +discard 131072/131072 bytes at offset 0 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +# Read the data from the discarded clusters +read 131072/131072 bytes at offset 0 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +# Output of qemu-img map +Offset Length Mapped to File +0 0x20000 0x50000 TEST_DIR/t.qcow2 +# Create an image with compat=1.1 and a backing file +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=131072 backing_file=TEST_DIR/t.IMGFMT.base +# Fill all clusters with data and then discard them +wrote 131072/131072 bytes at offset 0 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +discard 131072/131072 bytes at offset 0 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +# Read the data from the discarded clusters +read 131072/131072 bytes at offset 0 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +# Output of qemu-img map +Offset Length Mapped to File +*** done diff --git a/tests/qemu-iotests/check b/tests/qemu-iotests/check index 90970b0549..f7a2d3d6c3 100755 --- a/tests/qemu-iotests/check +++ b/tests/qemu-iotests/check @@ -137,6 +137,7 @@ sortme=false expunge=true have_test_arg=false cachemode=false +aiomode=false tmp="${TEST_DIR}"/$$ rm -f $tmp.list $tmp.tmp $tmp.sed @@ -146,6 +147,7 @@ export IMGFMT_GENERIC=true export IMGPROTO=file export IMGOPTS="" export CACHEMODE="writeback" +export AIOMODE="threads" export QEMU_IO_OPTIONS="" export QEMU_IO_OPTIONS_NO_FMT="" export CACHEMODE_IS_DEFAULT=true @@ -230,6 +232,11 @@ s/ .*//p CACHEMODE_IS_DEFAULT=false cachemode=false continue + elif $aiomode + then + AIOMODE="$r" + aiomode=false + continue fi xpand=true @@ -274,6 +281,7 @@ other options -n show me, do not run tests -o options -o options to pass to qemu-img create/convert -c mode cache mode + -i mode AIO mode -makecheck pretty print output for make check testlist options @@ -438,10 +446,13 @@ testlist options cachemode=true xpand=false ;; + -i) + aiomode=true + xpand=false + ;; -T) # deprecated timestamp option xpand=false ;; - -v) verbose=true xpand=false @@ -520,6 +531,8 @@ done # Set qemu-io cache mode with $CACHEMODE we have QEMU_IO_OPTIONS="$QEMU_IO_OPTIONS --cache $CACHEMODE" +# Set qemu-io aio mode with $AIOMODE we have +QEMU_IO_OPTIONS="$QEMU_IO_OPTIONS --aio $AIOMODE" QEMU_IO_OPTIONS_NO_FMT="$QEMU_IO_OPTIONS" if [ "$IMGOPTSSYNTAX" != "true" ]; then @@ -587,13 +600,13 @@ export QEMU_PROG="$(type -p "$QEMU_PROG")" case "$QEMU_PROG" in *qemu-system-arm|*qemu-system-aarch64) - export QEMU_OPTIONS="-nodefaults -display none -machine virt,accel=qtest" + export QEMU_OPTIONS="-nodefaults -display none -machine virt -accel qtest" ;; *qemu-system-tricore) - export QEMU_OPTIONS="-nodefaults -display none -machine tricore_testboard,accel=qtest" + export QEMU_OPTIONS="-nodefaults -display none -machine tricore_testboard -accel qtest" ;; *) - export QEMU_OPTIONS="-nodefaults -display none -machine accel=qtest" + export QEMU_OPTIONS="-nodefaults -display none -accel qtest" ;; esac @@ -642,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') @@ -825,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.filter b/tests/qemu-iotests/common.filter index 5367deea39..3f8ee3e5f7 100644 --- a/tests/qemu-iotests/common.filter +++ b/tests/qemu-iotests/common.filter @@ -122,7 +122,13 @@ _filter_actual_image_size() # replace driver-specific options in the "Formatting..." line _filter_img_create() { - $SED -e "s#$REMOTE_TEST_DIR#TEST_DIR#g" \ + data_file_filter=() + if data_file=$(_get_data_file "$TEST_IMG"); then + data_file_filter=(-e "s# data_file=$data_file##") + fi + + $SED "${data_file_filter[@]}" \ + -e "s#$REMOTE_TEST_DIR#TEST_DIR#g" \ -e "s#$IMGPROTO:$TEST_DIR#TEST_DIR#g" \ -e "s#$TEST_DIR#TEST_DIR#g" \ -e "s#$SOCK_DIR#SOCK_DIR#g" \ @@ -209,9 +215,22 @@ _filter_img_info() # human and json output _filter_qemu_img_map() { + # Assuming the data_file value in $IMGOPTS contains a '$TEST_IMG', + # create a filter that replaces the data file name by $TEST_IMG. + # Example: + # In $IMGOPTS: 'data_file=$TEST_IMG.data_file' + # Then data_file_pattern == '\(.*\).data_file' + # And data_file_filter == -e 's#\(.*\).data_file#\1# + data_file_filter=() + if data_file_pattern=$(_get_data_file '\\(.*\\)'); then + data_file_filter=(-e "s#$data_file_pattern#\\1#") + fi + $SED -e 's/\([0-9a-fx]* *[0-9a-fx]* *\)[0-9a-fx]* */\1/g' \ -e 's/"offset": [0-9]\+/"offset": OFFSET/g' \ - -e 's/Mapped to *//' | _filter_testdir | _filter_imgfmt + -e 's/Mapped to *//' \ + "${data_file_filter[@]}" \ + | _filter_testdir | _filter_imgfmt } _filter_nbd() @@ -232,5 +251,29 @@ _filter_qmp_empty_return() grep -v '{"return": {}}' } +_filter_json_filename() +{ + $PYTHON -c 'import sys +result, *fnames = sys.stdin.read().split("json:{") +depth = 0 +for fname in fnames: + depth += 1 # For the opening brace in the split separator + for chr_i, chr in enumerate(fname): + if chr == "{": + depth += 1 + elif chr == "}": + depth -= 1 + if depth == 0: + break + + # json:{} filenames may be nested; filter out everything from + # inside the outermost one + if depth == 0: + chr_i += 1 # First character past the filename + result += "json:{ /* filtered */ }" + fname[chr_i:] + +sys.stdout.write(result)' +} + # make sure this script returns success true diff --git a/tests/qemu-iotests/common.pattern b/tests/qemu-iotests/common.pattern index 4f5e5bcea0..4caa5de187 100644 --- a/tests/qemu-iotests/common.pattern +++ b/tests/qemu-iotests/common.pattern @@ -23,7 +23,7 @@ do_is_allocated() { local count=$4 for ((i=1;i<=$count;i++)); do - echo alloc $(( start + (i - 1) * step )) $size + echo "alloc $(( start + (i - 1) * step )) $size" done } @@ -39,9 +39,9 @@ do_io() { local count=$5 local pattern=$6 - echo === IO: pattern $pattern >&2 + echo "=== IO: pattern $pattern" >&2 for ((i=1;i<=$count;i++)); do - echo $op -P $pattern $(( start + (i - 1) * step )) $size + echo "$op -P $pattern $(( start + (i - 1) * step )) $size" done } @@ -110,31 +110,31 @@ io_test2() { # free - free - compressed # Write the clusters to be compressed - echo === Clusters to be compressed [1] + echo '=== Clusters to be compressed [1]' io_pattern writev $((offset + 4 * $cluster_size)) $cluster_size $((9 * $cluster_size)) $num 165 - echo === Clusters to be compressed [2] + echo '=== Clusters to be compressed [2]' io_pattern writev $((offset + 5 * $cluster_size)) $cluster_size $((9 * $cluster_size)) $num 165 - echo === Clusters to be compressed [3] + echo '=== Clusters to be compressed [3]' io_pattern writev $((offset + 8 * $cluster_size)) $cluster_size $((9 * $cluster_size)) $num 165 mv "$TEST_IMG" "$TEST_IMG.orig" $QEMU_IMG convert -f $IMGFMT -O $IMGFMT -c "$TEST_IMG.orig" "$TEST_IMG" # Write the used clusters - echo === Used clusters [1] + echo '=== Used clusters [1]' io_pattern writev $((offset + 0 * $cluster_size)) $cluster_size $((9 * $cluster_size)) $num 165 - echo === Used clusters [2] + echo '=== Used clusters [2]' io_pattern writev $((offset + 1 * $cluster_size)) $cluster_size $((9 * $cluster_size)) $num 165 - echo === Used clusters [3] + echo '=== Used clusters [3]' io_pattern writev $((offset + 3 * $cluster_size)) $cluster_size $((9 * $cluster_size)) $num 165 # Read them - echo === Read used/compressed clusters + echo '=== Read used/compressed clusters' io_pattern readv $((offset + 0 * $cluster_size)) $((2 * $cluster_size)) $((9 * $cluster_size)) $num 165 io_pattern readv $((offset + 3 * $cluster_size)) $((3 * $cluster_size)) $((9 * $cluster_size)) $num 165 io_pattern readv $((offset + 8 * $cluster_size)) $((1 * $cluster_size)) $((9 * $cluster_size)) $num 165 - echo === Read zeros + echo '=== Read zeros' io_zero readv $((offset + 2 * $cluster_size)) $((1 * $cluster_size)) $((9 * $cluster_size)) $num io_zero readv $((offset + 6 * $cluster_size)) $((2 * $cluster_size)) $((9 * $cluster_size)) $num } diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc index 0cc8acc9ed..bf3b9fdea0 100644 --- a/tests/qemu-iotests/common.rc +++ b/tests/qemu-iotests/common.rc @@ -53,21 +53,57 @@ poke_file() printf "$3" | dd "of=$1" bs=1 "seek=$2" conv=notrunc &>/dev/null } +# poke_file_le $img_filename $offset $byte_width $value +# Example: poke_file_le "$TEST_IMG" 512 2 65534 +poke_file_le() +{ + local img=$1 ofs=$2 len=$3 val=$4 str='' + + while ((len--)); do + str+=$(printf '\\x%02x' $((val & 0xff))) + val=$((val >> 8)) + done + + poke_file "$img" "$ofs" "$str" +} + +# poke_file_be $img_filename $offset $byte_width $value +# Example: poke_file_be "$TEST_IMG" 512 2 65279 +poke_file_be() +{ + local img=$1 ofs=$2 len=$3 val=$4 + local str=$(printf "%0$((len * 2))x\n" $val | sed 's/\(..\)/\\x\1/g') + + poke_file "$img" "$ofs" "$str" +} + # peek_file_le 'test.img' 512 2 => 65534 peek_file_le() { - # Wrap in echo $() to strip spaces - echo $(od -j"$2" -N"$3" --endian=little -An -vtu"$3" "$1") + local val=0 shift=0 byte + + # coreutils' od --endian is not portable, so manually assemble bytes. + for byte in $(od -j"$2" -N"$3" -An -v -tu1 "$1"); do + val=$(( val | (byte << shift) )) + shift=$((shift + 8)) + done + printf %llu $val } # peek_file_be 'test.img' 512 2 => 65279 peek_file_be() { - # Wrap in echo $() to strip spaces - echo $(od -j"$2" -N"$3" --endian=big -An -vtu"$3" "$1") + local val=0 byte + + # coreutils' od --endian is not portable, so manually assemble bytes. + for byte in $(od -j"$2" -N"$3" -An -v -tu1 "$1"); do + val=$(( (val << 8) | byte )) + done + printf %llu $val } -# peek_file_raw 'test.img' 512 2 => '\xff\xfe' +# peek_file_raw 'test.img' 512 2 => '\xff\xfe'. Do not use if the raw data +# is likely to contain \0 or trailing \n. peek_file_raw() { dd if="$1" bs=1 skip="$2" count="$3" status=none @@ -217,7 +253,8 @@ if [ "$IMGOPTSSYNTAX" = "true" ]; then TEST_IMG="$DRIVER,file.filename=$TEST_DIR/t.$IMGFMT" elif [ "$IMGPROTO" = "nbd" ]; then TEST_IMG_FILE=$TEST_DIR/t.$IMGFMT - TEST_IMG="$DRIVER,file.driver=nbd,file.type=unix,file.path=$SOCKDIR/nbd" + TEST_IMG="$DRIVER,file.driver=nbd,file.type=unix" + TEST_IMG="$TEST_IMG,file.path=$SOCK_DIR/nbd" elif [ "$IMGPROTO" = "ssh" ]; then TEST_IMG_FILE=$TEST_DIR/t.$IMGFMT TEST_IMG="$DRIVER,file.driver=ssh,file.host=127.0.0.1,file.path=$TEST_IMG_FILE" @@ -297,17 +334,32 @@ _stop_nbd_server() fi } +# Gets the data_file value from IMGOPTS and replaces the '$TEST_IMG' +# pattern by '$1' +# Caution: The replacement is done with sed, so $1 must be escaped +# properly. (The delimiter is '#'.) +_get_data_file() +{ + if ! echo "$IMGOPTS" | grep -q 'data_file='; then + return 1 + fi + + echo "$IMGOPTS" | sed -e 's/.*data_file=\([^,]*\).*/\1/' \ + | sed -e "s#\\\$TEST_IMG#$1#" +} + _make_test_img() { # extra qemu-img options can be added by tests # at least one argument (the image size) needs to be added local extra_img_options="" - local image_size=$* local optstr="" local img_name="" local use_backing=0 local backing_file="" local object_options="" + local opts_param=false + local misc_params=() if [ -n "$TEST_IMG_FILE" ]; then img_name=$TEST_IMG_FILE @@ -316,18 +368,43 @@ _make_test_img() fi if [ -n "$IMGOPTS" ]; then - optstr=$(_optstr_add "$optstr" "$IMGOPTS") + imgopts_expanded=$(echo "$IMGOPTS" | sed -e "s#\\\$TEST_IMG#$img_name#") + optstr=$(_optstr_add "$optstr" "$imgopts_expanded") fi if [ -n "$IMGKEYSECRET" ]; then object_options="--object secret,id=keysec0,data=$IMGKEYSECRET" optstr=$(_optstr_add "$optstr" "key-secret=keysec0") fi - if [ "$1" = "-b" ]; then - use_backing=1 - backing_file=$2 - image_size=$3 - fi + for param; do + if [ "$use_backing" = "1" -a -z "$backing_file" ]; then + backing_file=$param + continue + elif $opts_param; then + optstr=$(_optstr_add "$optstr" "$param") + opts_param=false + continue + fi + + case "$param" in + -b) + use_backing=1 + ;; + + -o) + opts_param=true + ;; + + --no-opts) + optstr="" + ;; + + *) + misc_params=("${misc_params[@]}" "$param") + ;; + esac + done + if [ \( "$IMGFMT" = "qcow2" -o "$IMGFMT" = "qed" \) -a -n "$CLUSTER_SIZE" ]; then optstr=$(_optstr_add "$optstr" "cluster_size=$CLUSTER_SIZE") fi @@ -343,9 +420,9 @@ _make_test_img() # XXX(hch): have global image options? ( if [ $use_backing = 1 ]; then - $QEMU_IMG create $object_options -f $IMGFMT $extra_img_options -b "$backing_file" "$img_name" $image_size 2>&1 + $QEMU_IMG create $object_options -f $IMGFMT $extra_img_options -b "$backing_file" "$img_name" "${misc_params[@]}" 2>&1 else - $QEMU_IMG create $object_options -f $IMGFMT $extra_img_options "$img_name" $image_size 2>&1 + $QEMU_IMG create $object_options -f $IMGFMT $extra_img_options "$img_name" "${misc_params[@]}" 2>&1 fi ) | _filter_img_create @@ -374,6 +451,11 @@ _rm_test_img() # Remove all the extents for vmdk "$QEMU_IMG" info "$img" 2>/dev/null | grep 'filename:' | cut -f 2 -d: \ | xargs -I {} rm -f "{}" + elif [ "$IMGFMT" = "qcow2" ]; then + # Remove external data file + if data_file=$(_get_data_file "$img"); then + rm -f "$data_file" + fi fi rm -f "$img" } @@ -599,6 +681,20 @@ _default_cache_mode() return fi } +_supported_aio_modes() +{ + for mode; do + if [ "$mode" = "$AIOMODE" ]; then + return + fi + done + _notrun "not suitable for aio mode: $AIOMODE" +} +_default_aio_mode() +{ + AIOMODE="$1" + QEMU_IO="$QEMU_IO --aio $1" +} _unsupported_imgopts() { @@ -643,5 +739,29 @@ _require_drivers() done } +# Check that we have a file system that allows huge (but very sparse) files +# +_require_large_file() +{ + if ! truncate --size="$1" "$TEST_IMG"; then + _notrun "file system on $TEST_DIR does not support large enough files" + fi + 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 6b10a6a762..435dccd5af 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,9 +270,10 @@ 253 rw quick 254 rw backing quick 255 rw quick -256 rw quick +256 rw auto quick 257 rw 258 rw quick +259 rw auto quick 260 rw quick 261 rw 262 rw quick migration @@ -286,3 +287,13 @@ 272 rw 273 backing quick 277 rw quick +279 rw backing quick +280 rw migration quick +281 rw quick +282 rw img quick +283 auto quick +284 rw +286 rw quick +288 quick +289 rw quick +290 rw auto quick diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py index df0708923d..7bc4934cd2 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. @@ -31,12 +30,15 @@ import logging import atexit import io from collections import OrderedDict +import faulthandler sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python')) from qemu import qtest assert sys.version_info >= (3,6) +faulthandler.enable() + # This will not work if arguments contain spaces but is necessary if we # want to support the override options that ./check supports. qemu_img_args = [os.environ.get('QEMU_IMG_PROG', 'qemu-img')] @@ -65,6 +67,7 @@ test_dir = os.environ.get('TEST_DIR') sock_dir = os.environ.get('SOCK_DIR') output_dir = os.environ.get('OUTPUT_DIR', '.') cachemode = os.environ.get('CACHEMODE') +aiomode = os.environ.get('AIOMODE') qemu_default_machine = os.environ.get('QEMU_DEFAULT_MACHINE') socket_scm_helper = os.environ.get('SOCKET_SCM_HELPER', 'socket_scm_helper') @@ -162,6 +165,11 @@ def qemu_io(*args): sys.stderr.write('qemu-io received signal %i: %s\n' % (-exitcode, ' '.join(args))) return subp.communicate()[0] +def qemu_io_log(*args): + result = qemu_io(*args) + log(result, filters=[filter_testfiles, filter_qemu_io]) + return result + def qemu_io_silent(*args): '''Run qemu-io and return the exit code, suppressing stdout''' args = qemu_io_args + list(args) @@ -490,6 +498,7 @@ class VM(qtest.QEMUQtestMachine): options.append('file=%s' % path) options.append('format=%s' % format) options.append('cache=%s' % cachemode) + options.append('aio=%s' % aiomode) if opts: options.append(opts) @@ -604,7 +613,7 @@ class VM(qtest.QEMUQtestMachine): ] error = None while True: - ev = filter_qmp_event(self.events_wait(events)) + ev = filter_qmp_event(self.events_wait(events, timeout=wait)) if ev['event'] != 'JOB_STATUS_CHANGE': if use_log: log(ev) @@ -617,6 +626,11 @@ class VM(qtest.QEMUQtestMachine): error = j['error'] if use_log: log('Job failed: %s' % (j['error'])) + elif status == 'ready': + if use_log: + self.qmp_log('job-complete', id=job) + else: + self.qmp('job-complete', id=job) elif status == 'pending' and not auto_finalize: if pre_finalize: pre_finalize() @@ -636,6 +650,22 @@ class VM(qtest.QEMUQtestMachine): elif status == 'null': return error + # Returns None on success, and an error string on failure + def blockdev_create(self, options, job_id='job0', filters=None): + if filters is None: + filters = [filter_qmp_testfiles] + result = self.qmp_log('blockdev-create', filters=filters, + job_id=job_id, options=options) + + if 'return' in result: + assert result['return'] == {} + job_result = self.run_job(job_id) + else: + job_result = result['error'] + + log("") + return job_result + def enable_migration_events(self, name): log('Enabling migration QMP events on %s...' % name) log(self.qmp('migrate-set-capabilities', capabilities=[ @@ -645,12 +675,16 @@ class VM(qtest.QEMUQtestMachine): } ])) - def wait_migration(self): + def wait_migration(self, expect_runstate): while True: event = self.event_wait('MIGRATION') log(event, filters=[filter_qmp_event]) if event['data']['status'] == 'completed': break + # The event may occur in finish-migrate, so wait for the expected + # post-migration runstate + while self.qmp('query-status')['return']['status'] != expect_runstate: + pass def node_info(self, node_name): nodes = self.qmp('query-named-block-nodes') @@ -686,6 +720,65 @@ class VM(qtest.QEMUQtestMachine): return fields.items() <= ret.items() + def assert_block_path(self, root, path, expected_node, graph=None): + """ + Check whether the node under the given path in the block graph + is @expected_node. + + @root is the node name of the node where the @path is rooted. + + @path is a string that consists of child names separated by + slashes. It must begin with a slash. + + Examples for @root + @path: + - root="qcow2-node", path="/backing/file" + - root="quorum-node", path="/children.2/file" + + Hypothetically, @path could be empty, in which case it would + point to @root. However, in practice this case is not useful + and hence not allowed. + + @expected_node may be None. (All elements of the path but the + leaf must still exist.) + + @graph may be None or the result of an x-debug-query-block-graph + call that has already been performed. + """ + if graph is None: + graph = self.qmp('x-debug-query-block-graph')['return'] + + iter_path = iter(path.split('/')) + + # Must start with a / + assert next(iter_path) == '' + + node = next((node for node in graph['nodes'] if node['name'] == root), + None) + + # An empty @path is not allowed, so the root node must be present + assert node is not None, 'Root node %s not found' % root + + for child_name in iter_path: + assert node is not None, 'Cannot follow path %s%s' % (root, path) + + try: + node_id = next(edge['child'] for edge in graph['edges'] \ + if edge['parent'] == node['id'] and + edge['name'] == child_name) + + node = next(node for node in graph['nodes'] \ + if node['id'] == node_id) + except StopIteration: + node = None + + if node is None: + assert expected_node is None, \ + 'No node found under %s (but expected %s)' % \ + (path, expected_node) + else: + assert node['name'] == expected_node, \ + 'Found node %s under %s (but expected %s)' % \ + (node['name'], path, expected_node) index_re = re.compile(r'([^\[]+)\[([^\]]+)\]') @@ -788,15 +881,20 @@ class QMPTestCase(unittest.TestCase): self.assert_no_active_block_jobs() return result - def wait_until_completed(self, drive='drive0', check_offset=True, wait=60.0): + def wait_until_completed(self, drive='drive0', check_offset=True, wait=60.0, + error=None): '''Wait for a block job to finish, returning the event''' while True: for event in self.vm.get_qmp_events(wait=wait): if event['event'] == 'BLOCK_JOB_COMPLETED': self.assert_qmp(event, 'data/device', drive) - self.assert_qmp_absent(event, 'data/error') - if check_offset: - self.assert_qmp(event, 'data/offset', event['data']['len']) + if error is None: + self.assert_qmp_absent(event, 'data/error') + if check_offset: + self.assert_qmp(event, 'data/offset', + event['data']['len']) + else: + self.assert_qmp(event, 'data/error', error) self.assert_no_active_block_jobs() return event elif event['event'] == 'JOB_STATUS_CHANGE': @@ -814,7 +912,8 @@ class QMPTestCase(unittest.TestCase): self.assert_qmp(event, 'data/type', 'mirror') self.assert_qmp(event, 'data/offset', event['data']['len']) - def complete_and_wait(self, drive='drive0', wait_ready=True): + def complete_and_wait(self, drive='drive0', wait_ready=True, + completion_error=None): '''Complete a block job and wait for it to finish''' if wait_ready: self.wait_ready(drive=drive) @@ -822,11 +921,11 @@ class QMPTestCase(unittest.TestCase): result = self.vm.qmp('block-job-complete', device=drive) self.assert_qmp(result, 'return', {}) - event = self.wait_until_completed(drive=drive) + event = self.wait_until_completed(drive=drive, error=completion_error) self.assert_qmp(event, 'data/type', 'mirror') def pause_wait(self, job_id='job0'): - with Timeout(1, "Timeout waiting for job to pause"): + with Timeout(3, "Timeout waiting for job to pause"): while True: result = self.vm.qmp('query-block-jobs') found = False @@ -896,14 +995,23 @@ 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): notrun('not suitable for this cache mode: %s' % cachemode) +def verify_aio_mode(supported_aio_modes=[]): + if supported_aio_modes and (aiomode not in supported_aio_modes): + notrun('not suitable for this aio mode: %s' % aiomode) + def supports_quorum(): return 'quorum' in qemu_img_pipe('--help') @@ -989,9 +1097,11 @@ def execute_unittest(output, verbosity, debug): sys.stderr.write(out) def execute_test(test_function=None, - supported_fmts=[], supported_oses=['linux'], - supported_cache_modes=[], unsupported_fmts=[], - supported_protocols=[], unsupported_protocols=[]): + supported_fmts=[], + supported_platforms=None, + supported_cache_modes=[], supported_aio_modes={}, + unsupported_fmts=[], supported_protocols=[], + unsupported_protocols=[]): """Run either unittest or script-style tests.""" # We are using TEST_DIR and QEMU_DEFAULT_MACHINE as proxies to @@ -1006,8 +1116,9 @@ 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) if debug: output = sys.stdout 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 b392972d1b..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 @@ -42,9 +41,9 @@ class QcowHeader: [ uint64_t, '%#x', 'snapshot_offset' ], # Version 3 header fields - [ uint64_t, '%#x', 'incompatible_features' ], - [ uint64_t, '%#x', 'compatible_features' ], - [ uint64_t, '%#x', 'autoclear_features' ], + [ uint64_t, 'mask', 'incompatible_features' ], + [ uint64_t, 'mask', 'compatible_features' ], + [ uint64_t, 'mask', 'autoclear_features' ], [ uint32_t, '%d', 'refcount_order' ], [ uint32_t, '%d', 'header_length' ], ]; @@ -130,7 +129,17 @@ class QcowHeader: def dump(self): for f in QcowHeader.fields: - print("%-25s" % f[2], f[1] % self.__dict__[f[2]]) + value = self.__dict__[f[2]] + if f[1] == 'mask': + bits = [] + for bit in range(64): + if value & (1 << bit): + bits.append(bit) + value_str = str(bits) + else: + value_str = f[1] % value + + print("%-25s" % f[2], value_str) print("") def dump_extensions(self): @@ -154,6 +163,10 @@ def cmd_dump_header(fd): h.dump() h.dump_extensions() +def cmd_dump_header_exts(fd): + h = QcowHeader(fd) + h.dump_extensions() + def cmd_set_header(fd, name, value): try: value = int(value, 0) @@ -230,6 +243,7 @@ def cmd_set_feature_bit(fd, group, bit): cmds = [ [ 'dump-header', cmd_dump_header, 0, 'Dump image header and header extensions' ], + [ 'dump-header-exts', cmd_dump_header_exts, 0, 'Dump image header extensions' ], [ 'set-header', cmd_set_header, 2, 'Set a field in the header'], [ 'add-header-ext', cmd_add_header_ext, 2, 'Add a header extension' ], [ 'add-header-ext-stdio', cmd_add_header_ext_stdio, 1, 'Add a header extension, data from stdin' ], 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/Makefile.include b/tests/qtest/Makefile.include new file mode 100644 index 0000000000..9e5a51d033 --- /dev/null +++ b/tests/qtest/Makefile.include @@ -0,0 +1,325 @@ +# All QTests for now are POSIX-only, but the dependencies are +# really in libqtest, not in the testcases themselves. + +check-qtest-generic-y += cdrom-test +check-qtest-generic-y += device-introspect-test +check-qtest-generic-y += machine-none-test +check-qtest-generic-y += qmp-test +check-qtest-generic-y += qmp-cmd-test +check-qtest-generic-y += qom-test +check-qtest-generic-$(CONFIG_MODULES) += modules-test +check-qtest-generic-y += test-hmp + +check-qtest-pci-$(CONFIG_RTL8139_PCI) += rtl8139-test +check-qtest-pci-$(CONFIG_VGA) += display-vga-test +check-qtest-pci-$(CONFIG_HDA) += intel-hda-test +check-qtest-pci-$(CONFIG_IVSHMEM_DEVICE) += ivshmem-test + +DBUS_DAEMON := $(shell which dbus-daemon 2>/dev/null) +ifneq ($(GDBUS_CODEGEN),) +ifneq ($(DBUS_DAEMON),) +# Temporarily disabled due to Patchew failures: +#check-qtest-pci-$(CONFIG_GIO) += dbus-vmstate-test +endif +endif + +check-qtest-i386-$(CONFIG_ISA_TESTDEV) = endianness-test +check-qtest-i386-y += fdc-test +check-qtest-i386-y += ide-test +check-qtest-i386-$(CONFIG_TOOLS) += ahci-test +check-qtest-i386-y += hd-geo-test +check-qtest-i386-y += boot-order-test +check-qtest-i386-y += bios-tables-test +check-qtest-i386-$(CONFIG_SGA) += boot-serial-test +check-qtest-i386-$(CONFIG_SLIRP) += pxe-test +check-qtest-i386-y += rtc-test +check-qtest-i386-$(CONFIG_ISA_IPMI_KCS) += ipmi-kcs-test +ifdef CONFIG_LINUX +check-qtest-i386-$(CONFIG_ISA_IPMI_BT) += ipmi-bt-test +endif +check-qtest-i386-y += i440fx-test +check-qtest-i386-y += fw_cfg-test +check-qtest-i386-y += device-plug-test +check-qtest-i386-y += drive_del-test +check-qtest-i386-$(CONFIG_WDT_IB700) += wdt_ib700-test +check-qtest-i386-y += tco-test +check-qtest-i386-y += $(check-qtest-pci-y) +check-qtest-i386-$(CONFIG_PVPANIC) += pvpanic-test +check-qtest-i386-$(CONFIG_I82801B11) += i82801b11-test +check-qtest-i386-$(CONFIG_IOH3420) += ioh3420-test +check-qtest-i386-$(CONFIG_USB_UHCI) += usb-hcd-uhci-test +check-qtest-i386-$(call land,$(CONFIG_USB_EHCI),$(CONFIG_USB_UHCI)) += usb-hcd-ehci-test +check-qtest-i386-$(CONFIG_USB_XHCI_NEC) += usb-hcd-xhci-test +check-qtest-i386-y += cpu-plug-test +check-qtest-i386-y += q35-test +check-qtest-i386-y += vmgenid-test +check-qtest-i386-$(CONFIG_TPM_CRB) += tpm-crb-swtpm-test +check-qtest-i386-$(CONFIG_TPM_CRB) += tpm-crb-test +check-qtest-i386-$(CONFIG_TPM_TIS_ISA) += tpm-tis-swtpm-test +check-qtest-i386-$(CONFIG_TPM_TIS_ISA) += tpm-tis-test +check-qtest-i386-$(CONFIG_SLIRP) += test-netfilter +check-qtest-i386-$(CONFIG_POSIX) += test-filter-mirror +check-qtest-i386-$(CONFIG_RTL8139_PCI) += test-filter-redirector +check-qtest-i386-y += migration-test +check-qtest-i386-y += test-x86-cpuid-compat +check-qtest-i386-y += numa-test + +check-qtest-x86_64-y += $(check-qtest-i386-y) + +check-qtest-alpha-y += boot-serial-test +check-qtest-alpha-$(CONFIG_VGA) += display-vga-test + +check-qtest-hppa-y += boot-serial-test +check-qtest-hppa-$(CONFIG_VGA) += display-vga-test + +check-qtest-m68k-y = boot-serial-test + +check-qtest-microblaze-y += boot-serial-test + +check-qtest-mips-$(CONFIG_ISA_TESTDEV) = endianness-test +check-qtest-mips-$(CONFIG_VGA) += display-vga-test + +check-qtest-mips64-$(CONFIG_ISA_TESTDEV) = endianness-test +check-qtest-mips64-$(CONFIG_VGA) += display-vga-test + +check-qtest-mips64el-$(CONFIG_ISA_TESTDEV) = endianness-test +check-qtest-mips64el-$(CONFIG_VGA) += display-vga-test + +check-qtest-moxie-y += boot-serial-test + +check-qtest-ppc-$(CONFIG_ISA_TESTDEV) = endianness-test +check-qtest-ppc-y += boot-order-test +check-qtest-ppc-y += prom-env-test +check-qtest-ppc-y += drive_del-test +check-qtest-ppc-y += boot-serial-test +check-qtest-ppc-$(CONFIG_M48T59) += m48t59-test + +check-qtest-ppc64-y += $(check-qtest-ppc-y) +check-qtest-ppc64-$(CONFIG_PSERIES) += device-plug-test +check-qtest-ppc64-$(CONFIG_POWERNV) += pnv-xscom-test +check-qtest-ppc64-y += migration-test +check-qtest-ppc64-$(CONFIG_PSERIES) += rtas-test +check-qtest-ppc64-$(CONFIG_SLIRP) += pxe-test +check-qtest-ppc64-$(CONFIG_USB_UHCI) += usb-hcd-uhci-test +check-qtest-ppc64-$(CONFIG_USB_XHCI_NEC) += usb-hcd-xhci-test +check-qtest-ppc64-$(CONFIG_SLIRP) += test-netfilter +check-qtest-ppc64-$(CONFIG_POSIX) += test-filter-mirror +check-qtest-ppc64-$(CONFIG_RTL8139_PCI) += test-filter-redirector +check-qtest-ppc64-$(CONFIG_VGA) += display-vga-test +check-qtest-ppc64-y += numa-test +check-qtest-ppc64-$(CONFIG_IVSHMEM_DEVICE) += ivshmem-test +check-qtest-ppc64-y += cpu-plug-test + +check-qtest-sh4-$(CONFIG_ISA_TESTDEV) = endianness-test + +check-qtest-sh4eb-$(CONFIG_ISA_TESTDEV) = endianness-test + +check-qtest-sparc-y += prom-env-test +check-qtest-sparc-y += m48t59-test +check-qtest-sparc-y += boot-serial-test + +check-qtest-sparc64-$(CONFIG_ISA_TESTDEV) = endianness-test +check-qtest-sparc64-y += prom-env-test +check-qtest-sparc64-y += boot-serial-test + +check-qtest-arm-y += arm-cpu-features +check-qtest-arm-y += microbit-test +check-qtest-arm-y += m25p80-test +check-qtest-arm-y += test-arm-mptimer +check-qtest-arm-y += boot-serial-test +check-qtest-arm-y += hexloader-test +check-qtest-arm-$(CONFIG_PFLASH_CFI02) += pflash-cfi02-test + +check-qtest-aarch64-y += arm-cpu-features +check-qtest-aarch64-$(CONFIG_TPM_TIS_SYSBUS) += tpm-tis-device-test +check-qtest-aarch64-$(CONFIG_TPM_TIS_SYSBUS) += tpm-tis-device-swtpm-test +check-qtest-aarch64-y += numa-test +check-qtest-aarch64-y += boot-serial-test +check-qtest-aarch64-y += migration-test + +# TODO: once aarch64 TCG is fixed on ARM 32 bit host, make test unconditional +ifneq ($(ARCH),arm) +check-qtest-aarch64-y += bios-tables-test +endif + +check-qtest-microblazeel-y += $(check-qtest-microblaze-y) + +check-qtest-xtensaeb-y += $(check-qtest-xtensa-y) + +check-qtest-s390x-y = boot-serial-test +check-qtest-s390x-$(CONFIG_SLIRP) += pxe-test +check-qtest-s390x-$(CONFIG_SLIRP) += test-netfilter +check-qtest-s390x-$(CONFIG_POSIX) += test-filter-mirror +check-qtest-s390x-$(CONFIG_POSIX) += test-filter-redirector +check-qtest-s390x-y += drive_del-test +check-qtest-s390x-y += device-plug-test +check-qtest-s390x-y += virtio-ccw-test +check-qtest-s390x-y += cpu-plug-test +check-qtest-s390x-y += migration-test + +# libqos / qgraph : +libqgraph-obj-y = tests/qtest/libqos/qgraph.o + +libqos-core-obj-y = $(libqgraph-obj-y) tests/qtest/libqos/pci.o tests/qtest/libqos/fw_cfg.o +libqos-core-obj-y += tests/qtest/libqos/malloc.o +libqos-core-obj-y += tests/qtest/libqos/libqos.o +libqos-spapr-obj-y = $(libqos-core-obj-y) tests/qtest/libqos/malloc-spapr.o +libqos-spapr-obj-y += tests/qtest/libqos/libqos-spapr.o +libqos-spapr-obj-y += tests/qtest/libqos/rtas.o +libqos-spapr-obj-y += tests/qtest/libqos/pci-spapr.o +libqos-pc-obj-y = $(libqos-core-obj-y) tests/qtest/libqos/pci-pc.o +libqos-pc-obj-y += tests/qtest/libqos/malloc-pc.o tests/qtest/libqos/libqos-pc.o +libqos-pc-obj-y += tests/qtest/libqos/ahci.o +libqos-usb-obj-y = $(libqos-spapr-obj-y) $(libqos-pc-obj-y) tests/qtest/libqos/usb.o + +# qos devices: +libqos-obj-y = $(libqgraph-obj-y) +libqos-obj-y += $(libqos-pc-obj-y) $(libqos-spapr-obj-y) +libqos-obj-y += tests/qtest/libqos/qos_external.o +libqos-obj-y += tests/qtest/libqos/e1000e.o +libqos-obj-y += tests/qtest/libqos/i2c.o +libqos-obj-y += tests/qtest/libqos/i2c-imx.o +libqos-obj-y += tests/qtest/libqos/i2c-omap.o +libqos-obj-y += tests/qtest/libqos/sdhci.o +libqos-obj-y += tests/qtest/libqos/tpci200.o +libqos-obj-y += tests/qtest/libqos/virtio.o +libqos-obj-$(CONFIG_VIRTFS) += tests/qtest/libqos/virtio-9p.o +libqos-obj-y += tests/qtest/libqos/virtio-balloon.o +libqos-obj-y += tests/qtest/libqos/virtio-blk.o +libqos-obj-y += tests/qtest/libqos/virtio-mmio.o +libqos-obj-y += tests/qtest/libqos/virtio-net.o +libqos-obj-y += tests/qtest/libqos/virtio-pci.o +libqos-obj-y += tests/qtest/libqos/virtio-pci-modern.o +libqos-obj-y += tests/qtest/libqos/virtio-rng.o +libqos-obj-y += tests/qtest/libqos/virtio-scsi.o +libqos-obj-y += tests/qtest/libqos/virtio-serial.o + +# qos machines: +libqos-obj-y += tests/qtest/libqos/aarch64-xlnx-zcu102-machine.o +libqos-obj-y += tests/qtest/libqos/arm-imx25-pdk-machine.o +libqos-obj-y += tests/qtest/libqos/arm-n800-machine.o +libqos-obj-y += tests/qtest/libqos/arm-raspi2-machine.o +libqos-obj-y += tests/qtest/libqos/arm-sabrelite-machine.o +libqos-obj-y += tests/qtest/libqos/arm-smdkc210-machine.o +libqos-obj-y += tests/qtest/libqos/arm-virt-machine.o +libqos-obj-y += tests/qtest/libqos/arm-xilinx-zynq-a9-machine.o +libqos-obj-y += tests/qtest/libqos/ppc64_pseries-machine.o +libqos-obj-y += tests/qtest/libqos/x86_64_pc-machine.o + +# qos tests: +qos-test-obj-y += tests/qtest/qos-test.o +qos-test-obj-y += tests/qtest/ac97-test.o +qos-test-obj-y += tests/qtest/ds1338-test.o +qos-test-obj-y += tests/qtest/e1000-test.o +qos-test-obj-y += tests/qtest/e1000e-test.o +qos-test-obj-y += tests/qtest/eepro100-test.o +qos-test-obj-y += tests/qtest/es1370-test.o +qos-test-obj-y += tests/qtest/ipoctal232-test.o +qos-test-obj-y += tests/qtest/megasas-test.o +qos-test-obj-y += tests/qtest/ne2000-test.o +qos-test-obj-y += tests/qtest/tulip-test.o +qos-test-obj-y += tests/qtest/nvme-test.o +qos-test-obj-y += tests/qtest/pca9552-test.o +qos-test-obj-y += tests/qtest/pci-test.o +qos-test-obj-y += tests/qtest/pcnet-test.o +qos-test-obj-y += tests/qtest/sdhci-test.o +qos-test-obj-y += tests/qtest/spapr-phb-test.o +qos-test-obj-y += tests/qtest/tmp105-test.o +qos-test-obj-y += tests/qtest/usb-hcd-ohci-test.o $(libqos-usb-obj-y) +qos-test-obj-$(CONFIG_VHOST_NET_USER) += tests/qtest/vhost-user-test.o $(chardev-obj-y) $(test-io-obj-y) +qos-test-obj-y += tests/qtest/virtio-test.o +qos-test-obj-$(CONFIG_VIRTFS) += tests/qtest/virtio-9p-test.o +qos-test-obj-y += tests/qtest/virtio-blk-test.o +qos-test-obj-y += tests/qtest/virtio-net-test.o +qos-test-obj-y += tests/qtest/virtio-rng-test.o +qos-test-obj-y += tests/qtest/virtio-scsi-test.o +qos-test-obj-y += tests/qtest/virtio-serial-test.o +qos-test-obj-y += tests/qtest/vmxnet3-test.o + +check-unit-y += tests/test-qgraph$(EXESUF) +tests/test-qgraph$(EXESUF): tests/test-qgraph.o $(libqgraph-obj-y) + +check-qtest-generic-y += qos-test +tests/qtest/qos-test$(EXESUF): $(qos-test-obj-y) $(libqos-obj-y) + +# QTest dependencies: +tests/qtest/qmp-test$(EXESUF): tests/qtest/qmp-test.o +tests/qtest/qmp-cmd-test$(EXESUF): tests/qtest/qmp-cmd-test.o +tests/qtest/device-introspect-test$(EXESUF): tests/qtest/device-introspect-test.o +tests/qtest/rtc-test$(EXESUF): tests/qtest/rtc-test.o +tests/qtest/m48t59-test$(EXESUF): tests/qtest/m48t59-test.o +tests/qtest/hexloader-test$(EXESUF): tests/qtest/hexloader-test.o +tests/qtest/pflash-cfi02$(EXESUF): tests/qtest/pflash-cfi02-test.o +tests/qtest/endianness-test$(EXESUF): tests/qtest/endianness-test.o +tests/qtest/prom-env-test$(EXESUF): tests/qtest/prom-env-test.o $(libqos-obj-y) +tests/qtest/rtas-test$(EXESUF): tests/qtest/rtas-test.o $(libqos-spapr-obj-y) +tests/qtest/fdc-test$(EXESUF): tests/qtest/fdc-test.o +tests/qtest/ide-test$(EXESUF): tests/qtest/ide-test.o $(libqos-pc-obj-y) +tests/qtest/ahci-test$(EXESUF): tests/qtest/ahci-test.o $(libqos-pc-obj-y) qemu-img$(EXESUF) +tests/qtest/ipmi-kcs-test$(EXESUF): tests/qtest/ipmi-kcs-test.o +tests/qtest/ipmi-bt-test$(EXESUF): tests/qtest/ipmi-bt-test.o +tests/qtest/hd-geo-test$(EXESUF): tests/qtest/hd-geo-test.o $(libqos-obj-y) +tests/qtest/boot-order-test$(EXESUF): tests/qtest/boot-order-test.o $(libqos-obj-y) +tests/qtest/boot-serial-test$(EXESUF): tests/qtest/boot-serial-test.o $(libqos-obj-y) +tests/qtest/bios-tables-test$(EXESUF): tests/qtest/bios-tables-test.o \ + tests/qtest/boot-sector.o tests/qtest/acpi-utils.o $(libqos-obj-y) +tests/qtest/pxe-test$(EXESUF): tests/qtest/pxe-test.o tests/qtest/boot-sector.o $(libqos-obj-y) +tests/qtest/microbit-test$(EXESUF): tests/qtest/microbit-test.o +tests/qtest/m25p80-test$(EXESUF): tests/qtest/m25p80-test.o +tests/qtest/i440fx-test$(EXESUF): tests/qtest/i440fx-test.o $(libqos-pc-obj-y) +tests/qtest/q35-test$(EXESUF): tests/qtest/q35-test.o $(libqos-pc-obj-y) +tests/qtest/fw_cfg-test$(EXESUF): tests/qtest/fw_cfg-test.o $(libqos-pc-obj-y) +tests/qtest/rtl8139-test$(EXESUF): tests/qtest/rtl8139-test.o $(libqos-pc-obj-y) +tests/qtest/pnv-xscom-test$(EXESUF): tests/qtest/pnv-xscom-test.o +tests/qtest/wdt_ib700-test$(EXESUF): tests/qtest/wdt_ib700-test.o +tests/qtest/tco-test$(EXESUF): tests/qtest/tco-test.o $(libqos-pc-obj-y) +tests/qtest/virtio-ccw-test$(EXESUF): tests/qtest/virtio-ccw-test.o +tests/qtest/display-vga-test$(EXESUF): tests/qtest/display-vga-test.o +tests/qtest/qom-test$(EXESUF): tests/qtest/qom-test.o +tests/qtest/test-hmp$(EXESUF): tests/qtest/test-hmp.o +tests/qtest/machine-none-test$(EXESUF): tests/qtest/machine-none-test.o +tests/qtest/device-plug-test$(EXESUF): tests/qtest/device-plug-test.o +tests/qtest/drive_del-test$(EXESUF): tests/qtest/drive_del-test.o +tests/qtest/pvpanic-test$(EXESUF): tests/qtest/pvpanic-test.o +tests/qtest/i82801b11-test$(EXESUF): tests/qtest/i82801b11-test.o +tests/qtest/intel-hda-test$(EXESUF): tests/qtest/intel-hda-test.o +tests/qtest/ioh3420-test$(EXESUF): tests/qtest/ioh3420-test.o +tests/qtest/usb-hcd-uhci-test$(EXESUF): tests/qtest/usb-hcd-uhci-test.o $(libqos-usb-obj-y) +tests/qtest/usb-hcd-ehci-test$(EXESUF): tests/qtest/usb-hcd-ehci-test.o $(libqos-usb-obj-y) +tests/qtest/usb-hcd-xhci-test$(EXESUF): tests/qtest/usb-hcd-xhci-test.o $(libqos-usb-obj-y) +tests/qtest/cpu-plug-test$(EXESUF): tests/qtest/cpu-plug-test.o +tests/qtest/migration-test$(EXESUF): tests/qtest/migration-test.o tests/qtest/migration-helpers.o +tests/qtest/test-netfilter$(EXESUF): tests/qtest/test-netfilter.o $(qtest-obj-y) +tests/qtest/test-filter-mirror$(EXESUF): tests/qtest/test-filter-mirror.o $(qtest-obj-y) +tests/qtest/test-filter-redirector$(EXESUF): tests/qtest/test-filter-redirector.o $(qtest-obj-y) +tests/qtest/test-x86-cpuid-compat$(EXESUF): tests/qtest/test-x86-cpuid-compat.o $(qtest-obj-y) +tests/qtest/ivshmem-test$(EXESUF): tests/qtest/ivshmem-test.o contrib/ivshmem-server/ivshmem-server.o $(libqos-pc-obj-y) $(libqos-spapr-obj-y) +tests/qtest/dbus-vmstate-test$(EXESUF): tests/qtest/dbus-vmstate-test.o tests/qtest/migration-helpers.o tests/qtest/dbus-vmstate1.o $(libqos-pc-obj-y) $(libqos-spapr-obj-y) +tests/qtest/test-arm-mptimer$(EXESUF): tests/qtest/test-arm-mptimer.o +tests/qtest/numa-test$(EXESUF): tests/qtest/numa-test.o +tests/qtest/vmgenid-test$(EXESUF): tests/qtest/vmgenid-test.o tests/qtest/boot-sector.o tests/qtest/acpi-utils.o +tests/qtest/cdrom-test$(EXESUF): tests/qtest/cdrom-test.o tests/qtest/boot-sector.o $(libqos-obj-y) +tests/qtest/arm-cpu-features$(EXESUF): tests/qtest/arm-cpu-features.o +tests/qtest/tpm-crb-swtpm-test$(EXESUF): tests/qtest/tpm-crb-swtpm-test.o tests/qtest/tpm-emu.o \ + tests/qtest/tpm-util.o tests/qtest/tpm-tests.o $(test-io-obj-y) +tests/qtest/tpm-crb-test$(EXESUF): tests/qtest/tpm-crb-test.o tests/qtest/tpm-emu.o $(test-io-obj-y) +tests/qtest/tpm-tis-swtpm-test$(EXESUF): tests/qtest/tpm-tis-swtpm-test.o tests/qtest/tpm-emu.o \ + tests/qtest/tpm-util.o tests/qtest/tpm-tests.o $(test-io-obj-y) +tests/qtest/tpm-tis-device-swtpm-test$(EXESUF): tests/qtest/tpm-tis-device-swtpm-test.o tests/qtest/tpm-emu.o \ + tests/qtest/tpm-util.o tests/qtest/tpm-tests.o $(test-io-obj-y) +tests/qtest/tpm-tis-test$(EXESUF): tests/qtest/tpm-tis-test.o tests/qtest/tpm-tis-util.o tests/qtest/tpm-emu.o $(test-io-obj-y) +tests/qtest/tpm-tis-device-test$(EXESUF): tests/qtest/tpm-tis-device-test.o tests/qtest/tpm-tis-util.o tests/qtest/tpm-emu.o $(test-io-obj-y) + +# QTest rules + +TARGETS=$(patsubst %-softmmu,%, $(filter %-softmmu,$(TARGET_DIRS))) +ifeq ($(CONFIG_POSIX),y) +QTEST_TARGETS = $(TARGETS) +check-qtest-y=$(foreach TARGET,$(TARGETS), $(check-qtest-$(TARGET)-y:%=tests/qtest/%$(EXESUF))) +check-qtest-y += $(check-qtest-generic-y:%=tests/qtest/%$(EXESUF)) +else +QTEST_TARGETS = +endif + +qtest-obj-y = tests/qtest/libqtest.o $(test-util-obj-y) +$(check-qtest-y): $(qtest-obj-y) diff --git a/tests/ac97-test.c b/tests/qtest/ac97-test.c similarity index 100% rename from tests/ac97-test.c rename to tests/qtest/ac97-test.c diff --git a/tests/acpi-utils.c b/tests/qtest/acpi-utils.c similarity index 100% rename from tests/acpi-utils.c rename to tests/qtest/acpi-utils.c diff --git a/tests/acpi-utils.h b/tests/qtest/acpi-utils.h similarity index 100% rename from tests/acpi-utils.h rename to tests/qtest/acpi-utils.h diff --git a/tests/ahci-test.c b/tests/qtest/ahci-test.c similarity index 100% rename from tests/ahci-test.c rename to tests/qtest/ahci-test.c diff --git a/tests/arm-cpu-features.c b/tests/qtest/arm-cpu-features.c similarity index 91% rename from tests/arm-cpu-features.c rename to tests/qtest/arm-cpu-features.c index 6e99aa951e..4692173676 100644 --- a/tests/arm-cpu-features.c +++ b/tests/qtest/arm-cpu-features.c @@ -20,8 +20,8 @@ */ #define SVE_MAX_VQ 16 -#define MACHINE "-machine virt,gic-version=max,accel=tcg " -#define MACHINE_KVM "-machine virt,gic-version=max,accel=kvm:tcg " +#define MACHINE "-machine virt,gic-version=max -accel tcg " +#define MACHINE_KVM "-machine virt,gic-version=max -accel kvm -accel tcg " #define QUERY_HEAD "{ 'execute': 'query-cpu-model-expansion', " \ " 'arguments': { 'type': 'full', " #define QUERY_TAIL "}}" @@ -159,6 +159,25 @@ static bool resp_get_feature(QDict *resp, const char *feature) qobject_unref(_resp); \ }) +#define assert_feature(qts, cpu_type, feature, expected_value) \ +({ \ + QDict *_resp, *_props; \ + \ + _resp = do_query_no_props(qts, cpu_type); \ + g_assert(_resp); \ + g_assert(resp_has_props(_resp)); \ + _props = resp_get_props(_resp); \ + g_assert(qdict_get(_props, feature)); \ + g_assert(qdict_get_bool(_props, feature) == (expected_value)); \ + qobject_unref(_resp); \ +}) + +#define assert_has_feature_enabled(qts, cpu_type, feature) \ + assert_feature(qts, cpu_type, feature, true) + +#define assert_has_feature_disabled(qts, cpu_type, feature) \ + assert_feature(qts, cpu_type, feature, false) + static void assert_type_full(QTestState *qts) { const char *error; @@ -405,16 +424,18 @@ static void test_query_cpu_model_expansion(const void *data) assert_error(qts, "host", "The CPU type 'host' requires KVM", NULL); /* Test expected feature presence/absence for some cpu types */ - assert_has_feature(qts, "max", "pmu"); - assert_has_feature(qts, "cortex-a15", "pmu"); + assert_has_feature_enabled(qts, "max", "pmu"); + assert_has_feature_enabled(qts, "cortex-a15", "pmu"); assert_has_not_feature(qts, "cortex-a15", "aarch64"); + assert_has_not_feature(qts, "max", "kvm-no-adjvtime"); + if (g_str_equal(qtest_get_arch(), "aarch64")) { - assert_has_feature(qts, "max", "aarch64"); - assert_has_feature(qts, "max", "sve"); - assert_has_feature(qts, "max", "sve128"); - assert_has_feature(qts, "cortex-a57", "pmu"); - assert_has_feature(qts, "cortex-a57", "aarch64"); + assert_has_feature_enabled(qts, "max", "aarch64"); + assert_has_feature_enabled(qts, "max", "sve"); + assert_has_feature_enabled(qts, "max", "sve128"); + assert_has_feature_enabled(qts, "cortex-a57", "pmu"); + assert_has_feature_enabled(qts, "cortex-a57", "aarch64"); sve_tests_default(qts, "max"); @@ -443,6 +464,8 @@ static void test_query_cpu_model_expansion_kvm(const void *data) return; } + assert_has_feature_disabled(qts, "host", "kvm-no-adjvtime"); + if (g_str_equal(qtest_get_arch(), "aarch64")) { bool kvm_supports_sve; char max_name[8], name[8]; @@ -451,8 +474,8 @@ static void test_query_cpu_model_expansion_kvm(const void *data) QDict *resp; char *error; - assert_has_feature(qts, "host", "aarch64"); - assert_has_feature(qts, "host", "pmu"); + assert_has_feature_enabled(qts, "host", "aarch64"); + assert_has_feature_enabled(qts, "host", "pmu"); assert_error(qts, "cortex-a15", "We cannot guarantee the CPU type 'cortex-a15' works " diff --git a/tests/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h similarity index 100% rename from tests/bios-tables-test-allowed-diff.h rename to tests/qtest/bios-tables-test-allowed-diff.h diff --git a/tests/bios-tables-test.c b/tests/qtest/bios-tables-test.c similarity index 88% rename from tests/bios-tables-test.c rename to tests/qtest/bios-tables-test.c index 79f5da092f..0a597bbacf 100644 --- a/tests/bios-tables-test.c +++ b/tests/qtest/bios-tables-test.c @@ -14,11 +14,14 @@ * How to add or update the tests: * Contributor: * 1. add empty files for new tables, if any, under tests/data/acpi - * 2. list any changed files in tests/bios-tables-test-allowed-diff.h + * 2. list any changed files in tests/qtest/bios-tables-test-allowed-diff.h * 3. commit the above *before* making changes that affect the tables - * Maintainer: + * + * Contributor or ACPI Maintainer (steps 4-7 need to be redone to resolve conflicts + * in binary commit created in step 6): + * * After 1-3 above tests will pass but ignore differences with the expected files. - * You will also notice that tests/bios-tables-test-allowed-diff.h lists + * You will also notice that tests/qtest/bios-tables-test-allowed-diff.h lists * a bunch of files. This is your hint that you need to do the below: * 4. Run * make check V=1 @@ -28,13 +31,23 @@ * output. If not - disassemble them yourself in any way you like. * Look at the differences - make sure they make sense and match what the * changes you are merging are supposed to do. + * Save the changes, preferably in form of ASL diff for the commit log in + * step 6. * * 5. From build directory, run: * $(SRC_PATH)/tests/data/acpi/rebuild-expected-aml.sh - * 6. Now commit any changes. - * 7. Before doing a pull request, make sure tests/bios-tables-test-allowed-diff.h - * is empty - this will ensure following changes to ACPI tables will - * be noticed. + * 6. Now commit any changes to the expected binary, include diff from step 4 + * in commit log. + * 7. Before sending patches to the list (Contributor) + * or before doing a pull request (Maintainer), make sure + * tests/qtest/bios-tables-test-allowed-diff.h is empty - this will ensure + * following changes to ACPI tables will be noticed. + * + * The resulting patchset/pull request then looks like this: + * - patch 1: list changed files in tests/qtest/bios-tables-test-allowed-diff.h. + * - patches 2 - n: real changes, may contain multiple patches. + * - patch n + 1: update golden master binaries and empty + * tests/qtest/bios-tables-test-allowed-diff.h */ #include "qemu/osdep.h" @@ -51,7 +64,7 @@ #define ACPI_REBUILD_EXPECTED_AML "TEST_ACPI_REBUILD_AML" typedef struct { - const char *accel; + bool tcg_only; const char *machine; const char *variant; const char *uefi_fl1; @@ -413,7 +426,9 @@ static void test_acpi_asl(test_data *data) fprintf(stderr, "acpi-test: Warning! %.4s binary file mismatch. " - "Actual [aml:%s], Expected [aml:%s].\n", + "Actual [aml:%s], Expected [aml:%s].\n" + "See source file tests/qtest/bios-tables-test.c " + "for instructions on how to update expected files.\n", exp_sdt->aml, sdt->aml_file, exp_sdt->aml_file); all_tables_match = all_tables_match && @@ -448,21 +463,20 @@ static void test_acpi_asl(test_data *data) "Actual [asl:%s, aml:%s], Expected [asl:%s, aml:%s].\n", exp_sdt->aml, sdt->asl_file, sdt->aml_file, exp_sdt->asl_file, exp_sdt->aml_file); + fflush(stderr); if (getenv("V")) { - const char *diff_cmd = getenv("DIFF"); - if (diff_cmd) { - int ret G_GNUC_UNUSED; - char *diff = g_strdup_printf("%s %s %s", diff_cmd, - exp_sdt->asl_file, sdt->asl_file); - ret = system(diff) ; - g_free(diff); - } else { - fprintf(stderr, "acpi-test: Warning. not showing " - "difference since no diff utility is specified. " - "Set 'DIFF' environment variable to a preferred " - "diff utility and run 'make V=1 check' again to " - "see ASL difference."); - } + const char *diff_env = getenv("DIFF"); + const char *diff_cmd = diff_env ? diff_env : "diff -u"; + char *diff = g_strdup_printf("%s %s %s", diff_cmd, + exp_sdt->asl_file, sdt->asl_file); + int out = dup(STDOUT_FILENO); + int ret G_GNUC_UNUSED; + + dup2(STDERR_FILENO, STDOUT_FILENO); + ret = system(diff) ; + dup2(out, STDOUT_FILENO); + close(out); + g_free(diff); } } } @@ -607,19 +621,19 @@ static void test_acpi_one(const char *params, test_data *data) * TODO: convert '-drive if=pflash' to new syntax (see e33763be7cd3) * when arm/virt boad starts to support it. */ - args = g_strdup_printf("-machine %s,accel=%s -nodefaults -nographic " + args = g_strdup_printf("-machine %s %s -accel tcg -nodefaults -nographic " "-drive if=pflash,format=raw,file=%s,readonly " "-drive if=pflash,format=raw,file=%s,snapshot=on -cdrom %s %s", - data->machine, data->accel ? data->accel : "kvm:tcg", + data->machine, data->tcg_only ? "" : "-accel kvm", data->uefi_fl1, data->uefi_fl2, data->cd, params ? params : ""); } else { /* Disable kernel irqchip to be able to override apic irq0. */ - args = g_strdup_printf("-machine %s,accel=%s,kernel-irqchip=off " + args = g_strdup_printf("-machine %s,kernel-irqchip=off %s -accel tcg " "-net none -display none %s " "-drive id=hd0,if=none,file=%s,format=raw " "-device ide-hd,drive=hd0 ", - data->machine, data->accel ? data->accel : "kvm:tcg", + data->machine, data->tcg_only ? "" : "-accel kvm", params ? params : "", disk); } @@ -904,7 +918,7 @@ static void test_acpi_virt_tcg_memhp(void) { test_data data = { .machine = "virt", - .accel = "tcg", + .tcg_only = true, .uefi_fl1 = "pc-bios/edk2-aarch64-code.fd", .uefi_fl2 = "pc-bios/edk2-arm-vars.fd", .cd = "tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2", @@ -929,7 +943,7 @@ static void test_acpi_virt_tcg_numamem(void) { test_data data = { .machine = "virt", - .accel = "tcg", + .tcg_only = true, .uefi_fl1 = "pc-bios/edk2-aarch64-code.fd", .uefi_fl2 = "pc-bios/edk2-arm-vars.fd", .cd = "tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2", @@ -947,11 +961,53 @@ static void test_acpi_virt_tcg_numamem(void) } +static void test_acpi_tcg_acpi_hmat(const char *machine) +{ + test_data data; + + memset(&data, 0, sizeof(data)); + data.machine = machine; + data.variant = ".acpihmat"; + test_acpi_one(" -machine hmat=on" + " -smp 2,sockets=2" + " -m 128M,slots=2,maxmem=1G" + " -object memory-backend-ram,size=64M,id=m0" + " -object memory-backend-ram,size=64M,id=m1" + " -numa node,nodeid=0,memdev=m0" + " -numa node,nodeid=1,memdev=m1,initiator=0" + " -numa cpu,node-id=0,socket-id=0" + " -numa cpu,node-id=0,socket-id=1" + " -numa hmat-lb,initiator=0,target=0,hierarchy=memory," + "data-type=access-latency,latency=1" + " -numa hmat-lb,initiator=0,target=0,hierarchy=memory," + "data-type=access-bandwidth,bandwidth=65534M" + " -numa hmat-lb,initiator=0,target=1,hierarchy=memory," + "data-type=access-latency,latency=65534" + " -numa hmat-lb,initiator=0,target=1,hierarchy=memory," + "data-type=access-bandwidth,bandwidth=32767M" + " -numa hmat-cache,node-id=0,size=10K,level=1," + "associativity=direct,policy=write-back,line=8" + " -numa hmat-cache,node-id=1,size=10K,level=1," + "associativity=direct,policy=write-back,line=8", + &data); + free_test_data(&data); +} + +static void test_acpi_q35_tcg_acpi_hmat(void) +{ + test_acpi_tcg_acpi_hmat(MACHINE_Q35); +} + +static void test_acpi_piix4_tcg_acpi_hmat(void) +{ + test_acpi_tcg_acpi_hmat(MACHINE_PC); +} + static void test_acpi_virt_tcg(void) { test_data data = { .machine = "virt", - .accel = "tcg", + .tcg_only = true, .uefi_fl1 = "pc-bios/edk2-aarch64-code.fd", .uefi_fl2 = "pc-bios/edk2-arm-vars.fd", .cd = "tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2", @@ -991,6 +1047,8 @@ int main(int argc, char *argv[]) qtest_add_func("acpi/q35/numamem", test_acpi_q35_tcg_numamem); qtest_add_func("acpi/piix4/dimmpxm", test_acpi_piix4_tcg_dimm_pxm); qtest_add_func("acpi/q35/dimmpxm", test_acpi_q35_tcg_dimm_pxm); + qtest_add_func("acpi/piix4/acpihmat", test_acpi_piix4_tcg_acpi_hmat); + qtest_add_func("acpi/q35/acpihmat", test_acpi_q35_tcg_acpi_hmat); } else if (strcmp(arch, "aarch64") == 0) { qtest_add_func("acpi/virt", test_acpi_virt_tcg); qtest_add_func("acpi/virt/numamem", test_acpi_virt_tcg_numamem); diff --git a/tests/boot-order-test.c b/tests/qtest/boot-order-test.c similarity index 85% rename from tests/boot-order-test.c rename to tests/qtest/boot-order-test.c index a725bce729..2f1c07221c 100644 --- a/tests/boot-order-test.c +++ b/tests/qtest/boot-order-test.c @@ -108,33 +108,9 @@ static void test_pc_boot_order(void) test_boot_orders(NULL, read_boot_order_pc, test_cases_pc); } -static uint8_t read_m48t59(QTestState *qts, uint64_t addr, uint16_t reg) -{ - qtest_writeb(qts, addr, reg & 0xff); - qtest_writeb(qts, addr + 1, reg >> 8); - return qtest_readb(qts, addr + 3); -} - -static uint64_t read_boot_order_prep(QTestState *qts) -{ - return read_m48t59(qts, 0x80000000 + 0x74, 0x34); -} - -static const boot_order_test test_cases_prep[] = { - { "", 'c', 'c' }, - { "-boot c", 'c', 'c' }, - { "-boot d", 'd', 'd' }, - {} -}; - -static void test_prep_boot_order(void) -{ - test_boot_orders("prep", read_boot_order_prep, test_cases_prep); -} - static uint64_t read_boot_order_pmac(QTestState *qts) { - QFWCFG *fw_cfg = mm_fw_cfg_init(qts, 0xf0000510); + g_autoptr(QFWCFG) fw_cfg = mm_fw_cfg_init(qts, 0xf0000510); return qfw_cfg_get_u16(fw_cfg, FW_CFG_BOOT_DEVICE); } @@ -159,7 +135,7 @@ static void test_pmac_newworld_boot_order(void) static uint64_t read_boot_order_sun4m(QTestState *qts) { - QFWCFG *fw_cfg = mm_fw_cfg_init(qts, 0xd00000510ULL); + g_autoptr(QFWCFG) fw_cfg = mm_fw_cfg_init(qts, 0xd00000510ULL); return qfw_cfg_get_u16(fw_cfg, FW_CFG_BOOT_DEVICE); } @@ -171,7 +147,7 @@ static void test_sun4m_boot_order(void) static uint64_t read_boot_order_sun4u(QTestState *qts) { - QFWCFG *fw_cfg = io_fw_cfg_init(qts, 0x510); + g_autoptr(QFWCFG) fw_cfg = io_fw_cfg_init(qts, 0x510); return qfw_cfg_get_u16(fw_cfg, FW_CFG_BOOT_DEVICE); } @@ -190,7 +166,6 @@ int main(int argc, char *argv[]) if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { qtest_add_func("boot-order/pc", test_pc_boot_order); } else if (strcmp(arch, "ppc") == 0 || strcmp(arch, "ppc64") == 0) { - qtest_add_func("boot-order/prep", test_prep_boot_order); qtest_add_func("boot-order/pmac_oldworld", test_pmac_oldworld_boot_order); qtest_add_func("boot-order/pmac_newworld", diff --git a/tests/boot-sector.c b/tests/qtest/boot-sector.c similarity index 97% rename from tests/boot-sector.c rename to tests/qtest/boot-sector.c index 7824286b9a..9e66c6d013 100644 --- a/tests/boot-sector.c +++ b/tests/qtest/boot-sector.c @@ -75,11 +75,11 @@ static const uint8_t s390x_psw_and_magic[] = { 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40 /* in the s390-ccw bios */ }; static const uint8_t s390x_code[] = { - 0xa7, 0xf4, 0x00, 0x0a, /* j 0x10010 */ + 0xa7, 0xf4, 0x00, 0x08, /* j 0x10010 */ 0x00, 0x00, 0x00, 0x00, 'S', '3', '9', '0', 'E', 'P', 0x00, 0x01, - 0xa7, 0x38, HIGH(SIGNATURE_ADDR), LOW(SIGNATURE_ADDR), /* lhi r3,0x7c10 */ + 0xa7, 0x39, HIGH(SIGNATURE_ADDR), LOW(SIGNATURE_ADDR), /* lghi r3,0x7c10 */ 0xa7, 0x48, LOW(SIGNATURE), HIGH(SIGNATURE), /* lhi r4,0xadde */ 0x40, 0x40, 0x30, 0x00, /* sth r4,0(r3) */ 0xa7, 0xf4, 0xff, 0xfa /* j 0x10010 */ diff --git a/tests/boot-sector.h b/tests/qtest/boot-sector.h similarity index 100% rename from tests/boot-sector.h rename to tests/qtest/boot-sector.h diff --git a/tests/boot-serial-test.c b/tests/qtest/boot-serial-test.c similarity index 96% rename from tests/boot-serial-test.c rename to tests/qtest/boot-serial-test.c index d3a54a0ba5..85a3614286 100644 --- a/tests/boot-serial-test.c +++ b/tests/qtest/boot-serial-test.c @@ -15,6 +15,7 @@ #include "qemu/osdep.h" #include "libqtest.h" +#include "libqos/libqos-spapr.h" static const uint8_t kernel_mcf5208[] = { 0x41, 0xf9, 0xfc, 0x06, 0x00, 0x00, /* lea 0xfc060000,%a0 */ @@ -112,7 +113,7 @@ static testdef_t tests[] = { { "ppc64", "40p", "-m 192", "Memory: 192M" }, { "ppc64", "mac99", "", "PowerPC,970FX" }, { "ppc64", "pseries", - "-machine cap-cfpc=broken,cap-sbbc=broken,cap-ibs=broken", + "-machine " PSERIES_DEFAULT_CAPABILITIES, "Open Firmware" }, { "ppc64", "powernv8", "", "OPAL" }, { "ppc64", "powernv9", "", "OPAL" }, @@ -135,7 +136,8 @@ static testdef_t tests[] = { sizeof(kernel_plml605), kernel_plml605 }, { "moxie", "moxiesim", "", "TT", sizeof(bios_moxiesim), 0, bios_moxiesim }, { "arm", "raspi2", "", "TT", sizeof(bios_raspi2), 0, bios_raspi2 }, - { "hppa", "hppa", "", "SeaBIOS wants SYSTEM HALT" }, + /* For hppa, force bios to output to serial by disabling graphics. */ + { "hppa", "hppa", "-vga none", "SeaBIOS wants SYSTEM HALT" }, { "aarch64", "virt", "-cpu cortex-a57", "TT", sizeof(kernel_aarch64), kernel_aarch64 }, { "arm", "microbit", "", "T", sizeof(kernel_nrf51), kernel_nrf51 }, @@ -215,9 +217,9 @@ static void test_machine(const void *data) * Make sure that this test uses tcg if available: It is used as a * fast-enough smoketest for that. */ - qts = qtest_initf("%s %s -M %s,accel=tcg:kvm -no-shutdown " + qts = qtest_initf("%s %s -M %s -no-shutdown " "-chardev file,id=serial0,path=%s " - "-serial chardev:serial0 %s", + "-serial chardev:serial0 -accel tcg -accel kvm %s", codeparam, code ? codetmp : "", test->machine, serialtmp, test->extra); if (code) { diff --git a/tests/cdrom-test.c b/tests/qtest/cdrom-test.c similarity index 98% rename from tests/cdrom-test.c rename to tests/qtest/cdrom-test.c index 34e9974634..833a0508a1 100644 --- a/tests/cdrom-test.c +++ b/tests/qtest/cdrom-test.c @@ -120,7 +120,7 @@ static void test_cdboot(gconstpointer data) { QTestState *qts; - qts = qtest_initf("-M accel=kvm:tcg -no-shutdown %s%s", (const char *)data, + qts = qtest_initf("-accel kvm -accel tcg -no-shutdown %s%s", (const char *)data, isoimage); boot_sector_test(qts); qtest_quit(qts); @@ -189,7 +189,7 @@ int main(int argc, char **argv) add_s390x_tests(); } else if (g_str_equal(arch, "ppc64")) { const char *ppcmachines[] = { - "pseries", "mac99", "g3beige", "40p", "prep", NULL + "pseries", "mac99", "g3beige", "40p", NULL }; add_cdrom_param_tests(ppcmachines); } else if (g_str_equal(arch, "sparc")) { diff --git a/tests/cpu-plug-test.c b/tests/qtest/cpu-plug-test.c similarity index 97% rename from tests/cpu-plug-test.c rename to tests/qtest/cpu-plug-test.c index 30e514bbfb..e8ffbbce4b 100644 --- a/tests/cpu-plug-test.c +++ b/tests/qtest/cpu-plug-test.c @@ -148,11 +148,7 @@ static void add_pc_test_case(const char *mname) (strcmp(mname, "pc-1.3") == 0) || (strcmp(mname, "pc-1.2") == 0) || (strcmp(mname, "pc-1.1") == 0) || - (strcmp(mname, "pc-1.0") == 0) || - (strcmp(mname, "pc-0.15") == 0) || - (strcmp(mname, "pc-0.14") == 0) || - (strcmp(mname, "pc-0.13") == 0) || - (strcmp(mname, "pc-0.12") == 0)) { + (strcmp(mname, "pc-1.0") == 0)) { path = g_strdup_printf("cpu-plug/%s/init/%ux%ux%u&maxcpus=%u", mname, data->sockets, data->cores, data->threads, data->maxcpus); diff --git a/tests/qtest/dbus-vmstate-test.c b/tests/qtest/dbus-vmstate-test.c new file mode 100644 index 0000000000..2e5e47dec2 --- /dev/null +++ b/tests/qtest/dbus-vmstate-test.c @@ -0,0 +1,382 @@ +#include "qemu/osdep.h" +#include +#include +#include "libqtest.h" +#include "qemu-common.h" +#include "dbus-vmstate1.h" +#include "migration-helpers.h" + +static char *workdir; + +typedef struct TestServerId { + const char *name; + const char *data; + size_t size; +} TestServerId; + +static const TestServerId idA = { + "idA", "I'am\0idA!", sizeof("I'am\0idA!") +}; + +static const TestServerId idB = { + "idB", "I'am\0idB!", sizeof("I'am\0idB!") +}; + +typedef struct TestServer { + const TestServerId *id; + bool save_called; + bool load_called; +} TestServer; + +typedef struct Test { + const char *id_list; + bool migrate_fail; + bool without_dst_b; + TestServer srcA; + TestServer dstA; + TestServer srcB; + TestServer dstB; + GMainLoop *loop; + QTestState *src_qemu; +} Test; + +static gboolean +vmstate_load(VMState1 *object, GDBusMethodInvocation *invocation, + const gchar *arg_data, gpointer user_data) +{ + TestServer *h = user_data; + g_autoptr(GVariant) var = NULL; + GVariant *args; + const uint8_t *data; + size_t size; + + args = g_dbus_method_invocation_get_parameters(invocation); + var = g_variant_get_child_value(args, 0); + data = g_variant_get_fixed_array(var, &size, sizeof(char)); + g_assert_cmpuint(size, ==, h->id->size); + g_assert(!memcmp(data, h->id->data, h->id->size)); + h->load_called = true; + + g_dbus_method_invocation_return_value(invocation, g_variant_new("()")); + return TRUE; +} + +static gboolean +vmstate_save(VMState1 *object, GDBusMethodInvocation *invocation, + gpointer user_data) +{ + TestServer *h = user_data; + GVariant *var; + + var = g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE, + h->id->data, h->id->size, sizeof(char)); + g_dbus_method_invocation_return_value(invocation, + g_variant_new("(@ay)", var)); + h->save_called = true; + + return TRUE; +} + +typedef struct WaitNamed { + GMainLoop *loop; + bool named; +} WaitNamed; + +static void +named_cb(GDBusConnection *connection, + const gchar *name, + gpointer user_data) +{ + WaitNamed *t = user_data; + + t->named = true; + g_main_loop_quit(t->loop); +} + +static GDBusConnection * +get_connection(Test *test, guint *ownid) +{ + g_autofree gchar *addr = NULL; + WaitNamed *wait; + GError *err = NULL; + GDBusConnection *c; + + wait = g_new0(WaitNamed, 1); + wait->loop = test->loop; + addr = g_dbus_address_get_for_bus_sync(G_BUS_TYPE_SESSION, NULL, &err); + g_assert_no_error(err); + + c = g_dbus_connection_new_for_address_sync( + addr, + G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION | + G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT, + NULL, NULL, &err); + g_assert_no_error(err); + *ownid = g_bus_own_name_on_connection(c, "org.qemu.VMState1", + G_BUS_NAME_OWNER_FLAGS_NONE, + named_cb, named_cb, wait, g_free); + if (!wait->named) { + g_main_loop_run(wait->loop); + } + + return c; +} + +static GDBusObjectManagerServer * +get_server(GDBusConnection *conn, TestServer *s, const TestServerId *id) +{ + g_autoptr(GDBusObjectSkeleton) sk = NULL; + g_autoptr(VMState1Skeleton) v = NULL; + GDBusObjectManagerServer *os; + + s->id = id; + os = g_dbus_object_manager_server_new("/org/qemu"); + sk = g_dbus_object_skeleton_new("/org/qemu/VMState1"); + + v = VMSTATE1_SKELETON(vmstate1_skeleton_new()); + g_object_set(v, "id", id->name, NULL); + + g_signal_connect(v, "handle-load", G_CALLBACK(vmstate_load), s); + g_signal_connect(v, "handle-save", G_CALLBACK(vmstate_save), s); + + g_dbus_object_skeleton_add_interface(sk, G_DBUS_INTERFACE_SKELETON(v)); + g_dbus_object_manager_server_export(os, sk); + g_dbus_object_manager_server_set_connection(os, conn); + + return os; +} + +static void +set_id_list(Test *test, QTestState *s) +{ + if (!test->id_list) { + return; + } + + g_assert(!qmp_rsp_is_err(qtest_qmp(s, + "{ 'execute': 'qom-set', 'arguments': " + "{ 'path': '/objects/dv', 'property': 'id-list', 'value': %s } }", + test->id_list))); +} + +static gpointer +dbus_vmstate_thread(gpointer data) +{ + GMainLoop *loop = data; + + g_main_loop_run(loop); + + return NULL; +} + +static void +test_dbus_vmstate(Test *test) +{ + g_autofree char *src_qemu_args = NULL; + g_autofree char *dst_qemu_args = NULL; + g_autoptr(GTestDBus) srcbus = NULL; + g_autoptr(GTestDBus) dstbus = NULL; + g_autoptr(GDBusConnection) srcconnA = NULL; + g_autoptr(GDBusConnection) srcconnB = NULL; + g_autoptr(GDBusConnection) dstconnA = NULL; + g_autoptr(GDBusConnection) dstconnB = NULL; + g_autoptr(GDBusObjectManagerServer) srcserverA = NULL; + g_autoptr(GDBusObjectManagerServer) srcserverB = NULL; + g_autoptr(GDBusObjectManagerServer) dstserverA = NULL; + g_autoptr(GDBusObjectManagerServer) dstserverB = NULL; + g_auto(GStrv) srcaddr = NULL; + g_auto(GStrv) dstaddr = NULL; + g_autoptr(GThread) thread = NULL; + g_autoptr(GMainLoop) loop = NULL; + g_autofree char *uri = NULL; + QTestState *src_qemu = NULL, *dst_qemu = NULL; + guint ownsrcA, ownsrcB, owndstA, owndstB; + + uri = g_strdup_printf("unix:%s/migsocket", workdir); + + loop = g_main_loop_new(NULL, FALSE); + test->loop = loop; + + srcbus = g_test_dbus_new(G_TEST_DBUS_NONE); + g_test_dbus_up(srcbus); + srcconnA = get_connection(test, &ownsrcA); + srcserverA = get_server(srcconnA, &test->srcA, &idA); + srcconnB = get_connection(test, &ownsrcB); + srcserverB = get_server(srcconnB, &test->srcB, &idB); + + /* remove ,guid=foo part */ + srcaddr = g_strsplit(g_test_dbus_get_bus_address(srcbus), ",", 2); + src_qemu_args = + g_strdup_printf("-object dbus-vmstate,id=dv,addr=%s", srcaddr[0]); + + dstbus = g_test_dbus_new(G_TEST_DBUS_NONE); + g_test_dbus_up(dstbus); + dstconnA = get_connection(test, &owndstA); + dstserverA = get_server(dstconnA, &test->dstA, &idA); + if (!test->without_dst_b) { + dstconnB = get_connection(test, &owndstB); + dstserverB = get_server(dstconnB, &test->dstB, &idB); + } + + dstaddr = g_strsplit(g_test_dbus_get_bus_address(dstbus), ",", 2); + dst_qemu_args = + g_strdup_printf("-object dbus-vmstate,id=dv,addr=%s -incoming %s", + dstaddr[0], uri); + + src_qemu = qtest_init(src_qemu_args); + dst_qemu = qtest_init(dst_qemu_args); + set_id_list(test, src_qemu); + set_id_list(test, dst_qemu); + + thread = g_thread_new("dbus-vmstate-thread", dbus_vmstate_thread, loop); + + migrate_qmp(src_qemu, uri, "{}"); + test->src_qemu = src_qemu; + if (test->migrate_fail) { + wait_for_migration_fail(src_qemu, true); + qtest_set_expected_status(dst_qemu, 1); + } else { + wait_for_migration_complete(src_qemu); + } + + qtest_quit(dst_qemu); + qtest_quit(src_qemu); + g_bus_unown_name(ownsrcA); + g_bus_unown_name(ownsrcB); + g_bus_unown_name(owndstA); + if (!test->without_dst_b) { + g_bus_unown_name(owndstB); + } + + g_main_loop_quit(test->loop); +} + +static void +check_not_migrated(TestServer *s, TestServer *d) +{ + assert(!s->save_called); + assert(!s->load_called); + assert(!d->save_called); + assert(!d->load_called); +} + +static void +check_migrated(TestServer *s, TestServer *d) +{ + assert(s->save_called); + assert(!s->load_called); + assert(!d->save_called); + assert(d->load_called); +} + +static void +test_dbus_vmstate_without_list(void) +{ + Test test = { 0, }; + + test_dbus_vmstate(&test); + + check_migrated(&test.srcA, &test.dstA); + check_migrated(&test.srcB, &test.dstB); +} + +static void +test_dbus_vmstate_with_list(void) +{ + Test test = { .id_list = "idA,idB" }; + + test_dbus_vmstate(&test); + + check_migrated(&test.srcA, &test.dstA); + check_migrated(&test.srcB, &test.dstB); +} + +static void +test_dbus_vmstate_only_a(void) +{ + Test test = { .id_list = "idA" }; + + test_dbus_vmstate(&test); + + check_migrated(&test.srcA, &test.dstA); + check_not_migrated(&test.srcB, &test.dstB); +} + +static void +test_dbus_vmstate_missing_src(void) +{ + Test test = { .id_list = "idA,idC", .migrate_fail = true }; + + /* run in subprocess to silence QEMU error reporting */ + if (g_test_subprocess()) { + test_dbus_vmstate(&test); + check_not_migrated(&test.srcA, &test.dstA); + check_not_migrated(&test.srcB, &test.dstB); + return; + } + + g_test_trap_subprocess(NULL, 0, 0); + g_test_trap_assert_passed(); +} + +static void +test_dbus_vmstate_missing_dst(void) +{ + Test test = { .id_list = "idA,idB", + .without_dst_b = true, + .migrate_fail = true }; + + /* run in subprocess to silence QEMU error reporting */ + if (g_test_subprocess()) { + test_dbus_vmstate(&test); + assert(test.srcA.save_called); + assert(test.srcB.save_called); + assert(!test.dstB.save_called); + return; + } + + g_test_trap_subprocess(NULL, 0, 0); + g_test_trap_assert_passed(); +} + +int +main(int argc, char **argv) +{ + GError *err = NULL; + g_autofree char *dbus_daemon = NULL; + int ret; + + dbus_daemon = g_build_filename(G_STRINGIFY(SRCDIR), + "tests", + "dbus-vmstate-daemon.sh", + NULL); + g_setenv("G_TEST_DBUS_DAEMON", dbus_daemon, true); + + g_test_init(&argc, &argv, NULL); + + workdir = g_dir_make_tmp("dbus-vmstate-test-XXXXXX", &err); + if (!workdir) { + g_error("Unable to create temporary dir: %s\n", err->message); + exit(1); + } + + g_setenv("DBUS_VMSTATE_TEST_TMPDIR", workdir, true); + + qtest_add_func("/dbus-vmstate/without-list", + test_dbus_vmstate_without_list); + qtest_add_func("/dbus-vmstate/with-list", + test_dbus_vmstate_with_list); + qtest_add_func("/dbus-vmstate/only-a", + test_dbus_vmstate_only_a); + qtest_add_func("/dbus-vmstate/missing-src", + test_dbus_vmstate_missing_src); + qtest_add_func("/dbus-vmstate/missing-dst", + test_dbus_vmstate_missing_dst); + + ret = g_test_run(); + + rmdir(workdir); + g_free(workdir); + + return ret; +} diff --git a/tests/qtest/dbus-vmstate1.xml b/tests/qtest/dbus-vmstate1.xml new file mode 100644 index 0000000000..cc8563be4c --- /dev/null +++ b/tests/qtest/dbus-vmstate1.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/tests/device-introspect-test.c b/tests/qtest/device-introspect-test.c similarity index 99% rename from tests/device-introspect-test.c rename to tests/qtest/device-introspect-test.c index 04f22903b0..f2c1576cae 100644 --- a/tests/device-introspect-test.c +++ b/tests/qtest/device-introspect-test.c @@ -288,7 +288,7 @@ static void add_machine_test_case(const char *mname) char *path, *args; /* Ignore blacklisted machines */ - if (g_str_equal("xenfv", mname) || g_str_equal("xenpv", mname)) { + if (!memcmp("xenfv", mname, 5) || g_str_equal("xenpv", mname)) { return; } diff --git a/tests/device-plug-test.c b/tests/qtest/device-plug-test.c similarity index 100% rename from tests/device-plug-test.c rename to tests/qtest/device-plug-test.c diff --git a/tests/display-vga-test.c b/tests/qtest/display-vga-test.c similarity index 100% rename from tests/display-vga-test.c rename to tests/qtest/display-vga-test.c diff --git a/tests/drive_del-test.c b/tests/qtest/drive_del-test.c similarity index 100% rename from tests/drive_del-test.c rename to tests/qtest/drive_del-test.c diff --git a/tests/ds1338-test.c b/tests/qtest/ds1338-test.c similarity index 100% rename from tests/ds1338-test.c rename to tests/qtest/ds1338-test.c diff --git a/tests/e1000-test.c b/tests/qtest/e1000-test.c similarity index 100% rename from tests/e1000-test.c rename to tests/qtest/e1000-test.c diff --git a/tests/e1000e-test.c b/tests/qtest/e1000e-test.c similarity index 100% rename from tests/e1000e-test.c rename to tests/qtest/e1000e-test.c diff --git a/tests/eepro100-test.c b/tests/qtest/eepro100-test.c similarity index 100% rename from tests/eepro100-test.c rename to tests/qtest/eepro100-test.c diff --git a/tests/endianness-test.c b/tests/qtest/endianness-test.c similarity index 99% rename from tests/endianness-test.c rename to tests/qtest/endianness-test.c index 58527952a5..2798802c63 100644 --- a/tests/endianness-test.c +++ b/tests/qtest/endianness-test.c @@ -35,7 +35,7 @@ static const TestCase test_cases[] = { { "mips64", "malta", 0x10000000, .bswap = true }, { "mips64el", "fulong2e", 0x1fd00000 }, { "ppc", "g3beige", 0xfe000000, .bswap = true, .superio = "i82378" }, - { "ppc", "prep", 0x80000000, .bswap = true }, + { "ppc", "40p", 0x80000000, .bswap = true }, { "ppc", "bamboo", 0xe8000000, .bswap = true, .superio = "i82378" }, { "ppc64", "mac99", 0xf2000000, .bswap = true, .superio = "i82378" }, { "ppc64", "pseries", (1ULL << 45), .bswap = true, .superio = "i82378" }, diff --git a/tests/es1370-test.c b/tests/qtest/es1370-test.c similarity index 100% rename from tests/es1370-test.c rename to tests/qtest/es1370-test.c diff --git a/tests/fdc-test.c b/tests/qtest/fdc-test.c similarity index 100% rename from tests/fdc-test.c rename to tests/qtest/fdc-test.c diff --git a/tests/qtest/fuzz/Makefile.include b/tests/qtest/fuzz/Makefile.include new file mode 100644 index 0000000000..cde3e9636c --- /dev/null +++ b/tests/qtest/fuzz/Makefile.include @@ -0,0 +1,18 @@ +QEMU_PROG_FUZZ=qemu-fuzz-$(TARGET_NAME)$(EXESUF) + +fuzz-obj-y += tests/qtest/libqtest.o +fuzz-obj-y += $(libqos-obj-y) +fuzz-obj-y += tests/qtest/fuzz/fuzz.o # Fuzzer skeleton +fuzz-obj-y += tests/qtest/fuzz/fork_fuzz.o +fuzz-obj-y += tests/qtest/fuzz/qos_fuzz.o + +# Targets +fuzz-obj-y += tests/qtest/fuzz/i440fx_fuzz.o +fuzz-obj-y += tests/qtest/fuzz/virtio_net_fuzz.o +fuzz-obj-y += tests/qtest/fuzz/virtio_scsi_fuzz.o + +FUZZ_CFLAGS += -I$(SRC_PATH)/tests -I$(SRC_PATH)/tests/qtest + +# Linker Script to force coverage-counters into known regions which we can mark +# shared +FUZZ_LDFLAGS += -Xlinker -T$(SRC_PATH)/tests/qtest/fuzz/fork_fuzz.ld diff --git a/tests/qtest/fuzz/fork_fuzz.c b/tests/qtest/fuzz/fork_fuzz.c new file mode 100644 index 0000000000..2bd0851903 --- /dev/null +++ b/tests/qtest/fuzz/fork_fuzz.c @@ -0,0 +1,55 @@ +/* + * Fork-based fuzzing helpers + * + * Copyright Red Hat Inc., 2019 + * + * Authors: + * Alexander Bulekov + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include "fork_fuzz.h" + + +void counter_shm_init(void) +{ + char *shm_path = g_strdup_printf("/qemu-fuzz-cntrs.%d", getpid()); + int fd = shm_open(shm_path, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR); + g_free(shm_path); + + if (fd == -1) { + perror("Error: "); + exit(1); + } + if (ftruncate(fd, &__FUZZ_COUNTERS_END - &__FUZZ_COUNTERS_START) == -1) { + perror("Error: "); + exit(1); + } + /* Copy what's in the counter region to the shm.. */ + void *rptr = mmap(NULL , + &__FUZZ_COUNTERS_END - &__FUZZ_COUNTERS_START, + PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + memcpy(rptr, + &__FUZZ_COUNTERS_START, + &__FUZZ_COUNTERS_END - &__FUZZ_COUNTERS_START); + + munmap(rptr, &__FUZZ_COUNTERS_END - &__FUZZ_COUNTERS_START); + + /* And map the shm over the counter region */ + rptr = mmap(&__FUZZ_COUNTERS_START, + &__FUZZ_COUNTERS_END - &__FUZZ_COUNTERS_START, + PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, fd, 0); + + close(fd); + + if (!rptr) { + perror("Error: "); + exit(1); + } +} + + diff --git a/tests/qtest/fuzz/fork_fuzz.h b/tests/qtest/fuzz/fork_fuzz.h new file mode 100644 index 0000000000..9ecb8b58ef --- /dev/null +++ b/tests/qtest/fuzz/fork_fuzz.h @@ -0,0 +1,23 @@ +/* + * Fork-based fuzzing helpers + * + * Copyright Red Hat Inc., 2019 + * + * Authors: + * Alexander Bulekov + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#ifndef FORK_FUZZ_H +#define FORK_FUZZ_H + +extern uint8_t __FUZZ_COUNTERS_START; +extern uint8_t __FUZZ_COUNTERS_END; + +void counter_shm_init(void); + +#endif + diff --git a/tests/qtest/fuzz/fork_fuzz.ld b/tests/qtest/fuzz/fork_fuzz.ld new file mode 100644 index 0000000000..e086bba873 --- /dev/null +++ b/tests/qtest/fuzz/fork_fuzz.ld @@ -0,0 +1,41 @@ +/* + * We adjust linker script modification to place all of the stuff that needs to + * persist across fuzzing runs into a contiguous section of memory. Then, it is + * easy to re-map the counter-related memory as shared. + */ + +SECTIONS +{ + .data.fuzz_start : ALIGN(4K) + { + __FUZZ_COUNTERS_START = .; + __start___sancov_cntrs = .; + *(_*sancov_cntrs); + __stop___sancov_cntrs = .; + + /* Lowest stack counter */ + *(__sancov_lowest_stack); + } + .data.fuzz_ordered : + { + /* + * Coverage counters. They're not necessary for fuzzing, but are useful + * for analyzing the fuzzing performance + */ + __start___llvm_prf_cnts = .; + *(*llvm_prf_cnts); + __stop___llvm_prf_cnts = .; + + /* Internal Libfuzzer TracePC object which contains the ValueProfileMap */ + FuzzerTracePC*(.bss*); + } + .data.fuzz_end : ALIGN(4K) + { + __FUZZ_COUNTERS_END = .; + } +} +/* + * Don't overwrite the SECTIONS in the default linker script. Instead insert the + * above into the default script + */ +INSERT AFTER .data; diff --git a/tests/qtest/fuzz/fuzz.c b/tests/qtest/fuzz/fuzz.c new file mode 100644 index 0000000000..0d78ac8d36 --- /dev/null +++ b/tests/qtest/fuzz/fuzz.c @@ -0,0 +1,179 @@ +/* + * fuzzing driver + * + * Copyright Red Hat Inc., 2019 + * + * Authors: + * Alexander Bulekov + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" + +#include + +#include "sysemu/qtest.h" +#include "sysemu/runstate.h" +#include "sysemu/sysemu.h" +#include "qemu/main-loop.h" +#include "tests/qtest/libqtest.h" +#include "tests/qtest/libqos/qgraph.h" +#include "fuzz.h" + +#define MAX_EVENT_LOOPS 10 + +typedef struct FuzzTargetState { + FuzzTarget *target; + QSLIST_ENTRY(FuzzTargetState) target_list; +} FuzzTargetState; + +typedef QSLIST_HEAD(, FuzzTargetState) FuzzTargetList; + +static const char *fuzz_arch = TARGET_NAME; + +static FuzzTargetList *fuzz_target_list; +static FuzzTarget *fuzz_target; +static QTestState *fuzz_qts; + + + +void flush_events(QTestState *s) +{ + int i = MAX_EVENT_LOOPS; + while (g_main_context_pending(NULL) && i-- > 0) { + main_loop_wait(false); + } +} + +static QTestState *qtest_setup(void) +{ + qtest_server_set_send_handler(&qtest_client_inproc_recv, &fuzz_qts); + return qtest_inproc_init(&fuzz_qts, false, fuzz_arch, + &qtest_server_inproc_recv); +} + +void fuzz_add_target(const FuzzTarget *target) +{ + FuzzTargetState *tmp; + FuzzTargetState *target_state; + if (!fuzz_target_list) { + fuzz_target_list = g_new0(FuzzTargetList, 1); + } + + QSLIST_FOREACH(tmp, fuzz_target_list, target_list) { + if (g_strcmp0(tmp->target->name, target->name) == 0) { + fprintf(stderr, "Error: Fuzz target name %s already in use\n", + target->name); + abort(); + } + } + target_state = g_new0(FuzzTargetState, 1); + target_state->target = g_new0(FuzzTarget, 1); + *(target_state->target) = *target; + QSLIST_INSERT_HEAD(fuzz_target_list, target_state, target_list); +} + + + +static void usage(char *path) +{ + printf("Usage: %s --fuzz-target=FUZZ_TARGET [LIBFUZZER ARGUMENTS]\n", path); + printf("where FUZZ_TARGET is one of:\n"); + FuzzTargetState *tmp; + if (!fuzz_target_list) { + fprintf(stderr, "Fuzz target list not initialized\n"); + abort(); + } + QSLIST_FOREACH(tmp, fuzz_target_list, target_list) { + printf(" * %s : %s\n", tmp->target->name, + tmp->target->description); + } + exit(0); +} + +static FuzzTarget *fuzz_get_target(char* name) +{ + FuzzTargetState *tmp; + if (!fuzz_target_list) { + fprintf(stderr, "Fuzz target list not initialized\n"); + abort(); + } + + QSLIST_FOREACH(tmp, fuzz_target_list, target_list) { + if (strcmp(tmp->target->name, name) == 0) { + return tmp->target; + } + } + return NULL; +} + + +/* Executed for each fuzzing-input */ +int LLVMFuzzerTestOneInput(const unsigned char *Data, size_t Size) +{ + /* + * Do the pre-fuzz-initialization before the first fuzzing iteration, + * instead of before the actual fuzz loop. This is needed since libfuzzer + * may fork off additional workers, prior to the fuzzing loop, and if + * pre_fuzz() sets up e.g. shared memory, this should be done for the + * individual worker processes + */ + static int pre_fuzz_done; + if (!pre_fuzz_done && fuzz_target->pre_fuzz) { + fuzz_target->pre_fuzz(fuzz_qts); + pre_fuzz_done = true; + } + + fuzz_target->fuzz(fuzz_qts, Data, Size); + return 0; +} + +/* Executed once, prior to fuzzing */ +int LLVMFuzzerInitialize(int *argc, char ***argv, char ***envp) +{ + + char *target_name; + + /* Initialize qgraph and modules */ + qos_graph_init(); + module_call_init(MODULE_INIT_FUZZ_TARGET); + module_call_init(MODULE_INIT_QOM); + module_call_init(MODULE_INIT_LIBQOS); + + if (*argc <= 1) { + usage(**argv); + } + + /* Identify the fuzz target */ + target_name = (*argv)[1]; + if (!strstr(target_name, "--fuzz-target=")) { + usage(**argv); + } + + target_name += strlen("--fuzz-target="); + + fuzz_target = fuzz_get_target(target_name); + if (!fuzz_target) { + usage(**argv); + } + + fuzz_qts = qtest_setup(); + + if (fuzz_target->pre_vm_init) { + fuzz_target->pre_vm_init(); + } + + /* Run QEMU's softmmu main with the fuzz-target dependent arguments */ + const char *init_cmdline = fuzz_target->get_init_cmdline(fuzz_target); + + /* Split the runcmd into an argv and argc */ + wordexp_t result; + wordexp(init_cmdline, &result, 0); + + qemu_init(result.we_wordc, result.we_wordv, NULL); + + return 0; +} diff --git a/tests/qtest/fuzz/fuzz.h b/tests/qtest/fuzz/fuzz.h new file mode 100644 index 0000000000..03901d414e --- /dev/null +++ b/tests/qtest/fuzz/fuzz.h @@ -0,0 +1,95 @@ +/* + * fuzzing driver + * + * Copyright Red Hat Inc., 2019 + * + * Authors: + * Alexander Bulekov + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#ifndef FUZZER_H_ +#define FUZZER_H_ + +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "qapi/error.h" + +#include "tests/qtest/libqtest.h" + +/** + * A libfuzzer fuzzing target + * + * The QEMU fuzzing binary is built with all available targets, each + * with a unique @name that can be specified on the command-line to + * select which target should run. + * + * A target must implement ->fuzz() to process a random input. If QEMU + * crashes in ->fuzz() then libfuzzer will record a failure. + * + * Fuzzing targets are registered with fuzz_add_target(): + * + * static const FuzzTarget fuzz_target = { + * .name = "my-device-fifo", + * .description = "Fuzz the FIFO buffer registers of my-device", + * ... + * }; + * + * static void register_fuzz_target(void) + * { + * fuzz_add_target(&fuzz_target); + * } + * fuzz_target_init(register_fuzz_target); + */ +typedef struct FuzzTarget { + const char *name; /* target identifier (passed to --fuzz-target=)*/ + const char *description; /* help text */ + + + /* + * returns the arg-list that is passed to qemu/softmmu init() + * Cannot be NULL + */ + const char* (*get_init_cmdline)(struct FuzzTarget *); + + /* + * will run once, prior to running qemu/softmmu init. + * eg: set up shared-memory for communication with the child-process + * Can be NULL + */ + void(*pre_vm_init)(void); + + /* + * will run once, after QEMU has been initialized, prior to the fuzz-loop. + * eg: detect the memory map + * Can be NULL + */ + void(*pre_fuzz)(QTestState *); + + /* + * accepts and executes an input from libfuzzer. this is repeatedly + * executed during the fuzzing loop. Its should handle setup, input + * execution and cleanup. + * Cannot be NULL + */ + void(*fuzz)(QTestState *, const unsigned char *, size_t); + +} FuzzTarget; + +void flush_events(QTestState *); +void reboot(QTestState *); + +/* + * makes a copy of *target and adds it to the target-list. + * i.e. fine to set up target on the caller's stack + */ +void fuzz_add_target(const FuzzTarget *target); + +int LLVMFuzzerTestOneInput(const unsigned char *Data, size_t Size); +int LLVMFuzzerInitialize(int *argc, char ***argv, char ***envp); + +#endif + diff --git a/tests/qtest/fuzz/i440fx_fuzz.c b/tests/qtest/fuzz/i440fx_fuzz.c new file mode 100644 index 0000000000..ab5f112584 --- /dev/null +++ b/tests/qtest/fuzz/i440fx_fuzz.c @@ -0,0 +1,193 @@ +/* + * I440FX Fuzzing Target + * + * Copyright Red Hat Inc., 2019 + * + * Authors: + * Alexander Bulekov + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" + +#include "qemu/main-loop.h" +#include "tests/qtest/libqtest.h" +#include "tests/qtest/libqos/pci.h" +#include "tests/qtest/libqos/pci-pc.h" +#include "fuzz.h" +#include "fuzz/qos_fuzz.h" +#include "fuzz/fork_fuzz.h" + + +#define I440FX_PCI_HOST_BRIDGE_CFG 0xcf8 +#define I440FX_PCI_HOST_BRIDGE_DATA 0xcfc + +/* + * the input to the fuzzing functions below is a buffer of random bytes. we + * want to convert these bytes into a sequence of qtest or qos calls. to do + * this we define some opcodes: + */ +enum action_id { + WRITEB, + WRITEW, + WRITEL, + READB, + READW, + READL, + ACTION_MAX +}; + +static void i440fx_fuzz_qtest(QTestState *s, + const unsigned char *Data, size_t Size) { + /* + * loop over the Data, breaking it up into actions. each action has an + * opcode, address offset and value + */ + typedef struct QTestFuzzAction { + uint8_t opcode; + uint8_t addr; + uint32_t value; + } QTestFuzzAction; + QTestFuzzAction a; + + while (Size >= sizeof(a)) { + /* make a copy of the action so we can normalize the values in-place */ + memcpy(&a, Data, sizeof(a)); + /* select between two i440fx Port IO addresses */ + uint16_t addr = a.addr % 2 ? I440FX_PCI_HOST_BRIDGE_CFG : + I440FX_PCI_HOST_BRIDGE_DATA; + switch (a.opcode % ACTION_MAX) { + case WRITEB: + qtest_outb(s, addr, (uint8_t)a.value); + break; + case WRITEW: + qtest_outw(s, addr, (uint16_t)a.value); + break; + case WRITEL: + qtest_outl(s, addr, (uint32_t)a.value); + break; + case READB: + qtest_inb(s, addr); + break; + case READW: + qtest_inw(s, addr); + break; + case READL: + qtest_inl(s, addr); + break; + } + /* Move to the next operation */ + Size -= sizeof(a); + Data += sizeof(a); + } + flush_events(s); +} + +static void i440fx_fuzz_qos(QTestState *s, + const unsigned char *Data, size_t Size) { + /* + * Same as i440fx_fuzz_qtest, but using QOS. devfn is incorporated into the + * value written over Port IO + */ + typedef struct QOSFuzzAction { + uint8_t opcode; + uint8_t offset; + int devfn; + uint32_t value; + } QOSFuzzAction; + + static QPCIBus *bus; + if (!bus) { + bus = qpci_new_pc(s, fuzz_qos_alloc); + } + + QOSFuzzAction a; + while (Size >= sizeof(a)) { + memcpy(&a, Data, sizeof(a)); + switch (a.opcode % ACTION_MAX) { + case WRITEB: + bus->config_writeb(bus, a.devfn, a.offset, (uint8_t)a.value); + break; + case WRITEW: + bus->config_writew(bus, a.devfn, a.offset, (uint16_t)a.value); + break; + case WRITEL: + bus->config_writel(bus, a.devfn, a.offset, (uint32_t)a.value); + break; + case READB: + bus->config_readb(bus, a.devfn, a.offset); + break; + case READW: + bus->config_readw(bus, a.devfn, a.offset); + break; + case READL: + bus->config_readl(bus, a.devfn, a.offset); + break; + } + Size -= sizeof(a); + Data += sizeof(a); + } + flush_events(s); +} + +static void i440fx_fuzz_qos_fork(QTestState *s, + const unsigned char *Data, size_t Size) { + if (fork() == 0) { + i440fx_fuzz_qos(s, Data, Size); + _Exit(0); + } else { + wait(NULL); + } +} + +static const char *i440fx_qtest_argv = TARGET_NAME " -machine accel=qtest" + "-m 0 -display none"; +static const char *i440fx_argv(FuzzTarget *t) +{ + return i440fx_qtest_argv; +} + +static void fork_init(void) +{ + counter_shm_init(); +} + +static void register_pci_fuzz_targets(void) +{ + /* Uses simple qtest commands and reboots to reset state */ + fuzz_add_target(&(FuzzTarget){ + .name = "i440fx-qtest-reboot-fuzz", + .description = "Fuzz the i440fx using raw qtest commands and" + "rebooting after each run", + .get_init_cmdline = i440fx_argv, + .fuzz = i440fx_fuzz_qtest}); + + /* Uses libqos and forks to prevent state leakage */ + fuzz_add_qos_target(&(FuzzTarget){ + .name = "i440fx-qos-fork-fuzz", + .description = "Fuzz the i440fx using raw qtest commands and" + "rebooting after each run", + .pre_vm_init = &fork_init, + .fuzz = i440fx_fuzz_qos_fork,}, + "i440FX-pcihost", + &(QOSGraphTestOptions){} + ); + + /* + * Uses libqos. Doesn't do anything to reset state. Note that if we were to + * reboot after each run, we would also have to redo the qos-related + * initialization (qos_init_path) + */ + fuzz_add_qos_target(&(FuzzTarget){ + .name = "i440fx-qos-noreset-fuzz", + .description = "Fuzz the i440fx using raw qtest commands and" + "rebooting after each run", + .fuzz = i440fx_fuzz_qos,}, + "i440FX-pcihost", + &(QOSGraphTestOptions){} + ); +} + +fuzz_target_init(register_pci_fuzz_targets); diff --git a/tests/qtest/fuzz/qos_fuzz.c b/tests/qtest/fuzz/qos_fuzz.c new file mode 100644 index 0000000000..af28c92866 --- /dev/null +++ b/tests/qtest/fuzz/qos_fuzz.c @@ -0,0 +1,234 @@ +/* + * QOS-assisted fuzzing helpers + * + * Copyright (c) 2018 Emanuele Giuseppe Esposito + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + */ + +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "exec/memory.h" +#include "exec/address-spaces.h" +#include "sysemu/sysemu.h" +#include "qemu/main-loop.h" + +#include "tests/qtest/libqtest.h" +#include "tests/qtest/libqos/malloc.h" +#include "tests/qtest/libqos/qgraph.h" +#include "tests/qtest/libqos/qgraph_internal.h" +#include "tests/qtest/libqos/qos_external.h" + +#include "fuzz.h" +#include "qos_fuzz.h" + +#include "qapi/qapi-commands-machine.h" +#include "qapi/qapi-commands-qom.h" +#include "qapi/qmp/qlist.h" + + +void *fuzz_qos_obj; +QGuestAllocator *fuzz_qos_alloc; + +static const char *fuzz_target_name; +static char **fuzz_path_vec; + +/* + * Replaced the qmp commands with direct qmp_marshal calls. + * Probably there is a better way to do this + */ +static void qos_set_machines_devices_available(void) +{ + QDict *req = qdict_new(); + QObject *response; + QDict *args = qdict_new(); + QList *lst; + + qmp_marshal_query_machines(NULL, &response, &error_abort); + lst = qobject_to(QList, response); + apply_to_qlist(lst, true); + + qobject_unref(response); + + + qdict_put_str(req, "execute", "qom-list-types"); + qdict_put_str(args, "implements", "device"); + qdict_put_bool(args, "abstract", true); + qdict_put_obj(req, "arguments", (QObject *) args); + + qmp_marshal_qom_list_types(args, &response, &error_abort); + lst = qobject_to(QList, response); + apply_to_qlist(lst, false); + qobject_unref(response); + qobject_unref(req); +} + +static char **current_path; + +void *qos_allocate_objects(QTestState *qts, QGuestAllocator **p_alloc) +{ + return allocate_objects(qts, current_path + 1, p_alloc); +} + +static const char *qos_build_main_args(void) +{ + char **path = fuzz_path_vec; + QOSGraphNode *test_node; + GString *cmd_line = g_string_new(path[0]); + void *test_arg; + + if (!path) { + fprintf(stderr, "QOS Path not found\n"); + abort(); + } + + /* Before test */ + current_path = path; + test_node = qos_graph_get_node(path[(g_strv_length(path) - 1)]); + test_arg = test_node->u.test.arg; + if (test_node->u.test.before) { + test_arg = test_node->u.test.before(cmd_line, test_arg); + } + /* Prepend the arguments that we need */ + g_string_prepend(cmd_line, + TARGET_NAME " -display none -machine accel=qtest -m 64 "); + return cmd_line->str; +} + +/* + * This function is largely a copy of qos-test.c:walk_path. Since walk_path + * is itself a callback, its a little annoying to add another argument/layer of + * indirection + */ +static void walk_path(QOSGraphNode *orig_path, int len) +{ + QOSGraphNode *path; + QOSGraphEdge *edge; + + /* + * etype set to QEDGE_CONSUMED_BY so that machine can add to the command + * line + */ + QOSEdgeType etype = QEDGE_CONSUMED_BY; + + /* twice QOS_PATH_MAX_ELEMENT_SIZE since each edge can have its arg */ + char **path_vec = g_new0(char *, (QOS_PATH_MAX_ELEMENT_SIZE * 2)); + int path_vec_size = 0; + + char *after_cmd, *before_cmd, *after_device; + GString *after_device_str = g_string_new(""); + char *node_name = orig_path->name, *path_str; + + GString *cmd_line = g_string_new(""); + GString *cmd_line2 = g_string_new(""); + + path = qos_graph_get_node(node_name); /* root */ + node_name = qos_graph_edge_get_dest(path->path_edge); /* machine name */ + + path_vec[path_vec_size++] = node_name; + path_vec[path_vec_size++] = qos_get_machine_type(node_name); + + for (;;) { + path = qos_graph_get_node(node_name); + if (!path->path_edge) { + break; + } + + node_name = qos_graph_edge_get_dest(path->path_edge); + + /* append node command line + previous edge command line */ + if (path->command_line && etype == QEDGE_CONSUMED_BY) { + g_string_append(cmd_line, path->command_line); + g_string_append(cmd_line, after_device_str->str); + g_string_truncate(after_device_str, 0); + } + + path_vec[path_vec_size++] = qos_graph_edge_get_name(path->path_edge); + /* detect if edge has command line args */ + after_cmd = qos_graph_edge_get_after_cmd_line(path->path_edge); + after_device = qos_graph_edge_get_extra_device_opts(path->path_edge); + before_cmd = qos_graph_edge_get_before_cmd_line(path->path_edge); + edge = qos_graph_get_edge(path->name, node_name); + etype = qos_graph_edge_get_type(edge); + + if (before_cmd) { + g_string_append(cmd_line, before_cmd); + } + if (after_cmd) { + g_string_append(cmd_line2, after_cmd); + } + if (after_device) { + g_string_append(after_device_str, after_device); + } + } + + path_vec[path_vec_size++] = NULL; + g_string_append(cmd_line, after_device_str->str); + g_string_free(after_device_str, true); + + g_string_append(cmd_line, cmd_line2->str); + g_string_free(cmd_line2, true); + + /* + * here position 0 has /, position 1 has . + * The path must not have the , qtest_add_data_func adds it. + */ + path_str = g_strjoinv("/", path_vec + 1); + + /* Check that this is the test we care about: */ + char *test_name = strrchr(path_str, '/') + 1; + if (strcmp(test_name, fuzz_target_name) == 0) { + /* + * put arch/machine in position 1 so run_one_test can do its work + * and add the command line at position 0. + */ + path_vec[1] = path_vec[0]; + path_vec[0] = g_string_free(cmd_line, false); + + fuzz_path_vec = path_vec; + } else { + g_free(path_vec); + } + + g_free(path_str); +} + +static const char *qos_get_cmdline(FuzzTarget *t) +{ + /* + * Set a global variable that we use to identify the qos_path for our + * fuzz_target + */ + fuzz_target_name = t->name; + qos_set_machines_devices_available(); + qos_graph_foreach_test_path(walk_path); + return qos_build_main_args(); +} + +void fuzz_add_qos_target( + FuzzTarget *fuzz_opts, + const char *interface, + QOSGraphTestOptions *opts + ) +{ + qos_add_test(fuzz_opts->name, interface, NULL, opts); + fuzz_opts->get_init_cmdline = qos_get_cmdline; + fuzz_add_target(fuzz_opts); +} + +void qos_init_path(QTestState *s) +{ + fuzz_qos_obj = qos_allocate_objects(s , &fuzz_qos_alloc); +} diff --git a/tests/qtest/fuzz/qos_fuzz.h b/tests/qtest/fuzz/qos_fuzz.h new file mode 100644 index 0000000000..477f11b02b --- /dev/null +++ b/tests/qtest/fuzz/qos_fuzz.h @@ -0,0 +1,33 @@ +/* + * QOS-assisted fuzzing helpers + * + * Copyright Red Hat Inc., 2019 + * + * Authors: + * Alexander Bulekov + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef _QOS_FUZZ_H_ +#define _QOS_FUZZ_H_ + +#include "tests/qtest/fuzz/fuzz.h" +#include "tests/qtest/libqos/qgraph.h" + +int qos_fuzz(const unsigned char *Data, size_t Size); +void qos_setup(void); + +extern void *fuzz_qos_obj; +extern QGuestAllocator *fuzz_qos_alloc; + +void fuzz_add_qos_target( + FuzzTarget *fuzz_opts, + const char *interface, + QOSGraphTestOptions *opts + ); + +void qos_init_path(QTestState *); + +#endif diff --git a/tests/qtest/fuzz/virtio_net_fuzz.c b/tests/qtest/fuzz/virtio_net_fuzz.c new file mode 100644 index 0000000000..d08a47e278 --- /dev/null +++ b/tests/qtest/fuzz/virtio_net_fuzz.c @@ -0,0 +1,198 @@ +/* + * virtio-net Fuzzing Target + * + * Copyright Red Hat Inc., 2019 + * + * Authors: + * Alexander Bulekov + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" + +#include "standard-headers/linux/virtio_config.h" +#include "tests/qtest/libqtest.h" +#include "tests/qtest/libqos/virtio-net.h" +#include "fuzz.h" +#include "fork_fuzz.h" +#include "qos_fuzz.h" + + +#define QVIRTIO_NET_TIMEOUT_US (30 * 1000 * 1000) +#define QVIRTIO_RX_VQ 0 +#define QVIRTIO_TX_VQ 1 +#define QVIRTIO_CTRL_VQ 2 + +static int sockfds[2]; +static bool sockfds_initialized; + +static void virtio_net_fuzz_multi(QTestState *s, + const unsigned char *Data, size_t Size, bool check_used) +{ + typedef struct vq_action { + uint8_t queue; + uint8_t length; + uint8_t write; + uint8_t next; + uint8_t rx; + } vq_action; + + uint32_t free_head = 0; + + QGuestAllocator *t_alloc = fuzz_qos_alloc; + + QVirtioNet *net_if = fuzz_qos_obj; + QVirtioDevice *dev = net_if->vdev; + QVirtQueue *q; + vq_action vqa; + while (Size >= sizeof(vqa)) { + memcpy(&vqa, Data, sizeof(vqa)); + Data += sizeof(vqa); + Size -= sizeof(vqa); + + q = net_if->queues[vqa.queue % 3]; + + vqa.length = vqa.length >= Size ? Size : vqa.length; + + /* + * Only attempt to write incoming packets, when using the socket + * backend. Otherwise, always place the input on a virtqueue. + */ + if (vqa.rx && sockfds_initialized) { + write(sockfds[0], Data, vqa.length); + } else { + vqa.rx = 0; + uint64_t req_addr = guest_alloc(t_alloc, vqa.length); + /* + * If checking used ring, ensure that the fuzzer doesn't trigger + * trivial asserion failure on zero-zied buffer + */ + qtest_memwrite(s, req_addr, Data, vqa.length); + + + free_head = qvirtqueue_add(s, q, req_addr, vqa.length, + vqa.write, vqa.next); + qvirtqueue_add(s, q, req_addr, vqa.length, vqa.write , vqa.next); + qvirtqueue_kick(s, dev, q, free_head); + } + + /* Run the main loop */ + qtest_clock_step(s, 100); + flush_events(s); + + /* Wait on used descriptors */ + if (check_used && !vqa.rx) { + gint64 start_time = g_get_monotonic_time(); + /* + * normally, we could just use qvirtio_wait_used_elem, but since we + * must manually run the main-loop for all the bhs to run, we use + * this hack with flush_events(), to run the main_loop + */ + while (!vqa.rx && q != net_if->queues[QVIRTIO_RX_VQ]) { + uint32_t got_desc_idx; + /* Input led to a virtio_error */ + if (dev->bus->get_status(dev) & VIRTIO_CONFIG_S_NEEDS_RESET) { + break; + } + if (dev->bus->get_queue_isr_status(dev, q) && + qvirtqueue_get_buf(s, q, &got_desc_idx, NULL)) { + g_assert_cmpint(got_desc_idx, ==, free_head); + break; + } + g_assert(g_get_monotonic_time() - start_time + <= QVIRTIO_NET_TIMEOUT_US); + + /* Run the main loop */ + qtest_clock_step(s, 100); + flush_events(s); + } + } + Data += vqa.length; + Size -= vqa.length; + } +} + +static void virtio_net_fork_fuzz(QTestState *s, + const unsigned char *Data, size_t Size) +{ + if (fork() == 0) { + virtio_net_fuzz_multi(s, Data, Size, false); + flush_events(s); + _Exit(0); + } else { + wait(NULL); + } +} + +static void virtio_net_fork_fuzz_check_used(QTestState *s, + const unsigned char *Data, size_t Size) +{ + if (fork() == 0) { + virtio_net_fuzz_multi(s, Data, Size, true); + flush_events(s); + _Exit(0); + } else { + wait(NULL); + } +} + +static void virtio_net_pre_fuzz(QTestState *s) +{ + qos_init_path(s); + counter_shm_init(); +} + +static void *virtio_net_test_setup_socket(GString *cmd_line, void *arg) +{ + int ret = socketpair(PF_UNIX, SOCK_STREAM, 0, sockfds); + g_assert_cmpint(ret, !=, -1); + fcntl(sockfds[0], F_SETFL, O_NONBLOCK); + sockfds_initialized = true; + g_string_append_printf(cmd_line, " -netdev socket,fd=%d,id=hs0 ", + sockfds[1]); + return arg; +} + +static void *virtio_net_test_setup_user(GString *cmd_line, void *arg) +{ + g_string_append_printf(cmd_line, " -netdev user,id=hs0 "); + return arg; +} + +static void register_virtio_net_fuzz_targets(void) +{ + fuzz_add_qos_target(&(FuzzTarget){ + .name = "virtio-net-socket", + .description = "Fuzz the virtio-net virtual queues. Fuzz incoming " + "traffic using the socket backend", + .pre_fuzz = &virtio_net_pre_fuzz, + .fuzz = virtio_net_fork_fuzz,}, + "virtio-net", + &(QOSGraphTestOptions){.before = virtio_net_test_setup_socket} + ); + + fuzz_add_qos_target(&(FuzzTarget){ + .name = "virtio-net-socket-check-used", + .description = "Fuzz the virtio-net virtual queues. Wait for the " + "descriptors to be used. Timeout may indicate improperly handled " + "input", + .pre_fuzz = &virtio_net_pre_fuzz, + .fuzz = virtio_net_fork_fuzz_check_used,}, + "virtio-net", + &(QOSGraphTestOptions){.before = virtio_net_test_setup_socket} + ); + fuzz_add_qos_target(&(FuzzTarget){ + .name = "virtio-net-slirp", + .description = "Fuzz the virtio-net virtual queues with the slirp " + " backend. Warning: May result in network traffic emitted from the " + " process. Run in an isolated network environment.", + .pre_fuzz = &virtio_net_pre_fuzz, + .fuzz = virtio_net_fork_fuzz,}, + "virtio-net", + &(QOSGraphTestOptions){.before = virtio_net_test_setup_user} + ); +} + +fuzz_target_init(register_virtio_net_fuzz_targets); diff --git a/tests/qtest/fuzz/virtio_scsi_fuzz.c b/tests/qtest/fuzz/virtio_scsi_fuzz.c new file mode 100644 index 0000000000..3b95247f12 --- /dev/null +++ b/tests/qtest/fuzz/virtio_scsi_fuzz.c @@ -0,0 +1,213 @@ +/* + * virtio-serial Fuzzing Target + * + * Copyright Red Hat Inc., 2019 + * + * Authors: + * Alexander Bulekov + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" + +#include "tests/qtest/libqtest.h" +#include "libqos/virtio-scsi.h" +#include "libqos/virtio.h" +#include "libqos/virtio-pci.h" +#include "standard-headers/linux/virtio_ids.h" +#include "standard-headers/linux/virtio_pci.h" +#include "standard-headers/linux/virtio_scsi.h" +#include "fuzz.h" +#include "fork_fuzz.h" +#include "qos_fuzz.h" + +#define PCI_SLOT 0x02 +#define PCI_FN 0x00 +#define QVIRTIO_SCSI_TIMEOUT_US (1 * 1000 * 1000) + +#define MAX_NUM_QUEUES 64 + +/* Based on tests/virtio-scsi-test.c */ +typedef struct { + int num_queues; + QVirtQueue *vq[MAX_NUM_QUEUES + 2]; +} QVirtioSCSIQueues; + +static QVirtioSCSIQueues *qvirtio_scsi_init(QVirtioDevice *dev, uint64_t mask) +{ + QVirtioSCSIQueues *vs; + uint64_t feat; + int i; + + vs = g_new0(QVirtioSCSIQueues, 1); + + feat = qvirtio_get_features(dev); + if (mask) { + feat &= ~QVIRTIO_F_BAD_FEATURE | mask; + } else { + feat &= ~(QVIRTIO_F_BAD_FEATURE | (1ull << VIRTIO_RING_F_EVENT_IDX)); + } + qvirtio_set_features(dev, feat); + + vs->num_queues = qvirtio_config_readl(dev, 0); + + for (i = 0; i < vs->num_queues + 2; i++) { + vs->vq[i] = qvirtqueue_setup(dev, fuzz_qos_alloc, i); + } + + qvirtio_set_driver_ok(dev); + + return vs; +} + +static void virtio_scsi_fuzz(QTestState *s, QVirtioSCSIQueues* queues, + const unsigned char *Data, size_t Size) +{ + /* + * Data is a sequence of random bytes. We split them up into "actions", + * followed by data: + * [vqa][dddddddd][vqa][dddd][vqa][dddddddddddd] ... + * The length of the data is specified by the preceding vqa.length + */ + typedef struct vq_action { + uint8_t queue; + uint8_t length; + uint8_t write; + uint8_t next; + uint8_t kick; + } vq_action; + + /* Keep track of the free head for each queue we interact with */ + bool vq_touched[MAX_NUM_QUEUES + 2] = {0}; + uint32_t free_head[MAX_NUM_QUEUES + 2]; + + QGuestAllocator *t_alloc = fuzz_qos_alloc; + + QVirtioSCSI *scsi = fuzz_qos_obj; + QVirtioDevice *dev = scsi->vdev; + QVirtQueue *q; + vq_action vqa; + while (Size >= sizeof(vqa)) { + /* Copy the action, so we can normalize length, queue and flags */ + memcpy(&vqa, Data, sizeof(vqa)); + + Data += sizeof(vqa); + Size -= sizeof(vqa); + + vqa.queue = vqa.queue % queues->num_queues; + /* Cap length at the number of remaining bytes in data */ + vqa.length = vqa.length >= Size ? Size : vqa.length; + vqa.write = vqa.write & 1; + vqa.next = vqa.next & 1; + vqa.kick = vqa.kick & 1; + + + q = queues->vq[vqa.queue]; + + /* Copy the data into ram, and place it on the virtqueue */ + uint64_t req_addr = guest_alloc(t_alloc, vqa.length); + qtest_memwrite(s, req_addr, Data, vqa.length); + if (vq_touched[vqa.queue] == 0) { + vq_touched[vqa.queue] = 1; + free_head[vqa.queue] = qvirtqueue_add(s, q, req_addr, vqa.length, + vqa.write, vqa.next); + } else { + qvirtqueue_add(s, q, req_addr, vqa.length, vqa.write , vqa.next); + } + + if (vqa.kick) { + qvirtqueue_kick(s, dev, q, free_head[vqa.queue]); + free_head[vqa.queue] = 0; + } + Data += vqa.length; + Size -= vqa.length; + } + /* In the end, kick each queue we interacted with */ + for (int i = 0; i < MAX_NUM_QUEUES + 2; i++) { + if (vq_touched[i]) { + qvirtqueue_kick(s, dev, queues->vq[i], free_head[i]); + } + } +} + +static void virtio_scsi_fork_fuzz(QTestState *s, + const unsigned char *Data, size_t Size) +{ + QVirtioSCSI *scsi = fuzz_qos_obj; + static QVirtioSCSIQueues *queues; + if (!queues) { + queues = qvirtio_scsi_init(scsi->vdev, 0); + } + if (fork() == 0) { + virtio_scsi_fuzz(s, queues, Data, Size); + flush_events(s); + _Exit(0); + } else { + wait(NULL); + } +} + +static void virtio_scsi_with_flag_fuzz(QTestState *s, + const unsigned char *Data, size_t Size) +{ + QVirtioSCSI *scsi = fuzz_qos_obj; + static QVirtioSCSIQueues *queues; + + if (fork() == 0) { + if (Size >= sizeof(uint64_t)) { + queues = qvirtio_scsi_init(scsi->vdev, *(uint64_t *)Data); + virtio_scsi_fuzz(s, queues, + Data + sizeof(uint64_t), Size - sizeof(uint64_t)); + flush_events(s); + } + _Exit(0); + } else { + wait(NULL); + } +} + +static void virtio_scsi_pre_fuzz(QTestState *s) +{ + qos_init_path(s); + counter_shm_init(); +} + +static void *virtio_scsi_test_setup(GString *cmd_line, void *arg) +{ + g_string_append(cmd_line, + " -drive file=blkdebug::null-co://," + "file.image.read-zeroes=on," + "if=none,id=dr1,format=raw,file.align=4k " + "-device scsi-hd,drive=dr1,lun=0,scsi-id=1"); + return arg; +} + + +static void register_virtio_scsi_fuzz_targets(void) +{ + fuzz_add_qos_target(&(FuzzTarget){ + .name = "virtio-scsi-fuzz", + .description = "Fuzz the virtio-scsi virtual queues, forking" + "for each fuzz run", + .pre_vm_init = &counter_shm_init, + .pre_fuzz = &virtio_scsi_pre_fuzz, + .fuzz = virtio_scsi_fork_fuzz,}, + "virtio-scsi", + &(QOSGraphTestOptions){.before = virtio_scsi_test_setup} + ); + + fuzz_add_qos_target(&(FuzzTarget){ + .name = "virtio-scsi-flags-fuzz", + .description = "Fuzz the virtio-scsi virtual queues, forking" + "for each fuzz run (also fuzzes the virtio flags)", + .pre_vm_init = &counter_shm_init, + .pre_fuzz = &virtio_scsi_pre_fuzz, + .fuzz = virtio_scsi_with_flag_fuzz,}, + "virtio-scsi", + &(QOSGraphTestOptions){.before = virtio_scsi_test_setup} + ); +} + +fuzz_target_init(register_virtio_scsi_fuzz_targets); diff --git a/tests/fw_cfg-test.c b/tests/qtest/fw_cfg-test.c similarity index 100% rename from tests/fw_cfg-test.c rename to tests/qtest/fw_cfg-test.c diff --git a/tests/hd-geo-test.c b/tests/qtest/hd-geo-test.c similarity index 98% rename from tests/hd-geo-test.c rename to tests/qtest/hd-geo-test.c index 7e86c5416c..48e8e02d6e 100644 --- a/tests/hd-geo-test.c +++ b/tests/qtest/hd-geo-test.c @@ -34,8 +34,13 @@ static char *create_test_img(int secs) fd = mkstemp(template); g_assert(fd >= 0); ret = ftruncate(fd, (off_t)secs * 512); - g_assert(ret == 0); close(fd); + + if (ret) { + free(template); + template = NULL; + } + return template; } @@ -416,7 +421,7 @@ static char *create_qcow2_with_mbr(MBRpartitions mbr, uint64_t sectors) char *raw_path = strdup(template); char *qcow2_path = strdup(template); char cmd[100 + 2 * PATH_MAX]; - uint8_t buf[512]; + uint8_t buf[512] = {}; int i, ret, fd, offset; uint64_t qcow2_size = sectors * 512; uint8_t status, parttype, head, sector, cyl; @@ -452,8 +457,8 @@ static char *create_qcow2_with_mbr(MBRpartitions mbr, uint64_t sectors) buf[offset + 0x6] = sector; buf[offset + 0x7] = cyl; - (*(uint32_t *)&buf[offset + 0x8]) = cpu_to_le32(mbr[i].start_sect); - (*(uint32_t *)&buf[offset + 0xc]) = cpu_to_le32(mbr[i].nr_sects); + stl_le_p(&buf[offset + 0x8], mbr[i].start_sect); + stl_le_p(&buf[offset + 0xc], mbr[i].nr_sects); offset += 0x10; } @@ -934,6 +939,10 @@ int main(int argc, char **argv) for (i = 0; i < backend_last; i++) { if (img_secs[i] >= 0) { img_file_name[i] = create_test_img(img_secs[i]); + if (!img_file_name[i]) { + g_test_message("Could not create test images."); + goto test_add_done; + } } else { img_file_name[i] = NULL; } @@ -965,6 +974,7 @@ int main(int argc, char **argv) "skipping hd-geo/override/* tests"); } +test_add_done: ret = g_test_run(); for (i = 0; i < backend_last; i++) { diff --git a/tests/hexloader-test.c b/tests/qtest/hexloader-test.c similarity index 100% rename from tests/hexloader-test.c rename to tests/qtest/hexloader-test.c diff --git a/tests/i440fx-test.c b/tests/qtest/i440fx-test.c similarity index 100% rename from tests/i440fx-test.c rename to tests/qtest/i440fx-test.c diff --git a/tests/i82801b11-test.c b/tests/qtest/i82801b11-test.c similarity index 100% rename from tests/i82801b11-test.c rename to tests/qtest/i82801b11-test.c diff --git a/tests/ide-test.c b/tests/qtest/ide-test.c similarity index 89% rename from tests/ide-test.c rename to tests/qtest/ide-test.c index 0277e7d5a9..5cfd97f915 100644 --- a/tests/ide-test.c +++ b/tests/qtest/ide-test.c @@ -445,104 +445,81 @@ static void test_bmdma_trim(void) test_bmdma_teardown(qts); } -static void test_bmdma_short_prdt(void) +/* + * This test is developed according to the Programming Interface for + * Bus Master IDE Controller (Revision 1.0 5/16/94) + */ +static void test_bmdma_various_prdts(void) { - QTestState *qts; - QPCIDevice *dev; - QPCIBar bmdma_bar, ide_bar; - uint8_t status; + int sectors = 0; + uint32_t size = 0; - PrdtEntry prdt[] = { - { - .addr = 0, - .size = cpu_to_le32(0x10 | PRDT_EOT), - }, - }; + for (sectors = 1; sectors <= 256; sectors *= 2) { + QTestState *qts = NULL; + QPCIDevice *dev = NULL; + QPCIBar bmdma_bar, ide_bar; - qts = test_bmdma_setup(); + qts = test_bmdma_setup(); + dev = get_pci_device(qts, &bmdma_bar, &ide_bar); - dev = get_pci_device(qts, &bmdma_bar, &ide_bar); + for (size = 0; size < 65536; size += 256) { + uint32_t req_size = sectors * 512; + uint32_t prd_size = size & 0xfffe; /* bit 0 is always set to 0 */ + uint8_t ret = 0; + uint8_t req_status = 0; + uint8_t abort_req_status = 0; + PrdtEntry prdt[] = { + { + .addr = 0, + .size = cpu_to_le32(size | PRDT_EOT), + }, + }; - /* Normal request */ - status = send_dma_request(qts, CMD_READ_DMA, 0, 1, - prdt, ARRAY_SIZE(prdt), NULL); - g_assert_cmphex(status, ==, 0); - assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR); + /* A value of zero in PRD size indicates 64K */ + if (prd_size == 0) { + prd_size = 65536; + } - /* Abort the request before it completes */ - status = send_dma_request(qts, CMD_READ_DMA | CMDF_ABORT, 0, 1, - prdt, ARRAY_SIZE(prdt), NULL); - g_assert_cmphex(status, ==, 0); - assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR); - free_pci_device(dev); - test_bmdma_teardown(qts); -} + /* + * 1. If PRDs specified a smaller size than the IDE transfer + * size, then the Interrupt and Active bits in the Controller + * status register are not set (Error Condition). + * + * 2. If the size of the physical memory regions was equal to + * the IDE device transfer size, the Interrupt bit in the + * Controller status register is set to 1, Active bit is set to 0. + * + * 3. If PRDs specified a larger size than the IDE transfer size, + * the Interrupt and Active bits in the Controller status register + * are both set to 1. + */ + if (prd_size < req_size) { + req_status = 0; + abort_req_status = 0; + } else if (prd_size == req_size) { + req_status = BM_STS_INTR; + abort_req_status = BM_STS_INTR; + } else { + req_status = BM_STS_ACTIVE | BM_STS_INTR; + abort_req_status = BM_STS_INTR; + } -static void test_bmdma_one_sector_short_prdt(void) -{ - QTestState *qts; - QPCIDevice *dev; - QPCIBar bmdma_bar, ide_bar; - uint8_t status; + /* Test the request */ + ret = send_dma_request(qts, CMD_READ_DMA, 0, sectors, + prdt, ARRAY_SIZE(prdt), NULL); + g_assert_cmphex(ret, ==, req_status); + assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR); - /* Read 2 sectors but only give 1 sector in PRDT */ - PrdtEntry prdt[] = { - { - .addr = 0, - .size = cpu_to_le32(0x200 | PRDT_EOT), - }, - }; + /* Now test aborting the same request */ + ret = send_dma_request(qts, CMD_READ_DMA | CMDF_ABORT, 0, + sectors, prdt, ARRAY_SIZE(prdt), NULL); + g_assert_cmphex(ret, ==, abort_req_status); + assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR); + } - qts = test_bmdma_setup(); - - dev = get_pci_device(qts, &bmdma_bar, &ide_bar); - - /* Normal request */ - status = send_dma_request(qts, CMD_READ_DMA, 0, 2, - prdt, ARRAY_SIZE(prdt), NULL); - g_assert_cmphex(status, ==, 0); - assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR); - - /* Abort the request before it completes */ - status = send_dma_request(qts, CMD_READ_DMA | CMDF_ABORT, 0, 2, - prdt, ARRAY_SIZE(prdt), NULL); - g_assert_cmphex(status, ==, 0); - assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR); - free_pci_device(dev); - test_bmdma_teardown(qts); -} - -static void test_bmdma_long_prdt(void) -{ - QTestState *qts; - QPCIDevice *dev; - QPCIBar bmdma_bar, ide_bar; - uint8_t status; - - PrdtEntry prdt[] = { - { - .addr = 0, - .size = cpu_to_le32(0x1000 | PRDT_EOT), - }, - }; - - qts = test_bmdma_setup(); - - dev = get_pci_device(qts, &bmdma_bar, &ide_bar); - - /* Normal request */ - status = send_dma_request(qts, CMD_READ_DMA, 0, 1, - prdt, ARRAY_SIZE(prdt), NULL); - g_assert_cmphex(status, ==, BM_STS_ACTIVE | BM_STS_INTR); - assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR); - - /* Abort the request before it completes */ - status = send_dma_request(qts, CMD_READ_DMA | CMDF_ABORT, 0, 1, - prdt, ARRAY_SIZE(prdt), NULL); - g_assert_cmphex(status, ==, BM_STS_INTR); - assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR); - free_pci_device(dev); - test_bmdma_teardown(qts); + free_pci_device(dev); + test_bmdma_teardown(qts); + } } static void test_bmdma_no_busmaster(void) @@ -1066,10 +1043,7 @@ int main(int argc, char **argv) qtest_add_func("/ide/bmdma/simple_rw", test_bmdma_simple_rw); qtest_add_func("/ide/bmdma/trim", test_bmdma_trim); - qtest_add_func("/ide/bmdma/short_prdt", test_bmdma_short_prdt); - qtest_add_func("/ide/bmdma/one_sector_short_prdt", - test_bmdma_one_sector_short_prdt); - qtest_add_func("/ide/bmdma/long_prdt", test_bmdma_long_prdt); + qtest_add_func("/ide/bmdma/various_prdts", test_bmdma_various_prdts); qtest_add_func("/ide/bmdma/no_busmaster", test_bmdma_no_busmaster); qtest_add_func("/ide/flush", test_flush); diff --git a/tests/intel-hda-test.c b/tests/qtest/intel-hda-test.c similarity index 100% rename from tests/intel-hda-test.c rename to tests/qtest/intel-hda-test.c diff --git a/tests/ioh3420-test.c b/tests/qtest/ioh3420-test.c similarity index 100% rename from tests/ioh3420-test.c rename to tests/qtest/ioh3420-test.c diff --git a/tests/ipmi-bt-test.c b/tests/qtest/ipmi-bt-test.c similarity index 100% rename from tests/ipmi-bt-test.c rename to tests/qtest/ipmi-bt-test.c diff --git a/tests/ipmi-kcs-test.c b/tests/qtest/ipmi-kcs-test.c similarity index 100% rename from tests/ipmi-kcs-test.c rename to tests/qtest/ipmi-kcs-test.c diff --git a/tests/ipoctal232-test.c b/tests/qtest/ipoctal232-test.c similarity index 100% rename from tests/ipoctal232-test.c rename to tests/qtest/ipoctal232-test.c diff --git a/tests/ivshmem-test.c b/tests/qtest/ivshmem-test.c similarity index 99% rename from tests/ivshmem-test.c rename to tests/qtest/ivshmem-test.c index be9aa92a61..ecda256472 100644 --- a/tests/ivshmem-test.c +++ b/tests/qtest/ivshmem-test.c @@ -443,7 +443,7 @@ static gchar *mktempshm(int size, int *fd) while (true) { gchar *name; - name = g_strdup_printf("/qtest-%u-%u", getpid(), g_random_int()); + name = g_strdup_printf("/qtest-%u-%u", getpid(), g_test_rand_int()); *fd = shm_open(name, O_CREAT|O_RDWR|O_EXCL, S_IRWXU|S_IRWXG|S_IRWXO); if (*fd > 0) { diff --git a/tests/libqos/aarch64-xlnx-zcu102-machine.c b/tests/qtest/libqos/aarch64-xlnx-zcu102-machine.c similarity index 100% rename from tests/libqos/aarch64-xlnx-zcu102-machine.c rename to tests/qtest/libqos/aarch64-xlnx-zcu102-machine.c diff --git a/tests/libqos/ahci.c b/tests/qtest/libqos/ahci.c similarity index 100% rename from tests/libqos/ahci.c rename to tests/qtest/libqos/ahci.c diff --git a/tests/libqos/ahci.h b/tests/qtest/libqos/ahci.h similarity index 99% rename from tests/libqos/ahci.h rename to tests/qtest/libqos/ahci.h index f05b3e5fce..44ab1104b5 100644 --- a/tests/libqos/ahci.h +++ b/tests/qtest/libqos/ahci.h @@ -351,7 +351,7 @@ typedef struct AHCIQState { typedef struct FIS { uint8_t fis_type; uint8_t flags; - char data[0]; + char data[]; } __attribute__((__packed__)) FIS; /** diff --git a/tests/libqos/arm-imx25-pdk-machine.c b/tests/qtest/libqos/arm-imx25-pdk-machine.c similarity index 100% rename from tests/libqos/arm-imx25-pdk-machine.c rename to tests/qtest/libqos/arm-imx25-pdk-machine.c diff --git a/tests/libqos/arm-n800-machine.c b/tests/qtest/libqos/arm-n800-machine.c similarity index 100% rename from tests/libqos/arm-n800-machine.c rename to tests/qtest/libqos/arm-n800-machine.c diff --git a/tests/libqos/arm-raspi2-machine.c b/tests/qtest/libqos/arm-raspi2-machine.c similarity index 100% rename from tests/libqos/arm-raspi2-machine.c rename to tests/qtest/libqos/arm-raspi2-machine.c diff --git a/tests/libqos/arm-sabrelite-machine.c b/tests/qtest/libqos/arm-sabrelite-machine.c similarity index 100% rename from tests/libqos/arm-sabrelite-machine.c rename to tests/qtest/libqos/arm-sabrelite-machine.c diff --git a/tests/libqos/arm-smdkc210-machine.c b/tests/qtest/libqos/arm-smdkc210-machine.c similarity index 100% rename from tests/libqos/arm-smdkc210-machine.c rename to tests/qtest/libqos/arm-smdkc210-machine.c diff --git a/tests/libqos/arm-virt-machine.c b/tests/qtest/libqos/arm-virt-machine.c similarity index 100% rename from tests/libqos/arm-virt-machine.c rename to tests/qtest/libqos/arm-virt-machine.c diff --git a/tests/libqos/arm-xilinx-zynq-a9-machine.c b/tests/qtest/libqos/arm-xilinx-zynq-a9-machine.c similarity index 100% rename from tests/libqos/arm-xilinx-zynq-a9-machine.c rename to tests/qtest/libqos/arm-xilinx-zynq-a9-machine.c diff --git a/tests/libqos/e1000e.c b/tests/qtest/libqos/e1000e.c similarity index 100% rename from tests/libqos/e1000e.c rename to tests/qtest/libqos/e1000e.c diff --git a/tests/libqos/e1000e.h b/tests/qtest/libqos/e1000e.h similarity index 100% rename from tests/libqos/e1000e.h rename to tests/qtest/libqos/e1000e.h diff --git a/tests/libqos/fw_cfg.c b/tests/qtest/libqos/fw_cfg.c similarity index 100% rename from tests/libqos/fw_cfg.c rename to tests/qtest/libqos/fw_cfg.c diff --git a/tests/libqos/fw_cfg.h b/tests/qtest/libqos/fw_cfg.h similarity index 96% rename from tests/libqos/fw_cfg.h rename to tests/qtest/libqos/fw_cfg.h index 13325cc4ff..c6a7cf8cf0 100644 --- a/tests/libqos/fw_cfg.h +++ b/tests/qtest/libqos/fw_cfg.h @@ -49,4 +49,6 @@ static inline void pc_fw_cfg_uninit(QFWCFG *fw_cfg) io_fw_cfg_uninit(fw_cfg); } +G_DEFINE_AUTOPTR_CLEANUP_FUNC(QFWCFG, mm_fw_cfg_uninit) + #endif diff --git a/tests/libqos/i2c-imx.c b/tests/qtest/libqos/i2c-imx.c similarity index 100% rename from tests/libqos/i2c-imx.c rename to tests/qtest/libqos/i2c-imx.c diff --git a/tests/libqos/i2c-omap.c b/tests/qtest/libqos/i2c-omap.c similarity index 100% rename from tests/libqos/i2c-omap.c rename to tests/qtest/libqos/i2c-omap.c diff --git a/tests/libqos/i2c.c b/tests/qtest/libqos/i2c.c similarity index 88% rename from tests/libqos/i2c.c rename to tests/qtest/libqos/i2c.c index 156114e745..38f800dbab 100644 --- a/tests/libqos/i2c.c +++ b/tests/qtest/libqos/i2c.c @@ -10,12 +10,12 @@ #include "libqos/i2c.h" #include "libqtest.h" -void i2c_send(QI2CDevice *i2cdev, const uint8_t *buf, uint16_t len) +void qi2c_send(QI2CDevice *i2cdev, const uint8_t *buf, uint16_t len) { i2cdev->bus->send(i2cdev->bus, i2cdev->addr, buf, len); } -void i2c_recv(QI2CDevice *i2cdev, uint8_t *buf, uint16_t len) +void qi2c_recv(QI2CDevice *i2cdev, uint8_t *buf, uint16_t len) { i2cdev->bus->recv(i2cdev->bus, i2cdev->addr, buf, len); } @@ -23,8 +23,8 @@ void i2c_recv(QI2CDevice *i2cdev, uint8_t *buf, uint16_t len) void i2c_read_block(QI2CDevice *i2cdev, uint8_t reg, uint8_t *buf, uint16_t len) { - i2c_send(i2cdev, ®, 1); - i2c_recv(i2cdev, buf, len); + qi2c_send(i2cdev, ®, 1); + qi2c_recv(i2cdev, buf, len); } void i2c_write_block(QI2CDevice *i2cdev, uint8_t reg, @@ -33,7 +33,7 @@ void i2c_write_block(QI2CDevice *i2cdev, uint8_t reg, uint8_t *cmd = g_malloc(len + 1); cmd[0] = reg; memcpy(&cmd[1], buf, len); - i2c_send(i2cdev, cmd, len + 1); + qi2c_send(i2cdev, cmd, len + 1); g_free(cmd); } diff --git a/tests/libqos/i2c.h b/tests/qtest/libqos/i2c.h similarity index 94% rename from tests/libqos/i2c.h rename to tests/qtest/libqos/i2c.h index 945b65b34c..c65f087834 100644 --- a/tests/libqos/i2c.h +++ b/tests/qtest/libqos/i2c.h @@ -47,8 +47,8 @@ struct QI2CDevice { void *i2c_device_create(void *i2c_bus, QGuestAllocator *alloc, void *addr); void add_qi2c_address(QOSGraphEdgeOptions *opts, QI2CAddress *addr); -void i2c_send(QI2CDevice *dev, const uint8_t *buf, uint16_t len); -void i2c_recv(QI2CDevice *dev, uint8_t *buf, uint16_t len); +void qi2c_send(QI2CDevice *dev, const uint8_t *buf, uint16_t len); +void qi2c_recv(QI2CDevice *dev, uint8_t *buf, uint16_t len); void i2c_read_block(QI2CDevice *dev, uint8_t reg, uint8_t *buf, uint16_t len); diff --git a/tests/libqos/libqos-pc.c b/tests/qtest/libqos/libqos-pc.c similarity index 100% rename from tests/libqos/libqos-pc.c rename to tests/qtest/libqos/libqos-pc.c diff --git a/tests/libqos/libqos-pc.h b/tests/qtest/libqos/libqos-pc.h similarity index 100% rename from tests/libqos/libqos-pc.h rename to tests/qtest/libqos/libqos-pc.h diff --git a/tests/libqos/libqos-spapr.c b/tests/qtest/libqos/libqos-spapr.c similarity index 100% rename from tests/libqos/libqos-spapr.c rename to tests/qtest/libqos/libqos-spapr.c diff --git a/tests/qtest/libqos/libqos-spapr.h b/tests/qtest/libqos/libqos-spapr.h new file mode 100644 index 0000000000..49bd72d20b --- /dev/null +++ b/tests/qtest/libqos/libqos-spapr.h @@ -0,0 +1,17 @@ +#ifndef LIBQOS_SPAPR_H +#define LIBQOS_SPAPR_H + +#include "libqos/libqos.h" + +QOSState *qtest_spapr_vboot(const char *cmdline_fmt, va_list ap); +QOSState *qtest_spapr_boot(const char *cmdline_fmt, ...); +void qtest_spapr_shutdown(QOSState *qs); + +/* List of capabilities needed to silence warnings with TCG */ +#define PSERIES_DEFAULT_CAPABILITIES \ + "cap-cfpc=broken," \ + "cap-sbbc=broken," \ + "cap-ibs=broken," \ + "cap-ccf-assist=off," + +#endif diff --git a/tests/libqos/libqos.c b/tests/qtest/libqos/libqos.c similarity index 100% rename from tests/libqos/libqos.c rename to tests/qtest/libqos/libqos.c diff --git a/tests/libqos/libqos.h b/tests/qtest/libqos/libqos.h similarity index 100% rename from tests/libqos/libqos.h rename to tests/qtest/libqos/libqos.h diff --git a/tests/libqos/malloc-pc.c b/tests/qtest/libqos/malloc-pc.c similarity index 100% rename from tests/libqos/malloc-pc.c rename to tests/qtest/libqos/malloc-pc.c diff --git a/tests/libqos/malloc-pc.h b/tests/qtest/libqos/malloc-pc.h similarity index 100% rename from tests/libqos/malloc-pc.h rename to tests/qtest/libqos/malloc-pc.h diff --git a/tests/libqos/malloc-spapr.c b/tests/qtest/libqos/malloc-spapr.c similarity index 100% rename from tests/libqos/malloc-spapr.c rename to tests/qtest/libqos/malloc-spapr.c diff --git a/tests/libqos/malloc-spapr.h b/tests/qtest/libqos/malloc-spapr.h similarity index 100% rename from tests/libqos/malloc-spapr.h rename to tests/qtest/libqos/malloc-spapr.h diff --git a/tests/libqos/malloc.c b/tests/qtest/libqos/malloc.c similarity index 100% rename from tests/libqos/malloc.c rename to tests/qtest/libqos/malloc.c diff --git a/tests/libqos/malloc.h b/tests/qtest/libqos/malloc.h similarity index 100% rename from tests/libqos/malloc.h rename to tests/qtest/libqos/malloc.h diff --git a/tests/libqos/pci-pc.c b/tests/qtest/libqos/pci-pc.c similarity index 100% rename from tests/libqos/pci-pc.c rename to tests/qtest/libqos/pci-pc.c diff --git a/tests/libqos/pci-pc.h b/tests/qtest/libqos/pci-pc.h similarity index 100% rename from tests/libqos/pci-pc.h rename to tests/qtest/libqos/pci-pc.h diff --git a/tests/libqos/pci-spapr.c b/tests/qtest/libqos/pci-spapr.c similarity index 100% rename from tests/libqos/pci-spapr.c rename to tests/qtest/libqos/pci-spapr.c diff --git a/tests/libqos/pci-spapr.h b/tests/qtest/libqos/pci-spapr.h similarity index 100% rename from tests/libqos/pci-spapr.h rename to tests/qtest/libqos/pci-spapr.h diff --git a/tests/libqos/pci.c b/tests/qtest/libqos/pci.c similarity index 100% rename from tests/libqos/pci.c rename to tests/qtest/libqos/pci.c diff --git a/tests/libqos/pci.h b/tests/qtest/libqos/pci.h similarity index 100% rename from tests/libqos/pci.h rename to tests/qtest/libqos/pci.h diff --git a/tests/libqos/ppc64_pseries-machine.c b/tests/qtest/libqos/ppc64_pseries-machine.c similarity index 100% rename from tests/libqos/ppc64_pseries-machine.c rename to tests/qtest/libqos/ppc64_pseries-machine.c diff --git a/tests/libqos/qgraph.c b/tests/qtest/libqos/qgraph.c similarity index 99% rename from tests/libqos/qgraph.c rename to tests/qtest/libqos/qgraph.c index 7a7ae2a19e..ca01de0743 100644 --- a/tests/libqos/qgraph.c +++ b/tests/qtest/libqos/qgraph.c @@ -474,7 +474,7 @@ QOSEdgeType qos_graph_edge_get_type(QOSGraphEdge *edge) if (!edge) { return -1; } - return edge->type;; + return edge->type; } char *qos_graph_edge_get_dest(QOSGraphEdge *edge) @@ -590,7 +590,7 @@ void qos_add_test(const char *name, const char *interface, QOSTestFunc test_func, QOSGraphTestOptions *opts) { QOSGraphNode *node; - char *test_name = g_strdup_printf("%s-tests/%s", interface, name);; + char *test_name = g_strdup_printf("%s-tests/%s", interface, name); QOSGraphTestOptions def_opts = { }; if (!opts) { diff --git a/tests/libqos/qgraph.h b/tests/qtest/libqos/qgraph.h similarity index 100% rename from tests/libqos/qgraph.h rename to tests/qtest/libqos/qgraph.h diff --git a/tests/libqos/qgraph_internal.h b/tests/qtest/libqos/qgraph_internal.h similarity index 100% rename from tests/libqos/qgraph_internal.h rename to tests/qtest/libqos/qgraph_internal.h diff --git a/tests/qtest/libqos/qos_external.c b/tests/qtest/libqos/qos_external.c new file mode 100644 index 0000000000..398556dde0 --- /dev/null +++ b/tests/qtest/libqos/qos_external.c @@ -0,0 +1,168 @@ +/* + * libqos driver framework + * + * Copyright (c) 2018 Emanuele Giuseppe Esposito + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + */ + +#include "qemu/osdep.h" +#include +#include "libqtest.h" +#include "qapi/qmp/qdict.h" +#include "qapi/qmp/qbool.h" +#include "qapi/qmp/qstring.h" +#include "qemu/module.h" +#include "qapi/qmp/qlist.h" +#include "libqos/malloc.h" +#include "libqos/qgraph.h" +#include "libqos/qgraph_internal.h" +#include "libqos/qos_external.h" + + + +void apply_to_node(const char *name, bool is_machine, bool is_abstract) +{ + char *machine_name = NULL; + if (is_machine) { + const char *arch = qtest_get_arch(); + machine_name = g_strconcat(arch, "/", name, NULL); + name = machine_name; + } + qos_graph_node_set_availability(name, true); + if (is_abstract) { + qos_delete_cmd_line(name); + } + g_free(machine_name); +} + +/** + * apply_to_qlist(): using QMP queries QEMU for a list of + * machines and devices available, and sets the respective node + * as true. If a node is found, also all its produced and contained + * child are marked available. + * + * See qos_graph_node_set_availability() for more info + */ +void apply_to_qlist(QList *list, bool is_machine) +{ + const QListEntry *p; + const char *name; + bool abstract; + QDict *minfo; + QObject *qobj; + QString *qstr; + QBool *qbool; + + for (p = qlist_first(list); p; p = qlist_next(p)) { + minfo = qobject_to(QDict, qlist_entry_obj(p)); + qobj = qdict_get(minfo, "name"); + qstr = qobject_to(QString, qobj); + name = qstring_get_str(qstr); + + qobj = qdict_get(minfo, "abstract"); + if (qobj) { + qbool = qobject_to(QBool, qobj); + abstract = qbool_get_bool(qbool); + } else { + abstract = false; + } + + apply_to_node(name, is_machine, abstract); + qobj = qdict_get(minfo, "alias"); + if (qobj) { + qstr = qobject_to(QString, qobj); + name = qstring_get_str(qstr); + apply_to_node(name, is_machine, abstract); + } + } +} + +QGuestAllocator *get_machine_allocator(QOSGraphObject *obj) +{ + return obj->get_driver(obj, "memory"); +} + +/** + * allocate_objects(): given an array of nodes @arg, + * walks the path invoking all constructors and + * passing the corresponding parameter in order to + * continue the objects allocation. + * Once the test is reached, return the object it consumes. + * + * Since the machine and QEDGE_CONSUMED_BY nodes allocate + * memory in the constructor, g_test_queue_destroy is used so + * that after execution they can be safely free'd. (The test's + * ->before callback is also welcome to use g_test_queue_destroy). + * + * Note: as specified in walk_path() too, @arg is an array of + * char *, where arg[0] is a pointer to the command line + * string that will be used to properly start QEMU when executing + * the test, and the remaining elements represent the actual objects + * that will be allocated. + */ +void *allocate_objects(QTestState *qts, char **path, QGuestAllocator **p_alloc) +{ + int current = 0; + QGuestAllocator *alloc; + QOSGraphObject *parent = NULL; + QOSGraphEdge *edge; + QOSGraphNode *node; + void *edge_arg; + void *obj; + + node = qos_graph_get_node(path[current]); + g_assert(node->type == QNODE_MACHINE); + + obj = qos_machine_new(node, qts); + qos_object_queue_destroy(obj); + + alloc = get_machine_allocator(obj); + if (p_alloc) { + *p_alloc = alloc; + } + + for (;;) { + if (node->type != QNODE_INTERFACE) { + qos_object_start_hw(obj); + parent = obj; + } + + /* follow edge and get object for next node constructor */ + current++; + edge = qos_graph_get_edge(path[current - 1], path[current]); + node = qos_graph_get_node(path[current]); + + if (node->type == QNODE_TEST) { + g_assert(qos_graph_edge_get_type(edge) == QEDGE_CONSUMED_BY); + return obj; + } + + switch (qos_graph_edge_get_type(edge)) { + case QEDGE_PRODUCES: + obj = parent->get_driver(parent, path[current]); + break; + + case QEDGE_CONSUMED_BY: + edge_arg = qos_graph_edge_get_arg(edge); + obj = qos_driver_new(node, obj, alloc, edge_arg); + qos_object_queue_destroy(obj); + break; + + case QEDGE_CONTAINS: + obj = parent->get_device(parent, path[current]); + break; + } + } +} + diff --git a/tests/qtest/libqos/qos_external.h b/tests/qtest/libqos/qos_external.h new file mode 100644 index 0000000000..7b44930c55 --- /dev/null +++ b/tests/qtest/libqos/qos_external.h @@ -0,0 +1,28 @@ +/* + * libqos driver framework + * + * Copyright (c) 2018 Emanuele Giuseppe Esposito + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + */ + +#ifndef QOS_EXTERNAL_H +#define QOS_EXTERNAL_H +#include "libqos/qgraph.h" + +void apply_to_node(const char *name, bool is_machine, bool is_abstract); +void apply_to_qlist(QList *list, bool is_machine); +QGuestAllocator *get_machine_allocator(QOSGraphObject *obj); +void *allocate_objects(QTestState *qts, char **path, QGuestAllocator **p_alloc); + +#endif diff --git a/tests/libqos/rtas.c b/tests/qtest/libqos/rtas.c similarity index 100% rename from tests/libqos/rtas.c rename to tests/qtest/libqos/rtas.c diff --git a/tests/libqos/rtas.h b/tests/qtest/libqos/rtas.h similarity index 100% rename from tests/libqos/rtas.h rename to tests/qtest/libqos/rtas.h diff --git a/tests/libqos/sdhci.c b/tests/qtest/libqos/sdhci.c similarity index 100% rename from tests/libqos/sdhci.c rename to tests/qtest/libqos/sdhci.c diff --git a/tests/libqos/sdhci.h b/tests/qtest/libqos/sdhci.h similarity index 100% rename from tests/libqos/sdhci.h rename to tests/qtest/libqos/sdhci.h diff --git a/tests/libqos/tpci200.c b/tests/qtest/libqos/tpci200.c similarity index 100% rename from tests/libqos/tpci200.c rename to tests/qtest/libqos/tpci200.c diff --git a/tests/libqos/usb.c b/tests/qtest/libqos/usb.c similarity index 100% rename from tests/libqos/usb.c rename to tests/qtest/libqos/usb.c diff --git a/tests/libqos/usb.h b/tests/qtest/libqos/usb.h similarity index 100% rename from tests/libqos/usb.h rename to tests/qtest/libqos/usb.h diff --git a/tests/libqos/virtio-9p.c b/tests/qtest/libqos/virtio-9p.c similarity index 100% rename from tests/libqos/virtio-9p.c rename to tests/qtest/libqos/virtio-9p.c diff --git a/tests/libqos/virtio-9p.h b/tests/qtest/libqos/virtio-9p.h similarity index 100% rename from tests/libqos/virtio-9p.h rename to tests/qtest/libqos/virtio-9p.h diff --git a/tests/libqos/virtio-balloon.c b/tests/qtest/libqos/virtio-balloon.c similarity index 100% rename from tests/libqos/virtio-balloon.c rename to tests/qtest/libqos/virtio-balloon.c diff --git a/tests/libqos/virtio-balloon.h b/tests/qtest/libqos/virtio-balloon.h similarity index 100% rename from tests/libqos/virtio-balloon.h rename to tests/qtest/libqos/virtio-balloon.h diff --git a/tests/libqos/virtio-blk.c b/tests/qtest/libqos/virtio-blk.c similarity index 100% rename from tests/libqos/virtio-blk.c rename to tests/qtest/libqos/virtio-blk.c diff --git a/tests/libqos/virtio-blk.h b/tests/qtest/libqos/virtio-blk.h similarity index 100% rename from tests/libqos/virtio-blk.h rename to tests/qtest/libqos/virtio-blk.h diff --git a/tests/libqos/virtio-mmio.c b/tests/qtest/libqos/virtio-mmio.c similarity index 100% rename from tests/libqos/virtio-mmio.c rename to tests/qtest/libqos/virtio-mmio.c diff --git a/tests/libqos/virtio-mmio.h b/tests/qtest/libqos/virtio-mmio.h similarity index 100% rename from tests/libqos/virtio-mmio.h rename to tests/qtest/libqos/virtio-mmio.h diff --git a/tests/libqos/virtio-net.c b/tests/qtest/libqos/virtio-net.c similarity index 100% rename from tests/libqos/virtio-net.c rename to tests/qtest/libqos/virtio-net.c diff --git a/tests/libqos/virtio-net.h b/tests/qtest/libqos/virtio-net.h similarity index 100% rename from tests/libqos/virtio-net.h rename to tests/qtest/libqos/virtio-net.h diff --git a/tests/libqos/virtio-pci-modern.c b/tests/qtest/libqos/virtio-pci-modern.c similarity index 100% rename from tests/libqos/virtio-pci-modern.c rename to tests/qtest/libqos/virtio-pci-modern.c diff --git a/tests/libqos/virtio-pci-modern.h b/tests/qtest/libqos/virtio-pci-modern.h similarity index 100% rename from tests/libqos/virtio-pci-modern.h rename to tests/qtest/libqos/virtio-pci-modern.h diff --git a/tests/libqos/virtio-pci.c b/tests/qtest/libqos/virtio-pci.c similarity index 100% rename from tests/libqos/virtio-pci.c rename to tests/qtest/libqos/virtio-pci.c diff --git a/tests/libqos/virtio-pci.h b/tests/qtest/libqos/virtio-pci.h similarity index 100% rename from tests/libqos/virtio-pci.h rename to tests/qtest/libqos/virtio-pci.h diff --git a/tests/libqos/virtio-rng.c b/tests/qtest/libqos/virtio-rng.c similarity index 100% rename from tests/libqos/virtio-rng.c rename to tests/qtest/libqos/virtio-rng.c diff --git a/tests/libqos/virtio-rng.h b/tests/qtest/libqos/virtio-rng.h similarity index 100% rename from tests/libqos/virtio-rng.h rename to tests/qtest/libqos/virtio-rng.h diff --git a/tests/libqos/virtio-scsi.c b/tests/qtest/libqos/virtio-scsi.c similarity index 100% rename from tests/libqos/virtio-scsi.c rename to tests/qtest/libqos/virtio-scsi.c diff --git a/tests/libqos/virtio-scsi.h b/tests/qtest/libqos/virtio-scsi.h similarity index 100% rename from tests/libqos/virtio-scsi.h rename to tests/qtest/libqos/virtio-scsi.h diff --git a/tests/libqos/virtio-serial.c b/tests/qtest/libqos/virtio-serial.c similarity index 100% rename from tests/libqos/virtio-serial.c rename to tests/qtest/libqos/virtio-serial.c diff --git a/tests/libqos/virtio-serial.h b/tests/qtest/libqos/virtio-serial.h similarity index 100% rename from tests/libqos/virtio-serial.h rename to tests/qtest/libqos/virtio-serial.h diff --git a/tests/libqos/virtio.c b/tests/qtest/libqos/virtio.c similarity index 100% rename from tests/libqos/virtio.c rename to tests/qtest/libqos/virtio.c diff --git a/tests/libqos/virtio.h b/tests/qtest/libqos/virtio.h similarity index 100% rename from tests/libqos/virtio.h rename to tests/qtest/libqos/virtio.h diff --git a/tests/libqos/x86_64_pc-machine.c b/tests/qtest/libqos/x86_64_pc-machine.c similarity index 100% rename from tests/libqos/x86_64_pc-machine.c rename to tests/qtest/libqos/x86_64_pc-machine.c diff --git a/tests/libqtest-single.h b/tests/qtest/libqtest-single.h similarity index 100% rename from tests/libqtest-single.h rename to tests/qtest/libqtest-single.h diff --git a/tests/libqtest.c b/tests/qtest/libqtest.c similarity index 90% rename from tests/libqtest.c rename to tests/qtest/libqtest.c index 91e9cb220c..49075b55a1 100644 --- a/tests/libqtest.c +++ b/tests/qtest/libqtest.c @@ -35,6 +35,23 @@ #define SOCKET_TIMEOUT 50 #define SOCKET_MAX_FDS 16 + +typedef void (*QTestSendFn)(QTestState *s, const char *buf); +typedef void (*ExternalSendFn)(void *s, const char *buf); +typedef GString* (*QTestRecvFn)(QTestState *); + +typedef struct QTestClientTransportOps { + QTestSendFn send; /* for sending qtest commands */ + + /* + * use external_send to send qtest command strings through functions which + * do not accept a QTestState as the first parameter. + */ + ExternalSendFn external_send; + + QTestRecvFn recv_line; /* for receiving qtest command responses */ +} QTestTransportOps; + struct QTestState { int fd; @@ -45,6 +62,7 @@ struct QTestState bool big_endian; bool irq_level[MAX_IRQ]; GString *rx; + QTestTransportOps ops; }; static GHookList abrt_hooks; @@ -52,6 +70,14 @@ static struct sigaction sigact_old; static int qtest_query_target_endianness(QTestState *s); +static void qtest_client_socket_send(QTestState*, const char *buf); +static void socket_send(int fd, const char *buf, size_t size); + +static GString *qtest_client_socket_recv_line(QTestState *); + +static void qtest_client_set_tx_handler(QTestState *s, QTestSendFn send); +static void qtest_client_set_rx_handler(QTestState *s, QTestRecvFn recv); + static int init_socket(const char *socket_path) { struct sockaddr_un addr; @@ -234,6 +260,9 @@ QTestState *qtest_init_without_qmp_handshake(const char *extra_args) sock = init_socket(socket_path); qmpsock = init_socket(qmp_socket_path); + qtest_client_set_rx_handler(s, qtest_client_socket_recv_line); + qtest_client_set_tx_handler(s, qtest_client_socket_send); + qtest_add_abrt_handler(kill_qemu_hook_func, s); command = g_strdup_printf("exec %s " @@ -241,9 +270,9 @@ QTestState *qtest_init_without_qmp_handshake(const char *extra_args) "-qtest-log %s " "-chardev socket,path=%s,id=char0 " "-mon chardev=char0,mode=control " - "-accel qtest " "-display none " - "%s", qemu_binary, socket_path, + "%s" + " -accel qtest", qemu_binary, socket_path, getenv("QTEST_LOG") ? "/dev/fd/2" : "/dev/null", qmp_socket_path, extra_args ?: ""); @@ -254,7 +283,7 @@ QTestState *qtest_init_without_qmp_handshake(const char *extra_args) s->expected_status = 0; s->qemu_pid = fork(); if (s->qemu_pid == 0) { - setenv("QEMU_AUDIO_DRV", "none", true); + g_setenv("QEMU_AUDIO_DRV", "none", true); execlp("/bin/sh", "sh", "-c", command, NULL); exit(1); } @@ -379,13 +408,9 @@ static void socket_send(int fd, const char *buf, size_t size) } } -static void socket_sendf(int fd, const char *fmt, va_list ap) +static void qtest_client_socket_send(QTestState *s, const char *buf) { - gchar *str = g_strdup_vprintf(fmt, ap); - size_t size = strlen(str); - - socket_send(fd, str, size); - g_free(str); + socket_send(s->fd, buf, strlen(buf)); } static void GCC_FMT_ATTR(2, 3) qtest_sendf(QTestState *s, const char *fmt, ...) @@ -393,8 +418,11 @@ static void GCC_FMT_ATTR(2, 3) qtest_sendf(QTestState *s, const char *fmt, ...) va_list ap; va_start(ap, fmt); - socket_sendf(s->fd, fmt, ap); + gchar *str = g_strdup_vprintf(fmt, ap); va_end(ap); + + s->ops.send(s, str); + g_free(str); } /* Sends a message and file descriptors to the socket. @@ -431,7 +459,7 @@ static void socket_send_fds(int socket_fd, int *fds, size_t fds_num, g_assert_cmpint(ret, >, 0); } -static GString *qtest_recv_line(QTestState *s) +static GString *qtest_client_socket_recv_line(QTestState *s) { GString *line; size_t offset; @@ -468,7 +496,7 @@ static gchar **qtest_rsp(QTestState *s, int expected_args) int i; redo: - line = qtest_recv_line(s); + line = s->ops.recv_line(s); words = g_strsplit(line->str, " ", 0); g_string_free(line, TRUE); @@ -1058,8 +1086,8 @@ void qtest_bufwrite(QTestState *s, uint64_t addr, const void *data, size_t size) bdata = g_base64_encode(data, size); qtest_sendf(s, "b64write 0x%" PRIx64 " 0x%zx ", addr, size); - socket_send(s->fd, bdata, strlen(bdata)); - socket_send(s->fd, "\n", 1); + s->ops.send(s, bdata); + s->ops.send(s, "\n"); qtest_rsp(s, 0); g_free(bdata); } @@ -1337,3 +1365,72 @@ void qmp_assert_error_class(QDict *rsp, const char *class) qobject_unref(rsp); } + +static void qtest_client_set_tx_handler(QTestState *s, + QTestSendFn send) +{ + s->ops.send = send; +} +static void qtest_client_set_rx_handler(QTestState *s, QTestRecvFn recv) +{ + s->ops.recv_line = recv; +} +/* A type-safe wrapper for s->send() */ +static void send_wrapper(QTestState *s, const char *buf) +{ + s->ops.external_send(s, buf); +} + +static GString *qtest_client_inproc_recv_line(QTestState *s) +{ + GString *line; + size_t offset; + char *eol; + + eol = strchr(s->rx->str, '\n'); + offset = eol - s->rx->str; + line = g_string_new_len(s->rx->str, offset); + g_string_erase(s->rx, 0, offset + 1); + return line; +} + +QTestState *qtest_inproc_init(QTestState **s, bool log, const char* arch, + void (*send)(void*, const char*)) +{ + QTestState *qts; + qts = g_new0(QTestState, 1); + *s = qts; /* Expose qts early on, since the query endianness relies on it */ + qts->wstatus = 0; + for (int i = 0; i < MAX_IRQ; i++) { + qts->irq_level[i] = false; + } + + qtest_client_set_rx_handler(qts, qtest_client_inproc_recv_line); + + /* send() may not have a matching protoype, so use a type-safe wrapper */ + qts->ops.external_send = send; + qtest_client_set_tx_handler(qts, send_wrapper); + + qts->big_endian = qtest_query_target_endianness(qts); + + /* + * Set a dummy path for QTEST_QEMU_BINARY. Doesn't need to exist, but this + * way, qtest_get_arch works for inproc qtest. + */ + gchar *bin_path = g_strconcat("/qemu-system-", arch, NULL); + setenv("QTEST_QEMU_BINARY", bin_path, 0); + g_free(bin_path); + + return qts; +} + +void qtest_client_inproc_recv(void *opaque, const char *str) +{ + QTestState *qts = *(QTestState **)opaque; + + if (!qts->rx) { + qts->rx = g_string_new(NULL); + } + g_string_append(qts->rx, str); + return; +} diff --git a/tests/libqtest.h b/tests/qtest/libqtest.h similarity index 99% rename from tests/libqtest.h rename to tests/qtest/libqtest.h index c9e21e05b3..f5cf93c386 100644 --- a/tests/libqtest.h +++ b/tests/qtest/libqtest.h @@ -729,4 +729,8 @@ bool qtest_probe_child(QTestState *s); */ void qtest_set_expected_status(QTestState *s, int status); +QTestState *qtest_inproc_init(QTestState **s, bool log, const char* arch, + void (*send)(void*, const char*)); + +void qtest_client_inproc_recv(void *opaque, const char *str); #endif diff --git a/tests/m25p80-test.c b/tests/qtest/m25p80-test.c similarity index 100% rename from tests/m25p80-test.c rename to tests/qtest/m25p80-test.c diff --git a/tests/m48t59-test.c b/tests/qtest/m48t59-test.c similarity index 100% rename from tests/m48t59-test.c rename to tests/qtest/m48t59-test.c diff --git a/tests/machine-none-test.c b/tests/qtest/machine-none-test.c similarity index 99% rename from tests/machine-none-test.c rename to tests/qtest/machine-none-test.c index 5953d31755..8bb54a6360 100644 --- a/tests/machine-none-test.c +++ b/tests/qtest/machine-none-test.c @@ -56,6 +56,7 @@ static struct arch2cpu cpus_map[] = { { "hppa", "hppa" }, { "riscv64", "rv64gcsu-v1.10.0" }, { "riscv32", "rv32gcsu-v1.9.1" }, + { "rx", "rx62n" }, }; static const char *get_cpu_model_by_arch(const char *arch) diff --git a/tests/megasas-test.c b/tests/qtest/megasas-test.c similarity index 100% rename from tests/megasas-test.c rename to tests/qtest/megasas-test.c diff --git a/tests/microbit-test.c b/tests/qtest/microbit-test.c similarity index 100% rename from tests/microbit-test.c rename to tests/qtest/microbit-test.c diff --git a/tests/qtest/migration-helpers.c b/tests/qtest/migration-helpers.c new file mode 100644 index 0000000000..516093b39a --- /dev/null +++ b/tests/qtest/migration-helpers.c @@ -0,0 +1,167 @@ +/* + * QTest migration helpers + * + * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates + * based on the vhost-user-test.c that is: + * Copyright (c) 2014 Virtual Open Systems Sarl. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include "qapi/qmp/qjson.h" + +#include "migration-helpers.h" + +bool got_stop; + +static void stop_cb(void *opaque, const char *name, QDict *data) +{ + if (!strcmp(name, "STOP")) { + got_stop = true; + } +} + +/* + * Events can get in the way of responses we are actually waiting for. + */ +QDict *wait_command_fd(QTestState *who, int fd, const char *command, ...) +{ + va_list ap; + + va_start(ap, command); + qtest_qmp_vsend_fds(who, &fd, 1, command, ap); + va_end(ap); + + return qtest_qmp_receive_success(who, stop_cb, NULL); +} + +/* + * Events can get in the way of responses we are actually waiting for. + */ +QDict *wait_command(QTestState *who, const char *command, ...) +{ + va_list ap; + + va_start(ap, command); + qtest_qmp_vsend(who, command, ap); + va_end(ap); + + return qtest_qmp_receive_success(who, stop_cb, NULL); +} + +/* + * Send QMP command "migrate". + * Arguments are built from @fmt... (formatted like + * qobject_from_jsonf_nofail()) with "uri": @uri spliced in. + */ +void migrate_qmp(QTestState *who, const char *uri, const char *fmt, ...) +{ + va_list ap; + QDict *args, *rsp; + + va_start(ap, fmt); + args = qdict_from_vjsonf_nofail(fmt, ap); + va_end(ap); + + g_assert(!qdict_haskey(args, "uri")); + qdict_put_str(args, "uri", uri); + + rsp = qtest_qmp(who, "{ 'execute': 'migrate', 'arguments': %p}", args); + + g_assert(qdict_haskey(rsp, "return")); + qobject_unref(rsp); +} + +/* + * Note: caller is responsible to free the returned object via + * qobject_unref() after use + */ +QDict *migrate_query(QTestState *who) +{ + return wait_command(who, "{ 'execute': 'query-migrate' }"); +} + +/* + * Note: caller is responsible to free the returned object via + * g_free() after use + */ +static gchar *migrate_query_status(QTestState *who) +{ + QDict *rsp_return = migrate_query(who); + gchar *status = g_strdup(qdict_get_str(rsp_return, "status")); + + g_assert(status); + qobject_unref(rsp_return); + + return status; +} + +static bool check_migration_status(QTestState *who, const char *goal, + const char **ungoals) +{ + bool ready; + char *current_status; + const char **ungoal; + + current_status = migrate_query_status(who); + ready = strcmp(current_status, goal) == 0; + if (!ungoals) { + g_assert_cmpstr(current_status, !=, "failed"); + /* + * If looking for a state other than completed, + * completion of migration would cause the test to + * hang. + */ + if (strcmp(goal, "completed") != 0) { + g_assert_cmpstr(current_status, !=, "completed"); + } + } else { + for (ungoal = ungoals; *ungoal; ungoal++) { + g_assert_cmpstr(current_status, !=, *ungoal); + } + } + g_free(current_status); + return ready; +} + +void wait_for_migration_status(QTestState *who, + const char *goal, const char **ungoals) +{ + while (!check_migration_status(who, goal, ungoals)) { + usleep(1000); + } +} + +void wait_for_migration_complete(QTestState *who) +{ + wait_for_migration_status(who, "completed", NULL); +} + +void wait_for_migration_fail(QTestState *from, bool allow_active) +{ + QDict *rsp_return; + char *status; + bool failed; + + do { + status = migrate_query_status(from); + bool result = !strcmp(status, "setup") || !strcmp(status, "failed") || + (allow_active && !strcmp(status, "active")); + if (!result) { + fprintf(stderr, "%s: unexpected status status=%s allow_active=%d\n", + __func__, status, allow_active); + } + g_assert(result); + failed = !strcmp(status, "failed"); + g_free(status); + } while (!failed); + + /* Is the machine currently running? */ + rsp_return = wait_command(from, "{ 'execute': 'query-status' }"); + g_assert(qdict_haskey(rsp_return, "running")); + g_assert(qdict_get_bool(rsp_return, "running")); + qobject_unref(rsp_return); +} diff --git a/tests/qtest/migration-helpers.h b/tests/qtest/migration-helpers.h new file mode 100644 index 0000000000..a11808b3b7 --- /dev/null +++ b/tests/qtest/migration-helpers.h @@ -0,0 +1,37 @@ +/* + * QTest migration helpers + * + * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates + * based on the vhost-user-test.c that is: + * Copyright (c) 2014 Virtual Open Systems Sarl. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ +#ifndef MIGRATION_HELPERS_H_ +#define MIGRATION_HELPERS_H_ + +#include "libqtest.h" + +extern bool got_stop; + +GCC_FMT_ATTR(3, 4) +QDict *wait_command_fd(QTestState *who, int fd, const char *command, ...); + +GCC_FMT_ATTR(2, 3) +QDict *wait_command(QTestState *who, const char *command, ...); + +GCC_FMT_ATTR(3, 4) +void migrate_qmp(QTestState *who, const char *uri, const char *fmt, ...); + +QDict *migrate_query(QTestState *who); + +void wait_for_migration_status(QTestState *who, + const char *goal, const char **ungoals); + +void wait_for_migration_complete(QTestState *who); + +void wait_for_migration_fail(QTestState *from, bool allow_active); + +#endif /* MIGRATION_HELPERS_H_ */ diff --git a/tests/migration-test.c b/tests/qtest/migration-test.c similarity index 71% rename from tests/migration-test.c rename to tests/qtest/migration-test.c index ebd77a581a..2568c9529c 100644 --- a/tests/migration-test.c +++ b/tests/qtest/migration-test.c @@ -14,7 +14,6 @@ #include "libqtest.h" #include "qapi/qmp/qdict.h" -#include "qapi/qmp/qjson.h" #include "qemu/module.h" #include "qemu/option.h" #include "qemu/range.h" @@ -24,6 +23,7 @@ #include "qapi/qobject-input-visitor.h" #include "qapi/qobject-output-visitor.h" +#include "migration-helpers.h" #include "migration/migration-test.h" /* TODO actually test the results and get rid of this */ @@ -31,7 +31,6 @@ unsigned start_address; unsigned end_address; -bool got_stop; static bool uffd_feature_thread_id; #if defined(__linux__) @@ -157,67 +156,6 @@ static void wait_for_serial(const char *side) } while (true); } -static void stop_cb(void *opaque, const char *name, QDict *data) -{ - if (!strcmp(name, "STOP")) { - got_stop = true; - } -} - -/* - * Events can get in the way of responses we are actually waiting for. - */ -GCC_FMT_ATTR(3, 4) -static QDict *wait_command_fd(QTestState *who, int fd, const char *command, ...) -{ - va_list ap; - - va_start(ap, command); - qtest_qmp_vsend_fds(who, &fd, 1, command, ap); - va_end(ap); - - return qtest_qmp_receive_success(who, stop_cb, NULL); -} - -/* - * Events can get in the way of responses we are actually waiting for. - */ -GCC_FMT_ATTR(2, 3) -static QDict *wait_command(QTestState *who, const char *command, ...) -{ - va_list ap; - - va_start(ap, command); - qtest_qmp_vsend(who, command, ap); - va_end(ap); - - return qtest_qmp_receive_success(who, stop_cb, NULL); -} - -/* - * Note: caller is responsible to free the returned object via - * qobject_unref() after use - */ -static QDict *migrate_query(QTestState *who) -{ - return wait_command(who, "{ 'execute': 'query-migrate' }"); -} - -/* - * Note: caller is responsible to free the returned object via - * g_free() after use - */ -static gchar *migrate_query_status(QTestState *who) -{ - QDict *rsp_return = migrate_query(who); - gchar *status = g_strdup(qdict_get_str(rsp_return, "status")); - - g_assert(status); - qobject_unref(rsp_return); - - return status; -} - /* * It's tricky to use qemu's migration event capability with qtest, * events suddenly appearing confuse the qmp()/hmp() responses. @@ -265,48 +203,6 @@ static void read_blocktime(QTestState *who) qobject_unref(rsp_return); } -static bool check_migration_status(QTestState *who, const char *goal, - const char **ungoals) -{ - bool ready; - char *current_status; - const char **ungoal; - - current_status = migrate_query_status(who); - ready = strcmp(current_status, goal) == 0; - if (!ungoals) { - g_assert_cmpstr(current_status, !=, "failed"); - /* - * If looking for a state other than completed, - * completion of migration would cause the test to - * hang. - */ - if (strcmp(goal, "completed") != 0) { - g_assert_cmpstr(current_status, !=, "completed"); - } - } else { - for (ungoal = ungoals; *ungoal; ungoal++) { - g_assert_cmpstr(current_status, !=, *ungoal); - } - } - g_free(current_status); - return ready; -} - -static void wait_for_migration_status(QTestState *who, - const char *goal, - const char **ungoals) -{ - while (!check_migration_status(who, goal, ungoals)) { - usleep(1000); - } -} - -static void wait_for_migration_complete(QTestState *who) -{ - wait_for_migration_status(who, "completed", NULL); -} - static void wait_for_migration_pass(QTestState *who) { uint64_t initial_pass = get_migration_pass(who); @@ -380,13 +276,6 @@ static void cleanup(const char *filename) g_free(path); } -static char *get_shmem_opts(const char *mem_size, const char *shmem_path) -{ - return g_strdup_printf("-object memory-backend-file,id=mem0,size=%s" - ",mem-path=%s,share=on -numa node,memdev=mem0", - mem_size, shmem_path); -} - static char *SocketAddress_to_str(SocketAddress *addr) { switch (addr->type) { @@ -467,6 +356,42 @@ static void migrate_set_parameter_int(QTestState *who, const char *parameter, migrate_check_parameter_int(who, parameter, value); } +static char *migrate_get_parameter_str(QTestState *who, + const char *parameter) +{ + QDict *rsp; + char *result; + + rsp = wait_command(who, "{ 'execute': 'query-migrate-parameters' }"); + result = g_strdup(qdict_get_str(rsp, parameter)); + qobject_unref(rsp); + return result; +} + +static void migrate_check_parameter_str(QTestState *who, const char *parameter, + const char *value) +{ + char *result; + + result = migrate_get_parameter_str(who, parameter); + g_assert_cmpstr(result, ==, value); + g_free(result); +} + +static void migrate_set_parameter_str(QTestState *who, const char *parameter, + const char *value) +{ + QDict *rsp; + + rsp = qtest_qmp(who, + "{ 'execute': 'migrate-set-parameters'," + "'arguments': { %s: %s } }", + parameter, value); + g_assert(qdict_haskey(rsp, "return")); + qobject_unref(rsp); + migrate_check_parameter_str(who, parameter, value); +} + static void migrate_pause(QTestState *who) { QDict *rsp; @@ -498,6 +423,14 @@ static void migrate_recover(QTestState *who, const char *uri) qobject_unref(rsp); } +static void migrate_cancel(QTestState *who) +{ + QDict *rsp; + + rsp = wait_command(who, "{ 'execute': 'migrate_cancel' }"); + qobject_unref(rsp); +} + static void migrate_set_capability(QTestState *who, const char *capability, bool value) { @@ -513,30 +446,6 @@ static void migrate_set_capability(QTestState *who, const char *capability, qobject_unref(rsp); } -/* - * Send QMP command "migrate". - * Arguments are built from @fmt... (formatted like - * qobject_from_jsonf_nofail()) with "uri": @uri spliced in. - */ -GCC_FMT_ATTR(3, 4) -static void migrate(QTestState *who, const char *uri, const char *fmt, ...) -{ - va_list ap; - QDict *args, *rsp; - - va_start(ap, fmt); - args = qdict_from_vjsonf_nofail(fmt, ap); - va_end(ap); - - g_assert(!qdict_haskey(args, "uri")); - qdict_put_str(args, "uri", uri); - - rsp = qtest_qmp(who, "{ 'execute': 'migrate', 'arguments': %p}", args); - - g_assert(qdict_haskey(rsp, "return")); - qobject_unref(rsp); -} - static void migrate_postcopy_start(QTestState *from, QTestState *to) { QDict *rsp; @@ -551,27 +460,51 @@ static void migrate_postcopy_start(QTestState *from, QTestState *to) qtest_qmp_eventwait(to, "RESUME"); } -static int test_migrate_start(QTestState **from, QTestState **to, - const char *uri, bool hide_stderr, - bool use_shmem, const char *opts_src, - const char *opts_dst) +typedef struct { + bool hide_stderr; + bool use_shmem; + /* only launch the target process */ + bool only_target; + char *opts_source; + char *opts_target; +} MigrateStart; + +static MigrateStart *migrate_start_new(void) { - gchar *cmd_src, *cmd_dst; + MigrateStart *args = g_new0(MigrateStart, 1); + + args->opts_source = g_strdup(""); + args->opts_target = g_strdup(""); + return args; +} + +static void migrate_start_destroy(MigrateStart *args) +{ + g_free(args->opts_source); + g_free(args->opts_target); + g_free(args); +} + +static int test_migrate_start(QTestState **from, QTestState **to, + const char *uri, MigrateStart *args) +{ + gchar *arch_source, *arch_target; + gchar *cmd_source, *cmd_target; + const gchar *ignore_stderr; char *bootpath = NULL; - char *extra_opts = NULL; - char *shmem_path = NULL; + char *shmem_opts; + char *shmem_path; const char *arch = qtest_get_arch(); - const char *accel = "kvm:tcg"; + const char *machine_opts = NULL; + const char *memory_size; + int ret = 0; - opts_src = opts_src ? opts_src : ""; - opts_dst = opts_dst ? opts_dst : ""; - - if (use_shmem) { + if (args->use_shmem) { if (!g_file_test("/dev/shm", G_FILE_TEST_IS_DIR)) { g_test_skip("/dev/shm is not supported"); - return -1; + ret = -1; + goto out; } - shmem_path = g_strdup_printf("/dev/shm/qemu-%d", getpid()); } got_stop = false; @@ -580,75 +513,37 @@ static int test_migrate_start(QTestState **from, QTestState **to, /* the assembled x86 boot sector should be exactly one sector large */ assert(sizeof(x86_bootsect) == 512); init_bootfile(bootpath, x86_bootsect, sizeof(x86_bootsect)); - extra_opts = use_shmem ? get_shmem_opts("150M", shmem_path) : NULL; - cmd_src = g_strdup_printf("-machine accel=%s -m 150M" - " -name source,debug-threads=on" - " -serial file:%s/src_serial" - " -drive file=%s,format=raw %s %s", - accel, tmpfs, bootpath, - extra_opts ? extra_opts : "", opts_src); - cmd_dst = g_strdup_printf("-machine accel=%s -m 150M" - " -name target,debug-threads=on" - " -serial file:%s/dest_serial" - " -drive file=%s,format=raw" - " -incoming %s %s %s", - accel, tmpfs, bootpath, uri, - extra_opts ? extra_opts : "", opts_dst); + memory_size = "150M"; + arch_source = g_strdup_printf("-drive file=%s,format=raw", bootpath); + arch_target = g_strdup(arch_source); start_address = X86_TEST_MEM_START; end_address = X86_TEST_MEM_END; } else if (g_str_equal(arch, "s390x")) { init_bootfile(bootpath, s390x_elf, sizeof(s390x_elf)); - extra_opts = use_shmem ? get_shmem_opts("128M", shmem_path) : NULL; - cmd_src = g_strdup_printf("-machine accel=%s -m 128M" - " -name source,debug-threads=on" - " -serial file:%s/src_serial -bios %s %s %s", - accel, tmpfs, bootpath, - extra_opts ? extra_opts : "", opts_src); - cmd_dst = g_strdup_printf("-machine accel=%s -m 128M" - " -name target,debug-threads=on" - " -serial file:%s/dest_serial -bios %s" - " -incoming %s %s %s", - accel, tmpfs, bootpath, uri, - extra_opts ? extra_opts : "", opts_dst); + memory_size = "128M"; + arch_source = g_strdup_printf("-bios %s", bootpath); + arch_target = g_strdup(arch_source); start_address = S390_TEST_MEM_START; end_address = S390_TEST_MEM_END; } else if (strcmp(arch, "ppc64") == 0) { - extra_opts = use_shmem ? get_shmem_opts("256M", shmem_path) : NULL; - cmd_src = g_strdup_printf("-machine accel=%s,vsmt=8 -m 256M -nodefaults" - " -name source,debug-threads=on" - " -serial file:%s/src_serial" - " -prom-env 'use-nvramrc?=true' -prom-env " - "'nvramrc=hex .\" _\" begin %x %x " - "do i c@ 1 + i c! 1000 +loop .\" B\" 0 " - "until' %s %s", accel, tmpfs, end_address, - start_address, extra_opts ? extra_opts : "", - opts_src); - cmd_dst = g_strdup_printf("-machine accel=%s,vsmt=8 -m 256M" - " -name target,debug-threads=on" - " -serial file:%s/dest_serial" - " -incoming %s %s %s", - accel, tmpfs, uri, - extra_opts ? extra_opts : "", opts_dst); - + machine_opts = "vsmt=8"; + memory_size = "256M"; start_address = PPC_TEST_MEM_START; end_address = PPC_TEST_MEM_END; + arch_source = g_strdup_printf("-nodefaults " + "-prom-env 'use-nvramrc?=true' -prom-env " + "'nvramrc=hex .\" _\" begin %x %x " + "do i c@ 1 + i c! 1000 +loop .\" B\" 0 " + "until'", end_address, start_address); + arch_target = g_strdup(""); } else if (strcmp(arch, "aarch64") == 0) { init_bootfile(bootpath, aarch64_kernel, sizeof(aarch64_kernel)); - extra_opts = use_shmem ? get_shmem_opts("150M", shmem_path) : NULL; - cmd_src = g_strdup_printf("-machine virt,accel=%s,gic-version=max " - "-name vmsource,debug-threads=on -cpu max " - "-m 150M -serial file:%s/src_serial " - "-kernel %s %s %s", - accel, tmpfs, bootpath, - extra_opts ? extra_opts : "", opts_src); - cmd_dst = g_strdup_printf("-machine virt,accel=%s,gic-version=max " - "-name vmdest,debug-threads=on -cpu max " - "-m 150M -serial file:%s/dest_serial " - "-kernel %s " - "-incoming %s %s %s", - accel, tmpfs, bootpath, uri, - extra_opts ? extra_opts : "", opts_dst); - + machine_opts = "virt,gic-version=max"; + memory_size = "150M"; + arch_source = g_strdup_printf("-cpu max " + "-kernel %s", + bootpath); + arch_target = g_strdup(arch_source); start_address = ARM_TEST_MEM_START; end_address = ARM_TEST_MEM_END; @@ -658,35 +553,68 @@ static int test_migrate_start(QTestState **from, QTestState **to, } g_free(bootpath); - g_free(extra_opts); - if (hide_stderr) { - gchar *tmp; - tmp = g_strdup_printf("%s 2>/dev/null", cmd_src); - g_free(cmd_src); - cmd_src = tmp; - - tmp = g_strdup_printf("%s 2>/dev/null", cmd_dst); - g_free(cmd_dst); - cmd_dst = tmp; + if (args->hide_stderr) { + ignore_stderr = "2>/dev/null"; + } else { + ignore_stderr = ""; } - *from = qtest_init(cmd_src); - g_free(cmd_src); + if (args->use_shmem) { + shmem_path = g_strdup_printf("/dev/shm/qemu-%d", getpid()); + shmem_opts = g_strdup_printf( + "-object memory-backend-file,id=mem0,size=%s" + ",mem-path=%s,share=on -numa node,memdev=mem0", + memory_size, shmem_path); + } else { + shmem_path = NULL; + shmem_opts = g_strdup(""); + } - *to = qtest_init(cmd_dst); - g_free(cmd_dst); + cmd_source = g_strdup_printf("-accel kvm -accel tcg%s%s " + "-name source,debug-threads=on " + "-m %s " + "-serial file:%s/src_serial " + "%s %s %s %s", + machine_opts ? " -machine " : "", + machine_opts ? machine_opts : "", + memory_size, tmpfs, + arch_source, shmem_opts, args->opts_source, + ignore_stderr); + g_free(arch_source); + if (!args->only_target) { + *from = qtest_init(cmd_source); + } + g_free(cmd_source); + cmd_target = g_strdup_printf("-accel kvm -accel tcg%s%s " + "-name target,debug-threads=on " + "-m %s " + "-serial file:%s/dest_serial " + "-incoming %s " + "%s %s %s %s", + machine_opts ? " -machine " : "", + machine_opts ? machine_opts : "", + memory_size, tmpfs, uri, + arch_target, shmem_opts, + args->opts_target, ignore_stderr); + g_free(arch_target); + *to = qtest_init(cmd_target); + g_free(cmd_target); + + g_free(shmem_opts); /* * Remove shmem file immediately to avoid memory leak in test failed case. * It's valid becase QEMU has already opened this file */ - if (use_shmem) { + if (args->use_shmem) { unlink(shmem_path); g_free(shmem_path); } - return 0; +out: + migrate_start_destroy(args); + return ret; } static void test_migrate_end(QTestState *from, QTestState *to, bool test_dest) @@ -771,13 +699,13 @@ static void test_deprecated(void) } static int migrate_postcopy_prepare(QTestState **from_ptr, - QTestState **to_ptr, - bool hide_error) + QTestState **to_ptr, + MigrateStart *args) { char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); QTestState *from, *to; - if (test_migrate_start(&from, &to, uri, hide_error, false, NULL, NULL)) { + if (test_migrate_start(&from, &to, uri, args)) { return -1; } @@ -795,7 +723,7 @@ static int migrate_postcopy_prepare(QTestState **from_ptr, /* Wait for the first serial output from the source */ wait_for_serial("src_serial"); - migrate(from, uri, "{}"); + migrate_qmp(from, uri, "{}"); g_free(uri); wait_for_migration_pass(from); @@ -822,9 +750,10 @@ static void migrate_postcopy_complete(QTestState *from, QTestState *to) static void test_postcopy(void) { + MigrateStart *args = migrate_start_new(); QTestState *from, *to; - if (migrate_postcopy_prepare(&from, &to, false)) { + if (migrate_postcopy_prepare(&from, &to, args)) { return; } migrate_postcopy_start(from, to); @@ -833,10 +762,13 @@ static void test_postcopy(void) static void test_postcopy_recovery(void) { + MigrateStart *args = migrate_start_new(); QTestState *from, *to; char *uri; - if (migrate_postcopy_prepare(&from, &to, true)) { + args->hide_stderr = true; + + if (migrate_postcopy_prepare(&from, &to, args)) { return; } @@ -882,7 +814,7 @@ static void test_postcopy_recovery(void) wait_for_migration_status(from, "postcopy-paused", (const char * []) { "failed", "active", "completed", NULL }); - migrate(from, uri, "{'resume': true}"); + migrate_qmp(from, uri, "{'resume': true}"); g_free(uri); /* Restore the postcopy bandwidth to unlimited */ @@ -891,40 +823,17 @@ static void test_postcopy_recovery(void) migrate_postcopy_complete(from, to); } -static void wait_for_migration_fail(QTestState *from, bool allow_active) -{ - QDict *rsp_return; - char *status; - bool failed; - - do { - status = migrate_query_status(from); - bool result = !strcmp(status, "setup") || !strcmp(status, "failed") || - (allow_active && !strcmp(status, "active")); - if (!result) { - fprintf(stderr, "%s: unexpected status status=%s allow_active=%d\n", - __func__, status, allow_active); - } - g_assert(result); - failed = !strcmp(status, "failed"); - g_free(status); - } while (!failed); - - /* Is the machine currently running? */ - rsp_return = wait_command(from, "{ 'execute': 'query-status' }"); - g_assert(qdict_haskey(rsp_return, "running")); - g_assert(qdict_get_bool(rsp_return, "running")); - qobject_unref(rsp_return); -} - static void test_baddest(void) { + MigrateStart *args = migrate_start_new(); QTestState *from, *to; - if (test_migrate_start(&from, &to, "tcp:0:0", true, false, NULL, NULL)) { + args->hide_stderr = true; + + if (test_migrate_start(&from, &to, "tcp:0:0", args)) { return; } - migrate(from, "tcp:0:0", "{}"); + migrate_qmp(from, "tcp:0:0", "{}"); wait_for_migration_fail(from, false); test_migrate_end(from, to, false); } @@ -932,9 +841,10 @@ static void test_baddest(void) static void test_precopy_unix(void) { char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); + MigrateStart *args = migrate_start_new(); QTestState *from, *to; - if (test_migrate_start(&from, &to, uri, false, false, NULL, NULL)) { + if (test_migrate_start(&from, &to, uri, args)) { return; } @@ -950,7 +860,7 @@ static void test_precopy_unix(void) /* Wait for the first serial output from the source */ wait_for_serial("src_serial"); - migrate(from, uri, "{}"); + migrate_qmp(from, uri, "{}"); wait_for_migration_pass(from); @@ -987,7 +897,7 @@ static void test_ignore_shared(void) /* Wait for the first serial output from the source */ wait_for_serial("src_serial"); - migrate(from, uri, "{}"); + migrate_qmp(from, uri, "{}"); wait_for_migration_pass(from); @@ -1010,9 +920,10 @@ static void test_ignore_shared(void) static void test_xbzrle(const char *uri) { + MigrateStart *args = migrate_start_new(); QTestState *from, *to; - if (test_migrate_start(&from, &to, uri, false, false, NULL, NULL)) { + if (test_migrate_start(&from, &to, uri, args)) { return; } @@ -1033,7 +944,7 @@ static void test_xbzrle(const char *uri) /* Wait for the first serial output from the source */ wait_for_serial("src_serial"); - migrate(from, uri, "{}"); + migrate_qmp(from, uri, "{}"); wait_for_migration_pass(from); @@ -1061,11 +972,11 @@ static void test_xbzrle_unix(void) static void test_precopy_tcp(void) { + MigrateStart *args = migrate_start_new(); char *uri; QTestState *from, *to; - if (test_migrate_start(&from, &to, "tcp:127.0.0.1:0", false, false, - NULL, NULL)) { + if (test_migrate_start(&from, &to, "tcp:127.0.0.1:0", args)) { return; } @@ -1084,7 +995,7 @@ static void test_precopy_tcp(void) uri = migrate_get_socket_address(to, "socket-address"); - migrate(from, uri, "{}"); + migrate_qmp(from, uri, "{}"); wait_for_migration_pass(from); @@ -1105,13 +1016,14 @@ static void test_precopy_tcp(void) static void test_migrate_fd_proto(void) { + MigrateStart *args = migrate_start_new(); QTestState *from, *to; int ret; int pair[2]; QDict *rsp; const char *error_desc; - if (test_migrate_start(&from, &to, "defer", false, false, NULL, NULL)) { + if (test_migrate_start(&from, &to, "defer", args)) { return; } @@ -1152,7 +1064,7 @@ static void test_migrate_fd_proto(void) close(pair[1]); /* Start migration to the 2nd socket*/ - migrate(from, "fd:fd-mig", "{}"); + migrate_qmp(from, "fd:fd-mig", "{}"); wait_for_migration_pass(from); @@ -1187,15 +1099,12 @@ static void test_migrate_fd_proto(void) test_migrate_end(from, to, true); } -static void do_test_validate_uuid(const char *uuid_arg_src, - const char *uuid_arg_dst, - bool should_fail, bool hide_stderr) +static void do_test_validate_uuid(MigrateStart *args, bool should_fail) { char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); QTestState *from, *to; - if (test_migrate_start(&from, &to, uri, hide_stderr, false, - uuid_arg_src, uuid_arg_dst)) { + if (test_migrate_start(&from, &to, uri, args)) { return; } @@ -1210,7 +1119,7 @@ static void do_test_validate_uuid(const char *uuid_arg_src, /* Wait for the first serial output from the source */ wait_for_serial("src_serial"); - migrate(from, uri, "{}"); + migrate_qmp(from, uri, "{}"); if (should_fail) { qtest_set_expected_status(to, 1); @@ -1225,33 +1134,51 @@ static void do_test_validate_uuid(const char *uuid_arg_src, static void test_validate_uuid(void) { - do_test_validate_uuid("-uuid 11111111-1111-1111-1111-111111111111", - "-uuid 11111111-1111-1111-1111-111111111111", - false, false); + MigrateStart *args = migrate_start_new(); + + g_free(args->opts_source); + g_free(args->opts_target); + args->opts_source = g_strdup("-uuid 11111111-1111-1111-1111-111111111111"); + args->opts_target = g_strdup("-uuid 11111111-1111-1111-1111-111111111111"); + do_test_validate_uuid(args, false); } static void test_validate_uuid_error(void) { - do_test_validate_uuid("-uuid 11111111-1111-1111-1111-111111111111", - "-uuid 22222222-2222-2222-2222-222222222222", - true, true); + MigrateStart *args = migrate_start_new(); + + g_free(args->opts_source); + g_free(args->opts_target); + args->opts_source = g_strdup("-uuid 11111111-1111-1111-1111-111111111111"); + args->opts_target = g_strdup("-uuid 22222222-2222-2222-2222-222222222222"); + args->hide_stderr = true; + do_test_validate_uuid(args, true); } static void test_validate_uuid_src_not_set(void) { - do_test_validate_uuid(NULL, "-uuid 11111111-1111-1111-1111-111111111111", - false, true); + MigrateStart *args = migrate_start_new(); + + g_free(args->opts_target); + args->opts_target = g_strdup("-uuid 22222222-2222-2222-2222-222222222222"); + args->hide_stderr = true; + do_test_validate_uuid(args, false); } static void test_validate_uuid_dst_not_set(void) { - do_test_validate_uuid("-uuid 11111111-1111-1111-1111-111111111111", NULL, - false, true); + MigrateStart *args = migrate_start_new(); + + g_free(args->opts_source); + args->opts_source = g_strdup("-uuid 11111111-1111-1111-1111-111111111111"); + args->hide_stderr = true; + do_test_validate_uuid(args, false); } static void test_migrate_auto_converge(void) { char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); + MigrateStart *args = migrate_start_new(); QTestState *from, *to; int64_t remaining, percentage; @@ -1270,7 +1197,7 @@ static void test_migrate_auto_converge(void) */ const int64_t expected_threshold = max_bandwidth * downtime_limit / 1000; - if (test_migrate_start(&from, &to, uri, false, false, NULL, NULL)) { + if (test_migrate_start(&from, &to, uri, args)) { return; } @@ -1284,7 +1211,7 @@ static void test_migrate_auto_converge(void) * without throttling. */ migrate_set_parameter_int(from, "downtime-limit", 1); - migrate_set_parameter_int(from, "max-bandwidth", 100000000); /* ~100Mb/s */ + migrate_set_parameter_int(from, "max-bandwidth", 1000000); /* ~1Mb/s */ /* To check remaining size after precopy */ migrate_set_capability(from, "pause-before-switchover", true); @@ -1292,7 +1219,7 @@ static void test_migrate_auto_converge(void) /* Wait for the first serial output from the source */ wait_for_serial("src_serial"); - migrate(from, uri, "{}"); + migrate_qmp(from, uri, "{}"); /* Wait for throttling begins */ percentage = 0; @@ -1318,7 +1245,8 @@ static void test_migrate_auto_converge(void) g_assert_cmpint(percentage, <=, max_pct); remaining = read_ram_property_int(from, "remaining"); - g_assert_cmpint(remaining, <, expected_threshold); + g_assert_cmpint(remaining, <, + (expected_threshold + expected_threshold / 100)); migrate_continue(from, "pre-switchover"); @@ -1332,6 +1260,178 @@ static void test_migrate_auto_converge(void) test_migrate_end(from, to, true); } +static void test_multifd_tcp(const char *method) +{ + MigrateStart *args = migrate_start_new(); + QTestState *from, *to; + QDict *rsp; + char *uri; + + if (test_migrate_start(&from, &to, "defer", args)) { + return; + } + + /* + * We want to pick a speed slow enough that the test completes + * quickly, but that it doesn't complete precopy even on a slow + * machine, so also set the downtime. + */ + /* 1 ms should make it not converge*/ + migrate_set_parameter_int(from, "downtime-limit", 1); + /* 1GB/s */ + migrate_set_parameter_int(from, "max-bandwidth", 1000000000); + + migrate_set_parameter_int(from, "multifd-channels", 16); + migrate_set_parameter_int(to, "multifd-channels", 16); + + migrate_set_parameter_str(from, "multifd-compression", method); + migrate_set_parameter_str(to, "multifd-compression", method); + + migrate_set_capability(from, "multifd", "true"); + migrate_set_capability(to, "multifd", "true"); + + /* Start incoming migration from the 1st socket */ + rsp = wait_command(to, "{ 'execute': 'migrate-incoming'," + " 'arguments': { 'uri': 'tcp:127.0.0.1:0' }}"); + qobject_unref(rsp); + + /* Wait for the first serial output from the source */ + wait_for_serial("src_serial"); + + uri = migrate_get_socket_address(to, "socket-address"); + + migrate_qmp(from, uri, "{}"); + + wait_for_migration_pass(from); + + /* 300ms it should converge */ + migrate_set_parameter_int(from, "downtime-limit", 300); + + if (!got_stop) { + qtest_qmp_eventwait(from, "STOP"); + } + qtest_qmp_eventwait(to, "RESUME"); + + wait_for_serial("dest_serial"); + wait_for_migration_complete(from); + test_migrate_end(from, to, true); + g_free(uri); +} + +static void test_multifd_tcp_none(void) +{ + test_multifd_tcp("none"); +} + +static void test_multifd_tcp_zlib(void) +{ + test_multifd_tcp("zlib"); +} + +#ifdef CONFIG_ZSTD +static void test_multifd_tcp_zstd(void) +{ + test_multifd_tcp("zstd"); +} +#endif + +/* + * This test does: + * source target + * migrate_incoming + * migrate + * migrate_cancel + * launch another target + * migrate + * + * And see that it works + */ +static void test_multifd_tcp_cancel(void) +{ + MigrateStart *args = migrate_start_new(); + QTestState *from, *to, *to2; + QDict *rsp; + char *uri; + + args->hide_stderr = true; + + if (test_migrate_start(&from, &to, "defer", args)) { + return; + } + + /* + * We want to pick a speed slow enough that the test completes + * quickly, but that it doesn't complete precopy even on a slow + * machine, so also set the downtime. + */ + /* 1 ms should make it not converge*/ + migrate_set_parameter_int(from, "downtime-limit", 1); + /* 300MB/s */ + migrate_set_parameter_int(from, "max-bandwidth", 30000000); + + migrate_set_parameter_int(from, "multifd-channels", 16); + migrate_set_parameter_int(to, "multifd-channels", 16); + + migrate_set_capability(from, "multifd", "true"); + migrate_set_capability(to, "multifd", "true"); + + /* Start incoming migration from the 1st socket */ + rsp = wait_command(to, "{ 'execute': 'migrate-incoming'," + " 'arguments': { 'uri': 'tcp:127.0.0.1:0' }}"); + qobject_unref(rsp); + + /* Wait for the first serial output from the source */ + wait_for_serial("src_serial"); + + uri = migrate_get_socket_address(to, "socket-address"); + + migrate_qmp(from, uri, "{}"); + + wait_for_migration_pass(from); + + migrate_cancel(from); + + args = migrate_start_new(); + args->only_target = true; + + if (test_migrate_start(&from, &to2, "defer", args)) { + return; + } + + migrate_set_parameter_int(to2, "multifd-channels", 16); + + migrate_set_capability(to2, "multifd", "true"); + + /* Start incoming migration from the 1st socket */ + rsp = wait_command(to2, "{ 'execute': 'migrate-incoming'," + " 'arguments': { 'uri': 'tcp:127.0.0.1:0' }}"); + qobject_unref(rsp); + + g_free(uri); + uri = migrate_get_socket_address(to2, "socket-address"); + + wait_for_migration_status(from, "cancelled", NULL); + + /* 300ms it should converge */ + migrate_set_parameter_int(from, "downtime-limit", 300); + /* 1GB/s */ + migrate_set_parameter_int(from, "max-bandwidth", 1000000000); + + migrate_qmp(from, uri, "{}"); + + wait_for_migration_pass(from); + + if (!got_stop) { + qtest_qmp_eventwait(from, "STOP"); + } + qtest_qmp_eventwait(to2, "RESUME"); + + wait_for_serial("dest_serial"); + wait_for_migration_complete(from); + test_migrate_end(from, to2, true); + g_free(uri); +} + int main(int argc, char **argv) { char template[] = "/tmp/migration-test-XXXXXX"; @@ -1349,7 +1449,8 @@ int main(int argc, char **argv) * some reason) */ if (g_str_equal(qtest_get_arch(), "ppc64") && - access("/sys/module/kvm_hv", F_OK)) { + (access("/sys/module/kvm_hv", F_OK) || + access("/dev/kvm", R_OK | W_OK))) { g_test_message("Skipping test: kvm_hv not available"); return g_test_run(); } @@ -1395,6 +1496,12 @@ int main(int argc, char **argv) test_validate_uuid_dst_not_set); qtest_add_func("/migration/auto_converge", test_migrate_auto_converge); + qtest_add_func("/migration/multifd/tcp/none", test_multifd_tcp_none); + qtest_add_func("/migration/multifd/tcp/cancel", test_multifd_tcp_cancel); + qtest_add_func("/migration/multifd/tcp/zlib", test_multifd_tcp_zlib); +#ifdef CONFIG_ZSTD + qtest_add_func("/migration/multifd/tcp/zstd", test_multifd_tcp_zstd); +#endif ret = g_test_run(); diff --git a/tests/modules-test.c b/tests/qtest/modules-test.c similarity index 91% rename from tests/modules-test.c rename to tests/qtest/modules-test.c index d1a6ace218..88217686e1 100644 --- a/tests/modules-test.c +++ b/tests/qtest/modules-test.c @@ -64,7 +64,8 @@ int main(int argc, char *argv[]) g_test_init(&argc, &argv, NULL); for (i = 0; i < G_N_ELEMENTS(modules); i += 2) { - char *testname = g_strdup_printf("/module/load/%s", modules[i + 1]); + char *testname = g_strdup_printf("/module/load/%s%s", + modules[i], modules[i + 1]); qtest_add_data_func(testname, modules + i, test_modules_load); g_free(testname); } diff --git a/tests/ne2000-test.c b/tests/qtest/ne2000-test.c similarity index 100% rename from tests/ne2000-test.c rename to tests/qtest/ne2000-test.c diff --git a/tests/qtest/numa-test.c b/tests/qtest/numa-test.c new file mode 100644 index 0000000000..e1ed6d4322 --- /dev/null +++ b/tests/qtest/numa-test.c @@ -0,0 +1,580 @@ +/* + * NUMA configuration test cases + * + * Copyright (c) 2017 Red Hat Inc. + * Authors: + * Igor Mammedov + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "qapi/qmp/qdict.h" +#include "qapi/qmp/qlist.h" + +static char *make_cli(const GString *generic_cli, const char *test_cli) +{ + return g_strdup_printf("%s %s", generic_cli->str, test_cli); +} + +static void test_mon_explicit(const void *data) +{ + QTestState *qts; + g_autofree char *s = NULL; + g_autofree char *cli = NULL; + + cli = make_cli(data, "-smp 8 -numa node,nodeid=0,memdev=ram,cpus=0-3 " + "-numa node,nodeid=1,cpus=4-7"); + qts = qtest_init(cli); + + s = qtest_hmp(qts, "info numa"); + g_assert(strstr(s, "node 0 cpus: 0 1 2 3")); + g_assert(strstr(s, "node 1 cpus: 4 5 6 7")); + + qtest_quit(qts); +} + +static void test_def_cpu_split(const void *data) +{ + QTestState *qts; + g_autofree char *s = NULL; + g_autofree char *cli = NULL; + + cli = make_cli(data, "-smp 8 -numa node,memdev=ram -numa node"); + qts = qtest_init(cli); + + s = qtest_hmp(qts, "info numa"); + g_assert(strstr(s, "node 0 cpus: 0 2 4 6")); + g_assert(strstr(s, "node 1 cpus: 1 3 5 7")); + + qtest_quit(qts); +} + +static void test_mon_partial(const void *data) +{ + QTestState *qts; + g_autofree char *s = NULL; + g_autofree char *cli = NULL; + + cli = make_cli(data, "-smp 8 " + "-numa node,nodeid=0,memdev=ram,cpus=0-1 " + "-numa node,nodeid=1,cpus=4-5 "); + qts = qtest_init(cli); + + s = qtest_hmp(qts, "info numa"); + g_assert(strstr(s, "node 0 cpus: 0 1 2 3 6 7")); + g_assert(strstr(s, "node 1 cpus: 4 5")); + + qtest_quit(qts); +} + +static QList *get_cpus(QTestState *qts, QDict **resp) +{ + *resp = qtest_qmp(qts, "{ 'execute': 'query-cpus' }"); + g_assert(*resp); + g_assert(qdict_haskey(*resp, "return")); + return qdict_get_qlist(*resp, "return"); +} + +static void test_query_cpus(const void *data) +{ + QDict *resp; + QList *cpus; + QObject *e; + QTestState *qts; + g_autofree char *cli = NULL; + + cli = make_cli(data, "-smp 8 -numa node,memdev=ram,cpus=0-3 " + "-numa node,cpus=4-7"); + qts = qtest_init(cli); + cpus = get_cpus(qts, &resp); + g_assert(cpus); + + while ((e = qlist_pop(cpus))) { + QDict *cpu, *props; + int64_t cpu_idx, node; + + cpu = qobject_to(QDict, e); + g_assert(qdict_haskey(cpu, "CPU")); + g_assert(qdict_haskey(cpu, "props")); + + cpu_idx = qdict_get_int(cpu, "CPU"); + props = qdict_get_qdict(cpu, "props"); + g_assert(qdict_haskey(props, "node-id")); + node = qdict_get_int(props, "node-id"); + if (cpu_idx >= 0 && cpu_idx < 4) { + g_assert_cmpint(node, ==, 0); + } else { + g_assert_cmpint(node, ==, 1); + } + qobject_unref(e); + } + + qobject_unref(resp); + qtest_quit(qts); +} + +static void pc_numa_cpu(const void *data) +{ + QDict *resp; + QList *cpus; + QObject *e; + QTestState *qts; + g_autofree char *cli = NULL; + + cli = make_cli(data, "-cpu pentium -smp 8,sockets=2,cores=2,threads=2 " + "-numa node,nodeid=0,memdev=ram -numa node,nodeid=1 " + "-numa cpu,node-id=1,socket-id=0 " + "-numa cpu,node-id=0,socket-id=1,core-id=0 " + "-numa cpu,node-id=0,socket-id=1,core-id=1,thread-id=0 " + "-numa cpu,node-id=1,socket-id=1,core-id=1,thread-id=1"); + qts = qtest_init(cli); + cpus = get_cpus(qts, &resp); + g_assert(cpus); + + while ((e = qlist_pop(cpus))) { + QDict *cpu, *props; + int64_t socket, core, thread, node; + + cpu = qobject_to(QDict, e); + g_assert(qdict_haskey(cpu, "props")); + props = qdict_get_qdict(cpu, "props"); + + g_assert(qdict_haskey(props, "node-id")); + node = qdict_get_int(props, "node-id"); + g_assert(qdict_haskey(props, "socket-id")); + socket = qdict_get_int(props, "socket-id"); + g_assert(qdict_haskey(props, "core-id")); + core = qdict_get_int(props, "core-id"); + g_assert(qdict_haskey(props, "thread-id")); + thread = qdict_get_int(props, "thread-id"); + + if (socket == 0) { + g_assert_cmpint(node, ==, 1); + } else if (socket == 1 && core == 0) { + g_assert_cmpint(node, ==, 0); + } else if (socket == 1 && core == 1 && thread == 0) { + g_assert_cmpint(node, ==, 0); + } else if (socket == 1 && core == 1 && thread == 1) { + g_assert_cmpint(node, ==, 1); + } else { + g_assert(false); + } + qobject_unref(e); + } + + qobject_unref(resp); + qtest_quit(qts); +} + +static void spapr_numa_cpu(const void *data) +{ + QDict *resp; + QList *cpus; + QObject *e; + QTestState *qts; + g_autofree char *cli = NULL; + + cli = make_cli(data, "-smp 4,cores=4 " + "-numa node,nodeid=0,memdev=ram -numa node,nodeid=1 " + "-numa cpu,node-id=0,core-id=0 " + "-numa cpu,node-id=0,core-id=1 " + "-numa cpu,node-id=0,core-id=2 " + "-numa cpu,node-id=1,core-id=3"); + qts = qtest_init(cli); + cpus = get_cpus(qts, &resp); + g_assert(cpus); + + while ((e = qlist_pop(cpus))) { + QDict *cpu, *props; + int64_t core, node; + + cpu = qobject_to(QDict, e); + g_assert(qdict_haskey(cpu, "props")); + props = qdict_get_qdict(cpu, "props"); + + g_assert(qdict_haskey(props, "node-id")); + node = qdict_get_int(props, "node-id"); + g_assert(qdict_haskey(props, "core-id")); + core = qdict_get_int(props, "core-id"); + + if (core >= 0 && core < 3) { + g_assert_cmpint(node, ==, 0); + } else if (core == 3) { + g_assert_cmpint(node, ==, 1); + } else { + g_assert(false); + } + qobject_unref(e); + } + + qobject_unref(resp); + qtest_quit(qts); +} + +static void aarch64_numa_cpu(const void *data) +{ + QDict *resp; + QList *cpus; + QObject *e; + QTestState *qts; + g_autofree char *cli = NULL; + + cli = make_cli(data, "-smp 2 " + "-numa node,nodeid=0,memdev=ram -numa node,nodeid=1 " + "-numa cpu,node-id=1,thread-id=0 " + "-numa cpu,node-id=0,thread-id=1"); + qts = qtest_init(cli); + cpus = get_cpus(qts, &resp); + g_assert(cpus); + + while ((e = qlist_pop(cpus))) { + QDict *cpu, *props; + int64_t thread, node; + + cpu = qobject_to(QDict, e); + g_assert(qdict_haskey(cpu, "props")); + props = qdict_get_qdict(cpu, "props"); + + g_assert(qdict_haskey(props, "node-id")); + node = qdict_get_int(props, "node-id"); + g_assert(qdict_haskey(props, "thread-id")); + thread = qdict_get_int(props, "thread-id"); + + if (thread == 0) { + g_assert_cmpint(node, ==, 1); + } else if (thread == 1) { + g_assert_cmpint(node, ==, 0); + } else { + g_assert(false); + } + qobject_unref(e); + } + + qobject_unref(resp); + qtest_quit(qts); +} + +static void pc_dynamic_cpu_cfg(const void *data) +{ + QObject *e; + QDict *resp; + QList *cpus; + QTestState *qs; + g_autofree char *cli = NULL; + + cli = make_cli(data, "-nodefaults --preconfig -smp 2"); + qs = qtest_init(cli); + + /* create 2 numa nodes */ + g_assert(!qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," + " 'arguments': { 'type': 'node', 'nodeid': 0, 'memdev': 'ram' } }"))); + g_assert(!qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," + " 'arguments': { 'type': 'node', 'nodeid': 1 } }"))); + + /* map 2 cpus in non default reverse order + * i.e socket1->node0, socket0->node1 + */ + g_assert(!qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," + " 'arguments': { 'type': 'cpu', 'node-id': 0, 'socket-id': 1 } }"))); + g_assert(!qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," + " 'arguments': { 'type': 'cpu', 'node-id': 1, 'socket-id': 0 } }"))); + + /* let machine initialization to complete and run */ + g_assert(!qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'x-exit-preconfig' }"))); + qtest_qmp_eventwait(qs, "RESUME"); + + /* check that CPUs are mapped as expected */ + resp = qtest_qmp(qs, "{ 'execute': 'query-hotpluggable-cpus'}"); + g_assert(qdict_haskey(resp, "return")); + cpus = qdict_get_qlist(resp, "return"); + g_assert(cpus); + while ((e = qlist_pop(cpus))) { + const QDict *cpu, *props; + int64_t socket, node; + + cpu = qobject_to(QDict, e); + g_assert(qdict_haskey(cpu, "props")); + props = qdict_get_qdict(cpu, "props"); + + g_assert(qdict_haskey(props, "node-id")); + node = qdict_get_int(props, "node-id"); + g_assert(qdict_haskey(props, "socket-id")); + socket = qdict_get_int(props, "socket-id"); + + if (socket == 0) { + g_assert_cmpint(node, ==, 1); + } else if (socket == 1) { + g_assert_cmpint(node, ==, 0); + } else { + g_assert(false); + } + qobject_unref(e); + } + qobject_unref(resp); + + qtest_quit(qs); +} + +static void pc_hmat_build_cfg(const void *data) +{ + QTestState *qs; + g_autofree char *cli = NULL; + + cli = make_cli(data, "-nodefaults --preconfig -machine hmat=on " + "-smp 2,sockets=2 " + "-m 128M,slots=2,maxmem=1G " + "-object memory-backend-ram,size=64M,id=m0 " + "-object memory-backend-ram,size=64M,id=m1 " + "-numa node,nodeid=0,memdev=m0 " + "-numa node,nodeid=1,memdev=m1,initiator=0 " + "-numa cpu,node-id=0,socket-id=0 " + "-numa cpu,node-id=0,socket-id=1"); + qs = qtest_init(cli); + + /* Fail: Initiator should be less than the number of nodes */ + g_assert_true(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," + " 'arguments': { 'type': 'hmat-lb', 'initiator': 2, 'target': 0," + " 'hierarchy': \"memory\", 'data-type': \"access-latency\" } }"))); + + /* Fail: Target should be less than the number of nodes */ + g_assert_true(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," + " 'arguments': { 'type': 'hmat-lb', 'initiator': 0, 'target': 2," + " 'hierarchy': \"memory\", 'data-type': \"access-latency\" } }"))); + + /* Fail: Initiator should contain cpu */ + g_assert_true(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," + " 'arguments': { 'type': 'hmat-lb', 'initiator': 1, 'target': 0," + " 'hierarchy': \"memory\", 'data-type': \"access-latency\" } }"))); + + /* Fail: Data-type mismatch */ + g_assert_true(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," + " 'arguments': { 'type': 'hmat-lb', 'initiator': 0, 'target': 0," + " 'hierarchy': \"memory\", 'data-type': \"write-latency\"," + " 'bandwidth': 524288000 } }"))); + g_assert_true(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," + " 'arguments': { 'type': 'hmat-lb', 'initiator': 0, 'target': 0," + " 'hierarchy': \"memory\", 'data-type': \"read-bandwidth\"," + " 'latency': 5 } }"))); + + /* Fail: Bandwidth should be 1MB (1048576) aligned */ + g_assert_true(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," + " 'arguments': { 'type': 'hmat-lb', 'initiator': 0, 'target': 0," + " 'hierarchy': \"memory\", 'data-type': \"access-bandwidth\"," + " 'bandwidth': 1048575 } }"))); + + /* Configuring HMAT bandwidth and latency details */ + g_assert_false(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," + " 'arguments': { 'type': 'hmat-lb', 'initiator': 0, 'target': 0," + " 'hierarchy': \"memory\", 'data-type': \"access-latency\"," + " 'latency': 1 } }"))); /* 1 ns */ + g_assert_true(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," + " 'arguments': { 'type': 'hmat-lb', 'initiator': 0, 'target': 0," + " 'hierarchy': \"memory\", 'data-type': \"access-latency\"," + " 'latency': 5 } }"))); /* Fail: Duplicate configuration */ + g_assert_false(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," + " 'arguments': { 'type': 'hmat-lb', 'initiator': 0, 'target': 0," + " 'hierarchy': \"memory\", 'data-type': \"access-bandwidth\"," + " 'bandwidth': 68717379584 } }"))); /* 65534 MB/s */ + g_assert_false(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," + " 'arguments': { 'type': 'hmat-lb', 'initiator': 0, 'target': 1," + " 'hierarchy': \"memory\", 'data-type': \"access-latency\"," + " 'latency': 65534 } }"))); /* 65534 ns */ + g_assert_false(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," + " 'arguments': { 'type': 'hmat-lb', 'initiator': 0, 'target': 1," + " 'hierarchy': \"memory\", 'data-type': \"access-bandwidth\"," + " 'bandwidth': 34358689792 } }"))); /* 32767 MB/s */ + + /* Fail: node_id should be less than the number of nodes */ + g_assert_true(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," + " 'arguments': { 'type': 'hmat-cache', 'node-id': 2, 'size': 10240," + " 'level': 1, 'associativity': \"direct\", 'policy': \"write-back\"," + " 'line': 8 } }"))); + + /* Fail: level should be less than HMAT_LB_LEVELS (4) */ + g_assert_true(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," + " 'arguments': { 'type': 'hmat-cache', 'node-id': 0, 'size': 10240," + " 'level': 4, 'associativity': \"direct\", 'policy': \"write-back\"," + " 'line': 8 } }"))); + + /* Fail: associativity option should be 'none', if level is 0 */ + g_assert_true(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," + " 'arguments': { 'type': 'hmat-cache', 'node-id': 0, 'size': 10240," + " 'level': 0, 'associativity': \"direct\", 'policy': \"none\"," + " 'line': 0 } }"))); + /* Fail: policy option should be 'none', if level is 0 */ + g_assert_true(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," + " 'arguments': { 'type': 'hmat-cache', 'node-id': 0, 'size': 10240," + " 'level': 0, 'associativity': \"none\", 'policy': \"write-back\"," + " 'line': 0 } }"))); + /* Fail: line option should be 0, if level is 0 */ + g_assert_true(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," + " 'arguments': { 'type': 'hmat-cache', 'node-id': 0, 'size': 10240," + " 'level': 0, 'associativity': \"none\", 'policy': \"none\"," + " 'line': 8 } }"))); + + /* Configuring HMAT memory side cache attributes */ + g_assert_false(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," + " 'arguments': { 'type': 'hmat-cache', 'node-id': 0, 'size': 10240," + " 'level': 1, 'associativity': \"direct\", 'policy': \"write-back\"," + " 'line': 8 } }"))); + g_assert_true(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," + " 'arguments': { 'type': 'hmat-cache', 'node-id': 0, 'size': 10240," + " 'level': 1, 'associativity': \"direct\", 'policy': \"write-back\"," + " 'line': 8 } }"))); /* Fail: Duplicate configuration */ + /* Fail: The size of level 2 size should be small than level 1 */ + g_assert_true(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," + " 'arguments': { 'type': 'hmat-cache', 'node-id': 0, 'size': 10240," + " 'level': 2, 'associativity': \"direct\", 'policy': \"write-back\"," + " 'line': 8 } }"))); + /* Fail: The size of level 0 size should be larger than level 1 */ + g_assert_true(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," + " 'arguments': { 'type': 'hmat-cache', 'node-id': 0, 'size': 10240," + " 'level': 0, 'associativity': \"direct\", 'policy': \"write-back\"," + " 'line': 8 } }"))); + g_assert_false(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," + " 'arguments': { 'type': 'hmat-cache', 'node-id': 1, 'size': 10240," + " 'level': 1, 'associativity': \"direct\", 'policy': \"write-back\"," + " 'line': 8 } }"))); + + /* let machine initialization to complete and run */ + g_assert_false(qmp_rsp_is_err(qtest_qmp(qs, + "{ 'execute': 'x-exit-preconfig' }"))); + qtest_qmp_eventwait(qs, "RESUME"); + + qtest_quit(qs); +} + +static void pc_hmat_off_cfg(const void *data) +{ + QTestState *qs; + g_autofree char *cli = NULL; + + cli = make_cli(data, "-nodefaults --preconfig " + "-smp 2,sockets=2 " + "-m 128M,slots=2,maxmem=1G " + "-object memory-backend-ram,size=64M,id=m0,prealloc=y " + "-object memory-backend-ram,size=64M,id=m1 " + "-numa node,nodeid=0,memdev=m0"); + qs = qtest_init(cli); + + /* + * Fail: Enable HMAT with -machine hmat=on + * before using any of hmat specific options + */ + g_assert_true(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," + " 'arguments': { 'type': 'node', 'nodeid': 1, 'memdev': \"m1\"," + " 'initiator': 0 } }"))); + g_assert_false(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," + " 'arguments': { 'type': 'node', 'nodeid': 1, 'memdev': \"m1\" } }"))); + g_assert_true(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," + " 'arguments': { 'type': 'hmat-lb', 'initiator': 0, 'target': 0," + " 'hierarchy': \"memory\", 'data-type': \"access-latency\"," + " 'latency': 1 } }"))); + g_assert_true(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," + " 'arguments': { 'type': 'hmat-cache', 'node-id': 0, 'size': 10240," + " 'level': 1, 'associativity': \"direct\", 'policy': \"write-back\"," + " 'line': 8 } }"))); + + /* let machine initialization to complete and run */ + g_assert_false(qmp_rsp_is_err(qtest_qmp(qs, + "{ 'execute': 'x-exit-preconfig' }"))); + qtest_qmp_eventwait(qs, "RESUME"); + + qtest_quit(qs); +} + +static void pc_hmat_erange_cfg(const void *data) +{ + QTestState *qs; + g_autofree char *cli = NULL; + + cli = make_cli(data, "-nodefaults --preconfig -machine hmat=on " + "-smp 2,sockets=2 " + "-m 128M,slots=2,maxmem=1G " + "-object memory-backend-ram,size=64M,id=m0 " + "-object memory-backend-ram,size=64M,id=m1 " + "-numa node,nodeid=0,memdev=m0 " + "-numa node,nodeid=1,memdev=m1,initiator=0 " + "-numa cpu,node-id=0,socket-id=0 " + "-numa cpu,node-id=0,socket-id=1"); + qs = qtest_init(cli); + + /* Can't store the compressed latency */ + g_assert_false(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," + " 'arguments': { 'type': 'hmat-lb', 'initiator': 0, 'target': 0," + " 'hierarchy': \"memory\", 'data-type': \"access-latency\"," + " 'latency': 1 } }"))); /* 1 ns */ + g_assert_true(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," + " 'arguments': { 'type': 'hmat-lb', 'initiator': 0, 'target': 1," + " 'hierarchy': \"memory\", 'data-type': \"access-latency\"," + " 'latency': 65535 } }"))); /* 65535 ns */ + + /* Test the 0 input (bandwidth not provided) */ + g_assert_false(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," + " 'arguments': { 'type': 'hmat-lb', 'initiator': 0, 'target': 0," + " 'hierarchy': \"memory\", 'data-type': \"access-bandwidth\"," + " 'bandwidth': 0 } }"))); /* 0 MB/s */ + /* Fail: bandwidth should be provided before memory side cache attributes */ + g_assert_true(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," + " 'arguments': { 'type': 'hmat-cache', 'node-id': 0, 'size': 10240," + " 'level': 1, 'associativity': \"direct\", 'policy': \"write-back\"," + " 'line': 8 } }"))); + + /* Can't store the compressed bandwidth */ + g_assert_true(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," + " 'arguments': { 'type': 'hmat-lb', 'initiator': 0, 'target': 1," + " 'hierarchy': \"memory\", 'data-type': \"access-bandwidth\"," + " 'bandwidth': 68718428160 } }"))); /* 65535 MB/s */ + + /* let machine initialization to complete and run */ + g_assert_false(qmp_rsp_is_err(qtest_qmp(qs, + "{ 'execute': 'x-exit-preconfig' }"))); + qtest_qmp_eventwait(qs, "RESUME"); + + qtest_quit(qs); +} + +int main(int argc, char **argv) +{ + g_autoptr(GString) args = g_string_new(NULL); + const char *arch = qtest_get_arch(); + + if (g_str_equal(arch, "ppc64")) { + g_string_append(args, " -object memory-backend-ram,id=ram,size=512M"); + } else { + g_string_append(args, " -object memory-backend-ram,id=ram,size=128M"); + } + + if (g_str_equal(arch, "aarch64")) { + g_string_append(args, " -machine virt"); + } + + g_test_init(&argc, &argv, NULL); + + qtest_add_data_func("/numa/mon/cpus/default", args, test_def_cpu_split); + qtest_add_data_func("/numa/mon/cpus/explicit", args, test_mon_explicit); + qtest_add_data_func("/numa/mon/cpus/partial", args, test_mon_partial); + qtest_add_data_func("/numa/qmp/cpus/query-cpus", args, test_query_cpus); + + if (!strcmp(arch, "i386") || !strcmp(arch, "x86_64")) { + qtest_add_data_func("/numa/pc/cpu/explicit", args, pc_numa_cpu); + qtest_add_data_func("/numa/pc/dynamic/cpu", args, pc_dynamic_cpu_cfg); + qtest_add_data_func("/numa/pc/hmat/build", args, pc_hmat_build_cfg); + qtest_add_data_func("/numa/pc/hmat/off", args, pc_hmat_off_cfg); + qtest_add_data_func("/numa/pc/hmat/erange", args, pc_hmat_erange_cfg); + } + + if (!strcmp(arch, "ppc64")) { + qtest_add_data_func("/numa/spapr/cpu/explicit", args, spapr_numa_cpu); + } + + if (!strcmp(arch, "aarch64")) { + qtest_add_data_func("/numa/aarch64/cpu/explicit", args, + aarch64_numa_cpu); + } + + return g_test_run(); +} diff --git a/tests/nvme-test.c b/tests/qtest/nvme-test.c similarity index 100% rename from tests/nvme-test.c rename to tests/qtest/nvme-test.c diff --git a/tests/pca9552-test.c b/tests/qtest/pca9552-test.c similarity index 93% rename from tests/pca9552-test.c rename to tests/qtest/pca9552-test.c index 4b800d3c3e..d80ed93cd3 100644 --- a/tests/pca9552-test.c +++ b/tests/qtest/pca9552-test.c @@ -32,22 +32,22 @@ static void receive_autoinc(void *obj, void *data, QGuestAllocator *alloc) pca9552_init(i2cdev); - i2c_send(i2cdev, ®, 1); + qi2c_send(i2cdev, ®, 1); /* PCA9552_LS0 */ - i2c_recv(i2cdev, &resp, 1); + qi2c_recv(i2cdev, &resp, 1); g_assert_cmphex(resp, ==, 0x54); /* PCA9552_LS1 */ - i2c_recv(i2cdev, &resp, 1); + qi2c_recv(i2cdev, &resp, 1); g_assert_cmphex(resp, ==, 0x55); /* PCA9552_LS2 */ - i2c_recv(i2cdev, &resp, 1); + qi2c_recv(i2cdev, &resp, 1); g_assert_cmphex(resp, ==, 0x55); /* PCA9552_LS3 */ - i2c_recv(i2cdev, &resp, 1); + qi2c_recv(i2cdev, &resp, 1); g_assert_cmphex(resp, ==, 0x54); } diff --git a/tests/pci-test.c b/tests/qtest/pci-test.c similarity index 100% rename from tests/pci-test.c rename to tests/qtest/pci-test.c diff --git a/tests/pcnet-test.c b/tests/qtest/pcnet-test.c similarity index 100% rename from tests/pcnet-test.c rename to tests/qtest/pcnet-test.c diff --git a/tests/pflash-cfi02-test.c b/tests/qtest/pflash-cfi02-test.c similarity index 99% rename from tests/pflash-cfi02-test.c rename to tests/qtest/pflash-cfi02-test.c index d3b23f4f66..17aa669b2e 100644 --- a/tests/pflash-cfi02-test.c +++ b/tests/qtest/pflash-cfi02-test.c @@ -260,7 +260,7 @@ static void test_geometry(const void *opaque) { const FlashConfig *config = opaque; QTestState *qtest; - qtest = qtest_initf("-M musicpal,accel=qtest" + qtest = qtest_initf("-M musicpal" " -drive if=pflash,file=%s,format=raw,copy-on-read" /* Device geometry properties. */ " -global driver=cfi.pflash02," @@ -580,7 +580,7 @@ static void test_cfi_in_autoselect(const void *opaque) { const FlashConfig *config = opaque; QTestState *qtest; - qtest = qtest_initf("-M musicpal,accel=qtest" + qtest = qtest_initf("-M musicpal" " -drive if=pflash,file=%s,format=raw,copy-on-read", image_path); FlashConfig explicit_config = expand_config_defaults(config); diff --git a/tests/pnv-xscom-test.c b/tests/qtest/pnv-xscom-test.c similarity index 97% rename from tests/pnv-xscom-test.c rename to tests/qtest/pnv-xscom-test.c index 9fddc7d5f9..2c46d5cf6d 100644 --- a/tests/pnv-xscom-test.c +++ b/tests/qtest/pnv-xscom-test.c @@ -84,7 +84,7 @@ static void test_cfam_id(const void *data) machine = "powernv9"; } - qts = qtest_initf("-M %s,accel=tcg -cpu %s", + qts = qtest_initf("-M %s -accel tcg -cpu %s", machine, chip->cpu_model); test_xscom_cfam_id(qts, chip); qtest_quit(qts); @@ -125,7 +125,7 @@ static void test_core(const void *data) machine = "powernv9"; } - qts = qtest_initf("-M %s,accel=tcg -cpu %s", + qts = qtest_initf("-M %s -accel tcg -cpu %s", machine, chip->cpu_model); test_xscom_core(qts, chip); qtest_quit(qts); diff --git a/tests/prom-env-test.c b/tests/qtest/prom-env-test.c similarity index 94% rename from tests/prom-env-test.c rename to tests/qtest/prom-env-test.c index 61bc1d1e7b..60e6ec3153 100644 --- a/tests/prom-env-test.c +++ b/tests/qtest/prom-env-test.c @@ -21,6 +21,7 @@ #include "qemu/osdep.h" #include "libqtest.h" +#include "libqos/libqos-spapr.h" #define MAGIC 0xcafec0de #define ADDRESS 0x4000 @@ -54,10 +55,10 @@ static void test_machine(const void *machine) */ if (strcmp(machine, "pseries") == 0) { extra_args = "-nodefaults" - " -machine cap-cfpc=broken,cap-sbbc=broken,cap-ibs=broken"; + " -machine " PSERIES_DEFAULT_CAPABILITIES; } - qts = qtest_initf("-M %s,accel=tcg %s -prom-env 'use-nvramrc?=true' " + qts = qtest_initf("-M %s -accel tcg %s -prom-env 'use-nvramrc?=true' " "-prom-env 'nvramrc=%x %x l!' ", (const char *)machine, extra_args, MAGIC, ADDRESS); check_guest_memory(qts); diff --git a/tests/pvpanic-test.c b/tests/qtest/pvpanic-test.c similarity index 100% rename from tests/pvpanic-test.c rename to tests/qtest/pvpanic-test.c diff --git a/tests/pxe-test.c b/tests/qtest/pxe-test.c similarity index 92% rename from tests/pxe-test.c rename to tests/qtest/pxe-test.c index aaae54f755..1161a773a4 100644 --- a/tests/pxe-test.c +++ b/tests/qtest/pxe-test.c @@ -17,6 +17,7 @@ #include "qemu-common.h" #include "libqtest.h" #include "boot-sector.h" +#include "libqos/libqos-spapr.h" #define NETNAME "net0" @@ -46,15 +47,15 @@ static testdef_t x86_tests_slow[] = { static testdef_t ppc64_tests[] = { { "pseries", "spapr-vlan", - "-machine cap-cfpc=broken,cap-sbbc=broken,cap-ibs=broken,vsmt=8" }, + "-machine vsmt=8," PSERIES_DEFAULT_CAPABILITIES }, { "pseries", "virtio-net-pci", - "-machine cap-cfpc=broken,cap-sbbc=broken,cap-ibs=broken,vsmt=8" }, + "-machine vsmt=8," PSERIES_DEFAULT_CAPABILITIES }, { NULL }, }; static testdef_t ppc64_tests_slow[] = { { "pseries", "e1000", - "-machine cap-cfpc=broken,cap-sbbc=broken,cap-ibs=broken,vsmt=8" }, + "-machine vsmt=8," PSERIES_DEFAULT_CAPABILITIES }, { NULL }, }; @@ -74,7 +75,7 @@ static void test_pxe_one(const testdef_t *test, bool ipv6) } args = g_strdup_printf( - "-machine %s,accel=kvm:tcg -nodefaults -boot order=n " + "-accel kvm -accel tcg -machine %s -nodefaults -boot order=n " "-netdev user,id=" NETNAME ",tftp=./,bootfile=%s,ipv4=%s,ipv6=%s " "-device %s,bootindex=1,netdev=" NETNAME " %s", test->machine, disk, ipv6 ? "off" : "on", ipv6 ? "on" : "off", diff --git a/tests/q35-test.c b/tests/qtest/q35-test.c similarity index 65% rename from tests/q35-test.c rename to tests/qtest/q35-test.c index a68183d513..c922d81bc0 100644 --- a/tests/q35-test.c +++ b/tests/qtest/q35-test.c @@ -186,6 +186,109 @@ static void test_tseg_size(const void *data) qtest_quit(qts); } +#define SMBASE 0x30000 +#define SMRAM_TEST_PATTERN 0x32 +#define SMRAM_TEST_RESET_PATTERN 0x23 + +static void test_smram_smbase_lock(void) +{ + QPCIBus *pcibus; + QPCIDevice *pcidev; + QDict *response; + QTestState *qts; + int i; + + qts = qtest_init("-M q35"); + + pcibus = qpci_new_pc(qts, NULL); + g_assert(pcibus != NULL); + + pcidev = qpci_device_find(pcibus, 0); + g_assert(pcidev != NULL); + + /* check that SMRAM is not enabled by default */ + g_assert(qpci_config_readb(pcidev, MCH_HOST_BRIDGE_F_SMBASE) == 0); + qtest_writeb(qts, SMBASE, SMRAM_TEST_PATTERN); + g_assert_cmpint(qtest_readb(qts, SMBASE), ==, SMRAM_TEST_PATTERN); + + /* check that writing junk to 0x9c before before negotiating is ignored */ + for (i = 0; i < 0xff; i++) { + qpci_config_writeb(pcidev, MCH_HOST_BRIDGE_F_SMBASE, i); + g_assert(qpci_config_readb(pcidev, MCH_HOST_BRIDGE_F_SMBASE) == 0); + } + + /* enable SMRAM at SMBASE */ + qpci_config_writeb(pcidev, MCH_HOST_BRIDGE_F_SMBASE, 0xff); + g_assert(qpci_config_readb(pcidev, MCH_HOST_BRIDGE_F_SMBASE) == 0x01); + /* lock SMRAM at SMBASE */ + qpci_config_writeb(pcidev, MCH_HOST_BRIDGE_F_SMBASE, 0x02); + g_assert(qpci_config_readb(pcidev, MCH_HOST_BRIDGE_F_SMBASE) == 0x02); + + /* check that SMRAM at SMBASE is locked and can't be unlocked */ + g_assert_cmpint(qtest_readb(qts, SMBASE), ==, 0xff); + for (i = 0; i <= 0xff; i++) { + /* make sure register is immutable */ + qpci_config_writeb(pcidev, MCH_HOST_BRIDGE_F_SMBASE, i); + g_assert(qpci_config_readb(pcidev, MCH_HOST_BRIDGE_F_SMBASE) == 0x02); + + /* RAM access should go into black hole */ + qtest_writeb(qts, SMBASE, SMRAM_TEST_PATTERN); + g_assert_cmpint(qtest_readb(qts, SMBASE), ==, 0xff); + } + + /* reset */ + response = qtest_qmp(qts, "{'execute': 'system_reset', 'arguments': {} }"); + g_assert(response); + g_assert(!qdict_haskey(response, "error")); + qobject_unref(response); + + /* check RAM at SMBASE is available after reset */ + g_assert_cmpint(qtest_readb(qts, SMBASE), ==, SMRAM_TEST_PATTERN); + g_assert(qpci_config_readb(pcidev, MCH_HOST_BRIDGE_F_SMBASE) == 0); + qtest_writeb(qts, SMBASE, SMRAM_TEST_RESET_PATTERN); + g_assert_cmpint(qtest_readb(qts, SMBASE), ==, SMRAM_TEST_RESET_PATTERN); + + g_free(pcidev); + qpci_free_pc(pcibus); + + qtest_quit(qts); +} + +static void test_without_smram_base(void) +{ + QPCIBus *pcibus; + QPCIDevice *pcidev; + QTestState *qts; + int i; + + qts = qtest_init("-M pc-q35-4.1"); + + pcibus = qpci_new_pc(qts, NULL); + g_assert(pcibus != NULL); + + pcidev = qpci_device_find(pcibus, 0); + g_assert(pcidev != NULL); + + /* check that RAM is accessible */ + qtest_writeb(qts, SMBASE, SMRAM_TEST_PATTERN); + g_assert_cmpint(qtest_readb(qts, SMBASE), ==, SMRAM_TEST_PATTERN); + + /* check that writing to 0x9c succeeds */ + for (i = 0; i <= 0xff; i++) { + qpci_config_writeb(pcidev, MCH_HOST_BRIDGE_F_SMBASE, i); + g_assert(qpci_config_readb(pcidev, MCH_HOST_BRIDGE_F_SMBASE) == i); + } + + /* check that RAM is still accessible */ + qtest_writeb(qts, SMBASE, SMRAM_TEST_PATTERN + 1); + g_assert_cmpint(qtest_readb(qts, SMBASE), ==, (SMRAM_TEST_PATTERN + 1)); + + g_free(pcidev); + qpci_free_pc(pcibus); + + qtest_quit(qts); +} + int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); @@ -197,5 +300,7 @@ int main(int argc, char **argv) qtest_add_data_func("/q35/tseg-size/8mb", &tseg_8mb, test_tseg_size); qtest_add_data_func("/q35/tseg-size/ext/16mb", &tseg_ext_16mb, test_tseg_size); + qtest_add_func("/q35/smram/smbase_lock", test_smram_smbase_lock); + qtest_add_func("/q35/smram/legacy_smbase", test_without_smram_base); return g_test_run(); } diff --git a/tests/qmp-cmd-test.c b/tests/qtest/qmp-cmd-test.c similarity index 100% rename from tests/qmp-cmd-test.c rename to tests/qtest/qmp-cmd-test.c diff --git a/tests/qmp-test.c b/tests/qtest/qmp-test.c similarity index 99% rename from tests/qmp-test.c rename to tests/qtest/qmp-test.c index 1b0eb69832..1a8876b6ca 100644 --- a/tests/qmp-test.c +++ b/tests/qtest/qmp-test.c @@ -13,7 +13,7 @@ #include "qemu/osdep.h" #include "libqtest.h" #include "qapi/error.h" -#include "qapi/qapi-visit-misc.h" +#include "qapi/qapi-visit-control.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qlist.h" #include "qapi/qobject-input-visitor.h" diff --git a/tests/qom-test.c b/tests/qtest/qom-test.c similarity index 75% rename from tests/qom-test.c rename to tests/qtest/qom-test.c index 4f94cc678c..e338a41194 100644 --- a/tests/qom-test.c +++ b/tests/qtest/qom-test.c @@ -15,35 +15,6 @@ #include "qemu/cutils.h" #include "libqtest.h" -static const char *blacklist_x86[] = { - "xenfv", "xenpv", NULL -}; - -static const struct { - const char *arch; - const char **machine; -} blacklists[] = { - { "i386", blacklist_x86 }, - { "x86_64", blacklist_x86 }, -}; - -static bool is_blacklisted(const char *arch, const char *mach) -{ - int i; - const char **p; - - for (i = 0; i < ARRAY_SIZE(blacklists); i++) { - if (!strcmp(blacklists[i].arch, arch)) { - for (p = blacklists[i].machine; *p; p++) { - if (!strcmp(*p, mach)) { - return true; - } - } - } - } - return false; -} - static void test_properties(QTestState *qts, const char *path, bool recurse) { char *child_path; @@ -108,13 +79,16 @@ static void test_machine(gconstpointer data) static void add_machine_test_case(const char *mname) { - const char *arch = qtest_get_arch(); + char *path; - if (!is_blacklisted(arch, mname)) { - char *path = g_strdup_printf("qom/%s", mname); - qtest_add_data_func(path, g_strdup(mname), test_machine); - g_free(path); + /* Ignore blacklisted machines that have known problems */ + if (!memcmp("xenfv", mname, 5) || g_str_equal("xenpv", mname)) { + return; } + + path = g_strdup_printf("qom/%s", mname); + qtest_add_data_func(path, g_strdup(mname), test_machine); + g_free(path); } int main(int argc, char **argv) diff --git a/tests/qos-test.c b/tests/qtest/qos-test.c similarity index 72% rename from tests/qos-test.c rename to tests/qtest/qos-test.c index fd70d73ea5..ad193f43a5 100644 --- a/tests/qos-test.c +++ b/tests/qtest/qos-test.c @@ -27,65 +27,11 @@ #include "libqos/malloc.h" #include "libqos/qgraph.h" #include "libqos/qgraph_internal.h" +#include "libqos/qos_external.h" static char *old_path; -static void apply_to_node(const char *name, bool is_machine, bool is_abstract) -{ - char *machine_name = NULL; - if (is_machine) { - const char *arch = qtest_get_arch(); - machine_name = g_strconcat(arch, "/", name, NULL); - name = machine_name; - } - qos_graph_node_set_availability(name, true); - if (is_abstract) { - qos_delete_cmd_line(name); - } - g_free(machine_name); -} -/** - * apply_to_qlist(): using QMP queries QEMU for a list of - * machines and devices available, and sets the respective node - * as true. If a node is found, also all its produced and contained - * child are marked available. - * - * See qos_graph_node_set_availability() for more info - */ -static void apply_to_qlist(QList *list, bool is_machine) -{ - const QListEntry *p; - const char *name; - bool abstract; - QDict *minfo; - QObject *qobj; - QString *qstr; - QBool *qbool; - - for (p = qlist_first(list); p; p = qlist_next(p)) { - minfo = qobject_to(QDict, qlist_entry_obj(p)); - qobj = qdict_get(minfo, "name"); - qstr = qobject_to(QString, qobj); - name = qstring_get_str(qstr); - - qobj = qdict_get(minfo, "abstract"); - if (qobj) { - qbool = qobject_to(QBool, qobj); - abstract = qbool_get_bool(qbool); - } else { - abstract = false; - } - - apply_to_node(name, is_machine, abstract); - qobj = qdict_get(minfo, "alias"); - if (qobj) { - qstr = qobject_to(QString, qobj); - name = qstring_get_str(qstr); - apply_to_node(name, is_machine, abstract); - } - } -} /** * qos_set_machines_devices_available(): sets availability of qgraph @@ -129,10 +75,6 @@ static void qos_set_machines_devices_available(void) qobject_unref(response); } -static QGuestAllocator *get_machine_allocator(QOSGraphObject *obj) -{ - return obj->get_driver(obj, "memory"); -} static void restart_qemu_or_continue(char *path) { @@ -159,78 +101,6 @@ void qos_invalidate_command_line(void) old_path = NULL; } -/** - * allocate_objects(): given an array of nodes @arg, - * walks the path invoking all constructors and - * passing the corresponding parameter in order to - * continue the objects allocation. - * Once the test is reached, return the object it consumes. - * - * Since the machine and QEDGE_CONSUMED_BY nodes allocate - * memory in the constructor, g_test_queue_destroy is used so - * that after execution they can be safely free'd. (The test's - * ->before callback is also welcome to use g_test_queue_destroy). - * - * Note: as specified in walk_path() too, @arg is an array of - * char *, where arg[0] is a pointer to the command line - * string that will be used to properly start QEMU when executing - * the test, and the remaining elements represent the actual objects - * that will be allocated. - */ -static void *allocate_objects(QTestState *qts, char **path, QGuestAllocator **p_alloc) -{ - int current = 0; - QGuestAllocator *alloc; - QOSGraphObject *parent = NULL; - QOSGraphEdge *edge; - QOSGraphNode *node; - void *edge_arg; - void *obj; - - node = qos_graph_get_node(path[current]); - g_assert(node->type == QNODE_MACHINE); - - obj = qos_machine_new(node, qts); - qos_object_queue_destroy(obj); - - alloc = get_machine_allocator(obj); - if (p_alloc) { - *p_alloc = alloc; - } - - for (;;) { - if (node->type != QNODE_INTERFACE) { - qos_object_start_hw(obj); - parent = obj; - } - - /* follow edge and get object for next node constructor */ - current++; - edge = qos_graph_get_edge(path[current - 1], path[current]); - node = qos_graph_get_node(path[current]); - - if (node->type == QNODE_TEST) { - g_assert(qos_graph_edge_get_type(edge) == QEDGE_CONSUMED_BY); - return obj; - } - - switch (qos_graph_edge_get_type(edge)) { - case QEDGE_PRODUCES: - obj = parent->get_driver(parent, path[current]); - break; - - case QEDGE_CONSUMED_BY: - edge_arg = qos_graph_edge_get_arg(edge); - obj = qos_driver_new(node, obj, alloc, edge_arg); - qos_object_queue_destroy(obj); - break; - - case QEDGE_CONTAINS: - obj = parent->get_device(parent, path[current]); - break; - } - } -} /* The argument to run_one_test, which is the test function that is registered * with GTest, is a vector of strings. The first item is the initial command diff --git a/tests/rtas-test.c b/tests/qtest/rtas-test.c similarity index 100% rename from tests/rtas-test.c rename to tests/qtest/rtas-test.c diff --git a/tests/rtc-test.c b/tests/qtest/rtc-test.c similarity index 100% rename from tests/rtc-test.c rename to tests/qtest/rtc-test.c diff --git a/tests/rtl8139-test.c b/tests/qtest/rtl8139-test.c similarity index 100% rename from tests/rtl8139-test.c rename to tests/qtest/rtl8139-test.c diff --git a/tests/sdhci-test.c b/tests/qtest/sdhci-test.c similarity index 100% rename from tests/sdhci-test.c rename to tests/qtest/sdhci-test.c diff --git a/tests/spapr-phb-test.c b/tests/qtest/spapr-phb-test.c similarity index 100% rename from tests/spapr-phb-test.c rename to tests/qtest/spapr-phb-test.c diff --git a/tests/tco-test.c b/tests/qtest/tco-test.c similarity index 100% rename from tests/tco-test.c rename to tests/qtest/tco-test.c diff --git a/tests/test-arm-mptimer.c b/tests/qtest/test-arm-mptimer.c similarity index 100% rename from tests/test-arm-mptimer.c rename to tests/qtest/test-arm-mptimer.c diff --git a/tests/test-filter-mirror.c b/tests/qtest/test-filter-mirror.c similarity index 100% rename from tests/test-filter-mirror.c rename to tests/qtest/test-filter-mirror.c diff --git a/tests/test-filter-redirector.c b/tests/qtest/test-filter-redirector.c similarity index 100% rename from tests/test-filter-redirector.c rename to tests/qtest/test-filter-redirector.c diff --git a/tests/test-hmp.c b/tests/qtest/test-hmp.c similarity index 98% rename from tests/test-hmp.c rename to tests/qtest/test-hmp.c index 5029c4d2c9..f8aa5f92c5 100644 --- a/tests/test-hmp.c +++ b/tests/qtest/test-hmp.c @@ -143,7 +143,7 @@ static void add_machine_test_case(const char *mname) char *path; /* Ignore blacklisted machines that have known problems */ - if (!strcmp("xenfv", mname) || !strcmp("xenpv", mname)) { + if (!memcmp("xenfv", mname, 5) || g_str_equal("xenpv", mname)) { return; } diff --git a/tests/test-netfilter.c b/tests/qtest/test-netfilter.c similarity index 100% rename from tests/test-netfilter.c rename to tests/qtest/test-netfilter.c diff --git a/tests/test-x86-cpuid-compat.c b/tests/qtest/test-x86-cpuid-compat.c similarity index 100% rename from tests/test-x86-cpuid-compat.c rename to tests/qtest/test-x86-cpuid-compat.c diff --git a/tests/tmp105-test.c b/tests/qtest/tmp105-test.c similarity index 100% rename from tests/tmp105-test.c rename to tests/qtest/tmp105-test.c diff --git a/tests/tpm-crb-swtpm-test.c b/tests/qtest/tpm-crb-swtpm-test.c similarity index 91% rename from tests/tpm-crb-swtpm-test.c rename to tests/qtest/tpm-crb-swtpm-test.c index 2c4fb8ae29..55fdb5657d 100644 --- a/tests/tpm-crb-swtpm-test.c +++ b/tests/qtest/tpm-crb-swtpm-test.c @@ -18,6 +18,10 @@ #include "libqtest.h" #include "qemu/module.h" #include "tpm-tests.h" +#include "hw/acpi/tpm.h" + +/* Not used but needed for linking */ +uint64_t tpm_tis_base_addr = TPM_TIS_ADDR_BASE; typedef struct TestState { char *src_tpm_path; @@ -29,7 +33,8 @@ static void tpm_crb_swtpm_test(const void *data) { const TestState *ts = data; - tpm_test_swtpm_test(ts->src_tpm_path, tpm_util_crb_transfer, "tpm-crb"); + tpm_test_swtpm_test(ts->src_tpm_path, tpm_util_crb_transfer, + "tpm-crb", NULL); } static void tpm_crb_swtpm_migration_test(const void *data) @@ -37,7 +42,7 @@ static void tpm_crb_swtpm_migration_test(const void *data) const TestState *ts = data; tpm_test_swtpm_migration_test(ts->src_tpm_path, ts->dst_tpm_path, ts->uri, - tpm_util_crb_transfer, "tpm-crb"); + tpm_util_crb_transfer, "tpm-crb", NULL); } int main(int argc, char **argv) diff --git a/tests/tpm-crb-test.c b/tests/qtest/tpm-crb-test.c similarity index 98% rename from tests/tpm-crb-test.c rename to tests/qtest/tpm-crb-test.c index 632fb7fbd8..ed533900d1 100644 --- a/tests/tpm-crb-test.c +++ b/tests/qtest/tpm-crb-test.c @@ -19,6 +19,9 @@ #include "qemu/module.h" #include "tpm-emu.h" +/* Not used but needed for linking */ +uint64_t tpm_tis_base_addr = TPM_TIS_ADDR_BASE; + #define TPM_CMD "\x80\x01\x00\x00\x00\x0c\x00\x00\x01\x44\x00\x00" static void tpm_crb_test(const void *data) diff --git a/tests/tpm-emu.c b/tests/qtest/tpm-emu.c similarity index 100% rename from tests/tpm-emu.c rename to tests/qtest/tpm-emu.c diff --git a/tests/tpm-emu.h b/tests/qtest/tpm-emu.h similarity index 100% rename from tests/tpm-emu.h rename to tests/qtest/tpm-emu.h diff --git a/tests/tpm-tests.c b/tests/qtest/tpm-tests.c similarity index 91% rename from tests/tpm-tests.c rename to tests/qtest/tpm-tests.c index 6e45a0ba85..a2f2838e15 100644 --- a/tests/tpm-tests.c +++ b/tests/qtest/tpm-tests.c @@ -30,7 +30,7 @@ tpm_test_swtpm_skip(void) } void tpm_test_swtpm_test(const char *src_tpm_path, tx_func *tx, - const char *ifmodel) + const char *ifmodel, const char *machine_options) { char *args = NULL; QTestState *s; @@ -47,10 +47,11 @@ void tpm_test_swtpm_test(const char *src_tpm_path, tx_func *tx, g_assert_true(succ); args = g_strdup_printf( + "%s " "-chardev socket,id=chr,path=%s " "-tpmdev emulator,id=dev,chardev=chr " "-device %s,tpmdev=dev", - addr->u.q_unix.path, ifmodel); + machine_options ? : "", addr->u.q_unix.path, ifmodel); s = qtest_start(args); g_free(args); @@ -78,7 +79,8 @@ void tpm_test_swtpm_test(const char *src_tpm_path, tx_func *tx, void tpm_test_swtpm_migration_test(const char *src_tpm_path, const char *dst_tpm_path, const char *uri, tx_func *tx, - const char *ifmodel) + const char *ifmodel, + const char *machine_options) { gboolean succ; GPid src_tpm_pid, dst_tpm_pid; @@ -100,7 +102,7 @@ void tpm_test_swtpm_migration_test(const char *src_tpm_path, tpm_util_migration_start_qemu(&src_qemu, &dst_qemu, src_tpm_addr, dst_tpm_addr, uri, - ifmodel); + ifmodel, machine_options); tpm_util_startup(src_qemu, tx); tpm_util_pcrextend(src_qemu, tx); diff --git a/tests/tpm-tests.h b/tests/qtest/tpm-tests.h similarity index 76% rename from tests/tpm-tests.h rename to tests/qtest/tpm-tests.h index b97688fe75..a5df35ab5b 100644 --- a/tests/tpm-tests.h +++ b/tests/qtest/tpm-tests.h @@ -16,11 +16,12 @@ #include "tpm-util.h" void tpm_test_swtpm_test(const char *src_tpm_path, tx_func *tx, - const char *ifmodel); + const char *ifmodel, const char *machine_options); void tpm_test_swtpm_migration_test(const char *src_tpm_path, const char *dst_tpm_path, const char *uri, tx_func *tx, - const char *ifmodel); + const char *ifmodel, + const char *machine_options); #endif /* TESTS_TPM_TESTS_H */ diff --git a/tests/qtest/tpm-tis-device-swtpm-test.c b/tests/qtest/tpm-tis-device-swtpm-test.c new file mode 100644 index 0000000000..7b20035142 --- /dev/null +++ b/tests/qtest/tpm-tis-device-swtpm-test.c @@ -0,0 +1,76 @@ +/* + * QTest testcase for Sysbus TPM TIS talking to external swtpm and swtpm + * migration + * + * Copyright (c) 2018 IBM Corporation + * with parts borrowed from migration-test.c that is: + * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates + * + * Authors: + * Stefan Berger + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include + +#include "libqtest.h" +#include "qemu/module.h" +#include "tpm-tests.h" +#include "hw/acpi/tpm.h" + +uint64_t tpm_tis_base_addr = 0xc000000; +#define MACHINE_OPTIONS "-machine virt,gic-version=max -accel tcg" + +typedef struct TestState { + char *src_tpm_path; + char *dst_tpm_path; + char *uri; +} TestState; + +static void tpm_tis_swtpm_test(const void *data) +{ + const TestState *ts = data; + + tpm_test_swtpm_test(ts->src_tpm_path, tpm_util_tis_transfer, + "tpm-tis-device", MACHINE_OPTIONS); +} + +static void tpm_tis_swtpm_migration_test(const void *data) +{ + const TestState *ts = data; + + tpm_test_swtpm_migration_test(ts->src_tpm_path, ts->dst_tpm_path, ts->uri, + tpm_util_tis_transfer, "tpm-tis-device", + MACHINE_OPTIONS); +} + +int main(int argc, char **argv) +{ + int ret; + TestState ts = { 0 }; + + ts.src_tpm_path = g_dir_make_tmp("qemu-tpm-tis-device-swtpm-test.XXXXXX", + NULL); + ts.dst_tpm_path = g_dir_make_tmp("qemu-tpm-tis-device-swtpm-test.XXXXXX", + NULL); + ts.uri = g_strdup_printf("unix:%s/migsocket", ts.src_tpm_path); + + module_call_init(MODULE_INIT_QOM); + g_test_init(&argc, &argv, NULL); + + qtest_add_data_func("/tpm/tis-swtpm/test", &ts, tpm_tis_swtpm_test); + qtest_add_data_func("/tpm/tis-swtpm-migration/test", &ts, + tpm_tis_swtpm_migration_test); + ret = g_test_run(); + + g_rmdir(ts.dst_tpm_path); + g_free(ts.dst_tpm_path); + g_rmdir(ts.src_tpm_path); + g_free(ts.src_tpm_path); + g_free(ts.uri); + + return ret; +} diff --git a/tests/qtest/tpm-tis-device-test.c b/tests/qtest/tpm-tis-device-test.c new file mode 100644 index 0000000000..63ed36440f --- /dev/null +++ b/tests/qtest/tpm-tis-device-test.c @@ -0,0 +1,87 @@ +/* + * QTest testcase for SYSBUS TPM TIS + * + * Copyright (c) 2018 Red Hat, Inc. + * Copyright (c) 2018 IBM Corporation + * + * Authors: + * Marc-André Lureau + * Stefan Berger + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include + +#include "io/channel-socket.h" +#include "libqtest-single.h" +#include "qemu/module.h" +#include "tpm-emu.h" +#include "tpm-util.h" +#include "tpm-tis-util.h" + +/* + * As the Sysbus tpm-tis-device is instantiated on the ARM virt + * platform bus and it is the only sysbus device dynamically + * instantiated, it gets plugged at its base address + */ +uint64_t tpm_tis_base_addr = 0xc000000; + +int main(int argc, char **argv) +{ + char *tmp_path = g_dir_make_tmp("qemu-tpm-tis-device-test.XXXXXX", NULL); + GThread *thread; + TestState test; + char *args; + int ret; + + module_call_init(MODULE_INIT_QOM); + g_test_init(&argc, &argv, NULL); + + test.addr = g_new0(SocketAddress, 1); + test.addr->type = SOCKET_ADDRESS_TYPE_UNIX; + test.addr->u.q_unix.path = g_build_filename(tmp_path, "sock", NULL); + g_mutex_init(&test.data_mutex); + g_cond_init(&test.data_cond); + test.data_cond_signal = false; + + thread = g_thread_new(NULL, tpm_emu_ctrl_thread, &test); + tpm_emu_test_wait_cond(&test); + + args = g_strdup_printf( + "-machine virt,gic-version=max -accel tcg " + "-chardev socket,id=chr,path=%s " + "-tpmdev emulator,id=dev,chardev=chr " + "-device tpm-tis-device,tpmdev=dev", + test.addr->u.q_unix.path); + qtest_start(args); + + qtest_add_data_func("/tpm-tis/test_check_localities", &test, + tpm_tis_test_check_localities); + + qtest_add_data_func("/tpm-tis/test_check_access_reg", &test, + tpm_tis_test_check_access_reg); + + qtest_add_data_func("/tpm-tis/test_check_access_reg_seize", &test, + tpm_tis_test_check_access_reg_seize); + + qtest_add_data_func("/tpm-tis/test_check_access_reg_release", &test, + tpm_tis_test_check_access_reg_release); + + qtest_add_data_func("/tpm-tis/test_check_transmit", &test, + tpm_tis_test_check_transmit); + + ret = g_test_run(); + + qtest_end(); + + g_thread_join(thread); + g_unlink(test.addr->u.q_unix.path); + qapi_free_SocketAddress(test.addr); + g_rmdir(tmp_path); + g_free(tmp_path); + g_free(args); + return ret; +} diff --git a/tests/tpm-tis-swtpm-test.c b/tests/qtest/tpm-tis-swtpm-test.c similarity index 93% rename from tests/tpm-tis-swtpm-test.c rename to tests/qtest/tpm-tis-swtpm-test.c index 9f58a3a92b..90131cb3c4 100644 --- a/tests/tpm-tis-swtpm-test.c +++ b/tests/qtest/tpm-tis-swtpm-test.c @@ -18,6 +18,9 @@ #include "libqtest.h" #include "qemu/module.h" #include "tpm-tests.h" +#include "hw/acpi/tpm.h" + +uint64_t tpm_tis_base_addr = TPM_TIS_ADDR_BASE; typedef struct TestState { char *src_tpm_path; @@ -29,7 +32,8 @@ static void tpm_tis_swtpm_test(const void *data) { const TestState *ts = data; - tpm_test_swtpm_test(ts->src_tpm_path, tpm_util_tis_transfer, "tpm-tis"); + tpm_test_swtpm_test(ts->src_tpm_path, tpm_util_tis_transfer, + "tpm-tis", NULL); } static void tpm_tis_swtpm_migration_test(const void *data) @@ -37,7 +41,7 @@ static void tpm_tis_swtpm_migration_test(const void *data) const TestState *ts = data; tpm_test_swtpm_migration_test(ts->src_tpm_path, ts->dst_tpm_path, ts->uri, - tpm_util_tis_transfer, "tpm-tis"); + tpm_util_tis_transfer, "tpm-tis", NULL); } int main(int argc, char **argv) diff --git a/tests/qtest/tpm-tis-test.c b/tests/qtest/tpm-tis-test.c new file mode 100644 index 0000000000..79ffbc943e --- /dev/null +++ b/tests/qtest/tpm-tis-test.c @@ -0,0 +1,80 @@ +/* + * QTest testcase for ISA TPM TIS + * + * Copyright (c) 2018 Red Hat, Inc. + * Copyright (c) 2018 IBM Corporation + * + * Authors: + * Marc-André Lureau + * Stefan Berger + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include + +#include "hw/acpi/tpm.h" +#include "io/channel-socket.h" +#include "libqtest-single.h" +#include "qemu/module.h" +#include "tpm-emu.h" +#include "tpm-tis-util.h" + +uint64_t tpm_tis_base_addr = TPM_TIS_ADDR_BASE; + +int main(int argc, char **argv) +{ + int ret; + char *args, *tmp_path = g_dir_make_tmp("qemu-tpm-tis-test.XXXXXX", NULL); + GThread *thread; + TestState test; + + module_call_init(MODULE_INIT_QOM); + g_test_init(&argc, &argv, NULL); + + test.addr = g_new0(SocketAddress, 1); + test.addr->type = SOCKET_ADDRESS_TYPE_UNIX; + test.addr->u.q_unix.path = g_build_filename(tmp_path, "sock", NULL); + g_mutex_init(&test.data_mutex); + g_cond_init(&test.data_cond); + test.data_cond_signal = false; + + thread = g_thread_new(NULL, tpm_emu_ctrl_thread, &test); + tpm_emu_test_wait_cond(&test); + + args = g_strdup_printf( + "-chardev socket,id=chr,path=%s " + "-tpmdev emulator,id=dev,chardev=chr " + "-device tpm-tis,tpmdev=dev", + test.addr->u.q_unix.path); + qtest_start(args); + + qtest_add_data_func("/tpm-tis/test_check_localities", &test, + tpm_tis_test_check_localities); + + qtest_add_data_func("/tpm-tis/test_check_access_reg", &test, + tpm_tis_test_check_access_reg); + + qtest_add_data_func("/tpm-tis/test_check_access_reg_seize", &test, + tpm_tis_test_check_access_reg_seize); + + qtest_add_data_func("/tpm-tis/test_check_access_reg_release", &test, + tpm_tis_test_check_access_reg_release); + + qtest_add_data_func("/tpm-tis/test_check_transmit", &test, + tpm_tis_test_check_transmit); + + ret = g_test_run(); + + qtest_end(); + + g_thread_join(thread); + g_unlink(test.addr->u.q_unix.path); + qapi_free_SocketAddress(test.addr); + g_rmdir(tmp_path); + g_free(tmp_path); + g_free(args); + return ret; +} diff --git a/tests/tpm-tis-test.c b/tests/qtest/tpm-tis-util.c similarity index 83% rename from tests/tpm-tis-test.c rename to tests/qtest/tpm-tis-util.c index dcf30e05b7..9aff503fd8 100644 --- a/tests/tpm-tis-test.c +++ b/tests/qtest/tpm-tis-util.c @@ -1,5 +1,6 @@ /* - * QTest testcase for TPM TIS + * QTest testcase for TPM TIS: common test functions used for both + * the ISA and SYSBUS devices * * Copyright (c) 2018 Red Hat, Inc. * Copyright (c) 2018 IBM Corporation @@ -20,9 +21,8 @@ #include "libqtest-single.h" #include "qemu/module.h" #include "tpm-emu.h" - -#define TIS_REG(LOCTY, REG) \ - (TPM_TIS_ADDR_BASE + ((LOCTY) << 12) + REG) +#include "tpm-util.h" +#include "tpm-tis-util.h" #define DEBUG_TIS_TEST 0 @@ -42,7 +42,7 @@ static const uint8_t TPM_CMD[12] = "\x80\x01\x00\x00\x00\x0c\x00\x00\x01\x44\x00\x00"; -static void tpm_tis_test_check_localities(const void *data) +void tpm_tis_test_check_localities(const void *data) { uint8_t locty; uint8_t access; @@ -72,7 +72,7 @@ static void tpm_tis_test_check_localities(const void *data) } } -static void tpm_tis_test_check_access_reg(const void *data) +void tpm_tis_test_check_access_reg(const void *data) { uint8_t locty; uint8_t access; @@ -103,7 +103,7 @@ static void tpm_tis_test_check_access_reg(const void *data) /* * Test case for seizing access by a higher number locality */ -static void tpm_tis_test_check_access_reg_seize(const void *data) +void tpm_tis_test_check_access_reg_seize(const void *data) { int locty, l; uint8_t access; @@ -136,8 +136,10 @@ static void tpm_tis_test_check_access_reg_seize(const void *data) /* try to request use from 'l' */ writeb(TIS_REG(l, TPM_TIS_REG_ACCESS), TPM_TIS_ACCESS_REQUEST_USE); - /* requesting use from 'l' was not possible; - we must see REQUEST_USE and possibly PENDING_REQUEST */ + /* + * requesting use from 'l' was not possible; + * we must see REQUEST_USE and possibly PENDING_REQUEST + */ access = readb(TIS_REG(l, TPM_TIS_REG_ACCESS)); DPRINTF_ACCESS; g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | @@ -145,8 +147,10 @@ static void tpm_tis_test_check_access_reg_seize(const void *data) pending_request_flag | TPM_TIS_ACCESS_TPM_ESTABLISHMENT); - /* locality 'locty' must be unchanged; - we must see PENDING_REQUEST */ + /* + * locality 'locty' must be unchanged; + * we must see PENDING_REQUEST + */ access = readb(TIS_REG(locty, TPM_TIS_REG_ACCESS)); g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | TPM_TIS_ACCESS_ACTIVE_LOCALITY | @@ -170,19 +174,25 @@ static void tpm_tis_test_check_access_reg_seize(const void *data) TPM_TIS_ACCESS_PENDING_REQUEST | TPM_TIS_ACCESS_TPM_ESTABLISHMENT); - /* on the next loop we will have a PENDING_REQUEST flag - set for locality 'l' */ + /* + * on the next loop we will have a PENDING_REQUEST flag + * set for locality 'l' + */ pending_request_flag = TPM_TIS_ACCESS_PENDING_REQUEST; } - /* higher localities can 'seize' access but not 'request use'; - note: this will activate first l+1, then l+2 etc. */ + /* + * higher localities can 'seize' access but not 'request use'; + * note: this will activate first l+1, then l+2 etc. + */ for (l = locty + 1; l < TPM_TIS_NUM_LOCALITIES - 1; l++) { /* try to 'request use' from 'l' */ writeb(TIS_REG(l, TPM_TIS_REG_ACCESS), TPM_TIS_ACCESS_REQUEST_USE); - /* requesting use from 'l' was not possible; we should see - REQUEST_USE and may see PENDING_REQUEST */ + /* + * requesting use from 'l' was not possible; we should see + * REQUEST_USE and may see PENDING_REQUEST + */ access = readb(TIS_REG(l, TPM_TIS_REG_ACCESS)); DPRINTF_ACCESS; g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | @@ -190,8 +200,10 @@ static void tpm_tis_test_check_access_reg_seize(const void *data) pending_request_flag | TPM_TIS_ACCESS_TPM_ESTABLISHMENT); - /* locality 'l-1' must be unchanged; we should always - see PENDING_REQUEST from 'l' requesting access */ + /* + * locality 'l-1' must be unchanged; we should always + * see PENDING_REQUEST from 'l' requesting access + */ access = readb(TIS_REG(l - 1, TPM_TIS_REG_ACCESS)); DPRINTF_ACCESS; g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS | @@ -228,9 +240,11 @@ static void tpm_tis_test_check_access_reg_seize(const void *data) TPM_TIS_ACCESS_TPM_ESTABLISHMENT); } - /* PENDING_REQUEST will not be set if locty = 0 since all localities - were active; in case of locty = 1, locality 0 will be active - but no PENDING_REQUEST anywhere */ + /* + * PENDING_REQUEST will not be set if locty = 0 since all localities + * were active; in case of locty = 1, locality 0 will be active + * but no PENDING_REQUEST anywhere + */ if (locty <= 1) { pending_request_flag = 0; } @@ -282,7 +296,7 @@ static void tpm_tis_test_check_access_reg_seize(const void *data) /* * Test case for getting access when higher number locality relinquishes access */ -static void tpm_tis_test_check_access_reg_release(const void *data) +void tpm_tis_test_check_access_reg_release(const void *data) { int locty, l; uint8_t access; @@ -308,8 +322,10 @@ static void tpm_tis_test_check_access_reg_release(const void *data) if (l == locty) { continue; } - /* request use of locality 'l' -- we MUST see REQUEST USE and - may see PENDING_REQUEST */ + /* + * request use of locality 'l' -- we MUST see REQUEST USE and + * may see PENDING_REQUEST + */ writeb(TIS_REG(l, TPM_TIS_REG_ACCESS), TPM_TIS_ACCESS_REQUEST_USE); access = readb(TIS_REG(l, TPM_TIS_REG_ACCESS)); DPRINTF_ACCESS; @@ -322,8 +338,10 @@ static void tpm_tis_test_check_access_reg_release(const void *data) /* release locality 'locty' */ writeb(TIS_REG(locty, TPM_TIS_REG_ACCESS), TPM_TIS_ACCESS_ACTIVE_LOCALITY); - /* highest locality should now be active; release it and make sure the - next higest locality is active afterwards */ + /* + * highest locality should now be active; release it and make sure the + * next higest locality is active afterwards + */ for (l = TPM_TIS_NUM_LOCALITIES - 2; l >= 0; l--) { if (l == locty) { continue; @@ -353,7 +371,7 @@ static void tpm_tis_test_check_access_reg_release(const void *data) /* * Test case for transmitting packets */ -static void tpm_tis_test_check_transmit(const void *data) +void tpm_tis_test_check_transmit(const void *data) { const TestState *s = data; uint8_t access; @@ -431,58 +449,3 @@ static void tpm_tis_test_check_transmit(const void *data) writeb(TIS_REG(0, TPM_TIS_REG_ACCESS), TPM_TIS_ACCESS_ACTIVE_LOCALITY); access = readb(TIS_REG(0, TPM_TIS_REG_ACCESS)); } - -int main(int argc, char **argv) -{ - int ret; - char *args, *tmp_path = g_dir_make_tmp("qemu-tpm-tis-test.XXXXXX", NULL); - GThread *thread; - TestState test; - - module_call_init(MODULE_INIT_QOM); - g_test_init(&argc, &argv, NULL); - - test.addr = g_new0(SocketAddress, 1); - test.addr->type = SOCKET_ADDRESS_TYPE_UNIX; - test.addr->u.q_unix.path = g_build_filename(tmp_path, "sock", NULL); - g_mutex_init(&test.data_mutex); - g_cond_init(&test.data_cond); - test.data_cond_signal = false; - - thread = g_thread_new(NULL, tpm_emu_ctrl_thread, &test); - tpm_emu_test_wait_cond(&test); - - args = g_strdup_printf( - "-chardev socket,id=chr,path=%s " - "-tpmdev emulator,id=dev,chardev=chr " - "-device tpm-tis,tpmdev=dev", - test.addr->u.q_unix.path); - qtest_start(args); - - qtest_add_data_func("/tpm-tis/test_check_localities", &test, - tpm_tis_test_check_localities); - - qtest_add_data_func("/tpm-tis/test_check_access_reg", &test, - tpm_tis_test_check_access_reg); - - qtest_add_data_func("/tpm-tis/test_check_access_reg_seize", &test, - tpm_tis_test_check_access_reg_seize); - - qtest_add_data_func("/tpm-tis/test_check_access_reg_release", &test, - tpm_tis_test_check_access_reg_release); - - qtest_add_data_func("/tpm-tis/test_check_transmit", &test, - tpm_tis_test_check_transmit); - - ret = g_test_run(); - - qtest_end(); - - g_thread_join(thread); - g_unlink(test.addr->u.q_unix.path); - qapi_free_SocketAddress(test.addr); - g_rmdir(tmp_path); - g_free(tmp_path); - g_free(args); - return ret; -} diff --git a/tests/qtest/tpm-tis-util.h b/tests/qtest/tpm-tis-util.h new file mode 100644 index 0000000000..d10efe86ae --- /dev/null +++ b/tests/qtest/tpm-tis-util.h @@ -0,0 +1,23 @@ +/* + * QTest TPM TIS: Common test functions used for both the + * ISA and SYSBUS devices + * + * Copyright (c) 2018 IBM Corporation + * + * Authors: + * Stefan Berger + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef TESTS_TPM_TIS_UTIL_H +#define TESTS_TPM_TIS_UTIL_H + +void tpm_tis_test_check_localities(const void *data); +void tpm_tis_test_check_access_reg(const void *data); +void tpm_tis_test_check_access_reg_seize(const void *data); +void tpm_tis_test_check_access_reg_release(const void *data); +void tpm_tis_test_check_transmit(const void *data); + +#endif /* TESTS_TPM_TIS_UTIL_H */ diff --git a/tests/tpm-util.c b/tests/qtest/tpm-util.c similarity index 97% rename from tests/tpm-util.c rename to tests/qtest/tpm-util.c index e08b137651..34efae8f18 100644 --- a/tests/tpm-util.c +++ b/tests/qtest/tpm-util.c @@ -19,9 +19,6 @@ #include "tpm-util.h" #include "qapi/qmp/qdict.h" -#define TIS_REG(LOCTY, REG) \ - (TPM_TIS_ADDR_BASE + ((LOCTY) << 12) + REG) - void tpm_util_crb_transfer(QTestState *s, const unsigned char *req, size_t req_size, unsigned char *rsp, size_t rsp_size) @@ -258,23 +255,27 @@ void tpm_util_migration_start_qemu(QTestState **src_qemu, SocketAddress *src_tpm_addr, SocketAddress *dst_tpm_addr, const char *miguri, - const char *ifmodel) + const char *ifmodel, + const char *machine_options) { char *src_qemu_args, *dst_qemu_args; src_qemu_args = g_strdup_printf( + "%s " "-chardev socket,id=chr,path=%s " "-tpmdev emulator,id=dev,chardev=chr " "-device %s,tpmdev=dev ", - src_tpm_addr->u.q_unix.path, ifmodel); + machine_options ? : "", src_tpm_addr->u.q_unix.path, ifmodel); *src_qemu = qtest_init(src_qemu_args); dst_qemu_args = g_strdup_printf( + "%s " "-chardev socket,id=chr,path=%s " "-tpmdev emulator,id=dev,chardev=chr " "-device %s,tpmdev=dev " "-incoming %s", + machine_options ? : "", dst_tpm_addr->u.q_unix.path, ifmodel, miguri); diff --git a/tests/tpm-util.h b/tests/qtest/tpm-util.h similarity index 88% rename from tests/tpm-util.h rename to tests/qtest/tpm-util.h index 5755698ad2..3b97d69017 100644 --- a/tests/tpm-util.h +++ b/tests/qtest/tpm-util.h @@ -15,6 +15,11 @@ #include "io/channel-socket.h" +extern uint64_t tpm_tis_base_addr; + +#define TIS_REG(LOCTY, REG) \ + (tpm_tis_base_addr + ((LOCTY) << 12) + REG) + typedef void (tx_func)(QTestState *s, const unsigned char *req, size_t req_size, unsigned char *rsp, size_t rsp_size); @@ -44,7 +49,8 @@ void tpm_util_migration_start_qemu(QTestState **src_qemu, SocketAddress *src_tpm_addr, SocketAddress *dst_tpm_addr, const char *miguri, - const char *ifmodel); + const char *ifmodel, + const char *machine_options); void tpm_util_wait_for_migration_complete(QTestState *who); diff --git a/tests/qtest/tulip-test.c b/tests/qtest/tulip-test.c new file mode 100644 index 0000000000..2fb6c4d5a7 --- /dev/null +++ b/tests/qtest/tulip-test.c @@ -0,0 +1,91 @@ +/* + * QTest testcase for DEC/Intel Tulip 21143 + * + * Copyright (c) 2020 Li Qiang + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "qemu/module.h" +#include "libqos/qgraph.h" +#include "libqos/pci.h" +#include "qemu/bitops.h" +#include "hw/net/tulip.h" + +typedef struct QTulip_pci QTulip_pci; + +struct QTulip_pci { + QOSGraphObject obj; + QPCIDevice dev; +}; + +static void *tulip_pci_get_driver(void *obj, const char *interface) +{ + QTulip_pci *tulip_pci = obj; + + if (!g_strcmp0(interface, "pci-device")) { + return &tulip_pci->dev; + } + + fprintf(stderr, "%s not present in tulip_pci\n", interface); + g_assert_not_reached(); +} + +static void *tulip_pci_create(void *pci_bus, QGuestAllocator *alloc, void *addr) +{ + QTulip_pci *tulip_pci = g_new0(QTulip_pci, 1); + QPCIBus *bus = pci_bus; + + qpci_device_init(&tulip_pci->dev, bus, addr); + tulip_pci->obj.get_driver = tulip_pci_get_driver; + + return &tulip_pci->obj; +} + +static void tulip_large_tx(void *obj, void *data, QGuestAllocator *alloc) +{ + QTulip_pci *tulip_pci = obj; + QPCIDevice *dev = &tulip_pci->dev; + QPCIBar bar; + struct tulip_descriptor context; + char guest_data[4096]; + uint64_t context_pa; + uint64_t guest_pa; + + qpci_device_enable(dev); + bar = qpci_iomap(dev, 0, NULL); + context_pa = guest_alloc(alloc, sizeof(context)); + guest_pa = guest_alloc(alloc, 4096); + memset(guest_data, 'A', sizeof(guest_data)); + context.status = TDES0_OWN; + context.control = TDES1_BUF2_SIZE_MASK << TDES1_BUF2_SIZE_SHIFT | + TDES1_BUF1_SIZE_MASK << TDES1_BUF1_SIZE_SHIFT; + context.buf_addr2 = guest_pa; + context.buf_addr1 = guest_pa; + + qtest_memwrite(dev->bus->qts, context_pa, &context, sizeof(context)); + qtest_memwrite(dev->bus->qts, guest_pa, guest_data, sizeof(guest_data)); + qpci_io_writel(dev, bar, 0x20, context_pa); + qpci_io_writel(dev, bar, 0x30, CSR6_ST); + guest_free(alloc, context_pa); + guest_free(alloc, guest_pa); +} + +static void tulip_register_nodes(void) +{ + QOSGraphEdgeOptions opts = { + .extra_device_opts = "addr=04.0", + }; + add_qpci_address(&opts, &(QPCIAddress) { .devfn = QPCI_DEVFN(4, 0) }); + + qos_node_create_driver("tulip", tulip_pci_create); + qos_node_consumes("tulip", "pci-bus", &opts); + qos_node_produces("tulip", "pci-device"); + + qos_add_test("tulip_large_tx", "tulip", tulip_large_tx, NULL); +} + +libqos_init(tulip_register_nodes); diff --git a/tests/usb-hcd-ehci-test.c b/tests/qtest/usb-hcd-ehci-test.c similarity index 100% rename from tests/usb-hcd-ehci-test.c rename to tests/qtest/usb-hcd-ehci-test.c diff --git a/tests/usb-hcd-ohci-test.c b/tests/qtest/usb-hcd-ohci-test.c similarity index 100% rename from tests/usb-hcd-ohci-test.c rename to tests/qtest/usb-hcd-ohci-test.c diff --git a/tests/usb-hcd-uhci-test.c b/tests/qtest/usb-hcd-uhci-test.c similarity index 100% rename from tests/usb-hcd-uhci-test.c rename to tests/qtest/usb-hcd-uhci-test.c diff --git a/tests/usb-hcd-xhci-test.c b/tests/qtest/usb-hcd-xhci-test.c similarity index 100% rename from tests/usb-hcd-xhci-test.c rename to tests/qtest/usb-hcd-xhci-test.c diff --git a/tests/vhost-user-test.c b/tests/qtest/vhost-user-test.c similarity index 98% rename from tests/vhost-user-test.c rename to tests/qtest/vhost-user-test.c index 91ea373ba5..9ee0f1e4fd 100644 --- a/tests/vhost-user-test.c +++ b/tests/qtest/vhost-user-test.c @@ -499,7 +499,7 @@ static TestServer *test_server_new(const gchar *name) return server; } -static void chr_event(void *opaque, int event) +static void chr_event(void *opaque, QEMUChrEvent event) { TestServer *s = opaque; @@ -707,9 +707,9 @@ static void test_read_guest_mem(void *obj, void *arg, QGuestAllocator *alloc) static void test_migrate(void *obj, void *arg, QGuestAllocator *alloc) { TestServer *s = arg; - TestServer *dest = test_server_new("dest"); - GString *dest_cmdline = g_string_new(qos_get_current_command_line()); - char *uri = g_strdup_printf("%s%s", "unix:", dest->mig_path); + TestServer *dest; + GString *dest_cmdline; + char *uri; QTestState *to; GSource *source; QDict *rsp; @@ -720,6 +720,10 @@ static void test_migrate(void *obj, void *arg, QGuestAllocator *alloc) return; } + dest = test_server_new("dest"); + dest_cmdline = g_string_new(qos_get_current_command_line()); + uri = g_strdup_printf("%s%s", "unix:", dest->mig_path); + size = get_log_size(s); g_assert_cmpint(size, ==, (256 * 1024 * 1024) / (VHOST_LOG_PAGE * 8)); @@ -778,6 +782,7 @@ static void test_migrate(void *obj, void *arg, QGuestAllocator *alloc) qtest_quit(to); test_server_free(dest); g_free(uri); + g_string_free(dest_cmdline, true); } static void wait_for_rings_started(TestServer *s, size_t count) diff --git a/tests/virtio-9p-test.c b/tests/qtest/virtio-9p-test.c similarity index 81% rename from tests/virtio-9p-test.c rename to tests/qtest/virtio-9p-test.c index e7b58e3a0c..2167322985 100644 --- a/tests/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" : ""; } @@ -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/virtio-blk-test.c b/tests/qtest/virtio-blk-test.c similarity index 100% rename from tests/virtio-blk-test.c rename to tests/qtest/virtio-blk-test.c diff --git a/tests/virtio-ccw-test.c b/tests/qtest/virtio-ccw-test.c similarity index 100% rename from tests/virtio-ccw-test.c rename to tests/qtest/virtio-ccw-test.c diff --git a/tests/virtio-net-test.c b/tests/qtest/virtio-net-test.c similarity index 100% rename from tests/virtio-net-test.c rename to tests/qtest/virtio-net-test.c diff --git a/tests/virtio-rng-test.c b/tests/qtest/virtio-rng-test.c similarity index 100% rename from tests/virtio-rng-test.c rename to tests/qtest/virtio-rng-test.c diff --git a/tests/virtio-scsi-test.c b/tests/qtest/virtio-scsi-test.c similarity index 100% rename from tests/virtio-scsi-test.c rename to tests/qtest/virtio-scsi-test.c diff --git a/tests/virtio-serial-test.c b/tests/qtest/virtio-serial-test.c similarity index 100% rename from tests/virtio-serial-test.c rename to tests/qtest/virtio-serial-test.c diff --git a/tests/virtio-test.c b/tests/qtest/virtio-test.c similarity index 100% rename from tests/virtio-test.c rename to tests/qtest/virtio-test.c diff --git a/tests/vmgenid-test.c b/tests/qtest/vmgenid-test.c similarity index 99% rename from tests/vmgenid-test.c rename to tests/qtest/vmgenid-test.c index 85d8e6463e..efba76e716 100644 --- a/tests/vmgenid-test.c +++ b/tests/qtest/vmgenid-test.c @@ -109,7 +109,7 @@ static void read_guid_from_monitor(QTestState *qts, QemuUUID *guid) static char disk[] = "tests/vmgenid-test-disk-XXXXXX"; #define GUID_CMD(guid) \ - "-machine accel=kvm:tcg " \ + "-accel kvm -accel tcg " \ "-device vmgenid,id=testvgid,guid=%s " \ "-drive id=hd0,if=none,file=%s,format=raw " \ "-device ide-hd,drive=hd0 ", guid, disk diff --git a/tests/vmxnet3-test.c b/tests/qtest/vmxnet3-test.c similarity index 100% rename from tests/vmxnet3-test.c rename to tests/qtest/vmxnet3-test.c diff --git a/tests/wdt_ib700-test.c b/tests/qtest/wdt_ib700-test.c similarity index 100% rename from tests/wdt_ib700-test.c rename to tests/qtest/wdt_ib700-test.c diff --git a/tests/rcutorture.c b/tests/rcutorture.c index 49311c82ea..732f03abda 100644 --- a/tests/rcutorture.c +++ b/tests/rcutorture.c @@ -65,8 +65,6 @@ #include "qemu/rcu.h" #include "qemu/thread.h" -long long n_reads = 0LL; -long n_updates = 0L; int nthreadsrunning; #define GOFLAG_INIT 0 @@ -78,11 +76,20 @@ static volatile int goflag = GOFLAG_INIT; #define RCU_READ_RUN 1000 #define NR_THREADS 100 -static QemuMutex counts_mutex; static QemuThread threads[NR_THREADS]; static struct rcu_reader_data *data[NR_THREADS]; static int n_threads; +/* + * Statistical counts + * + * These are the sum of local counters at the end of a run. + * Updates are protected by a mutex. + */ +static QemuMutex counts_mutex; +long long n_reads = 0LL; +long n_updates = 0L; + static void create_thread(void *(*func)(void *)) { if (n_threads >= NR_THREADS) { @@ -223,15 +230,15 @@ static void uperftest(int nupdaters, int duration) #define RCU_STRESS_PIPE_LEN 10 struct rcu_stress { - int pipe_count; + int age; /* how many update cycles while not rcu_stress_current */ int mbtest; }; struct rcu_stress rcu_stress_array[RCU_STRESS_PIPE_LEN] = { { 0 } }; struct rcu_stress *rcu_stress_current; -int rcu_stress_idx; - int n_mberror; + +/* Updates protected by counts_mutex */ long long rcu_stress_count[RCU_STRESS_PIPE_LEN + 1]; @@ -253,7 +260,7 @@ static void *rcu_read_stress_test(void *arg) while (goflag == GOFLAG_RUN) { rcu_read_lock(); p = atomic_rcu_read(&rcu_stress_current); - if (p->mbtest == 0) { + if (atomic_read(&p->mbtest) == 0) { n_mberror++; } rcu_read_lock(); @@ -261,7 +268,7 @@ static void *rcu_read_stress_test(void *arg) garbage++; } rcu_read_unlock(); - pc = p->pipe_count; + pc = atomic_read(&p->age); rcu_read_unlock(); if ((pc > RCU_STRESS_PIPE_LEN) || (pc < 0)) { pc = RCU_STRESS_PIPE_LEN; @@ -280,32 +287,52 @@ static void *rcu_read_stress_test(void *arg) return NULL; } +/* + * Stress Test Updater + * + * The updater cycles around updating rcu_stress_current to point at + * one of the rcu_stress_array_entries and resets it's age. It + * then increments the age of all the other entries. The age + * will be read under an rcu_read_lock() and distribution of values + * calculated. The final result gives an indication of how many + * previously current rcu_stress entries are in flight until the RCU + * cycle complete. + */ static void *rcu_update_stress_test(void *arg) { - int i; - struct rcu_stress *p; + int i, rcu_stress_idx = 0; + struct rcu_stress *cp = atomic_read(&rcu_stress_current); rcu_register_thread(); - *(struct rcu_reader_data **)arg = &rcu_reader; + while (goflag == GOFLAG_INIT) { g_usleep(1000); } + while (goflag == GOFLAG_RUN) { - i = rcu_stress_idx + 1; - if (i >= RCU_STRESS_PIPE_LEN) { - i = 0; + struct rcu_stress *p; + rcu_stress_idx++; + if (rcu_stress_idx >= RCU_STRESS_PIPE_LEN) { + rcu_stress_idx = 0; } - p = &rcu_stress_array[i]; - p->mbtest = 0; + p = &rcu_stress_array[rcu_stress_idx]; + /* catching up with ourselves would be a bug */ + assert(p != cp); + atomic_set(&p->mbtest, 0); smp_mb(); - p->pipe_count = 0; - p->mbtest = 1; + atomic_set(&p->age, 0); + atomic_set(&p->mbtest, 1); atomic_rcu_set(&rcu_stress_current, p); - rcu_stress_idx = i; + cp = p; + /* + * New RCU structure is now live, update pipe counts on old + * ones. + */ for (i = 0; i < RCU_STRESS_PIPE_LEN; i++) { if (i != rcu_stress_idx) { - rcu_stress_array[i].pipe_count++; + atomic_set(&rcu_stress_array[i].age, + rcu_stress_array[i].age + 1); } } synchronize_rcu(); @@ -338,7 +365,7 @@ static void stresstest(int nreaders, int duration) int i; rcu_stress_current = &rcu_stress_array[0]; - rcu_stress_current->pipe_count = 0; + rcu_stress_current->age = 0; rcu_stress_current->mbtest = 1; for (i = 0; i < nreaders; i++) { create_thread(rcu_read_stress_test); @@ -368,7 +395,7 @@ static void gtest_stress(int nreaders, int duration) int i; rcu_stress_current = &rcu_stress_array[0]; - rcu_stress_current->pipe_count = 0; + rcu_stress_current->age = 0; rcu_stress_current->mbtest = 1; for (i = 0; i < nreaders; i++) { create_thread(rcu_read_stress_test); @@ -413,7 +440,8 @@ static void gtest_stress_10_5(void) static void usage(int argc, char *argv[]) { - fprintf(stderr, "Usage: %s [nreaders [ perf | stress ] ]\n", argv[0]); + fprintf(stderr, "Usage: %s [nreaders [ [r|u]perf | stress [duration]]\n", + argv[0]); exit(-1); } diff --git a/tests/requirements.txt b/tests/requirements.txt index a2a587223a..f9c84b4ba1 100644 --- a/tests/requirements.txt +++ b/tests/requirements.txt @@ -1,4 +1,5 @@ # Add Python module requirements, one per line, to be installed # in the tests/venv Python virtual environment. For more info, # refer to: https://pip.pypa.io/en/stable/user_guide/#id1 -avocado-framework==72.0 +avocado-framework==76.0 +pycdlib==1.9.0 diff --git a/tests/tcg/Makefile.prereqs b/tests/tcg/Makefile.prereqs index 7494b31b95..9a29604a83 100644 --- a/tests/tcg/Makefile.prereqs +++ b/tests/tcg/Makefile.prereqs @@ -13,6 +13,6 @@ DOCKER_IMAGE:= ifneq ($(DOCKER_IMAGE),) build-tcg-tests-$(PROBE_TARGET): docker-image-$(DOCKER_IMAGE) +endif $(BUILD_DIR)/tests/tcg/config_$(PROBE_TARGET).mak: config-host.mak config-host.mak: $(SRC_PATH)/tests/tcg/configure.sh -endif diff --git a/tests/tcg/Makefile.target b/tests/tcg/Makefile.target index 3c7421a356..b3cff3cad1 100644 --- a/tests/tcg/Makefile.target +++ b/tests/tcg/Makefile.target @@ -79,7 +79,7 @@ QEMU_OPTS= # If TCG debugging is enabled things are a lot slower ifeq ($(CONFIG_DEBUG_TCG),y) -TIMEOUT=45 +TIMEOUT=60 else TIMEOUT=15 endif @@ -137,7 +137,7 @@ PLUGINS=$(notdir $(wildcard $(PLUGIN_DIR)/*.so)) $(foreach p,$(PLUGINS), \ $(foreach t,$(TESTS),\ $(eval run-plugin-$(t)-with-$(p): $t $p) \ - $(eval run-plugin-$(t)-with-$(p): TIMEOUT=30) \ + $(eval run-plugin-$(t)-with-$(p): TIMEOUT=60) \ $(eval RUN_TESTS+=run-plugin-$(t)-with-$(p)))) endif diff --git a/tests/tcg/aarch64/Makefile.softmmu-target b/tests/tcg/aarch64/Makefile.softmmu-target index 950dbb4bac..71f72cfbe3 100644 --- a/tests/tcg/aarch64/Makefile.softmmu-target +++ b/tests/tcg/aarch64/Makefile.softmmu-target @@ -31,7 +31,16 @@ LDFLAGS+=-static -nostdlib $(CRT_OBJS) $(MINILIB_OBJS) -lgcc memory: CFLAGS+=-DCHECK_UNALIGNED=1 # Running -QEMU_OPTS+=-M virt -cpu max -display none -semihosting-config enable=on,target=native,chardev=output -kernel +QEMU_BASE_MACHINE=-M virt -cpu max -display none +QEMU_OPTS+=$(QEMU_BASE_MACHINE) -semihosting-config enable=on,target=native,chardev=output -kernel + +# console test is manual only +QEMU_SEMIHOST=-chardev stdio,mux=on,id=stdio0 -semihosting-config enable=on,chardev=stdio0 -mon chardev=stdio0,mode=readline +run-semiconsole: QEMU_OPTS=$(QEMU_BASE_MACHINE) $(QEMU_SEMIHOST) -kernel +run-semiconsole: semiconsole + $(call skip-test, $<, "MANUAL ONLY") +run-plugin-semiconsole-with-%: semiconsole + $(call skip-test, $<, "MANUAL ONLY") # Simple Record/Replay Test .PHONY: memory-record @@ -53,3 +62,14 @@ run-memory-replay: memory-replay run-memory-record "$< on $(TARGET_NAME)") EXTRA_TESTS+=memory-record memory-replay + +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") +run-plugin-pauth-3-with-%: + $(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 96d2321045..d99b2a9ece 100644 --- a/tests/tcg/aarch64/Makefile.target +++ b/tests/tcg/aarch64/Makefile.target @@ -18,8 +18,11 @@ run-fcvt: fcvt $(call diff-out,$<,$(AARCH64_SRC)/fcvt.ref) # Pauth Tests -AARCH64_TESTS += pauth-1 pauth-2 +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 @@ -32,4 +35,43 @@ run-plugin-semihosting-with-%: $(call strip-plugin,$<) 2> $<.err, \ "$< on $(TARGET_NAME) with $*") +AARCH64_TESTS += semiconsole +run-semiconsole: semiconsole + $(call skip-test, $<, "MANUAL ONLY") + +run-plugin-semiconsole-with-%: + $(call skip-test, $<, "MANUAL ONLY") + +ifneq ($(DOCKER_IMAGE)$(CROSS_CC_HAS_SVE),) +# System Registers Tests +AARCH64_TESTS += sysregs +sysregs: CFLAGS+=-march=armv8.1-a+sve + +# SVE ioctl test +AARCH64_TESTS += sve-ioctls +sve-ioctls: CFLAGS+=-march=armv8.1-a+sve + +ifneq ($(HAVE_GDB_BIN),) +GDB_SCRIPT=$(SRC_PATH)/tests/guest-debug/run-test.py + +AARCH64_TESTS += gdbstub-sysregs gdbstub-sve-ioctls + +.PHONY: gdbstub-sysregs gdbstub-sve-ioctls +run-gdbstub-sysregs: sysregs + $(call run-test, $@, $(GDB_SCRIPT) \ + --gdb $(HAVE_GDB_BIN) \ + --qemu $(QEMU) --qargs "$(QEMU_OPTS)" \ + --bin $< --test $(AARCH64_SRC)/gdbstub/test-sve.py, \ + "basic gdbstub SVE support") + +run-gdbstub-sve-ioctls: sve-ioctls + $(call run-test, $@, $(GDB_SCRIPT) \ + --gdb $(HAVE_GDB_BIN) \ + --qemu $(QEMU) --qargs "$(QEMU_OPTS)" \ + --bin $< --test $(AARCH64_SRC)/gdbstub/test-sve-ioctl.py, \ + "basic gdbstub SVE ZLEN support") +endif + +endif + TESTS += $(AARCH64_TESTS) diff --git a/tests/tcg/aarch64/gdbstub/test-sve-ioctl.py b/tests/tcg/aarch64/gdbstub/test-sve-ioctl.py new file mode 100644 index 0000000000..984fbeb277 --- /dev/null +++ b/tests/tcg/aarch64/gdbstub/test-sve-ioctl.py @@ -0,0 +1,85 @@ +from __future__ import print_function +# +# Test the SVE ZReg reports the right amount of data. It uses the +# sve-ioctl test and examines the register data each time the +# __sve_ld_done breakpoint is hit. +# +# This is launched via tests/guest-debug/run-test.py +# + +import gdb +import sys + +initial_vlen = 0 +failcount = 0 + +def report(cond, msg): + "Report success/fail of test" + if cond: + print ("PASS: %s" % (msg)) + else: + print ("FAIL: %s" % (msg)) + global failcount + failcount += 1 + +class TestBreakpoint(gdb.Breakpoint): + def __init__(self, sym_name="__sve_ld_done"): + super(TestBreakpoint, self).__init__(sym_name) + # self.sym, ok = gdb.lookup_symbol(sym_name) + + def stop(self): + val_i = gdb.parse_and_eval('i') + global initial_vlen + try: + for i in range(0, int(val_i)): + val_z = gdb.parse_and_eval("$z0.b.u[%d]" % i) + report(int(val_z) == i, "z0.b.u[%d] == %d" % (i, i)) + for i in range(i + 1, initial_vlen): + val_z = gdb.parse_and_eval("$z0.b.u[%d]" % i) + report(int(val_z) == 0, "z0.b.u[%d] == 0" % (i)) + except gdb.error: + report(False, "checking zregs (out of range)") + + +def run_test(): + "Run through the tests one by one" + + print ("Setup breakpoint") + bp = TestBreakpoint() + + global initial_vlen + vg = gdb.parse_and_eval("$vg") + initial_vlen = int(vg) * 8 + + gdb.execute("c") + +# +# This runs as the script it sourced (via -x, via run-test.py) +# +try: + inferior = gdb.selected_inferior() + if inferior.was_attached == False: + print("SKIPPING (failed to attach)", file=sys.stderr) + exit(0) + arch = inferior.architecture() + report(arch.name() == "aarch64", "connected to aarch64") +except (gdb.error, AttributeError): + print("SKIPPING (not connected)", file=sys.stderr) + exit(0) + +try: + # These are not very useful in scripts + gdb.execute("set pagination off") + gdb.execute("set confirm off") + + # Run the actual tests + run_test() +except: + print ("GDB Exception: %s" % (sys.exc_info()[0])) + failcount += 1 + import code + code.InteractiveConsole(locals=globals()).interact() + raise + +print("All tests complete: %d failures" % failcount) +exit(failcount) diff --git a/tests/tcg/aarch64/gdbstub/test-sve.py b/tests/tcg/aarch64/gdbstub/test-sve.py new file mode 100644 index 0000000000..dbe7f2aa93 --- /dev/null +++ b/tests/tcg/aarch64/gdbstub/test-sve.py @@ -0,0 +1,84 @@ +from __future__ import print_function +# +# Test the SVE registers are visable and changeable via gdbstub +# +# This is launched via tests/guest-debug/run-test.py +# + +import gdb +import sys + +MAGIC = 0xDEADBEEF + +failcount = 0 + +def report(cond, msg): + "Report success/fail of test" + if cond: + print ("PASS: %s" % (msg)) + else: + print ("FAIL: %s" % (msg)) + global failcount + failcount += 1 + +def run_test(): + "Run through the tests one by one" + + gdb.execute("info registers") + report(True, "info registers") + + gdb.execute("info registers vector") + report(True, "info registers vector") + + # Now all the zregs + frame = gdb.selected_frame() + for i in range(0, 32): + rname = "z%d" % (i) + zreg = frame.read_register(rname) + report(True, "Reading %s" % rname) + for j in range(0, 4): + cmd = "set $%s.q.u[%d] = 0x%x" % (rname, j, MAGIC) + gdb.execute(cmd) + report(True, "%s" % cmd) + for j in range(0, 4): + reg = "$%s.q.u[%d]" % (rname, j) + v = gdb.parse_and_eval(reg) + report(str(v.type) == "uint128_t", "size of %s" % (reg)) + for j in range(0, 8): + cmd = "set $%s.d.u[%d] = 0x%x" % (rname, j, MAGIC) + gdb.execute(cmd) + report(True, "%s" % cmd) + for j in range(0, 8): + reg = "$%s.d.u[%d]" % (rname, j) + v = gdb.parse_and_eval(reg) + report(str(v.type) == "uint64_t", "size of %s" % (reg)) + report(int(v) == MAGIC, "%s is 0x%x" % (reg, MAGIC)) + +# +# This runs as the script it sourced (via -x, via run-test.py) +# +try: + inferior = gdb.selected_inferior() + if inferior.was_attached == False: + print("SKIPPING (failed to attach)", file=sys.stderr) + exit(0) + arch = inferior.architecture() + report(arch.name() == "aarch64", "connected to aarch64") +except (gdb.error, AttributeError): + print("SKIPPING (not connected)", file=sys.stderr) + exit(0) + +try: + # These are not very useful in scripts + gdb.execute("set pagination off") + gdb.execute("set confirm off") + + # Run the actual tests + run_test() +except: + print ("GDB Exception: %s" % (sys.exc_info()[0])) + failcount += 1 + +print("All tests complete: %d failures" % failcount) + +exit(failcount) diff --git a/tests/tcg/aarch64/pauth-1.c b/tests/tcg/aarch64/pauth-1.c index a3c1443cd0..d3878cbeb6 100644 --- a/tests/tcg/aarch64/pauth-1.c +++ b/tests/tcg/aarch64/pauth-1.c @@ -2,8 +2,6 @@ #include #include -asm(".arch armv8.4-a"); - #ifndef PR_PAC_RESET_KEYS #define PR_PAC_RESET_KEYS 54 #define PR_PAC_APDAKEY (1 << 2) @@ -31,7 +29,7 @@ int main() } perc = (float) count / (float) (TESTS * 2); - printf("Ptr Check: %0.2f%%", perc * 100.0); + printf("Ptr Check: %0.2f%%\n", perc * 100.0); assert(perc > 0.95); return 0; } diff --git a/tests/tcg/aarch64/pauth-2.c b/tests/tcg/aarch64/pauth-2.c index 2fe030ba3d..9bba0beb63 100644 --- a/tests/tcg/aarch64/pauth-2.c +++ b/tests/tcg/aarch64/pauth-2.c @@ -1,8 +1,6 @@ #include #include -asm(".arch armv8.4-a"); - void do_test(uint64_t value) { uint64_t salt1, salt2; diff --git a/tests/tcg/aarch64/pauth-4.c b/tests/tcg/aarch64/pauth-4.c new file mode 100644 index 0000000000..24a639e36c --- /dev/null +++ b/tests/tcg/aarch64/pauth-4.c @@ -0,0 +1,45 @@ +#include +#include +#include +#include + +#define TESTS 1000 + +int main() +{ + int i, count = 0; + float perc; + void *base = malloc(TESTS); + + for (i = 0; i < TESTS; i++) { + uintptr_t in, x, y; + + in = i + (uintptr_t) base; + + asm("mov %0, %[in]\n\t" + "pacia %0, sp\n\t" /* sigill if pauth not supported */ + "eor %0, %0, #4\n\t" /* corrupt single bit */ + "mov %1, %0\n\t" + "autia %1, sp\n\t" /* validate corrupted pointer */ + "xpaci %0\n\t" /* strip pac from corrupted pointer */ + : /* out */ "=r"(x), "=r"(y) + : /* in */ [in] "r" (in) + : /* clobbers */); + + /* + * Once stripped, the corrupted pointer is of the form 0x0000...wxyz. + * We expect the autia to indicate failure, producing a pointer of the + * form 0x000e....wxyz. Use xpaci and != for the test, rather than + * extracting explicit bits from the top, because the location of the + * error code "e" depends on the configuration of virtual memory. + */ + if (x != y) { + count++; + } + + } + perc = (float) count / (float) TESTS; + printf("Checks Passed: %0.2f%%", perc * 100.0); + assert(perc > 0.95); + return 0; +} diff --git a/tests/tcg/aarch64/sve-ioctls.c b/tests/tcg/aarch64/sve-ioctls.c new file mode 100644 index 0000000000..9544dffa0e --- /dev/null +++ b/tests/tcg/aarch64/sve-ioctls.c @@ -0,0 +1,70 @@ +/* + * SVE ioctls tests + * + * Test the SVE width setting ioctls work and provide a base for + * testing the gdbstub. + * + * Copyright (c) 2019 Linaro Ltd + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include +#include +#include +#include +#include +#include + +#ifndef HWCAP_CPUID +#define HWCAP_CPUID (1 << 11) +#endif + +#define SVE_MAX_QUADS (2048 / 128) +#define BYTES_PER_QUAD (128 / 8) + +#define get_cpu_reg(id) ({ \ + unsigned long __val; \ + asm("mrs %0, "#id : "=r" (__val)); \ + __val; \ + }) + +static int do_sve_ioctl_test(void) +{ + int i, res, init_vq; + + res = prctl(PR_SVE_GET_VL, 0, 0, 0, 0); + if (res < 0) { + printf("FAILED to PR_SVE_GET_VL (%d)", res); + return -1; + } + init_vq = res & PR_SVE_VL_LEN_MASK; + + for (i = init_vq; i > 15; i /= 2) { + printf("Checking PR_SVE_SET_VL=%d\n", i); + res = prctl(PR_SVE_SET_VL, i, 0, 0, 0, 0); + if (res < 0) { + printf("FAILED to PR_SVE_SET_VL (%d)", res); + return -1; + } + asm("index z0.b, #0, #1\n" + ".global __sve_ld_done\n" + "__sve_ld_done:\n" + "mov z0.b, #0\n" + : /* no outputs kept */ + : /* no inputs */ + : "memory", "z0"); + } + printf("PASS\n"); + return 0; +} + +int main(int argc, char **argv) +{ + /* we also need to probe for the ioctl support */ + if (getauxval(AT_HWCAP) & HWCAP_SVE) { + return do_sve_ioctl_test(); + } else { + printf("SKIP: no HWCAP_SVE on this system\n"); + return 0; + } +} diff --git a/tests/tcg/aarch64/sysregs.c b/tests/tcg/aarch64/sysregs.c new file mode 100644 index 0000000000..40cf8d2877 --- /dev/null +++ b/tests/tcg/aarch64/sysregs.c @@ -0,0 +1,172 @@ +/* + * Check emulated system register access for linux-user mode. + * + * See: https://www.kernel.org/doc/Documentation/arm64/cpu-feature-registers.txt + * + * Copyright (c) 2019 Linaro + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include +#include +#include +#include +#include + +#ifndef HWCAP_CPUID +#define HWCAP_CPUID (1 << 11) +#endif + +int failed_bit_count; + +/* Read and print system register `id' value */ +#define get_cpu_reg(id) ({ \ + unsigned long __val = 0xdeadbeef; \ + asm("mrs %0, "#id : "=r" (__val)); \ + printf("%-20s: 0x%016lx\n", #id, __val); \ + __val; \ + }) + +/* As above but also check no bits outside of `mask' are set*/ +#define get_cpu_reg_check_mask(id, mask) ({ \ + unsigned long __cval = get_cpu_reg(id); \ + unsigned long __extra = __cval & ~mask; \ + if (__extra) { \ + printf("%-20s: 0x%016lx\n", " !!extra bits!!", __extra); \ + failed_bit_count++; \ + } \ +}) + +/* As above but check RAZ */ +#define get_cpu_reg_check_zero(id) ({ \ + unsigned long __val = 0xdeadbeef; \ + asm("mrs %0, "#id : "=r" (__val)); \ + if (__val) { \ + printf("%-20s: 0x%016lx (not RAZ!)\n", #id, __val); \ + failed_bit_count++; \ + } \ +}) + +/* Chunk up mask into 63:48, 47:32, 31:16, 15:0 to ease counting */ +#define _m(a, b, c, d) (0x ## a ## b ## c ## d ##ULL) + +bool should_fail; +int should_fail_count; +int should_not_fail_count; +uintptr_t failed_pc[10]; + +void sigill_handler(int signo, siginfo_t *si, void *data) +{ + ucontext_t *uc = (ucontext_t *)data; + + if (should_fail) { + should_fail_count++; + } else { + uintptr_t pc = (uintptr_t) uc->uc_mcontext.pc; + failed_pc[should_not_fail_count++] = pc; + } + uc->uc_mcontext.pc += 4; +} + +int main(void) +{ + struct sigaction sa; + + /* Hook in a SIGILL handler */ + memset(&sa, 0, sizeof(struct sigaction)); + sa.sa_flags = SA_SIGINFO; + sa.sa_sigaction = &sigill_handler; + sigemptyset(&sa.sa_mask); + + if (sigaction(SIGILL, &sa, 0) != 0) { + perror("sigaction"); + return 1; + } + + /* Counter values have been exposed since Linux 4.12 */ + printf("Checking Counter registers\n"); + + get_cpu_reg(ctr_el0); + get_cpu_reg(cntvct_el0); + get_cpu_reg(cntfrq_el0); + + /* HWCAP_CPUID indicates we can read feature registers, since Linux 4.11 */ + if (!(getauxval(AT_HWCAP) & HWCAP_CPUID)) { + printf("CPUID registers unavailable\n"); + return 1; + } else { + printf("Checking CPUID registers\n"); + } + + /* + * Some registers only expose some bits to user-space. Anything + * that is IMPDEF is exported as 0 to user-space. The _mask checks + * assert no extra bits are set. + * + * This check is *not* comprehensive as some fields are set to + * minimum valid fields - for the purposes of this check allowed + * to have non-zero values. + */ + get_cpu_reg_check_mask(id_aa64isar0_el1, _m(00ff,ffff,f0ff,fff0)); + get_cpu_reg_check_mask(id_aa64isar1_el1, _m(0000,00f0,ffff,ffff)); + /* TGran4 & TGran64 as pegged to -1 */ + get_cpu_reg_check_mask(id_aa64mmfr0_el1, _m(0000,0000,ff00,0000)); + get_cpu_reg_check_zero(id_aa64mmfr1_el1); + /* EL1/EL0 reported as AA64 only */ + get_cpu_reg_check_mask(id_aa64pfr0_el1, _m(000f,000f,00ff,0011)); + get_cpu_reg_check_mask(id_aa64pfr1_el1, _m(0000,0000,0000,00f0)); + /* all hidden, DebugVer fixed to 0x6 (ARMv8 debug architecture) */ + get_cpu_reg_check_mask(id_aa64dfr0_el1, _m(0000,0000,0000,0006)); + get_cpu_reg_check_zero(id_aa64dfr1_el1); + get_cpu_reg_check_zero(id_aa64zfr0_el1); + + get_cpu_reg_check_zero(id_aa64afr0_el1); + get_cpu_reg_check_zero(id_aa64afr1_el1); + + get_cpu_reg_check_mask(midr_el1, _m(0000,0000,ffff,ffff)); + /* mpidr sets bit 31, everything else hidden */ + get_cpu_reg_check_mask(mpidr_el1, _m(0000,0000,8000,0000)); + /* REVIDR is all IMPDEF so should be all zeros to user-space */ + get_cpu_reg_check_zero(revidr_el1); + + /* + * There are a block of more registers that are RAZ in the rest of + * the Op0=3, Op1=0, CRn=0, CRm=0,4,5,6,7 space. However for + * brevity we don't check stuff that is currently un-allocated + * here. Feel free to add them ;-) + */ + + printf("Remaining registers should fail\n"); + should_fail = true; + + /* Unexposed register access causes SIGILL */ + get_cpu_reg(id_mmfr0_el1); + get_cpu_reg(id_mmfr1_el1); + get_cpu_reg(id_mmfr2_el1); + get_cpu_reg(id_mmfr3_el1); + + get_cpu_reg(mvfr0_el1); + get_cpu_reg(mvfr1_el1); + + if (should_not_fail_count > 0) { + int i; + for (i = 0; i < should_not_fail_count; i++) { + uintptr_t pc = failed_pc[i]; + uint32_t insn = *(uint32_t *) pc; + printf("insn %#x @ %#lx unexpected FAIL\n", insn, pc); + } + return 1; + } + + if (failed_bit_count > 0) { + printf("Extra information leaked to user-space!\n"); + return 1; + } + + return should_fail_count == 6 ? 0 : 1; +} diff --git a/tests/tcg/aarch64/system/pauth-3.c b/tests/tcg/aarch64/system/pauth-3.c new file mode 100644 index 0000000000..42eff4d5ea --- /dev/null +++ b/tests/tcg/aarch64/system/pauth-3.c @@ -0,0 +1,40 @@ +#include +#include + +int main() +{ + /* + * Test vector from QARMA paper (https://eprint.iacr.org/2016/444.pdf) + * to verify one computation of the pauth_computepac() function, + * which uses sbox2. + * + * Use PACGA, because it returns the most bits from ComputePAC. + * We still only get the most significant 32-bits of the result. + */ + + static const uint64_t d[5] = { + 0xfb623599da6e8127ull, + 0x477d469dec0b8762ull, + 0x84be85ce9804e94bull, + 0xec2802d4e0a488e9ull, + 0xc003b93999b33765ull & 0xffffffff00000000ull + }; + uint64_t r; + + asm("msr apgakeyhi_el1, %[w0]\n\t" + "msr apgakeylo_el1, %[k0]\n\t" + "pacga %[r], %[P], %[T]" + : [r] "=r"(r) + : [P] "r" (d[0]), + [T] "r" (d[1]), + [w0] "r" (d[2]), + [k0] "r" (d[3])); + + if (r == d[4]) { + ml_printf("OK\n"); + return 0; + } else { + ml_printf("FAIL: %lx != %lx\n", r, d[4]); + return 1; + } +} diff --git a/tests/tcg/aarch64/system/semiconsole.c b/tests/tcg/aarch64/system/semiconsole.c new file mode 100644 index 0000000000..bfe7c9e26b --- /dev/null +++ b/tests/tcg/aarch64/system/semiconsole.c @@ -0,0 +1,38 @@ +/* + * Semihosting Console Test + * + * Copyright (c) 2019 Linaro Ltd + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include + +#define SYS_READC 0x7 + +uintptr_t __semi_call(uintptr_t type, uintptr_t arg0) +{ + register uintptr_t t asm("x0") = type; + register uintptr_t a0 asm("x1") = arg0; + asm("hlt 0xf000" + : "=r" (t) + : "r" (t), "r" (a0)); + + return t; +} + +int main(void) +{ + char c; + + ml_printf("Semihosting Console Test\n"); + ml_printf("hit X to exit:"); + + do { + c = __semi_call(SYS_READC, 0); + __sys_outc(c); + } while (c != 'X'); + + return 0; +} diff --git a/tests/tcg/arm/Makefile.target b/tests/tcg/arm/Makefile.target index 0765f37ff0..11c39c601e 100644 --- a/tests/tcg/arm/Makefile.target +++ b/tests/tcg/arm/Makefile.target @@ -31,15 +31,43 @@ run-fcvt: fcvt # Semihosting smoke test for linux-user ARM_TESTS += semihosting +semihosting: CFLAGS += -mthumb run-semihosting: semihosting $(call run-test,$<,$(QEMU) $< 2> $<.err, "$< on $(TARGET_NAME)") +ARM_TESTS += semihosting-arm +semihosting-arm: CFLAGS += -marm +semihosting-arm: semihosting.c + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $< -o $@ $(LDFLAGS) + +run-semihosting-arm: semihosting-arm + $(call run-test,$<,$(QEMU) $< 2> $<.err, "$< on $(TARGET_NAME)") + run-plugin-semihosting-with-%: $(call run-test, $@, $(QEMU) $(QEMU_OPTS) \ -plugin $(PLUGIN_DIR)/$(call extract-plugin,$@) \ $(call strip-plugin,$<) 2> $<.err, \ "$< on $(TARGET_NAME) with $*") +ARM_TESTS += semiconsole semiconsole-arm + +semiconsole: CFLAGS += -mthumb +run-semiconsole: semiconsole + $(call skip-test, $<, "MANUAL ONLY") + +run-plugin-semiconsole-with-%: + $(call skip-test, $<, "MANUAL ONLY") + +semiconsole-arm: CFLAGS += -marm +semiconsole-arm: semiconsole.c + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $< -o $@ $(LDFLAGS) + +run-semiconsole-arm: semiconsole-arm + $(call skip-test, $<, "MANUAL ONLY") + +run-plugin-semiconsole-arm-with-%: + $(call skip-test, $<, "MANUAL ONLY") + TESTS += $(ARM_TESTS) # On ARM Linux only supports 4k pages diff --git a/tests/tcg/arm/semicall.h b/tests/tcg/arm/semicall.h new file mode 100644 index 0000000000..d4f6818192 --- /dev/null +++ b/tests/tcg/arm/semicall.h @@ -0,0 +1,35 @@ +/* + * Semihosting Tests + * + * Copyright (c) 2019 + * Written by Alex Bennée + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#define SYS_WRITE0 0x04 +#define SYS_READC 0x07 +#define SYS_REPORTEXC 0x18 + +uintptr_t __semi_call(uintptr_t type, uintptr_t arg0) +{ +#if defined(__arm__) + register uintptr_t t asm("r0") = type; + register uintptr_t a0 asm("r1") = arg0; +#ifdef __thumb__ +# define SVC "svc 0xab" +#else +# define SVC "svc 0x123456" +#endif + asm(SVC : "=r" (t) + : "r" (t), "r" (a0)); +#else + register uintptr_t t asm("x0") = type; + register uintptr_t a0 asm("x1") = arg0; + asm("hlt 0xf000" + : "=r" (t) + : "r" (t), "r" (a0)); +#endif + + return t; +} diff --git a/tests/tcg/arm/semiconsole.c b/tests/tcg/arm/semiconsole.c new file mode 100644 index 0000000000..6ef0bd2450 --- /dev/null +++ b/tests/tcg/arm/semiconsole.c @@ -0,0 +1,27 @@ +/* + * linux-user semihosting console + * + * Copyright (c) 2019 + * Written by Alex Bennée + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include +#include +#include "semicall.h" + +int main(void) +{ + char c; + + printf("Semihosting Console Test\n"); + printf("hit X to exit:"); + + do { + c = __semi_call(SYS_READC, 0); + printf("got '%c'\n", c); + } while (c != 'X'); + + return 0; +} diff --git a/tests/tcg/arm/semihosting.c b/tests/tcg/arm/semihosting.c index 09c89cb481..33faac9916 100644 --- a/tests/tcg/arm/semihosting.c +++ b/tests/tcg/arm/semihosting.c @@ -8,26 +8,7 @@ */ #include - -#define SYS_WRITE0 0x04 -#define SYS_REPORTEXC 0x18 - -void __semi_call(uintptr_t type, uintptr_t arg0) -{ -#if defined(__arm__) - register uintptr_t t asm("r0") = type; - register uintptr_t a0 asm("r1") = arg0; - asm("svc 0xab" - : /* no return */ - : "r" (t), "r" (a0)); -#else - register uintptr_t t asm("x0") = type; - register uintptr_t a0 asm("x1") = arg0; - asm("hlt 0xf000" - : /* no return */ - : "r" (t), "r" (a0)); -#endif -} +#include "semicall.h" int main(int argc, char *argv[argc]) { diff --git a/tests/tcg/configure.sh b/tests/tcg/configure.sh index 6c4a471aea..eaaaff6233 100755 --- a/tests/tcg/configure.sh +++ b/tests/tcg/configure.sh @@ -36,8 +36,10 @@ TMPC="${TMPDIR1}/qemu-conf.c" TMPE="${TMPDIR1}/qemu-conf.exe" container="no" -if has "docker" || has "podman"; then - container=$($python $source_path/tests/docker/docker.py probe) +if test $use_containers = "yes"; then + if has "docker" || has "podman"; then + container=$($python $source_path/tests/docker/docker.py probe) + fi fi # cross compilers defaults, can be overridden with --cross-cc-ARCH @@ -214,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=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/tcg/x86_64/system/boot.S b/tests/tcg/x86_64/system/boot.S index 205cfbd398..73b19a2bda 100644 --- a/tests/tcg/x86_64/system/boot.S +++ b/tests/tcg/x86_64/system/boot.S @@ -41,10 +41,7 @@ #define XEN_ELFNOTE_PHYS32_ENTRY 18 #define __ASM_FORM(x) x -#define __ASM_FORM_RAW(x) x -#define __ASM_FORM_COMMA(x) x, -#define __ASM_SEL(a,b) __ASM_FORM(b) -#define __ASM_SEL_RAW(a,b) __ASM_FORM_RAW(b) +#define __ASM_SEL(a,b) __ASM_FORM(b) #define _ASM_PTR __ASM_SEL(.long, .quad) ELFNOTE(Xen, XEN_ELFNOTE_VIRT_BASE, _ASM_PTR 0x100000) diff --git a/tests/test-aio.c b/tests/test-aio.c index 86fb73b3d5..8a46078463 100644 --- a/tests/test-aio.c +++ b/tests/test-aio.c @@ -615,7 +615,8 @@ static void test_source_bh_delete_from_cb(void) g_assert_cmpint(data1.n, ==, data1.max); g_assert(data1.bh == NULL); - g_assert(!g_main_context_iteration(NULL, false)); + assert(g_main_context_iteration(NULL, false)); + assert(!g_main_context_iteration(NULL, false)); } static void test_source_bh_delete_from_cb_many(void) diff --git a/tests/test-bitmap.c b/tests/test-bitmap.c index 087e02a26c..2f5b71458a 100644 --- a/tests/test-bitmap.c +++ b/tests/test-bitmap.c @@ -22,10 +22,10 @@ static void check_bitmap_copy_with_offset(void) bmap2 = bitmap_new(BMAP_SIZE); bmap3 = bitmap_new(BMAP_SIZE); - bmap1[0] = random(); - bmap1[1] = random(); - bmap1[2] = random(); - bmap1[3] = random(); + bmap1[0] = g_test_rand_int(); + bmap1[1] = g_test_rand_int(); + bmap1[2] = g_test_rand_int(); + bmap1[3] = g_test_rand_int(); total = BITS_PER_LONG * 4; /* Shift 115 bits into bmap2 */ diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c index 7844c9ffcb..7519847912 100644 --- a/tests/test-blockjob.c +++ b/tests/test-blockjob.c @@ -34,13 +34,13 @@ static BlockJob *mk_job(BlockBackend *blk, const char *id, int flags) { BlockJob *job; - Error *errp = NULL; + Error *err = NULL; job = block_job_create(id, drv, NULL, blk_bs(blk), 0, BLK_PERM_ALL, 0, flags, block_job_cb, - NULL, &errp); + NULL, &err); if (should_succeed) { - g_assert_null(errp); + g_assert_null(err); g_assert_nonnull(job); if (id) { g_assert_cmpstr(job->job.id, ==, id); @@ -48,9 +48,8 @@ static BlockJob *mk_job(BlockBackend *blk, const char *id, g_assert_cmpstr(job->job.id, ==, blk_name(blk)); } } else { - g_assert_nonnull(errp); + error_free_or_abort(&err); g_assert_null(job); - error_free(errp); } return job; @@ -80,9 +79,9 @@ static BlockBackend *create_blk(const char *name) bdrv_unref(bs); if (name) { - Error *errp = NULL; - monitor_add_blk(blk, name, &errp); - g_assert_null(errp); + Error *err = NULL; + monitor_add_blk(blk, name, &err); + g_assert_null(err); } return blk; @@ -368,7 +367,9 @@ static void test_cancel_concluded(void) aio_poll(qemu_get_aio_context(), true); assert(job->status == JOB_STATUS_PENDING); + aio_context_acquire(job->aio_context); job_finalize(job, &error_abort); + aio_context_release(job->aio_context); assert(job->status == JOB_STATUS_CONCLUDED); cancel_common(s); diff --git a/tests/test-char.c b/tests/test-char.c index 45e42af290..3afc9b1b8d 100644 --- a/tests/test-char.c +++ b/tests/test-char.c @@ -54,7 +54,7 @@ static void fe_read(void *opaque, const uint8_t *buf, int size) quit = true; } -static void fe_event(void *opaque, int event) +static void fe_event(void *opaque, QEMUChrEvent event) { FeHandler *h = opaque; bool new_open_state; @@ -633,7 +633,7 @@ typedef struct { static void -char_socket_event(void *opaque, int event) +char_socket_event(void *opaque, QEMUChrEvent event) { CharSocketTestData *data = opaque; data->event = event; @@ -1006,7 +1006,7 @@ static void char_socket_client_test(gconstpointer opaque) } static void -count_closed_event(void *opaque, int event) +count_closed_event(void *opaque, QEMUChrEvent event) { int *count = opaque; if (event == CHR_EVENT_CLOSED) { diff --git a/tests/test-crypto-tlscredsx509.c b/tests/test-crypto-tlscredsx509.c index a27efb7540..f487349c32 100644 --- a/tests/test-crypto-tlscredsx509.c +++ b/tests/test-crypto-tlscredsx509.c @@ -141,7 +141,7 @@ int main(int argc, char **argv) module_call_init(MODULE_INIT_QOM); g_test_init(&argc, &argv, NULL); - setenv("GNUTLS_FORCE_FIPS_MODE", "2", 1); + g_setenv("GNUTLS_FORCE_FIPS_MODE", "2", 1); mkdir(WORKDIR, 0700); diff --git a/tests/test-crypto-tlssession.c b/tests/test-crypto-tlssession.c index 6560d7134f..8b2453fa79 100644 --- a/tests/test-crypto-tlssession.c +++ b/tests/test-crypto-tlssession.c @@ -398,7 +398,7 @@ int main(int argc, char **argv) module_call_init(MODULE_INIT_QOM); g_test_init(&argc, &argv, NULL); - setenv("GNUTLS_FORCE_FIPS_MODE", "2", 1); + g_setenv("GNUTLS_FORCE_FIPS_MODE", "2", 1); mkdir(WORKDIR, 0700); diff --git a/tests/test-hbitmap.c b/tests/test-hbitmap.c index e1f867085f..b6726cf76b 100644 --- a/tests/test-hbitmap.c +++ b/tests/test-hbitmap.c @@ -22,7 +22,6 @@ typedef struct TestHBitmapData { HBitmap *hb; - HBitmap *meta; unsigned long *bits; size_t size; size_t old_size; @@ -94,14 +93,6 @@ static void hbitmap_test_init(TestHBitmapData *data, } } -static void hbitmap_test_init_meta(TestHBitmapData *data, - uint64_t size, int granularity, - int meta_chunk) -{ - hbitmap_test_init(data, size, granularity); - data->meta = hbitmap_create_meta(data->hb, meta_chunk); -} - static inline size_t hbitmap_test_array_size(size_t bits) { size_t n = DIV_ROUND_UP(bits, BITS_PER_LONG); @@ -144,9 +135,6 @@ static void hbitmap_test_teardown(TestHBitmapData *data, const void *unused) { if (data->hb) { - if (data->meta) { - hbitmap_free_meta(data->hb); - } hbitmap_free(data->hb); data->hb = NULL; } @@ -648,96 +636,6 @@ static void test_hbitmap_truncate_shrink_large(TestHBitmapData *data, hbitmap_test_truncate(data, size, -diff, 0); } -static void hbitmap_check_meta(TestHBitmapData *data, - int64_t start, int count) -{ - int64_t i; - - for (i = 0; i < data->size; i++) { - if (i >= start && i < start + count) { - g_assert(hbitmap_get(data->meta, i)); - } else { - g_assert(!hbitmap_get(data->meta, i)); - } - } -} - -static void hbitmap_test_meta(TestHBitmapData *data, - int64_t start, int count, - int64_t check_start, int check_count) -{ - hbitmap_reset_all(data->hb); - hbitmap_reset_all(data->meta); - - /* Test "unset" -> "unset" will not update meta. */ - hbitmap_reset(data->hb, start, count); - hbitmap_check_meta(data, 0, 0); - - /* Test "unset" -> "set" will update meta */ - hbitmap_set(data->hb, start, count); - hbitmap_check_meta(data, check_start, check_count); - - /* Test "set" -> "set" will not update meta */ - hbitmap_reset_all(data->meta); - hbitmap_set(data->hb, start, count); - hbitmap_check_meta(data, 0, 0); - - /* Test "set" -> "unset" will update meta */ - hbitmap_reset_all(data->meta); - hbitmap_reset(data->hb, start, count); - hbitmap_check_meta(data, check_start, check_count); -} - -static void hbitmap_test_meta_do(TestHBitmapData *data, int chunk_size) -{ - uint64_t size = chunk_size * 100; - hbitmap_test_init_meta(data, size, 0, chunk_size); - - hbitmap_test_meta(data, 0, 1, 0, chunk_size); - hbitmap_test_meta(data, 0, chunk_size, 0, chunk_size); - hbitmap_test_meta(data, chunk_size - 1, 1, 0, chunk_size); - hbitmap_test_meta(data, chunk_size - 1, 2, 0, chunk_size * 2); - hbitmap_test_meta(data, chunk_size - 1, chunk_size + 1, 0, chunk_size * 2); - hbitmap_test_meta(data, chunk_size - 1, chunk_size + 2, 0, chunk_size * 3); - hbitmap_test_meta(data, 7 * chunk_size - 1, chunk_size + 2, - 6 * chunk_size, chunk_size * 3); - hbitmap_test_meta(data, size - 1, 1, size - chunk_size, chunk_size); - hbitmap_test_meta(data, 0, size, 0, size); -} - -static void test_hbitmap_meta_byte(TestHBitmapData *data, const void *unused) -{ - hbitmap_test_meta_do(data, BITS_PER_BYTE); -} - -static void test_hbitmap_meta_word(TestHBitmapData *data, const void *unused) -{ - hbitmap_test_meta_do(data, BITS_PER_LONG); -} - -static void test_hbitmap_meta_sector(TestHBitmapData *data, const void *unused) -{ - hbitmap_test_meta_do(data, BDRV_SECTOR_SIZE * BITS_PER_BYTE); -} - -/** - * Create an HBitmap and test set/unset. - */ -static void test_hbitmap_meta_one(TestHBitmapData *data, const void *unused) -{ - int i; - int64_t offsets[] = { - 0, 1, L1 - 1, L1, L1 + 1, L2 - 1, L2, L2 + 1, L3 - 1, L3, L3 + 1 - }; - - hbitmap_test_init_meta(data, L3 * 2, 0, 1); - for (i = 0; i < ARRAY_SIZE(offsets); i++) { - hbitmap_test_meta(data, offsets[i], 1, offsets[i], 1); - hbitmap_test_meta(data, offsets[i], L1, offsets[i], L1); - hbitmap_test_meta(data, offsets[i], L2, offsets[i], L2); - } -} - static void test_hbitmap_serialize_align(TestHBitmapData *data, const void *unused) { @@ -750,13 +648,6 @@ static void test_hbitmap_serialize_align(TestHBitmapData *data, g_assert_cmpint(r, ==, 64 << 3); } -static void test_hbitmap_meta_zero(TestHBitmapData *data, const void *unused) -{ - hbitmap_test_init_meta(data, 0, 0, 1); - - hbitmap_check_meta(data, 0, 0); -} - static void hbitmap_test_serialize_range(TestHBitmapData *data, uint8_t *buf, size_t buf_size, uint64_t pos, uint64_t count) @@ -925,106 +816,123 @@ static void test_hbitmap_iter_and_reset(TestHBitmapData *data, hbitmap_iter_next(&hbi); } -static void test_hbitmap_next_zero_check_range(TestHBitmapData *data, - uint64_t start, - uint64_t count) +static void test_hbitmap_next_x_check_range(TestHBitmapData *data, + int64_t start, + int64_t count) { - int64_t ret1 = hbitmap_next_zero(data->hb, start, count); - int64_t ret2 = start; + int64_t next_zero = hbitmap_next_zero(data->hb, start, count); + int64_t next_dirty = hbitmap_next_dirty(data->hb, start, count); + int64_t next; int64_t end = start >= data->size || data->size - start < count ? data->size : start + count; + bool first_bit = hbitmap_get(data->hb, start); - for ( ; ret2 < end && hbitmap_get(data->hb, ret2); ret2++) { + for (next = start; + next < end && hbitmap_get(data->hb, next) == first_bit; + next++) + { ; } - if (ret2 == end) { - ret2 = -1; + + if (next == end) { + next = -1; } - g_assert_cmpint(ret1, ==, ret2); + g_assert_cmpint(next_dirty, ==, first_bit ? start : next); + g_assert_cmpint(next_zero, ==, first_bit ? next : start); } -static void test_hbitmap_next_zero_check(TestHBitmapData *data, int64_t start) +static void test_hbitmap_next_x_check(TestHBitmapData *data, int64_t start) { - test_hbitmap_next_zero_check_range(data, start, UINT64_MAX); + test_hbitmap_next_x_check_range(data, start, INT64_MAX); } -static void test_hbitmap_next_zero_do(TestHBitmapData *data, int granularity) +static void test_hbitmap_next_x_do(TestHBitmapData *data, int granularity) { hbitmap_test_init(data, L3, granularity); - test_hbitmap_next_zero_check(data, 0); - test_hbitmap_next_zero_check(data, L3 - 1); - test_hbitmap_next_zero_check_range(data, 0, 1); - test_hbitmap_next_zero_check_range(data, L3 - 1, 1); + test_hbitmap_next_x_check(data, 0); + test_hbitmap_next_x_check(data, L3 - 1); + test_hbitmap_next_x_check_range(data, 0, 1); + test_hbitmap_next_x_check_range(data, L3 - 1, 1); hbitmap_set(data->hb, L2, 1); - test_hbitmap_next_zero_check(data, 0); - test_hbitmap_next_zero_check(data, L2 - 1); - test_hbitmap_next_zero_check(data, L2); - test_hbitmap_next_zero_check(data, L2 + 1); - test_hbitmap_next_zero_check_range(data, 0, 1); - test_hbitmap_next_zero_check_range(data, 0, L2); - test_hbitmap_next_zero_check_range(data, L2 - 1, 1); - test_hbitmap_next_zero_check_range(data, L2 - 1, 2); - test_hbitmap_next_zero_check_range(data, L2, 1); - test_hbitmap_next_zero_check_range(data, L2 + 1, 1); + test_hbitmap_next_x_check(data, 0); + test_hbitmap_next_x_check(data, L2 - 1); + test_hbitmap_next_x_check(data, L2); + test_hbitmap_next_x_check(data, L2 + 1); + test_hbitmap_next_x_check_range(data, 0, 1); + test_hbitmap_next_x_check_range(data, 0, L2); + test_hbitmap_next_x_check_range(data, L2 - 1, 1); + test_hbitmap_next_x_check_range(data, L2 - 1, 2); + test_hbitmap_next_x_check_range(data, L2, 1); + test_hbitmap_next_x_check_range(data, L2 + 1, 1); hbitmap_set(data->hb, L2 + 5, L1); - test_hbitmap_next_zero_check(data, 0); - test_hbitmap_next_zero_check(data, L2 + 1); - test_hbitmap_next_zero_check(data, L2 + 2); - test_hbitmap_next_zero_check(data, L2 + 5); - test_hbitmap_next_zero_check(data, L2 + L1 - 1); - test_hbitmap_next_zero_check(data, L2 + L1); - test_hbitmap_next_zero_check_range(data, L2, 6); - test_hbitmap_next_zero_check_range(data, L2 + 1, 3); - test_hbitmap_next_zero_check_range(data, L2 + 4, L1); - test_hbitmap_next_zero_check_range(data, L2 + 5, L1); + test_hbitmap_next_x_check(data, 0); + test_hbitmap_next_x_check(data, L2 - L1); + test_hbitmap_next_x_check(data, L2 + 1); + test_hbitmap_next_x_check(data, L2 + 2); + test_hbitmap_next_x_check(data, L2 + 5); + test_hbitmap_next_x_check(data, L2 + L1 - 1); + test_hbitmap_next_x_check(data, L2 + L1); + test_hbitmap_next_x_check(data, L2 + L1 + 1); + test_hbitmap_next_x_check_range(data, L2 - 2, L1); + test_hbitmap_next_x_check_range(data, L2, 4); + test_hbitmap_next_x_check_range(data, L2, 6); + test_hbitmap_next_x_check_range(data, L2 + 1, 3); + test_hbitmap_next_x_check_range(data, L2 + 4, L1); + test_hbitmap_next_x_check_range(data, L2 + 5, L1); + test_hbitmap_next_x_check_range(data, L2 + 5 + L1 - 1, 1); + test_hbitmap_next_x_check_range(data, L2 + 5 + L1, 1); + test_hbitmap_next_x_check_range(data, L2 + 5 + L1 + 1, 1); hbitmap_set(data->hb, L2 * 2, L3 - L2 * 2); - test_hbitmap_next_zero_check(data, L2 * 2 - L1); - test_hbitmap_next_zero_check(data, L2 * 2 - 2); - test_hbitmap_next_zero_check(data, L2 * 2 - 1); - test_hbitmap_next_zero_check(data, L2 * 2); - test_hbitmap_next_zero_check(data, L3 - 1); - test_hbitmap_next_zero_check_range(data, L2 * 2 - L1, L1 + 1); - test_hbitmap_next_zero_check_range(data, L2 * 2, L2); + test_hbitmap_next_x_check(data, L2 * 2 - L1); + test_hbitmap_next_x_check(data, L2 * 2 - 2); + test_hbitmap_next_x_check(data, L2 * 2 - 1); + test_hbitmap_next_x_check(data, L2 * 2); + test_hbitmap_next_x_check(data, L2 * 2 + 1); + test_hbitmap_next_x_check(data, L2 * 2 + L1); + test_hbitmap_next_x_check(data, L3 - 1); + test_hbitmap_next_x_check_range(data, L2 * 2 - L1, L1 + 1); + test_hbitmap_next_x_check_range(data, L2 * 2, L2); hbitmap_set(data->hb, 0, L3); - test_hbitmap_next_zero_check(data, 0); + test_hbitmap_next_x_check(data, 0); } -static void test_hbitmap_next_zero_0(TestHBitmapData *data, const void *unused) +static void test_hbitmap_next_x_0(TestHBitmapData *data, const void *unused) { - test_hbitmap_next_zero_do(data, 0); + test_hbitmap_next_x_do(data, 0); } -static void test_hbitmap_next_zero_4(TestHBitmapData *data, const void *unused) +static void test_hbitmap_next_x_4(TestHBitmapData *data, const void *unused) { - test_hbitmap_next_zero_do(data, 4); + test_hbitmap_next_x_do(data, 4); } -static void test_hbitmap_next_zero_after_truncate(TestHBitmapData *data, - const void *unused) +static void test_hbitmap_next_x_after_truncate(TestHBitmapData *data, + const void *unused) { hbitmap_test_init(data, L1, 0); hbitmap_test_truncate_impl(data, L1 * 2); hbitmap_set(data->hb, 0, L1); - test_hbitmap_next_zero_check(data, 0); + test_hbitmap_next_x_check(data, 0); } -static void test_hbitmap_next_dirty_area_check(TestHBitmapData *data, - uint64_t offset, - uint64_t count) +static void test_hbitmap_next_dirty_area_check_limited(TestHBitmapData *data, + int64_t offset, + int64_t count, + int64_t max_dirty) { - uint64_t off1, off2; - uint64_t len1 = 0, len2; + int64_t off1, off2; + int64_t len1 = 0, len2; bool ret1, ret2; int64_t end; - off1 = offset; - len1 = count; - ret1 = hbitmap_next_dirty_area(data->hb, &off1, &len1); + ret1 = hbitmap_next_dirty_area(data->hb, + offset, count == INT64_MAX ? INT64_MAX : offset + count, max_dirty, + &off1, &len1); end = offset > data->size || data->size - offset < count ? data->size : offset + count; @@ -1033,45 +941,52 @@ static void test_hbitmap_next_dirty_area_check(TestHBitmapData *data, ; } - for (len2 = 1; off2 + len2 < end && hbitmap_get(data->hb, off2 + len2); - len2++) { + for (len2 = 1; (off2 + len2 < end && len2 < max_dirty && + hbitmap_get(data->hb, off2 + len2)); len2++) + { ; } ret2 = off2 < end; - if (!ret2) { - /* leave unchanged */ - off2 = offset; - len2 = count; - } - g_assert_cmpint(ret1, ==, ret2); - g_assert_cmpint(off1, ==, off2); - g_assert_cmpint(len1, ==, len2); + + if (ret2) { + g_assert_cmpint(off1, ==, off2); + g_assert_cmpint(len1, ==, len2); + } +} + +static void test_hbitmap_next_dirty_area_check(TestHBitmapData *data, + int64_t offset, int64_t count) +{ + test_hbitmap_next_dirty_area_check_limited(data, offset, count, INT64_MAX); } static void test_hbitmap_next_dirty_area_do(TestHBitmapData *data, int granularity) { hbitmap_test_init(data, L3, granularity); - test_hbitmap_next_dirty_area_check(data, 0, UINT64_MAX); + test_hbitmap_next_dirty_area_check(data, 0, INT64_MAX); test_hbitmap_next_dirty_area_check(data, 0, 1); test_hbitmap_next_dirty_area_check(data, L3 - 1, 1); + test_hbitmap_next_dirty_area_check_limited(data, 0, INT64_MAX, 1); hbitmap_set(data->hb, L2, 1); test_hbitmap_next_dirty_area_check(data, 0, 1); test_hbitmap_next_dirty_area_check(data, 0, L2); - test_hbitmap_next_dirty_area_check(data, 0, UINT64_MAX); - test_hbitmap_next_dirty_area_check(data, L2 - 1, UINT64_MAX); + test_hbitmap_next_dirty_area_check(data, 0, INT64_MAX); + test_hbitmap_next_dirty_area_check(data, L2 - 1, INT64_MAX); test_hbitmap_next_dirty_area_check(data, L2 - 1, 1); test_hbitmap_next_dirty_area_check(data, L2 - 1, 2); test_hbitmap_next_dirty_area_check(data, L2 - 1, 3); - test_hbitmap_next_dirty_area_check(data, L2, UINT64_MAX); + test_hbitmap_next_dirty_area_check(data, L2, INT64_MAX); test_hbitmap_next_dirty_area_check(data, L2, 1); test_hbitmap_next_dirty_area_check(data, L2 + 1, 1); + test_hbitmap_next_dirty_area_check_limited(data, 0, INT64_MAX, 1); + test_hbitmap_next_dirty_area_check_limited(data, L2 - 1, 2, 1); hbitmap_set(data->hb, L2 + 5, L1); - test_hbitmap_next_dirty_area_check(data, 0, UINT64_MAX); + test_hbitmap_next_dirty_area_check(data, 0, INT64_MAX); test_hbitmap_next_dirty_area_check(data, L2 - 2, 8); test_hbitmap_next_dirty_area_check(data, L2 + 1, 5); test_hbitmap_next_dirty_area_check(data, L2 + 1, 3); @@ -1081,18 +996,23 @@ static void test_hbitmap_next_dirty_area_do(TestHBitmapData *data, test_hbitmap_next_dirty_area_check(data, L2 + L1, L1); test_hbitmap_next_dirty_area_check(data, L2, 0); test_hbitmap_next_dirty_area_check(data, L2 + 1, 0); + test_hbitmap_next_dirty_area_check_limited(data, L2 + 3, INT64_MAX, 3); + test_hbitmap_next_dirty_area_check_limited(data, L2 + 3, 7, 10); hbitmap_set(data->hb, L2 * 2, L3 - L2 * 2); - test_hbitmap_next_dirty_area_check(data, 0, UINT64_MAX); - test_hbitmap_next_dirty_area_check(data, L2, UINT64_MAX); - test_hbitmap_next_dirty_area_check(data, L2 + 1, UINT64_MAX); - test_hbitmap_next_dirty_area_check(data, L2 + 5 + L1 - 1, UINT64_MAX); + test_hbitmap_next_dirty_area_check(data, 0, INT64_MAX); + test_hbitmap_next_dirty_area_check(data, L2, INT64_MAX); + test_hbitmap_next_dirty_area_check(data, L2 + 1, INT64_MAX); + test_hbitmap_next_dirty_area_check(data, L2 + 5 + L1 - 1, INT64_MAX); test_hbitmap_next_dirty_area_check(data, L2 + 5 + L1, 5); test_hbitmap_next_dirty_area_check(data, L2 * 2 - L1, L1 + 1); test_hbitmap_next_dirty_area_check(data, L2 * 2, L2); + test_hbitmap_next_dirty_area_check_limited(data, L2 * 2 + 1, INT64_MAX, 5); + test_hbitmap_next_dirty_area_check_limited(data, L2 * 2 + 1, 10, 5); + test_hbitmap_next_dirty_area_check_limited(data, L2 * 2 + 1, 2, 5); hbitmap_set(data->hb, 0, L3); - test_hbitmap_next_dirty_area_check(data, 0, UINT64_MAX); + test_hbitmap_next_dirty_area_check(data, 0, INT64_MAX); } static void test_hbitmap_next_dirty_area_0(TestHBitmapData *data, @@ -1119,7 +1039,7 @@ static void test_hbitmap_next_dirty_area_after_truncate(TestHBitmapData *data, hbitmap_test_init(data, L1, 0); hbitmap_test_truncate_impl(data, L1 * 2); hbitmap_set(data->hb, L1 + 1, 1); - test_hbitmap_next_dirty_area_check(data, 0, UINT64_MAX); + test_hbitmap_next_dirty_area_check(data, 0, INT64_MAX); } int main(int argc, char **argv) @@ -1165,12 +1085,6 @@ int main(int argc, char **argv) hbitmap_test_add("/hbitmap/truncate/shrink/large", test_hbitmap_truncate_shrink_large); - hbitmap_test_add("/hbitmap/meta/zero", test_hbitmap_meta_zero); - hbitmap_test_add("/hbitmap/meta/one", test_hbitmap_meta_one); - hbitmap_test_add("/hbitmap/meta/byte", test_hbitmap_meta_byte); - hbitmap_test_add("/hbitmap/meta/word", test_hbitmap_meta_word); - hbitmap_test_add("/hbitmap/meta/sector", test_hbitmap_meta_sector); - hbitmap_test_add("/hbitmap/serialize/align", test_hbitmap_serialize_align); hbitmap_test_add("/hbitmap/serialize/basic", @@ -1183,12 +1097,12 @@ int main(int argc, char **argv) hbitmap_test_add("/hbitmap/iter/iter_and_reset", test_hbitmap_iter_and_reset); - hbitmap_test_add("/hbitmap/next_zero/next_zero_0", - test_hbitmap_next_zero_0); - hbitmap_test_add("/hbitmap/next_zero/next_zero_4", - test_hbitmap_next_zero_4); - hbitmap_test_add("/hbitmap/next_zero/next_zero_after_truncate", - test_hbitmap_next_zero_after_truncate); + hbitmap_test_add("/hbitmap/next_zero/next_x_0", + test_hbitmap_next_x_0); + hbitmap_test_add("/hbitmap/next_zero/next_x_4", + test_hbitmap_next_x_4); + hbitmap_test_add("/hbitmap/next_zero/next_x_after_truncate", + test_hbitmap_next_x_after_truncate); hbitmap_test_add("/hbitmap/next_dirty_area/next_dirty_area_0", test_hbitmap_next_dirty_area_0); diff --git a/tests/test-io-channel-tls.c b/tests/test-io-channel-tls.c index 3c9ef6f941..ad7554c534 100644 --- a/tests/test-io-channel-tls.c +++ b/tests/test-io-channel-tls.c @@ -273,7 +273,7 @@ int main(int argc, char **argv) module_call_init(MODULE_INIT_QOM); g_test_init(&argc, &argv, NULL); - setenv("GNUTLS_FORCE_FIPS_MODE", "2", 1); + g_setenv("GNUTLS_FORCE_FIPS_MODE", "2", 1); mkdir(WORKDIR, 0700); diff --git a/tests/test-keyval.c b/tests/test-keyval.c index 09b0ae3c68..e331a84149 100644 --- a/tests/test-keyval.c +++ b/tests/test-keyval.c @@ -478,7 +478,7 @@ static void test_keyval_visit_size(void) visit_free(v); /* Trailing crap */ - qdict = keyval_parse("sz1=16E,sz2=16Gi", NULL, &error_abort); + qdict = keyval_parse("sz1=0Z,sz2=16Gi", NULL, &error_abort); v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); qobject_unref(qdict); visit_start_struct(v, NULL, NULL, 0, &error_abort); diff --git a/tests/test-logging.c b/tests/test-logging.c index a12585f70a..6387e4933f 100644 --- a/tests/test-logging.c +++ b/tests/test-logging.c @@ -108,6 +108,82 @@ static void test_parse_path(gconstpointer data) error_free_or_abort(&err); } +static void test_logfile_write(gconstpointer data) +{ + QemuLogFile *logfile; + QemuLogFile *logfile2; + gchar const *dir = data; + Error *err = NULL; + g_autofree gchar *file_path = NULL; + g_autofree gchar *file_path1 = NULL; + FILE *orig_fd; + + /* + * Before starting test, set log flags, to ensure the file gets + * opened below with the call to qemu_set_log_filename(). + * In cases where a logging backend other than log is used, + * this is needed. + */ + qemu_set_log(CPU_LOG_TB_OUT_ASM); + file_path = g_build_filename(dir, "qemu_test_log_write0.log", NULL); + file_path1 = g_build_filename(dir, "qemu_test_log_write1.log", NULL); + + /* + * Test that even if an open file handle is changed, + * our handle remains valid due to RCU. + */ + qemu_set_log_filename(file_path, &err); + g_assert(!err); + rcu_read_lock(); + logfile = atomic_rcu_read(&qemu_logfile); + orig_fd = logfile->fd; + g_assert(logfile && logfile->fd); + fprintf(logfile->fd, "%s 1st write to file\n", __func__); + fflush(logfile->fd); + + /* Change the logfile and ensure that the handle is still valid. */ + qemu_set_log_filename(file_path1, &err); + g_assert(!err); + logfile2 = atomic_rcu_read(&qemu_logfile); + g_assert(logfile->fd == orig_fd); + g_assert(logfile2->fd != logfile->fd); + fprintf(logfile->fd, "%s 2nd write to file\n", __func__); + fflush(logfile->fd); + rcu_read_unlock(); +} + +static void test_logfile_lock(gconstpointer data) +{ + FILE *logfile; + gchar const *dir = data; + Error *err = NULL; + g_autofree gchar *file_path = NULL; + + file_path = g_build_filename(dir, "qemu_test_logfile_lock0.log", NULL); + + /* + * Test the use of the logfile lock, such + * that even if an open file handle is closed, + * our handle remains valid for use due to RCU. + */ + qemu_set_log_filename(file_path, &err); + logfile = qemu_log_lock(); + g_assert(logfile); + fprintf(logfile, "%s 1st write to file\n", __func__); + fflush(logfile); + + /* + * Initiate a close file and make sure our handle remains + * valid since we still have the logfile lock. + */ + qemu_log_close(); + fprintf(logfile, "%s 2nd write to file\n", __func__); + fflush(logfile); + qemu_log_unlock(logfile); + + g_assert(!err); +} + /* Remove a directory and all its entries (non-recursive). */ static void rmdir_full(gchar const *root) { @@ -134,6 +210,10 @@ int main(int argc, char **argv) g_test_add_func("/logging/parse_range", test_parse_range); g_test_add_data_func("/logging/parse_path", tmp_path, test_parse_path); + g_test_add_data_func("/logging/logfile_write_path", + tmp_path, test_logfile_write); + g_test_add_data_func("/logging/logfile_lock_path", + tmp_path, test_logfile_lock); rc = g_test_run(); diff --git a/tests/test-qdev-global-props.c b/tests/test-qdev-global-props.c index 4ed3d06f4e..270c690479 100644 --- a/tests/test-qdev-global-props.c +++ b/tests/test-qdev-global-props.c @@ -55,7 +55,7 @@ static void static_prop_class_init(ObjectClass *oc, void *data) DeviceClass *dc = DEVICE_CLASS(oc); dc->realize = NULL; - dc->props = static_props; + device_class_set_props(dc, static_props); } static const TypeInfo static_prop_type = { diff --git a/tests/test-qga.c b/tests/test-qga.c index 1ca49bbced..d2b2435bb4 100644 --- a/tests/test-qga.c +++ b/tests/test-qga.c @@ -143,7 +143,7 @@ static void qmp_assertion_message_error(const char *domain, static void test_qga_sync_delimited(gconstpointer fix) { const TestFixture *fixture = fix; - guint32 v, r = g_random_int(); + guint32 v, r = g_test_rand_int(); unsigned char c; QDict *ret; @@ -186,7 +186,7 @@ static void test_qga_sync_delimited(gconstpointer fix) static void test_qga_sync(gconstpointer fix) { const TestFixture *fixture = fix; - guint32 v, r = g_random_int(); + guint32 v, r = g_test_rand_int(); QDict *ret; /* diff --git a/tests/test-qmp-cmds.c b/tests/test-qmp-cmds.c index 27b0afe55a..d12ff47e26 100644 --- a/tests/test-qmp-cmds.c +++ b/tests/test-qmp-cmds.c @@ -1,5 +1,6 @@ #include "qemu/osdep.h" #include "qapi/qmp/qdict.h" +#include "qapi/qmp/qjson.h" #include "qapi/qmp/qnum.h" #include "qapi/qmp/qstring.h" #include "qapi/error.h" @@ -7,6 +8,7 @@ #include "tests/test-qapi-types.h" #include "tests/test-qapi-visit.h" #include "test-qapi-commands.h" +#include "test-qapi-init-commands.h" static QmpCommandList qmp_commands; @@ -43,7 +45,7 @@ void qmp_user_def_cmd1(UserDefOne * ud1, Error **errp) { } -void qmp_test_features(FeatureStruct0 *fs0, FeatureStruct1 *fs1, +void qmp_test_features0(FeatureStruct0 *fs0, FeatureStruct1 *fs1, FeatureStruct2 *fs2, FeatureStruct3 *fs3, FeatureStruct4 *fs4, CondFeatureStruct1 *cfs1, CondFeatureStruct2 *cfs2, CondFeatureStruct3 *cfs3, @@ -51,10 +53,6 @@ void qmp_test_features(FeatureStruct0 *fs0, FeatureStruct1 *fs1, { } -void qmp_test_command_features0(Error **errp) -{ -} - void qmp_test_command_features1(Error **errp) { } @@ -144,66 +142,87 @@ __org_qemu_x_Union1 *qmp___org_qemu_x_command(__org_qemu_x_EnumList *a, } -/* test commands with no input and no return value */ -static void test_dispatch_cmd(void) +static QObject *do_qmp_dispatch(bool allow_oob, const char *template, ...) { - QDict *req = qdict_new(); - QDict *resp; + va_list ap; + QDict *req, *resp; + QObject *ret; - qdict_put_str(req, "execute", "user_def_cmd"); + va_start(ap, template); + req = qdict_from_vjsonf_nofail(template, ap); + va_end(ap); - resp = qmp_dispatch(&qmp_commands, QOBJECT(req), false); - assert(resp != NULL); - assert(!qdict_haskey(resp, "error")); + resp = qmp_dispatch(&qmp_commands, QOBJECT(req), allow_oob); + g_assert(resp); + ret = qdict_get(resp, "return"); + g_assert(ret); + g_assert(qdict_size(resp) == 1); + + qobject_ref(ret); + qobject_unref(resp); + qobject_unref(req); + return ret; +} + +static void do_qmp_dispatch_error(bool allow_oob, ErrorClass cls, + const char *template, ...) +{ + va_list ap; + QDict *req, *resp; + QDict *error; + + va_start(ap, template); + req = qdict_from_vjsonf_nofail(template, ap); + va_end(ap); + + resp = qmp_dispatch(&qmp_commands, QOBJECT(req), allow_oob); + g_assert(resp); + error = qdict_get_qdict(resp, "error"); + g_assert(error); + g_assert_cmpstr(qdict_get_try_str(error, "class"), + ==, QapiErrorClass_str(cls)); + g_assert(qdict_get_try_str(error, "desc")); + g_assert(qdict_size(error) == 2); + g_assert(qdict_size(resp) == 1); qobject_unref(resp); qobject_unref(req); } +/* test commands with no input and no return value */ +static void test_dispatch_cmd(void) +{ + QDict *ret; + + ret = qobject_to(QDict, + do_qmp_dispatch(false, + "{ 'execute': 'user_def_cmd' }")); + assert(ret && qdict_size(ret) == 0); + qobject_unref(ret); +} + static void test_dispatch_cmd_oob(void) { - QDict *req = qdict_new(); - QDict *resp; + QDict *ret; - qdict_put_str(req, "exec-oob", "test-flags-command"); - - resp = qmp_dispatch(&qmp_commands, QOBJECT(req), true); - assert(resp != NULL); - assert(!qdict_haskey(resp, "error")); - - qobject_unref(resp); - qobject_unref(req); + ret = qobject_to(QDict, + do_qmp_dispatch(true, + "{ 'exec-oob': 'test-flags-command' }")); + assert(ret && qdict_size(ret) == 0); + qobject_unref(ret); } /* test commands that return an error due to invalid parameters */ static void test_dispatch_cmd_failure(void) { - QDict *req = qdict_new(); - QDict *args = qdict_new(); - QDict *resp; + /* missing arguments */ + do_qmp_dispatch_error(false, ERROR_CLASS_GENERIC_ERROR, + "{ 'execute': 'user_def_cmd2' }"); - qdict_put_str(req, "execute", "user_def_cmd2"); - - resp = qmp_dispatch(&qmp_commands, QOBJECT(req), false); - assert(resp != NULL); - assert(qdict_haskey(resp, "error")); - - qobject_unref(resp); - qobject_unref(req); - - /* check that with extra arguments it throws an error */ - req = qdict_new(); - qdict_put_int(args, "a", 66); - qdict_put(req, "arguments", args); - - qdict_put_str(req, "execute", "user_def_cmd"); - - resp = qmp_dispatch(&qmp_commands, QOBJECT(req), false); - assert(resp != NULL); - assert(qdict_haskey(resp, "error")); - - qobject_unref(resp); - qobject_unref(req); + /* extra arguments */ + do_qmp_dispatch_error(false, ERROR_CLASS_GENERIC_ERROR, + "{ 'execute': 'user_def_cmd'," + " 'arguments': { 'a': 66 } }"); } static void test_dispatch_cmd_success_response(void) @@ -217,43 +236,18 @@ static void test_dispatch_cmd_success_response(void) qobject_unref(req); } -static QObject *test_qmp_dispatch(QDict *req) -{ - QDict *resp; - QObject *ret; - - resp = qmp_dispatch(&qmp_commands, QOBJECT(req), false); - assert(resp && !qdict_haskey(resp, "error")); - ret = qdict_get(resp, "return"); - assert(ret); - qobject_ref(ret); - qobject_unref(resp); - return ret; -} - /* test commands that involve both input parameters and return values */ static void test_dispatch_cmd_io(void) { - QDict *req = qdict_new(); - QDict *args = qdict_new(); - QDict *args3 = qdict_new(); - QDict *ud1a = qdict_new(); - QDict *ud1b = qdict_new(); QDict *ret, *ret_dict, *ret_dict_dict, *ret_dict_dict_userdef; QDict *ret_dict_dict2, *ret_dict_dict2_userdef; QNum *ret3; int64_t val; - qdict_put_int(ud1a, "integer", 42); - qdict_put_str(ud1a, "string", "hello"); - qdict_put_int(ud1b, "integer", 422); - qdict_put_str(ud1b, "string", "hello2"); - qdict_put(args, "ud1a", ud1a); - qdict_put(args, "ud1b", ud1b); - qdict_put(req, "arguments", args); - qdict_put_str(req, "execute", "user_def_cmd2"); - - ret = qobject_to(QDict, test_qmp_dispatch(req)); + ret = qobject_to(QDict, do_qmp_dispatch(false, + "{ 'execute': 'user_def_cmd2', 'arguments': {" + " 'ud1a': { 'integer': 42, 'string': 'hello' }," + " 'ud1b': { 'integer': 422, 'string': 'hello2' } } }")); assert(!strcmp(qdict_get_str(ret, "string0"), "blah1")); ret_dict = qdict_get_qdict(ret, "dict1"); @@ -270,16 +264,11 @@ static void test_dispatch_cmd_io(void) assert(!strcmp(qdict_get_str(ret_dict_dict2, "string"), "blah4")); qobject_unref(ret); - qdict_put_int(args3, "a", 66); - qdict_put(req, "arguments", args3); - qdict_put_str(req, "execute", "guest-get-time"); - - ret3 = qobject_to(QNum, test_qmp_dispatch(req)); + ret3 = qobject_to(QNum, do_qmp_dispatch(false, + "{ 'execute': 'guest-get-time', 'arguments': { 'a': 66 } }")); g_assert(qnum_get_try_int(ret3, &val)); g_assert_cmpint(val, ==, 66); qobject_unref(ret3); - - qobject_unref(req); } /* test generated dealloc functions for generated types */ diff --git a/tests/test-qmp-event.c b/tests/test-qmp-event.c index eee7e08ab6..7dd0053190 100644 --- a/tests/test-qmp-event.c +++ b/tests/test-qmp-event.c @@ -17,6 +17,7 @@ #include "qapi/error.h" #include "qapi/qmp/qbool.h" #include "qapi/qmp/qdict.h" +#include "qapi/qmp/qjson.h" #include "qapi/qmp/qnum.h" #include "qapi/qmp/qstring.h" #include "qapi/qmp-event.h" @@ -25,75 +26,12 @@ typedef struct TestEventData { QDict *expect; + bool emitted; } TestEventData; -typedef struct QDictCmpData { - QDict *expect; - bool result; -} QDictCmpData; - TestEventData *test_event_data; static GMutex test_event_lock; -/* Only compares bool, int, string */ -static -void qdict_cmp_do_simple(const char *key, QObject *obj1, void *opaque) - -{ - QObject *obj2; - QDictCmpData d_new, *d = opaque; - int64_t val1, val2; - - if (!d->result) { - return; - } - - obj2 = qdict_get(d->expect, key); - if (!obj2) { - d->result = false; - return; - } - - if (qobject_type(obj1) != qobject_type(obj2)) { - d->result = false; - return; - } - - switch (qobject_type(obj1)) { - case QTYPE_QBOOL: - d->result = (qbool_get_bool(qobject_to(QBool, obj1)) == - qbool_get_bool(qobject_to(QBool, obj2))); - return; - case QTYPE_QNUM: - g_assert(qnum_get_try_int(qobject_to(QNum, obj1), &val1)); - g_assert(qnum_get_try_int(qobject_to(QNum, obj2), &val2)); - d->result = val1 == val2; - return; - case QTYPE_QSTRING: - d->result = g_strcmp0(qstring_get_str(qobject_to(QString, obj1)), - qstring_get_str(qobject_to(QString, obj2))) == 0; - return; - case QTYPE_QDICT: - d_new.expect = qobject_to(QDict, obj2); - d_new.result = true; - qdict_iter(qobject_to(QDict, obj1), qdict_cmp_do_simple, &d_new); - d->result = d_new.result; - return; - default: - abort(); - } -} - -static bool qdict_cmp_simple(QDict *a, QDict *b) -{ - QDictCmpData d; - - d.expect = b; - d.result = true; - qdict_iter(a, qdict_cmp_do_simple, &d); - return d.result; -} - void test_qapi_event_emit(test_QAPIEvent event, QDict *d) { QDict *t; @@ -114,8 +52,8 @@ void test_qapi_event_emit(test_QAPIEvent event, QDict *d) qdict_del(d, "timestamp"); - g_assert(qdict_cmp_simple(d, test_event_data->expect)); - + g_assert(qobject_is_equal(QOBJECT(d), QOBJECT(test_event_data->expect))); + test_event_data->emitted = true; } static void event_prepare(TestEventData *data, @@ -124,17 +62,13 @@ static void event_prepare(TestEventData *data, /* Global variable test_event_data was used to pass the expectation, so test cases can't be executed at same time. */ g_mutex_lock(&test_event_lock); - - data->expect = qdict_new(); test_event_data = data; } static void event_teardown(TestEventData *data, const void *unused) { - qobject_unref(data->expect); test_event_data = NULL; - g_mutex_unlock(&test_event_lock); } @@ -152,90 +86,58 @@ static void event_test_add(const char *testpath, static void test_event_a(TestEventData *data, const void *unused) { - QDict *d; - d = data->expect; - qdict_put_str(d, "event", "EVENT_A"); + data->expect = qdict_from_jsonf_nofail("{ 'event': 'EVENT_A' }"); qapi_event_send_event_a(); + g_assert(data->emitted); + qobject_unref(data->expect); } static void test_event_b(TestEventData *data, const void *unused) { - QDict *d; - d = data->expect; - qdict_put_str(d, "event", "EVENT_B"); + data->expect = qdict_from_jsonf_nofail("{ 'event': 'EVENT_B' }"); qapi_event_send_event_b(); + g_assert(data->emitted); + qobject_unref(data->expect); } static void test_event_c(TestEventData *data, const void *unused) { - QDict *d, *d_data, *d_b; - - UserDefOne b; - b.integer = 2; - b.string = g_strdup("test1"); - b.has_enum1 = false; - - d_b = qdict_new(); - qdict_put_int(d_b, "integer", 2); - qdict_put_str(d_b, "string", "test1"); - - d_data = qdict_new(); - qdict_put_int(d_data, "a", 1); - qdict_put(d_data, "b", d_b); - qdict_put_str(d_data, "c", "test2"); - - d = data->expect; - qdict_put_str(d, "event", "EVENT_C"); - qdict_put(d, "data", d_data); + UserDefOne b = { .integer = 2, .string = (char *)"test1" }; + data->expect = qdict_from_jsonf_nofail( + "{ 'event': 'EVENT_C', 'data': {" + " 'a': 1, 'b': { 'integer': 2, 'string': 'test1' }, 'c': 'test2' } }"); qapi_event_send_event_c(true, 1, true, &b, "test2"); - - g_free(b.string); + g_assert(data->emitted); + qobject_unref(data->expect); } /* Complex type */ static void test_event_d(TestEventData *data, const void *unused) { - UserDefOne struct1; - EventStructOne a; - QDict *d, *d_data, *d_a, *d_struct1; - - struct1.integer = 2; - struct1.string = g_strdup("test1"); - struct1.has_enum1 = true; - struct1.enum1 = ENUM_ONE_VALUE1; - - a.struct1 = &struct1; - a.string = g_strdup("test2"); - a.has_enum2 = true; - a.enum2 = ENUM_ONE_VALUE2; - - d_struct1 = qdict_new(); - qdict_put_int(d_struct1, "integer", 2); - qdict_put_str(d_struct1, "string", "test1"); - qdict_put_str(d_struct1, "enum1", "value1"); - - d_a = qdict_new(); - qdict_put(d_a, "struct1", d_struct1); - qdict_put_str(d_a, "string", "test2"); - qdict_put_str(d_a, "enum2", "value2"); - - d_data = qdict_new(); - qdict_put(d_data, "a", d_a); - qdict_put_str(d_data, "b", "test3"); - qdict_put_str(d_data, "enum3", "value3"); - - d = data->expect; - qdict_put_str(d, "event", "EVENT_D"); - qdict_put(d, "data", d_data); + UserDefOne struct1 = { + .integer = 2, .string = (char *)"test1", + .has_enum1 = true, .enum1 = ENUM_ONE_VALUE1, + }; + EventStructOne a = { + .struct1 = &struct1, + .string = (char *)"test2", + .has_enum2 = true, + .enum2 = ENUM_ONE_VALUE2, + }; + data->expect = qdict_from_jsonf_nofail( + "{ 'event': 'EVENT_D', 'data': {" + " 'a': {" + " 'struct1': { 'integer': 2, 'string': 'test1', 'enum1': 'value1' }," + " 'string': 'test2', 'enum2': 'value2' }," + " 'b': 'test3', 'enum3': 'value3' } }"); qapi_event_send_event_d(&a, "test3", false, NULL, true, ENUM_ONE_VALUE3); - - g_free(struct1.string); - g_free(a.string); + g_assert(data->emitted); + qobject_unref(data->expect); } int main(int argc, char **argv) diff --git a/tests/test-qobject-output-visitor.c b/tests/test-qobject-output-visitor.c index 3e993e5ba8..d7761ebf84 100644 --- a/tests/test-qobject-output-visitor.c +++ b/tests/test-qobject-output-visitor.c @@ -145,10 +145,10 @@ static void test_visitor_out_enum_errors(TestOutputVisitorData *data, const void *unused) { EnumOne i, bad_values[] = { ENUM_ONE__MAX, -1 }; - Error *err; for (i = 0; i < ARRAY_SIZE(bad_values) ; i++) { - err = NULL; + Error *err = NULL; + visit_type_EnumOne(data->ov, "unused", &bad_values[i], &err); error_free_or_abort(&err); visitor_reset(data); @@ -240,11 +240,11 @@ static void test_visitor_out_struct_errors(TestOutputVisitorData *data, EnumOne bad_values[] = { ENUM_ONE__MAX, -1 }; UserDefOne u = {0}; UserDefOne *pu = &u; - Error *err; int i; for (i = 0; i < ARRAY_SIZE(bad_values) ; i++) { - err = NULL; + Error *err = NULL; + u.has_enum1 = true; u.enum1 = bad_values[i]; visit_type_UserDefOne(data->ov, "unused", &pu, &err); diff --git a/tests/test-rcu-list.c b/tests/test-rcu-list.c index 6f076473e0..92be51ec50 100644 --- a/tests/test-rcu-list.c +++ b/tests/test-rcu-list.c @@ -93,6 +93,8 @@ struct list_element { QSIMPLEQ_ENTRY(list_element) entry; #elif TEST_LIST_TYPE == 3 QTAILQ_ENTRY(list_element) entry; +#elif TEST_LIST_TYPE == 4 + QSLIST_ENTRY(list_element) entry; #else #error Invalid TEST_LIST_TYPE #endif @@ -144,6 +146,20 @@ static QTAILQ_HEAD(, list_element) Q_list_head; #define TEST_LIST_INSERT_HEAD_RCU QTAILQ_INSERT_HEAD_RCU #define TEST_LIST_FOREACH_RCU QTAILQ_FOREACH_RCU #define TEST_LIST_FOREACH_SAFE_RCU QTAILQ_FOREACH_SAFE_RCU + +#elif TEST_LIST_TYPE == 4 +static QSLIST_HEAD(, list_element) Q_list_head; + +#define TEST_NAME "qslist" +#define TEST_LIST_REMOVE_RCU(el, f) \ + QSLIST_REMOVE_RCU(&Q_list_head, el, list_element, f) + +#define TEST_LIST_INSERT_AFTER_RCU(list_el, el, f) \ + QSLIST_INSERT_AFTER_RCU(&Q_list_head, list_el, el, f) + +#define TEST_LIST_INSERT_HEAD_RCU QSLIST_INSERT_HEAD_RCU +#define TEST_LIST_FOREACH_RCU QSLIST_FOREACH_RCU +#define TEST_LIST_FOREACH_SAFE_RCU QSLIST_FOREACH_SAFE_RCU #else #error Invalid TEST_LIST_TYPE #endif @@ -219,7 +235,7 @@ static void *rcu_q_updater(void *arg) j++; if (target_el == j) { struct list_element *new_el = g_new(struct list_element, 1); - n_nodes += n_nodes_local; + n_nodes_local++; TEST_LIST_INSERT_AFTER_RCU(el, new_el, entry); break; } diff --git a/tests/test-rcu-slist.c b/tests/test-rcu-slist.c new file mode 100644 index 0000000000..868e1e472e --- /dev/null +++ b/tests/test-rcu-slist.c @@ -0,0 +1,2 @@ +#define TEST_LIST_TYPE 4 +#include "test-rcu-list.c" diff --git a/tests/test-replication.c b/tests/test-replication.c index 4747d8a6be..cbc37db2df 100644 --- a/tests/test-replication.c +++ b/tests/test-replication.c @@ -490,6 +490,56 @@ static void test_secondary_stop(void) teardown_secondary(); } +static void test_secondary_continuous_replication(void) +{ + BlockBackend *top_blk, *local_blk; + Error *local_err = NULL; + + top_blk = start_secondary(); + replication_start_all(REPLICATION_MODE_SECONDARY, &local_err); + g_assert(!local_err); + + /* write 0x22 to s_local_disk (IMG_SIZE / 2, IMG_SIZE) */ + local_blk = blk_by_name(S_LOCAL_DISK_ID); + test_blk_write(local_blk, 0x22, IMG_SIZE / 2, IMG_SIZE / 2, false); + + /* replication will backup s_local_disk to s_hidden_disk */ + test_blk_read(top_blk, 0x11, IMG_SIZE / 2, + IMG_SIZE / 2, 0, IMG_SIZE, false); + + /* write 0x33 to s_active_disk (0, IMG_SIZE / 2) */ + test_blk_write(top_blk, 0x33, 0, IMG_SIZE / 2, false); + + /* do failover (active commit) */ + replication_stop_all(true, &local_err); + g_assert(!local_err); + + /* it should ignore all requests from now on */ + + /* start after failover */ + replication_start_all(REPLICATION_MODE_PRIMARY, &local_err); + g_assert(!local_err); + + /* checkpoint */ + replication_do_checkpoint_all(&local_err); + g_assert(!local_err); + + /* stop */ + replication_stop_all(true, &local_err); + g_assert(!local_err); + + /* read from s_local_disk (0, IMG_SIZE / 2) */ + test_blk_read(top_blk, 0x33, 0, IMG_SIZE / 2, + 0, IMG_SIZE / 2, false); + + + /* read from s_local_disk (IMG_SIZE / 2, IMG_SIZE) */ + test_blk_read(top_blk, 0x22, IMG_SIZE / 2, + IMG_SIZE / 2, 0, IMG_SIZE, false); + + teardown_secondary(); +} + static void test_secondary_do_checkpoint(void) { BlockBackend *top_blk, *local_blk; @@ -585,6 +635,8 @@ int main(int argc, char **argv) g_test_add_func("/replication/secondary/write", test_secondary_write); g_test_add_func("/replication/secondary/start", test_secondary_start); g_test_add_func("/replication/secondary/stop", test_secondary_stop); + g_test_add_func("/replication/secondary/continuous_replication", + test_secondary_continuous_replication); g_test_add_func("/replication/secondary/do_checkpoint", test_secondary_do_checkpoint); g_test_add_func("/replication/secondary/get_error_all", diff --git a/tests/test-string-output-visitor.c b/tests/test-string-output-visitor.c index 02766c0f65..1be1540767 100644 --- a/tests/test-string-output-visitor.c +++ b/tests/test-string-output-visitor.c @@ -207,10 +207,10 @@ static void test_visitor_out_enum_errors(TestOutputVisitorData *data, const void *unused) { EnumOne i, bad_values[] = { ENUM_ONE__MAX, -1 }; - Error *err; for (i = 0; i < ARRAY_SIZE(bad_values) ; i++) { - err = NULL; + Error *err = NULL; + visit_type_EnumOne(data->ov, "unused", &bad_values[i], &err); error_free_or_abort(&err); } diff --git a/tests/test-util-filemonitor.c b/tests/test-util-filemonitor.c index 301cd2db61..45009c69f4 100644 --- a/tests/test-util-filemonitor.c +++ b/tests/test-util-filemonitor.c @@ -406,10 +406,21 @@ test_file_monitor_events(void) char *pathdst = NULL; QFileMonitorTestData data; GHashTable *ids = g_hash_table_new(g_int64_hash, g_int64_equal); + char *travis_arch; qemu_mutex_init(&data.lock); data.records = NULL; + /* + * This test does not work on Travis LXD containers since some + * syscalls are blocked in that environment. + */ + travis_arch = getenv("TRAVIS_ARCH"); + if (travis_arch && !g_str_equal(travis_arch, "x86_64")) { + g_test_skip("Test does not work on non-x86 Travis containers."); + return; + } + /* * The file monitor needs the main loop running in * order to receive events from inotify. We must diff --git a/tests/test-util-sockets.c b/tests/test-util-sockets.c index 8ce55efe70..5fd947c7bf 100644 --- a/tests/test-util-sockets.c +++ b/tests/test-util-sockets.c @@ -71,8 +71,8 @@ int monitor_get_fd(Monitor *mon, const char *fdname, Error **errp) */ __thread Monitor *cur_mon; int monitor_vprintf(Monitor *mon, const char *fmt, va_list ap) { abort(); } -void monitor_init_qmp(Chardev *chr, bool pretty) {} -void monitor_init_hmp(Chardev *chr, bool use_readline) {} +void monitor_init_qmp(Chardev *chr, bool pretty, Error **errp) {} +void monitor_init_hmp(Chardev *chr, bool use_readline, Error **errp) {} static void test_socket_fd_pass_name_good(void) diff --git a/tests/test-vmstate.c b/tests/test-vmstate.c index 1e5be1d4ff..f7b3868881 100644 --- a/tests/test-vmstate.c +++ b/tests/test-vmstate.c @@ -926,6 +926,28 @@ static const VMStateDescription vmstate_domain = { } }; +/* test QLIST Migration */ + +typedef struct TestQListElement { + uint32_t id; + QLIST_ENTRY(TestQListElement) next; +} TestQListElement; + +typedef struct TestQListContainer { + uint32_t id; + QLIST_HEAD(, TestQListElement) list; +} TestQListContainer; + +static const VMStateDescription vmstate_qlist_element = { + .name = "test/queue list", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(id, TestQListElement), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription vmstate_iommu = { .name = "iommu", .version_id = 1, @@ -939,6 +961,18 @@ static const VMStateDescription vmstate_iommu = { } }; +static const VMStateDescription vmstate_container = { + .name = "test/container/qlist", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(id, TestQListContainer), + VMSTATE_QLIST_V(list, TestQListContainer, 1, vmstate_qlist_element, + TestQListElement, next), + VMSTATE_END_OF_LIST() + } +}; + uint8_t first_domain_dump[] = { /* id */ 0x00, 0x0, 0x0, 0x6, @@ -1207,7 +1241,6 @@ static void test_gtree_load_iommu(void) TestGTreeIOMMU *orig_iommu = create_iommu(); QEMUFile *fsave, *fload; char eof; - int ret; fsave = open_test_file(true); qemu_put_buffer(fsave, iommu_dump, sizeof(iommu_dump)); @@ -1216,10 +1249,8 @@ static void test_gtree_load_iommu(void) fload = open_test_file(false); vmstate_load_state(fload, &vmstate_iommu, dest_iommu, 1); - ret = qemu_file_get_error(fload); eof = qemu_get_byte(fload); - ret = qemu_file_get_error(fload); - g_assert(!ret); + g_assert(!qemu_file_get_error(fload)); g_assert_cmpint(orig_iommu->id, ==, dest_iommu->id); g_assert_cmpint(eof, ==, QEMU_VM_EOF); @@ -1229,6 +1260,141 @@ static void test_gtree_load_iommu(void) qemu_fclose(fload); } +static uint8_t qlist_dump[] = { + 0x00, 0x00, 0x00, 0x01, /* container id */ + 0x1, /* start of a */ + 0x00, 0x00, 0x00, 0x0a, + 0x1, /* start of b */ + 0x00, 0x00, 0x0b, 0x00, + 0x1, /* start of c */ + 0x00, 0x0c, 0x00, 0x00, + 0x1, /* start of d */ + 0x0d, 0x00, 0x00, 0x00, + 0x0, /* end of list */ + QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */ +}; + +static TestQListContainer *alloc_container(void) +{ + TestQListElement *a = g_malloc(sizeof(TestQListElement)); + TestQListElement *b = g_malloc(sizeof(TestQListElement)); + TestQListElement *c = g_malloc(sizeof(TestQListElement)); + TestQListElement *d = g_malloc(sizeof(TestQListElement)); + TestQListContainer *container = g_malloc(sizeof(TestQListContainer)); + + a->id = 0x0a; + b->id = 0x0b00; + c->id = 0xc0000; + d->id = 0xd000000; + container->id = 1; + + QLIST_INIT(&container->list); + QLIST_INSERT_HEAD(&container->list, d, next); + QLIST_INSERT_HEAD(&container->list, c, next); + QLIST_INSERT_HEAD(&container->list, b, next); + QLIST_INSERT_HEAD(&container->list, a, next); + return container; +} + +static void free_container(TestQListContainer *container) +{ + TestQListElement *iter, *tmp; + + QLIST_FOREACH_SAFE(iter, &container->list, next, tmp) { + QLIST_REMOVE(iter, next); + g_free(iter); + } + g_free(container); +} + +static void compare_containers(TestQListContainer *c1, TestQListContainer *c2) +{ + TestQListElement *first_item_c1, *first_item_c2; + + while (!QLIST_EMPTY(&c1->list)) { + first_item_c1 = QLIST_FIRST(&c1->list); + first_item_c2 = QLIST_FIRST(&c2->list); + assert(first_item_c2); + assert(first_item_c1->id == first_item_c2->id); + QLIST_REMOVE(first_item_c1, next); + QLIST_REMOVE(first_item_c2, next); + g_free(first_item_c1); + g_free(first_item_c2); + } + assert(QLIST_EMPTY(&c2->list)); +} + +/* + * Check the prev & next fields are correct by doing list + * manipulations on the container. We will do that for both + * the source and the destination containers + */ +static void manipulate_container(TestQListContainer *c) +{ + TestQListElement *prev = NULL, *iter = QLIST_FIRST(&c->list); + TestQListElement *elem; + + elem = g_malloc(sizeof(TestQListElement)); + elem->id = 0x12; + QLIST_INSERT_AFTER(iter, elem, next); + + elem = g_malloc(sizeof(TestQListElement)); + elem->id = 0x13; + QLIST_INSERT_HEAD(&c->list, elem, next); + + while (iter) { + prev = iter; + iter = QLIST_NEXT(iter, next); + } + + elem = g_malloc(sizeof(TestQListElement)); + elem->id = 0x14; + QLIST_INSERT_BEFORE(prev, elem, next); + + elem = g_malloc(sizeof(TestQListElement)); + elem->id = 0x15; + QLIST_INSERT_AFTER(prev, elem, next); + + QLIST_REMOVE(prev, next); + g_free(prev); +} + +static void test_save_qlist(void) +{ + TestQListContainer *container = alloc_container(); + + save_vmstate(&vmstate_container, container); + compare_vmstate(qlist_dump, sizeof(qlist_dump)); + free_container(container); +} + +static void test_load_qlist(void) +{ + QEMUFile *fsave, *fload; + TestQListContainer *orig_container = alloc_container(); + TestQListContainer *dest_container = g_malloc0(sizeof(TestQListContainer)); + char eof; + + QLIST_INIT(&dest_container->list); + + fsave = open_test_file(true); + qemu_put_buffer(fsave, qlist_dump, sizeof(qlist_dump)); + g_assert(!qemu_file_get_error(fsave)); + qemu_fclose(fsave); + + fload = open_test_file(false); + vmstate_load_state(fload, &vmstate_container, dest_container, 1); + eof = qemu_get_byte(fload); + g_assert(!qemu_file_get_error(fload)); + g_assert_cmpint(eof, ==, QEMU_VM_EOF); + manipulate_container(orig_container); + manipulate_container(dest_container); + compare_containers(orig_container, dest_container); + free_container(orig_container); + free_container(dest_container); + qemu_fclose(fload); +} + typedef struct TmpTestStruct { TestStruct *parent; int64_t diff; @@ -1325,7 +1491,7 @@ int main(int argc, char **argv) module_call_init(MODULE_INIT_QOM); - setenv("QTEST_SILENT_ERRORS", "1", 1); + g_setenv("QTEST_SILENT_ERRORS", "1", 1); g_test_init(&argc, &argv, NULL); g_test_add_func("/vmstate/simple/primitive", test_simple_primitive); @@ -1353,6 +1519,8 @@ int main(int argc, char **argv) g_test_add_func("/vmstate/gtree/load/loaddomain", test_gtree_load_domain); g_test_add_func("/vmstate/gtree/save/saveiommu", test_gtree_save_iommu); g_test_add_func("/vmstate/gtree/load/loadiommu", test_gtree_load_iommu); + g_test_add_func("/vmstate/qlist/save/saveqlist", test_save_qlist); + g_test_add_func("/vmstate/qlist/load/loadqlist", test_load_qlist); g_test_add_func("/vmstate/tmp_struct", test_tmp_struct); g_test_run(); diff --git a/tests/test-x86-cpuid.c b/tests/test-x86-cpuid.c index 1942287f33..049030a50e 100644 --- a/tests/test-x86-cpuid.c +++ b/tests/test-x86-cpuid.c @@ -28,79 +28,101 @@ static void test_topo_bits(void) { - /* simple tests for 1 thread per core, 1 core per die, 1 die per package */ - g_assert_cmpuint(apicid_smt_width(1, 1, 1), ==, 0); - g_assert_cmpuint(apicid_core_width(1, 1, 1), ==, 0); - g_assert_cmpuint(apicid_die_width(1, 1, 1), ==, 0); + X86CPUTopoInfo topo_info = {0}; - g_assert_cmpuint(x86_apicid_from_cpu_idx(1, 1, 1, 0), ==, 0); - g_assert_cmpuint(x86_apicid_from_cpu_idx(1, 1, 1, 1), ==, 1); - g_assert_cmpuint(x86_apicid_from_cpu_idx(1, 1, 1, 2), ==, 2); - g_assert_cmpuint(x86_apicid_from_cpu_idx(1, 1, 1, 3), ==, 3); + /* simple tests for 1 thread per core, 1 core per die, 1 die per package */ + topo_info = (X86CPUTopoInfo) {0, 1, 1, 1}; + g_assert_cmpuint(apicid_smt_width(&topo_info), ==, 0); + g_assert_cmpuint(apicid_core_width(&topo_info), ==, 0); + g_assert_cmpuint(apicid_die_width(&topo_info), ==, 0); + + topo_info = (X86CPUTopoInfo) {0, 1, 1, 1}; + g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 0), ==, 0); + g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 1), ==, 1); + g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 2), ==, 2); + g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 3), ==, 3); /* Test field width calculation for multiple values */ - g_assert_cmpuint(apicid_smt_width(1, 1, 2), ==, 1); - g_assert_cmpuint(apicid_smt_width(1, 1, 3), ==, 2); - g_assert_cmpuint(apicid_smt_width(1, 1, 4), ==, 2); + topo_info = (X86CPUTopoInfo) {0, 1, 1, 2}; + g_assert_cmpuint(apicid_smt_width(&topo_info), ==, 1); + topo_info = (X86CPUTopoInfo) {0, 1, 1, 3}; + g_assert_cmpuint(apicid_smt_width(&topo_info), ==, 2); + topo_info = (X86CPUTopoInfo) {0, 1, 1, 4}; + g_assert_cmpuint(apicid_smt_width(&topo_info), ==, 2); - g_assert_cmpuint(apicid_smt_width(1, 1, 14), ==, 4); - g_assert_cmpuint(apicid_smt_width(1, 1, 15), ==, 4); - g_assert_cmpuint(apicid_smt_width(1, 1, 16), ==, 4); - g_assert_cmpuint(apicid_smt_width(1, 1, 17), ==, 5); + topo_info = (X86CPUTopoInfo) {0, 1, 1, 14}; + g_assert_cmpuint(apicid_smt_width(&topo_info), ==, 4); + topo_info = (X86CPUTopoInfo) {0, 1, 1, 15}; + g_assert_cmpuint(apicid_smt_width(&topo_info), ==, 4); + topo_info = (X86CPUTopoInfo) {0, 1, 1, 16}; + g_assert_cmpuint(apicid_smt_width(&topo_info), ==, 4); + topo_info = (X86CPUTopoInfo) {0, 1, 1, 17}; + g_assert_cmpuint(apicid_smt_width(&topo_info), ==, 5); - g_assert_cmpuint(apicid_core_width(1, 30, 2), ==, 5); - g_assert_cmpuint(apicid_core_width(1, 31, 2), ==, 5); - g_assert_cmpuint(apicid_core_width(1, 32, 2), ==, 5); - g_assert_cmpuint(apicid_core_width(1, 33, 2), ==, 6); + topo_info = (X86CPUTopoInfo) {0, 1, 30, 2}; + g_assert_cmpuint(apicid_core_width(&topo_info), ==, 5); + topo_info = (X86CPUTopoInfo) {0, 1, 31, 2}; + g_assert_cmpuint(apicid_core_width(&topo_info), ==, 5); + topo_info = (X86CPUTopoInfo) {0, 1, 32, 2}; + g_assert_cmpuint(apicid_core_width(&topo_info), ==, 5); + topo_info = (X86CPUTopoInfo) {0, 1, 33, 2}; + g_assert_cmpuint(apicid_core_width(&topo_info), ==, 6); - g_assert_cmpuint(apicid_die_width(1, 30, 2), ==, 0); - g_assert_cmpuint(apicid_die_width(2, 30, 2), ==, 1); - g_assert_cmpuint(apicid_die_width(3, 30, 2), ==, 2); - g_assert_cmpuint(apicid_die_width(4, 30, 2), ==, 2); + topo_info = (X86CPUTopoInfo) {0, 1, 30, 2}; + g_assert_cmpuint(apicid_die_width(&topo_info), ==, 0); + topo_info = (X86CPUTopoInfo) {0, 2, 30, 2}; + g_assert_cmpuint(apicid_die_width(&topo_info), ==, 1); + topo_info = (X86CPUTopoInfo) {0, 3, 30, 2}; + g_assert_cmpuint(apicid_die_width(&topo_info), ==, 2); + topo_info = (X86CPUTopoInfo) {0, 4, 30, 2}; + g_assert_cmpuint(apicid_die_width(&topo_info), ==, 2); /* build a weird topology and see if IDs are calculated correctly */ /* This will use 2 bits for thread ID and 3 bits for core ID */ - g_assert_cmpuint(apicid_smt_width(1, 6, 3), ==, 2); - g_assert_cmpuint(apicid_core_offset(1, 6, 3), ==, 2); - g_assert_cmpuint(apicid_die_offset(1, 6, 3), ==, 5); - g_assert_cmpuint(apicid_pkg_offset(1, 6, 3), ==, 5); + topo_info = (X86CPUTopoInfo) {0, 1, 6, 3}; + g_assert_cmpuint(apicid_smt_width(&topo_info), ==, 2); + g_assert_cmpuint(apicid_core_offset(&topo_info), ==, 2); + g_assert_cmpuint(apicid_die_offset(&topo_info), ==, 5); + g_assert_cmpuint(apicid_pkg_offset(&topo_info), ==, 5); - g_assert_cmpuint(x86_apicid_from_cpu_idx(1, 6, 3, 0), ==, 0); - g_assert_cmpuint(x86_apicid_from_cpu_idx(1, 6, 3, 1), ==, 1); - g_assert_cmpuint(x86_apicid_from_cpu_idx(1, 6, 3, 2), ==, 2); + topo_info = (X86CPUTopoInfo) {0, 1, 6, 3}; + g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 0), ==, 0); + g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 1), ==, 1); + g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 2), ==, 2); - g_assert_cmpuint(x86_apicid_from_cpu_idx(1, 6, 3, 1 * 3 + 0), ==, + topo_info = (X86CPUTopoInfo) {0, 1, 6, 3}; + g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 1 * 3 + 0), ==, (1 << 2) | 0); - g_assert_cmpuint(x86_apicid_from_cpu_idx(1, 6, 3, 1 * 3 + 1), ==, + g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 1 * 3 + 1), ==, (1 << 2) | 1); - g_assert_cmpuint(x86_apicid_from_cpu_idx(1, 6, 3, 1 * 3 + 2), ==, + g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 1 * 3 + 2), ==, (1 << 2) | 2); - g_assert_cmpuint(x86_apicid_from_cpu_idx(1, 6, 3, 2 * 3 + 0), ==, + g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 2 * 3 + 0), ==, (2 << 2) | 0); - g_assert_cmpuint(x86_apicid_from_cpu_idx(1, 6, 3, 2 * 3 + 1), ==, + g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 2 * 3 + 1), ==, (2 << 2) | 1); - g_assert_cmpuint(x86_apicid_from_cpu_idx(1, 6, 3, 2 * 3 + 2), ==, + g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 2 * 3 + 2), ==, (2 << 2) | 2); - g_assert_cmpuint(x86_apicid_from_cpu_idx(1, 6, 3, 5 * 3 + 0), ==, + g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 5 * 3 + 0), ==, (5 << 2) | 0); - g_assert_cmpuint(x86_apicid_from_cpu_idx(1, 6, 3, 5 * 3 + 1), ==, + g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 5 * 3 + 1), ==, (5 << 2) | 1); - g_assert_cmpuint(x86_apicid_from_cpu_idx(1, 6, 3, 5 * 3 + 2), ==, + g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 5 * 3 + 2), ==, (5 << 2) | 2); - g_assert_cmpuint(x86_apicid_from_cpu_idx(1, 6, 3, + g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 1 * 6 * 3 + 0 * 3 + 0), ==, (1 << 5)); - g_assert_cmpuint(x86_apicid_from_cpu_idx(1, 6, 3, + g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 1 * 6 * 3 + 1 * 3 + 1), ==, (1 << 5) | (1 << 2) | 1); - g_assert_cmpuint(x86_apicid_from_cpu_idx(1, 6, 3, + g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 3 * 6 * 3 + 5 * 3 + 2), ==, (3 << 5) | (5 << 2) | 2); } diff --git a/tests/vm/Makefile.include b/tests/vm/Makefile.include index fea348e845..1bf9693d19 100644 --- a/tests/vm/Makefile.include +++ b/tests/vm/Makefile.include @@ -2,7 +2,11 @@ .PHONY: vm-build-all vm-clean-all -IMAGES := ubuntu.i386 freebsd netbsd openbsd centos fedora +IMAGES := freebsd netbsd openbsd centos fedora +ifneq ($(GENISOIMAGE),) +IMAGES += ubuntu.i386 centos +endif + IMAGES_DIR := $(HOME)/.cache/qemu-vm/images IMAGE_FILES := $(patsubst %, $(IMAGES_DIR)/%.img, $(IMAGES)) @@ -12,12 +16,16 @@ IMAGE_FILES := $(patsubst %, $(IMAGES_DIR)/%.img, $(IMAGES)) vm-help vm-test: @echo "vm-help: Test QEMU in preconfigured virtual machines" @echo - @echo " vm-build-ubuntu.i386 - Build QEMU in ubuntu i386 VM" @echo " vm-build-freebsd - Build QEMU in FreeBSD VM" @echo " vm-build-netbsd - Build QEMU in NetBSD VM" @echo " vm-build-openbsd - Build QEMU in OpenBSD VM" - @echo " vm-build-centos - Build QEMU in CentOS VM, with Docker" @echo " vm-build-fedora - Build QEMU in Fedora VM" +ifneq ($(GENISOIMAGE),) + @echo " vm-build-centos - Build QEMU in CentOS VM, with Docker" + @echo " vm-build-ubuntu.i386 - Build QEMU in ubuntu i386 VM" +else + @echo " (install genisoimage to build centos/ubuntu images)" +endif @echo "" @echo " vm-build-all - Build QEMU in all VMs" @echo " vm-clean-all - Clean up VM images" @@ -34,6 +42,7 @@ vm-help vm-test: @echo " DEBUG=1 - Enable verbose output on host and interactive debugging" @echo " V=1 - Enable verbose ouput on host and guest commands" @echo " QEMU=/path/to/qemu - Change path to QEMU binary" + @echo " QEMU_IMG=/path/to/qemu-img - Change path to qemu-img tool" vm-build-all: $(addprefix vm-build-, $(IMAGES)) @@ -79,7 +88,7 @@ vm-boot-serial-%: $(IMAGES_DIR)/%.img vm-boot-ssh-%: $(IMAGES_DIR)/%.img $(call quiet-command, \ - $(SRC_PATH)/tests/vm/$* \ + $(PYTHON) $(SRC_PATH)/tests/vm/$* \ $(if $(J),--jobs $(J)) \ --image "$<" \ --interactive \ diff --git a/tests/vm/basevm.py b/tests/vm/basevm.py old mode 100755 new mode 100644 index 0b8c1b2657..756ccf7aca --- 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 @@ -21,7 +19,7 @@ import logging import time import datetime sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python')) -from qemu import kvm_available +from qemu.accel import kvm_available from qemu.machine import QEMUMachine import subprocess import hashlib @@ -59,6 +57,10 @@ class BaseVM(object): poweroff = "poweroff" # enable IPv6 networking ipv6 = True + # Scale up some timeouts under TCG. + # 4 is arbitrary, but greater than 2, + # since we found we need to wait more than twice as long. + tcg_ssh_timeout_multiplier = 4 def __init__(self, debug=False, vcpus=None): self._guest = None self._tmpdir = os.path.realpath(tempfile.mkdtemp(prefix="vm-test-", @@ -122,11 +124,16 @@ class BaseVM(object): return fname def _ssh_do(self, user, cmd, check): - ssh_cmd = ["ssh", "-q", "-t", + ssh_cmd = ["ssh", + "-t", "-o", "StrictHostKeyChecking=no", "-o", "UserKnownHostsFile=" + os.devnull, "-o", "ConnectTimeout=1", "-p", self.ssh_port, "-i", self._ssh_key_file] + # If not in debug mode, set ssh to quiet mode to + # avoid printing the results of commands. + if not self.debug: + ssh_cmd.append("-q") for var in self.envvars: ssh_cmd += ['-o', "SendEnv=%s" % var ] assert not isinstance(cmd, str) @@ -152,6 +159,11 @@ class BaseVM(object): def build_image(self, img): raise NotImplementedError + def exec_qemu_img(self, *args): + cmd = [os.environ.get("QEMU_IMG", "qemu-img")] + cmd.extend(list(args)) + subprocess.check_call(cmd) + def add_source_dir(self, src_dir): name = "data-" + hashlib.sha1(src_dir.encode("utf-8")).hexdigest()[:5] tarfile = os.path.join(self._tmpdir, name + ".tar") @@ -167,7 +179,6 @@ class BaseVM(object): def boot(self, img, extra_args=[]): args = self._args + [ - "-device", "VGA", "-drive", "file=%s,if=none,id=drive0,cache=writeback" % img, "-device", "virtio-blk,drive=drive0,bootindex=0"] args += self._data_args + extra_args @@ -201,6 +212,9 @@ class BaseVM(object): def console_init(self, timeout = 120): vm = self._guest vm.console_socket.settimeout(timeout) + self.console_raw_path = os.path.join(vm._temp_dir, + vm._name + "-console.raw") + self.console_raw_file = open(self.console_raw_path, 'wb') def console_log(self, text): for line in re.split("[\r\n]", text): @@ -222,6 +236,9 @@ class BaseVM(object): while True: try: chars = vm.console_socket.recv(1) + if self.console_raw_file: + self.console_raw_file.write(chars) + self.console_raw_file.flush() except socket.timeout: sys.stderr.write("console: *** read timeout ***\n") sys.stderr.write("console: waiting for: '%s'\n" % expect) @@ -300,12 +317,18 @@ class BaseVM(object): def print_step(self, text): sys.stderr.write("### %s ...\n" % text) - def wait_ssh(self, seconds=300): + def wait_ssh(self, wait_root=False, seconds=300): + # Allow more time for VM to boot under TCG. + if not kvm_available(self.arch): + seconds *= self.tcg_ssh_timeout_multiplier starttime = datetime.datetime.now() endtime = starttime + datetime.timedelta(seconds=seconds) guest_up = False while datetime.datetime.now() < endtime: - if self.ssh("exit 0") == 0: + if wait_root and self.ssh_root("exit 0") == 0: + guest_up = True + break + elif self.ssh("exit 0") == 0: guest_up = True break seconds = (endtime - datetime.datetime.now()).total_seconds() @@ -327,6 +350,46 @@ class BaseVM(object): def qmp(self, *args, **kwargs): return self._guest.qmp(*args, **kwargs) + def gen_cloud_init_iso(self): + cidir = self._tmpdir + mdata = open(os.path.join(cidir, "meta-data"), "w") + name = self.name.replace(".","-") + mdata.writelines(["instance-id: {}-vm-0\n".format(name), + "local-hostname: {}-guest\n".format(name)]) + mdata.close() + udata = open(os.path.join(cidir, "user-data"), "w") + print("guest user:pw {}:{}".format(self.GUEST_USER, + self.GUEST_PASS)) + udata.writelines(["#cloud-config\n", + "chpasswd:\n", + " list: |\n", + " root:%s\n" % self.ROOT_PASS, + " %s:%s\n" % (self.GUEST_USER, + self.GUEST_PASS), + " expire: False\n", + "users:\n", + " - name: %s\n" % self.GUEST_USER, + " sudo: ALL=(ALL) NOPASSWD:ALL\n", + " ssh-authorized-keys:\n", + " - %s\n" % SSH_PUB_KEY, + " - name: root\n", + " ssh-authorized-keys:\n", + " - %s\n" % SSH_PUB_KEY, + "locale: en_US.UTF-8\n"]) + proxy = os.environ.get("http_proxy") + if not proxy is None: + udata.writelines(["apt:\n", + " proxy: %s" % proxy]) + udata.close() + subprocess.check_call(["genisoimage", "-output", "cloud-init.iso", + "-volid", "cidata", "-joliet", "-rock", + "user-data", "meta-data"], + cwd=cidir, + stdin=self._devnull, stdout=self._stdout, + stderr=self._stdout) + + return os.path.join(cidir, "cloud-init.iso") + def parse_args(vmcls): def get_default_jobs(): diff --git a/tests/vm/centos b/tests/vm/centos index b9e851f2d3..0ad4ecf419 100755 --- a/tests/vm/centos +++ b/tests/vm/centos @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # CentOS image # @@ -31,45 +31,14 @@ class CentosVM(basevm.BaseVM): make docker-test-mingw@fedora {verbose} J={jobs} NETWORK=1; """ - def _gen_cloud_init_iso(self): - cidir = self._tmpdir - mdata = open(os.path.join(cidir, "meta-data"), "w") - mdata.writelines(["instance-id: centos-vm-0\n", - "local-hostname: centos-guest\n"]) - mdata.close() - udata = open(os.path.join(cidir, "user-data"), "w") - udata.writelines(["#cloud-config\n", - "chpasswd:\n", - " list: |\n", - " root:%s\n" % self.ROOT_PASS, - " %s:%s\n" % (self.GUEST_USER, self.GUEST_PASS), - " expire: False\n", - "users:\n", - " - name: %s\n" % self.GUEST_USER, - " sudo: ALL=(ALL) NOPASSWD:ALL\n", - " ssh-authorized-keys:\n", - " - %s\n" % basevm.SSH_PUB_KEY, - " - name: root\n", - " ssh-authorized-keys:\n", - " - %s\n" % basevm.SSH_PUB_KEY, - "locale: en_US.UTF-8\n"]) - udata.close() - subprocess.check_call(["genisoimage", "-output", "cloud-init.iso", - "-volid", "cidata", "-joliet", "-rock", - "user-data", "meta-data"], - cwd=cidir, - stdin=self._devnull, stdout=self._stdout, - stderr=self._stdout) - return os.path.join(cidir, "cloud-init.iso") - def build_image(self, img): cimg = self._download_with_cache("https://cloud.centos.org/centos/7/images/CentOS-7-x86_64-GenericCloud-1802.qcow2.xz") img_tmp = img + ".tmp" sys.stderr.write("Extracting the image...\n") subprocess.check_call(["ln", "-f", cimg, img_tmp + ".xz"]) subprocess.check_call(["xz", "--keep", "-dvf", img_tmp + ".xz"]) - subprocess.check_call(["qemu-img", "resize", img_tmp, "50G"]) - self.boot(img_tmp, extra_args = ["-cdrom", self._gen_cloud_init_iso()]) + self.exec_qemu_img("resize", img_tmp, "50G") + self.boot(img_tmp, extra_args = ["-cdrom", self.gen_cloud_init_iso()]) self.wait_ssh() self.ssh_root_check("touch /etc/cloud/cloud-init.disabled") self.ssh_root_check("yum update -y") diff --git a/tests/vm/fedora b/tests/vm/fedora index 7fec1479fb..bd9c6cf295 100755 --- a/tests/vm/fedora +++ b/tests/vm/fedora @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Fedora VM image # @@ -53,7 +53,10 @@ class FedoraVM(basevm.BaseVM): # libs: audio '"pkgconfig(libpulse)"', '"pkgconfig(alsa)"', - ] + + # libs: migration + '"pkgconfig(libzstd)"', +] BUILD_SCRIPT = """ set -e; @@ -74,13 +77,12 @@ class FedoraVM(basevm.BaseVM): self.print_step("Preparing iso and disk image") subprocess.check_call(["cp", "-f", cimg, iso]) - subprocess.check_call(["qemu-img", "create", "-f", "qcow2", - img_tmp, self.size]) - + self.exec_qemu_img("create", "-f", "qcow2", img_tmp, self.size) self.print_step("Booting installer") self.boot(img_tmp, extra_args = [ "-bios", "pc-bios/bios-256k.bin", "-machine", "graphics=off", + "-device", "VGA", "-cdrom", iso ]) self.console_init(300) diff --git a/tests/vm/freebsd b/tests/vm/freebsd index 2a19461a90..298967fe9c 100755 --- a/tests/vm/freebsd +++ b/tests/vm/freebsd @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # FreeBSD VM image # @@ -24,14 +24,15 @@ class FreeBSDVM(basevm.BaseVM): name = "freebsd" arch = "x86_64" - link = "https://download.freebsd.org/ftp/releases/ISO-IMAGES/12.0/FreeBSD-12.0-RELEASE-amd64-disc1.iso.xz" - csum = "1d40015bea89d05b8bd13e2ed80c40b522a9ec1abd8e7c8b80954fb485fb99db" + link = "https://download.freebsd.org/ftp/releases/ISO-IMAGES/12.1/FreeBSD-12.1-RELEASE-amd64-disc1.iso.xz" + csum = "7394c3f60a1e236e7bd3a05809cf43ae39a3b8e5d42d782004cf2f26b1cfcd88" size = "20G" pkgs = [ # build tools "git", "pkgconf", "bzip2", + "python37", # gnu tools "bash", @@ -54,6 +55,9 @@ class FreeBSDVM(basevm.BaseVM): # libs: opengl "libepoxy", "mesa-libs", + + # libs: migration + "zstd", ] BUILD_SCRIPT = """ @@ -63,7 +67,7 @@ class FreeBSDVM(basevm.BaseVM): mkdir src build; cd src; tar -xf /dev/vtbd1; cd ../build - ../src/configure --python=python3.6 {configure_opts}; + ../src/configure --python=python3.7 {configure_opts}; gmake --output-sync -j{jobs} {target} {verbose}; """ @@ -82,13 +86,13 @@ class FreeBSDVM(basevm.BaseVM): self.print_step("Preparing iso and disk image") subprocess.check_call(["cp", "-f", cimg, iso_xz]) subprocess.check_call(["xz", "-dvf", iso_xz]) - subprocess.check_call(["qemu-img", "create", "-f", "qcow2", - img_tmp, self.size]) + self.exec_qemu_img("create", "-f", "qcow2", img_tmp, self.size) self.print_step("Booting installer") self.boot(img_tmp, extra_args = [ "-bios", "pc-bios/bios-256k.bin", "-machine", "graphics=off", + "-device", "VGA", "-cdrom", iso ]) self.console_init() diff --git a/tests/vm/netbsd b/tests/vm/netbsd index 611e6cc5b5..b10c9d429d 100755 --- a/tests/vm/netbsd +++ b/tests/vm/netbsd @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # NetBSD VM image # @@ -22,8 +22,8 @@ class NetBSDVM(basevm.BaseVM): name = "netbsd" arch = "x86_64" - link = "https://cdn.netbsd.org/pub/NetBSD/NetBSD-8.1/images/NetBSD-8.1-amd64.iso" - csum = "718f275b7e0879599bdac95630c5e3f2184700032fdb6cdebf3bdd63687898c48ff3f08f57b89f4437a86cdd8ea07c01a39d432dbb37e1e4b008f4985f98da3f" + link = "https://cdn.netbsd.org/pub/NetBSD/NetBSD-9.0/images/NetBSD-9.0-amd64.iso" + csum = "34da4882ee61bdbf69f241195a8933dc800949d30b43fc6988da853d57fc2b8cac50cf97a0d2adaf93250b4e329d189c1a8b83c33bd515226f37745d50c33369" size = "20G" pkgs = [ # tools @@ -49,6 +49,9 @@ class NetBSDVM(basevm.BaseVM): "SDL2", "gtk3+", "libxkbcommon", + + # libs: migration + "zstd", ] BUILD_SCRIPT = """ @@ -77,8 +80,7 @@ class NetBSDVM(basevm.BaseVM): self.print_step("Preparing iso and disk image") subprocess.check_call(["ln", "-f", cimg, iso]) - subprocess.check_call(["qemu-img", "create", "-f", "qcow2", - img_tmp, self.size]) + self.exec_qemu_img("create", "-f", "qcow2", img_tmp, self.size) self.print_step("Booting installer") self.boot(img_tmp, extra_args = [ @@ -87,38 +89,28 @@ class NetBSDVM(basevm.BaseVM): "-cdrom", iso ]) self.console_init() - self.console_wait("Primary Bootstrap") - - # serial console boot menu output doesn't work for some - # reason, so we have to fly blind ... - for char in list("5consdev com0\n"): - time.sleep(0.2) - self.console_send(char) - self.console_consume() + self.console_wait_send("3. Drop to boot prompt", "3") + self.console_wait_send("> ", "consdev com0\n") self.console_wait_send("> ", "boot\n") self.console_wait_send("Terminal type", "xterm\n") self.console_wait_send("a: Installation messages", "a\n") - self.console_wait_send("b: US-English", "b\n") self.console_wait_send("a: Install NetBSD", "a\n") self.console_wait("Shall we continue?") self.console_wait_send("b: Yes", "b\n") self.console_wait_send("a: ld0", "a\n") + self.console_wait_send("a: Guid Partition Table", "a\n") self.console_wait_send("a: This is the correct", "a\n") - self.console_wait_send("b: Use the entire disk", "b\n") - self.console_wait("NetBSD bootcode") - self.console_wait_send("a: Yes", "a\n") - self.console_wait_send("b: Use existing part", "b\n") + self.console_wait_send("b: Use default part", "b\n") self.console_wait_send("x: Partition sizes ok", "x\n") - self.console_wait_send("for your NetBSD disk", "\n") self.console_wait("Shall we continue?") self.console_wait_send("b: Yes", "b\n") self.console_wait_send("b: Use serial port com0", "b\n") self.console_wait_send("f: Set serial baud rate", "f\n") self.console_wait_send("a: 9600", "a\n") - self.console_wait_send("x: Exit", "x\n") + self.console_wait_send("x: Continue", "x\n") self.console_wait_send("a: Full installation", "a\n") self.console_wait_send("a: CD-ROM", "a\n") diff --git a/tests/vm/openbsd b/tests/vm/openbsd index b92c39f89a..0b705f4945 100755 --- a/tests/vm/openbsd +++ b/tests/vm/openbsd @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # OpenBSD VM image # @@ -22,8 +22,8 @@ class OpenBSDVM(basevm.BaseVM): name = "openbsd" arch = "x86_64" - link = "https://cdn.openbsd.org/pub/OpenBSD/6.5/amd64/install65.iso" - csum = "38d1f8cadd502f1c27bf05c5abde6cc505dd28f3f34f8a941048ff9a54f9f608" + link = "https://cdn.openbsd.org/pub/OpenBSD/6.6/amd64/install66.iso" + csum = "b22e63df56e6266de6bbeed8e9be0fbe9ee2291551c5bc03f3cc2e4ab9436ee3" size = "20G" pkgs = [ # tools @@ -51,6 +51,9 @@ class OpenBSDVM(basevm.BaseVM): "sdl2", "gtk+3", "libxkbcommon", + + # libs: migration + "zstd", ] BUILD_SCRIPT = """ @@ -73,13 +76,13 @@ class OpenBSDVM(basevm.BaseVM): self.print_step("Preparing iso and disk image") subprocess.check_call(["cp", "-f", cimg, iso]) - subprocess.check_call(["qemu-img", "create", "-f", "qcow2", - img_tmp, self.size]) + self.exec_qemu_img("create", "-f", "qcow2", img_tmp, self.size) self.print_step("Booting installer") self.boot(img_tmp, extra_args = [ "-bios", "pc-bios/bios-256k.bin", "-machine", "graphics=off", + "-device", "VGA", "-cdrom", iso ]) self.console_init() diff --git a/tests/vm/ubuntu.i386 b/tests/vm/ubuntu.i386 index f611bebdc9..1570775335 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 # @@ -29,49 +29,17 @@ class UbuntuX86VM(basevm.BaseVM): make --output-sync {target} -j{jobs} {verbose}; """ - def _gen_cloud_init_iso(self): - cidir = self._tmpdir - mdata = open(os.path.join(cidir, "meta-data"), "w") - mdata.writelines(["instance-id: ubuntu-vm-0\n", - "local-hostname: ubuntu-guest\n"]) - mdata.close() - udata = open(os.path.join(cidir, "user-data"), "w") - udata.writelines(["#cloud-config\n", - "chpasswd:\n", - " list: |\n", - " root:%s\n" % self.ROOT_PASS, - " %s:%s\n" % (self.GUEST_USER, self.GUEST_PASS), - " expire: False\n", - "users:\n", - " - name: %s\n" % self.GUEST_USER, - " sudo: ALL=(ALL) NOPASSWD:ALL\n", - " ssh-authorized-keys:\n", - " - %s\n" % basevm.SSH_PUB_KEY, - " - name: root\n", - " ssh-authorized-keys:\n", - " - %s\n" % basevm.SSH_PUB_KEY, - "locale: en_US.UTF-8\n"]) - proxy = os.environ.get("http_proxy") - if not proxy is None: - udata.writelines(["apt:\n", - " proxy: %s" % proxy]) - udata.close() - subprocess.check_call(["genisoimage", "-output", "cloud-init.iso", - "-volid", "cidata", "-joliet", "-rock", - "user-data", "meta-data"], - cwd=cidir, - stdin=self._devnull, stdout=self._stdout, - stderr=self._stdout) - return os.path.join(cidir, "cloud-init.iso") - def build_image(self, img): cimg = self._download_with_cache( "https://cloud-images.ubuntu.com/releases/bionic/release-20191114/ubuntu-18.04-server-cloudimg-i386.img", sha256sum="28969840626d1ea80bb249c08eef1a4533e8904aa51a327b40f37ac4b4ff04ef") img_tmp = img + ".tmp" subprocess.check_call(["cp", "-f", cimg, img_tmp]) - subprocess.check_call(["qemu-img", "resize", img_tmp, "50G"]) - self.boot(img_tmp, extra_args = ["-cdrom", self._gen_cloud_init_iso()]) + self.exec_qemu_img("resize", img_tmp, "50G") + self.boot(img_tmp, extra_args = [ + "-device", "VGA", + "-cdrom", self.gen_cloud_init_iso() + ]) self.wait_ssh() self.ssh_root_check("touch /etc/cloud/cloud-init.disabled") self.ssh_root_check("apt-get update") diff --git a/tools/virtiofsd/50-qemu-virtiofsd.json.in b/tools/virtiofsd/50-qemu-virtiofsd.json.in new file mode 100644 index 0000000000..9bcd86f8dc --- /dev/null +++ b/tools/virtiofsd/50-qemu-virtiofsd.json.in @@ -0,0 +1,5 @@ +{ + "description": "QEMU virtiofsd vhost-user-fs", + "type": "fs", + "binary": "@libexecdir@/virtiofsd" +} diff --git a/tools/virtiofsd/Makefile.objs b/tools/virtiofsd/Makefile.objs new file mode 100644 index 0000000000..076f667e46 --- /dev/null +++ b/tools/virtiofsd/Makefile.objs @@ -0,0 +1,12 @@ +virtiofsd-obj-y = buffer.o \ + fuse_opt.o \ + fuse_log.o \ + fuse_lowlevel.o \ + fuse_signals.o \ + fuse_virtio.o \ + helper.o \ + passthrough_ll.o \ + seccomp.o + +seccomp.o-cflags := $(SECCOMP_CFLAGS) +seccomp.o-libs := $(SECCOMP_LIBS) diff --git a/tools/virtiofsd/buffer.c b/tools/virtiofsd/buffer.c new file mode 100644 index 0000000000..27c1377f22 --- /dev/null +++ b/tools/virtiofsd/buffer.c @@ -0,0 +1,351 @@ +/* + * FUSE: Filesystem in Userspace + * Copyright (C) 2010 Miklos Szeredi + * + * Functions for dealing with `struct fuse_buf` and `struct + * fuse_bufvec`. + * + * This program can be distributed under the terms of the GNU LGPLv2. + * See the file COPYING.LIB + */ + +#include "qemu/osdep.h" +#include "fuse_i.h" +#include "fuse_lowlevel.h" +#include +#include +#include +#include +#include + +size_t fuse_buf_size(const struct fuse_bufvec *bufv) +{ + size_t i; + size_t size = 0; + + for (i = 0; i < bufv->count; i++) { + if (bufv->buf[i].size == SIZE_MAX) { + size = SIZE_MAX; + } else { + size += bufv->buf[i].size; + } + } + + return size; +} + +static ssize_t fuse_buf_writev(struct fuse_buf *out_buf, + struct fuse_bufvec *in_buf) +{ + ssize_t res, i, j; + size_t iovcnt = in_buf->count; + struct iovec *iov; + int fd = out_buf->fd; + + iov = calloc(iovcnt, sizeof(struct iovec)); + if (!iov) { + return -ENOMEM; + } + + for (i = 0, j = 0; i < iovcnt; i++) { + /* Skip the buf with 0 size */ + if (in_buf->buf[i].size) { + iov[j].iov_base = in_buf->buf[i].mem; + iov[j].iov_len = in_buf->buf[i].size; + j++; + } + } + + if (out_buf->flags & FUSE_BUF_FD_SEEK) { + res = pwritev(fd, iov, iovcnt, out_buf->pos); + } else { + res = writev(fd, iov, iovcnt); + } + + if (res == -1) { + res = -errno; + } + + free(iov); + return res; +} + +static size_t min_size(size_t s1, size_t s2) +{ + return s1 < s2 ? s1 : s2; +} + +static ssize_t fuse_buf_write(const struct fuse_buf *dst, size_t dst_off, + const struct fuse_buf *src, size_t src_off, + size_t len) +{ + ssize_t res = 0; + size_t copied = 0; + + while (len) { + if (dst->flags & FUSE_BUF_FD_SEEK) { + res = pwrite(dst->fd, (char *)src->mem + src_off, len, + dst->pos + dst_off); + } else { + res = write(dst->fd, (char *)src->mem + src_off, len); + } + if (res == -1) { + if (!copied) { + return -errno; + } + break; + } + if (res == 0) { + break; + } + + copied += res; + if (!(dst->flags & FUSE_BUF_FD_RETRY)) { + break; + } + + src_off += res; + dst_off += res; + len -= res; + } + + return copied; +} + +static ssize_t fuse_buf_read(const struct fuse_buf *dst, size_t dst_off, + const struct fuse_buf *src, size_t src_off, + size_t len) +{ + ssize_t res = 0; + size_t copied = 0; + + while (len) { + if (src->flags & FUSE_BUF_FD_SEEK) { + res = pread(src->fd, (char *)dst->mem + dst_off, len, + src->pos + src_off); + } else { + res = read(src->fd, (char *)dst->mem + dst_off, len); + } + if (res == -1) { + if (!copied) { + return -errno; + } + break; + } + if (res == 0) { + break; + } + + copied += res; + if (!(src->flags & FUSE_BUF_FD_RETRY)) { + break; + } + + dst_off += res; + src_off += res; + len -= res; + } + + return copied; +} + +static ssize_t fuse_buf_fd_to_fd(const struct fuse_buf *dst, size_t dst_off, + const struct fuse_buf *src, size_t src_off, + size_t len) +{ + char buf[4096]; + struct fuse_buf tmp = { + .size = sizeof(buf), + .flags = 0, + }; + ssize_t res; + size_t copied = 0; + + tmp.mem = buf; + + while (len) { + size_t this_len = min_size(tmp.size, len); + size_t read_len; + + res = fuse_buf_read(&tmp, 0, src, src_off, this_len); + if (res < 0) { + if (!copied) { + return res; + } + break; + } + if (res == 0) { + break; + } + + read_len = res; + res = fuse_buf_write(dst, dst_off, &tmp, 0, read_len); + if (res < 0) { + if (!copied) { + return res; + } + break; + } + if (res == 0) { + break; + } + + copied += res; + + if (res < this_len) { + break; + } + + dst_off += res; + src_off += res; + len -= res; + } + + return copied; +} + +static ssize_t fuse_buf_copy_one(const struct fuse_buf *dst, size_t dst_off, + const struct fuse_buf *src, size_t src_off, + size_t len) +{ + int src_is_fd = src->flags & FUSE_BUF_IS_FD; + int dst_is_fd = dst->flags & FUSE_BUF_IS_FD; + + if (!src_is_fd && !dst_is_fd) { + char *dstmem = (char *)dst->mem + dst_off; + char *srcmem = (char *)src->mem + src_off; + + if (dstmem != srcmem) { + if (dstmem + len <= srcmem || srcmem + len <= dstmem) { + memcpy(dstmem, srcmem, len); + } else { + memmove(dstmem, srcmem, len); + } + } + + return len; + } else if (!src_is_fd) { + return fuse_buf_write(dst, dst_off, src, src_off, len); + } else if (!dst_is_fd) { + return fuse_buf_read(dst, dst_off, src, src_off, len); + } else { + return fuse_buf_fd_to_fd(dst, dst_off, src, src_off, len); + } +} + +static const struct fuse_buf *fuse_bufvec_current(struct fuse_bufvec *bufv) +{ + if (bufv->idx < bufv->count) { + return &bufv->buf[bufv->idx]; + } else { + return NULL; + } +} + +static int fuse_bufvec_advance(struct fuse_bufvec *bufv, size_t len) +{ + const struct fuse_buf *buf = fuse_bufvec_current(bufv); + + bufv->off += len; + assert(bufv->off <= buf->size); + if (bufv->off == buf->size) { + assert(bufv->idx < bufv->count); + bufv->idx++; + if (bufv->idx == bufv->count) { + return 0; + } + bufv->off = 0; + } + return 1; +} + +ssize_t fuse_buf_copy(struct fuse_bufvec *dstv, struct fuse_bufvec *srcv) +{ + size_t copied = 0, i; + + if (dstv == srcv) { + return fuse_buf_size(dstv); + } + + /* + * use writev to improve bandwidth when all the + * src buffers already mapped by the daemon + * process + */ + for (i = 0; i < srcv->count; i++) { + if (srcv->buf[i].flags & FUSE_BUF_IS_FD) { + break; + } + } + if ((i == srcv->count) && (dstv->count == 1) && + (dstv->idx == 0) && + (dstv->buf[0].flags & FUSE_BUF_IS_FD)) { + dstv->buf[0].pos += dstv->off; + return fuse_buf_writev(&dstv->buf[0], srcv); + } + + for (;;) { + const struct fuse_buf *src = fuse_bufvec_current(srcv); + const struct fuse_buf *dst = fuse_bufvec_current(dstv); + size_t src_len; + size_t dst_len; + size_t len; + ssize_t res; + + if (src == NULL || dst == NULL) { + break; + } + + src_len = src->size - srcv->off; + dst_len = dst->size - dstv->off; + len = min_size(src_len, dst_len); + + res = fuse_buf_copy_one(dst, dstv->off, src, srcv->off, len); + if (res < 0) { + if (!copied) { + return res; + } + break; + } + copied += res; + + if (!fuse_bufvec_advance(srcv, res) || + !fuse_bufvec_advance(dstv, res)) { + break; + } + + if (res < len) { + break; + } + } + + return copied; +} + +void *fuse_mbuf_iter_advance(struct fuse_mbuf_iter *iter, size_t len) +{ + void *ptr; + + if (len > iter->size - iter->pos) { + return NULL; + } + + ptr = iter->mem + iter->pos; + iter->pos += len; + return ptr; +} + +const char *fuse_mbuf_iter_advance_str(struct fuse_mbuf_iter *iter) +{ + const char *str = iter->mem + iter->pos; + size_t remaining = iter->size - iter->pos; + size_t i; + + for (i = 0; i < remaining; i++) { + if (str[i] == '\0') { + iter->pos += i + 1; + return str; + } + } + return NULL; +} diff --git a/tools/virtiofsd/fuse_common.h b/tools/virtiofsd/fuse_common.h new file mode 100644 index 0000000000..686c42c0a5 --- /dev/null +++ b/tools/virtiofsd/fuse_common.h @@ -0,0 +1,816 @@ +/* + * FUSE: Filesystem in Userspace + * Copyright (C) 2001-2007 Miklos Szeredi + * + * This program can be distributed under the terms of the GNU LGPLv2. + * See the file COPYING.LIB. + */ + +/** @file */ + +#if !defined(FUSE_H_) && !defined(FUSE_LOWLEVEL_H_) +#error \ + "Never include directly; use or instead." +#endif + +#ifndef FUSE_COMMON_H_ +#define FUSE_COMMON_H_ + +#include "fuse_log.h" +#include "fuse_opt.h" +#include +#include + +/** Major version of FUSE library interface */ +#define FUSE_MAJOR_VERSION 3 + +/** Minor version of FUSE library interface */ +#define FUSE_MINOR_VERSION 2 + +#define FUSE_MAKE_VERSION(maj, min) ((maj) * 10 + (min)) +#define FUSE_VERSION FUSE_MAKE_VERSION(FUSE_MAJOR_VERSION, FUSE_MINOR_VERSION) + +/** + * Information about an open file. + * + * File Handles are created by the open, opendir, and create methods and closed + * by the release and releasedir methods. Multiple file handles may be + * concurrently open for the same file. Generally, a client will create one + * file handle per file descriptor, though in some cases multiple file + * descriptors can share a single file handle. + */ +struct fuse_file_info { + /** Open flags. Available in open() and release() */ + int flags; + + /* + * In case of a write operation indicates if this was caused + * by a delayed write from the page cache. If so, then the + * context's pid, uid, and gid fields will not be valid, and + * the *fh* value may not match the *fh* value that would + * have been sent with the corresponding individual write + * requests if write caching had been disabled. + */ + unsigned int writepage:1; + + /** Can be filled in by open, to use direct I/O on this file. */ + unsigned int direct_io:1; + + /* + * Can be filled in by open. It signals the kernel that any + * currently cached file data (ie., data that the filesystem + * provided the last time the file was open) need not be + * invalidated. Has no effect when set in other contexts (in + * particular it does nothing when set by opendir()). + */ + unsigned int keep_cache:1; + + /* + * Indicates a flush operation. Set in flush operation, also + * maybe set in highlevel lock operation and lowlevel release + * operation. + */ + unsigned int flush:1; + + /* + * Can be filled in by open, to indicate that the file is not + * seekable. + */ + unsigned int nonseekable:1; + + /* + * Indicates that flock locks for this file should be + * released. If set, lock_owner shall contain a valid value. + * May only be set in ->release(). + */ + unsigned int flock_release:1; + + /* + * Can be filled in by opendir. It signals the kernel to + * enable caching of entries returned by readdir(). Has no + * effect when set in other contexts (in particular it does + * nothing when set by open()). + */ + unsigned int cache_readdir:1; + + /* Indicates that suid/sgid bits should be removed upon write */ + unsigned int kill_priv:1; + + + /** Padding. Reserved for future use*/ + unsigned int padding:24; + unsigned int padding2:32; + + /* + * File handle id. May be filled in by filesystem in create, + * open, and opendir(). Available in most other file operations on the + * same file handle. + */ + uint64_t fh; + + /** Lock owner id. Available in locking operations and flush */ + uint64_t lock_owner; + + /* + * Requested poll events. Available in ->poll. Only set on kernels + * which support it. If unsupported, this field is set to zero. + */ + uint32_t poll_events; +}; + +/* + * Capability bits for 'fuse_conn_info.capable' and 'fuse_conn_info.want' + */ + +/** + * Indicates that the filesystem supports asynchronous read requests. + * + * If this capability is not requested/available, the kernel will + * ensure that there is at most one pending read request per + * file-handle at any time, and will attempt to order read requests by + * increasing offset. + * + * This feature is enabled by default when supported by the kernel. + */ +#define FUSE_CAP_ASYNC_READ (1 << 0) + +/** + * Indicates that the filesystem supports "remote" locking. + * + * This feature is enabled by default when supported by the kernel, + * and if getlk() and setlk() handlers are implemented. + */ +#define FUSE_CAP_POSIX_LOCKS (1 << 1) + +/** + * Indicates that the filesystem supports the O_TRUNC open flag. If + * disabled, and an application specifies O_TRUNC, fuse first calls + * truncate() and then open() with O_TRUNC filtered out. + * + * This feature is enabled by default when supported by the kernel. + */ +#define FUSE_CAP_ATOMIC_O_TRUNC (1 << 3) + +/** + * Indicates that the filesystem supports lookups of "." and "..". + * + * This feature is disabled by default. + */ +#define FUSE_CAP_EXPORT_SUPPORT (1 << 4) + +/** + * Indicates that the kernel should not apply the umask to the + * file mode on create operations. + * + * This feature is disabled by default. + */ +#define FUSE_CAP_DONT_MASK (1 << 6) + +/** + * Indicates that libfuse should try to use splice() when writing to + * the fuse device. This may improve performance. + * + * This feature is disabled by default. + */ +#define FUSE_CAP_SPLICE_WRITE (1 << 7) + +/** + * Indicates that libfuse should try to move pages instead of copying when + * writing to / reading from the fuse device. This may improve performance. + * + * This feature is disabled by default. + */ +#define FUSE_CAP_SPLICE_MOVE (1 << 8) + +/** + * Indicates that libfuse should try to use splice() when reading from + * the fuse device. This may improve performance. + * + * This feature is enabled by default when supported by the kernel and + * if the filesystem implements a write_buf() handler. + */ +#define FUSE_CAP_SPLICE_READ (1 << 9) + +/** + * If set, the calls to flock(2) will be emulated using POSIX locks and must + * then be handled by the filesystem's setlock() handler. + * + * If not set, flock(2) calls will be handled by the FUSE kernel module + * internally (so any access that does not go through the kernel cannot be taken + * into account). + * + * This feature is enabled by default when supported by the kernel and + * if the filesystem implements a flock() handler. + */ +#define FUSE_CAP_FLOCK_LOCKS (1 << 10) + +/** + * Indicates that the filesystem supports ioctl's on directories. + * + * This feature is enabled by default when supported by the kernel. + */ +#define FUSE_CAP_IOCTL_DIR (1 << 11) + +/** + * Traditionally, while a file is open the FUSE kernel module only + * asks the filesystem for an update of the file's attributes when a + * client attempts to read beyond EOF. This is unsuitable for + * e.g. network filesystems, where the file contents may change + * without the kernel knowing about it. + * + * If this flag is set, FUSE will check the validity of the attributes + * on every read. If the attributes are no longer valid (i.e., if the + * *attr_timeout* passed to fuse_reply_attr() or set in `struct + * fuse_entry_param` has passed), it will first issue a `getattr` + * request. If the new mtime differs from the previous value, any + * cached file *contents* will be invalidated as well. + * + * This flag should always be set when available. If all file changes + * go through the kernel, *attr_timeout* should be set to a very large + * number to avoid unnecessary getattr() calls. + * + * This feature is enabled by default when supported by the kernel. + */ +#define FUSE_CAP_AUTO_INVAL_DATA (1 << 12) + +/** + * Indicates that the filesystem supports readdirplus. + * + * This feature is enabled by default when supported by the kernel and if the + * filesystem implements a readdirplus() handler. + */ +#define FUSE_CAP_READDIRPLUS (1 << 13) + +/** + * Indicates that the filesystem supports adaptive readdirplus. + * + * If FUSE_CAP_READDIRPLUS is not set, this flag has no effect. + * + * If FUSE_CAP_READDIRPLUS is set and this flag is not set, the kernel + * will always issue readdirplus() requests to retrieve directory + * contents. + * + * If FUSE_CAP_READDIRPLUS is set and this flag is set, the kernel + * will issue both readdir() and readdirplus() requests, depending on + * how much information is expected to be required. + * + * As of Linux 4.20, the algorithm is as follows: when userspace + * starts to read directory entries, issue a READDIRPLUS request to + * the filesystem. If any entry attributes have been looked up by the + * time userspace requests the next batch of entries continue with + * READDIRPLUS, otherwise switch to plain READDIR. This will reasult + * in eg plain "ls" triggering READDIRPLUS first then READDIR after + * that because it doesn't do lookups. "ls -l" should result in all + * READDIRPLUS, except if dentries are already cached. + * + * This feature is enabled by default when supported by the kernel and + * if the filesystem implements both a readdirplus() and a readdir() + * handler. + */ +#define FUSE_CAP_READDIRPLUS_AUTO (1 << 14) + +/** + * Indicates that the filesystem supports asynchronous direct I/O submission. + * + * If this capability is not requested/available, the kernel will ensure that + * there is at most one pending read and one pending write request per direct + * I/O file-handle at any time. + * + * This feature is enabled by default when supported by the kernel. + */ +#define FUSE_CAP_ASYNC_DIO (1 << 15) + +/** + * Indicates that writeback caching should be enabled. This means that + * individual write request may be buffered and merged in the kernel + * before they are send to the filesystem. + * + * This feature is disabled by default. + */ +#define FUSE_CAP_WRITEBACK_CACHE (1 << 16) + +/** + * Indicates support for zero-message opens. If this flag is set in + * the `capable` field of the `fuse_conn_info` structure, then the + * filesystem may return `ENOSYS` from the open() handler to indicate + * success. Further attempts to open files will be handled in the + * kernel. (If this flag is not set, returning ENOSYS will be treated + * as an error and signaled to the caller). + * + * Setting (or unsetting) this flag in the `want` field has *no + * effect*. + */ +#define FUSE_CAP_NO_OPEN_SUPPORT (1 << 17) + +/** + * Indicates support for parallel directory operations. If this flag + * is unset, the FUSE kernel module will ensure that lookup() and + * readdir() requests are never issued concurrently for the same + * directory. + * + * This feature is enabled by default when supported by the kernel. + */ +#define FUSE_CAP_PARALLEL_DIROPS (1 << 18) + +/** + * Indicates support for POSIX ACLs. + * + * If this feature is enabled, the kernel will cache and have + * responsibility for enforcing ACLs. ACL will be stored as xattrs and + * passed to userspace, which is responsible for updating the ACLs in + * the filesystem, keeping the file mode in sync with the ACL, and + * ensuring inheritance of default ACLs when new filesystem nodes are + * created. Note that this requires that the file system is able to + * parse and interpret the xattr representation of ACLs. + * + * Enabling this feature implicitly turns on the + * ``default_permissions`` mount option (even if it was not passed to + * mount(2)). + * + * This feature is disabled by default. + */ +#define FUSE_CAP_POSIX_ACL (1 << 19) + +/** + * Indicates that the filesystem is responsible for unsetting + * setuid and setgid bits when a file is written, truncated, or + * its owner is changed. + * + * This feature is enabled by default when supported by the kernel. + */ +#define FUSE_CAP_HANDLE_KILLPRIV (1 << 20) + +/** + * Indicates support for zero-message opendirs. If this flag is set in + * the `capable` field of the `fuse_conn_info` structure, then the filesystem + * may return `ENOSYS` from the opendir() handler to indicate success. Further + * opendir and releasedir messages will be handled in the kernel. (If this + * flag is not set, returning ENOSYS will be treated as an error and signalled + * to the caller.) + * + * Setting (or unsetting) this flag in the `want` field has *no effect*. + */ +#define FUSE_CAP_NO_OPENDIR_SUPPORT (1 << 24) + +/** + * Ioctl flags + * + * FUSE_IOCTL_COMPAT: 32bit compat ioctl on 64bit machine + * FUSE_IOCTL_UNRESTRICTED: not restricted to well-formed ioctls, retry allowed + * FUSE_IOCTL_RETRY: retry with new iovecs + * FUSE_IOCTL_DIR: is a directory + * + * FUSE_IOCTL_MAX_IOV: maximum of in_iovecs + out_iovecs + */ +#define FUSE_IOCTL_COMPAT (1 << 0) +#define FUSE_IOCTL_UNRESTRICTED (1 << 1) +#define FUSE_IOCTL_RETRY (1 << 2) +#define FUSE_IOCTL_DIR (1 << 4) + +#define FUSE_IOCTL_MAX_IOV 256 + +/** + * Connection information, passed to the ->init() method + * + * Some of the elements are read-write, these can be changed to + * indicate the value requested by the filesystem. The requested + * value must usually be smaller than the indicated value. + */ +struct fuse_conn_info { + /** + * Major version of the protocol (read-only) + */ + unsigned proto_major; + + /** + * Minor version of the protocol (read-only) + */ + unsigned proto_minor; + + /** + * Maximum size of the write buffer + */ + unsigned max_write; + + /** + * Maximum size of read requests. A value of zero indicates no + * limit. However, even if the filesystem does not specify a + * limit, the maximum size of read requests will still be + * limited by the kernel. + * + * NOTE: For the time being, the maximum size of read requests + * must be set both here *and* passed to fuse_session_new() + * using the ``-o max_read=`` mount option. At some point + * in the future, specifying the mount option will no longer + * be necessary. + */ + unsigned max_read; + + /** + * Maximum readahead + */ + unsigned max_readahead; + + /** + * Capability flags that the kernel supports (read-only) + */ + unsigned capable; + + /** + * Capability flags that the filesystem wants to enable. + * + * libfuse attempts to initialize this field with + * reasonable default values before calling the init() handler. + */ + unsigned want; + + /** + * Maximum number of pending "background" requests. A + * background request is any type of request for which the + * total number is not limited by other means. As of kernel + * 4.8, only two types of requests fall into this category: + * + * 1. Read-ahead requests + * 2. Asynchronous direct I/O requests + * + * Read-ahead requests are generated (if max_readahead is + * non-zero) by the kernel to preemptively fill its caches + * when it anticipates that userspace will soon read more + * data. + * + * Asynchronous direct I/O requests are generated if + * FUSE_CAP_ASYNC_DIO is enabled and userspace submits a large + * direct I/O request. In this case the kernel will internally + * split it up into multiple smaller requests and submit them + * to the filesystem concurrently. + * + * Note that the following requests are *not* background + * requests: writeback requests (limited by the kernel's + * flusher algorithm), regular (i.e., synchronous and + * buffered) userspace read/write requests (limited to one per + * thread), asynchronous read requests (Linux's io_submit(2) + * call actually blocks, so these are also limited to one per + * thread). + */ + unsigned max_background; + + /** + * Kernel congestion threshold parameter. If the number of pending + * background requests exceeds this number, the FUSE kernel module will + * mark the filesystem as "congested". This instructs the kernel to + * expect that queued requests will take some time to complete, and to + * adjust its algorithms accordingly (e.g. by putting a waiting thread + * to sleep instead of using a busy-loop). + */ + unsigned congestion_threshold; + + /** + * When FUSE_CAP_WRITEBACK_CACHE is enabled, the kernel is responsible + * for updating mtime and ctime when write requests are received. The + * updated values are passed to the filesystem with setattr() requests. + * However, if the filesystem does not support the full resolution of + * the kernel timestamps (nanoseconds), the mtime and ctime values used + * by kernel and filesystem will differ (and result in an apparent + * change of times after a cache flush). + * + * To prevent this problem, this variable can be used to inform the + * kernel about the timestamp granularity supported by the file-system. + * The value should be power of 10. The default is 1, i.e. full + * nano-second resolution. Filesystems supporting only second resolution + * should set this to 1000000000. + */ + unsigned time_gran; + + /** + * For future use. + */ + unsigned reserved[22]; +}; + +struct fuse_session; +struct fuse_pollhandle; +struct fuse_conn_info_opts; + +/** + * This function parses several command-line options that can be used + * to override elements of struct fuse_conn_info. The pointer returned + * by this function should be passed to the + * fuse_apply_conn_info_opts() method by the file system's init() + * handler. + * + * Before using this function, think twice if you really want these + * parameters to be adjustable from the command line. In most cases, + * they should be determined by the file system internally. + * + * The following options are recognized: + * + * -o max_write=N sets conn->max_write + * -o max_readahead=N sets conn->max_readahead + * -o max_background=N sets conn->max_background + * -o congestion_threshold=N sets conn->congestion_threshold + * -o async_read sets FUSE_CAP_ASYNC_READ in conn->want + * -o sync_read unsets FUSE_CAP_ASYNC_READ in conn->want + * -o atomic_o_trunc sets FUSE_CAP_ATOMIC_O_TRUNC in conn->want + * -o no_remote_lock Equivalent to -o + *no_remote_flock,no_remote_posix_lock -o no_remote_flock Unsets + *FUSE_CAP_FLOCK_LOCKS in conn->want -o no_remote_posix_lock Unsets + *FUSE_CAP_POSIX_LOCKS in conn->want -o [no_]splice_write (un-)sets + *FUSE_CAP_SPLICE_WRITE in conn->want -o [no_]splice_move (un-)sets + *FUSE_CAP_SPLICE_MOVE in conn->want -o [no_]splice_read (un-)sets + *FUSE_CAP_SPLICE_READ in conn->want -o [no_]auto_inval_data (un-)sets + *FUSE_CAP_AUTO_INVAL_DATA in conn->want -o readdirplus=no unsets + *FUSE_CAP_READDIRPLUS in conn->want -o readdirplus=yes sets + *FUSE_CAP_READDIRPLUS and unsets FUSE_CAP_READDIRPLUS_AUTO in conn->want -o + *readdirplus=auto sets FUSE_CAP_READDIRPLUS and FUSE_CAP_READDIRPLUS_AUTO + *in conn->want -o [no_]async_dio (un-)sets FUSE_CAP_ASYNC_DIO in + *conn->want -o [no_]writeback_cache (un-)sets FUSE_CAP_WRITEBACK_CACHE in + *conn->want -o time_gran=N sets conn->time_gran + * + * Known options will be removed from *args*, unknown options will be + * passed through unchanged. + * + * @param args argument vector (input+output) + * @return parsed options + **/ +struct fuse_conn_info_opts *fuse_parse_conn_info_opts(struct fuse_args *args); + +/** + * This function applies the (parsed) parameters in *opts* to the + * *conn* pointer. It may modify the following fields: wants, + * max_write, max_readahead, congestion_threshold, max_background, + * time_gran. A field is only set (or unset) if the corresponding + * option has been explicitly set. + */ +void fuse_apply_conn_info_opts(struct fuse_conn_info_opts *opts, + struct fuse_conn_info *conn); + +/** + * Go into the background + * + * @param foreground if true, stay in the foreground + * @return 0 on success, -1 on failure + */ +int fuse_daemonize(int foreground); + +/** + * Get the version of the library + * + * @return the version + */ +int fuse_version(void); + +/** + * Get the full package version string of the library + * + * @return the package version + */ +const char *fuse_pkgversion(void); + +/** + * Destroy poll handle + * + * @param ph the poll handle + */ +void fuse_pollhandle_destroy(struct fuse_pollhandle *ph); + +/* + * Data buffer + */ + +/** + * Buffer flags + */ +enum fuse_buf_flags { + /** + * Buffer contains a file descriptor + * + * If this flag is set, the .fd field is valid, otherwise the + * .mem fields is valid. + */ + FUSE_BUF_IS_FD = (1 << 1), + + /** + * Seek on the file descriptor + * + * If this flag is set then the .pos field is valid and is + * used to seek to the given offset before performing + * operation on file descriptor. + */ + FUSE_BUF_FD_SEEK = (1 << 2), + + /** + * Retry operation on file descriptor + * + * If this flag is set then retry operation on file descriptor + * until .size bytes have been copied or an error or EOF is + * detected. + */ + FUSE_BUF_FD_RETRY = (1 << 3), +}; + +/** + * Single data buffer + * + * Generic data buffer for I/O, extended attributes, etc... Data may + * be supplied as a memory pointer or as a file descriptor + */ +struct fuse_buf { + /** + * Size of data in bytes + */ + size_t size; + + /** + * Buffer flags + */ + enum fuse_buf_flags flags; + + /** + * Memory pointer + * + * Used unless FUSE_BUF_IS_FD flag is set. + */ + void *mem; + + /** + * File descriptor + * + * Used if FUSE_BUF_IS_FD flag is set. + */ + int fd; + + /** + * File position + * + * Used if FUSE_BUF_FD_SEEK flag is set. + */ + off_t pos; +}; + +/** + * Data buffer vector + * + * An array of data buffers, each containing a memory pointer or a + * file descriptor. + * + * Allocate dynamically to add more than one buffer. + */ +struct fuse_bufvec { + /** + * Number of buffers in the array + */ + size_t count; + + /** + * Index of current buffer within the array + */ + size_t idx; + + /** + * Current offset within the current buffer + */ + size_t off; + + /** + * Array of buffers + */ + struct fuse_buf buf[1]; +}; + +/* Initialize bufvec with a single buffer of given size */ +#define FUSE_BUFVEC_INIT(size__) \ + ((struct fuse_bufvec){ /* .count= */ 1, \ + /* .idx = */ 0, \ + /* .off = */ 0, /* .buf = */ \ + { /* [0] = */ { \ + /* .size = */ (size__), \ + /* .flags = */ (enum fuse_buf_flags)0, \ + /* .mem = */ NULL, \ + /* .fd = */ -1, \ + /* .pos = */ 0, \ + } } }) + +/** + * Get total size of data in a fuse buffer vector + * + * @param bufv buffer vector + * @return size of data + */ +size_t fuse_buf_size(const struct fuse_bufvec *bufv); + +/** + * Copy data from one buffer vector to another + * + * @param dst destination buffer vector + * @param src source buffer vector + * @return actual number of bytes copied or -errno on error + */ +ssize_t fuse_buf_copy(struct fuse_bufvec *dst, struct fuse_bufvec *src); + +/** + * Memory buffer iterator + * + */ +struct fuse_mbuf_iter { + /** + * Data pointer + */ + void *mem; + + /** + * Total length, in bytes + */ + size_t size; + + /** + * Offset from start of buffer + */ + size_t pos; +}; + +/* Initialize memory buffer iterator from a fuse_buf */ +#define FUSE_MBUF_ITER_INIT(fbuf) \ + ((struct fuse_mbuf_iter){ \ + .mem = fbuf->mem, \ + .size = fbuf->size, \ + .pos = 0, \ + }) + +/** + * Consume bytes from a memory buffer iterator + * + * @param iter memory buffer iterator + * @param len number of bytes to consume + * @return pointer to start of consumed bytes or + * NULL if advancing beyond end of buffer + */ +void *fuse_mbuf_iter_advance(struct fuse_mbuf_iter *iter, size_t len); + +/** + * Consume a NUL-terminated string from a memory buffer iterator + * + * @param iter memory buffer iterator + * @return pointer to the string or + * NULL if advancing beyond end of buffer or there is no NUL-terminator + */ +const char *fuse_mbuf_iter_advance_str(struct fuse_mbuf_iter *iter); + +/* + * Signal handling + */ +/** + * Exit session on HUP, TERM and INT signals and ignore PIPE signal + * + * Stores session in a global variable. May only be called once per + * process until fuse_remove_signal_handlers() is called. + * + * Once either of the POSIX signals arrives, the signal handler calls + * fuse_session_exit(). + * + * @param se the session to exit + * @return 0 on success, -1 on failure + * + * See also: + * fuse_remove_signal_handlers() + */ +int fuse_set_signal_handlers(struct fuse_session *se); + +/** + * Restore default signal handlers + * + * Resets global session. After this fuse_set_signal_handlers() may + * be called again. + * + * @param se the same session as given in fuse_set_signal_handlers() + * + * See also: + * fuse_set_signal_handlers() + */ +void fuse_remove_signal_handlers(struct fuse_session *se); + +/* + * Compatibility stuff + */ + +#if !defined(FUSE_USE_VERSION) || FUSE_USE_VERSION < 30 +#error only API version 30 or greater is supported +#endif + + +/* + * This interface uses 64 bit off_t. + * + * On 32bit systems please add -D_FILE_OFFSET_BITS=64 to your compile flags! + */ + +#if defined(__GNUC__) && \ + (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 6) && \ + !defined __cplusplus +_Static_assert(sizeof(off_t) == 8, "fuse: off_t must be 64bit"); +#else +struct _fuse_off_t_must_be_64bit_dummy_struct { + unsigned _fuse_off_t_must_be_64bit:((sizeof(off_t) == 8) ? 1 : -1); +}; +#endif + +#endif /* FUSE_COMMON_H_ */ diff --git a/tools/virtiofsd/fuse_i.h b/tools/virtiofsd/fuse_i.h new file mode 100644 index 0000000000..1240828208 --- /dev/null +++ b/tools/virtiofsd/fuse_i.h @@ -0,0 +1,99 @@ +/* + * FUSE: Filesystem in Userspace + * Copyright (C) 2001-2007 Miklos Szeredi + * + * This program can be distributed under the terms of the GNU LGPLv2. + * See the file COPYING.LIB + */ + +#ifndef FUSE_I_H +#define FUSE_I_H + +#define FUSE_USE_VERSION 31 +#include "fuse_lowlevel.h" + +struct fv_VuDev; +struct fv_QueueInfo; + +struct fuse_req { + struct fuse_session *se; + uint64_t unique; + int ctr; + pthread_mutex_t lock; + struct fuse_ctx ctx; + struct fuse_chan *ch; + int interrupted; + unsigned int ioctl_64bit:1; + union { + struct { + uint64_t unique; + } i; + struct { + fuse_interrupt_func_t func; + void *data; + } ni; + } u; + struct fuse_req *next; + struct fuse_req *prev; +}; + +struct fuse_notify_req { + uint64_t unique; + void (*reply)(struct fuse_notify_req *, fuse_req_t, fuse_ino_t, + const void *, const struct fuse_buf *); + struct fuse_notify_req *next; + struct fuse_notify_req *prev; +}; + +struct fuse_session { + char *mountpoint; + volatile int exited; + int fd; + int debug; + int deny_others; + struct fuse_lowlevel_ops op; + int got_init; + struct cuse_data *cuse_data; + void *userdata; + uid_t owner; + struct fuse_conn_info conn; + struct fuse_req list; + struct fuse_req interrupts; + pthread_mutex_t lock; + pthread_rwlock_t init_rwlock; + int got_destroy; + int broken_splice_nonblock; + uint64_t notify_ctr; + struct fuse_notify_req notify_list; + size_t bufsize; + int error; + char *vu_socket_path; + int vu_listen_fd; + int vu_socketfd; + struct fv_VuDev *virtio_dev; + int thread_pool_size; +}; + +struct fuse_chan { + pthread_mutex_t lock; + int ctr; + int fd; + struct fv_QueueInfo *qi; +}; + +int fuse_send_reply_iov_nofree(fuse_req_t req, int error, struct iovec *iov, + int count); +void fuse_free_req(fuse_req_t req); + +void fuse_session_process_buf_int(struct fuse_session *se, + struct fuse_bufvec *bufv, + struct fuse_chan *ch); + + +#define FUSE_MAX_MAX_PAGES 256 +#define FUSE_DEFAULT_MAX_PAGES_PER_REQ 32 + +/* room needed in buffer to accommodate header */ +#define FUSE_BUFFER_HEADER_SIZE 0x1000 + +#endif diff --git a/tools/virtiofsd/fuse_log.c b/tools/virtiofsd/fuse_log.c new file mode 100644 index 0000000000..c301ff6da1 --- /dev/null +++ b/tools/virtiofsd/fuse_log.c @@ -0,0 +1,41 @@ +/* + * FUSE: Filesystem in Userspace + * Copyright (C) 2019 Red Hat, Inc. + * + * Logging API. + * + * This program can be distributed under the terms of the GNU LGPLv2. + * See the file COPYING.LIB + */ + +#include "qemu/osdep.h" +#include "fuse_log.h" + +#include +#include + +static void default_log_func(__attribute__((unused)) enum fuse_log_level level, + const char *fmt, va_list ap) +{ + vfprintf(stderr, fmt, ap); +} + +static fuse_log_func_t log_func = default_log_func; + +void fuse_set_log_func(fuse_log_func_t func) +{ + if (!func) { + func = default_log_func; + } + + log_func = func; +} + +void fuse_log(enum fuse_log_level level, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + log_func(level, fmt, ap); + va_end(ap); +} diff --git a/tools/virtiofsd/fuse_log.h b/tools/virtiofsd/fuse_log.h new file mode 100644 index 0000000000..bf6c11ff11 --- /dev/null +++ b/tools/virtiofsd/fuse_log.h @@ -0,0 +1,74 @@ +/* + * FUSE: Filesystem in Userspace + * Copyright (C) 2019 Red Hat, Inc. + * + * This program can be distributed under the terms of the GNU LGPLv2. + * See the file COPYING.LIB. + */ + +#ifndef FUSE_LOG_H_ +#define FUSE_LOG_H_ + +/** @file + * + * This file defines the logging interface of FUSE + */ + +#include + +/** + * Log severity level + * + * These levels correspond to syslog(2) log levels since they are widely used. + */ +enum fuse_log_level { + FUSE_LOG_EMERG, + FUSE_LOG_ALERT, + FUSE_LOG_CRIT, + FUSE_LOG_ERR, + FUSE_LOG_WARNING, + FUSE_LOG_NOTICE, + FUSE_LOG_INFO, + FUSE_LOG_DEBUG +}; + +/** + * Log message handler function. + * + * This function must be thread-safe. It may be called from any libfuse + * function, including fuse_parse_cmdline() and other functions invoked before + * a FUSE filesystem is created. + * + * Install a custom log message handler function using fuse_set_log_func(). + * + * @param level log severity level + * @param fmt sprintf-style format string including newline + * @param ap format string arguments + */ +typedef void (*fuse_log_func_t)(enum fuse_log_level level, const char *fmt, + va_list ap); + +/** + * Install a custom log handler function. + * + * Log messages are emitted by libfuse functions to report errors and debug + * information. Messages are printed to stderr by default but this can be + * overridden by installing a custom log message handler function. + * + * The log message handler function is global and affects all FUSE filesystems + * created within this process. + * + * @param func a custom log message handler function or NULL to revert to + * the default + */ +void fuse_set_log_func(fuse_log_func_t func); + +/** + * Emit a log message + * + * @param level severity level (FUSE_LOG_ERR, FUSE_LOG_DEBUG, etc) + * @param fmt sprintf-style format string including newline + */ +void fuse_log(enum fuse_log_level level, const char *fmt, ...); + +#endif /* FUSE_LOG_H_ */ diff --git a/tools/virtiofsd/fuse_lowlevel.c b/tools/virtiofsd/fuse_lowlevel.c new file mode 100644 index 0000000000..2dd36ec03b --- /dev/null +++ b/tools/virtiofsd/fuse_lowlevel.c @@ -0,0 +1,2688 @@ +/* + * FUSE: Filesystem in Userspace + * Copyright (C) 2001-2007 Miklos Szeredi + * + * Implementation of (most of) the low-level FUSE API. The session loop + * functions are implemented in separate files. + * + * This program can be distributed under the terms of the GNU LGPLv2. + * See the file COPYING.LIB + */ + +#include "qemu/osdep.h" +#include "fuse_i.h" +#include "standard-headers/linux/fuse.h" +#include "fuse_misc.h" +#include "fuse_opt.h" +#include "fuse_virtio.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define THREAD_POOL_SIZE 64 + +#define OFFSET_MAX 0x7fffffffffffffffLL + +struct fuse_pollhandle { + uint64_t kh; + struct fuse_session *se; +}; + +static size_t pagesize; + +static __attribute__((constructor)) void fuse_ll_init_pagesize(void) +{ + pagesize = getpagesize(); +} + +static void convert_stat(const struct stat *stbuf, struct fuse_attr *attr) +{ + *attr = (struct fuse_attr){ + .ino = stbuf->st_ino, + .mode = stbuf->st_mode, + .nlink = stbuf->st_nlink, + .uid = stbuf->st_uid, + .gid = stbuf->st_gid, + .rdev = stbuf->st_rdev, + .size = stbuf->st_size, + .blksize = stbuf->st_blksize, + .blocks = stbuf->st_blocks, + .atime = stbuf->st_atime, + .mtime = stbuf->st_mtime, + .ctime = stbuf->st_ctime, + .atimensec = ST_ATIM_NSEC(stbuf), + .mtimensec = ST_MTIM_NSEC(stbuf), + .ctimensec = ST_CTIM_NSEC(stbuf), + }; +} + +static void convert_attr(const struct fuse_setattr_in *attr, struct stat *stbuf) +{ + stbuf->st_mode = attr->mode; + stbuf->st_uid = attr->uid; + stbuf->st_gid = attr->gid; + stbuf->st_size = attr->size; + stbuf->st_atime = attr->atime; + stbuf->st_mtime = attr->mtime; + stbuf->st_ctime = attr->ctime; + ST_ATIM_NSEC_SET(stbuf, attr->atimensec); + ST_MTIM_NSEC_SET(stbuf, attr->mtimensec); + ST_CTIM_NSEC_SET(stbuf, attr->ctimensec); +} + +static size_t iov_length(const struct iovec *iov, size_t count) +{ + size_t seg; + size_t ret = 0; + + for (seg = 0; seg < count; seg++) { + ret += iov[seg].iov_len; + } + return ret; +} + +static void list_init_req(struct fuse_req *req) +{ + req->next = req; + req->prev = req; +} + +static void list_del_req(struct fuse_req *req) +{ + struct fuse_req *prev = req->prev; + struct fuse_req *next = req->next; + prev->next = next; + next->prev = prev; +} + +static void list_add_req(struct fuse_req *req, struct fuse_req *next) +{ + struct fuse_req *prev = next->prev; + req->next = next; + req->prev = prev; + prev->next = req; + next->prev = req; +} + +static void destroy_req(fuse_req_t req) +{ + pthread_mutex_destroy(&req->lock); + free(req); +} + +void fuse_free_req(fuse_req_t req) +{ + int ctr; + struct fuse_session *se = req->se; + + pthread_mutex_lock(&se->lock); + req->u.ni.func = NULL; + req->u.ni.data = NULL; + list_del_req(req); + ctr = --req->ctr; + req->ch = NULL; + pthread_mutex_unlock(&se->lock); + if (!ctr) { + destroy_req(req); + } +} + +static struct fuse_req *fuse_ll_alloc_req(struct fuse_session *se) +{ + struct fuse_req *req; + + req = (struct fuse_req *)calloc(1, sizeof(struct fuse_req)); + if (req == NULL) { + fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate request\n"); + } else { + req->se = se; + req->ctr = 1; + list_init_req(req); + fuse_mutex_init(&req->lock); + } + + return req; +} + +/* Send data. If *ch* is NULL, send via session master fd */ +static int fuse_send_msg(struct fuse_session *se, struct fuse_chan *ch, + struct iovec *iov, int count) +{ + struct fuse_out_header *out = iov[0].iov_base; + + out->len = iov_length(iov, count); + if (out->unique == 0) { + fuse_log(FUSE_LOG_DEBUG, "NOTIFY: code=%d length=%u\n", out->error, + out->len); + } else if (out->error) { + fuse_log(FUSE_LOG_DEBUG, + " unique: %llu, error: %i (%s), outsize: %i\n", + (unsigned long long)out->unique, out->error, + strerror(-out->error), out->len); + } else { + fuse_log(FUSE_LOG_DEBUG, " unique: %llu, success, outsize: %i\n", + (unsigned long long)out->unique, out->len); + } + + if (fuse_lowlevel_is_virtio(se)) { + return virtio_send_msg(se, ch, iov, count); + } + + abort(); /* virtio should have taken it before here */ + return 0; +} + + +int fuse_send_reply_iov_nofree(fuse_req_t req, int error, struct iovec *iov, + int count) +{ + struct fuse_out_header out = { + .unique = req->unique, + .error = error, + }; + + if (error <= -1000 || error > 0) { + fuse_log(FUSE_LOG_ERR, "fuse: bad error value: %i\n", error); + out.error = -ERANGE; + } + + iov[0].iov_base = &out; + iov[0].iov_len = sizeof(struct fuse_out_header); + + return fuse_send_msg(req->se, req->ch, iov, count); +} + +static int send_reply_iov(fuse_req_t req, int error, struct iovec *iov, + int count) +{ + int res; + + res = fuse_send_reply_iov_nofree(req, error, iov, count); + fuse_free_req(req); + return res; +} + +static int send_reply(fuse_req_t req, int error, const void *arg, + size_t argsize) +{ + struct iovec iov[2]; + int count = 1; + if (argsize) { + iov[1].iov_base = (void *)arg; + iov[1].iov_len = argsize; + count++; + } + return send_reply_iov(req, error, iov, count); +} + +int fuse_reply_iov(fuse_req_t req, const struct iovec *iov, int count) +{ + int res; + struct iovec *padded_iov; + + padded_iov = malloc((count + 1) * sizeof(struct iovec)); + if (padded_iov == NULL) { + return fuse_reply_err(req, ENOMEM); + } + + memcpy(padded_iov + 1, iov, count * sizeof(struct iovec)); + count++; + + res = send_reply_iov(req, 0, padded_iov, count); + free(padded_iov); + + return res; +} + + +/* + * 'buf` is allowed to be empty so that the proper size may be + * allocated by the caller + */ +size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize, + const char *name, const struct stat *stbuf, off_t off) +{ + (void)req; + size_t namelen; + size_t entlen; + size_t entlen_padded; + struct fuse_dirent *dirent; + + namelen = strlen(name); + entlen = FUSE_NAME_OFFSET + namelen; + entlen_padded = FUSE_DIRENT_ALIGN(entlen); + + if ((buf == NULL) || (entlen_padded > bufsize)) { + return entlen_padded; + } + + dirent = (struct fuse_dirent *)buf; + dirent->ino = stbuf->st_ino; + dirent->off = off; + dirent->namelen = namelen; + dirent->type = (stbuf->st_mode & S_IFMT) >> 12; + memcpy(dirent->name, name, namelen); + memset(dirent->name + namelen, 0, entlen_padded - entlen); + + return entlen_padded; +} + +static void convert_statfs(const struct statvfs *stbuf, + struct fuse_kstatfs *kstatfs) +{ + *kstatfs = (struct fuse_kstatfs){ + .bsize = stbuf->f_bsize, + .frsize = stbuf->f_frsize, + .blocks = stbuf->f_blocks, + .bfree = stbuf->f_bfree, + .bavail = stbuf->f_bavail, + .files = stbuf->f_files, + .ffree = stbuf->f_ffree, + .namelen = stbuf->f_namemax, + }; +} + +static int send_reply_ok(fuse_req_t req, const void *arg, size_t argsize) +{ + return send_reply(req, 0, arg, argsize); +} + +int fuse_reply_err(fuse_req_t req, int err) +{ + return send_reply(req, -err, NULL, 0); +} + +void fuse_reply_none(fuse_req_t req) +{ + fuse_free_req(req); +} + +static unsigned long calc_timeout_sec(double t) +{ + if (t > (double)ULONG_MAX) { + return ULONG_MAX; + } else if (t < 0.0) { + return 0; + } else { + return (unsigned long)t; + } +} + +static unsigned int calc_timeout_nsec(double t) +{ + double f = t - (double)calc_timeout_sec(t); + if (f < 0.0) { + return 0; + } else if (f >= 0.999999999) { + return 999999999; + } else { + return (unsigned int)(f * 1.0e9); + } +} + +static void fill_entry(struct fuse_entry_out *arg, + const struct fuse_entry_param *e) +{ + *arg = (struct fuse_entry_out){ + .nodeid = e->ino, + .generation = e->generation, + .entry_valid = calc_timeout_sec(e->entry_timeout), + .entry_valid_nsec = calc_timeout_nsec(e->entry_timeout), + .attr_valid = calc_timeout_sec(e->attr_timeout), + .attr_valid_nsec = calc_timeout_nsec(e->attr_timeout), + }; + convert_stat(&e->attr, &arg->attr); +} + +/* + * `buf` is allowed to be empty so that the proper size may be + * allocated by the caller + */ +size_t fuse_add_direntry_plus(fuse_req_t req, char *buf, size_t bufsize, + const char *name, + const struct fuse_entry_param *e, off_t off) +{ + (void)req; + size_t namelen; + size_t entlen; + size_t entlen_padded; + + namelen = strlen(name); + entlen = FUSE_NAME_OFFSET_DIRENTPLUS + namelen; + entlen_padded = FUSE_DIRENT_ALIGN(entlen); + if ((buf == NULL) || (entlen_padded > bufsize)) { + return entlen_padded; + } + + struct fuse_direntplus *dp = (struct fuse_direntplus *)buf; + memset(&dp->entry_out, 0, sizeof(dp->entry_out)); + fill_entry(&dp->entry_out, e); + + struct fuse_dirent *dirent = &dp->dirent; + *dirent = (struct fuse_dirent){ + .ino = e->attr.st_ino, + .off = off, + .namelen = namelen, + .type = (e->attr.st_mode & S_IFMT) >> 12, + }; + memcpy(dirent->name, name, namelen); + memset(dirent->name + namelen, 0, entlen_padded - entlen); + + return entlen_padded; +} + +static void fill_open(struct fuse_open_out *arg, const struct fuse_file_info *f) +{ + arg->fh = f->fh; + if (f->direct_io) { + arg->open_flags |= FOPEN_DIRECT_IO; + } + if (f->keep_cache) { + arg->open_flags |= FOPEN_KEEP_CACHE; + } + if (f->cache_readdir) { + arg->open_flags |= FOPEN_CACHE_DIR; + } + if (f->nonseekable) { + arg->open_flags |= FOPEN_NONSEEKABLE; + } +} + +int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e) +{ + struct fuse_entry_out arg; + size_t size = sizeof(arg); + + memset(&arg, 0, sizeof(arg)); + fill_entry(&arg, e); + return send_reply_ok(req, &arg, size); +} + +int fuse_reply_create(fuse_req_t req, const struct fuse_entry_param *e, + const struct fuse_file_info *f) +{ + char buf[sizeof(struct fuse_entry_out) + sizeof(struct fuse_open_out)]; + size_t entrysize = sizeof(struct fuse_entry_out); + struct fuse_entry_out *earg = (struct fuse_entry_out *)buf; + struct fuse_open_out *oarg = (struct fuse_open_out *)(buf + entrysize); + + memset(buf, 0, sizeof(buf)); + fill_entry(earg, e); + fill_open(oarg, f); + return send_reply_ok(req, buf, entrysize + sizeof(struct fuse_open_out)); +} + +int fuse_reply_attr(fuse_req_t req, const struct stat *attr, + double attr_timeout) +{ + struct fuse_attr_out arg; + size_t size = sizeof(arg); + + memset(&arg, 0, sizeof(arg)); + arg.attr_valid = calc_timeout_sec(attr_timeout); + arg.attr_valid_nsec = calc_timeout_nsec(attr_timeout); + convert_stat(attr, &arg.attr); + + return send_reply_ok(req, &arg, size); +} + +int fuse_reply_readlink(fuse_req_t req, const char *linkname) +{ + return send_reply_ok(req, linkname, strlen(linkname)); +} + +int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *f) +{ + struct fuse_open_out arg; + + memset(&arg, 0, sizeof(arg)); + fill_open(&arg, f); + return send_reply_ok(req, &arg, sizeof(arg)); +} + +int fuse_reply_write(fuse_req_t req, size_t count) +{ + struct fuse_write_out arg; + + memset(&arg, 0, sizeof(arg)); + arg.size = count; + + return send_reply_ok(req, &arg, sizeof(arg)); +} + +int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size) +{ + return send_reply_ok(req, buf, size); +} + +static int fuse_send_data_iov_fallback(struct fuse_session *se, + struct fuse_chan *ch, struct iovec *iov, + int iov_count, struct fuse_bufvec *buf, + size_t len) +{ + /* Optimize common case */ + if (buf->count == 1 && buf->idx == 0 && buf->off == 0 && + !(buf->buf[0].flags & FUSE_BUF_IS_FD)) { + /* + * FIXME: also avoid memory copy if there are multiple buffers + * but none of them contain an fd + */ + + iov[iov_count].iov_base = buf->buf[0].mem; + iov[iov_count].iov_len = len; + iov_count++; + return fuse_send_msg(se, ch, iov, iov_count); + } + + if (fuse_lowlevel_is_virtio(se) && buf->count == 1 && + buf->buf[0].flags == (FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK)) { + return virtio_send_data_iov(se, ch, iov, iov_count, buf, len); + } + + abort(); /* Will have taken vhost path */ + return 0; +} + +static int fuse_send_data_iov(struct fuse_session *se, struct fuse_chan *ch, + struct iovec *iov, int iov_count, + struct fuse_bufvec *buf) +{ + size_t len = fuse_buf_size(buf); + + return fuse_send_data_iov_fallback(se, ch, iov, iov_count, buf, len); +} + +int fuse_reply_data(fuse_req_t req, struct fuse_bufvec *bufv) +{ + struct iovec iov[2]; + struct fuse_out_header out = { + .unique = req->unique, + }; + int res; + + iov[0].iov_base = &out; + iov[0].iov_len = sizeof(struct fuse_out_header); + + res = fuse_send_data_iov(req->se, req->ch, iov, 1, bufv); + if (res <= 0) { + fuse_free_req(req); + return res; + } else { + return fuse_reply_err(req, res); + } +} + +int fuse_reply_statfs(fuse_req_t req, const struct statvfs *stbuf) +{ + struct fuse_statfs_out arg; + size_t size = sizeof(arg); + + memset(&arg, 0, sizeof(arg)); + convert_statfs(stbuf, &arg.st); + + return send_reply_ok(req, &arg, size); +} + +int fuse_reply_xattr(fuse_req_t req, size_t count) +{ + struct fuse_getxattr_out arg; + + memset(&arg, 0, sizeof(arg)); + arg.size = count; + + return send_reply_ok(req, &arg, sizeof(arg)); +} + +int fuse_reply_lock(fuse_req_t req, const struct flock *lock) +{ + struct fuse_lk_out arg; + + memset(&arg, 0, sizeof(arg)); + arg.lk.type = lock->l_type; + if (lock->l_type != F_UNLCK) { + arg.lk.start = lock->l_start; + if (lock->l_len == 0) { + arg.lk.end = OFFSET_MAX; + } else { + arg.lk.end = lock->l_start + lock->l_len - 1; + } + } + arg.lk.pid = lock->l_pid; + return send_reply_ok(req, &arg, sizeof(arg)); +} + +int fuse_reply_bmap(fuse_req_t req, uint64_t idx) +{ + struct fuse_bmap_out arg; + + memset(&arg, 0, sizeof(arg)); + arg.block = idx; + + return send_reply_ok(req, &arg, sizeof(arg)); +} + +static struct fuse_ioctl_iovec *fuse_ioctl_iovec_copy(const struct iovec *iov, + size_t count) +{ + struct fuse_ioctl_iovec *fiov; + size_t i; + + fiov = malloc(sizeof(fiov[0]) * count); + if (!fiov) { + return NULL; + } + + for (i = 0; i < count; i++) { + fiov[i].base = (uintptr_t)iov[i].iov_base; + fiov[i].len = iov[i].iov_len; + } + + return fiov; +} + +int fuse_reply_ioctl_retry(fuse_req_t req, const struct iovec *in_iov, + size_t in_count, const struct iovec *out_iov, + size_t out_count) +{ + struct fuse_ioctl_out arg; + struct fuse_ioctl_iovec *in_fiov = NULL; + struct fuse_ioctl_iovec *out_fiov = NULL; + struct iovec iov[4]; + size_t count = 1; + int res; + + memset(&arg, 0, sizeof(arg)); + arg.flags |= FUSE_IOCTL_RETRY; + arg.in_iovs = in_count; + arg.out_iovs = out_count; + iov[count].iov_base = &arg; + iov[count].iov_len = sizeof(arg); + count++; + + /* Can't handle non-compat 64bit ioctls on 32bit */ + if (sizeof(void *) == 4 && req->ioctl_64bit) { + res = fuse_reply_err(req, EINVAL); + goto out; + } + + if (in_count) { + in_fiov = fuse_ioctl_iovec_copy(in_iov, in_count); + if (!in_fiov) { + goto enomem; + } + + iov[count].iov_base = (void *)in_fiov; + iov[count].iov_len = sizeof(in_fiov[0]) * in_count; + count++; + } + if (out_count) { + out_fiov = fuse_ioctl_iovec_copy(out_iov, out_count); + if (!out_fiov) { + goto enomem; + } + + iov[count].iov_base = (void *)out_fiov; + iov[count].iov_len = sizeof(out_fiov[0]) * out_count; + count++; + } + + res = send_reply_iov(req, 0, iov, count); +out: + free(in_fiov); + free(out_fiov); + + return res; + +enomem: + res = fuse_reply_err(req, ENOMEM); + goto out; +} + +int fuse_reply_ioctl(fuse_req_t req, int result, const void *buf, size_t size) +{ + struct fuse_ioctl_out arg; + struct iovec iov[3]; + size_t count = 1; + + memset(&arg, 0, sizeof(arg)); + arg.result = result; + iov[count].iov_base = &arg; + iov[count].iov_len = sizeof(arg); + count++; + + if (size) { + iov[count].iov_base = (char *)buf; + iov[count].iov_len = size; + count++; + } + + return send_reply_iov(req, 0, iov, count); +} + +int fuse_reply_ioctl_iov(fuse_req_t req, int result, const struct iovec *iov, + int count) +{ + struct iovec *padded_iov; + struct fuse_ioctl_out arg; + int res; + + padded_iov = malloc((count + 2) * sizeof(struct iovec)); + if (padded_iov == NULL) { + return fuse_reply_err(req, ENOMEM); + } + + memset(&arg, 0, sizeof(arg)); + arg.result = result; + padded_iov[1].iov_base = &arg; + padded_iov[1].iov_len = sizeof(arg); + + memcpy(&padded_iov[2], iov, count * sizeof(struct iovec)); + + res = send_reply_iov(req, 0, padded_iov, count + 2); + free(padded_iov); + + return res; +} + +int fuse_reply_poll(fuse_req_t req, unsigned revents) +{ + struct fuse_poll_out arg; + + memset(&arg, 0, sizeof(arg)); + arg.revents = revents; + + return send_reply_ok(req, &arg, sizeof(arg)); +} + +int fuse_reply_lseek(fuse_req_t req, off_t off) +{ + struct fuse_lseek_out arg; + + memset(&arg, 0, sizeof(arg)); + arg.offset = off; + + return send_reply_ok(req, &arg, sizeof(arg)); +} + +static void do_lookup(fuse_req_t req, fuse_ino_t nodeid, + struct fuse_mbuf_iter *iter) +{ + const char *name = fuse_mbuf_iter_advance_str(iter); + if (!name) { + fuse_reply_err(req, EINVAL); + return; + } + + if (req->se->op.lookup) { + req->se->op.lookup(req, nodeid, name); + } else { + fuse_reply_err(req, ENOSYS); + } +} + +static void do_forget(fuse_req_t req, fuse_ino_t nodeid, + struct fuse_mbuf_iter *iter) +{ + struct fuse_forget_in *arg; + + arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); + if (!arg) { + fuse_reply_err(req, EINVAL); + return; + } + + if (req->se->op.forget) { + req->se->op.forget(req, nodeid, arg->nlookup); + } else { + fuse_reply_none(req); + } +} + +static void do_batch_forget(fuse_req_t req, fuse_ino_t nodeid, + struct fuse_mbuf_iter *iter) +{ + struct fuse_batch_forget_in *arg; + struct fuse_forget_data *forgets; + size_t scount; + + (void)nodeid; + + arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); + if (!arg) { + fuse_reply_none(req); + return; + } + + /* + * Prevent integer overflow. The compiler emits the following warning + * unless we use the scount local variable: + * + * error: comparison is always false due to limited range of data type + * [-Werror=type-limits] + * + * This may be true on 64-bit hosts but we need this check for 32-bit + * hosts. + */ + scount = arg->count; + if (scount > SIZE_MAX / sizeof(forgets[0])) { + fuse_reply_none(req); + return; + } + + forgets = fuse_mbuf_iter_advance(iter, arg->count * sizeof(forgets[0])); + if (!forgets) { + fuse_reply_none(req); + return; + } + + if (req->se->op.forget_multi) { + req->se->op.forget_multi(req, arg->count, forgets); + } else if (req->se->op.forget) { + unsigned int i; + + for (i = 0; i < arg->count; i++) { + struct fuse_req *dummy_req; + + dummy_req = fuse_ll_alloc_req(req->se); + if (dummy_req == NULL) { + break; + } + + dummy_req->unique = req->unique; + dummy_req->ctx = req->ctx; + dummy_req->ch = NULL; + + req->se->op.forget(dummy_req, forgets[i].ino, forgets[i].nlookup); + } + fuse_reply_none(req); + } else { + fuse_reply_none(req); + } +} + +static void do_getattr(fuse_req_t req, fuse_ino_t nodeid, + struct fuse_mbuf_iter *iter) +{ + struct fuse_file_info *fip = NULL; + struct fuse_file_info fi; + + struct fuse_getattr_in *arg; + + arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); + if (!arg) { + fuse_reply_err(req, EINVAL); + return; + } + + if (arg->getattr_flags & FUSE_GETATTR_FH) { + memset(&fi, 0, sizeof(fi)); + fi.fh = arg->fh; + fip = &fi; + } + + if (req->se->op.getattr) { + req->se->op.getattr(req, nodeid, fip); + } else { + fuse_reply_err(req, ENOSYS); + } +} + +static void do_setattr(fuse_req_t req, fuse_ino_t nodeid, + struct fuse_mbuf_iter *iter) +{ + if (req->se->op.setattr) { + struct fuse_setattr_in *arg; + struct fuse_file_info *fi = NULL; + struct fuse_file_info fi_store; + struct stat stbuf; + + arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); + if (!arg) { + fuse_reply_err(req, EINVAL); + return; + } + + memset(&stbuf, 0, sizeof(stbuf)); + convert_attr(arg, &stbuf); + if (arg->valid & FATTR_FH) { + arg->valid &= ~FATTR_FH; + memset(&fi_store, 0, sizeof(fi_store)); + fi = &fi_store; + fi->fh = arg->fh; + } + arg->valid &= FUSE_SET_ATTR_MODE | FUSE_SET_ATTR_UID | + FUSE_SET_ATTR_GID | FUSE_SET_ATTR_SIZE | + FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME | + FUSE_SET_ATTR_ATIME_NOW | FUSE_SET_ATTR_MTIME_NOW | + FUSE_SET_ATTR_CTIME; + + req->se->op.setattr(req, nodeid, &stbuf, arg->valid, fi); + } else { + fuse_reply_err(req, ENOSYS); + } +} + +static void do_access(fuse_req_t req, fuse_ino_t nodeid, + struct fuse_mbuf_iter *iter) +{ + struct fuse_access_in *arg; + + arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); + if (!arg) { + fuse_reply_err(req, EINVAL); + return; + } + + if (req->se->op.access) { + req->se->op.access(req, nodeid, arg->mask); + } else { + fuse_reply_err(req, ENOSYS); + } +} + +static void do_readlink(fuse_req_t req, fuse_ino_t nodeid, + struct fuse_mbuf_iter *iter) +{ + (void)iter; + + if (req->se->op.readlink) { + req->se->op.readlink(req, nodeid); + } else { + fuse_reply_err(req, ENOSYS); + } +} + +static void do_mknod(fuse_req_t req, fuse_ino_t nodeid, + struct fuse_mbuf_iter *iter) +{ + struct fuse_mknod_in *arg; + const char *name; + + arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); + name = fuse_mbuf_iter_advance_str(iter); + if (!arg || !name) { + fuse_reply_err(req, EINVAL); + return; + } + + req->ctx.umask = arg->umask; + + if (req->se->op.mknod) { + req->se->op.mknod(req, nodeid, name, arg->mode, arg->rdev); + } else { + fuse_reply_err(req, ENOSYS); + } +} + +static void do_mkdir(fuse_req_t req, fuse_ino_t nodeid, + struct fuse_mbuf_iter *iter) +{ + struct fuse_mkdir_in *arg; + const char *name; + + arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); + name = fuse_mbuf_iter_advance_str(iter); + if (!arg || !name) { + fuse_reply_err(req, EINVAL); + return; + } + + req->ctx.umask = arg->umask; + + if (req->se->op.mkdir) { + req->se->op.mkdir(req, nodeid, name, arg->mode); + } else { + fuse_reply_err(req, ENOSYS); + } +} + +static void do_unlink(fuse_req_t req, fuse_ino_t nodeid, + struct fuse_mbuf_iter *iter) +{ + const char *name = fuse_mbuf_iter_advance_str(iter); + + if (!name) { + fuse_reply_err(req, EINVAL); + return; + } + + if (req->se->op.unlink) { + req->se->op.unlink(req, nodeid, name); + } else { + fuse_reply_err(req, ENOSYS); + } +} + +static void do_rmdir(fuse_req_t req, fuse_ino_t nodeid, + struct fuse_mbuf_iter *iter) +{ + const char *name = fuse_mbuf_iter_advance_str(iter); + + if (!name) { + fuse_reply_err(req, EINVAL); + return; + } + + if (req->se->op.rmdir) { + req->se->op.rmdir(req, nodeid, name); + } else { + fuse_reply_err(req, ENOSYS); + } +} + +static void do_symlink(fuse_req_t req, fuse_ino_t nodeid, + struct fuse_mbuf_iter *iter) +{ + const char *name = fuse_mbuf_iter_advance_str(iter); + const char *linkname = fuse_mbuf_iter_advance_str(iter); + + if (!name || !linkname) { + fuse_reply_err(req, EINVAL); + return; + } + + if (req->se->op.symlink) { + req->se->op.symlink(req, linkname, nodeid, name); + } else { + fuse_reply_err(req, ENOSYS); + } +} + +static void do_rename(fuse_req_t req, fuse_ino_t nodeid, + struct fuse_mbuf_iter *iter) +{ + struct fuse_rename_in *arg; + const char *oldname; + const char *newname; + + arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); + oldname = fuse_mbuf_iter_advance_str(iter); + newname = fuse_mbuf_iter_advance_str(iter); + if (!arg || !oldname || !newname) { + fuse_reply_err(req, EINVAL); + return; + } + + if (req->se->op.rename) { + req->se->op.rename(req, nodeid, oldname, arg->newdir, newname, 0); + } else { + fuse_reply_err(req, ENOSYS); + } +} + +static void do_rename2(fuse_req_t req, fuse_ino_t nodeid, + struct fuse_mbuf_iter *iter) +{ + struct fuse_rename2_in *arg; + const char *oldname; + const char *newname; + + arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); + oldname = fuse_mbuf_iter_advance_str(iter); + newname = fuse_mbuf_iter_advance_str(iter); + if (!arg || !oldname || !newname) { + fuse_reply_err(req, EINVAL); + return; + } + + if (req->se->op.rename) { + req->se->op.rename(req, nodeid, oldname, arg->newdir, newname, + arg->flags); + } else { + fuse_reply_err(req, ENOSYS); + } +} + +static void do_link(fuse_req_t req, fuse_ino_t nodeid, + struct fuse_mbuf_iter *iter) +{ + struct fuse_link_in *arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); + const char *name = fuse_mbuf_iter_advance_str(iter); + + if (!arg || !name) { + fuse_reply_err(req, EINVAL); + return; + } + + if (req->se->op.link) { + req->se->op.link(req, arg->oldnodeid, nodeid, name); + } else { + fuse_reply_err(req, ENOSYS); + } +} + +static void do_create(fuse_req_t req, fuse_ino_t nodeid, + struct fuse_mbuf_iter *iter) +{ + if (req->se->op.create) { + struct fuse_create_in *arg; + struct fuse_file_info fi; + const char *name; + + arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); + name = fuse_mbuf_iter_advance_str(iter); + if (!arg || !name) { + fuse_reply_err(req, EINVAL); + return; + } + + memset(&fi, 0, sizeof(fi)); + fi.flags = arg->flags; + + req->ctx.umask = arg->umask; + + req->se->op.create(req, nodeid, name, arg->mode, &fi); + } else { + fuse_reply_err(req, ENOSYS); + } +} + +static void do_open(fuse_req_t req, fuse_ino_t nodeid, + struct fuse_mbuf_iter *iter) +{ + struct fuse_open_in *arg; + 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.flags = arg->flags; + + if (req->se->op.open) { + req->se->op.open(req, nodeid, &fi); + } else { + fuse_reply_open(req, &fi); + } +} + +static void do_read(fuse_req_t req, fuse_ino_t nodeid, + struct fuse_mbuf_iter *iter) +{ + if (req->se->op.read) { + struct fuse_read_in *arg; + 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; + fi.lock_owner = arg->lock_owner; + fi.flags = arg->flags; + req->se->op.read(req, nodeid, arg->size, arg->offset, &fi); + } else { + fuse_reply_err(req, ENOSYS); + } +} + +static void do_write(fuse_req_t req, fuse_ino_t nodeid, + struct fuse_mbuf_iter *iter) +{ + struct fuse_write_in *arg; + struct fuse_file_info fi; + const char *param; + + arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); + if (!arg) { + fuse_reply_err(req, EINVAL); + return; + } + + param = fuse_mbuf_iter_advance(iter, arg->size); + if (!param) { + fuse_reply_err(req, EINVAL); + return; + } + + memset(&fi, 0, sizeof(fi)); + fi.fh = arg->fh; + fi.writepage = (arg->write_flags & FUSE_WRITE_CACHE) != 0; + fi.kill_priv = !!(arg->write_flags & FUSE_WRITE_KILL_PRIV); + + fi.lock_owner = arg->lock_owner; + fi.flags = arg->flags; + + if (req->se->op.write) { + req->se->op.write(req, nodeid, param, arg->size, arg->offset, &fi); + } else { + fuse_reply_err(req, ENOSYS); + } +} + +static void do_write_buf(fuse_req_t req, fuse_ino_t nodeid, + struct fuse_mbuf_iter *iter, struct fuse_bufvec *ibufv) +{ + struct fuse_session *se = req->se; + struct fuse_bufvec *pbufv = ibufv; + struct fuse_bufvec tmpbufv = { + .buf[0] = ibufv->buf[0], + .count = 1, + }; + struct fuse_write_in *arg; + size_t arg_size = sizeof(*arg); + struct fuse_file_info fi; + + memset(&fi, 0, sizeof(fi)); + + arg = fuse_mbuf_iter_advance(iter, arg_size); + if (!arg) { + fuse_reply_err(req, EINVAL); + return; + } + + fi.lock_owner = arg->lock_owner; + fi.flags = arg->flags; + fi.fh = arg->fh; + fi.writepage = !!(arg->write_flags & FUSE_WRITE_CACHE); + fi.kill_priv = !!(arg->write_flags & FUSE_WRITE_KILL_PRIV); + + if (ibufv->count == 1) { + assert(!(tmpbufv.buf[0].flags & FUSE_BUF_IS_FD)); + tmpbufv.buf[0].mem = ((char *)arg) + arg_size; + tmpbufv.buf[0].size -= sizeof(struct fuse_in_header) + arg_size; + pbufv = &tmpbufv; + } else { + /* + * Input bufv contains the headers in the first element + * and the data in the rest, we need to skip that first element + */ + ibufv->buf[0].size = 0; + } + + if (fuse_buf_size(pbufv) != arg->size) { + fuse_log(FUSE_LOG_ERR, + "fuse: do_write_buf: buffer size doesn't match arg->size\n"); + fuse_reply_err(req, EIO); + return; + } + + se->op.write_buf(req, nodeid, pbufv, arg->offset, &fi); +} + +static void do_flush(fuse_req_t req, fuse_ino_t nodeid, + struct fuse_mbuf_iter *iter) +{ + struct fuse_flush_in *arg; + 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; + fi.flush = 1; + fi.lock_owner = arg->lock_owner; + + if (req->se->op.flush) { + req->se->op.flush(req, nodeid, &fi); + } else { + fuse_reply_err(req, ENOSYS); + } +} + +static void do_release(fuse_req_t req, fuse_ino_t nodeid, + struct fuse_mbuf_iter *iter) +{ + struct fuse_release_in *arg; + 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.flags = arg->flags; + fi.fh = arg->fh; + fi.flush = (arg->release_flags & FUSE_RELEASE_FLUSH) ? 1 : 0; + fi.lock_owner = arg->lock_owner; + + if (arg->release_flags & FUSE_RELEASE_FLOCK_UNLOCK) { + fi.flock_release = 1; + } + + if (req->se->op.release) { + req->se->op.release(req, nodeid, &fi); + } else { + fuse_reply_err(req, 0); + } +} + +static void do_fsync(fuse_req_t req, fuse_ino_t nodeid, + struct fuse_mbuf_iter *iter) +{ + struct fuse_fsync_in *arg; + struct fuse_file_info fi; + int datasync; + + arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); + if (!arg) { + fuse_reply_err(req, EINVAL); + return; + } + datasync = arg->fsync_flags & 1; + + memset(&fi, 0, sizeof(fi)); + fi.fh = arg->fh; + + if (req->se->op.fsync) { + if (fi.fh == (uint64_t)-1) { + req->se->op.fsync(req, nodeid, datasync, NULL); + } else { + req->se->op.fsync(req, nodeid, datasync, &fi); + } + } else { + fuse_reply_err(req, ENOSYS); + } +} + +static void do_opendir(fuse_req_t req, fuse_ino_t nodeid, + struct fuse_mbuf_iter *iter) +{ + struct fuse_open_in *arg; + 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.flags = arg->flags; + + if (req->se->op.opendir) { + req->se->op.opendir(req, nodeid, &fi); + } else { + fuse_reply_open(req, &fi); + } +} + +static void do_readdir(fuse_req_t req, fuse_ino_t nodeid, + struct fuse_mbuf_iter *iter) +{ + struct fuse_read_in *arg; + 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; + + if (req->se->op.readdir) { + req->se->op.readdir(req, nodeid, arg->size, arg->offset, &fi); + } else { + fuse_reply_err(req, ENOSYS); + } +} + +static void do_readdirplus(fuse_req_t req, fuse_ino_t nodeid, + struct fuse_mbuf_iter *iter) +{ + struct fuse_read_in *arg; + 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; + + if (req->se->op.readdirplus) { + req->se->op.readdirplus(req, nodeid, arg->size, arg->offset, &fi); + } else { + fuse_reply_err(req, ENOSYS); + } +} + +static void do_releasedir(fuse_req_t req, fuse_ino_t nodeid, + struct fuse_mbuf_iter *iter) +{ + struct fuse_release_in *arg; + 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.flags = arg->flags; + fi.fh = arg->fh; + + if (req->se->op.releasedir) { + req->se->op.releasedir(req, nodeid, &fi); + } else { + fuse_reply_err(req, 0); + } +} + +static void do_fsyncdir(fuse_req_t req, fuse_ino_t nodeid, + struct fuse_mbuf_iter *iter) +{ + struct fuse_fsync_in *arg; + struct fuse_file_info fi; + int datasync; + + arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); + if (!arg) { + fuse_reply_err(req, EINVAL); + return; + } + datasync = arg->fsync_flags & 1; + + memset(&fi, 0, sizeof(fi)); + fi.fh = arg->fh; + + if (req->se->op.fsyncdir) { + req->se->op.fsyncdir(req, nodeid, datasync, &fi); + } else { + fuse_reply_err(req, ENOSYS); + } +} + +static void do_statfs(fuse_req_t req, fuse_ino_t nodeid, + struct fuse_mbuf_iter *iter) +{ + (void)nodeid; + (void)iter; + + if (req->se->op.statfs) { + req->se->op.statfs(req, nodeid); + } else { + struct statvfs buf = { + .f_namemax = 255, + .f_bsize = 512, + }; + fuse_reply_statfs(req, &buf); + } +} + +static void do_setxattr(fuse_req_t req, fuse_ino_t nodeid, + struct fuse_mbuf_iter *iter) +{ + struct fuse_setxattr_in *arg; + const char *name; + const char *value; + + arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); + name = fuse_mbuf_iter_advance_str(iter); + if (!arg || !name) { + fuse_reply_err(req, EINVAL); + return; + } + + value = fuse_mbuf_iter_advance(iter, arg->size); + if (!value) { + fuse_reply_err(req, EINVAL); + return; + } + + if (req->se->op.setxattr) { + req->se->op.setxattr(req, nodeid, name, value, arg->size, arg->flags); + } else { + fuse_reply_err(req, ENOSYS); + } +} + +static void do_getxattr(fuse_req_t req, fuse_ino_t nodeid, + struct fuse_mbuf_iter *iter) +{ + struct fuse_getxattr_in *arg; + const char *name; + + arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); + name = fuse_mbuf_iter_advance_str(iter); + if (!arg || !name) { + fuse_reply_err(req, EINVAL); + return; + } + + if (req->se->op.getxattr) { + req->se->op.getxattr(req, nodeid, name, arg->size); + } else { + fuse_reply_err(req, ENOSYS); + } +} + +static void do_listxattr(fuse_req_t req, fuse_ino_t nodeid, + struct fuse_mbuf_iter *iter) +{ + struct fuse_getxattr_in *arg; + + arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); + if (!arg) { + fuse_reply_err(req, EINVAL); + return; + } + + if (req->se->op.listxattr) { + req->se->op.listxattr(req, nodeid, arg->size); + } else { + fuse_reply_err(req, ENOSYS); + } +} + +static void do_removexattr(fuse_req_t req, fuse_ino_t nodeid, + struct fuse_mbuf_iter *iter) +{ + const char *name = fuse_mbuf_iter_advance_str(iter); + + if (!name) { + fuse_reply_err(req, EINVAL); + return; + } + + if (req->se->op.removexattr) { + req->se->op.removexattr(req, nodeid, name); + } else { + fuse_reply_err(req, ENOSYS); + } +} + +static void convert_fuse_file_lock(struct fuse_file_lock *fl, + struct flock *flock) +{ + memset(flock, 0, sizeof(struct flock)); + flock->l_type = fl->type; + flock->l_whence = SEEK_SET; + flock->l_start = fl->start; + if (fl->end == OFFSET_MAX) { + flock->l_len = 0; + } else { + flock->l_len = fl->end - fl->start + 1; + } + flock->l_pid = fl->pid; +} + +static void do_getlk(fuse_req_t req, fuse_ino_t nodeid, + struct fuse_mbuf_iter *iter) +{ + struct fuse_lk_in *arg; + struct fuse_file_info fi; + struct flock flock; + + 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; + fi.lock_owner = arg->owner; + + convert_fuse_file_lock(&arg->lk, &flock); + if (req->se->op.getlk) { + req->se->op.getlk(req, nodeid, &fi, &flock); + } else { + fuse_reply_err(req, ENOSYS); + } +} + +static void do_setlk_common(fuse_req_t req, fuse_ino_t nodeid, + struct fuse_mbuf_iter *iter, int sleep) +{ + struct fuse_lk_in *arg; + struct fuse_file_info fi; + struct flock flock; + + 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; + fi.lock_owner = arg->owner; + + if (arg->lk_flags & FUSE_LK_FLOCK) { + int op = 0; + + switch (arg->lk.type) { + case F_RDLCK: + op = LOCK_SH; + break; + case F_WRLCK: + op = LOCK_EX; + break; + case F_UNLCK: + op = LOCK_UN; + break; + } + if (!sleep) { + op |= LOCK_NB; + } + + if (req->se->op.flock) { + req->se->op.flock(req, nodeid, &fi, op); + } else { + fuse_reply_err(req, ENOSYS); + } + } else { + convert_fuse_file_lock(&arg->lk, &flock); + if (req->se->op.setlk) { + req->se->op.setlk(req, nodeid, &fi, &flock, sleep); + } else { + fuse_reply_err(req, ENOSYS); + } + } +} + +static void do_setlk(fuse_req_t req, fuse_ino_t nodeid, + struct fuse_mbuf_iter *iter) +{ + do_setlk_common(req, nodeid, iter, 0); +} + +static void do_setlkw(fuse_req_t req, fuse_ino_t nodeid, + struct fuse_mbuf_iter *iter) +{ + do_setlk_common(req, nodeid, iter, 1); +} + +static int find_interrupted(struct fuse_session *se, struct fuse_req *req) +{ + struct fuse_req *curr; + + for (curr = se->list.next; curr != &se->list; curr = curr->next) { + if (curr->unique == req->u.i.unique) { + fuse_interrupt_func_t func; + void *data; + + curr->ctr++; + pthread_mutex_unlock(&se->lock); + + /* Ugh, ugly locking */ + pthread_mutex_lock(&curr->lock); + pthread_mutex_lock(&se->lock); + curr->interrupted = 1; + func = curr->u.ni.func; + data = curr->u.ni.data; + pthread_mutex_unlock(&se->lock); + if (func) { + func(curr, data); + } + pthread_mutex_unlock(&curr->lock); + + pthread_mutex_lock(&se->lock); + curr->ctr--; + if (!curr->ctr) { + destroy_req(curr); + } + + return 1; + } + } + for (curr = se->interrupts.next; curr != &se->interrupts; + curr = curr->next) { + if (curr->u.i.unique == req->u.i.unique) { + return 1; + } + } + return 0; +} + +static void do_interrupt(fuse_req_t req, fuse_ino_t nodeid, + struct fuse_mbuf_iter *iter) +{ + struct fuse_interrupt_in *arg; + struct fuse_session *se = req->se; + + (void)nodeid; + + arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); + if (!arg) { + fuse_reply_err(req, EINVAL); + return; + } + + fuse_log(FUSE_LOG_DEBUG, "INTERRUPT: %llu\n", + (unsigned long long)arg->unique); + + req->u.i.unique = arg->unique; + + pthread_mutex_lock(&se->lock); + if (find_interrupted(se, req)) { + destroy_req(req); + } else { + list_add_req(req, &se->interrupts); + } + pthread_mutex_unlock(&se->lock); +} + +static struct fuse_req *check_interrupt(struct fuse_session *se, + struct fuse_req *req) +{ + struct fuse_req *curr; + + for (curr = se->interrupts.next; curr != &se->interrupts; + curr = curr->next) { + if (curr->u.i.unique == req->unique) { + req->interrupted = 1; + list_del_req(curr); + free(curr); + return NULL; + } + } + curr = se->interrupts.next; + if (curr != &se->interrupts) { + list_del_req(curr); + list_init_req(curr); + return curr; + } else { + return NULL; + } +} + +static void do_bmap(fuse_req_t req, fuse_ino_t nodeid, + struct fuse_mbuf_iter *iter) +{ + struct fuse_bmap_in *arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); + + if (!arg) { + fuse_reply_err(req, EINVAL); + return; + } + + if (req->se->op.bmap) { + req->se->op.bmap(req, nodeid, arg->blocksize, arg->block); + } else { + fuse_reply_err(req, ENOSYS); + } +} + +static void do_ioctl(fuse_req_t req, fuse_ino_t nodeid, + struct fuse_mbuf_iter *iter) +{ + struct fuse_ioctl_in *arg; + unsigned int flags; + void *in_buf = NULL; + struct fuse_file_info fi; + + arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); + if (!arg) { + fuse_reply_err(req, EINVAL); + return; + } + + flags = arg->flags; + if (flags & FUSE_IOCTL_DIR && !(req->se->conn.want & FUSE_CAP_IOCTL_DIR)) { + fuse_reply_err(req, ENOTTY); + return; + } + + if (arg->in_size) { + in_buf = fuse_mbuf_iter_advance(iter, arg->in_size); + if (!in_buf) { + fuse_reply_err(req, EINVAL); + return; + } + } + + memset(&fi, 0, sizeof(fi)); + fi.fh = arg->fh; + + if (sizeof(void *) == 4 && !(flags & FUSE_IOCTL_32BIT)) { + req->ioctl_64bit = 1; + } + + if (req->se->op.ioctl) { + req->se->op.ioctl(req, nodeid, arg->cmd, (void *)(uintptr_t)arg->arg, + &fi, flags, in_buf, arg->in_size, arg->out_size); + } else { + fuse_reply_err(req, ENOSYS); + } +} + +void fuse_pollhandle_destroy(struct fuse_pollhandle *ph) +{ + free(ph); +} + +static void do_poll(fuse_req_t req, fuse_ino_t nodeid, + struct fuse_mbuf_iter *iter) +{ + struct fuse_poll_in *arg; + 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; + fi.poll_events = arg->events; + + if (req->se->op.poll) { + struct fuse_pollhandle *ph = NULL; + + if (arg->flags & FUSE_POLL_SCHEDULE_NOTIFY) { + ph = malloc(sizeof(struct fuse_pollhandle)); + if (ph == NULL) { + fuse_reply_err(req, ENOMEM); + return; + } + ph->kh = arg->kh; + ph->se = req->se; + } + + req->se->op.poll(req, nodeid, &fi, ph); + } else { + fuse_reply_err(req, ENOSYS); + } +} + +static void do_fallocate(fuse_req_t req, fuse_ino_t nodeid, + struct fuse_mbuf_iter *iter) +{ + struct fuse_fallocate_in *arg; + 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; + + if (req->se->op.fallocate) { + req->se->op.fallocate(req, nodeid, arg->mode, arg->offset, arg->length, + &fi); + } else { + fuse_reply_err(req, ENOSYS); + } +} + +static void do_copy_file_range(fuse_req_t req, fuse_ino_t nodeid_in, + struct fuse_mbuf_iter *iter) +{ + struct fuse_copy_file_range_in *arg; + struct fuse_file_info fi_in, fi_out; + + arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); + if (!arg) { + fuse_reply_err(req, EINVAL); + return; + } + + memset(&fi_in, 0, sizeof(fi_in)); + fi_in.fh = arg->fh_in; + + memset(&fi_out, 0, sizeof(fi_out)); + fi_out.fh = arg->fh_out; + + + if (req->se->op.copy_file_range) { + req->se->op.copy_file_range(req, nodeid_in, arg->off_in, &fi_in, + arg->nodeid_out, arg->off_out, &fi_out, + arg->len, arg->flags); + } else { + fuse_reply_err(req, ENOSYS); + } +} + +static void do_lseek(fuse_req_t req, fuse_ino_t nodeid, + struct fuse_mbuf_iter *iter) +{ + struct fuse_lseek_in *arg; + 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; + + if (req->se->op.lseek) { + req->se->op.lseek(req, nodeid, arg->offset, arg->whence, &fi); + } else { + fuse_reply_err(req, ENOSYS); + } +} + +static void do_init(fuse_req_t req, fuse_ino_t nodeid, + struct fuse_mbuf_iter *iter) +{ + size_t compat_size = offsetof(struct fuse_init_in, max_readahead); + struct fuse_init_in *arg; + struct fuse_init_out outarg; + struct fuse_session *se = req->se; + size_t bufsize = se->bufsize; + size_t outargsize = sizeof(outarg); + + (void)nodeid; + + /* First consume the old fields... */ + arg = fuse_mbuf_iter_advance(iter, compat_size); + if (!arg) { + fuse_reply_err(req, EINVAL); + return; + } + + /* ...and now consume the new fields. */ + if (arg->major == 7 && arg->minor >= 6) { + if (!fuse_mbuf_iter_advance(iter, sizeof(*arg) - compat_size)) { + fuse_reply_err(req, EINVAL); + return; + } + } + + fuse_log(FUSE_LOG_DEBUG, "INIT: %u.%u\n", arg->major, arg->minor); + if (arg->major == 7 && arg->minor >= 6) { + fuse_log(FUSE_LOG_DEBUG, "flags=0x%08x\n", arg->flags); + fuse_log(FUSE_LOG_DEBUG, "max_readahead=0x%08x\n", arg->max_readahead); + } + se->conn.proto_major = arg->major; + se->conn.proto_minor = arg->minor; + se->conn.capable = 0; + se->conn.want = 0; + + memset(&outarg, 0, sizeof(outarg)); + outarg.major = FUSE_KERNEL_VERSION; + outarg.minor = FUSE_KERNEL_MINOR_VERSION; + + if (arg->major < 7 || (arg->major == 7 && arg->minor < 31)) { + fuse_log(FUSE_LOG_ERR, "fuse: unsupported protocol version: %u.%u\n", + arg->major, arg->minor); + fuse_reply_err(req, EPROTO); + return; + } + + if (arg->major > 7) { + /* Wait for a second INIT request with a 7.X version */ + send_reply_ok(req, &outarg, sizeof(outarg)); + return; + } + + if (arg->max_readahead < se->conn.max_readahead) { + se->conn.max_readahead = arg->max_readahead; + } + if (arg->flags & FUSE_ASYNC_READ) { + se->conn.capable |= FUSE_CAP_ASYNC_READ; + } + if (arg->flags & FUSE_POSIX_LOCKS) { + se->conn.capable |= FUSE_CAP_POSIX_LOCKS; + } + if (arg->flags & FUSE_ATOMIC_O_TRUNC) { + se->conn.capable |= FUSE_CAP_ATOMIC_O_TRUNC; + } + if (arg->flags & FUSE_EXPORT_SUPPORT) { + se->conn.capable |= FUSE_CAP_EXPORT_SUPPORT; + } + if (arg->flags & FUSE_DONT_MASK) { + se->conn.capable |= FUSE_CAP_DONT_MASK; + } + if (arg->flags & FUSE_FLOCK_LOCKS) { + se->conn.capable |= FUSE_CAP_FLOCK_LOCKS; + } + if (arg->flags & FUSE_AUTO_INVAL_DATA) { + se->conn.capable |= FUSE_CAP_AUTO_INVAL_DATA; + } + if (arg->flags & FUSE_DO_READDIRPLUS) { + se->conn.capable |= FUSE_CAP_READDIRPLUS; + } + if (arg->flags & FUSE_READDIRPLUS_AUTO) { + se->conn.capable |= FUSE_CAP_READDIRPLUS_AUTO; + } + if (arg->flags & FUSE_ASYNC_DIO) { + se->conn.capable |= FUSE_CAP_ASYNC_DIO; + } + if (arg->flags & FUSE_WRITEBACK_CACHE) { + se->conn.capable |= FUSE_CAP_WRITEBACK_CACHE; + } + if (arg->flags & FUSE_NO_OPEN_SUPPORT) { + se->conn.capable |= FUSE_CAP_NO_OPEN_SUPPORT; + } + if (arg->flags & FUSE_PARALLEL_DIROPS) { + se->conn.capable |= FUSE_CAP_PARALLEL_DIROPS; + } + if (arg->flags & FUSE_POSIX_ACL) { + se->conn.capable |= FUSE_CAP_POSIX_ACL; + } + if (arg->flags & FUSE_HANDLE_KILLPRIV) { + se->conn.capable |= FUSE_CAP_HANDLE_KILLPRIV; + } + if (arg->flags & FUSE_NO_OPENDIR_SUPPORT) { + se->conn.capable |= FUSE_CAP_NO_OPENDIR_SUPPORT; + } + if (!(arg->flags & FUSE_MAX_PAGES)) { + size_t max_bufsize = FUSE_DEFAULT_MAX_PAGES_PER_REQ * getpagesize() + + FUSE_BUFFER_HEADER_SIZE; + if (bufsize > max_bufsize) { + bufsize = max_bufsize; + } + } +#ifdef HAVE_SPLICE +#ifdef HAVE_VMSPLICE + se->conn.capable |= FUSE_CAP_SPLICE_WRITE | FUSE_CAP_SPLICE_MOVE; +#endif + se->conn.capable |= FUSE_CAP_SPLICE_READ; +#endif + se->conn.capable |= FUSE_CAP_IOCTL_DIR; + + /* + * Default settings for modern filesystems. + * + * Most of these capabilities were disabled by default in + * libfuse2 for backwards compatibility reasons. In libfuse3, + * we can finally enable them by default (as long as they're + * supported by the kernel). + */ +#define LL_SET_DEFAULT(cond, cap) \ + if ((cond) && (se->conn.capable & (cap))) \ + se->conn.want |= (cap) + LL_SET_DEFAULT(1, FUSE_CAP_ASYNC_READ); + LL_SET_DEFAULT(1, FUSE_CAP_PARALLEL_DIROPS); + LL_SET_DEFAULT(1, FUSE_CAP_AUTO_INVAL_DATA); + LL_SET_DEFAULT(1, FUSE_CAP_HANDLE_KILLPRIV); + LL_SET_DEFAULT(1, FUSE_CAP_ASYNC_DIO); + LL_SET_DEFAULT(1, FUSE_CAP_IOCTL_DIR); + LL_SET_DEFAULT(1, FUSE_CAP_ATOMIC_O_TRUNC); + LL_SET_DEFAULT(se->op.write_buf, FUSE_CAP_SPLICE_READ); + LL_SET_DEFAULT(se->op.getlk && se->op.setlk, FUSE_CAP_POSIX_LOCKS); + LL_SET_DEFAULT(se->op.flock, FUSE_CAP_FLOCK_LOCKS); + LL_SET_DEFAULT(se->op.readdirplus, FUSE_CAP_READDIRPLUS); + LL_SET_DEFAULT(se->op.readdirplus && se->op.readdir, + FUSE_CAP_READDIRPLUS_AUTO); + se->conn.time_gran = 1; + + if (bufsize < FUSE_MIN_READ_BUFFER) { + fuse_log(FUSE_LOG_ERR, "fuse: warning: buffer size too small: %zu\n", + bufsize); + bufsize = FUSE_MIN_READ_BUFFER; + } + se->bufsize = bufsize; + + if (se->conn.max_write > bufsize - FUSE_BUFFER_HEADER_SIZE) { + se->conn.max_write = bufsize - FUSE_BUFFER_HEADER_SIZE; + } + + se->got_init = 1; + se->got_destroy = 0; + if (se->op.init) { + se->op.init(se->userdata, &se->conn); + } + + if (se->conn.want & (~se->conn.capable)) { + fuse_log(FUSE_LOG_ERR, + "fuse: error: filesystem requested capabilities " + "0x%x that are not supported by kernel, aborting.\n", + se->conn.want & (~se->conn.capable)); + fuse_reply_err(req, EPROTO); + se->error = -EPROTO; + fuse_session_exit(se); + return; + } + + if (se->conn.max_write < bufsize - FUSE_BUFFER_HEADER_SIZE) { + se->bufsize = se->conn.max_write + FUSE_BUFFER_HEADER_SIZE; + } + if (arg->flags & FUSE_MAX_PAGES) { + outarg.flags |= FUSE_MAX_PAGES; + outarg.max_pages = (se->conn.max_write - 1) / getpagesize() + 1; + } + + /* + * Always enable big writes, this is superseded + * by the max_write option + */ + outarg.flags |= FUSE_BIG_WRITES; + + if (se->conn.want & FUSE_CAP_ASYNC_READ) { + outarg.flags |= FUSE_ASYNC_READ; + } + if (se->conn.want & FUSE_CAP_PARALLEL_DIROPS) { + outarg.flags |= FUSE_PARALLEL_DIROPS; + } + if (se->conn.want & FUSE_CAP_POSIX_LOCKS) { + outarg.flags |= FUSE_POSIX_LOCKS; + } + if (se->conn.want & FUSE_CAP_ATOMIC_O_TRUNC) { + outarg.flags |= FUSE_ATOMIC_O_TRUNC; + } + if (se->conn.want & FUSE_CAP_EXPORT_SUPPORT) { + outarg.flags |= FUSE_EXPORT_SUPPORT; + } + if (se->conn.want & FUSE_CAP_DONT_MASK) { + outarg.flags |= FUSE_DONT_MASK; + } + if (se->conn.want & FUSE_CAP_FLOCK_LOCKS) { + outarg.flags |= FUSE_FLOCK_LOCKS; + } + if (se->conn.want & FUSE_CAP_AUTO_INVAL_DATA) { + outarg.flags |= FUSE_AUTO_INVAL_DATA; + } + if (se->conn.want & FUSE_CAP_READDIRPLUS) { + outarg.flags |= FUSE_DO_READDIRPLUS; + } + if (se->conn.want & FUSE_CAP_READDIRPLUS_AUTO) { + outarg.flags |= FUSE_READDIRPLUS_AUTO; + } + if (se->conn.want & FUSE_CAP_ASYNC_DIO) { + outarg.flags |= FUSE_ASYNC_DIO; + } + if (se->conn.want & FUSE_CAP_WRITEBACK_CACHE) { + outarg.flags |= FUSE_WRITEBACK_CACHE; + } + if (se->conn.want & FUSE_CAP_POSIX_ACL) { + outarg.flags |= FUSE_POSIX_ACL; + } + outarg.max_readahead = se->conn.max_readahead; + outarg.max_write = se->conn.max_write; + if (se->conn.max_background >= (1 << 16)) { + se->conn.max_background = (1 << 16) - 1; + } + if (se->conn.congestion_threshold > se->conn.max_background) { + se->conn.congestion_threshold = se->conn.max_background; + } + if (!se->conn.congestion_threshold) { + se->conn.congestion_threshold = se->conn.max_background * 3 / 4; + } + + outarg.max_background = se->conn.max_background; + outarg.congestion_threshold = se->conn.congestion_threshold; + outarg.time_gran = se->conn.time_gran; + + fuse_log(FUSE_LOG_DEBUG, " INIT: %u.%u\n", outarg.major, outarg.minor); + fuse_log(FUSE_LOG_DEBUG, " flags=0x%08x\n", outarg.flags); + fuse_log(FUSE_LOG_DEBUG, " max_readahead=0x%08x\n", outarg.max_readahead); + fuse_log(FUSE_LOG_DEBUG, " max_write=0x%08x\n", outarg.max_write); + fuse_log(FUSE_LOG_DEBUG, " max_background=%i\n", outarg.max_background); + fuse_log(FUSE_LOG_DEBUG, " congestion_threshold=%i\n", + outarg.congestion_threshold); + fuse_log(FUSE_LOG_DEBUG, " time_gran=%u\n", outarg.time_gran); + + send_reply_ok(req, &outarg, outargsize); +} + +static void do_destroy(fuse_req_t req, fuse_ino_t nodeid, + struct fuse_mbuf_iter *iter) +{ + struct fuse_session *se = req->se; + + (void)nodeid; + (void)iter; + + se->got_destroy = 1; + se->got_init = 0; + if (se->op.destroy) { + se->op.destroy(se->userdata); + } + + send_reply_ok(req, NULL, 0); +} + +static int send_notify_iov(struct fuse_session *se, int notify_code, + struct iovec *iov, int count) +{ + struct fuse_out_header out = { + .error = notify_code, + }; + + if (!se->got_init) { + return -ENOTCONN; + } + + iov[0].iov_base = &out; + iov[0].iov_len = sizeof(struct fuse_out_header); + + return fuse_send_msg(se, NULL, iov, count); +} + +int fuse_lowlevel_notify_poll(struct fuse_pollhandle *ph) +{ + if (ph != NULL) { + struct fuse_notify_poll_wakeup_out outarg = { + .kh = ph->kh, + }; + struct iovec iov[2]; + + iov[1].iov_base = &outarg; + iov[1].iov_len = sizeof(outarg); + + return send_notify_iov(ph->se, FUSE_NOTIFY_POLL, iov, 2); + } else { + return 0; + } +} + +int fuse_lowlevel_notify_inval_inode(struct fuse_session *se, fuse_ino_t ino, + off_t off, off_t len) +{ + struct fuse_notify_inval_inode_out outarg = { + .ino = ino, + .off = off, + .len = len, + }; + struct iovec iov[2]; + + if (!se) { + return -EINVAL; + } + + iov[1].iov_base = &outarg; + iov[1].iov_len = sizeof(outarg); + + return send_notify_iov(se, FUSE_NOTIFY_INVAL_INODE, iov, 2); +} + +int fuse_lowlevel_notify_inval_entry(struct fuse_session *se, fuse_ino_t parent, + const char *name, size_t namelen) +{ + struct fuse_notify_inval_entry_out outarg = { + .parent = parent, + .namelen = namelen, + }; + struct iovec iov[3]; + + if (!se) { + return -EINVAL; + } + + iov[1].iov_base = &outarg; + iov[1].iov_len = sizeof(outarg); + iov[2].iov_base = (void *)name; + iov[2].iov_len = namelen + 1; + + return send_notify_iov(se, FUSE_NOTIFY_INVAL_ENTRY, iov, 3); +} + +int fuse_lowlevel_notify_delete(struct fuse_session *se, fuse_ino_t parent, + fuse_ino_t child, const char *name, + size_t namelen) +{ + struct fuse_notify_delete_out outarg = { + .parent = parent, + .child = child, + .namelen = namelen, + }; + struct iovec iov[3]; + + if (!se) { + return -EINVAL; + } + + iov[1].iov_base = &outarg; + iov[1].iov_len = sizeof(outarg); + iov[2].iov_base = (void *)name; + iov[2].iov_len = namelen + 1; + + return send_notify_iov(se, FUSE_NOTIFY_DELETE, iov, 3); +} + +int fuse_lowlevel_notify_store(struct fuse_session *se, fuse_ino_t ino, + off_t offset, struct fuse_bufvec *bufv) +{ + struct fuse_out_header out = { + .error = FUSE_NOTIFY_STORE, + }; + struct fuse_notify_store_out outarg = { + .nodeid = ino, + .offset = offset, + .size = fuse_buf_size(bufv), + }; + struct iovec iov[3]; + int res; + + if (!se) { + return -EINVAL; + } + + iov[0].iov_base = &out; + iov[0].iov_len = sizeof(out); + iov[1].iov_base = &outarg; + iov[1].iov_len = sizeof(outarg); + + res = fuse_send_data_iov(se, NULL, iov, 2, bufv); + if (res > 0) { + res = -res; + } + + return res; +} + +void *fuse_req_userdata(fuse_req_t req) +{ + return req->se->userdata; +} + +const struct fuse_ctx *fuse_req_ctx(fuse_req_t req) +{ + return &req->ctx; +} + +void fuse_req_interrupt_func(fuse_req_t req, fuse_interrupt_func_t func, + void *data) +{ + pthread_mutex_lock(&req->lock); + pthread_mutex_lock(&req->se->lock); + req->u.ni.func = func; + req->u.ni.data = data; + pthread_mutex_unlock(&req->se->lock); + if (req->interrupted && func) { + func(req, data); + } + pthread_mutex_unlock(&req->lock); +} + +int fuse_req_interrupted(fuse_req_t req) +{ + int interrupted; + + pthread_mutex_lock(&req->se->lock); + interrupted = req->interrupted; + pthread_mutex_unlock(&req->se->lock); + + return interrupted; +} + +static struct { + void (*func)(fuse_req_t, fuse_ino_t, struct fuse_mbuf_iter *); + const char *name; +} fuse_ll_ops[] = { + [FUSE_LOOKUP] = { do_lookup, "LOOKUP" }, + [FUSE_FORGET] = { do_forget, "FORGET" }, + [FUSE_GETATTR] = { do_getattr, "GETATTR" }, + [FUSE_SETATTR] = { do_setattr, "SETATTR" }, + [FUSE_READLINK] = { do_readlink, "READLINK" }, + [FUSE_SYMLINK] = { do_symlink, "SYMLINK" }, + [FUSE_MKNOD] = { do_mknod, "MKNOD" }, + [FUSE_MKDIR] = { do_mkdir, "MKDIR" }, + [FUSE_UNLINK] = { do_unlink, "UNLINK" }, + [FUSE_RMDIR] = { do_rmdir, "RMDIR" }, + [FUSE_RENAME] = { do_rename, "RENAME" }, + [FUSE_LINK] = { do_link, "LINK" }, + [FUSE_OPEN] = { do_open, "OPEN" }, + [FUSE_READ] = { do_read, "READ" }, + [FUSE_WRITE] = { do_write, "WRITE" }, + [FUSE_STATFS] = { do_statfs, "STATFS" }, + [FUSE_RELEASE] = { do_release, "RELEASE" }, + [FUSE_FSYNC] = { do_fsync, "FSYNC" }, + [FUSE_SETXATTR] = { do_setxattr, "SETXATTR" }, + [FUSE_GETXATTR] = { do_getxattr, "GETXATTR" }, + [FUSE_LISTXATTR] = { do_listxattr, "LISTXATTR" }, + [FUSE_REMOVEXATTR] = { do_removexattr, "REMOVEXATTR" }, + [FUSE_FLUSH] = { do_flush, "FLUSH" }, + [FUSE_INIT] = { do_init, "INIT" }, + [FUSE_OPENDIR] = { do_opendir, "OPENDIR" }, + [FUSE_READDIR] = { do_readdir, "READDIR" }, + [FUSE_RELEASEDIR] = { do_releasedir, "RELEASEDIR" }, + [FUSE_FSYNCDIR] = { do_fsyncdir, "FSYNCDIR" }, + [FUSE_GETLK] = { do_getlk, "GETLK" }, + [FUSE_SETLK] = { do_setlk, "SETLK" }, + [FUSE_SETLKW] = { do_setlkw, "SETLKW" }, + [FUSE_ACCESS] = { do_access, "ACCESS" }, + [FUSE_CREATE] = { do_create, "CREATE" }, + [FUSE_INTERRUPT] = { do_interrupt, "INTERRUPT" }, + [FUSE_BMAP] = { do_bmap, "BMAP" }, + [FUSE_IOCTL] = { do_ioctl, "IOCTL" }, + [FUSE_POLL] = { do_poll, "POLL" }, + [FUSE_FALLOCATE] = { do_fallocate, "FALLOCATE" }, + [FUSE_DESTROY] = { do_destroy, "DESTROY" }, + [FUSE_NOTIFY_REPLY] = { NULL, "NOTIFY_REPLY" }, + [FUSE_BATCH_FORGET] = { do_batch_forget, "BATCH_FORGET" }, + [FUSE_READDIRPLUS] = { do_readdirplus, "READDIRPLUS" }, + [FUSE_RENAME2] = { do_rename2, "RENAME2" }, + [FUSE_COPY_FILE_RANGE] = { do_copy_file_range, "COPY_FILE_RANGE" }, + [FUSE_LSEEK] = { do_lseek, "LSEEK" }, +}; + +#define FUSE_MAXOP (sizeof(fuse_ll_ops) / sizeof(fuse_ll_ops[0])) + +static const char *opname(enum fuse_opcode opcode) +{ + if (opcode >= FUSE_MAXOP || !fuse_ll_ops[opcode].name) { + return "???"; + } else { + return fuse_ll_ops[opcode].name; + } +} + +void fuse_session_process_buf(struct fuse_session *se, + const struct fuse_buf *buf) +{ + struct fuse_bufvec bufv = { .buf[0] = *buf, .count = 1 }; + fuse_session_process_buf_int(se, &bufv, NULL); +} + +/* + * Restriction: + * bufv is normally a single entry buffer, except for a write + * where (if it's in memory) then the bufv may be multiple entries, + * where the first entry contains all headers and subsequent entries + * contain data + * bufv shall not use any offsets etc to make the data anything + * other than contiguous starting from 0. + */ +void fuse_session_process_buf_int(struct fuse_session *se, + struct fuse_bufvec *bufv, + struct fuse_chan *ch) +{ + const struct fuse_buf *buf = bufv->buf; + struct fuse_mbuf_iter iter = FUSE_MBUF_ITER_INIT(buf); + struct fuse_in_header *in; + struct fuse_req *req; + int err; + + /* The first buffer must be a memory buffer */ + assert(!(buf->flags & FUSE_BUF_IS_FD)); + + in = fuse_mbuf_iter_advance(&iter, sizeof(*in)); + assert(in); /* caller guarantees the input buffer is large enough */ + + fuse_log( + FUSE_LOG_DEBUG, + "unique: %llu, opcode: %s (%i), nodeid: %llu, insize: %zu, pid: %u\n", + (unsigned long long)in->unique, opname((enum fuse_opcode)in->opcode), + in->opcode, (unsigned long long)in->nodeid, buf->size, in->pid); + + req = fuse_ll_alloc_req(se); + if (req == NULL) { + struct fuse_out_header out = { + .unique = in->unique, + .error = -ENOMEM, + }; + struct iovec iov = { + .iov_base = &out, + .iov_len = sizeof(struct fuse_out_header), + }; + + fuse_send_msg(se, ch, &iov, 1); + return; + } + + req->unique = in->unique; + req->ctx.uid = in->uid; + req->ctx.gid = in->gid; + req->ctx.pid = in->pid; + req->ch = ch; + + /* + * INIT and DESTROY requests are serialized, all other request types + * run in parallel. This prevents races between FUSE_INIT and ordinary + * requests, FUSE_INIT and FUSE_INIT, FUSE_INIT and FUSE_DESTROY, and + * FUSE_DESTROY and FUSE_DESTROY. + */ + if (in->opcode == FUSE_INIT || in->opcode == CUSE_INIT || + in->opcode == FUSE_DESTROY) { + pthread_rwlock_wrlock(&se->init_rwlock); + } else { + pthread_rwlock_rdlock(&se->init_rwlock); + } + + err = EIO; + if (!se->got_init) { + enum fuse_opcode expected; + + expected = se->cuse_data ? CUSE_INIT : FUSE_INIT; + if (in->opcode != expected) { + goto reply_err; + } + } else if (in->opcode == FUSE_INIT || in->opcode == CUSE_INIT) { + if (fuse_lowlevel_is_virtio(se)) { + /* + * TODO: This is after a hard reboot typically, we need to do + * a destroy, but we can't reply to this request yet so + * we can't use do_destroy + */ + fuse_log(FUSE_LOG_DEBUG, "%s: reinit\n", __func__); + se->got_destroy = 1; + se->got_init = 0; + if (se->op.destroy) { + se->op.destroy(se->userdata); + } + } else { + goto reply_err; + } + } + + err = EACCES; + /* Implement -o allow_root */ + if (se->deny_others && in->uid != se->owner && in->uid != 0 && + in->opcode != FUSE_INIT && in->opcode != FUSE_READ && + in->opcode != FUSE_WRITE && in->opcode != FUSE_FSYNC && + in->opcode != FUSE_RELEASE && in->opcode != FUSE_READDIR && + in->opcode != FUSE_FSYNCDIR && in->opcode != FUSE_RELEASEDIR && + in->opcode != FUSE_NOTIFY_REPLY && in->opcode != FUSE_READDIRPLUS) { + goto reply_err; + } + + err = ENOSYS; + if (in->opcode >= FUSE_MAXOP || !fuse_ll_ops[in->opcode].func) { + goto reply_err; + } + if (in->opcode != FUSE_INTERRUPT) { + struct fuse_req *intr; + pthread_mutex_lock(&se->lock); + intr = check_interrupt(se, req); + list_add_req(req, &se->list); + pthread_mutex_unlock(&se->lock); + if (intr) { + fuse_reply_err(intr, EAGAIN); + } + } + + if (in->opcode == FUSE_WRITE && se->op.write_buf) { + do_write_buf(req, in->nodeid, &iter, bufv); + } else { + fuse_ll_ops[in->opcode].func(req, in->nodeid, &iter); + } + + pthread_rwlock_unlock(&se->init_rwlock); + return; + +reply_err: + fuse_reply_err(req, err); + pthread_rwlock_unlock(&se->init_rwlock); +} + +#define LL_OPTION(n, o, v) \ + { \ + n, offsetof(struct fuse_session, o), v \ + } + +static const struct fuse_opt fuse_ll_opts[] = { + LL_OPTION("debug", debug, 1), + LL_OPTION("-d", debug, 1), + LL_OPTION("--debug", debug, 1), + LL_OPTION("allow_root", deny_others, 1), + LL_OPTION("--socket-path=%s", vu_socket_path, 0), + LL_OPTION("--fd=%d", vu_listen_fd, 0), + LL_OPTION("--thread-pool-size=%d", thread_pool_size, 0), + FUSE_OPT_END +}; + +void fuse_lowlevel_version(void) +{ + printf("using FUSE kernel interface version %i.%i\n", FUSE_KERNEL_VERSION, + FUSE_KERNEL_MINOR_VERSION); +} + +void fuse_lowlevel_help(void) +{ + /* + * These are not all options, but the ones that are + * potentially of interest to an end-user + */ + printf( + " -o allow_root allow access by root\n" + " --socket-path=PATH path for the vhost-user socket\n" + " --fd=FDNUM fd number of vhost-user socket\n" + " --thread-pool-size=NUM thread pool size limit (default %d)\n", + THREAD_POOL_SIZE); +} + +void fuse_session_destroy(struct fuse_session *se) +{ + if (se->got_init && !se->got_destroy) { + if (se->op.destroy) { + se->op.destroy(se->userdata); + } + } + pthread_rwlock_destroy(&se->init_rwlock); + pthread_mutex_destroy(&se->lock); + free(se->cuse_data); + if (se->fd != -1) { + close(se->fd); + } + + if (fuse_lowlevel_is_virtio(se)) { + virtio_session_close(se); + } + + free(se->vu_socket_path); + se->vu_socket_path = NULL; + + free(se); +} + + +struct fuse_session *fuse_session_new(struct fuse_args *args, + const struct fuse_lowlevel_ops *op, + size_t op_size, void *userdata) +{ + struct fuse_session *se; + + if (sizeof(struct fuse_lowlevel_ops) < op_size) { + fuse_log( + FUSE_LOG_ERR, + "fuse: warning: library too old, some operations may not work\n"); + op_size = sizeof(struct fuse_lowlevel_ops); + } + + if (args->argc == 0) { + fuse_log(FUSE_LOG_ERR, + "fuse: empty argv passed to fuse_session_new().\n"); + return NULL; + } + + se = (struct fuse_session *)calloc(1, sizeof(struct fuse_session)); + if (se == NULL) { + fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate fuse object\n"); + goto out1; + } + se->fd = -1; + se->vu_listen_fd = -1; + se->thread_pool_size = THREAD_POOL_SIZE; + se->conn.max_write = UINT_MAX; + se->conn.max_readahead = UINT_MAX; + + /* Parse options */ + if (fuse_opt_parse(args, se, fuse_ll_opts, NULL) == -1) { + goto out2; + } + if (args->argc == 1 && args->argv[0][0] == '-') { + fuse_log(FUSE_LOG_ERR, + "fuse: warning: argv[0] looks like an option, but " + "will be ignored\n"); + } else if (args->argc != 1) { + int i; + fuse_log(FUSE_LOG_ERR, "fuse: unknown option(s): `"); + for (i = 1; i < args->argc - 1; i++) { + fuse_log(FUSE_LOG_ERR, "%s ", args->argv[i]); + } + fuse_log(FUSE_LOG_ERR, "%s'\n", args->argv[i]); + goto out4; + } + + if (!se->vu_socket_path && se->vu_listen_fd < 0) { + fuse_log(FUSE_LOG_ERR, "fuse: missing --socket-path or --fd option\n"); + goto out4; + } + if (se->vu_socket_path && se->vu_listen_fd >= 0) { + fuse_log(FUSE_LOG_ERR, + "fuse: --socket-path and --fd cannot be given together\n"); + goto out4; + } + + se->bufsize = FUSE_MAX_MAX_PAGES * getpagesize() + FUSE_BUFFER_HEADER_SIZE; + + list_init_req(&se->list); + list_init_req(&se->interrupts); + fuse_mutex_init(&se->lock); + pthread_rwlock_init(&se->init_rwlock, NULL); + + memcpy(&se->op, op, op_size); + se->owner = getuid(); + se->userdata = userdata; + + return se; + +out4: + fuse_opt_free_args(args); +out2: + free(se); +out1: + return NULL; +} + +int fuse_session_mount(struct fuse_session *se) +{ + return virtio_session_mount(se); +} + +int fuse_session_fd(struct fuse_session *se) +{ + return se->fd; +} + +void fuse_session_unmount(struct fuse_session *se) +{ +} + +int fuse_lowlevel_is_virtio(struct fuse_session *se) +{ + return !!se->virtio_dev; +} + +void fuse_session_exit(struct fuse_session *se) +{ + se->exited = 1; +} + +void fuse_session_reset(struct fuse_session *se) +{ + se->exited = 0; + se->error = 0; +} + +int fuse_session_exited(struct fuse_session *se) +{ + return se->exited; +} diff --git a/tools/virtiofsd/fuse_lowlevel.h b/tools/virtiofsd/fuse_lowlevel.h new file mode 100644 index 0000000000..8f6d705b5c --- /dev/null +++ b/tools/virtiofsd/fuse_lowlevel.h @@ -0,0 +1,1970 @@ +/* + * FUSE: Filesystem in Userspace + * Copyright (C) 2001-2007 Miklos Szeredi + * + * This program can be distributed under the terms of the GNU LGPLv2. + * See the file COPYING.LIB. + */ + +#ifndef FUSE_LOWLEVEL_H_ +#define FUSE_LOWLEVEL_H_ + +/** + * @file + * + * Low level API + * + * IMPORTANT: you should define FUSE_USE_VERSION before including this + * header. To use the newest API define it to 31 (recommended for any + * new application). + */ + +#ifndef FUSE_USE_VERSION +#error FUSE_USE_VERSION not defined +#endif + +#include "fuse_common.h" + +#include +#include +#include +#include +#include +#include + +/* + * Miscellaneous definitions + */ + +/** The node ID of the root inode */ +#define FUSE_ROOT_ID 1 + +/** Inode number type */ +typedef uint64_t fuse_ino_t; + +/** Request pointer type */ +typedef struct fuse_req *fuse_req_t; + +/** + * Session + * + * This provides hooks for processing requests, and exiting + */ +struct fuse_session; + +/** Directory entry parameters supplied to fuse_reply_entry() */ +struct fuse_entry_param { + /** + * Unique inode number + * + * In lookup, zero means negative entry (from version 2.5) + * Returning ENOENT also means negative entry, but by setting zero + * ino the kernel may cache negative entries for entry_timeout + * seconds. + */ + fuse_ino_t ino; + + /** + * Generation number for this entry. + * + * If the file system will be exported over NFS, the + * ino/generation pairs need to be unique over the file + * system's lifetime (rather than just the mount time). So if + * the file system reuses an inode after it has been deleted, + * it must assign a new, previously unused generation number + * to the inode at the same time. + * + */ + uint64_t generation; + + /** + * Inode attributes. + * + * Even if attr_timeout == 0, attr must be correct. For example, + * for open(), FUSE uses attr.st_size from lookup() to determine + * how many bytes to request. If this value is not correct, + * incorrect data will be returned. + */ + struct stat attr; + + /** + * Validity timeout (in seconds) for inode attributes. If + * attributes only change as a result of requests that come + * through the kernel, this should be set to a very large + * value. + */ + double attr_timeout; + + /** + * Validity timeout (in seconds) for the name. If directory + * entries are changed/deleted only as a result of requests + * that come through the kernel, this should be set to a very + * large value. + */ + double entry_timeout; +}; + +/** + * Additional context associated with requests. + * + * Note that the reported client uid, gid and pid may be zero in some + * situations. For example, if the FUSE file system is running in a + * PID or user namespace but then accessed from outside the namespace, + * there is no valid uid/pid/gid that could be reported. + */ +struct fuse_ctx { + /** User ID of the calling process */ + uid_t uid; + + /** Group ID of the calling process */ + gid_t gid; + + /** Thread ID of the calling process */ + pid_t pid; + + /** Umask of the calling process */ + mode_t umask; +}; + +struct fuse_forget_data { + fuse_ino_t ino; + uint64_t nlookup; +}; + +/* 'to_set' flags in setattr */ +#define FUSE_SET_ATTR_MODE (1 << 0) +#define FUSE_SET_ATTR_UID (1 << 1) +#define FUSE_SET_ATTR_GID (1 << 2) +#define FUSE_SET_ATTR_SIZE (1 << 3) +#define FUSE_SET_ATTR_ATIME (1 << 4) +#define FUSE_SET_ATTR_MTIME (1 << 5) +#define FUSE_SET_ATTR_ATIME_NOW (1 << 7) +#define FUSE_SET_ATTR_MTIME_NOW (1 << 8) +#define FUSE_SET_ATTR_CTIME (1 << 10) + +/* + * Request methods and replies + */ + +/** + * Low level filesystem operations + * + * Most of the methods (with the exception of init and destroy) + * receive a request handle (fuse_req_t) as their first argument. + * This handle must be passed to one of the specified reply functions. + * + * This may be done inside the method invocation, or after the call + * has returned. The request handle is valid until one of the reply + * functions is called. + * + * Other pointer arguments (name, fuse_file_info, etc) are not valid + * after the call has returned, so if they are needed later, their + * contents have to be copied. + * + * In general, all methods are expected to perform any necessary + * permission checking. However, a filesystem may delegate this task + * to the kernel by passing the `default_permissions` mount option to + * `fuse_session_new()`. In this case, methods will only be called if + * the kernel's permission check has succeeded. + * + * The filesystem sometimes needs to handle a return value of -ENOENT + * from the reply function, which means, that the request was + * interrupted, and the reply discarded. For example if + * fuse_reply_open() return -ENOENT means, that the release method for + * this file will not be called. + */ +struct fuse_lowlevel_ops { + /** + * Initialize filesystem + * + * This function is called when libfuse establishes + * communication with the FUSE kernel module. The file system + * should use this module to inspect and/or modify the + * connection parameters provided in the `conn` structure. + * + * Note that some parameters may be overwritten by options + * passed to fuse_session_new() which take precedence over the + * values set in this handler. + * + * There's no reply to this function + * + * @param userdata the user data passed to fuse_session_new() + */ + void (*init)(void *userdata, struct fuse_conn_info *conn); + + /** + * Clean up filesystem. + * + * Called on filesystem exit. When this method is called, the + * connection to the kernel may be gone already, so that eg. calls + * to fuse_lowlevel_notify_* will fail. + * + * There's no reply to this function + * + * @param userdata the user data passed to fuse_session_new() + */ + void (*destroy)(void *userdata); + + /** + * Look up a directory entry by name and get its attributes. + * + * Valid replies: + * fuse_reply_entry + * fuse_reply_err + * + * @param req request handle + * @param parent inode number of the parent directory + * @param name the name to look up + */ + void (*lookup)(fuse_req_t req, fuse_ino_t parent, const char *name); + + /** + * Forget about an inode + * + * This function is called when the kernel removes an inode + * from its internal caches. + * + * The inode's lookup count increases by one for every call to + * fuse_reply_entry and fuse_reply_create. The nlookup parameter + * indicates by how much the lookup count should be decreased. + * + * Inodes with a non-zero lookup count may receive request from + * the kernel even after calls to unlink, rmdir or (when + * overwriting an existing file) rename. Filesystems must handle + * such requests properly and it is recommended to defer removal + * of the inode until the lookup count reaches zero. Calls to + * unlink, rmdir or rename will be followed closely by forget + * unless the file or directory is open, in which case the + * kernel issues forget only after the release or releasedir + * calls. + * + * Note that if a file system will be exported over NFS the + * inodes lifetime must extend even beyond forget. See the + * generation field in struct fuse_entry_param above. + * + * On unmount the lookup count for all inodes implicitly drops + * to zero. It is not guaranteed that the file system will + * receive corresponding forget messages for the affected + * inodes. + * + * Valid replies: + * fuse_reply_none + * + * @param req request handle + * @param ino the inode number + * @param nlookup the number of lookups to forget + */ + void (*forget)(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup); + + /** + * Get file attributes. + * + * If writeback caching is enabled, the kernel may have a + * better idea of a file's length than the FUSE file system + * (eg if there has been a write that extended the file size, + * but that has not yet been passed to the filesystem.n + * + * In this case, the st_size value provided by the file system + * will be ignored. + * + * Valid replies: + * fuse_reply_attr + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param fi for future use, currently always NULL + */ + void (*getattr)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi); + + /** + * Set file attributes + * + * In the 'attr' argument only members indicated by the 'to_set' + * bitmask contain valid values. Other members contain undefined + * values. + * + * Unless FUSE_CAP_HANDLE_KILLPRIV is disabled, this method is + * expected to reset the setuid and setgid bits if the file + * size or owner is being changed. + * + * If the setattr was invoked from the ftruncate() system call + * under Linux kernel versions 2.6.15 or later, the fi->fh will + * contain the value set by the open method or will be undefined + * if the open method didn't set any value. Otherwise (not + * ftruncate call, or kernel version earlier than 2.6.15) the fi + * parameter will be NULL. + * + * Valid replies: + * fuse_reply_attr + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param attr the attributes + * @param to_set bit mask of attributes which should be set + * @param fi file information, or NULL + */ + void (*setattr)(fuse_req_t req, fuse_ino_t ino, struct stat *attr, + int to_set, struct fuse_file_info *fi); + + /** + * Read symbolic link + * + * Valid replies: + * fuse_reply_readlink + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + */ + void (*readlink)(fuse_req_t req, fuse_ino_t ino); + + /** + * Create file node + * + * Create a regular file, character device, block device, fifo or + * socket node. + * + * Valid replies: + * fuse_reply_entry + * fuse_reply_err + * + * @param req request handle + * @param parent inode number of the parent directory + * @param name to create + * @param mode file type and mode with which to create the new file + * @param rdev the device number (only valid if created file is a device) + */ + void (*mknod)(fuse_req_t req, fuse_ino_t parent, const char *name, + mode_t mode, dev_t rdev); + + /** + * Create a directory + * + * Valid replies: + * fuse_reply_entry + * fuse_reply_err + * + * @param req request handle + * @param parent inode number of the parent directory + * @param name to create + * @param mode with which to create the new file + */ + void (*mkdir)(fuse_req_t req, fuse_ino_t parent, const char *name, + mode_t mode); + + /** + * Remove a file + * + * If the file's inode's lookup count is non-zero, the file + * system is expected to postpone any removal of the inode + * until the lookup count reaches zero (see description of the + * forget function). + * + * Valid replies: + * fuse_reply_err + * + * @param req request handle + * @param parent inode number of the parent directory + * @param name to remove + */ + void (*unlink)(fuse_req_t req, fuse_ino_t parent, const char *name); + + /** + * Remove a directory + * + * If the directory's inode's lookup count is non-zero, the + * file system is expected to postpone any removal of the + * inode until the lookup count reaches zero (see description + * of the forget function). + * + * Valid replies: + * fuse_reply_err + * + * @param req request handle + * @param parent inode number of the parent directory + * @param name to remove + */ + void (*rmdir)(fuse_req_t req, fuse_ino_t parent, const char *name); + + /** + * Create a symbolic link + * + * Valid replies: + * fuse_reply_entry + * fuse_reply_err + * + * @param req request handle + * @param link the contents of the symbolic link + * @param parent inode number of the parent directory + * @param name to create + */ + void (*symlink)(fuse_req_t req, const char *link, fuse_ino_t parent, + const char *name); + + /** + * Rename a file + * + * If the target exists it should be atomically replaced. If + * the target's inode's lookup count is non-zero, the file + * system is expected to postpone any removal of the inode + * until the lookup count reaches zero (see description of the + * forget function). + * + * If this request is answered with an error code of ENOSYS, this is + * treated as a permanent failure with error code EINVAL, i.e. all + * future bmap requests will fail with EINVAL without being + * send to the filesystem process. + * + * *flags* may be `RENAME_EXCHANGE` or `RENAME_NOREPLACE`. If + * RENAME_NOREPLACE is specified, the filesystem must not + * overwrite *newname* if it exists and return an error + * instead. If `RENAME_EXCHANGE` is specified, the filesystem + * must atomically exchange the two files, i.e. both must + * exist and neither may be deleted. + * + * Valid replies: + * fuse_reply_err + * + * @param req request handle + * @param parent inode number of the old parent directory + * @param name old name + * @param newparent inode number of the new parent directory + * @param newname new name + */ + void (*rename)(fuse_req_t req, fuse_ino_t parent, const char *name, + fuse_ino_t newparent, const char *newname, + unsigned int flags); + + /** + * Create a hard link + * + * Valid replies: + * fuse_reply_entry + * fuse_reply_err + * + * @param req request handle + * @param ino the old inode number + * @param newparent inode number of the new parent directory + * @param newname new name to create + */ + void (*link)(fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent, + const char *newname); + + /** + * Open a file + * + * Open flags are available in fi->flags. The following rules + * apply. + * + * - Creation (O_CREAT, O_EXCL, O_NOCTTY) flags will be + * filtered out / handled by the kernel. + * + * - Access modes (O_RDONLY, O_WRONLY, O_RDWR) should be used + * by the filesystem to check if the operation is + * permitted. If the ``-o default_permissions`` mount + * option is given, this check is already done by the + * kernel before calling open() and may thus be omitted by + * the filesystem. + * + * - When writeback caching is enabled, the kernel may send + * read requests even for files opened with O_WRONLY. The + * filesystem should be prepared to handle this. + * + * - When writeback caching is disabled, the filesystem is + * expected to properly handle the O_APPEND flag and ensure + * that each write is appending to the end of the file. + * + * - When writeback caching is enabled, the kernel will + * handle O_APPEND. However, unless all changes to the file + * come through the kernel this will not work reliably. The + * filesystem should thus either ignore the O_APPEND flag + * (and let the kernel handle it), or return an error + * (indicating that reliably O_APPEND is not available). + * + * Filesystem may store an arbitrary file handle (pointer, + * index, etc) in fi->fh, and use this in other all other file + * operations (read, write, flush, release, fsync). + * + * Filesystem may also implement stateless file I/O and not store + * anything in fi->fh. + * + * There are also some flags (direct_io, keep_cache) which the + * filesystem may set in fi, to change the way the file is opened. + * See fuse_file_info structure in for more details. + * + * If this request is answered with an error code of ENOSYS + * and FUSE_CAP_NO_OPEN_SUPPORT is set in + * `fuse_conn_info.capable`, this is treated as success and + * future calls to open and release will also succeed without being + * sent to the filesystem process. + * + * Valid replies: + * fuse_reply_open + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param fi file information + */ + void (*open)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi); + + /** + * Read data + * + * Read should send exactly the number of bytes requested except + * on EOF or error, otherwise the rest of the data will be + * substituted with zeroes. An exception to this is when the file + * has been opened in 'direct_io' mode, in which case the return + * value of the read system call will reflect the return value of + * this operation. + * + * fi->fh will contain the value set by the open method, or will + * be undefined if the open method didn't set any value. + * + * Valid replies: + * fuse_reply_buf + * fuse_reply_iov + * fuse_reply_data + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param size number of bytes to read + * @param off offset to read from + * @param fi file information + */ + void (*read)(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, + struct fuse_file_info *fi); + + /** + * Write data + * + * Write should return exactly the number of bytes requested + * except on error. An exception to this is when the file has + * been opened in 'direct_io' mode, in which case the return value + * of the write system call will reflect the return value of this + * operation. + * + * Unless FUSE_CAP_HANDLE_KILLPRIV is disabled, this method is + * expected to reset the setuid and setgid bits. + * + * fi->fh will contain the value set by the open method, or will + * be undefined if the open method didn't set any value. + * + * Valid replies: + * fuse_reply_write + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param buf data to write + * @param size number of bytes to write + * @param off offset to write to + * @param fi file information + */ + void (*write)(fuse_req_t req, fuse_ino_t ino, const char *buf, size_t size, + off_t off, struct fuse_file_info *fi); + + /** + * Flush method + * + * This is called on each close() of the opened file. + * + * Since file descriptors can be duplicated (dup, dup2, fork), for + * one open call there may be many flush calls. + * + * Filesystems shouldn't assume that flush will always be called + * after some writes, or that if will be called at all. + * + * fi->fh will contain the value set by the open method, or will + * be undefined if the open method didn't set any value. + * + * NOTE: the name of the method is misleading, since (unlike + * fsync) the filesystem is not forced to flush pending writes. + * One reason to flush data is if the filesystem wants to return + * write errors during close. However, such use is non-portable + * because POSIX does not require [close] to wait for delayed I/O to + * complete. + * + * If the filesystem supports file locking operations (setlk, + * getlk) it should remove all locks belonging to 'fi->owner'. + * + * If this request is answered with an error code of ENOSYS, + * this is treated as success and future calls to flush() will + * succeed automatically without being send to the filesystem + * process. + * + * Valid replies: + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param fi file information + * + * [close]: + * http://pubs.opengroup.org/onlinepubs/9699919799/functions/close.html + */ + void (*flush)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi); + + /** + * Release an open file + * + * Release is called when there are no more references to an open + * file: all file descriptors are closed and all memory mappings + * are unmapped. + * + * For every open call there will be exactly one release call (unless + * the filesystem is force-unmounted). + * + * The filesystem may reply with an error, but error values are + * not returned to close() or munmap() which triggered the + * release. + * + * fi->fh will contain the value set by the open method, or will + * be undefined if the open method didn't set any value. + * fi->flags will contain the same flags as for open. + * + * Valid replies: + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param fi file information + */ + void (*release)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi); + + /** + * Synchronize file contents + * + * If the datasync parameter is non-zero, then only the user data + * should be flushed, not the meta data. + * + * If this request is answered with an error code of ENOSYS, + * this is treated as success and future calls to fsync() will + * succeed automatically without being send to the filesystem + * process. + * + * Valid replies: + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param datasync flag indicating if only data should be flushed + * @param fi file information + */ + void (*fsync)(fuse_req_t req, fuse_ino_t ino, int datasync, + struct fuse_file_info *fi); + + /** + * Open a directory + * + * Filesystem may store an arbitrary file handle (pointer, index, + * etc) in fi->fh, and use this in other all other directory + * stream operations (readdir, releasedir, fsyncdir). + * + * If this request is answered with an error code of ENOSYS and + * FUSE_CAP_NO_OPENDIR_SUPPORT is set in `fuse_conn_info.capable`, + * this is treated as success and future calls to opendir and + * releasedir will also succeed without being sent to the filesystem + * process. In addition, the kernel will cache readdir results + * as if opendir returned FOPEN_KEEP_CACHE | FOPEN_CACHE_DIR. + * + * Valid replies: + * fuse_reply_open + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param fi file information + */ + void (*opendir)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi); + + /** + * Read directory + * + * Send a buffer filled using fuse_add_direntry(), with size not + * exceeding the requested size. Send an empty buffer on end of + * stream. + * + * fi->fh will contain the value set by the opendir method, or + * will be undefined if the opendir method didn't set any value. + * + * Returning a directory entry from readdir() does not affect + * its lookup count. + * + * If off_t is non-zero, then it will correspond to one of the off_t + * values that was previously returned by readdir() for the same + * directory handle. In this case, readdir() should skip over entries + * coming before the position defined by the off_t value. If entries + * are added or removed while the directory handle is open, they filesystem + * may still include the entries that have been removed, and may not + * report the entries that have been created. However, addition or + * removal of entries must never cause readdir() to skip over unrelated + * entries or to report them more than once. This means + * that off_t can not be a simple index that enumerates the entries + * that have been returned but must contain sufficient information to + * uniquely determine the next directory entry to return even when the + * set of entries is changing. + * + * The function does not have to report the '.' and '..' + * entries, but is allowed to do so. Note that, if readdir does + * not return '.' or '..', they will not be implicitly returned, + * and this behavior is observable by the caller. + * + * Valid replies: + * fuse_reply_buf + * fuse_reply_data + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param size maximum number of bytes to send + * @param off offset to continue reading the directory stream + * @param fi file information + */ + void (*readdir)(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, + struct fuse_file_info *fi); + + /** + * Release an open directory + * + * For every opendir call there will be exactly one releasedir + * call (unless the filesystem is force-unmounted). + * + * fi->fh will contain the value set by the opendir method, or + * will be undefined if the opendir method didn't set any value. + * + * Valid replies: + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param fi file information + */ + void (*releasedir)(fuse_req_t req, fuse_ino_t ino, + struct fuse_file_info *fi); + + /** + * Synchronize directory contents + * + * If the datasync parameter is non-zero, then only the directory + * contents should be flushed, not the meta data. + * + * fi->fh will contain the value set by the opendir method, or + * will be undefined if the opendir method didn't set any value. + * + * If this request is answered with an error code of ENOSYS, + * this is treated as success and future calls to fsyncdir() will + * succeed automatically without being send to the filesystem + * process. + * + * Valid replies: + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param datasync flag indicating if only data should be flushed + * @param fi file information + */ + void (*fsyncdir)(fuse_req_t req, fuse_ino_t ino, int datasync, + struct fuse_file_info *fi); + + /** + * Get file system statistics + * + * Valid replies: + * fuse_reply_statfs + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number, zero means "undefined" + */ + void (*statfs)(fuse_req_t req, fuse_ino_t ino); + + /** + * Set an extended attribute + * + * If this request is answered with an error code of ENOSYS, this is + * treated as a permanent failure with error code EOPNOTSUPP, i.e. all + * future setxattr() requests will fail with EOPNOTSUPP without being + * send to the filesystem process. + * + * Valid replies: + * fuse_reply_err + */ + void (*setxattr)(fuse_req_t req, fuse_ino_t ino, const char *name, + const char *value, size_t size, int flags); + + /** + * Get an extended attribute + * + * If size is zero, the size of the value should be sent with + * fuse_reply_xattr. + * + * If the size is non-zero, and the value fits in the buffer, the + * value should be sent with fuse_reply_buf. + * + * If the size is too small for the value, the ERANGE error should + * be sent. + * + * If this request is answered with an error code of ENOSYS, this is + * treated as a permanent failure with error code EOPNOTSUPP, i.e. all + * future getxattr() requests will fail with EOPNOTSUPP without being + * send to the filesystem process. + * + * Valid replies: + * fuse_reply_buf + * fuse_reply_data + * fuse_reply_xattr + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param name of the extended attribute + * @param size maximum size of the value to send + */ + void (*getxattr)(fuse_req_t req, fuse_ino_t ino, const char *name, + size_t size); + + /** + * List extended attribute names + * + * If size is zero, the total size of the attribute list should be + * sent with fuse_reply_xattr. + * + * If the size is non-zero, and the null character separated + * attribute list fits in the buffer, the list should be sent with + * fuse_reply_buf. + * + * If the size is too small for the list, the ERANGE error should + * be sent. + * + * If this request is answered with an error code of ENOSYS, this is + * treated as a permanent failure with error code EOPNOTSUPP, i.e. all + * future listxattr() requests will fail with EOPNOTSUPP without being + * send to the filesystem process. + * + * Valid replies: + * fuse_reply_buf + * fuse_reply_data + * fuse_reply_xattr + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param size maximum size of the list to send + */ + void (*listxattr)(fuse_req_t req, fuse_ino_t ino, size_t size); + + /** + * Remove an extended attribute + * + * If this request is answered with an error code of ENOSYS, this is + * treated as a permanent failure with error code EOPNOTSUPP, i.e. all + * future removexattr() requests will fail with EOPNOTSUPP without being + * send to the filesystem process. + * + * Valid replies: + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param name of the extended attribute + */ + void (*removexattr)(fuse_req_t req, fuse_ino_t ino, const char *name); + + /** + * Check file access permissions + * + * This will be called for the access() and chdir() system + * calls. If the 'default_permissions' mount option is given, + * this method is not called. + * + * This method is not called under Linux kernel versions 2.4.x + * + * If this request is answered with an error code of ENOSYS, this is + * treated as a permanent success, i.e. this and all future access() + * requests will succeed without being send to the filesystem process. + * + * Valid replies: + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param mask requested access mode + */ + void (*access)(fuse_req_t req, fuse_ino_t ino, int mask); + + /** + * Create and open a file + * + * If the file does not exist, first create it with the specified + * mode, and then open it. + * + * See the description of the open handler for more + * information. + * + * If this method is not implemented or under Linux kernel + * versions earlier than 2.6.15, the mknod() and open() methods + * will be called instead. + * + * If this request is answered with an error code of ENOSYS, the handler + * is treated as not implemented (i.e., for this and future requests the + * mknod() and open() handlers will be called instead). + * + * Valid replies: + * fuse_reply_create + * fuse_reply_err + * + * @param req request handle + * @param parent inode number of the parent directory + * @param name to create + * @param mode file type and mode with which to create the new file + * @param fi file information + */ + void (*create)(fuse_req_t req, fuse_ino_t parent, const char *name, + mode_t mode, struct fuse_file_info *fi); + + /** + * Test for a POSIX file lock + * + * Valid replies: + * fuse_reply_lock + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param fi file information + * @param lock the region/type to test + */ + void (*getlk)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, + struct flock *lock); + + /** + * Acquire, modify or release a POSIX file lock + * + * For POSIX threads (NPTL) there's a 1-1 relation between pid and + * owner, but otherwise this is not always the case. For checking + * lock ownership, 'fi->owner' must be used. The l_pid field in + * 'struct flock' should only be used to fill in this field in + * getlk(). + * + * Note: if the locking methods are not implemented, the kernel + * will still allow file locking to work locally. Hence these are + * only interesting for network filesystems and similar. + * + * Valid replies: + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param fi file information + * @param lock the region/type to set + * @param sleep locking operation may sleep + */ + void (*setlk)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, + struct flock *lock, int sleep); + + /** + * Map block index within file to block index within device + * + * Note: This makes sense only for block device backed filesystems + * mounted with the 'blkdev' option + * + * If this request is answered with an error code of ENOSYS, this is + * treated as a permanent failure, i.e. all future bmap() requests will + * fail with the same error code without being send to the filesystem + * process. + * + * Valid replies: + * fuse_reply_bmap + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param blocksize unit of block index + * @param idx block index within file + */ + void (*bmap)(fuse_req_t req, fuse_ino_t ino, size_t blocksize, + uint64_t idx); + + /** + * Ioctl + * + * Note: For unrestricted ioctls (not allowed for FUSE + * servers), data in and out areas can be discovered by giving + * iovs and setting FUSE_IOCTL_RETRY in *flags*. For + * restricted ioctls, kernel prepares in/out data area + * according to the information encoded in cmd. + * + * Valid replies: + * fuse_reply_ioctl_retry + * fuse_reply_ioctl + * fuse_reply_ioctl_iov + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param cmd ioctl command + * @param arg ioctl argument + * @param fi file information + * @param flags for FUSE_IOCTL_* flags + * @param in_buf data fetched from the caller + * @param in_bufsz number of fetched bytes + * @param out_bufsz maximum size of output data + * + * Note : the unsigned long request submitted by the application + * is truncated to 32 bits. + */ + void (*ioctl)(fuse_req_t req, fuse_ino_t ino, unsigned int cmd, void *arg, + struct fuse_file_info *fi, unsigned flags, const void *in_buf, + size_t in_bufsz, size_t out_bufsz); + + /** + * Poll for IO readiness + * + * Note: If ph is non-NULL, the client should notify + * when IO readiness events occur by calling + * fuse_lowlevel_notify_poll() with the specified ph. + * + * Regardless of the number of times poll with a non-NULL ph + * is received, single notification is enough to clear all. + * Notifying more times incurs overhead but doesn't harm + * correctness. + * + * The callee is responsible for destroying ph with + * fuse_pollhandle_destroy() when no longer in use. + * + * If this request is answered with an error code of ENOSYS, this is + * treated as success (with a kernel-defined default poll-mask) and + * future calls to pull() will succeed the same way without being send + * to the filesystem process. + * + * Valid replies: + * fuse_reply_poll + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param fi file information + * @param ph poll handle to be used for notification + */ + void (*poll)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, + struct fuse_pollhandle *ph); + + /** + * Write data made available in a buffer + * + * This is a more generic version of the ->write() method. If + * FUSE_CAP_SPLICE_READ is set in fuse_conn_info.want and the + * kernel supports splicing from the fuse device, then the + * data will be made available in pipe for supporting zero + * copy data transfer. + * + * buf->count is guaranteed to be one (and thus buf->idx is + * always zero). The write_buf handler must ensure that + * bufv->off is correctly updated (reflecting the number of + * bytes read from bufv->buf[0]). + * + * Unless FUSE_CAP_HANDLE_KILLPRIV is disabled, this method is + * expected to reset the setuid and setgid bits. + * + * Valid replies: + * fuse_reply_write + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param bufv buffer containing the data + * @param off offset to write to + * @param fi file information + */ + void (*write_buf)(fuse_req_t req, fuse_ino_t ino, struct fuse_bufvec *bufv, + off_t off, struct fuse_file_info *fi); + + /** + * Forget about multiple inodes + * + * See description of the forget function for more + * information. + * + * Valid replies: + * fuse_reply_none + * + * @param req request handle + */ + void (*forget_multi)(fuse_req_t req, size_t count, + struct fuse_forget_data *forgets); + + /** + * Acquire, modify or release a BSD file lock + * + * Note: if the locking methods are not implemented, the kernel + * will still allow file locking to work locally. Hence these are + * only interesting for network filesystems and similar. + * + * Valid replies: + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param fi file information + * @param op the locking operation, see flock(2) + */ + void (*flock)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, + int op); + + /** + * Allocate requested space. If this function returns success then + * subsequent writes to the specified range shall not fail due to the lack + * of free space on the file system storage media. + * + * If this request is answered with an error code of ENOSYS, this is + * treated as a permanent failure with error code EOPNOTSUPP, i.e. all + * future fallocate() requests will fail with EOPNOTSUPP without being + * send to the filesystem process. + * + * Valid replies: + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param offset starting point for allocated region + * @param length size of allocated region + * @param mode determines the operation to be performed on the given range, + * see fallocate(2) + */ + void (*fallocate)(fuse_req_t req, fuse_ino_t ino, int mode, off_t offset, + off_t length, struct fuse_file_info *fi); + + /** + * Read directory with attributes + * + * Send a buffer filled using fuse_add_direntry_plus(), with size not + * exceeding the requested size. Send an empty buffer on end of + * stream. + * + * fi->fh will contain the value set by the opendir method, or + * will be undefined if the opendir method didn't set any value. + * + * In contrast to readdir() (which does not affect the lookup counts), + * the lookup count of every entry returned by readdirplus(), except "." + * and "..", is incremented by one. + * + * Valid replies: + * fuse_reply_buf + * fuse_reply_data + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param size maximum number of bytes to send + * @param off offset to continue reading the directory stream + * @param fi file information + */ + void (*readdirplus)(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, + struct fuse_file_info *fi); + + /** + * Copy a range of data from one file to another + * + * Performs an optimized copy between two file descriptors without the + * additional cost of transferring data through the FUSE kernel module + * to user space (glibc) and then back into the FUSE filesystem again. + * + * In case this method is not implemented, glibc falls back to reading + * data from the source and writing to the destination. Effectively + * doing an inefficient copy of the data. + * + * If this request is answered with an error code of ENOSYS, this is + * treated as a permanent failure with error code EOPNOTSUPP, i.e. all + * future copy_file_range() requests will fail with EOPNOTSUPP without + * being send to the filesystem process. + * + * Valid replies: + * fuse_reply_write + * fuse_reply_err + * + * @param req request handle + * @param ino_in the inode number or the source file + * @param off_in starting point from were the data should be read + * @param fi_in file information of the source file + * @param ino_out the inode number or the destination file + * @param off_out starting point where the data should be written + * @param fi_out file information of the destination file + * @param len maximum size of the data to copy + * @param flags passed along with the copy_file_range() syscall + */ + void (*copy_file_range)(fuse_req_t req, fuse_ino_t ino_in, off_t off_in, + struct fuse_file_info *fi_in, fuse_ino_t ino_out, + off_t off_out, struct fuse_file_info *fi_out, + size_t len, int flags); + + /** + * Find next data or hole after the specified offset + * + * If this request is answered with an error code of ENOSYS, this is + * treated as a permanent failure, i.e. all future lseek() requests will + * fail with the same error code without being send to the filesystem + * process. + * + * Valid replies: + * fuse_reply_lseek + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param off offset to start search from + * @param whence either SEEK_DATA or SEEK_HOLE + * @param fi file information + */ + void (*lseek)(fuse_req_t req, fuse_ino_t ino, off_t off, int whence, + struct fuse_file_info *fi); +}; + +/** + * Reply with an error code or success. + * + * Possible requests: + * all except forget + * + * Whereever possible, error codes should be chosen from the list of + * documented error conditions in the corresponding system calls + * manpage. + * + * An error code of ENOSYS is sometimes treated specially. This is + * indicated in the documentation of the affected handler functions. + * + * The following requests may be answered with a zero error code: + * unlink, rmdir, rename, flush, release, fsync, fsyncdir, setxattr, + * removexattr, setlk. + * + * @param req request handle + * @param err the positive error value, or zero for success + * @return zero for success, -errno for failure to send reply + */ +int fuse_reply_err(fuse_req_t req, int err); + +/** + * Don't send reply + * + * Possible requests: + * forget + * forget_multi + * retrieve_reply + * + * @param req request handle + */ +void fuse_reply_none(fuse_req_t req); + +/** + * Reply with a directory entry + * + * Possible requests: + * lookup, mknod, mkdir, symlink, link + * + * Side effects: + * increments the lookup count on success + * + * @param req request handle + * @param e the entry parameters + * @return zero for success, -errno for failure to send reply + */ +int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e); + +/** + * Reply with a directory entry and open parameters + * + * currently the following members of 'fi' are used: + * fh, direct_io, keep_cache + * + * Possible requests: + * create + * + * Side effects: + * increments the lookup count on success + * + * @param req request handle + * @param e the entry parameters + * @param fi file information + * @return zero for success, -errno for failure to send reply + */ +int fuse_reply_create(fuse_req_t req, const struct fuse_entry_param *e, + const struct fuse_file_info *fi); + +/** + * Reply with attributes + * + * Possible requests: + * getattr, setattr + * + * @param req request handle + * @param attr the attributes + * @param attr_timeout validity timeout (in seconds) for the attributes + * @return zero for success, -errno for failure to send reply + */ +int fuse_reply_attr(fuse_req_t req, const struct stat *attr, + double attr_timeout); + +/** + * Reply with the contents of a symbolic link + * + * Possible requests: + * readlink + * + * @param req request handle + * @param link symbolic link contents + * @return zero for success, -errno for failure to send reply + */ +int fuse_reply_readlink(fuse_req_t req, const char *link); + +/** + * Reply with open parameters + * + * currently the following members of 'fi' are used: + * fh, direct_io, keep_cache + * + * Possible requests: + * open, opendir + * + * @param req request handle + * @param fi file information + * @return zero for success, -errno for failure to send reply + */ +int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi); + +/** + * Reply with number of bytes written + * + * Possible requests: + * write + * + * @param req request handle + * @param count the number of bytes written + * @return zero for success, -errno for failure to send reply + */ +int fuse_reply_write(fuse_req_t req, size_t count); + +/** + * Reply with data + * + * Possible requests: + * read, readdir, getxattr, listxattr + * + * @param req request handle + * @param buf buffer containing data + * @param size the size of data in bytes + * @return zero for success, -errno for failure to send reply + */ +int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size); + +/** + * Reply with data copied/moved from buffer(s) + * + * Possible requests: + * read, readdir, getxattr, listxattr + * + * Side effects: + * when used to return data from a readdirplus() (but not readdir()) + * call, increments the lookup count of each returned entry by one + * on success. + * + * @param req request handle + * @param bufv buffer vector + * @return zero for success, -errno for failure to send reply + */ +int fuse_reply_data(fuse_req_t req, struct fuse_bufvec *bufv); + +/** + * Reply with data vector + * + * Possible requests: + * read, readdir, getxattr, listxattr + * + * @param req request handle + * @param iov the vector containing the data + * @param count the size of vector + * @return zero for success, -errno for failure to send reply + */ +int fuse_reply_iov(fuse_req_t req, const struct iovec *iov, int count); + +/** + * Reply with filesystem statistics + * + * Possible requests: + * statfs + * + * @param req request handle + * @param stbuf filesystem statistics + * @return zero for success, -errno for failure to send reply + */ +int fuse_reply_statfs(fuse_req_t req, const struct statvfs *stbuf); + +/** + * Reply with needed buffer size + * + * Possible requests: + * getxattr, listxattr + * + * @param req request handle + * @param count the buffer size needed in bytes + * @return zero for success, -errno for failure to send reply + */ +int fuse_reply_xattr(fuse_req_t req, size_t count); + +/** + * Reply with file lock information + * + * Possible requests: + * getlk + * + * @param req request handle + * @param lock the lock information + * @return zero for success, -errno for failure to send reply + */ +int fuse_reply_lock(fuse_req_t req, const struct flock *lock); + +/** + * Reply with block index + * + * Possible requests: + * bmap + * + * @param req request handle + * @param idx block index within device + * @return zero for success, -errno for failure to send reply + */ +int fuse_reply_bmap(fuse_req_t req, uint64_t idx); + +/* + * Filling a buffer in readdir + */ + +/** + * Add a directory entry to the buffer + * + * Buffer needs to be large enough to hold the entry. If it's not, + * then the entry is not filled in but the size of the entry is still + * returned. The caller can check this by comparing the bufsize + * parameter with the returned entry size. If the entry size is + * larger than the buffer size, the operation failed. + * + * From the 'stbuf' argument the st_ino field and bits 12-15 of the + * st_mode field are used. The other fields are ignored. + * + * *off* should be any non-zero value that the filesystem can use to + * identify the current point in the directory stream. It does not + * need to be the actual physical position. A value of zero is + * reserved to mean "from the beginning", and should therefore never + * be used (the first call to fuse_add_direntry should be passed the + * offset of the second directory entry). + * + * @param req request handle + * @param buf the point where the new entry will be added to the buffer + * @param bufsize remaining size of the buffer + * @param name the name of the entry + * @param stbuf the file attributes + * @param off the offset of the next entry + * @return the space needed for the entry + */ +size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize, + const char *name, const struct stat *stbuf, off_t off); + +/** + * Add a directory entry to the buffer with the attributes + * + * See documentation of `fuse_add_direntry()` for more details. + * + * @param req request handle + * @param buf the point where the new entry will be added to the buffer + * @param bufsize remaining size of the buffer + * @param name the name of the entry + * @param e the directory entry + * @param off the offset of the next entry + * @return the space needed for the entry + */ +size_t fuse_add_direntry_plus(fuse_req_t req, char *buf, size_t bufsize, + const char *name, + const struct fuse_entry_param *e, off_t off); + +/** + * Reply to ask for data fetch and output buffer preparation. ioctl + * will be retried with the specified input data fetched and output + * buffer prepared. + * + * Possible requests: + * ioctl + * + * @param req request handle + * @param in_iov iovec specifying data to fetch from the caller + * @param in_count number of entries in in_iov + * @param out_iov iovec specifying addresses to write output to + * @param out_count number of entries in out_iov + * @return zero for success, -errno for failure to send reply + */ +int fuse_reply_ioctl_retry(fuse_req_t req, const struct iovec *in_iov, + size_t in_count, const struct iovec *out_iov, + size_t out_count); + +/** + * Reply to finish ioctl + * + * Possible requests: + * ioctl + * + * @param req request handle + * @param result result to be passed to the caller + * @param buf buffer containing output data + * @param size length of output data + */ +int fuse_reply_ioctl(fuse_req_t req, int result, const void *buf, size_t size); + +/** + * Reply to finish ioctl with iov buffer + * + * Possible requests: + * ioctl + * + * @param req request handle + * @param result result to be passed to the caller + * @param iov the vector containing the data + * @param count the size of vector + */ +int fuse_reply_ioctl_iov(fuse_req_t req, int result, const struct iovec *iov, + int count); + +/** + * Reply with poll result event mask + * + * @param req request handle + * @param revents poll result event mask + */ +int fuse_reply_poll(fuse_req_t req, unsigned revents); + +/** + * Reply with offset + * + * Possible requests: + * lseek + * + * @param req request handle + * @param off offset of next data or hole + * @return zero for success, -errno for failure to send reply + */ +int fuse_reply_lseek(fuse_req_t req, off_t off); + +/* + * Notification + */ + +/** + * Notify IO readiness event + * + * For more information, please read comment for poll operation. + * + * @param ph poll handle to notify IO readiness event for + */ +int fuse_lowlevel_notify_poll(struct fuse_pollhandle *ph); + +/** + * Notify to invalidate cache for an inode. + * + * Added in FUSE protocol version 7.12. If the kernel does not support + * this (or a newer) version, the function will return -ENOSYS and do + * nothing. + * + * If the filesystem has writeback caching enabled, invalidating an + * inode will first trigger a writeback of all dirty pages. The call + * will block until all writeback requests have completed and the + * inode has been invalidated. It will, however, not wait for + * completion of pending writeback requests that have been issued + * before. + * + * If there are no dirty pages, this function will never block. + * + * @param se the session object + * @param ino the inode number + * @param off the offset in the inode where to start invalidating + * or negative to invalidate attributes only + * @param len the amount of cache to invalidate or 0 for all + * @return zero for success, -errno for failure + */ +int fuse_lowlevel_notify_inval_inode(struct fuse_session *se, fuse_ino_t ino, + off_t off, off_t len); + +/** + * Notify to invalidate parent attributes and the dentry matching + * parent/name + * + * To avoid a deadlock this function must not be called in the + * execution path of a related filesytem operation or within any code + * that could hold a lock that could be needed to execute such an + * operation. As of kernel 4.18, a "related operation" is a lookup(), + * symlink(), mknod(), mkdir(), unlink(), rename(), link() or create() + * request for the parent, and a setattr(), unlink(), rmdir(), + * rename(), setxattr(), removexattr(), readdir() or readdirplus() + * request for the inode itself. + * + * When called correctly, this function will never block. + * + * Added in FUSE protocol version 7.12. If the kernel does not support + * this (or a newer) version, the function will return -ENOSYS and do + * nothing. + * + * @param se the session object + * @param parent inode number + * @param name file name + * @param namelen strlen() of file name + * @return zero for success, -errno for failure + */ +int fuse_lowlevel_notify_inval_entry(struct fuse_session *se, fuse_ino_t parent, + const char *name, size_t namelen); + +/** + * This function behaves like fuse_lowlevel_notify_inval_entry() with + * the following additional effect (at least as of Linux kernel 4.8): + * + * If the provided *child* inode matches the inode that is currently + * associated with the cached dentry, and if there are any inotify + * watches registered for the dentry, then the watchers are informed + * that the dentry has been deleted. + * + * To avoid a deadlock this function must not be called while + * executing a related filesytem operation or while holding a lock + * that could be needed to execute such an operation (see the + * description of fuse_lowlevel_notify_inval_entry() for more + * details). + * + * When called correctly, this function will never block. + * + * Added in FUSE protocol version 7.18. If the kernel does not support + * this (or a newer) version, the function will return -ENOSYS and do + * nothing. + * + * @param se the session object + * @param parent inode number + * @param child inode number + * @param name file name + * @param namelen strlen() of file name + * @return zero for success, -errno for failure + */ +int fuse_lowlevel_notify_delete(struct fuse_session *se, fuse_ino_t parent, + fuse_ino_t child, const char *name, + size_t namelen); + +/** + * Store data to the kernel buffers + * + * Synchronously store data in the kernel buffers belonging to the + * given inode. The stored data is marked up-to-date (no read will be + * performed against it, unless it's invalidated or evicted from the + * cache). + * + * If the stored data overflows the current file size, then the size + * is extended, similarly to a write(2) on the filesystem. + * + * If this function returns an error, then the store wasn't fully + * completed, but it may have been partially completed. + * + * Added in FUSE protocol version 7.15. If the kernel does not support + * this (or a newer) version, the function will return -ENOSYS and do + * nothing. + * + * @param se the session object + * @param ino the inode number + * @param offset the starting offset into the file to store to + * @param bufv buffer vector + * @return zero for success, -errno for failure + */ +int fuse_lowlevel_notify_store(struct fuse_session *se, fuse_ino_t ino, + off_t offset, struct fuse_bufvec *bufv); + +/* + * Utility functions + */ + +/** + * Get the userdata from the request + * + * @param req request handle + * @return the user data passed to fuse_session_new() + */ +void *fuse_req_userdata(fuse_req_t req); + +/** + * Get the context from the request + * + * The pointer returned by this function will only be valid for the + * request's lifetime + * + * @param req request handle + * @return the context structure + */ +const struct fuse_ctx *fuse_req_ctx(fuse_req_t req); + +/** + * Callback function for an interrupt + * + * @param req interrupted request + * @param data user data + */ +typedef void (*fuse_interrupt_func_t)(fuse_req_t req, void *data); + +/** + * Register/unregister callback for an interrupt + * + * If an interrupt has already happened, then the callback function is + * called from within this function, hence it's not possible for + * interrupts to be lost. + * + * @param req request handle + * @param func the callback function or NULL for unregister + * @param data user data passed to the callback function + */ +void fuse_req_interrupt_func(fuse_req_t req, fuse_interrupt_func_t func, + void *data); + +/** + * Check if a request has already been interrupted + * + * @param req request handle + * @return 1 if the request has been interrupted, 0 otherwise + */ +int fuse_req_interrupted(fuse_req_t req); + +/** + * Check if the session is connected via virtio + * + * @param se session object + * @return 1 if the session is a virtio session + */ +int fuse_lowlevel_is_virtio(struct fuse_session *se); + +/* + * Inquiry functions + */ + +/** + * Print low-level version information to stdout. + */ +void fuse_lowlevel_version(void); + +/** + * Print available low-level options to stdout. This is not an + * exhaustive list, but includes only those options that may be of + * interest to an end-user of a file system. + */ +void fuse_lowlevel_help(void); + +/** + * Print available options for `fuse_parse_cmdline()`. + */ +void fuse_cmdline_help(void); + +/* + * Filesystem setup & teardown + */ + +struct fuse_cmdline_opts { + int foreground; + int debug; + int nodefault_subtype; + int show_version; + int show_help; + int print_capabilities; + int syslog; + int log_level; + unsigned int max_idle_threads; +}; + +/** + * Utility function to parse common options for simple file systems + * using the low-level API. A help text that describes the available + * options can be printed with `fuse_cmdline_help`. A single + * non-option argument is treated as the mountpoint. Multiple + * non-option arguments will result in an error. + * + * If neither -o subtype= or -o fsname= options are given, a new + * subtype option will be added and set to the basename of the program + * (the fsname will remain unset, and then defaults to "fuse"). + * + * Known options will be removed from *args*, unknown options will + * remain. + * + * @param args argument vector (input+output) + * @param opts output argument for parsed options + * @return 0 on success, -1 on failure + */ +int fuse_parse_cmdline(struct fuse_args *args, struct fuse_cmdline_opts *opts); + +/** + * Create a low level session. + * + * Returns a session structure suitable for passing to + * fuse_session_mount() and fuse_session_loop(). + * + * This function accepts most file-system independent mount options + * (like context, nodev, ro - see mount(8)), as well as the general + * fuse mount options listed in mount.fuse(8) (e.g. -o allow_root and + * -o default_permissions, but not ``-o use_ino``). Instead of `-o + * debug`, debugging may also enabled with `-d` or `--debug`. + * + * If not all options are known, an error message is written to stderr + * and the function returns NULL. + * + * Option parsing skips argv[0], which is assumed to contain the + * program name. To prevent accidentally passing an option in + * argv[0], this element must always be present (even if no options + * are specified). It may be set to the empty string ('\0') if no + * reasonable value can be provided. + * + * @param args argument vector + * @param op the (low-level) filesystem operations + * @param op_size sizeof(struct fuse_lowlevel_ops) + * @param userdata user data + * + * @return the fuse session on success, NULL on failure + **/ +struct fuse_session *fuse_session_new(struct fuse_args *args, + const struct fuse_lowlevel_ops *op, + size_t op_size, void *userdata); + +/** + * Mount a FUSE file system. + * + * @param se session object + * + * @return 0 on success, -1 on failure. + **/ +int fuse_session_mount(struct fuse_session *se); + +/** + * Enter a single threaded, blocking event loop. + * + * When the event loop terminates because the connection to the FUSE + * kernel module has been closed, this function returns zero. This + * happens when the filesystem is unmounted regularly (by the + * filesystem owner or root running the umount(8) or fusermount(1) + * command), or if connection is explicitly severed by writing ``1`` + * to the``abort`` file in ``/sys/fs/fuse/connections/NNN``. The only + * way to distinguish between these two conditions is to check if the + * filesystem is still mounted after the session loop returns. + * + * When some error occurs during request processing, the function + * returns a negated errno(3) value. + * + * If the loop has been terminated because of a signal handler + * installed by fuse_set_signal_handlers(), this function returns the + * (positive) signal value that triggered the exit. + * + * @param se the session + * @return 0, -errno, or a signal value + */ +int fuse_session_loop(struct fuse_session *se); + +/** + * Flag a session as terminated. + * + * This function is invoked by the POSIX signal handlers, when + * registered using fuse_set_signal_handlers(). It will cause any + * running event loops to terminate on the next opportunity. + * + * @param se the session + */ +void fuse_session_exit(struct fuse_session *se); + +/** + * Reset the terminated flag of a session + * + * @param se the session + */ +void fuse_session_reset(struct fuse_session *se); + +/** + * Query the terminated flag of a session + * + * @param se the session + * @return 1 if exited, 0 if not exited + */ +int fuse_session_exited(struct fuse_session *se); + +/** + * Ensure that file system is unmounted. + * + * In regular operation, the file system is typically unmounted by the + * user calling umount(8) or fusermount(1), which then terminates the + * FUSE session loop. However, the session loop may also terminate as + * a result of an explicit call to fuse_session_exit() (e.g. by a + * signal handler installed by fuse_set_signal_handler()). In this + * case the filesystem remains mounted, but any attempt to access it + * will block (while the filesystem process is still running) or give + * an ESHUTDOWN error (after the filesystem process has terminated). + * + * If the communication channel with the FUSE kernel module is still + * open (i.e., if the session loop was terminated by an explicit call + * to fuse_session_exit()), this function will close it and unmount + * the filesystem. If the communication channel has been closed by the + * kernel, this method will do (almost) nothing. + * + * NOTE: The above semantics mean that if the connection to the kernel + * is terminated via the ``/sys/fs/fuse/connections/NNN/abort`` file, + * this method will *not* unmount the filesystem. + * + * @param se the session + */ +void fuse_session_unmount(struct fuse_session *se); + +/** + * Destroy a session + * + * @param se the session + */ +void fuse_session_destroy(struct fuse_session *se); + +/* + * Custom event loop support + */ + +/** + * Return file descriptor for communication with kernel. + * + * The file selector can be used to integrate FUSE with a custom event + * loop. Whenever data is available for reading on the provided fd, + * the event loop should call `fuse_session_receive_buf` followed by + * `fuse_session_process_buf` to process the request. + * + * The returned file descriptor is valid until `fuse_session_unmount` + * is called. + * + * @param se the session + * @return a file descriptor + */ +int fuse_session_fd(struct fuse_session *se); + +/** + * Process a raw request supplied in a generic buffer + * + * The fuse_buf may contain a memory buffer or a pipe file descriptor. + * + * @param se the session + * @param buf the fuse_buf containing the request + */ +void fuse_session_process_buf(struct fuse_session *se, + const struct fuse_buf *buf); + +/** + * Read a raw request from the kernel into the supplied buffer. + * + * Depending on file system options, system capabilities, and request + * size the request is either read into a memory buffer or spliced + * into a temporary pipe. + * + * @param se the session + * @param buf the fuse_buf to store the request in + * @return the actual size of the raw request, or -errno on error + */ +int fuse_session_receive_buf(struct fuse_session *se, struct fuse_buf *buf); + +#endif /* FUSE_LOWLEVEL_H_ */ diff --git a/tools/virtiofsd/fuse_misc.h b/tools/virtiofsd/fuse_misc.h new file mode 100644 index 0000000000..5c618ce21f --- /dev/null +++ b/tools/virtiofsd/fuse_misc.h @@ -0,0 +1,60 @@ +/* + * FUSE: Filesystem in Userspace + * Copyright (C) 2001-2007 Miklos Szeredi + * + * This program can be distributed under the terms of the GNU LGPLv2. + * See the file COPYING.LIB + */ + +#include +#include "config-host.h" + +/* + * Versioned symbols cannot be used in some cases because it + * - confuse the dynamic linker in uClibc + * - not supported on MacOSX (in MachO binary format) + */ +#if (!defined(__UCLIBC__) && !defined(__APPLE__)) +#define FUSE_SYMVER(x) __asm__(x) +#else +#define FUSE_SYMVER(x) +#endif + +#ifndef USE_UCLIBC +#define fuse_mutex_init(mut) pthread_mutex_init(mut, NULL) +#else +/* Is this hack still needed? */ +static inline void fuse_mutex_init(pthread_mutex_t *mut) +{ + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ADAPTIVE_NP); + pthread_mutex_init(mut, &attr); + pthread_mutexattr_destroy(&attr); +} +#endif + +#ifdef HAVE_STRUCT_STAT_ST_ATIM +/* Linux */ +#define ST_ATIM_NSEC(stbuf) ((stbuf)->st_atim.tv_nsec) +#define ST_CTIM_NSEC(stbuf) ((stbuf)->st_ctim.tv_nsec) +#define ST_MTIM_NSEC(stbuf) ((stbuf)->st_mtim.tv_nsec) +#define ST_ATIM_NSEC_SET(stbuf, val) (stbuf)->st_atim.tv_nsec = (val) +#define ST_CTIM_NSEC_SET(stbuf, val) (stbuf)->st_ctim.tv_nsec = (val) +#define ST_MTIM_NSEC_SET(stbuf, val) (stbuf)->st_mtim.tv_nsec = (val) +#elif defined(HAVE_STRUCT_STAT_ST_ATIMESPEC) +/* FreeBSD */ +#define ST_ATIM_NSEC(stbuf) ((stbuf)->st_atimespec.tv_nsec) +#define ST_CTIM_NSEC(stbuf) ((stbuf)->st_ctimespec.tv_nsec) +#define ST_MTIM_NSEC(stbuf) ((stbuf)->st_mtimespec.tv_nsec) +#define ST_ATIM_NSEC_SET(stbuf, val) (stbuf)->st_atimespec.tv_nsec = (val) +#define ST_CTIM_NSEC_SET(stbuf, val) (stbuf)->st_ctimespec.tv_nsec = (val) +#define ST_MTIM_NSEC_SET(stbuf, val) (stbuf)->st_mtimespec.tv_nsec = (val) +#else +#define ST_ATIM_NSEC(stbuf) 0 +#define ST_CTIM_NSEC(stbuf) 0 +#define ST_MTIM_NSEC(stbuf) 0 +#define ST_ATIM_NSEC_SET(stbuf, val) do { } while (0) +#define ST_CTIM_NSEC_SET(stbuf, val) do { } while (0) +#define ST_MTIM_NSEC_SET(stbuf, val) do { } while (0) +#endif diff --git a/tools/virtiofsd/fuse_opt.c b/tools/virtiofsd/fuse_opt.c new file mode 100644 index 0000000000..28922361a2 --- /dev/null +++ b/tools/virtiofsd/fuse_opt.c @@ -0,0 +1,450 @@ +/* + * FUSE: Filesystem in Userspace + * Copyright (C) 2001-2007 Miklos Szeredi + * + * Implementation of option parsing routines (dealing with `struct + * fuse_args`). + * + * This program can be distributed under the terms of the GNU LGPLv2. + * See the file COPYING.LIB + */ + +#include "qemu/osdep.h" +#include "fuse_opt.h" +#include "fuse_i.h" +#include "fuse_misc.h" + +#include +#include +#include +#include + +struct fuse_opt_context { + void *data; + const struct fuse_opt *opt; + fuse_opt_proc_t proc; + int argctr; + int argc; + char **argv; + struct fuse_args outargs; + char *opts; + int nonopt; +}; + +void fuse_opt_free_args(struct fuse_args *args) +{ + if (args) { + if (args->argv && args->allocated) { + int i; + for (i = 0; i < args->argc; i++) { + free(args->argv[i]); + } + free(args->argv); + } + args->argc = 0; + args->argv = NULL; + args->allocated = 0; + } +} + +static int alloc_failed(void) +{ + fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n"); + return -1; +} + +int fuse_opt_add_arg(struct fuse_args *args, const char *arg) +{ + char **newargv; + char *newarg; + + assert(!args->argv || args->allocated); + + newarg = strdup(arg); + if (!newarg) { + return alloc_failed(); + } + + newargv = realloc(args->argv, (args->argc + 2) * sizeof(char *)); + if (!newargv) { + free(newarg); + return alloc_failed(); + } + + args->argv = newargv; + args->allocated = 1; + args->argv[args->argc++] = newarg; + args->argv[args->argc] = NULL; + return 0; +} + +static int fuse_opt_insert_arg_common(struct fuse_args *args, int pos, + const char *arg) +{ + assert(pos <= args->argc); + if (fuse_opt_add_arg(args, arg) == -1) { + return -1; + } + + if (pos != args->argc - 1) { + char *newarg = args->argv[args->argc - 1]; + memmove(&args->argv[pos + 1], &args->argv[pos], + sizeof(char *) * (args->argc - pos - 1)); + args->argv[pos] = newarg; + } + return 0; +} + +int fuse_opt_insert_arg(struct fuse_args *args, int pos, const char *arg) +{ + return fuse_opt_insert_arg_common(args, pos, arg); +} + +static int next_arg(struct fuse_opt_context *ctx, const char *opt) +{ + if (ctx->argctr + 1 >= ctx->argc) { + fuse_log(FUSE_LOG_ERR, "fuse: missing argument after `%s'\n", opt); + return -1; + } + ctx->argctr++; + return 0; +} + +static int add_arg(struct fuse_opt_context *ctx, const char *arg) +{ + return fuse_opt_add_arg(&ctx->outargs, arg); +} + +static int add_opt_common(char **opts, const char *opt, int esc) +{ + unsigned oldlen = *opts ? strlen(*opts) : 0; + char *d = realloc(*opts, oldlen + 1 + strlen(opt) * 2 + 1); + + if (!d) { + return alloc_failed(); + } + + *opts = d; + if (oldlen) { + d += oldlen; + *d++ = ','; + } + + for (; *opt; opt++) { + if (esc && (*opt == ',' || *opt == '\\')) { + *d++ = '\\'; + } + *d++ = *opt; + } + *d = '\0'; + + return 0; +} + +int fuse_opt_add_opt(char **opts, const char *opt) +{ + return add_opt_common(opts, opt, 0); +} + +int fuse_opt_add_opt_escaped(char **opts, const char *opt) +{ + return add_opt_common(opts, opt, 1); +} + +static int add_opt(struct fuse_opt_context *ctx, const char *opt) +{ + return add_opt_common(&ctx->opts, opt, 1); +} + +static int call_proc(struct fuse_opt_context *ctx, const char *arg, int key, + int iso) +{ + if (key == FUSE_OPT_KEY_DISCARD) { + return 0; + } + + if (key != FUSE_OPT_KEY_KEEP && ctx->proc) { + int res = ctx->proc(ctx->data, arg, key, &ctx->outargs); + if (res == -1 || !res) { + return res; + } + } + if (iso) { + return add_opt(ctx, arg); + } else { + return add_arg(ctx, arg); + } +} + +static int match_template(const char *t, const char *arg, unsigned *sepp) +{ + int arglen = strlen(arg); + const char *sep = strchr(t, '='); + sep = sep ? sep : strchr(t, ' '); + if (sep && (!sep[1] || sep[1] == '%')) { + int tlen = sep - t; + if (sep[0] == '=') { + tlen++; + } + if (arglen >= tlen && strncmp(arg, t, tlen) == 0) { + *sepp = sep - t; + return 1; + } + } + if (strcmp(t, arg) == 0) { + *sepp = 0; + return 1; + } + return 0; +} + +static const struct fuse_opt *find_opt(const struct fuse_opt *opt, + const char *arg, unsigned *sepp) +{ + for (; opt && opt->templ; opt++) { + if (match_template(opt->templ, arg, sepp)) { + return opt; + } + } + return NULL; +} + +int fuse_opt_match(const struct fuse_opt *opts, const char *opt) +{ + unsigned dummy; + return find_opt(opts, opt, &dummy) ? 1 : 0; +} + +static int process_opt_param(void *var, const char *format, const char *param, + const char *arg) +{ + assert(format[0] == '%'); + if (format[1] == 's') { + char **s = var; + char *copy = strdup(param); + if (!copy) { + return alloc_failed(); + } + + free(*s); + *s = copy; + } else { + if (sscanf(param, format, var) != 1) { + fuse_log(FUSE_LOG_ERR, "fuse: invalid parameter in option `%s'\n", + arg); + return -1; + } + } + return 0; +} + +static int process_opt(struct fuse_opt_context *ctx, const struct fuse_opt *opt, + unsigned sep, const char *arg, int iso) +{ + if (opt->offset == -1U) { + if (call_proc(ctx, arg, opt->value, iso) == -1) { + return -1; + } + } else { + void *var = (char *)ctx->data + opt->offset; + if (sep && opt->templ[sep + 1]) { + const char *param = arg + sep; + if (opt->templ[sep] == '=') { + param++; + } + if (process_opt_param(var, opt->templ + sep + 1, param, arg) == + -1) { + return -1; + } + } else { + *(int *)var = opt->value; + } + } + return 0; +} + +static int process_opt_sep_arg(struct fuse_opt_context *ctx, + const struct fuse_opt *opt, unsigned sep, + const char *arg, int iso) +{ + int res; + char *newarg; + char *param; + + if (next_arg(ctx, arg) == -1) { + return -1; + } + + param = ctx->argv[ctx->argctr]; + newarg = malloc(sep + strlen(param) + 1); + if (!newarg) { + return alloc_failed(); + } + + memcpy(newarg, arg, sep); + strcpy(newarg + sep, param); + res = process_opt(ctx, opt, sep, newarg, iso); + free(newarg); + + return res; +} + +static int process_gopt(struct fuse_opt_context *ctx, const char *arg, int iso) +{ + unsigned sep; + const struct fuse_opt *opt = find_opt(ctx->opt, arg, &sep); + if (opt) { + for (; opt; opt = find_opt(opt + 1, arg, &sep)) { + int res; + if (sep && opt->templ[sep] == ' ' && !arg[sep]) { + res = process_opt_sep_arg(ctx, opt, sep, arg, iso); + } else { + res = process_opt(ctx, opt, sep, arg, iso); + } + if (res == -1) { + return -1; + } + } + return 0; + } else { + return call_proc(ctx, arg, FUSE_OPT_KEY_OPT, iso); + } +} + +static int process_real_option_group(struct fuse_opt_context *ctx, char *opts) +{ + char *s = opts; + char *d = s; + int end = 0; + + while (!end) { + if (*s == '\0') { + end = 1; + } + if (*s == ',' || end) { + int res; + + *d = '\0'; + res = process_gopt(ctx, opts, 1); + if (res == -1) { + return -1; + } + d = opts; + } else { + if (s[0] == '\\' && s[1] != '\0') { + s++; + if (s[0] >= '0' && s[0] <= '3' && s[1] >= '0' && s[1] <= '7' && + s[2] >= '0' && s[2] <= '7') { + *d++ = (s[0] - '0') * 0100 + (s[1] - '0') * 0010 + + (s[2] - '0'); + s += 2; + } else { + *d++ = *s; + } + } else { + *d++ = *s; + } + } + s++; + } + + return 0; +} + +static int process_option_group(struct fuse_opt_context *ctx, const char *opts) +{ + int res; + char *copy = strdup(opts); + + if (!copy) { + fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n"); + return -1; + } + res = process_real_option_group(ctx, copy); + free(copy); + return res; +} + +static int process_one(struct fuse_opt_context *ctx, const char *arg) +{ + if (ctx->nonopt || arg[0] != '-') { + return call_proc(ctx, arg, FUSE_OPT_KEY_NONOPT, 0); + } else if (arg[1] == 'o') { + if (arg[2]) { + return process_option_group(ctx, arg + 2); + } else { + if (next_arg(ctx, arg) == -1) { + return -1; + } + + return process_option_group(ctx, ctx->argv[ctx->argctr]); + } + } else if (arg[1] == '-' && !arg[2]) { + if (add_arg(ctx, arg) == -1) { + return -1; + } + ctx->nonopt = ctx->outargs.argc; + return 0; + } else { + return process_gopt(ctx, arg, 0); + } +} + +static int opt_parse(struct fuse_opt_context *ctx) +{ + if (ctx->argc) { + if (add_arg(ctx, ctx->argv[0]) == -1) { + return -1; + } + } + + for (ctx->argctr = 1; ctx->argctr < ctx->argc; ctx->argctr++) { + if (process_one(ctx, ctx->argv[ctx->argctr]) == -1) { + return -1; + } + } + + if (ctx->opts) { + if (fuse_opt_insert_arg(&ctx->outargs, 1, "-o") == -1 || + fuse_opt_insert_arg(&ctx->outargs, 2, ctx->opts) == -1) { + return -1; + } + } + + /* If option separator ("--") is the last argument, remove it */ + if (ctx->nonopt && ctx->nonopt == ctx->outargs.argc && + strcmp(ctx->outargs.argv[ctx->outargs.argc - 1], "--") == 0) { + free(ctx->outargs.argv[ctx->outargs.argc - 1]); + ctx->outargs.argv[--ctx->outargs.argc] = NULL; + } + + return 0; +} + +int fuse_opt_parse(struct fuse_args *args, void *data, + const struct fuse_opt opts[], fuse_opt_proc_t proc) +{ + int res; + struct fuse_opt_context ctx = { + .data = data, + .opt = opts, + .proc = proc, + }; + + if (!args || !args->argv || !args->argc) { + return 0; + } + + ctx.argc = args->argc; + ctx.argv = args->argv; + + res = opt_parse(&ctx); + if (res != -1) { + struct fuse_args tmp = *args; + *args = ctx.outargs; + ctx.outargs = tmp; + } + free(ctx.opts); + fuse_opt_free_args(&ctx.outargs); + return res; +} diff --git a/tools/virtiofsd/fuse_opt.h b/tools/virtiofsd/fuse_opt.h new file mode 100644 index 0000000000..8f59b4d301 --- /dev/null +++ b/tools/virtiofsd/fuse_opt.h @@ -0,0 +1,272 @@ +/* + * FUSE: Filesystem in Userspace + * Copyright (C) 2001-2007 Miklos Szeredi + * + * This program can be distributed under the terms of the GNU LGPLv2. + * See the file COPYING.LIB. + */ + +#ifndef FUSE_OPT_H_ +#define FUSE_OPT_H_ + +/** @file + * + * This file defines the option parsing interface of FUSE + */ + +/** + * Option description + * + * This structure describes a single option, and action associated + * with it, in case it matches. + * + * More than one such match may occur, in which case the action for + * each match is executed. + * + * There are three possible actions in case of a match: + * + * i) An integer (int or unsigned) variable determined by 'offset' is + * set to 'value' + * + * ii) The processing function is called, with 'value' as the key + * + * iii) An integer (any) or string (char *) variable determined by + * 'offset' is set to the value of an option parameter + * + * 'offset' should normally be either set to + * + * - 'offsetof(struct foo, member)' actions i) and iii) + * + * - -1 action ii) + * + * The 'offsetof()' macro is defined in the header. + * + * The template determines which options match, and also have an + * effect on the action. Normally the action is either i) or ii), but + * if a format is present in the template, then action iii) is + * performed. + * + * The types of templates are: + * + * 1) "-x", "-foo", "--foo", "--foo-bar", etc. These match only + * themselves. Invalid values are "--" and anything beginning + * with "-o" + * + * 2) "foo", "foo-bar", etc. These match "-ofoo", "-ofoo-bar" or + * the relevant option in a comma separated option list + * + * 3) "bar=", "--foo=", etc. These are variations of 1) and 2) + * which have a parameter + * + * 4) "bar=%s", "--foo=%lu", etc. Same matching as above but perform + * action iii). + * + * 5) "-x ", etc. Matches either "-xparam" or "-x param" as + * two separate arguments + * + * 6) "-x %s", etc. Combination of 4) and 5) + * + * If the format is "%s", memory is allocated for the string unlike with + * scanf(). The previous value (if non-NULL) stored at the this location is + * freed. + */ +struct fuse_opt { + /** Matching template and optional parameter formatting */ + const char *templ; + + /** + * Offset of variable within 'data' parameter of fuse_opt_parse() + * or -1 + */ + unsigned long offset; + + /** + * Value to set the variable to, or to be passed as 'key' to the + * processing function. Ignored if template has a format + */ + int value; +}; + +/** + * Key option. In case of a match, the processing function will be + * called with the specified key. + */ +#define FUSE_OPT_KEY(templ, key) \ + { \ + templ, -1U, key \ + } + +/** + * Last option. An array of 'struct fuse_opt' must end with a NULL + * template value + */ +#define FUSE_OPT_END \ + { \ + NULL, 0, 0 \ + } + +/** + * Argument list + */ +struct fuse_args { + /** Argument count */ + int argc; + + /** Argument vector. NULL terminated */ + char **argv; + + /** Is 'argv' allocated? */ + int allocated; +}; + +/** + * Initializer for 'struct fuse_args' + */ +#define FUSE_ARGS_INIT(argc, argv) \ + { \ + argc, argv, 0 \ + } + +/** + * Key value passed to the processing function if an option did not + * match any template + */ +#define FUSE_OPT_KEY_OPT -1 + +/** + * Key value passed to the processing function for all non-options + * + * Non-options are the arguments beginning with a character other than + * '-' or all arguments after the special '--' option + */ +#define FUSE_OPT_KEY_NONOPT -2 + +/** + * Special key value for options to keep + * + * Argument is not passed to processing function, but behave as if the + * processing function returned 1 + */ +#define FUSE_OPT_KEY_KEEP -3 + +/** + * Special key value for options to discard + * + * Argument is not passed to processing function, but behave as if the + * processing function returned zero + */ +#define FUSE_OPT_KEY_DISCARD -4 + +/** + * Processing function + * + * This function is called if + * - option did not match any 'struct fuse_opt' + * - argument is a non-option + * - option did match and offset was set to -1 + * + * The 'arg' parameter will always contain the whole argument or + * option including the parameter if exists. A two-argument option + * ("-x foo") is always converted to single argument option of the + * form "-xfoo" before this function is called. + * + * Options of the form '-ofoo' are passed to this function without the + * '-o' prefix. + * + * The return value of this function determines whether this argument + * is to be inserted into the output argument vector, or discarded. + * + * @param data is the user data passed to the fuse_opt_parse() function + * @param arg is the whole argument or option + * @param key determines why the processing function was called + * @param outargs the current output argument list + * @return -1 on error, 0 if arg is to be discarded, 1 if arg should be kept + */ +typedef int (*fuse_opt_proc_t)(void *data, const char *arg, int key, + struct fuse_args *outargs); + +/** + * Option parsing function + * + * If 'args' was returned from a previous call to fuse_opt_parse() or + * it was constructed from + * + * A NULL 'args' is equivalent to an empty argument vector + * + * A NULL 'opts' is equivalent to an 'opts' array containing a single + * end marker + * + * A NULL 'proc' is equivalent to a processing function always + * returning '1' + * + * @param args is the input and output argument list + * @param data is the user data + * @param opts is the option description array + * @param proc is the processing function + * @return -1 on error, 0 on success + */ +int fuse_opt_parse(struct fuse_args *args, void *data, + const struct fuse_opt opts[], fuse_opt_proc_t proc); + +/** + * Add an option to a comma separated option list + * + * @param opts is a pointer to an option list, may point to a NULL value + * @param opt is the option to add + * @return -1 on allocation error, 0 on success + */ +int fuse_opt_add_opt(char **opts, const char *opt); + +/** + * Add an option, escaping commas, to a comma separated option list + * + * @param opts is a pointer to an option list, may point to a NULL value + * @param opt is the option to add + * @return -1 on allocation error, 0 on success + */ +int fuse_opt_add_opt_escaped(char **opts, const char *opt); + +/** + * Add an argument to a NULL terminated argument vector + * + * @param args is the structure containing the current argument list + * @param arg is the new argument to add + * @return -1 on allocation error, 0 on success + */ +int fuse_opt_add_arg(struct fuse_args *args, const char *arg); + +/** + * Add an argument at the specified position in a NULL terminated + * argument vector + * + * Adds the argument to the N-th position. This is useful for adding + * options at the beginning of the array which must not come after the + * special '--' option. + * + * @param args is the structure containing the current argument list + * @param pos is the position at which to add the argument + * @param arg is the new argument to add + * @return -1 on allocation error, 0 on success + */ +int fuse_opt_insert_arg(struct fuse_args *args, int pos, const char *arg); + +/** + * Free the contents of argument list + * + * The structure itself is not freed + * + * @param args is the structure containing the argument list + */ +void fuse_opt_free_args(struct fuse_args *args); + + +/** + * Check if an option matches + * + * @param opts is the option description array + * @param opt is the option to match + * @return 1 if a match is found, 0 if not + */ +int fuse_opt_match(const struct fuse_opt opts[], const char *opt); + +#endif /* FUSE_OPT_H_ */ diff --git a/tools/virtiofsd/fuse_signals.c b/tools/virtiofsd/fuse_signals.c new file mode 100644 index 0000000000..f18625b6e2 --- /dev/null +++ b/tools/virtiofsd/fuse_signals.c @@ -0,0 +1,98 @@ +/* + * FUSE: Filesystem in Userspace + * Copyright (C) 2001-2007 Miklos Szeredi + * + * Utility functions for setting signal handlers. + * + * This program can be distributed under the terms of the GNU LGPLv2. + * See the file COPYING.LIB + */ + +#include "qemu/osdep.h" +#include "fuse_i.h" +#include "fuse_lowlevel.h" + +#include +#include +#include +#include +#include + +static struct fuse_session *fuse_instance; + +static void exit_handler(int sig) +{ + if (fuse_instance) { + fuse_session_exit(fuse_instance); + if (sig <= 0) { + fuse_log(FUSE_LOG_ERR, "assertion error: signal value <= 0\n"); + abort(); + } + fuse_instance->error = sig; + } +} + +static void do_nothing(int sig) +{ + (void)sig; +} + +static int set_one_signal_handler(int sig, void (*handler)(int), int remove) +{ + struct sigaction sa; + struct sigaction old_sa; + + memset(&sa, 0, sizeof(struct sigaction)); + sa.sa_handler = remove ? SIG_DFL : handler; + sigemptyset(&(sa.sa_mask)); + sa.sa_flags = 0; + + if (sigaction(sig, NULL, &old_sa) == -1) { + fuse_log(FUSE_LOG_ERR, "fuse: cannot get old signal handler: %s\n", + strerror(errno)); + return -1; + } + + if (old_sa.sa_handler == (remove ? handler : SIG_DFL) && + sigaction(sig, &sa, NULL) == -1) { + fuse_log(FUSE_LOG_ERR, "fuse: cannot set signal handler: %s\n", + strerror(errno)); + return -1; + } + return 0; +} + +int fuse_set_signal_handlers(struct fuse_session *se) +{ + /* + * If we used SIG_IGN instead of the do_nothing function, + * then we would be unable to tell if we set SIG_IGN (and + * thus should reset to SIG_DFL in fuse_remove_signal_handlers) + * or if it was already set to SIG_IGN (and should be left + * untouched. + */ + if (set_one_signal_handler(SIGHUP, exit_handler, 0) == -1 || + set_one_signal_handler(SIGINT, exit_handler, 0) == -1 || + set_one_signal_handler(SIGTERM, exit_handler, 0) == -1 || + set_one_signal_handler(SIGPIPE, do_nothing, 0) == -1) { + return -1; + } + + fuse_instance = se; + return 0; +} + +void fuse_remove_signal_handlers(struct fuse_session *se) +{ + if (fuse_instance != se) { + fuse_log(FUSE_LOG_ERR, + "fuse: fuse_remove_signal_handlers: unknown session\n"); + } else { + fuse_instance = NULL; + } + + set_one_signal_handler(SIGHUP, exit_handler, 1); + set_one_signal_handler(SIGINT, exit_handler, 1); + set_one_signal_handler(SIGTERM, exit_handler, 1); + set_one_signal_handler(SIGPIPE, do_nothing, 1); +} diff --git a/tools/virtiofsd/fuse_virtio.c b/tools/virtiofsd/fuse_virtio.c new file mode 100644 index 0000000000..3b6d16a041 --- /dev/null +++ b/tools/virtiofsd/fuse_virtio.c @@ -0,0 +1,1001 @@ +/* + * virtio-fs glue for FUSE + * Copyright (C) 2018 Red Hat, Inc. and/or its affiliates + * + * Authors: + * Dave Gilbert + * + * Implements the glue between libfuse and libvhost-user + * + * This program can be distributed under the terms of the GNU LGPLv2. + * See the file COPYING.LIB + */ + +#include "qemu/osdep.h" +#include "qemu/iov.h" +#include "qapi/error.h" +#include "fuse_i.h" +#include "standard-headers/linux/fuse.h" +#include "fuse_misc.h" +#include "fuse_opt.h" +#include "fuse_virtio.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "contrib/libvhost-user/libvhost-user.h" + +struct fv_VuDev; +struct fv_QueueInfo { + pthread_t thread; + /* + * This lock protects the VuVirtq preventing races between + * fv_queue_thread() and fv_queue_worker(). + */ + pthread_mutex_t vq_lock; + + struct fv_VuDev *virtio_dev; + + /* Our queue index, corresponds to array position */ + int qidx; + int kick_fd; + int kill_fd; /* For killing the thread */ +}; + +/* A FUSE request */ +typedef struct { + VuVirtqElement elem; + struct fuse_chan ch; + + /* Used to complete requests that involve no reply */ + bool reply_sent; +} FVRequest; + +/* + * We pass the dev element into libvhost-user + * and then use it to get back to the outer + * container for other data. + */ +struct fv_VuDev { + VuDev dev; + struct fuse_session *se; + + /* + * Either handle virtqueues or vhost-user protocol messages. Don't do + * both at the same time since that could lead to race conditions if + * virtqueues or memory tables change while another thread is accessing + * them. + * + * The assumptions are: + * 1. fv_queue_thread() reads/writes to virtqueues and only reads VuDev. + * 2. virtio_loop() reads/writes virtqueues and VuDev. + */ + pthread_rwlock_t vu_dispatch_rwlock; + + /* + * The following pair of fields are only accessed in the main + * virtio_loop + */ + size_t nqueues; + struct fv_QueueInfo **qi; +}; + +/* From spec */ +struct virtio_fs_config { + char tag[36]; + uint32_t num_queues; +}; + +/* Callback from libvhost-user */ +static uint64_t fv_get_features(VuDev *dev) +{ + return 1ULL << VIRTIO_F_VERSION_1; +} + +/* Callback from libvhost-user */ +static void fv_set_features(VuDev *dev, uint64_t features) +{ +} + +/* + * Callback from libvhost-user if there's a new fd we're supposed to listen + * to, typically a queue kick? + */ +static void fv_set_watch(VuDev *dev, int fd, int condition, vu_watch_cb cb, + void *data) +{ + fuse_log(FUSE_LOG_WARNING, "%s: TODO! fd=%d\n", __func__, fd); +} + +/* + * Callback from libvhost-user if we're no longer supposed to listen on an fd + */ +static void fv_remove_watch(VuDev *dev, int fd) +{ + fuse_log(FUSE_LOG_WARNING, "%s: TODO! fd=%d\n", __func__, fd); +} + +/* Callback from libvhost-user to panic */ +static void fv_panic(VuDev *dev, const char *err) +{ + fuse_log(FUSE_LOG_ERR, "%s: libvhost-user: %s\n", __func__, err); + /* TODO: Allow reconnects?? */ + exit(EXIT_FAILURE); +} + +/* + * Copy from an iovec into a fuse_buf (memory only) + * Caller must ensure there is space + */ +static void copy_from_iov(struct fuse_buf *buf, size_t out_num, + const struct iovec *out_sg) +{ + void *dest = buf->mem; + + while (out_num) { + size_t onelen = out_sg->iov_len; + memcpy(dest, out_sg->iov_base, onelen); + dest += onelen; + out_sg++; + out_num--; + } +} + +/* + * Copy from one iov to another, the given number of bytes + * The caller must have checked sizes. + */ +static void copy_iov(struct iovec *src_iov, int src_count, + struct iovec *dst_iov, int dst_count, size_t to_copy) +{ + size_t dst_offset = 0; + /* Outer loop copies 'src' elements */ + while (to_copy) { + assert(src_count); + size_t src_len = src_iov[0].iov_len; + size_t src_offset = 0; + + if (src_len > to_copy) { + src_len = to_copy; + } + /* Inner loop copies contents of one 'src' to maybe multiple dst. */ + while (src_len) { + assert(dst_count); + size_t dst_len = dst_iov[0].iov_len - dst_offset; + if (dst_len > src_len) { + dst_len = src_len; + } + + memcpy(dst_iov[0].iov_base + dst_offset, + src_iov[0].iov_base + src_offset, dst_len); + src_len -= dst_len; + to_copy -= dst_len; + src_offset += dst_len; + dst_offset += dst_len; + + assert(dst_offset <= dst_iov[0].iov_len); + if (dst_offset == dst_iov[0].iov_len) { + dst_offset = 0; + dst_iov++; + dst_count--; + } + } + src_iov++; + src_count--; + } +} + +/* + * Called back by ll whenever it wants to send a reply/message back + * The 1st element of the iov starts with the fuse_out_header + * 'unique'==0 means it's a notify message. + */ +int virtio_send_msg(struct fuse_session *se, struct fuse_chan *ch, + struct iovec *iov, int count) +{ + FVRequest *req = container_of(ch, FVRequest, ch); + struct fv_QueueInfo *qi = ch->qi; + VuDev *dev = &se->virtio_dev->dev; + VuVirtq *q = vu_get_queue(dev, qi->qidx); + VuVirtqElement *elem = &req->elem; + int ret = 0; + + assert(count >= 1); + assert(iov[0].iov_len >= sizeof(struct fuse_out_header)); + + struct fuse_out_header *out = iov[0].iov_base; + /* TODO: Endianness! */ + + size_t tosend_len = iov_size(iov, count); + + /* unique == 0 is notification, which we don't support */ + assert(out->unique); + assert(!req->reply_sent); + + /* The 'in' part of the elem is to qemu */ + unsigned int in_num = elem->in_num; + struct iovec *in_sg = elem->in_sg; + size_t in_len = iov_size(in_sg, in_num); + fuse_log(FUSE_LOG_DEBUG, "%s: elem %d: with %d in desc of length %zd\n", + __func__, elem->index, in_num, in_len); + + /* + * The elem should have room for a 'fuse_out_header' (out from fuse) + * plus the data based on the len in the header. + */ + if (in_len < sizeof(struct fuse_out_header)) { + fuse_log(FUSE_LOG_ERR, "%s: elem %d too short for out_header\n", + __func__, elem->index); + ret = -E2BIG; + goto err; + } + if (in_len < tosend_len) { + fuse_log(FUSE_LOG_ERR, "%s: elem %d too small for data len %zd\n", + __func__, elem->index, tosend_len); + ret = -E2BIG; + goto err; + } + + copy_iov(iov, count, in_sg, in_num, tosend_len); + + pthread_rwlock_rdlock(&qi->virtio_dev->vu_dispatch_rwlock); + pthread_mutex_lock(&qi->vq_lock); + vu_queue_push(dev, q, elem, tosend_len); + vu_queue_notify(dev, q); + pthread_mutex_unlock(&qi->vq_lock); + pthread_rwlock_unlock(&qi->virtio_dev->vu_dispatch_rwlock); + + req->reply_sent = true; + +err: + return ret; +} + +/* + * Callback from fuse_send_data_iov_* when it's virtio and the buffer + * is a single FD with FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK + * We need send the iov and then the buffer. + * Return 0 on success + */ +int virtio_send_data_iov(struct fuse_session *se, struct fuse_chan *ch, + struct iovec *iov, int count, struct fuse_bufvec *buf, + size_t len) +{ + FVRequest *req = container_of(ch, FVRequest, ch); + struct fv_QueueInfo *qi = ch->qi; + VuDev *dev = &se->virtio_dev->dev; + VuVirtq *q = vu_get_queue(dev, qi->qidx); + VuVirtqElement *elem = &req->elem; + int ret = 0; + + assert(count >= 1); + assert(iov[0].iov_len >= sizeof(struct fuse_out_header)); + + struct fuse_out_header *out = iov[0].iov_base; + /* TODO: Endianness! */ + + size_t iov_len = iov_size(iov, count); + size_t tosend_len = iov_len + len; + + out->len = tosend_len; + + fuse_log(FUSE_LOG_DEBUG, "%s: count=%d len=%zd iov_len=%zd\n", __func__, + count, len, iov_len); + + /* unique == 0 is notification which we don't support */ + assert(out->unique); + + assert(!req->reply_sent); + + /* The 'in' part of the elem is to qemu */ + unsigned int in_num = elem->in_num; + struct iovec *in_sg = elem->in_sg; + size_t in_len = iov_size(in_sg, in_num); + fuse_log(FUSE_LOG_DEBUG, "%s: elem %d: with %d in desc of length %zd\n", + __func__, elem->index, in_num, in_len); + + /* + * The elem should have room for a 'fuse_out_header' (out from fuse) + * plus the data based on the len in the header. + */ + if (in_len < sizeof(struct fuse_out_header)) { + fuse_log(FUSE_LOG_ERR, "%s: elem %d too short for out_header\n", + __func__, elem->index); + ret = E2BIG; + goto err; + } + if (in_len < tosend_len) { + fuse_log(FUSE_LOG_ERR, "%s: elem %d too small for data len %zd\n", + __func__, elem->index, tosend_len); + ret = E2BIG; + goto err; + } + + /* TODO: Limit to 'len' */ + + /* First copy the header data from iov->in_sg */ + copy_iov(iov, count, in_sg, in_num, iov_len); + + /* + * Build a copy of the the in_sg iov so we can skip bits in it, + * including changing the offsets + */ + struct iovec *in_sg_cpy = calloc(sizeof(struct iovec), in_num); + assert(in_sg_cpy); + memcpy(in_sg_cpy, in_sg, sizeof(struct iovec) * in_num); + /* These get updated as we skip */ + struct iovec *in_sg_ptr = in_sg_cpy; + int in_sg_cpy_count = in_num; + + /* skip over parts of in_sg that contained the header iov */ + size_t skip_size = iov_len; + + size_t in_sg_left = 0; + do { + while (skip_size != 0 && in_sg_cpy_count) { + if (skip_size >= in_sg_ptr[0].iov_len) { + skip_size -= in_sg_ptr[0].iov_len; + in_sg_ptr++; + in_sg_cpy_count--; + } else { + in_sg_ptr[0].iov_len -= skip_size; + in_sg_ptr[0].iov_base += skip_size; + break; + } + } + + int i; + for (i = 0, in_sg_left = 0; i < in_sg_cpy_count; i++) { + in_sg_left += in_sg_ptr[i].iov_len; + } + fuse_log(FUSE_LOG_DEBUG, + "%s: after skip skip_size=%zd in_sg_cpy_count=%d " + "in_sg_left=%zd\n", + __func__, skip_size, in_sg_cpy_count, in_sg_left); + ret = preadv(buf->buf[0].fd, in_sg_ptr, in_sg_cpy_count, + buf->buf[0].pos); + + if (ret == -1) { + ret = errno; + fuse_log(FUSE_LOG_DEBUG, "%s: preadv failed (%m) len=%zd\n", + __func__, len); + free(in_sg_cpy); + goto err; + } + fuse_log(FUSE_LOG_DEBUG, "%s: preadv ret=%d len=%zd\n", __func__, + ret, len); + if (ret < len && ret) { + fuse_log(FUSE_LOG_DEBUG, "%s: ret < len\n", __func__); + /* Skip over this much next time around */ + skip_size = ret; + buf->buf[0].pos += ret; + len -= ret; + + /* Lets do another read */ + continue; + } + if (!ret) { + /* EOF case? */ + fuse_log(FUSE_LOG_DEBUG, "%s: !ret in_sg_left=%zd\n", __func__, + in_sg_left); + break; + } + if (ret != len) { + fuse_log(FUSE_LOG_DEBUG, "%s: ret!=len\n", __func__); + ret = EIO; + free(in_sg_cpy); + goto err; + } + in_sg_left -= ret; + len -= ret; + } while (in_sg_left); + free(in_sg_cpy); + + /* Need to fix out->len on EOF */ + if (len) { + struct fuse_out_header *out_sg = in_sg[0].iov_base; + + tosend_len -= len; + out_sg->len = tosend_len; + } + + ret = 0; + + pthread_rwlock_rdlock(&qi->virtio_dev->vu_dispatch_rwlock); + pthread_mutex_lock(&qi->vq_lock); + vu_queue_push(dev, q, elem, tosend_len); + vu_queue_notify(dev, q); + pthread_mutex_unlock(&qi->vq_lock); + pthread_rwlock_unlock(&qi->virtio_dev->vu_dispatch_rwlock); + +err: + if (ret == 0) { + req->reply_sent = true; + } + + return ret; +} + +static __thread bool clone_fs_called; + +/* Process one FVRequest in a thread pool */ +static void fv_queue_worker(gpointer data, gpointer user_data) +{ + struct fv_QueueInfo *qi = user_data; + struct fuse_session *se = qi->virtio_dev->se; + struct VuDev *dev = &qi->virtio_dev->dev; + FVRequest *req = data; + VuVirtqElement *elem = &req->elem; + struct fuse_buf fbuf = {}; + bool allocated_bufv = false; + struct fuse_bufvec bufv; + struct fuse_bufvec *pbufv; + + assert(se->bufsize > sizeof(struct fuse_in_header)); + + if (!clone_fs_called) { + int ret; + + /* unshare FS for xattr operation */ + ret = unshare(CLONE_FS); + /* should not fail */ + assert(ret == 0); + + clone_fs_called = true; + } + + /* + * An element contains one request and the space to send our response + * They're spread over multiple descriptors in a scatter/gather set + * and we can't trust the guest to keep them still; so copy in/out. + */ + fbuf.mem = malloc(se->bufsize); + assert(fbuf.mem); + + fuse_mutex_init(&req->ch.lock); + req->ch.fd = -1; + req->ch.qi = qi; + + /* The 'out' part of the elem is from qemu */ + unsigned int out_num = elem->out_num; + struct iovec *out_sg = elem->out_sg; + size_t out_len = iov_size(out_sg, out_num); + fuse_log(FUSE_LOG_DEBUG, + "%s: elem %d: with %d out desc of length %zd\n", + __func__, elem->index, out_num, out_len); + + /* + * The elem should contain a 'fuse_in_header' (in to fuse) + * plus the data based on the len in the header. + */ + if (out_len < sizeof(struct fuse_in_header)) { + fuse_log(FUSE_LOG_ERR, "%s: elem %d too short for in_header\n", + __func__, elem->index); + assert(0); /* TODO */ + } + if (out_len > se->bufsize) { + fuse_log(FUSE_LOG_ERR, "%s: elem %d too large for buffer\n", __func__, + elem->index); + assert(0); /* TODO */ + } + /* Copy just the first element and look at it */ + copy_from_iov(&fbuf, 1, out_sg); + + pbufv = NULL; /* Compiler thinks an unitialised path */ + if (out_num > 2 && + out_sg[0].iov_len == sizeof(struct fuse_in_header) && + ((struct fuse_in_header *)fbuf.mem)->opcode == FUSE_WRITE && + out_sg[1].iov_len == sizeof(struct fuse_write_in)) { + /* + * For a write we don't actually need to copy the + * data, we can just do it straight out of guest memory + * but we must still copy the headers in case the guest + * was nasty and changed them while we were using them. + */ + fuse_log(FUSE_LOG_DEBUG, "%s: Write special case\n", __func__); + + /* copy the fuse_write_in header afte rthe fuse_in_header */ + fbuf.mem += out_sg->iov_len; + copy_from_iov(&fbuf, 1, out_sg + 1); + fbuf.mem -= out_sg->iov_len; + fbuf.size = out_sg[0].iov_len + out_sg[1].iov_len; + + /* Allocate the bufv, with space for the rest of the iov */ + pbufv = malloc(sizeof(struct fuse_bufvec) + + sizeof(struct fuse_buf) * (out_num - 2)); + if (!pbufv) { + fuse_log(FUSE_LOG_ERR, "%s: pbufv malloc failed\n", + __func__); + goto out; + } + + allocated_bufv = true; + pbufv->count = 1; + pbufv->buf[0] = fbuf; + + size_t iovindex, pbufvindex; + iovindex = 2; /* 2 headers, separate iovs */ + pbufvindex = 1; /* 2 headers, 1 fusebuf */ + + for (; iovindex < out_num; iovindex++, pbufvindex++) { + pbufv->count++; + pbufv->buf[pbufvindex].pos = ~0; /* Dummy */ + pbufv->buf[pbufvindex].flags = 0; + pbufv->buf[pbufvindex].mem = out_sg[iovindex].iov_base; + pbufv->buf[pbufvindex].size = out_sg[iovindex].iov_len; + } + } else { + /* Normal (non fast write) path */ + + /* Copy the rest of the buffer */ + fbuf.mem += out_sg->iov_len; + copy_from_iov(&fbuf, out_num - 1, out_sg + 1); + fbuf.mem -= out_sg->iov_len; + fbuf.size = out_len; + + /* TODO! Endianness of header */ + + /* TODO: Add checks for fuse_session_exited */ + bufv.buf[0] = fbuf; + bufv.count = 1; + pbufv = &bufv; + } + pbufv->idx = 0; + pbufv->off = 0; + fuse_session_process_buf_int(se, pbufv, &req->ch); + +out: + if (allocated_bufv) { + free(pbufv); + } + + /* If the request has no reply, still recycle the virtqueue element */ + if (!req->reply_sent) { + struct VuVirtq *q = vu_get_queue(dev, qi->qidx); + + fuse_log(FUSE_LOG_DEBUG, "%s: elem %d no reply sent\n", __func__, + elem->index); + + pthread_rwlock_rdlock(&qi->virtio_dev->vu_dispatch_rwlock); + pthread_mutex_lock(&qi->vq_lock); + vu_queue_push(dev, q, elem, 0); + vu_queue_notify(dev, q); + pthread_mutex_unlock(&qi->vq_lock); + pthread_rwlock_unlock(&qi->virtio_dev->vu_dispatch_rwlock); + } + + pthread_mutex_destroy(&req->ch.lock); + free(fbuf.mem); + free(req); +} + +/* Thread function for individual queues, created when a queue is 'started' */ +static void *fv_queue_thread(void *opaque) +{ + struct fv_QueueInfo *qi = opaque; + struct VuDev *dev = &qi->virtio_dev->dev; + struct VuVirtq *q = vu_get_queue(dev, qi->qidx); + struct fuse_session *se = qi->virtio_dev->se; + GThreadPool *pool; + + pool = g_thread_pool_new(fv_queue_worker, qi, se->thread_pool_size, TRUE, + NULL); + if (!pool) { + fuse_log(FUSE_LOG_ERR, "%s: g_thread_pool_new failed\n", __func__); + return NULL; + } + + fuse_log(FUSE_LOG_INFO, "%s: Start for queue %d kick_fd %d\n", __func__, + qi->qidx, qi->kick_fd); + while (1) { + struct pollfd pf[2]; + int ret; + + pf[0].fd = qi->kick_fd; + pf[0].events = POLLIN; + pf[0].revents = 0; + pf[1].fd = qi->kill_fd; + pf[1].events = POLLIN; + pf[1].revents = 0; + + fuse_log(FUSE_LOG_DEBUG, "%s: Waiting for Queue %d event\n", __func__, + qi->qidx); + int poll_res = ppoll(pf, 2, NULL, NULL); + + if (poll_res == -1) { + if (errno == EINTR) { + fuse_log(FUSE_LOG_INFO, "%s: ppoll interrupted, going around\n", + __func__); + continue; + } + fuse_log(FUSE_LOG_ERR, "fv_queue_thread ppoll: %m\n"); + break; + } + assert(poll_res >= 1); + if (pf[0].revents & (POLLERR | POLLHUP | POLLNVAL)) { + fuse_log(FUSE_LOG_ERR, "%s: Unexpected poll revents %x Queue %d\n", + __func__, pf[0].revents, qi->qidx); + break; + } + if (pf[1].revents & (POLLERR | POLLHUP | POLLNVAL)) { + fuse_log(FUSE_LOG_ERR, + "%s: Unexpected poll revents %x Queue %d killfd\n", + __func__, pf[1].revents, qi->qidx); + break; + } + if (pf[1].revents) { + fuse_log(FUSE_LOG_INFO, "%s: kill event on queue %d - quitting\n", + __func__, qi->qidx); + break; + } + assert(pf[0].revents & POLLIN); + fuse_log(FUSE_LOG_DEBUG, "%s: Got queue event on Queue %d\n", __func__, + qi->qidx); + + eventfd_t evalue; + if (eventfd_read(qi->kick_fd, &evalue)) { + fuse_log(FUSE_LOG_ERR, "Eventfd_read for queue: %m\n"); + break; + } + /* Mutual exclusion with virtio_loop() */ + ret = pthread_rwlock_rdlock(&qi->virtio_dev->vu_dispatch_rwlock); + assert(ret == 0); /* there is no possible error case */ + pthread_mutex_lock(&qi->vq_lock); + /* out is from guest, in is too guest */ + unsigned int in_bytes, out_bytes; + vu_queue_get_avail_bytes(dev, q, &in_bytes, &out_bytes, ~0, ~0); + + fuse_log(FUSE_LOG_DEBUG, + "%s: Queue %d gave evalue: %zx available: in: %u out: %u\n", + __func__, qi->qidx, (size_t)evalue, in_bytes, out_bytes); + + while (1) { + FVRequest *req = vu_queue_pop(dev, q, sizeof(FVRequest)); + if (!req) { + break; + } + + req->reply_sent = false; + + g_thread_pool_push(pool, req, NULL); + } + + pthread_mutex_unlock(&qi->vq_lock); + pthread_rwlock_unlock(&qi->virtio_dev->vu_dispatch_rwlock); + } + + g_thread_pool_free(pool, FALSE, TRUE); + + return NULL; +} + +static void fv_queue_cleanup_thread(struct fv_VuDev *vud, int qidx) +{ + int ret; + struct fv_QueueInfo *ourqi; + + assert(qidx < vud->nqueues); + ourqi = vud->qi[qidx]; + + /* Kill the thread */ + if (eventfd_write(ourqi->kill_fd, 1)) { + fuse_log(FUSE_LOG_ERR, "Eventfd_write for queue %d: %s\n", + qidx, strerror(errno)); + } + ret = pthread_join(ourqi->thread, NULL); + if (ret) { + fuse_log(FUSE_LOG_ERR, "%s: Failed to join thread idx %d err %d\n", + __func__, qidx, ret); + } + pthread_mutex_destroy(&ourqi->vq_lock); + close(ourqi->kill_fd); + ourqi->kick_fd = -1; + free(vud->qi[qidx]); + vud->qi[qidx] = NULL; +} + +/* Callback from libvhost-user on start or stop of a queue */ +static void fv_queue_set_started(VuDev *dev, int qidx, bool started) +{ + struct fv_VuDev *vud = container_of(dev, struct fv_VuDev, dev); + struct fv_QueueInfo *ourqi; + + fuse_log(FUSE_LOG_INFO, "%s: qidx=%d started=%d\n", __func__, qidx, + started); + assert(qidx >= 0); + + /* + * Ignore additional request queues for now. passthrough_ll.c must be + * audited for thread-safety issues first. It was written with a + * well-behaved client in mind and may not protect against all types of + * races yet. + */ + if (qidx > 1) { + fuse_log(FUSE_LOG_ERR, + "%s: multiple request queues not yet implemented, please only " + "configure 1 request queue\n", + __func__); + exit(EXIT_FAILURE); + } + + if (started) { + /* Fire up a thread to watch this queue */ + if (qidx >= vud->nqueues) { + vud->qi = realloc(vud->qi, (qidx + 1) * sizeof(vud->qi[0])); + assert(vud->qi); + memset(vud->qi + vud->nqueues, 0, + sizeof(vud->qi[0]) * (1 + (qidx - vud->nqueues))); + vud->nqueues = qidx + 1; + } + if (!vud->qi[qidx]) { + vud->qi[qidx] = calloc(sizeof(struct fv_QueueInfo), 1); + assert(vud->qi[qidx]); + vud->qi[qidx]->virtio_dev = vud; + vud->qi[qidx]->qidx = qidx; + } else { + /* Shouldn't have been started */ + assert(vud->qi[qidx]->kick_fd == -1); + } + ourqi = vud->qi[qidx]; + ourqi->kick_fd = dev->vq[qidx].kick_fd; + + ourqi->kill_fd = eventfd(0, EFD_CLOEXEC | EFD_SEMAPHORE); + assert(ourqi->kill_fd != -1); + pthread_mutex_init(&ourqi->vq_lock, NULL); + + if (pthread_create(&ourqi->thread, NULL, fv_queue_thread, ourqi)) { + fuse_log(FUSE_LOG_ERR, "%s: Failed to create thread for queue %d\n", + __func__, qidx); + assert(0); + } + } else { + fv_queue_cleanup_thread(vud, qidx); + } +} + +static bool fv_queue_order(VuDev *dev, int qidx) +{ + return false; +} + +static const VuDevIface fv_iface = { + .get_features = fv_get_features, + .set_features = fv_set_features, + + /* Don't need process message, we've not got any at vhost-user level */ + .queue_set_started = fv_queue_set_started, + + .queue_is_processed_in_order = fv_queue_order, +}; + +/* + * Main loop; this mostly deals with events on the vhost-user + * socket itself, and not actual fuse data. + */ +int virtio_loop(struct fuse_session *se) +{ + fuse_log(FUSE_LOG_INFO, "%s: Entry\n", __func__); + + while (!fuse_session_exited(se)) { + struct pollfd pf[1]; + bool ok; + int ret; + pf[0].fd = se->vu_socketfd; + pf[0].events = POLLIN; + pf[0].revents = 0; + + fuse_log(FUSE_LOG_DEBUG, "%s: Waiting for VU event\n", __func__); + int poll_res = ppoll(pf, 1, NULL, NULL); + + if (poll_res == -1) { + if (errno == EINTR) { + fuse_log(FUSE_LOG_INFO, "%s: ppoll interrupted, going around\n", + __func__); + continue; + } + fuse_log(FUSE_LOG_ERR, "virtio_loop ppoll: %m\n"); + break; + } + assert(poll_res == 1); + if (pf[0].revents & (POLLERR | POLLHUP | POLLNVAL)) { + fuse_log(FUSE_LOG_ERR, "%s: Unexpected poll revents %x\n", __func__, + pf[0].revents); + break; + } + assert(pf[0].revents & POLLIN); + fuse_log(FUSE_LOG_DEBUG, "%s: Got VU event\n", __func__); + /* Mutual exclusion with fv_queue_thread() */ + ret = pthread_rwlock_wrlock(&se->virtio_dev->vu_dispatch_rwlock); + assert(ret == 0); /* there is no possible error case */ + + ok = vu_dispatch(&se->virtio_dev->dev); + + pthread_rwlock_unlock(&se->virtio_dev->vu_dispatch_rwlock); + + if (!ok) { + fuse_log(FUSE_LOG_ERR, "%s: vu_dispatch failed\n", __func__); + break; + } + } + + /* + * Make sure all fv_queue_thread()s quit on exit, as we're about to + * free virtio dev and fuse session, no one should access them anymore. + */ + for (int i = 0; i < se->virtio_dev->nqueues; i++) { + if (!se->virtio_dev->qi[i]) { + continue; + } + + fuse_log(FUSE_LOG_INFO, "%s: Stopping queue %d thread\n", __func__, i); + fv_queue_cleanup_thread(se->virtio_dev, i); + } + + fuse_log(FUSE_LOG_INFO, "%s: Exit\n", __func__); + + return 0; +} + +static void strreplace(char *s, char old, char new) +{ + for (; *s; ++s) { + if (*s == old) { + *s = new; + } + } +} + +static bool fv_socket_lock(struct fuse_session *se) +{ + g_autofree gchar *sk_name = NULL; + g_autofree gchar *pidfile = NULL; + g_autofree gchar *dir = NULL; + Error *local_err = NULL; + + dir = qemu_get_local_state_pathname("run/virtiofsd"); + + if (g_mkdir_with_parents(dir, S_IRWXU) < 0) { + fuse_log(FUSE_LOG_ERR, "%s: Failed to create directory %s: %s", + __func__, dir, strerror(errno)); + return false; + } + + sk_name = g_strdup(se->vu_socket_path); + strreplace(sk_name, '/', '.'); + pidfile = g_strdup_printf("%s/%s.pid", dir, sk_name); + + if (!qemu_write_pidfile(pidfile, &local_err)) { + error_report_err(local_err); + return false; + } + + return true; +} + +static int fv_create_listen_socket(struct fuse_session *se) +{ + struct sockaddr_un un; + mode_t old_umask; + + /* Nothing to do if fd is already initialized */ + if (se->vu_listen_fd >= 0) { + return 0; + } + + if (strlen(se->vu_socket_path) >= sizeof(un.sun_path)) { + fuse_log(FUSE_LOG_ERR, "Socket path too long\n"); + return -1; + } + + if (!strlen(se->vu_socket_path)) { + fuse_log(FUSE_LOG_ERR, "Socket path is empty\n"); + return -1; + } + + /* Check the vu_socket_path is already used */ + if (!fv_socket_lock(se)) { + return -1; + } + + /* + * Create the Unix socket to communicate with qemu + * based on QEMU's vhost-user-bridge + */ + unlink(se->vu_socket_path); + strcpy(un.sun_path, se->vu_socket_path); + size_t addr_len = sizeof(un); + + int listen_sock = socket(AF_UNIX, SOCK_STREAM, 0); + if (listen_sock == -1) { + fuse_log(FUSE_LOG_ERR, "vhost socket creation: %m\n"); + return -1; + } + un.sun_family = AF_UNIX; + + /* + * Unfortunately bind doesn't let you set the mask on the socket, + * so set umask to 077 and restore it later. + */ + 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; + } + umask(old_umask); + + if (listen(listen_sock, 1) == -1) { + fuse_log(FUSE_LOG_ERR, "vhost socket listen: %m\n"); + close(listen_sock); + return -1; + } + + se->vu_listen_fd = listen_sock; + return 0; +} + +int virtio_session_mount(struct fuse_session *se) +{ + int ret; + + ret = fv_create_listen_socket(se); + if (ret < 0) { + return ret; + } + + se->fd = -1; + + fuse_log(FUSE_LOG_INFO, "%s: Waiting for vhost-user socket connection...\n", + __func__); + int data_sock = accept(se->vu_listen_fd, NULL, NULL); + if (data_sock == -1) { + fuse_log(FUSE_LOG_ERR, "vhost socket accept: %m\n"); + close(se->vu_listen_fd); + return -1; + } + close(se->vu_listen_fd); + se->vu_listen_fd = -1; + fuse_log(FUSE_LOG_INFO, "%s: Received vhost-user socket connection\n", + __func__); + + /* TODO: Some cleanup/deallocation! */ + se->virtio_dev = calloc(sizeof(struct fv_VuDev), 1); + if (!se->virtio_dev) { + fuse_log(FUSE_LOG_ERR, "%s: virtio_dev calloc failed\n", __func__); + close(data_sock); + return -1; + } + + se->vu_socketfd = data_sock; + se->virtio_dev->se = se; + pthread_rwlock_init(&se->virtio_dev->vu_dispatch_rwlock, NULL); + vu_init(&se->virtio_dev->dev, 2, se->vu_socketfd, fv_panic, fv_set_watch, + fv_remove_watch, &fv_iface); + + return 0; +} + +void virtio_session_close(struct fuse_session *se) +{ + close(se->vu_socketfd); + + if (!se->virtio_dev) { + return; + } + + free(se->virtio_dev->qi); + pthread_rwlock_destroy(&se->virtio_dev->vu_dispatch_rwlock); + free(se->virtio_dev); + se->virtio_dev = NULL; +} diff --git a/tools/virtiofsd/fuse_virtio.h b/tools/virtiofsd/fuse_virtio.h new file mode 100644 index 0000000000..111684032c --- /dev/null +++ b/tools/virtiofsd/fuse_virtio.h @@ -0,0 +1,33 @@ +/* + * virtio-fs glue for FUSE + * Copyright (C) 2018 Red Hat, Inc. and/or its affiliates + * + * Authors: + * Dave Gilbert + * + * Implements the glue between libfuse and libvhost-user + * + * This program can be distributed under the terms of the GNU LGPLv2. + * See the file COPYING.LIB + */ + +#ifndef FUSE_VIRTIO_H +#define FUSE_VIRTIO_H + +#include "fuse_i.h" + +struct fuse_session; + +int virtio_session_mount(struct fuse_session *se); +void virtio_session_close(struct fuse_session *se); +int virtio_loop(struct fuse_session *se); + + +int virtio_send_msg(struct fuse_session *se, struct fuse_chan *ch, + struct iovec *iov, int count); + +int virtio_send_data_iov(struct fuse_session *se, struct fuse_chan *ch, + struct iovec *iov, int count, + struct fuse_bufvec *buf, size_t len); + +#endif diff --git a/tools/virtiofsd/helper.c b/tools/virtiofsd/helper.c new file mode 100644 index 0000000000..819c2bc13c --- /dev/null +++ b/tools/virtiofsd/helper.c @@ -0,0 +1,349 @@ +/* + * FUSE: Filesystem in Userspace + * Copyright (C) 2001-2007 Miklos Szeredi + * + * Helper functions to create (simple) standalone programs. With the + * aid of these functions it should be possible to create full FUSE + * file system by implementing nothing but the request handlers. + + * This program can be distributed under the terms of the GNU LGPLv2. + * See the file COPYING.LIB. + */ + +#include "qemu/osdep.h" +#include "fuse_i.h" +#include "fuse_lowlevel.h" +#include "fuse_misc.h" +#include "fuse_opt.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#define FUSE_HELPER_OPT(t, p) \ + { \ + t, offsetof(struct fuse_cmdline_opts, p), 1 \ + } +#define FUSE_HELPER_OPT_VALUE(t, p, v) \ + { \ + t, offsetof(struct fuse_cmdline_opts, p), v \ + } + +static const struct fuse_opt fuse_helper_opts[] = { + FUSE_HELPER_OPT("-h", show_help), + FUSE_HELPER_OPT("--help", show_help), + FUSE_HELPER_OPT("-V", show_version), + FUSE_HELPER_OPT("--version", show_version), + FUSE_HELPER_OPT("--print-capabilities", print_capabilities), + FUSE_HELPER_OPT("-d", debug), + FUSE_HELPER_OPT("debug", debug), + FUSE_HELPER_OPT("-d", foreground), + FUSE_HELPER_OPT("debug", foreground), + FUSE_OPT_KEY("-d", FUSE_OPT_KEY_KEEP), + FUSE_OPT_KEY("debug", FUSE_OPT_KEY_KEEP), + FUSE_HELPER_OPT("-f", foreground), + FUSE_HELPER_OPT_VALUE("--daemonize", foreground, 0), + FUSE_HELPER_OPT("fsname=", nodefault_subtype), + FUSE_OPT_KEY("fsname=", FUSE_OPT_KEY_KEEP), + FUSE_HELPER_OPT("subtype=", nodefault_subtype), + FUSE_OPT_KEY("subtype=", FUSE_OPT_KEY_KEEP), + FUSE_HELPER_OPT("max_idle_threads=%u", max_idle_threads), + FUSE_HELPER_OPT("--syslog", syslog), + FUSE_HELPER_OPT_VALUE("log_level=debug", log_level, FUSE_LOG_DEBUG), + FUSE_HELPER_OPT_VALUE("log_level=info", log_level, FUSE_LOG_INFO), + FUSE_HELPER_OPT_VALUE("log_level=warn", log_level, FUSE_LOG_WARNING), + FUSE_HELPER_OPT_VALUE("log_level=err", log_level, FUSE_LOG_ERR), + FUSE_OPT_END +}; + +struct fuse_conn_info_opts { + int atomic_o_trunc; + int no_remote_posix_lock; + int no_remote_flock; + int splice_write; + int splice_move; + int splice_read; + int no_splice_write; + int no_splice_move; + int no_splice_read; + int auto_inval_data; + int no_auto_inval_data; + int no_readdirplus; + int no_readdirplus_auto; + int async_dio; + int no_async_dio; + int writeback_cache; + int no_writeback_cache; + int async_read; + int sync_read; + unsigned max_write; + unsigned max_readahead; + unsigned max_background; + unsigned congestion_threshold; + unsigned time_gran; + int set_max_write; + int set_max_readahead; + int set_max_background; + int set_congestion_threshold; + int set_time_gran; +}; + +#define CONN_OPTION(t, p, v) \ + { \ + t, offsetof(struct fuse_conn_info_opts, p), v \ + } +static const struct fuse_opt conn_info_opt_spec[] = { + CONN_OPTION("max_write=%u", max_write, 0), + CONN_OPTION("max_write=", set_max_write, 1), + CONN_OPTION("max_readahead=%u", max_readahead, 0), + CONN_OPTION("max_readahead=", set_max_readahead, 1), + CONN_OPTION("max_background=%u", max_background, 0), + CONN_OPTION("max_background=", set_max_background, 1), + CONN_OPTION("congestion_threshold=%u", congestion_threshold, 0), + CONN_OPTION("congestion_threshold=", set_congestion_threshold, 1), + CONN_OPTION("sync_read", sync_read, 1), + CONN_OPTION("async_read", async_read, 1), + CONN_OPTION("atomic_o_trunc", atomic_o_trunc, 1), + CONN_OPTION("no_remote_lock", no_remote_posix_lock, 1), + CONN_OPTION("no_remote_lock", no_remote_flock, 1), + CONN_OPTION("no_remote_flock", no_remote_flock, 1), + CONN_OPTION("no_remote_posix_lock", no_remote_posix_lock, 1), + CONN_OPTION("splice_write", splice_write, 1), + CONN_OPTION("no_splice_write", no_splice_write, 1), + CONN_OPTION("splice_move", splice_move, 1), + CONN_OPTION("no_splice_move", no_splice_move, 1), + CONN_OPTION("splice_read", splice_read, 1), + CONN_OPTION("no_splice_read", no_splice_read, 1), + CONN_OPTION("auto_inval_data", auto_inval_data, 1), + CONN_OPTION("no_auto_inval_data", no_auto_inval_data, 1), + CONN_OPTION("readdirplus=no", no_readdirplus, 1), + CONN_OPTION("readdirplus=yes", no_readdirplus, 0), + CONN_OPTION("readdirplus=yes", no_readdirplus_auto, 1), + CONN_OPTION("readdirplus=auto", no_readdirplus, 0), + CONN_OPTION("readdirplus=auto", no_readdirplus_auto, 0), + CONN_OPTION("async_dio", async_dio, 1), + CONN_OPTION("no_async_dio", no_async_dio, 1), + CONN_OPTION("writeback_cache", writeback_cache, 1), + CONN_OPTION("no_writeback_cache", no_writeback_cache, 1), + CONN_OPTION("time_gran=%u", time_gran, 0), + CONN_OPTION("time_gran=", set_time_gran, 1), + FUSE_OPT_END +}; + + +void fuse_cmdline_help(void) +{ + printf(" -h --help print help\n" + " -V --version print version\n" + " --print-capabilities print vhost-user.json\n" + " -d -o debug enable debug output (implies -f)\n" + " --syslog log to syslog (default stderr)\n" + " -f foreground operation\n" + " --daemonize run in background\n" + " -o cache= cache mode. could be one of \"auto, " + "always, none\"\n" + " default: auto\n" + " -o flock|no_flock enable/disable flock\n" + " default: no_flock\n" + " -o log_level= log level, default to \"info\"\n" + " level could be one of \"debug, " + "info, warn, err\"\n" + " -o max_idle_threads the maximum number of idle worker " + "threads\n" + " allowed (default: 10)\n" + " -o norace disable racy fallback\n" + " default: false\n" + " -o posix_lock|no_posix_lock\n" + " enable/disable remote posix lock\n" + " default: posix_lock\n" + " -o readdirplus|no_readdirplus\n" + " enable/disable readirplus\n" + " default: readdirplus except with " + "cache=none\n" + " -o timeout= I/O timeout (seconds)\n" + " default: depends on cache= option.\n" + " -o writeback|no_writeback enable/disable writeback cache\n" + " default: no_writeback\n" + " -o xattr|no_xattr enable/disable xattr\n" + " default: no_xattr\n" + ); +} + +static int fuse_helper_opt_proc(void *data, const char *arg, int key, + struct fuse_args *outargs) +{ + (void)data; + (void)outargs; + + switch (key) { + case FUSE_OPT_KEY_NONOPT: + fuse_log(FUSE_LOG_ERR, "fuse: invalid argument `%s'\n", arg); + return -1; + + default: + /* Pass through unknown options */ + return 1; + } +} + +int fuse_parse_cmdline(struct fuse_args *args, struct fuse_cmdline_opts *opts) +{ + memset(opts, 0, sizeof(struct fuse_cmdline_opts)); + + opts->max_idle_threads = 10; + opts->foreground = 1; + + if (fuse_opt_parse(args, opts, fuse_helper_opts, fuse_helper_opt_proc) == + -1) { + return -1; + } + + return 0; +} + + +int fuse_daemonize(int foreground) +{ + int ret = 0, rett; + if (!foreground) { + int nullfd; + int waiter[2]; + char completed; + + if (pipe(waiter)) { + fuse_log(FUSE_LOG_ERR, "fuse_daemonize: pipe: %s\n", + strerror(errno)); + return -1; + } + + /* + * demonize current process by forking it and killing the + * parent. This makes current process as a child of 'init'. + */ + switch (fork()) { + case -1: + fuse_log(FUSE_LOG_ERR, "fuse_daemonize: fork: %s\n", + strerror(errno)); + return -1; + case 0: + break; + default: + _exit(read(waiter[0], &completed, + sizeof(completed) != sizeof(completed))); + } + + if (setsid() == -1) { + fuse_log(FUSE_LOG_ERR, "fuse_daemonize: setsid: %s\n", + strerror(errno)); + return -1; + } + + ret = chdir("/"); + + nullfd = open("/dev/null", O_RDWR, 0); + if (nullfd != -1) { + rett = dup2(nullfd, 0); + if (!ret) { + ret = rett; + } + rett = dup2(nullfd, 1); + if (!ret) { + ret = rett; + } + rett = dup2(nullfd, 2); + if (!ret) { + ret = rett; + } + if (nullfd > 2) { + close(nullfd); + } + } + + /* Propagate completion of daemon initialization */ + completed = 1; + rett = write(waiter[1], &completed, sizeof(completed)); + if (!ret) { + ret = rett; + } + close(waiter[0]); + close(waiter[1]); + } else { + ret = chdir("/"); + } + return ret; +} + +void fuse_apply_conn_info_opts(struct fuse_conn_info_opts *opts, + struct fuse_conn_info *conn) +{ + if (opts->set_max_write) { + conn->max_write = opts->max_write; + } + if (opts->set_max_background) { + conn->max_background = opts->max_background; + } + if (opts->set_congestion_threshold) { + conn->congestion_threshold = opts->congestion_threshold; + } + if (opts->set_time_gran) { + conn->time_gran = opts->time_gran; + } + if (opts->set_max_readahead) { + conn->max_readahead = opts->max_readahead; + } + +#define LL_ENABLE(cond, cap) \ + if (cond) \ + conn->want |= (cap) +#define LL_DISABLE(cond, cap) \ + if (cond) \ + conn->want &= ~(cap) + + LL_ENABLE(opts->splice_read, FUSE_CAP_SPLICE_READ); + LL_DISABLE(opts->no_splice_read, FUSE_CAP_SPLICE_READ); + + LL_ENABLE(opts->splice_write, FUSE_CAP_SPLICE_WRITE); + LL_DISABLE(opts->no_splice_write, FUSE_CAP_SPLICE_WRITE); + + LL_ENABLE(opts->splice_move, FUSE_CAP_SPLICE_MOVE); + LL_DISABLE(opts->no_splice_move, FUSE_CAP_SPLICE_MOVE); + + LL_ENABLE(opts->auto_inval_data, FUSE_CAP_AUTO_INVAL_DATA); + LL_DISABLE(opts->no_auto_inval_data, FUSE_CAP_AUTO_INVAL_DATA); + + LL_DISABLE(opts->no_readdirplus, FUSE_CAP_READDIRPLUS); + LL_DISABLE(opts->no_readdirplus_auto, FUSE_CAP_READDIRPLUS_AUTO); + + LL_ENABLE(opts->async_dio, FUSE_CAP_ASYNC_DIO); + LL_DISABLE(opts->no_async_dio, FUSE_CAP_ASYNC_DIO); + + LL_ENABLE(opts->writeback_cache, FUSE_CAP_WRITEBACK_CACHE); + LL_DISABLE(opts->no_writeback_cache, FUSE_CAP_WRITEBACK_CACHE); + + LL_ENABLE(opts->async_read, FUSE_CAP_ASYNC_READ); + LL_DISABLE(opts->sync_read, FUSE_CAP_ASYNC_READ); + + LL_DISABLE(opts->no_remote_posix_lock, FUSE_CAP_POSIX_LOCKS); + LL_DISABLE(opts->no_remote_flock, FUSE_CAP_FLOCK_LOCKS); +} + +struct fuse_conn_info_opts *fuse_parse_conn_info_opts(struct fuse_args *args) +{ + struct fuse_conn_info_opts *opts; + + opts = calloc(1, sizeof(struct fuse_conn_info_opts)); + if (opts == NULL) { + fuse_log(FUSE_LOG_ERR, "calloc failed\n"); + return NULL; + } + if (fuse_opt_parse(args, opts, conn_info_opt_spec, NULL) == -1) { + free(opts); + return NULL; + } + return opts; +} diff --git a/tools/virtiofsd/passthrough_helpers.h b/tools/virtiofsd/passthrough_helpers.h new file mode 100644 index 0000000000..0b98275ed5 --- /dev/null +++ b/tools/virtiofsd/passthrough_helpers.h @@ -0,0 +1,51 @@ +/* + * FUSE: Filesystem in Userspace + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE + */ + +/* + * Creates files on the underlying file system in response to a FUSE_MKNOD + * operation + */ +static int mknod_wrapper(int dirfd, const char *path, const char *link, + int mode, dev_t rdev) +{ + int res; + + if (S_ISREG(mode)) { + res = openat(dirfd, path, O_CREAT | O_EXCL | O_WRONLY, mode); + if (res >= 0) { + res = close(res); + } + } else if (S_ISDIR(mode)) { + res = mkdirat(dirfd, path, mode); + } else if (S_ISLNK(mode) && link != NULL) { + res = symlinkat(link, dirfd, path); + } else if (S_ISFIFO(mode)) { + res = mkfifoat(dirfd, path, mode); + } else { + res = mknodat(dirfd, path, mode, rdev); + } + + return res; +} diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c new file mode 100644 index 0000000000..4c35c95b25 --- /dev/null +++ b/tools/virtiofsd/passthrough_ll.c @@ -0,0 +1,3003 @@ +/* + * FUSE: Filesystem in Userspace + * Copyright (C) 2001-2007 Miklos Szeredi + * + * This program can be distributed under the terms of the GNU GPLv2. + * See the file COPYING. + */ + +/* + * + * This file system mirrors the existing file system hierarchy of the + * system, starting at the root file system. This is implemented by + * just "passing through" all requests to the corresponding user-space + * libc functions. In contrast to passthrough.c and passthrough_fh.c, + * this implementation uses the low-level API. Its performance should + * be the least bad among the three, but many operations are not + * implemented. In particular, it is not possible to remove files (or + * directories) because the code necessary to defer actual removal + * until the file is not opened anymore would make the example much + * more complicated. + * + * When writeback caching is enabled (-o writeback mount option), it + * is only possible to write to files for which the mounting user has + * read permissions. This is because the writeback cache requires the + * kernel to be able to issue read requests for all files (which the + * passthrough filesystem cannot satisfy if it can't read the file in + * the underlying filesystem). + * + * Compile with: + * + * gcc -Wall passthrough_ll.c `pkg-config fuse3 --cflags --libs` -o + * passthrough_ll + * + * ## Source code ## + * \include passthrough_ll.c + */ + +#include "qemu/osdep.h" +#include "qemu/timer.h" +#include "fuse_virtio.h" +#include "fuse_log.h" +#include "fuse_lowlevel.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "passthrough_helpers.h" +#include "seccomp.h" + +/* Keep track of inode posix locks for each owner. */ +struct lo_inode_plock { + uint64_t lock_owner; + int fd; /* fd for OFD locks */ +}; + +struct lo_map_elem { + union { + struct lo_inode *inode; + struct lo_dirp *dirp; + int fd; + ssize_t freelist; + }; + bool in_use; +}; + +/* Maps FUSE fh or ino values to internal objects */ +struct lo_map { + struct lo_map_elem *elems; + size_t nelems; + ssize_t freelist; +}; + +struct lo_key { + ino_t ino; + dev_t dev; +}; + +struct lo_inode { + int fd; + + /* + * Atomic reference count for this object. The nlookup field holds a + * reference and release it when nlookup reaches 0. + */ + gint refcount; + + struct lo_key key; + + /* + * This counter keeps the inode alive during the FUSE session. + * Incremented when the FUSE inode number is sent in a reply + * (FUSE_LOOKUP, FUSE_READDIRPLUS, etc). Decremented when an inode is + * released by requests like FUSE_FORGET, FUSE_RMDIR, FUSE_RENAME, etc. + * + * Note that this value is untrusted because the client can manipulate + * it arbitrarily using FUSE_FORGET requests. + * + * Protected by lo->mutex. + */ + uint64_t nlookup; + + fuse_ino_t fuse_ino; + pthread_mutex_t plock_mutex; + GHashTable *posix_locks; /* protected by lo_inode->plock_mutex */ + + mode_t filetype; +}; + +struct lo_cred { + uid_t euid; + gid_t egid; +}; + +enum { + CACHE_NONE, + CACHE_AUTO, + CACHE_ALWAYS, +}; + +struct lo_data { + pthread_mutex_t mutex; + int debug; + int norace; + int writeback; + int flock; + int posix_lock; + int xattr; + char *source; + double timeout; + int cache; + int timeout_set; + int readdirplus_set; + int readdirplus_clear; + struct lo_inode root; + GHashTable *inodes; /* protected by lo->mutex */ + struct lo_map ino_map; /* protected by lo->mutex */ + struct lo_map dirp_map; /* protected by lo->mutex */ + struct lo_map fd_map; /* protected by lo->mutex */ + + /* An O_PATH file descriptor to /proc/self/fd/ */ + int proc_self_fd; +}; + +static const struct fuse_opt lo_opts[] = { + { "writeback", offsetof(struct lo_data, writeback), 1 }, + { "no_writeback", offsetof(struct lo_data, writeback), 0 }, + { "source=%s", offsetof(struct lo_data, source), 0 }, + { "flock", offsetof(struct lo_data, flock), 1 }, + { "no_flock", offsetof(struct lo_data, flock), 0 }, + { "posix_lock", offsetof(struct lo_data, posix_lock), 1 }, + { "no_posix_lock", offsetof(struct lo_data, posix_lock), 0 }, + { "xattr", offsetof(struct lo_data, xattr), 1 }, + { "no_xattr", offsetof(struct lo_data, xattr), 0 }, + { "timeout=%lf", offsetof(struct lo_data, timeout), 0 }, + { "timeout=", offsetof(struct lo_data, timeout_set), 1 }, + { "cache=none", offsetof(struct lo_data, cache), CACHE_NONE }, + { "cache=auto", offsetof(struct lo_data, cache), CACHE_AUTO }, + { "cache=always", offsetof(struct lo_data, cache), CACHE_ALWAYS }, + { "norace", offsetof(struct lo_data, norace), 1 }, + { "readdirplus", offsetof(struct lo_data, readdirplus_set), 1 }, + { "no_readdirplus", offsetof(struct lo_data, readdirplus_clear), 1 }, + FUSE_OPT_END +}; +static bool use_syslog = false; +static int current_log_level; +static void unref_inode_lolocked(struct lo_data *lo, struct lo_inode *inode, + uint64_t n); + +static struct { + pthread_mutex_t mutex; + void *saved; +} cap; +/* That we loaded cap-ng in the current thread from the saved */ +static __thread bool cap_loaded = 0; + +static struct lo_inode *lo_find(struct lo_data *lo, struct stat *st); + +static int is_dot_or_dotdot(const char *name) +{ + return name[0] == '.' && + (name[1] == '\0' || (name[1] == '.' && name[2] == '\0')); +} + +/* Is `path` a single path component that is not "." or ".."? */ +static int is_safe_path_component(const char *path) +{ + if (strchr(path, '/')) { + return 0; + } + + return !is_dot_or_dotdot(path); +} + +static struct lo_data *lo_data(fuse_req_t req) +{ + return (struct lo_data *)fuse_req_userdata(req); +} + +/* + * Load capng's state from our saved state if the current thread + * hadn't previously been loaded. + * returns 0 on success + */ +static int load_capng(void) +{ + if (!cap_loaded) { + pthread_mutex_lock(&cap.mutex); + capng_restore_state(&cap.saved); + /* + * restore_state free's the saved copy + * so make another. + */ + 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; + } + pthread_mutex_unlock(&cap.mutex); + + /* + * We want to use the loaded state for our pid, + * not the original + */ + capng_setpid(syscall(SYS_gettid)); + cap_loaded = true; + } + return 0; +} + +/* + * Helpers for dropping and regaining effective capabilities. Returns 0 + * on success, error otherwise + */ +static int drop_effective_cap(const char *cap_name, bool *cap_dropped) +{ + int cap, ret; + + cap = capng_name_to_capability(cap_name); + if (cap < 0) { + ret = errno; + fuse_log(FUSE_LOG_ERR, "capng_name_to_capability(%s) failed:%s\n", + cap_name, strerror(errno)); + goto out; + } + + if (load_capng()) { + ret = errno; + fuse_log(FUSE_LOG_ERR, "load_capng() failed\n"); + goto out; + } + + /* We dont have this capability in effective set already. */ + if (!capng_have_capability(CAPNG_EFFECTIVE, cap)) { + ret = 0; + goto out; + } + + if (capng_update(CAPNG_DROP, CAPNG_EFFECTIVE, cap)) { + ret = errno; + fuse_log(FUSE_LOG_ERR, "capng_update(DROP,) failed\n"); + goto out; + } + + if (capng_apply(CAPNG_SELECT_CAPS)) { + ret = errno; + fuse_log(FUSE_LOG_ERR, "drop:capng_apply() failed\n"); + goto out; + } + + ret = 0; + if (cap_dropped) { + *cap_dropped = true; + } + +out: + return ret; +} + +static int gain_effective_cap(const char *cap_name) +{ + int cap; + int ret = 0; + + cap = capng_name_to_capability(cap_name); + if (cap < 0) { + ret = errno; + fuse_log(FUSE_LOG_ERR, "capng_name_to_capability(%s) failed:%s\n", + cap_name, strerror(errno)); + goto out; + } + + if (load_capng()) { + ret = errno; + fuse_log(FUSE_LOG_ERR, "load_capng() failed\n"); + goto out; + } + + if (capng_update(CAPNG_ADD, CAPNG_EFFECTIVE, cap)) { + ret = errno; + fuse_log(FUSE_LOG_ERR, "capng_update(ADD,) failed\n"); + goto out; + } + + if (capng_apply(CAPNG_SELECT_CAPS)) { + ret = errno; + fuse_log(FUSE_LOG_ERR, "gain:capng_apply() failed\n"); + goto out; + } + ret = 0; + +out: + return ret; +} + +static void lo_map_init(struct lo_map *map) +{ + map->elems = NULL; + map->nelems = 0; + map->freelist = -1; +} + +static void lo_map_destroy(struct lo_map *map) +{ + free(map->elems); +} + +static int lo_map_grow(struct lo_map *map, size_t new_nelems) +{ + struct lo_map_elem *new_elems; + size_t i; + + if (new_nelems <= map->nelems) { + return 1; + } + + new_elems = realloc(map->elems, sizeof(map->elems[0]) * new_nelems); + if (!new_elems) { + return 0; + } + + for (i = map->nelems; i < new_nelems; i++) { + new_elems[i].freelist = i + 1; + new_elems[i].in_use = false; + } + new_elems[new_nelems - 1].freelist = -1; + + map->elems = new_elems; + map->freelist = map->nelems; + map->nelems = new_nelems; + return 1; +} + +static struct lo_map_elem *lo_map_alloc_elem(struct lo_map *map) +{ + struct lo_map_elem *elem; + + if (map->freelist == -1 && !lo_map_grow(map, map->nelems + 256)) { + return NULL; + } + + elem = &map->elems[map->freelist]; + map->freelist = elem->freelist; + + elem->in_use = true; + + return elem; +} + +static struct lo_map_elem *lo_map_reserve(struct lo_map *map, size_t key) +{ + ssize_t *prev; + + if (!lo_map_grow(map, key + 1)) { + return NULL; + } + + for (prev = &map->freelist; *prev != -1; + prev = &map->elems[*prev].freelist) { + if (*prev == key) { + struct lo_map_elem *elem = &map->elems[key]; + + *prev = elem->freelist; + elem->in_use = true; + return elem; + } + } + return NULL; +} + +static struct lo_map_elem *lo_map_get(struct lo_map *map, size_t key) +{ + if (key >= map->nelems) { + return NULL; + } + if (!map->elems[key].in_use) { + return NULL; + } + return &map->elems[key]; +} + +static void lo_map_remove(struct lo_map *map, size_t key) +{ + struct lo_map_elem *elem; + + if (key >= map->nelems) { + return; + } + + elem = &map->elems[key]; + if (!elem->in_use) { + return; + } + + elem->in_use = false; + + elem->freelist = map->freelist; + map->freelist = key; +} + +/* Assumes lo->mutex is held */ +static ssize_t lo_add_fd_mapping(fuse_req_t req, int fd) +{ + struct lo_map_elem *elem; + + elem = lo_map_alloc_elem(&lo_data(req)->fd_map); + if (!elem) { + return -1; + } + + elem->fd = fd; + return elem - lo_data(req)->fd_map.elems; +} + +/* Assumes lo->mutex is held */ +static ssize_t lo_add_dirp_mapping(fuse_req_t req, struct lo_dirp *dirp) +{ + struct lo_map_elem *elem; + + elem = lo_map_alloc_elem(&lo_data(req)->dirp_map); + if (!elem) { + return -1; + } + + elem->dirp = dirp; + return elem - lo_data(req)->dirp_map.elems; +} + +/* Assumes lo->mutex is held */ +static ssize_t lo_add_inode_mapping(fuse_req_t req, struct lo_inode *inode) +{ + struct lo_map_elem *elem; + + elem = lo_map_alloc_elem(&lo_data(req)->ino_map); + if (!elem) { + return -1; + } + + elem->inode = inode; + return elem - lo_data(req)->ino_map.elems; +} + +static void lo_inode_put(struct lo_data *lo, struct lo_inode **inodep) +{ + struct lo_inode *inode = *inodep; + + if (!inode) { + return; + } + + *inodep = NULL; + + if (g_atomic_int_dec_and_test(&inode->refcount)) { + close(inode->fd); + free(inode); + } +} + +/* Caller must release refcount using lo_inode_put() */ +static struct lo_inode *lo_inode(fuse_req_t req, fuse_ino_t ino) +{ + struct lo_data *lo = lo_data(req); + struct lo_map_elem *elem; + + pthread_mutex_lock(&lo->mutex); + elem = lo_map_get(&lo->ino_map, ino); + if (elem) { + g_atomic_int_inc(&elem->inode->refcount); + } + pthread_mutex_unlock(&lo->mutex); + + if (!elem) { + return NULL; + } + + return elem->inode; +} + +/* + * TODO Remove this helper and force callers to hold an inode refcount until + * they are done with the fd. This will be done in a later patch to make + * review easier. + */ +static int lo_fd(fuse_req_t req, fuse_ino_t ino) +{ + struct lo_inode *inode = lo_inode(req, ino); + int fd; + + if (!inode) { + return -1; + } + + fd = inode->fd; + lo_inode_put(lo_data(req), &inode); + return fd; +} + +static void lo_init(void *userdata, struct fuse_conn_info *conn) +{ + struct lo_data *lo = (struct lo_data *)userdata; + + if (conn->capable & FUSE_CAP_EXPORT_SUPPORT) { + conn->want |= FUSE_CAP_EXPORT_SUPPORT; + } + + if (lo->writeback && conn->capable & FUSE_CAP_WRITEBACK_CACHE) { + fuse_log(FUSE_LOG_DEBUG, "lo_init: activating writeback\n"); + conn->want |= FUSE_CAP_WRITEBACK_CACHE; + } + if (conn->capable & FUSE_CAP_FLOCK_LOCKS) { + if (lo->flock) { + fuse_log(FUSE_LOG_DEBUG, "lo_init: activating flock locks\n"); + conn->want |= FUSE_CAP_FLOCK_LOCKS; + } else { + fuse_log(FUSE_LOG_DEBUG, "lo_init: disabling flock locks\n"); + conn->want &= ~FUSE_CAP_FLOCK_LOCKS; + } + } + + if (conn->capable & FUSE_CAP_POSIX_LOCKS) { + if (lo->posix_lock) { + fuse_log(FUSE_LOG_DEBUG, "lo_init: activating posix locks\n"); + conn->want |= FUSE_CAP_POSIX_LOCKS; + } else { + fuse_log(FUSE_LOG_DEBUG, "lo_init: disabling posix locks\n"); + conn->want &= ~FUSE_CAP_POSIX_LOCKS; + } + } + + if ((lo->cache == CACHE_NONE && !lo->readdirplus_set) || + lo->readdirplus_clear) { + fuse_log(FUSE_LOG_DEBUG, "lo_init: disabling readdirplus\n"); + conn->want &= ~FUSE_CAP_READDIRPLUS; + } +} + +static void lo_getattr(fuse_req_t req, fuse_ino_t ino, + struct fuse_file_info *fi) +{ + int res; + struct stat buf; + struct lo_data *lo = lo_data(req); + + (void)fi; + + res = + fstatat(lo_fd(req, ino), "", &buf, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW); + if (res == -1) { + return (void)fuse_reply_err(req, errno); + } + + fuse_reply_attr(req, &buf, lo->timeout); +} + +/* + * Increments parent->nlookup and caller must release refcount using + * lo_inode_put(&parent). + */ +static int lo_parent_and_name(struct lo_data *lo, struct lo_inode *inode, + char path[PATH_MAX], struct lo_inode **parent) +{ + char procname[64]; + char *last; + struct stat stat; + struct lo_inode *p; + int retries = 2; + int res; + +retry: + sprintf(procname, "%i", inode->fd); + + res = readlinkat(lo->proc_self_fd, procname, path, PATH_MAX); + if (res < 0) { + fuse_log(FUSE_LOG_WARNING, "%s: readlink failed: %m\n", __func__); + goto fail_noretry; + } + + if (res >= PATH_MAX) { + fuse_log(FUSE_LOG_WARNING, "%s: readlink overflowed\n", __func__); + goto fail_noretry; + } + path[res] = '\0'; + + last = strrchr(path, '/'); + if (last == NULL) { + /* Shouldn't happen */ + fuse_log( + FUSE_LOG_WARNING, + "%s: INTERNAL ERROR: bad path read from proc\n", __func__); + goto fail_noretry; + } + if (last == path) { + p = &lo->root; + pthread_mutex_lock(&lo->mutex); + p->nlookup++; + g_atomic_int_inc(&p->refcount); + pthread_mutex_unlock(&lo->mutex); + } else { + *last = '\0'; + res = fstatat(AT_FDCWD, last == path ? "/" : path, &stat, 0); + if (res == -1) { + if (!retries) { + fuse_log(FUSE_LOG_WARNING, + "%s: failed to stat parent: %m\n", __func__); + } + goto fail; + } + p = lo_find(lo, &stat); + if (p == NULL) { + if (!retries) { + fuse_log(FUSE_LOG_WARNING, + "%s: failed to find parent\n", __func__); + } + goto fail; + } + } + last++; + res = fstatat(p->fd, last, &stat, AT_SYMLINK_NOFOLLOW); + if (res == -1) { + if (!retries) { + fuse_log(FUSE_LOG_WARNING, + "%s: failed to stat last\n", __func__); + } + goto fail_unref; + } + if (stat.st_dev != inode->key.dev || stat.st_ino != inode->key.ino) { + if (!retries) { + fuse_log(FUSE_LOG_WARNING, + "%s: failed to match last\n", __func__); + } + goto fail_unref; + } + *parent = p; + memmove(path, last, strlen(last) + 1); + + return 0; + +fail_unref: + unref_inode_lolocked(lo, p, 1); + lo_inode_put(lo, &p); +fail: + if (retries) { + retries--; + goto retry; + } +fail_noretry: + errno = EIO; + return -1; +} + +static int utimensat_empty(struct lo_data *lo, struct lo_inode *inode, + const struct timespec *tv) +{ + int res; + struct lo_inode *parent; + char path[PATH_MAX]; + + if (S_ISLNK(inode->filetype)) { + res = utimensat(inode->fd, "", tv, AT_EMPTY_PATH); + if (res == -1 && errno == EINVAL) { + /* Sorry, no race free way to set times on symlink. */ + if (lo->norace) { + errno = EPERM; + } else { + goto fallback; + } + } + return res; + } + sprintf(path, "%i", inode->fd); + + return utimensat(lo->proc_self_fd, path, tv, 0); + +fallback: + res = lo_parent_and_name(lo, inode, path, &parent); + if (res != -1) { + res = utimensat(parent->fd, path, tv, AT_SYMLINK_NOFOLLOW); + unref_inode_lolocked(lo, parent, 1); + lo_inode_put(lo, &parent); + } + + return res; +} + +static int lo_fi_fd(fuse_req_t req, struct fuse_file_info *fi) +{ + struct lo_data *lo = lo_data(req); + struct lo_map_elem *elem; + + pthread_mutex_lock(&lo->mutex); + elem = lo_map_get(&lo->fd_map, fi->fh); + pthread_mutex_unlock(&lo->mutex); + + if (!elem) { + return -1; + } + + return elem->fd; +} + +static void lo_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, + int valid, struct fuse_file_info *fi) +{ + int saverr; + char procname[64]; + struct lo_data *lo = lo_data(req); + struct lo_inode *inode; + int ifd; + int res; + int fd; + + inode = lo_inode(req, ino); + if (!inode) { + fuse_reply_err(req, EBADF); + return; + } + + ifd = inode->fd; + + /* If fi->fh is invalid we'll report EBADF later */ + if (fi) { + fd = lo_fi_fd(req, fi); + } + + if (valid & FUSE_SET_ATTR_MODE) { + if (fi) { + res = fchmod(fd, attr->st_mode); + } else { + sprintf(procname, "%i", ifd); + res = fchmodat(lo->proc_self_fd, procname, attr->st_mode, 0); + } + if (res == -1) { + goto out_err; + } + } + if (valid & (FUSE_SET_ATTR_UID | FUSE_SET_ATTR_GID)) { + uid_t uid = (valid & FUSE_SET_ATTR_UID) ? attr->st_uid : (uid_t)-1; + gid_t gid = (valid & FUSE_SET_ATTR_GID) ? attr->st_gid : (gid_t)-1; + + res = fchownat(ifd, "", uid, gid, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW); + if (res == -1) { + goto out_err; + } + } + if (valid & FUSE_SET_ATTR_SIZE) { + int truncfd; + + if (fi) { + truncfd = fd; + } else { + sprintf(procname, "%i", ifd); + truncfd = openat(lo->proc_self_fd, procname, O_RDWR); + if (truncfd < 0) { + goto out_err; + } + } + + res = ftruncate(truncfd, attr->st_size); + if (!fi) { + saverr = errno; + close(truncfd); + errno = saverr; + } + if (res == -1) { + goto out_err; + } + } + if (valid & (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME)) { + struct timespec tv[2]; + + tv[0].tv_sec = 0; + tv[1].tv_sec = 0; + tv[0].tv_nsec = UTIME_OMIT; + tv[1].tv_nsec = UTIME_OMIT; + + if (valid & FUSE_SET_ATTR_ATIME_NOW) { + tv[0].tv_nsec = UTIME_NOW; + } else if (valid & FUSE_SET_ATTR_ATIME) { + tv[0] = attr->st_atim; + } + + if (valid & FUSE_SET_ATTR_MTIME_NOW) { + tv[1].tv_nsec = UTIME_NOW; + } else if (valid & FUSE_SET_ATTR_MTIME) { + tv[1] = attr->st_mtim; + } + + if (fi) { + res = futimens(fd, tv); + } else { + res = utimensat_empty(lo, inode, tv); + } + if (res == -1) { + goto out_err; + } + } + lo_inode_put(lo, &inode); + + return lo_getattr(req, ino, fi); + +out_err: + saverr = errno; + lo_inode_put(lo, &inode); + fuse_reply_err(req, saverr); +} + +static struct lo_inode *lo_find(struct lo_data *lo, struct stat *st) +{ + struct lo_inode *p; + struct lo_key key = { + .ino = st->st_ino, + .dev = st->st_dev, + }; + + pthread_mutex_lock(&lo->mutex); + p = g_hash_table_lookup(lo->inodes, &key); + if (p) { + assert(p->nlookup > 0); + p->nlookup++; + g_atomic_int_inc(&p->refcount); + } + pthread_mutex_unlock(&lo->mutex); + + return p; +} + +/* value_destroy_func for posix_locks GHashTable */ +static void posix_locks_value_destroy(gpointer data) +{ + struct lo_inode_plock *plock = data; + + /* + * We had used open() for locks and had only one fd. So + * closing this fd should release all OFD locks. + */ + close(plock->fd); + free(plock); +} + +/* + * Increments nlookup and caller must release refcount using + * lo_inode_put(&parent). + */ +static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name, + struct fuse_entry_param *e) +{ + int newfd; + int res; + int saverr; + struct lo_data *lo = lo_data(req); + struct lo_inode *inode = NULL; + struct lo_inode *dir = lo_inode(req, parent); + + /* + * name_to_handle_at() and open_by_handle_at() can reach here with fuse + * mount point in guest, but we don't have its inode info in the + * ino_map. + */ + if (!dir) { + return ENOENT; + } + + memset(e, 0, sizeof(*e)); + e->attr_timeout = lo->timeout; + e->entry_timeout = lo->timeout; + + /* Do not allow escaping root directory */ + if (dir == &lo->root && strcmp(name, "..") == 0) { + name = "."; + } + + newfd = openat(dir->fd, name, O_PATH | O_NOFOLLOW); + if (newfd == -1) { + goto out_err; + } + + res = fstatat(newfd, "", &e->attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW); + if (res == -1) { + goto out_err; + } + + inode = lo_find(lo, &e->attr); + if (inode) { + close(newfd); + } else { + inode = calloc(1, sizeof(struct lo_inode)); + if (!inode) { + goto out_err; + } + + /* cache only filetype */ + inode->filetype = (e->attr.st_mode & S_IFMT); + + /* + * One for the caller and one for nlookup (released in + * unref_inode_lolocked()) + */ + g_atomic_int_set(&inode->refcount, 2); + + inode->nlookup = 1; + inode->fd = newfd; + inode->key.ino = e->attr.st_ino; + inode->key.dev = e->attr.st_dev; + pthread_mutex_init(&inode->plock_mutex, NULL); + inode->posix_locks = g_hash_table_new_full( + g_direct_hash, g_direct_equal, NULL, posix_locks_value_destroy); + + pthread_mutex_lock(&lo->mutex); + inode->fuse_ino = lo_add_inode_mapping(req, inode); + g_hash_table_insert(lo->inodes, &inode->key, inode); + pthread_mutex_unlock(&lo->mutex); + } + e->ino = inode->fuse_ino; + lo_inode_put(lo, &inode); + lo_inode_put(lo, &dir); + + fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n", (unsigned long long)parent, + name, (unsigned long long)e->ino); + + return 0; + +out_err: + saverr = errno; + if (newfd != -1) { + close(newfd); + } + lo_inode_put(lo, &inode); + lo_inode_put(lo, &dir); + return saverr; +} + +static void lo_lookup(fuse_req_t req, fuse_ino_t parent, const char *name) +{ + struct fuse_entry_param e; + int err; + + fuse_log(FUSE_LOG_DEBUG, "lo_lookup(parent=%" PRIu64 ", name=%s)\n", parent, + name); + + /* + * Don't use is_safe_path_component(), allow "." and ".." for NFS export + * support. + */ + if (strchr(name, '/')) { + fuse_reply_err(req, EINVAL); + return; + } + + err = lo_do_lookup(req, parent, name, &e); + if (err) { + fuse_reply_err(req, err); + } else { + fuse_reply_entry(req, &e); + } +} + +/* + * On some archs, setres*id is limited to 2^16 but they + * provide setres*id32 variants that allow 2^32. + * Others just let setres*id do 2^32 anyway. + */ +#ifdef SYS_setresgid32 +#define OURSYS_setresgid SYS_setresgid32 +#else +#define OURSYS_setresgid SYS_setresgid +#endif + +#ifdef SYS_setresuid32 +#define OURSYS_setresuid SYS_setresuid32 +#else +#define OURSYS_setresuid SYS_setresuid +#endif + +/* + * Change to uid/gid of caller so that file is created with + * ownership of caller. + * TODO: What about selinux context? + */ +static int lo_change_cred(fuse_req_t req, struct lo_cred *old) +{ + int res; + + old->euid = geteuid(); + old->egid = getegid(); + + res = syscall(OURSYS_setresgid, -1, fuse_req_ctx(req)->gid, -1); + if (res == -1) { + return errno; + } + + res = syscall(OURSYS_setresuid, -1, fuse_req_ctx(req)->uid, -1); + if (res == -1) { + int errno_save = errno; + + syscall(OURSYS_setresgid, -1, old->egid, -1); + return errno_save; + } + + return 0; +} + +/* Regain Privileges */ +static void lo_restore_cred(struct lo_cred *old) +{ + int res; + + res = syscall(OURSYS_setresuid, -1, old->euid, -1); + if (res == -1) { + fuse_log(FUSE_LOG_ERR, "seteuid(%u): %m\n", old->euid); + exit(1); + } + + res = syscall(OURSYS_setresgid, -1, old->egid, -1); + if (res == -1) { + fuse_log(FUSE_LOG_ERR, "setegid(%u): %m\n", old->egid); + exit(1); + } +} + +static void lo_mknod_symlink(fuse_req_t req, fuse_ino_t parent, + const char *name, mode_t mode, dev_t rdev, + const char *link) +{ + int res; + int saverr; + struct lo_data *lo = lo_data(req); + struct lo_inode *dir; + struct fuse_entry_param e; + struct lo_cred old = {}; + + if (!is_safe_path_component(name)) { + fuse_reply_err(req, EINVAL); + return; + } + + dir = lo_inode(req, parent); + if (!dir) { + fuse_reply_err(req, EBADF); + return; + } + + saverr = lo_change_cred(req, &old); + if (saverr) { + goto out; + } + + res = mknod_wrapper(dir->fd, name, link, mode, rdev); + + saverr = errno; + + lo_restore_cred(&old); + + if (res == -1) { + goto out; + } + + saverr = lo_do_lookup(req, parent, name, &e); + if (saverr) { + goto out; + } + + fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n", (unsigned long long)parent, + name, (unsigned long long)e.ino); + + fuse_reply_entry(req, &e); + lo_inode_put(lo, &dir); + return; + +out: + lo_inode_put(lo, &dir); + fuse_reply_err(req, saverr); +} + +static void lo_mknod(fuse_req_t req, fuse_ino_t parent, const char *name, + mode_t mode, dev_t rdev) +{ + lo_mknod_symlink(req, parent, name, mode, rdev, NULL); +} + +static void lo_mkdir(fuse_req_t req, fuse_ino_t parent, const char *name, + mode_t mode) +{ + lo_mknod_symlink(req, parent, name, S_IFDIR | mode, 0, NULL); +} + +static void lo_symlink(fuse_req_t req, const char *link, fuse_ino_t parent, + const char *name) +{ + lo_mknod_symlink(req, parent, name, S_IFLNK, 0, link); +} + +static int linkat_empty_nofollow(struct lo_data *lo, struct lo_inode *inode, + int dfd, const char *name) +{ + int res; + struct lo_inode *parent; + char path[PATH_MAX]; + + if (S_ISLNK(inode->filetype)) { + res = linkat(inode->fd, "", dfd, name, AT_EMPTY_PATH); + if (res == -1 && (errno == ENOENT || errno == EINVAL)) { + /* Sorry, no race free way to hard-link a symlink. */ + if (lo->norace) { + errno = EPERM; + } else { + goto fallback; + } + } + return res; + } + + sprintf(path, "%i", inode->fd); + + return linkat(lo->proc_self_fd, path, dfd, name, AT_SYMLINK_FOLLOW); + +fallback: + res = lo_parent_and_name(lo, inode, path, &parent); + if (res != -1) { + res = linkat(parent->fd, path, dfd, name, 0); + unref_inode_lolocked(lo, parent, 1); + lo_inode_put(lo, &parent); + } + + return res; +} + +static void lo_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t parent, + const char *name) +{ + int res; + struct lo_data *lo = lo_data(req); + struct lo_inode *parent_inode; + struct lo_inode *inode; + struct fuse_entry_param e; + int saverr; + + if (!is_safe_path_component(name)) { + fuse_reply_err(req, EINVAL); + return; + } + + parent_inode = lo_inode(req, parent); + inode = lo_inode(req, ino); + if (!parent_inode || !inode) { + errno = EBADF; + goto out_err; + } + + memset(&e, 0, sizeof(struct fuse_entry_param)); + e.attr_timeout = lo->timeout; + e.entry_timeout = lo->timeout; + + res = linkat_empty_nofollow(lo, inode, parent_inode->fd, name); + if (res == -1) { + goto out_err; + } + + res = fstatat(inode->fd, "", &e.attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW); + if (res == -1) { + goto out_err; + } + + pthread_mutex_lock(&lo->mutex); + inode->nlookup++; + pthread_mutex_unlock(&lo->mutex); + e.ino = inode->fuse_ino; + + fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n", (unsigned long long)parent, + name, (unsigned long long)e.ino); + + fuse_reply_entry(req, &e); + lo_inode_put(lo, &parent_inode); + lo_inode_put(lo, &inode); + return; + +out_err: + saverr = errno; + lo_inode_put(lo, &parent_inode); + lo_inode_put(lo, &inode); + fuse_reply_err(req, saverr); +} + +/* Increments nlookup and caller must release refcount using lo_inode_put() */ +static struct lo_inode *lookup_name(fuse_req_t req, fuse_ino_t parent, + const char *name) +{ + int res; + struct stat attr; + + res = fstatat(lo_fd(req, parent), name, &attr, + AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW); + if (res == -1) { + return NULL; + } + + return lo_find(lo_data(req), &attr); +} + +static void lo_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name) +{ + int res; + struct lo_inode *inode; + struct lo_data *lo = lo_data(req); + + if (!is_safe_path_component(name)) { + fuse_reply_err(req, EINVAL); + return; + } + + inode = lookup_name(req, parent, name); + if (!inode) { + fuse_reply_err(req, EIO); + return; + } + + res = unlinkat(lo_fd(req, parent), name, AT_REMOVEDIR); + + fuse_reply_err(req, res == -1 ? errno : 0); + unref_inode_lolocked(lo, inode, 1); + lo_inode_put(lo, &inode); +} + +static void lo_rename(fuse_req_t req, fuse_ino_t parent, const char *name, + fuse_ino_t newparent, const char *newname, + unsigned int flags) +{ + int res; + struct lo_inode *parent_inode; + struct lo_inode *newparent_inode; + struct lo_inode *oldinode = NULL; + struct lo_inode *newinode = NULL; + struct lo_data *lo = lo_data(req); + + if (!is_safe_path_component(name) || !is_safe_path_component(newname)) { + fuse_reply_err(req, EINVAL); + return; + } + + parent_inode = lo_inode(req, parent); + newparent_inode = lo_inode(req, newparent); + if (!parent_inode || !newparent_inode) { + fuse_reply_err(req, EBADF); + goto out; + } + + oldinode = lookup_name(req, parent, name); + newinode = lookup_name(req, newparent, newname); + + if (!oldinode) { + fuse_reply_err(req, EIO); + goto out; + } + + if (flags) { +#ifndef SYS_renameat2 + fuse_reply_err(req, EINVAL); +#else + res = syscall(SYS_renameat2, parent_inode->fd, name, + newparent_inode->fd, newname, flags); + if (res == -1 && errno == ENOSYS) { + fuse_reply_err(req, EINVAL); + } else { + fuse_reply_err(req, res == -1 ? errno : 0); + } +#endif + goto out; + } + + res = renameat(parent_inode->fd, name, newparent_inode->fd, newname); + + fuse_reply_err(req, res == -1 ? errno : 0); +out: + unref_inode_lolocked(lo, oldinode, 1); + unref_inode_lolocked(lo, newinode, 1); + lo_inode_put(lo, &oldinode); + lo_inode_put(lo, &newinode); + lo_inode_put(lo, &parent_inode); + lo_inode_put(lo, &newparent_inode); +} + +static void lo_unlink(fuse_req_t req, fuse_ino_t parent, const char *name) +{ + int res; + struct lo_inode *inode; + struct lo_data *lo = lo_data(req); + + if (!is_safe_path_component(name)) { + fuse_reply_err(req, EINVAL); + return; + } + + inode = lookup_name(req, parent, name); + if (!inode) { + fuse_reply_err(req, EIO); + return; + } + + res = unlinkat(lo_fd(req, parent), name, 0); + + fuse_reply_err(req, res == -1 ? errno : 0); + unref_inode_lolocked(lo, inode, 1); + lo_inode_put(lo, &inode); +} + +/* To be called with lo->mutex held */ +static void unref_inode(struct lo_data *lo, struct lo_inode *inode, uint64_t n) +{ + if (!inode) { + return; + } + + assert(inode->nlookup >= n); + inode->nlookup -= n; + if (!inode->nlookup) { + lo_map_remove(&lo->ino_map, inode->fuse_ino); + g_hash_table_remove(lo->inodes, &inode->key); + if (g_hash_table_size(inode->posix_locks)) { + fuse_log(FUSE_LOG_WARNING, "Hash table is not empty\n"); + } + g_hash_table_destroy(inode->posix_locks); + pthread_mutex_destroy(&inode->plock_mutex); + + /* Drop our refcount from lo_do_lookup() */ + lo_inode_put(lo, &inode); + } +} + +static void unref_inode_lolocked(struct lo_data *lo, struct lo_inode *inode, + uint64_t n) +{ + if (!inode) { + return; + } + + pthread_mutex_lock(&lo->mutex); + unref_inode(lo, inode, n); + pthread_mutex_unlock(&lo->mutex); +} + +static void lo_forget_one(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup) +{ + struct lo_data *lo = lo_data(req); + struct lo_inode *inode; + + inode = lo_inode(req, ino); + if (!inode) { + return; + } + + fuse_log(FUSE_LOG_DEBUG, " forget %lli %lli -%lli\n", + (unsigned long long)ino, (unsigned long long)inode->nlookup, + (unsigned long long)nlookup); + + unref_inode_lolocked(lo, inode, nlookup); + lo_inode_put(lo, &inode); +} + +static void lo_forget(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup) +{ + lo_forget_one(req, ino, nlookup); + fuse_reply_none(req); +} + +static void lo_forget_multi(fuse_req_t req, size_t count, + struct fuse_forget_data *forgets) +{ + int i; + + for (i = 0; i < count; i++) { + lo_forget_one(req, forgets[i].ino, forgets[i].nlookup); + } + fuse_reply_none(req); +} + +static void lo_readlink(fuse_req_t req, fuse_ino_t ino) +{ + char buf[PATH_MAX + 1]; + int res; + + res = readlinkat(lo_fd(req, ino), "", buf, sizeof(buf)); + if (res == -1) { + return (void)fuse_reply_err(req, errno); + } + + if (res == sizeof(buf)) { + return (void)fuse_reply_err(req, ENAMETOOLONG); + } + + buf[res] = '\0'; + + fuse_reply_readlink(req, buf); +} + +struct lo_dirp { + gint refcount; + DIR *dp; + struct dirent *entry; + off_t offset; +}; + +static void lo_dirp_put(struct lo_dirp **dp) +{ + struct lo_dirp *d = *dp; + + if (!d) { + return; + } + *dp = NULL; + + if (g_atomic_int_dec_and_test(&d->refcount)) { + closedir(d->dp); + free(d); + } +} + +/* Call lo_dirp_put() on the return value when no longer needed */ +static struct lo_dirp *lo_dirp(fuse_req_t req, struct fuse_file_info *fi) +{ + struct lo_data *lo = lo_data(req); + struct lo_map_elem *elem; + + pthread_mutex_lock(&lo->mutex); + elem = lo_map_get(&lo->dirp_map, fi->fh); + if (elem) { + g_atomic_int_inc(&elem->dirp->refcount); + } + pthread_mutex_unlock(&lo->mutex); + if (!elem) { + return NULL; + } + + return elem->dirp; +} + +static void lo_opendir(fuse_req_t req, fuse_ino_t ino, + struct fuse_file_info *fi) +{ + int error = ENOMEM; + struct lo_data *lo = lo_data(req); + struct lo_dirp *d; + int fd; + ssize_t fh; + + d = calloc(1, sizeof(struct lo_dirp)); + if (d == NULL) { + goto out_err; + } + + fd = openat(lo_fd(req, ino), ".", O_RDONLY); + if (fd == -1) { + goto out_errno; + } + + d->dp = fdopendir(fd); + if (d->dp == NULL) { + goto out_errno; + } + + d->offset = 0; + d->entry = NULL; + + g_atomic_int_set(&d->refcount, 1); /* paired with lo_releasedir() */ + pthread_mutex_lock(&lo->mutex); + fh = lo_add_dirp_mapping(req, d); + pthread_mutex_unlock(&lo->mutex); + if (fh == -1) { + goto out_err; + } + + fi->fh = fh; + if (lo->cache == CACHE_ALWAYS) { + fi->cache_readdir = 1; + } + fuse_reply_open(req, fi); + return; + +out_errno: + error = errno; +out_err: + if (d) { + if (d->dp) { + closedir(d->dp); + } else if (fd != -1) { + close(fd); + } + free(d); + } + fuse_reply_err(req, error); +} + +static void lo_do_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, + off_t offset, struct fuse_file_info *fi, int plus) +{ + struct lo_data *lo = lo_data(req); + struct lo_dirp *d = NULL; + struct lo_inode *dinode; + char *buf = NULL; + char *p; + size_t rem = size; + int err = EBADF; + + dinode = lo_inode(req, ino); + if (!dinode) { + goto error; + } + + d = lo_dirp(req, fi); + if (!d) { + goto error; + } + + err = ENOMEM; + buf = calloc(1, size); + if (!buf) { + goto error; + } + p = buf; + + if (offset != d->offset) { + seekdir(d->dp, offset); + d->entry = NULL; + d->offset = offset; + } + while (1) { + size_t entsize; + off_t nextoff; + const char *name; + + if (!d->entry) { + errno = 0; + d->entry = readdir(d->dp); + if (!d->entry) { + if (errno) { /* Error */ + err = errno; + goto error; + } else { /* End of stream */ + break; + } + } + } + nextoff = d->entry->d_off; + name = d->entry->d_name; + + fuse_ino_t entry_ino = 0; + struct fuse_entry_param e = (struct fuse_entry_param){ + .attr.st_ino = d->entry->d_ino, + .attr.st_mode = d->entry->d_type << 12, + }; + + /* Hide root's parent directory */ + if (dinode == &lo->root && strcmp(name, "..") == 0) { + e.attr.st_ino = lo->root.key.ino; + e.attr.st_mode = DT_DIR << 12; + } + + if (plus) { + if (!is_dot_or_dotdot(name)) { + err = lo_do_lookup(req, ino, name, &e); + if (err) { + goto error; + } + entry_ino = e.ino; + } + + entsize = fuse_add_direntry_plus(req, p, rem, name, &e, nextoff); + } else { + entsize = fuse_add_direntry(req, p, rem, name, &e.attr, nextoff); + } + if (entsize > rem) { + if (entry_ino != 0) { + lo_forget_one(req, entry_ino, 1); + } + break; + } + + p += entsize; + rem -= entsize; + + d->entry = NULL; + d->offset = nextoff; + } + + err = 0; +error: + lo_dirp_put(&d); + lo_inode_put(lo, &dinode); + + /* + * If there's an error, we can only signal it if we haven't stored + * any entries yet - otherwise we'd end up with wrong lookup + * counts for the entries that are already in the buffer. So we + * return what we've collected until that point. + */ + if (err && rem == size) { + fuse_reply_err(req, err); + } else { + fuse_reply_buf(req, buf, size - rem); + } + free(buf); +} + +static void lo_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, + off_t offset, struct fuse_file_info *fi) +{ + lo_do_readdir(req, ino, size, offset, fi, 0); +} + +static void lo_readdirplus(fuse_req_t req, fuse_ino_t ino, size_t size, + off_t offset, struct fuse_file_info *fi) +{ + lo_do_readdir(req, ino, size, offset, fi, 1); +} + +static void lo_releasedir(fuse_req_t req, fuse_ino_t ino, + struct fuse_file_info *fi) +{ + struct lo_data *lo = lo_data(req); + struct lo_map_elem *elem; + struct lo_dirp *d; + + (void)ino; + + pthread_mutex_lock(&lo->mutex); + elem = lo_map_get(&lo->dirp_map, fi->fh); + if (!elem) { + pthread_mutex_unlock(&lo->mutex); + fuse_reply_err(req, EBADF); + return; + } + + d = elem->dirp; + lo_map_remove(&lo->dirp_map, fi->fh); + pthread_mutex_unlock(&lo->mutex); + + lo_dirp_put(&d); /* paired with lo_opendir() */ + + fuse_reply_err(req, 0); +} + +static void update_open_flags(int writeback, struct fuse_file_info *fi) +{ + /* + * With writeback cache, kernel may send read requests even + * when userspace opened write-only + */ + if (writeback && (fi->flags & O_ACCMODE) == O_WRONLY) { + fi->flags &= ~O_ACCMODE; + fi->flags |= O_RDWR; + } + + /* + * With writeback cache, O_APPEND is handled by the kernel. + * This breaks atomicity (since the file may change in the + * underlying filesystem, so that the kernel's idea of the + * end of the file isn't accurate anymore). In this example, + * we just accept that. A more rigorous filesystem may want + * to return an error here + */ + if (writeback && (fi->flags & O_APPEND)) { + fi->flags &= ~O_APPEND; + } + + /* + * O_DIRECT in guest should not necessarily mean bypassing page + * cache on host as well. If somebody needs that behavior, it + * probably should be a configuration knob in daemon. + */ + fi->flags &= ~O_DIRECT; +} + +static void lo_create(fuse_req_t req, fuse_ino_t parent, const char *name, + mode_t mode, struct fuse_file_info *fi) +{ + int fd; + struct lo_data *lo = lo_data(req); + struct lo_inode *parent_inode; + struct fuse_entry_param e; + int err; + struct lo_cred old = {}; + + fuse_log(FUSE_LOG_DEBUG, "lo_create(parent=%" PRIu64 ", name=%s)\n", parent, + name); + + if (!is_safe_path_component(name)) { + fuse_reply_err(req, EINVAL); + return; + } + + parent_inode = lo_inode(req, parent); + if (!parent_inode) { + fuse_reply_err(req, EBADF); + return; + } + + err = lo_change_cred(req, &old); + if (err) { + goto out; + } + + update_open_flags(lo->writeback, fi); + + fd = openat(parent_inode->fd, name, (fi->flags | O_CREAT) & ~O_NOFOLLOW, + mode); + err = fd == -1 ? errno : 0; + lo_restore_cred(&old); + + if (!err) { + ssize_t fh; + + pthread_mutex_lock(&lo->mutex); + fh = lo_add_fd_mapping(req, fd); + pthread_mutex_unlock(&lo->mutex); + if (fh == -1) { + close(fd); + err = ENOMEM; + goto out; + } + + fi->fh = fh; + err = lo_do_lookup(req, parent, name, &e); + } + if (lo->cache == CACHE_NONE) { + fi->direct_io = 1; + } else if (lo->cache == CACHE_ALWAYS) { + fi->keep_cache = 1; + } + +out: + lo_inode_put(lo, &parent_inode); + + if (err) { + fuse_reply_err(req, err); + } else { + fuse_reply_create(req, &e, fi); + } +} + +/* Should be called with inode->plock_mutex held */ +static struct lo_inode_plock *lookup_create_plock_ctx(struct lo_data *lo, + struct lo_inode *inode, + uint64_t lock_owner, + pid_t pid, int *err) +{ + struct lo_inode_plock *plock; + char procname[64]; + int fd; + + plock = + g_hash_table_lookup(inode->posix_locks, GUINT_TO_POINTER(lock_owner)); + + if (plock) { + return plock; + } + + plock = malloc(sizeof(struct lo_inode_plock)); + if (!plock) { + *err = ENOMEM; + return NULL; + } + + /* Open another instance of file which can be used for ofd locks. */ + sprintf(procname, "%i", inode->fd); + + /* TODO: What if file is not writable? */ + fd = openat(lo->proc_self_fd, procname, O_RDWR); + if (fd == -1) { + *err = errno; + free(plock); + return NULL; + } + + plock->lock_owner = lock_owner; + plock->fd = fd; + g_hash_table_insert(inode->posix_locks, GUINT_TO_POINTER(plock->lock_owner), + plock); + return plock; +} + +static void lo_getlk(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, + struct flock *lock) +{ + struct lo_data *lo = lo_data(req); + struct lo_inode *inode; + struct lo_inode_plock *plock; + int ret, saverr = 0; + + fuse_log(FUSE_LOG_DEBUG, + "lo_getlk(ino=%" PRIu64 ", flags=%d)" + " owner=0x%lx, l_type=%d l_start=0x%lx" + " l_len=0x%lx\n", + ino, fi->flags, fi->lock_owner, lock->l_type, lock->l_start, + lock->l_len); + + inode = lo_inode(req, ino); + if (!inode) { + fuse_reply_err(req, EBADF); + return; + } + + pthread_mutex_lock(&inode->plock_mutex); + plock = + lookup_create_plock_ctx(lo, inode, fi->lock_owner, lock->l_pid, &ret); + if (!plock) { + saverr = ret; + goto out; + } + + ret = fcntl(plock->fd, F_OFD_GETLK, lock); + if (ret == -1) { + saverr = errno; + } + +out: + pthread_mutex_unlock(&inode->plock_mutex); + lo_inode_put(lo, &inode); + + if (saverr) { + fuse_reply_err(req, saverr); + } else { + fuse_reply_lock(req, lock); + } +} + +static void lo_setlk(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, + struct flock *lock, int sleep) +{ + struct lo_data *lo = lo_data(req); + struct lo_inode *inode; + struct lo_inode_plock *plock; + int ret, saverr = 0; + + fuse_log(FUSE_LOG_DEBUG, + "lo_setlk(ino=%" PRIu64 ", flags=%d)" + " cmd=%d pid=%d owner=0x%lx sleep=%d l_whence=%d" + " l_start=0x%lx l_len=0x%lx\n", + ino, fi->flags, lock->l_type, lock->l_pid, fi->lock_owner, sleep, + lock->l_whence, lock->l_start, lock->l_len); + + if (sleep) { + fuse_reply_err(req, EOPNOTSUPP); + return; + } + + inode = lo_inode(req, ino); + if (!inode) { + fuse_reply_err(req, EBADF); + return; + } + + pthread_mutex_lock(&inode->plock_mutex); + plock = + lookup_create_plock_ctx(lo, inode, fi->lock_owner, lock->l_pid, &ret); + + if (!plock) { + saverr = ret; + goto out; + } + + /* TODO: Is it alright to modify flock? */ + lock->l_pid = 0; + ret = fcntl(plock->fd, F_OFD_SETLK, lock); + if (ret == -1) { + saverr = errno; + } + +out: + pthread_mutex_unlock(&inode->plock_mutex); + lo_inode_put(lo, &inode); + + fuse_reply_err(req, saverr); +} + +static void lo_fsyncdir(fuse_req_t req, fuse_ino_t ino, int datasync, + struct fuse_file_info *fi) +{ + int res; + struct lo_dirp *d; + int fd; + + (void)ino; + + d = lo_dirp(req, fi); + if (!d) { + fuse_reply_err(req, EBADF); + return; + } + + fd = dirfd(d->dp); + if (datasync) { + res = fdatasync(fd); + } else { + res = fsync(fd); + } + + lo_dirp_put(&d); + + fuse_reply_err(req, res == -1 ? errno : 0); +} + +static void lo_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) +{ + int fd; + ssize_t fh; + char buf[64]; + struct lo_data *lo = lo_data(req); + + fuse_log(FUSE_LOG_DEBUG, "lo_open(ino=%" PRIu64 ", flags=%d)\n", ino, + fi->flags); + + update_open_flags(lo->writeback, fi); + + sprintf(buf, "%i", lo_fd(req, ino)); + fd = openat(lo->proc_self_fd, buf, fi->flags & ~O_NOFOLLOW); + if (fd == -1) { + return (void)fuse_reply_err(req, errno); + } + + pthread_mutex_lock(&lo->mutex); + fh = lo_add_fd_mapping(req, fd); + pthread_mutex_unlock(&lo->mutex); + if (fh == -1) { + close(fd); + fuse_reply_err(req, ENOMEM); + return; + } + + fi->fh = fh; + if (lo->cache == CACHE_NONE) { + fi->direct_io = 1; + } else if (lo->cache == CACHE_ALWAYS) { + fi->keep_cache = 1; + } + fuse_reply_open(req, fi); +} + +static void lo_release(fuse_req_t req, fuse_ino_t ino, + struct fuse_file_info *fi) +{ + struct lo_data *lo = lo_data(req); + struct lo_map_elem *elem; + int fd = -1; + + (void)ino; + + pthread_mutex_lock(&lo->mutex); + elem = lo_map_get(&lo->fd_map, fi->fh); + if (elem) { + fd = elem->fd; + elem = NULL; + lo_map_remove(&lo->fd_map, fi->fh); + } + pthread_mutex_unlock(&lo->mutex); + + close(fd); + fuse_reply_err(req, 0); +} + +static void lo_flush(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) +{ + int res; + (void)ino; + struct lo_inode *inode; + + inode = lo_inode(req, ino); + if (!inode) { + fuse_reply_err(req, EBADF); + return; + } + + /* An fd is going away. Cleanup associated posix locks */ + pthread_mutex_lock(&inode->plock_mutex); + g_hash_table_remove(inode->posix_locks, GUINT_TO_POINTER(fi->lock_owner)); + pthread_mutex_unlock(&inode->plock_mutex); + + res = close(dup(lo_fi_fd(req, fi))); + lo_inode_put(lo_data(req), &inode); + fuse_reply_err(req, res == -1 ? errno : 0); +} + +static void lo_fsync(fuse_req_t req, fuse_ino_t ino, int datasync, + struct fuse_file_info *fi) +{ + int res; + int fd; + char *buf; + + fuse_log(FUSE_LOG_DEBUG, "lo_fsync(ino=%" PRIu64 ", fi=0x%p)\n", ino, + (void *)fi); + + if (!fi) { + struct lo_data *lo = lo_data(req); + + res = asprintf(&buf, "%i", lo_fd(req, ino)); + if (res == -1) { + return (void)fuse_reply_err(req, errno); + } + + fd = openat(lo->proc_self_fd, buf, O_RDWR); + free(buf); + if (fd == -1) { + return (void)fuse_reply_err(req, errno); + } + } else { + fd = lo_fi_fd(req, fi); + } + + if (datasync) { + res = fdatasync(fd); + } else { + res = fsync(fd); + } + if (!fi) { + close(fd); + } + fuse_reply_err(req, res == -1 ? errno : 0); +} + +static void lo_read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t offset, + struct fuse_file_info *fi) +{ + struct fuse_bufvec buf = FUSE_BUFVEC_INIT(size); + + fuse_log(FUSE_LOG_DEBUG, + "lo_read(ino=%" PRIu64 ", size=%zd, " + "off=%lu)\n", + ino, size, (unsigned long)offset); + + buf.buf[0].flags = FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK; + buf.buf[0].fd = lo_fi_fd(req, fi); + buf.buf[0].pos = offset; + + fuse_reply_data(req, &buf); +} + +static void lo_write_buf(fuse_req_t req, fuse_ino_t ino, + struct fuse_bufvec *in_buf, off_t off, + struct fuse_file_info *fi) +{ + (void)ino; + ssize_t res; + struct fuse_bufvec out_buf = FUSE_BUFVEC_INIT(fuse_buf_size(in_buf)); + bool cap_fsetid_dropped = false; + + out_buf.buf[0].flags = FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK; + out_buf.buf[0].fd = lo_fi_fd(req, fi); + out_buf.buf[0].pos = off; + + fuse_log(FUSE_LOG_DEBUG, + "lo_write_buf(ino=%" PRIu64 ", size=%zd, off=%lu)\n", ino, + out_buf.buf[0].size, (unsigned long)off); + + /* + * If kill_priv is set, drop CAP_FSETID which should lead to kernel + * clearing setuid/setgid on file. + */ + if (fi->kill_priv) { + res = drop_effective_cap("FSETID", &cap_fsetid_dropped); + if (res != 0) { + fuse_reply_err(req, res); + return; + } + } + + res = fuse_buf_copy(&out_buf, in_buf); + if (res < 0) { + fuse_reply_err(req, -res); + } else { + fuse_reply_write(req, (size_t)res); + } + + if (cap_fsetid_dropped) { + res = gain_effective_cap("FSETID"); + if (res) { + fuse_log(FUSE_LOG_ERR, "Failed to gain CAP_FSETID\n"); + } + } +} + +static void lo_statfs(fuse_req_t req, fuse_ino_t ino) +{ + int res; + struct statvfs stbuf; + + res = fstatvfs(lo_fd(req, ino), &stbuf); + if (res == -1) { + fuse_reply_err(req, errno); + } else { + fuse_reply_statfs(req, &stbuf); + } +} + +static void lo_fallocate(fuse_req_t req, fuse_ino_t ino, int mode, off_t offset, + off_t length, struct fuse_file_info *fi) +{ + int err = EOPNOTSUPP; + (void)ino; + +#ifdef CONFIG_FALLOCATE + err = fallocate(lo_fi_fd(req, fi), mode, offset, length); + if (err < 0) { + err = errno; + } + +#elif defined(CONFIG_POSIX_FALLOCATE) + if (mode) { + fuse_reply_err(req, EOPNOTSUPP); + return; + } + + err = posix_fallocate(lo_fi_fd(req, fi), offset, length); +#endif + + fuse_reply_err(req, err); +} + +static void lo_flock(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, + int op) +{ + int res; + (void)ino; + + res = flock(lo_fi_fd(req, fi), op); + + fuse_reply_err(req, res == -1 ? errno : 0); +} + +static void lo_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, + size_t size) +{ + struct lo_data *lo = lo_data(req); + char *value = NULL; + char procname[64]; + struct lo_inode *inode; + ssize_t ret; + int saverr; + int fd = -1; + + inode = lo_inode(req, ino); + if (!inode) { + fuse_reply_err(req, EBADF); + return; + } + + saverr = ENOSYS; + if (!lo_data(req)->xattr) { + goto out; + } + + fuse_log(FUSE_LOG_DEBUG, "lo_getxattr(ino=%" PRIu64 ", name=%s size=%zd)\n", + ino, name, size); + + if (size) { + value = malloc(size); + if (!value) { + goto out_err; + } + } + + sprintf(procname, "%i", inode->fd); + /* + * It is not safe to open() non-regular/non-dir files in file server + * unless O_PATH is used, so use that method for regular files/dir + * only (as it seems giving less performance overhead). + * Otherwise, call fchdir() to avoid open(). + */ + if (S_ISREG(inode->filetype) || S_ISDIR(inode->filetype)) { + fd = openat(lo->proc_self_fd, procname, O_RDONLY); + if (fd < 0) { + goto out_err; + } + ret = fgetxattr(fd, name, value, size); + } else { + /* fchdir should not fail here */ + assert(fchdir(lo->proc_self_fd) == 0); + ret = getxattr(procname, name, value, size); + assert(fchdir(lo->root.fd) == 0); + } + + if (ret == -1) { + goto out_err; + } + if (size) { + saverr = 0; + if (ret == 0) { + goto out; + } + fuse_reply_buf(req, value, ret); + } else { + fuse_reply_xattr(req, ret); + } +out_free: + free(value); + + if (fd >= 0) { + close(fd); + } + + lo_inode_put(lo, &inode); + return; + +out_err: + saverr = errno; +out: + fuse_reply_err(req, saverr); + goto out_free; +} + +static void lo_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size) +{ + struct lo_data *lo = lo_data(req); + char *value = NULL; + char procname[64]; + struct lo_inode *inode; + ssize_t ret; + int saverr; + int fd = -1; + + inode = lo_inode(req, ino); + if (!inode) { + fuse_reply_err(req, EBADF); + return; + } + + saverr = ENOSYS; + if (!lo_data(req)->xattr) { + goto out; + } + + fuse_log(FUSE_LOG_DEBUG, "lo_listxattr(ino=%" PRIu64 ", size=%zd)\n", ino, + size); + + if (size) { + value = malloc(size); + if (!value) { + goto out_err; + } + } + + sprintf(procname, "%i", inode->fd); + if (S_ISREG(inode->filetype) || S_ISDIR(inode->filetype)) { + fd = openat(lo->proc_self_fd, procname, O_RDONLY); + if (fd < 0) { + goto out_err; + } + ret = flistxattr(fd, value, size); + } else { + /* fchdir should not fail here */ + assert(fchdir(lo->proc_self_fd) == 0); + ret = listxattr(procname, value, size); + assert(fchdir(lo->root.fd) == 0); + } + + if (ret == -1) { + goto out_err; + } + if (size) { + saverr = 0; + if (ret == 0) { + goto out; + } + fuse_reply_buf(req, value, ret); + } else { + fuse_reply_xattr(req, ret); + } +out_free: + free(value); + + if (fd >= 0) { + close(fd); + } + + lo_inode_put(lo, &inode); + return; + +out_err: + saverr = errno; +out: + fuse_reply_err(req, saverr); + goto out_free; +} + +static void lo_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name, + const char *value, size_t size, int flags) +{ + char procname[64]; + struct lo_data *lo = lo_data(req); + struct lo_inode *inode; + ssize_t ret; + int saverr; + int fd = -1; + + inode = lo_inode(req, ino); + if (!inode) { + fuse_reply_err(req, EBADF); + return; + } + + saverr = ENOSYS; + if (!lo_data(req)->xattr) { + goto out; + } + + fuse_log(FUSE_LOG_DEBUG, "lo_setxattr(ino=%" PRIu64 + ", name=%s value=%s size=%zd)\n", ino, name, value, size); + + sprintf(procname, "%i", inode->fd); + if (S_ISREG(inode->filetype) || S_ISDIR(inode->filetype)) { + fd = openat(lo->proc_self_fd, procname, O_RDONLY); + if (fd < 0) { + saverr = errno; + goto out; + } + ret = fsetxattr(fd, name, value, size, flags); + } else { + /* fchdir should not fail here */ + assert(fchdir(lo->proc_self_fd) == 0); + ret = setxattr(procname, name, value, size, flags); + assert(fchdir(lo->root.fd) == 0); + } + + saverr = ret == -1 ? errno : 0; + +out: + if (fd >= 0) { + close(fd); + } + + lo_inode_put(lo, &inode); + fuse_reply_err(req, saverr); +} + +static void lo_removexattr(fuse_req_t req, fuse_ino_t ino, const char *name) +{ + char procname[64]; + struct lo_data *lo = lo_data(req); + struct lo_inode *inode; + ssize_t ret; + int saverr; + int fd = -1; + + inode = lo_inode(req, ino); + if (!inode) { + fuse_reply_err(req, EBADF); + return; + } + + saverr = ENOSYS; + if (!lo_data(req)->xattr) { + goto out; + } + + fuse_log(FUSE_LOG_DEBUG, "lo_removexattr(ino=%" PRIu64 ", name=%s)\n", ino, + name); + + sprintf(procname, "%i", inode->fd); + if (S_ISREG(inode->filetype) || S_ISDIR(inode->filetype)) { + fd = openat(lo->proc_self_fd, procname, O_RDONLY); + if (fd < 0) { + saverr = errno; + goto out; + } + ret = fremovexattr(fd, name); + } else { + /* fchdir should not fail here */ + assert(fchdir(lo->proc_self_fd) == 0); + ret = removexattr(procname, name); + assert(fchdir(lo->root.fd) == 0); + } + + saverr = ret == -1 ? errno : 0; + +out: + if (fd >= 0) { + close(fd); + } + + lo_inode_put(lo, &inode); + fuse_reply_err(req, saverr); +} + +#ifdef HAVE_COPY_FILE_RANGE +static void lo_copy_file_range(fuse_req_t req, fuse_ino_t ino_in, off_t off_in, + struct fuse_file_info *fi_in, fuse_ino_t ino_out, + off_t off_out, struct fuse_file_info *fi_out, + size_t len, int flags) +{ + int in_fd, out_fd; + ssize_t res; + + in_fd = lo_fi_fd(req, fi_in); + out_fd = lo_fi_fd(req, fi_out); + + fuse_log(FUSE_LOG_DEBUG, + "lo_copy_file_range(ino=%" PRIu64 "/fd=%d, " + "off=%lu, ino=%" PRIu64 "/fd=%d, " + "off=%lu, size=%zd, flags=0x%x)\n", + ino_in, in_fd, off_in, ino_out, out_fd, off_out, len, flags); + + res = copy_file_range(in_fd, &off_in, out_fd, &off_out, len, flags); + if (res < 0) { + fuse_reply_err(req, errno); + } else { + fuse_reply_write(req, res); + } +} +#endif + +static void lo_lseek(fuse_req_t req, fuse_ino_t ino, off_t off, int whence, + struct fuse_file_info *fi) +{ + off_t res; + + (void)ino; + res = lseek(lo_fi_fd(req, fi), off, whence); + if (res != -1) { + fuse_reply_lseek(req, res); + } else { + fuse_reply_err(req, errno); + } +} + +static void lo_destroy(void *userdata) +{ + struct lo_data *lo = (struct lo_data *)userdata; + + pthread_mutex_lock(&lo->mutex); + while (true) { + GHashTableIter iter; + gpointer key, value; + + g_hash_table_iter_init(&iter, lo->inodes); + if (!g_hash_table_iter_next(&iter, &key, &value)) { + break; + } + + struct lo_inode *inode = value; + unref_inode(lo, inode, inode->nlookup); + } + pthread_mutex_unlock(&lo->mutex); +} + +static struct fuse_lowlevel_ops lo_oper = { + .init = lo_init, + .lookup = lo_lookup, + .mkdir = lo_mkdir, + .mknod = lo_mknod, + .symlink = lo_symlink, + .link = lo_link, + .unlink = lo_unlink, + .rmdir = lo_rmdir, + .rename = lo_rename, + .forget = lo_forget, + .forget_multi = lo_forget_multi, + .getattr = lo_getattr, + .setattr = lo_setattr, + .readlink = lo_readlink, + .opendir = lo_opendir, + .readdir = lo_readdir, + .readdirplus = lo_readdirplus, + .releasedir = lo_releasedir, + .fsyncdir = lo_fsyncdir, + .create = lo_create, + .getlk = lo_getlk, + .setlk = lo_setlk, + .open = lo_open, + .release = lo_release, + .flush = lo_flush, + .fsync = lo_fsync, + .read = lo_read, + .write_buf = lo_write_buf, + .statfs = lo_statfs, + .fallocate = lo_fallocate, + .flock = lo_flock, + .getxattr = lo_getxattr, + .listxattr = lo_listxattr, + .setxattr = lo_setxattr, + .removexattr = lo_removexattr, +#ifdef HAVE_COPY_FILE_RANGE + .copy_file_range = lo_copy_file_range, +#endif + .lseek = lo_lseek, + .destroy = lo_destroy, +}; + +/* Print vhost-user.json backend program capabilities */ +static void print_capabilities(void) +{ + printf("{\n"); + printf(" \"type\": \"fs\"\n"); + printf("}\n"); +} + +/* + * Move to a new mount, net, and pid namespaces to isolate this process. + */ +static void setup_namespaces(struct lo_data *lo, struct fuse_session *se) +{ + pid_t child; + + /* + * Create a new pid namespace for *child* processes. We'll have to + * fork in order to enter the new pid namespace. A new mount namespace + * is also needed so that we can remount /proc for the new pid + * namespace. + * + * Our UNIX domain sockets have been created. Now we can move to + * an empty network namespace to prevent TCP/IP and other network + * activity in case this process is compromised. + */ + if (unshare(CLONE_NEWPID | CLONE_NEWNS | CLONE_NEWNET) != 0) { + fuse_log(FUSE_LOG_ERR, "unshare(CLONE_NEWPID | CLONE_NEWNS): %m\n"); + exit(1); + } + + child = fork(); + if (child < 0) { + fuse_log(FUSE_LOG_ERR, "fork() failed: %m\n"); + exit(1); + } + if (child > 0) { + pid_t waited; + int wstatus; + + /* The parent waits for the child */ + do { + waited = waitpid(child, &wstatus, 0); + } while (waited < 0 && errno == EINTR && !se->exited); + + /* We were terminated by a signal, see fuse_signals.c */ + if (se->exited) { + exit(0); + } + + if (WIFEXITED(wstatus)) { + exit(WEXITSTATUS(wstatus)); + } + + exit(1); + } + + /* Send us SIGTERM when the parent thread terminates, see prctl(2) */ + prctl(PR_SET_PDEATHSIG, SIGTERM); + + /* + * If the mounts have shared propagation then we want to opt out so our + * mount changes don't affect the parent mount namespace. + */ + if (mount(NULL, "/", NULL, MS_REC | MS_SLAVE, NULL) < 0) { + fuse_log(FUSE_LOG_ERR, "mount(/, MS_REC|MS_SLAVE): %m\n"); + exit(1); + } + + /* The child must remount /proc to use the new pid namespace */ + if (mount("proc", "/proc", "proc", + MS_NODEV | MS_NOEXEC | MS_NOSUID | MS_RELATIME, NULL) < 0) { + fuse_log(FUSE_LOG_ERR, "mount(/proc): %m\n"); + exit(1); + } + + /* Now we can get our /proc/self/fd directory file descriptor */ + lo->proc_self_fd = open("/proc/self/fd", O_PATH); + if (lo->proc_self_fd == -1) { + fuse_log(FUSE_LOG_ERR, "open(/proc/self/fd, O_PATH): %m\n"); + exit(1); + } +} + +/* + * Capture the capability state, we'll need to restore this for individual + * threads later; see load_capng. + */ +static void setup_capng(void) +{ + /* Note this accesses /proc so has to happen before the sandbox */ + if (capng_get_caps_process()) { + fuse_log(FUSE_LOG_ERR, "capng_get_caps_process\n"); + exit(1); + } + pthread_mutex_init(&cap.mutex, NULL); + pthread_mutex_lock(&cap.mutex); + cap.saved = capng_save_state(); + if (!cap.saved) { + fuse_log(FUSE_LOG_ERR, "capng_save_state\n"); + exit(1); + } + pthread_mutex_unlock(&cap.mutex); +} + +static void cleanup_capng(void) +{ + free(cap.saved); + cap.saved = NULL; + pthread_mutex_destroy(&cap.mutex); +} + + +/* + * Make the source directory our root so symlinks cannot escape and no other + * files are accessible. Assumes unshare(CLONE_NEWNS) was already called. + */ +static void setup_mounts(const char *source) +{ + int oldroot; + int newroot; + + if (mount(source, source, NULL, MS_BIND, NULL) < 0) { + fuse_log(FUSE_LOG_ERR, "mount(%s, %s, MS_BIND): %m\n", source, source); + exit(1); + } + + /* This magic is based on lxc's lxc_pivot_root() */ + oldroot = open("/", O_DIRECTORY | O_RDONLY | O_CLOEXEC); + if (oldroot < 0) { + fuse_log(FUSE_LOG_ERR, "open(/): %m\n"); + exit(1); + } + + newroot = open(source, O_DIRECTORY | O_RDONLY | O_CLOEXEC); + if (newroot < 0) { + fuse_log(FUSE_LOG_ERR, "open(%s): %m\n", source); + exit(1); + } + + if (fchdir(newroot) < 0) { + fuse_log(FUSE_LOG_ERR, "fchdir(newroot): %m\n"); + exit(1); + } + + if (syscall(__NR_pivot_root, ".", ".") < 0) { + fuse_log(FUSE_LOG_ERR, "pivot_root(., .): %m\n"); + exit(1); + } + + if (fchdir(oldroot) < 0) { + fuse_log(FUSE_LOG_ERR, "fchdir(oldroot): %m\n"); + exit(1); + } + + if (mount("", ".", "", MS_SLAVE | MS_REC, NULL) < 0) { + fuse_log(FUSE_LOG_ERR, "mount(., MS_SLAVE | MS_REC): %m\n"); + exit(1); + } + + if (umount2(".", MNT_DETACH) < 0) { + fuse_log(FUSE_LOG_ERR, "umount2(., MNT_DETACH): %m\n"); + exit(1); + } + + if (fchdir(newroot) < 0) { + fuse_log(FUSE_LOG_ERR, "fchdir(newroot): %m\n"); + exit(1); + } + + close(newroot); + close(oldroot); +} + +/* + * Lock down this process to prevent access to other processes or files outside + * source directory. This reduces the impact of arbitrary code execution bugs. + */ +static void setup_sandbox(struct lo_data *lo, struct fuse_session *se, + bool enable_syslog) +{ + setup_namespaces(lo, se); + setup_mounts(lo->source); + setup_seccomp(enable_syslog); +} + +/* Raise the maximum number of open file descriptors */ +static void setup_nofile_rlimit(void) +{ + const rlim_t max_fds = 1000000; + struct rlimit rlim; + + if (getrlimit(RLIMIT_NOFILE, &rlim) < 0) { + fuse_log(FUSE_LOG_ERR, "getrlimit(RLIMIT_NOFILE): %m\n"); + exit(1); + } + + if (rlim.rlim_cur >= max_fds) { + return; /* nothing to do */ + } + + rlim.rlim_cur = max_fds; + rlim.rlim_max = max_fds; + + if (setrlimit(RLIMIT_NOFILE, &rlim) < 0) { + /* Ignore SELinux denials */ + if (errno == EPERM) { + return; + } + + fuse_log(FUSE_LOG_ERR, "setrlimit(RLIMIT_NOFILE): %m\n"); + exit(1); + } +} + +static void log_func(enum fuse_log_level level, const char *fmt, va_list ap) +{ + g_autofree char *localfmt = NULL; + + if (current_log_level < level) { + return; + } + + if (current_log_level == FUSE_LOG_DEBUG) { + if (!use_syslog) { + localfmt = g_strdup_printf("[%" PRId64 "] [ID: %08ld] %s", + get_clock(), syscall(__NR_gettid), fmt); + } else { + localfmt = g_strdup_printf("[ID: %08ld] %s", syscall(__NR_gettid), + fmt); + } + fmt = localfmt; + } + + if (use_syslog) { + int priority = LOG_ERR; + switch (level) { + case FUSE_LOG_EMERG: + priority = LOG_EMERG; + break; + case FUSE_LOG_ALERT: + priority = LOG_ALERT; + break; + case FUSE_LOG_CRIT: + priority = LOG_CRIT; + break; + case FUSE_LOG_ERR: + priority = LOG_ERR; + break; + case FUSE_LOG_WARNING: + priority = LOG_WARNING; + break; + case FUSE_LOG_NOTICE: + priority = LOG_NOTICE; + break; + case FUSE_LOG_INFO: + priority = LOG_INFO; + break; + case FUSE_LOG_DEBUG: + priority = LOG_DEBUG; + break; + } + vsyslog(priority, fmt, ap); + } else { + vfprintf(stderr, fmt, ap); + } +} + +static void setup_root(struct lo_data *lo, struct lo_inode *root) +{ + int fd, res; + struct stat stat; + + fd = open("/", O_PATH); + if (fd == -1) { + fuse_log(FUSE_LOG_ERR, "open(%s, O_PATH): %m\n", lo->source); + exit(1); + } + + res = fstatat(fd, "", &stat, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW); + if (res == -1) { + fuse_log(FUSE_LOG_ERR, "fstatat(%s): %m\n", lo->source); + exit(1); + } + + root->filetype = S_IFDIR; + root->fd = fd; + root->key.ino = stat.st_ino; + root->key.dev = stat.st_dev; + root->nlookup = 2; + g_atomic_int_set(&root->refcount, 2); +} + +static guint lo_key_hash(gconstpointer key) +{ + const struct lo_key *lkey = key; + + return (guint)lkey->ino + (guint)lkey->dev; +} + +static gboolean lo_key_equal(gconstpointer a, gconstpointer b) +{ + const struct lo_key *la = a; + const struct lo_key *lb = b; + + return la->ino == lb->ino && la->dev == lb->dev; +} + +static void fuse_lo_data_cleanup(struct lo_data *lo) +{ + if (lo->inodes) { + g_hash_table_destroy(lo->inodes); + } + lo_map_destroy(&lo->fd_map); + lo_map_destroy(&lo->dirp_map); + lo_map_destroy(&lo->ino_map); + + if (lo->proc_self_fd >= 0) { + close(lo->proc_self_fd); + } + + if (lo->root.fd >= 0) { + close(lo->root.fd); + } + + free(lo->source); +} + +int main(int argc, char *argv[]) +{ + struct fuse_args args = FUSE_ARGS_INIT(argc, argv); + struct fuse_session *se; + struct fuse_cmdline_opts opts; + struct lo_data lo = { + .debug = 0, + .writeback = 0, + .posix_lock = 1, + .proc_self_fd = -1, + }; + struct lo_map_elem *root_elem; + int ret = -1; + + /* Don't mask creation mode, kernel already did that */ + umask(0); + + pthread_mutex_init(&lo.mutex, NULL); + lo.inodes = g_hash_table_new(lo_key_hash, lo_key_equal); + lo.root.fd = -1; + lo.root.fuse_ino = FUSE_ROOT_ID; + lo.cache = CACHE_AUTO; + + /* + * Set up the ino map like this: + * [0] Reserved (will not be used) + * [1] Root inode + */ + lo_map_init(&lo.ino_map); + lo_map_reserve(&lo.ino_map, 0)->in_use = false; + root_elem = lo_map_reserve(&lo.ino_map, lo.root.fuse_ino); + root_elem->inode = &lo.root; + + lo_map_init(&lo.dirp_map); + lo_map_init(&lo.fd_map); + + if (fuse_parse_cmdline(&args, &opts) != 0) { + goto err_out1; + } + fuse_set_log_func(log_func); + use_syslog = opts.syslog; + if (use_syslog) { + openlog("virtiofsd", LOG_PID, LOG_DAEMON); + } + + if (opts.show_help) { + printf("usage: %s [options]\n\n", argv[0]); + fuse_cmdline_help(); + printf(" -o source=PATH shared directory tree\n"); + fuse_lowlevel_help(); + ret = 0; + goto err_out1; + } else if (opts.show_version) { + fuse_lowlevel_version(); + ret = 0; + goto err_out1; + } else if (opts.print_capabilities) { + print_capabilities(); + ret = 0; + goto err_out1; + } + + if (fuse_opt_parse(&args, &lo, lo_opts, NULL) == -1) { + goto err_out1; + } + + /* + * log_level is 0 if not configured via cmd options (0 is LOG_EMERG, + * and we don't use this log level). + */ + if (opts.log_level != 0) { + current_log_level = opts.log_level; + } + lo.debug = opts.debug; + if (lo.debug) { + current_log_level = FUSE_LOG_DEBUG; + } + if (lo.source) { + struct stat stat; + int res; + + res = lstat(lo.source, &stat); + if (res == -1) { + fuse_log(FUSE_LOG_ERR, "failed to stat source (\"%s\"): %m\n", + lo.source); + exit(1); + } + if (!S_ISDIR(stat.st_mode)) { + fuse_log(FUSE_LOG_ERR, "source is not a directory\n"); + exit(1); + } + } else { + lo.source = strdup("/"); + } + if (!lo.timeout_set) { + switch (lo.cache) { + case CACHE_NONE: + lo.timeout = 0.0; + break; + + case CACHE_AUTO: + lo.timeout = 1.0; + break; + + case CACHE_ALWAYS: + lo.timeout = 86400.0; + break; + } + } else if (lo.timeout < 0) { + fuse_log(FUSE_LOG_ERR, "timeout is negative (%lf)\n", lo.timeout); + exit(1); + } + + se = fuse_session_new(&args, &lo_oper, sizeof(lo_oper), &lo); + if (se == NULL) { + goto err_out1; + } + + if (fuse_set_signal_handlers(se) != 0) { + goto err_out2; + } + + if (fuse_session_mount(se) != 0) { + goto err_out3; + } + + fuse_daemonize(opts.foreground); + + setup_nofile_rlimit(); + + /* Must be before sandbox since it wants /proc */ + setup_capng(); + + setup_sandbox(&lo, se, opts.syslog); + + setup_root(&lo, &lo.root); + /* Block until ctrl+c or fusermount -u */ + ret = virtio_loop(se); + + fuse_session_unmount(se); + cleanup_capng(); +err_out3: + fuse_remove_signal_handlers(se); +err_out2: + fuse_session_destroy(se); +err_out1: + fuse_opt_free_args(&args); + + fuse_lo_data_cleanup(&lo); + + return ret ? 1 : 0; +} diff --git a/tools/virtiofsd/seccomp.c b/tools/virtiofsd/seccomp.c new file mode 100644 index 0000000000..bd9e7b083c --- /dev/null +++ b/tools/virtiofsd/seccomp.c @@ -0,0 +1,171 @@ +/* + * Seccomp sandboxing for virtiofsd + * + * Copyright (C) 2019 Red Hat, Inc. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "seccomp.h" +#include "fuse_i.h" +#include "fuse_log.h" +#include +#include +#include +#include + +/* Bodge for libseccomp 2.4.2 which broke ppoll */ +#if !defined(__SNR_ppoll) && defined(__SNR_brk) +#ifdef __NR_ppoll +#define __SNR_ppoll __NR_ppoll +#else +#define __SNR_ppoll __PNR_ppoll +#endif +#endif + +static const int syscall_whitelist[] = { + /* TODO ireg sem*() syscalls */ + SCMP_SYS(brk), + SCMP_SYS(capget), /* For CAP_FSETID */ + SCMP_SYS(capset), + SCMP_SYS(clock_gettime), + SCMP_SYS(clone), +#ifdef __NR_clone3 + SCMP_SYS(clone3), +#endif + SCMP_SYS(close), + SCMP_SYS(copy_file_range), + SCMP_SYS(dup), + SCMP_SYS(eventfd2), + SCMP_SYS(exit), + SCMP_SYS(exit_group), + SCMP_SYS(fallocate), + SCMP_SYS(fchdir), + SCMP_SYS(fchmodat), + SCMP_SYS(fchownat), + SCMP_SYS(fcntl), + SCMP_SYS(fdatasync), + SCMP_SYS(fgetxattr), + SCMP_SYS(flistxattr), + SCMP_SYS(flock), + SCMP_SYS(fremovexattr), + SCMP_SYS(fsetxattr), + SCMP_SYS(fstat), + SCMP_SYS(fstatfs), + SCMP_SYS(fsync), + SCMP_SYS(ftruncate), + SCMP_SYS(futex), + SCMP_SYS(getdents), + SCMP_SYS(getdents64), + SCMP_SYS(getegid), + SCMP_SYS(geteuid), + SCMP_SYS(getpid), + SCMP_SYS(gettid), + SCMP_SYS(gettimeofday), + SCMP_SYS(getxattr), + SCMP_SYS(linkat), + SCMP_SYS(listxattr), + SCMP_SYS(lseek), + SCMP_SYS(madvise), + SCMP_SYS(mkdirat), + SCMP_SYS(mknodat), + SCMP_SYS(mmap), + SCMP_SYS(mprotect), + SCMP_SYS(mremap), + SCMP_SYS(munmap), + SCMP_SYS(newfstatat), + SCMP_SYS(open), + SCMP_SYS(openat), + SCMP_SYS(ppoll), + SCMP_SYS(prctl), /* TODO restrict to just PR_SET_NAME? */ + SCMP_SYS(preadv), + SCMP_SYS(pread64), + SCMP_SYS(pwritev), + SCMP_SYS(pwrite64), + SCMP_SYS(read), + SCMP_SYS(readlinkat), + SCMP_SYS(recvmsg), + SCMP_SYS(renameat), + SCMP_SYS(renameat2), + SCMP_SYS(removexattr), + SCMP_SYS(rt_sigaction), + SCMP_SYS(rt_sigprocmask), + SCMP_SYS(rt_sigreturn), + SCMP_SYS(sendmsg), + SCMP_SYS(setresgid), + SCMP_SYS(setresuid), +#ifdef __NR_setresgid32 + SCMP_SYS(setresgid32), +#endif +#ifdef __NR_setresuid32 + SCMP_SYS(setresuid32), +#endif + SCMP_SYS(set_robust_list), + SCMP_SYS(setxattr), + SCMP_SYS(symlinkat), + SCMP_SYS(time), /* Rarely needed, except on static builds */ + SCMP_SYS(tgkill), + SCMP_SYS(unlinkat), + SCMP_SYS(unshare), + SCMP_SYS(utimensat), + SCMP_SYS(write), + SCMP_SYS(writev), +}; + +/* Syscalls used when --syslog is enabled */ +static const int syscall_whitelist_syslog[] = { + SCMP_SYS(sendto), +}; + +static void add_whitelist(scmp_filter_ctx ctx, const int syscalls[], size_t len) +{ + size_t i; + + for (i = 0; i < len; i++) { + if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, syscalls[i], 0) != 0) { + fuse_log(FUSE_LOG_ERR, "seccomp_rule_add syscall %d failed\n", + syscalls[i]); + exit(1); + } + } +} + +void setup_seccomp(bool enable_syslog) +{ + scmp_filter_ctx ctx; + +#ifdef SCMP_ACT_KILL_PROCESS + ctx = seccomp_init(SCMP_ACT_KILL_PROCESS); + /* Handle a newer libseccomp but an older kernel */ + if (!ctx && errno == EOPNOTSUPP) { + ctx = seccomp_init(SCMP_ACT_TRAP); + } +#else + ctx = seccomp_init(SCMP_ACT_TRAP); +#endif + if (!ctx) { + fuse_log(FUSE_LOG_ERR, "seccomp_init() failed\n"); + exit(1); + } + + add_whitelist(ctx, syscall_whitelist, G_N_ELEMENTS(syscall_whitelist)); + if (enable_syslog) { + add_whitelist(ctx, syscall_whitelist_syslog, + G_N_ELEMENTS(syscall_whitelist_syslog)); + } + + /* libvhost-user calls this for post-copy migration, we don't need it */ + if (seccomp_rule_add(ctx, SCMP_ACT_ERRNO(ENOSYS), + SCMP_SYS(userfaultfd), 0) != 0) { + fuse_log(FUSE_LOG_ERR, "seccomp_rule_add userfaultfd failed\n"); + exit(1); + } + + if (seccomp_load(ctx) < 0) { + fuse_log(FUSE_LOG_ERR, "seccomp_load() failed\n"); + exit(1); + } + + seccomp_release(ctx); +} diff --git a/tools/virtiofsd/seccomp.h b/tools/virtiofsd/seccomp.h new file mode 100644 index 0000000000..d47c8eade6 --- /dev/null +++ b/tools/virtiofsd/seccomp.h @@ -0,0 +1,16 @@ +/* + * Seccomp sandboxing for virtiofsd + * + * Copyright (C) 2019 Red Hat, Inc. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef VIRTIOFSD_SECCOMP_H +#define VIRTIOFSD_SECCOMP_H + +#include + +void setup_seccomp(bool enable_syslog); + +#endif /* VIRTIOFSD_SECCOMP_H */ diff --git a/trace/control.c b/trace/control.c index d9cafc161b..2ffe000818 100644 --- a/trace/control.c +++ b/trace/control.c @@ -98,38 +98,6 @@ TraceEvent *trace_event_name(const char *name) return NULL; } -static bool pattern_glob(const char *pat, const char *ev) -{ - while (*pat != '\0' && *ev != '\0') { - if (*pat == *ev) { - pat++; - ev++; - } - else if (*pat == '*') { - if (pattern_glob(pat, ev+1)) { - return true; - } else if (pattern_glob(pat+1, ev)) { - return true; - } else { - return false; - } - } else { - return false; - } - } - - while (*pat == '*') { - pat++; - } - - if (*pat == '\0' && *ev == '\0') { - return true; - } else { - return false; - } -} - - void trace_event_iter_init(TraceEventIter *iter, const char *pattern) { iter->event = 0; @@ -148,8 +116,7 @@ TraceEvent *trace_event_iter_next(TraceEventIter *iter) iter->group++; } if (!iter->pattern || - pattern_glob(iter->pattern, - trace_event_get_name(ev))) { + g_pattern_match_simple(iter->pattern, trace_event_get_name(ev))) { return ev; } } @@ -259,8 +226,11 @@ void trace_init_file(const char *file) #ifdef CONFIG_TRACE_SIMPLE st_set_trace_file(file); #elif defined CONFIG_TRACE_LOG - /* If both the simple and the log backends are enabled, "--trace file" - * only applies to the simple backend; use "-D" for the log backend. + /* + * If both the simple and the log backends are enabled, "--trace file" + * only applies to the simple backend; use "-D" for the log + * backend. However we should only override -D if we actually have + * something to override it with. */ if (file) { qemu_set_log_filename(file, &error_fatal); diff --git a/trace/mem-internal.h b/trace/mem-internal.h index 0a32aa22ca..8b72b678fa 100644 --- a/trace/mem-internal.h +++ b/trace/mem-internal.h @@ -47,21 +47,4 @@ static inline uint16_t trace_mem_get_info(MemOp op, mmu_idx); } -/* Used by the atomic helpers */ -static inline -uint16_t trace_mem_build_info_no_se_be(int size_shift, bool store, - TCGMemOpIdx oi) -{ - return trace_mem_build_info(size_shift, false, MO_BE, store, - get_mmuidx(oi)); -} - -static inline -uint16_t trace_mem_build_info_no_se_le(int size_shift, bool store, - TCGMemOpIdx oi) -{ - return trace_mem_build_info(size_shift, false, MO_LE, store, - get_mmuidx(oi)); -} - #endif /* TRACE__MEM_INTERNAL_H */ diff --git a/ui/cocoa.m b/ui/cocoa.m index fbb5b1b45f..cb556e4e66 100644 --- a/ui/cocoa.m +++ b/ui/cocoa.m @@ -42,60 +42,10 @@ #include #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) -#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]; @@ -1232,7 +1174,7 @@ QemuCocoaView *cocoaView; - (void) openDocumentation: (NSString *) filename { /* Where to look for local files */ - NSString *path_array[] = {@"../share/doc/qemu/", @"../doc/qemu/", @"../"}; + NSString *path_array[] = {@"../share/doc/qemu/", @"../doc/qemu/", @"../docs/"}; NSString *full_file_path; /* iterate thru the possible paths until the file is found */ @@ -1256,7 +1198,7 @@ QemuCocoaView *cocoaView; { COCOA_DEBUG("QemuCocoaAppController: showQEMUDoc\n"); - [self openDocumentation: @"qemu-doc.html"]; + [self openDocumentation: @"index.html"]; } /* Stretches video to fit host monitor size */ @@ -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)); diff --git a/ui/console.c b/ui/console.c index 82d1ddac9c..184e173687 100644 --- a/ui/console.c +++ b/ui/console.c @@ -33,6 +33,7 @@ #include "chardev/char-fe.h" #include "trace.h" #include "exec/memory.h" +#include "io/channel-file.h" #define DEFAULT_BACKSCROLL 512 #define CONSOLE_CURSOR_PERIOD 500 @@ -193,6 +194,7 @@ static void dpy_refresh(DisplayState *s); static DisplayState *get_alloc_displaystate(void); static void text_console_update_cursor_timer(void); static void text_console_update_cursor(void *opaque); +static bool ppm_save(int fd, DisplaySurface *ds, Error **errp); static void gui_update(void *opaque) { @@ -259,13 +261,22 @@ static void gui_setup_refresh(DisplayState *ds) ds->have_text = have_text; } +void graphic_hw_update_done(QemuConsole *con) +{ +} + void graphic_hw_update(QemuConsole *con) { + bool async = false; if (!con) { con = active_console; } if (con && con->hw_ops->gfx_update) { con->hw_ops->gfx_update(con->hw); + async = con->hw_ops->gfx_update_async; + } + if (!async) { + graphic_hw_update_done(con); } } @@ -299,52 +310,34 @@ void graphic_hw_invalidate(QemuConsole *con) } } -static void ppm_save(const char *filename, DisplaySurface *ds, - Error **errp) +static bool ppm_save(int fd, DisplaySurface *ds, Error **errp) { int width = pixman_image_get_width(ds->image); int height = pixman_image_get_height(ds->image); - int fd; - FILE *f; + g_autoptr(Object) ioc = OBJECT(qio_channel_file_new_fd(fd)); + g_autofree char *header = NULL; + g_autoptr(pixman_image_t) linebuf = NULL; int y; - int ret; - pixman_image_t *linebuf; - trace_ppm_save(filename, ds); - fd = qemu_open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666); - if (fd == -1) { - error_setg(errp, "failed to open file '%s': %s", filename, - strerror(errno)); - return; - } - f = fdopen(fd, "wb"); - ret = fprintf(f, "P6\n%d %d\n%d\n", width, height, 255); - if (ret < 0) { - linebuf = NULL; - goto write_err; + trace_ppm_save(fd, ds); + + header = g_strdup_printf("P6\n%d %d\n%d\n", width, height, 255); + if (qio_channel_write_all(QIO_CHANNEL(ioc), + header, strlen(header), errp) < 0) { + return false; } + linebuf = qemu_pixman_linebuf_create(PIXMAN_BE_r8g8b8, width); for (y = 0; y < height; y++) { qemu_pixman_linebuf_fill(linebuf, ds->image, width, 0, y); - clearerr(f); - ret = fwrite(pixman_image_get_data(linebuf), 1, - pixman_image_get_stride(linebuf), f); - (void)ret; - if (ferror(f)) { - goto write_err; + if (qio_channel_write_all(QIO_CHANNEL(ioc), + (char *)pixman_image_get_data(linebuf), + pixman_image_get_stride(linebuf), errp) < 0) { + return false; } } -out: - qemu_pixman_image_unref(linebuf); - fclose(f); - return; - -write_err: - error_setg(errp, "failed to write to file '%s': %s", filename, - strerror(errno)); - unlink(filename); - goto out; + return true; } void qmp_screendump(const char *filename, bool has_device, const char *device, @@ -352,6 +345,7 @@ void qmp_screendump(const char *filename, bool has_device, const char *device, { QemuConsole *con; DisplaySurface *surface; + int fd; if (has_device) { con = qemu_console_lookup_by_device_name(device, has_head ? head : 0, @@ -378,7 +372,16 @@ void qmp_screendump(const char *filename, bool has_device, const char *device, return; } - ppm_save(filename, surface, errp); + fd = qemu_open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666); + if (fd == -1) { + error_setg(errp, "failed to open file '%s': %s", filename, + strerror(errno)); + return; + } + + if (!ppm_save(fd, surface, errp)) { + qemu_unlink(filename); + } } void graphic_hw_text_update(QemuConsole *con, console_ch_t *chardata) @@ -1296,8 +1299,8 @@ static QemuConsole *new_console(DisplayState *ds, console_type_t console_type, object_property_allow_set_link, OBJ_PROP_LINK_STRONG, &error_abort); - object_property_add_uint32_ptr(obj, "head", - &s->head, &error_abort); + object_property_add_uint32_ptr(obj, "head", &s->head, + OBJ_PROP_FLAG_READ, &error_abort); if (!active_console || ((active_console->console_type != GRAPHIC_CONSOLE) && (console_type == GRAPHIC_CONSOLE))) { @@ -2330,6 +2333,22 @@ void qemu_display_init(DisplayState *ds, DisplayOptions *opts) dpys[opts->type]->init(ds, opts); } +void qemu_display_help(void) +{ + int idx; + + printf("Available display backend types:\n"); + printf("none\n"); + for (idx = DISPLAY_TYPE_NONE; idx < DISPLAY_TYPE__MAX; idx++) { + if (!dpys[idx]) { + ui_module_load_one(DisplayType_str(idx)); + } + if (dpys[idx]) { + printf("%s\n", DisplayType_str(dpys[idx]->type)); + } + } +} + void qemu_chr_parse_vc(QemuOpts *opts, ChardevBackend *backend, Error **errp) { int val; diff --git a/ui/curses.c b/ui/curses.c index 3a1b71451c..a59b23a9cf 100644 --- a/ui/curses.c +++ b/ui/curses.c @@ -54,13 +54,13 @@ enum maybe_keycode { }; static DisplayChangeListener *dcl; -static console_ch_t screen[160 * 100]; +static console_ch_t *screen; static WINDOW *screenpad = NULL; static int width, height, gwidth, gheight, invalidate; static int px, py, sminx, sminy, smaxx, smaxy; static const char *font_charset = "CP437"; -static cchar_t vga_to_curses[256]; +static cchar_t *vga_to_curses; static void curses_update(DisplayChangeListener *dcl, int x, int y, int w, int h) @@ -405,6 +405,8 @@ static void curses_refresh(DisplayChangeListener *dcl) static void curses_atexit(void) { endwin(); + g_free(vga_to_curses); + g_free(screen); } /* @@ -529,7 +531,7 @@ static void font_setup(void) * Control characters are normally non-printable, but VGA does have * well-known glyphs for them. */ - static uint16_t control_characters[0x20] = { + static const uint16_t control_characters[0x20] = { 0x0020, 0x263a, 0x263b, @@ -783,6 +785,8 @@ static void curses_display_init(DisplayState *ds, DisplayOptions *opts) if (opts->u.curses.charset) { font_charset = opts->u.curses.charset; } + screen = g_new0(console_ch_t, 160 * 100); + vga_to_curses = g_new0(cchar_t, 256); curses_setup(); curses_keyboard_setup(); atexit(curses_atexit); diff --git a/ui/gtk.c b/ui/gtk.c index 692ccc7bbb..030b251c61 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -32,6 +32,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" +#include "qapi/qapi-commands-control.h" #include "qapi/qapi-commands-misc.h" #include "qemu/cutils.h" @@ -1961,11 +1962,31 @@ static GtkWidget *gd_create_menu_machine(GtkDisplayState *s) return machine_menu; } +/* + * If available, return the refresh rate of the display in milli-Hertz, + * else return 0. + */ +static int gd_refresh_rate_millihz(GtkWidget *window) +{ +#ifdef GDK_VERSION_3_22 + GdkWindow *win = gtk_widget_get_window(window); + + 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, QemuConsole *con, int idx, GSList *group, GtkWidget *view_menu) { bool zoom_to_fit = false; + int refresh_rate_millihz; vc->label = qemu_console_get_label(con); vc->s = s; @@ -2026,6 +2047,13 @@ 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(vc->window ? + vc->window : s->window); + if (refresh_rate_millihz) { + vc->gfx.dcl.update_interval = MILLISEC_PER_SEC / refresh_rate_millihz; + } + register_displaychangelistener(&vc->gfx.dcl); gd_connect_vc_gfx_signals(vc); @@ -2219,8 +2247,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); diff --git a/ui/input-barrier.c b/ui/input-barrier.c index fe35049b83..527c75e130 100644 --- a/ui/input-barrier.c +++ b/ui/input-barrier.c @@ -455,7 +455,7 @@ static gboolean writecmd(InputBarrier *ib, struct barrierMsg *msg) break; default: write_cmd(p, barrierCmdEUnknown, avail); - break;; + break; } len = MAX_HELLO_LENGTH - avail - sizeof(int); diff --git a/ui/input-linux.c b/ui/input-linux.c index a7b280b25b..ef37b14d6f 100644 --- a/ui/input-linux.c +++ b/ui/input-linux.c @@ -334,13 +334,15 @@ static void input_linux_complete(UserCreatable *uc, Error **errp) rc = ioctl(il->fd, EVIOCGBIT(0, sizeof(evtmap)), &evtmap); if (rc < 0) { - error_setg(errp, "%s: failed to read event bits", il->evdev); - goto err_close; + goto err_read_event_bits; } if (evtmap & (1 << EV_REL)) { relmap = 0; rc = ioctl(il->fd, EVIOCGBIT(EV_REL, sizeof(relmap)), &relmap); + if (rc < 0) { + goto err_read_event_bits; + } if (relmap & (1 << REL_X)) { il->has_rel_x = true; } @@ -349,12 +351,25 @@ static void input_linux_complete(UserCreatable *uc, Error **errp) if (evtmap & (1 << EV_ABS)) { absmap = 0; rc = ioctl(il->fd, EVIOCGBIT(EV_ABS, sizeof(absmap)), &absmap); + if (rc < 0) { + goto err_read_event_bits; + } if (absmap & (1 << ABS_X)) { il->has_abs_x = true; rc = ioctl(il->fd, EVIOCGABS(ABS_X), &absinfo); + if (rc < 0) { + error_setg(errp, "%s: failed to get get absolute X value", + il->evdev); + goto err_close; + } il->abs_x_min = absinfo.minimum; il->abs_x_max = absinfo.maximum; rc = ioctl(il->fd, EVIOCGABS(ABS_Y), &absinfo); + if (rc < 0) { + error_setg(errp, "%s: failed to get get absolute Y value", + il->evdev); + goto err_close; + } il->abs_y_min = absinfo.minimum; il->abs_y_max = absinfo.maximum; } @@ -363,7 +378,14 @@ static void input_linux_complete(UserCreatable *uc, Error **errp) if (evtmap & (1 << EV_KEY)) { memset(keymap, 0, sizeof(keymap)); rc = ioctl(il->fd, EVIOCGBIT(EV_KEY, sizeof(keymap)), keymap); + if (rc < 0) { + goto err_read_event_bits; + } rc = ioctl(il->fd, EVIOCGKEY(sizeof(keystate)), keystate); + if (rc < 0) { + error_setg(errp, "%s: failed to get global key state", il->evdev); + goto err_close; + } for (i = 0; i < KEY_CNT; i++) { if (keymap[i / 8] & (1 << (i % 8))) { if (linux_is_button(i)) { @@ -390,6 +412,9 @@ static void input_linux_complete(UserCreatable *uc, Error **errp) il->initialized = true; return; +err_read_event_bits: + error_setg(errp, "%s: failed to read event bits", il->evdev); + err_close: close(il->fd); return; diff --git a/ui/sdl2.c b/ui/sdl2.c index bd4e73661d..3c9424eb42 100644 --- a/ui/sdl2.c +++ b/ui/sdl2.c @@ -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; @@ -772,7 +772,7 @@ static void sdl2_display_init(DisplayState *ds, DisplayOptions *o) * This is a bit hackish but saves us from bigger problem. * Maybe it's a good idea to fix this in SDL instead. */ - setenv("SDL_VIDEODRIVER", "x11", 0); + g_setenv("SDL_VIDEODRIVER", "x11", 0); #endif if (SDL_Init(SDL_INIT_VIDEO)) { diff --git a/ui/trace-events b/ui/trace-events index 63de72a798..0dcda393c1 100644 --- a/ui/trace-events +++ b/ui/trace-events @@ -15,7 +15,7 @@ displaysurface_create_pixman(void *display_surface) "surface=%p" displaysurface_free(void *display_surface) "surface=%p" displaychangelistener_register(void *dcl, const char *name) "%p [ %s ]" displaychangelistener_unregister(void *dcl, const char *name) "%p [ %s ]" -ppm_save(const char *filename, void *display_surface) "%s surface=%p" +ppm_save(int fd, void *display_surface) "fd=%d surface=%p" # gtk.c # gtk-gl-area.c diff --git a/ui/vnc-enc-zrle.c b/ui/vnc-enc-zrle.c index 17fd28a2e2..b4f71e32cf 100644 --- a/ui/vnc-enc-zrle.c +++ b/ui/vnc-enc-zrle.c @@ -98,8 +98,8 @@ static int zrle_compress_data(VncState *vs, int level) /* set pointers */ zstream->next_in = vs->zrle->zrle.buffer; zstream->avail_in = vs->zrle->zrle.offset; - zstream->next_out = vs->zrle->zlib.buffer + vs->zrle->zlib.offset; - zstream->avail_out = vs->zrle->zlib.capacity - vs->zrle->zlib.offset; + zstream->next_out = vs->zrle->zlib.buffer; + zstream->avail_out = vs->zrle->zlib.capacity; zstream->data_type = Z_BINARY; /* start encoding */ diff --git a/ui/vnc.c b/ui/vnc.c index 87b8045afe..1d7138a3a0 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -898,8 +898,6 @@ int vnc_raw_send_framebuffer_update(VncState *vs, int x, int y, int w, int h) int vnc_send_framebuffer_update(VncState *vs, int x, int y, int w, int h) { int n = 0; - bool encode_raw = false; - size_t saved_offs = vs->output.offset; switch(vs->vnc_encoding) { case VNC_ENCODING_ZLIB: @@ -922,24 +920,10 @@ int vnc_send_framebuffer_update(VncState *vs, int x, int y, int w, int h) n = vnc_zywrle_send_framebuffer_update(vs, x, y, w, h); break; default: - encode_raw = true; + vnc_framebuffer_update(vs, x, y, w, h, VNC_ENCODING_RAW); + n = vnc_raw_send_framebuffer_update(vs, x, y, w, h); break; } - - /* If the client has the same pixel format as our internal buffer and - * a RAW encoding would need less space fall back to RAW encoding to - * save bandwidth and processing power in the client. */ - if (!encode_raw && vs->write_pixels == vnc_write_pixels_copy && - 12 + h * w * VNC_SERVER_FB_BYTES <= (vs->output.offset - saved_offs)) { - vs->output.offset = saved_offs; - encode_raw = true; - } - - if (encode_raw) { - vnc_framebuffer_update(vs, x, y, w, h, VNC_ENCODING_RAW); - n = vnc_raw_send_framebuffer_update(vs, x, y, w, h); - } - return n; } @@ -1312,7 +1296,7 @@ void vnc_disconnect_finish(VncState *vs) g_free(vs); } -size_t vnc_client_io_error(VncState *vs, ssize_t ret, Error **errp) +size_t vnc_client_io_error(VncState *vs, ssize_t ret, Error *err) { if (ret <= 0) { if (ret == 0) { @@ -1320,15 +1304,11 @@ size_t vnc_client_io_error(VncState *vs, ssize_t ret, Error **errp) vnc_disconnect_start(vs); } else if (ret != QIO_CHANNEL_ERR_BLOCK) { trace_vnc_client_io_error(vs, vs->ioc, - errp ? error_get_pretty(*errp) : - "Unknown"); + err ? error_get_pretty(err) : "Unknown"); vnc_disconnect_start(vs); } - if (errp) { - error_free(*errp); - *errp = NULL; - } + error_free(err); return 0; } return ret; @@ -1361,10 +1341,9 @@ size_t vnc_client_write_buf(VncState *vs, const uint8_t *data, size_t datalen) { Error *err = NULL; ssize_t ret; - ret = qio_channel_write( - vs->ioc, (const char *)data, datalen, &err); + ret = qio_channel_write(vs->ioc, (const char *)data, datalen, &err); VNC_DEBUG("Wrote wire %p %zd -> %ld\n", data, datalen, ret); - return vnc_client_io_error(vs, ret, &err); + return vnc_client_io_error(vs, ret, err); } @@ -1488,10 +1467,9 @@ size_t vnc_client_read_buf(VncState *vs, uint8_t *data, size_t datalen) { ssize_t ret; Error *err = NULL; - ret = qio_channel_read( - vs->ioc, (char *)data, datalen, &err); + ret = qio_channel_read(vs->ioc, (char *)data, datalen, &err); VNC_DEBUG("Read wire %p %zd -> %ld\n", data, datalen, ret); - return vnc_client_io_error(vs, ret, &err); + return vnc_client_io_error(vs, ret, err); } @@ -2093,8 +2071,15 @@ static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings) break; #endif case VNC_ENCODING_ZLIB: - vs->features |= VNC_FEATURE_ZLIB_MASK; - vs->vnc_encoding = enc; + /* + * VNC_ENCODING_ZRLE compresses better than VNC_ENCODING_ZLIB. + * So prioritize ZRLE, even if the client hints that it prefers + * ZLIB. + */ + if ((vs->features & VNC_FEATURE_ZRLE_MASK) == 0) { + vs->features |= VNC_FEATURE_ZLIB_MASK; + vs->vnc_encoding = enc; + } break; case VNC_ENCODING_ZRLE: vs->features |= VNC_FEATURE_ZRLE_MASK; diff --git a/ui/vnc.h b/ui/vnc.h index fea79c2fc9..4e2637ce6c 100644 --- a/ui/vnc.h +++ b/ui/vnc.h @@ -547,7 +547,7 @@ uint32_t read_u32(uint8_t *data, size_t offset); /* Protocol stage functions */ void vnc_client_error(VncState *vs); -size_t vnc_client_io_error(VncState *vs, ssize_t ret, Error **errp); +size_t vnc_client_io_error(VncState *vs, ssize_t ret, Error *err); void start_client_init(VncState *vs); void start_auth_vnc(VncState *vs); diff --git a/util/Makefile.objs b/util/Makefile.objs index df124af1c5..fe339c2636 100644 --- a/util/Makefile.objs +++ b/util/Makefile.objs @@ -5,6 +5,9 @@ util-obj-y += aiocb.o async.o aio-wait.o thread-pool.o qemu-timer.o util-obj-y += main-loop.o util-obj-$(call lnot,$(CONFIG_ATOMIC64)) += atomic64.o util-obj-$(CONFIG_POSIX) += aio-posix.o +util-obj-$(CONFIG_POSIX) += fdmon-poll.o +util-obj-$(CONFIG_EPOLL_CREATE1) += fdmon-epoll.o +util-obj-$(CONFIG_LINUX_IO_URING) += fdmon-io_uring.o util-obj-$(CONFIG_POSIX) += compatfd.o util-obj-$(CONFIG_POSIX) += event_notifier-posix.o util-obj-$(CONFIG_POSIX) += mmap-alloc.o @@ -20,6 +23,7 @@ util-obj-y += envlist.o path.o module.o util-obj-y += host-utils.o util-obj-y += bitmap.o bitops.o hbitmap.o util-obj-y += fifo8.o +util-obj-y += nvdimm-utils.o util-obj-y += cacheinfo.o util-obj-y += error.o qemu-error.o util-obj-y += qemu-print.o @@ -52,8 +56,11 @@ util-obj-y += stats64.o util-obj-y += systemd.o util-obj-y += iova-tree.o util-obj-$(CONFIG_INOTIFY1) += filemonitor-inotify.o +util-obj-$(call lnot,$(CONFIG_INOTIFY1)) += filemonitor-stub.o util-obj-$(CONFIG_LINUX) += vfio-helpers.o util-obj-$(CONFIG_POSIX) += drm.o util-obj-y += guest-random.o - -stub-obj-y += filemonitor-stub.o +util-obj-$(CONFIG_GIO) += dbus.o +dbus.o-cflags = $(GIO_CFLAGS) +dbus.o-libs = $(GIO_LIBS) +util-obj-$(CONFIG_USER_ONLY) += selfmap.o diff --git a/util/aio-posix.c b/util/aio-posix.c index a4977f538e..c3613d299e 100644 --- a/util/aio-posix.c +++ b/util/aio-posix.c @@ -15,185 +15,40 @@ #include "qemu/osdep.h" #include "block/block.h" +#include "qemu/rcu.h" #include "qemu/rcu_queue.h" #include "qemu/sockets.h" #include "qemu/cutils.h" #include "trace.h" -#ifdef CONFIG_EPOLL_CREATE1 -#include -#endif +#include "aio-posix.h" -struct AioHandler +/* Stop userspace polling on a handler if it isn't active for some time */ +#define POLL_IDLE_INTERVAL_NS (7 * NANOSECONDS_PER_SECOND) + +bool aio_poll_disabled(AioContext *ctx) { - GPollFD pfd; - IOHandler *io_read; - IOHandler *io_write; - AioPollFn *io_poll; - IOHandler *io_poll_begin; - IOHandler *io_poll_end; - int deleted; - void *opaque; - bool is_external; - QLIST_ENTRY(AioHandler) node; -}; - -#ifdef CONFIG_EPOLL_CREATE1 - -/* The fd number threshold to switch to epoll */ -#define EPOLL_ENABLE_THRESHOLD 64 - -static void aio_epoll_disable(AioContext *ctx) -{ - ctx->epoll_enabled = false; - if (!ctx->epoll_available) { - return; - } - ctx->epoll_available = false; - close(ctx->epollfd); + return atomic_read(&ctx->poll_disable_cnt); } -static inline int epoll_events_from_pfd(int pfd_events) +void aio_add_ready_handler(AioHandlerList *ready_list, + AioHandler *node, + int revents) { - return (pfd_events & G_IO_IN ? EPOLLIN : 0) | - (pfd_events & G_IO_OUT ? EPOLLOUT : 0) | - (pfd_events & G_IO_HUP ? EPOLLHUP : 0) | - (pfd_events & G_IO_ERR ? EPOLLERR : 0); + QLIST_SAFE_REMOVE(node, node_ready); /* remove from nested parent's list */ + node->pfd.revents = revents; + QLIST_INSERT_HEAD(ready_list, node, node_ready); } -static bool aio_epoll_try_enable(AioContext *ctx) -{ - AioHandler *node; - struct epoll_event event; - - QLIST_FOREACH_RCU(node, &ctx->aio_handlers, node) { - int r; - if (node->deleted || !node->pfd.events) { - continue; - } - event.events = epoll_events_from_pfd(node->pfd.events); - event.data.ptr = node; - r = epoll_ctl(ctx->epollfd, EPOLL_CTL_ADD, node->pfd.fd, &event); - if (r) { - return false; - } - } - ctx->epoll_enabled = true; - return true; -} - -static void aio_epoll_update(AioContext *ctx, AioHandler *node, bool is_new) -{ - struct epoll_event event; - int r; - int ctl; - - if (!ctx->epoll_enabled) { - return; - } - if (!node->pfd.events) { - ctl = EPOLL_CTL_DEL; - } else { - event.data.ptr = node; - event.events = epoll_events_from_pfd(node->pfd.events); - ctl = is_new ? EPOLL_CTL_ADD : EPOLL_CTL_MOD; - } - - r = epoll_ctl(ctx->epollfd, ctl, node->pfd.fd, &event); - if (r) { - aio_epoll_disable(ctx); - } -} - -static int aio_epoll(AioContext *ctx, GPollFD *pfds, - unsigned npfd, int64_t timeout) -{ - AioHandler *node; - int i, ret = 0; - struct epoll_event events[128]; - - assert(npfd == 1); - assert(pfds[0].fd == ctx->epollfd); - if (timeout > 0) { - ret = qemu_poll_ns(pfds, npfd, timeout); - } - if (timeout <= 0 || ret > 0) { - ret = epoll_wait(ctx->epollfd, events, - ARRAY_SIZE(events), - timeout); - if (ret <= 0) { - goto out; - } - for (i = 0; i < ret; i++) { - int ev = events[i].events; - node = events[i].data.ptr; - node->pfd.revents = (ev & EPOLLIN ? G_IO_IN : 0) | - (ev & EPOLLOUT ? G_IO_OUT : 0) | - (ev & EPOLLHUP ? G_IO_HUP : 0) | - (ev & EPOLLERR ? G_IO_ERR : 0); - } - } -out: - return ret; -} - -static bool aio_epoll_enabled(AioContext *ctx) -{ - /* Fall back to ppoll when external clients are disabled. */ - return !aio_external_disabled(ctx) && ctx->epoll_enabled; -} - -static bool aio_epoll_check_poll(AioContext *ctx, GPollFD *pfds, - unsigned npfd, int64_t timeout) -{ - if (!ctx->epoll_available) { - return false; - } - if (aio_epoll_enabled(ctx)) { - return true; - } - if (npfd >= EPOLL_ENABLE_THRESHOLD) { - if (aio_epoll_try_enable(ctx)) { - return true; - } else { - aio_epoll_disable(ctx); - } - } - return false; -} - -#else - -static void aio_epoll_update(AioContext *ctx, AioHandler *node, bool is_new) -{ -} - -static int aio_epoll(AioContext *ctx, GPollFD *pfds, - unsigned npfd, int64_t timeout) -{ - assert(false); -} - -static bool aio_epoll_enabled(AioContext *ctx) -{ - return false; -} - -static bool aio_epoll_check_poll(AioContext *ctx, GPollFD *pfds, - unsigned npfd, int64_t timeout) -{ - return false; -} - -#endif - static AioHandler *find_aio_handler(AioContext *ctx, int fd) { AioHandler *node; QLIST_FOREACH(node, &ctx->aio_handlers, node) { - if (node->pfd.fd == fd) - if (!node->deleted) + if (node->pfd.fd == fd) { + if (!QLIST_IS_INSERTED(node, node_deleted)) { return node; + } + } } return NULL; @@ -210,16 +65,23 @@ static bool aio_remove_fd_handler(AioContext *ctx, AioHandler *node) g_source_remove_poll(&ctx->source, &node->pfd); } + node->pfd.revents = 0; + + /* If the fd monitor has already marked it deleted, leave it alone */ + if (QLIST_IS_INSERTED(node, node_deleted)) { + return false; + } + /* If a read is in progress, just mark the node as deleted */ if (qemu_lockcnt_count(&ctx->list_lock)) { - node->deleted = 1; - node->pfd.revents = 0; + QLIST_INSERT_HEAD_RCU(&ctx->deleted_aio_handlers, node, node_deleted); return false; } /* Otherwise, delete it for real. We can't just mark it as * deleted because deleted nodes are only cleaned up while * no one is walking the handlers list. */ + QLIST_SAFE_REMOVE(node, node_poll); QLIST_REMOVE(node, node); return true; } @@ -279,9 +141,6 @@ void aio_set_fd_handler(AioContext *ctx, QLIST_INSERT_HEAD_RCU(&ctx->aio_handlers, new_node, node); } - if (node) { - deleted = aio_remove_fd_handler(ctx, node); - } /* No need to order poll_disable_cnt writes against other updates; * the counter is only used to avoid wasting time and latency on @@ -292,11 +151,9 @@ void aio_set_fd_handler(AioContext *ctx, atomic_set(&ctx->poll_disable_cnt, atomic_read(&ctx->poll_disable_cnt) + poll_disable_change); - if (new_node) { - aio_epoll_update(ctx, new_node, is_new); - } else if (node) { - /* Unregister deleted fd_handler */ - aio_epoll_update(ctx, node, false); + ctx->fdmon_ops->update(ctx, node, new_node); + if (node) { + deleted = aio_remove_fd_handler(ctx, node); } qemu_lockcnt_unlock(&ctx->list_lock); aio_notify(ctx); @@ -340,21 +197,22 @@ void aio_set_event_notifier_poll(AioContext *ctx, (IOHandler *)io_poll_end); } -static void poll_set_started(AioContext *ctx, bool started) +static bool poll_set_started(AioContext *ctx, bool started) { AioHandler *node; + bool progress = false; if (started == ctx->poll_started) { - return; + return false; } ctx->poll_started = started; qemu_lockcnt_inc(&ctx->list_lock); - QLIST_FOREACH_RCU(node, &ctx->aio_handlers, node) { + QLIST_FOREACH(node, &ctx->poll_aio_handlers, node_poll) { IOHandler *fn; - if (node->deleted) { + if (QLIST_IS_INSERTED(node, node_deleted)) { continue; } @@ -367,8 +225,15 @@ static void poll_set_started(AioContext *ctx, bool started) if (fn) { fn(node->opaque); } + + /* Poll one last time in case ->io_poll_end() raced with the event */ + if (!started) { + progress = node->io_poll(node->opaque) || progress; + } } qemu_lockcnt_dec(&ctx->list_lock); + + return progress; } @@ -411,43 +276,99 @@ bool aio_pending(AioContext *ctx) return result; } +static void aio_free_deleted_handlers(AioContext *ctx) +{ + AioHandler *node; + + if (QLIST_EMPTY_RCU(&ctx->deleted_aio_handlers)) { + return; + } + if (!qemu_lockcnt_dec_if_lock(&ctx->list_lock)) { + return; /* we are nested, let the parent do the freeing */ + } + + while ((node = QLIST_FIRST_RCU(&ctx->deleted_aio_handlers))) { + QLIST_REMOVE(node, node); + QLIST_REMOVE(node, node_deleted); + QLIST_SAFE_REMOVE(node, node_poll); + g_free(node); + } + + qemu_lockcnt_inc_and_unlock(&ctx->list_lock); +} + +static bool aio_dispatch_handler(AioContext *ctx, AioHandler *node) +{ + bool progress = false; + int revents; + + revents = node->pfd.revents & node->pfd.events; + node->pfd.revents = 0; + + /* + * Start polling AioHandlers when they become ready because activity is + * likely to continue. Note that starvation is theoretically possible when + * fdmon_supports_polling(), but only until the fd fires for the first + * time. + */ + if (!QLIST_IS_INSERTED(node, node_deleted) && + !QLIST_IS_INSERTED(node, node_poll) && + node->io_poll) { + trace_poll_add(ctx, node, node->pfd.fd, revents); + if (ctx->poll_started && node->io_poll_begin) { + node->io_poll_begin(node->opaque); + } + QLIST_INSERT_HEAD(&ctx->poll_aio_handlers, node, node_poll); + } + + if (!QLIST_IS_INSERTED(node, node_deleted) && + (revents & (G_IO_IN | G_IO_HUP | G_IO_ERR)) && + aio_node_check(ctx, node->is_external) && + node->io_read) { + node->io_read(node->opaque); + + /* aio_notify() does not count as progress */ + if (node->opaque != &ctx->notifier) { + progress = true; + } + } + if (!QLIST_IS_INSERTED(node, node_deleted) && + (revents & (G_IO_OUT | G_IO_ERR)) && + aio_node_check(ctx, node->is_external) && + node->io_write) { + node->io_write(node->opaque); + progress = true; + } + + return progress; +} + +/* + * If we have a list of ready handlers then this is more efficient than + * scanning all handlers with aio_dispatch_handlers(). + */ +static bool aio_dispatch_ready_handlers(AioContext *ctx, + AioHandlerList *ready_list) +{ + bool progress = false; + AioHandler *node; + + while ((node = QLIST_FIRST(ready_list))) { + QLIST_REMOVE(node, node_ready); + progress = aio_dispatch_handler(ctx, node) || progress; + } + + return progress; +} + +/* Slower than aio_dispatch_ready_handlers() but only used via glib */ static bool aio_dispatch_handlers(AioContext *ctx) { AioHandler *node, *tmp; bool progress = false; QLIST_FOREACH_SAFE_RCU(node, &ctx->aio_handlers, node, tmp) { - int revents; - - revents = node->pfd.revents & node->pfd.events; - node->pfd.revents = 0; - - if (!node->deleted && - (revents & (G_IO_IN | G_IO_HUP | G_IO_ERR)) && - aio_node_check(ctx, node->is_external) && - node->io_read) { - node->io_read(node->opaque); - - /* aio_notify() does not count as progress */ - if (node->opaque != &ctx->notifier) { - progress = true; - } - } - if (!node->deleted && - (revents & (G_IO_OUT | G_IO_ERR)) && - aio_node_check(ctx, node->is_external) && - node->io_write) { - node->io_write(node->opaque); - progress = true; - } - - if (node->deleted) { - if (qemu_lockcnt_dec_if_lock(&ctx->list_lock)) { - QLIST_REMOVE(node, node); - g_free(node); - qemu_lockcnt_inc_and_unlock(&ctx->list_lock); - } - } + progress = aio_dispatch_handler(ctx, node) || progress; } return progress; @@ -458,66 +379,25 @@ void aio_dispatch(AioContext *ctx) qemu_lockcnt_inc(&ctx->list_lock); aio_bh_poll(ctx); aio_dispatch_handlers(ctx); + aio_free_deleted_handlers(ctx); qemu_lockcnt_dec(&ctx->list_lock); timerlistgroup_run_timers(&ctx->tlg); } -/* These thread-local variables are used only in a small part of aio_poll - * around the call to the poll() system call. In particular they are not - * used while aio_poll is performing callbacks, which makes it much easier - * to think about reentrancy! - * - * Stack-allocated arrays would be perfect but they have size limitations; - * heap allocation is expensive enough that we want to reuse arrays across - * calls to aio_poll(). And because poll() has to be called without holding - * any lock, the arrays cannot be stored in AioContext. Thread-local data - * has none of the disadvantages of these three options. - */ -static __thread GPollFD *pollfds; -static __thread AioHandler **nodes; -static __thread unsigned npfd, nalloc; -static __thread Notifier pollfds_cleanup_notifier; - -static void pollfds_cleanup(Notifier *n, void *unused) -{ - g_assert(npfd == 0); - g_free(pollfds); - g_free(nodes); - nalloc = 0; -} - -static void add_pollfd(AioHandler *node) -{ - if (npfd == nalloc) { - if (nalloc == 0) { - pollfds_cleanup_notifier.notify = pollfds_cleanup; - qemu_thread_atexit_add(&pollfds_cleanup_notifier); - nalloc = 8; - } else { - g_assert(nalloc <= INT_MAX); - nalloc *= 2; - } - pollfds = g_renew(GPollFD, pollfds, nalloc); - nodes = g_renew(AioHandler *, nodes, nalloc); - } - nodes[npfd] = node; - pollfds[npfd] = (GPollFD) { - .fd = node->pfd.fd, - .events = node->pfd.events, - }; - npfd++; -} - -static bool run_poll_handlers_once(AioContext *ctx, int64_t *timeout) +static bool run_poll_handlers_once(AioContext *ctx, + int64_t now, + int64_t *timeout) { bool progress = false; AioHandler *node; + AioHandler *tmp; - QLIST_FOREACH_RCU(node, &ctx->aio_handlers, node) { - if (!node->deleted && node->io_poll && - aio_node_check(ctx, node->is_external) && + QLIST_FOREACH_SAFE(node, &ctx->poll_aio_handlers, node_poll, tmp) { + if (aio_node_check(ctx, node->is_external) && node->io_poll(node->opaque)) { + node->poll_idle_timeout = now + POLL_IDLE_INTERVAL_NS; + /* * Polling was successful, exit try_poll_mode immediately * to adjust the next polling time. @@ -534,6 +414,50 @@ static bool run_poll_handlers_once(AioContext *ctx, int64_t *timeout) return progress; } +static bool fdmon_supports_polling(AioContext *ctx) +{ + return ctx->fdmon_ops->need_wait != aio_poll_disabled; +} + +static bool remove_idle_poll_handlers(AioContext *ctx, int64_t now) +{ + AioHandler *node; + AioHandler *tmp; + bool progress = false; + + /* + * File descriptor monitoring implementations without userspace polling + * support suffer from starvation when a subset of handlers is polled + * because fds will not be processed in a timely fashion. Don't remove + * idle poll handlers. + */ + if (!fdmon_supports_polling(ctx)) { + return false; + } + + QLIST_FOREACH_SAFE(node, &ctx->poll_aio_handlers, node_poll, tmp) { + if (node->poll_idle_timeout == 0LL) { + node->poll_idle_timeout = now + POLL_IDLE_INTERVAL_NS; + } else if (now >= node->poll_idle_timeout) { + trace_poll_remove(ctx, node, node->pfd.fd); + node->poll_idle_timeout = 0LL; + QLIST_SAFE_REMOVE(node, node_poll); + if (ctx->poll_started && node->io_poll_end) { + node->io_poll_end(node->opaque); + + /* + * Final poll in case ->io_poll_end() races with an event. + * Nevermind about re-adding the handler in the rare case where + * this causes progress. + */ + progress = node->io_poll(node->opaque) || progress; + } + } + } + + return progress; +} + /* run_poll_handlers: * @ctx: the AioContext * @max_ns: maximum time to poll for, in nanoseconds @@ -557,13 +481,28 @@ static bool run_poll_handlers(AioContext *ctx, int64_t max_ns, int64_t *timeout) trace_run_poll_handlers_begin(ctx, max_ns, *timeout); + /* + * Optimization: ->io_poll() handlers often contain RCU read critical + * sections and we therefore see many rcu_read_lock() -> rcu_read_unlock() + * -> rcu_read_lock() -> ... sequences with expensive memory + * synchronization primitives. Make the entire polling loop an RCU + * critical section because nested rcu_read_lock()/rcu_read_unlock() calls + * are cheap. + */ + RCU_READ_LOCK_GUARD(); + start_time = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); do { - progress = run_poll_handlers_once(ctx, timeout); + progress = run_poll_handlers_once(ctx, start_time, timeout); elapsed_time = qemu_clock_get_ns(QEMU_CLOCK_REALTIME) - start_time; max_ns = qemu_soonest_timeout(*timeout, max_ns); assert(!(max_ns && progress)); - } while (elapsed_time < max_ns && !atomic_read(&ctx->poll_disable_cnt)); + } while (elapsed_time < max_ns && !ctx->fdmon_ops->need_wait(ctx)); + + if (remove_idle_poll_handlers(ctx, start_time + elapsed_time)) { + *timeout = 0; + progress = true; + } /* If time has passed with no successful polling, adjust *timeout to * keep the same ending time. @@ -589,9 +528,14 @@ static bool run_poll_handlers(AioContext *ctx, int64_t max_ns, int64_t *timeout) */ static bool try_poll_mode(AioContext *ctx, int64_t *timeout) { - int64_t max_ns = qemu_soonest_timeout(*timeout, ctx->poll_ns); + int64_t max_ns; - if (max_ns && !atomic_read(&ctx->poll_disable_cnt)) { + if (QLIST_EMPTY_RCU(&ctx->poll_aio_handlers)) { + return false; + } + + max_ns = qemu_soonest_timeout(*timeout, ctx->poll_ns); + if (max_ns && !ctx->fdmon_ops->need_wait(ctx)) { poll_set_started(ctx, true); if (run_poll_handlers(ctx, max_ns, timeout)) { @@ -599,23 +543,27 @@ static bool try_poll_mode(AioContext *ctx, int64_t *timeout) } } - poll_set_started(ctx, false); + if (poll_set_started(ctx, false)) { + *timeout = 0; + return true; + } - /* Even if we don't run busy polling, try polling once in case it can make - * progress and the caller will be able to avoid ppoll(2)/epoll_wait(2). - */ - return run_poll_handlers_once(ctx, timeout); + return false; } bool aio_poll(AioContext *ctx, bool blocking) { - AioHandler *node; - int i; + AioHandlerList ready_list = QLIST_HEAD_INITIALIZER(ready_list); int ret = 0; bool progress; int64_t timeout; int64_t start = 0; + /* + * There cannot be two concurrent aio_poll calls for the same AioContext (or + * an aio_poll concurrent with a GSource prepare/check/dispatch callback). + * We rely on this below to avoid slow locked accesses to ctx->notify_me. + */ assert(in_aio_context_home_thread(ctx)); /* aio_notify can avoid the expensive event_notifier_set if @@ -626,7 +574,13 @@ bool aio_poll(AioContext *ctx, bool blocking) * so disable the optimization now. */ if (blocking) { - atomic_add(&ctx->notify_me, 2); + atomic_set(&ctx->notify_me, atomic_read(&ctx->notify_me) + 2); + /* + * Write ctx->notify_me before computing the timeout + * (reading bottom half flags, etc.). Pairs with + * smp_mb in aio_notify(). + */ + smp_mb(); } qemu_lockcnt_inc(&ctx->list_lock); @@ -642,36 +596,13 @@ bool aio_poll(AioContext *ctx, bool blocking) /* If polling is allowed, non-blocking aio_poll does not need the * system call---a single round of run_poll_handlers_once suffices. */ - if (timeout || atomic_read(&ctx->poll_disable_cnt)) { - assert(npfd == 0); - - /* fill pollfds */ - - if (!aio_epoll_enabled(ctx)) { - QLIST_FOREACH_RCU(node, &ctx->aio_handlers, node) { - if (!node->deleted && node->pfd.events - && aio_node_check(ctx, node->is_external)) { - add_pollfd(node); - } - } - } - - /* wait until next event */ - if (aio_epoll_check_poll(ctx, pollfds, npfd, timeout)) { - AioHandler epoll_handler; - - epoll_handler.pfd.fd = ctx->epollfd; - epoll_handler.pfd.events = G_IO_IN | G_IO_OUT | G_IO_HUP | G_IO_ERR; - npfd = 0; - add_pollfd(&epoll_handler); - ret = aio_epoll(ctx, pollfds, npfd, timeout); - } else { - ret = qemu_poll_ns(pollfds, npfd, timeout); - } + if (timeout || ctx->fdmon_ops->need_wait(ctx)) { + ret = ctx->fdmon_ops->wait(ctx, &ready_list, timeout); } if (blocking) { - atomic_sub(&ctx->notify_me, 2); + /* Finish the poll before clearing the flag. */ + atomic_store_release(&ctx->notify_me, atomic_read(&ctx->notify_me) - 2); aio_notify_accept(ctx); } @@ -716,21 +647,14 @@ bool aio_poll(AioContext *ctx, bool blocking) } } - /* if we have any readable fds, dispatch event */ - if (ret > 0) { - for (i = 0; i < npfd; i++) { - nodes[i]->pfd.revents = pollfds[i].revents; - } - } - - npfd = 0; - progress |= aio_bh_poll(ctx); if (ret > 0) { - progress |= aio_dispatch_handlers(ctx); + progress |= aio_dispatch_ready_handlers(ctx, &ready_list); } + aio_free_deleted_handlers(ctx); + qemu_lockcnt_dec(&ctx->list_lock); progress |= timerlistgroup_run_timers(&ctx->tlg); @@ -740,23 +664,21 @@ bool aio_poll(AioContext *ctx, bool blocking) void aio_context_setup(AioContext *ctx) { -#ifdef CONFIG_EPOLL_CREATE1 - assert(!ctx->epollfd); - ctx->epollfd = epoll_create1(EPOLL_CLOEXEC); - if (ctx->epollfd == -1) { - fprintf(stderr, "Failed to create epoll instance: %s", strerror(errno)); - ctx->epoll_available = false; - } else { - ctx->epoll_available = true; + ctx->fdmon_ops = &fdmon_poll_ops; + ctx->epollfd = -1; + + /* Use the fastest fd monitoring implementation if available */ + if (fdmon_io_uring_setup(ctx)) { + return; } -#endif + + fdmon_epoll_setup(ctx); } void aio_context_destroy(AioContext *ctx) { -#ifdef CONFIG_EPOLL_CREATE1 - aio_epoll_disable(ctx); -#endif + fdmon_io_uring_destroy(ctx); + fdmon_epoll_disable(ctx); } void aio_context_set_poll_params(AioContext *ctx, int64_t max_ns, diff --git a/util/aio-posix.h b/util/aio-posix.h new file mode 100644 index 0000000000..c80c04506a --- /dev/null +++ b/util/aio-posix.h @@ -0,0 +1,81 @@ +/* + * AioContext POSIX event loop implementation internal APIs + * + * Copyright IBM, Corp. 2008 + * Copyright Red Hat, Inc. 2020 + * + * Authors: + * Anthony Liguori + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + * Contributions after 2012-01-13 are licensed under the terms of the + * GNU GPL, version 2 or (at your option) any later version. + */ + +#ifndef AIO_POSIX_H +#define AIO_POSIX_H + +#include "block/aio.h" + +struct AioHandler { + GPollFD pfd; + IOHandler *io_read; + IOHandler *io_write; + AioPollFn *io_poll; + IOHandler *io_poll_begin; + IOHandler *io_poll_end; + void *opaque; + QLIST_ENTRY(AioHandler) node; + QLIST_ENTRY(AioHandler) node_ready; /* only used during aio_poll() */ + QLIST_ENTRY(AioHandler) node_deleted; + QLIST_ENTRY(AioHandler) node_poll; +#ifdef CONFIG_LINUX_IO_URING + QSLIST_ENTRY(AioHandler) node_submitted; + unsigned flags; /* see fdmon-io_uring.c */ +#endif + int64_t poll_idle_timeout; /* when to stop userspace polling */ + bool is_external; +}; + +/* Add a handler to a ready list */ +void aio_add_ready_handler(AioHandlerList *ready_list, AioHandler *node, + int revents); + +extern const FDMonOps fdmon_poll_ops; + +#ifdef CONFIG_EPOLL_CREATE1 +bool fdmon_epoll_try_upgrade(AioContext *ctx, unsigned npfd); +void fdmon_epoll_setup(AioContext *ctx); +void fdmon_epoll_disable(AioContext *ctx); +#else +static inline bool fdmon_epoll_try_upgrade(AioContext *ctx, unsigned npfd) +{ + return false; +} + +static inline void fdmon_epoll_setup(AioContext *ctx) +{ +} + +static inline void fdmon_epoll_disable(AioContext *ctx) +{ +} +#endif /* !CONFIG_EPOLL_CREATE1 */ + +#ifdef CONFIG_LINUX_IO_URING +bool fdmon_io_uring_setup(AioContext *ctx); +void fdmon_io_uring_destroy(AioContext *ctx); +#else +static inline bool fdmon_io_uring_setup(AioContext *ctx) +{ + return false; +} + +static inline void fdmon_io_uring_destroy(AioContext *ctx) +{ +} +#endif /* !CONFIG_LINUX_IO_URING */ + +#endif /* AIO_POSIX_H */ diff --git a/util/aio-win32.c b/util/aio-win32.c index a23b9c364d..729d533faf 100644 --- a/util/aio-win32.c +++ b/util/aio-win32.c @@ -321,6 +321,12 @@ bool aio_poll(AioContext *ctx, bool blocking) int count; int timeout; + /* + * There cannot be two concurrent aio_poll calls for the same AioContext (or + * an aio_poll concurrent with a GSource prepare/check/dispatch callback). + * We rely on this below to avoid slow locked accesses to ctx->notify_me. + */ + assert(in_aio_context_home_thread(ctx)); progress = false; /* aio_notify can avoid the expensive event_notifier_set if @@ -331,7 +337,13 @@ bool aio_poll(AioContext *ctx, bool blocking) * so disable the optimization now. */ if (blocking) { - atomic_add(&ctx->notify_me, 2); + atomic_set(&ctx->notify_me, atomic_read(&ctx->notify_me) + 2); + /* + * Write ctx->notify_me before computing the timeout + * (reading bottom half flags, etc.). Pairs with + * smp_mb in aio_notify(). + */ + smp_mb(); } qemu_lockcnt_inc(&ctx->list_lock); @@ -364,8 +376,7 @@ bool aio_poll(AioContext *ctx, bool blocking) ret = WaitForMultipleObjects(count, events, FALSE, timeout); if (blocking) { assert(first); - assert(in_aio_context_home_thread(ctx)); - atomic_sub(&ctx->notify_me, 2); + atomic_store_release(&ctx->notify_me, atomic_read(&ctx->notify_me) - 2); aio_notify_accept(ctx); } diff --git a/util/async.c b/util/async.c index b1fa5319e5..3165a28f2f 100644 --- a/util/async.c +++ b/util/async.c @@ -29,6 +29,7 @@ #include "block/thread-pool.h" #include "qemu/main-loop.h" #include "qemu/atomic.h" +#include "qemu/rcu_queue.h" #include "block/raw-aio.h" #include "qemu/coroutine_int.h" #include "trace.h" @@ -36,16 +37,76 @@ /***********************************************************/ /* bottom halves (can be seen as timers which expire ASAP) */ +/* QEMUBH::flags values */ +enum { + /* Already enqueued and waiting for aio_bh_poll() */ + BH_PENDING = (1 << 0), + + /* Invoke the callback */ + BH_SCHEDULED = (1 << 1), + + /* Delete without invoking callback */ + BH_DELETED = (1 << 2), + + /* Delete after invoking callback */ + BH_ONESHOT = (1 << 3), + + /* Schedule periodically when the event loop is idle */ + BH_IDLE = (1 << 4), +}; + struct QEMUBH { AioContext *ctx; QEMUBHFunc *cb; void *opaque; - QEMUBH *next; - bool scheduled; - bool idle; - bool deleted; + QSLIST_ENTRY(QEMUBH) next; + unsigned flags; }; +/* Called concurrently from any thread */ +static void aio_bh_enqueue(QEMUBH *bh, unsigned new_flags) +{ + AioContext *ctx = bh->ctx; + unsigned old_flags; + + /* + * The memory barrier implicit in atomic_fetch_or makes sure that: + * 1. idle & any writes needed by the callback are done before the + * locations are read in the aio_bh_poll. + * 2. ctx is loaded before the callback has a chance to execute and bh + * could be freed. + */ + old_flags = atomic_fetch_or(&bh->flags, BH_PENDING | new_flags); + if (!(old_flags & BH_PENDING)) { + QSLIST_INSERT_HEAD_ATOMIC(&ctx->bh_list, bh, next); + } + + aio_notify(ctx); +} + +/* Only called from aio_bh_poll() and aio_ctx_finalize() */ +static QEMUBH *aio_bh_dequeue(BHList *head, unsigned *flags) +{ + QEMUBH *bh = QSLIST_FIRST_RCU(head); + + if (!bh) { + return NULL; + } + + QSLIST_REMOVE_HEAD(head, next); + + /* + * The atomic_and is paired with aio_bh_enqueue(). The implicit memory + * barrier ensures that the callback sees all writes done by the scheduling + * thread. It also ensures that the scheduling thread sees the cleared + * flag before bh->cb has run, and thus will call aio_notify again if + * necessary. + */ + *flags = atomic_fetch_and(&bh->flags, + ~(BH_PENDING | BH_SCHEDULED | BH_IDLE)); + return bh; +} + void aio_bh_schedule_oneshot(AioContext *ctx, QEMUBHFunc *cb, void *opaque) { QEMUBH *bh; @@ -55,15 +116,7 @@ void aio_bh_schedule_oneshot(AioContext *ctx, QEMUBHFunc *cb, void *opaque) .cb = cb, .opaque = opaque, }; - qemu_lockcnt_lock(&ctx->list_lock); - bh->next = ctx->first_bh; - bh->scheduled = 1; - bh->deleted = 1; - /* Make sure that the members are ready before putting bh into list */ - smp_wmb(); - ctx->first_bh = bh; - qemu_lockcnt_unlock(&ctx->list_lock); - aio_notify(ctx); + aio_bh_enqueue(bh, BH_SCHEDULED | BH_ONESHOT); } QEMUBH *aio_bh_new(AioContext *ctx, QEMUBHFunc *cb, void *opaque) @@ -75,12 +128,6 @@ QEMUBH *aio_bh_new(AioContext *ctx, QEMUBHFunc *cb, void *opaque) .cb = cb, .opaque = opaque, }; - qemu_lockcnt_lock(&ctx->list_lock); - bh->next = ctx->first_bh; - /* Make sure that the members are ready before putting bh into list */ - smp_wmb(); - ctx->first_bh = bh; - qemu_lockcnt_unlock(&ctx->list_lock); return bh; } @@ -89,91 +136,56 @@ void aio_bh_call(QEMUBH *bh) bh->cb(bh->opaque); } -/* Multiple occurrences of aio_bh_poll cannot be called concurrently. - * The count in ctx->list_lock is incremented before the call, and is - * not affected by the call. - */ +/* Multiple occurrences of aio_bh_poll cannot be called concurrently. */ int aio_bh_poll(AioContext *ctx) { - QEMUBH *bh, **bhp, *next; - int ret; - bool deleted = false; + BHListSlice slice; + BHListSlice *s; + int ret = 0; - ret = 0; - for (bh = atomic_rcu_read(&ctx->first_bh); bh; bh = next) { - next = atomic_rcu_read(&bh->next); - /* The atomic_xchg is paired with the one in qemu_bh_schedule. The - * implicit memory barrier ensures that the callback sees all writes - * done by the scheduling thread. It also ensures that the scheduling - * thread sees the zero before bh->cb has run, and thus will call - * aio_notify again if necessary. - */ - if (atomic_xchg(&bh->scheduled, 0)) { + QSLIST_MOVE_ATOMIC(&slice.bh_list, &ctx->bh_list); + QSIMPLEQ_INSERT_TAIL(&ctx->bh_slice_list, &slice, next); + + while ((s = QSIMPLEQ_FIRST(&ctx->bh_slice_list))) { + QEMUBH *bh; + unsigned flags; + + bh = aio_bh_dequeue(&s->bh_list, &flags); + if (!bh) { + QSIMPLEQ_REMOVE_HEAD(&ctx->bh_slice_list, next); + continue; + } + + if ((flags & (BH_SCHEDULED | BH_DELETED)) == BH_SCHEDULED) { /* Idle BHs don't count as progress */ - if (!bh->idle) { + if (!(flags & BH_IDLE)) { ret = 1; } - bh->idle = 0; aio_bh_call(bh); } - if (bh->deleted) { - deleted = true; + if (flags & (BH_DELETED | BH_ONESHOT)) { + g_free(bh); } } - /* remove deleted bhs */ - if (!deleted) { - return ret; - } - - if (qemu_lockcnt_dec_if_lock(&ctx->list_lock)) { - bhp = &ctx->first_bh; - while (*bhp) { - bh = *bhp; - if (bh->deleted && !bh->scheduled) { - *bhp = bh->next; - g_free(bh); - } else { - bhp = &bh->next; - } - } - qemu_lockcnt_inc_and_unlock(&ctx->list_lock); - } return ret; } void qemu_bh_schedule_idle(QEMUBH *bh) { - bh->idle = 1; - /* Make sure that idle & any writes needed by the callback are done - * before the locations are read in the aio_bh_poll. - */ - atomic_mb_set(&bh->scheduled, 1); + aio_bh_enqueue(bh, BH_SCHEDULED | BH_IDLE); } void qemu_bh_schedule(QEMUBH *bh) { - AioContext *ctx; - - ctx = bh->ctx; - bh->idle = 0; - /* The memory barrier implicit in atomic_xchg makes sure that: - * 1. idle & any writes needed by the callback are done before the - * locations are read in the aio_bh_poll. - * 2. ctx is loaded before scheduled is set and the callback has a chance - * to execute. - */ - if (atomic_xchg(&bh->scheduled, 1) == 0) { - aio_notify(ctx); - } + aio_bh_enqueue(bh, BH_SCHEDULED); } - /* This func is async. */ void qemu_bh_cancel(QEMUBH *bh) { - atomic_mb_set(&bh->scheduled, 0); + atomic_and(&bh->flags, ~BH_SCHEDULED); } /* This func is async.The bottom half will do the delete action at the finial @@ -181,21 +193,16 @@ void qemu_bh_cancel(QEMUBH *bh) */ void qemu_bh_delete(QEMUBH *bh) { - bh->scheduled = 0; - bh->deleted = 1; + aio_bh_enqueue(bh, BH_DELETED); } -int64_t -aio_compute_timeout(AioContext *ctx) +static int64_t aio_compute_bh_timeout(BHList *head, int timeout) { - int64_t deadline; - int timeout = -1; QEMUBH *bh; - for (bh = atomic_rcu_read(&ctx->first_bh); bh; - bh = atomic_rcu_read(&bh->next)) { - if (bh->scheduled) { - if (bh->idle) { + QSLIST_FOREACH_RCU(bh, head, next) { + if ((bh->flags & (BH_SCHEDULED | BH_DELETED)) == BH_SCHEDULED) { + if (bh->flags & BH_IDLE) { /* idle bottom halves will be polled at least * every 10ms */ timeout = 10000000; @@ -207,6 +214,28 @@ aio_compute_timeout(AioContext *ctx) } } + return timeout; +} + +int64_t +aio_compute_timeout(AioContext *ctx) +{ + BHListSlice *s; + int64_t deadline; + int timeout = -1; + + timeout = aio_compute_bh_timeout(&ctx->bh_list, timeout); + if (timeout == 0) { + return 0; + } + + QSIMPLEQ_FOREACH(s, &ctx->bh_slice_list, next) { + timeout = aio_compute_bh_timeout(&s->bh_list, timeout); + if (timeout == 0) { + return 0; + } + } + deadline = timerlistgroup_deadline_ns(&ctx->tlg); if (deadline == 0) { return 0; @@ -220,7 +249,14 @@ aio_ctx_prepare(GSource *source, gint *timeout) { AioContext *ctx = (AioContext *) source; - atomic_or(&ctx->notify_me, 1); + atomic_set(&ctx->notify_me, atomic_read(&ctx->notify_me) | 1); + + /* + * Write ctx->notify_me before computing the timeout + * (reading bottom half flags, etc.). Pairs with + * smp_mb in aio_notify(). + */ + smp_mb(); /* We assume there is no timeout already supplied */ *timeout = qemu_timeout_ns_to_ms(aio_compute_timeout(ctx)); @@ -237,15 +273,25 @@ aio_ctx_check(GSource *source) { AioContext *ctx = (AioContext *) source; QEMUBH *bh; + BHListSlice *s; - atomic_and(&ctx->notify_me, ~1); + /* Finish computing the timeout before clearing the flag. */ + atomic_store_release(&ctx->notify_me, atomic_read(&ctx->notify_me) & ~1); aio_notify_accept(ctx); - for (bh = ctx->first_bh; bh; bh = bh->next) { - if (bh->scheduled) { + QSLIST_FOREACH_RCU(bh, &ctx->bh_list, next) { + if ((bh->flags & (BH_SCHEDULED | BH_DELETED)) == BH_SCHEDULED) { return true; } } + + QSIMPLEQ_FOREACH(s, &ctx->bh_slice_list, next) { + QSLIST_FOREACH_RCU(bh, &s->bh_list, next) { + if ((bh->flags & (BH_SCHEDULED | BH_DELETED)) == BH_SCHEDULED) { + return true; + } + } + } return aio_pending(ctx) || (timerlistgroup_deadline_ns(&ctx->tlg) == 0); } @@ -265,6 +311,8 @@ static void aio_ctx_finalize(GSource *source) { AioContext *ctx = (AioContext *) source; + QEMUBH *bh; + unsigned flags; thread_pool_free(ctx->thread_pool); @@ -276,21 +324,26 @@ aio_ctx_finalize(GSource *source) } #endif +#ifdef CONFIG_LINUX_IO_URING + if (ctx->linux_io_uring) { + luring_detach_aio_context(ctx->linux_io_uring, ctx); + luring_cleanup(ctx->linux_io_uring); + ctx->linux_io_uring = NULL; + } +#endif + assert(QSLIST_EMPTY(&ctx->scheduled_coroutines)); qemu_bh_delete(ctx->co_schedule_bh); - qemu_lockcnt_lock(&ctx->list_lock); - assert(!qemu_lockcnt_count(&ctx->list_lock)); - while (ctx->first_bh) { - QEMUBH *next = ctx->first_bh->next; + /* There must be no aio_bh_poll() calls going on */ + assert(QSIMPLEQ_EMPTY(&ctx->bh_slice_list)); + while ((bh = aio_bh_dequeue(&ctx->bh_list, &flags))) { /* qemu_bh_delete() must have been called on BHs in this AioContext */ - assert(ctx->first_bh->deleted); + assert(flags & BH_DELETED); - g_free(ctx->first_bh); - ctx->first_bh = next; + g_free(bh); } - qemu_lockcnt_unlock(&ctx->list_lock); aio_set_event_notifier(ctx, &ctx->notifier, false, NULL, NULL); event_notifier_cleanup(&ctx->notifier); @@ -340,13 +393,36 @@ LinuxAioState *aio_get_linux_aio(AioContext *ctx) } #endif +#ifdef CONFIG_LINUX_IO_URING +LuringState *aio_setup_linux_io_uring(AioContext *ctx, Error **errp) +{ + if (ctx->linux_io_uring) { + return ctx->linux_io_uring; + } + + ctx->linux_io_uring = luring_init(errp); + if (!ctx->linux_io_uring) { + return NULL; + } + + luring_attach_aio_context(ctx->linux_io_uring, ctx); + return ctx->linux_io_uring; +} + +LuringState *aio_get_linux_io_uring(AioContext *ctx) +{ + assert(ctx->linux_io_uring); + return ctx->linux_io_uring; +} +#endif + void aio_notify(AioContext *ctx) { /* Write e.g. bh->scheduled before reading ctx->notify_me. Pairs - * with atomic_or in aio_ctx_prepare or atomic_add in aio_poll. + * with smp_mb in aio_ctx_prepare or aio_poll. */ smp_mb(); - if (ctx->notify_me) { + if (atomic_read(&ctx->notify_me)) { event_notifier_set(&ctx->notifier); atomic_mb_set(&ctx->notified, true); } @@ -414,6 +490,8 @@ AioContext *aio_context_new(Error **errp) AioContext *ctx; ctx = (AioContext *) g_source_new(&aio_source_funcs, sizeof(AioContext)); + QSLIST_INIT(&ctx->bh_list); + QSIMPLEQ_INIT(&ctx->bh_slice_list); aio_context_setup(ctx); ret = event_notifier_init(&ctx->notifier, false); @@ -434,6 +512,11 @@ AioContext *aio_context_new(Error **errp) #ifdef CONFIG_LINUX_AIO ctx->linux_aio = NULL; #endif + +#ifdef CONFIG_LINUX_IO_URING + ctx->linux_io_uring = NULL; +#endif + ctx->thread_pool = NULL; qemu_rec_mutex_init(&ctx->lock); timerlistgroup_init(&ctx->tlg, aio_timerlist_notify, ctx); diff --git a/util/bufferiszero.c b/util/bufferiszero.c index bfb2605466..695bb4ce28 100644 --- a/util/bufferiszero.c +++ b/util/bufferiszero.c @@ -63,11 +63,11 @@ buffer_zero_int(const void *buf, size_t len) } } -#if defined(CONFIG_AVX2_OPT) || defined(__SSE2__) +#if defined(CONFIG_AVX512F_OPT) || defined(CONFIG_AVX2_OPT) || defined(__SSE2__) /* Do not use push_options pragmas unnecessarily, because clang * does not support them. */ -#ifdef CONFIG_AVX2_OPT +#if defined(CONFIG_AVX512F_OPT) || defined(CONFIG_AVX2_OPT) #pragma GCC push_options #pragma GCC target("sse2") #endif @@ -104,7 +104,7 @@ buffer_zero_sse2(const void *buf, size_t len) return _mm_movemask_epi8(_mm_cmpeq_epi8(t, zero)) == 0xFFFF; } -#ifdef CONFIG_AVX2_OPT +#if defined(CONFIG_AVX512F_OPT) || defined(CONFIG_AVX2_OPT) #pragma GCC pop_options #endif @@ -158,27 +158,19 @@ buffer_zero_avx2(const void *buf, size_t len) __m256i *p = (__m256i *)(((uintptr_t)buf + 5 * 32) & -32); __m256i *e = (__m256i *)(((uintptr_t)buf + len) & -32); - if (likely(p <= e)) { - /* Loop over 32-byte aligned blocks of 128. */ - do { - __builtin_prefetch(p); - if (unlikely(!_mm256_testz_si256(t, t))) { - return false; - } - t = p[-4] | p[-3] | p[-2] | p[-1]; - p += 4; - } while (p <= e); - } else { - t |= _mm256_loadu_si256(buf + 32); - if (len <= 128) { - goto last2; + /* Loop over 32-byte aligned blocks of 128. */ + while (p <= e) { + __builtin_prefetch(p); + if (unlikely(!_mm256_testz_si256(t, t))) { + return false; } - } + t = p[-4] | p[-3] | p[-2] | p[-1]; + p += 4; + } ; /* Finish the last block of 128 unaligned. */ t |= _mm256_loadu_si256(buf + len - 4 * 32); t |= _mm256_loadu_si256(buf + len - 3 * 32); - last2: t |= _mm256_loadu_si256(buf + len - 2 * 32); t |= _mm256_loadu_si256(buf + len - 1 * 32); @@ -187,18 +179,54 @@ buffer_zero_avx2(const void *buf, size_t len) #pragma GCC pop_options #endif /* CONFIG_AVX2_OPT */ +#ifdef CONFIG_AVX512F_OPT +#pragma GCC push_options +#pragma GCC target("avx512f") +#include + +static bool +buffer_zero_avx512(const void *buf, size_t len) +{ + /* Begin with an unaligned head of 64 bytes. */ + __m512i t = _mm512_loadu_si512(buf); + __m512i *p = (__m512i *)(((uintptr_t)buf + 5 * 64) & -64); + __m512i *e = (__m512i *)(((uintptr_t)buf + len) & -64); + + /* Loop over 64-byte aligned blocks of 256. */ + while (p <= e) { + __builtin_prefetch(p); + if (unlikely(_mm512_test_epi64_mask(t, t))) { + return false; + } + t = p[-4] | p[-3] | p[-2] | p[-1]; + p += 4; + } + + t |= _mm512_loadu_si512(buf + len - 4 * 64); + t |= _mm512_loadu_si512(buf + len - 3 * 64); + t |= _mm512_loadu_si512(buf + len - 2 * 64); + t |= _mm512_loadu_si512(buf + len - 1 * 64); + + return !_mm512_test_epi64_mask(t, t); + +} +#pragma GCC pop_options +#endif + + /* Note that for test_buffer_is_zero_next_accel, the most preferred * ISA must have the least significant bit. */ -#define CACHE_AVX2 1 -#define CACHE_SSE4 2 -#define CACHE_SSE2 4 +#define CACHE_AVX512F 1 +#define CACHE_AVX2 2 +#define CACHE_SSE4 4 +#define CACHE_SSE2 8 /* Make sure that these variables are appropriately initialized when * SSE2 is enabled on the compiler command-line, but the compiler is * too old to support CONFIG_AVX2_OPT. */ -#ifdef CONFIG_AVX2_OPT +#if defined(CONFIG_AVX512F_OPT) || defined(CONFIG_AVX2_OPT) # define INIT_CACHE 0 # define INIT_ACCEL buffer_zero_int #else @@ -211,25 +239,35 @@ buffer_zero_avx2(const void *buf, size_t len) static unsigned cpuid_cache = INIT_CACHE; static bool (*buffer_accel)(const void *, size_t) = INIT_ACCEL; +static int length_to_accel = 64; static void init_accel(unsigned cache) { bool (*fn)(const void *, size_t) = buffer_zero_int; if (cache & CACHE_SSE2) { fn = buffer_zero_sse2; + length_to_accel = 64; } #ifdef CONFIG_AVX2_OPT if (cache & CACHE_SSE4) { fn = buffer_zero_sse4; + length_to_accel = 64; } if (cache & CACHE_AVX2) { fn = buffer_zero_avx2; + length_to_accel = 128; + } +#endif +#ifdef CONFIG_AVX512F_OPT + if (cache & CACHE_AVX512F) { + fn = buffer_zero_avx512; + length_to_accel = 256; } #endif buffer_accel = fn; } -#ifdef CONFIG_AVX2_OPT +#if defined(CONFIG_AVX512F_OPT) || defined(CONFIG_AVX2_OPT) #include "qemu/cpuid.h" static void __attribute__((constructor)) init_cpuid_cache(void) @@ -252,9 +290,17 @@ static void __attribute__((constructor)) init_cpuid_cache(void) int bv; __asm("xgetbv" : "=a"(bv), "=d"(d) : "c"(0)); __cpuid_count(7, 0, a, b, c, d); - if ((bv & 6) == 6 && (b & bit_AVX2)) { + if ((bv & 0x6) == 0x6 && (b & bit_AVX2)) { cache |= CACHE_AVX2; } + /* 0xe6: + * XCR0[7:5] = 111b (OPMASK state, upper 256-bit of ZMM0-ZMM15 + * and ZMM16-ZMM31 state are enabled by OS) + * XCR0[2:1] = 11b (XMM state and YMM state are enabled by OS) + */ + if ((bv & 0xe6) == 0xe6 && (b & bit_AVX512F)) { + cache |= CACHE_AVX512F; + } } } cpuid_cache = cache; @@ -277,7 +323,7 @@ bool test_buffer_is_zero_next_accel(void) static bool select_accel_fn(const void *buf, size_t len) { - if (likely(len >= 64)) { + if (likely(len >= length_to_accel)) { return buffer_accel(buf, len); } return buffer_zero_int(buf, len); diff --git a/util/cacheinfo.c b/util/cacheinfo.c index ea6f3e99bf..d94dc6adc8 100644 --- a/util/cacheinfo.c +++ b/util/cacheinfo.c @@ -93,10 +93,16 @@ static void sys_cache_info(int *isize, int *dsize) static void sys_cache_info(int *isize, int *dsize) { # ifdef _SC_LEVEL1_ICACHE_LINESIZE - *isize = sysconf(_SC_LEVEL1_ICACHE_LINESIZE); + int tmp_isize = (int) sysconf(_SC_LEVEL1_ICACHE_LINESIZE); + if (tmp_isize > 0) { + *isize = tmp_isize; + } # endif # ifdef _SC_LEVEL1_DCACHE_LINESIZE - *dsize = sysconf(_SC_LEVEL1_DCACHE_LINESIZE); + int tmp_dsize = (int) sysconf(_SC_LEVEL1_DCACHE_LINESIZE); + if (tmp_dsize > 0) { + *dsize = tmp_dsize; + } # endif } #endif /* sys_cache_info */ diff --git a/util/cutils.c b/util/cutils.c index 77acadc70a..36ce712271 100644 --- a/util/cutils.c +++ b/util/cutils.c @@ -164,6 +164,44 @@ int qemu_fdatasync(int fd) #endif } +/** + * Sync changes made to the memory mapped file back to the backing + * storage. For POSIX compliant systems this will fallback + * to regular msync call. Otherwise it will trigger whole file sync + * (including the metadata case there is no support to skip that otherwise) + * + * @addr - start of the memory area to be synced + * @length - length of the are to be synced + * @fd - file descriptor for the file to be synced + * (mandatory only for POSIX non-compliant systems) + */ +int qemu_msync(void *addr, size_t length, int fd) +{ +#ifdef CONFIG_POSIX + size_t align_mask = ~(qemu_real_host_page_size - 1); + + /** + * There are no strict reqs as per the length of mapping + * to be synced. Still the length needs to follow the address + * alignment changes. Additionally - round the size to the multiple + * of PAGE_SIZE + */ + length += ((uintptr_t)addr & (qemu_real_host_page_size - 1)); + length = (length + ~align_mask) & align_mask; + + addr = (void *)((uintptr_t)addr & align_mask); + + return msync(addr, length, MS_SYNC); +#else /* CONFIG_POSIX */ + /** + * Perform the sync based on the file descriptor + * The sync range will most probably be wider than the one + * requested - but it will still get the job done + */ + return qemu_fdatasync(fd); +#endif /* CONFIG_POSIX */ +} + #ifndef _WIN32 /* Sets a specific flag */ int fcntl_setfl(int fd, int flag) @@ -504,7 +542,7 @@ int qemu_strtoul(const char *nptr, const char **endptr, int base, * Convert string @nptr to an int64_t. * * Works like qemu_strtol(), except it stores INT64_MAX on overflow, - * and INT_MIN on underflow. + * and INT64_MIN on underflow. */ int qemu_strtoi64(const char *nptr, const char **endptr, int base, int64_t *result) @@ -519,8 +557,9 @@ int qemu_strtoi64(const char *nptr, const char **endptr, int base, return -EINVAL; } + /* This assumes int64_t is long long TODO relax */ + QEMU_BUILD_BUG_ON(sizeof(int64_t) != sizeof(long long)); errno = 0; - /* FIXME This assumes int64_t is long long */ *result = strtoll(nptr, &ep, base); return check_strtox_error(nptr, ep, endptr, errno); } @@ -543,8 +582,9 @@ int qemu_strtou64(const char *nptr, const char **endptr, int base, return -EINVAL; } + /* This assumes uint64_t is unsigned long long TODO relax */ + QEMU_BUILD_BUG_ON(sizeof(uint64_t) != sizeof(unsigned long long)); errno = 0; - /* FIXME This assumes uint64_t is unsigned long long */ *result = strtoull(nptr, &ep, base); /* Windows returns 1 for negative out-of-range values. */ if (errno == ERANGE) { diff --git a/util/dbus.c b/util/dbus.c new file mode 100644 index 0000000000..9099dc5b4b --- /dev/null +++ b/util/dbus.c @@ -0,0 +1,57 @@ +/* + * Helpers for using D-Bus + * + * Copyright (C) 2019 Red Hat, Inc. + * + * 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/dbus.h" +#include "qemu/error-report.h" +#include "qapi/error.h" + +/* + * qemu_dbus_get_queued_owners() - return the list of queued unique names + * @connection: A GDBusConnection + * @name: a service name + * + * Return: a GStrv of unique names, or NULL on failure. + */ +GStrv +qemu_dbus_get_queued_owners(GDBusConnection *connection, const char *name, + Error **errp) +{ + g_autoptr(GDBusProxy) proxy = NULL; + g_autoptr(GVariant) result = NULL; + g_autoptr(GVariant) child = NULL; + g_autoptr(GError) err = NULL; + + proxy = g_dbus_proxy_new_sync(connection, G_DBUS_PROXY_FLAGS_NONE, NULL, + "org.freedesktop.DBus", + "/org/freedesktop/DBus", + "org.freedesktop.DBus", + NULL, &err); + if (!proxy) { + error_setg(errp, "Failed to create DBus proxy: %s", err->message); + return NULL; + } + + result = g_dbus_proxy_call_sync(proxy, "ListQueuedOwners", + g_variant_new("(s)", name), + G_DBUS_CALL_FLAGS_NO_AUTO_START, + -1, NULL, &err); + if (!result) { + if (g_error_matches(err, + G_DBUS_ERROR, + G_DBUS_ERROR_NAME_HAS_NO_OWNER)) { + return g_new0(char *, 1); + } + error_setg(errp, "Failed to call ListQueuedOwners: %s", err->message); + return NULL; + } + + child = g_variant_get_child_value(result, 0); + return g_variant_dup_strv(child, NULL); +} diff --git a/util/error.c b/util/error.c index d4532ce318..b6c89d1412 100644 --- a/util/error.c +++ b/util/error.c @@ -121,7 +121,7 @@ void error_setg_file_open_internal(Error **errp, "Could not open '%s'", filename); } -void error_vprepend(Error **errp, const char *fmt, va_list ap) +void error_vprepend(Error *const *errp, const char *fmt, va_list ap) { GString *newmsg; @@ -136,7 +136,7 @@ void error_vprepend(Error **errp, const char *fmt, va_list ap) (*errp)->msg = g_string_free(newmsg, 0); } -void error_prepend(Error **errp, const char *fmt, ...) +void error_prepend(Error *const *errp, const char *fmt, ...) { va_list ap; @@ -145,7 +145,7 @@ void error_prepend(Error **errp, const char *fmt, ...) va_end(ap); } -void error_append_hint(Error **errp, const char *fmt, ...) +void error_append_hint(Error *const *errp, const char *fmt, ...) { va_list ap; int saved_errno = errno; diff --git a/util/fdmon-epoll.c b/util/fdmon-epoll.c new file mode 100644 index 0000000000..fcd989d47d --- /dev/null +++ b/util/fdmon-epoll.c @@ -0,0 +1,155 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * epoll(7) file descriptor monitoring + */ + +#include "qemu/osdep.h" +#include +#include "qemu/rcu_queue.h" +#include "aio-posix.h" + +/* The fd number threshold to switch to epoll */ +#define EPOLL_ENABLE_THRESHOLD 64 + +void fdmon_epoll_disable(AioContext *ctx) +{ + if (ctx->epollfd >= 0) { + close(ctx->epollfd); + ctx->epollfd = -1; + } + + /* Switch back */ + ctx->fdmon_ops = &fdmon_poll_ops; +} + +static inline int epoll_events_from_pfd(int pfd_events) +{ + return (pfd_events & G_IO_IN ? EPOLLIN : 0) | + (pfd_events & G_IO_OUT ? EPOLLOUT : 0) | + (pfd_events & G_IO_HUP ? EPOLLHUP : 0) | + (pfd_events & G_IO_ERR ? EPOLLERR : 0); +} + +static void fdmon_epoll_update(AioContext *ctx, + AioHandler *old_node, + AioHandler *new_node) +{ + struct epoll_event event = { + .data.ptr = new_node, + .events = new_node ? epoll_events_from_pfd(new_node->pfd.events) : 0, + }; + int r; + + if (!new_node) { + r = epoll_ctl(ctx->epollfd, EPOLL_CTL_DEL, old_node->pfd.fd, &event); + } else if (!old_node) { + r = epoll_ctl(ctx->epollfd, EPOLL_CTL_ADD, new_node->pfd.fd, &event); + } else { + r = epoll_ctl(ctx->epollfd, EPOLL_CTL_MOD, new_node->pfd.fd, &event); + } + + if (r) { + fdmon_epoll_disable(ctx); + } +} + +static int fdmon_epoll_wait(AioContext *ctx, AioHandlerList *ready_list, + int64_t timeout) +{ + GPollFD pfd = { + .fd = ctx->epollfd, + .events = G_IO_IN | G_IO_OUT | G_IO_HUP | G_IO_ERR, + }; + AioHandler *node; + int i, ret = 0; + struct epoll_event events[128]; + + /* Fall back while external clients are disabled */ + if (atomic_read(&ctx->external_disable_cnt)) { + return fdmon_poll_ops.wait(ctx, ready_list, timeout); + } + + if (timeout > 0) { + ret = qemu_poll_ns(&pfd, 1, timeout); + if (ret > 0) { + timeout = 0; + } + } + if (timeout <= 0 || ret > 0) { + ret = epoll_wait(ctx->epollfd, events, + ARRAY_SIZE(events), + timeout); + if (ret <= 0) { + goto out; + } + for (i = 0; i < ret; i++) { + int ev = events[i].events; + int revents = (ev & EPOLLIN ? G_IO_IN : 0) | + (ev & EPOLLOUT ? G_IO_OUT : 0) | + (ev & EPOLLHUP ? G_IO_HUP : 0) | + (ev & EPOLLERR ? G_IO_ERR : 0); + + node = events[i].data.ptr; + aio_add_ready_handler(ready_list, node, revents); + } + } +out: + return ret; +} + +static const FDMonOps fdmon_epoll_ops = { + .update = fdmon_epoll_update, + .wait = fdmon_epoll_wait, + .need_wait = aio_poll_disabled, +}; + +static bool fdmon_epoll_try_enable(AioContext *ctx) +{ + AioHandler *node; + struct epoll_event event; + + QLIST_FOREACH_RCU(node, &ctx->aio_handlers, node) { + int r; + if (QLIST_IS_INSERTED(node, node_deleted) || !node->pfd.events) { + continue; + } + event.events = epoll_events_from_pfd(node->pfd.events); + event.data.ptr = node; + r = epoll_ctl(ctx->epollfd, EPOLL_CTL_ADD, node->pfd.fd, &event); + if (r) { + return false; + } + } + + ctx->fdmon_ops = &fdmon_epoll_ops; + return true; +} + +bool fdmon_epoll_try_upgrade(AioContext *ctx, unsigned npfd) +{ + if (ctx->epollfd < 0) { + return false; + } + + /* Do not upgrade while external clients are disabled */ + if (atomic_read(&ctx->external_disable_cnt)) { + return false; + } + + if (npfd >= EPOLL_ENABLE_THRESHOLD) { + if (fdmon_epoll_try_enable(ctx)) { + return true; + } else { + fdmon_epoll_disable(ctx); + } + } + return false; +} + +void fdmon_epoll_setup(AioContext *ctx) +{ + ctx->epollfd = epoll_create1(EPOLL_CLOEXEC); + if (ctx->epollfd == -1) { + fprintf(stderr, "Failed to create epoll instance: %s", strerror(errno)); + } +} diff --git a/util/fdmon-io_uring.c b/util/fdmon-io_uring.c new file mode 100644 index 0000000000..d5a80ed6fb --- /dev/null +++ b/util/fdmon-io_uring.c @@ -0,0 +1,354 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Linux io_uring file descriptor monitoring + * + * The Linux io_uring API supports file descriptor monitoring with a few + * advantages over existing APIs like poll(2) and epoll(7): + * + * 1. Userspace polling of events is possible because the completion queue (cq + * ring) is shared between the kernel and userspace. This allows + * applications that rely on userspace polling to also monitor file + * descriptors in the same userspace polling loop. + * + * 2. Submission and completion is batched and done together in a single system + * call. This minimizes the number of system calls. + * + * 3. File descriptor monitoring is O(1) like epoll(7) so it scales better than + * poll(2). + * + * 4. Nanosecond timeouts are supported so it requires fewer syscalls than + * epoll(7). + * + * This code only monitors file descriptors and does not do asynchronous disk + * I/O. Implementing disk I/O efficiently has other requirements and should + * use a separate io_uring so it does not make sense to unify the code. + * + * File descriptor monitoring is implemented using the following operations: + * + * 1. IORING_OP_POLL_ADD - adds a file descriptor to be monitored. + * 2. IORING_OP_POLL_REMOVE - removes a file descriptor being monitored. When + * the poll mask changes for a file descriptor it is first removed and then + * re-added with the new poll mask, so this operation is also used as part + * of modifying an existing monitored file descriptor. + * 3. IORING_OP_TIMEOUT - added every time a blocking syscall is made to wait + * for events. This operation self-cancels if another event completes + * before the timeout. + * + * io_uring calls the submission queue the "sq ring" and the completion queue + * the "cq ring". Ring entries are called "sqe" and "cqe", respectively. + * + * The code is structured so that sq/cq rings are only modified within + * fdmon_io_uring_wait(). Changes to AioHandlers are made by enqueuing them on + * ctx->submit_list so that fdmon_io_uring_wait() can submit IORING_OP_POLL_ADD + * and/or IORING_OP_POLL_REMOVE sqes for them. + */ + +#include "qemu/osdep.h" +#include +#include "qemu/rcu_queue.h" +#include "aio-posix.h" + +enum { + FDMON_IO_URING_ENTRIES = 128, /* sq/cq ring size */ + + /* AioHandler::flags */ + FDMON_IO_URING_PENDING = (1 << 0), + FDMON_IO_URING_ADD = (1 << 1), + FDMON_IO_URING_REMOVE = (1 << 2), +}; + +static inline int poll_events_from_pfd(int pfd_events) +{ + return (pfd_events & G_IO_IN ? POLLIN : 0) | + (pfd_events & G_IO_OUT ? POLLOUT : 0) | + (pfd_events & G_IO_HUP ? POLLHUP : 0) | + (pfd_events & G_IO_ERR ? POLLERR : 0); +} + +static inline int pfd_events_from_poll(int poll_events) +{ + return (poll_events & POLLIN ? G_IO_IN : 0) | + (poll_events & POLLOUT ? G_IO_OUT : 0) | + (poll_events & POLLHUP ? G_IO_HUP : 0) | + (poll_events & POLLERR ? G_IO_ERR : 0); +} + +/* + * Returns an sqe for submitting a request. Only be called within + * fdmon_io_uring_wait(). + */ +static struct io_uring_sqe *get_sqe(AioContext *ctx) +{ + struct io_uring *ring = &ctx->fdmon_io_uring; + struct io_uring_sqe *sqe = io_uring_get_sqe(ring); + int ret; + + if (likely(sqe)) { + return sqe; + } + + /* No free sqes left, submit pending sqes first */ + do { + ret = io_uring_submit(ring); + } while (ret == -EINTR); + + assert(ret > 1); + sqe = io_uring_get_sqe(ring); + assert(sqe); + return sqe; +} + +/* Atomically enqueue an AioHandler for sq ring submission */ +static void enqueue(AioHandlerSList *head, AioHandler *node, unsigned flags) +{ + unsigned old_flags; + + old_flags = atomic_fetch_or(&node->flags, FDMON_IO_URING_PENDING | flags); + if (!(old_flags & FDMON_IO_URING_PENDING)) { + QSLIST_INSERT_HEAD_ATOMIC(head, node, node_submitted); + } +} + +/* Dequeue an AioHandler for sq ring submission. Called by fill_sq_ring(). */ +static AioHandler *dequeue(AioHandlerSList *head, unsigned *flags) +{ + AioHandler *node = QSLIST_FIRST(head); + + if (!node) { + return NULL; + } + + /* Doesn't need to be atomic since fill_sq_ring() moves the list */ + QSLIST_REMOVE_HEAD(head, node_submitted); + + /* + * Don't clear FDMON_IO_URING_REMOVE. It's sticky so it can serve two + * purposes: telling fill_sq_ring() to submit IORING_OP_POLL_REMOVE and + * telling process_cqe() to delete the AioHandler when its + * IORING_OP_POLL_ADD completes. + */ + *flags = atomic_fetch_and(&node->flags, ~(FDMON_IO_URING_PENDING | + FDMON_IO_URING_ADD)); + return node; +} + +static void fdmon_io_uring_update(AioContext *ctx, + AioHandler *old_node, + AioHandler *new_node) +{ + if (new_node) { + enqueue(&ctx->submit_list, new_node, FDMON_IO_URING_ADD); + } + + if (old_node) { + /* + * Deletion is tricky because IORING_OP_POLL_ADD and + * IORING_OP_POLL_REMOVE are async. We need to wait for the original + * IORING_OP_POLL_ADD to complete before this handler can be freed + * safely. + * + * It's possible that the file descriptor becomes ready and the + * IORING_OP_POLL_ADD cqe is enqueued before IORING_OP_POLL_REMOVE is + * submitted, too. + * + * Mark this handler deleted right now but don't place it on + * ctx->deleted_aio_handlers yet. Instead, manually fudge the list + * entry to make QLIST_IS_INSERTED() think this handler has been + * inserted and other code recognizes this AioHandler as deleted. + * + * Once the original IORING_OP_POLL_ADD completes we enqueue the + * handler on the real ctx->deleted_aio_handlers list to be freed. + */ + assert(!QLIST_IS_INSERTED(old_node, node_deleted)); + old_node->node_deleted.le_prev = &old_node->node_deleted.le_next; + + enqueue(&ctx->submit_list, old_node, FDMON_IO_URING_REMOVE); + } +} + +static void add_poll_add_sqe(AioContext *ctx, AioHandler *node) +{ + struct io_uring_sqe *sqe = get_sqe(ctx); + int events = poll_events_from_pfd(node->pfd.events); + + io_uring_prep_poll_add(sqe, node->pfd.fd, events); + io_uring_sqe_set_data(sqe, node); +} + +static void add_poll_remove_sqe(AioContext *ctx, AioHandler *node) +{ + struct io_uring_sqe *sqe = get_sqe(ctx); + + io_uring_prep_poll_remove(sqe, node); +} + +/* Add a timeout that self-cancels when another cqe becomes ready */ +static void add_timeout_sqe(AioContext *ctx, int64_t ns) +{ + struct io_uring_sqe *sqe; + struct __kernel_timespec ts = { + .tv_sec = ns / NANOSECONDS_PER_SECOND, + .tv_nsec = ns % NANOSECONDS_PER_SECOND, + }; + + sqe = get_sqe(ctx); + io_uring_prep_timeout(sqe, &ts, 1, 0); +} + +/* Add sqes from ctx->submit_list for submission */ +static void fill_sq_ring(AioContext *ctx) +{ + AioHandlerSList submit_list; + AioHandler *node; + unsigned flags; + + QSLIST_MOVE_ATOMIC(&submit_list, &ctx->submit_list); + + while ((node = dequeue(&submit_list, &flags))) { + /* Order matters, just in case both flags were set */ + if (flags & FDMON_IO_URING_ADD) { + add_poll_add_sqe(ctx, node); + } + if (flags & FDMON_IO_URING_REMOVE) { + add_poll_remove_sqe(ctx, node); + } + } +} + +/* Returns true if a handler became ready */ +static bool process_cqe(AioContext *ctx, + AioHandlerList *ready_list, + struct io_uring_cqe *cqe) +{ + AioHandler *node = io_uring_cqe_get_data(cqe); + unsigned flags; + + /* poll_timeout and poll_remove have a zero user_data field */ + if (!node) { + return false; + } + + /* + * Deletion can only happen when IORING_OP_POLL_ADD completes. If we race + * with enqueue() here then we can safely clear the FDMON_IO_URING_REMOVE + * bit before IORING_OP_POLL_REMOVE is submitted. + */ + flags = atomic_fetch_and(&node->flags, ~FDMON_IO_URING_REMOVE); + if (flags & FDMON_IO_URING_REMOVE) { + QLIST_INSERT_HEAD_RCU(&ctx->deleted_aio_handlers, node, node_deleted); + return false; + } + + aio_add_ready_handler(ready_list, node, pfd_events_from_poll(cqe->res)); + + /* IORING_OP_POLL_ADD is one-shot so we must re-arm it */ + add_poll_add_sqe(ctx, node); + return true; +} + +static int process_cq_ring(AioContext *ctx, AioHandlerList *ready_list) +{ + struct io_uring *ring = &ctx->fdmon_io_uring; + struct io_uring_cqe *cqe; + unsigned num_cqes = 0; + unsigned num_ready = 0; + unsigned head; + + io_uring_for_each_cqe(ring, head, cqe) { + if (process_cqe(ctx, ready_list, cqe)) { + num_ready++; + } + + num_cqes++; + } + + io_uring_cq_advance(ring, num_cqes); + return num_ready; +} + +static int fdmon_io_uring_wait(AioContext *ctx, AioHandlerList *ready_list, + int64_t timeout) +{ + unsigned wait_nr = 1; /* block until at least one cqe is ready */ + int ret; + + /* Fall back while external clients are disabled */ + if (atomic_read(&ctx->external_disable_cnt)) { + return fdmon_poll_ops.wait(ctx, ready_list, timeout); + } + + if (timeout == 0) { + wait_nr = 0; /* non-blocking */ + } else if (timeout > 0) { + add_timeout_sqe(ctx, timeout); + } + + fill_sq_ring(ctx); + + do { + ret = io_uring_submit_and_wait(&ctx->fdmon_io_uring, wait_nr); + } while (ret == -EINTR); + + assert(ret >= 0); + + return process_cq_ring(ctx, ready_list); +} + +static bool fdmon_io_uring_need_wait(AioContext *ctx) +{ + /* Have io_uring events completed? */ + if (io_uring_cq_ready(&ctx->fdmon_io_uring)) { + return true; + } + + /* Are there pending sqes to submit? */ + if (io_uring_sq_ready(&ctx->fdmon_io_uring)) { + return true; + } + + /* Do we need to process AioHandlers for io_uring changes? */ + if (!QSLIST_EMPTY_RCU(&ctx->submit_list)) { + return true; + } + + /* Are we falling back to fdmon-poll? */ + return atomic_read(&ctx->external_disable_cnt); +} + +static const FDMonOps fdmon_io_uring_ops = { + .update = fdmon_io_uring_update, + .wait = fdmon_io_uring_wait, + .need_wait = fdmon_io_uring_need_wait, +}; + +bool fdmon_io_uring_setup(AioContext *ctx) +{ + int ret; + + ret = io_uring_queue_init(FDMON_IO_URING_ENTRIES, &ctx->fdmon_io_uring, 0); + if (ret != 0) { + return false; + } + + QSLIST_INIT(&ctx->submit_list); + ctx->fdmon_ops = &fdmon_io_uring_ops; + return true; +} + +void fdmon_io_uring_destroy(AioContext *ctx) +{ + if (ctx->fdmon_ops == &fdmon_io_uring_ops) { + AioHandler *node; + + io_uring_queue_exit(&ctx->fdmon_io_uring); + + /* No need to submit these anymore, just free them. */ + while ((node = QSLIST_FIRST_RCU(&ctx->submit_list))) { + QSLIST_REMOVE_HEAD_RCU(&ctx->submit_list, node_submitted); + QLIST_REMOVE(node, node); + g_free(node); + } + + ctx->fdmon_ops = &fdmon_poll_ops; + } +} diff --git a/util/fdmon-poll.c b/util/fdmon-poll.c new file mode 100644 index 0000000000..488067b679 --- /dev/null +++ b/util/fdmon-poll.c @@ -0,0 +1,107 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * poll(2) file descriptor monitoring + * + * Uses ppoll(2) when available, g_poll() otherwise. + */ + +#include "qemu/osdep.h" +#include "aio-posix.h" +#include "qemu/rcu_queue.h" + +/* + * These thread-local variables are used only in fdmon_poll_wait() around the + * call to the poll() system call. In particular they are not used while + * aio_poll is performing callbacks, which makes it much easier to think about + * reentrancy! + * + * Stack-allocated arrays would be perfect but they have size limitations; + * heap allocation is expensive enough that we want to reuse arrays across + * calls to aio_poll(). And because poll() has to be called without holding + * any lock, the arrays cannot be stored in AioContext. Thread-local data + * has none of the disadvantages of these three options. + */ +static __thread GPollFD *pollfds; +static __thread AioHandler **nodes; +static __thread unsigned npfd, nalloc; +static __thread Notifier pollfds_cleanup_notifier; + +static void pollfds_cleanup(Notifier *n, void *unused) +{ + g_assert(npfd == 0); + g_free(pollfds); + g_free(nodes); + nalloc = 0; +} + +static void add_pollfd(AioHandler *node) +{ + if (npfd == nalloc) { + if (nalloc == 0) { + pollfds_cleanup_notifier.notify = pollfds_cleanup; + qemu_thread_atexit_add(&pollfds_cleanup_notifier); + nalloc = 8; + } else { + g_assert(nalloc <= INT_MAX); + nalloc *= 2; + } + pollfds = g_renew(GPollFD, pollfds, nalloc); + nodes = g_renew(AioHandler *, nodes, nalloc); + } + nodes[npfd] = node; + pollfds[npfd] = (GPollFD) { + .fd = node->pfd.fd, + .events = node->pfd.events, + }; + npfd++; +} + +static int fdmon_poll_wait(AioContext *ctx, AioHandlerList *ready_list, + int64_t timeout) +{ + AioHandler *node; + int ret; + + assert(npfd == 0); + + QLIST_FOREACH_RCU(node, &ctx->aio_handlers, node) { + if (!QLIST_IS_INSERTED(node, node_deleted) && node->pfd.events + && aio_node_check(ctx, node->is_external)) { + add_pollfd(node); + } + } + + /* epoll(7) is faster above a certain number of fds */ + if (fdmon_epoll_try_upgrade(ctx, npfd)) { + return ctx->fdmon_ops->wait(ctx, ready_list, timeout); + } + + ret = qemu_poll_ns(pollfds, npfd, timeout); + if (ret > 0) { + int i; + + for (i = 0; i < npfd; i++) { + int revents = pollfds[i].revents; + + if (revents) { + aio_add_ready_handler(ready_list, nodes[i], revents); + } + } + } + + npfd = 0; + return ret; +} + +static void fdmon_poll_update(AioContext *ctx, + AioHandler *old_node, + AioHandler *new_node) +{ + /* Do nothing, AioHandler already contains the state we'll need */ +} + +const FDMonOps fdmon_poll_ops = { + .update = fdmon_poll_update, + .wait = fdmon_poll_wait, + .need_wait = aio_poll_disabled, +}; diff --git a/util/guest-random.c b/util/guest-random.c index 9453968bd7..086115bd67 100644 --- a/util/guest-random.c +++ b/util/guest-random.c @@ -14,6 +14,7 @@ #include "qapi/error.h" #include "qemu/guest-random.h" #include "crypto/random.h" +#include "sysemu/replay.h" static __thread GRand *thread_rand; @@ -44,13 +45,21 @@ static int glib_random_bytes(void *buf, size_t len) int qemu_guest_getrandom(void *buf, size_t len, Error **errp) { + int ret; + if (replay_mode == REPLAY_MODE_PLAY) { + return replay_read_random(buf, len); + } if (unlikely(deterministic)) { /* Deterministic implementation using Glib's Mersenne Twister. */ - return glib_random_bytes(buf, len); + ret = glib_random_bytes(buf, len); } else { /* Non-deterministic implementation using crypto routines. */ - return qcrypto_random_bytes(buf, len, errp); + ret = qcrypto_random_bytes(buf, len, errp); } + if (replay_mode == REPLAY_MODE_RECORD) { + replay_save_random(ret, buf, len); + } + return ret; } void qemu_guest_getrandom_nofail(void *buf, size_t len) diff --git a/util/hbitmap.c b/util/hbitmap.c index 242c6e519c..305b894a63 100644 --- a/util/hbitmap.c +++ b/util/hbitmap.c @@ -104,7 +104,7 @@ struct HBitmap { /* Advance hbi to the next nonzero word and return it. hbi->pos * is updated. Returns zero if we reach the end of the bitmap. */ -unsigned long hbitmap_iter_skip_words(HBitmapIter *hbi) +static unsigned long hbitmap_iter_skip_words(HBitmapIter *hbi) { size_t pos = hbi->pos; const HBitmap *hb = hbi->hb; @@ -193,7 +193,31 @@ void hbitmap_iter_init(HBitmapIter *hbi, const HBitmap *hb, uint64_t first) } } -int64_t hbitmap_next_zero(const HBitmap *hb, uint64_t start, uint64_t count) +int64_t hbitmap_next_dirty(const HBitmap *hb, int64_t start, int64_t count) +{ + HBitmapIter hbi; + int64_t first_dirty_off; + uint64_t end; + + assert(start >= 0 && count >= 0); + + if (start >= hb->orig_size || count == 0) { + return -1; + } + + end = count > hb->orig_size - start ? hb->orig_size : start + count; + + hbitmap_iter_init(&hbi, hb, start); + first_dirty_off = hbitmap_iter_next(&hbi); + + if (first_dirty_off < 0 || first_dirty_off >= end) { + return -1; + } + + return MAX(start, first_dirty_off); +} + +int64_t hbitmap_next_zero(const HBitmap *hb, int64_t start, int64_t count) { size_t pos = (start >> hb->granularity) >> BITS_PER_LEVEL; unsigned long *last_lev = hb->levels[HBITMAP_LEVELS - 1]; @@ -202,6 +226,8 @@ int64_t hbitmap_next_zero(const HBitmap *hb, uint64_t start, uint64_t count) uint64_t end_bit, sz; int64_t res; + assert(start >= 0 && count >= 0); + if (start >= hb->orig_size || count == 0) { return -1; } @@ -244,41 +270,33 @@ int64_t hbitmap_next_zero(const HBitmap *hb, uint64_t start, uint64_t count) return res; } -bool hbitmap_next_dirty_area(const HBitmap *hb, uint64_t *start, - uint64_t *count) +bool hbitmap_next_dirty_area(const HBitmap *hb, int64_t start, int64_t end, + int64_t max_dirty_count, + int64_t *dirty_start, int64_t *dirty_count) { - HBitmapIter hbi; - int64_t firt_dirty_off, area_end; - uint32_t granularity = 1UL << hb->granularity; - uint64_t end; + int64_t next_zero; - if (*start >= hb->orig_size || *count == 0) { + assert(start >= 0 && end >= 0 && max_dirty_count > 0); + + end = MIN(end, hb->orig_size); + if (start >= end) { return false; } - end = *count > hb->orig_size - *start ? hb->orig_size : *start + *count; - - hbitmap_iter_init(&hbi, hb, *start); - firt_dirty_off = hbitmap_iter_next(&hbi); - - if (firt_dirty_off < 0 || firt_dirty_off >= end) { + start = hbitmap_next_dirty(hb, start, end - start); + if (start < 0) { return false; } - if (firt_dirty_off + granularity >= end) { - area_end = end; - } else { - area_end = hbitmap_next_zero(hb, firt_dirty_off + granularity, - end - firt_dirty_off - granularity); - if (area_end < 0) { - area_end = end; - } + end = start + MIN(end - start, max_dirty_count); + + next_zero = hbitmap_next_zero(hb, start, end - start); + if (next_zero >= 0) { + end = next_zero; } - if (firt_dirty_off > *start) { - *start = firt_dirty_off; - } - *count = area_end - *start; + *dirty_start = start; + *dirty_count = end - start; return true; } @@ -298,6 +316,35 @@ uint64_t hbitmap_count(const HBitmap *hb) return hb->count << hb->granularity; } +/** + * hbitmap_iter_next_word: + * @hbi: HBitmapIter to operate on. + * @p_cur: Location where to store the next non-zero word. + * + * Return the index of the next nonzero word that is set in @hbi's + * associated HBitmap, and set *p_cur to the content of that word + * (bits before the index that was passed to hbitmap_iter_init are + * trimmed on the first call). Return -1, and set *p_cur to zero, + * if all remaining words are zero. + */ +static size_t hbitmap_iter_next_word(HBitmapIter *hbi, unsigned long *p_cur) +{ + unsigned long cur = hbi->cur[HBITMAP_LEVELS - 1]; + + if (cur == 0) { + cur = hbitmap_iter_skip_words(hbi); + if (cur == 0) { + *p_cur = 0; + return -1; + } + } + + /* The next call will resume work from the next word. */ + hbi->cur[HBITMAP_LEVELS - 1] = 0; + *p_cur = cur; + return hbi->pos; +} + /* Count the number of set bits between start and end, not accounting for * the granularity. Also an example of how to use hbitmap_iter_next_word. */ @@ -716,6 +763,7 @@ HBitmap *hbitmap_alloc(uint64_t size, int granularity) HBitmap *hb = g_new0(struct HBitmap, 1); unsigned i; + assert(size <= INT64_MAX); hb->orig_size = size; assert(granularity >= 0 && granularity < 64); @@ -746,6 +794,7 @@ void hbitmap_truncate(HBitmap *hb, uint64_t size) uint64_t num_elements = size; uint64_t old; + assert(size <= INT64_MAX); hb->orig_size = size; /* Size comes in as logical elements, adjust for granularity. */ @@ -803,16 +852,15 @@ bool hbitmap_can_merge(const HBitmap *a, const HBitmap *b) */ static void hbitmap_sparse_merge(HBitmap *dst, const HBitmap *src) { - uint64_t offset = 0; - uint64_t count = src->orig_size; + int64_t offset; + int64_t count; - while (hbitmap_next_dirty_area(src, &offset, &count)) { + for (offset = 0; + hbitmap_next_dirty_area(src, offset, src->orig_size, INT64_MAX, + &offset, &count); + offset += count) + { hbitmap_set(dst, offset, count); - offset += count; - if (offset >= src->orig_size) { - break; - } - count = src->orig_size - offset; } } @@ -874,22 +922,6 @@ bool hbitmap_merge(const HBitmap *a, const HBitmap *b, HBitmap *result) return true; } -HBitmap *hbitmap_create_meta(HBitmap *hb, int chunk_size) -{ - assert(!(chunk_size & (chunk_size - 1))); - assert(!hb->meta); - hb->meta = hbitmap_alloc(hb->size << hb->granularity, - hb->granularity + ctz32(chunk_size)); - return hb->meta; -} - -void hbitmap_free_meta(HBitmap *hb) -{ - assert(hb->meta); - hbitmap_free(hb->meta); - hb->meta = NULL; -} - char *hbitmap_sha256(const HBitmap *bitmap, Error **errp) { size_t size = bitmap->sizes[HBITMAP_LEVELS - 1] * sizeof(unsigned long); diff --git a/util/id.c b/util/id.c index af1c5f1b81..5addb4460e 100644 --- a/util/id.c +++ b/util/id.c @@ -34,6 +34,7 @@ bool id_wellformed(const char *id) static const char *const id_subsys_str[ID_MAX] = { [ID_QDEV] = "qdev", [ID_BLOCK] = "block", + [ID_CHR] = "chr", }; /* diff --git a/util/log.c b/util/log.c index 1ca13059ee..2da6cb31dc 100644 --- a/util/log.c +++ b/util/log.c @@ -24,9 +24,11 @@ #include "qapi/error.h" #include "qemu/cutils.h" #include "trace/control.h" +#include "qemu/thread.h" static char *logfilename; -FILE *qemu_logfile; +static QemuMutex qemu_logfile_mutex; +QemuLogFile *qemu_logfile; int qemu_loglevel; static int log_append = 0; static GArray *debug_regions; @@ -35,10 +37,14 @@ static GArray *debug_regions; int qemu_log(const char *fmt, ...) { int ret = 0; - if (qemu_logfile) { + QemuLogFile *logfile; + + rcu_read_lock(); + logfile = atomic_rcu_read(&qemu_logfile); + if (logfile) { va_list ap; va_start(ap, fmt); - ret = vfprintf(qemu_logfile, fmt, ap); + ret = vfprintf(logfile->fd, fmt, ap); va_end(ap); /* Don't pass back error results. */ @@ -46,57 +52,91 @@ int qemu_log(const char *fmt, ...) ret = 0; } } + rcu_read_unlock(); return ret; } +static void __attribute__((__constructor__)) qemu_logfile_init(void) +{ + qemu_mutex_init(&qemu_logfile_mutex); +} + +static void qemu_logfile_free(QemuLogFile *logfile) +{ + g_assert(logfile); + + if (logfile->fd != stderr) { + fclose(logfile->fd); + } + g_free(logfile); +} + static bool log_uses_own_buffers; /* enable or disable low levels log */ void qemu_set_log(int log_flags) { + bool need_to_open_file = false; + QemuLogFile *logfile; + qemu_loglevel = log_flags; #ifdef CONFIG_TRACE_LOG qemu_loglevel |= LOG_TRACE; #endif - if (!qemu_logfile && - (is_daemonized() ? logfilename != NULL : qemu_loglevel)) { + /* + * In all cases we only log if qemu_loglevel is set. + * Also: + * If not daemonized we will always log either to stderr + * or to a file (if there is a logfilename). + * If we are daemonized, + * we will only log if there is a logfilename. + */ + if (qemu_loglevel && (!is_daemonized() || logfilename)) { + need_to_open_file = true; + } + qemu_mutex_lock(&qemu_logfile_mutex); + if (qemu_logfile && !need_to_open_file) { + logfile = qemu_logfile; + atomic_rcu_set(&qemu_logfile, NULL); + call_rcu(logfile, qemu_logfile_free, rcu); + } else if (!qemu_logfile && need_to_open_file) { + logfile = g_new0(QemuLogFile, 1); if (logfilename) { - qemu_logfile = fopen(logfilename, log_append ? "a" : "w"); - if (!qemu_logfile) { + logfile->fd = fopen(logfilename, log_append ? "a" : "w"); + if (!logfile->fd) { + g_free(logfile); perror(logfilename); _exit(1); } /* In case we are a daemon redirect stderr to logfile */ if (is_daemonized()) { - dup2(fileno(qemu_logfile), STDERR_FILENO); - fclose(qemu_logfile); + dup2(fileno(logfile->fd), STDERR_FILENO); + fclose(logfile->fd); /* This will skip closing logfile in qemu_log_close() */ - qemu_logfile = stderr; + logfile->fd = stderr; } } else { /* Default to stderr if no log file specified */ assert(!is_daemonized()); - qemu_logfile = stderr; + logfile->fd = stderr; } /* must avoid mmap() usage of glibc by setting a buffer "by hand" */ if (log_uses_own_buffers) { static char logfile_buf[4096]; - setvbuf(qemu_logfile, logfile_buf, _IOLBF, sizeof(logfile_buf)); + setvbuf(logfile->fd, logfile_buf, _IOLBF, sizeof(logfile_buf)); } else { #if defined(_WIN32) /* Win32 doesn't support line-buffering, so use unbuffered output. */ - setvbuf(qemu_logfile, NULL, _IONBF, 0); + setvbuf(logfile->fd, NULL, _IONBF, 0); #else - setvbuf(qemu_logfile, NULL, _IOLBF, 0); + setvbuf(logfile->fd, NULL, _IOLBF, 0); #endif log_append = 1; } + atomic_rcu_set(&qemu_logfile, logfile); } - if (qemu_logfile && - (is_daemonized() ? logfilename == NULL : !qemu_loglevel)) { - qemu_log_close(); - } + qemu_mutex_unlock(&qemu_logfile_mutex); } void qemu_log_needs_buffers(void) @@ -108,24 +148,29 @@ void qemu_log_needs_buffers(void) * Allow the user to include %d in their logfile which will be * substituted with the current PID. This is useful for debugging many * nested linux-user tasks but will result in lots of logs. + * + * filename may be NULL. In that case, log output is sent to stderr */ void qemu_set_log_filename(const char *filename, Error **errp) { - char *pidstr; g_free(logfilename); + logfilename = NULL; - pidstr = strstr(filename, "%"); - if (pidstr) { - /* We only accept one %d, no other format strings */ - if (pidstr[1] != 'd' || strchr(pidstr + 2, '%')) { - error_setg(errp, "Bad logfile format: %s", filename); - return; - } else { - logfilename = g_strdup_printf(filename, getpid()); - } - } else { - logfilename = g_strdup(filename); + if (filename) { + char *pidstr = strstr(filename, "%"); + if (pidstr) { + /* We only accept one %d, no other format strings */ + if (pidstr[1] != 'd' || strchr(pidstr + 2, '%')) { + error_setg(errp, "Bad logfile format: %s", filename); + return; + } else { + logfilename = g_strdup_printf(filename, getpid()); + } + } else { + logfilename = g_strdup(filename); + } } + qemu_log_close(); qemu_set_log(qemu_loglevel); } @@ -224,18 +269,29 @@ out: /* fflush() the log file */ void qemu_log_flush(void) { - fflush(qemu_logfile); + QemuLogFile *logfile; + + rcu_read_lock(); + logfile = atomic_rcu_read(&qemu_logfile); + if (logfile) { + fflush(logfile->fd); + } + rcu_read_unlock(); } /* Close the log file */ void qemu_log_close(void) { - if (qemu_logfile) { - if (qemu_logfile != stderr) { - fclose(qemu_logfile); - } - qemu_logfile = NULL; + QemuLogFile *logfile; + + qemu_mutex_lock(&qemu_logfile_mutex); + logfile = qemu_logfile; + + if (logfile) { + atomic_rcu_set(&qemu_logfile, NULL); + call_rcu(logfile, qemu_logfile_free, rcu); } + qemu_mutex_unlock(&qemu_logfile_mutex); } const QEMULogItem qemu_log_items[] = { @@ -276,6 +332,8 @@ const QEMULogItem qemu_log_items[] = { #ifdef CONFIG_PLUGIN { CPU_LOG_PLUGIN, "plugin", "output from TCG plugins\n"}, #endif + { LOG_STRACE, "strace", + "log every user-mode syscall, its input, and its result" }, { 0, NULL, NULL }, }; diff --git a/util/module.c b/util/module.c index e9fe3e5422..e48d9aacc0 100644 --- a/util/module.c +++ b/util/module.c @@ -19,6 +19,9 @@ #endif #include "qemu/queue.h" #include "qemu/module.h" +#ifdef CONFIG_MODULE_UPGRADES +#include "qemu-version.h" +#endif typedef struct ModuleEntry { @@ -30,6 +33,7 @@ typedef struct ModuleEntry typedef QTAILQ_HEAD(, ModuleEntry) ModuleTypeList; static ModuleTypeList init_type_list[MODULE_INIT_MAX]; +static bool modules_init_done[MODULE_INIT_MAX]; static ModuleTypeList dso_init_list; @@ -91,11 +95,17 @@ void module_call_init(module_init_type type) ModuleTypeList *l; ModuleEntry *e; + if (modules_init_done[type]) { + return; + } + l = find_type(type); QTAILQ_FOREACH(e, l, node) { e->init(); } + + modules_init_done[type] = true; } #ifdef CONFIG_MODULES @@ -163,8 +173,11 @@ bool module_load_one(const char *prefix, const char *lib_name) #ifdef CONFIG_MODULES char *fname = NULL; char *exec_dir; +#ifdef CONFIG_MODULE_UPGRADES + char *version_dir; +#endif const char *search_dir; - char *dirs[4]; + char *dirs[5]; char *module_name; int i = 0, n_dirs = 0; int ret; @@ -194,6 +207,14 @@ bool module_load_one(const char *prefix, const char *lib_name) dirs[n_dirs++] = g_strdup_printf("%s", CONFIG_QEMU_MODDIR); dirs[n_dirs++] = g_strdup_printf("%s/..", exec_dir ? : ""); dirs[n_dirs++] = g_strdup_printf("%s", exec_dir ? : ""); + +#ifdef CONFIG_MODULE_UPGRADES + version_dir = g_strcanon(g_strdup(QEMU_PKGVERSION), + G_CSET_A_2_Z G_CSET_a_2_z G_CSET_DIGITS "+-.~", + '_'); + dirs[n_dirs++] = g_strdup_printf("/var/run/qemu/%s", version_dir); +#endif + assert(n_dirs <= ARRAY_SIZE(dirs)); g_free(exec_dir); @@ -214,6 +235,7 @@ bool module_load_one(const char *prefix, const char *lib_name) if (!success) { g_hash_table_remove(loaded_modules, module_name); + g_free(module_name); } for (i = 0; i < n_dirs; i++) { diff --git a/util/nvdimm-utils.c b/util/nvdimm-utils.c new file mode 100644 index 0000000000..5cc768ca47 --- /dev/null +++ b/util/nvdimm-utils.c @@ -0,0 +1,29 @@ +#include "qemu/nvdimm-utils.h" +#include "hw/mem/nvdimm.h" + +static int nvdimm_device_list(Object *obj, void *opaque) +{ + GSList **list = opaque; + + if (object_dynamic_cast(obj, TYPE_NVDIMM)) { + *list = g_slist_append(*list, DEVICE(obj)); + } + + object_child_foreach(obj, nvdimm_device_list, opaque); + return 0; +} + +/* + * inquire NVDIMM devices and link them into the list which is + * returned to the caller. + * + * Note: it is the caller's responsibility to free the list to avoid + * memory leak. + */ +GSList *nvdimm_get_device_list(void) +{ + GSList *list = NULL; + + object_child_foreach(qdev_get_machine(), nvdimm_device_list, &list); + return list; +} diff --git a/util/osdep.c b/util/osdep.c index 3f04326040..4829c07ff6 100644 --- a/util/osdep.c +++ b/util/osdep.c @@ -82,8 +82,8 @@ static int qemu_mprotect__osdep(void *addr, size_t size, int prot) DWORD old_protect; if (!VirtualProtect(addr, size, prot, &old_protect)) { - error_report("%s: VirtualProtect failed with error code %ld", - __func__, GetLastError()); + g_autofree gchar *emsg = g_win32_error_message(GetLastError()); + error_report("%s: VirtualProtect failed: %s", __func__, emsg); return -1; } return 0; @@ -370,6 +370,21 @@ int qemu_close(int fd) return close(fd); } +/* + * Delete a file from the filesystem, unless the filename is /dev/fdset/... + * + * Returns: On success, zero is returned. On error, -1 is returned, + * and errno is set appropriately. + */ +int qemu_unlink(const char *name) +{ + if (g_str_has_prefix(name, "/dev/fdset/")) { + return 0; + } + + return unlink(name); +} + /* * A variant of write(2) which handles partial write. * diff --git a/util/oslib-posix.c b/util/oslib-posix.c index 5a291cc982..062236a1ab 100644 --- a/util/oslib-posix.c +++ b/util/oslib-posix.c @@ -76,6 +76,10 @@ static MemsetThread *memset_thread; static int memset_num_threads; static bool memset_thread_failed; +static QemuMutex page_mutex; +static QemuCond page_cond; +static bool threads_created_flag; + int qemu_get_thread_id(void) { #if defined(__linux__) @@ -403,6 +407,17 @@ static void *do_touch_pages(void *arg) MemsetThread *memset_args = (MemsetThread *)arg; sigset_t set, oldset; + /* + * On Linux, the page faults from the loop below can cause mmap_sem + * contention with allocation of the thread stacks. Do not start + * clearing until all threads have been created. + */ + qemu_mutex_lock(&page_mutex); + while(!threads_created_flag){ + qemu_cond_wait(&page_cond, &page_mutex); + } + qemu_mutex_unlock(&page_mutex); + /* unblock SIGBUS */ sigemptyset(&set); sigaddset(&set, SIGBUS); @@ -451,27 +466,38 @@ static inline int get_memset_num_threads(int smp_cpus) static bool touch_all_pages(char *area, size_t hpagesize, size_t numpages, int smp_cpus) { - size_t numpages_per_thread; - size_t size_per_thread; + static gsize initialized = 0; + size_t numpages_per_thread, leftover; char *addr = area; int i = 0; + if (g_once_init_enter(&initialized)) { + qemu_mutex_init(&page_mutex); + qemu_cond_init(&page_cond); + g_once_init_leave(&initialized, 1); + } + memset_thread_failed = false; + threads_created_flag = false; memset_num_threads = get_memset_num_threads(smp_cpus); memset_thread = g_new0(MemsetThread, memset_num_threads); - numpages_per_thread = (numpages / memset_num_threads); - size_per_thread = (hpagesize * numpages_per_thread); + numpages_per_thread = numpages / memset_num_threads; + leftover = numpages % memset_num_threads; for (i = 0; i < memset_num_threads; i++) { memset_thread[i].addr = addr; - memset_thread[i].numpages = (i == (memset_num_threads - 1)) ? - numpages : numpages_per_thread; + memset_thread[i].numpages = numpages_per_thread + (i < leftover); memset_thread[i].hpagesize = hpagesize; qemu_thread_create(&memset_thread[i].pgthread, "touch_pages", do_touch_pages, &memset_thread[i], QEMU_THREAD_JOINABLE); - addr += size_per_thread; - numpages -= numpages_per_thread; + addr += memset_thread[i].numpages * hpagesize; } + + qemu_mutex_lock(&page_mutex); + threads_created_flag = true; + qemu_cond_broadcast(&page_cond); + qemu_mutex_unlock(&page_mutex); + for (i = 0; i < memset_num_threads; i++) { qemu_thread_join(&memset_thread[i].pgthread); } diff --git a/util/qemu-error.c b/util/qemu-error.c index f373f3b3b0..dac7c7dc50 100644 --- a/util/qemu-error.c +++ b/util/qemu-error.c @@ -24,6 +24,9 @@ typedef enum { REPORT_TYPE_INFO, } report_type; +/* Prepend timestamp to messages */ +bool error_with_timestamp; + int error_printf(const char *fmt, ...) { va_list ap; @@ -191,7 +194,6 @@ static void print_loc(void) } } -bool enable_timestamp_msg; /* * Print a message to current monitor if we have one, else to stderr. * @report_type is the type of message: error, warning or informational. @@ -204,7 +206,7 @@ static void vreport(report_type type, const char *fmt, va_list ap) GTimeVal tv; gchar *timestr; - if (enable_timestamp_msg && !cur_mon) { + if (error_with_timestamp && !cur_mon) { g_get_current_time(&tv); timestr = g_time_val_to_iso8601(&tv); error_printf("%s ", timestr); diff --git a/util/qemu-timer-common.c b/util/qemu-timer-common.c index 06d084d364..baf3317f74 100644 --- a/util/qemu-timer-common.c +++ b/util/qemu-timer-common.c @@ -49,14 +49,11 @@ int use_rt_clock; static void __attribute__((constructor)) init_get_clock(void) { + struct timespec ts; + use_rt_clock = 0; -#ifdef CLOCK_MONOTONIC - { - struct timespec ts; - if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) { - use_rt_clock = 1; - } + if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) { + use_rt_clock = 1; } -#endif } #endif diff --git a/util/qemu-timer.c b/util/qemu-timer.c index ef52d28d37..d548d3c1ad 100644 --- a/util/qemu-timer.c +++ b/util/qemu-timer.c @@ -25,6 +25,7 @@ #include "qemu/osdep.h" #include "qemu/main-loop.h" #include "qemu/timer.h" +#include "qemu/lockable.h" #include "sysemu/replay.h" #include "sysemu/cpus.h" @@ -186,13 +187,12 @@ bool timerlist_expired(QEMUTimerList *timer_list) return false; } - qemu_mutex_lock(&timer_list->active_timers_lock); - if (!timer_list->active_timers) { - qemu_mutex_unlock(&timer_list->active_timers_lock); - return false; + WITH_QEMU_LOCK_GUARD(&timer_list->active_timers_lock) { + if (!timer_list->active_timers) { + return false; + } + expire_time = timer_list->active_timers->expire_time; } - expire_time = timer_list->active_timers->expire_time; - qemu_mutex_unlock(&timer_list->active_timers_lock); return expire_time <= qemu_clock_get_ns(timer_list->clock->type); } @@ -225,13 +225,12 @@ int64_t timerlist_deadline_ns(QEMUTimerList *timer_list) * value but ->notify_cb() is called when the deadline changes. Therefore * the caller should notice the change and there is no race condition. */ - qemu_mutex_lock(&timer_list->active_timers_lock); - if (!timer_list->active_timers) { - qemu_mutex_unlock(&timer_list->active_timers_lock); - return -1; + WITH_QEMU_LOCK_GUARD(&timer_list->active_timers_lock) { + if (!timer_list->active_timers) { + return -1; + } + expire_time = timer_list->active_timers->expire_time; } - expire_time = timer_list->active_timers->expire_time; - qemu_mutex_unlock(&timer_list->active_timers_lock); delta = expire_time - qemu_clock_get_ns(timer_list->clock->type); diff --git a/util/qsp.c b/util/qsp.c index 62265417fd..7d5147f1b2 100644 --- a/util/qsp.c +++ b/util/qsp.c @@ -598,7 +598,6 @@ static void qsp_ht_delete(void *p, uint32_t h, void *htp) static void qsp_mktree(GTree *tree, bool callsite_coalesce) { - QSPSnapshot *snap; struct qht ht, coalesce_ht; struct qht *htp; @@ -610,20 +609,19 @@ static void qsp_mktree(GTree *tree, bool callsite_coalesce) * We must remain in an RCU read-side critical section until we're done * with the snapshot. */ - rcu_read_lock(); - snap = atomic_rcu_read(&qsp_snapshot); + WITH_RCU_READ_LOCK_GUARD() { + QSPSnapshot *snap = atomic_rcu_read(&qsp_snapshot); - /* Aggregate all results from the global hash table into a local one */ - qht_init(&ht, qsp_entry_no_thread_cmp, QSP_INITIAL_SIZE, - QHT_MODE_AUTO_RESIZE | QHT_MODE_RAW_MUTEXES); - qht_iter(&qsp_ht, qsp_aggregate, &ht); + /* Aggregate all results from the global hash table into a local one */ + qht_init(&ht, qsp_entry_no_thread_cmp, QSP_INITIAL_SIZE, + QHT_MODE_AUTO_RESIZE | QHT_MODE_RAW_MUTEXES); + qht_iter(&qsp_ht, qsp_aggregate, &ht); - /* compute the difference wrt the snapshot, if any */ - if (snap) { - qsp_diff(&snap->ht, &ht); + /* compute the difference wrt the snapshot, if any */ + if (snap) { + qsp_diff(&snap->ht, &ht); + } } - /* done with the snapshot; RCU can reclaim it */ - rcu_read_unlock(); htp = &ht; if (callsite_coalesce) { diff --git a/util/selfmap.c b/util/selfmap.c new file mode 100644 index 0000000000..2ec99dfdda --- /dev/null +++ b/util/selfmap.c @@ -0,0 +1,78 @@ +/* + * Utility function to get QEMU's own process map + * + * Copyright (c) 2020 Linaro Ltd + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/cutils.h" +#include "qemu/selfmap.h" + +GSList *read_self_maps(void) +{ + gchar *maps; + GSList *map_info = NULL; + + if (g_file_get_contents("/proc/self/maps", &maps, NULL, NULL)) { + gchar **lines = g_strsplit(maps, "\n", 0); + int i, entries = g_strv_length(lines); + + for (i = 0; i < entries; i++) { + gchar **fields = g_strsplit(lines[i], " ", 6); + if (g_strv_length(fields) > 4) { + MapInfo *e = g_new0(MapInfo, 1); + int errors; + const char *end; + + errors = qemu_strtoul(fields[0], &end, 16, &e->start); + errors += qemu_strtoul(end + 1, NULL, 16, &e->end); + + e->is_read = fields[1][0] == 'r'; + e->is_write = fields[1][1] == 'w'; + e->is_exec = fields[1][2] == 'x'; + e->is_priv = fields[1][3] == 'p'; + + errors += qemu_strtoul(fields[2], NULL, 16, &e->offset); + e->dev = g_strdup(fields[3]); + errors += qemu_strtou64(fields[4], NULL, 10, &e->inode); + + /* + * The last field may have leading spaces which we + * need to strip. + */ + if (g_strv_length(fields) == 6) { + e->path = g_strdup(g_strchug(fields[5])); + } + map_info = g_slist_prepend(map_info, e); + } + + g_strfreev(fields); + } + g_strfreev(lines); + g_free(maps); + } + + /* ensure the map data is in the same order we collected it */ + return g_slist_reverse(map_info); +} + +/** + * free_self_maps: + * @info: a GSlist + * + * Free a list of MapInfo structures. + */ +static void free_info(gpointer data) +{ + MapInfo *e = (MapInfo *) data; + g_free(e->dev); + g_free(e->path); + g_free(e); +} + +void free_self_maps(GSList *info) +{ + g_slist_free_full(info, &free_info); +} diff --git a/util/trace-events b/util/trace-events index 83b6639018..0ce42822eb 100644 --- a/util/trace-events +++ b/util/trace-events @@ -5,6 +5,8 @@ run_poll_handlers_begin(void *ctx, int64_t max_ns, int64_t timeout) "ctx %p max_ run_poll_handlers_end(void *ctx, bool progress, int64_t timeout) "ctx %p progress %d new timeout %"PRId64 poll_shrink(void *ctx, int64_t old, int64_t new) "ctx %p old %"PRId64" new %"PRId64 poll_grow(void *ctx, int64_t old, int64_t new) "ctx %p old %"PRId64" new %"PRId64 +poll_add(void *ctx, void *node, int fd, unsigned revents) "ctx %p node %p fd %d revents 0x%x" +poll_remove(void *ctx, void *node, int fd) "ctx %p node %p fd %d" # async.c aio_co_schedule(void *ctx, void *co) "ctx %p co %p" diff --git a/util/vfio-helpers.c b/util/vfio-helpers.c index 813f7ec564..ddd9a96e76 100644 --- a/util/vfio-helpers.c +++ b/util/vfio-helpers.c @@ -545,7 +545,7 @@ static int qemu_vfio_do_mapping(QEMUVFIOState *s, void *host, size_t size, trace_qemu_vfio_do_mapping(s, host, size, iova); if (ioctl(s->container, VFIO_IOMMU_MAP_DMA, &dma_map)) { - error_report("VFIO_MAP_DMA: %d", -errno); + error_report("VFIO_MAP_DMA failed: %s", strerror(errno)); return -errno; } return 0; @@ -570,7 +570,7 @@ static void qemu_vfio_undo_mapping(QEMUVFIOState *s, IOVAMapping *mapping, assert(QEMU_IS_ALIGNED(mapping->size, qemu_real_host_page_size)); assert(index >= 0 && index < s->nr_mappings); if (ioctl(s->container, VFIO_IOMMU_UNMAP_DMA, &unmap)) { - error_setg(errp, "VFIO_UNMAP_DMA failed: %d", -errno); + error_setg_errno(errp, errno, "VFIO_UNMAP_DMA failed"); } memmove(mapping, &s->mappings[index + 1], sizeof(s->mappings[0]) * (s->nr_mappings - index - 1)); @@ -669,7 +669,7 @@ int qemu_vfio_dma_reset_temporary(QEMUVFIOState *s) trace_qemu_vfio_dma_reset_temporary(s); qemu_mutex_lock(&s->lock); if (ioctl(s->container, VFIO_IOMMU_UNMAP_DMA, &unmap)) { - error_report("VFIO_UNMAP_DMA: %d", -errno); + error_report("VFIO_UNMAP_DMA failed: %s", strerror(errno)); qemu_mutex_unlock(&s->lock); return -errno; }